#include <sys_defs.h>
#include <sys/stat.h>
#include <ctype.h>
#include <string.h>
#include <msg.h>
#include <mymalloc.h>
#include <htable.h>
#include <iostuff.h>
#include <vstring.h>
#include <stringops.h>
#include <readlline.h>
#include <dict.h>
#include <dict_thash.h>
#include <warn_stat.h>
typedef struct {
DICT dict;
HTABLE *table;
HTABLE_INFO **info;
HTABLE_INFO **cursor;
} DICT_THASH;
#define STR vstring_str
static const char *dict_thash_lookup(DICT *dict, const char *name)
{
DICT_THASH *dict_thash = (DICT_THASH *) dict;
const char *result = 0;
if (dict->flags & DICT_FLAG_FOLD_FIX) {
if (dict->fold_buf == 0)
dict->fold_buf = vstring_alloc(10);
vstring_strcpy(dict->fold_buf, name);
name = lowercase(vstring_str(dict->fold_buf));
}
result = htable_find(dict_thash->table, name);
DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, result);
}
static int dict_thash_sequence(DICT *dict, int function,
const char **key, const char **value)
{
const char *myname = "dict_thash_sequence";
DICT_THASH *dict_thash = (DICT_THASH *) dict;
switch (function) {
case DICT_SEQ_FUN_FIRST:
if (dict_thash->info == 0)
dict_thash->info = htable_list(dict_thash->table);
dict_thash->cursor = dict_thash->info;
break;
case DICT_SEQ_FUN_NEXT:
if (dict_thash->cursor[0])
dict_thash->cursor += 1;
break;
default:
msg_panic("%s: invalid function: %d", myname, function);
}
if (dict_thash->cursor[0]) {
*key = dict_thash->cursor[0]->key;
*value = dict_thash->cursor[0]->value;
DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS);
} else {
*key = 0;
*value = 0;
DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL);
}
}
static void dict_thash_close(DICT *dict)
{
DICT_THASH *dict_thash = (DICT_THASH *) dict;
htable_free(dict_thash->table, myfree);
if (dict_thash->info)
myfree((char *) dict_thash->info);
if (dict->fold_buf)
vstring_free(dict->fold_buf);
dict_free(dict);
}
DICT *dict_thash_open(const char *path, int open_flags, int dict_flags)
{
DICT_THASH *dict_thash;
VSTREAM *fp;
struct stat st;
time_t before;
time_t after;
VSTRING *line_buffer = 0;
int lineno;
char *key;
char *value;
HTABLE *table;
HTABLE_INFO *ht;
if (open_flags != O_RDONLY)
return (dict_surrogate(DICT_TYPE_THASH, path, open_flags, dict_flags,
"%s:%s map requires O_RDONLY access mode",
DICT_TYPE_THASH, path));
for (before = time((time_t *) 0); ; before = after) {
if ((fp = vstream_fopen(path, open_flags, 0644)) == 0) {
return (dict_surrogate(DICT_TYPE_THASH, path, open_flags, dict_flags,
"open database %s: %m", path));
}
if (line_buffer == 0)
line_buffer = vstring_alloc(100);
lineno = 0;
table = htable_create(13);
while (readlline(line_buffer, fp, &lineno)) {
key = STR(line_buffer);
value = key + strcspn(key, " \t\r\n");
if (*value)
*value++ = 0;
while (ISSPACE(*value))
value++;
trimblanks(key, 0)[0] = 0;
trimblanks(value, 0)[0] = 0;
if (*key == 0 || *value == 0) {
msg_warn("%s, line %d: expected format: key whitespace value"
" -- ignoring this line", path, lineno);
continue;
}
if (key[strlen(key) - 1] == ':')
msg_warn("%s, line %d: record is in \"key: value\" format;"
" is this an alias file?", path, lineno);
if (dict_flags & DICT_FLAG_FOLD_FIX)
lowercase(key);
if ((ht = htable_locate(table, key)) != 0) {
if (dict_flags & DICT_FLAG_DUP_IGNORE) {
;
} else if (dict_flags & DICT_FLAG_DUP_REPLACE) {
myfree(ht->value);
ht->value = mystrdup(value);
} else if (dict_flags & DICT_FLAG_DUP_WARN) {
msg_warn("%s, line %d: duplicate entry: \"%s\"",
path, lineno, key);
} else {
msg_fatal("%s, line %d: duplicate entry: \"%s\"",
path, lineno, key);
}
} else {
htable_enter(table, key, mystrdup(value));
}
}
if (fstat(vstream_fileno(fp), &st) < 0)
msg_fatal("fstat %s: %m", path);
if (vstream_fclose(fp))
msg_fatal("read %s: %m", path);
after = time((time_t *) 0);
if (st.st_mtime < before - 1 || st.st_mtime > after)
break;
htable_free(table, myfree);
if (msg_verbose > 1)
msg_info("pausing to let file %s cool down", path);
doze(300000);
}
vstring_free(line_buffer);
dict_thash = (DICT_THASH *)
dict_alloc(DICT_TYPE_THASH, path, sizeof(*dict_thash));
dict_thash->dict.lookup = dict_thash_lookup;
dict_thash->dict.sequence = dict_thash_sequence;
dict_thash->dict.close = dict_thash_close;
dict_thash->dict.flags = dict_flags | DICT_FLAG_DUP_WARN | DICT_FLAG_FIXED;
if (dict_flags & DICT_FLAG_FOLD_FIX)
dict_thash->dict.fold_buf = vstring_alloc(10);
dict_thash->info = 0;
dict_thash->table = table;
dict_thash->dict.owner.uid = st.st_uid;
dict_thash->dict.owner.status = (st.st_uid != 0);
return (DICT_DEBUG (&dict_thash->dict));
}