1 #include <sys/stat.h>
2 #include <sys/types.h>
3
4 #include <ctype.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <strings.h>
9 #include <time.h>
10 #include <unistd.h>
11
12 #include "blaze822.h"
13 #include "xpledge.h"
14
15 struct mail {
16 char *file;
17 long idx;
18 char *from;
19 char *subj;
20 time_t date;
21 time_t mtime;
22 off_t size;
23 };
24
25 struct mail *mails;
26 ssize_t mailalloc = 1024;
27
28 int idx;
29 int rflag;
30
31 static int (*sortorders[16])(const void *, const void *);
32 int order_idx;
33
34 int mystrverscmp(const char *, const char *);
35
36 char *
fetch_subj(char * file)37 fetch_subj(char *file)
38 {
39 struct message *msg = blaze822(file);
40 if (!msg)
41 return " (error)";
42 char *v = blaze822_hdr(msg, "subject");
43 if (!v) {
44 blaze822_free(msg);
45 return " (no subject)";
46 }
47
48 char *ov = 0;
49 char *s;
50 while (v != ov) {
51 while (*v == ' ')
52 v++;
53 if (strncasecmp(v, "Re:", 3) == 0)
54 v += 3;
55 if (strncasecmp(v, "Aw:", 3) == 0)
56 v += 3;
57 if (strncasecmp(v, "Sv:", 3) == 0)
58 v += 3;
59 if (strncasecmp(v, "Wg:", 3) == 0)
60 v += 3;
61 if (strncasecmp(v, "Fwd:", 4) == 0)
62 v += 4;
63 // XXX skip [prefix]?
64 ov = v;
65 }
66 s = strdup(v);
67
68 blaze822_free(msg);
69 return s;
70 }
71
72 char *
fetch_from(char * file)73 fetch_from(char *file)
74 {
75 char *from = " (unknown)";
76 struct message *msg = blaze822(file);
77 if (!msg)
78 return " (error)";
79 char *v = blaze822_hdr(msg, "from");
80 if (v) {
81 char *disp, *addr;
82 blaze822_addr(v, &disp, &addr);
83 if (disp)
84 from = strdup(disp);
85 else if (addr)
86 from = strdup(addr);
87 }
88
89 blaze822_free(msg);
90 return from;
91 }
92
93 time_t
fetch_date(char * file)94 fetch_date(char *file)
95 {
96 time_t t = -1;
97 struct message *msg = blaze822(file);
98 if (!msg)
99 return -1;
100 char *v = blaze822_hdr(msg, "date");
101 if (v)
102 t = blaze822_date(v);
103 blaze822_free(msg);
104 return t;
105 }
106
107 time_t
fetch_mtime(char * file)108 fetch_mtime(char *file)
109 {
110 struct stat st;
111 if (stat(file, &st) < 0)
112 return -1;
113 return st.st_mtime;
114 }
115
116 off_t
fetch_size(char * file)117 fetch_size(char *file)
118 {
119 struct stat st;
120 if (stat(file, &st) < 0)
121 return 0;
122 return st.st_size;
123 }
124
125 int
subjorder(const void * a,const void * b)126 subjorder(const void *a, const void *b)
127 {
128 struct mail *ia = (struct mail *)a;
129 struct mail *ib = (struct mail *)b;
130
131 if (!ia->subj)
132 ia->subj = fetch_subj(ia->file);
133 if (!ib->subj)
134 ib->subj = fetch_subj(ib->file);
135
136 return strcasecmp(ia->subj, ib->subj);
137 }
138
139 int
fromorder(const void * a,const void * b)140 fromorder(const void *a, const void *b)
141 {
142 struct mail *ia = (struct mail *)a;
143 struct mail *ib = (struct mail *)b;
144
145 if (!ia->from)
146 ia->from = fetch_from(ia->file);
147 if (!ib->from)
148 ib->from = fetch_from(ib->file);
149
150 return strcasecmp(ia->from, ib->from);
151 }
152
153 int
sizeorder(const void * a,const void * b)154 sizeorder(const void *a, const void *b)
155 {
156 struct mail *ia = (struct mail *)a;
157 struct mail *ib = (struct mail *)b;
158
159 if (!ia->size)
160 ia->size = fetch_size(ia->file);
161 if (!ib->size)
162 ib->size = fetch_size(ib->file);
163
164 if (ia->size > ib->size)
165 return 1;
166 else if (ia->size < ib->size)
167 return -1;
168 return 0;
169 }
170
171 int
mtimeorder(const void * a,const void * b)172 mtimeorder(const void *a, const void *b)
173 {
174 struct mail *ia = (struct mail *)a;
175 struct mail *ib = (struct mail *)b;
176
177 if (!ia->mtime)
178 ia->mtime = fetch_mtime(ia->file);
179 if (!ib->mtime)
180 ib->mtime = fetch_mtime(ib->file);
181
182 if (ia->mtime > ib->mtime)
183 return 1;
184 else if (ia->mtime < ib->mtime)
185 return -1;
186 return 0;
187 }
188
189 int
dateorder(const void * a,const void * b)190 dateorder(const void *a, const void *b)
191 {
192 struct mail *ia = (struct mail *)a;
193 struct mail *ib = (struct mail *)b;
194
195 if (!ia->date)
196 ia->date = fetch_date(ia->file);
197 if (!ib->date)
198 ib->date = fetch_date(ib->file);
199
200 if (ia->date > ib->date)
201 return 1;
202 else if (ia->date < ib->date)
203 return -1;
204 return 0;
205 }
206
207 int
fileorder(const void * a,const void * b)208 fileorder(const void *a, const void *b)
209 {
210 struct mail *ia = (struct mail *)a;
211 struct mail *ib = (struct mail *)b;
212
213 return mystrverscmp(ia->file, ib->file);
214 }
215
216 int
unreadorder(const void * a,const void * b)217 unreadorder(const void *a, const void *b)
218 {
219 struct mail *ia = (struct mail *)a;
220 struct mail *ib = (struct mail *)b;
221
222 char *fa = strstr(ia->file, ":2,");
223 char *fb = strstr(ib->file, ":2,");
224
225 int unreada = fa ? !strchr(fa, 'S') : 0;
226 int unreadb = fb ? !strchr(fb, 'S') : 0;
227
228 return unreada - unreadb;
229 }
230
231 int
flaggedorder(const void * a,const void * b)232 flaggedorder(const void *a, const void *b)
233 {
234 struct mail *ia = (struct mail *)a;
235 struct mail *ib = (struct mail *)b;
236
237 char *fa = strstr(ia->file, ":2,");
238 char *fb = strstr(ib->file, ":2,");
239
240 int unreada = fa ? !!strchr(fa, 'F') : 0;
241 int unreadb = fb ? !!strchr(fb, 'F') : 0;
242
243 return unreadb - unreada;
244 }
245
246 int
idxorder(const void * a,const void * b)247 idxorder(const void *a, const void *b)
248 {
249 struct mail *ia = (struct mail *)a;
250 struct mail *ib = (struct mail *)b;
251
252 if (ia->idx > ib->idx)
253 return 1;
254 else if (ia->idx < ib->idx)
255 return -1;
256 else
257 return 0;
258 }
259
260 void
add(char * file)261 add(char *file)
262 {
263 if (idx >= mailalloc) {
264 mailalloc *= 2;
265 if (mailalloc < 0)
266 exit(-1);
267 mails = realloc(mails, sizeof (struct mail) * mailalloc);
268 if (!mails)
269 exit(-1);
270 memset(mails+mailalloc/2, 0, sizeof (struct mail) * mailalloc/2);
271 }
272 if (!mails)
273 exit(-1);
274 mails[idx].file = strdup(file);
275 mails[idx].idx = idx;
276 idx++;
277 }
278
279 int
order(const void * a,const void * b)280 order(const void *a, const void *b)
281 {
282 int i;
283 for (i = 0; i < order_idx; i++) {
284 int r = (sortorders[i])(a, b);
285 if (r != 0)
286 return r;
287 }
288 return idxorder(a, b);
289 }
290
291 void
addorder(int (* sortorder)(const void *,const void *))292 addorder(int (*sortorder)(const void *, const void *))
293 {
294 if (order_idx < (int)(sizeof sortorders / sizeof sortorders[0]))
295 sortorders[order_idx++] = sortorder;
296 }
297
298 int
main(int argc,char * argv[])299 main(int argc, char *argv[])
300 {
301 int c, i;
302
303 while ((c = getopt(argc, argv, "fdsFMSUIr")) != -1)
304 switch (c) {
305 case 'f': addorder(fromorder); break;
306 case 'd': addorder(dateorder); break;
307 case 's': addorder(subjorder); break;
308 case 'F': addorder(fileorder); break;
309 case 'M': addorder(mtimeorder); break;
310 case 'S': addorder(sizeorder); break;
311 case 'U': addorder(unreadorder); break;
312 case 'I': addorder(flaggedorder); break;
313 case 'r': rflag = !rflag; break;
314 default:
315 fprintf(stderr,
316 "Usage: msort [-r] [-fdsFMSUI] [msgs...]\n");
317 exit(1);
318 }
319
320 xpledge("stdio rpath", "");
321
322 mails = calloc(sizeof (struct mail), mailalloc);
323 if (!mails)
324 exit(-1);
325
326 if (argc == optind && isatty(0))
327 blaze822_loop1(":", add);
328 else
329 blaze822_loop(argc-optind, argv+optind, add);
330
331 qsort(mails, idx, sizeof (struct mail), order);
332
333 if (rflag)
334 for (i = idx-1; i >= 0; i--)
335 printf("%s\n", mails[i].file);
336 else
337 for (i = 0; i < idx; i++)
338 printf("%s\n", mails[i].file);
339
340 return 0;
341 }
342