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

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_buffer3(pctx, buffer, NULL, 0, type, 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 627 628 629 630 631 632
{
	return (cfg_parse_buffer3(pctx, buffer, file, 0, type, ret));
}

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
{
	isc_result_t result;
635

636 637
	REQUIRE(pctx != NULL);
	REQUIRE(type != NULL);
638
	REQUIRE(buffer != NULL);
639
	REQUIRE(ret != NULL && *ret == NULL);
640

Automatic Updater's avatar
Automatic Updater committed
641
	CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
642 643 644 645 646 647

	pctx->buf_name = file;

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

648
	CHECK(parse2(pctx, type, ret));
649
	pctx->buf_name = NULL;
650

651 652 653 654
 cleanup:
	return (result);
}

655 656 657 658
void
cfg_parser_attach(cfg_parser_t *src, cfg_parser_t **dest) {
	REQUIRE(src != NULL);
	REQUIRE(dest != NULL && *dest == NULL);
659

660 661 662 663
	isc_refcount_increment(&src->references, NULL);
	*dest = src;
}

664 665
void
cfg_parser_destroy(cfg_parser_t **pctxp) {
666
	cfg_parser_t *pctx;
667 668
	unsigned int refs;

669 670 671 672 673
	REQUIRE(pctxp != NULL && *pctxp != NULL);

	pctx = *pctxp;
	*pctxp = NULL;

674 675 676 677 678 679 680 681 682 683
	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);
684
		isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
685
	}
686 687 688 689 690
}

/*
 * void
 */
691 692
isc_result_t
cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
693 694 695
	REQUIRE(pctx != NULL);
	REQUIRE(ret != NULL && *ret == NULL);

696
	UNUSED(type);
697

698
	return (cfg_create_obj(pctx, &cfg_type_void, ret));
699 700
}

701
void
702
cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
703 704 705 706

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

707 708 709
	UNUSED(pctx);
	UNUSED(obj);
}
710

711 712
void
cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
713 714 715 716

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

717 718 719 720
	UNUSED(pctx);
	UNUSED(type);
}

721
isc_boolean_t
722
cfg_obj_isvoid(const cfg_obj_t *obj) {
723 724 725 726
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_void));
}

727
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_void = {
728 729
	"void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
	NULL };
730

731 732 733 734 735 736 737 738 739 740
/*
 * 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;
741
	isc_uint64_t percent;
742

743 744 745
	REQUIRE(pctx != NULL);
	REQUIRE(ret != NULL && *ret == NULL);

746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
	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));
763
	obj->value.uint32 = (isc_uint32_t)percent;
764 765 766 767 768 769 770 771 772 773 774
	*ret = obj;

 cleanup:
	return (result);
}

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

775 776 777
	REQUIRE(pctx != NULL);
	REQUIRE(obj != NULL);

778 779 780 781 782 783 784 785 786 787 788
	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);
}

789
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_percentage = {
790 791 792 793 794 795 796 797 798 799
	"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
800 801 802 803 804 805 806 807 808 809 810 811
/*
 * 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;

812 813 814
	REQUIRE(pctx != NULL);
	REQUIRE(ret != NULL && *ret == NULL);

Evan Hunt's avatar
Evan Hunt committed
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 854 855 856 857 858 859
	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;

860 861 862
	REQUIRE(pctx != NULL);
	REQUIRE(obj != NULL);

Evan Hunt's avatar
Evan Hunt committed
863 864 865 866 867 868 869 870 871 872 873 874
	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);
}

875
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_fixedpoint = {
Evan Hunt's avatar
Evan Hunt committed
876 877 878
	"fixedpoint", cfg_parse_fixedpoint, cfg_print_fixedpoint,
	cfg_doc_terminal, &cfg_rep_fixedpoint, NULL
};
879

880 881 882 883 884 885
isc_boolean_t
cfg_obj_isfixedpoint(const cfg_obj_t *obj) {
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_fixedpoint));
}

886 887 888
/*
 * uint32
 */
889 890
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
891
	isc_result_t result;
892
	cfg_obj_t *obj = NULL;
893 894 895 896

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

897 898
	UNUSED(type);

899
	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
900
	if (pctx->token.type != isc_tokentype_number) {
901
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
902 903 904
		return (ISC_R_UNEXPECTEDTOKEN);
	}

905
	CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
906 907 908 909 910 911 912

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

913 914 915
void
cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
	cfg_print_chars(pctx, s, strlen(s));
916 917
}

918 919
void
cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
920
	char buf[32];
921

922
	snprintf(buf, sizeof(buf), "%u", u);
923
	cfg_print_cstr(pctx, buf);
924 925
}

926
void
927
cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
928
	cfg_print_rawuint(pctx, obj->value.uint32);
929 930
}

931
isc_boolean_t
932
cfg_obj_isuint32(const cfg_obj_t *obj) {
933 934 935 936
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
}

937
isc_uint32_t
938
cfg_obj_asuint32(const cfg_obj_t *obj) {
939 940 941 942
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
	return (obj->value.uint32);
}

943
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_uint32 = {
944 945 946
	"integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
	&cfg_rep_uint32, NULL
};
947 948


949 950 951 952
/*
 * uint64
 */
isc_boolean_t
953
cfg_obj_isuint64(const cfg_obj_t *obj) {
954 955 956 957 958
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
}

isc_uint64_t
959
cfg_obj_asuint64(const cfg_obj_t *obj) {
960 961 962 963
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
	return (obj->value.uint64);
}

964
void
965
cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
966
	char buf[32];
967

968 969
	snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
		 obj->value.uint64);
970
	cfg_print_cstr(pctx, buf);
971 972
}

973
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_uint64 = {
974 975
	"64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
	&cfg_rep_uint64, NULL
976 977
};

978
/*
979 980
 * qstring (quoted string), ustring (unquoted string), astring
 * (any string)
981 982 983 984
 */

/* Create a string object from a null-terminated C string. */
static isc_result_t
985
create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
986 987 988 989 990 991
	      cfg_obj_t **ret)
{
	isc_result_t result;
	cfg_obj_t *obj = NULL;
	int len;

992
	CHECK(cfg_create_obj(pctx, type, &obj));
993 994 995 996
	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) {
997
		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
998 999
		return (ISC_R_NOMEMORY);
	}
1000
	memmove(obj->value.string.base, contents, len);
1001 1002 1003 1004 1005 1006 1007
	obj->value.string.base[len] = '\0';

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

1008 1009
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
1010
	isc_result_t result;
1011 1012 1013 1014

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

1015 1016
	UNUSED(type);

1017
	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
1018
	if (pctx->token.type != isc_tokentype_qstring) {
1019
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
1020 1021
		return (ISC_R_UNEXPECTEDTOKEN);
	}
1022 1023
	return (create_string(pctx, TOKEN_STRING(pctx),
			      &cfg_type_qstring, ret));
1024 1025 1026 1027 1028
 cleanup:
	return (result);
}

static isc_result_t
1029
parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
Automatic Updater's avatar
Automatic Updater committed
1030
	isc_result_t result;
1031

1032 1033 1034 1035
	UNUSED(type);

	CHECK(cfg_gettoken(pctx, 0));
	if (pctx->token.type != isc_tokentype_string) {
1036
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
1037 1038 1039
		return (ISC_R_UNEXPECTEDTOKEN);
	}
	return (create_string(pctx,
1040
			      TOKEN_STRING(pctx),
1041 1042 1043 1044 1045 1046
			      &cfg_type_ustring,
			      ret));
 cleanup:
	return (result);
}

1047
isc_result_t
1048 1049 1050
cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
		  cfg_obj_t **ret)
{
Automatic Updater's avatar
Automatic Updater committed
1051
	isc_result_t result;
1052 1053 1054 1055

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