7 #define LEN(a) (sizeof(a) / sizeof((a)[0]))
9 static int uc_len(char *s
)
11 int c
= (unsigned char) s
[0];
27 static int uc_wid(char *s
)
32 static char *msg_dec(char *msg
, long msz
, char *hdr
)
34 char *val
= msg_get(msg
, msz
, hdr
);
39 val_len
= hdrlen(val
, msg
+ msz
- val
) - 1;
40 buf
= malloc(val_len
+ 1);
41 memcpy(buf
, val
, val_len
);
43 ret
= msg_hdrdec(buf
);
48 static int msg_stat(char *msg
, long msz
)
50 char *val
= msg_get(msg
, msz
, "status:");
53 val
+= strlen("status:");
54 while (isspace((unsigned char) val
[0]))
59 static char *fieldformat(char *msg
, long msz
, char *hdr
, int wid
)
63 char *val
, *val0
, *end
;
64 val
= msg_dec(msg
, msz
, hdr
[0] == '~' ? hdr
+ 1 : hdr
);
70 end
= strchr(val
, '\0');
73 while (val
< end
&& isspace((unsigned char) *val
))
76 if (!strcmp("~subject:", hdr
)) {
77 while (startswith(val
, "re:") || startswith(val
, "fwd:")) {
80 val
= strchr(val
, ':') + 1;
81 while (val
< end
&& isspace((unsigned char) *val
))
85 while (val
< end
&& (wid
<= 0 || dst_wid
< wid
)) {
88 int c
= (unsigned char) *val
;
89 sbuf_chr(dst
, isblank(c
) || !isprint(c
) ? ' ' : c
);
91 sbuf_mem(dst
, val
, l
);
93 dst_wid
+= uc_wid(val
);
97 while (dst_wid
++ < wid
)
100 return sbuf_done(dst
);
103 static char *segment(char *d
, char *s
, int m
)
105 char *r
= strchr(s
, m
);
106 char *e
= r
? r
+ 1 : strchr(s
, '\0');
113 "usage: neatmail mk [options] mbox\n\n"
115 " -0 fmt \tmessage first line format (e.g., 20from:40subject:)\n"
116 " -1 fmt \tmessage second line format\n"
117 " -sd \tsort by receiving date\n"
118 " -st \tsort by threads\n"
119 " -f n \tthe first message to list\n";
121 static int sort_mails(struct mbox
*mbox
, int *mids
, int *levs
);
127 char *ln
[4] = {"18from:40~subject:"};
131 for (i
= 0; argv
[i
] && argv
[i
][0] == '-'; i
++) {
132 if (argv
[i
][1] == 'f') {
133 first
= atoi(argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
]);
136 if (argv
[i
][1] == 's') {
137 char t
= (argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
])[0];
138 sort
= t
== 't' ? 2 : 1;
141 if (argv
[i
][1] == '0' || argv
[i
][1] == '1') {
142 int idx
= argv
[i
][1] - '0';
143 ln
[idx
] = argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
];
151 mbox
= mbox_open(argv
[i
]);
153 fprintf(stderr
, "neatmail: cannot open <%s>\n", argv
[i
]);
156 mids
= malloc(mbox_len(mbox
) * sizeof(mids
[0]));
157 levs
= malloc(mbox_len(mbox
) * sizeof(levs
[0]));
158 for (i
= 0; i
< mbox_len(mbox
); i
++)
160 for (i
= 0; i
< mbox_len(mbox
); i
++)
163 sort_mails(mbox
, mids
, sort
== 2 ? levs
: NULL
);
164 for (i
= first
; i
< mbox_len(mbox
); i
++) {
167 mbox_get(mbox
, mids
[i
], &msg
, &msz
);
168 printf("%c%04d", msg_stat(msg
, msz
), mids
[i
]);
169 for (j
= 0; ln
[j
]; j
++) {
171 char *tok
= malloc(strlen(ln
[j
]) + 1);
174 while ((cln
= segment(tok
, cln
, ':')) && tok
[0]) {
179 while (isdigit((unsigned char) *hdr
))
182 if (!strcmp("~subject:", hdr
))
183 for (k
= 0; k
< levs
[i
]; k
++, wid
--)
185 val
= fieldformat(msg
, msz
, hdr
, wid
);
199 /* sorting messages */
204 char *id
; /* message-id header value */
205 int id_len
; /* message-id length */
206 char *rply
; /* reply-to header value */
207 int rply_len
; /* reply-to length */
208 int date
; /* message receiving date */
209 int depth
; /* depth of message in the thread */
210 int oidx
; /* the original index of the message */
217 static int id_cmp(char *i1
, int l1
, char *i2
, int l2
)
221 return strncmp(i1
, i2
, l1
);
224 static int msgcmp_id(void *v1
, void *v2
)
226 struct msg
*t1
= *(struct msg
**) v1
;
227 struct msg
*t2
= *(struct msg
**) v2
;
228 return id_cmp(t1
->id
, t1
->id_len
, t2
->id
, t2
->id_len
);
231 static int msgcmp_date(void *v1
, void *v2
)
233 struct msg
*t1
= *(struct msg
**) v1
;
234 struct msg
*t2
= *(struct msg
**) v2
;
235 return t1
->date
== t2
->date
? t1
->oidx
- t2
->oidx
: t1
->date
- t2
->date
;
238 static char *months
[] = {
239 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
240 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
243 static char *readtok(char *s
, char *d
)
245 while (*s
== ' ' || *s
== ':')
247 while (!strchr(" \t\r\n:", *s
))
253 static int fromdate(char *s
)
256 int year
, mon
, day
, hour
, min
, sec
;
258 /* parsing "From ali Tue Apr 16 20:18:40 2013" */
259 s
= readtok(s
, tok
); /* From */
260 s
= readtok(s
, tok
); /* username */
261 s
= readtok(s
, tok
); /* day of week */
262 s
= readtok(s
, tok
); /* month name */
264 for (i
= 0; i
< LEN(months
); i
++)
265 if (!strcmp(months
[i
], tok
))
267 s
= readtok(s
, tok
); /* day of month */
269 s
= readtok(s
, tok
); /* hour */
271 s
= readtok(s
, tok
); /* minute */
273 s
= readtok(s
, tok
); /* seconds */
275 s
= readtok(s
, tok
); /* year */
277 return ((year
- 1970) * 400 + mon
* 31 + day
) * 24 * 3600 +
278 hour
* 3600 + min
* 60 + sec
;
281 static struct msg
*msg_byid(struct msg
**msgs
, int n
, char *id
, int len
)
287 int d
= id_cmp(id
, len
, msgs
[m
]->id
, msgs
[m
]->id_len
);
298 static void msgs_tree(struct msg
**all
, struct msg
**sorted_id
, int n
)
301 for (i
= 0; i
< n
; i
++) {
302 struct msg
*msg
= all
[i
];
306 dad
= msg_byid(sorted_id
, n
, msg
->rply
, msg
->rply_len
);
310 msg
->depth
= dad
->depth
+ 1;
311 if (!msg
->parent
->head
)
312 msg
->parent
->head
= msg
;
314 msg
->parent
->tail
->next
= msg
;
315 msg
->parent
->tail
= msg
;
319 static void msg_init(struct msg
*msg
)
321 char *id_hdr
= msg_get(msg
->msg
, msg
->msglen
, "Message-ID:");
322 char *rply_hdr
= msg_get(msg
->msg
, msg
->msglen
, "In-Reply-To:");
323 char *end
= msg
->msg
+ msg
->msglen
;
325 int len
= hdrlen(id_hdr
, end
- id_hdr
);
326 char *beg
= memchr(id_hdr
, '<', len
);
327 char *end
= beg
? memchr(id_hdr
, '>', len
) : NULL
;
332 msg
->id_len
= end
- beg
;
336 int len
= hdrlen(rply_hdr
, end
- rply_hdr
);
337 char *beg
= memchr(rply_hdr
, '<', len
);
338 char *end
= beg
? memchr(rply_hdr
, '>', len
) : NULL
;
343 msg
->rply_len
= end
- beg
;
346 msg
->date
= fromdate(msg
->msg
);
349 static struct msg
**put_msg(struct msg
**sorted
, struct msg
*msg
)
351 struct msg
*cur
= msg
->head
;
354 sorted
= put_msg(sorted
, cur
);
360 static void msgs_sort(struct msg
**sorted
, struct msg
**msgs
, int n
)
363 for (i
= 0; i
< n
; i
++)
364 if (!msgs
[i
]->parent
)
365 sorted
= put_msg(sorted
, msgs
[i
]);
368 static int sort_mails(struct mbox
*mbox
, int *mids
, int *levs
)
370 int n
= mbox_len(mbox
);
371 struct msg
*msgs
= malloc(n
* sizeof(*msgs
));
372 struct msg
**sorted_date
= malloc(n
* sizeof(*sorted_date
));
373 struct msg
**sorted_id
= malloc(n
* sizeof(*sorted_id
));
374 struct msg
**sorted
= malloc(n
* sizeof(*sorted
));
376 if (!msgs
|| !sorted_date
|| !sorted_id
) {
383 memset(msgs
, 0, n
* sizeof(*msgs
));
384 for (i
= 0; i
< n
; i
++) {
385 struct msg
*msg
= &msgs
[i
];
387 mbox_get(mbox
, i
, &msg
->msg
, &msg
->msglen
);
389 sorted_date
[i
] = msg
;
392 qsort(sorted_date
, n
, sizeof(*sorted_date
), (void *) msgcmp_date
);
393 qsort(sorted_id
, n
, sizeof(*sorted_id
), (void *) msgcmp_id
);
395 msgs_tree(sorted_date
, sorted_id
, n
);
396 msgs_sort(sorted
, sorted_date
, n
);
397 for (i
= 0; i
< n
; i
++)
398 mids
[i
] = sorted
[i
]->oidx
;
400 for (i
= 0; i
< n
; i
++)
401 levs
[i
] = sorted
[i
]->depth
;