Sync usage with man page.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / util / dict_open.c
blob2519137d49692d3247f385cccd269926da16ad72
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* dict_open 3
6 /* SUMMARY
7 /* low-level dictionary interface
8 /* SYNOPSIS
9 /* #include <dict.h>
11 /* DICT *dict_open(dict_spec, open_flags, dict_flags)
12 /* const char *dict_spec;
13 /* int open_flags;
14 /* int dict_flags;
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;
22 /* void dict_put(dict, key, value)
23 /* DICT *dict;
24 /* const char *key;
25 /* const char *value;
27 /* const char *dict_get(dict, key)
28 /* DICT *dict;
29 /* const char *key;
31 /* int dict_del(dict, key)
32 /* DICT *dict;
33 /* const char *key;
35 /* void dict_seq(dict, func, key, value)
36 /* DICT *dict;
37 /* int func;
38 /* const char **key;
39 /* const char **value;
41 /* void dict_close(dict)
42 /* DICT *dict;
44 /* dict_open_register(type, open)
45 /* char *type;
46 /* DICT *(*open) (const char *, int, int);
48 /* ARGV *dict_mapnames()
49 /* DESCRIPTION
50 /* This module implements a low-level interface to multiple
51 /* physical dictionary types.
53 /* dict_open() takes a type:name pair that specifies a dictionary type
54 /* and dictionary name, opens the dictionary, and returns a dictionary
55 /* handle. The \fIopen_flags\fR arguments are as in open(2). The
56 /* \fIdict_flags\fR are the bit-wise OR of zero or more of the following:
57 /* .IP DICT_FLAG_DUP_WARN
58 /* Warn about duplicate keys, if the underlying database does not
59 /* support duplicate keys. The default is to terminate with a fatal
60 /* error.
61 /* .IP DICT_FLAG_DUP_IGNORE
62 /* Ignore duplicate keys if the underlying database does not
63 /* support duplicate keys. The default is to terminate with a fatal
64 /* error.
65 /* .IP DICT_FLAG_DUP_REPLACE
66 /* Replace duplicate keys if the underlying database supports such
67 /* an operation. The default is to terminate with a fatal error.
68 /* .IP DICT_FLAG_TRY0NULL
69 /* With maps where this is appropriate, append no null byte to
70 /* keys and values.
71 /* When neither DICT_FLAG_TRY0NULL nor DICT_FLAG_TRY1NULL are
72 /* specified, the software guesses what format to use for reading;
73 /* and in the absence of definite information, a system-dependent
74 /* default is chosen for writing.
75 /* .IP DICT_FLAG_TRY1NULL
76 /* With maps where this is appropriate, append one null byte to
77 /* keys and values.
78 /* When neither DICT_FLAG_TRY0NULL nor DICT_FLAG_TRY1NULL are
79 /* specified, the software guesses what format to use for reading;
80 /* and in the absence of definite information, a system-dependent
81 /* default is chosen for writing.
82 /* .IP DICT_FLAG_LOCK
83 /* With maps where this is appropriate, acquire an exclusive lock
84 /* before writing, and acquire a shared lock before reading.
85 /* .IP DICT_FLAG_FOLD_FIX
86 /* With databases whose lookup fields are fixed-case strings,
87 /* fold the search key to lower case before accessing the
88 /* database. This includes hash:, cdb:, dbm:. nis:, ldap:,
89 /* *sql.
90 /* .IP DICT_FLAG_FOLD_MUL
91 /* With databases where one lookup field can match both upper
92 /* and lower case, fold the search key to lower case before
93 /* accessing the database. This includes regexp: and pcre:
94 /* .IP DICT_FLAG_FOLD_ANY
95 /* Short-hand for (DICT_FLAG_FOLD_FIX | DICT_FLAG_FOLD_MUL).
96 /* .IP DICT_FLAG_SYNC_UPDATE
97 /* With file-based maps, flush I/O buffers to file after each update.
98 /* Thus feature is not supported with some file-based dictionaries.
99 /* .IP DICT_FLAG_NO_REGSUB
100 /* Disallow regular expression substitution from left-hand side data
101 /* into the right-hand side.
102 /* .IP DICT_FLAG_NO_PROXY
103 /* Disallow access through the \fBproxymap\fR service.
104 /* .IP DICT_FLAG_NO_UNAUTH
105 /* Disallow network lookup mechanisms that lack any form of
106 /* authentication (example: tcp_table; even NIS can be secured
107 /* to some extent by requiring that the server binds to a
108 /* privileged port).
109 /* .IP DICT_FLAG_PARANOID
110 /* A combination of all the paranoia flags: DICT_FLAG_NO_REGSUB,
111 /* DICT_FLAG_NO_PROXY and DICT_FLAG_NO_UNAUTH.
112 /* .PP
113 /* Specify DICT_FLAG_NONE for no special processing.
115 /* The dictionary types are as follows:
116 /* .IP environ
117 /* The process environment array. The \fIdict_name\fR argument is ignored.
118 /* .IP dbm
119 /* DBM file.
120 /* .IP hash
121 /* Berkeley DB file in hash format.
122 /* .IP btree
123 /* Berkeley DB file in btree format.
124 /* .IP nis
125 /* NIS map. Only read access is supported.
126 /* .IP nisplus
127 /* NIS+ map. Only read access is supported.
128 /* .IP netinfo
129 /* NetInfo table. Only read access is supported.
130 /* .IP ldap
131 /* LDAP ("light-weight" directory access protocol) database access.
132 /* .IP pcre
133 /* PERL-compatible regular expressions.
134 /* .IP regexp
135 /* POSIX-compatible regular expressions.
136 /* .PP
137 /* dict_open3() takes separate arguments for dictionary type and
138 /* name, but otherwise performs the same functions as dict_open().
140 /* dict_get() retrieves the value stored in the named dictionary
141 /* under the given key. A null pointer means the value was not found.
142 /* As with dict_lookup(), the result is owned by the lookup table
143 /* implementation. Make a copy if the result is to be modified,
144 /* or if the result is to survive multiple table lookups.
146 /* dict_put() stores the specified key and value into the named
147 /* dictionary.
149 /* dict_del() removes a dictionary entry, and returns non-zero
150 /* in case of success.
152 /* dict_seq() iterates over all members in the named dictionary.
153 /* func is define DICT_SEQ_FUN_FIRST (select first member) or
154 /* DICT_SEQ_FUN_NEXT (select next member). A null result means
155 /* there is more.
157 /* dict_close() closes the specified dictionary and cleans up the
158 /* associated data structures.
160 /* dict_open_register() adds support for a new dictionary type.
162 /* dict_mapnames() returns a sorted list with the names of all available
163 /* dictionary types.
164 /* DIAGNOSTICS
165 /* Fatal error: open error, unsupported dictionary type, attempt to
166 /* update non-writable dictionary.
167 /* LICENSE
168 /* .ad
169 /* .fi
170 /* The Secure Mailer license must be distributed with this software.
171 /* AUTHOR(S)
172 /* Wietse Venema
173 /* IBM T.J. Watson Research
174 /* P.O. Box 704
175 /* Yorktown Heights, NY 10598, USA
176 /*--*/
178 /* System library. */
180 #include <sys_defs.h>
181 #include <string.h>
182 #include <stdlib.h>
184 #ifdef STRCASECMP_IN_STRINGS_H
185 #include <strings.h>
186 #endif
188 /* Utility library. */
190 #include <argv.h>
191 #include <mymalloc.h>
192 #include <msg.h>
193 #include <dict.h>
194 #include <dict_cdb.h>
195 #include <dict_env.h>
196 #include <dict_unix.h>
197 #include <dict_tcp.h>
198 #include <dict_sdbm.h>
199 #include <dict_dbm.h>
200 #include <dict_db.h>
201 #include <dict_nis.h>
202 #include <dict_nisplus.h>
203 #include <dict_ni.h>
204 #include <dict_pcre.h>
205 #include <dict_regexp.h>
206 #include <dict_static.h>
207 #include <dict_cidr.h>
208 #include <stringops.h>
209 #include <split_at.h>
210 #include <htable.h>
213 * lookup table for available map types.
215 typedef struct {
216 char *type;
217 struct DICT *(*open) (const char *, int, int);
218 } DICT_OPEN_INFO;
220 static const DICT_OPEN_INFO dict_open_info[] = {
221 #ifdef HAS_CDB
222 DICT_TYPE_CDB, dict_cdb_open,
223 #endif
224 DICT_TYPE_ENVIRON, dict_env_open,
225 DICT_TYPE_UNIX, dict_unix_open,
226 #ifdef SNAPSHOT
227 DICT_TYPE_TCP, dict_tcp_open,
228 #endif
229 #ifdef HAS_SDBM
230 DICT_TYPE_SDBM, dict_sdbm_open,
231 #endif
232 #ifdef HAS_DBM
233 DICT_TYPE_DBM, dict_dbm_open,
234 #endif
235 #ifdef HAS_DB
236 DICT_TYPE_HASH, dict_hash_open,
237 DICT_TYPE_BTREE, dict_btree_open,
238 #endif
239 #ifdef HAS_NIS
240 DICT_TYPE_NIS, dict_nis_open,
241 #endif
242 #ifdef HAS_NISPLUS
243 DICT_TYPE_NISPLUS, dict_nisplus_open,
244 #endif
245 #ifdef HAS_NETINFO
246 DICT_TYPE_NETINFO, dict_ni_open,
247 #endif
248 #ifdef HAS_PCRE
249 DICT_TYPE_PCRE, dict_pcre_open,
250 #endif
251 #ifdef HAS_POSIX_REGEXP
252 DICT_TYPE_REGEXP, dict_regexp_open,
253 #endif
254 DICT_TYPE_STATIC, dict_static_open,
255 DICT_TYPE_CIDR, dict_cidr_open,
259 static HTABLE *dict_open_hash;
261 /* dict_open_init - one-off initialization */
263 static void dict_open_init(void)
265 const char *myname = "dict_open_init";
266 const DICT_OPEN_INFO *dp;
268 if (dict_open_hash != 0)
269 msg_panic("%s: multiple initialization", myname);
270 dict_open_hash = htable_create(10);
272 for (dp = dict_open_info; dp->type; dp++)
273 htable_enter(dict_open_hash, dp->type, (char *) dp);
276 /* dict_open - open dictionary */
278 DICT *dict_open(const char *dict_spec, int open_flags, int dict_flags)
280 char *saved_dict_spec = mystrdup(dict_spec);
281 char *dict_name;
282 DICT *dict;
284 if ((dict_name = split_at(saved_dict_spec, ':')) == 0)
285 msg_fatal("open dictionary: expecting \"type:name\" form instead of \"%s\"",
286 dict_spec);
288 dict = dict_open3(saved_dict_spec, dict_name, open_flags, dict_flags);
289 myfree(saved_dict_spec);
290 return (dict);
294 /* dict_open3 - open dictionary */
296 DICT *dict_open3(const char *dict_type, const char *dict_name,
297 int open_flags, int dict_flags)
299 const char *myname = "dict_open";
300 DICT_OPEN_INFO *dp;
301 DICT *dict;
303 if (*dict_type == 0 || *dict_name == 0)
304 msg_fatal("open dictionary: expecting \"type:name\" form instead of \"%s:%s\"",
305 dict_type, dict_name);
306 if (dict_open_hash == 0)
307 dict_open_init();
308 if ((dp = (DICT_OPEN_INFO *) htable_find(dict_open_hash, dict_type)) == 0)
309 msg_fatal("unsupported dictionary type: %s", dict_type);
310 if ((dict = dp->open(dict_name, open_flags, dict_flags)) == 0)
311 msg_fatal("opening %s:%s %m", dict_type, dict_name);
312 if (msg_verbose)
313 msg_info("%s: %s:%s", myname, dict_type, dict_name);
314 return (dict);
317 /* dict_open_register - register dictionary type */
319 void dict_open_register(const char *type,
320 DICT *(*open) (const char *, int, int))
322 const char *myname = "dict_open_register";
323 DICT_OPEN_INFO *dp;
325 if (dict_open_hash == 0)
326 dict_open_init();
327 if (htable_find(dict_open_hash, type))
328 msg_panic("%s: dictionary type exists: %s", myname, type);
329 dp = (DICT_OPEN_INFO *) mymalloc(sizeof(*dp));
330 dp->type = mystrdup(type);
331 dp->open = open;
332 htable_enter(dict_open_hash, dp->type, (char *) dp);
335 /* dict_sort_alpha_cpp - qsort() callback */
337 static int dict_sort_alpha_cpp(const void *a, const void *b)
339 return (strcmp(((char **) a)[0], ((char **) b)[0]));
342 /* dict_mapnames - return an ARGV of available map_names */
344 ARGV *dict_mapnames()
346 HTABLE_INFO **ht_info;
347 HTABLE_INFO **ht;
348 DICT_OPEN_INFO *dp;
349 ARGV *mapnames;
351 if (dict_open_hash == 0)
352 dict_open_init();
353 mapnames = argv_alloc(dict_open_hash->used + 1);
354 for (ht_info = ht = htable_list(dict_open_hash); *ht; ht++) {
355 dp = (DICT_OPEN_INFO *) ht[0]->value;
356 argv_add(mapnames, dp->type, ARGV_END);
358 qsort((void *) mapnames->argv, mapnames->argc, sizeof(mapnames->argv[0]),
359 dict_sort_alpha_cpp);
360 myfree((char *) ht_info);
361 argv_terminate(mapnames);
362 return mapnames;
365 #ifdef TEST
368 * Proof-of-concept test program. Create, update or read a database. When
369 * the input is a name=value pair, the database is updated, otherwise the
370 * program assumes that the input specifies a lookup key and prints the
371 * corresponding value.
374 /* System library. */
376 #include <stdlib.h>
377 #include <fcntl.h>
378 #include <unistd.h>
379 #include <signal.h>
381 /* Utility library. */
383 #include "vstring.h"
384 #include "vstream.h"
385 #include "msg_vstream.h"
386 #include "vstring_vstream.h"
388 static NORETURN usage(char *myname)
390 msg_fatal("usage: %s type:file read|write|create [fold]", myname);
393 int main(int argc, char **argv)
395 VSTRING *keybuf = vstring_alloc(1);
396 VSTRING *inbuf = vstring_alloc(1);
397 DICT *dict;
398 char *dict_name;
399 int open_flags;
400 char *bufp;
401 char *cmd;
402 const char *key;
403 const char *value;
404 int ch;
405 int dict_flags = DICT_FLAG_LOCK | DICT_FLAG_DUP_REPLACE;
407 signal(SIGPIPE, SIG_IGN);
409 msg_vstream_init(argv[0], VSTREAM_ERR);
410 while ((ch = GETOPT(argc, argv, "v")) > 0) {
411 switch (ch) {
412 default:
413 usage(argv[0]);
414 case 'v':
415 msg_verbose++;
416 break;
419 optind = OPTIND;
420 if (argc - optind < 2 || argc - optind > 3)
421 usage(argv[0]);
422 if (strcasecmp(argv[optind + 1], "create") == 0)
423 open_flags = O_CREAT | O_RDWR | O_TRUNC;
424 else if (strcasecmp(argv[optind + 1], "write") == 0)
425 open_flags = O_RDWR;
426 else if (strcasecmp(argv[optind + 1], "read") == 0)
427 open_flags = O_RDONLY;
428 else
429 msg_fatal("unknown access mode: %s", argv[2]);
430 if (argv[optind + 2] && strcasecmp(argv[optind + 2], "fold") == 0)
431 dict_flags |= DICT_FLAG_FOLD_ANY;
432 dict_name = argv[optind];
433 dict = dict_open(dict_name, open_flags, dict_flags);
434 dict_register(dict_name, dict);
435 while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) {
436 bufp = vstring_str(inbuf);
437 if (!isatty(0)) {
438 vstream_printf("> %s\n", bufp);
439 vstream_fflush(VSTREAM_OUT);
441 if (*bufp == '#')
442 continue;
443 if ((cmd = mystrtok(&bufp, " ")) == 0) {
444 vstream_printf("usage: del key|get key|put key=value|first|next\n");
445 vstream_fflush(VSTREAM_OUT);
446 continue;
448 if (dict_changed_name())
449 msg_warn("dictionary has changed");
450 key = *bufp ? vstring_str(unescape(keybuf, mystrtok(&bufp, " ="))) : 0;
451 value = mystrtok(&bufp, " =");
452 if (strcmp(cmd, "del") == 0 && key && !value) {
453 if (dict_del(dict, key))
454 vstream_printf("%s: not found\n", key);
455 else
456 vstream_printf("%s: deleted\n", key);
457 } else if (strcmp(cmd, "get") == 0 && key && !value) {
458 if ((value = dict_get(dict, key)) == 0) {
459 vstream_printf("%s: %s\n", key,
460 dict_errno == DICT_ERR_RETRY ?
461 "soft error" : "not found");
462 } else {
463 vstream_printf("%s=%s\n", key, value);
465 } else if (strcmp(cmd, "put") == 0 && key && value) {
466 dict_put(dict, key, value);
467 vstream_printf("%s=%s\n", key, value);
468 } else if (strcmp(cmd, "first") == 0 && !key && !value) {
469 if (dict_seq(dict, DICT_SEQ_FUN_FIRST, &key, &value) == 0)
470 vstream_printf("%s=%s\n", key, value);
471 else
472 vstream_printf("%s\n",
473 dict_errno == DICT_ERR_RETRY ?
474 "soft error" : "not found");
475 } else if (strcmp(cmd, "next") == 0 && !key && !value) {
476 if (dict_seq(dict, DICT_SEQ_FUN_NEXT, &key, &value) == 0)
477 vstream_printf("%s=%s\n", key, value);
478 else
479 vstream_printf("%s\n",
480 dict_errno == DICT_ERR_RETRY ?
481 "soft error" : "not found");
482 } else {
483 vstream_printf("usage: del key|get key|put key=value|first|next\n");
485 vstream_fflush(VSTREAM_OUT);
487 vstring_free(keybuf);
488 vstring_free(inbuf);
489 dict_close(dict);
490 return (0);
493 #endif