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