* Implement a different way to delete a password from the cache.
[alpine.git] / pith / thread.c
blob7a96addeee35ec58726691d3671ed46d3b36dce3
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include "../pith/headers.h"
16 #include "../pith/thread.h"
17 #include "../pith/flag.h"
18 #include "../pith/icache.h"
19 #include "../pith/mailindx.h"
20 #include "../pith/msgno.h"
21 #include "../pith/sort.h"
22 #include "../pith/pineelt.h"
23 #include "../pith/status.h"
24 #include "../pith/news.h"
25 #include "../pith/search.h"
26 #include "../pith/mailcmd.h"
27 #include "../pith/ablookup.h"
31 * Internal prototypes
33 long *sort_thread_flatten(THREADNODE *, MAILSTREAM *, long *,
34 char *, long, PINETHRD_S *, unsigned);
35 void make_thrdflags_consistent(MAILSTREAM *, MSGNO_S *, PINETHRD_S *, int);
36 THREADNODE *collapse_threadnode_tree(THREADNODE *);
37 THREADNODE *collapse_threadnode_tree_sorted(THREADNODE *);
38 THREADNODE *sort_threads_and_collapse(THREADNODE *);
39 THREADNODE *insert_tree_in_place(THREADNODE *, THREADNODE *);
40 unsigned long branch_greatest_num(THREADNODE *, int);
41 long calculate_visible_threads(MAILSTREAM *);
44 PINETHRD_S *
45 fetch_thread(MAILSTREAM *stream, long unsigned int rawno)
47 MESSAGECACHE *mc;
48 PINELT_S *pelt;
49 PINETHRD_S *thrd = NULL;
51 if(stream && rawno > 0L && rawno <= stream->nmsgs
52 && !sp_need_to_rethread(stream)){
53 mc = (rawno > 0L && stream && rawno <= stream->nmsgs)
54 ? mail_elt(stream, rawno) : NULL;
55 if(mc && (pelt = (PINELT_S *) mc->sparep))
56 thrd = pelt->pthrd;
59 return(thrd);
63 PINETHRD_S *
64 fetch_head_thread(MAILSTREAM *stream)
66 unsigned long rawno;
67 PINETHRD_S *thrd = NULL;
69 if(stream){
70 /* first find any thread */
71 for(rawno = 1L; !thrd && rawno <= stream->nmsgs; rawno++)
72 thrd = fetch_thread(stream, rawno);
74 if(thrd && thrd->head)
75 thrd = fetch_thread(stream, thrd->head);
78 return(thrd);
83 * Set flag f to v for all messages in thrd.
85 * Watch out when calling this. The thrd->branch is not part of thrd.
86 * Branch is a sibling to thrd, not a child. Zero out branch before calling
87 * or call on thrd->next and worry about thrd separately.
88 * Ok to call it on top-level thread which has no branch already.
90 void
91 set_flags_for_thread(MAILSTREAM *stream, MSGNO_S *msgmap, int f, PINETHRD_S *thrd, int v)
93 PINETHRD_S *nthrd, *bthrd;
95 if(!(stream && thrd && msgmap))
96 return;
98 set_lflag(stream, msgmap, mn_raw2m(msgmap, thrd->rawno), f, v);
100 if(thrd->next){
101 nthrd = fetch_thread(stream, thrd->next);
102 if(nthrd)
103 set_flags_for_thread(stream, msgmap, f, nthrd, v);
106 if(thrd->branch){
107 bthrd = fetch_thread(stream, thrd->branch);
108 if(bthrd)
109 set_flags_for_thread(stream, msgmap, f, bthrd, v);
114 void
115 erase_threading_info(MAILSTREAM *stream, MSGNO_S *msgmap)
117 unsigned long n;
118 MESSAGECACHE *mc;
119 PINELT_S *peltp;
121 if(!(stream && stream->spare))
122 return;
124 ps_global->view_skipped_index = 0;
125 sp_set_viewing_a_thread(stream, 0);
127 if(THRD_INDX())
128 setup_for_thread_index_screen();
129 else
130 setup_for_index_index_screen();
132 stream->spare = 0;
134 for(n = 1L; n <= stream->nmsgs; n++){
135 set_lflag(stream, msgmap, mn_raw2m(msgmap, n),
136 MN_COLL | MN_CHID | MN_CHID2, 0);
137 mc = mail_elt(stream, n);
138 if(mc && mc->sparep){
139 peltp = (PINELT_S *) mc->sparep;
140 if(peltp->pthrd)
141 fs_give((void **) &peltp->pthrd);
147 void
148 sort_thread_callback(MAILSTREAM *stream, THREADNODE *tree)
150 THREADNODE *collapsed_tree = NULL;
151 PINETHRD_S *thrd = NULL;
152 unsigned long msgno, rawno;
153 int un_view_thread = 0;
154 long raw_current;
155 char *dup_chk = NULL;
158 dprint((2, "sort_thread_callback\n"));
160 g_sort.msgmap->max_thrdno = 0L;
163 * Eliminate dummy nodes from tree and collapse the tree in a logical
164 * way. If the dummy node is at the top-level, then its children are
165 * promoted to the top-level as separate threads.
167 if(F_ON(F_THREAD_SORTS_BY_ARRIVAL, ps_global))
168 collapsed_tree = collapse_threadnode_tree_sorted(tree);
169 else
170 collapsed_tree = collapse_threadnode_tree(tree);
172 /* dup_chk is like sort with an origin of 1 */
173 dup_chk = (char *) fs_get((mn_get_nmsgs(g_sort.msgmap)+1) * sizeof(char));
174 memset(dup_chk, 0, (mn_get_nmsgs(g_sort.msgmap)+1) * sizeof(char));
176 memset(&g_sort.msgmap->sort[1], 0, mn_get_total(g_sort.msgmap) * sizeof(long));
178 (void) sort_thread_flatten(collapsed_tree, stream,
179 &g_sort.msgmap->sort[1],
180 dup_chk, mn_get_nmsgs(g_sort.msgmap),
181 NULL, THD_TOP);
183 /* reset the inverse array */
184 msgno_reset_isort(g_sort.msgmap);
186 if(dup_chk)
187 fs_give((void **) &dup_chk);
189 if(collapsed_tree)
190 mail_free_threadnode(&collapsed_tree);
192 if(any_lflagged(g_sort.msgmap, MN_HIDE))
193 g_sort.msgmap->visible_threads = calculate_visible_threads(stream);
194 else
195 g_sort.msgmap->visible_threads = g_sort.msgmap->max_thrdno;
197 raw_current = mn_m2raw(g_sort.msgmap, mn_get_cur(g_sort.msgmap));
199 sp_set_need_to_rethread(stream, 0);
202 * Set appropriate bits to start out collapsed if desired. We use the
203 * stream spare bit to tell us if we've done this before for this
204 * stream.
206 if(!stream->spare
207 && (COLL_THRDS() || SEP_THRDINDX())
208 && mn_get_total(g_sort.msgmap) > 1L){
210 collapse_threads(stream, g_sort.msgmap, NULL);
212 else if(stream->spare){
215 * If we're doing auto collapse then new threads need to have
216 * their collapse bit set. This happens below if we're in the
217 * thread index, but if we're in the regular index with auto
218 * collapse we have to look for these.
220 if(any_lflagged(g_sort.msgmap, MN_USOR)){
221 if(COLL_THRDS()){
222 for(msgno = 1L; msgno <= mn_get_total(g_sort.msgmap); msgno++){
223 rawno = mn_m2raw(g_sort.msgmap, msgno);
224 if(get_lflag(stream, NULL, rawno, MN_USOR)){
225 thrd = fetch_thread(stream, rawno);
228 * Node is new, unsorted, top-level thread,
229 * and we're using auto collapse.
231 if(thrd && !thrd->parent)
232 set_lflag(stream, g_sort.msgmap, msgno, MN_COLL, 1);
235 * If a parent is collapsed, clear that parent's
236 * index cache entry. This is only necessary if
237 * the parent's index display can depend on its
238 * children, of course.
240 if(thrd && thrd->parent){
241 thrd = fetch_thread(stream, thrd->parent);
242 while(thrd){
243 long t;
245 if(get_lflag(stream, NULL, thrd->rawno, MN_COLL)
246 && (t = mn_raw2m(g_sort.msgmap,
247 (long) thrd->rawno)))
248 clear_index_cache_ent(stream, t, 0);
250 if(thrd->parent)
251 thrd = fetch_thread(stream, thrd->parent);
252 else
253 thrd = NULL;
261 set_lflags(stream, g_sort.msgmap, MN_USOR, 0);
264 if(sp_viewing_a_thread(stream)){
265 if(any_lflagged(g_sort.msgmap, MN_CHID2)){
266 /* current should be part of viewed thread */
267 if(get_lflag(stream, NULL, raw_current, MN_CHID2)){
268 thrd = fetch_thread(stream, raw_current);
269 if(thrd && thrd->top && thrd->top != thrd->rawno)
270 thrd = fetch_thread(stream, thrd->top);
272 if(thrd){
274 * For messages that are part of thread set MN_CHID2
275 * and for messages that aren't part of the thread
276 * clear MN_CHID2. Easiest is to just do it instead
277 * of checking if it is true first.
279 set_lflags(stream, g_sort.msgmap, MN_CHID2, 0);
280 set_thread_lflags(stream, thrd, g_sort.msgmap,
281 MN_CHID2, 1);
284 * Outside of the viewed thread everything else
285 * should be collapsed at the top-levels.
287 collapse_threads(stream, g_sort.msgmap, thrd);
290 * Inside of the thread, the top of the thread
291 * can't be hidden, the rest are hidden if a
292 * parent somewhere above them is collapsed.
293 * There can be collapse points that are hidden
294 * inside of the tree. They remain collapsed even
295 * if the parent above them uncollapses.
297 msgno = mn_raw2m(g_sort.msgmap, (long) thrd->rawno);
298 if(msgno)
299 set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0);
301 if(thrd->next){
302 PINETHRD_S *nthrd;
304 nthrd = fetch_thread(stream, thrd->next);
305 if(nthrd)
306 make_thrdflags_consistent(stream, g_sort.msgmap,
307 nthrd,
308 get_lflag(stream, NULL,
309 thrd->rawno,
310 MN_COLL));
313 else
314 un_view_thread++;
316 else
317 un_view_thread++;
319 else
320 un_view_thread++;
322 if(un_view_thread){
323 set_lflags(stream, g_sort.msgmap, MN_CHID2, 0);
324 unview_thread(ps_global, stream, g_sort.msgmap);
326 else{
327 mn_reset_cur(g_sort.msgmap,
328 mn_raw2m(g_sort.msgmap, raw_current));
329 view_thread(ps_global, stream, g_sort.msgmap, 0);
332 else if(SEP_THRDINDX()){
333 set_lflags(stream, g_sort.msgmap, MN_CHID2, 0);
334 collapse_threads(stream, g_sort.msgmap, NULL);
336 else{
337 thrd = fetch_head_thread(stream);
338 while(thrd){
340 * The top-level threads aren't hidden by collapse.
342 msgno = mn_raw2m(g_sort.msgmap, thrd->rawno);
343 if(msgno)
344 set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0);
346 if(thrd->next){
347 PINETHRD_S *nthrd;
349 nthrd = fetch_thread(stream, thrd->next);
350 if(nthrd)
351 make_thrdflags_consistent(stream, g_sort.msgmap,
352 nthrd,
353 get_lflag(stream, NULL,
354 thrd->rawno,
355 MN_COLL));
358 if(thrd->nextthd)
359 thrd = fetch_thread(stream, thrd->nextthd);
360 else
361 thrd = NULL;
366 stream->spare = 1;
368 dprint((2, "sort_thread_callback done\n"));
372 void
373 collapse_threads(MAILSTREAM *stream, MSGNO_S *msgmap, PINETHRD_S *not_this_thread)
375 PINETHRD_S *thrd = NULL, *nthrd;
376 unsigned long msgno;
378 dprint((9, "collapse_threads\n"));
380 thrd = fetch_head_thread(stream);
381 while(thrd){
382 if(thrd != not_this_thread){
383 msgno = mn_raw2m(g_sort.msgmap, thrd->rawno);
385 /* set collapsed bit */
386 if(msgno){
387 set_lflag(stream, g_sort.msgmap, msgno, MN_COLL, 1);
388 set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0);
391 /* hide its children */
392 if(thrd->next && (nthrd = fetch_thread(stream, thrd->next)))
393 set_thread_subtree(stream, nthrd, msgmap, 1, MN_CHID);
396 if(thrd->nextthd)
397 thrd = fetch_thread(stream, thrd->nextthd);
398 else
399 thrd = NULL;
402 dprint((9, "collapse_threads done\n"));
406 void
407 make_thrdflags_consistent(MAILSTREAM *stream, MSGNO_S *msgmap, PINETHRD_S *thrd,
408 int a_parent_is_collapsed)
410 PINETHRD_S *nthrd, *bthrd;
411 unsigned long msgno;
413 if(!thrd)
414 return;
416 msgno = mn_raw2m(msgmap, thrd->rawno);
418 if(a_parent_is_collapsed){
419 /* if some parent is collapsed, we should be hidden */
420 if(msgno)
421 set_lflag(stream, msgmap, msgno, MN_CHID, 1);
423 else{
424 /* no parent is collapsed so we are not hidden */
425 if(msgno)
426 set_lflag(stream, msgmap, msgno, MN_CHID, 0);
429 if(thrd->next){
430 nthrd = fetch_thread(stream, thrd->next);
431 if(nthrd)
432 make_thrdflags_consistent(stream, msgmap, nthrd,
433 a_parent_is_collapsed
434 ? a_parent_is_collapsed
435 : get_lflag(stream, NULL, thrd->rawno,
436 MN_COLL));
439 if(thrd->branch){
440 bthrd = fetch_thread(stream, thrd->branch);
441 if(bthrd)
442 make_thrdflags_consistent(stream, msgmap, bthrd,
443 a_parent_is_collapsed);
448 long
449 calculate_visible_threads(MAILSTREAM *stream)
451 PINETHRD_S *thrd = NULL;
452 long vis = 0L;
454 thrd = fetch_head_thread(stream);
455 while(thrd){
456 vis += (thread_has_some_visible(stream, thrd) ? 1 : 0);
458 if(thrd->nextthd)
459 thrd = fetch_thread(stream, thrd->nextthd);
460 else
461 thrd = NULL;
464 return(vis);
469 * This routine does a couple things. The input is the THREADNODE node
470 * that we get from c-client because of the THREAD command. The rest of
471 * the arguments are used to help guide this function through its
472 * recursive steps. One thing it does is to place the sort order in
473 * the array initially pointed to by the entry argument. All it is doing
474 * is walking the tree in the next then branch order you see below and
475 * incrementing the entry number one for each node. The other thing it
476 * is doing at the same time is to create a PINETHRD_S tree from the
477 * THREADNODE tree. The two trees are completely equivalent but the
478 * PINETHRD_S version has additional back pointers and parent pointers
479 * and so on to make it easier for alpine to deal with it. Each node
480 * of that tree is tied to the data associated with a particular message
481 * by the msgno_thread_info() call, so that we can go from a message
482 * number to the place in the thread tree that message sits.
484 long *
485 sort_thread_flatten(THREADNODE *node, MAILSTREAM *stream,
486 long *entry, char *dup_chk, long maxno,
487 PINETHRD_S *thrd, unsigned int flags)
489 PINETHRD_S *newthrd = NULL;
491 if(node){
492 if(node->num > 0L && node->num <= maxno){ /* holes happen */
493 if(!dup_chk[node->num]){ /* not a duplicate */
494 *entry = node->num;
495 dup_chk[node->num] = 1;
498 * Build a richer threading structure that will help us paint
499 * and operate on threads and subthreads.
501 newthrd = msgno_thread_info(stream, node->num, thrd, flags);
502 if(newthrd){
503 entry++;
505 if(node->next)
506 entry = sort_thread_flatten(node->next, stream,
507 entry, dup_chk, maxno,
508 newthrd, THD_NEXT);
510 if(node->branch)
511 entry = sort_thread_flatten(node->branch, stream,
512 entry, dup_chk, maxno,
513 newthrd,
514 (flags == THD_TOP) ? THD_TOP
515 : THD_BRANCH);
521 return(entry);
526 * Make a copy of c-client's THREAD tree while eliminating dummy nodes.
528 THREADNODE *
529 collapse_threadnode_tree(THREADNODE *tree)
531 THREADNODE *newtree = NULL;
533 if(tree){
534 if(tree->num){
535 newtree = mail_newthreadnode(NULL);
536 newtree->num = tree->num;
537 if(tree->next)
538 newtree->next = collapse_threadnode_tree(tree->next);
540 if(tree->branch)
541 newtree->branch = collapse_threadnode_tree(tree->branch);
543 else{
544 if(tree->next)
545 newtree = collapse_threadnode_tree(tree->next);
547 if(tree->branch){
548 if(newtree){
549 THREADNODE *last_branch = NULL;
552 * Next moved up to replace "tree" in the tree.
553 * If next has no branches, then we want to branch off
554 * of next. If next has branches, we want to branch off
555 * of the last of those branches instead.
557 last_branch = newtree;
558 while(last_branch->branch)
559 last_branch = last_branch->branch;
561 last_branch->branch = collapse_threadnode_tree(tree->branch);
563 else
564 newtree = collapse_threadnode_tree(tree->branch);
569 return(newtree);
574 * Like collapse_threadnode_tree, we collapse the dummy nodes.
575 * In addition we rearrange the threads by order of arrival of
576 * the last message in the thread, rather than the first message
577 * in the thread.
579 THREADNODE *
580 collapse_threadnode_tree_sorted(THREADNODE *tree)
582 THREADNODE *sorted_tree = NULL;
584 sorted_tree = sort_threads_and_collapse(tree);
587 * We used to eliminate top-level dummy nodes here so that
588 * orphans would still get sorted together, but we changed
589 * to sort the orphans themselves as top-level threads.
591 * It might be a matter of choice how they get sorted, but
592 * we'll try doing it this way and not add another feature.
595 return(sorted_tree);
599 * Recurse through the tree, sorting each top-level branch by the
600 * greatest num in the thread.
602 THREADNODE *
603 sort_threads_and_collapse(THREADNODE *tree)
605 THREADNODE *newtree = NULL, *newbranchtree = NULL, *newtreefree = NULL;
607 if(tree){
608 newtree = mail_newthreadnode(NULL);
609 newtree->num = tree->num;
612 * Only sort at the top level. Individual threads can
613 * rely on collapse_threadnode_tree
615 if(tree->next)
616 newtree->next = collapse_threadnode_tree(tree->next);
618 if(tree->branch){
620 * This recursive call returns an already re-sorted tree.
621 * With that, we can loop through and inject ourselves
622 * where we fit in with that sort, and pass back to the
623 * caller to inject themselves.
625 newbranchtree = sort_threads_and_collapse(tree->branch);
628 if(newtree->num)
629 newtree = insert_tree_in_place(newtree, newbranchtree);
630 else{
632 * If top node is a dummy, here is where we collapse it.
634 newtreefree = newtree;
635 newtree = insert_tree_in_place(newtree->next, newbranchtree);
636 newtreefree->next = NULL;
637 mail_free_threadnode(&newtreefree);
641 return(newtree);
645 * Recursively insert each of the top-level nodes in newtree in their place
646 * in tree according to which tree has the most recent arrival
648 THREADNODE *
649 insert_tree_in_place(THREADNODE *newtree, THREADNODE *tree)
651 THREADNODE *node = NULL;
652 unsigned long newtree_greatest_num = 0;
653 if(newtree->branch){
654 node = newtree->branch;
655 newtree->branch = NULL;
656 tree = insert_tree_in_place(node, tree);
659 newtree_greatest_num = branch_greatest_num(newtree, 0);
661 if(tree){
663 * Since tree is already sorted, we can insert when we find something
664 * newtree is less than
666 if(newtree_greatest_num < branch_greatest_num(tree, 0))
667 newtree->branch = tree;
668 else {
669 for(node = tree; node->branch; node = node->branch){
670 if(newtree_greatest_num < branch_greatest_num(node->branch, 0)){
671 newtree->branch = node->branch;
672 node->branch = newtree;
673 break;
676 if(!node->branch)
677 node->branch = newtree;
679 newtree = tree;
683 return(newtree);
687 * Given a thread, return the greatest num in the tree.
688 * is_subthread tells us not to recurse through branches, so
689 * we can split the top level into threads.
691 unsigned long
692 branch_greatest_num(THREADNODE *tree, int is_subthread)
694 unsigned long ret, branch_ret;
696 ret = tree->num;
698 if(tree->next && (branch_ret = branch_greatest_num(tree->next, 1)) > ret)
699 ret = branch_ret;
700 if(is_subthread && tree->branch &&
701 (branch_ret = branch_greatest_num(tree->branch, 1)) > ret)
702 ret = branch_ret;
704 return ret;
709 * Args stream -- the usual
710 * rawno -- the raw msg num associated with this new node
711 * attached_to_thrd -- the PINETHRD_S node that this is either a next or branch
712 * off of
713 * flags --
715 PINETHRD_S *
716 msgno_thread_info(MAILSTREAM *stream, long unsigned int rawno,
717 PINETHRD_S *attached_to_thrd, unsigned int flags)
719 PINELT_S **peltp = NULL;
720 MESSAGECACHE *mc;
722 if(!stream || rawno < 1L || rawno > stream->nmsgs)
723 return NULL;
726 * any private elt data yet?
728 if((mc = mail_elt(stream, rawno))
729 && (*(peltp = (PINELT_S **) &mc->sparep) == NULL)){
730 *peltp = (PINELT_S *) fs_get(sizeof(PINELT_S));
731 memset(*peltp, 0, sizeof(PINELT_S));
734 if((*peltp)->pthrd == NULL)
735 (*peltp)->pthrd = (PINETHRD_S *) fs_get(sizeof(PINETHRD_S));
737 memset((*peltp)->pthrd, 0, sizeof(PINETHRD_S));
739 (*peltp)->pthrd->rawno = rawno;
741 if(attached_to_thrd)
742 (*peltp)->pthrd->head = attached_to_thrd->head;
743 else
744 (*peltp)->pthrd->head = (*peltp)->pthrd->rawno; /* it's me */
746 if(flags == THD_TOP){
748 * We can tell this thread is a top-level thread because it doesn't
749 * have a parent.
751 (*peltp)->pthrd->top = (*peltp)->pthrd->rawno; /* I am a top */
752 if(attached_to_thrd){
753 attached_to_thrd->nextthd = (*peltp)->pthrd->rawno;
754 (*peltp)->pthrd->prevthd = attached_to_thrd->rawno;
755 (*peltp)->pthrd->thrdno = attached_to_thrd->thrdno + 1L;
757 else
758 (*peltp)->pthrd->thrdno = 1L; /* 1st thread */
760 g_sort.msgmap->max_thrdno = (*peltp)->pthrd->thrdno;
762 else if(flags == THD_NEXT){
763 if(attached_to_thrd){
764 attached_to_thrd->next = (*peltp)->pthrd->rawno;
765 (*peltp)->pthrd->parent = attached_to_thrd->rawno;
766 (*peltp)->pthrd->top = attached_to_thrd->top;
769 else if(flags == THD_BRANCH){
770 if(attached_to_thrd){
771 attached_to_thrd->branch = (*peltp)->pthrd->rawno;
772 (*peltp)->pthrd->parent = attached_to_thrd->parent;
773 (*peltp)->pthrd->top = attached_to_thrd->top;
777 return((*peltp)->pthrd);
782 * Collapse or expand a threading subtree. Not called from separate thread
783 * index.
785 void
786 collapse_or_expand(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
787 long unsigned int msgno)
789 int collapsed, adjust_current = 0;
790 PINETHRD_S *thrd = NULL, *nthrd;
791 unsigned long rawno;
793 if(!stream)
794 return;
797 * If msgno is a good msgno, then we collapse or expand the subthread
798 * which begins at msgno. If msgno is 0, we collapse or expand the
799 * entire current thread.
802 if(msgno > 0L && msgno <= mn_get_total(msgmap)){
803 rawno = mn_m2raw(msgmap, msgno);
804 if(rawno)
805 thrd = fetch_thread(stream, rawno);
807 else if(msgno == 0L){
808 rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
809 if(rawno)
810 thrd = fetch_thread(stream, rawno);
812 if(thrd && thrd->top != thrd->rawno){
813 adjust_current++;
814 thrd = fetch_thread(stream, thrd->top);
817 * Special case. If the user is collapsing the entire thread
818 * (msgno == 0), and we are in a Zoomed view, and the top of
819 * the entire thread is not part of the Zoomed view, then watch
820 * out. If we were to collapse the entire thread it would just
821 * disappear, because the top is not in the Zoom. Therefore,
822 * don't allow it. Do what the user probably wants, which is to
823 * collapse the thread at that point instead of the entire thread,
824 * leaving behind the top of the subthread to expand if needed.
825 * In other words, treat it as if they didn't have the
826 * F_SLASH_COLL_ENTIRE feature set.
828 collapsed = get_lflag(stream, NULL, thrd->rawno, MN_COLL)
829 && thrd->next;
831 if(!collapsed && get_lflag(stream, NULL, thrd->rawno, MN_HIDE))
832 thrd = fetch_thread(stream, rawno);
837 if(!thrd)
838 return;
840 collapsed = get_lflag(stream, NULL, thrd->rawno, MN_COLL) && thrd->next;
842 if(collapsed){
843 msgno = mn_raw2m(msgmap, thrd->rawno);
844 if(msgno > 0L && msgno <= mn_get_total(msgmap)){
845 set_lflag(stream, msgmap, msgno, MN_COLL, 0);
846 if(thrd->next){
847 if((nthrd = fetch_thread(stream, thrd->next)) != NULL)
848 set_thread_subtree(stream, nthrd, msgmap, 0, MN_CHID);
850 clear_index_cache_ent(stream, msgno, 0);
854 else if(thrd && thrd->next){
855 msgno = mn_raw2m(msgmap, thrd->rawno);
856 if(msgno > 0L && msgno <= mn_get_total(msgmap)){
857 set_lflag(stream, msgmap, msgno, MN_COLL, 1);
858 if((nthrd = fetch_thread(stream, thrd->next)) != NULL)
859 set_thread_subtree(stream, nthrd, msgmap, 1, MN_CHID);
861 clear_index_cache_ent(stream, msgno, 0);
864 else
865 q_status_message(SM_ORDER, 0, 1,
866 _("No thread to collapse or expand on this line"));
868 /* if current is hidden, adjust */
869 if(adjust_current)
870 adjust_cur_to_visible(stream, msgmap);
875 * Select the messages in a subthread. If all of the messages are already
876 * selected, unselect them. This routine is a bit strange because it
877 * doesn't set the MN_SLCT bit. Instead, it sets MN_STMP in apply_command
878 * and then thread_command copies the MN_STMP messages back to MN_SLCT.
880 void
881 select_thread_stmp(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap)
883 PINETHRD_S *thrd = NULL;
884 unsigned long rawno, in_thread, set_in_thread, save_branch;
886 /* ugly bit means the same thing as return of 1 from individual_select */
887 state->ugly_consider_advancing_bit = 0;
889 if(!(stream && msgmap))
890 return;
892 rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
893 if(rawno)
894 thrd = fetch_thread(stream, rawno);
896 if(!thrd)
897 return;
899 /* run through thrd to see if it is all selected */
900 save_branch = thrd->branch;
901 thrd->branch = 0L;
902 if((set_in_thread = count_lflags_in_thread(stream, thrd, msgmap, MN_STMP))
903 == (in_thread = count_lflags_in_thread(stream, thrd, msgmap, MN_NONE))){
905 * If everything is selected, the first unselect should cause
906 * an autozoom. In order to trigger the right code in
907 * thread_command()
908 * copy_lflags()
909 * we set the MN_HIDE bit on the current message here.
911 if(F_ON(F_AUTO_ZOOM, state) && !any_lflagged(msgmap, MN_HIDE)
912 && any_lflagged(msgmap, MN_STMP) == mn_get_total(msgmap))
913 set_lflag(stream, msgmap, mn_get_cur(msgmap), MN_HIDE, 1);
914 set_thread_lflags(stream, thrd, msgmap, MN_STMP, 0);
916 else{
917 set_thread_lflags(stream, thrd, msgmap, MN_STMP, 1);
918 state->ugly_consider_advancing_bit = 1;
921 thrd->branch = save_branch;
923 if(set_in_thread == in_thread)
924 q_status_message1(SM_ORDER, 0, 3, _("Unselected %s messages in thread"),
925 comatose((long) in_thread));
926 else if(set_in_thread == 0)
927 q_status_message1(SM_ORDER, 0, 3, _("Selected %s messages in thread"),
928 comatose((long) in_thread));
929 else
930 q_status_message1(SM_ORDER, 0, 3,
931 _("Selected %s more messages in thread"),
932 comatose((long) (in_thread-set_in_thread)));
937 * Count how many of this system flag in this thread subtree.
938 * If flags == 0 count the messages in the thread.
940 * Watch out when calling this. The thrd->branch is not part of thrd.
941 * Branch is a sibling to thrd, not a child. Zero out branch before calling
942 * or call on thrd->next and worry about thrd separately.
943 * Ok to call it on top-level thread which has no branch already.
945 unsigned long
946 count_flags_in_thread(MAILSTREAM *stream, PINETHRD_S *thrd, long int flags)
948 unsigned long count = 0;
949 PINETHRD_S *nthrd, *bthrd;
950 MESSAGECACHE *mc;
952 if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
953 return count;
955 if(thrd->next){
956 nthrd = fetch_thread(stream, thrd->next);
957 if(nthrd)
958 count += count_flags_in_thread(stream, nthrd, flags);
961 if(thrd->branch){
962 bthrd = fetch_thread(stream, thrd->branch);
963 if(bthrd)
964 count += count_flags_in_thread(stream, bthrd, flags);
967 mc = (thrd && thrd->rawno > 0L && stream && thrd->rawno <= stream->nmsgs)
968 ? mail_elt(stream, thrd->rawno) : NULL;
969 if(mc && mc->valid && FLAG_MATCH(flags, mc, stream))
970 count++;
972 return count;
977 * Count how many of this local flag in this thread subtree.
978 * If flags == MN_NONE then we just count the messages instead of whether
979 * the messages have a flag set.
981 * Watch out when calling this. The thrd->branch is not part of thrd.
982 * Branch is a sibling to thrd, not a child. Zero out branch before calling
983 * or call on thrd->next and worry about thrd separately.
984 * Ok to call it on top-level thread which has no branch already.
986 unsigned long
987 count_lflags_in_thread(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap, int flags)
989 unsigned long count = 0;
990 PINETHRD_S *nthrd, *bthrd;
992 if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
993 return count;
995 if(thrd->next){
996 nthrd = fetch_thread(stream, thrd->next);
997 if(nthrd)
998 count += count_lflags_in_thread(stream, nthrd, msgmap, flags);
1001 if(thrd->branch){
1002 bthrd = fetch_thread(stream, thrd->branch);
1003 if(bthrd)
1004 count += count_lflags_in_thread(stream, bthrd, msgmap,flags);
1007 if(flags == MN_NONE)
1008 count++;
1009 else
1010 count += get_lflag(stream, msgmap, mn_raw2m(msgmap, thrd->rawno), flags);
1012 return count;
1017 * Special-purpose for performance improvement.
1020 thread_has_some_visible(MAILSTREAM *stream, PINETHRD_S *thrd)
1022 PINETHRD_S *nthrd, *bthrd;
1024 if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
1025 return 0;
1027 if(get_lflag(stream, NULL, thrd->rawno, MN_HIDE) == 0)
1028 return 1;
1030 if(thrd->next){
1031 nthrd = fetch_thread(stream, thrd->next);
1032 if(nthrd && thread_has_some_visible(stream, nthrd))
1033 return 1;
1036 if(thrd->branch){
1037 bthrd = fetch_thread(stream, thrd->branch);
1038 if(bthrd && thread_has_some_visible(stream, bthrd))
1039 return 1;
1042 return 0;
1047 mark_msgs_in_thread(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap)
1049 int count = 0;
1050 PINETHRD_S *nthrd, *bthrd;
1051 MESSAGECACHE *mc;
1053 if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
1054 return count;
1056 if(thrd->next){
1057 nthrd = fetch_thread(stream, thrd->next);
1058 if(nthrd)
1059 count += mark_msgs_in_thread(stream, nthrd, msgmap);
1062 if(thrd->branch){
1063 bthrd = fetch_thread(stream, thrd->branch);
1064 if(bthrd)
1065 count += mark_msgs_in_thread(stream, bthrd, msgmap);
1068 if(stream && thrd->rawno >= 1L && thrd->rawno <= stream->nmsgs &&
1069 (mc = mail_elt(stream,thrd->rawno))
1070 && !mc->sequence
1071 && !mc->private.msg.env){
1072 mc->sequence = 1;
1073 count++;
1076 return count;
1081 * This sets or clears flags for the messages at this node and below in
1082 * a tree.
1084 * Watch out when calling this. The thrd->branch is not part of thrd.
1085 * Branch is a sibling to thrd, not a child. Zero out branch before calling
1086 * or call on thrd->next and worry about thrd separately.
1087 * Ok to call it on top-level thread which has no branch already.
1089 void
1090 set_thread_lflags(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap, int flags, int v)
1094 /* flags to set or clear */
1095 /* set or clear? */
1097 unsigned long msgno;
1098 PINETHRD_S *nthrd, *bthrd;
1100 if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
1101 return;
1103 msgno = mn_raw2m(msgmap, thrd->rawno);
1105 set_lflag(stream, msgmap, msgno, flags, v);
1108 * Careful, performance hack. This should logically be a separate
1109 * operation on the thread but it is convenient to stick it in here.
1111 * When we back out of viewing a thread to the separate-thread-index
1112 * we may leave behind some cached hlines that aren't quite right
1113 * because they were collapsed. In particular, the plus_col character
1114 * may be wrong. Instead of trying to figure out what it should be just
1115 * clear the cache entries for the this thread when we come back in
1116 * to view it again.
1118 if(msgno > 0L && flags == MN_CHID2 && v == 1)
1119 clear_index_cache_ent(stream, msgno, 0);
1121 if(thrd->next){
1122 nthrd = fetch_thread(stream, thrd->next);
1123 if(nthrd)
1124 set_thread_lflags(stream, nthrd, msgmap, flags, v);
1127 if(thrd->branch){
1128 bthrd = fetch_thread(stream, thrd->branch);
1129 if(bthrd)
1130 set_thread_lflags(stream, bthrd, msgmap, flags, v);
1135 char
1136 status_symbol_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, IndexColType type)
1138 char status = ' ';
1139 unsigned long save_branch, cnt, tot_in_thrd;
1141 if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
1142 return status;
1144 save_branch = thrd->branch;
1145 thrd->branch = 0L; /* branch is a sibling, not part of thread */
1148 * This is D if all of thread is deleted,
1149 * else A if all of thread is answered,
1150 * else F if all of thread is forwarded,
1151 * else N if any unseen and not deleted,
1152 * else blank.
1154 if(type == iStatus){
1155 tot_in_thrd = count_flags_in_thread(stream, thrd, F_NONE);
1156 /* all deleted */
1157 if(count_flags_in_thread(stream, thrd, F_DEL) == tot_in_thrd)
1158 status = 'D';
1159 /* all answered */
1160 else if(count_flags_in_thread(stream, thrd, F_ANS) == tot_in_thrd)
1161 status = 'A';
1162 /* all forwarded */
1163 else if(count_flags_in_thread(stream, thrd, F_FWD) == tot_in_thrd)
1164 status = 'F';
1165 /* or any new and not deleted */
1166 else if((!IS_NEWS(stream)
1167 || F_ON(F_FAKE_NEW_IN_NEWS, ps_global))
1168 && count_flags_in_thread(stream, thrd, F_UNDEL|F_UNSEEN))
1169 status = 'N';
1171 else if(type == iFStatus){
1172 if(!IS_NEWS(stream) || F_ON(F_FAKE_NEW_IN_NEWS, ps_global)){
1173 tot_in_thrd = count_flags_in_thread(stream, thrd, F_NONE);
1174 cnt = count_flags_in_thread(stream, thrd, F_UNSEEN);
1175 if(cnt)
1176 status = (cnt == tot_in_thrd) ? 'N' : 'n';
1179 else if(type == iIStatus || type == iSIStatus){
1180 tot_in_thrd = count_flags_in_thread(stream, thrd, F_NONE);
1182 /* unseen and recent */
1183 cnt = count_flags_in_thread(stream, thrd, F_RECENT|F_UNSEEN);
1184 if(cnt)
1185 status = (cnt == tot_in_thrd) ? 'N' : 'n';
1186 else{
1187 /* unseen and !recent */
1188 cnt = count_flags_in_thread(stream, thrd, F_UNSEEN);
1189 if(cnt)
1190 status = (cnt == tot_in_thrd) ? 'U' : 'u';
1191 else{
1192 /* seen and recent */
1193 cnt = count_flags_in_thread(stream, thrd, F_RECENT|F_SEEN);
1194 if(cnt)
1195 status = (cnt == tot_in_thrd) ? 'R' : 'r';
1200 thrd->branch = save_branch;
1202 return status;
1207 * Symbol is * if some message in thread is important,
1208 * + if some message is to us,
1209 * - if mark-for-cc and some message is cc to us, else blank.
1211 char
1212 to_us_symbol_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, int consider_flagged)
1214 char to_us = ' ';
1215 char branch_to_us = ' ';
1216 PINETHRD_S *nthrd, *bthrd;
1217 MESSAGECACHE *mc;
1219 if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
1220 return to_us;
1222 if(thrd->next){
1223 nthrd = fetch_thread(stream, thrd->next);
1224 if(nthrd)
1225 to_us = to_us_symbol_for_thread(stream, nthrd, consider_flagged);
1228 if(((consider_flagged && to_us != '*') || (!consider_flagged && to_us != '+'))
1229 && thrd->branch){
1230 bthrd = fetch_thread(stream, thrd->branch);
1231 if(bthrd)
1232 branch_to_us = to_us_symbol_for_thread(stream, bthrd, consider_flagged);
1234 /* use branch to_us symbol if it has higher priority than what we have so far */
1235 if(to_us == ' '){
1236 if(branch_to_us == '-' || branch_to_us == '+' || branch_to_us == '*')
1237 to_us = branch_to_us;
1239 else if(to_us == '-'){
1240 if(branch_to_us == '+' || branch_to_us == '*')
1241 to_us = branch_to_us;
1243 else if(to_us == '+'){
1244 if(branch_to_us == '*')
1245 to_us = branch_to_us;
1249 if((consider_flagged && to_us != '*') || (!consider_flagged && to_us != '+')){
1250 if(consider_flagged && thrd && thrd->rawno > 0L
1251 && stream && thrd->rawno <= stream->nmsgs
1252 && (mc = mail_elt(stream, thrd->rawno))
1253 && FLAG_MATCH(F_FLAG, mc, stream))
1254 to_us = '*';
1255 else if(to_us != '+' && !IS_NEWS(stream)){
1256 INDEXDATA_S idata;
1257 MESSAGECACHE *mc;
1258 ADDRESS *addr;
1260 memset(&idata, 0, sizeof(INDEXDATA_S));
1261 idata.stream = stream;
1262 idata.rawno = thrd->rawno;
1263 idata.msgno = mn_raw2m(sp_msgmap(stream), idata.rawno);
1264 if(idata.rawno > 0L && stream && idata.rawno <= stream->nmsgs
1265 && (mc = mail_elt(stream, idata.rawno))){
1266 idata.size = mc->rfc822_size;
1267 index_data_env(&idata,
1268 pine_mail_fetchenvelope(stream, idata.rawno));
1270 else
1271 idata.bogus = 2;
1273 for(addr = fetch_to(&idata); addr; addr = addr->next)
1274 if(address_is_us(addr, ps_global)){
1275 to_us = '+';
1276 break;
1279 if(to_us != '+' && resent_to_us(&idata))
1280 to_us = '+';
1282 if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global))
1283 for(addr = fetch_cc(&idata); addr; addr = addr->next)
1284 if(address_is_us(addr, ps_global)){
1285 to_us = '-';
1286 break;
1291 return to_us;
1296 * This sets or clears flags for the messages at this node and below in
1297 * a tree. It doesn't just blindly do it, perhaps it should. Instead,
1298 * when un-hiding a subtree it leaves the sub-subtree hidden if a node
1299 * is collapsed.
1301 * Watch out when calling this. The thrd->branch is not part of thrd.
1302 * Branch is a sibling to thrd, not a child. Zero out branch before calling
1303 * or call on thrd->next and worry about thrd separately.
1304 * Ok to call it on top-level thread which has no branch already.
1306 void
1307 set_thread_subtree(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap, int v, int flags)
1311 /* set or clear? */
1312 /* flags to set or clear */
1314 int hiding;
1315 unsigned long msgno;
1316 PINETHRD_S *nthrd, *bthrd;
1318 hiding = (flags == MN_CHID) && v;
1320 if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
1321 return;
1323 msgno = mn_raw2m(msgmap, thrd->rawno);
1325 set_lflag(stream, msgmap, msgno, flags, v);
1327 if(thrd->next && (hiding || !get_lflag(stream,NULL,thrd->rawno,MN_COLL))){
1328 nthrd = fetch_thread(stream, thrd->next);
1329 if(nthrd)
1330 set_thread_subtree(stream, nthrd, msgmap, v, flags);
1333 if(thrd->branch){
1334 bthrd = fetch_thread(stream, thrd->branch);
1335 if(bthrd)
1336 set_thread_subtree(stream, bthrd, msgmap, v, flags);
1342 * View a thread. Move from the thread index screen to a message index
1343 * screen for the current thread.
1345 * set_lflags - Set the local flags appropriately to start viewing
1346 * the thread. We would not want to set this if we are
1347 * already viewing the thread (and expunge or new mail
1348 * happened) and we want to preserve the collapsed state
1349 * of the subthreads.
1352 view_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int set_lflags)
1354 PINETHRD_S *thrd = NULL;
1355 unsigned long rawno, cur;
1357 if(!any_messages(msgmap, NULL, "to View"))
1358 return 0;
1360 if(!(stream && msgmap))
1361 return 0;
1363 rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
1364 if(rawno)
1365 thrd = fetch_thread(stream, rawno);
1367 if(thrd && thrd->top && thrd->top != thrd->rawno)
1368 thrd = fetch_thread(stream, thrd->top);
1370 if(!thrd)
1371 return 0;
1374 * Clear hidden and collapsed flag for this thread.
1375 * And set CHID2.
1376 * Don't have to worry about there being a branch because
1377 * this is a toplevel thread.
1379 if(set_lflags){
1380 set_thread_lflags(stream, thrd, msgmap, MN_COLL | MN_CHID, 0);
1381 set_thread_lflags(stream, thrd, msgmap, MN_CHID2, 1);
1385 * If this is one of those wacky users who like to sort backwards
1386 * they would probably prefer that the current message be the last
1387 * one in the thread (the one highest up the screen).
1389 if(mn_get_revsort(msgmap)){
1390 cur = mn_get_cur(msgmap);
1391 while(cur > 1L && get_lflag(stream, msgmap, cur-1L, MN_CHID2))
1392 cur--;
1394 if(cur != mn_get_cur(msgmap))
1395 mn_set_cur(msgmap, cur);
1398 /* first message in thread might be hidden if zoomed */
1399 if(any_lflagged(msgmap, MN_HIDE)){
1400 cur = mn_get_cur(msgmap);
1401 while(get_lflag(stream, msgmap, cur, MN_HIDE))
1402 cur++;
1404 if(cur != mn_get_cur(msgmap))
1405 mn_set_cur(msgmap, cur);
1408 msgmap->top = mn_get_cur(msgmap);
1409 sp_set_viewing_a_thread(stream, 1);
1411 state->mangled_screen = 1;
1412 setup_for_index_index_screen();
1414 return 1;
1419 unview_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap)
1421 PINETHRD_S *thrd = NULL, *topthrd = NULL;
1422 unsigned long rawno;
1424 if(!(stream && msgmap))
1425 return 0;
1427 rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
1428 if(rawno)
1429 thrd = fetch_thread(stream, rawno);
1431 if(thrd && thrd->top)
1432 topthrd = fetch_thread(stream, thrd->top);
1434 if(!topthrd)
1435 return 0;
1437 /* hide this thread */
1438 set_thread_lflags(stream, topthrd, msgmap, MN_CHID, 1);
1440 /* clear special CHID2 flags for this thread */
1441 set_thread_lflags(stream, topthrd, msgmap, MN_CHID2, 0);
1443 /* clear CHID for top-level message and set COLL */
1444 set_lflag(stream, msgmap, mn_raw2m(msgmap, topthrd->rawno), MN_CHID, 0);
1445 set_lflag(stream, msgmap, mn_raw2m(msgmap, topthrd->rawno), MN_COLL, 1);
1447 mn_set_cur(msgmap, mn_raw2m(msgmap, topthrd->rawno));
1448 sp_set_viewing_a_thread(stream, 0);
1449 setup_for_thread_index_screen();
1451 return 1;
1455 PINETHRD_S *
1456 find_thread_by_number(MAILSTREAM *stream, MSGNO_S *msgmap, long int target, PINETHRD_S *startthrd)
1458 PINETHRD_S *thrd = NULL;
1460 if(!(stream && msgmap))
1461 return(thrd);
1463 thrd = startthrd;
1465 if(!thrd || !(thrd->prevthd || thrd->nextthd))
1466 thrd = fetch_thread(stream, mn_m2raw(msgmap, mn_get_cur(msgmap)));
1468 if(thrd && !(thrd->prevthd || thrd->nextthd) && thrd->head)
1469 thrd = fetch_thread(stream, thrd->head);
1471 if(thrd){
1472 /* go forward from here */
1473 if(thrd->thrdno < target){
1474 while(thrd){
1475 if(thrd->thrdno == target)
1476 break;
1478 if(mn_get_revsort(msgmap) && thrd->prevthd)
1479 thrd = fetch_thread(stream, thrd->prevthd);
1480 else if(!mn_get_revsort(msgmap) && thrd->nextthd)
1481 thrd = fetch_thread(stream, thrd->nextthd);
1482 else
1483 thrd = NULL;
1486 /* back up from here */
1487 else if(thrd->thrdno > target
1488 && (mn_get_revsort(msgmap)
1489 || (thrd->thrdno - target) < (target - 1L))){
1490 while(thrd){
1491 if(thrd->thrdno == target)
1492 break;
1494 if(mn_get_revsort(msgmap) && thrd->nextthd)
1495 thrd = fetch_thread(stream, thrd->nextthd);
1496 else if(!mn_get_revsort(msgmap) && thrd->prevthd)
1497 thrd = fetch_thread(stream, thrd->prevthd);
1498 else
1499 thrd = NULL;
1502 /* go forward from head */
1503 else if(thrd->thrdno > target){
1504 if(thrd->head){
1505 thrd = fetch_thread(stream, thrd->head);
1506 while(thrd){
1507 if(thrd->thrdno == target)
1508 break;
1510 if(thrd->nextthd)
1511 thrd = fetch_thread(stream, thrd->nextthd);
1512 else
1513 thrd = NULL;
1519 return(thrd);
1524 * Set search bit for every message in a thread.
1526 * Watch out when calling this. The thrd->branch is not part of thrd.
1527 * Branch is a sibling to thrd, not a child. Zero out branch before calling
1528 * or call on thrd->next and worry about thrd separately. Top-level threads
1529 * already have a branch equal to zero.
1531 * If msgset is non-NULL, then only set the search bit for a message if that
1532 * message is included in the msgset.
1534 void
1535 set_search_bit_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, SEARCHSET **msgset)
1537 PINETHRD_S *nthrd, *bthrd;
1539 if(!(stream && thrd))
1540 return;
1542 if(thrd->rawno > 0L && thrd->rawno <= stream->nmsgs
1543 && (!(msgset && *msgset) || in_searchset(*msgset, thrd->rawno)))
1544 mm_searched(stream, thrd->rawno);
1546 if(thrd->next){
1547 nthrd = fetch_thread(stream, thrd->next);
1548 if(nthrd)
1549 set_search_bit_for_thread(stream, nthrd, msgset);
1552 if(thrd->branch){
1553 bthrd = fetch_thread(stream, thrd->branch);
1554 if(bthrd)
1555 set_search_bit_for_thread(stream, bthrd, msgset);