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