dma: beautify queue listing output
[dragonfly.git] / contrib / bind-9.3 / lib / isccfg / parser.c
blob42ce9f0c03a67881cfe6b0a33438feb9f8924b28
1 /*
2 * Copyright (C) 2004, 2006 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000-2003 Internet Software Consortium.
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
18 /* $Id: parser.c,v 1.70.2.20.2.21 2006/02/28 06:32:54 marka Exp $ */
20 #include <config.h>
22 #include <isc/buffer.h>
23 #include <isc/dir.h>
24 #include <isc/formatcheck.h>
25 #include <isc/lex.h>
26 #include <isc/log.h>
27 #include <isc/mem.h>
28 #include <isc/net.h>
29 #include <isc/netaddr.h>
30 #include <isc/print.h>
31 #include <isc/string.h>
32 #include <isc/sockaddr.h>
33 #include <isc/netscope.h>
34 #include <isc/util.h>
35 #include <isc/symtab.h>
37 #include <isccfg/cfg.h>
38 #include <isccfg/grammar.h>
39 #include <isccfg/log.h>
41 /* Shorthand */
42 #define CAT CFG_LOGCATEGORY_CONFIG
43 #define MOD CFG_LOGMODULE_PARSER
45 #define MAP_SYM 1 /* Unique type for isc_symtab */
47 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
49 /* Check a return value. */
50 #define CHECK(op) \
51 do { result = (op); \
52 if (result != ISC_R_SUCCESS) goto cleanup; \
53 } while (0)
55 /* Clean up a configuration object if non-NULL. */
56 #define CLEANUP_OBJ(obj) \
57 do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
61 * Forward declarations of static functions.
64 static void
65 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
67 static isc_result_t
68 parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
70 static void
71 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
73 static void
74 free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
76 static isc_result_t
77 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
79 static isc_result_t
80 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
81 cfg_obj_t **ret);
83 static void
84 free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
86 static isc_result_t
87 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
89 static void
90 free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
92 static isc_result_t
93 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
94 cfg_type_t *elttype, isc_symtab_t *symtab,
95 isc_boolean_t callback);
97 static void
98 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
100 static isc_result_t
101 cfg_getstringtoken(cfg_parser_t *pctx);
103 static void
104 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
105 unsigned int flags, const char *format, va_list args);
108 * Data representations. These correspond to members of the
109 * "value" union in struct cfg_obj (except "void", which does
110 * not need a union member).
113 cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
114 cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
115 cfg_rep_t cfg_rep_string = { "string", free_string };
116 cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
117 cfg_rep_t cfg_rep_map = { "map", free_map };
118 cfg_rep_t cfg_rep_list = { "list", free_list };
119 cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
120 cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
121 cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
122 cfg_rep_t cfg_rep_void = { "void", free_noop };
125 * Configuration type definitions.
129 * An implicit list. These are formed by clauses that occur multiple times.
131 static cfg_type_t cfg_type_implicitlist = {
132 "implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL };
134 /* Functions. */
136 void
137 cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
138 obj->type->print(pctx, obj);
141 void
142 cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
143 pctx->f(pctx->closure, text, len);
146 static void
147 print_open(cfg_printer_t *pctx) {
148 cfg_print_chars(pctx, "{\n", 2);
149 pctx->indent++;
152 static void
153 print_indent(cfg_printer_t *pctx) {
154 int indent = pctx->indent;
155 while (indent > 0) {
156 cfg_print_chars(pctx, "\t", 1);
157 indent--;
161 static void
162 print_close(cfg_printer_t *pctx) {
163 pctx->indent--;
164 print_indent(pctx);
165 cfg_print_chars(pctx, "}", 1);
168 isc_result_t
169 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
170 isc_result_t result;
171 INSIST(ret != NULL && *ret == NULL);
172 result = type->parse(pctx, type, ret);
173 if (result != ISC_R_SUCCESS)
174 return (result);
175 INSIST(*ret != NULL);
176 return (ISC_R_SUCCESS);
179 void
180 cfg_print(const cfg_obj_t *obj,
181 void (*f)(void *closure, const char *text, int textlen),
182 void *closure)
184 cfg_printer_t pctx;
185 pctx.f = f;
186 pctx.closure = closure;
187 pctx.indent = 0;
188 obj->type->print(&pctx, obj);
192 /* Tuples. */
194 isc_result_t
195 cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
196 isc_result_t result;
197 const cfg_tuplefielddef_t *fields = type->of;
198 const cfg_tuplefielddef_t *f;
199 cfg_obj_t *obj = NULL;
200 unsigned int nfields = 0;
201 int i;
203 for (f = fields; f->name != NULL; f++)
204 nfields++;
206 CHECK(cfg_create_obj(pctx, type, &obj));
207 obj->value.tuple = isc_mem_get(pctx->mctx,
208 nfields * sizeof(cfg_obj_t *));
209 if (obj->value.tuple == NULL) {
210 result = ISC_R_NOMEMORY;
211 goto cleanup;
213 for (f = fields, i = 0; f->name != NULL; f++, i++)
214 obj->value.tuple[i] = NULL;
215 *ret = obj;
216 return (ISC_R_SUCCESS);
218 cleanup:
219 if (obj != NULL)
220 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
221 return (result);
224 isc_result_t
225 cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
227 isc_result_t result;
228 const cfg_tuplefielddef_t *fields = type->of;
229 const cfg_tuplefielddef_t *f;
230 cfg_obj_t *obj = NULL;
231 unsigned int i;
233 CHECK(cfg_create_tuple(pctx, type, &obj));
234 for (f = fields, i = 0; f->name != NULL; f++, i++)
235 CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
237 *ret = obj;
238 return (ISC_R_SUCCESS);
240 cleanup:
241 CLEANUP_OBJ(obj);
242 return (result);
245 void
246 cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
247 unsigned int i;
248 const cfg_tuplefielddef_t *fields = obj->type->of;
249 const cfg_tuplefielddef_t *f;
250 isc_boolean_t need_space = ISC_FALSE;
252 for (f = fields, i = 0; f->name != NULL; f++, i++) {
253 const cfg_obj_t *fieldobj = obj->value.tuple[i];
254 if (need_space)
255 cfg_print_chars(pctx, " ", 1);
256 cfg_print_obj(pctx, fieldobj);
257 need_space = ISC_TF(fieldobj->type->print != cfg_print_void);
261 void
262 cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
263 const cfg_tuplefielddef_t *fields = type->of;
264 const cfg_tuplefielddef_t *f;
265 isc_boolean_t need_space = ISC_FALSE;
267 for (f = fields; f->name != NULL; f++) {
268 if (need_space)
269 cfg_print_chars(pctx, " ", 1);
270 cfg_doc_obj(pctx, f->type);
271 need_space = ISC_TF(f->type->print != cfg_print_void);
275 static void
276 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
277 unsigned int i;
278 const cfg_tuplefielddef_t *fields = obj->type->of;
279 const cfg_tuplefielddef_t *f;
280 unsigned int nfields = 0;
282 if (obj->value.tuple == NULL)
283 return;
285 for (f = fields, i = 0; f->name != NULL; f++, i++) {
286 CLEANUP_OBJ(obj->value.tuple[i]);
287 nfields++;
289 isc_mem_put(pctx->mctx, obj->value.tuple,
290 nfields * sizeof(cfg_obj_t *));
293 isc_boolean_t
294 cfg_obj_istuple(const cfg_obj_t *obj) {
295 REQUIRE(obj != NULL);
296 return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
299 const cfg_obj_t *
300 cfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) {
301 unsigned int i;
302 const cfg_tuplefielddef_t *fields;
303 const cfg_tuplefielddef_t *f;
305 REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
307 fields = tupleobj->type->of;
308 for (f = fields, i = 0; f->name != NULL; f++, i++) {
309 if (strcmp(f->name, name) == 0)
310 return (tupleobj->value.tuple[i]);
312 INSIST(0);
313 return (NULL);
316 isc_result_t
317 cfg_parse_special(cfg_parser_t *pctx, int special) {
318 isc_result_t result;
319 CHECK(cfg_gettoken(pctx, 0));
320 if (pctx->token.type == isc_tokentype_special &&
321 pctx->token.value.as_char == special)
322 return (ISC_R_SUCCESS);
324 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
325 return (ISC_R_UNEXPECTEDTOKEN);
326 cleanup:
327 return (result);
331 * Parse a required semicolon. If it is not there, log
332 * an error and increment the error count but continue
333 * parsing. Since the next token is pushed back,
334 * care must be taken to make sure it is eventually
335 * consumed or an infinite loop may result.
337 static isc_result_t
338 parse_semicolon(cfg_parser_t *pctx) {
339 isc_result_t result;
340 CHECK(cfg_gettoken(pctx, 0));
341 if (pctx->token.type == isc_tokentype_special &&
342 pctx->token.value.as_char == ';')
343 return (ISC_R_SUCCESS);
345 cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
346 cfg_ungettoken(pctx);
347 cleanup:
348 return (result);
352 * Parse EOF, logging and returning an error if not there.
354 static isc_result_t
355 parse_eof(cfg_parser_t *pctx) {
356 isc_result_t result;
357 CHECK(cfg_gettoken(pctx, 0));
359 if (pctx->token.type == isc_tokentype_eof)
360 return (ISC_R_SUCCESS);
362 cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
363 return (ISC_R_UNEXPECTEDTOKEN);
364 cleanup:
365 return (result);
368 /* A list of files, used internally for pctx->files. */
370 static cfg_type_t cfg_type_filelist = {
371 "filelist", NULL, print_list, NULL, &cfg_rep_list,
372 &cfg_type_qstring
375 isc_result_t
376 cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
377 isc_result_t result;
378 cfg_parser_t *pctx;
379 isc_lexspecials_t specials;
381 REQUIRE(mctx != NULL);
382 REQUIRE(ret != NULL && *ret == NULL);
384 pctx = isc_mem_get(mctx, sizeof(*pctx));
385 if (pctx == NULL)
386 return (ISC_R_NOMEMORY);
388 pctx->mctx = mctx;
389 pctx->lctx = lctx;
390 pctx->lexer = NULL;
391 pctx->seen_eof = ISC_FALSE;
392 pctx->ungotten = ISC_FALSE;
393 pctx->errors = 0;
394 pctx->warnings = 0;
395 pctx->open_files = NULL;
396 pctx->closed_files = NULL;
397 pctx->line = 0;
398 pctx->callback = NULL;
399 pctx->callbackarg = NULL;
400 pctx->token.type = isc_tokentype_unknown;
402 memset(specials, 0, sizeof(specials));
403 specials['{'] = 1;
404 specials['}'] = 1;
405 specials[';'] = 1;
406 specials['/'] = 1;
407 specials['"'] = 1;
408 specials['!'] = 1;
410 CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer));
412 isc_lex_setspecials(pctx->lexer, specials);
413 isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C |
414 ISC_LEXCOMMENT_CPLUSPLUS |
415 ISC_LEXCOMMENT_SHELL));
417 CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
418 CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
420 *ret = pctx;
421 return (ISC_R_SUCCESS);
423 cleanup:
424 if (pctx->lexer != NULL)
425 isc_lex_destroy(&pctx->lexer);
426 CLEANUP_OBJ(pctx->open_files);
427 CLEANUP_OBJ(pctx->closed_files);
428 isc_mem_put(mctx, pctx, sizeof(*pctx));
429 return (result);
432 static isc_result_t
433 parser_openfile(cfg_parser_t *pctx, const char *filename) {
434 isc_result_t result;
435 cfg_listelt_t *elt = NULL;
436 cfg_obj_t *stringobj = NULL;
438 result = isc_lex_openfile(pctx->lexer, filename);
439 if (result != ISC_R_SUCCESS) {
440 cfg_parser_error(pctx, 0, "open: %s: %s",
441 filename, isc_result_totext(result));
442 goto cleanup;
445 CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
446 CHECK(create_listelt(pctx, &elt));
447 elt->obj = stringobj;
448 ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
450 return (ISC_R_SUCCESS);
451 cleanup:
452 CLEANUP_OBJ(stringobj);
453 return (result);
456 void
457 cfg_parser_setcallback(cfg_parser_t *pctx,
458 cfg_parsecallback_t callback,
459 void *arg)
461 pctx->callback = callback;
462 pctx->callbackarg = arg;
466 * Parse a configuration using a pctx where a lexer has already
467 * been set up with a source.
469 static isc_result_t
470 parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
471 isc_result_t result;
472 cfg_obj_t *obj = NULL;
474 result = cfg_parse_obj(pctx, type, &obj);
476 if (pctx->errors != 0) {
477 /* Errors have been logged. */
478 if (result == ISC_R_SUCCESS)
479 result = ISC_R_FAILURE;
480 goto cleanup;
483 if (result != ISC_R_SUCCESS) {
484 /* Parsing failed but no errors have been logged. */
485 cfg_parser_error(pctx, 0, "parsing failed");
486 goto cleanup;
489 CHECK(parse_eof(pctx));
491 *ret = obj;
492 return (ISC_R_SUCCESS);
494 cleanup:
495 CLEANUP_OBJ(obj);
496 return (result);
499 isc_result_t
500 cfg_parse_file(cfg_parser_t *pctx, const char *filename,
501 const cfg_type_t *type, cfg_obj_t **ret)
503 isc_result_t result;
505 REQUIRE(filename != NULL);
507 CHECK(parser_openfile(pctx, filename));
508 CHECK(parse2(pctx, type, ret));
509 cleanup:
510 return (result);
514 isc_result_t
515 cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
516 const cfg_type_t *type, cfg_obj_t **ret)
518 isc_result_t result;
519 REQUIRE(buffer != NULL);
520 CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
521 CHECK(parse2(pctx, type, ret));
522 cleanup:
523 return (result);
526 void
527 cfg_parser_destroy(cfg_parser_t **pctxp) {
528 cfg_parser_t *pctx = *pctxp;
529 isc_lex_destroy(&pctx->lexer);
531 * Cleaning up open_files does not
532 * close the files; that was already done
533 * by closing the lexer.
535 CLEANUP_OBJ(pctx->open_files);
536 CLEANUP_OBJ(pctx->closed_files);
537 isc_mem_put(pctx->mctx, pctx, sizeof(*pctx));
538 *pctxp = NULL;
542 * void
544 isc_result_t
545 cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
546 UNUSED(type);
547 return (cfg_create_obj(pctx, &cfg_type_void, ret));
550 void
551 cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
552 UNUSED(pctx);
553 UNUSED(obj);
556 void
557 cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
558 UNUSED(pctx);
559 UNUSED(type);
562 isc_boolean_t
563 cfg_obj_isvoid(const cfg_obj_t *obj) {
564 REQUIRE(obj != NULL);
565 return (ISC_TF(obj->type->rep == &cfg_rep_void));
568 cfg_type_t cfg_type_void = {
569 "void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
570 NULL };
574 * uint32
576 isc_result_t
577 cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
578 isc_result_t result;
579 cfg_obj_t *obj = NULL;
580 UNUSED(type);
582 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
583 if (pctx->token.type != isc_tokentype_number) {
584 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
585 return (ISC_R_UNEXPECTEDTOKEN);
588 CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
590 obj->value.uint32 = pctx->token.value.as_ulong;
591 *ret = obj;
592 cleanup:
593 return (result);
596 void
597 cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
598 cfg_print_chars(pctx, s, strlen(s));
601 void
602 cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
603 char buf[32];
604 snprintf(buf, sizeof(buf), "%u", u);
605 cfg_print_cstr(pctx, buf);
608 void
609 cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
610 cfg_print_rawuint(pctx, obj->value.uint32);
613 isc_boolean_t
614 cfg_obj_isuint32(const cfg_obj_t *obj) {
615 REQUIRE(obj != NULL);
616 return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
619 isc_uint32_t
620 cfg_obj_asuint32(const cfg_obj_t *obj) {
621 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
622 return (obj->value.uint32);
625 cfg_type_t cfg_type_uint32 = {
626 "integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
627 &cfg_rep_uint32, NULL
632 * uint64
634 isc_boolean_t
635 cfg_obj_isuint64(const cfg_obj_t *obj) {
636 REQUIRE(obj != NULL);
637 return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
640 isc_uint64_t
641 cfg_obj_asuint64(const cfg_obj_t *obj) {
642 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
643 return (obj->value.uint64);
646 void
647 cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
648 char buf[32];
649 snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
650 obj->value.uint64);
651 cfg_print_cstr(pctx, buf);
654 cfg_type_t cfg_type_uint64 = {
655 "64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
656 &cfg_rep_uint64, NULL
660 * qstring (quoted string), ustring (unquoted string), astring
661 * (any string)
664 /* Create a string object from a null-terminated C string. */
665 static isc_result_t
666 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
667 cfg_obj_t **ret)
669 isc_result_t result;
670 cfg_obj_t *obj = NULL;
671 int len;
673 CHECK(cfg_create_obj(pctx, type, &obj));
674 len = strlen(contents);
675 obj->value.string.length = len;
676 obj->value.string.base = isc_mem_get(pctx->mctx, len + 1);
677 if (obj->value.string.base == 0) {
678 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
679 return (ISC_R_NOMEMORY);
681 memcpy(obj->value.string.base, contents, len);
682 obj->value.string.base[len] = '\0';
684 *ret = obj;
685 cleanup:
686 return (result);
689 isc_result_t
690 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
691 isc_result_t result;
692 UNUSED(type);
694 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
695 if (pctx->token.type != isc_tokentype_qstring) {
696 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
697 return (ISC_R_UNEXPECTEDTOKEN);
699 return (create_string(pctx,
700 TOKEN_STRING(pctx),
701 &cfg_type_qstring,
702 ret));
703 cleanup:
704 return (result);
707 static isc_result_t
708 parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
709 isc_result_t result;
710 UNUSED(type);
712 CHECK(cfg_gettoken(pctx, 0));
713 if (pctx->token.type != isc_tokentype_string) {
714 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
715 return (ISC_R_UNEXPECTEDTOKEN);
717 return (create_string(pctx,
718 TOKEN_STRING(pctx),
719 &cfg_type_ustring,
720 ret));
721 cleanup:
722 return (result);
725 isc_result_t
726 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
727 cfg_obj_t **ret)
729 isc_result_t result;
730 UNUSED(type);
732 CHECK(cfg_getstringtoken(pctx));
733 return (create_string(pctx,
734 TOKEN_STRING(pctx),
735 &cfg_type_qstring,
736 ret));
737 cleanup:
738 return (result);
741 isc_boolean_t
742 cfg_is_enum(const char *s, const char *const *enums) {
743 const char * const *p;
744 for (p = enums; *p != NULL; p++) {
745 if (strcasecmp(*p, s) == 0)
746 return (ISC_TRUE);
748 return (ISC_FALSE);
751 static isc_result_t
752 check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
753 const char *s = obj->value.string.base;
754 if (cfg_is_enum(s, enums))
755 return (ISC_R_SUCCESS);
756 cfg_parser_error(pctx, 0, "'%s' unexpected", s);
757 return (ISC_R_UNEXPECTEDTOKEN);
760 isc_result_t
761 cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
762 isc_result_t result;
763 cfg_obj_t *obj = NULL;
764 CHECK(parse_ustring(pctx, NULL, &obj));
765 CHECK(check_enum(pctx, obj, type->of));
766 *ret = obj;
767 return (ISC_R_SUCCESS);
768 cleanup:
769 CLEANUP_OBJ(obj);
770 return (result);
773 void
774 cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
775 const char * const *p;
776 cfg_print_chars(pctx, "( ", 2);
777 for (p = type->of; *p != NULL; p++) {
778 cfg_print_cstr(pctx, *p);
779 if (p[1] != NULL)
780 cfg_print_chars(pctx, " | ", 3);
782 cfg_print_chars(pctx, " )", 2);
785 void
786 cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
787 cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
790 static void
791 print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
792 cfg_print_chars(pctx, "\"", 1);
793 cfg_print_ustring(pctx, obj);
794 cfg_print_chars(pctx, "\"", 1);
797 static void
798 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
799 isc_mem_put(pctx->mctx, obj->value.string.base,
800 obj->value.string.length + 1);
803 isc_boolean_t
804 cfg_obj_isstring(const cfg_obj_t *obj) {
805 REQUIRE(obj != NULL);
806 return (ISC_TF(obj->type->rep == &cfg_rep_string));
809 const char *
810 cfg_obj_asstring(const cfg_obj_t *obj) {
811 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
812 return (obj->value.string.base);
815 /* Quoted string only */
816 cfg_type_t cfg_type_qstring = {
817 "quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal,
818 &cfg_rep_string, NULL
821 /* Unquoted string only */
822 cfg_type_t cfg_type_ustring = {
823 "string", parse_ustring, cfg_print_ustring, cfg_doc_terminal,
824 &cfg_rep_string, NULL
827 /* Any string (quoted or unquoted); printed with quotes */
828 cfg_type_t cfg_type_astring = {
829 "string", cfg_parse_astring, print_qstring, cfg_doc_terminal,
830 &cfg_rep_string, NULL
834 * Booleans
837 isc_boolean_t
838 cfg_obj_isboolean(const cfg_obj_t *obj) {
839 REQUIRE(obj != NULL);
840 return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
843 isc_boolean_t
844 cfg_obj_asboolean(const cfg_obj_t *obj) {
845 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
846 return (obj->value.boolean);
849 static isc_result_t
850 parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
852 isc_result_t result;
853 isc_boolean_t value;
854 cfg_obj_t *obj = NULL;
855 UNUSED(type);
857 result = cfg_gettoken(pctx, 0);
858 if (result != ISC_R_SUCCESS)
859 return (result);
861 if (pctx->token.type != isc_tokentype_string)
862 goto bad_boolean;
864 if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
865 (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
866 (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
867 value = ISC_TRUE;
868 } else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
869 (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
870 (strcmp(TOKEN_STRING(pctx), "0") == 0)) {
871 value = ISC_FALSE;
872 } else {
873 goto bad_boolean;
876 CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
877 obj->value.boolean = value;
878 *ret = obj;
879 return (result);
881 bad_boolean:
882 cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
883 return (ISC_R_UNEXPECTEDTOKEN);
885 cleanup:
886 return (result);
889 static void
890 print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
891 if (obj->value.boolean)
892 cfg_print_chars(pctx, "yes", 3);
893 else
894 cfg_print_chars(pctx, "no", 2);
897 cfg_type_t cfg_type_boolean = {
898 "boolean", parse_boolean, print_boolean, cfg_doc_terminal,
899 &cfg_rep_boolean, NULL
903 * Lists.
906 isc_result_t
907 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
908 isc_result_t result;
909 CHECK(cfg_create_obj(pctx, type, obj));
910 ISC_LIST_INIT((*obj)->value.list);
911 cleanup:
912 return (result);
915 static isc_result_t
916 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
917 cfg_listelt_t *elt;
918 elt = isc_mem_get(pctx->mctx, sizeof(*elt));
919 if (elt == NULL)
920 return (ISC_R_NOMEMORY);
921 elt->obj = NULL;
922 ISC_LINK_INIT(elt, link);
923 *eltp = elt;
924 return (ISC_R_SUCCESS);
927 static void
928 free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
929 cfg_obj_destroy(pctx, &elt->obj);
930 isc_mem_put(pctx->mctx, elt, sizeof(*elt));
933 static void
934 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
935 cfg_listelt_t *elt, *next;
936 for (elt = ISC_LIST_HEAD(obj->value.list);
937 elt != NULL;
938 elt = next)
940 next = ISC_LIST_NEXT(elt, link);
941 free_list_elt(pctx, elt);
945 isc_result_t
946 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
947 cfg_listelt_t **ret)
949 isc_result_t result;
950 cfg_listelt_t *elt = NULL;
951 cfg_obj_t *value = NULL;
953 CHECK(create_listelt(pctx, &elt));
955 result = cfg_parse_obj(pctx, elttype, &value);
956 if (result != ISC_R_SUCCESS)
957 goto cleanup;
959 elt->obj = value;
961 *ret = elt;
962 return (ISC_R_SUCCESS);
964 cleanup:
965 isc_mem_put(pctx->mctx, elt, sizeof(*elt));
966 return (result);
970 * Parse a homogeneous list whose elements are of type 'elttype'
971 * and where each element is terminated by a semicolon.
973 static isc_result_t
974 parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
976 cfg_obj_t *listobj = NULL;
977 const cfg_type_t *listof = listtype->of;
978 isc_result_t result;
979 cfg_listelt_t *elt = NULL;
981 CHECK(cfg_create_list(pctx, listtype, &listobj));
983 for (;;) {
984 CHECK(cfg_peektoken(pctx, 0));
985 if (pctx->token.type == isc_tokentype_special &&
986 pctx->token.value.as_char == /*{*/ '}')
987 break;
988 CHECK(cfg_parse_listelt(pctx, listof, &elt));
989 CHECK(parse_semicolon(pctx));
990 ISC_LIST_APPEND(listobj->value.list, elt, link);
991 elt = NULL;
993 *ret = listobj;
994 return (ISC_R_SUCCESS);
996 cleanup:
997 if (elt != NULL)
998 free_list_elt(pctx, elt);
999 CLEANUP_OBJ(listobj);
1000 return (result);
1003 static void
1004 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1005 const cfg_list_t *list = &obj->value.list;
1006 const cfg_listelt_t *elt;
1008 for (elt = ISC_LIST_HEAD(*list);
1009 elt != NULL;
1010 elt = ISC_LIST_NEXT(elt, link)) {
1011 print_indent(pctx);
1012 cfg_print_obj(pctx, elt->obj);
1013 cfg_print_chars(pctx, ";\n", 2);
1017 isc_result_t
1018 cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
1019 cfg_obj_t **ret)
1021 isc_result_t result;
1022 CHECK(cfg_parse_special(pctx, '{'));
1023 CHECK(parse_list(pctx, type, ret));
1024 CHECK(cfg_parse_special(pctx, '}'));
1025 cleanup:
1026 return (result);
1029 void
1030 cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1031 print_open(pctx);
1032 print_list(pctx, obj);
1033 print_close(pctx);
1036 void
1037 cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
1038 cfg_print_chars(pctx, "{ ", 2);
1039 cfg_doc_obj(pctx, type->of);
1040 cfg_print_chars(pctx, "; ... }", 7);
1044 * Parse a homogeneous list whose elements are of type 'elttype'
1045 * and where elements are separated by space. The list ends
1046 * before the first semicolon.
1048 isc_result_t
1049 cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
1050 cfg_obj_t **ret)
1052 cfg_obj_t *listobj = NULL;
1053 const cfg_type_t *listof = listtype->of;
1054 isc_result_t result;
1056 CHECK(cfg_create_list(pctx, listtype, &listobj));
1058 for (;;) {
1059 cfg_listelt_t *elt = NULL;
1061 CHECK(cfg_peektoken(pctx, 0));
1062 if (pctx->token.type == isc_tokentype_special &&
1063 pctx->token.value.as_char == ';')
1064 break;
1065 CHECK(cfg_parse_listelt(pctx, listof, &elt));
1066 ISC_LIST_APPEND(listobj->value.list, elt, link);
1068 *ret = listobj;
1069 return (ISC_R_SUCCESS);
1071 cleanup:
1072 CLEANUP_OBJ(listobj);
1073 return (result);
1076 void
1077 cfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1078 const cfg_list_t *list = &obj->value.list;
1079 const cfg_listelt_t *elt;
1081 for (elt = ISC_LIST_HEAD(*list);
1082 elt != NULL;
1083 elt = ISC_LIST_NEXT(elt, link)) {
1084 cfg_print_obj(pctx, elt->obj);
1085 if (ISC_LIST_NEXT(elt, link) != NULL)
1086 cfg_print_chars(pctx, " ", 1);
1091 isc_boolean_t
1092 cfg_obj_islist(const cfg_obj_t *obj) {
1093 REQUIRE(obj != NULL);
1094 return (ISC_TF(obj->type->rep == &cfg_rep_list));
1097 const cfg_listelt_t *
1098 cfg_list_first(const cfg_obj_t *obj) {
1099 REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
1100 if (obj == NULL)
1101 return (NULL);
1102 return (ISC_LIST_HEAD(obj->value.list));
1105 const cfg_listelt_t *
1106 cfg_list_next(const cfg_listelt_t *elt) {
1107 REQUIRE(elt != NULL);
1108 return (ISC_LIST_NEXT(elt, link));
1111 const cfg_obj_t *
1112 cfg_listelt_value(const cfg_listelt_t *elt) {
1113 REQUIRE(elt != NULL);
1114 return (elt->obj);
1118 * Maps.
1122 * Parse a map body. That's something like
1124 * "foo 1; bar { glub; }; zap true; zap false;"
1126 * i.e., a sequence of option names followed by values and
1127 * terminated by semicolons. Used for the top level of
1128 * the named.conf syntax, as well as for the body of the
1129 * options, view, zone, and other statements.
1131 isc_result_t
1132 cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1134 const cfg_clausedef_t * const *clausesets = type->of;
1135 isc_result_t result;
1136 const cfg_clausedef_t * const *clauseset;
1137 const cfg_clausedef_t *clause;
1138 cfg_obj_t *value = NULL;
1139 cfg_obj_t *obj = NULL;
1140 cfg_obj_t *eltobj = NULL;
1141 cfg_obj_t *includename = NULL;
1142 isc_symvalue_t symval;
1143 cfg_list_t *list = NULL;
1145 CHECK(create_map(pctx, type, &obj));
1147 obj->value.map.clausesets = clausesets;
1149 for (;;) {
1150 cfg_listelt_t *elt;
1152 redo:
1154 * Parse the option name and see if it is known.
1156 CHECK(cfg_gettoken(pctx, 0));
1158 if (pctx->token.type != isc_tokentype_string) {
1159 cfg_ungettoken(pctx);
1160 break;
1164 * We accept "include" statements wherever a map body
1165 * clause can occur.
1167 if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
1169 * Turn the file name into a temporary configuration
1170 * object just so that it is not overwritten by the
1171 * semicolon token.
1173 CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
1174 CHECK(parse_semicolon(pctx));
1175 CHECK(parser_openfile(pctx, includename->
1176 value.string.base));
1177 cfg_obj_destroy(pctx, &includename);
1178 goto redo;
1181 clause = NULL;
1182 for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
1183 for (clause = *clauseset;
1184 clause->name != NULL;
1185 clause++) {
1186 if (strcasecmp(TOKEN_STRING(pctx),
1187 clause->name) == 0)
1188 goto done;
1191 done:
1192 if (clause == NULL || clause->name == NULL) {
1193 cfg_parser_error(pctx, CFG_LOG_NOPREP, "unknown option");
1195 * Try to recover by parsing this option as an unknown
1196 * option and discarding it.
1198 CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, &eltobj));
1199 cfg_obj_destroy(pctx, &eltobj);
1200 CHECK(parse_semicolon(pctx));
1201 continue;
1204 /* Clause is known. */
1206 /* Issue warnings if appropriate */
1207 if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0)
1208 cfg_parser_warning(pctx, 0, "option '%s' is obsolete",
1209 clause->name);
1210 if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0)
1211 cfg_parser_warning(pctx, 0, "option '%s' is "
1212 "not implemented", clause->name);
1213 if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0)
1214 cfg_parser_warning(pctx, 0, "option '%s' is "
1215 "not implemented", clause->name);
1217 * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
1218 * set here - we need to log the *lack* of such an option,
1219 * not its presence.
1222 /* See if the clause already has a value; if not create one. */
1223 result = isc_symtab_lookup(obj->value.map.symtab,
1224 clause->name, 0, &symval);
1226 if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
1227 /* Multivalued clause */
1228 cfg_obj_t *listobj = NULL;
1229 if (result == ISC_R_NOTFOUND) {
1230 CHECK(cfg_create_list(pctx,
1231 &cfg_type_implicitlist,
1232 &listobj));
1233 symval.as_pointer = listobj;
1234 result = isc_symtab_define(obj->value.
1235 map.symtab,
1236 clause->name,
1237 1, symval,
1238 isc_symexists_reject);
1239 if (result != ISC_R_SUCCESS) {
1240 cfg_parser_error(pctx, CFG_LOG_NEAR,
1241 "isc_symtab_define(%s) "
1242 "failed", clause->name);
1243 isc_mem_put(pctx->mctx, list,
1244 sizeof(cfg_list_t));
1245 goto cleanup;
1247 } else {
1248 INSIST(result == ISC_R_SUCCESS);
1249 listobj = symval.as_pointer;
1252 elt = NULL;
1253 CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
1254 CHECK(parse_semicolon(pctx));
1256 ISC_LIST_APPEND(listobj->value.list, elt, link);
1257 } else {
1258 /* Single-valued clause */
1259 if (result == ISC_R_NOTFOUND) {
1260 isc_boolean_t callback =
1261 ISC_TF((clause->flags &
1262 CFG_CLAUSEFLAG_CALLBACK) != 0);
1263 CHECK(parse_symtab_elt(pctx, clause->name,
1264 clause->type,
1265 obj->value.map.symtab,
1266 callback));
1267 CHECK(parse_semicolon(pctx));
1268 } else if (result == ISC_R_SUCCESS) {
1269 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
1270 clause->name);
1271 result = ISC_R_EXISTS;
1272 goto cleanup;
1273 } else {
1274 cfg_parser_error(pctx, CFG_LOG_NEAR,
1275 "isc_symtab_define() failed");
1276 goto cleanup;
1282 *ret = obj;
1283 return (ISC_R_SUCCESS);
1285 cleanup:
1286 CLEANUP_OBJ(value);
1287 CLEANUP_OBJ(obj);
1288 CLEANUP_OBJ(eltobj);
1289 CLEANUP_OBJ(includename);
1290 return (result);
1293 static isc_result_t
1294 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
1295 cfg_type_t *elttype, isc_symtab_t *symtab,
1296 isc_boolean_t callback)
1298 isc_result_t result;
1299 cfg_obj_t *obj = NULL;
1300 isc_symvalue_t symval;
1302 CHECK(cfg_parse_obj(pctx, elttype, &obj));
1304 if (callback && pctx->callback != NULL)
1305 CHECK(pctx->callback(name, obj, pctx->callbackarg));
1307 symval.as_pointer = obj;
1308 CHECK(isc_symtab_define(symtab, name,
1309 1, symval,
1310 isc_symexists_reject));
1311 return (ISC_R_SUCCESS);
1313 cleanup:
1314 CLEANUP_OBJ(obj);
1315 return (result);
1319 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
1321 isc_result_t
1322 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1323 isc_result_t result;
1324 CHECK(cfg_parse_special(pctx, '{'));
1325 CHECK(cfg_parse_mapbody(pctx, type, ret));
1326 CHECK(cfg_parse_special(pctx, '}'));
1327 cleanup:
1328 return (result);
1332 * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
1334 static isc_result_t
1335 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type,
1336 cfg_obj_t **ret)
1338 isc_result_t result;
1339 cfg_obj_t *idobj = NULL;
1340 cfg_obj_t *mapobj = NULL;
1342 CHECK(cfg_parse_obj(pctx, nametype, &idobj));
1343 CHECK(cfg_parse_map(pctx, type, &mapobj));
1344 mapobj->value.map.id = idobj;
1345 idobj = NULL;
1346 *ret = mapobj;
1347 cleanup:
1348 CLEANUP_OBJ(idobj);
1349 return (result);
1353 * Parse a map identified by a string name. E.g., "name { foo 1; }".
1354 * Used for the "key" and "channel" statements.
1356 isc_result_t
1357 cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1358 return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
1362 * Parse a map identified by a network address.
1363 * Used for the "server" statement.
1365 isc_result_t
1366 cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1367 return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
1370 void
1371 cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1372 isc_result_t result = ISC_R_SUCCESS;
1374 const cfg_clausedef_t * const *clauseset;
1376 for (clauseset = obj->value.map.clausesets;
1377 *clauseset != NULL;
1378 clauseset++)
1380 isc_symvalue_t symval;
1381 const cfg_clausedef_t *clause;
1383 for (clause = *clauseset;
1384 clause->name != NULL;
1385 clause++) {
1386 result = isc_symtab_lookup(obj->value.map.symtab,
1387 clause->name, 0, &symval);
1388 if (result == ISC_R_SUCCESS) {
1389 cfg_obj_t *obj = symval.as_pointer;
1390 if (obj->type == &cfg_type_implicitlist) {
1391 /* Multivalued. */
1392 cfg_list_t *list = &obj->value.list;
1393 cfg_listelt_t *elt;
1394 for (elt = ISC_LIST_HEAD(*list);
1395 elt != NULL;
1396 elt = ISC_LIST_NEXT(elt, link)) {
1397 print_indent(pctx);
1398 cfg_print_cstr(pctx, clause->name);
1399 cfg_print_chars(pctx, " ", 1);
1400 cfg_print_obj(pctx, elt->obj);
1401 cfg_print_chars(pctx, ";\n", 2);
1403 } else {
1404 /* Single-valued. */
1405 print_indent(pctx);
1406 cfg_print_cstr(pctx, clause->name);
1407 cfg_print_chars(pctx, " ", 1);
1408 cfg_print_obj(pctx, obj);
1409 cfg_print_chars(pctx, ";\n", 2);
1411 } else if (result == ISC_R_NOTFOUND) {
1412 ; /* do nothing */
1413 } else {
1414 INSIST(0);
1420 void
1421 cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
1422 const cfg_clausedef_t * const *clauseset;
1423 const cfg_clausedef_t *clause;
1425 for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1426 for (clause = *clauseset;
1427 clause->name != NULL;
1428 clause++) {
1429 cfg_print_cstr(pctx, clause->name);
1430 cfg_print_chars(pctx, " ", 1);
1431 cfg_doc_obj(pctx, clause->type);
1432 cfg_print_chars(pctx, ";", 1);
1433 /* XXX print flags here? */
1434 cfg_print_chars(pctx, "\n\n", 2);
1439 static struct flagtext {
1440 unsigned int flag;
1441 const char *text;
1442 } flagtexts[] = {
1443 { CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
1444 { CFG_CLAUSEFLAG_NYI, "not yet implemented" },
1445 { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
1446 { CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
1447 { 0, NULL }
1450 void
1451 cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1452 if (obj->value.map.id != NULL) {
1453 cfg_print_obj(pctx, obj->value.map.id);
1454 cfg_print_chars(pctx, " ", 1);
1456 print_open(pctx);
1457 cfg_print_mapbody(pctx, obj);
1458 print_close(pctx);
1461 static void
1462 print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
1463 struct flagtext *p;
1464 isc_boolean_t first = ISC_TRUE;
1465 for (p = flagtexts; p->flag != 0; p++) {
1466 if ((flags & p->flag) != 0) {
1467 if (first)
1468 cfg_print_chars(pctx, " // ", 4);
1469 else
1470 cfg_print_chars(pctx, ", ", 2);
1471 cfg_print_cstr(pctx, p->text);
1472 first = ISC_FALSE;
1477 void
1478 cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
1479 const cfg_clausedef_t * const *clauseset;
1480 const cfg_clausedef_t *clause;
1482 if (type->parse == cfg_parse_named_map) {
1483 cfg_doc_obj(pctx, &cfg_type_astring);
1484 cfg_print_chars(pctx, " ", 1);
1485 } else if (type->parse == cfg_parse_addressed_map) {
1486 cfg_doc_obj(pctx, &cfg_type_netaddr);
1487 cfg_print_chars(pctx, " ", 1);
1490 print_open(pctx);
1492 for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1493 for (clause = *clauseset;
1494 clause->name != NULL;
1495 clause++) {
1496 print_indent(pctx);
1497 cfg_print_cstr(pctx, clause->name);
1498 if (clause->type->print != cfg_print_void)
1499 cfg_print_chars(pctx, " ", 1);
1500 cfg_doc_obj(pctx, clause->type);
1501 cfg_print_chars(pctx, ";", 1);
1502 print_clause_flags(pctx, clause->flags);
1503 cfg_print_chars(pctx, "\n", 1);
1506 print_close(pctx);
1509 isc_boolean_t
1510 cfg_obj_ismap(const cfg_obj_t *obj) {
1511 REQUIRE(obj != NULL);
1512 return (ISC_TF(obj->type->rep == &cfg_rep_map));
1515 isc_result_t
1516 cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
1517 isc_result_t result;
1518 isc_symvalue_t val;
1519 const cfg_map_t *map;
1521 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1522 REQUIRE(name != NULL);
1523 REQUIRE(obj != NULL && *obj == NULL);
1525 map = &mapobj->value.map;
1527 result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
1528 if (result != ISC_R_SUCCESS)
1529 return (result);
1530 *obj = val.as_pointer;
1531 return (ISC_R_SUCCESS);
1534 const cfg_obj_t *
1535 cfg_map_getname(const cfg_obj_t *mapobj) {
1536 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1537 return (mapobj->value.map.id);
1541 /* Parse an arbitrary token, storing its raw text representation. */
1542 static isc_result_t
1543 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1544 cfg_obj_t *obj = NULL;
1545 isc_result_t result;
1546 isc_region_t r;
1548 UNUSED(type);
1550 CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
1551 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
1552 if (pctx->token.type == isc_tokentype_eof) {
1553 cfg_ungettoken(pctx);
1554 result = ISC_R_EOF;
1555 goto cleanup;
1558 isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
1560 obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
1561 if (obj->value.string.base == NULL) {
1562 result = ISC_R_NOMEMORY;
1563 goto cleanup;
1565 obj->value.string.length = r.length;
1566 memcpy(obj->value.string.base, r.base, r.length);
1567 obj->value.string.base[r.length] = '\0';
1568 *ret = obj;
1569 return (result);
1571 cleanup:
1572 if (obj != NULL)
1573 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
1574 return (result);
1577 cfg_type_t cfg_type_token = {
1578 "token", parse_token, cfg_print_ustring, cfg_doc_terminal,
1579 &cfg_rep_string, NULL
1583 * An unsupported option. This is just a list of tokens with balanced braces
1584 * ending in a semicolon.
1587 static isc_result_t
1588 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1589 cfg_obj_t *listobj = NULL;
1590 isc_result_t result;
1591 int braces = 0;
1593 CHECK(cfg_create_list(pctx, type, &listobj));
1595 for (;;) {
1596 cfg_listelt_t *elt = NULL;
1598 CHECK(cfg_peektoken(pctx, 0));
1599 if (pctx->token.type == isc_tokentype_special) {
1600 if (pctx->token.value.as_char == '{')
1601 braces++;
1602 else if (pctx->token.value.as_char == '}')
1603 braces--;
1604 else if (pctx->token.value.as_char == ';')
1605 if (braces == 0)
1606 break;
1608 if (pctx->token.type == isc_tokentype_eof || braces < 0) {
1609 cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
1610 result = ISC_R_UNEXPECTEDTOKEN;
1611 goto cleanup;
1614 CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
1615 ISC_LIST_APPEND(listobj->value.list, elt, link);
1617 INSIST(braces == 0);
1618 *ret = listobj;
1619 return (ISC_R_SUCCESS);
1621 cleanup:
1622 CLEANUP_OBJ(listobj);
1623 return (result);
1626 cfg_type_t cfg_type_unsupported = {
1627 "unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal,
1628 &cfg_rep_list, NULL
1632 * Try interpreting the current token as a network address.
1634 * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
1635 * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set. The
1636 * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
1637 * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
1638 * and the IPv6 wildcard address otherwise.
1640 static isc_result_t
1641 token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1642 char *s;
1643 struct in_addr in4a;
1644 struct in6_addr in6a;
1646 if (pctx->token.type != isc_tokentype_string)
1647 return (ISC_R_UNEXPECTEDTOKEN);
1649 s = TOKEN_STRING(pctx);
1650 if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
1651 if ((flags & CFG_ADDR_V4OK) != 0) {
1652 isc_netaddr_any(na);
1653 return (ISC_R_SUCCESS);
1654 } else if ((flags & CFG_ADDR_V6OK) != 0) {
1655 isc_netaddr_any6(na);
1656 return (ISC_R_SUCCESS);
1657 } else {
1658 INSIST(0);
1660 } else {
1661 if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
1662 if (inet_pton(AF_INET, s, &in4a) == 1) {
1663 isc_netaddr_fromin(na, &in4a);
1664 return (ISC_R_SUCCESS);
1667 if ((flags & CFG_ADDR_V4PREFIXOK) != 0 &&
1668 strlen(s) <= 15U) {
1669 char buf[64];
1670 int i;
1672 strcpy(buf, s);
1673 for (i = 0; i < 3; i++) {
1674 strcat(buf, ".0");
1675 if (inet_pton(AF_INET, buf, &in4a) == 1) {
1676 isc_netaddr_fromin(na, &in4a);
1677 return (ISC_R_SUCCESS);
1681 if ((flags & CFG_ADDR_V6OK) != 0 &&
1682 strlen(s) <= 127U) {
1683 char buf[128]; /* see lib/bind9/getaddresses.c */
1684 char *d; /* zone delimiter */
1685 isc_uint32_t zone = 0; /* scope zone ID */
1687 strcpy(buf, s);
1688 d = strchr(buf, '%');
1689 if (d != NULL)
1690 *d = '\0';
1692 if (inet_pton(AF_INET6, buf, &in6a) == 1) {
1693 if (d != NULL) {
1694 #ifdef ISC_PLATFORM_HAVESCOPEID
1695 isc_result_t result;
1697 result = isc_netscope_pton(AF_INET6,
1698 d + 1,
1699 &in6a,
1700 &zone);
1701 if (result != ISC_R_SUCCESS)
1702 return (result);
1703 #else
1704 return (ISC_R_BADADDRESSFORM);
1705 #endif
1708 isc_netaddr_fromin6(na, &in6a);
1709 isc_netaddr_setzone(na, zone);
1710 return (ISC_R_SUCCESS);
1714 return (ISC_R_UNEXPECTEDTOKEN);
1717 isc_result_t
1718 cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1719 isc_result_t result;
1720 CHECK(cfg_gettoken(pctx, 0));
1721 result = token_addr(pctx, flags, na);
1722 if (result == ISC_R_UNEXPECTEDTOKEN)
1723 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected IP address");
1724 cleanup:
1725 return (result);
1728 isc_boolean_t
1729 cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
1730 isc_result_t result;
1731 isc_netaddr_t na_dummy;
1732 result = token_addr(pctx, flags, &na_dummy);
1733 return (ISC_TF(result == ISC_R_SUCCESS));
1736 isc_result_t
1737 cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
1738 isc_result_t result;
1740 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1742 if ((flags & CFG_ADDR_WILDOK) != 0 &&
1743 pctx->token.type == isc_tokentype_string &&
1744 strcmp(TOKEN_STRING(pctx), "*") == 0) {
1745 *port = 0;
1746 return (ISC_R_SUCCESS);
1748 if (pctx->token.type != isc_tokentype_number) {
1749 cfg_parser_error(pctx, CFG_LOG_NEAR,
1750 "expected port number or '*'");
1751 return (ISC_R_UNEXPECTEDTOKEN);
1753 if (pctx->token.value.as_ulong >= 65536U) {
1754 cfg_parser_error(pctx, CFG_LOG_NEAR,
1755 "port number out of range");
1756 return (ISC_R_UNEXPECTEDTOKEN);
1758 *port = (in_port_t)(pctx->token.value.as_ulong);
1759 return (ISC_R_SUCCESS);
1760 cleanup:
1761 return (result);
1764 void
1765 cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
1766 isc_result_t result;
1767 char text[128];
1768 isc_buffer_t buf;
1770 isc_buffer_init(&buf, text, sizeof(text));
1771 result = isc_netaddr_totext(na, &buf);
1772 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1773 cfg_print_chars(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf));
1776 /* netaddr */
1778 static isc_result_t
1779 parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1780 isc_result_t result;
1781 cfg_obj_t *obj = NULL;
1782 isc_netaddr_t netaddr;
1783 UNUSED(type);
1784 CHECK(cfg_create_obj(pctx, type, &obj));
1785 CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK, &netaddr));
1786 isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
1787 *ret = obj;
1788 return (ISC_R_SUCCESS);
1789 cleanup:
1790 CLEANUP_OBJ(obj);
1791 return (result);
1794 cfg_type_t cfg_type_netaddr = {
1795 "netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_terminal,
1796 &cfg_rep_sockaddr, NULL
1799 /* netprefix */
1801 isc_result_t
1802 cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
1803 cfg_obj_t **ret)
1805 cfg_obj_t *obj = NULL;
1806 isc_result_t result;
1807 isc_netaddr_t netaddr;
1808 unsigned int addrlen, prefixlen;
1809 UNUSED(type);
1811 CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
1812 CFG_ADDR_V6OK, &netaddr));
1813 switch (netaddr.family) {
1814 case AF_INET:
1815 addrlen = 32;
1816 break;
1817 case AF_INET6:
1818 addrlen = 128;
1819 break;
1820 default:
1821 addrlen = 0;
1822 INSIST(0);
1823 break;
1825 CHECK(cfg_peektoken(pctx, 0));
1826 if (pctx->token.type == isc_tokentype_special &&
1827 pctx->token.value.as_char == '/') {
1828 CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
1829 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1830 if (pctx->token.type != isc_tokentype_number) {
1831 cfg_parser_error(pctx, CFG_LOG_NEAR,
1832 "expected prefix length");
1833 return (ISC_R_UNEXPECTEDTOKEN);
1835 prefixlen = pctx->token.value.as_ulong;
1836 if (prefixlen > addrlen) {
1837 cfg_parser_error(pctx, CFG_LOG_NOPREP,
1838 "invalid prefix length");
1839 return (ISC_R_RANGE);
1841 } else {
1842 prefixlen = addrlen;
1844 CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
1845 obj->value.netprefix.address = netaddr;
1846 obj->value.netprefix.prefixlen = prefixlen;
1847 *ret = obj;
1848 return (ISC_R_SUCCESS);
1849 cleanup:
1850 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
1851 return (result);
1854 static void
1855 print_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1856 const cfg_netprefix_t *p = &obj->value.netprefix;
1858 cfg_print_rawaddr(pctx, &p->address);
1859 cfg_print_chars(pctx, "/", 1);
1860 cfg_print_rawuint(pctx, p->prefixlen);
1863 isc_boolean_t
1864 cfg_obj_isnetprefix(const cfg_obj_t *obj) {
1865 REQUIRE(obj != NULL);
1866 return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
1869 void
1870 cfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr,
1871 unsigned int *prefixlen) {
1872 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
1873 *netaddr = obj->value.netprefix.address;
1874 *prefixlen = obj->value.netprefix.prefixlen;
1877 cfg_type_t cfg_type_netprefix = {
1878 "netprefix", cfg_parse_netprefix, print_netprefix, cfg_doc_terminal,
1879 &cfg_rep_netprefix, NULL
1882 static isc_result_t
1883 parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type,
1884 int flags, cfg_obj_t **ret)
1886 isc_result_t result;
1887 isc_netaddr_t netaddr;
1888 in_port_t port = 0;
1889 cfg_obj_t *obj = NULL;
1891 CHECK(cfg_create_obj(pctx, type, &obj));
1892 CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
1893 CHECK(cfg_peektoken(pctx, 0));
1894 if (pctx->token.type == isc_tokentype_string &&
1895 strcasecmp(TOKEN_STRING(pctx), "port") == 0) {
1896 CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
1897 CHECK(cfg_parse_rawport(pctx, flags, &port));
1899 isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
1900 *ret = obj;
1901 return (ISC_R_SUCCESS);
1903 cleanup:
1904 CLEANUP_OBJ(obj);
1905 return (result);
1908 static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
1909 cfg_type_t cfg_type_sockaddr = {
1910 "sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr,
1911 &cfg_rep_sockaddr, &sockaddr_flags
1914 isc_result_t
1915 cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1916 const unsigned int *flagp = type->of;
1917 return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret));
1920 void
1921 cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1922 isc_netaddr_t netaddr;
1923 in_port_t port;
1924 char buf[ISC_NETADDR_FORMATSIZE];
1926 isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
1927 isc_netaddr_format(&netaddr, buf, sizeof(buf));
1928 cfg_print_cstr(pctx, buf);
1929 port = isc_sockaddr_getport(&obj->value.sockaddr);
1930 if (port != 0) {
1931 cfg_print_chars(pctx, " port ", 6);
1932 cfg_print_rawuint(pctx, port);
1936 void
1937 cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
1938 const unsigned int *flagp = type->of;
1939 int n = 0;
1940 cfg_print_chars(pctx, "( ", 2);
1941 if (*flagp & CFG_ADDR_V4OK) {
1942 cfg_print_cstr(pctx, "<ipv4_address>");
1943 n++;
1945 if (*flagp & CFG_ADDR_V6OK) {
1946 if (n != 0)
1947 cfg_print_chars(pctx, " | ", 3);
1948 cfg_print_cstr(pctx, "<ipv6_address>");
1949 n++;
1951 if (*flagp & CFG_ADDR_WILDOK) {
1952 if (n != 0)
1953 cfg_print_chars(pctx, " | ", 3);
1954 cfg_print_chars(pctx, "*", 1);
1955 n++;
1957 cfg_print_chars(pctx, " ) ", 3);
1958 if (*flagp & CFG_ADDR_WILDOK) {
1959 cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
1960 } else {
1961 cfg_print_cstr(pctx, "[ port <integer> ]");
1965 isc_boolean_t
1966 cfg_obj_issockaddr(const cfg_obj_t *obj) {
1967 REQUIRE(obj != NULL);
1968 return (ISC_TF(obj->type->rep == &cfg_rep_sockaddr));
1971 const isc_sockaddr_t *
1972 cfg_obj_assockaddr(const cfg_obj_t *obj) {
1973 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
1974 return (&obj->value.sockaddr);
1977 isc_result_t
1978 cfg_gettoken(cfg_parser_t *pctx, int options) {
1979 isc_result_t result;
1981 if (pctx->seen_eof)
1982 return (ISC_R_SUCCESS);
1984 options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
1986 redo:
1987 pctx->token.type = isc_tokentype_unknown;
1988 result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
1989 pctx->ungotten = ISC_FALSE;
1990 pctx->line = isc_lex_getsourceline(pctx->lexer);
1992 switch (result) {
1993 case ISC_R_SUCCESS:
1994 if (pctx->token.type == isc_tokentype_eof) {
1995 result = isc_lex_close(pctx->lexer);
1996 INSIST(result == ISC_R_NOMORE ||
1997 result == ISC_R_SUCCESS);
1999 if (isc_lex_getsourcename(pctx->lexer) != NULL) {
2001 * Closed an included file, not the main file.
2003 cfg_listelt_t *elt;
2004 elt = ISC_LIST_TAIL(pctx->open_files->
2005 value.list);
2006 INSIST(elt != NULL);
2007 ISC_LIST_UNLINK(pctx->open_files->
2008 value.list, elt, link);
2009 ISC_LIST_APPEND(pctx->closed_files->
2010 value.list, elt, link);
2011 goto redo;
2013 pctx->seen_eof = ISC_TRUE;
2015 break;
2017 case ISC_R_NOSPACE:
2018 /* More understandable than "ran out of space". */
2019 cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
2020 break;
2022 case ISC_R_IOERROR:
2023 cfg_parser_error(pctx, 0, "%s",
2024 isc_result_totext(result));
2025 break;
2027 default:
2028 cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
2029 isc_result_totext(result));
2030 break;
2032 return (result);
2035 void
2036 cfg_ungettoken(cfg_parser_t *pctx) {
2037 if (pctx->seen_eof)
2038 return;
2039 isc_lex_ungettoken(pctx->lexer, &pctx->token);
2040 pctx->ungotten = ISC_TRUE;
2043 isc_result_t
2044 cfg_peektoken(cfg_parser_t *pctx, int options) {
2045 isc_result_t result;
2046 CHECK(cfg_gettoken(pctx, options));
2047 cfg_ungettoken(pctx);
2048 cleanup:
2049 return (result);
2053 * Get a string token, accepting both the quoted and the unquoted form.
2054 * Log an error if the next token is not a string.
2056 static isc_result_t
2057 cfg_getstringtoken(cfg_parser_t *pctx) {
2058 isc_result_t result;
2060 result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
2061 if (result != ISC_R_SUCCESS)
2062 return (result);
2064 if (pctx->token.type != isc_tokentype_string &&
2065 pctx->token.type != isc_tokentype_qstring) {
2066 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
2067 return (ISC_R_UNEXPECTEDTOKEN);
2069 return (ISC_R_SUCCESS);
2072 void
2073 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2074 va_list args;
2075 va_start(args, fmt);
2076 parser_complain(pctx, ISC_FALSE, flags, fmt, args);
2077 va_end(args);
2078 pctx->errors++;
2081 void
2082 cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2083 va_list args;
2084 va_start(args, fmt);
2085 parser_complain(pctx, ISC_TRUE, flags, fmt, args);
2086 va_end(args);
2087 pctx->warnings++;
2090 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
2092 static char *
2093 current_file(cfg_parser_t *pctx) {
2094 static char none[] = "none";
2095 cfg_listelt_t *elt;
2096 cfg_obj_t *fileobj;
2098 if (pctx->open_files == NULL)
2099 return (none);
2100 elt = ISC_LIST_TAIL(pctx->open_files->value.list);
2101 if (elt == NULL)
2102 return (none);
2104 fileobj = elt->obj;
2105 INSIST(fileobj->type == &cfg_type_qstring);
2106 return (fileobj->value.string.base);
2109 static void
2110 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
2111 unsigned int flags, const char *format,
2112 va_list args)
2114 char tokenbuf[MAX_LOG_TOKEN + 10];
2115 static char where[ISC_DIR_PATHMAX + 100];
2116 static char message[2048];
2117 int level = ISC_LOG_ERROR;
2118 const char *prep = "";
2119 size_t len;
2121 if (is_warning)
2122 level = ISC_LOG_WARNING;
2124 snprintf(where, sizeof(where), "%s:%u: ",
2125 current_file(pctx), pctx->line);
2127 len = vsnprintf(message, sizeof(message), format, args);
2128 if (len >= sizeof(message))
2129 FATAL_ERROR(__FILE__, __LINE__,
2130 "error message would overflow");
2132 if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) {
2133 isc_region_t r;
2135 if (pctx->ungotten)
2136 (void)cfg_gettoken(pctx, 0);
2138 if (pctx->token.type == isc_tokentype_eof) {
2139 snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
2140 } else if (pctx->token.type == isc_tokentype_unknown) {
2141 flags = 0;
2142 tokenbuf[0] = '\0';
2143 } else {
2144 isc_lex_getlasttokentext(pctx->lexer,
2145 &pctx->token, &r);
2146 if (r.length > MAX_LOG_TOKEN)
2147 snprintf(tokenbuf, sizeof(tokenbuf),
2148 "'%.*s...'", MAX_LOG_TOKEN, r.base);
2149 else
2150 snprintf(tokenbuf, sizeof(tokenbuf),
2151 "'%.*s'", (int)r.length, r.base);
2154 /* Choose a preposition. */
2155 if (flags & CFG_LOG_NEAR)
2156 prep = " near ";
2157 else if (flags & CFG_LOG_BEFORE)
2158 prep = " before ";
2159 else
2160 prep = " ";
2161 } else {
2162 tokenbuf[0] = '\0';
2164 isc_log_write(pctx->lctx, CAT, MOD, level,
2165 "%s%s%s%s", where, message, prep, tokenbuf);
2168 void
2169 cfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level,
2170 const char *fmt, ...) {
2171 va_list ap;
2172 char msgbuf[2048];
2174 if (! isc_log_wouldlog(lctx, level))
2175 return;
2177 va_start(ap, fmt);
2179 vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
2180 isc_log_write(lctx, CAT, MOD, level,
2181 "%s:%u: %s",
2182 obj->file == NULL ? "<unknown file>" : obj->file,
2183 obj->line, msgbuf);
2184 va_end(ap);
2187 const char *
2188 cfg_obj_file(const cfg_obj_t *obj) {
2189 return (obj->file);
2192 unsigned int
2193 cfg_obj_line(const cfg_obj_t *obj) {
2194 return (obj->line);
2197 isc_result_t
2198 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2199 cfg_obj_t *obj;
2201 obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
2202 if (obj == NULL)
2203 return (ISC_R_NOMEMORY);
2204 obj->type = type;
2205 obj->file = current_file(pctx);
2206 obj->line = pctx->line;
2207 *ret = obj;
2208 return (ISC_R_SUCCESS);
2211 static void
2212 map_symtabitem_destroy(char *key, unsigned int type,
2213 isc_symvalue_t symval, void *userarg)
2215 cfg_obj_t *obj = symval.as_pointer;
2216 cfg_parser_t *pctx = (cfg_parser_t *)userarg;
2218 UNUSED(key);
2219 UNUSED(type);
2221 cfg_obj_destroy(pctx, &obj);
2225 static isc_result_t
2226 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2227 isc_result_t result;
2228 isc_symtab_t *symtab = NULL;
2229 cfg_obj_t *obj = NULL;
2231 CHECK(cfg_create_obj(pctx, type, &obj));
2232 CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */
2233 map_symtabitem_destroy,
2234 pctx, ISC_FALSE, &symtab));
2235 obj->value.map.symtab = symtab;
2236 obj->value.map.id = NULL;
2238 *ret = obj;
2239 return (ISC_R_SUCCESS);
2241 cleanup:
2242 if (obj != NULL)
2243 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
2244 return (result);
2247 static void
2248 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
2249 CLEANUP_OBJ(obj->value.map.id);
2250 isc_symtab_destroy(&obj->value.map.symtab);
2253 isc_boolean_t
2254 cfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type) {
2255 return (ISC_TF(obj->type == type));
2259 * Destroy 'obj', a configuration object created in 'pctx'.
2261 void
2262 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
2263 cfg_obj_t *obj = *objp;
2264 obj->type->rep->free(pctx, obj);
2265 isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
2266 *objp = NULL;
2269 static void
2270 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
2271 UNUSED(pctx);
2272 UNUSED(obj);
2275 void
2276 cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
2277 type->doc(pctx, type);
2280 void
2281 cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) {
2282 cfg_print_chars(pctx, "<", 1);
2283 cfg_print_cstr(pctx, type->name);
2284 cfg_print_chars(pctx, ">", 1);
2287 void
2288 cfg_print_grammar(const cfg_type_t *type,
2289 void (*f)(void *closure, const char *text, int textlen),
2290 void *closure)
2292 cfg_printer_t pctx;
2293 pctx.f = f;
2294 pctx.closure = closure;
2295 pctx.indent = 0;
2296 cfg_doc_obj(&pctx, type);