NeoMutt  2018-07-16 +2225-8687db
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 "mutt.h"
#include "mutt_thread.h"
#include "context.h"
#include "core/lib.h"
#include "curs_lib.h"
#include "mutt_menu.h"
#include "mx.h"
#include "protos.h"
#include "sort.h"

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

67 {
68  return e->vnum >= 0 || (e->collapsed && (!ctx->pattern || e->limited));
69 }
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:42
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 77 of file mutt_thread.c.

78 {
79  struct MuttThread *tmp = NULL;
80  struct MuttThread *tree = e->thread;
81 
82  /* if the user disabled subject hiding, display it */
84  return true;
85 
86  /* if our subject is different from our parent's, display it */
87  if (e->subject_changed)
88  return true;
89 
90  /* if our subject is different from that of our closest previously displayed
91  * sibling, display the subject */
92  for (tmp = tree->prev; tmp; tmp = tmp->prev)
93  {
94  e = tmp->message;
95  if (e && is_visible(e, ctx))
96  {
97  if (e->subject_changed)
98  return true;
99  else
100  break;
101  }
102  }
103 
104  /* if there is a parent-to-child subject change anywhere between us and our
105  * closest displayed ancestor, display the subject */
106  for (tmp = tree->parent; tmp; tmp = tmp->parent)
107  {
108  e = tmp->message;
109  if (e)
110  {
111  if (is_visible(e, ctx))
112  return false;
113  else if (e->subject_changed)
114  return true;
115  }
116  }
117 
118  /* if we have no visible parent or previous sibling, display the subject */
119  return true;
120 }
static bool is_visible(struct Email *e, struct Context *ctx)
Is the message visible?
Definition: mutt_thread.c:66
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:52
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
static void linearize_tree ( struct Context ctx)
static

Flatten an email thread.

Parameters
ctxMailbox

Definition at line 126 of file mutt_thread.c.

127 {
128  if (!ctx || !ctx->mailbox)
129  return;
130 
131  struct Mailbox *m = ctx->mailbox;
132 
133  struct MuttThread *tree = ctx->tree;
134  struct Email **array = m->emails + ((C_Sort & SORT_REVERSE) ? m->msg_count - 1 : 0);
135 
136  while (tree)
137  {
138  while (!tree->message)
139  tree = tree->child;
140 
141  *array = tree->message;
142  array += (C_Sort & SORT_REVERSE) ? -1 : 1;
143 
144  if (tree->child)
145  tree = tree->child;
146  else
147  {
148  while (tree)
149  {
150  if (tree->next)
151  {
152  tree = tree->next;
153  break;
154  }
155  else
156  tree = tree->parent;
157  }
158  }
159  }
160 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:111
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
int msg_count
Total number of messages.
Definition: mailbox.h:103
The envelope/body of an email.
Definition: email.h:37
struct MuttThread * tree
top of thread tree
Definition: context.h:45
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
struct Mailbox * mailbox
Definition: context.h:53
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:59
struct MuttThread * child
Child of this Thread.
Definition: thread.h:46
A mailbox.
Definition: mailbox.h:93
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
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 174 of file mutt_thread.c.

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

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

413 {
414  struct MuttThread *start = cur;
415  struct Envelope *env = NULL;
416  time_t thisdate;
417  int rc = 0;
418 
419  while (true)
420  {
421  while (!cur->message)
422  cur = cur->child;
423 
424  if (dateptr)
425  {
426  thisdate = C_ThreadReceived ? cur->message->received : cur->message->date_sent;
427  if (!*dateptr || (thisdate < *dateptr))
428  *dateptr = thisdate;
429  }
430 
431  env = cur->message->env;
432  if (env->real_subj && ((env->real_subj != env->subject) || (!C_SortRe)))
433  {
434  struct ListNode *np = NULL;
435  STAILQ_FOREACH(np, subjects, entries)
436  {
437  rc = mutt_str_strcmp(env->real_subj, np->data);
438  if (rc >= 0)
439  break;
440  }
441  if (!np)
442  mutt_list_insert_head(subjects, env->real_subj);
443  else if (rc > 0)
444  mutt_list_insert_after(subjects, np, env->real_subj);
445  }
446 
447  while (!cur->next && (cur != start))
448  {
449  cur = cur->parent;
450  }
451  if (cur == start)
452  break;
453  cur = cur->next;
454  }
455 }
struct MuttThread * next
Next sibling Thread.
Definition: thread.h:47
char * real_subj
Offset of the real subject.
Definition: envelope.h:53
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
bool C_SortRe
Config: Sort method for the sidebar.
Definition: mutt_thread.c:56
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
Definition: list.h:35
char * subject
Email&#39;s subject.
Definition: envelope.h:52
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:58
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:82
The header of an Email.
Definition: envelope.h:40
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 466 of file mutt_thread.c.

467 {
468  if (!m)
469  return NULL;
470 
471  struct HashElem *ptr = NULL;
472  struct MuttThread *tmp = NULL, *last = NULL;
473  struct ListHead subjects = STAILQ_HEAD_INITIALIZER(subjects);
474  time_t date = 0;
475 
476  make_subject_list(&subjects, cur, &date);
477 
478  struct ListNode *np = NULL;
479  STAILQ_FOREACH(np, &subjects, entries)
480  {
481  for (ptr = mutt_hash_find_bucket(m->subj_hash, np->data); ptr; ptr = ptr->next)
482  {
483  tmp = ((struct Email *) ptr->data)->thread;
484  if ((tmp != cur) && /* don't match the same message */
485  !tmp->fake_thread && /* don't match pseudo threads */
486  tmp->message->subject_changed && /* only match interesting replies */
487  !is_descendant(tmp, cur) && /* don't match in the same thread */
488  (date >= (C_ThreadReceived ? tmp->message->received : tmp->message->date_sent)) &&
489  (!last || (C_ThreadReceived ?
490  (last->message->received < tmp->message->received) :
491  (last->message->date_sent < tmp->message->date_sent))) &&
492  tmp->message->env->real_subj &&
493  (mutt_str_strcmp(np->data, tmp->message->env->real_subj) == 0))
494  {
495  last = tmp; /* best match so far */
496  }
497  }
498  }
499 
500  mutt_list_clear(&subjects);
501  return last;
502 }
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:37
char * real_subj
Offset of the real subject.
Definition: envelope.h:53
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
Definition: list.h:35
struct Hash * subj_hash
Hash table by subject.
Definition: mailbox.h:140
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:412
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:58
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:82
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 509 of file mutt_thread.c.

510 {
511  if (!m)
512  return NULL;
513 
514  struct Hash *hash = mutt_hash_new(m->msg_count * 2, MUTT_HASH_ALLOW_DUPS);
515 
516  for (int i = 0; i < m->msg_count; i++)
517  {
518  struct Email *e = m->emails[i];
519  if (e->env->real_subj)
520  mutt_hash_insert(hash, e->env->real_subj, e);
521  }
522 
523  return hash;
524 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:111
int msg_count
Total number of messages.
Definition: mailbox.h:103
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:53
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: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
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 532 of file mutt_thread.c.

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

Clear the threading of message in a mailbox.

Parameters
ctxMailbox

Definition at line 603 of file mutt_thread.c.

604 {
605  if (!ctx || !ctx->mailbox || !ctx->mailbox->emails)
606  return;
607 
608  struct Mailbox *m = ctx->mailbox;
609 
610  for (int i = 0; i < m->msg_count; i++)
611  {
612  /* mailbox may have been only partially read */
613  if (m->emails[i])
614  {
615  m->emails[i]->thread = NULL;
616  m->emails[i]->threaded = false;
617  }
618  }
619  ctx->tree = NULL;
620 
622 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:111
int msg_count
Total number of messages.
Definition: mailbox.h:103
struct MuttThread * tree
top of thread tree
Definition: context.h:45
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:53
struct Hash * thread_hash
hash table for threading
Definition: context.h:46
A mailbox.
Definition: mailbox.h:93
void mutt_hash_free(struct Hash **ptr)
elem_free a hash table
Definition: hash.c:472
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 632 of file mutt_thread.c.

633 {
634  static sort_t *sort_func = NULL;
635 
636  if (a && b)
637  {
638  return (*sort_func)(&(*((struct MuttThread const *const *) a))->sort_key,
639  &(*((struct MuttThread const *const *) b))->sort_key);
640  }
641  /* a hack to let us reset sort_func even though we can't
642  * have extra arguments because of qsort */
643  else
644  {
645  sort_func = mutt_get_sort_func(C_Sort & SORT_MASK);
646  return sort_func ? 1 : 0;
647  }
648 }
int sort_t(const void *a, const void *b)
typedef sort_t - Prototype for a function to compare two emails
Definition: sort.h:49
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:59
sort_t * mutt_get_sort_func(enum SortType method)
Get the sort function for a given sort id.
Definition: sort.c:323
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
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 656 of file mutt_thread.c.

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

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

Sort email threads.

Parameters
ctxMailbox
initIf true, rebuild the thread

Definition at line 834 of file mutt_thread.c.

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

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

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

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

Parameters
ctxMailbox

Definition at line 1187 of file mutt_thread.c.

1188 {
1189  if (!ctx || !ctx->mailbox)
1190  return;
1191 
1192  struct Mailbox *m = ctx->mailbox;
1193 
1194  struct Email *e = NULL;
1195 
1196  m->vcount = 0;
1197  ctx->vsize = 0;
1198  int padding = mx_msg_padding_size(m);
1199 
1200  for (int i = 0; i < m->msg_count; i++)
1201  {
1202  e = m->emails[i];
1203  if (e->vnum >= 0)
1204  {
1205  e->vnum = m->vcount;
1206  m->v2r[m->vcount] = i;
1207  m->vcount++;
1208  ctx->vsize += e->content->length + e->content->offset - e->content->hdr_offset + padding;
1209  e->num_hidden = mutt_get_hidden(ctx, e);
1210  }
1211  }
1212 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:111
int msg_count
Total number of messages.
Definition: mailbox.h:103
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:58
LOFF_T offset
offset where the actual data begins
Definition: body.h:44
int vcount
The number of virtual messages.
Definition: mailbox.h:114
struct Mailbox * mailbox
Definition: context.h:53
off_t vsize
Definition: context.h:41
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
A mailbox.
Definition: mailbox.h:93
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:113
int mx_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Wrapper for MxOps::msg_padding_size.
Definition: mx.c:1467
long hdr_offset
Offset in stream where the headers begin.
Definition: body.h:42
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 1221 of file mutt_thread.c.

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

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

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

1460 {
1461  if (child == parent)
1462  return false;
1463 
1464  mutt_break_thread(child);
1466  mutt_set_flag(m, child, MUTT_TAG, false);
1467 
1468  child->changed = true;
1469  child->env->changed |= MUTT_ENV_CHANGED_IRT;
1470  return true;
1471 }
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:68
bool changed
Email has been edited.
Definition: email.h:48
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:71
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:68
char * message_id
Message ID.
Definition: envelope.h:55
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
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 1480 of file mutt_thread.c.

1481 {
1482  if (!parent || !children || !m)
1483  return false;
1484 
1485  bool changed = false;
1486 
1487  struct EmailNode *en = NULL;
1488  STAILQ_FOREACH(en, children, entries)
1489  {
1490  changed |= link_threads(parent, en->email, m);
1491  }
1492 
1493  return changed;
1494 }
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:1459
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:350
struct Email * email
Email in the list.
Definition: email.h:119
List of Emails.
Definition: email.h:117

Variable Documentation

bool C_DuplicateThreads

Config: Highlight messages with duplicated message IDs.

Definition at line 49 of file mutt_thread.c.

bool C_HideLimited

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

Definition at line 50 of file mutt_thread.c.

bool C_HideMissing

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

Definition at line 51 of file mutt_thread.c.

bool C_HideThreadSubject

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

Definition at line 52 of file mutt_thread.c.

bool C_HideTopLimited

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

Definition at line 53 of file mutt_thread.c.

bool C_HideTopMissing

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

Definition at line 54 of file mutt_thread.c.

bool C_NarrowTree

Config: Draw a narrower thread tree in the index.

Definition at line 55 of file mutt_thread.c.

bool C_SortRe

Config: Sort method for the sidebar.

Definition at line 56 of file mutt_thread.c.

bool C_StrictThreads

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

Definition at line 57 of file mutt_thread.c.

bool C_ThreadReceived

Config: Sort threaded messages by their received date.

Definition at line 58 of file mutt_thread.c.