Change the kernel dev_t, representing a pointer to a specinfo structure,
[dragonfly.git] / contrib / bind-9.2.4rc7 / 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.24 2004/03/28 23:09:55 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/util.h>
34 #include <isc/symtab.h>
35
36 #include <isccfg/cfg.h>
37 #include <isccfg/log.h>
38
39 /* Shorthand */
40 #define CAT CFG_LOGCATEGORY_CONFIG
41 #define MOD CFG_LOGMODULE_PARSER
42
43 #define QSTRING (ISC_LEXOPT_QSTRING | ISC_LEXOPT_QSTRINGMULTILINE)
44
45 /*
46  * Pass one of these flags to parser_error() to include the
47  * token text in log message.
48  */
49 #define LOG_NEAR    0x00000001  /* Say "near <token>" */
50 #define LOG_BEFORE  0x00000002  /* Say "before <token>" */
51 #define LOG_NOPREP  0x00000004  /* Say just "<token>" */
52
53 #define MAP_SYM 1       /* Unique type for isc_symtab */
54
55 /* Clause may occur multiple times (e.g., "zone") */
56 #define CFG_CLAUSEFLAG_MULTI            0x00000001
57 /* Clause is obsolete */
58 #define CFG_CLAUSEFLAG_OBSOLETE         0x00000002
59 /* Clause is not implemented, and may never be */
60 #define CFG_CLAUSEFLAG_NOTIMP           0x00000004
61 /* Clause is not implemented yet */
62 #define CFG_CLAUSEFLAG_NYI              0x00000008
63 /* Default value has changed since earlier release */
64 #define CFG_CLAUSEFLAG_NEWDEFAULT       0x00000010
65 /*
66  * Clause needs to be interpreted during parsing
67  * by calling a callback function, like the
68  * "directory" option.
69  */
70 #define CFG_CLAUSEFLAG_CALLBACK         0x00000020
71
72 /*
73  * Flags defining whether to accept certain types of network addresses.
74  */
75 #define V4OK            0x00000001
76 #define V4PREFIXOK      0x00000002
77 #define V6OK            0x00000004
78 #define WILDOK          0x00000008
79
80 /* Check a return value. */
81 #define CHECK(op)                                               \
82         do { result = (op);                                     \
83                 if (result != ISC_R_SUCCESS) goto cleanup;      \
84         } while (0)
85
86 /* Clean up a configuration object if non-NULL. */
87 #define CLEANUP_OBJ(obj) \
88         do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
89
90
91 typedef struct cfg_clausedef cfg_clausedef_t;
92 typedef struct cfg_tuplefielddef cfg_tuplefielddef_t;
93 typedef struct cfg_printer cfg_printer_t;
94 typedef ISC_LIST(cfg_listelt_t) cfg_list_t;
95 typedef struct cfg_map cfg_map_t;
96 typedef struct cfg_rep cfg_rep_t;
97
98 /*
99  * Function types for configuration object methods
100  */
101
102 typedef isc_result_t (*cfg_parsefunc_t)(cfg_parser_t *, const cfg_type_t *type,
103                                         cfg_obj_t **);
104 typedef void         (*cfg_printfunc_t)(cfg_printer_t *, cfg_obj_t *);
105 typedef void         (*cfg_freefunc_t)(cfg_parser_t *, cfg_obj_t *);
106
107
108 /*
109  * Structure definitions
110  */
111
112 /* The parser object. */
113 struct cfg_parser {
114         isc_mem_t *     mctx;
115         isc_log_t *     lctx;
116         isc_lex_t *     lexer;
117         unsigned int    errors;
118         unsigned int    warnings;
119         isc_token_t     token;
120
121         /* We are at the end of all input. */
122         isc_boolean_t   seen_eof;
123
124         /* The current token has been pushed back. */
125         isc_boolean_t   ungotten;
126
127         /*
128          * The stack of currently active files, represented
129          * as a configuration list of configuration strings.
130          * The head is the top-level file, subsequent elements 
131          * (if any) are the nested include files, and the 
132          * last element is the file currently being parsed.
133          */
134         cfg_obj_t *     open_files;
135
136         /*
137          * Names of files that we have parsed and closed
138          * and were previously on the open_file list.
139          * We keep these objects around after closing
140          * the files because the file names may still be
141          * referenced from other configuration objects
142          * for use in reporting semantic errors after
143          * parsing is complete.
144          */
145         cfg_obj_t *     closed_files;
146
147         /*
148          * Current line number.  We maintain our own
149          * copy of this so that it is available even
150          * when a file has just been closed.
151          */
152         unsigned int    line;
153
154         cfg_parsecallback_t callback;
155         void *callbackarg;
156 };
157
158 /*
159  * A configuration printer object.  This is an abstract
160  * interface to a destination to which text can be printed
161  * by calling the function 'f'.
162  */
163 struct cfg_printer {
164         void (*f)(void *closure, const char *text, int textlen);
165         void *closure;
166         int indent;
167 };
168
169 /* A clause definition. */
170
171 struct cfg_clausedef {
172         const char      *name;
173         cfg_type_t      *type;
174         unsigned int    flags;
175 };
176
177 /* A tuple field definition. */
178
179 struct cfg_tuplefielddef {
180         const char      *name;
181         cfg_type_t      *type;
182         unsigned int    flags;
183 };
184
185 /* A configuration object type definition. */
186 struct cfg_type {
187         const char *name;       /* For debugging purposes only */
188         cfg_parsefunc_t  parse;
189         cfg_printfunc_t  print;
190         cfg_rep_t *      rep;   /* Data representation */
191         const void *     of;    /* For meta-types */
192 };
193
194 /* A keyword-type definition, for things like "port <integer>". */
195
196 typedef struct {
197         const char *name;
198         const cfg_type_t *type;
199 } keyword_type_t;
200
201 struct cfg_map {
202         cfg_obj_t        *id; /* Used for 'named maps' like keys, zones, &c */
203         const cfg_clausedef_t * const *clausesets; /* The clauses that
204                                                       can occur in this map;
205                                                       used for printing */
206         isc_symtab_t     *symtab;
207 };
208
209 typedef struct cfg_netprefix cfg_netprefix_t;
210
211 struct cfg_netprefix {
212         isc_netaddr_t address; /* IP4/IP6 */
213         unsigned int prefixlen;
214 };
215
216 /*
217  * A configuration data representation.
218  */
219 struct cfg_rep {
220         const char *    name;   /* For debugging only */
221         cfg_freefunc_t  free;   /* How to free this kind of data. */
222 };
223
224 /*
225  * A configuration object.  This is the main building block
226  * of the configuration parse tree.
227  */
228
229 struct cfg_obj {
230         const cfg_type_t *type;
231         union {
232                 isc_uint32_t    uint32;
233                 isc_uint64_t    uint64;
234                 isc_textregion_t string; /* null terminated, too */
235                 isc_boolean_t   boolean;
236                 cfg_map_t       map;
237                 cfg_list_t      list;
238                 cfg_obj_t **    tuple;
239                 isc_sockaddr_t  sockaddr;
240                 cfg_netprefix_t netprefix;
241         }               value;
242         char *          file;
243         unsigned int    line;
244 };
245
246
247 /* A list element. */
248
249 struct cfg_listelt {
250         cfg_obj_t               *obj;
251         ISC_LINK(cfg_listelt_t)  link;
252 };
253
254 /*
255  * Forward declarations of static functions.
256  */
257
258 static isc_result_t
259 create_cfgobj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
260
261 static isc_result_t
262 create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
263
264 static isc_result_t
265 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
266
267 static void
268 free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
269
270 static isc_result_t
271 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
272               cfg_obj_t **ret);
273
274 static void
275 free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
276
277 static isc_result_t
278 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
279
280 static isc_result_t
281 create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
282
283 static void
284 free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
285
286 static isc_result_t
287 get_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na);
288
289 static void
290 print(cfg_printer_t *pctx, const char *text, int len);
291
292 static void
293 print_void(cfg_printer_t *pctx, cfg_obj_t *obj);
294
295 static isc_result_t
296 parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype,
297                     const cfg_type_t *othertype, cfg_obj_t **ret);
298
299 static isc_result_t
300 parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
301
302 static void
303 print_mapbody(cfg_printer_t *pctx, cfg_obj_t *obj);
304
305 static isc_result_t
306 parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
307
308 static void
309 print_map(cfg_printer_t *pctx, cfg_obj_t *obj);
310
311 static isc_result_t
312 parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
313
314 static isc_result_t
315 parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
316
317 static isc_result_t
318 parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
319
320 static void
321 print_list(cfg_printer_t *pctx, cfg_obj_t *obj);
322
323 static isc_result_t
324 parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
325
326 static void
327 print_tuple(cfg_printer_t *pctx, cfg_obj_t *obj);
328
329 static void
330 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
331
332 static isc_result_t
333 parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
334
335 static void
336 print_spacelist(cfg_printer_t *pctx, cfg_obj_t *obj);
337
338 static void
339 print_sockaddr(cfg_printer_t *pctx, cfg_obj_t *obj);
340
341 static isc_result_t
342 parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
343
344 static isc_result_t
345 parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
346
347 static void
348 print_bracketed_list(cfg_printer_t *pctx, cfg_obj_t *obj);
349
350 static isc_result_t
351 parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
352
353 static isc_result_t
354 parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
355
356 static void
357 print_keyvalue(cfg_printer_t *pctx, cfg_obj_t *obj);
358
359 static isc_result_t
360 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
361                  cfg_type_t *elttype, isc_symtab_t *symtab,
362                  isc_boolean_t callback);
363
364 static void
365 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
366
367 static isc_result_t
368 cfg_gettoken(cfg_parser_t *pctx, int options);
369
370 static void
371 cfg_ungettoken(cfg_parser_t *pctx);
372
373 static isc_result_t
374 cfg_peektoken(cfg_parser_t *pctx, int options);
375
376 static isc_result_t
377 cfg_getstringtoken(cfg_parser_t *pctx);
378
379 static void
380 parser_error(cfg_parser_t *pctx, unsigned int flags,
381              const char *fmt, ...) ISC_FORMAT_PRINTF(3, 4);
382
383 static void
384 parser_warning(cfg_parser_t *pctx, unsigned int flags,
385                const char *fmt, ...) ISC_FORMAT_PRINTF(3, 4);
386
387 static void
388 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
389                 unsigned int flags, const char *format, va_list args);
390
391 static void
392 print_uint32(cfg_printer_t *pctx, cfg_obj_t *obj);
393
394 static void
395 print_ustring(cfg_printer_t *pctx, cfg_obj_t *obj);
396
397 static isc_result_t
398 parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
399
400 /*
401  * Data representations.  These correspond to members of the
402  * "value" union in struct cfg_obj (except "void", which does
403  * not need a union member).
404  */
405
406 cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
407 cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
408 cfg_rep_t cfg_rep_string = { "string", free_string };
409 cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
410 cfg_rep_t cfg_rep_map = { "map", free_map };
411 cfg_rep_t cfg_rep_list = { "list", free_list };
412 cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
413 cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
414 cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
415 cfg_rep_t cfg_rep_void = { "void", free_noop };
416
417 /*
418  * Forward declarations of configuration type definitions.
419  * Additional types are declared publicly in cfg.h.
420  */
421
422 static cfg_type_t cfg_type_boolean;
423 static cfg_type_t cfg_type_uint32;
424 static cfg_type_t cfg_type_qstring;
425 static cfg_type_t cfg_type_astring;
426 static cfg_type_t cfg_type_ustring;
427 static cfg_type_t cfg_type_optional_port;
428 static cfg_type_t cfg_type_bracketed_aml;
429 static cfg_type_t cfg_type_acl;
430 static cfg_type_t cfg_type_portiplist;
431 static cfg_type_t cfg_type_bracketed_sockaddrlist;
432 static cfg_type_t cfg_type_sockaddr;
433 static cfg_type_t cfg_type_netaddr;
434 static cfg_type_t cfg_type_optional_keyref;
435 static cfg_type_t cfg_type_options;
436 static cfg_type_t cfg_type_view;
437 static cfg_type_t cfg_type_viewopts;
438 static cfg_type_t cfg_type_key;
439 static cfg_type_t cfg_type_server;
440 static cfg_type_t cfg_type_controls;
441 static cfg_type_t cfg_type_bracketed_sockaddrkeylist;
442 static cfg_type_t cfg_type_querysource4;
443 static cfg_type_t cfg_type_querysource6;
444 static cfg_type_t cfg_type_querysource;
445 static cfg_type_t cfg_type_sockaddr4wild;
446 static cfg_type_t cfg_type_sockaddr6wild;
447 static cfg_type_t cfg_type_sockaddr;
448 static cfg_type_t cfg_type_netprefix;
449 static cfg_type_t cfg_type_zone;
450 static cfg_type_t cfg_type_zoneopts;
451 static cfg_type_t cfg_type_logging;
452 static cfg_type_t cfg_type_optional_facility;
453 static cfg_type_t cfg_type_void;
454 static cfg_type_t cfg_type_optional_class;
455 static cfg_type_t cfg_type_destinationlist;
456 static cfg_type_t cfg_type_size;
457 static cfg_type_t cfg_type_sizenodefault;
458 static cfg_type_t cfg_type_negated;
459 static cfg_type_t cfg_type_addrmatchelt;
460 static cfg_type_t cfg_type_unsupported;
461 static cfg_type_t cfg_type_token;
462 static cfg_type_t cfg_type_server_key_kludge;
463 static cfg_type_t cfg_type_optional_facility;
464 static cfg_type_t cfg_type_logseverity;
465 static cfg_type_t cfg_type_logfile;
466 static cfg_type_t cfg_type_lwres;
467 static cfg_type_t cfg_type_controls_sockaddr;
468 static cfg_type_t cfg_type_notifytype;
469 static cfg_type_t cfg_type_dialuptype;
470
471 /*
472  * Configuration type definitions.
473  */
474
475 /* tkey-dhkey */
476
477 static cfg_tuplefielddef_t tkey_dhkey_fields[] = {
478         { "name", &cfg_type_qstring, 0 },
479         { "keyid", &cfg_type_uint32, 0 },
480         { NULL, NULL, 0 }
481 };
482
483 static cfg_type_t cfg_type_tkey_dhkey = {
484         "tkey-dhkey", parse_tuple, print_tuple, &cfg_rep_tuple,
485         tkey_dhkey_fields
486 };
487
488 /* listen-on */
489
490 static cfg_tuplefielddef_t listenon_fields[] = {
491         { "port", &cfg_type_optional_port, 0 },
492         { "acl", &cfg_type_bracketed_aml, 0 },
493         { NULL, NULL, 0 }
494 };
495 static cfg_type_t cfg_type_listenon = {
496         "listenon", parse_tuple, print_tuple, &cfg_rep_tuple, listenon_fields };
497
498 /* acl */
499
500 static cfg_tuplefielddef_t acl_fields[] = {
501         { "name", &cfg_type_astring, 0 },
502         { "value", &cfg_type_bracketed_aml, 0 },
503         { NULL, NULL, 0 }
504 };
505
506 static cfg_type_t cfg_type_acl = {
507         "acl", parse_tuple, print_tuple, &cfg_rep_tuple, acl_fields };
508
509
510 /*
511  * "sockaddrkeylist", a list of socket addresses with optional keys
512  * and an optional default port, as used in the masters option.
513  * E.g.,
514  *   "port 1234 { 10.0.0.1 key foo; 1::2 port 69; }"
515  */
516
517 static cfg_tuplefielddef_t sockaddrkey_fields[] = {
518         { "sockaddr", &cfg_type_sockaddr, 0 },
519         { "key", &cfg_type_optional_keyref, 0 },
520         { NULL, NULL, 0 },
521 };
522
523 static cfg_type_t cfg_type_sockaddrkey = {
524         "sockaddrkey", parse_tuple, print_tuple, &cfg_rep_tuple,
525         sockaddrkey_fields
526 };
527
528 static cfg_type_t cfg_type_bracketed_sockaddrkeylist = {
529         "bracketed_sockaddrkeylist", parse_bracketed_list,
530         print_bracketed_list, &cfg_rep_list, &cfg_type_sockaddrkey
531 };
532
533 static cfg_tuplefielddef_t sockaddrkeylist_fields[] = {
534         { "port", &cfg_type_optional_port, 0 },
535         { "addresses", &cfg_type_bracketed_sockaddrkeylist, 0 },
536         { NULL, NULL, 0 }
537 };
538 static cfg_type_t cfg_type_sockaddrkeylist = {
539         "sockaddrkeylist", parse_tuple, print_tuple, &cfg_rep_tuple,
540         sockaddrkeylist_fields
541 };
542
543 /*
544  * A list of socket addresses with an optional default port,
545  * as used in the also-notify option.  E.g.,
546  * "port 1234 { 10.0.0.1; 1::2 port 69; }"
547  */
548 static cfg_tuplefielddef_t portiplist_fields[] = {
549         { "port", &cfg_type_optional_port, 0 },
550         { "addresses", &cfg_type_bracketed_sockaddrlist, 0 },
551         { NULL, NULL, 0 }
552 };
553 static cfg_type_t cfg_type_portiplist = {
554         "portiplist", parse_tuple, print_tuple, &cfg_rep_tuple,
555         portiplist_fields
556 };
557
558 /*
559  * A public key, as in the "pubkey" statement.
560  */
561 static cfg_tuplefielddef_t pubkey_fields[] = {
562         { "flags", &cfg_type_uint32, 0 },
563         { "protocol", &cfg_type_uint32, 0 },
564         { "algorithm", &cfg_type_uint32, 0 },
565         { "key", &cfg_type_qstring, 0 },
566         { NULL, NULL, 0 }
567 };
568 static cfg_type_t cfg_type_pubkey = {
569         "pubkey", parse_tuple, print_tuple, &cfg_rep_tuple, pubkey_fields };
570
571
572 /*
573  * A list of RR types, used in grant statements.
574  * Note that the old parser allows quotes around the RR type names.
575  */
576 static cfg_type_t cfg_type_rrtypelist = {
577         "rrtypelist", parse_spacelist, print_spacelist, &cfg_rep_list,
578         &cfg_type_astring
579 };
580
581 static const char *mode_enums[] = { "grant", "deny", NULL };
582 static cfg_type_t cfg_type_mode = {
583         "mode", parse_enum, print_ustring, &cfg_rep_string,
584         &mode_enums
585 };
586
587 static const char *matchtype_enums[] = {
588         "name", "subdomain", "wildcard", "self", NULL };
589 static cfg_type_t cfg_type_matchtype = {
590         "matchtype", parse_enum, print_ustring, &cfg_rep_string,
591         &matchtype_enums
592 };
593
594 /*
595  * A grant statement, used in the update policy.
596  */
597 static cfg_tuplefielddef_t grant_fields[] = {
598         { "mode", &cfg_type_mode, 0 },
599         { "identity", &cfg_type_astring, 0 }, /* domain name */ 
600         { "matchtype", &cfg_type_matchtype, 0 },
601         { "name", &cfg_type_astring, 0 }, /* domain name */
602         { "types", &cfg_type_rrtypelist, 0 },
603         { NULL, NULL, 0 }
604 };
605 static cfg_type_t cfg_type_grant = {
606         "grant", parse_tuple, print_tuple, &cfg_rep_tuple, grant_fields };
607
608 static cfg_type_t cfg_type_updatepolicy = {
609         "update_policy", parse_bracketed_list, print_bracketed_list,
610         &cfg_rep_list, &cfg_type_grant
611 };
612
613 /*
614  * A view statement.
615  */
616 static cfg_tuplefielddef_t view_fields[] = {
617         { "name", &cfg_type_astring, 0 },
618         { "class", &cfg_type_optional_class, 0 },
619         { "options", &cfg_type_viewopts, 0 },
620         { NULL, NULL, 0 }
621 };
622 static cfg_type_t cfg_type_view = {
623         "view", parse_tuple, print_tuple, &cfg_rep_tuple, view_fields };
624
625 /*
626  * A zone statement.
627  */
628 static cfg_tuplefielddef_t zone_fields[] = {
629         { "name", &cfg_type_astring, 0 },
630         { "class", &cfg_type_optional_class, 0 },
631         { "options", &cfg_type_zoneopts, 0 },
632         { NULL, NULL, 0 }
633 };
634 static cfg_type_t cfg_type_zone = {
635         "zone", parse_tuple, print_tuple, &cfg_rep_tuple, zone_fields };
636
637 /*
638  * A "category" clause in the "logging" statement.
639  */
640 static cfg_tuplefielddef_t category_fields[] = {
641         { "name", &cfg_type_astring, 0 },
642         { "destinations", &cfg_type_destinationlist,0 },
643         { NULL, NULL, 0 }
644 };
645 static cfg_type_t cfg_type_category = {
646         "category", parse_tuple, print_tuple, &cfg_rep_tuple, category_fields };
647
648
649 /*
650  * A trusted key, as used in the "trusted-keys" statement.
651  */
652 static cfg_tuplefielddef_t trustedkey_fields[] = {
653         { "name", &cfg_type_astring, 0 },
654         { "flags", &cfg_type_uint32, 0 },
655         { "protocol", &cfg_type_uint32, 0 },
656         { "algorithm", &cfg_type_uint32, 0 },
657         { "key", &cfg_type_qstring, 0 },
658         { NULL, NULL, 0 }
659 };
660 static cfg_type_t cfg_type_trustedkey = {
661         "trustedkey", parse_tuple, print_tuple, &cfg_rep_tuple,
662         trustedkey_fields
663 };
664
665
666 static keyword_type_t wild_class_kw = { "class", &cfg_type_ustring };
667
668 static cfg_type_t cfg_type_optional_wild_class = {
669         "optional_wild_class", parse_optional_keyvalue,
670         print_keyvalue, &cfg_rep_string, &wild_class_kw
671 };
672
673 static keyword_type_t wild_type_kw = { "type", &cfg_type_ustring };
674
675 static cfg_type_t cfg_type_optional_wild_type = {
676         "optional_wild_type", parse_optional_keyvalue,
677         print_keyvalue, &cfg_rep_string, &wild_type_kw
678 };
679
680 static keyword_type_t wild_name_kw = { "name", &cfg_type_qstring };
681
682 static cfg_type_t cfg_type_optional_wild_name = {
683         "optional_wild_name", parse_optional_keyvalue,
684         print_keyvalue, &cfg_rep_string, &wild_name_kw
685 };
686
687 /*
688  * An rrset ordering element.
689  */
690 static cfg_tuplefielddef_t rrsetorderingelement_fields[] = {
691         { "class", &cfg_type_optional_wild_class, 0 },
692         { "type", &cfg_type_optional_wild_type, 0 },
693         { "name", &cfg_type_optional_wild_name, 0 },
694         { "order", &cfg_type_ustring, 0 }, /* must be literal "order" */ 
695         { "ordering", &cfg_type_ustring, 0 },
696         { NULL, NULL, 0 }
697 };
698 static cfg_type_t cfg_type_rrsetorderingelement = {
699         "rrsetorderingelement", parse_tuple, print_tuple, &cfg_rep_tuple,
700         rrsetorderingelement_fields
701 };
702
703 /*
704  * A global or view "check-names" option.  Note that the zone
705  * "check-names" option has a different syntax.
706  */
707 static cfg_tuplefielddef_t checknames_fields[] = {
708         { "type", &cfg_type_ustring, 0 },
709         { "mode", &cfg_type_ustring, 0 },
710         { NULL, NULL, 0 }
711 };
712 static cfg_type_t cfg_type_checknames = {
713         "checknames", parse_tuple, print_tuple, &cfg_rep_tuple,
714         checknames_fields
715 };
716
717 static cfg_type_t cfg_type_bracketed_sockaddrlist = {
718         "bracketed_sockaddrlist", parse_bracketed_list, print_bracketed_list,
719         &cfg_rep_list, &cfg_type_sockaddr
720 };
721
722 static cfg_type_t cfg_type_rrsetorder = {
723         "rrsetorder", parse_bracketed_list, print_bracketed_list,
724         &cfg_rep_list, &cfg_type_rrsetorderingelement
725 };
726
727 static keyword_type_t port_kw = { "port", &cfg_type_uint32 };
728
729 static cfg_type_t cfg_type_optional_port = {
730         "optional_port", parse_optional_keyvalue, print_keyvalue,
731         &cfg_rep_uint32, &port_kw
732 };
733
734 /* A list of keys, as in the "key" clause of the controls statement. */
735 static cfg_type_t cfg_type_keylist = {
736         "keylist", parse_bracketed_list, print_bracketed_list, &cfg_rep_list,
737         &cfg_type_astring
738 };
739
740 static cfg_type_t cfg_type_trustedkeys = {
741         "trusted-keys", parse_bracketed_list, print_bracketed_list, &cfg_rep_list,
742         &cfg_type_trustedkey
743 };
744
745 /*
746  * An implicit list.  These are formed by clauses that occur multiple times.
747  */
748 static cfg_type_t cfg_type_implicitlist = {
749         "implicitlist", NULL, print_list, &cfg_rep_list, NULL };
750
751 static const char *forwardtype_enums[] = { "first", "only", NULL };
752 static cfg_type_t cfg_type_forwardtype = {
753         "forwardtype", parse_enum, print_ustring, &cfg_rep_string,
754         &forwardtype_enums
755 };
756
757 static const char *zonetype_enums[] = {
758         "master", "slave", "stub", "hint", "forward", "delegation-only", NULL };
759 static cfg_type_t cfg_type_zonetype = {
760         "zonetype", parse_enum, print_ustring, &cfg_rep_string,
761         &zonetype_enums
762 };
763
764 static const char *loglevel_enums[] = {
765         "critical", "error", "warning", "notice", "info", "dynamic", NULL };
766 static cfg_type_t cfg_type_loglevel = {
767         "loglevel", parse_enum, print_ustring, &cfg_rep_string,
768         &loglevel_enums
769 };
770
771 static const char *transferformat_enums[] = {
772         "many-answers", "one-answer", NULL };
773 static cfg_type_t cfg_type_transferformat = {
774         "transferformat", parse_enum, print_ustring, &cfg_rep_string,
775         &transferformat_enums
776 };
777
778 /*
779  * Clauses that can be found within the top level of the named.conf
780  * file only.
781  */
782 static cfg_clausedef_t
783 namedconf_clauses[] = {
784         { "options", &cfg_type_options, 0 },
785         { "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI },
786         { "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI },
787         { "logging", &cfg_type_logging, 0 },
788         { "view", &cfg_type_view, CFG_CLAUSEFLAG_MULTI },
789         { "lwres", &cfg_type_lwres, CFG_CLAUSEFLAG_MULTI },
790         { NULL, NULL, 0 }
791 };
792
793 /*
794  * Clauses that can occur at the top level or in the view
795  * statement, but not in the options block.
796  */
797 static cfg_clausedef_t
798 namedconf_or_view_clauses[] = {
799         { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
800         { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI },
801         { "server", &cfg_type_server, CFG_CLAUSEFLAG_MULTI },
802 #ifdef ISC_RFC2535
803         { "trusted-keys", &cfg_type_trustedkeys, CFG_CLAUSEFLAG_MULTI },
804 #else
805         { "trusted-keys", &cfg_type_trustedkeys,
806                  CFG_CLAUSEFLAG_MULTI|CFG_CLAUSEFLAG_OBSOLETE },
807 #endif
808         { NULL, NULL, 0 }
809 };
810
811 /*
812  * Clauses that can be found within the 'options' statement.
813  */
814 static cfg_clausedef_t
815 options_clauses[] = {
816         { "blackhole", &cfg_type_bracketed_aml, 0 },
817         { "coresize", &cfg_type_size, 0 },
818         { "datasize", &cfg_type_size, 0 },
819         { "deallocate-on-exit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
820         { "directory", &cfg_type_qstring, CFG_CLAUSEFLAG_CALLBACK },
821         { "dump-file", &cfg_type_qstring, 0 },
822         { "fake-iquery", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
823         { "files", &cfg_type_size, 0 },
824         { "has-old-clients", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
825         { "heartbeat-interval", &cfg_type_uint32, 0 },
826         { "host-statistics", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTIMP },
827         { "interface-interval", &cfg_type_uint32, 0 },
828         { "listen-on", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
829         { "listen-on-v6", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
830         { "match-mapped-addresses", &cfg_type_boolean, 0 },
831         { "memstatistics-file", &cfg_type_qstring, CFG_CLAUSEFLAG_NOTIMP },
832         { "multiple-cnames", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
833         { "named-xfer", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
834         { "pid-file", &cfg_type_qstring, 0 },
835         { "port", &cfg_type_uint32, 0 },
836         { "random-device", &cfg_type_qstring, 0 },
837         { "recursive-clients", &cfg_type_uint32, 0 },
838         { "rrset-order", &cfg_type_rrsetorder, CFG_CLAUSEFLAG_NOTIMP },
839         { "serial-queries", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
840         { "serial-query-rate", &cfg_type_uint32, 0 },
841         { "stacksize", &cfg_type_size, 0 },
842         { "statistics-file", &cfg_type_qstring, 0 },
843         { "statistics-interval", &cfg_type_uint32, CFG_CLAUSEFLAG_NYI },
844         { "tcp-clients", &cfg_type_uint32, 0 },
845         { "tkey-dhkey", &cfg_type_tkey_dhkey, 0 },
846         { "tkey-gssapi-credential", &cfg_type_qstring, 0 },
847         { "tkey-domain", &cfg_type_qstring, 0 },
848         { "transfers-per-ns", &cfg_type_uint32, 0 },
849         { "transfers-in", &cfg_type_uint32, 0 },
850         { "transfers-out", &cfg_type_uint32, 0 },
851         { "treat-cr-as-space", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
852         { "use-id-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
853         { "use-ixfr", &cfg_type_boolean, 0 },
854         { "version", &cfg_type_qstring, 0 },
855         { NULL, NULL, 0 }
856 };
857
858
859 static cfg_type_t cfg_type_namelist = {
860         "namelist", parse_bracketed_list, print_bracketed_list,
861         &cfg_rep_list, &cfg_type_qstring };
862
863 static keyword_type_t exclude_kw = { "exclude", &cfg_type_namelist };
864
865 static cfg_type_t cfg_type_optional_exclude = {
866         "optional_exclude", parse_optional_keyvalue, print_keyvalue,
867         &cfg_rep_list, &exclude_kw };
868
869 /*
870  * Clauses that can be found within the 'view' statement,
871  * with defaults in the 'options' statement.
872  */
873
874 static cfg_clausedef_t
875 view_clauses[] = {
876         { "allow-recursion", &cfg_type_bracketed_aml, 0 },
877         { "allow-v6-synthesis", &cfg_type_bracketed_aml, 0 },
878         { "sortlist", &cfg_type_bracketed_aml, 0 },
879         { "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_NOTIMP },
880         { "auth-nxdomain", &cfg_type_boolean, CFG_CLAUSEFLAG_NEWDEFAULT },
881         { "minimal-responses", &cfg_type_boolean, 0 },
882         { "recursion", &cfg_type_boolean, 0 },
883         { "provide-ixfr", &cfg_type_boolean, 0 },
884         { "request-ixfr", &cfg_type_boolean, 0 },
885         { "fetch-glue", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
886         { "rfc2308-type1", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
887         { "additional-from-auth", &cfg_type_boolean, 0 },
888         { "additional-from-cache", &cfg_type_boolean, 0 },
889         /*
890          * Note that the query-source option syntax is different
891          * from the other -source options.
892          */
893         { "query-source", &cfg_type_querysource4, 0 },
894         { "query-source-v6", &cfg_type_querysource6, 0 },
895         { "cleaning-interval", &cfg_type_uint32, 0 },
896         { "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
897         { "lame-ttl", &cfg_type_uint32, 0 },
898         { "max-ncache-ttl", &cfg_type_uint32, 0 },
899         { "max-cache-ttl", &cfg_type_uint32, 0 },
900         { "transfer-format", &cfg_type_transferformat, 0 },
901         { "max-cache-size", &cfg_type_sizenodefault, 0 },
902         { "check-names", &cfg_type_checknames,
903           CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_NOTIMP },
904         { "cache-file", &cfg_type_qstring, 0 },
905         { "root-delegation-only",  &cfg_type_optional_exclude, 0 },
906         { NULL, NULL, 0 }
907 };
908
909 /*
910  * Clauses that can be found within the 'view' statement only.
911  */
912 static cfg_clausedef_t
913 view_only_clauses[] = {
914         { "match-clients", &cfg_type_bracketed_aml, 0 },
915         { "match-destinations", &cfg_type_bracketed_aml, 0 },
916         { "match-recursive-only", &cfg_type_boolean, 0 },
917         { NULL, NULL, 0 }
918 };
919
920 /*
921  * Clauses that can be found in a 'zone' statement,
922  * with defaults in the 'view' or 'options' statement.
923  */
924 static cfg_clausedef_t
925 zone_clauses[] = {
926         { "allow-query", &cfg_type_bracketed_aml, 0 },
927         { "allow-transfer", &cfg_type_bracketed_aml, 0 },
928         { "allow-update-forwarding", &cfg_type_bracketed_aml, 0 },
929         { "allow-notify", &cfg_type_bracketed_aml, 0 },
930         { "notify", &cfg_type_notifytype, 0 },
931         { "notify-source", &cfg_type_sockaddr4wild, 0 },
932         { "notify-source-v6", &cfg_type_sockaddr6wild, 0 },
933         { "also-notify", &cfg_type_portiplist, 0 },
934         { "dialup", &cfg_type_dialuptype, 0 },
935         { "forward", &cfg_type_forwardtype, 0 },
936         { "forwarders", &cfg_type_portiplist, 0 },
937         { "maintain-ixfr-base", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
938         { "max-ixfr-log-size", &cfg_type_size, CFG_CLAUSEFLAG_OBSOLETE },
939         { "transfer-source", &cfg_type_sockaddr4wild, 0 },
940         { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 },
941         { "max-transfer-time-in", &cfg_type_uint32, 0 },
942         { "max-transfer-time-out", &cfg_type_uint32, 0 },
943         { "max-transfer-idle-in", &cfg_type_uint32, 0 },
944         { "max-transfer-idle-out", &cfg_type_uint32, 0 },
945         { "max-retry-time", &cfg_type_uint32, 0 },
946         { "min-retry-time", &cfg_type_uint32, 0 },
947         { "max-refresh-time", &cfg_type_uint32, 0 },
948         { "min-refresh-time", &cfg_type_uint32, 0 },
949         { "sig-validity-interval", &cfg_type_uint32, 0 },
950         { "zone-statistics", &cfg_type_boolean, 0 },
951         { NULL, NULL, 0 }
952 };
953
954 /*
955  * Clauses that can be found in a 'zone' statement
956  * only.
957  */
958 static cfg_clausedef_t
959 zone_only_clauses[] = {
960         { "type", &cfg_type_zonetype, 0 },
961         { "allow-update", &cfg_type_bracketed_aml, 0 },
962         { "file", &cfg_type_qstring, 0 },
963         { "ixfr-base", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
964         { "ixfr-tmp-file", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
965         { "masters", &cfg_type_sockaddrkeylist, 0 },
966         { "pubkey", &cfg_type_pubkey,
967           CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_OBSOLETE },
968         { "update-policy", &cfg_type_updatepolicy, 0 },
969         { "database", &cfg_type_astring, 0 },
970         { "delegation-only", &cfg_type_boolean, 0 },
971         /*
972          * Note that the format of the check-names option is different between
973          * the zone options and the global/view options.  Ugh.
974          */
975         { "check-names", &cfg_type_ustring, CFG_CLAUSEFLAG_NOTIMP },
976         { NULL, NULL, 0 }
977 };
978
979
980 /* The top-level named.conf syntax. */
981
982 static cfg_clausedef_t *
983 namedconf_clausesets[] = {
984         namedconf_clauses,
985         namedconf_or_view_clauses,
986         NULL
987 };
988
989 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_namedconf = {
990         "namedconf", parse_mapbody, print_mapbody, &cfg_rep_map,
991         namedconf_clausesets
992 };
993
994 /* The "options" statement syntax. */
995
996 static cfg_clausedef_t *
997 options_clausesets[] = {
998         options_clauses,
999         view_clauses,
1000         zone_clauses,
1001         NULL
1002 };
1003 static cfg_type_t cfg_type_options = {
1004         "options", parse_map, print_map, &cfg_rep_map, options_clausesets };
1005
1006 /* The "view" statement syntax. */
1007
1008 static cfg_clausedef_t *
1009 view_clausesets[] = {
1010         view_only_clauses,
1011         namedconf_or_view_clauses,
1012         view_clauses,
1013         zone_clauses,
1014         NULL
1015 };
1016 static cfg_type_t cfg_type_viewopts = {
1017         "view", parse_map, print_map, &cfg_rep_map, view_clausesets };
1018
1019 /* The "zone" statement syntax. */
1020
1021 static cfg_clausedef_t *
1022 zone_clausesets[] = {
1023         zone_only_clauses,
1024         zone_clauses,
1025         NULL
1026 };
1027 static cfg_type_t cfg_type_zoneopts = {
1028         "zoneopts", parse_map, print_map, &cfg_rep_map, zone_clausesets };
1029
1030 /*
1031  * Clauses that can be found within the 'key' statement.
1032  */
1033 static cfg_clausedef_t
1034 key_clauses[] = {
1035         { "algorithm", &cfg_type_astring, 0 },
1036         { "secret", &cfg_type_astring, 0 },
1037         { NULL, NULL, 0 }
1038 };
1039
1040 static cfg_clausedef_t *
1041 key_clausesets[] = {
1042         key_clauses,
1043         NULL
1044 };
1045 static cfg_type_t cfg_type_key = {
1046         "key", parse_named_map, print_map, &cfg_rep_map, key_clausesets };
1047
1048
1049 /*
1050  * Clauses that can be found in a 'server' statement.
1051  */
1052 static cfg_clausedef_t
1053 server_clauses[] = {
1054         { "bogus", &cfg_type_boolean, 0 },
1055         { "provide-ixfr", &cfg_type_boolean, 0 },
1056         { "request-ixfr", &cfg_type_boolean, 0 },
1057         { "support-ixfr", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1058         { "transfers", &cfg_type_uint32, 0 },
1059         { "transfer-format", &cfg_type_transferformat, 0 },
1060         { "keys", &cfg_type_server_key_kludge, 0 },
1061         { "edns", &cfg_type_boolean, 0 },
1062         { NULL, NULL, 0 }
1063 };
1064 static cfg_clausedef_t *
1065 server_clausesets[] = {
1066         server_clauses,
1067         NULL
1068 };
1069 static cfg_type_t cfg_type_server = {
1070         "server", parse_addressed_map, print_map, &cfg_rep_map,
1071         server_clausesets
1072 };
1073
1074
1075 /*
1076  * Clauses that can be found in a 'channel' clause in the
1077  * 'logging' statement.
1078  *
1079  * These have some additional constraints that need to be
1080  * checked after parsing:
1081  *  - There must exactly one of file/syslog/null/stderr
1082  *
1083  */
1084 static cfg_clausedef_t
1085 channel_clauses[] = {
1086         /* Destinations.  We no longer require these to be first. */
1087         { "file", &cfg_type_logfile, 0 },
1088         { "syslog", &cfg_type_optional_facility, 0 },
1089         { "null", &cfg_type_void, 0 },
1090         { "stderr", &cfg_type_void, 0 },
1091         /* Options.  We now accept these for the null channel, too. */
1092         { "severity", &cfg_type_logseverity, 0 },
1093         { "print-time", &cfg_type_boolean, 0 },
1094         { "print-severity", &cfg_type_boolean, 0 },
1095         { "print-category", &cfg_type_boolean, 0 },
1096         { NULL, NULL, 0 }
1097 };
1098 static cfg_clausedef_t *
1099 channel_clausesets[] = {
1100         channel_clauses,
1101         NULL
1102 };
1103 static cfg_type_t cfg_type_channel = {
1104         "channel", parse_named_map, print_map,
1105         &cfg_rep_map, channel_clausesets
1106 };
1107
1108 /* A list of log destination, used in the "category" clause. */
1109 static cfg_type_t cfg_type_destinationlist = {
1110         "destinationlist", parse_bracketed_list, print_bracketed_list,
1111         &cfg_rep_list, &cfg_type_astring };
1112
1113 /*
1114  * Clauses that can be found in a 'logging' statement.
1115  */
1116 static cfg_clausedef_t
1117 logging_clauses[] = {
1118         { "channel", &cfg_type_channel, CFG_CLAUSEFLAG_MULTI },
1119         { "category", &cfg_type_category, CFG_CLAUSEFLAG_MULTI },
1120         { NULL, NULL, 0 }
1121 };
1122 static cfg_clausedef_t *
1123 logging_clausesets[] = {
1124         logging_clauses,
1125         NULL
1126 };
1127 static cfg_type_t cfg_type_logging = {
1128         "logging", parse_map, print_map, &cfg_rep_map, logging_clausesets };
1129
1130
1131 /* Functions. */
1132
1133 static void
1134 print_obj(cfg_printer_t *pctx, cfg_obj_t *obj) {
1135         obj->type->print(pctx, obj);
1136 }
1137
1138 static void
1139 print(cfg_printer_t *pctx, const char *text, int len) {
1140         pctx->f(pctx->closure, text, len);
1141 }
1142
1143 static void
1144 print_open(cfg_printer_t *pctx) {
1145         print(pctx, "{\n", 2);
1146         pctx->indent++;
1147 }
1148
1149 static void
1150 print_indent(cfg_printer_t *pctx) {
1151         int indent = pctx->indent;
1152         while (indent > 0) {
1153                 print(pctx, "\t", 1);
1154                 indent--;
1155         }
1156 }
1157
1158 static void
1159 print_close(cfg_printer_t *pctx) {
1160         pctx->indent--;
1161         print_indent(pctx);
1162         print(pctx, "}", 1);
1163 }
1164
1165 static isc_result_t
1166 parse(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1167         isc_result_t result;
1168         INSIST(ret != NULL && *ret == NULL);
1169         result = type->parse(pctx, type, ret);
1170         if (result != ISC_R_SUCCESS)
1171                 return (result);
1172         INSIST(*ret != NULL);
1173         return (ISC_R_SUCCESS);
1174 }
1175
1176 void
1177 cfg_print(cfg_obj_t *obj,
1178           void (*f)(void *closure, const char *text, int textlen),
1179           void *closure)
1180 {
1181         cfg_printer_t pctx;
1182         pctx.f = f;
1183         pctx.closure = closure;
1184         pctx.indent = 0;
1185         obj->type->print(&pctx, obj);
1186 }
1187
1188
1189 /* Tuples. */
1190   
1191 static isc_result_t
1192 create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1193         isc_result_t result;
1194         const cfg_tuplefielddef_t *fields = type->of;
1195         const cfg_tuplefielddef_t *f;
1196         cfg_obj_t *obj = NULL;
1197         unsigned int nfields = 0;
1198         int i;
1199
1200         for (f = fields; f->name != NULL; f++)
1201                 nfields++;
1202
1203         CHECK(create_cfgobj(pctx, type, &obj));
1204         obj->value.tuple = isc_mem_get(pctx->mctx,
1205                                        nfields * sizeof(cfg_obj_t *));
1206         if (obj->value.tuple == NULL) {
1207                 result = ISC_R_NOMEMORY;
1208                 goto cleanup;
1209         }
1210         for (f = fields, i = 0; f->name != NULL; f++, i++)
1211                 obj->value.tuple[i] = NULL;
1212         *ret = obj;
1213         return (ISC_R_SUCCESS);
1214
1215  cleanup:
1216         if (obj != NULL)
1217                 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
1218         return (result);
1219 }
1220
1221 static isc_result_t
1222 parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1223 {
1224         isc_result_t result;
1225         const cfg_tuplefielddef_t *fields = type->of;
1226         const cfg_tuplefielddef_t *f;
1227         cfg_obj_t *obj = NULL;
1228         unsigned int i;
1229
1230         CHECK(create_tuple(pctx, type, &obj));
1231         for (f = fields, i = 0; f->name != NULL; f++, i++)
1232                 CHECK(parse(pctx, f->type, &obj->value.tuple[i]));
1233
1234         *ret = obj;
1235         return (ISC_R_SUCCESS);
1236
1237  cleanup:
1238         CLEANUP_OBJ(obj);
1239         return (result);
1240 }
1241
1242 static void
1243 print_tuple(cfg_printer_t *pctx, cfg_obj_t *obj) {
1244         unsigned int i;
1245         const cfg_tuplefielddef_t *fields = obj->type->of;
1246         const cfg_tuplefielddef_t *f;
1247         isc_boolean_t need_space = ISC_FALSE;
1248
1249         for (f = fields, i = 0; f->name != NULL; f++, i++) {
1250                 cfg_obj_t *fieldobj = obj->value.tuple[i];
1251                 if (need_space)
1252                         print(pctx, " ", 1);
1253                 print_obj(pctx, fieldobj);
1254                 need_space = ISC_TF(fieldobj->type->print != print_void);
1255         }
1256 }
1257
1258 static void
1259 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
1260         unsigned int i;
1261         const cfg_tuplefielddef_t *fields = obj->type->of;
1262         const cfg_tuplefielddef_t *f;
1263         unsigned int nfields = 0;
1264
1265         if (obj->value.tuple == NULL)
1266                 return;
1267
1268         for (f = fields, i = 0; f->name != NULL; f++, i++) {
1269                 CLEANUP_OBJ(obj->value.tuple[i]);
1270                 nfields++;
1271         }
1272         isc_mem_put(pctx->mctx, obj->value.tuple,
1273                     nfields * sizeof(cfg_obj_t *));
1274 }
1275
1276 isc_boolean_t
1277 cfg_obj_istuple(cfg_obj_t *obj) {
1278         REQUIRE(obj != NULL);
1279         return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
1280 }
1281
1282 cfg_obj_t *
1283 cfg_tuple_get(cfg_obj_t *tupleobj, const char* name) {
1284         unsigned int i;
1285         const cfg_tuplefielddef_t *fields;
1286         const cfg_tuplefielddef_t *f;
1287         
1288         REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
1289
1290         fields = tupleobj->type->of;
1291         for (f = fields, i = 0; f->name != NULL; f++, i++) {
1292                 if (strcmp(f->name, name) == 0)
1293                         return (tupleobj->value.tuple[i]);
1294         }
1295         INSIST(0);
1296         return (NULL);
1297 }
1298
1299 /*
1300  * Parse a required special character.
1301  */
1302 static isc_result_t
1303 parse_special(cfg_parser_t *pctx, int special) {
1304         isc_result_t result;
1305         CHECK(cfg_gettoken(pctx, 0));
1306         if (pctx->token.type == isc_tokentype_special &&
1307             pctx->token.value.as_char == special)
1308                 return (ISC_R_SUCCESS);
1309
1310         parser_error(pctx, LOG_NEAR, "'%c' expected", special);
1311         return (ISC_R_UNEXPECTEDTOKEN);
1312  cleanup:
1313         return (result);
1314 }
1315
1316 /*
1317  * Parse a required semicolon.  If it is not there, log
1318  * an error and increment the error count but continue
1319  * parsing.  Since the next token is pushed back,
1320  * care must be taken to make sure it is eventually
1321  * consumed or an infinite loop may result.
1322  */
1323 static isc_result_t
1324 parse_semicolon(cfg_parser_t *pctx) {
1325         isc_result_t result;
1326         CHECK(cfg_gettoken(pctx, 0));
1327         if (pctx->token.type == isc_tokentype_special &&
1328             pctx->token.value.as_char == ';')
1329                 return (ISC_R_SUCCESS);
1330
1331         parser_error(pctx, LOG_BEFORE, "missing ';'");
1332         cfg_ungettoken(pctx);
1333  cleanup:
1334         return (result);
1335 }
1336
1337 /*
1338  * Parse EOF, logging and returning an error if not there.
1339  */
1340 static isc_result_t
1341 parse_eof(cfg_parser_t *pctx) {
1342         isc_result_t result;
1343         CHECK(cfg_gettoken(pctx, 0));
1344
1345         if (pctx->token.type == isc_tokentype_eof)
1346                 return (ISC_R_SUCCESS);
1347
1348         parser_error(pctx, LOG_NEAR, "syntax error");
1349         return (ISC_R_UNEXPECTEDTOKEN);
1350  cleanup:
1351         return(result);
1352 }
1353
1354 /* A list of files, used internally for pctx->files. */
1355
1356 static cfg_type_t cfg_type_filelist = {
1357         "filelist", NULL, print_list, &cfg_rep_list,
1358         &cfg_type_qstring
1359 };
1360
1361 isc_result_t
1362 cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret)
1363 {
1364         isc_result_t result;
1365         cfg_parser_t *pctx;
1366         isc_lexspecials_t specials;
1367
1368         REQUIRE(mctx != NULL);
1369         REQUIRE(ret != NULL && *ret == NULL);
1370
1371         pctx = isc_mem_get(mctx, sizeof(*pctx));
1372         if (pctx == NULL)
1373                 return (ISC_R_NOMEMORY);
1374
1375         pctx->mctx = mctx;
1376         pctx->lctx = lctx;
1377         pctx->lexer = NULL;
1378         pctx->seen_eof = ISC_FALSE;
1379         pctx->ungotten = ISC_FALSE;
1380         pctx->errors = 0;
1381         pctx->warnings = 0;
1382         pctx->open_files = NULL;
1383         pctx->closed_files = NULL;
1384         pctx->line = 0;
1385         pctx->callback = NULL;
1386         pctx->callbackarg = NULL;
1387         pctx->token.type = isc_tokentype_unknown;
1388
1389         memset(specials, 0, sizeof(specials));
1390         specials['{'] = 1;
1391         specials['}'] = 1;
1392         specials[';'] = 1;
1393         specials['/'] = 1;
1394         specials['"'] = 1;
1395         specials['!'] = 1;
1396
1397         CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer));
1398
1399         isc_lex_setspecials(pctx->lexer, specials);
1400         isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C |
1401                                          ISC_LEXCOMMENT_CPLUSPLUS |
1402                                          ISC_LEXCOMMENT_SHELL));
1403
1404         CHECK(create_list(pctx, &cfg_type_filelist, &pctx->open_files));
1405         CHECK(create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
1406
1407         *ret = pctx;
1408         return (ISC_R_SUCCESS);
1409
1410  cleanup:
1411         if (pctx->lexer != NULL)
1412                 isc_lex_destroy(&pctx->lexer);
1413         CLEANUP_OBJ(pctx->open_files);
1414         CLEANUP_OBJ(pctx->closed_files);
1415         isc_mem_put(mctx, pctx, sizeof(*pctx));
1416         return (result);
1417 }
1418
1419 static isc_result_t
1420 parser_openfile(cfg_parser_t *pctx, const char *filename) {
1421         isc_result_t result;
1422         cfg_listelt_t *elt = NULL;
1423         cfg_obj_t *stringobj = NULL;
1424
1425         result = isc_lex_openfile(pctx->lexer, filename);
1426         if (result != ISC_R_SUCCESS) {
1427                 parser_error(pctx, 0, "open: %s: %s",
1428                              filename, isc_result_totext(result));
1429                 goto cleanup;
1430         }
1431
1432         CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
1433         CHECK(create_listelt(pctx, &elt));
1434         elt->obj = stringobj;
1435         ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
1436
1437         return (ISC_R_SUCCESS);
1438  cleanup:
1439         CLEANUP_OBJ(stringobj);
1440         return (result);
1441 }
1442
1443 void
1444 cfg_parser_setcallback(cfg_parser_t *pctx,
1445                        cfg_parsecallback_t callback,
1446                        void *arg)
1447 {
1448         pctx->callback = callback;
1449         pctx->callbackarg = arg;
1450 }
1451
1452 /*
1453  * Parse a configuration using a pctx where a lexer has already
1454  * been set up with a source.
1455  */
1456 static isc_result_t
1457 parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1458         isc_result_t result;
1459         cfg_obj_t *obj = NULL;
1460
1461         result = parse(pctx, type, &obj);
1462
1463         if (pctx->errors != 0) {
1464                 /* Errors have been logged. */
1465                 if (result == ISC_R_SUCCESS)
1466                         result = ISC_R_FAILURE;
1467                 goto cleanup;
1468         }
1469
1470         if (result != ISC_R_SUCCESS) {
1471                 /* Parsing failed but no errors have been logged. */
1472                 parser_error(pctx, 0, "parsing failed");
1473                 goto cleanup;
1474         }
1475
1476         CHECK(parse_eof(pctx));
1477
1478         *ret = obj;
1479         return (ISC_R_SUCCESS);
1480
1481  cleanup:
1482         CLEANUP_OBJ(obj);
1483         return (result);
1484 }
1485
1486 isc_result_t
1487 cfg_parse_file(cfg_parser_t *pctx, const char *filename,
1488                const cfg_type_t *type, cfg_obj_t **ret)
1489 {
1490         isc_result_t result;
1491
1492         REQUIRE(filename != NULL);
1493
1494         CHECK(parser_openfile(pctx, filename));
1495         CHECK(parse2(pctx, type, ret));
1496  cleanup:
1497         return (result);
1498 }
1499
1500
1501 isc_result_t
1502 cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
1503         const cfg_type_t *type, cfg_obj_t **ret)
1504 {
1505         isc_result_t result;
1506         REQUIRE(buffer != NULL);
1507         CHECK(isc_lex_openbuffer(pctx->lexer, buffer)); 
1508         CHECK(parse2(pctx, type, ret));
1509  cleanup:
1510         return (result);
1511 }
1512
1513 void
1514 cfg_parser_destroy(cfg_parser_t **pctxp) {
1515         cfg_parser_t *pctx = *pctxp;
1516         isc_lex_destroy(&pctx->lexer);
1517         /*
1518          * Cleaning up open_files does not
1519          * close the files; that was already done
1520          * by closing the lexer.
1521          */
1522         CLEANUP_OBJ(pctx->open_files);
1523         CLEANUP_OBJ(pctx->closed_files);
1524         isc_mem_put(pctx->mctx, pctx, sizeof(*pctx));
1525         *pctxp = NULL;
1526 }
1527
1528 /*
1529  * void
1530  */
1531 static isc_result_t
1532 parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1533         UNUSED(type);
1534         return (create_cfgobj(pctx, &cfg_type_void, ret));
1535 }
1536
1537 static void
1538 print_void(cfg_printer_t *pctx, cfg_obj_t *obj) {
1539         UNUSED(pctx);
1540         UNUSED(obj);
1541 }
1542
1543 isc_boolean_t
1544 cfg_obj_isvoid(cfg_obj_t *obj) {
1545         REQUIRE(obj != NULL);
1546         return (ISC_TF(obj->type->rep == &cfg_rep_void));
1547 }
1548
1549 static cfg_type_t cfg_type_void = {
1550         "void", parse_void, print_void, &cfg_rep_void, NULL };
1551
1552
1553 /*
1554  * uint32
1555  */
1556 static isc_result_t
1557 parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1558         isc_result_t result;
1559         cfg_obj_t *obj = NULL;
1560         UNUSED(type);
1561
1562         CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
1563         if (pctx->token.type != isc_tokentype_number) {
1564                 parser_error(pctx, LOG_NEAR, "expected number");
1565                 return (ISC_R_UNEXPECTEDTOKEN);
1566         }
1567
1568         CHECK(create_cfgobj(pctx, &cfg_type_uint32, &obj));
1569
1570         obj->value.uint32 = pctx->token.value.as_ulong;
1571         *ret = obj;
1572  cleanup:
1573         return (result);
1574 }
1575
1576 static void
1577 print_cstr(cfg_printer_t *pctx, const char *s) {
1578         print(pctx, s, strlen(s));
1579 }
1580
1581 static void
1582 print_uint(cfg_printer_t *pctx, unsigned int u) {
1583         char buf[32];
1584         snprintf(buf, sizeof(buf), "%u", u);
1585         print_cstr(pctx, buf);
1586 }
1587
1588 static void
1589 print_uint32(cfg_printer_t *pctx, cfg_obj_t *obj) {
1590         print_uint(pctx, obj->value.uint32);
1591 }
1592
1593 isc_boolean_t
1594 cfg_obj_isuint32(cfg_obj_t *obj) {
1595         REQUIRE(obj != NULL);
1596         return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
1597 }
1598
1599 isc_uint32_t
1600 cfg_obj_asuint32(cfg_obj_t *obj) {
1601         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
1602         return (obj->value.uint32);
1603 }
1604
1605 static cfg_type_t cfg_type_uint32 = {
1606         "integer", parse_uint32, print_uint32, &cfg_rep_uint32, NULL };
1607
1608
1609 /*
1610  * uint64
1611  */
1612 isc_boolean_t
1613 cfg_obj_isuint64(cfg_obj_t *obj) {
1614         REQUIRE(obj != NULL);
1615         return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
1616 }
1617
1618 isc_uint64_t
1619 cfg_obj_asuint64(cfg_obj_t *obj) {
1620         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
1621         return (obj->value.uint64);
1622 }
1623
1624 static isc_result_t
1625 parse_unitstring(char *str, isc_resourcevalue_t *valuep) {
1626         char *endp;
1627         unsigned int len;
1628         isc_uint64_t value;
1629         isc_uint64_t unit;
1630
1631         value = isc_string_touint64(str, &endp, 10);
1632         if (*endp == 0) {
1633                 *valuep = value;
1634                 return (ISC_R_SUCCESS);
1635         }
1636
1637         len = strlen(str);
1638         if (len < 2 || endp[1] != '\0')
1639                 return (ISC_R_FAILURE);
1640
1641         switch (str[len - 1]) {
1642         case 'k':
1643         case 'K':
1644                 unit = 1024;
1645                 break;
1646         case 'm':
1647         case 'M':
1648                 unit = 1024 * 1024;
1649                 break;
1650         case 'g':
1651         case 'G':
1652                 unit = 1024 * 1024 * 1024;
1653                 break;
1654         default:
1655                 return (ISC_R_FAILURE);
1656         }
1657         if (value > ISC_UINT64_MAX / unit)
1658                 return (ISC_R_FAILURE);
1659         *valuep = value * unit;
1660         return (ISC_R_SUCCESS);
1661 }
1662
1663 static void
1664 print_uint64(cfg_printer_t *pctx, cfg_obj_t *obj) {
1665         char buf[32];
1666         sprintf(buf, "%" ISC_PRINT_QUADFORMAT "u", obj->value.uint64);
1667         print_cstr(pctx, buf);
1668 }
1669
1670 static cfg_type_t cfg_type_uint64 = {
1671         "64_bit_integer", NULL, print_uint64, &cfg_rep_uint64, NULL };
1672
1673 static isc_result_t
1674 parse_sizeval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1675         isc_result_t result;
1676         cfg_obj_t *obj = NULL;
1677         isc_uint64_t val;
1678
1679         UNUSED(type);
1680
1681         CHECK(cfg_gettoken(pctx, 0));
1682         if (pctx->token.type != isc_tokentype_string) {
1683                 result = ISC_R_UNEXPECTEDTOKEN;
1684                 goto cleanup;
1685         }
1686         CHECK(parse_unitstring(pctx->token.value.as_pointer, &val));
1687
1688         CHECK(create_cfgobj(pctx, &cfg_type_uint64, &obj));
1689         obj->value.uint64 = val;
1690         *ret = obj;
1691         return (ISC_R_SUCCESS);
1692
1693  cleanup:
1694         parser_error(pctx, LOG_NEAR, "expected integer and optional unit");
1695         return (result);
1696 }
1697
1698 /*
1699  * A size value (number + optional unit).
1700  */
1701 static cfg_type_t cfg_type_sizeval = {
1702         "sizeval", parse_sizeval, print_uint64, &cfg_rep_uint64, NULL };
1703
1704 /*
1705  * A size, "unlimited", or "default".
1706  */
1707
1708 static isc_result_t
1709 parse_size(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1710         return (parse_enum_or_other(pctx, type, &cfg_type_sizeval, ret));
1711 }
1712
1713 static const char *size_enums[] = { "unlimited", "default", NULL };
1714 static cfg_type_t cfg_type_size = {
1715         "size", parse_size, print_ustring, &cfg_rep_string, size_enums
1716 };
1717
1718 /*
1719  * A size or "unlimited", but not "default".
1720  */
1721 static const char *sizenodefault_enums[] = { "unlimited", NULL };
1722 static cfg_type_t cfg_type_sizenodefault = {
1723         "size_no_default", parse_size, print_ustring, &cfg_rep_string,
1724         sizenodefault_enums
1725 };
1726
1727 /*
1728  * optional_keyvalue
1729  */
1730 static isc_result_t
1731 parse_maybe_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
1732                               isc_boolean_t optional, cfg_obj_t **ret)
1733 {
1734         isc_result_t result;
1735         cfg_obj_t *obj = NULL;
1736         const keyword_type_t *kw = type->of;
1737
1738         CHECK(cfg_peektoken(pctx, 0));
1739         if (pctx->token.type == isc_tokentype_string &&
1740             strcasecmp(pctx->token.value.as_pointer, kw->name) == 0) {
1741                 CHECK(cfg_gettoken(pctx, 0));
1742                 CHECK(kw->type->parse(pctx, kw->type, &obj));
1743                 obj->type = type; /* XXX kludge */
1744         } else {
1745                 if (optional) {
1746                         CHECK(parse_void(pctx, NULL, &obj));
1747                 } else {
1748                         parser_error(pctx, LOG_NEAR, "expected '%s'",
1749                                      kw->name);
1750                         result = ISC_R_UNEXPECTEDTOKEN;
1751                         goto cleanup;
1752                 }
1753         }
1754         *ret = obj;
1755  cleanup:
1756         return (result);
1757 }
1758
1759 static isc_result_t
1760 parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1761         return (parse_maybe_optional_keyvalue(pctx, type, ISC_FALSE, ret));
1762 }
1763
1764 static isc_result_t
1765 parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1766         return (parse_maybe_optional_keyvalue(pctx, type, ISC_TRUE, ret));
1767 }
1768
1769 static void
1770 print_keyvalue(cfg_printer_t *pctx, cfg_obj_t *obj) {
1771         const keyword_type_t *kw = obj->type->of;
1772         print_cstr(pctx, kw->name);
1773         print(pctx, " ", 1);
1774         kw->type->print(pctx, obj);
1775 }
1776
1777 /*
1778  * qstring, ustring, astring
1779  */
1780
1781 /* Create a string object from a null-terminated C string. */
1782 static isc_result_t
1783 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
1784               cfg_obj_t **ret)
1785 {
1786         isc_result_t result;
1787         cfg_obj_t *obj = NULL;
1788         int len;
1789
1790         CHECK(create_cfgobj(pctx, type, &obj));
1791         len = strlen(contents);
1792         obj->value.string.length = len;
1793         obj->value.string.base = isc_mem_get(pctx->mctx, len + 1);
1794         if (obj->value.string.base == 0) {
1795                 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
1796                 return (ISC_R_NOMEMORY);
1797         }
1798         memcpy(obj->value.string.base, contents, len);
1799         obj->value.string.base[len] = '\0';
1800
1801         *ret = obj;
1802  cleanup:
1803         return (result);
1804 }
1805
1806 static isc_result_t
1807 parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1808         isc_result_t result;
1809         UNUSED(type);
1810
1811         CHECK(cfg_gettoken(pctx, QSTRING));
1812         if (pctx->token.type != isc_tokentype_qstring) {
1813                 parser_error(pctx, LOG_NEAR, "expected quoted string");
1814                 return (ISC_R_UNEXPECTEDTOKEN);
1815         }
1816         return (create_string(pctx,
1817                               pctx->token.value.as_pointer,
1818                               &cfg_type_qstring,
1819                               ret));
1820  cleanup:
1821         return (result);
1822 }
1823
1824 static isc_result_t
1825 parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1826         isc_result_t result;
1827         UNUSED(type);
1828
1829         CHECK(cfg_gettoken(pctx, 0));
1830         if (pctx->token.type != isc_tokentype_string) {
1831                 parser_error(pctx, LOG_NEAR, "expected unquoted string");
1832                 return (ISC_R_UNEXPECTEDTOKEN);
1833         }
1834         return (create_string(pctx,
1835                               pctx->token.value.as_pointer,
1836                               &cfg_type_ustring,
1837                               ret));
1838  cleanup:
1839         return (result);
1840 }
1841
1842 static isc_result_t
1843 parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1844         isc_result_t result;
1845         UNUSED(type);
1846
1847         CHECK(cfg_getstringtoken(pctx));
1848         return (create_string(pctx,
1849                               pctx->token.value.as_pointer,
1850                               &cfg_type_qstring,
1851                               ret));
1852  cleanup:
1853         return (result);
1854 }
1855
1856 static isc_boolean_t
1857 is_enum(const char *s, const char *const *enums) {
1858         const char * const *p;
1859         for (p = enums; *p != NULL; p++) {
1860                 if (strcasecmp(*p, s) == 0)
1861                         return (ISC_TRUE);
1862         }
1863         return (ISC_FALSE);
1864 }
1865
1866 static isc_result_t
1867 check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
1868         const char *s = obj->value.string.base;
1869         if (is_enum(s, enums))
1870                 return (ISC_R_SUCCESS);
1871         parser_error(pctx, 0, "'%s' unexpected", s);
1872         return (ISC_R_UNEXPECTEDTOKEN);
1873 }
1874
1875 static isc_result_t
1876 parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1877         isc_result_t result;
1878         cfg_obj_t *obj = NULL;
1879         CHECK(parse_ustring(pctx, NULL, &obj));
1880         CHECK(check_enum(pctx, obj, type->of));
1881         *ret = obj;
1882         return (ISC_R_SUCCESS);
1883  cleanup:
1884         CLEANUP_OBJ(obj);       
1885         return (result);
1886 }
1887
1888 static isc_result_t
1889 parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype,
1890                     const cfg_type_t *othertype, cfg_obj_t **ret)
1891 {
1892         isc_result_t result;
1893         CHECK(cfg_peektoken(pctx, 0));
1894         if (pctx->token.type == isc_tokentype_string &&
1895             is_enum(pctx->token.value.as_pointer, enumtype->of)) {
1896                 CHECK(parse_enum(pctx, enumtype, ret));
1897         } else {
1898                 CHECK(parse(pctx, othertype, ret));
1899         }
1900  cleanup:
1901         return (result);
1902 }
1903
1904
1905 /*
1906  * Print a string object.
1907  */
1908 static void
1909 print_ustring(cfg_printer_t *pctx, cfg_obj_t *obj) {
1910         print(pctx, obj->value.string.base, obj->value.string.length);
1911 }
1912
1913 static void
1914 print_qstring(cfg_printer_t *pctx, cfg_obj_t *obj) {
1915         print(pctx, "\"", 1);
1916         print_ustring(pctx, obj);
1917         print(pctx, "\"", 1);
1918 }
1919
1920 static void
1921 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
1922         isc_mem_put(pctx->mctx, obj->value.string.base,
1923                     obj->value.string.length + 1);
1924 }
1925
1926 isc_boolean_t
1927 cfg_obj_isstring(cfg_obj_t *obj) {
1928         REQUIRE(obj != NULL);
1929         return (ISC_TF(obj->type->rep == &cfg_rep_string));
1930 }
1931
1932 char *
1933 cfg_obj_asstring(cfg_obj_t *obj) {
1934         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
1935         return (obj->value.string.base);
1936 }
1937
1938 isc_boolean_t
1939 cfg_obj_isboolean(cfg_obj_t *obj) {
1940         REQUIRE(obj != NULL);
1941         return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
1942 }
1943
1944 isc_boolean_t
1945 cfg_obj_asboolean(cfg_obj_t *obj) {
1946         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
1947         return (obj->value.boolean);
1948 }
1949
1950 /* Quoted string only */
1951 static cfg_type_t cfg_type_qstring = {
1952         "quoted_string", parse_qstring, print_qstring, &cfg_rep_string, NULL };
1953
1954 /* Unquoted string only */
1955 static cfg_type_t cfg_type_ustring = {
1956         "string", parse_ustring, print_ustring, &cfg_rep_string, NULL };
1957
1958 /* Any string (quoted or unquoted); printed with quotes */
1959 static cfg_type_t cfg_type_astring = {
1960         "string", parse_astring, print_qstring, &cfg_rep_string, NULL };
1961
1962
1963 /*
1964  * boolean
1965  */
1966 static isc_result_t
1967 parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1968 {
1969         isc_result_t result;
1970         isc_boolean_t value;
1971         cfg_obj_t *obj = NULL;
1972         UNUSED(type);
1973
1974         result = cfg_gettoken(pctx, 0);
1975         if (result != ISC_R_SUCCESS)
1976                 return (result);
1977
1978         if (pctx->token.type != isc_tokentype_string)
1979                 goto bad_boolean;
1980
1981         if ((strcasecmp(pctx->token.value.as_pointer, "true") == 0) ||
1982             (strcasecmp(pctx->token.value.as_pointer, "yes") == 0) ||
1983             (strcmp(pctx->token.value.as_pointer, "1") == 0)) {
1984                 value = ISC_TRUE;
1985         } else if ((strcasecmp(pctx->token.value.as_pointer, "false") == 0) ||
1986                    (strcasecmp(pctx->token.value.as_pointer, "no") == 0) ||
1987                    (strcmp(pctx->token.value.as_pointer, "0") == 0)) {
1988                 value = ISC_FALSE;
1989         } else {
1990                 goto bad_boolean;
1991         }
1992
1993         CHECK(create_cfgobj(pctx, &cfg_type_boolean, &obj));
1994         obj->value.boolean = value;
1995         *ret = obj;
1996         return (result);
1997
1998  bad_boolean:
1999         parser_error(pctx, LOG_NEAR, "boolean expected");
2000         return (ISC_R_UNEXPECTEDTOKEN);
2001
2002  cleanup:
2003         return (result);
2004 }
2005
2006 static void
2007 print_boolean(cfg_printer_t *pctx, cfg_obj_t *obj) {
2008         if (obj->value.boolean)
2009                 print(pctx, "yes", 3);
2010         else
2011                 print(pctx, "no", 2);
2012 }
2013
2014 static cfg_type_t cfg_type_boolean = {
2015         "boolean", parse_boolean, print_boolean, &cfg_rep_boolean, NULL };
2016
2017 static const char *dialup_enums[] = {
2018         "notify", "notify-passive", "refresh", "passive", NULL };
2019 static isc_result_t
2020 parse_dialup_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2021         return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2022 }
2023 static cfg_type_t cfg_type_dialuptype = {
2024         "dialuptype", parse_dialup_type, print_ustring, 
2025         &cfg_rep_string, dialup_enums
2026 };
2027
2028 static const char *notify_enums[] = { "explicit", NULL };
2029 static isc_result_t
2030 parse_notify_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2031         return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2032 }
2033 static cfg_type_t cfg_type_notifytype = {
2034         "notifytype", parse_notify_type, print_ustring, 
2035         &cfg_rep_string, notify_enums,
2036 };
2037
2038 static keyword_type_t key_kw = { "key", &cfg_type_astring };
2039
2040 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_keyref = {
2041         "keyref", parse_keyvalue, print_keyvalue,
2042         &cfg_rep_string, &key_kw
2043 };
2044
2045 static cfg_type_t cfg_type_optional_keyref = {
2046         "optional_keyref", parse_optional_keyvalue, print_keyvalue,
2047         &cfg_rep_string, &key_kw
2048 };
2049
2050
2051 /*
2052  * Lists.
2053  */
2054
2055 static isc_result_t
2056 create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
2057         isc_result_t result;
2058         CHECK(create_cfgobj(pctx, type, obj));
2059         ISC_LIST_INIT((*obj)->value.list);
2060  cleanup:
2061         return (result);
2062 }
2063
2064 static isc_result_t
2065 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
2066         cfg_listelt_t *elt;
2067         elt = isc_mem_get(pctx->mctx, sizeof(*elt));
2068         if (elt == NULL)
2069                 return (ISC_R_NOMEMORY);
2070         elt->obj = NULL;
2071         ISC_LINK_INIT(elt, link);
2072         *eltp = elt;
2073         return (ISC_R_SUCCESS);
2074 }
2075
2076 static void
2077 free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
2078         cfg_obj_destroy(pctx, &elt->obj);
2079         isc_mem_put(pctx->mctx, elt, sizeof(*elt));
2080 }
2081
2082 static void
2083 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
2084         cfg_listelt_t *elt, *next;
2085         for (elt = ISC_LIST_HEAD(obj->value.list);
2086              elt != NULL;
2087              elt = next)
2088         {
2089                 next = ISC_LIST_NEXT(elt, link);
2090                 free_list_elt(pctx, elt);
2091         }
2092 }
2093
2094 static isc_result_t
2095 parse_list_elt(cfg_parser_t *pctx, const cfg_type_t *elttype,
2096                cfg_listelt_t **ret)
2097 {
2098         isc_result_t result;
2099         cfg_listelt_t *elt = NULL;
2100         cfg_obj_t *value = NULL;
2101
2102         CHECK(create_listelt(pctx, &elt));
2103
2104         result = parse(pctx, elttype, &value);
2105         if (result != ISC_R_SUCCESS)
2106                 goto cleanup;
2107
2108         elt->obj = value;
2109
2110         *ret = elt;
2111         return (ISC_R_SUCCESS);
2112
2113  cleanup:
2114         isc_mem_put(pctx->mctx, elt, sizeof(*elt));
2115         return (result);
2116 }
2117
2118 /*
2119  * Parse a homogeneous list whose elements are of type 'elttype'
2120  * and where each element is terminated by a semicolon.
2121  */
2122 static isc_result_t
2123 parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
2124 {
2125         cfg_obj_t *listobj = NULL;
2126         const cfg_type_t *listof = listtype->of;
2127         isc_result_t result;
2128         cfg_listelt_t *elt = NULL;
2129
2130         CHECK(create_list(pctx, listtype, &listobj));
2131
2132         for (;;) {
2133                 CHECK(cfg_peektoken(pctx, 0));
2134                 if (pctx->token.type == isc_tokentype_special &&
2135                     pctx->token.value.as_char == /*{*/ '}')
2136                         break;
2137                 CHECK(parse_list_elt(pctx, listof, &elt));
2138                 CHECK(parse_semicolon(pctx));
2139                 ISC_LIST_APPEND(listobj->value.list, elt, link);
2140                 elt = NULL;
2141         }
2142         *ret = listobj;
2143         return (ISC_R_SUCCESS);
2144
2145  cleanup:
2146         if (elt != NULL)
2147                 free_list_elt(pctx, elt);
2148         CLEANUP_OBJ(listobj);
2149         return (result);
2150 }
2151
2152 static void
2153 print_list(cfg_printer_t *pctx, cfg_obj_t *obj) {
2154         cfg_list_t *list = &obj->value.list;
2155         cfg_listelt_t *elt;
2156
2157         for (elt = ISC_LIST_HEAD(*list);
2158              elt != NULL;
2159              elt = ISC_LIST_NEXT(elt, link)) {
2160                 print_indent(pctx);
2161                 print_obj(pctx, elt->obj);
2162                 print(pctx, ";\n", 2);
2163         }
2164 }
2165
2166 static isc_result_t
2167 parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
2168 {
2169         isc_result_t result;
2170         CHECK(parse_special(pctx, '{'));
2171         CHECK(parse_list(pctx, type, ret));
2172         CHECK(parse_special(pctx, '}'));
2173  cleanup:
2174         return (result);
2175 }
2176
2177 static void
2178 print_bracketed_list(cfg_printer_t *pctx, cfg_obj_t *obj) {
2179         print_open(pctx);
2180         print_list(pctx, obj);
2181         print_close(pctx);
2182 }
2183
2184 /*
2185  * Parse a homogeneous list whose elements are of type 'elttype'
2186  * and where elements are separated by space.  The list ends
2187  * before the first semicolon.
2188  */
2189 static isc_result_t
2190 parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
2191 {
2192         cfg_obj_t *listobj = NULL;
2193         const cfg_type_t *listof = listtype->of;
2194         isc_result_t result;
2195
2196         CHECK(create_list(pctx, listtype, &listobj));
2197
2198         for (;;) {
2199                 cfg_listelt_t *elt = NULL;
2200
2201                 CHECK(cfg_peektoken(pctx, 0));
2202                 if (pctx->token.type == isc_tokentype_special &&
2203                     pctx->token.value.as_char == ';')
2204                         break;
2205                 CHECK(parse_list_elt(pctx, listof, &elt));
2206                 ISC_LIST_APPEND(listobj->value.list, elt, link);
2207         }
2208         *ret = listobj;
2209         return (ISC_R_SUCCESS);
2210
2211  cleanup:
2212         CLEANUP_OBJ(listobj);
2213         return (result);
2214 }
2215
2216 static void
2217 print_spacelist(cfg_printer_t *pctx, cfg_obj_t *obj) {
2218         cfg_list_t *list = &obj->value.list;
2219         cfg_listelt_t *elt;
2220
2221         for (elt = ISC_LIST_HEAD(*list);
2222              elt != NULL;
2223              elt = ISC_LIST_NEXT(elt, link)) {
2224                 print_obj(pctx, elt->obj);
2225                 if (ISC_LIST_NEXT(elt, link) != NULL)
2226                         print(pctx, " ", 1);
2227         }
2228 }
2229
2230 isc_boolean_t
2231 cfg_obj_islist(cfg_obj_t *obj) {
2232         REQUIRE(obj != NULL);
2233         return (ISC_TF(obj->type->rep == &cfg_rep_list));
2234 }
2235
2236 cfg_listelt_t *
2237 cfg_list_first(cfg_obj_t *obj) {
2238         REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
2239         if (obj == NULL)
2240                 return (NULL);
2241         return (ISC_LIST_HEAD(obj->value.list));
2242 }
2243
2244 cfg_listelt_t *
2245 cfg_list_next(cfg_listelt_t *elt) {
2246         REQUIRE(elt != NULL);
2247         return (ISC_LIST_NEXT(elt, link));
2248 }
2249
2250 cfg_obj_t *
2251 cfg_listelt_value(cfg_listelt_t *elt) {
2252         REQUIRE(elt != NULL);
2253         return (elt->obj);
2254 }
2255
2256 /*
2257  * Maps.
2258  */
2259
2260 /*
2261  * Parse a map body.  That's something like
2262  *
2263  *   "foo 1; bar { glub; }; zap true; zap false;"
2264  *
2265  * i.e., a sequence of option names followed by values and
2266  * terminated by semicolons.  Used for the top level of
2267  * the named.conf syntax, as well as for the body of the
2268  * options, view, zone, and other statements.
2269  */
2270 static isc_result_t
2271 parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
2272 {
2273         const cfg_clausedef_t * const *clausesets = type->of;
2274         isc_result_t result;
2275         const cfg_clausedef_t * const *clauseset;
2276         const cfg_clausedef_t *clause;
2277         cfg_obj_t *value = NULL;
2278         cfg_obj_t *obj = NULL;
2279         cfg_obj_t *eltobj = NULL;
2280         cfg_obj_t *includename = NULL;
2281         isc_symvalue_t symval;
2282         cfg_list_t *list = NULL;
2283
2284         CHECK(create_map(pctx, type, &obj));
2285
2286         obj->value.map.clausesets = clausesets;
2287
2288         for (;;) {
2289                 cfg_listelt_t *elt;
2290
2291         redo:
2292                 /*
2293                  * Parse the option name and see if it is known.
2294                  */
2295                 CHECK(cfg_gettoken(pctx, 0));
2296
2297                 if (pctx->token.type != isc_tokentype_string) {
2298                         cfg_ungettoken(pctx);
2299                         break;
2300                 }
2301
2302                 /*
2303                  * We accept "include" statements wherever a map body
2304                  * clause can occur.
2305                  */
2306                 if (strcasecmp(pctx->token.value.as_pointer, "include") == 0) {
2307                         /*
2308                          * Turn the file name into a temporary configuration
2309                          * object just so that it is not overwritten by the
2310                          * semicolon token.
2311                          */
2312                         CHECK(parse(pctx, &cfg_type_qstring, &includename));
2313                         CHECK(parse_semicolon(pctx));
2314                         CHECK(parser_openfile(pctx, includename->
2315                                               value.string.base));
2316                          cfg_obj_destroy(pctx, &includename);
2317                          goto redo;
2318                 }
2319
2320                 clause = NULL;
2321                 for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
2322                         for (clause = *clauseset;
2323                              clause->name != NULL;
2324                              clause++) {
2325                                 if (strcasecmp(pctx->token.value.as_pointer,
2326                                            clause->name) == 0)
2327                                         goto done;
2328                         }
2329                 }
2330         done:
2331                 if (clause == NULL || clause->name == NULL) {
2332                         parser_error(pctx, LOG_NOPREP, "unknown option");
2333                         /*
2334                          * Try to recover by parsing this option as an unknown
2335                          * option and discarding it.
2336                          */
2337                          CHECK(parse(pctx, &cfg_type_unsupported, &eltobj));
2338                          cfg_obj_destroy(pctx, &eltobj);
2339                          CHECK(parse_semicolon(pctx));
2340                          continue;
2341                 }
2342
2343                 /* Clause is known. */
2344
2345                 /* Issue warnings if appropriate */
2346                 if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0)
2347                         parser_warning(pctx, 0, "option '%s' is obsolete",
2348                                        clause->name);
2349                 if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0)
2350                         parser_warning(pctx, 0, "option '%s' is "
2351                                        "not implemented", clause->name);
2352                 if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0)
2353                         parser_warning(pctx, 0, "option '%s' is "
2354                                        "not implemented", clause->name);
2355                 /*
2356                  * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
2357                  * set here - we need to log the *lack* of such an option,
2358                  * not its presence.
2359                  */
2360
2361                 /* See if the clause already has a value; if not create one. */
2362                 result = isc_symtab_lookup(obj->value.map.symtab,
2363                                            clause->name, 0, &symval);
2364
2365                 if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
2366                         /* Multivalued clause */
2367                         cfg_obj_t *listobj = NULL;
2368                         if (result == ISC_R_NOTFOUND) {
2369                                 CHECK(create_list(pctx,
2370                                                   &cfg_type_implicitlist,
2371                                                   &listobj));
2372                                 symval.as_pointer = listobj;
2373                                 result = isc_symtab_define(obj->value.
2374                                                    map.symtab,
2375                                                    clause->name,
2376                                                    1, symval,
2377                                                    isc_symexists_reject);
2378                                 if (result != ISC_R_SUCCESS) {
2379                                         parser_error(pctx, LOG_NEAR,
2380                                                      "isc_symtab_define(%s) "
2381                                                      "failed", clause->name);
2382                                         isc_mem_put(pctx->mctx, list,
2383                                                     sizeof(cfg_list_t));
2384                                         goto cleanup;
2385                                 }
2386                         } else {
2387                                 INSIST(result == ISC_R_SUCCESS);
2388                                 listobj = symval.as_pointer;
2389                         }
2390
2391                         elt = NULL;
2392                         CHECK(parse_list_elt(pctx, clause->type, &elt));
2393                         CHECK(parse_semicolon(pctx));
2394
2395                         ISC_LIST_APPEND(listobj->value.list, elt, link);
2396                 } else {
2397                         /* Single-valued clause */
2398                         if (result == ISC_R_NOTFOUND) {
2399                                 isc_boolean_t callback =
2400                                         ISC_TF((clause->flags &
2401                                                 CFG_CLAUSEFLAG_CALLBACK) != 0);
2402                                 CHECK(parse_symtab_elt(pctx, clause->name,
2403                                                        clause->type,
2404                                                        obj->value.map.symtab,
2405                                                        callback));
2406                                 CHECK(parse_semicolon(pctx));
2407                         } else if (result == ISC_R_SUCCESS) {
2408                                 parser_error(pctx, LOG_NEAR, "'%s' redefined",
2409                                              clause->name);
2410                                 result = ISC_R_EXISTS;
2411                                 goto cleanup;
2412                         } else {
2413                                 parser_error(pctx, LOG_NEAR,
2414                                              "isc_symtab_define() failed");
2415                                 goto cleanup;
2416                         }
2417                 }
2418         }
2419
2420
2421         *ret = obj;
2422         return (ISC_R_SUCCESS);
2423
2424  cleanup:
2425         CLEANUP_OBJ(value);
2426         CLEANUP_OBJ(obj);
2427         CLEANUP_OBJ(eltobj);
2428         CLEANUP_OBJ(includename);
2429         return (result);
2430 }
2431
2432 static isc_result_t
2433 parse_symtab_elt(cfg_parser_t *pctx, const char *name,
2434                  cfg_type_t *elttype, isc_symtab_t *symtab,
2435                  isc_boolean_t callback)
2436 {
2437         isc_result_t result;
2438         cfg_obj_t *obj = NULL;
2439         isc_symvalue_t symval;
2440
2441         CHECK(parse(pctx, elttype, &obj));
2442
2443         if (callback && pctx->callback != NULL)
2444                 CHECK(pctx->callback(name, obj, pctx->callbackarg));
2445         
2446         symval.as_pointer = obj;
2447         CHECK(isc_symtab_define(symtab, name,
2448                                 1, symval,
2449                                 isc_symexists_reject));
2450         return (ISC_R_SUCCESS);
2451
2452  cleanup:
2453         CLEANUP_OBJ(obj);
2454         return (result);
2455 }
2456
2457 /*
2458  * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
2459  */
2460 static isc_result_t
2461 parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
2462 {
2463         isc_result_t result;
2464         CHECK(parse_special(pctx, '{'));
2465         CHECK(parse_mapbody(pctx, type, ret));
2466         CHECK(parse_special(pctx, '}'));
2467  cleanup:
2468         return (result);
2469 }
2470
2471 /*
2472  * Subroutine for parse_named_map() and parse_addressed_map().
2473  */
2474 static isc_result_t
2475 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type,
2476                     cfg_obj_t **ret)
2477 {
2478         isc_result_t result;
2479         cfg_obj_t *idobj = NULL;
2480         cfg_obj_t *mapobj = NULL;
2481
2482         CHECK(parse(pctx, nametype, &idobj));
2483         CHECK(parse_map(pctx, type, &mapobj));
2484         mapobj->value.map.id = idobj;
2485         idobj = NULL;
2486         *ret = mapobj;
2487  cleanup:
2488         CLEANUP_OBJ(idobj);
2489         return (result);
2490 }
2491
2492 /*
2493  * Parse a map identified by a string name.  E.g., "name { foo 1; }".  
2494  * Used for the "key" and "channel" statements.
2495  */
2496 static isc_result_t
2497 parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2498         return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
2499 }
2500
2501 /*
2502  * Parse a map identified by a network address.
2503  * Used for the "server" statement.
2504  */
2505 static isc_result_t
2506 parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2507         return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
2508 }
2509
2510 static void
2511 print_mapbody(cfg_printer_t *pctx, cfg_obj_t *obj) {
2512         isc_result_t result = ISC_R_SUCCESS;
2513
2514         const cfg_clausedef_t * const *clauseset;
2515
2516         for (clauseset = obj->value.map.clausesets;
2517              *clauseset != NULL;
2518              clauseset++)
2519         {
2520                 isc_symvalue_t symval;
2521                 const cfg_clausedef_t *clause;
2522
2523                 for (clause = *clauseset;
2524                      clause->name != NULL;
2525                      clause++) {
2526                         result = isc_symtab_lookup(obj->value.map.symtab,
2527                                                    clause->name, 0, &symval);
2528                         if (result == ISC_R_SUCCESS) {
2529                                 cfg_obj_t *obj = symval.as_pointer;
2530                                 if (obj->type == &cfg_type_implicitlist) {
2531                                         /* Multivalued. */
2532                                         cfg_list_t *list = &obj->value.list;
2533                                         cfg_listelt_t *elt;
2534                                         for (elt = ISC_LIST_HEAD(*list);
2535                                              elt != NULL;
2536                                              elt = ISC_LIST_NEXT(elt, link)) {
2537                                                 print_indent(pctx);
2538                                                 print_cstr(pctx, clause->name);
2539                                                 print(pctx, " ", 1);
2540                                                 print_obj(pctx, elt->obj);
2541                                                 print(pctx, ";\n", 2);
2542                                         }
2543                                 } else {
2544                                         /* Single-valued. */
2545                                         print_indent(pctx);
2546                                         print_cstr(pctx, clause->name);
2547                                         print(pctx, " ", 1);
2548                                         print_obj(pctx, obj);
2549                                         print(pctx, ";\n", 2);
2550                                 }
2551                         } else if (result == ISC_R_NOTFOUND) {
2552                                 ; /* do nothing */
2553                         } else {
2554                                 INSIST(0);
2555                         }
2556                 }
2557         }
2558 }
2559
2560 static void
2561 print_map(cfg_printer_t *pctx, cfg_obj_t *obj) {
2562         if (obj->value.map.id != NULL) {
2563                 print_obj(pctx, obj->value.map.id);
2564                 print(pctx, " ", 1);
2565         }
2566         print_open(pctx);
2567         print_mapbody(pctx, obj);
2568         print_close(pctx);
2569 }
2570
2571 isc_boolean_t
2572 cfg_obj_ismap(cfg_obj_t *obj) {
2573         REQUIRE(obj != NULL);
2574         return (ISC_TF(obj->type->rep == &cfg_rep_map));
2575 }
2576
2577 isc_result_t
2578 cfg_map_get(cfg_obj_t *mapobj, const char* name, cfg_obj_t **obj) {
2579         isc_result_t result;
2580         isc_symvalue_t val;
2581         cfg_map_t *map;
2582         
2583         REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
2584         REQUIRE(name != NULL);
2585         REQUIRE(obj != NULL && *obj == NULL);
2586
2587         map = &mapobj->value.map;
2588         
2589         result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
2590         if (result != ISC_R_SUCCESS)
2591                 return (result);
2592         *obj = val.as_pointer;
2593         return (ISC_R_SUCCESS);
2594 }
2595
2596 cfg_obj_t *
2597 cfg_map_getname(cfg_obj_t *mapobj) {
2598         REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
2599         return (mapobj->value.map.id);
2600 }
2601
2602
2603 /* Parse an arbitrary token, storing its raw text representation. */
2604 static isc_result_t
2605 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2606         cfg_obj_t *obj = NULL;
2607         isc_result_t result;
2608         isc_region_t r;
2609
2610         UNUSED(type);
2611
2612         CHECK(create_cfgobj(pctx, &cfg_type_token, &obj));
2613         CHECK(cfg_gettoken(pctx, QSTRING));
2614         if (pctx->token.type == isc_tokentype_eof) {
2615                 cfg_ungettoken(pctx);
2616                 result = ISC_R_EOF;
2617                 goto cleanup;
2618         }
2619
2620         isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
2621
2622         obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
2623         obj->value.string.length = r.length;
2624         memcpy(obj->value.string.base, r.base, r.length);
2625         obj->value.string.base[r.length] = '\0';
2626         *ret = obj;
2627
2628  cleanup:
2629         return (result);
2630 }
2631
2632 static cfg_type_t cfg_type_token = {
2633         "token", parse_token, print_ustring, &cfg_rep_string, NULL };
2634
2635 /*
2636  * An unsupported option.  This is just a list of tokens with balanced braces
2637  * ending in a semicolon.
2638  */
2639
2640 static isc_result_t
2641 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2642         cfg_obj_t *listobj = NULL;
2643         isc_result_t result;
2644         int braces = 0;
2645
2646         CHECK(create_list(pctx, type, &listobj));
2647
2648         for (;;) {
2649                 cfg_listelt_t *elt = NULL;
2650
2651                 CHECK(cfg_peektoken(pctx, 0));
2652                 if (pctx->token.type == isc_tokentype_special) {
2653                         if (pctx->token.value.as_char == '{')
2654                                 braces++;
2655                         else if (pctx->token.value.as_char == '}')
2656                                 braces--;
2657                         else if (pctx->token.value.as_char == ';')
2658                                 if (braces == 0)
2659                                         break;
2660                 }
2661                 if (pctx->token.type == isc_tokentype_eof || braces < 0) {
2662                         parser_error(pctx, LOG_NEAR, "unexpected token");
2663                         result = ISC_R_UNEXPECTEDTOKEN;
2664                         goto cleanup;
2665                 }
2666
2667                 CHECK(parse_list_elt(pctx, &cfg_type_token, &elt));
2668                 ISC_LIST_APPEND(listobj->value.list, elt, link);
2669         }
2670         INSIST(braces == 0);
2671         *ret = listobj;
2672         return (ISC_R_SUCCESS);
2673
2674  cleanup:
2675         CLEANUP_OBJ(listobj);
2676         return (result);
2677 }
2678
2679 static cfg_type_t cfg_type_unsupported = {
2680         "unsupported", parse_unsupported, print_spacelist,
2681         &cfg_rep_list, NULL
2682 };
2683
2684 /*
2685  * A "controls" statement is represented as a map with the multivalued
2686  * "inet" and "unix" clauses.  Inet controls are tuples; unix controls
2687  * are cfg_unsupported_t objects.
2688  */
2689
2690 static keyword_type_t controls_allow_kw = {
2691         "allow", &cfg_type_bracketed_aml };
2692 static cfg_type_t cfg_type_controls_allow = {
2693         "controls_allow", parse_keyvalue,
2694         print_keyvalue, &cfg_rep_list, &controls_allow_kw
2695 };
2696
2697 static keyword_type_t controls_keys_kw = {
2698         "keys", &cfg_type_keylist };
2699 static cfg_type_t cfg_type_controls_keys = {
2700         "controls_keys", parse_optional_keyvalue,
2701         print_keyvalue, &cfg_rep_list, &controls_keys_kw
2702 };
2703
2704 static cfg_tuplefielddef_t inetcontrol_fields[] = {
2705         { "address", &cfg_type_controls_sockaddr, 0 },
2706         { "allow", &cfg_type_controls_allow, 0 },
2707         { "keys", &cfg_type_controls_keys, 0 },
2708         { NULL, NULL, 0 }
2709 };
2710 static cfg_type_t cfg_type_inetcontrol = {
2711         "inetcontrol", parse_tuple, print_tuple, &cfg_rep_tuple,
2712         inetcontrol_fields
2713 };
2714
2715 static cfg_clausedef_t
2716 controls_clauses[] = {
2717         { "inet", &cfg_type_inetcontrol, CFG_CLAUSEFLAG_MULTI },
2718         { "unix", &cfg_type_unsupported,
2719           CFG_CLAUSEFLAG_MULTI|CFG_CLAUSEFLAG_NOTIMP },
2720         { NULL, NULL, 0 }
2721 };
2722 static cfg_clausedef_t *
2723 controls_clausesets[] = {
2724         controls_clauses,
2725         NULL
2726 };
2727 static cfg_type_t cfg_type_controls = {
2728         "controls", parse_map, print_map, &cfg_rep_map, &controls_clausesets
2729 };
2730
2731 /*
2732  * An optional class, as used in view and zone statements.
2733  */
2734 static isc_result_t
2735 parse_optional_class(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2736         isc_result_t result;
2737         UNUSED(type);
2738         CHECK(cfg_peektoken(pctx, 0));
2739         if (pctx->token.type == isc_tokentype_string)
2740                 CHECK(parse(pctx, &cfg_type_ustring, ret));
2741         else
2742                 CHECK(parse(pctx, &cfg_type_void, ret));
2743  cleanup:
2744         return (result);
2745 }
2746
2747 static cfg_type_t cfg_type_optional_class = {
2748         "optional_class", parse_optional_class, NULL, NULL, NULL };
2749
2750
2751 /*
2752  * Try interpreting the current token as a network address.
2753  *
2754  * If WILDOK is set in flags, "*" can be used as a wildcard
2755  * and at least one of V4OK and V6OK must also be set.  The
2756  * "*" is interpreted as the IPv4 wildcard address if V4OK is 
2757  * set (including the case where V4OK and V6OK are both set),
2758  * and the IPv6 wildcard address otherwise.
2759  */
2760 static isc_result_t
2761 token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
2762         char *s;
2763         struct in_addr in4a;
2764         struct in6_addr in6a;
2765
2766         if (pctx->token.type != isc_tokentype_string)
2767                 return (ISC_R_UNEXPECTEDTOKEN);
2768
2769         s = pctx->token.value.as_pointer;
2770         if ((flags & WILDOK) != 0 && strcmp(s, "*") == 0) {
2771                 if ((flags & V4OK) != 0) {
2772                         isc_netaddr_any(na);
2773                         return (ISC_R_SUCCESS);
2774                 } else if ((flags & V6OK) != 0) {
2775                         isc_netaddr_any6(na);
2776                         return (ISC_R_SUCCESS);
2777                 } else {
2778                         INSIST(0);
2779                 }
2780         } else {
2781                 if ((flags & (V4OK | V4PREFIXOK)) != 0) {
2782                         if (inet_pton(AF_INET, s, &in4a) == 1) {
2783                                 isc_netaddr_fromin(na, &in4a);
2784                                 return (ISC_R_SUCCESS);
2785                         }
2786                 }
2787                 if ((flags & V4PREFIXOK) != 0 &&
2788                     strlen(s) <= 15U) {
2789                         char buf[64];
2790                         int i;
2791
2792                         strcpy(buf, s);
2793                         for (i = 0; i < 3; i++) {
2794                                 strcat(buf, ".0");
2795                                 if (inet_pton(AF_INET, buf, &in4a) == 1) {
2796                                         isc_netaddr_fromin(na, &in4a);
2797                                         return (ISC_R_SUCCESS);
2798                                 }
2799                         }
2800                 }
2801                 if (flags & V6OK) {
2802                         if (inet_pton(AF_INET6, s, &in6a) == 1) {
2803                                 isc_netaddr_fromin6(na, &in6a);
2804                                 return (ISC_R_SUCCESS);
2805                         }
2806                 }
2807         }
2808         return (ISC_R_UNEXPECTEDTOKEN);
2809 }
2810
2811 static isc_result_t
2812 get_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
2813         isc_result_t result;
2814         CHECK(cfg_gettoken(pctx, 0));
2815         result = token_addr(pctx, flags, na);
2816         if (result == ISC_R_UNEXPECTEDTOKEN)
2817                 parser_error(pctx, LOG_NEAR, "expected IP address");
2818  cleanup:
2819         return (result);
2820 }
2821
2822 static isc_boolean_t
2823 looking_at_netaddr(cfg_parser_t *pctx, unsigned int flags) {
2824         isc_result_t result;
2825         isc_netaddr_t na_dummy;
2826         result = token_addr(pctx, flags, &na_dummy);
2827         return (ISC_TF(result == ISC_R_SUCCESS));
2828 }
2829
2830 static isc_result_t
2831 get_port(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
2832         isc_result_t result;
2833
2834         CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
2835
2836         if ((flags & WILDOK) != 0 &&
2837             pctx->token.type == isc_tokentype_string &&
2838             strcmp(pctx->token.value.as_pointer, "*") == 0) {
2839                 *port = 0;
2840                 return (ISC_R_SUCCESS);
2841         }
2842         if (pctx->token.type != isc_tokentype_number) {
2843                 parser_error(pctx, LOG_NEAR,
2844                              "expected port number or '*'");
2845                 return (ISC_R_UNEXPECTEDTOKEN);
2846         }
2847         if (pctx->token.value.as_ulong >= 65536U) {
2848                 parser_error(pctx, LOG_NEAR,
2849                              "port number out of range");
2850                 return (ISC_R_UNEXPECTEDTOKEN);
2851         }
2852         *port = (in_port_t)(pctx->token.value.as_ulong);
2853         return (ISC_R_SUCCESS);
2854  cleanup:
2855         return (result);
2856 }
2857
2858 static isc_result_t
2859 parse_querysource(cfg_parser_t *pctx, int flags, cfg_obj_t **ret) {
2860         isc_result_t result;
2861         cfg_obj_t *obj = NULL;
2862         isc_netaddr_t netaddr;
2863         in_port_t port;
2864         unsigned int have_address = 0;
2865         unsigned int have_port = 0;
2866
2867         if ((flags & V4OK) != 0)
2868                 isc_netaddr_any(&netaddr);
2869         else if ((flags & V6OK) != 0)
2870                 isc_netaddr_any6(&netaddr);
2871         else
2872                 INSIST(0);
2873
2874         port = 0;
2875
2876         CHECK(create_cfgobj(pctx, &cfg_type_querysource, &obj));
2877         for (;;) {
2878                 CHECK(cfg_peektoken(pctx, 0));
2879                 if (pctx->token.type == isc_tokentype_string) {
2880                         if (strcasecmp(pctx->token.value.as_pointer,
2881                                        "address") == 0)
2882                         {
2883                                 /* read "address" */
2884                                 CHECK(cfg_gettoken(pctx, 0)); 
2885                                 CHECK(get_addr(pctx, flags|WILDOK, &netaddr));
2886                                 have_address++;
2887                         } else if (strcasecmp(pctx->token.value.as_pointer,
2888                                               "port") == 0)
2889                         {
2890                                 /* read "port" */
2891                                 CHECK(cfg_gettoken(pctx, 0)); 
2892                                 CHECK(get_port(pctx, WILDOK, &port));
2893                                 have_port++;
2894                         } else {
2895                                 parser_error(pctx, LOG_NEAR,
2896                                              "expected 'address' or 'port'");
2897                                 return (ISC_R_UNEXPECTEDTOKEN);
2898                         }
2899                 } else
2900                         break;
2901         }
2902         if (have_address > 1 || have_port > 1 ||
2903             have_address + have_port == 0) {
2904                 parser_error(pctx, 0, "expected one address and/or port");
2905                 return (ISC_R_UNEXPECTEDTOKEN);
2906         }
2907
2908         isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
2909         *ret = obj;
2910         return (ISC_R_SUCCESS);
2911
2912  cleanup:
2913         parser_error(pctx, LOG_NEAR, "invalid query source");
2914         CLEANUP_OBJ(obj);
2915         return (result);
2916 }
2917
2918 static isc_result_t
2919 parse_querysource4(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2920         UNUSED(type);
2921         return (parse_querysource(pctx, V4OK, ret));
2922 }
2923
2924 static isc_result_t
2925 parse_querysource6(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2926         UNUSED(type);
2927         return (parse_querysource(pctx, V6OK, ret));
2928 }
2929
2930 static void
2931 print_isc_netaddr(cfg_printer_t *pctx, isc_netaddr_t *na) {
2932         isc_result_t result;
2933         char text[128];
2934         isc_buffer_t buf;
2935
2936         isc_buffer_init(&buf, text, sizeof(text));
2937         result = isc_netaddr_totext(na, &buf);
2938         RUNTIME_CHECK(result == ISC_R_SUCCESS);
2939         print(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf));
2940 }
2941
2942 static void
2943 print_querysource(cfg_printer_t *pctx, cfg_obj_t *obj) {
2944         isc_netaddr_t na;
2945         isc_netaddr_fromsockaddr(&na, &obj->value.sockaddr);
2946         print(pctx, "address ", 8);
2947         print_isc_netaddr(pctx, &na);
2948         print(pctx, " port ", 6);
2949         print_uint(pctx, isc_sockaddr_getport(&obj->value.sockaddr));
2950 }
2951
2952 static cfg_type_t cfg_type_querysource4 = {
2953         "querysource4", parse_querysource4, NULL, NULL, NULL };
2954 static cfg_type_t cfg_type_querysource6 = {
2955         "querysource6", parse_querysource6, NULL, NULL, NULL };
2956 static cfg_type_t cfg_type_querysource = {
2957         "querysource", NULL, print_querysource, &cfg_rep_sockaddr, NULL };
2958
2959 /* netaddr */
2960
2961 static isc_result_t
2962 parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2963         isc_result_t result;
2964         cfg_obj_t *obj = NULL;
2965         isc_netaddr_t netaddr;
2966         UNUSED(type);
2967         CHECK(create_cfgobj(pctx, type, &obj));
2968         CHECK(get_addr(pctx, V4OK|V6OK, &netaddr));
2969         isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
2970         *ret = obj;
2971         return (ISC_R_SUCCESS);
2972  cleanup:
2973         CLEANUP_OBJ(obj);
2974         return (result);
2975 }
2976
2977 static cfg_type_t cfg_type_netaddr = {
2978         "netaddr", parse_netaddr, print_sockaddr, &cfg_rep_sockaddr, NULL };
2979
2980 /* netprefix */
2981
2982 static isc_result_t
2983 parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2984         cfg_obj_t *obj = NULL;
2985         isc_result_t result;
2986         isc_netaddr_t netaddr;
2987         unsigned int addrlen, prefixlen;
2988         UNUSED(type);
2989
2990         CHECK(get_addr(pctx, V4OK|V4PREFIXOK|V6OK, &netaddr));
2991         switch (netaddr.family) {
2992         case AF_INET:
2993                 addrlen = 32;
2994                 break;
2995         case AF_INET6:
2996                 addrlen = 128;
2997                 break;
2998         default:
2999                 addrlen = 0;
3000                 INSIST(0);
3001                 break;
3002         }
3003         CHECK(cfg_peektoken(pctx, 0));
3004         if (pctx->token.type == isc_tokentype_special &&
3005             pctx->token.value.as_char == '/') {
3006                 CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
3007                 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
3008                 if (pctx->token.type != isc_tokentype_number) {
3009                         parser_error(pctx, LOG_NEAR,
3010                                      "expected prefix length");
3011                         return (ISC_R_UNEXPECTEDTOKEN);
3012                 }
3013                 prefixlen = pctx->token.value.as_ulong;
3014                 if (prefixlen > addrlen) {
3015                         parser_error(pctx, LOG_NOPREP,
3016                                      "invalid prefix length");
3017                         return (ISC_R_RANGE);
3018                 }
3019         } else {
3020                 prefixlen = addrlen;
3021         }
3022         CHECK(create_cfgobj(pctx, &cfg_type_netprefix, &obj));
3023         obj->value.netprefix.address = netaddr;
3024         obj->value.netprefix.prefixlen = prefixlen;
3025         *ret = obj;
3026         return (ISC_R_SUCCESS);
3027  cleanup:
3028         parser_error(pctx, LOG_NEAR, "expected network prefix");
3029         return (result);
3030 }
3031
3032 static void
3033 print_netprefix(cfg_printer_t *pctx, cfg_obj_t *obj) {
3034         cfg_netprefix_t *p = &obj->value.netprefix;
3035         print_isc_netaddr(pctx, &p->address);
3036         print(pctx, "/", 1);
3037         print_uint(pctx, p->prefixlen);
3038 }
3039
3040 isc_boolean_t
3041 cfg_obj_isnetprefix(cfg_obj_t *obj) {
3042         REQUIRE(obj != NULL);
3043         return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
3044 }
3045
3046 void
3047 cfg_obj_asnetprefix(cfg_obj_t *obj, isc_netaddr_t *netaddr,
3048                     unsigned int *prefixlen) {
3049         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
3050         *netaddr = obj->value.netprefix.address;
3051         *prefixlen = obj->value.netprefix.prefixlen;
3052 }
3053
3054 static cfg_type_t cfg_type_netprefix = {
3055         "netprefix", parse_netprefix, print_netprefix, &cfg_rep_netprefix, NULL };
3056
3057 /* addrmatchelt */
3058
3059 static isc_result_t
3060 parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3061         isc_result_t result;
3062         UNUSED(type);
3063
3064         CHECK(cfg_peektoken(pctx, QSTRING));
3065
3066         if (pctx->token.type == isc_tokentype_string ||
3067             pctx->token.type == isc_tokentype_qstring) {
3068                 if (pctx->token.type == isc_tokentype_string &&
3069                     (strcasecmp(pctx->token.value.as_pointer, "key") == 0)) {
3070                         CHECK(parse(pctx, &cfg_type_keyref, ret));
3071                 } else {
3072                         if (looking_at_netaddr(pctx, V4OK|V4PREFIXOK|V6OK)) {
3073                                 CHECK(parse_netprefix(pctx, NULL, ret));
3074                         } else {
3075                                 CHECK(parse_astring(pctx, NULL, ret));
3076                         }
3077                 }
3078         } else if (pctx->token.type == isc_tokentype_special) {
3079                 if (pctx->token.value.as_char == '{') {
3080                         /* Nested match list. */
3081                         CHECK(parse(pctx, &cfg_type_bracketed_aml, ret));
3082                 } else if (pctx->token.value.as_char == '!') {
3083                         CHECK(cfg_gettoken(pctx, 0)); /* read "!" */
3084                         CHECK(parse(pctx, &cfg_type_negated, ret));
3085                 } else {
3086                         goto bad;
3087                 }
3088         } else {
3089         bad:
3090                 parser_error(pctx, LOG_NEAR,
3091                              "expected IP match list element");
3092                 return (ISC_R_UNEXPECTEDTOKEN);
3093         }
3094  cleanup:
3095         return (result);
3096 }
3097
3098 /*
3099  * A negated address match list element (like "! 10.0.0.1").
3100  * Somewhat sneakily, the caller is expected to parse the
3101  * "!", but not to print it.
3102  */
3103
3104 static cfg_tuplefielddef_t negated_fields[] = {
3105         { "value", &cfg_type_addrmatchelt, 0 },
3106         { NULL, NULL, 0 }
3107 };
3108
3109 static void
3110 print_negated(cfg_printer_t *pctx, cfg_obj_t *obj) {
3111         print(pctx, "!", 1);
3112         print_tuple(pctx, obj);
3113 }
3114
3115 static cfg_type_t cfg_type_negated = {
3116         "negated", parse_tuple, print_negated, &cfg_rep_tuple,
3117         &negated_fields
3118 };
3119
3120 /* an address match list element */
3121
3122 static cfg_type_t cfg_type_addrmatchelt = {
3123         "address_match_element", parse_addrmatchelt, NULL, NULL, NULL };
3124 static cfg_type_t cfg_type_bracketed_aml = {
3125         "bracketed_aml", parse_bracketed_list, print_bracketed_list,
3126         &cfg_rep_list, &cfg_type_addrmatchelt
3127 };
3128
3129 static isc_result_t
3130 parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type,
3131                   int flags, cfg_obj_t **ret)
3132 {
3133         isc_result_t result;
3134         isc_netaddr_t netaddr;
3135         in_port_t port = 0;
3136         cfg_obj_t *obj = NULL;
3137
3138         CHECK(create_cfgobj(pctx, type, &obj));
3139         CHECK(get_addr(pctx, flags, &netaddr));
3140         CHECK(cfg_peektoken(pctx, 0));
3141         if (pctx->token.type == isc_tokentype_string &&
3142             strcasecmp(pctx->token.value.as_pointer, "port") == 0) {
3143                 CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
3144                 CHECK(get_port(pctx, flags, &port));
3145         }
3146         isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
3147         *ret = obj;
3148         return (ISC_R_SUCCESS);
3149
3150  cleanup:
3151         CLEANUP_OBJ(obj);
3152         return (result);
3153 }
3154
3155 static isc_result_t
3156 parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3157         const unsigned int *flagp = type->of;
3158         return (parse_sockaddrsub(pctx, &cfg_type_sockaddr4wild, *flagp, ret));
3159 }
3160
3161 static void
3162 print_sockaddr(cfg_printer_t *pctx, cfg_obj_t *obj) {
3163         isc_netaddr_t netaddr;
3164         in_port_t port;
3165         char buf[ISC_NETADDR_FORMATSIZE];
3166
3167         isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
3168         isc_netaddr_format(&netaddr, buf, sizeof(buf));
3169         print_cstr(pctx, buf);
3170         port = isc_sockaddr_getport(&obj->value.sockaddr);
3171         if (port != 0) {
3172                 print(pctx, " port ", 6);
3173                 print_uint(pctx, port);
3174         }
3175 }
3176
3177 isc_boolean_t
3178 cfg_obj_issockaddr(cfg_obj_t *obj) {
3179         REQUIRE(obj != NULL);
3180         return (ISC_TF(obj->type->rep == &cfg_rep_sockaddr));
3181 }
3182
3183 isc_sockaddr_t *
3184 cfg_obj_assockaddr(cfg_obj_t *obj) {
3185         REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
3186         return (&obj->value.sockaddr);
3187 }
3188
3189 /* An IPv4/IPv6 address with optional port, "*" accepted as wildcard. */
3190 static unsigned int sockaddr4wild_flags = WILDOK|V4OK;
3191 static cfg_type_t cfg_type_sockaddr4wild = {
3192         "sockaddr4wild", parse_sockaddr, print_sockaddr,
3193         &cfg_rep_sockaddr, &sockaddr4wild_flags
3194 };
3195
3196 static unsigned int sockaddr6wild_flags = WILDOK|V6OK;
3197 static cfg_type_t cfg_type_sockaddr6wild = {
3198         "v6addrportwild", parse_sockaddr, print_sockaddr,
3199         &cfg_rep_sockaddr, &sockaddr6wild_flags
3200 };
3201
3202 static unsigned int sockaddr_flags = V4OK|V6OK;
3203 static cfg_type_t cfg_type_sockaddr = {
3204         "sockaddr", parse_sockaddr, print_sockaddr,
3205         &cfg_rep_sockaddr, &sockaddr_flags
3206 };
3207
3208 /*
3209  * The socket address syntax in the "controls" statement is silly.
3210  * It allows both socket address families, but also allows "*",
3211  * whis is gratuitously interpreted as the IPv4 wildcard address.
3212  */
3213 static unsigned int controls_sockaddr_flags = V4OK|V6OK|WILDOK;
3214 static cfg_type_t cfg_type_controls_sockaddr = {
3215         "controls_sockaddr", parse_sockaddr, print_sockaddr,
3216         &cfg_rep_sockaddr, &controls_sockaddr_flags };
3217
3218
3219 /*
3220  * Handle the special kludge syntax of the "keys" clause in the "server"
3221  * statement, which takes a single key with our without braces and semicolon.
3222  */
3223 static isc_result_t
3224 parse_server_key_kludge(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
3225 {
3226         isc_result_t result;
3227         isc_boolean_t braces = ISC_FALSE;
3228         UNUSED(type);
3229
3230         /* Allow opening brace. */
3231         CHECK(cfg_peektoken(pctx, 0));
3232         if (pctx->token.type == isc_tokentype_special &&
3233             pctx->token.value.as_char == '{') {
3234                 result = cfg_gettoken(pctx, 0);
3235                 braces = ISC_TRUE;
3236         }
3237
3238         CHECK(parse(pctx, &cfg_type_astring, ret));
3239
3240         if (braces) {
3241                 /* Skip semicolon if present. */
3242                 CHECK(cfg_peektoken(pctx, 0));
3243                 if (pctx->token.type == isc_tokentype_special &&
3244                     pctx->token.value.as_char == ';')
3245                         CHECK(cfg_gettoken(pctx, 0));
3246
3247                 CHECK(parse_special(pctx, '}'));
3248         }
3249  cleanup:
3250         return (result);
3251 }
3252 static cfg_type_t cfg_type_server_key_kludge = {
3253         "server_key", parse_server_key_kludge, NULL, NULL, NULL };
3254
3255
3256 /*
3257  * An optional logging facility.
3258  */
3259
3260 static isc_result_t
3261 parse_optional_facility(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
3262 {
3263         isc_result_t result;
3264         UNUSED(type);
3265
3266         CHECK(cfg_peektoken(pctx, QSTRING));
3267         if (pctx->token.type == isc_tokentype_string ||
3268             pctx->token.type == isc_tokentype_qstring) {
3269                 CHECK(parse(pctx, &cfg_type_astring, ret));
3270         } else {
3271                 CHECK(parse(pctx, &cfg_type_void, ret));
3272         }
3273  cleanup:
3274         return (result);
3275 }
3276
3277 static cfg_type_t cfg_type_optional_facility = {
3278         "optional_facility", parse_optional_facility, NULL, NULL, NULL };
3279
3280
3281 /*
3282  * A log severity.  Return as a string, except "debug N",
3283  * which is returned as a keyword object.
3284  */
3285
3286 static keyword_type_t debug_kw = { "debug", &cfg_type_uint32 };
3287 static cfg_type_t cfg_type_debuglevel = {
3288         "debuglevel", parse_keyvalue,
3289         print_keyvalue, &cfg_rep_uint32, &debug_kw
3290 };
3291
3292 static isc_result_t
3293 parse_logseverity(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3294         isc_result_t result;
3295         UNUSED(type);
3296
3297         CHECK(cfg_peektoken(pctx, 0));
3298         if (pctx->token.type == isc_tokentype_string &&
3299             strcasecmp(pctx->token.value.as_pointer, "debug") == 0) {
3300                 CHECK(cfg_gettoken(pctx, 0)); /* read "debug" */
3301                 CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER));
3302                 if (pctx->token.type == isc_tokentype_number) {
3303                         CHECK(parse_uint32(pctx, NULL, ret));
3304                 } else {
3305                         /*
3306                          * The debug level is optional and defaults to 1.
3307                          * This makes little sense, but we support it for
3308                          * compatibility with BIND 8.
3309                          */
3310                         CHECK(create_cfgobj(pctx, &cfg_type_uint32, ret));
3311                         (*ret)->value.uint32 = 1;
3312                 }
3313                 (*ret)->type = &cfg_type_debuglevel; /* XXX kludge */
3314         } else {
3315                 CHECK(parse(pctx, &cfg_type_loglevel, ret));
3316         }
3317  cleanup:
3318         return (result);
3319 }
3320
3321 static cfg_type_t cfg_type_logseverity = {
3322         "logseverity", parse_logseverity, NULL, NULL, NULL };
3323
3324 /*
3325  * The "file" clause of the "channel" statement.
3326  * This is yet another special case.
3327  */
3328
3329 static const char *logversions_enums[] = { "unlimited", NULL };
3330 static isc_result_t
3331 parse_logversions(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3332         return (parse_enum_or_other(pctx, type, &cfg_type_uint32, ret));
3333 }
3334 static cfg_type_t cfg_type_logversions = {
3335         "logversions", parse_logversions, print_ustring, 
3336         &cfg_rep_string, logversions_enums
3337 };
3338
3339 static cfg_tuplefielddef_t logfile_fields[] = {
3340         { "file", &cfg_type_qstring, 0 },
3341         { "versions", &cfg_type_logversions, 0 },
3342         { "size", &cfg_type_size, 0 },
3343         { NULL, NULL, 0 }
3344 };
3345
3346 static isc_result_t
3347 parse_logfile(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3348         isc_result_t result;
3349         cfg_obj_t *obj = NULL;
3350         const cfg_tuplefielddef_t *fields = type->of;   
3351
3352         CHECK(create_tuple(pctx, type, &obj));  
3353
3354         /* Parse the mandatory "file" field */
3355         CHECK(parse(pctx, fields[0].type, &obj->value.tuple[0]));
3356
3357         /* Parse "versions" and "size" fields in any order. */
3358         for (;;) {
3359                 CHECK(cfg_peektoken(pctx, 0));
3360                 if (pctx->token.type == isc_tokentype_string) {
3361                         CHECK(cfg_gettoken(pctx, 0));           
3362                         if (strcasecmp(pctx->token.value.as_pointer,
3363                                        "versions") == 0 &&
3364                             obj->value.tuple[1] == NULL) {
3365                                 CHECK(parse(pctx, fields[1].type,
3366                                             &obj->value.tuple[1]));
3367                         } else if (strcasecmp(pctx->token.value.as_pointer,
3368                                               "size") == 0 &&
3369                                    obj->value.tuple[2] == NULL) {
3370                                 CHECK(parse(pctx, fields[2].type,
3371                                             &obj->value.tuple[2]));
3372                         } else {
3373                                 break;
3374                         }
3375                 } else {
3376                         break;
3377                 }
3378         }
3379
3380         /* Create void objects for missing optional values. */
3381         if (obj->value.tuple[1] == NULL)
3382                 CHECK(parse_void(pctx, NULL, &obj->value.tuple[1]));
3383         if (obj->value.tuple[2] == NULL)
3384                 CHECK(parse_void(pctx, NULL, &obj->value.tuple[2]));
3385
3386         *ret = obj;
3387         return (ISC_R_SUCCESS);
3388
3389  cleanup:
3390         CLEANUP_OBJ(obj);       
3391         return (result);
3392 }
3393
3394 static void
3395 print_logfile(cfg_printer_t *pctx, cfg_obj_t *obj) {
3396         print_obj(pctx, obj->value.tuple[0]); /* file */
3397         if (obj->value.tuple[1]->type->print != print_void) {
3398                 print(pctx, " versions ", 10);
3399                 print_obj(pctx, obj->value.tuple[1]);
3400         }
3401         if (obj->value.tuple[2]->type->print != print_void) {
3402                 print(pctx, " size ", 6);
3403                 print_obj(pctx, obj->value.tuple[2]);
3404         }
3405 }
3406
3407 static cfg_type_t cfg_type_logfile = {
3408         "logfile", parse_logfile, print_logfile, &cfg_rep_tuple,
3409         logfile_fields
3410 };
3411
3412
3413 /*
3414  * lwres
3415  */
3416
3417 static cfg_tuplefielddef_t lwres_view_fields[] = {
3418         { "name", &cfg_type_astring, 0 },
3419         { "class", &cfg_type_optional_class, 0 },
3420         { NULL, NULL, 0 }
3421 };
3422 static cfg_type_t cfg_type_lwres_view = {
3423         "lwres_view", parse_tuple, print_tuple, &cfg_rep_tuple,
3424         lwres_view_fields
3425 };
3426
3427 static cfg_type_t cfg_type_lwres_searchlist = {
3428         "lwres_searchlist", parse_bracketed_list, print_bracketed_list,
3429         &cfg_rep_list, &cfg_type_astring };
3430
3431 static cfg_clausedef_t
3432 lwres_clauses[] = {
3433         { "listen-on", &cfg_type_portiplist, 0 },
3434         { "view", &cfg_type_lwres_view, 0 },
3435         { "search", &cfg_type_lwres_searchlist, 0 },
3436         { "ndots", &cfg_type_uint32, 0 },
3437         { NULL, NULL, 0 }
3438 };
3439
3440 static cfg_clausedef_t *
3441 lwres_clausesets[] = {
3442         lwres_clauses,
3443         NULL
3444 };
3445 static cfg_type_t cfg_type_lwres = {
3446         "lwres", parse_map, print_map, &cfg_rep_map, lwres_clausesets };
3447
3448 /*
3449  * rndc
3450  */
3451
3452 static cfg_clausedef_t
3453 rndcconf_options_clauses[] = {
3454         { "default-server", &cfg_type_astring, 0 },
3455         { "default-key", &cfg_type_astring, 0 },
3456         { "default-port", &cfg_type_uint32, 0 },
3457         { NULL, NULL, 0 }
3458 };
3459
3460 static cfg_clausedef_t *
3461 rndcconf_options_clausesets[] = {
3462         rndcconf_options_clauses,
3463         NULL
3464 };
3465
3466 static cfg_type_t cfg_type_rndcconf_options = {
3467         "rndcconf_options", parse_map, print_map, &cfg_rep_map,
3468         rndcconf_options_clausesets
3469 };
3470
3471 static cfg_clausedef_t
3472 rndcconf_server_clauses[] = {
3473         { "key", &cfg_type_astring, 0 },
3474         { "port", &cfg_type_uint32, 0 },
3475         { NULL, NULL, 0 }
3476 };
3477
3478 static cfg_clausedef_t *
3479 rndcconf_server_clausesets[] = {
3480         rndcconf_server_clauses,
3481         NULL
3482 };
3483
3484 static cfg_type_t cfg_type_rndcconf_server = {
3485         "rndcconf_server", parse_named_map, print_map, &cfg_rep_map,
3486         rndcconf_server_clausesets
3487 };
3488
3489 static cfg_clausedef_t
3490 rndcconf_clauses[] = {
3491         { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
3492         { "server", &cfg_type_rndcconf_server, CFG_CLAUSEFLAG_MULTI },
3493         { "options", &cfg_type_rndcconf_options, 0 },
3494         { NULL, NULL, 0 }
3495 };
3496
3497 static cfg_clausedef_t *
3498 rndcconf_clausesets[] = {
3499         rndcconf_clauses,
3500         NULL
3501 };
3502
3503 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndcconf = {
3504         "rndcconf", parse_mapbody, print_mapbody, &cfg_rep_map,
3505         rndcconf_clausesets
3506 };
3507
3508 static cfg_clausedef_t
3509 rndckey_clauses[] = {
3510         { "key", &cfg_type_key, 0 },
3511         { NULL, NULL, 0 }
3512 };
3513
3514 static cfg_clausedef_t *
3515 rndckey_clausesets[] = {
3516         rndckey_clauses,
3517         NULL
3518 };
3519
3520 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndckey = {
3521         "rndckey", parse_mapbody, print_mapbody, &cfg_rep_map,
3522         rndckey_clausesets
3523 };
3524
3525
3526 static isc_result_t
3527 cfg_gettoken(cfg_parser_t *pctx, int options) {
3528         isc_result_t result;
3529
3530         if (pctx->seen_eof)
3531                 return (ISC_R_SUCCESS);
3532
3533         options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
3534
3535  redo:
3536         pctx->token.type = isc_tokentype_unknown;
3537         result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
3538         pctx->ungotten = ISC_FALSE;
3539         pctx->line = isc_lex_getsourceline(pctx->lexer);
3540
3541         switch (result) {
3542         case ISC_R_SUCCESS:
3543                 if (pctx->token.type == isc_tokentype_eof) {
3544                         result = isc_lex_close(pctx->lexer);
3545                         INSIST(result == ISC_R_NOMORE ||
3546                                result == ISC_R_SUCCESS);
3547
3548                         if (isc_lex_getsourcename(pctx->lexer) != NULL) {
3549                                 /*
3550                                  * Closed an included file, not the main file.
3551                                  */
3552                                 cfg_listelt_t *elt;
3553                                 elt = ISC_LIST_TAIL(pctx->open_files->
3554                                                     value.list);
3555                                 INSIST(elt != NULL);
3556                                 ISC_LIST_UNLINK(pctx->open_files->
3557                                                 value.list, elt, link);
3558                                 ISC_LIST_APPEND(pctx->closed_files->
3559                                                 value.list, elt, link);
3560                                 goto redo;
3561                         }
3562                         pctx->seen_eof = ISC_TRUE;
3563                 }
3564                 break;
3565
3566         case ISC_R_NOSPACE:
3567                 /* More understandable than "ran out of space". */
3568                 parser_error(pctx, LOG_NEAR, "token too big");
3569                 break;
3570
3571         case ISC_R_IOERROR:
3572                 parser_error(pctx, 0, "%s",
3573                                  isc_result_totext(result));
3574                 break;
3575
3576         default:
3577                 parser_error(pctx, LOG_NEAR, "%s",
3578                              isc_result_totext(result));
3579                 break;
3580         }
3581         return (result);
3582 }
3583
3584 static void
3585 cfg_ungettoken(cfg_parser_t *pctx) {
3586         if (pctx->seen_eof)
3587                 return;
3588         isc_lex_ungettoken(pctx->lexer, &pctx->token);
3589         pctx->ungotten = ISC_TRUE;
3590 }
3591
3592 static isc_result_t
3593 cfg_peektoken(cfg_parser_t *pctx, int options) {
3594         isc_result_t result;
3595         CHECK(cfg_gettoken(pctx, options));
3596         cfg_ungettoken(pctx);
3597  cleanup:
3598         return (result);
3599 }
3600
3601 /*
3602  * Get a string token, accepting both the quoted and the unquoted form.
3603  * Log an error if the next token is not a string.
3604  */
3605 static isc_result_t
3606 cfg_getstringtoken(cfg_parser_t *pctx) {
3607         isc_result_t result;
3608
3609         result = cfg_gettoken(pctx, QSTRING);
3610         if (result != ISC_R_SUCCESS)
3611                 return (result);
3612
3613         if (pctx->token.type != isc_tokentype_string &&
3614             pctx->token.type != isc_tokentype_qstring) {
3615                 parser_error(pctx, LOG_NEAR, "expected string");
3616                 return (ISC_R_UNEXPECTEDTOKEN);
3617         }
3618         return (ISC_R_SUCCESS);
3619 }
3620
3621 static void
3622 parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
3623         va_list args;
3624         va_start(args, fmt);
3625         parser_complain(pctx, ISC_FALSE, flags, fmt, args);
3626         va_end(args);
3627         pctx->errors++;
3628 }
3629
3630 static void
3631 parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
3632         va_list args;
3633         va_start(args, fmt);
3634         parser_complain(pctx, ISC_TRUE, flags, fmt, args);
3635         va_end(args);
3636         pctx->warnings++;
3637 }
3638
3639 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
3640
3641 static char *
3642 current_file(cfg_parser_t *pctx) {
3643         static char none[] = "none";
3644         cfg_listelt_t *elt;
3645         cfg_obj_t *fileobj;
3646
3647         if (pctx->open_files == NULL)
3648                 return (none);
3649         elt = ISC_LIST_TAIL(pctx->open_files->value.list);
3650         if (elt == NULL)
3651               return (none);
3652
3653         fileobj = elt->obj;
3654         INSIST(fileobj->type == &cfg_type_qstring);
3655         return (fileobj->value.string.base);
3656 }
3657
3658 static void
3659 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
3660                 unsigned int flags, const char *format,
3661                 va_list args)
3662 {
3663         char tokenbuf[MAX_LOG_TOKEN + 10];
3664         static char where[ISC_DIR_PATHMAX + 100];
3665         static char message[2048];
3666         int level = ISC_LOG_ERROR;
3667         const char *prep = "";
3668
3669         if (is_warning)
3670                 level = ISC_LOG_WARNING;
3671
3672         sprintf(where, "%s:%u: ", current_file(pctx), pctx->line);
3673
3674         if ((unsigned int)vsprintf(message, format, args) >= sizeof message)
3675                 FATAL_ERROR(__FILE__, __LINE__,
3676                             "error message would overflow");
3677
3678         if ((flags & (LOG_NEAR|LOG_BEFORE|LOG_NOPREP)) != 0) {
3679                 isc_region_t r;
3680
3681                 if (pctx->ungotten)
3682                         (void)cfg_gettoken(pctx, 0);
3683
3684                 if (pctx->token.type == isc_tokentype_eof) {
3685                         snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
3686                 } else if (pctx->token.type == isc_tokentype_unknown) {
3687                         flags = 0;
3688                         tokenbuf[0] = '\0';
3689                 } else {
3690                         isc_lex_getlasttokentext(pctx->lexer,
3691                                                  &pctx->token, &r);
3692                         if (r.length > MAX_LOG_TOKEN)
3693                                 snprintf(tokenbuf, sizeof(tokenbuf),
3694                                          "'%.*s...'", MAX_LOG_TOKEN, r.base);
3695                         else
3696                                 snprintf(tokenbuf, sizeof(tokenbuf),
3697                                          "'%.*s'", (int)r.length, r.base);
3698                 }
3699
3700                 /* Choose a preposition. */
3701                 if (flags & LOG_NEAR)
3702                         prep = " near ";
3703                 else if (flags & LOG_BEFORE)
3704                         prep = " before ";
3705                 else
3706                         prep = " ";
3707         } else {
3708                 tokenbuf[0] = '\0';
3709         }
3710         isc_log_write(pctx->lctx, CAT, MOD, level,
3711                       "%s%s%s%s", where, message, prep, tokenbuf);
3712 }
3713
3714 void
3715 cfg_obj_log(cfg_obj_t *obj, isc_log_t *lctx, int level, const char *fmt, ...) {
3716         va_list ap;
3717         char msgbuf[2048];
3718
3719         if (! isc_log_wouldlog(lctx, level))
3720                 return;
3721
3722         va_start(ap, fmt);
3723
3724         vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
3725         isc_log_write(lctx, CAT, MOD, level,
3726                       "%s:%u: %s",
3727                       obj->file == NULL ? "<unknown file>" : obj->file,
3728                       obj->line, msgbuf);
3729         va_end(ap);
3730 }
3731
3732 static isc_result_t
3733 create_cfgobj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3734         cfg_obj_t *obj;
3735
3736         obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
3737         if (obj == NULL)
3738                 return (ISC_R_NOMEMORY);
3739         obj->type = type;
3740         obj->file = current_file(pctx);
3741         obj->line = pctx->line;
3742         *ret = obj;
3743         return (ISC_R_SUCCESS);
3744 }
3745
3746 static void
3747 map_symtabitem_destroy(char *key, unsigned int type,
3748                        isc_symvalue_t symval, void *userarg)
3749 {
3750         cfg_obj_t *obj = symval.as_pointer;
3751         cfg_parser_t *pctx = (cfg_parser_t *)userarg;
3752
3753         UNUSED(key);
3754         UNUSED(type);
3755
3756         cfg_obj_destroy(pctx, &obj);
3757 }
3758
3759
3760 static isc_result_t
3761 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3762         isc_result_t result;
3763         isc_symtab_t *symtab = NULL;
3764         cfg_obj_t *obj = NULL;
3765
3766         CHECK(create_cfgobj(pctx, type, &obj));
3767         CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */
3768                                 map_symtabitem_destroy,
3769                                 pctx, ISC_FALSE, &symtab));
3770
3771         obj->value.map.symtab = symtab;
3772         obj->value.map.id = NULL;
3773
3774         *ret = obj;
3775         return (ISC_R_SUCCESS);
3776
3777  cleanup:
3778         if (obj != NULL)
3779                 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
3780         return (result);
3781 }
3782
3783 static void
3784 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
3785         CLEANUP_OBJ(obj->value.map.id);
3786         isc_symtab_destroy(&obj->value.map.symtab);
3787 }
3788
3789 isc_boolean_t
3790 cfg_obj_istype(cfg_obj_t *obj, const cfg_type_t *type) {
3791         return (ISC_TF(obj->type == type));
3792 }
3793
3794 /*
3795  * Destroy 'obj', a configuration object created in 'pctx'.
3796  */
3797 void
3798 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
3799         cfg_obj_t *obj = *objp;
3800         obj->type->rep->free(pctx, obj);
3801         isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
3802         *objp = NULL;
3803 }
3804
3805 static void
3806 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
3807         UNUSED(pctx);
3808         UNUSED(obj);
3809 }
3810
3811 /*
3812  * Data and functions for printing grammar summaries.
3813  */
3814 static struct flagtext {
3815         unsigned int flag;
3816         const char *text;
3817 } flagtexts[] = {
3818         { CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
3819         { CFG_CLAUSEFLAG_NYI, "not yet implemented" },
3820         { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
3821         { CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
3822         { 0, NULL }
3823 };
3824
3825 static void
3826 print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
3827         struct flagtext *p;
3828         isc_boolean_t first = ISC_TRUE;
3829         for (p = flagtexts; p->flag != 0; p++) {
3830                 if ((flags & p->flag) != 0) {
3831                         if (first)
3832                                 print(pctx, " // ", 4);
3833                         else
3834                                 print(pctx, ", ", 2);
3835                         print_cstr(pctx, p->text);
3836                         first = ISC_FALSE;
3837                 }
3838         }
3839 }
3840
3841 static void
3842 print_grammar(cfg_printer_t *pctx, const cfg_type_t *type) {
3843         if (type->print == print_mapbody) {
3844                 const cfg_clausedef_t * const *clauseset;
3845                 const cfg_clausedef_t *clause;
3846
3847                 for (clauseset = type->of; *clauseset != NULL; clauseset++) {
3848                         for (clause = *clauseset;
3849                              clause->name != NULL;
3850                      clause++) {
3851                                 print_cstr(pctx, clause->name);
3852                                 print(pctx, " ", 1);
3853                                 print_grammar(pctx, clause->type);
3854                                 print(pctx, ";", 1);
3855                                 /* XXX print flags here? */
3856                                 print(pctx, "\n\n", 2);
3857                         }
3858                 }
3859         } else if (type->print == print_map) {
3860                 const cfg_clausedef_t * const *clauseset;
3861                 const cfg_clausedef_t *clause;
3862
3863                 if (type->parse == parse_named_map) {
3864                         print_grammar(pctx, &cfg_type_astring);
3865                         print(pctx, " ", 1);
3866                 } else if (type->parse == parse_addressed_map) {
3867                         print_grammar(pctx, &cfg_type_netaddr);
3868                         print(pctx, " ", 1);
3869                 }
3870                 
3871                 print_open(pctx);
3872
3873                 for (clauseset = type->of; *clauseset != NULL; clauseset++) {
3874                         for (clause = *clauseset;
3875                              clause->name != NULL;
3876                              clause++) {
3877                                 print_indent(pctx);
3878                                 print_cstr(pctx, clause->name);
3879                                 if (clause->type->print != print_void)
3880                                         print(pctx, " ", 1);
3881                                 print_grammar(pctx, clause->type);
3882                                 print(pctx, ";", 1);
3883                                 print_clause_flags(pctx, clause->flags);
3884                                 print(pctx, "\n", 1);
3885                         }
3886                 }
3887                 print_close(pctx);
3888         } else if (type->print == print_tuple) {
3889                 const cfg_tuplefielddef_t *fields = type->of;
3890                 const cfg_tuplefielddef_t *f;
3891                 isc_boolean_t need_space = ISC_FALSE;
3892
3893                 for (f = fields; f->name != NULL; f++) {
3894                         if (need_space)
3895                                 print(pctx, " ", 1);
3896                         print_grammar(pctx, f->type);
3897                         need_space = ISC_TF(f->type->print != print_void);
3898                 }
3899         } else if (type->parse == parse_enum) {
3900                 const char * const *p;
3901                 print(pctx, "( ", 2);
3902                 for (p = type->of; *p != NULL; p++) {
3903                         print_cstr(pctx, *p);
3904                         if (p[1] != NULL)
3905                                 print(pctx, " | ", 3);
3906                 }
3907                 print(pctx, " )", 2);
3908         } else if (type->print == print_bracketed_list) {
3909                 print(pctx, "{ ", 2);
3910                 print_grammar(pctx, type->of);
3911                 print(pctx, "; ... }", 7);
3912         } else if (type->parse == parse_keyvalue) {
3913                 const keyword_type_t *kw = type->of;
3914                 print_cstr(pctx, kw->name);
3915                 print(pctx, " ", 1);
3916                 print_grammar(pctx, kw->type);
3917         } else if (type->parse == parse_optional_keyvalue) {
3918                 const keyword_type_t *kw = type->of;
3919                 print(pctx, "[ ", 2);
3920                 print_cstr(pctx, kw->name);
3921                 print(pctx, " ", 1);
3922                 print_grammar(pctx, kw->type);
3923                 print(pctx, " ]", 2);
3924         } else if (type->parse == parse_sockaddr) {
3925                 const unsigned int *flagp = type->of;
3926                 int n = 0;
3927                 print(pctx, "( ", 2);
3928                 if (*flagp & V4OK) {
3929                         if (n != 0)
3930                                 print(pctx, " | ", 3);
3931                         print_cstr(pctx, "<ipv4_address>");
3932                         n++;
3933                 }
3934                 if (*flagp & V6OK) {
3935                         if (n != 0)
3936                                 print(pctx, " | ", 3);
3937                         print_cstr(pctx, "<ipv6_address>");
3938                         n++;                    
3939                 }
3940                 if (*flagp & WILDOK) {
3941                         if (n != 0)
3942                                 print(pctx, " | ", 3);
3943                         print(pctx, "*", 1);
3944                         n++;
3945                 }
3946                 print(pctx, " ) ", 3);
3947                 if (*flagp & WILDOK) {
3948                         print_cstr(pctx, "[ port ( <integer> | * ) ]");
3949                 } else {
3950                         print_cstr(pctx, "[ port <integer> ]");
3951                 }
3952         } else if (type->print == print_void) {
3953                 /* Print nothing. */
3954         } else {
3955                 print(pctx, "<", 1);
3956                 print_cstr(pctx, type->name);
3957                 print(pctx, ">", 1);
3958         }
3959 }
3960
3961 void
3962 cfg_print_grammar(const cfg_type_t *type,
3963         void (*f)(void *closure, const char *text, int textlen),
3964         void *closure)
3965 {
3966         cfg_printer_t pctx;
3967         pctx.f = f;
3968         pctx.closure = closure;
3969         pctx.indent = 0;
3970         print_grammar(&pctx, type);
3971 }