home *** CD-ROM | disk | FTP | other *** search
- /* $Id: rt-rn.c,v 2.3 1992/12/14 00:14:00 davison Trn $
- */
-
- #include "EXTERN.h"
- #include "common.h"
- #include "term.h"
- #include "final.h"
- #include "util.h"
- #include "bits.h"
- #include "artio.h"
- #include "ng.h"
- #include "ngdata.h"
- #include "search.h"
- #include "artstate.h"
- #include "backpage.h"
-
- #ifdef USETHREADS
-
- #include "threads.h"
- #include "rthreads.h"
-
- static void find_depth(), cache_tree(), display_tree();
- static PACKED_ARTICLE *first_sib(), *last_sib();
- static char letter();
-
- /* Find the article structure information based on article number.
- */
- void
- find_article(artnum)
- ART_NUM artnum;
- {
- register PACKED_ARTICLE *article;
- register int i;
-
- if (!p_articles) {
- p_art = Nullart;
- return;
- }
-
- if (!p_art) {
- p_art = p_articles;
- }
- /* Start looking for the article num from our last known spot in the array.
- ** That way, if we already know where we are, we run into ourselves right
- ** away.
- */
- for (article=p_art, i=p_art-p_articles; i < total.article; article++,i++) {
- if (article->num == artnum) {
- p_art = article;
- return;
- }
- }
- /* Didn't find it, so search the ones before our current position.
- */
- for (article = p_articles; article != p_art; article++) {
- if (article->num == artnum) {
- p_art = article;
- return;
- }
- }
- p_art = Nullart;
- }
-
- static char tree_indent[] = {
- ' ', 0,
- ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0,
- ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0,
- ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0,
- ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0,
- ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0,
- ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0,
- ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0,
- ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0,
- ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0,
- ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0,
- ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0,
- ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0,
- ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0,
- ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0
- };
-
- char letters[] = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz?";
-
- static PACKED_ARTICLE *tree_article;
-
- static int max_depth, max_line = -1;
- static int first_depth, first_line;
- static int my_depth, my_line;
- static bool node_on_line;
- static int node_line_cnt;
-
- static int line_num;
- static int header_indent;
-
- static char *tree_lines[11];
- static char tree_buff[128], *str;
-
- /* Prepare tree display for inclusion in the article header.
- */
- void
- init_tree()
- {
- register PACKED_ARTICLE *article;
-
- while (max_line >= 0) { /* free any previous tree data */
- free(tree_lines[max_line--]);
- }
- tree_article = curr_p_art;
-
- if (!curr_p_art) {
- return;
- }
- article = p_articles + p_roots[curr_p_art->root].articles;
-
- max_depth = max_line = my_depth = my_line = node_line_cnt = 0;
- find_depth(article, 0);
-
- if (max_depth <= 5) {
- first_depth = 0;
- } else {
- if (my_depth+2 > max_depth) {
- first_depth = max_depth - 5;
- } else if ((first_depth = my_depth - 3) < 0) {
- first_depth = 0;
- }
- max_depth = first_depth + 5;
- }
- if (--max_line < max_tree_lines) {
- first_line = 0;
- } else {
- if (my_line + max_tree_lines/2 > max_line) {
- first_line = max_line - (max_tree_lines-1);
- } else if ((first_line = my_line - (max_tree_lines-1)/2) < 0) {
- first_line = 0;
- }
- max_line = first_line + max_tree_lines-1;
- }
-
- str = tree_buff; /* initialize first line's data */
- *str++ = ' ';
- node_on_line = FALSE;
- line_num = 0;
- /* cache our portion of the tree */
- cache_tree(article, 0, tree_indent);
-
- max_depth = (max_depth-first_depth) * 5; /* turn depth into char width */
- max_line -= first_line; /* turn max_line into count */
- /* shorten tree if lower lines aren't visible */
- if (node_line_cnt < max_line) {
- max_line = node_line_cnt + 1;
- }
- }
-
- /* A recursive routine to find the maximum tree extents and where we are.
- */
- static void
- find_depth(article, depth)
- PACKED_ARTICLE *article;
- int depth;
- {
- if (depth > max_depth) {
- max_depth = depth;
- }
- for (;;) {
- if (article == tree_article) {
- my_depth = depth;
- my_line = max_line;
- }
- if (article->child_cnt) {
- find_depth(article+1, depth+1);
- } else {
- max_line++;
- }
- if (!article->siblings) {
- break;
- }
- article += article->siblings;
- }
- }
-
- /* Place the tree display in a maximum of 11 lines x 6 nodes.
- */
- static void
- cache_tree(article, depth, cp)
- PACKED_ARTICLE *article;
- int depth;
- char *cp;
- {
- int depth_mode;
-
- cp[1] = ' ';
- if (depth >= first_depth && depth <= max_depth) {
- cp += 5;
- depth_mode = 1;
- } else if (depth+1 == first_depth) {
- depth_mode = 2;
- } else {
- cp = tree_indent;
- depth_mode = 0;
- }
- for (;;) {
- switch (depth_mode) {
- case 1: {
- char ch;
-
- *str++ = (article->flags & ROOT_ARTICLE)? ' ' : '-';
- if (article == tree_article) {
- *str++ = '*';
- }
- if (was_read(article->num)) {
- *str++ = '(';
- ch = ')';
- } else {
- *str++ = '[';
- ch = ']';
- }
- if (article == recent_p_art && article != tree_article) {
- *str++ = '@';
- }
- *str++ = letter(article);
- *str++ = ch;
- if (article->child_cnt) {
- *str++ = (article->child_cnt == 1)? '-' : '+';
- }
- if (article->siblings) {
- *cp = '|';
- } else {
- *cp = ' ';
- }
- node_on_line = TRUE;
- break;
- }
- case 2:
- *tree_buff = (!article->child_cnt)? ' ' :
- (article->child_cnt == 1)? '-' : '+';
- break;
- default:
- break;
- }
- if (article->child_cnt) {
- cache_tree(article+1, depth+1, cp);
- cp[1] = '\0';
- } else {
- if (!node_on_line && first_line == line_num) {
- first_line++;
- }
- if (line_num >= first_line) {
- if (str[-1] == ' ') {
- str--;
- }
- *str = '\0';
- tree_lines[line_num-first_line]
- = safemalloc(str-tree_buff + 1);
- strcpy(tree_lines[line_num - first_line], tree_buff);
- if (node_on_line) {
- node_line_cnt = line_num - first_line;
- }
- }
- line_num++;
- node_on_line = FALSE;
- }
- if (!article->siblings || line_num > max_line) {
- break;
- }
- article += article->siblings;
- if (!article->siblings) {
- *cp = '\\';
- }
- if (!first_depth) {
- tree_indent[5] = ' ';
- }
- strcpy(tree_buff, tree_indent+5);
- str = tree_buff + strlen(tree_buff);
- }
- }
-
- /* Output a header line with possible tree display on the right hand side.
- ** Does automatic wrapping of lines that are too long.
- */
- int
- tree_puts(orig_line, header_line, use_underline)
- char *orig_line;
- ART_LINE header_line;
- int use_underline;
- {
- char *buf;
- register char *line, *cp, *end;
- int pad_cnt, wrap_at;
- ART_LINE start_line = header_line;
- int i;
- char ch;
-
- /* Make a modifiable copy of the line */
- buf = safemalloc(strlen(orig_line) + 2); /* yes, I mean "2" */
- strcpy(buf, orig_line);
- line = buf;
-
- /* Change any embedded control characters to spaces */
- for (end = line; *end && *end != '\n'; end++) {
- if ((unsigned char)*end < ' ') {
- *end = ' ';
- }
- }
- *end = '\0';
-
- if (!*line) {
- strcpy(line, " ");
- end = line+1;
- }
-
- /* If this is the first subject line, output it with a preceeding [1] */
- if (use_underline && curr_p_art && (unsigned char)*line > ' ') {
- #ifdef NOFIREWORKS
- no_sofire();
- #endif
- standout();
- putchar('[');
- putchar(letter(curr_p_art));
- putchar(']');
- un_standout();
- putchar(' ');
- header_indent = 4;
- line += 9;
- i = 0;
- } else {
- if (*line != ' ') {
- /* A "normal" header line -- output keyword and set header_indent
- ** _except_ for the first line, which is a non-standard header.
- */
- if (!header_line || !(cp = index(line, ':')) || *++cp != ' ') {
- header_indent = 0;
- } else {
- *cp = '\0';
- fputs(line, stdout);
- putchar(' ');
- header_indent = ++cp - line;
- line = cp;
- if (!*line) {
- *--line = ' ';
- }
- }
- i = 0;
- } else {
- /* Skip whitespace of continuation lines and prepare to indent */
- while (*++line == ' ') {
- ;
- }
- i = header_indent;
- }
- }
- for (; *line; i = header_indent) {
- #ifdef CLEAREOL
- maybe_eol();
- #endif
- if (i) {
- putchar('+');
- while (--i) {
- putchar(' ');
- }
- }
- /* If no (more) tree lines, wrap at COLS-1 */
- if (max_line < 0 || header_line > max_line+1) {
- wrap_at = COLS-1;
- } else {
- wrap_at = COLS - max_depth - 5 - 3;
- }
- /* Figure padding between header and tree output, wrapping long lines */
- pad_cnt = wrap_at - (end - line + header_indent);
- if (pad_cnt <= 0) {
- cp = line + wrap_at - header_indent - 1;
- pad_cnt = 1;
- while (cp > line && *cp != ' ') {
- if (*--cp == ',' || *cp == '.' || *cp == '-' || *cp == '!') {
- cp++;
- break;
- }
- pad_cnt++;
- }
- if (cp == line) {
- cp += wrap_at - header_indent;
- pad_cnt = 0;
- }
- ch = *cp;
- *cp = '\0';
- /* keep rn's backpager happy */
- vwtary(artline, vrdary(artline - 1));
- artline++;
- } else {
- cp = end;
- ch = '\0';
- }
- if (use_underline) {
- underprint(line);
- } else {
- fputs(line, stdout);
- }
- *cp = ch;
- /* Skip whitespace in wrapped line */
- while (*cp == ' ') {
- cp++;
- }
- line = cp;
- /* Check if we've got any tree lines to output */
- if (wrap_at != COLS-1 && header_line <= max_line) {
- char *cp1, *cp2;
-
- do {
- putchar(' ');
- } while (pad_cnt--);
- /* Check string for the '*' flagging our current node
- ** and the '@' flagging our prior node.
- */
- cp = tree_lines[header_line];
- cp1 = index(cp, '*');
- cp2 = index(cp, '@');
- if (cp1 != Nullch) {
- *cp1 = '\0';
- }
- if (cp2 != Nullch) {
- *cp2 = '\0';
- }
- fputs(cp, stdout);
- /* Handle standout output for '*' and '@' marked nodes, then
- ** continue with the rest of the line.
- */
- while (cp1 || cp2) {
- standout();
- if (cp1 && (!cp2 || cp1 < cp2)) {
- cp = cp1;
- cp1 = Nullch;
- *cp++ = '*';
- putchar(*cp++);
- putchar(*cp++);
- } else {
- cp = cp2;
- cp2 = Nullch;
- *cp++ = '@';
- }
- putchar(*cp++);
- un_standout();
- if (*cp) {
- fputs(cp, stdout);
- }
- }/* while */
- }/* if */
- putchar('\n') ; FLUSH;
- header_line++;
- }/* for remainder of line */
-
- /* free allocated copy of line */
- free(buf);
-
- /* return number of lines displayed */
- return header_line - start_line;
- }
-
- /* Output any parts of the tree that are left to display. Called at the
- ** end of each header.
- */
- int
- finish_tree(last_line)
- ART_LINE last_line;
- {
- ART_LINE start_line = last_line;
-
- while (last_line <= max_line) {
- artline++;
- last_line += tree_puts("+", last_line, 0);
- vwtary(artline, artpos); /* keep rn's backpager happy */
- }
- return last_line - start_line;
- }
-
- /* Output the entire article tree for the user.
- */
- void
- entire_tree()
- {
- int j, root;
-
- if (!ThreadedGroup) {
- ThreadedGroup = use_data(TRUE);
- find_article(art);
- curr_p_art = p_art;
- }
- if (check_page_line()) {
- return;
- }
- if (!p_art) {
- #ifdef VERBOSE
- IF (verbose)
- fputs("\nNo article tree to display.\n", stdout);
- ELSE
- #endif
- #ifdef TERSE
- fputs("\nNo tree.\n", stdout);
- #endif
- } else {
- root = p_art->root;
- #ifdef NOFIREWORKS
- no_sofire();
- #endif
- standout();
- printf("T%ld:\n", (long)p_roots[root].root_num);
- un_standout();
- if (check_page_line()) {
- return;
- }
- putchar('\n');
- for (j = 0; j < p_roots[root].subject_cnt; j++) {
- sprintf(buf, "[%c] %s\n", letters[j > 9+26+26 ? 9+26+26 : j],
- subject_ptrs[root_subjects[root]+j]);
- if (check_page_line()) {
- return;
- }
- fputs(buf, stdout);
- }
- if (check_page_line()) {
- return;
- }
- putchar('\n');
- if (check_page_line()) {
- return;
- }
- putchar(' ');
- buf[3] = '\0';
- display_tree(p_articles+p_roots[p_art->root].articles, tree_indent);
-
- if (check_page_line()) {
- return;
- }
- putchar('\n');
- }
- }
-
- /* A recursive routine to output the entire article tree.
- */
- static void
- display_tree(article, cp)
- PACKED_ARTICLE *article;
- char *cp;
- {
- if (cp - tree_indent > COLS || page_line < 0) {
- return;
- }
- cp[1] = ' ';
- cp += 5;
- for (;;) {
- putchar((article->flags & ROOT_ARTICLE)? ' ' : '-');
- if (was_read(article->num)) {
- buf[0] = '(';
- buf[2] = ')';
- } else {
- buf[0] = '[';
- buf[2] = ']';
- }
- buf[1] = letter(article);
- if (article == curr_p_art) {
- standout();
- fputs(buf, stdout);
- un_standout();
- } else if (article == recent_p_art) {
- putchar(buf[0]);
- standout();
- putchar(buf[1]);
- un_standout();
- putchar(buf[2]);
- } else {
- fputs(buf, stdout);
- }
-
- if (article->siblings) {
- *cp = '|';
- } else {
- *cp = ' ';
- }
- if (article->child_cnt) {
- putchar((article->child_cnt == 1)? '-' : '+');
- display_tree(article+1, cp);
- cp[1] = '\0';
- } else {
- putchar('\n') ; FLUSH;
- }
- if (!article->siblings) {
- break;
- }
- article += article->siblings;
- if (!article->siblings) {
- *cp = '\\';
- }
- tree_indent[5] = ' ';
- if (check_page_line()) {
- return;
- }
- fputs(tree_indent+5, stdout);
- }
- }
-
- int
- check_page_line()
- {
- if (page_line < 0) {
- return -1;
- }
- if (page_line >= LINES || int_count) {
- register int cmd = -1;
- if (int_count || (cmd = get_anything())) {
- page_line = -1; /* disable further printing */
- if (cmd > 0) {
- pushchar(cmd);
- }
- return cmd;
- }
- }
- page_line++;
- return 0;
- }
-
- /* Calculate the subject letter representation. "Place-holder" nodes
- ** are marked with a ' ', others get a letter in the sequence:
- ** ' ', '1'-'9', 'A'-'Z', 'a'-'z', '?'
- */
- static char
- letter(article)
- PACKED_ARTICLE *article;
- {
- register int subj = article->subject;
-
- if (subj < 0) {
- return ' ';
- }
- subj -= root_subjects[article->root];
- if (subj < 9+26+26) {
- return letters[subj];
- }
- return '?';
- }
-
- /* Find the first unread article in the (possibly selected) root order.
- */
- void
- first_art()
- {
- if (!ThreadedGroup) {
- art = firstart;
- return;
- }
- p_art = Nullart;
- art = lastart+1;
- follow_thread('n');
- }
-
- /* Perform a command over all or a section of the article tree. Most of
- ** the option letters match commands entered from article mode:
- ** n - find the next unread article after current article.
- ** ^N - find the next unread article with the same subject.
- ** N - goto the next article in the thread.
- ** j - junk the entire thread.
- ** J - junk the entire thread, chasing xrefs.
- ** k - junk all articles with this same subject, chasing xrefs.
- ** K - kill all this article's descendants, chasing xrefs (we know that
- ** the caller killed the current article on the way here).
- ** u - mark entire thread as "unread".
- ** U - mark this article and its descendants as "unread".
- ** f - follow the thread (like 'n'), but don't attempt to find a new thread
- ** if we run off the end.
- */
- void
- follow_thread(cmd)
- char_int cmd;
- {
- int curr_subj = -1, selected;
- PACKED_ARTICLE *root_limit, *p_art_old = Nullart;
- bool subthread_flag, chase_flag;
-
- reread = (cmd == 'N');
-
- if (!p_art) {
- if (ThreadedGroup && art > lastart) {
- p_art = root_limit = p_articles;
- goto follow_root;
- }
- art++;
- return;
- }
- if (cmd == 'k' || cmd == Ctl('n')) {
- if ((curr_subj = p_art->subject) == -1) {
- return;
- }
- p_art_old = p_art;
- }
- selected = (selected_roots[p_art->root] & 1);
- if (cmd == 'U' || cmd == 'K') {
- subthread_flag = TRUE;
- p_art_old = p_art;
- } else {
- subthread_flag = FALSE;
- }
- chase_flag = (!olden_days && (cmd == 'J' || cmd == 'k' || cmd == 'K'));
-
- /* Some commands encompass the entire thread */
- if (cmd == 'k' || cmd == 'j' || cmd == 'J' || cmd == 'u') {
- p_art = p_articles + p_roots[p_art->root].articles;
- art = p_art->num;
- }
- /* The current article is already marked as read for 'K' */
- if (cmd == 'k' || cmd == 'j' || cmd == 'J') {
- if (!was_read(art) && (curr_subj < 0 || curr_subj == p_art->subject)) {
- set_read(art, selected, chase_flag);
- }
- cmd = 'K';
- }
- if (cmd == 'u') {
- p_art_old = p_art;
- cmd = 'U';
- }
- if (cmd == 'U') {
- if (p_art->subject != -1) {
- set_unread(art, selected);
- }
- root_article_cnts[p_art->root] = 1;
- scan_all_roots = FALSE;
- }
- follow_again:
- selected = (selected_roots[p_art->root] & 1);
- root_limit = upper_limit(p_art, subthread_flag);
- for (;;) {
- if (Ctl(cmd) == Ctl('n') || cmd == 'f')
- next_art();
- else
- p_art++;
- if (p_art == root_limit) {
- break;
- }
- if (!(art = p_art->num)) {
- continue;
- }
- if (cmd == 'K' || p_art->subject == -1) {
- if (!was_read(art)
- && (curr_subj < 0 || curr_subj == p_art->subject)) {
- set_read(art, selected, chase_flag);
- }
- } else if (cmd == 'U') {
- set_unread(art, selected);
- } else if (!was_read(art)
- && (curr_subj < 0 || curr_subj == p_art->subject)) {
- return;
- } else if (cmd == 'N') {
- return;
- }
- }/* for */
- if (p_art_old) {
- p_art = p_art_old;
- if (cmd == 'U' && p_art->subject != -1) {
- art = p_art->num;
- return;
- }
- p_art_old = Nullart;
- cmd = 'n';
- curr_subj = -1;
- subthread_flag = FALSE;
- goto follow_again;
- }
- if (cmd == 'f') {
- p_art = Nullart;
- art = lastart+1;
- return;
- }
- follow_root:
- if (root_limit != p_articles + total.article) {
- register int r;
-
- for (r = p_art->root; r < total.root; r++) {
- if (!selected_root_cnt || selected_roots[r]) {
- p_art = p_articles + p_roots[r].articles;
- art = p_art->num;
- if (p_art->subject == -1 || (cmd != 'N' && was_read(art))) {
- if (cmd != 'N') {
- cmd = 'n';
- }
- curr_subj = -1;
- subthread_flag = FALSE;
- goto follow_again;
- }
- return;
- }
- }
- }
- if (!count_roots(FALSE) && unthreaded) {
- /* No threaded articles left -- blow everything else away */
- for (art = firstbit; art <= lastart; art++) {
- oneless(art);
- }
- unthreaded = 0;
- }
- p_art = Nullart;
- art = lastart+1;
- if (cmd == 'N') {
- forcelast = TRUE;
- reread = FALSE;
- }
- }
-
- /* Bump p_art to the next article. Honors the breadth_first flag.
- */
- void
- next_art()
- {
- if (breadth_first) {
- for (;;) {
- if (p_art->siblings) {
- p_art += p_art->siblings;
- return; /* RETURN */
- }
- if (p_art->parent) {
- p_art += p_art->parent + 1;
- } else {
- p_art = p_articles + p_roots[p_art->root].articles;
- }
- for (;;) {
- if (p_art->child_cnt) {
- p_art++;
- return; /* RETURN */
- }
- while (!p_art->siblings) {
- if (!p_art->parent) {
- p_art = upper_limit(p_art, FALSE);
- return; /* RETURN */
- }
- p_art += p_art->parent;
- }
- p_art += p_art->siblings;
- }/* for */
- }/* for */
- } else {
- p_art++;
- }
- }
-
- /* Go backward in the article tree. Options match commands in article mode:
- ** p - previous unread article.
- ** ^P - previous unread article with same subject.
- ** P - previous article.
- */
- void
- backtrack_thread(cmd)
- char_int cmd;
- {
- int curr_subj = -1, selected;
- PACKED_ARTICLE *root_limit, *p_art_old = Nullart;
-
- if (art > lastart) {
- p_art = p_articles + total.article - 1;
- root_limit = Nullart;
- goto backtrack_root;
- }
- if (!p_art) {
- art--;
- return;
- }
- if (cmd == Ctl('p')) {
- if ((curr_subj = p_art->subject) == -1) {
- return;
- }
- p_art_old = p_art;
- }
- backtrack_again:
- selected = (selected_roots[p_art->root] & 1);
- root_limit = p_articles + p_roots[p_art->root].articles;
- for (;;) {
- if (p_art-- == root_limit) {
- break;
- }
- if (!(art = p_art->num)) {
- continue;
- }
- if (p_art->subject == -1) {
- set_read(art, selected, FALSE);
- } else if (!was_read(art)
- && (curr_subj < 0 || curr_subj == p_art->subject)) {
- return;
- } else if (cmd == 'P') {
- reread = TRUE;
- return;
- }
- }/* for */
- if (p_art_old) {
- p_art = p_art_old;
- p_art_old = Nullart;
- curr_subj = -1;
- goto backtrack_again;
- }
- backtrack_root:
- if (root_limit != p_articles) {
- register int r;
-
- for (r = p_art->root; r >= 0; r--) {
- if (!selected_root_cnt || selected_roots[r]) {
- art = p_art->num;
- if (cmd != 'P' && was_read(art)) {
- goto backtrack_again;
- }
- return;
- }
- p_art = p_articles + p_roots[r].articles - 1;
- }
- }
- p_art = Nullart;
- art = absfirst-1;
- }
-
- /* Find the next root (first if p_art == NULL). If roots are selected,
- ** only choose from selected roots.
- */
- void
- next_root()
- {
- register int r;
-
- reread = FALSE;
-
- if (p_art) {
- r = p_art->root+1;
- } else {
- r = 0;
- }
- for (; r < total.root; r++) {
- if (!selected_root_cnt || selected_roots[r]) {
- try_again:
- p_art = p_articles + p_roots[r].articles;
- art = p_art->num;
- if (p_art->subject == -1 || (!reread && was_read(art))) {
- follow_thread(reread ? 'N' : 'f');
- if (art == lastart+1) {
- if (scan_all_roots || selected_root_cnt
- || root_article_cnts[r]) {
- reread = TRUE;
- goto try_again;
- }
- continue;
- }
- }
- return;
- }
- }
- p_art = Nullart;
- art = lastart+1;
- forcelast = TRUE;
- }
-
- /* Find previous root (or last if p_art == NULL). If roots are selected,
- ** only choose from selected roots.
- */
- void
- prev_root()
- {
- register int r;
-
- reread = FALSE;
-
- if (p_art) {
- r = p_art->root - 1;
- } else {
- r = total.root - 1;
- }
- for (; r >= 0; r--) {
- if (!selected_root_cnt || selected_roots[r]) {
- try_again:
- p_art = p_articles + p_roots[r].articles;
- art = p_art->num;
- if (p_art->subject == -1 || (!reread && was_read(art))) {
- follow_thread(reread ? 'N' : 'f');
- if (art == lastart+1) {
- if (scan_all_roots || selected_root_cnt
- || root_article_cnts[r]) {
- reread = TRUE;
- goto try_again;
- }
- continue;
- }
- }
- return;
- }
- }
- p_art = Nullart;
- art = lastart+1;
- forcelast = TRUE;
- }
-
- /* Return a pointer value that we will equal when we've reached the end of
- ** the current (sub-)thread.
- */
- PACKED_ARTICLE *
- upper_limit(artp, subthread_flag)
- PACKED_ARTICLE *artp;
- bool_int subthread_flag;
- {
- if (subthread_flag) {
- for (;;) {
- if (artp->siblings) {
- return artp + artp->siblings;
- }
- if (!artp->parent) {
- break;
- }
- artp += artp->parent;
- }
- }
- return p_articles + (artp->root == total.root-1 ?
- total.article : p_roots[artp->root+1].articles);
- }
-
- /* Find the next "sibling" of this article, including cousins that are
- ** the same distance from the root as we are.
- */
- PACKED_ARTICLE *
- find_next_sib()
- {
- PACKED_ARTICLE *ta, *tb;
- int ascent;
-
- ascent = 0;
- ta = p_art;
- for (;;) {
- while (ta->siblings) {
- ta += ta->siblings;
- if (tb = first_sib(ta, ascent)) {
- return tb;
- }
- }
- if (!ta->parent) {
- break;
- }
- ta += ta->parent;
- ascent++;
- }
- return Nullart;
- }
-
- /* A recursive routine to find the first node at the proper depth. This
- ** article is at depth 0.
- */
- static PACKED_ARTICLE *
- first_sib(ta, depth)
- PACKED_ARTICLE *ta;
- int depth;
- {
- PACKED_ARTICLE *tb;
-
- if (!depth) {
- return ta;
- }
- for (;;) {
- if (ta->child_cnt && (tb = first_sib(ta+1, depth-1))) {
- return tb;
- }
- if (!ta->siblings) {
- return Nullart;
- }
- ta += ta->siblings;
- }
- }
-
- /* Find the previous "sibling" of this article, including cousins that are
- ** the same distance from the root as we are.
- */
- PACKED_ARTICLE *
- find_prev_sib()
- {
- PACKED_ARTICLE *ta, *tb;
- int i, ascent;
-
- ascent = 0;
- ta = p_art;
- for (;;) {
- tb = ta;
- if (ta->parent) {
- ta += ta->parent + 1;
- } else {
- ta = p_articles + p_roots[ta->root].articles;
- }
- if (tb = last_sib(ta, ascent, tb)) {
- return tb;
- }
- if (!ta->parent) {
- break;
- }
- ta += ta->parent;
- ascent++;
- }
- return Nullart;
- }
-
- /* A recursive routine to find the last node at the proper depth. This
- ** article is at depth 0.
- */
- static PACKED_ARTICLE *
- last_sib(ta, depth, limit)
- PACKED_ARTICLE *ta;
- int depth;
- PACKED_ARTICLE *limit;
- {
- PACKED_ARTICLE *tb, *tc;
-
- if (ta == limit) {
- return Nullart;
- }
- if (ta->siblings) {
- tc = ta+ta->siblings;
- if (tc != limit && (tb = last_sib(tc,depth,limit))) {
- return tb;
- }
- }
- if (!depth) {
- return ta;
- }
- if (ta->child_cnt) {
- return last_sib(ta+1, depth-1, limit);
- }
- return Nullart;
- }
-
- #endif /* USETHREADS */
-