parser.c 70.3 KB
Newer Older
1
/*
2
 * Copyright (C) 2000-2016  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
/*! \file */
10 11 12

#include <config.h>

Evan Hunt's avatar
Evan Hunt committed
13 14
#include <stdlib.h>

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

#include <isccfg/cfg.h>
31
#include <isccfg/grammar.h>
32
#include <isccfg/log.h>
33

34
/* Shorthand */
35 36
#define CAT CFG_LOGCATEGORY_CONFIG
#define MOD CFG_LOGMODULE_PARSER
37 38 39

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

40 41
#define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)

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

60
static isc_result_t
61
parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
62 63

static void
64
print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
65 66

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

69
static isc_result_t
70
create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
71 72

static isc_result_t
73 74
create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
	      cfg_obj_t **ret);
75 76

static void
77
free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
78

79
static isc_result_t
80
create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
81 82

static void
83
free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
84 85 86

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

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

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

/*
 * Configuration type definitions.
 */

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

/* Functions. */

134
void
135
cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
136 137 138
	obj->type->print(pctx, obj);
}

139 140
void
cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
141 142 143 144 145
	pctx->f(pctx->closure, text, len);
}

static void
print_open(cfg_printer_t *pctx) {
146 147 148 149 150 151
	if ((pctx->flags & CFG_PRINTER_ONELINE) != 0)
		cfg_print_cstr(pctx, "{ ");
	else {
		cfg_print_cstr(pctx, "{\n");
		pctx->indent++;
	}
152 153 154 155 156
}

static void
print_indent(cfg_printer_t *pctx) {
	int indent = pctx->indent;
157 158 159 160
	if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) {
		cfg_print_cstr(pctx, " ");
		return;
	}
161
	while (indent > 0) {
162
		cfg_print_cstr(pctx, "\t");
163 164 165 166 167 168
		indent--;
	}
}

static void
print_close(cfg_printer_t *pctx) {
169 170 171 172 173
	if ((pctx->flags & CFG_PRINTER_ONELINE) == 0) {
		pctx->indent--;
		print_indent(pctx);
	}
	cfg_print_cstr(pctx, "}");
174 175
}

176 177
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
178 179 180 181 182 183 184
	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);
185 186 187
}

void
188
cfg_print(const cfg_obj_t *obj,
189 190
	  void (*f)(void *closure, const char *text, int textlen),
	  void *closure)
191 192 193 194 195 196 197 198
{
	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)
199 200 201 202 203
{
	cfg_printer_t pctx;
	pctx.f = f;
	pctx.closure = closure;
	pctx.indent = 0;
204
	pctx.flags = flags;
205 206 207
	obj->type->print(&pctx, obj);
}

208
/* Tuples. */
Automatic Updater's avatar
Automatic Updater committed
209

210 211
isc_result_t
cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
212
	isc_result_t result;
213 214
	const cfg_tuplefielddef_t *fields = type->of;
	const cfg_tuplefielddef_t *f;
215 216 217
	cfg_obj_t *obj = NULL;
	unsigned int nfields = 0;
	int i;
218

219
	for (f = fields; f->name != NULL; f++)
220 221
		nfields++;

222
	CHECK(cfg_create_obj(pctx, type, &obj));
223 224
	obj->value.tuple = isc_mem_get(pctx->mctx,
				       nfields * sizeof(cfg_obj_t *));
225 226 227 228
	if (obj->value.tuple == NULL) {
		result = ISC_R_NOMEMORY;
		goto cleanup;
	}
229
	for (f = fields, i = 0; f->name != NULL; f++, i++)
230
		obj->value.tuple[i] = NULL;
231 232 233 234
	*ret = obj;
	return (ISC_R_SUCCESS);

 cleanup:
235 236
	if (obj != NULL)
		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
237 238 239
	return (result);
}

240 241
isc_result_t
cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
242 243
{
	isc_result_t result;
244 245
	const cfg_tuplefielddef_t *fields = type->of;
	const cfg_tuplefielddef_t *f;
246 247
	cfg_obj_t *obj = NULL;
	unsigned int i;
248

249
	CHECK(cfg_create_tuple(pctx, type, &obj));
250
	for (f = fields, i = 0; f->name != NULL; f++, i++)
251
		CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
252 253 254 255 256 257 258 259 260

	*ret = obj;
	return (ISC_R_SUCCESS);

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

261
void
262
cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
263
	unsigned int i;
264 265
	const cfg_tuplefielddef_t *fields = obj->type->of;
	const cfg_tuplefielddef_t *f;
266
	isc_boolean_t need_space = ISC_FALSE;
267

268
	for (f = fields, i = 0; f->name != NULL; f++, i++) {
269
		const cfg_obj_t *fieldobj = obj->value.tuple[i];
270
		if (need_space && fieldobj->type->rep != &cfg_rep_void)
271
			cfg_print_cstr(pctx, " ");
272
		cfg_print_obj(pctx, fieldobj);
273 274
		need_space = ISC_TF(need_space ||
				    fieldobj->type->print != cfg_print_void);
275 276 277 278 279 280 281 282 283 284 285
	}
}

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)
286
			cfg_print_cstr(pctx, " ");
287 288
		cfg_doc_obj(pctx, f->type);
		need_space = ISC_TF(f->type->print != cfg_print_void);
289 290 291 292 293 294
	}
}

static void
free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
	unsigned int i;
295 296
	const cfg_tuplefielddef_t *fields = obj->type->of;
	const cfg_tuplefielddef_t *f;
297 298
	unsigned int nfields = 0;

299 300 301
	if (obj->value.tuple == NULL)
		return;

302
	for (f = fields, i = 0; f->name != NULL; f++, i++) {
303 304 305 306 307 308 309
		CLEANUP_OBJ(obj->value.tuple[i]);
		nfields++;
	}
	isc_mem_put(pctx->mctx, obj->value.tuple,
		    nfields * sizeof(cfg_obj_t *));
}

310
isc_boolean_t
311
cfg_obj_istuple(const cfg_obj_t *obj) {
312 313 314 315
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
}

316 317
const cfg_obj_t *
cfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) {
318
	unsigned int i;
319 320
	const cfg_tuplefielddef_t *fields;
	const cfg_tuplefielddef_t *f;
Automatic Updater's avatar
Automatic Updater committed
321

322 323 324 325 326 327 328 329 330 331 332
	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);
}

333 334
isc_result_t
cfg_parse_special(cfg_parser_t *pctx, int special) {
Automatic Updater's avatar
Automatic Updater committed
335
	isc_result_t result;
336 337 338 339 340
	CHECK(cfg_gettoken(pctx, 0));
	if (pctx->token.type == isc_tokentype_special &&
	    pctx->token.value.as_char == special)
		return (ISC_R_SUCCESS);

341
	cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
342 343 344 345 346 347 348 349 350 351 352 353 354 355
	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
356
	isc_result_t result;
357 358 359 360 361
	CHECK(cfg_gettoken(pctx, 0));
	if (pctx->token.type == isc_tokentype_special &&
	    pctx->token.value.as_char == ';')
		return (ISC_R_SUCCESS);

362
	cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
363 364 365 366 367 368 369 370 371 372
	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
373
	isc_result_t result;
374 375 376 377 378
	CHECK(cfg_gettoken(pctx, 0));

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

379
	cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
380 381
	return (ISC_R_UNEXPECTEDTOKEN);
 cleanup:
Andreas Gustafsson's avatar
spacing  
Andreas Gustafsson committed
382
	return (result);
383 384 385
}

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

387
static cfg_type_t cfg_type_filelist = {
388
	"filelist", NULL, print_list, NULL, &cfg_rep_list,
389 390 391 392
	&cfg_type_qstring
};

isc_result_t
393
cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
394 395 396 397 398 399 400 401 402 403 404
	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);

405 406 407
	pctx->mctx = NULL;
	isc_mem_attach(mctx, &pctx->mctx);

408 409
	result = isc_refcount_init(&pctx->references, 1);
	if (result != ISC_R_SUCCESS) {
410
		isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
411 412 413
		return (result);
	}

414 415 416 417 418
	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
419
	pctx->warnings = 0;
420 421
	pctx->open_files = NULL;
	pctx->closed_files = NULL;
422
	pctx->line = 0;
423 424
	pctx->callback = NULL;
	pctx->callbackarg = NULL;
Mark Andrews's avatar
Mark Andrews committed
425
	pctx->token.type = isc_tokentype_unknown;
Automatic Updater's avatar
Automatic Updater committed
426
	pctx->flags = 0;
427
	pctx->buf_name = NULL;
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443

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

444 445
	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
446 447 448 449 450 451 452

	*ret = pctx;
	return (ISC_R_SUCCESS);

 cleanup:
	if (pctx->lexer != NULL)
		isc_lex_destroy(&pctx->lexer);
453 454
	CLEANUP_OBJ(pctx->open_files);
	CLEANUP_OBJ(pctx->closed_files);
455
	isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
456 457 458 459
	return (result);
}

static isc_result_t
460
parser_openfile(cfg_parser_t *pctx, const char *filename) {
461 462 463 464 465 466
	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) {
467
		cfg_parser_error(pctx, 0, "open: %s: %s",
468 469 470 471 472 473 474
			     filename, isc_result_totext(result));
		goto cleanup;
	}

	CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
	CHECK(create_listelt(pctx, &elt));
	elt->obj = stringobj;
475
	ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
476 477 478 479 480 481 482

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

483 484 485 486 487 488 489 490 491
void
cfg_parser_setcallback(cfg_parser_t *pctx,
		       cfg_parsecallback_t callback,
		       void *arg)
{
	pctx->callback = callback;
	pctx->callbackarg = arg;
}

492 493 494 495 496 497 498 499 500 501 502 503 504 505
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;
}

506 507 508 509 510
/*
 * Parse a configuration using a pctx where a lexer has already
 * been set up with a source.
 */
static isc_result_t
511
parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
512 513 514
	isc_result_t result;
	cfg_obj_t *obj = NULL;

515
	result = cfg_parse_obj(pctx, type, &obj);
516 517 518 519 520 521 522 523 524 525

	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. */
526
		cfg_parser_error(pctx, 0, "parsing failed");
527 528 529 530 531 532 533 534 535 536 537 538 539
		goto cleanup;
	}

	CHECK(parse_eof(pctx));

	*ret = obj;
	return (ISC_R_SUCCESS);

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

540 541
isc_result_t
cfg_parse_file(cfg_parser_t *pctx, const char *filename,
542
	       const cfg_type_t *type, cfg_obj_t **ret)
543 544
{
	isc_result_t result;
545
	cfg_listelt_t *elt;
546 547 548 549

	REQUIRE(filename != NULL);

	CHECK(parser_openfile(pctx, filename));
550 551 552 553 554 555 556 557 558

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

559 560 561 562 563 564 565
 cleanup:
	return (result);
}


isc_result_t
cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
566
	const cfg_type_t *type, cfg_obj_t **ret)
567 568 569 570 571 572 573 574
{
	return (cfg_parse_buffer2(pctx, buffer, NULL, type, ret));
}

isc_result_t
cfg_parse_buffer2(cfg_parser_t *pctx, isc_buffer_t *buffer,
		  const char *bufname, const cfg_type_t *type,
		  cfg_obj_t **ret)
575 576
{
	isc_result_t result;
577

578
	REQUIRE(buffer != NULL);
579

Automatic Updater's avatar
Automatic Updater committed
580
	CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
581
	pctx->buf_name = bufname;
582
	CHECK(parse2(pctx, type, ret));
583
	pctx->buf_name = NULL;
584

585 586 587 588
 cleanup:
	return (result);
}

589 590 591 592 593 594 595 596
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;
}

597 598 599
void
cfg_parser_destroy(cfg_parser_t **pctxp) {
	cfg_parser_t *pctx = *pctxp;
600 601 602 603 604 605 606 607 608 609 610 611
	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);
612
		isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
613
	}
614 615 616 617 618 619
	*pctxp = NULL;
}

/*
 * void
 */
620 621
isc_result_t
cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
622
	UNUSED(type);
623
	return (cfg_create_obj(pctx, &cfg_type_void, ret));
624 625
}

626
void
627
cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
628 629 630
	UNUSED(pctx);
	UNUSED(obj);
}
631

632 633 634 635 636 637
void
cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
	UNUSED(pctx);
	UNUSED(type);
}

638
isc_boolean_t
639
cfg_obj_isvoid(const cfg_obj_t *obj) {
640 641 642 643
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_void));
}

644
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_void = {
645 646
	"void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
	NULL };
647

648 649 650 651 652 653 654 655 656 657
/*
 * 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;
658
	isc_uint64_t percent;
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676

	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));
677
	obj->value.uint32 = (isc_uint32_t)percent;
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699
	*ret = obj;

 cleanup:
	return (result);
}

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

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

700
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_percentage = {
701 702 703 704 705 706 707 708 709 710
	"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
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
/*
 * 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;

	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;

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

780
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_fixedpoint = {
Evan Hunt's avatar
Evan Hunt committed
781 782 783
	"fixedpoint", cfg_parse_fixedpoint, cfg_print_fixedpoint,
	cfg_doc_terminal, &cfg_rep_fixedpoint, NULL
};
784

785 786 787 788 789 790
isc_boolean_t
cfg_obj_isfixedpoint(const cfg_obj_t *obj) {
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_fixedpoint));
}

791 792 793
/*
 * uint32
 */
794 795
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
796
	isc_result_t result;
797 798 799
	cfg_obj_t *obj = NULL;
	UNUSED(type);

800
	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
801
	if (pctx->token.type != isc_tokentype_number) {
802
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
803 804 805
		return (ISC_R_UNEXPECTEDTOKEN);
	}

806
	CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
807 808 809 810 811 812 813

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

814 815 816
void
cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
	cfg_print_chars(pctx, s, strlen(s));
817 818
}

819 820
void
cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
821
	char buf[32];
822
	snprintf(buf, sizeof(buf), "%u", u);
823
	cfg_print_cstr(pctx, buf);
824 825
}

826
void
827
cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
828
	cfg_print_rawuint(pctx, obj->value.uint32);
829 830
}

831
isc_boolean_t
832
cfg_obj_isuint32(const cfg_obj_t *obj) {
833 834 835 836
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
}

837
isc_uint32_t
838
cfg_obj_asuint32(const cfg_obj_t *obj) {
839 840 841 842
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
	return (obj->value.uint32);
}

843
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_uint32 = {
844 845 846
	"integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
	&cfg_rep_uint32, NULL
};
847 848


849 850 851 852
/*
 * uint64
 */
isc_boolean_t
853
cfg_obj_isuint64(const cfg_obj_t *obj) {
854 855 856 857 858
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
}

isc_uint64_t
859
cfg_obj_asuint64(const cfg_obj_t *obj) {
860 861 862 863
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
	return (obj->value.uint64);
}

864
void
865
cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
866
	char buf[32];
867 868
	snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
		 obj->value.uint64);
869
	cfg_print_cstr(pctx, buf);
870 871
}

872
LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_uint64 = {
873 874
	"64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
	&cfg_rep_uint64, NULL
875 876
};

877
/*
878 879
 * qstring (quoted string), ustring (unquoted string), astring
 * (any string)
880 881 882 883
 */

/* Create a string object from a null-terminated C string. */
static isc_result_t
884
create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
885 886 887 888 889 890
	      cfg_obj_t **ret)
{
	isc_result_t result;
	cfg_obj_t *obj = NULL;
	int len;

891
	CHECK(cfg_create_obj(pctx, type, &obj));
892 893 894 895
	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) {
896
		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
897 898
		return (ISC_R_NOMEMORY);
	}
899
	memmove(obj->value.string.base, contents, len);
900 901 902 903 904 905 906
	obj->value.string.base[len] = '\0';

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

907 908
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
909
	isc_result_t result;
910 911
	UNUSED(type);

912
	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
913
	if (pctx->token.type != isc_tokentype_qstring) {
914
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
915 916 917
		return (ISC_R_UNEXPECTEDTOKEN);
	}
	return (create_string(pctx,
918
			      TOKEN_STRING(pctx),
919 920 921 922 923 924 925
			      &cfg_type_qstring,
			      ret));
 cleanup:
	return (result);
}

static isc_result_t
926
parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
Automatic Updater's avatar
Automatic Updater committed
927
	isc_result_t result;
928 929 930 931
	UNUSED(type);

	CHECK(cfg_gettoken(pctx, 0));
	if (pctx->token.type != isc_tokentype_string) {
932
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
933 934 935
		return (ISC_R_UNEXPECTEDTOKEN);
	}
	return (create_string(pctx,
936
			      TOKEN_STRING(pctx),
937 938 939 940 941 942
			      &cfg_type_ustring,
			      ret));
 cleanup:
	return (result);
}

943
isc_result_t
944 945 946
cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
		  cfg_obj_t **ret)
{
Automatic Updater's avatar
Automatic Updater committed
947
	isc_result_t result;
948 949 950 951
	UNUSED(type);

	CHECK(cfg_getstringtoken(pctx));
	return (create_string(pctx,
952
			      TOKEN_STRING(pctx),
953 954 955 956 957 958
			      &cfg_type_qstring,
			      ret));
 cleanup:
	return (result);
}

959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974
isc_result_t
cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type,
		  cfg_obj_t **ret)
{
	isc_result_t result;
	UNUSED(type);

	CHECK(cfg_getstringtoken(pctx));
	return (create_string(pctx,
			      TOKEN_STRING(pctx),
			      &cfg_type_sstring,
			      ret));
 cleanup:
	return (result);
}

Evan Hunt's avatar
Evan Hunt committed
975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
static isc_result_t
parse_btext(cfg_parser_t *pctx, const cfg_type_t *type,
	    cfg_obj_t **ret)
{
	isc_result_t result;
	UNUSED(type);

	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_BTEXT));
	if (pctx->token.type != isc_tokentype_btext) {
		cfg_parser_error(pctx, CFG_LOG_NEAR,
				 "expected bracketed text");
		return (ISC_R_UNEXPECTEDTOKEN);
	}
	return (create_string(pctx,
			      TOKEN_STRING(pctx),
			      &cfg_type_bracketed_text,
			      ret));
 cleanup:
	return (result);
}

static void
print_btext(cfg_printer_t *pctx, const cfg_obj_t *obj) {
	cfg_print_cstr(pctx, "{");
	cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
	print_close(pctx);
}

static void
doc_btext(cfg_printer_t *pctx, const cfg_type_t *type) {
	UNUSED(type);

	cfg_print_cstr(pctx, "{ <unspecified text> }");
}


1011 1012
isc_boolean_t
cfg_is_enum(const char *s, const char *const *enums) {
1013
	const char * const *p;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
1014 1015
	for (p = enums; *p != NULL; p++) {
		if (strcasecmp(*p, s) == 0)
1016
			return (ISC_TRUE);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
1017
	}
1018 1019 1020 1021
	return (ISC_FALSE);
}

static isc_result_t
1022
check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
1023
	const char *s = obj->value.string.base;
1024
	if (cfg_is_enum(s, enums))
1025
		return (ISC_R_SUCCESS);
1026
	cfg_parser_error(pctx, 0, "'%s' unexpected", s);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
1027 1028 1029
	return (ISC_R_UNEXPECTEDTOKEN);
}

1030 1031
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
1032
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
1033 1034 1035 1036 1037 1038
	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
1039
	CLEANUP_OBJ(obj);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
1040 1041 1042
	return (result);
}

1043 1044 1045
void
cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
	const char * const *p;
1046
	cfg_print_cstr(pctx, "( ");
1047 1048 1049
	for (p = type->of; *p != NULL; p++) {
		cfg_print_cstr(pctx, *p);
		if (p[1] != NULL)
1050
			cfg_print_cstr(pctx, " | ");
1051
	}
1052
	cfg_print_cstr(pctx, " )");
1053 1054
}

1055
void
1056
cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1057
	cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
1058 1059 1060
}

static void
1061
print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1062
	cfg_print_cstr(pctx, "\"");
1063
	cfg_print_ustring(pctx, obj);
1064
	cfg_print_cstr(pctx, "\"");
1065 1066
}

1067 1068
static void
print_sstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1069
	cfg_print_cstr(pctx, "\"");
1070 1071 1072
	if ((pctx->flags & CFG_PRINTER_XKEY) != 0) {
		unsigned int len = obj->value.string.length;
		while (len-- > 0)
1073
			cfg_print_cstr(pctx, "?");
1074 1075
	} else
		cfg_print_ustring(pctx, obj);
1076
	cfg_print_cstr(pctx, "\"");
1077 1078
}

1079 1080
static void
free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
1081 1082
	isc_mem_put(pctx->mctx, obj->value.string.base,
		    obj->value.string.length + 1);
1083 1084
}

1085
isc_boolean_t
1086
cfg_obj_isstring(const cfg_obj_t *obj) {
1087 1088 1089 1090
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_string));
}

1091
const char *