Article 2702 of comp.sources.unix:
Path: nntpd.lkg.dec.com!crl.dec.com!crl.dec.com!bloom-beacon.mit.edu!news.kei.com!news.mathworks.com!newsfeed.internetmci.com!in1.uu.net!news1.digital.com!vixie!vixie!not-for-mail
From: davison@borland.com (Wayne Davison)
Newsgroups: comp.sources.unix
Subject: v29i044: trn-3.6 - threaded newsreader based on RN, V3.6, Part07/14
Date: 29 Oct 1995 01:32:54 -0800
Organization: Vixie Enterprises
Lines: 3449
Sender: vixie@vix.com
Approved: vixie@gw.home.vix.com
Message-ID: <46vho6$t4q@gw.home.vix.com>
References: <1.814959141.29825@gw.home.vix.com>
NNTP-Posting-Host: gw.home.vix.com

Submitted-By: davison@borland.com (Wayne Davison)
Posting-Number: Volume 29, Issue 44
Archive-Name: trn-3.6/part07

#!/bin/sh
# This is `part07' (part 7 of a multipart archive).
# Do not concatenate these parts, unpack them in order with `/bin/sh'.
# File `trn-3.6/rcln.c' is being continued...
#
touch -am 1231235999 $$.touch >/dev/null 2>&1
if test ! -f 1231235999 && test -f $$.touch; then
  shar_touch=touch
else
  shar_touch=:
  echo
  echo 'WARNING: not restoring timestamps.  Consider getting and'
  echo "installing GNU \`touch', distributed in GNU File Utilities..."
  echo
fi
rm -f 1231235999 $$.touch
#
if test ! -r _sharseq.tmp; then
  echo 'Please unpack part 1 first!'
  exit 1
fi
shar_sequence=`cat _sharseq.tmp`
if test "$shar_sequence" != 7; then
  echo "Please unpack part $shar_sequence next!"
  exit 1
fi
if test ! -f _sharnew.tmp; then
  echo 'x - still skipping trn-3.6/rcln.c'
else
  echo 'x - continuing file trn-3.6/rcln.c'
  sed 's/^X//' << 'SHAR_EOF' >> 'trn-3.6/rcln.c' &&
X    }
X    newnum = mbuf+rcnums[ngnum];
X    sprintf(newnum," 1-%ld",(long)(a1st - (lastnum != a1st)));
X    if (*s)
X	sprintf(newnum+strlen(newnum),",%s",s);
X
X    if (!checkflag && mbuf == rcline[ngnum]) {
X	rcline[ngnum] = saferealloc(rcline[ngnum],
X	    (MEM_SIZE)(rcnums[ngnum] + strlen(newnum) + 1));
X    }
X    else {
X	if (!checkflag)
X	    free(rcline[ngnum]);
X	rcline[ngnum] = mbuf;
X    }
X
Xret:;		/* semicolon in case DEBUG undefined */
X#ifdef DEBUG
X    if (debug & DEB_XREF_MARKER) {
X	printf("%s%c%s\n",rcline[ngnum],rcchar[ngnum],
X	  rcline[ngnum] + rcnums[ngnum]) FLUSH;
X    }
X#endif
X}
X
SHAR_EOF
  echo 'File trn-3.6/rcln.c is complete' &&
  $shar_touch -am 1028161994 'trn-3.6/rcln.c' &&
  chmod 0644 'trn-3.6/rcln.c' ||
  echo 'restore of trn-3.6/rcln.c failed'
  shar_count="`wc -c < 'trn-3.6/rcln.c'`"
  test 12713 -eq "$shar_count" ||
    echo "trn-3.6/rcln.c: original size 12713, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/patchlevel.h ==============
if test -f 'trn-3.6/patchlevel.h' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/patchlevel.h (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/patchlevel.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/patchlevel.h' &&
X#define PATCHLEVEL " 3.6 (20 Nov 1994)"
SHAR_EOF
  $shar_touch -am 1121154294 'trn-3.6/patchlevel.h' &&
  chmod 0644 'trn-3.6/patchlevel.h' ||
  echo 'restore of trn-3.6/patchlevel.h failed'
  shar_count="`wc -c < 'trn-3.6/patchlevel.h'`"
  test 40 -eq "$shar_count" ||
    echo "trn-3.6/patchlevel.h: original size 40, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/rcln.h ==============
if test -f 'trn-3.6/rcln.h' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/rcln.h (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/rcln.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/rcln.h' &&
X/* $Id: rcln.h,v 3.0 1992/02/01 03:09:32 davison Trn $
X */
X/* This software is Copyright 1991 by Stan Barber. 
X *
X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
X * use this software as long as: there is no monetary profit gained
X * specifically from the use or reproduction of this software, it is not
X * sold, rented, traded or otherwise marketed, and this copyright notice is
X * included prominently in any copy made. 
X *
X * The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
XEXT ART_NUM *ngmax INIT(NULL);
X
Xvoid    rcln_init _((void));
X#ifdef CATCHUP
Xvoid	catch_up _((NG_NUM,int));
X#endif
Xint	addartnum _((ART_NUM,char*));
X#ifdef MCHASE
Xvoid	subartnum _((ART_NUM,char*));
X#endif
Xvoid	prange _((char*,ART_NUM,ART_NUM));
Xvoid	set_toread _((NG_NUM));
Xvoid	checkexpired _((NG_NUM,ART_NUM));
SHAR_EOF
  $shar_touch -am 0831164294 'trn-3.6/rcln.h' &&
  chmod 0644 'trn-3.6/rcln.h' ||
  echo 'restore of trn-3.6/rcln.h failed'
  shar_count="`wc -c < 'trn-3.6/rcln.h'`"
  test 961 -eq "$shar_count" ||
    echo "trn-3.6/rcln.h: original size 961, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/rcstuff.c ==============
if test -f 'trn-3.6/rcstuff.c' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/rcstuff.c (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/rcstuff.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/rcstuff.c' &&
X/* $Id: rcstuff.c,v 3.0 1992/02/01 03:09:32 davison Trn $
X */
X/* This software is Copyright 1991 by Stan Barber. 
X *
X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
X * use this software as long as: there is no monetary profit gained
X * specifically from the use or reproduction of this software, it is not
X * sold, rented, traded or otherwise marketed, and this copyright notice is
X * included prominently in any copy made. 
X *
X * The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "util.h"
X#include "cache.h"
X#include "bits.h"
X#include "ngdata.h"
X#include "term.h"
X#include "final.h"
X#include "trn.h"
X#include "intrp.h"
X#include "only.h"
X#include "rcln.h"
X#include "nntp.h"
X#include "autosub.h"
X#include "hash.h"
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
Xstatic void grow_rc_arrays _((int));
Xstatic void parse_rcline _((NG_NUM));
X
X#ifdef HASHNG
Xstatic HASHTABLE *rc_hash;
Xstatic int rcline_cmp _((char*,int,HASHDATUM));
Xstatic void del_rc_line _((HASHDATUM*,int));
Xstatic void ins_rc_line _((HASHDATUM*,int));
X#endif
X
Xbool
Xrcstuff_init()
X{
X    register NG_NUM newng;
X    register int i;
X    register bool foundany = FALSE;
X    char *some_buf;
X    long length;
X
X    /* make filenames */
X
X#ifdef USE_NNTP
X    if (getenv("NEWSRC"))
X	printf("NEWSRC environment variable ignored -- use DOTDIR to set the directory.\n") FLUSH;
X#endif
X    rcname = savestr(filexp(RCNAME));
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    /* allocate memory for rc file globals */
X    grow_rc_arrays(1500);
X
X    /* read in the .newsrc file */
X
X    for (nextrcline = 0;
X	(some_buf = get_a_line(buf,LBUFLEN,FALSE,rcfp)) != Nullch;
X	nextrcline++)			/* for each line in .newsrc */
X    {
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	    grow_rc_arrays(maxrcline + 500);
X	if (tmpfp != Nullfp && fgets(tmpbuf,10,tmpfp) != Nullch)
X	    softptr[newng] = atol(tmpbuf);
X	else
X	    softptr[newng] = 0;
X	if (some_buf[--length] == '\n')
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);  /* make semipermanent copy */
X	else {
X	    /*NOSTRICT*/
X#ifndef lint
X	    some_buf = saferealloc(some_buf,(MEM_SIZE)(length+1));
X#endif
X	    rcline[newng] = some_buf;
X	}
X	if (*some_buf == ' ' || *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	parse_rcline(newng);
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    if (paranoid)
X	cleanup_rc();
X
X#ifdef HASHNG
X    rc_hash = hashcreate((int)nextrcline+50, rcline_cmp);
X    for (i = 0; i < nextrcline; i++)
X	if (toread[i] >= TR_UNSUB)
X	    sethash(i);
X#endif
X
X    return foundany;
X}
X
Xstatic void
Xparse_rcline(ngnum)
XNG_NUM ngnum;
X{
X    char *s;
X    int len;
X
X    for (s=rcline[ngnum]; *s && *s!=':' && *s!=NEGCHAR && !isspace(*s); s++) ;
X    len = s - rcline[ngnum];
X    if ((!*s || isspace(*s)) && !checkflag) {
X#ifndef lint
X	rcline[ngnum] = saferealloc(rcline[ngnum],(MEM_SIZE)len + 3);
X#endif
X	s = rcline[ngnum] + len;
X	strcpy(s, ": ");
X    }
X    if (*s == ':' && s[1] && s[2] == '0') {
X	rcchar[ngnum] = '0';
X	s[2] = '1';
X    } else
X	rcchar[ngnum] = *s;	/* salt away the : or ! */
X    rcnums[ngnum] = (char)len + 1;
X				/* remember where the numbers are */
X    *s = '\0';			/* null terminate newsgroup name */
X}
X
Xvoid
Xabandon_ng(ngnum)
XNG_NUM ngnum;
X{
X    char *some_buf = Nullch;
X
X    /* open .oldnewsrc and try to find the prior value for the group. */
X    if ((rcfp = fopen(rcbname, "r")) != Nullfp) {
X	int length = rcnums[ngnum] - 1;
X
X	while ((some_buf = get_a_line(buf,LBUFLEN,FALSE,rcfp)) != Nullch) {
X	    if (len_last_line_got <= 0)
X		continue;
X	    some_buf[len_last_line_got-1] = '\0';	/* wipe out newline */
X	    if ((some_buf[length] == ':' || some_buf[length] == NEGCHAR)
X	     && strnEQ(rcline[ngnum], some_buf, length)) {
X		break;
X	    }
X	    if (some_buf != buf)
X		free(some_buf);
X	}
X	fclose(rcfp);
X    } else if (errno != ENOENT) {
X	printf("Unable to open %s.\n", rcbname) FLUSH;
X	return;
X    }
X    if (some_buf == Nullch) {
X	some_buf = rcline[ngnum] + rcnums[ngnum];
X	if (*some_buf == ' ')
X	    some_buf++;
X	*some_buf = '\0';
X	abs1st[ngnum] = 0;	/* force group to be re-calculated */
X    }
X    else {
X	free(rcline[ngnum]);
X	if (some_buf == buf) {
X	    rcline[ngnum] = savestr(some_buf);
X	}
X	else {
X	    /*NOSTRICT*/
X#ifndef lint
X	    some_buf = saferealloc(some_buf, (MEM_SIZE)(len_last_line_got));
X#endif /* lint */
X	    rcline[ngnum] = some_buf;
X	}
X    }
X    parse_rcline(ngnum);
X    if (rcchar[ngnum] == NEGCHAR)
X	rcchar[ngnum] = ':';
X    set_toread(ngnum);
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 NEWSSPOOL */
X
Xbool
Xget_ng(what,flags)
Xchar *what;
Xint flags;
X{
X    char *ntoforget;
X    char promptbuf[128];
X    int autosub;
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      check_fuzzy_match:
X#ifdef EDIT_DISTANCE
X	if (fuzzyGet && (flags & GNG_FUZZY)) {
X	    flags &= ~GNG_FUZZY;
X	    if (find_close_match())
X		what = ngname;
X	    else
X		return FALSE;
X	} else
X#endif
X	    return FALSE;
X    }
X    set_ngname(what);
X    ng = find_ng(ngname);
X    if (ng == nextrcline) {		/* not in .newsrc? */
X	if (ng >= maxrcline)		/* check for overflow */
X	    grow_rc_arrays(maxrcline + 25);
X	if ((softptr[ng] = findact(buf,ngname,strlen(ngname),0L)) < 0) {
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	    if (novice_delays)
X		sleep(2);
X	    goto check_fuzzy_match;
X	}
X	if (mode != 'i' || !(autosub = auto_subscribe(ngname)))
X	    autosub = addnewbydefault;
X	if (autosub) {
X	    if (append_unsub) {
X		printf("(Adding %s to end of your .newsrc %ssubscribed)\n",
X		       ngname, (autosub == ADDNEW_SUB) ? "" : "un") FLUSH;
X		ng = add_newsgroup(ngname, autosub);
X	    } else {
X		if (autosub == ADDNEW_SUB) {
X		    printf("(Subscribing to %s)\n", ngname) FLUSH;
X		    ng = add_newsgroup(ngname, autosub);
X		} else {
X		    printf("(Ignoring %s)\n", ngname) FLUSH;
X		    return FALSE;
X		}
X	    }
X	    flags &= ~GNG_RELOC;
X	} else {
X#ifdef VERBOSE
X	IF(verbose)
X	    sprintf(promptbuf,"\nNewsgroup %s not in .newsrc -- subscribe?",ngname);
X	ELSE
X#endif
X#ifdef TERSE
X	    sprintf(promptbuf,"\nSubscribe %s?",ngname);
X#endif
Xreask_add:
X	in_char(promptbuf,'A',"ynYN");
X#ifdef VERIFY
X	printcmd();
X#endif
X	putchar('\n') FLUSH;
X	if (*buf == 'h') {
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("Type y or SP to subscribe to %s.\n\
XType Y to subscribe to this and all remaining new groups.\n\
XType N to leave all remaining new groups unsubscribed.\n", ngname)
X		  FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("y or SP to subscribe, Y to subscribe all new groups, N to unsubscribe all\n",stdout) FLUSH;
X#endif
X	    fputs(ntoforget,stdout) FLUSH;
X	    goto reask_add;
X	}
X	else if (*buf == 'n' || *buf == 'q') {
X	    if (append_unsub) {
X		ng = add_newsgroup(ngname, NEGCHAR);
X	    }
X	    return FALSE;
X	}
X	else if (*buf == 'y') {
X	    ng = add_newsgroup(ngname, ':');
X	    flags |= GNG_RELOC;
X	}
X	else if (*buf == 'Y') {
X	    addnewbydefault = ADDNEW_SUB;
X	    if (append_unsub)
X		printf("(Adding %s to end of your .newsrc subscribed)\n",
X		       ngname) FLUSH;
X	    else
X		printf("(Subscribing to %s)\n", ngname) FLUSH;
X	    ng = add_newsgroup(ngname, ':');
X	    flags &= ~GNG_RELOC;
X	}
X	else if (*buf == 'N') {
X	    addnewbydefault = ADDNEW_UNSUB;
X	    if (append_unsub) {
X		printf("(Adding %s to end of your .newsrc unsubscribed)\n",
X		       ngname) FLUSH;
X		ng = add_newsgroup(ngname, NEGCHAR);
X		flags &= ~GNG_RELOC;
X	    } else {
X		printf("(Ignoring %s)\n", ngname) FLUSH;
X		return FALSE;
X	    }
X	}
X	else {
X	    fputs(hforhelp,stdout) FLUSH;
X	    settle_down();
X	    goto reask_add;
X	}
X      }
X    }
X    else if (mode == 'i')		/* adding new groups during init? */
X	return FALSE;
X    else if (rcchar[ng] == NEGCHAR) {	/* unsubscribed? */
X#ifdef VERBOSE
X	IF(verbose)
X	    sprintf(promptbuf,
X"\nNewsgroup %s is unsubscribed -- resubscribe?",ngname)
X  FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    sprintf(promptbuf,"\nResubscribe %s?",ngname)
X	      FLUSH;
X#endif
Xreask_unsub:
X	in_char(promptbuf,'R',"yn");
X#ifdef VERIFY
X	printcmd();
X#endif
X	putchar('\n') FLUSH;
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	    register char *cp = rcline[ng] + rcnums[ng];
X	    rcchar[ng] = (*cp && cp[1] == '0' ? '0' : ':');
X	    flags &= ~GNG_RELOC;
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 (flags & GNG_RELOC) {
X	ng = relocate_newsgroup(ng,-1);
X	if (ng < 0)
X	    return FALSE;
X    }
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;
Xchar_int c;
X{
X    register NG_NUM newng = nextrcline++;
X					/* increment max rcline index */
X
X    if (newng >= maxrcline)		/* check for overflow */
X	grow_rc_arrays(maxrcline + 25);
X
X    rcnums[newng] = strlen(ngn) + 1;
X    rcline[newng] = safemalloc((MEM_SIZE)(rcnums[newng] + 2));
X    strcpy(rcline[newng],ngn);		/* and copy over the name */
X    strcpy(rcline[newng]+rcnums[newng], " ");
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    return newng;
X}
X
X#ifdef RELOCATE
XNG_NUM
Xrelocate_newsgroup(ngx,newng)
XNG_NUM ngx;
XNG_NUM newng;
X{
X    char *dflt = (ngx!=current_ng ? "$^.Lq" : "$^Lq");
X    char *tmprcline;
X    ART_UNREAD tmptoread;
X    char tmprcchar;
X    char tmprcnums;
X    ACT_POS tmpsoftptr;
X    register NG_NUM i;
X    ART_NUM tmpngmax;
X    ART_NUM tmpabs1st;
X    
X    starthere = 0;                      /* Disable this optimization */
X    writesoft = TRUE;			/* Update soft pointer file */
X    if (ngx < nextrcline-1) {
X#ifdef HASHNG
X	if (rc_hash)
X	    hashwalk(rc_hash, del_rc_line, ngx);
X#endif
X	tmprcline = rcline[ngx];
X	tmptoread = toread[ngx];
X	tmprcchar = rcchar[ngx];
X	tmprcnums = rcnums[ngx];
X	tmpsoftptr = softptr[ngx];
X	tmpngmax = ngmax[ngx];
X	tmpabs1st = abs1st[ngx];
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	    ngmax[i-1] = ngmax[i];
X	    abs1st[i-1] = abs1st[i];
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	ngmax[nextrcline-1] = tmpngmax;
X	abs1st[nextrcline-1] = tmpabs1st;
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 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	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\
XType q to abort the current action.\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\
Xq to abort\n") FLUSH;
X	    }
X#endif
X	    goto reask_reloc;
X	}
X	else if (*buf == 'q')
X	    return -1;
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	    newng = 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	if (rc_hash)
X	    hashwalk(rc_hash, ins_rc_line, newng);
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	tmpngmax = ngmax[nextrcline-1];
X	tmpabs1st = abs1st[nextrcline-1];
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	    ngmax[i+1] = ngmax[i];
X	    abs1st[i+1] = abs1st[i];
X	}
X	rcline[newng] = tmprcline;
X	toread[newng] = tmptoread;
X	rcchar[newng] = tmprcchar;
X	rcnums[newng] = tmprcnums;
X	softptr[newng] = tmpsoftptr;
X	ngmax[newng] = tmpngmax;
X	abs1st[newng] = tmpabs1st;
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(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#ifdef HASHNG
X    HASHDATUM data;
X
X    assert(rc_hash != 0);
X    data = hashfetch(rc_hash, ngnam, strlen(ngnam));
X    if (!data.dat_ptr)
X	return nextrcline;		/* = notfound */
X    return data.dat_len;
X
X#else /* just do linear search */
X    register NG_NUM ngnum;
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?", 'D', "ny");
X#ifdef VERIFY
X	printcmd();
X#endif
X	putchar('\n') FLUSH;
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    HASHDATUM data;
X
X    data.dat_ptr = nullstr;
X    data.dat_len = thisng;
X    hashstore(rc_hash, rcline[thisng], rcnums[thisng]-1, data);
X}
X
Xstatic int
Xrcline_cmp(key, keylen, data)
Xchar *key;
Xint keylen;
XHASHDATUM data;
X{
X    /* We already know that the lengths are equal, just compare the strings */
X    return bcmp(key, rcline[data.dat_len], keylen);
X}
X
Xstatic void
Xdel_rc_line(data, ngnum)
XHASHDATUM *data;
Xint ngnum;
X{
X    if (data->dat_len == ngnum)
X	data->dat_len = nextrcline-1;
X    else if (data->dat_len > ngnum)
X	data->dat_len--;
X}
X
Xstatic void
Xins_rc_line(data, ngnum)
XHASHDATUM *data;
Xint ngnum;
X{
X    if (data->dat_len == nextrcline-1)
X	data->dat_len = ngnum;
X    else if (data->dat_len >= ngnum)
X	data->dat_len++;
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("\nTrying 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("\nCan'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	get_anything();
X	putchar('\n') FLUSH;
X    }
X    else {
X	/* File exists; if zero length and backup isn't, complain */
X	if (fstat(fileno(rcfp),&filestat) < 0) {
X	    perror(rcname);
X	    finalize(1);
X	}
X	if (filestat.st_size == 0
X	 && stat(rcbname,&filestat) >= 0 && filestat.st_size > 0) {
X	    printf("Warning: %s is zero length but %s is not.\n",rcname,rcbname);
X	    printf("Either recover your newsrc or else remove the backup copy.\n");
X	    finalize(1);
X	}
X	UNLINK(rcbname);		/* unlink backup file name */
X	safelink(rcname,rcbname);	/* and backup current name */
X    }
X}
X
X/* checkpoint the .newsrc */
X
Xvoid
Xcheckpoint_rc()
X{
X#ifdef DEBUG
X    if (debug & DEB_CHECKPOINTING) {
X	fputs("(ckpt)",stdout);
X	fflush(stdout);
X    }
X#endif
X    if (doing_ng)
X	bits_to_rc();			/* do not restore M articles */
X    if (rc_changed)
X	write_rc();
X#ifdef DEBUG
X    if (debug & DEB_CHECKPOINTING) {
X	fputs("(done)",stdout);
X	fflush(stdout);
X    }
X#endif
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 .newnewsrc */
X    if (rcfp == Nullfp) {
X	printf(cantrecreate,".newsrc") FLUSH;
X	finalize(1);
X    }
X    if (stat(rcname,&filestat)>=0) {	/* preserve permissions */
X	chmod(rctname,filestat.st_mode&0666);
X	chown(rctname,filestat.st_uid,filestat.st_gid);	/* if possible */
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(rcchar[tmpng]);
X	    if (rcchar[tmpng] == '0' && delim[2] == '1')
X		delim[2] = '0';
X	}
X	else
X	    delim = Nullch;
X#ifdef DEBUG
X	if (debug & DEB_NEWSRC_LINE)
X	    printf("%s\n",rcline[tmpng]) FLUSH;
X#endif
X	if (fprintf(rcfp,"%s\n",rcline[tmpng]) < 0) {
X	write_error:
X	    printf(cantrecreate,".newsrc") FLUSH;
X	    fclose(rcfp);		/* close .newnewsrc */
X	    UNLINK(rctname);
X	    finalize(1);
X	}
X	if (delim) {
X	    *delim = '\0';		/* might still need this line */
X	    if (rcchar[tmpng] == '0' && delim[2] == '0')
X		delim[2] = '1';
X	}
X    }
X    fflush(rcfp);
X    /* fclose is the only sure test for full disks via NFS */
X    if (ferror(rcfp) || fclose(rcfp) == EOF)
X	goto write_error;
X
X    UNLINK(rcname);
X#ifdef HAS_RENAME
X    rename(rctname,rcname);
X#else
X    safelink(rctname,rcname);
X    UNLINK(rctname);
X#endif
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#ifdef HAS_RENAME
X    rename(rcname,rctname);
X    rename(rcbname,rcname);
X#else
X    safelink(rcname,rctname);
X    UNLINK(rcname);
X    safelink(rcbname,rcname);
X    UNLINK(rcbname);
X#endif
X}
X
Xstatic void
Xgrow_rc_arrays(newsize)
Xint newsize;
X{
X    abs1st = (ART_NUM*)saferealloc((char*)abs1st,
X		(MEM_SIZE)newsize * sizeof (ART_NUM));
X    ngmax = (ART_NUM*)saferealloc((char*)ngmax,
X		(MEM_SIZE)newsize * sizeof (ART_NUM));
X    rcline = (char**)saferealloc((char*)rcline,
X		(MEM_SIZE)newsize * sizeof (char*));
X    toread = (ART_UNREAD*)saferealloc((char*)toread,
X		(MEM_SIZE)newsize * sizeof(ART_UNREAD));
X    rcchar = (char *) saferealloc(rcchar,
X		(MEM_SIZE)newsize * sizeof (char));
X    rcnums = (char*)saferealloc(rcnums,
X		(MEM_SIZE)newsize * sizeof (char));
X    softptr = (ACT_POS*)saferealloc((char*)softptr,
X		(MEM_SIZE)newsize * sizeof (ACT_POS));
X
X    bzero((char*)(abs1st+maxrcline), (newsize-maxrcline) * sizeof (ART_NUM));
X    bzero((char*)(ngmax+maxrcline), (newsize-maxrcline) * sizeof (ART_NUM));
X    maxrcline = newsize;
X
X    return;
X}
SHAR_EOF
  $shar_touch -am 0820134794 'trn-3.6/rcstuff.c' &&
  chmod 0644 'trn-3.6/rcstuff.c' ||
  echo 'restore of trn-3.6/rcstuff.c failed'
  shar_count="`wc -c < 'trn-3.6/rcstuff.c'`"
  test 25896 -eq "$shar_count" ||
    echo "trn-3.6/rcstuff.c: original size 25896, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/rcstuff.h ==============
if test -f 'trn-3.6/rcstuff.h' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/rcstuff.h (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/rcstuff.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/rcstuff.h' &&
X/* $Id: rcstuff.h,v 3.0 1992/02/01 03:09:32 davison Trn $
X */
X/* This software is Copyright 1991 by Stan Barber. 
X *
X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
X * use this software as long as: there is no monetary profit gained
X * specifically from the use or reproduction of this software, it is not
X * sold, rented, traded or otherwise marketed, and this copyright notice is
X * included prominently in any copy made. 
X *
X * The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
XEXT char **rcline INIT(NULL);/* pointers to lines of .newsrc */
XEXT ART_UNREAD *toread INIT(NULL);
X			/* number of articles to be read in newsgroup */
X			/* <0 => invalid or unsubscribed newsgroup */
X#define TR_ONE ((ART_UNREAD) 1)
X#define TR_NONE ((ART_UNREAD) 0)
X#define TR_UNSUB ((ART_UNREAD) -1)
X			/* keep this one as -1, some tests use >= TR_UNSUB */
X#define TR_BOGUS ((ART_UNREAD) -2)
X#define TR_JUNK ((ART_UNREAD) -3)
X
X#define RCCHAR(ch) ((ch) == '0' ? ':' : (ch))
X
X#define ADDNEW_SUB ':'
X#define ADDNEW_UNSUB '!'
X
X#define GNG_RELOC	0x0001
X#define GNG_FUZZY	0x0002
X
XEXT char *rcchar INIT(NULL); /* holds the character : or ! while spot is \0 */
XEXT char *rcnums INIT(NULL); /* offset from rcline to numbers on line */
XEXT ACT_POS *softptr INIT(NULL);
X			/* likely ptr to active file entry for newsgroup */
XEXT bool paranoid INIT(FALSE);	/* did we detect some inconsistency in .newsrc? */
XEXT int maxrcline INIT(0);	/* current maximum # of lines in .newsrc */
XEXT int addnewbydefault INIT(0);
X
Xbool	rcstuff_init _((void));
Xvoid	abandon_ng _((NG_NUM));
Xbool	get_ng _((char*,int)); /* return TRUE if newsgroup is found or added */
XNG_NUM	add_newsgroup _((char*,char_int));
X#ifdef RELOCATE
XNG_NUM	relocate_newsgroup _((NG_NUM,NG_NUM)); /* move newsgroup around */
X#endif
Xvoid	list_newsgroups _((void));
XNG_NUM	find_ng _((char*));	/* return index of newsgroup */
Xvoid	cleanup_rc _((void));
Xvoid	sethash _((NG_NUM));
Xint	hash _((char*));
Xvoid	newsrc_check _((void));
Xvoid	checkpoint_rc _((void));
Xvoid	write_rc _((void));
Xvoid	get_old_rc _((void));
SHAR_EOF
  $shar_touch -am 0711223693 'trn-3.6/rcstuff.h' &&
  chmod 0644 'trn-3.6/rcstuff.h' ||
  echo 'restore of trn-3.6/rcstuff.h failed'
  shar_count="`wc -c < 'trn-3.6/rcstuff.h'`"
  test 2203 -eq "$shar_count" ||
    echo "trn-3.6/rcstuff.h: original size 2203, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/respond.c ==============
if test -f 'trn-3.6/respond.c' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/respond.c (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/respond.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/respond.c' &&
X/* $Id: respond.c,v 3.0 1992/02/01 03:09:32 davison Trn $
X */
X/* This software is Copyright 1991 by Stan Barber. 
X *
X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
X * use this software as long as: there is no monetary profit gained
X * specifically from the use or reproduction or this software, it is not
X * sold, rented, traded or otherwise marketed, and this copyright notice is
X * included prominently in any copy made. 
X *
X * The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "intrp.h"
X#include "cache.h"
X#include "head.h"
X#include "term.h"
X#include "ngdata.h"
X#include "ng.h"
X#include "util.h"
X#include "util2.h"
X#include "trn.h"
X#include "artio.h"
X#include "nntp.h"
X#include "final.h"
X#include "decode.h"
X#include "charsubst.h"
X#include "INTERN.h"
X#include "respond.h"
X
Xstatic char nullart[] = "\nNull article\n";
X
Xbool cut_line();
X
Xvoid
Xrespond_init()
X{
X    if (actfp)
X	export("NEWSACTIVE", filexp(ACTIVE));
X}
X
Xint
Xsave_article()
X{
X    bool_int use_pref;
X    register char *s, *c;
X    char altbuf[CBUFLEN];
X    int iter;
X    bool interactive = (buf[1] == FINISHCMD);
X    char cmd = *buf;
X    
X    if (!finish_command(interactive))	/* get rest of command */
X	return SAVE_ABORT;
X    if ((use_pref = isupper(cmd)) != 0)
X	cmd = tolower(cmd);
X    parseheader(art);
X#ifdef MIMESTORE
X    savefrom = (!mime_article && (cmd == 'w' || cmd == 'e'))
X#else
X    savefrom = (cmd == 'w' || cmd == 'e')
X#endif
X		? htype[PAST_HEADER].ht_minpos : 0;
X    if (artopen(art) == Nullfp) {
X#ifdef VERBOSE
X	IF(verbose)
X	    fputs("\n\
XSaving null articles is not very productive!  :-)\n\
X",stdout) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    fputs(nullart,stdout) FLUSH;
X#endif
X	return SAVE_DONE;
X    }
X    if (chdir(cwd)) {
X	printf(nocd,cwd) FLUSH;
X	sig_catcher(0);
X    }
X    if (cmd == 'e') {		/* is this an extract command? */
X	static bool custom_extract = FALSE;
X	int cnt = 0;
X	bool found_cut = FALSE;
X	char art_buf[LBUFLEN], *cmdstr;
X
X	s = buf+1;		/* skip e */
X	while (*s == ' ') s++;	/* skip leading spaces */
X	safecpy(altbuf,filexp(s),sizeof altbuf);
X	s = altbuf;
X	if (*s) {
X	    cmdstr = cpytill(buf,s,'|');	/* check for | */
X	    s = buf + strlen(buf)-1;
X	    while (*s == ' ') s--;		/* trim trailing spaces */
X	    *++s = '\0';
X	    if (*cmdstr) {
X		s = cmdstr+1;			/* skip | */
X		while (*s == ' ') s++;
X		if (*s)	{			/* if new command, use it */
X		    if (extractprog)
X			free(extractprog);
X		    extractprog = savestr(s);	/* put extracter in %e */
X		}
X		else
X		    cmdstr = extractprog;
X	    }
X	    else
X		cmdstr = Nullch;
X	    s = buf;
X	}
X	else {
X	    if (extractdest)
X		strcpy(s, extractdest);
X	    if (custom_extract)
X		cmdstr = extractprog;
X	    else
X		cmdstr = Nullch;
X	}
X	if (cmdstr) {
X	    if (strEQ(extractprog,"-"))
X		cmdstr = Nullch;
X	    else if (decode_fp != Nullfp)
X		decode_end();
X	}
X	custom_extract = (cmdstr != 0);
X
X	seekart((long)savefrom);
X	if (*s != '/') {		/* relative path? */
X	    c = (s==buf ? altbuf : buf);
X	    interp(c, (sizeof buf), getval("SAVEDIR",SAVEDIR));
X	    if (makedir(c,MD_DIR))	/* ensure directory exists */
X		strcpy(c,cwd);
X	    if (*s) {
X		while (*c) c++;
X		*c++ = '/';
X		strcpy(c,s);		/* add filename */
X	    }
X	    s = (s==buf ? altbuf : buf);
X	}
X	if (*s != '/') {		/* path still relative? */
X	    c = (s==buf ? altbuf : buf);
X	    sprintf(c, "%s/%s", cwd, s);
X	    s = c;			/* absolutize it */
X	}
X	if (decode_fp != Nullfp) {
X	    printf("Continuing %s:%s\n", decode_fname,
X		cmd != '\0' && strNE(extractdest,s) ?
X		 " (Ignoring conflicting directory)" : nullstr ) FLUSH;
X#ifdef USE_NNTP
X	    nntp_finishbody(FB_SILENT);
X#endif
X	    if (decode_type == UUDECODE)
X		uudecode(artfp);
X	    else
X		unship(artfp);
X	}
X	else {
X	    if (extractdest)
X		free(extractdest);
X	    s = extractdest = savestr(s); /* make it handy for %E */
X	    if (makedir(s, MD_DIR)) {	/* ensure directory exists */
X		int_count++;
X#ifdef USE_NNTP
X		nntp_finishbody(FB_SILENT);
X#endif
X		return SAVE_DONE;
X	    }
X	    if (chdir(s)) {
X		printf(nocd,s) FLUSH;
X		sig_catcher(0);
X	    }
X	    s = getwd(buf);		/* simplify path for output */
X	    while(readart(art_buf,LBUFLEN) != Nullch) {
X		if (*art_buf <= ' ')
X		    continue;	/* Ignore empty or initially-whitespace lines */
X#ifdef MIMESTORE
X		if (mime_article) {
X		    char oldmode = mode;
X		    if (!custom_extract) {
X			printf("Extracting MIME article into %s:\n", s) FLUSH;
X			extractprog = savestr(filexp(getval("MIMESTORE",MIMESTORE)));
X		    }
X		    else
X			printf("Extracting MIME article into %s using %s\n",
X			       s, extractprog) FLUSH;
X		    cnt = 0;
X#ifdef USE_NNTP
X		    nntp_finishbody(FB_SILENT);
X#endif
X		    interp(cmd_buf, sizeof cmd_buf,
X			   getval("EXMIMESAVER",EXMIMESAVER));
X		    termlib_reset();
X		    mode = 'x';
X		    resetty();		/* restore tty state */
X		    doshell(SH,cmd_buf);
X		    noecho();		/* revert to cbreaking */
X		    crmode();
X		    termlib_init();
X		    mode = oldmode;
X		    break;
X		}
X#endif
X		if (found_cut && custom_extract) {
X		    printf("Extracting data into %s using %s:\n",
X			s, extractprog) FLUSH;
X		    goto extract_it;
X		}
X		if (((*art_buf == '#' || *art_buf == ':')
X		  && (strnEQ(art_buf+1, "! /bin/sh", 9)
X		   || strnEQ(art_buf+1, "!/bin/sh", 8)
X		   || strnEQ(art_buf+2, "This is ", 8)))
X		 || strnEQ(art_buf, "sed ", 4)
X		 || strnEQ(art_buf, "cat ", 4)
X		 || strnEQ(art_buf, "echo ", 5)) {
X		    savefrom = tellart()-strlen(art_buf)-NL_SIZE+1;
X		    seekart((long)savefrom);
X		    if (custom_extract) {
X			printf("Extracting shar into %s using %s:\n",
X				s, extractprog) FLUSH;
X			goto extract_it;
X		    }
X		    /* Check for special-case of shar'ed-uuencoded file */
X		    while(readart(art_buf,LBUFLEN) != Nullch) {
X			if (*art_buf == '#' || *art_buf == ':'
X			 || strnEQ(art_buf, "echo ", 5)
X			 || strnEQ(art_buf, "sed ", 4))
X			    continue;
X			if (strnEQ(art_buf, "Xbegin ", 7)) {
X			    decode_type = UUDECODE;
X			    goto decode_it;
X			}
X			break;
X		    }
X		    printf("Extracting shar into %s:\n", s) FLUSH;
X		    if (extractprog)
X			free(extractprog);
X		    extractprog = savestr(filexp(getval("UNSHAR",UNSHAR)));
X		  extract_it:
X		    cnt = 0;
X#ifdef USE_NNTP
X		    nntp_finishbody(FB_SILENT);
X#endif
X		    interp(cmd_buf,(sizeof cmd_buf),getval("EXSAVER",EXSAVER));
X		    termlib_reset();
X		    resetty();		/* restore tty state */
X		    doshell(SH,cmd_buf);
X		    noecho();		/* revert to cbreaking */
X		    crmode();
X		    termlib_init();
X		    break;
X		}
X		else
X		if (!custom_extract
X		 && (strEQ(art_buf,"$\n")
X		  || strEQ(art_buf,"$ f\n"))) {
X		    savefrom = tellart()-strlen(art_buf)-NL_SIZE+1;
X		    if (found_cut
X		     || (readart(art_buf,LBUFLEN) != Nullch
X		      && (strnEQ(art_buf, "ship ", 5)
X		       || strnEQ(art_buf, "cont ", 5)))) {
X			decode_type = UNSHIP;
X			goto decode_it;
X		    }
X		}
X		else
X		if (!custom_extract
X		 && (strEQ(art_buf,"table\n")
X		  || strnEQ(art_buf,"begin ", 6))) {
X		    decode_type = UUDECODE;
X		    savefrom = tellart()-strlen(art_buf)-NL_SIZE+1;
X		 decode_it:
X		    printf("Extracting %s file into %s:\n",
X			decode_type == UNSHIP? "shipped":"uuencoded", s) FLUSH;
X		    if (extractprog)
X			free(extractprog);
X		    extractprog = savestr("-");
X		    seekart((long)savefrom);
X		    cnt = 0;
X#ifdef USE_NNTP
X		    nntp_finishbody(FB_SILENT);
X#endif
X		    if (decode_type == UUDECODE) {
X			uud_start();
X			uudecode(artfp);
X		    } else
X			unship(artfp);
X		    break;
X		}
X		else {
X		    if (cut_line(art_buf)) {
X			savefrom = tellart();
X			found_cut = TRUE;
X		    }
X		    else if (found_cut || ++cnt == 300) {
X			break;
X		    }
X		}
X	    }/* while */
X	    if (cnt) {
X		if (custom_extract)
X		    printf("Didn't find cut line for extraction to '%s'.\n",
X			extractprog) FLUSH;
X		else
X		    printf("Unable to determine type of file.\n") FLUSH;
X	    }
X	}/* if */
X    }
X    else if ((s = index(buf,'|')) != Nullch) {
X				/* is it a pipe command? */
X	s++;			/* skip the | */
X	while (*s == ' ') s++;
X	safecpy(altbuf,filexp(s),sizeof altbuf);
X	if (savedest)
X	    free(savedest);
X	savedest = savestr(altbuf);
X#ifdef USE_NNTP
X	nntp_finishbody(FB_SILENT);
X#endif
X	interp(cmd_buf, (sizeof cmd_buf), getval("PIPESAVER",PIPESAVER));
X				/* then set up for command */
X	termlib_reset();
X	resetty();		/* restore tty state */
X	if (use_pref)		/* use preferred shell? */
X	    doshell(Nullch,cmd_buf);
X				/* do command with it */
X	else
X	    doshell(sh,cmd_buf);	/* do command with sh */
X	noecho();		/* and stop echoing */
X	crmode();		/* and start cbreaking */
X	termlib_init();
X    }
X    else {			/* normal save */
X	bool there, mailbox;
X	char *savename = getval("SAVENAME",SAVENAME);
X
X	s = buf+1;		/* skip s or S */
X	if (*s == '-') {	/* if they are confused, skip - also */
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs("Warning: '-' ignored.  This isn't readnews.\n",stdout)
X		  FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("'-' ignored.\n",stdout) FLUSH;
X#endif
X	    s++;
X	}
X	for (; *s == ' '; s++);	/* skip spaces */
X	safecpy(altbuf,filexp(s),sizeof altbuf);
X	s = altbuf;
X	if (*s != '/') {
X	    interp(buf, (sizeof buf), getval("SAVEDIR",SAVEDIR));
X	    if (makedir(buf,MD_DIR))	/* ensure directory exists */
X		strcpy(buf,cwd);
X	    if (*s) {
X		for (c = buf; *c; c++) ;
X		*c++ = '/';
X		strcpy(c,s);		/* add filename */
X	    }
X	    s = buf;
X	}
X	for (iter = 0;
X	    (there = stat(s,&filestat) >= 0) && S_ISDIR(filestat.st_mode);
X	    iter++) {			/* is it a directory? */
X
X	    c = (s+strlen(s));
X	    *c++ = '/';			/* put a slash before filename */
X	    interp(c, s==buf?(sizeof buf):(sizeof altbuf),
X		iter ? "News" : savename );
X				/* generate a default name somehow or other */
X	}
X	makedir(s,MD_FILE);
X	if (*s != '/') {		/* relative path? */
X	    c = (s==buf ? altbuf : buf);
X	    sprintf(c, "%s/%s", cwd, s);
X	    s = c;			/* absolutize it */
X	}
X	if (savedest)
X	    free(savedest);
X	s = savedest = savestr(s);	/* doesn't move any more */
X					/* make it handy for %b */
X	if (!there) {
X	    if (mbox_always)
X		mailbox = TRUE;
X	    else if (norm_always)
X		mailbox = FALSE;
X	    else {
X		char *dflt = (instr(savename,"%a", TRUE) ? "nyq" : "ynq");
X		
X		sprintf(cmd_buf,
X		"\nFile %s doesn't exist--\n	use mailbox format?",s);
X	      reask_save:
X		in_char(cmd_buf, 'M', dflt);
X		putchar('\n') FLUSH;
X#ifdef VERIFY
X		printcmd();
X#endif
X		if (*buf == 'h') {
X#ifdef VERBOSE
X		    IF(verbose)
X			printf("\n\
XType y to create %s as a mailbox.\n\
XType n to create it as a normal file.\n\
XType q to abort the save.\n\
X",s) FLUSH;
X		    ELSE
X#endif
X#ifdef TERSE
X			fputs("\n\
Xy to create mailbox.\n\
Xn to create normal file.\n\
Xq to abort.\n\
X",stdout) FLUSH;
X#endif
X		    goto reask_save;
X		}
X		else if (*buf == 'n') {
X		    mailbox = FALSE;
X		}
X		else if (*buf == 'y') {
X		    mailbox = TRUE;
X		}
X		else if (*buf == 'q') {
X		    goto s_bomb;
X		}
X		else {
X		    fputs(hforhelp,stdout) FLUSH;
X		    settle_down();
X		    goto reask_save;
X		}
X	    }
X	}
X	else if (S_ISCHR(filestat.st_mode))
X	    mailbox = FALSE;
X	else {
X	    int tmpfd;
X	    
X	    tmpfd = open(s,0);
X	    if (tmpfd == -1)
X		mailbox = FALSE;
X	    else {
X		if (read(tmpfd,buf,LBUFLEN)) {
X		    c = buf;
X		    if (!isspace(MBOXCHAR))   /* if non-zero, */
X			while (isspace(*c))   /* check the first character */
X			    c++;
X		    mailbox = (*c == MBOXCHAR);
X		} else {
X		    mailbox = mbox_always;    /* if zero length, recheck -M */
X		}
X		close(tmpfd);
X	    }
X	}
X
X	safecpy(cmd_buf, filexp(mailbox ?
X	    getval("MBOXSAVER",MBOXSAVER) :
X	    getval("NORMSAVER",NORMSAVER) ), sizeof cmd_buf);
X				/* format the command */
X#ifdef USE_NNTP
X	nntp_finishbody(FB_SILENT);
X#endif
X	termlib_reset();
X	resetty();		/* make terminal behave */
X	if (doshell(use_pref?Nullch:SH,cmd_buf)) {
X	    termlib_init();
X	    fputs("Not saved",stdout);
X	} else {
X	    termlib_init();
X	    printf("%s to %s %s",
X	      there?"Appended":"Saved",
X	      mailbox?"mailbox":"file",
X	      s);
X	}
X	if (interactive)
X	    putchar('\n') FLUSH;
X	noecho();		/* make terminal do what we want */
X	crmode();
X    }
Xs_bomb:
X#ifdef USE_NNTP
X    if (chdir(spool)) {
X#else
X    if (chdir(spool) || chdir(ngdir)) {
X#endif
X	printf(nocd,ngdir) FLUSH;
X	sig_catcher(0);
X    }
X#ifdef USE_NNTP
X    nntp_finishbody(FB_SILENT);
X#endif
X    return SAVE_DONE;
X}
X
Xint
Xcancel_article()
X{
X    char *ngs_buf;
X    char *from_buf;
X    char *reply_buf;
X    int myuid = getuid();
X    int r = -1;
X
X    if (artopen(art) == Nullfp) {
X#ifdef VERBOSE
X	IF(verbose)
X	    fputs("\n\
XCanceling null articles is your idea of fun?  :-)\n\
X",stdout) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    fputs(nullart,stdout) FLUSH;
X#endif
X	return r;
X    }
X    reply_buf = fetchlines(art,REPLY_LINE);
X    from_buf = fetchlines(art,FROM_LINE);
X    ngs_buf = fetchlines(art,NGS_LINE);
X    if (!instr(from_buf,phostname,FALSE) ||
X	(!instr(from_buf,loginName,TRUE) &&
X	 !instr(reply_buf,loginName,TRUE) &&
X#ifdef NEWS_ADMIN
X	 myuid != newsuid &&
X#endif
X	 myuid != ROOTID ) ) {
X#ifdef DEBUG
X	    if (debug)
X		printf("\n%s@%s != %s\n",loginName,phostname,from_buf) FLUSH;
X#endif
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs("\nYou can't cancel someone else's article\n",stdout)
X		  FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("\nNot your article\n",stdout) FLUSH;
X#endif
X    }
X    else {
X	tmpfp = fopen(headname,"w");	/* open header file */
X	if (tmpfp == Nullfp) {
X	    printf(cantcreate,headname) FLUSH;
X	    goto no_cancel;
X	}
X	interp(buf, (sizeof buf), getval("CANCELHEADER",CANCELHEADER));
X	fputs(buf,tmpfp);
X	fclose(tmpfp);
X	fputs("\nCanceling...\n",stdout) FLUSH;
X	r = doshell(sh,filexp(getval("CANCEL",CANCEL)));
X    }
Xno_cancel:
X    free(ngs_buf);
X    free(from_buf);
X    free(reply_buf);
X    return r;
X}
X
Xint
Xsupersede_article()		/* Supersedes: */
X{
X    char *ngs_buf;
X    char *from_buf;
X    char *reply_buf;
X    int myuid = getuid();
X    int r = -1;
X    bool incl_body = (*buf == 'Z');
X
X    if (artopen(art) == Nullfp) {
X#ifdef VERBOSE
X	IF(verbose)
X	    fputs("\n\
XSuperceding null articles is your idea of fun?  :-)\n\
X",stdout) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    fputs(nullart,stdout) FLUSH;
X#endif
X	return r;
X    }
X    reply_buf = fetchlines(art,REPLY_LINE);
X    from_buf = fetchlines(art,FROM_LINE);
X    ngs_buf = fetchlines(art,NGS_LINE);
X    if (!instr(from_buf,phostname,FALSE) ||
X	(!instr(from_buf,loginName,TRUE) &&
X	 !instr(reply_buf,loginName,TRUE) &&
X#ifdef NEWS_ADMIN
X	 myuid != newsuid &&
X#endif
X	 myuid != ROOTID ) ) {
X#ifdef DEBUG
X	    if (debug)
X		printf("\n%s@%s != %s\n",loginName,phostname,from_buf) FLUSH;
X#endif
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs("\nYou can't supersede someone else's article\n",stdout)
X		  FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("\nNot your article\n",stdout) FLUSH;
X#endif
X    }
X    else {
X	tmpfp = fopen(headname,"w");	/* open header file */
X	if (tmpfp == Nullfp) {
X	    printf(cantcreate,headname) FLUSH;
X	    goto no_commute;
X	}
X	interp(buf, (sizeof buf), getval("SUPERSEDEHEADER",SUPERSEDEHEADER));
X	fputs(buf,tmpfp);
X	if (incl_body && artfp != Nullfp) {
X	    parseheader(art);
X	    seekart((long)htype[PAST_HEADER].ht_minpos);
X	    while (readart(buf,LBUFLEN) != Nullch) {
X		fputs(buf,tmpfp);
X	    }
X	}
X	fclose(tmpfp);
X    	safecpy(cmd_buf,filexp(getval("NEWSPOSTER",NEWSPOSTER)),
X		sizeof cmd_buf);
X    	invoke(cmd_buf,origdir);
X	r = 0;
X    }
Xno_commute:
X    free(ngs_buf);
X    free(from_buf);
X    free(reply_buf);
X    return r;
X}
X
Xvoid
Xreply()
X{
X    bool incl_body = (*buf == 'R');
X#ifdef CHARSUBST
X    char hbuf[4*LBUFLEN];
X#endif
X    char *maildoer = savestr(getval("MAILPOSTER",MAILPOSTER));
X
X    artopen(art);
X    tmpfp = fopen(headname,"w");	/* open header file */
X    if (tmpfp == Nullfp) {
X	printf(cantcreate,headname) FLUSH;
X	goto no_reply;
X    }
X    interp(buf, (sizeof buf), getval("MAILHEADER",MAILHEADER));
X    fputs(buf,tmpfp);
X    if (!instr(maildoer,"%h",TRUE))
X#ifdef VERBOSE
X	IF(verbose)
X	    printf("\n%s\n(Above lines saved in file %s)\n",buf,headname)
X	      FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    printf("\n%s\n(Header in %s)\n",buf,headname) FLUSH;
X#endif
X    if (incl_body && artfp != Nullfp) {
X	interp(buf, (sizeof buf), getval("YOUSAID",YOUSAID));
X	fprintf(tmpfp,"%s\n",buf);
X	parseheader(art);
X	seekart((long)htype[PAST_HEADER].ht_minpos);
X	while (readart(buf,LBUFLEN) != Nullch) {
X#ifdef CHARSUBST
X	    strcharsubst(buf,hbuf);
X	    fprintf(tmpfp,"%s%s",indstr,hbuf);
X#else
X	    fprintf(tmpfp,"%s%s",indstr,buf);
X#endif
X	}
X	fprintf(tmpfp,"\n");
X    }
X    fclose(tmpfp);
X    safecpy(cmd_buf,filexp(maildoer),sizeof cmd_buf);
X    invoke(cmd_buf,origdir);
X    UNLINK(headname);		/* kill the header file */
Xno_reply:
X    free(maildoer);
X}
X  
Xvoid
Xforward()
X{
X#ifdef CHARSUBST
X    char hbuf[4*LBUFLEN];
X#endif
X    char *maildoer = savestr(getval("FORWARDPOSTER",FORWARDPOSTER));
X
X    artopen(art);
X    tmpfp = fopen(headname,"w");	/* open header file */
X    if (tmpfp == Nullfp) {
X	printf(cantcreate,headname) FLUSH;
X	goto no_forward;
X    }
X    interp(buf, (sizeof buf), getval("FORWARDHEADER",FORWARDHEADER));
X    fputs(buf,tmpfp);
X    if (!instr(maildoer,"%h",TRUE))
X#ifdef VERBOSE
X	IF(verbose)
X	    printf("\n%s\n(Above lines saved in file %s)\n",buf,headname)
X	      FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    printf("\n%s\n(Header in %s)\n",buf,headname) FLUSH;
X#endif
X    if (artfp != Nullfp) {
X	interp(buf, (sizeof buf), getval("FORWARDMSG",FORWARDMSG));
X	if (*buf)
X	    fprintf(tmpfp,"%s\n",buf);
X	parseheader(art);
X	seekart(0L);
X	while (readart(buf,LBUFLEN) != Nullch) {
X#ifdef CHARSUBST
X	    strcharsubst(buf,hbuf);
X	    fprintf(tmpfp,"%s",hbuf);
X#else
X	    fprintf(tmpfp,"%s",buf);
X#endif
X	}
X	interp(buf, (sizeof buf), getval("FORWARDMSGEND",FORWARDMSGEND));
X	if (*buf)
X	    fprintf(tmpfp,"%s\n",buf);
X    }
X    fclose(tmpfp);
X    safecpy(cmd_buf,filexp(maildoer),sizeof cmd_buf);
X    invoke(cmd_buf,origdir);
X    UNLINK(headname);		/* kill the header file */
Xno_forward:
X    free(maildoer);
X}
X
Xvoid
Xfollowup()
X{
X    bool incl_body = (*buf == 'F');
X    char hbuf[4*LBUFLEN];	/* four times the old size */
X    ART_NUM oldart = art;
X
X    if (!incl_body && art <= lastart) {
X	in_answer("\n\nAre you starting an unrelated topic? [ynq] ", 'F');
X	setdef(buf,"y");
X	if (*buf == 'q')  /*TODO: need to add 'h' also */
X	    return;
X	if (*buf != 'n')
X	    art = lastart + 1;
X    }
X    artopen(art);
X    tmpfp = fopen(headname,"w");
X    if (tmpfp == Nullfp) {
X	printf(cantcreate,headname) FLUSH;
X	art = oldart;
X	return;
X    }
X    interp(hbuf, (sizeof hbuf), getval("NEWSHEADER",NEWSHEADER));
X    fprintf(tmpfp,"%s",hbuf);
X    if (incl_body && artfp != Nullfp) {
X#ifdef VERBOSE
X	if (verbose)
X	    fputs("\n\
X(Be sure to double-check the attribution against the signature, and\n\
Xtrim the quoted article down as much as possible.)\n\
X",stdout) FLUSH;
X#endif
X	interp(buf, (sizeof buf), getval("ATTRIBUTION",ATTRIBUTION));
X	fprintf(tmpfp,"%s\n",buf);
X	parseheader(art);
X	seekart((long)htype[PAST_HEADER].ht_minpos);
X	while (readart(buf,LBUFLEN) != Nullch) {
X#ifdef CHARSUBST
X	    strcharsubst(buf,hbuf);
X	    fprintf(tmpfp,"%s%s",indstr,hbuf);
X#else
X	    fprintf(tmpfp,"%s%s",indstr,buf);
X#endif
X	}
X	fprintf(tmpfp,"\n");
X    }
X    fclose(tmpfp);
X    safecpy(cmd_buf,filexp(getval("NEWSPOSTER",NEWSPOSTER)),sizeof cmd_buf);
X    invoke(cmd_buf,origdir);
X    UNLINK(headname);
X    art = oldart;
X}
X
Xvoid
Xinvoke(cmd,dir)
Xchar *cmd,*dir;
X{
X    char oldmode = mode;
X    if (chdir(dir)) {
X	printf(nocd,dir) FLUSH;
X	return;
X    }
X    termlib_reset();
X    mode = 'x';
X#ifdef VERBOSE
X    IF(verbose)
X	printf("\n(leaving cbreak mode; cwd=%s)",dir);
X    ELSE
X#endif
X#ifdef TERSE
X	printf("\n(-cbreak; cwd=%s)",dir);
X#endif
X#ifdef DEBUG
X    if (debug)
X	printf("\nInvoking command: %s",cmd);
X#endif
X    printf("\n\n") FLUSH;
X    resetty();			/* make terminal well-behaved */
X    doshell(sh,cmd);		/* do the command */
X    noecho();			/* set no echo */
X    crmode();			/* and cbreak mode */
X#ifdef VERBOSE
X    IF(verbose)
X	fputs("\n(re-entering cbreak mode)\n",stdout) FLUSH;
X    ELSE
X#endif
X#ifdef TERSE
X	fputs("\n(+cbreak)\n",stdout) FLUSH;
X#endif
X    termlib_init();
X    mode = oldmode;
X#ifdef USE_NNTP
X    if (chdir(spool)) {
X#else
X    if (chdir(spool) || chdir(ngdir)) {
X#endif
X	printf(nocd,ngdir) FLUSH;
X	sig_catcher(0);
X    }
X}
X
X/*
X** cut_line() determines if a line is meant as a "cut here" marker.
X** Some examples that we understand:
X**
X**  BEGIN--cut here--cut here
X**
X**  ------------------ tear at this line ------------------
X**
X**  #----cut here-----cut here-----cut here-----cut here----#
X*/
Xbool
Xcut_line(str)
Xchar *str;
X{
X    char *cp, got_flag;
X    char word[80];
X    int  dash_cnt, equal_cnt, other_cnt;
X
X    /* Disallow any single-/double-quoted, parenthetical or c-commented
X    ** string lines.  Make sure it has the cut-phrase and at least six
X    ** '-'s or '='s.  If only four '-'s are present, check for a duplicate
X    ** of the cut phrase.  If over 20 unknown characters are encountered,
X    ** assume it isn't a cut line.  If we succeed, return TRUE.
X    */
X    for (cp = str, dash_cnt = equal_cnt = other_cnt = 0; *cp; cp++) {
X	switch (*cp) {
X	case '-':
X	    dash_cnt++;
X	    break;
X	case '=':
X	    equal_cnt++;
X	    break;
X	case '/':
X	    if(*(cp+1) != '*') {
X		break;
X	    }
X	case '"':
X	case '\'':
X	case '(':
X	case ')':
X	case '[':
X	case ']':
X	case '{':
X	case '}':
X	    return FALSE;
X	default:
X	    other_cnt++;
X	    break;
X	}
X    }
X    if (dash_cnt < 4 && equal_cnt < 6)
X	return FALSE;
X
X    got_flag = 0;
X
X    for (*(cp = word) = '\0'; *str; str++) {
X	if (islower(*str))
X	    *cp++ = *str;
X	else if (isupper(*str))
X	    *cp++ = tolower(*str);
X	else {
X	    if (*word) {
X		*cp = '\0';
X		switch (got_flag) {
X		case 2:
X		    if (!strcmp(word, "line")
X		     || !strcmp(word, "here"))
X			if ((other_cnt -= 4) <= 20)
X			    return TRUE;
X		    break;
X		case 1:
X		    if (!strcmp(word, "this")) {
X			got_flag = 2;
X			other_cnt -= 4;
X		    }
X		    else if (!strcmp(word, "here")) {
X			other_cnt -= 4;
X			if ((dash_cnt >= 6 || equal_cnt >= 6)
X			 && other_cnt <= 20)
X			    return TRUE;
X			dash_cnt = 6;
X			got_flag = 0;
X		    }
X		    break;
X		case 0:
X		    if (!strcmp(word, "cut")
X		     || !strcmp(word, "snip")
X		     || !strcmp(word, "tear")) {
X			got_flag = 1;
X			other_cnt -= strlen(word);
X		    }
X		    break;
X		}
X		*(cp = word) = '\0';
X	    }
X	}
X    } /* for *str */
X
X    return FALSE;
X}
SHAR_EOF
  $shar_touch -am 1028161994 'trn-3.6/respond.c' &&
  chmod 0644 'trn-3.6/respond.c' ||
  echo 'restore of trn-3.6/respond.c failed'
  shar_count="`wc -c < 'trn-3.6/respond.c'`"
  test 22294 -eq "$shar_count" ||
    echo "trn-3.6/respond.c: original size 22294, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/respond.h ==============
if test -f 'trn-3.6/respond.h' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/respond.h (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/respond.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/respond.h' &&
X/* $Id: respond.h,v 3.0 1992/02/01 03:09:32 davison Trn $
X */
X/* This software is Copyright 1991 by Stan Barber. 
X *
X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
X * use this software as long as: there is no monetary profit gained
X * specifically from the use or reproduction of this software, it is not
X * sold, rented, traded or otherwise marketed, and this copyright notice is
X * included prominently in any copy made. 
X *
X * The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
XEXT char *savedest INIT(Nullch);	/* value of %b */
XEXT char *extractdest INIT(Nullch);	/* value of %E */
XEXT char *extractprog INIT(Nullch);	/* value of %e */
XEXT ART_POS savefrom INIT(0);		/* value of %B */
XEXT char *headname INIT(Nullch);
X
X#define SAVE_ABORT 0
X#define SAVE_DONE 1
X
Xvoid	respond_init _((void));
Xint	save_article _((void));
Xint	cancel_article _((void));
Xint	supersede_article _((void));
Xvoid	reply _((void));
Xvoid	forward _((void));
Xvoid	followup _((void));
Xvoid	invoke _((char*,char*));
SHAR_EOF
  $shar_touch -am 0429181194 'trn-3.6/respond.h' &&
  chmod 0644 'trn-3.6/respond.h' ||
  echo 'restore of trn-3.6/respond.h failed'
  shar_count="`wc -c < 'trn-3.6/respond.h'`"
  test 1157 -eq "$shar_count" ||
    echo "trn-3.6/respond.h: original size 1157, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/Makefile.SH ==============
if test -f 'trn-3.6/Makefile.SH' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/Makefile.SH (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/Makefile.SH (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/Makefile.SH' &&
Xcase $CONFIG in
X    '') . ./config.sh ;;
Xesac
Xecho "Extracting Makefile (with variable substitutions)"
Xcase "$srcdir" in
X'') srcdir='.';;
Xesac
X$cat >Makefile <<!GROK!THIS!
X# $Id: Makefile.SH,v 3.0 1992/03/01 02:13:32 davison Trn $
X#
X# This software is Copyright 1991 by Stan Barber. 
X#
X# Permission is hereby granted to copy, reproduce, redistribute or otherwise
X# use this software as long as: there is no monetary profit gained
X# specifically from the use or reproduction of this software, it is not
X# sold, rented, traded or otherwise marketed, and this copyright notice is
X# included prominently in any copy made. 
X#
X# The author make no claims as to the fitness or correctness of this software
X# for any use whatsoever, and it is provided as is. Any use of this software
X# is at the user's own risk. 
X# 
X# optional defines you can add to the CFLAGS
X#    -DDEBUG                      compile in support for the -D option
X#    -DNO_LISTGROUP               avoids attempting to use LISTGROUP
X#    -DREPLYTO_POSTER_CHECKING    makes -p also check Reply-To (slow)
X
Xsrcdir = $srcdir
XVPATH = $vpath
X
XCC = $cc
XTOUCH = touch
X#YACC = yacc
X#LINT = lint
Xlintflags = -phbvxac $vincludes
X
XCFLAGS = $optimize $ccflags $vincludes
XLDFLAGS = $ldflags
X
Xrnbin = $installbin
Xrnlib = $installprivlib
Xmansrc = $installmansrc
Xmanext = $manext
X#NNTPNNTPFLAGS = $nntpflags
Xndirc = $ndirc
Xndiro = $ndiro
Xstrftimec = $strftimec
Xstrftimeo = $strftimeo
Xinstallfilexp = $filexp
X
Xlibs = $libndir $termlib $jobslib -lm $libs
X!GROK!THIS!
X$cat >>Makefile <<'!NO!SUBS!'
Xpublic = trn newsetup newsgroups Pnews Rnmail trn-artchk $(nntpbin)
Xprivate = norm.saver mbox.saver makedir Pnews.header Speller filexp
Xutil = makedepend newsnews
Xmanpages = trn.1 Pnews.1 Rnmail.1 newsetup.1 newsgroups.1
X#NNTPnntpbin=nntplist
X#NNTPnntpsrc=nntpinit.c nntpclient.c nntpauth.c nntp.c
X#NNTPnntpobj1=nntpinit.o nntpclient.o nntpauth.o
X#NNTPnntpobj2=nntp.o
X
Xh1 = addng.h art.h artio.h artsrch.h autosub.h backpage.h bits.h cache.h 
Xh2 = charsubst.h common.h decode.h final.h hash.h head.h help.h init.h intrp.h
Xh3 = kfile.h last.h ng.h ngdata.h ngsrch.h ngstuff.h only.h rcln.h rcstuff.h
Xh4 = respond.h rthread.h rt-mt.h rt-ov.h rt-page.h rt-process.h rt-select.h
Xh5 = rt-util.h rt-wumpus.h search.h sw.h term.h trn.h util.h util2.h
X
Xh = $(h1) $(h2) $(h3) $(h4) $(h5)
X
Xc1 = addng.c art.c artio.c artsrch.c autosub.c backpage.c bits.c cache.c
Xc2 = charsubst.c decode.c edit_dist.c final.c hash.c head.c help.c init.c
Xc3 = intrp.c kfile.c last.c $(ndirc) ng.c ngdata.c nghash.c ngsrch.c ngstuff.c
Xc4 = only.c rcln.c rcstuff.c respond.c rthread.c rt-mt.c rt-ov.c
Xc5 = rt-process.c rt-page.c rt-select.c rt-util.c rt-wumpus.c search.c
Xc6 = $(strftimec) sw.c term.c trn.c util.c util2.c unship.c uudecode.c
Xc7 = $(nntpsrc) charsubst.c
X
Xc = $(c1) $(c2) $(c3) $(c4) $(c5) $(c6) $(c7)
X
Xobj1 = addng.o art.o artio.o artsrch.o autosub.o backpage.o bits.o cache.o
Xobj2 = charsubst.o decode.o edit_dist.o final.o hash.o head.o help.o init.o
Xobj3 = intrp.o kfile.o last.o $(ndiro) ng.o ngdata.o nghash.o ngsrch.o
Xobj4 = ngstuff.o only.o rcln.o rcstuff.o respond.o rthread.o rt-mt.o rt-ov.o
Xobj5 = rt-process.o rt-page.o rt-select.o rt-util.o rt-wumpus.o search.o
Xobj6 = $(strftimeo) sw.o term.o trn.o util.o util2.o unship.o uudecode.o
Xobj7 = parsedate.o $(nntpobj1) $(nntpobj2)
X
Xobj = $(obj1) $(obj2) $(obj3) $(obj4) $(obj5) $(obj6) $(obj7)
X
Xaddedbymake = $(public) $(private) $(util) Makefile.old config.h\
X	parsedate.c cppstdin all
X
X# grrr
XSHELL = /bin/sh
X
X.c.o:
X	$(CC) -c $(CFLAGS) $<
X
Xall: $(public) $(private) $(util) Makefile
X	$(TOUCH) all
X
Xtrn: $(obj)
X	$(CC) $(LDFLAGS) $(obj) $(libs) -o trn
X#NNTP
X#NNTPnntpinit.o: nntpinit.c
X#NNTP	$(CC) -c $(CFLAGS) $(NNTPFLAGS) $<
X#NNTP
X#NNTPnntplist: nntplist.o util2.o $(nntpobj1)
X#NNTP	$(CC) $(LDFLAGS) nntplist.o util2.o $(nntpobj1) -o nntplist $(libs)
X
Xtrn-artchk: trn-artchk.o util2.o $(nntpobj1)
X	$(CC) $(LDFLAGS) trn-artchk.o util2.o $(nntpobj1) -o trn-artchk $(libs)
X
Xparsedate.c: parsedate.y
X	@echo 'Expect 6 shift/reduce conflicts'
X	$(YACC) $(srcdir)/parsedate.y
X	mv -f y.tab.c parsedate.c
X#STRFTIME
X#STRFTIME$(strftimeo): $(strftimec)
X#STRFTIME	$(CC) -c $(CFLAGS) $(strftimec)
X
Xunipatch: unipatch.o
X	$(CC) $(LDFLAGS) unipatch.o -o unipatch
X
XPnews.header: Pnews
X
X# if a .h file depends on another .h file...
X$(h):
X	-$(TOUCH) $@
X
Xinstall: $(public) $(private) $(manpages)
X# won't work with csh
X	export PATH || exit 1
X	- (cd `./filexp $(rnbin)`; mv trn trn.old)
X	- if test `pwd` != `./filexp $(rnbin)`; then cp $(public) `./filexp $(rnbin)`;\
X (cd `./filexp $(rnbin)`; chmod o+x $(public); strip trn trn-artchk $(nntpbin)); fi
X	- if test $(installfilexp) = Rnfilexp; then cp ./filexp `./filexp $(rnbin)/Rnfilexp`; fi
X	- ./makedir `./filexp $(rnlib)`
X	- chmod o+r `./filexp $(rnlib)`
X	- if test `pwd` != `./filexp $(rnlib)`; then cp $(srcdir)/INIT $(private) `./filexp $(rnlib)`; fi
X	- if test ! -f `./filexp $(rnlib)/newsnews`; then cp newsnews `./filexp $(rnlib)`; fi
X	- (cd `./filexp $(rnlib)`; chmod o+x $(private); chmod o+r INIT newsnews)
X	- if test "X$(mansrc)" != "X" -a "X`pwd`" != X`./filexp $(mansrc)`; then \
Xfor page in $(manpages); do \
Xdest=$(mansrc)/`basename $$page .1`.$(manext); \
Xrm -f $$dest; cp $(srcdir)/$$page $$dest; chmod 444 $$dest; \
Xdone; \
Xfi
X
Xclean:
X	@echo 'Use "make realclean" to also remove the Makefile.'
X	@echo 'Use "make spotless" to even remove config.sh.'
X	rm -rf UU
X	rm -f *.o core $(addedbymake)
X
Xrealclean:
X	@echo 'You can use "Configure -S ; make depend ; make" to reverse this.'
X	rm -rf UU
X	rm -f *.o core $(addedbymake) Makefile
X
Xspotless:
X	rm -rf UU .config
X	rm -f *.o core $(addedbymake) Makefile config.sh
X
X# The following lint has practically everything turned on.  Unfortunately,
X# you have to wade through a lot of mumbo jumbo that can't be suppressed.
X# If the source file has a /*NOSTRICT*/ somewhere, ignore the lint message
X# for that spot.
X
Xlint: $(c) parsedate.c
X	$(LINT) $(lintflags) $(defs) $? > trn.fuzz 2>&1
X
Xsabertrn: $(c) parsedate.c
X	#load $(c) parsedate.c $(libs)
X
Xdepend: config.h Makefile makedepend
X	./makedepend
X
X# AUTOMATICALLY GENERATED MAKE DEPENDENCIES--PUT NOTHING BELOW THIS LINE
X!NO!SUBS!
Xif test -f dependencies; then
X	$cat dependencies >>Makefile
Xelse
X	$cat $srcdir/dependencies >>Makefile
Xfi
Xcase "$d_nntp" in
Xdefine)	sed < Makefile -e '/^#NNTP/s/^#NNTP//' > Makefile.new ;;
X*)	sed < Makefile -e '/^#NNTP/d' > Makefile.new ;;
Xesac
Xcase "$strftimec" in
X''|' ')	sed < Makefile.new -e '/^#STRFTIME/d' > Makefile ;;
X*)	sed < Makefile.new -e '/^#STRFTIME/s/^#STRFTIME//' > Makefile ;;
Xesac
Xrm Makefile.new
X$eunicefix Makefile
SHAR_EOF
  $shar_touch -am 1118220194 'trn-3.6/Makefile.SH' &&
  chmod 0644 'trn-3.6/Makefile.SH' ||
  echo 'restore of trn-3.6/Makefile.SH failed'
  shar_count="`wc -c < 'trn-3.6/Makefile.SH'`"
  test 6589 -eq "$shar_count" ||
    echo "trn-3.6/Makefile.SH: original size 6589, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/rt-mt.c ==============
if test -f 'trn-3.6/rt-mt.c' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/rt-mt.c (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/rt-mt.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/rt-mt.c' &&
X/* $Id: rt-mt.c,v 3.0 1992/12/14 00:14:13 davison Trn $
X*/
X/* The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "intrp.h"
X#include "trn.h"
X#include "cache.h"
X#include "bits.h"
X#include "ng.h"
X#include "ngdata.h"
X#include "rcln.h"
X#include "util.h"
X#include "hash.h"
X#include "nntp.h"
X#include "rthread.h"
X#include "rt-process.h"
X#include "INTERN.h"
X#include "rt-mt.h"
X
Xextern HASHTABLE *msgid_hash;
X
X#ifndef USE_XTHREAD
Xstatic FILE *fp;
X#endif
Xstatic bool word_same, long_same;
Xstatic BMAP my_bmap, mt_bmap;
X
Xstatic char *strings = Nullch;
Xstatic WORD *author_cnts = 0;
Xstatic WORD *ids = 0;
X
Xstatic ARTICLE **article_array = 0;
Xstatic SUBJECT **subject_array = 0;
Xstatic char **author_array = 0;
X
Xstatic TOTAL total;
Xstatic PACKED_ROOT p_root;
Xstatic PACKED_ARTICLE p_article;
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*/
Xbool
Xmt_init()
X{
X    int i;
X#ifdef USE_XTHREAD
X    long size;
X#endif
X    bool success = TRUE;			/* I'm an optimist */
X
X    word_same = long_same = TRUE;
X#ifdef USE_XTHREAD
X    nntp_command("XTHREAD DBINIT");
X    size = nntp_readcheck();
X    if (size < 0)
X	return FALSE;
X    size = nntp_read((char*)&mt_bmap, (long)sizeof (BMAP));
X    if (size >= sizeof (BMAP) - 1) {
X#else /* !USE_XTHREAD */
X    if ((fp = fopen(filexp(DBINIT), FOPEN_RB)) != Nullfp
X     && fread((char*)&mt_bmap, 1, sizeof (BMAP), fp) >= sizeof (BMAP) - 1) {
X#endif
X	if (mt_bmap.version != DB_VERSION) {
X	    printf("\nMthreads database is the wrong version -- ignoring it.\n")
X		FLUSH;
X	    return FALSE;
X	}
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    } else
X	success = FALSE;
X#ifdef USE_XTHREAD
X    while (nntp_read(ser_line, (long)sizeof ser_line))
X	;		/* trash any extraneous bytes */
X#else
X    if (fp != Nullfp)
X	fclose(fp);
X#endif
X    return success;
X}
X
X/* Open and process the data in the group's thread file.  Returns TRUE unless
X** we discovered a bogus thread file, destroyed the cache, and re-built it.
X*/
Xbool
Xmt_data()
X{
X    bool success = TRUE;
X#ifdef USE_XTHREAD		/* use remote thread file? */
X    long size;
X
X    nntp_command("XTHREAD THREAD");
X    size = nntp_readcheck();
X    if (size < 0)
X	return TRUE;
X
X#ifdef VERBOSE
X    IF(verbose)
X	printf("\nGetting thread file."), fflush(stdout);
X#endif
X    if (nntp_read((char*)&total, (long)sizeof (TOTAL)) < sizeof (TOTAL))
X	goto exit;
X
X#else /* !USE_XTHREAD */
X    if ((fp = fopen(mt_name(ngname), FOPEN_RB)) == Nullfp)
X	return TRUE;
X#ifdef VERBOSE
X    IF(verbose)
X	printf("\nReading thread file."), fflush(stdout);
X#endif
X
X    if (fread((char*)&total, 1, sizeof (TOTAL), fp) < sizeof (TOTAL))
X	goto exit;
X
X#endif /* !USE_XTHREAD */
X
X    lp_bmap(&total.first, 4);
X    wp_bmap(&total.root, 5);
X    if (!total.root) {
X	tweak_data();
X	goto exit;
X    }
X    if (total.last > lastart)
X#ifdef USE_NNTP
X	total.last = lastart;
X#else
X	grow_cache(total.last);
X#endif
X
X    if (read_authors()
X     && read_subjects()
X     && read_roots()
X     && read_articles()
X     && read_ids())
X    {
X	tweak_data();
X	first_cached = absfirst;
X	last_cached = (total.last < absfirst ? absfirst-1: total.last);
X	cached_all_in_range = TRUE;
X	goto exit;
X    }
X    /* Something failed.  Safefree takes care of checking if some items
X    ** were already freed.  Any partially-allocated structures were freed
X    ** before we got here.  All other structures are cleaned up now.
X    */
X    close_cache();
X    safefree(&strings);
X    safefree((char**)&article_array);
X    safefree((char**)&subject_array);
X    safefree((char**)&author_array);
X    safefree((char**)&ids);
X    try_mt = 0;
X    build_cache();
X    try_mt = 1;
X    success = FALSE;
X
Xexit:
X#ifdef USE_XTHREAD
X    while (nntp_read(ser_line, (long)sizeof ser_line))
X	;		/* trash any extraneous bytes */
X#else
X    fclose(fp);
X#endif
X    return success;
X}
X
X#ifndef USE_XTHREAD
X/* Change a newsgroup name into the name of the thread data file.  We
X** subsitute any '.'s in the group name into '/'s (unless LONG_THREAD_NAMES
X** is defined), prepend the path, and append the '/.thread' or '.th' on to
X** the end.
X*/
Xstatic char *
Xmt_name(group)
Xchar *group;
X{
X#ifdef LONG_THREAD_NAMES
X    sprintf(buf, "%s/%s", threaddir, group);
X#else
X    register char *cp;
X
X    cp = strcpy(buf, threaddir) + strlen(threaddir);
X    *cp++ = '/';
X    strcpy(cp, group);
X    while ((cp = index(cp, '.')))
X	*cp = '/';
X    if (threaddir == spool)
X	strcat(buf, "/.thread");
X    else
X	strcat(buf, ".th");
X#endif
X    return buf;
X}
X#endif
X
Xstatic char *subject_strings, *string_end;
X
X/* The author information is an array of use-counts, followed by all the
X** null-terminated strings crammed together.  The subject strings are read
X** in at the same time, since they are appended to the end of the author
X** strings.
X*/
Xstatic int
Xread_authors()
X{
X    register int count;
X    register char *string_ptr, **author_ptr;
X
X    if (!read_item((char**)&author_cnts, (MEM_SIZE)total.author*sizeof (WORD)))
X	return 0;
X    safefree((char**)&author_cnts);   /* we don't need these */
X
X    if (!read_item(&strings, (MEM_SIZE)total.string1))
X	return 0;
X
X    string_ptr = strings;
X    string_end = string_ptr + total.string1;
X    if (string_end[-1] != '\0') {
X	/*error("first string table is invalid.\n");*/
X	return 0;
X    }
X
X    /* We'll use this array to point each article at its proper author
X    ** (the packed values were saved as indexes).
X    */
X    author_array = (char**)safemalloc(total.author * sizeof (char*));
X    author_ptr = author_array;
X
X    for (count = total.author; count; count--) {
X	if (string_ptr >= string_end)
X	    break;
X	*author_ptr++ = string_ptr;
X	string_ptr += strlen(string_ptr) + 1;
X    }
X    subject_strings = string_ptr;
X
X    if (count) {
X	/*error("author unpacking failed.\n");*/
X	return 0;
X    }
X    return 1;
X}
X
X/* The subject values consist of the crammed-together null-terminated strings
X** (already read in above) and the use-count array.  They were saved in the
X** order that the roots require while being unpacked.
X*/
Xstatic int
Xread_subjects()
X{
X    register int count;
X    register char *string_ptr;
X    register SUBJECT **subj_ptr;
X    WORD *subject_cnts;
X
X    if (!read_item((char**)&subject_cnts,
X		   (MEM_SIZE)total.subject * sizeof (WORD))) {
X	/* (Error already logged.) */
X	return 0;
X    }
X    free((char*)subject_cnts);		/* we don't need these */
X
X    /* Use this array when unpacking the article's subject offset. */
X    subject_array = (SUBJECT**)safemalloc(total.subject * sizeof (SUBJECT*));
X    subj_ptr = subject_array;
X
X    string_ptr = subject_strings;	/* string_end is already set */
X
X    for (count = total.subject; count; count--) {
X	int len;
X	ARTICLE arty;
X	if (string_ptr >= string_end)
X	    break;
X	len = strlen(string_ptr);
X	arty.subj = 0;
X	set_subj_line(&arty, string_ptr, len);
X	if (len == 72)
X	    arty.subj->flags |= SF_SUBJTRUNCED;
X	string_ptr += len + 1;
X	*subj_ptr++ = arty.subj;
X    }
X    if (count || string_ptr != string_end) {
X	/*error("subject data is invalid.\n");*/
X	return 0;
X    }
X    return 1;
X}
X
X/* Read in the packed root structures to set each subject's thread article
X** offset.  This gets turned into a real pointer later.
X*/
Xstatic int
Xread_roots()
X{
X    register int count, i;
X    register SUBJECT **subj_ptr;
X    int ret;
X
X    subj_ptr = subject_array;
X
X    for (count = total.root; count--; ) {
X#ifdef USE_XTHREAD
X	ret = nntp_read((char*)&p_root, (long)sizeof (PACKED_ROOT));
X#else
X	ret = fread((char*)&p_root, 1, sizeof (PACKED_ROOT), fp);
X#endif
X	if (ret != sizeof (PACKED_ROOT)) {
X	    /*error("failed root read -- %d bytes instead of %d.\n",
X		ret, sizeof (PACKED_ROOT));*/
X	    return 0;
X	}
X	wp_bmap(&p_root.articles, 3);	/* converts subject_cnt too */
X	if (p_root.articles < 0 || p_root.articles >= total.article) {
X	    /*error("root has invalid values.\n");*/
X	    return 0;
X	}
X	i = p_root.subject_cnt;
X	if (i <= 0 || (subj_ptr - subject_array) + i > total.subject) {
X	    /*error("root has invalid values.\n");*/
X	    return 0;
X	}
X	subj_ptr[i-1]->thread_link = subj_ptr[0];
X	while (i--) {
X	    union { ARTICLE *ap; int num; } uni;
X	    if (i)
X		subj_ptr[0]->thread_link = subj_ptr[1];
X	    uni.num = p_root.articles;
X	    (*subj_ptr++)->thread = uni.ap;
X	}
X    }
X    return 1;
X}
X
Xstatic bool invalid_data;
X
X/* A simple routine that checks the validity of the article's subject value.
X** A -1 means that it is NULL, otherwise it should be an offset into the
X** subject array we just unpacked.
X*/
Xstatic SUBJECT *
Xthe_subject(num)
XWORD num;
X{
X    if (num == -1)
X	return Nullsubj;
X    if (num < 0 || num >= total.subject) {
X	/*printf("Invalid subject in thread file: %d [%ld]\n", num, art_num);*/
X	invalid_data = TRUE;
X	return Nullsubj;
X    }
X    return subject_array[num];
X}
X
X/* Ditto for author checking. */
Xstatic char *
Xthe_author(num)
XWORD num;
X{
X    if (num == -1)
X	return Nullch;
X    if (num < 0 || num >= total.author) {
X	/*error("invalid author in thread file: %d [%ld]\n", num, art_num);*/
X	invalid_data = TRUE;
X	return Nullch;
X    }
X    return savestr(author_array[num]);
X}
X
X/* Our parent/sibling information is a relative offset in the article array.
X** zero for none.  Child values are always found in the very next array
X** element if child_cnt is non-zero.
X*/
Xstatic ARTICLE *
Xthe_article(relative_offset, num)
XWORD relative_offset;
Xint num;
X{
X    union { ARTICLE *ap; int num; } uni;
X
X    if (!relative_offset)
X	return Nullart;
X    num += relative_offset;
X    if (num < 0 || num >= total.article) {
X	/*error("invalid article offset in thread file.\n");*/
X	invalid_data = TRUE;
X	return Nullart;
X    }
X    uni.num = num+1;
X    return uni.ap;		/* slip them an offset in disguise */
X}
X
X/* Read the articles into their trees.  Point everything everywhere. */
Xstatic int
Xread_articles()
X{
X    register int count;
X    register ARTICLE *article, **art_ptr;
X    int ret;
X
X    /* Build an array to interpret interlinkages of articles. */
X    article_array = (ARTICLE**)safemalloc(total.article * sizeof (ARTICLE*));
X    art_ptr = article_array;
X
X    invalid_data = FALSE;
X    for (count = 0; count < total.article; count++) {
X#ifdef USE_XTHREAD
X	ret = nntp_read((char*)&p_article, (long)sizeof (PACKED_ARTICLE));
X#else
X	ret = fread((char*)&p_article, 1, sizeof (PACKED_ARTICLE), fp);
X#endif
X	if (ret != sizeof (PACKED_ARTICLE)) {
X	    /*error("failed article read -- %d bytes instead of %d.\n",
X		ret, sizeof (PACKED_ARTICLE));*/
X	    return 0;
X	}
X	lp_bmap(&p_article.num, 2);
X	wp_bmap(&p_article.subject, 8);
X
X#ifdef USE_NNTP
X	article = *art_ptr++ = allocate_article(p_article.num > lastart?
X						0 : p_article.num);
X#else
X	article = *art_ptr++ = allocate_article(p_article.num);
X#endif
X	article->date = p_article.date;
X#ifndef DBM_XREFS
X	if (olden_days < 2 && !(p_article.flags & HAS_XREFS))
X	    article->xrefs = nullstr;
X#endif
X	article->from = the_author(p_article.author);
X	article->parent = the_article(p_article.parent, count);
X	article->child1 = the_article(p_article.child_cnt ? 1 : 0, count);
X	article->sibling = the_article(p_article.sibling, count);
X	article->subj = the_subject(p_article.subject);
X	if (invalid_data) {
X	    /* (Error already logged.) */
X	    return 0;
X	}
X	/* This is ok because parent articles precede their children */
X	if (article->parent) {
X	    union { ARTICLE *ap; int num; } uni;
X	    uni.ap = article->parent;
X	    article->parent = article_array[uni.num-1];
X	}
X	if (article->subj) {
X	    if (!(article->flags & AF_MISSING)) {
X		article->flags |= AF_FROMTRUNCED | AF_THREADED
X		    | ((p_article.flags & ROOT_ARTICLE)? 0 : AF_HAS_RE);
X	    }
X	    /* Give this subject to any faked parent articles */
X	    while (article->parent && !article->parent->subj) {
X		article->parent->subj = article->subj;
X		article = article->parent;
X	    }
X	} else
X	    article->flags |= AF_FAKE|AF_MISSING;
X    }
X
X    /* We're done with most of the pointer arrays at this point. */
X    safefree((char**)&subject_array);
X    safefree((char**)&author_array);
X    safefree(&strings);
X
X    return 1;
X}
X
X/* Read the message-id strings and attach them to each article.  The data
X** format consists of the mushed-together null-terminated strings (a domain
X** name followed by all its unique-id prefixes) and then the article offsets
X** to which they belong.  The first domain name was omitted, as it is a null
X** domain for those truly weird message-id's without '@'s.
X*/
Xstatic int
Xread_ids()
X{
X    register ARTICLE *article;
X    register char *string_ptr;
X    register int i, count, len, len2;
X
X    if (!read_item(&strings, (MEM_SIZE)total.string2)
X     || !read_item((char**)&ids,
X		(MEM_SIZE)(total.article+total.domain+1) * sizeof (WORD))) {
X	return 0;
X    }
X    wp_bmap(ids, total.article + total.domain + 1);
X
X    string_ptr = strings;
X    string_end = string_ptr + total.string2;
X
X    if (string_end[-1] != '\0') {
X	/*error("second string table is invalid.\n");*/
X	return 0;
X    }
X
X    for (i = 0, count = total.domain + 1; count--; i++) {
X	if (i) {
X	    if (string_ptr >= string_end) {
X		/*error("error unpacking domain strings.\n");*/
X		return 0;
X	    }
X	    sprintf(buf, "@%s", string_ptr);
X	    len = strlen(string_ptr) + 1;
X	    string_ptr += len;
X	} else {
X	    *buf = '\0';
X	    len = 0;
X	}
X	if (ids[i] != -1) {
X	    if (ids[i] < 0 || ids[i] >= total.article) {
X		/*error("error in id array.\n");*/
X		return 0;
X	    }
X	    article = article_array[ids[i]];
X	    for (;;) {
X		if (string_ptr >= string_end) {
X		    /*error("error unpacking domain strings.\n");*/
X		    return 0;
X		}
X		len2 = strlen(string_ptr);
X		article->msgid = safemalloc(len2 + len + 2 + 1);
X		sprintf(article->msgid, "<%s%s>", string_ptr, buf);
X		string_ptr += len2 + 1;
X		if (msgid_hash)
X		{
X		    HASHDATUM data;
X		    if ((article->flags & AF_TMPMEM) == AF_TMPMEM) {
X			data.dat_ptr = (char*)article;
X			data.dat_len = 0;
X		    } else {
X			data.dat_ptr = Nullch;
X			data.dat_len = article_num(article);
X		    }
X		    hashstore(msgid_hash, article->msgid, len2+len+2, data);
X		}
X		if (++i >= total.article + total.domain + !count) {
X		    /*error("overran id array unpacking domains.\n");*/
X		    return 0;
X		}
X		if (ids[i] != -1) {
X		    if (ids[i] < 0 || ids[i] >= total.article)
X			return 0;
X		    article = article_array[ids[i]];
X		} else
X		    break;
X	    }
X	}
X    }
X    safefree((char**)&ids);
X    safefree(&strings);
X
X    return 1;
X}
X
X/* And finally, turn all the links into real pointers and mark missing
X** articles as read.
X*/
Xstatic void
Xtweak_data()
X{
X    register int count;
X    register ARTICLE *ap, **art_ptr;
X    register SUBJECT *sp;
X    register ART_NUM i;
X    union { ARTICLE *ap; int num; } uni;
X
X    for (sp = first_subject; sp; sp = sp->next) {
X	uni.ap = sp->thread;
X	sp->thread = article_array[uni.num];
X    }
X    art_ptr = article_array;
X    for (count = total.article; count--; ) {
X	ap = *art_ptr++;
X	if (ap->child1) {
X	    uni.ap = ap->child1;
X	    ap->child1 = article_array[uni.num-1];
X	}
X	if (ap->sibling) {
X	    uni.ap = ap->sibling;
X	    ap->sibling = article_array[uni.num-1];
X	}
X    }
X
X    art_ptr = article_array;
X    for (count = total.article; count--; ) {
X	ap = *art_ptr++;
X	if (!(ap->flags & AF_MISSING))
X	    cache_article(ap);
X    }
X
X    /* Mark any missing articles as read */
X    for (i = absfirst, ap = article_ptr(i); i <= total.last; i++, ap++) {
X	if ((ap->flags & (AF_CACHED|AF_MISSING)) == AF_CACHED)
X	    check_poster(ap);
X	else
X	    onemissing(ap);
X    }
X    safefree((char**)&article_array);
X}
X
X/* A shorthand for reading a chunk of the file into a malloc'ed array.
X*/
Xstatic int
Xread_item(dest, len)
Xchar **dest;
XMEM_SIZE len;
X{
X    long ret;
X
X    *dest = safemalloc(len);
X#ifdef USE_XTHREAD
X    ret = nntp_read(*dest, (long)len);
X#else
X    ret = fread(*dest, 1, (int)len, fp);
X#endif
X    if (ret != len) {
X	free(*dest);
X	*dest = Nullch;
X	return 0;
X    }
X    putchar('.'), fflush(stdout);
X    return 1;
X}
X
X/* Free some memory if it hasn't already been freed.
X*/
Xstatic void
Xsafefree(pp)
Xchar **pp;
X{
X    if (*pp) {
X	free(*pp);
X	*pp = Nullch;
X    }
X}
X
X/* Determine this machine's byte map for WORDs and LONGs.  A byte map is an
X** array of BYTEs (sizeof (WORD) or sizeof (LONG) of them) with the 0th BYTE
X** being the byte number of the high-order byte in my <type>, and so forth.
X*/
Xstatic void
Xmybytemap(map)
XBMAP *map;
X{
X    union {
X	BYTE b[sizeof (LONG)];
X	WORD w;
X	LONG l;
X    } u;
X    register BYTE *mp;
X    register int i, j;
X
X    mp = &map->w[sizeof (WORD)];
X    u.w = 1;
X    for (i = sizeof (WORD); i > 0; i--) {
X	for (j = 0; j < sizeof (WORD); j++) {
X	    if (u.b[j] != 0)
X		break;
X	}
X	if (j == sizeof (WORD))
X	    goto bad_news;
X	*--mp = j;
X	while (u.b[j] != 0 && u.w)
X	    u.w <<= 1;
X    }
X
X    mp = &map->l[sizeof (LONG)];
X    u.l = 1;
X    for (i = sizeof (LONG); i > 0; i--) {
X	for (j = 0; j < sizeof (LONG); j++) {
X	    if (u.b[j] != 0)
X		break;
X	}
X	if (j == sizeof (LONG)) {
X	  bad_news:
X	    /* trouble -- set both to *something* consistent */
X	    for (j = 0; j < sizeof (WORD); j++)
X		map->w[j] = j;
X	    for (j = 0; j < sizeof (LONG); j++)
X		map->l[j] = j;
X	    return;
X	}
X	*--mp = j;
X	while (u.b[j] != 0 && u.l)
X	    u.l <<= 1;
X    }
X}
X
X/* Transform each WORD's byte-ordering in a buffer of the designated length.
X*/
Xstatic void
Xwp_bmap(buf, len)
XWORD *buf;
Xint len;
X{
X    union {
X	BYTE b[sizeof (WORD)];
X	WORD w;
X    } in, out;
X    register int i;
X
X    if (word_same)
X	return;
X
X    while (len--) {
X	in.w = *buf;
X	for (i = 0; i < sizeof (WORD); i++)
X	    out.b[my_bmap.w[i]] = in.b[mt_bmap.w[i]];
X	*buf++ = out.w;
X    }
X}
X
X/* Transform each LONG's byte-ordering in a buffer of the designated length.
X*/
Xstatic void
Xlp_bmap(buf, len)
XLONG *buf;
Xint len;
X{
X    union {
X	BYTE b[sizeof (LONG)];
X	LONG l;
X    } in, out;
X    register int i;
X
X    if (long_same)
X	return;
X
X    while (len--) {
X	in.l = *buf;
X	for (i = 0; i < sizeof (LONG); i++)
X	    out.b[my_bmap.l[i]] = in.b[mt_bmap.l[i]];
X	*buf++ = out.l;
X    }
X}
SHAR_EOF
  $shar_touch -am 1118220194 'trn-3.6/rt-mt.c' &&
  chmod 0644 'trn-3.6/rt-mt.c' ||
  echo 'restore of trn-3.6/rt-mt.c failed'
  shar_count="`wc -c < 'trn-3.6/rt-mt.c'`"
  test 18258 -eq "$shar_count" ||
    echo "trn-3.6/rt-mt.c: original size 18258, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/rt-mt.h ==============
if test -f 'trn-3.6/rt-mt.h' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/rt-mt.h (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/rt-mt.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/rt-mt.h' &&
X/* $Id: rt-mt.h,v 3.0 1992/12/14 00:14:09 davison Trn $
X*/
X/* The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
Xbool mt_init _((void));
Xbool mt_data _((void));
X
X/* Stuff internal to rt-mt.c */
X
X#ifdef DOINIT
X
X#define DB_VERSION	2
X
Xtypedef char		BYTE;
Xtypedef short		WORD;
X#ifndef __alpha
Xtypedef long		LONG;
X#else
Xtypedef int		LONG;
X#endif
X
X#define ROOT_ARTICLE	0x0001		/* article flag definitions */
X#define HAS_XREFS	0x0004		/* article has an xref line */
X
Xtypedef struct {
X    LONG root_num;
X    WORD articles;
X    WORD article_cnt;
X    WORD subject_cnt;
X    WORD pad_hack;
X} PACKED_ROOT;
X
Xtypedef struct {
X    LONG num;
X    LONG date;
X    WORD subject, author;
X    WORD flags;
X    WORD child_cnt;
X    WORD parent;
X    WORD padding;
X    WORD sibling;
X    WORD root;
X} PACKED_ARTICLE;
X
Xtypedef struct Total {
X    LONG first, last;
X    LONG string1;
X    LONG string2;
X    WORD root;
X    WORD article;
X    WORD subject;
X    WORD author;
X    WORD domain;
X    WORD pad_hack;
X} TOTAL;
X
Xtypedef struct {
X    BYTE l[sizeof (LONG)];
X    BYTE w[sizeof (WORD)];
X    BYTE version;
X    BYTE pad_hack;
X} BMAP;
X
X# ifndef USE_XTHREAD
Xstatic char *mt_name _((char*));
X# endif
Xstatic int read_authors _((void));
Xstatic int read_subjects _((void));
Xstatic int read_roots _((void));
Xstatic int read_articles _((void));
Xstatic int read_ids _((void));
Xstatic void tweak_data _((void));
Xstatic int read_item();
Xstatic void safefree _((char**));
Xstatic void mybytemap _((BMAP*));
Xstatic void wp_bmap(), lp_bmap();
X
X#endif
SHAR_EOF
  $shar_touch -am 0816122093 'trn-3.6/rt-mt.h' &&
  chmod 0644 'trn-3.6/rt-mt.h' ||
  echo 'restore of trn-3.6/rt-mt.h failed'
  shar_count="`wc -c < 'trn-3.6/rt-mt.h'`"
  test 1642 -eq "$shar_count" ||
    echo "trn-3.6/rt-mt.h: original size 1642, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/rt-ov.c ==============
if test -f 'trn-3.6/rt-ov.c' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/rt-ov.c (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/rt-ov.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/rt-ov.c' &&
X/* $Id: rt-ov.c,v 3.0 1992/12/14 00:14:13 davison Trn $
X*/
X/* The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "trn.h"
X#include "cache.h"
X#include "bits.h"
X#include "head.h"
X#include "ngdata.h"
X#include "util.h"
X#include "ng.h"
X#include "nntp.h"
X#include "term.h"
X#include "final.h"
X#include "hash.h"
X#include "rthread.h"
X#include "rt-process.h"
X#include "rt-util.h"
X#include "overview.h"
X#include "INTERN.h"
X#include "rt-ov.h"
X
Xbool
Xov_init()
X{
X#ifdef USE_XOVER
X    /* Check if the server is XOVER compliant (we're not in a group, BTW) */
X    nntp_command("XOVER");
X    nntp_check(FALSE);
X    if (atoi(ser_line) == NNTP_BAD_COMMAND_VAL)
X	return FALSE;
X    /* paranoia reins supreme */
X    if (*ser_line == NNTP_CLASS_OK) {
X	do {
X	    while (nntp_gets(ser_line, sizeof ser_line) == 0) {
X		/* A line w/o a newline is too long to be the end of the
X		** list, so grab the rest of this line and try again. */
X		while (nntp_gets(ser_line, sizeof ser_line) == 0)
X		    ;
X	    }
X	} while (!NNTP_LIST_END(ser_line));
X    }
X#endif
X    return TRUE;
X}
X
X#ifndef OV_XREFS
X# ifdef OV_OTHERS_HAS_XREFS
Xbool ov_files_have_xrefs = TRUE;	/* set once per session */
SHAR_EOF
  : || echo 'restore of trn-3.6/rt-ov.c failed'
fi
echo 'End of archive part 7'
echo 'File trn-3.6/rt-ov.c is continued in part 8'
echo 8 > _sharseq.tmp
exit 0


