12 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
13 #define MIN(a, b) ((a) < (b) ? (a) : (b))
14 #define MAILBUF (1 << 16)
15 #define MAXLINE (1 << 7)
16 #define BUFSIZE (1 << 12)
18 static struct mbox
*mbox
;
19 static struct sort
*sort
;
21 static int sel
[MAXMAILS
];
24 static int read_line(char *dst
, int size
)
26 static char buf
[BUFSIZE
];
31 int cur_len
= MIN(len
- cur
, size
- nw
- 1);
32 char *nl
= memchr(buf
+ cur
, '\n', cur_len
);
33 int nr
= nl
? nl
- buf
- cur
+ 1 : cur_len
;
35 memcpy(dst
+ nw
, buf
+ cur
, nr
);
40 if (nl
|| nw
== size
- 1)
43 if ((len
= read(STDIN_FILENO
, buf
, BUFSIZE
)) <= 0)
49 static char *till_eol(char *r
, int len
, char *s
)
52 while (r
< d
&& *s
&& *s
!= '\r' && *s
!= '\n')
57 static char *put_mem(char *dst
, char *src
, int len
)
59 memcpy(dst
, src
, len
);
63 static char *cut_word(char *dst
, char *s
)
65 while (*s
&& isspace(*s
))
67 while (*s
&& !isspace(*s
))
73 static int msg_num(char *num
)
76 if (!*num
|| !strcmp(".", num
))
80 if (!strcmp("$", num
))
82 if (!strcmp(",", num
)) {
84 while (++i
< sort
->n
) {
85 if (!(sort
->mails
[i
]->stat
& STAT_READ
)) {
91 if (n
< 0 || n
>= mbox
->n
)
96 static int search(char *args
, int all
)
98 static char hdr_name
[MAXLINE
];
99 static char hdr_val
[MAXLINE
];
100 char *beg
= strchr(args
, '(');
101 char *spc
= beg
? strchr(beg
, ' ') : NULL
;
102 char *end
= spc
? strchr(spc
, ')') : NULL
;
104 if (beg
&& (!end
|| !spc
)) {
108 int hdr_len
= spc
- beg
- 1;
109 put_mem(hdr_name
, beg
+ 1, hdr_len
);
110 hdr_name
[hdr_len
] = '\0';
111 while (isspace(*spc
))
113 put_mem(hdr_val
, spc
, end
- spc
);
114 hdr_val
[end
- spc
] = '\0';
116 for (i
= all
? 0 : cur
+ 1; i
< sort
->n
; i
++) {
117 char *hdr
= mail_hdr(sort
->mails
[i
], hdr_name
);
118 char *s
= hdr
+ strlen(hdr_name
);
119 char *d
= hdr
+ hdr_len(hdr
);
121 if (!strncmp(hdr_val
, s
, strlen(hdr_val
))) {
129 s
= memchr(s
+ 1, hdr_val
[0], d
- s
- 1);
135 static int sel_msgs(char *args
, int all
)
144 while (isspace(*args
))
147 return search(args
, all
);
149 args
= cut_word(num
, args
);
152 if ((dash
= strchr(num
, '-'))) {
153 char beg_str
[MAXLINE
];
154 memcpy(beg_str
, num
, dash
- num
);
155 beg_str
[dash
- num
] = '\0';
156 beg
= msg_num(beg_str
);
157 end
= msg_num(dash
+ 1);
162 if (beg
!= -1 && end
!= -1) {
167 for (i
= beg
; i
<= end
; i
++)
174 static void cmd_page(char *args
)
178 char *pg_args
[] = {PAGER
, NULL
};
180 if (!sel_msgs(args
, 0))
182 mail
= sort
->mails
[cur
];
183 mail
->stat
|= STAT_READ
;
184 s
+= mail_head(mail
, buf
, sizeof(buf
), hdr_filt
, ARRAY_SIZE(hdr_filt
));
185 s
= put_mem(s
, mail
->body
, MIN(sizeof(buf
) - (s
- buf
),
187 exec_pipe(PAGER
, pg_args
, buf
, s
- buf
);
190 static char *cut_cmd(char *dst
, char *s
)
192 while (isalpha(*s
)) {
199 static char *put_int(char *s
, int n
, int w
)
202 for (i
= 0; i
< w
; i
++) {
203 s
[w
- i
- 1] = n
|| !i
? '0' + n
% 10 : ' ';
209 static char *put_str(char *dst
, char *src
)
211 int len
= strchr(src
, '\0') - src
;
212 memcpy(dst
, src
, len
+ 1);
216 static char *put_hdr(struct mail
*mail
, char *name
, int w
, char *s
)
218 char *hdr
= mail_hdr(mail
, name
);
219 return till_eol(s
, w
, hdr
? hdr
+ strlen(name
) + 1 : NULL
);
222 static void cmd_head(char *args
)
226 if (!sel_msgs(args
, 0))
228 beg
= cur
/ NHEAD
* NHEAD
;
229 end
= MIN(beg
+ NHEAD
, mbox
->n
);
230 for (i
= beg
; i
< end
; i
++) {
231 struct mail
*mail
= sort
->mails
[i
];
234 *s
++ = i
== cur
? '>' : ' ';
235 if (mail
->stat
& STAT_DEL
)
239 if (mail
->stat
& STAT_READ
)
242 *s
++ = mail
->stat
& STAT_NEW
? 'N' : 'O';
243 s
= put_int(s
, i
, DIGITS
);
246 s
= put_hdr(mail
, "From:", 16, s
);
249 s
= sort_draw(sort
, i
, s
, WIDTH
- (s
- fmt
));
250 s
= put_hdr(mail
, "Subject:", WIDTH
- (s
- fmt
), s
);
252 write(STDOUT_FILENO
, fmt
, s
- fmt
);
256 static void cmd_z(char *args
)
262 page
= MIN(cur
+ NHEAD
, mbox
->n
) / NHEAD
;
263 if (*num
== '+' || *num
== '-') {
264 int d
= (*num
+ 1) ? (*num
== '-' ? -1 : 1) : atoi(num
);
265 page
= cur
/ NHEAD
+ d
;
268 page
= mbox
->n
/ NHEAD
;
271 if (page
>= 0 && page
* NHEAD
< mbox
->n
) {
277 static void cmd_del(char *args
)
280 if (!sel_msgs(args
, 1))
282 for (i
= 0; i
< nsel
; i
++)
283 sort
->mails
[sel
[i
]]->stat
|= STAT_DEL
;
286 static void cmd_undel(char *args
)
289 if (!sel_msgs(args
, 1))
291 for (i
= 0; i
< nsel
; i
++)
292 sort
->mails
[sel
[i
]]->stat
&= ~STAT_DEL
;
295 static void print(char *s
)
297 write(STDOUT_FILENO
, s
, strlen(s
));
300 static void open_mbox(char *filename
)
302 mbox
= mbox_alloc(filename
);
303 sort
= sort_alloc(mbox
);
307 static void close_mbox(void)
313 static int is_mbox(char *filename
)
317 return S_ISREG(st
.st_mode
);
320 static void sum_mbox(void)
323 char *s
= put_int(msg
, mbox
->n
, DIGITS
);
328 s
= put_str(s
, " messages ");
329 for (i
= 0; i
< mbox
->n
; i
++) {
330 if (mbox
->mails
[i
].stat
& STAT_NEW
)
332 if (!(mbox
->mails
[i
].stat
& STAT_READ
))
334 if (mbox
->mails
[i
].stat
& STAT_DEL
)
338 s
= put_int(s
, new, DIGITS
);
339 s
= put_str(s
, " new ");
342 s
= put_int(s
, unread
, DIGITS
);
343 s
= put_str(s
, " unread ");
346 s
= put_int(s
, del
, DIGITS
);
347 s
= put_str(s
, " deleted ");
349 s
= put_str(s
, "\n");
353 static int has_mail(char *path
)
356 if (stat(path
, &st
) == -1)
358 return st
.st_mtime
> st
.st_atime
;
361 static int mbox_path(char *path
, char *addr
)
366 s
= put_str(s
, FOLDER
);
367 s
= put_str(s
, addr
+ 1);
370 for (i
= 0; i
< ARRAY_SIZE(boxes
); i
++) {
371 if (has_mail(boxes
[i
])) {
372 s
= put_str(s
, boxes
[i
]);
377 if (!strcmp(".", addr
) && mbox
)
378 s
= put_str(s
, mbox
->path
);
379 if (s
== path
&& *addr
)
380 s
= put_str(s
, addr
);
384 static void cmd_fold(char *args
)
386 char filename
[MAXLINE
];
387 args
= cut_word(filename
, args
);
389 char path
[MAXPATHLEN
];
390 if (mbox_path(path
, filename
) && is_mbox(path
)) {
399 static void warn_nomem(void)
401 print("no mem for new msgs\n");
404 static void cmd_inc(char *args
)
407 int new = mbox_inc(mbox
);
414 s
= put_int(msg
, new, DIGITS
);
415 s
= put_str(s
, " new\n");
418 for (i
= 0; i
< ARRAY_SIZE(boxes
); i
++) {
419 if (has_mail(boxes
[i
])) {
420 char *box
= strrchr(boxes
[i
], '/');
422 box
= box
? box
+ 1 : boxes
[i
];
423 s
= put_str(s
, "mbox: ");
425 s
= put_str(s
, "\n");
431 static void cmd_next(char *args
)
436 if (cur
+ 1 < mbox
->n
) {
446 static void prompt(void)
448 write(STDOUT_FILENO
, "? ", 2);
451 static void loop(void)
456 while (read_line(line
, sizeof(line
)) > 0) {
457 char *args
= cut_cmd(cmd
, line
);
458 int len
= strlen(cmd
);
464 if (!strncmp("page", cmd
, len
))
466 if (!strncmp("next", cmd
, len
))
468 if (!strncmp("header", cmd
, len
))
470 if (!strncmp("file", cmd
, len
) || !strncmp("folder", cmd
, len
))
472 if (!strncmp("inc", cmd
, len
))
474 if (!strncmp("z", cmd
, len
))
476 if (!strncmp("delete", cmd
, len
) || !strncmp("rm", cmd
, len
))
478 if (!strncmp("undelete", cmd
, len
))
480 if (!strncmp("quit", cmd
, len
)) {
481 if (mbox_write(mbox
) == -1)
486 if (!strncmp("xit", cmd
, len
) || !strncmp("exit", cmd
, len
))
492 int main(int argc
, char *argv
[])
495 char *filename
= NULL
;
497 if (!strcmp("-f", argv
[i
]))
498 filename
= argv
[++i
];
500 char path
[MAXPATHLEN
];
501 if (mbox_path(path
, filename
) && is_mbox(path
)) {