NeoMutt  2023-11-03-85-g512e01
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
sort.c
Go to the documentation of this file.
1
31#include "config.h"
32#include <stdbool.h>
33#include <stdlib.h>
34#include "mutt/lib.h"
35#include "address/lib.h"
36#include "config/lib.h"
37#include "email/lib.h"
38#include "core/lib.h"
39#include "alias/lib.h"
40#include "sort.h"
41#include "nntp/lib.h"
42#include "globals.h"
43#include "mutt_logging.h"
44#include "mutt_thread.h"
45#include "mview.h"
46#include "mx.h"
47#include "score.h"
48
53{
55 short sort;
56 short sort_aux;
57};
58
62static int compare_email_shim(const void *a, const void *b, void *sdata)
63{
64 const struct Email *ea = *(struct Email const *const *) a;
65 const struct Email *eb = *(struct Email const *const *) b;
66 const struct EmailCompare *cmp = sdata;
67 return mutt_compare_emails(ea, eb, cmp->type, cmp->sort, cmp->sort_aux);
68}
69
73static int compare_score(const struct Email *a, const struct Email *b, bool reverse)
74{
75 int result = mutt_numeric_cmp(b->score, a->score); /* note that this is reverse */
76 return reverse ? -result : result;
77}
78
82static int compare_size(const struct Email *a, const struct Email *b, bool reverse)
83{
84 int result = mutt_numeric_cmp(a->body->length, b->body->length);
85 return reverse ? -result : result;
86}
87
91static int compare_date_sent(const struct Email *a, const struct Email *b, bool reverse)
92{
93 int result = mutt_numeric_cmp(a->date_sent, b->date_sent);
94 return reverse ? -result : result;
95}
96
100static int compare_subject(const struct Email *a, const struct Email *b, bool reverse)
101{
102 int rc;
103
104 if (!a->env->real_subj)
105 {
106 if (!b->env->real_subj)
107 rc = compare_date_sent(a, b, false);
108 else
109 rc = -1;
110 }
111 else if (!b->env->real_subj)
112 {
113 rc = 1;
114 }
115 else
116 {
117 rc = mutt_istr_cmp(a->env->real_subj, b->env->real_subj);
118 }
119 return reverse ? -rc : rc;
120}
121
132const char *mutt_get_name(const struct Address *a)
133{
134 struct Address *ali = NULL;
135
136 if (a)
137 {
138 const bool c_reverse_alias = cs_subset_bool(NeoMutt->sub, "reverse_alias");
139 if (c_reverse_alias && (ali = alias_reverse_lookup(a)) && ali->personal)
140 return buf_string(ali->personal);
141 if (a->personal)
142 return buf_string(a->personal);
143 if (a->mailbox)
144 return mutt_addr_for_display(a);
145 }
146 /* don't return NULL to avoid segfault when printing/comparing */
147 return "";
148}
149
153static int compare_to(const struct Email *a, const struct Email *b, bool reverse)
154{
155 char fa[128] = { 0 };
156
157 mutt_str_copy(fa, mutt_get_name(TAILQ_FIRST(&a->env->to)), sizeof(fa));
158 const char *fb = mutt_get_name(TAILQ_FIRST(&b->env->to));
159 int result = mutt_istrn_cmp(fa, fb, sizeof(fa));
160 return reverse ? -result : result;
161}
162
166static int compare_from(const struct Email *a, const struct Email *b, bool reverse)
167{
168 char fa[128] = { 0 };
169
170 mutt_str_copy(fa, mutt_get_name(TAILQ_FIRST(&a->env->from)), sizeof(fa));
171 const char *fb = mutt_get_name(TAILQ_FIRST(&b->env->from));
172 int result = mutt_istrn_cmp(fa, fb, sizeof(fa));
173 return reverse ? -result : result;
174}
175
179static int compare_date_received(const struct Email *a, const struct Email *b, bool reverse)
180{
181 int result = mutt_numeric_cmp(a->received, b->received);
182 return reverse ? -result : result;
183}
184
188static int compare_order(const struct Email *a, const struct Email *b, bool reverse)
189{
190 int result = mutt_numeric_cmp(a->index, b->index);
191 return reverse ? -result : result;
192}
193
197static int compare_spam(const struct Email *a, const struct Email *b, bool reverse)
198{
199 char *aptr = NULL, *bptr = NULL;
200 int ahas, bhas;
201 int result = 0;
202 double difference;
203
204 /* Firstly, require spam attributes for both msgs */
205 /* to compare. Determine which msgs have one. */
206 ahas = a->env && !buf_is_empty(&a->env->spam);
207 bhas = b->env && !buf_is_empty(&b->env->spam);
208
209 /* If one msg has spam attr but other does not, sort the one with first. */
210 if (ahas && !bhas)
211 return reverse ? -1 : 1;
212 if (!ahas && bhas)
213 return reverse ? 1 : -1;
214
215 /* Else, if neither has a spam attr, presume equality. Fall back on aux. */
216 if (!ahas && !bhas)
217 return 0;
218
219 /* Both have spam attrs. */
220
221 /* preliminary numeric examination */
222 difference = (strtod(a->env->spam.data, &aptr) - strtod(b->env->spam.data, &bptr));
223
224 /* map double into comparison (-1, 0, or 1) */
225 result = ((difference < 0.0) ? -1 : (difference > 0.0) ? 1 : 0);
226
227 /* If either aptr or bptr is equal to data, there is no numeric */
228 /* value for that spam attribute. In this case, compare lexically. */
229 if ((aptr == a->env->spam.data) || (bptr == b->env->spam.data))
230 {
231 result = mutt_str_cmp(aptr, bptr);
232 return reverse ? -result : result;
233 }
234
235 /* Otherwise, we have numeric value for both attrs. If these values */
236 /* are equal, then we first fall back upon string comparison, then */
237 /* upon auxiliary sort. */
238 if (result == 0)
239 result = mutt_str_cmp(aptr, bptr);
240 return reverse ? -result : result;
241}
242
246static int compare_label(const struct Email *a, const struct Email *b, bool reverse)
247{
248 int ahas, bhas, result = 0;
249
250 /* As with compare_spam, not all messages will have the x-label
251 * property. Blank X-Labels are treated as null in the index
252 * display, so we'll consider them as null for sort, too. */
253 ahas = a->env && a->env->x_label && *(a->env->x_label);
254 bhas = b->env && b->env->x_label && *(b->env->x_label);
255
256 /* First we bias toward a message with a label, if the other does not. */
257 if (ahas && !bhas)
258 return reverse ? 1 : -1;
259 if (!ahas && bhas)
260 return reverse ? -1 : 1;
261
262 /* If neither has a label, use aux sort. */
263 if (!ahas && !bhas)
264 return 0;
265
266 /* If both have a label, we just do a lexical compare. */
267 result = mutt_istr_cmp(a->env->x_label, b->env->x_label);
268 return reverse ? -result : result;
269}
270
277static sort_mail_t get_sort_func(enum SortType method, enum MailboxType type)
278{
279 switch (method)
280 {
281 case SORT_DATE:
282 return compare_date_sent;
283 case SORT_FROM:
284 return compare_from;
285 case SORT_LABEL:
286 return compare_label;
287 case SORT_ORDER:
288 if (type == MUTT_NNTP)
289 return nntp_compare_order;
290 else
291 return compare_order;
292 case SORT_RECEIVED:
294 case SORT_SCORE:
295 return compare_score;
296 case SORT_SIZE:
297 return compare_size;
298 case SORT_SPAM:
299 return compare_spam;
300 case SORT_SUBJECT:
301 return compare_subject;
302 case SORT_TO:
303 return compare_to;
304 default:
305 mutt_error(_("Could not find sorting function [report this bug]"));
306 return NULL;
307 }
308 /* not reached */
309}
310
322int mutt_compare_emails(const struct Email *a, const struct Email *b,
323 enum MailboxType type, short sort, short sort_aux)
324{
325 sort_mail_t func = get_sort_func(sort & SORT_MASK, type);
326 int rc = func(a, b, (sort & SORT_REVERSE) != 0);
327 if (rc == 0)
328 {
329 func = get_sort_func(sort_aux & SORT_MASK, type);
330 rc = func(a, b, (sort_aux & SORT_REVERSE) != 0);
331 }
332 if (rc == 0)
333 {
334 /* Fallback of last resort to preserve stable order; will only
335 * return 0 if a and b have the same index, which is probably a
336 * bug in the code. */
337 func = compare_order;
338 rc = func(a, b, false);
339 }
340 return rc;
341}
342
348void mutt_sort_headers(struct MailboxView *mv, bool init)
349{
350 if (!mv)
351 return;
352
353 struct Mailbox *m = mv->mailbox;
354 if (!m || !m->emails[0])
355 return;
356
357 OptNeedResort = false;
358
359 if (m->msg_count == 0)
360 {
361 /* this function gets called by mutt_sync_mailbox(), which may have just
362 * deleted all the messages. the virtual message numbers are not updated
363 * in that routine, so we must make sure to zero the vcount member. */
364 m->vcount = 0;
366 mv->vsize = 0;
367 return; /* nothing to do! */
368 }
369
370 if (m->verbose)
371 mutt_message(_("Sorting mailbox..."));
372
373 const bool c_score = cs_subset_bool(NeoMutt->sub, "score");
374 if (OptNeedRescore && c_score)
375 {
376 for (int i = 0; i < m->msg_count; i++)
377 {
378 struct Email *e = m->emails[i];
379 if (!e)
380 break;
381 mutt_score_message(m, e, true);
382 }
383 }
384 OptNeedRescore = false;
385
386 if (OptResortInit)
387 {
388 OptResortInit = false;
389 init = true;
390 }
391
392 if (init)
394
395 const bool threaded = mutt_using_threads();
396 if (threaded)
397 {
398 mutt_sort_threads(mv->threads, init);
399 }
400 else
401 {
402 struct EmailCompare cmp = { 0 };
403 cmp.type = mx_type(m);
404 cmp.sort = cs_subset_sort(NeoMutt->sub, "sort");
405 cmp.sort_aux = cs_subset_sort(NeoMutt->sub, "sort_aux");
406 mutt_qsort_r((void *) m->emails, m->msg_count, sizeof(struct Email *),
407 compare_email_shim, &cmp);
408 }
409
410 /* adjust the virtual message numbers */
411 m->vcount = 0;
412 for (int i = 0; i < m->msg_count; i++)
413 {
414 struct Email *e_cur = m->emails[i];
415 if (!e_cur)
416 break;
417
418 if ((e_cur->vnum != -1) || (e_cur->collapsed && e_cur->visible))
419 {
420 e_cur->vnum = m->vcount;
421 m->v2r[m->vcount] = i;
422 m->vcount++;
423 }
424 e_cur->msgno = i;
425 }
426
427 /* re-collapse threads marked as collapsed */
428 if (threaded)
429 {
431 mv->vsize = mutt_set_vnum(m);
432 }
433
434 if (m->verbose)
436
437 return;
438}
const char * mutt_addr_for_display(const struct Address *a)
Convert an Address for display purposes.
Definition: address.c:1009
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:105
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:303
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:267
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
Structs that make up an email.
bool OptNeedRescore
(pseudo) set when the 'score' command is used
Definition: globals.c:73
bool OptResortInit
(pseudo) used to force the next resort to be from scratch
Definition: globals.c:79
bool OptNeedResort
(pseudo) used to force a re-sort
Definition: globals.c:74
Global variables.
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_message(...)
Definition: logging2.h:91
static int compare_email_shim(const void *a, const void *b, void *sdata)
Helper to sort emails - Implements sort_t -.
Definition: sort.c:62
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:322
static int compare_to(const struct Email *a, const struct Email *b, bool reverse)
Compare the 'to' fields of two emails - Implements sort_mail_t -.
Definition: sort.c:153
int nntp_compare_order(const struct Email *a, const struct Email *b, bool reverse)
Restore the 'unsorted' order of emails - Implements sort_mail_t -.
Definition: nntp.c:2308
static int compare_label(const struct Email *a, const struct Email *b, bool reverse)
Compare the labels of two emails - Implements sort_mail_t -.
Definition: sort.c:246
static int compare_spam(const struct Email *a, const struct Email *b, bool reverse)
Compare the spam values of two emails - Implements sort_mail_t -.
Definition: sort.c:197
static int compare_order(const struct Email *a, const struct Email *b, bool reverse)
Restore the 'unsorted' order of emails - Implements sort_mail_t -.
Definition: sort.c:188
static int compare_date_sent(const struct Email *a, const struct Email *b, bool reverse)
Compare the sent date of two emails - Implements sort_mail_t -.
Definition: sort.c:91
static int compare_subject(const struct Email *a, const struct Email *b, bool reverse)
Compare the subject of two emails - Implements sort_mail_t -.
Definition: sort.c:100
static int compare_score(const struct Email *a, const struct Email *b, bool reverse)
Compare two emails using their scores - Implements sort_mail_t -.
Definition: sort.c:73
static int compare_from(const struct Email *a, const struct Email *b, bool reverse)
Compare the 'from' fields of two emails - Implements sort_mail_t -.
Definition: sort.c:166
static int compare_size(const struct Email *a, const struct Email *b, bool reverse)
Compare the size of two emails - Implements sort_mail_t -.
Definition: sort.c:82
static int compare_date_received(const struct Email *a, const struct Email *b, bool reverse)
Compare the date received of two emails - Implements sort_mail_t -.
Definition: sort.c:179
MailboxType
Supported mailbox formats.
Definition: mailbox.h:41
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition: mailbox.h:49
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:471
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:511
int mutt_istr_cmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:484
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:653
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:73
NeoMutt Logging.
void mutt_clear_threads(struct ThreadsContext *tctx)
Clear the threading of message in a mailbox.
Definition: mutt_thread.c:717
void mutt_thread_collapse_collapsed(struct ThreadsContext *tctx)
Re-collapse threads marked as collapsed.
Definition: mutt_thread.c:1765
void mutt_sort_threads(struct ThreadsContext *tctx, bool init)
Sort email threads.
Definition: mutt_thread.c:1028
off_t mutt_set_vnum(struct Mailbox *m)
Set the virtual index number of all the messages in a mailbox.
Definition: mutt_thread.c:1401
Create/manipulate threading in emails.
#define mutt_using_threads()
Definition: mutt_thread.h:112
View of a Mailbox.
enum MailboxType mx_type(struct Mailbox *m)
Return the type of the Mailbox.
Definition: mx.c:1812
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:66
#define TAILQ_FIRST(head)
Definition: queue.h:723
void mutt_score_message(struct Mailbox *m, struct Email *e, bool upd_mbox)
Apply scoring to an email.
Definition: score.c:161
Routines for adding user scores to emails.
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:74
SortType
Methods for sorting.
Definition: sort2.h:38
@ SORT_SUBJECT
Sort by the email's subject.
Definition: sort2.h:42
@ SORT_ORDER
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:44
@ SORT_SPAM
Sort by the email's spam score.
Definition: sort2.h:53
@ SORT_LABEL
Sort by the emails label.
Definition: sort2.h:58
@ SORT_FROM
Sort by the email's From field.
Definition: sort2.h:43
@ SORT_SIZE
Sort by the size of the email.
Definition: sort2.h:40
@ SORT_RECEIVED
Sort by when the message were delivered locally.
Definition: sort2.h:46
@ SORT_TO
Sort by the email's To field.
Definition: sort2.h:47
@ SORT_DATE
Sort by the date the email was sent.
Definition: sort2.h:39
@ SORT_SCORE
Sort by the email's score.
Definition: sort2.h:48
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:75
const char * mutt_get_name(const struct Address *a)
Pick the best name to display from an address.
Definition: sort.c:132
static sort_mail_t get_sort_func(enum SortType method, enum MailboxType type)
Get the sort function for a given sort id.
Definition: sort.c:277
void mutt_sort_headers(struct MailboxView *mv, bool init)
Sort emails by their headers.
Definition: sort.c:348
Assorted sorting methods.
int(* sort_mail_t)(const struct Email *a, const struct Email *b, bool reverse)
Definition: sort.h:48
#define mutt_numeric_cmp(a, b)
Definition: sort.h:34
An email address.
Definition: address.h:36
struct Buffer * personal
Real name of address.
Definition: address.h:37
struct Buffer * mailbox
Mailbox and host address.
Definition: address.h:38
LOFF_T length
length (in bytes) of attachment
Definition: body.h:53
char * data
Pointer to data.
Definition: buffer.h:35
Context for compare_email_shim()
Definition: sort.c:53
short sort_aux
Secondary sort.
Definition: sort.c:56
short sort
Primary sort.
Definition: sort.c:55
enum MailboxType type
Current mailbox type.
Definition: sort.c:54
The envelope/body of an email.
Definition: email.h:37
bool visible
Is this message part of the view?
Definition: email.h:122
struct Envelope * env
Envelope information.
Definition: email.h:66
bool collapsed
Is this message part of a collapsed thread?
Definition: email.h:121
struct Body * body
List of MIME parts.
Definition: email.h:67
bool threaded
Used for threading.
Definition: email.h:109
time_t date_sent
Time when the message was sent (UTC)
Definition: email.h:58
int vnum
Virtual message number.
Definition: email.h:115
int score
Message score.
Definition: email.h:114
int msgno
Number displayed to the user.
Definition: email.h:112
int index
The absolute (unsorted) message number.
Definition: email.h:111
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:59
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 * 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:39
off_t vsize
Size (in bytes) of the messages shown.
Definition: mview.h:40
struct ThreadsContext * threads
Threads context.
Definition: mview.h:43
struct Mailbox * mailbox
Current Mailbox.
Definition: mview.h:50
A mailbox.
Definition: mailbox.h:79
int vcount
The number of virtual messages.
Definition: mailbox.h:99
int * v2r
Mapping from virtual to real msgno.
Definition: mailbox.h:98
int msg_count
Total number of messages.
Definition: mailbox.h:88
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
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:45