mbox: update mbox atime and show number of new msgs
[mailx.git] / mailx.c
blobb4eb90d6bc2b0bd7f143712c1e46629563e147e1
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 "mbox.h"
9 #include "sort.h"
11 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
12 #define MIN(a, b) ((a) < (b) ? (a) : (b))
13 #define MAXLINE (1 << 7)
14 #define BUFSIZE (1 << 12)
15 #define PAGER "pgmail"
16 #define NHEAD (1 << 4)
17 #define WIDTH 80
18 #define DIGITS 5
19 #define FOLDER "/home/ali/.mailx/"
21 static char *hdr_filt[] = {
22 "Subject:", "From:", "To:", "Cc:", "Date:", "User-Agent:",
23 "X-Mailer", "Organization"};
25 static char *boxes[] = {FOLDER "inbox"};
27 static struct mbox *mbox;
28 static struct sort *sort;
29 static int cur;
31 static int read_line(char *dst, int size)
33 static char buf[BUFSIZE];
34 static int cur;
35 static int len;
36 int nw = 0;
37 while (1) {
38 int cur_len = MIN(len - cur, size - nw - 1);
39 char *nl = memchr(buf + cur, '\n', cur_len);
40 int nr = nl ? nl - buf - cur + 1 : cur_len;
41 if (nr) {
42 memcpy(dst + nw, buf + cur, nr);
43 nw += nr;
44 cur += nr;
45 dst[nw] = '\0';
47 if (nl || nw == size - 1)
48 return nw;
49 cur = 0;
50 if ((len = read(STDIN_FILENO, buf, BUFSIZE)) <= 0)
51 return -1;
53 return nw;
56 static char *till_eol(char *r, int len, char *s)
58 char *d = r + len;
59 while (r < d && *s && *s != '\r' && *s != '\n')
60 *r++ = *s++;
61 return r;
64 static char *cut_word(char *dst, char *s)
66 while (isspace(*s))
67 s++;
68 while (!isspace(*s))
69 *dst++ = *s++;
70 *dst = '\0';
71 return s;
74 static int msg_num(char *num)
76 int n = -1;
77 if (!*num || !strcmp(".", num))
78 n = cur;
79 if (isdigit(*num))
80 n = atoi(num);
81 if (!strcmp("$", num))
82 n = mbox->n - 1;
83 if (n < 0 || n >= mbox->n)
84 return -1;
85 cur = n;
86 return n;
89 static void cmd_page(char *args)
91 struct mail *mail;
92 pid_t pid;
93 int fds[2];
94 char head[BUFSIZE];
95 char num[MAXLINE];
96 int hdr_len;
97 args = cut_word(num, args);
98 if (msg_num(num) == -1)
99 return;
100 mail = sort->mails[cur];
101 mail->stat |= STAT_READ;
102 pipe(fds);
103 if (!(pid = fork())) {
104 char *args[] = {PAGER, NULL};
105 close(fds[1]);
106 dup2(fds[0], STDIN_FILENO);
107 execvp(PAGER, args);
108 exit(1);
110 close(fds[0]);
111 hdr_len = mail_head(mail, head, sizeof(head),
112 hdr_filt, ARRAY_SIZE(hdr_filt));
113 write(fds[1], head, hdr_len);
114 write(fds[1], mail->body, mail->body_len);
115 close(fds[1]);
116 waitpid(pid, NULL, 0);
119 static char *cut_cmd(char *dst, char *s)
121 while (isalpha(*s)) {
122 *dst++ = *s++;
124 *dst = '\0';
125 return s;
128 static char *put_int(char *s, int n, int w)
130 int i;
131 for (i = 0; i < w; i++) {
132 s[w - i - 1] = n || !i ? '0' + n % 10 : ' ';
133 n = n / 10;
135 return s + w;
138 static char *put_str(char *dst, char *src)
140 int len = strchr(src, '\0') - src;
141 memcpy(dst, src, len + 1);
142 return dst + len;
145 static char *put_hdr(struct mail *mail, char *name, int w, char *s)
147 char *hdr = mail_hdr(mail, name);
148 return till_eol(s, w, hdr ? hdr + strlen(name) + 1 : NULL);
151 static void cmd_head(char *args)
153 char num[MAXLINE];
154 int beg, end;
155 int i;
156 args = cut_word(num, args);
157 if (msg_num(num) == -1)
158 return;
159 beg = cur / NHEAD * NHEAD;
160 end = MIN(beg + NHEAD, mbox->n);
161 for (i = beg; i < end; i++) {
162 struct mail *mail = sort->mails[i];
163 char fmt[MAXLINE];
164 char *s = fmt;
165 *s++ = i == cur ? '>' : ' ';
166 if (mail->stat & STAT_READ)
167 *s++ = ' ';
168 else
169 *s++ = mail->stat & STAT_NEW ? 'N' : 'O';
170 s = put_int(s, i, DIGITS);
171 *s++ = ' ';
172 *s++ = ' ';
173 s = put_hdr(mail, "From:", 16, s);
174 *s++ = ' ';
175 *s++ = ' ';
176 s = sort_draw(sort, i, s, WIDTH - (s - fmt));
177 s = put_hdr(mail, "Subject:", WIDTH - (s - fmt), s);
178 *s++ = '\n';
179 write(STDOUT_FILENO, fmt, s - fmt);
183 static void cmd_z(char *args)
185 char num[MAXLINE];
186 int page = -1;
187 cut_word(num, args);
188 if (!*num)
189 page = MIN(cur + NHEAD, mbox->n) / NHEAD;
190 if (*num == '+' || *num == '-') {
191 int d = (*num + 1) ? (*num == '-' ? -1 : 1) : atoi(num);
192 page = cur / NHEAD + d;
194 if (*num == '$')
195 page = mbox->n / NHEAD;
196 if (isdigit(*num))
197 page = atoi(num);
198 if (page >= 0 && page * NHEAD < mbox->n) {
199 cur = page * NHEAD;
200 cmd_head("");
204 static void print(char *s)
206 write(STDOUT_FILENO, s, strlen(s));
209 static void cmd_fold(char *args)
211 char msg[MAXLINE];
212 char *s = put_int(msg, mbox->n, DIGITS);
213 int new = 0;
214 int unread = 0;
215 int i;
216 s = put_str(s, " messages ");
217 for (i = 0; i < mbox->n; i++)
218 if (mbox->mails[i].stat & STAT_NEW)
219 new++;
220 else if (!(mbox->mails[i].stat & STAT_READ))
221 unread++;
222 if (new) {
223 s = put_int(s, new, DIGITS);
224 s = put_str(s, " new ");
226 if (unread) {
227 s = put_int(s, unread, DIGITS);
228 s = put_str(s, " unread ");
230 s = put_str(s, "\n");
231 print(msg);
234 static void cmd_inc(char *args)
236 char msg[MAXLINE];
237 struct stat st;
238 int new = mbox_inc(mbox);
239 char *s;
240 int i;
241 if (new) {
242 sort_inc(sort);
243 s = put_int(msg, new, DIGITS);
244 s = put_str(s, " new\n");
245 print(msg);
247 for (i = 0; i < ARRAY_SIZE(boxes); i++) {
248 if (stat(boxes[i], &st) == -1)
249 continue;
250 if (st.st_mtime > st.st_atime) {
251 char *box = strrchr(boxes[i], '/');
252 s = msg;
253 box = box ? box + 1 : boxes[i];
254 s = put_str(s, "mbox: ");
255 s = put_str(s, box);
256 s = put_str(s, "\n");
257 print(msg);
262 static void prompt(void)
264 write(STDOUT_FILENO, "? ", 2);
267 static void loop(void)
269 char line[MAXLINE];
270 char cmd[MAXLINE];
271 prompt();
272 while (read_line(line, sizeof(line)) > 0) {
273 char *args = cut_cmd(cmd, line);
274 int len = strlen(cmd);
275 if (!len) {
276 if (cur + 1 < mbox->n) {
277 cur++;
278 cmd_page(args);
279 } else {
280 print("EOF\n");
282 prompt();
283 continue;
285 if (!strncmp("page", cmd, len))
286 cmd_page(args);
287 if (!strncmp("header", cmd, len))
288 cmd_head(args);
289 if (!strncmp("folder", cmd, len))
290 cmd_fold(args);
291 if (!strncmp("inc", cmd, len))
292 cmd_inc(args);
293 if (!strncmp("z", cmd, len))
294 cmd_z(args);
295 if (!strncmp("quit", cmd, len)) {
296 mbox_write(mbox);
297 return;
299 if (!strncmp("xit", cmd, len) || !strncmp("exit", cmd, len))
300 return;
301 prompt();
305 int main(int argc, char *argv[])
307 int i = 0;
308 char *filename = NULL;
309 while (++i < argc)
310 if (!strcmp("-f", argv[i]))
311 filename = argv[++i];
312 if (filename) {
313 mbox = mbox_alloc(filename);
314 sort = sort_alloc(mbox);
315 loop();
316 sort_free(sort);
317 mbox_free(mbox);
319 return 0;