#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <isc/lex.h>
#include <isc/log.h>
#include <isc/symtab.h>
#include <isc/util.h>
#include <isccfg/cfg.h>
#include <isccfg/grammar.h>
#define CFG_LOG_NEAR 0x00000001
#define CFG_LOG_BEFORE 0x00000002
#define CFG_LOG_NOPREP 0x00000004
isc_logcategory_t cfg_category = { "config", 0 };
isc_logmodule_t cfg_module = { "isccfg/parser", 0 };
#define CAT &cfg_category
#define MOD &cfg_module
#define MAP_SYM 1
#define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
#define CFG_LEXOPT_QSTRING (ISC_LEXOPT_QSTRING | ISC_LEXOPT_QSTRINGMULTILINE)
#define CHECK(op) \
do { result = (op); \
if (result != ISC_R_SUCCESS) goto cleanup; \
} while (0)
#define CLEANUP_OBJ(obj) \
do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
cfg_rep_t cfg_rep_string;
cfg_rep_t cfg_rep_list;
cfg_type_t cfg_type_qstring;
cfg_type_t cfg_type_sstring;
cfg_type_t cfg_type_token;
cfg_type_t cfg_type_unsupported;
static isc_result_t
cfg_gettoken(cfg_parser_t *pctx, int options);
static isc_result_t
cfg_peektoken(cfg_parser_t *pctx, int options);
static void
cfg_ungettoken(cfg_parser_t *pctx);
static isc_result_t
cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
static isc_result_t
cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
static isc_result_t
cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
static isc_result_t
cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
static isc_result_t
cfg_parse_special(cfg_parser_t *pctx, int special);
static isc_result_t
cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
static isc_result_t
cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
cfg_listelt_t **ret);
static isc_result_t
cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
static isc_result_t
cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
static void
cfg_parser_error(cfg_parser_t *pctx, unsigned int flags,
const char *fmt, ...) __attribute__((__format__(__printf__, 3, 4)));
static void
free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
static isc_result_t
create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
static isc_result_t
create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
cfg_obj_t **ret);
static void
free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
static isc_result_t
create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
static void
free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
static isc_result_t
parse_symtab_elt(cfg_parser_t *pctx, const char *name,
cfg_type_t *elttype, isc_symtab_t *symtab);
static isc_result_t
cfg_getstringtoken(cfg_parser_t *pctx);
static void
parser_complain(cfg_parser_t *pctx, int is_warning,
unsigned int flags, const char *format, va_list args);
cfg_rep_t cfg_rep_string = { "string", free_string };
cfg_rep_t cfg_rep_map = { "map", free_map };
cfg_rep_t cfg_rep_list = { "list", free_list };
static isc_result_t
cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
isc_result_t result;
REQUIRE(pctx != NULL);
REQUIRE(type != NULL);
REQUIRE(ret != NULL && *ret == NULL);
result = type->parse(pctx, type, ret);
if (result != ISC_R_SUCCESS)
return (result);
ENSURE(*ret != NULL);
return (ISC_R_SUCCESS);
}
static isc_result_t
cfg_parse_special(cfg_parser_t *pctx, int special) {
isc_result_t result;
REQUIRE(pctx != NULL);
CHECK(cfg_gettoken(pctx, 0));
if (pctx->token.type == isc_tokentype_special &&
pctx->token.value.as_char == special)
return (ISC_R_SUCCESS);
cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
return (ISC_R_UNEXPECTEDTOKEN);
cleanup:
return (result);
}
static isc_result_t
parse_semicolon(cfg_parser_t *pctx) {
isc_result_t result;
CHECK(cfg_gettoken(pctx, 0));
if (pctx->token.type == isc_tokentype_special &&
pctx->token.value.as_char == ';')
return (ISC_R_SUCCESS);
cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
cfg_ungettoken(pctx);
cleanup:
return (result);
}
static isc_result_t
parse_eof(cfg_parser_t *pctx) {
isc_result_t result;
CHECK(cfg_gettoken(pctx, 0));
if (pctx->token.type == isc_tokentype_eof)
return (ISC_R_SUCCESS);
cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
return (ISC_R_UNEXPECTEDTOKEN);
cleanup:
return (result);
}
static cfg_type_t cfg_type_filelist = {
"filelist", NULL, &cfg_rep_list,
&cfg_type_qstring
};
isc_result_t
cfg_parser_create(isc_log_t *lctx, cfg_parser_t **ret) {
isc_result_t result;
cfg_parser_t *pctx;
isc_lexspecials_t specials;
REQUIRE(ret != NULL && *ret == NULL);
pctx = malloc(sizeof(*pctx));
if (pctx == NULL)
return (ISC_R_NOMEMORY);
pctx->lctx = lctx;
pctx->lexer = NULL;
pctx->seen_eof = 0;
pctx->ungotten = 0;
pctx->errors = 0;
pctx->open_files = NULL;
pctx->closed_files = NULL;
pctx->line = 0;
pctx->token.type = isc_tokentype_unknown;
pctx->flags = 0;
memset(specials, 0, sizeof(specials));
specials['{'] = 1;
specials['}'] = 1;
specials[';'] = 1;
specials['/'] = 1;
specials['"'] = 1;
specials['!'] = 1;
CHECK(isc_lex_create(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);
free(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);
}
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) {
if (result == ISC_R_SUCCESS)
result = ISC_R_FAILURE;
goto cleanup;
}
if (result != ISC_R_SUCCESS) {
cfg_parser_error(pctx, 0, "parsing failed: %s",
isc_result_totext(result));
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(pctx != NULL);
REQUIRE(filename != NULL);
REQUIRE(type != NULL);
REQUIRE(ret != NULL && *ret == NULL);
CHECK(parser_openfile(pctx, filename));
CHECK(parse2(pctx, type, ret));
cleanup:
return (result);
}
void
cfg_parser_destroy(cfg_parser_t **pctxp) {
cfg_parser_t *pctx;
REQUIRE(pctxp != NULL && *pctxp != NULL);
pctx = *pctxp;
*pctxp = NULL;
isc_lex_destroy(&pctx->lexer);
CLEANUP_OBJ(pctx->open_files);
CLEANUP_OBJ(pctx->closed_files);
free(pctx);
}
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 = malloc(len + 1);
if (obj->value.string.base == NULL) {
free(obj);
return (ISC_R_NOMEMORY);
}
memmove(obj->value.string.base, contents, len);
obj->value.string.base[len] = '\0';
*ret = obj;
cleanup:
return (result);
}
static isc_result_t
cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
isc_result_t result;
REQUIRE(pctx != NULL);
REQUIRE(ret != NULL && *ret == NULL);
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
cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
cfg_obj_t **ret)
{
isc_result_t result;
REQUIRE(pctx != NULL);
REQUIRE(ret != NULL && *ret == NULL);
UNUSED(type);
CHECK(cfg_getstringtoken(pctx));
return (create_string(pctx,
TOKEN_STRING(pctx),
&cfg_type_qstring,
ret));
cleanup:
return (result);
}
static isc_result_t
cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type,
cfg_obj_t **ret)
{
isc_result_t result;
REQUIRE(pctx != NULL);
REQUIRE(ret != NULL && *ret == NULL);
UNUSED(type);
CHECK(cfg_getstringtoken(pctx));
return (create_string(pctx,
TOKEN_STRING(pctx),
&cfg_type_sstring,
ret));
cleanup:
return (result);
}
static void
free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
UNUSED(pctx);
free(obj->value.string.base);
}
const char *
cfg_obj_asstring(const cfg_obj_t *obj) {
REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
return (obj->value.string.base);
}
cfg_type_t cfg_type_qstring = {
"quoted_string", cfg_parse_qstring, &cfg_rep_string, NULL
};
cfg_type_t cfg_type_astring = {
"string", cfg_parse_astring, &cfg_rep_string, NULL
};
cfg_type_t cfg_type_sstring = {
"string", cfg_parse_sstring, &cfg_rep_string, NULL
};
static isc_result_t
cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
isc_result_t result;
REQUIRE(pctx != NULL);
REQUIRE(type != NULL);
REQUIRE(obj != NULL && *obj == NULL);
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) {
UNUSED(pctx);
cfg_listelt_t *elt;
elt = malloc(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);
free(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);
}
}
static 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;
REQUIRE(pctx != NULL);
REQUIRE(elttype != NULL);
REQUIRE(ret != NULL && *ret == 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:
free(elt);
return (result);
}
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;
REQUIRE(pctx != NULL);
REQUIRE(type != NULL);
REQUIRE(ret != NULL && *ret == NULL);
CHECK(create_map(pctx, type, &obj));
obj->value.map.clausesets = clausesets;
for (;;) {
redo:
CHECK(cfg_gettoken(pctx, 0));
if (pctx->token.type != isc_tokentype_string) {
cfg_ungettoken(pctx);
break;
}
if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
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");
CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported,
&eltobj));
cfg_obj_destroy(pctx, &eltobj);
CHECK(parse_semicolon(pctx));
continue;
}
result = isc_symtab_lookup(obj->value.map.symtab,
clause->name, 0, &symval);
if (result == ISC_R_NOTFOUND) {
CHECK(parse_symtab_elt(pctx, clause->name,
clause->type,
obj->value.map.symtab));
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_result_t result;
cfg_obj_t *obj = NULL;
isc_symvalue_t symval;
CHECK(cfg_parse_obj(pctx, elttype, &obj));
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);
}
static isc_result_t
cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
isc_result_t result;
REQUIRE(pctx != NULL);
REQUIRE(type != NULL);
REQUIRE(ret != NULL && *ret == NULL);
CHECK(cfg_parse_special(pctx, '{'));
CHECK(cfg_parse_mapbody(pctx, type, ret));
CHECK(cfg_parse_special(pctx, '}'));
cleanup:
return (result);
}
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;
REQUIRE(pctx != NULL);
REQUIRE(nametype != NULL);
REQUIRE(type != NULL);
REQUIRE(ret != NULL && *ret == NULL);
CHECK(cfg_parse_obj(pctx, nametype, &idobj));
CHECK(cfg_parse_map(pctx, type, &mapobj));
mapobj->value.map.id = idobj;
*ret = mapobj;
return (result);
cleanup:
CLEANUP_OBJ(idobj);
CLEANUP_OBJ(mapobj);
return (result);
}
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));
}
isc_result_t
cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
isc_result_t result;
isc_symvalue_t val;
const 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);
}
const cfg_obj_t *
cfg_map_getname(const cfg_obj_t *mapobj) {
REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
return (mapobj->value.map.id);
}
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 = malloc(r.length + 1);
if (obj->value.string.base == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
obj->value.string.length = r.length;
memmove(obj->value.string.base, r.base, r.length);
obj->value.string.base[r.length] = '\0';
*ret = obj;
return (result);
cleanup:
if (obj != NULL)
free(obj);
return (result);
}
cfg_type_t cfg_type_token = {
"token", parse_token, &cfg_rep_string, NULL
};
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_rep_list, NULL
};
static isc_result_t
cfg_gettoken(cfg_parser_t *pctx, int options) {
isc_result_t result;
REQUIRE(pctx != NULL);
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 = 0;
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) {
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 = 1;
}
break;
case ISC_R_NOSPACE:
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);
}
static void
cfg_ungettoken(cfg_parser_t *pctx) {
REQUIRE(pctx != NULL);
if (pctx->seen_eof)
return;
isc_lex_ungettoken(pctx->lexer, &pctx->token);
pctx->ungotten = 1;
}
static isc_result_t
cfg_peektoken(cfg_parser_t *pctx, int options) {
isc_result_t result;
REQUIRE(pctx != NULL);
CHECK(cfg_gettoken(pctx, options));
cfg_ungettoken(pctx);
cleanup:
return (result);
}
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);
}
static void
cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
va_list args;
REQUIRE(pctx != NULL);
REQUIRE(fmt != NULL);
va_start(args, fmt);
parser_complain(pctx, 0, flags, fmt, args);
va_end(args);
pctx->errors++;
}
#define MAX_LOG_TOKEN 30
static int
have_current_file(cfg_parser_t *pctx) {
cfg_listelt_t *elt;
if (pctx->open_files == NULL)
return (0);
elt = ISC_LIST_TAIL(pctx->open_files->value.list);
if (elt == NULL)
return (0);
return (1);
}
static char *
current_file(cfg_parser_t *pctx) {
static char none[] = "none";
cfg_listelt_t *elt;
cfg_obj_t *fileobj;
if (!have_current_file(pctx))
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, int is_warning,
unsigned int flags, const char *format,
va_list args)
{
char tokenbuf[MAX_LOG_TOKEN + 10];
static char where[PATH_MAX + 100];
static char message[2048];
int level = ISC_LOG_ERROR;
const char *prep = "";
size_t len;
if (is_warning)
level = ISC_LOG_WARNING;
where[0] = '\0';
if (have_current_file(pctx))
snprintf(where, sizeof(where), "%s:%u: ",
current_file(pctx), pctx->line);
len = vsnprintf(message, sizeof(message), format, args);
#define ELIPSIS " ... "
if (len >= sizeof(message)) {
message[sizeof(message) - sizeof(ELIPSIS)] = 0;
strlcat(message, ELIPSIS, sizeof(message));
}
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);
}
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);
}
static isc_result_t
cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
cfg_obj_t *obj;
REQUIRE(pctx != NULL);
REQUIRE(type != NULL);
REQUIRE(ret != NULL && *ret == NULL);
obj = malloc(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(5,
map_symtabitem_destroy,
pctx, 0, &symtab));
obj->value.map.symtab = symtab;
obj->value.map.id = NULL;
*ret = obj;
return (ISC_R_SUCCESS);
cleanup:
if (obj != NULL)
free(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);
}
void
cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
cfg_obj_t *obj;
REQUIRE(objp != NULL && *objp != NULL);
REQUIRE(pctx != NULL);
obj = *objp;
obj->type->rep->free(pctx, obj);
free(obj);
*objp = NULL;
}