regex: updates from neatvi
[neatmail.git] / mbox.c
blob213b873522191d10ea0a47cdcced5bee95d47743
1 #include <ctype.h>
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <sys/stat.h>
8 #include <sys/types.h>
9 #include <time.h>
10 #include <unistd.h>
11 #include <utime.h>
12 #include "mail.h"
14 #define MBOX_N 16 /* number of mboxes */
16 struct mbox {
17 char *boxpath[MBOX_N]; /* mbox paths */
18 char *boxbuf[MBOX_N]; /* mbox bufferes */
19 long boxlen[MBOX_N]; /* mbox buf lengths */
20 int boxcnt[MBOX_N]; /* number mbox messages */
21 int boxbeg[MBOX_N]; /* mbox's first message */
22 int boxend[MBOX_N]; /* mbox's last message */
23 int cnt; /* number of mboxes */
24 char **msg; /* messages */
25 long *msglen; /* message lengths */
26 char **mod; /* modified messages */
27 long *modlen; /* modified message lengths */
28 int msgmax; /* size of msg arrays */
29 int msgcnt; /* number of messages */
32 static void set_atime(char *filename)
34 struct stat st;
35 struct utimbuf times;
36 stat(filename, &st);
37 times.actime = time(NULL);
38 times.modtime = st.st_mtime;
39 utime(filename, &times);
42 static char *mbox_from_(char *s, char *e)
44 char *r;
45 while (s && s + 6 < e) {
46 if (s[0] == 'F' && s[1] == 'r' && s[2] == 'o' &&
47 s[3] == 'm' && s[4] == ' ')
48 return s;
49 r = memchr(s, '\n', e - s);
50 s = r && r + 7 < e ? r + 1 : NULL;
52 return NULL;
55 int mbox_get(struct mbox *mbox, int i, char **msg, long *msglen)
57 if (i < 0 || i >= mbox->msgcnt)
58 return 1;
59 if (mbox->mod[i]) {
60 *msg = mbox->mod[i];
61 *msglen = mbox->modlen[i];
62 } else {
63 *msg = mbox->msg[i];
64 *msglen = mbox->msglen[i];
66 return 0;
69 int mbox_set(struct mbox *mbox, int i, char *msg, long msz)
71 if (i < 0 || i >= mbox->msgcnt)
72 return 1;
73 free(mbox->mod[i]);
74 mbox->mod[i] = malloc(msz + 1);
75 if (mbox->mod[i]) {
76 mbox->modlen[i] = msz;
77 memcpy(mbox->mod[i], msg, msz);
78 mbox->mod[i][msz] = '\0';
80 return 0;
83 int mbox_len(struct mbox *mbox)
85 return mbox->msgcnt;
88 static void *mextend(void *old, long oldsz, long newsz, long memsz)
90 void *new = malloc(newsz * memsz);
91 memcpy(new, old, oldsz * memsz);
92 memset(new + oldsz * memsz, 0, (newsz - oldsz) * memsz);
93 free(old);
94 return new;
97 static int mbox_extend(struct mbox *mbox, int cnt)
99 mbox->msgmax = mbox->msgmax + cnt;
100 mbox->msg = mextend(mbox->msg, mbox->msgcnt, mbox->msgmax, sizeof(mbox->msg[0]));
101 mbox->msglen = mextend(mbox->msglen, mbox->msgcnt, mbox->msgmax, sizeof(mbox->msglen[0]));
102 if (!mbox->msg || !mbox->msglen)
103 return 1;
104 return 0;
107 static int mbox_mails(struct mbox *mbox, char *s, char *e)
109 s = mbox_from_(s, e);
110 while (s && s < e) {
111 char *r = s;
112 if (mbox->msgcnt == mbox->msgmax)
113 if (mbox_extend(mbox, mbox->msgmax ? mbox->msgmax : 256))
114 return 1;
115 mbox->msg[mbox->msgcnt] = s;
116 s = mbox_from_(s + 6, e);
117 mbox->msglen[mbox->msgcnt] = s ? s - r : e - r;
118 mbox->msgcnt++;
120 return 0;
123 static int filesize(int fd)
125 struct stat stat;
126 fstat(fd, &stat);
127 return stat.st_size;
130 static char *sdup(char *s)
132 int n = strlen(s) + 1;
133 char *r = malloc(n);
134 if (r)
135 memcpy(r, s, n);
136 return r;
139 static int mbox_read(struct mbox *mbox, char *path)
141 int tag = mbox->cnt++;
142 int fd;
143 mbox->boxpath[tag] = sdup(path);
144 if (!mbox->boxpath[tag])
145 return 1;
146 fd = open(path, O_RDONLY);
147 if (fd < 0)
148 return 1;
149 mbox->boxlen[tag] = filesize(fd);
150 mbox->boxbuf[tag] = malloc(mbox->boxlen[tag] + 1);
151 if (!mbox->boxbuf[tag])
152 return 1;
153 xread(fd, mbox->boxbuf[tag], mbox->boxlen[tag]);
154 mbox->boxbuf[tag][mbox->boxlen[tag]] = '\0';
155 close(fd);
156 set_atime(mbox->boxpath[tag]); /* update mbox access time */
157 mbox->boxbeg[tag] = mbox->msgcnt;
158 if (mbox_mails(mbox, mbox->boxbuf[tag], mbox->boxbuf[tag] + mbox->boxlen[tag]))
159 return 1;
160 mbox->boxend[tag] = mbox->msgcnt;
161 return 0;
164 struct mbox *mbox_open(char **path)
166 struct mbox *mbox = malloc(sizeof(*mbox));
167 if (!mbox)
168 return NULL;
169 memset(mbox, 0, sizeof(*mbox));
170 for (; *path; path++) {
171 if (mbox->cnt + 1 < MBOX_N && mbox_read(mbox, *path)) {
172 mbox_free(mbox);
173 return NULL;
176 mbox->mod = calloc(mbox->msgcnt, sizeof(mbox->mod[0]));
177 mbox->modlen = calloc(mbox->msgcnt, sizeof(mbox->modlen[0]));
178 if (!mbox->mod || !mbox->modlen) {
179 mbox_free(mbox);
180 return NULL;
182 return mbox;
185 void mbox_free(struct mbox *mbox)
187 int i;
188 for (i = 0; i < mbox->cnt; i++) {
189 free(mbox->boxpath[i]);
190 free(mbox->boxbuf[i]);
192 if (mbox->mod) {
193 for (i = 0; i < mbox->msgcnt; i++)
194 free(mbox->mod[i]);
196 free(mbox->msg);
197 free(mbox->msglen);
198 free(mbox->mod);
199 free(mbox->modlen);
200 free(mbox);
203 static int mbox_modified(struct mbox *mbox, int tag)
205 int i;
206 for (i = mbox->boxbeg[tag]; i < mbox->boxend[tag]; i++)
207 if (mbox->mod[i])
208 return 1;
209 return 0;
212 int mbox_copy(struct mbox *mbox, char *path)
214 int fd;
215 int i;
216 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
217 if (fd < 0)
218 return -1;
219 for (i = mbox->boxbeg[0]; i < mbox->boxend[0]; i++) {
220 char *msg = mbox->mod[i] ? mbox->mod[i] : mbox->msg[i];
221 long len = mbox->mod[i] ? mbox->modlen[i] : mbox->msglen[i];
222 xwrite(fd, msg, len);
224 close(fd);
225 return 0;
228 int mbox_savetag(struct mbox *mbox, int tag)
230 int fd;
231 int i = 0;
232 char *newbuf = NULL;
233 long off = 0;
234 long newlen;
235 if (!mbox_modified(mbox, tag))
236 return 0;
237 for (i = mbox->boxbeg[tag]; i < mbox->boxend[tag] && !mbox->mod[i]; i++)
238 off += mbox->msglen[i];
239 fd = open(mbox->boxpath[tag], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
240 newlen = filesize(fd) - mbox->boxlen[tag];
241 if (newlen > 0) {
242 newbuf = malloc(newlen);
243 lseek(fd, mbox->boxlen[tag], 0);
244 xread(fd, newbuf, newlen);
246 ftruncate(fd, lseek(fd, off, 0));
247 for (; i < mbox->boxend[tag]; i++) {
248 char *msg = mbox->mod[i] ? mbox->mod[i] : mbox->msg[i];
249 long len = mbox->mod[i] ? mbox->modlen[i] : mbox->msglen[i];
250 lseek(fd, 0, 2);
251 xwrite(fd, msg, len);
253 if (newlen > 0) {
254 lseek(fd, 0, 2);
255 xwrite(fd, newbuf, newlen);
256 free(newbuf);
258 close(fd);
259 return newlen;
262 int mbox_save(struct mbox *mbox)
264 return mbox_savetag(mbox, 0);
267 /* return file index of a message and its position within that file */
268 int mbox_pos(struct mbox *mbox, int n, int *idx)
270 int i;
271 for (i = 1; i < mbox->cnt; i++)
272 if (n < mbox->boxbeg[i])
273 break;
274 *idx = n - mbox->boxbeg[i - 1];
275 return i - 1;
278 int mbox_ith(char *path, int n, char **msg, long *msz)
280 int fd = open(path, O_RDONLY);
281 char *s, *e, *r;
282 char *buf;
283 int len;
284 int i;
285 if (fd < 0)
286 return 1;
287 len = filesize(fd);
288 buf = malloc(len + 1);
289 if (!buf)
290 return 1;
291 xread(fd, buf, len);
292 buf[len] = '\0';
293 close(fd);
294 e = buf + len;
295 s = mbox_from_(buf, e);
296 for (i = 0; s && i < n; i++)
297 s = mbox_from_(s + 1, e);
298 if (!s)
299 return 1;
300 r = mbox_from_(s + 1, e);
301 if (!r)
302 r = buf + len;
303 *msg = malloc(r - s + 1);
304 if (!*msg)
305 return 1;
306 *msz = r - s;
307 memcpy(*msg, s, *msz);
308 (*msg)[*msz] = '\0';
309 free(buf);
310 return 0;
313 int mbox_off(char *path, long beg, long end, char **msg, long *msz)
315 int fd = open(path, O_RDONLY);
316 long nr;
317 if (fd < 0)
318 return 1;
319 if (lseek(fd, beg, 0) < 0) {
320 close(fd);
321 return 1;
323 *msg = malloc(end - beg + 1);
324 if (!*msg)
325 return 1;
326 nr = xread(fd, *msg, end - beg);
327 (*msg)[nr] = '\0';
328 *msz = nr;
329 close(fd);
330 return 0;