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