xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/dict_thash.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: dict_thash.c,v 1.1.1.1 2011/03/02 19:32:42 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	dict_thash 3
6 /* SUMMARY
7 /*	dictionary manager interface to hashed flat text files
8 /* SYNOPSIS
9 /*	#include <dict_thash.h>
10 /*
11 /*	DICT	*dict_thash_open(path, open_flags, dict_flags)
12 /*	const char *name;
13 /*	const char *path;
14 /*	int	open_flags;
15 /*	int	dict_flags;
16 /* DESCRIPTION
17 /*	dict_thash_open() opens the named flat text file, creates
18 /*	an in-memory hash table, and makes it available via the
19 /*	generic interface described in dict_open(3). The input
20 /*	format is as with postmap(1).
21 /* DIAGNOSTICS
22 /*	Fatal errors: cannot open file, out of memory.
23 /* SEE ALSO
24 /*	dict(3) generic dictionary manager
25 /* LICENSE
26 /* .ad
27 /* .fi
28 /*	The Secure Mailer license must be distributed with this software.
29 /* AUTHOR(S)
30 /*	Wietse Venema
31 /*	IBM T.J. Watson Research
32 /*	P.O. Box 704
33 /*	Yorktown Heights, NY 10598, USA
34 /*--*/
35 
36 /* System library. */
37 
38 #include <sys_defs.h>
39 #include <sys/stat.h>
40 #include <ctype.h>
41 #include <string.h>
42 
43 /* Utility library. */
44 
45 #include <msg.h>
46 #include <mymalloc.h>
47 #include <htable.h>
48 #include <iostuff.h>
49 #include <vstring.h>
50 #include <stringops.h>
51 #include <readlline.h>
52 #include <dict.h>
53 #include <dict_thash.h>
54 
55 /* Application-specific. */
56 
57 typedef struct {
58     DICT    dict;			/* generic members */
59     HTABLE *table;			/* in-memory hash */
60     HTABLE_INFO **info;			/* for iterator */
61     HTABLE_INFO **cursor;		/* ditto */
62 }       DICT_THASH;
63 
64 #define STR	vstring_str
65 
66 /* dict_thash_lookup - find database entry */
67 
68 static const char *dict_thash_lookup(DICT *dict, const char *name)
69 {
70     DICT_THASH *dict_thash = (DICT_THASH *) dict;
71     const char *result = 0;
72 
73     dict_errno = 0;
74 
75     /*
76      * Optionally fold the key.
77      */
78     if (dict->flags & DICT_FLAG_FOLD_FIX) {
79 	if (dict->fold_buf == 0)
80 	    dict->fold_buf = vstring_alloc(10);
81 	vstring_strcpy(dict->fold_buf, name);
82 	name = lowercase(vstring_str(dict->fold_buf));
83     }
84 
85     /*
86      * Look up the value.
87      */
88     result = htable_find(dict_thash->table, name);
89 
90     return (result);
91 }
92 
93 /* dict_thash_sequence - traverse the dictionary */
94 
95 static int dict_thash_sequence(DICT *dict, int function,
96 			               const char **key, const char **value)
97 {
98     const char *myname = "dict_thash_sequence";
99     DICT_THASH *dict_thash = (DICT_THASH *) dict;
100 
101     /*
102      * Determine and execute the seek function.
103      */
104     switch (function) {
105     case DICT_SEQ_FUN_FIRST:
106 	if (dict_thash->info == 0)
107 	    dict_thash->info = htable_list(dict_thash->table);
108 	dict_thash->cursor = dict_thash->info;
109 	break;
110     case DICT_SEQ_FUN_NEXT:
111 	if (dict_thash->cursor[0])
112 	    dict_thash->cursor += 1;
113 	break;
114     default:
115 	msg_panic("%s: invalid function: %d", myname, function);
116     }
117 
118     /*
119      * Return the entry under the cursor.
120      */
121     if (dict_thash->cursor[0]) {
122 	*key = dict_thash->cursor[0]->key;
123 	*value = dict_thash->cursor[0]->value;
124 	return (0);
125     } else {
126 	return (1);
127     }
128 }
129 
130 /* dict_thash_close - disassociate from data base */
131 
132 static void dict_thash_close(DICT *dict)
133 {
134     DICT_THASH *dict_thash = (DICT_THASH *) dict;
135 
136     htable_free(dict_thash->table, myfree);
137     if (dict_thash->info)
138 	myfree((char *) dict_thash->info);
139     if (dict->fold_buf)
140 	vstring_free(dict->fold_buf);
141     dict_free(dict);
142 }
143 
144 /* dict_thash_open - open flat text data base */
145 
146 DICT   *dict_thash_open(const char *path, int open_flags, int dict_flags)
147 {
148     DICT_THASH *dict_thash;
149     VSTREAM *fp;
150     struct stat st;
151     time_t  before;
152     time_t  after;
153     VSTRING *line_buffer = vstring_alloc(100);
154     int     lineno;
155     char   *key;
156     char   *value;
157     HTABLE_INFO *ht;
158 
159     /*
160      * Sanity checks.
161      */
162     if (open_flags != O_RDONLY)
163 	msg_fatal("%s:%s map requires O_RDONLY access mode",
164 		  DICT_TYPE_THASH, path);
165 
166     /*
167      * Create the in-memory table.
168      */
169     dict_thash = (DICT_THASH *)
170 	dict_alloc(DICT_TYPE_THASH, path, sizeof(*dict_thash));
171     dict_thash->dict.lookup = dict_thash_lookup;
172     dict_thash->dict.sequence = dict_thash_sequence;
173     dict_thash->dict.close = dict_thash_close;
174     dict_thash->dict.flags = dict_flags | DICT_FLAG_DUP_WARN | DICT_FLAG_FIXED;
175     if (dict_flags & DICT_FLAG_FOLD_FIX)
176 	dict_thash->dict.fold_buf = vstring_alloc(10);
177     dict_thash->info = 0;
178 
179     /*
180      * Read the flat text file into in-memory hash. Read the file again if it
181      * may have changed while we were reading.
182      */
183     for (before = time((time_t *) 0); /* see below */ ; before = after) {
184 	if ((fp = vstream_fopen(path, open_flags, 0644)) == 0)
185 	    msg_fatal("open database %s: %m", path);
186 	lineno = 0;
187 	dict_thash->table = htable_create(13);
188 	while (readlline(line_buffer, fp, &lineno)) {
189 
190 	    /*
191 	     * Split on the first whitespace character, then trim leading and
192 	     * trailing whitespace from key and value.
193 	     */
194 	    key = STR(line_buffer);
195 	    value = key + strcspn(key, " \t\r\n");
196 	    if (*value)
197 		*value++ = 0;
198 	    while (ISSPACE(*value))
199 		value++;
200 	    trimblanks(key, 0)[0] = 0;
201 	    trimblanks(value, 0)[0] = 0;
202 
203 	    /*
204 	     * Enforce the "key whitespace value" format. Disallow missing
205 	     * keys or missing values.
206 	     */
207 	    if (*key == 0 || *value == 0) {
208 		msg_warn("%s, line %d: expected format: key whitespace value"
209 			 " -- ignoring this line", path, lineno);
210 		continue;
211 	    }
212 	    if (key[strlen(key) - 1] == ':')
213 		msg_warn("%s, line %d: record is in \"key: value\" format;"
214 			 " is this an alias file?", path, lineno);
215 
216 	    /*
217 	     * Optionally fold the key.
218 	     */
219 	    if (dict_thash->dict.flags & DICT_FLAG_FOLD_FIX)
220 		lowercase(key);
221 
222 	    /*
223 	     * Store the value under the key. Handle duplicates
224 	     * appropriately.
225 	     */
226 	    if ((ht = htable_locate(dict_thash->table, key)) != 0) {
227 		if (dict_thash->dict.flags & DICT_FLAG_DUP_IGNORE) {
228 		     /* void */ ;
229 		} else if (dict_thash->dict.flags & DICT_FLAG_DUP_REPLACE) {
230 		    myfree(ht->value);
231 		    ht->value = mystrdup(value);
232 		} else if (dict_thash->dict.flags & DICT_FLAG_DUP_WARN) {
233 		    msg_warn("%s, line %d: duplicate entry: \"%s\"",
234 			     path, lineno, key);
235 		} else {
236 		    msg_fatal("%s, line %d: duplicate entry: \"%s\"",
237 			      path, lineno, key);
238 		}
239 	    } else {
240 		htable_enter(dict_thash->table, key, mystrdup(value));
241 	    }
242 	}
243 
244 	/*
245 	 * See if the source file is hot.
246 	 */
247 	if (fstat(vstream_fileno(fp), &st) < 0)
248 	    msg_fatal("fstat %s: %m", path);
249 	if (vstream_fclose(fp))
250 	    msg_fatal("read %s: %m", path);
251 	after = time((time_t *) 0);
252 	if (st.st_mtime < before - 1 || st.st_mtime > after)
253 	    break;
254 
255 	/*
256 	 * Yes, it is hot. Discard the result and read the file again.
257 	 */
258 	htable_free(dict_thash->table, myfree);
259 	if (msg_verbose > 1)
260 	    msg_info("pausing to let file %s cool down", path);
261 	doze(300000);
262     }
263     vstring_free(line_buffer);
264 
265     return (DICT_DEBUG (&dict_thash->dict));
266 }
267