mk: specify a default message line format
[neatmail.git] / mbox.c
blobf2fff94871d436b2dae35967607d8ed90fa0064e
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 struct mbox {
15 char *path;
16 char **msg; /* messages */
17 long *msglen; /* message lengths */
18 char **mod; /* modified messages */
19 long *modlen; /* modified message lengths */
20 char *buf; /* mbox buffer */
21 long len; /* buf len */
22 int n; /* number of messages */
25 static void set_atime(char *filename)
27 struct stat st;
28 struct utimbuf times;
29 stat(filename, &st);
30 times.actime = time(NULL);
31 times.modtime = st.st_mtime;
32 utime(filename, &times);
35 static char *mbox_from_(char *s, char *e)
37 char *r;
38 while (s && s + 6 < e) {
39 if (s[0] == 'F' && s[1] == 'r' && s[2] == 'o' &&
40 s[3] == 'm' && s[4] == ' ')
41 return s;
42 r = memchr(s, '\n', e - s);
43 s = r && r + 7 < e ? r + 1 : NULL;
45 return NULL;
48 static int mbox_count(char *s, char *e)
50 int n = 0;
51 while ((s = mbox_from_(s, e)))
52 n++, s++;
53 return n;
56 void mbox_get(struct mbox *mbox, int i, char **msg, long *msglen)
58 if (mbox->mod[i]) {
59 *msg = mbox->mod[i];
60 *msglen = mbox->modlen[i];
61 } else {
62 *msg = mbox->msg[i];
63 *msglen = mbox->msglen[i];
67 void mbox_set(struct mbox *mbox, int i, char *msg, long msz)
69 free(mbox->mod[i]);
70 mbox->mod[i] = malloc(msz + 1);
71 if (mbox->mod[i]) {
72 mbox->modlen[i] = msz;
73 memcpy(mbox->mod[i], msg, msz);
74 mbox->mod[i][msz] = '\0';
78 int mbox_len(struct mbox *mbox)
80 return mbox->n;
83 static int mbox_mails(struct mbox *mbox, char *s, char *e)
85 int i;
86 s = mbox_from_(s, e);
87 for (i = 0; s && s < e; i++) {
88 char *r = s;
89 mbox->msg[i] = s;
90 s = mbox_from_(s + 6, e);
91 mbox->msglen[i] = s ? s - r : mbox->buf + mbox->len - r;
93 mbox->n = i;
94 return 0;
97 static int filesize(int fd)
99 struct stat stat;
100 fstat(fd, &stat);
101 return stat.st_size;
104 static int mbox_read(struct mbox *mbox)
106 int fd = open(mbox->path, O_RDONLY);
107 if (fd < 0)
108 return 1;
109 mbox->len = filesize(fd);
110 mbox->buf = malloc(mbox->len + 1);
111 if (!mbox->buf)
112 return 1;
113 xread(fd, mbox->buf, mbox->len);
114 mbox->buf[mbox->len] = '\0';
115 close(fd);
116 set_atime(mbox->path); /* update mbox access time */
117 mbox->n = mbox_count(mbox->buf, mbox->buf + mbox->len);
118 mbox->msg = malloc(mbox->n * sizeof(mbox->msg[0]));
119 mbox->msglen = malloc(mbox->n * sizeof(mbox->msglen[0]));
120 mbox->mod = malloc(mbox->n * sizeof(mbox->mod[0]));
121 mbox->modlen = malloc(mbox->n * sizeof(mbox->modlen[0]));
122 memset(mbox->mod, 0, mbox->n * sizeof(mbox->mod[0]));
123 if (!mbox->msg || !mbox->msglen || !mbox->mod || !mbox->modlen)
124 return 1;
125 if (mbox_mails(mbox, mbox->buf, mbox->buf + mbox->len))
126 return 1;
127 return 0;
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 struct mbox *mbox_open(char *path)
141 struct mbox *mbox;
142 mbox = malloc(sizeof(*mbox));
143 if (!mbox)
144 return NULL;
145 memset(mbox, 0, sizeof(*mbox));
146 mbox->path = sdup(path);
147 if (!mbox->path || mbox_read(mbox)) {
148 mbox_free(mbox);
149 return NULL;
151 return mbox;
154 void mbox_free(struct mbox *mbox)
156 int i;
157 for (i = 0; i < mbox->n; i++)
158 free(mbox->mod[i]);
159 free(mbox->msg);
160 free(mbox->msglen);
161 free(mbox->mod);
162 free(mbox->modlen);
163 free(mbox->path);
164 free(mbox->buf);
165 free(mbox);
168 static int mbox_modified(struct mbox *mbox)
170 int i;
171 for (i = 0; i < mbox->n; i++)
172 if (mbox->mod[i])
173 return 1;
174 return 0;
177 int mbox_copy(struct mbox *mbox, char *path)
179 int fd;
180 int i;
181 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
182 if (fd < 0)
183 return -1;
184 for (i = 0; i < mbox->n; i++) {
185 char *msg = mbox->mod[i] ? mbox->mod[i] : mbox->msg[i];
186 long len = mbox->mod[i] ? mbox->modlen[i] : mbox->msglen[i];
187 xwrite(fd, msg, len);
189 close(fd);
190 return 0;
193 int mbox_save(struct mbox *mbox)
195 int fd;
196 int i = 0;
197 char *newbuf;
198 long off = 0;
199 long newlen;
200 if (!mbox_modified(mbox))
201 return 0;
202 fd = open(mbox->path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
203 newlen = filesize(fd) - mbox->len;
204 if (newlen > 0) {
205 newbuf = malloc(newlen);
206 lseek(fd, mbox->len, 0);
207 xread(fd, newbuf, newlen);
209 while (i < mbox->n && !mbox->mod[i])
210 off += mbox->msglen[i++];
211 lseek(fd, off, 0);
212 for (; i < mbox->n; i++) {
213 char *msg = mbox->mod[i] ? mbox->mod[i] : mbox->msg[i];
214 long len = mbox->mod[i] ? mbox->modlen[i] : mbox->msglen[i];
215 xwrite(fd, msg, len);
217 ftruncate(fd, lseek(fd, 0, 1));
218 close(fd);
219 return newlen;
222 int mbox_ith(char *path, int n, char **msg, long *msz)
224 int fd = open(path, O_RDONLY);
225 char *s, *e, *r;
226 char *buf;
227 int len;
228 int i;
229 if (fd < 0)
230 return 1;
231 len = filesize(fd);
232 buf = malloc(len + 1);
233 if (!buf)
234 return 1;
235 xread(fd, buf, len);
236 buf[len] = '\0';
237 close(fd);
238 e = buf + len;
239 s = mbox_from_(buf, e);
240 for (i = 0; s && i < n; i++)
241 s = mbox_from_(s + 1, e);
242 if (!s)
243 return 1;
244 r = mbox_from_(s + 1, e);
245 if (!r)
246 r = buf + len;
247 *msg = malloc(r - s + 1);
248 if (!*msg)
249 return 1;
250 *msz = r - s;
251 memcpy(*msg, s, *msz);
252 (*msg)[*msz] = '\0';
253 free(buf);
254 return 0;