Merge from vendor branch LESS:
[dragonfly.git] / contrib / bind-9.3 / lib / isccfg / parser.c
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
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.
16  */
17
18 /* $Id: parser.c,v 1.70.2.20.2.18 2004/05/15 03:46:13 jinmei Exp $ */
19
20 #include <config.h>
21
22 #include <isc/buffer.h>
23 #include <isc/dir.h>
24 #include <isc/formatcheck.h>
25 #include <isc/lex.h>
26 #include <isc/log.h>
27 #include <isc/mem.h>
28 #include <isc/net.h>
29 #include <isc/netaddr.h>
30 #include <isc/print.h>
31 #include <isc/string.h>
32 #include <isc/sockaddr.h>
33 #include <isc/netscope.h>
34 #include <isc/util.h>
35 #include <isc/symtab.h>
36
37 #include <isccfg/cfg.h>
38 #include <isccfg/grammar.h>
39 #include <isccfg/log.h>
40
41 /* Shorthand */
42 #define CAT CFG_LOGCATEGORY_CONFIG
43 #define MOD CFG_LOGMODULE_PARSER
44
45 #define MAP_SYM 1       /* Unique type for isc_symtab */
46
47 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
48
49 /* Check a return value. */
50 #define CHECK(op)                                               \
51         do { result = (op);                                     \
52                 if (result != ISC_R_SUCCESS) goto cleanup;      \
53         } while (0)
54
55 /* Clean up a configuration object if non-NULL. */
56 #define CLEANUP_OBJ(obj) \
57         do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
58
59
60 /*
61  * Forward declarations of static functions.
62  */
63
64 static void
65 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
66
67 static isc_result_t
68 parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
69
70 static void
71 print_list(cfg_printer_t *pctx, cfg_obj_t *obj);
72
73 static void
74 free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
75
76 static isc_result_t
77 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
78
79 static isc_result_t
80 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
81               cfg_obj_t **ret);
82
83 static void
84 free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
85
86 static isc_result_t
87 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
88
89 static void
90 free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
91
92 static isc_result_t
93 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
94                  cfg_type_t *elttype, isc_symtab_t *symtab,
95                  isc_boolean_t callback);
96
97 static void
98 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
99
100 static isc_result_t
101 cfg_getstringtoken(cfg_parser_t *pctx);
102
103 static void
104 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
105                 unsigned int flags, const char *format, va_list args);
106
107 /*
108  * Data representations.  These correspond to members of the
109  * "value" union in struct cfg_obj (except "void", which does
110  * not need a union member).
111  */
112
113 cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
114 cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
115 cfg_rep_t cfg_rep_string = { "string", free_string };
116 cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
117 cfg_rep_t cfg_rep_map = { "map", free_map };
118 cfg_rep_t cfg_rep_list = { "list", free_list };
119 cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
120 cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
121 cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
122 cfg_rep_t cfg_rep_void = { "void", free_noop };
123
124 /*
125  * Configuration type definitions.
126  */
127
128 /*
129  * An implicit list.  These are formed by clauses that occur multiple times.
130  */
131 static cfg_type_t cfg_type_implicitlist = {
132         "implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL };
133
134 /* Functions. */
135
136 void
137 cfg_print_obj(cfg_printer_t *pctx, cfg_obj_t *obj) {
138         obj->type->print(pctx, obj);
139 }
140
141 void
142 cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
143         pctx->f(pctx->closure, text, len);
144 }
145
146 static void
147 print_open(cfg_printer_t *pctx) {
148         cfg_print_chars(pctx, "{\n", 2);
149         pctx->indent++;
150 }
151
152 static void
153 print_indent(cfg_printer_t *pctx) {
154         int indent = pctx->indent;
155         while (indent > 0) {
156                 cfg_print_chars(pctx, "\t", 1);
157                 indent--;
158         }
159 }
160
161 static void
162 print_close(cfg_printer_t *pctx) {
163         pctx->indent--;
164         print_indent(pctx);
165         cfg_print_chars(pctx, "}", 1);
166 }
167
168 isc_result_t
169 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
170         isc_result_t result;
171         INSIST(ret != NULL && *ret == NULL);
172         result = type->parse(pctx, type, ret);
173         if (result != ISC_R_SUCCESS)
174                 return (result);
175         INSIST(*ret != NULL);
176         return (ISC_R_SUCCESS);
177 }
178
179 void
180 cfg_print(cfg_obj_t *obj,
181           void (*f)(void *closure, const char *text, int textlen),
182           void *closure)
183 {
184         cfg_printer_t pctx;
185         pctx.f = f;
186         pctx.closure = closure;
187         pctx.indent = 0;
188         obj->type->print(&pctx, obj);
189 }
190
191
192 /* Tuples. */
193   
194 isc_result_t
195 cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
196         isc_result_t result;
197         const cfg_tuplefielddef_t *fields = type->of;
198         const cfg_tuplefielddef_t *f;
199         cfg_obj_t *obj = NULL;
200         unsigned int nfields = 0;
201         int i;
202
203         for (f = fields; f->name != NULL; f++)
204                 nfields++;
205
206         CHECK(cfg_create_obj(pctx, type, &obj));
207         obj->value.tuple = isc_mem_get(pctx->mctx,
208                                        nfields * sizeof(cfg_obj_t *));
209         if (obj->value.tuple == NULL) {
210                 result = ISC_R_NOMEMORY;
211                 goto cleanup;
212         }
213         for (f = fields, i = 0; f->name != NULL; f++, i++)
214                 obj->value.tuple[i] = NULL;
215         *ret = obj;
216         return (ISC_R_SUCCESS);
217
218  cleanup:
219         if (obj != NULL)
220                 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
221         return (result);
222 }
223
224 isc_result_t
225 cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
226 {
227         isc_result_t result;
228         const cfg_tuplefielddef_t *fields = type->of;
229         const cfg_tuplefielddef_t *f;
230         cfg_obj_t *obj = NULL;
231         unsigned int i;
232
233         CHECK(cfg_create_tuple(pctx, type, &obj));
234         for (f = fields, i = 0; f->name != NULL; f++, i++)
235                 CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
236
237         *ret = obj;
238         return (ISC_R_SUCCESS);
239
240  cleanup:
241         CLEANUP_OBJ(obj);
242         return (result);
243 }
244
245 void
246 cfg_print_tuple(cfg_printer_t *pctx, cfg_obj_t *obj) {
247         unsigned int i;
248         const cfg_tuplefielddef_t *fields = obj->type->of;
249         const cfg_tuplefielddef_t *f;
250         isc_boolean_t need_space = ISC_FALSE;
251
252         for (f = fields, i = 0; f->name != NULL; f++, i++) {
253                 cfg_obj_t *fieldobj = obj->value.tuple[i];
254                 if (need_space)
255                         cfg_print_chars(pctx, " ", 1);
256                 cfg_print_obj(pctx, fieldobj);
257                 need_space = ISC_TF(fieldobj->type->print != cfg_print_void);
258         }
259 }
260
261 void
262 cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
263         const cfg_tuplefielddef_t *fields = type->of;
264         const cfg_tuplefielddef_t *f;
265         isc_boolean_t need_space = ISC_FALSE;
266
267         for (f = fields; f->name != NULL; f++) {
268                 if (need_space)
269                         cfg_print_chars(pctx, " ", 1);
270                 cfg_doc_obj(pctx, f->type);
271                 need_space = ISC_TF(f->type->print != cfg_print_void);
272         }
273 }
274
275 static void
276 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
277         unsigned int i;
278         const cfg_tuplefielddef_t *fields = obj->type->of;
279         const cfg_tuplefielddef_t *f;
280         unsigned int nfields = 0;
281
282         if (obj->value.tuple == NULL)
283                 return;
284
285         for (f = fields, i = 0; f->name != NULL; f++, i++) {
286                 CLEANUP_OBJ(obj->value.tuple[i]);
287                 nfields++;
288         }
289         isc_mem_put(pctx->mctx, obj->value.tuple,
290                     nfields * sizeof(cfg_obj_t *));
291 }
292
293 isc_boolean_t
294 cfg_obj_istuple(cfg_obj_t *obj) {
295         REQUIRE(obj != NULL);
296         return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
297 }
298
299 cfg_obj_t *
300 cfg_tuple_get(cfg_obj_t *tupleobj, const char* name) {
301         unsigned int i;
302         const cfg_tuplefielddef_t *fields;
303         const cfg_tuplefielddef_t *f;
304         
305         REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
306
307         fields = tupleobj->type->of;
308         for (f = fields, i = 0; f->name != NULL; f++, i++) {
309                 if (strcmp(f->name, name) == 0)
310                         return (tupleobj->value.tuple[i]);
311         }
312         INSIST(0);
313         return (NULL);
314 }
315
316 isc_result_t
317 cfg_parse_special(cfg_parser_t *pctx, int special) {
318         isc_result_t result;
319         CHECK(cfg_gettoken(pctx, 0));
320         if (pctx->token.type == isc_tokentype_special &&
321             pctx->token.value.as_char == special)
322                 return (ISC_R_SUCCESS);
323
324         cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
325         return (ISC_R_UNEXPECTEDTOKEN);
326  cleanup:
327         return (result);
328 }
329
330 /*
331  * Parse a required semicolon.  If it is not there, log
332  * an error and increment the error count but continue
333  * parsing.  Since the next token is pushed back,
334  * care must be taken to make sure it is eventually
335  * consumed or an infinite loop may result.
336  */
337 static isc_result_t
338 parse_semicolon(cfg_parser_t *pctx) {
339         isc_result_t result;
340         CHECK(cfg_gettoken(pctx, 0));
341         if (pctx->token.type == isc_tokentype_special &&
342             pctx->token.value.as_char == ';')
343                 return (ISC_R_SUCCESS);
344
345         cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
346         cfg_ungettoken(pctx);
347  cleanup:
348         return (result);
349 }
350
351 /*
352  * Parse EOF, logging and returning an error if not there.
353  */
354 static isc_result_t
355 parse_eof(cfg_parser_t *pctx) {
356         isc_result_t result;
357         CHECK(cfg_gettoken(pctx, 0));
358
359         if (pctx->token.type == isc_tokentype_eof)
360                 return (ISC_R_SUCCESS);
361
362         cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
363         return (ISC_R_UNEXPECTEDTOKEN);
364  cleanup:
365         return (result);
366 }
367
368 /* A list of files, used internally for pctx->files. */
369
370 static cfg_type_t cfg_type_filelist = {
371         "filelist", NULL, print_list, NULL, &cfg_rep_list,
372         &cfg_type_qstring
373 };
374
375 isc_result_t
376 cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
377         isc_result_t result;
378         cfg_parser_t *pctx;
379         isc_lexspecials_t specials;
380
381         REQUIRE(mctx != NULL);
382         REQUIRE(ret != NULL && *ret == NULL);
383
384         pctx = isc_mem_get(mctx, sizeof(*pctx));
385         if (pctx == NULL)
386                 return (ISC_R_NOMEMORY);
387
388         pctx->mctx = mctx;
389         pctx->lctx = lctx;
390         pctx->lexer = NULL;
391         pctx->seen_eof = ISC_FALSE;
392         pctx->ungotten = ISC_FALSE;
393         pctx->errors = 0;
394         pctx->warnings = 0;
395         pctx->open_files = NULL;
396         pctx->closed_files = NULL;
397         pctx->line = 0;
398         pctx->callback = NULL;
399         pctx->callbackarg = NULL;
400         pctx->token.type = isc_tokentype_unknown;
401
402         memset(specials, 0, sizeof(specials));
403         specials['{'] = 1;
404         specials['}'] = 1;
405         specials[';'] = 1;
406         specials['/'] = 1;
407         specials['"'] = 1;
408         specials['!'] = 1;
409
410         CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer));
411
412         isc_lex_setspecials(pctx->lexer, specials);
413         isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C |
414                                          ISC_LEXCOMMENT_CPLUSPLUS |
415                                          ISC_LEXCOMMENT_SHELL));
416
417         CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
418         CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
419
420         *ret = pctx;
421         return (ISC_R_SUCCESS);
422
423  cleanup:
424         if (pctx->lexer != NULL)
425                 isc_lex_destroy(&pctx->lexer);
426         CLEANUP_OBJ(pctx->open_files);
427         CLEANUP_OBJ(pctx->closed_files);
428         isc_mem_put(mctx, pctx, sizeof(*pctx));
429         return (result);
430 }
431
432 static isc_result_t
433 parser_openfile(cfg_parser_t *pctx, const char *filename) {
434         isc_result_t result;
435         cfg_listelt_t *elt = NULL;
436         cfg_obj_t *stringobj = NULL;
437
438         result = isc_lex_openfile(pctx->lexer, filename);
439         if (result != ISC_R_SUCCESS) {
440                 cfg_parser_error(pctx, 0, "open: %s: %s",
441                              filename, isc_result_totext(result));
442                 goto cleanup;
443         }
444
445         CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
446         CHECK(create_listelt(pctx, &elt));
447         elt->obj = stringobj;
448         ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
449
450         return (ISC_R_SUCCESS);
451  cleanup:
452         CLEANUP_OBJ(stringobj);
453         return (result);
454 }
455
456 void
457 cfg_parser_setcallback(cfg_parser_t *pctx,
458                        cfg_parsecallback_t callback,
459                        void *arg)
460 {
461         pctx->callback = callback;
462         pctx->callbackarg = arg;
463 }
464
465 /*
466  * Parse a configuration using a pctx where a lexer has already
467  * been set up with a source.
468  */
469 static isc_result_t
470 parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
471         isc_result_t result;
472         cfg_obj_t *obj = NULL;
473
474         result = cfg_parse_obj(pctx, type, &obj);
475
476         if (pctx->errors != 0) {
477                 /* Errors have been logged. */
478                 if (result == ISC_R_SUCCESS)
479                         result = ISC_R_FAILURE;
480                 goto cleanup;
481         }
482
483         if (result != ISC_R_SUCCESS) {
484                 /* Parsing failed but no errors have been logged. */
485                 cfg_parser_error(pctx, 0, "parsing failed");
486                 goto cleanup;
487         }
488
489         CHECK(parse_eof(pctx));
490
491         *ret = obj;
492         return (ISC_R_SUCCESS);
493
494  cleanup:
495         CLEANUP_OBJ(obj);
496         return (result);
497 }
498
499 isc_result_t
500 cfg_parse_file(cfg_parser_t *pctx, const char *filename,
501                const cfg_type_t *type, cfg_obj_t **ret)
502 {
503         isc_result_t result;
504
505         REQUIRE(filename != NULL);
506
507         CHECK(parser_openfile(pctx, filename));
508         CHECK(parse2(pctx, type, ret));
509  cleanup:
510         return (result);
511 }
512
513
514 isc_result_t
515 cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
516         const cfg_type_t *type, cfg_obj_t **ret)
517 {
518         isc_result_t result;
519         REQUIRE(buffer != NULL);
520         CHECK(isc_lex_openbuffer(pctx->lexer, buffer)); 
521         CHECK(parse2(pctx, type, ret));
522  cleanup:
523         return (result);
524 }
525
526 void
527 cfg_parser_destroy(cfg_parser_t **pctxp) {
528         cfg_parser_t *pctx = *pctxp;
529         isc_lex_destroy(&pctx->lexer);
530         /*
531          * Cleaning up open_files does not
532          * close the files; that was already done
533          * by closing the lexer.
534          */
535         CLEANUP_OBJ(pctx->open_files);
536         CLEANUP_OBJ(pctx->closed_files);
537         isc_mem_put(pctx->mctx, pctx, sizeof(*pctx));
538         *pctxp = NULL;
539 }
540
541 /*
542  * void
543  */
544 isc_result_t
545 cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
546         UNUSED(type);
547         return (cfg_create_obj(pctx, &cfg_type_void, ret));
548 }
549
550 void
551 cfg_print_void(cfg_printer_t *pctx, cfg_obj_t *obj) {
552         UNUSED(pctx);
553         UNUSED(obj);
554 }
555
556 void
557 cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
558         UNUSED(pctx);
559         UNUSED(type);
560 }
561
562 isc_boolean_t
563 cfg_obj_isvoid(cfg_obj_t *obj) {
564         REQUIRE(obj != NULL);
565         return (ISC_TF(obj->type->rep == &cfg_rep_void));
566 }
567
568 cfg_type_t cfg_type_void = {
569         "void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
570         NULL };
571
572
573 /*
574  * uint32
575  */
576 isc_result_t
577 cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
578         isc_result_t result;
579         cfg_obj_t *obj = NULL;
580         UNUSED(type);
581
582         CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
583         if (pctx->token.type != isc_tokentype_number) {
584                 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
585                 return (ISC_R_UNEXPECTEDTOKEN);
586         }
587
588         CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
589
590         obj->value.uint32 = pctx->token.value.as_ulong;
591         *ret = obj;
592  cleanup:
593         return (result);
594 }
595
596 void
597 cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
598         cfg_print_chars(pctx, s, strlen(s));
599 }
600
601 void
602 cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
603         char buf[32];
604         snprintf(buf, sizeof(buf), "%u", u);
605         cfg_print_cstr(pctx, buf);
606 }
607
608 void
609 cfg_print_uint32(cfg_printer_t *pctx, cfg_obj_t *obj) {
610         cfg_print_rawuint(pctx, obj->value.uint32);
611 }
612
613 isc_boolean_t
614 cfg_obj_isuint32(cfg_obj_t *obj) {
615         REQUIRE(obj != NULL);
616         return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
617 }
618
619 isc_uint32_t
620 cfg_obj_asuint32(cfg_obj_t *obj) {
621         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
622         return (obj->value.uint32);
623 }
624
625 cfg_type_t cfg_type_uint32 = {
626         "integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
627         &cfg_rep_uint32, NULL
628 };
629
630
631 /*
632  * uint64
633  */
634 isc_boolean_t
635 cfg_obj_isuint64(cfg_obj_t *obj) {
636         REQUIRE(obj != NULL);
637         return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
638 }
639
640 isc_uint64_t
641 cfg_obj_asuint64(cfg_obj_t *obj) {
642         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
643         return (obj->value.uint64);
644 }
645
646 void
647 cfg_print_uint64(cfg_printer_t *pctx, cfg_obj_t *obj) {
648         char buf[32];
649         snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
650                  obj->value.uint64);
651         cfg_print_cstr(pctx, buf);
652 }
653
654 cfg_type_t cfg_type_uint64 = {
655         "64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
656         &cfg_rep_uint64, NULL
657 };
658
659 /*
660  * qstring (quoted string), ustring (unquoted string), astring
661  * (any string)
662  */
663
664 /* Create a string object from a null-terminated C string. */
665 static isc_result_t
666 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
667               cfg_obj_t **ret)
668 {
669         isc_result_t result;
670         cfg_obj_t *obj = NULL;
671         int len;
672
673         CHECK(cfg_create_obj(pctx, type, &obj));
674         len = strlen(contents);
675         obj->value.string.length = len;
676         obj->value.string.base = isc_mem_get(pctx->mctx, len + 1);
677         if (obj->value.string.base == 0) {
678                 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
679                 return (ISC_R_NOMEMORY);
680         }
681         memcpy(obj->value.string.base, contents, len);
682         obj->value.string.base[len] = '\0';
683
684         *ret = obj;
685  cleanup:
686         return (result);
687 }
688
689 isc_result_t
690 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
691         isc_result_t result;
692         UNUSED(type);
693
694         CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
695         if (pctx->token.type != isc_tokentype_qstring) {
696                 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
697                 return (ISC_R_UNEXPECTEDTOKEN);
698         }
699         return (create_string(pctx,
700                               TOKEN_STRING(pctx),
701                               &cfg_type_qstring,
702                               ret));
703  cleanup:
704         return (result);
705 }
706
707 static isc_result_t
708 parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
709         isc_result_t result;
710         UNUSED(type);
711
712         CHECK(cfg_gettoken(pctx, 0));
713         if (pctx->token.type != isc_tokentype_string) {
714                 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
715                 return (ISC_R_UNEXPECTEDTOKEN);
716         }
717         return (create_string(pctx,
718                               TOKEN_STRING(pctx),
719                               &cfg_type_ustring,
720                               ret));
721  cleanup:
722         return (result);
723 }
724
725 isc_result_t
726 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
727         isc_result_t result;
728         UNUSED(type);
729
730         CHECK(cfg_getstringtoken(pctx));
731         return (create_string(pctx,
732                               TOKEN_STRING(pctx),
733                               &cfg_type_qstring,
734                               ret));
735  cleanup:
736         return (result);
737 }
738
739 isc_boolean_t
740 cfg_is_enum(const char *s, const char *const *enums) {
741         const char * const *p;
742         for (p = enums; *p != NULL; p++) {
743                 if (strcasecmp(*p, s) == 0)
744                         return (ISC_TRUE);
745         }
746         return (ISC_FALSE);
747 }
748
749 static isc_result_t
750 check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
751         const char *s = obj->value.string.base;
752         if (cfg_is_enum(s, enums))
753                 return (ISC_R_SUCCESS);
754         cfg_parser_error(pctx, 0, "'%s' unexpected", s);
755         return (ISC_R_UNEXPECTEDTOKEN);
756 }
757
758 isc_result_t
759 cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
760         isc_result_t result;
761         cfg_obj_t *obj = NULL;
762         CHECK(parse_ustring(pctx, NULL, &obj));
763         CHECK(check_enum(pctx, obj, type->of));
764         *ret = obj;
765         return (ISC_R_SUCCESS);
766  cleanup:
767         CLEANUP_OBJ(obj);       
768         return (result);
769 }
770
771 void
772 cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
773         const char * const *p;
774         cfg_print_chars(pctx, "( ", 2);
775         for (p = type->of; *p != NULL; p++) {
776                 cfg_print_cstr(pctx, *p);
777                 if (p[1] != NULL)
778                         cfg_print_chars(pctx, " | ", 3);
779         }
780         cfg_print_chars(pctx, " )", 2);
781 }
782
783 void
784 cfg_print_ustring(cfg_printer_t *pctx, cfg_obj_t *obj) {
785         cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
786 }
787
788 static void
789 print_qstring(cfg_printer_t *pctx, cfg_obj_t *obj) {
790         cfg_print_chars(pctx, "\"", 1);
791         cfg_print_ustring(pctx, obj);
792         cfg_print_chars(pctx, "\"", 1);
793 }
794
795 static void
796 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
797         isc_mem_put(pctx->mctx, obj->value.string.base,
798                     obj->value.string.length + 1);
799 }
800
801 isc_boolean_t
802 cfg_obj_isstring(cfg_obj_t *obj) {
803         REQUIRE(obj != NULL);
804         return (ISC_TF(obj->type->rep == &cfg_rep_string));
805 }
806
807 char *
808 cfg_obj_asstring(cfg_obj_t *obj) {
809         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
810         return (obj->value.string.base);
811 }
812
813 /* Quoted string only */
814 cfg_type_t cfg_type_qstring = {
815         "quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal,
816         &cfg_rep_string, NULL
817 };
818
819 /* Unquoted string only */
820 cfg_type_t cfg_type_ustring = {
821         "string", parse_ustring, cfg_print_ustring, cfg_doc_terminal,
822         &cfg_rep_string, NULL
823 };
824
825 /* Any string (quoted or unquoted); printed with quotes */
826 cfg_type_t cfg_type_astring = {
827         "string", cfg_parse_astring, print_qstring, cfg_doc_terminal,
828         &cfg_rep_string, NULL
829 };
830
831 /*
832  * Booleans
833  */
834
835 isc_boolean_t
836 cfg_obj_isboolean(cfg_obj_t *obj) {
837         REQUIRE(obj != NULL);
838         return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
839 }
840
841 isc_boolean_t
842 cfg_obj_asboolean(cfg_obj_t *obj) {
843         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
844         return (obj->value.boolean);
845 }
846
847 static isc_result_t
848 parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
849 {
850         isc_result_t result;
851         isc_boolean_t value;
852         cfg_obj_t *obj = NULL;
853         UNUSED(type);
854
855         result = cfg_gettoken(pctx, 0);
856         if (result != ISC_R_SUCCESS)
857                 return (result);
858
859         if (pctx->token.type != isc_tokentype_string)
860                 goto bad_boolean;
861
862         if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
863             (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
864             (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
865                 value = ISC_TRUE;
866         } else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
867                    (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
868                    (strcmp(TOKEN_STRING(pctx), "0") == 0)) {
869                 value = ISC_FALSE;
870         } else {
871                 goto bad_boolean;
872         }
873
874         CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
875         obj->value.boolean = value;
876         *ret = obj;
877         return (result);
878
879  bad_boolean:
880         cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
881         return (ISC_R_UNEXPECTEDTOKEN);
882
883  cleanup:
884         return (result);
885 }
886
887 static void
888 print_boolean(cfg_printer_t *pctx, cfg_obj_t *obj) {
889         if (obj->value.boolean)
890                 cfg_print_chars(pctx, "yes", 3);
891         else
892                 cfg_print_chars(pctx, "no", 2);
893 }
894
895 cfg_type_t cfg_type_boolean = {
896         "boolean", parse_boolean, print_boolean, cfg_doc_terminal,
897         &cfg_rep_boolean, NULL
898 };
899
900 /*
901  * Lists.
902  */
903
904 isc_result_t
905 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
906         isc_result_t result;
907         CHECK(cfg_create_obj(pctx, type, obj));
908         ISC_LIST_INIT((*obj)->value.list);
909  cleanup:
910         return (result);
911 }
912
913 static isc_result_t
914 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
915         cfg_listelt_t *elt;
916         elt = isc_mem_get(pctx->mctx, sizeof(*elt));
917         if (elt == NULL)
918                 return (ISC_R_NOMEMORY);
919         elt->obj = NULL;
920         ISC_LINK_INIT(elt, link);
921         *eltp = elt;
922         return (ISC_R_SUCCESS);
923 }
924
925 static void
926 free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
927         cfg_obj_destroy(pctx, &elt->obj);
928         isc_mem_put(pctx->mctx, elt, sizeof(*elt));
929 }
930
931 static void
932 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
933         cfg_listelt_t *elt, *next;
934         for (elt = ISC_LIST_HEAD(obj->value.list);
935              elt != NULL;
936              elt = next)
937         {
938                 next = ISC_LIST_NEXT(elt, link);
939                 free_list_elt(pctx, elt);
940         }
941 }
942
943 isc_result_t
944 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
945                   cfg_listelt_t **ret)
946 {
947         isc_result_t result;
948         cfg_listelt_t *elt = NULL;
949         cfg_obj_t *value = NULL;
950
951         CHECK(create_listelt(pctx, &elt));
952
953         result = cfg_parse_obj(pctx, elttype, &value);
954         if (result != ISC_R_SUCCESS)
955                 goto cleanup;
956
957         elt->obj = value;
958
959         *ret = elt;
960         return (ISC_R_SUCCESS);
961
962  cleanup:
963         isc_mem_put(pctx->mctx, elt, sizeof(*elt));
964         return (result);
965 }
966
967 /*
968  * Parse a homogeneous list whose elements are of type 'elttype'
969  * and where each element is terminated by a semicolon.
970  */
971 static isc_result_t
972 parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
973 {
974         cfg_obj_t *listobj = NULL;
975         const cfg_type_t *listof = listtype->of;
976         isc_result_t result;
977         cfg_listelt_t *elt = NULL;
978
979         CHECK(cfg_create_list(pctx, listtype, &listobj));
980
981         for (;;) {
982                 CHECK(cfg_peektoken(pctx, 0));
983                 if (pctx->token.type == isc_tokentype_special &&
984                     pctx->token.value.as_char == /*{*/ '}')
985                         break;
986                 CHECK(cfg_parse_listelt(pctx, listof, &elt));
987                 CHECK(parse_semicolon(pctx));
988                 ISC_LIST_APPEND(listobj->value.list, elt, link);
989                 elt = NULL;
990         }
991         *ret = listobj;
992         return (ISC_R_SUCCESS);
993
994  cleanup:
995         if (elt != NULL)
996                 free_list_elt(pctx, elt);
997         CLEANUP_OBJ(listobj);
998         return (result);
999 }
1000
1001 static void
1002 print_list(cfg_printer_t *pctx, cfg_obj_t *obj) {
1003         cfg_list_t *list = &obj->value.list;
1004         cfg_listelt_t *elt;
1005
1006         for (elt = ISC_LIST_HEAD(*list);
1007              elt != NULL;
1008              elt = ISC_LIST_NEXT(elt, link)) {
1009                 print_indent(pctx);
1010                 cfg_print_obj(pctx, elt->obj);
1011                 cfg_print_chars(pctx, ";\n", 2);
1012         }
1013 }
1014
1015 isc_result_t
1016 cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
1017                      cfg_obj_t **ret)
1018 {
1019         isc_result_t result;
1020         CHECK(cfg_parse_special(pctx, '{'));
1021         CHECK(parse_list(pctx, type, ret));
1022         CHECK(cfg_parse_special(pctx, '}'));
1023  cleanup:
1024         return (result);
1025 }
1026
1027 void
1028 cfg_print_bracketed_list(cfg_printer_t *pctx, cfg_obj_t *obj) {
1029         print_open(pctx);
1030         print_list(pctx, obj);
1031         print_close(pctx);
1032 }
1033
1034 void
1035 cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
1036         cfg_print_chars(pctx, "{ ", 2);
1037         cfg_doc_obj(pctx, type->of);
1038         cfg_print_chars(pctx, "; ... }", 7);
1039 }
1040
1041 /*
1042  * Parse a homogeneous list whose elements are of type 'elttype'
1043  * and where elements are separated by space.  The list ends
1044  * before the first semicolon.
1045  */
1046 isc_result_t
1047 cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
1048                     cfg_obj_t **ret)
1049 {
1050         cfg_obj_t *listobj = NULL;
1051         const cfg_type_t *listof = listtype->of;
1052         isc_result_t result;
1053
1054         CHECK(cfg_create_list(pctx, listtype, &listobj));
1055
1056         for (;;) {
1057                 cfg_listelt_t *elt = NULL;
1058
1059                 CHECK(cfg_peektoken(pctx, 0));
1060                 if (pctx->token.type == isc_tokentype_special &&
1061                     pctx->token.value.as_char == ';')
1062                         break;
1063                 CHECK(cfg_parse_listelt(pctx, listof, &elt));
1064                 ISC_LIST_APPEND(listobj->value.list, elt, link);
1065         }
1066         *ret = listobj;
1067         return (ISC_R_SUCCESS);
1068
1069  cleanup:
1070         CLEANUP_OBJ(listobj);
1071         return (result);
1072 }
1073
1074 void
1075 cfg_print_spacelist(cfg_printer_t *pctx, cfg_obj_t *obj) {
1076         cfg_list_t *list = &obj->value.list;
1077         cfg_listelt_t *elt;
1078
1079         for (elt = ISC_LIST_HEAD(*list);
1080              elt != NULL;
1081              elt = ISC_LIST_NEXT(elt, link)) {
1082                 cfg_print_obj(pctx, elt->obj);
1083                 if (ISC_LIST_NEXT(elt, link) != NULL)
1084                         cfg_print_chars(pctx, " ", 1);
1085         }
1086 }
1087
1088
1089 isc_boolean_t
1090 cfg_obj_islist(cfg_obj_t *obj) {
1091         REQUIRE(obj != NULL);
1092         return (ISC_TF(obj->type->rep == &cfg_rep_list));
1093 }
1094
1095 cfg_listelt_t *
1096 cfg_list_first(cfg_obj_t *obj) {
1097         REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
1098         if (obj == NULL)
1099                 return (NULL);
1100         return (ISC_LIST_HEAD(obj->value.list));
1101 }
1102
1103 cfg_listelt_t *
1104 cfg_list_next(cfg_listelt_t *elt) {
1105         REQUIRE(elt != NULL);
1106         return (ISC_LIST_NEXT(elt, link));
1107 }
1108
1109 cfg_obj_t *
1110 cfg_listelt_value(cfg_listelt_t *elt) {
1111         REQUIRE(elt != NULL);
1112         return (elt->obj);
1113 }
1114
1115 /*
1116  * Maps.
1117  */
1118
1119 /*
1120  * Parse a map body.  That's something like
1121  *
1122  *   "foo 1; bar { glub; }; zap true; zap false;"
1123  *
1124  * i.e., a sequence of option names followed by values and
1125  * terminated by semicolons.  Used for the top level of
1126  * the named.conf syntax, as well as for the body of the
1127  * options, view, zone, and other statements.
1128  */
1129 isc_result_t
1130 cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1131 {
1132         const cfg_clausedef_t * const *clausesets = type->of;
1133         isc_result_t result;
1134         const cfg_clausedef_t * const *clauseset;
1135         const cfg_clausedef_t *clause;
1136         cfg_obj_t *value = NULL;
1137         cfg_obj_t *obj = NULL;
1138         cfg_obj_t *eltobj = NULL;
1139         cfg_obj_t *includename = NULL;
1140         isc_symvalue_t symval;
1141         cfg_list_t *list = NULL;
1142
1143         CHECK(create_map(pctx, type, &obj));
1144
1145         obj->value.map.clausesets = clausesets;
1146
1147         for (;;) {
1148                 cfg_listelt_t *elt;
1149
1150         redo:
1151                 /*
1152                  * Parse the option name and see if it is known.
1153                  */
1154                 CHECK(cfg_gettoken(pctx, 0));
1155
1156                 if (pctx->token.type != isc_tokentype_string) {
1157                         cfg_ungettoken(pctx);
1158                         break;
1159                 }
1160
1161                 /*
1162                  * We accept "include" statements wherever a map body
1163                  * clause can occur.
1164                  */
1165                 if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
1166                         /*
1167                          * Turn the file name into a temporary configuration
1168                          * object just so that it is not overwritten by the
1169                          * semicolon token.
1170                          */
1171                         CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
1172                         CHECK(parse_semicolon(pctx));
1173                         CHECK(parser_openfile(pctx, includename->
1174                                               value.string.base));
1175                          cfg_obj_destroy(pctx, &includename);
1176                          goto redo;
1177                 }
1178
1179                 clause = NULL;
1180                 for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
1181                         for (clause = *clauseset;
1182                              clause->name != NULL;
1183                              clause++) {
1184                                 if (strcasecmp(TOKEN_STRING(pctx),
1185                                            clause->name) == 0)
1186                                         goto done;
1187                         }
1188                 }
1189         done:
1190                 if (clause == NULL || clause->name == NULL) {
1191                         cfg_parser_error(pctx, CFG_LOG_NOPREP, "unknown option");
1192                         /*
1193                          * Try to recover by parsing this option as an unknown
1194                          * option and discarding it.
1195                          */
1196                         CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, &eltobj));
1197                         cfg_obj_destroy(pctx, &eltobj);
1198                         CHECK(parse_semicolon(pctx));
1199                         continue;
1200                 }
1201
1202                 /* Clause is known. */
1203
1204                 /* Issue warnings if appropriate */
1205                 if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0)
1206                         cfg_parser_warning(pctx, 0, "option '%s' is obsolete",
1207                                        clause->name);
1208                 if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0)
1209                         cfg_parser_warning(pctx, 0, "option '%s' is "
1210                                        "not implemented", clause->name);
1211                 if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0)
1212                         cfg_parser_warning(pctx, 0, "option '%s' is "
1213                                        "not implemented", clause->name);
1214                 /*
1215                  * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
1216                  * set here - we need to log the *lack* of such an option,
1217                  * not its presence.
1218                  */
1219
1220                 /* See if the clause already has a value; if not create one. */
1221                 result = isc_symtab_lookup(obj->value.map.symtab,
1222                                            clause->name, 0, &symval);
1223
1224                 if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
1225                         /* Multivalued clause */
1226                         cfg_obj_t *listobj = NULL;
1227                         if (result == ISC_R_NOTFOUND) {
1228                                 CHECK(cfg_create_list(pctx,
1229                                                   &cfg_type_implicitlist,
1230                                                   &listobj));
1231                                 symval.as_pointer = listobj;
1232                                 result = isc_symtab_define(obj->value.
1233                                                    map.symtab,
1234                                                    clause->name,
1235                                                    1, symval,
1236                                                    isc_symexists_reject);
1237                                 if (result != ISC_R_SUCCESS) {
1238                                         cfg_parser_error(pctx, CFG_LOG_NEAR,
1239                                                      "isc_symtab_define(%s) "
1240                                                      "failed", clause->name);
1241                                         isc_mem_put(pctx->mctx, list,
1242                                                     sizeof(cfg_list_t));
1243                                         goto cleanup;
1244                                 }
1245                         } else {
1246                                 INSIST(result == ISC_R_SUCCESS);
1247                                 listobj = symval.as_pointer;
1248                         }
1249
1250                         elt = NULL;
1251                         CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
1252                         CHECK(parse_semicolon(pctx));
1253
1254                         ISC_LIST_APPEND(listobj->value.list, elt, link);
1255                 } else {
1256                         /* Single-valued clause */
1257                         if (result == ISC_R_NOTFOUND) {
1258                                 isc_boolean_t callback =
1259                                         ISC_TF((clause->flags &
1260                                                 CFG_CLAUSEFLAG_CALLBACK) != 0);
1261                                 CHECK(parse_symtab_elt(pctx, clause->name,
1262                                                        clause->type,
1263                                                        obj->value.map.symtab,
1264                                                        callback));
1265                                 CHECK(parse_semicolon(pctx));
1266                         } else if (result == ISC_R_SUCCESS) {
1267                                 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
1268                                              clause->name);
1269                                 result = ISC_R_EXISTS;
1270                                 goto cleanup;
1271                         } else {
1272                                 cfg_parser_error(pctx, CFG_LOG_NEAR,
1273                                              "isc_symtab_define() failed");
1274                                 goto cleanup;
1275                         }
1276                 }
1277         }
1278
1279
1280         *ret = obj;
1281         return (ISC_R_SUCCESS);
1282
1283  cleanup:
1284         CLEANUP_OBJ(value);
1285         CLEANUP_OBJ(obj);
1286         CLEANUP_OBJ(eltobj);
1287         CLEANUP_OBJ(includename);
1288         return (result);
1289 }
1290
1291 static isc_result_t
1292 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
1293                  cfg_type_t *elttype, isc_symtab_t *symtab,
1294                  isc_boolean_t callback)
1295 {
1296         isc_result_t result;
1297         cfg_obj_t *obj = NULL;
1298         isc_symvalue_t symval;
1299
1300         CHECK(cfg_parse_obj(pctx, elttype, &obj));
1301
1302         if (callback && pctx->callback != NULL)
1303                 CHECK(pctx->callback(name, obj, pctx->callbackarg));
1304         
1305         symval.as_pointer = obj;
1306         CHECK(isc_symtab_define(symtab, name,
1307                                 1, symval,
1308                                 isc_symexists_reject));
1309         return (ISC_R_SUCCESS);
1310
1311  cleanup:
1312         CLEANUP_OBJ(obj);
1313         return (result);
1314 }
1315
1316 /*
1317  * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
1318  */
1319 isc_result_t
1320 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1321         isc_result_t result;
1322         CHECK(cfg_parse_special(pctx, '{'));
1323         CHECK(cfg_parse_mapbody(pctx, type, ret));
1324         CHECK(cfg_parse_special(pctx, '}'));
1325  cleanup:
1326         return (result);
1327 }
1328
1329 /*
1330  * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
1331  */
1332 static isc_result_t
1333 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type,
1334                     cfg_obj_t **ret)
1335 {
1336         isc_result_t result;
1337         cfg_obj_t *idobj = NULL;
1338         cfg_obj_t *mapobj = NULL;
1339
1340         CHECK(cfg_parse_obj(pctx, nametype, &idobj));
1341         CHECK(cfg_parse_map(pctx, type, &mapobj));
1342         mapobj->value.map.id = idobj;
1343         idobj = NULL;
1344         *ret = mapobj;
1345  cleanup:
1346         CLEANUP_OBJ(idobj);
1347         return (result);
1348 }
1349
1350 /*
1351  * Parse a map identified by a string name.  E.g., "name { foo 1; }".  
1352  * Used for the "key" and "channel" statements.
1353  */
1354 isc_result_t
1355 cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1356         return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
1357 }
1358
1359 /*
1360  * Parse a map identified by a network address.
1361  * Used for the "server" statement.
1362  */
1363 isc_result_t
1364 cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1365         return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
1366 }
1367
1368 void
1369 cfg_print_mapbody(cfg_printer_t *pctx, cfg_obj_t *obj) {
1370         isc_result_t result = ISC_R_SUCCESS;
1371
1372         const cfg_clausedef_t * const *clauseset;
1373
1374         for (clauseset = obj->value.map.clausesets;
1375              *clauseset != NULL;
1376              clauseset++)
1377         {
1378                 isc_symvalue_t symval;
1379                 const cfg_clausedef_t *clause;
1380
1381                 for (clause = *clauseset;
1382                      clause->name != NULL;
1383                      clause++) {
1384                         result = isc_symtab_lookup(obj->value.map.symtab,
1385                                                    clause->name, 0, &symval);
1386                         if (result == ISC_R_SUCCESS) {
1387                                 cfg_obj_t *obj = symval.as_pointer;
1388                                 if (obj->type == &cfg_type_implicitlist) {
1389                                         /* Multivalued. */
1390                                         cfg_list_t *list = &obj->value.list;
1391                                         cfg_listelt_t *elt;
1392                                         for (elt = ISC_LIST_HEAD(*list);
1393                                              elt != NULL;
1394                                              elt = ISC_LIST_NEXT(elt, link)) {
1395                                                 print_indent(pctx);
1396                                                 cfg_print_cstr(pctx, clause->name);
1397                                                 cfg_print_chars(pctx, " ", 1);
1398                                                 cfg_print_obj(pctx, elt->obj);
1399                                                 cfg_print_chars(pctx, ";\n", 2);
1400                                         }
1401                                 } else {
1402                                         /* Single-valued. */
1403                                         print_indent(pctx);
1404                                         cfg_print_cstr(pctx, clause->name);
1405                                         cfg_print_chars(pctx, " ", 1);
1406                                         cfg_print_obj(pctx, obj);
1407                                         cfg_print_chars(pctx, ";\n", 2);
1408                                 }
1409                         } else if (result == ISC_R_NOTFOUND) {
1410                                 ; /* do nothing */
1411                         } else {
1412                                 INSIST(0);
1413                         }
1414                 }
1415         }
1416 }
1417
1418 void
1419 cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
1420         const cfg_clausedef_t * const *clauseset;
1421         const cfg_clausedef_t *clause;
1422         
1423         for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1424                 for (clause = *clauseset;
1425                      clause->name != NULL;
1426                      clause++) {
1427                         cfg_print_cstr(pctx, clause->name);
1428                         cfg_print_chars(pctx, " ", 1);
1429                         cfg_doc_obj(pctx, clause->type);
1430                         cfg_print_chars(pctx, ";", 1);
1431                         /* XXX print flags here? */
1432                         cfg_print_chars(pctx, "\n\n", 2);
1433                 }
1434         }
1435 }
1436
1437 static struct flagtext {
1438         unsigned int flag;
1439         const char *text;
1440 } flagtexts[] = {
1441         { CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
1442         { CFG_CLAUSEFLAG_NYI, "not yet implemented" },
1443         { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
1444         { CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
1445         { 0, NULL }
1446 };
1447
1448 void
1449 cfg_print_map(cfg_printer_t *pctx, cfg_obj_t *obj) {
1450         if (obj->value.map.id != NULL) {
1451                 cfg_print_obj(pctx, obj->value.map.id);
1452                 cfg_print_chars(pctx, " ", 1);
1453         }
1454         print_open(pctx);
1455         cfg_print_mapbody(pctx, obj);
1456         print_close(pctx);
1457 }
1458
1459 static void
1460 print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
1461         struct flagtext *p;
1462         isc_boolean_t first = ISC_TRUE;
1463         for (p = flagtexts; p->flag != 0; p++) {
1464                 if ((flags & p->flag) != 0) {
1465                         if (first)
1466                                 cfg_print_chars(pctx, " // ", 4);
1467                         else
1468                                 cfg_print_chars(pctx, ", ", 2);
1469                         cfg_print_cstr(pctx, p->text);
1470                         first = ISC_FALSE;
1471                 }
1472         }
1473 }
1474
1475 void
1476 cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
1477         const cfg_clausedef_t * const *clauseset;
1478         const cfg_clausedef_t *clause;
1479         
1480         if (type->parse == cfg_parse_named_map) {
1481                 cfg_doc_obj(pctx, &cfg_type_astring);
1482                 cfg_print_chars(pctx, " ", 1);
1483         } else if (type->parse == cfg_parse_addressed_map) {
1484                 cfg_doc_obj(pctx, &cfg_type_netaddr);
1485                 cfg_print_chars(pctx, " ", 1);
1486         }
1487         
1488         print_open(pctx);
1489         
1490         for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1491                 for (clause = *clauseset;
1492                      clause->name != NULL;
1493                      clause++) {
1494                         print_indent(pctx);
1495                         cfg_print_cstr(pctx, clause->name);
1496                         if (clause->type->print != cfg_print_void)
1497                                 cfg_print_chars(pctx, " ", 1);
1498                         cfg_doc_obj(pctx, clause->type);
1499                         cfg_print_chars(pctx, ";", 1);
1500                         print_clause_flags(pctx, clause->flags);
1501                         cfg_print_chars(pctx, "\n", 1);
1502                 }
1503         }
1504         print_close(pctx);
1505 }
1506
1507 isc_boolean_t
1508 cfg_obj_ismap(cfg_obj_t *obj) {
1509         REQUIRE(obj != NULL);
1510         return (ISC_TF(obj->type->rep == &cfg_rep_map));
1511 }
1512
1513 isc_result_t
1514 cfg_map_get(cfg_obj_t *mapobj, const char* name, cfg_obj_t **obj) {
1515         isc_result_t result;
1516         isc_symvalue_t val;
1517         cfg_map_t *map;
1518         
1519         REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1520         REQUIRE(name != NULL);
1521         REQUIRE(obj != NULL && *obj == NULL);
1522
1523         map = &mapobj->value.map;
1524         
1525         result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
1526         if (result != ISC_R_SUCCESS)
1527                 return (result);
1528         *obj = val.as_pointer;
1529         return (ISC_R_SUCCESS);
1530 }
1531
1532 cfg_obj_t *
1533 cfg_map_getname(cfg_obj_t *mapobj) {
1534         REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1535         return (mapobj->value.map.id);
1536 }
1537
1538
1539 /* Parse an arbitrary token, storing its raw text representation. */
1540 static isc_result_t
1541 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1542         cfg_obj_t *obj = NULL;
1543         isc_result_t result;
1544         isc_region_t r;
1545
1546         UNUSED(type);
1547
1548         CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
1549         CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
1550         if (pctx->token.type == isc_tokentype_eof) {
1551                 cfg_ungettoken(pctx);
1552                 result = ISC_R_EOF;
1553                 goto cleanup;
1554         }
1555
1556         isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
1557
1558         obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
1559         obj->value.string.length = r.length;
1560         memcpy(obj->value.string.base, r.base, r.length);
1561         obj->value.string.base[r.length] = '\0';
1562         *ret = obj;
1563
1564  cleanup:
1565         return (result);
1566 }
1567
1568 cfg_type_t cfg_type_token = {
1569         "token", parse_token, cfg_print_ustring, cfg_doc_terminal,
1570         &cfg_rep_string, NULL
1571 };
1572
1573 /*
1574  * An unsupported option.  This is just a list of tokens with balanced braces
1575  * ending in a semicolon.
1576  */
1577
1578 static isc_result_t
1579 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1580         cfg_obj_t *listobj = NULL;
1581         isc_result_t result;
1582         int braces = 0;
1583
1584         CHECK(cfg_create_list(pctx, type, &listobj));
1585
1586         for (;;) {
1587                 cfg_listelt_t *elt = NULL;
1588
1589                 CHECK(cfg_peektoken(pctx, 0));
1590                 if (pctx->token.type == isc_tokentype_special) {
1591                         if (pctx->token.value.as_char == '{')
1592                                 braces++;
1593                         else if (pctx->token.value.as_char == '}')
1594                                 braces--;
1595                         else if (pctx->token.value.as_char == ';')
1596                                 if (braces == 0)
1597                                         break;
1598                 }
1599                 if (pctx->token.type == isc_tokentype_eof || braces < 0) {
1600                         cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
1601                         result = ISC_R_UNEXPECTEDTOKEN;
1602                         goto cleanup;
1603                 }
1604
1605                 CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
1606                 ISC_LIST_APPEND(listobj->value.list, elt, link);
1607         }
1608         INSIST(braces == 0);
1609         *ret = listobj;
1610         return (ISC_R_SUCCESS);
1611
1612  cleanup:
1613         CLEANUP_OBJ(listobj);
1614         return (result);
1615 }
1616
1617 cfg_type_t cfg_type_unsupported = {
1618         "unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal,
1619         &cfg_rep_list, NULL
1620 };
1621
1622 /*
1623  * Try interpreting the current token as a network address.
1624  *
1625  * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
1626  * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set.  The
1627  * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is 
1628  * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
1629  * and the IPv6 wildcard address otherwise.
1630  */
1631 static isc_result_t
1632 token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1633         char *s;
1634         struct in_addr in4a;
1635         struct in6_addr in6a;
1636
1637         if (pctx->token.type != isc_tokentype_string)
1638                 return (ISC_R_UNEXPECTEDTOKEN);
1639
1640         s = TOKEN_STRING(pctx);
1641         if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
1642                 if ((flags & CFG_ADDR_V4OK) != 0) {
1643                         isc_netaddr_any(na);
1644                         return (ISC_R_SUCCESS);
1645                 } else if ((flags & CFG_ADDR_V6OK) != 0) {
1646                         isc_netaddr_any6(na);
1647                         return (ISC_R_SUCCESS);
1648                 } else {
1649                         INSIST(0);
1650                 }
1651         } else {
1652                 if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
1653                         if (inet_pton(AF_INET, s, &in4a) == 1) {
1654                                 isc_netaddr_fromin(na, &in4a);
1655                                 return (ISC_R_SUCCESS);
1656                         }
1657                 }
1658                 if ((flags & CFG_ADDR_V4PREFIXOK) != 0 &&
1659                     strlen(s) <= 15U) {
1660                         char buf[64];
1661                         int i;
1662
1663                         strcpy(buf, s);
1664                         for (i = 0; i < 3; i++) {
1665                                 strcat(buf, ".0");
1666                                 if (inet_pton(AF_INET, buf, &in4a) == 1) {
1667                                         isc_netaddr_fromin(na, &in4a);
1668                                         return (ISC_R_SUCCESS);
1669                                 }
1670                         }
1671                 }
1672                 if ((flags & CFG_ADDR_V6OK) != 0 &&
1673                     strlen(s) <= 127U) {
1674                         char buf[128]; /* see lib/bind9/getaddresses.c */
1675                         char *d; /* zone delimiter */
1676                         isc_uint32_t zone = 0; /* scope zone ID */
1677
1678                         strcpy(buf, s);
1679                         d = strchr(buf, '%');
1680                         if (d != NULL)
1681                                 *d = '\0';
1682
1683                         if (inet_pton(AF_INET6, buf, &in6a) == 1) {
1684                                 if (d != NULL) {
1685 #ifdef ISC_PLATFORM_HAVESCOPEID
1686                                         isc_result_t result;
1687
1688                                         result = isc_netscope_pton(AF_INET6,
1689                                                                    d + 1,
1690                                                                    &in6a,
1691                                                                    &zone);
1692                                         if (result != ISC_R_SUCCESS)
1693                                                 return (result);
1694 #else
1695                                 return (ISC_R_BADADDRESSFORM);
1696 #endif
1697                                 }
1698
1699                                 isc_netaddr_fromin6(na, &in6a);
1700                                 isc_netaddr_setzone(na, zone);
1701                                 return (ISC_R_SUCCESS);
1702                         }
1703                 }
1704         }
1705         return (ISC_R_UNEXPECTEDTOKEN);
1706 }
1707
1708 isc_result_t
1709 cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1710         isc_result_t result;
1711         CHECK(cfg_gettoken(pctx, 0));
1712         result = token_addr(pctx, flags, na);
1713         if (result == ISC_R_UNEXPECTEDTOKEN)
1714                 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected IP address");
1715  cleanup:
1716         return (result);
1717 }
1718
1719 isc_boolean_t
1720 cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
1721         isc_result_t result;
1722         isc_netaddr_t na_dummy;
1723         result = token_addr(pctx, flags, &na_dummy);
1724         return (ISC_TF(result == ISC_R_SUCCESS));
1725 }
1726
1727 isc_result_t
1728 cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
1729         isc_result_t result;
1730
1731         CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1732
1733         if ((flags & CFG_ADDR_WILDOK) != 0 &&
1734             pctx->token.type == isc_tokentype_string &&
1735             strcmp(TOKEN_STRING(pctx), "*") == 0) {
1736                 *port = 0;
1737                 return (ISC_R_SUCCESS);
1738         }
1739         if (pctx->token.type != isc_tokentype_number) {
1740                 cfg_parser_error(pctx, CFG_LOG_NEAR,
1741                              "expected port number or '*'");
1742                 return (ISC_R_UNEXPECTEDTOKEN);
1743         }
1744         if (pctx->token.value.as_ulong >= 65536U) {
1745                 cfg_parser_error(pctx, CFG_LOG_NEAR,
1746                              "port number out of range");
1747                 return (ISC_R_UNEXPECTEDTOKEN);
1748         }
1749         *port = (in_port_t)(pctx->token.value.as_ulong);
1750         return (ISC_R_SUCCESS);
1751  cleanup:
1752         return (result);
1753 }
1754
1755 void
1756 cfg_print_rawaddr(cfg_printer_t *pctx, isc_netaddr_t *na) {
1757         isc_result_t result;
1758         char text[128];
1759         isc_buffer_t buf;
1760
1761         isc_buffer_init(&buf, text, sizeof(text));
1762         result = isc_netaddr_totext(na, &buf);
1763         RUNTIME_CHECK(result == ISC_R_SUCCESS);
1764         cfg_print_chars(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf));
1765 }
1766
1767 /* netaddr */
1768
1769 static isc_result_t
1770 parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1771         isc_result_t result;
1772         cfg_obj_t *obj = NULL;
1773         isc_netaddr_t netaddr;
1774         UNUSED(type);
1775         CHECK(cfg_create_obj(pctx, type, &obj));
1776         CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK, &netaddr));
1777         isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
1778         *ret = obj;
1779         return (ISC_R_SUCCESS);
1780  cleanup:
1781         CLEANUP_OBJ(obj);
1782         return (result);
1783 }
1784
1785 cfg_type_t cfg_type_netaddr = {
1786         "netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_terminal,
1787         &cfg_rep_sockaddr, NULL
1788 };
1789
1790 /* netprefix */
1791
1792 isc_result_t
1793 cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
1794                     cfg_obj_t **ret)
1795 {
1796         cfg_obj_t *obj = NULL;
1797         isc_result_t result;
1798         isc_netaddr_t netaddr;
1799         unsigned int addrlen, prefixlen;
1800         UNUSED(type);
1801
1802         CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
1803                                 CFG_ADDR_V6OK, &netaddr));
1804         switch (netaddr.family) {
1805         case AF_INET:
1806                 addrlen = 32;
1807                 break;
1808         case AF_INET6:
1809                 addrlen = 128;
1810                 break;
1811         default:
1812                 addrlen = 0;
1813                 INSIST(0);
1814                 break;
1815         }
1816         CHECK(cfg_peektoken(pctx, 0));
1817         if (pctx->token.type == isc_tokentype_special &&
1818             pctx->token.value.as_char == '/') {
1819                 CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
1820                 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1821                 if (pctx->token.type != isc_tokentype_number) {
1822                         cfg_parser_error(pctx, CFG_LOG_NEAR,
1823                                      "expected prefix length");
1824                         return (ISC_R_UNEXPECTEDTOKEN);
1825                 }
1826                 prefixlen = pctx->token.value.as_ulong;
1827                 if (prefixlen > addrlen) {
1828                         cfg_parser_error(pctx, CFG_LOG_NOPREP,
1829                                      "invalid prefix length");
1830                         return (ISC_R_RANGE);
1831                 }
1832         } else {
1833                 prefixlen = addrlen;
1834         }
1835         CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
1836         obj->value.netprefix.address = netaddr;
1837         obj->value.netprefix.prefixlen = prefixlen;
1838         *ret = obj;
1839         return (ISC_R_SUCCESS);
1840  cleanup:
1841         cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
1842         return (result);
1843 }
1844
1845 static void
1846 print_netprefix(cfg_printer_t *pctx, cfg_obj_t *obj) {
1847         cfg_netprefix_t *p = &obj->value.netprefix;
1848         cfg_print_rawaddr(pctx, &p->address);
1849         cfg_print_chars(pctx, "/", 1);
1850         cfg_print_rawuint(pctx, p->prefixlen);
1851 }
1852
1853 isc_boolean_t
1854 cfg_obj_isnetprefix(cfg_obj_t *obj) {
1855         REQUIRE(obj != NULL);
1856         return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
1857 }
1858
1859 void
1860 cfg_obj_asnetprefix(cfg_obj_t *obj, isc_netaddr_t *netaddr,
1861                     unsigned int *prefixlen) {
1862         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
1863         *netaddr = obj->value.netprefix.address;
1864         *prefixlen = obj->value.netprefix.prefixlen;
1865 }
1866
1867 cfg_type_t cfg_type_netprefix = {
1868         "netprefix", cfg_parse_netprefix, print_netprefix, cfg_doc_terminal,
1869         &cfg_rep_netprefix, NULL
1870 };
1871
1872 static isc_result_t
1873 parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type,
1874                   int flags, cfg_obj_t **ret)
1875 {
1876         isc_result_t result;
1877         isc_netaddr_t netaddr;
1878         in_port_t port = 0;
1879         cfg_obj_t *obj = NULL;
1880
1881         CHECK(cfg_create_obj(pctx, type, &obj));
1882         CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
1883         CHECK(cfg_peektoken(pctx, 0));
1884         if (pctx->token.type == isc_tokentype_string &&
1885             strcasecmp(TOKEN_STRING(pctx), "port") == 0) {
1886                 CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
1887                 CHECK(cfg_parse_rawport(pctx, flags, &port));
1888         }
1889         isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
1890         *ret = obj;
1891         return (ISC_R_SUCCESS);
1892
1893  cleanup:
1894         CLEANUP_OBJ(obj);
1895         return (result);
1896 }
1897
1898 static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
1899 cfg_type_t cfg_type_sockaddr = {
1900         "sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr,
1901         &cfg_rep_sockaddr, &sockaddr_flags
1902 };
1903
1904 isc_result_t
1905 cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1906         const unsigned int *flagp = type->of;
1907         return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret));
1908 }
1909
1910 void
1911 cfg_print_sockaddr(cfg_printer_t *pctx, cfg_obj_t *obj) {
1912         isc_netaddr_t netaddr;
1913         in_port_t port;
1914         char buf[ISC_NETADDR_FORMATSIZE];
1915
1916         isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
1917         isc_netaddr_format(&netaddr, buf, sizeof(buf));
1918         cfg_print_cstr(pctx, buf);
1919         port = isc_sockaddr_getport(&obj->value.sockaddr);
1920         if (port != 0) {
1921                 cfg_print_chars(pctx, " port ", 6);
1922                 cfg_print_rawuint(pctx, port);
1923         }
1924 }
1925
1926 void
1927 cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
1928         const unsigned int *flagp = type->of;
1929         int n = 0;
1930         cfg_print_chars(pctx, "( ", 2);
1931         if (*flagp & CFG_ADDR_V4OK) {
1932                 if (n != 0)
1933                         cfg_print_chars(pctx, " | ", 3);
1934                 cfg_print_cstr(pctx, "<ipv4_address>");
1935                 n++;
1936         }
1937         if (*flagp & CFG_ADDR_V6OK) {
1938                 if (n != 0)
1939                         cfg_print_chars(pctx, " | ", 3);
1940                 cfg_print_cstr(pctx, "<ipv6_address>");
1941                 n++;                    
1942         }
1943         if (*flagp & CFG_ADDR_WILDOK) {
1944                 if (n != 0)
1945                         cfg_print_chars(pctx, " | ", 3);
1946                 cfg_print_chars(pctx, "*", 1);
1947                 n++;
1948         }
1949         cfg_print_chars(pctx, " ) ", 3);
1950         if (*flagp & CFG_ADDR_WILDOK) {
1951                 cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
1952         } else {
1953                 cfg_print_cstr(pctx, "[ port <integer> ]");
1954         }
1955 }
1956
1957 isc_boolean_t
1958 cfg_obj_issockaddr(cfg_obj_t *obj) {
1959         REQUIRE(obj != NULL);
1960         return (ISC_TF(obj->type->rep == &cfg_rep_sockaddr));
1961 }
1962
1963 isc_sockaddr_t *
1964 cfg_obj_assockaddr(cfg_obj_t *obj) {
1965         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
1966         return (&obj->value.sockaddr);
1967 }
1968
1969 isc_result_t
1970 cfg_gettoken(cfg_parser_t *pctx, int options) {
1971         isc_result_t result;
1972
1973         if (pctx->seen_eof)
1974                 return (ISC_R_SUCCESS);
1975
1976         options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
1977
1978  redo:
1979         pctx->token.type = isc_tokentype_unknown;
1980         result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
1981         pctx->ungotten = ISC_FALSE;
1982         pctx->line = isc_lex_getsourceline(pctx->lexer);
1983
1984         switch (result) {
1985         case ISC_R_SUCCESS:
1986                 if (pctx->token.type == isc_tokentype_eof) {
1987                         result = isc_lex_close(pctx->lexer);
1988                         INSIST(result == ISC_R_NOMORE ||
1989                                result == ISC_R_SUCCESS);
1990
1991                         if (isc_lex_getsourcename(pctx->lexer) != NULL) {
1992                                 /*
1993                                  * Closed an included file, not the main file.
1994                                  */
1995                                 cfg_listelt_t *elt;
1996                                 elt = ISC_LIST_TAIL(pctx->open_files->
1997                                                     value.list);
1998                                 INSIST(elt != NULL);
1999                                 ISC_LIST_UNLINK(pctx->open_files->
2000                                                 value.list, elt, link);
2001                                 ISC_LIST_APPEND(pctx->closed_files->
2002                                                 value.list, elt, link);
2003                                 goto redo;
2004                         }
2005                         pctx->seen_eof = ISC_TRUE;
2006                 }
2007                 break;
2008
2009         case ISC_R_NOSPACE:
2010                 /* More understandable than "ran out of space". */
2011                 cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
2012                 break;
2013
2014         case ISC_R_IOERROR:
2015                 cfg_parser_error(pctx, 0, "%s",
2016                                  isc_result_totext(result));
2017                 break;
2018
2019         default:
2020                 cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
2021                                  isc_result_totext(result));
2022                 break;
2023         }
2024         return (result);
2025 }
2026
2027 void
2028 cfg_ungettoken(cfg_parser_t *pctx) {
2029         if (pctx->seen_eof)
2030                 return;
2031         isc_lex_ungettoken(pctx->lexer, &pctx->token);
2032         pctx->ungotten = ISC_TRUE;
2033 }
2034
2035 isc_result_t
2036 cfg_peektoken(cfg_parser_t *pctx, int options) {
2037         isc_result_t result;
2038         CHECK(cfg_gettoken(pctx, options));
2039         cfg_ungettoken(pctx);
2040  cleanup:
2041         return (result);
2042 }
2043
2044 /*
2045  * Get a string token, accepting both the quoted and the unquoted form.
2046  * Log an error if the next token is not a string.
2047  */
2048 static isc_result_t
2049 cfg_getstringtoken(cfg_parser_t *pctx) {
2050         isc_result_t result;
2051
2052         result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
2053         if (result != ISC_R_SUCCESS)
2054                 return (result);
2055
2056         if (pctx->token.type != isc_tokentype_string &&
2057             pctx->token.type != isc_tokentype_qstring) {
2058                 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
2059                 return (ISC_R_UNEXPECTEDTOKEN);
2060         }
2061         return (ISC_R_SUCCESS);
2062 }
2063
2064 void
2065 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2066         va_list args;
2067         va_start(args, fmt);
2068         parser_complain(pctx, ISC_FALSE, flags, fmt, args);
2069         va_end(args);
2070         pctx->errors++;
2071 }
2072
2073 void
2074 cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2075         va_list args;
2076         va_start(args, fmt);
2077         parser_complain(pctx, ISC_TRUE, flags, fmt, args);
2078         va_end(args);
2079         pctx->warnings++;
2080 }
2081
2082 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
2083
2084 static char *
2085 current_file(cfg_parser_t *pctx) {
2086         static char none[] = "none";
2087         cfg_listelt_t *elt;
2088         cfg_obj_t *fileobj;
2089
2090         if (pctx->open_files == NULL)
2091                 return (none);
2092         elt = ISC_LIST_TAIL(pctx->open_files->value.list);
2093         if (elt == NULL)
2094               return (none);
2095
2096         fileobj = elt->obj;
2097         INSIST(fileobj->type == &cfg_type_qstring);
2098         return (fileobj->value.string.base);
2099 }
2100
2101 static void
2102 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
2103                 unsigned int flags, const char *format,
2104                 va_list args)
2105 {
2106         char tokenbuf[MAX_LOG_TOKEN + 10];
2107         static char where[ISC_DIR_PATHMAX + 100];
2108         static char message[2048];
2109         int level = ISC_LOG_ERROR;
2110         const char *prep = "";
2111         size_t len;
2112
2113         if (is_warning)
2114                 level = ISC_LOG_WARNING;
2115
2116         snprintf(where, sizeof(where), "%s:%u: ",
2117                  current_file(pctx), pctx->line);
2118
2119         len = vsnprintf(message, sizeof(message), format, args);
2120         if (len >= sizeof(message))
2121                 FATAL_ERROR(__FILE__, __LINE__,
2122                             "error message would overflow");
2123
2124         if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) {
2125                 isc_region_t r;
2126
2127                 if (pctx->ungotten)
2128                         (void)cfg_gettoken(pctx, 0);
2129
2130                 if (pctx->token.type == isc_tokentype_eof) {
2131                         snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
2132                 } else if (pctx->token.type == isc_tokentype_unknown) {
2133                         flags = 0;
2134                         tokenbuf[0] = '\0';
2135                 } else {
2136                         isc_lex_getlasttokentext(pctx->lexer,
2137                                                  &pctx->token, &r);
2138                         if (r.length > MAX_LOG_TOKEN)
2139                                 snprintf(tokenbuf, sizeof(tokenbuf),
2140                                          "'%.*s...'", MAX_LOG_TOKEN, r.base);
2141                         else
2142                                 snprintf(tokenbuf, sizeof(tokenbuf),
2143                                          "'%.*s'", (int)r.length, r.base);
2144                 }
2145
2146                 /* Choose a preposition. */
2147                 if (flags & CFG_LOG_NEAR)
2148                         prep = " near ";
2149                 else if (flags & CFG_LOG_BEFORE)
2150                         prep = " before ";
2151                 else
2152                         prep = " ";
2153         } else {
2154                 tokenbuf[0] = '\0';
2155         }
2156         isc_log_write(pctx->lctx, CAT, MOD, level,
2157                       "%s%s%s%s", where, message, prep, tokenbuf);
2158 }
2159
2160 void
2161 cfg_obj_log(cfg_obj_t *obj, isc_log_t *lctx, int level, const char *fmt, ...) {
2162         va_list ap;
2163         char msgbuf[2048];
2164
2165         if (! isc_log_wouldlog(lctx, level))
2166                 return;
2167
2168         va_start(ap, fmt);
2169
2170         vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
2171         isc_log_write(lctx, CAT, MOD, level,
2172                       "%s:%u: %s",
2173                       obj->file == NULL ? "<unknown file>" : obj->file,
2174                       obj->line, msgbuf);
2175         va_end(ap);
2176 }
2177
2178 const char *
2179 cfg_obj_file(cfg_obj_t *obj) {
2180         return (obj->file);
2181 }
2182
2183 unsigned int
2184 cfg_obj_line(cfg_obj_t *obj) {
2185         return (obj->line);
2186 }
2187
2188 isc_result_t
2189 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2190         cfg_obj_t *obj;
2191
2192         obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
2193         if (obj == NULL)
2194                 return (ISC_R_NOMEMORY);
2195         obj->type = type;
2196         obj->file = current_file(pctx);
2197         obj->line = pctx->line;
2198         *ret = obj;
2199         return (ISC_R_SUCCESS);
2200 }
2201
2202 static void
2203 map_symtabitem_destroy(char *key, unsigned int type,
2204                        isc_symvalue_t symval, void *userarg)
2205 {
2206         cfg_obj_t *obj = symval.as_pointer;
2207         cfg_parser_t *pctx = (cfg_parser_t *)userarg;
2208
2209         UNUSED(key);
2210         UNUSED(type);
2211
2212         cfg_obj_destroy(pctx, &obj);
2213 }
2214
2215
2216 static isc_result_t
2217 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2218         isc_result_t result;
2219         isc_symtab_t *symtab = NULL;
2220         cfg_obj_t *obj = NULL;
2221
2222         CHECK(cfg_create_obj(pctx, type, &obj));
2223         CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */
2224                                 map_symtabitem_destroy,
2225                                 pctx, ISC_FALSE, &symtab));
2226
2227         obj->value.map.symtab = symtab;
2228         obj->value.map.id = NULL;
2229
2230         *ret = obj;
2231         return (ISC_R_SUCCESS);
2232
2233  cleanup:
2234         if (obj != NULL)
2235                 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
2236         return (result);
2237 }
2238
2239 static void
2240 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
2241         CLEANUP_OBJ(obj->value.map.id);
2242         isc_symtab_destroy(&obj->value.map.symtab);
2243 }
2244
2245 isc_boolean_t
2246 cfg_obj_istype(cfg_obj_t *obj, const cfg_type_t *type) {
2247         return (ISC_TF(obj->type == type));
2248 }
2249
2250 /*
2251  * Destroy 'obj', a configuration object created in 'pctx'.
2252  */
2253 void
2254 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
2255         cfg_obj_t *obj = *objp;
2256         obj->type->rep->free(pctx, obj);
2257         isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
2258         *objp = NULL;
2259 }
2260
2261 static void
2262 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
2263         UNUSED(pctx);
2264         UNUSED(obj);
2265 }
2266
2267 void
2268 cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
2269         type->doc(pctx, type);
2270 }
2271
2272 void
2273 cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) {
2274         cfg_print_chars(pctx, "<", 1);
2275         cfg_print_cstr(pctx, type->name);
2276         cfg_print_chars(pctx, ">", 1);
2277 }
2278
2279 void
2280 cfg_print_grammar(const cfg_type_t *type,
2281         void (*f)(void *closure, const char *text, int textlen),
2282         void *closure)
2283 {
2284         cfg_printer_t pctx;
2285         pctx.f = f;
2286         pctx.closure = closure;
2287         pctx.indent = 0;
2288         cfg_doc_obj(&pctx, type);
2289 }