NeoMutt  2018-07-16 +952-a2da0a
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 "curs_lib.h"
#include "mailbox.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 || cur->limited)
 

Functions

static bool is_visible (struct Email *e, struct Context *ctx)
 Is the message visible? More...
 
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_virtual (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 *cur, int 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 *cur, struct Email *last, struct Context *ctx)
 Forcibly link threads together. More...
 

Variables

bool DuplicateThreads
 Config: Highlight messages with duplicated message IDs. More...
 
bool HideLimited
 Config: Don't indicate hidden messages, in the thread tree. More...
 
bool HideMissing
 Config: Don't indicate missing messages, in the thread tree. More...
 
bool HideThreadSubject
 Config: Hide subjects that are similar to that of the parent message. More...
 
bool HideTopLimited
 Config: Don't indicate hidden top message, in the thread tree. More...
 
bool HideTopMissing
 Config: Don't indicate missing top message, in the thread tree. More...
 
bool NarrowTree
 Config: Draw a narrower thread tree in the index. More...
 
bool SortRe
 Config: Sort method for the sidebar. More...
 
bool StrictThreads
 Config: Thread messages using 'In-Reply-To' and 'References' headers. More...
 
bool 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 || 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->virtual >= 0 || (e->collapsed && (!ctx->pattern || e->limited));
68 }
int virtual
virtual message number
Definition: email.h:89
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
char * pattern
limit pattern string
Definition: context.h:39

+ Here is the caller graph for this function:

bool need_display_subject ( struct Context ctx,
struct Email e 
)

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

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

125 {
126  if (!ctx || !ctx->mailbox)
127  return;
128 
129  struct Mailbox *m = ctx->mailbox;
130 
131  struct MuttThread *tree = ctx->tree;
132  struct Email **array = m->hdrs + ((Sort & SORT_REVERSE) ? m->msg_count - 1 : 0);
133 
134  while (tree)
135  {
136  while (!tree->message)
137  tree = tree->child;
138 
139  *array = tree->message;
140  array += (Sort & SORT_REVERSE) ? -1 : 1;
141 
142  if (tree->child)
143  tree = tree->child;
144  else
145  {
146  while (tree)
147  {
148  if (tree->next)
149  {
150  tree = tree->next;
151  break;
152  }
153  else
154  tree = tree->parent;
155  }
156  }
157  }
158 }
struct MuttThread * next
Definition: thread.h:46
int msg_count
total number of messages
Definition: mailbox.h:86
The envelope/body of an email.
Definition: email.h:35
struct MuttThread * tree
top of thread tree
Definition: context.h:42
struct MuttThread * parent
Definition: thread.h:44
struct Email ** hdrs
Definition: mailbox.h:93
struct Mailbox * mailbox
Definition: context.h:50
struct MuttThread * child
Definition: thread.h:45
A mailbox.
Definition: mailbox.h:76
struct Email * message
Definition: thread.h:48
An email conversation.
Definition: thread.h:34
WHERE short Sort
Config: Sort method for the index.
Definition: sort.h:58
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort.h:78

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

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

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

287 {
288  char *pfx = NULL, *mypfx = NULL, *arrow = NULL, *myarrow = NULL, *new_tree = NULL;
290  char vtee = (Sort & SORT_REVERSE) ? MUTT_TREE_BTEE : MUTT_TREE_TTEE;
291  int depth = 0, start_depth = 0, max_depth = 0, width = NarrowTree ? 1 : 2;
292  struct MuttThread *nextdisp = NULL, *pseudo = NULL, *parent = NULL, *tree = ctx->tree;
293 
294  /* Do the visibility calculations and free the old thread chars.
295  * From now on we can simply ignore invisible subtrees
296  */
297  calculate_visibility(ctx, &max_depth);
298  pfx = mutt_mem_malloc(width * max_depth + 2);
299  arrow = mutt_mem_malloc(width * max_depth + 2);
300  while (tree)
301  {
302  if (depth)
303  {
304  myarrow = arrow + (depth - start_depth - (start_depth ? 0 : 1)) * width;
305  if (depth && start_depth == depth)
306  myarrow[0] = nextdisp ? MUTT_TREE_LTEE : corner;
307  else if (parent->message && !HideLimited)
308  myarrow[0] = MUTT_TREE_HIDDEN;
309  else if (!parent->message && !HideMissing)
310  myarrow[0] = MUTT_TREE_MISSING;
311  else
312  myarrow[0] = vtee;
313  if (width == 2)
314  {
315  myarrow[1] = pseudo ? MUTT_TREE_STAR :
316  (tree->duplicate_thread ? MUTT_TREE_EQUALS : MUTT_TREE_HLINE);
317  }
318  if (tree->visible)
319  {
320  myarrow[width] = MUTT_TREE_RARROW;
321  myarrow[width + 1] = 0;
322  new_tree = mutt_mem_malloc((2 + depth * width));
323  if (start_depth > 1)
324  {
325  strncpy(new_tree, pfx, (start_depth - 1) * width);
326  mutt_str_strfcpy(new_tree + (start_depth - 1) * width, arrow,
327  (1 + depth - start_depth) * width + 2);
328  }
329  else
330  mutt_str_strfcpy(new_tree, arrow, 2 + depth * width);
331  tree->message->tree = new_tree;
332  }
333  }
334  if (tree->child && depth)
335  {
336  mypfx = pfx + (depth - 1) * width;
337  mypfx[0] = nextdisp ? MUTT_TREE_VLINE : MUTT_TREE_SPACE;
338  if (width == 2)
339  mypfx[1] = MUTT_TREE_SPACE;
340  }
341  parent = tree;
342  nextdisp = NULL;
343  pseudo = NULL;
344  do
345  {
346  if (tree->child && tree->subtree_visible)
347  {
348  if (tree->deep)
349  depth++;
350  if (tree->visible)
351  start_depth = depth;
352  tree = tree->child;
353 
354  /* we do this here because we need to make sure that the first child thread
355  * of the old tree that we deal with is actually displayed if any are,
356  * or we might set the parent variable wrong while going through it. */
357  while (!tree->subtree_visible && tree->next)
358  tree = tree->next;
359  }
360  else
361  {
362  while (!tree->next && tree->parent)
363  {
364  if (tree == pseudo)
365  pseudo = NULL;
366  if (tree == nextdisp)
367  nextdisp = NULL;
368  if (tree->visible)
369  start_depth = depth;
370  tree = tree->parent;
371  if (tree->deep)
372  {
373  if (start_depth == depth)
374  start_depth--;
375  depth--;
376  }
377  }
378  if (tree == pseudo)
379  pseudo = NULL;
380  if (tree == nextdisp)
381  nextdisp = NULL;
382  if (tree->visible)
383  start_depth = depth;
384  tree = tree->next;
385  if (!tree)
386  break;
387  }
388  if (!pseudo && tree->fake_thread)
389  pseudo = tree;
390  if (!nextdisp && tree->next_subtree_visible)
391  nextdisp = tree;
392  } while (!tree->deep);
393  }
394 
395  FREE(&pfx);
396  FREE(&arrow);
397 }
struct MuttThread * next
Definition: thread.h:46
#define MUTT_TREE_ULCORNER
Definition: mutt.h:84
struct MuttThread * tree
top of thread tree
Definition: context.h:42
bool HideMissing
Config: Don&#39;t indicate missing messages, in the thread tree.
Definition: mutt_thread.c:50
static void calculate_visibility(struct Context *ctx, int *max_depth)
Are tree nodes visible.
Definition: mutt_thread.c:172
struct MuttThread * parent
Definition: thread.h:44
bool next_subtree_visible
Definition: thread.h:43
bool NarrowTree
Config: Draw a narrower thread tree in the index.
Definition: mutt_thread.c:54
#define MUTT_TREE_LTEE
Definition: mutt.h:85
struct MuttThread * child
Definition: thread.h:45
#define MUTT_TREE_TTEE
Definition: mutt.h:93
#define MUTT_TREE_SPACE
Definition: mutt.h:88
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:99
#define MUTT_TREE_HLINE
Definition: mutt.h:86
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:742
#define MUTT_TREE_LLCORNER
Definition: mutt.h:83
#define MUTT_TREE_VLINE
Definition: mutt.h:87
#define MUTT_TREE_HIDDEN
Definition: mutt.h:91
An email conversation.
Definition: thread.h:34
#define MUTT_TREE_MISSING
Definition: mutt.h:95
bool HideLimited
Config: Don&#39;t indicate hidden messages, in the thread tree.
Definition: mutt_thread.c:49
WHERE short Sort
Config: Sort method for the index.
Definition: sort.h:58
#define MUTT_TREE_EQUALS
Definition: mutt.h:92
#define MUTT_TREE_STAR
Definition: mutt.h:90
#define FREE(x)
Definition: memory.h:46
#define MUTT_TREE_BTEE
Definition: mutt.h:94
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort.h:78
#define MUTT_TREE_RARROW
Definition: mutt.h:89

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

410 {
411  struct MuttThread *start = cur;
412  struct Envelope *env = NULL;
413  time_t thisdate;
414  int rc = 0;
415 
416  while (true)
417  {
418  while (!cur->message)
419  cur = cur->child;
420 
421  if (dateptr)
422  {
423  thisdate = ThreadReceived ? cur->message->received : cur->message->date_sent;
424  if (!*dateptr || thisdate < *dateptr)
425  *dateptr = thisdate;
426  }
427 
428  env = cur->message->env;
429  if (env->real_subj && ((env->real_subj != env->subject) || (!SortRe)))
430  {
431  struct ListNode *np = NULL;
432  STAILQ_FOREACH(np, subjects, entries)
433  {
434  rc = mutt_str_strcmp(env->real_subj, np->data);
435  if (rc >= 0)
436  break;
437  }
438  if (!np)
439  mutt_list_insert_head(subjects, env->real_subj);
440  else if (rc > 0)
441  mutt_list_insert_after(subjects, np, env->real_subj);
442  }
443 
444  while (!cur->next && cur != start)
445  {
446  cur = cur->parent;
447  }
448  if (cur == start)
449  break;
450  cur = cur->next;
451  }
452 }
struct MuttThread * next
Definition: thread.h:46
char * real_subj
offset of the real subject
Definition: envelope.h:45
struct MuttThread * parent
Definition: thread.h:44
bool ThreadReceived
Config: Sort threaded messages by their received date.
Definition: mutt_thread.c:57
bool 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
Definition: thread.h:45
struct ListNode * mutt_list_insert_after(struct ListHead *h, struct ListNode *n, char *s)
Insert a string after a given ListNode.
Definition: list.c:73
struct ListNode * mutt_list_insert_head(struct ListHead *h, char *s)
Insert a string at the beginning of a List.
Definition: list.c:44
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:350
struct Email * message
Definition: thread.h:48
An email conversation.
Definition: thread.h:34
char * data
Definition: list.h:35
char * subject
Definition: envelope.h:44
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:612
time_t received
time when the message was placed in the mailbox
Definition: email.h:84
The header of an email.
Definition: envelope.h:32

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

464 {
465  if (!m)
466  return NULL;
467 
468  struct HashElem *ptr = NULL;
469  struct MuttThread *tmp = NULL, *last = NULL;
470  struct ListHead subjects = STAILQ_HEAD_INITIALIZER(subjects);
471  time_t date = 0;
472 
473  make_subject_list(&subjects, cur, &date);
474 
475  struct ListNode *np = NULL;
476  STAILQ_FOREACH(np, &subjects, entries)
477  {
478  for (ptr = mutt_hash_find_bucket(m->subj_hash, np->data); ptr; ptr = ptr->next)
479  {
480  tmp = ((struct Email *) ptr->data)->thread;
481  if (tmp != cur && /* don't match the same message */
482  !tmp->fake_thread && /* don't match pseudo threads */
483  tmp->message->subject_changed && /* only match interesting replies */
484  !is_descendant(tmp, cur) && /* don't match in the same thread */
485  (date >= (ThreadReceived ? tmp->message->received : tmp->message->date_sent)) &&
486  (!last || (ThreadReceived ? (last->message->received < tmp->message->received) :
487  (last->message->date_sent < tmp->message->date_sent))) &&
488  tmp->message->env->real_subj &&
489  (mutt_str_strcmp(np->data, tmp->message->env->real_subj) == 0))
490  {
491  last = tmp; /* best match so far */
492  }
493  }
494  }
495 
496  mutt_list_clear(&subjects);
497  return last;
498 }
struct HashElem * mutt_hash_find_bucket(const struct Hash *table, const char *strkey)
Find the HashElem in a Hash table element using a key.
Definition: hash.c:415
The envelope/body of an email.
Definition: email.h:35
char * real_subj
offset of the real subject
Definition: envelope.h:45
bool ThreadReceived
Config: Sort threaded messages by their received date.
Definition: mutt_thread.c:57
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
Definition: thread.h:36
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:350
struct Email * message
Definition: thread.h:48
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:122
bool subject_changed
used for threading
Definition: email.h:53
#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:409
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:145
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:612
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 505 of file mutt_thread.c.

506 {
507  if (!m)
508  return NULL;
509 
511 
512  for (int i = 0; i < m->msg_count; i++)
513  {
514  struct Email *e = m->hdrs[i];
515  if (e->env->real_subj)
516  mutt_hash_insert(hash, e->env->real_subj, e);
517  }
518 
519  return hash;
520 }
struct Hash * mutt_hash_new(size_t nelem, int flags)
Create a new Hash table (with string keys)
Definition: hash.c:278
int msg_count
total number of messages
Definition: mailbox.h:86
The envelope/body of an email.
Definition: email.h:35
A Hash Table.
Definition: hash.h:61
const char * hash
Definition: md5.c:10
char * real_subj
offset of the real subject
Definition: envelope.h:45
struct Email ** hdrs
Definition: mailbox.h:93
struct Envelope * env
envelope information
Definition: email.h:91
#define MUTT_HASH_ALLOW_DUPS
allow duplicate keys to be inserted
Definition: hash.h:76
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:349

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

529 {
530  if (!ctx || !ctx->mailbox)
531  return;
532 
533  struct Mailbox *m = ctx->mailbox;
534 
535  struct MuttThread *tree = ctx->tree, *top = tree;
536  struct MuttThread *tmp = NULL, *cur = NULL, *parent = NULL, *curchild = NULL,
537  *nextchild = NULL;
538 
539  if (!m->subj_hash)
540  m->subj_hash = make_subj_hash(ctx->mailbox);
541 
542  while (tree)
543  {
544  cur = tree;
545  tree = tree->next;
546  parent = find_subject(ctx->mailbox, cur);
547  if (parent)
548  {
549  cur->fake_thread = true;
550  unlink_message(&top, cur);
551  insert_message(&parent->child, parent, cur);
552  parent->sort_children = true;
553  tmp = cur;
554  while (true)
555  {
556  while (!tmp->message)
557  tmp = tmp->child;
558 
559  /* if the message we're attaching has pseudo-children, they
560  * need to be attached to its parent, so move them up a level.
561  * but only do this if they have the same real subject as the
562  * parent, since otherwise they rightly belong to the message
563  * we're attaching. */
564  if (tmp == cur || (mutt_str_strcmp(tmp->message->env->real_subj,
565  parent->message->env->real_subj) == 0))
566  {
567  tmp->message->subject_changed = false;
568 
569  for (curchild = tmp->child; curchild;)
570  {
571  nextchild = curchild->next;
572  if (curchild->fake_thread)
573  {
574  unlink_message(&tmp->child, curchild);
575  insert_message(&parent->child, parent, curchild);
576  }
577  curchild = nextchild;
578  }
579  }
580 
581  while (!tmp->next && tmp != cur)
582  {
583  tmp = tmp->parent;
584  }
585  if (tmp == cur)
586  break;
587  tmp = tmp->next;
588  }
589  }
590  }
591  ctx->tree = top;
592 }
struct MuttThread * next
Definition: thread.h:46
struct MuttThread * tree
top of thread tree
Definition: context.h:42
char * real_subj
offset of the real subject
Definition: envelope.h:45
struct MuttThread * parent
Definition: thread.h:44
void insert_message(struct MuttThread **new, struct MuttThread *newparent, struct MuttThread *cur)
Insert a message into a thread.
Definition: thread.c:91
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:463
struct Envelope * env
envelope information
Definition: email.h:91
struct MuttThread * child
Definition: thread.h:45
A mailbox.
Definition: mailbox.h:76
struct Email * message
Definition: thread.h:48
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:122
bool subject_changed
used for threading
Definition: email.h:53
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:612
static struct Hash * make_subj_hash(struct Mailbox *m)
Create a Hash Table for the email subjects.
Definition: mutt_thread.c:505

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void mutt_clear_threads ( struct Context ctx)

Clear the threading of message in a mailbox.

Parameters
ctxMailbox

Definition at line 598 of file mutt_thread.c.

599 {
600  if (!ctx || !ctx->mailbox || !ctx->mailbox->hdrs)
601  return;
602 
603  struct Mailbox *m = ctx->mailbox;
604 
605  for (int i = 0; i < m->msg_count; i++)
606  {
607  /* mailbox may have been only partially read */
608  if (m->hdrs[i])
609  {
610  m->hdrs[i]->thread = NULL;
611  m->hdrs[i]->threaded = false;
612  }
613  }
614  ctx->tree = NULL;
615 
616  if (ctx->thread_hash)
618 }
int msg_count
total number of messages
Definition: mailbox.h:86
struct MuttThread * tree
top of thread tree
Definition: context.h:42
struct MuttThread * thread
Definition: email.h:96
bool threaded
used for threading
Definition: email.h:54
void mutt_hash_destroy(struct Hash **ptr)
Destroy a hash table.
Definition: hash.c:458
struct Email ** hdrs
Definition: mailbox.h:93
struct Mailbox * mailbox
Definition: context.h:50
struct Hash * thread_hash
hash table for threading
Definition: context.h:43
A mailbox.
Definition: mailbox.h:76

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

629 {
630  static sort_t *sort_func = NULL;
631 
632  if (a && b)
633  {
634  return (*sort_func)(&(*((struct MuttThread **) a))->sort_key,
635  &(*((struct MuttThread **) b))->sort_key);
636  }
637  /* a hack to let us reset sort_func even though we can't
638  * have extra arguments because of qsort
639  */
640  else
641  {
642  sort_func = mutt_get_sort_func(Sort);
643  return sort_func ? 1 : 0;
644  }
645 }
int sort_t(const void *a, const void *b)
typedef sort_t - Prototype for a function to compare two emails
Definition: sort.h:48
struct Email * sort_key
Definition: thread.h:49
An email conversation.
Definition: thread.h:34
WHERE short Sort
Config: Sort method for the index.
Definition: sort.h:58
sort_t * mutt_get_sort_func(int method)
Get the sort function for a given sort id.
Definition: sort.c:320

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

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

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

790 {
791  if (!m)
792  return;
793 
794  struct Email *cur = NULL;
795  struct MuttThread *tmp = NULL;
796  for (int i = 0; i < m->msg_count; i++)
797  {
798  cur = m->hdrs[i];
799  if (cur->thread->check_subject)
800  cur->thread->check_subject = false;
801  else if (!init)
802  continue;
803 
804  /* figure out which messages have subjects different than their parents' */
805  tmp = cur->thread->parent;
806  while (tmp && !tmp->message)
807  {
808  tmp = tmp->parent;
809  }
810 
811  if (!tmp)
812  cur->subject_changed = true;
813  else if (cur->env->real_subj && tmp->message->env->real_subj)
814  {
815  cur->subject_changed =
816  (mutt_str_strcmp(cur->env->real_subj, tmp->message->env->real_subj) != 0) ? true : false;
817  }
818  else
819  {
820  cur->subject_changed =
821  (cur->env->real_subj || tmp->message->env->real_subj) ? true : false;
822  }
823  }
824 }
int msg_count
total number of messages
Definition: mailbox.h:86
The envelope/body of an email.
Definition: email.h:35
struct MuttThread * thread
Definition: email.h:96
char * real_subj
offset of the real subject
Definition: envelope.h:45
struct MuttThread * parent
Definition: thread.h:44
struct Email ** hdrs
Definition: mailbox.h:93
struct Envelope * env
envelope information
Definition: email.h:91
struct Email * message
Definition: thread.h:48
An email conversation.
Definition: thread.h:34
bool subject_changed
used for threading
Definition: email.h:53
bool check_subject
Definition: thread.h:39
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:612

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

832 {
833  if (!ctx || !ctx->mailbox)
834  return;
835 
836  struct Mailbox *m = ctx->mailbox;
837 
838  struct Email *cur = NULL;
839  int i, oldsort, using_refs = 0;
840  struct MuttThread *thread = NULL, *new = NULL, *tmp = NULL, top;
841  memset(&top, 0, sizeof(top));
842  struct ListNode *ref = NULL;
843 
844  /* set Sort to the secondary method to support the set sort_aux=reverse-*
845  * settings. The sorting functions just look at the value of
846  * SORT_REVERSE
847  */
848  oldsort = Sort;
849  Sort = 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  cur = m->hdrs[i];
878 
879  if (!cur->thread)
880  {
881  if ((!init || DuplicateThreads) && cur->env->message_id)
882  thread = mutt_hash_find(ctx->thread_hash, cur->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 = cur;
890  cur->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  new = (DuplicateThreads ? thread : NULL);
926 
927  thread = mutt_mem_calloc(1, sizeof(struct MuttThread));
928  thread->message = cur;
929  thread->check_subject = true;
930  cur->thread = thread;
932  cur->env->message_id ? cur->env->message_id : "", thread);
933 
934  if (new)
935  {
936  if (new->duplicate_thread)
937  new = new->parent;
938 
939  thread = cur->thread;
940 
941  insert_message(&new->child, new, 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 = cur->thread;
952  for (new = thread->child; new;)
953  {
954  tmp = new->next;
955  if (new->fake_thread)
956  {
957  unlink_message(&thread->child, new);
958  insert_message(&top.child, &top, new);
959  new->fake_thread = false;
960  }
961  new = tmp;
962  }
963  }
964  }
965 
966  /* thread by references */
967  for (i = 0; i < m->msg_count; i++)
968  {
969  cur = m->hdrs[i];
970  if (cur->threaded)
971  continue;
972  cur->threaded = true;
973 
974  thread = cur->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(&cur->env->in_reply_to);
983  if (ref)
984  using_refs = 1;
985  else
986  {
987  ref = STAILQ_FIRST(&cur->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  */
999  if (STAILQ_EMPTY(&cur->env->references))
1000  ref = STAILQ_NEXT(ref, entries);
1001  else
1002  {
1003  if (mutt_str_strcmp(ref->data, STAILQ_FIRST(&cur->env->references)->data) != 0)
1004  ref = STAILQ_FIRST(&cur->env->references);
1005  else
1006  ref = STAILQ_NEXT(STAILQ_FIRST(&cur->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  new = mutt_hash_find(ctx->thread_hash, ref->data);
1018  if (!new)
1019  {
1020  new = mutt_mem_calloc(1, sizeof(struct MuttThread));
1021  mutt_hash_insert(ctx->thread_hash, ref->data, new);
1022  }
1023  else
1024  {
1025  if (new->duplicate_thread)
1026  new = new->parent;
1027  if (is_descendant(new, thread)) /* no loops! */
1028  continue;
1029  }
1030 
1031  if (thread->parent)
1032  unlink_message(&top.child, thread);
1033  insert_message(&new->child, new, thread);
1034  thread = new;
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 (!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  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 Hash * mutt_hash_new(size_t nelem, int flags)
Create a new Hash table (with string keys)
Definition: hash.c:278
struct MuttThread * next
Definition: thread.h:46
bool StrictThreads
Config: Thread messages using &#39;In-Reply-To&#39; and &#39;References&#39; headers.
Definition: mutt_thread.c:56
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:51
int msg_count
total number of messages
Definition: mailbox.h:86
void thread_hash_destructor(int type, void *obj, intptr_t data)
Hash Destructor callback - Implements hash_destructor_t.
Definition: thread.c:106
The envelope/body of an email.
Definition: email.h:35
struct MuttThread * tree
top of thread tree
Definition: context.h:42
struct MuttThread * thread
Definition: email.h:96
static void linearize_tree(struct Context *ctx)
Flatten an email thread.
Definition: mutt_thread.c:124
struct MuttThread * mutt_sort_subthreads(struct MuttThread *thread, bool init)
Sort the children of a thread.
Definition: mutt_thread.c:653
void mutt_hash_set_destructor(struct Hash *table, hash_destructor_t fn, intptr_t fn_data)
Set the destructor for a Hash Table.
Definition: hash.c:320
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:374
bool threaded
used for threading
Definition: email.h:54
struct MuttThread * parent
Definition: thread.h:44
struct Email ** hdrs
Definition: mailbox.h:93
void insert_message(struct MuttThread **new, struct MuttThread *newparent, struct MuttThread *cur)
Insert a message into a thread.
Definition: thread.c:91
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:60
char * message_id
Definition: envelope.h:47
struct Mailbox * mailbox
Definition: context.h:50
static void pseudo_threads(struct Context *ctx)
Thread messages by subject.
Definition: mutt_thread.c:528
struct Envelope * env
envelope information
Definition: email.h:91
struct MuttThread * child
Definition: thread.h:45
struct Hash * thread_hash
hash table for threading
Definition: context.h:43
bool duplicate_thread
Definition: thread.h:37
#define MUTT_HASH_ALLOW_DUPS
allow duplicate keys to be inserted
Definition: hash.h:76
struct Email * sort_key
Definition: thread.h:49
bool fake_thread
Definition: thread.h:36
A mailbox.
Definition: mailbox.h:76
#define STAILQ_NEXT(elm, field)
Definition: queue.h:398
struct Email * message
Definition: thread.h:48
void unlink_message(struct MuttThread **old, struct MuttThread *cur)
Break the message out of the thread.
Definition: thread.c:64
bool DuplicateThreads
Config: Highlight messages with duplicated message IDs.
Definition: mutt_thread.c:48
An email conversation.
Definition: thread.h:34
char * data
Definition: list.h:35
WHERE short Sort
Config: Sort method for the index.
Definition: sort.h:58
#define STAILQ_EMPTY(head)
Definition: queue.h:346
bool check_subject
Definition: thread.h:39
static void check_subjects(struct Mailbox *m, bool init)
Find out which emails&#39; subjects differ from their parent&#39;s.
Definition: mutt_thread.c:789
bool is_descendant(struct MuttThread *a, struct MuttThread *b)
Is one thread a descendant of another.
Definition: thread.c:44
struct HashElem * mutt_hash_insert(struct Hash *table, const char *strkey, void *data)
Add a new element to the Hash table (with string keys)
Definition: hash.c:349
struct ListHead references
message references (in reverse order)
Definition: envelope.h:59
A List node for strings.
Definition: list.h:33
WHERE short SortAux
Config: Secondary sort method for the index.
Definition: sort.h:59
#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:612
void mutt_draw_tree(struct Context *ctx)
Draw a tree of threaded emails.
Definition: mutt_thread.c:286

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

1078 {
1079  struct MuttThread *cur = NULL;
1080  struct Email *tmp = NULL;
1081 
1082  if ((Sort & SORT_MASK) != SORT_THREADS)
1083  {
1084  mutt_error(_("Threading is not enabled"));
1085  return e->virtual;
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 ^ ((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 ^ ((Sort & SORT_REVERSE) != 0))
1110  {
1111  do
1112  {
1113  cur = cur->next;
1114  if (!cur)
1115  return -1;
1116  tmp = find_virtual(cur, 0);
1117  } while (!tmp);
1118  }
1119  else
1120  {
1121  do
1122  {
1123  cur = cur->prev;
1124  if (!cur)
1125  return -1;
1126  tmp = find_virtual(cur, 1);
1127  } while (!tmp);
1128  }
1129 
1130  return tmp->virtual;
1131 }
struct MuttThread * next
Definition: thread.h:46
The envelope/body of an email.
Definition: email.h:35
struct Email * find_virtual(struct MuttThread *cur, int reverse)
Find an email with a Virtual message number.
Definition: thread.c:117
struct MuttThread * thread
Definition: email.h:96
int virtual
virtual message number
Definition: email.h:89
#define _(a)
Definition: message.h:28
struct MuttThread * parent
Definition: thread.h:44
struct MuttThread * prev
Definition: thread.h:47
#define SORT_THREADS
Sort by email threads.
Definition: sort.h:51
An email conversation.
Definition: thread.h:34
WHERE short Sort
Config: Sort method for the index.
Definition: sort.h:58
#define mutt_error(...)
Definition: logging.h:88
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort.h:78
#define SORT_MASK
Mask for the sort id.
Definition: sort.h:77

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

1142 {
1143  struct MuttThread *thread = NULL;
1144  struct Email *parent = NULL;
1145 
1146  if ((Sort & SORT_MASK) != SORT_THREADS)
1147  {
1148  mutt_error(_("Threading is not enabled"));
1149  return e->virtual;
1150  }
1151 
1152  /* Root may be the current message */
1153  if (find_root)
1154  parent = e;
1155 
1156  for (thread = e->thread->parent; thread; thread = thread->parent)
1157  {
1158  e = thread->message;
1159  if (e)
1160  {
1161  parent = e;
1162  if (!find_root)
1163  break;
1164  }
1165  }
1166 
1167  if (!parent)
1168  {
1169  mutt_error(_("Parent message is not available"));
1170  return -1;
1171  }
1172  if (!is_visible(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 parent->virtual;
1181 }
The envelope/body of an email.
Definition: email.h:35
static bool is_visible(struct Email *e, struct Context *ctx)
Is the message visible?
Definition: mutt_thread.c:65
struct MuttThread * thread
Definition: email.h:96
int virtual
virtual message number
Definition: email.h:89
#define _(a)
Definition: message.h:28
struct MuttThread * parent
Definition: thread.h:44
#define SORT_THREADS
Sort by email threads.
Definition: sort.h:51
struct Email * message
Definition: thread.h:48
An email conversation.
Definition: thread.h:34
WHERE short Sort
Config: Sort method for the index.
Definition: sort.h:58
#define mutt_error(...)
Definition: logging.h:88
#define SORT_MASK
Mask for the sort id.
Definition: sort.h:77

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void mutt_set_virtual ( 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 *cur = 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  cur = m->hdrs[i];
1203  if (cur->virtual >= 0)
1204  {
1205  cur->virtual = m->vcount;
1206  m->v2r[m->vcount] = i;
1207  m->vcount++;
1208  ctx->vsize += cur->content->length + cur->content->offset -
1209  cur->content->hdr_offset + padding;
1210  cur->num_hidden = mutt_get_hidden(ctx, cur);
1211  }
1212  }
1213 }
int msg_count
total number of messages
Definition: mailbox.h:86
The envelope/body of an email.
Definition: email.h:35
int virtual
virtual message number
Definition: email.h:89
struct Body * content
list of MIME parts
Definition: email.h:92
LOFF_T offset
offset where the actual data begins
Definition: body.h:45
struct Email ** hdrs
Definition: mailbox.h:93
int vcount
the number of virtual messages
Definition: mailbox.h:96
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:46
A mailbox.
Definition: mailbox.h:76
size_t num_hidden
number of hidden messages in this view
Definition: email.h:77
int * v2r
mapping from virtual to real msgno
Definition: mailbox.h:95
int mx_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Wrapper for MxOps::msg_padding_size.
Definition: mx.c:1660
long hdr_offset
offset in stream where the headers begin.
Definition: body.h:41
#define mutt_get_hidden(x, y)
Definition: mutt_thread.h:61

+ 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 cur,
int  flag 
)

Recurse through an email thread, matching messages.

Parameters
ctxMailbox
curHeader of current message
flagFlag to set, e.g. MUTT_THREAD_NEXT_UNREAD
Return values
numNumber of matches

Definition at line 1222 of file mutt_thread.c.

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

1398 {
1399  if (!m || !e)
1400  return 1;
1401 
1402  struct MuttThread *threads[2];
1403  int rc;
1404 
1405  if ((Sort & SORT_MASK) != SORT_THREADS || !e->thread)
1406  return 1;
1407 
1408  threads[0] = e->thread;
1409  while (threads[0]->parent)
1410  threads[0] = threads[0]->parent;
1411 
1412  threads[1] = flag ? e->thread : threads[0]->next;
1413 
1414  for (int i = 0; i < ((flag || !threads[1]) ? 1 : 2); i++)
1415  {
1416  while (!threads[i]->message)
1417  threads[i] = threads[i]->child;
1418  }
1419 
1420  if (Sort & SORT_REVERSE)
1421  rc = threads[0]->message->msgno - (threads[1] ? threads[1]->message->msgno : -1);
1422  else
1423  {
1424  rc = (threads[1] ? threads[1]->message->msgno : m->msg_count) -
1425  threads[0]->message->msgno;
1426  }
1427 
1428  if (flag)
1429  rc += 1;
1430 
1431  return rc;
1432 }
struct MuttThread * next
Definition: thread.h:46
int msg_count
total number of messages
Definition: mailbox.h:86
struct MuttThread * thread
Definition: email.h:96
struct MuttThread * parent
Definition: thread.h:44
struct MuttThread * child
Definition: thread.h:45
#define SORT_THREADS
Sort by email threads.
Definition: sort.h:51
struct Email * message
Definition: thread.h:48
An email conversation.
Definition: thread.h:34
WHERE short Sort
Config: Sort method for the index.
Definition: sort.h:58
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort.h:78
#define SORT_MASK
Mask for the sort id.
Definition: sort.h:77
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 1439 of file mutt_thread.c.

1440 {
1441  struct Hash *hash = mutt_hash_new(m->msg_count * 2, 0);
1442 
1443  for (int i = 0; i < m->msg_count; i++)
1444  {
1445  struct Email *e = m->hdrs[i];
1446  if (e->env->message_id)
1447  mutt_hash_insert(hash, e->env->message_id, e);
1448  }
1449 
1450  return hash;
1451 }
struct Hash * mutt_hash_new(size_t nelem, int flags)
Create a new Hash table (with string keys)
Definition: hash.c:278
int msg_count
total number of messages
Definition: mailbox.h:86
The envelope/body of an email.
Definition: email.h:35
A Hash Table.
Definition: hash.h:61
const char * hash
Definition: md5.c:10
struct Email ** hdrs
Definition: mailbox.h:93
char * message_id
Definition: envelope.h:47
struct Envelope * env
envelope information
Definition: email.h:91
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:349

+ 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
parentHeader of parent message
childHeader of child message
mMailbox
Return values
trueOn success

Definition at line 1460 of file mutt_thread.c.

1461 {
1462  if (child == parent)
1463  return false;
1464 
1465  mutt_break_thread(child);
1467  mutt_set_flag(m, child, MUTT_TAG, 0);
1468 
1469  child->env->irt_changed = true;
1470  child->changed = true;
1471  return true;
1472 }
bool changed
Definition: email.h:46
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:60
char * message_id
Definition: envelope.h:47
struct Envelope * env
envelope information
Definition: email.h:91
Tagged messages.
Definition: mutt.h:123
void mutt_break_thread(struct Email *e)
Break the email Thread.
Definition: thread.c:217
bool irt_changed
In-Reply-To changed to link/break threads.
Definition: envelope.h:63
#define mutt_set_flag(a, b, c, d)
Definition: protos.h:54
struct ListNode * mutt_list_insert_head(struct ListHead *h, char *s)
Insert a string at the beginning of a List.
Definition: list.c:44
char * mutt_str_strdup(const char *str)
Copy a string, safely.
Definition: string.c:384

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

bool mutt_link_threads ( struct Email cur,
struct Email last,
struct Context ctx 
)

Forcibly link threads together.

Parameters
curHeader of current message
lastHeader of thread to link (OPTIONAL)
ctxMailbox
Return values
trueOn success

if last is omitted, all the tagged threads will be used.

Definition at line 1483 of file mutt_thread.c.

1484 {
1485  if (!ctx || !ctx->mailbox)
1486  return false;
1487 
1488  struct Mailbox *m = ctx->mailbox;
1489 
1490  bool changed = false;
1491 
1492  if (!last)
1493  {
1494  for (int i = 0; i < m->msg_count; i++)
1495  if (message_is_tagged(ctx, i))
1496  changed |= link_threads(cur, m->hdrs[i], ctx->mailbox);
1497  }
1498  else
1499  changed = link_threads(cur, last, ctx->mailbox);
1500 
1501  return changed;
1502 }
int msg_count
total number of messages
Definition: mailbox.h:86
struct Email ** hdrs
Definition: mailbox.h:93
struct Mailbox * mailbox
Definition: context.h:50
A mailbox.
Definition: mailbox.h:76
static bool link_threads(struct Email *parent, struct Email *child, struct Mailbox *m)
Forcibly link messages together.
Definition: mutt_thread.c:1460
bool message_is_tagged(struct Context *ctx, int index)
Is a message in the index tagged (and within limit)
Definition: curs_lib.c:1236
bool changed
mailbox has been modified
Definition: mailbox.h:108

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

Variable Documentation

bool DuplicateThreads

Config: Highlight messages with duplicated message IDs.

Definition at line 48 of file mutt_thread.c.

bool HideLimited

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

Definition at line 49 of file mutt_thread.c.

bool HideMissing

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

Definition at line 50 of file mutt_thread.c.

bool HideThreadSubject

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

Definition at line 51 of file mutt_thread.c.

bool HideTopLimited

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

Definition at line 52 of file mutt_thread.c.

bool HideTopMissing

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

Definition at line 53 of file mutt_thread.c.

bool NarrowTree

Config: Draw a narrower thread tree in the index.

Definition at line 54 of file mutt_thread.c.

bool SortRe

Config: Sort method for the sidebar.

Definition at line 55 of file mutt_thread.c.

bool StrictThreads

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

Definition at line 56 of file mutt_thread.c.

bool ThreadReceived

Config: Sort threaded messages by their received date.

Definition at line 57 of file mutt_thread.c.