rsalz@bbn.com (Rich Salz) (12/04/90)
Submitted-by: Wayne Davison <davison@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@drivax.UUCP (davison%%drivax@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@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.