NeoMutt  2018-07-16 +1360-3df4a2
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:40

+ 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:100
struct MuttThread * next
Definition: thread.h:46
int msg_count
total number of messages
Definition: mailbox.h:93
The envelope/body of an email.
Definition: email.h:37
struct MuttThread * tree
top of thread tree
Definition: context.h:43
struct MuttThread * parent
Definition: thread.h:44
struct Mailbox * mailbox
Definition: context.h:51
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:43
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:43
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:99
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:741
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: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: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:611
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:414
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:129
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: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:611
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 
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:100
int msg_count
total number of messages
Definition: mailbox.h:93
The envelope/body of an email.
Definition: email.h:37
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: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:277
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:348

+ 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:43
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:91
struct Mailbox * mailbox
Definition: context.h:51
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:129
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:611
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:100
int msg_count
total number of messages
Definition: mailbox.h:93
struct MuttThread * tree
top of thread tree
Definition: context.h:43
struct MuttThread * thread
Definition: email.h:97
bool threaded
used for threading
Definition: email.h:56
struct Mailbox * mailbox
Definition: context.h:51
struct Hash * thread_hash
hash table for threading
Definition: context.h:44
A mailbox.
Definition: mailbox.h:83
void mutt_hash_free(struct Hash **ptr)
elem_free a hash table
Definition: hash.c:457

+ 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:320
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: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
#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) ? true : false;
815  }
816  else
817  {
818  cur->subject_changed =
819  (cur->env->real_subj || tmp->message->env->real_subj) ? true : false;
820  }
821  }
822 }
struct Email ** emails
Definition: mailbox.h:100
int msg_count
total number of messages
Definition: mailbox.h:93
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:611

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

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

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

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

1183 {
1184  if (!ctx || !ctx->mailbox)
1185  return;
1186 
1187  struct Mailbox *m = ctx->mailbox;
1188 
1189  struct Email *cur = NULL;
1190 
1191  m->vcount = 0;
1192  ctx->vsize = 0;
1193  int padding = mx_msg_padding_size(m);
1194 
1195  for (int i = 0; i < m->msg_count; i++)
1196  {
1197  cur = m->emails[i];
1198  if (cur->virtual >= 0)
1199  {
1200  cur->virtual = m->vcount;
1201  m->v2r[m->vcount] = i;
1202  m->vcount++;
1203  ctx->vsize += cur->content->length + cur->content->offset -
1204  cur->content->hdr_offset + padding;
1205  cur->num_hidden = mutt_get_hidden(ctx, cur);
1206  }
1207  }
1208 }
struct Email ** emails
Definition: mailbox.h:100
int msg_count
total number of messages
Definition: mailbox.h:93
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:103
struct Mailbox * mailbox
Definition: context.h:51
#define mutt_get_hidden(ctx, cur)
Definition: mutt_thread.h:58
off_t vsize
Definition: context.h:39
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:102
int mx_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Wrapper for MxOps::msg_padding_size.
Definition: mx.c:1429
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 1217 of file mutt_thread.c.

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

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

1435 {
1436  struct Hash *hash = mutt_hash_new(m->msg_count * 2, 0);
1437 
1438  for (int i = 0; i < m->msg_count; i++)
1439  {
1440  struct Email *e = m->emails[i];
1441  if (e->env->message_id)
1442  mutt_hash_insert(hash, e->env->message_id, e);
1443  }
1444 
1445  return hash;
1446 }
struct Email ** emails
Definition: mailbox.h:100
int msg_count
total number of messages
Definition: mailbox.h:93
The envelope/body of an email.
Definition: email.h:37
A Hash Table.
Definition: hash.h:61
const char * hash
Definition: md5.c:10
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:277
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:348

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

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

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