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