parser.c revision 7d4a465de03b26bf9f5ef131d03253b8f6afc169
d6fa26d0adaec6c910115be34fe7a5a5f402c14fMark Andrews/*
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
f0aad5341752aefe5059832f6cf3abc3283c6e16Tinderbox User * Copyright (C) 2000-2003 Internet Software Consortium.
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein *
5347c0fcb04eaea19d9f39795646239f487c6207Tinderbox User * Permission to use, copy, modify, and distribute this software for any
5347c0fcb04eaea19d9f39795646239f487c6207Tinderbox User * purpose with or without fee is hereby granted, provided that the above
5347c0fcb04eaea19d9f39795646239f487c6207Tinderbox User * copyright notice and this permission notice appear in all copies.
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein *
d6fa26d0adaec6c910115be34fe7a5a5f402c14fMark Andrews * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
fd2597f75693a2279fdf588bd40dfe2407c42028Tinderbox User * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein * PERFORMANCE OF THIS SOFTWARE.
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein */
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein/* $Id: parser.c,v 1.121 2006/02/17 00:24:21 marka Exp $ */
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein/*! \file */
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
71c66a876ecca77923638d3f94cc0783152b2f03Mark Andrews#include <config.h>
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#include <isc/buffer.h>
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#include <isc/dir.h>
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#include <isc/formatcheck.h>
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#include <isc/lex.h>
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#include <isc/log.h>
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#include <isc/mem.h>
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#include <isc/net.h>
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#include <isc/netaddr.h>
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#include <isc/print.h>
fd2597f75693a2279fdf588bd40dfe2407c42028Tinderbox User#include <isc/string.h>
fd2597f75693a2279fdf588bd40dfe2407c42028Tinderbox User#include <isc/sockaddr.h>
fd2597f75693a2279fdf588bd40dfe2407c42028Tinderbox User#include <isc/netscope.h>
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#include <isc/util.h>
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#include <isc/symtab.h>
fd2597f75693a2279fdf588bd40dfe2407c42028Tinderbox User
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt#include <isccfg/cfg.h>
af40ebed6257e4ac1996144530b3de317cf4da11Tinderbox User#include <isccfg/grammar.h>
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#include <isccfg/log.h>
af40ebed6257e4ac1996144530b3de317cf4da11Tinderbox User
af40ebed6257e4ac1996144530b3de317cf4da11Tinderbox User/* Shorthand */
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#define CAT CFG_LOGCATEGORY_CONFIG
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt#define MOD CFG_LOGMODULE_PARSER
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein#define MAP_SYM 1 /* Unique type for isc_symtab */
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User#define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein/* Check a return value. */
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User#define CHECK(op) \
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User do { result = (op); \
9c6a5d1f22f972232d7a9fd5c5fa64f10bacbdffAutomatic Updater if (result != ISC_R_SUCCESS) goto cleanup; \
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User } while (0)
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt/* Clean up a configuration object if non-NULL. */
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt#define CLEANUP_OBJ(obj) \
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein/*
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein * Forward declarations of static functions.
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein */
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox Userstatic void
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinfree_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Userstatic isc_result_t
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Userparse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox Userstatic void
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Userprint_list(cfg_printer_t *pctx, cfg_obj_t *obj);
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Userstatic void
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Userfree_list(cfg_parser_t *pctx, cfg_obj_t *obj);
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox Userstatic isc_result_t
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Usercreate_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox Userstatic isc_result_t
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeincreate_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
ac93437301f55ed69bf85883a497a75598c628f9Automatic Updater cfg_obj_t **ret);
ac93437301f55ed69bf85883a497a75598c628f9Automatic Updater
ac93437301f55ed69bf85883a497a75598c628f9Automatic Updaterstatic void
5a4557e8de2951a2796676b5ec4b6a90caa5be14Mark Andrewsfree_string(cfg_parser_t *pctx, cfg_obj_t *obj);
77dccf2a5d9327d16b4374a135cdb99bdd48620eAutomatic Updater
77dccf2a5d9327d16b4374a135cdb99bdd48620eAutomatic Updaterstatic isc_result_t
5a4557e8de2951a2796676b5ec4b6a90caa5be14Mark Andrewscreate_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
47012ae6dbf18a2503d7b33c1c9583dc38625cb7Mark Andrews
ac93437301f55ed69bf85883a497a75598c628f9Automatic Updaterstatic void
ac93437301f55ed69bf85883a497a75598c628f9Automatic Updaterfree_map(cfg_parser_t *pctx, cfg_obj_t *obj);
5a4557e8de2951a2796676b5ec4b6a90caa5be14Mark Andrews
f293a69bcd1c1dd7bdac8f4102fc2398b9e475c8Eric Lucestatic isc_result_t
f293a69bcd1c1dd7bdac8f4102fc2398b9e475c8Eric Luceparse_symtab_elt(cfg_parser_t *pctx, const char *name,
f293a69bcd1c1dd7bdac8f4102fc2398b9e475c8Eric Luce cfg_type_t *elttype, isc_symtab_t *symtab,
f293a69bcd1c1dd7bdac8f4102fc2398b9e475c8Eric Luce isc_boolean_t callback);
f293a69bcd1c1dd7bdac8f4102fc2398b9e475c8Eric Luce
f293a69bcd1c1dd7bdac8f4102fc2398b9e475c8Eric Lucestatic void
f293a69bcd1c1dd7bdac8f4102fc2398b9e475c8Eric Lucefree_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
f293a69bcd1c1dd7bdac8f4102fc2398b9e475c8Eric Luce
f293a69bcd1c1dd7bdac8f4102fc2398b9e475c8Eric Lucestatic isc_result_t
47012ae6dbf18a2503d7b33c1c9583dc38625cb7Mark Andrewscfg_getstringtoken(cfg_parser_t *pctx);
f293a69bcd1c1dd7bdac8f4102fc2398b9e475c8Eric Luce
f293a69bcd1c1dd7bdac8f4102fc2398b9e475c8Eric Lucestatic void
f293a69bcd1c1dd7bdac8f4102fc2398b9e475c8Eric Luceparser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
f293a69bcd1c1dd7bdac8f4102fc2398b9e475c8Eric Luce unsigned int flags, const char *format, va_list args);
f293a69bcd1c1dd7bdac8f4102fc2398b9e475c8Eric Luce
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein/*
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User * Data representations. These correspond to members of the
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User * "value" union in struct cfg_obj (except "void", which does
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User * not need a union member).
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User */
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Usercfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Usercfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox Usercfg_rep_t cfg_rep_string = { "string", free_string };
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Usercfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Usercfg_rep_t cfg_rep_map = { "map", free_map };
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Usercfg_rep_t cfg_rep_list = { "list", free_list };
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Usercfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Usercfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Usercfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Usercfg_rep_t cfg_rep_void = { "void", free_noop };
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User/*
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User * Configuration type definitions.
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User */
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User/*%
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User * An implicit list. These are formed by clauses that occur multiple times.
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User */
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Huntstatic cfg_type_t cfg_type_implicitlist = {
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User "implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL };
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User/* Functions. */
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Uservoid
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Usercfg_print_obj(cfg_printer_t *pctx, cfg_obj_t *obj) {
ffe29868b4bbc64953fc5d0de51f988c20158967Tinderbox User obj->type->print(pctx, obj);
ffe29868b4bbc64953fc5d0de51f988c20158967Tinderbox User}
ffe29868b4bbc64953fc5d0de51f988c20158967Tinderbox User
ffe29868b4bbc64953fc5d0de51f988c20158967Tinderbox Uservoid
ffe29868b4bbc64953fc5d0de51f988c20158967Tinderbox Usercfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
ffe29868b4bbc64953fc5d0de51f988c20158967Tinderbox User pctx->f(pctx->closure, text, len);
ffe29868b4bbc64953fc5d0de51f988c20158967Tinderbox User}
ffe29868b4bbc64953fc5d0de51f988c20158967Tinderbox User
ffe29868b4bbc64953fc5d0de51f988c20158967Tinderbox Userstatic void
ffe29868b4bbc64953fc5d0de51f988c20158967Tinderbox Userprint_open(cfg_printer_t *pctx) {
ffe29868b4bbc64953fc5d0de51f988c20158967Tinderbox User cfg_print_chars(pctx, "{\n", 2);
ffe29868b4bbc64953fc5d0de51f988c20158967Tinderbox User pctx->indent++;
ffe29868b4bbc64953fc5d0de51f988c20158967Tinderbox User}
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Userstatic void
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Userprint_indent(cfg_printer_t *pctx) {
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User int indent = pctx->indent;
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt while (indent > 0) {
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User cfg_print_chars(pctx, "\t", 1);
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User indent--;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User }
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User}
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Userstatic void
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Userprint_close(cfg_printer_t *pctx) {
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User pctx->indent--;
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User print_indent(pctx);
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User cfg_print_chars(pctx, "}", 1);
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User}
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Userisc_result_t
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Usercfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User isc_result_t result;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User INSIST(ret != NULL && *ret == NULL);
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User result = type->parse(pctx, type, ret);
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User if (result != ISC_R_SUCCESS)
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User return (result);
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User INSIST(*ret != NULL);
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User return (ISC_R_SUCCESS);
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User}
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Uservoid
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox Usercfg_print(cfg_obj_t *obj,
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User void (*f)(void *closure, const char *text, int textlen),
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User void *closure)
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User{
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User cfg_printer_t pctx;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User pctx.f = f;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User pctx.closure = closure;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User pctx.indent = 0;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User obj->type->print(&pctx, obj);
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User}
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User/* Tuples. */
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Userisc_result_t
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox Usercfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User isc_result_t result;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User const cfg_tuplefielddef_t *fields = type->of;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User const cfg_tuplefielddef_t *f;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User cfg_obj_t *obj = NULL;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User unsigned int nfields = 0;
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt int i;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User for (f = fields; f->name != NULL; f++)
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User nfields++;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User CHECK(cfg_create_obj(pctx, type, &obj));
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User obj->value.tuple = isc_mem_get(pctx->mctx,
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User nfields * sizeof(cfg_obj_t *));
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User if (obj->value.tuple == NULL) {
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User result = ISC_R_NOMEMORY;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User goto cleanup;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User }
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User for (f = fields, i = 0; f->name != NULL; f++, i++)
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User obj->value.tuple[i] = NULL;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User *ret = obj;
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User return (ISC_R_SUCCESS);
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User cleanup:
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User if (obj != NULL)
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User isc_mem_put(pctx->mctx, obj, sizeof(*obj));
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User return (result);
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt}
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Userisc_result_t
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Usercfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User{
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User isc_result_t result;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User const cfg_tuplefielddef_t *fields = type->of;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User const cfg_tuplefielddef_t *f;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User cfg_obj_t *obj = NULL;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User unsigned int i;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User CHECK(cfg_create_tuple(pctx, type, &obj));
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User for (f = fields, i = 0; f->name != NULL; f++, i++)
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User *ret = obj;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User return (ISC_R_SUCCESS);
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User cleanup:
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User CLEANUP_OBJ(obj);
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User return (result);
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User}
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Uservoid
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox Usercfg_print_tuple(cfg_printer_t *pctx, cfg_obj_t *obj) {
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User unsigned int i;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User const cfg_tuplefielddef_t *fields = obj->type->of;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User const cfg_tuplefielddef_t *f;
aa1905addf2f33d90aa020080e4e77a8651e829aTinderbox User isc_boolean_t need_space = ISC_FALSE;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User for (f = fields, i = 0; f->name != NULL; f++, i++) {
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User cfg_obj_t *fieldobj = obj->value.tuple[i];
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User if (need_space)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein cfg_print_chars(pctx, " ", 1);
af40ebed6257e4ac1996144530b3de317cf4da11Tinderbox User cfg_print_obj(pctx, fieldobj);
4abdfc917e6635a7c81d1f931a0c79227e72d025Mark Andrews need_space = ISC_TF(fieldobj->type->print != cfg_print_void);
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User }
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User}
9c6a5d1f22f972232d7a9fd5c5fa64f10bacbdffAutomatic Updater
9c6a5d1f22f972232d7a9fd5c5fa64f10bacbdffAutomatic Updatervoid
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Huntcfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt const cfg_tuplefielddef_t *fields = type->of;
9c6a5d1f22f972232d7a9fd5c5fa64f10bacbdffAutomatic Updater const cfg_tuplefielddef_t *f;
9c6a5d1f22f972232d7a9fd5c5fa64f10bacbdffAutomatic Updater isc_boolean_t need_space = ISC_FALSE;
9c6a5d1f22f972232d7a9fd5c5fa64f10bacbdffAutomatic Updater
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein for (f = fields; f->name != NULL; f++) {
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User if (need_space)
71c66a876ecca77923638d3f94cc0783152b2f03Mark Andrews cfg_print_chars(pctx, " ", 1);
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein cfg_doc_obj(pctx, f->type);
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt need_space = ISC_TF(f->type->print != cfg_print_void);
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein }
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User}
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Huntstatic void
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinfree_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein unsigned int i;
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User const cfg_tuplefielddef_t *fields = obj->type->of;
9c6a5d1f22f972232d7a9fd5c5fa64f10bacbdffAutomatic Updater const cfg_tuplefielddef_t *f;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein unsigned int nfields = 0;
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User if (obj->value.tuple == NULL)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return;
af40ebed6257e4ac1996144530b3de317cf4da11Tinderbox User
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User for (f = fields, i = 0; f->name != NULL; f++, i++) {
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User CLEANUP_OBJ(obj->value.tuple[i]);
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt nfields++;
56bd026e6c96482dccab83778bf8f9c92c36bf11Tinderbox User }
56bd026e6c96482dccab83778bf8f9c92c36bf11Tinderbox User isc_mem_put(pctx->mctx, obj->value.tuple,
56bd026e6c96482dccab83778bf8f9c92c36bf11Tinderbox User nfields * sizeof(cfg_obj_t *));
56bd026e6c96482dccab83778bf8f9c92c36bf11Tinderbox User}
56bd026e6c96482dccab83778bf8f9c92c36bf11Tinderbox User
56bd026e6c96482dccab83778bf8f9c92c36bf11Tinderbox Userisc_boolean_t
56bd026e6c96482dccab83778bf8f9c92c36bf11Tinderbox Usercfg_obj_istuple(cfg_obj_t *obj) {
56bd026e6c96482dccab83778bf8f9c92c36bf11Tinderbox User REQUIRE(obj != NULL);
56bd026e6c96482dccab83778bf8f9c92c36bf11Tinderbox User return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein}
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User
bea931e17b7567f09107f93ab7e25c7f00abeb9cMark Andrewscfg_obj_t *
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Huntcfg_tuple_get(cfg_obj_t *tupleobj, const char* name) {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein unsigned int i;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein const cfg_tuplefielddef_t *fields;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein const cfg_tuplefielddef_t *f;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
47012ae6dbf18a2503d7b33c1c9583dc38625cb7Mark Andrews
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein fields = tupleobj->type->of;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein for (f = fields, i = 0; f->name != NULL; f++, i++) {
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User if (strcmp(f->name, name) == 0)
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User return (tupleobj->value.tuple[i]);
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User }
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein INSIST(0);
af40ebed6257e4ac1996144530b3de317cf4da11Tinderbox User return (NULL);
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User}
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Huntisc_result_t
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeincfg_parse_special(cfg_parser_t *pctx, int special) {
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt isc_result_t result;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein CHECK(cfg_gettoken(pctx, 0));
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt if (pctx->token.type == isc_tokentype_special &&
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein pctx->token.value.as_char == special)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return (ISC_R_SUCCESS);
71c66a876ecca77923638d3f94cc0783152b2f03Mark Andrews
5a4557e8de2951a2796676b5ec4b6a90caa5be14Mark Andrews cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return (ISC_R_UNEXPECTEDTOKEN);
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User cleanup:
5a4557e8de2951a2796676b5ec4b6a90caa5be14Mark Andrews return (result);
dec590a3deb8e87380a8bd3a77d535dba3729bf6Tinderbox User}
dec590a3deb8e87380a8bd3a77d535dba3729bf6Tinderbox User
5a4557e8de2951a2796676b5ec4b6a90caa5be14Mark Andrews/*
5a4557e8de2951a2796676b5ec4b6a90caa5be14Mark Andrews * Parse a required semicolon. If it is not there, log
dec590a3deb8e87380a8bd3a77d535dba3729bf6Tinderbox User * an error and increment the error count but continue
dec590a3deb8e87380a8bd3a77d535dba3729bf6Tinderbox User * parsing. Since the next token is pushed back,
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User * care must be taken to make sure it is eventually
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User * consumed or an infinite loop may result.
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User */
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox Userstatic isc_result_t
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinparse_semicolon(cfg_parser_t *pctx) {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein isc_result_t result;
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User CHECK(cfg_gettoken(pctx, 0));
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User if (pctx->token.type == isc_tokentype_special &&
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein pctx->token.value.as_char == ';')
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return (ISC_R_SUCCESS);
71c66a876ecca77923638d3f94cc0783152b2f03Mark Andrews
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein cfg_ungettoken(pctx);
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein cleanup:
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt return (result);
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein}
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein/*
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein * Parse EOF, logging and returning an error if not there.
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt */
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinstatic isc_result_t
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinparse_eof(cfg_parser_t *pctx) {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein isc_result_t result;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein CHECK(cfg_gettoken(pctx, 0));
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if (pctx->token.type == isc_tokentype_eof)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return (ISC_R_SUCCESS);
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return (ISC_R_UNEXPECTEDTOKEN);
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein cleanup:
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt return (result);
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein}
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
14a656f94b1fd0ababd84a772228dfa52276ba15Evan Hunt/* A list of files, used internally for pctx->files. */
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinstatic cfg_type_t cfg_type_filelist = {
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User "filelist", NULL, print_list, NULL, &cfg_rep_list,
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User &cfg_type_qstring
b05bdb520d83f7ecaad708fe305268c3420be01dMark Andrews};
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinisc_result_t
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeincfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein isc_result_t result;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein cfg_parser_t *pctx;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein isc_lexspecials_t specials;
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User REQUIRE(mctx != NULL);
7911e6f9de303bca5a3d8b34f4330c8f7cecffaeTinderbox User REQUIRE(ret != NULL && *ret == NULL);
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein pctx = isc_mem_get(mctx, sizeof(*pctx));
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if (pctx == NULL)
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return (ISC_R_NOMEMORY);
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein pctx->mctx = mctx;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein pctx->lctx = lctx;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein pctx->lexer = NULL;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein pctx->seen_eof = ISC_FALSE;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein pctx->ungotten = ISC_FALSE;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein pctx->errors = 0;
71c66a876ecca77923638d3f94cc0783152b2f03Mark Andrews pctx->warnings = 0;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein pctx->open_files = NULL;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein pctx->closed_files = NULL;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein pctx->line = 0;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein pctx->callback = NULL;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein pctx->callbackarg = NULL;
1ca759b3f5c0672b2a66bc02288fe010cabbfe37Tinderbox User pctx->token.type = isc_tokentype_unknown;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein 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));
CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
*ret = pctx;
return (ISC_R_SUCCESS);
cleanup:
if (pctx->lexer != NULL)
isc_lex_destroy(&pctx->lexer);
CLEANUP_OBJ(pctx->open_files);
CLEANUP_OBJ(pctx->closed_files);
isc_mem_put(mctx, pctx, sizeof(*pctx));
return (result);
}
static isc_result_t
parser_openfile(cfg_parser_t *pctx, const char *filename) {
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) {
cfg_parser_error(pctx, 0, "open: %s: %s",
filename, isc_result_totext(result));
goto cleanup;
}
CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
CHECK(create_listelt(pctx, &elt));
elt->obj = stringobj;
ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
return (ISC_R_SUCCESS);
cleanup:
CLEANUP_OBJ(stringobj);
return (result);
}
void
cfg_parser_setcallback(cfg_parser_t *pctx,
cfg_parsecallback_t callback,
void *arg)
{
pctx->callback = callback;
pctx->callbackarg = arg;
}
/*
* Parse a configuration using a pctx where a lexer has already
* been set up with a source.
*/
static isc_result_t
parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
isc_result_t result;
cfg_obj_t *obj = NULL;
result = cfg_parse_obj(pctx, type, &obj);
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. */
cfg_parser_error(pctx, 0, "parsing failed");
goto cleanup;
}
CHECK(parse_eof(pctx));
*ret = obj;
return (ISC_R_SUCCESS);
cleanup:
CLEANUP_OBJ(obj);
return (result);
}
isc_result_t
cfg_parse_file(cfg_parser_t *pctx, const char *filename,
const cfg_type_t *type, cfg_obj_t **ret)
{
isc_result_t result;
REQUIRE(filename != NULL);
CHECK(parser_openfile(pctx, filename));
CHECK(parse2(pctx, type, ret));
cleanup:
return (result);
}
isc_result_t
cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
const cfg_type_t *type, cfg_obj_t **ret)
{
isc_result_t result;
REQUIRE(buffer != NULL);
CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
CHECK(parse2(pctx, type, ret));
cleanup:
return (result);
}
void
cfg_parser_destroy(cfg_parser_t **pctxp) {
cfg_parser_t *pctx = *pctxp;
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);
isc_mem_put(pctx->mctx, pctx, sizeof(*pctx));
*pctxp = NULL;
}
/*
* void
*/
isc_result_t
cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
UNUSED(type);
return (cfg_create_obj(pctx, &cfg_type_void, ret));
}
void
cfg_print_void(cfg_printer_t *pctx, cfg_obj_t *obj) {
UNUSED(pctx);
UNUSED(obj);
}
void
cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
UNUSED(pctx);
UNUSED(type);
}
isc_boolean_t
cfg_obj_isvoid(cfg_obj_t *obj) {
REQUIRE(obj != NULL);
return (ISC_TF(obj->type->rep == &cfg_rep_void));
}
cfg_type_t cfg_type_void = {
"void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
NULL };
/*
* uint32
*/
isc_result_t
cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
isc_result_t result;
cfg_obj_t *obj = NULL;
UNUSED(type);
CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
if (pctx->token.type != isc_tokentype_number) {
cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
return (ISC_R_UNEXPECTEDTOKEN);
}
CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
obj->value.uint32 = pctx->token.value.as_ulong;
*ret = obj;
cleanup:
return (result);
}
void
cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
cfg_print_chars(pctx, s, strlen(s));
}
void
cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
char buf[32];
snprintf(buf, sizeof(buf), "%u", u);
cfg_print_cstr(pctx, buf);
}
void
cfg_print_uint32(cfg_printer_t *pctx, cfg_obj_t *obj) {
cfg_print_rawuint(pctx, obj->value.uint32);
}
isc_boolean_t
cfg_obj_isuint32(cfg_obj_t *obj) {
REQUIRE(obj != NULL);
return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
}
isc_uint32_t
cfg_obj_asuint32(cfg_obj_t *obj) {
REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
return (obj->value.uint32);
}
cfg_type_t cfg_type_uint32 = {
"integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
&cfg_rep_uint32, NULL
};
/*
* uint64
*/
isc_boolean_t
cfg_obj_isuint64(cfg_obj_t *obj) {
REQUIRE(obj != NULL);
return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
}
isc_uint64_t
cfg_obj_asuint64(cfg_obj_t *obj) {
REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
return (obj->value.uint64);
}
void
cfg_print_uint64(cfg_printer_t *pctx, cfg_obj_t *obj) {
char buf[32];
snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
obj->value.uint64);
cfg_print_cstr(pctx, buf);
}
cfg_type_t cfg_type_uint64 = {
"64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
&cfg_rep_uint64, NULL
};
/*
* qstring (quoted string), ustring (unquoted string), astring
* (any string)
*/
/* Create a string object from a null-terminated C string. */
static isc_result_t
create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
cfg_obj_t **ret)
{
isc_result_t result;
cfg_obj_t *obj = NULL;
int len;
CHECK(cfg_create_obj(pctx, type, &obj));
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) {
isc_mem_put(pctx->mctx, obj, sizeof(*obj));
return (ISC_R_NOMEMORY);
}
memcpy(obj->value.string.base, contents, len);
obj->value.string.base[len] = '\0';
*ret = obj;
cleanup:
return (result);
}
isc_result_t
cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
isc_result_t result;
UNUSED(type);
CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
if (pctx->token.type != isc_tokentype_qstring) {
cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
return (ISC_R_UNEXPECTEDTOKEN);
}
return (create_string(pctx,
TOKEN_STRING(pctx),
&cfg_type_qstring,
ret));
cleanup:
return (result);
}
static isc_result_t
parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
isc_result_t result;
UNUSED(type);
CHECK(cfg_gettoken(pctx, 0));
if (pctx->token.type != isc_tokentype_string) {
cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
return (ISC_R_UNEXPECTEDTOKEN);
}
return (create_string(pctx,
TOKEN_STRING(pctx),
&cfg_type_ustring,
ret));
cleanup:
return (result);
}
isc_result_t
cfg_parse_astring(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_qstring,
ret));
cleanup:
return (result);
}
isc_boolean_t
cfg_is_enum(const char *s, const char *const *enums) {
const char * const *p;
for (p = enums; *p != NULL; p++) {
if (strcasecmp(*p, s) == 0)
return (ISC_TRUE);
}
return (ISC_FALSE);
}
static isc_result_t
check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
const char *s = obj->value.string.base;
if (cfg_is_enum(s, enums))
return (ISC_R_SUCCESS);
cfg_parser_error(pctx, 0, "'%s' unexpected", s);
return (ISC_R_UNEXPECTEDTOKEN);
}
isc_result_t
cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
isc_result_t result;
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:
CLEANUP_OBJ(obj);
return (result);
}
void
cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
const char * const *p;
cfg_print_chars(pctx, "( ", 2);
for (p = type->of; *p != NULL; p++) {
cfg_print_cstr(pctx, *p);
if (p[1] != NULL)
cfg_print_chars(pctx, " | ", 3);
}
cfg_print_chars(pctx, " )", 2);
}
void
cfg_print_ustring(cfg_printer_t *pctx, cfg_obj_t *obj) {
cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
}
static void
print_qstring(cfg_printer_t *pctx, cfg_obj_t *obj) {
cfg_print_chars(pctx, "\"", 1);
cfg_print_ustring(pctx, obj);
cfg_print_chars(pctx, "\"", 1);
}
static void
free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
isc_mem_put(pctx->mctx, obj->value.string.base,
obj->value.string.length + 1);
}
isc_boolean_t
cfg_obj_isstring(cfg_obj_t *obj) {
REQUIRE(obj != NULL);
return (ISC_TF(obj->type->rep == &cfg_rep_string));
}
const char *
cfg_obj_asstring(cfg_obj_t *obj) {
REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
return (obj->value.string.base);
}
/* 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
};
/*
* Booleans
*/
isc_boolean_t
cfg_obj_isboolean(cfg_obj_t *obj) {
REQUIRE(obj != NULL);
return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
}
isc_boolean_t
cfg_obj_asboolean(cfg_obj_t *obj) {
REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
return (obj->value.boolean);
}
static isc_result_t
parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
{
isc_result_t result;
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)
goto bad_boolean;
if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
(strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
(strcmp(TOKEN_STRING(pctx), "1") == 0)) {
value = ISC_TRUE;
} else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
(strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
(strcmp(TOKEN_STRING(pctx), "0") == 0)) {
value = ISC_FALSE;
} else {
goto bad_boolean;
}
CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
obj->value.boolean = value;
*ret = obj;
return (result);
bad_boolean:
cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
return (ISC_R_UNEXPECTEDTOKEN);
cleanup:
return (result);
}
static void
print_boolean(cfg_printer_t *pctx, cfg_obj_t *obj) {
if (obj->value.boolean)
cfg_print_chars(pctx, "yes", 3);
else
cfg_print_chars(pctx, "no", 2);
}
cfg_type_t cfg_type_boolean = {
"boolean", parse_boolean, print_boolean, cfg_doc_terminal,
&cfg_rep_boolean, NULL
};
/*
* Lists.
*/
isc_result_t
cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
isc_result_t result;
CHECK(cfg_create_obj(pctx, type, obj));
ISC_LIST_INIT((*obj)->value.list);
cleanup:
return (result);
}
static isc_result_t
create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
cfg_listelt_t *elt;
elt = isc_mem_get(pctx->mctx, sizeof(*elt));
if (elt == NULL)
return (ISC_R_NOMEMORY);
elt->obj = NULL;
ISC_LINK_INIT(elt, link);
*eltp = elt;
return (ISC_R_SUCCESS);
}
static void
free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
cfg_obj_destroy(pctx, &elt->obj);
isc_mem_put(pctx->mctx, elt, sizeof(*elt));
}
static void
free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
cfg_listelt_t *elt, *next;
for (elt = ISC_LIST_HEAD(obj->value.list);
elt != NULL;
elt = next)
{
next = ISC_LIST_NEXT(elt, link);
free_list_elt(pctx, elt);
}
}
isc_result_t
cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
cfg_listelt_t **ret)
{
isc_result_t result;
cfg_listelt_t *elt = NULL;
cfg_obj_t *value = NULL;
CHECK(create_listelt(pctx, &elt));
result = cfg_parse_obj(pctx, elttype, &value);
if (result != ISC_R_SUCCESS)
goto cleanup;
elt->obj = value;
*ret = elt;
return (ISC_R_SUCCESS);
cleanup:
isc_mem_put(pctx->mctx, elt, sizeof(*elt));
return (result);
}
/*
* Parse a homogeneous list whose elements are of type 'elttype'
* and where each element is terminated by a semicolon.
*/
static isc_result_t
parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
{
cfg_obj_t *listobj = NULL;
const cfg_type_t *listof = listtype->of;
isc_result_t result;
cfg_listelt_t *elt = NULL;
CHECK(cfg_create_list(pctx, listtype, &listobj));
for (;;) {
CHECK(cfg_peektoken(pctx, 0));
if (pctx->token.type == isc_tokentype_special &&
pctx->token.value.as_char == /*{*/ '}')
break;
CHECK(cfg_parse_listelt(pctx, listof, &elt));
CHECK(parse_semicolon(pctx));
ISC_LIST_APPEND(listobj->value.list, elt, link);
elt = NULL;
}
*ret = listobj;
return (ISC_R_SUCCESS);
cleanup:
if (elt != NULL)
free_list_elt(pctx, elt);
CLEANUP_OBJ(listobj);
return (result);
}
static void
print_list(cfg_printer_t *pctx, cfg_obj_t *obj) {
cfg_list_t *list = &obj->value.list;
cfg_listelt_t *elt;
for (elt = ISC_LIST_HEAD(*list);
elt != NULL;
elt = ISC_LIST_NEXT(elt, link)) {
print_indent(pctx);
cfg_print_obj(pctx, elt->obj);
cfg_print_chars(pctx, ";\n", 2);
}
}
isc_result_t
cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
cfg_obj_t **ret)
{
isc_result_t result;
CHECK(cfg_parse_special(pctx, '{'));
CHECK(parse_list(pctx, type, ret));
CHECK(cfg_parse_special(pctx, '}'));
cleanup:
return (result);
}
void
cfg_print_bracketed_list(cfg_printer_t *pctx, cfg_obj_t *obj) {
print_open(pctx);
print_list(pctx, obj);
print_close(pctx);
}
void
cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
cfg_print_chars(pctx, "{ ", 2);
cfg_doc_obj(pctx, type->of);
cfg_print_chars(pctx, "; ... }", 7);
}
/*
* Parse a homogeneous list whose elements are of type 'elttype'
* and where elements are separated by space. The list ends
* before the first semicolon.
*/
isc_result_t
cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
cfg_obj_t **ret)
{
cfg_obj_t *listobj = NULL;
const cfg_type_t *listof = listtype->of;
isc_result_t result;
CHECK(cfg_create_list(pctx, listtype, &listobj));
for (;;) {
cfg_listelt_t *elt = NULL;
CHECK(cfg_peektoken(pctx, 0));
if (pctx->token.type == isc_tokentype_special &&
pctx->token.value.as_char == ';')
break;
CHECK(cfg_parse_listelt(pctx, listof, &elt));
ISC_LIST_APPEND(listobj->value.list, elt, link);
}
*ret = listobj;
return (ISC_R_SUCCESS);
cleanup:
CLEANUP_OBJ(listobj);
return (result);
}
void
cfg_print_spacelist(cfg_printer_t *pctx, cfg_obj_t *obj) {
cfg_list_t *list = &obj->value.list;
cfg_listelt_t *elt;
for (elt = ISC_LIST_HEAD(*list);
elt != NULL;
elt = ISC_LIST_NEXT(elt, link)) {
cfg_print_obj(pctx, elt->obj);
if (ISC_LIST_NEXT(elt, link) != NULL)
cfg_print_chars(pctx, " ", 1);
}
}
isc_boolean_t
cfg_obj_islist(cfg_obj_t *obj) {
REQUIRE(obj != NULL);
return (ISC_TF(obj->type->rep == &cfg_rep_list));
}
cfg_listelt_t *
cfg_list_first(cfg_obj_t *obj) {
REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
if (obj == NULL)
return (NULL);
return (ISC_LIST_HEAD(obj->value.list));
}
cfg_listelt_t *
cfg_list_next(cfg_listelt_t *elt) {
REQUIRE(elt != NULL);
return (ISC_LIST_NEXT(elt, link));
}
cfg_obj_t *
cfg_listelt_value(cfg_listelt_t *elt) {
REQUIRE(elt != NULL);
return (elt->obj);
}
/*
* Maps.
*/
/*
* Parse a map body. That's something like
*
* "foo 1; bar { glub; }; zap true; zap false;"
*
* i.e., a sequence of option names followed by values and
* terminated by semicolons. Used for the top level of
* the named.conf syntax, as well as for the body of the
* options, view, zone, and other statements.
*/
isc_result_t
cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
{
const cfg_clausedef_t * const *clausesets = type->of;
isc_result_t result;
const cfg_clausedef_t * const *clauseset;
const cfg_clausedef_t *clause;
cfg_obj_t *value = NULL;
cfg_obj_t *obj = NULL;
cfg_obj_t *eltobj = NULL;
cfg_obj_t *includename = NULL;
isc_symvalue_t symval;
cfg_list_t *list = NULL;
CHECK(create_map(pctx, type, &obj));
obj->value.map.clausesets = clausesets;
for (;;) {
cfg_listelt_t *elt;
redo:
/*
* Parse the option name and see if it is known.
*/
CHECK(cfg_gettoken(pctx, 0));
if (pctx->token.type != isc_tokentype_string) {
cfg_ungettoken(pctx);
break;
}
/*
* We accept "include" statements wherever a map body
* clause can occur.
*/
if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
/*
* Turn the file name into a temporary configuration
* object just so that it is not overwritten by the
* semicolon token.
*/
CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
CHECK(parse_semicolon(pctx));
CHECK(parser_openfile(pctx, includename->
value.string.base));
cfg_obj_destroy(pctx, &includename);
goto redo;
}
clause = NULL;
for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
for (clause = *clauseset;
clause->name != NULL;
clause++) {
if (strcasecmp(TOKEN_STRING(pctx),
clause->name) == 0)
goto done;
}
}
done:
if (clause == NULL || clause->name == NULL) {
cfg_parser_error(pctx, CFG_LOG_NOPREP, "unknown option");
/*
* Try to recover by parsing this option as an unknown
* option and discarding it.
*/
CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, &eltobj));
cfg_obj_destroy(pctx, &eltobj);
CHECK(parse_semicolon(pctx));
continue;
}
/* Clause is known. */
/* Issue warnings if appropriate */
if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0)
cfg_parser_warning(pctx, 0, "option '%s' is obsolete",
clause->name);
if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0)
cfg_parser_warning(pctx, 0, "option '%s' is "
"not implemented", clause->name);
if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0)
cfg_parser_warning(pctx, 0, "option '%s' is "
"not implemented", clause->name);
/*
* Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
* set here - we need to log the *lack* of such an option,
* not its presence.
*/
/* See if the clause already has a value; if not create one. */
result = isc_symtab_lookup(obj->value.map.symtab,
clause->name, 0, &symval);
if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
/* Multivalued clause */
cfg_obj_t *listobj = NULL;
if (result == ISC_R_NOTFOUND) {
CHECK(cfg_create_list(pctx,
&cfg_type_implicitlist,
&listobj));
symval.as_pointer = listobj;
result = isc_symtab_define(obj->value.
map.symtab,
clause->name,
1, symval,
isc_symexists_reject);
if (result != ISC_R_SUCCESS) {
cfg_parser_error(pctx, CFG_LOG_NEAR,
"isc_symtab_define(%s) "
"failed", clause->name);
isc_mem_put(pctx->mctx, list,
sizeof(cfg_list_t));
goto cleanup;
}
} else {
INSIST(result == ISC_R_SUCCESS);
listobj = symval.as_pointer;
}
elt = NULL;
CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
CHECK(parse_semicolon(pctx));
ISC_LIST_APPEND(listobj->value.list, elt, link);
} else {
/* Single-valued clause */
if (result == ISC_R_NOTFOUND) {
isc_boolean_t callback =
ISC_TF((clause->flags &
CFG_CLAUSEFLAG_CALLBACK) != 0);
CHECK(parse_symtab_elt(pctx, clause->name,
clause->type,
obj->value.map.symtab,
callback));
CHECK(parse_semicolon(pctx));
} else if (result == ISC_R_SUCCESS) {
cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
clause->name);
result = ISC_R_EXISTS;
goto cleanup;
} else {
cfg_parser_error(pctx, CFG_LOG_NEAR,
"isc_symtab_define() failed");
goto cleanup;
}
}
}
*ret = obj;
return (ISC_R_SUCCESS);
cleanup:
CLEANUP_OBJ(value);
CLEANUP_OBJ(obj);
CLEANUP_OBJ(eltobj);
CLEANUP_OBJ(includename);
return (result);
}
static isc_result_t
parse_symtab_elt(cfg_parser_t *pctx, const char *name,
cfg_type_t *elttype, isc_symtab_t *symtab,
isc_boolean_t callback)
{
isc_result_t result;
cfg_obj_t *obj = NULL;
isc_symvalue_t symval;
CHECK(cfg_parse_obj(pctx, elttype, &obj));
if (callback && pctx->callback != NULL)
CHECK(pctx->callback(name, obj, pctx->callbackarg));
symval.as_pointer = obj;
CHECK(isc_symtab_define(symtab, name,
1, symval,
isc_symexists_reject));
return (ISC_R_SUCCESS);
cleanup:
CLEANUP_OBJ(obj);
return (result);
}
/*
* Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
*/
isc_result_t
cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
isc_result_t result;
CHECK(cfg_parse_special(pctx, '{'));
CHECK(cfg_parse_mapbody(pctx, type, ret));
CHECK(cfg_parse_special(pctx, '}'));
cleanup:
return (result);
}
/*
* Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
*/
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)
{
isc_result_t result;
cfg_obj_t *idobj = NULL;
cfg_obj_t *mapobj = NULL;
CHECK(cfg_parse_obj(pctx, nametype, &idobj));
CHECK(cfg_parse_map(pctx, type, &mapobj));
mapobj->value.map.id = idobj;
idobj = NULL;
*ret = mapobj;
cleanup:
CLEANUP_OBJ(idobj);
return (result);
}
/*
* Parse a map identified by a string name. E.g., "name { foo 1; }".
* Used for the "key" and "channel" statements.
*/
isc_result_t
cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
}
/*
* Parse a map identified by a network address.
* Used to be used for the "server" statement.
*/
isc_result_t
cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
}
/*
* Parse a map identified by a network prefix.
* Used for the "server" statement.
*/
isc_result_t
cfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
return (parse_any_named_map(pctx, &cfg_type_netprefix, type, ret));
}
void
cfg_print_mapbody(cfg_printer_t *pctx, cfg_obj_t *obj) {
isc_result_t result = ISC_R_SUCCESS;
const cfg_clausedef_t * const *clauseset;
for (clauseset = obj->value.map.clausesets;
*clauseset != NULL;
clauseset++)
{
isc_symvalue_t symval;
const cfg_clausedef_t *clause;
for (clause = *clauseset;
clause->name != NULL;
clause++) {
result = isc_symtab_lookup(obj->value.map.symtab,
clause->name, 0, &symval);
if (result == ISC_R_SUCCESS) {
cfg_obj_t *obj = symval.as_pointer;
if (obj->type == &cfg_type_implicitlist) {
/* Multivalued. */
cfg_list_t *list = &obj->value.list;
cfg_listelt_t *elt;
for (elt = ISC_LIST_HEAD(*list);
elt != NULL;
elt = ISC_LIST_NEXT(elt, link)) {
print_indent(pctx);
cfg_print_cstr(pctx, clause->name);
cfg_print_chars(pctx, " ", 1);
cfg_print_obj(pctx, elt->obj);
cfg_print_chars(pctx, ";\n", 2);
}
} else {
/* Single-valued. */
print_indent(pctx);
cfg_print_cstr(pctx, clause->name);
cfg_print_chars(pctx, " ", 1);
cfg_print_obj(pctx, obj);
cfg_print_chars(pctx, ";\n", 2);
}
} else if (result == ISC_R_NOTFOUND) {
; /* do nothing */
} else {
INSIST(0);
}
}
}
}
void
cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
const cfg_clausedef_t * const *clauseset;
const cfg_clausedef_t *clause;
for (clauseset = type->of; *clauseset != NULL; clauseset++) {
for (clause = *clauseset;
clause->name != NULL;
clause++) {
cfg_print_cstr(pctx, clause->name);
cfg_print_chars(pctx, " ", 1);
cfg_doc_obj(pctx, clause->type);
cfg_print_chars(pctx, ";", 1);
/* XXX print flags here? */
cfg_print_chars(pctx, "\n\n", 2);
}
}
}
static struct flagtext {
unsigned int flag;
const char *text;
} flagtexts[] = {
{ CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
{ CFG_CLAUSEFLAG_NYI, "not yet implemented" },
{ CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
{ CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
{ 0, NULL }
};
void
cfg_print_map(cfg_printer_t *pctx, cfg_obj_t *obj) {
if (obj->value.map.id != NULL) {
cfg_print_obj(pctx, obj->value.map.id);
cfg_print_chars(pctx, " ", 1);
}
print_open(pctx);
cfg_print_mapbody(pctx, obj);
print_close(pctx);
}
static void
print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
struct flagtext *p;
isc_boolean_t first = ISC_TRUE;
for (p = flagtexts; p->flag != 0; p++) {
if ((flags & p->flag) != 0) {
if (first)
cfg_print_chars(pctx, " // ", 4);
else
cfg_print_chars(pctx, ", ", 2);
cfg_print_cstr(pctx, p->text);
first = ISC_FALSE;
}
}
}
void
cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
const cfg_clausedef_t * const *clauseset;
const cfg_clausedef_t *clause;
if (type->parse == cfg_parse_named_map) {
cfg_doc_obj(pctx, &cfg_type_astring);
cfg_print_chars(pctx, " ", 1);
} else if (type->parse == cfg_parse_addressed_map) {
cfg_doc_obj(pctx, &cfg_type_netaddr);
cfg_print_chars(pctx, " ", 1);
} else if (type->parse == cfg_parse_netprefix_map) {
cfg_doc_obj(pctx, &cfg_type_netprefix);
cfg_print_chars(pctx, " ", 1);
}
print_open(pctx);
for (clauseset = type->of; *clauseset != NULL; clauseset++) {
for (clause = *clauseset;
clause->name != NULL;
clause++) {
print_indent(pctx);
cfg_print_cstr(pctx, clause->name);
if (clause->type->print != cfg_print_void)
cfg_print_chars(pctx, " ", 1);
cfg_doc_obj(pctx, clause->type);
cfg_print_chars(pctx, ";", 1);
print_clause_flags(pctx, clause->flags);
cfg_print_chars(pctx, "\n", 1);
}
}
print_close(pctx);
}
isc_boolean_t
cfg_obj_ismap(cfg_obj_t *obj) {
REQUIRE(obj != NULL);
return (ISC_TF(obj->type->rep == &cfg_rep_map));
}
isc_result_t
cfg_map_get(cfg_obj_t *mapobj, const char* name, cfg_obj_t **obj) {
isc_result_t result;
isc_symvalue_t val;
cfg_map_t *map;
REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
REQUIRE(name != NULL);
REQUIRE(obj != NULL && *obj == NULL);
map = &mapobj->value.map;
result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
if (result != ISC_R_SUCCESS)
return (result);
*obj = val.as_pointer;
return (ISC_R_SUCCESS);
}
cfg_obj_t *
cfg_map_getname(cfg_obj_t *mapobj) {
REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
return (mapobj->value.map.id);
}
/* Parse an arbitrary token, storing its raw text representation. */
static isc_result_t
parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
cfg_obj_t *obj = NULL;
isc_result_t result;
isc_region_t r;
UNUSED(type);
CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
if (pctx->token.type == isc_tokentype_eof) {
cfg_ungettoken(pctx);
result = ISC_R_EOF;
goto cleanup;
}
isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
if (obj->value.string.base == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
obj->value.string.length = r.length;
memcpy(obj->value.string.base, r.base, r.length);
obj->value.string.base[r.length] = '\0';
*ret = obj;
return (result);
cleanup:
if (obj != NULL)
isc_mem_put(pctx->mctx, obj, sizeof(*obj));
return (result);
}
cfg_type_t cfg_type_token = {
"token", parse_token, cfg_print_ustring, cfg_doc_terminal,
&cfg_rep_string, NULL
};
/*
* An unsupported option. This is just a list of tokens with balanced braces
* ending in a semicolon.
*/
static isc_result_t
parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
cfg_obj_t *listobj = NULL;
isc_result_t result;
int braces = 0;
CHECK(cfg_create_list(pctx, type, &listobj));
for (;;) {
cfg_listelt_t *elt = NULL;
CHECK(cfg_peektoken(pctx, 0));
if (pctx->token.type == isc_tokentype_special) {
if (pctx->token.value.as_char == '{')
braces++;
else if (pctx->token.value.as_char == '}')
braces--;
else if (pctx->token.value.as_char == ';')
if (braces == 0)
break;
}
if (pctx->token.type == isc_tokentype_eof || braces < 0) {
cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
result = ISC_R_UNEXPECTEDTOKEN;
goto cleanup;
}
CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
ISC_LIST_APPEND(listobj->value.list, elt, link);
}
INSIST(braces == 0);
*ret = listobj;
return (ISC_R_SUCCESS);
cleanup:
CLEANUP_OBJ(listobj);
return (result);
}
cfg_type_t cfg_type_unsupported = {
"unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal,
&cfg_rep_list, NULL
};
/*
* Try interpreting the current token as a network address.
*
* If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
* and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set. The
* "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
* set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
* and the IPv6 wildcard address otherwise.
*/
static isc_result_t
token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
char *s;
struct in_addr in4a;
struct in6_addr in6a;
if (pctx->token.type != isc_tokentype_string)
return (ISC_R_UNEXPECTEDTOKEN);
s = TOKEN_STRING(pctx);
if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
if ((flags & CFG_ADDR_V4OK) != 0) {
isc_netaddr_any(na);
return (ISC_R_SUCCESS);
} else if ((flags & CFG_ADDR_V6OK) != 0) {
isc_netaddr_any6(na);
return (ISC_R_SUCCESS);
} else {
INSIST(0);
}
} else {
if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
if (inet_pton(AF_INET, s, &in4a) == 1) {
isc_netaddr_fromin(na, &in4a);
return (ISC_R_SUCCESS);
}
}
if ((flags & CFG_ADDR_V4PREFIXOK) != 0 &&
strlen(s) <= 15U) {
char buf[64];
int i;
strcpy(buf, s);
for (i = 0; i < 3; i++) {
strcat(buf, ".0");
if (inet_pton(AF_INET, buf, &in4a) == 1) {
isc_netaddr_fromin(na, &in4a);
return (ISC_R_SUCCESS);
}
}
}
if ((flags & CFG_ADDR_V6OK) != 0 &&
strlen(s) <= 127U) {
char buf[128]; /* see lib/bind9/getaddresses.c */
char *d; /* zone delimiter */
isc_uint32_t zone = 0; /* scope zone ID */
strcpy(buf, s);
d = strchr(buf, '%');
if (d != NULL)
*d = '\0';
if (inet_pton(AF_INET6, buf, &in6a) == 1) {
if (d != NULL) {
#ifdef ISC_PLATFORM_HAVESCOPEID
isc_result_t result;
result = isc_netscope_pton(AF_INET6,
d + 1,
&in6a,
&zone);
if (result != ISC_R_SUCCESS)
return (result);
#else
return (ISC_R_BADADDRESSFORM);
#endif
}
isc_netaddr_fromin6(na, &in6a);
isc_netaddr_setzone(na, zone);
return (ISC_R_SUCCESS);
}
}
}
return (ISC_R_UNEXPECTEDTOKEN);
}
isc_result_t
cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
isc_result_t result;
const char *wild = "";
const char *prefix = "";
CHECK(cfg_gettoken(pctx, 0));
result = token_addr(pctx, flags, na);
if (result == ISC_R_UNEXPECTEDTOKEN) {
if ((flags & CFG_ADDR_WILDOK) != 0)
wild = " or '*'";
if ((flags & CFG_ADDR_V4PREFIXOK) != 0)
wild = " or IPv4 prefix";
if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V4OK)
cfg_parser_error(pctx, CFG_LOG_NEAR,
"expected IPv4 address%s%s",
prefix, wild);
else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK)
cfg_parser_error(pctx, CFG_LOG_NEAR,
"expected IPv6 address%s%s",
prefix, wild);
else
cfg_parser_error(pctx, CFG_LOG_NEAR,
"expected IP address%s%s",
prefix, wild);
}
cleanup:
return (result);
}
isc_boolean_t
cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
isc_result_t result;
isc_netaddr_t na_dummy;
result = token_addr(pctx, flags, &na_dummy);
return (ISC_TF(result == ISC_R_SUCCESS));
}
isc_result_t
cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
isc_result_t result;
CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
if ((flags & CFG_ADDR_WILDOK) != 0 &&
pctx->token.type == isc_tokentype_string &&
strcmp(TOKEN_STRING(pctx), "*") == 0) {
*port = 0;
return (ISC_R_SUCCESS);
}
if (pctx->token.type != isc_tokentype_number) {
cfg_parser_error(pctx, CFG_LOG_NEAR,
"expected port number or '*'");
return (ISC_R_UNEXPECTEDTOKEN);
}
if (pctx->token.value.as_ulong >= 65536U) {
cfg_parser_error(pctx, CFG_LOG_NEAR,
"port number out of range");
return (ISC_R_UNEXPECTEDTOKEN);
}
*port = (in_port_t)(pctx->token.value.as_ulong);
return (ISC_R_SUCCESS);
cleanup:
return (result);
}
void
cfg_print_rawaddr(cfg_printer_t *pctx, isc_netaddr_t *na) {
isc_result_t result;
char text[128];
isc_buffer_t buf;
isc_buffer_init(&buf, text, sizeof(text));
result = isc_netaddr_totext(na, &buf);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
cfg_print_chars(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf));
}
/* netaddr */
static unsigned int netaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
static unsigned int netaddr4_flags = CFG_ADDR_V4OK;
static unsigned int netaddr4wild_flags = CFG_ADDR_V4OK | CFG_ADDR_WILDOK;
static unsigned int netaddr6_flags = CFG_ADDR_V6OK;
static unsigned int netaddr6wild_flags = CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
static isc_result_t
parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
isc_result_t result;
cfg_obj_t *obj = NULL;
isc_netaddr_t netaddr;
unsigned int flags = *(const unsigned int *)type->of;
CHECK(cfg_create_obj(pctx, type, &obj));
CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
*ret = obj;
return (ISC_R_SUCCESS);
cleanup:
CLEANUP_OBJ(obj);
return (result);
}
static void
cfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
const unsigned int *flagp = type->of;
int n = 0;
if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
cfg_print_chars(pctx, "( ", 2);
if (*flagp & CFG_ADDR_V4OK) {
cfg_print_cstr(pctx, "<ipv4_address>");
n++;
}
if (*flagp & CFG_ADDR_V6OK) {
if (n != 0)
cfg_print_chars(pctx, " | ", 3);
cfg_print_cstr(pctx, "<ipv6_address>");
n++;
}
if (*flagp & CFG_ADDR_WILDOK) {
if (n != 0)
cfg_print_chars(pctx, " | ", 3);
cfg_print_chars(pctx, "*", 1);
n++;
}
if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
cfg_print_chars(pctx, " )", 2);
}
cfg_type_t cfg_type_netaddr = {
"netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
&cfg_rep_sockaddr, &netaddr_flags
};
cfg_type_t cfg_type_netaddr4 = {
"netaddr4", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
&cfg_rep_sockaddr, &netaddr4_flags
};
cfg_type_t cfg_type_netaddr4wild = {
"netaddr4wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
&cfg_rep_sockaddr, &netaddr4wild_flags
};
cfg_type_t cfg_type_netaddr6 = {
"netaddr6", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
&cfg_rep_sockaddr, &netaddr6_flags
};
cfg_type_t cfg_type_netaddr6wild = {
"netaddr6wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
&cfg_rep_sockaddr, &netaddr6wild_flags
};
/* netprefix */
isc_result_t
cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
cfg_obj_t **ret)
{
cfg_obj_t *obj = NULL;
isc_result_t result;
isc_netaddr_t netaddr;
unsigned int addrlen, prefixlen;
UNUSED(type);
CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
CFG_ADDR_V6OK, &netaddr));
switch (netaddr.family) {
case AF_INET:
addrlen = 32;
break;
case AF_INET6:
addrlen = 128;
break;
default:
addrlen = 0;
INSIST(0);
break;
}
CHECK(cfg_peektoken(pctx, 0));
if (pctx->token.type == isc_tokentype_special &&
pctx->token.value.as_char == '/') {
CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
if (pctx->token.type != isc_tokentype_number) {
cfg_parser_error(pctx, CFG_LOG_NEAR,
"expected prefix length");
return (ISC_R_UNEXPECTEDTOKEN);
}
prefixlen = pctx->token.value.as_ulong;
if (prefixlen > addrlen) {
cfg_parser_error(pctx, CFG_LOG_NOPREP,
"invalid prefix length");
return (ISC_R_RANGE);
}
} else {
prefixlen = addrlen;
}
CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
obj->value.netprefix.address = netaddr;
obj->value.netprefix.prefixlen = prefixlen;
*ret = obj;
return (ISC_R_SUCCESS);
cleanup:
cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
return (result);
}
static void
print_netprefix(cfg_printer_t *pctx, cfg_obj_t *obj) {
cfg_netprefix_t *p = &obj->value.netprefix;
cfg_print_rawaddr(pctx, &p->address);
cfg_print_chars(pctx, "/", 1);
cfg_print_rawuint(pctx, p->prefixlen);
}
isc_boolean_t
cfg_obj_isnetprefix(cfg_obj_t *obj) {
REQUIRE(obj != NULL);
return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
}
void
cfg_obj_asnetprefix(cfg_obj_t *obj, isc_netaddr_t *netaddr,
unsigned int *prefixlen) {
REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
*netaddr = obj->value.netprefix.address;
*prefixlen = obj->value.netprefix.prefixlen;
}
cfg_type_t cfg_type_netprefix = {
"netprefix", cfg_parse_netprefix, print_netprefix, cfg_doc_terminal,
&cfg_rep_netprefix, NULL
};
static isc_result_t
parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type,
int flags, cfg_obj_t **ret)
{
isc_result_t result;
isc_netaddr_t netaddr;
in_port_t port = 0;
cfg_obj_t *obj = NULL;
CHECK(cfg_create_obj(pctx, type, &obj));
CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
CHECK(cfg_peektoken(pctx, 0));
if (pctx->token.type == isc_tokentype_string &&
strcasecmp(TOKEN_STRING(pctx), "port") == 0) {
CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
CHECK(cfg_parse_rawport(pctx, flags, &port));
}
isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
*ret = obj;
return (ISC_R_SUCCESS);
cleanup:
CLEANUP_OBJ(obj);
return (result);
}
static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
cfg_type_t cfg_type_sockaddr = {
"sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr,
&cfg_rep_sockaddr, &sockaddr_flags
};
isc_result_t
cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
const unsigned int *flagp = type->of;
return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret));
}
void
cfg_print_sockaddr(cfg_printer_t *pctx, cfg_obj_t *obj) {
isc_netaddr_t netaddr;
in_port_t port;
char buf[ISC_NETADDR_FORMATSIZE];
isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
isc_netaddr_format(&netaddr, buf, sizeof(buf));
cfg_print_cstr(pctx, buf);
port = isc_sockaddr_getport(&obj->value.sockaddr);
if (port != 0) {
cfg_print_chars(pctx, " port ", 6);
cfg_print_rawuint(pctx, port);
}
}
void
cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
const unsigned int *flagp = type->of;
int n = 0;
cfg_print_chars(pctx, "( ", 2);
if (*flagp & CFG_ADDR_V4OK) {
cfg_print_cstr(pctx, "<ipv4_address>");
n++;
}
if (*flagp & CFG_ADDR_V6OK) {
if (n != 0)
cfg_print_chars(pctx, " | ", 3);
cfg_print_cstr(pctx, "<ipv6_address>");
n++;
}
if (*flagp & CFG_ADDR_WILDOK) {
if (n != 0)
cfg_print_chars(pctx, " | ", 3);
cfg_print_chars(pctx, "*", 1);
n++;
}
cfg_print_chars(pctx, " ) ", 3);
if (*flagp & CFG_ADDR_WILDOK) {
cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
} else {
cfg_print_cstr(pctx, "[ port <integer> ]");
}
}
isc_boolean_t
cfg_obj_issockaddr(cfg_obj_t *obj) {
REQUIRE(obj != NULL);
return (ISC_TF(obj->type->rep == &cfg_rep_sockaddr));
}
isc_sockaddr_t *
cfg_obj_assockaddr(cfg_obj_t *obj) {
REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
return (&obj->value.sockaddr);
}
isc_result_t
cfg_gettoken(cfg_parser_t *pctx, int options) {
isc_result_t result;
if (pctx->seen_eof)
return (ISC_R_SUCCESS);
options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
redo:
pctx->token.type = isc_tokentype_unknown;
result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
pctx->ungotten = ISC_FALSE;
pctx->line = isc_lex_getsourceline(pctx->lexer);
switch (result) {
case ISC_R_SUCCESS:
if (pctx->token.type == isc_tokentype_eof) {
result = isc_lex_close(pctx->lexer);
INSIST(result == ISC_R_NOMORE ||
result == ISC_R_SUCCESS);
if (isc_lex_getsourcename(pctx->lexer) != NULL) {
/*
* Closed an included file, not the main file.
*/
cfg_listelt_t *elt;
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);
goto redo;
}
pctx->seen_eof = ISC_TRUE;
}
break;
case ISC_R_NOSPACE:
/* More understandable than "ran out of space". */
cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
break;
case ISC_R_IOERROR:
cfg_parser_error(pctx, 0, "%s",
isc_result_totext(result));
break;
default:
cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
isc_result_totext(result));
break;
}
return (result);
}
void
cfg_ungettoken(cfg_parser_t *pctx) {
if (pctx->seen_eof)
return;
isc_lex_ungettoken(pctx->lexer, &pctx->token);
pctx->ungotten = ISC_TRUE;
}
isc_result_t
cfg_peektoken(cfg_parser_t *pctx, int options) {
isc_result_t result;
CHECK(cfg_gettoken(pctx, options));
cfg_ungettoken(pctx);
cleanup:
return (result);
}
/*
* Get a string token, accepting both the quoted and the unquoted form.
* Log an error if the next token is not a string.
*/
static isc_result_t
cfg_getstringtoken(cfg_parser_t *pctx) {
isc_result_t result;
result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
if (result != ISC_R_SUCCESS)
return (result);
if (pctx->token.type != isc_tokentype_string &&
pctx->token.type != isc_tokentype_qstring) {
cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
return (ISC_R_UNEXPECTEDTOKEN);
}
return (ISC_R_SUCCESS);
}
void
cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
parser_complain(pctx, ISC_FALSE, flags, fmt, args);
va_end(args);
pctx->errors++;
}
void
cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
parser_complain(pctx, ISC_TRUE, flags, fmt, args);
va_end(args);
pctx->warnings++;
}
#define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
static char *
current_file(cfg_parser_t *pctx) {
static char none[] = "none";
cfg_listelt_t *elt;
cfg_obj_t *fileobj;
if (pctx->open_files == NULL)
return (none);
elt = ISC_LIST_TAIL(pctx->open_files->value.list);
if (elt == NULL)
return (none);
fileobj = elt->obj;
INSIST(fileobj->type == &cfg_type_qstring);
return (fileobj->value.string.base);
}
static void
parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
unsigned int flags, const char *format,
va_list args)
{
char tokenbuf[MAX_LOG_TOKEN + 10];
static char where[ISC_DIR_PATHMAX + 100];
static char message[2048];
int level = ISC_LOG_ERROR;
const char *prep = "";
size_t len;
if (is_warning)
level = ISC_LOG_WARNING;
snprintf(where, sizeof(where), "%s:%u: ",
current_file(pctx), pctx->line);
len = vsnprintf(message, sizeof(message), format, args);
if (len >= sizeof(message))
FATAL_ERROR(__FILE__, __LINE__,
"error message would overflow");
if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) {
isc_region_t r;
if (pctx->ungotten)
(void)cfg_gettoken(pctx, 0);
if (pctx->token.type == isc_tokentype_eof) {
snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
} else if (pctx->token.type == isc_tokentype_unknown) {
flags = 0;
tokenbuf[0] = '\0';
} else {
isc_lex_getlasttokentext(pctx->lexer,
&pctx->token, &r);
if (r.length > MAX_LOG_TOKEN)
snprintf(tokenbuf, sizeof(tokenbuf),
"'%.*s...'", MAX_LOG_TOKEN, r.base);
else
snprintf(tokenbuf, sizeof(tokenbuf),
"'%.*s'", (int)r.length, r.base);
}
/* Choose a preposition. */
if (flags & CFG_LOG_NEAR)
prep = " near ";
else if (flags & CFG_LOG_BEFORE)
prep = " before ";
else
prep = " ";
} else {
tokenbuf[0] = '\0';
}
isc_log_write(pctx->lctx, CAT, MOD, level,
"%s%s%s%s", where, message, prep, tokenbuf);
}
void
cfg_obj_log(cfg_obj_t *obj, isc_log_t *lctx, int level, const char *fmt, ...) {
va_list ap;
char msgbuf[2048];
if (! isc_log_wouldlog(lctx, level))
return;
va_start(ap, fmt);
vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
isc_log_write(lctx, CAT, MOD, level,
"%s:%u: %s",
obj->file == NULL ? "<unknown file>" : obj->file,
obj->line, msgbuf);
va_end(ap);
}
const char *
cfg_obj_file(cfg_obj_t *obj) {
return (obj->file);
}
unsigned int
cfg_obj_line(cfg_obj_t *obj) {
return (obj->line);
}
isc_result_t
cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
cfg_obj_t *obj;
obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
if (obj == NULL)
return (ISC_R_NOMEMORY);
obj->type = type;
obj->file = current_file(pctx);
obj->line = pctx->line;
*ret = obj;
return (ISC_R_SUCCESS);
}
static void
map_symtabitem_destroy(char *key, unsigned int type,
isc_symvalue_t symval, void *userarg)
{
cfg_obj_t *obj = symval.as_pointer;
cfg_parser_t *pctx = (cfg_parser_t *)userarg;
UNUSED(key);
UNUSED(type);
cfg_obj_destroy(pctx, &obj);
}
static isc_result_t
create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
isc_result_t result;
isc_symtab_t *symtab = NULL;
cfg_obj_t *obj = NULL;
CHECK(cfg_create_obj(pctx, type, &obj));
CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */
map_symtabitem_destroy,
pctx, ISC_FALSE, &symtab));
obj->value.map.symtab = symtab;
obj->value.map.id = NULL;
*ret = obj;
return (ISC_R_SUCCESS);
cleanup:
if (obj != NULL)
isc_mem_put(pctx->mctx, obj, sizeof(*obj));
return (result);
}
static void
free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
CLEANUP_OBJ(obj->value.map.id);
isc_symtab_destroy(&obj->value.map.symtab);
}
isc_boolean_t
cfg_obj_istype(cfg_obj_t *obj, const cfg_type_t *type) {
return (ISC_TF(obj->type == type));
}
/*
* Destroy 'obj', a configuration object created in 'pctx'.
*/
void
cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
cfg_obj_t *obj = *objp;
obj->type->rep->free(pctx, obj);
isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
*objp = NULL;
}
static void
free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
UNUSED(pctx);
UNUSED(obj);
}
void
cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
type->doc(pctx, type);
}
void
cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) {
cfg_print_chars(pctx, "<", 1);
cfg_print_cstr(pctx, type->name);
cfg_print_chars(pctx, ">", 1);
}
void
cfg_print_grammar(const cfg_type_t *type,
void (*f)(void *closure, const char *text, int textlen),
void *closure)
{
cfg_printer_t pctx;
pctx.f = f;
pctx.closure = closure;
pctx.indent = 0;
cfg_doc_obj(&pctx, type);
}