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

18
/*! \file */
19 20 21 22 23

#include <config.h>

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

#include <isccfg/cfg.h>
38
#include <isccfg/grammar.h>
39
#include <isccfg/log.h>
40

41
/* Shorthand */
42 43
#define CAT CFG_LOGCATEGORY_CONFIG
#define MOD CFG_LOGMODULE_PARSER
44 45 46

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

47 48
#define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)

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

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

static void
71
print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
72 73

static void
74
free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
75

76
static isc_result_t
77
create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
78 79

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

static void
84
free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
85

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

static void
90
free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
91 92 93

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

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

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

/*
 * Configuration type definitions.
 */

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

/* Functions. */

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

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

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

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

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

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

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

210
/* Tuples. */
Automatic Updater's avatar
Automatic Updater committed
211

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

221
	for (f = fields; f->name != NULL; f++)
222 223
		nfields++;

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

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

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

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

	*ret = obj;
	return (ISC_R_SUCCESS);

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

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

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

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

	for (f = fields; f->name != NULL; f++) {
		if (need_space)
287
			cfg_print_cstr(pctx, " ");
288 289
		cfg_doc_obj(pctx, f->type);
		need_space = ISC_TF(f->type->print != cfg_print_void);
290 291 292 293 294 295
	}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	*ret = pctx;
	return (ISC_R_SUCCESS);

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

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

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

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

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

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

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

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

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

	CHECK(parse_eof(pctx));

	*ret = obj;
	return (ISC_R_SUCCESS);

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

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

	REQUIRE(filename != NULL);

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

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

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


isc_result_t
cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
567
	const cfg_type_t *type, cfg_obj_t **ret)
568 569 570 571 572 573 574 575
{
	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)
576 577
{
	isc_result_t result;
578

579
	REQUIRE(buffer != NULL);
580

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

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

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

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

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

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

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

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

645 646 647
cfg_type_t cfg_type_void = {
	"void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
	NULL };
648 649 650 651 652


/*
 * uint32
 */
653 654
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
655
	isc_result_t result;
656 657 658
	cfg_obj_t *obj = NULL;
	UNUSED(type);

659
	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
660
	if (pctx->token.type != isc_tokentype_number) {
661
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
662 663 664
		return (ISC_R_UNEXPECTEDTOKEN);
	}

665
	CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
666 667 668 669 670 671 672

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

673 674 675
void
cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
	cfg_print_chars(pctx, s, strlen(s));
676 677
}

678 679
void
cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
680
	char buf[32];
681
	snprintf(buf, sizeof(buf), "%u", u);
682
	cfg_print_cstr(pctx, buf);
683 684
}

685
void
686
cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
687
	cfg_print_rawuint(pctx, obj->value.uint32);
688 689
}

690
isc_boolean_t
691
cfg_obj_isuint32(const cfg_obj_t *obj) {
692 693 694 695
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
}

696
isc_uint32_t
697
cfg_obj_asuint32(const cfg_obj_t *obj) {
698 699 700 701
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
	return (obj->value.uint32);
}

702 703 704 705
cfg_type_t cfg_type_uint32 = {
	"integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
	&cfg_rep_uint32, NULL
};
706 707


708 709 710 711
/*
 * uint64
 */
isc_boolean_t
712
cfg_obj_isuint64(const cfg_obj_t *obj) {
713 714 715 716 717
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
}

isc_uint64_t
718
cfg_obj_asuint64(const cfg_obj_t *obj) {
719 720 721 722
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
	return (obj->value.uint64);
}

723
void
724
cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
725
	char buf[32];
726 727
	snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
		 obj->value.uint64);
728
	cfg_print_cstr(pctx, buf);
729 730
}

731 732 733
cfg_type_t cfg_type_uint64 = {
	"64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
	&cfg_rep_uint64, NULL
734 735
};

736
/*
737 738
 * qstring (quoted string), ustring (unquoted string), astring
 * (any string)
739 740 741 742
 */

/* Create a string object from a null-terminated C string. */
static isc_result_t
743
create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
744 745 746 747 748 749
	      cfg_obj_t **ret)
{
	isc_result_t result;
	cfg_obj_t *obj = NULL;
	int len;

750
	CHECK(cfg_create_obj(pctx, type, &obj));
751 752 753 754
	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) {
755
		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
756 757
		return (ISC_R_NOMEMORY);
	}
758
	memmove(obj->value.string.base, contents, len);
759 760 761 762 763 764 765
	obj->value.string.base[len] = '\0';

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

766 767
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
768
	isc_result_t result;
769 770
	UNUSED(type);

771
	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
772
	if (pctx->token.type != isc_tokentype_qstring) {
773
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
774 775 776
		return (ISC_R_UNEXPECTEDTOKEN);
	}
	return (create_string(pctx,
777
			      TOKEN_STRING(pctx),
778 779 780 781 782 783 784
			      &cfg_type_qstring,
			      ret));
 cleanup:
	return (result);
}

static isc_result_t
785
parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
Automatic Updater's avatar
Automatic Updater committed
786
	isc_result_t result;
787 788 789 790
	UNUSED(type);

	CHECK(cfg_gettoken(pctx, 0));
	if (pctx->token.type != isc_tokentype_string) {
791
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
792 793 794
		return (ISC_R_UNEXPECTEDTOKEN);
	}
	return (create_string(pctx,
795
			      TOKEN_STRING(pctx),
796 797 798 799 800 801
			      &cfg_type_ustring,
			      ret));
 cleanup:
	return (result);
}

802
isc_result_t
803 804 805
cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
		  cfg_obj_t **ret)
{
Automatic Updater's avatar
Automatic Updater committed
806
	isc_result_t result;
807 808 809 810
	UNUSED(type);

	CHECK(cfg_getstringtoken(pctx));
	return (create_string(pctx,
811
			      TOKEN_STRING(pctx),
812 813 814 815 816 817
			      &cfg_type_qstring,
			      ret));
 cleanup:
	return (result);
}

818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833
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);
}

834 835
isc_boolean_t
cfg_is_enum(const char *s, const char *const *enums) {
836
	const char * const *p;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
837 838
	for (p = enums; *p != NULL; p++) {
		if (strcasecmp(*p, s) == 0)
839
			return (ISC_TRUE);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
840
	}
841 842 843 844
	return (ISC_FALSE);
}

static isc_result_t
845
check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
846
	const char *s = obj->value.string.base;
847
	if (cfg_is_enum(s, enums))
848
		return (ISC_R_SUCCESS);
849
	cfg_parser_error(pctx, 0, "'%s' unexpected", s);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
850 851 852
	return (ISC_R_UNEXPECTEDTOKEN);
}

853 854
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
855
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
856 857 858 859 860 861
	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
862
	CLEANUP_OBJ(obj);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
863 864 865
	return (result);
}

866 867 868
void
cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
	const char * const *p;
869
	cfg_print_cstr(pctx, "( ");
870 871 872
	for (p = type->of; *p != NULL; p++) {
		cfg_print_cstr(pctx, *p);
		if (p[1] != NULL)
873
			cfg_print_cstr(pctx, " | ");
874
	}
875
	cfg_print_cstr(pctx, " )");
876 877
}

878
void
879
cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
880
	cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
881 882 883
}

static void
884
print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
885
	cfg_print_cstr(pctx, "\"");
886
	cfg_print_ustring(pctx, obj);
887
	cfg_print_cstr(pctx, "\"");
888 889
}

890 891
static void
print_sstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
892
	cfg_print_cstr(pctx, "\"");
893 894 895
	if ((pctx->flags & CFG_PRINTER_XKEY) != 0) {
		unsigned int len = obj->value.string.length;
		while (len-- > 0)
896
			cfg_print_cstr(pctx, "?");
897 898
	} else
		cfg_print_ustring(pctx, obj);
899
	cfg_print_cstr(pctx, "\"");
900 901
}

902 903
static void
free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
904 905
	isc_mem_put(pctx->mctx, obj->value.string.base,
		    obj->value.string.length + 1);
906 907
}

908
isc_boolean_t
909
cfg_obj_isstring(const cfg_obj_t *obj) {
910 911 912 913
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_string));
}

914
const char *
915
cfg_obj_asstring(const cfg_obj_t *obj) {
916 917 918 919
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
	return (obj->value.string.base);
}

920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937
/* Quoted string only */
cfg_type_t cfg_type_qstring = {
	"quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal,
	&cfg_rep_string, NULL
};

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

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

938 939 940 941 942 943 944 945 946
/*
 * Any string (quoted or unquoted); printed with quotes.
 * If CFG_PRINTER_XKEY is set when printing the string will be '?' out.
 */
cfg_type_t cfg_type_sstring = {
	"string", cfg_parse_sstring, print_sstring, cfg_doc_terminal,
	&cfg_rep_string, NULL
};

947 948 949 950
/*
 * Booleans
 */

951
isc_boolean_t
952
cfg_obj_isboolean(const cfg_obj_t *obj) {
953 954 955 956
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
}

Brian Wellington's avatar
Brian Wellington committed
957
isc_boolean_t
958
cfg_obj_asboolean(const cfg_obj_t *obj) {
Brian Wellington's avatar
Brian Wellington committed
959 960 961 962
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
	return (obj->value.boolean);
}

963 964
isc_result_t
cfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
965
{
Automatic Updater's avatar
Automatic Updater committed
966
	isc_result_t result;
967 968 969 970 971 972 973 974 975
	isc_boolean_t value;
	cfg_obj_t *obj = NULL;
	UNUSED(type);

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

	if (pctx->token.type != isc_tokentype_string)
976
		goto bad_boolean;
977

978 979 980
	if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
	    (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
	    (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
981
		value = ISC_TRUE;
982 983 984
	} else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
		   (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
		   (strcmp(TOKEN_STRING(pctx), "0") == 0)) {
985 986 987 988 989
		value = ISC_FALSE;
	} else {
		goto bad_boolean;
	}

990
	CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
991 992 993 994 995
	obj->value.boolean = value;
	*ret = obj;
	return (result);

 bad_boolean:
996
	cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
997 998 999 1000 1001 1002
	return (ISC_R_UNEXPECTEDTOKEN);

 cleanup:
	return (result);
}

1003 1004
void
cfg_print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1005
	if (obj->value.boolean)
1006
		cfg_print_cstr(pctx, "yes");
1007
	else
1008
		cfg_print_cstr(pctx, "no");
Andreas Gustafsson's avatar
Andreas Gustafsson committed
1009
}
1010

1011
cfg_type_t cfg_type_boolean = {
1012
	"boolean", cfg_parse_boolean, cfg_print_boolean, cfg_doc_terminal,
1013
	&cfg_rep_boolean, NULL
1014 1015
};

1016
/*
1017
 * Lists.
1018 1019
 */