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