1 /* $NetBSD: dict_open.c,v 1.4 2023/12/23 20:30:46 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* dict_open 3
6 /* SUMMARY
7 /* low-level dictionary interface
8 /* SYNOPSIS
9 /* #include <dict.h>
10 /*
11 /* DICT *dict_open(dict_spec, open_flags, dict_flags)
12 /* const char *dict_spec;
13 /* int open_flags;
14 /* int dict_flags;
15 /*
16 /* DICT *dict_open3(dict_type, dict_name, open_flags, dict_flags)
17 /* const char *dict_type;
18 /* const char *dict_name;
19 /* int open_flags;
20 /* int dict_flags;
21 /*
22 /* int dict_put(dict, key, value)
23 /* DICT *dict;
24 /* const char *key;
25 /* const char *value;
26 /*
27 /* const char *dict_get(dict, key)
28 /* DICT *dict;
29 /* const char *key;
30 /*
31 /* int dict_del(dict, key)
32 /* DICT *dict;
33 /* const char *key;
34 /*
35 /* int dict_seq(dict, func, key, value)
36 /* DICT *dict;
37 /* int func;
38 /* const char **key;
39 /* const char **value;
40 /*
41 /* void dict_close(dict)
42 /* DICT *dict;
43 /*
44 /* typedef struct {
45 /* .in +4
46 /* char *type;
47 /* DICT_OPEN_FN dict_fn;
48 /* MKMAP_OPEN_FN mkmap_fn; /* See <mkmap.h> */
49 /* .in -4
50 /* } DICT_OPEN_INFO;
51 /*
52 /* typedef DICT *(*DICT_OPEN_FN) (const char *, int, int);
53 /*
54 /* void dict_open_register(open_info)
55 /* DICT_OPEN_INFO *open_info;
56 /*
57 /* const DICT_OPEN_INFO *dict_open_lookup(dict_type)
58 /* const char *dict_type;
59 /*
60 /* typedef DICT_OPEN_INFO (*DICT_OPEN_EXTEND_FN)(char *);
61 /*
62 /* DICT_OPEN_EXTEND_FN dict_open_extend(call_back)
63 /* DICT_OPEN_EXTEND_FN call_back;
64 /*
65 /* ARGV *dict_mapnames()
66 /*
67 /* typedef ARGV *(*DICT_MAPNAMES_EXTEND_FN)(ARGV *names);
68 /*
69 /* DICT_MAPNAMES_EXTEND_FN dict_mapnames_extend(call_back)
70 /* DICT_MAPNAMES_EXTEND_FN call_back;
71 /*
72 /* int dict_isjmp(dict)
73 /* DICT *dict;
74 /*
75 /* int dict_setjmp(dict)
76 /* DICT *dict;
77 /*
78 /* int dict_longjmp(dict, val)
79 /* DICT *dict;
80 /* int val;
81 /*
82 /* void dict_type_override(dict, type)
83 /* DICT *dict;
84 /* const char *type;
85 /* DESCRIPTION
86 /* This module implements a low-level interface to multiple
87 /* physical dictionary types.
88 /*
89 /* dict_open() takes a type:name pair that specifies a dictionary type
90 /* and dictionary name, opens the dictionary, and returns a dictionary
91 /* handle. The \fIopen_flags\fR arguments are as in open(2). The
92 /* \fIdict_flags\fR are the bit-wise OR of zero or more of the following:
93 /* .IP DICT_FLAG_DUP_WARN
94 /* Warn about duplicate keys, if the underlying database does not
95 /* support duplicate keys. The default is to terminate with a fatal
96 /* error.
97 /* .IP DICT_FLAG_DUP_IGNORE
98 /* Ignore duplicate keys if the underlying database does not
99 /* support duplicate keys. The default is to terminate with a fatal
100 /* error.
101 /* .IP DICT_FLAG_DUP_REPLACE
102 /* Replace duplicate keys if the underlying database supports such
103 /* an operation. The default is to terminate with a fatal error.
104 /* .IP DICT_FLAG_TRY0NULL
105 /* With maps where this is appropriate, append no null byte to
106 /* keys and values.
107 /* When neither DICT_FLAG_TRY0NULL nor DICT_FLAG_TRY1NULL are
108 /* specified, the software guesses what format to use for reading;
109 /* and in the absence of definite information, a system-dependent
110 /* default is chosen for writing.
111 /* .IP DICT_FLAG_TRY1NULL
112 /* With maps where this is appropriate, append one null byte to
113 /* keys and values.
114 /* When neither DICT_FLAG_TRY0NULL nor DICT_FLAG_TRY1NULL are
115 /* specified, the software guesses what format to use for reading;
116 /* and in the absence of definite information, a system-dependent
117 /* default is chosen for writing.
118 /* .IP DICT_FLAG_LOCK
119 /* With maps where this is appropriate, acquire an exclusive lock
120 /* before writing, and acquire a shared lock before reading.
121 /* Release the lock when the operation completes.
122 /* .IP DICT_FLAG_OPEN_LOCK
123 /* The behavior of this flag depends on whether a database
124 /* sets the DICT_FLAG_MULTI_WRITER flag to indicate that it
125 /* is multi-writer safe.
126 /*
127 /* With databases that are not multi-writer safe, dict_open()
128 /* acquires a persistent exclusive lock, or it terminates with
129 /* a fatal run-time error.
130 /*
131 /* With databases that are multi-writer safe, dict_open()
132 /* downgrades the DICT_FLAG_OPEN_LOCK flag (persistent lock)
133 /* to DICT_FLAG_LOCK (temporary lock).
134 /* .IP DICT_FLAG_FOLD_FIX
135 /* With databases whose lookup fields are fixed-case strings,
136 /* fold the search string to lower case before accessing the
137 /* database. This includes hash:, cdb:, dbm:. nis:, ldap:,
138 /* *sql. WARNING: case folding is supported only for ASCII or
139 /* valid UTF-8.
140 /* .IP DICT_FLAG_FOLD_MUL
141 /* With databases where one lookup field can match both upper
142 /* and lower case, fold the search key to lower case before
143 /* accessing the database. This includes regexp: and pcre:.
144 /* WARNING: case folding is supported only for ASCII or valid
145 /* UTF-8.
146 /* .IP DICT_FLAG_FOLD_ANY
147 /* Short-hand for (DICT_FLAG_FOLD_FIX | DICT_FLAG_FOLD_MUL).
148 /* .IP DICT_FLAG_SYNC_UPDATE
149 /* With file-based maps, flush I/O buffers to file after each update.
150 /* Thus feature is not supported with some file-based dictionaries.
151 /* .IP DICT_FLAG_NO_REGSUB
152 /* Disallow regular expression substitution from the lookup string
153 /* into the lookup result, to block data injection attacks.
154 /* .IP DICT_FLAG_NO_PROXY
155 /* Disallow access through the unprivileged \fBproxymap\fR
156 /* service, to block privilege escalation attacks.
157 /* .IP DICT_FLAG_NO_UNAUTH
158 /* Disallow lookup mechanisms that lack any form of authentication,
159 /* to block privilege escalation attacks (example: tcp_table;
160 /* even NIS can be secured to some extent by requiring that
161 /* the server binds to a privileged port).
162 /* .IP DICT_FLAG_PARANOID
163 /* A combination of all the paranoia flags: DICT_FLAG_NO_REGSUB,
164 /* DICT_FLAG_NO_PROXY and DICT_FLAG_NO_UNAUTH.
165 /* .IP DICT_FLAG_BULK_UPDATE
166 /* Enable preliminary code for bulk-mode database updates.
167 /* The caller must create an exception handler with dict_jmp_alloc()
168 /* and must trap exceptions from the database client with dict_setjmp().
169 /* .IP DICT_FLAG_DEBUG
170 /* Enable additional logging.
171 /* .IP DICT_FLAG_UTF8_REQUEST
172 /* With util_utf8_enable != 0, require that lookup/update/delete
173 /* keys and values are valid UTF-8. Skip a lookup/update/delete
174 /* request with a non-UTF-8 key, skip an update request with
175 /* a non-UTF-8 value, and fail a lookup request with a non-UTF-8
176 /* value.
177 /* .IP DICT_FLAG_SRC_RHS_IS_FILE
178 /* With dictionaries that are created from source text, each
179 /* value in the source of a dictionary specifies a list of
180 /* file names separated by comma and/or whitespace. The file
181 /* contents are concatenated with a newline inserted between
182 /* files, and the base64-encoded result is stored under the
183 /* key.
184 /* .sp
185 /* NOTE 1: it is up to the application to decode lookup results
186 /* with dict_file_lookup() or equivalent (this requires that
187 /* the dictionary is opened with DICT_FLAG_SRC_RHS_IS_FILE).
188 /* Decoding is not built into the normal dictionary lookup
189 /* method, because that would complicate dictionary nesting,
190 /* pipelining, and proxying.
191 /* .sp
192 /* NOTE 2: it is up to the application to convert file names
193 /* into base64-encoded file content before calling the dictionary
194 /* update method (see dict_file(3) for support). Automatic
195 /* file content encoding is available only when a dictionary
196 /* is created from source text.
197 /* .PP
198 /* Specify DICT_FLAG_NONE for no special processing.
199 /*
200 /* The dictionary types are as follows:
201 /* .IP environ
202 /* The process environment array. The \fIdict_name\fR argument is ignored.
203 /* .IP dbm
204 /* DBM file.
205 /* .IP hash
206 /* Berkeley DB file in hash format.
207 /* .IP btree
208 /* Berkeley DB file in btree format.
209 /* .IP nis
210 /* NIS map. Only read access is supported.
211 /* .IP nisplus
212 /* NIS+ map. Only read access is supported.
213 /* .IP netinfo
214 /* NetInfo table. Only read access is supported.
215 /* .IP ldap
216 /* LDAP ("light-weight" directory access protocol) database access.
217 /* .IP pcre
218 /* PERL-compatible regular expressions.
219 /* .IP regexp
220 /* POSIX-compatible regular expressions.
221 /* .IP texthash
222 /* Flat text in postmap(1) input format.
223 /* .PP
224 /* dict_open3() takes separate arguments for dictionary type and
225 /* name, but otherwise performs the same functions as dict_open().
226 /*
227 /* The dict_get(), dict_put(), dict_del(), and dict_seq()
228 /* macros evaluate their first argument multiple times.
229 /* These names should have been in uppercase.
230 /*
231 /* dict_get() retrieves the value stored in the named dictionary
232 /* under the given key. A null pointer means the value was not found.
233 /* As with dict_lookup(), the result is owned by the lookup table
234 /* implementation. Make a copy if the result is to be modified,
235 /* or if the result is to survive multiple table lookups.
236 /*
237 /* dict_put() stores the specified key and value into the named
238 /* dictionary. A zero (DICT_STAT_SUCCESS) result means the
239 /* update was made.
240 /*
241 /* dict_del() removes a dictionary entry, and returns
242 /* DICT_STAT_SUCCESS in case of success.
243 /*
244 /* dict_seq() iterates over all members in the named dictionary.
245 /* func is define DICT_SEQ_FUN_FIRST (select first member) or
246 /* DICT_SEQ_FUN_NEXT (select next member). A zero (DICT_STAT_SUCCESS)
247 /* result means that an entry was found.
248 /*
249 /* dict_close() closes the specified dictionary and cleans up the
250 /* associated data structures.
251 /*
252 /* dict_open_register() adds support for a new dictionary type.
253 /* NOTE: this function does not copy its argument.
254 /*
255 /* dict_open_lookup() returns a pointer to the DICT_OPEN_INFO
256 /* for the specified dictionary type, or a null pointer if the
257 /* requested information is not found.
258 /*
259 /* dict_open_extend() registers a call-back function that looks
260 /* up the dictionary open() function for a type that is not
261 /* registered, or null in case of error. The result value is
262 /* the last previously-registered call-back or null.
263 /*
264 /* dict_mapnames() returns a sorted list with the names of all available
265 /* dictionary types.
266 /*
267 /* dict_mapnames_extend() registers a call-back function that
268 /* enumerates additional dictionary type names. The result
269 /* will be sorted by dict_mapnames(). The result value
270 /* is the last previously-registered call-back or null.
271 /*
272 /* dict_setjmp() saves processing context and makes that context
273 /* available for use with dict_longjmp(). Normally, dict_setjmp()
274 /* returns zero. A non-zero result means that dict_setjmp()
275 /* returned through a dict_longjmp() call; the result is the
276 /* \fIval\fR argument given to dict_longjmp(). dict_isjmp()
277 /* returns non-zero when dict_setjmp() and dict_longjmp()
278 /* are enabled for a given dictionary.
279 /*
280 /* NB: non-local jumps such as dict_longjmp() are not safe for
281 /* jumping out of any routine that manipulates DICT data.
282 /* longjmp() like calls are best avoided in signal handlers.
283 /*
284 /* dict_type_override() changes the symbolic dictionary type.
285 /* This is used by dictionaries whose internals are based on
286 /* some other dictionary type.
287 /* DIAGNOSTICS
288 /* Fatal error: open error, unsupported dictionary type, attempt to
289 /* update non-writable dictionary.
290 /*
291 /* The lookup routine returns non-null when the request is
292 /* satisfied. The update, delete and sequence routines return
293 /* zero (DICT_STAT_SUCCESS) when the request is satisfied.
294 /* The dict->errno value is non-zero only when the last operation
295 /* was not satisfied due to a dictionary access error. This
296 /* can have the following values:
297 /* .IP DICT_ERR_NONE(zero)
298 /* There was no dictionary access error. For example, the
299 /* request was satisfied, the requested information did not
300 /* exist in the dictionary, or the information already existed
301 /* when it should not exist (collision).
302 /* .IP DICT_ERR_RETRY(<0)
303 /* The dictionary was temporarily unavailable. This can happen
304 /* with network-based services.
305 /* .IP DICT_ERR_CONFIG(<0)
306 /* The dictionary was unavailable due to a configuration error.
307 /* .PP
308 /* Generally, a program is expected to test the function result
309 /* value for "success" first. If the operation was not successful,
310 /* a program is expected to test for a non-zero dict->error
311 /* status to distinguish between a data notfound/collision
312 /* condition or a dictionary access error.
313 /* LICENSE
314 /* .ad
315 /* .fi
316 /* The Secure Mailer license must be distributed with this software.
317 /* AUTHOR(S)
318 /* Wietse Venema
319 /* IBM T.J. Watson Research
320 /* P.O. Box 704
321 /* Yorktown Heights, NY 10598, USA
322 /*
323 /* Wietse Venema
324 /* Google, Inc.
325 /* 111 8th Avenue
326 /* New York, NY 10011, USA
327 /*--*/
328
329 /* System library. */
330
331 #include <sys_defs.h>
332 #include <string.h>
333 #include <stdlib.h>
334
335 /* Utility library. */
336
337 #include <argv.h>
338 #include <mymalloc.h>
339 #include <msg.h>
340 #include <dict.h>
341 #include <dict_cdb.h>
342 #include <dict_env.h>
343 #include <dict_unix.h>
344 #include <dict_tcp.h>
345 #include <dict_sdbm.h>
346 #include <dict_dbm.h>
347 #include <dict_db.h>
348 #include <dict_lmdb.h>
349 #include <dict_nis.h>
350 #include <dict_nisplus.h>
351 #include <dict_ni.h>
352 #include <dict_pcre.h>
353 #include <dict_regexp.h>
354 #include <dict_static.h>
355 #include <dict_cidr.h>
356 #include <dict_ht.h>
357 #include <dict_thash.h>
358 #include <dict_sockmap.h>
359 #include <dict_fail.h>
360 #include <dict_pipe.h>
361 #include <dict_random.h>
362 #include <dict_union.h>
363 #include <dict_inline.h>
364 #include <stringops.h>
365 #include <split_at.h>
366 #include <htable.h>
367 #include <myflock.h>
368 #include <mkmap.h>
369
370 /*
371 * lookup table for available map types.
372 */
373 static const DICT_OPEN_INFO dict_open_info[] = {
374 DICT_TYPE_ENVIRON, dict_env_open, 0,
375 DICT_TYPE_HT, dict_ht_open, 0,
376 DICT_TYPE_UNIX, dict_unix_open, 0,
377 DICT_TYPE_TCP, dict_tcp_open, 0,
378 #ifdef HAS_DBM
379 DICT_TYPE_DBM, dict_dbm_open, mkmap_dbm_open,
380 #endif
381 #ifdef HAS_DB
382 DICT_TYPE_HASH, dict_hash_open, mkmap_hash_open,
383 DICT_TYPE_BTREE, dict_btree_open, mkmap_btree_open,
384 #endif
385 #ifdef HAS_NIS
386 DICT_TYPE_NIS, dict_nis_open, 0,
387 #endif
388 #ifdef HAS_NISPLUS
389 DICT_TYPE_NISPLUS, dict_nisplus_open, 0,
390 #endif
391 #ifdef HAS_NETINFO
392 DICT_TYPE_NETINFO, dict_ni_open, 0,
393 #endif
394 #ifdef HAS_POSIX_REGEXP
395 DICT_TYPE_REGEXP, dict_regexp_open, 0,
396 #endif
397 DICT_TYPE_STATIC, dict_static_open, 0,
398 DICT_TYPE_CIDR, dict_cidr_open, 0,
399 DICT_TYPE_THASH, dict_thash_open, 0,
400 DICT_TYPE_SOCKMAP, dict_sockmap_open, 0,
401 DICT_TYPE_FAIL, dict_fail_open, mkmap_fail_open,
402 DICT_TYPE_PIPE, dict_pipe_open, 0,
403 DICT_TYPE_RANDOM, dict_random_open, 0,
404 DICT_TYPE_UNION, dict_union_open, 0,
405 DICT_TYPE_INLINE, dict_inline_open, 0,
406 #ifndef USE_DYNAMIC_MAPS
407 #ifdef HAS_PCRE
408 DICT_TYPE_PCRE, dict_pcre_open, 0,
409 #endif
410 #ifdef HAS_CDB
411 DICT_TYPE_CDB, dict_cdb_open, mkmap_cdb_open,
412 #endif
413 #ifdef HAS_SDBM
414 DICT_TYPE_SDBM, dict_sdbm_open, mkmap_sdbm_open,
415 #endif
416 #ifdef HAS_LMDB
417 DICT_TYPE_LMDB, dict_lmdb_open, mkmap_lmdb_open,
418 #endif
419 #endif /* !USE_DYNAMIC_MAPS */
420 0,
421 };
422
423 static HTABLE *dict_open_hash;
424
425 /*
426 * Extension hooks.
427 */
428 static DICT_OPEN_EXTEND_FN dict_open_extend_hook;
429 static DICT_MAPNAMES_EXTEND_FN dict_mapnames_extend_hook;
430
431 /*
432 * Workaround: define global variables here to control database cache sizes.
433 * When a database driver is dynamically loaded, global control variables
434 * cannot simply be owned by the loadable objects because that would result
435 * in build-time linker errors.
436 */
437 DEFINE_DICT_LMDB_MAP_SIZE;
438 DEFINE_DICT_DB_CACHE_SIZE;
439
440 /*
441 * Replace obscure code with a more readable expression.
442 */
443 #define NEED_DICT_OPEN_INIT() (dict_open_hash == 0)
444
445 /* dict_open_init - one-off initialization */
446
dict_open_init(void)447 static void dict_open_init(void)
448 {
449 const char *myname = "dict_open_init";
450 const DICT_OPEN_INFO *dp;
451
452 if (!NEED_DICT_OPEN_INIT())
453 msg_panic("%s: multiple initialization", myname);
454 dict_open_hash = htable_create(10);
455
456 for (dp = dict_open_info; dp->type; dp++)
457 htable_enter(dict_open_hash, dp->type, (void *) dp);
458 }
459
460 /* dict_open - open dictionary */
461
dict_open(const char * dict_spec,int open_flags,int dict_flags)462 DICT *dict_open(const char *dict_spec, int open_flags, int dict_flags)
463 {
464 char *saved_dict_spec = mystrdup(dict_spec);
465 char *dict_name;
466 DICT *dict;
467
468 if ((dict_name = split_at(saved_dict_spec, ':')) == 0)
469 msg_fatal("open dictionary: expecting \"type:name\" form instead of \"%s\"",
470 dict_spec);
471
472 dict = dict_open3(saved_dict_spec, dict_name, open_flags, dict_flags);
473 myfree(saved_dict_spec);
474 return (dict);
475 }
476
477 /* dict_open3 - open dictionary */
478
dict_open3(const char * dict_type,const char * dict_name,int open_flags,int dict_flags)479 DICT *dict_open3(const char *dict_type, const char *dict_name,
480 int open_flags, int dict_flags)
481 {
482 const char *myname = "dict_open";
483 const DICT_OPEN_INFO *dp;
484 DICT *dict;
485
486 if (*dict_type == 0 || *dict_name == 0)
487 msg_fatal("open dictionary: expecting \"type:name\" form instead of \"%s:%s\"",
488 dict_type, dict_name);
489 if (NEED_DICT_OPEN_INIT())
490 dict_open_init();
491 if ((dp = dict_open_lookup(dict_type)) == 0)
492 return (dict_surrogate(dict_type, dict_name, open_flags, dict_flags,
493 "unsupported dictionary type: %s", dict_type));
494 if ((dict = dp->dict_fn(dict_name, open_flags, dict_flags)) == 0)
495 return (dict_surrogate(dict_type, dict_name, open_flags, dict_flags,
496 "cannot open %s:%s: %m", dict_type, dict_name));
497 if (msg_verbose)
498 msg_info("%s: %s:%s", myname, dict_type, dict_name);
499 /* XXX The choice between wait-for-lock or no-wait is hard-coded. */
500 if (dict->flags & DICT_FLAG_OPEN_LOCK) {
501 if (dict->flags & DICT_FLAG_LOCK)
502 msg_panic("%s: attempt to open %s:%s with both \"open\" lock and \"access\" lock",
503 myname, dict_type, dict_name);
504 /* Multi-writer safe map: downgrade persistent lock to temporary. */
505 if (dict->flags & DICT_FLAG_MULTI_WRITER) {
506 dict->flags &= ~DICT_FLAG_OPEN_LOCK;
507 dict->flags |= DICT_FLAG_LOCK;
508 }
509 /* Multi-writer unsafe map: acquire exclusive lock or bust. */
510 else if (dict->lock(dict, MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT) < 0)
511 msg_fatal("%s:%s: unable to get exclusive lock: %m",
512 dict_type, dict_name);
513 }
514 /* Last step: insert proxy for UTF-8 syntax checks and casefolding. */
515 if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
516 && DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags))
517 dict = dict_utf8_activate(dict);
518 return (dict);
519 }
520
521 /* dict_open_register - register dictionary type */
522
dict_open_register(const DICT_OPEN_INFO * dp)523 void dict_open_register(const DICT_OPEN_INFO *dp)
524 {
525 const char *myname = "dict_open_register";
526
527 if (msg_verbose > 1)
528 msg_info("%s: %s", myname, dp->type);
529 if (NEED_DICT_OPEN_INIT())
530 dict_open_init();
531 if (htable_find(dict_open_hash, dp->type))
532 msg_panic("%s: dictionary type exists: %s", myname, dp->type);
533 (void) htable_enter(dict_open_hash, dp->type, (void *) dp);
534 }
535
536 /* dict_open_lookup - look up DICT_OPEN_INFO for dictionary type */
537
dict_open_lookup(const char * dict_type)538 const DICT_OPEN_INFO *dict_open_lookup(const char *dict_type)
539 {
540 const char myname[] = "dict_open_lookup";
541 const DICT_OPEN_INFO *dp;
542
543 if (msg_verbose > 1)
544 msg_info("%s: %s", myname, dict_type);
545 if ((dp = (DICT_OPEN_INFO *) htable_find(dict_open_hash, dict_type)) == 0
546 && dict_open_extend_hook != 0
547 && (dp = dict_open_extend_hook(dict_type)) != 0)
548 dict_open_register(dp);
549 return (dp);
550 }
551
552 /* dict_open_extend - register alternate dictionary search routine */
553
dict_open_extend(DICT_OPEN_EXTEND_FN new_cb)554 DICT_OPEN_EXTEND_FN dict_open_extend(DICT_OPEN_EXTEND_FN new_cb)
555 {
556 DICT_OPEN_EXTEND_FN old_cb;
557
558 old_cb = dict_open_extend_hook;
559 dict_open_extend_hook = new_cb;
560 return (old_cb);
561 }
562
563 /* dict_mapnames - return an ARGV of available map_names */
564
dict_mapnames()565 ARGV *dict_mapnames()
566 {
567 HTABLE_INFO **ht_info;
568 HTABLE_INFO **ht;
569 DICT_OPEN_INFO *dp;
570 ARGV *mapnames;
571
572 if (NEED_DICT_OPEN_INIT())
573 dict_open_init();
574 mapnames = argv_alloc(dict_open_hash->used + 1);
575 for (ht_info = ht = htable_list(dict_open_hash); *ht; ht++) {
576 dp = (DICT_OPEN_INFO *) ht[0]->value;
577 argv_add(mapnames, dp->type, ARGV_END);
578 }
579 if (dict_mapnames_extend_hook != 0)
580 (void) dict_mapnames_extend_hook(mapnames);
581 argv_qsort(mapnames, (ARGV_COMPAR_FN) 0);
582 /* In case some drivers have been loaded dynamically. */
583 argv_uniq(mapnames, (ARGV_COMPAR_FN) 0);
584 myfree((void *) ht_info);
585 argv_terminate(mapnames);
586 return mapnames;
587 }
588
589 /* dict_mapnames_extend - register alternate dictionary type list routine */
590
dict_mapnames_extend(DICT_MAPNAMES_EXTEND_FN new_cb)591 DICT_MAPNAMES_EXTEND_FN dict_mapnames_extend(DICT_MAPNAMES_EXTEND_FN new_cb)
592 {
593 DICT_MAPNAMES_EXTEND_FN old_cb;
594
595 old_cb = dict_mapnames_extend_hook;
596 dict_mapnames_extend_hook = new_cb;
597 return (old_cb);
598 }
599
600 /* dict_type_override - disguise a dictionary type */
601
dict_type_override(DICT * dict,const char * type)602 void dict_type_override(DICT *dict, const char *type)
603 {
604 myfree(dict->type);
605 dict->type = mystrdup(type);
606 }
607
608 #ifdef TEST
609
610 /*
611 * Proof-of-concept test program.
612 */
main(int argc,char ** argv)613 int main(int argc, char **argv)
614 {
615 dict_test(argc, argv);
616 return (0);
617 }
618
619 #endif
620