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