parser.c 84.2 KB
Newer Older
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4 5 6
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 8 9
 *
 * See the COPYRIGHT file distributed with this work for additional
 * information regarding copyright ownership.
10 11
 */

12
/*! \file */
13 14 15

#include <config.h>

16
#include <inttypes.h>
17
#include <stdbool.h>
18
#include <stdlib.h>
Evan Hunt's avatar
Evan Hunt committed
19

20 21
#include <isc/buffer.h>
#include <isc/dir.h>
22
#include <isc/formatcheck.h>
23 24 25 26 27
#include <isc/lex.h>
#include <isc/log.h>
#include <isc/mem.h>
#include <isc/net.h>
#include <isc/netaddr.h>
28
#include <isc/netscope.h>
29
#include <isc/print.h>
30 31 32
#include <isc/string.h>
#include <isc/sockaddr.h>
#include <isc/symtab.h>
33
#include <isc/util.h>
34 35

#include <isccfg/cfg.h>
36
#include <isccfg/grammar.h>
37
#include <isccfg/log.h>
38

39
/* Shorthand */
40 41
#define CAT CFG_LOGCATEGORY_CONFIG
#define MOD CFG_LOGMODULE_PARSER
42 43 44

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

45 46
#define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)

47 48
/* Check a return value. */
#define CHECK(op) 						\
Automatic Updater's avatar
Automatic Updater committed
49
	do { result = (op); 					\
50 51 52 53 54 55 56 57 58 59 60 61 62
		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
63
free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
64

65
static isc_result_t
66
parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
67 68

static void
69
print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
70 71

static void
72
free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
73

74
static isc_result_t
75
create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
76 77

static isc_result_t
78 79
create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
	      cfg_obj_t **ret);
80 81

static void
82
free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
83

84
static isc_result_t
85
create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
86 87

static void
88
free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
89 90 91

static isc_result_t
parse_symtab_elt(cfg_parser_t *pctx, const char *name,
92
		 cfg_type_t *elttype, isc_symtab_t *symtab,
93
		 bool callback);
94 95 96 97 98 99 100 101

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

static isc_result_t
cfg_getstringtoken(cfg_parser_t *pctx);

static void
102
parser_complain(cfg_parser_t *pctx, bool is_warning,
103 104
		unsigned int flags, const char *format, va_list args);

Mark Andrews's avatar
Mark Andrews committed
105 106 107 108 109 110 111 112 113 114 115
#ifdef HAVE_GEOIP
static isc_result_t
parse_geoip(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);

static void
print_geoip(cfg_printer_t *pctx, const cfg_obj_t *obj);

static void
doc_geoip(cfg_printer_t *pctx, const cfg_type_t *type);
#endif /* HAVE_GEOIP */

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

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_string = { "string", free_string };
LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_map = { "map", free_map };
LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_list = { "list", free_list };
LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_netprefix =
	{ "netprefix", free_noop };
LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_void = { "void", free_noop };
LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_fixedpoint =
	{ "fixedpoint", free_noop };
LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_percentage =
	{ "percentage", free_noop };
137 138 139 140 141

/*
 * Configuration type definitions.
 */

142
/*%
143 144 145
 * An implicit list.  These are formed by clauses that occur multiple times.
 */
static cfg_type_t cfg_type_implicitlist = {
146
	"implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL };
147 148 149

/* Functions. */

150
void
151
cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
152 153 154
	REQUIRE(pctx != NULL);
	REQUIRE(obj != NULL);

155 156 157
	obj->type->print(pctx, obj);
}

158 159
void
cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
160 161 162
	REQUIRE(pctx != NULL);
	REQUIRE(text != NULL);

163 164 165 166 167
	pctx->f(pctx->closure, text, len);
}

static void
print_open(cfg_printer_t *pctx) {
168 169 170 171 172 173
	if ((pctx->flags & CFG_PRINTER_ONELINE) != 0)
		cfg_print_cstr(pctx, "{ ");
	else {
		cfg_print_cstr(pctx, "{\n");
		pctx->indent++;
	}
174 175
}

176 177
void
cfg_print_indent(cfg_printer_t *pctx) {
178
	int indent = pctx->indent;
179 180 181 182
	if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) {
		cfg_print_cstr(pctx, " ");
		return;
	}
183
	while (indent > 0) {
184
		cfg_print_cstr(pctx, "\t");
185 186 187 188 189 190
		indent--;
	}
}

static void
print_close(cfg_printer_t *pctx) {
191 192
	if ((pctx->flags & CFG_PRINTER_ONELINE) == 0) {
		pctx->indent--;
193
		cfg_print_indent(pctx);
194 195
	}
	cfg_print_cstr(pctx, "}");
196 197
}

198 199
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
200
	isc_result_t result;
201 202 203 204 205

	REQUIRE(pctx != NULL);
	REQUIRE(type != NULL);
	REQUIRE(ret != NULL && *ret == NULL);

Andreas Gustafsson's avatar
Andreas Gustafsson committed
206 207 208
	result = type->parse(pctx, type, ret);
	if (result != ISC_R_SUCCESS)
		return (result);
209
	ENSURE(*ret != NULL);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
210
	return (ISC_R_SUCCESS);
211 212 213
}

void
214
cfg_print(const cfg_obj_t *obj,
215 216
	  void (*f)(void *closure, const char *text, int textlen),
	  void *closure)
217
{
218 219 220
	REQUIRE(obj != NULL);
	REQUIRE(f != NULL);

221 222 223 224 225 226 227
	cfg_printx(obj, 0, f, closure);
}

void
cfg_printx(const cfg_obj_t *obj, unsigned int flags,
	     void (*f)(void *closure, const char *text, int textlen),
	     void *closure)
228 229
{
	cfg_printer_t pctx;
230 231 232 233

	REQUIRE(obj != NULL);
	REQUIRE(f != NULL);

234 235 236
	pctx.f = f;
	pctx.closure = closure;
	pctx.indent = 0;
237
	pctx.flags = flags;
238 239 240
	obj->type->print(&pctx, obj);
}

241
/* Tuples. */
Automatic Updater's avatar
Automatic Updater committed
242

243 244
isc_result_t
cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
245
	isc_result_t result;
246 247
	const cfg_tuplefielddef_t *fields = type->of;
	const cfg_tuplefielddef_t *f;
248 249 250
	cfg_obj_t *obj = NULL;
	unsigned int nfields = 0;
	int i;
251

252 253 254 255
	REQUIRE(pctx != NULL);
	REQUIRE(type != NULL);
	REQUIRE(ret != NULL && *ret == NULL);

256
	for (f = fields; f->name != NULL; f++)
257 258
		nfields++;

259
	CHECK(cfg_create_obj(pctx, type, &obj));
260 261
	obj->value.tuple = isc_mem_get(pctx->mctx,
				       nfields * sizeof(cfg_obj_t *));
262 263 264 265
	if (obj->value.tuple == NULL) {
		result = ISC_R_NOMEMORY;
		goto cleanup;
	}
266
	for (f = fields, i = 0; f->name != NULL; f++, i++)
267
		obj->value.tuple[i] = NULL;
268 269 270 271
	*ret = obj;
	return (ISC_R_SUCCESS);

 cleanup:
272 273
	if (obj != NULL)
		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
274 275 276
	return (result);
}

277 278
isc_result_t
cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
279 280
{
	isc_result_t result;
281 282
	const cfg_tuplefielddef_t *fields = type->of;
	const cfg_tuplefielddef_t *f;
283 284
	cfg_obj_t *obj = NULL;
	unsigned int i;
285

286 287 288 289
	REQUIRE(pctx != NULL);
	REQUIRE(type != NULL);
	REQUIRE(ret != NULL && *ret == NULL);

290
	CHECK(cfg_create_tuple(pctx, type, &obj));
291
	for (f = fields, i = 0; f->name != NULL; f++, i++)
292
		CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
293 294 295 296 297 298 299 300 301

	*ret = obj;
	return (ISC_R_SUCCESS);

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

302
void
303
cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
304
	unsigned int i;
305
	const cfg_tuplefielddef_t *fields;
306
	const cfg_tuplefielddef_t *f;
307
	bool need_space = false;
308

309 310 311 312 313
	REQUIRE(pctx != NULL);
	REQUIRE(obj != NULL);

	fields = obj->type->of;

314
	for (f = fields, i = 0; f->name != NULL; f++, i++) {
315
		const cfg_obj_t *fieldobj = obj->value.tuple[i];
316
		if (need_space && fieldobj->type->rep != &cfg_rep_void)
317
			cfg_print_cstr(pctx, " ");
318
		cfg_print_obj(pctx, fieldobj);
319 320
		need_space = (need_space ||
			      fieldobj->type->print != cfg_print_void);
321 322 323 324 325
	}
}

void
cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
326
	const cfg_tuplefielddef_t *fields;
327
	const cfg_tuplefielddef_t *f;
328
	bool need_space = false;
329

330 331 332 333 334
	REQUIRE(pctx != NULL);
	REQUIRE(type != NULL);

	fields = type->of;

335 336
	for (f = fields; f->name != NULL; f++) {
		if (need_space)
337
			cfg_print_cstr(pctx, " ");
338
		cfg_doc_obj(pctx, f->type);
339
		need_space = (f->type->print != cfg_print_void);
340 341 342 343 344 345
	}
}

static void
free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
	unsigned int i;
346 347
	const cfg_tuplefielddef_t *fields = obj->type->of;
	const cfg_tuplefielddef_t *f;
348 349
	unsigned int nfields = 0;

350 351 352
	if (obj->value.tuple == NULL)
		return;

353
	for (f = fields, i = 0; f->name != NULL; f++, i++) {
354 355 356 357 358 359 360
		CLEANUP_OBJ(obj->value.tuple[i]);
		nfields++;
	}
	isc_mem_put(pctx->mctx, obj->value.tuple,
		    nfields * sizeof(cfg_obj_t *));
}

361
bool
362
cfg_obj_istuple(const cfg_obj_t *obj) {
363
	REQUIRE(obj != NULL);
364
	return (obj->type->rep == &cfg_rep_tuple);
365 366
}

367 368
const cfg_obj_t *
cfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) {
369
	unsigned int i;
370 371
	const cfg_tuplefielddef_t *fields;
	const cfg_tuplefielddef_t *f;
Automatic Updater's avatar
Automatic Updater committed
372

373
	REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
374
	REQUIRE(name != NULL);
375 376 377 378 379 380 381

	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);
382
	ISC_UNREACHABLE();
383 384
}

385 386
isc_result_t
cfg_parse_special(cfg_parser_t *pctx, int special) {
Automatic Updater's avatar
Automatic Updater committed
387
	isc_result_t result;
388 389 390

	REQUIRE(pctx != NULL);

391 392 393 394 395
	CHECK(cfg_gettoken(pctx, 0));
	if (pctx->token.type == isc_tokentype_special &&
	    pctx->token.value.as_char == special)
		return (ISC_R_SUCCESS);

396
	cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
397 398 399 400 401 402 403 404 405 406 407 408 409 410
	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
411
	isc_result_t result;
412

413 414 415 416 417
	CHECK(cfg_gettoken(pctx, 0));
	if (pctx->token.type == isc_tokentype_special &&
	    pctx->token.value.as_char == ';')
		return (ISC_R_SUCCESS);

418
	cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
419 420 421 422 423 424 425 426 427 428
	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
429
	isc_result_t result;
430

431 432 433 434 435
	CHECK(cfg_gettoken(pctx, 0));

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

436
	cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
437 438
	return (ISC_R_UNEXPECTEDTOKEN);
 cleanup:
Andreas Gustafsson's avatar
spacing  
Andreas Gustafsson committed
439
	return (result);
440 441 442
}

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

444
static cfg_type_t cfg_type_filelist = {
445
	"filelist", NULL, print_list, NULL, &cfg_rep_list,
446 447 448 449
	&cfg_type_qstring
};

isc_result_t
450
cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
451 452 453 454 455 456 457 458 459 460 461
	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);

462 463 464
	pctx->mctx = NULL;
	isc_mem_attach(mctx, &pctx->mctx);

465
	isc_refcount_init(&pctx->references, 1);
466

467 468
	pctx->lctx = lctx;
	pctx->lexer = NULL;
469 470
	pctx->seen_eof = false;
	pctx->ungotten = false;
471
	pctx->errors = 0;
Mark Andrews's avatar
Mark Andrews committed
472
	pctx->warnings = 0;
473 474
	pctx->open_files = NULL;
	pctx->closed_files = NULL;
475
	pctx->line = 0;
476 477
	pctx->callback = NULL;
	pctx->callbackarg = NULL;
Mark Andrews's avatar
Mark Andrews committed
478
	pctx->token.type = isc_tokentype_unknown;
Automatic Updater's avatar
Automatic Updater committed
479
	pctx->flags = 0;
480
	pctx->buf_name = NULL;
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496

	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));

497 498
	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
499 500 501 502 503 504 505

	*ret = pctx;
	return (ISC_R_SUCCESS);

 cleanup:
	if (pctx->lexer != NULL)
		isc_lex_destroy(&pctx->lexer);
506 507
	CLEANUP_OBJ(pctx->open_files);
	CLEANUP_OBJ(pctx->closed_files);
508
	isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
509 510 511 512
	return (result);
}

static isc_result_t
513
parser_openfile(cfg_parser_t *pctx, const char *filename) {
514 515 516 517 518 519
	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) {
520
		cfg_parser_error(pctx, 0, "open: %s: %s",
521 522 523 524 525 526 527
			     filename, isc_result_totext(result));
		goto cleanup;
	}

	CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
	CHECK(create_listelt(pctx, &elt));
	elt->obj = stringobj;
528
	ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
529 530 531 532 533 534 535

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

536 537 538 539 540
void
cfg_parser_setcallback(cfg_parser_t *pctx,
		       cfg_parsecallback_t callback,
		       void *arg)
{
541 542
	REQUIRE(pctx != NULL);

543 544 545 546
	pctx->callback = callback;
	pctx->callbackarg = arg;
}

547 548 549 550 551 552 553
void
cfg_parser_reset(cfg_parser_t *pctx) {
	REQUIRE(pctx != NULL);

	if (pctx->lexer != NULL)
		isc_lex_close(pctx->lexer);

554 555
	pctx->seen_eof = false;
	pctx->ungotten = false;
556 557 558 559 560
	pctx->errors = 0;
	pctx->warnings = 0;
	pctx->line = 0;
}

561 562 563 564 565
/*
 * Parse a configuration using a pctx where a lexer has already
 * been set up with a source.
 */
static isc_result_t
566
parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
567 568 569
	isc_result_t result;
	cfg_obj_t *obj = NULL;

570
	result = cfg_parse_obj(pctx, type, &obj);
571 572 573 574 575 576 577 578 579 580

	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. */
581 582
		cfg_parser_error(pctx, 0, "parsing failed: %s",
				 isc_result_totext(result));
583 584 585 586 587 588 589 590 591 592 593 594 595
		goto cleanup;
	}

	CHECK(parse_eof(pctx));

	*ret = obj;
	return (ISC_R_SUCCESS);

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

596 597
isc_result_t
cfg_parse_file(cfg_parser_t *pctx, const char *filename,
598
	       const cfg_type_t *type, cfg_obj_t **ret)
599 600
{
	isc_result_t result;
601
	cfg_listelt_t *elt;
602

603
	REQUIRE(pctx != NULL);
604
	REQUIRE(filename != NULL);
605 606
	REQUIRE(type != NULL);
	REQUIRE(ret != NULL && *ret == NULL);
607 608

	CHECK(parser_openfile(pctx, filename));
609 610 611 612 613 614 615 616 617

	result = parse2(pctx, type, ret);

	/* Clean up the opened file */
	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
	INSIST(elt != NULL);
	ISC_LIST_UNLINK(pctx->open_files->value.list, elt, link);
	ISC_LIST_APPEND(pctx->closed_files->value.list, elt, link);

618 619 620 621 622 623 624
 cleanup:
	return (result);
}


isc_result_t
cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
625 626 627
		 const char *file, unsigned int line,
		 const cfg_type_t *type, unsigned int flags,
		 cfg_obj_t **ret)
628 629
{
	isc_result_t result;
630

631 632
	REQUIRE(pctx != NULL);
	REQUIRE(type != NULL);
633
	REQUIRE(buffer != NULL);
634
	REQUIRE(ret != NULL && *ret == NULL);
635
	REQUIRE((flags & ~(CFG_PCTX_NODEPRECATED)) == 0);
636

Automatic Updater's avatar
Automatic Updater committed
637
	CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
638 639

	pctx->buf_name = file;
640
	pctx->flags = flags;
641 642 643 644

	if (line != 0U)
		CHECK(isc_lex_setsourceline(pctx->lexer, line));

645
	CHECK(parse2(pctx, type, ret));
646
	pctx->buf_name = NULL;
647

648 649 650 651
 cleanup:
	return (result);
}

652 653 654 655
void
cfg_parser_attach(cfg_parser_t *src, cfg_parser_t **dest) {
	REQUIRE(src != NULL);
	REQUIRE(dest != NULL && *dest == NULL);
656

657
	isc_refcount_increment(&src->references);
658 659 660
	*dest = src;
}

661 662
void
cfg_parser_destroy(cfg_parser_t **pctxp) {
663
	cfg_parser_t *pctx;
664

665 666 667 668
	REQUIRE(pctxp != NULL && *pctxp != NULL);
	pctx = *pctxp;
	*pctxp = NULL;

669
	if (isc_refcount_decrement(&pctx->references) == 1) {
670 671 672 673 674 675 676 677
		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);
678
		isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
679
	}
680 681 682 683 684
}

/*
 * void
 */
685 686
isc_result_t
cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
687 688 689
	REQUIRE(pctx != NULL);
	REQUIRE(ret != NULL && *ret == NULL);

690
	UNUSED(type);
691

692
	return (cfg_create_obj(pctx, &cfg_type_void, ret));
693 694
}

695
void
696
cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
697 698 699 700

	REQUIRE(pctx != NULL);
	REQUIRE(obj != NULL);

701 702 703
	UNUSED(pctx);
	UNUSED(obj);
}
704

705 706
void
cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
707 708 709 710

	REQUIRE(pctx != NULL);
	REQUIRE(type != NULL);

711 712 713 714
	UNUSED(pctx);
	UNUSED(type);
}

715
bool
716
cfg_obj_isvoid(const cfg_obj_t *obj) {
717
	REQUIRE(obj != NULL);
718
	return (obj->type->rep == &cfg_rep_void);
719 720
}

721
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_void = {
722 723
	"void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
	NULL };
724

725 726 727 728 729 730 731 732 733 734
/*
 * percentage
 */
isc_result_t
cfg_parse_percentage(cfg_parser_t *pctx, const cfg_type_t *type,
		     cfg_obj_t **ret)
{
	char *endp;
	isc_result_t result;
	cfg_obj_t *obj = NULL;
735
	uint64_t percent;
736

737 738 739
	REQUIRE(pctx != NULL);
	REQUIRE(ret != NULL && *ret == NULL);

740 741 742 743 744 745 746 747 748
	UNUSED(type);

	CHECK(cfg_gettoken(pctx, 0));
	if (pctx->token.type != isc_tokentype_string) {
		cfg_parser_error(pctx, CFG_LOG_NEAR,
				 "expected percentage");
		return (ISC_R_UNEXPECTEDTOKEN);
	}

749
	percent = strtoull(TOKEN_STRING(pctx), &endp, 10);
750 751 752 753 754 755 756
	if (*endp != '%' || *(endp+1) != 0) {
		cfg_parser_error(pctx, CFG_LOG_NEAR,
				 "expected percentage");
		return (ISC_R_UNEXPECTEDTOKEN);
	}

	CHECK(cfg_create_obj(pctx, &cfg_type_percentage, &obj));
757
	obj->value.uint32 = (uint32_t)percent;
758 759 760 761 762 763 764 765 766 767 768
	*ret = obj;

 cleanup:
	return (result);
}

void
cfg_print_percentage(cfg_printer_t *pctx, const cfg_obj_t *obj) {
	char buf[64];
	int n;

769 770 771
	REQUIRE(pctx != NULL);
	REQUIRE(obj != NULL);

772 773 774 775 776
	n = snprintf(buf, sizeof(buf), "%u%%", obj->value.uint32);
	INSIST(n > 0 && (size_t)n < sizeof(buf));
	cfg_print_chars(pctx, buf, strlen(buf));
}

777
uint32_t
778 779 780 781 782
cfg_obj_aspercentage(const cfg_obj_t *obj) {
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_percentage);
	return (obj->value.uint32);
}

783
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_percentage = {
784 785 786 787
	"percentage", cfg_parse_percentage, cfg_print_percentage,
	cfg_doc_terminal, &cfg_rep_percentage, NULL
};

788
bool
789 790
cfg_obj_ispercentage(const cfg_obj_t *obj) {
	REQUIRE(obj != NULL);
791
	return (obj->type->rep == &cfg_rep_percentage);
792 793
}

Evan Hunt's avatar
Evan Hunt committed
794 795 796 797 798 799 800 801 802 803 804 805
/*
 * Fixed point
 */
isc_result_t
cfg_parse_fixedpoint(cfg_parser_t *pctx, const cfg_type_t *type,
		     cfg_obj_t **ret)
{
	isc_result_t result;
	cfg_obj_t *obj = NULL;
	size_t n1, n2, n3, l;
	const char *p;

806 807 808
	REQUIRE(pctx != NULL);
	REQUIRE(ret != NULL && *ret == NULL);

Evan Hunt's avatar
Evan Hunt committed
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853
	UNUSED(type);

	CHECK(cfg_gettoken(pctx, 0));
	if (pctx->token.type != isc_tokentype_string) {
		cfg_parser_error(pctx, CFG_LOG_NEAR,
				 "expected fixed point number");
		return (ISC_R_UNEXPECTEDTOKEN);
	}


	p = TOKEN_STRING(pctx);
	l = strlen(p);
	n1 = strspn(p, "0123456789");
	n2 = strspn(p + n1, ".");
	n3 = strspn(p + n1 + n2, "0123456789");

	if ((n1 + n2 + n3 != l) || (n1 + n3 == 0) ||
	    n1 > 5 || n2 > 1 || n3 > 2) {
		cfg_parser_error(pctx, CFG_LOG_NEAR,
				 "expected fixed point number");
		return (ISC_R_UNEXPECTEDTOKEN);
	}

	CHECK(cfg_create_obj(pctx, &cfg_type_fixedpoint, &obj));

	obj->value.uint32 = strtoul(p, NULL, 10) * 100;
	switch (n3) {
	case 2:
		obj->value.uint32 += strtoul(p + n1 + n2, NULL, 10);
		break;
	case 1:
		obj->value.uint32 += strtoul(p + n1 + n2, NULL, 10) * 10;
		break;
	}
	*ret = obj;

 cleanup:
	return (result);
}

void
cfg_print_fixedpoint(cfg_printer_t *pctx, const cfg_obj_t *obj) {
	char buf[64];
	int n;

854 855 856
	REQUIRE(pctx != NULL);
	REQUIRE(obj != NULL);

Evan Hunt's avatar
Evan Hunt committed
857 858 859 860 861 862
	n = snprintf(buf, sizeof(buf), "%u.%02u",
		     obj->value.uint32/100, obj->value.uint32%100);
	INSIST(n > 0 && (size_t)n < sizeof(buf));
	cfg_print_chars(pctx, buf, strlen(buf));
}

863
uint32_t
Evan Hunt's avatar
Evan Hunt committed
864 865 866 867 868
cfg_obj_asfixedpoint(const cfg_obj_t *obj) {
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_fixedpoint);
	return (obj->value.uint32);
}

869
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_fixedpoint = {
Evan Hunt's avatar
Evan Hunt committed
870 871 872
	"fixedpoint", cfg_parse_fixedpoint, cfg_print_fixedpoint,
	cfg_doc_terminal, &cfg_rep_fixedpoint, NULL
};
873

874
bool
875 876
cfg_obj_isfixedpoint(const cfg_obj_t *obj) {
	REQUIRE(obj != NULL);
877
	return (obj->type->rep == &cfg_rep_fixedpoint);
878 879
}

880 881 882
/*
 * uint32
 */
883 884
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
885
	isc_result_t result;
886
	cfg_obj_t *obj = NULL;
887 888 889 890

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

891 892
	UNUSED(type);

893
	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
894
	if (pctx->token.type != isc_tokentype_number) {
895
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
896 897 898
		return (ISC_R_UNEXPECTEDTOKEN);
	}

899
	CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
900 901 902 903 904 905 906

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

907 908 909
void
cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
	cfg_print_chars(pctx, s, strlen(s));
910 911
}

912 913
void
cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
914
	char buf[32];
915

916
	snprintf(buf, sizeof(buf), "%u", u);
917
	cfg_print_cstr(pctx, buf);
918 919
}

920
void
921
cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
922
	cfg_print_rawuint(pctx, obj->value.uint32);
923 924
}

925
bool
926
cfg_obj_isuint32(const cfg_obj_t *obj) {
927
	REQUIRE(obj != NULL);
928
	return (obj->type->rep == &cfg_rep_uint32);
929 930
}

931
uint32_t
932
cfg_obj_asuint32(const cfg_obj_t *obj) {
933 934 935 936
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
	return (obj->value.uint32);
}

937
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_uint32 = {