mutt stable branch with some hacks
at jcs 378 lines 10 kB view raw
1/* 2 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 */ 18 19#if HAVE_CONFIG_H 20# include "config.h" 21#endif 22 23#include "mutt.h" 24#include "sort.h" 25#include "mutt_idna.h" 26 27#include <stdlib.h> 28#include <string.h> 29#include <ctype.h> 30#include <unistd.h> 31 32#define SORTCODE(x) ((option(OPTAUXSORT) ? SortAux : Sort) & SORT_REVERSE) ? -(x) : x 33 34/* function to use as discriminator when normal sort method is equal */ 35static sort_t *AuxSort = NULL; 36 37#define AUXSORT(code,a,b) \ 38 if (!code && AuxSort && !option(OPTAUXSORT)) \ 39 { \ 40 set_option(OPTAUXSORT); \ 41 code = AuxSort(a,b); \ 42 unset_option(OPTAUXSORT); \ 43 if (code) \ 44 return (code); \ 45 } \ 46 if (!code) \ 47 code = (*((HEADER **)a))->index - (*((HEADER **)b))->index; 48 49static int compare_score (const void *a, const void *b) 50{ 51 HEADER **pa = (HEADER **) a; 52 HEADER **pb = (HEADER **) b; 53 int result = (*pb)->score - (*pa)->score; /* note that this is reverse */ 54 AUXSORT(result,a,b); 55 return (SORTCODE (result)); 56} 57 58static int compare_size (const void *a, const void *b) 59{ 60 HEADER **pa = (HEADER **) a; 61 HEADER **pb = (HEADER **) b; 62 int result = (*pa)->content->length - (*pb)->content->length; 63 AUXSORT(result,a,b); 64 return (SORTCODE (result)); 65} 66 67static int compare_date_sent (const void *a, const void *b) 68{ 69 HEADER **pa = (HEADER **) a; 70 HEADER **pb = (HEADER **) b; 71 int result = (*pa)->date_sent - (*pb)->date_sent; 72 AUXSORT(result,a,b); 73 return (SORTCODE (result)); 74} 75 76static int compare_subject (const void *a, const void *b) 77{ 78 HEADER **pa = (HEADER **) a; 79 HEADER **pb = (HEADER **) b; 80 int rc; 81 82 if (!(*pa)->env->real_subj) 83 { 84 if (!(*pb)->env->real_subj) 85 rc = compare_date_sent (pa, pb); 86 else 87 rc = -1; 88 } 89 else if (!(*pb)->env->real_subj) 90 rc = 1; 91 else 92 rc = mutt_strcasecmp ((*pa)->env->real_subj, (*pb)->env->real_subj); 93 AUXSORT(rc,a,b); 94 return (SORTCODE (rc)); 95} 96 97const char *mutt_get_name (ADDRESS *a) 98{ 99 ADDRESS *ali; 100 101 if (a) 102 { 103 if (option (OPTREVALIAS) && (ali = alias_reverse_lookup (a)) && ali->personal) 104 return ali->personal; 105 else if (a->personal) 106 return a->personal; 107 else if (a->mailbox) 108 return (mutt_addr_for_display (a)); 109 } 110 /* don't return NULL to avoid segfault when printing/comparing */ 111 return (""); 112} 113 114static int compare_to (const void *a, const void *b) 115{ 116 HEADER **ppa = (HEADER **) a; 117 HEADER **ppb = (HEADER **) b; 118 char fa[SHORT_STRING]; 119 const char *fb; 120 int result; 121 122 strfcpy (fa, mutt_get_name ((*ppa)->env->to), SHORT_STRING); 123 fb = mutt_get_name ((*ppb)->env->to); 124 result = mutt_strncasecmp (fa, fb, SHORT_STRING); 125 AUXSORT(result,a,b); 126 return (SORTCODE (result)); 127} 128 129static int compare_from (const void *a, const void *b) 130{ 131 HEADER **ppa = (HEADER **) a; 132 HEADER **ppb = (HEADER **) b; 133 char fa[SHORT_STRING]; 134 const char *fb; 135 int result; 136 137 strfcpy (fa, mutt_get_name ((*ppa)->env->from), SHORT_STRING); 138 fb = mutt_get_name ((*ppb)->env->from); 139 result = mutt_strncasecmp (fa, fb, SHORT_STRING); 140 AUXSORT(result,a,b); 141 return (SORTCODE (result)); 142} 143 144static int compare_date_received (const void *a, const void *b) 145{ 146 HEADER **pa = (HEADER **) a; 147 HEADER **pb = (HEADER **) b; 148 int result = (*pa)->received - (*pb)->received; 149 AUXSORT(result,a,b); 150 return (SORTCODE (result)); 151} 152 153static int compare_order (const void *a, const void *b) 154{ 155 HEADER **ha = (HEADER **) a; 156 HEADER **hb = (HEADER **) b; 157 158 /* no need to auxsort because you will never have equality here */ 159 return (SORTCODE ((*ha)->index - (*hb)->index)); 160} 161 162static int compare_spam (const void *a, const void *b) 163{ 164 HEADER **ppa = (HEADER **) a; 165 HEADER **ppb = (HEADER **) b; 166 char *aptr, *bptr; 167 int ahas, bhas; 168 int result = 0; 169 double difference; 170 171 /* Firstly, require spam attributes for both msgs */ 172 /* to compare. Determine which msgs have one. */ 173 ahas = (*ppa)->env && (*ppa)->env->spam; 174 bhas = (*ppb)->env && (*ppb)->env->spam; 175 176 /* If one msg has spam attr but other does not, sort the one with first. */ 177 if (ahas && !bhas) 178 return (SORTCODE(1)); 179 if (!ahas && bhas) 180 return (SORTCODE(-1)); 181 182 /* Else, if neither has a spam attr, presume equality. Fall back on aux. */ 183 if (!ahas && !bhas) 184 { 185 AUXSORT(result, a, b); 186 return (SORTCODE(result)); 187 } 188 189 190 /* Both have spam attrs. */ 191 192 /* preliminary numeric examination */ 193 difference = (strtod((*ppa)->env->spam->data, &aptr) - 194 strtod((*ppb)->env->spam->data, &bptr)); 195 196 /* map double into comparison (-1, 0, or 1) */ 197 result = (difference < 0.0 ? -1 : difference > 0.0 ? 1 : 0); 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(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 { 209 result = strcmp(aptr, bptr); 210 AUXSORT(result, a, b); 211 } 212 213 return (SORTCODE(result)); 214} 215 216int compare_label (const void *a, const void *b) 217{ 218 HEADER **ppa = (HEADER **) a; 219 HEADER **ppb = (HEADER **) b; 220 int ahas, bhas, result = 0; 221 222 /* As with compare_spam, not all messages will have the x-label 223 * property. Blank X-Labels are treated as null in the index 224 * display, so we'll consider them as null for sort, too. */ 225 ahas = (*ppa)->env && (*ppa)->env->x_label && *((*ppa)->env->x_label); 226 bhas = (*ppb)->env && (*ppb)->env->x_label && *((*ppb)->env->x_label); 227 228 /* First we bias toward a message with a label, if the other does not. */ 229 if (ahas && !bhas) 230 return (SORTCODE(-1)); 231 if (!ahas && bhas) 232 return (SORTCODE(1)); 233 234 /* If neither has a label, use aux sort. */ 235 if (!ahas && !bhas) 236 { 237 AUXSORT(result, a, b); 238 return (SORTCODE(result)); 239 } 240 241 /* If both have a label, we just do a lexical compare. */ 242 result = mutt_strcasecmp((*ppa)->env->x_label, (*ppb)->env->x_label); 243 return (SORTCODE(result)); 244} 245 246sort_t *mutt_get_sort_func (int method) 247{ 248 switch (method & SORT_MASK) 249 { 250 case SORT_RECEIVED: 251 return (compare_date_received); 252 case SORT_ORDER: 253 return (compare_order); 254 case SORT_DATE: 255 return (compare_date_sent); 256 case SORT_SUBJECT: 257 return (compare_subject); 258 case SORT_FROM: 259 return (compare_from); 260 case SORT_SIZE: 261 return (compare_size); 262 case SORT_TO: 263 return (compare_to); 264 case SORT_SCORE: 265 return (compare_score); 266 case SORT_SPAM: 267 return (compare_spam); 268 case SORT_LABEL: 269 return (compare_label); 270 default: 271 return (NULL); 272 } 273 /* not reached */ 274} 275 276void mutt_sort_headers (CONTEXT *ctx, int init) 277{ 278 int i; 279 HEADER *h; 280 THREAD *thread, *top; 281 sort_t *sortfunc; 282 283 unset_option (OPTNEEDRESORT); 284 285 if (!ctx) 286 return; 287 288 if (!ctx->msgcount) 289 { 290 /* this function gets called by mutt_sync_mailbox(), which may have just 291 * deleted all the messages. the virtual message numbers are not updated 292 * in that routine, so we must make sure to zero the vcount member. 293 */ 294 ctx->vcount = 0; 295 ctx->vsize = 0; 296 mutt_clear_threads (ctx); 297 return; /* nothing to do! */ 298 } 299 300 if (!ctx->quiet) 301 mutt_message _("Sorting mailbox..."); 302 303 if (option (OPTNEEDRESCORE) && option (OPTSCORE)) 304 { 305 for (i = 0; i < ctx->msgcount; i++) 306 mutt_score_message (ctx, ctx->hdrs[i], 1); 307 } 308 unset_option (OPTNEEDRESCORE); 309 310 if (option (OPTRESORTINIT)) 311 { 312 unset_option (OPTRESORTINIT); 313 init = 1; 314 } 315 316 if (init && ctx->tree) 317 mutt_clear_threads (ctx); 318 319 if ((Sort & SORT_MASK) == SORT_THREADS) 320 { 321 AuxSort = NULL; 322 /* if $sort_aux changed after the mailbox is sorted, then all the 323 subthreads need to be resorted */ 324 if (option (OPTSORTSUBTHREADS)) 325 { 326 i = Sort; 327 Sort = SortAux; 328 if (ctx->tree) 329 ctx->tree = mutt_sort_subthreads (ctx->tree, 1); 330 Sort = i; 331 unset_option (OPTSORTSUBTHREADS); 332 } 333 mutt_sort_threads (ctx, init); 334 } 335 else if ((sortfunc = mutt_get_sort_func (Sort)) == NULL || 336 (AuxSort = mutt_get_sort_func (SortAux)) == NULL) 337 { 338 mutt_error _("Could not find sorting function! [report this bug]"); 339 mutt_sleep (1); 340 return; 341 } 342 else 343 qsort ((void *) ctx->hdrs, ctx->msgcount, sizeof (HEADER *), sortfunc); 344 345 /* adjust the virtual message numbers */ 346 ctx->vcount = 0; 347 for (i = 0; i < ctx->msgcount; i++) 348 { 349 HEADER *cur = ctx->hdrs[i]; 350 if (cur->virtual != -1 || (cur->collapsed && (!ctx->pattern || cur->limited))) 351 { 352 cur->virtual = ctx->vcount; 353 ctx->v2r[ctx->vcount] = i; 354 ctx->vcount++; 355 } 356 cur->msgno = i; 357 } 358 359 /* re-collapse threads marked as collapsed */ 360 if ((Sort & SORT_MASK) == SORT_THREADS) 361 { 362 top = ctx->tree; 363 while ((thread = top) != NULL) 364 { 365 while (!thread->message) 366 thread = thread->child; 367 h = thread->message; 368 369 if (h->collapsed) 370 mutt_collapse_thread (ctx, h); 371 top = top->next; 372 } 373 mutt_set_virtual (ctx); 374 } 375 376 if (!ctx->quiet) 377 mutt_clear_error (); 378}