Merge branch 'ical'
[alpine.git] / pith / thread.c
blobd2bec483a3c2ace9a1f4f17126b4545f8f110e2e
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: thread.c 942 2008-03-04 18:21:33Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2017 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "../pith/headers.h"
20 #include "../pith/thread.h"
21 #include "../pith/flag.h"
22 #include "../pith/icache.h"
23 #include "../pith/mailindx.h"
24 #include "../pith/msgno.h"
25 #include "../pith/sort.h"
26 #include "../pith/pineelt.h"
27 #include "../pith/status.h"
28 #include "../pith/news.h"
29 #include "../pith/search.h"
30 #include "../pith/mailcmd.h"
31 #include "../pith/ablookup.h"
35 * Internal prototypes
37 long *sort_thread_flatten(THREADNODE *, MAILSTREAM *, long *,
38 char *, long, PINETHRD_S *, unsigned);
39 void make_thrdflags_consistent(MAILSTREAM *, MSGNO_S *, PINETHRD_S *, int);
40 THREADNODE *collapse_threadnode_tree(THREADNODE *);
41 THREADNODE *collapse_threadnode_tree_sorted(THREADNODE *);
42 THREADNODE *sort_threads_and_collapse(THREADNODE *);
43 THREADNODE *insert_tree_in_place(THREADNODE *, THREADNODE *);
44 unsigned long branch_greatest_num(THREADNODE *, int);
45 long calculate_visible_threads(MAILSTREAM *);
48 PINETHRD_S *
49 fetch_thread(MAILSTREAM *stream, long unsigned int rawno)
51 MESSAGECACHE *mc;
52 PINELT_S *pelt;
53 PINETHRD_S *thrd = NULL;
55 if(stream && rawno > 0L && rawno <= stream->nmsgs
56 && !sp_need_to_rethread(stream)){
57 mc = (rawno > 0L && stream && rawno <= stream->nmsgs)
58 ? mail_elt(stream, rawno) : NULL;
59 if(mc && (pelt = (PINELT_S *) mc->sparep))
60 thrd = pelt->pthrd;
63 return(thrd);
67 PINETHRD_S *
68 fetch_head_thread(MAILSTREAM *stream)
70 unsigned long rawno;
71 PINETHRD_S *thrd = NULL;
73 if(stream){
74 /* first find any thread */
75 for(rawno = 1L; !thrd && rawno <= stream->nmsgs; rawno++)
76 thrd = fetch_thread(stream, rawno);
78 if(thrd && thrd->head)
79 thrd = fetch_thread(stream, thrd->head);
82 return(thrd);
87 * Set flag f to v for all messages in thrd.
89 * Watch out when calling this. The thrd->branch is not part of thrd.
90 * Branch is a sibling to thrd, not a child. Zero out branch before calling
91 * or call on thrd->next and worry about thrd separately.
92 * Ok to call it on top-level thread which has no branch already.
94 void
95 set_flags_for_thread(MAILSTREAM *stream, MSGNO_S *msgmap, int f, PINETHRD_S *thrd, int v)
97 PINETHRD_S *nthrd, *bthrd;
99 if(!(stream && thrd && msgmap))
100 return;
102 set_lflag(stream, msgmap, mn_raw2m(msgmap, thrd->rawno), f, v);
104 if(thrd->next){
105 nthrd = fetch_thread(stream, thrd->next);
106 if(nthrd)
107 set_flags_for_thread(stream, msgmap, f, nthrd, v);
110 if(thrd->branch){
111 bthrd = fetch_thread(stream, thrd->branch);
112 if(bthrd)
113 set_flags_for_thread(stream, msgmap, f, bthrd, v);
118 void
119 erase_threading_info(MAILSTREAM *stream, MSGNO_S *msgmap)
121 unsigned long n;
122 MESSAGECACHE *mc;
123 PINELT_S *peltp;
125 if(!(stream && stream->spare))
126 return;
128 ps_global->view_skipped_index = 0;
129 sp_set_viewing_a_thread(stream, 0);
131 if(THRD_INDX())
132 setup_for_thread_index_screen();
133 else
134 setup_for_index_index_screen();
136 stream->spare = 0;
138 for(n = 1L; n <= stream->nmsgs; n++){
139 set_lflag(stream, msgmap, mn_raw2m(msgmap, n),
140 MN_COLL | MN_CHID | MN_CHID2, 0);
141 mc = mail_elt(stream, n);
142 if(mc && mc->sparep){
143 peltp = (PINELT_S *) mc->sparep;
144 if(peltp->pthrd)
145 fs_give((void **) &peltp->pthrd);
151 void
152 sort_thread_callback(MAILSTREAM *stream, THREADNODE *tree)
154 THREADNODE *collapsed_tree = NULL;
155 PINETHRD_S *thrd = NULL;
156 unsigned long msgno, rawno;
157 int un_view_thread = 0;
158 long raw_current;
159 char *dup_chk = NULL;
162 dprint((2, "sort_thread_callback\n"));
164 g_sort.msgmap->max_thrdno = 0L;
167 * Eliminate dummy nodes from tree and collapse the tree in a logical
168 * way. If the dummy node is at the top-level, then its children are
169 * promoted to the top-level as separate threads.
171 if(F_ON(F_THREAD_SORTS_BY_ARRIVAL, ps_global))
172 collapsed_tree = collapse_threadnode_tree_sorted(tree);
173 else
174 collapsed_tree = collapse_threadnode_tree(tree);
176 /* dup_chk is like sort with an origin of 1 */
177 dup_chk = (char *) fs_get((mn_get_nmsgs(g_sort.msgmap)+1) * sizeof(char));
178 memset(dup_chk, 0, (mn_get_nmsgs(g_sort.msgmap)+1) * sizeof(char));
180 memset(&g_sort.msgmap->sort[1], 0, mn_get_total(g_sort.msgmap) * sizeof(long));
182 (void) sort_thread_flatten(collapsed_tree, stream,
183 &g_sort.msgmap->sort[1],
184 dup_chk, mn_get_nmsgs(g_sort.msgmap),
185 NULL, THD_TOP);
187 /* reset the inverse array */
188 msgno_reset_isort(g_sort.msgmap);
190 if(dup_chk)
191 fs_give((void **) &dup_chk);
193 if(collapsed_tree)
194 mail_free_threadnode(&collapsed_tree);
196 if(any_lflagged(g_sort.msgmap, MN_HIDE))
197 g_sort.msgmap->visible_threads = calculate_visible_threads(stream);
198 else
199 g_sort.msgmap->visible_threads = g_sort.msgmap->max_thrdno;
201 raw_current = mn_m2raw(g_sort.msgmap, mn_get_cur(g_sort.msgmap));
203 sp_set_need_to_rethread(stream, 0);
206 * Set appropriate bits to start out collapsed if desired. We use the
207 * stream spare bit to tell us if we've done this before for this
208 * stream.
210 if(!stream->spare
211 && (COLL_THRDS() || SEP_THRDINDX())
212 && mn_get_total(g_sort.msgmap) > 1L){
214 collapse_threads(stream, g_sort.msgmap, NULL);
216 else if(stream->spare){
219 * If we're doing auto collapse then new threads need to have
220 * their collapse bit set. This happens below if we're in the
221 * thread index, but if we're in the regular index with auto
222 * collapse we have to look for these.
224 if(any_lflagged(g_sort.msgmap, MN_USOR)){
225 if(COLL_THRDS()){
226 for(msgno = 1L; msgno <= mn_get_total(g_sort.msgmap); msgno++){
227 rawno = mn_m2raw(g_sort.msgmap, msgno);
228 if(get_lflag(stream, NULL, rawno, MN_USOR)){
229 thrd = fetch_thread(stream, rawno);
232 * Node is new, unsorted, top-level thread,
233 * and we're using auto collapse.
235 if(thrd && !thrd->parent)
236 set_lflag(stream, g_sort.msgmap, msgno, MN_COLL, 1);
239 * If a parent is collapsed, clear that parent's
240 * index cache entry. This is only necessary if
241 * the parent's index display can depend on its
242 * children, of course.
244 if(thrd && thrd->parent){
245 thrd = fetch_thread(stream, thrd->parent);
246 while(thrd){
247 long t;
249 if(get_lflag(stream, NULL, thrd->rawno, MN_COLL)
250 && (t = mn_raw2m(g_sort.msgmap,
251 (long) thrd->rawno)))
252 clear_index_cache_ent(stream, t, 0);
254 if(thrd->parent)
255 thrd = fetch_thread(stream, thrd->parent);
256 else
257 thrd = NULL;
265 set_lflags(stream, g_sort.msgmap, MN_USOR, 0);
268 if(sp_viewing_a_thread(stream)){
269 if(any_lflagged(g_sort.msgmap, MN_CHID2)){
270 /* current should be part of viewed thread */
271 if(get_lflag(stream, NULL, raw_current, MN_CHID2)){
272 thrd = fetch_thread(stream, raw_current);
273 if(thrd && thrd->top && thrd->top != thrd->rawno)
274 thrd = fetch_thread(stream, thrd->top);
276 if(thrd){
278 * For messages that are part of thread set MN_CHID2
279 * and for messages that aren't part of the thread
280 * clear MN_CHID2. Easiest is to just do it instead
281 * of checking if it is true first.
283 set_lflags(stream, g_sort.msgmap, MN_CHID2, 0);
284 set_thread_lflags(stream, thrd, g_sort.msgmap,
285 MN_CHID2, 1);
288 * Outside of the viewed thread everything else
289 * should be collapsed at the top-levels.
291 collapse_threads(stream, g_sort.msgmap, thrd);
294 * Inside of the thread, the top of the thread
295 * can't be hidden, the rest are hidden if a
296 * parent somewhere above them is collapsed.
297 * There can be collapse points that are hidden
298 * inside of the tree. They remain collapsed even
299 * if the parent above them uncollapses.
301 msgno = mn_raw2m(g_sort.msgmap, (long) thrd->rawno);
302 if(msgno)
303 set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0);
305 if(thrd->next){
306 PINETHRD_S *nthrd;
308 nthrd = fetch_thread(stream, thrd->next);
309 if(nthrd)
310 make_thrdflags_consistent(stream, g_sort.msgmap,
311 nthrd,
312 get_lflag(stream, NULL,
313 thrd->rawno,
314 MN_COLL));
317 else
318 un_view_thread++;
320 else
321 un_view_thread++;
323 else
324 un_view_thread++;
326 if(un_view_thread){
327 set_lflags(stream, g_sort.msgmap, MN_CHID2, 0);
328 unview_thread(ps_global, stream, g_sort.msgmap);
330 else{
331 mn_reset_cur(g_sort.msgmap,
332 mn_raw2m(g_sort.msgmap, raw_current));
333 view_thread(ps_global, stream, g_sort.msgmap, 0);
336 else if(SEP_THRDINDX()){
337 set_lflags(stream, g_sort.msgmap, MN_CHID2, 0);
338 collapse_threads(stream, g_sort.msgmap, NULL);
340 else{
341 thrd = fetch_head_thread(stream);
342 while(thrd){
344 * The top-level threads aren't hidden by collapse.
346 msgno = mn_raw2m(g_sort.msgmap, thrd->rawno);
347 if(msgno)
348 set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0);
350 if(thrd->next){
351 PINETHRD_S *nthrd;
353 nthrd = fetch_thread(stream, thrd->next);
354 if(nthrd)
355 make_thrdflags_consistent(stream, g_sort.msgmap,
356 nthrd,
357 get_lflag(stream, NULL,
358 thrd->rawno,
359 MN_COLL));
362 if(thrd->nextthd)
363 thrd = fetch_thread(stream, thrd->nextthd);
364 else
365 thrd = NULL;
370 stream->spare = 1;
372 dprint((2, "sort_thread_callback done\n"));
376 void
377 collapse_threads(MAILSTREAM *stream, MSGNO_S *msgmap, PINETHRD_S *not_this_thread)
379 PINETHRD_S *thrd = NULL, *nthrd;
380 unsigned long msgno;
382 dprint((9, "collapse_threads\n"));
384 thrd = fetch_head_thread(stream);
385 while(thrd){
386 if(thrd != not_this_thread){
387 msgno = mn_raw2m(g_sort.msgmap, thrd->rawno);
389 /* set collapsed bit */
390 if(msgno){
391 set_lflag(stream, g_sort.msgmap, msgno, MN_COLL, 1);
392 set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0);
395 /* hide its children */
396 if(thrd->next && (nthrd = fetch_thread(stream, thrd->next)))
397 set_thread_subtree(stream, nthrd, msgmap, 1, MN_CHID);
400 if(thrd->nextthd)
401 thrd = fetch_thread(stream, thrd->nextthd);
402 else
403 thrd = NULL;
406 dprint((9, "collapse_threads done\n"));
410 void
411 make_thrdflags_consistent(MAILSTREAM *stream, MSGNO_S *msgmap, PINETHRD_S *thrd,
412 int a_parent_is_collapsed)
414 PINETHRD_S *nthrd, *bthrd;
415 unsigned long msgno;
417 if(!thrd)
418 return;
420 msgno = mn_raw2m(msgmap, thrd->rawno);
422 if(a_parent_is_collapsed){
423 /* if some parent is collapsed, we should be hidden */
424 if(msgno)
425 set_lflag(stream, msgmap, msgno, MN_CHID, 1);
427 else{
428 /* no parent is collapsed so we are not hidden */
429 if(msgno)
430 set_lflag(stream, msgmap, msgno, MN_CHID, 0);
433 if(thrd->next){
434 nthrd = fetch_thread(stream, thrd->next);
435 if(nthrd)
436 make_thrdflags_consistent(stream, msgmap, nthrd,
437 a_parent_is_collapsed
438 ? a_parent_is_collapsed
439 : get_lflag(stream, NULL, thrd->rawno,
440 MN_COLL));
443 if(thrd->branch){
444 bthrd = fetch_thread(stream, thrd->branch);
445 if(bthrd)
446 make_thrdflags_consistent(stream, msgmap, bthrd,
447 a_parent_is_collapsed);
452 long
453 calculate_visible_threads(MAILSTREAM *stream)
455 PINETHRD_S *thrd = NULL;
456 long vis = 0L;
458 thrd = fetch_head_thread(stream);
459 while(thrd){
460 vis += (thread_has_some_visible(stream, thrd) ? 1 : 0);
462 if(thrd->nextthd)
463 thrd = fetch_thread(stream, thrd->nextthd);
464 else
465 thrd = NULL;
468 return(vis);
473 * This routine does a couple things. The input is the THREADNODE node
474 * that we get from c-client because of the THREAD command. The rest of
475 * the arguments are used to help guide this function through its
476 * recursive steps. One thing it does is to place the sort order in
477 * the array initially pointed to by the entry argument. All it is doing
478 * is walking the tree in the next then branch order you see below and
479 * incrementing the entry number one for each node. The other thing it
480 * is doing at the same time is to create a PINETHRD_S tree from the
481 * THREADNODE tree. The two trees are completely equivalent but the
482 * PINETHRD_S version has additional back pointers and parent pointers
483 * and so on to make it easier for alpine to deal with it. Each node
484 * of that tree is tied to the data associated with a particular message
485 * by the msgno_thread_info() call, so that we can go from a message
486 * number to the place in the thread tree that message sits.
488 long *
489 sort_thread_flatten(THREADNODE *node, MAILSTREAM *stream,
490 long *entry, char *dup_chk, long maxno,
491 PINETHRD_S *thrd, unsigned int flags)
493 PINETHRD_S *newthrd = NULL;
495 if(node){
496 if(node->num > 0L && node->num <= maxno){ /* holes happen */
497 if(!dup_chk[node->num]){ /* not a duplicate */
498 *entry = node->num;
499 dup_chk[node->num] = 1;
502 * Build a richer threading structure that will help us paint
503 * and operate on threads and subthreads.
505 newthrd = msgno_thread_info(stream, node->num, thrd, flags);
506 if(newthrd){
507 entry++;
509 if(node->next)
510 entry = sort_thread_flatten(node->next, stream,
511 entry, dup_chk, maxno,
512 newthrd, THD_NEXT);
514 if(node->branch)
515 entry = sort_thread_flatten(node->branch, stream,
516 entry, dup_chk, maxno,
517 newthrd,
518 (flags == THD_TOP) ? THD_TOP
519 : THD_BRANCH);
525 return(entry);
530 * Make a copy of c-client's THREAD tree while eliminating dummy nodes.
532 THREADNODE *
533 collapse_threadnode_tree(THREADNODE *tree)
535 THREADNODE *newtree = NULL;
537 if(tree){
538 if(tree->num){
539 newtree = mail_newthreadnode(NULL);
540 newtree->num = tree->num;
541 if(tree->next)
542 newtree->next = collapse_threadnode_tree(tree->next);
544 if(tree->branch)
545 newtree->branch = collapse_threadnode_tree(tree->branch);
547 else{
548 if(tree->next)
549 newtree = collapse_threadnode_tree(tree->next);
551 if(tree->branch){
552 if(newtree){
553 THREADNODE *last_branch = NULL;
556 * Next moved up to replace "tree" in the tree.
557 * If next has no branches, then we want to branch off
558 * of next. If next has branches, we want to branch off
559 * of the last of those branches instead.
561 last_branch = newtree;
562 while(last_branch->branch)
563 last_branch = last_branch->branch;
565 last_branch->branch = collapse_threadnode_tree(tree->branch);
567 else
568 newtree = collapse_threadnode_tree(tree->branch);
573 return(newtree);
578 * Like collapse_threadnode_tree, we collapse the dummy nodes.
579 * In addition we rearrange the threads by order of arrival of
580 * the last message in the thread, rather than the first message
581 * in the thread.
583 THREADNODE *
584 collapse_threadnode_tree_sorted(THREADNODE *tree)
586 THREADNODE *sorted_tree = NULL;
588 sorted_tree = sort_threads_and_collapse(tree);
591 * We used to eliminate top-level dummy nodes here so that
592 * orphans would still get sorted together, but we changed
593 * to sort the orphans themselves as top-level threads.
595 * It might be a matter of choice how they get sorted, but
596 * we'll try doing it this way and not add another feature.
599 return(sorted_tree);
603 * Recurse through the tree, sorting each top-level branch by the
604 * greatest num in the thread.
606 THREADNODE *
607 sort_threads_and_collapse(THREADNODE *tree)
609 THREADNODE *newtree = NULL, *newbranchtree = NULL, *newtreefree = NULL;
611 if(tree){
612 newtree = mail_newthreadnode(NULL);
613 newtree->num = tree->num;
616 * Only sort at the top level. Individual threads can
617 * rely on collapse_threadnode_tree
619 if(tree->next)
620 newtree->next = collapse_threadnode_tree(tree->next);
622 if(tree->branch){
624 * This recursive call returns an already re-sorted tree.
625 * With that, we can loop through and inject ourselves
626 * where we fit in with that sort, and pass back to the
627 * caller to inject themselves.
629 newbranchtree = sort_threads_and_collapse(tree->branch);
632 if(newtree->num)
633 newtree = insert_tree_in_place(newtree, newbranchtree);
634 else{
636 * If top node is a dummy, here is where we collapse it.
638 newtreefree = newtree;
639 newtree = insert_tree_in_place(newtree->next, newbranchtree);
640 newtreefree->next = NULL;
641 mail_free_threadnode(&newtreefree);
645 return(newtree);
649 * Recursively insert each of the top-level nodes in newtree in their place
650 * in tree according to which tree has the most recent arrival
652 THREADNODE *
653 insert_tree_in_place(THREADNODE *newtree, THREADNODE *tree)
655 THREADNODE *node = NULL;
656 unsigned long newtree_greatest_num = 0;
657 if(newtree->branch){
658 node = newtree->branch;
659 newtree->branch = NULL;
660 tree = insert_tree_in_place(node, tree);
663 newtree_greatest_num = branch_greatest_num(newtree, 0);
665 if(tree){
667 * Since tree is already sorted, we can insert when we find something
668 * newtree is less than
670 if(newtree_greatest_num < branch_greatest_num(tree, 0))
671 newtree->branch = tree;
672 else {
673 for(node = tree; node->branch; node = node->branch){
674 if(newtree_greatest_num < branch_greatest_num(node->branch, 0)){
675 newtree->branch = node->branch;
676 node->branch = newtree;
677 break;
680 if(!node->branch)
681 node->branch = newtree;
683 newtree = tree;
687 return(newtree);
691 * Given a thread, return the greatest num in the tree.
692 * is_subthread tells us not to recurse through branches, so
693 * we can split the top level into threads.
695 unsigned long
696 branch_greatest_num(THREADNODE *tree, int is_subthread)
698 unsigned long ret, branch_ret;
700 ret = tree->num;
702 if(tree->next && (branch_ret = branch_greatest_num(tree->next, 1)) > ret)
703 ret = branch_ret;
704 if(is_subthread && tree->branch &&
705 (branch_ret = branch_greatest_num(tree->branch, 1)) > ret)
706 ret = branch_ret;
708 return ret;
713 * Args stream -- the usual
714 * rawno -- the raw msg num associated with this new node
715 * attached_to_thrd -- the PINETHRD_S node that this is either a next or branch
716 * off of
717 * flags --
719 PINETHRD_S *
720 msgno_thread_info(MAILSTREAM *stream, long unsigned int rawno,
721 PINETHRD_S *attached_to_thrd, unsigned int flags)
723 PINELT_S **peltp;
724 MESSAGECACHE *mc;
726 if(!stream || rawno < 1L || rawno > stream->nmsgs)
727 return NULL;
730 * any private elt data yet?
732 if((mc = mail_elt(stream, rawno))
733 && (*(peltp = (PINELT_S **) &mc->sparep) == NULL)){
734 *peltp = (PINELT_S *) fs_get(sizeof(PINELT_S));
735 memset(*peltp, 0, sizeof(PINELT_S));
738 if((*peltp)->pthrd == NULL)
739 (*peltp)->pthrd = (PINETHRD_S *) fs_get(sizeof(PINETHRD_S));
741 memset((*peltp)->pthrd, 0, sizeof(PINETHRD_S));
743 (*peltp)->pthrd->rawno = rawno;
745 if(attached_to_thrd)
746 (*peltp)->pthrd->head = attached_to_thrd->head;
747 else
748 (*peltp)->pthrd->head = (*peltp)->pthrd->rawno; /* it's me */
750 if(flags == THD_TOP){
752 * We can tell this thread is a top-level thread because it doesn't
753 * have a parent.
755 (*peltp)->pthrd->top = (*peltp)->pthrd->rawno; /* I am a top */
756 if(attached_to_thrd){
757 attached_to_thrd->nextthd = (*peltp)->pthrd->rawno;
758 (*peltp)->pthrd->prevthd = attached_to_thrd->rawno;
759 (*peltp)->pthrd->thrdno = attached_to_thrd->thrdno + 1L;
761 else
762 (*peltp)->pthrd->thrdno = 1L; /* 1st thread */
764 g_sort.msgmap->max_thrdno = (*peltp)->pthrd->thrdno;
766 else if(flags == THD_NEXT){
767 if(attached_to_thrd){
768 attached_to_thrd->next = (*peltp)->pthrd->rawno;
769 (*peltp)->pthrd->parent = attached_to_thrd->rawno;
770 (*peltp)->pthrd->top = attached_to_thrd->top;
773 else if(flags == THD_BRANCH){
774 if(attached_to_thrd){
775 attached_to_thrd->branch = (*peltp)->pthrd->rawno;
776 (*peltp)->pthrd->parent = attached_to_thrd->parent;
777 (*peltp)->pthrd->top = attached_to_thrd->top;
781 return((*peltp)->pthrd);
786 * Collapse or expand a threading subtree. Not called from separate thread
787 * index.
789 void
790 collapse_or_expand(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
791 long unsigned int msgno)
793 int collapsed, adjust_current = 0;
794 PINETHRD_S *thrd = NULL, *nthrd;
795 unsigned long rawno;
797 if(!stream)
798 return;
801 * If msgno is a good msgno, then we collapse or expand the subthread
802 * which begins at msgno. If msgno is 0, we collapse or expand the
803 * entire current thread.
806 if(msgno > 0L && msgno <= mn_get_total(msgmap)){
807 rawno = mn_m2raw(msgmap, msgno);
808 if(rawno)
809 thrd = fetch_thread(stream, rawno);
811 else if(msgno == 0L){
812 rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
813 if(rawno)
814 thrd = fetch_thread(stream, rawno);
816 if(thrd && thrd->top != thrd->rawno){
817 adjust_current++;
818 thrd = fetch_thread(stream, thrd->top);
821 * Special case. If the user is collapsing the entire thread
822 * (msgno == 0), and we are in a Zoomed view, and the top of
823 * the entire thread is not part of the Zoomed view, then watch
824 * out. If we were to collapse the entire thread it would just
825 * disappear, because the top is not in the Zoom. Therefore,
826 * don't allow it. Do what the user probably wants, which is to
827 * collapse the thread at that point instead of the entire thread,
828 * leaving behind the top of the subthread to expand if needed.
829 * In other words, treat it as if they didn't have the
830 * F_SLASH_COLL_ENTIRE feature set.
832 collapsed = get_lflag(stream, NULL, thrd->rawno, MN_COLL)
833 && thrd->next;
835 if(!collapsed && get_lflag(stream, NULL, thrd->rawno, MN_HIDE))
836 thrd = fetch_thread(stream, rawno);
841 if(!thrd)
842 return;
844 collapsed = get_lflag(stream, NULL, thrd->rawno, MN_COLL) && thrd->next;
846 if(collapsed){
847 msgno = mn_raw2m(msgmap, thrd->rawno);
848 if(msgno > 0L && msgno <= mn_get_total(msgmap)){
849 set_lflag(stream, msgmap, msgno, MN_COLL, 0);
850 if(thrd->next){
851 if((nthrd = fetch_thread(stream, thrd->next)) != NULL)
852 set_thread_subtree(stream, nthrd, msgmap, 0, MN_CHID);
854 clear_index_cache_ent(stream, msgno, 0);
858 else if(thrd && thrd->next){
859 msgno = mn_raw2m(msgmap, thrd->rawno);
860 if(msgno > 0L && msgno <= mn_get_total(msgmap)){
861 set_lflag(stream, msgmap, msgno, MN_COLL, 1);
862 if((nthrd = fetch_thread(stream, thrd->next)) != NULL)
863 set_thread_subtree(stream, nthrd, msgmap, 1, MN_CHID);
865 clear_index_cache_ent(stream, msgno, 0);
868 else
869 q_status_message(SM_ORDER, 0, 1,
870 _("No thread to collapse or expand on this line"));
872 /* if current is hidden, adjust */
873 if(adjust_current)
874 adjust_cur_to_visible(stream, msgmap);
879 * Select the messages in a subthread. If all of the messages are already
880 * selected, unselect them. This routine is a bit strange because it
881 * doesn't set the MN_SLCT bit. Instead, it sets MN_STMP in apply_command
882 * and then thread_command copies the MN_STMP messages back to MN_SLCT.
884 void
885 select_thread_stmp(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap)
887 PINETHRD_S *thrd = NULL;
888 unsigned long rawno, in_thread, set_in_thread, save_branch;
890 /* ugly bit means the same thing as return of 1 from individual_select */
891 state->ugly_consider_advancing_bit = 0;
893 if(!(stream && msgmap))
894 return;
896 rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
897 if(rawno)
898 thrd = fetch_thread(stream, rawno);
900 if(!thrd)
901 return;
903 /* run through thrd to see if it is all selected */
904 save_branch = thrd->branch;
905 thrd->branch = 0L;
906 if((set_in_thread = count_lflags_in_thread(stream, thrd, msgmap, MN_STMP))
907 == (in_thread = count_lflags_in_thread(stream, thrd, msgmap, MN_NONE))){
909 * If everything is selected, the first unselect should cause
910 * an autozoom. In order to trigger the right code in
911 * thread_command()
912 * copy_lflags()
913 * we set the MN_HIDE bit on the current message here.
915 if(F_ON(F_AUTO_ZOOM, state) && !any_lflagged(msgmap, MN_HIDE)
916 && any_lflagged(msgmap, MN_STMP) == mn_get_total(msgmap))
917 set_lflag(stream, msgmap, mn_get_cur(msgmap), MN_HIDE, 1);
918 set_thread_lflags(stream, thrd, msgmap, MN_STMP, 0);
920 else{
921 set_thread_lflags(stream, thrd, msgmap, MN_STMP, 1);
922 state->ugly_consider_advancing_bit = 1;
925 thrd->branch = save_branch;
927 if(set_in_thread == in_thread)
928 q_status_message1(SM_ORDER, 0, 3, _("Unselected %s messages in thread"),
929 comatose((long) in_thread));
930 else if(set_in_thread == 0)
931 q_status_message1(SM_ORDER, 0, 3, _("Selected %s messages in thread"),
932 comatose((long) in_thread));
933 else
934 q_status_message1(SM_ORDER, 0, 3,
935 _("Selected %s more messages in thread"),
936 comatose((long) (in_thread-set_in_thread)));
941 * Count how many of this system flag in this thread subtree.
942 * If flags == 0 count the messages in the thread.
944 * Watch out when calling this. The thrd->branch is not part of thrd.
945 * Branch is a sibling to thrd, not a child. Zero out branch before calling
946 * or call on thrd->next and worry about thrd separately.
947 * Ok to call it on top-level thread which has no branch already.
949 unsigned long
950 count_flags_in_thread(MAILSTREAM *stream, PINETHRD_S *thrd, long int flags)
952 unsigned long count = 0;
953 PINETHRD_S *nthrd, *bthrd;
954 MESSAGECACHE *mc;
956 if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
957 return count;
959 if(thrd->next){
960 nthrd = fetch_thread(stream, thrd->next);
961 if(nthrd)
962 count += count_flags_in_thread(stream, nthrd, flags);
965 if(thrd->branch){
966 bthrd = fetch_thread(stream, thrd->branch);
967 if(bthrd)
968 count += count_flags_in_thread(stream, bthrd, flags);
971 mc = (thrd && thrd->rawno > 0L && stream && thrd->rawno <= stream->nmsgs)
972 ? mail_elt(stream, thrd->rawno) : NULL;
973 if(mc && mc->valid && FLAG_MATCH(flags, mc, stream))
974 count++;
976 return count;
981 * Count how many of this local flag in this thread subtree.
982 * If flags == MN_NONE then we just count the messages instead of whether
983 * the messages have a flag set.
985 * Watch out when calling this. The thrd->branch is not part of thrd.
986 * Branch is a sibling to thrd, not a child. Zero out branch before calling
987 * or call on thrd->next and worry about thrd separately.
988 * Ok to call it on top-level thread which has no branch already.
990 unsigned long
991 count_lflags_in_thread(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap, int flags)
993 unsigned long count = 0;
994 PINETHRD_S *nthrd, *bthrd;
996 if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
997 return count;
999 if(thrd->next){
1000 nthrd = fetch_thread(stream, thrd->next);
1001 if(nthrd)
1002 count += count_lflags_in_thread(stream, nthrd, msgmap, flags);
1005 if(thrd->branch){
1006 bthrd = fetch_thread(stream, thrd->branch);
1007 if(bthrd)
1008 count += count_lflags_in_thread(stream, bthrd, msgmap,flags);
1011 if(flags == MN_NONE)
1012 count++;
1013 else
1014 count += get_lflag(stream, msgmap, mn_raw2m(msgmap, thrd->rawno), flags);
1016 return count;
1021 * Special-purpose for performance improvement.
1024 thread_has_some_visible(MAILSTREAM *stream, PINETHRD_S *thrd)
1026 PINETHRD_S *nthrd, *bthrd;
1028 if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
1029 return 0;
1031 if(get_lflag(stream, NULL, thrd->rawno, MN_HIDE) == 0)
1032 return 1;
1034 if(thrd->next){
1035 nthrd = fetch_thread(stream, thrd->next);
1036 if(nthrd && thread_has_some_visible(stream, nthrd))
1037 return 1;
1040 if(thrd->branch){
1041 bthrd = fetch_thread(stream, thrd->branch);
1042 if(bthrd && thread_has_some_visible(stream, bthrd))
1043 return 1;
1046 return 0;
1051 mark_msgs_in_thread(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap)
1053 int count = 0;
1054 PINETHRD_S *nthrd, *bthrd;
1055 MESSAGECACHE *mc;
1057 if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
1058 return count;
1060 if(thrd->next){
1061 nthrd = fetch_thread(stream, thrd->next);
1062 if(nthrd)
1063 count += mark_msgs_in_thread(stream, nthrd, msgmap);
1066 if(thrd->branch){
1067 bthrd = fetch_thread(stream, thrd->branch);
1068 if(bthrd)
1069 count += mark_msgs_in_thread(stream, bthrd, msgmap);
1072 if(stream && thrd->rawno >= 1L && thrd->rawno <= stream->nmsgs &&
1073 (mc = mail_elt(stream,thrd->rawno))
1074 && !mc->sequence
1075 && !mc->private.msg.env){
1076 mc->sequence = 1;
1077 count++;
1080 return count;
1085 * This sets or clears flags for the messages at this node and below in
1086 * a tree.
1088 * Watch out when calling this. The thrd->branch is not part of thrd.
1089 * Branch is a sibling to thrd, not a child. Zero out branch before calling
1090 * or call on thrd->next and worry about thrd separately.
1091 * Ok to call it on top-level thread which has no branch already.
1093 void
1094 set_thread_lflags(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap, int flags, int v)
1098 /* flags to set or clear */
1099 /* set or clear? */
1101 unsigned long msgno;
1102 PINETHRD_S *nthrd, *bthrd;
1104 if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
1105 return;
1107 msgno = mn_raw2m(msgmap, thrd->rawno);
1109 set_lflag(stream, msgmap, msgno, flags, v);
1112 * Careful, performance hack. This should logically be a separate
1113 * operation on the thread but it is convenient to stick it in here.
1115 * When we back out of viewing a thread to the separate-thread-index
1116 * we may leave behind some cached hlines that aren't quite right
1117 * because they were collapsed. In particular, the plus_col character
1118 * may be wrong. Instead of trying to figure out what it should be just
1119 * clear the cache entries for the this thread when we come back in
1120 * to view it again.
1122 if(msgno > 0L && flags == MN_CHID2 && v == 1)
1123 clear_index_cache_ent(stream, msgno, 0);
1125 if(thrd->next){
1126 nthrd = fetch_thread(stream, thrd->next);
1127 if(nthrd)
1128 set_thread_lflags(stream, nthrd, msgmap, flags, v);
1131 if(thrd->branch){
1132 bthrd = fetch_thread(stream, thrd->branch);
1133 if(bthrd)
1134 set_thread_lflags(stream, bthrd, msgmap, flags, v);
1139 char
1140 status_symbol_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, IndexColType type)
1142 char status = ' ';
1143 unsigned long save_branch, cnt, tot_in_thrd;
1145 if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
1146 return status;
1148 save_branch = thrd->branch;
1149 thrd->branch = 0L; /* branch is a sibling, not part of thread */
1152 * This is D if all of thread is deleted,
1153 * else A if all of thread is answered,
1154 * else F if all of thread is forwarded,
1155 * else N if any unseen and not deleted,
1156 * else blank.
1158 if(type == iStatus){
1159 tot_in_thrd = count_flags_in_thread(stream, thrd, F_NONE);
1160 /* all deleted */
1161 if(count_flags_in_thread(stream, thrd, F_DEL) == tot_in_thrd)
1162 status = 'D';
1163 /* all answered */
1164 else if(count_flags_in_thread(stream, thrd, F_ANS) == tot_in_thrd)
1165 status = 'A';
1166 /* all forwarded */
1167 else if(count_flags_in_thread(stream, thrd, F_FWD) == tot_in_thrd)
1168 status = 'F';
1169 /* or any new and not deleted */
1170 else if((!IS_NEWS(stream)
1171 || F_ON(F_FAKE_NEW_IN_NEWS, ps_global))
1172 && count_flags_in_thread(stream, thrd, F_UNDEL|F_UNSEEN))
1173 status = 'N';
1175 else if(type == iFStatus){
1176 if(!IS_NEWS(stream) || F_ON(F_FAKE_NEW_IN_NEWS, ps_global)){
1177 tot_in_thrd = count_flags_in_thread(stream, thrd, F_NONE);
1178 cnt = count_flags_in_thread(stream, thrd, F_UNSEEN);
1179 if(cnt)
1180 status = (cnt == tot_in_thrd) ? 'N' : 'n';
1183 else if(type == iIStatus || type == iSIStatus){
1184 tot_in_thrd = count_flags_in_thread(stream, thrd, F_NONE);
1186 /* unseen and recent */
1187 cnt = count_flags_in_thread(stream, thrd, F_RECENT|F_UNSEEN);
1188 if(cnt)
1189 status = (cnt == tot_in_thrd) ? 'N' : 'n';
1190 else{
1191 /* unseen and !recent */
1192 cnt = count_flags_in_thread(stream, thrd, F_UNSEEN);
1193 if(cnt)
1194 status = (cnt == tot_in_thrd) ? 'U' : 'u';
1195 else{
1196 /* seen and recent */
1197 cnt = count_flags_in_thread(stream, thrd, F_RECENT|F_SEEN);
1198 if(cnt)
1199 status = (cnt == tot_in_thrd) ? 'R' : 'r';
1204 thrd->branch = save_branch;
1206 return status;
1211 * Symbol is * if some message in thread is important,
1212 * + if some message is to us,
1213 * - if mark-for-cc and some message is cc to us, else blank.
1215 char
1216 to_us_symbol_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, int consider_flagged)
1218 char to_us = ' ';
1219 char branch_to_us = ' ';
1220 PINETHRD_S *nthrd, *bthrd;
1221 MESSAGECACHE *mc;
1223 if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
1224 return to_us;
1226 if(thrd->next){
1227 nthrd = fetch_thread(stream, thrd->next);
1228 if(nthrd)
1229 to_us = to_us_symbol_for_thread(stream, nthrd, consider_flagged);
1232 if(((consider_flagged && to_us != '*') || (!consider_flagged && to_us != '+'))
1233 && thrd->branch){
1234 bthrd = fetch_thread(stream, thrd->branch);
1235 if(bthrd)
1236 branch_to_us = to_us_symbol_for_thread(stream, bthrd, consider_flagged);
1238 /* use branch to_us symbol if it has higher priority than what we have so far */
1239 if(to_us == ' '){
1240 if(branch_to_us == '-' || branch_to_us == '+' || branch_to_us == '*')
1241 to_us = branch_to_us;
1243 else if(to_us == '-'){
1244 if(branch_to_us == '+' || branch_to_us == '*')
1245 to_us = branch_to_us;
1247 else if(to_us == '+'){
1248 if(branch_to_us == '*')
1249 to_us = branch_to_us;
1253 if((consider_flagged && to_us != '*') || (!consider_flagged && to_us != '+')){
1254 if(consider_flagged && thrd && thrd->rawno > 0L
1255 && stream && thrd->rawno <= stream->nmsgs
1256 && (mc = mail_elt(stream, thrd->rawno))
1257 && FLAG_MATCH(F_FLAG, mc, stream))
1258 to_us = '*';
1259 else if(to_us != '+' && !IS_NEWS(stream)){
1260 INDEXDATA_S idata;
1261 MESSAGECACHE *mc;
1262 ADDRESS *addr;
1264 memset(&idata, 0, sizeof(INDEXDATA_S));
1265 idata.stream = stream;
1266 idata.rawno = thrd->rawno;
1267 idata.msgno = mn_raw2m(sp_msgmap(stream), idata.rawno);
1268 if(idata.rawno > 0L && stream && idata.rawno <= stream->nmsgs
1269 && (mc = mail_elt(stream, idata.rawno))){
1270 idata.size = mc->rfc822_size;
1271 index_data_env(&idata,
1272 pine_mail_fetchenvelope(stream, idata.rawno));
1274 else
1275 idata.bogus = 2;
1277 for(addr = fetch_to(&idata); addr; addr = addr->next)
1278 if(address_is_us(addr, ps_global)){
1279 to_us = '+';
1280 break;
1283 if(to_us != '+' && resent_to_us(&idata))
1284 to_us = '+';
1286 if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global))
1287 for(addr = fetch_cc(&idata); addr; addr = addr->next)
1288 if(address_is_us(addr, ps_global)){
1289 to_us = '-';
1290 break;
1295 return to_us;
1300 * This sets or clears flags for the messages at this node and below in
1301 * a tree. It doesn't just blindly do it, perhaps it should. Instead,
1302 * when un-hiding a subtree it leaves the sub-subtree hidden if a node
1303 * is collapsed.
1305 * Watch out when calling this. The thrd->branch is not part of thrd.
1306 * Branch is a sibling to thrd, not a child. Zero out branch before calling
1307 * or call on thrd->next and worry about thrd separately.
1308 * Ok to call it on top-level thread which has no branch already.
1310 void
1311 set_thread_subtree(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap, int v, int flags)
1315 /* set or clear? */
1316 /* flags to set or clear */
1318 int hiding;
1319 unsigned long msgno;
1320 PINETHRD_S *nthrd, *bthrd;
1322 hiding = (flags == MN_CHID) && v;
1324 if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
1325 return;
1327 msgno = mn_raw2m(msgmap, thrd->rawno);
1329 set_lflag(stream, msgmap, msgno, flags, v);
1331 if(thrd->next && (hiding || !get_lflag(stream,NULL,thrd->rawno,MN_COLL))){
1332 nthrd = fetch_thread(stream, thrd->next);
1333 if(nthrd)
1334 set_thread_subtree(stream, nthrd, msgmap, v, flags);
1337 if(thrd->branch){
1338 bthrd = fetch_thread(stream, thrd->branch);
1339 if(bthrd)
1340 set_thread_subtree(stream, bthrd, msgmap, v, flags);
1346 * View a thread. Move from the thread index screen to a message index
1347 * screen for the current thread.
1349 * set_lflags - Set the local flags appropriately to start viewing
1350 * the thread. We would not want to set this if we are
1351 * already viewing the thread (and expunge or new mail
1352 * happened) and we want to preserve the collapsed state
1353 * of the subthreads.
1356 view_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int set_lflags)
1358 PINETHRD_S *thrd = NULL;
1359 unsigned long rawno, cur;
1361 if(!any_messages(msgmap, NULL, "to View"))
1362 return 0;
1364 if(!(stream && msgmap))
1365 return 0;
1367 rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
1368 if(rawno)
1369 thrd = fetch_thread(stream, rawno);
1371 if(thrd && thrd->top && thrd->top != thrd->rawno)
1372 thrd = fetch_thread(stream, thrd->top);
1374 if(!thrd)
1375 return 0;
1378 * Clear hidden and collapsed flag for this thread.
1379 * And set CHID2.
1380 * Don't have to worry about there being a branch because
1381 * this is a toplevel thread.
1383 if(set_lflags){
1384 set_thread_lflags(stream, thrd, msgmap, MN_COLL | MN_CHID, 0);
1385 set_thread_lflags(stream, thrd, msgmap, MN_CHID2, 1);
1389 * If this is one of those wacky users who like to sort backwards
1390 * they would probably prefer that the current message be the last
1391 * one in the thread (the one highest up the screen).
1393 if(mn_get_revsort(msgmap)){
1394 cur = mn_get_cur(msgmap);
1395 while(cur > 1L && get_lflag(stream, msgmap, cur-1L, MN_CHID2))
1396 cur--;
1398 if(cur != mn_get_cur(msgmap))
1399 mn_set_cur(msgmap, cur);
1402 /* first message in thread might be hidden if zoomed */
1403 if(any_lflagged(msgmap, MN_HIDE)){
1404 cur = mn_get_cur(msgmap);
1405 while(get_lflag(stream, msgmap, cur, MN_HIDE))
1406 cur++;
1408 if(cur != mn_get_cur(msgmap))
1409 mn_set_cur(msgmap, cur);
1412 msgmap->top = mn_get_cur(msgmap);
1413 sp_set_viewing_a_thread(stream, 1);
1415 state->mangled_screen = 1;
1416 setup_for_index_index_screen();
1418 return 1;
1423 unview_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap)
1425 PINETHRD_S *thrd = NULL, *topthrd = NULL;
1426 unsigned long rawno;
1428 if(!(stream && msgmap))
1429 return 0;
1431 rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
1432 if(rawno)
1433 thrd = fetch_thread(stream, rawno);
1435 if(thrd && thrd->top)
1436 topthrd = fetch_thread(stream, thrd->top);
1438 if(!topthrd)
1439 return 0;
1441 /* hide this thread */
1442 set_thread_lflags(stream, topthrd, msgmap, MN_CHID, 1);
1444 /* clear special CHID2 flags for this thread */
1445 set_thread_lflags(stream, topthrd, msgmap, MN_CHID2, 0);
1447 /* clear CHID for top-level message and set COLL */
1448 set_lflag(stream, msgmap, mn_raw2m(msgmap, topthrd->rawno), MN_CHID, 0);
1449 set_lflag(stream, msgmap, mn_raw2m(msgmap, topthrd->rawno), MN_COLL, 1);
1451 mn_set_cur(msgmap, mn_raw2m(msgmap, topthrd->rawno));
1452 sp_set_viewing_a_thread(stream, 0);
1453 setup_for_thread_index_screen();
1455 return 1;
1459 PINETHRD_S *
1460 find_thread_by_number(MAILSTREAM *stream, MSGNO_S *msgmap, long int target, PINETHRD_S *startthrd)
1462 PINETHRD_S *thrd = NULL;
1464 if(!(stream && msgmap))
1465 return(thrd);
1467 thrd = startthrd;
1469 if(!thrd || !(thrd->prevthd || thrd->nextthd))
1470 thrd = fetch_thread(stream, mn_m2raw(msgmap, mn_get_cur(msgmap)));
1472 if(thrd && !(thrd->prevthd || thrd->nextthd) && thrd->head)
1473 thrd = fetch_thread(stream, thrd->head);
1475 if(thrd){
1476 /* go forward from here */
1477 if(thrd->thrdno < target){
1478 while(thrd){
1479 if(thrd->thrdno == target)
1480 break;
1482 if(mn_get_revsort(msgmap) && thrd->prevthd)
1483 thrd = fetch_thread(stream, thrd->prevthd);
1484 else if(!mn_get_revsort(msgmap) && thrd->nextthd)
1485 thrd = fetch_thread(stream, thrd->nextthd);
1486 else
1487 thrd = NULL;
1490 /* back up from here */
1491 else if(thrd->thrdno > target
1492 && (mn_get_revsort(msgmap)
1493 || (thrd->thrdno - target) < (target - 1L))){
1494 while(thrd){
1495 if(thrd->thrdno == target)
1496 break;
1498 if(mn_get_revsort(msgmap) && thrd->nextthd)
1499 thrd = fetch_thread(stream, thrd->nextthd);
1500 else if(!mn_get_revsort(msgmap) && thrd->prevthd)
1501 thrd = fetch_thread(stream, thrd->prevthd);
1502 else
1503 thrd = NULL;
1506 /* go forward from head */
1507 else if(thrd->thrdno > target){
1508 if(thrd->head){
1509 thrd = fetch_thread(stream, thrd->head);
1510 while(thrd){
1511 if(thrd->thrdno == target)
1512 break;
1514 if(thrd->nextthd)
1515 thrd = fetch_thread(stream, thrd->nextthd);
1516 else
1517 thrd = NULL;
1523 return(thrd);
1528 * Set search bit for every message in a thread.
1530 * Watch out when calling this. The thrd->branch is not part of thrd.
1531 * Branch is a sibling to thrd, not a child. Zero out branch before calling
1532 * or call on thrd->next and worry about thrd separately. Top-level threads
1533 * already have a branch equal to zero.
1535 * If msgset is non-NULL, then only set the search bit for a message if that
1536 * message is included in the msgset.
1538 void
1539 set_search_bit_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, SEARCHSET **msgset)
1541 PINETHRD_S *nthrd, *bthrd;
1543 if(!(stream && thrd))
1544 return;
1546 if(thrd->rawno > 0L && thrd->rawno <= stream->nmsgs
1547 && (!(msgset && *msgset) || in_searchset(*msgset, thrd->rawno)))
1548 mm_searched(stream, thrd->rawno);
1550 if(thrd->next){
1551 nthrd = fetch_thread(stream, thrd->next);
1552 if(nthrd)
1553 set_search_bit_for_thread(stream, nthrd, msgset);
1556 if(thrd->branch){
1557 bthrd = fetch_thread(stream, thrd->branch);
1558 if(bthrd)
1559 set_search_bit_for_thread(stream, bthrd, msgset);