xref: /openbsd-src/usr.bin/dig/lib/isccfg/parser.c (revision ac19a2a78de14b2db59150dc99fcc2bd5c328bb7)
15185a700Sflorian /*
25185a700Sflorian  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
35185a700Sflorian  *
45185a700Sflorian  * Permission to use, copy, modify, and/or distribute this software for any
55185a700Sflorian  * purpose with or without fee is hereby granted, provided that the above
65185a700Sflorian  * copyright notice and this permission notice appear in all copies.
75185a700Sflorian  *
85185a700Sflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
95185a700Sflorian  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
105185a700Sflorian  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
115185a700Sflorian  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
125185a700Sflorian  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
135185a700Sflorian  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
145185a700Sflorian  * PERFORMANCE OF THIS SOFTWARE.
155185a700Sflorian  */
165185a700Sflorian 
175185a700Sflorian /*! \file */
185185a700Sflorian 
195185a700Sflorian #include <limits.h>
205185a700Sflorian #include <stdlib.h>
215185a700Sflorian #include <string.h>
225185a700Sflorian 
235185a700Sflorian #include <isc/lex.h>
245185a700Sflorian #include <isc/log.h>
255185a700Sflorian #include <isc/symtab.h>
265185a700Sflorian #include <isc/util.h>
275185a700Sflorian 
285185a700Sflorian #include <isccfg/cfg.h>
295185a700Sflorian #include <isccfg/grammar.h>
305185a700Sflorian 
315185a700Sflorian /*!
325185a700Sflorian  * Pass one of these flags to cfg_parser_error() to include the
335185a700Sflorian  * token text in log message.
345185a700Sflorian  */
355185a700Sflorian #define CFG_LOG_NEAR    0x00000001	/*%< Say "near <token>" */
365185a700Sflorian #define CFG_LOG_BEFORE  0x00000002	/*%< Say "before <token>" */
375185a700Sflorian #define CFG_LOG_NOPREP  0x00000004	/*%< Say just "<token>" */
385185a700Sflorian 
395185a700Sflorian isc_logcategory_t cfg_category = { "config",     0 };
405185a700Sflorian isc_logmodule_t cfg_module = { "isccfg/parser",      0 };
415185a700Sflorian 
425185a700Sflorian /* Shorthand */
435185a700Sflorian #define CAT &cfg_category
445185a700Sflorian #define MOD &cfg_module
455185a700Sflorian 
465185a700Sflorian #define MAP_SYM 1 	/* Unique type for isc_symtab */
475185a700Sflorian 
485185a700Sflorian #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
495185a700Sflorian 
505185a700Sflorian #define CFG_LEXOPT_QSTRING (ISC_LEXOPT_QSTRING | ISC_LEXOPT_QSTRINGMULTILINE)
515185a700Sflorian 
525185a700Sflorian /* Check a return value. */
535185a700Sflorian #define CHECK(op) 						\
545185a700Sflorian 	do { result = (op); 					\
555185a700Sflorian 		if (result != ISC_R_SUCCESS) goto cleanup; 	\
565185a700Sflorian 	} while (0)
575185a700Sflorian 
585185a700Sflorian /* Clean up a configuration object if non-NULL. */
595185a700Sflorian #define CLEANUP_OBJ(obj) \
605185a700Sflorian 	do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
615185a700Sflorian 
625185a700Sflorian /* Forward declarations of variables */
635185a700Sflorian cfg_rep_t cfg_rep_string;
645185a700Sflorian cfg_rep_t cfg_rep_list;
655185a700Sflorian 
665185a700Sflorian cfg_type_t cfg_type_qstring;
675185a700Sflorian cfg_type_t cfg_type_sstring;
685185a700Sflorian cfg_type_t cfg_type_token;
695185a700Sflorian cfg_type_t cfg_type_unsupported;
705185a700Sflorian 
715185a700Sflorian /*
725185a700Sflorian  * Forward declarations of static functions.
735185a700Sflorian  */
745185a700Sflorian 
755185a700Sflorian static isc_result_t
765185a700Sflorian cfg_gettoken(cfg_parser_t *pctx, int options);
775185a700Sflorian 
785185a700Sflorian static isc_result_t
795185a700Sflorian cfg_peektoken(cfg_parser_t *pctx, int options);
805185a700Sflorian 
815185a700Sflorian static void
825185a700Sflorian cfg_ungettoken(cfg_parser_t *pctx);
835185a700Sflorian 
845185a700Sflorian static isc_result_t
855185a700Sflorian cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
865185a700Sflorian 
875185a700Sflorian static isc_result_t
885185a700Sflorian cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
895185a700Sflorian 
905185a700Sflorian static isc_result_t
915185a700Sflorian cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
925185a700Sflorian 
935185a700Sflorian static isc_result_t
945185a700Sflorian cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
955185a700Sflorian 
965185a700Sflorian static isc_result_t
975185a700Sflorian cfg_parse_special(cfg_parser_t *pctx, int special);
985185a700Sflorian /*%< Parse a required special character 'special'. */
995185a700Sflorian 
1005185a700Sflorian static isc_result_t
1015185a700Sflorian cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
1025185a700Sflorian 
1035185a700Sflorian static isc_result_t
1045185a700Sflorian cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
1055185a700Sflorian 		  cfg_listelt_t **ret);
1065185a700Sflorian 
1075185a700Sflorian static isc_result_t
1085185a700Sflorian cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
1095185a700Sflorian 
1105185a700Sflorian static isc_result_t
1115185a700Sflorian cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
1125185a700Sflorian 
1135185a700Sflorian static void
1145185a700Sflorian cfg_parser_error(cfg_parser_t *pctx, unsigned int flags,
11587f06ebfSflorian 		 const char *fmt, ...) __attribute__((__format__(__printf__, 3, 4)));
1165185a700Sflorian 
1175185a700Sflorian static void
1185185a700Sflorian free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
1195185a700Sflorian 
1205185a700Sflorian static isc_result_t
1215185a700Sflorian create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
1225185a700Sflorian 
1235185a700Sflorian static isc_result_t
1245185a700Sflorian create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
1255185a700Sflorian 	      cfg_obj_t **ret);
1265185a700Sflorian 
1275185a700Sflorian static void
1285185a700Sflorian free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
1295185a700Sflorian 
1305185a700Sflorian static isc_result_t
1315185a700Sflorian create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
1325185a700Sflorian 
1335185a700Sflorian static void
1345185a700Sflorian free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
1355185a700Sflorian 
1365185a700Sflorian static isc_result_t
1375185a700Sflorian parse_symtab_elt(cfg_parser_t *pctx, const char *name,
1385185a700Sflorian 		 cfg_type_t *elttype, isc_symtab_t *symtab);
1395185a700Sflorian 
1405185a700Sflorian static isc_result_t
1415185a700Sflorian cfg_getstringtoken(cfg_parser_t *pctx);
1425185a700Sflorian 
1435185a700Sflorian static void
144*1fb015a8Sflorian parser_complain(cfg_parser_t *pctx, int is_warning,
1455185a700Sflorian 		unsigned int flags, const char *format, va_list args);
1465185a700Sflorian 
1475185a700Sflorian /*
1485185a700Sflorian  * Data representations.  These correspond to members of the
1495185a700Sflorian  * "value" union in struct cfg_obj (except "void", which does
1505185a700Sflorian  * not need a union member).
1515185a700Sflorian  */
1525185a700Sflorian 
1535185a700Sflorian cfg_rep_t cfg_rep_string = { "string", free_string };
1545185a700Sflorian cfg_rep_t cfg_rep_map = { "map", free_map };
1555185a700Sflorian cfg_rep_t cfg_rep_list = { "list", free_list };
1565185a700Sflorian 
1575185a700Sflorian /*
1585185a700Sflorian  * Configuration type definitions.
1595185a700Sflorian  */
1605185a700Sflorian 
1615185a700Sflorian /* Functions. */
1625185a700Sflorian 
1635185a700Sflorian static isc_result_t
cfg_parse_obj(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1645185a700Sflorian cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1655185a700Sflorian 	isc_result_t result;
1665185a700Sflorian 
1675185a700Sflorian 	REQUIRE(pctx != NULL);
1685185a700Sflorian 	REQUIRE(type != NULL);
1695185a700Sflorian 	REQUIRE(ret != NULL && *ret == NULL);
1705185a700Sflorian 
1715185a700Sflorian 	result = type->parse(pctx, type, ret);
1725185a700Sflorian 	if (result != ISC_R_SUCCESS)
1735185a700Sflorian 		return (result);
1745185a700Sflorian 	ENSURE(*ret != NULL);
1755185a700Sflorian 	return (ISC_R_SUCCESS);
1765185a700Sflorian }
1775185a700Sflorian 
1785185a700Sflorian static isc_result_t
cfg_parse_special(cfg_parser_t * pctx,int special)1795185a700Sflorian cfg_parse_special(cfg_parser_t *pctx, int special) {
1805185a700Sflorian 	isc_result_t result;
1815185a700Sflorian 
1825185a700Sflorian 	REQUIRE(pctx != NULL);
1835185a700Sflorian 
1845185a700Sflorian 	CHECK(cfg_gettoken(pctx, 0));
1855185a700Sflorian 	if (pctx->token.type == isc_tokentype_special &&
1865185a700Sflorian 	    pctx->token.value.as_char == special)
1875185a700Sflorian 		return (ISC_R_SUCCESS);
1885185a700Sflorian 
1895185a700Sflorian 	cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
1905185a700Sflorian 	return (ISC_R_UNEXPECTEDTOKEN);
1915185a700Sflorian  cleanup:
1925185a700Sflorian 	return (result);
1935185a700Sflorian }
1945185a700Sflorian 
1955185a700Sflorian /*
1965185a700Sflorian  * Parse a required semicolon.  If it is not there, log
1975185a700Sflorian  * an error and increment the error count but continue
1985185a700Sflorian  * parsing.  Since the next token is pushed back,
1995185a700Sflorian  * care must be taken to make sure it is eventually
2005185a700Sflorian  * consumed or an infinite loop may result.
2015185a700Sflorian  */
2025185a700Sflorian static isc_result_t
parse_semicolon(cfg_parser_t * pctx)2035185a700Sflorian parse_semicolon(cfg_parser_t *pctx) {
2045185a700Sflorian 	isc_result_t result;
2055185a700Sflorian 
2065185a700Sflorian 	CHECK(cfg_gettoken(pctx, 0));
2075185a700Sflorian 	if (pctx->token.type == isc_tokentype_special &&
2085185a700Sflorian 	    pctx->token.value.as_char == ';')
2095185a700Sflorian 		return (ISC_R_SUCCESS);
2105185a700Sflorian 
2115185a700Sflorian 	cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
2125185a700Sflorian 	cfg_ungettoken(pctx);
2135185a700Sflorian  cleanup:
2145185a700Sflorian 	return (result);
2155185a700Sflorian }
2165185a700Sflorian 
2175185a700Sflorian /*
2185185a700Sflorian  * Parse EOF, logging and returning an error if not there.
2195185a700Sflorian  */
2205185a700Sflorian static isc_result_t
parse_eof(cfg_parser_t * pctx)2215185a700Sflorian parse_eof(cfg_parser_t *pctx) {
2225185a700Sflorian 	isc_result_t result;
2235185a700Sflorian 
2245185a700Sflorian 	CHECK(cfg_gettoken(pctx, 0));
2255185a700Sflorian 
2265185a700Sflorian 	if (pctx->token.type == isc_tokentype_eof)
2275185a700Sflorian 		return (ISC_R_SUCCESS);
2285185a700Sflorian 
2295185a700Sflorian 	cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
2305185a700Sflorian 	return (ISC_R_UNEXPECTEDTOKEN);
2315185a700Sflorian  cleanup:
2325185a700Sflorian 	return (result);
2335185a700Sflorian }
2345185a700Sflorian 
2355185a700Sflorian /* A list of files, used internally for pctx->files. */
2365185a700Sflorian 
2375185a700Sflorian static cfg_type_t cfg_type_filelist = {
2385185a700Sflorian 	"filelist", NULL, &cfg_rep_list,
2395185a700Sflorian 	&cfg_type_qstring
2405185a700Sflorian };
2415185a700Sflorian 
2425185a700Sflorian isc_result_t
cfg_parser_create(isc_log_t * lctx,cfg_parser_t ** ret)2435185a700Sflorian cfg_parser_create(isc_log_t *lctx, cfg_parser_t **ret) {
2445185a700Sflorian 	isc_result_t result;
2455185a700Sflorian 	cfg_parser_t *pctx;
2465185a700Sflorian 	isc_lexspecials_t specials;
2475185a700Sflorian 
2485185a700Sflorian 	REQUIRE(ret != NULL && *ret == NULL);
2495185a700Sflorian 
2505185a700Sflorian 	pctx = malloc(sizeof(*pctx));
2515185a700Sflorian 	if (pctx == NULL)
2525185a700Sflorian 		return (ISC_R_NOMEMORY);
2535185a700Sflorian 
2545185a700Sflorian 	pctx->lctx = lctx;
2555185a700Sflorian 	pctx->lexer = NULL;
256*1fb015a8Sflorian 	pctx->seen_eof = 0;
257*1fb015a8Sflorian 	pctx->ungotten = 0;
2585185a700Sflorian 	pctx->errors = 0;
2595185a700Sflorian 	pctx->open_files = NULL;
2605185a700Sflorian 	pctx->closed_files = NULL;
2615185a700Sflorian 	pctx->line = 0;
2625185a700Sflorian 	pctx->token.type = isc_tokentype_unknown;
2635185a700Sflorian 	pctx->flags = 0;
2645185a700Sflorian 
2655185a700Sflorian 	memset(specials, 0, sizeof(specials));
2665185a700Sflorian 	specials['{'] = 1;
2675185a700Sflorian 	specials['}'] = 1;
2685185a700Sflorian 	specials[';'] = 1;
2695185a700Sflorian 	specials['/'] = 1;
2705185a700Sflorian 	specials['"'] = 1;
2715185a700Sflorian 	specials['!'] = 1;
2725185a700Sflorian 
2735185a700Sflorian 	CHECK(isc_lex_create(1024, &pctx->lexer));
2745185a700Sflorian 
2755185a700Sflorian 	isc_lex_setspecials(pctx->lexer, specials);
2765185a700Sflorian 	isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C |
2775185a700Sflorian 					 ISC_LEXCOMMENT_CPLUSPLUS |
2785185a700Sflorian 					 ISC_LEXCOMMENT_SHELL));
2795185a700Sflorian 
2805185a700Sflorian 	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
2815185a700Sflorian 	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
2825185a700Sflorian 
2835185a700Sflorian 	*ret = pctx;
2845185a700Sflorian 	return (ISC_R_SUCCESS);
2855185a700Sflorian 
2865185a700Sflorian  cleanup:
2875185a700Sflorian 	if (pctx->lexer != NULL)
2885185a700Sflorian 		isc_lex_destroy(&pctx->lexer);
2895185a700Sflorian 	CLEANUP_OBJ(pctx->open_files);
2905185a700Sflorian 	CLEANUP_OBJ(pctx->closed_files);
2915185a700Sflorian 	free(pctx);
2925185a700Sflorian 	return (result);
2935185a700Sflorian }
2945185a700Sflorian 
2955185a700Sflorian static isc_result_t
parser_openfile(cfg_parser_t * pctx,const char * filename)2965185a700Sflorian parser_openfile(cfg_parser_t *pctx, const char *filename) {
2975185a700Sflorian 	isc_result_t result;
2985185a700Sflorian 	cfg_listelt_t *elt = NULL;
2995185a700Sflorian 	cfg_obj_t *stringobj = NULL;
3005185a700Sflorian 
3015185a700Sflorian 	result = isc_lex_openfile(pctx->lexer, filename);
3025185a700Sflorian 	if (result != ISC_R_SUCCESS) {
3035185a700Sflorian 		cfg_parser_error(pctx, 0, "open: %s: %s",
3045185a700Sflorian 			     filename, isc_result_totext(result));
3055185a700Sflorian 		goto cleanup;
3065185a700Sflorian 	}
3075185a700Sflorian 
3085185a700Sflorian 	CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
3095185a700Sflorian 	CHECK(create_listelt(pctx, &elt));
3105185a700Sflorian 	elt->obj = stringobj;
3115185a700Sflorian 	ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
3125185a700Sflorian 
3135185a700Sflorian 	return (ISC_R_SUCCESS);
3145185a700Sflorian  cleanup:
3155185a700Sflorian 	CLEANUP_OBJ(stringobj);
3165185a700Sflorian 	return (result);
3175185a700Sflorian }
3185185a700Sflorian 
3195185a700Sflorian /*
3205185a700Sflorian  * Parse a configuration using a pctx where a lexer has already
3215185a700Sflorian  * been set up with a source.
3225185a700Sflorian  */
3235185a700Sflorian static isc_result_t
parse2(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)3245185a700Sflorian parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3255185a700Sflorian 	isc_result_t result;
3265185a700Sflorian 	cfg_obj_t *obj = NULL;
3275185a700Sflorian 
3285185a700Sflorian 	result = cfg_parse_obj(pctx, type, &obj);
3295185a700Sflorian 
3305185a700Sflorian 	if (pctx->errors != 0) {
3315185a700Sflorian 		/* Errors have been logged. */
3325185a700Sflorian 		if (result == ISC_R_SUCCESS)
3335185a700Sflorian 			result = ISC_R_FAILURE;
3345185a700Sflorian 		goto cleanup;
3355185a700Sflorian 	}
3365185a700Sflorian 
3375185a700Sflorian 	if (result != ISC_R_SUCCESS) {
3385185a700Sflorian 		/* Parsing failed but no errors have been logged. */
3395185a700Sflorian 		cfg_parser_error(pctx, 0, "parsing failed: %s",
3405185a700Sflorian 				 isc_result_totext(result));
3415185a700Sflorian 		goto cleanup;
3425185a700Sflorian 	}
3435185a700Sflorian 
3445185a700Sflorian 	CHECK(parse_eof(pctx));
3455185a700Sflorian 
3465185a700Sflorian 	*ret = obj;
3475185a700Sflorian 	return (ISC_R_SUCCESS);
3485185a700Sflorian 
3495185a700Sflorian  cleanup:
3505185a700Sflorian 	CLEANUP_OBJ(obj);
3515185a700Sflorian 	return (result);
3525185a700Sflorian }
3535185a700Sflorian 
3545185a700Sflorian isc_result_t
cfg_parse_file(cfg_parser_t * pctx,const char * filename,const cfg_type_t * type,cfg_obj_t ** ret)3555185a700Sflorian cfg_parse_file(cfg_parser_t *pctx, const char *filename,
3565185a700Sflorian 	       const cfg_type_t *type, cfg_obj_t **ret)
3575185a700Sflorian {
3585185a700Sflorian 	isc_result_t result;
3595185a700Sflorian 
3605185a700Sflorian 	REQUIRE(pctx != NULL);
3615185a700Sflorian 	REQUIRE(filename != NULL);
3625185a700Sflorian 	REQUIRE(type != NULL);
3635185a700Sflorian 	REQUIRE(ret != NULL && *ret == NULL);
3645185a700Sflorian 
3655185a700Sflorian 	CHECK(parser_openfile(pctx, filename));
3665185a700Sflorian 	CHECK(parse2(pctx, type, ret));
3675185a700Sflorian  cleanup:
3685185a700Sflorian 	return (result);
3695185a700Sflorian }
3705185a700Sflorian 
3715185a700Sflorian void
cfg_parser_destroy(cfg_parser_t ** pctxp)3725185a700Sflorian cfg_parser_destroy(cfg_parser_t **pctxp) {
3735185a700Sflorian 	cfg_parser_t *pctx;
3745185a700Sflorian 
3755185a700Sflorian 	REQUIRE(pctxp != NULL && *pctxp != NULL);
3765185a700Sflorian 
3775185a700Sflorian 	pctx = *pctxp;
3785185a700Sflorian 	*pctxp = NULL;
3795185a700Sflorian 
3805185a700Sflorian 	isc_lex_destroy(&pctx->lexer);
3815185a700Sflorian 	/*
3825185a700Sflorian 	 * Cleaning up open_files does not
3835185a700Sflorian 	 * close the files; that was already done
3845185a700Sflorian 	 * by closing the lexer.
3855185a700Sflorian 	 */
3865185a700Sflorian 	CLEANUP_OBJ(pctx->open_files);
3875185a700Sflorian 	CLEANUP_OBJ(pctx->closed_files);
3885185a700Sflorian 	free(pctx);
3895185a700Sflorian }
3905185a700Sflorian 
3915185a700Sflorian /*
3925185a700Sflorian  * qstring (quoted string), ustring (unquoted string), astring
3935185a700Sflorian  * (any string)
3945185a700Sflorian  */
3955185a700Sflorian 
3965185a700Sflorian /* Create a string object from a null-terminated C string. */
3975185a700Sflorian static isc_result_t
create_string(cfg_parser_t * pctx,const char * contents,const cfg_type_t * type,cfg_obj_t ** ret)3985185a700Sflorian create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
3995185a700Sflorian 	      cfg_obj_t **ret)
4005185a700Sflorian {
4015185a700Sflorian 	isc_result_t result;
4025185a700Sflorian 	cfg_obj_t *obj = NULL;
4035185a700Sflorian 	int len;
4045185a700Sflorian 
4055185a700Sflorian 	CHECK(cfg_create_obj(pctx, type, &obj));
4065185a700Sflorian 	len = strlen(contents);
4075185a700Sflorian 	obj->value.string.length = len;
4085185a700Sflorian 	obj->value.string.base = malloc(len + 1);
409267a2de3Sjsg 	if (obj->value.string.base == NULL) {
4105185a700Sflorian 		free(obj);
4115185a700Sflorian 		return (ISC_R_NOMEMORY);
4125185a700Sflorian 	}
4135185a700Sflorian 	memmove(obj->value.string.base, contents, len);
4145185a700Sflorian 	obj->value.string.base[len] = '\0';
4155185a700Sflorian 
4165185a700Sflorian 	*ret = obj;
4175185a700Sflorian  cleanup:
4185185a700Sflorian 	return (result);
4195185a700Sflorian }
4205185a700Sflorian 
4215185a700Sflorian static isc_result_t
cfg_parse_qstring(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)4225185a700Sflorian cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
4235185a700Sflorian 	isc_result_t result;
4245185a700Sflorian 
4255185a700Sflorian 	REQUIRE(pctx != NULL);
4265185a700Sflorian 	REQUIRE(ret != NULL && *ret == NULL);
4275185a700Sflorian 
4285185a700Sflorian 	UNUSED(type);
4295185a700Sflorian 
4305185a700Sflorian 	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
4315185a700Sflorian 	if (pctx->token.type != isc_tokentype_qstring) {
4325185a700Sflorian 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
4335185a700Sflorian 		return (ISC_R_UNEXPECTEDTOKEN);
4345185a700Sflorian 	}
4355185a700Sflorian 	return (create_string(pctx, TOKEN_STRING(pctx),
4365185a700Sflorian 			      &cfg_type_qstring, ret));
4375185a700Sflorian  cleanup:
4385185a700Sflorian 	return (result);
4395185a700Sflorian }
4405185a700Sflorian 
4415185a700Sflorian static isc_result_t
cfg_parse_astring(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)4425185a700Sflorian cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
4435185a700Sflorian 		  cfg_obj_t **ret)
4445185a700Sflorian {
4455185a700Sflorian 	isc_result_t result;
4465185a700Sflorian 
4475185a700Sflorian 	REQUIRE(pctx != NULL);
4485185a700Sflorian 	REQUIRE(ret != NULL && *ret == NULL);
4495185a700Sflorian 
4505185a700Sflorian 	UNUSED(type);
4515185a700Sflorian 
4525185a700Sflorian 	CHECK(cfg_getstringtoken(pctx));
4535185a700Sflorian 	return (create_string(pctx,
4545185a700Sflorian 			      TOKEN_STRING(pctx),
4555185a700Sflorian 			      &cfg_type_qstring,
4565185a700Sflorian 			      ret));
4575185a700Sflorian  cleanup:
4585185a700Sflorian 	return (result);
4595185a700Sflorian }
4605185a700Sflorian 
4615185a700Sflorian static isc_result_t
cfg_parse_sstring(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)4625185a700Sflorian cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type,
4635185a700Sflorian 		  cfg_obj_t **ret)
4645185a700Sflorian {
4655185a700Sflorian 	isc_result_t result;
4665185a700Sflorian 
4675185a700Sflorian 	REQUIRE(pctx != NULL);
4685185a700Sflorian 	REQUIRE(ret != NULL && *ret == NULL);
4695185a700Sflorian 
4705185a700Sflorian 	UNUSED(type);
4715185a700Sflorian 
4725185a700Sflorian 	CHECK(cfg_getstringtoken(pctx));
4735185a700Sflorian 	return (create_string(pctx,
4745185a700Sflorian 			      TOKEN_STRING(pctx),
4755185a700Sflorian 			      &cfg_type_sstring,
4765185a700Sflorian 			      ret));
4775185a700Sflorian  cleanup:
4785185a700Sflorian 	return (result);
4795185a700Sflorian }
4805185a700Sflorian 
4815185a700Sflorian static void
free_string(cfg_parser_t * pctx,cfg_obj_t * obj)4825185a700Sflorian free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
4835185a700Sflorian 	UNUSED(pctx);
4845185a700Sflorian 	free(obj->value.string.base);
4855185a700Sflorian }
4865185a700Sflorian 
4875185a700Sflorian const char *
cfg_obj_asstring(const cfg_obj_t * obj)4885185a700Sflorian cfg_obj_asstring(const cfg_obj_t *obj) {
4895185a700Sflorian 	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
4905185a700Sflorian 	return (obj->value.string.base);
4915185a700Sflorian }
4925185a700Sflorian 
4935185a700Sflorian /* Quoted string only */
4945185a700Sflorian cfg_type_t cfg_type_qstring = {
4955185a700Sflorian 	"quoted_string", cfg_parse_qstring, &cfg_rep_string, NULL
4965185a700Sflorian };
4975185a700Sflorian 
4985185a700Sflorian /* Any string (quoted or unquoted); printed with quotes */
4995185a700Sflorian cfg_type_t cfg_type_astring = {
5005185a700Sflorian 	"string", cfg_parse_astring, &cfg_rep_string, NULL
5015185a700Sflorian };
5025185a700Sflorian 
5035185a700Sflorian /*
5045185a700Sflorian  * Any string (quoted or unquoted); printed with quotes.
5055185a700Sflorian  * If CFG_PRINTER_XKEY is set when printing the string will be '?' out.
5065185a700Sflorian  */
5075185a700Sflorian cfg_type_t cfg_type_sstring = {
5085185a700Sflorian 	"string", cfg_parse_sstring, &cfg_rep_string, NULL
5095185a700Sflorian };
5105185a700Sflorian 
5115185a700Sflorian /*
5125185a700Sflorian  * Booleans
5135185a700Sflorian  */
5145185a700Sflorian 
5155185a700Sflorian /*
5165185a700Sflorian  * Lists.
5175185a700Sflorian  */
5185185a700Sflorian 
5195185a700Sflorian static isc_result_t
cfg_create_list(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** obj)5205185a700Sflorian cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
5215185a700Sflorian 	isc_result_t result;
5225185a700Sflorian 
5235185a700Sflorian 	REQUIRE(pctx != NULL);
5245185a700Sflorian 	REQUIRE(type != NULL);
5255185a700Sflorian 	REQUIRE(obj != NULL && *obj == NULL);
5265185a700Sflorian 
5275185a700Sflorian 	CHECK(cfg_create_obj(pctx, type, obj));
5285185a700Sflorian 	ISC_LIST_INIT((*obj)->value.list);
5295185a700Sflorian  cleanup:
5305185a700Sflorian 	return (result);
5315185a700Sflorian }
5325185a700Sflorian 
5335185a700Sflorian static isc_result_t
create_listelt(cfg_parser_t * pctx,cfg_listelt_t ** eltp)5345185a700Sflorian create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
5355185a700Sflorian 	UNUSED(pctx);
5365185a700Sflorian 	cfg_listelt_t *elt;
5375185a700Sflorian 
5385185a700Sflorian 	elt = malloc(sizeof(*elt));
5395185a700Sflorian 	if (elt == NULL)
5405185a700Sflorian 		return (ISC_R_NOMEMORY);
5415185a700Sflorian 	elt->obj = NULL;
5425185a700Sflorian 	ISC_LINK_INIT(elt, link);
5435185a700Sflorian 	*eltp = elt;
5445185a700Sflorian 	return (ISC_R_SUCCESS);
5455185a700Sflorian }
5465185a700Sflorian 
5475185a700Sflorian static void
free_list_elt(cfg_parser_t * pctx,cfg_listelt_t * elt)5485185a700Sflorian free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
5495185a700Sflorian 	cfg_obj_destroy(pctx, &elt->obj);
5505185a700Sflorian 	free(elt);
5515185a700Sflorian }
5525185a700Sflorian 
5535185a700Sflorian static void
free_list(cfg_parser_t * pctx,cfg_obj_t * obj)5545185a700Sflorian free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
5555185a700Sflorian 	cfg_listelt_t *elt, *next;
5565185a700Sflorian 	for (elt = ISC_LIST_HEAD(obj->value.list);
5575185a700Sflorian 	     elt != NULL;
5585185a700Sflorian 	     elt = next)
5595185a700Sflorian 	{
5605185a700Sflorian 		next = ISC_LIST_NEXT(elt, link);
5615185a700Sflorian 		free_list_elt(pctx, elt);
5625185a700Sflorian 	}
5635185a700Sflorian }
5645185a700Sflorian 
5655185a700Sflorian static isc_result_t
cfg_parse_listelt(cfg_parser_t * pctx,const cfg_type_t * elttype,cfg_listelt_t ** ret)5665185a700Sflorian cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
5675185a700Sflorian 		  cfg_listelt_t **ret)
5685185a700Sflorian {
5695185a700Sflorian 	isc_result_t result;
5705185a700Sflorian 	cfg_listelt_t *elt = NULL;
5715185a700Sflorian 	cfg_obj_t *value = NULL;
5725185a700Sflorian 
5735185a700Sflorian 	REQUIRE(pctx != NULL);
5745185a700Sflorian 	REQUIRE(elttype != NULL);
5755185a700Sflorian 	REQUIRE(ret != NULL && *ret == NULL);
5765185a700Sflorian 
5775185a700Sflorian 	CHECK(create_listelt(pctx, &elt));
5785185a700Sflorian 
5795185a700Sflorian 	result = cfg_parse_obj(pctx, elttype, &value);
5805185a700Sflorian 	if (result != ISC_R_SUCCESS)
5815185a700Sflorian 		goto cleanup;
5825185a700Sflorian 
5835185a700Sflorian 	elt->obj = value;
5845185a700Sflorian 
5855185a700Sflorian 	*ret = elt;
5865185a700Sflorian 	return (ISC_R_SUCCESS);
5875185a700Sflorian 
5885185a700Sflorian  cleanup:
5895185a700Sflorian 	free(elt);
5905185a700Sflorian 	return (result);
5915185a700Sflorian }
5925185a700Sflorian 
5935185a700Sflorian /*
5945185a700Sflorian  * Maps.
5955185a700Sflorian  */
5965185a700Sflorian 
5975185a700Sflorian /*
5985185a700Sflorian  * Parse a map body.  That's something like
5995185a700Sflorian  *
6005185a700Sflorian  *   "foo 1; bar { glub; }; zap true; zap false;"
6015185a700Sflorian  *
6025185a700Sflorian  * i.e., a sequence of option names followed by values and
6035185a700Sflorian  * terminated by semicolons.  Used for the top level of
6045185a700Sflorian  * the named.conf syntax, as well as for the body of the
6055185a700Sflorian  * options, view, zone, and other statements.
6065185a700Sflorian  */
6075185a700Sflorian isc_result_t
cfg_parse_mapbody(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)6085185a700Sflorian cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
6095185a700Sflorian {
6105185a700Sflorian 	const cfg_clausedef_t * const *clausesets = type->of;
6115185a700Sflorian 	isc_result_t result;
6125185a700Sflorian 	const cfg_clausedef_t * const *clauseset;
6135185a700Sflorian 	const cfg_clausedef_t *clause;
6145185a700Sflorian 	cfg_obj_t *value = NULL;
6155185a700Sflorian 	cfg_obj_t *obj = NULL;
6165185a700Sflorian 	cfg_obj_t *eltobj = NULL;
6175185a700Sflorian 	cfg_obj_t *includename = NULL;
6185185a700Sflorian 	isc_symvalue_t symval;
6195185a700Sflorian 
6205185a700Sflorian 	REQUIRE(pctx != NULL);
6215185a700Sflorian 	REQUIRE(type != NULL);
6225185a700Sflorian 	REQUIRE(ret != NULL && *ret == NULL);
6235185a700Sflorian 
6245185a700Sflorian 	CHECK(create_map(pctx, type, &obj));
6255185a700Sflorian 
6265185a700Sflorian 	obj->value.map.clausesets = clausesets;
6275185a700Sflorian 
6285185a700Sflorian 	for (;;) {
6295185a700Sflorian 	redo:
6305185a700Sflorian 		/*
6315185a700Sflorian 		 * Parse the option name and see if it is known.
6325185a700Sflorian 		 */
6335185a700Sflorian 		CHECK(cfg_gettoken(pctx, 0));
6345185a700Sflorian 
6355185a700Sflorian 		if (pctx->token.type != isc_tokentype_string) {
6365185a700Sflorian 			cfg_ungettoken(pctx);
6375185a700Sflorian 			break;
6385185a700Sflorian 		}
6395185a700Sflorian 
6405185a700Sflorian 		/*
6415185a700Sflorian 		 * We accept "include" statements wherever a map body
6425185a700Sflorian 		 * clause can occur.
6435185a700Sflorian 		 */
6445185a700Sflorian 		if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
6455185a700Sflorian 			/*
6465185a700Sflorian 			 * Turn the file name into a temporary configuration
6475185a700Sflorian 			 * object just so that it is not overwritten by the
6485185a700Sflorian 			 * semicolon token.
6495185a700Sflorian 			 */
6505185a700Sflorian 			CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
6515185a700Sflorian 			CHECK(parse_semicolon(pctx));
6525185a700Sflorian 			CHECK(parser_openfile(pctx, includename->
6535185a700Sflorian 					      value.string.base));
6545185a700Sflorian 			 cfg_obj_destroy(pctx, &includename);
6555185a700Sflorian 			 goto redo;
6565185a700Sflorian 		}
6575185a700Sflorian 
6585185a700Sflorian 		clause = NULL;
6595185a700Sflorian 		for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
6605185a700Sflorian 			for (clause = *clauseset;
6615185a700Sflorian 			     clause->name != NULL;
6625185a700Sflorian 			     clause++) {
6635185a700Sflorian 				if (strcasecmp(TOKEN_STRING(pctx),
6645185a700Sflorian 					   clause->name) == 0)
6655185a700Sflorian 					goto done;
6665185a700Sflorian 			}
6675185a700Sflorian 		}
6685185a700Sflorian 	done:
6695185a700Sflorian 		if (clause == NULL || clause->name == NULL) {
6705185a700Sflorian 			cfg_parser_error(pctx, CFG_LOG_NOPREP,
6715185a700Sflorian 					 "unknown option");
6725185a700Sflorian 			/*
6735185a700Sflorian 			 * Try to recover by parsing this option as an unknown
6745185a700Sflorian 			 * option and discarding it.
6755185a700Sflorian 			 */
6765185a700Sflorian 			CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported,
6775185a700Sflorian 					    &eltobj));
6785185a700Sflorian 			cfg_obj_destroy(pctx, &eltobj);
6795185a700Sflorian 			CHECK(parse_semicolon(pctx));
6805185a700Sflorian 			continue;
6815185a700Sflorian 		}
6825185a700Sflorian 
6835185a700Sflorian 		/* Clause is known. */
6845185a700Sflorian 
6855185a700Sflorian 		/* See if the clause already has a value; if not create one. */
6865185a700Sflorian 		result = isc_symtab_lookup(obj->value.map.symtab,
6875185a700Sflorian 					   clause->name, 0, &symval);
6885185a700Sflorian 
6895185a700Sflorian 		/* Single-valued clause */
6905185a700Sflorian 		if (result == ISC_R_NOTFOUND) {
6915185a700Sflorian 			CHECK(parse_symtab_elt(pctx, clause->name,
6925185a700Sflorian 					       clause->type,
6935185a700Sflorian 					       obj->value.map.symtab));
6945185a700Sflorian 			CHECK(parse_semicolon(pctx));
6955185a700Sflorian 		} else if (result == ISC_R_SUCCESS) {
6965185a700Sflorian 			cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
6975185a700Sflorian 				     clause->name);
6985185a700Sflorian 			result = ISC_R_EXISTS;
6995185a700Sflorian 			goto cleanup;
7005185a700Sflorian 		} else {
7015185a700Sflorian 			cfg_parser_error(pctx, CFG_LOG_NEAR,
7025185a700Sflorian 				     "isc_symtab_define() failed");
7035185a700Sflorian 			goto cleanup;
7045185a700Sflorian 		}
7055185a700Sflorian 	}
7065185a700Sflorian 
7075185a700Sflorian 	*ret = obj;
7085185a700Sflorian 	return (ISC_R_SUCCESS);
7095185a700Sflorian 
7105185a700Sflorian  cleanup:
7115185a700Sflorian 	CLEANUP_OBJ(value);
7125185a700Sflorian 	CLEANUP_OBJ(obj);
7135185a700Sflorian 	CLEANUP_OBJ(eltobj);
7145185a700Sflorian 	CLEANUP_OBJ(includename);
7155185a700Sflorian 	return (result);
7165185a700Sflorian }
7175185a700Sflorian 
7185185a700Sflorian static isc_result_t
parse_symtab_elt(cfg_parser_t * pctx,const char * name,cfg_type_t * elttype,isc_symtab_t * symtab)7195185a700Sflorian parse_symtab_elt(cfg_parser_t *pctx, const char *name,
7205185a700Sflorian 		 cfg_type_t *elttype, isc_symtab_t *symtab)
7215185a700Sflorian {
7225185a700Sflorian 	isc_result_t result;
7235185a700Sflorian 	cfg_obj_t *obj = NULL;
7245185a700Sflorian 	isc_symvalue_t symval;
7255185a700Sflorian 
7265185a700Sflorian 	CHECK(cfg_parse_obj(pctx, elttype, &obj));
7275185a700Sflorian 
7285185a700Sflorian 	symval.as_pointer = obj;
7295185a700Sflorian 	CHECK(isc_symtab_define(symtab, name,
7305185a700Sflorian 				1, symval,
7315185a700Sflorian 				isc_symexists_reject));
7325185a700Sflorian 	return (ISC_R_SUCCESS);
7335185a700Sflorian 
7345185a700Sflorian  cleanup:
7355185a700Sflorian 	CLEANUP_OBJ(obj);
7365185a700Sflorian 	return (result);
7375185a700Sflorian }
7385185a700Sflorian 
7395185a700Sflorian /*
7405185a700Sflorian  * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
7415185a700Sflorian  */
7425185a700Sflorian static isc_result_t
cfg_parse_map(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)7435185a700Sflorian cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
7445185a700Sflorian 	isc_result_t result;
7455185a700Sflorian 
7465185a700Sflorian 	REQUIRE(pctx != NULL);
7475185a700Sflorian 	REQUIRE(type != NULL);
7485185a700Sflorian 	REQUIRE(ret != NULL && *ret == NULL);
7495185a700Sflorian 
7505185a700Sflorian 	CHECK(cfg_parse_special(pctx, '{'));
7515185a700Sflorian 	CHECK(cfg_parse_mapbody(pctx, type, ret));
7525185a700Sflorian 	CHECK(cfg_parse_special(pctx, '}'));
7535185a700Sflorian  cleanup:
7545185a700Sflorian 	return (result);
7555185a700Sflorian }
7565185a700Sflorian 
7575185a700Sflorian /*
7585185a700Sflorian  * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
7595185a700Sflorian  */
7605185a700Sflorian static isc_result_t
parse_any_named_map(cfg_parser_t * pctx,cfg_type_t * nametype,const cfg_type_t * type,cfg_obj_t ** ret)7615185a700Sflorian parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype,
7625185a700Sflorian 		    const cfg_type_t *type, cfg_obj_t **ret)
7635185a700Sflorian {
7645185a700Sflorian 	isc_result_t result;
7655185a700Sflorian 	cfg_obj_t *idobj = NULL;
7665185a700Sflorian 	cfg_obj_t *mapobj = NULL;
7675185a700Sflorian 
7685185a700Sflorian 	REQUIRE(pctx != NULL);
7695185a700Sflorian 	REQUIRE(nametype != NULL);
7705185a700Sflorian 	REQUIRE(type != NULL);
7715185a700Sflorian 	REQUIRE(ret != NULL && *ret == NULL);
7725185a700Sflorian 
7735185a700Sflorian 	CHECK(cfg_parse_obj(pctx, nametype, &idobj));
7745185a700Sflorian 	CHECK(cfg_parse_map(pctx, type, &mapobj));
7755185a700Sflorian 	mapobj->value.map.id = idobj;
7765185a700Sflorian 	*ret = mapobj;
7775185a700Sflorian 	return (result);
7785185a700Sflorian  cleanup:
7795185a700Sflorian 	CLEANUP_OBJ(idobj);
7805185a700Sflorian 	CLEANUP_OBJ(mapobj);
7815185a700Sflorian 	return (result);
7825185a700Sflorian }
7835185a700Sflorian 
7845185a700Sflorian /*
7855185a700Sflorian  * Parse a map identified by a string name.  E.g., "name { foo 1; }".
7865185a700Sflorian  * Used for the "key" and "channel" statements.
7875185a700Sflorian  */
7885185a700Sflorian isc_result_t
cfg_parse_named_map(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)7895185a700Sflorian cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
7905185a700Sflorian 	return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
7915185a700Sflorian }
7925185a700Sflorian 
7935185a700Sflorian isc_result_t
cfg_map_get(const cfg_obj_t * mapobj,const char * name,const cfg_obj_t ** obj)7945185a700Sflorian cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
7955185a700Sflorian 	isc_result_t result;
7965185a700Sflorian 	isc_symvalue_t val;
7975185a700Sflorian 	const cfg_map_t *map;
7985185a700Sflorian 
7995185a700Sflorian 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
8005185a700Sflorian 	REQUIRE(name != NULL);
8015185a700Sflorian 	REQUIRE(obj != NULL && *obj == NULL);
8025185a700Sflorian 
8035185a700Sflorian 	map = &mapobj->value.map;
8045185a700Sflorian 
8055185a700Sflorian 	result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
8065185a700Sflorian 	if (result != ISC_R_SUCCESS)
8075185a700Sflorian 		return (result);
8085185a700Sflorian 	*obj = val.as_pointer;
8095185a700Sflorian 	return (ISC_R_SUCCESS);
8105185a700Sflorian }
8115185a700Sflorian 
8125185a700Sflorian const cfg_obj_t *
cfg_map_getname(const cfg_obj_t * mapobj)8135185a700Sflorian cfg_map_getname(const cfg_obj_t *mapobj) {
8145185a700Sflorian 	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
8155185a700Sflorian 	return (mapobj->value.map.id);
8165185a700Sflorian }
8175185a700Sflorian 
8185185a700Sflorian /* Parse an arbitrary token, storing its raw text representation. */
8195185a700Sflorian static isc_result_t
parse_token(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)8205185a700Sflorian parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
8215185a700Sflorian 	cfg_obj_t *obj = NULL;
8225185a700Sflorian 	isc_result_t result;
8235185a700Sflorian 	isc_region_t r;
8245185a700Sflorian 
8255185a700Sflorian 	UNUSED(type);
8265185a700Sflorian 
8275185a700Sflorian 	CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
8285185a700Sflorian 	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
8295185a700Sflorian 	if (pctx->token.type == isc_tokentype_eof) {
8305185a700Sflorian 		cfg_ungettoken(pctx);
8315185a700Sflorian 		result = ISC_R_EOF;
8325185a700Sflorian 		goto cleanup;
8335185a700Sflorian 	}
8345185a700Sflorian 
8355185a700Sflorian 	isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
8365185a700Sflorian 
8375185a700Sflorian 	obj->value.string.base = malloc(r.length + 1);
8385185a700Sflorian 	if (obj->value.string.base == NULL) {
8395185a700Sflorian 		result = ISC_R_NOMEMORY;
8405185a700Sflorian 		goto cleanup;
8415185a700Sflorian 	}
8425185a700Sflorian 	obj->value.string.length = r.length;
8435185a700Sflorian 	memmove(obj->value.string.base, r.base, r.length);
8445185a700Sflorian 	obj->value.string.base[r.length] = '\0';
8455185a700Sflorian 	*ret = obj;
8465185a700Sflorian 	return (result);
8475185a700Sflorian 
8485185a700Sflorian  cleanup:
8495185a700Sflorian 	if (obj != NULL)
8505185a700Sflorian 		free(obj);
8515185a700Sflorian 	return (result);
8525185a700Sflorian }
8535185a700Sflorian 
8545185a700Sflorian cfg_type_t cfg_type_token = {
8555185a700Sflorian 	"token", parse_token, &cfg_rep_string, NULL
8565185a700Sflorian };
8575185a700Sflorian 
8585185a700Sflorian /*
8595185a700Sflorian  * An unsupported option.  This is just a list of tokens with balanced braces
8605185a700Sflorian  * ending in a semicolon.
8615185a700Sflorian  */
8625185a700Sflorian 
8635185a700Sflorian static isc_result_t
parse_unsupported(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)8645185a700Sflorian parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
8655185a700Sflorian 	cfg_obj_t *listobj = NULL;
8665185a700Sflorian 	isc_result_t result;
8675185a700Sflorian 	int braces = 0;
8685185a700Sflorian 
8695185a700Sflorian 	CHECK(cfg_create_list(pctx, type, &listobj));
8705185a700Sflorian 
8715185a700Sflorian 	for (;;) {
8725185a700Sflorian 		cfg_listelt_t *elt = NULL;
8735185a700Sflorian 
8745185a700Sflorian 		CHECK(cfg_peektoken(pctx, 0));
8755185a700Sflorian 		if (pctx->token.type == isc_tokentype_special) {
8765185a700Sflorian 			if (pctx->token.value.as_char == '{')
8775185a700Sflorian 				braces++;
8785185a700Sflorian 			else if (pctx->token.value.as_char == '}')
8795185a700Sflorian 				braces--;
8805185a700Sflorian 			else if (pctx->token.value.as_char == ';')
8815185a700Sflorian 				if (braces == 0)
8825185a700Sflorian 					break;
8835185a700Sflorian 		}
8845185a700Sflorian 		if (pctx->token.type == isc_tokentype_eof || braces < 0) {
8855185a700Sflorian 			cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
8865185a700Sflorian 			result = ISC_R_UNEXPECTEDTOKEN;
8875185a700Sflorian 			goto cleanup;
8885185a700Sflorian 		}
8895185a700Sflorian 
8905185a700Sflorian 		CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
8915185a700Sflorian 		ISC_LIST_APPEND(listobj->value.list, elt, link);
8925185a700Sflorian 	}
8935185a700Sflorian 	INSIST(braces == 0);
8945185a700Sflorian 	*ret = listobj;
8955185a700Sflorian 	return (ISC_R_SUCCESS);
8965185a700Sflorian 
8975185a700Sflorian  cleanup:
8985185a700Sflorian 	CLEANUP_OBJ(listobj);
8995185a700Sflorian 	return (result);
9005185a700Sflorian }
9015185a700Sflorian 
9025185a700Sflorian cfg_type_t cfg_type_unsupported = {
9035185a700Sflorian 	"unsupported", parse_unsupported, &cfg_rep_list, NULL
9045185a700Sflorian };
9055185a700Sflorian 
9065185a700Sflorian static isc_result_t
cfg_gettoken(cfg_parser_t * pctx,int options)9075185a700Sflorian cfg_gettoken(cfg_parser_t *pctx, int options) {
9085185a700Sflorian 	isc_result_t result;
9095185a700Sflorian 
9105185a700Sflorian 	REQUIRE(pctx != NULL);
9115185a700Sflorian 
9125185a700Sflorian 	if (pctx->seen_eof)
9135185a700Sflorian 		return (ISC_R_SUCCESS);
9145185a700Sflorian 
9155185a700Sflorian 	options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
9165185a700Sflorian 
9175185a700Sflorian  redo:
9185185a700Sflorian 	pctx->token.type = isc_tokentype_unknown;
9195185a700Sflorian 	result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
920*1fb015a8Sflorian 	pctx->ungotten = 0;
9215185a700Sflorian 	pctx->line = isc_lex_getsourceline(pctx->lexer);
9225185a700Sflorian 
9235185a700Sflorian 	switch (result) {
9245185a700Sflorian 	case ISC_R_SUCCESS:
9255185a700Sflorian 		if (pctx->token.type == isc_tokentype_eof) {
9265185a700Sflorian 			result = isc_lex_close(pctx->lexer);
9275185a700Sflorian 			INSIST(result == ISC_R_NOMORE ||
9285185a700Sflorian 			       result == ISC_R_SUCCESS);
9295185a700Sflorian 
9305185a700Sflorian 			if (isc_lex_getsourcename(pctx->lexer) != NULL) {
9315185a700Sflorian 				/*
9325185a700Sflorian 				 * Closed an included file, not the main file.
9335185a700Sflorian 				 */
9345185a700Sflorian 				cfg_listelt_t *elt;
9355185a700Sflorian 				elt = ISC_LIST_TAIL(pctx->open_files->
9365185a700Sflorian 						    value.list);
9375185a700Sflorian 				INSIST(elt != NULL);
9385185a700Sflorian 				ISC_LIST_UNLINK(pctx->open_files->
9395185a700Sflorian 						value.list, elt, link);
9405185a700Sflorian 				ISC_LIST_APPEND(pctx->closed_files->
9415185a700Sflorian 						value.list, elt, link);
9425185a700Sflorian 				goto redo;
9435185a700Sflorian 			}
944*1fb015a8Sflorian 			pctx->seen_eof = 1;
9455185a700Sflorian 		}
9465185a700Sflorian 		break;
9475185a700Sflorian 
9485185a700Sflorian 	case ISC_R_NOSPACE:
9495185a700Sflorian 		/* More understandable than "ran out of space". */
9505185a700Sflorian 		cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
9515185a700Sflorian 		break;
9525185a700Sflorian 
9535185a700Sflorian 	case ISC_R_IOERROR:
9545185a700Sflorian 		cfg_parser_error(pctx, 0, "%s",
9555185a700Sflorian 				 isc_result_totext(result));
9565185a700Sflorian 		break;
9575185a700Sflorian 
9585185a700Sflorian 	default:
9595185a700Sflorian 		cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
9605185a700Sflorian 				 isc_result_totext(result));
9615185a700Sflorian 		break;
9625185a700Sflorian 	}
9635185a700Sflorian 	return (result);
9645185a700Sflorian }
9655185a700Sflorian 
9665185a700Sflorian static void
cfg_ungettoken(cfg_parser_t * pctx)9675185a700Sflorian cfg_ungettoken(cfg_parser_t *pctx) {
9685185a700Sflorian 	REQUIRE(pctx != NULL);
9695185a700Sflorian 
9705185a700Sflorian 	if (pctx->seen_eof)
9715185a700Sflorian 		return;
9725185a700Sflorian 	isc_lex_ungettoken(pctx->lexer, &pctx->token);
973*1fb015a8Sflorian 	pctx->ungotten = 1;
9745185a700Sflorian }
9755185a700Sflorian 
9765185a700Sflorian static isc_result_t
cfg_peektoken(cfg_parser_t * pctx,int options)9775185a700Sflorian cfg_peektoken(cfg_parser_t *pctx, int options) {
9785185a700Sflorian 	isc_result_t result;
9795185a700Sflorian 
9805185a700Sflorian 	REQUIRE(pctx != NULL);
9815185a700Sflorian 
9825185a700Sflorian 	CHECK(cfg_gettoken(pctx, options));
9835185a700Sflorian 	cfg_ungettoken(pctx);
9845185a700Sflorian  cleanup:
9855185a700Sflorian 	return (result);
9865185a700Sflorian }
9875185a700Sflorian 
9885185a700Sflorian /*
9895185a700Sflorian  * Get a string token, accepting both the quoted and the unquoted form.
9905185a700Sflorian  * Log an error if the next token is not a string.
9915185a700Sflorian  */
9925185a700Sflorian static isc_result_t
cfg_getstringtoken(cfg_parser_t * pctx)9935185a700Sflorian cfg_getstringtoken(cfg_parser_t *pctx) {
9945185a700Sflorian 	isc_result_t result;
9955185a700Sflorian 
9965185a700Sflorian 	result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
9975185a700Sflorian 	if (result != ISC_R_SUCCESS)
9985185a700Sflorian 		return (result);
9995185a700Sflorian 
10005185a700Sflorian 	if (pctx->token.type != isc_tokentype_string &&
10015185a700Sflorian 	    pctx->token.type != isc_tokentype_qstring) {
10025185a700Sflorian 		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
10035185a700Sflorian 		return (ISC_R_UNEXPECTEDTOKEN);
10045185a700Sflorian 	}
10055185a700Sflorian 	return (ISC_R_SUCCESS);
10065185a700Sflorian }
10075185a700Sflorian 
10085185a700Sflorian static void
cfg_parser_error(cfg_parser_t * pctx,unsigned int flags,const char * fmt,...)10095185a700Sflorian cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
10105185a700Sflorian 	va_list args;
10115185a700Sflorian 
10125185a700Sflorian 	REQUIRE(pctx != NULL);
10135185a700Sflorian 	REQUIRE(fmt != NULL);
10145185a700Sflorian 
10155185a700Sflorian 	va_start(args, fmt);
1016*1fb015a8Sflorian 	parser_complain(pctx, 0, flags, fmt, args);
10175185a700Sflorian 	va_end(args);
10185185a700Sflorian 	pctx->errors++;
10195185a700Sflorian }
10205185a700Sflorian 
10215185a700Sflorian #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
10225185a700Sflorian 
1023*1fb015a8Sflorian static int
have_current_file(cfg_parser_t * pctx)10245185a700Sflorian have_current_file(cfg_parser_t *pctx) {
10255185a700Sflorian 	cfg_listelt_t *elt;
10265185a700Sflorian 	if (pctx->open_files == NULL)
1027*1fb015a8Sflorian 		return (0);
10285185a700Sflorian 
10295185a700Sflorian 	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
10305185a700Sflorian 	if (elt == NULL)
1031*1fb015a8Sflorian 	      return (0);
10325185a700Sflorian 
1033*1fb015a8Sflorian 	return (1);
10345185a700Sflorian }
10355185a700Sflorian 
10365185a700Sflorian static char *
current_file(cfg_parser_t * pctx)10375185a700Sflorian current_file(cfg_parser_t *pctx) {
10385185a700Sflorian 	static char none[] = "none";
10395185a700Sflorian 	cfg_listelt_t *elt;
10405185a700Sflorian 	cfg_obj_t *fileobj;
10415185a700Sflorian 
10425185a700Sflorian 	if (!have_current_file(pctx))
10435185a700Sflorian 		return (none);
10445185a700Sflorian 
10455185a700Sflorian 	elt = ISC_LIST_TAIL(pctx->open_files->value.list);
10465185a700Sflorian 	if (elt == NULL)	/* shouldn't be possible, but... */
10475185a700Sflorian 	      return (none);
10485185a700Sflorian 
10495185a700Sflorian 	fileobj = elt->obj;
10505185a700Sflorian 	INSIST(fileobj->type == &cfg_type_qstring);
10515185a700Sflorian 	return (fileobj->value.string.base);
10525185a700Sflorian }
10535185a700Sflorian 
10545185a700Sflorian static void
parser_complain(cfg_parser_t * pctx,int is_warning,unsigned int flags,const char * format,va_list args)1055*1fb015a8Sflorian parser_complain(cfg_parser_t *pctx, int is_warning,
10565185a700Sflorian 		unsigned int flags, const char *format,
10575185a700Sflorian 		va_list args)
10585185a700Sflorian {
10595185a700Sflorian 	char tokenbuf[MAX_LOG_TOKEN + 10];
10605185a700Sflorian 	static char where[PATH_MAX + 100];
10615185a700Sflorian 	static char message[2048];
10625185a700Sflorian 	int level = ISC_LOG_ERROR;
10635185a700Sflorian 	const char *prep = "";
10645185a700Sflorian 	size_t len;
10655185a700Sflorian 
10665185a700Sflorian 	if (is_warning)
10675185a700Sflorian 		level = ISC_LOG_WARNING;
10685185a700Sflorian 
10695185a700Sflorian 	where[0] = '\0';
10705185a700Sflorian 	if (have_current_file(pctx))
10715185a700Sflorian 		snprintf(where, sizeof(where), "%s:%u: ",
10725185a700Sflorian 			 current_file(pctx), pctx->line);
10735185a700Sflorian 
10745185a700Sflorian 	len = vsnprintf(message, sizeof(message), format, args);
10755185a700Sflorian #define ELIPSIS " ... "
10765185a700Sflorian 	if (len >= sizeof(message)) {
10775185a700Sflorian 		message[sizeof(message) - sizeof(ELIPSIS)] = 0;
10785185a700Sflorian 		strlcat(message, ELIPSIS, sizeof(message));
10795185a700Sflorian 	}
10805185a700Sflorian 
10815185a700Sflorian 	if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) {
10825185a700Sflorian 		isc_region_t r;
10835185a700Sflorian 
10845185a700Sflorian 		if (pctx->ungotten)
10855185a700Sflorian 			(void)cfg_gettoken(pctx, 0);
10865185a700Sflorian 
10875185a700Sflorian 		if (pctx->token.type == isc_tokentype_eof) {
10885185a700Sflorian 			snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
10895185a700Sflorian 		} else if (pctx->token.type == isc_tokentype_unknown) {
10905185a700Sflorian 			flags = 0;
10915185a700Sflorian 			tokenbuf[0] = '\0';
10925185a700Sflorian 		} else {
10935185a700Sflorian 			isc_lex_getlasttokentext(pctx->lexer,
10945185a700Sflorian 						 &pctx->token, &r);
10955185a700Sflorian 			if (r.length > MAX_LOG_TOKEN)
10965185a700Sflorian 				snprintf(tokenbuf, sizeof(tokenbuf),
10975185a700Sflorian 					 "'%.*s...'", MAX_LOG_TOKEN, r.base);
10985185a700Sflorian 			else
10995185a700Sflorian 				snprintf(tokenbuf, sizeof(tokenbuf),
11005185a700Sflorian 					 "'%.*s'", (int)r.length, r.base);
11015185a700Sflorian 		}
11025185a700Sflorian 
11035185a700Sflorian 		/* Choose a preposition. */
11045185a700Sflorian 		if (flags & CFG_LOG_NEAR)
11055185a700Sflorian 			prep = " near ";
11065185a700Sflorian 		else if (flags & CFG_LOG_BEFORE)
11075185a700Sflorian 			prep = " before ";
11085185a700Sflorian 		else
11095185a700Sflorian 			prep = " ";
11105185a700Sflorian 	} else {
11115185a700Sflorian 		tokenbuf[0] = '\0';
11125185a700Sflorian 	}
11135185a700Sflorian 	isc_log_write(pctx->lctx, CAT, MOD, level,
11145185a700Sflorian 		      "%s%s%s%s", where, message, prep, tokenbuf);
11155185a700Sflorian }
11165185a700Sflorian 
11175185a700Sflorian static isc_result_t
cfg_create_obj(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)11185185a700Sflorian cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
11195185a700Sflorian 	cfg_obj_t *obj;
11205185a700Sflorian 
11215185a700Sflorian 	REQUIRE(pctx != NULL);
11225185a700Sflorian 	REQUIRE(type != NULL);
11235185a700Sflorian 	REQUIRE(ret != NULL && *ret == NULL);
11245185a700Sflorian 
11255185a700Sflorian 	obj = malloc(sizeof(cfg_obj_t));
11265185a700Sflorian 	if (obj == NULL)
11275185a700Sflorian 		return (ISC_R_NOMEMORY);
11285185a700Sflorian 	obj->type = type;
11295185a700Sflorian 	obj->file = current_file(pctx);
11305185a700Sflorian 	obj->line = pctx->line;
11315185a700Sflorian 	*ret = obj;
11325185a700Sflorian 	return (ISC_R_SUCCESS);
11335185a700Sflorian }
11345185a700Sflorian 
11355185a700Sflorian static void
map_symtabitem_destroy(char * key,unsigned int type,isc_symvalue_t symval,void * userarg)11365185a700Sflorian map_symtabitem_destroy(char *key, unsigned int type,
11375185a700Sflorian 		       isc_symvalue_t symval, void *userarg)
11385185a700Sflorian {
11395185a700Sflorian 	cfg_obj_t *obj = symval.as_pointer;
11405185a700Sflorian 	cfg_parser_t *pctx = (cfg_parser_t *)userarg;
11415185a700Sflorian 
11425185a700Sflorian 	UNUSED(key);
11435185a700Sflorian 	UNUSED(type);
11445185a700Sflorian 
11455185a700Sflorian 	cfg_obj_destroy(pctx, &obj);
11465185a700Sflorian }
11475185a700Sflorian 
11485185a700Sflorian static isc_result_t
create_map(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)11495185a700Sflorian create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
11505185a700Sflorian 	isc_result_t result;
11515185a700Sflorian 	isc_symtab_t *symtab = NULL;
11525185a700Sflorian 	cfg_obj_t *obj = NULL;
11535185a700Sflorian 
11545185a700Sflorian 	CHECK(cfg_create_obj(pctx, type, &obj));
11555185a700Sflorian 	CHECK(isc_symtab_create(5, /* XXX */
11565185a700Sflorian 				map_symtabitem_destroy,
1157*1fb015a8Sflorian 				pctx, 0, &symtab));
11585185a700Sflorian 	obj->value.map.symtab = symtab;
11595185a700Sflorian 	obj->value.map.id = NULL;
11605185a700Sflorian 
11615185a700Sflorian 	*ret = obj;
11625185a700Sflorian 	return (ISC_R_SUCCESS);
11635185a700Sflorian 
11645185a700Sflorian  cleanup:
11655185a700Sflorian 	if (obj != NULL)
11665185a700Sflorian 		free(obj);
11675185a700Sflorian 	return (result);
11685185a700Sflorian }
11695185a700Sflorian 
11705185a700Sflorian static void
free_map(cfg_parser_t * pctx,cfg_obj_t * obj)11715185a700Sflorian free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
11725185a700Sflorian 	CLEANUP_OBJ(obj->value.map.id);
11735185a700Sflorian 	isc_symtab_destroy(&obj->value.map.symtab);
11745185a700Sflorian }
11755185a700Sflorian 
11765185a700Sflorian /*
11775185a700Sflorian  * Destroy 'obj', a configuration object created in 'pctx'.
11785185a700Sflorian  */
11795185a700Sflorian void
cfg_obj_destroy(cfg_parser_t * pctx,cfg_obj_t ** objp)11805185a700Sflorian cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
11815185a700Sflorian 	cfg_obj_t *obj;
11825185a700Sflorian 
11835185a700Sflorian 	REQUIRE(objp != NULL && *objp != NULL);
11845185a700Sflorian 	REQUIRE(pctx != NULL);
11855185a700Sflorian 
11865185a700Sflorian 	obj = *objp;
11875185a700Sflorian 
11885185a700Sflorian 	obj->type->rep->free(pctx, obj);
11895185a700Sflorian 	free(obj);
11905185a700Sflorian 	*objp = NULL;
11915185a700Sflorian }
1192