NeoMutt  2019-11-11
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:110
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
int msg_count
Total number of messages.
Definition: mailbox.h:102
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:92
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:350
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:350
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:139
bool subject_changed
Used for threading.
Definition: email.h:55
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:322
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->env->real_subj)
516  mutt_hash_insert(hash, e->env->real_subj, e);
517  }
518 
519  return hash;
520 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:110
int msg_count
Total number of messages.
Definition: mailbox.h:102
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 528 of file mutt_thread.c.

529 {
530  if (!ctx || !ctx->mailbox)
531  return;
532 
533  struct Mailbox *m = ctx->mailbox;
534 
535  struct MuttThread *tree = ctx->tree;
536  struct MuttThread *top = tree;
537  struct MuttThread *tmp = NULL, *cur = NULL, *parent = NULL, *curchild = NULL,
538  *nextchild = NULL;
539 
540  if (!m->subj_hash)
541  m->subj_hash = make_subj_hash(ctx->mailbox);
542 
543  while (tree)
544  {
545  cur = tree;
546  tree = tree->next;
547  parent = find_subject(ctx->mailbox, cur);
548  if (parent)
549  {
550  cur->fake_thread = true;
551  unlink_message(&top, cur);
552  insert_message(&parent->child, parent, cur);
553  parent->sort_children = true;
554  tmp = cur;
555  while (true)
556  {
557  while (!tmp->message)
558  tmp = tmp->child;
559 
560  /* if the message we're attaching has pseudo-children, they
561  * need to be attached to its parent, so move them up a level.
562  * but only do this if they have the same real subject as the
563  * parent, since otherwise they rightly belong to the message
564  * we're attaching. */
565  if ((tmp == cur) || (mutt_str_strcmp(tmp->message->env->real_subj,
566  parent->message->env->real_subj) == 0))
567  {
568  tmp->message->subject_changed = false;
569 
570  for (curchild = tmp->child; curchild;)
571  {
572  nextchild = curchild->next;
573  if (curchild->fake_thread)
574  {
575  unlink_message(&tmp->child, curchild);
576  insert_message(&parent->child, parent, curchild);
577  }
578  curchild = nextchild;
579  }
580  }
581 
582  while (!tmp->next && (tmp != cur))
583  {
584  tmp = tmp->parent;
585  }
586  if (tmp == cur)
587  break;
588  tmp = tmp->next;
589  }
590  }
591  }
592  ctx->tree = top;
593 }
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:92
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:139
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 599 of file mutt_thread.c.

600 {
601  if (!ctx || !ctx->mailbox || !ctx->mailbox->emails)
602  return;
603 
604  struct Mailbox *m = ctx->mailbox;
605 
606  for (int i = 0; i < m->msg_count; i++)
607  {
608  /* mailbox may have been only partially read */
609  if (m->emails[i])
610  {
611  m->emails[i]->thread = NULL;
612  m->emails[i]->threaded = false;
613  }
614  }
615  ctx->tree = NULL;
616 
618 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:110
int msg_count
Total number of messages.
Definition: mailbox.h:102
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:92
void mutt_hash_free(struct Hash **ptr)
free_hdata 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 628 of file mutt_thread.c.

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

653 {
654  struct MuttThread **array = NULL, *sort_key = NULL, *top = NULL, *tmp = NULL;
655  struct Email *oldsort_key = NULL;
656  int i, array_size, sort_top = 0;
657 
658  /* we put things into the array backwards to save some cycles,
659  * but we want to have to move less stuff around if we're
660  * resorting, so we sort backwards and then put them back
661  * in reverse order so they're forwards */
662  C_Sort ^= SORT_REVERSE;
663  if (compare_threads(NULL, NULL) == 0)
664  return thread;
665 
666  top = thread;
667 
668  array_size = 256;
669  array = mutt_mem_calloc(array_size, sizeof(struct MuttThread *));
670  while (true)
671  {
672  if (init || !thread->sort_key)
673  {
674  thread->sort_key = NULL;
675 
676  if (thread->parent)
677  thread->parent->sort_children = true;
678  else
679  sort_top = 1;
680  }
681 
682  if (thread->child)
683  {
684  thread = thread->child;
685  continue;
686  }
687  else
688  {
689  /* if it has no children, it must be real. sort it on its own merits */
690  thread->sort_key = thread->message;
691 
692  if (thread->next)
693  {
694  thread = thread->next;
695  continue;
696  }
697  }
698 
699  while (!thread->next)
700  {
701  /* if it has siblings and needs to be sorted, sort it... */
702  if (thread->prev && (thread->parent ? thread->parent->sort_children : sort_top))
703  {
704  /* put them into the array */
705  for (i = 0; thread; i++, thread = thread->prev)
706  {
707  if (i >= array_size)
708  mutt_mem_realloc(&array, (array_size *= 2) * sizeof(struct MuttThread *));
709 
710  array[i] = thread;
711  }
712 
713  qsort((void *) array, i, sizeof(struct MuttThread *), *compare_threads);
714 
715  /* attach them back together. make thread the last sibling. */
716  thread = array[0];
717  thread->next = NULL;
718  array[i - 1]->prev = NULL;
719 
720  if (thread->parent)
721  thread->parent->child = array[i - 1];
722  else
723  top = array[i - 1];
724 
725  while (--i)
726  {
727  array[i - 1]->prev = array[i];
728  array[i]->next = array[i - 1];
729  }
730  }
731 
732  if (thread->parent)
733  {
734  tmp = thread;
735  thread = thread->parent;
736 
737  if (!thread->sort_key || thread->sort_children)
738  {
739  /* make sort_key the first or last sibling, as appropriate */
740  sort_key = ((!(C_Sort & SORT_LAST)) ^ (!(C_Sort & SORT_REVERSE))) ?
741  thread->child :
742  tmp;
743 
744  /* we just sorted its children */
745  thread->sort_children = false;
746 
747  oldsort_key = thread->sort_key;
748  thread->sort_key = thread->message;
749 
750  if (C_Sort & SORT_LAST)
751  {
752  if (!thread->sort_key ||
753  ((((C_Sort & SORT_REVERSE) ? 1 : -1) *
754  compare_threads((void *) &thread, (void *) &sort_key)) > 0))
755  {
756  thread->sort_key = sort_key->sort_key;
757  }
758  }
759  else if (!thread->sort_key)
760  thread->sort_key = sort_key->sort_key;
761 
762  /* if its sort_key has changed, we need to resort it and siblings */
763  if (oldsort_key != thread->sort_key)
764  {
765  if (thread->parent)
766  thread->parent->sort_children = true;
767  else
768  sort_top = 1;
769  }
770  }
771  }
772  else
773  {
774  C_Sort ^= SORT_REVERSE;
775  FREE(&array);
776  return top;
777  }
778  }
779 
780  thread = thread->next;
781  }
782 }
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:628
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.
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 789 of file mutt_thread.c.

790 {
791  if (!m)
792  return;
793 
794  struct Email *e = NULL;
795  struct MuttThread *tmp = NULL;
796  for (int i = 0; i < m->msg_count; i++)
797  {
798  e = m->emails[i];
799  if (e->thread->check_subject)
800  e->thread->check_subject = false;
801  else if (!init)
802  continue;
803 
804  /* figure out which messages have subjects different than their parents' */
805  tmp = e->thread->parent;
806  while (tmp && !tmp->message)
807  {
808  tmp = tmp->parent;
809  }
810 
811  if (!tmp)
812  e->subject_changed = true;
813  else if (e->env->real_subj && tmp->message->env->real_subj)
814  {
815  e->subject_changed =
816  (mutt_str_strcmp(e->env->real_subj, tmp->message->env->real_subj) != 0);
817  }
818  else
819  {
820  e->subject_changed = (e->env->real_subj || tmp->message->env->real_subj);
821  }
822  }
823 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:110
int msg_count
Total number of messages.
Definition: mailbox.h:102
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 830 of file mutt_thread.c.

831 {
832  if (!ctx || !ctx->mailbox)
833  return;
834 
835  struct Mailbox *m = ctx->mailbox;
836 
837  struct Email *e = NULL;
838  int i, oldsort, using_refs = 0;
839  struct MuttThread *thread = NULL, *tnew = NULL, *tmp = NULL;
840  struct MuttThread top = { 0 };
841  struct ListNode *ref = NULL;
842 
843  /* Set C_Sort to the secondary method to support the set sort_aux=reverse-*
844  * settings. The sorting functions just look at the value of SORT_REVERSE */
845  oldsort = C_Sort;
846  C_Sort = C_SortAux;
847 
848  if (!ctx->thread_hash)
849  init = true;
850 
851  if (init)
852  {
855  }
856 
857  /* we want a quick way to see if things are actually attached to the top of the
858  * thread tree or if they're just dangling, so we attach everything to a top
859  * node temporarily */
860  top.parent = NULL;
861  top.next = NULL;
862  top.prev = NULL;
863 
864  top.child = ctx->tree;
865  for (thread = ctx->tree; thread; thread = thread->next)
866  thread->parent = &top;
867 
868  /* put each new message together with the matching messageless MuttThread if it
869  * exists. otherwise, if there is a MuttThread that already has a message, thread
870  * new message as an identical child. if we didn't attach the message to a
871  * MuttThread, make a new one for it. */
872  for (i = 0; i < m->msg_count; i++)
873  {
874  e = m->emails[i];
875 
876  if (!e->thread)
877  {
878  if ((!init || C_DuplicateThreads) && e->env->message_id)
879  thread = mutt_hash_find(ctx->thread_hash, e->env->message_id);
880  else
881  thread = NULL;
882 
883  if (thread && !thread->message)
884  {
885  /* this is a message which was missing before */
886  thread->message = e;
887  e->thread = thread;
888  thread->check_subject = true;
889 
890  /* mark descendants as needing subject_changed checked */
891  for (tmp = (thread->child ? thread->child : thread); tmp != thread;)
892  {
893  while (!tmp->message)
894  tmp = tmp->child;
895  tmp->check_subject = true;
896  while (!tmp->next && (tmp != thread))
897  tmp = tmp->parent;
898  if (tmp != thread)
899  tmp = tmp->next;
900  }
901 
902  if (thread->parent)
903  {
904  /* remove threading info above it based on its children, which we'll
905  * recalculate based on its headers. make sure not to leave
906  * dangling missing messages. note that we haven't kept track
907  * of what info came from its children and what from its siblings'
908  * children, so we just remove the stuff that's definitely from it */
909  do
910  {
911  tmp = thread->parent;
912  unlink_message(&tmp->child, thread);
913  thread->parent = NULL;
914  thread->sort_key = NULL;
915  thread->fake_thread = false;
916  thread = tmp;
917  } while (thread != &top && !thread->child && !thread->message);
918  }
919  }
920  else
921  {
922  tnew = (C_DuplicateThreads ? thread : NULL);
923 
924  thread = mutt_mem_calloc(1, sizeof(struct MuttThread));
925  thread->message = e;
926  thread->check_subject = true;
927  e->thread = thread;
929  e->env->message_id ? e->env->message_id : "", thread);
930 
931  if (tnew)
932  {
933  if (tnew->duplicate_thread)
934  tnew = tnew->parent;
935 
936  thread = e->thread;
937 
938  insert_message(&tnew->child, tnew, thread);
939  thread->duplicate_thread = true;
940  thread->message->threaded = true;
941  }
942  }
943  }
944  else
945  {
946  /* unlink pseudo-threads because they might be children of newly
947  * arrived messages */
948  thread = e->thread;
949  for (tnew = thread->child; tnew;)
950  {
951  tmp = tnew->next;
952  if (tnew->fake_thread)
953  {
954  unlink_message(&thread->child, tnew);
955  insert_message(&top.child, &top, tnew);
956  tnew->fake_thread = false;
957  }
958  tnew = tmp;
959  }
960  }
961  }
962 
963  /* thread by references */
964  for (i = 0; i < m->msg_count; i++)
965  {
966  e = m->emails[i];
967  if (e->threaded)
968  continue;
969  e->threaded = true;
970 
971  thread = e->thread;
972  using_refs = 0;
973 
974  while (true)
975  {
976  if (using_refs == 0)
977  {
978  /* look at the beginning of in-reply-to: */
979  ref = STAILQ_FIRST(&e->env->in_reply_to);
980  if (ref)
981  using_refs = 1;
982  else
983  {
984  ref = STAILQ_FIRST(&e->env->references);
985  using_refs = 2;
986  }
987  }
988  else if (using_refs == 1)
989  {
990  /* if there's no references header, use all the in-reply-to:
991  * data that we have. otherwise, use the first reference
992  * if it's different than the first in-reply-to, otherwise use
993  * the second reference (since at least eudora puts the most
994  * recent reference in in-reply-to and the rest in references) */
995  if (STAILQ_EMPTY(&e->env->references))
996  ref = STAILQ_NEXT(ref, entries);
997  else
998  {
999  if (mutt_str_strcmp(ref->data, STAILQ_FIRST(&e->env->references)->data) != 0)
1000  ref = STAILQ_FIRST(&e->env->references);
1001  else
1002  ref = STAILQ_NEXT(STAILQ_FIRST(&e->env->references), entries);
1003 
1004  using_refs = 2;
1005  }
1006  }
1007  else
1008  ref = STAILQ_NEXT(ref, entries); /* go on with references */
1009 
1010  if (!ref)
1011  break;
1012 
1013  tnew = mutt_hash_find(ctx->thread_hash, ref->data);
1014  if (tnew)
1015  {
1016  if (tnew->duplicate_thread)
1017  tnew = tnew->parent;
1018  if (is_descendant(tnew, thread)) /* no loops! */
1019  continue;
1020  }
1021  else
1022  {
1023  tnew = mutt_mem_calloc(1, sizeof(struct MuttThread));
1024  mutt_hash_insert(ctx->thread_hash, ref->data, tnew);
1025  }
1026 
1027  if (thread->parent)
1028  unlink_message(&top.child, thread);
1029  insert_message(&tnew->child, tnew, thread);
1030  thread = tnew;
1031  if (thread->message || (thread->parent && (thread->parent != &top)))
1032  break;
1033  }
1034 
1035  if (!thread->parent)
1036  insert_message(&top.child, &top, thread);
1037  }
1038 
1039  /* detach everything from the temporary top node */
1040  for (thread = top.child; thread; thread = thread->next)
1041  {
1042  thread->parent = NULL;
1043  }
1044  ctx->tree = top.child;
1045 
1046  check_subjects(ctx->mailbox, init);
1047 
1048  if (!C_StrictThreads)
1049  pseudo_threads(ctx);
1050 
1051  if (ctx->tree)
1052  {
1053  ctx->tree = mutt_sort_subthreads(ctx->tree, init);
1054 
1055  /* restore the oldsort order. */
1056  C_Sort = oldsort;
1057 
1058  /* Put the list into an array. */
1059  linearize_tree(ctx);
1060 
1061  /* Draw the thread tree. */
1062  mutt_draw_tree(ctx);
1063  }
1064 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:110
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:102
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:652
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:528
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:92
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:398
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:346
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:789
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:348
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 1073 of file mutt_thread.c.

1074 {
1075  struct MuttThread *cur = NULL;
1076  struct Email *e_tmp = NULL;
1077 
1078  if ((C_Sort & SORT_MASK) != SORT_THREADS)
1079  {
1080  mutt_error(_("Threading is not enabled"));
1081  return e->vnum;
1082  }
1083 
1084  cur = e->thread;
1085 
1086  if (subthreads)
1087  {
1088  if (forwards ^ ((C_Sort & SORT_REVERSE) != 0))
1089  {
1090  while (!cur->next && cur->parent)
1091  cur = cur->parent;
1092  }
1093  else
1094  {
1095  while (!cur->prev && cur->parent)
1096  cur = cur->parent;
1097  }
1098  }
1099  else
1100  {
1101  while (cur->parent)
1102  cur = cur->parent;
1103  }
1104 
1105  if (forwards ^ ((C_Sort & SORT_REVERSE) != 0))
1106  {
1107  do
1108  {
1109  cur = cur->next;
1110  if (!cur)
1111  return -1;
1112  e_tmp = find_virtual(cur, 0);
1113  } while (!e_tmp);
1114  }
1115  else
1116  {
1117  do
1118  {
1119  cur = cur->prev;
1120  if (!cur)
1121  return -1;
1122  e_tmp = find_virtual(cur, 1);
1123  } while (!e_tmp);
1124  }
1125 
1126  return e_tmp->vnum;
1127 }
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 1137 of file mutt_thread.c.

1138 {
1139  struct MuttThread *thread = NULL;
1140  struct Email *e_parent = NULL;
1141 
1142  if ((C_Sort & SORT_MASK) != SORT_THREADS)
1143  {
1144  mutt_error(_("Threading is not enabled"));
1145  return e->vnum;
1146  }
1147 
1148  /* Root may be the current message */
1149  if (find_root)
1150  e_parent = e;
1151 
1152  for (thread = e->thread->parent; thread; thread = thread->parent)
1153  {
1154  e = thread->message;
1155  if (e)
1156  {
1157  e_parent = e;
1158  if (!find_root)
1159  break;
1160  }
1161  }
1162 
1163  if (!e_parent)
1164  {
1165  mutt_error(_("Parent message is not available"));
1166  return -1;
1167  }
1168  if (!is_visible(e_parent, ctx))
1169  {
1170  if (find_root)
1171  mutt_error(_("Root message is not visible in this limited view"));
1172  else
1173  mutt_error(_("Parent message is not visible in this limited view"));
1174  return -1;
1175  }
1176  return e_parent->vnum;
1177 }
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 1183 of file mutt_thread.c.

1184 {
1185  if (!ctx || !ctx->mailbox)
1186  return;
1187 
1188  struct Mailbox *m = ctx->mailbox;
1189 
1190  struct Email *e = NULL;
1191 
1192  m->vcount = 0;
1193  ctx->vsize = 0;
1194  int padding = mx_msg_padding_size(m);
1195 
1196  for (int i = 0; i < m->msg_count; i++)
1197  {
1198  e = m->emails[i];
1199  if (e->vnum >= 0)
1200  {
1201  e->vnum = m->vcount;
1202  m->v2r[m->vcount] = i;
1203  m->vcount++;
1204  ctx->vsize += e->content->length + e->content->offset - e->content->hdr_offset + padding;
1205  e->num_hidden = mutt_get_hidden(ctx, e);
1206  }
1207  }
1208 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:110
int msg_count
Total number of messages.
Definition: mailbox.h:102
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:113
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:92
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:112
int mx_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Wrapper for MxOps::msg_padding_size()
Definition: mx.c:1475
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 1217 of file mutt_thread.c.

1218 {
1219  struct MuttThread *thread = NULL, *top = NULL;
1220  struct Email *e_root = NULL;
1221  int final, reverse = (C_Sort & SORT_REVERSE), minmsgno;
1222  int num_hidden = 0, new_mail = 0, old_mail = 0;
1223  bool flagged = false;
1224  int min_unread_msgno = INT_MAX, min_unread = e_cur->vnum;
1225 #define CHECK_LIMIT (!ctx->pattern || e_cur->limited)
1226 
1227  if (((C_Sort & SORT_MASK) != SORT_THREADS) && !(flag & MUTT_THREAD_GET_HIDDEN))
1228  {
1229  mutt_error(_("Threading is not enabled"));
1230  return e_cur->vnum;
1231  }
1232 
1233  final = e_cur->vnum;
1234  thread = e_cur->thread;
1235  while (thread->parent)
1236  thread = thread->parent;
1237  top = thread;
1238  while (!thread->message)
1239  thread = thread->child;
1240  e_cur = thread->message;
1241  minmsgno = e_cur->msgno;
1242 
1243  if (!e_cur->read && CHECK_LIMIT)
1244  {
1245  if (e_cur->old)
1246  old_mail = 2;
1247  else
1248  new_mail = 1;
1249  if (e_cur->msgno < min_unread_msgno)
1250  {
1251  min_unread = e_cur->vnum;
1252  min_unread_msgno = e_cur->msgno;
1253  }
1254  }
1255 
1256  if (e_cur->flagged && CHECK_LIMIT)
1257  flagged = true;
1258 
1259  if ((e_cur->vnum == -1) && CHECK_LIMIT)
1260  num_hidden++;
1261 
1263  {
1264  e_cur->pair = 0; /* force index entry's color to be re-evaluated */
1265  e_cur->collapsed = flag & MUTT_THREAD_COLLAPSE;
1266  if (e_cur->vnum != -1)
1267  {
1268  e_root = e_cur;
1269  if (flag & MUTT_THREAD_COLLAPSE)
1270  final = e_root->vnum;
1271  }
1272  }
1273 
1274  if ((thread == top) && !(thread = thread->child))
1275  {
1276  /* return value depends on action requested */
1277  if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
1278  return final;
1279  if (flag & MUTT_THREAD_UNREAD)
1280  return (old_mail && new_mail) ? new_mail : (old_mail ? old_mail : new_mail);
1281  if (flag & MUTT_THREAD_GET_HIDDEN)
1282  return num_hidden;
1283  if (flag & MUTT_THREAD_NEXT_UNREAD)
1284  return min_unread;
1285  if (flag & MUTT_THREAD_FLAGGED)
1286  return flagged;
1287  }
1288 
1289  while (true)
1290  {
1291  e_cur = thread->message;
1292 
1293  if (e_cur)
1294  {
1295  if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
1296  {
1297  e_cur->pair = 0; /* force index entry's color to be re-evaluated */
1298  e_cur->collapsed = flag & MUTT_THREAD_COLLAPSE;
1299  if (!e_root && CHECK_LIMIT)
1300  {
1301  e_root = e_cur;
1302  if (flag & MUTT_THREAD_COLLAPSE)
1303  final = e_root->vnum;
1304  }
1305 
1306  if (reverse && (flag & MUTT_THREAD_COLLAPSE) && (e_cur->msgno < minmsgno) && CHECK_LIMIT)
1307  {
1308  minmsgno = e_cur->msgno;
1309  final = e_cur->vnum;
1310  }
1311 
1312  if (flag & MUTT_THREAD_COLLAPSE)
1313  {
1314  if (e_cur != e_root)
1315  e_cur->vnum = -1;
1316  }
1317  else
1318  {
1319  if (CHECK_LIMIT)
1320  e_cur->vnum = e_cur->msgno;
1321  }
1322  }
1323 
1324  if (!e_cur->read && CHECK_LIMIT)
1325  {
1326  if (e_cur->old)
1327  old_mail = 2;
1328  else
1329  new_mail = 1;
1330  if (e_cur->msgno < min_unread_msgno)
1331  {
1332  min_unread = e_cur->vnum;
1333  min_unread_msgno = e_cur->msgno;
1334  }
1335  }
1336 
1337  if (e_cur->flagged && CHECK_LIMIT)
1338  flagged = true;
1339 
1340  if ((e_cur->vnum == -1) && CHECK_LIMIT)
1341  num_hidden++;
1342  }
1343 
1344  if (thread->child)
1345  thread = thread->child;
1346  else if (thread->next)
1347  thread = thread->next;
1348  else
1349  {
1350  bool done = false;
1351  while (!thread->next)
1352  {
1353  thread = thread->parent;
1354  if (thread == top)
1355  {
1356  done = true;
1357  break;
1358  }
1359  }
1360  if (done)
1361  break;
1362  thread = thread->next;
1363  }
1364  }
1365 
1366  /* return value depends on action requested */
1367  if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
1368  return final;
1369  if (flag & MUTT_THREAD_UNREAD)
1370  return (old_mail && new_mail) ? new_mail : (old_mail ? old_mail : new_mail);
1371  if (flag & MUTT_THREAD_GET_HIDDEN)
1372  return num_hidden + 1;
1373  if (flag & MUTT_THREAD_NEXT_UNREAD)
1374  return min_unread;
1375  if (flag & MUTT_THREAD_FLAGGED)
1376  return flagged;
1377 
1378  return 0;
1379 #undef CHECK_LIMIT
1380 }
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 1392 of file mutt_thread.c.

1393 {
1394  if (!m || !e)
1395  return 1;
1396 
1397  struct MuttThread *threads[2];
1398  int rc;
1399 
1400  if (((C_Sort & SORT_MASK) != SORT_THREADS) || !e->thread)
1401  return 1;
1402 
1403  threads[0] = e->thread;
1404  while (threads[0]->parent)
1405  threads[0] = threads[0]->parent;
1406 
1407  threads[1] = flag ? e->thread : threads[0]->next;
1408 
1409  for (int i = 0; i < ((flag || !threads[1]) ? 1 : 2); i++)
1410  {
1411  while (!threads[i]->message)
1412  threads[i] = threads[i]->child;
1413  }
1414 
1415  if (C_Sort & SORT_REVERSE)
1416  rc = threads[0]->message->msgno - (threads[1] ? threads[1]->message->msgno : -1);
1417  else
1418  {
1419  rc = (threads[1] ? threads[1]->message->msgno : m->msg_count) -
1420  threads[0]->message->msgno;
1421  }
1422 
1423  if (flag)
1424  rc += 1;
1425 
1426  return rc;
1427 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
int msg_count
Total number of messages.
Definition: mailbox.h:102
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 1434 of file mutt_thread.c.

1435 {
1436  struct Hash *hash = mutt_hash_new(m->msg_count * 2, MUTT_HASH_NO_FLAGS);
1437 
1438  for (int i = 0; i < m->msg_count; i++)
1439  {
1440  struct Email *e = m->emails[i];
1441  if (e->env->message_id)
1442  mutt_hash_insert(hash, e->env->message_id, e);
1443  }
1444 
1445  return hash;
1446 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:110
int msg_count
Total number of messages.
Definition: mailbox.h:102
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 1455 of file mutt_thread.c.

1456 {
1457  if (child == parent)
1458  return false;
1459 
1460  mutt_break_thread(child);
1462  mutt_set_flag(m, child, MUTT_TAG, false);
1463 
1464  child->changed = true;
1465  child->env->changed |= MUTT_ENV_CHANGED_IRT;
1466  return true;
1467 }
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:69
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 1476 of file mutt_thread.c.

1477 {
1478  if (!parent || !children || !m)
1479  return false;
1480 
1481  bool changed = false;
1482 
1483  struct EmailNode *en = NULL;
1484  STAILQ_FOREACH(en, children, entries)
1485  {
1486  changed |= link_threads(parent, en->email, m);
1487  }
1488 
1489  return changed;
1490 }
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:1455
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:350
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.