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