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