parser.c 59.5 KB
Newer Older
1
/*
Tinderbox User's avatar
Tinderbox User committed
2
 * Copyright (C) 2004-2013  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 2000-2003  Internet Software Consortium.
4
 *
Automatic Updater's avatar
Automatic Updater committed
5
 * Permission to use, copy, modify, and/or distribute this software for any
6 7 8
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
Mark Andrews's avatar
Mark Andrews committed
9 10 11 12 13 14 15
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
16 17
 */

Mark Andrews's avatar
Mark Andrews committed
18
/* $Id$ */
19 20

/*! \file */
21 22 23 24 25

#include <config.h>

#include <isc/buffer.h>
#include <isc/dir.h>
26
#include <isc/formatcheck.h>
27 28 29 30 31
#include <isc/lex.h>
#include <isc/log.h>
#include <isc/mem.h>
#include <isc/net.h>
#include <isc/netaddr.h>
32
#include <isc/netscope.h>
33
#include <isc/print.h>
34 35 36
#include <isc/string.h>
#include <isc/sockaddr.h>
#include <isc/symtab.h>
37
#include <isc/util.h>
38 39

#include <isccfg/cfg.h>
40
#include <isccfg/grammar.h>
41
#include <isccfg/log.h>
42

43
/* Shorthand */
44 45
#define CAT CFG_LOGCATEGORY_CONFIG
#define MOD CFG_LOGMODULE_PARSER
46 47 48

#define MAP_SYM 1 	/* Unique type for isc_symtab */

49 50
#define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)

51 52
/* Check a return value. */
#define CHECK(op) 						\
Automatic Updater's avatar
Automatic Updater committed
53
	do { result = (op); 					\
54 55 56 57 58 59 60 61 62 63 64 65 66
		if (result != ISC_R_SUCCESS) goto cleanup; 	\
	} while (0)

/* Clean up a configuration object if non-NULL. */
#define CLEANUP_OBJ(obj) \
	do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)


/*
 * Forward declarations of static functions.
 */

static void
67
free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
68

69
static isc_result_t
70
parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
71 72

static void
73
print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
74 75

static void
76
free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
77

78
static isc_result_t
79
create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
80 81

static isc_result_t
82 83
create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
	      cfg_obj_t **ret);
84 85

static void
86
free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
87

88
static isc_result_t
89
create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
90 91

static void
92
free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
93 94 95

static isc_result_t
parse_symtab_elt(cfg_parser_t *pctx, const char *name,
96 97
		 cfg_type_t *elttype, isc_symtab_t *symtab,
		 isc_boolean_t callback);
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113

static void
free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);

static isc_result_t
cfg_getstringtoken(cfg_parser_t *pctx);

static void
parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
		unsigned int flags, const char *format, va_list args);

/*
 * Data representations.  These correspond to members of the
 * "value" union in struct cfg_obj (except "void", which does
 * not need a union member).
 */
114

115
cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
116
cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
117 118 119 120 121 122 123 124 125 126 127 128 129
cfg_rep_t cfg_rep_string = { "string", free_string };
cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
cfg_rep_t cfg_rep_map = { "map", free_map };
cfg_rep_t cfg_rep_list = { "list", free_list };
cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
cfg_rep_t cfg_rep_void = { "void", free_noop };

/*
 * Configuration type definitions.
 */

130
/*%
131 132 133
 * An implicit list.  These are formed by clauses that occur multiple times.
 */
static cfg_type_t cfg_type_implicitlist = {
134
	"implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL };
135 136 137

/* Functions. */

138
void
139
cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
140 141 142
	obj->type->print(pctx, obj);
}

143 144
void
cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
145 146 147 148 149
	pctx->f(pctx->closure, text, len);
}

static void
print_open(cfg_printer_t *pctx) {
150
	cfg_print_chars(pctx, "{\n", 2);
151 152 153 154 155 156 157
	pctx->indent++;
}

static void
print_indent(cfg_printer_t *pctx) {
	int indent = pctx->indent;
	while (indent > 0) {
158
		cfg_print_chars(pctx, "\t", 1);
159 160 161 162 163 164 165 166
		indent--;
	}
}

static void
print_close(cfg_printer_t *pctx) {
	pctx->indent--;
	print_indent(pctx);
167
	cfg_print_chars(pctx, "}", 1);
168 169
}

170 171
isc_result_t
cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
172 173 174 175 176 177 178
	isc_result_t result;
	INSIST(ret != NULL && *ret == NULL);
	result = type->parse(pctx, type, ret);
	if (result != ISC_R_SUCCESS)
		return (result);
	INSIST(*ret != NULL);
	return (ISC_R_SUCCESS);
179 180 181
}

void
182
cfg_print(const cfg_obj_t *obj,
183 184 185 186 187 188 189 190 191 192
	  void (*f)(void *closure, const char *text, int textlen),
	  void *closure)
{
	cfg_printer_t pctx;
	pctx.f = f;
	pctx.closure = closure;
	pctx.indent = 0;
	obj->type->print(&pctx, obj);
}

193 194

/* Tuples. */
Automatic Updater's avatar
Automatic Updater committed
195

196 197
isc_result_t
cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
198
	isc_result_t result;
199 200
	const cfg_tuplefielddef_t *fields = type->of;
	const cfg_tuplefielddef_t *f;
201 202 203
	cfg_obj_t *obj = NULL;
	unsigned int nfields = 0;
	int i;
204

205
	for (f = fields; f->name != NULL; f++)
206 207
		nfields++;

208
	CHECK(cfg_create_obj(pctx, type, &obj));
209 210
	obj->value.tuple = isc_mem_get(pctx->mctx,
				       nfields * sizeof(cfg_obj_t *));
211 212 213 214
	if (obj->value.tuple == NULL) {
		result = ISC_R_NOMEMORY;
		goto cleanup;
	}
215
	for (f = fields, i = 0; f->name != NULL; f++, i++)
216
		obj->value.tuple[i] = NULL;
217 218 219 220
	*ret = obj;
	return (ISC_R_SUCCESS);

 cleanup:
221 222
	if (obj != NULL)
		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
223 224 225
	return (result);
}

226 227
isc_result_t
cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
228 229
{
	isc_result_t result;
230 231
	const cfg_tuplefielddef_t *fields = type->of;
	const cfg_tuplefielddef_t *f;
232 233
	cfg_obj_t *obj = NULL;
	unsigned int i;
234

235
	CHECK(cfg_create_tuple(pctx, type, &obj));
236
	for (f = fields, i = 0; f->name != NULL; f++, i++)
237
		CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
238 239 240 241 242 243 244 245 246

	*ret = obj;
	return (ISC_R_SUCCESS);

 cleanup:
	CLEANUP_OBJ(obj);
	return (result);
}

247
void
248
cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
249
	unsigned int i;
250 251
	const cfg_tuplefielddef_t *fields = obj->type->of;
	const cfg_tuplefielddef_t *f;
252
	isc_boolean_t need_space = ISC_FALSE;
253

254
	for (f = fields, i = 0; f->name != NULL; f++, i++) {
255
		const cfg_obj_t *fieldobj = obj->value.tuple[i];
256
		if (need_space)
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
			cfg_print_chars(pctx, " ", 1);
		cfg_print_obj(pctx, fieldobj);
		need_space = ISC_TF(fieldobj->type->print != cfg_print_void);
	}
}

void
cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
	const cfg_tuplefielddef_t *fields = type->of;
	const cfg_tuplefielddef_t *f;
	isc_boolean_t need_space = ISC_FALSE;

	for (f = fields; f->name != NULL; f++) {
		if (need_space)
			cfg_print_chars(pctx, " ", 1);
		cfg_doc_obj(pctx, f->type);
		need_space = ISC_TF(f->type->print != cfg_print_void);
274 275 276 277 278 279
	}
}

static void
free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
	unsigned int i;
280 281
	const cfg_tuplefielddef_t *fields = obj->type->of;
	const cfg_tuplefielddef_t *f;
282 283
	unsigned int nfields = 0;

284 285 286
	if (obj->value.tuple == NULL)
		return;

287
	for (f = fields, i = 0; f->name != NULL; f++, i++) {
288 289 290 291 292 293 294
		CLEANUP_OBJ(obj->value.tuple[i]);
		nfields++;
	}
	isc_mem_put(pctx->mctx, obj->value.tuple,
		    nfields * sizeof(cfg_obj_t *));
}

295
isc_boolean_t
296
cfg_obj_istuple(const cfg_obj_t *obj) {
297 298 299 300
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
}

301 302
const cfg_obj_t *
cfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) {
303
	unsigned int i;
304 305
	const cfg_tuplefielddef_t *fields;
	const cfg_tuplefielddef_t *f;
Automatic Updater's avatar
Automatic Updater committed
306

307 308 309 310 311 312 313 314 315 316 317
	REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);

	fields = tupleobj->type->of;
	for (f = fields, i = 0; f->name != NULL; f++, i++) {
		if (strcmp(f->name, name) == 0)
			return (tupleobj->value.tuple[i]);
	}
	INSIST(0);
	return (NULL);
}

318 319
isc_result_t
cfg_parse_special(cfg_parser_t *pctx, int special) {
Automatic Updater's avatar
Automatic Updater committed
320
	isc_result_t result;
321 322 323 324 325
	CHECK(cfg_gettoken(pctx, 0));
	if (pctx->token.type == isc_tokentype_special &&
	    pctx->token.value.as_char == special)
		return (ISC_R_SUCCESS);

326
	cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
327 328 329 330 331 332 333 334 335 336 337 338 339 340
	return (ISC_R_UNEXPECTEDTOKEN);
 cleanup:
	return (result);
}

/*
 * Parse a required semicolon.  If it is not there, log
 * an error and increment the error count but continue
 * parsing.  Since the next token is pushed back,
 * care must be taken to make sure it is eventually
 * consumed or an infinite loop may result.
 */
static isc_result_t
parse_semicolon(cfg_parser_t *pctx) {
Automatic Updater's avatar
Automatic Updater committed
341
	isc_result_t result;
342 343 344 345 346
	CHECK(cfg_gettoken(pctx, 0));
	if (pctx->token.type == isc_tokentype_special &&
	    pctx->token.value.as_char == ';')
		return (ISC_R_SUCCESS);

347
	cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
348 349 350 351 352 353 354 355 356 357
	cfg_ungettoken(pctx);
 cleanup:
	return (result);
}

/*
 * Parse EOF, logging and returning an error if not there.
 */
static isc_result_t
parse_eof(cfg_parser_t *pctx) {
Automatic Updater's avatar
Automatic Updater committed
358
	isc_result_t result;
359 360 361 362 363
	CHECK(cfg_gettoken(pctx, 0));

	if (pctx->token.type == isc_tokentype_eof)
		return (ISC_R_SUCCESS);

364
	cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
365 366
	return (ISC_R_UNEXPECTEDTOKEN);
 cleanup:
Andreas Gustafsson's avatar
spacing  
Andreas Gustafsson committed
367
	return (result);
368 369 370
}

/* A list of files, used internally for pctx->files. */
371

372
static cfg_type_t cfg_type_filelist = {
373
	"filelist", NULL, print_list, NULL, &cfg_rep_list,
374 375 376 377
	&cfg_type_qstring
};

isc_result_t
378
cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
379 380 381 382 383 384 385 386 387 388 389
	isc_result_t result;
	cfg_parser_t *pctx;
	isc_lexspecials_t specials;

	REQUIRE(mctx != NULL);
	REQUIRE(ret != NULL && *ret == NULL);

	pctx = isc_mem_get(mctx, sizeof(*pctx));
	if (pctx == NULL)
		return (ISC_R_NOMEMORY);

390 391 392 393 394 395
	result = isc_refcount_init(&pctx->references, 1);
	if (result != ISC_R_SUCCESS) {
		isc_mem_put(mctx, pctx, sizeof(*pctx));
		return (result);
	}

396 397 398 399 400 401
	pctx->mctx = mctx;
	pctx->lctx = lctx;
	pctx->lexer = NULL;
	pctx->seen_eof = ISC_FALSE;
	pctx->ungotten = ISC_FALSE;
	pctx->errors = 0;
Mark Andrews's avatar
Mark Andrews committed
402
	pctx->warnings = 0;
403 404
	pctx->open_files = NULL;
	pctx->closed_files = NULL;
405
	pctx->line = 0;
406 407
	pctx->callback = NULL;
	pctx->callbackarg = NULL;
Mark Andrews's avatar
Mark Andrews committed
408
	pctx->token.type = isc_tokentype_unknown;
Automatic Updater's avatar
Automatic Updater committed
409
	pctx->flags = 0;
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425

	memset(specials, 0, sizeof(specials));
	specials['{'] = 1;
	specials['}'] = 1;
	specials[';'] = 1;
	specials['/'] = 1;
	specials['"'] = 1;
	specials['!'] = 1;

	CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer));

	isc_lex_setspecials(pctx->lexer, specials);
	isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C |
					 ISC_LEXCOMMENT_CPLUSPLUS |
					 ISC_LEXCOMMENT_SHELL));

426 427
	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
428 429 430 431 432 433 434

	*ret = pctx;
	return (ISC_R_SUCCESS);

 cleanup:
	if (pctx->lexer != NULL)
		isc_lex_destroy(&pctx->lexer);
435 436
	CLEANUP_OBJ(pctx->open_files);
	CLEANUP_OBJ(pctx->closed_files);
437 438 439 440 441
	isc_mem_put(mctx, pctx, sizeof(*pctx));
	return (result);
}

static isc_result_t
442
parser_openfile(cfg_parser_t *pctx, const char *filename) {
443 444 445 446 447 448
	isc_result_t result;
	cfg_listelt_t *elt = NULL;
	cfg_obj_t *stringobj = NULL;

	result = isc_lex_openfile(pctx->lexer, filename);
	if (result != ISC_R_SUCCESS) {
449
		cfg_parser_error(pctx, 0, "open: %s: %s",
450 451 452 453 454 455 456
			     filename, isc_result_totext(result));
		goto cleanup;
	}

	CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
	CHECK(create_listelt(pctx, &elt));
	elt->obj = stringobj;
457
	ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
458 459 460 461 462 463 464

	return (ISC_R_SUCCESS);
 cleanup:
	CLEANUP_OBJ(stringobj);
	return (result);
}

465 466 467 468 469 470 471 472 473
void
cfg_parser_setcallback(cfg_parser_t *pctx,
		       cfg_parsecallback_t callback,
		       void *arg)
{
	pctx->callback = callback;
	pctx->callbackarg = arg;
}

474 475 476 477 478
/*
 * Parse a configuration using a pctx where a lexer has already
 * been set up with a source.
 */
static isc_result_t
479
parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
480 481 482
	isc_result_t result;
	cfg_obj_t *obj = NULL;

483
	result = cfg_parse_obj(pctx, type, &obj);
484 485 486 487 488 489 490 491 492 493

	if (pctx->errors != 0) {
		/* Errors have been logged. */
		if (result == ISC_R_SUCCESS)
			result = ISC_R_FAILURE;
		goto cleanup;
	}

	if (result != ISC_R_SUCCESS) {
		/* Parsing failed but no errors have been logged. */
494
		cfg_parser_error(pctx, 0, "parsing failed");
495 496 497 498 499 500 501 502 503 504 505 506 507
		goto cleanup;
	}

	CHECK(parse_eof(pctx));

	*ret = obj;
	return (ISC_R_SUCCESS);

 cleanup:
	CLEANUP_OBJ(obj);
	return (result);
}

508 509
isc_result_t
cfg_parse_file(cfg_parser_t *pctx, const char *filename,
510
	       const cfg_type_t *type, cfg_obj_t **ret)
511 512 513 514 515 516 517 518 519 520 521 522 523 524
{
	isc_result_t result;

	REQUIRE(filename != NULL);

	CHECK(parser_openfile(pctx, filename));
	CHECK(parse2(pctx, type, ret));
 cleanup:
	return (result);
}


isc_result_t
cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
525
	const cfg_type_t *type, cfg_obj_t **ret)
526 527 528
{
	isc_result_t result;
	REQUIRE(buffer != NULL);
Automatic Updater's avatar
Automatic Updater committed
529
	CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
530 531 532 533 534
	CHECK(parse2(pctx, type, ret));
 cleanup:
	return (result);
}

535 536 537 538 539 540 541 542
void
cfg_parser_attach(cfg_parser_t *src, cfg_parser_t **dest) {
	REQUIRE(src != NULL);
	REQUIRE(dest != NULL && *dest == NULL);
	isc_refcount_increment(&src->references, NULL);
	*dest = src;
}

543 544 545
void
cfg_parser_destroy(cfg_parser_t **pctxp) {
	cfg_parser_t *pctx = *pctxp;
546 547 548 549 550 551 552 553 554 555 556 557 558 559
	unsigned int refs;

	isc_refcount_decrement(&pctx->references, &refs);
	if (refs == 0) {
		isc_lex_destroy(&pctx->lexer);
		/*
		 * Cleaning up open_files does not
		 * close the files; that was already done
		 * by closing the lexer.
		 */
		CLEANUP_OBJ(pctx->open_files);
		CLEANUP_OBJ(pctx->closed_files);
		isc_mem_put(pctx->mctx, pctx, sizeof(*pctx));
	}
560 561 562 563 564 565
	*pctxp = NULL;
}

/*
 * void
 */
566 567
isc_result_t
cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
568
	UNUSED(type);
569
	return (cfg_create_obj(pctx, &cfg_type_void, ret));
570 571
}

572
void
573
cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
574 575 576
	UNUSED(pctx);
	UNUSED(obj);
}
577

578 579 580 581 582 583
void
cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
	UNUSED(pctx);
	UNUSED(type);
}

584
isc_boolean_t
585
cfg_obj_isvoid(const cfg_obj_t *obj) {
586 587 588 589
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_void));
}

590 591 592
cfg_type_t cfg_type_void = {
	"void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
	NULL };
593 594 595 596 597


/*
 * uint32
 */
598 599
isc_result_t
cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
Automatic Updater's avatar
Automatic Updater committed
600
	isc_result_t result;
601 602 603
	cfg_obj_t *obj = NULL;
	UNUSED(type);

604
	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
605
	if (pctx->token.type != isc_tokentype_number) {
606
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
607 608 609
		return (ISC_R_UNEXPECTEDTOKEN);
	}

610
	CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
611 612 613 614 615 616 617

	obj->value.uint32 = pctx->token.value.as_ulong;
	*ret = obj;
 cleanup:
	return (result);
}

618 619 620
void
cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
	cfg_print_chars(pctx, s, strlen(s));
621 622
}

623 624
void
cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
625
	char buf[32];
626
	snprintf(buf, sizeof(buf), "%u", u);
627
	cfg_print_cstr(pctx, buf);
628 629
}

630
void
631
cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
632
	cfg_print_rawuint(pctx, obj->value.uint32);
633 634
}

635
isc_boolean_t
636
cfg_obj_isuint32(const cfg_obj_t *obj) {
637 638 639 640
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
}

641
isc_uint32_t
642
cfg_obj_asuint32(const cfg_obj_t *obj) {
643 644 645 646
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
	return (obj->value.uint32);
}

647 648 649 650
cfg_type_t cfg_type_uint32 = {
	"integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
	&cfg_rep_uint32, NULL
};
651 652


653 654 655 656
/*
 * uint64
 */
isc_boolean_t
657
cfg_obj_isuint64(const cfg_obj_t *obj) {
658 659 660 661 662
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
}

isc_uint64_t
663
cfg_obj_asuint64(const cfg_obj_t *obj) {
664 665 666 667
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
	return (obj->value.uint64);
}

668
void
669
cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
670
	char buf[32];
671 672
	snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
		 obj->value.uint64);
673
	cfg_print_cstr(pctx, buf);
674 675
}

676 677 678
cfg_type_t cfg_type_uint64 = {
	"64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
	&cfg_rep_uint64, NULL
679 680
};

681
/*
682 683
 * qstring (quoted string), ustring (unquoted string), astring
 * (any string)
684 685 686 687
 */

/* Create a string object from a null-terminated C string. */
static isc_result_t
688
create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
689 690 691 692 693 694
	      cfg_obj_t **ret)
{
	isc_result_t result;
	cfg_obj_t *obj = NULL;
	int len;

695
	CHECK(cfg_create_obj(pctx, type, &obj));
696 697 698 699
	len = strlen(contents);
	obj->value.string.length = len;
	obj->value.string.base = isc_mem_get(pctx->mctx, len + 1);
	if (obj->value.string.base == 0) {
700
		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
701 702 703 704 705 706 707 708 709 710
		return (ISC_R_NOMEMORY);
	}
	memcpy(obj->value.string.base, contents, len);
	obj->value.string.base[len] = '\0';

	*ret = obj;
 cleanup:
	return (result);
}

711 712
isc_result_t
cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
Automatic Updater's avatar
Automatic Updater committed
713
	isc_result_t result;
714 715
	UNUSED(type);

716
	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
717
	if (pctx->token.type != isc_tokentype_qstring) {
718
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
719 720 721
		return (ISC_R_UNEXPECTEDTOKEN);
	}
	return (create_string(pctx,
722
			      TOKEN_STRING(pctx),
723 724 725 726 727 728 729
			      &cfg_type_qstring,
			      ret));
 cleanup:
	return (result);
}

static isc_result_t
730
parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
Automatic Updater's avatar
Automatic Updater committed
731
	isc_result_t result;
732 733 734 735
	UNUSED(type);

	CHECK(cfg_gettoken(pctx, 0));
	if (pctx->token.type != isc_tokentype_string) {
736
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
737 738 739
		return (ISC_R_UNEXPECTEDTOKEN);
	}
	return (create_string(pctx,
740
			      TOKEN_STRING(pctx),
741 742 743 744 745 746
			      &cfg_type_ustring,
			      ret));
 cleanup:
	return (result);
}

747
isc_result_t
748 749 750
cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
		  cfg_obj_t **ret)
{
Automatic Updater's avatar
Automatic Updater committed
751
	isc_result_t result;
752 753 754 755
	UNUSED(type);

	CHECK(cfg_getstringtoken(pctx));
	return (create_string(pctx,
756
			      TOKEN_STRING(pctx),
757 758 759 760 761 762
			      &cfg_type_qstring,
			      ret));
 cleanup:
	return (result);
}

763 764
isc_boolean_t
cfg_is_enum(const char *s, const char *const *enums) {
765
	const char * const *p;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
766 767
	for (p = enums; *p != NULL; p++) {
		if (strcasecmp(*p, s) == 0)
768
			return (ISC_TRUE);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
769
	}
770 771 772 773
	return (ISC_FALSE);
}

static isc_result_t
774
check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
775
	const char *s = obj->value.string.base;
776
	if (cfg_is_enum(s, enums))
777
		return (ISC_R_SUCCESS);
778
	cfg_parser_error(pctx, 0, "'%s' unexpected", s);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
779 780 781
	return (ISC_R_UNEXPECTEDTOKEN);
}

782 783
isc_result_t
cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
Automatic Updater's avatar
Automatic Updater committed
784
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
785 786 787 788 789 790
	cfg_obj_t *obj = NULL;
	CHECK(parse_ustring(pctx, NULL, &obj));
	CHECK(check_enum(pctx, obj, type->of));
	*ret = obj;
	return (ISC_R_SUCCESS);
 cleanup:
Automatic Updater's avatar
Automatic Updater committed
791
	CLEANUP_OBJ(obj);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
792 793 794
	return (result);
}

795 796 797 798 799 800 801 802
void
cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
	const char * const *p;
	cfg_print_chars(pctx, "( ", 2);
	for (p = type->of; *p != NULL; p++) {
		cfg_print_cstr(pctx, *p);
		if (p[1] != NULL)
			cfg_print_chars(pctx, " | ", 3);
803
	}
804
	cfg_print_chars(pctx, " )", 2);
805 806
}

807
void
808
cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
809
	cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
810 811 812
}

static void
813
print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
814 815 816
	cfg_print_chars(pctx, "\"", 1);
	cfg_print_ustring(pctx, obj);
	cfg_print_chars(pctx, "\"", 1);
817 818 819 820
}

static void
free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
821 822
	isc_mem_put(pctx->mctx, obj->value.string.base,
		    obj->value.string.length + 1);
823 824
}

825
isc_boolean_t
826
cfg_obj_isstring(const cfg_obj_t *obj) {
827 828 829 830
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_string));
}

831
const char *
832
cfg_obj_asstring(const cfg_obj_t *obj) {
833 834 835 836
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
	return (obj->value.string.base);
}

837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
/* Quoted string only */
cfg_type_t cfg_type_qstring = {
	"quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal,
	&cfg_rep_string, NULL
};

/* Unquoted string only */
cfg_type_t cfg_type_ustring = {
	"string", parse_ustring, cfg_print_ustring, cfg_doc_terminal,
	&cfg_rep_string, NULL
};

/* Any string (quoted or unquoted); printed with quotes */
cfg_type_t cfg_type_astring = {
	"string", cfg_parse_astring, print_qstring, cfg_doc_terminal,
	&cfg_rep_string, NULL
};

/*
 * Booleans
 */

859
isc_boolean_t
860
cfg_obj_isboolean(const cfg_obj_t *obj) {
861 862 863 864
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
}

Brian Wellington's avatar
Brian Wellington committed
865
isc_boolean_t
866
cfg_obj_asboolean(const cfg_obj_t *obj) {
Brian Wellington's avatar
Brian Wellington committed
867 868 869 870
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
	return (obj->value.boolean);
}

871 872
isc_result_t
cfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
873
{
Automatic Updater's avatar
Automatic Updater committed
874
	isc_result_t result;
875 876 877 878 879 880 881 882 883
	isc_boolean_t value;
	cfg_obj_t *obj = NULL;
	UNUSED(type);

	result = cfg_gettoken(pctx, 0);
	if (result != ISC_R_SUCCESS)
		return (result);

	if (pctx->token.type != isc_tokentype_string)
884
		goto bad_boolean;
885

886 887 888
	if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
	    (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
	    (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
889
		value = ISC_TRUE;
890 891 892
	} else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
		   (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
		   (strcmp(TOKEN_STRING(pctx), "0") == 0)) {
893 894 895 896 897
		value = ISC_FALSE;
	} else {
		goto bad_boolean;
	}

898
	CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
899 900 901 902 903
	obj->value.boolean = value;
	*ret = obj;
	return (result);

 bad_boolean:
904
	cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
905 906 907 908 909 910
	return (ISC_R_UNEXPECTEDTOKEN);

 cleanup:
	return (result);
}

911 912
void
cfg_print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
913
	if (obj->value.boolean)
914
		cfg_print_chars(pctx, "yes", 3);
915
	else
916
		cfg_print_chars(pctx, "no", 2);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
917
}
918

919
cfg_type_t cfg_type_boolean = {
920
	"boolean", cfg_parse_boolean, cfg_print_boolean, cfg_doc_terminal,
921
	&cfg_rep_boolean, NULL
922 923
};

924
/*
925
 * Lists.
926 927
 */

928 929
isc_result_t
cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
930
	isc_result_t result;
931
	CHECK(cfg_create_obj(pctx, type, obj));
932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948
	ISC_LIST_INIT((*obj)->value.list);
 cleanup:
	return (result);
}

static isc_result_t
create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
	cfg_listelt_t *elt;
	elt = isc_mem_get(pctx->mctx, sizeof(*elt));
	if (elt == NULL)
		return (ISC_R_NOMEMORY);
	elt->obj = NULL;
	ISC_LINK_INIT(elt, link);
	*eltp = elt;
	return (ISC_R_SUCCESS);
}

949 950 951 952 953 954
static void
free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
	cfg_obj_destroy(pctx, &elt->obj);
	isc_mem_put(pctx->mctx, elt, sizeof(*elt));
}

955 956 957 958 959 960 961 962
static void
free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
	cfg_listelt_t *elt, *next;
	for (elt = ISC_LIST_HEAD(obj->value.list);
	     elt != NULL;
	     elt = next)
	{
		next = ISC_LIST_NEXT(elt, link);
963
		free_list_elt(pctx, elt);
964 965 966
	}
}

967 968 969
isc_result_t
cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
		  cfg_listelt_t **ret)
970 971 972 973 974 975 976
{
	isc_result_t result;
	cfg_listelt_t *elt = NULL;
	cfg_obj_t *value = NULL;

	CHECK(create_listelt(pctx, &elt));

977
	result = cfg_parse_obj(pctx, elttype, &value);
978 979 980 981 982 983 984 985 986 987 988 989 990
	if (result != ISC_R_SUCCESS)
		goto cleanup;

	elt->obj = value;

	*ret = elt;
	return (ISC_R_SUCCESS);

 cleanup:
	isc_mem_put(pctx->mctx, elt, sizeof(*elt));
	return (result);
}

991 992 993 994
/*
 * Parse a homogeneous list whose elements are of type 'elttype'
 * and where each element is terminated by a semicolon.
 */
995
static isc_result_t
996
parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
997 998
{
	cfg_obj_t *listobj = NULL;
999
	const cfg_type_t *listof = listtype->of;
1000
	isc_result_t result;
1001
	cfg_listelt_t *elt = NULL;
1002

1003
	CHECK(cfg_create_list(pctx, listtype, &listobj));
1004 1005 1006 1007

	for (;;) {
		CHECK(cfg_peektoken(pctx, 0));
		if (pctx->token.type == isc_tokentype_special &&
1008
		    pctx->token.value.as_char == /*{*/ '}')
1009
			break;
1010
		CHECK(cfg_parse_listelt(pctx, listof, &elt));
1011 1012
		CHECK(parse_semicolon(pctx));
		ISC_LIST_APPEND(listobj->value.list, elt, link);
1013
		elt = NULL;
1014 1015 1016 1017 1018
	}
	*ret = listobj;
	return (ISC_R_SUCCESS);

 cleanup:
1019 1020
	if (elt != NULL)
		free_list_elt(pctx, elt);
1021 1022 1023 1024 1025
	CLEANUP_OBJ(listobj);
	return (result);
}

static void
1026 1027 1028
print_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
	const cfg_list_t *list = &obj->value.list;
	const cfg_listelt_t *elt;
1029 1030 1031 1032 1033

	for (elt = ISC_LIST_HEAD(*list);
	     elt != NULL;
	     elt = ISC_LIST_NEXT(elt, link)) {
		print_indent(pctx);
1034 1035
		cfg_print_obj(pctx, elt->obj);
		cfg_print_chars(pctx, ";\n", 2);
1036 1037 1038
	}
}

1039 1040 1041
isc_result_t
cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
		     cfg_obj_t **ret)
1042 1043
{
	isc_result_t result;
1044
	CHECK(cfg_parse_special(pctx, '{'));
1045