summaryrefslogtreecommitdiff
path: root/cdbmake.c
blob: c13420a14de35aea55cc383c03e3e627dac602a7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include <errno.h>
#include <stdio.h>
#include <skalibs/bytestr.h>
#include <skalibs/buffer.h>
#include <skalibs/cdbmake.h>
#include <skalibs/djbunix.h>
#include <skalibs/posixplz.h>
#include <skalibs/skamisc.h>
#include <skalibs/strerr.h>
#include <skalibs/uint32.h>

static int
record_read(buffer *b, stralloc *key, stralloc *data, char **errmsg)
{
	char buf[sizeof("+4294967295,4294967295:")-1];
	size_t len = 0;
	int r = getlnmax(b, buf, sizeof(buf), &len, ':');
	if (r <= 0)
		return r;
	if (buf[0] != '+')
		return (*errmsg = "expected '+'", errno = EINVAL, -1);

	uint32_t keylen, datalen;

	size_t comma_pos = byte_chr(buf, len, ',');
	if (comma_pos == len)
		return (*errmsg = "expected ','", errno = EINVAL, -1);
	buf[comma_pos] = 0;
	if (!uint320_scan(buf + 1, &keylen))
		return (*errmsg = "could not parse key length", errno = EINVAL, -1);
	buf[comma_pos] = ',';

	if (len < comma_pos + 1)
		return (*errmsg = "expected data length after ','", errno = EINVAL, -1);
	size_t colon_pos = byte_chr(buf, len, ':');
	if (colon_pos == len)
		return (*errmsg = "expected ':'", errno = EINVAL, -1);
	buf[colon_pos] = 0;
	if (!uint320_scan(buf + comma_pos + 1, &datalen))
		return (*errmsg = "could not parse data length", errno = EINVAL, -1);
	buf[colon_pos] = ':';

	size_t w = 0;
	if (!stralloc_ready(key, keylen))
		return (errno = ENOMEM, -1);
	if ((r = buffer_getall(b, key->s, keylen, &w)) <= 0 && errno != EPIPE)
		return -1;
	if (w < keylen)
		return (*errmsg = "unexpected EOF while reading key", errno = EINVAL, -1);
	key->len = keylen;
	w = 0;
	if (buffer_getall(b, buf, 2, &w) <= 0 && errno != EPIPE)
		return -1;
	if (w < 2 || memcmp(buf, "->", 2))
		return (*errmsg = "expected '->'", errno = EINVAL, -1);
	if (!stralloc_ready(data, datalen))
		return (errno = ENOMEM, -1);
	w = 0;
	if (buffer_getall(b, data->s, datalen, &w) <= 0 && errno != EPIPE)
		return -1;
	if (w < datalen)
		return (*errmsg = "unexpected EOF while reading data", errno = EINVAL, -1);
	data->len = datalen;
	w = 0;
	if (buffer_getall(b, buf, 1, &w) <= 0 && errno != EPIPE)
		return -1;
	if (w < 1 || buf[0] != '\n')
		return (*errmsg = "expected '\\n'", errno = EINVAL, -1);
	return 1;
}

int
main(int argc, char *argv[])
{
	PROG = "cdbmake";
	if (argc != 3)
		strerr_dieusage(100, "cdbmake CDB TMP");
	char *cdb_path = argv[1];
	char *tmp_path = argv[2];

	int tmp_fd = open_trunc(tmp_path);
	if (tmp_fd < 0)
		strerr_diefusys(111, "open ", tmp_path, " for writing");
	cdbmaker cm = CDBMAKER_ZERO;
	if (!cdbmake_start(&cm, tmp_fd)) {
		unlink_void(tmp_path);
		strerr_diefusys(111, "cdbmake_start ", tmp_path);
	}

	stralloc key = STRALLOC_ZERO;
	stralloc data = STRALLOC_ZERO;
	for (uint32_t record = 0;; ++record) {
		char *errmsg = "";
		int r = record_read(buffer_0, &key, &data, &errmsg);
		if (!r) break;
		if (r < 0) {
			unlink_void(tmp_path);
			if (errno == EINVAL) {
				char recordbuf[sizeof("4294967295")];
				recordbuf[uint32_fmt(recordbuf, record)] = 0;
				strerr_dief(111, "syntax error on record ", recordbuf, ": ", errmsg);
			} else {
				strerr_diefusys(111, "read from stdin");
			}
		}
		if (!cdbmake_add(&cm, key.s, key.len, data.s, data.len)) {
			unlink_void(tmp_path);
			strerr_diefusys(111, "write cdb to ", tmp_path);
		}
	}
	stralloc_free(&key);
	stralloc_free(&data);
	if (!cdbmake_finish(&cm)) {
		unlink_void(tmp_path);
		strerr_diefusys(111, "cdbmake_finish ", tmp_path);
	}
	if (fd_sync(tmp_fd) < 0) {
		unlink_void(tmp_path);
		strerr_diefusys(111, "fsync ", tmp_path);
	}
	if (rename(tmp_path, cdb_path) < 0) {
		unlink_void(tmp_path);
		strerr_diefusys(111, "rename ", tmp_path, " to ", cdb_path);
	}
	return 0;
}