#include <sys_defs.h>
#include <sys/stat.h>
#include <ctype.h>
#include <string.h>
#include <msg.h>
#include <iostuff.h>
#include <vstring.h>
#include <stringops.h>
#include <readlline.h>
#include <dict.h>
#include <dict_ht.h>
#include <dict_thash.h>
#define STR vstring_str
#define LEN VSTRING_LEN
DICT *dict_thash_open(const char *path, int open_flags, int dict_flags)
{
DICT *dict;
VSTREAM *fp = 0;
struct stat st;
time_t before;
time_t after;
VSTRING *line_buffer = 0;
int lineno;
int last_line;
char *key;
char *value;
#define DICT_THASH_OPEN_RETURN(d) do { \
DICT *__d = (d); \
if (fp != 0) \
vstream_fclose(fp); \
if (line_buffer != 0) \
vstring_free(line_buffer); \
return (__d); \
} while (0)
if (open_flags != O_RDONLY)
DICT_THASH_OPEN_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) {
DICT_THASH_OPEN_RETURN(dict_surrogate(DICT_TYPE_THASH, path,
open_flags, dict_flags,
"open database %s: %m", path));
}
dict = dict_open3(DICT_TYPE_HT, path, open_flags, dict_flags);
dict_type_override(dict, DICT_TYPE_THASH);
if (line_buffer == 0)
line_buffer = vstring_alloc(100);
last_line = 0;
while (readllines(line_buffer, fp, &last_line, &lineno)) {
int in_quotes = 0;
if ((dict->flags & DICT_FLAG_UTF8_ACTIVE)
&& allascii(STR(line_buffer)) == 0
&& valid_utf8_string(STR(line_buffer), LEN(line_buffer)) == 0) {
msg_warn("%s, line %d: non-UTF-8 input \"%s\""
" -- ignoring this line",
VSTREAM_PATH(fp), lineno, STR(line_buffer));
continue;
}
for (value = STR(line_buffer); *value; value++) {
if (*value == '\\') {
if (*++value == 0)
break;
} else if (ISSPACE(*value)) {
if (!in_quotes)
break;
} else if (*value == '"') {
in_quotes = !in_quotes;
}
}
if (in_quotes) {
msg_warn("%s, line %d: unbalanced '\"' in '%s'"
" -- ignoring this line",
VSTREAM_PATH(fp), lineno, STR(line_buffer));
continue;
}
if (*value)
*value++ = 0;
while (ISSPACE(*value))
value++;
trimblanks(value, 0)[0] = 0;
key = STR(line_buffer);
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->lookup(dict, key) != 0) {
if (dict_flags & DICT_FLAG_DUP_IGNORE) {
;
} else if (dict_flags & DICT_FLAG_DUP_REPLACE) {
dict->update(dict, key, value);
} else if (dict_flags & DICT_FLAG_DUP_WARN) {
msg_warn("%s, line %d: duplicate entry: \"%s\"",
path, lineno, key);
} else {
dict->close(dict);
DICT_THASH_OPEN_RETURN(dict_surrogate(DICT_TYPE_THASH, path,
open_flags, dict_flags,
"%s, line %d: duplicate entry: \"%s\"",
path, lineno, key));
}
} else {
dict->update(dict, key, value);
}
}
if (fstat(vstream_fileno(fp), &st) < 0)
msg_fatal("fstat %s: %m", path);
if (vstream_fclose(fp))
msg_fatal("read %s: %m", path);
fp = 0;
after = time((time_t *) 0);
if (st.st_mtime < before - 1 || st.st_mtime > after)
break;
dict->close(dict);
if (msg_verbose > 1)
msg_info("pausing to let file %s cool down", path);
doze(300000);
}
dict->owner.uid = st.st_uid;
dict->owner.status = (st.st_uid != 0);
DICT_THASH_OPEN_RETURN(DICT_DEBUG (dict));
}