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