Detect FPU by checking CPUID features.
[dragonfly.git] / contrib / bind-9.5.2 / lib / isccfg / parser.c
blob79c7c48914baa80ff7a612a59de729817f4cd53b
1 /*
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 $ */
20 /*! \file */
22 #include <config.h>
24 #include <isc/buffer.h>
25 #include <isc/dir.h>
26 #include <isc/formatcheck.h>
27 #include <isc/lex.h>
28 #include <isc/log.h>
29 #include <isc/mem.h>
30 #include <isc/net.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>
36 #include <isc/util.h>
37 #include <isc/symtab.h>
39 #include <isccfg/cfg.h>
40 #include <isccfg/grammar.h>
41 #include <isccfg/log.h>
43 /* Shorthand */
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. */
52 #define CHECK(op) \
53 do { result = (op); \
54 if (result != ISC_R_SUCCESS) goto cleanup; \
55 } while (0)
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.
66 static void
67 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
69 static isc_result_t
70 parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
72 static void
73 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
75 static void
76 free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
78 static isc_result_t
79 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
81 static isc_result_t
82 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
83 cfg_obj_t **ret);
85 static void
86 free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
88 static isc_result_t
89 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
91 static void
92 free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
94 static isc_result_t
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);
99 static void
100 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
102 static isc_result_t
103 cfg_getstringtoken(cfg_parser_t *pctx);
105 static void
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 };
136 /* Functions. */
138 void
139 cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
140 obj->type->print(pctx, obj);
143 void
144 cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
145 pctx->f(pctx->closure, text, len);
148 static void
149 print_open(cfg_printer_t *pctx) {
150 cfg_print_chars(pctx, "{\n", 2);
151 pctx->indent++;
154 static void
155 print_indent(cfg_printer_t *pctx) {
156 int indent = pctx->indent;
157 while (indent > 0) {
158 cfg_print_chars(pctx, "\t", 1);
159 indent--;
163 static void
164 print_close(cfg_printer_t *pctx) {
165 pctx->indent--;
166 print_indent(pctx);
167 cfg_print_chars(pctx, "}", 1);
170 isc_result_t
171 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
172 isc_result_t result;
173 INSIST(ret != NULL && *ret == NULL);
174 result = type->parse(pctx, type, ret);
175 if (result != ISC_R_SUCCESS)
176 return (result);
177 INSIST(*ret != NULL);
178 return (ISC_R_SUCCESS);
181 void
182 cfg_print(const cfg_obj_t *obj,
183 void (*f)(void *closure, const char *text, int textlen),
184 void *closure)
186 cfg_printer_t pctx;
187 pctx.f = f;
188 pctx.closure = closure;
189 pctx.indent = 0;
190 obj->type->print(&pctx, obj);
194 /* Tuples. */
196 isc_result_t
197 cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
198 isc_result_t result;
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;
203 int i;
205 for (f = fields; f->name != NULL; f++)
206 nfields++;
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;
213 goto cleanup;
215 for (f = fields, i = 0; f->name != NULL; f++, i++)
216 obj->value.tuple[i] = NULL;
217 *ret = obj;
218 return (ISC_R_SUCCESS);
220 cleanup:
221 if (obj != NULL)
222 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
223 return (result);
226 isc_result_t
227 cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
229 isc_result_t result;
230 const cfg_tuplefielddef_t *fields = type->of;
231 const cfg_tuplefielddef_t *f;
232 cfg_obj_t *obj = NULL;
233 unsigned int i;
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]));
239 *ret = obj;
240 return (ISC_R_SUCCESS);
242 cleanup:
243 CLEANUP_OBJ(obj);
244 return (result);
247 void
248 cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
249 unsigned int i;
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];
256 if (need_space)
257 cfg_print_chars(pctx, " ", 1);
258 cfg_print_obj(pctx, fieldobj);
259 need_space = ISC_TF(fieldobj->type->print != cfg_print_void);
263 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++) {
270 if (need_space)
271 cfg_print_chars(pctx, " ", 1);
272 cfg_doc_obj(pctx, f->type);
273 need_space = ISC_TF(f->type->print != cfg_print_void);
277 static void
278 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
279 unsigned int i;
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)
285 return;
287 for (f = fields, i = 0; f->name != NULL; f++, i++) {
288 CLEANUP_OBJ(obj->value.tuple[i]);
289 nfields++;
291 isc_mem_put(pctx->mctx, obj->value.tuple,
292 nfields * sizeof(cfg_obj_t *));
295 isc_boolean_t
296 cfg_obj_istuple(const cfg_obj_t *obj) {
297 REQUIRE(obj != NULL);
298 return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
301 const cfg_obj_t *
302 cfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) {
303 unsigned int i;
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]);
314 INSIST(0);
315 return (NULL);
318 isc_result_t
319 cfg_parse_special(cfg_parser_t *pctx, int special) {
320 isc_result_t result;
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);
328 cleanup:
329 return (result);
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.
339 static isc_result_t
340 parse_semicolon(cfg_parser_t *pctx) {
341 isc_result_t result;
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);
349 cleanup:
350 return (result);
354 * Parse EOF, logging and returning an error if not there.
356 static isc_result_t
357 parse_eof(cfg_parser_t *pctx) {
358 isc_result_t result;
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);
366 cleanup:
367 return (result);
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,
374 &cfg_type_qstring
377 isc_result_t
378 cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
379 isc_result_t result;
380 cfg_parser_t *pctx;
381 isc_lexspecials_t specials;
383 REQUIRE(mctx != NULL);
384 REQUIRE(ret != NULL && *ret == NULL);
386 pctx = isc_mem_get(mctx, sizeof(*pctx));
387 if (pctx == NULL)
388 return (ISC_R_NOMEMORY);
390 pctx->mctx = mctx;
391 pctx->lctx = lctx;
392 pctx->lexer = NULL;
393 pctx->seen_eof = ISC_FALSE;
394 pctx->ungotten = ISC_FALSE;
395 pctx->errors = 0;
396 pctx->warnings = 0;
397 pctx->open_files = NULL;
398 pctx->closed_files = NULL;
399 pctx->line = 0;
400 pctx->callback = NULL;
401 pctx->callbackarg = NULL;
402 pctx->token.type = isc_tokentype_unknown;
404 memset(specials, 0, sizeof(specials));
405 specials['{'] = 1;
406 specials['}'] = 1;
407 specials[';'] = 1;
408 specials['/'] = 1;
409 specials['"'] = 1;
410 specials['!'] = 1;
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));
422 *ret = pctx;
423 return (ISC_R_SUCCESS);
425 cleanup:
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));
431 return (result);
434 static isc_result_t
435 parser_openfile(cfg_parser_t *pctx, const char *filename) {
436 isc_result_t result;
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));
444 goto cleanup;
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);
453 cleanup:
454 CLEANUP_OBJ(stringobj);
455 return (result);
458 void
459 cfg_parser_setcallback(cfg_parser_t *pctx,
460 cfg_parsecallback_t callback,
461 void *arg)
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.
471 static isc_result_t
472 parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
473 isc_result_t result;
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;
482 goto cleanup;
485 if (result != ISC_R_SUCCESS) {
486 /* Parsing failed but no errors have been logged. */
487 cfg_parser_error(pctx, 0, "parsing failed");
488 goto cleanup;
491 CHECK(parse_eof(pctx));
493 *ret = obj;
494 return (ISC_R_SUCCESS);
496 cleanup:
497 CLEANUP_OBJ(obj);
498 return (result);
501 isc_result_t
502 cfg_parse_file(cfg_parser_t *pctx, const char *filename,
503 const cfg_type_t *type, cfg_obj_t **ret)
505 isc_result_t result;
507 REQUIRE(filename != NULL);
509 CHECK(parser_openfile(pctx, filename));
510 CHECK(parse2(pctx, type, ret));
511 cleanup:
512 return (result);
516 isc_result_t
517 cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
518 const cfg_type_t *type, cfg_obj_t **ret)
520 isc_result_t result;
521 REQUIRE(buffer != NULL);
522 CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
523 CHECK(parse2(pctx, type, ret));
524 cleanup:
525 return (result);
528 void
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));
540 *pctxp = NULL;
544 * void
546 isc_result_t
547 cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
548 UNUSED(type);
549 return (cfg_create_obj(pctx, &cfg_type_void, ret));
552 void
553 cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
554 UNUSED(pctx);
555 UNUSED(obj);
558 void
559 cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
560 UNUSED(pctx);
561 UNUSED(type);
564 isc_boolean_t
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,
572 NULL };
576 * uint32
578 isc_result_t
579 cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
580 isc_result_t result;
581 cfg_obj_t *obj = NULL;
582 UNUSED(type);
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;
593 *ret = obj;
594 cleanup:
595 return (result);
598 void
599 cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
600 cfg_print_chars(pctx, s, strlen(s));
603 void
604 cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
605 char buf[32];
606 snprintf(buf, sizeof(buf), "%u", u);
607 cfg_print_cstr(pctx, buf);
610 void
611 cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
612 cfg_print_rawuint(pctx, obj->value.uint32);
615 isc_boolean_t
616 cfg_obj_isuint32(const cfg_obj_t *obj) {
617 REQUIRE(obj != NULL);
618 return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
621 isc_uint32_t
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
634 * uint64
636 isc_boolean_t
637 cfg_obj_isuint64(const cfg_obj_t *obj) {
638 REQUIRE(obj != NULL);
639 return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
642 isc_uint64_t
643 cfg_obj_asuint64(const cfg_obj_t *obj) {
644 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
645 return (obj->value.uint64);
648 void
649 cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
650 char buf[32];
651 snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
652 obj->value.uint64);
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
663 * (any string)
666 /* Create a string object from a null-terminated C string. */
667 static isc_result_t
668 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
669 cfg_obj_t **ret)
671 isc_result_t result;
672 cfg_obj_t *obj = NULL;
673 int len;
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';
686 *ret = obj;
687 cleanup:
688 return (result);
691 isc_result_t
692 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
693 isc_result_t result;
694 UNUSED(type);
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,
702 TOKEN_STRING(pctx),
703 &cfg_type_qstring,
704 ret));
705 cleanup:
706 return (result);
709 static isc_result_t
710 parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
711 isc_result_t result;
712 UNUSED(type);
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,
720 TOKEN_STRING(pctx),
721 &cfg_type_ustring,
722 ret));
723 cleanup:
724 return (result);
727 isc_result_t
728 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
729 cfg_obj_t **ret)
731 isc_result_t result;
732 UNUSED(type);
734 CHECK(cfg_getstringtoken(pctx));
735 return (create_string(pctx,
736 TOKEN_STRING(pctx),
737 &cfg_type_qstring,
738 ret));
739 cleanup:
740 return (result);
743 isc_boolean_t
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)
748 return (ISC_TRUE);
750 return (ISC_FALSE);
753 static isc_result_t
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);
762 isc_result_t
763 cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
764 isc_result_t result;
765 cfg_obj_t *obj = NULL;
766 CHECK(parse_ustring(pctx, NULL, &obj));
767 CHECK(check_enum(pctx, obj, type->of));
768 *ret = obj;
769 return (ISC_R_SUCCESS);
770 cleanup:
771 CLEANUP_OBJ(obj);
772 return (result);
775 void
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);
781 if (p[1] != NULL)
782 cfg_print_chars(pctx, " | ", 3);
784 cfg_print_chars(pctx, " )", 2);
787 void
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);
792 static void
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);
799 static void
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);
805 isc_boolean_t
806 cfg_obj_isstring(const cfg_obj_t *obj) {
807 REQUIRE(obj != NULL);
808 return (ISC_TF(obj->type->rep == &cfg_rep_string));
811 const char *
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
836 * Booleans
839 isc_boolean_t
840 cfg_obj_isboolean(const cfg_obj_t *obj) {
841 REQUIRE(obj != NULL);
842 return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
845 isc_boolean_t
846 cfg_obj_asboolean(const cfg_obj_t *obj) {
847 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
848 return (obj->value.boolean);
851 static isc_result_t
852 parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
854 isc_result_t result;
855 isc_boolean_t value;
856 cfg_obj_t *obj = NULL;
857 UNUSED(type);
859 result = cfg_gettoken(pctx, 0);
860 if (result != ISC_R_SUCCESS)
861 return (result);
863 if (pctx->token.type != isc_tokentype_string)
864 goto bad_boolean;
866 if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
867 (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
868 (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
869 value = ISC_TRUE;
870 } else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
871 (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
872 (strcmp(TOKEN_STRING(pctx), "0") == 0)) {
873 value = ISC_FALSE;
874 } else {
875 goto bad_boolean;
878 CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
879 obj->value.boolean = value;
880 *ret = obj;
881 return (result);
883 bad_boolean:
884 cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
885 return (ISC_R_UNEXPECTEDTOKEN);
887 cleanup:
888 return (result);
891 static void
892 print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
893 if (obj->value.boolean)
894 cfg_print_chars(pctx, "yes", 3);
895 else
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
905 * Lists.
908 isc_result_t
909 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
910 isc_result_t result;
911 CHECK(cfg_create_obj(pctx, type, obj));
912 ISC_LIST_INIT((*obj)->value.list);
913 cleanup:
914 return (result);
917 static isc_result_t
918 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
919 cfg_listelt_t *elt;
920 elt = isc_mem_get(pctx->mctx, sizeof(*elt));
921 if (elt == NULL)
922 return (ISC_R_NOMEMORY);
923 elt->obj = NULL;
924 ISC_LINK_INIT(elt, link);
925 *eltp = elt;
926 return (ISC_R_SUCCESS);
929 static void
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));
935 static void
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);
939 elt != NULL;
940 elt = next)
942 next = ISC_LIST_NEXT(elt, link);
943 free_list_elt(pctx, elt);
947 isc_result_t
948 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
949 cfg_listelt_t **ret)
951 isc_result_t result;
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)
959 goto cleanup;
961 elt->obj = value;
963 *ret = elt;
964 return (ISC_R_SUCCESS);
966 cleanup:
967 isc_mem_put(pctx->mctx, elt, sizeof(*elt));
968 return (result);
972 * Parse a homogeneous list whose elements are of type 'elttype'
973 * and where each element is terminated by a semicolon.
975 static isc_result_t
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;
980 isc_result_t result;
981 cfg_listelt_t *elt = NULL;
983 CHECK(cfg_create_list(pctx, listtype, &listobj));
985 for (;;) {
986 CHECK(cfg_peektoken(pctx, 0));
987 if (pctx->token.type == isc_tokentype_special &&
988 pctx->token.value.as_char == /*{*/ '}')
989 break;
990 CHECK(cfg_parse_listelt(pctx, listof, &elt));
991 CHECK(parse_semicolon(pctx));
992 ISC_LIST_APPEND(listobj->value.list, elt, link);
993 elt = NULL;
995 *ret = listobj;
996 return (ISC_R_SUCCESS);
998 cleanup:
999 if (elt != NULL)
1000 free_list_elt(pctx, elt);
1001 CLEANUP_OBJ(listobj);
1002 return (result);
1005 static void
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);
1011 elt != NULL;
1012 elt = ISC_LIST_NEXT(elt, link)) {
1013 print_indent(pctx);
1014 cfg_print_obj(pctx, elt->obj);
1015 cfg_print_chars(pctx, ";\n", 2);
1019 isc_result_t
1020 cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
1021 cfg_obj_t **ret)
1023 isc_result_t result;
1024 CHECK(cfg_parse_special(pctx, '{'));
1025 CHECK(parse_list(pctx, type, ret));
1026 CHECK(cfg_parse_special(pctx, '}'));
1027 cleanup:
1028 return (result);
1031 void
1032 cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1033 print_open(pctx);
1034 print_list(pctx, obj);
1035 print_close(pctx);
1038 void
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.
1050 isc_result_t
1051 cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
1052 cfg_obj_t **ret)
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));
1060 for (;;) {
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 == ';')
1066 break;
1067 CHECK(cfg_parse_listelt(pctx, listof, &elt));
1068 ISC_LIST_APPEND(listobj->value.list, elt, link);
1070 *ret = listobj;
1071 return (ISC_R_SUCCESS);
1073 cleanup:
1074 CLEANUP_OBJ(listobj);
1075 return (result);
1078 void
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);
1084 elt != NULL;
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);
1092 isc_boolean_t
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);
1101 if (obj == NULL)
1102 return (NULL);
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
1114 * a list, return 0.
1116 unsigned int
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))
1122 return (0U);
1123 for (elt = cfg_list_first(obj);
1124 elt != NULL;
1125 elt = cfg_list_next(elt)) {
1126 if (recurse && cfg_obj_islist(elt->obj)) {
1127 count += cfg_list_length(elt->obj, recurse);
1128 } else {
1129 count++;
1132 return (count);
1135 const cfg_obj_t *
1136 cfg_listelt_value(const cfg_listelt_t *elt) {
1137 REQUIRE(elt != NULL);
1138 return (elt->obj);
1142 * Maps.
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.
1155 isc_result_t
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;
1173 for (;;) {
1174 cfg_listelt_t *elt;
1176 redo:
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);
1184 break;
1188 * We accept "include" statements wherever a map body
1189 * clause can occur.
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
1195 * semicolon token.
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);
1202 goto redo;
1205 clause = NULL;
1206 for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
1207 for (clause = *clauseset;
1208 clause->name != NULL;
1209 clause++) {
1210 if (strcasecmp(TOKEN_STRING(pctx),
1211 clause->name) == 0)
1212 goto done;
1215 done:
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));
1225 continue;
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",
1233 clause->name);
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,
1243 * not its presence.
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,
1256 &listobj));
1257 symval.as_pointer = listobj;
1258 result = isc_symtab_define(obj->value.
1259 map.symtab,
1260 clause->name,
1261 1, symval,
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));
1269 goto cleanup;
1271 } else {
1272 INSIST(result == ISC_R_SUCCESS);
1273 listobj = symval.as_pointer;
1276 elt = NULL;
1277 CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
1278 CHECK(parse_semicolon(pctx));
1280 ISC_LIST_APPEND(listobj->value.list, elt, link);
1281 } else {
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,
1288 clause->type,
1289 obj->value.map.symtab,
1290 callback));
1291 CHECK(parse_semicolon(pctx));
1292 } else if (result == ISC_R_SUCCESS) {
1293 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
1294 clause->name);
1295 result = ISC_R_EXISTS;
1296 goto cleanup;
1297 } else {
1298 cfg_parser_error(pctx, CFG_LOG_NEAR,
1299 "isc_symtab_define() failed");
1300 goto cleanup;
1306 *ret = obj;
1307 return (ISC_R_SUCCESS);
1309 cleanup:
1310 CLEANUP_OBJ(value);
1311 CLEANUP_OBJ(obj);
1312 CLEANUP_OBJ(eltobj);
1313 CLEANUP_OBJ(includename);
1314 return (result);
1317 static isc_result_t
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,
1333 1, symval,
1334 isc_symexists_reject));
1335 return (ISC_R_SUCCESS);
1337 cleanup:
1338 CLEANUP_OBJ(obj);
1339 return (result);
1343 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
1345 isc_result_t
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, '}'));
1351 cleanup:
1352 return (result);
1356 * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
1358 static isc_result_t
1359 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type,
1360 cfg_obj_t **ret)
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;
1369 idobj = NULL;
1370 *ret = mapobj;
1371 cleanup:
1372 CLEANUP_OBJ(idobj);
1373 return (result);
1377 * Parse a map identified by a string name. E.g., "name { foo 1; }".
1378 * Used for the "key" and "channel" statements.
1380 isc_result_t
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.
1389 isc_result_t
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.
1398 isc_result_t
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));
1403 void
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;
1410 *clauseset != NULL;
1411 clauseset++)
1413 isc_symvalue_t symval;
1414 const cfg_clausedef_t *clause;
1416 for (clause = *clauseset;
1417 clause->name != NULL;
1418 clause++) {
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) {
1424 /* Multivalued. */
1425 cfg_list_t *list = &obj->value.list;
1426 cfg_listelt_t *elt;
1427 for (elt = ISC_LIST_HEAD(*list);
1428 elt != NULL;
1429 elt = ISC_LIST_NEXT(elt, link)) {
1430 print_indent(pctx);
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);
1436 } else {
1437 /* Single-valued. */
1438 print_indent(pctx);
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) {
1445 ; /* do nothing */
1446 } else {
1447 INSIST(0);
1453 void
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;
1461 clause++) {
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 {
1473 unsigned int flag;
1474 const char *text;
1475 } flagtexts[] = {
1476 { CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
1477 { CFG_CLAUSEFLAG_NYI, "not yet implemented" },
1478 { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
1479 { CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
1480 { 0, NULL }
1483 void
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);
1489 print_open(pctx);
1490 cfg_print_mapbody(pctx, obj);
1491 print_close(pctx);
1494 static void
1495 print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
1496 struct flagtext *p;
1497 isc_boolean_t first = ISC_TRUE;
1498 for (p = flagtexts; p->flag != 0; p++) {
1499 if ((flags & p->flag) != 0) {
1500 if (first)
1501 cfg_print_chars(pctx, " // ", 4);
1502 else
1503 cfg_print_chars(pctx, ", ", 2);
1504 cfg_print_cstr(pctx, p->text);
1505 first = ISC_FALSE;
1510 void
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);
1526 print_open(pctx);
1528 for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1529 for (clause = *clauseset;
1530 clause->name != NULL;
1531 clause++) {
1532 print_indent(pctx);
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);
1542 print_close(pctx);
1545 isc_boolean_t
1546 cfg_obj_ismap(const cfg_obj_t *obj) {
1547 REQUIRE(obj != NULL);
1548 return (ISC_TF(obj->type->rep == &cfg_rep_map));
1551 isc_result_t
1552 cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
1553 isc_result_t result;
1554 isc_symvalue_t val;
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)
1565 return (result);
1566 *obj = val.as_pointer;
1567 return (ISC_R_SUCCESS);
1570 const cfg_obj_t *
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. */
1578 static isc_result_t
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;
1582 isc_region_t r;
1584 UNUSED(type);
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);
1590 result = ISC_R_EOF;
1591 goto cleanup;
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;
1599 goto cleanup;
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';
1604 *ret = obj;
1605 return (result);
1607 cleanup:
1608 if (obj != NULL)
1609 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
1610 return (result);
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.
1623 static isc_result_t
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;
1627 int braces = 0;
1629 CHECK(cfg_create_list(pctx, type, &listobj));
1631 for (;;) {
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 == '{')
1637 braces++;
1638 else if (pctx->token.value.as_char == '}')
1639 braces--;
1640 else if (pctx->token.value.as_char == ';')
1641 if (braces == 0)
1642 break;
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;
1647 goto cleanup;
1650 CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
1651 ISC_LIST_APPEND(listobj->value.list, elt, link);
1653 INSIST(braces == 0);
1654 *ret = listobj;
1655 return (ISC_R_SUCCESS);
1657 cleanup:
1658 CLEANUP_OBJ(listobj);
1659 return (result);
1662 cfg_type_t cfg_type_unsupported = {
1663 "unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal,
1664 &cfg_rep_list, NULL
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.
1676 static isc_result_t
1677 token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1678 char *s;
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);
1693 } else {
1694 INSIST(0);
1696 } else {
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 &&
1704 strlen(s) <= 15U) {
1705 char buf[64];
1706 int i;
1708 strcpy(buf, s);
1709 for (i = 0; i < 3; i++) {
1710 strcat(buf, ".0");
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 */
1723 strcpy(buf, s);
1724 d = strchr(buf, '%');
1725 if (d != NULL)
1726 *d = '\0';
1728 if (inet_pton(AF_INET6, buf, &in6a) == 1) {
1729 if (d != NULL) {
1730 #ifdef ISC_PLATFORM_HAVESCOPEID
1731 isc_result_t result;
1733 result = isc_netscope_pton(AF_INET6,
1734 d + 1,
1735 &in6a,
1736 &zone);
1737 if (result != ISC_R_SUCCESS)
1738 return (result);
1739 #else
1740 return (ISC_R_BADADDRESSFORM);
1741 #endif
1744 isc_netaddr_fromin6(na, &in6a);
1745 isc_netaddr_setzone(na, zone);
1746 return (ISC_R_SUCCESS);
1750 return (ISC_R_UNEXPECTEDTOKEN);
1753 isc_result_t
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)
1763 wild = " or '*'";
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",
1769 prefix, wild);
1770 else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK)
1771 cfg_parser_error(pctx, CFG_LOG_NEAR,
1772 "expected IPv6 address%s%s",
1773 prefix, wild);
1774 else
1775 cfg_parser_error(pctx, CFG_LOG_NEAR,
1776 "expected IP address%s%s",
1777 prefix, wild);
1779 cleanup:
1780 return (result);
1783 isc_boolean_t
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));
1791 isc_result_t
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) {
1800 *port = 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);
1815 cleanup:
1816 return (result);
1819 void
1820 cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
1821 isc_result_t result;
1822 char text[128];
1823 isc_buffer_t buf;
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));
1831 /* netaddr */
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;
1839 static isc_result_t
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);
1849 *ret = obj;
1850 return (ISC_R_SUCCESS);
1851 cleanup:
1852 CLEANUP_OBJ(obj);
1853 return (result);
1856 static void
1857 cfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
1858 const unsigned int *flagp = type->of;
1859 int n = 0;
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>");
1864 n++;
1866 if (*flagp & CFG_ADDR_V6OK) {
1867 if (n != 0)
1868 cfg_print_chars(pctx, " | ", 3);
1869 cfg_print_cstr(pctx, "<ipv6_address>");
1870 n++;
1872 if (*flagp & CFG_ADDR_WILDOK) {
1873 if (n != 0)
1874 cfg_print_chars(pctx, " | ", 3);
1875 cfg_print_chars(pctx, "*", 1);
1876 n++;
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
1907 /* netprefix */
1909 isc_result_t
1910 cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
1911 cfg_obj_t **ret)
1913 cfg_obj_t *obj = NULL;
1914 isc_result_t result;
1915 isc_netaddr_t netaddr;
1916 unsigned int addrlen, prefixlen;
1917 UNUSED(type);
1919 CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
1920 CFG_ADDR_V6OK, &netaddr));
1921 switch (netaddr.family) {
1922 case AF_INET:
1923 addrlen = 32;
1924 break;
1925 case AF_INET6:
1926 addrlen = 128;
1927 break;
1928 default:
1929 addrlen = 0;
1930 INSIST(0);
1931 break;
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);
1949 } else {
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;
1955 *ret = obj;
1956 return (ISC_R_SUCCESS);
1957 cleanup:
1958 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
1959 return (result);
1962 static void
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);
1971 isc_boolean_t
1972 cfg_obj_isnetprefix(const cfg_obj_t *obj) {
1973 REQUIRE(obj != NULL);
1974 return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
1977 void
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
1990 static isc_result_t
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;
1996 in_port_t port = 0;
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);
2008 *ret = obj;
2009 return (ISC_R_SUCCESS);
2011 cleanup:
2012 CLEANUP_OBJ(obj);
2013 return (result);
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
2022 isc_result_t
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));
2028 void
2029 cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2030 isc_netaddr_t netaddr;
2031 in_port_t port;
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);
2038 if (port != 0) {
2039 cfg_print_chars(pctx, " port ", 6);
2040 cfg_print_rawuint(pctx, port);
2044 void
2045 cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
2046 const unsigned int *flagp = type->of;
2047 int n = 0;
2048 cfg_print_chars(pctx, "( ", 2);
2049 if (*flagp & CFG_ADDR_V4OK) {
2050 cfg_print_cstr(pctx, "<ipv4_address>");
2051 n++;
2053 if (*flagp & CFG_ADDR_V6OK) {
2054 if (n != 0)
2055 cfg_print_chars(pctx, " | ", 3);
2056 cfg_print_cstr(pctx, "<ipv6_address>");
2057 n++;
2059 if (*flagp & CFG_ADDR_WILDOK) {
2060 if (n != 0)
2061 cfg_print_chars(pctx, " | ", 3);
2062 cfg_print_chars(pctx, "*", 1);
2063 n++;
2065 cfg_print_chars(pctx, " ) ", 3);
2066 if (*flagp & CFG_ADDR_WILDOK) {
2067 cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
2068 } else {
2069 cfg_print_cstr(pctx, "[ port <integer> ]");
2073 isc_boolean_t
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);
2085 isc_result_t
2086 cfg_gettoken(cfg_parser_t *pctx, int options) {
2087 isc_result_t result;
2089 if (pctx->seen_eof)
2090 return (ISC_R_SUCCESS);
2092 options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
2094 redo:
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);
2100 switch (result) {
2101 case ISC_R_SUCCESS:
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.
2111 cfg_listelt_t *elt;
2112 elt = ISC_LIST_TAIL(pctx->open_files->
2113 value.list);
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);
2119 goto redo;
2121 pctx->seen_eof = ISC_TRUE;
2123 break;
2125 case ISC_R_NOSPACE:
2126 /* More understandable than "ran out of space". */
2127 cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
2128 break;
2130 case ISC_R_IOERROR:
2131 cfg_parser_error(pctx, 0, "%s",
2132 isc_result_totext(result));
2133 break;
2135 default:
2136 cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
2137 isc_result_totext(result));
2138 break;
2140 return (result);
2143 void
2144 cfg_ungettoken(cfg_parser_t *pctx) {
2145 if (pctx->seen_eof)
2146 return;
2147 isc_lex_ungettoken(pctx->lexer, &pctx->token);
2148 pctx->ungotten = ISC_TRUE;
2151 isc_result_t
2152 cfg_peektoken(cfg_parser_t *pctx, int options) {
2153 isc_result_t result;
2154 CHECK(cfg_gettoken(pctx, options));
2155 cfg_ungettoken(pctx);
2156 cleanup:
2157 return (result);
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.
2164 static isc_result_t
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)
2170 return (result);
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);
2180 void
2181 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2182 va_list args;
2183 va_start(args, fmt);
2184 parser_complain(pctx, ISC_FALSE, flags, fmt, args);
2185 va_end(args);
2186 pctx->errors++;
2189 void
2190 cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2191 va_list args;
2192 va_start(args, fmt);
2193 parser_complain(pctx, ISC_TRUE, flags, fmt, args);
2194 va_end(args);
2195 pctx->warnings++;
2198 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
2200 static char *
2201 current_file(cfg_parser_t *pctx) {
2202 static char none[] = "none";
2203 cfg_listelt_t *elt;
2204 cfg_obj_t *fileobj;
2206 if (pctx->open_files == NULL)
2207 return (none);
2208 elt = ISC_LIST_TAIL(pctx->open_files->value.list);
2209 if (elt == NULL)
2210 return (none);
2212 fileobj = elt->obj;
2213 INSIST(fileobj->type == &cfg_type_qstring);
2214 return (fileobj->value.string.base);
2217 static void
2218 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
2219 unsigned int flags, const char *format,
2220 va_list args)
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 = "";
2227 size_t len;
2229 if (is_warning)
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) {
2241 isc_region_t r;
2243 if (pctx->ungotten)
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) {
2249 flags = 0;
2250 tokenbuf[0] = '\0';
2251 } else {
2252 isc_lex_getlasttokentext(pctx->lexer,
2253 &pctx->token, &r);
2254 if (r.length > MAX_LOG_TOKEN)
2255 snprintf(tokenbuf, sizeof(tokenbuf),
2256 "'%.*s...'", MAX_LOG_TOKEN, r.base);
2257 else
2258 snprintf(tokenbuf, sizeof(tokenbuf),
2259 "'%.*s'", (int)r.length, r.base);
2262 /* Choose a preposition. */
2263 if (flags & CFG_LOG_NEAR)
2264 prep = " near ";
2265 else if (flags & CFG_LOG_BEFORE)
2266 prep = " before ";
2267 else
2268 prep = " ";
2269 } else {
2270 tokenbuf[0] = '\0';
2272 isc_log_write(pctx->lctx, CAT, MOD, level,
2273 "%s%s%s%s", where, message, prep, tokenbuf);
2276 void
2277 cfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level,
2278 const char *fmt, ...) {
2279 va_list ap;
2280 char msgbuf[2048];
2282 if (! isc_log_wouldlog(lctx, level))
2283 return;
2285 va_start(ap, fmt);
2287 vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
2288 isc_log_write(lctx, CAT, MOD, level,
2289 "%s:%u: %s",
2290 obj->file == NULL ? "<unknown file>" : obj->file,
2291 obj->line, msgbuf);
2292 va_end(ap);
2295 const char *
2296 cfg_obj_file(const cfg_obj_t *obj) {
2297 return (obj->file);
2300 unsigned int
2301 cfg_obj_line(const cfg_obj_t *obj) {
2302 return (obj->line);
2305 isc_result_t
2306 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2307 cfg_obj_t *obj;
2309 obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
2310 if (obj == NULL)
2311 return (ISC_R_NOMEMORY);
2312 obj->type = type;
2313 obj->file = current_file(pctx);
2314 obj->line = pctx->line;
2315 *ret = obj;
2316 return (ISC_R_SUCCESS);
2319 static void
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;
2326 UNUSED(key);
2327 UNUSED(type);
2329 cfg_obj_destroy(pctx, &obj);
2333 static isc_result_t
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;
2346 *ret = obj;
2347 return (ISC_R_SUCCESS);
2349 cleanup:
2350 if (obj != NULL)
2351 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
2352 return (result);
2355 static void
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);
2361 isc_boolean_t
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'.
2369 void
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));
2374 *objp = NULL;
2377 static void
2378 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
2379 UNUSED(pctx);
2380 UNUSED(obj);
2383 void
2384 cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
2385 type->doc(pctx, type);
2388 void
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);
2395 void
2396 cfg_print_grammar(const cfg_type_t *type,
2397 void (*f)(void *closure, const char *text, int textlen),
2398 void *closure)
2400 cfg_printer_t pctx;
2401 pctx.f = f;
2402 pctx.closure = closure;
2403 pctx.indent = 0;
2404 cfg_doc_obj(&pctx, type);