mk: two-character status
[neatmail.git] / ex.c
blob9e90ad0c5e6e12c46f6ff1fa76c9fe81df77c5cc
1 #include <ctype.h>
2 #include <fcntl.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <unistd.h>
7 #include <sys/stat.h>
8 #include "mail.h"
9 #include "regex.h"
11 #define EXLEN 512
13 static struct mbox *mbox;
14 static int pos;
15 static char kwd[EXLEN];
16 static char kwd_hdr[EXLEN];
17 static regex_t kwd_re;
19 static char *ex_loc(char *s, char *loc)
21 while (*s == ':' || isspace((unsigned char) *s))
22 s++;
23 while (*s && !isalpha((unsigned char) *s)) {
24 if (*s == '/' || *s == '?') {
25 int d = *s;
26 *loc++ = *s++;
27 while (*s && *s != d) {
28 if (*s == '\\' && s[1])
29 *loc++ = *s++;
30 *loc++ = *s++;
33 if (*s)
34 *loc++ = *s++;
36 *loc = '\0';
37 return s;
40 static char *ex_cmd(char *s, char *cmd)
42 while (isspace((unsigned char) *s))
43 s++;
44 while (isalpha((unsigned char) *s))
45 *cmd++ = *s++;
46 *cmd = '\0';
47 return s;
50 static char *ex_arg(char *s, char *arg)
52 while (isspace((unsigned char) *s))
53 s++;
54 if (*s == '"') {
55 s++;
56 while (*s && *s != '"') {
57 if (*s == '\\' && s[1])
58 s++;
59 *arg++ = *s++;
61 s++;
62 } else {
63 while (*s && !isspace((unsigned char) *s)) {
64 if (*s == '\\' && s[1])
65 s++;
66 *arg++ = *s++;
69 *arg = '\0';
70 return s;
73 static int ex_keyword(char *pat)
75 struct sbuf *sb;
76 char *b = pat;
77 char *e = b;
78 sb = sbuf_make();
79 while (*++e) {
80 if (*e == *pat)
81 break;
82 if (*e == '\\' && e[1])
83 e++;
84 sbuf_chr(sb, (unsigned char) *e);
86 if (sbuf_len(sb) && strcmp(kwd, sbuf_buf(sb))) {
87 if (kwd[0])
88 regfree(&kwd_re);
89 snprintf(kwd, sizeof(kwd), "%s", sbuf_buf(sb));
90 if (regcomp(&kwd_re, kwd, REG_EXTENDED | REG_ICASE))
91 kwd[0] = '\0';
92 if (kwd[0] == '^' && isalpha((unsigned char) (kwd[1])) &&
93 strchr(kwd, ':')) {
94 int len = strchr(kwd, ':') - kwd;
95 memcpy(kwd_hdr, kwd + 1, len);
96 kwd_hdr[len] = '\0';
97 } else {
98 strcpy(kwd_hdr, "subject:");
101 sbuf_free(sb);
102 return !kwd[0];
105 static int ex_match(int i)
107 char *msg;
108 long msglen;
109 char *hdr;
110 char *buf;
111 int len, ret;
112 if (mbox_get(mbox, i, &msg, &msglen))
113 return 1;
114 hdr = msg_get(msg, msglen, kwd_hdr);
115 if (!hdr)
116 return 1;
117 len = hdrlen(hdr, msg + msglen - hdr);
118 buf = malloc(len + 1);
119 memcpy(buf, hdr, len);
120 buf[len] = '\0';
121 ret = regexec(&kwd_re, buf, 0, NULL, 0);
122 free(buf);
123 return ret != 0;
126 static int ex_search(char *pat)
128 int dir = *pat == '/' ? +1 : -1;
129 int i = pos + dir;
130 if (ex_keyword(pat))
131 return 1;
132 while (i >= 0 && i < mbox_len(mbox)) {
133 if (!ex_match(i))
134 return i;
135 i += dir;
137 return pos;
140 static int ex_lineno(char *num)
142 int n = pos;
143 if (!num[0] || num[0] == '.')
144 n = pos;
145 if (isdigit(num[0]))
146 n = atoi(num);
147 if (num[0] == '$')
148 n = mbox_len(mbox) - 1;
149 if (num[0] == '-')
150 n = pos - (num[1] ? ex_lineno(num + 1) : 1);
151 if (num[0] == '+')
152 n = pos + (num[1] ? ex_lineno(num + 1) : 1);
153 if (num[0] == '/' && num[1])
154 n = ex_search(num);
155 if (num[0] == '?' && num[1])
156 n = ex_search(num);
157 return n;
160 static int ex_region(char *loc, int *beg, int *end)
162 if (loc[0] == '%') {
163 *beg = 0;
164 *end = mbox_len(mbox);
165 return 0;
167 if (!*loc || loc[0] == ';') {
168 *beg = pos;
169 *end = pos == mbox_len(mbox) ? pos : pos + 1;
170 return 0;
172 *beg = ex_lineno(loc);
173 while (*loc && *loc != ',' && *loc != ';')
174 loc++;
175 if (*loc == ',')
176 *end = ex_lineno(++loc) + 1;
177 else
178 *end = *beg == mbox_len(mbox) ? *beg : *beg + 1;
179 if (*beg < 0 || *beg >= mbox_len(mbox))
180 return 1;
181 if (*end < *beg || *end > mbox_len(mbox))
182 return 1;
183 return 0;
186 static int ec_rm(char *arg)
188 mbox_set(mbox, pos, "", 0);
189 return 0;
192 static int ec_cp(char *arg)
194 char box[EXLEN];
195 char *msg;
196 long msz;
197 int fd;
198 arg = ex_arg(arg, box);
199 fd = open(box, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
200 mbox_get(mbox, pos, &msg, &msz);
201 xwrite(fd, msg, msz);
202 close(fd);
203 return 0;
206 static int ec_mv(char *arg)
208 ec_cp(arg);
209 ec_rm("");
210 return 0;
213 static int ec_hd(char *arg)
215 char hdr[EXLEN];
216 char val[EXLEN];
217 char *msg, *mod;
218 long msglen, modlen;
219 struct sbuf *sb = sbuf_make();
220 arg = ex_arg(arg, hdr);
221 arg = ex_arg(arg, val);
222 mbox_get(mbox, pos, &msg, &msglen);
223 sbuf_printf(sb, "%s %s\n", hdr, val);
224 if (msg_set(msg, msglen, &mod, &modlen, hdr, sbuf_buf(sb)))
225 return 1;
226 sbuf_free(sb);
227 mbox_set(mbox, pos, mod, modlen);
228 free(mod);
229 return 0;
232 static int ec_ft(char *arg)
234 char cmd[EXLEN];
235 char *msg, *mod;
236 long msglen, modlen;
237 arg = ex_arg(arg, cmd);
238 mbox_get(mbox, pos, &msg, &msglen);
239 if (xpipe(cmd, msg, msglen, &mod, &modlen))
240 return 1;
241 mbox_set(mbox, pos, mod, modlen);
242 free(mod);
243 return 0;
246 static int ec_threadjoin(char *arg)
248 char *th, *msg, *mod;
249 long thlen, msglen, modlen;
250 struct sbuf *sb;
251 char *id, *id_end;
252 if (mbox_get(mbox, atoi(arg), &th, &thlen))
253 return 1;
254 if (mbox_get(mbox, pos, &msg, &msglen))
255 return 1;
256 id = msg_get(th, thlen, "message-id:");
257 if (!id)
258 return 1;
259 id_end = id + hdrlen(id, th + thlen - id);
260 id = strchr(id, ':') + 1;
261 sb = sbuf_make();
262 sbuf_str(sb, "In-Reply-To:");
263 sbuf_mem(sb, id, id_end - id);
264 if (msg_set(msg, msglen, &mod, &modlen, "in-reply-to:", sbuf_done(sb)))
265 return 1;
266 mbox_set(mbox, pos, mod, modlen);
267 return 0;
270 static int ec_chop(char *arg)
272 char *msg, *mod;
273 long msglen, modlen;
274 struct sbuf *sb;
275 long newlen = atoi(arg);
276 if (newlen < 1024)
277 return 1;
278 if (mbox_get(mbox, pos, &msg, &msglen))
279 return 1;
280 while (newlen < msglen && msg[newlen] != '\n')
281 newlen++;
282 sb = sbuf_make();
283 sbuf_mem(sb, msg, newlen);
284 sbuf_str(sb, "\nCHOPPED...\n\n");
285 modlen = sbuf_len(sb);
286 mod = sbuf_done(sb);
287 mbox_set(mbox, pos, mod, modlen);
288 return 0;
291 static int ec_wr(char *arg)
293 char box[EXLEN];
294 arg = ex_arg(arg, box);
295 if (box[0])
296 mbox_copy(mbox, box);
297 else
298 mbox_save(mbox);
299 return 0;
302 static int ex_exec(char *ec);
304 static int ec_g(char *arg, int not)
306 while (isspace((unsigned char) *arg))
307 arg++;
308 if (arg[0] != '/' || ex_keyword(arg))
309 return 1;
310 arg++;
311 while (arg[0] && arg[0] != '/')
312 arg++;
313 if (kwd[0] && arg[0] == '/') {
314 arg++;
315 if (!ex_match(pos) == !not)
316 ex_exec(arg);
317 return 0;
319 return 1;
322 static int ex_exec(char *ec)
324 char cmd[EXLEN];
325 char *arg = ex_cmd(ec, cmd);
326 if (!strcmp("rm", cmd))
327 return ec_rm(arg);
328 if (!strcmp("cp", cmd))
329 return ec_cp(arg);
330 if (!strcmp("mv", cmd))
331 return ec_mv(arg);
332 if (!strcmp("hd", cmd) || !strcmp("set", cmd))
333 return ec_hd(arg);
334 if (!strcmp("ft", cmd) || !strcmp("filt", cmd))
335 return ec_ft(arg);
336 if (!strcmp("w", cmd))
337 return ec_wr(arg);
338 if (!strcmp("g", cmd))
339 return ec_g(arg, 0);
340 if (!strcmp("g!", cmd))
341 return ec_g(arg, 1);
342 if (!strcmp("tj", cmd))
343 return ec_threadjoin(arg);
344 if (!strcmp("ch", cmd) || !strcmp("chop", cmd))
345 return ec_chop(arg);
346 return 1;
349 static int ec_stat(char *ec)
351 char *val;
352 char newval[16];
353 int c0 = (unsigned char) ec[0];
354 int c1 = isalpha((unsigned char) ec[1]) ? ec[1] : 0;
355 int s0 = 'N';
356 int s1 = 0;
357 int i = atoi(ec + 1 + (c1 != 0));
358 char *msg, *mod;
359 long msglen, modlen;
360 if (mbox_get(mbox, i, &msg, &msglen))
361 return 1;
362 pos = i;
363 val = msg_get(msg, msglen, "status:");
364 if (val) {
365 val += strlen("status:");
366 while (val + 1 < msg + msglen && isspace((unsigned char) val[0]))
367 val++;
368 s0 = !isspace(val[0]) ? (unsigned char) val[0] : s0;
369 s1 = !isspace(val[1]) ? (unsigned char) val[1] : s1;
371 if (s0 == c0 && s1 == c1)
372 return 0;
373 if (c1)
374 sprintf(newval, "Status: %c%c\n", c0, c1);
375 else
376 sprintf(newval, "Status: %c\n", c0);
377 if (msg_set(msg, msglen, &mod, &modlen, "status:", newval))
378 return 1;
379 mbox_set(mbox, pos, mod, modlen);
380 free(mod);
381 return 0;
384 int ex(char *argv[])
386 char ec[EXLEN];
387 char loc[EXLEN];
388 int beg, end, i;
389 char *cmd;
390 if (!argv[0]) {
391 printf("usage: neatmail ex mbox <cmds\n");
392 return 1;
394 mbox = mbox_open(argv[0]);
395 if (!mbox) {
396 fprintf(stderr, "neatmail: cannot open <%s>\n", argv[0]);
397 return 1;
399 while (fgets(ec, sizeof(ec), stdin)) {
400 char *cur = loc;
401 if (isupper((unsigned char) ec[0]) && ec[1])
402 ec_stat(ec);
403 if (ec[0] != ':')
404 continue;
405 cmd = ex_loc(ec, loc);
406 do {
407 if (!ex_region(cur, &beg, &end)) {
408 for (i = beg; i < end; i++) {
409 pos = i;
410 ex_exec(cmd);
413 while (*cur && *cur != ';')
414 cur++;
415 if (*cur == ';')
416 cur++;
417 } while (*cur);
419 mbox_free(mbox);
420 if (kwd[0])
421 regfree(&kwd_re);
422 return 0;