mail: list available commands and options
[neatmail.git] / ex.c
blobc1b5aaedce7f090b4e2899a3fcd2696830145484
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 regex_t re;
79 sb = sbuf_make();
80 while (*++e) {
81 if (*e == *pat)
82 break;
83 sbuf_chr(sb, (unsigned char) *e);
84 if (*e == '\\' && e[1])
85 e++;
87 if (sbuf_len(sb)) {
88 if (kwd[0])
89 regfree(&kwd_re);
90 snprintf(kwd, sizeof(kwd), "%s", sbuf_buf(sb));
91 if (regcomp(&re, kwd, REG_EXTENDED | REG_ICASE))
92 kwd[0] = '\0';
94 sbuf_free(sb);
95 if (kwd[0] == '^' && isalpha((unsigned char) (kwd[1])) &&
96 strchr(kwd, ':')) {
97 int len = strchr(kwd, ':') - kwd;
98 memcpy(kwd_hdr, kwd + 1, len);
99 kwd_hdr[len] = '\n';
100 } else {
101 strcpy(kwd_hdr, "subject:");
103 return !kwd[0];
106 static int ex_match(int i)
108 char *msg;
109 long msglen;
110 char *hdr;
111 char *buf;
112 int len, ret;
113 if (i < 0 || i >= mbox_len(mbox))
114 return 1;
115 mbox_get(mbox, i, &msg, &msglen);
116 hdr = msg_get(msg, msglen, kwd_hdr);
117 if (!hdr)
118 return 1;
119 len = hdrlen(hdr, msg + msglen - hdr);
120 buf = malloc(len + 1);
121 memcpy(buf, hdr, len);
122 buf[len] = '\0';
123 ret = regexec(&kwd_re, buf, 0, NULL, 0);
124 free(buf);
125 return ret != 0;
128 static int ex_search(char *pat)
130 int dir = *pat == '/' ? +1 : -1;
131 int i = pos + dir;
132 if (ex_keyword(pat))
133 return 1;
134 while (i >= 0 && i < mbox_len(mbox)) {
135 if (!ex_match(i))
136 return i;
137 i += dir;
139 return pos;
142 static int ex_lineno(char *num)
144 int n = pos;
145 if (!num[0] || num[0] == '.')
146 n = pos;
147 if (isdigit(num[0]))
148 n = atoi(num);
149 if (num[0] == '$')
150 n = mbox_len(mbox) - 1;
151 if (num[0] == '-')
152 n = pos - (num[1] ? ex_lineno(num + 1) : 1);
153 if (num[0] == '+')
154 n = pos + (num[1] ? ex_lineno(num + 1) : 1);
155 if (num[0] == '/' && num[1])
156 n = ex_search(num);
157 if (num[0] == '?' && num[1])
158 n = ex_search(num);
159 return n;
162 static int ex_region(char *loc, int *beg, int *end)
164 if (loc[0] == '%') {
165 *beg = 0;
166 *end = mbox_len(mbox);
167 return 0;
169 if (!*loc || loc[0] == ';') {
170 *beg = pos;
171 *end = pos == mbox_len(mbox) ? pos : pos + 1;
172 return 0;
174 *beg = ex_lineno(loc);
175 while (*loc && *loc != ',' && *loc != ';')
176 loc++;
177 if (*loc == ',')
178 *end = ex_lineno(++loc);
179 else
180 *end = *beg == mbox_len(mbox) ? *beg : *beg + 1;
181 if (*beg < 0 || *beg >= mbox_len(mbox))
182 return 1;
183 if (*end < *beg || *end > mbox_len(mbox))
184 return 1;
185 return 0;
188 static int ec_rm(char *arg)
190 mbox_set(mbox, pos, "", 0);
191 return 0;
194 static int ec_cp(char *arg)
196 char box[EXLEN];
197 char *msg;
198 long msz;
199 int fd;
200 arg = ex_arg(arg, box);
201 fd = open(box, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
202 mbox_get(mbox, pos, &msg, &msz);
203 xwrite(fd, msg, msz);
204 close(fd);
205 return 0;
208 static int ec_mv(char *arg)
210 ec_cp(arg);
211 ec_rm("");
212 return 0;
215 static int ec_hd(char *arg)
217 char hdr[EXLEN];
218 char val[EXLEN];
219 char *msg, *mod;
220 long msglen, modlen;
221 struct sbuf *sb = sbuf_make();
222 arg = ex_arg(arg, hdr);
223 arg = ex_arg(arg, val);
224 mbox_get(mbox, pos, &msg, &msglen);
225 sbuf_printf(sb, "%s %s\n", hdr, val);
226 if (msg_set(msg, msglen, &mod, &modlen, hdr, sbuf_buf(sb)))
227 return 1;
228 sbuf_free(sb);
229 mbox_set(mbox, pos, mod, modlen);
230 free(mod);
231 return 0;
234 static int ec_ft(char *arg)
236 char *msg, *mod;
237 long msglen, modlen;
238 mbox_get(mbox, pos, &msg, &msglen);
239 if (xpipe(arg, msg, msglen, &mod, &modlen))
240 return 1;
241 mbox_set(mbox, pos, mod, modlen);
242 free(mod);
243 return 0;
246 static int ec_wr(char *arg)
248 char box[EXLEN];
249 arg = ex_arg(arg, box);
250 if (box[0])
251 mbox_copy(mbox, box);
252 else
253 mbox_save(mbox);
254 return 0;
257 static int ex_exec(char *ec);
259 static int ec_g(char *arg)
261 while (isspace((unsigned char) *arg))
262 arg++;
263 if (arg[0] != '/' || ex_keyword(arg))
264 return 1;
265 arg++;
266 while (arg[0] && arg[0] != '/')
267 arg++;
268 if (kwd[0]) {
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 char 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;