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