NeoMutt  2018-07-16 +2388-bcedc8
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

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

Function Documentation

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:76
bool collapsed
Is this message part of a collapsed thread?
Definition: email.h:75
int vnum
Virtual message number.
Definition: email.h:89
char * pattern
Limit pattern string.
Definition: context.h:39

+ Here is the caller graph for this function:

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  else
99  break;
100  }
101  }
102 
103  /* if there is a parent-to-child subject change anywhere between us and our
104  * closest displayed ancestor, display the subject */
105  for (tmp = tree->parent; tmp; tmp = tmp->parent)
106  {
107  e = tmp->message;
108  if (e)
109  {
110  if (is_visible(e, ctx))
111  return false;
112  else if (e->subject_changed)
113  return true;
114  }
115  }
116 
117  /* if we have no visible parent or previous sibling, display the subject */
118  return true;
119 }
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:96
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:57

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

static void linearize_tree ( struct Context ctx)
static

Flatten an email thread.

Parameters
ctxMailbox

Definition at line 125 of file mutt_thread.c.

126 {
127  if (!ctx || !ctx->mailbox)
128  return;
129 
130  struct Mailbox *m = ctx->mailbox;
131 
132  struct MuttThread *tree = ctx->tree;
133  struct Email **array = m->emails + ((C_Sort & SORT_REVERSE) ? m->msg_count - 1 : 0);
134 
135  while (tree)
136  {
137  while (!tree->message)
138  tree = tree->child;
139 
140  *array = tree->message;
141  array += (C_Sort & SORT_REVERSE) ? -1 : 1;
142 
143  if (tree->child)
144  tree = tree->child;
145  else
146  {
147  while (tree)
148  {
149  if (tree->next)
150  {
151  tree = tree->next;
152  break;
153  }
154  else
155  tree = tree->parent;
156  }
157  }
158  }
159 }
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:39
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: sort.h:86

+ Here is the caller graph for this function:

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 173 of file mutt_thread.c.

174 {
175  struct MuttThread *tmp = NULL;
176  struct MuttThread *tree = ctx->tree;
177  int hide_top_missing = C_HideTopMissing && !C_HideMissing;
178  int hide_top_limited = C_HideTopLimited && !C_HideLimited;
179  int depth = 0;
180 
181  /* we walk each level backwards to make it easier to compute next_subtree_visible */
182  while (tree->next)
183  tree = tree->next;
184  *max_depth = 0;
185 
186  while (true)
187  {
188  if (depth > *max_depth)
189  *max_depth = depth;
190 
191  tree->subtree_visible = 0;
192  if (tree->message)
193  {
194  FREE(&tree->message->tree);
195  if (is_visible(tree->message, ctx))
196  {
197  tree->deep = true;
198  tree->visible = true;
200  for (tmp = tree; tmp; tmp = tmp->parent)
201  {
202  if (tmp->subtree_visible)
203  {
204  tmp->deep = true;
205  tmp->subtree_visible = 2;
206  break;
207  }
208  else
209  tmp->subtree_visible = 1;
210  }
211  }
212  else
213  {
214  tree->visible = false;
215  tree->deep = !C_HideLimited;
216  }
217  }
218  else
219  {
220  tree->visible = false;
221  tree->deep = !C_HideMissing;
222  }
223  tree->next_subtree_visible =
224  tree->next && (tree->next->next_subtree_visible || tree->next->subtree_visible);
225  if (tree->child)
226  {
227  depth++;
228  tree = tree->child;
229  while (tree->next)
230  tree = tree->next;
231  }
232  else if (tree->prev)
233  tree = tree->prev;
234  else
235  {
236  while (tree && !tree->prev)
237  {
238  depth--;
239  tree = tree->parent;
240  }
241  if (!tree)
242  break;
243  else
244  tree = tree->prev;
245  }
246  }
247 
248  /* now fix up for the OPTHIDETOP* options if necessary */
249  if (hide_top_limited || hide_top_missing)
250  {
251  tree = ctx->tree;
252  while (true)
253  {
254  if (!tree->visible && tree->deep && (tree->subtree_visible < 2) &&
255  ((tree->message && hide_top_limited) || (!tree->message && hide_top_missing)))
256  {
257  tree->deep = false;
258  }
259  if (!tree->deep && tree->child && tree->subtree_visible)
260  tree = tree->child;
261  else if (tree->next)
262  tree = tree->next;
263  else
264  {
265  while (tree && !tree->next)
266  tree = tree->parent;
267  if (!tree)
268  break;
269  else
270  tree = tree->next;
271  }
272  }
273  }
274 }
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:59
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:95
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:

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 288 of file mutt_thread.c.

289 {
290  char *pfx = NULL, *mypfx = NULL, *arrow = NULL, *myarrow = NULL, *new_tree = NULL;
293  int depth = 0, start_depth = 0, max_depth = 0, width = C_NarrowTree ? 1 : 2;
294  struct MuttThread *nextdisp = NULL, *pseudo = NULL, *parent = NULL;
295  struct MuttThread *tree = ctx->tree;
296 
297  /* Do the visibility calculations and free the old thread chars.
298  * From now on we can simply ignore invisible subtrees */
299  calculate_visibility(ctx, &max_depth);
300  pfx = mutt_mem_malloc(width * max_depth + 2);
301  arrow = mutt_mem_malloc(width * max_depth + 2);
302  while (tree)
303  {
304  if (depth)
305  {
306  myarrow = arrow + (depth - start_depth - ((start_depth != 0) ? 0 : 1)) * width;
307  if (depth && (start_depth == depth))
308  myarrow[0] = nextdisp ? MUTT_TREE_LTEE : corner;
309  else if (parent->message && !C_HideLimited)
310  myarrow[0] = MUTT_TREE_HIDDEN;
311  else if (!parent->message && !C_HideMissing)
312  myarrow[0] = MUTT_TREE_MISSING;
313  else
314  myarrow[0] = vtee;
315  if (width == 2)
316  {
317  myarrow[1] = pseudo ? MUTT_TREE_STAR :
319  }
320  if (tree->visible)
321  {
322  myarrow[width] = MUTT_TREE_RARROW;
323  myarrow[width + 1] = 0;
324  new_tree = mutt_mem_malloc((2 + depth * width));
325  if (start_depth > 1)
326  {
327  strncpy(new_tree, pfx, (start_depth - 1) * width);
328  mutt_str_strfcpy(new_tree + (start_depth - 1) * width, arrow,
329  (1 + depth - start_depth) * width + 2);
330  }
331  else
332  mutt_str_strfcpy(new_tree, arrow, 2 + depth * width);
333  tree->message->tree = new_tree;
334  }
335  }
336  if (tree->child && depth)
337  {
338  mypfx = pfx + (depth - 1) * width;
339  mypfx[0] = nextdisp ? MUTT_TREE_VLINE : MUTT_TREE_SPACE;
340  if (width == 2)
341  mypfx[1] = MUTT_TREE_SPACE;
342  }
343  parent = tree;
344  nextdisp = NULL;
345  pseudo = NULL;
346  do
347  {
348  if (tree->child && tree->subtree_visible)
349  {
350  if (tree->deep)
351  depth++;
352  if (tree->visible)
353  start_depth = depth;
354  tree = tree->child;
355 
356  /* we do this here because we need to make sure that the first child thread
357  * of the old tree that we deal with is actually displayed if any are,
358  * or we might set the parent variable wrong while going through it. */
359  while (!tree->subtree_visible && tree->next)
360  tree = tree->next;
361  }
362  else
363  {
364  while (!tree->next && tree->parent)
365  {
366  if (tree == pseudo)
367  pseudo = NULL;
368  if (tree == nextdisp)
369  nextdisp = NULL;
370  if (tree->visible)
371  start_depth = depth;
372  tree = tree->parent;
373  if (tree->deep)
374  {
375  if (start_depth == depth)
376  start_depth--;
377  depth--;
378  }
379  }
380  if (tree == pseudo)
381  pseudo = NULL;
382  if (tree == nextdisp)
383  nextdisp = NULL;
384  if (tree->visible)
385  start_depth = depth;
386  tree = tree->next;
387  if (!tree)
388  break;
389  }
390  if (!pseudo && tree->fake_thread)
391  pseudo = tree;
392  if (!nextdisp && tree->next_subtree_visible)
393  nextdisp = tree;
394  } while (!tree->deep);
395  }
396 
397  FREE(&pfx);
398  FREE(&arrow);
399 }
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:173
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:95
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: sort.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:

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 411 of file mutt_thread.c.

412 {
413  struct MuttThread *start = cur;
414  struct Envelope *env = NULL;
415  time_t thisdate;
416  int rc = 0;
417 
418  while (true)
419  {
420  while (!cur->message)
421  cur = cur->child;
422 
423  if (dateptr)
424  {
425  thisdate = C_ThreadReceived ? cur->message->received : cur->message->date_sent;
426  if (!*dateptr || (thisdate < *dateptr))
427  *dateptr = thisdate;
428  }
429 
430  env = cur->message->env;
431  if (env->real_subj && ((env->real_subj != env->subject) || (!C_SortRe)))
432  {
433  struct ListNode *np = NULL;
434  STAILQ_FOREACH(np, subjects, entries)
435  {
436  rc = mutt_str_strcmp(env->real_subj, np->data);
437  if (rc >= 0)
438  break;
439  }
440  if (!np)
441  mutt_list_insert_head(subjects, env->real_subj);
442  else if (rc > 0)
443  mutt_list_insert_after(subjects, np, env->real_subj);
444  }
445 
446  while (!cur->next && (cur != start))
447  {
448  cur = cur->parent;
449  }
450  if (cur == start)
451  break;
452  cur = cur->next;
453  }
454 }
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:91
time_t date_sent
Time when the message was sent (UTC)
Definition: email.h:83
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
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:84
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:

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 465 of file mutt_thread.c.

466 {
467  if (!m)
468  return NULL;
469 
470  struct HashElem *ptr = NULL;
471  struct MuttThread *tmp = NULL, *last = NULL;
472  struct ListHead subjects = STAILQ_HEAD_INITIALIZER(subjects);
473  time_t date = 0;
474 
475  make_subject_list(&subjects, cur, &date);
476 
477  struct ListNode *np = NULL;
478  STAILQ_FOREACH(np, &subjects, entries)
479  {
480  for (ptr = mutt_hash_find_bucket(m->subj_hash, np->data); ptr; ptr = ptr->next)
481  {
482  tmp = ((struct Email *) ptr->data)->thread;
483  if ((tmp != cur) && /* don't match the same message */
484  !tmp->fake_thread && /* don't match pseudo threads */
485  tmp->message->subject_changed && /* only match interesting replies */
486  !is_descendant(tmp, cur) && /* don't match in the same thread */
487  (date >= (C_ThreadReceived ? tmp->message->received : tmp->message->date_sent)) &&
488  (!last || (C_ThreadReceived ?
489  (last->message->received < tmp->message->received) :
490  (last->message->date_sent < tmp->message->date_sent))) &&
491  tmp->message->env->real_subj &&
492  (mutt_str_strcmp(np->data, tmp->message->env->real_subj) == 0))
493  {
494  last = tmp; /* best match so far */
495  }
496  }
497  }
498 
499  mutt_list_clear(&subjects);
500  return last;
501 }
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:426
The envelope/body of an email.
Definition: email.h:39
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:91
time_t date_sent
Time when the message was sent (UTC)
Definition: email.h:83
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
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:57
#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:411
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:84

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

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 508 of file mutt_thread.c.

509 {
510  if (!m)
511  return NULL;
512 
513  struct Hash *hash = mutt_hash_new(m->msg_count * 2, MUTT_HASH_ALLOW_DUPS);
514 
515  for (int i = 0; i < m->msg_count; i++)
516  {
517  struct Email *e = m->emails[i];
518  if (e->env->real_subj)
519  mutt_hash_insert(hash, e->env->real_subj, e);
520  }
521 
522  return hash;
523 }
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:39
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:91
#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:276
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:352

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

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 531 of file mutt_thread.c.

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

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void mutt_clear_threads ( struct Context ctx)

Clear the threading of message in a mailbox.

Parameters
ctxMailbox

Definition at line 602 of file mutt_thread.c.

603 {
604  if (!ctx || !ctx->mailbox || !ctx->mailbox->emails)
605  return;
606 
607  struct Mailbox *m = ctx->mailbox;
608 
609  for (int i = 0; i < m->msg_count; i++)
610  {
611  /* mailbox may have been only partially read */
612  if (m->emails[i])
613  {
614  m->emails[i]->thread = NULL;
615  m->emails[i]->threaded = false;
616  }
617  }
618  ctx->tree = NULL;
619 
621 }
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:96
bool threaded
Used for threading.
Definition: email.h:58
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:472

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

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 }
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: sort.h:85

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

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:39
struct MuttThread * thread
Thread of Emails.
Definition: email.h:96
#define SORT_LAST
Sort thread by last-X, e.g.
Definition: sort.h:87
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: sort.h:86

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

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  struct Email *e = NULL;
798  struct MuttThread *tmp = NULL;
799  for (int i = 0; i < m->msg_count; i++)
800  {
801  e = m->emails[i];
802  if (e->thread->check_subject)
803  e->thread->check_subject = false;
804  else if (!init)
805  continue;
806 
807  /* figure out which messages have subjects different than their parents' */
808  tmp = e->thread->parent;
809  while (tmp && !tmp->message)
810  {
811  tmp = tmp->parent;
812  }
813 
814  if (!tmp)
815  e->subject_changed = true;
816  else if (e->env->real_subj && tmp->message->env->real_subj)
817  {
818  e->subject_changed =
819  (mutt_str_strcmp(e->env->real_subj, tmp->message->env->real_subj) != 0);
820  }
821  else
822  {
823  e->subject_changed = (e->env->real_subj || tmp->message->env->real_subj);
824  }
825  }
826 }
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:39
struct MuttThread * thread
Thread of Emails.
Definition: email.h:96
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:91
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:57
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:

void mutt_sort_threads ( struct Context ctx,
bool  init 
)

Sort email threads.

Parameters
ctxMailbox
initIf true, rebuild the thread

Definition at line 833 of file mutt_thread.c.

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

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

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 1076 of file mutt_thread.c.

1077 {
1078  struct MuttThread *cur = NULL;
1079  struct Email *e_tmp = NULL;
1080 
1081  if ((C_Sort & SORT_MASK) != SORT_THREADS)
1082  {
1083  mutt_error(_("Threading is not enabled"));
1084  return e->vnum;
1085  }
1086 
1087  cur = e->thread;
1088 
1089  if (!subthreads)
1090  {
1091  while (cur->parent)
1092  cur = cur->parent;
1093  }
1094  else
1095  {
1096  if (forwards ^ ((C_Sort & SORT_REVERSE) != 0))
1097  {
1098  while (!cur->next && cur->parent)
1099  cur = cur->parent;
1100  }
1101  else
1102  {
1103  while (!cur->prev && cur->parent)
1104  cur = cur->parent;
1105  }
1106  }
1107 
1108  if (forwards ^ ((C_Sort & SORT_REVERSE) != 0))
1109  {
1110  do
1111  {
1112  cur = cur->next;
1113  if (!cur)
1114  return -1;
1115  e_tmp = find_virtual(cur, 0);
1116  } while (!e_tmp);
1117  }
1118  else
1119  {
1120  do
1121  {
1122  cur = cur->prev;
1123  if (!cur)
1124  return -1;
1125  e_tmp = find_virtual(cur, 1);
1126  } while (!e_tmp);
1127  }
1128 
1129  return e_tmp->vnum;
1130 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
The envelope/body of an email.
Definition: email.h:39
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:96
#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
int vnum
Virtual message number.
Definition: email.h:89
An Email conversation.
Definition: thread.h:34
#define mutt_error(...)
Definition: logging.h:84
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort.h:86
Sort by email threads.
Definition: sort.h:56
#define SORT_MASK
Mask for the sort id.
Definition: sort.h:85

+ Here is the call graph for this function:

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 1140 of file mutt_thread.c.

1141 {
1142  struct MuttThread *thread = NULL;
1143  struct Email *e_parent = NULL;
1144 
1145  if ((C_Sort & SORT_MASK) != SORT_THREADS)
1146  {
1147  mutt_error(_("Threading is not enabled"));
1148  return e->vnum;
1149  }
1150 
1151  /* Root may be the current message */
1152  if (find_root)
1153  e_parent = e;
1154 
1155  for (thread = e->thread->parent; thread; thread = thread->parent)
1156  {
1157  e = thread->message;
1158  if (e)
1159  {
1160  e_parent = e;
1161  if (!find_root)
1162  break;
1163  }
1164  }
1165 
1166  if (!e_parent)
1167  {
1168  mutt_error(_("Parent message is not available"));
1169  return -1;
1170  }
1171  if (!is_visible(e_parent, ctx))
1172  {
1173  if (find_root)
1174  mutt_error(_("Root message is not visible in this limited view"));
1175  else
1176  mutt_error(_("Parent message is not visible in this limited view"));
1177  return -1;
1178  }
1179  return e_parent->vnum;
1180 }
The envelope/body of an email.
Definition: email.h:39
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:96
#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
int vnum
Virtual message number.
Definition: email.h:89
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
Sort by email threads.
Definition: sort.h:56
#define SORT_MASK
Mask for the sort id.
Definition: sort.h:85

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void mutt_set_vnum ( struct Context ctx)

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

Parameters
ctxMailbox

Definition at line 1186 of file mutt_thread.c.

1187 {
1188  if (!ctx || !ctx->mailbox)
1189  return;
1190 
1191  struct Mailbox *m = ctx->mailbox;
1192 
1193  struct Email *e = NULL;
1194 
1195  m->vcount = 0;
1196  ctx->vsize = 0;
1197  int padding = mx_msg_padding_size(m);
1198 
1199  for (int i = 0; i < m->msg_count; i++)
1200  {
1201  e = m->emails[i];
1202  if (e->vnum >= 0)
1203  {
1204  e->vnum = m->vcount;
1205  m->v2r[m->vcount] = i;
1206  m->vcount++;
1207  ctx->vsize += e->content->length + e->content->offset - e->content->hdr_offset + padding;
1208  e->num_hidden = mutt_get_hidden(ctx, e);
1209  }
1210  }
1211 }
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:39
struct Body * content
List of MIME parts.
Definition: email.h:92
#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:77
int vnum
Virtual message number.
Definition: email.h:89
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:1464
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:

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 1220 of file mutt_thread.c.

1221 {
1222  struct MuttThread *thread = NULL, *top = NULL;
1223  struct Email *e_root = NULL;
1224  int final, reverse = (C_Sort & SORT_REVERSE), minmsgno;
1225  int num_hidden = 0, new_mail = 0, old_mail = 0;
1226  bool flagged = false;
1227  int min_unread_msgno = INT_MAX, min_unread = e_cur->vnum;
1228 #define CHECK_LIMIT (!ctx->pattern || e_cur->limited)
1229 
1230  if (((C_Sort & SORT_MASK) != SORT_THREADS) && !(flag & MUTT_THREAD_GET_HIDDEN))
1231  {
1232  mutt_error(_("Threading is not enabled"));
1233  return e_cur->vnum;
1234  }
1235 
1236  final = e_cur->vnum;
1237  thread = e_cur->thread;
1238  while (thread->parent)
1239  thread = thread->parent;
1240  top = thread;
1241  while (!thread->message)
1242  thread = thread->child;
1243  e_cur = thread->message;
1244  minmsgno = e_cur->msgno;
1245 
1246  if (!e_cur->read && CHECK_LIMIT)
1247  {
1248  if (e_cur->old)
1249  old_mail = 2;
1250  else
1251  new_mail = 1;
1252  if (e_cur->msgno < min_unread_msgno)
1253  {
1254  min_unread = e_cur->vnum;
1255  min_unread_msgno = e_cur->msgno;
1256  }
1257  }
1258 
1259  if (e_cur->flagged && CHECK_LIMIT)
1260  flagged = true;
1261 
1262  if ((e_cur->vnum == -1) && CHECK_LIMIT)
1263  num_hidden++;
1264 
1266  {
1267  e_cur->pair = 0; /* force index entry's color to be re-evaluated */
1268  e_cur->collapsed = flag & MUTT_THREAD_COLLAPSE;
1269  if (e_cur->vnum != -1)
1270  {
1271  e_root = e_cur;
1272  if (flag & MUTT_THREAD_COLLAPSE)
1273  final = e_root->vnum;
1274  }
1275  }
1276 
1277  if ((thread == top) && !(thread = thread->child))
1278  {
1279  /* return value depends on action requested */
1280  if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
1281  return final;
1282  else if (flag & MUTT_THREAD_UNREAD)
1283  return (old_mail && new_mail) ? new_mail : (old_mail ? old_mail : new_mail);
1284  else if (flag & MUTT_THREAD_GET_HIDDEN)
1285  return num_hidden;
1286  else if (flag & MUTT_THREAD_NEXT_UNREAD)
1287  return min_unread;
1288  else if (flag & MUTT_THREAD_FLAGGED)
1289  return flagged;
1290  }
1291 
1292  while (true)
1293  {
1294  e_cur = thread->message;
1295 
1296  if (e_cur)
1297  {
1298  if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
1299  {
1300  e_cur->pair = 0; /* force index entry's color to be re-evaluated */
1301  e_cur->collapsed = flag & MUTT_THREAD_COLLAPSE;
1302  if (!e_root && CHECK_LIMIT)
1303  {
1304  e_root = e_cur;
1305  if (flag & MUTT_THREAD_COLLAPSE)
1306  final = e_root->vnum;
1307  }
1308 
1309  if (reverse && (flag & MUTT_THREAD_COLLAPSE) && (e_cur->msgno < minmsgno) && CHECK_LIMIT)
1310  {
1311  minmsgno = e_cur->msgno;
1312  final = e_cur->vnum;
1313  }
1314 
1315  if (flag & MUTT_THREAD_COLLAPSE)
1316  {
1317  if (e_cur != e_root)
1318  e_cur->vnum = -1;
1319  }
1320  else
1321  {
1322  if (CHECK_LIMIT)
1323  e_cur->vnum = e_cur->msgno;
1324  }
1325  }
1326 
1327  if (!e_cur->read && CHECK_LIMIT)
1328  {
1329  if (e_cur->old)
1330  old_mail = 2;
1331  else
1332  new_mail = 1;
1333  if (e_cur->msgno < min_unread_msgno)
1334  {
1335  min_unread = e_cur->vnum;
1336  min_unread_msgno = e_cur->msgno;
1337  }
1338  }
1339 
1340  if (e_cur->flagged && CHECK_LIMIT)
1341  flagged = true;
1342 
1343  if ((e_cur->vnum == -1) && CHECK_LIMIT)
1344  num_hidden++;
1345  }
1346 
1347  if (thread->child)
1348  thread = thread->child;
1349  else if (thread->next)
1350  thread = thread->next;
1351  else
1352  {
1353  bool done = false;
1354  while (!thread->next)
1355  {
1356  thread = thread->parent;
1357  if (thread == top)
1358  {
1359  done = true;
1360  break;
1361  }
1362  }
1363  if (done)
1364  break;
1365  thread = thread->next;
1366  }
1367  }
1368 
1369  /* return value depends on action requested */
1370  if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
1371  return final;
1372  else if (flag & MUTT_THREAD_UNREAD)
1373  return (old_mail && new_mail) ? new_mail : (old_mail ? old_mail : new_mail);
1374  else if (flag & MUTT_THREAD_GET_HIDDEN)
1375  return num_hidden + 1;
1376  else if (flag & MUTT_THREAD_NEXT_UNREAD)
1377  return min_unread;
1378  else if (flag & MUTT_THREAD_FLAGGED)
1379  return flagged;
1380 
1381  return 0;
1382 #undef CHECK_LIMIT
1383 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
The envelope/body of an email.
Definition: email.h:39
#define MUTT_THREAD_UNCOLLAPSE
Uncollapse an email thread.
Definition: mutt_thread.h:50
struct MuttThread * thread
Thread of Emails.
Definition: email.h:96
#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:53
bool old
Email is seen, but unread.
Definition: email.h:52
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
size_t num_hidden
Number of hidden messages in this view.
Definition: email.h:77
bool collapsed
Is this message part of a collapsed thread?
Definition: email.h:75
int vnum
Virtual message number.
Definition: email.h:89
#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:45
#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: sort.h:86
Sort by email threads.
Definition: sort.h:56
#define SORT_MASK
Mask for the sort id.
Definition: sort.h:85
int pair
Color-pair to use when displaying in the index.
Definition: email.h:81
int msgno
Number displayed to the user.
Definition: email.h:88
#define MUTT_THREAD_UNREAD
Count unread emails in a thread.
Definition: mutt_thread.h:52
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 1395 of file mutt_thread.c.

1396 {
1397  if (!m || !e)
1398  return 1;
1399 
1400  struct MuttThread *threads[2];
1401  int rc;
1402 
1403  if (((C_Sort & SORT_MASK) != SORT_THREADS) || !e->thread)
1404  return 1;
1405 
1406  threads[0] = e->thread;
1407  while (threads[0]->parent)
1408  threads[0] = threads[0]->parent;
1409 
1410  threads[1] = flag ? e->thread : threads[0]->next;
1411 
1412  for (int i = 0; i < ((flag || !threads[1]) ? 1 : 2); i++)
1413  {
1414  while (!threads[i]->message)
1415  threads[i] = threads[i]->child;
1416  }
1417 
1418  if (C_Sort & SORT_REVERSE)
1419  rc = threads[0]->message->msgno - (threads[1] ? threads[1]->message->msgno : -1);
1420  else
1421  {
1422  rc = (threads[1] ? threads[1]->message->msgno : m->msg_count) -
1423  threads[0]->message->msgno;
1424  }
1425 
1426  if (flag)
1427  rc += 1;
1428 
1429  return rc;
1430 }
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:96
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
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: sort.h:86
Sort by email threads.
Definition: sort.h:56
#define SORT_MASK
Mask for the sort id.
Definition: sort.h:85
int msgno
Number displayed to the user.
Definition: email.h:88

+ Here is the caller graph for this function:

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 1437 of file mutt_thread.c.

1438 {
1439  struct Hash *hash = mutt_hash_new(m->msg_count * 2, MUTT_HASH_NO_FLAGS);
1440 
1441  for (int i = 0; i < m->msg_count; i++)
1442  {
1443  struct Email *e = m->emails[i];
1444  if (e->env->message_id)
1445  mutt_hash_insert(hash, e->env->message_id, e);
1446  }
1447 
1448  return hash;
1449 }
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:39
A Hash Table.
Definition: hash.h:61
char * message_id
Message ID.
Definition: envelope.h:69
struct Envelope * env
Envelope information.
Definition: email.h:91
struct Hash * mutt_hash_new(size_t nelem, HashFlags flags)
Create a new Hash table (with string keys)
Definition: hash.c:276
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:352
#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:

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 1458 of file mutt_thread.c.

1459 {
1460  if (child == parent)
1461  return false;
1462 
1463  mutt_break_thread(child);
1465  mutt_set_flag(m, child, MUTT_TAG, false);
1466 
1467  child->changed = true;
1468  child->env->changed |= MUTT_ENV_CHANGED_IRT;
1469  return true;
1470 }
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:68
bool changed
Email has been edited.
Definition: email.h:50
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:91
void mutt_break_thread(struct Email *e)
Break the email Thread.
Definition: thread.c:225
Tagged messages.
Definition: mutt.h:108
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:

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 1479 of file mutt_thread.c.

1480 {
1481  if (!parent || !children || !m)
1482  return false;
1483 
1484  bool changed = false;
1485 
1486  struct EmailNode *en = NULL;
1487  STAILQ_FOREACH(en, children, entries)
1488  {
1489  changed |= link_threads(parent, en->email, m);
1490  }
1491 
1492  return changed;
1493 }
bool changed
Email has been edited.
Definition: email.h:50
static bool link_threads(struct Email *parent, struct Email *child, struct Mailbox *m)
Forcibly link messages together.
Definition: mutt_thread.c:1458
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:350
struct Email * email
Email in the list.
Definition: email.h:122
List of Emails.
Definition: email.h:120

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

Variable Documentation

bool C_DuplicateThreads

Config: Highlight messages with duplicated message IDs.

Definition at line 48 of file mutt_thread.c.

bool C_HideLimited

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

Definition at line 49 of file mutt_thread.c.

bool C_HideMissing

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

Definition at line 50 of file mutt_thread.c.

bool C_HideThreadSubject

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

Definition at line 51 of file mutt_thread.c.

bool C_HideTopLimited

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

Definition at line 52 of file mutt_thread.c.

bool C_HideTopMissing

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

Definition at line 53 of file mutt_thread.c.

bool C_NarrowTree

Config: Draw a narrower thread tree in the index.

Definition at line 54 of file mutt_thread.c.

bool C_SortRe

Config: Sort method for the sidebar.

Definition at line 55 of file mutt_thread.c.

bool C_StrictThreads

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

Definition at line 56 of file mutt_thread.c.

bool C_ThreadReceived

Config: Sort threaded messages by their received date.

Definition at line 57 of file mutt_thread.c.