home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 35 Internet
/
35-Internet.zip
/
trn_12.zip
/
src
/
rt-rn.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-12-04
|
25KB
|
1,124 lines
/* $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 */