v23i062: Complete reposting of TRN at patchlevel 1, Part03/14

Rich Salz rsalz at bbn.com
Fri Jan 4 04:54:03 AEST 1991


Submitted-by: Wayne Davison <0004475895 at mcimail.com>
Posting-number: Volume 23, Issue 62
Archive-name: trn/part03

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix at uunet.uu.net if you want that tool.
# Contents:  makekit ng.c rcstuff.c
# Wrapped by rsalz at litchi.bbn.com on Thu Dec 27 11:34:00 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo '          "shar: End of archive 3 (of 14)."'
if test -f 'makekit' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'makekit'\"
else
  echo shar: Extracting \"'makekit'\" \(745 characters\)
  sed "s/^X//" >'makekit' <<'END_OF_FILE'
X#!/bin/sh
X# $Header: makekit,v 4.3 85/05/01 11:42:38 lwall Exp $
X#
X# $Log:	makekit,v $
X# Revision 4.3  85/05/01  11:42:38  lwall
X# Baseline for release with 4.3bsd.
X# 
X
Xnumkits=$#
Xfor kitlist in $*; do
X    kit=`basename $kitlist .list`
X    kitnum=`expr "$kit" : 'kit\([0-9][0-9]*\)'`
X    echo "*** Making $kit ***"
X    kitleader "$kit" "$kitnum" "$numkits"
X    for file in `/bin/cat $kitlist`; do
X	echo $file
X	echo "echo Extracting $file" >> $kit
X	if egrep '^\.$' $file; then
X	    echo "sed >$file <<'!STUFFY!FUNK!' -e 's/X//'" >> $kit
X	    sed <$file >>$kit -e 's/^/X/'
X	else
X	    echo "cat >$file <<'!STUFFY!FUNK!'" >> $kit
X	    /bin/cat $file >> $kit
X	fi
X	echo "!STUFFY!FUNK!" >> $kit
X    done
X    kittrailer "$kit" "$kitnum" "$numkits"
Xdone
END_OF_FILE
  if test 745 -ne `wc -c <'makekit'`; then
    echo shar: \"'makekit'\" unpacked with wrong size!
  fi
  chmod +x 'makekit'
  # end of 'makekit'
fi
if test -f 'ng.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ng.c'\"
else
  echo shar: Extracting \"'ng.c'\" \(34937 characters\)
  sed "s/^X//" >'ng.c' <<'END_OF_FILE'
X/* $Header: ng.c,v 4.3.3.2 90/08/20 16:03:45 davison Trn $
X *
X * $Log:	ng.c,v $
X * Revision 4.3.3.2  90/08/20  16:03:45  davison
X * Fixed bug in backpage code.
X * 
X * Revision 4.3.3.1  90/07/21  20:27:17  davison
X * Initial Trn Release
X * 
X * Revision 4.3.2.7  90/04/21  14:44:23  sob
X * Revised previous patch insure that it does not decrement below zero.
X * 
X * Revision 4.3.2.6  90/03/22  23:04:49  sob
X * Fixes provided by Wayne Davison <drivax!davison>
X * 
X * Revision 4.3.2.5  89/12/09  01:18:42  sob
X * Fixed a bad call to nntpopen().
X * 
X * Revision 4.3.2.4  89/11/28  01:51:20  sob
X * Removed redundant #include directive.
X * 
X * Revision 4.3.2.3  89/11/27  01:31:03  sob
X * Altered NNTP code per ideas suggested by Bela Lubkin
X * <filbo at gorn.santa-cruz.ca.us>
X * 
X * Revision 4.3.2.2  89/11/26  22:53:35  sob
X * Add new patches to make RRN be faster.
X * 
X * Revision 4.3.2.1  89/11/06  00:54:27  sob
X * Added RRN support from NNTP 1.5
X * 
X * Revision 4.3.1.6  85/09/10  11:03:42  lwall
X * Improved %m in in_char().
X * 
X * Revision 4.3.1.5  85/09/05  12:34:37  lwall
X * Catchup command could make unread article count too big.
X * 
X * Revision 4.3.1.4  85/07/23  18:19:46  lwall
X * Added MAILCALL environment variable.
X * 
X * Revision 4.3.1.3  85/05/16  16:48:09  lwall
X * Fixed unsubsubscribe.
X * 
X * Revision 4.3.1.2  85/05/13  09:29:28  lwall
X * Added CUSTOMLINES option.
X * 
X * Revision 4.3.1.1  85/05/10  11:36:00  lwall
X * Branch for patches.
X * 
X * Revision 4.3  85/05/01  11:43:43  lwall
X * Baseline for release with 4.3bsd.
X * 
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "rn.h"
X#include "term.h"
X#include "final.h"
X#include "util.h"
X#include "artsrch.h"
X#include "cheat.h"
X#include "help.h"
X#include "kfile.h"
X#include "rcstuff.h"
X#include "head.h"
X#include "bits.h"
X#include "art.h"
X#include "artio.h"
X#include "ngstuff.h"
X#include "intrp.h"
X#include "respond.h"
X#include "ngdata.h"
X#include "backpage.h"
X#include "rcln.h"
X#include "last.h"
X#include "search.h"
X#ifdef SERVER
X#include "server.h"
X#endif
X#ifdef USETHREADS
X#include "rthreads.h"
X#endif
X#include "uudecode.h"
X#include "INTERN.h"
X#include "ng.h"
X#include "artstate.h"			/* somebody has to do it */
X
X/* art_switch() return values */
X
X#define AS_NORM 0
X#define AS_INP 1
X#define AS_ASK 2
X#define AS_CLEAN 3
X
XART_NUM recent_art = -1;	/* previous article # for '-' command */
XART_NUM curr_art = -1;		/* current article # */
Xint exit_code = NG_NORM;
X
Xvoid
Xng_init()
X{
X
X#ifdef KILLFILES
X    open_kfile(KF_GLOBAL);
X#endif
X#ifdef CUSTOMLINES
X    init_compex(&hide_compex);
X    init_compex(&page_compex);
X#endif
X}
X
X/* do newsgroup on line ng with name ngname */
X
X/* assumes that we are chdir'ed to SPOOL, and assures that that is
X * still true upon return, but chdirs to SPOOL/ngname in between
X *
X * If you can understand this routine, you understand most of the program.
X * The basic structure is:
X *	for each desired article
X *		for each desired page
X *			for each line on page
X *				if we need another line from file
X *					get it
X *					if it's a header line
X *						do special things
X *				for each column on page
X *					put out a character
X *				end loop
X *			end loop
X *		end loop
X *	end loop
X *
X *	(Actually, the pager is in another routine.)
X *
X * The chief problem is deciding what is meant by "desired".  Most of
X * the messiness of this routine is due to the fact that people want
X * to do unstructured things all the time.  I have used a few judicious
X * goto's where I thought it improved readability.  The rest of the messiness
X * arises from trying to be both space and time efficient.  Have fun.
X */
X
Xint
Xdo_newsgroup(start_command)
Xchar *start_command;			/* command to fake up first */
X{
X#ifdef SERVER
X    char ser_line[256];
X    char artname[32];
X    static long our_pid;
X#endif /* SERVER */
X    char oldmode = mode;
X    register long i;			/* scratch */
X    int skipstate;			/* how many unavailable articles */
X					/*   have we skipped already? */
X    
X    char *whatnext = "%sWhat next? [%s]";
X
X#ifdef SERVER
X    if (our_pid == 0)           /* Agreed, this is gross */
X        our_pid = getpid();
X#endif /* SERVER */
X
X#ifdef ARTSEARCH
X    srchahead = (scanon && ((ART_NUM)toread[ng]) >= scanon ? -1 : 0);
X					/* did they say -S? */
X#endif
X    
X    mode = 'a';
X#ifdef USETHREADS
X    recent_p_art = curr_p_art = Nullart;
X#endif
X    recent_art = curr_art = -1;
X    exit_code = NG_NORM;
X
X#ifdef SERVER
X    sprintf(ser_line, "GROUP %s", ngname);
X    put_server(ser_line);
X    if (get_server(ser_line, sizeof(ser_line)) < 0) {
X	fprintf(stderr, "rrn: Unexpected close of server socket.\n");
X	finalize(1);
X    }
X    if (*ser_line != CHAR_OK) {
X	if (atoi(ser_line) != ERR_NOGROUP)
X		fprintf(stderr, "rrn: server response to GROUP %s:\n%s\n",
X			ngname, ser_line);
X	return (-1);
X    }
X#else /* not SERVER */
X    if (eaccess(ngdir,5)) {		/* directory read protected? */
X	if (eaccess(ngdir,0)) {
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("\nNewsgroup %s does not have a spool directory!\n",
X		    ngname) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		printf("\nNo spool for %s!\n",ngname) FLUSH;
X#endif
X#ifdef CATCHUP
X	    catch_up(ng);
X#endif
X	    toread[ng] = TR_NONE;
X	}
X	else {
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("\nNewsgroup %s is not currently accessible.\n",
X		    ngname) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		printf("\n%s not readable.\n",ngname) FLUSH;
X#endif
X	    toread[ng] = TR_NONE;	/* make this newsgroup invisible */
X					/* (temporarily) */
X	}
X	mode = oldmode;
X	return -1;
X    }
X
X    /* chdir to newsgroup subdirectory */
X
X    if (chdir(ngdir)) {
X	printf(nocd,ngdir) FLUSH;
X	mode = oldmode;
X	return -1;
X    }
X#endif /* SERVER */
X
X#ifdef CACHESUBJ
X    subj_list = Null(char **);		/* no subject list till needed */
X#endif
X    
X    /* initialize control bitmap */
X
X    if (initctl()) {
X	mode = oldmode;
X	return -1;
X    }
X
X    /* FROM HERE ON, RETURN THRU CLEANUP OR WE ARE SCREWED */
X
X    /* grab threaded data */
X
X#ifdef USETHREADS
X    if (ThreadedGroup && (ThreadedGroup = use_data(thread_name(ngname)))) {
X	/* check if thread file is newer than the active2 entry (this is
X	** possible when mthreads is still running.) */
X	if (total.last > lastart) {
X	    grow_ctl(total.last);	/* sets lastart */
X	}
X	else if (total.last < lastart) {
X	    /* If the active2 entry is newer than the data file, something
X	    ** bad is going on.  Forget using the thread data. */
X	    unuse_data(0);
X	    ThreadedGroup = FALSE;
X	}
X    }
X#endif
X
X    in_ng = TRUE;			/* tell the world we are here */
X    forcelast = TRUE;			/* if 0 unread, do not bomb out */
X
X    /* remember what newsgroup we were in for sake of posterity */
X
X    writelast();
X
X    /* see if there are any special searches to do */
X
X#ifdef KILLFILES
X    open_kfile(KF_LOCAL);
X#ifdef VERBOSE
X    IF(verbose)
X	kill_unwanted(firstart,"Looking for articles to kill...\n\n",TRUE);
X    ELSE
X#endif
X#ifdef TERSE
X	kill_unwanted(firstart,"Killing...\n\n",TRUE);
X#endif
X#endif
X#ifdef USETHREADS
X    first_art();
X#else
X    art=firstart;
X#endif
X    
X    /* do they want a special top line? */
X
X    firstline = getval("FIRSTLINE",Nullch);
X
X    /* custom line suppression, custom page ending */
X
X#ifdef CUSTOMLINES
X    if (hideline = getval("HIDELINE",Nullch))
X	compile(&hide_compex,hideline,TRUE,TRUE);
X    if (pagestop = getval("PAGESTOP",Nullch))
X	compile(&page_compex,pagestop,TRUE,TRUE);
X#endif
X
X    /* now read each unread article */
X
X    rc_changed = doing_ng = TRUE;	/* enter the twilight zone */
X    skipstate = 0;			/* we have not skipped anything (yet) */
X    checkcount = 0;			/* do not checkpoint for a while */
X    do_fseek = FALSE;			/* start 1st article at top */
X    if (art > lastart)
X#ifdef USETHREADS
X	first_art();
X#else
X	art=firstart;			/* init the for loop below */
X#endif
X    for (; art<=lastart+1; ) {		/* for each article */
X
X	/* do we need to "grow" the newsgroup? */
X
X#ifdef USETHREADS
X	if (ThreadedGroup) {
X	    if ((art > lastart || forcegrow) && getngsize(ng) > total.last) {
X		unuse_data(1);		/* free data with selections saved */
X		if ((ThreadedGroup = use_data(thread_name(ngname))) != 0) {
X		    grow_ctl(total.last);	/* sets lastart */
X		    find_article(art);
X		    curr_p_art = p_art;
X		}
X		forcegrow = FALSE;
X	    }
X	}
X	else
X#endif
X	if (art > lastart || forcegrow)
X	    grow_ctl(getngsize(ng));
X	check_first(art);		/* make sure firstart is still 1st */
X	if (start_command) {		/* fake up an initial command? */
X	    prompt = whatnext;
X	    strcpy(buf,start_command);
X	    free(start_command);
X	    start_command = Nullch;
X#ifdef USETHREADS
X	    p_art = Nullart;
X#endif
X	    art = lastart+1;
X	    goto article_level;
X	}
X	if (art>lastart) {		/* are we off the end still? */
X	    ART_NUM ucount = 0;		/* count of unread articles left */
X
X	    for (i=firstart; i<=lastart; i++)
X		if (!(ctl_read(i)))
X		    ucount++;		/* count the unread articles */
X#ifdef DEBUGGING
X	    /*NOSTRICT*/
X	    if (debug && ((ART_NUM)toread[ng]) != ucount)
X		printf("(toread=%ld sb %ld)",(long)toread[ng],(long)ucount)
X		  FLUSH;
X#endif
X	    /*NOSTRICT*/
X	    toread[ng] = (ART_UNREAD)ucount;	/* this is perhaps pointless */
X	    art = lastart + 1;		/* keep bitmap references sane */
X	    if (art != curr_art) {
X#ifdef USETHREADS
X		recent_p_art = curr_p_art;
X		find_article(art);
X		curr_p_art = p_art;
X#endif
X		recent_art = curr_art;
X					/* remember last article # (for '-') */
X		curr_art = art;      /* remember this article # */
X	    }
X	    if (erase_screen)
X		clear();			/* clear the screen */
X	    else
X		fputs("\n\n",stdout) FLUSH;
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("End of newsgroup %s.",ngname);
X					/* print pseudo-article */
X	    ELSE
X#endif
X#ifdef TERSE
X		printf("End of %s",ngname);
X#endif
X	    if (ucount) {
X#ifdef USETHREADS
X		if (selected_root_cnt)
X		    printf("  (%ld + %ld articles still unread)",
X			(long)selected_count,(long)ucount-selected_count);
X		else
X#endif
X		    printf("  (%ld article%s still unread)",
X			(long)ucount,ucount==1?nullstr:"s");
X	    }
X	    else {
X		if (!forcelast)
X		    goto cleanup;	/* actually exit newsgroup */
X	    }
X	    prompt = whatnext;
X#ifdef ARTSEARCH
X	    srchahead = 0;		/* no more subject search mode */
X#endif
X	    fputs("\n\n",stdout) FLUSH;
X	    skipstate = 0;		/* back to none skipped */
X	}
X	else if (!reread && was_read(art)) {
X					/* has this article been read? */
X#ifdef USETHREADS
X	    follow_thread('n');
X#else
X	    art++;			/* then skip it */
X#endif
X	    continue;
X	}
X	else if
X	  (!reread && !was_read(art)
X#ifdef SERVER
X	    && nntpopen(art,GET_HEADER) == Nullfp) { 
X#else
X	    && artopen(art) == Nullfp) { /* never read it, & cannot find it? */
X	    if (errno != ENOENT) {	/* has it not been deleted? */
X#ifdef VERBOSE
X		IF(verbose)
X		    printf("\n(Article %ld exists but is unreadable.)\n",
X			(long)art) FLUSH;
X		ELSE
X#endif
X#ifdef TERSE
X		    printf("\n(%ld unreadable.)\n",(long)art) FLUSH;
X#endif
X		skipstate = 0;
X		sleep(2);
X	    }
X#endif
X	    switch(skipstate++) {
X	    case 0:
X		clear();
X#ifdef VERBOSE
X		IF(verbose)
X		    fputs("Skipping unavailable article",stdout);
X		ELSE
X#endif
X#ifdef TERSE
X		    fputs("Skipping",stdout);
X#endif
X		for (i = just_a_sec/3; i; --i)
X		    putchar(PC);
X		fflush(stdout);
X		sleep(1);
X		break;
X	    case 1:
X		fputs("..",stdout);
X		fflush(stdout);
X		break;
X	    default:
X		putchar('.');
X		fflush(stdout);
X#ifndef SERVER
X#define READDIR
X#ifdef READDIR
X		{			/* fast skip patch */
X		    ART_NUM newart;
X		    
X		    if (! (newart=getngmin(".",art)))
X			newart = lastart+1;
X		    for (i=art; i<newart; i++)
X			oneless(i);
X		    art = newart - 1;
X		}
X#endif
X#else
X		{
X			char	ser_line[256];
X			ART_NUM	newart;
X
X			put_server("NEXT");
X			if (get_server(ser_line, sizeof (ser_line)) < 0) {
X				fprintf(stderr,
X			"rrn: unexpected close of server socket.\n");
X				finalize(1);
X			}
X			if (ser_line[0] != CHAR_OK)
X				newart = lastart + 1;
X			else
X				newart = atoi(ser_line+4);
X		        for (i=art; i<newart; i++)
X				oneless(i);
X		        art = newart - 1;
X		}
X#endif /* SERVER */
X		break;
X	    }
X	    oneless(art);		/* mark deleted as read */
X#ifdef USETHREADS
X	    count_roots(FALSE);		/* Keep selected_count accurate */
X	    find_article(art);
X	    follow_thread('n');
X#else
X	    art++;			/* try next article */
X#endif
X	    continue;
X	}
X	else {				/* we have a real live article */
X	    skipstate = 0;		/* back to none skipped */
X	    if (art != curr_art) {
X#ifdef USETHREADS
X		recent_p_art = curr_p_art;
X		find_article(art);
X		curr_p_art = p_art;
X#endif
X		recent_art = curr_art;
X					/* remember last article # (for '-') */
X		curr_art = art;      /* remember this article # */
X	    }
X	    if (!do_fseek) {		/* starting at top of article? */
X		artline = 0;		/* start at the beginning */
X		topline = -1;		/* and remember top line of screen */
X					/*  (line # within article file) */
X	    }
X	    clear();			/* clear screen */
X	    if (!artopen(art)) {	/* make sure article is found & open */
X#ifdef USETHREADS
X		char tmpbuf[256];
X		/* see if we have tree data for this article anyway */
X		init_tree();
X		sprintf(tmpbuf,"%s #%ld is not available.",ngname,(long)art);
X		tree_puts(tmpbuf,0,0);
X		vwtary((ART_LINE)0,(ART_POS)0);
X		finish_tree(1);
X		prompt = whatnext;
X#else
X		printf("Article %ld of %s is not available.\n\n",
X		    (long)art,ngname) FLUSH;
X		prompt = whatnext;
X#endif
X#ifdef ARTSEARCH
X		srchahead = 0;
X#endif
X	    }
X	    else {			/* found it, so print it */
X		switch (do_article()) {
X		case DA_CLEAN:		/* quit newsgroup */
X		    goto cleanup;
X		case DA_TOEND:		/* do not mark as read */
X		    goto reask_article; 
X		case DA_RAISE:		/* reparse command at end of art */
X		    goto article_level;
X		case DA_NORM:		/* normal end of article */
X		    break;
X		}
X	    }
X	    if (art >= absfirst)	/* don't mark non-existant articles */
X		mark_as_read();		/* mark current article as read */
X	    reread = FALSE;
X	    do_hiding = TRUE;
X#ifdef ROTATION
X	    rotate = FALSE;
X#endif
X	}
X
X/* if these gotos bother you, think of this as a little state machine */
X
Xreask_article:
X#ifdef MAILCALL
X	setmail();
X#endif
X	setdfltcmd();
X#ifdef CLEAREOL
X	if (erase_screen && can_home_clear)
X	    clear_rest();
X#endif /* CLEAREOL */
X	unflush_output();		/* disable any ^O in effect */
X	standout();			/* enter standout mode */
X	printf(prompt,mailcall,dfltcmd);/* print prompt, whatever it is */
X	un_standout();			/* leave standout mode */
X	putchar(' ');
X	fflush(stdout);
Xreinp_article:
X	eat_typeahead();
X#ifdef PENDING
X	look_ahead();			/* see what we can do in advance */
X	if (!input_pending())
X	    collect_subjects();		/* loads subject cache until */
X					/* input is pending */
X#endif
X	getcmd(buf);
X	if (errno || *buf == '\f') {
X	    if (LINES < 100 && !int_count)
X		*buf = '\f';		/* on CONT fake up refresh */
X	    else {
X		putchar('\n') FLUSH;		/* but only on a crt */
X		goto reask_article;
X	    }
X	}
Xarticle_level:
X
X	/* parse and process article level command */
X
X	switch (art_switch()) {
X	case AS_INP:			/* multichar command rubbed out */
X	    goto reinp_article;
X	case AS_ASK:			/* reprompt "End of article..." */
X	    goto reask_article;
X	case AS_CLEAN:			/* exit newsgroup */
X	    goto cleanup;
X	case AS_NORM:			/* display article art */
X	    break;
X	}
X    }					/* end of article selection loop */
X    
X/* shut down newsgroup */
X
Xcleanup:
X    uud_end();
X#ifdef KILLFILES
X    kill_unwanted(firstart,"\nCleaning up...\n\n",FALSE);
X					/* do cleanup from KILL file, if any */
X#endif
X#ifdef USETHREADS
X    if (ThreadedGroup)
X	unuse_data(0);			/* free article thread data */
X#endif
X    in_ng = FALSE;			/* leave newsgroup state */
X    if (artfp != Nullfp) {		/* article still open? */
X	fclose(artfp);			/* close it */
X	artfp = Nullfp;			/* and tell the world */
X#ifdef SERVER
X        sprintf(artname, "/tmp/rrn%ld.%ld", (long) openart, our_pid);
X        UNLINK(artname);
X#endif /* SERVER */
X	openart = 0;
X    }
X    putchar('\n') FLUSH;
X    yankback();				/* do a Y command */
X    restore_ng();			/* reconstitute .newsrc line */
X    doing_ng = FALSE;			/* tell sig_catcher to cool it */
X    free(ctlarea);			/* return the control area */
X#ifdef CACHESUBJ
X    if (subj_list) {
X	for (i=OFFSET(lastart); i>=0; --i)
X	    if (subj_list[i])
X		free(subj_list[i]);
X#ifndef lint
X	free((char*)subj_list);
X#endif /* lint */
X    }
X#endif
X    write_rc();				/* and update .newsrc */
X    rc_changed = FALSE;			/* tell sig_catcher it is ok */
X    if (chdir(spool)) {
X	printf(nocd,spool) FLUSH;
X	sig_catcher(0);
X    }
X#ifdef KILLFILES
X    if (localkfp) {
X	fclose(localkfp);
X	localkfp = Nullfp;
X    }
X#endif
X    mode = oldmode;
X    return exit_code;
X}					/* Whew! */
X
X/* decide what to do at the end of an article */
X
Xint
Xart_switch()
X{
X    register ART_NUM i;
X      
X    setdef(buf,dfltcmd);
X#ifdef VERIFY
X    printcmd();
X#endif
X
X    switch (*buf) {
X#ifdef USETHREADS
X    case '<':			/* goto previous thread */
X	if (!ThreadedGroup) {
X	    goto group_unthreaded;
X	}
X	prev_root();
X	return AS_NORM;
X    case '>':			/* goto next thread */
X	if (!ThreadedGroup) {
X	    goto group_unthreaded;
X	}
X	next_root();
X	return AS_NORM;
X    case 'U': {			/* unread some articles */
X	char *u_prompt, *u_help_thread;
X
X	if (!ThreadedGroup) {
X	    dfltcmd = "a";
X	    u_help_thread = nullstr;
X#ifdef VERBOSE
X	    IF(verbose)
X		u_prompt = "\nSet unread: all articles? [an] ";
X	    ELSE
X#endif
X#ifdef TERSE
X		u_prompt = "\nUnread? [an] ";
X#endif
X	}
X	else if (!p_art || art > lastart) {
X	    dfltcmd = "+";
X	    u_help_thread = nullstr;
X#ifdef VERBOSE
X	    IF(verbose)
X		u_prompt = "\nSet unread: +select or all? [+an] ";
X	    ELSE
X#endif
X#ifdef TERSE
X		u_prompt = "\nUnread? [+an] ";
X#endif
X	}
X	else {
X	    dfltcmd = "t";
X#ifdef VERBOSE
X	    IF(verbose) {
X		u_prompt = "\n\
XSet unread: thread, subthread, +select, or all? [ts+an] ";
X		u_help_thread = "\
XType t or SP to mark this thread's articles as unread.\n\
XType s to mark the current article and its descendants as unread.\n";
X	    }
X	    ELSE
X#endif
X#ifdef TERSE
X	    {
X		u_prompt = "\nUnread? [ts+an] ";
X		u_help_thread = "\
Xt or SP to mark thread unread.\n\
Xs to mark subthread unread.\n";
X	    }
X#endif
X	}
X      reask_unread:
X	in_char(u_prompt,'u');
X	putchar('\n') FLUSH;
X	setdef(buf,dfltcmd);
X#ifdef VERIFY
X	printcmd();
X#endif
X	if (*buf == 'h') {
X	    fputs(u_help_thread,stdout);
X#ifdef VERBOSE
X	    IF(verbose)
X	    {
X		if (ThreadedGroup)
X		    fputs("\
XType + to enter select thread mode using all the unread articles.\n\
X(The selected threads will be marked as unread and displayed as usual.)\n\
X",stdout) FLUSH;
X		fputs("\
XType a to mark all articles in this group as unread.\n\
XType n to change nothing.\n\
X",stdout) FLUSH;
X	    }
X	    ELSE
X#endif
X#ifdef TERSE
X	    {
X		if (ThreadedGroup)
X		    fputs("\
X+ to select threads from the unread.\n\
X",stdout) FLUSH;
X		fputs("\
Xa to mark all articles unread.\n\
Xn to change nothing.\n\
X",stdout) FLUSH;
X	    }
X#endif
X	    goto reask_unread;
X	}
X	else if (*buf == 'n' || *buf == 'q') {
X	    return AS_ASK;
X	}
X	else if (*buf == 't' && *dfltcmd == 't')
X	    follow_thread('u');
X	else if (*buf == 's' && *dfltcmd == 't') {
X	    follow_thread('U');
X	}
X	else if (*buf == 'a') {
X	    check_first(absfirst);
X	    for (i = absfirst; i <= lastart; i++) {
X		onemore(i);		/* mark as unread */
X	    }
X	    scan_all_roots = FALSE;
X	    count_roots(FALSE);
X	    if (art > lastart) {
X		first_art();
X	    }
X	}
X	else if (ThreadedGroup && *buf == '+') {
X	    *buf = 'U';
X	    goto select_threads;
X	}
X	else {
X	    fputs(hforhelp,stdout) FLUSH;
X	    settle_down();
X	    goto reask_unread;
X	}
X	return AS_NORM;
X    }
X    case '[':			/* goto parent article */
X    case '{':			/* goto thread's root article */
X	if (p_art) {
X	    if (!p_art->parent) {
X		if (p_art == p_articles + p_roots[p_art->root].articles) {
X		    register char *cp = (*buf=='['?"parent":"root");
X#ifdef VERBOSE
X		    IF(verbose)
X			fprintf(stdout,"\n\
XThere is no %s article prior to this one.\n",cp) FLUSH;
X		    ELSE
X#endif
X#ifdef TERSE
X			fprintf(stdout,"\nNo prior %s.\n",cp) FLUSH;
X#endif
X		    return AS_ASK;
X		}
X		*buf = '{';
X		p_art--;
X	    }
X	    else
X		p_art += p_art->parent;
X
X	    if (*buf == '{')
X		while (p_art->parent)
X		    p_art += p_art->parent;
X
X	    art = p_art->num;
X	    reread = TRUE;
X	    return AS_NORM;
X	}
Xnot_threaded:
X	if (ThreadedGroup) {
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs("\nThis article is not threaded.\n",stdout) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("\nUnthreaded article.\n",stdout) FLUSH;
X#endif
X	    return AS_ASK;
X	}
Xgroup_unthreaded:
X#ifdef VERBOSE
X	IF(verbose)
X	    fputs("\nThis group is not threaded.\n",stdout) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    fputs("\nUnthreaded group.\n",stdout) FLUSH;
X#endif
X	return AS_ASK;
X    case ']':			/* goto child article */
X    case '}':			/* goto thread's leaf article */
X	if (p_art) {
X	    if (!(p_art++)->child_cnt) {
X		PACKED_ARTICLE *root_limit = upper_limit(p_art-1,FALSE);
X
X		if (p_art == root_limit) {
X#ifdef VERBOSE
X		    IF(verbose)
X			fputs("\n\
XThis is the last leaf in this tree.\n",stdout) FLUSH;
X		    ELSE
X#endif
X#ifdef TERSE
X			fputs("\nLast leaf.\n",stdout) FLUSH;
X#endif
X		    p_art--;
X		    return AS_ASK;
X		}
X		if (*buf == ']')
X		    *buf = '}';
X		else {
X		    while (++p_art != root_limit && p_art->parent)
X			;
X		    p_art--;
X		    *buf = ' ';
X		}
X	    }
X	    if( *buf == '}' )
X		while (p_art->child_cnt)
X		    p_art++;
X
X	    art = p_art->num;
X	    reread = TRUE;
X	    return AS_NORM;
X	}
X	goto not_threaded;
X    case 'T':
X	if (p_art) {
X	    sprintf(buf,"T%ld\t# %s",(long)p_roots[p_art->root].root_num,
X		subject_ptrs[p_art->subject]);
X	    fputs(buf,stdout);
X	    kf_append(buf);
X	    *buf = 'J';
X	    goto follow_threads;
X	}
X	goto not_threaded;
X    case 'K':
X	if (p_art) {
X	    /* first, write kill-subject command */
X	    (void)art_search(buf, (sizeof buf), TRUE);
X	    art = curr_art;
X	    p_art = curr_p_art;
X	    *buf = 'k';			/* then take care of any prior subjs */
X	    goto follow_threads;
X	}
X	goto normal_search;
X    case ',':		/* kill this node and all descendants */
X	mark_as_read();
X	*buf = 'K';
X    case 'k':		/* kill current subject # (e.g. [1]) */
X    case 'J':		/* Junk all nodes in this thread */
X	if (!ThreadedGroup) {
X	    *buf = 'k';
X	    goto normal_search;
X	}
Xfollow_threads:
X	follow_thread(*buf);
X	if (!reread && !toread[ng])
X	    return AS_CLEAN;
X	if (!reread && selected_root_cnt && !selected_count)
X	    goto select_threads;
X	return AS_NORM;
X    case 't':
X	carriage_return();
X#ifndef CLEAREOL
X	erase_eol();		/* erase the prompt */
X#else
X	if (erase_screen && can_home_clear)
X	    clear_rest();
X	else
X	    erase_eol();	/* erase the prompt */
X#endif /* CLEAREOL */
X	fflush(stdout);
X	page_line = 1;
X	p_art = curr_p_art;
X	entire_tree();
X	return AS_ASK;
X    case ':':			/* execute command on selected articles */
X	if (!ThreadedGroup) {
X	    goto group_unthreaded;
X	}
X	page_line = 1;
X	if (!use_selected())
X	    return AS_INP;
X	putchar('\n');
X	art = curr_art;
X	p_art = curr_p_art;
X	return AS_ASK;
X#endif /* USETHREADS */
X    case 'p':			/* find previous unread article */
X#ifdef USETHREADS
X	if (ThreadedGroup) {
X	    goto backtrack_threads;
X	}
X#endif
X	do {
X	    if (art <= firstart)
X		break;
X	    art--;
X#ifdef SERVER
X	} while (was_read(art) || nntpopen(art,GET_HEADER) == Nullfp);
X#else
X	} while (was_read(art) || artopen(art) == Nullfp);
X#endif
X#ifdef ARTSEARCH
X	srchahead = 0;
X#endif
X	return AS_NORM;
X    case 'P':		/* goto previous article */
X#ifdef USETHREADS
X	if (ThreadedGroup) {
Xbacktrack_threads:
X	    backtrack_thread( *buf );
X	    art++;		/* prepare for art-- below */
X	}
X#endif
X	if (art > absfirst)
X	    art--;
X	else {
X#ifdef VERBOSE
X	    IF(verbose)
X		fprintf(stdout,"\n\
XThere are no%s articles prior to this one.\n\
X",*buf=='P'?nullstr:" unread") FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fprintf(stdout,"\n\
XNo previous%s articles\n\
X",*buf=='P'?nullstr:" unread") FLUSH;
X#endif
X	    art = curr_art;
X#ifdef USETHREADS
X	    p_art = curr_p_art;
X#endif
X	    return AS_ASK;
X	}
X	reread = TRUE;
X#ifdef ARTSEARCH
X	srchahead = 0;
X#endif
X	return AS_NORM;
X    case '-':
X	if (recent_art >= 0) {
X#ifdef USETHREADS
X	    p_art = recent_p_art;
X#endif
X	    art = recent_art;
X	    reread = TRUE;
X#ifdef ARTSEARCH
X	    srchahead = -(srchahead != 0);
X#endif
X	    return AS_NORM;
X	}
X	else {
X	    exit_code = NG_MINUS;
X	    return AS_CLEAN;
X	}
X    case 'n':		/* find next unread article? */
X#ifdef USETHREADS
X	if (ThreadedGroup) {
X	    goto follow_threads;
X	}
X#endif
X	if (art > lastart) {
X	    if (!toread[ng])
X		return AS_CLEAN;
X	    art = firstart;
X	}
X#ifdef ARTSEARCH
X	else if (scanon && srchahead) {
X	    *buf = Ctl('n');
X	    goto normal_search;
X	}
X#endif
X	else
X	    art++;
X
X#ifdef ARTSEARCH
X	srchahead = 0;
X#endif
X	return AS_NORM;
X    case 'N':			/* goto next article */
X#ifdef USETHREADS
X	if (ThreadedGroup) {
X	    goto follow_threads;
X	}
X#endif
X	if (art > lastart)
X	    art = absfirst;
X	else
X	    art++;
X	if (art <= lastart)
X	    reread = TRUE;
X#ifdef ARTSEARCH
X	srchahead = 0;
X#endif
X	return AS_NORM;
X    case '$':
X	art = lastart+1;
X	forcelast = TRUE;
X#ifdef USETHREADS
X	p_art = Nullart;
X#endif
X#ifdef ARTSEARCH
X	srchahead = 0;
X#endif
X	return AS_NORM;
X    case '1': case '2': case '3':	/* goto specified article */
X    case '4': case '5': case '6':	/* or do something with a range */
X    case '7': case '8': case '9': case '.':
X	forcelast = TRUE;
X	switch (numnum()) {
X	case NN_INP:
X	    return AS_INP;
X	case NN_ASK:
X	    return AS_ASK;
X	case NN_REREAD:
X	    reread = TRUE;
X#ifdef ARTSEARCH
X	    if (srchahead)
X		srchahead = -1;
X#endif
X	    break;
X	case NN_NORM:
X	    if (was_read(art)) {
X#ifdef USETHREADS
X		first_art();
X#else
X		art = firstart;
X#endif
X		pad(just_a_sec/3);
X	    }
X	    else {
X		putchar('\n');
X		return AS_ASK;
X	    }
X	    break;
X	}
X	return AS_NORM;
X    case Ctl('k'):
X	edit_kfile();
X	return AS_ASK;
X#ifndef USETHREADS
X    case 'K':
X    case 'k':
X#endif
X    case Ctl('n'):	/* search for next article with same subject */
X#ifdef USETHREADS
X	if (ThreadedGroup) {
X	    goto follow_threads;
X	}
X#endif
X    case Ctl('p'):	/* search for previous article with same subject */
X#ifdef USETHREADS
X	if (ThreadedGroup) {
X	    goto backtrack_threads;
X	}
X#endif
X    case '/': case '?':
Xnormal_search:
X#ifdef ARTSEARCH
X    {		/* search for article by pattern */
X	char cmd = *buf;
X	
X	reread = TRUE;		/* assume this */
X	page_line = 1;
X	switch (art_search(buf, (sizeof buf), TRUE)) {
X	case SRCH_ERROR:
X	    art = curr_art;
X	    return AS_ASK;
X	case SRCH_ABORT:
X	    art = curr_art;
X	    return AS_INP;
X	case SRCH_INTR:
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("\n(Interrupted at article %ld)\n",(long)art) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		printf("\n(Intr at %ld)\n",(long)art) FLUSH;
X#endif
X	    art = curr_art;
X			    /* restore to current article */
X	    return AS_ASK;
X	case SRCH_DONE:
X	    fputs("done\n",stdout) FLUSH;
X	    pad(just_a_sec/3);	/* 1/3 second */
X	    if (!srchahead) {
X		art = curr_art;
X		return AS_ASK;
X	    }
X#ifdef USETHREADS
X	    first_art();
X#else
X	    art = firstart;
X#endif
X	    reread = FALSE;
X	    return AS_NORM;
X	case SRCH_SUBJDONE:
X#ifdef UNDEF
X	    fputs("\n\n\n\nSubject not found.\n",stdout) FLUSH;
X	    pad(just_a_sec/3);	/* 1/3 second */
X#endif
X#ifdef USETHREADS
X	    first_art();
X#else
X	    art = firstart;
X#endif
X	    reread = FALSE;
X	    return AS_NORM;
X	case SRCH_NOTFOUND:
X	    fputs("\n\n\n\nNot found.\n",stdout) FLUSH;
X	    art = curr_art;  /* restore to current article */
X	    return AS_ASK;
X	case SRCH_FOUND:
X	    if (cmd == Ctl('n') || cmd == Ctl('p'))
X		oldsubject = TRUE;
X	    break;
X	}
X	return AS_NORM;
X    }
X#else
X    buf[1] = '\0';
X    notincl(buf);
X    return AS_ASK;
X#endif
X    case 'u':			/* unsubscribe from this newsgroup? */
X	rcchar[ng] = NEGCHAR;
X	return AS_CLEAN;
X    case 'M':
X#ifdef DELAYMARK
X	if (art <= lastart) {
X	    delay_unmark(art);
X	    printf("\nArticle %ld will return.\n",(long)art) FLUSH;
X	}
X#else
X	notincl("M");
X#endif
X	return AS_ASK;
X    case 'm':
X	if (art <= lastart) {
X	    unmark_as_read();
X	    printf("\nArticle %ld marked as still unread.\n",(long)art) FLUSH;
X	}
X	return AS_ASK;
X    case 'c':			/* catch up */
X      reask_catchup:
X#ifdef VERBOSE
X	IF(verbose)
X	    in_char("\nDo you really want to mark everything as read? [yn] ",
X		'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#ifdef VERIFY
X	printcmd();
X#endif
X	if (*buf == 'h') {
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs("\
XType y or SP to mark all articles as read.\n\
XType n to leave articles marked as they are.\n\
XType u to mark everything read and unsubscribe.\n\
X",stdout) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("\
Xy or SP to mark all read.\n\
Xn to forget it.\n\
Xu to mark all and unsubscribe.\n\
X",stdout) FLUSH;
X#endif
X	    goto reask_catchup;
X	}
X	else if (*buf == 'n' || *buf == 'q') {
X	    return AS_ASK;
X	}
X	else if (*buf != 'y' && *buf != 'u') {
X	    fputs(hforhelp,stdout) FLUSH;
X	    settle_down();
X	    goto reask_catchup;
X	}
X	for (i = firstart; i <= lastart; i++) {
X	    oneless(i);		/* mark as read */
X	}
X#ifdef USETHREADS
X	selected_root_cnt = selected_count = 0;
X#endif
X#ifdef DELAYMARK
X	if (dmfp)
X	    yankback();
X#endif
X	if (*buf == 'u') {
X	    rcchar[ng] = NEGCHAR;
X	    return AS_CLEAN;
X	}
X#ifdef USETHREADS
X	p_art = Nullart;
X	selected_count = 0;
X#endif
X	art = lastart+1;
X	forcelast = FALSE;
X	return AS_NORM;
X    case 'Q':
X	exit_code = NG_ASK;
X	/* FALL THROUGH */
X    case 'q':			/* go back up to newsgroup level? */
X	return AS_CLEAN;
X    case 'j':
X	putchar('\n') FLUSH;
X	if (art <= lastart)
X	    mark_as_read();
X	return AS_ASK;
X    case 'h': {			/* help? */
X	int cmd;
X
X	if ((cmd = help_art()) > 0)
X	    pushchar(cmd);
X	return AS_ASK;
X    }
X    case '&':
X	if (switcheroo()) /* get rest of command */
X	    return AS_INP;	/* if rubbed out, try something else */
X	return AS_ASK;
X    case '#':
X#ifdef VERBOSE
X	IF(verbose)
X	    printf("\nThe last article is %ld.\n",(long)lastart) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    printf("\n%ld\n",(long)lastart) FLUSH;
X#endif
X	return AS_ASK;
X#ifdef USETHREADS
X    case '+':			/* enter thread selection mode */
X	if (ThreadedGroup) {
Xselect_threads:
X	    *buf = select_thread(*buf);
X	    if (*buf == 'q') {
X		putchar( '\n' ) FLUSH;
X		return AS_ASK;
X	    }
X	    if (*buf == 'Q') {
X		exit_code = NG_ASK;
X		return AS_CLEAN;
X	    }
X	    if (*buf == 'N' || !toread[ng])
X		return AS_CLEAN;
X	    return AS_NORM;
X	}
X	/* FALLTHROUGH */
X#endif
X    case '=': {			/* list subjects */
X	char tmpbuf[256];
X	ART_NUM oldart = art;
X	int cmd;
X	char *subjline = getval("SUBJLINE",Nullch);
X#ifndef CACHESUBJ
X	char *s;
X#endif
X
X	page_init();
X#ifdef CACHESUBJ
X	if (!subj_list)
X	    fetchsubj(art,TRUE,FALSE);
X#endif
X	for (i=firstart; i<=lastart && !int_count; i++) {
X#ifdef CACHESUBJ
X	    if (!was_read(i) &&
X	      (subj_list[OFFSET(i)] != Nullch || fetchsubj(i,FALSE,FALSE)) &&
X	      *subj_list[OFFSET(i)] ) {
X		sprintf(tmpbuf,"%5ld ", i);
X		if (subjline) {
X		    art = i;
X		    interp(tmpbuf + 6, (sizeof tmpbuf) - 6, subjline);
X		}
X		else
X		    safecpy(tmpbuf + 6, subj_list[OFFSET(i)],
X			(sizeof tmpbuf) - 6);
X		if (cmd = print_lines(tmpbuf,NOMARKING)) {
X		    if (cmd > 0)
X			pushchar(cmd);
X		    break;
X		}
X	    }
X#else
X	    if (!was_read(i) && (s = fetchsubj(i,FALSE,FALSE)) && *s) {
X		sprintf(tmpbuf,"%5ld ", i);
X		if (subjline) {	/* probably fetches it again! */
X		    art = i;
X		    interp(tmpbuf + 6, (sizeof tmpbuf) - 6, subjline);
X		}
X		else
X		    safecpy(tmpbuf + 6, s, (sizeof tmpbuf) - 6);
X		if (cmd = print_lines(tmpbuf,NOMARKING)) {
X		    if (cmd > 0)
X			pushchar(cmd);
X		    break;
X		}
X	    }
X#endif
X	}
X	int_count = 0;
X	art = oldart;
X	return AS_ASK;
X    }
X    case '^':
X#ifdef USETHREADS
X	first_art();
X#else
X	art = firstart;
X#endif
X#ifdef ARTSEARCH
X	srchahead = 0;
X#endif
X	return AS_NORM;
X#if defined(CACHESUBJ) && defined(DEBUGGING)
X    case 'D':
X	printf("\nFirst article: %ld\n",(long)firstart) FLUSH;
X	if (!subj_list)
X	    fetchsubj(art,TRUE,FALSE);
X	if (subj_list != Null(char **)) {
X	    for (i=1; i<=lastart && !int_count; i++) {
X		if (subj_list[OFFSET(i)])
X		    printf("%5ld %c %s\n",
X			i, (was_read(i)?'y':'n'), subj_list[OFFSET(i)]) FLUSH;
X	    }
X	}
X	int_count = 0;
X	return AS_ASK;
X#endif
X    case 'v':
X	if (art <= lastart) {
X	    reread = TRUE;
X	    do_hiding = FALSE;
X	}
X	return AS_NORM;
X#ifdef ROTATION
X    case Ctl('x'):
X#endif
X    case Ctl('r'):
X#ifdef ROTATION
X	rotate = (*buf==Ctl('x'));
X#endif
X	if (art <= lastart)
X	    reread = TRUE;
X	return AS_NORM;
X#ifdef ROTATION
X    case 'X':
X	rotate = !rotate;
X	/* FALL THROUGH */
X#else
X    case Ctl('x'):
X    case 'x':
X    case 'X':
X	notincl("x");
X	return AS_ASK;
X#endif
X    case 'l': case Ctl('l'):		/* refresh screen */
X	if (art <= lastart) {
X	    reread = TRUE;
X	    clear();
X	    do_fseek = TRUE;
X	    artline = topline;
X	    if (artline < 0)
X		artline = 0;
X	}
X	return AS_NORM;
X    case 'b': case Ctl('b'):		/* back up a page */
X	if (art <= lastart) {
X	    ART_LINE target;
X
X	    reread = TRUE;
X	    clear();
X	    do_fseek = TRUE;
X	    target = topline - (LINES - 2);
X	    artline = topline;
X	    if (artline >= 0) do {
X		artline--;
X	    } while(artline >= 0 && artline > target && vrdary(artline-1) >= 0);
X	    topline = artline;
X	    if (artline < 0)
X		artline = 0;
X	}
X	return AS_NORM;
X    case '!':			/* shell escape */
X	if (escapade())
X	    return AS_INP;
X	return AS_ASK;
X    case 'C': {
X	cancel_article();
X	return AS_ASK;
X    }
X    case 'R':
X    case 'r': {			/* reply? */
X	reply();
X	return AS_ASK;
X    }
X    case 'F':
X    case 'f': {			/* followup command */
X	followup();
X	forcegrow = TRUE;		/* recalculate lastart */
X	return AS_ASK;
X    }
X    case '|':
X    case 'w': case 'W':
X    case 's': case 'S':		/* save command */
X    case 'e':			/* extract command */
X	if (save_article() == SAVE_ABORT)
X	    return AS_INP;
X	int_count = 0;
X	return AS_ASK;
X    case 'E':
X	if (uu_out != Nullfp) {
X	    uud_end();
X	}
X	return AS_ASK;
X#ifdef DELAYMARK
X    case 'Y':				/* yank back M articles */
X	yankback();
X#ifdef USETHREADS
X	first_art();
X#else
X	art = firstart;			/* from the beginning */
X#endif
X	return AS_NORM;			/* pretend nothing happened */
X#endif
X#ifdef STRICTCR
X    case '\n':
X	fputs(badcr,stdout) FLUSH;
X	return AS_ASK;
X#endif
X    default:
X	printf("\n%s",hforhelp) FLUSH;
X	settle_down();
X	return AS_ASK;
X    }
X}
X
X#ifdef MAILCALL
X/* see if there is any mail */
X
Xvoid
Xsetmail()
X{
X    if (! (mailcount++)) {
X	char *mailfile = filexp(getval("MAILFILE",MAILFILE));
X	
X	if (stat(mailfile,&filestat) < 0 || !filestat.st_size
X	    || filestat.st_atime > filestat.st_mtime)
X	    mailcall = nullstr;
X	else
X	    mailcall = getval("MAILCALL","(Mail) ");
X    }
X    mailcount %= 10;			/* check every 10 articles */
X}
X#endif
X
Xvoid
Xsetdfltcmd()
X{
X#ifdef USETHREADS
X    if (toread[ng] == unthreaded) {
X#else
X    if (!toread[ng]) {
X#endif
X	if (art > lastart)
X	    dfltcmd = "qnp";
X	else
X	    dfltcmd = "npq";
X    }
X    else {
X#ifdef USETHREADS
X	if (selected_root_cnt && !selected_count)
X	    dfltcmd = "+npq";
X	else
X# ifdef ARTSEARCH
X	if (!ThreadedGroup && srchahead)
X	    dfltcmd = "^Nnpq";
X	else
X# endif
X#else
X# ifdef ARTSEARCH
X	if (srchahead)
X	    dfltcmd = "^Nnpq";
X	else
X# endif
X#endif
X	    dfltcmd = "npq";
X	}
X}
END_OF_FILE
  if test 34937 -ne `wc -c <'ng.c'`; then
    echo shar: \"'ng.c'\" unpacked with wrong size!
  fi
  # end of 'ng.c'
fi
if test -f 'rcstuff.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rcstuff.c'\"
else
  echo shar: Extracting \"'rcstuff.c'\" \(23291 characters\)
  sed "s/^X//" >'rcstuff.c' <<'END_OF_FILE'
X/* $Header: rcstuff.c,v 4.3.3.2 90/08/20 16:47:44 davison Trn $
X *
X * $Log:	rcstuff.c,v $
X * Revision 4.3.3.2  90/08/20  16:47:44  davison
X * Removed ngmax array.
X * 
X * Revision 4.3.3.1  90/06/20  22:39:28  davison
X * Initial Trn Release
X * 
X * Revision 4.3.2.5  90/05/04  00:44:07  sob
X * Fixes to add_newsgroup() from lar at usl.edu.
X * 
X * Revision 4.3.2.4  90/04/23  00:25:45  sob
X * Changed atoi to atol.
X * 
X * Revision 4.3.2.3  89/12/20  23:25:04  sob
X * Changed the maximum lenght of a newsgroup name from 20 to 40 characters.
X * 
X * Revision 4.3.2.2  89/11/26  18:22:26  sob
X * Added changes to addnewgroup() to cause rn to ask once and only once
X * to add a new group to .newsrc. 
X * Fix provided by Fletcher Mattox <fletcher at cs.utexas.edu>
X * 
X * Revision 4.3.2.1  89/11/06  00:58:29  sob
X * Added RRN support from NNTP 1.5
X * 
X * Revision 4.3.1.5  86/07/24  14:09:10  lwall
X * Removed check for spool directory existence in get_ng.
X * 
X * Revision 4.3.1.4  85/09/10  11:04:44  lwall
X * Improved %m in in_char().
X * 
X * Revision 4.3.1.3  85/05/29  09:13:25  lwall
X * %d that should be %ld.
X * 
X * Revision 4.3.1.2  85/05/17  11:40:08  lwall
X * Sped up "rn -c" by not mallocing unnecessarily.
X * 
X * Revision 4.3.1.1  85/05/10  11:37:18  lwall
X * Branch for patches.
X * 
X * Revision 4.3  85/05/01  11:45:56  lwall
X * Baseline for release with 4.3bsd.
X * 
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "util.h"
X#include "ngdata.h"
X#include "term.h"
X#include "final.h"
X#include "rn.h"
X#include "intrp.h"
X#include "only.h"
X#include "rcln.h"
X#ifdef SERVER
X#include "server.h"
X#endif
X#include "INTERN.h"
X#include "rcstuff.h"
X
Xchar *rcname INIT(Nullch);		/* path name of .newsrc file */
Xchar *rctname INIT(Nullch);		/* path name of temp .newsrc file */
Xchar *rcbname INIT(Nullch);		/* path name of backup .newsrc file */
Xchar *softname INIT(Nullch);		/* path name of .rnsoft file */
XFILE *rcfp INIT(Nullfp);			/* .newsrc file pointer */
X
X#ifdef HASHNG
X    short hashtbl[HASHSIZ];
X#endif
X
Xbool
Xrcstuff_init()
X{
X    register NG_NUM newng;
X    register char *s;
X    register int i;
X    register bool foundany = FALSE;
X    char *some_buf;
X    long length;
X#ifdef SERVER
X    char *cp;
X#endif /* SERVER */
X
X#ifdef HASHNG
X    for (i=0; i<HASHSIZ; i++)
X	hashtbl[i] = -1;
X#endif
X
X    /* make filenames */
X
X#ifdef SERVER
X
X    if (cp = getenv("NEWSRC"))
X	rcname = savestr(filexp(cp));
X    else
X	rcname = savestr(filexp(RCNAME));
X
X#else /* not SERVER */
X
X    rcname = savestr(filexp(RCNAME));
X
X#endif /* SERVER */
X
X    rctname = savestr(filexp(RCTNAME));
X    rcbname = savestr(filexp(RCBNAME));
X    softname = savestr(filexp(SOFTNAME));
X    
X    /* make sure the .newsrc file exists */
X
X    newsrc_check();
X
X    /* open .rnsoft file containing soft ptrs to active file */
X
X    tmpfp = fopen(softname,"r");
X    if (tmpfp == Nullfp)
X	writesoft = TRUE;
X
X    /* read in the .newsrc file */
X
X    for (nextrcline = 0;
X	(some_buf = get_a_line(buf,LBUFLEN,rcfp)) != Nullch;
X	nextrcline++) {
X					/* for each line in .newsrc */
X	char tmpbuf[10];
X
X	newng = nextrcline;		/* get it into a register */
X	length = len_last_line_got;	/* side effect of get_a_line */
X	if (length <= 1) {		/* only a newline??? */
X	    nextrcline--;		/* compensate for loop increment */
X	    continue;
X	}
X	if (newng >= MAXRCLINE) {	/* check for overflow */
X	    fputs("Too many lines in .newsrc\n",stdout) FLUSH;
X	    finalize(1);
X	}
X	if (tmpfp != Nullfp && fgets(tmpbuf,10,tmpfp) != Nullch)
X	    softptr[newng] = atol(tmpbuf);
X	else
X	    softptr[newng] = 0;
X	some_buf[--length] = '\0';	/* wipe out newline */
X	if (checkflag)			/* no extra mallocs for -c */
X	    rcline[newng] = some_buf;
X	else if (some_buf == buf) {
X	    rcline[newng] = savestr(some_buf);
X					/* make a semipermanent copy */
X	}
X	else {
X	    /*NOSTRICT*/
X#ifndef lint
X	    some_buf = saferealloc(some_buf,(MEM_SIZE)(length+1));
X#endif /* lint */
X	    rcline[newng] = some_buf;
X	}
X#ifdef NOTDEF
X	if (strnEQ(some_buf,"to.",3)) {	/* is this a non-newsgroup? */
X	    nextrcline--;		/* destroy this line */
X	    continue;
X	}
X#endif
X	if (*some_buf == ' ' ||
X	  *some_buf == '\t' ||
X	  strnEQ(some_buf,"options",7)) {		/* non-useful line? */
X	    toread[newng] = TR_JUNK;
X	    rcchar[newng] = ' ';
X	    rcnums[newng] = 0;
X	    continue;
X	}
X	for (s = rcline[newng]; *s && *s != ':' && *s != NEGCHAR; s++) ;
X	if (!*s && !checkflag) {
X#ifndef lint
X	    rcline[newng] = saferealloc(rcline[newng],(MEM_SIZE)length+2);
X#endif /* lint */
X	    s = rcline[newng] + length;
X	    *s = ':';
X	    *(s+1) = '\0';
X	}
X	rcchar[newng] = *s;		/* salt away the : or ! */
X	rcnums[newng] = (char)(s - rcline[newng]); 
X	rcnums[newng]++;		/* remember where it was */
X	*s = '\0';			/* null terminate newsgroup name */
X#ifdef HASHNG
X	if (!checkflag)
X	    sethash(newng);
X#endif
X	if (rcchar[newng] == NEGCHAR) {
X	    toread[newng] = TR_UNSUB;
X	    continue;
X	}
X
X	/* now find out how much there is to read */
X
X	if (!inlist(buf) || (suppress_cn && foundany && !paranoid))
X	    toread[newng] = TR_NONE;	/* no need to calculate now */
X	else
X	    set_toread(newng);
X#ifdef VERBOSE
X	if (!checkflag && softmisses == 1) {
X	    softmisses++;		/* lie a little */
X	    fputs("(Revising soft pointers--be patient.)\n",stdout) FLUSH;
X	}
X#endif
X	if (toread[newng] > TR_NONE) {	/* anything unread? */
X	    if (!foundany) {
X		starthere = newng;
X		foundany = TRUE;	/* remember that fact*/
X	    }
X	    if (suppress_cn) {		/* if no listing desired */
X		if (checkflag) {	/* if that is all they wanted */
X		    finalize(1);	/* then bomb out */
X		}
X	    }
X	    else {
X#ifdef VERBOSE
X		IF(verbose)
X		    printf("Unread news in %-40s %5ld article%s\n",
X			rcline[newng],(long)toread[newng],
X			toread[newng]==TR_ONE ? nullstr : "s") FLUSH;
X		ELSE
X#endif
X#ifdef TERSE
X		    printf("%s: %ld article%s\n",
X			rcline[newng],(long)toread[newng],
X			toread[newng]==TR_ONE ? nullstr : "s") FLUSH;
X#endif
X		if (int_count) {
X		    countdown = 1;
X		    int_count = 0;
X		}
X		if (countdown) {
X		    if (! --countdown) {
X			fputs("etc.\n",stdout) FLUSH;
X			if (checkflag)
X			    finalize(1);
X			suppress_cn = TRUE;
X		    }
X		}
X	    }
X	}
X    }
X    fclose(rcfp);			/* close .newsrc */
X    if (tmpfp != Nullfp)
X	fclose(tmpfp);			/* close .rnsoft */
X    if (checkflag) {			/* were we just checking? */
X	finalize(foundany);		/* tell them what we found */
X    }
X    if (paranoid)
X	cleanup_rc();
X
X#ifdef DEBUGGING
X    if (debug & DEB_HASH) {
X	page_init();
X	for (i=0; i<HASHSIZ; i++) {
X	    sprintf(buf,"%d	%d",i,hashtbl[i]);
X	    print_lines(buf,NOMARKING);
X	}
X    }
X#endif
X
X    return foundany;
X}
X
X/* try to find or add an explicitly specified newsgroup */
X/* returns TRUE if found or added, FALSE if not. */
X/* assumes that we are chdir'ed to SPOOL */
X
X#ifdef SERVER
Xstatic int addnewbydefault = 0;
X#endif /* SERVER */
X
Xbool
Xget_ng(what,do_reloc)
Xchar *what;
Xbool do_reloc;
X{
X    char *ntoforget;
X    char promptbuf[128];
X#ifdef SERVER
X    char ser_line[256];
X#endif /* SERVER */
X
X#ifdef VERBOSE
X    IF(verbose)
X	ntoforget = "Type n to forget about this newsgroup.\n";
X    ELSE
X#endif
X#ifdef TERSE
X	ntoforget = "n to forget it.\n";
X#endif
X    if (index(what,'/')) {
X	dingaling();
X	printf("\nBad newsgroup name.\n") FLUSH;
X	return FALSE;
X    }
X    set_ngname(what);
X    ng = find_ng(ngname);
X    if (ng == nextrcline) {		/* not in .newsrc? */
X
X#ifdef SERVER
X	sprintf(ser_line, "GROUP %s", ngname);
X	put_server(ser_line);
X	if (get_server(ser_line, sizeof(ser_line)) < 0) {
X	    fprintf(stderr, "rrn: Unexpected close of server socket.\n");
X	    finalize(1);
X	}
X	if (*ser_line != CHAR_OK) {
X	    if (atoi(ser_line) != ERR_NOGROUP) {
X		fprintf(stderr, "Server response to GROUP %s:\n%s\n",
X		    ngname, ser_line);
X	    }
X#else /* not SERVER */
X
X	if ((softptr[ng] = findact(buf,ngname,strlen(ngname),0L)) < 0 ) {
X
X#endif /* SERVER */
X
X	    dingaling();
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("\nNewsgroup %s does not exist!\n",ngname) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		printf("\nNo %s!\n",ngname) FLUSH;
X#endif
X	    sleep(2);
X	    return FALSE;
X	}
X#ifdef SERVER
X	if (addnewbydefault) {
X		printf("(Adding %s to end of your .newsrc)\n", ngname);
X	        ng = add_newsgroup(ngname, ':');
X	        do_reloc = FALSE;
X	} else {
X#endif /* SERVER */
X#ifdef VERBOSE
X	IF(verbose)
X	    sprintf(promptbuf,"\nNewsgroup %s not in .newsrc--add? [yn] ",ngname);
X	ELSE
X#endif
X#ifdef TERSE
X	    sprintf(promptbuf,"\nAdd %s? [yn] ",ngname);
X#endif
Xreask_add:
X	in_char(promptbuf,'A');
X	putchar('\n') FLUSH;
X	setdef(buf,"y");
X#ifdef VERIFY
X	printcmd();
X#endif
X	if (*buf == 'h') {
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("Type y or SP to add %s to your .newsrc.\n", ngname)
X		  FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("y or SP to add\n",stdout) FLUSH;
X#endif
X	    fputs(ntoforget,stdout) FLUSH;
X	    goto reask_add;
X	}
X	else if (*buf == 'n' || *buf == 'q') {
X	    ng = add_newsgroup(ngname, '!');
X	    return FALSE;
X	}
X	else if (*buf == 'y') {
X	    ng = add_newsgroup(ngname, ':');
X	    do_reloc = FALSE;
X	}
X#ifdef SERVER
X	else if (*buf == 'Y') {
X	    fputs(
X	"(I'll add all new newsgroups to the end of your .newsrc.)\n", stdout);
X	    addnewbydefault = 1;
X	    printf("(Adding %s to end of your .newsrc)\n", ngname);
X	    ng = add_newsgroup(ngname, ':');
X	    do_reloc = FALSE;
X	}
X#endif /* SERVER */
X	else {
X	    fputs(hforhelp,stdout) FLUSH;
X	    settle_down();
X	    goto reask_add;
X	}
X#ifdef SERVER
X      }
X#endif /* SERVER */
X    }
X    else if (rcchar[ng] == NEGCHAR) {	/* unsubscribed? */
X#ifdef VERBOSE
X	IF(verbose)
X	    sprintf(promptbuf,
X"\nNewsgroup %s is currently unsubscribed to--resubscribe? [yn] ",ngname)
X  FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    sprintf(promptbuf,"\n%s unsubscribed--resubscribe? [yn] ",ngname)
X	      FLUSH;
X#endif
Xreask_unsub:
X	in_char(promptbuf,'R');
X	putchar('\n') FLUSH;
X	setdef(buf,"y");
X#ifdef VERIFY
X	printcmd();
X#endif
X	if (*buf == 'h') {
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("Type y or SP to resubscribe to %s.\n", ngname) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("y or SP to resubscribe.\n",stdout) FLUSH;
X#endif
X	    fputs(ntoforget,stdout) FLUSH;
X	    goto reask_unsub;
X	}
X	else if (*buf == 'n' || *buf == 'q') {
X	    return FALSE;
X	}
X	else if (*buf == 'y') {
X	    rcchar[ng] = ':';
X	}
X	else {
X	    fputs(hforhelp,stdout) FLUSH;
X	    settle_down();
X	    goto reask_unsub;
X	}
X    }
X
X    /* now calculate how many unread articles in newsgroup */
X
X    set_toread(ng);
X#ifdef RELOCATE
X    if (do_reloc)
X	ng = relocate_newsgroup(ng,-1);
X#endif
X    return toread[ng] >= TR_NONE;
X}
X
X/* add a newsgroup to the .newsrc file (eventually) */
X
XNG_NUM
Xadd_newsgroup(ngn, c)
Xchar *ngn, c;
X{
X    register NG_NUM newng = nextrcline++;
X					/* increment max rcline index */
X    
X    rcnums[newng] = strlen(ngn) + 1;
X    rcline[newng] = safemalloc((MEM_SIZE)(rcnums[newng] + 1));
X    strcpy(rcline[newng],ngn);		/* and copy over the name */
X    *(rcline[newng] + rcnums[newng]) = '\0';
X    rcchar[newng] = c;			/* subscribe or unsubscribe */
X    toread[newng] = TR_NONE;	/* just for prettiness */
X#ifdef HASHNG
X    sethash(newng);			/* so we can find it again */
X#endif
X#ifdef RELOCATE
X    return c=='!' ? newng : relocate_newsgroup(newng,-1);
X#else
X    return newng;
X#endif
X}
X
X#ifdef RELOCATE
XNG_NUM
Xrelocate_newsgroup(ngx,newng)
XNG_NUM ngx;
XNG_NUM newng;
X{
X    char *dflt = (ngx!=current_ng ? "$^.L" : "$^L");
X    char *tmprcline;
X    ART_UNREAD tmptoread;
X    char tmprcchar;
X    char tmprcnums;
X    ACT_POS tmpsoftptr;
X    register NG_NUM i;
X#if defined(DEBUGGING) && !defined(USETHREADS)
X    ART_NUM tmpngmax;
X#endif
X#ifdef CACHEFIRST
X    ART_NUM tmpabs1st;
X#endif
X    
X    starthere = 0;                      /* Disable this optimization */
X    writesoft = TRUE;			/* Update soft pointer file */
X    if (ngx < nextrcline-1) {
X#ifdef HASHNG
X	for (i=0; i<HASHSIZ; i++) {
X	    if (hashtbl[i] > ngx)
X		--hashtbl[i];
X	    else if (hashtbl[i] == ngx)
X		hashtbl[i] = nextrcline-1;
X	}
X#endif
X	tmprcline = rcline[ngx];
X	tmptoread = toread[ngx];
X	tmprcchar = rcchar[ngx];
X	tmprcnums = rcnums[ngx];
X	tmpsoftptr = softptr[ngx];
X#if defined(DEBUGGING) && !defined(USETHREADS)
X	tmpngmax = ngmax[ngx];
X#endif
X#ifdef CACHEFIRST
X	tmpabs1st = abs1st[ngx];
X#endif
X	for (i=ngx+1; i<nextrcline; i++) {
X	    rcline[i-1] = rcline[i];
X	    toread[i-1] = toread[i];
X	    rcchar[i-1] = rcchar[i];
X	    rcnums[i-1] = rcnums[i];
X	    softptr[i-1] = softptr[i];
X#if defined(DEBUGGING) && !defined(USETHREADS)
X	    ngmax[i-1] = ngmax[i];
X#endif
X#ifdef CACHEFIRST
X	    abs1st[i-1] = abs1st[i];
X#endif
X	}
X	rcline[nextrcline-1] = tmprcline;
X	toread[nextrcline-1] = tmptoread;
X	rcchar[nextrcline-1] = tmprcchar;
X	rcnums[nextrcline-1] = tmprcnums;
X	softptr[nextrcline-1] = tmpsoftptr;
X#if defined(DEBUGGING) && !defined(USETHREADS)
X	ngmax[nextrcline-1] = tmpngmax;
X#endif
X#ifdef CACHEFIRST
X	abs1st[nextrcline-1] = tmpabs1st;
X#endif
X    }
X    if (current_ng > ngx)
X	current_ng--;
X    if (newng < 0) {
X      reask_reloc:
X	unflush_output();		/* disable any ^O in effect */
X#ifdef SERVER
X	if (addnewbydefault) {
X	    buf[0] = '$';
X	    buf[1] = '\0';
X	} else {
X#endif /* SERVER */
X#ifdef VERBOSE
X	IF(verbose)
X	    printf("\nPut newsgroup where? [%s] ", dflt);
X	ELSE
X#endif
X#ifdef TERSE
X	    printf("\nPut where? [%s] ", dflt);
X#endif
X	fflush(stdout);
X      reinp_reloc:
X	eat_typeahead();
X	getcmd(buf);
X#ifdef SERVER
X	}
X#endif /* SERVER */
X	if (errno || *buf == '\f') {
X			    /* if return from stop signal */
X	    goto reask_reloc;	/* give them a prompt again */
X	}
X	setdef(buf,dflt);
X#ifdef VERIFY
X	printcmd();
X#endif
X	if (*buf == 'h') {
X#ifdef VERBOSE
X	    IF(verbose) {
X		printf("\n\n\
XType ^ to put the newsgroup first (position 0).\n\
XType $ to put the newsgroup last (position %d).\n", nextrcline-1);
X		printf("\
XType . to put it before the current newsgroup (position %d).\n", current_ng);
X		printf("\
XType -newsgroup name to put it before that newsgroup.\n\
XType +newsgroup name to put it after that newsgroup.\n\
XType a number between 0 and %d to put it at that position.\n", nextrcline-1);
X		printf("\
XType L for a listing of newsgroups and their positions.\n") FLUSH;
X	    }
X	    ELSE
X#endif
X#ifdef TERSE
X	    {
X		printf("\n\n\
X^ to put newsgroup first (pos 0).\n\
X$ to put last (pos %d).\n", nextrcline-1);
X		printf("\
X. to put before current newsgroup (pos %d).\n", current_ng);
X		printf("\
X-newsgroup to put before newsgroup.\n\
X+newsgroup to put after.\n\
Xnumber in 0-%d to put at that pos.\n", nextrcline-1);
X		printf("\
XL for list of .newsrc.\n") FLUSH;
X	    }
X#endif
X	    goto reask_reloc;
X	}
X	else if (*buf == 'L') {
X	    putchar('\n') FLUSH;
X	    list_newsgroups();
X	    goto reask_reloc;
X	}
X	else if (isdigit(*buf)) {
X	    if (!finish_command(TRUE))	/* get rest of command */
X		goto reinp_reloc;
X	    newng = atol(buf);
X	    if (newng < 0)
X		newng = 0;
X	    if (newng >= nextrcline)
X		return nextrcline-1;
X	}
X	else if (*buf == '^') {
X	    putchar('\n') FLUSH;
X	    newng = 0;
X	}
X	else if (*buf == '$') {
X	    putchar('\n') FLUSH;
X	    return nextrcline-1;
X	}
X	else if (*buf == '.') {
X	    putchar('\n') FLUSH;
X	    newng = current_ng;
X	}
X	else if (*buf == '-' || *buf == '+') {
X	    if (!finish_command(TRUE))	/* get rest of command */
X		goto reinp_reloc;
X	    newng = find_ng(buf+1);
X	    if (newng == nextrcline) {
X		fputs("Not found.",stdout) FLUSH;
X		goto reask_reloc;
X	    }
X	    if (*buf == '+')
X		newng++;
X	}
X	else {
X	    printf("\n%s",hforhelp) FLUSH;
X	    settle_down();
X	    goto reask_reloc;
X	}
X    }
X    if (newng < nextrcline-1) {
X#ifdef HASHNG
X	for (i=0; i<HASHSIZ; i++) {
X	    if (hashtbl[i] == nextrcline-1)
X		hashtbl[i] = newng;
X	    else if (hashtbl[i] >= newng)
X		++hashtbl[i];
X	}
X#endif
X	tmprcline = rcline[nextrcline-1];
X	tmptoread = toread[nextrcline-1];
X	tmprcchar = rcchar[nextrcline-1];
X	tmprcnums = rcnums[nextrcline-1];
X	tmpsoftptr = softptr[nextrcline-1];
X#if defined(DEBUGGING) && !defined(USETHREADS)
X	tmpngmax = ngmax[nextrcline-1];
X#endif
X#ifdef CACHEFIRST
X	tmpabs1st = abs1st[nextrcline-1];
X#endif
X	for (i=nextrcline-2; i>=newng; i--) {
X	    rcline[i+1] = rcline[i];
X	    toread[i+1] = toread[i];
X	    rcchar[i+1] = rcchar[i];
X	    rcnums[i+1] = rcnums[i];
X	    softptr[i+1] = softptr[i];
X#if defined(DEBUGGING) && !defined(USETHREADS)
X	    ngmax[i+1] = ngmax[i];
X#endif
X#ifdef CACHEFIRST
X	    abs1st[i+1] = abs1st[i];
X#endif
X	}
X	rcline[newng] = tmprcline;
X	toread[newng] = tmptoread;
X	rcchar[newng] = tmprcchar;
X	rcnums[newng] = tmprcnums;
X	softptr[newng] = tmpsoftptr;
X#if defined(DEBUGGING) && !defined(USETHREADS)
X	ngmax[newng] = tmpngmax;
X#endif
X#ifdef CACHEFIRST
X	abs1st[newng] = tmpabs1st;
X#endif
X    }
X    if (current_ng >= newng)
X	current_ng++;
X    return newng;
X}
X#endif
X
X/* List out the newsrc with annotations */
X
Xvoid
Xlist_newsgroups()
X{
X    register NG_NUM i;
X    char tmpbuf[2048];
X    static char *status[] = {"(READ)","(UNSUB)","(BOGUS)","(JUNK)"};
X    int cmd;
X
X    page_init();
X    print_lines("\
X  #  Status  Newsgroup\n\
X",STANDOUT);
X    for (i=0; i<nextrcline && !int_count; i++) {
X	if (toread[i] >= 0)
X	    set_toread(i);
X	*(rcline[i] + rcnums[i] - 1) = rcchar[i];
X	if (toread[i] > 0)
X	    sprintf(tmpbuf,"%3d %6ld   ",i,(long)toread[i]);
X	else
X	    sprintf(tmpbuf,"%3d %7s  ",i,status[-toread[i]]);
X	safecpy(tmpbuf+13,rcline[i],2034);
X	*(rcline[i] + rcnums[i] - 1) = '\0';
X	if (cmd = print_lines(tmpbuf,NOMARKING)) {
X	    if (cmd > 0)
X		pushchar(cmd);
X	    break;
X	}
X    }
X    int_count = 0;
X}
X
X/* find a newsgroup in .newsrc */
X
XNG_NUM
Xfind_ng(ngnam)
Xchar *ngnam;
X{
X    register NG_NUM ngnum;
X#ifdef HASHNG
X    register int hashix = hash(ngnam);
X    register int incr = 1;
X
X    while ((ngnum = hashtbl[hashix]) >= 0) {
X	if (strEQ(rcline[ngnum], ngnam) && toread[ngnum] >= TR_UNSUB)
X	    return ngnum;
X	hashix = (hashix + incr) % HASHSIZ;
X	incr += 2;			/* offsets from original are in n*2 */
X    }
X    return nextrcline;			/* = notfound */
X
X#else /* just do linear search */
X
X    for (ngnum = 0; ngnum < nextrcline; ngnum++) {
X	if (strEQ(rcline[ngnum],ngnam))
X	    break;
X    }
X    return ngnum;
X#endif
X}
X
Xvoid
Xcleanup_rc()
X{
X    register NG_NUM ngx;
X    register NG_NUM bogosity = 0;
X
X#ifdef VERBOSE
X    IF(verbose)
X	fputs("Checking out your .newsrc--hang on a second...\n",stdout)
X	  FLUSH;
X    ELSE
X#endif
X#ifdef TERSE
X	fputs("Checking .newsrc--hang on...\n",stdout) FLUSH;
X#endif
X    for (ngx = 0; ngx < nextrcline; ngx++) {
X	if (toread[ngx] >= TR_UNSUB) {
X	    set_toread(ngx);		/* this may reset newsgroup */
X					/* or declare it bogus */
X	}
X	if (toread[ngx] == TR_BOGUS)
X	    bogosity++;
X    }
X    for (ngx = nextrcline-1; ngx >= 0 && toread[ngx] == TR_BOGUS; ngx--)
X	bogosity--;			/* discount already moved ones */
X    if (nextrcline > 5 && bogosity > nextrcline / 2) {
X	fputs(
X"It looks like the active file is messed up.  Contact your news administrator,\n\
X",stdout);
X	fputs(
X"leave the \"bogus\" groups alone, and they may come back to normal.  Maybe.\n\
X",stdout) FLUSH;
X    }
X#ifdef RELOCATE
X    else if (bogosity) {
X#ifdef VERBOSE
X	IF(verbose)
X	    fputs("Moving bogus newsgroups to the end of your .newsrc.\n",
X		stdout) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    fputs("Moving boguses to the end.\n",stdout) FLUSH;
X#endif
X	for (; ngx >= 0; ngx--) {
X	    if (toread[ngx] == TR_BOGUS)
X		relocate_newsgroup(ngx,nextrcline-1);
X	}
X#ifdef DELBOGUS
Xreask_bogus:
X	in_char("Delete bogus newsgroups? [ny] ", 'D');
X	putchar('\n') FLUSH;
X	setdef(buf,"n");
X#ifdef VERIFY
X	printcmd();
X#endif
X	if (*buf == 'h') {
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs("\
XType y to delete bogus newsgroups.\n\
XType n or SP to leave them at the end in case they return.\n\
X",stdout) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("y to delete, n to keep\n",stdout) FLUSH;
X#endif
X	    goto reask_bogus;
X	}
X	else if (*buf == 'n' || *buf == 'q')
X	    ;
X	else if (*buf == 'y') {
X	    while (toread[nextrcline-1] == TR_BOGUS && nextrcline > 0)
X		--nextrcline;		/* real tough, huh? */
X	}
X	else {
X	    fputs(hforhelp,stdout) FLUSH;
X	    settle_down();
X	    goto reask_bogus;
X	}
X#endif
X    }
X#else
X#ifdef VERBOSE
X    IF(verbose)
X	fputs("You should edit bogus newsgroups out of your .newsrc.\n",
X	    stdout) FLUSH;
X    ELSE
X#endif
X#ifdef TERSE
X	fputs("Edit boguses from .newsrc.\n",stdout) FLUSH;
X#endif
X#endif
X    paranoid = FALSE;
X}
X
X#ifdef HASHNG
X/* make an entry in the hash table for the current newsgroup */
X
Xvoid
Xsethash(thisng)
XNG_NUM thisng;
X{
X    register int hashix = hash(rcline[thisng]);
X    register int incr = 1;
X#ifdef DEBUGGING
X    static int hashhits = 0, hashtries = 0;
X#endif
X
X#ifdef DEBUGGING
X    hashtries++;
X#endif
X    while (hashtbl[hashix] >= 0) {
X#ifdef DEBUGGING
X	hashhits++;
X	if (debug & DEB_HASH) {
X	    printf("  Hash hits: %d / %d\n",hashhits, hashtries) FLUSH;
X	}
X	hashtries++;
X#endif
X	hashix = (hashix + incr) % HASHSIZ;
X	incr += 2;			/* offsets from original are in n*2 */
X    }
X    hashtbl[hashix] = thisng;
X}
X
Xshort prime[] = {1,2,-3,-5,7,11,-13,-17,19,23,-29,-31,37,41,-43,-47,53,57,-59,
X    -61,67,71,-73,-79,83,89,-97,-101,1,1,1,1,1,1,1,1,1,1,1,1};
X
Xint
Xhash(ngnam)
Xregister char *ngnam;
X{
X    register int i = 0;
X    register int ch;
X    register int sum = 0;
X#ifdef DEBUGGING
X    char *ngn = ngnam;
X#endif
X
X    while (ch = *ngnam++) {
X	sum += (ch + i) * prime[i];   /* gives ~ 10% hits at 25% full */
X	i++;
X    }
X#ifdef DEBUGGING
X    if (debug & DEB_HASH)
X	printf("hash(%s) => %d => %d\n",ngn, sum, (sum<0?-sum:sum)%HASHSIZ)
X	  FLUSH;
X#endif
X    if (sum < 0)
X	sum = -sum;
X    return sum % HASHSIZ;
X}
X
X#endif
X
Xvoid
Xnewsrc_check()
X{
X    rcfp = fopen(rcname,"r");		/* open it */
X    if (rcfp == Nullfp) {			/* not there? */
X#ifdef VERBOSE
X	IF(verbose)
X	    fputs("\
XTrying to set up a .newsrc file--running newsetup...\n\n\
X",stdout) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    fputs("Setting up .newsrc...\n",stdout) FLUSH;
X#endif
X	if (doshell(sh,filexp(NEWSETUP)) ||
X	    (rcfp = fopen(rcname,"r")) == Nullfp) {
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs("\
XCan't create a .newsrc--you must do it yourself.\n\
X",stdout) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("(Fatal)\n",stdout) FLUSH;
X#endif
X	    finalize(1);
X	}
X    }
X    else {
X	UNLINK(rcbname);		/* unlink backup file name */
X	link(rcname,rcbname);		/* and backup current name */
X    }
X}
X
X/* write out the (presumably) revised .newsrc */
X
Xvoid
Xwrite_rc()
X{
X    register NG_NUM tmpng;
X    register char *delim;
X
X    rcfp = fopen(rctname, "w");		/* open .newsrc */
X    if (rcfp == Nullfp) {
X	printf("Can't recreate .newsrc\n") FLUSH;
X	finalize(1);
X    }
X
X    /* write out each line*/
X
X    for (tmpng = 0; tmpng < nextrcline; tmpng++) {
X	if (rcnums[tmpng]) {
X	    delim = rcline[tmpng] + rcnums[tmpng] - 1;
X	    *delim = rcchar[tmpng];
X	}
X	else
X	    delim = Nullch;
X#ifdef DEBUGGING
X	if (debug & DEB_NEWSRC_LINE)
X	    printf("%s\n",rcline[tmpng]) FLUSH;
X#endif
X	fprintf(rcfp,"%s\n",rcline[tmpng]);
X	if (delim)
X	    *delim = '\0';		/* might still need this line */
X    }
X
X    fclose(rcfp);			/* close .newsrc */
X    UNLINK(rcname);
X    link(rctname,rcname);
X    UNLINK(rctname);
X
X    if (writesoft) {
X	tmpfp = fopen(filexp(softname), "w");	/* open .rnsoft */
X	if (tmpfp == Nullfp) {
X	    printf(cantcreate,filexp(softname)) FLUSH;
X	    return;
X	}
X	for (tmpng = 0; tmpng < nextrcline; tmpng++) {
X	    fprintf(tmpfp,"%ld\n",(long)softptr[tmpng]);
X	}
X	fclose(tmpfp);
X    }
X}
X
Xvoid
Xget_old_rc()
X{
X    UNLINK(rctname);
X    link(rcname,rctname);
X    UNLINK(rcname);
X    link(rcbname,rcname);
X    UNLINK(rcbname);
X}
END_OF_FILE
  if test 23291 -ne `wc -c <'rcstuff.c'`; then
    echo shar: \"'rcstuff.c'\" unpacked with wrong size!
  fi
  # end of 'rcstuff.c'
fi
echo shar: End of archive 3 \(of 14\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 14 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still must unpack the following archives:
    echo "        " ${MISSING}
fi
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