4 * Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 2000-2003 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
20 /* Id: parser.c,v 1.132 2009/09/02 23:43:54 each Exp */
26 #include <isc/buffer.h>
28 #include <isc/formatcheck.h>
33 #include <isc/netaddr.h>
34 #include <isc/netscope.h>
35 #include <isc/print.h>
36 #include <isc/string.h>
37 #include <isc/sockaddr.h>
38 #include <isc/symtab.h>
41 #include <isccfg/cfg.h>
42 #include <isccfg/grammar.h>
43 #include <isccfg/log.h>
46 #define CAT CFG_LOGCATEGORY_CONFIG
47 #define MOD CFG_LOGMODULE_PARSER
49 #define MAP_SYM 1 /* Unique type for isc_symtab */
51 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
53 /* Check a return value. */
56 if (result != ISC_R_SUCCESS) goto cleanup; \
59 /* Clean up a configuration object if non-NULL. */
60 #define CLEANUP_OBJ(obj) \
61 do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
65 * Forward declarations of static functions.
69 free_tuple(cfg_parser_t
*pctx
, cfg_obj_t
*obj
);
72 parse_list(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
);
75 print_list(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
);
78 free_list(cfg_parser_t
*pctx
, cfg_obj_t
*obj
);
81 create_listelt(cfg_parser_t
*pctx
, cfg_listelt_t
**eltp
);
84 create_string(cfg_parser_t
*pctx
, const char *contents
, const cfg_type_t
*type
,
88 free_string(cfg_parser_t
*pctx
, cfg_obj_t
*obj
);
91 create_map(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**objp
);
94 free_map(cfg_parser_t
*pctx
, cfg_obj_t
*obj
);
97 parse_symtab_elt(cfg_parser_t
*pctx
, const char *name
,
98 cfg_type_t
*elttype
, isc_symtab_t
*symtab
,
99 isc_boolean_t callback
);
102 free_noop(cfg_parser_t
*pctx
, cfg_obj_t
*obj
);
105 cfg_getstringtoken(cfg_parser_t
*pctx
);
108 parser_complain(cfg_parser_t
*pctx
, isc_boolean_t is_warning
,
109 unsigned int flags
, const char *format
, va_list args
);
112 * Data representations. These correspond to members of the
113 * "value" union in struct cfg_obj (except "void", which does
114 * not need a union member).
117 cfg_rep_t cfg_rep_uint32
= { "uint32", free_noop
};
118 cfg_rep_t cfg_rep_uint64
= { "uint64", free_noop
};
119 cfg_rep_t cfg_rep_string
= { "string", free_string
};
120 cfg_rep_t cfg_rep_boolean
= { "boolean", free_noop
};
121 cfg_rep_t cfg_rep_map
= { "map", free_map
};
122 cfg_rep_t cfg_rep_list
= { "list", free_list
};
123 cfg_rep_t cfg_rep_tuple
= { "tuple", free_tuple
};
124 cfg_rep_t cfg_rep_sockaddr
= { "sockaddr", free_noop
};
125 cfg_rep_t cfg_rep_netprefix
= { "netprefix", free_noop
};
126 cfg_rep_t cfg_rep_void
= { "void", free_noop
};
129 * Configuration type definitions.
133 * An implicit list. These are formed by clauses that occur multiple times.
135 static cfg_type_t cfg_type_implicitlist
= {
136 "implicitlist", NULL
, print_list
, NULL
, &cfg_rep_list
, NULL
};
141 cfg_print_obj(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
142 obj
->type
->print(pctx
, obj
);
146 cfg_print_chars(cfg_printer_t
*pctx
, const char *text
, int len
) {
147 pctx
->f(pctx
->closure
, text
, len
);
151 print_open(cfg_printer_t
*pctx
) {
152 cfg_print_chars(pctx
, "{\n", 2);
157 print_indent(cfg_printer_t
*pctx
) {
158 int indent
= pctx
->indent
;
160 cfg_print_chars(pctx
, "\t", 1);
166 print_close(cfg_printer_t
*pctx
) {
169 cfg_print_chars(pctx
, "}", 1);
173 cfg_parse_obj(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
175 INSIST(ret
!= NULL
&& *ret
== NULL
);
176 result
= type
->parse(pctx
, type
, ret
);
177 if (result
!= ISC_R_SUCCESS
)
179 INSIST(*ret
!= NULL
);
180 return (ISC_R_SUCCESS
);
184 cfg_print(const cfg_obj_t
*obj
,
185 void (*f
)(void *closure
, const char *text
, int textlen
),
190 pctx
.closure
= closure
;
192 obj
->type
->print(&pctx
, obj
);
199 cfg_create_tuple(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
201 const cfg_tuplefielddef_t
*fields
= type
->of
;
202 const cfg_tuplefielddef_t
*f
;
203 cfg_obj_t
*obj
= NULL
;
204 unsigned int nfields
= 0;
207 for (f
= fields
; f
->name
!= NULL
; f
++)
210 CHECK(cfg_create_obj(pctx
, type
, &obj
));
211 obj
->value
.tuple
= isc_mem_get(pctx
->mctx
,
212 nfields
* sizeof(cfg_obj_t
*));
213 if (obj
->value
.tuple
== NULL
) {
214 result
= ISC_R_NOMEMORY
;
217 for (f
= fields
, i
= 0; f
->name
!= NULL
; f
++, i
++)
218 obj
->value
.tuple
[i
] = NULL
;
220 return (ISC_R_SUCCESS
);
224 isc_mem_put(pctx
->mctx
, obj
, sizeof(*obj
));
229 cfg_parse_tuple(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
)
232 const cfg_tuplefielddef_t
*fields
= type
->of
;
233 const cfg_tuplefielddef_t
*f
;
234 cfg_obj_t
*obj
= NULL
;
237 CHECK(cfg_create_tuple(pctx
, type
, &obj
));
238 for (f
= fields
, i
= 0; f
->name
!= NULL
; f
++, i
++)
239 CHECK(cfg_parse_obj(pctx
, f
->type
, &obj
->value
.tuple
[i
]));
242 return (ISC_R_SUCCESS
);
250 cfg_print_tuple(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
252 const cfg_tuplefielddef_t
*fields
= obj
->type
->of
;
253 const cfg_tuplefielddef_t
*f
;
254 isc_boolean_t need_space
= ISC_FALSE
;
256 for (f
= fields
, i
= 0; f
->name
!= NULL
; f
++, i
++) {
257 const cfg_obj_t
*fieldobj
= obj
->value
.tuple
[i
];
259 cfg_print_chars(pctx
, " ", 1);
260 cfg_print_obj(pctx
, fieldobj
);
261 need_space
= ISC_TF(fieldobj
->type
->print
!= cfg_print_void
);
266 cfg_doc_tuple(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
267 const cfg_tuplefielddef_t
*fields
= type
->of
;
268 const cfg_tuplefielddef_t
*f
;
269 isc_boolean_t need_space
= ISC_FALSE
;
271 for (f
= fields
; f
->name
!= NULL
; f
++) {
273 cfg_print_chars(pctx
, " ", 1);
274 cfg_doc_obj(pctx
, f
->type
);
275 need_space
= ISC_TF(f
->type
->print
!= cfg_print_void
);
280 free_tuple(cfg_parser_t
*pctx
, cfg_obj_t
*obj
) {
282 const cfg_tuplefielddef_t
*fields
= obj
->type
->of
;
283 const cfg_tuplefielddef_t
*f
;
284 unsigned int nfields
= 0;
286 if (obj
->value
.tuple
== NULL
)
289 for (f
= fields
, i
= 0; f
->name
!= NULL
; f
++, i
++) {
290 CLEANUP_OBJ(obj
->value
.tuple
[i
]);
293 isc_mem_put(pctx
->mctx
, obj
->value
.tuple
,
294 nfields
* sizeof(cfg_obj_t
*));
298 cfg_obj_istuple(const cfg_obj_t
*obj
) {
299 REQUIRE(obj
!= NULL
);
300 return (ISC_TF(obj
->type
->rep
== &cfg_rep_tuple
));
304 cfg_tuple_get(const cfg_obj_t
*tupleobj
, const char* name
) {
306 const cfg_tuplefielddef_t
*fields
;
307 const cfg_tuplefielddef_t
*f
;
309 REQUIRE(tupleobj
!= NULL
&& tupleobj
->type
->rep
== &cfg_rep_tuple
);
311 fields
= tupleobj
->type
->of
;
312 for (f
= fields
, i
= 0; f
->name
!= NULL
; f
++, i
++) {
313 if (strcmp(f
->name
, name
) == 0)
314 return (tupleobj
->value
.tuple
[i
]);
321 cfg_parse_special(cfg_parser_t
*pctx
, int special
) {
323 CHECK(cfg_gettoken(pctx
, 0));
324 if (pctx
->token
.type
== isc_tokentype_special
&&
325 pctx
->token
.value
.as_char
== special
)
326 return (ISC_R_SUCCESS
);
328 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "'%c' expected", special
);
329 return (ISC_R_UNEXPECTEDTOKEN
);
335 * Parse a required semicolon. If it is not there, log
336 * an error and increment the error count but continue
337 * parsing. Since the next token is pushed back,
338 * care must be taken to make sure it is eventually
339 * consumed or an infinite loop may result.
342 parse_semicolon(cfg_parser_t
*pctx
) {
344 CHECK(cfg_gettoken(pctx
, 0));
345 if (pctx
->token
.type
== isc_tokentype_special
&&
346 pctx
->token
.value
.as_char
== ';')
347 return (ISC_R_SUCCESS
);
349 cfg_parser_error(pctx
, CFG_LOG_BEFORE
, "missing ';'");
350 cfg_ungettoken(pctx
);
356 * Parse EOF, logging and returning an error if not there.
359 parse_eof(cfg_parser_t
*pctx
) {
361 CHECK(cfg_gettoken(pctx
, 0));
363 if (pctx
->token
.type
== isc_tokentype_eof
)
364 return (ISC_R_SUCCESS
);
366 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "syntax error");
367 return (ISC_R_UNEXPECTEDTOKEN
);
372 /* A list of files, used internally for pctx->files. */
374 static cfg_type_t cfg_type_filelist
= {
375 "filelist", NULL
, print_list
, NULL
, &cfg_rep_list
,
380 cfg_parser_create(isc_mem_t
*mctx
, isc_log_t
*lctx
, cfg_parser_t
**ret
) {
383 isc_lexspecials_t specials
;
385 REQUIRE(mctx
!= NULL
);
386 REQUIRE(ret
!= NULL
&& *ret
== NULL
);
388 pctx
= isc_mem_get(mctx
, sizeof(*pctx
));
390 return (ISC_R_NOMEMORY
);
395 pctx
->seen_eof
= ISC_FALSE
;
396 pctx
->ungotten
= ISC_FALSE
;
399 pctx
->open_files
= NULL
;
400 pctx
->closed_files
= NULL
;
402 pctx
->callback
= NULL
;
403 pctx
->callbackarg
= NULL
;
404 pctx
->token
.type
= isc_tokentype_unknown
;
407 memset(specials
, 0, sizeof(specials
));
415 CHECK(isc_lex_create(pctx
->mctx
, 1024, &pctx
->lexer
));
417 isc_lex_setspecials(pctx
->lexer
, specials
);
418 isc_lex_setcomments(pctx
->lexer
, (ISC_LEXCOMMENT_C
|
419 ISC_LEXCOMMENT_CPLUSPLUS
|
420 ISC_LEXCOMMENT_SHELL
));
422 CHECK(cfg_create_list(pctx
, &cfg_type_filelist
, &pctx
->open_files
));
423 CHECK(cfg_create_list(pctx
, &cfg_type_filelist
, &pctx
->closed_files
));
426 return (ISC_R_SUCCESS
);
429 if (pctx
->lexer
!= NULL
)
430 isc_lex_destroy(&pctx
->lexer
);
431 CLEANUP_OBJ(pctx
->open_files
);
432 CLEANUP_OBJ(pctx
->closed_files
);
433 isc_mem_put(mctx
, pctx
, sizeof(*pctx
));
438 parser_openfile(cfg_parser_t
*pctx
, const char *filename
) {
440 cfg_listelt_t
*elt
= NULL
;
441 cfg_obj_t
*stringobj
= NULL
;
443 result
= isc_lex_openfile(pctx
->lexer
, filename
);
444 if (result
!= ISC_R_SUCCESS
) {
445 cfg_parser_error(pctx
, 0, "open: %s: %s",
446 filename
, isc_result_totext(result
));
450 CHECK(create_string(pctx
, filename
, &cfg_type_qstring
, &stringobj
));
451 CHECK(create_listelt(pctx
, &elt
));
452 elt
->obj
= stringobj
;
453 ISC_LIST_APPEND(pctx
->open_files
->value
.list
, elt
, link
);
455 return (ISC_R_SUCCESS
);
457 CLEANUP_OBJ(stringobj
);
462 cfg_parser_setcallback(cfg_parser_t
*pctx
,
463 cfg_parsecallback_t callback
,
466 pctx
->callback
= callback
;
467 pctx
->callbackarg
= arg
;
471 * Parse a configuration using a pctx where a lexer has already
472 * been set up with a source.
475 parse2(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
477 cfg_obj_t
*obj
= NULL
;
479 result
= cfg_parse_obj(pctx
, type
, &obj
);
481 if (pctx
->errors
!= 0) {
482 /* Errors have been logged. */
483 if (result
== ISC_R_SUCCESS
)
484 result
= ISC_R_FAILURE
;
488 if (result
!= ISC_R_SUCCESS
) {
489 /* Parsing failed but no errors have been logged. */
490 cfg_parser_error(pctx
, 0, "parsing failed");
494 CHECK(parse_eof(pctx
));
497 return (ISC_R_SUCCESS
);
505 cfg_parse_file(cfg_parser_t
*pctx
, const char *filename
,
506 const cfg_type_t
*type
, cfg_obj_t
**ret
)
510 REQUIRE(filename
!= NULL
);
512 CHECK(parser_openfile(pctx
, filename
));
513 CHECK(parse2(pctx
, type
, ret
));
520 cfg_parse_buffer(cfg_parser_t
*pctx
, isc_buffer_t
*buffer
,
521 const cfg_type_t
*type
, cfg_obj_t
**ret
)
524 REQUIRE(buffer
!= NULL
);
525 CHECK(isc_lex_openbuffer(pctx
->lexer
, buffer
));
526 CHECK(parse2(pctx
, type
, ret
));
532 cfg_parser_destroy(cfg_parser_t
**pctxp
) {
533 cfg_parser_t
*pctx
= *pctxp
;
534 isc_lex_destroy(&pctx
->lexer
);
536 * Cleaning up open_files does not
537 * close the files; that was already done
538 * by closing the lexer.
540 CLEANUP_OBJ(pctx
->open_files
);
541 CLEANUP_OBJ(pctx
->closed_files
);
542 isc_mem_put(pctx
->mctx
, pctx
, sizeof(*pctx
));
550 cfg_parse_void(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
552 return (cfg_create_obj(pctx
, &cfg_type_void
, ret
));
556 cfg_print_void(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
562 cfg_doc_void(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
568 cfg_obj_isvoid(const cfg_obj_t
*obj
) {
569 REQUIRE(obj
!= NULL
);
570 return (ISC_TF(obj
->type
->rep
== &cfg_rep_void
));
573 cfg_type_t cfg_type_void
= {
574 "void", cfg_parse_void
, cfg_print_void
, cfg_doc_void
, &cfg_rep_void
,
582 cfg_parse_uint32(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
584 cfg_obj_t
*obj
= NULL
;
587 CHECK(cfg_gettoken(pctx
, ISC_LEXOPT_NUMBER
| ISC_LEXOPT_CNUMBER
));
588 if (pctx
->token
.type
!= isc_tokentype_number
) {
589 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "expected number");
590 return (ISC_R_UNEXPECTEDTOKEN
);
593 CHECK(cfg_create_obj(pctx
, &cfg_type_uint32
, &obj
));
595 obj
->value
.uint32
= pctx
->token
.value
.as_ulong
;
602 cfg_print_cstr(cfg_printer_t
*pctx
, const char *s
) {
603 cfg_print_chars(pctx
, s
, strlen(s
));
607 cfg_print_rawuint(cfg_printer_t
*pctx
, unsigned int u
) {
609 snprintf(buf
, sizeof(buf
), "%u", u
);
610 cfg_print_cstr(pctx
, buf
);
614 cfg_print_uint32(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
615 cfg_print_rawuint(pctx
, obj
->value
.uint32
);
619 cfg_obj_isuint32(const cfg_obj_t
*obj
) {
620 REQUIRE(obj
!= NULL
);
621 return (ISC_TF(obj
->type
->rep
== &cfg_rep_uint32
));
625 cfg_obj_asuint32(const cfg_obj_t
*obj
) {
626 REQUIRE(obj
!= NULL
&& obj
->type
->rep
== &cfg_rep_uint32
);
627 return (obj
->value
.uint32
);
630 cfg_type_t cfg_type_uint32
= {
631 "integer", cfg_parse_uint32
, cfg_print_uint32
, cfg_doc_terminal
,
632 &cfg_rep_uint32
, NULL
640 cfg_obj_isuint64(const cfg_obj_t
*obj
) {
641 REQUIRE(obj
!= NULL
);
642 return (ISC_TF(obj
->type
->rep
== &cfg_rep_uint64
));
646 cfg_obj_asuint64(const cfg_obj_t
*obj
) {
647 REQUIRE(obj
!= NULL
&& obj
->type
->rep
== &cfg_rep_uint64
);
648 return (obj
->value
.uint64
);
652 cfg_print_uint64(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
654 snprintf(buf
, sizeof(buf
), "%" ISC_PRINT_QUADFORMAT
"u",
656 cfg_print_cstr(pctx
, buf
);
659 cfg_type_t cfg_type_uint64
= {
660 "64_bit_integer", NULL
, cfg_print_uint64
, cfg_doc_terminal
,
661 &cfg_rep_uint64
, NULL
665 * qstring (quoted string), ustring (unquoted string), astring
669 /* Create a string object from a null-terminated C string. */
671 create_string(cfg_parser_t
*pctx
, const char *contents
, const cfg_type_t
*type
,
675 cfg_obj_t
*obj
= NULL
;
678 CHECK(cfg_create_obj(pctx
, type
, &obj
));
679 len
= strlen(contents
);
680 obj
->value
.string
.length
= len
;
681 obj
->value
.string
.base
= isc_mem_get(pctx
->mctx
, len
+ 1);
682 if (obj
->value
.string
.base
== 0) {
683 isc_mem_put(pctx
->mctx
, obj
, sizeof(*obj
));
684 return (ISC_R_NOMEMORY
);
686 memcpy(obj
->value
.string
.base
, contents
, len
);
687 obj
->value
.string
.base
[len
] = '\0';
695 cfg_parse_qstring(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
699 CHECK(cfg_gettoken(pctx
, CFG_LEXOPT_QSTRING
));
700 if (pctx
->token
.type
!= isc_tokentype_qstring
) {
701 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "expected quoted string");
702 return (ISC_R_UNEXPECTEDTOKEN
);
704 return (create_string(pctx
,
713 parse_ustring(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
717 CHECK(cfg_gettoken(pctx
, 0));
718 if (pctx
->token
.type
!= isc_tokentype_string
) {
719 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "expected unquoted string");
720 return (ISC_R_UNEXPECTEDTOKEN
);
722 return (create_string(pctx
,
731 cfg_parse_astring(cfg_parser_t
*pctx
, const cfg_type_t
*type
,
737 CHECK(cfg_getstringtoken(pctx
));
738 return (create_string(pctx
,
747 cfg_is_enum(const char *s
, const char *const *enums
) {
748 const char * const *p
;
749 for (p
= enums
; *p
!= NULL
; p
++) {
750 if (strcasecmp(*p
, s
) == 0)
757 check_enum(cfg_parser_t
*pctx
, cfg_obj_t
*obj
, const char *const *enums
) {
758 const char *s
= obj
->value
.string
.base
;
759 if (cfg_is_enum(s
, enums
))
760 return (ISC_R_SUCCESS
);
761 cfg_parser_error(pctx
, 0, "'%s' unexpected", s
);
762 return (ISC_R_UNEXPECTEDTOKEN
);
766 cfg_parse_enum(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
768 cfg_obj_t
*obj
= NULL
;
769 CHECK(parse_ustring(pctx
, NULL
, &obj
));
770 CHECK(check_enum(pctx
, obj
, type
->of
));
772 return (ISC_R_SUCCESS
);
779 cfg_doc_enum(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
780 const char * const *p
;
781 cfg_print_chars(pctx
, "( ", 2);
782 for (p
= type
->of
; *p
!= NULL
; p
++) {
783 cfg_print_cstr(pctx
, *p
);
785 cfg_print_chars(pctx
, " | ", 3);
787 cfg_print_chars(pctx
, " )", 2);
791 cfg_print_ustring(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
792 cfg_print_chars(pctx
, obj
->value
.string
.base
, obj
->value
.string
.length
);
796 print_qstring(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
797 cfg_print_chars(pctx
, "\"", 1);
798 cfg_print_ustring(pctx
, obj
);
799 cfg_print_chars(pctx
, "\"", 1);
803 free_string(cfg_parser_t
*pctx
, cfg_obj_t
*obj
) {
804 isc_mem_put(pctx
->mctx
, obj
->value
.string
.base
,
805 obj
->value
.string
.length
+ 1);
809 cfg_obj_isstring(const cfg_obj_t
*obj
) {
810 REQUIRE(obj
!= NULL
);
811 return (ISC_TF(obj
->type
->rep
== &cfg_rep_string
));
815 cfg_obj_asstring(const cfg_obj_t
*obj
) {
816 REQUIRE(obj
!= NULL
&& obj
->type
->rep
== &cfg_rep_string
);
817 return (obj
->value
.string
.base
);
820 /* Quoted string only */
821 cfg_type_t cfg_type_qstring
= {
822 "quoted_string", cfg_parse_qstring
, print_qstring
, cfg_doc_terminal
,
823 &cfg_rep_string
, NULL
826 /* Unquoted string only */
827 cfg_type_t cfg_type_ustring
= {
828 "string", parse_ustring
, cfg_print_ustring
, cfg_doc_terminal
,
829 &cfg_rep_string
, NULL
832 /* Any string (quoted or unquoted); printed with quotes */
833 cfg_type_t cfg_type_astring
= {
834 "string", cfg_parse_astring
, print_qstring
, cfg_doc_terminal
,
835 &cfg_rep_string
, NULL
843 cfg_obj_isboolean(const cfg_obj_t
*obj
) {
844 REQUIRE(obj
!= NULL
);
845 return (ISC_TF(obj
->type
->rep
== &cfg_rep_boolean
));
849 cfg_obj_asboolean(const cfg_obj_t
*obj
) {
850 REQUIRE(obj
!= NULL
&& obj
->type
->rep
== &cfg_rep_boolean
);
851 return (obj
->value
.boolean
);
855 parse_boolean(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
)
859 cfg_obj_t
*obj
= NULL
;
862 result
= cfg_gettoken(pctx
, 0);
863 if (result
!= ISC_R_SUCCESS
)
866 if (pctx
->token
.type
!= isc_tokentype_string
)
869 if ((strcasecmp(TOKEN_STRING(pctx
), "true") == 0) ||
870 (strcasecmp(TOKEN_STRING(pctx
), "yes") == 0) ||
871 (strcmp(TOKEN_STRING(pctx
), "1") == 0)) {
873 } else if ((strcasecmp(TOKEN_STRING(pctx
), "false") == 0) ||
874 (strcasecmp(TOKEN_STRING(pctx
), "no") == 0) ||
875 (strcmp(TOKEN_STRING(pctx
), "0") == 0)) {
881 CHECK(cfg_create_obj(pctx
, &cfg_type_boolean
, &obj
));
882 obj
->value
.boolean
= value
;
887 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "boolean expected");
888 return (ISC_R_UNEXPECTEDTOKEN
);
895 print_boolean(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
896 if (obj
->value
.boolean
)
897 cfg_print_chars(pctx
, "yes", 3);
899 cfg_print_chars(pctx
, "no", 2);
902 cfg_type_t cfg_type_boolean
= {
903 "boolean", parse_boolean
, print_boolean
, cfg_doc_terminal
,
904 &cfg_rep_boolean
, NULL
912 cfg_create_list(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**obj
) {
914 CHECK(cfg_create_obj(pctx
, type
, obj
));
915 ISC_LIST_INIT((*obj
)->value
.list
);
921 create_listelt(cfg_parser_t
*pctx
, cfg_listelt_t
**eltp
) {
923 elt
= isc_mem_get(pctx
->mctx
, sizeof(*elt
));
925 return (ISC_R_NOMEMORY
);
927 ISC_LINK_INIT(elt
, link
);
929 return (ISC_R_SUCCESS
);
933 free_list_elt(cfg_parser_t
*pctx
, cfg_listelt_t
*elt
) {
934 cfg_obj_destroy(pctx
, &elt
->obj
);
935 isc_mem_put(pctx
->mctx
, elt
, sizeof(*elt
));
939 free_list(cfg_parser_t
*pctx
, cfg_obj_t
*obj
) {
940 cfg_listelt_t
*elt
, *next
;
941 for (elt
= ISC_LIST_HEAD(obj
->value
.list
);
945 next
= ISC_LIST_NEXT(elt
, link
);
946 free_list_elt(pctx
, elt
);
951 cfg_parse_listelt(cfg_parser_t
*pctx
, const cfg_type_t
*elttype
,
955 cfg_listelt_t
*elt
= NULL
;
956 cfg_obj_t
*value
= NULL
;
958 CHECK(create_listelt(pctx
, &elt
));
960 result
= cfg_parse_obj(pctx
, elttype
, &value
);
961 if (result
!= ISC_R_SUCCESS
)
967 return (ISC_R_SUCCESS
);
970 isc_mem_put(pctx
->mctx
, elt
, sizeof(*elt
));
975 * Parse a homogeneous list whose elements are of type 'elttype'
976 * and where each element is terminated by a semicolon.
979 parse_list(cfg_parser_t
*pctx
, const cfg_type_t
*listtype
, cfg_obj_t
**ret
)
981 cfg_obj_t
*listobj
= NULL
;
982 const cfg_type_t
*listof
= listtype
->of
;
984 cfg_listelt_t
*elt
= NULL
;
986 CHECK(cfg_create_list(pctx
, listtype
, &listobj
));
989 CHECK(cfg_peektoken(pctx
, 0));
990 if (pctx
->token
.type
== isc_tokentype_special
&&
991 pctx
->token
.value
.as_char
== /*{*/ '}')
993 CHECK(cfg_parse_listelt(pctx
, listof
, &elt
));
994 CHECK(parse_semicolon(pctx
));
995 ISC_LIST_APPEND(listobj
->value
.list
, elt
, link
);
999 return (ISC_R_SUCCESS
);
1003 free_list_elt(pctx
, elt
);
1004 CLEANUP_OBJ(listobj
);
1009 print_list(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
1010 const cfg_list_t
*list
= &obj
->value
.list
;
1011 const cfg_listelt_t
*elt
;
1013 for (elt
= ISC_LIST_HEAD(*list
);
1015 elt
= ISC_LIST_NEXT(elt
, link
)) {
1017 cfg_print_obj(pctx
, elt
->obj
);
1018 cfg_print_chars(pctx
, ";\n", 2);
1023 cfg_parse_bracketed_list(cfg_parser_t
*pctx
, const cfg_type_t
*type
,
1026 isc_result_t result
;
1027 CHECK(cfg_parse_special(pctx
, '{'));
1028 CHECK(parse_list(pctx
, type
, ret
));
1029 CHECK(cfg_parse_special(pctx
, '}'));
1035 cfg_print_bracketed_list(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
1037 print_list(pctx
, obj
);
1042 cfg_doc_bracketed_list(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
1043 cfg_print_chars(pctx
, "{ ", 2);
1044 cfg_doc_obj(pctx
, type
->of
);
1045 cfg_print_chars(pctx
, "; ... }", 7);
1049 * Parse a homogeneous list whose elements are of type 'elttype'
1050 * and where elements are separated by space. The list ends
1051 * before the first semicolon.
1054 cfg_parse_spacelist(cfg_parser_t
*pctx
, const cfg_type_t
*listtype
,
1057 cfg_obj_t
*listobj
= NULL
;
1058 const cfg_type_t
*listof
= listtype
->of
;
1059 isc_result_t result
;
1061 CHECK(cfg_create_list(pctx
, listtype
, &listobj
));
1064 cfg_listelt_t
*elt
= NULL
;
1066 CHECK(cfg_peektoken(pctx
, 0));
1067 if (pctx
->token
.type
== isc_tokentype_special
&&
1068 pctx
->token
.value
.as_char
== ';')
1070 CHECK(cfg_parse_listelt(pctx
, listof
, &elt
));
1071 ISC_LIST_APPEND(listobj
->value
.list
, elt
, link
);
1074 return (ISC_R_SUCCESS
);
1077 CLEANUP_OBJ(listobj
);
1082 cfg_print_spacelist(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
1083 const cfg_list_t
*list
= &obj
->value
.list
;
1084 const cfg_listelt_t
*elt
;
1086 for (elt
= ISC_LIST_HEAD(*list
);
1088 elt
= ISC_LIST_NEXT(elt
, link
)) {
1089 cfg_print_obj(pctx
, elt
->obj
);
1090 if (ISC_LIST_NEXT(elt
, link
) != NULL
)
1091 cfg_print_chars(pctx
, " ", 1);
1096 cfg_obj_islist(const cfg_obj_t
*obj
) {
1097 REQUIRE(obj
!= NULL
);
1098 return (ISC_TF(obj
->type
->rep
== &cfg_rep_list
));
1101 const cfg_listelt_t
*
1102 cfg_list_first(const cfg_obj_t
*obj
) {
1103 REQUIRE(obj
== NULL
|| obj
->type
->rep
== &cfg_rep_list
);
1106 return (ISC_LIST_HEAD(obj
->value
.list
));
1109 const cfg_listelt_t
*
1110 cfg_list_next(const cfg_listelt_t
*elt
) {
1111 REQUIRE(elt
!= NULL
);
1112 return (ISC_LIST_NEXT(elt
, link
));
1116 * Return the length of a list object. If obj is NULL or is not
1120 cfg_list_length(const cfg_obj_t
*obj
, isc_boolean_t recurse
) {
1121 const cfg_listelt_t
*elt
;
1122 unsigned int count
= 0;
1124 if (obj
== NULL
|| !cfg_obj_islist(obj
))
1126 for (elt
= cfg_list_first(obj
);
1128 elt
= cfg_list_next(elt
)) {
1129 if (recurse
&& cfg_obj_islist(elt
->obj
)) {
1130 count
+= cfg_list_length(elt
->obj
, recurse
);
1139 cfg_listelt_value(const cfg_listelt_t
*elt
) {
1140 REQUIRE(elt
!= NULL
);
1149 * Parse a map body. That's something like
1151 * "foo 1; bar { glub; }; zap true; zap false;"
1153 * i.e., a sequence of option names followed by values and
1154 * terminated by semicolons. Used for the top level of
1155 * the named.conf syntax, as well as for the body of the
1156 * options, view, zone, and other statements.
1159 cfg_parse_mapbody(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
)
1161 const cfg_clausedef_t
* const *clausesets
= type
->of
;
1162 isc_result_t result
;
1163 const cfg_clausedef_t
* const *clauseset
;
1164 const cfg_clausedef_t
*clause
;
1165 cfg_obj_t
*value
= NULL
;
1166 cfg_obj_t
*obj
= NULL
;
1167 cfg_obj_t
*eltobj
= NULL
;
1168 cfg_obj_t
*includename
= NULL
;
1169 isc_symvalue_t symval
;
1170 cfg_list_t
*list
= NULL
;
1172 CHECK(create_map(pctx
, type
, &obj
));
1174 obj
->value
.map
.clausesets
= clausesets
;
1181 * Parse the option name and see if it is known.
1183 CHECK(cfg_gettoken(pctx
, 0));
1185 if (pctx
->token
.type
!= isc_tokentype_string
) {
1186 cfg_ungettoken(pctx
);
1191 * We accept "include" statements wherever a map body
1194 if (strcasecmp(TOKEN_STRING(pctx
), "include") == 0) {
1196 * Turn the file name into a temporary configuration
1197 * object just so that it is not overwritten by the
1200 CHECK(cfg_parse_obj(pctx
, &cfg_type_qstring
, &includename
));
1201 CHECK(parse_semicolon(pctx
));
1202 CHECK(parser_openfile(pctx
, includename
->
1203 value
.string
.base
));
1204 cfg_obj_destroy(pctx
, &includename
);
1209 for (clauseset
= clausesets
; *clauseset
!= NULL
; clauseset
++) {
1210 for (clause
= *clauseset
;
1211 clause
->name
!= NULL
;
1213 if (strcasecmp(TOKEN_STRING(pctx
),
1219 if (clause
== NULL
|| clause
->name
== NULL
) {
1220 cfg_parser_error(pctx
, CFG_LOG_NOPREP
, "unknown option");
1222 * Try to recover by parsing this option as an unknown
1223 * option and discarding it.
1225 CHECK(cfg_parse_obj(pctx
, &cfg_type_unsupported
, &eltobj
));
1226 cfg_obj_destroy(pctx
, &eltobj
);
1227 CHECK(parse_semicolon(pctx
));
1231 /* Clause is known. */
1233 /* Issue warnings if appropriate */
1234 if ((clause
->flags
& CFG_CLAUSEFLAG_OBSOLETE
) != 0)
1235 cfg_parser_warning(pctx
, 0, "option '%s' is obsolete",
1237 if ((clause
->flags
& CFG_CLAUSEFLAG_NOTIMP
) != 0)
1238 cfg_parser_warning(pctx
, 0, "option '%s' is "
1239 "not implemented", clause
->name
);
1240 if ((clause
->flags
& CFG_CLAUSEFLAG_NYI
) != 0)
1241 cfg_parser_warning(pctx
, 0, "option '%s' is "
1242 "not implemented", clause
->name
);
1244 * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
1245 * set here - we need to log the *lack* of such an option,
1249 /* See if the clause already has a value; if not create one. */
1250 result
= isc_symtab_lookup(obj
->value
.map
.symtab
,
1251 clause
->name
, 0, &symval
);
1253 if ((clause
->flags
& CFG_CLAUSEFLAG_MULTI
) != 0) {
1254 /* Multivalued clause */
1255 cfg_obj_t
*listobj
= NULL
;
1256 if (result
== ISC_R_NOTFOUND
) {
1257 CHECK(cfg_create_list(pctx
,
1258 &cfg_type_implicitlist
,
1260 symval
.as_pointer
= listobj
;
1261 result
= isc_symtab_define(obj
->value
.
1265 isc_symexists_reject
);
1266 if (result
!= ISC_R_SUCCESS
) {
1267 cfg_parser_error(pctx
, CFG_LOG_NEAR
,
1268 "isc_symtab_define(%s) "
1269 "failed", clause
->name
);
1270 isc_mem_put(pctx
->mctx
, list
,
1271 sizeof(cfg_list_t
));
1275 INSIST(result
== ISC_R_SUCCESS
);
1276 listobj
= symval
.as_pointer
;
1280 CHECK(cfg_parse_listelt(pctx
, clause
->type
, &elt
));
1281 CHECK(parse_semicolon(pctx
));
1283 ISC_LIST_APPEND(listobj
->value
.list
, elt
, link
);
1285 /* Single-valued clause */
1286 if (result
== ISC_R_NOTFOUND
) {
1287 isc_boolean_t callback
=
1288 ISC_TF((clause
->flags
&
1289 CFG_CLAUSEFLAG_CALLBACK
) != 0);
1290 CHECK(parse_symtab_elt(pctx
, clause
->name
,
1292 obj
->value
.map
.symtab
,
1294 CHECK(parse_semicolon(pctx
));
1295 } else if (result
== ISC_R_SUCCESS
) {
1296 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "'%s' redefined",
1298 result
= ISC_R_EXISTS
;
1301 cfg_parser_error(pctx
, CFG_LOG_NEAR
,
1302 "isc_symtab_define() failed");
1310 return (ISC_R_SUCCESS
);
1315 CLEANUP_OBJ(eltobj
);
1316 CLEANUP_OBJ(includename
);
1321 parse_symtab_elt(cfg_parser_t
*pctx
, const char *name
,
1322 cfg_type_t
*elttype
, isc_symtab_t
*symtab
,
1323 isc_boolean_t callback
)
1325 isc_result_t result
;
1326 cfg_obj_t
*obj
= NULL
;
1327 isc_symvalue_t symval
;
1329 CHECK(cfg_parse_obj(pctx
, elttype
, &obj
));
1331 if (callback
&& pctx
->callback
!= NULL
)
1332 CHECK(pctx
->callback(name
, obj
, pctx
->callbackarg
));
1334 symval
.as_pointer
= obj
;
1335 CHECK(isc_symtab_define(symtab
, name
,
1337 isc_symexists_reject
));
1338 return (ISC_R_SUCCESS
);
1346 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
1349 cfg_parse_map(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
1350 isc_result_t result
;
1351 CHECK(cfg_parse_special(pctx
, '{'));
1352 CHECK(cfg_parse_mapbody(pctx
, type
, ret
));
1353 CHECK(cfg_parse_special(pctx
, '}'));
1359 * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
1362 parse_any_named_map(cfg_parser_t
*pctx
, cfg_type_t
*nametype
, const cfg_type_t
*type
,
1365 isc_result_t result
;
1366 cfg_obj_t
*idobj
= NULL
;
1367 cfg_obj_t
*mapobj
= NULL
;
1369 CHECK(cfg_parse_obj(pctx
, nametype
, &idobj
));
1370 CHECK(cfg_parse_map(pctx
, type
, &mapobj
));
1371 mapobj
->value
.map
.id
= idobj
;
1380 * Parse a map identified by a string name. E.g., "name { foo 1; }".
1381 * Used for the "key" and "channel" statements.
1384 cfg_parse_named_map(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
1385 return (parse_any_named_map(pctx
, &cfg_type_astring
, type
, ret
));
1389 * Parse a map identified by a network address.
1390 * Used to be used for the "server" statement.
1393 cfg_parse_addressed_map(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
1394 return (parse_any_named_map(pctx
, &cfg_type_netaddr
, type
, ret
));
1398 * Parse a map identified by a network prefix.
1399 * Used for the "server" statement.
1402 cfg_parse_netprefix_map(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
1403 return (parse_any_named_map(pctx
, &cfg_type_netprefix
, type
, ret
));
1407 cfg_print_mapbody(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
1408 isc_result_t result
= ISC_R_SUCCESS
;
1410 const cfg_clausedef_t
* const *clauseset
;
1412 for (clauseset
= obj
->value
.map
.clausesets
;
1416 isc_symvalue_t symval
;
1417 const cfg_clausedef_t
*clause
;
1419 for (clause
= *clauseset
;
1420 clause
->name
!= NULL
;
1422 result
= isc_symtab_lookup(obj
->value
.map
.symtab
,
1423 clause
->name
, 0, &symval
);
1424 if (result
== ISC_R_SUCCESS
) {
1425 cfg_obj_t
*obj
= symval
.as_pointer
;
1426 if (obj
->type
== &cfg_type_implicitlist
) {
1428 cfg_list_t
*list
= &obj
->value
.list
;
1430 for (elt
= ISC_LIST_HEAD(*list
);
1432 elt
= ISC_LIST_NEXT(elt
, link
)) {
1434 cfg_print_cstr(pctx
, clause
->name
);
1435 cfg_print_chars(pctx
, " ", 1);
1436 cfg_print_obj(pctx
, elt
->obj
);
1437 cfg_print_chars(pctx
, ";\n", 2);
1440 /* Single-valued. */
1442 cfg_print_cstr(pctx
, clause
->name
);
1443 cfg_print_chars(pctx
, " ", 1);
1444 cfg_print_obj(pctx
, obj
);
1445 cfg_print_chars(pctx
, ";\n", 2);
1447 } else if (result
== ISC_R_NOTFOUND
) {
1457 cfg_doc_mapbody(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
1458 const cfg_clausedef_t
* const *clauseset
;
1459 const cfg_clausedef_t
*clause
;
1461 for (clauseset
= type
->of
; *clauseset
!= NULL
; clauseset
++) {
1462 for (clause
= *clauseset
;
1463 clause
->name
!= NULL
;
1465 cfg_print_cstr(pctx
, clause
->name
);
1466 cfg_print_chars(pctx
, " ", 1);
1467 cfg_doc_obj(pctx
, clause
->type
);
1468 cfg_print_chars(pctx
, ";", 1);
1469 /* XXX print flags here? */
1470 cfg_print_chars(pctx
, "\n\n", 2);
1475 static struct flagtext
{
1479 { CFG_CLAUSEFLAG_NOTIMP
, "not implemented" },
1480 { CFG_CLAUSEFLAG_NYI
, "not yet implemented" },
1481 { CFG_CLAUSEFLAG_OBSOLETE
, "obsolete" },
1482 { CFG_CLAUSEFLAG_NEWDEFAULT
, "default changed" },
1483 { CFG_CLAUSEFLAG_TESTONLY
, "test only" },
1488 cfg_print_map(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
1489 if (obj
->value
.map
.id
!= NULL
) {
1490 cfg_print_obj(pctx
, obj
->value
.map
.id
);
1491 cfg_print_chars(pctx
, " ", 1);
1494 cfg_print_mapbody(pctx
, obj
);
1499 print_clause_flags(cfg_printer_t
*pctx
, unsigned int flags
) {
1501 isc_boolean_t first
= ISC_TRUE
;
1502 for (p
= flagtexts
; p
->flag
!= 0; p
++) {
1503 if ((flags
& p
->flag
) != 0) {
1505 cfg_print_chars(pctx
, " // ", 4);
1507 cfg_print_chars(pctx
, ", ", 2);
1508 cfg_print_cstr(pctx
, p
->text
);
1515 cfg_doc_map(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
1516 const cfg_clausedef_t
* const *clauseset
;
1517 const cfg_clausedef_t
*clause
;
1519 if (type
->parse
== cfg_parse_named_map
) {
1520 cfg_doc_obj(pctx
, &cfg_type_astring
);
1521 cfg_print_chars(pctx
, " ", 1);
1522 } else if (type
->parse
== cfg_parse_addressed_map
) {
1523 cfg_doc_obj(pctx
, &cfg_type_netaddr
);
1524 cfg_print_chars(pctx
, " ", 1);
1525 } else if (type
->parse
== cfg_parse_netprefix_map
) {
1526 cfg_doc_obj(pctx
, &cfg_type_netprefix
);
1527 cfg_print_chars(pctx
, " ", 1);
1532 for (clauseset
= type
->of
; *clauseset
!= NULL
; clauseset
++) {
1533 for (clause
= *clauseset
;
1534 clause
->name
!= NULL
;
1537 cfg_print_cstr(pctx
, clause
->name
);
1538 if (clause
->type
->print
!= cfg_print_void
)
1539 cfg_print_chars(pctx
, " ", 1);
1540 cfg_doc_obj(pctx
, clause
->type
);
1541 cfg_print_chars(pctx
, ";", 1);
1542 print_clause_flags(pctx
, clause
->flags
);
1543 cfg_print_chars(pctx
, "\n", 1);
1550 cfg_obj_ismap(const cfg_obj_t
*obj
) {
1551 REQUIRE(obj
!= NULL
);
1552 return (ISC_TF(obj
->type
->rep
== &cfg_rep_map
));
1556 cfg_map_get(const cfg_obj_t
*mapobj
, const char* name
, const cfg_obj_t
**obj
) {
1557 isc_result_t result
;
1559 const cfg_map_t
*map
;
1561 REQUIRE(mapobj
!= NULL
&& mapobj
->type
->rep
== &cfg_rep_map
);
1562 REQUIRE(name
!= NULL
);
1563 REQUIRE(obj
!= NULL
&& *obj
== NULL
);
1565 map
= &mapobj
->value
.map
;
1567 result
= isc_symtab_lookup(map
->symtab
, name
, MAP_SYM
, &val
);
1568 if (result
!= ISC_R_SUCCESS
)
1570 *obj
= val
.as_pointer
;
1571 return (ISC_R_SUCCESS
);
1575 cfg_map_getname(const cfg_obj_t
*mapobj
) {
1576 REQUIRE(mapobj
!= NULL
&& mapobj
->type
->rep
== &cfg_rep_map
);
1577 return (mapobj
->value
.map
.id
);
1581 /* Parse an arbitrary token, storing its raw text representation. */
1583 parse_token(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
1584 cfg_obj_t
*obj
= NULL
;
1585 isc_result_t result
;
1590 CHECK(cfg_create_obj(pctx
, &cfg_type_token
, &obj
));
1591 CHECK(cfg_gettoken(pctx
, CFG_LEXOPT_QSTRING
));
1592 if (pctx
->token
.type
== isc_tokentype_eof
) {
1593 cfg_ungettoken(pctx
);
1598 isc_lex_getlasttokentext(pctx
->lexer
, &pctx
->token
, &r
);
1600 obj
->value
.string
.base
= isc_mem_get(pctx
->mctx
, r
.length
+ 1);
1601 if (obj
->value
.string
.base
== NULL
) {
1602 result
= ISC_R_NOMEMORY
;
1605 obj
->value
.string
.length
= r
.length
;
1606 memcpy(obj
->value
.string
.base
, r
.base
, r
.length
);
1607 obj
->value
.string
.base
[r
.length
] = '\0';
1613 isc_mem_put(pctx
->mctx
, obj
, sizeof(*obj
));
1617 cfg_type_t cfg_type_token
= {
1618 "token", parse_token
, cfg_print_ustring
, cfg_doc_terminal
,
1619 &cfg_rep_string
, NULL
1623 * An unsupported option. This is just a list of tokens with balanced braces
1624 * ending in a semicolon.
1628 parse_unsupported(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
1629 cfg_obj_t
*listobj
= NULL
;
1630 isc_result_t result
;
1633 CHECK(cfg_create_list(pctx
, type
, &listobj
));
1636 cfg_listelt_t
*elt
= NULL
;
1638 CHECK(cfg_peektoken(pctx
, 0));
1639 if (pctx
->token
.type
== isc_tokentype_special
) {
1640 if (pctx
->token
.value
.as_char
== '{')
1642 else if (pctx
->token
.value
.as_char
== '}')
1644 else if (pctx
->token
.value
.as_char
== ';')
1648 if (pctx
->token
.type
== isc_tokentype_eof
|| braces
< 0) {
1649 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "unexpected token");
1650 result
= ISC_R_UNEXPECTEDTOKEN
;
1654 CHECK(cfg_parse_listelt(pctx
, &cfg_type_token
, &elt
));
1655 ISC_LIST_APPEND(listobj
->value
.list
, elt
, link
);
1657 INSIST(braces
== 0);
1659 return (ISC_R_SUCCESS
);
1662 CLEANUP_OBJ(listobj
);
1666 cfg_type_t cfg_type_unsupported
= {
1667 "unsupported", parse_unsupported
, cfg_print_spacelist
, cfg_doc_terminal
,
1672 * Try interpreting the current token as a network address.
1674 * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
1675 * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set. The
1676 * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
1677 * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
1678 * and the IPv6 wildcard address otherwise.
1681 token_addr(cfg_parser_t
*pctx
, unsigned int flags
, isc_netaddr_t
*na
) {
1683 struct in_addr in4a
;
1684 struct in6_addr in6a
;
1686 if (pctx
->token
.type
!= isc_tokentype_string
)
1687 return (ISC_R_UNEXPECTEDTOKEN
);
1689 s
= TOKEN_STRING(pctx
);
1690 if ((flags
& CFG_ADDR_WILDOK
) != 0 && strcmp(s
, "*") == 0) {
1691 if ((flags
& CFG_ADDR_V4OK
) != 0) {
1692 isc_netaddr_any(na
);
1693 return (ISC_R_SUCCESS
);
1694 } else if ((flags
& CFG_ADDR_V6OK
) != 0) {
1695 isc_netaddr_any6(na
);
1696 return (ISC_R_SUCCESS
);
1701 if ((flags
& (CFG_ADDR_V4OK
| CFG_ADDR_V4PREFIXOK
)) != 0) {
1702 if (inet_pton(AF_INET
, s
, &in4a
) == 1) {
1703 isc_netaddr_fromin(na
, &in4a
);
1704 return (ISC_R_SUCCESS
);
1707 if ((flags
& CFG_ADDR_V4PREFIXOK
) != 0 &&
1713 for (i
= 0; i
< 3; i
++) {
1715 if (inet_pton(AF_INET
, buf
, &in4a
) == 1) {
1716 isc_netaddr_fromin(na
, &in4a
);
1717 return (ISC_R_SUCCESS
);
1721 if ((flags
& CFG_ADDR_V6OK
) != 0 &&
1722 strlen(s
) <= 127U) {
1723 char buf
[128]; /* see lib/bind9/getaddresses.c */
1724 char *d
; /* zone delimiter */
1725 isc_uint32_t zone
= 0; /* scope zone ID */
1728 d
= strchr(buf
, '%');
1732 if (inet_pton(AF_INET6
, buf
, &in6a
) == 1) {
1734 #ifdef ISC_PLATFORM_HAVESCOPEID
1735 isc_result_t result
;
1737 result
= isc_netscope_pton(AF_INET6
,
1741 if (result
!= ISC_R_SUCCESS
)
1744 return (ISC_R_BADADDRESSFORM
);
1748 isc_netaddr_fromin6(na
, &in6a
);
1749 isc_netaddr_setzone(na
, zone
);
1750 return (ISC_R_SUCCESS
);
1754 return (ISC_R_UNEXPECTEDTOKEN
);
1758 cfg_parse_rawaddr(cfg_parser_t
*pctx
, unsigned int flags
, isc_netaddr_t
*na
) {
1759 isc_result_t result
;
1760 const char *wild
= "";
1761 const char *prefix
= "";
1763 CHECK(cfg_gettoken(pctx
, 0));
1764 result
= token_addr(pctx
, flags
, na
);
1765 if (result
== ISC_R_UNEXPECTEDTOKEN
) {
1766 if ((flags
& CFG_ADDR_WILDOK
) != 0)
1768 if ((flags
& CFG_ADDR_V4PREFIXOK
) != 0)
1769 wild
= " or IPv4 prefix";
1770 if ((flags
& CFG_ADDR_MASK
) == CFG_ADDR_V4OK
)
1771 cfg_parser_error(pctx
, CFG_LOG_NEAR
,
1772 "expected IPv4 address%s%s",
1774 else if ((flags
& CFG_ADDR_MASK
) == CFG_ADDR_V6OK
)
1775 cfg_parser_error(pctx
, CFG_LOG_NEAR
,
1776 "expected IPv6 address%s%s",
1779 cfg_parser_error(pctx
, CFG_LOG_NEAR
,
1780 "expected IP address%s%s",
1788 cfg_lookingat_netaddr(cfg_parser_t
*pctx
, unsigned int flags
) {
1789 isc_result_t result
;
1790 isc_netaddr_t na_dummy
;
1791 result
= token_addr(pctx
, flags
, &na_dummy
);
1792 return (ISC_TF(result
== ISC_R_SUCCESS
));
1796 cfg_parse_rawport(cfg_parser_t
*pctx
, unsigned int flags
, in_port_t
*port
) {
1797 isc_result_t result
;
1799 CHECK(cfg_gettoken(pctx
, ISC_LEXOPT_NUMBER
));
1801 if ((flags
& CFG_ADDR_WILDOK
) != 0 &&
1802 pctx
->token
.type
== isc_tokentype_string
&&
1803 strcmp(TOKEN_STRING(pctx
), "*") == 0) {
1805 return (ISC_R_SUCCESS
);
1807 if (pctx
->token
.type
!= isc_tokentype_number
) {
1808 cfg_parser_error(pctx
, CFG_LOG_NEAR
,
1809 "expected port number or '*'");
1810 return (ISC_R_UNEXPECTEDTOKEN
);
1812 if (pctx
->token
.value
.as_ulong
>= 65536U) {
1813 cfg_parser_error(pctx
, CFG_LOG_NEAR
,
1814 "port number out of range");
1815 return (ISC_R_UNEXPECTEDTOKEN
);
1817 *port
= (in_port_t
)(pctx
->token
.value
.as_ulong
);
1818 return (ISC_R_SUCCESS
);
1824 cfg_print_rawaddr(cfg_printer_t
*pctx
, const isc_netaddr_t
*na
) {
1825 isc_result_t result
;
1829 isc_buffer_init(&buf
, text
, sizeof(text
));
1830 result
= isc_netaddr_totext(na
, &buf
);
1831 RUNTIME_CHECK(result
== ISC_R_SUCCESS
);
1832 cfg_print_chars(pctx
, isc_buffer_base(&buf
), isc_buffer_usedlength(&buf
));
1837 static unsigned int netaddr_flags
= CFG_ADDR_V4OK
| CFG_ADDR_V6OK
;
1838 static unsigned int netaddr4_flags
= CFG_ADDR_V4OK
;
1839 static unsigned int netaddr4wild_flags
= CFG_ADDR_V4OK
| CFG_ADDR_WILDOK
;
1840 static unsigned int netaddr6_flags
= CFG_ADDR_V6OK
;
1841 static unsigned int netaddr6wild_flags
= CFG_ADDR_V6OK
| CFG_ADDR_WILDOK
;
1844 parse_netaddr(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
1845 isc_result_t result
;
1846 cfg_obj_t
*obj
= NULL
;
1847 isc_netaddr_t netaddr
;
1848 unsigned int flags
= *(const unsigned int *)type
->of
;
1850 CHECK(cfg_create_obj(pctx
, type
, &obj
));
1851 CHECK(cfg_parse_rawaddr(pctx
, flags
, &netaddr
));
1852 isc_sockaddr_fromnetaddr(&obj
->value
.sockaddr
, &netaddr
, 0);
1854 return (ISC_R_SUCCESS
);
1861 cfg_doc_netaddr(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
1862 const unsigned int *flagp
= type
->of
;
1864 if (*flagp
!= CFG_ADDR_V4OK
&& *flagp
!= CFG_ADDR_V6OK
)
1865 cfg_print_chars(pctx
, "( ", 2);
1866 if (*flagp
& CFG_ADDR_V4OK
) {
1867 cfg_print_cstr(pctx
, "<ipv4_address>");
1870 if (*flagp
& CFG_ADDR_V6OK
) {
1872 cfg_print_chars(pctx
, " | ", 3);
1873 cfg_print_cstr(pctx
, "<ipv6_address>");
1876 if (*flagp
& CFG_ADDR_WILDOK
) {
1878 cfg_print_chars(pctx
, " | ", 3);
1879 cfg_print_chars(pctx
, "*", 1);
1882 if (*flagp
!= CFG_ADDR_V4OK
&& *flagp
!= CFG_ADDR_V6OK
)
1883 cfg_print_chars(pctx
, " )", 2);
1886 cfg_type_t cfg_type_netaddr
= {
1887 "netaddr", parse_netaddr
, cfg_print_sockaddr
, cfg_doc_netaddr
,
1888 &cfg_rep_sockaddr
, &netaddr_flags
1891 cfg_type_t cfg_type_netaddr4
= {
1892 "netaddr4", parse_netaddr
, cfg_print_sockaddr
, cfg_doc_netaddr
,
1893 &cfg_rep_sockaddr
, &netaddr4_flags
1896 cfg_type_t cfg_type_netaddr4wild
= {
1897 "netaddr4wild", parse_netaddr
, cfg_print_sockaddr
, cfg_doc_netaddr
,
1898 &cfg_rep_sockaddr
, &netaddr4wild_flags
1901 cfg_type_t cfg_type_netaddr6
= {
1902 "netaddr6", parse_netaddr
, cfg_print_sockaddr
, cfg_doc_netaddr
,
1903 &cfg_rep_sockaddr
, &netaddr6_flags
1906 cfg_type_t cfg_type_netaddr6wild
= {
1907 "netaddr6wild", parse_netaddr
, cfg_print_sockaddr
, cfg_doc_netaddr
,
1908 &cfg_rep_sockaddr
, &netaddr6wild_flags
1914 cfg_parse_netprefix(cfg_parser_t
*pctx
, const cfg_type_t
*type
,
1917 cfg_obj_t
*obj
= NULL
;
1918 isc_result_t result
;
1919 isc_netaddr_t netaddr
;
1920 unsigned int addrlen
, prefixlen
;
1923 CHECK(cfg_parse_rawaddr(pctx
, CFG_ADDR_V4OK
| CFG_ADDR_V4PREFIXOK
|
1924 CFG_ADDR_V6OK
, &netaddr
));
1925 switch (netaddr
.family
) {
1937 CHECK(cfg_peektoken(pctx
, 0));
1938 if (pctx
->token
.type
== isc_tokentype_special
&&
1939 pctx
->token
.value
.as_char
== '/') {
1940 CHECK(cfg_gettoken(pctx
, 0)); /* read "/" */
1941 CHECK(cfg_gettoken(pctx
, ISC_LEXOPT_NUMBER
));
1942 if (pctx
->token
.type
!= isc_tokentype_number
) {
1943 cfg_parser_error(pctx
, CFG_LOG_NEAR
,
1944 "expected prefix length");
1945 return (ISC_R_UNEXPECTEDTOKEN
);
1947 prefixlen
= pctx
->token
.value
.as_ulong
;
1948 if (prefixlen
> addrlen
) {
1949 cfg_parser_error(pctx
, CFG_LOG_NOPREP
,
1950 "invalid prefix length");
1951 return (ISC_R_RANGE
);
1954 prefixlen
= addrlen
;
1956 CHECK(cfg_create_obj(pctx
, &cfg_type_netprefix
, &obj
));
1957 obj
->value
.netprefix
.address
= netaddr
;
1958 obj
->value
.netprefix
.prefixlen
= prefixlen
;
1960 return (ISC_R_SUCCESS
);
1962 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "expected network prefix");
1967 print_netprefix(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
1968 const cfg_netprefix_t
*p
= &obj
->value
.netprefix
;
1970 cfg_print_rawaddr(pctx
, &p
->address
);
1971 cfg_print_chars(pctx
, "/", 1);
1972 cfg_print_rawuint(pctx
, p
->prefixlen
);
1976 cfg_obj_isnetprefix(const cfg_obj_t
*obj
) {
1977 REQUIRE(obj
!= NULL
);
1978 return (ISC_TF(obj
->type
->rep
== &cfg_rep_netprefix
));
1982 cfg_obj_asnetprefix(const cfg_obj_t
*obj
, isc_netaddr_t
*netaddr
,
1983 unsigned int *prefixlen
) {
1984 REQUIRE(obj
!= NULL
&& obj
->type
->rep
== &cfg_rep_netprefix
);
1985 *netaddr
= obj
->value
.netprefix
.address
;
1986 *prefixlen
= obj
->value
.netprefix
.prefixlen
;
1989 cfg_type_t cfg_type_netprefix
= {
1990 "netprefix", cfg_parse_netprefix
, print_netprefix
, cfg_doc_terminal
,
1991 &cfg_rep_netprefix
, NULL
1995 parse_sockaddrsub(cfg_parser_t
*pctx
, const cfg_type_t
*type
,
1996 int flags
, cfg_obj_t
**ret
)
1998 isc_result_t result
;
1999 isc_netaddr_t netaddr
;
2001 cfg_obj_t
*obj
= NULL
;
2003 CHECK(cfg_create_obj(pctx
, type
, &obj
));
2004 CHECK(cfg_parse_rawaddr(pctx
, flags
, &netaddr
));
2005 CHECK(cfg_peektoken(pctx
, 0));
2006 if (pctx
->token
.type
== isc_tokentype_string
&&
2007 strcasecmp(TOKEN_STRING(pctx
), "port") == 0) {
2008 CHECK(cfg_gettoken(pctx
, 0)); /* read "port" */
2009 CHECK(cfg_parse_rawport(pctx
, flags
, &port
));
2011 isc_sockaddr_fromnetaddr(&obj
->value
.sockaddr
, &netaddr
, port
);
2013 return (ISC_R_SUCCESS
);
2020 static unsigned int sockaddr_flags
= CFG_ADDR_V4OK
| CFG_ADDR_V6OK
;
2021 cfg_type_t cfg_type_sockaddr
= {
2022 "sockaddr", cfg_parse_sockaddr
, cfg_print_sockaddr
, cfg_doc_sockaddr
,
2023 &cfg_rep_sockaddr
, &sockaddr_flags
2027 cfg_parse_sockaddr(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
2028 const unsigned int *flagp
= type
->of
;
2029 return (parse_sockaddrsub(pctx
, &cfg_type_sockaddr
, *flagp
, ret
));
2033 cfg_print_sockaddr(cfg_printer_t
*pctx
, const cfg_obj_t
*obj
) {
2034 isc_netaddr_t netaddr
;
2036 char buf
[ISC_NETADDR_FORMATSIZE
];
2038 isc_netaddr_fromsockaddr(&netaddr
, &obj
->value
.sockaddr
);
2039 isc_netaddr_format(&netaddr
, buf
, sizeof(buf
));
2040 cfg_print_cstr(pctx
, buf
);
2041 port
= isc_sockaddr_getport(&obj
->value
.sockaddr
);
2043 cfg_print_chars(pctx
, " port ", 6);
2044 cfg_print_rawuint(pctx
, port
);
2049 cfg_doc_sockaddr(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
2050 const unsigned int *flagp
= type
->of
;
2052 cfg_print_chars(pctx
, "( ", 2);
2053 if (*flagp
& CFG_ADDR_V4OK
) {
2054 cfg_print_cstr(pctx
, "<ipv4_address>");
2057 if (*flagp
& CFG_ADDR_V6OK
) {
2059 cfg_print_chars(pctx
, " | ", 3);
2060 cfg_print_cstr(pctx
, "<ipv6_address>");
2063 if (*flagp
& CFG_ADDR_WILDOK
) {
2065 cfg_print_chars(pctx
, " | ", 3);
2066 cfg_print_chars(pctx
, "*", 1);
2069 cfg_print_chars(pctx
, " ) ", 3);
2070 if (*flagp
& CFG_ADDR_WILDOK
) {
2071 cfg_print_cstr(pctx
, "[ port ( <integer> | * ) ]");
2073 cfg_print_cstr(pctx
, "[ port <integer> ]");
2078 cfg_obj_issockaddr(const cfg_obj_t
*obj
) {
2079 REQUIRE(obj
!= NULL
);
2080 return (ISC_TF(obj
->type
->rep
== &cfg_rep_sockaddr
));
2083 const isc_sockaddr_t
*
2084 cfg_obj_assockaddr(const cfg_obj_t
*obj
) {
2085 REQUIRE(obj
!= NULL
&& obj
->type
->rep
== &cfg_rep_sockaddr
);
2086 return (&obj
->value
.sockaddr
);
2090 cfg_gettoken(cfg_parser_t
*pctx
, int options
) {
2091 isc_result_t result
;
2094 return (ISC_R_SUCCESS
);
2096 options
|= (ISC_LEXOPT_EOF
| ISC_LEXOPT_NOMORE
);
2099 pctx
->token
.type
= isc_tokentype_unknown
;
2100 result
= isc_lex_gettoken(pctx
->lexer
, options
, &pctx
->token
);
2101 pctx
->ungotten
= ISC_FALSE
;
2102 pctx
->line
= isc_lex_getsourceline(pctx
->lexer
);
2106 if (pctx
->token
.type
== isc_tokentype_eof
) {
2107 result
= isc_lex_close(pctx
->lexer
);
2108 INSIST(result
== ISC_R_NOMORE
||
2109 result
== ISC_R_SUCCESS
);
2111 if (isc_lex_getsourcename(pctx
->lexer
) != NULL
) {
2113 * Closed an included file, not the main file.
2116 elt
= ISC_LIST_TAIL(pctx
->open_files
->
2118 INSIST(elt
!= NULL
);
2119 ISC_LIST_UNLINK(pctx
->open_files
->
2120 value
.list
, elt
, link
);
2121 ISC_LIST_APPEND(pctx
->closed_files
->
2122 value
.list
, elt
, link
);
2125 pctx
->seen_eof
= ISC_TRUE
;
2130 /* More understandable than "ran out of space". */
2131 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "token too big");
2135 cfg_parser_error(pctx
, 0, "%s",
2136 isc_result_totext(result
));
2140 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "%s",
2141 isc_result_totext(result
));
2148 cfg_ungettoken(cfg_parser_t
*pctx
) {
2151 isc_lex_ungettoken(pctx
->lexer
, &pctx
->token
);
2152 pctx
->ungotten
= ISC_TRUE
;
2156 cfg_peektoken(cfg_parser_t
*pctx
, int options
) {
2157 isc_result_t result
;
2158 CHECK(cfg_gettoken(pctx
, options
));
2159 cfg_ungettoken(pctx
);
2165 * Get a string token, accepting both the quoted and the unquoted form.
2166 * Log an error if the next token is not a string.
2169 cfg_getstringtoken(cfg_parser_t
*pctx
) {
2170 isc_result_t result
;
2172 result
= cfg_gettoken(pctx
, CFG_LEXOPT_QSTRING
);
2173 if (result
!= ISC_R_SUCCESS
)
2176 if (pctx
->token
.type
!= isc_tokentype_string
&&
2177 pctx
->token
.type
!= isc_tokentype_qstring
) {
2178 cfg_parser_error(pctx
, CFG_LOG_NEAR
, "expected string");
2179 return (ISC_R_UNEXPECTEDTOKEN
);
2181 return (ISC_R_SUCCESS
);
2185 cfg_parser_error(cfg_parser_t
*pctx
, unsigned int flags
, const char *fmt
, ...) {
2187 va_start(args
, fmt
);
2188 parser_complain(pctx
, ISC_FALSE
, flags
, fmt
, args
);
2194 cfg_parser_warning(cfg_parser_t
*pctx
, unsigned int flags
, const char *fmt
, ...) {
2196 va_start(args
, fmt
);
2197 parser_complain(pctx
, ISC_TRUE
, flags
, fmt
, args
);
2202 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
2205 current_file(cfg_parser_t
*pctx
) {
2206 static char none
[] = "none";
2210 if (pctx
->open_files
== NULL
)
2212 elt
= ISC_LIST_TAIL(pctx
->open_files
->value
.list
);
2217 INSIST(fileobj
->type
== &cfg_type_qstring
);
2218 return (fileobj
->value
.string
.base
);
2222 parser_complain(cfg_parser_t
*pctx
, isc_boolean_t is_warning
,
2223 unsigned int flags
, const char *format
,
2226 char tokenbuf
[MAX_LOG_TOKEN
+ 10];
2227 static char where
[ISC_DIR_PATHMAX
+ 100];
2228 static char message
[2048];
2229 int level
= ISC_LOG_ERROR
;
2230 const char *prep
= "";
2234 level
= ISC_LOG_WARNING
;
2236 snprintf(where
, sizeof(where
), "%s:%u: ",
2237 current_file(pctx
), pctx
->line
);
2239 len
= vsnprintf(message
, sizeof(message
), format
, args
);
2240 if (len
>= sizeof(message
))
2241 FATAL_ERROR(__FILE__
, __LINE__
,
2242 "error message would overflow");
2244 if ((flags
& (CFG_LOG_NEAR
|CFG_LOG_BEFORE
|CFG_LOG_NOPREP
)) != 0) {
2248 (void)cfg_gettoken(pctx
, 0);
2250 if (pctx
->token
.type
== isc_tokentype_eof
) {
2251 snprintf(tokenbuf
, sizeof(tokenbuf
), "end of file");
2252 } else if (pctx
->token
.type
== isc_tokentype_unknown
) {
2256 isc_lex_getlasttokentext(pctx
->lexer
,
2258 if (r
.length
> MAX_LOG_TOKEN
)
2259 snprintf(tokenbuf
, sizeof(tokenbuf
),
2260 "'%.*s...'", MAX_LOG_TOKEN
, r
.base
);
2262 snprintf(tokenbuf
, sizeof(tokenbuf
),
2263 "'%.*s'", (int)r
.length
, r
.base
);
2266 /* Choose a preposition. */
2267 if (flags
& CFG_LOG_NEAR
)
2269 else if (flags
& CFG_LOG_BEFORE
)
2276 isc_log_write(pctx
->lctx
, CAT
, MOD
, level
,
2277 "%s%s%s%s", where
, message
, prep
, tokenbuf
);
2281 cfg_obj_log(const cfg_obj_t
*obj
, isc_log_t
*lctx
, int level
,
2282 const char *fmt
, ...) {
2286 if (! isc_log_wouldlog(lctx
, level
))
2291 vsnprintf(msgbuf
, sizeof(msgbuf
), fmt
, ap
);
2292 isc_log_write(lctx
, CAT
, MOD
, level
,
2294 obj
->file
== NULL
? "<unknown file>" : obj
->file
,
2300 cfg_obj_file(const cfg_obj_t
*obj
) {
2305 cfg_obj_line(const cfg_obj_t
*obj
) {
2310 cfg_create_obj(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
2313 obj
= isc_mem_get(pctx
->mctx
, sizeof(cfg_obj_t
));
2315 return (ISC_R_NOMEMORY
);
2317 obj
->file
= current_file(pctx
);
2318 obj
->line
= pctx
->line
;
2320 return (ISC_R_SUCCESS
);
2324 map_symtabitem_destroy(char *key
, unsigned int type
,
2325 isc_symvalue_t symval
, void *userarg
)
2327 cfg_obj_t
*obj
= symval
.as_pointer
;
2328 cfg_parser_t
*pctx
= (cfg_parser_t
*)userarg
;
2333 cfg_obj_destroy(pctx
, &obj
);
2338 create_map(cfg_parser_t
*pctx
, const cfg_type_t
*type
, cfg_obj_t
**ret
) {
2339 isc_result_t result
;
2340 isc_symtab_t
*symtab
= NULL
;
2341 cfg_obj_t
*obj
= NULL
;
2343 CHECK(cfg_create_obj(pctx
, type
, &obj
));
2344 CHECK(isc_symtab_create(pctx
->mctx
, 5, /* XXX */
2345 map_symtabitem_destroy
,
2346 pctx
, ISC_FALSE
, &symtab
));
2347 obj
->value
.map
.symtab
= symtab
;
2348 obj
->value
.map
.id
= NULL
;
2351 return (ISC_R_SUCCESS
);
2355 isc_mem_put(pctx
->mctx
, obj
, sizeof(*obj
));
2360 free_map(cfg_parser_t
*pctx
, cfg_obj_t
*obj
) {
2361 CLEANUP_OBJ(obj
->value
.map
.id
);
2362 isc_symtab_destroy(&obj
->value
.map
.symtab
);
2366 cfg_obj_istype(const cfg_obj_t
*obj
, const cfg_type_t
*type
) {
2367 return (ISC_TF(obj
->type
== type
));
2371 * Destroy 'obj', a configuration object created in 'pctx'.
2374 cfg_obj_destroy(cfg_parser_t
*pctx
, cfg_obj_t
**objp
) {
2375 cfg_obj_t
*obj
= *objp
;
2376 obj
->type
->rep
->free(pctx
, obj
);
2377 isc_mem_put(pctx
->mctx
, obj
, sizeof(cfg_obj_t
));
2382 free_noop(cfg_parser_t
*pctx
, cfg_obj_t
*obj
) {
2388 cfg_doc_obj(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
2389 type
->doc(pctx
, type
);
2393 cfg_doc_terminal(cfg_printer_t
*pctx
, const cfg_type_t
*type
) {
2394 cfg_print_chars(pctx
, "<", 1);
2395 cfg_print_cstr(pctx
, type
->name
);
2396 cfg_print_chars(pctx
, ">", 1);
2400 cfg_print_grammar(const cfg_type_t
*type
,
2401 void (*f
)(void *closure
, const char *text
, int textlen
),
2406 pctx
.closure
= closure
;
2408 cfg_doc_obj(&pctx
, type
);