PageRenderTime 74ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/thread.c

https://bitbucket.org/mutt/mutt
C | 1440 lines | 1131 code | 178 blank | 131 comment | 378 complexity | 7baed38601e4280cf435e9090c5677fa MD5 | raw file
Possible License(s): AGPL-1.0, GPL-2.0, LGPL-2.0, LGPL-2.1
  1. /*
  2. * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  17. */
  18. #if HAVE_CONFIG_H
  19. # include "config.h"
  20. #endif
  21. #include "mutt.h"
  22. #include "sort.h"
  23. #include <string.h>
  24. #include <ctype.h>
  25. #define VISIBLE(hdr, ctx) (hdr->virtual >= 0 || (hdr->collapsed && (!ctx->pattern || hdr->limited)))
  26. /* determine whether a is a descendant of b */
  27. static int is_descendant (THREAD *a, THREAD *b)
  28. {
  29. while (a)
  30. {
  31. if (a == b)
  32. return (1);
  33. a = a->parent;
  34. }
  35. return (0);
  36. }
  37. /* Determines whether to display a message's subject. */
  38. static int need_display_subject (CONTEXT *ctx, HEADER *hdr)
  39. {
  40. THREAD *tmp, *tree = hdr->thread;
  41. /* if the user disabled subject hiding, display it */
  42. if (!option (OPTHIDETHREADSUBJECT))
  43. return (1);
  44. /* if our subject is different from our parent's, display it */
  45. if (hdr->subject_changed)
  46. return (1);
  47. /* if our subject is different from that of our closest previously displayed
  48. * sibling, display the subject */
  49. for (tmp = tree->prev; tmp; tmp = tmp->prev)
  50. {
  51. hdr = tmp->message;
  52. if (hdr && VISIBLE (hdr, ctx))
  53. {
  54. if (hdr->subject_changed)
  55. return (1);
  56. else
  57. break;
  58. }
  59. }
  60. /* if there is a parent-to-child subject change anywhere between us and our
  61. * closest displayed ancestor, display the subject */
  62. for (tmp = tree->parent; tmp; tmp = tmp->parent)
  63. {
  64. hdr = tmp->message;
  65. if (hdr)
  66. {
  67. if (VISIBLE (hdr, ctx))
  68. return (0);
  69. else if (hdr->subject_changed)
  70. return (1);
  71. }
  72. }
  73. /* if we have no visible parent or previous sibling, display the subject */
  74. return (1);
  75. }
  76. static void linearize_tree (CONTEXT *ctx)
  77. {
  78. THREAD *tree = ctx->tree;
  79. HEADER **array = ctx->hdrs + (Sort & SORT_REVERSE ? ctx->msgcount - 1 : 0);
  80. while (tree)
  81. {
  82. while (!tree->message)
  83. tree = tree->child;
  84. *array = tree->message;
  85. array += Sort & SORT_REVERSE ? -1 : 1;
  86. if (tree->child)
  87. tree = tree->child;
  88. else
  89. {
  90. while (tree)
  91. {
  92. if (tree->next)
  93. {
  94. tree = tree->next;
  95. break;
  96. }
  97. else
  98. tree = tree->parent;
  99. }
  100. }
  101. }
  102. }
  103. /* this calculates whether a node is the root of a subtree that has visible
  104. * nodes, whether a node itself is visible, whether, if invisible, it has
  105. * depth anyway, and whether any of its later siblings are roots of visible
  106. * subtrees. while it's at it, it frees the old thread display, so we can
  107. * skip parts of the tree in mutt_draw_tree() if we've decided here that we
  108. * don't care about them any more.
  109. */
  110. static void calculate_visibility (CONTEXT *ctx, int *max_depth)
  111. {
  112. THREAD *tmp, *tree = ctx->tree;
  113. int hide_top_missing = option (OPTHIDETOPMISSING) && !option (OPTHIDEMISSING);
  114. int hide_top_limited = option (OPTHIDETOPLIMITED) && !option (OPTHIDELIMITED);
  115. int depth = 0;
  116. /* we walk each level backwards to make it easier to compute next_subtree_visible */
  117. while (tree->next)
  118. tree = tree->next;
  119. *max_depth = 0;
  120. FOREVER
  121. {
  122. if (depth > *max_depth)
  123. *max_depth = depth;
  124. tree->subtree_visible = 0;
  125. if (tree->message)
  126. {
  127. FREE (&tree->message->tree);
  128. if (VISIBLE (tree->message, ctx))
  129. {
  130. tree->deep = 1;
  131. tree->visible = 1;
  132. tree->message->display_subject = need_display_subject (ctx, tree->message);
  133. for (tmp = tree; tmp; tmp = tmp->parent)
  134. {
  135. if (tmp->subtree_visible)
  136. {
  137. tmp->deep = 1;
  138. tmp->subtree_visible = 2;
  139. break;
  140. }
  141. else
  142. tmp->subtree_visible = 1;
  143. }
  144. }
  145. else
  146. {
  147. tree->visible = 0;
  148. tree->deep = !option (OPTHIDELIMITED);
  149. }
  150. }
  151. else
  152. {
  153. tree->visible = 0;
  154. tree->deep = !option (OPTHIDEMISSING);
  155. }
  156. tree->next_subtree_visible = tree->next && (tree->next->next_subtree_visible
  157. || tree->next->subtree_visible);
  158. if (tree->child)
  159. {
  160. depth++;
  161. tree = tree->child;
  162. while (tree->next)
  163. tree = tree->next;
  164. }
  165. else if (tree->prev)
  166. tree = tree->prev;
  167. else
  168. {
  169. while (tree && !tree->prev)
  170. {
  171. depth--;
  172. tree = tree->parent;
  173. }
  174. if (!tree)
  175. break;
  176. else
  177. tree = tree->prev;
  178. }
  179. }
  180. /* now fix up for the OPTHIDETOP* options if necessary */
  181. if (hide_top_limited || hide_top_missing)
  182. {
  183. tree = ctx->tree;
  184. FOREVER
  185. {
  186. if (!tree->visible && tree->deep && tree->subtree_visible < 2
  187. && ((tree->message && hide_top_limited) || (!tree->message && hide_top_missing)))
  188. tree->deep = 0;
  189. if (!tree->deep && tree->child && tree->subtree_visible)
  190. tree = tree->child;
  191. else if (tree->next)
  192. tree = tree->next;
  193. else
  194. {
  195. while (tree && !tree->next)
  196. tree = tree->parent;
  197. if (!tree)
  198. break;
  199. else
  200. tree = tree->next;
  201. }
  202. }
  203. }
  204. }
  205. /* Since the graphics characters have a value >255, I have to resort to
  206. * using escape sequences to pass the information to print_enriched_string().
  207. * These are the macros MUTT_TREE_* defined in mutt.h.
  208. *
  209. * ncurses should automatically use the default ASCII characters instead of
  210. * graphics chars on terminals which don't support them (see the man page
  211. * for curs_addch).
  212. */
  213. void mutt_draw_tree (CONTEXT *ctx)
  214. {
  215. char *pfx = NULL, *mypfx = NULL, *arrow = NULL, *myarrow = NULL, *new_tree;
  216. char corner = (Sort & SORT_REVERSE) ? MUTT_TREE_ULCORNER : MUTT_TREE_LLCORNER;
  217. char vtee = (Sort & SORT_REVERSE) ? MUTT_TREE_BTEE : MUTT_TREE_TTEE;
  218. int depth = 0, start_depth = 0, max_depth = 0, width = option (OPTNARROWTREE) ? 1 : 2;
  219. THREAD *nextdisp = NULL, *pseudo = NULL, *parent = NULL, *tree = ctx->tree;
  220. /* Do the visibility calculations and free the old thread chars.
  221. * From now on we can simply ignore invisible subtrees
  222. */
  223. calculate_visibility (ctx, &max_depth);
  224. pfx = safe_malloc (width * max_depth + 2);
  225. arrow = safe_malloc (width * max_depth + 2);
  226. while (tree)
  227. {
  228. if (depth)
  229. {
  230. myarrow = arrow + (depth - start_depth - (start_depth ? 0 : 1)) * width;
  231. if (depth && start_depth == depth)
  232. myarrow[0] = nextdisp ? MUTT_TREE_LTEE : corner;
  233. else if (parent->message && !option (OPTHIDELIMITED))
  234. myarrow[0] = MUTT_TREE_HIDDEN;
  235. else if (!parent->message && !option (OPTHIDEMISSING))
  236. myarrow[0] = MUTT_TREE_MISSING;
  237. else
  238. myarrow[0] = vtee;
  239. if (width == 2)
  240. myarrow[1] = pseudo ? MUTT_TREE_STAR
  241. : (tree->duplicate_thread ? MUTT_TREE_EQUALS : MUTT_TREE_HLINE);
  242. if (tree->visible)
  243. {
  244. myarrow[width] = MUTT_TREE_RARROW;
  245. myarrow[width + 1] = 0;
  246. new_tree = safe_malloc ((2 + depth * width));
  247. if (start_depth > 1)
  248. {
  249. strncpy (new_tree, pfx, (start_depth - 1) * width);
  250. strfcpy (new_tree + (start_depth - 1) * width,
  251. arrow, (1 + depth - start_depth) * width + 2);
  252. }
  253. else
  254. strfcpy (new_tree, arrow, 2 + depth * width);
  255. tree->message->tree = new_tree;
  256. }
  257. }
  258. if (tree->child && depth)
  259. {
  260. mypfx = pfx + (depth - 1) * width;
  261. mypfx[0] = nextdisp ? MUTT_TREE_VLINE : MUTT_TREE_SPACE;
  262. if (width == 2)
  263. mypfx[1] = MUTT_TREE_SPACE;
  264. }
  265. parent = tree;
  266. nextdisp = NULL;
  267. pseudo = NULL;
  268. do
  269. {
  270. if (tree->child && tree->subtree_visible)
  271. {
  272. if (tree->deep)
  273. depth++;
  274. if (tree->visible)
  275. start_depth = depth;
  276. tree = tree->child;
  277. /* we do this here because we need to make sure that the first child thread
  278. * of the old tree that we deal with is actually displayed if any are,
  279. * or we might set the parent variable wrong while going through it. */
  280. while (!tree->subtree_visible && tree->next)
  281. tree = tree->next;
  282. }
  283. else
  284. {
  285. while (!tree->next && tree->parent)
  286. {
  287. if (tree == pseudo)
  288. pseudo = NULL;
  289. if (tree == nextdisp)
  290. nextdisp = NULL;
  291. if (tree->visible)
  292. start_depth = depth;
  293. tree = tree->parent;
  294. if (tree->deep)
  295. {
  296. if (start_depth == depth)
  297. start_depth--;
  298. depth--;
  299. }
  300. }
  301. if (tree == pseudo)
  302. pseudo = NULL;
  303. if (tree == nextdisp)
  304. nextdisp = NULL;
  305. if (tree->visible)
  306. start_depth = depth;
  307. tree = tree->next;
  308. if (!tree)
  309. break;
  310. }
  311. if (!pseudo && tree->fake_thread)
  312. pseudo = tree;
  313. if (!nextdisp && tree->next_subtree_visible)
  314. nextdisp = tree;
  315. }
  316. while (!tree->deep);
  317. }
  318. FREE (&pfx);
  319. FREE (&arrow);
  320. }
  321. /* since we may be trying to attach as a pseudo-thread a THREAD that
  322. * has no message, we have to make a list of all the subjects of its
  323. * most immediate existing descendants. we also note the earliest
  324. * date on any of the parents and put it in *dateptr. */
  325. static LIST *make_subject_list (THREAD *cur, time_t *dateptr)
  326. {
  327. THREAD *start = cur;
  328. ENVELOPE *env;
  329. time_t thisdate;
  330. LIST *curlist, *oldlist, *newlist, *subjects = NULL;
  331. int rc = 0;
  332. FOREVER
  333. {
  334. while (!cur->message)
  335. cur = cur->child;
  336. if (dateptr)
  337. {
  338. thisdate = option (OPTTHREADRECEIVED)
  339. ? cur->message->received : cur->message->date_sent;
  340. if (!*dateptr || thisdate < *dateptr)
  341. *dateptr = thisdate;
  342. }
  343. env = cur->message->env;
  344. if (env->real_subj &&
  345. ((env->real_subj != env->subject) || (!option (OPTSORTRE))))
  346. {
  347. for (curlist = subjects, oldlist = NULL;
  348. curlist; oldlist = curlist, curlist = curlist->next)
  349. {
  350. rc = mutt_strcmp (env->real_subj, curlist->data);
  351. if (rc >= 0)
  352. break;
  353. }
  354. if (!curlist || rc > 0)
  355. {
  356. newlist = safe_calloc (1, sizeof (LIST));
  357. newlist->data = env->real_subj;
  358. if (oldlist)
  359. {
  360. newlist->next = oldlist->next;
  361. oldlist->next = newlist;
  362. }
  363. else
  364. {
  365. newlist->next = subjects;
  366. subjects = newlist;
  367. }
  368. }
  369. }
  370. while (!cur->next && cur != start)
  371. {
  372. cur = cur->parent;
  373. }
  374. if (cur == start)
  375. break;
  376. cur = cur->next;
  377. }
  378. return (subjects);
  379. }
  380. /* find the best possible match for a parent mesage based upon subject.
  381. * if there are multiple matches, the one which was sent the latest, but
  382. * before the current message, is used.
  383. */
  384. static THREAD *find_subject (CONTEXT *ctx, THREAD *cur)
  385. {
  386. struct hash_elem *ptr;
  387. THREAD *tmp, *last = NULL;
  388. LIST *subjects = NULL, *oldlist;
  389. time_t date = 0;
  390. subjects = make_subject_list (cur, &date);
  391. while (subjects)
  392. {
  393. for (ptr = hash_find_bucket (ctx->subj_hash, subjects->data); ptr; ptr = ptr->next)
  394. {
  395. tmp = ((HEADER *) ptr->data)->thread;
  396. if (tmp != cur && /* don't match the same message */
  397. !tmp->fake_thread && /* don't match pseudo threads */
  398. tmp->message->subject_changed && /* only match interesting replies */
  399. !is_descendant (tmp, cur) && /* don't match in the same thread */
  400. (date >= (option (OPTTHREADRECEIVED) ?
  401. tmp->message->received :
  402. tmp->message->date_sent)) &&
  403. (!last ||
  404. (option (OPTTHREADRECEIVED) ?
  405. (last->message->received < tmp->message->received) :
  406. (last->message->date_sent < tmp->message->date_sent))) &&
  407. tmp->message->env->real_subj &&
  408. mutt_strcmp (subjects->data, tmp->message->env->real_subj) == 0)
  409. last = tmp; /* best match so far */
  410. }
  411. oldlist = subjects;
  412. subjects = subjects->next;
  413. FREE (&oldlist);
  414. }
  415. return (last);
  416. }
  417. /* remove cur and its descendants from their current location.
  418. * also make sure ancestors of cur no longer are sorted by the
  419. * fact that cur is their descendant. */
  420. static void unlink_message (THREAD **old, THREAD *cur)
  421. {
  422. THREAD *tmp;
  423. if (cur->prev)
  424. cur->prev->next = cur->next;
  425. else
  426. *old = cur->next;
  427. if (cur->next)
  428. cur->next->prev = cur->prev;
  429. if (cur->sort_key)
  430. {
  431. for (tmp = cur->parent; tmp && tmp->sort_key == cur->sort_key;
  432. tmp = tmp->parent)
  433. tmp->sort_key = NULL;
  434. }
  435. }
  436. /* add cur as a prior sibling of *new, with parent newparent */
  437. static void insert_message (THREAD **new, THREAD *newparent, THREAD *cur)
  438. {
  439. if (*new)
  440. (*new)->prev = cur;
  441. cur->parent = newparent;
  442. cur->next = *new;
  443. cur->prev = NULL;
  444. *new = cur;
  445. }
  446. /* thread by subject things that didn't get threaded by message-id */
  447. static void pseudo_threads (CONTEXT *ctx)
  448. {
  449. THREAD *tree = ctx->tree, *top = tree;
  450. THREAD *tmp, *cur, *parent, *curchild, *nextchild;
  451. if (!ctx->subj_hash)
  452. ctx->subj_hash = mutt_make_subj_hash (ctx);
  453. while (tree)
  454. {
  455. cur = tree;
  456. tree = tree->next;
  457. if ((parent = find_subject (ctx, cur)) != NULL)
  458. {
  459. cur->fake_thread = 1;
  460. unlink_message (&top, cur);
  461. insert_message (&parent->child, parent, cur);
  462. parent->sort_children = 1;
  463. tmp = cur;
  464. FOREVER
  465. {
  466. while (!tmp->message)
  467. tmp = tmp->child;
  468. /* if the message we're attaching has pseudo-children, they
  469. * need to be attached to its parent, so move them up a level.
  470. * but only do this if they have the same real subject as the
  471. * parent, since otherwise they rightly belong to the message
  472. * we're attaching. */
  473. if (tmp == cur
  474. || !mutt_strcmp (tmp->message->env->real_subj,
  475. parent->message->env->real_subj))
  476. {
  477. tmp->message->subject_changed = 0;
  478. for (curchild = tmp->child; curchild; )
  479. {
  480. nextchild = curchild->next;
  481. if (curchild->fake_thread)
  482. {
  483. unlink_message (&tmp->child, curchild);
  484. insert_message (&parent->child, parent, curchild);
  485. }
  486. curchild = nextchild;
  487. }
  488. }
  489. while (!tmp->next && tmp != cur)
  490. {
  491. tmp = tmp->parent;
  492. }
  493. if (tmp == cur)
  494. break;
  495. tmp = tmp->next;
  496. }
  497. }
  498. }
  499. ctx->tree = top;
  500. }
  501. void mutt_clear_threads (CONTEXT *ctx)
  502. {
  503. int i;
  504. for (i = 0; i < ctx->msgcount; i++)
  505. {
  506. /* mailbox may have been only partially read */
  507. if (ctx->hdrs[i])
  508. {
  509. ctx->hdrs[i]->thread = NULL;
  510. ctx->hdrs[i]->threaded = 0;
  511. }
  512. }
  513. ctx->tree = NULL;
  514. if (ctx->thread_hash)
  515. hash_destroy (&ctx->thread_hash, *free);
  516. }
  517. static int compare_threads (const void *a, const void *b)
  518. {
  519. static sort_t *sort_func = NULL;
  520. if (a && b)
  521. return ((*sort_func) (&(*((THREAD **) a))->sort_key,
  522. &(*((THREAD **) b))->sort_key));
  523. /* a hack to let us reset sort_func even though we can't
  524. * have extra arguments because of qsort
  525. */
  526. else
  527. {
  528. sort_func = mutt_get_sort_func (Sort);
  529. return (sort_func ? 1 : 0);
  530. }
  531. }
  532. THREAD *mutt_sort_subthreads (THREAD *thread, int init)
  533. {
  534. THREAD **array, *sort_key, *top, *tmp;
  535. HEADER *oldsort_key;
  536. int i, array_size, sort_top = 0;
  537. /* we put things into the array backwards to save some cycles,
  538. * but we want to have to move less stuff around if we're
  539. * resorting, so we sort backwards and then put them back
  540. * in reverse order so they're forwards
  541. */
  542. Sort ^= SORT_REVERSE;
  543. if (!compare_threads (NULL, NULL))
  544. return (thread);
  545. top = thread;
  546. array = safe_calloc ((array_size = 256), sizeof (THREAD *));
  547. while (1)
  548. {
  549. if (init || !thread->sort_key)
  550. {
  551. thread->sort_key = NULL;
  552. if (thread->parent)
  553. thread->parent->sort_children = 1;
  554. else
  555. sort_top = 1;
  556. }
  557. if (thread->child)
  558. {
  559. thread = thread->child;
  560. continue;
  561. }
  562. else
  563. {
  564. /* if it has no children, it must be real. sort it on its own merits */
  565. thread->sort_key = thread->message;
  566. if (thread->next)
  567. {
  568. thread = thread->next;
  569. continue;
  570. }
  571. }
  572. while (!thread->next)
  573. {
  574. /* if it has siblings and needs to be sorted, sort it... */
  575. if (thread->prev && (thread->parent ? thread->parent->sort_children : sort_top))
  576. {
  577. /* put them into the array */
  578. for (i = 0; thread; i++, thread = thread->prev)
  579. {
  580. if (i >= array_size)
  581. safe_realloc (&array, (array_size *= 2) * sizeof (THREAD *));
  582. array[i] = thread;
  583. }
  584. qsort ((void *) array, i, sizeof (THREAD *), *compare_threads);
  585. /* attach them back together. make thread the last sibling. */
  586. thread = array[0];
  587. thread->next = NULL;
  588. array[i - 1]->prev = NULL;
  589. if (thread->parent)
  590. thread->parent->child = array[i - 1];
  591. else
  592. top = array[i - 1];
  593. while (--i)
  594. {
  595. array[i - 1]->prev = array[i];
  596. array[i]->next = array[i - 1];
  597. }
  598. }
  599. if (thread->parent)
  600. {
  601. tmp = thread;
  602. thread = thread->parent;
  603. if (!thread->sort_key || thread->sort_children)
  604. {
  605. /* make sort_key the first or last sibling, as appropriate */
  606. sort_key = (!(Sort & SORT_LAST) ^ !(Sort & SORT_REVERSE)) ? thread->child : tmp;
  607. /* we just sorted its children */
  608. thread->sort_children = 0;
  609. oldsort_key = thread->sort_key;
  610. thread->sort_key = thread->message;
  611. if (Sort & SORT_LAST)
  612. {
  613. if (!thread->sort_key
  614. || ((((Sort & SORT_REVERSE) ? 1 : -1)
  615. * compare_threads ((void *) &thread,
  616. (void *) &sort_key))
  617. > 0))
  618. thread->sort_key = sort_key->sort_key;
  619. }
  620. else if (!thread->sort_key)
  621. thread->sort_key = sort_key->sort_key;
  622. /* if its sort_key has changed, we need to resort it and siblings */
  623. if (oldsort_key != thread->sort_key)
  624. {
  625. if (thread->parent)
  626. thread->parent->sort_children = 1;
  627. else
  628. sort_top = 1;
  629. }
  630. }
  631. }
  632. else
  633. {
  634. Sort ^= SORT_REVERSE;
  635. FREE (&array);
  636. return (top);
  637. }
  638. }
  639. thread = thread->next;
  640. }
  641. }
  642. static void check_subjects (CONTEXT *ctx, int init)
  643. {
  644. HEADER *cur;
  645. THREAD *tmp;
  646. int i;
  647. for (i = 0; i < ctx->msgcount; i++)
  648. {
  649. cur = ctx->hdrs[i];
  650. if (cur->thread->check_subject)
  651. cur->thread->check_subject = 0;
  652. else if (!init)
  653. continue;
  654. /* figure out which messages have subjects different than their parents' */
  655. tmp = cur->thread->parent;
  656. while (tmp && !tmp->message)
  657. {
  658. tmp = tmp->parent;
  659. }
  660. if (!tmp)
  661. cur->subject_changed = 1;
  662. else if (cur->env->real_subj && tmp->message->env->real_subj)
  663. cur->subject_changed = mutt_strcmp (cur->env->real_subj,
  664. tmp->message->env->real_subj) ? 1 : 0;
  665. else
  666. cur->subject_changed = (cur->env->real_subj
  667. || tmp->message->env->real_subj) ? 1 : 0;
  668. }
  669. }
  670. void mutt_sort_threads (CONTEXT *ctx, int init)
  671. {
  672. HEADER *cur;
  673. int i, oldsort, using_refs = 0;
  674. THREAD *thread, *new, *tmp, top;
  675. LIST *ref = NULL;
  676. /* set Sort to the secondary method to support the set sort_aux=reverse-*
  677. * settings. The sorting functions just look at the value of
  678. * SORT_REVERSE
  679. */
  680. oldsort = Sort;
  681. Sort = SortAux;
  682. if (!ctx->thread_hash)
  683. init = 1;
  684. if (init)
  685. ctx->thread_hash = hash_create (ctx->msgcount * 2, MUTT_HASH_ALLOW_DUPS);
  686. /* we want a quick way to see if things are actually attached to the top of the
  687. * thread tree or if they're just dangling, so we attach everything to a top
  688. * node temporarily */
  689. top.parent = top.next = top.prev = NULL;
  690. top.child = ctx->tree;
  691. for (thread = ctx->tree; thread; thread = thread->next)
  692. thread->parent = &top;
  693. /* put each new message together with the matching messageless THREAD if it
  694. * exists. otherwise, if there is a THREAD that already has a message, thread
  695. * new message as an identical child. if we didn't attach the message to a
  696. * THREAD, make a new one for it. */
  697. for (i = 0; i < ctx->msgcount; i++)
  698. {
  699. cur = ctx->hdrs[i];
  700. if (!cur->thread)
  701. {
  702. if ((!init || option (OPTDUPTHREADS)) && cur->env->message_id)
  703. thread = hash_find (ctx->thread_hash, cur->env->message_id);
  704. else
  705. thread = NULL;
  706. if (thread && !thread->message)
  707. {
  708. /* this is a message which was missing before */
  709. thread->message = cur;
  710. cur->thread = thread;
  711. thread->check_subject = 1;
  712. /* mark descendants as needing subject_changed checked */
  713. for (tmp = (thread->child ? thread->child : thread); tmp != thread; )
  714. {
  715. while (!tmp->message)
  716. tmp = tmp->child;
  717. tmp->check_subject = 1;
  718. while (!tmp->next && tmp != thread)
  719. tmp = tmp->parent;
  720. if (tmp != thread)
  721. tmp = tmp->next;
  722. }
  723. if (thread->parent)
  724. {
  725. /* remove threading info above it based on its children, which we'll
  726. * recalculate based on its headers. make sure not to leave
  727. * dangling missing messages. note that we haven't kept track
  728. * of what info came from its children and what from its siblings'
  729. * children, so we just remove the stuff that's definitely from it */
  730. do
  731. {
  732. tmp = thread->parent;
  733. unlink_message (&tmp->child, thread);
  734. thread->parent = NULL;
  735. thread->sort_key = NULL;
  736. thread->fake_thread = 0;
  737. thread = tmp;
  738. } while (thread != &top && !thread->child && !thread->message);
  739. }
  740. }
  741. else
  742. {
  743. new = (option (OPTDUPTHREADS) ? thread : NULL);
  744. thread = safe_calloc (1, sizeof (THREAD));
  745. thread->message = cur;
  746. thread->check_subject = 1;
  747. cur->thread = thread;
  748. hash_insert (ctx->thread_hash,
  749. cur->env->message_id ? cur->env->message_id : "",
  750. thread);
  751. if (new)
  752. {
  753. if (new->duplicate_thread)
  754. new = new->parent;
  755. thread = cur->thread;
  756. insert_message (&new->child, new, thread);
  757. thread->duplicate_thread = 1;
  758. thread->message->threaded = 1;
  759. }
  760. }
  761. }
  762. else
  763. {
  764. /* unlink pseudo-threads because they might be children of newly
  765. * arrived messages */
  766. thread = cur->thread;
  767. for (new = thread->child; new; )
  768. {
  769. tmp = new->next;
  770. if (new->fake_thread)
  771. {
  772. unlink_message (&thread->child, new);
  773. insert_message (&top.child, &top, new);
  774. new->fake_thread = 0;
  775. }
  776. new = tmp;
  777. }
  778. }
  779. }
  780. /* thread by references */
  781. for (i = 0; i < ctx->msgcount; i++)
  782. {
  783. cur = ctx->hdrs[i];
  784. if (cur->threaded)
  785. continue;
  786. cur->threaded = 1;
  787. thread = cur->thread;
  788. using_refs = 0;
  789. while (1)
  790. {
  791. if (using_refs == 0)
  792. {
  793. /* look at the beginning of in-reply-to: */
  794. if ((ref = cur->env->in_reply_to) != NULL)
  795. using_refs = 1;
  796. else
  797. {
  798. ref = cur->env->references;
  799. using_refs = 2;
  800. }
  801. }
  802. else if (using_refs == 1)
  803. {
  804. /* if there's no references header, use all the in-reply-to:
  805. * data that we have. otherwise, use the first reference
  806. * if it's different than the first in-reply-to, otherwise use
  807. * the second reference (since at least eudora puts the most
  808. * recent reference in in-reply-to and the rest in references)
  809. */
  810. if (!cur->env->references)
  811. ref = ref->next;
  812. else
  813. {
  814. if (mutt_strcmp (ref->data, cur->env->references->data))
  815. ref = cur->env->references;
  816. else
  817. ref = cur->env->references->next;
  818. using_refs = 2;
  819. }
  820. }
  821. else
  822. ref = ref->next; /* go on with references */
  823. if (!ref)
  824. break;
  825. if ((new = hash_find (ctx->thread_hash, ref->data)) == NULL)
  826. {
  827. new = safe_calloc (1, sizeof (THREAD));
  828. hash_insert (ctx->thread_hash, ref->data, new);
  829. }
  830. else
  831. {
  832. if (new->duplicate_thread)
  833. new = new->parent;
  834. if (is_descendant (new, thread)) /* no loops! */
  835. continue;
  836. }
  837. if (thread->parent)
  838. unlink_message (&top.child, thread);
  839. insert_message (&new->child, new, thread);
  840. thread = new;
  841. if (thread->message || (thread->parent && thread->parent != &top))
  842. break;
  843. }
  844. if (!thread->parent)
  845. insert_message (&top.child, &top, thread);
  846. }
  847. /* detach everything from the temporary top node */
  848. for (thread = top.child; thread; thread = thread->next)
  849. {
  850. thread->parent = NULL;
  851. }
  852. ctx->tree = top.child;
  853. check_subjects (ctx, init);
  854. if (!option (OPTSTRICTTHREADS))
  855. pseudo_threads (ctx);
  856. if (ctx->tree)
  857. {
  858. ctx->tree = mutt_sort_subthreads (ctx->tree, init);
  859. /* restore the oldsort order. */
  860. Sort = oldsort;
  861. /* Put the list into an array. */
  862. linearize_tree (ctx);
  863. /* Draw the thread tree. */
  864. mutt_draw_tree (ctx);
  865. }
  866. }
  867. static HEADER *find_virtual (THREAD *cur, int reverse)
  868. {
  869. THREAD *top;
  870. if (cur->message && cur->message->virtual >= 0)
  871. return (cur->message);
  872. top = cur;
  873. if ((cur = cur->child) == NULL)
  874. return (NULL);
  875. while (reverse && cur->next)
  876. cur = cur->next;
  877. FOREVER
  878. {
  879. if (cur->message && cur->message->virtual >= 0)
  880. return (cur->message);
  881. if (cur->child)
  882. {
  883. cur = cur->child;
  884. while (reverse && cur->next)
  885. cur = cur->next;
  886. }
  887. else if (reverse ? cur->prev : cur->next)
  888. cur = reverse ? cur->prev : cur->next;
  889. else
  890. {
  891. while (!(reverse ? cur->prev : cur->next))
  892. {
  893. cur = cur->parent;
  894. if (cur == top)
  895. return (NULL);
  896. }
  897. cur = reverse ? cur->prev : cur->next;
  898. }
  899. /* not reached */
  900. }
  901. }
  902. /* dir => true when moving forward, false when moving in reverse
  903. * subthreads => false when moving to next thread, true when moving to next subthread
  904. */
  905. int _mutt_aside_thread (HEADER *hdr, short dir, short subthreads)
  906. {
  907. THREAD *cur;
  908. HEADER *tmp;
  909. if ((Sort & SORT_MASK) != SORT_THREADS)
  910. {
  911. mutt_error _("Threading is not enabled.");
  912. return (hdr->virtual);
  913. }
  914. cur = hdr->thread;
  915. if (!subthreads)
  916. {
  917. while (cur->parent)
  918. cur = cur->parent;
  919. }
  920. else
  921. {
  922. if ((dir != 0) ^ ((Sort & SORT_REVERSE) != 0))
  923. {
  924. while (!cur->next && cur->parent)
  925. cur = cur->parent;
  926. }
  927. else
  928. {
  929. while (!cur->prev && cur->parent)
  930. cur = cur->parent;
  931. }
  932. }
  933. if ((dir != 0) ^ ((Sort & SORT_REVERSE) != 0))
  934. {
  935. do
  936. {
  937. cur = cur->next;
  938. if (!cur)
  939. return (-1);
  940. tmp = find_virtual (cur, 0);
  941. } while (!tmp);
  942. }
  943. else
  944. {
  945. do
  946. {
  947. cur = cur->prev;
  948. if (!cur)
  949. return (-1);
  950. tmp = find_virtual (cur, 1);
  951. } while (!tmp);
  952. }
  953. return (tmp->virtual);
  954. }
  955. int mutt_parent_message (CONTEXT *ctx, HEADER *hdr, int find_root)
  956. {
  957. THREAD *thread;
  958. HEADER *parent = NULL;
  959. if ((Sort & SORT_MASK) != SORT_THREADS)
  960. {
  961. mutt_error _("Threading is not enabled.");
  962. return (hdr->virtual);
  963. }
  964. /* Root may be the current message */
  965. if (find_root)
  966. parent = hdr;
  967. for (thread = hdr->thread->parent; thread; thread = thread->parent)
  968. {
  969. if ((hdr = thread->message) != NULL)
  970. {
  971. parent = hdr;
  972. if (!find_root)
  973. break;
  974. }
  975. }
  976. if (!parent)
  977. {
  978. mutt_error _("Parent message is not available.");
  979. return (-1);
  980. }
  981. if (!VISIBLE (parent, ctx))
  982. {
  983. if (find_root)
  984. mutt_error _("Root message is not visible in this limited view.");
  985. else
  986. mutt_error _("Parent message is not visible in this limited view.");
  987. return (-1);
  988. }
  989. return (parent->virtual);
  990. }
  991. void mutt_set_virtual (CONTEXT *ctx)
  992. {
  993. int i;
  994. HEADER *cur;
  995. ctx->vcount = 0;
  996. ctx->vsize = 0;
  997. for (i = 0; i < ctx->msgcount; i++)
  998. {
  999. cur = ctx->hdrs[i];
  1000. if (cur->virtual >= 0)
  1001. {
  1002. cur->virtual = ctx->vcount;
  1003. ctx->v2r[ctx->vcount] = i;
  1004. ctx->vcount++;
  1005. ctx->vsize += cur->content->length + cur->content->offset - cur->content->hdr_offset;
  1006. cur->num_hidden = mutt_get_hidden (ctx, cur);
  1007. }
  1008. }
  1009. }
  1010. int _mutt_traverse_thread (CONTEXT *ctx, HEADER *cur, int flag)
  1011. {
  1012. THREAD *thread, *top;
  1013. HEADER *roothdr = NULL;
  1014. int final, reverse = (Sort & SORT_REVERSE), minmsgno;
  1015. int num_hidden = 0, new = 0, old = 0;
  1016. int min_unread_msgno = INT_MAX, min_unread = cur->virtual;
  1017. #define CHECK_LIMIT (!ctx->pattern || cur->limited)
  1018. if ((Sort & SORT_MASK) != SORT_THREADS && !(flag & MUTT_THREAD_GET_HIDDEN))
  1019. {
  1020. mutt_error (_("Threading is not enabled."));
  1021. return (cur->virtual);
  1022. }
  1023. final = cur->virtual;
  1024. thread = cur->thread;
  1025. while (thread->parent)
  1026. thread = thread->parent;
  1027. top = thread;
  1028. while (!thread->message)
  1029. thread = thread->child;
  1030. cur = thread->message;
  1031. minmsgno = cur->msgno;
  1032. if (!cur->read && CHECK_LIMIT)
  1033. {
  1034. if (cur->old)
  1035. old = 2;
  1036. else
  1037. new = 1;
  1038. if (cur->msgno < min_unread_msgno)
  1039. {
  1040. min_unread = cur->virtual;
  1041. min_unread_msgno = cur->msgno;
  1042. }
  1043. }
  1044. if (cur->virtual == -1 && CHECK_LIMIT)
  1045. num_hidden++;
  1046. if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
  1047. {
  1048. cur->pair = 0; /* force index entry's color to be re-evaluated */
  1049. cur->collapsed = flag & MUTT_THREAD_COLLAPSE;
  1050. if (cur->virtual != -1)
  1051. {
  1052. roothdr = cur;
  1053. if (flag & MUTT_THREAD_COLLAPSE)
  1054. final = roothdr->virtual;
  1055. }
  1056. }
  1057. if (thread == top && (thread = thread->child) == NULL)
  1058. {
  1059. /* return value depends on action requested */
  1060. if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
  1061. return (final);
  1062. else if (flag & MUTT_THREAD_UNREAD)
  1063. return ((old && new) ? new : (old ? old : new));
  1064. else if (flag & MUTT_THREAD_GET_HIDDEN)
  1065. return (num_hidden);
  1066. else if (flag & MUTT_THREAD_NEXT_UNREAD)
  1067. return (min_unread);
  1068. }
  1069. FOREVER
  1070. {
  1071. cur = thread->message;
  1072. if (cur)
  1073. {
  1074. if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
  1075. {
  1076. cur->pair = 0; /* force index entry's color to be re-evaluated */
  1077. cur->collapsed = flag & MUTT_THREAD_COLLAPSE;
  1078. if (!roothdr && CHECK_LIMIT)
  1079. {
  1080. roothdr = cur;
  1081. if (flag & MUTT_THREAD_COLLAPSE)
  1082. final = roothdr->virtual;
  1083. }
  1084. if (reverse && (flag & MUTT_THREAD_COLLAPSE) && (cur->msgno < minmsgno) && CHECK_LIMIT)
  1085. {
  1086. minmsgno = cur->msgno;
  1087. final = cur->virtual;
  1088. }
  1089. if (flag & MUTT_THREAD_COLLAPSE)
  1090. {
  1091. if (cur != roothdr)
  1092. cur->virtual = -1;
  1093. }
  1094. else
  1095. {
  1096. if (CHECK_LIMIT)
  1097. cur->virtual = cur->msgno;
  1098. }
  1099. }
  1100. if (!cur->read && CHECK_LIMIT)
  1101. {
  1102. if (cur->old)
  1103. old = 2;
  1104. else
  1105. new = 1;
  1106. if (cur->msgno < min_unread_msgno)
  1107. {
  1108. min_unread = cur->virtual;
  1109. min_unread_msgno = cur->msgno;
  1110. }
  1111. }
  1112. if (cur->virtual == -1 && CHECK_LIMIT)
  1113. num_hidden++;
  1114. }
  1115. if (thread->child)
  1116. thread = thread->child;
  1117. else if (thread->next)
  1118. thread = thread->next;
  1119. else
  1120. {
  1121. int done = 0;
  1122. while (!thread->next)
  1123. {
  1124. thread = thread->parent;
  1125. if (thread == top)
  1126. {
  1127. done = 1;
  1128. break;
  1129. }
  1130. }
  1131. if (done)
  1132. break;
  1133. thread = thread->next;
  1134. }
  1135. }
  1136. /* return value depends on action requested */
  1137. if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
  1138. return (final);
  1139. else if (flag & MUTT_THREAD_UNREAD)
  1140. return ((old && new) ? new : (old ? old : new));
  1141. else if (flag & MUTT_THREAD_GET_HIDDEN)
  1142. return (num_hidden+1);
  1143. else if (flag & MUTT_THREAD_NEXT_UNREAD)
  1144. return (min_unread);
  1145. return (0);
  1146. #undef CHECK_LIMIT
  1147. }
  1148. /* if flag is 0, we want to know how many messages
  1149. * are in the thread. if flag is 1, we want to know
  1150. * our position in the thread. */
  1151. int mutt_messages_in_thread (CONTEXT *ctx, HEADER *hdr, int flag)
  1152. {
  1153. THREAD *threads[2];
  1154. int i, rc;
  1155. if ((Sort & SORT_MASK) != SORT_THREADS || !hdr->thread)
  1156. return (1);
  1157. threads[0] = hdr->thread;
  1158. while (threads[0]->parent)
  1159. threads[0] = threads[0]->parent;
  1160. threads[1] = flag ? hdr->thread : threads[0]->next;
  1161. for (i = 0; i < ((flag || !threads[1]) ? 1 : 2); i++)
  1162. {
  1163. while (!threads[i]->message)
  1164. threads[i] = threads[i]->child;
  1165. }
  1166. if (Sort & SORT_REVERSE)
  1167. rc = threads[0]->message->msgno - (threads[1] ? threads[1]->message->msgno : -1);
  1168. else
  1169. rc = (threads[1] ? threads[1]->message->msgno : ctx->msgcount) - threads[0]->message->msgno;
  1170. if (flag)
  1171. rc += 1;
  1172. return (rc);
  1173. }
  1174. HASH *mutt_make_id_hash (CONTEXT *ctx)
  1175. {
  1176. int i;
  1177. HEADER *hdr;
  1178. HASH *hash;
  1179. hash = hash_create (ctx->msgcount * 2, 0);
  1180. for (i = 0; i < ctx->msgcount; i++)
  1181. {
  1182. hdr = ctx->hdrs[i];
  1183. if (hdr->env->message_id)
  1184. hash_insert (hash, hdr->env->message_id, hdr);
  1185. }
  1186. return hash;
  1187. }
  1188. HASH *mutt_make_subj_hash (CONTEXT *ctx)
  1189. {
  1190. int i;
  1191. HEADER *hdr;
  1192. HASH *hash;
  1193. hash = hash_create (ctx->msgcount * 2, MUTT_HASH_ALLOW_DUPS);
  1194. for (i = 0; i < ctx->msgcount; i++)
  1195. {
  1196. hdr = ctx->hdrs[i];
  1197. if (hdr->env->real_subj)
  1198. hash_insert (hash, hdr->env->real_subj, hdr);
  1199. }
  1200. return hash;
  1201. }
  1202. static void clean_references (THREAD *brk, THREAD *cur)
  1203. {
  1204. THREAD *p;
  1205. LIST *ref = NULL;
  1206. int done = 0;
  1207. for (; cur; cur = cur->next, done = 0)
  1208. {
  1209. /* parse subthread recursively */
  1210. clean_references (brk, cur->child);
  1211. if (!cur->message)
  1212. break; /* skip pseudo-message */
  1213. /* Looking for the first bad reference according to the new threading.
  1214. * Optimal since Mutt stores the references in reverse order, and the
  1215. * first loop should match immediately for mails respecting RFC2822. */
  1216. for (p = brk; !done && p; p = p->parent)
  1217. for (ref = cur->message->env->references; p->message && ref; ref = ref->next)
  1218. if (!mutt_strcasecmp (ref->data, p->message->env->message_id))
  1219. {
  1220. done = 1;
  1221. break;
  1222. }
  1223. if (done)
  1224. {
  1225. HEADER *h = cur->message;
  1226. /* clearing the References: header from obsolete Message-ID(s) */
  1227. mutt_free_list (&ref->next);
  1228. h->env->refs_changed = h->changed = 1;
  1229. }
  1230. }
  1231. }
  1232. void mutt_break_thread (HEADER *hdr)
  1233. {
  1234. mutt_free_list (&hdr->env->in_reply_to);
  1235. mutt_free_list (&hdr->env->references);
  1236. hdr->env->irt_changed = hdr->env->refs_changed = hdr->changed = 1;
  1237. clean_references (hdr->thread, hdr->thread->child);
  1238. }
  1239. static int link_threads (HEADER *parent, HEADER *child, CONTEXT *ctx)
  1240. {
  1241. if (child == parent)
  1242. return 0;
  1243. mutt_break_thread (child);
  1244. child->env->in_reply_to = mutt_new_list ();
  1245. child->env->in_reply_to->data = safe_strdup (parent->env->message_id);
  1246. mutt_set_flag (ctx, child, MUTT_TAG, 0);
  1247. child->env->irt_changed = child->changed = 1;
  1248. return 1;
  1249. }
  1250. int mutt_link_threads (HEADER *cur, HEADER *last, CONTEXT *ctx)
  1251. {
  1252. int i, changed = 0;
  1253. if (!last)
  1254. {
  1255. for (i = 0; i < ctx->vcount; i++)
  1256. if (ctx->hdrs[Context->v2r[i]]->tagged)
  1257. changed |= link_threads (cur, ctx->hdrs[Context->v2r[i]], ctx);
  1258. }
  1259. else
  1260. changed = link_threads (cur, last, ctx);
  1261. return changed;
  1262. }