NeoMutt  2020-04-24
Teaching an old dog new tricks
DOXYGEN
sort.c
Go to the documentation of this file.
1 
30 #include "config.h"
31 #include <stdbool.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include "mutt/lib.h"
35 #include "address/lib.h"
36 #include "email/lib.h"
37 #include "core/lib.h"
38 #include "sort.h"
39 #include "alias.h"
40 #include "context.h"
41 #include "globals.h"
42 #include "mutt_logging.h"
43 #include "mutt_thread.h"
44 #include "options.h"
45 #include "score.h"
46 #ifdef USE_NNTP
47 #include "nntp/lib.h"
48 #endif
49 
50 /* These Config Variables are only used in sort.c */
52 
53 /* function to use as discriminator when normal sort method is equal */
54 static sort_t AuxSort = NULL;
55 
65 int perform_auxsort(int retval, const void *a, const void *b)
66 {
67  /* If the items compared equal by the main sort
68  * and we're not already doing an 'aux' sort... */
69  if ((retval == 0) && AuxSort && !OptAuxSort)
70  {
71  OptAuxSort = true;
72  retval = AuxSort(a, b);
73  OptAuxSort = false;
74  if (retval != 0)
75  return retval;
76  }
77  /* If the items still match, use their index positions
78  * to maintain a stable sort order */
79  if (retval == 0)
80  {
81  retval = (*((struct Email const *const *) a))->index -
82  (*((struct Email const *const *) b))->index;
83  }
84  return retval;
85 }
86 
90 static int compare_score(const void *a, const void *b)
91 {
92  struct Email const *const *pa = (struct Email const *const *) a;
93  struct Email const *const *pb = (struct Email const *const *) b;
94  int result = (*pb)->score - (*pa)->score; /* note that this is reverse */
95  result = perform_auxsort(result, a, b);
96  return SORT_CODE(result);
97 }
98 
102 static int compare_size(const void *a, const void *b)
103 {
104  struct Email const *const *pa = (struct Email const *const *) a;
105  struct Email const *const *pb = (struct Email const *const *) b;
106  int result = (*pa)->content->length - (*pb)->content->length;
107  result = perform_auxsort(result, a, b);
108  return SORT_CODE(result);
109 }
110 
114 static int compare_date_sent(const void *a, const void *b)
115 {
116  struct Email const *const *pa = (struct Email const *const *) a;
117  struct Email const *const *pb = (struct Email const *const *) b;
118  int result = (*pa)->date_sent - (*pb)->date_sent;
119  result = perform_auxsort(result, a, b);
120  return SORT_CODE(result);
121 }
122 
126 static int compare_subject(const void *a, const void *b)
127 {
128  struct Email const *const *pa = (struct Email const *const *) a;
129  struct Email const *const *pb = (struct Email const *const *) b;
130  int rc;
131 
132  if (!(*pa)->env->real_subj)
133  {
134  if (!(*pb)->env->real_subj)
135  rc = compare_date_sent(pa, pb);
136  else
137  rc = -1;
138  }
139  else if (!(*pb)->env->real_subj)
140  rc = 1;
141  else
142  rc = mutt_str_strcasecmp((*pa)->env->real_subj, (*pb)->env->real_subj);
143  rc = perform_auxsort(rc, a, b);
144  return SORT_CODE(rc);
145 }
146 
157 const char *mutt_get_name(const struct Address *a)
158 {
159  struct Address *ali = NULL;
160 
161  if (a)
162  {
163  if (C_ReverseAlias && (ali = mutt_alias_reverse_lookup(a)) && ali->personal)
164  return ali->personal;
165  if (a->personal)
166  return a->personal;
167  if (a->mailbox)
168  return mutt_addr_for_display(a);
169  }
170  /* don't return NULL to avoid segfault when printing/comparing */
171  return "";
172 }
173 
177 static int compare_to(const void *a, const void *b)
178 {
179  struct Email const *const *ppa = (struct Email const *const *) a;
180  struct Email const *const *ppb = (struct Email const *const *) b;
181  char fa[128];
182 
183  mutt_str_strfcpy(fa, mutt_get_name(TAILQ_FIRST(&(*ppa)->env->to)), sizeof(fa));
184  const char *fb = mutt_get_name(TAILQ_FIRST(&(*ppb)->env->to));
185  int result = mutt_str_strncasecmp(fa, fb, sizeof(fa));
186  result = perform_auxsort(result, a, b);
187  return SORT_CODE(result);
188 }
189 
193 static int compare_from(const void *a, const void *b)
194 {
195  struct Email const *const *ppa = (struct Email const *const *) a;
196  struct Email const *const *ppb = (struct Email const *const *) b;
197  char fa[128];
198 
199  mutt_str_strfcpy(fa, mutt_get_name(TAILQ_FIRST(&(*ppa)->env->from)), sizeof(fa));
200  const char *fb = mutt_get_name(TAILQ_FIRST(&(*ppb)->env->from));
201  int result = mutt_str_strncasecmp(fa, fb, sizeof(fa));
202  result = perform_auxsort(result, a, b);
203  return SORT_CODE(result);
204 }
205 
209 static int compare_date_received(const void *a, const void *b)
210 {
211  struct Email const *const *pa = (struct Email const *const *) a;
212  struct Email const *const *pb = (struct Email const *const *) b;
213  int result = (*pa)->received - (*pb)->received;
214  result = perform_auxsort(result, a, b);
215  return SORT_CODE(result);
216 }
217 
221 static int compare_order(const void *a, const void *b)
222 {
223  struct Email const *const *ea = (struct Email const *const *) a;
224  struct Email const *const *eb = (struct Email const *const *) b;
225 
226  /* no need to auxsort because you will never have equality here */
227  return SORT_CODE((*ea)->index - (*eb)->index);
228 }
229 
233 static int compare_spam(const void *a, const void *b)
234 {
235  struct Email const *const *ppa = (struct Email const *const *) a;
236  struct Email const *const *ppb = (struct Email const *const *) b;
237  char *aptr = NULL, *bptr = NULL;
238  int ahas, bhas;
239  int result = 0;
240  double difference;
241 
242  /* Firstly, require spam attributes for both msgs */
243  /* to compare. Determine which msgs have one. */
244  ahas = (*ppa)->env && !mutt_buffer_is_empty(&(*ppa)->env->spam);
245  bhas = (*ppb)->env && !mutt_buffer_is_empty(&(*ppb)->env->spam);
246 
247  /* If one msg has spam attr but other does not, sort the one with first. */
248  if (ahas && !bhas)
249  return SORT_CODE(1);
250  if (!ahas && bhas)
251  return SORT_CODE(-1);
252 
253  /* Else, if neither has a spam attr, presume equality. Fall back on aux. */
254  if (!ahas && !bhas)
255  {
256  result = perform_auxsort(result, a, b);
257  return SORT_CODE(result);
258  }
259 
260  /* Both have spam attrs. */
261 
262  /* preliminary numeric examination */
263  difference =
264  (strtod((*ppa)->env->spam.data, &aptr) - strtod((*ppb)->env->spam.data, &bptr));
265 
266  /* map double into comparison (-1, 0, or 1) */
267  result = ((difference < 0.0) ? -1 : (difference > 0.0) ? 1 : 0);
268 
269  /* If either aptr or bptr is equal to data, there is no numeric */
270  /* value for that spam attribute. In this case, compare lexically. */
271  if ((aptr == (*ppa)->env->spam.data) || (bptr == (*ppb)->env->spam.data))
272  return SORT_CODE(strcmp(aptr, bptr));
273 
274  /* Otherwise, we have numeric value for both attrs. If these values */
275  /* are equal, then we first fall back upon string comparison, then */
276  /* upon auxiliary sort. */
277  if (result == 0)
278  {
279  result = strcmp(aptr, bptr);
280  result = perform_auxsort(result, a, b);
281  }
282 
283  return SORT_CODE(result);
284 }
285 
289 static int compare_label(const void *a, const void *b)
290 {
291  struct Email const *const *ppa = (struct Email const *const *) a;
292  struct Email const *const *ppb = (struct Email const *const *) b;
293  int ahas, bhas, result = 0;
294 
295  /* As with compare_spam, not all messages will have the x-label
296  * property. Blank X-Labels are treated as null in the index
297  * display, so we'll consider them as null for sort, too. */
298  ahas = (*ppa)->env && (*ppa)->env->x_label && *((*ppa)->env->x_label);
299  bhas = (*ppb)->env && (*ppb)->env->x_label && *((*ppb)->env->x_label);
300 
301  /* First we bias toward a message with a label, if the other does not. */
302  if (ahas && !bhas)
303  return SORT_CODE(-1);
304  if (!ahas && bhas)
305  return SORT_CODE(1);
306 
307  /* If neither has a label, use aux sort. */
308  if (!ahas && !bhas)
309  {
310  result = perform_auxsort(result, a, b);
311  return SORT_CODE(result);
312  }
313 
314  /* If both have a label, we just do a lexical compare. */
315  result = mutt_str_strcasecmp((*ppa)->env->x_label, (*ppb)->env->x_label);
316  return SORT_CODE(result);
317 }
318 
325 {
326  switch (method)
327  {
328  case SORT_DATE:
329  return compare_date_sent;
330  case SORT_FROM:
331  return compare_from;
332  case SORT_LABEL:
333  return compare_label;
334  case SORT_ORDER:
335 #ifdef USE_NNTP
336  if (Context && Context->mailbox && (Context->mailbox->type == MUTT_NNTP))
337  return nntp_compare_order;
338  else
339 #endif
340  return compare_order;
341  case SORT_RECEIVED:
342  return compare_date_received;
343  case SORT_SCORE:
344  return compare_score;
345  case SORT_SIZE:
346  return compare_size;
347  case SORT_SPAM:
348  return compare_spam;
349  case SORT_SUBJECT:
350  return compare_subject;
351  case SORT_TO:
352  return compare_to;
353  default:
354  return NULL;
355  }
356  /* not reached */
357 }
358 
364 void mutt_sort_headers(struct Context *ctx, bool init)
365 {
366  if (!ctx || !ctx->mailbox || !ctx->mailbox->emails || !ctx->mailbox->emails[0])
367  return;
368 
369  struct MuttThread *thread = NULL, *top = NULL;
370  sort_t sortfunc = NULL;
371  struct Mailbox *m = ctx->mailbox;
372 
373  OptNeedResort = false;
374 
375  if (m->msg_count == 0)
376  {
377  /* this function gets called by mutt_sync_mailbox(), which may have just
378  * deleted all the messages. the virtual message numbers are not updated
379  * in that routine, so we must make sure to zero the vcount member. */
380  m->vcount = 0;
381  ctx->vsize = 0;
382  mutt_clear_threads(ctx);
383  return; /* nothing to do! */
384  }
385 
386  if (!m->quiet)
387  mutt_message(_("Sorting mailbox..."));
388 
389  if (OptNeedRescore && C_Score)
390  {
391  for (int i = 0; i < m->msg_count; i++)
392  {
393  struct Email *e = m->emails[i];
394  if (!e)
395  break;
396  mutt_score_message(m, e, true);
397  }
398  }
399  OptNeedRescore = false;
400 
401  if (OptResortInit)
402  {
403  OptResortInit = false;
404  init = true;
405  }
406 
407  if (init && ctx->tree)
408  mutt_clear_threads(ctx);
409 
410  if ((C_Sort & SORT_MASK) == SORT_THREADS)
411  {
412  AuxSort = NULL;
413  /* if $sort_aux changed after the mailbox is sorted, then all the
414  * subthreads need to be resorted */
415  if (OptSortSubthreads)
416  {
417  int i = C_Sort;
418  C_Sort = C_SortAux;
419  if (ctx->tree)
420  ctx->tree = mutt_sort_subthreads(ctx->tree, true);
421  C_Sort = i;
422  OptSortSubthreads = false;
423  }
424  mutt_sort_threads(ctx, init);
425  }
426  else if (!(sortfunc = mutt_get_sort_func(C_Sort & SORT_MASK)) ||
427  !(AuxSort = mutt_get_sort_func(C_SortAux & SORT_MASK)))
428  {
429  mutt_error(_("Could not find sorting function [report this bug]"));
430  return;
431  }
432  else
433  {
434  qsort((void *) m->emails, m->msg_count, sizeof(struct Email *), sortfunc);
435  }
436 
437  /* adjust the virtual message numbers */
438  m->vcount = 0;
439  for (int i = 0; i < m->msg_count; i++)
440  {
441  struct Email *e_cur = m->emails[i];
442  if (!e_cur)
443  break;
444 
445  if ((e_cur->vnum != -1) || (e_cur->collapsed && (!ctx->pattern || e_cur->limited)))
446  {
447  e_cur->vnum = m->vcount;
448  m->v2r[m->vcount] = i;
449  m->vcount++;
450  }
451  e_cur->msgno = i;
452  }
453 
454  /* re-collapse threads marked as collapsed */
455  if ((C_Sort & SORT_MASK) == SORT_THREADS)
456  {
457  top = ctx->tree;
458  while ((thread = top))
459  {
460  while (!thread->message)
461  thread = thread->child;
462 
463  struct Email *e = thread->message;
464  if (e->collapsed)
465  mutt_collapse_thread(ctx, e);
466  top = top->next;
467  }
468  mutt_set_vnum(ctx);
469  }
470 
471  if (!m->quiet)
473 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
The "current" mailbox.
Definition: context.h:37
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
int msg_count
Total number of messages.
Definition: mailbox.h:91
The envelope/body of an email.
Definition: email.h:37
SortType
Methods for sorting.
Definition: sort2.h:48
#define TAILQ_FIRST(head)
Definition: queue.h:716
void mutt_set_vnum(struct Context *ctx)
Set the virtual index number of all the messages in a mailbox.
Definition: mutt_thread.c:1197
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:52
Structs that make up an email.
struct MuttThread * tree
Top of thread tree.
Definition: context.h:43
The "currently-open" mailbox.
#define mutt_message(...)
Definition: logging.h:83
NeoMutt Logging.
WHERE bool OptNeedResort
(pseudo) used to force a re-sort
Definition: options.h:44
struct Body * content
List of MIME parts.
Definition: email.h:90
struct MuttThread * mutt_sort_subthreads(struct MuttThread *thread, bool init)
Sort the children of a thread.
Definition: mutt_thread.c:655
void mutt_score_message(struct Mailbox *m, struct Email *e, bool upd_mbox)
Apply scoring to an email.
Definition: score.c:174
Sort by the email&#39;s From field.
Definition: sort2.h:54
#define _(a)
Definition: message.h:28
Sort by the email&#39;s score.
Definition: sort2.h:59
WHERE bool OptAuxSort
(pseudo) using auxiliary sort function
Definition: options.h:35
An email address.
Definition: address.h:34
char * mailbox
Mailbox and host address.
Definition: address.h:37
Sort by the emails label.
Definition: sort2.h:69
static int compare_label(const void *a, const void *b)
Compare the labels of two emails - Implements sort_t.
Definition: sort.c:289
WHERE bool OptNeedRescore
(pseudo) set when the &#39;score&#39; command is used
Definition: options.h:43
static int compare_date_sent(const void *a, const void *b)
Compare the sent date of two emails - Implements sort_t.
Definition: sort.c:114
Representation of a single alias to an email address.
#define mutt_collapse_thread(ctx, e)
Definition: mutt_thread.h:57
int vcount
The number of virtual messages.
Definition: mailbox.h:102
Hundreds of global variables to back the user variables.
Email Address Handling.
Assorted sorting methods.
static int compare_order(const void *a, const void *b)
Restore the &#39;unsorted&#39; order of emails - Implements sort_t.
Definition: sort.c:221
Sort by the size of the email.
Definition: sort2.h:51
const char * mutt_addr_for_display(const struct Address *a)
Convert an Address for display purposes.
Definition: address.c:983
void mutt_sort_threads(struct Context *ctx, bool init)
Sort email threads.
Definition: mutt_thread.c:834
struct Mailbox * mailbox
Definition: context.h:51
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:55
struct Envelope * env
Envelope information.
Definition: email.h:89
Convenience wrapper for the core headers.
struct Address * mutt_alias_reverse_lookup(const struct Address *a)
Does the user have an alias for the given address.
Definition: alias.c:545
bool limited
Is this message in a limited view?
Definition: email.h:74
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:58
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:113
Usenet network mailbox type; talk to an NNTP server.
int(* sort_t)(const void *a, const void *b)
Prototype for a function to compare two emails.
Definition: sort.h:48
int score
Message score.
Definition: email.h:88
time_t date_sent
Time when the message was sent (UTC)
Definition: email.h:81
off_t vsize
Definition: context.h:39
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
WHERE short C_SortAux
Config: Secondary sort method for the index.
Definition: sort.h:59
bool C_ReverseAlias
Config: Display the alias in the index, rather than the message&#39;s sender.
Definition: sort.c:51
int mutt_str_strncasecmp(const char *a, const char *b, size_t l)
Compare two strings ignoring case (to a maximum), safely.
Definition: string.c:682
void mutt_sort_headers(struct Context *ctx, bool init)
Sort emails by their headers.
Definition: sort.c:364
Sort by email threads.
Definition: sort2.h:56
static int compare_date_received(const void *a, const void *b)
Compare the date received of two emails - Implements sort_t.
Definition: sort.c:209
Create/manipulate threading in emails.
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
A mailbox.
Definition: mailbox.h:81
int nntp_compare_order(const void *a, const void *b)
Sort to mailbox order - Implements sort_t.
Definition: nntp.c:2368
static sort_t AuxSort
Definition: sort.c:54
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:776
#define SORT_CODE(x)
Definition: sort.h:38
bool collapsed
Is this message part of a collapsed thread?
Definition: email.h:73
bool quiet
Inhibit status messages?
Definition: mailbox.h:118
Sort by the email&#39;s spam score.
Definition: sort2.h:64
int vnum
Virtual message number.
Definition: email.h:87
static int compare_spam(const void *a, const void *b)
Compare the spam values of two emails - Implements sort_t.
Definition: sort.c:233
Sort by the email&#39;s To field.
Definition: sort2.h:58
WHERE bool OptResortInit
(pseudo) used to force the next resort to be from scratch
Definition: options.h:52
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
void mutt_clear_threads(struct Context *ctx)
Clear the threading of message in a mailbox.
Definition: mutt_thread.c:601
Routines for adding user scores to emails.
An Email conversation.
Definition: thread.h:34
char * personal
Real name of address.
Definition: address.h:36
int * v2r
Mapping from virtual to real msgno.
Definition: mailbox.h:101
static int compare_size(const void *a, const void *b)
Compare the size of two emails - Implements sort_t.
Definition: sort.c:102
#define mutt_error(...)
Definition: logging.h:84
int mutt_str_strcasecmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:654
int index
The absolute (unsorted) message number.
Definition: email.h:85
static int compare_to(const void *a, const void *b)
Compare the &#39;to&#39; fields of two emails - Implements sort_t.
Definition: sort.c:177
WHERE bool C_Score
Config: Use message scoring.
Definition: globals.h:247
int perform_auxsort(int retval, const void *a, const void *b)
Compare two emails using the auxiliary sort method.
Definition: sort.c:65
static int compare_subject(const void *a, const void *b)
Compare the subject of two emails - Implements sort_t.
Definition: sort.c:126
WHERE bool OptSortSubthreads
(pseudo) used when $sort_aux changes
Definition: options.h:55
Handling of global boolean variables.
Sort by when the message were delivered locally.
Definition: sort2.h:57
Sort by the date the email was sent.
Definition: sort2.h:50
sort_t mutt_get_sort_func(enum SortType method)
Get the sort function for a given sort id.
Definition: sort.c:324
Convenience wrapper for the library headers.
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
static int compare_score(const void *a, const void *b)
Compare two emails using their scores - Implements sort_t.
Definition: sort.c:90
char * pattern
Limit pattern string.
Definition: context.h:40
char * x_label
X-Label.
Definition: envelope.h:72
Sort by the email&#39;s subject.
Definition: sort2.h:53
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:85
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:82
int msgno
Number displayed to the user.
Definition: email.h:86
const char * mutt_get_name(const struct Address *a)
Pick the best name to display from an address.
Definition: sort.c:157
static int compare_from(const void *a, const void *b)
Compare the &#39;from&#39; fields of two emails - Implements sort_t.
Definition: sort.c:193