📄 thread.c
字号:
}/* add cur as a prior sibling of *new, with parent newparent */static void insert_message (THREAD **new, THREAD *newparent, THREAD *cur){ if (*new) (*new)->prev = cur; cur->parent = newparent; cur->next = *new; cur->prev = NULL; *new = cur;}/* thread by subject things that didn't get threaded by message-id */static void pseudo_threads (CONTEXT *ctx){ THREAD *tree = ctx->tree, *top = tree; THREAD *tmp, *cur, *parent, *curchild, *nextchild; if (!ctx->subj_hash) ctx->subj_hash = mutt_make_subj_hash (ctx); while (tree) { cur = tree; tree = tree->next; if ((parent = find_subject (ctx, cur)) != NULL) { cur->fake_thread = 1; unlink_message (&top, cur); insert_message (&parent->child, parent, cur); parent->sort_children = 1; tmp = cur; FOREVER { while (!tmp->message) tmp = tmp->child; /* if the message we're attaching has pseudo-children, they * need to be attached to its parent, so move them up a level. * but only do this if they have the same real subject as the * parent, since otherwise they rightly belong to the message * we're attaching. */ if (tmp == cur || !mutt_strcmp (tmp->message->env->real_subj, parent->message->env->real_subj)) { tmp->message->subject_changed = 0; for (curchild = tmp->child; curchild; ) { nextchild = curchild->next; if (curchild->fake_thread) { unlink_message (&tmp->child, curchild); insert_message (&parent->child, parent, curchild); } curchild = nextchild; } } while (!tmp->next && tmp != cur) { tmp = tmp->parent; } if (tmp == cur) break; tmp = tmp->next; } } } ctx->tree = top;}void mutt_clear_threads (CONTEXT *ctx){ int i; for (i = 0; i < ctx->msgcount; i++) { ctx->hdrs[i]->thread = NULL; ctx->hdrs[i]->threaded = 0; } ctx->tree = NULL; if (ctx->thread_hash) hash_destroy (&ctx->thread_hash, *free);}int compare_threads (const void *a, const void *b){ static sort_t *sort_func = NULL; if (a || b) return ((*sort_func) (&(*((THREAD **) a))->sort_key, &(*((THREAD **) b))->sort_key)); /* a hack to let us reset sort_func even though we can't * have extra arguments because of qsort */ else { sort_func = NULL; sort_func = mutt_get_sort_func (Sort); return (sort_func ? 1 : 0); }}THREAD *mutt_sort_subthreads (THREAD *thread, int init){ THREAD **array, *sort_key, *top, *tmp; HEADER *oldsort_key; int i, array_size, sort_top = 0; /* we put things into the array backwards to save some cycles, * but we want to have to move less stuff around if we're * resorting, so we sort backwards and then put them back * in reverse order so they're forwards */ Sort ^= SORT_REVERSE; if (!compare_threads (NULL, NULL)) return (thread); top = thread; array = safe_calloc ((array_size = 256), sizeof (THREAD *)); while (1) { if (init || !thread->sort_key) { thread->sort_key = NULL; if (thread->parent) thread->parent->sort_children = 1; else sort_top = 1; } if (thread->child) { thread = thread->child; continue; } else { /* if it has no children, it must be real. sort it on its own merits */ thread->sort_key = thread->message; if (thread->next) { thread = thread->next; continue; } } while (!thread->next) { /* if it has siblings and needs to be sorted, sort it... */ if (thread->prev && (thread->parent ? thread->parent->sort_children : sort_top)) { /* put them into the array */ for (i = 0; thread; i++, thread = thread->prev) { if (i >= array_size) safe_realloc (&array, (array_size *= 2) * sizeof (THREAD *)); array[i] = thread; } qsort ((void *) array, i, sizeof (THREAD *), *compare_threads); /* attach them back together. make thread the last sibling. */ thread = array[0]; thread->next = NULL; array[i - 1]->prev = NULL; if (thread->parent) thread->parent->child = array[i - 1]; else top = array[i - 1]; while (--i) { array[i - 1]->prev = array[i]; array[i]->next = array[i - 1]; } } if (thread->parent) { tmp = thread; thread = thread->parent; if (!thread->sort_key || thread->sort_children) { /* make sort_key the first or last sibling, as appropriate */ sort_key = (!(Sort & SORT_LAST) ^ !(Sort & SORT_REVERSE)) ? thread->child : tmp; /* we just sorted its children */ thread->sort_children = 0; oldsort_key = thread->sort_key; thread->sort_key = thread->message; if (Sort & SORT_LAST) { if (!thread->sort_key || ((((Sort & SORT_REVERSE) ? 1 : -1) * compare_threads ((void *) &thread, (void *) &sort_key)) > 0)) thread->sort_key = sort_key->sort_key; } else if (!thread->sort_key) thread->sort_key = sort_key->sort_key; /* if its sort_key has changed, we need to resort it and siblings */ if (oldsort_key != thread->sort_key) { if (thread->parent) thread->parent->sort_children = 1; else sort_top = 1; } } } else { Sort ^= SORT_REVERSE; FREE (&array); return (top); } } thread = thread->next; }}static void check_subjects (CONTEXT *ctx, int init){ HEADER *cur; THREAD *tmp; int i; for (i = 0; i < ctx->msgcount; i++) { cur = ctx->hdrs[i]; if (cur->thread->check_subject) cur->thread->check_subject = 0; else if (!init) continue; /* figure out which messages have subjects different than their parents' */ tmp = cur->thread->parent; while (tmp && !tmp->message) { tmp = tmp->parent; } if (!tmp) cur->subject_changed = 1; else if (cur->env->real_subj && tmp->message->env->real_subj) cur->subject_changed = mutt_strcmp (cur->env->real_subj, tmp->message->env->real_subj) ? 1 : 0; else cur->subject_changed = (cur->env->real_subj || tmp->message->env->real_subj) ? 1 : 0; }}void mutt_sort_threads (CONTEXT *ctx, int init){ HEADER *cur; int i, oldsort, using_refs = 0; THREAD *thread, *new, *tmp, top; LIST *ref = NULL; /* set Sort to the secondary method to support the set sort_aux=reverse-* * settings. The sorting functions just look at the value of * SORT_REVERSE */ oldsort = Sort; Sort = SortAux; if (!ctx->thread_hash) init = 1; if (init) ctx->thread_hash = hash_create (ctx->msgcount * 2); /* we want a quick way to see if things are actually attached to the top of the * thread tree or if they're just dangling, so we attach everything to a top * node temporarily */ top.parent = top.next = top.prev = NULL; top.child = ctx->tree; for (thread = ctx->tree; thread; thread = thread->next) thread->parent = ⊤ /* put each new message together with the matching messageless THREAD if it * exists. otherwise, if there is a THREAD that already has a message, thread * new message as an identical child. if we didn't attach the message to a * THREAD, make a new one for it. */ for (i = 0; i < ctx->msgcount; i++) { cur = ctx->hdrs[i]; if (!cur->thread) { if ((!init || option (OPTDUPTHREADS)) && cur->env->message_id) thread = hash_find (ctx->thread_hash, cur->env->message_id); else thread = NULL; if (thread && !thread->message) { /* this is a message which was missing before */ thread->message = cur; cur->thread = thread; thread->check_subject = 1; /* mark descendants as needing subject_changed checked */ for (tmp = (thread->child ? thread->child : thread); tmp != thread; ) { while (!tmp->message) tmp = tmp->child; tmp->check_subject = 1; while (!tmp->next && tmp != thread) tmp = tmp->parent; if (tmp != thread) tmp = tmp->next; } if (thread->parent) { /* remove threading info above it based on its children, which we'll * recalculate based on its headers. make sure not to leave * dangling missing messages. note that we haven't kept track * of what info came from its children and what from its siblings' * children, so we just remove the stuff that's definitely from it */ do { tmp = thread->parent; unlink_message (&tmp->child, thread); thread->parent = NULL; thread->sort_key = NULL; thread->fake_thread = 0; thread = tmp; } while (thread != &top && !thread->child && !thread->message); } } else { new = (option (OPTDUPTHREADS) ? thread : NULL); thread = safe_calloc (1, sizeof (THREAD)); thread->message = cur; thread->check_subject = 1; cur->thread = thread; hash_insert (ctx->thread_hash, cur->env->message_id ? cur->env->message_id : "", thread, 1); if (new) { if (new->duplicate_thread) new = new->parent; thread = cur->thread; insert_message (&new->child, new, thread); thread->duplicate_thread = 1; thread->message->threaded = 1; } } } else { /* unlink pseudo-threads because they might be children of newly * arrived messages */ thread = cur->thread; for (new = thread->child; new; ) { tmp = new->next; if (new->fake_thread) { unlink_message (&thread->child, new); insert_message (&top.child, &top, new); new->fake_thread = 0; } new = tmp; } } } /* thread by references */ for (i = 0; i < ctx->msgcount; i++) { cur = ctx->hdrs[i]; if (cur->threaded) continue; cur->threaded = 1; thread = cur->thread; using_refs = 0; while (1) { if (using_refs == 0) { /* look at the beginning of in-reply-to: */ if ((ref = cur->env->in_reply_to) != NULL) using_refs = 1; else { ref = cur->env->references; using_refs = 2; } } else if (using_refs == 1) { /* if there's no references header, use all the in-reply-to: * data that we have. otherwise, use the first reference * if it's different than the first in-reply-to, otherwise use * the second reference (since at least eudora puts the most * recent reference in in-reply-to and the rest in references) */ if (!cur->env->references) ref = ref->next; else { if (mutt_strcmp (ref->data, cur->env->references->data)) ref = cur->env->references; else ref = cur->env->references->next; using_refs = 2; } } else ref = ref->next; /* go on with references */ if (!ref) break; if ((new = hash_find (ctx->thread_hash, ref->data)) == NULL) { new = safe_calloc (1, sizeof (THREAD)); hash_insert (ctx->thread_hash, ref->data, new, 1); } else { if (new->duplicate_thread) new = new->parent; if (is_descendant (new, thread)) /* no loops! */ continue; } if (thread->parent) unlink_message (&top.child, thread); insert_message (&new->child, new, thread); thread = new; if (thread->message || (thread->parent && thread->parent != &top)) break; } if (!thread->parent) insert_message (&top.child, &top, thread); } /* detach everything from the temporary top node */ for (thread = top.child; thread; thread = thread->next) { thread->parent = NULL; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -