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