parser.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2004-2015  Internet Systems Consortium, Inc. ("ISC")
00003  * Copyright (C) 2000-2003  Internet Software Consortium.
00004  *
00005  * Permission to use, copy, modify, and/or distribute this software for any
00006  * purpose with or without fee is hereby granted, provided that the above
00007  * copyright notice and this permission notice appear in all copies.
00008  *
00009  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00010  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00011  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00012  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00013  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00014  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00015  * PERFORMANCE OF THIS SOFTWARE.
00016  */
00017 
00018 /* $Id$ */
00019 
00020 /*! \file */
00021 
00022 #include <config.h>
00023 
00024 #include <isc/buffer.h>
00025 #include <isc/dir.h>
00026 #include <isc/formatcheck.h>
00027 #include <isc/lex.h>
00028 #include <isc/log.h>
00029 #include <isc/mem.h>
00030 #include <isc/net.h>
00031 #include <isc/netaddr.h>
00032 #include <isc/netscope.h>
00033 #include <isc/print.h>
00034 #include <isc/string.h>
00035 #include <isc/sockaddr.h>
00036 #include <isc/symtab.h>
00037 #include <isc/util.h>
00038 
00039 #include <isccfg/cfg.h>
00040 #include <isccfg/grammar.h>
00041 #include <isccfg/log.h>
00042 
00043 /* Shorthand */
00044 #define CAT CFG_LOGCATEGORY_CONFIG
00045 #define MOD CFG_LOGMODULE_PARSER
00046 
00047 #define MAP_SYM 1       /* Unique type for isc_symtab */
00048 
00049 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
00050 
00051 /* Check a return value. */
00052 #define CHECK(op)                                               \
00053         do { result = (op);                                     \
00054                 if (result != ISC_R_SUCCESS) goto cleanup;      \
00055         } while (0)
00056 
00057 /* Clean up a configuration object if non-NULL. */
00058 #define CLEANUP_OBJ(obj) \
00059         do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
00060 
00061 
00062 /*
00063  * Forward declarations of static functions.
00064  */
00065 
00066 static void
00067 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
00068 
00069 static isc_result_t
00070 parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
00071 
00072 static void
00073 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
00074 
00075 static void
00076 free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
00077 
00078 static isc_result_t
00079 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
00080 
00081 static isc_result_t
00082 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
00083               cfg_obj_t **ret);
00084 
00085 static void
00086 free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
00087 
00088 static isc_result_t
00089 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
00090 
00091 static void
00092 free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
00093 
00094 static isc_result_t
00095 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
00096                  cfg_type_t *elttype, isc_symtab_t *symtab,
00097                  isc_boolean_t callback);
00098 
00099 static void
00100 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
00101 
00102 static isc_result_t
00103 cfg_getstringtoken(cfg_parser_t *pctx);
00104 
00105 static void
00106 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
00107                 unsigned int flags, const char *format, va_list args);
00108 
00109 /*
00110  * Data representations.  These correspond to members of the
00111  * "value" union in struct cfg_obj (except "void", which does
00112  * not need a union member).
00113  */
00114 
00115 cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
00116 cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
00117 cfg_rep_t cfg_rep_string = { "string", free_string };
00118 cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
00119 cfg_rep_t cfg_rep_map = { "map", free_map };
00120 cfg_rep_t cfg_rep_list = { "list", free_list };
00121 cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
00122 cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
00123 cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
00124 cfg_rep_t cfg_rep_void = { "void", free_noop };
00125 
00126 /*
00127  * Configuration type definitions.
00128  */
00129 
00130 /*%
00131  * An implicit list.  These are formed by clauses that occur multiple times.
00132  */
00133 static cfg_type_t cfg_type_implicitlist = {
00134         "implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL };
00135 
00136 /* Functions. */
00137 
00138 void
00139 cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
00140         obj->type->print(pctx, obj);
00141 }
00142 
00143 void
00144 cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
00145         pctx->f(pctx->closure, text, len);
00146 }
00147 
00148 static void
00149 print_open(cfg_printer_t *pctx) {
00150         if ((pctx->flags & CFG_PRINTER_ONELINE) != 0)
00151                 cfg_print_cstr(pctx, "{ ");
00152         else {
00153                 cfg_print_cstr(pctx, "{\n");
00154                 pctx->indent++;
00155         }
00156 }
00157 
00158 static void
00159 print_indent(cfg_printer_t *pctx) {
00160         int indent = pctx->indent;
00161         if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) {
00162                 cfg_print_cstr(pctx, " ");
00163                 return;
00164         }
00165         while (indent > 0) {
00166                 cfg_print_cstr(pctx, "\t");
00167                 indent--;
00168         }
00169 }
00170 
00171 static void
00172 print_close(cfg_printer_t *pctx) {
00173         if ((pctx->flags & CFG_PRINTER_ONELINE) == 0) {
00174                 pctx->indent--;
00175                 print_indent(pctx);
00176         }
00177         cfg_print_cstr(pctx, "}");
00178 }
00179 
00180 isc_result_t
00181 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
00182         isc_result_t result;
00183         INSIST(ret != NULL && *ret == NULL);
00184         result = type->parse(pctx, type, ret);
00185         if (result != ISC_R_SUCCESS)
00186                 return (result);
00187         INSIST(*ret != NULL);
00188         return (ISC_R_SUCCESS);
00189 }
00190 
00191 void
00192 cfg_print(const cfg_obj_t *obj,
00193           void (*f)(void *closure, const char *text, int textlen),
00194           void *closure)
00195 {
00196         cfg_printx(obj, 0, f, closure);
00197 }
00198 
00199 void
00200 cfg_printx(const cfg_obj_t *obj, unsigned int flags,
00201              void (*f)(void *closure, const char *text, int textlen),
00202              void *closure)
00203 {
00204         cfg_printer_t pctx;
00205         pctx.f = f;
00206         pctx.closure = closure;
00207         pctx.indent = 0;
00208         pctx.flags = flags;
00209         obj->type->print(&pctx, obj);
00210 }
00211 
00212 /* Tuples. */
00213 
00214 isc_result_t
00215 cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
00216         isc_result_t result;
00217         const cfg_tuplefielddef_t *fields = type->of;
00218         const cfg_tuplefielddef_t *f;
00219         cfg_obj_t *obj = NULL;
00220         unsigned int nfields = 0;
00221         int i;
00222 
00223         for (f = fields; f->name != NULL; f++)
00224                 nfields++;
00225 
00226         CHECK(cfg_create_obj(pctx, type, &obj));
00227         obj->value.tuple = isc_mem_get(pctx->mctx,
00228                                        nfields * sizeof(cfg_obj_t *));
00229         if (obj->value.tuple == NULL) {
00230                 result = ISC_R_NOMEMORY;
00231                 goto cleanup;
00232         }
00233         for (f = fields, i = 0; f->name != NULL; f++, i++)
00234                 obj->value.tuple[i] = NULL;
00235         *ret = obj;
00236         return (ISC_R_SUCCESS);
00237 
00238  cleanup:
00239         if (obj != NULL)
00240                 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
00241         return (result);
00242 }
00243 
00244 isc_result_t
00245 cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
00246 {
00247         isc_result_t result;
00248         const cfg_tuplefielddef_t *fields = type->of;
00249         const cfg_tuplefielddef_t *f;
00250         cfg_obj_t *obj = NULL;
00251         unsigned int i;
00252 
00253         CHECK(cfg_create_tuple(pctx, type, &obj));
00254         for (f = fields, i = 0; f->name != NULL; f++, i++)
00255                 CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
00256 
00257         *ret = obj;
00258         return (ISC_R_SUCCESS);
00259 
00260  cleanup:
00261         CLEANUP_OBJ(obj);
00262         return (result);
00263 }
00264 
00265 void
00266 cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
00267         unsigned int i;
00268         const cfg_tuplefielddef_t *fields = obj->type->of;
00269         const cfg_tuplefielddef_t *f;
00270         isc_boolean_t need_space = ISC_FALSE;
00271 
00272         for (f = fields, i = 0; f->name != NULL; f++, i++) {
00273                 const cfg_obj_t *fieldobj = obj->value.tuple[i];
00274                 if (need_space)
00275                         cfg_print_cstr(pctx, " ");
00276                 cfg_print_obj(pctx, fieldobj);
00277                 need_space = ISC_TF(fieldobj->type->print != cfg_print_void);
00278         }
00279 }
00280 
00281 void
00282 cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
00283         const cfg_tuplefielddef_t *fields = type->of;
00284         const cfg_tuplefielddef_t *f;
00285         isc_boolean_t need_space = ISC_FALSE;
00286 
00287         for (f = fields; f->name != NULL; f++) {
00288                 if (need_space)
00289                         cfg_print_cstr(pctx, " ");
00290                 cfg_doc_obj(pctx, f->type);
00291                 need_space = ISC_TF(f->type->print != cfg_print_void);
00292         }
00293 }
00294 
00295 static void
00296 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
00297         unsigned int i;
00298         const cfg_tuplefielddef_t *fields = obj->type->of;
00299         const cfg_tuplefielddef_t *f;
00300         unsigned int nfields = 0;
00301 
00302         if (obj->value.tuple == NULL)
00303                 return;
00304 
00305         for (f = fields, i = 0; f->name != NULL; f++, i++) {
00306                 CLEANUP_OBJ(obj->value.tuple[i]);
00307                 nfields++;
00308         }
00309         isc_mem_put(pctx->mctx, obj->value.tuple,
00310                     nfields * sizeof(cfg_obj_t *));
00311 }
00312 
00313 isc_boolean_t
00314 cfg_obj_istuple(const cfg_obj_t *obj) {
00315         REQUIRE(obj != NULL);
00316         return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
00317 }
00318 
00319 const cfg_obj_t *
00320 cfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) {
00321         unsigned int i;
00322         const cfg_tuplefielddef_t *fields;
00323         const cfg_tuplefielddef_t *f;
00324 
00325         REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
00326 
00327         fields = tupleobj->type->of;
00328         for (f = fields, i = 0; f->name != NULL; f++, i++) {
00329                 if (strcmp(f->name, name) == 0)
00330                         return (tupleobj->value.tuple[i]);
00331         }
00332         INSIST(0);
00333         return (NULL);
00334 }
00335 
00336 isc_result_t
00337 cfg_parse_special(cfg_parser_t *pctx, int special) {
00338         isc_result_t result;
00339         CHECK(cfg_gettoken(pctx, 0));
00340         if (pctx->token.type == isc_tokentype_special &&
00341             pctx->token.value.as_char == special)
00342                 return (ISC_R_SUCCESS);
00343 
00344         cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
00345         return (ISC_R_UNEXPECTEDTOKEN);
00346  cleanup:
00347         return (result);
00348 }
00349 
00350 /*
00351  * Parse a required semicolon.  If it is not there, log
00352  * an error and increment the error count but continue
00353  * parsing.  Since the next token is pushed back,
00354  * care must be taken to make sure it is eventually
00355  * consumed or an infinite loop may result.
00356  */
00357 static isc_result_t
00358 parse_semicolon(cfg_parser_t *pctx) {
00359         isc_result_t result;
00360         CHECK(cfg_gettoken(pctx, 0));
00361         if (pctx->token.type == isc_tokentype_special &&
00362             pctx->token.value.as_char == ';')
00363                 return (ISC_R_SUCCESS);
00364 
00365         cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
00366         cfg_ungettoken(pctx);
00367  cleanup:
00368         return (result);
00369 }
00370 
00371 /*
00372  * Parse EOF, logging and returning an error if not there.
00373  */
00374 static isc_result_t
00375 parse_eof(cfg_parser_t *pctx) {
00376         isc_result_t result;
00377         CHECK(cfg_gettoken(pctx, 0));
00378 
00379         if (pctx->token.type == isc_tokentype_eof)
00380                 return (ISC_R_SUCCESS);
00381 
00382         cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
00383         return (ISC_R_UNEXPECTEDTOKEN);
00384  cleanup:
00385         return (result);
00386 }
00387 
00388 /* A list of files, used internally for pctx->files. */
00389 
00390 static cfg_type_t cfg_type_filelist = {
00391         "filelist", NULL, print_list, NULL, &cfg_rep_list,
00392         &cfg_type_qstring
00393 };
00394 
00395 isc_result_t
00396 cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
00397         isc_result_t result;
00398         cfg_parser_t *pctx;
00399         isc_lexspecials_t specials;
00400 
00401         REQUIRE(mctx != NULL);
00402         REQUIRE(ret != NULL && *ret == NULL);
00403 
00404         pctx = isc_mem_get(mctx, sizeof(*pctx));
00405         if (pctx == NULL)
00406                 return (ISC_R_NOMEMORY);
00407 
00408         pctx->mctx = NULL;
00409         isc_mem_attach(mctx, &pctx->mctx);
00410 
00411         result = isc_refcount_init(&pctx->references, 1);
00412         if (result != ISC_R_SUCCESS) {
00413                 isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
00414                 return (result);
00415         }
00416 
00417         pctx->lctx = lctx;
00418         pctx->lexer = NULL;
00419         pctx->seen_eof = ISC_FALSE;
00420         pctx->ungotten = ISC_FALSE;
00421         pctx->errors = 0;
00422         pctx->warnings = 0;
00423         pctx->open_files = NULL;
00424         pctx->closed_files = NULL;
00425         pctx->line = 0;
00426         pctx->callback = NULL;
00427         pctx->callbackarg = NULL;
00428         pctx->token.type = isc_tokentype_unknown;
00429         pctx->flags = 0;
00430 
00431         memset(specials, 0, sizeof(specials));
00432         specials['{'] = 1;
00433         specials['}'] = 1;
00434         specials[';'] = 1;
00435         specials['/'] = 1;
00436         specials['"'] = 1;
00437         specials['!'] = 1;
00438 
00439         CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer));
00440 
00441         isc_lex_setspecials(pctx->lexer, specials);
00442         isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C |
00443                                          ISC_LEXCOMMENT_CPLUSPLUS |
00444                                          ISC_LEXCOMMENT_SHELL));
00445 
00446         CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
00447         CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
00448 
00449         *ret = pctx;
00450         return (ISC_R_SUCCESS);
00451 
00452  cleanup:
00453         if (pctx->lexer != NULL)
00454                 isc_lex_destroy(&pctx->lexer);
00455         CLEANUP_OBJ(pctx->open_files);
00456         CLEANUP_OBJ(pctx->closed_files);
00457         isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
00458         return (result);
00459 }
00460 
00461 static isc_result_t
00462 parser_openfile(cfg_parser_t *pctx, const char *filename) {
00463         isc_result_t result;
00464         cfg_listelt_t *elt = NULL;
00465         cfg_obj_t *stringobj = NULL;
00466 
00467         result = isc_lex_openfile(pctx->lexer, filename);
00468         if (result != ISC_R_SUCCESS) {
00469                 cfg_parser_error(pctx, 0, "open: %s: %s",
00470                              filename, isc_result_totext(result));
00471                 goto cleanup;
00472         }
00473 
00474         CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
00475         CHECK(create_listelt(pctx, &elt));
00476         elt->obj = stringobj;
00477         ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
00478 
00479         return (ISC_R_SUCCESS);
00480  cleanup:
00481         CLEANUP_OBJ(stringobj);
00482         return (result);
00483 }
00484 
00485 void
00486 cfg_parser_setcallback(cfg_parser_t *pctx,
00487                        cfg_parsecallback_t callback,
00488                        void *arg)
00489 {
00490         pctx->callback = callback;
00491         pctx->callbackarg = arg;
00492 }
00493 
00494 void
00495 cfg_parser_reset(cfg_parser_t *pctx) {
00496         REQUIRE(pctx != NULL);
00497 
00498         if (pctx->lexer != NULL)
00499                 isc_lex_close(pctx->lexer);
00500 
00501         pctx->seen_eof = ISC_FALSE;
00502         pctx->ungotten = ISC_FALSE;
00503         pctx->errors = 0;
00504         pctx->warnings = 0;
00505         pctx->line = 0;
00506 }
00507 
00508 /*
00509  * Parse a configuration using a pctx where a lexer has already
00510  * been set up with a source.
00511  */
00512 static isc_result_t
00513 parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
00514         isc_result_t result;
00515         cfg_obj_t *obj = NULL;
00516 
00517         result = cfg_parse_obj(pctx, type, &obj);
00518 
00519         if (pctx->errors != 0) {
00520                 /* Errors have been logged. */
00521                 if (result == ISC_R_SUCCESS)
00522                         result = ISC_R_FAILURE;
00523                 goto cleanup;
00524         }
00525 
00526         if (result != ISC_R_SUCCESS) {
00527                 /* Parsing failed but no errors have been logged. */
00528                 cfg_parser_error(pctx, 0, "parsing failed");
00529                 goto cleanup;
00530         }
00531 
00532         CHECK(parse_eof(pctx));
00533 
00534         *ret = obj;
00535         return (ISC_R_SUCCESS);
00536 
00537  cleanup:
00538         CLEANUP_OBJ(obj);
00539         return (result);
00540 }
00541 
00542 isc_result_t
00543 cfg_parse_file(cfg_parser_t *pctx, const char *filename,
00544                const cfg_type_t *type, cfg_obj_t **ret)
00545 {
00546         isc_result_t result;
00547 
00548         REQUIRE(filename != NULL);
00549 
00550         CHECK(parser_openfile(pctx, filename));
00551         CHECK(parse2(pctx, type, ret));
00552  cleanup:
00553         return (result);
00554 }
00555 
00556 
00557 isc_result_t
00558 cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
00559         const cfg_type_t *type, cfg_obj_t **ret)
00560 {
00561         isc_result_t result;
00562 
00563         REQUIRE(buffer != NULL);
00564 
00565         CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
00566         CHECK(parse2(pctx, type, ret));
00567 
00568  cleanup:
00569         return (result);
00570 }
00571 
00572 void
00573 cfg_parser_attach(cfg_parser_t *src, cfg_parser_t **dest) {
00574         REQUIRE(src != NULL);
00575         REQUIRE(dest != NULL && *dest == NULL);
00576         isc_refcount_increment(&src->references, NULL);
00577         *dest = src;
00578 }
00579 
00580 void
00581 cfg_parser_destroy(cfg_parser_t **pctxp) {
00582         cfg_parser_t *pctx = *pctxp;
00583         unsigned int refs;
00584 
00585         isc_refcount_decrement(&pctx->references, &refs);
00586         if (refs == 0) {
00587                 isc_lex_destroy(&pctx->lexer);
00588                 /*
00589                  * Cleaning up open_files does not
00590                  * close the files; that was already done
00591                  * by closing the lexer.
00592                  */
00593                 CLEANUP_OBJ(pctx->open_files);
00594                 CLEANUP_OBJ(pctx->closed_files);
00595                 isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
00596         }
00597         *pctxp = NULL;
00598 }
00599 
00600 /*
00601  * void
00602  */
00603 isc_result_t
00604 cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
00605         UNUSED(type);
00606         return (cfg_create_obj(pctx, &cfg_type_void, ret));
00607 }
00608 
00609 void
00610 cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
00611         UNUSED(pctx);
00612         UNUSED(obj);
00613 }
00614 
00615 void
00616 cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
00617         UNUSED(pctx);
00618         UNUSED(type);
00619 }
00620 
00621 isc_boolean_t
00622 cfg_obj_isvoid(const cfg_obj_t *obj) {
00623         REQUIRE(obj != NULL);
00624         return (ISC_TF(obj->type->rep == &cfg_rep_void));
00625 }
00626 
00627 cfg_type_t cfg_type_void = {
00628         "void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
00629         NULL };
00630 
00631 
00632 /*
00633  * uint32
00634  */
00635 isc_result_t
00636 cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
00637         isc_result_t result;
00638         cfg_obj_t *obj = NULL;
00639         UNUSED(type);
00640 
00641         CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
00642         if (pctx->token.type != isc_tokentype_number) {
00643                 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
00644                 return (ISC_R_UNEXPECTEDTOKEN);
00645         }
00646 
00647         CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
00648 
00649         obj->value.uint32 = pctx->token.value.as_ulong;
00650         *ret = obj;
00651  cleanup:
00652         return (result);
00653 }
00654 
00655 void
00656 cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
00657         cfg_print_chars(pctx, s, strlen(s));
00658 }
00659 
00660 void
00661 cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
00662         char buf[32];
00663         snprintf(buf, sizeof(buf), "%u", u);
00664         cfg_print_cstr(pctx, buf);
00665 }
00666 
00667 void
00668 cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
00669         cfg_print_rawuint(pctx, obj->value.uint32);
00670 }
00671 
00672 isc_boolean_t
00673 cfg_obj_isuint32(const cfg_obj_t *obj) {
00674         REQUIRE(obj != NULL);
00675         return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
00676 }
00677 
00678 isc_uint32_t
00679 cfg_obj_asuint32(const cfg_obj_t *obj) {
00680         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
00681         return (obj->value.uint32);
00682 }
00683 
00684 cfg_type_t cfg_type_uint32 = {
00685         "integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
00686         &cfg_rep_uint32, NULL
00687 };
00688 
00689 
00690 /*
00691  * uint64
00692  */
00693 isc_boolean_t
00694 cfg_obj_isuint64(const cfg_obj_t *obj) {
00695         REQUIRE(obj != NULL);
00696         return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
00697 }
00698 
00699 isc_uint64_t
00700 cfg_obj_asuint64(const cfg_obj_t *obj) {
00701         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
00702         return (obj->value.uint64);
00703 }
00704 
00705 void
00706 cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
00707         char buf[32];
00708         snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
00709                  obj->value.uint64);
00710         cfg_print_cstr(pctx, buf);
00711 }
00712 
00713 cfg_type_t cfg_type_uint64 = {
00714         "64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
00715         &cfg_rep_uint64, NULL
00716 };
00717 
00718 /*
00719  * qstring (quoted string), ustring (unquoted string), astring
00720  * (any string)
00721  */
00722 
00723 /* Create a string object from a null-terminated C string. */
00724 static isc_result_t
00725 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
00726               cfg_obj_t **ret)
00727 {
00728         isc_result_t result;
00729         cfg_obj_t *obj = NULL;
00730         int len;
00731 
00732         CHECK(cfg_create_obj(pctx, type, &obj));
00733         len = strlen(contents);
00734         obj->value.string.length = len;
00735         obj->value.string.base = isc_mem_get(pctx->mctx, len + 1);
00736         if (obj->value.string.base == 0) {
00737                 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
00738                 return (ISC_R_NOMEMORY);
00739         }
00740         memmove(obj->value.string.base, contents, len);
00741         obj->value.string.base[len] = '\0';
00742 
00743         *ret = obj;
00744  cleanup:
00745         return (result);
00746 }
00747 
00748 isc_result_t
00749 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
00750         isc_result_t result;
00751         UNUSED(type);
00752 
00753         CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
00754         if (pctx->token.type != isc_tokentype_qstring) {
00755                 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
00756                 return (ISC_R_UNEXPECTEDTOKEN);
00757         }
00758         return (create_string(pctx,
00759                               TOKEN_STRING(pctx),
00760                               &cfg_type_qstring,
00761                               ret));
00762  cleanup:
00763         return (result);
00764 }
00765 
00766 static isc_result_t
00767 parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
00768         isc_result_t result;
00769         UNUSED(type);
00770 
00771         CHECK(cfg_gettoken(pctx, 0));
00772         if (pctx->token.type != isc_tokentype_string) {
00773                 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
00774                 return (ISC_R_UNEXPECTEDTOKEN);
00775         }
00776         return (create_string(pctx,
00777                               TOKEN_STRING(pctx),
00778                               &cfg_type_ustring,
00779                               ret));
00780  cleanup:
00781         return (result);
00782 }
00783 
00784 isc_result_t
00785 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
00786                   cfg_obj_t **ret)
00787 {
00788         isc_result_t result;
00789         UNUSED(type);
00790 
00791         CHECK(cfg_getstringtoken(pctx));
00792         return (create_string(pctx,
00793                               TOKEN_STRING(pctx),
00794                               &cfg_type_qstring,
00795                               ret));
00796  cleanup:
00797         return (result);
00798 }
00799 
00800 isc_result_t
00801 cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type,
00802                   cfg_obj_t **ret)
00803 {
00804         isc_result_t result;
00805         UNUSED(type);
00806 
00807         CHECK(cfg_getstringtoken(pctx));
00808         return (create_string(pctx,
00809                               TOKEN_STRING(pctx),
00810                               &cfg_type_sstring,
00811                               ret));
00812  cleanup:
00813         return (result);
00814 }
00815 
00816 isc_boolean_t
00817 cfg_is_enum(const char *s, const char *const *enums) {
00818         const char * const *p;
00819         for (p = enums; *p != NULL; p++) {
00820                 if (strcasecmp(*p, s) == 0)
00821                         return (ISC_TRUE);
00822         }
00823         return (ISC_FALSE);
00824 }
00825 
00826 static isc_result_t
00827 check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
00828         const char *s = obj->value.string.base;
00829         if (cfg_is_enum(s, enums))
00830                 return (ISC_R_SUCCESS);
00831         cfg_parser_error(pctx, 0, "'%s' unexpected", s);
00832         return (ISC_R_UNEXPECTEDTOKEN);
00833 }
00834 
00835 isc_result_t
00836 cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
00837         isc_result_t result;
00838         cfg_obj_t *obj = NULL;
00839         CHECK(parse_ustring(pctx, NULL, &obj));
00840         CHECK(check_enum(pctx, obj, type->of));
00841         *ret = obj;
00842         return (ISC_R_SUCCESS);
00843  cleanup:
00844         CLEANUP_OBJ(obj);
00845         return (result);
00846 }
00847 
00848 void
00849 cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
00850         const char * const *p;
00851         cfg_print_cstr(pctx, "( ");
00852         for (p = type->of; *p != NULL; p++) {
00853                 cfg_print_cstr(pctx, *p);
00854                 if (p[1] != NULL)
00855                         cfg_print_cstr(pctx, " | ");
00856         }
00857         cfg_print_cstr(pctx, " )");
00858 }
00859 
00860 void
00861 cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
00862         cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
00863 }
00864 
00865 static void
00866 print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
00867         cfg_print_cstr(pctx, "\"");
00868         cfg_print_ustring(pctx, obj);
00869         cfg_print_cstr(pctx, "\"");
00870 }
00871 
00872 static void
00873 print_sstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
00874         cfg_print_cstr(pctx, "\"");
00875         if ((pctx->flags & CFG_PRINTER_XKEY) != 0) {
00876                 unsigned int len = obj->value.string.length;
00877                 while (len-- > 0)
00878                         cfg_print_cstr(pctx, "?");
00879         } else
00880                 cfg_print_ustring(pctx, obj);
00881         cfg_print_cstr(pctx, "\"");
00882 }
00883 
00884 static void
00885 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
00886         isc_mem_put(pctx->mctx, obj->value.string.base,
00887                     obj->value.string.length + 1);
00888 }
00889 
00890 isc_boolean_t
00891 cfg_obj_isstring(const cfg_obj_t *obj) {
00892         REQUIRE(obj != NULL);
00893         return (ISC_TF(obj->type->rep == &cfg_rep_string));
00894 }
00895 
00896 const char *
00897 cfg_obj_asstring(const cfg_obj_t *obj) {
00898         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
00899         return (obj->value.string.base);
00900 }
00901 
00902 /* Quoted string only */
00903 cfg_type_t cfg_type_qstring = {
00904         "quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal,
00905         &cfg_rep_string, NULL
00906 };
00907 
00908 /* Unquoted string only */
00909 cfg_type_t cfg_type_ustring = {
00910         "string", parse_ustring, cfg_print_ustring, cfg_doc_terminal,
00911         &cfg_rep_string, NULL
00912 };
00913 
00914 /* Any string (quoted or unquoted); printed with quotes */
00915 cfg_type_t cfg_type_astring = {
00916         "string", cfg_parse_astring, print_qstring, cfg_doc_terminal,
00917         &cfg_rep_string, NULL
00918 };
00919 
00920 /*
00921  * Any string (quoted or unquoted); printed with quotes.
00922  * If CFG_PRINTER_XKEY is set when printing the string will be '?' out.
00923  */
00924 cfg_type_t cfg_type_sstring = {
00925         "string", cfg_parse_sstring, print_sstring, cfg_doc_terminal,
00926         &cfg_rep_string, NULL
00927 };
00928 
00929 /*
00930  * Booleans
00931  */
00932 
00933 isc_boolean_t
00934 cfg_obj_isboolean(const cfg_obj_t *obj) {
00935         REQUIRE(obj != NULL);
00936         return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
00937 }
00938 
00939 isc_boolean_t
00940 cfg_obj_asboolean(const cfg_obj_t *obj) {
00941         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
00942         return (obj->value.boolean);
00943 }
00944 
00945 isc_result_t
00946 cfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
00947 {
00948         isc_result_t result;
00949         isc_boolean_t value;
00950         cfg_obj_t *obj = NULL;
00951         UNUSED(type);
00952 
00953         result = cfg_gettoken(pctx, 0);
00954         if (result != ISC_R_SUCCESS)
00955                 return (result);
00956 
00957         if (pctx->token.type != isc_tokentype_string)
00958                 goto bad_boolean;
00959 
00960         if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
00961             (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
00962             (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
00963                 value = ISC_TRUE;
00964         } else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
00965                    (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
00966                    (strcmp(TOKEN_STRING(pctx), "0") == 0)) {
00967                 value = ISC_FALSE;
00968         } else {
00969                 goto bad_boolean;
00970         }
00971 
00972         CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
00973         obj->value.boolean = value;
00974         *ret = obj;
00975         return (result);
00976 
00977  bad_boolean:
00978         cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
00979         return (ISC_R_UNEXPECTEDTOKEN);
00980 
00981  cleanup:
00982         return (result);
00983 }
00984 
00985 void
00986 cfg_print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
00987         if (obj->value.boolean)
00988                 cfg_print_cstr(pctx, "yes");
00989         else
00990                 cfg_print_cstr(pctx, "no");
00991 }
00992 
00993 cfg_type_t cfg_type_boolean = {
00994         "boolean", cfg_parse_boolean, cfg_print_boolean, cfg_doc_terminal,
00995         &cfg_rep_boolean, NULL
00996 };
00997 
00998 /*
00999  * Lists.
01000  */
01001 
01002 isc_result_t
01003 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
01004         isc_result_t result;
01005         CHECK(cfg_create_obj(pctx, type, obj));
01006         ISC_LIST_INIT((*obj)->value.list);
01007  cleanup:
01008         return (result);
01009 }
01010 
01011 static isc_result_t
01012 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
01013         cfg_listelt_t *elt;
01014         elt = isc_mem_get(pctx->mctx, sizeof(*elt));
01015         if (elt == NULL)
01016                 return (ISC_R_NOMEMORY);
01017         elt->obj = NULL;
01018         ISC_LINK_INIT(elt, link);
01019         *eltp = elt;
01020         return (ISC_R_SUCCESS);
01021 }
01022 
01023 static void
01024 free_listelt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
01025         if (elt->obj != NULL)
01026                 cfg_obj_destroy(pctx, &elt->obj);
01027         isc_mem_put(pctx->mctx, elt, sizeof(*elt));
01028 }
01029 
01030 static void
01031 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
01032         cfg_listelt_t *elt, *next;
01033         for (elt = ISC_LIST_HEAD(obj->value.list);
01034              elt != NULL;
01035              elt = next)
01036         {
01037                 next = ISC_LIST_NEXT(elt, link);
01038                 free_listelt(pctx, elt);
01039         }
01040 }
01041 
01042 isc_result_t
01043 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
01044                   cfg_listelt_t **ret)
01045 {
01046         isc_result_t result;
01047         cfg_listelt_t *elt = NULL;
01048         cfg_obj_t *value = NULL;
01049 
01050         CHECK(create_listelt(pctx, &elt));
01051 
01052         result = cfg_parse_obj(pctx, elttype, &value);
01053         if (result != ISC_R_SUCCESS)
01054                 goto cleanup;
01055 
01056         elt->obj = value;
01057 
01058         *ret = elt;
01059         return (ISC_R_SUCCESS);
01060 
01061  cleanup:
01062         isc_mem_put(pctx->mctx, elt, sizeof(*elt));
01063         return (result);
01064 }
01065 
01066 /*
01067  * Parse a homogeneous list whose elements are of type 'elttype'
01068  * and where each element is terminated by a semicolon.
01069  */
01070 static isc_result_t
01071 parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
01072 {
01073         cfg_obj_t *listobj = NULL;
01074         const cfg_type_t *listof = listtype->of;
01075         isc_result_t result;
01076         cfg_listelt_t *elt = NULL;
01077 
01078         CHECK(cfg_create_list(pctx, listtype, &listobj));
01079 
01080         for (;;) {
01081                 CHECK(cfg_peektoken(pctx, 0));
01082                 if (pctx->token.type == isc_tokentype_special &&
01083                     pctx->token.value.as_char == /*{*/ '}')
01084                         break;
01085                 CHECK(cfg_parse_listelt(pctx, listof, &elt));
01086                 CHECK(parse_semicolon(pctx));
01087                 ISC_LIST_APPEND(listobj->value.list, elt, link);
01088                 elt = NULL;
01089         }
01090         *ret = listobj;
01091         return (ISC_R_SUCCESS);
01092 
01093  cleanup:
01094         if (elt != NULL)
01095                 free_listelt(pctx, elt);
01096         CLEANUP_OBJ(listobj);
01097         return (result);
01098 }
01099 
01100 static void
01101 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
01102         const cfg_list_t *list = &obj->value.list;
01103         const cfg_listelt_t *elt;
01104 
01105         for (elt = ISC_LIST_HEAD(*list);
01106              elt != NULL;
01107              elt = ISC_LIST_NEXT(elt, link))
01108         {
01109                 if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) {
01110                         cfg_print_obj(pctx, elt->obj);
01111                         cfg_print_cstr(pctx, "; ");
01112                 } else {
01113                         print_indent(pctx);
01114                         cfg_print_obj(pctx, elt->obj);
01115                         cfg_print_cstr(pctx, ";\n");
01116                 }
01117         }
01118 }
01119 
01120 isc_result_t
01121 cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
01122                      cfg_obj_t **ret)
01123 {
01124         isc_result_t result;
01125         CHECK(cfg_parse_special(pctx, '{'));
01126         CHECK(parse_list(pctx, type, ret));
01127         CHECK(cfg_parse_special(pctx, '}'));
01128  cleanup:
01129         return (result);
01130 }
01131 
01132 void
01133 cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
01134         print_open(pctx);
01135         print_list(pctx, obj);
01136         print_close(pctx);
01137 }
01138 
01139 void
01140 cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
01141         cfg_print_cstr(pctx, "{ ");
01142         cfg_doc_obj(pctx, type->of);
01143         cfg_print_cstr(pctx, "; ... }");
01144 }
01145 
01146 /*
01147  * Parse a homogeneous list whose elements are of type 'elttype'
01148  * and where elements are separated by space.  The list ends
01149  * before the first semicolon.
01150  */
01151 isc_result_t
01152 cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
01153                     cfg_obj_t **ret)
01154 {
01155         cfg_obj_t *listobj = NULL;
01156         const cfg_type_t *listof = listtype->of;
01157         isc_result_t result;
01158 
01159         CHECK(cfg_create_list(pctx, listtype, &listobj));
01160 
01161         for (;;) {
01162                 cfg_listelt_t *elt = NULL;
01163 
01164                 CHECK(cfg_peektoken(pctx, 0));
01165                 if (pctx->token.type == isc_tokentype_special &&
01166                     pctx->token.value.as_char == ';')
01167                         break;
01168                 CHECK(cfg_parse_listelt(pctx, listof, &elt));
01169                 ISC_LIST_APPEND(listobj->value.list, elt, link);
01170         }
01171         *ret = listobj;
01172         return (ISC_R_SUCCESS);
01173 
01174  cleanup:
01175         CLEANUP_OBJ(listobj);
01176         return (result);
01177 }
01178 
01179 void
01180 cfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) {
01181         const cfg_list_t *list = &obj->value.list;
01182         const cfg_listelt_t *elt;
01183 
01184         for (elt = ISC_LIST_HEAD(*list);
01185              elt != NULL;
01186              elt = ISC_LIST_NEXT(elt, link)) {
01187                 cfg_print_obj(pctx, elt->obj);
01188                 if (ISC_LIST_NEXT(elt, link) != NULL)
01189                         cfg_print_cstr(pctx, " ");
01190         }
01191 }
01192 
01193 isc_boolean_t
01194 cfg_obj_islist(const cfg_obj_t *obj) {
01195         REQUIRE(obj != NULL);
01196         return (ISC_TF(obj->type->rep == &cfg_rep_list));
01197 }
01198 
01199 const cfg_listelt_t *
01200 cfg_list_first(const cfg_obj_t *obj) {
01201         REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
01202         if (obj == NULL)
01203                 return (NULL);
01204         return (ISC_LIST_HEAD(obj->value.list));
01205 }
01206 
01207 const cfg_listelt_t *
01208 cfg_list_next(const cfg_listelt_t *elt) {
01209         REQUIRE(elt != NULL);
01210         return (ISC_LIST_NEXT(elt, link));
01211 }
01212 
01213 /*
01214  * Return the length of a list object.  If obj is NULL or is not
01215  * a list, return 0.
01216  */
01217 unsigned int
01218 cfg_list_length(const cfg_obj_t *obj, isc_boolean_t recurse) {
01219         const cfg_listelt_t *elt;
01220         unsigned int count = 0;
01221 
01222         if (obj == NULL || !cfg_obj_islist(obj))
01223                 return (0U);
01224         for (elt = cfg_list_first(obj);
01225              elt != NULL;
01226              elt = cfg_list_next(elt)) {
01227                 if (recurse && cfg_obj_islist(elt->obj)) {
01228                         count += cfg_list_length(elt->obj, recurse);
01229                 } else {
01230                         count++;
01231                 }
01232         }
01233         return (count);
01234 }
01235 
01236 cfg_obj_t *
01237 cfg_listelt_value(const cfg_listelt_t *elt) {
01238         REQUIRE(elt != NULL);
01239         return (elt->obj);
01240 }
01241 
01242 /*
01243  * Maps.
01244  */
01245 
01246 /*
01247  * Parse a map body.  That's something like
01248  *
01249  *   "foo 1; bar { glub; }; zap true; zap false;"
01250  *
01251  * i.e., a sequence of option names followed by values and
01252  * terminated by semicolons.  Used for the top level of
01253  * the named.conf syntax, as well as for the body of the
01254  * options, view, zone, and other statements.
01255  */
01256 isc_result_t
01257 cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
01258 {
01259         const cfg_clausedef_t * const *clausesets = type->of;
01260         isc_result_t result;
01261         const cfg_clausedef_t * const *clauseset;
01262         const cfg_clausedef_t *clause;
01263         cfg_obj_t *value = NULL;
01264         cfg_obj_t *obj = NULL;
01265         cfg_obj_t *eltobj = NULL;
01266         cfg_obj_t *includename = NULL;
01267         isc_symvalue_t symval;
01268         cfg_list_t *list = NULL;
01269 
01270         CHECK(create_map(pctx, type, &obj));
01271 
01272         obj->value.map.clausesets = clausesets;
01273 
01274         for (;;) {
01275                 cfg_listelt_t *elt;
01276 
01277         redo:
01278                 /*
01279                  * Parse the option name and see if it is known.
01280                  */
01281                 CHECK(cfg_gettoken(pctx, 0));
01282 
01283                 if (pctx->token.type != isc_tokentype_string) {
01284                         cfg_ungettoken(pctx);
01285                         break;
01286                 }
01287 
01288                 /*
01289                  * We accept "include" statements wherever a map body
01290                  * clause can occur.
01291                  */
01292                 if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
01293                         /*
01294                          * Turn the file name into a temporary configuration
01295                          * object just so that it is not overwritten by the
01296                          * semicolon token.
01297                          */
01298                         CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
01299                         CHECK(parse_semicolon(pctx));
01300                         CHECK(parser_openfile(pctx, includename->
01301                                               value.string.base));
01302                          cfg_obj_destroy(pctx, &includename);
01303                          goto redo;
01304                 }
01305 
01306                 clause = NULL;
01307                 for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
01308                         for (clause = *clauseset;
01309                              clause->name != NULL;
01310                              clause++) {
01311                                 if (strcasecmp(TOKEN_STRING(pctx),
01312                                            clause->name) == 0)
01313                                         goto done;
01314                         }
01315                 }
01316         done:
01317                 if (clause == NULL || clause->name == NULL) {
01318                         cfg_parser_error(pctx, CFG_LOG_NOPREP, "unknown option");
01319                         /*
01320                          * Try to recover by parsing this option as an unknown
01321                          * option and discarding it.
01322                          */
01323                         CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, &eltobj));
01324                         cfg_obj_destroy(pctx, &eltobj);
01325                         CHECK(parse_semicolon(pctx));
01326                         continue;
01327                 }
01328 
01329                 /* Clause is known. */
01330 
01331                 /* Issue warnings if appropriate */
01332                 if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0)
01333                         cfg_parser_warning(pctx, 0, "option '%s' is obsolete",
01334                                        clause->name);
01335                 if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0)
01336                         cfg_parser_warning(pctx, 0, "option '%s' is "
01337                                        "not implemented", clause->name);
01338                 if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0)
01339                         cfg_parser_warning(pctx, 0, "option '%s' is "
01340                                        "not implemented", clause->name);
01341 
01342                 if ((clause->flags & CFG_CLAUSEFLAG_NOTCONFIGURED) != 0) {
01343                         cfg_parser_warning(pctx, 0, "option '%s' was not "
01344                                            "enabled at compile time",
01345                                            clause->name);
01346                         result = ISC_R_FAILURE;
01347                         goto cleanup;
01348                 }
01349 
01350                 /*
01351                  * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
01352                  * set here - we need to log the *lack* of such an option,
01353                  * not its presence.
01354                  */
01355 
01356                 /* See if the clause already has a value; if not create one. */
01357                 result = isc_symtab_lookup(obj->value.map.symtab,
01358                                            clause->name, 0, &symval);
01359 
01360                 if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
01361                         /* Multivalued clause */
01362                         cfg_obj_t *listobj = NULL;
01363                         if (result == ISC_R_NOTFOUND) {
01364                                 CHECK(cfg_create_list(pctx,
01365                                                   &cfg_type_implicitlist,
01366                                                   &listobj));
01367                                 symval.as_pointer = listobj;
01368                                 result = isc_symtab_define(obj->value.
01369                                                    map.symtab,
01370                                                    clause->name,
01371                                                    1, symval,
01372                                                    isc_symexists_reject);
01373                                 if (result != ISC_R_SUCCESS) {
01374                                         cfg_parser_error(pctx, CFG_LOG_NEAR,
01375                                                      "isc_symtab_define(%s) "
01376                                                      "failed", clause->name);
01377                                         isc_mem_put(pctx->mctx, list,
01378                                                     sizeof(cfg_list_t));
01379                                         goto cleanup;
01380                                 }
01381                         } else {
01382                                 INSIST(result == ISC_R_SUCCESS);
01383                                 listobj = symval.as_pointer;
01384                         }
01385 
01386                         elt = NULL;
01387                         CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
01388                         CHECK(parse_semicolon(pctx));
01389 
01390                         ISC_LIST_APPEND(listobj->value.list, elt, link);
01391                 } else {
01392                         /* Single-valued clause */
01393                         if (result == ISC_R_NOTFOUND) {
01394                                 isc_boolean_t callback =
01395                                         ISC_TF((clause->flags &
01396                                                 CFG_CLAUSEFLAG_CALLBACK) != 0);
01397                                 CHECK(parse_symtab_elt(pctx, clause->name,
01398                                                        clause->type,
01399                                                        obj->value.map.symtab,
01400                                                        callback));
01401                                 CHECK(parse_semicolon(pctx));
01402                         } else if (result == ISC_R_SUCCESS) {
01403                                 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
01404                                              clause->name);
01405                                 result = ISC_R_EXISTS;
01406                                 goto cleanup;
01407                         } else {
01408                                 cfg_parser_error(pctx, CFG_LOG_NEAR,
01409                                              "isc_symtab_define() failed");
01410                                 goto cleanup;
01411                         }
01412                 }
01413         }
01414 
01415 
01416         *ret = obj;
01417         return (ISC_R_SUCCESS);
01418 
01419  cleanup:
01420         CLEANUP_OBJ(value);
01421         CLEANUP_OBJ(obj);
01422         CLEANUP_OBJ(eltobj);
01423         CLEANUP_OBJ(includename);
01424         return (result);
01425 }
01426 
01427 static isc_result_t
01428 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
01429                  cfg_type_t *elttype, isc_symtab_t *symtab,
01430                  isc_boolean_t callback)
01431 {
01432         isc_result_t result;
01433         cfg_obj_t *obj = NULL;
01434         isc_symvalue_t symval;
01435 
01436         CHECK(cfg_parse_obj(pctx, elttype, &obj));
01437 
01438         if (callback && pctx->callback != NULL)
01439                 CHECK(pctx->callback(name, obj, pctx->callbackarg));
01440 
01441         symval.as_pointer = obj;
01442         CHECK(isc_symtab_define(symtab, name,
01443                                 1, symval,
01444                                 isc_symexists_reject));
01445         return (ISC_R_SUCCESS);
01446 
01447  cleanup:
01448         CLEANUP_OBJ(obj);
01449         return (result);
01450 }
01451 
01452 /*
01453  * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
01454  */
01455 isc_result_t
01456 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
01457         isc_result_t result;
01458         CHECK(cfg_parse_special(pctx, '{'));
01459         CHECK(cfg_parse_mapbody(pctx, type, ret));
01460         CHECK(cfg_parse_special(pctx, '}'));
01461  cleanup:
01462         return (result);
01463 }
01464 
01465 /*
01466  * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
01467  */
01468 static isc_result_t
01469 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type,
01470                     cfg_obj_t **ret)
01471 {
01472         isc_result_t result;
01473         cfg_obj_t *idobj = NULL;
01474         cfg_obj_t *mapobj = NULL;
01475 
01476         CHECK(cfg_parse_obj(pctx, nametype, &idobj));
01477         CHECK(cfg_parse_map(pctx, type, &mapobj));
01478         mapobj->value.map.id = idobj;
01479         *ret = mapobj;
01480         return (result);
01481  cleanup:
01482         CLEANUP_OBJ(idobj);
01483         CLEANUP_OBJ(mapobj);
01484         return (result);
01485 }
01486 
01487 /*
01488  * Parse a map identified by a string name.  E.g., "name { foo 1; }".
01489  * Used for the "key" and "channel" statements.
01490  */
01491 isc_result_t
01492 cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
01493         return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
01494 }
01495 
01496 /*
01497  * Parse a map identified by a network address.
01498  * Used to be used for the "server" statement.
01499  */
01500 isc_result_t
01501 cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
01502         return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
01503 }
01504 
01505 /*
01506  * Parse a map identified by a network prefix.
01507  * Used for the "server" statement.
01508  */
01509 isc_result_t
01510 cfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
01511         return (parse_any_named_map(pctx, &cfg_type_netprefix, type, ret));
01512 }
01513 
01514 static void
01515 print_symval(cfg_printer_t *pctx, const char *name, cfg_obj_t *obj) {
01516         if ((pctx->flags & CFG_PRINTER_ONELINE) == 0)
01517                 print_indent(pctx);
01518 
01519         cfg_print_cstr(pctx, name);
01520         cfg_print_cstr(pctx, " ");
01521         cfg_print_obj(pctx, obj);
01522 
01523         if ((pctx->flags & CFG_PRINTER_ONELINE) == 0)
01524                 cfg_print_cstr(pctx, ";\n");
01525         else
01526                 cfg_print_cstr(pctx, "; ");
01527 }
01528 
01529 void
01530 cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) {
01531         isc_result_t result = ISC_R_SUCCESS;
01532         const cfg_clausedef_t * const *clauseset;
01533 
01534         for (clauseset = obj->value.map.clausesets;
01535              *clauseset != NULL;
01536              clauseset++)
01537         {
01538                 isc_symvalue_t symval;
01539                 const cfg_clausedef_t *clause;
01540 
01541                 for (clause = *clauseset;
01542                      clause->name != NULL;
01543                      clause++) {
01544                         result = isc_symtab_lookup(obj->value.map.symtab,
01545                                                    clause->name, 0, &symval);
01546                         if (result == ISC_R_SUCCESS) {
01547                                 cfg_obj_t *symobj = symval.as_pointer;
01548                                 if (symobj->type == &cfg_type_implicitlist) {
01549                                         /* Multivalued. */
01550                                         cfg_list_t *list = &symobj->value.list;
01551                                         cfg_listelt_t *elt;
01552                                         for (elt = ISC_LIST_HEAD(*list);
01553                                              elt != NULL;
01554                                              elt = ISC_LIST_NEXT(elt, link)) {
01555                                                 print_symval(pctx,
01556                                                              clause->name,
01557                                                              elt->obj);
01558                                         }
01559                                 } else {
01560                                         /* Single-valued. */
01561                                         print_symval(pctx, clause->name,
01562                                                      symobj);
01563                                 }
01564                         } else if (result == ISC_R_NOTFOUND) {
01565                                 ; /* do nothing */
01566                         } else {
01567                                 INSIST(0);
01568                         }
01569                 }
01570         }
01571 }
01572 
01573 void
01574 cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
01575         const cfg_clausedef_t * const *clauseset;
01576         const cfg_clausedef_t *clause;
01577 
01578         for (clauseset = type->of; *clauseset != NULL; clauseset++) {
01579                 for (clause = *clauseset;
01580                      clause->name != NULL;
01581                      clause++) {
01582                         cfg_print_cstr(pctx, clause->name);
01583                         cfg_print_cstr(pctx, " ");
01584                         cfg_doc_obj(pctx, clause->type);
01585                         /* XXX print flags here? */
01586                         cfg_print_cstr(pctx, ";\n\n");
01587                 }
01588         }
01589 }
01590 
01591 static struct flagtext {
01592         unsigned int flag;
01593         const char *text;
01594 } flagtexts[] = {
01595         { CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
01596         { CFG_CLAUSEFLAG_NYI, "not yet implemented" },
01597         { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
01598         { CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
01599         { CFG_CLAUSEFLAG_TESTONLY, "test only" },
01600         { CFG_CLAUSEFLAG_NOTCONFIGURED, "not configured" },
01601         { 0, NULL }
01602 };
01603 
01604 void
01605 cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) {
01606         if (obj->value.map.id != NULL) {
01607                 cfg_print_obj(pctx, obj->value.map.id);
01608                 cfg_print_cstr(pctx, " ");
01609         }
01610         print_open(pctx);
01611         cfg_print_mapbody(pctx, obj);
01612         print_close(pctx);
01613 }
01614 
01615 static void
01616 print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
01617         struct flagtext *p;
01618         isc_boolean_t first = ISC_TRUE;
01619         for (p = flagtexts; p->flag != 0; p++) {
01620                 if ((flags & p->flag) != 0) {
01621                         if (first)
01622                                 cfg_print_cstr(pctx, " // ");
01623                         else
01624                                 cfg_print_cstr(pctx, ", ");
01625                         cfg_print_cstr(pctx, p->text);
01626                         first = ISC_FALSE;
01627                 }
01628         }
01629 }
01630 
01631 void
01632 cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
01633         const cfg_clausedef_t * const *clauseset;
01634         const cfg_clausedef_t *clause;
01635 
01636         if (type->parse == cfg_parse_named_map) {
01637                 cfg_doc_obj(pctx, &cfg_type_astring);
01638                 cfg_print_cstr(pctx, " ");
01639         } else if (type->parse == cfg_parse_addressed_map) {
01640                 cfg_doc_obj(pctx, &cfg_type_netaddr);
01641                 cfg_print_cstr(pctx, " ");
01642         } else if (type->parse == cfg_parse_netprefix_map) {
01643                 cfg_doc_obj(pctx, &cfg_type_netprefix);
01644                 cfg_print_cstr(pctx, " ");
01645         }
01646 
01647         print_open(pctx);
01648 
01649         for (clauseset = type->of; *clauseset != NULL; clauseset++) {
01650                 for (clause = *clauseset;
01651                      clause->name != NULL;
01652                      clause++) {
01653                         print_indent(pctx);
01654                         cfg_print_cstr(pctx, clause->name);
01655                         if (clause->type->print != cfg_print_void)
01656                                 cfg_print_cstr(pctx, " ");
01657                         cfg_doc_obj(pctx, clause->type);
01658                         cfg_print_cstr(pctx, ";");
01659                         print_clause_flags(pctx, clause->flags);
01660                         cfg_print_cstr(pctx, "\n");
01661                 }
01662         }
01663         print_close(pctx);
01664 }
01665 
01666 isc_boolean_t
01667 cfg_obj_ismap(const cfg_obj_t *obj) {
01668         REQUIRE(obj != NULL);
01669         return (ISC_TF(obj->type->rep == &cfg_rep_map));
01670 }
01671 
01672 isc_result_t
01673 cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
01674         isc_result_t result;
01675         isc_symvalue_t val;
01676         const cfg_map_t *map;
01677 
01678         REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
01679         REQUIRE(name != NULL);
01680         REQUIRE(obj != NULL && *obj == NULL);
01681 
01682         map = &mapobj->value.map;
01683 
01684         result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
01685         if (result != ISC_R_SUCCESS)
01686                 return (result);
01687         *obj = val.as_pointer;
01688         return (ISC_R_SUCCESS);
01689 }
01690 
01691 const cfg_obj_t *
01692 cfg_map_getname(const cfg_obj_t *mapobj) {
01693         REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
01694         return (mapobj->value.map.id);
01695 }
01696 
01697 unsigned int
01698 cfg_map_count(const cfg_obj_t *mapobj) {
01699         const cfg_map_t *map;
01700         REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
01701         map = &mapobj->value.map;
01702         return (isc_symtab_count(map->symtab));
01703 }
01704 
01705 /* Parse an arbitrary token, storing its raw text representation. */
01706 static isc_result_t
01707 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
01708         cfg_obj_t *obj = NULL;
01709         isc_result_t result;
01710         isc_region_t r;
01711 
01712         UNUSED(type);
01713 
01714         CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
01715         CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
01716         if (pctx->token.type == isc_tokentype_eof) {
01717                 cfg_ungettoken(pctx);
01718                 result = ISC_R_EOF;
01719                 goto cleanup;
01720         }
01721 
01722         isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
01723 
01724         obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
01725         if (obj->value.string.base == NULL) {
01726                 result = ISC_R_NOMEMORY;
01727                 goto cleanup;
01728         }
01729         obj->value.string.length = r.length;
01730         memmove(obj->value.string.base, r.base, r.length);
01731         obj->value.string.base[r.length] = '\0';
01732         *ret = obj;
01733         return (result);
01734 
01735  cleanup:
01736         if (obj != NULL)
01737                 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
01738         return (result);
01739 }
01740 
01741 cfg_type_t cfg_type_token = {
01742         "token", parse_token, cfg_print_ustring, cfg_doc_terminal,
01743         &cfg_rep_string, NULL
01744 };
01745 
01746 /*
01747  * An unsupported option.  This is just a list of tokens with balanced braces
01748  * ending in a semicolon.
01749  */
01750 
01751 static isc_result_t
01752 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
01753         cfg_obj_t *listobj = NULL;
01754         isc_result_t result;
01755         int braces = 0;
01756 
01757         CHECK(cfg_create_list(pctx, type, &listobj));
01758 
01759         for (;;) {
01760                 cfg_listelt_t *elt = NULL;
01761 
01762                 CHECK(cfg_peektoken(pctx, 0));
01763                 if (pctx->token.type == isc_tokentype_special) {
01764                         if (pctx->token.value.as_char == '{')
01765                                 braces++;
01766                         else if (pctx->token.value.as_char == '}')
01767                                 braces--;
01768                         else if (pctx->token.value.as_char == ';')
01769                                 if (braces == 0)
01770                                         break;
01771                 }
01772                 if (pctx->token.type == isc_tokentype_eof || braces < 0) {
01773                         cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
01774                         result = ISC_R_UNEXPECTEDTOKEN;
01775                         goto cleanup;
01776                 }
01777 
01778                 CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
01779                 ISC_LIST_APPEND(listobj->value.list, elt, link);
01780         }
01781         INSIST(braces == 0);
01782         *ret = listobj;
01783         return (ISC_R_SUCCESS);
01784 
01785  cleanup:
01786         CLEANUP_OBJ(listobj);
01787         return (result);
01788 }
01789 
01790 cfg_type_t cfg_type_unsupported = {
01791         "unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal,
01792         &cfg_rep_list, NULL
01793 };
01794 
01795 /*
01796  * Try interpreting the current token as a network address.
01797  *
01798  * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
01799  * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set.  The
01800  * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
01801  * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
01802  * and the IPv6 wildcard address otherwise.
01803  */
01804 static isc_result_t
01805 token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
01806         char *s;
01807         struct in_addr in4a;
01808         struct in6_addr in6a;
01809 
01810         if (pctx->token.type != isc_tokentype_string)
01811                 return (ISC_R_UNEXPECTEDTOKEN);
01812 
01813         s = TOKEN_STRING(pctx);
01814         if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
01815                 if ((flags & CFG_ADDR_V4OK) != 0) {
01816                         isc_netaddr_any(na);
01817                         return (ISC_R_SUCCESS);
01818                 } else if ((flags & CFG_ADDR_V6OK) != 0) {
01819                         isc_netaddr_any6(na);
01820                         return (ISC_R_SUCCESS);
01821                 } else {
01822                         INSIST(0);
01823                 }
01824         } else {
01825                 if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
01826                         if (inet_pton(AF_INET, s, &in4a) == 1) {
01827                                 isc_netaddr_fromin(na, &in4a);
01828                                 return (ISC_R_SUCCESS);
01829                         }
01830                 }
01831                 if ((flags & CFG_ADDR_V4PREFIXOK) != 0 &&
01832                     strlen(s) <= 15U) {
01833                         char buf[64];
01834                         int i;
01835 
01836                         strcpy(buf, s);
01837                         for (i = 0; i < 3; i++) {
01838                                 strcat(buf, ".0");
01839                                 if (inet_pton(AF_INET, buf, &in4a) == 1) {
01840                                         isc_netaddr_fromin(na, &in4a);
01841                                         return (ISC_R_SUCCESS);
01842                                 }
01843                         }
01844                 }
01845                 if ((flags & CFG_ADDR_V6OK) != 0 &&
01846                     strlen(s) <= 127U) {
01847                         char buf[128]; /* see lib/bind9/getaddresses.c */
01848                         char *d; /* zone delimiter */
01849                         isc_uint32_t zone = 0; /* scope zone ID */
01850 
01851                         strcpy(buf, s);
01852                         d = strchr(buf, '%');
01853                         if (d != NULL)
01854                                 *d = '\0';
01855 
01856                         if (inet_pton(AF_INET6, buf, &in6a) == 1) {
01857                                 if (d != NULL) {
01858 #ifdef ISC_PLATFORM_HAVESCOPEID
01859                                         isc_result_t result;
01860 
01861                                         result = isc_netscope_pton(AF_INET6,
01862                                                                    d + 1,
01863                                                                    &in6a,
01864                                                                    &zone);
01865                                         if (result != ISC_R_SUCCESS)
01866                                                 return (result);
01867 #else
01868                                 return (ISC_R_BADADDRESSFORM);
01869 #endif
01870                                 }
01871 
01872                                 isc_netaddr_fromin6(na, &in6a);
01873                                 isc_netaddr_setzone(na, zone);
01874                                 return (ISC_R_SUCCESS);
01875                         }
01876                 }
01877         }
01878         return (ISC_R_UNEXPECTEDTOKEN);
01879 }
01880 
01881 isc_result_t
01882 cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
01883         isc_result_t result;
01884         const char *wild = "";
01885         const char *prefix = "";
01886 
01887         CHECK(cfg_gettoken(pctx, 0));
01888         result = token_addr(pctx, flags, na);
01889         if (result == ISC_R_UNEXPECTEDTOKEN) {
01890                 if ((flags & CFG_ADDR_WILDOK) != 0)
01891                         wild = " or '*'";
01892                 if ((flags & CFG_ADDR_V4PREFIXOK) != 0)
01893                         wild = " or IPv4 prefix";
01894                 if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V4OK)
01895                         cfg_parser_error(pctx, CFG_LOG_NEAR,
01896                                          "expected IPv4 address%s%s",
01897                                          prefix, wild);
01898                 else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK)
01899                         cfg_parser_error(pctx, CFG_LOG_NEAR,
01900                                          "expected IPv6 address%s%s",
01901                                          prefix, wild);
01902                 else
01903                         cfg_parser_error(pctx, CFG_LOG_NEAR,
01904                                          "expected IP address%s%s",
01905                                          prefix, wild);
01906         }
01907  cleanup:
01908         return (result);
01909 }
01910 
01911 isc_boolean_t
01912 cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
01913         isc_result_t result;
01914         isc_netaddr_t na_dummy;
01915         result = token_addr(pctx, flags, &na_dummy);
01916         return (ISC_TF(result == ISC_R_SUCCESS));
01917 }
01918 
01919 isc_result_t
01920 cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
01921         isc_result_t result;
01922 
01923         CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
01924 
01925         if ((flags & CFG_ADDR_WILDOK) != 0 &&
01926             pctx->token.type == isc_tokentype_string &&
01927             strcmp(TOKEN_STRING(pctx), "*") == 0) {
01928                 *port = 0;
01929                 return (ISC_R_SUCCESS);
01930         }
01931         if (pctx->token.type != isc_tokentype_number) {
01932                 cfg_parser_error(pctx, CFG_LOG_NEAR,
01933                              "expected port number or '*'");
01934                 return (ISC_R_UNEXPECTEDTOKEN);
01935         }
01936         if (pctx->token.value.as_ulong >= 65536U) {
01937                 cfg_parser_error(pctx, CFG_LOG_NEAR,
01938                              "port number out of range");
01939                 return (ISC_R_UNEXPECTEDTOKEN);
01940         }
01941         *port = (in_port_t)(pctx->token.value.as_ulong);
01942         return (ISC_R_SUCCESS);
01943  cleanup:
01944         return (result);
01945 }
01946 
01947 void
01948 cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
01949         isc_result_t result;
01950         char text[128];
01951         isc_buffer_t buf;
01952 
01953         isc_buffer_init(&buf, text, sizeof(text));
01954         result = isc_netaddr_totext(na, &buf);
01955         RUNTIME_CHECK(result == ISC_R_SUCCESS);
01956         cfg_print_chars(pctx, isc_buffer_base(&buf),
01957                         isc_buffer_usedlength(&buf));
01958 }
01959 
01960 isc_result_t
01961 cfg_parse_dscp(cfg_parser_t *pctx, isc_dscp_t *dscp) {
01962         isc_result_t result;
01963 
01964         CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
01965 
01966         if (pctx->token.type != isc_tokentype_number) {
01967                 cfg_parser_error(pctx, CFG_LOG_NEAR,
01968                              "expected number");
01969                 return (ISC_R_UNEXPECTEDTOKEN);
01970         }
01971         if (pctx->token.value.as_ulong > 63U) {
01972                 cfg_parser_error(pctx, CFG_LOG_NEAR,
01973                              "dscp out of range");
01974                 return (ISC_R_RANGE);
01975         }
01976         *dscp = (isc_dscp_t)(pctx->token.value.as_ulong);
01977         return (ISC_R_SUCCESS);
01978  cleanup:
01979         return (result);
01980 }
01981 
01982 /* netaddr */
01983 
01984 static unsigned int netaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
01985 static unsigned int netaddr4_flags = CFG_ADDR_V4OK;
01986 static unsigned int netaddr4wild_flags = CFG_ADDR_V4OK | CFG_ADDR_WILDOK;
01987 static unsigned int netaddr6_flags = CFG_ADDR_V6OK;
01988 static unsigned int netaddr6wild_flags = CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
01989 
01990 static isc_result_t
01991 parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
01992         isc_result_t result;
01993         cfg_obj_t *obj = NULL;
01994         isc_netaddr_t netaddr;
01995         unsigned int flags = *(const unsigned int *)type->of;
01996 
01997         CHECK(cfg_create_obj(pctx, type, &obj));
01998         CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
01999         isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
02000         *ret = obj;
02001         return (ISC_R_SUCCESS);
02002  cleanup:
02003         CLEANUP_OBJ(obj);
02004         return (result);
02005 }
02006 
02007 static void
02008 cfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
02009         const unsigned int *flagp = type->of;
02010         int n = 0;
02011         if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
02012                 cfg_print_cstr(pctx, "( ");
02013         if (*flagp & CFG_ADDR_V4OK) {
02014                 cfg_print_cstr(pctx, "<ipv4_address>");
02015                 n++;
02016         }
02017         if (*flagp & CFG_ADDR_V6OK) {
02018                 if (n != 0)
02019                         cfg_print_cstr(pctx, " | ");
02020                 cfg_print_cstr(pctx, "<ipv6_address>");
02021                 n++;
02022         }
02023         if (*flagp & CFG_ADDR_WILDOK) {
02024                 if (n != 0)
02025                         cfg_print_cstr(pctx, " | ");
02026                 cfg_print_cstr(pctx, "*");
02027                 n++;
02028                 POST(n);
02029         }
02030         if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
02031                 cfg_print_cstr(pctx, " )");
02032 }
02033 
02034 cfg_type_t cfg_type_netaddr = {
02035         "netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
02036         &cfg_rep_sockaddr, &netaddr_flags
02037 };
02038 
02039 cfg_type_t cfg_type_netaddr4 = {
02040         "netaddr4", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
02041         &cfg_rep_sockaddr, &netaddr4_flags
02042 };
02043 
02044 cfg_type_t cfg_type_netaddr4wild = {
02045         "netaddr4wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
02046         &cfg_rep_sockaddr, &netaddr4wild_flags
02047 };
02048 
02049 cfg_type_t cfg_type_netaddr6 = {
02050         "netaddr6", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
02051         &cfg_rep_sockaddr, &netaddr6_flags
02052 };
02053 
02054 cfg_type_t cfg_type_netaddr6wild = {
02055         "netaddr6wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
02056         &cfg_rep_sockaddr, &netaddr6wild_flags
02057 };
02058 
02059 /* netprefix */
02060 
02061 isc_result_t
02062 cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
02063                     cfg_obj_t **ret)
02064 {
02065         cfg_obj_t *obj = NULL;
02066         isc_result_t result;
02067         isc_netaddr_t netaddr;
02068         unsigned int addrlen = 0, prefixlen;
02069         UNUSED(type);
02070 
02071         CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
02072                                 CFG_ADDR_V6OK, &netaddr));
02073         switch (netaddr.family) {
02074         case AF_INET:
02075                 addrlen = 32;
02076                 break;
02077         case AF_INET6:
02078                 addrlen = 128;
02079                 break;
02080         default:
02081                 INSIST(0);
02082                 break;
02083         }
02084         CHECK(cfg_peektoken(pctx, 0));
02085         if (pctx->token.type == isc_tokentype_special &&
02086             pctx->token.value.as_char == '/') {
02087                 CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
02088                 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
02089                 if (pctx->token.type != isc_tokentype_number) {
02090                         cfg_parser_error(pctx, CFG_LOG_NEAR,
02091                                      "expected prefix length");
02092                         return (ISC_R_UNEXPECTEDTOKEN);
02093                 }
02094                 prefixlen = pctx->token.value.as_ulong;
02095                 if (prefixlen > addrlen) {
02096                         cfg_parser_error(pctx, CFG_LOG_NOPREP,
02097                                      "invalid prefix length");
02098                         return (ISC_R_RANGE);
02099                 }
02100         } else {
02101                 prefixlen = addrlen;
02102         }
02103         CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
02104         obj->value.netprefix.address = netaddr;
02105         obj->value.netprefix.prefixlen = prefixlen;
02106         *ret = obj;
02107         return (ISC_R_SUCCESS);
02108  cleanup:
02109         cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
02110         return (result);
02111 }
02112 
02113 static void
02114 print_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) {
02115         const cfg_netprefix_t *p = &obj->value.netprefix;
02116 
02117         cfg_print_rawaddr(pctx, &p->address);
02118         cfg_print_cstr(pctx, "/");
02119         cfg_print_rawuint(pctx, p->prefixlen);
02120 }
02121 
02122 isc_boolean_t
02123 cfg_obj_isnetprefix(const cfg_obj_t *obj) {
02124         REQUIRE(obj != NULL);
02125         return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
02126 }
02127 
02128 void
02129 cfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr,
02130                     unsigned int *prefixlen)
02131 {
02132         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
02133         REQUIRE(netaddr != NULL);
02134         REQUIRE(prefixlen != NULL);
02135 
02136         *netaddr = obj->value.netprefix.address;
02137         *prefixlen = obj->value.netprefix.prefixlen;
02138 }
02139 
02140 cfg_type_t cfg_type_netprefix = {
02141         "netprefix", cfg_parse_netprefix, print_netprefix, cfg_doc_terminal,
02142         &cfg_rep_netprefix, NULL
02143 };
02144 
02145 static isc_result_t
02146 parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type,
02147                   int flags, cfg_obj_t **ret)
02148 {
02149         isc_result_t result;
02150         isc_netaddr_t netaddr;
02151         in_port_t port = 0;
02152         isc_dscp_t dscp = -1;
02153         cfg_obj_t *obj = NULL;
02154         int have_port = 0, have_dscp = 0;
02155 
02156         CHECK(cfg_create_obj(pctx, type, &obj));
02157         CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
02158         for (;;) {
02159                 CHECK(cfg_peektoken(pctx, 0));
02160                 if (pctx->token.type == isc_tokentype_string) {
02161                         if (strcasecmp(TOKEN_STRING(pctx), "port") == 0) {
02162                                 CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
02163                                 CHECK(cfg_parse_rawport(pctx, flags, &port));
02164                                 ++have_port;
02165                         } else if ((flags & CFG_ADDR_DSCPOK) != 0 &&
02166                                    strcasecmp(TOKEN_STRING(pctx), "dscp") == 0)
02167                         {
02168                                 CHECK(cfg_gettoken(pctx, 0)); /* read "dscp" */
02169                                 CHECK(cfg_parse_dscp(pctx, &dscp));
02170                                 ++have_dscp;
02171                         } else
02172                                 break;
02173                 } else
02174                         break;
02175         }
02176         if (have_port > 1) {
02177                 cfg_parser_error(pctx, 0, "expected at most one port");
02178                 result = ISC_R_UNEXPECTEDTOKEN;
02179                 goto cleanup;
02180         }
02181 
02182         if (have_dscp > 1) {
02183                 cfg_parser_error(pctx, 0, "expected at most one dscp");
02184                 result = ISC_R_UNEXPECTEDTOKEN;
02185                 goto cleanup;
02186         }
02187         isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
02188         obj->value.sockaddrdscp.dscp = dscp;
02189         *ret = obj;
02190         return (ISC_R_SUCCESS);
02191 
02192  cleanup:
02193         CLEANUP_OBJ(obj);
02194         return (result);
02195 }
02196 
02197 static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
02198 cfg_type_t cfg_type_sockaddr = {
02199         "sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr,
02200         &cfg_rep_sockaddr, &sockaddr_flags
02201 };
02202 
02203 static unsigned int sockaddrdscp_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK |
02204                                          CFG_ADDR_DSCPOK;
02205 cfg_type_t cfg_type_sockaddrdscp = {
02206         "sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr,
02207         &cfg_rep_sockaddr, &sockaddrdscp_flags
02208 };
02209 
02210 isc_result_t
02211 cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
02212         const unsigned int *flagp = type->of;
02213         return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret));
02214 }
02215 
02216 void
02217 cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) {
02218         isc_netaddr_t netaddr;
02219         in_port_t port;
02220         char buf[ISC_NETADDR_FORMATSIZE];
02221 
02222         isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
02223         isc_netaddr_format(&netaddr, buf, sizeof(buf));
02224         cfg_print_cstr(pctx, buf);
02225         port = isc_sockaddr_getport(&obj->value.sockaddr);
02226         if (port != 0) {
02227                 cfg_print_cstr(pctx, " port ");
02228                 cfg_print_rawuint(pctx, port);
02229         }
02230         if (obj->value.sockaddrdscp.dscp != -1) {
02231                 cfg_print_cstr(pctx, " dscp ");
02232                 cfg_print_rawuint(pctx, obj->value.sockaddrdscp.dscp);
02233         }
02234 }
02235 
02236 void
02237 cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
02238         const unsigned int *flagp = type->of;
02239         int n = 0;
02240         cfg_print_cstr(pctx, "( ");
02241         if (*flagp & CFG_ADDR_V4OK) {
02242                 cfg_print_cstr(pctx, "<ipv4_address>");
02243                 n++;
02244         }
02245         if (*flagp & CFG_ADDR_V6OK) {
02246                 if (n != 0)
02247                         cfg_print_cstr(pctx, " | ");
02248                 cfg_print_cstr(pctx, "<ipv6_address>");
02249                 n++;
02250         }
02251         if (*flagp & CFG_ADDR_WILDOK) {
02252                 if (n != 0)
02253                         cfg_print_cstr(pctx, " | ");
02254                 cfg_print_cstr(pctx, "*");
02255                 n++;
02256                 POST(n);
02257         }
02258         cfg_print_cstr(pctx, " ) ");
02259         if (*flagp & CFG_ADDR_WILDOK) {
02260                 cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
02261         } else {
02262                 cfg_print_cstr(pctx, "[ port <integer> ]");
02263         }
02264         if ((*flagp & CFG_ADDR_DSCPOK) != 0) {
02265                 cfg_print_cstr(pctx, " [ dscp <integer> ]");
02266         }
02267 }
02268 
02269 isc_boolean_t
02270 cfg_obj_issockaddr(const cfg_obj_t *obj) {
02271         REQUIRE(obj != NULL);
02272         return (ISC_TF(obj->type->rep == &cfg_rep_sockaddr));
02273 }
02274 
02275 const isc_sockaddr_t *
02276 cfg_obj_assockaddr(const cfg_obj_t *obj) {
02277         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
02278         return (&obj->value.sockaddr);
02279 }
02280 
02281 isc_dscp_t
02282 cfg_obj_getdscp(const cfg_obj_t *obj) {
02283         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
02284         return (obj->value.sockaddrdscp.dscp);
02285 }
02286 
02287 isc_result_t
02288 cfg_gettoken(cfg_parser_t *pctx, int options) {
02289         isc_result_t result;
02290 
02291         if (pctx->seen_eof)
02292                 return (ISC_R_SUCCESS);
02293 
02294         options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
02295 
02296  redo:
02297         pctx->token.type = isc_tokentype_unknown;
02298         result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
02299         pctx->ungotten = ISC_FALSE;
02300         pctx->line = isc_lex_getsourceline(pctx->lexer);
02301 
02302         switch (result) {
02303         case ISC_R_SUCCESS:
02304                 if (pctx->token.type == isc_tokentype_eof) {
02305                         result = isc_lex_close(pctx->lexer);
02306                         INSIST(result == ISC_R_NOMORE ||
02307                                result == ISC_R_SUCCESS);
02308 
02309                         if (isc_lex_getsourcename(pctx->lexer) != NULL) {
02310                                 /*
02311                                  * Closed an included file, not the main file.
02312                                  */
02313                                 cfg_listelt_t *elt;
02314                                 elt = ISC_LIST_TAIL(pctx->open_files->
02315                                                     value.list);
02316                                 INSIST(elt != NULL);
02317                                 ISC_LIST_UNLINK(pctx->open_files->
02318                                                 value.list, elt, link);
02319                                 ISC_LIST_APPEND(pctx->closed_files->
02320                                                 value.list, elt, link);
02321                                 goto redo;
02322                         }
02323                         pctx->seen_eof = ISC_TRUE;
02324                 }
02325                 break;
02326 
02327         case ISC_R_NOSPACE:
02328                 /* More understandable than "ran out of space". */
02329                 cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
02330                 break;
02331 
02332         case ISC_R_IOERROR:
02333                 cfg_parser_error(pctx, 0, "%s",
02334                                  isc_result_totext(result));
02335                 break;
02336 
02337         default:
02338                 cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
02339                                  isc_result_totext(result));
02340                 break;
02341         }
02342         return (result);
02343 }
02344 
02345 void
02346 cfg_ungettoken(cfg_parser_t *pctx) {
02347         if (pctx->seen_eof)
02348                 return;
02349         isc_lex_ungettoken(pctx->lexer, &pctx->token);
02350         pctx->ungotten = ISC_TRUE;
02351 }
02352 
02353 isc_result_t
02354 cfg_peektoken(cfg_parser_t *pctx, int options) {
02355         isc_result_t result;
02356         CHECK(cfg_gettoken(pctx, options));
02357         cfg_ungettoken(pctx);
02358  cleanup:
02359         return (result);
02360 }
02361 
02362 /*
02363  * Get a string token, accepting both the quoted and the unquoted form.
02364  * Log an error if the next token is not a string.
02365  */
02366 static isc_result_t
02367 cfg_getstringtoken(cfg_parser_t *pctx) {
02368         isc_result_t result;
02369 
02370         result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
02371         if (result != ISC_R_SUCCESS)
02372                 return (result);
02373 
02374         if (pctx->token.type != isc_tokentype_string &&
02375             pctx->token.type != isc_tokentype_qstring) {
02376                 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
02377                 return (ISC_R_UNEXPECTEDTOKEN);
02378         }
02379         return (ISC_R_SUCCESS);
02380 }
02381 
02382 void
02383 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
02384         va_list args;
02385         va_start(args, fmt);
02386         parser_complain(pctx, ISC_FALSE, flags, fmt, args);
02387         va_end(args);
02388         pctx->errors++;
02389 }
02390 
02391 void
02392 cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
02393         va_list args;
02394         va_start(args, fmt);
02395         parser_complain(pctx, ISC_TRUE, flags, fmt, args);
02396         va_end(args);
02397         pctx->warnings++;
02398 }
02399 
02400 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
02401 
02402 static isc_boolean_t
02403 have_current_file(cfg_parser_t *pctx) {
02404         cfg_listelt_t *elt;
02405         if (pctx->open_files == NULL)
02406                 return (ISC_FALSE);
02407 
02408         elt = ISC_LIST_TAIL(pctx->open_files->value.list);
02409         if (elt == NULL)
02410               return (ISC_FALSE);
02411 
02412         return (ISC_TRUE);
02413 }
02414 
02415 static char *
02416 current_file(cfg_parser_t *pctx) {
02417         static char none[] = "none";
02418         cfg_listelt_t *elt;
02419         cfg_obj_t *fileobj;
02420 
02421         if (!have_current_file(pctx))
02422                 return (none);
02423 
02424         elt = ISC_LIST_TAIL(pctx->open_files->value.list);
02425         if (elt == NULL)        /* shouldn't be possible, but... */
02426               return (none);
02427 
02428         fileobj = elt->obj;
02429         INSIST(fileobj->type == &cfg_type_qstring);
02430         return (fileobj->value.string.base);
02431 }
02432 
02433 static void
02434 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
02435                 unsigned int flags, const char *format,
02436                 va_list args)
02437 {
02438         char tokenbuf[MAX_LOG_TOKEN + 10];
02439         static char where[ISC_DIR_PATHMAX + 100];
02440         static char message[2048];
02441         int level = ISC_LOG_ERROR;
02442         const char *prep = "";
02443         size_t len;
02444 
02445         if (is_warning)
02446                 level = ISC_LOG_WARNING;
02447 
02448         where[0] = '\0';
02449         if (have_current_file(pctx))
02450                 snprintf(where, sizeof(where), "%s:%u: ",
02451                          current_file(pctx), pctx->line);
02452 
02453         len = vsnprintf(message, sizeof(message), format, args);
02454         if (len >= sizeof(message))
02455                 FATAL_ERROR(__FILE__, __LINE__,
02456                             "error message would overflow");
02457 
02458         if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) {
02459                 isc_region_t r;
02460 
02461                 if (pctx->ungotten)
02462                         (void)cfg_gettoken(pctx, 0);
02463 
02464                 if (pctx->token.type == isc_tokentype_eof) {
02465                         snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
02466                 } else if (pctx->token.type == isc_tokentype_unknown) {
02467                         flags = 0;
02468                         tokenbuf[0] = '\0';
02469                 } else {
02470                         isc_lex_getlasttokentext(pctx->lexer,
02471                                                  &pctx->token, &r);
02472                         if (r.length > MAX_LOG_TOKEN)
02473                                 snprintf(tokenbuf, sizeof(tokenbuf),
02474                                          "'%.*s...'", MAX_LOG_TOKEN, r.base);
02475                         else
02476                                 snprintf(tokenbuf, sizeof(tokenbuf),
02477                                          "'%.*s'", (int)r.length, r.base);
02478                 }
02479 
02480                 /* Choose a preposition. */
02481                 if (flags & CFG_LOG_NEAR)
02482                         prep = " near ";
02483                 else if (flags & CFG_LOG_BEFORE)
02484                         prep = " before ";
02485                 else
02486                         prep = " ";
02487         } else {
02488                 tokenbuf[0] = '\0';
02489         }
02490         isc_log_write(pctx->lctx, CAT, MOD, level,
02491                       "%s%s%s%s", where, message, prep, tokenbuf);
02492 }
02493 
02494 void
02495 cfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level,
02496             const char *fmt, ...) {
02497         va_list ap;
02498         char msgbuf[2048];
02499 
02500         if (! isc_log_wouldlog(lctx, level))
02501                 return;
02502 
02503         va_start(ap, fmt);
02504 
02505         vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
02506         isc_log_write(lctx, CAT, MOD, level,
02507                       "%s:%u: %s",
02508                       obj->file == NULL ? "<unknown file>" : obj->file,
02509                       obj->line, msgbuf);
02510         va_end(ap);
02511 }
02512 
02513 const char *
02514 cfg_obj_file(const cfg_obj_t *obj) {
02515         return (obj->file);
02516 }
02517 
02518 unsigned int
02519 cfg_obj_line(const cfg_obj_t *obj) {
02520         return (obj->line);
02521 }
02522 
02523 isc_result_t
02524 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
02525         isc_result_t result;
02526         cfg_obj_t *obj;
02527 
02528         obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
02529         if (obj == NULL)
02530                 return (ISC_R_NOMEMORY);
02531         obj->type = type;
02532         obj->file = current_file(pctx);
02533         obj->line = pctx->line;
02534         result = isc_refcount_init(&obj->references, 1);
02535         if (result != ISC_R_SUCCESS) {
02536                 isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
02537                 return (result);
02538         }
02539         *ret = obj;
02540         return (ISC_R_SUCCESS);
02541 }
02542 
02543 
02544 static void
02545 map_symtabitem_destroy(char *key, unsigned int type,
02546                        isc_symvalue_t symval, void *userarg)
02547 {
02548         cfg_obj_t *obj = symval.as_pointer;
02549         cfg_parser_t *pctx = (cfg_parser_t *)userarg;
02550 
02551         UNUSED(key);
02552         UNUSED(type);
02553 
02554         cfg_obj_destroy(pctx, &obj);
02555 }
02556 
02557 static isc_result_t
02558 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
02559         isc_result_t result;
02560         isc_symtab_t *symtab = NULL;
02561         cfg_obj_t *obj = NULL;
02562 
02563         CHECK(cfg_create_obj(pctx, type, &obj));
02564         CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */
02565                                 map_symtabitem_destroy,
02566                                 pctx, ISC_FALSE, &symtab));
02567         obj->value.map.symtab = symtab;
02568         obj->value.map.id = NULL;
02569 
02570         *ret = obj;
02571         return (ISC_R_SUCCESS);
02572 
02573  cleanup:
02574         if (obj != NULL)
02575                 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
02576         return (result);
02577 }
02578 
02579 static void
02580 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
02581         CLEANUP_OBJ(obj->value.map.id);
02582         isc_symtab_destroy(&obj->value.map.symtab);
02583 }
02584 
02585 isc_boolean_t
02586 cfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type) {
02587         return (ISC_TF(obj->type == type));
02588 }
02589 
02590 /*
02591  * Destroy 'obj', a configuration object created in 'pctx'.
02592  */
02593 void
02594 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
02595         cfg_obj_t *obj;
02596         unsigned int refs;
02597 
02598         REQUIRE(objp != NULL && *objp != NULL);
02599         REQUIRE(pctx != NULL);
02600 
02601         obj = *objp;
02602 
02603         isc_refcount_decrement(&obj->references, &refs);
02604         if (refs == 0) {
02605                 obj->type->rep->free(pctx, obj);
02606                 isc_refcount_destroy(&obj->references);
02607                 isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
02608         }
02609         *objp = NULL;
02610 }
02611 
02612 void
02613 cfg_obj_attach(cfg_obj_t *src, cfg_obj_t **dest) {
02614     REQUIRE(src != NULL);
02615     REQUIRE(dest != NULL && *dest == NULL);
02616     isc_refcount_increment(&src->references, NULL);
02617     *dest = src;
02618 }
02619 
02620 static void
02621 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
02622         UNUSED(pctx);
02623         UNUSED(obj);
02624 }
02625 
02626 void
02627 cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
02628         type->doc(pctx, type);
02629 }
02630 
02631 void
02632 cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) {
02633         cfg_print_cstr(pctx, "<");
02634         cfg_print_cstr(pctx, type->name);
02635         cfg_print_cstr(pctx, ">");
02636 }
02637 
02638 void
02639 cfg_print_grammar(const cfg_type_t *type,
02640         void (*f)(void *closure, const char *text, int textlen),
02641         void *closure)
02642 {
02643         cfg_printer_t pctx;
02644         pctx.f = f;
02645         pctx.closure = closure;
02646         pctx.indent = 0;
02647         pctx.flags = 0;
02648         cfg_doc_obj(&pctx, type);
02649 }
02650 
02651 isc_result_t
02652 cfg_parser_mapadd(cfg_parser_t *pctx, cfg_obj_t *mapobj,
02653                   cfg_obj_t *obj, const char *clausename)
02654 {
02655         isc_result_t result = ISC_R_SUCCESS;
02656         const cfg_map_t *map;
02657         isc_symvalue_t symval;
02658         cfg_obj_t *destobj = NULL;
02659         cfg_listelt_t *elt = NULL;
02660         const cfg_clausedef_t * const *clauseset;
02661         const cfg_clausedef_t *clause;
02662 
02663         REQUIRE(pctx != NULL);
02664         REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
02665         REQUIRE(obj != NULL);
02666 
02667         map = &mapobj->value.map;
02668 
02669         clause = NULL;
02670         for (clauseset = map->clausesets; *clauseset != NULL; clauseset++) {
02671                 for (clause = *clauseset; clause->name != NULL; clause++) {
02672                         if (strcasecmp(clause->name, clausename) == 0) {
02673                                 goto breakout;
02674                         }
02675                 }
02676         }
02677 
02678  breakout:
02679         if (clause == NULL || clause->name == NULL)
02680                 return (ISC_R_FAILURE);
02681 
02682         result = isc_symtab_lookup(map->symtab, clausename, 0, &symval);
02683         if (result == ISC_R_NOTFOUND) {
02684                 if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
02685                         CHECK(cfg_create_list(pctx, &cfg_type_implicitlist,
02686                                               &destobj));
02687                         CHECK(create_listelt(pctx, &elt));
02688                         cfg_obj_attach(obj, &elt->obj);
02689                         ISC_LIST_APPEND(destobj->value.list, elt, link);
02690                         symval.as_pointer = destobj;
02691                 } else
02692                         symval.as_pointer = obj;
02693 
02694                 CHECK(isc_symtab_define(map->symtab, clausename, 1, symval,
02695                                         isc_symexists_reject));
02696         } else {
02697                 cfg_obj_t *destobj2 = symval.as_pointer;
02698 
02699                 INSIST(result == ISC_R_SUCCESS);
02700 
02701                 if (destobj2->type == &cfg_type_implicitlist) {
02702                         CHECK(create_listelt(pctx, &elt));
02703                         cfg_obj_attach(obj, &elt->obj);
02704                         ISC_LIST_APPEND(destobj2->value.list, elt, link);
02705                 } else
02706                         result = ISC_R_EXISTS;
02707         }
02708 
02709         destobj = NULL;
02710         elt = NULL;
02711 
02712  cleanup:
02713         if (elt != NULL)
02714                 free_listelt(pctx, elt);
02715         CLEANUP_OBJ(destobj);
02716 
02717         return (result);
02718 }

Generated on Tue Apr 28 17:41:06 2015 by Doxygen 1.5.4 for BIND9 Internals 9.11.0pre-alpha