parser.c 76.8 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>

Evan Hunt's avatar
Evan Hunt committed
16 17
#include <stdlib.h>

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

#include <isccfg/cfg.h>
34
#include <isccfg/grammar.h>
35
#include <isccfg/log.h>
36

37
/* Shorthand */
38 39
#define CAT CFG_LOGCATEGORY_CONFIG
#define MOD CFG_LOGMODULE_PARSER
40 41 42

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

43 44
#define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)

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

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

static void
67
print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
68 69

static void
70
free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
71

72
static isc_result_t
73
create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
74 75

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

static void
80
free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
81

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

static void
86
free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
87 88 89

static isc_result_t
parse_symtab_elt(cfg_parser_t *pctx, const char *name,
90 91
		 cfg_type_t *elttype, isc_symtab_t *symtab,
		 isc_boolean_t callback);
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107

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).
 */
108

109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
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 };
124 125 126 127 128

/*
 * Configuration type definitions.
 */

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

/* Functions. */

137
void
138
cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
139 140 141
	REQUIRE(pctx != NULL);
	REQUIRE(obj != NULL);

142 143 144
	obj->type->print(pctx, obj);
}

145 146
void
cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
147 148 149
	REQUIRE(pctx != NULL);
	REQUIRE(text != NULL);

150 151 152 153 154
	pctx->f(pctx->closure, text, len);
}

static void
print_open(cfg_printer_t *pctx) {
155 156 157 158 159 160
	if ((pctx->flags & CFG_PRINTER_ONELINE) != 0)
		cfg_print_cstr(pctx, "{ ");
	else {
		cfg_print_cstr(pctx, "{\n");
		pctx->indent++;
	}
161 162
}

163 164
void
cfg_print_indent(cfg_printer_t *pctx) {
165
	int indent = pctx->indent;
166 167 168 169
	if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) {
		cfg_print_cstr(pctx, " ");
		return;
	}
170
	while (indent > 0) {
171
		cfg_print_cstr(pctx, "\t");
172 173 174 175 176 177
		indent--;
	}
}

static void
print_close(cfg_printer_t *pctx) {
178 179
	if ((pctx->flags & CFG_PRINTER_ONELINE) == 0) {
		pctx->indent--;
180
		cfg_print_indent(pctx);
181 182
	}
	cfg_print_cstr(pctx, "}");
183 184
}

185 186
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
187
	isc_result_t result;
188 189 190 191 192

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

Andreas Gustafsson's avatar
Andreas Gustafsson committed
193 194 195
	result = type->parse(pctx, type, ret);
	if (result != ISC_R_SUCCESS)
		return (result);
196
	ENSURE(*ret != NULL);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
197
	return (ISC_R_SUCCESS);
198 199 200
}

void
201
cfg_print(const cfg_obj_t *obj,
202 203
	  void (*f)(void *closure, const char *text, int textlen),
	  void *closure)
204
{
205 206 207
	REQUIRE(obj != NULL);
	REQUIRE(f != NULL);

208 209 210 211 212 213 214
	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)
215 216
{
	cfg_printer_t pctx;
217 218 219 220

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

221 222 223
	pctx.f = f;
	pctx.closure = closure;
	pctx.indent = 0;
224
	pctx.flags = flags;
225 226 227
	obj->type->print(&pctx, obj);
}

228
/* Tuples. */
Automatic Updater's avatar
Automatic Updater committed
229

230 231
isc_result_t
cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
232
	isc_result_t result;
233 234
	const cfg_tuplefielddef_t *fields = type->of;
	const cfg_tuplefielddef_t *f;
235 236 237
	cfg_obj_t *obj = NULL;
	unsigned int nfields = 0;
	int i;
238

239 240 241 242
	REQUIRE(pctx != NULL);
	REQUIRE(type != NULL);
	REQUIRE(ret != NULL && *ret == NULL);

243
	for (f = fields; f->name != NULL; f++)
244 245
		nfields++;

246
	CHECK(cfg_create_obj(pctx, type, &obj));
247 248
	obj->value.tuple = isc_mem_get(pctx->mctx,
				       nfields * sizeof(cfg_obj_t *));
249 250 251 252
	if (obj->value.tuple == NULL) {
		result = ISC_R_NOMEMORY;
		goto cleanup;
	}
253
	for (f = fields, i = 0; f->name != NULL; f++, i++)
254
		obj->value.tuple[i] = NULL;
255 256 257 258
	*ret = obj;
	return (ISC_R_SUCCESS);

 cleanup:
259 260
	if (obj != NULL)
		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
261 262 263
	return (result);
}

264 265
isc_result_t
cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
266 267
{
	isc_result_t result;
268 269
	const cfg_tuplefielddef_t *fields = type->of;
	const cfg_tuplefielddef_t *f;
270 271
	cfg_obj_t *obj = NULL;
	unsigned int i;
272

273 274 275 276
	REQUIRE(pctx != NULL);
	REQUIRE(type != NULL);
	REQUIRE(ret != NULL && *ret == NULL);

277
	CHECK(cfg_create_tuple(pctx, type, &obj));
278
	for (f = fields, i = 0; f->name != NULL; f++, i++)
279
		CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
280 281 282 283 284 285 286 287 288

	*ret = obj;
	return (ISC_R_SUCCESS);

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

289
void
290
cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
291
	unsigned int i;
292
	const cfg_tuplefielddef_t *fields;
293
	const cfg_tuplefielddef_t *f;
294
	isc_boolean_t need_space = ISC_FALSE;
295

296 297 298 299 300
	REQUIRE(pctx != NULL);
	REQUIRE(obj != NULL);

	fields = obj->type->of;

301
	for (f = fields, i = 0; f->name != NULL; f++, i++) {
302
		const cfg_obj_t *fieldobj = obj->value.tuple[i];
303
		if (need_space && fieldobj->type->rep != &cfg_rep_void)
304
			cfg_print_cstr(pctx, " ");
305
		cfg_print_obj(pctx, fieldobj);
306 307
		need_space = ISC_TF(need_space ||
				    fieldobj->type->print != cfg_print_void);
308 309 310 311 312
	}
}

void
cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
313
	const cfg_tuplefielddef_t *fields;
314 315 316
	const cfg_tuplefielddef_t *f;
	isc_boolean_t need_space = ISC_FALSE;

317 318 319 320 321
	REQUIRE(pctx != NULL);
	REQUIRE(type != NULL);

	fields = type->of;

322 323
	for (f = fields; f->name != NULL; f++) {
		if (need_space)
324
			cfg_print_cstr(pctx, " ");
325 326
		cfg_doc_obj(pctx, f->type);
		need_space = ISC_TF(f->type->print != cfg_print_void);
327 328 329 330 331 332
	}
}

static void
free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
	unsigned int i;
333 334
	const cfg_tuplefielddef_t *fields = obj->type->of;
	const cfg_tuplefielddef_t *f;
335 336
	unsigned int nfields = 0;

337 338 339
	if (obj->value.tuple == NULL)
		return;

340
	for (f = fields, i = 0; f->name != NULL; f++, i++) {
341 342 343 344 345 346 347
		CLEANUP_OBJ(obj->value.tuple[i]);
		nfields++;
	}
	isc_mem_put(pctx->mctx, obj->value.tuple,
		    nfields * sizeof(cfg_obj_t *));
}

348
isc_boolean_t
349
cfg_obj_istuple(const cfg_obj_t *obj) {
350 351 352 353
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
}

354 355
const cfg_obj_t *
cfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) {
356
	unsigned int i;
357 358
	const cfg_tuplefielddef_t *fields;
	const cfg_tuplefielddef_t *f;
Automatic Updater's avatar
Automatic Updater committed
359

360
	REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
361
	REQUIRE(name != NULL);
362 363 364 365 366 367 368 369 370 371

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

372 373
isc_result_t
cfg_parse_special(cfg_parser_t *pctx, int special) {
Automatic Updater's avatar
Automatic Updater committed
374
	isc_result_t result;
375 376 377

	REQUIRE(pctx != NULL);

378 379 380 381 382
	CHECK(cfg_gettoken(pctx, 0));
	if (pctx->token.type == isc_tokentype_special &&
	    pctx->token.value.as_char == special)
		return (ISC_R_SUCCESS);

383
	cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
384 385 386 387 388 389 390 391 392 393 394 395 396 397
	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
398
	isc_result_t result;
399

400 401 402 403 404
	CHECK(cfg_gettoken(pctx, 0));
	if (pctx->token.type == isc_tokentype_special &&
	    pctx->token.value.as_char == ';')
		return (ISC_R_SUCCESS);

405
	cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
406 407 408 409 410 411 412 413 414 415
	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
416
	isc_result_t result;
417

418 419 420 421 422
	CHECK(cfg_gettoken(pctx, 0));

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

423
	cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
424 425
	return (ISC_R_UNEXPECTEDTOKEN);
 cleanup:
Andreas Gustafsson's avatar
spacing  
Andreas Gustafsson committed
426
	return (result);
427 428 429
}

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

431
static cfg_type_t cfg_type_filelist = {
432
	"filelist", NULL, print_list, NULL, &cfg_rep_list,
433 434 435 436
	&cfg_type_qstring
};

isc_result_t
437
cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
438 439 440 441 442 443 444 445 446 447 448
	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);

449 450 451
	pctx->mctx = NULL;
	isc_mem_attach(mctx, &pctx->mctx);

452 453
	result = isc_refcount_init(&pctx->references, 1);
	if (result != ISC_R_SUCCESS) {
454
		isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
455 456 457
		return (result);
	}

458 459 460 461 462
	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
463
	pctx->warnings = 0;
464 465
	pctx->open_files = NULL;
	pctx->closed_files = NULL;
466
	pctx->line = 0;
467 468
	pctx->callback = NULL;
	pctx->callbackarg = NULL;
Mark Andrews's avatar
Mark Andrews committed
469
	pctx->token.type = isc_tokentype_unknown;
Automatic Updater's avatar
Automatic Updater committed
470
	pctx->flags = 0;
471
	pctx->buf_name = NULL;
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487

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

488 489
	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
490 491 492 493 494 495 496

	*ret = pctx;
	return (ISC_R_SUCCESS);

 cleanup:
	if (pctx->lexer != NULL)
		isc_lex_destroy(&pctx->lexer);
497 498
	CLEANUP_OBJ(pctx->open_files);
	CLEANUP_OBJ(pctx->closed_files);
499
	isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
500 501 502 503
	return (result);
}

static isc_result_t
504
parser_openfile(cfg_parser_t *pctx, const char *filename) {
505 506 507 508 509 510
	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) {
511
		cfg_parser_error(pctx, 0, "open: %s: %s",
512 513 514 515 516 517 518
			     filename, isc_result_totext(result));
		goto cleanup;
	}

	CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
	CHECK(create_listelt(pctx, &elt));
	elt->obj = stringobj;
519
	ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
520 521 522 523 524 525 526

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

527 528 529 530 531
void
cfg_parser_setcallback(cfg_parser_t *pctx,
		       cfg_parsecallback_t callback,
		       void *arg)
{
532 533
	REQUIRE(pctx != NULL);

534 535 536 537
	pctx->callback = callback;
	pctx->callbackarg = arg;
}

538 539 540 541 542 543 544 545 546 547 548 549 550 551
void
cfg_parser_reset(cfg_parser_t *pctx) {
	REQUIRE(pctx != NULL);

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

	pctx->seen_eof = ISC_FALSE;
	pctx->ungotten = ISC_FALSE;
	pctx->errors = 0;
	pctx->warnings = 0;
	pctx->line = 0;
}

552 553 554 555 556
/*
 * Parse a configuration using a pctx where a lexer has already
 * been set up with a source.
 */
static isc_result_t
557
parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
558 559 560
	isc_result_t result;
	cfg_obj_t *obj = NULL;

561
	result = cfg_parse_obj(pctx, type, &obj);
562 563 564 565 566 567 568 569 570 571

	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. */
572 573
		cfg_parser_error(pctx, 0, "parsing failed: %s",
				 isc_result_totext(result));
574 575 576 577 578 579 580 581 582 583 584 585 586
		goto cleanup;
	}

	CHECK(parse_eof(pctx));

	*ret = obj;
	return (ISC_R_SUCCESS);

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

587 588
isc_result_t
cfg_parse_file(cfg_parser_t *pctx, const char *filename,
589
	       const cfg_type_t *type, cfg_obj_t **ret)
590 591
{
	isc_result_t result;
592
	cfg_listelt_t *elt;
593

594
	REQUIRE(pctx != NULL);
595
	REQUIRE(filename != NULL);
596 597
	REQUIRE(type != NULL);
	REQUIRE(ret != NULL && *ret == NULL);
598 599

	CHECK(parser_openfile(pctx, filename));
600 601 602 603 604 605 606 607 608

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

609 610 611 612 613 614 615
 cleanup:
	return (result);
}


isc_result_t
cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
616
	const cfg_type_t *type, cfg_obj_t **ret)
617
{
618
	return (cfg_parse_buffer4(pctx, buffer, NULL, 0, type, 0, ret));
619 620 621 622
}

isc_result_t
cfg_parse_buffer2(cfg_parser_t *pctx, isc_buffer_t *buffer,
623
		  const char *file, const cfg_type_t *type,
624
		  cfg_obj_t **ret)
625
{
626
	return (cfg_parse_buffer4(pctx, buffer, file, 0, type, 0, ret));
627 628 629 630 631 632
}

isc_result_t
cfg_parse_buffer3(cfg_parser_t *pctx, isc_buffer_t *buffer,
		  const char *file, unsigned int line,
		  const cfg_type_t *type, cfg_obj_t **ret)
633 634 635 636 637 638 639 640 641
{
	return (cfg_parse_buffer4(pctx, buffer, file, line, type, 0, ret));
}

isc_result_t
cfg_parse_buffer4(cfg_parser_t *pctx, isc_buffer_t *buffer,
                  const char *file, unsigned int line,
                  const cfg_type_t *type, unsigned int flags,
		  cfg_obj_t **ret)
642 643
{
	isc_result_t result;
644

645 646
	REQUIRE(pctx != NULL);
	REQUIRE(type != NULL);
647
	REQUIRE(buffer != NULL);
648
	REQUIRE(ret != NULL && *ret == NULL);
649
	REQUIRE((flags & ~(CFG_PCTX_NODEPRECATED)) == 0);
650

Automatic Updater's avatar
Automatic Updater committed
651
	CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
652 653

	pctx->buf_name = file;
654
	pctx->flags = flags;
655 656 657 658

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

659
	CHECK(parse2(pctx, type, ret));
660
	pctx->buf_name = NULL;
661

662 663 664 665
 cleanup:
	return (result);
}

666 667 668 669
void
cfg_parser_attach(cfg_parser_t *src, cfg_parser_t **dest) {
	REQUIRE(src != NULL);
	REQUIRE(dest != NULL && *dest == NULL);
670

671 672 673 674
	isc_refcount_increment(&src->references, NULL);
	*dest = src;
}

675 676
void
cfg_parser_destroy(cfg_parser_t **pctxp) {
677
	cfg_parser_t *pctx;
678 679
	unsigned int refs;

680 681 682 683 684
	REQUIRE(pctxp != NULL && *pctxp != NULL);

	pctx = *pctxp;
	*pctxp = NULL;

685 686 687 688 689 690 691 692 693 694
	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);
695
		isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
696
	}
697 698 699 700 701
}

/*
 * void
 */
702 703
isc_result_t
cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
704 705 706
	REQUIRE(pctx != NULL);
	REQUIRE(ret != NULL && *ret == NULL);

707
	UNUSED(type);
708

709
	return (cfg_create_obj(pctx, &cfg_type_void, ret));
710 711
}

712
void
713
cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
714 715 716 717

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

718 719 720
	UNUSED(pctx);
	UNUSED(obj);
}
721

722 723
void
cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
724 725 726 727

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

728 729 730 731
	UNUSED(pctx);
	UNUSED(type);
}

732
isc_boolean_t
733
cfg_obj_isvoid(const cfg_obj_t *obj) {
734 735 736 737
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_void));
}

738
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_void = {
739 740
	"void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
	NULL };
741

742 743 744 745 746 747 748 749 750 751
/*
 * 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;
752
	isc_uint64_t percent;
753

754 755 756
	REQUIRE(pctx != NULL);
	REQUIRE(ret != NULL && *ret == NULL);

757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
	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);
	}

	percent = isc_string_touint64(TOKEN_STRING(pctx), &endp, 10);
	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));
774
	obj->value.uint32 = (isc_uint32_t)percent;
775 776 777 778 779 780 781 782 783 784 785
	*ret = obj;

 cleanup:
	return (result);
}

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

786 787 788
	REQUIRE(pctx != NULL);
	REQUIRE(obj != NULL);

789 790 791 792 793 794 795 796 797 798 799
	n = snprintf(buf, sizeof(buf), "%u%%", obj->value.uint32);
	INSIST(n > 0 && (size_t)n < sizeof(buf));
	cfg_print_chars(pctx, buf, strlen(buf));
}

isc_uint32_t
cfg_obj_aspercentage(const cfg_obj_t *obj) {
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_percentage);
	return (obj->value.uint32);
}

800
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_percentage = {
801 802 803 804 805 806 807 808 809 810
	"percentage", cfg_parse_percentage, cfg_print_percentage,
	cfg_doc_terminal, &cfg_rep_percentage, NULL
};

isc_boolean_t
cfg_obj_ispercentage(const cfg_obj_t *obj) {
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_percentage));
}

Evan Hunt's avatar
Evan Hunt committed
811 812 813 814 815 816 817 818 819 820 821 822
/*
 * 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;

823 824 825
	REQUIRE(pctx != NULL);
	REQUIRE(ret != NULL && *ret == NULL);

Evan Hunt's avatar
Evan Hunt committed
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 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870
	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;

871 872 873
	REQUIRE(pctx != NULL);
	REQUIRE(obj != NULL);

Evan Hunt's avatar
Evan Hunt committed
874 875 876 877 878 879 880 881 882 883 884 885
	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));
}

isc_uint32_t
cfg_obj_asfixedpoint(const cfg_obj_t *obj) {
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_fixedpoint);
	return (obj->value.uint32);
}

886
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_fixedpoint = {
Evan Hunt's avatar
Evan Hunt committed
887 888 889
	"fixedpoint", cfg_parse_fixedpoint, cfg_print_fixedpoint,
	cfg_doc_terminal, &cfg_rep_fixedpoint, NULL
};
890

891 892 893 894 895 896
isc_boolean_t
cfg_obj_isfixedpoint(const cfg_obj_t *obj) {
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_fixedpoint));
}

897 898 899
/*
 * uint32
 */
900 901
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
902
	isc_result_t result;
903
	cfg_obj_t *obj = NULL;
904 905 906 907

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

908 909
	UNUSED(type);

910
	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
911
	if (pctx->token.type != isc_tokentype_number) {
912
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
913 914 915
		return (ISC_R_UNEXPECTEDTOKEN);
	}

916
	CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
917 918 919 920 921 922 923

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

924 925 926
void
cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
	cfg_print_chars(pctx, s, strlen(s));
927 928
}

929 930
void
cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
931
	char buf[32];
932

933
	snprintf(buf, sizeof(buf), "%u", u);
934
	cfg_print_cstr(pctx, buf);
935 936
}

937
void
938
cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
939
	cfg_print_rawuint(pctx, obj->value.uint32);
940 941
}

942
isc_boolean_t
943
cfg_obj_isuint32(const cfg_obj_t *obj) {
944 945 946 947
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
}

948
isc_uint32_t
949
cfg_obj_asuint32(const cfg_obj_t *obj) {
950 951 952 953
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
	return (obj->value.uint32);
}

954
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_uint32 = {
955 956 957
	"integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
	&cfg_rep_uint32, NULL
};
958 959


960 961 962 963
/*
 * uint64
 */
isc_boolean_t
964
cfg_obj_isuint64(const cfg_obj_t *obj) {
965 966 967 968 969
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
}

isc_uint64_t
970
cfg_obj_asuint64(const cfg_obj_t *obj) {
971 972 973 974
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
	return (obj->value.uint64);
}

975
void
976
cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
977
	char buf[32];
978

979 980
	snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
		 obj->value.uint64);
981
	cfg_print_cstr(pctx, buf);
982 983
}

984
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_uint64 = {
985 986
	"64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
	&cfg_rep_uint64, NULL
987 988
};

989
/*
990 991
 * qstring (quoted string), ustring (unquoted string), astring
 * (any string)
992 993 994 995
 */

/* Create a string object from a null-terminated C string. */
static isc_result_t
996
create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
997 998 999 1000 1001 1002
	      cfg_obj_t **ret)
{
	isc_result_t result;
	cfg_obj_t *obj = NULL;
	int len;

1003
	CHECK(cfg_create_obj(pctx, type, &obj));
1004 1005 1006 1007
	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) {
1008
		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
1009 1010
		return (ISC_R_NOMEMORY);
	}
1011
	memmove(obj->value.string.base, contents, len);
1012 1013 1014 1015 1016 1017 1018
	obj->value.string.base[len] = '\0';

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

1019 1020
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
1021
	isc_result_t result;
1022 1023 1024 1025

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

1026 1027
	UNUSED(type);

1028
	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
1029
	if (pctx->token.type != isc_tokentype_qstring) {
1030
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
1031 1032
		return (ISC_R_UNEXPECTEDTOKEN);
	}
1033 1034
	return (create_string(pctx, TOKEN_STRING(pctx),
			      &cfg_type_qstring, ret));
1035 1036 1037 1038 1039
 cleanup:
	return (result);
}

static isc_result_t
1040
parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
Automatic Updater's avatar
Automatic Updater committed
1041
	isc_result_t result;
1042

1043 1044 1045 1046
	UNUSED(type);

	CHECK(cfg_gettoken(pctx, 0));
	if (pctx->token.type != isc_tokentype_string) {
1047
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
1048 1049 1050
		return (ISC_R_UNEXPECTEDTOKEN);
	}
	return (create_string(pctx,
1051
			      TOKEN_STRING(pctx