2 * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000-2003 Internet Software Consortium.
5 * Permission to use, copy, modify, and/or 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.127 2007/10/12 04:17:18 each Exp $ */
24 #include <isc/buffer.h>
26 #include <isc/formatcheck.h>
31 #include <isc/netaddr.h>
32 #include <isc/print.h>
33 #include <isc/string.h>
34 #include <isc/sockaddr.h>
35 #include <isc/netscope.h>
37 #include <isc/symtab.h>
39 #include <isccfg/cfg.h>
40 #include <isccfg/grammar.h>
41 #include <isccfg/log.h>
44 #define CAT CFG_LOGCATEGORY_CONFIG
45 #define MOD CFG_LOGMODULE_PARSER
47 #define MAP_SYM 1 /* Unique type for isc_symtab */
49 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
51 /* Check a return value. */
54 if (result != ISC_R_SUCCESS) goto cleanup; \
57 /* Clean up a configuration object if non-NULL. */
58 #define CLEANUP_OBJ(obj) \
59 do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
63 * Forward declarations of static functions.
67 free_tuple(cfg_parser_t
*pctx
, cfg_obj_t
*obj
);
70 parse_list(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
);
73 print_list(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
);
76 free_list(cfg_parser_t
*pctx
, cfg_obj_t
*obj
);
79 create_listelt(cfg_parser_t
*pctx
, cfg_listelt_t
**eltp
);
82 create_string(cfg_parser_t
*pctx
, const char *contents
, const cfg_type_t
*type
,
86 free_string(cfg_parser_t
*pctx
, cfg_obj_t
*obj
);
89 create_map(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**objp
);
92 free_map(cfg_parser_t
*pctx
, cfg_obj_t
*obj
);
95 parse_symtab_elt(cfg_parser_t
*pctx
, const char *name
,
96 cfg_type_t
*elttype
, isc_symtab_t
*symtab
,
97 isc_boolean_t callback
);
100 free_noop(cfg_parser_t
*pctx
, cfg_obj_t
*obj
);
103 cfg_getstringtoken(cfg_parser_t
*pctx
);
106 parser_complain(cfg_parser_t
*pctx
, isc_boolean_t is_warning
,
107 unsigned int flags
, const char *format
, va_list args
);
110 * Data representations. These correspond to members of the
111 * "value" union in struct cfg_obj (except "void", which does
112 * not need a union member).
115 cfg_rep_t cfg_rep_uint32
= { "uint32", free_noop
};
116 cfg_rep_t cfg_rep_uint64
= { "uint64", free_noop
};
117 cfg_rep_t cfg_rep_string
= { "string", free_string
};
118 cfg_rep_t cfg_rep_boolean
= { "boolean", free_noop
};
119 cfg_rep_t cfg_rep_map
= { "map", free_map
};
120 cfg_rep_t cfg_rep_list
= { "list", free_list
};
121 cfg_rep_t cfg_rep_tuple
= { "tuple", free_tuple
};
122 cfg_rep_t cfg_rep_sockaddr
= { "sockaddr", free_noop
};
123 cfg_rep_t cfg_rep_netprefix
= { "netprefix", free_noop
};
124 cfg_rep_t cfg_rep_void
= { "void", free_noop
};
127 * Configuration type definitions.
131 * An implicit list. These are formed by clauses that occur multiple times.
133 static cfg_type_t cfg_type_implicitlist
= {
134 "implicitlist", NULL
, print_list
, NULL
, &cfg_rep_list
, NULL
};
139 cfg_print_obj(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
140 obj
->type
->print(pctx
, obj
);
144 cfg_print_chars(cfg_printer_t
*pctx
, const char *text
, int len
) {
145 pctx
->f(pctx
->closure
, text
, len
);
149 print_open(cfg_printer_t
*pctx
) {
150 cfg_print_chars(pctx
, "{\n", 2);
155 print_indent(cfg_printer_t
*pctx
) {
156 int indent
= pctx
->indent
;
158 cfg_print_chars(pctx
, "\t", 1);
164 print_close(cfg_printer_t
*pctx
) {
167 cfg_print_chars(pctx
, "}", 1);
171 cfg_parse_obj(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
173 INSIST(ret
!= NULL
&& *ret
== NULL
);
174 result
= type
->parse(pctx
, type
, ret
);
175 if (result
!= ISC_R_SUCCESS
)
177 INSIST(*ret
!= NULL
);
178 return (ISC_R_SUCCESS
);
182 cfg_print(const cfg_obj_t
*obj
,
183 void (*f
)(void *closure
, const char *text
, int textlen
),
188 pctx
.closure
= closure
;
190 obj
->type
->print(&pctx
, obj
);
197 cfg_create_tuple(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
199 const cfg_tuplefielddef_t
*fields
= type
->of
;
200 const cfg_tuplefielddef_t
*f
;
201 cfg_obj_t
*obj
= NULL
;
202 unsigned int nfields
= 0;
205 for (f
= fields
; f
->name
!= NULL
; f
++)
208 CHECK(cfg_create_obj(pctx
, type
, &obj
));
209 obj
->value
.tuple
= isc_mem_get(pctx
->mctx
,
210 nfields
* sizeof(cfg_obj_t
*));
211 if (obj
->value
.tuple
== NULL
) {
212 result
= ISC_R_NOMEMORY
;
215 for (f
= fields
, i
= 0; f
->name
!= NULL
; f
++, i
++)
216 obj
->value
.tuple
[i
] = NULL
;
218 return (ISC_R_SUCCESS
);
222 isc_mem_put(pctx
->mctx
, obj
, sizeof(*obj
));
227 cfg_parse_tuple(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
)
230 const cfg_tuplefielddef_t
*fields
= type
->of
;
231 const cfg_tuplefielddef_t
*f
;
232 cfg_obj_t
*obj
= NULL
;
235 CHECK(cfg_create_tuple(pctx
, type
, &obj
));
236 for (f
= fields
, i
= 0; f
->name
!= NULL
; f
++, i
++)
237 CHECK(cfg_parse_obj(pctx
, f
->type
, &obj
->value
.tuple
[i
]));
240 return (ISC_R_SUCCESS
);
248 cfg_print_tuple(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
250 const cfg_tuplefielddef_t
*fields
= obj
->type
->of
;
251 const cfg_tuplefielddef_t
*f
;
252 isc_boolean_t need_space
= ISC_FALSE
;
254 for (f
= fields
, i
= 0; f
->name
!= NULL
; f
++, i
++) {
255 const cfg_obj_t
*fieldobj
= obj
->value
.tuple
[i
];
257 cfg_print_chars(pctx
, " ", 1);
258 cfg_print_obj(pctx
, fieldobj
);
259 need_space
= ISC_TF(fieldobj
->type
->print
!= cfg_print_void
);
264 cfg_doc_tuple(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
265 const cfg_tuplefielddef_t
*fields
= type
->of
;
266 const cfg_tuplefielddef_t
*f
;
267 isc_boolean_t need_space
= ISC_FALSE
;
269 for (f
= fields
; f
->name
!= NULL
; f
++) {
271 cfg_print_chars(pctx
, " ", 1);
272 cfg_doc_obj(pctx
, f
->type
);
273 need_space
= ISC_TF(f
->type
->print
!= cfg_print_void
);
278 free_tuple(cfg_parser_t
*pctx
, cfg_obj_t
*obj
) {
280 const cfg_tuplefielddef_t
*fields
= obj
->type
->of
;
281 const cfg_tuplefielddef_t
*f
;
282 unsigned int nfields
= 0;
284 if (obj
->value
.tuple
== NULL
)
287 for (f
= fields
, i
= 0; f
->name
!= NULL
; f
++, i
++) {
288 CLEANUP_OBJ(obj
->value
.tuple
[i
]);
291 isc_mem_put(pctx
->mctx
, obj
->value
.tuple
,
292 nfields
* sizeof(cfg_obj_t
*));
296 cfg_obj_istuple(const cfg_obj_t
*obj
) {
297 REQUIRE(obj
!= NULL
);
298 return (ISC_TF(obj
->type
->rep
== &cfg_rep_tuple
));
302 cfg_tuple_get(const cfg_obj_t
*tupleobj
, const char* name
) {
304 const cfg_tuplefielddef_t
*fields
;
305 const cfg_tuplefielddef_t
*f
;
307 REQUIRE(tupleobj
!= NULL
&& tupleobj
->type
->rep
== &cfg_rep_tuple
);
309 fields
= tupleobj
->type
->of
;
310 for (f
= fields
, i
= 0; f
->name
!= NULL
; f
++, i
++) {
311 if (strcmp(f
->name
, name
) == 0)
312 return (tupleobj
->value
.tuple
[i
]);
319 cfg_parse_special(cfg_parser_t
*pctx
, int special
) {
321 CHECK(cfg_gettoken(pctx
, 0));
322 if (pctx
->token
.type
== isc_tokentype_special
&&
323 pctx
->token
.value
.as_char
== special
)
324 return (ISC_R_SUCCESS
);
326 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "'%c' expected", special
);
327 return (ISC_R_UNEXPECTEDTOKEN
);
333 * Parse a required semicolon. If it is not there, log
334 * an error and increment the error count but continue
335 * parsing. Since the next token is pushed back,
336 * care must be taken to make sure it is eventually
337 * consumed or an infinite loop may result.
340 parse_semicolon(cfg_parser_t
*pctx
) {
342 CHECK(cfg_gettoken(pctx
, 0));
343 if (pctx
->token
.type
== isc_tokentype_special
&&
344 pctx
->token
.value
.as_char
== ';')
345 return (ISC_R_SUCCESS
);
347 cfg_parser_error(pctx
, CFG_LOG_BEFORE
, "missing ';'");
348 cfg_ungettoken(pctx
);
354 * Parse EOF, logging and returning an error if not there.
357 parse_eof(cfg_parser_t
*pctx
) {
359 CHECK(cfg_gettoken(pctx
, 0));
361 if (pctx
->token
.type
== isc_tokentype_eof
)
362 return (ISC_R_SUCCESS
);
364 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "syntax error");
365 return (ISC_R_UNEXPECTEDTOKEN
);
370 /* A list of files, used internally for pctx->files. */
372 static cfg_type_t cfg_type_filelist
= {
373 "filelist", NULL
, print_list
, NULL
, &cfg_rep_list
,
378 cfg_parser_create(isc_mem_t
*mctx
, isc_log_t
*lctx
, cfg_parser_t
**ret
) {
381 isc_lexspecials_t specials
;
383 REQUIRE(mctx
!= NULL
);
384 REQUIRE(ret
!= NULL
&& *ret
== NULL
);
386 pctx
= isc_mem_get(mctx
, sizeof(*pctx
));
388 return (ISC_R_NOMEMORY
);
393 pctx
->seen_eof
= ISC_FALSE
;
394 pctx
->ungotten
= ISC_FALSE
;
397 pctx
->open_files
= NULL
;
398 pctx
->closed_files
= NULL
;
400 pctx
->callback
= NULL
;
401 pctx
->callbackarg
= NULL
;
402 pctx
->token
.type
= isc_tokentype_unknown
;
404 memset(specials
, 0, sizeof(specials
));
412 CHECK(isc_lex_create(pctx
->mctx
, 1024, &pctx
->lexer
));
414 isc_lex_setspecials(pctx
->lexer
, specials
);
415 isc_lex_setcomments(pctx
->lexer
, (ISC_LEXCOMMENT_C
|
416 ISC_LEXCOMMENT_CPLUSPLUS
|
417 ISC_LEXCOMMENT_SHELL
));
419 CHECK(cfg_create_list(pctx
, &cfg_type_filelist
, &pctx
->open_files
));
420 CHECK(cfg_create_list(pctx
, &cfg_type_filelist
, &pctx
->closed_files
));
423 return (ISC_R_SUCCESS
);
426 if (pctx
->lexer
!= NULL
)
427 isc_lex_destroy(&pctx
->lexer
);
428 CLEANUP_OBJ(pctx
->open_files
);
429 CLEANUP_OBJ(pctx
->closed_files
);
430 isc_mem_put(mctx
, pctx
, sizeof(*pctx
));
435 parser_openfile(cfg_parser_t
*pctx
, const char *filename
) {
437 cfg_listelt_t
*elt
= NULL
;
438 cfg_obj_t
*stringobj
= NULL
;
440 result
= isc_lex_openfile(pctx
->lexer
, filename
);
441 if (result
!= ISC_R_SUCCESS
) {
442 cfg_parser_error(pctx
, 0, "open: %s: %s",
443 filename
, isc_result_totext(result
));
447 CHECK(create_string(pctx
, filename
, &cfg_type_qstring
, &stringobj
));
448 CHECK(create_listelt(pctx
, &elt
));
449 elt
->obj
= stringobj
;
450 ISC_LIST_APPEND(pctx
->open_files
->value
.list
, elt
, link
);
452 return (ISC_R_SUCCESS
);
454 CLEANUP_OBJ(stringobj
);
459 cfg_parser_setcallback(cfg_parser_t
*pctx
,
460 cfg_parsecallback_t callback
,
463 pctx
->callback
= callback
;
464 pctx
->callbackarg
= arg
;
468 * Parse a configuration using a pctx where a lexer has already
469 * been set up with a source.
472 parse2(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
474 cfg_obj_t
*obj
= NULL
;
476 result
= cfg_parse_obj(pctx
, type
, &obj
);
478 if (pctx
->errors
!= 0) {
479 /* Errors have been logged. */
480 if (result
== ISC_R_SUCCESS
)
481 result
= ISC_R_FAILURE
;
485 if (result
!= ISC_R_SUCCESS
) {
486 /* Parsing failed but no errors have been logged. */
487 cfg_parser_error(pctx
, 0, "parsing failed");
491 CHECK(parse_eof(pctx
));
494 return (ISC_R_SUCCESS
);
502 cfg_parse_file(cfg_parser_t
*pctx
, const char *filename
,
503 const cfg_type_t
*type
, cfg_obj_t
**ret
)
507 REQUIRE(filename
!= NULL
);
509 CHECK(parser_openfile(pctx
, filename
));
510 CHECK(parse2(pctx
, type
, ret
));
517 cfg_parse_buffer(cfg_parser_t
*pctx
, isc_buffer_t
*buffer
,
518 const cfg_type_t
*type
, cfg_obj_t
**ret
)
521 REQUIRE(buffer
!= NULL
);
522 CHECK(isc_lex_openbuffer(pctx
->lexer
, buffer
));
523 CHECK(parse2(pctx
, type
, ret
));
529 cfg_parser_destroy(cfg_parser_t
**pctxp
) {
530 cfg_parser_t
*pctx
= *pctxp
;
531 isc_lex_destroy(&pctx
->lexer
);
533 * Cleaning up open_files does not
534 * close the files; that was already done
535 * by closing the lexer.
537 CLEANUP_OBJ(pctx
->open_files
);
538 CLEANUP_OBJ(pctx
->closed_files
);
539 isc_mem_put(pctx
->mctx
, pctx
, sizeof(*pctx
));
547 cfg_parse_void(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
549 return (cfg_create_obj(pctx
, &cfg_type_void
, ret
));
553 cfg_print_void(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
559 cfg_doc_void(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
565 cfg_obj_isvoid(const cfg_obj_t
*obj
) {
566 REQUIRE(obj
!= NULL
);
567 return (ISC_TF(obj
->type
->rep
== &cfg_rep_void
));
570 cfg_type_t cfg_type_void
= {
571 "void", cfg_parse_void
, cfg_print_void
, cfg_doc_void
, &cfg_rep_void
,
579 cfg_parse_uint32(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
581 cfg_obj_t
*obj
= NULL
;
584 CHECK(cfg_gettoken(pctx
, ISC_LEXOPT_NUMBER
| ISC_LEXOPT_CNUMBER
));
585 if (pctx
->token
.type
!= isc_tokentype_number
) {
586 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "expected number");
587 return (ISC_R_UNEXPECTEDTOKEN
);
590 CHECK(cfg_create_obj(pctx
, &cfg_type_uint32
, &obj
));
592 obj
->value
.uint32
= pctx
->token
.value
.as_ulong
;
599 cfg_print_cstr(cfg_printer_t
*pctx
, const char *s
) {
600 cfg_print_chars(pctx
, s
, strlen(s
));
604 cfg_print_rawuint(cfg_printer_t
*pctx
, unsigned int u
) {
606 snprintf(buf
, sizeof(buf
), "%u", u
);
607 cfg_print_cstr(pctx
, buf
);
611 cfg_print_uint32(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
612 cfg_print_rawuint(pctx
, obj
->value
.uint32
);
616 cfg_obj_isuint32(const cfg_obj_t
*obj
) {
617 REQUIRE(obj
!= NULL
);
618 return (ISC_TF(obj
->type
->rep
== &cfg_rep_uint32
));
622 cfg_obj_asuint32(const cfg_obj_t
*obj
) {
623 REQUIRE(obj
!= NULL
&& obj
->type
->rep
== &cfg_rep_uint32
);
624 return (obj
->value
.uint32
);
627 cfg_type_t cfg_type_uint32
= {
628 "integer", cfg_parse_uint32
, cfg_print_uint32
, cfg_doc_terminal
,
629 &cfg_rep_uint32
, NULL
637 cfg_obj_isuint64(const cfg_obj_t
*obj
) {
638 REQUIRE(obj
!= NULL
);
639 return (ISC_TF(obj
->type
->rep
== &cfg_rep_uint64
));
643 cfg_obj_asuint64(const cfg_obj_t
*obj
) {
644 REQUIRE(obj
!= NULL
&& obj
->type
->rep
== &cfg_rep_uint64
);
645 return (obj
->value
.uint64
);
649 cfg_print_uint64(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
651 snprintf(buf
, sizeof(buf
), "%" ISC_PRINT_QUADFORMAT
"u",
653 cfg_print_cstr(pctx
, buf
);
656 cfg_type_t cfg_type_uint64
= {
657 "64_bit_integer", NULL
, cfg_print_uint64
, cfg_doc_terminal
,
658 &cfg_rep_uint64
, NULL
662 * qstring (quoted string), ustring (unquoted string), astring
666 /* Create a string object from a null-terminated C string. */
668 create_string(cfg_parser_t
*pctx
, const char *contents
, const cfg_type_t
*type
,
672 cfg_obj_t
*obj
= NULL
;
675 CHECK(cfg_create_obj(pctx
, type
, &obj
));
676 len
= strlen(contents
);
677 obj
->value
.string
.length
= len
;
678 obj
->value
.string
.base
= isc_mem_get(pctx
->mctx
, len
+ 1);
679 if (obj
->value
.string
.base
== 0) {
680 isc_mem_put(pctx
->mctx
, obj
, sizeof(*obj
));
681 return (ISC_R_NOMEMORY
);
683 memcpy(obj
->value
.string
.base
, contents
, len
);
684 obj
->value
.string
.base
[len
] = '\0';
692 cfg_parse_qstring(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
696 CHECK(cfg_gettoken(pctx
, CFG_LEXOPT_QSTRING
));
697 if (pctx
->token
.type
!= isc_tokentype_qstring
) {
698 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "expected quoted string");
699 return (ISC_R_UNEXPECTEDTOKEN
);
701 return (create_string(pctx
,
710 parse_ustring(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
714 CHECK(cfg_gettoken(pctx
, 0));
715 if (pctx
->token
.type
!= isc_tokentype_string
) {
716 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "expected unquoted string");
717 return (ISC_R_UNEXPECTEDTOKEN
);
719 return (create_string(pctx
,
728 cfg_parse_astring(cfg_parser_t
*pctx
, const cfg_type_t
*type
,
734 CHECK(cfg_getstringtoken(pctx
));
735 return (create_string(pctx
,
744 cfg_is_enum(const char *s
, const char *const *enums
) {
745 const char * const *p
;
746 for (p
= enums
; *p
!= NULL
; p
++) {
747 if (strcasecmp(*p
, s
) == 0)
754 check_enum(cfg_parser_t
*pctx
, cfg_obj_t
*obj
, const char *const *enums
) {
755 const char *s
= obj
->value
.string
.base
;
756 if (cfg_is_enum(s
, enums
))
757 return (ISC_R_SUCCESS
);
758 cfg_parser_error(pctx
, 0, "'%s' unexpected", s
);
759 return (ISC_R_UNEXPECTEDTOKEN
);
763 cfg_parse_enum(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
765 cfg_obj_t
*obj
= NULL
;
766 CHECK(parse_ustring(pctx
, NULL
, &obj
));
767 CHECK(check_enum(pctx
, obj
, type
->of
));
769 return (ISC_R_SUCCESS
);
776 cfg_doc_enum(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
777 const char * const *p
;
778 cfg_print_chars(pctx
, "( ", 2);
779 for (p
= type
->of
; *p
!= NULL
; p
++) {
780 cfg_print_cstr(pctx
, *p
);
782 cfg_print_chars(pctx
, " | ", 3);
784 cfg_print_chars(pctx
, " )", 2);
788 cfg_print_ustring(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
789 cfg_print_chars(pctx
, obj
->value
.string
.base
, obj
->value
.string
.length
);
793 print_qstring(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
794 cfg_print_chars(pctx
, "\"", 1);
795 cfg_print_ustring(pctx
, obj
);
796 cfg_print_chars(pctx
, "\"", 1);
800 free_string(cfg_parser_t
*pctx
, cfg_obj_t
*obj
) {
801 isc_mem_put(pctx
->mctx
, obj
->value
.string
.base
,
802 obj
->value
.string
.length
+ 1);
806 cfg_obj_isstring(const cfg_obj_t
*obj
) {
807 REQUIRE(obj
!= NULL
);
808 return (ISC_TF(obj
->type
->rep
== &cfg_rep_string
));
812 cfg_obj_asstring(const cfg_obj_t
*obj
) {
813 REQUIRE(obj
!= NULL
&& obj
->type
->rep
== &cfg_rep_string
);
814 return (obj
->value
.string
.base
);
817 /* Quoted string only */
818 cfg_type_t cfg_type_qstring
= {
819 "quoted_string", cfg_parse_qstring
, print_qstring
, cfg_doc_terminal
,
820 &cfg_rep_string
, NULL
823 /* Unquoted string only */
824 cfg_type_t cfg_type_ustring
= {
825 "string", parse_ustring
, cfg_print_ustring
, cfg_doc_terminal
,
826 &cfg_rep_string
, NULL
829 /* Any string (quoted or unquoted); printed with quotes */
830 cfg_type_t cfg_type_astring
= {
831 "string", cfg_parse_astring
, print_qstring
, cfg_doc_terminal
,
832 &cfg_rep_string
, NULL
840 cfg_obj_isboolean(const cfg_obj_t
*obj
) {
841 REQUIRE(obj
!= NULL
);
842 return (ISC_TF(obj
->type
->rep
== &cfg_rep_boolean
));
846 cfg_obj_asboolean(const cfg_obj_t
*obj
) {
847 REQUIRE(obj
!= NULL
&& obj
->type
->rep
== &cfg_rep_boolean
);
848 return (obj
->value
.boolean
);
852 parse_boolean(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
)
856 cfg_obj_t
*obj
= NULL
;
859 result
= cfg_gettoken(pctx
, 0);
860 if (result
!= ISC_R_SUCCESS
)
863 if (pctx
->token
.type
!= isc_tokentype_string
)
866 if ((strcasecmp(TOKEN_STRING(pctx
), "true") == 0) ||
867 (strcasecmp(TOKEN_STRING(pctx
), "yes") == 0) ||
868 (strcmp(TOKEN_STRING(pctx
), "1") == 0)) {
870 } else if ((strcasecmp(TOKEN_STRING(pctx
), "false") == 0) ||
871 (strcasecmp(TOKEN_STRING(pctx
), "no") == 0) ||
872 (strcmp(TOKEN_STRING(pctx
), "0") == 0)) {
878 CHECK(cfg_create_obj(pctx
, &cfg_type_boolean
, &obj
));
879 obj
->value
.boolean
= value
;
884 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "boolean expected");
885 return (ISC_R_UNEXPECTEDTOKEN
);
892 print_boolean(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
893 if (obj
->value
.boolean
)
894 cfg_print_chars(pctx
, "yes", 3);
896 cfg_print_chars(pctx
, "no", 2);
899 cfg_type_t cfg_type_boolean
= {
900 "boolean", parse_boolean
, print_boolean
, cfg_doc_terminal
,
901 &cfg_rep_boolean
, NULL
909 cfg_create_list(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**obj
) {
911 CHECK(cfg_create_obj(pctx
, type
, obj
));
912 ISC_LIST_INIT((*obj
)->value
.list
);
918 create_listelt(cfg_parser_t
*pctx
, cfg_listelt_t
**eltp
) {
920 elt
= isc_mem_get(pctx
->mctx
, sizeof(*elt
));
922 return (ISC_R_NOMEMORY
);
924 ISC_LINK_INIT(elt
, link
);
926 return (ISC_R_SUCCESS
);
930 free_list_elt(cfg_parser_t
*pctx
, cfg_listelt_t
*elt
) {
931 cfg_obj_destroy(pctx
, &elt
->obj
);
932 isc_mem_put(pctx
->mctx
, elt
, sizeof(*elt
));
936 free_list(cfg_parser_t
*pctx
, cfg_obj_t
*obj
) {
937 cfg_listelt_t
*elt
, *next
;
938 for (elt
= ISC_LIST_HEAD(obj
->value
.list
);
942 next
= ISC_LIST_NEXT(elt
, link
);
943 free_list_elt(pctx
, elt
);
948 cfg_parse_listelt(cfg_parser_t
*pctx
, const cfg_type_t
*elttype
,
952 cfg_listelt_t
*elt
= NULL
;
953 cfg_obj_t
*value
= NULL
;
955 CHECK(create_listelt(pctx
, &elt
));
957 result
= cfg_parse_obj(pctx
, elttype
, &value
);
958 if (result
!= ISC_R_SUCCESS
)
964 return (ISC_R_SUCCESS
);
967 isc_mem_put(pctx
->mctx
, elt
, sizeof(*elt
));
972 * Parse a homogeneous list whose elements are of type 'elttype'
973 * and where each element is terminated by a semicolon.
976 parse_list(cfg_parser_t
*pctx
, const cfg_type_t
*listtype
, cfg_obj_t
**ret
)
978 cfg_obj_t
*listobj
= NULL
;
979 const cfg_type_t
*listof
= listtype
->of
;
981 cfg_listelt_t
*elt
= NULL
;
983 CHECK(cfg_create_list(pctx
, listtype
, &listobj
));
986 CHECK(cfg_peektoken(pctx
, 0));
987 if (pctx
->token
.type
== isc_tokentype_special
&&
988 pctx
->token
.value
.as_char
== /*{*/ '}')
990 CHECK(cfg_parse_listelt(pctx
, listof
, &elt
));
991 CHECK(parse_semicolon(pctx
));
992 ISC_LIST_APPEND(listobj
->value
.list
, elt
, link
);
996 return (ISC_R_SUCCESS
);
1000 free_list_elt(pctx
, elt
);
1001 CLEANUP_OBJ(listobj
);
1006 print_list(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
1007 const cfg_list_t
*list
= &obj
->value
.list
;
1008 const cfg_listelt_t
*elt
;
1010 for (elt
= ISC_LIST_HEAD(*list
);
1012 elt
= ISC_LIST_NEXT(elt
, link
)) {
1014 cfg_print_obj(pctx
, elt
->obj
);
1015 cfg_print_chars(pctx
, ";\n", 2);
1020 cfg_parse_bracketed_list(cfg_parser_t
*pctx
, const cfg_type_t
*type
,
1023 isc_result_t result
;
1024 CHECK(cfg_parse_special(pctx
, '{'));
1025 CHECK(parse_list(pctx
, type
, ret
));
1026 CHECK(cfg_parse_special(pctx
, '}'));
1032 cfg_print_bracketed_list(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
1034 print_list(pctx
, obj
);
1039 cfg_doc_bracketed_list(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
1040 cfg_print_chars(pctx
, "{ ", 2);
1041 cfg_doc_obj(pctx
, type
->of
);
1042 cfg_print_chars(pctx
, "; ... }", 7);
1046 * Parse a homogeneous list whose elements are of type 'elttype'
1047 * and where elements are separated by space. The list ends
1048 * before the first semicolon.
1051 cfg_parse_spacelist(cfg_parser_t
*pctx
, const cfg_type_t
*listtype
,
1054 cfg_obj_t
*listobj
= NULL
;
1055 const cfg_type_t
*listof
= listtype
->of
;
1056 isc_result_t result
;
1058 CHECK(cfg_create_list(pctx
, listtype
, &listobj
));
1061 cfg_listelt_t
*elt
= NULL
;
1063 CHECK(cfg_peektoken(pctx
, 0));
1064 if (pctx
->token
.type
== isc_tokentype_special
&&
1065 pctx
->token
.value
.as_char
== ';')
1067 CHECK(cfg_parse_listelt(pctx
, listof
, &elt
));
1068 ISC_LIST_APPEND(listobj
->value
.list
, elt
, link
);
1071 return (ISC_R_SUCCESS
);
1074 CLEANUP_OBJ(listobj
);
1079 cfg_print_spacelist(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
1080 const cfg_list_t
*list
= &obj
->value
.list
;
1081 const cfg_listelt_t
*elt
;
1083 for (elt
= ISC_LIST_HEAD(*list
);
1085 elt
= ISC_LIST_NEXT(elt
, link
)) {
1086 cfg_print_obj(pctx
, elt
->obj
);
1087 if (ISC_LIST_NEXT(elt
, link
) != NULL
)
1088 cfg_print_chars(pctx
, " ", 1);
1093 cfg_obj_islist(const cfg_obj_t
*obj
) {
1094 REQUIRE(obj
!= NULL
);
1095 return (ISC_TF(obj
->type
->rep
== &cfg_rep_list
));
1098 const cfg_listelt_t
*
1099 cfg_list_first(const cfg_obj_t
*obj
) {
1100 REQUIRE(obj
== NULL
|| obj
->type
->rep
== &cfg_rep_list
);
1103 return (ISC_LIST_HEAD(obj
->value
.list
));
1106 const cfg_listelt_t
*
1107 cfg_list_next(const cfg_listelt_t
*elt
) {
1108 REQUIRE(elt
!= NULL
);
1109 return (ISC_LIST_NEXT(elt
, link
));
1113 * Return the length of a list object. If obj is NULL or is not
1117 cfg_list_length(const cfg_obj_t
*obj
, isc_boolean_t recurse
) {
1118 const cfg_listelt_t
*elt
;
1119 unsigned int count
= 0;
1121 if (obj
== NULL
|| !cfg_obj_islist(obj
))
1123 for (elt
= cfg_list_first(obj
);
1125 elt
= cfg_list_next(elt
)) {
1126 if (recurse
&& cfg_obj_islist(elt
->obj
)) {
1127 count
+= cfg_list_length(elt
->obj
, recurse
);
1136 cfg_listelt_value(const cfg_listelt_t
*elt
) {
1137 REQUIRE(elt
!= NULL
);
1146 * Parse a map body. That's something like
1148 * "foo 1; bar { glub; }; zap true; zap false;"
1150 * i.e., a sequence of option names followed by values and
1151 * terminated by semicolons. Used for the top level of
1152 * the named.conf syntax, as well as for the body of the
1153 * options, view, zone, and other statements.
1156 cfg_parse_mapbody(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
)
1158 const cfg_clausedef_t
* const *clausesets
= type
->of
;
1159 isc_result_t result
;
1160 const cfg_clausedef_t
* const *clauseset
;
1161 const cfg_clausedef_t
*clause
;
1162 cfg_obj_t
*value
= NULL
;
1163 cfg_obj_t
*obj
= NULL
;
1164 cfg_obj_t
*eltobj
= NULL
;
1165 cfg_obj_t
*includename
= NULL
;
1166 isc_symvalue_t symval
;
1167 cfg_list_t
*list
= NULL
;
1169 CHECK(create_map(pctx
, type
, &obj
));
1171 obj
->value
.map
.clausesets
= clausesets
;
1178 * Parse the option name and see if it is known.
1180 CHECK(cfg_gettoken(pctx
, 0));
1182 if (pctx
->token
.type
!= isc_tokentype_string
) {
1183 cfg_ungettoken(pctx
);
1188 * We accept "include" statements wherever a map body
1191 if (strcasecmp(TOKEN_STRING(pctx
), "include") == 0) {
1193 * Turn the file name into a temporary configuration
1194 * object just so that it is not overwritten by the
1197 CHECK(cfg_parse_obj(pctx
, &cfg_type_qstring
, &includename
));
1198 CHECK(parse_semicolon(pctx
));
1199 CHECK(parser_openfile(pctx
, includename
->
1200 value
.string
.base
));
1201 cfg_obj_destroy(pctx
, &includename
);
1206 for (clauseset
= clausesets
; *clauseset
!= NULL
; clauseset
++) {
1207 for (clause
= *clauseset
;
1208 clause
->name
!= NULL
;
1210 if (strcasecmp(TOKEN_STRING(pctx
),
1216 if (clause
== NULL
|| clause
->name
== NULL
) {
1217 cfg_parser_error(pctx
, CFG_LOG_NOPREP
, "unknown option");
1219 * Try to recover by parsing this option as an unknown
1220 * option and discarding it.
1222 CHECK(cfg_parse_obj(pctx
, &cfg_type_unsupported
, &eltobj
));
1223 cfg_obj_destroy(pctx
, &eltobj
);
1224 CHECK(parse_semicolon(pctx
));
1228 /* Clause is known. */
1230 /* Issue warnings if appropriate */
1231 if ((clause
->flags
& CFG_CLAUSEFLAG_OBSOLETE
) != 0)
1232 cfg_parser_warning(pctx
, 0, "option '%s' is obsolete",
1234 if ((clause
->flags
& CFG_CLAUSEFLAG_NOTIMP
) != 0)
1235 cfg_parser_warning(pctx
, 0, "option '%s' is "
1236 "not implemented", clause
->name
);
1237 if ((clause
->flags
& CFG_CLAUSEFLAG_NYI
) != 0)
1238 cfg_parser_warning(pctx
, 0, "option '%s' is "
1239 "not implemented", clause
->name
);
1241 * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
1242 * set here - we need to log the *lack* of such an option,
1246 /* See if the clause already has a value; if not create one. */
1247 result
= isc_symtab_lookup(obj
->value
.map
.symtab
,
1248 clause
->name
, 0, &symval
);
1250 if ((clause
->flags
& CFG_CLAUSEFLAG_MULTI
) != 0) {
1251 /* Multivalued clause */
1252 cfg_obj_t
*listobj
= NULL
;
1253 if (result
== ISC_R_NOTFOUND
) {
1254 CHECK(cfg_create_list(pctx
,
1255 &cfg_type_implicitlist
,
1257 symval
.as_pointer
= listobj
;
1258 result
= isc_symtab_define(obj
->value
.
1262 isc_symexists_reject
);
1263 if (result
!= ISC_R_SUCCESS
) {
1264 cfg_parser_error(pctx
, CFG_LOG_NEAR
,
1265 "isc_symtab_define(%s) "
1266 "failed", clause
->name
);
1267 isc_mem_put(pctx
->mctx
, list
,
1268 sizeof(cfg_list_t
));
1272 INSIST(result
== ISC_R_SUCCESS
);
1273 listobj
= symval
.as_pointer
;
1277 CHECK(cfg_parse_listelt(pctx
, clause
->type
, &elt
));
1278 CHECK(parse_semicolon(pctx
));
1280 ISC_LIST_APPEND(listobj
->value
.list
, elt
, link
);
1282 /* Single-valued clause */
1283 if (result
== ISC_R_NOTFOUND
) {
1284 isc_boolean_t callback
=
1285 ISC_TF((clause
->flags
&
1286 CFG_CLAUSEFLAG_CALLBACK
) != 0);
1287 CHECK(parse_symtab_elt(pctx
, clause
->name
,
1289 obj
->value
.map
.symtab
,
1291 CHECK(parse_semicolon(pctx
));
1292 } else if (result
== ISC_R_SUCCESS
) {
1293 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "'%s' redefined",
1295 result
= ISC_R_EXISTS
;
1298 cfg_parser_error(pctx
, CFG_LOG_NEAR
,
1299 "isc_symtab_define() failed");
1307 return (ISC_R_SUCCESS
);
1312 CLEANUP_OBJ(eltobj
);
1313 CLEANUP_OBJ(includename
);
1318 parse_symtab_elt(cfg_parser_t
*pctx
, const char *name
,
1319 cfg_type_t
*elttype
, isc_symtab_t
*symtab
,
1320 isc_boolean_t callback
)
1322 isc_result_t result
;
1323 cfg_obj_t
*obj
= NULL
;
1324 isc_symvalue_t symval
;
1326 CHECK(cfg_parse_obj(pctx
, elttype
, &obj
));
1328 if (callback
&& pctx
->callback
!= NULL
)
1329 CHECK(pctx
->callback(name
, obj
, pctx
->callbackarg
));
1331 symval
.as_pointer
= obj
;
1332 CHECK(isc_symtab_define(symtab
, name
,
1334 isc_symexists_reject
));
1335 return (ISC_R_SUCCESS
);
1343 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
1346 cfg_parse_map(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
1347 isc_result_t result
;
1348 CHECK(cfg_parse_special(pctx
, '{'));
1349 CHECK(cfg_parse_mapbody(pctx
, type
, ret
));
1350 CHECK(cfg_parse_special(pctx
, '}'));
1356 * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
1359 parse_any_named_map(cfg_parser_t
*pctx
, cfg_type_t
*nametype
, const cfg_type_t
*type
,
1362 isc_result_t result
;
1363 cfg_obj_t
*idobj
= NULL
;
1364 cfg_obj_t
*mapobj
= NULL
;
1366 CHECK(cfg_parse_obj(pctx
, nametype
, &idobj
));
1367 CHECK(cfg_parse_map(pctx
, type
, &mapobj
));
1368 mapobj
->value
.map
.id
= idobj
;
1377 * Parse a map identified by a string name. E.g., "name { foo 1; }".
1378 * Used for the "key" and "channel" statements.
1381 cfg_parse_named_map(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
1382 return (parse_any_named_map(pctx
, &cfg_type_astring
, type
, ret
));
1386 * Parse a map identified by a network address.
1387 * Used to be used for the "server" statement.
1390 cfg_parse_addressed_map(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
1391 return (parse_any_named_map(pctx
, &cfg_type_netaddr
, type
, ret
));
1395 * Parse a map identified by a network prefix.
1396 * Used for the "server" statement.
1399 cfg_parse_netprefix_map(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
1400 return (parse_any_named_map(pctx
, &cfg_type_netprefix
, type
, ret
));
1404 cfg_print_mapbody(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
1405 isc_result_t result
= ISC_R_SUCCESS
;
1407 const cfg_clausedef_t
* const *clauseset
;
1409 for (clauseset
= obj
->value
.map
.clausesets
;
1413 isc_symvalue_t symval
;
1414 const cfg_clausedef_t
*clause
;
1416 for (clause
= *clauseset
;
1417 clause
->name
!= NULL
;
1419 result
= isc_symtab_lookup(obj
->value
.map
.symtab
,
1420 clause
->name
, 0, &symval
);
1421 if (result
== ISC_R_SUCCESS
) {
1422 cfg_obj_t
*obj
= symval
.as_pointer
;
1423 if (obj
->type
== &cfg_type_implicitlist
) {
1425 cfg_list_t
*list
= &obj
->value
.list
;
1427 for (elt
= ISC_LIST_HEAD(*list
);
1429 elt
= ISC_LIST_NEXT(elt
, link
)) {
1431 cfg_print_cstr(pctx
, clause
->name
);
1432 cfg_print_chars(pctx
, " ", 1);
1433 cfg_print_obj(pctx
, elt
->obj
);
1434 cfg_print_chars(pctx
, ";\n", 2);
1437 /* Single-valued. */
1439 cfg_print_cstr(pctx
, clause
->name
);
1440 cfg_print_chars(pctx
, " ", 1);
1441 cfg_print_obj(pctx
, obj
);
1442 cfg_print_chars(pctx
, ";\n", 2);
1444 } else if (result
== ISC_R_NOTFOUND
) {
1454 cfg_doc_mapbody(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
1455 const cfg_clausedef_t
* const *clauseset
;
1456 const cfg_clausedef_t
*clause
;
1458 for (clauseset
= type
->of
; *clauseset
!= NULL
; clauseset
++) {
1459 for (clause
= *clauseset
;
1460 clause
->name
!= NULL
;
1462 cfg_print_cstr(pctx
, clause
->name
);
1463 cfg_print_chars(pctx
, " ", 1);
1464 cfg_doc_obj(pctx
, clause
->type
);
1465 cfg_print_chars(pctx
, ";", 1);
1466 /* XXX print flags here? */
1467 cfg_print_chars(pctx
, "\n\n", 2);
1472 static struct flagtext
{
1476 { CFG_CLAUSEFLAG_NOTIMP
, "not implemented" },
1477 { CFG_CLAUSEFLAG_NYI
, "not yet implemented" },
1478 { CFG_CLAUSEFLAG_OBSOLETE
, "obsolete" },
1479 { CFG_CLAUSEFLAG_NEWDEFAULT
, "default changed" },
1484 cfg_print_map(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
1485 if (obj
->value
.map
.id
!= NULL
) {
1486 cfg_print_obj(pctx
, obj
->value
.map
.id
);
1487 cfg_print_chars(pctx
, " ", 1);
1490 cfg_print_mapbody(pctx
, obj
);
1495 print_clause_flags(cfg_printer_t
*pctx
, unsigned int flags
) {
1497 isc_boolean_t first
= ISC_TRUE
;
1498 for (p
= flagtexts
; p
->flag
!= 0; p
++) {
1499 if ((flags
& p
->flag
) != 0) {
1501 cfg_print_chars(pctx
, " // ", 4);
1503 cfg_print_chars(pctx
, ", ", 2);
1504 cfg_print_cstr(pctx
, p
->text
);
1511 cfg_doc_map(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
1512 const cfg_clausedef_t
* const *clauseset
;
1513 const cfg_clausedef_t
*clause
;
1515 if (type
->parse
== cfg_parse_named_map
) {
1516 cfg_doc_obj(pctx
, &cfg_type_astring
);
1517 cfg_print_chars(pctx
, " ", 1);
1518 } else if (type
->parse
== cfg_parse_addressed_map
) {
1519 cfg_doc_obj(pctx
, &cfg_type_netaddr
);
1520 cfg_print_chars(pctx
, " ", 1);
1521 } else if (type
->parse
== cfg_parse_netprefix_map
) {
1522 cfg_doc_obj(pctx
, &cfg_type_netprefix
);
1523 cfg_print_chars(pctx
, " ", 1);
1528 for (clauseset
= type
->of
; *clauseset
!= NULL
; clauseset
++) {
1529 for (clause
= *clauseset
;
1530 clause
->name
!= NULL
;
1533 cfg_print_cstr(pctx
, clause
->name
);
1534 if (clause
->type
->print
!= cfg_print_void
)
1535 cfg_print_chars(pctx
, " ", 1);
1536 cfg_doc_obj(pctx
, clause
->type
);
1537 cfg_print_chars(pctx
, ";", 1);
1538 print_clause_flags(pctx
, clause
->flags
);
1539 cfg_print_chars(pctx
, "\n", 1);
1546 cfg_obj_ismap(const cfg_obj_t
*obj
) {
1547 REQUIRE(obj
!= NULL
);
1548 return (ISC_TF(obj
->type
->rep
== &cfg_rep_map
));
1552 cfg_map_get(const cfg_obj_t
*mapobj
, const char* name
, const cfg_obj_t
**obj
) {
1553 isc_result_t result
;
1555 const cfg_map_t
*map
;
1557 REQUIRE(mapobj
!= NULL
&& mapobj
->type
->rep
== &cfg_rep_map
);
1558 REQUIRE(name
!= NULL
);
1559 REQUIRE(obj
!= NULL
&& *obj
== NULL
);
1561 map
= &mapobj
->value
.map
;
1563 result
= isc_symtab_lookup(map
->symtab
, name
, MAP_SYM
, &val
);
1564 if (result
!= ISC_R_SUCCESS
)
1566 *obj
= val
.as_pointer
;
1567 return (ISC_R_SUCCESS
);
1571 cfg_map_getname(const cfg_obj_t
*mapobj
) {
1572 REQUIRE(mapobj
!= NULL
&& mapobj
->type
->rep
== &cfg_rep_map
);
1573 return (mapobj
->value
.map
.id
);
1577 /* Parse an arbitrary token, storing its raw text representation. */
1579 parse_token(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
1580 cfg_obj_t
*obj
= NULL
;
1581 isc_result_t result
;
1586 CHECK(cfg_create_obj(pctx
, &cfg_type_token
, &obj
));
1587 CHECK(cfg_gettoken(pctx
, CFG_LEXOPT_QSTRING
));
1588 if (pctx
->token
.type
== isc_tokentype_eof
) {
1589 cfg_ungettoken(pctx
);
1594 isc_lex_getlasttokentext(pctx
->lexer
, &pctx
->token
, &r
);
1596 obj
->value
.string
.base
= isc_mem_get(pctx
->mctx
, r
.length
+ 1);
1597 if (obj
->value
.string
.base
== NULL
) {
1598 result
= ISC_R_NOMEMORY
;
1601 obj
->value
.string
.length
= r
.length
;
1602 memcpy(obj
->value
.string
.base
, r
.base
, r
.length
);
1603 obj
->value
.string
.base
[r
.length
] = '\0';
1609 isc_mem_put(pctx
->mctx
, obj
, sizeof(*obj
));
1613 cfg_type_t cfg_type_token
= {
1614 "token", parse_token
, cfg_print_ustring
, cfg_doc_terminal
,
1615 &cfg_rep_string
, NULL
1619 * An unsupported option. This is just a list of tokens with balanced braces
1620 * ending in a semicolon.
1624 parse_unsupported(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
1625 cfg_obj_t
*listobj
= NULL
;
1626 isc_result_t result
;
1629 CHECK(cfg_create_list(pctx
, type
, &listobj
));
1632 cfg_listelt_t
*elt
= NULL
;
1634 CHECK(cfg_peektoken(pctx
, 0));
1635 if (pctx
->token
.type
== isc_tokentype_special
) {
1636 if (pctx
->token
.value
.as_char
== '{')
1638 else if (pctx
->token
.value
.as_char
== '}')
1640 else if (pctx
->token
.value
.as_char
== ';')
1644 if (pctx
->token
.type
== isc_tokentype_eof
|| braces
< 0) {
1645 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "unexpected token");
1646 result
= ISC_R_UNEXPECTEDTOKEN
;
1650 CHECK(cfg_parse_listelt(pctx
, &cfg_type_token
, &elt
));
1651 ISC_LIST_APPEND(listobj
->value
.list
, elt
, link
);
1653 INSIST(braces
== 0);
1655 return (ISC_R_SUCCESS
);
1658 CLEANUP_OBJ(listobj
);
1662 cfg_type_t cfg_type_unsupported
= {
1663 "unsupported", parse_unsupported
, cfg_print_spacelist
, cfg_doc_terminal
,
1668 * Try interpreting the current token as a network address.
1670 * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
1671 * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set. The
1672 * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
1673 * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
1674 * and the IPv6 wildcard address otherwise.
1677 token_addr(cfg_parser_t
*pctx
, unsigned int flags
, isc_netaddr_t
*na
) {
1679 struct in_addr in4a
;
1680 struct in6_addr in6a
;
1682 if (pctx
->token
.type
!= isc_tokentype_string
)
1683 return (ISC_R_UNEXPECTEDTOKEN
);
1685 s
= TOKEN_STRING(pctx
);
1686 if ((flags
& CFG_ADDR_WILDOK
) != 0 && strcmp(s
, "*") == 0) {
1687 if ((flags
& CFG_ADDR_V4OK
) != 0) {
1688 isc_netaddr_any(na
);
1689 return (ISC_R_SUCCESS
);
1690 } else if ((flags
& CFG_ADDR_V6OK
) != 0) {
1691 isc_netaddr_any6(na
);
1692 return (ISC_R_SUCCESS
);
1697 if ((flags
& (CFG_ADDR_V4OK
| CFG_ADDR_V4PREFIXOK
)) != 0) {
1698 if (inet_pton(AF_INET
, s
, &in4a
) == 1) {
1699 isc_netaddr_fromin(na
, &in4a
);
1700 return (ISC_R_SUCCESS
);
1703 if ((flags
& CFG_ADDR_V4PREFIXOK
) != 0 &&
1709 for (i
= 0; i
< 3; i
++) {
1711 if (inet_pton(AF_INET
, buf
, &in4a
) == 1) {
1712 isc_netaddr_fromin(na
, &in4a
);
1713 return (ISC_R_SUCCESS
);
1717 if ((flags
& CFG_ADDR_V6OK
) != 0 &&
1718 strlen(s
) <= 127U) {
1719 char buf
[128]; /* see lib/bind9/getaddresses.c */
1720 char *d
; /* zone delimiter */
1721 isc_uint32_t zone
= 0; /* scope zone ID */
1724 d
= strchr(buf
, '%');
1728 if (inet_pton(AF_INET6
, buf
, &in6a
) == 1) {
1730 #ifdef ISC_PLATFORM_HAVESCOPEID
1731 isc_result_t result
;
1733 result
= isc_netscope_pton(AF_INET6
,
1737 if (result
!= ISC_R_SUCCESS
)
1740 return (ISC_R_BADADDRESSFORM
);
1744 isc_netaddr_fromin6(na
, &in6a
);
1745 isc_netaddr_setzone(na
, zone
);
1746 return (ISC_R_SUCCESS
);
1750 return (ISC_R_UNEXPECTEDTOKEN
);
1754 cfg_parse_rawaddr(cfg_parser_t
*pctx
, unsigned int flags
, isc_netaddr_t
*na
) {
1755 isc_result_t result
;
1756 const char *wild
= "";
1757 const char *prefix
= "";
1759 CHECK(cfg_gettoken(pctx
, 0));
1760 result
= token_addr(pctx
, flags
, na
);
1761 if (result
== ISC_R_UNEXPECTEDTOKEN
) {
1762 if ((flags
& CFG_ADDR_WILDOK
) != 0)
1764 if ((flags
& CFG_ADDR_V4PREFIXOK
) != 0)
1765 wild
= " or IPv4 prefix";
1766 if ((flags
& CFG_ADDR_MASK
) == CFG_ADDR_V4OK
)
1767 cfg_parser_error(pctx
, CFG_LOG_NEAR
,
1768 "expected IPv4 address%s%s",
1770 else if ((flags
& CFG_ADDR_MASK
) == CFG_ADDR_V6OK
)
1771 cfg_parser_error(pctx
, CFG_LOG_NEAR
,
1772 "expected IPv6 address%s%s",
1775 cfg_parser_error(pctx
, CFG_LOG_NEAR
,
1776 "expected IP address%s%s",
1784 cfg_lookingat_netaddr(cfg_parser_t
*pctx
, unsigned int flags
) {
1785 isc_result_t result
;
1786 isc_netaddr_t na_dummy
;
1787 result
= token_addr(pctx
, flags
, &na_dummy
);
1788 return (ISC_TF(result
== ISC_R_SUCCESS
));
1792 cfg_parse_rawport(cfg_parser_t
*pctx
, unsigned int flags
, in_port_t
*port
) {
1793 isc_result_t result
;
1795 CHECK(cfg_gettoken(pctx
, ISC_LEXOPT_NUMBER
));
1797 if ((flags
& CFG_ADDR_WILDOK
) != 0 &&
1798 pctx
->token
.type
== isc_tokentype_string
&&
1799 strcmp(TOKEN_STRING(pctx
), "*") == 0) {
1801 return (ISC_R_SUCCESS
);
1803 if (pctx
->token
.type
!= isc_tokentype_number
) {
1804 cfg_parser_error(pctx
, CFG_LOG_NEAR
,
1805 "expected port number or '*'");
1806 return (ISC_R_UNEXPECTEDTOKEN
);
1808 if (pctx
->token
.value
.as_ulong
>= 65536U) {
1809 cfg_parser_error(pctx
, CFG_LOG_NEAR
,
1810 "port number out of range");
1811 return (ISC_R_UNEXPECTEDTOKEN
);
1813 *port
= (in_port_t
)(pctx
->token
.value
.as_ulong
);
1814 return (ISC_R_SUCCESS
);
1820 cfg_print_rawaddr(cfg_printer_t
*pctx
, const isc_netaddr_t
*na
) {
1821 isc_result_t result
;
1825 isc_buffer_init(&buf
, text
, sizeof(text
));
1826 result
= isc_netaddr_totext(na
, &buf
);
1827 RUNTIME_CHECK(result
== ISC_R_SUCCESS
);
1828 cfg_print_chars(pctx
, isc_buffer_base(&buf
), isc_buffer_usedlength(&buf
));
1833 static unsigned int netaddr_flags
= CFG_ADDR_V4OK
| CFG_ADDR_V6OK
;
1834 static unsigned int netaddr4_flags
= CFG_ADDR_V4OK
;
1835 static unsigned int netaddr4wild_flags
= CFG_ADDR_V4OK
| CFG_ADDR_WILDOK
;
1836 static unsigned int netaddr6_flags
= CFG_ADDR_V6OK
;
1837 static unsigned int netaddr6wild_flags
= CFG_ADDR_V6OK
| CFG_ADDR_WILDOK
;
1840 parse_netaddr(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
1841 isc_result_t result
;
1842 cfg_obj_t
*obj
= NULL
;
1843 isc_netaddr_t netaddr
;
1844 unsigned int flags
= *(const unsigned int *)type
->of
;
1846 CHECK(cfg_create_obj(pctx
, type
, &obj
));
1847 CHECK(cfg_parse_rawaddr(pctx
, flags
, &netaddr
));
1848 isc_sockaddr_fromnetaddr(&obj
->value
.sockaddr
, &netaddr
, 0);
1850 return (ISC_R_SUCCESS
);
1857 cfg_doc_netaddr(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
1858 const unsigned int *flagp
= type
->of
;
1860 if (*flagp
!= CFG_ADDR_V4OK
&& *flagp
!= CFG_ADDR_V6OK
)
1861 cfg_print_chars(pctx
, "( ", 2);
1862 if (*flagp
& CFG_ADDR_V4OK
) {
1863 cfg_print_cstr(pctx
, "<ipv4_address>");
1866 if (*flagp
& CFG_ADDR_V6OK
) {
1868 cfg_print_chars(pctx
, " | ", 3);
1869 cfg_print_cstr(pctx
, "<ipv6_address>");
1872 if (*flagp
& CFG_ADDR_WILDOK
) {
1874 cfg_print_chars(pctx
, " | ", 3);
1875 cfg_print_chars(pctx
, "*", 1);
1878 if (*flagp
!= CFG_ADDR_V4OK
&& *flagp
!= CFG_ADDR_V6OK
)
1879 cfg_print_chars(pctx
, " )", 2);
1882 cfg_type_t cfg_type_netaddr
= {
1883 "netaddr", parse_netaddr
, cfg_print_sockaddr
, cfg_doc_netaddr
,
1884 &cfg_rep_sockaddr
, &netaddr_flags
1887 cfg_type_t cfg_type_netaddr4
= {
1888 "netaddr4", parse_netaddr
, cfg_print_sockaddr
, cfg_doc_netaddr
,
1889 &cfg_rep_sockaddr
, &netaddr4_flags
1892 cfg_type_t cfg_type_netaddr4wild
= {
1893 "netaddr4wild", parse_netaddr
, cfg_print_sockaddr
, cfg_doc_netaddr
,
1894 &cfg_rep_sockaddr
, &netaddr4wild_flags
1897 cfg_type_t cfg_type_netaddr6
= {
1898 "netaddr6", parse_netaddr
, cfg_print_sockaddr
, cfg_doc_netaddr
,
1899 &cfg_rep_sockaddr
, &netaddr6_flags
1902 cfg_type_t cfg_type_netaddr6wild
= {
1903 "netaddr6wild", parse_netaddr
, cfg_print_sockaddr
, cfg_doc_netaddr
,
1904 &cfg_rep_sockaddr
, &netaddr6wild_flags
1910 cfg_parse_netprefix(cfg_parser_t
*pctx
, const cfg_type_t
*type
,
1913 cfg_obj_t
*obj
= NULL
;
1914 isc_result_t result
;
1915 isc_netaddr_t netaddr
;
1916 unsigned int addrlen
, prefixlen
;
1919 CHECK(cfg_parse_rawaddr(pctx
, CFG_ADDR_V4OK
| CFG_ADDR_V4PREFIXOK
|
1920 CFG_ADDR_V6OK
, &netaddr
));
1921 switch (netaddr
.family
) {
1933 CHECK(cfg_peektoken(pctx
, 0));
1934 if (pctx
->token
.type
== isc_tokentype_special
&&
1935 pctx
->token
.value
.as_char
== '/') {
1936 CHECK(cfg_gettoken(pctx
, 0)); /* read "/" */
1937 CHECK(cfg_gettoken(pctx
, ISC_LEXOPT_NUMBER
));
1938 if (pctx
->token
.type
!= isc_tokentype_number
) {
1939 cfg_parser_error(pctx
, CFG_LOG_NEAR
,
1940 "expected prefix length");
1941 return (ISC_R_UNEXPECTEDTOKEN
);
1943 prefixlen
= pctx
->token
.value
.as_ulong
;
1944 if (prefixlen
> addrlen
) {
1945 cfg_parser_error(pctx
, CFG_LOG_NOPREP
,
1946 "invalid prefix length");
1947 return (ISC_R_RANGE
);
1950 prefixlen
= addrlen
;
1952 CHECK(cfg_create_obj(pctx
, &cfg_type_netprefix
, &obj
));
1953 obj
->value
.netprefix
.address
= netaddr
;
1954 obj
->value
.netprefix
.prefixlen
= prefixlen
;
1956 return (ISC_R_SUCCESS
);
1958 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "expected network prefix");
1963 print_netprefix(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
1964 const cfg_netprefix_t
*p
= &obj
->value
.netprefix
;
1966 cfg_print_rawaddr(pctx
, &p
->address
);
1967 cfg_print_chars(pctx
, "/", 1);
1968 cfg_print_rawuint(pctx
, p
->prefixlen
);
1972 cfg_obj_isnetprefix(const cfg_obj_t
*obj
) {
1973 REQUIRE(obj
!= NULL
);
1974 return (ISC_TF(obj
->type
->rep
== &cfg_rep_netprefix
));
1978 cfg_obj_asnetprefix(const cfg_obj_t
*obj
, isc_netaddr_t
*netaddr
,
1979 unsigned int *prefixlen
) {
1980 REQUIRE(obj
!= NULL
&& obj
->type
->rep
== &cfg_rep_netprefix
);
1981 *netaddr
= obj
->value
.netprefix
.address
;
1982 *prefixlen
= obj
->value
.netprefix
.prefixlen
;
1985 cfg_type_t cfg_type_netprefix
= {
1986 "netprefix", cfg_parse_netprefix
, print_netprefix
, cfg_doc_terminal
,
1987 &cfg_rep_netprefix
, NULL
1991 parse_sockaddrsub(cfg_parser_t
*pctx
, const cfg_type_t
*type
,
1992 int flags
, cfg_obj_t
**ret
)
1994 isc_result_t result
;
1995 isc_netaddr_t netaddr
;
1997 cfg_obj_t
*obj
= NULL
;
1999 CHECK(cfg_create_obj(pctx
, type
, &obj
));
2000 CHECK(cfg_parse_rawaddr(pctx
, flags
, &netaddr
));
2001 CHECK(cfg_peektoken(pctx
, 0));
2002 if (pctx
->token
.type
== isc_tokentype_string
&&
2003 strcasecmp(TOKEN_STRING(pctx
), "port") == 0) {
2004 CHECK(cfg_gettoken(pctx
, 0)); /* read "port" */
2005 CHECK(cfg_parse_rawport(pctx
, flags
, &port
));
2007 isc_sockaddr_fromnetaddr(&obj
->value
.sockaddr
, &netaddr
, port
);
2009 return (ISC_R_SUCCESS
);
2016 static unsigned int sockaddr_flags
= CFG_ADDR_V4OK
| CFG_ADDR_V6OK
;
2017 cfg_type_t cfg_type_sockaddr
= {
2018 "sockaddr", cfg_parse_sockaddr
, cfg_print_sockaddr
, cfg_doc_sockaddr
,
2019 &cfg_rep_sockaddr
, &sockaddr_flags
2023 cfg_parse_sockaddr(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
2024 const unsigned int *flagp
= type
->of
;
2025 return (parse_sockaddrsub(pctx
, &cfg_type_sockaddr
, *flagp
, ret
));
2029 cfg_print_sockaddr(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
2030 isc_netaddr_t netaddr
;
2032 char buf
[ISC_NETADDR_FORMATSIZE
];
2034 isc_netaddr_fromsockaddr(&netaddr
, &obj
->value
.sockaddr
);
2035 isc_netaddr_format(&netaddr
, buf
, sizeof(buf
));
2036 cfg_print_cstr(pctx
, buf
);
2037 port
= isc_sockaddr_getport(&obj
->value
.sockaddr
);
2039 cfg_print_chars(pctx
, " port ", 6);
2040 cfg_print_rawuint(pctx
, port
);
2045 cfg_doc_sockaddr(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
2046 const unsigned int *flagp
= type
->of
;
2048 cfg_print_chars(pctx
, "( ", 2);
2049 if (*flagp
& CFG_ADDR_V4OK
) {
2050 cfg_print_cstr(pctx
, "<ipv4_address>");
2053 if (*flagp
& CFG_ADDR_V6OK
) {
2055 cfg_print_chars(pctx
, " | ", 3);
2056 cfg_print_cstr(pctx
, "<ipv6_address>");
2059 if (*flagp
& CFG_ADDR_WILDOK
) {
2061 cfg_print_chars(pctx
, " | ", 3);
2062 cfg_print_chars(pctx
, "*", 1);
2065 cfg_print_chars(pctx
, " ) ", 3);
2066 if (*flagp
& CFG_ADDR_WILDOK
) {
2067 cfg_print_cstr(pctx
, "[ port ( <integer> | * ) ]");
2069 cfg_print_cstr(pctx
, "[ port <integer> ]");
2074 cfg_obj_issockaddr(const cfg_obj_t
*obj
) {
2075 REQUIRE(obj
!= NULL
);
2076 return (ISC_TF(obj
->type
->rep
== &cfg_rep_sockaddr
));
2079 const isc_sockaddr_t
*
2080 cfg_obj_assockaddr(const cfg_obj_t
*obj
) {
2081 REQUIRE(obj
!= NULL
&& obj
->type
->rep
== &cfg_rep_sockaddr
);
2082 return (&obj
->value
.sockaddr
);
2086 cfg_gettoken(cfg_parser_t
*pctx
, int options
) {
2087 isc_result_t result
;
2090 return (ISC_R_SUCCESS
);
2092 options
|= (ISC_LEXOPT_EOF
| ISC_LEXOPT_NOMORE
);
2095 pctx
->token
.type
= isc_tokentype_unknown
;
2096 result
= isc_lex_gettoken(pctx
->lexer
, options
, &pctx
->token
);
2097 pctx
->ungotten
= ISC_FALSE
;
2098 pctx
->line
= isc_lex_getsourceline(pctx
->lexer
);
2102 if (pctx
->token
.type
== isc_tokentype_eof
) {
2103 result
= isc_lex_close(pctx
->lexer
);
2104 INSIST(result
== ISC_R_NOMORE
||
2105 result
== ISC_R_SUCCESS
);
2107 if (isc_lex_getsourcename(pctx
->lexer
) != NULL
) {
2109 * Closed an included file, not the main file.
2112 elt
= ISC_LIST_TAIL(pctx
->open_files
->
2114 INSIST(elt
!= NULL
);
2115 ISC_LIST_UNLINK(pctx
->open_files
->
2116 value
.list
, elt
, link
);
2117 ISC_LIST_APPEND(pctx
->closed_files
->
2118 value
.list
, elt
, link
);
2121 pctx
->seen_eof
= ISC_TRUE
;
2126 /* More understandable than "ran out of space". */
2127 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "token too big");
2131 cfg_parser_error(pctx
, 0, "%s",
2132 isc_result_totext(result
));
2136 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "%s",
2137 isc_result_totext(result
));
2144 cfg_ungettoken(cfg_parser_t
*pctx
) {
2147 isc_lex_ungettoken(pctx
->lexer
, &pctx
->token
);
2148 pctx
->ungotten
= ISC_TRUE
;
2152 cfg_peektoken(cfg_parser_t
*pctx
, int options
) {
2153 isc_result_t result
;
2154 CHECK(cfg_gettoken(pctx
, options
));
2155 cfg_ungettoken(pctx
);
2161 * Get a string token, accepting both the quoted and the unquoted form.
2162 * Log an error if the next token is not a string.
2165 cfg_getstringtoken(cfg_parser_t
*pctx
) {
2166 isc_result_t result
;
2168 result
= cfg_gettoken(pctx
, CFG_LEXOPT_QSTRING
);
2169 if (result
!= ISC_R_SUCCESS
)
2172 if (pctx
->token
.type
!= isc_tokentype_string
&&
2173 pctx
->token
.type
!= isc_tokentype_qstring
) {
2174 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "expected string");
2175 return (ISC_R_UNEXPECTEDTOKEN
);
2177 return (ISC_R_SUCCESS
);
2181 cfg_parser_error(cfg_parser_t
*pctx
, unsigned int flags
, const char *fmt
, ...) {
2183 va_start(args
, fmt
);
2184 parser_complain(pctx
, ISC_FALSE
, flags
, fmt
, args
);
2190 cfg_parser_warning(cfg_parser_t
*pctx
, unsigned int flags
, const char *fmt
, ...) {
2192 va_start(args
, fmt
);
2193 parser_complain(pctx
, ISC_TRUE
, flags
, fmt
, args
);
2198 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
2201 current_file(cfg_parser_t
*pctx
) {
2202 static char none
[] = "none";
2206 if (pctx
->open_files
== NULL
)
2208 elt
= ISC_LIST_TAIL(pctx
->open_files
->value
.list
);
2213 INSIST(fileobj
->type
== &cfg_type_qstring
);
2214 return (fileobj
->value
.string
.base
);
2218 parser_complain(cfg_parser_t
*pctx
, isc_boolean_t is_warning
,
2219 unsigned int flags
, const char *format
,
2222 char tokenbuf
[MAX_LOG_TOKEN
+ 10];
2223 static char where
[ISC_DIR_PATHMAX
+ 100];
2224 static char message
[2048];
2225 int level
= ISC_LOG_ERROR
;
2226 const char *prep
= "";
2230 level
= ISC_LOG_WARNING
;
2232 snprintf(where
, sizeof(where
), "%s:%u: ",
2233 current_file(pctx
), pctx
->line
);
2235 len
= vsnprintf(message
, sizeof(message
), format
, args
);
2236 if (len
>= sizeof(message
))
2237 FATAL_ERROR(__FILE__
, __LINE__
,
2238 "error message would overflow");
2240 if ((flags
& (CFG_LOG_NEAR
|CFG_LOG_BEFORE
|CFG_LOG_NOPREP
)) != 0) {
2244 (void)cfg_gettoken(pctx
, 0);
2246 if (pctx
->token
.type
== isc_tokentype_eof
) {
2247 snprintf(tokenbuf
, sizeof(tokenbuf
), "end of file");
2248 } else if (pctx
->token
.type
== isc_tokentype_unknown
) {
2252 isc_lex_getlasttokentext(pctx
->lexer
,
2254 if (r
.length
> MAX_LOG_TOKEN
)
2255 snprintf(tokenbuf
, sizeof(tokenbuf
),
2256 "'%.*s...'", MAX_LOG_TOKEN
, r
.base
);
2258 snprintf(tokenbuf
, sizeof(tokenbuf
),
2259 "'%.*s'", (int)r
.length
, r
.base
);
2262 /* Choose a preposition. */
2263 if (flags
& CFG_LOG_NEAR
)
2265 else if (flags
& CFG_LOG_BEFORE
)
2272 isc_log_write(pctx
->lctx
, CAT
, MOD
, level
,
2273 "%s%s%s%s", where
, message
, prep
, tokenbuf
);
2277 cfg_obj_log(const cfg_obj_t
*obj
, isc_log_t
*lctx
, int level
,
2278 const char *fmt
, ...) {
2282 if (! isc_log_wouldlog(lctx
, level
))
2287 vsnprintf(msgbuf
, sizeof(msgbuf
), fmt
, ap
);
2288 isc_log_write(lctx
, CAT
, MOD
, level
,
2290 obj
->file
== NULL
? "<unknown file>" : obj
->file
,
2296 cfg_obj_file(const cfg_obj_t
*obj
) {
2301 cfg_obj_line(const cfg_obj_t
*obj
) {
2306 cfg_create_obj(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
2309 obj
= isc_mem_get(pctx
->mctx
, sizeof(cfg_obj_t
));
2311 return (ISC_R_NOMEMORY
);
2313 obj
->file
= current_file(pctx
);
2314 obj
->line
= pctx
->line
;
2316 return (ISC_R_SUCCESS
);
2320 map_symtabitem_destroy(char *key
, unsigned int type
,
2321 isc_symvalue_t symval
, void *userarg
)
2323 cfg_obj_t
*obj
= symval
.as_pointer
;
2324 cfg_parser_t
*pctx
= (cfg_parser_t
*)userarg
;
2329 cfg_obj_destroy(pctx
, &obj
);
2334 create_map(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
2335 isc_result_t result
;
2336 isc_symtab_t
*symtab
= NULL
;
2337 cfg_obj_t
*obj
= NULL
;
2339 CHECK(cfg_create_obj(pctx
, type
, &obj
));
2340 CHECK(isc_symtab_create(pctx
->mctx
, 5, /* XXX */
2341 map_symtabitem_destroy
,
2342 pctx
, ISC_FALSE
, &symtab
));
2343 obj
->value
.map
.symtab
= symtab
;
2344 obj
->value
.map
.id
= NULL
;
2347 return (ISC_R_SUCCESS
);
2351 isc_mem_put(pctx
->mctx
, obj
, sizeof(*obj
));
2356 free_map(cfg_parser_t
*pctx
, cfg_obj_t
*obj
) {
2357 CLEANUP_OBJ(obj
->value
.map
.id
);
2358 isc_symtab_destroy(&obj
->value
.map
.symtab
);
2362 cfg_obj_istype(const cfg_obj_t
*obj
, const cfg_type_t
*type
) {
2363 return (ISC_TF(obj
->type
== type
));
2367 * Destroy 'obj', a configuration object created in 'pctx'.
2370 cfg_obj_destroy(cfg_parser_t
*pctx
, cfg_obj_t
**objp
) {
2371 cfg_obj_t
*obj
= *objp
;
2372 obj
->type
->rep
->free(pctx
, obj
);
2373 isc_mem_put(pctx
->mctx
, obj
, sizeof(cfg_obj_t
));
2378 free_noop(cfg_parser_t
*pctx
, cfg_obj_t
*obj
) {
2384 cfg_doc_obj(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
2385 type
->doc(pctx
, type
);
2389 cfg_doc_terminal(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
2390 cfg_print_chars(pctx
, "<", 1);
2391 cfg_print_cstr(pctx
, type
->name
);
2392 cfg_print_chars(pctx
, ">", 1);
2396 cfg_print_grammar(const cfg_type_t
*type
,
2397 void (*f
)(void *closure
, const char *text
, int textlen
),
2402 pctx
.closure
= closure
;
2404 cfg_doc_obj(&pctx
, type
);