mutt stable branch with some hacks
1/*
2 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19#if HAVE_CONFIG_H
20# include "config.h"
21#endif
22
23#include "mutt.h"
24#include "sort.h"
25#include "mutt_idna.h"
26
27#include <stdlib.h>
28#include <string.h>
29#include <ctype.h>
30#include <unistd.h>
31
32#define SORTCODE(x) ((option(OPTAUXSORT) ? SortAux : Sort) & SORT_REVERSE) ? -(x) : x
33
34/* function to use as discriminator when normal sort method is equal */
35static sort_t *AuxSort = NULL;
36
37#define AUXSORT(code,a,b) \
38 if (!code && AuxSort && !option(OPTAUXSORT)) \
39 { \
40 set_option(OPTAUXSORT); \
41 code = AuxSort(a,b); \
42 unset_option(OPTAUXSORT); \
43 if (code) \
44 return (code); \
45 } \
46 if (!code) \
47 code = (*((HEADER **)a))->index - (*((HEADER **)b))->index;
48
49static int compare_score (const void *a, const void *b)
50{
51 HEADER **pa = (HEADER **) a;
52 HEADER **pb = (HEADER **) b;
53 int result = (*pb)->score - (*pa)->score; /* note that this is reverse */
54 AUXSORT(result,a,b);
55 return (SORTCODE (result));
56}
57
58static int compare_size (const void *a, const void *b)
59{
60 HEADER **pa = (HEADER **) a;
61 HEADER **pb = (HEADER **) b;
62 int result = (*pa)->content->length - (*pb)->content->length;
63 AUXSORT(result,a,b);
64 return (SORTCODE (result));
65}
66
67static int compare_date_sent (const void *a, const void *b)
68{
69 HEADER **pa = (HEADER **) a;
70 HEADER **pb = (HEADER **) b;
71 int result = (*pa)->date_sent - (*pb)->date_sent;
72 AUXSORT(result,a,b);
73 return (SORTCODE (result));
74}
75
76static int compare_subject (const void *a, const void *b)
77{
78 HEADER **pa = (HEADER **) a;
79 HEADER **pb = (HEADER **) b;
80 int rc;
81
82 if (!(*pa)->env->real_subj)
83 {
84 if (!(*pb)->env->real_subj)
85 rc = compare_date_sent (pa, pb);
86 else
87 rc = -1;
88 }
89 else if (!(*pb)->env->real_subj)
90 rc = 1;
91 else
92 rc = mutt_strcasecmp ((*pa)->env->real_subj, (*pb)->env->real_subj);
93 AUXSORT(rc,a,b);
94 return (SORTCODE (rc));
95}
96
97const char *mutt_get_name (ADDRESS *a)
98{
99 ADDRESS *ali;
100
101 if (a)
102 {
103 if (option (OPTREVALIAS) && (ali = alias_reverse_lookup (a)) && ali->personal)
104 return ali->personal;
105 else if (a->personal)
106 return a->personal;
107 else if (a->mailbox)
108 return (mutt_addr_for_display (a));
109 }
110 /* don't return NULL to avoid segfault when printing/comparing */
111 return ("");
112}
113
114static int compare_to (const void *a, const void *b)
115{
116 HEADER **ppa = (HEADER **) a;
117 HEADER **ppb = (HEADER **) b;
118 char fa[SHORT_STRING];
119 const char *fb;
120 int result;
121
122 strfcpy (fa, mutt_get_name ((*ppa)->env->to), SHORT_STRING);
123 fb = mutt_get_name ((*ppb)->env->to);
124 result = mutt_strncasecmp (fa, fb, SHORT_STRING);
125 AUXSORT(result,a,b);
126 return (SORTCODE (result));
127}
128
129static int compare_from (const void *a, const void *b)
130{
131 HEADER **ppa = (HEADER **) a;
132 HEADER **ppb = (HEADER **) b;
133 char fa[SHORT_STRING];
134 const char *fb;
135 int result;
136
137 strfcpy (fa, mutt_get_name ((*ppa)->env->from), SHORT_STRING);
138 fb = mutt_get_name ((*ppb)->env->from);
139 result = mutt_strncasecmp (fa, fb, SHORT_STRING);
140 AUXSORT(result,a,b);
141 return (SORTCODE (result));
142}
143
144static int compare_date_received (const void *a, const void *b)
145{
146 HEADER **pa = (HEADER **) a;
147 HEADER **pb = (HEADER **) b;
148 int result = (*pa)->received - (*pb)->received;
149 AUXSORT(result,a,b);
150 return (SORTCODE (result));
151}
152
153static int compare_order (const void *a, const void *b)
154{
155 HEADER **ha = (HEADER **) a;
156 HEADER **hb = (HEADER **) b;
157
158 /* no need to auxsort because you will never have equality here */
159 return (SORTCODE ((*ha)->index - (*hb)->index));
160}
161
162static int compare_spam (const void *a, const void *b)
163{
164 HEADER **ppa = (HEADER **) a;
165 HEADER **ppb = (HEADER **) b;
166 char *aptr, *bptr;
167 int ahas, bhas;
168 int result = 0;
169 double difference;
170
171 /* Firstly, require spam attributes for both msgs */
172 /* to compare. Determine which msgs have one. */
173 ahas = (*ppa)->env && (*ppa)->env->spam;
174 bhas = (*ppb)->env && (*ppb)->env->spam;
175
176 /* If one msg has spam attr but other does not, sort the one with first. */
177 if (ahas && !bhas)
178 return (SORTCODE(1));
179 if (!ahas && bhas)
180 return (SORTCODE(-1));
181
182 /* Else, if neither has a spam attr, presume equality. Fall back on aux. */
183 if (!ahas && !bhas)
184 {
185 AUXSORT(result, a, b);
186 return (SORTCODE(result));
187 }
188
189
190 /* Both have spam attrs. */
191
192 /* preliminary numeric examination */
193 difference = (strtod((*ppa)->env->spam->data, &aptr) -
194 strtod((*ppb)->env->spam->data, &bptr));
195
196 /* map double into comparison (-1, 0, or 1) */
197 result = (difference < 0.0 ? -1 : difference > 0.0 ? 1 : 0);
198
199 /* If either aptr or bptr is equal to data, there is no numeric */
200 /* value for that spam attribute. In this case, compare lexically. */
201 if ((aptr == (*ppa)->env->spam->data) || (bptr == (*ppb)->env->spam->data))
202 return (SORTCODE(strcmp(aptr, bptr)));
203
204 /* Otherwise, we have numeric value for both attrs. If these values */
205 /* are equal, then we first fall back upon string comparison, then */
206 /* upon auxiliary sort. */
207 if (result == 0)
208 {
209 result = strcmp(aptr, bptr);
210 AUXSORT(result, a, b);
211 }
212
213 return (SORTCODE(result));
214}
215
216int compare_label (const void *a, const void *b)
217{
218 HEADER **ppa = (HEADER **) a;
219 HEADER **ppb = (HEADER **) b;
220 int ahas, bhas, result = 0;
221
222 /* As with compare_spam, not all messages will have the x-label
223 * property. Blank X-Labels are treated as null in the index
224 * display, so we'll consider them as null for sort, too. */
225 ahas = (*ppa)->env && (*ppa)->env->x_label && *((*ppa)->env->x_label);
226 bhas = (*ppb)->env && (*ppb)->env->x_label && *((*ppb)->env->x_label);
227
228 /* First we bias toward a message with a label, if the other does not. */
229 if (ahas && !bhas)
230 return (SORTCODE(-1));
231 if (!ahas && bhas)
232 return (SORTCODE(1));
233
234 /* If neither has a label, use aux sort. */
235 if (!ahas && !bhas)
236 {
237 AUXSORT(result, a, b);
238 return (SORTCODE(result));
239 }
240
241 /* If both have a label, we just do a lexical compare. */
242 result = mutt_strcasecmp((*ppa)->env->x_label, (*ppb)->env->x_label);
243 return (SORTCODE(result));
244}
245
246sort_t *mutt_get_sort_func (int method)
247{
248 switch (method & SORT_MASK)
249 {
250 case SORT_RECEIVED:
251 return (compare_date_received);
252 case SORT_ORDER:
253 return (compare_order);
254 case SORT_DATE:
255 return (compare_date_sent);
256 case SORT_SUBJECT:
257 return (compare_subject);
258 case SORT_FROM:
259 return (compare_from);
260 case SORT_SIZE:
261 return (compare_size);
262 case SORT_TO:
263 return (compare_to);
264 case SORT_SCORE:
265 return (compare_score);
266 case SORT_SPAM:
267 return (compare_spam);
268 case SORT_LABEL:
269 return (compare_label);
270 default:
271 return (NULL);
272 }
273 /* not reached */
274}
275
276void mutt_sort_headers (CONTEXT *ctx, int init)
277{
278 int i;
279 HEADER *h;
280 THREAD *thread, *top;
281 sort_t *sortfunc;
282
283 unset_option (OPTNEEDRESORT);
284
285 if (!ctx)
286 return;
287
288 if (!ctx->msgcount)
289 {
290 /* this function gets called by mutt_sync_mailbox(), which may have just
291 * deleted all the messages. the virtual message numbers are not updated
292 * in that routine, so we must make sure to zero the vcount member.
293 */
294 ctx->vcount = 0;
295 ctx->vsize = 0;
296 mutt_clear_threads (ctx);
297 return; /* nothing to do! */
298 }
299
300 if (!ctx->quiet)
301 mutt_message _("Sorting mailbox...");
302
303 if (option (OPTNEEDRESCORE) && option (OPTSCORE))
304 {
305 for (i = 0; i < ctx->msgcount; i++)
306 mutt_score_message (ctx, ctx->hdrs[i], 1);
307 }
308 unset_option (OPTNEEDRESCORE);
309
310 if (option (OPTRESORTINIT))
311 {
312 unset_option (OPTRESORTINIT);
313 init = 1;
314 }
315
316 if (init && ctx->tree)
317 mutt_clear_threads (ctx);
318
319 if ((Sort & SORT_MASK) == SORT_THREADS)
320 {
321 AuxSort = NULL;
322 /* if $sort_aux changed after the mailbox is sorted, then all the
323 subthreads need to be resorted */
324 if (option (OPTSORTSUBTHREADS))
325 {
326 i = Sort;
327 Sort = SortAux;
328 if (ctx->tree)
329 ctx->tree = mutt_sort_subthreads (ctx->tree, 1);
330 Sort = i;
331 unset_option (OPTSORTSUBTHREADS);
332 }
333 mutt_sort_threads (ctx, init);
334 }
335 else if ((sortfunc = mutt_get_sort_func (Sort)) == NULL ||
336 (AuxSort = mutt_get_sort_func (SortAux)) == NULL)
337 {
338 mutt_error _("Could not find sorting function! [report this bug]");
339 mutt_sleep (1);
340 return;
341 }
342 else
343 qsort ((void *) ctx->hdrs, ctx->msgcount, sizeof (HEADER *), sortfunc);
344
345 /* adjust the virtual message numbers */
346 ctx->vcount = 0;
347 for (i = 0; i < ctx->msgcount; i++)
348 {
349 HEADER *cur = ctx->hdrs[i];
350 if (cur->virtual != -1 || (cur->collapsed && (!ctx->pattern || cur->limited)))
351 {
352 cur->virtual = ctx->vcount;
353 ctx->v2r[ctx->vcount] = i;
354 ctx->vcount++;
355 }
356 cur->msgno = i;
357 }
358
359 /* re-collapse threads marked as collapsed */
360 if ((Sort & SORT_MASK) == SORT_THREADS)
361 {
362 top = ctx->tree;
363 while ((thread = top) != NULL)
364 {
365 while (!thread->message)
366 thread = thread->child;
367 h = thread->message;
368
369 if (h->collapsed)
370 mutt_collapse_thread (ctx, h);
371 top = top->next;
372 }
373 mutt_set_virtual (ctx);
374 }
375
376 if (!ctx->quiet)
377 mutt_clear_error ();
378}