Merge from vendor branch TCPDUMP:
[dragonfly.git] / contrib / bind-9.3 / lib / isccfg / parser.c
1 /*
2  * Copyright (C) 2004, 2006  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.21 2006/02/28 06:32:54 marka 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, const 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, const 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(const 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, const 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                 const 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(const cfg_obj_t *obj) {
295         REQUIRE(obj != NULL);
296         return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
297 }
298
299 const cfg_obj_t *
300 cfg_tuple_get(const 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, const 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(const 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, const cfg_obj_t *obj) {
610         cfg_print_rawuint(pctx, obj->value.uint32);
611 }
612
613 isc_boolean_t
614 cfg_obj_isuint32(const 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(const 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(const 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(const 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, const 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,
727                   cfg_obj_t **ret)
728 {
729         isc_result_t result;
730         UNUSED(type);
731
732         CHECK(cfg_getstringtoken(pctx));
733         return (create_string(pctx,
734                               TOKEN_STRING(pctx),
735                               &cfg_type_qstring,
736                               ret));
737  cleanup:
738         return (result);
739 }
740
741 isc_boolean_t
742 cfg_is_enum(const char *s, const char *const *enums) {
743         const char * const *p;
744         for (p = enums; *p != NULL; p++) {
745                 if (strcasecmp(*p, s) == 0)
746                         return (ISC_TRUE);
747         }
748         return (ISC_FALSE);
749 }
750
751 static isc_result_t
752 check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
753         const char *s = obj->value.string.base;
754         if (cfg_is_enum(s, enums))
755                 return (ISC_R_SUCCESS);
756         cfg_parser_error(pctx, 0, "'%s' unexpected", s);
757         return (ISC_R_UNEXPECTEDTOKEN);
758 }
759
760 isc_result_t
761 cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
762         isc_result_t result;
763         cfg_obj_t *obj = NULL;
764         CHECK(parse_ustring(pctx, NULL, &obj));
765         CHECK(check_enum(pctx, obj, type->of));
766         *ret = obj;
767         return (ISC_R_SUCCESS);
768  cleanup:
769         CLEANUP_OBJ(obj);       
770         return (result);
771 }
772
773 void
774 cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
775         const char * const *p;
776         cfg_print_chars(pctx, "( ", 2);
777         for (p = type->of; *p != NULL; p++) {
778                 cfg_print_cstr(pctx, *p);
779                 if (p[1] != NULL)
780                         cfg_print_chars(pctx, " | ", 3);
781         }
782         cfg_print_chars(pctx, " )", 2);
783 }
784
785 void
786 cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
787         cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
788 }
789
790 static void
791 print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
792         cfg_print_chars(pctx, "\"", 1);
793         cfg_print_ustring(pctx, obj);
794         cfg_print_chars(pctx, "\"", 1);
795 }
796
797 static void
798 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
799         isc_mem_put(pctx->mctx, obj->value.string.base,
800                     obj->value.string.length + 1);
801 }
802
803 isc_boolean_t
804 cfg_obj_isstring(const cfg_obj_t *obj) {
805         REQUIRE(obj != NULL);
806         return (ISC_TF(obj->type->rep == &cfg_rep_string));
807 }
808
809 const char *
810 cfg_obj_asstring(const cfg_obj_t *obj) {
811         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
812         return (obj->value.string.base);
813 }
814
815 /* Quoted string only */
816 cfg_type_t cfg_type_qstring = {
817         "quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal,
818         &cfg_rep_string, NULL
819 };
820
821 /* Unquoted string only */
822 cfg_type_t cfg_type_ustring = {
823         "string", parse_ustring, cfg_print_ustring, cfg_doc_terminal,
824         &cfg_rep_string, NULL
825 };
826
827 /* Any string (quoted or unquoted); printed with quotes */
828 cfg_type_t cfg_type_astring = {
829         "string", cfg_parse_astring, print_qstring, cfg_doc_terminal,
830         &cfg_rep_string, NULL
831 };
832
833 /*
834  * Booleans
835  */
836
837 isc_boolean_t
838 cfg_obj_isboolean(const cfg_obj_t *obj) {
839         REQUIRE(obj != NULL);
840         return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
841 }
842
843 isc_boolean_t
844 cfg_obj_asboolean(const cfg_obj_t *obj) {
845         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
846         return (obj->value.boolean);
847 }
848
849 static isc_result_t
850 parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
851 {
852         isc_result_t result;
853         isc_boolean_t value;
854         cfg_obj_t *obj = NULL;
855         UNUSED(type);
856
857         result = cfg_gettoken(pctx, 0);
858         if (result != ISC_R_SUCCESS)
859                 return (result);
860
861         if (pctx->token.type != isc_tokentype_string)
862                 goto bad_boolean;
863
864         if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
865             (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
866             (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
867                 value = ISC_TRUE;
868         } else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
869                    (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
870                    (strcmp(TOKEN_STRING(pctx), "0") == 0)) {
871                 value = ISC_FALSE;
872         } else {
873                 goto bad_boolean;
874         }
875
876         CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
877         obj->value.boolean = value;
878         *ret = obj;
879         return (result);
880
881  bad_boolean:
882         cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
883         return (ISC_R_UNEXPECTEDTOKEN);
884
885  cleanup:
886         return (result);
887 }
888
889 static void
890 print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
891         if (obj->value.boolean)
892                 cfg_print_chars(pctx, "yes", 3);
893         else
894                 cfg_print_chars(pctx, "no", 2);
895 }
896
897 cfg_type_t cfg_type_boolean = {
898         "boolean", parse_boolean, print_boolean, cfg_doc_terminal,
899         &cfg_rep_boolean, NULL
900 };
901
902 /*
903  * Lists.
904  */
905
906 isc_result_t
907 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
908         isc_result_t result;
909         CHECK(cfg_create_obj(pctx, type, obj));
910         ISC_LIST_INIT((*obj)->value.list);
911  cleanup:
912         return (result);
913 }
914
915 static isc_result_t
916 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
917         cfg_listelt_t *elt;
918         elt = isc_mem_get(pctx->mctx, sizeof(*elt));
919         if (elt == NULL)
920                 return (ISC_R_NOMEMORY);
921         elt->obj = NULL;
922         ISC_LINK_INIT(elt, link);
923         *eltp = elt;
924         return (ISC_R_SUCCESS);
925 }
926
927 static void
928 free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
929         cfg_obj_destroy(pctx, &elt->obj);
930         isc_mem_put(pctx->mctx, elt, sizeof(*elt));
931 }
932
933 static void
934 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
935         cfg_listelt_t *elt, *next;
936         for (elt = ISC_LIST_HEAD(obj->value.list);
937              elt != NULL;
938              elt = next)
939         {
940                 next = ISC_LIST_NEXT(elt, link);
941                 free_list_elt(pctx, elt);
942         }
943 }
944
945 isc_result_t
946 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
947                   cfg_listelt_t **ret)
948 {
949         isc_result_t result;
950         cfg_listelt_t *elt = NULL;
951         cfg_obj_t *value = NULL;
952
953         CHECK(create_listelt(pctx, &elt));
954
955         result = cfg_parse_obj(pctx, elttype, &value);
956         if (result != ISC_R_SUCCESS)
957                 goto cleanup;
958
959         elt->obj = value;
960
961         *ret = elt;
962         return (ISC_R_SUCCESS);
963
964  cleanup:
965         isc_mem_put(pctx->mctx, elt, sizeof(*elt));
966         return (result);
967 }
968
969 /*
970  * Parse a homogeneous list whose elements are of type 'elttype'
971  * and where each element is terminated by a semicolon.
972  */
973 static isc_result_t
974 parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
975 {
976         cfg_obj_t *listobj = NULL;
977         const cfg_type_t *listof = listtype->of;
978         isc_result_t result;
979         cfg_listelt_t *elt = NULL;
980
981         CHECK(cfg_create_list(pctx, listtype, &listobj));
982
983         for (;;) {
984                 CHECK(cfg_peektoken(pctx, 0));
985                 if (pctx->token.type == isc_tokentype_special &&
986                     pctx->token.value.as_char == /*{*/ '}')
987                         break;
988                 CHECK(cfg_parse_listelt(pctx, listof, &elt));
989                 CHECK(parse_semicolon(pctx));
990                 ISC_LIST_APPEND(listobj->value.list, elt, link);
991                 elt = NULL;
992         }
993         *ret = listobj;
994         return (ISC_R_SUCCESS);
995
996  cleanup:
997         if (elt != NULL)
998                 free_list_elt(pctx, elt);
999         CLEANUP_OBJ(listobj);
1000         return (result);
1001 }
1002
1003 static void
1004 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1005         const cfg_list_t *list = &obj->value.list;
1006         const cfg_listelt_t *elt;
1007
1008         for (elt = ISC_LIST_HEAD(*list);
1009              elt != NULL;
1010              elt = ISC_LIST_NEXT(elt, link)) {
1011                 print_indent(pctx);
1012                 cfg_print_obj(pctx, elt->obj);
1013                 cfg_print_chars(pctx, ";\n", 2);
1014         }
1015 }
1016
1017 isc_result_t
1018 cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
1019                      cfg_obj_t **ret)
1020 {
1021         isc_result_t result;
1022         CHECK(cfg_parse_special(pctx, '{'));
1023         CHECK(parse_list(pctx, type, ret));
1024         CHECK(cfg_parse_special(pctx, '}'));
1025  cleanup:
1026         return (result);
1027 }
1028
1029 void
1030 cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1031         print_open(pctx);
1032         print_list(pctx, obj);
1033         print_close(pctx);
1034 }
1035
1036 void
1037 cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
1038         cfg_print_chars(pctx, "{ ", 2);
1039         cfg_doc_obj(pctx, type->of);
1040         cfg_print_chars(pctx, "; ... }", 7);
1041 }
1042
1043 /*
1044  * Parse a homogeneous list whose elements are of type 'elttype'
1045  * and where elements are separated by space.  The list ends
1046  * before the first semicolon.
1047  */
1048 isc_result_t
1049 cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
1050                     cfg_obj_t **ret)
1051 {
1052         cfg_obj_t *listobj = NULL;
1053         const cfg_type_t *listof = listtype->of;
1054         isc_result_t result;
1055
1056         CHECK(cfg_create_list(pctx, listtype, &listobj));
1057
1058         for (;;) {
1059                 cfg_listelt_t *elt = NULL;
1060
1061                 CHECK(cfg_peektoken(pctx, 0));
1062                 if (pctx->token.type == isc_tokentype_special &&
1063                     pctx->token.value.as_char == ';')
1064                         break;
1065                 CHECK(cfg_parse_listelt(pctx, listof, &elt));
1066                 ISC_LIST_APPEND(listobj->value.list, elt, link);
1067         }
1068         *ret = listobj;
1069         return (ISC_R_SUCCESS);
1070
1071  cleanup:
1072         CLEANUP_OBJ(listobj);
1073         return (result);
1074 }
1075
1076 void
1077 cfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1078         const cfg_list_t *list = &obj->value.list;
1079         const cfg_listelt_t *elt;
1080
1081         for (elt = ISC_LIST_HEAD(*list);
1082              elt != NULL;
1083              elt = ISC_LIST_NEXT(elt, link)) {
1084                 cfg_print_obj(pctx, elt->obj);
1085                 if (ISC_LIST_NEXT(elt, link) != NULL)
1086                         cfg_print_chars(pctx, " ", 1);
1087         }
1088 }
1089
1090
1091 isc_boolean_t
1092 cfg_obj_islist(const cfg_obj_t *obj) {
1093         REQUIRE(obj != NULL);
1094         return (ISC_TF(obj->type->rep == &cfg_rep_list));
1095 }
1096
1097 const cfg_listelt_t *
1098 cfg_list_first(const cfg_obj_t *obj) {
1099         REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
1100         if (obj == NULL)
1101                 return (NULL);
1102         return (ISC_LIST_HEAD(obj->value.list));
1103 }
1104
1105 const cfg_listelt_t *
1106 cfg_list_next(const cfg_listelt_t *elt) {
1107         REQUIRE(elt != NULL);
1108         return (ISC_LIST_NEXT(elt, link));
1109 }
1110
1111 const cfg_obj_t *
1112 cfg_listelt_value(const cfg_listelt_t *elt) {
1113         REQUIRE(elt != NULL);
1114         return (elt->obj);
1115 }
1116
1117 /*
1118  * Maps.
1119  */
1120
1121 /*
1122  * Parse a map body.  That's something like
1123  *
1124  *   "foo 1; bar { glub; }; zap true; zap false;"
1125  *
1126  * i.e., a sequence of option names followed by values and
1127  * terminated by semicolons.  Used for the top level of
1128  * the named.conf syntax, as well as for the body of the
1129  * options, view, zone, and other statements.
1130  */
1131 isc_result_t
1132 cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1133 {
1134         const cfg_clausedef_t * const *clausesets = type->of;
1135         isc_result_t result;
1136         const cfg_clausedef_t * const *clauseset;
1137         const cfg_clausedef_t *clause;
1138         cfg_obj_t *value = NULL;
1139         cfg_obj_t *obj = NULL;
1140         cfg_obj_t *eltobj = NULL;
1141         cfg_obj_t *includename = NULL;
1142         isc_symvalue_t symval;
1143         cfg_list_t *list = NULL;
1144
1145         CHECK(create_map(pctx, type, &obj));
1146
1147         obj->value.map.clausesets = clausesets;
1148
1149         for (;;) {
1150                 cfg_listelt_t *elt;
1151
1152         redo:
1153                 /*
1154                  * Parse the option name and see if it is known.
1155                  */
1156                 CHECK(cfg_gettoken(pctx, 0));
1157
1158                 if (pctx->token.type != isc_tokentype_string) {
1159                         cfg_ungettoken(pctx);
1160                         break;
1161                 }
1162
1163                 /*
1164                  * We accept "include" statements wherever a map body
1165                  * clause can occur.
1166                  */
1167                 if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
1168                         /*
1169                          * Turn the file name into a temporary configuration
1170                          * object just so that it is not overwritten by the
1171                          * semicolon token.
1172                          */
1173                         CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
1174                         CHECK(parse_semicolon(pctx));
1175                         CHECK(parser_openfile(pctx, includename->
1176                                               value.string.base));
1177                          cfg_obj_destroy(pctx, &includename);
1178                          goto redo;
1179                 }
1180
1181                 clause = NULL;
1182                 for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
1183                         for (clause = *clauseset;
1184                              clause->name != NULL;
1185                              clause++) {
1186                                 if (strcasecmp(TOKEN_STRING(pctx),
1187                                            clause->name) == 0)
1188                                         goto done;
1189                         }
1190                 }
1191         done:
1192                 if (clause == NULL || clause->name == NULL) {
1193                         cfg_parser_error(pctx, CFG_LOG_NOPREP, "unknown option");
1194                         /*
1195                          * Try to recover by parsing this option as an unknown
1196                          * option and discarding it.
1197                          */
1198                         CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, &eltobj));
1199                         cfg_obj_destroy(pctx, &eltobj);
1200                         CHECK(parse_semicolon(pctx));
1201                         continue;
1202                 }
1203
1204                 /* Clause is known. */
1205
1206                 /* Issue warnings if appropriate */
1207                 if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0)
1208                         cfg_parser_warning(pctx, 0, "option '%s' is obsolete",
1209                                        clause->name);
1210                 if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0)
1211                         cfg_parser_warning(pctx, 0, "option '%s' is "
1212                                        "not implemented", clause->name);
1213                 if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0)
1214                         cfg_parser_warning(pctx, 0, "option '%s' is "
1215                                        "not implemented", clause->name);
1216                 /*
1217                  * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
1218                  * set here - we need to log the *lack* of such an option,
1219                  * not its presence.
1220                  */
1221
1222                 /* See if the clause already has a value; if not create one. */
1223                 result = isc_symtab_lookup(obj->value.map.symtab,
1224                                            clause->name, 0, &symval);
1225
1226                 if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
1227                         /* Multivalued clause */
1228                         cfg_obj_t *listobj = NULL;
1229                         if (result == ISC_R_NOTFOUND) {
1230                                 CHECK(cfg_create_list(pctx,
1231                                                   &cfg_type_implicitlist,
1232                                                   &listobj));
1233                                 symval.as_pointer = listobj;
1234                                 result = isc_symtab_define(obj->value.
1235                                                    map.symtab,
1236                                                    clause->name,
1237                                                    1, symval,
1238                                                    isc_symexists_reject);
1239                                 if (result != ISC_R_SUCCESS) {
1240                                         cfg_parser_error(pctx, CFG_LOG_NEAR,
1241                                                      "isc_symtab_define(%s) "
1242                                                      "failed", clause->name);
1243                                         isc_mem_put(pctx->mctx, list,
1244                                                     sizeof(cfg_list_t));
1245                                         goto cleanup;
1246                                 }
1247                         } else {
1248                                 INSIST(result == ISC_R_SUCCESS);
1249                                 listobj = symval.as_pointer;
1250                         }
1251
1252                         elt = NULL;
1253                         CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
1254                         CHECK(parse_semicolon(pctx));
1255
1256                         ISC_LIST_APPEND(listobj->value.list, elt, link);
1257                 } else {
1258                         /* Single-valued clause */
1259                         if (result == ISC_R_NOTFOUND) {
1260                                 isc_boolean_t callback =
1261                                         ISC_TF((clause->flags &
1262                                                 CFG_CLAUSEFLAG_CALLBACK) != 0);
1263                                 CHECK(parse_symtab_elt(pctx, clause->name,
1264                                                        clause->type,
1265                                                        obj->value.map.symtab,
1266                                                        callback));
1267                                 CHECK(parse_semicolon(pctx));
1268                         } else if (result == ISC_R_SUCCESS) {
1269                                 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
1270                                              clause->name);
1271                                 result = ISC_R_EXISTS;
1272                                 goto cleanup;
1273                         } else {
1274                                 cfg_parser_error(pctx, CFG_LOG_NEAR,
1275                                              "isc_symtab_define() failed");
1276                                 goto cleanup;
1277                         }
1278                 }
1279         }
1280
1281
1282         *ret = obj;
1283         return (ISC_R_SUCCESS);
1284
1285  cleanup:
1286         CLEANUP_OBJ(value);
1287         CLEANUP_OBJ(obj);
1288         CLEANUP_OBJ(eltobj);
1289         CLEANUP_OBJ(includename);
1290         return (result);
1291 }
1292
1293 static isc_result_t
1294 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
1295                  cfg_type_t *elttype, isc_symtab_t *symtab,
1296                  isc_boolean_t callback)
1297 {
1298         isc_result_t result;
1299         cfg_obj_t *obj = NULL;
1300         isc_symvalue_t symval;
1301
1302         CHECK(cfg_parse_obj(pctx, elttype, &obj));
1303
1304         if (callback && pctx->callback != NULL)
1305                 CHECK(pctx->callback(name, obj, pctx->callbackarg));
1306         
1307         symval.as_pointer = obj;
1308         CHECK(isc_symtab_define(symtab, name,
1309                                 1, symval,
1310                                 isc_symexists_reject));
1311         return (ISC_R_SUCCESS);
1312
1313  cleanup:
1314         CLEANUP_OBJ(obj);
1315         return (result);
1316 }
1317
1318 /*
1319  * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
1320  */
1321 isc_result_t
1322 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1323         isc_result_t result;
1324         CHECK(cfg_parse_special(pctx, '{'));
1325         CHECK(cfg_parse_mapbody(pctx, type, ret));
1326         CHECK(cfg_parse_special(pctx, '}'));
1327  cleanup:
1328         return (result);
1329 }
1330
1331 /*
1332  * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
1333  */
1334 static isc_result_t
1335 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type,
1336                     cfg_obj_t **ret)
1337 {
1338         isc_result_t result;
1339         cfg_obj_t *idobj = NULL;
1340         cfg_obj_t *mapobj = NULL;
1341
1342         CHECK(cfg_parse_obj(pctx, nametype, &idobj));
1343         CHECK(cfg_parse_map(pctx, type, &mapobj));
1344         mapobj->value.map.id = idobj;
1345         idobj = NULL;
1346         *ret = mapobj;
1347  cleanup:
1348         CLEANUP_OBJ(idobj);
1349         return (result);
1350 }
1351
1352 /*
1353  * Parse a map identified by a string name.  E.g., "name { foo 1; }".  
1354  * Used for the "key" and "channel" statements.
1355  */
1356 isc_result_t
1357 cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1358         return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
1359 }
1360
1361 /*
1362  * Parse a map identified by a network address.
1363  * Used for the "server" statement.
1364  */
1365 isc_result_t
1366 cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1367         return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
1368 }
1369
1370 void
1371 cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1372         isc_result_t result = ISC_R_SUCCESS;
1373
1374         const cfg_clausedef_t * const *clauseset;
1375
1376         for (clauseset = obj->value.map.clausesets;
1377              *clauseset != NULL;
1378              clauseset++)
1379         {
1380                 isc_symvalue_t symval;
1381                 const cfg_clausedef_t *clause;
1382
1383                 for (clause = *clauseset;
1384                      clause->name != NULL;
1385                      clause++) {
1386                         result = isc_symtab_lookup(obj->value.map.symtab,
1387                                                    clause->name, 0, &symval);
1388                         if (result == ISC_R_SUCCESS) {
1389                                 cfg_obj_t *obj = symval.as_pointer;
1390                                 if (obj->type == &cfg_type_implicitlist) {
1391                                         /* Multivalued. */
1392                                         cfg_list_t *list = &obj->value.list;
1393                                         cfg_listelt_t *elt;
1394                                         for (elt = ISC_LIST_HEAD(*list);
1395                                              elt != NULL;
1396                                              elt = ISC_LIST_NEXT(elt, link)) {
1397                                                 print_indent(pctx);
1398                                                 cfg_print_cstr(pctx, clause->name);
1399                                                 cfg_print_chars(pctx, " ", 1);
1400                                                 cfg_print_obj(pctx, elt->obj);
1401                                                 cfg_print_chars(pctx, ";\n", 2);
1402                                         }
1403                                 } else {
1404                                         /* Single-valued. */
1405                                         print_indent(pctx);
1406                                         cfg_print_cstr(pctx, clause->name);
1407                                         cfg_print_chars(pctx, " ", 1);
1408                                         cfg_print_obj(pctx, obj);
1409                                         cfg_print_chars(pctx, ";\n", 2);
1410                                 }
1411                         } else if (result == ISC_R_NOTFOUND) {
1412                                 ; /* do nothing */
1413                         } else {
1414                                 INSIST(0);
1415                         }
1416                 }
1417         }
1418 }
1419
1420 void
1421 cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
1422         const cfg_clausedef_t * const *clauseset;
1423         const cfg_clausedef_t *clause;
1424         
1425         for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1426                 for (clause = *clauseset;
1427                      clause->name != NULL;
1428                      clause++) {
1429                         cfg_print_cstr(pctx, clause->name);
1430                         cfg_print_chars(pctx, " ", 1);
1431                         cfg_doc_obj(pctx, clause->type);
1432                         cfg_print_chars(pctx, ";", 1);
1433                         /* XXX print flags here? */
1434                         cfg_print_chars(pctx, "\n\n", 2);
1435                 }
1436         }
1437 }
1438
1439 static struct flagtext {
1440         unsigned int flag;
1441         const char *text;
1442 } flagtexts[] = {
1443         { CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
1444         { CFG_CLAUSEFLAG_NYI, "not yet implemented" },
1445         { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
1446         { CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
1447         { 0, NULL }
1448 };
1449
1450 void
1451 cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1452         if (obj->value.map.id != NULL) {
1453                 cfg_print_obj(pctx, obj->value.map.id);
1454                 cfg_print_chars(pctx, " ", 1);
1455         }
1456         print_open(pctx);
1457         cfg_print_mapbody(pctx, obj);
1458         print_close(pctx);
1459 }
1460
1461 static void
1462 print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
1463         struct flagtext *p;
1464         isc_boolean_t first = ISC_TRUE;
1465         for (p = flagtexts; p->flag != 0; p++) {
1466                 if ((flags & p->flag) != 0) {
1467                         if (first)
1468                                 cfg_print_chars(pctx, " // ", 4);
1469                         else
1470                                 cfg_print_chars(pctx, ", ", 2);
1471                         cfg_print_cstr(pctx, p->text);
1472                         first = ISC_FALSE;
1473                 }
1474         }
1475 }
1476
1477 void
1478 cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
1479         const cfg_clausedef_t * const *clauseset;
1480         const cfg_clausedef_t *clause;
1481         
1482         if (type->parse == cfg_parse_named_map) {
1483                 cfg_doc_obj(pctx, &cfg_type_astring);
1484                 cfg_print_chars(pctx, " ", 1);
1485         } else if (type->parse == cfg_parse_addressed_map) {
1486                 cfg_doc_obj(pctx, &cfg_type_netaddr);
1487                 cfg_print_chars(pctx, " ", 1);
1488         }
1489         
1490         print_open(pctx);
1491         
1492         for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1493                 for (clause = *clauseset;
1494                      clause->name != NULL;
1495                      clause++) {
1496                         print_indent(pctx);
1497                         cfg_print_cstr(pctx, clause->name);
1498                         if (clause->type->print != cfg_print_void)
1499                                 cfg_print_chars(pctx, " ", 1);
1500                         cfg_doc_obj(pctx, clause->type);
1501                         cfg_print_chars(pctx, ";", 1);
1502                         print_clause_flags(pctx, clause->flags);
1503                         cfg_print_chars(pctx, "\n", 1);
1504                 }
1505         }
1506         print_close(pctx);
1507 }
1508
1509 isc_boolean_t
1510 cfg_obj_ismap(const cfg_obj_t *obj) {
1511         REQUIRE(obj != NULL);
1512         return (ISC_TF(obj->type->rep == &cfg_rep_map));
1513 }
1514
1515 isc_result_t
1516 cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
1517         isc_result_t result;
1518         isc_symvalue_t val;
1519         const cfg_map_t *map;
1520         
1521         REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1522         REQUIRE(name != NULL);
1523         REQUIRE(obj != NULL && *obj == NULL);
1524
1525         map = &mapobj->value.map;
1526         
1527         result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
1528         if (result != ISC_R_SUCCESS)
1529                 return (result);
1530         *obj = val.as_pointer;
1531         return (ISC_R_SUCCESS);
1532 }
1533
1534 const cfg_obj_t *
1535 cfg_map_getname(const cfg_obj_t *mapobj) {
1536         REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1537         return (mapobj->value.map.id);
1538 }
1539
1540
1541 /* Parse an arbitrary token, storing its raw text representation. */
1542 static isc_result_t
1543 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1544         cfg_obj_t *obj = NULL;
1545         isc_result_t result;
1546         isc_region_t r;
1547
1548         UNUSED(type);
1549
1550         CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
1551         CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
1552         if (pctx->token.type == isc_tokentype_eof) {
1553                 cfg_ungettoken(pctx);
1554                 result = ISC_R_EOF;
1555                 goto cleanup;
1556         }
1557
1558         isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
1559
1560         obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
1561         if (obj->value.string.base == NULL) {
1562                 result = ISC_R_NOMEMORY;
1563                 goto cleanup;
1564         }
1565         obj->value.string.length = r.length;
1566         memcpy(obj->value.string.base, r.base, r.length);
1567         obj->value.string.base[r.length] = '\0';
1568         *ret = obj;
1569         return (result);
1570
1571  cleanup:
1572         if (obj != NULL)
1573                 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
1574         return (result);
1575 }
1576
1577 cfg_type_t cfg_type_token = {
1578         "token", parse_token, cfg_print_ustring, cfg_doc_terminal,
1579         &cfg_rep_string, NULL
1580 };
1581
1582 /*
1583  * An unsupported option.  This is just a list of tokens with balanced braces
1584  * ending in a semicolon.
1585  */
1586
1587 static isc_result_t
1588 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1589         cfg_obj_t *listobj = NULL;
1590         isc_result_t result;
1591         int braces = 0;
1592
1593         CHECK(cfg_create_list(pctx, type, &listobj));
1594
1595         for (;;) {
1596                 cfg_listelt_t *elt = NULL;
1597
1598                 CHECK(cfg_peektoken(pctx, 0));
1599                 if (pctx->token.type == isc_tokentype_special) {
1600                         if (pctx->token.value.as_char == '{')
1601                                 braces++;
1602                         else if (pctx->token.value.as_char == '}')
1603                                 braces--;
1604                         else if (pctx->token.value.as_char == ';')
1605                                 if (braces == 0)
1606                                         break;
1607                 }
1608                 if (pctx->token.type == isc_tokentype_eof || braces < 0) {
1609                         cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
1610                         result = ISC_R_UNEXPECTEDTOKEN;
1611                         goto cleanup;
1612                 }
1613
1614                 CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
1615                 ISC_LIST_APPEND(listobj->value.list, elt, link);
1616         }
1617         INSIST(braces == 0);
1618         *ret = listobj;
1619         return (ISC_R_SUCCESS);
1620
1621  cleanup:
1622         CLEANUP_OBJ(listobj);
1623         return (result);
1624 }
1625
1626 cfg_type_t cfg_type_unsupported = {
1627         "unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal,
1628         &cfg_rep_list, NULL
1629 };
1630
1631 /*
1632  * Try interpreting the current token as a network address.
1633  *
1634  * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
1635  * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set.  The
1636  * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is 
1637  * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
1638  * and the IPv6 wildcard address otherwise.
1639  */
1640 static isc_result_t
1641 token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1642         char *s;
1643         struct in_addr in4a;
1644         struct in6_addr in6a;
1645
1646         if (pctx->token.type != isc_tokentype_string)
1647                 return (ISC_R_UNEXPECTEDTOKEN);
1648
1649         s = TOKEN_STRING(pctx);
1650         if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
1651                 if ((flags & CFG_ADDR_V4OK) != 0) {
1652                         isc_netaddr_any(na);
1653                         return (ISC_R_SUCCESS);
1654                 } else if ((flags & CFG_ADDR_V6OK) != 0) {
1655                         isc_netaddr_any6(na);
1656                         return (ISC_R_SUCCESS);
1657                 } else {
1658                         INSIST(0);
1659                 }
1660         } else {
1661                 if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
1662                         if (inet_pton(AF_INET, s, &in4a) == 1) {
1663                                 isc_netaddr_fromin(na, &in4a);
1664                                 return (ISC_R_SUCCESS);
1665                         }
1666                 }
1667                 if ((flags & CFG_ADDR_V4PREFIXOK) != 0 &&
1668                     strlen(s) <= 15U) {
1669                         char buf[64];
1670                         int i;
1671
1672                         strcpy(buf, s);
1673                         for (i = 0; i < 3; i++) {
1674                                 strcat(buf, ".0");
1675                                 if (inet_pton(AF_INET, buf, &in4a) == 1) {
1676                                         isc_netaddr_fromin(na, &in4a);
1677                                         return (ISC_R_SUCCESS);
1678                                 }
1679                         }
1680                 }
1681                 if ((flags & CFG_ADDR_V6OK) != 0 &&
1682                     strlen(s) <= 127U) {
1683                         char buf[128]; /* see lib/bind9/getaddresses.c */
1684                         char *d; /* zone delimiter */
1685                         isc_uint32_t zone = 0; /* scope zone ID */
1686
1687                         strcpy(buf, s);
1688                         d = strchr(buf, '%');
1689                         if (d != NULL)
1690                                 *d = '\0';
1691
1692                         if (inet_pton(AF_INET6, buf, &in6a) == 1) {
1693                                 if (d != NULL) {
1694 #ifdef ISC_PLATFORM_HAVESCOPEID
1695                                         isc_result_t result;
1696
1697                                         result = isc_netscope_pton(AF_INET6,
1698                                                                    d + 1,
1699                                                                    &in6a,
1700                                                                    &zone);
1701                                         if (result != ISC_R_SUCCESS)
1702                                                 return (result);
1703 #else
1704                                 return (ISC_R_BADADDRESSFORM);
1705 #endif
1706                                 }
1707
1708                                 isc_netaddr_fromin6(na, &in6a);
1709                                 isc_netaddr_setzone(na, zone);
1710                                 return (ISC_R_SUCCESS);
1711                         }
1712                 }
1713         }
1714         return (ISC_R_UNEXPECTEDTOKEN);
1715 }
1716
1717 isc_result_t
1718 cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1719         isc_result_t result;
1720         CHECK(cfg_gettoken(pctx, 0));
1721         result = token_addr(pctx, flags, na);
1722         if (result == ISC_R_UNEXPECTEDTOKEN)
1723                 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected IP address");
1724  cleanup:
1725         return (result);
1726 }
1727
1728 isc_boolean_t
1729 cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
1730         isc_result_t result;
1731         isc_netaddr_t na_dummy;
1732         result = token_addr(pctx, flags, &na_dummy);
1733         return (ISC_TF(result == ISC_R_SUCCESS));
1734 }
1735
1736 isc_result_t
1737 cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
1738         isc_result_t result;
1739
1740         CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1741
1742         if ((flags & CFG_ADDR_WILDOK) != 0 &&
1743             pctx->token.type == isc_tokentype_string &&
1744             strcmp(TOKEN_STRING(pctx), "*") == 0) {
1745                 *port = 0;
1746                 return (ISC_R_SUCCESS);
1747         }
1748         if (pctx->token.type != isc_tokentype_number) {
1749                 cfg_parser_error(pctx, CFG_LOG_NEAR,
1750                              "expected port number or '*'");
1751                 return (ISC_R_UNEXPECTEDTOKEN);
1752         }
1753         if (pctx->token.value.as_ulong >= 65536U) {
1754                 cfg_parser_error(pctx, CFG_LOG_NEAR,
1755                              "port number out of range");
1756                 return (ISC_R_UNEXPECTEDTOKEN);
1757         }
1758         *port = (in_port_t)(pctx->token.value.as_ulong);
1759         return (ISC_R_SUCCESS);
1760  cleanup:
1761         return (result);
1762 }
1763
1764 void
1765 cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
1766         isc_result_t result;
1767         char text[128];
1768         isc_buffer_t buf;
1769
1770         isc_buffer_init(&buf, text, sizeof(text));
1771         result = isc_netaddr_totext(na, &buf);
1772         RUNTIME_CHECK(result == ISC_R_SUCCESS);
1773         cfg_print_chars(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf));
1774 }
1775
1776 /* netaddr */
1777
1778 static isc_result_t
1779 parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1780         isc_result_t result;
1781         cfg_obj_t *obj = NULL;
1782         isc_netaddr_t netaddr;
1783         UNUSED(type);
1784         CHECK(cfg_create_obj(pctx, type, &obj));
1785         CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK, &netaddr));
1786         isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
1787         *ret = obj;
1788         return (ISC_R_SUCCESS);
1789  cleanup:
1790         CLEANUP_OBJ(obj);
1791         return (result);
1792 }
1793
1794 cfg_type_t cfg_type_netaddr = {
1795         "netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_terminal,
1796         &cfg_rep_sockaddr, NULL
1797 };
1798
1799 /* netprefix */
1800
1801 isc_result_t
1802 cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
1803                     cfg_obj_t **ret)
1804 {
1805         cfg_obj_t *obj = NULL;
1806         isc_result_t result;
1807         isc_netaddr_t netaddr;
1808         unsigned int addrlen, prefixlen;
1809         UNUSED(type);
1810
1811         CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
1812                                 CFG_ADDR_V6OK, &netaddr));
1813         switch (netaddr.family) {
1814         case AF_INET:
1815                 addrlen = 32;
1816                 break;
1817         case AF_INET6:
1818                 addrlen = 128;
1819                 break;
1820         default:
1821                 addrlen = 0;
1822                 INSIST(0);
1823                 break;
1824         }
1825         CHECK(cfg_peektoken(pctx, 0));
1826         if (pctx->token.type == isc_tokentype_special &&
1827             pctx->token.value.as_char == '/') {
1828                 CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
1829                 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1830                 if (pctx->token.type != isc_tokentype_number) {
1831                         cfg_parser_error(pctx, CFG_LOG_NEAR,
1832                                      "expected prefix length");
1833                         return (ISC_R_UNEXPECTEDTOKEN);
1834                 }
1835                 prefixlen = pctx->token.value.as_ulong;
1836                 if (prefixlen > addrlen) {
1837                         cfg_parser_error(pctx, CFG_LOG_NOPREP,
1838                                      "invalid prefix length");
1839                         return (ISC_R_RANGE);
1840                 }
1841         } else {
1842                 prefixlen = addrlen;
1843         }
1844         CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
1845         obj->value.netprefix.address = netaddr;
1846         obj->value.netprefix.prefixlen = prefixlen;
1847         *ret = obj;
1848         return (ISC_R_SUCCESS);
1849  cleanup:
1850         cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
1851         return (result);
1852 }
1853
1854 static void
1855 print_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1856         const cfg_netprefix_t *p = &obj->value.netprefix;
1857
1858         cfg_print_rawaddr(pctx, &p->address);
1859         cfg_print_chars(pctx, "/", 1);
1860         cfg_print_rawuint(pctx, p->prefixlen);
1861 }
1862
1863 isc_boolean_t
1864 cfg_obj_isnetprefix(const cfg_obj_t *obj) {
1865         REQUIRE(obj != NULL);
1866         return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
1867 }
1868
1869 void
1870 cfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr,
1871                     unsigned int *prefixlen) {
1872         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
1873         *netaddr = obj->value.netprefix.address;
1874         *prefixlen = obj->value.netprefix.prefixlen;
1875 }
1876
1877 cfg_type_t cfg_type_netprefix = {
1878         "netprefix", cfg_parse_netprefix, print_netprefix, cfg_doc_terminal,
1879         &cfg_rep_netprefix, NULL
1880 };
1881
1882 static isc_result_t
1883 parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type,
1884                   int flags, cfg_obj_t **ret)
1885 {
1886         isc_result_t result;
1887         isc_netaddr_t netaddr;
1888         in_port_t port = 0;
1889         cfg_obj_t *obj = NULL;
1890
1891         CHECK(cfg_create_obj(pctx, type, &obj));
1892         CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
1893         CHECK(cfg_peektoken(pctx, 0));
1894         if (pctx->token.type == isc_tokentype_string &&
1895             strcasecmp(TOKEN_STRING(pctx), "port") == 0) {
1896                 CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
1897                 CHECK(cfg_parse_rawport(pctx, flags, &port));
1898         }
1899         isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
1900         *ret = obj;
1901         return (ISC_R_SUCCESS);
1902
1903  cleanup:
1904         CLEANUP_OBJ(obj);
1905         return (result);
1906 }
1907
1908 static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
1909 cfg_type_t cfg_type_sockaddr = {
1910         "sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr,
1911         &cfg_rep_sockaddr, &sockaddr_flags
1912 };
1913
1914 isc_result_t
1915 cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1916         const unsigned int *flagp = type->of;
1917         return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret));
1918 }
1919
1920 void
1921 cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1922         isc_netaddr_t netaddr;
1923         in_port_t port;
1924         char buf[ISC_NETADDR_FORMATSIZE];
1925
1926         isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
1927         isc_netaddr_format(&netaddr, buf, sizeof(buf));
1928         cfg_print_cstr(pctx, buf);
1929         port = isc_sockaddr_getport(&obj->value.sockaddr);
1930         if (port != 0) {
1931                 cfg_print_chars(pctx, " port ", 6);
1932                 cfg_print_rawuint(pctx, port);
1933         }
1934 }
1935
1936 void
1937 cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
1938         const unsigned int *flagp = type->of;
1939         int n = 0;
1940         cfg_print_chars(pctx, "( ", 2);
1941         if (*flagp & CFG_ADDR_V4OK) {
1942                 cfg_print_cstr(pctx, "<ipv4_address>");
1943                 n++;
1944         }
1945         if (*flagp & CFG_ADDR_V6OK) {
1946                 if (n != 0)
1947                         cfg_print_chars(pctx, " | ", 3);
1948                 cfg_print_cstr(pctx, "<ipv6_address>");
1949                 n++;                    
1950         }
1951         if (*flagp & CFG_ADDR_WILDOK) {
1952                 if (n != 0)
1953                         cfg_print_chars(pctx, " | ", 3);
1954                 cfg_print_chars(pctx, "*", 1);
1955                 n++;
1956         }
1957         cfg_print_chars(pctx, " ) ", 3);
1958         if (*flagp & CFG_ADDR_WILDOK) {
1959                 cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
1960         } else {
1961                 cfg_print_cstr(pctx, "[ port <integer> ]");
1962         }
1963 }
1964
1965 isc_boolean_t
1966 cfg_obj_issockaddr(const cfg_obj_t *obj) {
1967         REQUIRE(obj != NULL);
1968         return (ISC_TF(obj->type->rep == &cfg_rep_sockaddr));
1969 }
1970
1971 const isc_sockaddr_t *
1972 cfg_obj_assockaddr(const cfg_obj_t *obj) {
1973         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
1974         return (&obj->value.sockaddr);
1975 }
1976
1977 isc_result_t
1978 cfg_gettoken(cfg_parser_t *pctx, int options) {
1979         isc_result_t result;
1980
1981         if (pctx->seen_eof)
1982                 return (ISC_R_SUCCESS);
1983
1984         options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
1985
1986  redo:
1987         pctx->token.type = isc_tokentype_unknown;
1988         result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
1989         pctx->ungotten = ISC_FALSE;
1990         pctx->line = isc_lex_getsourceline(pctx->lexer);
1991
1992         switch (result) {
1993         case ISC_R_SUCCESS:
1994                 if (pctx->token.type == isc_tokentype_eof) {
1995                         result = isc_lex_close(pctx->lexer);
1996                         INSIST(result == ISC_R_NOMORE ||
1997                                result == ISC_R_SUCCESS);
1998
1999                         if (isc_lex_getsourcename(pctx->lexer) != NULL) {
2000                                 /*
2001                                  * Closed an included file, not the main file.
2002                                  */
2003                                 cfg_listelt_t *elt;
2004                                 elt = ISC_LIST_TAIL(pctx->open_files->
2005                                                     value.list);
2006                                 INSIST(elt != NULL);
2007                                 ISC_LIST_UNLINK(pctx->open_files->
2008                                                 value.list, elt, link);
2009                                 ISC_LIST_APPEND(pctx->closed_files->
2010                                                 value.list, elt, link);
2011                                 goto redo;
2012                         }
2013                         pctx->seen_eof = ISC_TRUE;
2014                 }
2015                 break;
2016
2017         case ISC_R_NOSPACE:
2018                 /* More understandable than "ran out of space". */
2019                 cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
2020                 break;
2021
2022         case ISC_R_IOERROR:
2023                 cfg_parser_error(pctx, 0, "%s",
2024                                  isc_result_totext(result));
2025                 break;
2026
2027         default:
2028                 cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
2029                                  isc_result_totext(result));
2030                 break;
2031         }
2032         return (result);
2033 }
2034
2035 void
2036 cfg_ungettoken(cfg_parser_t *pctx) {
2037         if (pctx->seen_eof)
2038                 return;
2039         isc_lex_ungettoken(pctx->lexer, &pctx->token);
2040         pctx->ungotten = ISC_TRUE;
2041 }
2042
2043 isc_result_t
2044 cfg_peektoken(cfg_parser_t *pctx, int options) {
2045         isc_result_t result;
2046         CHECK(cfg_gettoken(pctx, options));
2047         cfg_ungettoken(pctx);
2048  cleanup:
2049         return (result);
2050 }
2051
2052 /*
2053  * Get a string token, accepting both the quoted and the unquoted form.
2054  * Log an error if the next token is not a string.
2055  */
2056 static isc_result_t
2057 cfg_getstringtoken(cfg_parser_t *pctx) {
2058         isc_result_t result;
2059
2060         result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
2061         if (result != ISC_R_SUCCESS)
2062                 return (result);
2063
2064         if (pctx->token.type != isc_tokentype_string &&
2065             pctx->token.type != isc_tokentype_qstring) {
2066                 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
2067                 return (ISC_R_UNEXPECTEDTOKEN);
2068         }
2069         return (ISC_R_SUCCESS);
2070 }
2071
2072 void
2073 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2074         va_list args;
2075         va_start(args, fmt);
2076         parser_complain(pctx, ISC_FALSE, flags, fmt, args);
2077         va_end(args);
2078         pctx->errors++;
2079 }
2080
2081 void
2082 cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
2083         va_list args;
2084         va_start(args, fmt);
2085         parser_complain(pctx, ISC_TRUE, flags, fmt, args);
2086         va_end(args);
2087         pctx->warnings++;
2088 }
2089
2090 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
2091
2092 static char *
2093 current_file(cfg_parser_t *pctx) {
2094         static char none[] = "none";
2095         cfg_listelt_t *elt;
2096         cfg_obj_t *fileobj;
2097
2098         if (pctx->open_files == NULL)
2099                 return (none);
2100         elt = ISC_LIST_TAIL(pctx->open_files->value.list);
2101         if (elt == NULL)
2102               return (none);
2103
2104         fileobj = elt->obj;
2105         INSIST(fileobj->type == &cfg_type_qstring);
2106         return (fileobj->value.string.base);
2107 }
2108
2109 static void
2110 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
2111                 unsigned int flags, const char *format,
2112                 va_list args)
2113 {
2114         char tokenbuf[MAX_LOG_TOKEN + 10];
2115         static char where[ISC_DIR_PATHMAX + 100];
2116         static char message[2048];
2117         int level = ISC_LOG_ERROR;
2118         const char *prep = "";
2119         size_t len;
2120
2121         if (is_warning)
2122                 level = ISC_LOG_WARNING;
2123
2124         snprintf(where, sizeof(where), "%s:%u: ",
2125                  current_file(pctx), pctx->line);
2126
2127         len = vsnprintf(message, sizeof(message), format, args);
2128         if (len >= sizeof(message))
2129                 FATAL_ERROR(__FILE__, __LINE__,
2130                             "error message would overflow");
2131
2132         if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) {
2133                 isc_region_t r;
2134
2135                 if (pctx->ungotten)
2136                         (void)cfg_gettoken(pctx, 0);
2137
2138                 if (pctx->token.type == isc_tokentype_eof) {
2139                         snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
2140                 } else if (pctx->token.type == isc_tokentype_unknown) {
2141                         flags = 0;
2142                         tokenbuf[0] = '\0';
2143                 } else {
2144                         isc_lex_getlasttokentext(pctx->lexer,
2145                                                  &pctx->token, &r);
2146                         if (r.length > MAX_LOG_TOKEN)
2147                                 snprintf(tokenbuf, sizeof(tokenbuf),
2148                                          "'%.*s...'", MAX_LOG_TOKEN, r.base);
2149                         else
2150                                 snprintf(tokenbuf, sizeof(tokenbuf),
2151                                          "'%.*s'", (int)r.length, r.base);
2152                 }
2153
2154                 /* Choose a preposition. */
2155                 if (flags & CFG_LOG_NEAR)
2156                         prep = " near ";
2157                 else if (flags & CFG_LOG_BEFORE)
2158                         prep = " before ";
2159                 else
2160                         prep = " ";
2161         } else {
2162                 tokenbuf[0] = '\0';
2163         }
2164         isc_log_write(pctx->lctx, CAT, MOD, level,
2165                       "%s%s%s%s", where, message, prep, tokenbuf);
2166 }
2167
2168 void
2169 cfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level,
2170             const char *fmt, ...) {
2171         va_list ap;
2172         char msgbuf[2048];
2173
2174         if (! isc_log_wouldlog(lctx, level))
2175                 return;
2176
2177         va_start(ap, fmt);
2178
2179         vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
2180         isc_log_write(lctx, CAT, MOD, level,
2181                       "%s:%u: %s",
2182                       obj->file == NULL ? "<unknown file>" : obj->file,
2183                       obj->line, msgbuf);
2184         va_end(ap);
2185 }
2186
2187 const char *
2188 cfg_obj_file(const cfg_obj_t *obj) {
2189         return (obj->file);
2190 }
2191
2192 unsigned int
2193 cfg_obj_line(const cfg_obj_t *obj) {
2194         return (obj->line);
2195 }
2196
2197 isc_result_t
2198 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2199         cfg_obj_t *obj;
2200
2201         obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
2202         if (obj == NULL)
2203                 return (ISC_R_NOMEMORY);
2204         obj->type = type;
2205         obj->file = current_file(pctx);
2206         obj->line = pctx->line;
2207         *ret = obj;
2208         return (ISC_R_SUCCESS);
2209 }
2210
2211 static void
2212 map_symtabitem_destroy(char *key, unsigned int type,
2213                        isc_symvalue_t symval, void *userarg)
2214 {
2215         cfg_obj_t *obj = symval.as_pointer;
2216         cfg_parser_t *pctx = (cfg_parser_t *)userarg;
2217
2218         UNUSED(key);
2219         UNUSED(type);
2220
2221         cfg_obj_destroy(pctx, &obj);
2222 }
2223
2224
2225 static isc_result_t
2226 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2227         isc_result_t result;
2228         isc_symtab_t *symtab = NULL;
2229         cfg_obj_t *obj = NULL;
2230
2231         CHECK(cfg_create_obj(pctx, type, &obj));
2232         CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */
2233                                 map_symtabitem_destroy,
2234                                 pctx, ISC_FALSE, &symtab));
2235         obj->value.map.symtab = symtab;
2236         obj->value.map.id = NULL;
2237
2238         *ret = obj;
2239         return (ISC_R_SUCCESS);
2240
2241  cleanup:
2242         if (obj != NULL)
2243                 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
2244         return (result);
2245 }
2246
2247 static void
2248 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
2249         CLEANUP_OBJ(obj->value.map.id);
2250         isc_symtab_destroy(&obj->value.map.symtab);
2251 }
2252
2253 isc_boolean_t
2254 cfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type) {
2255         return (ISC_TF(obj->type == type));
2256 }
2257
2258 /*
2259  * Destroy 'obj', a configuration object created in 'pctx'.
2260  */
2261 void
2262 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
2263         cfg_obj_t *obj = *objp;
2264         obj->type->rep->free(pctx, obj);
2265         isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
2266         *objp = NULL;
2267 }
2268
2269 static void
2270 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
2271         UNUSED(pctx);
2272         UNUSED(obj);
2273 }
2274
2275 void
2276 cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
2277         type->doc(pctx, type);
2278 }
2279
2280 void
2281 cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) {
2282         cfg_print_chars(pctx, "<", 1);
2283         cfg_print_cstr(pctx, type->name);
2284         cfg_print_chars(pctx, ">", 1);
2285 }
2286
2287 void
2288 cfg_print_grammar(const cfg_type_t *type,
2289         void (*f)(void *closure, const char *text, int textlen),
2290         void *closure)
2291 {
2292         cfg_printer_t pctx;
2293         pctx.f = f;
2294         pctx.closure = closure;
2295         pctx.indent = 0;
2296         cfg_doc_obj(&pctx, type);
2297 }