v23i071: TRN, version of RN that follows conversation threads, Part12/14

Rich Salz rsalz at bbn.com
Tue Dec 4 07:28:16 AEST 1990


Submitted-by: Wayne Davison <davison at dri.com>
Posting-number: Volume 23, Issue 71
Archive-name: trn/part12

---- Cut Here and unpack ----
#!/bin/sh
# this is part 12 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file rn.c continued
#
CurArch=12
if test ! -r s2_seq_.tmp
then echo "Please unpack part 1 first!"
     exit 1; fi
( read Scheck
  if test "$Scheck" != $CurArch
  then echo "Please unpack part $Scheck next!"
       exit 1;
  else exit 0; fi
) < s2_seq_.tmp || exit 1
echo "x - Continuing file rn.c"
sed 's/^X//' << 'SHAR_EOF' >> rn.c
X#ifdef VERBOSE
X			    IF(verbose)
X				printf("\nRestriction %s%s still in effect.\n",
X				    ngtodo[0],
X				    maxngtodo > 1 ? ", etc." : nullstr) FLUSH;
X			    ELSE
X#endif
X#ifdef TERSE
X				fputs("\n(\"Only\" mode.)\n",stdout) FLUSH;
X#endif
X			else {
X#ifdef VERBOSE
X			    IF(verbose)
X				fputs("\nNo articles under restriction.",
X				  stdout) FLUSH;
X			    ELSE
X#endif
X#ifdef TERSE
X				fputs("\nNo \"only\" articles.",stdout) FLUSH;
X#endif
X			    end_only();	/* release the restriction */
X			    retry = TRUE;
X			}
X		    }
X#endif
X		    dfltcmd = (retry ? "npq" : "qnp");
X#ifdef VERBOSE
X		    IF(verbose)
X			sprintf(promptbuf,
X			    "\n******** End of newsgroups--what next? [%s] ",
X			    dfltcmd);
X		    ELSE
X#endif
X#ifdef TERSE
X			sprintf(promptbuf,
X			    "\n**** End--next? [%s] ", dfltcmd);
X#endif
X		}
X		else {
X		    bool shoe_fits;	/* newsgroup matches restriction? */
X
X		    if (toread[ng] >= TR_NONE) {	/* recalc toread? */
X			set_ngname(rcline[ng]);
X			if (shoe_fits = (special || inlist(ngname)))
X			    set_toread(ng);
X			if (paranoid) {
X			    recent_ng = current_ng;
X			    current_ng = ng;
X			    cleanup_rc();
X					/* this may move newsgroups around */
X			    ng = current_ng;
X			    set_ngname(rcline[ng]);
X			}
X		    }
X		    if (toread[ng] < (maxngtodo||special ? TR_NONE : TR_ONE) || !shoe_fits) {
X					/* unwanted newsgroup? */
X			ng++;		/* then skip it */
X			continue;
X		    }
X    reprompt_newsgroup:
X#ifdef USETHREADS
X		    dfltcmd = (use_threads && select_on
X			&& (ART_NUM)toread[ng] >= select_on ?
X			ThreadedGroup ? "+ynq" : "=ynq" : "ynq");
X#else
X		    dfltcmd = "ynq";
X#endif
X#ifdef VERBOSE
X		    IF(verbose)
X			sprintf(promptbuf,
X			    "\n******** %3ld unread article%c in %s--read now? [%s] ",
X			    (long)toread[ng], (toread[ng]==TR_ONE ? ' ' : 's'),
X			    ngname, dfltcmd);	/* format prompt string */
X		    ELSE
X#endif
X#ifdef TERSE
X			sprintf(promptbuf,
X			    "\n**** %3ld in %s--read? [%s] ",
X			    (long)toread[ng],
X			    ngname,dfltcmd);	/* format prompt string */
X#endif
X		}
X		special = FALSE;	/* go back to normal mode */
X		if (ng != current_ng) {
X		    recent_ng = current_ng;
X					/* remember previous newsgroup */
X		    current_ng = ng;	/* remember current newsgroup */
X		}
X    reask_newsgroup:
X		unflush_output();	/* disable any ^O in effect */
X		fputs(promptbuf,stdout) FLUSH;/* print prompt */
X		fflush(stdout);
X    reinp_newsgroup:
X		eat_typeahead();
X		getcmd(buf);
X		if (errno || *buf == '\f') {
X		    putchar('\n') FLUSH; /* if return from stop signal */
X		    goto reask_newsgroup;	/* give them a prompt again */
X		}
X		setdef(buf,dfltcmd);
X#ifdef VERIFY
X		printcmd();
X#endif
X		switch (*buf) {
X		case 'p':		/* find previous unread newsgroup */
X		    do {
X			if (ng <= 0)
X			    break;
X			ng--;
X			if (toread[ng] == TR_NONE)
X			    set_toread(ng);
X		    } while (toread[ng] <= TR_NONE);
X		    break;
X		case 'P':		/* goto previous newsgroup */
X		    do {
X			if (ng <= 0)
X			    break;
X			ng--;
X		    } while (toread[ng] < TR_NONE);
X		    special = TRUE;	/* don't skip it if toread==0 */
X		    break;
X		case '-':
X		    ng = recent_ng;	/* recall previous newsgroup */
X		    special = TRUE;	/* don't skip it if toread==0 */
X		    break;
X		case 'q': case 'Q': case 'x':	/* quit? */
X		    oh_for_the_good_old_days = (*buf == 'x');
X		    putchar('\n') FLUSH;
X		    ng = nextrcline+1;	/* satisfy */
X		    retry = FALSE;	/*   loop conditions */
X		    break;
X		case '^':
X		    putchar('\n') FLUSH;
X		    ng = 0;
X		    break;
X		case 'n':		/* find next unread newsgroup */
X		    if (ng == nextrcline) {
X			putchar('\n') FLUSH;
X			retry = TRUE;
X		    }
X		    else if (toread[ng] > TR_NONE)
X			retry = TRUE;
X		    ng++;
X		    break;
X		case 'N':		/* goto next newsgroup */
X		    ng++;
X		    special = TRUE;	/* and don't skip it if toread==0 */
X		    break;
X		case '1':		/* goto 1st newsgroup */
X		    ng = 0;
X		    special = TRUE;	/* and don't skip it if toread==0 */
X		    break;
X		case '$':
X		    ng = nextrcline;	/* goto last newsgroup */
X		    retry = TRUE;
X		    break;
X		case 'L':
X		    list_newsgroups();
X		    goto reask_newsgroup;
X		case '/': case '?':	/* scan for newsgroup pattern */
X#ifdef NGSEARCH
X		    switch (ng_search(buf,TRUE)) {
X		    case NGS_ABORT:
X			goto reinp_newsgroup;
X		    case NGS_INTR:
X#ifdef VERBOSE
X			IF(verbose)
X			    fputs("\n(Interrupted)\n",stdout) FLUSH;
X			ELSE
X#endif
X#ifdef TERSE
X			    fputs("\n(Intr)\n",stdout) FLUSH;
X#endif
X			ng = current_ng;
X			goto reask_newsgroup;
X		    case NGS_FOUND:
X			special = TRUE;	/* don't skip it if toread==0 */
X			break;
X		    case NGS_NOTFOUND:
X#ifdef VERBOSE
X			IF(verbose)
X			    fputs("\n\nNot found--use g to add newsgroups\n",
X				stdout) FLUSH;
X			ELSE
X#endif
X#ifdef TERSE
X			    fputs("\n\nNot found\n",stdout) FLUSH;
X#endif
X			goto reask_newsgroup;
X		    }
X#else
X		    notincl("/");
X#endif
X		    break;
X		case 'm':
X#ifndef RELOCATE
X		    notincl("m");
X		    break;
X#endif		    
X		case 'g':	/* goto named newsgroup */
X		    if (!finish_command(FALSE))
X					/* if they didn't finish command */
X			goto reinp_newsgroup;	/* go try something else */
X		    for (s = buf+1; *s == ' '; s++);
X					/* skip leading spaces */
X		    if (!*s)
X			strcpy(s,ngname);
X#ifdef RELOCATE
X		    if (!get_ng(s,*buf=='m'))	/* try to find newsgroup */
X#else
X		    if (!get_ng(s,FALSE))	/* try to find newsgroup */
X#endif
X			ng = current_ng;/* if not found, go nowhere */
X		    special = TRUE;	/* don't skip it if toread==0 */
X		    break;
X#ifdef DEBUGGING
X		case 'D':
X		    printf("\nTries: %d Hits: %d\n",
X			softtries,softtries-softmisses) FLUSH;
X		    goto reask_newsgroup;
X#endif
X		case '!':		/* shell escape */
X		    if (escapade())	 /* do command */
X			goto reinp_newsgroup;
X					/* if rubbed out, re input */
X		    goto reask_newsgroup;
X		case Ctl('k'):		/* edit global KILL file */
X		    edit_kfile();
X		    goto reask_newsgroup;
X		case 'c':		/* catch up */
X#ifdef CATCHUP
Xreask_catchup:
X#ifdef VERBOSE
X		IF(verbose)
X		    in_char("\nDo you really want to mark everything as read? [yn] ", 'C');
X		ELSE
X#endif
X#ifdef TERSE
X		    in_char("\nReally? [ynh] ", 'C');
X#endif
X		    putchar('\n') FLUSH;
X		    setdef(buf,"y");
X		    if (*buf == 'h') {
X#ifdef VERBOSE
X		    printf("Type y or SP to mark all articles as read.\n");
X		    printf("Type n to leave articles marked as they are.\n");
X#else
X		    printf("y or SP to mark all read.\n");
X		    printf("n to forget it.\n");
X#endif
X			goto reask_catchup;
X		    }
X		    else if (*buf!=' ' && *buf!='y' && *buf!='n' && *buf!='q') {
X			printf(hforhelp);
X			settle_down();
X			goto reask_catchup;
X		    } else if ( (*buf == ' ' || *buf == 'y') && ng<nextrcline )
X			catch_up(ng);
X		    else
X			retry = TRUE;
X		    ng++;
X#else
X		    notincl("c");
X#endif
X		    break;
X		case 'u':		/* unsubscribe */
X		    if (ng < nextrcline && toread[ng] >= TR_NONE) {
X					/* unsubscribable? */
X			printf(unsubto,rcline[ng]) FLUSH;
X			rcchar[ng] = NEGCHAR;
X					/* unsubscribe to (from?) it */
X			toread[ng] = TR_UNSUB;
X					/* and make line invisible */
X			ng++;		/* do an automatic 'n' */
X		    }
X		    break;
X		case 'h': {		/* help */
X		    int cmd;
X
X		    if ((cmd = help_ng()) > 0)
X			pushchar(cmd);
X		    goto reask_newsgroup;
X		}
X		case 'a':
X#ifndef FINDNEWNG
X		    notincl("a");
X		    goto reask_newsgroup;
X#else
X		    /* FALL THROUGH */
X#endif
X		case 'o':
X#ifdef ONLY
X		{
X#ifdef FINDNEWNG
X		    bool doscan = (*buf == 'a');
X#endif
X
X		    if (!finish_command(TRUE)) /* get rest of command */
X			goto reinp_newsgroup;	/* if rubbed out, try something else */
X		    end_only();
X		    if (buf[1]) {
X			bool minusd = instr(buf+1,"-d") != Nullch;
X
X			sw_list(buf+1);
X			if (minusd)
X			    cwd_check();
X			putchar('\n') FLUSH;
X#ifdef FINDNEWNG
X			if (doscan && maxngtodo)
X			    scanactive();
X#endif
X		    }
X		    ng = 0;		/* simulate ^ */
X		    retry = FALSE;
X		    break;
X		}
X#else
X		    notincl("o");
X		    goto reask_newsgroup;
X#endif
X		case '&':
X		    if (switcheroo()) /* get rest of command */
X			goto reinp_newsgroup;	/* if rubbed out, try something else */
X		    goto reask_newsgroup;
X		case 'l': {		/* list other newsgroups */
X		    if (!finish_command(TRUE)) /* get rest of command */
X			goto reinp_newsgroup;	/* if rubbed out, try something else */
X		    for (s = buf+1; *s == ' '; s++);
X		    			/* skip leading spaces */
X		    sprintf(cmd_buf,"%s '%s'",filexp(NEWSGROUPS),s);
X		    resetty();
X		    if (doshell(sh,cmd_buf))
X#ifdef VERBOSE
X			IF(verbose)
X			    fputs("    (Error from newsgroups program)\n",
X				stdout) FLUSH;
X			ELSE
X#endif
X#ifdef TERSE
X			    fputs("(Error)\n",stdout) FLUSH;
X#endif
X		    noecho();
X		    crmode();
X		    goto reask_newsgroup;
X		}
X#ifdef USETHREADS
X		case 'U': case '+':
X#endif
X		case '.': case '=':
X		case 'y': case 'Y': /* do normal thing */
X		    if (ng >= nextrcline) {
X			fputs("\nNot on a newsgroup.",stdout) FLUSH;
X			goto reask_newsgroup;
X		    }
X#ifdef USETHREADS
X		    else if (*buf == '+' || *buf == 'U' || *buf == '=') {
X			buf[1] = '\0';
X			s = savestr(buf);
X		    }
X#else
X		    if (*buf == '=')
X			s = savestr("=");
X#endif
X		    else if (*buf == '.') {	/* start command? */
X			if (!finish_command(FALSE)) /* get rest of command */
X			    goto reinp_newsgroup;
X			s = savestr(buf+1);
X					/* do_newsgroup will free it */
X		    }
X		    else
X			s = Nullch;
X		    if (toread[ng])
X			retry = TRUE;
X		    switch (do_newsgroup(s)) {
X		    case NG_ERROR:
X		    case NG_NORM:
X			ng++;
X			break;
X		    case NG_ASK:
X			goto reprompt_newsgroup;
X		    case NG_MINUS:
X			ng = recent_ng;	/* recall previous newsgroup */
X			special = TRUE;	/* don't skip it if toread==0 */
X			break;
X		    }
X		    break;
X#ifdef STRICTCR
X		case '\n':
X		    fputs(badcr,stdout) FLUSH;
X		    goto reask_newsgroup;
X#endif
X		case 'v':
X		    printf("\n%s",rnid);
X		    printf("\n%s",patchlevel);
X		    printf("\nSend bugs to davison at drivax.UUCP (davison%%drivax at uunet.uu.net)\n") FLUSH;
X		    goto reask_newsgroup;
X		default:
X		    printf("\n%s",hforhelp) FLUSH;
X		    settle_down();
X		    goto reask_newsgroup;
X		}
X	    }
X	} while (retry);
X    }
X
X    /* now write .newsrc back out */
X
X    write_rc();
X
X    if (oh_for_the_good_old_days)
X	get_old_rc();
X
X    finalize(0);			/* and exit */
X}
X
X/* set current newsgroup */
X
Xvoid
Xset_ngname(what)
Xchar *what;
X{
X    int len = strlen(what)+1;
X
X    growstr(&ngname,&ngnlen,len);
X    strcpy(ngname,what);
X    growstr(&ngdir,&ngdlen,len);
X    strcpy(ngdir,getngdir(ngname));
X}
X
Xstatic char *myngdir;
Xstatic int ngdirlen = 0;
X
Xchar *
Xgetngdir(ngnam)
Xchar *ngnam;
X{
X    register char *s;
X
X    growstr(&myngdir,&ngdirlen,strlen(ngnam)+1);
X    strcpy(myngdir,ngnam);
X    for (s = myngdir; *s; s++)
X	if (*s == '.')
X	    *s = '/';
X    return myngdir;
X}
X
SHAR_EOF
echo "File rn.c is complete"
chmod 0660 rn.c || echo "restore of rn.c fails"
echo "x - extracting rn.h (Text)"
sed 's/^X//' << 'SHAR_EOF' > rn.h &&
X/* $Header: rn.h,v 4.3 85/05/01 11:48:19 lwall Exp $
X *
X * $Log:	rn.h,v $
X * Revision 4.3  85/05/01  11:48:19  lwall
X * Baseline for release with 4.3bsd.
X * 
X */
X
XEXT char *ngname INIT(Nullch);		/* name of current newsgroup */
XEXT int ngnlen INIT(0);			/* current malloced size of ngname */
XEXT char *ngdir INIT(Nullch);		/* same thing in directory name form */
XEXT int ngdlen INIT(0);			/* current malloced size of ngdir */
X
XEXT NG_NUM ng INIT(0);		/* current newsgroup index into rcline and toread */
XEXT NG_NUM current_ng INIT(0);	/* stable current newsgroup so we can ditz with ng */
XEXT NG_NUM starthere INIT(0);   /* set to the first newsgroup with unread news on startup */
XEXT char *spool INIT(Nullch);		/* public news spool directory */
X
Xvoid	rn_init();
Xvoid	main();
Xvoid	set_ngname();
Xchar	*getngdir();
SHAR_EOF
chmod 0660 rn.h || echo "restore of rn.h fails"
echo "x - extracting rt-rn.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > rt-rn.c &&
X/* $Header: rt-rn.c,v 4.3.3.1 90/07/28 18:07:55 davison Trn $
X**
X** $Log:	rt-rn.c,v $
X** Revision 4.3.3.1  90/07/28  18:07:55  davison
X** Initial Trn Release
X** 
X*/
X
X#include "EXTERN.h"
X#include "common.h"
X#include "term.h"
X#include "final.h"
X#include "util.h"
X#include "bits.h"
X#include "artio.h"
X#include "ng.h"
X#include "ngdata.h"
X#include "search.h"
X#include "artstate.h"
X#include "backpage.h"
X#include "rthreads.h"
X
X#ifdef USETHREADS
X
Xstatic void find_depth(), cache_tree(), display_tree();
Xstatic char letter();
X
X/* Find the article structure information based on article number.
X*/
Xvoid
Xfind_article( artnum )
XART_NUM artnum;
X{
X    register PACKED_ARTICLE *article;
X    register int i;
X
X    if( !p_articles ) {
X	p_art = Nullart;
X	return;
X    }
X
X    if( !p_art ) {
X	p_art = p_articles;
X    }
X    /* Start looking for the article num from our last known spot in the array.
X    ** That way, if we already know where we are, we run into ourselves right
X    ** away.
X    */
X    for( article=p_art, i=p_art-p_articles; i < total.article; article++,i++ ) {
X	if( article->num == artnum ) {
X	    p_art = article;
X	    return;
X	}
X    }
X    /* Didn't find it, so search the ones before our current position.
X    */
X    for( article = p_articles; article != p_art; article++ ) {
X	if( article->num == artnum ) {
X	    p_art = article;
X	    return;
X	}
X    }
X    p_art = Nullart;
X}
X
Xstatic char tree_indent[] = {
X    ' ', 0,
X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0
X};
X
Xchar letters[] = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz?";
X
Xstatic PACKED_ARTICLE *tree_article;
X
Xstatic int max_depth, max_line = -1;
Xstatic int first_depth, first_line;
Xstatic int my_depth, my_line;
Xstatic bool node_on_line;
Xstatic int node_line_cnt;
X
Xstatic int line_num;
Xstatic int header_indent;
X
Xstatic char *tree_lines[11];
Xstatic char tree_buff[128], *str;
X
X/* Prepare tree display for inclusion in the article header.
X*/
Xvoid
Xinit_tree()
X{
X    register PACKED_ARTICLE *article;
X
X#if 000			/* don't do this, since read-status may change */
X    if( curr_p_art == tree_article ) {
X	return;
X    }
X#endif
X    while( max_line >= 0 ) {		/* free any previous tree data */
X	free( tree_lines[max_line--] );
X    }
X    tree_article = curr_p_art;
X
X    if( !curr_p_art ) {
X	return;
X    }
X    article = p_articles + p_roots[curr_p_art->root].articles;
X
X    max_depth = max_line = my_depth = my_line = node_line_cnt = 0;
X    find_depth( article, 0 );
X
X    if( max_depth <= 5 ) {
X	first_depth = 0;
X    } else {
X	if( my_depth+2 > max_depth ) {
X	    first_depth = max_depth - 5;
X	} else if( (first_depth = my_depth - 3) < 0 ) {
X	    first_depth = 0;
X	}
X	max_depth = first_depth + 5;
X    }
X    if( --max_line < max_tree_lines ) {
X	first_line = 0;
X    } else {
X	if( my_line + max_tree_lines/2 > max_line ) {
X	    first_line = max_line - (max_tree_lines-1);
X	} else if( (first_line = my_line - (max_tree_lines-1)/2) < 0 ) {
X	    first_line = 0;
X	}
X	max_line = first_line + max_tree_lines-1;
X    }
X
X    str = tree_buff;		/* initialize first line's data */
X    *str++ = ' ';
X    node_on_line = FALSE;
X    line_num = 0;
X    /* cache our portion of the tree */
X    cache_tree( article, 0, tree_indent );
X
X    max_depth = (max_depth-first_depth) * 5;	/* turn depth into char width */
X    max_line -= first_line;			/* turn max_line into count */
X    /* shorten tree if lower lines aren't visible */
X    if( node_line_cnt < max_line ) {
X	max_line = node_line_cnt + 1;
X    }
X}
X
X/* A recursive routine to find the maximum tree extents and where we are.
X*/
Xstatic void
Xfind_depth( article, depth )
XPACKED_ARTICLE *article;
X{
X    if( depth > max_depth ) {
X	max_depth = depth;
X    }
X    for( ;; ) {
X	if( article == tree_article ) {
X	    my_depth = depth;
X	    my_line = max_line;
X	}
X	if( article->child_cnt ) {
X	    find_depth( article+1, depth+1 );
X	} else {
X	    max_line++;
X	}
X	if( !article->siblings ) {
X	    break;
X	}
X	article += article->siblings;
X    }
X}
X
X/* Place the tree display in a maximum of 11 lines x 6 nodes.
X*/
Xstatic void
Xcache_tree( article, depth, cp )
XPACKED_ARTICLE *article;
Xint depth;
Xchar *cp;
X{
X    int depth_mode;
X
X    cp[1] = ' ';
X    if( depth >= first_depth && depth <= max_depth ) {
X	cp += 5;
X	depth_mode = 1;
X    } else if( depth+1 == first_depth ) {
X	depth_mode = 2;
X    } else {
X	cp = tree_indent;
X	depth_mode = 0;
X    }
X    for( ;; ) {
X	switch( depth_mode ) {
X	case 1: {
X	    char ch;
X
X	    *str++ = (article->flags & ROOT_ARTICLE)? ' ' : '-';
X	    if( article == tree_article ) {
X		*str++ = '*';
X	    }
X	    if( was_read( article->num ) ) {
X		*str++ = '(';
X		ch = ')';
X	    } else {
X		*str++ = '[';
X		ch = ']';
X	    }
X	    if( article == recent_p_art && article != tree_article ) {
X		*str++ = '@';
X	    }
X	    *str++ = letter( article );
X	    *str++ = ch;
X	    if( article->child_cnt ) {
X		*str++ = (article->child_cnt == 1)? '-' : '+';
X	    }
X	    if( article->siblings ) {
X		*cp = '|';
X	    } else {
X		*cp = ' ';
X	    }
X	    node_on_line = TRUE;
X	    break;
X	}
X	case 2:
X	    *tree_buff = (!article->child_cnt)? ' ' :
X		(article->child_cnt == 1)? '-' : '+';
X	    break;
X	default:
X	    break;
X	}
X	if( article->child_cnt ) {
X	    cache_tree( article+1, depth+1, cp );
X	    cp[1] = '\0';
X	} else {
X	    if( !node_on_line && first_line == line_num ) {
X		first_line++;
X	    }
X	    if( line_num >= first_line ) {
X		if( str[-1] == ' ' ) {
X		    str--;
X		}
X		*str = '\0';
X		tree_lines[line_num-first_line]
X			= safemalloc( str-tree_buff + 1 );
X		strcpy( tree_lines[line_num - first_line], tree_buff );
X		if( node_on_line ) {
X		    node_line_cnt = line_num - first_line;
X		}
X	    }
X	    line_num++;
X	    node_on_line = FALSE;
X	}
X	if( !article->siblings || line_num > max_line ) {
X	    break;
X	}
X	article += article->siblings;
X	if( !article->siblings ) {
X	    *cp = '\\';
X	}
X	if( !first_depth ) {
X	    tree_indent[5] = ' ';
X	}
X	strcpy( tree_buff, tree_indent+5 );
X	str = tree_buff + strlen( tree_buff );
X    }
X}
X
X/* Output a header line with possible tree display on the right hand side.
X** Does automatic wrapping of lines that are too long.
X*/
Xint
Xtree_puts( orig_line, header_line, use_underline )
Xchar *orig_line;
XART_LINE header_line;
Xint use_underline;
X{
X    char *buf;
X    register char *line, *cp, *end;
X    int pad_cnt, wrap_at;
X    ART_LINE start_line = header_line;
X    int i;
X    char ch;
X
X    /* Make a modifiable copy of the line */
X    buf = safemalloc( strlen( orig_line ) + 2 );  /* yes, I mean "2" */
X    strcpy( buf, orig_line );
X    line = buf;
X
X    /* Change any embedded control characters to spaces */
X    for( end = line; *end && *end != '\n'; end++ ) {
X	if( (unsigned char)*end < ' ' ) {
X	    *end = ' ';
X	}
X    }
X    *end = '\0';
X
X    if( !*line ) {
X	strcpy( line, " " );
X	end = line+1;
X    }
X
X    /* If this is the first subject line, output it with a preceeding [1] */
X    if( use_underline && curr_p_art && (unsigned char)*line > ' ' ) {
X#ifdef NOFIREWORKS
X	no_sofire();
X#endif
X	standout();
X	putchar( '[' );
X	putchar( letter( curr_p_art ) );
X	putchar( ']' );
X	un_standout();
X	putchar( ' ' );
X	header_indent = 4;
X	line += 9;
X	i = 0;
X    } else {
X	if( *line != ' ' ) {
X	    /* A "normal" header line -- output keyword and set header_indent
X	    ** _except_ for the first line, which is a non-standard header.
X	    */
X	    if( !header_line || !(cp = index( line, ':' )) || *++cp != ' ' ) {
X		header_indent = 0;
X	    } else {
X		*cp = '\0';
X		fputs( line, stdout );
X		putchar( ' ' );
X		header_indent = ++cp - line;
X		line = cp;
X	    }
X	    i = 0;
X	} else {
X	    /* Skip whitespace of continuation lines and prepare to indent */
X	    while( *++line == ' ' ) {
X		;
X	    }
X	    i = header_indent;
X	}
X    }
X    for( ; *line; i = header_indent ) {
X#ifdef CLEAREOL
X	maybe_eol();
X#endif
X	if( i ) {
X	    putchar( '+' );
X	    while( --i ) {
X		putchar( ' ' );
X	    }
X	}
X	/* If no (more) tree lines, wrap at COLS-1 */
X	if( max_line < 0 || header_line > max_line+1 ) {
X	    wrap_at = COLS-1;
X	} else {
X	    wrap_at = COLS - max_depth - 5 - 3;
X	}
X	/* Figure padding between header and tree output, wrapping long lines */
X	pad_cnt = wrap_at - (end - line + header_indent);
X	if( pad_cnt <= 0 ) {
X	    cp = line + wrap_at - header_indent - 1;
X	    pad_cnt = 1;
X	    while( cp > line && *cp != ' ' ) {
X		if( *--cp == ',' || *cp == '.' || *cp == '-' || *cp == '!' ) {
X		    cp++;
X		    break;
X		}
X		pad_cnt++;
X	    }
X	    if( cp == line ) {
X		cp += wrap_at - header_indent;
X		pad_cnt = 0;
X	    }
X	    ch = *cp;
X	    *cp = '\0';
X	    /* keep rn's backpager happy */
X	    vwtary( artline, vrdary( artline - 1 ) );
X	    artline++;
X	} else {
X	    cp = end;
X	    ch = '\0';
X	}
X	if( use_underline ) {
X	    underprint( line );
X	} else {
X	    fputs( line, stdout );
X	}
X	*cp = ch;
X	/* Skip whitespace in wrapped line */
X	while( *cp == ' ' ) {
X	    cp++;
X	}
X	line = cp;
X	/* Check if we've got any tree lines to output */
X	if( wrap_at != COLS-1 && header_line <= max_line ) {
X	    char *cp1, *cp2;
X
X	    do {
X		putchar( ' ' );
X	    } while( pad_cnt-- );
X	    /* Check string for the '*' flagging our current node
X	    ** and the '@' flagging our prior node.
X	    */
X	    cp = tree_lines[header_line];
X	    cp1 = index( cp, '*' );
X	    cp2 = index( cp, '@' );
X	    if( cp1 != Nullch ) {
X		*cp1 = '\0';
X	    }
X	    if( cp2 != Nullch ) {
X		*cp2 = '\0';
X	    }
X	    fputs( cp, stdout );
X	    /* Handle standout output for '*' and '@' marked nodes, then
X	    ** continue with the rest of the line.
X	    */
X	    while( cp1 || cp2 ) {
X		standout();
X		if( cp1 && (!cp2 || cp1 < cp2) ) {
X		    cp = cp1;
X		    cp1 = Nullch;
X		    *cp++ = '*';
X		    putchar( *cp++ );
X		    putchar( *cp++ );
X		} else {
X		    cp = cp2;
X		    cp2 = Nullch;
X		    *cp++ = '@';
X		}
X		putchar( *cp++ );
X		un_standout();
X		if( *cp ) {
X		    fputs( cp, stdout );
X		}
X	    }/* while */
X	}/* if */
X	putchar( '\n' ) FLUSH;
X	header_line++;
X    }/* for remainder of line */
X
X    /* free allocated copy of line */
X    free( buf );
X
X    /* return number of lines displayed */
X    return header_line - start_line;
X}
X
X/* Output any parts of the tree that are left to display.  Called at the
X** end of each header.
X*/
Xint
Xfinish_tree( last_line )
XART_LINE last_line;
X{
X    ART_LINE start_line = last_line;
X
X    while( last_line <= max_line ) {
X	artline++;
X	last_line += tree_puts( "+", last_line, 0 );
X	vwtary( artline, artpos );	/* keep rn's backpager happy */
X    }
X    return last_line - start_line;
X}
X
X/* Output the entire article tree for the user.
X*/
Xvoid
Xentire_tree()
X{
X    int j, root;
X
X    if( check_page_line() ) {
X	return;
X    }
X    if( !p_art ) {
X#ifdef VERBOSE
X	IF( verbose )
X	    fputs( "\nNo article tree to display.\n", stdout );
X	ELSE
X#endif
X#ifdef TERSE
X	    fputs( "\nNo tree.\n", stdout );
X#endif
X    } else {
X	root = p_art->root;
X#ifdef NOFIREWORKS
X	no_sofire();
X#endif
X	standout();
X	printf( "T%ld:\n", (long)p_roots[root].root_num );
X	un_standout();
X	if( check_page_line() ) {
X	    return;
X	}
X	putchar( '\n' );
X	for( j = 0; j < p_roots[root].subject_cnt; j++ ) {
X	    sprintf( buf, "[%c] %s\n", letters[j > 9+26+26 ? 9+26+26 : j],
X		subject_ptrs[root_subjects[root]+j] );
X	    if( check_page_line() ) {
X		return;
X	    }
X	    fputs( buf, stdout );
X	}
X	if( check_page_line() ) {
X	    return;
X	}
X	putchar( '\n' );
X	if( check_page_line() ) {
X	    return;
X	}
X	putchar( ' ' );
X	buf[3] = '\0';
X	display_tree( p_articles+p_roots[p_art->root].articles, tree_indent );
X
X	if( check_page_line() ) {
X	    return;
X	}
X	putchar( '\n' );
X    }
X}
X
X/* A recursive routine to output the entire article tree.
X*/
Xstatic void
Xdisplay_tree( article, cp )
XPACKED_ARTICLE *article;
Xchar *cp;
X{
X    if( cp - tree_indent > COLS || page_line < 0 ) {
X	return;
X    }
X    cp[1] = ' ';
X    cp += 5;
X    for( ;; ) {
X	putchar( (article->flags & ROOT_ARTICLE)? ' ' : '-' );
X	if( was_read( article->num ) ) {
X	    buf[0] = '(';
X	    buf[2] = ')';
X	} else {
X	    buf[0] = '[';
X	    buf[2] = ']';
X	}
X	buf[1] = letter( article );
X	if( article == curr_p_art ) {
X	    standout();
X	    fputs( buf, stdout );
X	    un_standout();
X	} else if( article == recent_p_art ) {
X	    putchar( buf[0] );
X	    standout();
X	    putchar( buf[1] );
X	    un_standout();
X	    putchar( buf[2] );
X	} else {
X	    fputs( buf, stdout );
X	}
X
X	if( article->siblings ) {
X	    *cp = '|';
X	} else {
X	    *cp = ' ';
X	}
X	if( article->child_cnt ) {
X	    putchar( (article->child_cnt == 1)? '-' : '+' );
X	    display_tree( article+1, cp );
X	    cp[1] = '\0';
X	} else {
X	    putchar( '\n' ) FLUSH;
X	}
X	if( !article->siblings ) {
X	    break;
X	}
X	article += article->siblings;
X	if( !article->siblings ) {
X	    *cp = '\\';
X	}
X	tree_indent[5] = ' ';
X	if( check_page_line() ) {
X	    return;
X	}
X	fputs( tree_indent+5, stdout );
X    }
X}
X
Xint
Xcheck_page_line()
X{
X    if( page_line < 0 ) {
X	return -1;
X    }
X    if( page_line >= LINES || int_count ) {
X      register int cmd = -1;
X	if( int_count || (cmd = get_anything()) ) {
X	    page_line = -1;		/* disable further printing */
X	    if( cmd > 0 ) {
X		pushchar( cmd );
X	    }
X	    return cmd;
X	}
X    }
X    page_line++;
X    return 0;
X}
X
X/* Calculate the subject letter representation.  "Place-holder" nodes
X** are marked with a ' ', others get a letter in the sequence:
X**	' ', '1'-'9', 'A'-'Z', 'a'-'z', '?'
X*/
Xstatic char
Xletter( article )
XPACKED_ARTICLE *article;
X{
X    register int subj = article->subject;
X
X    if( subj < 0 ) {
X	return ' ';
X    }
X    subj -= root_subjects[article->root];
X    if( subj < 9+26+26 ) {
X	return letters[subj];
X    }
X    return '?';
X}
X
X/* Find the first unread article in the (possibly selected) root order.
X*/
Xvoid
Xfirst_art()
X{
X    register int r;
X
X    if( !ThreadedGroup ) {
X	art = firstart;
X	return;
X    }
X    p_art = Nullart;
X    art = lastart+1;
X    follow_thread( 'n' );
X}
X
X/* Perform a command over all or a section of the article tree.  Most of
X** the option letters match commands entered from article mode:
X**   n - find the next unread article after current article.
X**  ^N - find the next unread article with the same subject.
X**   N - goto the next article in the thread.
X**   J - junk the entire thread.
X**   k - junk all articles with this same subject.
X**   K - kill all this article's descendants (we know that the caller
X**	 killed the current article on the way here).
X**   u - mark entire thread as "unread".
X**   U - mark this article and its descendants as "unread".
X**   f - follow the thread (like 'n'), but don't attempt to find a new thread
X**	 if we run off the end.
X*/
Xvoid
Xfollow_thread( cmd )
Xchar cmd;
X{
X    int curr_subj = -1, sel;
X    PACKED_ARTICLE *root_limit, *p_art_old = Nullart;
X    bool subthread_flag;
X
X    reread = FALSE;
X
X    if( !p_art ) {
X	if( ThreadedGroup && art > lastart ) {
X	    p_art = root_limit = p_articles;
X	    goto follow_root;
X	}
X	art++;
X	return;
X    }
X    if( cmd == 'k' || cmd == Ctl('n') ) {
X	if( (curr_subj = p_art->subject) == -1) {
X	    return;
X	}
X	p_art_old = p_art;
X    }
X    sel = (selected_roots[p_art->root] & 1);
X    if( cmd == 'U' || cmd == 'K' ) {
X	subthread_flag = TRUE;
X	p_art_old = p_art;
X    } else {
X	subthread_flag = FALSE;
X    }
X    /* The current article is already marked as read for 'K' */
X    if( cmd == 'k' || cmd == 'J' || cmd == 'u' ) {
X	p_art = p_articles + p_roots[p_art->root].articles;
X	art = p_art->num;
X	if( cmd == 'u' ) {
X	    p_art_old = p_art;
X	    cmd = 'U';
X	} else {
X	    if( !was_read( art )
X	     && (curr_subj < 0 || curr_subj == p_art->subject) ) {
X		set_read( art, sel );
X	    }
X	    cmd = 'K';
X	}
X    }
X    if( cmd == 'U' ) {
X	if( p_art->subject != -1 ) {
X	    set_unread( art, sel );
X	}
X	root_article_cnts[p_art->root] = 1;
X	scan_all_roots = FALSE;
X    }
X  follow_again:
X    sel = (selected_roots[p_art->root] & 1);
X    root_limit = upper_limit( p_art, subthread_flag );
X    for( ;; ) {
X	if( ++p_art == root_limit ) {
X	    break;
X	}
X	if( !(art = p_art->num) ) {
X	    continue;
X	}
X	if( cmd == 'K' || p_art->subject == -1 ) {
X	    if( !was_read( art )
X	     && (curr_subj < 0 || curr_subj == p_art->subject) ) {
X		set_read( art, sel );
X	    }
X	} else if( cmd == 'U' ) {
X	    set_unread( art, sel );
X	} else if( !was_read( art )
X		&& (curr_subj < 0 || curr_subj == p_art->subject) ) {
X	    return;
X	} else if( cmd == 'N' ) {
X	    reread = TRUE;
X	    return;
X	}
X    }/* for */
X    if( p_art_old ) {
X	p_art = p_art_old;
X	if( cmd == 'U' && p_art->subject != -1 ) {
X	    art = p_art->num;
X	    return;
X	}
X	p_art_old = Nullart;
X	cmd = 'n';
X	curr_subj = -1;
X	subthread_flag = FALSE;
X	goto follow_again;
X    }
X    if( cmd == 'f' ) {
X	p_art = Nullart;
X	art = lastart+1;
X	return;
X    }
X  follow_root:
X    if( root_limit != p_articles + total.article ) {
X	register int r;
X
X	for( r = p_art->root; r < total.root; r++ ) {
X	    if( !selected_root_cnt || selected_roots[r] ) {
X		p_art = p_articles + p_roots[r].articles;
X		art = p_art->num;
X		if( p_art->subject == -1 || (cmd != 'N' && was_read( art )) ) {
X		    if( cmd != 'N' ) {
X			cmd = 'n';
X		    }
X		    curr_subj = -1;
X		    subthread_flag = FALSE;
X		    goto follow_again;
X		}
X		return;
X	    }
X	}
X    }
X    if( !count_roots( FALSE ) && unthreaded ) {
X	/* No threaded articles left -- blow everything else away */
X	for( art = firstart; art <= lastart; art++ ) {
X	    oneless( art );
X	}
X	unthreaded = 0;
X    }
X    p_art = Nullart;
X    art = lastart+1;
X}
X
X/* Go backward in the article tree.  Options match commands in article mode:
X**    p - previous unread article.
X**   ^P - previous unread article with same subject.
X**    P - previous article.
X*/
Xvoid
Xbacktrack_thread( cmd )
Xchar cmd;
X{
X    int curr_subj = -1, sel;
X    PACKED_ARTICLE *root_limit, *p_art_old = Nullart;
X
X    if( art > lastart ) {
X	p_art = p_articles + total.article - 1;
X	root_limit = Nullart;
X	goto backtrack_root;
X    }
X    if( !p_art ) {
X	art--;
X	return;
X    }
X    if( cmd == Ctl('p') ) {
X	if( (curr_subj = p_art->subject) == -1) {
X	    return;
X	}
X	p_art_old = p_art;
X    }
X  backtrack_again:
X    sel = (selected_roots[p_art->root] & 1);
X    root_limit = p_articles + p_roots[p_art->root].articles;
X    for( ;; ) {
X	if( p_art-- == root_limit ) {
X	    break;
X	}
X	if( !(art = p_art->num) ) {
X	    continue;
X	}
X	if( p_art->subject == -1 ) {
X	    set_read( art, sel );
X	} else if( !was_read( art )
X		&& (curr_subj < 0 || curr_subj == p_art->subject) ) {
X	    return;
X	} else if( cmd == 'P' ) {
X	    reread = TRUE;
X	    return;
X	}
X    }/* for */
X    if( p_art_old ) {
X	p_art = p_art_old;
X	p_art_old = Nullart;
X	curr_subj = -1;
X	goto backtrack_again;
X    }
X  backtrack_root:
X    if( root_limit != p_articles ) {
X	register int r;
X
X	for( r = p_art->root; r >= 0; r-- ) {
X	    if( !selected_root_cnt || selected_roots[r] ) {
X		art = p_art->num;
X		if( cmd != 'P' && was_read( art ) ) {
X		    goto backtrack_again;
X		}
X		return;
X	    }
X	    p_art = p_articles + p_roots[r].articles - 1;
X	}
X    }
X    p_art = Nullart;
X    art = absfirst-1;
X}
X
X/* Find the next root (first if p_art == NULL).  If roots are selected,
X** only choose from selected roots.
X*/
Xvoid
Xnext_root()
X{
X    register int r;
X
X    reread = FALSE;
X
X    if( p_art ) {
X	r = p_art->root+1;
X    } else {
X	r = 0;
X    }
X    for( ; r < total.root; r++ ) {
X	if( !selected_root_cnt || selected_roots[r] ) {
X	  try_again:
X	    p_art = p_articles + p_roots[r].articles;
X	    art = p_art->num;
X	    if( p_art->subject == -1 || (!reread && was_read( art )) ) {
X		follow_thread( reread ? 'N' : 'f' );
X		if( art == lastart+1 ) {
X		    if( scan_all_roots || selected_root_cnt
X		     || root_article_cnts[r] ) {
X			reread = TRUE;
X			goto try_again;
X		    }
X		    continue;
X		}
X	    }
X	    return;
X	}
X    }
X    p_art = Nullart;
X    art = lastart+1;
X    forcelast = TRUE;
X}
X
X/* Find previous root (or last if p_art == NULL).  If roots are selected,
X** only choose from selected roots.
X*/
Xvoid
Xprev_root()
X{
X    register int r;
X
X    reread = FALSE;
X
X    if( p_art ) {
X	r = p_art->root - 1;
X    } else {
X	r = total.root - 1;
X    }
X    for( ; r >= 0; r-- ) {
X	if( !selected_root_cnt || selected_roots[r] ) {
X	  try_again:
X	    p_art = p_articles + p_roots[r].articles;
X	    art = p_art->num;
X	    if( p_art->subject == -1 || (!reread && was_read( art )) ) {
X		follow_thread( reread ? 'N' : 'f' );
X		if( art == lastart+1 ) {
X		    if( scan_all_roots || selected_root_cnt
X		     || root_article_cnts[r] ) {
X			reread = TRUE;
X			goto try_again;
X		    }
X		    continue;
X		}
X	    }
X	    return;
X	}
X    }
X    p_art = Nullart;
X    art = lastart+1;
X    forcelast = TRUE;
X}
X
X/* Return a pointer value that we will equal when we've reached the end of
X** the current (sub-)thread.
X*/
XPACKED_ARTICLE *
Xupper_limit( artp, subthread_flag )
XPACKED_ARTICLE *artp;
Xbool subthread_flag;
X{
X    if( subthread_flag ) {
X	for( ;; ) {
X	    if( artp->siblings ) {
X		return artp + artp->siblings;
X	    }
X	    if( !artp->parent ) {
X		break;
X	    }
X	    artp += artp->parent;
X	}
X    }
X    return p_articles + (artp->root == total.root-1 ?
X	total.article : p_roots[artp->root+1].articles);
X}
X
X#endif /* USETHREADS */
SHAR_EOF
chmod 0660 rt-rn.c || echo "restore of rt-rn.c fails"
echo "x - extracting rt-select.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > rt-select.c &&
X/* $Header: rt-select.c,v 4.3.3.1 90/07/24 22:04:07 davison Trn $
X**
X** $Log:	rt-select.c,v $
X** Revision 4.3.3.1  90/07/24  22:04:07  davison
X** Initial Trn Release
X** 
X*/
X
X#include "EXTERN.h"
X#include "common.h"
X#include "rn.h"
X#include "rcstuff.h"
X#include "term.h"
X#include "final.h"
X#include "util.h"
X#include "help.h"
X#include "bits.h"
X#include "artsrch.h"
X#include "ng.h"
X#include "ngstuff.h"
X#include "rthreads.h"
X
X#ifdef USETHREADS
X
Xstatic int count_subj_lines();
Xstatic void display_subj();
X
X/* When display mode is 'l', each author gets a separate line; when 'm', up to
X** three authors share a line; when 's', no authors are displayed.
X*/
Xstatic char *display_mode = select_order;
Xstatic ART_NUM article_count;
Xstatic int author_line, mask = 1;
Xstatic char first_two_chars[3] = { ' ', ' ', '\0' };
X
X#define MAX_SEL 64
X
X/* Display a menu of roots for the user to choose from.  If cmd is '+'
X** we display all the unread roots and allow the user to mark roots as
X** selected and perform various commands upon the articles.  If cmd is
X** 'U' we display all the previously read roots and allow the user to
X** select which ones should be marked as unread.
X*/
Xchar
Xselect_thread( cmd )
Xchar cmd;
X{
X    register int i, j, cnt;
X    ART_NUM art_hold = art;
X    int line_cnt, screen_line, subj_line_cnt;
X    int cur_root, page_root, last_root = -1;
X    ART_LINE running_total, last_running;
X    int last_line, got_dash;
X    int max_root;
X    int first, last;
X    int root_line[MAX_SEL], root_hold[MAX_SEL];
X    int ch, action;
X    char page_char, end_char;
X    char promptbuf[80];
X    bool etc, clean_screen, empty_ok, displayed_status;
X    char oldmode = mode;
X#ifndef CONDSUB
X    char tmpbuf[2];
X#endif
X    char *select_chars, *in_select;
X    int max_cnt;
X
X    mode = 't';
X    unread_selector = (cmd == 'U');
X    clear_on_stop = TRUE;
X    empty_ok = FALSE;
X
X  select_threads:
X    /* Setup for selecting articles to read or set unread */
X    if( unread_selector ) {
X	page_char = '>';
X	end_char = 'Z';
X	page_root = 0;
X	last_root = -1;
X	cmd = 0;
X    } else {
X	page_char = page_select;
X	end_char = end_select;
X	page_root = select_page;
X	if( curr_p_art ) {
X	    last_root = curr_p_art->root;
X	}
X    }
X    mask = unread_selector+1;
X
X    /* Leave empty roots selected for a short time to give them a chance
X    ** to 'q' out of the selector if they got here by mistake.
X    */
X    max_root = count_roots( FALSE );
X
X    /* If nothing to display, we're done. */
X    if( !article_count && !empty_ok ) {
X     all_empty:
X	clear_on_stop = FALSE;
X	mode = oldmode;
X	putchar( '\n' );
X	if( unread_selector ) {
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs( "\nNo articles to set unread.\n", stdout );
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs( "\nNo articles.\n", stdout ) FLUSH;
X#endif
X	    unread_selector = 0;
X	    mask = 1;
X	} else {
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs( "\nNo unread articles to select.\n", stdout );
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs( "\nNo articles.\n", stdout );	/* let "them" FLUSH */
X#endif
X	}
X	(void) count_roots( TRUE );
X	art = art_hold;
X	p_art = curr_p_art;
X	return 'q';
X    }
X    if( unread_selector ) {
X	for( j = 0; j < total.root; j++ ) {
X	    selected_roots[j] |= 4;
X	}
X    }
X    if( page_root >= max_root ) {
X	ch = '<';
X    } else {
X	ch = '>';
X    }
X    cur_root = 0;
X    running_total = 0;
X    for( i = 0; i < page_root; i++ ) {
X	running_total += root_article_cnts[i];
X    }
X    do {
X	select_chars = getval( "SELECTCHARS", SELECTCHARS );
X	max_cnt = strlen( select_chars );
X	if( max_cnt > MAX_SEL ) {
X	    max_cnt = MAX_SEL;
X	}
X	if( ch == '<' && i ) {
X	    screen_line = 2;
X	    cnt = 0;
X	    /* Scan the roots in reverse to go back a page */
X	    do {
X		if( !root_article_cnts[--i] ) {
X		    continue;
X		}
X		first = root_subjects[i];
X		last = first + p_roots[i].subject_cnt;
X		line_cnt = 0;
X		for( j = first; j < last; j++ ) {
X		    line_cnt += count_subj_lines( i, j );
X		}
X		if( line_cnt > LINES - 5 ) {
X		    line_cnt = LINES - 5;
X		}
X		screen_line += line_cnt;
X		if( screen_line > LINES - 3 ) {
X		    i++;
X		    break;
X		}
X		running_total -= root_article_cnts[i];
X		cnt++;
X	    } while( i > 0 && cnt < max_cnt );
X	}
X
X	/* Present a page of subjects to the user */
X#ifndef CLEAREOL
X	clear();
X#else
X	if( can_home_clear ) {
X	    home_cursor();
X	    maybe_eol();
X	} else {
X	    clear();
X	}
X#endif
X	carriage_return();
X	page_root = i;
X	last_running = running_total;
X#ifdef NOFIREWORKS
X	no_sofire();
X#endif
X	standout();
X	fputs( ngname, stdout );
X	un_standout();
X	printf( "\t\t\t\t%ld %sarticle%s\n", (long)article_count,
X	    unread_selector? "read " : nullstr,
X	    article_count == 1 ? nullstr : "s" );
X#ifdef CLEAREOL
X	maybe_eol();
X#endif
X	putchar( '\n' ) FLUSH;
X	screen_line = 2;
X	for( cnt = 0; i < max_root && cnt < max_cnt; i++ ) {
X	    if( last_root == i ) {
X		cur_root = cnt;
X	    }
X	    /* Check each root for articles to list */
X	    if( !root_article_cnts[i] ) {
X		continue;
X	    }
X	    first = root_subjects[i];
X	    last = first + p_roots[i].subject_cnt;
X
X	    /* Compute how many lines we need to display the subjects/authors */
X	    etc = FALSE;
X	    line_cnt = 0;
X	    for( j = first; j < last; j++ ) {
X		subj_line_cnt = count_subj_lines( i, j );
X		line_cnt += subj_line_cnt;
X		/* If this root is too long to fit on the screen all by
X		** itself, trim it to fit and set the "etc" flag.
X		*/
X		if( line_cnt > LINES - 5 ) {
X		    last = j;
X		    line_cnt -= subj_line_cnt;
X		    if( line_cnt != LINES - 5 ) {
X			last++;
X			line_cnt = LINES - 5;
X		    }
X		    if( screen_line == 2 ) {
X			etc = TRUE;
X		    }
X		    break;
X		}
X	    }
X	    /* If it doesn't fit, save it for the next page */
X	    if( screen_line + line_cnt > LINES - 3 ) {
X		break;
X	    }
X	    /* Output the subjects, with optional authors */
X	    root_line[cnt] = screen_line;
X	    running_total += root_article_cnts[i];
X	    first_two_chars[0] = select_chars[cnt];
X	    first_two_chars[1] = (selected_roots[i] & 4) ? '-' :
X				 (selected_roots[i] & mask) ? '+' : ' ';
X	    author_line = screen_line;
X	    for( j = first; j < last; j++ ) {
X		display_subj( i, j );
X	    }
X	    screen_line += line_cnt;
X	    root_hold[cnt++] = i;
X	    if( etc ) {
X		fputs( "      ...etc.", stdout );
X		i++;
X		break;
X	    }
X	}/* for */
X	last_root = -1;
X	if( cur_root && cur_root >= cnt ) {
X	    cur_root = cnt - 1;
X	}
X
X	/* Check if there is really anything left to display. */
X	if( !running_total && !empty_ok ) {
X	    goto all_empty;
X	}
X	empty_ok = FALSE;
X
X	last_line = screen_line+1;
X#ifdef CLEAREOL
X	maybe_eol();
X#endif
X	putchar( '\n' ) FLUSH;
X	/* Prompt the user */
X	strcpy( promptbuf, "-- Select threads -- " );
X	if( i != max_root ) {
X	    sprintf( promptbuf+21, "%s%d%% [%c%c] --",
X		(!page_root? "Top " : nullstr),
X		running_total*100 / article_count, page_char, end_char );
X	} else {
X	    sprintf( promptbuf+21, "%s [%c%c] --",
X		(!page_root? "All" : "Bot"), end_char, page_char );
X	}
X	if( cur_root > cnt ) {
X	    cur_root = 0;
X	}
X	screen_line = root_line[cur_root];
X#ifdef CLEAREOL
X	if( erase_screen && can_home_clear ) {
X	    clear_rest();
X	}
X#endif
X	displayed_status = FALSE;
X      prompt_select:
X	standout();
X	fputs( promptbuf, stdout );
X	un_standout();
X	if( can_home ) {
X	    carriage_return();
X	    goto_line( last_line, screen_line );
X	}
X	got_dash = 0;
X	/* Grab some commands from the user */
X	for( ;; ) {
X	    fflush(stdout);
X	    eat_typeahead();
X#ifdef CONDSUB
X	    getcmd( buf );
X	    ch = *buf;
X#else
X	    getcmd( tmpbuf );	/* If no conditionals, don't allow macros */ 
X	    ch = *tmpbuf;
X	    buf[0] = ch;
X	    buf[1] = FINISHCMD;
X#endif
X	    in_select = index( select_chars, ch );
X	    /* Plaster any inherited empty roots on first command, if not q. */
X	    if( cmd && (in_select || (ch != '\033' && ch != 'q')) ) {
X		max_root = count_roots( TRUE );
X		cmd = 0;
X	    }
X	    if( displayed_status && can_home ) {
X		goto_line( screen_line, last_line+1 );
X		erase_eol();
X		screen_line = last_line+1;
X		displayed_status = FALSE;
X	    }
X	    if( ch == '-' ) {
X		got_dash = 1;
X		if( !can_home ) {
X		    putchar( '-' );
X		    fflush( stdout );
X		}
X		continue;
X	    }
X	    if( ch == ' ' ) {
X		if( i == max_root ) {
X		    ch = end_char;
X		} else {
X		    ch = page_char;
X		}
X	    }
X	    if( !in_select && (index( "<>^$!?&:/hDJLNqQUXZ\n\r\t\033", ch )
X	     || ch == Ctl('l') || ch == Ctl('r') || ch == Ctl('k')) ) {
X		break;
X	    }
X	    if( in_select ) {
X		j = in_select - select_chars;
X		if( j >= cnt ) {
X		    dingaling();
X		    j = -1;
X		} else if( got_dash ) {
X		    ;
X		} else if( selected_roots[root_hold[j]] & mask ) {
X		    action = (unread_selector ? 'k' : '-');
X		} else {
X		    action = '+';
X		}
X	    } else if( ch == 'y' || ch == '.' ) {
X		j = cur_root;
X		if( selected_roots[root_hold[j]] & mask ) {
X		    action = (unread_selector ? 'k' : '-');
X		} else {
X		    action = '+';
X		}
X	    } else if( ch == 'k' || ch == 'j' || ch == ',' ) {
X		j = cur_root;
X		action = 'k';
X	    } else if( ch == 'm' || ch == '\\' ) {
X		j = cur_root;
X		action = 'm';
X	    } else if( ch == '@' ) {
X		cur_root = 0;
X		j = cnt-1;
X		got_dash = 1;
X		action = '@';
X	    } else if( ch == '[' || ch == 'p' ) {
X		if( --cur_root < 0 ) {
X		    cur_root = cnt ? cnt-1 : 0;
X		}
X		j = -1;
X	    } else if( ch == ']' || ch == 'n' ) {
X		if( ++cur_root >= cnt ) {
X		    cur_root = 0;
X		}
X		j = -1;
X	    } else {
X		if( can_home ) {
X		    goto_line( screen_line, last_line+1 );
X		    screen_line = last_line+1;
X		} else {
X		    putchar( '\n' );
X		}
X		printf( "Type ? for help." );
X		settle_down();
X		displayed_status = TRUE;
X
X		if( can_home ) {
X		    carriage_return();
X		} else {
X		    putchar( '\n' );
X		}
X		j = -1;
X	    }
X	    if( j >= 0 ) {
X		if( !got_dash ) {
X		    cur_root = j;
X		} else {
X		    got_dash = 0;
X		    if( j < cur_root ) {
X			ch = cur_root-1;
X			cur_root = j;
X			j = ch;
X		    }
X		}
X		if( ++j == cnt ) {
X		    j = 0;
X		}
X		do {
X		    if( can_home ) {
X			goto_line( screen_line, root_line[cur_root] );
X			screen_line = root_line[cur_root];
X		    }
X		    putchar( select_chars[cur_root] );
X		    if( action == '@' ) {
X			if( selected_roots[root_hold[cur_root]] & 4 ) {
X			    ch = (unread_selector ? '+' : ' ');
X			} else if( unread_selector ) {
X			    ch = 'k';
X			} else
X			if( selected_roots[root_hold[cur_root]] & mask ) {
X			    ch = '-';
X			} else {
X			    ch = '+';
X			}
X		    } else {
X			ch = action;
X		    }
X		    switch( ch ) {
X		    case '+':
X			if( !(selected_roots[root_hold[cur_root]] & mask) ) {
X			    selected_roots[root_hold[cur_root]] |= mask;
X			    selected_root_cnt++;
X			    selected_count
X				+= root_article_cnts[root_hold[cur_root]];
X			    putchar( '+' );
X			}
X			/* FALL THROUGH */
X		    case 'm':
X			if( selected_roots[root_hold[cur_root]] & 4 ) {
X			    selected_roots[root_hold[cur_root]] &= ~4;
X			    if( ch == 'm' ) {
X				putchar( ' ' );
X			    }
X			} else if( ch == 'm' ) {
X			    goto unsel;
X			}
X			break;
X		    case 'k':
X			if( !(selected_roots[root_hold[cur_root]] & 4) ) {
X			    selected_roots[root_hold[cur_root]] |= 4;
X			    putchar( '-' );
X			}
X			/* FALL THROUGH */
X		    case '-':
X		    unsel:
X			if( selected_roots[root_hold[cur_root]] & mask ) {
X			    selected_roots[root_hold[cur_root]] &= ~mask;
X			    selected_root_cnt--;
X			    selected_count
X				-= root_article_cnts[root_hold[cur_root]];
X			    if( ch != 'k' ) {
X				putchar( ' ' );
X			    }
X			}
X			break;
X		    }
X		    fflush( stdout );
X		    if( ++cur_root == cnt ) {
X			cur_root = 0;
X		    }
X		    if( can_home ) {
X			carriage_return();
X		    }
X		} while( cur_root != j );
X	    } else {
X		got_dash = FALSE;
X	    }
X	    if( can_home ) {
X		goto_line( screen_line, root_line[cur_root] );
X		screen_line = root_line[cur_root];
X	    }
X	}/* for */
X	if( can_home) {
X	    goto_line( screen_line, last_line );
X	}
X	clean_screen = TRUE;
X      do_command:
X	if( ch == 'L' ) {
X	    if( !*++display_mode ) {
X		display_mode = select_order;
X	    }
X	    ch = Ctl('l');
X	    cur_root = 0;
X	} else if( ch == '$' ) {
X	    ch = '<';
X	    page_root = max_root;
X	    last_running = article_count;
X	    cur_root = 0;
X	} else if( ch == '^' || ch == Ctl('r') ) {
X	    ch = '>';
X	    i = 0;
X	    running_total = 0;
X	    cur_root = 0;
X	} else if( ch == 'h' || ch == '?' ) {
X	    putchar( '\n' );
X	    if( (ch = help_select()) || (ch = pause_getcmd()) ) {
X		goto got_cmd;
X	    }
X	    ch = Ctl('l');
X	} else if( index( ":/&!", ch ) ) {
X	    erase_eol();		/* erase the prompt */
X	    if( !finish_command( TRUE ) ) {	/* get rest of command */
X		if( clean_screen ) {
X		    screen_line = root_line[cur_root];
X		    goto prompt_select;
X		}
X		goto extend_done;
X	    }
X	    if( ch == '&' || ch == '!' ) {
X		one_command = TRUE;
X		perform( buf, FALSE );
X		one_command = FALSE;
X
X		putchar( '\n' ) FLUSH;
X		clean_screen = FALSE;
X	    } else {
X		int selected_save = selected_root_cnt;
X
X		if( ch == ':' ) {
X		    clean_screen = (use_selected() == 2) && clean_screen;
X		} else {
X		    /* Force the search to begin at absfirst or firstart,
X		    ** depending upon whether they specified the 'r' option.
X		    */
X		    art = lastart+1;
X		    page_line = 1;
X		    switch( art_search( buf, sizeof buf, FALSE ) ) {
X		    case SRCH_ERROR:
X		    case SRCH_ABORT:
X		    case SRCH_INTR:
X			fputs( "\nInterrupted\n", stdout ) FLUSH;
X			break;
X		    case SRCH_DONE:
X		    case SRCH_SUBJDONE:
X			fputs( "Done\n", stdout ) FLUSH;
X			break;
X		    case SRCH_NOTFOUND:
X			fputs( "\nNot found.\n", stdout ) FLUSH;
X			break;
X		    case SRCH_FOUND:
X			break;
X		    }
X		    clean_screen = FALSE;
X		}
X		/* Recount, in case something has changed. */
X		max_root = count_roots( !unread_selector );
X
X		if( (selected_save -= selected_root_cnt) != 0 ) {
X		    putchar( '\n' );
X		    if( selected_save < 0 ) {
X			fputs( "S", stdout );
X			selected_save *= -1;
X		    } else {
X			fputs( "Des", stdout );
X		    }
X		    printf( "elected %d thread%s.", selected_save,
X			selected_save == 1 ? nullstr : "s" );
X		    clean_screen = FALSE;
X		}
X		if( !clean_screen ) {
X		    putchar('\n') FLUSH;
X		}
X	    }/* if !& or :/ */
X
X	    if( clean_screen ) {
X		carriage_return();
X		up_line();
X		erase_eol();
X		screen_line = root_line[cur_root];
X		goto prompt_select;
X	    }
X	  extend_done:
X	    if( (ch = pause_getcmd()) ) {
X	      got_cmd:
X		if( ch > 0 ) {
X		    /* try to optimize the screen update for some commands. */
X		    if( !index( select_chars, ch )
X		     && (index( "<>^$!?&:/hDJLNqQUXZ\n\r\t\033", ch )
X		      || ch == Ctl('k')) ) {
X			buf[0] = ch;
X			buf[1] = FINISHCMD;
X			goto do_command;
X		    }
X		    pushchar( ch | 0200 );
X		}
X	    }
X	    ch = Ctl('l');
X	} else if( ch == Ctl('k') ) {
X	    edit_kfile();
X	    ch = Ctl('l');
X	} else if( !unread_selector && (ch == 'X' || ch == 'D' || ch == 'J') ) {
X	    if( ch == 'D' ) {
X		j = page_root;
X		last = i;
X	    } else {
X		j = 0;
X		last = max_root;
X	    }
X	    for( ; j < last; j++ ) {
X		if( (!(selected_roots[j] & 1) ^ (ch == 'J'))
X		 && (cnt = root_article_cnts[j]) ) {
X		    p_art = p_articles + p_roots[j].articles;
X		    art = 0;
X		    follow_thread( 'J' );
X		}
X	    }
X	    max_root = count_roots( TRUE );
X	    if( article_count
X	     && (ch == 'J' || (ch == 'D' && !selected_root_cnt)) ) {
X		ch = Ctl('l');
X		cur_root = 0;
X	    } else {
X		break;
X	    }
X	} else if( ch == 'J' ) {
X	    for( j = 0; j < max_root; j++ ) {
X		selected_roots[j] = (selected_roots[j] & ~2) | 4;
X	    }
X	    selected_root_cnt = selected_count = 0;
X	    ch = Ctl('l');
X	}
X	if( ch == '>' ) {
X	    cur_root = 0;
X	} else if( ch == '<' || (page_root && page_root >= max_root) ) {
X	    cur_root = 0;
X	    running_total = last_running;
X	    if( !(i = page_root) || !max_root ) {
X		ch = '>';
X	    } else {
X		ch = '<';
X	    }
X	} else if( ch == Ctl('l') ) {
X	    i = page_root;
X	    running_total = last_running;
X	    ch = '>';
X	} else if( ch == '\r' || ch == '\n' ) {
X	    if( !selected_root_cnt ) {
X		selected_roots[root_hold[cur_root]] = mask;
X		selected_root_cnt++;
X		selected_count += root_article_cnts[root_hold[cur_root]];
X	    }
X	}
X    } while( (ch == '>' && i < max_root) || ch == '<' );
X    putchar( '\n' ) FLUSH;
X
X    if( unread_selector ) {
X	/* Turn selections into unread selected roots.  Let count_roots()
X	** fix the counts after we're through.
X	*/
X	last_root = -1;
X	for( j = 0; j < total.root; j++ ) {
X	    if( !(selected_roots[j] & 4) ) {
X		if( selected_roots[j] & 2 ) {
X		    selected_roots[j] = 1;
X		}
X		p_art = p_articles + p_roots[j].articles;
X		art = 0;
X		follow_thread( 'u' );
X	    } else {
X		selected_roots[j] &= ~4;
X	    }
X	}
X    } else {
X	select_page = page_root;
X	for( j = 0; j < total.root; j++ ) {
X	    if( selected_roots[j] & 4 ) {
X		selected_roots[j] = 0;
X		p_art = p_articles + p_roots[j].articles;
X		art = 0;
X		follow_thread( 'J' );
X	    }
X	}
X    }
X    if( ch == 'U' ) {
X	unread_selector = !unread_selector;
X	empty_ok = TRUE;
X	goto select_threads;
X    }
X
X    if( unread_selector ) {
X	unread_selector = 0;
X	mask = 1;
X	(void) count_roots( FALSE );
X    }
X    if( ch == '\033' || Ctl(ch) == Ctl('q') ) {
X	ch = (ch == 'Q' ? 'Q' : 'q');
X	art = art_hold;
X	p_art = curr_p_art;
X    } else if( ch == 'N' ) {
X	art = art_hold;
X	p_art = curr_p_art;
X    } else {
X	first_art();
X    }
X    clear_on_stop = FALSE;
X    mode = oldmode;
X    return ch;
X}
X
Xstatic int author_cnt, first_author;
X
X/* Counts the number of lines needed to output a subject, including optional
X** authors.
X*/
Xstatic int
Xcount_subj_lines( root, subj )
Xint root;
Xint subj;
X{
X    PACKED_ARTICLE *artp, *root_limit;
X    int author_subj;
X
X    author_cnt = 0;
X    author_subj = subj;
X    first_author = -1;
X
X    if( !subject_cnts[subj] ) {
X	return 0;
X    }
X    if( *display_mode == 's' ) {	/* no-author mode takes one line */
X	return ++author_cnt;
X    }
X    bzero( author_cnts, total.author * sizeof (WORD) );
X
X    /* Count authors associated with this subject.  Increments author_cnts. */
X    artp = p_articles + p_roots[root].articles;
X    root_limit = upper_limit( artp, FALSE );
X    for( ; artp != root_limit; artp++ ) {
X	if( artp->subject == author_subj
X	 && (!was_read( artp->num ) ^ unread_selector) ) {
X	    if( artp->author < 0 || artp->author >= total.author ) {
X		printf( "\
XFound invalid author (%d) with valid subject (%d)! [%ld]\n",
X			artp->author, artp->subject, artp->num );
X		artp->author = 0;
X	    } else {
X		if( first_author < 0 ) {
X		    first_author = artp->author;
X		}
X		if( !author_cnts[artp->author]++ ) {
X		    author_cnt++;
X		}
X	    }
X	}
X    }
X
X    if( *display_mode == 'm' ) {
X	return (author_cnt+4)/3;
X    } else {
X	return author_cnt;
X    }
X}
X
Xstatic void
Xdisplay_subj( root, subj )
Xint root;
Xint subj;
X{
X    PACKED_ARTICLE *artp, *root_limit;
X    char *str;
X
X    count_subj_lines( root, subj );
X    if( !author_cnt ) {
X	return;
X    }
X    artp = p_articles + p_roots[root].articles;
X    if( artp->subject != -1 && (artp->flags & ROOT_ARTICLE)
X     && (!was_read(artp->num) ^ unread_selector) ) {
X	str = nullstr;
X    } else {
X	str = ">";
X    }
X#ifdef CLEAREOL
X    maybe_eol();
X#endif
X    if( *display_mode == 's' ) {
X	printf( "%s%3d  %s%.71s\n", first_two_chars,
X		subject_cnts[subj], str, subject_ptrs[subj] ) FLUSH;
X    } else {
X	printf( "%s%-16.16s%3d  %s%.55s", first_two_chars,
X		author_ptrs[first_author],
X		subject_cnts[subj], str, subject_ptrs[subj] );
X	if( author_cnt > 1 ) {
X	    author_cnts[first_author] = 0;
X	    author_cnt = 0;
X	    root_limit = upper_limit( artp, FALSE );
X	    for( ; artp != root_limit; artp++ ) {
X		if( artp->author >= 0 && author_cnts[artp->author] ) {
X		    switch( author_cnt % 3 ) {
X		    case 0:
X			putchar( '\n' ) FLUSH;
X			if( ++author_line >= LINES - 3 ) {
X			    return;
X			}
X#ifdef CLEAREOL
X			maybe_eol();
X#endif
X			putchar( ' ' );
X			putchar( ' ' );
X			break;
X		    case 1:
X			putchar( '\t' );
X			putchar( '\t' );
X			break;
X		    case 2:
X			putchar( '\t' );
X			break;
X		    }
X		    author_cnt += (*display_mode == 'm');
X		    printf( "%-16.16s", author_ptrs[artp->author] );
X		    author_cnts[artp->author] = 0;
X		}/* if */
X	    }/* for */
X	}/* if */
X	putchar( '\n' ) FLUSH;
X	author_line++;
X    }/* if */
X    first_two_chars[0] = first_two_chars[1] = ' ';
X}
X
X/* Get each root's article count, and subject count(s); count total
X** articles and selected articles (use unread_selector to determine
X** whether to count read or unread articles); deselect any roots we
X** find that are empty (if do_unselect is TRUE); find the last non-
X** empty root, and return its count (the index+1).
X*/
Xint
Xcount_roots( do_unselect )
Xbool do_unselect;
X{
X    register int count;
X    register PACKED_ARTICLE *artp, *root_limit, *art_limit;
X    int last_root = -1;
X
X    article_count = selected_count = selected_root_cnt = 0;
X
X    if( !(artp = p_articles) ) {
X	return 0;
X    }
X    art_limit = artp + total.article;
X    root_limit = upper_limit( artp, 0 );
X
X    bzero( subject_cnts, total.subject * sizeof (WORD) );
X    count = 0;
X
X    for( ;; ) {
X	if( artp->subject == -1 ) {
X	    if( !was_read( artp->num ) ) {
X		oneless( artp->num );
X	    }
X	} else if( (!was_read( artp->num ) ^ unread_selector) ) {
X	    count++;
X	    subject_cnts[artp->subject]++;
X	}
X	if( ++artp == root_limit ) {
X	    int root_num = artp[-1].root;
X
X	    root_article_cnts[root_num] = count;
X	    if( count ) {
X		article_count += count;
X		if( selected_roots[root_num] & mask ) {
X		    selected_roots[root_num] &= ~4;
X		    selected_root_cnt++;
X		    selected_count += count;
X		}
X		last_root = root_num;
X	    } else if( do_unselect ) {
X		selected_roots[root_num] &= ~mask;
X	    } else if( selected_roots[root_num] & mask ) {
X		selected_roots[root_num] &= ~4;
X		selected_root_cnt++;
X	    }
X	    if( artp == art_limit ) {
X		break;
X	    }
X	    root_limit = upper_limit( artp, 0 );
X	    count = 0;
X	}
X    }
X    if( do_unselect ) {
X	scan_all_roots = !article_count;
X    }
X    unthreaded = toread[ng] - article_count;
X
X    return last_root+1;
X}
X
X/* Count the unread articles attached to the given root number.
X*/
Xint
Xcount_one_root( root_num )
Xint root_num;
X{
X    int last = (root_num == total.root-1 ? total.article
X					 : p_roots[root_num+1].articles);
X    register int count = 0, i;
X
X    for( i = p_roots[root_num].articles; i < last; i++ ) {
X	if( p_articles[i].subject != -1 && !was_read( p_articles[i].num ) ) {
X	    count++;
X	}
X    }
X    root_article_cnts[root_num] = count;
X
X    return count;
X}
X
X#endif /* USETHREADS */
SHAR_EOF
chmod 0660 rt-select.c || echo "restore of rt-select.c fails"
echo "x - extracting rthreads.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > rthreads.c &&
X/* $Header: rthreads.c,v 4.3.3.1 90/06/20 23:00:28 davison Trn $
X**
X** $Log:	rthreads.c,v $
X** Revision 4.3.3.1  90/06/20  23:00:28  davison
X** Initial Trn Release
X** 
X*/
X
X#include "EXTERN.h"
X#include "common.h"
X#include "intrp.h"
X
X#ifdef USETHREADS
X#include "INTERN.h"
X#include "rthreads.h"
X
Xstatic FILE *fp_in;
X
Xstatic char *strings = Nullch;
X
Xstatic int read_item();
Xstatic void wp_bmap(), lp_bmap();
Xstatic void Free();
X
X/* Initialize our thread code by determining the byte-order of the thread
X** files and our own current byte-order.  If they differ, set flags to let
X** the read code know what we'll need to translate.
X*/
Xvoid
Xthread_init()
X{
X    char *filename;
X    int i;
X
X    word_same = long_same = TRUE;
X    filename = filexp( "%X/db.init" );
X    if( (fp_in = fopen( filename, "r" )) != Nullfp ) {
X	if( fread( &mt_bmap, 1, sizeof (BMAP), fp_in ) == sizeof (BMAP) ) {
X	    mybytemap( &my_bmap );
X	    for( i = 0; i < sizeof (LONG); i++ ) {
X		if( i < sizeof (WORD) ) {
X		    if( my_bmap.w[i] != mt_bmap.w[i] ) {
X			word_same = FALSE;
X		    }
X		}
X		if( my_bmap.l[i] != mt_bmap.l[i] ) {
X		    long_same = FALSE;
X		}
X	    }
X	}
X	fclose( fp_in );
X    }
X}
X
X/* Open a thread file for the sole purpose of using it in a newsreader-
X** style application.  Everything is read into arrays in chunks and some
X** useful massaging of the data is performed to make the newsreader's life
X** easier.  Be sure to call unuse_data() before calling this a second time.
X*/
Xint
Xuse_data( threadname )
Xchar *threadname;
X{
X    register int i, j, k;
X    register char *ptr;
X
X    if( (fp_in = fopen( threadname, "r" )) == Nullfp ) {
X	if (errno != ENOENT) {
X	    printf( "\n\nOpen failed for thread data -- continuing unthreaded.\n" );
X	}
X	bzero( &total, sizeof (TOTAL) );
X	return 0;
X    }
X    if( fread( &total, 1, sizeof (TOTAL), fp_in ) < sizeof (TOTAL) ) {
SHAR_EOF
echo "End of part 12"
echo "File rthreads.c is continued in part 13"
echo "13" > s2_seq_.tmp
exit 0

exit 0 # Just in case...
-- 
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.



More information about the Comp.sources.unix mailing list