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