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