5
/* dictionary manager interface to hashed flat text files
7
/* #include <dict_thash.h>
9
/* DICT *dict_thash_open(path, open_flags, dict_flags)
15
/* dict_thash_open() opens the named flat text file, creates
16
/* an in-memory hash table, and makes it available via the
17
/* generic interface described in dict_open(3). The input
18
/* format is as with postmap(1).
20
/* Fatal errors: cannot open file, out of memory.
22
/* dict(3) generic dictionary manager
26
/* The Secure Mailer license must be distributed with this software.
29
/* IBM T.J. Watson Research
31
/* Yorktown Heights, NY 10598, USA
41
/* Utility library. */
48
#include <stringops.h>
49
#include <readlline.h>
51
#include <dict_thash.h>
53
/* Application-specific. */
56
DICT dict; /* generic members */
57
HTABLE *table; /* in-memory hash */
58
HTABLE_INFO **info; /* for iterator */
59
HTABLE_INFO **cursor; /* ditto */
62
#define STR vstring_str
64
/* dict_thash_lookup - find database entry */
66
static const char *dict_thash_lookup(DICT *dict, const char *name)
68
DICT_THASH *dict_thash = (DICT_THASH *) dict;
69
const char *result = 0;
74
* Optionally fold the key.
76
if (dict->flags & DICT_FLAG_FOLD_FIX) {
77
if (dict->fold_buf == 0)
78
dict->fold_buf = vstring_alloc(10);
79
vstring_strcpy(dict->fold_buf, name);
80
name = lowercase(vstring_str(dict->fold_buf));
86
result = htable_find(dict_thash->table, name);
91
/* dict_thash_sequence - traverse the dictionary */
93
static int dict_thash_sequence(DICT *dict, int function,
94
const char **key, const char **value)
96
const char *myname = "dict_thash_sequence";
97
DICT_THASH *dict_thash = (DICT_THASH *) dict;
100
* Determine and execute the seek function.
103
case DICT_SEQ_FUN_FIRST:
104
if (dict_thash->info == 0)
105
dict_thash->info = htable_list(dict_thash->table);
106
dict_thash->cursor = dict_thash->info;
108
case DICT_SEQ_FUN_NEXT:
109
if (dict_thash->cursor[0])
110
dict_thash->cursor += 1;
113
msg_panic("%s: invalid function: %d", myname, function);
117
* Return the entry under the cursor.
119
if (dict_thash->cursor[0]) {
120
*key = dict_thash->cursor[0]->key;
121
*value = dict_thash->cursor[0]->value;
128
/* dict_thash_close - disassociate from data base */
130
static void dict_thash_close(DICT *dict)
132
DICT_THASH *dict_thash = (DICT_THASH *) dict;
134
htable_free(dict_thash->table, myfree);
135
if (dict_thash->info)
136
myfree((char *) dict_thash->info);
138
vstring_free(dict->fold_buf);
142
/* dict_thash_open - open flat text data base */
144
DICT *dict_thash_open(const char *path, int open_flags, int dict_flags)
146
DICT_THASH *dict_thash;
151
VSTRING *line_buffer = vstring_alloc(100);
160
if (open_flags != O_RDONLY)
161
msg_fatal("%s:%s map requires O_RDONLY access mode",
162
DICT_TYPE_THASH, path);
165
* Create the in-memory table.
167
dict_thash = (DICT_THASH *)
168
dict_alloc(DICT_TYPE_THASH, path, sizeof(*dict_thash));
169
dict_thash->dict.lookup = dict_thash_lookup;
170
dict_thash->dict.sequence = dict_thash_sequence;
171
dict_thash->dict.close = dict_thash_close;
172
dict_thash->dict.flags = dict_flags | DICT_FLAG_DUP_WARN | DICT_FLAG_FIXED;
173
if (dict_flags & DICT_FLAG_FOLD_FIX)
174
dict_thash->dict.fold_buf = vstring_alloc(10);
175
dict_thash->info = 0;
178
* Read the flat text file into in-memory hash. Read the file again if it
179
* may have changed while we were reading.
181
for (before = time((time_t *) 0); /* see below */ ; before = after) {
182
if ((fp = vstream_fopen(path, open_flags, 0644)) == 0)
183
msg_fatal("open database %s: %m", path);
185
dict_thash->table = htable_create(13);
186
while (readlline(line_buffer, fp, &lineno)) {
189
* Split on the first whitespace character, then trim leading and
190
* trailing whitespace from key and value.
192
key = STR(line_buffer);
193
value = key + strcspn(key, " \t\r\n");
196
while (ISSPACE(*value))
198
trimblanks(key, 0)[0] = 0;
199
trimblanks(value, 0)[0] = 0;
202
* Enforce the "key whitespace value" format. Disallow missing
203
* keys or missing values.
205
if (*key == 0 || *value == 0) {
206
msg_warn("%s, line %d: expected format: key whitespace value"
207
" -- ignoring this line", path, lineno);
210
if (key[strlen(key) - 1] == ':')
211
msg_warn("%s, line %d: record is in \"key: value\" format;"
212
" is this an alias file?", path, lineno);
215
* Optionally fold the key.
217
if (dict_thash->dict.flags & DICT_FLAG_FOLD_FIX)
221
* Store the value under the key. Handle duplicates
224
if ((ht = htable_locate(dict_thash->table, key)) != 0) {
225
if (dict_thash->dict.flags & DICT_FLAG_DUP_IGNORE) {
227
} else if (dict_thash->dict.flags & DICT_FLAG_DUP_REPLACE) {
229
ht->value = mystrdup(value);
230
} else if (dict_thash->dict.flags & DICT_FLAG_DUP_WARN) {
231
msg_warn("%s, line %d: duplicate entry: \"%s\"",
234
msg_fatal("%s, line %d: duplicate entry: \"%s\"",
238
htable_enter(dict_thash->table, key, mystrdup(value));
243
* See if the source file is hot.
245
if (fstat(vstream_fileno(fp), &st) < 0)
246
msg_fatal("fstat %s: %m", path);
247
if (vstream_fclose(fp))
248
msg_fatal("read %s: %m", path);
249
after = time((time_t *) 0);
250
if (st.st_mtime < before - 1 || st.st_mtime > after)
254
* Yes, it is hot. Discard the result and read the file again.
256
htable_free(dict_thash->table, myfree);
258
msg_info("pausing to let file %s cool down", path);
261
vstring_free(line_buffer);
263
return (DICT_DEBUG (&dict_thash->dict));