NeoMutt  2019-12-07-60-g0cfa53
Teaching an old dog new tricks
DOXYGEN
mutt_thread.c File Reference

Create/manipulate threading in emails. More...

#include "config.h"
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "mutt/mutt.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "mutt.h"
#include "mutt_thread.h"
#include "context.h"
#include "mutt_menu.h"
#include "mx.h"
#include "protos.h"
#include "sort.h"
+ Include dependency graph for mutt_thread.c:

Go to the source code of this file.

Macros

#define CHECK_LIMIT   (!ctx->pattern || e_cur->limited)
 

Functions

static bool is_visible (struct Email *e, struct Context *ctx)
 Is the message visible? More...
 
static bool need_display_subject (struct Context *ctx, struct Email *e)
 Determines whether to display a message's subject. More...
 
static void linearize_tree (struct Context *ctx)
 Flatten an email thread. More...
 
static void calculate_visibility (struct Context *ctx, int *max_depth)
 Are tree nodes visible. More...
 
void mutt_draw_tree (struct Context *ctx)
 Draw a tree of threaded emails. More...
 
static void make_subject_list (struct ListHead *subjects, struct MuttThread *cur, time_t *dateptr)
 Create a sorted list of all subjects in a thread. More...
 
static struct MuttThreadfind_subject (struct Mailbox *m, struct MuttThread *cur)
 Find the best possible match for a parent based on subject. More...
 
static struct Hashmake_subj_hash (struct Mailbox *m)
 Create a Hash Table for the email subjects. More...
 
static void pseudo_threads (struct Context *ctx)
 Thread messages by subject. More...
 
void mutt_clear_threads (struct Context *ctx)
 Clear the threading of message in a mailbox. More...
 
static int compare_threads (const void *a, const void *b)
 Sorting function for email threads. More...
 
struct MuttThreadmutt_sort_subthreads (struct MuttThread *thread, bool init)
 Sort the children of a thread. More...
 
static void check_subjects (struct Mailbox *m, bool init)
 Find out which emails' subjects differ from their parent's. More...
 
void mutt_sort_threads (struct Context *ctx, bool init)
 Sort email threads. More...
 
int mutt_aside_thread (struct Email *e, bool forwards, bool subthreads)
 Find the next/previous (sub)thread. More...
 
int mutt_parent_message (struct Context *ctx, struct Email *e, bool find_root)
 Find the parent of a message. More...
 
void mutt_set_vnum (struct Context *ctx)
 Set the virtual index number of all the messages in a mailbox. More...
 
int mutt_traverse_thread (struct Context *ctx, struct Email *e_cur, MuttThreadFlags flag)
 Recurse through an email thread, matching messages. More...
 
int mutt_messages_in_thread (struct Mailbox *m, struct Email *e, int flag)
 Count the messages in a thread. More...
 
struct Hashmutt_make_id_hash (struct Mailbox *m)
 Create a Hash Table for message-ids. More...
 
static bool link_threads (struct Email *parent, struct Email *child, struct Mailbox *m)
 Forcibly link messages together. More...
 
bool mutt_link_threads (struct Email *parent, struct EmailList *children, struct Mailbox *m)
 Forcibly link threads together. More...
 

Variables

bool C_DuplicateThreads
 Config: Highlight messages with duplicated message IDs. More...
 
bool C_HideLimited
 Config: Don't indicate hidden messages, in the thread tree. More...
 
bool C_HideMissing
 Config: Don't indicate missing messages, in the thread tree. More...
 
bool C_HideThreadSubject
 Config: Hide subjects that are similar to that of the parent message. More...
 
bool C_HideTopLimited
 Config: Don't indicate hidden top message, in the thread tree. More...
 
bool C_HideTopMissing
 Config: Don't indicate missing top message, in the thread tree. More...
 
bool C_NarrowTree
 Config: Draw a narrower thread tree in the index. More...
 
bool C_SortRe
 Config: Sort method for the sidebar. More...
 
bool C_StrictThreads
 Config: Thread messages using 'In-Reply-To' and 'References' headers. More...
 
bool C_ThreadReceived
 Config: Sort threaded messages by their received date. More...
 

Detailed Description

Create/manipulate threading in emails.

Authors
  • Michael R. Elkins

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file mutt_thread.c.

Macro Definition Documentation

◆ CHECK_LIMIT

#define CHECK_LIMIT   (!ctx->pattern || e_cur->limited)

Function Documentation

◆ is_visible()

static bool is_visible ( struct Email e,
struct Context ctx 
)
static

Is the message visible?

Parameters
eEmail
ctxMailbox
Return values
trueIf the message is not hidden in some way

Definition at line 65 of file mutt_thread.c.

66 {
67  return e->vnum >= 0 || (e->collapsed && (!ctx->pattern || e->limited));
68 }
bool limited
Is this message in a limited view?
Definition: email.h:74
bool collapsed
Is this message part of a collapsed thread?
Definition: email.h:73
int vnum
Virtual message number.
Definition: email.h:87
char * pattern
Limit pattern string.
Definition: context.h:39
+ Here is the caller graph for this function:

◆ need_display_subject()

static bool need_display_subject ( struct Context ctx,
struct Email e 
)
static

Determines whether to display a message's subject.

Parameters
ctxMailbox
eEmail
Return values
trueIf the subject should be displayed

Definition at line 76 of file mutt_thread.c.

77 {
78  struct MuttThread *tmp = NULL;
79  struct MuttThread *tree = e->thread;
80 
81  /* if the user disabled subject hiding, display it */
83  return true;
84 
85  /* if our subject is different from our parent's, display it */
86  if (e->subject_changed)
87  return true;
88 
89  /* if our subject is different from that of our closest previously displayed
90  * sibling, display the subject */
91  for (tmp = tree->prev; tmp; tmp = tmp->prev)
92  {
93  e = tmp->message;
94  if (e && is_visible(e, ctx))
95  {
96  if (e->subject_changed)
97  return true;
98  break;
99  }
100  }
101 
102  /* if there is a parent-to-child subject change anywhere between us and our
103  * closest displayed ancestor, display the subject */
104  for (tmp = tree->parent; tmp; tmp = tmp->parent)
105  {
106  e = tmp->message;
107  if (e)
108  {
109  if (is_visible(e, ctx))
110  return false;
111  if (e->subject_changed)
112  return true;
113  }
114  }
115 
116  /* if we have no visible parent or previous sibling, display the subject */
117  return true;
118 }
static bool is_visible(struct Email *e, struct Context *ctx)
Is the message visible?
Definition: mutt_thread.c:65
struct MuttThread * thread
Thread of Emails.
Definition: email.h:94
bool C_HideThreadSubject
Config: Hide subjects that are similar to that of the parent message.
Definition: mutt_thread.c:51
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
bool subject_changed
Used for threading.
Definition: email.h:55
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ linearize_tree()

static void linearize_tree ( struct Context ctx)
static

Flatten an email thread.

Parameters
ctxMailbox

Definition at line 124 of file mutt_thread.c.

125 {
126  if (!ctx || !ctx->mailbox)
127  return;
128 
129  struct Mailbox *m = ctx->mailbox;
130 
131  struct MuttThread *tree = ctx->tree;
132  struct Email **array = m->emails + ((C_Sort & SORT_REVERSE) ? m->msg_count - 1 : 0);
133 
134  while (tree)
135  {
136  while (!tree->message)
137  tree = tree->child;
138 
139  *array = tree->message;
140  array += (C_Sort & SORT_REVERSE) ? -1 : 1;
141 
142  if (tree->child)
143  tree = tree->child;
144  else
145  {
146  while (tree)
147  {
148  if (tree->next)
149  {
150  tree = tree->next;
151  break;
152  }
153  else
154  tree = tree->parent;
155  }
156  }
157  }
158 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:98
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
int msg_count
Total number of messages.
Definition: mailbox.h:90
The envelope/body of an email.
Definition: email.h:37
struct MuttThread * tree
Top of thread tree.
Definition: context.h:42
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
struct Mailbox * mailbox
Definition: context.h:50
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:58
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
A mailbox.
Definition: mailbox.h:80
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:86
+ Here is the caller graph for this function:

◆ calculate_visibility()

static void calculate_visibility ( struct Context ctx,
int *  max_depth 
)
static

Are tree nodes visible.

Parameters
ctxMailbox
max_depthMaximum depth to check

this calculates whether a node is the root of a subtree that has visible nodes, whether a node itself is visible, whether, if invisible, it has depth anyway, and whether any of its later siblings are roots of visible subtrees. while it's at it, it frees the old thread display, so we can skip parts of the tree in mutt_draw_tree() if we've decided here that we don't care about them any more.

Definition at line 172 of file mutt_thread.c.

173 {
174  struct MuttThread *tmp = NULL;
175  struct MuttThread *tree = ctx->tree;
176  int hide_top_missing = C_HideTopMissing && !C_HideMissing;
177  int hide_top_limited = C_HideTopLimited && !C_HideLimited;
178  int depth = 0;
179 
180  /* we walk each level backwards to make it easier to compute next_subtree_visible */
181  while (tree->next)
182  tree = tree->next;
183  *max_depth = 0;
184 
185  while (true)
186  {
187  if (depth > *max_depth)
188  *max_depth = depth;
189 
190  tree->subtree_visible = 0;
191  if (tree->message)
192  {
193  FREE(&tree->message->tree);
194  if (is_visible(tree->message, ctx))
195  {
196  tree->deep = true;
197  tree->visible = true;
199  for (tmp = tree; tmp; tmp = tmp->parent)
200  {
201  if (tmp->subtree_visible)
202  {
203  tmp->deep = true;
204  tmp->subtree_visible = 2;
205  break;
206  }
207  else
208  tmp->subtree_visible = 1;
209  }
210  }
211  else
212  {
213  tree->visible = false;
214  tree->deep = !C_HideLimited;
215  }
216  }
217  else
218  {
219  tree->visible = false;
220  tree->deep = !C_HideMissing;
221  }
222  tree->next_subtree_visible =
223  tree->next && (tree->next->next_subtree_visible || tree->next->subtree_visible);
224  if (tree->child)
225  {
226  depth++;
227  tree = tree->child;
228  while (tree->next)
229  tree = tree->next;
230  }
231  else if (tree->prev)
232  tree = tree->prev;
233  else
234  {
235  while (tree && !tree->prev)
236  {
237  depth--;
238  tree = tree->parent;
239  }
240  if (!tree)
241  break;
242  tree = tree->prev;
243  }
244  }
245 
246  /* now fix up for the OPTHIDETOP* options if necessary */
247  if (hide_top_limited || hide_top_missing)
248  {
249  tree = ctx->tree;
250  while (true)
251  {
252  if (!tree->visible && tree->deep && (tree->subtree_visible < 2) &&
253  ((tree->message && hide_top_limited) || (!tree->message && hide_top_missing)))
254  {
255  tree->deep = false;
256  }
257  if (!tree->deep && tree->child && tree->subtree_visible)
258  tree = tree->child;
259  else if (tree->next)
260  tree = tree->next;
261  else
262  {
263  while (tree && !tree->next)
264  tree = tree->parent;
265  if (!tree)
266  break;
267  tree = tree->next;
268  }
269  }
270  }
271 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
bool C_HideMissing
Config: Don&#39;t indicate missing messages, in the thread tree.
Definition: mutt_thread.c:50
static bool is_visible(struct Email *e, struct Context *ctx)
Is the message visible?
Definition: mutt_thread.c:65
struct MuttThread * tree
Top of thread tree.
Definition: context.h:42
bool deep
Is the Thread deeply nested?
Definition: thread.h:41
bool display_subject
Used for threading.
Definition: email.h:57
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
bool next_subtree_visible
Is the next Thread subtree visible?
Definition: thread.h:43
bool C_HideLimited
Config: Don&#39;t indicate hidden messages, in the thread tree.
Definition: mutt_thread.c:49
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
bool C_HideTopLimited
Config: Don&#39;t indicate hidden top message, in the thread tree.
Definition: mutt_thread.c:52
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
bool C_HideTopMissing
Config: Don&#39;t indicate missing top message, in the thread tree.
Definition: mutt_thread.c:53
unsigned int subtree_visible
Is this Thread subtree visible?
Definition: thread.h:42
char * tree
Character string to print thread tree.
Definition: email.h:93
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
static bool need_display_subject(struct Context *ctx, struct Email *e)
Determines whether to display a message&#39;s subject.
Definition: mutt_thread.c:76
#define FREE(x)
Definition: memory.h:40
bool visible
Is this Thread visible?
Definition: thread.h:40
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_draw_tree()

void mutt_draw_tree ( struct Context ctx)

Draw a tree of threaded emails.

Parameters
ctxMailbox

Since the graphics characters have a value >255, I have to resort to using escape sequences to pass the information to print_enriched_string(). These are the macros MUTT_TREE_* defined in mutt.h.

ncurses should automatically use the default ASCII characters instead of graphics chars on terminals which don't support them (see the man page for curs_addch).

Definition at line 285 of file mutt_thread.c.

286 {
287  char *pfx = NULL, *mypfx = NULL, *arrow = NULL, *myarrow = NULL, *new_tree = NULL;
290  int depth = 0, start_depth = 0, max_depth = 0, width = C_NarrowTree ? 1 : 2;
291  struct MuttThread *nextdisp = NULL, *pseudo = NULL, *parent = NULL;
292  struct MuttThread *tree = ctx->tree;
293 
294  /* Do the visibility calculations and free the old thread chars.
295  * From now on we can simply ignore invisible subtrees */
296  calculate_visibility(ctx, &max_depth);
297  pfx = mutt_mem_malloc((width * max_depth) + 2);
298  arrow = mutt_mem_malloc((width * max_depth) + 2);
299  while (tree)
300  {
301  if (depth != 0)
302  {
303  myarrow = arrow + (depth - start_depth - ((start_depth != 0) ? 0 : 1)) * width;
304  if (start_depth == depth)
305  myarrow[0] = nextdisp ? MUTT_TREE_LTEE : corner;
306  else if (parent->message && !C_HideLimited)
307  myarrow[0] = MUTT_TREE_HIDDEN;
308  else if (!parent->message && !C_HideMissing)
309  myarrow[0] = MUTT_TREE_MISSING;
310  else
311  myarrow[0] = vtee;
312  if (width == 2)
313  {
314  myarrow[1] = pseudo ? MUTT_TREE_STAR :
316  }
317  if (tree->visible)
318  {
319  myarrow[width] = MUTT_TREE_RARROW;
320  myarrow[width + 1] = 0;
321  new_tree = mutt_mem_malloc(((size_t) depth * width) + 2);
322  if (start_depth > 1)
323  {
324  strncpy(new_tree, pfx, (size_t) width * (start_depth - 1));
325  mutt_str_strfcpy(new_tree + (start_depth - 1) * width, arrow,
326  (1 + depth - start_depth) * width + 2);
327  }
328  else
329  mutt_str_strfcpy(new_tree, arrow, ((size_t) depth * width) + 2);
330  tree->message->tree = new_tree;
331  }
332  }
333  if (tree->child && (depth != 0))
334  {
335  mypfx = pfx + (depth - 1) * width;
336  mypfx[0] = nextdisp ? MUTT_TREE_VLINE : MUTT_TREE_SPACE;
337  if (width == 2)
338  mypfx[1] = MUTT_TREE_SPACE;
339  }
340  parent = tree;
341  nextdisp = NULL;
342  pseudo = NULL;
343  do
344  {
345  if (tree->child && tree->subtree_visible)
346  {
347  if (tree->deep)
348  depth++;
349  if (tree->visible)
350  start_depth = depth;
351  tree = tree->child;
352 
353  /* we do this here because we need to make sure that the first child thread
354  * of the old tree that we deal with is actually displayed if any are,
355  * or we might set the parent variable wrong while going through it. */
356  while (!tree->subtree_visible && tree->next)
357  tree = tree->next;
358  }
359  else
360  {
361  while (!tree->next && tree->parent)
362  {
363  if (tree == pseudo)
364  pseudo = NULL;
365  if (tree == nextdisp)
366  nextdisp = NULL;
367  if (tree->visible)
368  start_depth = depth;
369  tree = tree->parent;
370  if (tree->deep)
371  {
372  if (start_depth == depth)
373  start_depth--;
374  depth--;
375  }
376  }
377  if (tree == pseudo)
378  pseudo = NULL;
379  if (tree == nextdisp)
380  nextdisp = NULL;
381  if (tree->visible)
382  start_depth = depth;
383  tree = tree->next;
384  if (!tree)
385  break;
386  }
387  if (!pseudo && tree->fake_thread)
388  pseudo = tree;
389  if (!nextdisp && tree->next_subtree_visible)
390  nextdisp = tree;
391  } while (!tree->deep);
392  }
393 
394  FREE(&pfx);
395  FREE(&arrow);
396 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
Lower left corner.
Definition: mutt_menu.h:61
bool C_HideMissing
Config: Don&#39;t indicate missing messages, in the thread tree.
Definition: mutt_thread.c:50
struct MuttThread * tree
Top of thread tree.
Definition: context.h:42
bool deep
Is the Thread deeply nested?
Definition: thread.h:41
static void calculate_visibility(struct Context *ctx, int *max_depth)
Are tree nodes visible.
Definition: mutt_thread.c:172
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
bool next_subtree_visible
Is the next Thread subtree visible?
Definition: thread.h:43
Right arrow.
Definition: mutt_menu.h:67
bool C_HideLimited
Config: Don&#39;t indicate hidden messages, in the thread tree.
Definition: mutt_thread.c:49
Bottom T-piece.
Definition: mutt_menu.h:72
Blank space.
Definition: mutt_menu.h:66
Vertical line.
Definition: mutt_menu.h:65
Top T-piece.
Definition: mutt_menu.h:71
Star character (for threads)
Definition: mutt_menu.h:68
Equals (for threads)
Definition: mutt_menu.h:70
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:58
Ampersand character (for threads)
Definition: mutt_menu.h:69
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
bool duplicate_thread
Duplicated Email in Thread.
Definition: thread.h:37
Question mark.
Definition: mutt_menu.h:73
bool fake_thread
Emails grouped by Subject.
Definition: thread.h:36
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
unsigned int subtree_visible
Is this Thread subtree visible?
Definition: thread.h:42
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:750
TreeChar
Tree characters for menus.
Definition: mutt_menu.h:59
char * tree
Character string to print thread tree.
Definition: email.h:93
bool C_NarrowTree
Config: Draw a narrower thread tree in the index.
Definition: mutt_thread.c:54
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
#define FREE(x)
Definition: memory.h:40
bool visible
Is this Thread visible?
Definition: thread.h:40
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:86
Left T-piece.
Definition: mutt_menu.h:63
Upper left corner.
Definition: mutt_menu.h:62
Horizontal line.
Definition: mutt_menu.h:64
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ make_subject_list()

static void make_subject_list ( struct ListHead *  subjects,
struct MuttThread cur,
time_t *  dateptr 
)
static

Create a sorted list of all subjects in a thread.

Parameters
[out]subjectsString List of subjects
[in]curEmail Thread
[out]dateptrEarliest date found in thread

Since we may be trying to attach as a pseudo-thread a MuttThread that has no message, we have to make a list of all the subjects of its most immediate existing descendants.

Definition at line 408 of file mutt_thread.c.

409 {
410  struct MuttThread *start = cur;
411  struct Envelope *env = NULL;
412  time_t thisdate;
413  int rc = 0;
414 
415  while (true)
416  {
417  while (!cur->message)
418  cur = cur->child;
419 
420  if (dateptr)
421  {
422  thisdate = C_ThreadReceived ? cur->message->received : cur->message->date_sent;
423  if (!*dateptr || (thisdate < *dateptr))
424  *dateptr = thisdate;
425  }
426 
427  env = cur->message->env;
428  if (env->real_subj && ((env->real_subj != env->subject) || (!C_SortRe)))
429  {
430  struct ListNode *np = NULL;
431  STAILQ_FOREACH(np, subjects, entries)
432  {
433  rc = mutt_str_strcmp(env->real_subj, np->data);
434  if (rc >= 0)
435  break;
436  }
437  if (!np)
438  mutt_list_insert_head(subjects, env->real_subj);
439  else if (rc > 0)
440  mutt_list_insert_after(subjects, np, env->real_subj);
441  }
442 
443  while (!cur->next && (cur != start))
444  {
445  cur = cur->parent;
446  }
447  if (cur == start)
448  break;
449  cur = cur->next;
450  }
451 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
char * real_subj
Offset of the real subject.
Definition: envelope.h:67
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
bool C_SortRe
Config: Sort method for the sidebar.
Definition: mutt_thread.c:55
struct Envelope * env
Envelope information.
Definition: email.h:89
time_t date_sent
Time when the message was sent (UTC)
Definition: email.h:81
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
struct ListNode * mutt_list_insert_after(struct ListHead *h, struct ListNode *n, char *s)
Insert a string after a given ListNode.
Definition: list.c:85
struct ListNode * mutt_list_insert_head(struct ListHead *h, char *s)
Insert a string at the beginning of a List.
Definition: list.c:46
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
char * data
String.
Definition: list.h:35
char * subject
Email&#39;s subject.
Definition: envelope.h:66
A List node for strings.
Definition: list.h:33
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:615
bool C_ThreadReceived
Config: Sort threaded messages by their received date.
Definition: mutt_thread.c:57
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:82
The header of an Email.
Definition: envelope.h:54
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ find_subject()

static struct MuttThread* find_subject ( struct Mailbox m,
struct MuttThread cur 
)
static

Find the best possible match for a parent based on subject.

Parameters
mMailbox
curEmail to match
Return values
ptrBest match for a parent

If there are multiple matches, the one which was sent the latest, but before the current message, is used.

Definition at line 462 of file mutt_thread.c.

463 {
464  if (!m)
465  return NULL;
466 
467  struct HashElem *ptr = NULL;
468  struct MuttThread *tmp = NULL, *last = NULL;
469  struct ListHead subjects = STAILQ_HEAD_INITIALIZER(subjects);
470  time_t date = 0;
471 
472  make_subject_list(&subjects, cur, &date);
473 
474  struct ListNode *np = NULL;
475  STAILQ_FOREACH(np, &subjects, entries)
476  {
477  for (ptr = mutt_hash_find_bucket(m->subj_hash, np->data); ptr; ptr = ptr->next)
478  {
479  tmp = ((struct Email *) ptr->data)->thread;
480  if ((tmp != cur) && /* don't match the same message */
481  !tmp->fake_thread && /* don't match pseudo threads */
482  tmp->message->subject_changed && /* only match interesting replies */
483  !is_descendant(tmp, cur) && /* don't match in the same thread */
484  (date >= (C_ThreadReceived ? tmp->message->received : tmp->message->date_sent)) &&
485  (!last || (C_ThreadReceived ?
486  (last->message->received < tmp->message->received) :
487  (last->message->date_sent < tmp->message->date_sent))) &&
488  tmp->message->env->real_subj &&
489  (mutt_str_strcmp(np->data, tmp->message->env->real_subj) == 0))
490  {
491  last = tmp; /* best match so far */
492  }
493  }
494  }
495 
496  mutt_list_clear(&subjects);
497  return last;
498 }
struct HashElem * mutt_hash_find_bucket(const struct Hash *table, const char *strkey)
Find the HashElem in a Hash table element using a key.
Definition: hash.c:425
The envelope/body of an email.
Definition: email.h:37
char * real_subj
Offset of the real subject.
Definition: envelope.h:67
struct HashElem * next
Definition: hash.h:47
struct Envelope * env
Envelope information.
Definition: email.h:89
time_t date_sent
Time when the message was sent (UTC)
Definition: email.h:81
bool fake_thread
Emails grouped by Subject.
Definition: thread.h:36
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
void * data
Definition: hash.h:46
char * data
String.
Definition: list.h:35
struct Hash * subj_hash
Hash table by subject.
Definition: mailbox.h:127
bool subject_changed
Used for threading.
Definition: email.h:55
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:321
The item stored in a Hash Table.
Definition: hash.h:42
static void make_subject_list(struct ListHead *subjects, struct MuttThread *cur, time_t *dateptr)
Create a sorted list of all subjects in a thread.
Definition: mutt_thread.c:408
bool is_descendant(struct MuttThread *a, struct MuttThread *b)
Is one thread a descendant of another.
Definition: thread.c:44
void mutt_list_clear(struct ListHead *h)
Free a list, but NOT its strings.
Definition: list.c:168
A List node for strings.
Definition: list.h:33
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:615
bool C_ThreadReceived
Config: Sort threaded messages by their received date.
Definition: mutt_thread.c:57
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:82
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ make_subj_hash()

static struct Hash* make_subj_hash ( struct Mailbox m)
static

Create a Hash Table for the email subjects.

Parameters
mMailbox
Return values
ptrNewly allocated Hash Table

Definition at line 505 of file mutt_thread.c.

506 {
507  if (!m)
508  return NULL;
509 
510  struct Hash *hash = mutt_hash_new(m->msg_count * 2, MUTT_HASH_ALLOW_DUPS);
511 
512  for (int i = 0; i < m->msg_count; i++)
513  {
514  struct Email *e = m->emails[i];
515  if (!e || !e->env)
516  continue;
517  if (e->env->real_subj)
518  mutt_hash_insert(hash, e->env->real_subj, e);
519  }
520 
521  return hash;
522 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:98
int msg_count
Total number of messages.
Definition: mailbox.h:90
The envelope/body of an email.
Definition: email.h:37
A Hash Table.
Definition: hash.h:61
char * real_subj
Offset of the real subject.
Definition: envelope.h:67
struct Envelope * env
Envelope information.
Definition: email.h:89
#define MUTT_HASH_ALLOW_DUPS
allow duplicate keys to be inserted
Definition: hash.h:77
struct Hash * mutt_hash_new(size_t nelem, HashFlags flags)
Create a new Hash table (with string keys)
Definition: hash.c:275
struct HashElem * mutt_hash_insert(struct Hash *table, const char *strkey, void *data)
Add a new element to the Hash table (with string keys)
Definition: hash.c:351
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ pseudo_threads()

static void pseudo_threads ( struct Context ctx)
static

Thread messages by subject.

Parameters
ctxMailbox

Thread by subject things that didn't get threaded by message-id

Definition at line 530 of file mutt_thread.c.

531 {
532  if (!ctx || !ctx->mailbox)
533  return;
534 
535  struct Mailbox *m = ctx->mailbox;
536 
537  struct MuttThread *tree = ctx->tree;
538  struct MuttThread *top = tree;
539  struct MuttThread *tmp = NULL, *cur = NULL, *parent = NULL, *curchild = NULL,
540  *nextchild = NULL;
541 
542  if (!m->subj_hash)
543  m->subj_hash = make_subj_hash(ctx->mailbox);
544 
545  while (tree)
546  {
547  cur = tree;
548  tree = tree->next;
549  parent = find_subject(ctx->mailbox, cur);
550  if (parent)
551  {
552  cur->fake_thread = true;
553  unlink_message(&top, cur);
554  insert_message(&parent->child, parent, cur);
555  parent->sort_children = true;
556  tmp = cur;
557  while (true)
558  {
559  while (!tmp->message)
560  tmp = tmp->child;
561 
562  /* if the message we're attaching has pseudo-children, they
563  * need to be attached to its parent, so move them up a level.
564  * but only do this if they have the same real subject as the
565  * parent, since otherwise they rightly belong to the message
566  * we're attaching. */
567  if ((tmp == cur) || (mutt_str_strcmp(tmp->message->env->real_subj,
568  parent->message->env->real_subj) == 0))
569  {
570  tmp->message->subject_changed = false;
571 
572  for (curchild = tmp->child; curchild;)
573  {
574  nextchild = curchild->next;
575  if (curchild->fake_thread)
576  {
577  unlink_message(&tmp->child, curchild);
578  insert_message(&parent->child, parent, curchild);
579  }
580  curchild = nextchild;
581  }
582  }
583 
584  while (!tmp->next && (tmp != cur))
585  {
586  tmp = tmp->parent;
587  }
588  if (tmp == cur)
589  break;
590  tmp = tmp->next;
591  }
592  }
593  }
594  ctx->tree = top;
595 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
struct MuttThread * tree
Top of thread tree.
Definition: context.h:42
char * real_subj
Offset of the real subject.
Definition: envelope.h:67
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
struct Mailbox * mailbox
Definition: context.h:50
static struct MuttThread * find_subject(struct Mailbox *m, struct MuttThread *cur)
Find the best possible match for a parent based on subject.
Definition: mutt_thread.c:462
struct Envelope * env
Envelope information.
Definition: email.h:89
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
A mailbox.
Definition: mailbox.h:80
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
void unlink_message(struct MuttThread **old, struct MuttThread *cur)
Break the message out of the thread.
Definition: thread.c:64
An Email conversation.
Definition: thread.h:34
struct Hash * subj_hash
Hash table by subject.
Definition: mailbox.h:127
bool subject_changed
Used for threading.
Definition: email.h:55
void insert_message(struct MuttThread **add, struct MuttThread *parent, struct MuttThread *cur)
Insert a message into a thread.
Definition: thread.c:94
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:615
static struct Hash * make_subj_hash(struct Mailbox *m)
Create a Hash Table for the email subjects.
Definition: mutt_thread.c:505
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_clear_threads()

void mutt_clear_threads ( struct Context ctx)

Clear the threading of message in a mailbox.

Parameters
ctxMailbox

Definition at line 601 of file mutt_thread.c.

602 {
603  if (!ctx || !ctx->mailbox || !ctx->mailbox->emails)
604  return;
605 
606  struct Mailbox *m = ctx->mailbox;
607 
608  for (int i = 0; i < m->msg_count; i++)
609  {
610  struct Email *e = m->emails[i];
611  if (!e)
612  break;
613 
614  /* mailbox may have been only partially read */
615  e->thread = NULL;
616  e->threaded = false;
617  }
618  ctx->tree = NULL;
619 
621 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:98
int msg_count
Total number of messages.
Definition: mailbox.h:90
The envelope/body of an email.
Definition: email.h:37
struct MuttThread * tree
Top of thread tree.
Definition: context.h:42
struct MuttThread * thread
Thread of Emails.
Definition: email.h:94
bool threaded
Used for threading.
Definition: email.h:56
struct Mailbox * mailbox
Definition: context.h:50
struct Hash * thread_hash
Hash table for threading.
Definition: context.h:43
A mailbox.
Definition: mailbox.h:80
void mutt_hash_free(struct Hash **ptr)
Free a hash table.
Definition: hash.c:471
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ compare_threads()

static int compare_threads ( const void *  a,
const void *  b 
)
static

Sorting function for email threads.

Parameters
aFirst thread to compare
bSecond thread to compare
Return values
-1a precedes b
0a and b are identical
1b precedes a

Definition at line 631 of file mutt_thread.c.

632 {
633  static sort_t sort_func = NULL;
634 
635  if (a && b)
636  {
637  return (*sort_func)(&(*((struct MuttThread const *const *) a))->sort_key,
638  &(*((struct MuttThread const *const *) b))->sort_key);
639  }
640  /* a hack to let us reset sort_func even though we can't
641  * have extra arguments because of qsort */
642  else
643  {
644  sort_func = mutt_get_sort_func(C_Sort & SORT_MASK);
645  return sort_func ? 1 : 0;
646  }
647 }
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:58
int(* sort_t)(const void *a, const void *b)
typedef sort_t - Prototype for a function to compare two emails
Definition: sort.h:48
struct Email * sort_key
Email that this Thread is sorted against.
Definition: thread.h:50
An Email conversation.
Definition: thread.h:34
sort_t mutt_get_sort_func(enum SortType method)
Get the sort function for a given sort id.
Definition: sort.c:322
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:85
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_sort_subthreads()

struct MuttThread* mutt_sort_subthreads ( struct MuttThread thread,
bool  init 
)

Sort the children of a thread.

Parameters
threadThread to start at
initIf true, rebuild the thread
Return values
ptrSorted threads

Definition at line 655 of file mutt_thread.c.

656 {
657  struct MuttThread **array = NULL, *sort_key = NULL, *top = NULL, *tmp = NULL;
658  struct Email *oldsort_key = NULL;
659  int i, array_size, sort_top = 0;
660 
661  /* we put things into the array backwards to save some cycles,
662  * but we want to have to move less stuff around if we're
663  * resorting, so we sort backwards and then put them back
664  * in reverse order so they're forwards */
665  C_Sort ^= SORT_REVERSE;
666  if (compare_threads(NULL, NULL) == 0)
667  return thread;
668 
669  top = thread;
670 
671  array_size = 256;
672  array = mutt_mem_calloc(array_size, sizeof(struct MuttThread *));
673  while (true)
674  {
675  if (init || !thread->sort_key)
676  {
677  thread->sort_key = NULL;
678 
679  if (thread->parent)
680  thread->parent->sort_children = true;
681  else
682  sort_top = 1;
683  }
684 
685  if (thread->child)
686  {
687  thread = thread->child;
688  continue;
689  }
690  else
691  {
692  /* if it has no children, it must be real. sort it on its own merits */
693  thread->sort_key = thread->message;
694 
695  if (thread->next)
696  {
697  thread = thread->next;
698  continue;
699  }
700  }
701 
702  while (!thread->next)
703  {
704  /* if it has siblings and needs to be sorted, sort it... */
705  if (thread->prev && (thread->parent ? thread->parent->sort_children : sort_top))
706  {
707  /* put them into the array */
708  for (i = 0; thread; i++, thread = thread->prev)
709  {
710  if (i >= array_size)
711  mutt_mem_realloc(&array, (array_size *= 2) * sizeof(struct MuttThread *));
712 
713  array[i] = thread;
714  }
715 
716  qsort((void *) array, i, sizeof(struct MuttThread *), *compare_threads);
717 
718  /* attach them back together. make thread the last sibling. */
719  thread = array[0];
720  thread->next = NULL;
721  array[i - 1]->prev = NULL;
722 
723  if (thread->parent)
724  thread->parent->child = array[i - 1];
725  else
726  top = array[i - 1];
727 
728  while (--i)
729  {
730  array[i - 1]->prev = array[i];
731  array[i]->next = array[i - 1];
732  }
733  }
734 
735  if (thread->parent)
736  {
737  tmp = thread;
738  thread = thread->parent;
739 
740  if (!thread->sort_key || thread->sort_children)
741  {
742  /* make sort_key the first or last sibling, as appropriate */
743  sort_key = ((!(C_Sort & SORT_LAST)) ^ (!(C_Sort & SORT_REVERSE))) ?
744  thread->child :
745  tmp;
746 
747  /* we just sorted its children */
748  thread->sort_children = false;
749 
750  oldsort_key = thread->sort_key;
751  thread->sort_key = thread->message;
752 
753  if (C_Sort & SORT_LAST)
754  {
755  if (!thread->sort_key ||
756  ((((C_Sort & SORT_REVERSE) ? 1 : -1) *
757  compare_threads((void *) &thread, (void *) &sort_key)) > 0))
758  {
759  thread->sort_key = sort_key->sort_key;
760  }
761  }
762  else if (!thread->sort_key)
763  thread->sort_key = sort_key->sort_key;
764 
765  /* if its sort_key has changed, we need to resort it and siblings */
766  if (oldsort_key != thread->sort_key)
767  {
768  if (thread->parent)
769  thread->parent->sort_children = true;
770  else
771  sort_top = 1;
772  }
773  }
774  }
775  else
776  {
777  C_Sort ^= SORT_REVERSE;
778  FREE(&array);
779  return top;
780  }
781  }
782 
783  thread = thread->next;
784  }
785 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
The envelope/body of an email.
Definition: email.h:37
struct MuttThread * thread
Thread of Emails.
Definition: email.h:94
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:58
static int compare_threads(const void *a, const void *b)
Sorting function for email threads.
Definition: mutt_thread.c:631
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
struct Email * sort_key
Email that this Thread is sorted against.
Definition: thread.h:50
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
bool sort_children
Sort the children.
Definition: thread.h:38
#define FREE(x)
Definition: memory.h:40
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:86
#define SORT_LAST
Sort thread by last-X, e.g. received date.
Definition: sort2.h:87
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ check_subjects()

static void check_subjects ( struct Mailbox m,
bool  init 
)
static

Find out which emails' subjects differ from their parent's.

Parameters
mMailbox
initIf true, rebuild the thread

Definition at line 792 of file mutt_thread.c.

793 {
794  if (!m)
795  return;
796 
797  for (int i = 0; i < m->msg_count; i++)
798  {
799  struct Email *e = m->emails[i];
800  if (!e || !e->thread)
801  continue;
802 
803  if (e->thread->check_subject)
804  e->thread->check_subject = false;
805  else if (!init)
806  continue;
807 
808  /* figure out which messages have subjects different than their parents' */
809  struct MuttThread *tmp = e->thread->parent;
810  while (tmp && !tmp->message)
811  {
812  tmp = tmp->parent;
813  }
814 
815  if (!tmp)
816  e->subject_changed = true;
817  else if (e->env->real_subj && tmp->message->env->real_subj)
818  {
819  e->subject_changed =
820  (mutt_str_strcmp(e->env->real_subj, tmp->message->env->real_subj) != 0);
821  }
822  else
823  {
824  e->subject_changed = (e->env->real_subj || tmp->message->env->real_subj);
825  }
826  }
827 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:98
int msg_count
Total number of messages.
Definition: mailbox.h:90
The envelope/body of an email.
Definition: email.h:37
struct MuttThread * thread
Thread of Emails.
Definition: email.h:94
char * real_subj
Offset of the real subject.
Definition: envelope.h:67
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
struct Envelope * env
Envelope information.
Definition: email.h:89
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
bool subject_changed
Used for threading.
Definition: email.h:55
bool check_subject
Should the Subject be checked?
Definition: thread.h:39
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:615
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_sort_threads()

void mutt_sort_threads ( struct Context ctx,
bool  init 
)

Sort email threads.

Parameters
ctxMailbox
initIf true, rebuild the thread

Definition at line 834 of file mutt_thread.c.

835 {
836  if (!ctx || !ctx->mailbox)
837  return;
838 
839  struct Mailbox *m = ctx->mailbox;
840 
841  struct Email *e = NULL;
842  int i, oldsort, using_refs = 0;
843  struct MuttThread *thread = NULL, *tnew = NULL, *tmp = NULL;
844  struct MuttThread top = { 0 };
845  struct ListNode *ref = NULL;
846 
847  /* Set C_Sort to the secondary method to support the set sort_aux=reverse-*
848  * settings. The sorting functions just look at the value of SORT_REVERSE */
849  oldsort = C_Sort;
850  C_Sort = C_SortAux;
851 
852  if (!ctx->thread_hash)
853  init = true;
854 
855  if (init)
856  {
859  }
860 
861  /* we want a quick way to see if things are actually attached to the top of the
862  * thread tree or if they're just dangling, so we attach everything to a top
863  * node temporarily */
864  top.parent = NULL;
865  top.next = NULL;
866  top.prev = NULL;
867 
868  top.child = ctx->tree;
869  for (thread = ctx->tree; thread; thread = thread->next)
870  thread->parent = &top;
871 
872  /* put each new message together with the matching messageless MuttThread if it
873  * exists. otherwise, if there is a MuttThread that already has a message, thread
874  * new message as an identical child. if we didn't attach the message to a
875  * MuttThread, make a new one for it. */
876  for (i = 0; i < m->msg_count; i++)
877  {
878  e = m->emails[i];
879  if (!e)
880  continue;
881 
882  if (!e->thread)
883  {
884  if ((!init || C_DuplicateThreads) && e->env->message_id)
885  thread = mutt_hash_find(ctx->thread_hash, e->env->message_id);
886  else
887  thread = NULL;
888 
889  if (thread && !thread->message)
890  {
891  /* this is a message which was missing before */
892  thread->message = e;
893  e->thread = thread;
894  thread->check_subject = true;
895 
896  /* mark descendants as needing subject_changed checked */
897  for (tmp = (thread->child ? thread->child : thread); tmp != thread;)
898  {
899  while (!tmp->message)
900  tmp = tmp->child;
901  tmp->check_subject = true;
902  while (!tmp->next && (tmp != thread))
903  tmp = tmp->parent;
904  if (tmp != thread)
905  tmp = tmp->next;
906  }
907 
908  if (thread->parent)
909  {
910  /* remove threading info above it based on its children, which we'll
911  * recalculate based on its headers. make sure not to leave
912  * dangling missing messages. note that we haven't kept track
913  * of what info came from its children and what from its siblings'
914  * children, so we just remove the stuff that's definitely from it */
915  do
916  {
917  tmp = thread->parent;
918  unlink_message(&tmp->child, thread);
919  thread->parent = NULL;
920  thread->sort_key = NULL;
921  thread->fake_thread = false;
922  thread = tmp;
923  } while (thread != &top && !thread->child && !thread->message);
924  }
925  }
926  else
927  {
928  tnew = (C_DuplicateThreads ? thread : NULL);
929 
930  thread = mutt_mem_calloc(1, sizeof(struct MuttThread));
931  thread->message = e;
932  thread->check_subject = true;
933  e->thread = thread;
935  e->env->message_id ? e->env->message_id : "", thread);
936 
937  if (tnew)
938  {
939  if (tnew->duplicate_thread)
940  tnew = tnew->parent;
941 
942  thread = e->thread;
943 
944  insert_message(&tnew->child, tnew, thread);
945  thread->duplicate_thread = true;
946  thread->message->threaded = true;
947  }
948  }
949  }
950  else
951  {
952  /* unlink pseudo-threads because they might be children of newly
953  * arrived messages */
954  thread = e->thread;
955  for (tnew = thread->child; tnew;)
956  {
957  tmp = tnew->next;
958  if (tnew->fake_thread)
959  {
960  unlink_message(&thread->child, tnew);
961  insert_message(&top.child, &top, tnew);
962  tnew->fake_thread = false;
963  }
964  tnew = tmp;
965  }
966  }
967  }
968 
969  /* thread by references */
970  for (i = 0; i < m->msg_count; i++)
971  {
972  e = m->emails[i];
973  if (!e)
974  break;
975 
976  if (e->threaded)
977  continue;
978  e->threaded = true;
979 
980  thread = e->thread;
981  if (!thread)
982  continue;
983  using_refs = 0;
984 
985  while (true)
986  {
987  if (using_refs == 0)
988  {
989  /* look at the beginning of in-reply-to: */
990  ref = STAILQ_FIRST(&e->env->in_reply_to);
991  if (ref)
992  using_refs = 1;
993  else
994  {
995  ref = STAILQ_FIRST(&e->env->references);
996  using_refs = 2;
997  }
998  }
999  else if (using_refs == 1)
1000  {
1001  /* if there's no references header, use all the in-reply-to:
1002  * data that we have. otherwise, use the first reference
1003  * if it's different than the first in-reply-to, otherwise use
1004  * the second reference (since at least eudora puts the most
1005  * recent reference in in-reply-to and the rest in references) */
1006  if (STAILQ_EMPTY(&e->env->references))
1007  ref = STAILQ_NEXT(ref, entries);
1008  else
1009  {
1010  if (mutt_str_strcmp(ref->data, STAILQ_FIRST(&e->env->references)->data) != 0)
1011  ref = STAILQ_FIRST(&e->env->references);
1012  else
1013  ref = STAILQ_NEXT(STAILQ_FIRST(&e->env->references), entries);
1014 
1015  using_refs = 2;
1016  }
1017  }
1018  else
1019  ref = STAILQ_NEXT(ref, entries); /* go on with references */
1020 
1021  if (!ref)
1022  break;
1023 
1024  tnew = mutt_hash_find(ctx->thread_hash, ref->data);
1025  if (tnew)
1026  {
1027  if (tnew->duplicate_thread)
1028  tnew = tnew->parent;
1029  if (is_descendant(tnew, thread)) /* no loops! */
1030  continue;
1031  }
1032  else
1033  {
1034  tnew = mutt_mem_calloc(1, sizeof(struct MuttThread));
1035  mutt_hash_insert(ctx->thread_hash, ref->data, tnew);
1036  }
1037 
1038  if (thread->parent)
1039  unlink_message(&top.child, thread);
1040  insert_message(&tnew->child, tnew, thread);
1041  thread = tnew;
1042  if (thread->message || (thread->parent && (thread->parent != &top)))
1043  break;
1044  }
1045 
1046  if (!thread->parent)
1047  insert_message(&top.child, &top, thread);
1048  }
1049 
1050  /* detach everything from the temporary top node */
1051  for (thread = top.child; thread; thread = thread->next)
1052  {
1053  thread->parent = NULL;
1054  }
1055  ctx->tree = top.child;
1056 
1057  check_subjects(ctx->mailbox, init);
1058 
1059  if (!C_StrictThreads)
1060  pseudo_threads(ctx);
1061 
1062  if (ctx->tree)
1063  {
1064  ctx->tree = mutt_sort_subthreads(ctx->tree, init);
1065 
1066  /* restore the oldsort order. */
1067  C_Sort = oldsort;
1068 
1069  /* Put the list into an array. */
1070  linearize_tree(ctx);
1071 
1072  /* Draw the thread tree. */
1073  mutt_draw_tree(ctx);
1074  }
1075 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:98
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
int msg_count
Total number of messages.
Definition: mailbox.h:90
void thread_hash_destructor(int type, void *obj, intptr_t data)
Hash Destructor callback - Implements hashelem_free_t.
Definition: thread.c:111
The envelope/body of an email.
Definition: email.h:37
struct MuttThread * tree
Top of thread tree.
Definition: context.h:42
struct MuttThread * thread
Thread of Emails.
Definition: email.h:94
static void linearize_tree(struct Context *ctx)
Flatten an email thread.
Definition: mutt_thread.c:124
struct MuttThread * mutt_sort_subthreads(struct MuttThread *thread, bool init)
Sort the children of a thread.
Definition: mutt_thread.c:655
void * mutt_hash_find(const struct Hash *table, const char *strkey)
Find the HashElem data in a Hash table element using a key.
Definition: hash.c:378
bool threaded
Used for threading.
Definition: email.h:56
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
bool C_DuplicateThreads
Config: Highlight messages with duplicated message IDs.
Definition: mutt_thread.c:48
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:82
char * message_id
Message ID.
Definition: envelope.h:69
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
struct Mailbox * mailbox
Definition: context.h:50
static void pseudo_threads(struct Context *ctx)
Thread messages by subject.
Definition: mutt_thread.c:530
struct Envelope * env
Envelope information.
Definition: email.h:89
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:58
bool C_StrictThreads
Config: Thread messages using &#39;In-Reply-To&#39; and &#39;References&#39; headers.
Definition: mutt_thread.c:56
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
struct Hash * thread_hash
Hash table for threading.
Definition: context.h:43
bool duplicate_thread
Duplicated Email in Thread.
Definition: thread.h:37
WHERE short C_SortAux
Config: Secondary sort method for the index.
Definition: sort.h:59
#define MUTT_HASH_ALLOW_DUPS
allow duplicate keys to be inserted
Definition: hash.h:77
struct Email * sort_key
Email that this Thread is sorted against.
Definition: thread.h:50
bool fake_thread
Emails grouped by Subject.
Definition: thread.h:36
A mailbox.
Definition: mailbox.h:80
struct Hash * mutt_hash_new(size_t nelem, HashFlags flags)
Create a new Hash table (with string keys)
Definition: hash.c:275
#define STAILQ_NEXT(elm, field)
Definition: queue.h:397
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
void unlink_message(struct MuttThread **old, struct MuttThread *cur)
Break the message out of the thread.
Definition: thread.c:64
An Email conversation.
Definition: thread.h:34
char * data
String.
Definition: list.h:35
void insert_message(struct MuttThread **add, struct MuttThread *parent, struct MuttThread *cur)
Insert a message into a thread.
Definition: thread.c:94
#define STAILQ_EMPTY(head)
Definition: queue.h:345
bool check_subject
Should the Subject be checked?
Definition: thread.h:39
void mutt_hash_set_destructor(struct Hash *table, hashelem_free_t fn, intptr_t fn_data)
Set the destructor for a Hash Table.
Definition: hash.c:317
static void check_subjects(struct Mailbox *m, bool init)
Find out which emails&#39; subjects differ from their parent&#39;s.
Definition: mutt_thread.c:792
bool is_descendant(struct MuttThread *a, struct MuttThread *b)
Is one thread a descendant of another.
Definition: thread.c:44
struct HashElem * mutt_hash_insert(struct Hash *table, const char *strkey, void *data)
Add a new element to the Hash table (with string keys)
Definition: hash.c:351
struct ListHead references
message references (in reverse order)
Definition: envelope.h:81
A List node for strings.
Definition: list.h:33
#define STAILQ_FIRST(head)
Definition: queue.h:347
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:615
void mutt_draw_tree(struct Context *ctx)
Draw a tree of threaded emails.
Definition: mutt_thread.c:285
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_aside_thread()

int mutt_aside_thread ( struct Email e,
bool  forwards,
bool  subthreads 
)

Find the next/previous (sub)thread.

Parameters
eSearch from this Email
forwardsDirection to search: 'true' forwards, 'false' backwards
subthreadsSearch subthreads: 'true' subthread, 'false' not
Return values
numIndex into the virtual email table

Definition at line 1084 of file mutt_thread.c.

1085 {
1086  struct MuttThread *cur = NULL;
1087  struct Email *e_tmp = NULL;
1088 
1089  if ((C_Sort & SORT_MASK) != SORT_THREADS)
1090  {
1091  mutt_error(_("Threading is not enabled"));
1092  return e->vnum;
1093  }
1094 
1095  cur = e->thread;
1096 
1097  if (subthreads)
1098  {
1099  if (forwards ^ ((C_Sort & SORT_REVERSE) != 0))
1100  {
1101  while (!cur->next && cur->parent)
1102  cur = cur->parent;
1103  }
1104  else
1105  {
1106  while (!cur->prev && cur->parent)
1107  cur = cur->parent;
1108  }
1109  }
1110  else
1111  {
1112  while (cur->parent)
1113  cur = cur->parent;
1114  }
1115 
1116  if (forwards ^ ((C_Sort & SORT_REVERSE) != 0))
1117  {
1118  do
1119  {
1120  cur = cur->next;
1121  if (!cur)
1122  return -1;
1123  e_tmp = find_virtual(cur, 0);
1124  } while (!e_tmp);
1125  }
1126  else
1127  {
1128  do
1129  {
1130  cur = cur->prev;
1131  if (!cur)
1132  return -1;
1133  e_tmp = find_virtual(cur, 1);
1134  } while (!e_tmp);
1135  }
1136 
1137  return e_tmp->vnum;
1138 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
The envelope/body of an email.
Definition: email.h:37
struct Email * find_virtual(struct MuttThread *cur, int reverse)
Find an email with a Virtual message number.
Definition: thread.c:122
struct MuttThread * thread
Thread of Emails.
Definition: email.h:94
#define _(a)
Definition: message.h:28
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:58
Sort by email threads.
Definition: sort2.h:56
int vnum
Virtual message number.
Definition: email.h:87
An Email conversation.
Definition: thread.h:34
#define mutt_error(...)
Definition: logging.h:84
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:86
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:85
+ Here is the call graph for this function:

◆ mutt_parent_message()

int mutt_parent_message ( struct Context ctx,
struct Email e,
bool  find_root 
)

Find the parent of a message.

Parameters
ctxMailbox
eCurrent Email
find_rootIf true, find the root message
Return values
>=0Virtual index number of parent/root message
-1Error

Definition at line 1148 of file mutt_thread.c.

1149 {
1150  if (!ctx || !e)
1151  return -1;
1152 
1153  struct MuttThread *thread = NULL;
1154  struct Email *e_parent = NULL;
1155 
1156  if ((C_Sort & SORT_MASK) != SORT_THREADS)
1157  {
1158  mutt_error(_("Threading is not enabled"));
1159  return e->vnum;
1160  }
1161 
1162  /* Root may be the current message */
1163  if (find_root)
1164  e_parent = e;
1165 
1166  for (thread = e->thread->parent; thread; thread = thread->parent)
1167  {
1168  e = thread->message;
1169  if (e)
1170  {
1171  e_parent = e;
1172  if (!find_root)
1173  break;
1174  }
1175  }
1176 
1177  if (!e_parent)
1178  {
1179  mutt_error(_("Parent message is not available"));
1180  return -1;
1181  }
1182  if (!is_visible(e_parent, ctx))
1183  {
1184  if (find_root)
1185  mutt_error(_("Root message is not visible in this limited view"));
1186  else
1187  mutt_error(_("Parent message is not visible in this limited view"));
1188  return -1;
1189  }
1190  return e_parent->vnum;
1191 }
The envelope/body of an email.
Definition: email.h:37
static bool is_visible(struct Email *e, struct Context *ctx)
Is the message visible?
Definition: mutt_thread.c:65
struct MuttThread * thread
Thread of Emails.
Definition: email.h:94
#define _(a)
Definition: message.h:28
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:58
Sort by email threads.
Definition: sort2.h:56
int vnum
Virtual message number.
Definition: email.h:87
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
#define mutt_error(...)
Definition: logging.h:84
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:85
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_set_vnum()

void mutt_set_vnum ( struct Context ctx)

Set the virtual index number of all the messages in a mailbox.

Parameters
ctxMailbox

Definition at line 1197 of file mutt_thread.c.

1198 {
1199  if (!ctx || !ctx->mailbox)
1200  return;
1201 
1202  struct Mailbox *m = ctx->mailbox;
1203 
1204  struct Email *e = NULL;
1205 
1206  m->vcount = 0;
1207  ctx->vsize = 0;
1208  int padding = mx_msg_padding_size(m);
1209 
1210  for (int i = 0; i < m->msg_count; i++)
1211  {
1212  e = m->emails[i];
1213  if (!e)
1214  break;
1215 
1216  if (e->vnum >= 0)
1217  {
1218  e->vnum = m->vcount;
1219  m->v2r[m->vcount] = i;
1220  m->vcount++;
1221  ctx->vsize += e->content->length + e->content->offset - e->content->hdr_offset + padding;
1222  e->num_hidden = mutt_get_hidden(ctx, e);
1223  }
1224  }
1225 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:98
int msg_count
Total number of messages.
Definition: mailbox.h:90
The envelope/body of an email.
Definition: email.h:37
struct Body * content
List of MIME parts.
Definition: email.h:90
#define mutt_get_hidden(ctx, e)
Definition: mutt_thread.h:59
LOFF_T offset
offset where the actual data begins
Definition: body.h:44
int vcount
The number of virtual messages.
Definition: mailbox.h:101
struct Mailbox * mailbox
Definition: context.h:50
off_t vsize
Definition: context.h:38
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
A mailbox.
Definition: mailbox.h:80
size_t num_hidden
Number of hidden messages in this view.
Definition: email.h:75
int vnum
Virtual message number.
Definition: email.h:87
int * v2r
Mapping from virtual to real msgno.
Definition: mailbox.h:100
int mx_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Wrapper for MxOps::msg_padding_size()
Definition: mx.c:1517
long hdr_offset
Offset in stream where the headers begin.
Definition: body.h:42
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_traverse_thread()

int mutt_traverse_thread ( struct Context ctx,
struct Email e_cur,
MuttThreadFlags  flag 
)

Recurse through an email thread, matching messages.

Parameters
ctxMailbox
e_curCurrent Email
flagFlag to set, see MuttThreadFlags
Return values
numNumber of matches

Definition at line 1234 of file mutt_thread.c.

1235 {
1236  struct MuttThread *thread = NULL, *top = NULL;
1237  struct Email *e_root = NULL;
1238  int final, reverse = (C_Sort & SORT_REVERSE), minmsgno;
1239  int num_hidden = 0, new_mail = 0, old_mail = 0;
1240  bool flagged = false;
1241  int min_unread_msgno = INT_MAX, min_unread = e_cur->vnum;
1242 #define CHECK_LIMIT (!ctx->pattern || e_cur->limited)
1243 
1244  if (((C_Sort & SORT_MASK) != SORT_THREADS) && !(flag & MUTT_THREAD_GET_HIDDEN))
1245  {
1246  mutt_error(_("Threading is not enabled"));
1247  return e_cur->vnum;
1248  }
1249 
1250  if (!e_cur->thread)
1251  {
1252  return e_cur->vnum;
1253  }
1254 
1255  final = e_cur->vnum;
1256  thread = e_cur->thread;
1257  while (thread->parent)
1258  thread = thread->parent;
1259  top = thread;
1260  while (!thread->message)
1261  thread = thread->child;
1262  e_cur = thread->message;
1263  minmsgno = e_cur->msgno;
1264 
1265  if (!e_cur->read && CHECK_LIMIT)
1266  {
1267  if (e_cur->old)
1268  old_mail = 2;
1269  else
1270  new_mail = 1;
1271  if (e_cur->msgno < min_unread_msgno)
1272  {
1273  min_unread = e_cur->vnum;
1274  min_unread_msgno = e_cur->msgno;
1275  }
1276  }
1277 
1278  if (e_cur->flagged && CHECK_LIMIT)
1279  flagged = true;
1280 
1281  if ((e_cur->vnum == -1) && CHECK_LIMIT)
1282  num_hidden++;
1283 
1285  {
1286  e_cur->pair = 0; /* force index entry's color to be re-evaluated */
1287  e_cur->collapsed = flag & MUTT_THREAD_COLLAPSE;
1288  if (e_cur->vnum != -1)
1289  {
1290  e_root = e_cur;
1291  if (flag & MUTT_THREAD_COLLAPSE)
1292  final = e_root->vnum;
1293  }
1294  }
1295 
1296  if ((thread == top) && !(thread = thread->child))
1297  {
1298  /* return value depends on action requested */
1299  if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
1300  return final;
1301  if (flag & MUTT_THREAD_UNREAD)
1302  return (old_mail && new_mail) ? new_mail : (old_mail ? old_mail : new_mail);
1303  if (flag & MUTT_THREAD_GET_HIDDEN)
1304  return num_hidden;
1305  if (flag & MUTT_THREAD_NEXT_UNREAD)
1306  return min_unread;
1307  if (flag & MUTT_THREAD_FLAGGED)
1308  return flagged;
1309  }
1310 
1311  while (true)
1312  {
1313  e_cur = thread->message;
1314 
1315  if (e_cur)
1316  {
1317  if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
1318  {
1319  e_cur->pair = 0; /* force index entry's color to be re-evaluated */
1320  e_cur->collapsed = flag & MUTT_THREAD_COLLAPSE;
1321  if (!e_root && CHECK_LIMIT)
1322  {
1323  e_root = e_cur;
1324  if (flag & MUTT_THREAD_COLLAPSE)
1325  final = e_root->vnum;
1326  }
1327 
1328  if (reverse && (flag & MUTT_THREAD_COLLAPSE) && (e_cur->msgno < minmsgno) && CHECK_LIMIT)
1329  {
1330  minmsgno = e_cur->msgno;
1331  final = e_cur->vnum;
1332  }
1333 
1334  if (flag & MUTT_THREAD_COLLAPSE)
1335  {
1336  if (e_cur != e_root)
1337  e_cur->vnum = -1;
1338  }
1339  else
1340  {
1341  if (CHECK_LIMIT)
1342  e_cur->vnum = e_cur->msgno;
1343  }
1344  }
1345 
1346  if (!e_cur->read && CHECK_LIMIT)
1347  {
1348  if (e_cur->old)
1349  old_mail = 2;
1350  else
1351  new_mail = 1;
1352  if (e_cur->msgno < min_unread_msgno)
1353  {
1354  min_unread = e_cur->vnum;
1355  min_unread_msgno = e_cur->msgno;
1356  }
1357  }
1358 
1359  if (e_cur->flagged && CHECK_LIMIT)
1360  flagged = true;
1361 
1362  if ((e_cur->vnum == -1) && CHECK_LIMIT)
1363  num_hidden++;
1364  }
1365 
1366  if (thread->child)
1367  thread = thread->child;
1368  else if (thread->next)
1369  thread = thread->next;
1370  else
1371  {
1372  bool done = false;
1373  while (!thread->next)
1374  {
1375  thread = thread->parent;
1376  if (thread == top)
1377  {
1378  done = true;
1379  break;
1380  }
1381  }
1382  if (done)
1383  break;
1384  thread = thread->next;
1385  }
1386  }
1387 
1388  /* return value depends on action requested */
1389  if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
1390  return final;
1391  if (flag & MUTT_THREAD_UNREAD)
1392  return (old_mail && new_mail) ? new_mail : (old_mail ? old_mail : new_mail);
1393  if (flag & MUTT_THREAD_GET_HIDDEN)
1394  return num_hidden + 1;
1395  if (flag & MUTT_THREAD_NEXT_UNREAD)
1396  return min_unread;
1397  if (flag & MUTT_THREAD_FLAGGED)
1398  return flagged;
1399 
1400  return 0;
1401 #undef CHECK_LIMIT
1402 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
The envelope/body of an email.
Definition: email.h:37
#define MUTT_THREAD_UNCOLLAPSE
Uncollapse an email thread.
Definition: mutt_thread.h:50
struct MuttThread * thread
Thread of Emails.
Definition: email.h:94
#define _(a)
Definition: message.h:28
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
bool read
Email is read.
Definition: email.h:51
bool old
Email is seen, but unread.
Definition: email.h:50
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:58
#define MUTT_THREAD_COLLAPSE
Collapse an email thread.
Definition: mutt_thread.h:49
#define MUTT_THREAD_FLAGGED
Count flagged emails in a thread.
Definition: mutt_thread.h:54
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
Sort by email threads.
Definition: sort2.h:56
size_t num_hidden
Number of hidden messages in this view.
Definition: email.h:75
bool collapsed
Is this message part of a collapsed thread?
Definition: email.h:73
int vnum
Virtual message number.
Definition: email.h:87
#define MUTT_THREAD_NEXT_UNREAD
Find the next unread email.
Definition: mutt_thread.h:53
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
bool flagged
Marked important?
Definition: email.h:43
#define mutt_error(...)
Definition: logging.h:84
#define CHECK_LIMIT
#define MUTT_THREAD_GET_HIDDEN
Count non-visible emails in a thread.
Definition: mutt_thread.h:51
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:86
int pair
Color-pair to use when displaying in the index.
Definition: email.h:79
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:85
int msgno
Number displayed to the user.
Definition: email.h:86
#define MUTT_THREAD_UNREAD
Count unread emails in a thread.
Definition: mutt_thread.h:52

◆ mutt_messages_in_thread()

int mutt_messages_in_thread ( struct Mailbox m,
struct Email e,
int  flag 
)

Count the messages in a thread.

Parameters
mMailbox
eEmail
flagFlag, see notes below
Return values
numNumber of message / Our position

If flag is 0, we want to know how many messages are in the thread. If flag is 1, we want to know our position in the thread.

Definition at line 1414 of file mutt_thread.c.

1415 {
1416  if (!m || !e)
1417  return 1;
1418 
1419  struct MuttThread *threads[2];
1420  int rc;
1421 
1422  if (((C_Sort & SORT_MASK) != SORT_THREADS) || !e->thread)
1423  return 1;
1424 
1425  threads[0] = e->thread;
1426  while (threads[0]->parent)
1427  threads[0] = threads[0]->parent;
1428 
1429  threads[1] = flag ? e->thread : threads[0]->next;
1430 
1431  for (int i = 0; i < ((flag || !threads[1]) ? 1 : 2); i++)
1432  {
1433  while (!threads[i]->message)
1434  threads[i] = threads[i]->child;
1435  }
1436 
1437  if (C_Sort & SORT_REVERSE)
1438  rc = threads[0]->message->msgno - (threads[1] ? threads[1]->message->msgno : -1);
1439  else
1440  {
1441  rc = (threads[1] ? threads[1]->message->msgno : m->msg_count) -
1442  threads[0]->message->msgno;
1443  }
1444 
1445  if (flag)
1446  rc += 1;
1447 
1448  return rc;
1449 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
int msg_count
Total number of messages.
Definition: mailbox.h:90
struct MuttThread * thread
Thread of Emails.
Definition: email.h:94
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:58
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
Sort by email threads.
Definition: sort2.h:56
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
An Email conversation.
Definition: thread.h:34
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:86
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:85
int msgno
Number displayed to the user.
Definition: email.h:86
+ Here is the caller graph for this function:

◆ mutt_make_id_hash()

struct Hash* mutt_make_id_hash ( struct Mailbox m)

Create a Hash Table for message-ids.

Parameters
mMailbox
Return values
ptrNewly allocated Hash Table

Definition at line 1456 of file mutt_thread.c.

1457 {
1458  struct Hash *hash = mutt_hash_new(m->msg_count * 2, MUTT_HASH_NO_FLAGS);
1459 
1460  for (int i = 0; i < m->msg_count; i++)
1461  {
1462  struct Email *e = m->emails[i];
1463  if (!e || !e->env)
1464  continue;
1465 
1466  if (e->env->message_id)
1467  mutt_hash_insert(hash, e->env->message_id, e);
1468  }
1469 
1470  return hash;
1471 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:98
int msg_count
Total number of messages.
Definition: mailbox.h:90
The envelope/body of an email.
Definition: email.h:37
A Hash Table.
Definition: hash.h:61
char * message_id
Message ID.
Definition: envelope.h:69
struct Envelope * env
Envelope information.
Definition: email.h:89
struct Hash * mutt_hash_new(size_t nelem, HashFlags flags)
Create a new Hash table (with string keys)
Definition: hash.c:275
struct HashElem * mutt_hash_insert(struct Hash *table, const char *strkey, void *data)
Add a new element to the Hash table (with string keys)
Definition: hash.c:351
#define MUTT_HASH_NO_FLAGS
No flags are set.
Definition: hash.h:74
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ link_threads()

static bool link_threads ( struct Email parent,
struct Email child,
struct Mailbox m 
)
static

Forcibly link messages together.

Parameters
parentParent Email
childChild Email
mMailbox
Return values
trueOn success

Definition at line 1480 of file mutt_thread.c.

1481 {
1482  if (child == parent)
1483  return false;
1484 
1485  mutt_break_thread(child);
1487  mutt_set_flag(m, child, MUTT_TAG, false);
1488 
1489  child->changed = true;
1490  child->env->changed |= MUTT_ENV_CHANGED_IRT;
1491  return true;
1492 }
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:70
bool changed
Email has been edited.
Definition: email.h:48
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:88
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:82
char * message_id
Message ID.
Definition: envelope.h:69
struct Envelope * env
Envelope information.
Definition: email.h:89
void mutt_break_thread(struct Email *e)
Break the email Thread.
Definition: thread.c:225
Tagged messages.
Definition: mutt.h:107
struct ListNode * mutt_list_insert_head(struct ListHead *h, char *s)
Insert a string at the beginning of a List.
Definition: list.c:46
char * mutt_str_strdup(const char *str)
Copy a string, safely.
Definition: string.c:380
#define MUTT_ENV_CHANGED_IRT
In-Reply-To changed to link/break threads.
Definition: envelope.h:32
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_link_threads()

bool mutt_link_threads ( struct Email parent,
struct EmailList *  children,
struct Mailbox m 
)

Forcibly link threads together.

Parameters
parentParent Email
childrenList of children Emails
mMailbox
Return values
trueOn success

Definition at line 1501 of file mutt_thread.c.

1502 {
1503  if (!parent || !children || !m)
1504  return false;
1505 
1506  bool changed = false;
1507 
1508  struct EmailNode *en = NULL;
1509  STAILQ_FOREACH(en, children, entries)
1510  {
1511  changed |= link_threads(parent, en->email, m);
1512  }
1513 
1514  return changed;
1515 }
bool changed
Email has been edited.
Definition: email.h:48
static bool link_threads(struct Email *parent, struct Email *child, struct Mailbox *m)
Forcibly link messages together.
Definition: mutt_thread.c:1480
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
struct Email * email
Email in the list.
Definition: email.h:116
List of Emails.
Definition: email.h:114
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ C_DuplicateThreads

bool C_DuplicateThreads

Config: Highlight messages with duplicated message IDs.

Definition at line 48 of file mutt_thread.c.

◆ C_HideLimited

bool C_HideLimited

Config: Don't indicate hidden messages, in the thread tree.

Definition at line 49 of file mutt_thread.c.

◆ C_HideMissing

bool C_HideMissing

Config: Don't indicate missing messages, in the thread tree.

Definition at line 50 of file mutt_thread.c.

◆ C_HideThreadSubject

bool C_HideThreadSubject

Config: Hide subjects that are similar to that of the parent message.

Definition at line 51 of file mutt_thread.c.

◆ C_HideTopLimited

bool C_HideTopLimited

Config: Don't indicate hidden top message, in the thread tree.

Definition at line 52 of file mutt_thread.c.

◆ C_HideTopMissing

bool C_HideTopMissing

Config: Don't indicate missing top message, in the thread tree.

Definition at line 53 of file mutt_thread.c.

◆ C_NarrowTree

bool C_NarrowTree

Config: Draw a narrower thread tree in the index.

Definition at line 54 of file mutt_thread.c.

◆ C_SortRe

bool C_SortRe

Config: Sort method for the sidebar.

Definition at line 55 of file mutt_thread.c.

◆ C_StrictThreads

bool C_StrictThreads

Config: Thread messages using 'In-Reply-To' and 'References' headers.

Definition at line 56 of file mutt_thread.c.

◆ C_ThreadReceived

bool C_ThreadReceived

Config: Sort threaded messages by their received date.

Definition at line 57 of file mutt_thread.c.