15 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
16 #define MAILBUF (1 << 16)
17 #define MAXLINE (1 << 7)
18 #define BUFSIZE (1 << 12)
20 static struct mbox
*mbox
;
21 static struct sort
*sort
;
23 static int sel
[MAXMAILS
];
27 static int read_line(char *dst
, int size
)
29 static char buf
[BUFSIZE
];
34 int cur_len
= MIN(len
- cur
, size
- nw
- 1);
35 char *nl
= memchr(buf
+ cur
, '\n', cur_len
);
36 int nr
= nl
? nl
- buf
- cur
+ 1 : cur_len
;
38 memcpy(dst
+ nw
, buf
+ cur
, nr
);
43 if (nl
|| nw
== size
- 1)
46 if ((len
= read(STDIN_FILENO
, buf
, BUFSIZE
)) <= 0)
52 static char *till_eol(char *r
, int len
, char *s
)
55 while (r
< d
&& s
&& *s
&& *s
!= '\r' && *s
!= '\n')
60 static int msg_num(char *num
)
63 if (!*num
|| !strcmp(".", num
))
67 if (!strcmp("$", num
))
70 n
= cur
- (*(num
+ 1) ? atoi(num
+ 1) : 1);
72 n
= cur
+ (*(num
+ 1) ? atoi(num
+ 1) : 1);
73 if (!strcmp(",", num
)) {
75 while (++i
< sort
->n
) {
76 int stat
= sort
->mails
[i
]->stat
;
77 if (!(stat
& STAT_READ
) && (stat
& STAT_NEW
)) {
83 if (n
< 0 || n
>= mbox
->n
)
88 static int stat_char(struct mail
*mail
)
90 if (mail
->stat
& STAT_NEW
)
91 return mail
->stat
& STAT_READ
? 'R' : 'N';
93 return mail
->stat
& STAT_READ
? ' ' : 'U';
96 static int search(char *args
, int all
)
98 static char hdr_name
[MAXLINE
];
99 static char hdr_val
[MAXLINE
];
101 char *beg
= strchr(args
, '(');
102 char *spc
= beg
? strchr(beg
, ' ') : NULL
;
103 char *end
= spc
? strchr(spc
, ')') : NULL
;
105 if (beg
&& (!end
|| !spc
)) {
109 int hdr_len
= spc
- beg
- 1;
110 put_mem(hdr_name
, beg
+ 1, hdr_len
);
111 hdr_name
[hdr_len
] = '\0';
112 while (isspace(*spc
))
114 put_mem(hdr_val
, spc
, end
- spc
);
115 hdr_val
[end
- spc
] = '\0';
116 stat
= isalpha(*(args
+ 1)) ? toupper(*(args
+ 1)) : 0;
118 for (i
= all
? 0 : cur
+ 1; i
< sort
->n
; i
++) {
119 char *hdr
= mail_hdr(sort
->mails
[i
], hdr_name
);
120 char *s
= hdr
? hdr
+ strlen(hdr_name
) : NULL
;
121 char *d
= hdr
? hdr
+ hdr_len(hdr
) : NULL
;
123 if (!strncmp(hdr_val
, s
, strlen(hdr_val
)) &&
124 (!stat
|| stat_char(sort
->mails
[i
]) == stat
)) {
132 s
= memchr(s
+ 1, hdr_val
[0], d
- s
- 1);
138 static int sel_msgs(char *args
, int all
)
143 while (isspace(*args
))
146 return search(args
, all
);
147 args
= cut_word(num
, args
);
152 if (*num
&& (dash
= strchr(num
+ 1, '-'))) {
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);
158 } else if (!strcmp("*", num
)) {
165 if (beg
!= -1 && end
!= -1) {
170 for (i
= beg
; i
<= end
; i
++)
173 args
= cut_word(num
, args
);
180 static void show_mail(char *args
, int filthdr
)
184 char *pg_args
[] = {PAGER
, NULL
};
186 if (!sel_msgs(args
, 0))
188 mail
= sort
->mails
[cur
];
189 mail
->stat
|= STAT_READ
;
191 s
+= mail_head(mail
, buf
, sizeof(buf
),
192 hdr_filt
, ARRAY_SIZE(hdr_filt
));
193 s
= put_mem(s
, mail
->body
, MIN(sizeof(buf
) - (s
- buf
),
196 s
= put_mem(s
, mail
->head
, mail
->len
);
198 exec_pipe(PAGER
, pg_args
, buf
, s
- buf
);
201 static void cmd_page(char *args
)
206 static void cmd_cat(char *args
)
211 static char *cut_cmd(char *dst
, char *s
)
213 while (isalpha(*s
)) {
220 static char *put_hdr(struct mail
*mail
, char *name
, int w
, char *s
)
222 char *hdr
= mail_hdr(mail
, name
);
223 return till_eol(s
, w
, hdr
? hdr
+ strlen(name
) + 1 : NULL
);
226 static void cmd_head(char *args
)
230 if (!sel_msgs(args
, 0))
232 beg
= cur
/ NHEAD
* NHEAD
;
233 end
= MIN(beg
+ NHEAD
, mbox
->n
);
234 for (i
= beg
; i
< end
; i
++) {
235 struct mail
*mail
= sort
->mails
[i
];
238 *s
++ = i
== cur
? '>' : ' ';
239 *s
++ = mail
->stat
& STAT_DEL
? 'D' : ' ';
240 *s
++ = stat_char(mail
);
241 s
= put_int(s
, i
, DIGITS
);
244 s
= put_hdr(mail
, "From:", 16, s
);
247 s
= sort_draw(sort
, i
, s
, WIDTH
- (s
- fmt
));
248 s
= put_hdr(mail
, "Subject:", WIDTH
- (s
- fmt
), s
);
250 write(STDOUT_FILENO
, fmt
, s
- fmt
);
254 static void cmd_z(char *args
)
260 page
= MIN(cur
+ NHEAD
, mbox
->n
) / NHEAD
;
262 page
= cur
/ NHEAD
- (*(num
+ 1) ? atoi(num
+ 1) : 1);
264 page
= cur
/ NHEAD
+ (*(num
+ 1) ? atoi(num
+ 1) : 1);
266 page
= mbox
->n
/ NHEAD
;
269 if (page
>= 0 && page
* NHEAD
< mbox
->n
) {
275 static void cmd_del(char *args
)
278 if (!sel_msgs(args
, 1))
280 for (i
= 0; i
< nsel
; i
++)
281 sort
->mails
[sel
[i
]]->stat
|= STAT_DEL
;
284 static void cmd_undel(char *args
)
287 if (!sel_msgs(args
, 1))
289 for (i
= 0; i
< nsel
; i
++)
290 sort
->mails
[sel
[i
]]->stat
&= ~STAT_DEL
;
293 static void print(char *s
)
295 write(STDOUT_FILENO
, s
, strlen(s
));
298 static int is_mbox(char *filename
)
302 return S_ISREG(st
.st_mode
);
305 static char *filename(char *path
)
307 char *slash
= strrchr(path
, '/');
308 return slash
? slash
+ 1 : path
;
311 static void sum_mbox(void)
319 s
= put_str(s
, filename(mbox
->path
));
320 s
= put_str(s
, ": ");
321 s
= put_int(s
, mbox
->n
, DIGITS
);
322 s
= put_str(s
, " msgs ");
323 for (i
= 0; i
< mbox
->n
; i
++) {
324 if (!(mbox
->mails
[i
].stat
& STAT_READ
)) {
326 if (mbox
->mails
[i
].stat
& STAT_NEW
)
329 if (mbox
->mails
[i
].stat
& STAT_DEL
)
333 s
= put_int(s
, new, DIGITS
);
334 s
= put_str(s
, " new ");
337 s
= put_int(s
, unread
, DIGITS
);
338 s
= put_str(s
, " unread ");
341 s
= put_int(s
, del
, DIGITS
);
342 s
= put_str(s
, " del ");
344 s
= put_str(s
, "\n");
348 static void open_mbox(char *filename
)
350 mbox
= mbox_alloc(filename
);
351 sort
= sort_alloc(mbox
, THREADED
? SORT_THREAD
: 0);
356 static void close_mbox(void)
362 static void mbox_old(struct mbox
*mbox
)
365 for (i
= 0; i
< mbox
->n
; i
++) {
366 struct mail
*mail
= &mbox
->mails
[i
];
367 mail
->stat
= (mail
->stat
& ~STAT_NEW
) | STAT_OLD
;
371 static int has_mail(char *path
)
374 if (stat(path
, &st
) == -1)
376 return st
.st_mtime
> st
.st_atime
;
379 static int mbox_path(char *path
, char *addr
)
384 s
= put_str(s
, FOLDER
);
385 s
= put_str(s
, addr
+ 1);
388 for (i
= 0; i
< ARRAY_SIZE(boxes
); i
++) {
389 if (has_mail(boxes
[i
])) {
390 s
= put_str(s
, boxes
[i
]);
395 if (!strcmp(".", addr
) && mbox
)
396 s
= put_str(s
, mbox
->path
);
397 if (s
== path
&& *addr
)
398 s
= put_str(s
, addr
);
402 static void warn_nomem(void)
404 print("no mem for new msgs\n");
407 static void cmd_fold(char *args
)
409 char filename
[MAXLINE
];
410 args
= cut_word(filename
, args
);
412 char path
[MAXPATHLEN
];
413 if (mbox_path(path
, filename
) && is_mbox(path
)) {
415 if (mbox_write(mbox
) == -1) {
427 static void cmd_news(char *args
)
432 for (i
= 0; i
< ARRAY_SIZE(boxes
); i
++) {
433 if (has_mail(boxes
[i
])) {
435 s
= put_str(s
, "\t");
436 s
= put_str(s
, filename(boxes
[i
]));
437 s
= put_str(s
, "\n");
443 static void cmd_inc(char *args
)
446 int new = mbox_inc(mbox
);
453 s
= put_int(s
, new, DIGITS
);
454 s
= put_str(s
, " new\n");
459 static void cmd_next(char *args
)
464 if (cur
+ 1 < mbox
->n
) {
474 static void copy_mail(char *args
, int del
)
479 char *spc
= strrchr(args
, ' ');
483 if (!sel_msgs(args
, 1) || !*last
)
485 mbox_path(path
, last
);
486 fd
= open(path
, O_WRONLY
| O_APPEND
| O_CREAT
, S_IRUSR
| S_IWUSR
);
488 print("failed to open mbox for writing\n");
491 for (i
= 0; i
< nsel
; i
++) {
492 struct mail
*mail
= sort
->mails
[sel
[i
]];
493 mail_write(mail
, fd
);
495 mail
->stat
|= STAT_DEL
;
500 static void cmd_copy(char *args
)
505 static void cmd_move(char *args
)
510 static void compose(struct draft
*draft
)
512 char record
[MAXPATHLEN
] = "";
515 char *pg_args
[] = {PAGER
, NULL
};
517 mbox_path(record
, RECORD
);
519 strcpy(record
, mbox
->path
);
520 while (read_line(line
, sizeof(line
)) > 0) {
522 if (!strcmp("~e", cmd
))
524 if (!strcmp("~.", cmd
)) {
526 draft_save(draft
, record
);
530 if (!strcmp("~p", cmd
))
531 exec_pipe(PAGER
, pg_args
, draft
->mail
, draft
->len
);
532 if (!strcmp("~q", cmd
) || !strcmp("~x", cmd
))
537 static void cmd_mail(char *args
)
540 while (isspace(*args
))
542 draft_init(&draft
, *args
? &args
: NULL
, *args
? 1 : 0, NULL
);
546 static void cmd_reply(char *args
)
549 if (!sel_msgs(args
, 0))
551 draft_reply(&draft
, sort
->mails
[cur
]);
555 static void prompt(void)
557 write(STDOUT_FILENO
, "? ", 2);
560 static void cmd_quit(char *args
)
563 if (mbox_write(mbox
) == -1)
569 static void cmd_exit(char *args
)
576 void (*cmd
)(char *args
);
580 {"header", cmd_head
},
583 {"folder", cmd_fold
},
591 {"reply", cmd_reply
},
595 {"undelete", cmd_undel
},
602 static void loop(void)
607 while (read_line(line
, sizeof(line
)) > 0) {
608 char *args
= cut_cmd(cmd
, line
);
609 int len
= strlen(cmd
);
614 for (i
= 0; i
< ARRAY_SIZE(cmds
); i
++)
615 if (!strncmp(cmds
[i
].name
, cmd
, len
)) {
626 int main(int argc
, char *argv
[])
629 char *filename
= NULL
;
632 if (argv
[i
][0] != '-')
634 if (!strcmp("-f", argv
[i
]))
635 filename
= argv
[++i
];
636 if (!strcmp("-s", argv
[i
]))
640 char path
[MAXPATHLEN
];
641 if (mbox_path(path
, filename
) && is_mbox(path
)) {
648 draft_init(&draft
, argv
+ i
, argc
- i
, subj
);