mailx: support ',', '.' and '+box' in file cmd
[mailx.git] / mailx.c
blob19d0ba8d963d9e99ce10d178e74dde19b00b50fe
1 #include <ctype.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <sys/stat.h>
6 #include <sys/types.h>
7 #include <sys/wait.h>
8 #include "config.h"
9 #include "mbox.h"
10 #include "sort.h"
12 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
13 #define MIN(a, b) ((a) < (b) ? (a) : (b))
14 #define MAXLINE (1 << 7)
15 #define BUFSIZE (1 << 12)
17 static struct mbox *mbox;
18 static struct sort *sort;
19 static int cur;
21 static int read_line(char *dst, int size)
23 static char buf[BUFSIZE];
24 static int cur;
25 static int len;
26 int nw = 0;
27 while (1) {
28 int cur_len = MIN(len - cur, size - nw - 1);
29 char *nl = memchr(buf + cur, '\n', cur_len);
30 int nr = nl ? nl - buf - cur + 1 : cur_len;
31 if (nr) {
32 memcpy(dst + nw, buf + cur, nr);
33 nw += nr;
34 cur += nr;
35 dst[nw] = '\0';
37 if (nl || nw == size - 1)
38 return nw;
39 cur = 0;
40 if ((len = read(STDIN_FILENO, buf, BUFSIZE)) <= 0)
41 return -1;
43 return nw;
46 static char *till_eol(char *r, int len, char *s)
48 char *d = r + len;
49 while (r < d && *s && *s != '\r' && *s != '\n')
50 *r++ = *s++;
51 return r;
54 static char *cut_word(char *dst, char *s)
56 while (isspace(*s))
57 s++;
58 while (!isspace(*s))
59 *dst++ = *s++;
60 *dst = '\0';
61 return s;
64 static int msg_num(char *num)
66 int n = -1;
67 if (!*num || !strcmp(".", num))
68 n = cur;
69 if (isdigit(*num))
70 n = atoi(num);
71 if (!strcmp("$", num))
72 n = mbox->n - 1;
73 if (n < 0 || n >= mbox->n)
74 return -1;
75 cur = n;
76 return n;
79 static void cmd_page(char *args)
81 struct mail *mail;
82 pid_t pid;
83 int fds[2];
84 char head[BUFSIZE];
85 char num[MAXLINE];
86 int hdr_len;
87 args = cut_word(num, args);
88 if (msg_num(num) == -1)
89 return;
90 mail = sort->mails[cur];
91 mail->stat |= STAT_READ;
92 pipe(fds);
93 if (!(pid = fork())) {
94 char *args[] = {PAGER, NULL};
95 close(fds[1]);
96 dup2(fds[0], STDIN_FILENO);
97 execvp(PAGER, args);
98 exit(1);
100 close(fds[0]);
101 hdr_len = mail_head(mail, head, sizeof(head),
102 hdr_filt, ARRAY_SIZE(hdr_filt));
103 write(fds[1], head, hdr_len);
104 write(fds[1], mail->body, mail->body_len);
105 close(fds[1]);
106 waitpid(pid, NULL, 0);
109 static char *cut_cmd(char *dst, char *s)
111 while (isalpha(*s)) {
112 *dst++ = *s++;
114 *dst = '\0';
115 return s;
118 static char *put_int(char *s, int n, int w)
120 int i;
121 for (i = 0; i < w; i++) {
122 s[w - i - 1] = n || !i ? '0' + n % 10 : ' ';
123 n = n / 10;
125 return s + w;
128 static char *put_str(char *dst, char *src)
130 int len = strchr(src, '\0') - src;
131 memcpy(dst, src, len + 1);
132 return dst + len;
135 static char *put_hdr(struct mail *mail, char *name, int w, char *s)
137 char *hdr = mail_hdr(mail, name);
138 return till_eol(s, w, hdr ? hdr + strlen(name) + 1 : NULL);
141 static void cmd_head(char *args)
143 char num[MAXLINE];
144 int beg, end;
145 int i;
146 args = cut_word(num, args);
147 if (msg_num(num) == -1)
148 return;
149 beg = cur / NHEAD * NHEAD;
150 end = MIN(beg + NHEAD, mbox->n);
151 for (i = beg; i < end; i++) {
152 struct mail *mail = sort->mails[i];
153 char fmt[MAXLINE];
154 char *s = fmt;
155 *s++ = i == cur ? '>' : ' ';
156 if (mail->stat & STAT_READ)
157 *s++ = ' ';
158 else
159 *s++ = mail->stat & STAT_NEW ? 'N' : 'O';
160 s = put_int(s, i, DIGITS);
161 *s++ = ' ';
162 *s++ = ' ';
163 s = put_hdr(mail, "From:", 16, s);
164 *s++ = ' ';
165 *s++ = ' ';
166 s = sort_draw(sort, i, s, WIDTH - (s - fmt));
167 s = put_hdr(mail, "Subject:", WIDTH - (s - fmt), s);
168 *s++ = '\n';
169 write(STDOUT_FILENO, fmt, s - fmt);
173 static void cmd_z(char *args)
175 char num[MAXLINE];
176 int page = -1;
177 cut_word(num, args);
178 if (!*num)
179 page = MIN(cur + NHEAD, mbox->n) / NHEAD;
180 if (*num == '+' || *num == '-') {
181 int d = (*num + 1) ? (*num == '-' ? -1 : 1) : atoi(num);
182 page = cur / NHEAD + d;
184 if (*num == '$')
185 page = mbox->n / NHEAD;
186 if (isdigit(*num))
187 page = atoi(num);
188 if (page >= 0 && page * NHEAD < mbox->n) {
189 cur = page * NHEAD;
190 cmd_head("");
194 static void print(char *s)
196 write(STDOUT_FILENO, s, strlen(s));
199 static void open_mbox(char *filename)
201 mbox = mbox_alloc(filename);
202 sort = sort_alloc(mbox);
203 cur = 0;
206 static void close_mbox(void)
208 sort_free(sort);
209 mbox_free(mbox);
212 static int is_mbox(char *filename)
214 struct stat st;
215 stat(filename, &st);
216 return S_ISREG(st.st_mode);
219 static void sum_mbox(void)
221 char msg[MAXLINE];
222 char *s = put_int(msg, mbox->n, DIGITS);
223 int new = 0;
224 int unread = 0;
225 int i;
226 s = put_str(s, " messages ");
227 for (i = 0; i < mbox->n; i++)
228 if (mbox->mails[i].stat & STAT_NEW)
229 new++;
230 else if (!(mbox->mails[i].stat & STAT_READ))
231 unread++;
232 if (new) {
233 s = put_int(s, new, DIGITS);
234 s = put_str(s, " new ");
236 if (unread) {
237 s = put_int(s, unread, DIGITS);
238 s = put_str(s, " unread ");
240 s = put_str(s, "\n");
241 print(msg);
244 static int has_mail(char *path)
246 struct stat st;
247 if (stat(path, &st) == -1)
248 return 0;
249 return st.st_mtime > st.st_atime;
252 static int mbox_path(char *path, char *addr)
254 char *s = path;
255 int i;
256 if (*addr == '+') {
257 s = put_str(s, FOLDER);
258 s = put_str(s, addr + 1);
260 if (*addr == ',') {
261 for (i = 0; i < ARRAY_SIZE(boxes); i++) {
262 if (has_mail(boxes[i])) {
263 s = put_str(s, boxes[i]);
264 break;
268 if (!strcmp(".", addr) && mbox)
269 s = put_str(s, mbox->path);
270 if (s == path && *addr)
271 s = put_str(s, addr);
272 return s - path;
275 static void cmd_fold(char *args)
277 char filename[MAXLINE];
278 args = cut_word(filename, args);
279 if (*filename) {
280 char path[MAXPATHLEN];
281 if (mbox_path(path, filename) && is_mbox(path)) {
282 close_mbox();
283 open_mbox(filename);
285 } else {
286 sum_mbox();
290 static void cmd_inc(char *args)
292 char msg[MAXLINE];
293 int new = mbox_inc(mbox);
294 char *s;
295 int i;
296 if (new) {
297 sort_inc(sort);
298 s = put_int(msg, new, DIGITS);
299 s = put_str(s, " new\n");
300 print(msg);
302 for (i = 0; i < ARRAY_SIZE(boxes); i++) {
303 if (has_mail(boxes[i])) {
304 char *box = strrchr(boxes[i], '/');
305 s = msg;
306 box = box ? box + 1 : boxes[i];
307 s = put_str(s, "mbox: ");
308 s = put_str(s, box);
309 s = put_str(s, "\n");
310 print(msg);
315 static void prompt(void)
317 write(STDOUT_FILENO, "? ", 2);
320 static void loop(void)
322 char line[MAXLINE];
323 char cmd[MAXLINE];
324 prompt();
325 while (read_line(line, sizeof(line)) > 0) {
326 char *args = cut_cmd(cmd, line);
327 int len = strlen(cmd);
328 if (!len) {
329 if (cur + 1 < mbox->n) {
330 cur++;
331 cmd_page(args);
332 } else {
333 print("EOF\n");
335 prompt();
336 continue;
338 if (!strncmp("page", cmd, len))
339 cmd_page(args);
340 if (!strncmp("header", cmd, len))
341 cmd_head(args);
342 if (!strncmp("file", cmd, len) || !strncmp("folder", cmd, len))
343 cmd_fold(args);
344 if (!strncmp("inc", cmd, len))
345 cmd_inc(args);
346 if (!strncmp("z", cmd, len))
347 cmd_z(args);
348 if (!strncmp("quit", cmd, len)) {
349 mbox_write(mbox);
350 return;
352 if (!strncmp("xit", cmd, len) || !strncmp("exit", cmd, len))
353 return;
354 prompt();
358 int main(int argc, char *argv[])
360 int i = 0;
361 char *filename = NULL;
362 while (++i < argc)
363 if (!strcmp("-f", argv[i]))
364 filename = argv[++i];
365 if (filename) {
366 char path[MAXPATHLEN];
367 if (mbox_path(path, filename) && is_mbox(path)) {
368 open_mbox(path);
369 loop();
370 close_mbox();
373 return 0;