📄 parser.c
字号:
/* * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000-2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. *//* $Id: parser.c,v 1.70.2.20.2.18 2004/05/15 03:46:13 jinmei Exp $ */#include <config.h>#include <isc/buffer.h>#include <isc/dir.h>#include <isc/formatcheck.h>#include <isc/lex.h>#include <isc/log.h>#include <isc/mem.h>#include <isc/net.h>#include <isc/netaddr.h>#include <isc/print.h>#include <isc/string.h>#include <isc/sockaddr.h>#include <isc/netscope.h>#include <isc/util.h>#include <isc/symtab.h>#include <isccfg/cfg.h>#include <isccfg/grammar.h>#include <isccfg/log.h>/* Shorthand */#define CAT CFG_LOGCATEGORY_CONFIG#define MOD CFG_LOGMODULE_PARSER#define MAP_SYM 1 /* Unique type for isc_symtab */#define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)/* Check a return value. */#define CHECK(op) \ do { result = (op); \ if (result != ISC_R_SUCCESS) goto cleanup; \ } while (0)/* Clean up a configuration object if non-NULL. */#define CLEANUP_OBJ(obj) \ do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)/* * Forward declarations of static functions. */static voidfree_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);static isc_result_tparse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);static voidprint_list(cfg_printer_t *pctx, cfg_obj_t *obj);static voidfree_list(cfg_parser_t *pctx, cfg_obj_t *obj);static isc_result_tcreate_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);static isc_result_tcreate_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type, cfg_obj_t **ret);static voidfree_string(cfg_parser_t *pctx, cfg_obj_t *obj);static isc_result_tcreate_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);static voidfree_map(cfg_parser_t *pctx, cfg_obj_t *obj);static isc_result_tparse_symtab_elt(cfg_parser_t *pctx, const char *name, cfg_type_t *elttype, isc_symtab_t *symtab, isc_boolean_t callback);static voidfree_noop(cfg_parser_t *pctx, cfg_obj_t *obj);static isc_result_tcfg_getstringtoken(cfg_parser_t *pctx);static voidparser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning, unsigned int flags, const char *format, va_list args);/* * Data representations. These correspond to members of the * "value" union in struct cfg_obj (except "void", which does * not need a union member). */cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };cfg_rep_t cfg_rep_string = { "string", free_string };cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };cfg_rep_t cfg_rep_map = { "map", free_map };cfg_rep_t cfg_rep_list = { "list", free_list };cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };cfg_rep_t cfg_rep_void = { "void", free_noop };/* * Configuration type definitions. *//* * An implicit list. These are formed by clauses that occur multiple times. */static cfg_type_t cfg_type_implicitlist = { "implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL };/* Functions. */voidcfg_print_obj(cfg_printer_t *pctx, cfg_obj_t *obj) { obj->type->print(pctx, obj);}voidcfg_print_chars(cfg_printer_t *pctx, const char *text, int len) { pctx->f(pctx->closure, text, len);}static voidprint_open(cfg_printer_t *pctx) { cfg_print_chars(pctx, "{\n", 2); pctx->indent++;}static voidprint_indent(cfg_printer_t *pctx) { int indent = pctx->indent; while (indent > 0) { cfg_print_chars(pctx, "\t", 1); indent--; }}static voidprint_close(cfg_printer_t *pctx) { pctx->indent--; print_indent(pctx); cfg_print_chars(pctx, "}", 1);}isc_result_tcfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; INSIST(ret != NULL && *ret == NULL); result = type->parse(pctx, type, ret); if (result != ISC_R_SUCCESS) return (result); INSIST(*ret != NULL); return (ISC_R_SUCCESS);}voidcfg_print(cfg_obj_t *obj, void (*f)(void *closure, const char *text, int textlen), void *closure){ cfg_printer_t pctx; pctx.f = f; pctx.closure = closure; pctx.indent = 0; obj->type->print(&pctx, obj);}/* Tuples. */ isc_result_tcfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { isc_result_t result; const cfg_tuplefielddef_t *fields = type->of; const cfg_tuplefielddef_t *f; cfg_obj_t *obj = NULL; unsigned int nfields = 0; int i; for (f = fields; f->name != NULL; f++) nfields++; CHECK(cfg_create_obj(pctx, type, &obj)); obj->value.tuple = isc_mem_get(pctx->mctx, nfields * sizeof(cfg_obj_t *)); if (obj->value.tuple == NULL) { result = ISC_R_NOMEMORY; goto cleanup; } for (f = fields, i = 0; f->name != NULL; f++, i++) obj->value.tuple[i] = NULL; *ret = obj; return (ISC_R_SUCCESS); cleanup: if (obj != NULL) isc_mem_put(pctx->mctx, obj, sizeof(*obj)); return (result);}isc_result_tcfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret){ isc_result_t result; const cfg_tuplefielddef_t *fields = type->of; const cfg_tuplefielddef_t *f; cfg_obj_t *obj = NULL; unsigned int i; CHECK(cfg_create_tuple(pctx, type, &obj)); for (f = fields, i = 0; f->name != NULL; f++, i++) CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i])); *ret = obj; return (ISC_R_SUCCESS); cleanup: CLEANUP_OBJ(obj); return (result);}voidcfg_print_tuple(cfg_printer_t *pctx, cfg_obj_t *obj) { unsigned int i; const cfg_tuplefielddef_t *fields = obj->type->of; const cfg_tuplefielddef_t *f; isc_boolean_t need_space = ISC_FALSE; for (f = fields, i = 0; f->name != NULL; f++, i++) { cfg_obj_t *fieldobj = obj->value.tuple[i]; if (need_space) cfg_print_chars(pctx, " ", 1); cfg_print_obj(pctx, fieldobj); need_space = ISC_TF(fieldobj->type->print != cfg_print_void); }}voidcfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) { const cfg_tuplefielddef_t *fields = type->of; const cfg_tuplefielddef_t *f; isc_boolean_t need_space = ISC_FALSE; for (f = fields; f->name != NULL; f++) { if (need_space) cfg_print_chars(pctx, " ", 1); cfg_doc_obj(pctx, f->type); need_space = ISC_TF(f->type->print != cfg_print_void); }}static voidfree_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) { unsigned int i; const cfg_tuplefielddef_t *fields = obj->type->of; const cfg_tuplefielddef_t *f; unsigned int nfields = 0; if (obj->value.tuple == NULL) return; for (f = fields, i = 0; f->name != NULL; f++, i++) { CLEANUP_OBJ(obj->value.tuple[i]); nfields++; } isc_mem_put(pctx->mctx, obj->value.tuple, nfields * sizeof(cfg_obj_t *));}isc_boolean_tcfg_obj_istuple(cfg_obj_t *obj) { REQUIRE(obj != NULL); return (ISC_TF(obj->type->rep == &cfg_rep_tuple));}cfg_obj_t *cfg_tuple_get(cfg_obj_t *tupleobj, const char* name) { unsigned int i; const cfg_tuplefielddef_t *fields; const cfg_tuplefielddef_t *f; REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple); fields = tupleobj->type->of; for (f = fields, i = 0; f->name != NULL; f++, i++) { if (strcmp(f->name, name) == 0) return (tupleobj->value.tuple[i]); } INSIST(0); return (NULL);}isc_result_tcfg_parse_special(cfg_parser_t *pctx, int special) { isc_result_t result; 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);}/* * Parse a required semicolon. If it is not there, log * an error and increment the error count but continue * parsing. Since the next token is pushed back, * care must be taken to make sure it is eventually * consumed or an infinite loop may result. */static isc_result_tparse_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);}/* * Parse EOF, logging and returning an error if not there. */static isc_result_tparse_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);}/* A list of files, used internally for pctx->files. */static cfg_type_t cfg_type_filelist = { "filelist", NULL, print_list, NULL, &cfg_rep_list, &cfg_type_qstring};isc_result_tcfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) { isc_result_t result; cfg_parser_t *pctx; isc_lexspecials_t specials; REQUIRE(mctx != NULL); REQUIRE(ret != NULL && *ret == NULL); pctx = isc_mem_get(mctx, sizeof(*pctx)); if (pctx == NULL) return (ISC_R_NOMEMORY); pctx->mctx = mctx; pctx->lctx = lctx; pctx->lexer = NULL; pctx->seen_eof = ISC_FALSE; pctx->ungotten = ISC_FALSE; pctx->errors = 0; pctx->warnings = 0; pctx->open_files = NULL; pctx->closed_files = NULL; pctx->line = 0; pctx->callback = NULL; pctx->callbackarg = NULL; pctx->token.type = isc_tokentype_unknown; 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_tparser_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);}voidcfg_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_tparse2(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_tcfg_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_tcfg_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);}voidcfg_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_tcfg_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));}voidcfg_print_void(cfg_printer_t *pctx, cfg_obj_t *obj) { UNUSED(pctx); UNUSED(obj);}voidcfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) { UNUSED(pctx); UNUSED(type);}isc_boolean_tcfg_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 };/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -