NeoMutt  2018-07-16 +1783-b00bd9
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 "mutt_menu.h"
#include "mx.h"
#include "protos.h"
#include "sort.h"
+ Include dependency graph for mutt_thread.c:

Go to the source code of this file.

Macros

#define CHECK_LIMIT   (!ctx->pattern || 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_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, 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 || 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->virtual >= 0 || (e->collapsed && (!ctx->pattern || e->limited));
69 }
int virtual
virtual message number
Definition: email.h:90
bool limited
is this message in a limited view?
Definition: email.h:77
bool collapsed
is this message part of a collapsed thread?
Definition: email.h:76
char * pattern
limit pattern string
Definition: context.h:41

+ Here is the caller graph for this function:

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

Determines whether to display a message's subject.

Parameters
ctxMailbox
eEmail
Return values
trueIf the subject should be displayed

Definition at line 77 of file mutt_thread.c.

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

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

static void linearize_tree ( struct Context ctx)
static

Flatten an email thread.

Parameters
ctxMailbox

Definition at line 125 of file mutt_thread.c.

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

+ Here is the caller graph for this function:

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

Are tree nodes visible.

Parameters
ctxMailbox
max_depthMaximum depth to check

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

Definition at line 173 of file mutt_thread.c.

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

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

288 {
289  char *pfx = NULL, *mypfx = NULL, *arrow = NULL, *myarrow = NULL, *new_tree = NULL;
292  int depth = 0, start_depth = 0, max_depth = 0, width = C_NarrowTree ? 1 : 2;
293  struct MuttThread *nextdisp = NULL, *pseudo = NULL, *parent = NULL, *tree = ctx->tree;
294 
295  /* Do the visibility calculations and free the old thread chars.
296  * From now on we can simply ignore invisible subtrees */
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 && !C_HideLimited)
308  myarrow[0] = MUTT_TREE_HIDDEN;
309  else if (!parent->message && !C_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
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:44
static void calculate_visibility(struct Context *ctx, int *max_depth)
Are tree nodes visible.
Definition: mutt_thread.c:173
struct MuttThread * parent
Definition: thread.h:44
bool next_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:58
Ampersand character (for threads)
Definition: mutt_menu.h:69
struct MuttThread * child
Definition: thread.h:45
Question mark.
Definition: mutt_menu.h:73
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:97
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:753
TreeChar
Tree characters for menus.
Definition: mutt_menu.h:59
bool C_NarrowTree
Config: Draw a narrower thread tree in the index.
Definition: mutt_thread.c:55
An email conversation.
Definition: thread.h:34
#define FREE(x)
Definition: memory.h:40
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort.h:86
Left T-piece.
Definition: mutt_menu.h:63
Upper left corner.
Definition: mutt_menu.h:62
Horizontal line.
Definition: mutt_menu.h:64

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

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

Create a sorted list of all subjects in a thread.

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

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

Definition at line 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 = C_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) || (!C_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:51
struct MuttThread * parent
Definition: thread.h:44
bool C_SortRe
Config: Sort method for the sidebar.
Definition: mutt_thread.c:56
struct Envelope * env
envelope information
Definition: email.h:92
time_t date_sent
time when the message was sent (UTC)
Definition: email.h:84
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:79
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:50
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:618
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:85
The header of an email.
Definition: envelope.h:38

+ 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 >= (C_ThreadReceived ? tmp->message->received : tmp->message->date_sent)) &&
486  (!last || (C_ThreadReceived ?
487  (last->message->received < tmp->message->received) :
488  (last->message->date_sent < tmp->message->date_sent))) &&
489  tmp->message->env->real_subj &&
490  (mutt_str_strcmp(np->data, tmp->message->env->real_subj) == 0))
491  {
492  last = tmp; /* best match so far */
493  }
494  }
495  }
496 
497  mutt_list_clear(&subjects);
498  return last;
499 }
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:51
struct HashElem * next
Definition: hash.h:47
struct Envelope * env
envelope information
Definition: email.h:92
time_t date_sent
time when the message was sent (UTC)
Definition: email.h:84
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:128
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: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:160
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:618
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:85

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

507 {
508  if (!m)
509  return NULL;
510 
511  struct Hash *hash = mutt_hash_new(m->msg_count * 2, MUTT_HASH_ALLOW_DUPS);
512 
513  for (int i = 0; i < m->msg_count; i++)
514  {
515  struct Email *e = m->emails[i];
516  if (e->env->real_subj)
517  mutt_hash_insert(hash, e->env->real_subj, e);
518  }
519 
520  return hash;
521 }
struct Email ** emails
Definition: mailbox.h:99
int msg_count
total number of messages
Definition: mailbox.h:92
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:51
struct Envelope * env
envelope information
Definition: email.h:92
#define MUTT_HASH_ALLOW_DUPS
allow duplicate keys to be inserted
Definition: hash.h:77
struct Hash * mutt_hash_new(size_t nelem, HashFlags flags)
Create a new Hash table (with string keys)
Definition: hash.c:276
struct HashElem * mutt_hash_insert(struct Hash *table, const char *strkey, void *data)
Add a new element to the Hash table (with string keys)
Definition: hash.c:352

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

static void pseudo_threads ( struct Context ctx)
static

Thread messages by subject.

Parameters
ctxMailbox

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

Definition at line 529 of file mutt_thread.c.

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

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

600 {
601  if (!ctx || !ctx->mailbox || !ctx->mailbox->emails)
602  return;
603 
604  struct Mailbox *m = ctx->mailbox;
605 
606  for (int i = 0; i < m->msg_count; i++)
607  {
608  /* mailbox may have been only partially read */
609  if (m->emails[i])
610  {
611  m->emails[i]->thread = NULL;
612  m->emails[i]->threaded = false;
613  }
614  }
615  ctx->tree = NULL;
616 
618 }
struct Email ** emails
Definition: mailbox.h:99
int msg_count
total number of messages
Definition: mailbox.h:92
struct MuttThread * tree
top of thread tree
Definition: context.h:44
struct MuttThread * thread
Definition: email.h:97
bool threaded
used for threading
Definition: email.h:56
struct Mailbox * mailbox
Definition: context.h:52
struct Hash * thread_hash
hash table for threading
Definition: context.h:45
A mailbox.
Definition: mailbox.h:83
void mutt_hash_free(struct Hash **ptr)
elem_free a hash table
Definition: hash.c:472

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

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

Sorting function for email threads.

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

Definition at line 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  else
640  {
641  sort_func = mutt_get_sort_func(C_Sort & SORT_MASK);
642  return sort_func ? 1 : 0;
643  }
644 }
int sort_t(const void *a, const void *b)
typedef sort_t - Prototype for a function to compare two emails
Definition: sort.h:48
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:58
sort_t * mutt_get_sort_func(enum SortType method)
Get the sort function for a given sort id.
Definition: sort.c:321
struct Email * sort_key
Definition: thread.h:49
An email conversation.
Definition: thread.h:34
#define SORT_MASK
Mask for the sort id.
Definition: sort.h:85

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

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

Sort the children of a thread.

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

Definition at line 652 of file mutt_thread.c.

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

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

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

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

Parameters
mMailbox
initIf true, rebuild the thread

Definition at line 787 of file mutt_thread.c.

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

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

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

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

1072 {
1073  struct MuttThread *cur = NULL;
1074  struct Email *tmp = NULL;
1075 
1076  if ((C_Sort & SORT_MASK) != SORT_THREADS)
1077  {
1078  mutt_error(_("Threading is not enabled"));
1079  return e->virtual;
1080  }
1081 
1082  cur = e->thread;
1083 
1084  if (!subthreads)
1085  {
1086  while (cur->parent)
1087  cur = cur->parent;
1088  }
1089  else
1090  {
1091  if (forwards ^ ((C_Sort & SORT_REVERSE) != 0))
1092  {
1093  while (!cur->next && cur->parent)
1094  cur = cur->parent;
1095  }
1096  else
1097  {
1098  while (!cur->prev && cur->parent)
1099  cur = cur->parent;
1100  }
1101  }
1102 
1103  if (forwards ^ ((C_Sort & SORT_REVERSE) != 0))
1104  {
1105  do
1106  {
1107  cur = cur->next;
1108  if (!cur)
1109  return -1;
1110  tmp = find_virtual(cur, 0);
1111  } while (!tmp);
1112  }
1113  else
1114  {
1115  do
1116  {
1117  cur = cur->prev;
1118  if (!cur)
1119  return -1;
1120  tmp = find_virtual(cur, 1);
1121  } while (!tmp);
1122  }
1123 
1124  return tmp->virtual;
1125 }
struct MuttThread * next
Definition: thread.h:46
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:123
struct MuttThread * thread
Definition: email.h:97
int virtual
virtual message number
Definition: email.h:90
#define _(a)
Definition: message.h:28
struct MuttThread * parent
Definition: thread.h:44
struct MuttThread * prev
Definition: thread.h:47
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:58
An email conversation.
Definition: thread.h:34
#define mutt_error(...)
Definition: logging.h:83
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort.h:86
Sort by email threads.
Definition: sort.h:56
#define SORT_MASK
Mask for the sort id.
Definition: sort.h:85

+ Here is the call graph for this function:

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

Find the parent of a message.

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

Definition at line 1135 of file mutt_thread.c.

1136 {
1137  struct MuttThread *thread = NULL;
1138  struct Email *parent = NULL;
1139 
1140  if ((C_Sort & SORT_MASK) != SORT_THREADS)
1141  {
1142  mutt_error(_("Threading is not enabled"));
1143  return e->virtual;
1144  }
1145 
1146  /* Root may be the current message */
1147  if (find_root)
1148  parent = e;
1149 
1150  for (thread = e->thread->parent; thread; thread = thread->parent)
1151  {
1152  e = thread->message;
1153  if (e)
1154  {
1155  parent = e;
1156  if (!find_root)
1157  break;
1158  }
1159  }
1160 
1161  if (!parent)
1162  {
1163  mutt_error(_("Parent message is not available"));
1164  return -1;
1165  }
1166  if (!is_visible(parent, ctx))
1167  {
1168  if (find_root)
1169  mutt_error(_("Root message is not visible in this limited view"));
1170  else
1171  mutt_error(_("Parent message is not visible in this limited view"));
1172  return -1;
1173  }
1174  return parent->virtual;
1175 }
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
Definition: email.h:97
int virtual
virtual message number
Definition: email.h:90
#define _(a)
Definition: message.h:28
struct MuttThread * parent
Definition: thread.h:44
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:58
struct Email * message
Definition: thread.h:48
An email conversation.
Definition: thread.h:34
#define mutt_error(...)
Definition: logging.h:83
Sort by email threads.
Definition: sort.h:56
#define SORT_MASK
Mask for the sort id.
Definition: sort.h:85

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void mutt_set_virtual ( struct Context ctx)

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

Parameters
ctxMailbox

Definition at line 1181 of file mutt_thread.c.

1182 {
1183  if (!ctx || !ctx->mailbox)
1184  return;
1185 
1186  struct Mailbox *m = ctx->mailbox;
1187 
1188  struct Email *cur = NULL;
1189 
1190  m->vcount = 0;
1191  ctx->vsize = 0;
1192  int padding = mx_msg_padding_size(m);
1193 
1194  for (int i = 0; i < m->msg_count; i++)
1195  {
1196  cur = m->emails[i];
1197  if (cur->virtual >= 0)
1198  {
1199  cur->virtual = m->vcount;
1200  m->v2r[m->vcount] = i;
1201  m->vcount++;
1202  ctx->vsize += cur->content->length + cur->content->offset -
1203  cur->content->hdr_offset + padding;
1204  cur->num_hidden = mutt_get_hidden(ctx, cur);
1205  }
1206  }
1207 }
struct Email ** emails
Definition: mailbox.h:99
int msg_count
total number of messages
Definition: mailbox.h:92
The envelope/body of an email.
Definition: email.h:37
int virtual
virtual message number
Definition: email.h:90
struct Body * content
list of MIME parts
Definition: email.h:93
LOFF_T offset
offset where the actual data begins
Definition: body.h:46
int vcount
the number of virtual messages
Definition: mailbox.h:102
struct Mailbox * mailbox
Definition: context.h:52
#define mutt_get_hidden(ctx, cur)
Definition: mutt_thread.h:58
off_t vsize
Definition: context.h:40
LOFF_T length
length (in bytes) of attachment
Definition: body.h:47
A mailbox.
Definition: mailbox.h:83
size_t num_hidden
number of hidden messages in this view
Definition: email.h:78
int * v2r
mapping from virtual to real msgno
Definition: mailbox.h:101
int mx_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Wrapper for MxOps::msg_padding_size.
Definition: mx.c:1454
long hdr_offset
offset in stream where the headers begin.
Definition: body.h:42

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

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

Recurse through an email thread, matching messages.

Parameters
ctxMailbox
curHeader of current message
flagFlag to set, see MuttThreadFlags
Return values
numNumber of matches

Definition at line 1216 of file mutt_thread.c.

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

1392 {
1393  if (!m || !e)
1394  return 1;
1395 
1396  struct MuttThread *threads[2];
1397  int rc;
1398 
1399  if (((C_Sort & SORT_MASK) != SORT_THREADS) || !e->thread)
1400  return 1;
1401 
1402  threads[0] = e->thread;
1403  while (threads[0]->parent)
1404  threads[0] = threads[0]->parent;
1405 
1406  threads[1] = flag ? e->thread : threads[0]->next;
1407 
1408  for (int i = 0; i < ((flag || !threads[1]) ? 1 : 2); i++)
1409  {
1410  while (!threads[i]->message)
1411  threads[i] = threads[i]->child;
1412  }
1413 
1414  if (C_Sort & SORT_REVERSE)
1415  rc = threads[0]->message->msgno - (threads[1] ? threads[1]->message->msgno : -1);
1416  else
1417  {
1418  rc = (threads[1] ? threads[1]->message->msgno : m->msg_count) -
1419  threads[0]->message->msgno;
1420  }
1421 
1422  if (flag)
1423  rc += 1;
1424 
1425  return rc;
1426 }
struct MuttThread * next
Definition: thread.h:46
int msg_count
total number of messages
Definition: mailbox.h:92
struct MuttThread * thread
Definition: email.h:97
struct MuttThread * parent
Definition: thread.h:44
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:58
struct MuttThread * child
Definition: thread.h:45
struct Email * message
Definition: thread.h:48
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:89

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

1434 {
1435  struct Hash *hash = mutt_hash_new(m->msg_count * 2, MUTT_HASH_NO_FLAGS);
1436 
1437  for (int i = 0; i < m->msg_count; i++)
1438  {
1439  struct Email *e = m->emails[i];
1440  if (e->env->message_id)
1441  mutt_hash_insert(hash, e->env->message_id, e);
1442  }
1443 
1444  return hash;
1445 }
struct Email ** emails
Definition: mailbox.h:99
int msg_count
total number of messages
Definition: mailbox.h:92
The envelope/body of an email.
Definition: email.h:37
A Hash Table.
Definition: hash.h:61
char * message_id
Definition: envelope.h:53
struct Envelope * env
envelope information
Definition: email.h:92
struct Hash * mutt_hash_new(size_t nelem, HashFlags flags)
Create a new Hash table (with string keys)
Definition: hash.c:276
struct HashElem * mutt_hash_insert(struct Hash *table, const char *strkey, void *data)
Add a new element to the Hash table (with string keys)
Definition: hash.c:352
#define MUTT_HASH_NO_FLAGS
No flags are set.
Definition: hash.h:74

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

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

Forcibly link messages together.

Parameters
parentParent Email
childChild Email
mMailbox
Return values
trueOn success

Definition at line 1454 of file mutt_thread.c.

1455 {
1456  if (child == parent)
1457  return false;
1458 
1459  mutt_break_thread(child);
1461  mutt_set_flag(m, child, MUTT_TAG, false);
1462 
1463  child->changed = true;
1464  child->env->changed |= MUTT_ENV_CHANGED_IRT;
1465  return true;
1466 }
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:68
bool changed
Definition: email.h:48
unsigned char changed
Definition: envelope.h:69
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:66
char * message_id
Definition: envelope.h:53
struct Envelope * env
envelope information
Definition: email.h:92
void mutt_break_thread(struct Email *e)
Break the email Thread.
Definition: thread.c:226
Tagged messages.
Definition: mutt.h:106
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:380
#define MUTT_ENV_CHANGED_IRT
In-Reply-To changed to link/break threads.
Definition: envelope.h:30

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

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

Forcibly link threads together.

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

Definition at line 1475 of file mutt_thread.c.

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

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

Variable Documentation

bool C_DuplicateThreads

Config: Highlight messages with duplicated message IDs.

Definition at line 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.