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