Andreas Krennmair:
[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 sort_t *mutt_get_sort_func (int method)
181 {
182   switch (method & SORT_MASK)
183   {
184     case SORT_RECEIVED:
185       return (compare_date_received);
186     case SORT_ORDER:
187       return (compare_order);
188     case SORT_DATE:
189       return (compare_date_sent);
190     case SORT_SUBJECT:
191       return (compare_subject);
192     case SORT_FROM:
193       return (compare_from);
194     case SORT_SIZE:
195       return (compare_size);
196     case SORT_TO:
197       return (compare_to);
198     case SORT_SCORE:
199       return (compare_score);
200     default:
201       return (NULL);
202   }
203   /* not reached */
204 }
205
206 void mutt_sort_headers (CONTEXT *ctx, int init)
207 {
208   int i;
209   HEADER *h;
210   THREAD *thread, *top;
211   sort_t *sortfunc;
212   
213   unset_option (OPTNEEDRESORT);
214
215   if (!ctx)
216     return;
217
218   if (!ctx->msgcount)
219   {
220     /* this function gets called by mutt_sync_mailbox(), which may have just
221      * deleted all the messages.  the virtual message numbers are not updated
222      * in that routine, so we must make sure to zero the vcount member.
223      */
224     ctx->vcount = 0;
225     mutt_clear_threads (ctx);
226     return; /* nothing to do! */
227   }
228
229   if (!ctx->quiet)
230     mutt_message _("Sorting mailbox...");
231
232   if (option (OPTNEEDRESCORE) && option (OPTSCORE))
233   {
234     for (i = 0; i < ctx->msgcount; i++)
235       mutt_score_message (ctx, ctx->hdrs[i], 1);
236   }
237   unset_option (OPTNEEDRESCORE);
238
239   if (option (OPTRESORTINIT))
240   {
241     unset_option (OPTRESORTINIT);
242     init = 1;
243   }
244
245   if (init && ctx->tree)
246     mutt_clear_threads (ctx);
247
248   if ((Sort & SORT_MASK) == SORT_THREADS)
249   {
250     AuxSort = NULL;
251     /* if $sort_aux changed after the mailbox is sorted, then all the
252        subthreads need to be resorted */
253     if (option (OPTSORTSUBTHREADS))
254     {
255       i = Sort;
256       Sort = SortAux;
257       if (ctx->tree)
258         ctx->tree = mutt_sort_subthreads (ctx->tree, 1);
259       Sort = i;
260       unset_option (OPTSORTSUBTHREADS);
261     }
262     mutt_sort_threads (ctx, init);
263   }
264   else if ((sortfunc = mutt_get_sort_func (Sort)) == NULL ||
265            (AuxSort = mutt_get_sort_func (SortAux)) == NULL)
266   {
267     mutt_error _("Could not find sorting function! [report this bug]");
268     mutt_sleep (1);
269     return;
270   }
271   else 
272     qsort ((void *) ctx->hdrs, ctx->msgcount, sizeof (HEADER *), sortfunc);
273
274   /* adjust the virtual message numbers */
275   ctx->vcount = 0;
276   for (i = 0; i < ctx->msgcount; i++)
277   {
278     HEADER *cur = ctx->hdrs[i];
279     if (cur->virtual != -1 || (cur->collapsed && (!ctx->pattern || cur->limited)))
280     {
281       cur->virtual = ctx->vcount;
282       ctx->v2r[ctx->vcount] = i;
283       ctx->vcount++;
284     }
285     cur->msgno = i;
286   }
287
288   /* re-collapse threads marked as collapsed */
289   if ((Sort & SORT_MASK) == SORT_THREADS)
290   {
291     top = ctx->tree;
292     while ((thread = top) != NULL)
293     {
294       while (!thread->message)
295         thread = thread->child;
296       h = thread->message;
297
298       if (h->collapsed)
299         mutt_collapse_thread (ctx, h);
300       top = top->next;
301     }
302     mutt_set_virtual (ctx);
303   }
304
305   if (!ctx->quiet)
306     mutt_clear_error ();
307 }