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