NeoMutt  2025-12-11-769-g906513
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
sort.c
Go to the documentation of this file.
1
26
32
33#include "config.h"
34#include <stdbool.h>
35#include <stdlib.h>
36#include "mutt/lib.h"
37#include "address/lib.h"
38#include "config/lib.h"
39#include "core/lib.h"
40#include "alias/lib.h"
41#include "gui/lib.h"
42#include "sort.h"
43#include "nntp/lib.h"
44#include "body.h"
45#include "email.h"
46#include "envelope.h"
47#include "mutt_logging.h"
48#include "mx.h"
49#include "score.h"
50
51extern bool OptNeedRescore;
52extern bool OptNeedResort;
53extern bool OptResortInit;
54
59{
61 short sort;
62 short sort_aux;
63};
64
68static int email_sort_shim(const void *a, const void *b, void *sdata)
69{
70 const struct Email *ea = *(struct Email const *const *) a;
71 const struct Email *eb = *(struct Email const *const *) b;
72 const struct EmailCompare *cmp = sdata;
73 return mutt_compare_emails(ea, eb, cmp->type, cmp->sort, cmp->sort_aux);
74}
75
79static int email_sort_score(const struct Email *a, const struct Email *b, bool reverse)
80{
81 int result = mutt_numeric_cmp(b->score, a->score); /* note that this is reverse */
82 return reverse ? -result : result;
83}
84
88static int email_sort_size(const struct Email *a, const struct Email *b, bool reverse)
89{
90 int result = mutt_numeric_cmp(a->body->length, b->body->length);
91 return reverse ? -result : result;
92}
93
97static int email_sort_date(const struct Email *a, const struct Email *b, bool reverse)
98{
99 int result = mutt_numeric_cmp(a->date_sent, b->date_sent);
100 return reverse ? -result : result;
101}
102
106static int email_sort_subject(const struct Email *a, const struct Email *b, bool reverse)
107{
108 int rc;
109
110 if (!a->env->real_subj)
111 {
112 if (!b->env->real_subj)
113 rc = email_sort_date(a, b, false);
114 else
115 rc = -1;
116 }
117 else if (!b->env->real_subj)
118 {
119 rc = 1;
120 }
121 else
122 {
123 rc = mutt_istr_cmp(a->env->real_subj, b->env->real_subj);
124 }
125 return reverse ? -rc : rc;
126}
127
138const char *mutt_get_name(const struct Address *a)
139{
140 struct Address *ali = NULL;
141
142 if (a)
143 {
144 const bool c_reverse_alias = cs_subset_bool(NeoMutt->sub, "reverse_alias");
145 if (c_reverse_alias && (ali = alias_reverse_lookup(a)) && ali->personal)
146 return buf_string(ali->personal);
147 if (a->personal)
148 return buf_string(a->personal);
149 if (a->mailbox)
150 return mutt_addr_for_display(a);
151 }
152 /* don't return NULL to avoid segfault when printing/comparing */
153 return "";
154}
155
159static int email_sort_to(const struct Email *a, const struct Email *b, bool reverse)
160{
161 char fa[128] = { 0 };
162
163 mutt_str_copy(fa, mutt_get_name(TAILQ_FIRST(&a->env->to)), sizeof(fa));
164 const char *fb = mutt_get_name(TAILQ_FIRST(&b->env->to));
165 int result = mutt_istrn_cmp(fa, fb, sizeof(fa));
166 return reverse ? -result : result;
167}
168
172static int email_sort_from(const struct Email *a, const struct Email *b, bool reverse)
173{
174 char fa[128] = { 0 };
175
176 mutt_str_copy(fa, mutt_get_name(TAILQ_FIRST(&a->env->from)), sizeof(fa));
177 const char *fb = mutt_get_name(TAILQ_FIRST(&b->env->from));
178 int result = mutt_istrn_cmp(fa, fb, sizeof(fa));
179 return reverse ? -result : result;
180}
181
185static int email_sort_date_received(const struct Email *a, const struct Email *b, bool reverse)
186{
187 int result = mutt_numeric_cmp(a->received, b->received);
188 return reverse ? -result : result;
189}
190
194static int email_sort_unsorted(const struct Email *a, const struct Email *b, bool reverse)
195{
196 int result = mutt_numeric_cmp(a->index, b->index);
197 return reverse ? -result : result;
198}
199
203static int email_sort_spam(const struct Email *a, const struct Email *b, bool reverse)
204{
205 char *aptr = NULL, *bptr = NULL;
206 int ahas, bhas;
207 int result = 0;
208 double difference;
209
210 /* Firstly, require spam attributes for both msgs */
211 /* to compare. Determine which msgs have one. */
212 ahas = a->env && !buf_is_empty(&a->env->spam);
213 bhas = b->env && !buf_is_empty(&b->env->spam);
214
215 /* If one msg has spam attr but other does not, sort the one with first. */
216 if (ahas && !bhas)
217 return reverse ? -1 : 1;
218 if (!ahas && bhas)
219 return reverse ? 1 : -1;
220
221 /* Else, if neither has a spam attr, presume equality. Fall back on aux. */
222 if (!ahas && !bhas)
223 return 0;
224
225 /* Both have spam attrs. */
226
227 /* preliminary numeric examination */
228 difference = (strtod(a->env->spam.data, &aptr) - strtod(b->env->spam.data, &bptr));
229
230 /* map double into comparison (-1, 0, or 1) */
231 result = ((difference < 0.0) ? -1 : (difference > 0.0) ? 1 : 0);
232
233 /* If either aptr or bptr is equal to data, there is no numeric */
234 /* value for that spam attribute. In this case, compare lexically. */
235 if ((aptr == a->env->spam.data) || (bptr == b->env->spam.data))
236 {
237 result = mutt_str_cmp(aptr, bptr);
238 return reverse ? -result : result;
239 }
240
241 /* Otherwise, we have numeric value for both attrs. If these values */
242 /* are equal, then we first fall back upon string comparison, then */
243 /* upon auxiliary sort. */
244 if (result == 0)
245 result = mutt_str_cmp(aptr, bptr);
246 return reverse ? -result : result;
247}
248
252static int email_sort_label(const struct Email *a, const struct Email *b, bool reverse)
253{
254 int ahas, bhas, result = 0;
255
256 /* As with email_sort_spam, not all messages will have the x-label
257 * property. Blank X-Labels are treated as null in the index
258 * display, so we'll consider them as null for sort, too. */
259 ahas = a->env && a->env->x_label && *(a->env->x_label);
260 bhas = b->env && b->env->x_label && *(b->env->x_label);
261
262 /* First we bias toward a message with a label, if the other does not. */
263 if (ahas && !bhas)
264 return reverse ? 1 : -1;
265 if (!ahas && bhas)
266 return reverse ? -1 : 1;
267
268 /* If neither has a label, use aux sort. */
269 if (!ahas && !bhas)
270 return 0;
271
272 /* If both have a label, we just do a lexical compare. */
273 result = mutt_istr_cmp(a->env->x_label, b->env->x_label);
274 return reverse ? -result : result;
275}
276
284{
285 switch (method)
286 {
287 case EMAIL_SORT_DATE:
288 return email_sort_date;
291 case EMAIL_SORT_FROM:
292 return email_sort_from;
293 case EMAIL_SORT_LABEL:
294 return email_sort_label;
295 case EMAIL_SORT_SCORE:
296 return email_sort_score;
297 case EMAIL_SORT_SIZE:
298 return email_sort_size;
299 case EMAIL_SORT_SPAM:
300 return email_sort_spam;
302 return email_sort_subject;
303 case EMAIL_SORT_TO:
304 return email_sort_to;
306 if (type == MUTT_NNTP)
307 return nntp_sort_unsorted;
308 else
309 return email_sort_unsorted;
310 default:
311 mutt_error(_("Could not find sorting function [report this bug]"));
312 return NULL;
313 }
314 /* not reached */
315}
316
328int mutt_compare_emails(const struct Email *a, const struct Email *b,
329 enum MailboxType type, short sort, short sort_aux)
330{
331 sort_email_t func = get_sort_func(sort & SORT_MASK, type);
332 int rc = func(a, b, (sort & SORT_REVERSE) != 0);
333 if (rc == 0)
334 {
335 func = get_sort_func(sort_aux & SORT_MASK, type);
336 rc = func(a, b, (sort_aux & SORT_REVERSE) != 0);
337 }
338 if (rc == 0)
339 {
340 /* Fallback of last resort to preserve stable order; will only
341 * return 0 if a and b have the same index, which is probably a
342 * bug in the code. */
343 func = email_sort_unsorted;
344 rc = func(a, b, false);
345 }
346 return rc;
347}
348
354void mutt_sort_headers(struct MailboxView *mv, bool init)
355{
356 if (!mv)
357 return;
358
359 struct Mailbox *m = mv->mailbox;
360 if (!m || !m->emails[0])
361 return;
362
363 OptNeedResort = false;
364
365 if (m->msg_count == 0)
366 {
367 /* this function gets called by mutt_sync_mailbox(), which may have just
368 * deleted all the messages. the virtual message numbers are not updated
369 * in that routine, so we must make sure to zero the vcount member. */
370 m->vcount = 0;
372 mv->vsize = 0;
373 return; /* nothing to do! */
374 }
375
376 if (m->verbose)
377 mutt_message(_("Sorting mailbox..."));
378
379 const bool c_score = cs_subset_bool(NeoMutt->sub, "score");
380 if (OptNeedRescore && c_score)
381 {
382 for (int i = 0; i < m->msg_count; i++)
383 {
384 struct Email *e = m->emails[i];
385 if (!e)
386 break;
387 mutt_score_message(m, e, true);
388 }
389 }
390 OptNeedRescore = false;
391
392 if (OptResortInit)
393 {
394 OptResortInit = false;
395 init = true;
396 }
397
398 if (init)
400
401 const bool threaded = mutt_using_threads();
402 if (threaded)
403 {
404 mutt_sort_threads(mv->threads, init);
405 }
406 else
407 {
408 struct EmailCompare cmp = { 0 };
409 cmp.type = mx_type(m);
410 cmp.sort = cs_subset_sort(NeoMutt->sub, "sort");
411 cmp.sort_aux = cs_subset_sort(NeoMutt->sub, "sort_aux");
412 mutt_qsort_r((void *) m->emails, m->msg_count, sizeof(struct Email *),
413 email_sort_shim, &cmp);
414 }
415
416 /* adjust the virtual message numbers */
417 m->vcount = 0;
418 for (int i = 0; i < m->msg_count; i++)
419 {
420 struct Email *e_cur = m->emails[i];
421 if (!e_cur)
422 break;
423
424 if ((e_cur->vnum != -1) || (e_cur->collapsed && e_cur->visible))
425 {
426 e_cur->vnum = m->vcount;
427 m->v2r[m->vcount] = i;
428 m->vcount++;
429 }
430 e_cur->msgno = i;
431 }
432
433 /* re-collapse threads marked as collapsed */
434 if (threaded)
435 {
437 mv->vsize = mutt_set_vnum(m);
438 }
439
440 if (m->verbose)
442}
443
449{
450 if (!m)
451 return;
452
453 struct EmailCompare cmp = { 0 };
454 cmp.type = mx_type(m);
456 mutt_qsort_r((void *) m->emails, m->msg_count, sizeof(struct Email *),
457 email_sort_shim, &cmp);
458}
const char * mutt_addr_for_display(const struct Address *a)
Convert an Address for display purposes.
Definition address.c:1021
Email Address Handling.
Email Aliases.
struct Address * alias_reverse_lookup(const struct Address *addr)
Does the user have an alias for the given address.
Definition reverse.c:112
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition helpers.c:266
Convenience wrapper for the config headers.
#define SORT_MASK
Mask for the sort id.
Definition sort.h:39
#define mutt_numeric_cmp(a, b)
Compare two numbers, return -1, 0, or 1.
Definition sort.h:27
#define SORT_REVERSE
Reverse the order of the sort.
Definition sort.h:40
Convenience wrapper for the core headers.
MailboxType
Supported mailbox formats.
Definition mailbox.h:40
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition mailbox.h:48
Representation of the body of an email.
const char * mutt_get_name(const struct Address *a)
Pick the best name to display from an address.
Definition sort.c:138
bool OptNeedRescore
(pseudo) set when the 'score' command is used
Definition globals.c:51
bool OptResortInit
(pseudo) used to force the next resort to be from scratch
Definition globals.c:56
static sort_email_t get_sort_func(enum EmailSortType method, enum MailboxType type)
Get the sort function for a given sort id.
Definition sort.c:283
void mutt_sort_headers(struct MailboxView *mv, bool init)
Sort emails by their headers.
Definition sort.c:354
bool OptNeedResort
(pseudo) used to force a re-sort
Definition globals.c:52
void mutt_sort_unsorted(struct Mailbox *m)
Sort emails by their disk order.
Definition sort.c:448
Assorted sorting methods.
int(* sort_email_t)(const struct Email *a, const struct Email *b, bool reverse)
Definition sort.h:47
EmailSortType
Methods for sorting Emails.
Definition sort.h:53
@ EMAIL_SORT_LABEL
Sort by the emails label.
Definition sort.h:57
@ EMAIL_SORT_DATE_RECEIVED
Sort by when the message was delivered locally.
Definition sort.h:55
@ EMAIL_SORT_SPAM
Sort by the email's spam score.
Definition sort.h:60
@ EMAIL_SORT_SCORE
Sort by the email's score.
Definition sort.h:58
@ EMAIL_SORT_DATE
Sort by the date the email was sent.
Definition sort.h:54
@ EMAIL_SORT_SUBJECT
Sort by the email's subject.
Definition sort.h:61
@ EMAIL_SORT_FROM
Sort by the email's From field.
Definition sort.h:56
@ EMAIL_SORT_UNSORTED
Sort by the order the messages appear in the mailbox.
Definition sort.h:64
@ EMAIL_SORT_SIZE
Sort by the size of the email.
Definition sort.h:59
@ EMAIL_SORT_TO
Sort by the email's To field.
Definition sort.h:63
Representation of an email.
Representation of an email header (envelope)
#define mutt_error(...)
Definition logging2.h:94
#define mutt_message(...)
Definition logging2.h:93
int mutt_compare_emails(const struct Email *a, const struct Email *b, enum MailboxType type, short sort, short sort_aux)
Compare two emails using up to two sort methods -.
Definition sort.c:328
static int email_sort_shim(const void *a, const void *b, void *sdata)
Helper to sort emails - Implements sort_t -.
Definition sort.c:68
static int email_sort_date(const struct Email *a, const struct Email *b, bool reverse)
Compare the sent date of two emails - Implements sort_email_t -.
Definition sort.c:97
int nntp_sort_unsorted(const struct Email *a, const struct Email *b, bool reverse)
Restore the 'unsorted' order of emails - Implements sort_email_t -.
Definition nntp.c:2356
static int email_sort_spam(const struct Email *a, const struct Email *b, bool reverse)
Compare the spam values of two emails - Implements sort_email_t -.
Definition sort.c:203
static int email_sort_size(const struct Email *a, const struct Email *b, bool reverse)
Compare the size of two emails - Implements sort_email_t -.
Definition sort.c:88
static int email_sort_score(const struct Email *a, const struct Email *b, bool reverse)
Compare two emails using their scores - Implements sort_email_t -.
Definition sort.c:79
static int email_sort_to(const struct Email *a, const struct Email *b, bool reverse)
Compare the 'to' fields of two emails - Implements sort_email_t -.
Definition sort.c:159
static int email_sort_subject(const struct Email *a, const struct Email *b, bool reverse)
Compare the subject of two emails - Implements sort_email_t -.
Definition sort.c:106
static int email_sort_from(const struct Email *a, const struct Email *b, bool reverse)
Compare the 'from' fields of two emails - Implements sort_email_t -.
Definition sort.c:172
static int email_sort_label(const struct Email *a, const struct Email *b, bool reverse)
Compare the labels of two emails - Implements sort_email_t -.
Definition sort.c:252
static int email_sort_date_received(const struct Email *a, const struct Email *b, bool reverse)
Compare the date received of two emails - Implements sort_email_t -.
Definition sort.c:185
static int email_sort_unsorted(const struct Email *a, const struct Email *b, bool reverse)
Restore the 'unsorted' order of emails - Implements sort_email_t -.
Definition sort.c:194
Convenience wrapper for the gui headers.
void mutt_clear_threads(struct ThreadsContext *tctx)
Clear the threading of message in a mailbox.
Definition thread.c:797
void mutt_thread_collapse_collapsed(struct ThreadsContext *tctx)
Re-collapse threads marked as collapsed.
Definition thread.c:1847
void mutt_sort_threads(struct ThreadsContext *tctx, bool init)
Sort email threads.
Definition thread.c:1110
off_t mutt_set_vnum(struct Mailbox *m)
Set the virtual index number of all the messages in a mailbox.
Definition thread.c:1483
#define mutt_using_threads()
Definition thread.h:113
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
int mutt_str_cmp(const char *a, const char *b)
Compare two strings, safely.
Definition string.c:403
int mutt_istrn_cmp(const char *a, const char *b, size_t num)
Compare two strings ignoring case (to a maximum), safely.
Definition string.c:443
int mutt_istr_cmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition string.c:416
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition string.c:586
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
NeoMutt Logging.
enum MailboxType mx_type(struct Mailbox *m)
Return the type of the Mailbox.
Definition mx.c:1810
API for mailboxes.
Usenet network mailbox type; talk to an NNTP server.
void mutt_qsort_r(void *base, size_t nmemb, size_t size, sort_t compar, void *sdata)
Sort an array, where the comparator has access to opaque data rather than requiring global variables.
Definition qsort_r.c:72
#define TAILQ_FIRST(head)
Definition queue.h:780
void mutt_score_message(struct Mailbox *m, struct Email *e, bool upd_mbox)
Apply scoring to an email.
Definition score.c:243
Routines for adding user scores to emails.
An email address.
Definition address.h:35
struct Buffer * personal
Real name of address.
Definition address.h:36
struct Buffer * mailbox
Mailbox and host address.
Definition address.h:37
LOFF_T length
length (in bytes) of attachment
Definition body.h:53
char * data
Pointer to data.
Definition buffer.h:37
Context for email_sort_shim()
Definition sort.c:59
short sort_aux
Secondary sort.
Definition sort.c:62
short sort
Primary sort.
Definition sort.c:61
enum MailboxType type
Current mailbox type.
Definition sort.c:60
The envelope/body of an email.
Definition email.h:39
bool visible
Is this message part of the view?
Definition email.h:121
struct Envelope * env
Envelope information.
Definition email.h:68
bool collapsed
Is this message part of a collapsed thread?
Definition email.h:120
struct Body * body
List of MIME parts.
Definition email.h:69
bool threaded
Used for threading.
Definition email.h:108
time_t date_sent
Time when the message was sent (UTC)
Definition email.h:60
int vnum
Virtual message number.
Definition email.h:114
int score
Message score.
Definition email.h:113
int msgno
Number displayed to the user.
Definition email.h:111
int index
The absolute (unsorted) message number.
Definition email.h:110
time_t received
Time when the message was placed in the mailbox.
Definition email.h:61
struct AddressList to
Email's 'To' list.
Definition envelope.h:60
struct Buffer spam
Spam header.
Definition envelope.h:82
char * x_label
X-Label.
Definition envelope.h:76
char *const real_subj
Offset of the real subject.
Definition envelope.h:71
struct AddressList from
Email's 'From' list.
Definition envelope.h:59
View of a Mailbox.
Definition mview.h:40
off_t vsize
Size (in bytes) of the messages shown.
Definition mview.h:41
struct ThreadsContext * threads
Threads context.
Definition mview.h:44
struct Mailbox * mailbox
Current Mailbox.
Definition mview.h:51
A mailbox.
Definition mailbox.h:78
int vcount
The number of virtual messages.
Definition mailbox.h:98
int * v2r
Mapping from virtual to real msgno.
Definition mailbox.h:97
int msg_count
Total number of messages.
Definition mailbox.h:87
struct Email ** emails
Array of Emails.
Definition mailbox.h:95
bool verbose
Display status messages?
Definition mailbox.h:116
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49