mk: specify a default message line format
[neatmail.git] / ex.c
blob619b6c8300999cbf4d04fa69db78a5bcbdeda48f
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 (i < 0 || i >= mbox_len(mbox))
113 return 1;
114 mbox_get(mbox, i, &msg, &msglen);
115 hdr = msg_get(msg, msglen, kwd_hdr);
116 if (!hdr)
117 return 1;
118 len = hdrlen(hdr, msg + msglen - hdr);
119 buf = malloc(len + 1);
120 memcpy(buf, hdr, len);
121 buf[len] = '\0';
122 ret = regexec(&kwd_re, buf, 0, NULL, 0);
123 free(buf);
124 return ret != 0;
127 static int ex_search(char *pat)
129 int dir = *pat == '/' ? +1 : -1;
130 int i = pos + dir;
131 if (ex_keyword(pat))
132 return 1;
133 while (i >= 0 && i < mbox_len(mbox)) {
134 if (!ex_match(i))
135 return i;
136 i += dir;
138 return pos;
141 static int ex_lineno(char *num)
143 int n = pos;
144 if (!num[0] || num[0] == '.')
145 n = pos;
146 if (isdigit(num[0]))
147 n = atoi(num);
148 if (num[0] == '$')
149 n = mbox_len(mbox) - 1;
150 if (num[0] == '-')
151 n = pos - (num[1] ? ex_lineno(num + 1) : 1);
152 if (num[0] == '+')
153 n = pos + (num[1] ? ex_lineno(num + 1) : 1);
154 if (num[0] == '/' && num[1])
155 n = ex_search(num);
156 if (num[0] == '?' && num[1])
157 n = ex_search(num);
158 return n;
161 static int ex_region(char *loc, int *beg, int *end)
163 if (loc[0] == '%') {
164 *beg = 0;
165 *end = mbox_len(mbox);
166 return 0;
168 if (!*loc || loc[0] == ';') {
169 *beg = pos;
170 *end = pos == mbox_len(mbox) ? pos : pos + 1;
171 return 0;
173 *beg = ex_lineno(loc);
174 while (*loc && *loc != ',' && *loc != ';')
175 loc++;
176 if (*loc == ',')
177 *end = ex_lineno(++loc) + 1;
178 else
179 *end = *beg == mbox_len(mbox) ? *beg : *beg + 1;
180 if (*beg < 0 || *beg >= mbox_len(mbox))
181 return 1;
182 if (*end < *beg || *end > mbox_len(mbox))
183 return 1;
184 return 0;
187 static int ec_rm(char *arg)
189 mbox_set(mbox, pos, "", 0);
190 return 0;
193 static int ec_cp(char *arg)
195 char box[EXLEN];
196 char *msg;
197 long msz;
198 int fd;
199 arg = ex_arg(arg, box);
200 fd = open(box, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
201 mbox_get(mbox, pos, &msg, &msz);
202 xwrite(fd, msg, msz);
203 close(fd);
204 return 0;
207 static int ec_mv(char *arg)
209 ec_cp(arg);
210 ec_rm("");
211 return 0;
214 static int ec_hd(char *arg)
216 char hdr[EXLEN];
217 char val[EXLEN];
218 char *msg, *mod;
219 long msglen, modlen;
220 struct sbuf *sb = sbuf_make();
221 arg = ex_arg(arg, hdr);
222 arg = ex_arg(arg, val);
223 mbox_get(mbox, pos, &msg, &msglen);
224 sbuf_printf(sb, "%s %s\n", hdr, val);
225 if (msg_set(msg, msglen, &mod, &modlen, hdr, sbuf_buf(sb)))
226 return 1;
227 sbuf_free(sb);
228 mbox_set(mbox, pos, mod, modlen);
229 free(mod);
230 return 0;
233 static int ec_ft(char *arg)
235 char *msg, *mod;
236 long msglen, modlen;
237 mbox_get(mbox, pos, &msg, &msglen);
238 if (xpipe(arg, msg, msglen, &mod, &modlen))
239 return 1;
240 mbox_set(mbox, pos, mod, modlen);
241 free(mod);
242 return 0;
245 static int ec_wr(char *arg)
247 char box[EXLEN];
248 arg = ex_arg(arg, box);
249 if (box[0])
250 mbox_copy(mbox, box);
251 else
252 mbox_save(mbox);
253 return 0;
256 static int ex_exec(char *ec);
258 static int ec_g(char *arg)
260 while (isspace((unsigned char) *arg))
261 arg++;
262 if (arg[0] != '/' || ex_keyword(arg))
263 return 1;
264 arg++;
265 while (arg[0] && arg[0] != '/')
266 arg++;
267 if (kwd[0] && arg[0] == '/') {
268 arg++;
269 if (!ex_match(pos))
270 ex_exec(arg);
271 return 0;
273 return 1;
276 static int ex_exec(char *ec)
278 char cmd[EXLEN];
279 char *arg = ex_cmd(ec, cmd);
280 if (!strcmp("rm", cmd))
281 return ec_rm(arg);
282 if (!strcmp("cp", cmd))
283 return ec_cp(arg);
284 if (!strcmp("mv", cmd))
285 return ec_mv(arg);
286 if (!strcmp("hd", cmd) || !strcmp("set", cmd))
287 return ec_hd(arg);
288 if (!strcmp("ft", cmd) || !strcmp("filt", cmd))
289 return ec_ft(arg);
290 if (!strcmp("w", cmd))
291 return ec_wr(arg);
292 if (!strcmp("g", cmd))
293 return ec_g(arg);
294 return 1;
297 static int ec_stat(char *ec)
299 char *val;
300 char newval[16];
301 char c = ec[0];
302 int i = atoi(ec + 1);
303 char *msg, *mod;
304 long msglen, modlen;
305 if (i < 0 || i >= mbox_len(mbox))
306 return 1;
307 pos = i;
308 mbox_get(mbox, pos, &msg, &msglen);
309 val = msg_get(msg, msglen, "status:");
310 if (val) {
311 val += strlen("status:");
312 while (isspace((unsigned char) val[0]))
313 val++;
315 if ((!val && c == 'N') || (val && val[0] == c))
316 return 0;
317 sprintf(newval, "Status: %c\n", c);
318 if (msg_set(msg, msglen, &mod, &modlen, "status:", newval))
319 return 1;
320 mbox_set(mbox, pos, mod, modlen);
321 free(mod);
322 return 0;
325 int ex(char *argv[])
327 char ec[EXLEN];
328 char loc[EXLEN];
329 int beg, end, i;
330 char *cmd;
331 if (!argv[0]) {
332 printf("usage: neatmail ex mbox <cmds\n");
333 return 1;
335 mbox = mbox_open(argv[0]);
336 if (!mbox) {
337 fprintf(stderr, "neatmail: cannot open <%s>\n", argv[0]);
338 return 1;
340 while (fgets(ec, sizeof(ec), stdin)) {
341 char *cur = loc;
342 if (isupper((unsigned char) ec[0]))
343 ec_stat(ec);
344 if (ec[0] != ':')
345 continue;
346 cmd = ex_loc(ec, loc);
347 do {
348 if (!ex_region(cur, &beg, &end)) {
349 for (i = beg; i < end; i++) {
350 pos = i;
351 ex_exec(cmd);
354 while (*cur && *cur != ';')
355 cur++;
356 if (*cur == ';')
357 cur++;
358 } while (*cur);
360 mbox_free(mbox);
361 if (kwd[0])
362 regfree(&kwd_re);
363 return 0;