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

#include <config.h>

Evan Hunt's avatar
Evan Hunt committed
22 23
#include <stdlib.h>

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

#include <isccfg/cfg.h>
40
#include <isccfg/grammar.h>
41
#include <isccfg/log.h>
42

43
/* Shorthand */
44 45
#define CAT CFG_LOGCATEGORY_CONFIG
#define MOD CFG_LOGMODULE_PARSER
46 47 48

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

49 50
#define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)

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

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

static void
73
print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
74 75

static void
76
free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
77

78
static isc_result_t
79
create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
80 81

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

static void
86
free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
87

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

static void
92
free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
93 94 95

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

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

115
cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
116
cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
117 118 119 120 121 122 123 124
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 };
Evan Hunt's avatar
Evan Hunt committed
125
cfg_rep_t cfg_rep_fixedpoint = { "fixedpoint", free_noop };
126 127 128 129 130

/*
 * Configuration type definitions.
 */

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

/* Functions. */

139
void
140
cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
141 142 143
	obj->type->print(pctx, obj);
}

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

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

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

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

181 182
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
183 184 185 186 187 188 189
	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);
190 191 192
}

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

213
/* Tuples. */
Automatic Updater's avatar
Automatic Updater committed
214

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

224
	for (f = fields; f->name != NULL; f++)
225 226
		nfields++;

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

 cleanup:
240 241
	if (obj != NULL)
		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
242 243 244
	return (result);
}

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

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

	*ret = obj;
	return (ISC_R_SUCCESS);

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

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

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

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)
291
			cfg_print_cstr(pctx, " ");
292 293
		cfg_doc_obj(pctx, f->type);
		need_space = ISC_TF(f->type->print != cfg_print_void);
294 295 296 297 298 299
	}
}

static void
free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
	unsigned int i;
300 301
	const cfg_tuplefielddef_t *fields = obj->type->of;
	const cfg_tuplefielddef_t *f;
302 303
	unsigned int nfields = 0;

304 305 306
	if (obj->value.tuple == NULL)
		return;

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

315
isc_boolean_t
316
cfg_obj_istuple(const cfg_obj_t *obj) {
317 318 319 320
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
}

321 322
const cfg_obj_t *
cfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) {
323
	unsigned int i;
324 325
	const cfg_tuplefielddef_t *fields;
	const cfg_tuplefielddef_t *f;
Automatic Updater's avatar
Automatic Updater committed
326

327 328 329 330 331 332 333 334 335 336 337
	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);
}

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

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

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

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

384
	cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
385 386
	return (ISC_R_UNEXPECTEDTOKEN);
 cleanup:
Andreas Gustafsson's avatar
spacing  
Andreas Gustafsson committed
387
	return (result);
388 389 390
}

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

392
static cfg_type_t cfg_type_filelist = {
393
	"filelist", NULL, print_list, NULL, &cfg_rep_list,
394 395 396 397
	&cfg_type_qstring
};

isc_result_t
398
cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
399 400 401 402 403 404 405 406 407 408 409
	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);

410 411 412
	pctx->mctx = NULL;
	isc_mem_attach(mctx, &pctx->mctx);

413 414
	result = isc_refcount_init(&pctx->references, 1);
	if (result != ISC_R_SUCCESS) {
415
		isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
416 417 418
		return (result);
	}

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

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

449 450
	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
451 452 453 454 455 456 457

	*ret = pctx;
	return (ISC_R_SUCCESS);

 cleanup:
	if (pctx->lexer != NULL)
		isc_lex_destroy(&pctx->lexer);
458 459
	CLEANUP_OBJ(pctx->open_files);
	CLEANUP_OBJ(pctx->closed_files);
460
	isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
461 462 463 464
	return (result);
}

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

	CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
	CHECK(create_listelt(pctx, &elt));
	elt->obj = stringobj;
480
	ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
481 482 483 484 485 486 487

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

488 489 490 491 492 493 494 495 496
void
cfg_parser_setcallback(cfg_parser_t *pctx,
		       cfg_parsecallback_t callback,
		       void *arg)
{
	pctx->callback = callback;
	pctx->callbackarg = arg;
}

497 498 499 500 501 502 503 504 505 506 507 508 509 510
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;
}

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

520
	result = cfg_parse_obj(pctx, type, &obj);
521 522 523 524 525 526 527 528 529 530

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

	CHECK(parse_eof(pctx));

	*ret = obj;
	return (ISC_R_SUCCESS);

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

545 546
isc_result_t
cfg_parse_file(cfg_parser_t *pctx, const char *filename,
547
	       const cfg_type_t *type, cfg_obj_t **ret)
548 549
{
	isc_result_t result;
550
	cfg_listelt_t *elt;
551 552 553 554

	REQUIRE(filename != NULL);

	CHECK(parser_openfile(pctx, filename));
555 556 557 558 559 560 561 562 563

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

564 565 566 567 568 569 570
 cleanup:
	return (result);
}


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

583
	REQUIRE(buffer != NULL);
584

Automatic Updater's avatar
Automatic Updater committed
585
	CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
586
	pctx->buf_name = bufname;
587
	CHECK(parse2(pctx, type, ret));
588
	pctx->buf_name = NULL;
589

590 591 592 593
 cleanup:
	return (result);
}

594 595 596 597 598 599 600 601
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;
}

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

/*
 * void
 */
625 626
isc_result_t
cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
627
	UNUSED(type);
628
	return (cfg_create_obj(pctx, &cfg_type_void, ret));
629 630
}

631
void
632
cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
633 634 635
	UNUSED(pctx);
	UNUSED(obj);
}
636

637 638 639 640 641 642
void
cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
	UNUSED(pctx);
	UNUSED(type);
}

643
isc_boolean_t
644
cfg_obj_isvoid(const cfg_obj_t *obj) {
645 646 647 648
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_void));
}

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

Evan Hunt's avatar
Evan Hunt committed
653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
/*
 * 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);
}

cfg_type_t cfg_type_fixedpoint = {
	"fixedpoint", cfg_parse_fixedpoint, cfg_print_fixedpoint,
	cfg_doc_terminal, &cfg_rep_fixedpoint, NULL
};
726 727 728 729

/*
 * uint32
 */
730 731
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
732
	isc_result_t result;
733 734 735
	cfg_obj_t *obj = NULL;
	UNUSED(type);

736
	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
737
	if (pctx->token.type != isc_tokentype_number) {
738
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
739 740 741
		return (ISC_R_UNEXPECTEDTOKEN);
	}

742
	CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
743 744 745 746 747 748 749

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

750 751 752
void
cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
	cfg_print_chars(pctx, s, strlen(s));
753 754
}

755 756
void
cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
757
	char buf[32];
758
	snprintf(buf, sizeof(buf), "%u", u);
759
	cfg_print_cstr(pctx, buf);
760 761
}

762
void
763
cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
764
	cfg_print_rawuint(pctx, obj->value.uint32);
765 766
}

767
isc_boolean_t
768
cfg_obj_isuint32(const cfg_obj_t *obj) {
769 770 771 772
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
}

773
isc_uint32_t
774
cfg_obj_asuint32(const cfg_obj_t *obj) {
775 776 777 778
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
	return (obj->value.uint32);
}

779 780 781 782
cfg_type_t cfg_type_uint32 = {
	"integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
	&cfg_rep_uint32, NULL
};
783 784


785 786 787 788
/*
 * uint64
 */
isc_boolean_t
789
cfg_obj_isuint64(const cfg_obj_t *obj) {
790 791 792 793 794
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
}

isc_uint64_t
795
cfg_obj_asuint64(const cfg_obj_t *obj) {
796 797 798 799
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
	return (obj->value.uint64);
}

800
void
801
cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
802
	char buf[32];
803 804
	snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
		 obj->value.uint64);
805
	cfg_print_cstr(pctx, buf);
806 807
}

808 809 810
cfg_type_t cfg_type_uint64 = {
	"64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
	&cfg_rep_uint64, NULL
811 812
};

813
/*
814 815
 * qstring (quoted string), ustring (unquoted string), astring
 * (any string)
816 817 818 819
 */

/* Create a string object from a null-terminated C string. */
static isc_result_t
820
create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
821 822 823 824 825 826
	      cfg_obj_t **ret)
{
	isc_result_t result;
	cfg_obj_t *obj = NULL;
	int len;

827
	CHECK(cfg_create_obj(pctx, type, &obj));
828 829 830 831
	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) {
832
		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
833 834
		return (ISC_R_NOMEMORY);
	}
835
	memmove(obj->value.string.base, contents, len);
836 837 838 839 840 841 842
	obj->value.string.base[len] = '\0';

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

843 844
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
845
	isc_result_t result;
846 847
	UNUSED(type);

848
	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
849
	if (pctx->token.type != isc_tokentype_qstring) {
850
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
851 852 853
		return (ISC_R_UNEXPECTEDTOKEN);
	}
	return (create_string(pctx,
854
			      TOKEN_STRING(pctx),
855 856 857 858 859 860 861
			      &cfg_type_qstring,
			      ret));
 cleanup:
	return (result);
}

static isc_result_t
862
parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
Automatic Updater's avatar
Automatic Updater committed
863
	isc_result_t result;
864 865 866 867
	UNUSED(type);

	CHECK(cfg_gettoken(pctx, 0));
	if (pctx->token.type != isc_tokentype_string) {
868
		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
869 870 871
		return (ISC_R_UNEXPECTEDTOKEN);
	}
	return (create_string(pctx,
872
			      TOKEN_STRING(pctx),
873 874 875 876 877 878
			      &cfg_type_ustring,
			      ret));
 cleanup:
	return (result);
}

879
isc_result_t
880 881 882
cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
		  cfg_obj_t **ret)
{
Automatic Updater's avatar
Automatic Updater committed
883
	isc_result_t result;
884 885 886 887
	UNUSED(type);

	CHECK(cfg_getstringtoken(pctx));
	return (create_string(pctx,
888
			      TOKEN_STRING(pctx),
889 890 891 892 893 894
			      &cfg_type_qstring,
			      ret));
 cleanup:
	return (result);
}

895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910
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);
}

911 912
isc_boolean_t
cfg_is_enum(const char *s, const char *const *enums) {
913
	const char * const *p;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
914 915
	for (p = enums; *p != NULL; p++) {
		if (strcasecmp(*p, s) == 0)
916
			return (ISC_TRUE);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
917
	}
918 919 920 921
	return (ISC_FALSE);
}

static isc_result_t
922
check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
923
	const char *s = obj->value.string.base;
924
	if (cfg_is_enum(s, enums))
925
		return (ISC_R_SUCCESS);
926
	cfg_parser_error(pctx, 0, "'%s' unexpected", s);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
927 928 929
	return (ISC_R_UNEXPECTEDTOKEN);
}

930 931
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
932
	isc_result_t result;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
933 934 935 936 937 938
	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
939
	CLEANUP_OBJ(obj);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
940 941 942
	return (result);
}

943 944 945
void
cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
	const char * const *p;
946
	cfg_print_cstr(pctx, "( ");
947 948 949
	for (p = type->of; *p != NULL; p++) {
		cfg_print_cstr(pctx, *p);
		if (p[1] != NULL)
950
			cfg_print_cstr(pctx, " | ");
951
	}
952
	cfg_print_cstr(pctx, " )");
953 954
}

955
void
956
cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
957
	cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
958 959 960
}

static void
961
print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
962
	cfg_print_cstr(pctx, "\"");
963
	cfg_print_ustring(pctx, obj);
964
	cfg_print_cstr(pctx, "\"");
965 966
}

967 968
static void
print_sstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
969
	cfg_print_cstr(pctx, "\"");
970 971 972
	if ((pctx->flags & CFG_PRINTER_XKEY) != 0) {
		unsigned int len = obj->value.string.length;
		while (len-- > 0)
973
			cfg_print_cstr(pctx, "?");
974 975
	} else
		cfg_print_ustring(pctx, obj);
976
	cfg_print_cstr(pctx, "\"");
977 978
}

979 980
static void
free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
981 982
	isc_mem_put(pctx->mctx, obj->value.string.base,
		    obj->value.string.length + 1);
983 984
}

985
isc_boolean_t
986
cfg_obj_isstring(const cfg_obj_t *obj) {
987 988 989 990
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_string));
}

991
const char *
992
cfg_obj_asstring(const cfg_obj_t *obj) {
993 994 995 996
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
	return (obj->value.string.base);
}

997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014
/* 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
};

1015 1016 1017 1018 1019 1020 1021 1022 1023
/*
 * 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
};

1024 1025 1026 1027
/*
 * Booleans
 */

1028
isc_boolean_t
1029
cfg_obj_isboolean(const cfg_obj_t *obj) {
1030 1031 1032 1033
	REQUIRE(obj != NULL);
	return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
}

Brian Wellington's avatar
Brian Wellington committed
1034
isc_boolean_t
1035
cfg_obj_asboolean(const cfg_obj_t *obj) {
Brian Wellington's avatar
Brian Wellington committed
1036 1037 1038 1039
	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
	return (obj->value.boolean);
}

1040 1041
isc_result_t
cfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1042
{
Automatic Updater's avatar
Automatic Updater committed
1043
	isc_result_t result;
1044 1045 1046 1047 1048 1049 1050 1051 1052
	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)
1053
		goto bad_boolean;
1054

1055 1056 1057
	if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
	    (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
	    (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
1058
		value = ISC_TRUE;