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