]> git.llucax.com Git - software/mutt-debian.git/blob - sort.c
Move Mutt with NNTP support to mutt-nntp package
[software/mutt-debian.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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 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 static 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 static 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 static 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 static 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
97   if (a)
98   {
99     if (option (OPTREVALIAS) && (ali = alias_reverse_lookup (a)) && ali->personal)
100       return ali->personal;
101     else if (a->personal)
102       return a->personal;
103     else if (a->mailbox)
104       return (mutt_addr_for_display (a));
105   }
106   /* don't return NULL to avoid segfault when printing/comparing */
107   return ("");
108 }
109
110 static int compare_to (const void *a, const void *b)
111 {
112   HEADER **ppa = (HEADER **) a;
113   HEADER **ppb = (HEADER **) b;
114   char fa[SHORT_STRING];
115   const char *fb;
116   int result;
117
118   strfcpy (fa, mutt_get_name ((*ppa)->env->to), SHORT_STRING);
119   fb = mutt_get_name ((*ppb)->env->to);
120   result = mutt_strncasecmp (fa, fb, SHORT_STRING);
121   AUXSORT(result,a,b);
122   return (SORTCODE (result));
123 }
124
125 static int compare_from (const void *a, const void *b)
126 {
127   HEADER **ppa = (HEADER **) a;
128   HEADER **ppb = (HEADER **) b;
129   char fa[SHORT_STRING];
130   const char *fb;
131   int result;
132
133   strfcpy (fa, mutt_get_name ((*ppa)->env->from), SHORT_STRING);
134   fb = mutt_get_name ((*ppb)->env->from);
135   result = mutt_strncasecmp (fa, fb, SHORT_STRING);
136   AUXSORT(result,a,b);
137   return (SORTCODE (result));
138 }
139
140 static int compare_date_received (const void *a, const void *b)
141 {
142   HEADER **pa = (HEADER **) a;
143   HEADER **pb = (HEADER **) b;
144   int result = (*pa)->received - (*pb)->received;
145   AUXSORT(result,a,b);
146   return (SORTCODE (result));
147 }
148
149 static int compare_order (const void *a, const void *b)
150 {
151   HEADER **ha = (HEADER **) a;
152   HEADER **hb = (HEADER **) b;
153
154   /* no need to auxsort because you will never have equality here */
155   return (SORTCODE ((*ha)->index - (*hb)->index));
156 }
157
158 static int compare_spam (const void *a, const void *b)
159 {
160   HEADER **ppa = (HEADER **) a;
161   HEADER **ppb = (HEADER **) b;
162   char   *aptr, *bptr;
163   int     ahas, bhas;
164   int     result = 0;
165   double  difference;
166
167   /* Firstly, require spam attributes for both msgs */
168   /* to compare. Determine which msgs have one.     */
169   ahas = (*ppa)->env && (*ppa)->env->spam;
170   bhas = (*ppb)->env && (*ppb)->env->spam;
171
172   /* If one msg has spam attr but other does not, sort the one with first. */
173   if (ahas && !bhas)
174     return (SORTCODE(1));
175   if (!ahas && bhas)
176     return (SORTCODE(-1));
177
178   /* Else, if neither has a spam attr, presume equality. Fall back on aux. */
179   if (!ahas && !bhas)
180   {
181     AUXSORT(result, a, b);
182     return (SORTCODE(result));
183   }
184
185
186   /* Both have spam attrs. */
187
188   /* preliminary numeric examination */
189   difference = (strtod((*ppa)->env->spam->data, &aptr) -
190                 strtod((*ppb)->env->spam->data, &bptr));
191
192   /* map double into comparison (-1, 0, or 1) */
193   result = (difference < 0.0 ? -1 : difference > 0.0 ? 1 : 0);
194
195   /* If either aptr or bptr is equal to data, there is no numeric    */
196   /* value for that spam attribute. In this case, compare lexically. */
197   if ((aptr == (*ppa)->env->spam->data) || (bptr == (*ppb)->env->spam->data))
198     return (SORTCODE(strcmp(aptr, bptr)));
199
200   /* Otherwise, we have numeric value for both attrs. If these values */
201   /* are equal, then we first fall back upon string comparison, then  */
202   /* upon auxiliary sort.                                             */
203   if (result == 0)
204   {
205     result = strcmp(aptr, bptr);
206     if (result == 0)
207       AUXSORT(result, a, b);
208   }
209
210   return (SORTCODE(result));
211 }
212
213 sort_t *mutt_get_sort_func (int method)
214 {
215   switch (method & SORT_MASK)
216   {
217     case SORT_RECEIVED:
218       return (compare_date_received);
219     case SORT_ORDER:
220       return (compare_order);
221     case SORT_DATE:
222       return (compare_date_sent);
223     case SORT_SUBJECT:
224       return (compare_subject);
225     case SORT_FROM:
226       return (compare_from);
227     case SORT_SIZE:
228       return (compare_size);
229     case SORT_TO:
230       return (compare_to);
231     case SORT_SCORE:
232       return (compare_score);
233     case SORT_SPAM:
234       return (compare_spam);
235     default:
236       return (NULL);
237   }
238   /* not reached */
239 }
240
241 void mutt_sort_headers (CONTEXT *ctx, int init)
242 {
243   int i;
244   HEADER *h;
245   THREAD *thread, *top;
246   sort_t *sortfunc;
247   
248   unset_option (OPTNEEDRESORT);
249
250   if (!ctx)
251     return;
252
253   if (!ctx->msgcount)
254   {
255     /* this function gets called by mutt_sync_mailbox(), which may have just
256      * deleted all the messages.  the virtual message numbers are not updated
257      * in that routine, so we must make sure to zero the vcount member.
258      */
259     ctx->vcount = 0;
260     mutt_clear_threads (ctx);
261     return; /* nothing to do! */
262   }
263
264   if (!ctx->quiet)
265     mutt_message _("Sorting mailbox...");
266
267   if (option (OPTNEEDRESCORE) && option (OPTSCORE))
268   {
269     for (i = 0; i < ctx->msgcount; i++)
270       mutt_score_message (ctx, ctx->hdrs[i], 1);
271   }
272   unset_option (OPTNEEDRESCORE);
273
274   if (option (OPTRESORTINIT))
275   {
276     unset_option (OPTRESORTINIT);
277     init = 1;
278   }
279
280   if (init && ctx->tree)
281     mutt_clear_threads (ctx);
282
283   if ((Sort & SORT_MASK) == SORT_THREADS)
284   {
285     AuxSort = NULL;
286     /* if $sort_aux changed after the mailbox is sorted, then all the
287        subthreads need to be resorted */
288     if (option (OPTSORTSUBTHREADS))
289     {
290       i = Sort;
291       Sort = SortAux;
292       if (ctx->tree)
293         ctx->tree = mutt_sort_subthreads (ctx->tree, 1);
294       Sort = i;
295       unset_option (OPTSORTSUBTHREADS);
296     }
297     mutt_sort_threads (ctx, init);
298   }
299   else if ((sortfunc = mutt_get_sort_func (Sort)) == NULL ||
300            (AuxSort = mutt_get_sort_func (SortAux)) == NULL)
301   {
302     mutt_error _("Could not find sorting function! [report this bug]");
303     mutt_sleep (1);
304     return;
305   }
306   else 
307     qsort ((void *) ctx->hdrs, ctx->msgcount, sizeof (HEADER *), sortfunc);
308
309   /* adjust the virtual message numbers */
310   ctx->vcount = 0;
311   for (i = 0; i < ctx->msgcount; i++)
312   {
313     HEADER *cur = ctx->hdrs[i];
314     if (cur->virtual != -1 || (cur->collapsed && (!ctx->pattern || cur->limited)))
315     {
316       cur->virtual = ctx->vcount;
317       ctx->v2r[ctx->vcount] = i;
318       ctx->vcount++;
319     }
320     cur->msgno = i;
321   }
322
323   /* re-collapse threads marked as collapsed */
324   if ((Sort & SORT_MASK) == SORT_THREADS)
325   {
326     top = ctx->tree;
327     while ((thread = top) != NULL)
328     {
329       while (!thread->message)
330         thread = thread->child;
331       h = thread->message;
332
333       if (h->collapsed)
334         mutt_collapse_thread (ctx, h);
335       top = top->next;
336     }
337     mutt_set_virtual (ctx);
338   }
339
340   if (!ctx->quiet)
341     mutt_clear_error ();
342 }