mime: fix uninitialised mime->depth
[neatmail.git] / ex.c
blob3cbe86e88e94cd2599ac5874510ec508b0bfa0f2
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_wr(char *arg)
272 char box[EXLEN];
273 arg = ex_arg(arg, box);
274 if (box[0])
275 mbox_copy(mbox, box);
276 else
277 mbox_save(mbox);
278 return 0;
281 static int ex_exec(char *ec);
283 static int ec_g(char *arg, int not)
285 while (isspace((unsigned char) *arg))
286 arg++;
287 if (arg[0] != '/' || ex_keyword(arg))
288 return 1;
289 arg++;
290 while (arg[0] && arg[0] != '/')
291 arg++;
292 if (kwd[0] && arg[0] == '/') {
293 arg++;
294 if (!ex_match(pos) == !not)
295 ex_exec(arg);
296 return 0;
298 return 1;
301 static int ex_exec(char *ec)
303 char cmd[EXLEN];
304 char *arg = ex_cmd(ec, cmd);
305 if (!strcmp("rm", cmd))
306 return ec_rm(arg);
307 if (!strcmp("cp", cmd))
308 return ec_cp(arg);
309 if (!strcmp("mv", cmd))
310 return ec_mv(arg);
311 if (!strcmp("hd", cmd) || !strcmp("set", cmd))
312 return ec_hd(arg);
313 if (!strcmp("ft", cmd) || !strcmp("filt", cmd))
314 return ec_ft(arg);
315 if (!strcmp("w", cmd))
316 return ec_wr(arg);
317 if (!strcmp("g", cmd))
318 return ec_g(arg, 0);
319 if (!strcmp("g!", cmd))
320 return ec_g(arg, 1);
321 if (!strcmp("tj", cmd))
322 return ec_threadjoin(arg);
323 return 1;
326 static int ec_stat(char *ec)
328 char *val;
329 char newval[16];
330 char c = ec[0];
331 int i = atoi(ec + 1);
332 char *msg, *mod;
333 long msglen, modlen;
334 if (mbox_get(mbox, i, &msg, &msglen))
335 return 1;
336 pos = i;
337 val = msg_get(msg, msglen, "status:");
338 if (val) {
339 val += strlen("status:");
340 while (isspace((unsigned char) val[0]))
341 val++;
343 if ((!val && c == 'N') || (val && val[0] == c))
344 return 0;
345 sprintf(newval, "Status: %c\n", c);
346 if (msg_set(msg, msglen, &mod, &modlen, "status:", newval))
347 return 1;
348 mbox_set(mbox, pos, mod, modlen);
349 free(mod);
350 return 0;
353 int ex(char *argv[])
355 char ec[EXLEN];
356 char loc[EXLEN];
357 int beg, end, i;
358 char *cmd;
359 if (!argv[0]) {
360 printf("usage: neatmail ex mbox <cmds\n");
361 return 1;
363 mbox = mbox_open(argv[0]);
364 if (!mbox) {
365 fprintf(stderr, "neatmail: cannot open <%s>\n", argv[0]);
366 return 1;
368 while (fgets(ec, sizeof(ec), stdin)) {
369 char *cur = loc;
370 if (isupper((unsigned char) ec[0]))
371 ec_stat(ec);
372 if (ec[0] != ':')
373 continue;
374 cmd = ex_loc(ec, loc);
375 do {
376 if (!ex_region(cur, &beg, &end)) {
377 for (i = beg; i < end; i++) {
378 pos = i;
379 ex_exec(cmd);
382 while (*cur && *cur != ';')
383 cur++;
384 if (*cur == ';')
385 cur++;
386 } while (*cur);
388 mbox_free(mbox);
389 if (kwd[0])
390 regfree(&kwd_re);
391 return 0;