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