No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / bind / dist / lib / isccfg / parser.c
blob813483ccf03cd0dbd610e18ed2dae3bf342bbf9c
1 /* $NetBSD$ */
3 /*
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 */
22 /*! \file */
24 #include <config.h>
26 #include <isc/buffer.h>
27 #include <isc/dir.h>
28 #include <isc/formatcheck.h>
29 #include <isc/lex.h>
30 #include <isc/log.h>
31 #include <isc/mem.h>
32 #include <isc/net.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>
39 #include <isc/util.h>
41 #include <isccfg/cfg.h>
42 #include <isccfg/grammar.h>
43 #include <isccfg/log.h>
45 /* Shorthand */
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. */
54 #define CHECK(op) \
55 do { result = (op); \
56 if (result != ISC_R_SUCCESS) goto cleanup; \
57 } while (0)
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.
68 static void
69 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
71 static isc_result_t
72 parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
74 static void
75 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
77 static void
78 free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
80 static isc_result_t
81 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
83 static isc_result_t
84 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
85 cfg_obj_t **ret);
87 static void
88 free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
90 static isc_result_t
91 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
93 static void
94 free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
96 static isc_result_t
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);
101 static void
102 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
104 static isc_result_t
105 cfg_getstringtoken(cfg_parser_t *pctx);
107 static void
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 };
138 /* Functions. */
140 void
141 cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
142 obj->type->print(pctx, obj);
145 void
146 cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
147 pctx->f(pctx->closure, text, len);
150 static void
151 print_open(cfg_printer_t *pctx) {
152 cfg_print_chars(pctx, "{\n", 2);
153 pctx->indent++;
156 static void
157 print_indent(cfg_printer_t *pctx) {
158 int indent = pctx->indent;
159 while (indent > 0) {
160 cfg_print_chars(pctx, "\t", 1);
161 indent--;
165 static void
166 print_close(cfg_printer_t *pctx) {
167 pctx->indent--;
168 print_indent(pctx);
169 cfg_print_chars(pctx, "}", 1);
172 isc_result_t
173 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
174 isc_result_t result;
175 INSIST(ret != NULL && *ret == NULL);
176 result = type->parse(pctx, type, ret);
177 if (result != ISC_R_SUCCESS)
178 return (result);
179 INSIST(*ret != NULL);
180 return (ISC_R_SUCCESS);
183 void
184 cfg_print(const cfg_obj_t *obj,
185 void (*f)(void *closure, const char *text, int textlen),
186 void *closure)
188 cfg_printer_t pctx;
189 pctx.f = f;
190 pctx.closure = closure;
191 pctx.indent = 0;
192 obj->type->print(&pctx, obj);
196 /* Tuples. */
198 isc_result_t
199 cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
200 isc_result_t result;
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;
205 int i;
207 for (f = fields; f->name != NULL; f++)
208 nfields++;
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;
215 goto cleanup;
217 for (f = fields, i = 0; f->name != NULL; f++, i++)
218 obj->value.tuple[i] = NULL;
219 *ret = obj;
220 return (ISC_R_SUCCESS);
222 cleanup:
223 if (obj != NULL)
224 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
225 return (result);
228 isc_result_t
229 cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
231 isc_result_t result;
232 const cfg_tuplefielddef_t *fields = type->of;
233 const cfg_tuplefielddef_t *f;
234 cfg_obj_t *obj = NULL;
235 unsigned int i;
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]));
241 *ret = obj;
242 return (ISC_R_SUCCESS);
244 cleanup:
245 CLEANUP_OBJ(obj);
246 return (result);
249 void
250 cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
251 unsigned int i;
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];
258 if (need_space)
259 cfg_print_chars(pctx, " ", 1);
260 cfg_print_obj(pctx, fieldobj);
261 need_space = ISC_TF(fieldobj->type->print != cfg_print_void);
265 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++) {
272 if (need_space)
273 cfg_print_chars(pctx, " ", 1);
274 cfg_doc_obj(pctx, f->type);
275 need_space = ISC_TF(f->type->print != cfg_print_void);
279 static void
280 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
281 unsigned int i;
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)
287 return;
289 for (f = fields, i = 0; f->name != NULL; f++, i++) {
290 CLEANUP_OBJ(obj->value.tuple[i]);
291 nfields++;
293 isc_mem_put(pctx->mctx, obj->value.tuple,
294 nfields * sizeof(cfg_obj_t *));
297 isc_boolean_t
298 cfg_obj_istuple(const cfg_obj_t *obj) {
299 REQUIRE(obj != NULL);
300 return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
303 const cfg_obj_t *
304 cfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) {
305 unsigned int i;
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]);
316 INSIST(0);
317 return (NULL);
320 isc_result_t
321 cfg_parse_special(cfg_parser_t *pctx, int special) {
322 isc_result_t result;
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);
330 cleanup:
331 return (result);
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.
341 static isc_result_t
342 parse_semicolon(cfg_parser_t *pctx) {
343 isc_result_t result;
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);
351 cleanup:
352 return (result);
356 * Parse EOF, logging and returning an error if not there.
358 static isc_result_t
359 parse_eof(cfg_parser_t *pctx) {
360 isc_result_t result;
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);
368 cleanup:
369 return (result);
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,
376 &cfg_type_qstring
379 isc_result_t
380 cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
381 isc_result_t result;
382 cfg_parser_t *pctx;
383 isc_lexspecials_t specials;
385 REQUIRE(mctx != NULL);
386 REQUIRE(ret != NULL && *ret == NULL);
388 pctx = isc_mem_get(mctx, sizeof(*pctx));
389 if (pctx == NULL)
390 return (ISC_R_NOMEMORY);
392 pctx->mctx = mctx;
393 pctx->lctx = lctx;
394 pctx->lexer = NULL;
395 pctx->seen_eof = ISC_FALSE;
396 pctx->ungotten = ISC_FALSE;
397 pctx->errors = 0;
398 pctx->warnings = 0;
399 pctx->open_files = NULL;
400 pctx->closed_files = NULL;
401 pctx->line = 0;
402 pctx->callback = NULL;
403 pctx->callbackarg = NULL;
404 pctx->token.type = isc_tokentype_unknown;
405 pctx->flags = 0;
407 memset(specials, 0, sizeof(specials));
408 specials['{'] = 1;
409 specials['}'] = 1;
410 specials[';'] = 1;
411 specials['/'] = 1;
412 specials['"'] = 1;
413 specials['!'] = 1;
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));
425 *ret = pctx;
426 return (ISC_R_SUCCESS);
428 cleanup:
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));
434 return (result);
437 static isc_result_t
438 parser_openfile(cfg_parser_t *pctx, const char *filename) {
439 isc_result_t result;
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));
447 goto cleanup;
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);
456 cleanup:
457 CLEANUP_OBJ(stringobj);
458 return (result);
461 void
462 cfg_parser_setcallback(cfg_parser_t *pctx,
463 cfg_parsecallback_t callback,
464 void *arg)
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.
474 static isc_result_t
475 parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
476 isc_result_t result;
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;
485 goto cleanup;
488 if (result != ISC_R_SUCCESS) {
489 /* Parsing failed but no errors have been logged. */
490 cfg_parser_error(pctx, 0, "parsing failed");
491 goto cleanup;
494 CHECK(parse_eof(pctx));
496 *ret = obj;
497 return (ISC_R_SUCCESS);
499 cleanup:
500 CLEANUP_OBJ(obj);
501 return (result);
504 isc_result_t
505 cfg_parse_file(cfg_parser_t *pctx, const char *filename,
506 const cfg_type_t *type, cfg_obj_t **ret)
508 isc_result_t result;
510 REQUIRE(filename != NULL);
512 CHECK(parser_openfile(pctx, filename));
513 CHECK(parse2(pctx, type, ret));
514 cleanup:
515 return (result);
519 isc_result_t
520 cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
521 const cfg_type_t *type, cfg_obj_t **ret)
523 isc_result_t result;
524 REQUIRE(buffer != NULL);
525 CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
526 CHECK(parse2(pctx, type, ret));
527 cleanup:
528 return (result);
531 void
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));
543 *pctxp = NULL;
547 * void
549 isc_result_t
550 cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
551 UNUSED(type);
552 return (cfg_create_obj(pctx, &cfg_type_void, ret));
555 void
556 cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
557 UNUSED(pctx);
558 UNUSED(obj);
561 void
562 cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
563 UNUSED(pctx);
564 UNUSED(type);
567 isc_boolean_t
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,
575 NULL };
579 * uint32
581 isc_result_t
582 cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
583 isc_result_t result;
584 cfg_obj_t *obj = NULL;
585 UNUSED(type);
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;
596 *ret = obj;
597 cleanup:
598 return (result);
601 void
602 cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
603 cfg_print_chars(pctx, s, strlen(s));
606 void
607 cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
608 char buf[32];
609 snprintf(buf, sizeof(buf), "%u", u);
610 cfg_print_cstr(pctx, buf);
613 void
614 cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
615 cfg_print_rawuint(pctx, obj->value.uint32);
618 isc_boolean_t
619 cfg_obj_isuint32(const cfg_obj_t *obj) {
620 REQUIRE(obj != NULL);
621 return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
624 isc_uint32_t
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
637 * uint64
639 isc_boolean_t
640 cfg_obj_isuint64(const cfg_obj_t *obj) {
641 REQUIRE(obj != NULL);
642 return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
645 isc_uint64_t
646 cfg_obj_asuint64(const cfg_obj_t *obj) {
647 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
648 return (obj->value.uint64);
651 void
652 cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
653 char buf[32];
654 snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
655 obj->value.uint64);
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
666 * (any string)
669 /* Create a string object from a null-terminated C string. */
670 static isc_result_t
671 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
672 cfg_obj_t **ret)
674 isc_result_t result;
675 cfg_obj_t *obj = NULL;
676 int len;
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';
689 *ret = obj;
690 cleanup:
691 return (result);
694 isc_result_t
695 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
696 isc_result_t result;
697 UNUSED(type);
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,
705 TOKEN_STRING(pctx),
706 &cfg_type_qstring,
707 ret));
708 cleanup:
709 return (result);
712 static isc_result_t
713 parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
714 isc_result_t result;
715 UNUSED(type);
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,
723 TOKEN_STRING(pctx),
724 &cfg_type_ustring,
725 ret));
726 cleanup:
727 return (result);
730 isc_result_t
731 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
732 cfg_obj_t **ret)
734 isc_result_t result;
735 UNUSED(type);
737 CHECK(cfg_getstringtoken(pctx));
738 return (create_string(pctx,
739 TOKEN_STRING(pctx),
740 &cfg_type_qstring,
741 ret));
742 cleanup:
743 return (result);
746 isc_boolean_t
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)
751 return (ISC_TRUE);
753 return (ISC_FALSE);
756 static isc_result_t
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);
765 isc_result_t
766 cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
767 isc_result_t result;
768 cfg_obj_t *obj = NULL;
769 CHECK(parse_ustring(pctx, NULL, &obj));
770 CHECK(check_enum(pctx, obj, type->of));
771 *ret = obj;
772 return (ISC_R_SUCCESS);
773 cleanup:
774 CLEANUP_OBJ(obj);
775 return (result);
778 void
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);
784 if (p[1] != NULL)
785 cfg_print_chars(pctx, " | ", 3);
787 cfg_print_chars(pctx, " )", 2);
790 void
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);
795 static void
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);
802 static void
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);
808 isc_boolean_t
809 cfg_obj_isstring(const cfg_obj_t *obj) {
810 REQUIRE(obj != NULL);
811 return (ISC_TF(obj->type->rep == &cfg_rep_string));
814 const char *
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
839 * Booleans
842 isc_boolean_t
843 cfg_obj_isboolean(const cfg_obj_t *obj) {
844 REQUIRE(obj != NULL);
845 return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
848 isc_boolean_t
849 cfg_obj_asboolean(const cfg_obj_t *obj) {
850 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
851 return (obj->value.boolean);
854 static isc_result_t
855 parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
857 isc_result_t result;
858 isc_boolean_t value;
859 cfg_obj_t *obj = NULL;
860 UNUSED(type);
862 result = cfg_gettoken(pctx, 0);
863 if (result != ISC_R_SUCCESS)
864 return (result);
866 if (pctx->token.type != isc_tokentype_string)
867 goto bad_boolean;
869 if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
870 (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
871 (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
872 value = ISC_TRUE;
873 } else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
874 (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
875 (strcmp(TOKEN_STRING(pctx), "0") == 0)) {
876 value = ISC_FALSE;
877 } else {
878 goto bad_boolean;
881 CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
882 obj->value.boolean = value;
883 *ret = obj;
884 return (result);
886 bad_boolean:
887 cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
888 return (ISC_R_UNEXPECTEDTOKEN);
890 cleanup:
891 return (result);
894 static void
895 print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
896 if (obj->value.boolean)
897 cfg_print_chars(pctx, "yes", 3);
898 else
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
908 * Lists.
911 isc_result_t
912 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
913 isc_result_t result;
914 CHECK(cfg_create_obj(pctx, type, obj));
915 ISC_LIST_INIT((*obj)->value.list);
916 cleanup:
917 return (result);
920 static isc_result_t
921 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
922 cfg_listelt_t *elt;
923 elt = isc_mem_get(pctx->mctx, sizeof(*elt));
924 if (elt == NULL)
925 return (ISC_R_NOMEMORY);
926 elt->obj = NULL;
927 ISC_LINK_INIT(elt, link);
928 *eltp = elt;
929 return (ISC_R_SUCCESS);
932 static void
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));
938 static void
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);
942 elt != NULL;
943 elt = next)
945 next = ISC_LIST_NEXT(elt, link);
946 free_list_elt(pctx, elt);
950 isc_result_t
951 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
952 cfg_listelt_t **ret)
954 isc_result_t result;
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)
962 goto cleanup;
964 elt->obj = value;
966 *ret = elt;
967 return (ISC_R_SUCCESS);
969 cleanup:
970 isc_mem_put(pctx->mctx, elt, sizeof(*elt));
971 return (result);
975 * Parse a homogeneous list whose elements are of type 'elttype'
976 * and where each element is terminated by a semicolon.
978 static isc_result_t
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;
983 isc_result_t result;
984 cfg_listelt_t *elt = NULL;
986 CHECK(cfg_create_list(pctx, listtype, &listobj));
988 for (;;) {
989 CHECK(cfg_peektoken(pctx, 0));
990 if (pctx->token.type == isc_tokentype_special &&
991 pctx->token.value.as_char == /*{*/ '}')
992 break;
993 CHECK(cfg_parse_listelt(pctx, listof, &elt));
994 CHECK(parse_semicolon(pctx));
995 ISC_LIST_APPEND(listobj->value.list, elt, link);
996 elt = NULL;
998 *ret = listobj;
999 return (ISC_R_SUCCESS);
1001 cleanup:
1002 if (elt != NULL)
1003 free_list_elt(pctx, elt);
1004 CLEANUP_OBJ(listobj);
1005 return (result);
1008 static void
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);
1014 elt != NULL;
1015 elt = ISC_LIST_NEXT(elt, link)) {
1016 print_indent(pctx);
1017 cfg_print_obj(pctx, elt->obj);
1018 cfg_print_chars(pctx, ";\n", 2);
1022 isc_result_t
1023 cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
1024 cfg_obj_t **ret)
1026 isc_result_t result;
1027 CHECK(cfg_parse_special(pctx, '{'));
1028 CHECK(parse_list(pctx, type, ret));
1029 CHECK(cfg_parse_special(pctx, '}'));
1030 cleanup:
1031 return (result);
1034 void
1035 cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1036 print_open(pctx);
1037 print_list(pctx, obj);
1038 print_close(pctx);
1041 void
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.
1053 isc_result_t
1054 cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
1055 cfg_obj_t **ret)
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));
1063 for (;;) {
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 == ';')
1069 break;
1070 CHECK(cfg_parse_listelt(pctx, listof, &elt));
1071 ISC_LIST_APPEND(listobj->value.list, elt, link);
1073 *ret = listobj;
1074 return (ISC_R_SUCCESS);
1076 cleanup:
1077 CLEANUP_OBJ(listobj);
1078 return (result);
1081 void
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);
1087 elt != NULL;
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);
1095 isc_boolean_t
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);
1104 if (obj == NULL)
1105 return (NULL);
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
1117 * a list, return 0.
1119 unsigned int
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))
1125 return (0U);
1126 for (elt = cfg_list_first(obj);
1127 elt != NULL;
1128 elt = cfg_list_next(elt)) {
1129 if (recurse && cfg_obj_islist(elt->obj)) {
1130 count += cfg_list_length(elt->obj, recurse);
1131 } else {
1132 count++;
1135 return (count);
1138 const cfg_obj_t *
1139 cfg_listelt_value(const cfg_listelt_t *elt) {
1140 REQUIRE(elt != NULL);
1141 return (elt->obj);
1145 * Maps.
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.
1158 isc_result_t
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;
1176 for (;;) {
1177 cfg_listelt_t *elt;
1179 redo:
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);
1187 break;
1191 * We accept "include" statements wherever a map body
1192 * clause can occur.
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
1198 * semicolon token.
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);
1205 goto redo;
1208 clause = NULL;
1209 for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
1210 for (clause = *clauseset;
1211 clause->name != NULL;
1212 clause++) {
1213 if (strcasecmp(TOKEN_STRING(pctx),
1214 clause->name) == 0)
1215 goto done;
1218 done:
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));
1228 continue;
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",
1236 clause->name);
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,
1246 * not its presence.
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,
1259 &listobj));
1260 symval.as_pointer = listobj;
1261 result = isc_symtab_define(obj->value.
1262 map.symtab,
1263 clause->name,
1264 1, symval,
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));
1272 goto cleanup;
1274 } else {
1275 INSIST(result == ISC_R_SUCCESS);
1276 listobj = symval.as_pointer;
1279 elt = NULL;
1280 CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
1281 CHECK(parse_semicolon(pctx));
1283 ISC_LIST_APPEND(listobj->value.list, elt, link);
1284 } else {
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,
1291 clause->type,
1292 obj->value.map.symtab,
1293 callback));
1294 CHECK(parse_semicolon(pctx));
1295 } else if (result == ISC_R_SUCCESS) {
1296 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
1297 clause->name);
1298 result = ISC_R_EXISTS;
1299 goto cleanup;
1300 } else {
1301 cfg_parser_error(pctx, CFG_LOG_NEAR,
1302 "isc_symtab_define() failed");
1303 goto cleanup;
1309 *ret = obj;
1310 return (ISC_R_SUCCESS);
1312 cleanup:
1313 CLEANUP_OBJ(value);
1314 CLEANUP_OBJ(obj);
1315 CLEANUP_OBJ(eltobj);
1316 CLEANUP_OBJ(includename);
1317 return (result);
1320 static isc_result_t
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,
1336 1, symval,
1337 isc_symexists_reject));
1338 return (ISC_R_SUCCESS);
1340 cleanup:
1341 CLEANUP_OBJ(obj);
1342 return (result);
1346 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
1348 isc_result_t
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, '}'));
1354 cleanup:
1355 return (result);
1359 * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
1361 static isc_result_t
1362 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type,
1363 cfg_obj_t **ret)
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;
1372 idobj = NULL;
1373 *ret = mapobj;
1374 cleanup:
1375 CLEANUP_OBJ(idobj);
1376 return (result);
1380 * Parse a map identified by a string name. E.g., "name { foo 1; }".
1381 * Used for the "key" and "channel" statements.
1383 isc_result_t
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.
1392 isc_result_t
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.
1401 isc_result_t
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));
1406 void
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;
1413 *clauseset != NULL;
1414 clauseset++)
1416 isc_symvalue_t symval;
1417 const cfg_clausedef_t *clause;
1419 for (clause = *clauseset;
1420 clause->name != NULL;
1421 clause++) {
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) {
1427 /* Multivalued. */
1428 cfg_list_t *list = &obj->value.list;
1429 cfg_listelt_t *elt;
1430 for (elt = ISC_LIST_HEAD(*list);
1431 elt != NULL;
1432 elt = ISC_LIST_NEXT(elt, link)) {
1433 print_indent(pctx);
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);
1439 } else {
1440 /* Single-valued. */
1441 print_indent(pctx);
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) {
1448 ; /* do nothing */
1449 } else {
1450 INSIST(0);
1456 void
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;
1464 clause++) {
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 {
1476 unsigned int flag;
1477 const char *text;
1478 } flagtexts[] = {
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" },
1484 { 0, NULL }
1487 void
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);
1493 print_open(pctx);
1494 cfg_print_mapbody(pctx, obj);
1495 print_close(pctx);
1498 static void
1499 print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
1500 struct flagtext *p;
1501 isc_boolean_t first = ISC_TRUE;
1502 for (p = flagtexts; p->flag != 0; p++) {
1503 if ((flags & p->flag) != 0) {
1504 if (first)
1505 cfg_print_chars(pctx, " // ", 4);
1506 else
1507 cfg_print_chars(pctx, ", ", 2);
1508 cfg_print_cstr(pctx, p->text);
1509 first = ISC_FALSE;
1514 void
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);
1530 print_open(pctx);
1532 for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1533 for (clause = *clauseset;
1534 clause->name != NULL;
1535 clause++) {
1536 print_indent(pctx);
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);
1546 print_close(pctx);
1549 isc_boolean_t
1550 cfg_obj_ismap(const cfg_obj_t *obj) {
1551 REQUIRE(obj != NULL);
1552 return (ISC_TF(obj->type->rep == &cfg_rep_map));
1555 isc_result_t
1556 cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
1557 isc_result_t result;
1558 isc_symvalue_t val;
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)
1569 return (result);
1570 *obj = val.as_pointer;
1571 return (ISC_R_SUCCESS);
1574 const cfg_obj_t *
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. */
1582 static isc_result_t
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;
1586 isc_region_t r;
1588 UNUSED(type);
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);
1594 result = ISC_R_EOF;
1595 goto cleanup;
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;
1603 goto cleanup;
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';
1608 *ret = obj;
1609 return (result);
1611 cleanup:
1612 if (obj != NULL)
1613 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
1614 return (result);
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.
1627 static isc_result_t
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;
1631 int braces = 0;
1633 CHECK(cfg_create_list(pctx, type, &listobj));
1635 for (;;) {
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 == '{')
1641 braces++;
1642 else if (pctx->token.value.as_char == '}')
1643 braces--;
1644 else if (pctx->token.value.as_char == ';')
1645 if (braces == 0)
1646 break;
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;
1651 goto cleanup;
1654 CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
1655 ISC_LIST_APPEND(listobj->value.list, elt, link);
1657 INSIST(braces == 0);
1658 *ret = listobj;
1659 return (ISC_R_SUCCESS);
1661 cleanup:
1662 CLEANUP_OBJ(listobj);
1663 return (result);
1666 cfg_type_t cfg_type_unsupported = {
1667 "unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal,
1668 &cfg_rep_list, NULL
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.
1680 static isc_result_t
1681 token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1682 char *s;
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);
1697 } else {
1698 INSIST(0);
1700 } else {
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 &&
1708 strlen(s) <= 15U) {
1709 char buf[64];
1710 int i;
1712 strcpy(buf, s);
1713 for (i = 0; i < 3; i++) {
1714 strcat(buf, ".0");
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 */
1727 strcpy(buf, s);
1728 d = strchr(buf, '%');
1729 if (d != NULL)
1730 *d = '\0';
1732 if (inet_pton(AF_INET6, buf, &in6a) == 1) {
1733 if (d != NULL) {
1734 #ifdef ISC_PLATFORM_HAVESCOPEID
1735 isc_result_t result;
1737 result = isc_netscope_pton(AF_INET6,
1738 d + 1,
1739 &in6a,
1740 &zone);
1741 if (result != ISC_R_SUCCESS)
1742 return (result);
1743 #else
1744 return (ISC_R_BADADDRESSFORM);
1745 #endif
1748 isc_netaddr_fromin6(na, &in6a);
1749 isc_netaddr_setzone(na, zone);
1750 return (ISC_R_SUCCESS);
1754 return (ISC_R_UNEXPECTEDTOKEN);
1757 isc_result_t
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)
1767 wild = " or '*'";
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",
1773 prefix, wild);
1774 else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK)
1775 cfg_parser_error(pctx, CFG_LOG_NEAR,
1776 "expected IPv6 address%s%s",
1777 prefix, wild);
1778 else
1779 cfg_parser_error(pctx, CFG_LOG_NEAR,
1780 "expected IP address%s%s",
1781 prefix, wild);
1783 cleanup:
1784 return (result);
1787 isc_boolean_t
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));
1795 isc_result_t
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) {
1804 *port = 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);
1819 cleanup:
1820 return (result);
1823 void
1824 cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
1825 isc_result_t result;
1826 char text[128];
1827 isc_buffer_t buf;
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));
1835 /* netaddr */
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;
1843 static isc_result_t
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);
1853 *ret = obj;
1854 return (ISC_R_SUCCESS);
1855 cleanup:
1856 CLEANUP_OBJ(obj);
1857 return (result);
1860 static void
1861 cfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
1862 const unsigned int *flagp = type->of;
1863 int n = 0;
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>");
1868 n++;
1870 if (*flagp & CFG_ADDR_V6OK) {
1871 if (n != 0)
1872 cfg_print_chars(pctx, " | ", 3);
1873 cfg_print_cstr(pctx, "<ipv6_address>");
1874 n++;
1876 if (*flagp & CFG_ADDR_WILDOK) {
1877 if (n != 0)
1878 cfg_print_chars(pctx, " | ", 3);
1879 cfg_print_chars(pctx, "*", 1);
1880 n++;
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
1911 /* netprefix */
1913 isc_result_t
1914 cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
1915 cfg_obj_t **ret)
1917 cfg_obj_t *obj = NULL;
1918 isc_result_t result;
1919 isc_netaddr_t netaddr;
1920 unsigned int addrlen, prefixlen;
1921 UNUSED(type);
1923 CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
1924 CFG_ADDR_V6OK, &netaddr));
1925 switch (netaddr.family) {
1926 case AF_INET:
1927 addrlen = 32;
1928 break;
1929 case AF_INET6:
1930 addrlen = 128;
1931 break;
1932 default:
1933 addrlen = 0;
1934 INSIST(0);
1935 break;
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);
1953 } else {
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;
1959 *ret = obj;
1960 return (ISC_R_SUCCESS);
1961 cleanup:
1962 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
1963 return (result);
1966 static void
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);
1975 isc_boolean_t
1976 cfg_obj_isnetprefix(const cfg_obj_t *obj) {
1977 REQUIRE(obj != NULL);
1978 return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
1981 void
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
1994 static isc_result_t
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;
2000 in_port_t port = 0;
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);
2012 *ret = obj;
2013 return (ISC_R_SUCCESS);
2015 cleanup:
2016 CLEANUP_OBJ(obj);
2017 return (result);
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
2026 isc_result_t
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));
2032 void
2033 cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2034 isc_netaddr_t netaddr;
2035 in_port_t port;
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);
2042 if (port != 0) {
2043 cfg_print_chars(pctx, " port ", 6);
2044 cfg_print_rawuint(pctx, port);
2048 void
2049 cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
2050 const unsigned int *flagp = type->of;
2051 int n = 0;
2052 cfg_print_chars(pctx, "( ", 2);
2053 if (*flagp & CFG_ADDR_V4OK) {
2054 cfg_print_cstr(pctx, "<ipv4_address>");
2055 n++;
2057 if (*flagp & CFG_ADDR_V6OK) {
2058 if (n != 0)
2059 cfg_print_chars(pctx, " | ", 3);
2060 cfg_print_cstr(pctx, "<ipv6_address>");
2061 n++;
2063 if (*flagp & CFG_ADDR_WILDOK) {
2064 if (n != 0)
2065 cfg_print_chars(pctx, " | ", 3);
2066 cfg_print_chars(pctx, "*", 1);
2067 n++;
2069 cfg_print_chars(pctx, " ) ", 3);
2070 if (*flagp & CFG_ADDR_WILDOK) {
2071 cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
2072 } else {
2073 cfg_print_cstr(pctx, "[ port <integer> ]");
2077 isc_boolean_t
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);
2089 isc_result_t
2090 cfg_gettoken(cfg_parser_t *pctx, int options) {
2091 isc_result_t result;
2093 if (pctx->seen_eof)
2094 return (ISC_R_SUCCESS);
2096 options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
2098 redo:
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);
2104 switch (result) {
2105 case ISC_R_SUCCESS:
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.
2115 cfg_listelt_t *elt;
2116 elt = ISC_LIST_TAIL(pctx->open_files->
2117 value.list);
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);
2123 goto redo;
2125 pctx->seen_eof = ISC_TRUE;
2127 break;
2129 case ISC_R_NOSPACE:
2130 /* More understandable than "ran out of space". */
2131 cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
2132 break;
2134 case ISC_R_IOERROR:
2135 cfg_parser_error(pctx, 0, "%s",
2136 isc_result_totext(result));
2137 break;
2139 default:
2140 cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
2141 isc_result_totext(result));
2142 break;
2144 return (result);
2147 void
2148 cfg_ungettoken(cfg_parser_t *pctx) {
2149 if (pctx->seen_eof)
2150 return;
2151 isc_lex_ungettoken(pctx->lexer, &pctx->token);
2152 pctx->ungotten = ISC_TRUE;
2155 isc_result_t
2156 cfg_peektoken(cfg_parser_t *pctx, int options) {
2157 isc_result_t result;
2158 CHECK(cfg_gettoken(pctx, options));
2159 cfg_ungettoken(pctx);
2160 cleanup:
2161 return (result);
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.
2168 static isc_result_t
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)
2174 return (result);
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);
2184 void
2185 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2186 va_list args;
2187 va_start(args, fmt);
2188 parser_complain(pctx, ISC_FALSE, flags, fmt, args);
2189 va_end(args);
2190 pctx->errors++;
2193 void
2194 cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2195 va_list args;
2196 va_start(args, fmt);
2197 parser_complain(pctx, ISC_TRUE, flags, fmt, args);
2198 va_end(args);
2199 pctx->warnings++;
2202 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
2204 static char *
2205 current_file(cfg_parser_t *pctx) {
2206 static char none[] = "none";
2207 cfg_listelt_t *elt;
2208 cfg_obj_t *fileobj;
2210 if (pctx->open_files == NULL)
2211 return (none);
2212 elt = ISC_LIST_TAIL(pctx->open_files->value.list);
2213 if (elt == NULL)
2214 return (none);
2216 fileobj = elt->obj;
2217 INSIST(fileobj->type == &cfg_type_qstring);
2218 return (fileobj->value.string.base);
2221 static void
2222 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
2223 unsigned int flags, const char *format,
2224 va_list args)
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 = "";
2231 size_t len;
2233 if (is_warning)
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) {
2245 isc_region_t r;
2247 if (pctx->ungotten)
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) {
2253 flags = 0;
2254 tokenbuf[0] = '\0';
2255 } else {
2256 isc_lex_getlasttokentext(pctx->lexer,
2257 &pctx->token, &r);
2258 if (r.length > MAX_LOG_TOKEN)
2259 snprintf(tokenbuf, sizeof(tokenbuf),
2260 "'%.*s...'", MAX_LOG_TOKEN, r.base);
2261 else
2262 snprintf(tokenbuf, sizeof(tokenbuf),
2263 "'%.*s'", (int)r.length, r.base);
2266 /* Choose a preposition. */
2267 if (flags & CFG_LOG_NEAR)
2268 prep = " near ";
2269 else if (flags & CFG_LOG_BEFORE)
2270 prep = " before ";
2271 else
2272 prep = " ";
2273 } else {
2274 tokenbuf[0] = '\0';
2276 isc_log_write(pctx->lctx, CAT, MOD, level,
2277 "%s%s%s%s", where, message, prep, tokenbuf);
2280 void
2281 cfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level,
2282 const char *fmt, ...) {
2283 va_list ap;
2284 char msgbuf[2048];
2286 if (! isc_log_wouldlog(lctx, level))
2287 return;
2289 va_start(ap, fmt);
2291 vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
2292 isc_log_write(lctx, CAT, MOD, level,
2293 "%s:%u: %s",
2294 obj->file == NULL ? "<unknown file>" : obj->file,
2295 obj->line, msgbuf);
2296 va_end(ap);
2299 const char *
2300 cfg_obj_file(const cfg_obj_t *obj) {
2301 return (obj->file);
2304 unsigned int
2305 cfg_obj_line(const cfg_obj_t *obj) {
2306 return (obj->line);
2309 isc_result_t
2310 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2311 cfg_obj_t *obj;
2313 obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
2314 if (obj == NULL)
2315 return (ISC_R_NOMEMORY);
2316 obj->type = type;
2317 obj->file = current_file(pctx);
2318 obj->line = pctx->line;
2319 *ret = obj;
2320 return (ISC_R_SUCCESS);
2323 static void
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;
2330 UNUSED(key);
2331 UNUSED(type);
2333 cfg_obj_destroy(pctx, &obj);
2337 static isc_result_t
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;
2350 *ret = obj;
2351 return (ISC_R_SUCCESS);
2353 cleanup:
2354 if (obj != NULL)
2355 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
2356 return (result);
2359 static void
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);
2365 isc_boolean_t
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'.
2373 void
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));
2378 *objp = NULL;
2381 static void
2382 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
2383 UNUSED(pctx);
2384 UNUSED(obj);
2387 void
2388 cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
2389 type->doc(pctx, type);
2392 void
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);
2399 void
2400 cfg_print_grammar(const cfg_type_t *type,
2401 void (*f)(void *closure, const char *text, int textlen),
2402 void *closure)
2404 cfg_printer_t pctx;
2405 pctx.f = f;
2406 pctx.closure = closure;
2407 pctx.indent = 0;
2408 cfg_doc_obj(&pctx, type);