Article 2700 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: v29i042: trn-3.6 - threaded newsreader based on RN, V3.6, Part05/14
Date: 29 Oct 1995 01:32:50 -0800
Organization: Vixie Enterprises
Lines: 3654
Sender: vixie@vix.com
Approved: vixie@gw.home.vix.com
Message-ID: <46vho2$t4k@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 42
Archive-Name: trn-3.6/part05

#!/bin/sh
# This is `part05' (part 5 of a multipart archive).
# Do not concatenate these parts, unpack them in order with `/bin/sh'.
# File `trn-3.6/intrp.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" != 5; then
  echo "Please unpack part $shar_sequence next!"
  exit 1
fi
if test ! -f _sharnew.tmp; then
  echo 'x - still skipping trn-3.6/intrp.c'
else
  echo 'x - continuing file trn-3.6/intrp.c'
  sed 's/^X//' << 'SHAR_EOF' >> 'trn-3.6/intrp.c' &&
X	    case '`': {
X		pattern = skipinterp(pattern+1,"`");
X		break;
X	    }
X#endif
X#ifdef PROMPTTTY
X	    case '"':
X		pattern = skipinterp(pattern+1,"\"");
X		break;
X#endif
X	    default:
X		break;
X	    }
X	    pattern++;
X	}
X	else {
X	    if (*pattern == '^' && pattern[1])
X		pattern += 2;
X	    else if (*pattern == '\\' && pattern[1])
X		pattern += 2;
X	    else
X		pattern++;
X	}
X    }
Xgetout:
X    return pattern;			/* where we left off */
X}
X#endif
X
X/* interpret interpolations */
X
Xchar *
Xdointerp(dest,destsize,pattern,stoppers)
Xregister char *dest;
Xregister int destsize;
Xregister char *pattern;
Xchar *stoppers;
X{
X    char *subj_buf = Nullch;
X    char *ngs_buf = Nullch;
X    char *refs_buf = Nullch;
X    char *artid_buf = Nullch;
X    char *reply_buf = Nullch;
X    char *from_buf = Nullch;
X    char *path_buf = Nullch;
X    char *follow_buf = Nullch;
X    char *dist_buf = Nullch;
X    char *line_buf = Nullch;
X    char *line_split = Nullch;
X    char *orig_dest = dest;
X    register char *s, *h;
X    register int i;
X    char scrbuf[512];
X    char spfbuf[512];
X    static char *input_str = Nullch;
X    static int input_siz = 0;
X    bool upper = FALSE;
X    bool lastcomp = FALSE;
X    bool re_quote = FALSE;
X    int tick_quote = 0;
X    bool address_parse = FALSE;
X    bool comment_parse = FALSE;
X    bool proc_sprintf = FALSE;
X    int metabit = 0;
X
X    while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
X#ifdef DEBUG
X	if (debug & DEB_INTRP)
X	    printf("dointerp till %s at %s\n",stoppers?stoppers:"",pattern);
X#endif
X	if (*pattern == '%' && pattern[1]) {
X	    upper = FALSE;
X	    lastcomp = FALSE;
X	    re_quote = FALSE;
X	    tick_quote = 0;
X	    address_parse = FALSE;
X	    comment_parse = FALSE;
X	    proc_sprintf = FALSE;
X	    for (s=Nullch; !s; ) {
X		switch (*++pattern) {
X		case '^':
X		    upper = TRUE;
X		    break;
X		case '_':
X		    lastcomp = TRUE;
X		    break;
X		case '\\':
X		    re_quote = TRUE;
X		    break;
X		case '\'':
X		    tick_quote++;
X		    break;
X		case '>':
X		    address_parse = TRUE;
X		    break;
X		case ')':
X		    comment_parse = TRUE;
X		    break;
X		case ':':
X		    proc_sprintf = TRUE;
X		    h = spfbuf;
X		    *h++ = '%';
X		    pattern++;	/* Skip over ':' */
X		    while (*pattern
X		     && (*pattern=='.' || *pattern=='-' || isdigit(*pattern))) {
X			*h++ = *pattern++;
X		    }
X		    *h++ = 's';
X		    *h++ = '\0';
X		    pattern--;
X		    break;
X		case '/':
X#ifdef ARTSEARCH
X		    s = scrbuf;
X		    if (!index("/?g",pattern[-2]))
X			*s++ = '/';
X		    strcpy(s,lastpat);
X		    s += strlen(s);
X		    if (pattern[-2] != 'g') {
X			if (index("/?",pattern[-2]))
X			    *s++ = pattern[-2];
X			else
X			    *s++ = '/';
X			if (art_doread)
X			    *s++ = 'r';
X			if (art_howmuch != ARTSCOPE_SUBJECT) {
X			    *s++ = scopestr[art_howmuch];
X			    if (art_howmuch == ARTSCOPE_ONEHDR) {
X				safecpy(s,art_srchhdr,
X					(sizeof scrbuf) - (s-scrbuf));
X				s = index(s,':') + 1;
X				if (!s)
X				    s = scrbuf+(sizeof scrbuf)-1;
X			    }
X			}
X		    }
X		    *s = '\0';
X		    s = scrbuf;
X#else
X		    s = nullstr;
X#endif
X		    break;
X		case '{':
X		    pattern = cpytill(scrbuf,pattern+1,'}');
X		    if (s = index(scrbuf,'-'))
X			*s++ = '\0';
X		    else
X			s = nullstr;
X		    s = getval(scrbuf,s);
X		    break;
X		case '[':
X		    if (in_ng) {
X			pattern = cpytill(scrbuf,pattern+1,']');
X			i = set_line_type(scrbuf,scrbuf+strlen(scrbuf));
X			if (line_buf)
X			    free(line_buf);
X			s = line_buf = fetchlines(art,i);
X		    }
X		    else
X			s = nullstr;
X		    break;
X#ifdef CONDSUB
X		case '(': {
X		    COMPEX *oldbra_compex = bra_compex;
X		    char rch;
X		    bool matched;
X		    
X		    pattern = dointerp(dest,destsize,pattern+1,"!=");
X		    rch = *pattern;
X		    if (rch == '!')
X			pattern++;
X		    if (*pattern != '=')
X			goto getout;
X		    pattern = cpytill(scrbuf,pattern+1,'?');
X		    if (!*pattern)
X			goto getout;
X		    s = scrbuf;
X		    h = spfbuf;
X		    proc_sprintf = FALSE;
X		    do {
X			switch (*s) {
X			case '^':
X			    *h++ = '\\';
X			    break;
X			case '\\':
X			    *h++ = '\\';
X			    *h++ = '\\';
X			    break;
X			case '%':
X			    proc_sprintf = TRUE;
X			    break;
X			}
X			*h++ = *s;
X		    } while (*s++);
X		    if (proc_sprintf) {
X			dointerp(scrbuf,sizeof scrbuf,spfbuf,Nullch);
X			proc_sprintf = FALSE;
X		    }
X		    if (s = compile(&cond_compex,scrbuf,TRUE,TRUE)) {
X			printf("%s: %s\n",scrbuf,s) FLUSH;
X			pattern += strlen(pattern);
X			free_compex(&cond_compex);
X			goto getout;
X		    }
X		    matched = (execute(&cond_compex,dest) != Nullch);
X		    if (getbracket(&cond_compex, 0)) /* were there brackets? */
X			bra_compex = &cond_compex;
X		    if (matched==(rch == '=')) {
X			pattern = dointerp(dest,destsize,pattern+1,":)");
X			if (*pattern == ':')
X			    pattern = skipinterp(pattern+1,")");
X		    }
X		    else {
X			pattern = skipinterp(pattern+1,":)");
X			if (*pattern == ':')
X			    pattern++;
X			pattern = dointerp(dest,destsize,pattern,")");
X		    }
X		    s = dest;
X		    bra_compex = oldbra_compex;
X		    free_compex(&cond_compex);
X		    break;
X		}
X#endif
X#ifdef BACKTICK
X		case '`': {
X		    FILE *pipefp, *popen();
X
X		    pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"`");
X		    pipefp = popen(scrbuf,"r");
X		    if (pipefp != Nullfp) {
X			int len;
X
X			len = fread(scrbuf,sizeof(char),(sizeof scrbuf)-1,
X			    pipefp);
X			scrbuf[len] = '\0';
X			pclose(pipefp);
X		    }
X		    else {
X			printf("\nCan't run %s\n",scrbuf);
X			*scrbuf = '\0';
X		    }
X		    for (s=scrbuf; *s; s++) {
X			if (*s == '\n') {
X			    if (s[1])
X				*s = ' ';
X			    else
X				*s = '\0';
X			}
X		    }
X		    s = scrbuf;
X		    break;
X		}
X#endif
X#ifdef PROMPTTTY
X		case '"':
X		    pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"\"");
X		    fputs(scrbuf,stdout) FLUSH;
X		    resetty();
X		    fgets(scrbuf, sizeof scrbuf, stdin);
X		    noecho();
X		    crmode();
X		    i = strlen(scrbuf);
X		    if (scrbuf[i-1] == '\n') {
X			scrbuf[--i] = '\0';
X		    }
X		    growstr(&input_str, &input_siz, i+1);
X		    safecpy(input_str, scrbuf, i+1);
X		    s = input_str;
X		    break;
X#endif
X		case '~':
X		    s = homedir;
X		    break;
X		case '.':
X		    s = dotdir;
X		    break;
X		case '$':
X		    s = scrbuf;
X		    sprintf(s,"%ld",our_pid);
X		    break;
X		case '#':
X		    s = scrbuf;
X		    sprintf(s,"%d",perform_cnt);
X		    break;
X		case '?':
X		    s = " ";
X		    line_split = dest;
X		    break;
X		case '0': case '1': case '2': case '3': case '4':
X		case '5': case '6': case '7': case '8': case '9':
X#ifdef CONDSUB
X		    s = getbracket(bra_compex,*pattern - '0');
X#else
X		    s = nullstr;
X#endif
X		    break;
X		case 'a':
X		    if (in_ng) {
X			s = scrbuf;
X			sprintf(s,"%ld",(long)art);
X		    }
X		    else
X			s = nullstr;
X		    break;
X		case 'A':
X		    if (in_ng) {
X#ifdef LINKART
X			s = linkartname;  /* so Eunice people get right file */
X#else
X			s = scrbuf;
X#ifdef USE_NNTP
X			if (artopen(art))
X			    nntp_finishbody(FB_SILENT);
X			sprintf(s,"%s/%s",spool,nntp_artname());
X#else
X			sprintf(s,"%s/%s/%ld",spool,ngdir,(long)art);
X#endif
X#endif
X		    }
X		    else
X			s = nullstr;
X		    break;
X		case 'b':
X		    s = savedest;
X		    break;
X		case 'B':
X		    s = scrbuf;
X		    sprintf(s,"%ld",(long)savefrom);
X		    break;
X		case 'c':
X		    s = ngdir;
X		    break;
X		case 'C':
X		    s = ngname;
X		    break;
X		case 'd':
X		    s = scrbuf;
X		    sprintf(s,"%s/%s",spool,ngdir);
X		    break;
X		case 'D':
X		    if (in_ng)
X			s = dist_buf = fetchlines(art,DIST_LINE);
X		    else
X			s = nullstr;
X		    break;
X		case 'e':
X		    s = (extractprog ? extractprog : "-");
X		    break;
X		case 'E':
X		    s = extractdest;
X		    break;
X		case 'f':			/* from line */
X		    if (in_ng) {
X			parseheader(art);
X			if (htype[REPLY_LINE].ht_minpos >= 0 && !comment_parse) {
X						/* was there a reply line? */
X			    if (!(s=reply_buf))
X				s = reply_buf = fetchlines(art,REPLY_LINE);
X			}
X			else if (!(s = from_buf))
X			    s = from_buf = fetchlines(art,FROM_LINE);
X		    }
X		    else
X			s = nullstr;
X		    break;
X		case 'F':
X		    if (in_ng) {
X			parseheader(art);
X			if (htype[FOLLOW_LINE].ht_minpos >= 0)
X					/* is there a Followup-To line? */
X			    s = follow_buf = fetchlines(art,FOLLOW_LINE);
X			else 
X			    s = ngs_buf = fetchlines(art,NGS_LINE);
X		    }
X		    else
X			s = nullstr;
X		    break;
X		case 'h':			/* header file name */
X		    s = headname;
X		    break;
X		case 'H':			/* host name in postings */
X		    s = phostname;
X		    break;
X		case 'i':
X		    if (in_ng) {
X			if (!(s=artid_buf))
X			    s = artid_buf = fetchlines(art,MESSID_LINE);
X			if (*s && *s != '<') {
X			    sprintf(scrbuf,"<%s>",artid_buf);
X			    s = scrbuf;
X			}
X		    }
X		    else
X			s = nullstr;
X		    break;
X		case 'I':			/* ref article indicator */
X		    s = scrbuf;
X		    sprintf(scrbuf,"'%s'",indstr);
X		    break;
X		case 'l':			/* rn library */
X#ifdef NEWS_ADMIN
X		    s = newsadmin;
X#else
X		    s = "???";
X#endif
X		    break;
X		case 'L':			/* login id */
X		    s = loginName;
X		    break;
X		case 'm':		/* current mode */
X		    s = scrbuf;
X		    *s = mode;
X		    s[1] = '\0';
X		    break;
X		case 'M':
X		    sprintf(scrbuf,"%ld",(long)dmcount);
X		    s = scrbuf;
X		    break;
X		case 'n':			/* newsgroups */
X		    if (in_ng)
X			s = ngs_buf = fetchlines(art,NGS_LINE);
X		    else
X			s = nullstr;
X		    break;
X		case 'N':			/* full name */
X		    s = getval("NAME",realname);
X		    break;
X		case 'o':			/* organization */
X#ifdef IGNOREORG
X		    s = getval("NEWSORG",orgname); 
X#else
X		    s = getenv("NEWSORG");
X		    if (s == Nullch) 
X			s = getval("ORGANIZATION",orgname); 
X#endif
X		    s = filexp(s);
X#ifdef ORGFILE
X		    if (*s == '/') {
X			FILE *ofp = fopen(s,"r");
X
X			if (ofp) {
X			    if (fgets(scrbuf,sizeof scrbuf,ofp) == NULL)
X			    	*scrbuf = '\0';
X			    fclose(ofp);
X			    s = scrbuf+strlen(scrbuf)-1;
X			    if (*scrbuf && *s == '\n')
X				*s = '\0';
X			    s = scrbuf;
X			}
X			else
X			    s = nullstr;
X		    }
X#endif
X		    break;
X		case 'O':
X		    s = origdir;
X		    break;
X		case 'p':
X		    s = cwd;
X		    break;
X		case 'P':
X		    s = spool;
X		    break;
X		case 'q':
X		    s = input_str;
X		    break;
X		case 'r':
X		    if (in_ng) {
X			parseheader(art);
X			if (htype[REFS_LINE].ht_minpos >= 0) {
X			    refs_buf = fetchlines(art,REFS_LINE);
X			    refscpy(scrbuf,(sizeof scrbuf),refs_buf);
X			}
X			else
X			    *scrbuf = '\0';
X			s = rindex(scrbuf,'<');
X		    }
X		    else
X			s = nullstr;
X		    break;
X		case 'R':
X		    if (!in_ng) {
X			s = nullstr;
X			break;
X		    }
X		    parseheader(art);
X		    if (htype[REFS_LINE].ht_minpos >= 0) {
X			refs_buf = fetchlines(art,REFS_LINE);
X			refscpy(scrbuf,(sizeof scrbuf),refs_buf);
X			/* no more than 3 prior references PLUS the
X			** root article allowed, including the one
X			** concatenated below */
X			if ((s = rindex(scrbuf,'<')) > scrbuf) {
X			    *s = '\0';
X			    h = rindex(scrbuf,'<');
X			    *s = '<';
X			    if (h > scrbuf) {
X				s = index(scrbuf+1,'<');
X				if (s < h)
X				    strcpy(s,h);
X			    }
X			}
X		    }
X		    else
X			*scrbuf = '\0';
X		    if (!artid_buf)
X			artid_buf = fetchlines(art,MESSID_LINE);
X		    if (artid_buf[0] == '<')
X			safecat(scrbuf,artid_buf,sizeof(scrbuf));
X		    else if (artid_buf[0]) {
X			char tmpbuf[64];
X    
X			sprintf(tmpbuf,"<%s>",artid_buf);
X			safecat(scrbuf,tmpbuf,sizeof(scrbuf));
X		    }
X		    s = scrbuf;
X		    break;
X		case 's':
X		    if (!in_ng) {
X			s = nullstr;
X			break;
X		    }
X		    if (!(s=subj_buf))
X			s = subj_buf = fetchsubj(art,TRUE);
X						/* get subject handy */
X		    while ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
X						/* skip extra Re: */
X			s += 3;
X			if (*s == ' ')
X			    s++;
X		    }
X		    if (h = instr(s,"- (nf", TRUE))
X			*h = '\0';
X		    break;
X		case 'S':
X		    if (!in_ng) {
X			s = nullstr;
X			break;
X		    }
X		    if (!(s=subj_buf))
X			s = subj_buf = fetchsubj(art,TRUE);
X						/* get subject handy */
X		    if ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
X						/* skip extra Re: */
X			s += 3;
X			if (*s == ' ')
X			    s++;
X		    }
X		    break;
X		case 't':
X		case 'T':
X		    if (!in_ng) {
X			s = nullstr;
X			break;
X		    }
X		    parseheader(art);
X		    if (htype[REPLY_LINE].ht_minpos >= 0) {
X					/* was there a reply line? */
X			if (!(s=reply_buf))
X			    s = reply_buf = fetchlines(art,REPLY_LINE);
X		    }
X		    else if (!(s = from_buf))
X			s = from_buf = fetchlines(art,FROM_LINE);
X		    else
X			s = "noname";
X		    if (*pattern == 'T') {
X			if (htype[PATH_LINE].ht_minpos >= 0) {
X					/* should we substitute path? */
X			    s = path_buf = fetchlines(art,PATH_LINE);
X			}
X			i = strlen(phostname);
X			if (strnEQ(phostname,s,i) && s[i] == '!')
X			    s += i + 1;
X		    }
X		    address_parse = TRUE;	/* just the good part */
X		    break;
X		case 'u':
X		    sprintf(scrbuf,"%ld",(long)toread[ng]);
X		    s = scrbuf;
X		    break;
X		case 'U': {
X		    int unseen;
X
X		    if (!in_ng) {
X			s = nullstr;
X			break;
X		    }
X		    unseen = (art <= lastart) && !was_read(art);
X		    if (selected_only) {
X			int selected;
X
X			selected = (curr_artp->flags & AF_SEL);
X			sprintf(scrbuf,"%ld",
X				(long)selected_count - (selected && unseen));
X		    }
X		    else
X			sprintf(scrbuf,"%ld",(long)toread[ng] - unseen);
X		    s = scrbuf;
X		    break;
X		}
X		case 'v': {
X		    int selected, unseen;
X
X		    if (in_ng) {
X			selected = (curr_artp->flags & AF_SEL);
X			unseen = (art <= lastart) && !was_read(art);
X			sprintf(scrbuf,"%ld",(long)toread[ng] - selected_count
X						- (!selected && unseen));
X			s = scrbuf;
X		    }
X		    else
X			s = nullstr;
X		    break;
X		}
X		case 'W':
X		    s = threaddir;
X		    break;
X		case 'x':			/* news library */
X		    s = lib;
X		    break;
X		case 'X':			/* rn library */
X		    s = rnlib;
X		    break;
X		case 'y':
X		    s = tmpdir;
X		    break;
X		case 'z':
X		    if (!in_ng) {
X			s = nullstr;
X			break;
X		    }
X#ifdef LINKART
X		    s = linkartname;	/* so Eunice people get right file */
X#else
X		    s = scrbuf;
X		    sprintf(s,"%ld",(long)art);
X#endif
X		    if (stat(s,&filestat) < 0)
X			filestat.st_size = 0L;
X		    sprintf(scrbuf,"%5ld",(long)filestat.st_size);
X		    s = scrbuf;
X		    break;
X		case 'Z':
X		    sprintf(scrbuf,"%ld",(long)selected_count);
X		    s = scrbuf;
X		    break;
X		default:
X		    if (--destsize <= 0)
X			abort_interp();
X		    *dest++ = *pattern | metabit;
X		    s = nullstr;
X		    break;
X		}
X	    }
X	    if (!s)
X		s = nullstr;
X	    if (proc_sprintf) {
X		sprintf(scrbuf,spfbuf,s);
X		s = scrbuf;
X	    }
X	    pattern++;
X	    if (upper || lastcomp) {
X		char *t;
X
X		if (s != scrbuf) {
X		    safecpy(scrbuf,s,(sizeof scrbuf));
X		    s = scrbuf;
X		}
X		if (upper || !(t=rindex(s,'/')))
X		    t = s;
X		while (*t && !isalpha(*t))
X		    t++;
X		if (islower(*t))
X		    *t = toupper(*t);
X	    }
X	    /* Do we have room left? */
X	    i = strlen(s);
X	    if (destsize <= i)
X		abort_interp();
X	    destsize -= i;	/* adjust the size now. */
X
X	    /* A maze of twisty little conditions, all alike... */
X	    if (address_parse || comment_parse) {
X		if (s != scrbuf) {
X		    safecpy(scrbuf,s,(sizeof scrbuf));
X		    s = scrbuf;
X		}
X		if (address_parse) {
X		    if ((h=index(s,'<')) != Nullch) { /* grab the good part */
X			s = h+1;
X			if ((h=index(s,'>')) != Nullch)
X			    *h = '\0';
X		    } else if ((h=index(s,'(')) != Nullch) {
X			while (h-- != s && *h == ' ')
X			    ;
X			h[1] = '\0';		/* or strip the comment */
X		    }
X		} else {
X		    if (!(s = extract_name(s)))
X			s = nullstr;
X		}
X	    }
X	    if (metabit) {
X		/* set meta bit while copying. */
X		i = metabit;		/* maybe get into register */
X		if (s == dest) {
X		    while (*dest)
X			*dest++ |= i;
X		} else {
X		    while (*s)
X			*dest++ = *s++ | i;
X		}
X	    } else if (re_quote || tick_quote) {
X		/* put a backslash before regexp specials while copying. */
X		if (s == dest) {
X		    /* copy out so we can copy in. */
X		    safecpy(scrbuf, s, sizeof scrbuf);
X		    s = scrbuf;
X		    if (i > sizeof scrbuf)	/* we truncated, ack! */
X			abort_interp();
X		}
X		while (*s) {
X		    if ((re_quote && index(regexp_specials, *s))
X		     || (tick_quote == 2 && *s == '"')) {
X			if (--destsize <= 0)
X			    abort_interp();
X			*dest++ = '\\';
X		    }
X		    else if (re_quote && *s == ' ' && s[1] == ' ') {
X			*dest++ = ' ';
X			*dest++ = '*';
X			while (*++s == ' ') ;
X			continue;
X		    }
X		    else if (tick_quote && *s == '\'') {
X			if ((destsize -= 3) <= 0)
X			    abort_interp();
X			*dest++ = '\'';
X			*dest++ = '\\';
X			*dest++ = '\'';
X		    }
X		    *dest++ = *s++;
X		}
X	    } else {
X		/* straight copy. */
X		if (s == dest) {
X		    dest += i;
X		} else {
X		    while (*s)
X			*dest++ = *s++;
X		}
X	    }
X	}
X	else {
X	    if (--destsize <= 0)
X		abort_interp();
X	    if (*pattern == '^' && pattern[1]) {
X		++pattern;			/* skip uparrow */
X		i = *pattern;		/* get char into a register */
X		if (i == '?')
X		    *dest++ = '\177' | metabit;
X		else if (i == '(') {
X		    metabit = 0200;
X		    destsize++;
X		}
X		else if (i == ')') {
X		    metabit = 0;
X		    destsize++;
X		}
X		else
X		    *dest++ = (i & 037) | metabit;
X		pattern++;
X	    }
X	    else if (*pattern == '\\' && pattern[1]) {
X		++pattern;			/* skip backslash */
X		i = *pattern;		/* get char into a register */
X    
X		/* this used to be a switch but the if may save space */
X		
X		if (i >= '0' && i <= '7') {
X		    i = 1;
X		    while (i < 01000 && *pattern >= '0' && *pattern <= '7') {
X			i <<= 3;
X			i += *pattern++ - '0';
X		    }
X		    *dest++ = (i & 0377) | metabit;
X		    --pattern;
X		}
X		else if (i == 'b')
X		    *dest++ = '\b' | metabit;
X		else if (i == 'f')
X		    *dest++ = '\f' | metabit;
X		else if (i == 'n')
X		    *dest++ = '\n' | metabit;
X		else if (i == 'r')
X		    *dest++ = '\r' | metabit;
X		else if (i == 't')
X		    *dest++ = '\t' | metabit;
X		else
X		    *dest++ = i | metabit;
X		pattern++;
X	    }
X	    else
X		*dest++ = *pattern++ | metabit;
X	}
X    }
X    *dest = '\0';
X    if (line_split != Nullch)
X	if ((int)strlen(orig_dest) > 79)
X	    *line_split = '\n';
Xgetout:
X    if (subj_buf != Nullch)	/* return any checked out storage */
X	free(subj_buf);
X    if (ngs_buf != Nullch)
X	free(ngs_buf);
X    if (refs_buf != Nullch)
X	free(refs_buf);
X    if (artid_buf != Nullch)
X	free(artid_buf);
X    if (reply_buf != Nullch)
X	free(reply_buf);
X    if (from_buf != Nullch)
X	free(from_buf);
X    if (path_buf != Nullch)
X	free(path_buf);
X    if (follow_buf != Nullch)
X	free(follow_buf);
X    if (dist_buf != Nullch)
X	free(dist_buf);
X    if (line_buf != Nullch)
X	free(line_buf);
X    return pattern;			/* where we left off */
X}
X
Xvoid
Xinterp(dest,destsize,pattern)
Xchar *dest;
Xint destsize;
Xchar *pattern;
X{
X    dointerp(dest,destsize,pattern,Nullch);
X#ifdef DEBUG
X    if (debug & DEB_FILEXP)
X	fputs(dest,stdout);
X#endif
X}
X
X/* copy a references line, normalizing as we go */
X
Xvoid
Xrefscpy(dest,destsize,src)
Xregister char *dest, *src;
Xregister int destsize;
X{
X    register char *dot, *at, *beg;
X    char tmpbuf[64];
X    
X    while (*src) {
X	if (*src != '<') {
X	    if (--destsize <= 0)
X		break;
X	    *dest++ = '<';
X	    at = dot = Nullch;
X	    beg = src;
X	    while (*src && *src != ' ' && *src != ',') {
X		if (*src == '.')
X		    dot = src;
X		else if (*src == '@')
X		    at = src;
X		if (--destsize <= 0)
X		    break;
X		*dest++ = *src++;
X	    }
X	    if (destsize <= 0)
X		break;
X	    if (dot && !at) {
X		int len;
X
X		*dest = *dot++ = '\0';
X		sprintf(tmpbuf,"%s@%s.UUCP",dot,beg);
X		len = strlen(tmpbuf);
X		if (destsize > len) {
X		    strcpy(dest,tmpbuf);
X		    dest = dest + len;
X		    destsize -= len;
X		}
X	    }
X	    if (--destsize <= 0)
X		break;
X	    *dest++ = '>';
X	}
X	else {
X	    while (*src && --destsize > 0 && (*dest++ = *src++) != '>') ;
X	    if (destsize <= 0)
X		break;
X	}
X	while (*src == ' ' || *src == '\t' || *src == '\n' || *src == ',')
X	    src++;
X	if (*src && --destsize > 0)
X	    *dest++ = ' ';
X    }
X    *dest = '\0';
X} 
X
X/* get the person's real name from /etc/passwd */
X/* (string is overwritten, so it must be copied) */
X
Xchar *
Xgetrealname(uid)
Xlong uid;
X{
X    char *s, *c;
X
X#ifdef PASSNAMES
X#ifdef HAS_GETPWENT
X#ifdef notdef
X    struct passwd *getpwuid _((uid_t));
X#endif
X    struct passwd *pwd = getpwuid(uid);
X
X    if (!pwd)
X	return nullstr;
X
X    s = pwd->pw_gecos;
X#else
X    char tmpbuf[512];
X    int i;
X
X    getpw(uid, tmpbuf);
X    for (s=tmpbuf, i=GCOSFIELD-1; i; i--) {
X	if (s)
X	    s = index(s,':')+1;
X    }
X    if (!s)
X	return nullstr;
X    cpytill(tmpbuf,s,':');
X    s = tmpbuf;
X#endif
X#ifdef BERKNAMES
X#ifdef BERKJUNK
X    while (*s && !isalnum(*s) && *s != '&') s++;
X#endif
X    if ((c = index(s, ',')) != Nullch)
X	*c = '\0';
X    if ((c = index(s, ';')) != Nullch)
X	*c = '\0';
X    s = cpytill(buf,s,'&');
X    if (*s == '&') {			/* whoever thought this one up was */
X	c = buf + strlen(buf);		/* in the middle of the night */
X	strcat(c,loginName);		/* before the morning after */
X	strcat(c,s+1);
X	if (islower(*c))
X	    *c = toupper(*c);		/* gack and double gack */
X    }
X#else
X    if ((c = index(s, '(')) != Nullch)
X	*c = '\0';
X    if ((c = index(s, '-')) != Nullch)
X	s = c;
X    strcpy(buf,s);
X#endif
X#ifdef HAS_GETPWENT
X    endpwent();
X#endif
X    return buf;				/* return something static */
X#else
X    if ((tmpfp=fopen(filexp(FULLNAMEFILE),"r")) != Nullfp) {
X	fgets(buf,sizeof buf,tmpfp);
X	fclose(tmpfp);
X	buf[strlen(buf)-1] = '\0';
X	return buf;
X    }
X    return "PUT YOUR NAME HERE";
X#endif
X}
X
Xstatic void
Xabort_interp()
X{
X    fputs("\n% interp buffer overflow!\n",stdout) FLUSH;
X    sig_catcher(0);
X}
X
X
SHAR_EOF
  echo 'File trn-3.6/intrp.c is complete' &&
  $shar_touch -am 1110023094 'trn-3.6/intrp.c' &&
  chmod 0644 'trn-3.6/intrp.c' ||
  echo 'restore of trn-3.6/intrp.c failed'
  shar_count="`wc -c < 'trn-3.6/intrp.c'`"
  test 30284 -eq "$shar_count" ||
    echo "trn-3.6/intrp.c: original size 30284, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/intrp.h ==============
if test -f 'trn-3.6/intrp.h' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/intrp.h (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/intrp.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/intrp.h' &&
X/* $Id: intrp.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 *lib INIT(Nullch);		/* news library */
XEXT char *rnlib INIT(Nullch);		/* private news program library */
XEXT char *origdir INIT(Nullch);		/* cwd when rn invoked */
XEXT char *homedir INIT(Nullch);		/* login directory */
XEXT char *dotdir INIT(Nullch);		/* where . files go */
XEXT char *tmpdir INIT(Nullch);		/* where tmp files go */
XEXT char *loginName INIT(Nullch);	/* login id of user */
XEXT char *realname INIT(Nullch);	/* real name of user */
XEXT char *phostname INIT(Nullch);	/* host name in a posting */
XEXT int perform_cnt;
X
X#ifdef NEWS_ADMIN
X    EXT char newsadmin[] INIT(NEWS_ADMIN);/* news administrator */
X    EXT int newsuid INIT(0);
X#endif
X
Xvoid    intrp_init _((char*));
Xchar	*filexp _((char*));
Xchar	*dointerp _((char*,int,char*,char*));
Xvoid	interp _((char*,int,char*));
Xvoid	refscpy _((char*,int,char*));
Xchar	*getrealname _((long));
SHAR_EOF
  $shar_touch -am 1017161694 'trn-3.6/intrp.h' &&
  chmod 0644 'trn-3.6/intrp.h' ||
  echo 'restore of trn-3.6/intrp.h failed'
  shar_count="`wc -c < 'trn-3.6/intrp.h'`"
  test 1508 -eq "$shar_count" ||
    echo "trn-3.6/intrp.h: original size 1508, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/kfile.c ==============
if test -f 'trn-3.6/kfile.c' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/kfile.c (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/kfile.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/kfile.c' &&
X/* $Id: kfile.c,v 3.0 1991/09/09 20:18:23 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 "term.h"
X#include "util.h"
X#include "cache.h"
X#include "artsrch.h"
X#include "ng.h"
X#include "ngdata.h"
X#include "intrp.h"
X#include "ngstuff.h"
X#include "rcstuff.h"
X#include "trn.h"
X#include "bits.h"
X#include "hash.h"
X#include "rthread.h"
X#include "rt-process.h"
X#include "rt-select.h"
X#include "INTERN.h"
X#include "kfile.h"
X
Xextern HASHTABLE *msgid_hash;
X
Xstatic bool exitcmds = FALSE;
X
Xvoid
Xkfile_init()
X{
X    ;
X}
X
X#ifndef KILLFILES
Xint
Xedit_kfile()
X{
X    notincl("^K");
X    return -1;
X}
X
X#else /* KILLFILES */
X
Xchar killglobal[] = KILLGLOBAL;
Xchar killlocal[] = KILLLOCAL;
X
Xvoid
Xmention(str)
Xchar *str;
X{
X#ifdef VERBOSE
X    IF(verbose) {
X#ifdef NOFIREWORKS
X	no_sofire();
X#endif
X	standout();
X	fputs(str,stdout);
X	un_standout();
X	putchar('\n');
X    }
X    ELSE
X#endif
X#ifdef TERSE
X	putchar('.');
X#endif
X    fflush(stdout);
X}
X
Xstatic bool kill_mentioned;
X
Xint
Xdo_kfile(kfp,entering)
XFILE *kfp;
Xint entering;
X{
X    bool first_time = (entering && !killfirst);
X    ART_UNREAD selections = selected_count;
X    ART_UNREAD unread = toread[ng];
X    int thread_kill_cnt = 0;
X    int thread_select_cnt = 0;
X    char *cp;
X
X    art = lastart+1;
X    killfirst = firstart;
X    fseek(kfp,0L,0);			/* rewind file */
X    while (fgets(buf,LBUFLEN,kfp) != Nullch) {
X	if (*(cp = buf + strlen(buf) - 1) == '\n')
X	    *cp = '\0';
X	if (strnEQ(buf,"THRU",4)) {
X	    killfirst = atol(buf+4)+1;
X	    if (killfirst < absfirst)
X		killfirst = absfirst;
X	    if (killfirst > lastart)
X		killfirst = lastart+1;
X	    continue;
X	}
X	if (*buf == 'X') {		/* exit command? */
X	    if (entering) {
X		exitcmds = TRUE;
X		continue;
X	    }
X	    strcpy(buf,buf+1);
X	}
X	else if (!entering)
X	    continue;
X
X	if (*buf == '&') {
X	    mention(buf);
X	    switcheroo();
X	}
X	else if (*buf == '/') {
X	    has_normal_kills = TRUE;
X	    if (firstart > lastart)
X		continue;
X	    mention(buf);
X	    kill_mentioned = TRUE;
X	    switch (art_search(buf, (sizeof buf), FALSE)) {
X	    case SRCH_ABORT:
X		continue;
X	    case SRCH_INTR:
X#ifdef VERBOSE
X		IF(verbose)
X		    printf("\n(Interrupted at article %ld)\n",(long)art)
X		      FLUSH;
X		ELSE
X#endif
X#ifdef TERSE
X		    printf("\n(Intr at %ld)\n",(long)art) FLUSH;
X#endif
X		return -1;
X	    case SRCH_DONE:
X		break;
X	    case SRCH_SUBJDONE:
X		fputs("\tsubject not found (?)\n",stdout) FLUSH;
X		break;
X	    case SRCH_NOTFOUND:
X		fputs("\tnot found\n",stdout) FLUSH;
X		break;
X	    case SRCH_FOUND:
X		fputs("\tfound\n",stdout) FLUSH;
X	    }
X	}
X	else if (first_time && *buf == '<') {
X	    register ARTICLE *ap;
X	    cp = index(buf,' ');
X	    if (!cp)
X		cp = "T,";
X	    else
X		*cp++ = '\0';
X	    if ((ap = get_article(buf)) != Nullart) {
X		if ((ap->flags & AF_FAKE) == AF_FAKE) {
X		    if (*cp == 'T')
X			cp++;
X		    switch (*cp) {
X		    case '+':
X			ap->autofl |= AUTO_SELECTALL;
X			thread_select_cnt++;
X			break;
X		    case '.':
X			ap->autofl |= AUTO_SELECT;
X			thread_select_cnt++;
X			break;
X		    case 'J':
X		    case 'j':
X			ap->autofl |= AUTO_KILLALL;
X			thread_kill_cnt++;
X			break;
X		    case ',':
X			ap->autofl |= AUTO_KILL;
X			thread_kill_cnt++;
X			break;
X		    }
X		} else {
X		    art = article_num(ap);
X		    artp = ap;
X		    perform(cp,FALSE);
X		    if (ap->autofl & (AUTO_SELECT|AUTO_SELECTALL))
X			thread_select_cnt++;
X		    else if (ap->autofl & (AUTO_KILL|AUTO_KILLALL))
X			thread_kill_cnt++;
X		}
X	    }
X	    art = lastart+1;
X	} else if (*buf == '*') {
X	    register ARTICLE *ap;
X	    register int killmask = AF_READ;
X	    switch (buf[1]) {
X	    case 'X':
X		killmask |= sel_mask;	/* don't kill selected articles */
X		/* FALL THROUGH */
X	    case 'j':
X		for (art = killfirst, ap = article_ptr(killfirst);
X		     art <= lastart;
X		     art++, ap++
X		) {
X		    if (!(ap->flags & killmask))
X			set_read(ap);
X		    else if (ap->flags & sel_mask) {
X			ap->flags &= ~sel_mask;
X			if (!selected_count--)
X			    selected_count = 0;
X		    }
X		}
X		break;
X	    }
X	    has_normal_kills = TRUE;
X	}
X    }
X    if (thread_kill_cnt) {
X	sprintf(buf,"%ld auto-kill command%s.", (long)thread_kill_cnt,
X		thread_kill_cnt == 1? "" : "s");
X	mention(buf);
X	kill_mentioned = TRUE;
X    }
X    if (thread_select_cnt) {
X	sprintf(buf,"%ld auto-select command%s.", (long)thread_select_cnt,
X		thread_select_cnt == 1? "" : "s");
X	mention(buf);
X	kill_mentioned = TRUE;
X    }
X    unread -= toread[ng];
X    selections -= selected_count;
X#ifdef VERBOSE
X    IF(verbose && (unread > 0 || selections < 0)) {
X	putchar('\n');
X	if (unread > 0) {
X	    printf("Killed %ld article%s", (long)unread,
X		unread == 1? nullstr : "s");
X	    if (selections < 0)
X		fputs("; ",stdout);
X	}
X	if (selections < 0)
X	    printf("Selected %ld article%s", (long)-selections,
X		selections == -1? nullstr : "s");
X	fputs(".\n",stdout) FLUSH;
X	kill_mentioned = TRUE;
X    }
X#endif
X    return 0;
X}
X
Xvoid
Xkill_unwanted(starting,message,entering)
XART_NUM starting;
Xchar *message;
Xint entering;
X{
X    bool intr = FALSE;			/* did we get an interrupt? */
X    ART_NUM oldfirst;
X    char oldmode = mode;
X    bool anytokill = (toread[ng] > 0);
X
X    mode = 'k';
X    if ((entering || exitcmds) && (localkfp || globkfp)) {
X	exitcmds = FALSE;
X	oldfirst = firstart;
X	firstart = starting;
X	clear();
X#ifdef VERBOSE
X# ifdef TERSE
X	if (message && (verbose || entering))
X# else
X	if (message)
X# endif
X#else
X	if (message && entering)
X#endif
X	    fputs(message,stdout) FLUSH;
X
X	kill_mentioned = FALSE;
X	if (localkfp) {
X	    if (entering)
X		localkf_changes |= 1;
X	    intr = do_kfile(localkfp,entering);
X	}
X	if (globkfp && !intr)
X	    intr = do_kfile(globkfp,entering);
X	putchar('\n') FLUSH;
X	if (entering && kill_mentioned && novice_delays)
X#ifdef VERBOSE
X	    IF(verbose)
X		get_anything();
X	    ELSE
X#endif
X#ifdef TERSE
X		pad(just_a_sec);
X#endif
X	if (anytokill)			/* if there was anything to kill */
X	    forcelast = FALSE;		/* allow for having killed it all */
X	firstart = oldfirst;
X    }
X    if (!entering && localkf_changes && !intr)
X	rewrite_kfile(lastart);
X    mode = oldmode;
X}
X
Xstatic FILE *newkfp;
X
Xstatic void write_thread_commands(data, extra)
XHASHDATUM *data;
Xint extra;
X{
X    register ARTICLE *ap = (data->dat_ptr? (ARTICLE*)data->dat_ptr
X					: article_ptr(data->dat_len));
X    register int autofl;
X    char ch;
X
X    if ((autofl = ap->autofl) != 0) {
X	if (!(ap->flags & AF_MISSING) || ap->child1) {
X	    if (autofl & AUTO_KILLALL)
X		ch = 'J';
X	    else if (autofl & AUTO_KILL)
X		ch = ',';
X	    else if (autofl & AUTO_SELECTALL)
X		ch = '+';
X	    else if (autofl & AUTO_SELECT)
X		ch = '.';
X	    else
X		ch = '?';
X	    fprintf(newkfp,"%s T%c\n", ap->msgid, ch);
X	}
X    }
X}
X
Xvoid
Xrewrite_kfile(thru)
XART_NUM thru;
X{
X    bool no_kills = 0, has_star_commands = FALSE;
X
X    if (localkfp) {
X	fseek(localkfp,0L,0);		/* rewind current file */
X	/* If we're writing ids, we know the file is not null */
X	if (localkf_changes > 1)
X	    ;
X	else if (fgets(buf,LBUFLEN,localkfp) != Nullch
X	 && (strnNE(buf,"THRU",4) || fgets(buf,LBUFLEN,localkfp) != Nullch))
X	    fseek(localkfp,0L,0);
X	else
X	    no_kills = 1;
X    }
X    strcpy(buf,filexp(getval("KILLLOCAL",killlocal)));
X    if (!localkfp)
X	makedir(buf,MD_FILE);
X    UNLINK(buf);			/* to prevent file reuse */
X    if (no_kills)
X	open_kfile(KF_LOCAL);		/* close file and reset open flag */
X    else if (newkfp = fopen(buf,"w")) {
X	fprintf(newkfp,"THRU %ld\n",(long)thru);
X	while (localkfp && fgets(buf,LBUFLEN,localkfp) != Nullch) {
X	    if (strnEQ(buf,"THRU",4))
X		continue;
X	    /* Write star commands after other kill commands */
X	    if (*buf == '*') {
X		has_star_commands = TRUE;
X		continue;
X	    }
X	    /* Leave out any outdated thread commands */
X	    if (*buf != 'T' && *buf != '<')
X		fputs(buf,newkfp);
X	}
X	if (has_star_commands) {
X	    fseek(localkfp,0L,0);			/* rewind file */
X	    while (fgets(buf,LBUFLEN,localkfp) != Nullch) {
X		if (*buf == '*')
X		    fputs(buf,newkfp);
X	    }
X	}
X	/* Append all the still-valid thread commands */
X	hashwalk(msgid_hash, write_thread_commands, 0);
X	fclose(newkfp);
X	open_kfile(KF_LOCAL);		/* and reopen local file */
X    }
X    else
X	printf(cantcreate,buf) FLUSH;
X    localkf_changes = 0;
X    has_normal_kills = FALSE;
X}
X
X/* edit KILL file for newsgroup */
X
Xint
Xedit_kfile()
X{
X    int r = -1;
X
X    if (in_ng) {
X	register SUBJECT *sp;
X
X	if (localkf_changes)
X	    rewrite_kfile(lastart);
X	for (sp = first_subject; sp; sp = sp->next)
X	    clear_subject(sp);
X	localkf_changes = 0;
X	strcpy(buf,filexp(getval("KILLLOCAL",killlocal)));
X    } else
X	strcpy(buf,filexp(getval("KILLGLOBAL",killglobal)));
X    if ((r = makedir(buf,MD_FILE)) >= 0) {
X	sprintf(cmd_buf,"%s %s",
X	    filexp(getval("VISUAL",getval("EDITOR",defeditor))),buf);
X	printf("\nEditing %s KILL file:\n%s\n",
X	    (in_ng?"local":"global"),cmd_buf) FLUSH;
X	resetty();			/* make sure tty is friendly */
X	r = doshell(sh,cmd_buf);/* invoke the shell */
X	noecho();			/* and make terminal */
X	crmode();			/*   unfriendly again */
X	open_kfile(in_ng);
X	if (localkfp) {
X	    fseek(localkfp,0L,0);			/* rewind file */
X	    has_normal_kills = FALSE;
X	    while (fgets(buf,LBUFLEN,localkfp) != Nullch) {
X		if (*buf == '/' || *buf == '*')
X		    has_normal_kills = TRUE;
X		else if (*buf == '<') {
X		    register ARTICLE *ap;
X		    char *cp = index(buf,' ');
X		    if (!cp)
X			cp = ",";
X		    else
X			*cp++ = '\0';
X		    if ((ap = get_article(buf)) != Nullart) {
X			if (*cp == 'T')
X			    cp++;
X			switch (*cp) {
X			case '+':
X			    ap->autofl |= AUTO_SELECTALL;
X			    break;
X			case '.':
X			    ap->autofl |= AUTO_SELECT;
X			    break;
X			case 'J':
X			case 'j':
X			    ap->autofl |= AUTO_KILLALL;
X			    break;
X			case ',':
X			    ap->autofl |= AUTO_KILL;
X			    break;
X			}
X		    }
X		}
X	    }
X	}
X    }
X    else
X	printf("Can't make %s\n",buf) FLUSH;
X    return r;
X}
X
Xvoid
Xopen_kfile(local)
Xint local;
X{
X    char *kname = filexp(local ?
X	getval("KILLLOCAL",killlocal) :
X	getval("KILLGLOBAL",killglobal)
X	);
X    
X    /* delete the file if it is empty */
X    if (stat(kname,&filestat) >= 0 && !filestat.st_size)
X	UNLINK(kname);
X    if (local) {
X	if (localkfp)
X	    fclose(localkfp);
X	localkfp = fopen(kname,"r");
X    }
X    else {
X	if (globkfp)
X	    fclose(globkfp);
X	globkfp = fopen(kname,"r");
X    }
X}
X
Xvoid
Xkf_append(cmd)
Xchar *cmd;
X{
X    strcpy(cmd_buf,filexp(getval("KILLLOCAL",killlocal)));
X    if (makedir(cmd_buf,MD_FILE) >= 0) {
X#ifdef VERBOSE
X	IF(verbose)
X	    printf("\nDepositing command in %s...",cmd_buf);
X	ELSE
X#endif
X#ifdef TERSE
X	    printf("\n--> %s...",cmd_buf);
X#endif
X	fflush(stdout);
X	if (novice_delays)
X	    sleep(2);
X	if ((tmpfp = fopen(cmd_buf,"a+")) != Nullfp) {
X	    char ch;
X	    if (fseek(tmpfp,-1L,2) < 0)
X		ch = '\n';
X	    else
X		ch = getc(tmpfp);
X	    fseek(tmpfp,0L,2);
X	    if (ch != '\n')
X		putc('\n', tmpfp);
X	    fprintf(tmpfp,"%s\n",cmd);
X	    fclose(tmpfp);
X	    if (!localkfp)
X		open_kfile(KF_LOCAL);
X	    fputs("done\n",stdout) FLUSH;
X	}
X	else
X	    printf(cantopen,cmd_buf) FLUSH;
X    }
X    has_normal_kills = TRUE;
X}
X#endif /* KILLFILES */
SHAR_EOF
  $shar_touch -am 1118220194 'trn-3.6/kfile.c' &&
  chmod 0644 'trn-3.6/kfile.c' ||
  echo 'restore of trn-3.6/kfile.c failed'
  shar_count="`wc -c < 'trn-3.6/kfile.c'`"
  test 11520 -eq "$shar_count" ||
    echo "trn-3.6/kfile.c: original size 11520, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/kfile.h ==============
if test -f 'trn-3.6/kfile.h' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/kfile.h (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/kfile.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/kfile.h' &&
X/* $Id: kfile.h,v 3.0 1991/09/09 20:18:23 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#define KF_GLOBAL 0
X#define KF_LOCAL 1
X
X#ifdef KILLFILES
XEXT FILE *globkfp INIT(Nullfp);		/* global article killer file */
XEXT FILE *localkfp INIT(Nullfp);	/* local (for this newsgroup) file */
XEXT int localkf_changes;		/* do we need to write changes? */
XEXT bool has_normal_kills;		/* flag when KILLs needs rereading */
XEXT ART_NUM killfirst;			/* used as firstart when killing */
X#endif
X
Xvoid	kfile_init _((void));
Xint	do_kfile _((FILE*,int));
Xvoid	kill_unwanted _((ART_NUM,char*,int));
Xint	edit_kfile _((void));
Xvoid	open_kfile _((int));
Xvoid    kf_append _((char*));
Xvoid	rewrite_kfile _((ART_NUM));
SHAR_EOF
  $shar_touch -am 0711223593 'trn-3.6/kfile.h' &&
  chmod 0644 'trn-3.6/kfile.h' ||
  echo 'restore of trn-3.6/kfile.h failed'
  shar_count="`wc -c < 'trn-3.6/kfile.h'`"
  test 1254 -eq "$shar_count" ||
    echo "trn-3.6/kfile.h: original size 1254, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/last.c ==============
if test -f 'trn-3.6/last.c' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/last.c (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/last.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/last.c' &&
X/* $Id: last.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 "trn.h"
X#include "util.h"
X#include "intrp.h"
X#include "INTERN.h"
X#include "last.h"
X
Xchar *lastname = Nullch;	/* path name of .rnlast file */
X
Xvoid
Xlast_init(tcbuf)
Xchar *tcbuf;
X{
X    lastname = savestr(filexp(LASTNAME));
X    if ((tmpfp = fopen(lastname,"r")) != Nullfp &&
X	fgets(tcbuf,1024,tmpfp) != Nullch) {
X	tcbuf[strlen(tcbuf)-1] = '\0';
X	lastngname = savestr(tcbuf);
X	fgets(tcbuf,1024,tmpfp);
X	lasttime = atol(tcbuf);
X	fgets(tcbuf,1024,tmpfp);
X	lastactsiz = atol(tcbuf);
X	if (fgets(tcbuf,1024,tmpfp) != Nullch)
X	    lastnewtime = atol(tcbuf);
X	else
X	    lastnewtime = lasttime;
X#if 1
X	/* If the time is in the future, rewind by thirty days */
X	if (lastnewtime > time(Null(time_t*)))
X	    lastnewtime = time(Null(time_t*)) - 30L*24*60*60;
X#endif
X	/* If the time wasn't set, just give them 2 days worth of groups */
X	if (!lastnewtime)
X	    lastnewtime = time(Null(time_t*)) - 2L*24*60*60;
X
X	if (fgets(tcbuf,1024,tmpfp) != Nullch)
X	    lastnewsize = atol(tcbuf);
X	else
X	    lastnewsize = 0;
X	fclose(tmpfp);
X    }
X    else {
X	lastngname = nullstr;
X	lasttime = 0;
X	lastactsiz = 0;
X	lastnewsize = 0;
X	/* Use two days ago as an initial value for finding new groups. */
X	lastnewtime = time(Null(time_t*)) - 2L*24*60*60;
X    }
X}
X
X/* put out certain values for next run of rn */
X
Xvoid
Xwritelast()
X{
X    if ((tmpfp = fopen(lastname,"w")) != Nullfp) {
X	fprintf(tmpfp,"%s\n%ld\n%ld\n%ld\n%ld\n",
X	    (ngname==Nullch?nullstr:ngname),(long)lasttime,(long)lastactsiz,
X	    (long)lastnewtime,(long)lastnewsize);
X	fclose(tmpfp);
X    }
X    else
X	printf(cantcreate,lastname) FLUSH;
X}
SHAR_EOF
  $shar_touch -am 1130170793 'trn-3.6/last.c' &&
  chmod 0644 'trn-3.6/last.c' ||
  echo 'restore of trn-3.6/last.c failed'
  shar_count="`wc -c < 'trn-3.6/last.c'`"
  test 2268 -eq "$shar_count" ||
    echo "trn-3.6/last.c: original size 2268, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/last.h ==============
if test -f 'trn-3.6/last.h' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/last.h (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/last.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/last.h' &&
X/* $Id: last.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 *lastngname INIT(Nullch);	/* last newsgroup read, from .rnlast file */
XEXT time_t lasttime INIT(0);	/* time last rn was started up */
XEXT long lastactsiz INIT(0);	/* size of active file when rn last started up */
XEXT time_t lastnewtime INIT(0);	/* time last new group was found */
XEXT long lastnewsize INIT(0);	/* size of active.times file when rn last started up */
X
Xvoid	last_init _((char*));
Xvoid    writelast _((void));
SHAR_EOF
  $shar_touch -am 0711223593 'trn-3.6/last.h' &&
  chmod 0644 'trn-3.6/last.h' ||
  echo 'restore of trn-3.6/last.h failed'
  shar_count="`wc -c < 'trn-3.6/last.h'`"
  test 1082 -eq "$shar_count" ||
    echo "trn-3.6/last.h: original size 1082, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/ndir.c ==============
if test -f 'trn-3.6/ndir.c' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/ndir.c (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/ndir.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/ndir.c' &&
X/* $Id: ndir.c,v 3.0 1991/09/09 20:23:31 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 "INTERN.h"
X#include "ndir.h"
X
X#ifdef EMULATE_NDIR
X/*
X * support for Berkeley directory reading routine on a V7 file system
X */
X
X/*
X * open a directory.
X */
XDIR *
Xopendir(name)
Xchar *name;
X{
X	register DIR *dirp;
X	register int fd;
X	char *malloc();
X
X	if ((fd = open(name, 0)) == -1)
X		return NULL;
X	if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
X		close (fd);
X		return NULL;
X	}
X	dirp->dd_fd = fd;
X	dirp->dd_loc = 0;
X	return dirp;
X}
X
X/*
X * read an old style directory entry and present it as a new one
X */
X#ifndef pyr
X#define	ODIRSIZ	14
X
Xstruct	olddirect {
X	ino_t	od_ino;
X	char	od_name[ODIRSIZ];
X};
X#else	/* an Pyramid in the ATT universe */
X#define	ODIRSIZ	248
X
Xstruct	olddirect {
X	long	od_ino;
X	short	od_fill1, od_fill2;
X	char	od_name[ODIRSIZ];
X};
X#endif
X
X/*
X * get next entry in a directory.
X */
XDirentry_t *
Xreaddir(dirp)
Xregister DIR *dirp;
X{
X	register struct olddirect *dp;
X	static Direntry_t dir;
X
X	for (;;) {
X		if (dirp->dd_loc == 0) {
X			dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
X			    DIRBLKSIZ);
X			if (dirp->dd_size <= 0)
X				return NULL;
X		}
X		if (dirp->dd_loc >= dirp->dd_size) {
X			dirp->dd_loc = 0;
X			continue;
X		}
X		dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc);
X		dirp->dd_loc += sizeof(struct olddirect);
X		if (dp->od_ino == 0)
X			continue;
X		dir.d_ino = dp->od_ino;
X		strncpy(dir.d_name, dp->od_name, ODIRSIZ);
X		dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */
X		dir.d_namlen = strlen(dir.d_name);
X		dir.d_reclen = DIRSIZ(&dir);
X		return (&dir);
X	}
X}
X
X/*
X * close a directory.
X */
Xvoid
Xclosedir(dirp)
Xregister DIR *dirp;
X{
X	close(dirp->dd_fd);
X	dirp->dd_fd = -1;
X	dirp->dd_loc = 0;
X	free(dirp);
X}
X
X#endif /* EMULATE_NDIR */
SHAR_EOF
  $shar_touch -am 0327202494 'trn-3.6/ndir.c' &&
  chmod 0644 'trn-3.6/ndir.c' ||
  echo 'restore of trn-3.6/ndir.c failed'
  shar_count="`wc -c < 'trn-3.6/ndir.c'`"
  test 2369 -eq "$shar_count" ||
    echo "trn-3.6/ndir.c: original size 2369, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/ndir.h ==============
if test -f 'trn-3.6/ndir.h' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/ndir.h (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/ndir.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/ndir.h' &&
X/* $Id: ndir.h,v 3.0 1991/09/09 20:23:31 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#ifdef I_NDIR
X#include <ndir.h>
X#else
X#ifdef I_DIRENT
X#include <dirent.h>
X#else
X#ifdef I_SYS_NDIR
X#include <sys/ndir.h>
X#else
X#ifdef I_SYS_DIR
X#include <sys/dir.h>
X#else
X
X#ifndef DEV_BSIZE
X#define	DEV_BSIZE	512
X#endif
X#define DIRBLKSIZ	DEV_BSIZE
X#define	MAXNAMLEN	255
X
XDirentry_t {
X	long	d_ino;			/* inode number of entry */
X	short	d_reclen;		/* length of this record */
X	short	d_namlen;		/* length of string in d_name */
X	char	d_name[MAXNAMLEN + 1];	/* name must be no longer than this */
X};
X
X/*
X * The DIRSIZ macro gives the minimum record length which will hold
X * the directory entry.  This requires the amount of space in Direntry_t
X * without the d_name field, plus enough space for the name with a terminating
X * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
X */
X#undef DIRSIZ
X#define DIRSIZ(dp) ((sizeof(Direntry_t)-(MAXNAMLEN+1))+(((dp)->d_namlen+1+3)&~3))
X
X/*
X * Definitions for library routines operating on directories.
X */
Xtypedef struct _dirdesc {
X	int	dd_fd;
X	long	dd_loc;
X	long	dd_size;
X	char	dd_buf[DIRBLKSIZ];
X} DIR;
X#ifndef NULL
X#define NULL 0
X#endif
Xextern	DIR *opendir _((char*));
Xextern	Direntry_t *readdir _((DIR*));
Xextern	long telldir _((DIR*));
Xextern	void seekdir _((DIR*));
X#define rewinddir(dirp)	seekdir((dirp), (long)0)
Xextern	void closedir _((DIR*));
X
X#endif
X#endif
X#endif
X#endif
SHAR_EOF
  $shar_touch -am 0327202494 'trn-3.6/ndir.h' &&
  chmod 0644 'trn-3.6/ndir.h' ||
  echo 'restore of trn-3.6/ndir.h failed'
  shar_count="`wc -c < 'trn-3.6/ndir.h'`"
  test 1976 -eq "$shar_count" ||
    echo "trn-3.6/ndir.h: original size 1976, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/ng.c ==============
if test -f 'trn-3.6/ng.c' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/ng.c (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/ng.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/ng.c' &&
X/* $Id: ng.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 "trn.h"
X#include "term.h"
X#include "final.h"
X#include "util.h"
X#include "cache.h"
X#include "bits.h"
X#include "artsrch.h"
X#include "help.h"
X#include "kfile.h"
X#include "rcstuff.h"
X#include "head.h"
X#include "art.h"
X#include "artio.h"
X#include "ngstuff.h"
X#include "intrp.h"
X#include "respond.h"
X#include "ngdata.h"
X#include "backpage.h"
X#include "rcln.h"
X#include "sw.h"
X#include "last.h"
X#include "search.h"
X#include "nntp.h"
X#include "rthread.h"
X#include "rt-select.h"
X#include "rt-wumpus.h"
X#include "decode.h"
X#include "charsubst.h"
X#include "INTERN.h"
X#include "ng.h"
X#include "artstate.h"			/* somebody has to do it */
X
X/* art_switch() return values */
X
X#define AS_NORM 0
X#define AS_INP 1
X#define AS_ASK 2
X#define AS_CLEAN 3
X
Xint exit_code = NG_NORM;
X
Xvoid
Xng_init()
X{
X
X#ifdef KILLFILES
X    open_kfile(KF_GLOBAL);
X#endif
X#ifdef CUSTOMLINES
X    init_compex(&hide_compex);
X    init_compex(&page_compex);
X#endif
X}
X
X/* do newsgroup on line ng with name ngname */
X
X/* assumes that we are chdir'ed to NEWSSPOOL, and assures that that is
X * still true upon return, but chdirs to NEWSSPOOL/ngname in between
X *
X * If you can understand this routine, you understand most of the program.
X * The basic structure is:
X *	for each desired article
X *		for each desired page
X *			for each line on page
X *				if we need another line from file
X *					get it
X *					if it's a header line
X *						do special things
X *				for each column on page
X *					put out a character
X *				end loop
X *			end loop
X *		end loop
X *	end loop
X *
X *	(Actually, the pager is in another routine.)
X *
X * The chief problem is deciding what is meant by "desired".  Most of
X * the messiness of this routine is due to the fact that people want
X * to do unstructured things all the time.  I have used a few judicious
X * goto's where I thought it improved readability.  The rest of the messiness
X * arises from trying to be both space and time efficient.  Have fun.
X */
X
Xint
Xdo_newsgroup(start_command)
Xchar *start_command;			/* command to fake up first */
X{
X    char oldmode = mode;
X#ifdef CHARSUBST
X    char *whatnext = "%s%sWhat next? [%s]";
X    charsubst = charsets;
X#else
X    char *whatnext = "%sWhat next? [%s]";
X#endif
X
X    exit_code = NG_NORM;
X    localkf_changes = 0;
X    killfirst = 0;
X
X    if (extractdest) {
X	free(extractdest);
X	extractdest = Nullch;
X    }
X    if (extractprog) {
X	free(extractprog);
X	extractprog = Nullch;
X    }
X
X    /* initialize the newsgroup data structures */
X
X    if (!access_ng())
X	return -1;
X
X#ifdef ARTSEARCH
X    srchahead = (scanon && !ThreadedGroup	/* did they say -S? */
X	      && ((ART_NUM)toread[ng]) >= scanon ? -1 : 0);
X#endif
X
X    /* FROM HERE ON, RETURN THRU CLEANUP OR WE ARE SCREWED */
X
X    forcelast = TRUE;			/* if 0 unread, do not bomb out */
X    recent_artp = curr_artp = Nullart;
X    recent_art = curr_art = lastart+1;
X    prompt = whatnext;
X
X    /* remember what newsgroup we were in for sake of posterity */
X
X    writelast();
X
X    /* see if there are any special searches to do */
X
X    has_normal_kills = FALSE;
X#ifdef KILLFILES
X    open_kfile(KF_LOCAL);
X# ifdef VERBOSE
X    IF(verbose)
X	kill_unwanted(firstart,"Processing memorized commands...\n\n",TRUE);
X    ELSE
X# endif
X# ifdef TERSE
X	kill_unwanted(firstart,"Auto-processing...\n\n",TRUE);
X# endif
X#endif
X    if (!selected_count)
X	selected_only = FALSE;
X    top_article();
X
X    /* do they want a special top line? */
X
X    firstline = getval("FIRSTLINE",Nullch);
X
X    /* custom line suppression, custom page ending */
X
X#ifdef CUSTOMLINES
X    if (hideline = getval("HIDELINE",Nullch))
X	compile(&hide_compex,hideline,TRUE,TRUE);
X    if (pagestop = getval("PAGESTOP",Nullch))
X	compile(&page_compex,pagestop,TRUE,TRUE);
X#endif
X
X    /* now read each unread article */
X
X    rc_changed = doing_ng = TRUE;	/* enter the twilight zone */
X    if (!unsafe_rc_saves)
X	checkcount = 0;			/* do not checkpoint for a while */
X    do_fseek = FALSE;			/* start 1st article at top */
X    for (; art<=lastart+1; ) {		/* for each article */
X	mode = 'a';
X
X	/* do we need to "grow" the newsgroup? */
X
X	if ((art > lastart || forcegrow) && !keep_the_group_static) {
X	    ART_NUM oldlast = lastart;
X#ifdef USE_NNTP
X	    if ((actfp || getngsize(ng) > lastart)
X	     && !nntp_group(ngname,ng)) {
X		fprintf(stderr,"Your server went south for the winter:\n%s\n",
X			ser_line);
X		finalize(1);
X	    }
X	    if (ngmax[ng] > lastart) {
X		grow_ng(ngmax[ng]);
X	    }
X#else
X	    grow_ng(getngsize(ng));
X#endif
X	    if (forcelast && art > oldlast)
X		art = lastart+1;
X	}
X	if (!artp || (artp->flags & AF_TMPMEM) != AF_TMPMEM || art != 0)
X	    artp = find_article(art);
X	if (start_command) {		/* do we have an initial command? */
X	    hide_pending();
X	    pushstring(start_command, 0);
X	    free(start_command);
X	    start_command = Nullch;
X	    art = curr_art = lastart+1;
X	    artp = curr_artp = Nullart;
X	    if (input_pending())
X		goto reinp_article;
X	}
X	if (art>lastart) {		/* are we off the end still? */
X	    ARTICLE *ap;
X	    ART_NUM i;
X	    art = lastart + 1;		/* keep pointer references sane */
X	    if (!forcelast && toread[ng] && selected_only && !selected_count) {
X		art = curr_art;
X		artp = curr_artp;
X		strcpy(buf, "+");
X		goto article_level;
X	    }
X	    count_subjects(CS_RETAIN);
X	    for (i = absfirst, ap = article_ptr(i); i <= lastart; i++, ap++)
X		if (!(ap->flags & (AF_READ|AF_CACHED)))
X		    article_count++;
X	    toread[ng] = (ART_UNREAD)article_count;
X	    if (artp != curr_artp) {
X		recent_art = curr_art;	/* remember last article # (for '-') */
X		curr_art = art;		/* set current article # */
X		recent_artp = curr_artp;
X		curr_artp = artp;
X	    }
X	    if (erase_screen)
X		clear();			/* clear the screen */
X	    else
X		fputs("\n\n",stdout) FLUSH;
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("End of newsgroup %s.",ngname);
X					/* print pseudo-article */
X	    ELSE
X#endif
X#ifdef TERSE
X		printf("End of %s",ngname);
X#endif
X	    if (article_count) {
X		if (selected_only)
X		    printf("  (%ld + %ld articles still unread)",
X			(long)selected_count,
X			(long)article_count-selected_count);
X		else
X		    printf("  (%ld article%s still unread)",
X			(long)article_count,article_count==1?nullstr:"s");
X	    }
X	    if (redirected) {
X		if (redirected == nullstr)
X		    printf("\n\n** This group has been disabled by your news admin **");
X		else
X		    printf("\n\n** Please start using %s **", redirected);
X	    } else if (!article_count && !forcelast)
X		goto cleanup;		/* actually exit newsgroup */
X	    mode = 'e';
X	    prompt = whatnext;
X#ifdef ARTSEARCH
X	    srchahead = 0;		/* no more subject search mode */
X#endif
X	    fputs("\n\n",stdout) FLUSH;
X	}
X	else if (!reread && (was_read(art) || (artp->flags & AF_MISSING)
X		|| (selected_only && !(artp->flags & AF_SEL)))) {
X					/* has this article been read? */
X	    inc_art(selected_only,FALSE);/* then skip it */
X	    continue;
X	}
X	else if (!reread && !parseheader(art)) {
X	    oneless(artp);		/* mark deleted as read */
X	    ng_skip();
X	}
X	else {				/* we have a real live article */
X	    if (artp != curr_artp) {
X		recent_art = curr_art;	/* remember last article # (for '-') */
X		curr_art = art;		/* set current article # */
X		recent_artp = curr_artp;
X		curr_artp = artp;
X	    }
X	    if (!do_fseek) {		/* starting at top of article? */
X		artline = 0;		/* start at the beginning */
X		topline = -1;		/* and remember top line of screen */
X					/*  (line # within article file) */
X	    }
X	    clear();			/* clear screen */
X#ifdef USE_NNTP
X	    if (art == 0 && artp && artp->msgid
X	     && !(artp->flags & AF_CACHED)) {
X		art = nntp_stat_id(artp->msgid);
X		if (art)
X		    artp = find_article(art);
X	    }
X#endif
X	    if (!artopen(art)) {	/* make sure article is found & open */
X		char tmpbuf[256];
X		ART_LINE linenum;
X		/* see if we have tree data for this article anyway */
X		init_tree();
X		sprintf(tmpbuf,"%s: article is not available.",ngname);
X		if (artp && !(artp->flags & AF_CACHED)) {
X		    if (absfirst < first_cached || last_cached < lastart
X		     || !cached_all_in_range)
X			sprintf(tmpbuf,"%s: article may show up in a moment.",
X				ngname);
X		}
X		linenum = tree_puts(tmpbuf,0,0);
X		vwtary(artline,(ART_POS)0);
X		finish_tree(linenum);
X		prompt = whatnext;
X#ifdef ARTSEARCH
X		srchahead = 0;
X#endif
X	    }
X	    else {			/* found it, so print it */
X		switch (do_article()) {
X		case DA_CLEAN:		/* quit newsgroup */
X		    goto cleanup;
X		case DA_TOEND:		/* do not mark as read */
X		    goto reask_article; 
X		case DA_RAISE:		/* reparse command at end of art */
X		    goto article_level;
X		case DA_NORM:		/* normal end of article */
X		    break;
X		}
X	    }
X	    if (art >= absfirst)	/* don't mark non-existant articles */
X		mark_as_read();		/* mark current article as read */
X	    do_hiding = TRUE;
X#ifdef ROTATION
X	    rotate = FALSE;
X#endif
X	}
X
X/* if these gotos bother you, think of this as a little state machine */
X
Xreask_article:
X#ifdef MAILCALL
X	setmail(FALSE);
X#endif
X	setdfltcmd();
X#ifdef CLEAREOL
X	if (erase_screen && can_home_clear)
X	    clear_rest();
X#endif /* CLEAREOL */
X	unflush_output();		/* disable any ^O in effect */
X	standout();			/* enter standout mode */
X	/* print prompt, whatever it is */
X	interp(cmd_buf, sizeof cmd_buf, mailcall);
X#ifdef CHARSUBST
X	printf(prompt,cmd_buf,current_charsubst(),dfltcmd);
X#else
X	printf(prompt,cmd_buf,dfltcmd);
X#endif
X	un_standout();			/* leave standout mode */
X	putchar(' ');
X	fflush(stdout);
Xreinp_article:
X	reread = FALSE;
X	forcelast = FALSE;
X	eat_typeahead();
X#ifdef PENDING
X	look_ahead();			/* see what we can do in advance */
X	cache_until_key();
X#endif
X	art = curr_art;
X	artp = curr_artp;
X	getcmd(buf);
X	if (errno || *buf == '\f') {
X	    if (LINES < 100 && !int_count)
X		*buf = '\f';		/* on CONT fake up refresh */
X	    else {
X		putchar('\n') FLUSH;		/* but only on a crt */
X		goto reask_article;
X	    }
X	}
Xarticle_level:
X	output_chase_phrase = TRUE;
X
X	/* parse and process article level command */
X
X	switch (art_switch()) {
X	case AS_INP:			/* multichar command rubbed out */
X	    goto reinp_article;
X	case AS_ASK:			/* reprompt "End of article..." */
X	    goto reask_article;
X	case AS_CLEAN:			/* exit newsgroup */
X	    goto cleanup;
X	case AS_NORM:			/* display article art */
X	    break;
X	}
X    }					/* end of article selection loop */
X    
X/* shut down newsgroup */
X
Xcleanup:
X    decode_end();
X#ifdef KILLFILES
X    kill_unwanted(firstart,"\nCleaning up...\n\n",FALSE);
X					/* do cleanup from KILL file, if any */
X#endif
X    chase_xrefs(FALSE);
X    in_ng = FALSE;			/* leave newsgroup state */
X    if (artfp != Nullfp) {		/* article still open? */
X	fclose(artfp);			/* close it */
X	artfp = Nullfp;			/* and tell the world */
X	openart = 0;
X    }
X    putchar('\n') FLUSH;
X    deselect_all();
X    yankback();				/* do a Y command */
X    bits_to_rc();			/* reconstitute .newsrc line */
X    doing_ng = FALSE;			/* tell sig_catcher to cool it */
X    if (!unsafe_rc_saves)
X	write_rc();			/* and update .newsrc */
X    rc_changed = FALSE;			/* tell sig_catcher it is ok */
X    if (chdir(spool)) {
X	printf(nocd,spool) FLUSH;
X	sig_catcher(0);
X    }
X#ifdef KILLFILES
X    if (localkfp) {
X	fclose(localkfp);
X	localkfp = Nullfp;
X    }
X#endif
X    mode = oldmode;
X    return exit_code;
X}					/* Whew! */
X
X/* decide what to do at the end of an article */
X
Xint
Xart_switch()
X{
X    register ART_NUM i;
X      
X    setdef(buf,dfltcmd);
X#ifdef VERIFY
X    printcmd();
X#endif
X
X    switch (*buf) {
X    case '<':			/* goto previous subject/thread */
X	visit_prev_thread();
X	return AS_NORM;
X    case '>':			/* goto next subject/thread */
X	visit_next_thread();
X	return AS_NORM;
X    case 'U': {			/* unread some articles */
X	char *u_prompt, *u_help_thread;
X
X	if (!artp) {
X	    u_help_thread = nullstr;
X#ifdef VERBOSE
X	    IF(verbose)
X		u_prompt = "\nSet unread: +select or all?";
X	    ELSE
X#endif
X#ifdef TERSE
X		u_prompt = "\nSet unread?";
X#endif
X	    dfltcmd = "+an";
X	}
X	else {
X#ifdef VERBOSE
X	    IF(verbose) {
X		u_prompt = "\n\
XSet unread: +select, thread, subthread, or all?";
X		u_help_thread = "\
XType t or SP to mark this thread's articles as unread.\n\
XType s to mark the current article and its descendants as unread.\n";
X	    }
X	    ELSE
X#endif
X#ifdef TERSE
X	    {
X		u_prompt = "\nSet unread?";
X		u_help_thread = "\
Xt or SP to mark thread unread.\n\
Xs to mark subthread unread.\n";
X	    }
X#endif
X	    dfltcmd = "+tsan";
X	}
X      reask_unread:
X	in_char(u_prompt,'u',dfltcmd);
X#ifdef VERIFY
X	printcmd();
X#endif
X	putchar('\n') FLUSH;
X	if (*buf == 'h') {
X#ifdef VERBOSE
X	    IF(verbose)
X	    {
X		fputs("\
XType + to enter select thread mode using all the already-read articles.\n\
X(The selected threads will be marked as unread and displayed as usual.)\n\
X",stdout) FLUSH;
X		fputs(u_help_thread,stdout);
X		fputs("\
XType a to mark all articles in this group as unread.\n\
XType n to change nothing.\n\
X",stdout) FLUSH;
X	    }
X	    ELSE
X#endif
X#ifdef TERSE
X	    {
X		fputs("\
X+ to select threads from the unread.\n\
X",stdout) FLUSH;
X		fputs(u_help_thread,stdout);
X		fputs("\
Xa to mark all articles unread.\n\
Xn to change nothing.\n\
X",stdout) FLUSH;
X	    }
X#endif
X	    goto reask_unread;
X	}
X	else if (*buf == 'n' || *buf == 'q')
X	    return AS_ASK;
X	else if (*buf == 't' && u_help_thread != nullstr) {
X	    unkill_thread(artp->subj->thread);
X	    if ((artp = first_art(artp->subj)) != Nullart)
X		art = article_num(artp);
X	} else if (*buf == 's' && u_help_thread != nullstr)
X	    unkill_subthread(artp);
X	else if (*buf == 'a') {
X	    register ARTICLE *ap;
X	    check_first(absfirst);
X	    ap = article_ptr(absfirst);
X	    for (i = absfirst; i <= lastart; i++, ap++)
X		if ((ap->flags & (AF_READ|AF_MISSING)) == AF_READ) {
X		    ap->flags &= ~AF_READ;		/* mark as unread */
X		    toread[ng]++;
X		}
X	    count_subjects(CS_NORM);
X	}
X	else if (*buf == '+') {
X	    *buf = 'U';
X	    goto run_the_selector;
X	}
X	else {
X	    fputs(hforhelp,stdout) FLUSH;
X	    settle_down();
X	    goto reask_unread;
X	}
X	return AS_NORM;
X    }
X    case '[':			/* goto parent article */
X    case '{':			/* goto thread's root article */
X	if (artp && ThreadedGroup) {
X	    if (!find_parent(*buf == '{')) {
X		register char *cp = (*buf=='['?"parent":"root");
X#ifdef VERBOSE
X		IF(verbose)
X		    printf("\nThere is no %s article prior to this one.\n",
X			cp) FLUSH;
X		ELSE
X#endif
X#ifdef TERSE
X		    printf("\nNo prior %s.\n",cp) FLUSH;
X#endif
X		return AS_ASK;
X	    }
X	    reread = TRUE;
X	    return AS_NORM;
X	}
Xnot_threaded:
X	if (!artp) {
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs("\nYou're at the end of the group.\n",stdout) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("\nEnd of group.\n",stdout) FLUSH;
X#endif
X	    return AS_ASK;
X	}
X#ifdef VERBOSE
X	IF(verbose)
X	    fputs("\nThis group is not threaded.\n",stdout) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    fputs("\nUnthreaded group.\n",stdout) FLUSH;
X#endif
X	return AS_ASK;
X    case ']':			/* goto child article */
X    case '}':			/* goto thread's leaf article */
X	if (artp && ThreadedGroup) {
X	    if (!find_leaf(*buf == '}')) {
X#ifdef VERBOSE
X		IF(verbose)
X		    fputs("\n\
XThis is the last leaf in this tree.\n",stdout) FLUSH;
X		ELSE
X#endif
X#ifdef TERSE
X		    fputs("\nLast leaf.\n",stdout) FLUSH;
X#endif
X		return AS_ASK;
X	    }
X	    reread = TRUE;
X	    return AS_NORM;
X	}
X	goto not_threaded;
X    case '(':			/* goto previous sibling */
X    case ')':			/* goto next sibling */
X	if (artp && ThreadedGroup) {
X	    if (!(*buf == '(' ? find_prev_sib() : find_next_sib())) {
X		register char *cp = (*buf == '(' ? "previous" : "next");
X#ifdef VERBOSE
X		IF(verbose)
X		    printf("\nThis article has no %s sibling.\n",cp) FLUSH;
X		ELSE
X#endif
X#ifdef TERSE
X		    printf("\nNo %s sibling.\n",cp) FLUSH;
X#endif
X		return AS_ASK;
X	    }
X	    reread = TRUE;
X	    return AS_NORM;
X	}
X	goto not_threaded;
X    case 'T':
X	if (!ThreadedGroup)
X	    goto not_threaded;
X	/* FALL THROUGH */
X    case 'A':
X	if (!artp)
X	    goto not_threaded;
X	switch (ask_memorize(*buf)) {
X	case ',':  case 'j':
X	    return AS_NORM;
X	}
X	return AS_ASK;
X    case 'K':
X	if (!artp)
X	    goto not_threaded;
X	/* first, write kill-subject command */
X	(void)art_search(buf, (sizeof buf), TRUE);
X	art = curr_art;
X	artp = curr_artp;
X	kill_subject(artp->subj,KF_ALL);/* take care of any prior subjects */
X	return AS_NORM;
X    case ',':		/* kill this node and all descendants */
X	if (!artp)
X	    goto not_threaded;
X	if (ThreadedGroup)
X	    kill_subthread(artp,KF_ALL);
X	else if (art >= absfirst && art <= lastart)
X	    mark_as_read();
X	return AS_NORM;
X    case 'J':		/* Junk all nodes in this thread */
X	if (!artp)
X	    goto not_threaded;
X	if (ThreadedGroup) {
X	    kill_thread(artp->subj->thread,KF_ALL);
X	    return AS_NORM;
X	}
X	/* FALL THROUGH */
X    case 'k':		/* kill current subject */
X	if (!artp)
X	    goto not_threaded;
X	kill_subject(artp->subj,KF_ALL);
X	if (!ThreadedGroup || last_cached < lastart) {
X	    *buf = 'k';
X	    goto normal_search;
X	}
X	return AS_NORM;
X    case 't':
X	carriage_return();
X#ifndef CLEAREOL
X	erase_eol();		/* erase the prompt */
X#else
X	if (erase_screen && can_home_clear)
X	    clear_rest();
X	else
X	    erase_eol();	/* erase the prompt */
X#endif /* CLEAREOL */
X	fflush(stdout);
X	page_line = 1;
X	entire_tree(curr_artp);
X	return AS_ASK;
X    case ':':			/* execute command on selected articles */
X	page_line = 1;
X	if (!thread_perform())
X	    return AS_INP;
X	putchar('\n');
X	art = curr_art;
X	artp = curr_artp;
X	return AS_ASK;
X    case 'p':			/* find previous unread article */
X	do {
X	    dec_art(selected_only,FALSE);
X	} while (art >= firstart && (was_read(art) || !parseheader(art)));
X#ifdef ARTSEARCH
X	srchahead = 0;
X#endif
X	if (art >= firstart)
X	    return AS_NORM;
X	art = absfirst;	
X	/* FALL THROUGH */
X    case 'P':		/* goto previous article */
X	dec_art(FALSE,TRUE);
X      check_dec_art:
X	if (art < absfirst) {
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("\nThere are no%s%s articles prior to this one.\n",
X			*buf=='P'?nullstr:" unread",
X			selected_only?" selected":nullstr) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		printf("\nNo previous%s%s articles\n",
X			*buf=='P'?nullstr:" unread",
X			selected_only?" selected":nullstr) FLUSH;
X#endif
X	    art = curr_art;
X	    artp = curr_artp;
X	    return AS_ASK;
X	}
X	reread = TRUE;
X#ifdef ARTSEARCH
X	srchahead = 0;
X#endif
X	return AS_NORM;
X    case '-':
X	if (recent_art >= 0) {
X	    art = recent_art;
X	    artp = recent_artp;
X	    reread = TRUE;
X	    forcelast = TRUE;
X#ifdef ARTSEARCH
X	    srchahead = -(srchahead != 0);
X#endif
X	    return AS_NORM;
X	}
X	else {
X	    exit_code = NG_MINUS;
X	    return AS_CLEAN;
X	}
X    case 'n':		/* find next unread article? */
X	if (art > lastart) {
X	    if (!toread[ng])
X		return AS_CLEAN;
X	    top_article();
X	}
X#ifdef ARTSEARCH
X	else if (scanon && !ThreadedGroup && srchahead) {
X	    *buf = Ctl('n');
X	    if (!next_art_with_subj())
X		goto normal_search;
X	    return AS_NORM;
X	}
X#endif
X	else {
X	    inc_art(selected_only,FALSE);
X	    if (art > lastart)
X		top_article();
X	}
X#ifdef ARTSEARCH
X	srchahead = 0;
X#endif
X	return AS_NORM;
X    case 'N':			/* goto next article */
X	if (art > lastart)
X	    if (!first_subject) {
X		art = absfirst;
X		artp = article_ptr(art);
X	    } else {
X		artp = first_subject->articles;
X		if (artp->flags & AF_MISSING)
X		    inc_art(FALSE,TRUE);
X		else
X		    art = article_num(artp);
X	    }
X	else
X	    inc_art(FALSE,TRUE);
X	if (art <= lastart)
X	    reread = TRUE;
X	else
X	    forcelast = TRUE;
X#ifdef ARTSEARCH
X	srchahead = 0;
X#endif
X	return AS_NORM;
X    case '$':
X	art = lastart+1;
X	artp = Nullart;
X	forcelast = TRUE;
X#ifdef ARTSEARCH
X	srchahead = 0;
X#endif
X	return AS_NORM;
X    case '1': case '2': case '3':	/* goto specified article */
X    case '4': case '5': case '6':	/* or do something with a range */
X    case '7': case '8': case '9': case '.':
X	forcelast = TRUE;
X	switch (numnum()) {
X	case NN_INP:
X	    return AS_INP;
X	case NN_ASK:
X	    return AS_ASK;
X	case NN_REREAD:
X	    reread = TRUE;
X#ifdef ARTSEARCH
X	    if (srchahead)
X		srchahead = -1;
X#endif
X	    break;
X	case NN_NORM:
X	    if (was_read(art)) {
X		top_article();
X		pad(just_a_sec/3);
X	    }
X	    else {
X		putchar('\n');
X		return AS_ASK;
X	    }
X	    break;
X	}
X	return AS_NORM;
X    case Ctl('k'):
X	edit_kfile();
X	return AS_ASK;
X    case Ctl('n'):	/* search for next article with same subject */
X    case Ctl('p'):	/* search for previous article with same subject */
X	if (*buf == Ctl('n')? next_art_with_subj() : prev_art_with_subj())
X	    return AS_NORM;
X    case '/': case '?':
Xnormal_search:
X#ifdef ARTSEARCH
X    {		/* search for article by pattern */
X	char cmd = *buf;
X	
X	reread = TRUE;		/* assume this */
X	page_line = 1;
X	switch (art_search(buf, (sizeof buf), TRUE)) {
X	case SRCH_ERROR:
X	    art = curr_art;
X	    return AS_ASK;
X	case SRCH_ABORT:
X	    art = curr_art;
X	    return AS_INP;
X	case SRCH_INTR:
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("\n(Interrupted at article %ld)\n",(long)art) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		printf("\n(Intr at %ld)\n",(long)art) FLUSH;
X#endif
X	    art = curr_art;	    /* restore to current article */
X	    return AS_ASK;
X	case SRCH_DONE:
X	    fputs("done\n",stdout) FLUSH;
X	    pad(just_a_sec/3);	/* 1/3 second */
X	    if (!srchahead) {
X		art = curr_art;
X		return AS_ASK;
X	    }
X	    top_article();
X	    reread = FALSE;
X	    return AS_NORM;
X	case SRCH_SUBJDONE:
X#ifdef UNDEF
X	    fputs("\n\n\n\nSubject not found.\n",stdout) FLUSH;
X	    pad(just_a_sec/3);	/* 1/3 second */
X#endif
X	    top_article();
X	    reread = FALSE;
X	    return AS_NORM;
X	case SRCH_NOTFOUND:
X	    fputs("\n\n\n\nNot found.\n",stdout) FLUSH;
X	    art = curr_art;  /* restore to current article */
X	    return AS_ASK;
X	case SRCH_FOUND:
X	    if (cmd == Ctl('n') || cmd == Ctl('p')) {
X		oldsubject = TRUE;
X		reread = FALSE;
X	    }
X	    break;
X	}
X	return AS_NORM;
X    }
X#else /* !ARTSEARCH */
X	buf[1] = '\0';
X	notincl(buf);
X	return AS_ASK;
X#endif
X    case 'u':			/* unsubscribe from this newsgroup? */
X	rcchar[ng] = NEGCHAR;
X	return AS_CLEAN;
X    case 'M':
X	if (art <= lastart) {
X	    delay_unmark(artp);
X	    oneless(artp);
X	    printf("\nArticle %ld will return.\n",(long)art) FLUSH;
X	}
X	return AS_ASK;
X    case 'm':
X	if (art >= absfirst && art <= lastart) {
X	    unmark_as_read();
X	    printf("\nArticle %ld marked as still unread.\n",(long)art) FLUSH;
X	}
X	return AS_ASK;
X    case 'c':			/* catch up */
X	switch (ask_catchup()) {
X	case 'n':
X	    return AS_ASK;
X	case 'u':
X	    return AS_CLEAN;
X	}
X	art = lastart+1;
X	artp = Nullart;
X	forcelast = FALSE;
X	return AS_NORM;
X    case 'Q':
X	exit_code = NG_ASK;
X	/* FALL THROUGH */
X    case 'q':			/* go back up to newsgroup level? */
X	return AS_CLEAN;
X    case 'j':
X	putchar('\n') FLUSH;
X	if (art >= absfirst && art <= lastart)
X	    mark_as_read();
X	return AS_ASK;
X    case 'h': {			/* help? */
X	int cmd;
X
X	if ((cmd = help_art()) > 0)
X	    pushchar(cmd);
X	return AS_ASK;
X    }
X    case '&':
X	if (switcheroo()) /* get rest of command */
X	    return AS_INP;	/* if rubbed out, try something else */
X	return AS_ASK;
X    case '#':
X#ifdef VERBOSE
X	IF(verbose)
X	    printf("\nThe last article is %ld.\n",(long)lastart) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    printf("\n%ld\n",(long)lastart) FLUSH;
X#endif
X	return AS_ASK;
X    case '+':			/* enter selection mode */
Xrun_the_selector:
X	*buf = do_selector(*buf);
X	switch (*buf) {
X	case '+':
X	    putchar('\n') FLUSH;
X	    return AS_ASK;
X	case 'Q':
X	    exit_code = NG_ASK;
X	    /* FALL THROUGH */
X	case 'q':
X	    break;
X	case 'N':
X	    exit_code = NG_SELNEXT;
X	    break;
X	case 'P':
X	    exit_code = NG_SELPRIOR;
X	    break;
X	default:
X	    if (toread[ng])
X		return AS_NORM;
X	    break;
X	}
X	return AS_CLEAN;
X    case '=': {			/* list subjects */
X	char tmpbuf[256];
X	ART_NUM oldart = art;
X	int cmd, len;
X	char *s;
X	char *subjline = getval("SUBJLINE",Nullch);
X	ARTICLE *ap = article_ptr(firstart);
X
X	page_init();
X	for (i=firstart; i<=lastart && !int_count; i++, ap++) {
X	    if (!(ap->flags & AF_READ) && (s = fetchsubj(i,FALSE)) != Nullch) {
X		sprintf(tmpbuf,"%5ld ", i);
X		len = strlen(tmpbuf);
X		if (subjline) {
X		    art = i;
X		    interp(tmpbuf + len, sizeof tmpbuf - len, subjline);
X		}
X		else
X		    safecpy(tmpbuf + len, s, sizeof tmpbuf - len);
X		if (cmd = print_lines(tmpbuf,NOMARKING)) {
X		    if (cmd > 0)
X			pushchar(cmd);
X		    break;
X		}
X	    }
X	}
X	int_count = 0;
X	art = oldart;
X	return AS_ASK;
X    }
X    case '^':
X	top_article();
X#ifdef ARTSEARCH
X	srchahead = 0;
X#endif
X	return AS_NORM;
X#ifdef DEBUG
X    case 'D':
X	printf("\nFirst article: %ld\n",(long)firstart) FLUSH;
X	{
X	    ARTICLE *ap = article_ptr(firstart);
X	    for (i = firstart; i <= lastart && !int_count; i++, ap++) {
X		if (ap->subj)
X		    printf("%5ld %c %s\n",i,(was_read(i)?'y':'n'),
X			   ap->subj->str) FLUSH;
X	    }
X	}
X	int_count = 0;
X	return AS_ASK;
X#endif
X    case 'v':
X	if (art <= lastart) {
X	    reread = TRUE;
X	    do_hiding = FALSE;
X	}
X	return AS_NORM;
X#ifdef ROTATION
X    case Ctl('x'):
X#endif
X    case Ctl('r'):
X#ifdef ROTATION
X	rotate = (*buf==Ctl('x'));
X#endif
X	if (art <= lastart)
X	    reread = TRUE;
X	else
X	    forcelast = TRUE;
X	return AS_NORM;
X#ifdef ROTATION
X    case 'X':
X	rotate = !rotate;
X	/* FALL THROUGH */
X#else
X    case Ctl('x'):
X    case 'x':
X    case 'X':
X	notincl("x");
X	return AS_ASK;
X#endif
X    case 'l': case Ctl('l'):		/* refresh screen */
X    refresh_screen:
X	if (art <= lastart) {
X	    reread = TRUE;
X	    clear();
X	    do_fseek = TRUE;
X	    artline = topline;
X	    if (artline < 0)
X		artline = 0;
X	}
X	return AS_NORM;
X    case Ctl('^'):
X	carriage_return();
X	erase_eol();		/* erase the prompt */
X#ifdef MAILCALL
X	setmail(TRUE);		/* force a mail check */
X#endif
X	return AS_ASK;
X#ifdef INNERSEARCH
X    case Ctl('e'):
X	if (art <= lastart) {
X	    reread = TRUE;
X	    do_fseek = TRUE;
X	    innerlight = artline - 1;
X	    topline = artline;
X	    innersearch = artsize;
X	    gline = 0;
X	    hide_everything = 'b';
X	}
X	return AS_NORM;
X#endif
X    case 'B':				/* back up one line */
X    case 'b': case Ctl('b'):		/* back up a page */
X	if (art <= lastart) {
X	    ART_LINE target;
X
X	    reread = TRUE;
X	    clear();
X	    do_fseek = TRUE;
X	    if (*buf == 'B')
X		target = topline - 1;
X	    else {
X		target = topline - (LINES - 2);
X		if (marking && (marking_areas & BACKPAGE_MARKING)) {
X		    highlight = topline;
X		}
X	    }
X	    artline = topline;
X	    if (artline >= 0) do {
X		artline--;
X	    } while(artline >= 0 && artline > target && vrdary(artline-1) >= 0);
X	    topline = artline;
X	    if (artline < 0)
X		artline = 0;
X	}
X	return AS_NORM;
X    case '!':			/* shell escape */
X	if (escapade())
X	    return AS_INP;
X	return AS_ASK;
X    case 'C': {
X	cancel_article();
X	return AS_ASK;
X    }
X    case 'Z':
X    case 'z': {
X	supersede_article();	/* supersedes */
X	return AS_ASK;
X    }
X    case 'R':
X    case 'r': {			/* reply? */
X	reply();
X	return AS_ASK;
X    }
X    case 'F':
X    case 'f': {			/* followup command */
X	followup();
X	forcegrow = TRUE;		/* recalculate lastart */
X	return AS_ASK;
X    }
X    case Ctl('f'): {			/* forward? */
X	forward();
X	return AS_ASK;
X    }
X    case '|':
X    case 'w': case 'W':
X    case 's': case 'S':		/* save command */
X    case 'e':			/* extract command */
X	if (save_article() == SAVE_ABORT)
X	    return AS_INP;
X	int_count = 0;
X	return AS_ASK;
X    case 'E':
X	if (decode_fp)
X	    decode_end();
X	else
X	    putchar('\n') FLUSH;
X	return AS_ASK;
X    case 'Y':				/* yank back M articles */
X	yankback();
X	top_article();			/* from the beginning */
X	return AS_NORM;			/* pretend nothing happened */
X#ifdef STRICTCR
X    case '\n':
X	fputs(badcr,stdout) FLUSH;
X	return AS_ASK;
X#endif
X    case '_':
X	if (!finish_dblchar())
X	    return AS_INP;
X	switch (buf[1] & 0177) {
X	case 'P':
X	    art--;
X	    goto check_dec_art;
X	case 'N':
X	    if (art > lastart)
X		art = absfirst;
X	    else
X		art++;
X	    if (art <= lastart)
X		reread = TRUE;
X#ifdef ARTSEARCH
X	    srchahead = 0;
X#endif
X	    return AS_NORM;
X	case '+':
X	    if (!artp)
X		goto not_threaded;
X	    if (ThreadedGroup) {
X		select_arts_thread(artp, 0);
X		printf("\nSelected all articles in this thread.\n");
X	    } else {
X		select_arts_subject(artp, 0);
X		printf("\nSelected all articles in this subject.\n");
X	    }
X	    if ((artp = first_art(artp->subj)) != Nullart) {
X		if (art == article_num(artp))
X		    return AS_ASK;
X		art = article_num(artp);
X	    }
X	    return AS_NORM;
X	case '-':
X	    if (!artp)
X		goto not_threaded;
X	    if (sel_mode == SM_THREAD) {
X		deselect_arts_thread(artp);
X		printf("\nDeselected all articles in this thread.\n");
X	    } else {
X		deselect_arts_subject(artp);
X		printf("\nDeselected all articles in this subject.\n");
X	    }
X	    return AS_ASK;
X#ifdef CHARSUBST
X	case 'C':
X	    if (!*(++charsubst))
X		charsubst = charsets;
X	    goto refresh_screen;
X#endif
X	case 'a':  case 's':  case 't':  case 'T':
X	    *buf = buf[1];
X	    goto run_the_selector;
X	}
X	/* FALL THROUGH */
X    default:
X	printf("\n%s",hforhelp) FLUSH;
X	settle_down();
X	break;
X    }
X    return AS_ASK;
X}
X
X#ifdef MAILCALL
X/* see if there is any mail */
X
Xvoid
Xsetmail(force)
Xbool_int force;
X{
X    if (force)
X	mailcount = 0;
X    if (!(mailcount++)) {
X	char *mailfile = filexp(getval("MAILFILE",MAILFILE));
X	
X	if (stat(mailfile,&filestat) < 0 || !filestat.st_size
X	    || filestat.st_atime > filestat.st_mtime)
X	    mailcall = nullstr;
X	else
X	    mailcall = getval("MAILCALL","(Mail) ");
X    }
X    mailcount %= 5;			/* check every 5 articles */
X}
X#endif
X
Xvoid
Xsetdfltcmd()
X{
X    if (!toread[ng]) {
X	if (art > lastart)
X	    dfltcmd = "qnp";
X	else
X	    dfltcmd = "npq";
X    }
X    else {
X#ifdef ARTSEARCH
X	if (srchahead)
X	    dfltcmd = "^Nnpq";
X	else
X#endif
X	    dfltcmd = "npq";
X    }
X}
X
X/* Ask the user about catching-up the current group.  Returns 'y' if yes,
X** 'n' or 'N' if no ('N' means we used one line when in the selector),
X** or 'u' for yes with unsubscribe.  Actually performs the catchup and
X** unsubscription as needed.
X*/
Xchar
Xask_catchup()
X{
X    char ch;
X    bool use_one_line = (mode == 't');
X    int leave = 0;
X
X    if (!use_one_line)
X	putchar('\n') FLUSH;
Xreask_catchup:
X#ifdef VERBOSE
X    IF(verbose)
X	in_char("Do you really want to mark everything as read?",'C',"yn#h");
X    ELSE
X#endif
X#ifdef TERSE
X	in_char("Really?",'C',"ynh");
X#endif
X#ifdef VERIFY
X    printcmd();
X#endif
X    if ((ch = *buf) == 'h') {
X	use_one_line = FALSE;
X#ifdef VERBOSE
X	IF(verbose)
X	    fputs("\n\
XType y or SP to mark all articles as read.\n\
XType n to leave articles marked as they are.\n\
XEnter a number to mark all but the last # articles as read.\n\
XType u to mark everything read and unsubscribe.\n\n\
X",stdout) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    fputs("\n\
Xy or SP to mark all read.\n\
Xn to forget it.\n\
Xu to mark all and unsubscribe.\n\n\
X",stdout) FLUSH;
X#endif
X	goto reask_catchup;
X    }
X    if (ch == 'n' || ch == 'q') {
X	if (use_one_line)
X	    return 'N';
X	putchar('\n') FLUSH;
X	return 'n';
X    }
X    if (ch == '#') {
X	use_one_line = FALSE;
X	in_char("\nEnter the number of articles to leave unread: ", 'C', "0");
X	if ((ch = *buf) == '0')
X	    ch = 'y';
X    }
X    if (isdigit(ch)) {
X	buf[1] = FINISHCMD;
X	if (!finish_command(FALSE)) {
X	    use_one_line = FALSE;
X	    putchar('\n') FLUSH;
X	    goto reask_catchup;
X	}
X	else {
X	    leave = atoi(buf);
X	    ch = 'y';
X	}
X    }
X    if (ch != 'y' && ch != 'u') {
X	use_one_line = FALSE;
X	printf("\n%s\n", hforhelp) FLUSH;
X	settle_down();
X	goto reask_catchup;
X    }
X    if (mode == 'n') {
X	putchar('\n') FLUSH;
X	catch_up(ng, leave);
X    }
X    else {
X	int i;
X	ARTICLE *ap;
X	for (i = firstart, ap = article_ptr(i); i <= lastart-leave; i++, ap++)
X	    ap->flags = ((ap->flags & ~sel_mask) | AF_READ);
X	selected_count = selected_subj_cnt = selected_only = 0;
X	toread[ng] = 0;
X	if (dmcount)
X	    yankback();
X	putchar('\n') FLUSH;
X    }
X    if (ch == 'u') {
X	rcchar[ng] = NEGCHAR;
X	printf("(If you meant to hit 'y' instead of 'u', press '-'.)\n");
X    }
X    return ch;
X}
X
Xchar
Xask_memorize(ch)
Xchar_int ch;
X{
X    bool thread_cmd = (ch == 'T');
X    bool use_one_line = (mode == 't');
X    char *mode_string = (thread_cmd? "thread" : "subject");
X    char *mode_phrase = (thread_cmd? "replies to this article" :
X				     "this subject and all replies");
X    ART_NUM art_hold = art;
X    ARTICLE *artp_hold = artp;
X
X    if (!use_one_line)
X	putchar('\n') FLUSH;
X    sprintf(cmd_buf,"Memorize %s command:", mode_string);
Xreask_memorize:
X    in_char(cmd_buf, 'm', "+.j,cC");
X#ifdef VERIFY
X    printcmd();
X#endif
X    if ((ch = *buf) == 'h') {
X	use_one_line = FALSE;
X#ifdef VERBOSE
X	IF(verbose)
X	    printf("\n\
XType + or SP to auto-select this %s (i.e. includes future articles).\n\
XType . to auto-select %s.\n\
XType j to auto-kill (junk) this %s.\n\
XType , to auto-kill %s.\n\
XType c to clear all selection/killing on this %s.\n\
XType C to clear all selection/killing on %s.\n\
XType q to abort the operation.\n\n\
X",mode_string,mode_phrase,mode_string,mode_phrase,mode_string,mode_phrase) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    printf("\n\
X+ or SP auto-selects this %s.\n\
X. auto-selects %s.\n\
Xj auto-kills this %s.\n\
X, auto-kills %s.\n\
Xc clears auto-commands for this %s.\n\
XC clears auto-commands for %s.\n\
Xq aborts.\n\n\
X",mode_string,mode_phrase,mode_string,mode_phrase,mode_string,mode_phrase) FLUSH;
X#endif
X	goto reask_memorize;
X    }
X    if (ch == 'q') {
X	if (use_one_line)
X	    return 'Q';
X	putchar('\n');
X	return 'q';
X    }
X    if (ch == '+') {
X	if (!thread_cmd) {
X	    (void)art_search(buf, (sizeof buf), TRUE);
X	    art = art_hold;
X	    artp = artp_hold;
X	    ch = '.';
X	} else
X	    ch = (use_one_line? '+' : '.');
X	if (thread_cmd)
X	    select_arts_thread(artp, AUTO_SELECTALL);
X	else
X	    select_arts_subject(artp, 0);
X	if (mode != 't')
X	    printf("\nSelection memorized.\n");
X    } else if (ch == '.') {
X	if (!thread_cmd) {
X	    (void)art_search(buf, (sizeof buf), TRUE);
X	    art = art_hold;
X	    artp = artp_hold;
X	} else
X	    ch = (use_one_line? '+' : '.');
X	select_subthread(artp,thread_cmd? AUTO_SELECT : 0);
X	if (mode != 't')
X	    printf("\nSelection memorized.\n");
X    } else if (ch == 'j') {
X	if (!thread_cmd) {
X	    *buf = 'K';
X	    (void)art_search(buf, (sizeof buf), TRUE);
X	    art = art_hold;
X	    artp = artp_hold;
X	}
X	if (thread_cmd)
X	    kill_thread(artp->subj->thread,KF_ALL|KF_KILLFILE);
X	else
X	    kill_subject(artp->subj,KF_ALL);
X	if (mode != 't')
X	    printf("\nKill memorized.\n");
X    } else if (ch == ',') {
X	if (!thread_cmd) {
X	    (void)art_search(buf, (sizeof buf), TRUE);
X	    art = art_hold;
X	    artp = artp_hold;
X	}
X	kill_subthread(artp,KF_ALL|(thread_cmd?KF_KILLFILE:0));
X	if (mode != 't')
X	    printf("\nKill memorized.\n");
X    } else if (ch == 'c') {
X	if (thread_cmd)
X	    clear_thread(artp->subj->thread);
X	else
X	    clear_subject(artp->subj);
X    } else if (ch == 'C') {
X	clear_subthread(artp);
X    } else {
X	use_one_line = FALSE;
X	printf("\n%s\n", hforhelp) FLUSH;
X	settle_down();
X	goto reask_memorize;
X    }
X    if (!use_one_line)
X	putchar('\n') FLUSH;
X    return ch;
X}
SHAR_EOF
  $shar_touch -am 1118220194 'trn-3.6/ng.c' &&
  chmod 0644 'trn-3.6/ng.c' ||
  echo 'restore of trn-3.6/ng.c failed'
  shar_count="`wc -c < 'trn-3.6/ng.c'`"
  test 35273 -eq "$shar_count" ||
    echo "trn-3.6/ng.c: original size 35273, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/ng.h ==============
if test -f 'trn-3.6/ng.h' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/ng.h (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/ng.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/ng.h' &&
X/* $Id: ng.h,v 3.0 1991/09/09 20:23:31 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 art INIT(0);	/* current or prospective article # */
X
XEXT ART_NUM recent_art;		/* previous article # for '-' command */
XEXT ART_NUM curr_art;		/* current article # */
XEXT ARTICLE *recent_artp INIT(0);/* article_ptr equivilents */
XEXT ARTICLE *curr_artp INIT(0);
XEXT ARTICLE *artp INIT(0);  /* the article ptr we use when art is 0 */
X
XEXT int checkcount INIT(0);	/* how many articles have we read */
X			/*   in the current newsgroup since */
X			/*   the last checkpoint? */
XEXT int docheckwhen INIT(20);	/* how often to do checkpoint */
X
X#ifdef MAILCALL
XEXT int mailcount INIT(0);		/* check for mail when 0 mod 10 */
X#endif
XEXT char *mailcall INIT(nullstr);
XEXT bool forcelast INIT(FALSE);		/* ought we show "End of newsgroup"? */
XEXT bool forcegrow INIT(FALSE);		/* do we want to recalculate size */
X				    /* of newsgroup, e.g. after posting? */
X
X#define NG_ERROR -1
X#define NG_NORM 0
X#define NG_ASK 1
X#define NG_MINUS 2
X#define NG_SELPRIOR 3
X#define NG_SELNEXT 4
X
Xvoid    ng_init _((void));
Xint	do_newsgroup _((char*));
Xint	art_switch _((void));
X#ifdef MAILCALL
Xvoid	setmail _((bool_int));
X#endif
Xvoid	setdfltcmd _((void));
Xchar	ask_catchup _((void));
Xchar	ask_memorize _((char_int));
SHAR_EOF
  $shar_touch -am 0711223693 'trn-3.6/ng.h' &&
  chmod 0644 'trn-3.6/ng.h' ||
  echo 'restore of trn-3.6/ng.h failed'
  shar_count="`wc -c < 'trn-3.6/ng.h'`"
  test 1846 -eq "$shar_count" ||
    echo "trn-3.6/ng.h: original size 1846, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= trn-3.6/ngdata.c ==============
if test -f 'trn-3.6/ngdata.c' && test X"$1" != X"-c"; then
  echo 'x - skipping trn-3.6/ngdata.c (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting trn-3.6/ngdata.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'trn-3.6/ngdata.c' &&
X/* $Id: ngdata.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 "rcstuff.h"
X#include "trn.h"
X#include "cache.h"
SHAR_EOF
  : || echo 'restore of trn-3.6/ngdata.c failed'
fi
echo 'End of archive part 5'
echo 'File trn-3.6/ngdata.c is continued in part 6'
echo 6 > _sharseq.tmp
exit 0


