Newsgroups: comp.sources.unix
From: davison@borland.com (Wayne Davison)
Subject: v27i096: trn-3.3 - threaded newsreader based on RN, V3.3, Part03/12
References: <1.754431075.7231@gw.home.vix.com>
Sender: unix-sources-moderator@gw.home.vix.com
Approved: vixie@gw.home.vix.com

Submitted-By: davison@borland.com (Wayne Davison)
Posting-Number: Volume 27, Issue 96
Archive-Name: trn-3.3/part03

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 3 (of 12)."
# Contents:  Rnmail.SH addng.c artsrch.c edit_dist.c hash.c nghash.c
#   nntp.c nntpinit.c rt-ov.c term.h uudecode.c
# Wrapped by vixie@gw.home.vix.com on Sun Nov 21 01:14:02 1993
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Rnmail.SH' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Rnmail.SH'\"
else
echo shar: Extracting \"'Rnmail.SH'\" \(6522 characters\)
sed "s/^X//" >'Rnmail.SH' <<'END_OF_FILE'
Xcase $CONFIG in
X    '') . ./config.sh ;;
Xesac
Xecho "Extracting Rnmail (with variable substitutions)"
X$spitshell >Rnmail <<!GROK!THIS!
X$startsh
X# $Id: Rnmail.SH,v 3.0 1992/02/23 21:25:39 davison Trn $
X# 
X# This software is Copyright 1991 by Stan Barber. 
X#
X# Permission is hereby granted to copy, reproduce, redistribute or otherwise
X# use this software as long as: there is no monetary profit gained
X# specifically from the use or reproduction of this software, it is not
X# sold, rented, traded or otherwise marketed, and this copyright notice is
X# included prominently in any copy made. 
X#
X# The author make no claims as to the fitness or correctness of this software
X# for any use whatsoever, and it is provided as is. Any use of this software
X# is at the user's own risk. 
X#
X#
X# syntax: Rnmail -h headerfile [oldart]		or
X#         Rnmail destination-list 		or just
X#         Rnmail
X
Xexport PATH || (echo "OOPS, this isn't sh.  Desperation time.  I will feed myself to sh."; sh \$0; kill \$\$)
X
X# System dependencies
X
Xmailer="\${RNMAILER-${mailer-/bin/mail}}"
X# if you change this to something that does signatures, take out signature code
X
X# your organization name
Xorgname="$orgname"
X# what pager you use--if you have kernal paging use cat
Xpager="\${PAGER-$pager}"
X# how you derive full names, bsd, usg, or other
Xnametype="$nametype"
X# default editor
Xdefeditor="$defeditor"
X# where the non-publics are
Xcase $d_portable in
Xdefine)  rnlib=\`$filexp $privlib\` ;;
Xundef)   rnlib="$privlib" ;;
Xesac
X# how not to do a newline with echo
Xn="$n"
Xc="$c"
X
Xtest=${test-test}
Xsed=${sed-sed}
Xecho=${echo-echo}
Xcat=${cat-cat}
Xgrep=${grep-grep}
Xrm=${rm-rm}
X
X!GROK!THIS!
Xcase "$d_ignoreorg" in
Xdefine) $spitshell >>Rnmail <<'!NO!SUBS!'
Xorgname=${NEWSORG-$orgname}
X!NO!SUBS!
X	;;
X*)	$spitshell >>Rnmail <<'!NO!SUBS!'
Xorgname=${NEWSORG-${ORGANIZATION-$orgname}}
X!NO!SUBS!
X	;;
Xesac
X$spitshell >>Rnmail <<'!NO!SUBS!'
Xdotdir=${DOTDIR-${HOME-$LOGDIR}}
Xtmpart=$dotdir/.letter
Xspeller=$rnlib/Speller
Xmail_sig=${MAILSIGNATURE-$dotdir/.mail_sig}
X
Xheaderfile=""
Xcase $# in
X0) ;;
X*)  case $1 in
X    -h)
X	headerfile="$2"
X	case $# in
X	3) oldart=$3 ;;
X	esac
X	;;
X    esac
X    ;;
Xesac
X
Xcase $headerfile in
X'')
X    case $# in
X    0)
X	to=h
X	while $test "$to" = h ; do
X	    $echo ""
X	    $echo $n "To: $c"
X	    read to
X	    case $to in
X	    h)
X		$cat <<'EOH'
X
XType the net address of those people to whom you wish the message sent.
XAdditional recipients may be added on the Cc: line when you edit.
X
XSeparate multiple addresses with spaces.
X
XEOH
X		;;
X	    esac
X	done
X	;;
X    *)
X	to="$*"
X	;;
X    esac
X    to=`$echo "$to" | $sed 's/  */ /g'`
X
X    title=h
X    while $test "$title" = h ; do
X	$echo ""
X	$echo $n "Title/Subject: $c"
X	read title
X	case $title in
X	h)
X	    $cat <<'EOH'
X
XType the title for your message.  
XEOH
X	    ;;
X	esac
X    done
X
X# now build a file with a header for them to edit
X    
X    case $orgname in
X    /*) orgname=`$cat $orgname` ;;
X    esac
X
X    $sed -e '/^Reply-To: $/d' > $tmpart <<EOHeader
XTo: $to
XSubject: $title
XOrganization: $orgname
XReply-To: $REPLYTO
XCc:
XBcc:
X
XEOHeader
X
X    ;;
X*)
X    $cat < $headerfile  > $tmpart
X    ;;
Xesac
X
X
Xfile=h
Xwhile $test X$file = Xh ; do
X    $echo ""
X    $echo $n "Prepared file to include [none]: $c"
X    read file
X    case $file in
X    h)
X	$cat <<'EOH'
X
XIf you have already produced the body of your message, type the filename
Xfor it here.  If you just want to proceed directly to the editor, type a
XRETURN.  In any event, you will be allowed to edit as many times as you
Xwant before you send off the message.
XEOH
X	;;
X    '')
X	$echo "" >> $tmpart
X	state=edit
X	;;
X    *)
X	$cat $file >>$tmpart
X	state=ask
X	;;
X    esac
Xdone
X
X$echo ""
X
Xif $test -r $mail_sig; then
X    $echo "-- " >> $tmpart
X    $cat $mail_sig >> $tmpart
Xfi
X
Xwhile true ; do
X    case $state in
X    edit)
X	rescue="sleep 1; $cat $tmpart >>${HOME-$LOGDIR}/dead.letter ; $echo Message appended to ${HOME-$LOGDIR}/dead.letter ; exit"
X	trap "$rescue" 1
X	trap : 2
X	case "${VISUAL-${EDITOR-}}" in
X	'')
X	    tmp=h
X	    ;;
X	*)
X	    tmp=''
X	    ;;
X	esac
X	while $test "$tmp" = h ; do
X	    $echo $n "Editor [${VISUAL-${EDITOR-$defeditor}}]: $c"
X	    read tmp
X	    case $tmp in
X	    h)
X		$cat <<'EOH'
X
XType a return to get the default editor, or type the name of the editor you
Xprefer.  The default editor depends on the VISUAL and EDITOR environment
Xvariables.
X
XEOH
X		;;
X	    '')
X		;;
X	    *)
X		VISUAL=$tmp
X		export VISUAL
X		;;
X	    esac
X	done
X	${VISUAL-${EDITOR-$defeditor}} $tmpart $oldart
X	trap "$rescue" 2
X	state=ask
X	;;
X	
X    ask)
X	$echo ""
X	$echo $n "Check spelling, Send, Abort, Edit, or List? $c"
X	read ans
X
X	case $ans in
X	[aA]*)
X	    state=rescue
X	    ;;
X	[eE]*)
X	    set $ans
X	    case $# in
X	    2)  VISUAL="$2" ;;
X	    esac
X	    state=edit
X	    ;;
X	[lL]*)
X	    $pager $tmpart
X	    state=ask
X	    ;;
X	[cC]*|[sS][pP]*)
X	    $speller $tmpart
X	    state=ask
X	    ;;
X	[sS]*)
X	    state=send
X	    ;;
X	[hH]*)
X	    $cat <<'EOH'
X
XType c to check the message's spelling, s to send the message, a to abort
Xand append the message to dead.letter, e to edit the message again, or l
Xto list the message with your pager.
X
XTo invoke an alternate editor, type 'e editor'.
XEOH
X	esac
X	;;
X    
X    send)
X	if $test -f $dotdir/.signature; then
X	    $echo $n "Append .signature file? [y] $c"
X	    read ans
X	    case $ans in
X	    ''|y*)
X		$echo "-- " >> $tmpart
X		cat $dotdir/.signature >> $tmpart
X		;;
X	    esac
X	fi
X	case $mailer in
X	*sendmail)
X	    $mailer -t <$tmpart
X	    ;;
X# but recmail does not know about Bcc, alas
X	*recmail)
X	    $mailer <$tmpart
X	    ;;
X	*)
X	    set X `$sed <$tmpart -n -e '/^To:/{' -e 's/To: *//p' -e q -e '}'`
X	    shift
X	    set X "$@" `$sed <$tmpart -n -e '/^Cc:/{' -e 's/Cc: *//p' -e q -e '}'`
X	    shift
X	    set X "$@" `$sed <$tmpart -n -e '/^Bcc:/{' -e 's/Bcc: *//p' -e q -e '}'`
X	    shift
X	    $grep -v "^Bcc:"  <$tmpart | $mailer "$@"
X	    ;;
X	esac
X	case $? in
X	0)
X	    state=cleanup
X	    ;;
X	*)
X	    state=rescue
X	    ;;
X	esac
X	;;
X    rescue)
X	$cat $tmpart >> ${HOME-$LOGDIR}/dead.letter
X	$echo "Message appended to ${HOME-$LOGDIR}/dead.letter"
X	$echo "A copy may be temporarily found in $tmpart"
X	exit
X	;;
X    cleanup)
X	case "${MAILRECORD-none}" in
X	none)
X	    ;;
X	*)
X	    set X ${USER-${LOGNAME-`who am i`}} unknown
X	    shift
X	    $rnlib/mbox.saver $tmpart "." "." 0 0 Pnews $MAILRECORD "From $1 `LANG= date`"
X	    if $test $? -eq 0 ; then
X		$echo "Message appended to $MAILRECORD"
X	    else
X		$echo "Cannot append to $MAILRECORD"
X	    fi
X	    ;;
X	esac
X	exit
X	;;
X    esac
Xdone
X!NO!SUBS!
X$eunicefix Rnmail
Xchmod 755 Rnmail
END_OF_FILE
if test 6522 -ne `wc -c <'Rnmail.SH'`; then
    echo shar: \"'Rnmail.SH'\" unpacked with wrong size!
fi
chmod +x 'Rnmail.SH'
# end of 'Rnmail.SH'
fi
if test -f 'addng.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'addng.c'\"
else
echo shar: Extracting \"'addng.c'\" \(7375 characters\)
sed "s/^X//" >'addng.c' <<'END_OF_FILE'
X/* $Id: addng.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 "ngdata.h"
X#include "last.h"
X#include "util.h"
X#include "intrp.h"
X#include "only.h"
X#include "rcstuff.h"
X#include "nntp.h"
X#include "final.h"
X#include "INTERN.h"
X#include "addng.h"
X
Xvoid
Xaddng_init()
X{
X    ;
X}
X
X#ifdef FINDNEWNG
X/* generate a list of new newsgroups from active file */
X
Xbool
Xnewlist(munged,checkinlist)
Xbool_int munged;			/* are we scanning the whole file? */
Xbool_int checkinlist;
X{
X    char *tmpname;
X    register char *s, *status;
X    register NG_NUM ngnum;
X
X    tmpname = filexp(RNEWNAME);
X    tmpfp = fopen(tmpname,"w+");
X    if (tmpfp == Nullfp) {
X	printf(cantcreate,tmpname) FLUSH;
X	return FALSE;
X    }
X    UNLINK(tmpname);			/* be nice to the world */
X
X    while (fgets(buf,LBUFLEN,actfp) != Nullch) {
X	/* Check if they want to break out of the new newsgroups search */
X	if (int_count) {
X	    int_count = 0;
X	    fclose(tmpfp);
X	    return FALSE;
X	}
X	if (s = index(buf,' ')) {
X	    status=s;
X	    while (isdigit(*status) || isspace(*status)) status++;
X	    *s++ = '\0';
X	    if (strnEQ(buf,"to.",3) || *status == 'x' || *status == '=')
X	        /* since = groups are refiling to another group, just
X		   ignore their existence */
X		continue;
X#ifdef ACTIVE_TIMES
X	    if (inlist(buf) && ((ngnum = find_ng(buf)) == nextrcline
X				|| toread[ngnum] == TR_UNSUB)
X#else
X	    if (checkinlist ?
X		(inlist(buf) && ((ngnum = find_ng(buf)) == nextrcline
X				 || toread[ngnum] == TR_UNSUB))
X	      : (find_ng(buf) == nextrcline
X		 && birthof(buf,(ART_NUM)atol(s)) > lasttime)
X#endif
X	    ) {
X					/* if not in .newsrc and younger */
X					/* than the last time we checked */
X		fprintf(tmpfp,"%s\n",buf);
X					/* then remember said newsgroup */
X	    }
X#ifdef FASTNEW
X	    else {			/* not really a new group */
X		if (!munged) {		/* did we assume not munged? */
X		    fclose(tmpfp);	/* then go back, knowing that */
X		    return TRUE;	/* active file was indeed munged */
X		}
X	    }
X#endif
X	}
X#ifdef DEBUG
X	else
X	    printf("Bad active record: %s\n",buf) FLUSH;
X#endif
X    }
X
X    /* we have successfully generated the list */
X
X    fseek(tmpfp,0L,0);			/* rewind back to the beginning */
X    while (fgets(buf,LBUFLEN,tmpfp) != Nullch) {
X	buf[strlen(buf)-1] = '\0';
X	get_ng(buf,GNG_RELOC);		/* add newsgroup, maybe */
X    }
X    fclose(tmpfp);			/* be nice to ourselves */
X    return FALSE;			/* do not call us again */
X}
X
X#ifdef ACTIVE_TIMES
X#ifdef USE_NNTP
X
Xbool
Xfind_new_groups()
X{
X    char *tmpname;
X    register char *s;
X    time_t server_time;
X    NG_NUM oldnext = nextrcline;	/* remember # lines in newsrc */
X
X    tmpname = filexp(RNEWNAME);
X    tmpfp = fopen(tmpname,"w+");
X    if (tmpfp == Nullfp) {
X	printf(cantcreate,tmpname) FLUSH;
X	return FALSE;
X    }
X    UNLINK(tmpname);			/* be nice to the world */
X
X    server_time = nntp_time();
X    if (!nntp_newgroups(lastnewtime)) {
X	fclose(tmpfp);
X	printf("Can't get new groups from server:\n%s\n", ser_line);
X	return FALSE;
X    }
X
X    while (1) {
X	nntp_gets(ser_line, sizeof ser_line);
X#ifdef DEBUG
X	if (debug & DEB_NNTP)
X	    printf("<%s\n", ser_line) FLUSH;
X#endif
X	if (ser_line[0] == '.')
X	    break;
X	if ((s = index(ser_line, ' ')) != Nullch)
X	    *s = '\0';
X	fprintf(tmpfp,"%s\n",ser_line);
X    }
X
X    /* we have successfully generated the list */
X
X    if (ftell(tmpfp)) {
X	fputs("\nFinding new newsgroups:\n",stdout) FLUSH;
X
X	fseek(tmpfp,0L,0);		/* rewind back to the beginning */
X	while (fgets(buf,LBUFLEN,tmpfp) != Nullch) {
X	    buf[strlen(buf)-1] = '\0';
X	    get_ng(buf,0);		/* add newsgroup, maybe */
X	}
X	lastnewtime = server_time;	/* remember when we found new groups */
X    }					/* (ends up back in .rnlast) */
X    fclose(tmpfp);			/* be nice to ourselves */
X
X    return oldnext != nextrcline;
X}
X#else /* !USE_NNTP */
X
Xbool
Xfind_new_groups()
X{
X    register char *s;
X    time_t lastone;
X    NG_NUM oldnext = nextrcline;	/* remember # lines in newsrc */
X
X    fstat(fileno(actfp),&filestat);	/* find active file size */
X    lastactsiz = filestat.st_size;	/* just to save it in .rnlast */
X
X    stat(ACTIVE_TIMES,&filestat);	/* did active.times file grow? */
X    if (filestat.st_size == lastnewsize)
X	return FALSE;
X    lastnewsize = filestat.st_size;
X
X    fputs("\nChecking for new newsgroups...\n",stdout) FLUSH;
X
X    s = filexp(ACTIVE_TIMES);
X    tmpfp = fopen(s,"r");
X    if (tmpfp == Nullfp) {
X	printf(cantopen,s) FLUSH;
X	return FALSE;
X    }
X    lastone = time(Null(time_t*)) - 24L * 60 * 60 - 1;
X    while (fgets(buf,LBUFLEN,tmpfp) != Nullch) {
X	if ((s = index(buf, ' ')) != Nullch)
X	    if ((lastone = atol(s+1)) >= lastnewtime) {
X		char tmpbuf[LBUFLEN];
X		*s = '\0';
X		if (findact(tmpbuf, buf, s - buf, 0L) >= 0)
X		    get_ng(buf,0);	/* add newsgroup, maybe */
X	    }
X    }
X    fclose(tmpfp);
X    lastnewtime = lastone+1;		/* remember time of last new group */
X					/* (ends up back in .rnlast) */
X    return oldnext != nextrcline;
X}
X#endif /* !USE_NNTP */
X#else /* not ACTIVE_TIMES */
X
Xbool
Xfind_new_groups()
X{
X    long oldactsiz = lastactsiz;
X    NG_NUM oldnext = nextrcline;	/* remember # lines in newsrc */
X
X    fstat(fileno(actfp),&filestat);	/* did active file grow? */
X
X    if (filestat.st_size == lastactsiz)
X	return FALSE;
X    lastactsiz = filestat.st_size;	/* remember new size */
X
X#ifdef VERBOSE
X    IF(verbose)
X	fputs("\nChecking active file for new newsgroups...\n",stdout) FLUSH;
X    ELSE
X#endif
X#ifdef TERSE
X	fputs("\nNew newsgroups:\n",stdout) FLUSH;
X#endif
X
X#ifdef FASTNEW				/* bad soft ptrs -> edited active */
X    if (!writesoft && oldactsiz) {	/* maybe just do tail of file? */
X	fseek(actfp,oldactsiz-NL_SIZE,0);
X	fgets(buf,LBUFLEN,actfp);
X	if (*buf == '\n' && !newlist(FALSE,FALSE))
X	    goto bugout;
X    }
X#endif
X    fseek(actfp,0L,0);		/* rewind active file */
X    newlist(TRUE,FALSE);		/* sure hope they use hashing... */
Xbugout:
X    return oldnext != nextrcline;
X}
X
X/* return creation time of newsgroup */
X
Xtime_t
Xbirthof(ngnam,ngsize)
Xchar *ngnam;
XART_NUM ngsize;
X{
X#ifdef USE_NNTP		/* ngsize not used */
X    long tot;
X
X    if (!nntp_group(ngnam,-1))
X	return 0;	/* not a real group */
X    (void) sscanf(ser_line,"%*d%ld",&tot);
X    if (tot > 0)
X	return time(Null(time_t*));
X    return 0;
X
X#else /* !USE_NNTP */
X    char tst[128];
X
X    sprintf(tst, ngsize ? "%s/%s/1" : "%s/%s" ,spool,getngdir(ngnam));
X    if (stat(tst,&filestat) < 0)
X	return (ngsize ? 0L : time(Null(time_t*)));
X    /* not there, assume something good */
X    return filestat.st_mtime;
X
X#endif /* !USE_NNTP */
X}
X#endif /* ACTIVE_TIMES */
X
Xbool
Xscanactive()
X{
X    NG_NUM oldnext = nextrcline;	/* remember # lines in newsrc */
X
X    fseek(actfp,0L,0);
X    newlist(TRUE,TRUE);
X    if (nextrcline != oldnext) {	/* did we add any new groups? */
X	return TRUE;
X    }
X    return FALSE;
X}
X
X#endif
X
END_OF_FILE
if test 7375 -ne `wc -c <'addng.c'`; then
    echo shar: \"'addng.c'\" unpacked with wrong size!
fi
# end of 'addng.c'
fi
if test -f 'artsrch.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'artsrch.c'\"
else
echo shar: Extracting \"'artsrch.c'\" \(9284 characters\)
sed "s/^X//" >'artsrch.c' <<'END_OF_FILE'
X/* $Id: artsrch.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 "search.h"
X#include "term.h"
X#include "util.h"
X#include "intrp.h"
X#include "cache.h"
X#include "bits.h"
X#include "kfile.h"
X#include "head.h"
X#include "final.h"
X#include "nntp.h"
X#include "ng.h"
X#include "ngdata.h"
X#include "ngstuff.h"
X#include "artio.h"
X#include "rthread.h"
X#include "rt-select.h"
X#include "INTERN.h"
X#include "artsrch.h"
X
Xvoid
Xartsrch_init()
X{
X#ifdef ARTSEARCH
X    init_compex(&sub_compex);
X    init_compex(&art_compex);
X#endif
X}
X
X/* search for an article containing some pattern */
X
X#ifdef ARTSEARCH
Xint
Xart_search(patbuf,patbufsiz,get_cmd)
Xchar *patbuf;				/* if patbuf != buf, get_cmd must */
Xint patbufsiz;
Xint get_cmd;				/*   be set to FALSE!!! */
X{
X    char *pattern;			/* unparsed pattern */
X    register char cmdchr = *patbuf;	/* what kind of search? */
X    register char *s;
X    bool backward = cmdchr == '?' || cmdchr == Ctl('p');
X					/* direction of search */
X    COMPEX *compex;			/* which compiled expression */
X    char *cmdlst = Nullch;		/* list of commands to do */
X    int normal_return = SRCH_NOTFOUND;	/* assume no commands */
X    bool saltaway = FALSE;		/* store in KILL file? */
X    char howmuch;		/* search scope: subj/from/Hdr/head/art */
X    char *srchhdr;		/* header to search if Hdr scope */
X    bool doread;			/* search read articles? */
X    bool foldcase = TRUE;		/* fold upper and lower case? */
X    ART_NUM srchfirst;
X
X    int_count = 0;
X    if (cmdchr == '/' || cmdchr == '?') {	/* normal search? */
X	if (get_cmd && buf == patbuf)
X	    if (!finish_command(FALSE))	/* get rest of command */
X		return SRCH_ABORT;
X	compex = &art_compex;
X	if (patbuf[1]) {
X	    howmuch = ARTSCOPE_SUBJECT;
X	    srchhdr = Nullch;
X	    doread = FALSE;
X	}
X	else {
X	    howmuch = art_howmuch;
X	    srchhdr = art_srchhdr;
X	    doread = art_doread;
X	}
X	s = cpytill(buf,patbuf+1,cmdchr);/* ok to cpy buf+1 to buf */
X	pattern = buf;
X	if (*pattern) {
X	    if (*lastpat)
X		free(lastpat);
X	    lastpat = savestr(pattern);
X	}
X	if (*s) {			/* modifiers or commands? */
X	    for (s++; *s && index("KarchHf",*s); s++) {
X		if (*s == 'f')		/* scan from line */
X		    howmuch = ARTSCOPE_FROM;
X		else if (*s == 'H') {	/* scan a specific header */
X		    howmuch = ARTSCOPE_ONEHDR;
X		    srchhdr = s + 1;
X		    if (!(s = index(srchhdr, ':'))) {
X			s = buf + strlen(buf);
X			*s++ = ':';
X			*s = '\0';
X		    }
X		    else
X			s++;
X		    srchhdr = savestr(srchhdr);
X		    break;
X		} else if (*s == 'h')	/* scan header */
X		    howmuch = ARTSCOPE_HEAD;
X		else if (*s == 'a')	/* scan article */
X		    howmuch = ARTSCOPE_ARTICLE;
X		else if (*s == 'r')	/* scan read articles */
X		    doread = TRUE;
X		else if (*s == 'K')	/* put into KILL file */
X		    saltaway = TRUE;
X		else if (*s == 'c')	/* make search case sensitive */
X		    foldcase = FALSE;
X	    }
X	}
X	while (isspace(*s) || *s == ':')
X	    s++;
X	if (*s) {
X	    if (*s == 'm' || *s == 'M')
X		doread = TRUE;
X	    if (*s == 'k')		/* grandfather clause */
X		*s = 'j';
X	    cmdlst = savestr(s);
X	    normal_return = SRCH_DONE;
X	}
X	art_howmuch = howmuch;
X	if (art_srchhdr != srchhdr) {
X	    if (art_srchhdr)
X		free(art_srchhdr);
X	    art_srchhdr = srchhdr;
X	}
X	art_doread = doread;
X	if (srchahead)
X	    srchahead = -1;
X    }
X    else {
X	register char *h;
X
X	howmuch = ARTSCOPE_SUBJECT;	/* just search subjects */
X	doread = (cmdchr == Ctl('p'));
X	if (cmdchr == Ctl('n'))
X	    normal_return = SRCH_SUBJDONE;
X	compex = &sub_compex;
X	pattern = patbuf+1;
X	strcpy(pattern,": *");
X	h = pattern + strlen(pattern);
X	interp(h,patbufsiz - (h-patbuf),"%\\s");  /* fetch current subject */
X	if (cmdchr == 'k' || cmdchr == 'K' || cmdchr == ','
X	 || cmdchr == '+' || cmdchr == '.') {
X	    if (cmdchr != 'k')
X		saltaway = TRUE;
X	    normal_return = SRCH_DONE;
X	    if (cmdchr == '+')
X		cmdlst = savestr("++");
X	    else if (cmdchr == '.')
X		cmdlst = savestr(".");
X	    else {
X		if (cmdchr == ',')
X		    cmdlst = savestr(",");
X		mark_as_read();		/* this article has this subject */
X	    }
X	    if (!*h) {
X#ifdef VERBOSE
X		IF(verbose)
X		    fputs("\nCannot process a null subject.\n",stdout) FLUSH;
X		ELSE
X#endif
X#ifdef TERSE
X		    fputs("\nNull subject.\n",stdout) FLUSH;
X#endif
X		return SRCH_ABORT;
X	    }
X#ifdef VERBOSE
X	    else if (verbose)
X		if (cmdchr != '+' && cmdchr != '.')
X		    printf("\nMarking subject \"%s\" as read.\n",h) FLUSH;
X		else
X		    printf("\nSelecting subject \"%s\".\n",h) FLUSH;
X#endif
X	}
X	else if (!srchahead)
X	    srchahead = -1;
X
X	{			/* compensate for notesfiles */
X	    register int i;
X	    for (i = 24; *h && i--; h++)
X		if (*h == '\\')
X		    h++;
X	    *h = '\0';
X	}
X#ifdef DEBUG
X	if (debug) {
X	    printf("\npattern = %s\n",pattern) FLUSH;
X	}
X#endif
X    }
X    if ((s = compile(compex,pattern,TRUE,foldcase)) != Nullch) {
X					/* compile regular expression */
X	printf("\n%s\n",s) FLUSH;
X	return SRCH_ABORT;
X    }
X#ifdef KILLFILES
X    if (saltaway) {
X	char saltbuf[LBUFLEN];
X
X	s = saltbuf;
X	sprintf(s,"/%s/",pattern);
X	s += strlen(s);
X	if (doread)
X	    *s++ = 'r';
X	if (howmuch != ARTSCOPE_SUBJECT) {
X	    *s++ = scopestr[howmuch];
X	    if (howmuch == ARTSCOPE_ONEHDR) {
X		safecpy(s,srchhdr,LBUFLEN-(s-saltbuf));
X		s = index(s,':');
X		if (!s)
X		    s = saltbuf+LBUFLEN-2;
X	    }
X	}
X	*s++ = ':';
X	if (!cmdlst)
X	    cmdlst = savestr("j");
X	safecpy(s,cmdlst,LBUFLEN-(s-saltbuf));
X	kf_append(saltbuf);
X    }
X#endif
X    if (cmdlst && index(cmdlst,'='))
X	normal_return = SRCH_ERROR;	/* listing subjects is an error? */
X    if (get_cmd) {
X	fputs("\nSearching...\n",stdout) FLUSH;
X					/* give them something to read */
X    }
X    if (mode == 't') {
X	if (!cmdlst)
X	    if (sel_mode == SM_ARTICLE)/* set the selector's default command */
X		cmdlst = savestr("+");
X	    else
X		cmdlst = savestr("++");
X	if (sel_rereading)
X	    doread = TRUE;
X	normal_return = SRCH_DONE;
X    }
X    srchfirst = (doread? absfirst :
X	(mode == 'k' && (howmuch > ARTSCOPE_FROM || lastart - last_cached > 25)
X	 ? killfirst : firstart));
X    if (backward) {
X	if (cmdlst && art <= lastart)
X	    art++;			/* include current article */
X    }
X    else {
X	if (art > lastart)
X	    art = srchfirst-1;
X	else if (cmdlst && art >= absfirst)
X	    art--;			/* include current article */
X    }
X    if (srchahead > 0) {
X	if (!backward)
X	    art = srchahead - 1;
X	srchahead = -1;
X    }
X    assert(!cmdlst || *cmdlst);
X    perform_cnt = 0;
X    for (;;) {
X	/* check if we're out of articles */
X	if (backward? (--art < srchfirst) : (++art > lastart)) {
X	    if (cmdlst)
X		free(cmdlst);
X	    return normal_return;
X	}
X	if (int_count) {
X	    int_count = 0;
X	    if (cmdlst)
X		free(cmdlst);
X	    return SRCH_INTR;
X	}
X	artp = article_ptr(art);
X	if (doread || !(artp->flags & AF_READ)) {
X	    if (wanted(compex,art,howmuch)) {
X				    /* does the shoe fit? */
X		if (cmdlst) {
X		    if (perform(cmdlst,TRUE)) {
X			if (cmdlst)
X			    free(cmdlst);
X			return SRCH_INTR;
X		    }
X		}
X		else {
X		    if (cmdlst)
X			free(cmdlst);
X		    return SRCH_FOUND;
X		}
X	    }
X	    else if (!cmdlst && ! (art%50)) {
X		printf("...%ld",(long)art);
X		fflush(stdout);
X	    }
X	}
X    }
X}
X
X/* determine if article fits pattern */
X/* returns TRUE if it exists and fits pattern, FALSE otherwise */
X
Xbool
Xwanted(compex, artnum, scope)
XCOMPEX *compex;
XART_NUM artnum;
Xchar_int scope;
X{
X    ARTICLE *ap = find_article(artnum);
X
X    if (!ap || (ap->flags & AF_MISSING))
X	return FALSE;
X
X    switch (scope) {
X    case ARTSCOPE_SUBJECT:
X	strcpy(buf,"Subject: ");
X	strncpy(buf+9,fetchsubj(artnum,FALSE),256);
X#ifdef DEBUG
X	if (debug & DEB_SEARCH_AHEAD)
X	    printf("%s\n",buf) FLUSH;
X#endif
X	break;
X    case ARTSCOPE_FROM:
X	strcpy(buf, "From: ");
X	strncpy(buf+6,fetchfrom(artnum,FALSE),256);
X	break;
X    case ARTSCOPE_ONEHDR:
X    {
X	int header_num;
X	char *s;
X	assert(art_srchhdr != Nullch);
X	s = index(art_srchhdr,':');
X	header_num = set_line_type(art_srchhdr, s);
X	if (header_num == SOME_LINE)
X	    return FALSE;  /* FIX ME */
X	untrim_cache = TRUE;
X	strcpy(buf, art_srchhdr);
X	sprintf(buf + (s-art_srchhdr), ": %s",
X		prefetchlines(artnum,header_num,FALSE));
X	untrim_cache = FALSE;
X	break;
X    }
X    default:
X	if (!parseheader(artnum))
X	    return FALSE;
X	/* see if it's in the header */
X	if (execute(compex,headbuf) != Nullch)	/* does it match? */
X	    return TRUE;			/* say, "Eureka!" */
X	if (scope < ARTSCOPE_ARTICLE)
X	    return FALSE;
X	if (!artopen(artnum))		/* ensure we have the body */
X	    return FALSE;
X	/* loop through each line of the article */
X	while (fgets(buf,LBUFLEN,artfp) != Nullch) {
X	    if (execute(compex,buf) != Nullch)	/* does it match? */
X		return TRUE;			/* say, "Eureka!" */
X	}
X	return FALSE;			/* out of article, so no match */
X    }
X    return execute(compex,buf) != Nullch;
X}
X#endif
X
END_OF_FILE
if test 9284 -ne `wc -c <'artsrch.c'`; then
    echo shar: \"'artsrch.c'\" unpacked with wrong size!
fi
# end of 'artsrch.c'
fi
if test -f 'edit_dist.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'edit_dist.c'\"
else
echo shar: Extracting \"'edit_dist.c'\" \(7885 characters\)
sed "s/^X//" >'edit_dist.c' <<'END_OF_FILE'
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"		/* Declare MEM_SIZE */
X#include "util.h"		/* Declare safemalloc() */
X
X#ifdef EDIT_DISTANCE
X
X/* edit_dist -- returns the minimum edit distance between two strings
X
X	Program by:  Mark Maimone   CMU Computer Science   13 Nov 89
X	Last Modified:  28 Jan 90
X
X   If the input strings have length n and m, the algorithm runs in time
X   O(nm) and space O(min(m,n)).
X
XHISTORY
X   13 Nov 89 (mwm) Created edit_dist() and set_costs().
X
X   28 Jan 90 (mwm) Added view_costs().  Should verify that THRESHOLD
X   computations will work even when THRESHOLD is not a multiple of
X   sizeof(int).
X
X   17 May 93 (mwm) Improved performance when used with trn's newsgroup
X   processing; assume all costs are 1, and you can terminate when a
X   threshold is exceeded.
X*/
X
X
X#define	TRN_SPEEDUP		/* Use a less-general version of the
X				   routine, one that's better for trn.
X				   All change costs are 1, and it's okay
X				   to terminate if the edit distance is
X				   known to exceed MIN_DIST */
X
X#define THRESHOLD 4000		/* worry about allocating more memory only
X				   when this # of bytes is exceeded */
X#define STRLENTHRESHOLD ((int) ((THRESHOLD / sizeof (int) - 3) / 2))
X
X#define SAFE_ASSIGN(x,y) (((x) != NULL) ? (*(x) = (y)) : (y))
X
X#define swap_int(x,y)  (_iswap = (x), (x) = (y), (y) = _iswap)
X#define swap_char(x,y) (_cswap = (x), (x) = (y), (y) = _cswap)
X#define min3(x,y,z) (_mx = (x), _my = (y), _mz = (z), (_mx < _my ? (_mx < _mz ? _mx : _mz) : (_mz < _my) ? _mz : _my))
X#define min2(x,y) (_mx = (x), _my = (y), (_mx < _my ? _mx : _my))
X
X
Xstatic int insert_cost = 1;
Xstatic int delete_cost = 1;
Xstatic int change_cost = 1;
Xstatic int swap_cost   = 1;
X
Xstatic int _iswap;			/* swap_int temp variable */
Xstatic char *_cswap;			/* swap_char temp variable */
Xstatic int _mx, _my, _mz;		/* min2, min3 temp variables */
X
X
X
Xvoid
Xview_costs(ins, del, ch, swap)
Xint *ins, *del, *ch, *swap;
X{
X    SAFE_ASSIGN(ins, insert_cost);
X    SAFE_ASSIGN(del, delete_cost);
X    SAFE_ASSIGN(ch, change_cost);
X    SAFE_ASSIGN(swap, swap_cost);
X} /* view_costs */
X
Xvoid
Xset_costs(ins, del, ch, swap)
Xint ins, del, ch, swap;
X{
X    insert_cost = ins;
X    delete_cost = del;
X    change_cost = ch;
X    swap_cost   = swap;
X} /* set_costs */
X
X
X/* edit_distn -- returns the edit distance between two strings, or -1 on
X   failure */
X
Xint
Xedit_distn(from, from_len, to, to_len)
Xchar *from, *to;
Xregister int from_len, to_len;
X{
X#ifndef TRN_SPEEDUP
X    register int ins, del, ch;	  	/* local copies of edit costs */
X#endif
X    register int row, col, index;	/* dynamic programming counters */
X    register int radix;			/* radix for modular indexing */
X#ifdef TRN_SPEEDUP
X    register int low;
X#endif
X    int *buffer;			/* pointer to storage for one row
X					   of the d.p. array */
X    static int store[THRESHOLD / sizeof (int)];
X					/* a small amount of static
X					   storage, to be used when the
X					   input strings are small enough */
X
X/* Handle trivial cases when one string is empty */
X
X    if (from == NULL || !from_len)
X	if (to == NULL || !to_len)
X	    return 0;
X	else
X	    return to_len * insert_cost;
X    else if (to == NULL || !to_len)
X	return from_len * delete_cost;
X
X/* Initialize registers */
X
X    radix = 2 * from_len + 3;
X#ifdef TRN_SPEEDUP
X#define ins 1
X#define del 1
X#define ch 1
X#define swap_cost 1
X#else
X    ins  = insert_cost;
X    del  = delete_cost;
X    ch   = change_cost;
X#endif
X
X/* Make   from   short enough to fit in the static storage, if it's at all
X   possible */
X
X    if (from_len > to_len && from_len > STRLENTHRESHOLD) {
X	swap_int(from_len, to_len);
X	swap_char(from, to);
X#ifndef TRN_SPEEDUP
X	swap_int(ins, del);
X#endif
X    } /* if from_len > to_len */
X
X/* Allocate the array storage (from the heap if necessary) */
X
X    if (from_len <= STRLENTHRESHOLD)
X	buffer = store;
X    else
X	buffer = (int *) safemalloc((MEM_SIZE) radix * sizeof (int));
X
X/* Here's where the fun begins.  We will find the minimum edit distance
X   using dynamic programming.  We only need to store two rows of the matrix
X   at a time, since we always progress down the matrix.  For example,
X   given the strings "one" and "two", and insert, delete and change costs
X   equal to 1:
X
X	   _  o  n  e
X	_  0  1  2  3
X	t  1  1  2  3
X	w  2  2  2  3
X	o  3  2  3  3
X
X   The dynamic programming recursion is defined as follows:
X
X	ar(x,0) := x * insert_cost
X	ar(0,y) := y * delete_cost
X	ar(x,y) := min(a(x - 1, y - 1) + (from[x] == to[y] ? 0 : change),
X		       a(x - 1, y) + insert_cost,
X		       a(x, y - 1) + delete_cost,
X		       a(x - 2, y - 2) + (from[x] == to[y-1] &&
X					  from[x-1] == to[y] ? swap_cost :
X					  infinity))
X
X   Since this only looks at most two rows and three columns back, we need
X   only store the values for the two preceeding rows.  In this
X   implementation, we do not explicitly store the zero column, so only 2 *
X   from_len + 2   words are needed.  However, in the implementation of the
X   swap_cost   check, the current matrix value is used as a buffer; we
X   can't overwrite the earlier value until the   swap_cost   check has
X   been performed.  So we use   2 * from_len + 3   elements in the buffer.
X*/
X
X#define ar(x,y,index) (((x) == 0) ? (y) * del : (((y) == 0) ? (x) * ins : \
X	buffer[mod(index)]))
X#define NW(x,y)	  ar(x, y, index + from_len + 2)
X#define N(x,y)	  ar(x, y, index + from_len + 3)
X#define W(x,y)	  ar(x, y, index + radix - 1)
X#define NNWW(x,y) ar(x, y, index + 1)
X#define mod(x) ((x) % radix)
X
X    index = 0;
X
X#ifdef DEBUG_EDITDIST
X    printf("      ");
X    for (col = 0; col < from_len; col++)
X	printf(" %c ", from[col]);
X    printf("\n   ");
X
X    for (col = 0; col <= from_len; col++)
X	printf("%2d ", col * del);
X#endif
X
X/* Row 0 is handled implicitly; its value at a given column is   col*del.
X   The loop below computes the values for Row 1.  At this point we know the
X   strings are nonempty.  We also don't need to consider swap costs in row
X   1.
X
X   COMMENT:  the indicies   row and col   below point into the STRING, so
X   the corresponding MATRIX indicies are   row+1 and col+1.
X*/
X
X    buffer[index++] = min2(ins + del, (from[0] == to[0] ? 0 : ch));
X#ifdef TRN_SPEEDUP
X    low = buffer[mod(index + radix - 1)];
X#endif
X
X#ifdef DEBUG_EDITDIST
X    printf("\n %c %2d %2d ", to[0], ins, buffer[index - 1]);
X#endif
X
X    for (col = 1; col < from_len; col++) {
X	buffer[index] = min3(
X		col * del + ((from[col] == to[0]) ? 0 : ch),
X		(col + 1) * del + ins,
X		buffer[index - 1] + del);
X#ifdef TRN_SPEEDUP
X	if (buffer[index] < low)
X	    low = buffer[index];
X#endif
X	index++;
X
X#ifdef DEBUG_EDITDIST
X	printf("%2d ", buffer[index - 1]);
X#endif
X
X    } /* for col = 1 */
X
X#ifdef DEBUG_EDITDIST
X    printf("\n %c %2d ", to[1], 2 * ins);
X#endif
X
X/* Now handle the rest of the matrix */
X
X    for (row = 1; row < to_len; row++) {
X	for (col = 0; col < from_len; col++) {
X	    buffer[index] = min3(
X		    NW(row, col) + ((from[col] == to[row]) ? 0 : ch),
X		    N(row, col + 1) + ins,
X		    W(row + 1, col) + del);
X	    if (from[col] == to[row - 1] && col > 0 &&
X		    from[col - 1] == to[row])		    
X		buffer[index] = min2(buffer[index],
X			NNWW(row - 1, col - 1) + swap_cost);
X
X#ifdef DEBUG_EDITDIST
X	    printf("%2d ", buffer[index]);
X#endif
X#ifdef TRN_SPEEDUP
X	    if (buffer[index] < low || col == 0)
X		low = buffer[index];
X#endif
X
X	    index = mod(index + 1);
X	} /* for col = 1 */
X#ifdef DEBUG_EDITDIST
X	if (row < to_len - 1)
X	    printf("\n %c %2d ", to[row+1], (row + 2) * ins);
X	else
X	    printf("\n");
X#endif
X#ifdef TRN_SPEEDUP
X	if (low > MIN_DIST)
X	    break;
X#endif
X    } /* for row = 1 */
X
X    row = buffer[mod(index + radix - 1)];
X    if (buffer != store)
X	free((char *) buffer);
X    return row;
X} /* edit_distn */
X
X#endif /* EDIT_DISTANCE */
END_OF_FILE
if test 7885 -ne `wc -c <'edit_dist.c'`; then
    echo shar: \"'edit_dist.c'\" unpacked with wrong size!
fi
# end of 'edit_dist.c'
fi
if test -f 'hash.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'hash.c'\"
else
echo shar: Extracting \"'hash.c'\" \(7223 characters\)
sed "s/^X//" >'hash.c' <<'END_OF_FILE'
X/* $Id: hash.c,v 3.0 1992/12/14 00:14:13 davison Trn $
X*/
X/* This file is an altered version of a set of hash routines by
X** Geoffrey Collyer.  See the end of the file for his copyright.
X*/
X
X#include "EXTERN.h"
X#include "common.h"
X#include "util.h"
X#include "final.h"
X#include "INTERN.h"
X#include "hash.h"
X
X/* tunable parameters */
X#define RETAIN 600		/* retain & recycle this many HASHENTs */
X
Xstatic HASHENT *hereuse = NULL;
Xstatic int reusables = 0;
X
Xstatic HASHENT **hashfind _((HASHTABLE*,char*,int));
Xstatic unsigned hash _((char*,int));
Xstatic int default_cmp _((char*,int,HASHDATUM));
Xstatic HASHENT *healloc _((void));
Xstatic void hefree _((HASHENT*));
X
XHASHTABLE *
Xhashcreate(size, cmpfunc)
Xunsigned size;			/* a crude guide to size */
Xint (*cmpfunc)();
X{
X    register HASHTABLE *tbl;
X    /* allocate HASHTABLE and (HASHENT*) array together to reduce the
X    ** number of malloc calls. */
X    register struct alignalloc {
X	HASHTABLE ht;
X	HASHENT *hepa[1];	/* longer than it looks */
X    } *aap;
X
X    aap = (struct alignalloc*)
X	safemalloc(sizeof *aap + (size-1)*sizeof (HASHENT*));
X    bzero((char*)aap, sizeof *aap + (size-1)*sizeof (HASHENT*));
X    tbl = &aap->ht;
X    tbl->ht_size = (size == 0? 1: size);	/* size of 0 is nonsense */
X    tbl->ht_magic = HASHMAG;
X    tbl->ht_cmp = (cmpfunc == NULL? default_cmp: cmpfunc);
X    tbl->ht_addr = aap->hepa;
X    return tbl;
X}
X
X/* Free all the memory associated with tbl, erase the pointers to it, and
X** invalidate tbl to prevent further use via other pointers to it.
X*/
Xvoid
Xhashdestroy(tbl)
Xregister HASHTABLE *tbl;
X{
X    register unsigned idx;
X    register HASHENT *hp, *next;
X    register HASHENT **hepp;
X    register int tblsize;
X
X    if (tbl == NULL || BADTBL(tbl))
X	return;
X    tblsize = tbl->ht_size;
X    hepp = tbl->ht_addr;
X    for (idx = 0; idx < tblsize; idx++) {
X	for (hp = hepp[idx]; hp != NULL; hp = next) {
X	    next = hp->he_next;
X	    hp->he_next = NULL;
X	    hefree(hp);
X	}
X	hepp[idx] = NULL;
X    }
X    tbl->ht_magic = 0;			/* de-certify this table */
X    tbl->ht_addr = NULL;
X    free((char*)tbl);
X}
X
Xvoid
Xhashstore(tbl, key, keylen, data)
Xregister HASHTABLE *tbl;
Xchar *key;
Xint keylen;
XHASHDATUM data;
X{
X    register HASHENT *hp;
X    register HASHENT **nextp;
X
X    nextp = hashfind(tbl, key, keylen);
X    hp = *nextp;
X    if (hp == NULL) {			/* absent; allocate an entry */
X	hp = healloc();
X	hp->he_next = NULL;
X	hp->he_keylen = keylen;
X	*nextp = hp;			/* append to hash chain */
X    }
X    hp->he_data = data;		/* supersede any old data for this key */
X}
X
Xvoid
Xhashdelete(tbl, key, keylen)
Xregister HASHTABLE *tbl;
Xchar *key;
Xint keylen;
X{
X    register HASHENT *hp;
X    register HASHENT **nextp;
X
X    nextp = hashfind(tbl, key, keylen);
X    hp = *nextp;
X    if (hp == NULL)			/* absent */
X	return;
X    *nextp = hp->he_next;		/* skip this entry */
X    hp->he_next = NULL;
X    hp->he_data.dat_ptr = NULL;
X    hefree(hp);
X}
X
XHASHENT **slast_nextp;
Xint slast_keylen;
X
XHASHDATUM				/* data corresponding to key */
Xhashfetch(tbl, key, keylen)
Xregister HASHTABLE *tbl;
Xchar *key;
Xint keylen;
X{
X    register HASHENT *hp;
X    register HASHENT **nextp;
X    static HASHDATUM errdatum = { NULL, 0 };
X
X    nextp = hashfind(tbl, key, keylen);
X    slast_nextp = nextp;
X    slast_keylen = keylen;
X    hp = *nextp;
X    if (hp == NULL)			/* absent */
X	return errdatum;
X    else
X	return hp->he_data;
X}
X
Xvoid
Xhashstorelast(data)
XHASHDATUM data;
X{
X    register HASHENT *hp;
X
X    hp = *slast_nextp;
X    if (hp == NULL) {			/* absent; allocate an entry */
X	hp = healloc();
X	hp->he_next = NULL;
X	hp->he_keylen = slast_keylen;
X	*slast_nextp = hp;		/* append to hash chain */
X    }
X    hp->he_data = data;		/* supersede any old data for this key */
X}
X
X/* Visit each entry by calling nodefunc at each, with key, data and extra as
X** arguments.
X*/
Xvoid
Xhashwalk(tbl, nodefunc, extra)
XHASHTABLE *tbl;
Xregister void (*nodefunc)();
Xregister int extra;
X{
X    register unsigned idx;
X    register HASHENT *hp;
X    register HASHENT **hepp;
X    register int tblsize;
X
X    if (BADTBL(tbl))
X	return;
X    hepp = tbl->ht_addr;
X    tblsize = tbl->ht_size;
X    for (idx = 0; idx < tblsize; idx++)
X	for (hp = hepp[idx]; hp != NULL; hp = hp->he_next)
X	    (*nodefunc)(&hp->he_data, extra);
X}
X
X/* The returned value is the address of the pointer that refers to the
X** found object.  Said pointer may be NULL if the object was not found;
X** if so, this pointer should be updated with the address of the object
X** to be inserted, if insertion is desired.
X*/
Xstatic HASHENT **
Xhashfind(tbl, key, keylen)
Xregister HASHTABLE *tbl;
Xchar *key;
Xregister int keylen;
X{
X    register HASHENT *hp, *prevhp = NULL;
X    register HASHENT **hepp;
X    register unsigned size; 
X
X    if (BADTBL(tbl))
X	fatal_error("Hash table is invalid.");
X    size = tbl->ht_size;
X    hepp = &tbl->ht_addr[hash(key,keylen) % size];
X    for (hp = *hepp; hp != NULL; prevhp = hp, hp = hp->he_next) {
X	if (hp->he_keylen == keylen && !(*tbl->ht_cmp)(key, keylen, hp->he_data))
X	    break;
X    }
X    /* assert: *(returned value) == hp */
X    return (prevhp == NULL? hepp: &prevhp->he_next);
X}
X
Xstatic unsigned				/* not yet taken modulus table size */
Xhash(key, keylen)
Xregister char *key;
Xregister int keylen;
X{
X    register unsigned hash = 0;
X
X    while (keylen--)
X	hash += *key++;
X    return hash;
X}
X
Xstatic int
Xdefault_cmp(key, keylen, data)
Xchar *key;
Xint keylen;
XHASHDATUM data;
X{
X    /* We already know that the lengths are equal, just compare the strings */
X    return bcmp(key, data.dat_ptr, keylen);
X}
X
Xstatic HASHENT *
Xhealloc()				/* allocate a hash entry */
X{
X    register HASHENT *hp;
X
X    if (hereuse == NULL)
X	return (HASHENT*)safemalloc(sizeof (HASHENT));
X    /* pull the first reusable one off the pile */
X    hp = hereuse;
X    hereuse = hereuse->he_next;
X    hp->he_next = NULL;			/* prevent accidents */
X    reusables--;
X    return hp;
X}
X
Xstatic void
Xhefree(hp)				/* free a hash entry */
Xregister HASHENT *hp;
X{
X    if (reusables >= RETAIN)		/* compost heap is full? */
X	free((char*)hp);		/* yup, just pitch this one */
X    else {				/* no, just stash for reuse */
X	++reusables;
X	hp->he_next = hereuse;
X	hereuse = hp;
X    }
X}
X
X/*
X * Copyright (c) 1992 Geoffrey Collyer
X * All rights reserved.
X * Written by Geoffrey Collyer.
X *
X * This software is not subject to any license of the American Telephone
X * and Telegraph Company, the Regents of the University of California, or
X * the Free Software Foundation.
X *
X * Permission is granted to anyone to use this software for any purpose on
X * any computer system, and to alter it and redistribute it freely, subject
X * to the following restrictions:
X *
X * 1. The author is not responsible for the consequences of use of this
X *    software, no matter how awful, even if they arise from flaws in it.
X *
X * 2. The origin of this software must not be misrepresented, either by
X *    explicit claim or by omission.  Since few users ever read sources,
X *    credits must appear in the documentation.
X *
X * 3. Altered versions must be plainly marked as such, and must not be
X *    misrepresented as being the original software.  Since few users
X *    ever read sources, credits must appear in the documentation.
X *
X * 4. This notice may not be removed or altered.
X */
END_OF_FILE
if test 7223 -ne `wc -c <'hash.c'`; then
    echo shar: \"'hash.c'\" unpacked with wrong size!
fi
# end of 'hash.c'
fi
if test -f 'nghash.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'nghash.c'\"
else
echo shar: Extracting \"'nghash.c'\" \(9643 characters\)
sed "s/^X//" >'nghash.c' <<'END_OF_FILE'
X/* $Id: nghash.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 "ndir.h"
X#include "rcstuff.h"
X#include "trn.h"
X#include "intrp.h"
X#include "final.h"
X#include "rcln.h"
X#include "util.h"
X#include "nntp.h"
X#include "ngdata.h"
X#include "hash.h"
X#include "term.h"
X
Xstatic HASHTABLE *acthash;
Xstatic char *actfile;
X
Xvoid
Xngdatahash_init()
X{
X    register long offset;
X    char *cp = filexp(ACTIVE);
X    struct stat actstat;
X
X#ifdef USE_NNTP
X    nntp_command("LIST");	/* tell server we want the active file */
X    if (nntp_check(TRUE) != NNTP_CLASS_OK) { /* and then see if that's ok */
X	printf("Can't get active file from server: \n%s\n", ser_line);
X	finalize(1);
X    }
X    time(&lastactfetch);
X
X    strcpy(active_name, cp);
X    actfp = fopen(active_name, "w+");	/* and get ready */
X    if (actfp == Nullfp) {
X	printf(cantopen,active_name) FLUSH;
X	finalize(1);
X    }
X
X    while (1) {
X	nntp_gets(ser_line, sizeof ser_line);
X	if (ser_line[0] == '.')		/* while there's another line */
X	    break;			/* get it and write it to */
X	fputs(ser_line, actfp);
X	putc('\n', actfp);
X    }
X
X    if (ferror(actfp)) {
X	printf("Error writing to active file %s.\n", active_name) FLUSH;
X	finalize(1);
X    }
X    fseek(actfp,0L,0);			/* rewind file */
X
X#else /* !USE_NNTP */
X    actfp = fopen(cp, "r");
X    if (actfp == Nullfp) {
X	printf(cantopen, cp) FLUSH;
X	finalize(1);
X    }
X#endif
X
X    /* rn was being a slug about rereading the active file over and
X     * over; try using a really big buffer to keep it in core. */
X    (void) fstat(fileno(actfp), &actstat);
X    actfile = safemalloc(actstat.st_size + 1);
X
X    /*
X     * NOTE: this won't work on machines with ints too small to hold
X     * the size of the active file.
X     */
X    if (fread(actfile, 1, (int)actstat.st_size, actfp) != actstat.st_size) {
X	fprintf(stderr, "active file not read\n");
X	actfile[0] = '\0';
X    }
X    actfile[actstat.st_size] = '\0';
X
X    acthash = hashcreate((int)(actstat.st_size/40), (int (*)())NULL);
X
X    /* count entries */
X    for (activeitems=0, offset=0; offset < actstat.st_size; activeitems++) {
X	register char *p;
X	HASHDATUM data;
X
X	data.dat_ptr = actfile + offset;
X	if ((p = index(data.dat_ptr, '\n')) == NULL)
X	    data.dat_len = actstat.st_size - offset;
X	else
X	    data.dat_len = p - data.dat_ptr + 1;
X	if ((p = index(data.dat_ptr, ' ')) == NULL)
X	    p = data.dat_ptr;
X	hashstore(acthash, data.dat_ptr, p - data.dat_ptr, data);
X	offset += data.dat_len;
X    }
X}
X
XACT_POS
Xfindact(outbuf, nam, len, suggestion)
Xregister char *outbuf;
Xregister char *nam;
Xregister int len;
Xregister long suggestion;
X{
X    register ACT_POS retval;
X    extern int debug;
X
X    /* see if we know the right place and can just return */
X    if (suggestion != 0 && fseek(actfp, suggestion, 0) >= 0
X     && fgets(outbuf, LBUFLEN, actfp) != NULL && outbuf[len] == ' '
X     && strnEQ(outbuf, nam, len))
X	return suggestion;
X
X    /* hmm, apparently not, gotta look it up. */
X#ifdef DEBUG
X    if (debug & DEB_SOFT_POINTERS)
X	printf("Missed, looking for %s in %sLen = %d\n",nam,outbuf,len) FLUSH;
X#endif
X    /* can we just do a quick hashed lookup? */
X    if (acthash != NULL) {
X	HASHDATUM data;
X
X	outbuf[0] = '\0';
X	data = hashfetch(acthash, nam, len);
X	if (data.dat_ptr == NULL)
X	    return -1;
X	else {
X	    (void) bcopy(data.dat_ptr, outbuf, (int)data.dat_len);
X	    outbuf[data.dat_len] = '\0';
X	    return data.dat_ptr - actfile;
X	}
X    }
X
X    /* apparently not.  gotta do it the slow way. */
X    (void) fseek(actfp, 0L, 0);
X    for (retval=0; fgets(outbuf,LBUFLEN,actfp)!=NULL; retval+=strlen(outbuf))
X	if (outbuf[len] == ' ' && strnEQ(outbuf, nam, len))
X	    break;
X    if (ferror(actfp)) {
X	perror("error on active file");
X	sig_catcher(0);
X	/* NOTREACHED */
X    } else if (feof(actfp))
X	return -1;	/* no such group */
X    return retval;
X}
X
X#ifdef EDIT_DISTANCE
X
X/* Edit Distance extension to trn
X *
X *	Mark Maimone (mwm@cmu.edu)
X *	Carnegie Mellon Computer Science
X *	9 May 1993
X *
X *	This code helps trn handle typos in newsgroup names much more
X *   gracefully.  Instead of "... does not exist!!", it will pick the
X *   nearest one, or offer you a choice if there are several options.
X */
X
X#define LENGTH_HACK 5	/* Don't bother comparing strings with lengths
X			 * that differ by more than this; making this
X			 * smaller than MIN_DIST prunes the number of
X			 * calls to edit_dist().  We won't catch any
X			 * sequences of MIN_DIST INSERTs or DELETEs,
X			 * but we (should!) save some time. */
X#define MAX_NG 9	/* Maximum number of options */
X
X#define ABS(x) (((x) < 0) ? -(x) : (x))
X
Xstatic void check_distance _((HASHDATUM*,int));
X
Xstatic char **ngptrs;		/* List of potential matches */
Xstatic int ngn;			/* Length of list in ngptrs[] */
Xstatic int nglen;		/* Length of name in ngname */
Xstatic int best_match;		/* Value of best match */
X
X/* find_close_match -- Finds the closest match for the string given in
X * global ngname.  If found, the result will be the corrected string
X * returned in that global.
X *
X * We compare the (presumably misspelled) newsgroup name with all legal
X * newsgroups, using the Edit Distance metric.  The edit distance between
X * two strings is the minimum number of simple operations required to
X * convert one string to another (the implementation here supports INSERT,
X * DELETE, CHANGE and SWAP).  This gives every legal newsgroup an integer
X * rank.
X *
X * You might want to present all of the closest matches, and let the user
X * choose among them.  But because I'm lazy I chose to only keep track of
X * all with newsgroups with the *single* smallest error, in array ngptrs[].
X * A more flexible approach would keep around the 10 best matches, whether
X * or not they had precisely the same edit distance, but oh well.
X */
X
Xint
Xfind_close_match()
X{
X   int ret = 0;
X
X    best_match = -1;
X    ngptrs = (char**)safemalloc(MAX_NG * sizeof (char*));
X    ngn = 0;
X    nglen = strlen(ngname);
X
X/* Iterate over all legal newsgroups */
X    hashwalk(acthash, check_distance, 0);
X
X/* ngn is the number of possibilities.  If there's just one, go with it. */
X
X    switch (ngn) {
X        case 0:
X	    break;
X	case 1: {
X	    char *cp = index(ngptrs[0], ' ');
X	    *cp = '\0';
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("(I assume you meant %s)\n", ngptrs[0]) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		printf("(Using %s)\n", ngptrs[0]) FLUSH;
X#endif
X	    set_ngname(ngptrs[0]);
X	    *cp = ' ';
X	    ret = 1;
X	    break;
X	}
X	default:
X	    ret = get_near_miss();
X	    break;
X    } /* switch */
X    free((char*)ngptrs);
X    return ret;
X} /* find_close_match */
X
Xstatic void
Xcheck_distance(data, ngnum)
XHASHDATUM *data;
Xint ngnum;
X{
X    int value, len;
X    char *cp;
X    if ((cp = index(data->dat_ptr, ' ')) == NULL)
X	return;
X    len = cp - data->dat_ptr;
X
X/* Efficiency:  don't call edit_dist when the lengths are already too
X * different */
X
X    if (ABS(len - nglen) > LENGTH_HACK)
X	return;
X
X    value = edit_distn(ngname, strlen(ngname), data->dat_ptr, len);
X    if (value > MIN_DIST)
X	return;
X
X    if (value < best_match)
X	ngn = 0;
X    if (best_match < 0 || value <= best_match) {
X	best_match = value;
X	if (ngn < MAX_NG)
X	    ngptrs[ngn++] = data->dat_ptr;
X    } /* if */
X}
X
X/* Now we've got several potential matches, and have to choose between them
X * somehow.  Again, results will be returned in global ngname */
X
Xint
Xget_near_miss()
X{
X    char promptbuf[256];
X    char options[MAX_NG+10], *op = options;
X    int i;
X
X#ifdef VERBOSE
X    IF(verbose)
X	printf("However, here are some close matches:\n") FLUSH;
X#endif
X    if (ngn > 9)
X	ngn = 9;	/* Since we're using single digits.... */
X    *options = '\0';
X    for (i = 0; i < ngn; i++) {
X	char *cp = index(ngptrs[i], ' ');
X	*cp = '\0';
X	printf("  %d.  %s\n", i+1, ngptrs[i]);
X	sprintf(op++, "%d", i+1);	/* Expensive, but avoids ASCII deps */
X	*cp = ' ';
X    } /* for */
X
X#ifdef VERBOSE
X    IF(verbose)
X	sprintf(promptbuf, "Which of these would you like? [%sn] ", options);
X    ELSE
X#endif
X#ifdef TERSE
X	sprintf(promptbuf, "Which? [%sn] ", options);
X#endif
Xreask:
X    in_char(promptbuf, 'A');
X    setdef(buf, "1");
X#ifdef VERIFY
X    printcmd();
X#endif
X    putchar('\n') FLUSH;
X    switch (*buf) {
X        case 'n':
X	case 'N':
X	case 'q':
X	case 'Q':
X	case 'x':
X	case 'X':
X	    return 0;
X	    break;
X	case 'h':
X	case 'H':
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs("  You entered an illegal newsgroup name, and these are the nearest possible\n  matches.  If you want one of these, then enter its number.  Otherwise\n  just say 'n'.\n", stdout) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("Illegal newsgroup, enter a number or 'n'.\n", stdout) FLUSH;
X#endif
X	    goto reask;
X	default:
X	    if (isdigit(*buf)) {
X		char *s = index(options, *buf);
X
X		i = s ? (s - options) : ngn;
X		if (i >= 0 && i < ngn) {
X		    char *cp = index(ngptrs[i], ' ');
X		    *cp = '\0';
X		    set_ngname(ngptrs[i]);
X		    *cp = ' ';
X		    return 1;
X		} /* if */
X	    } /* if */
X	    fputs(hforhelp, stdout) FLUSH;
X	    break;
X    } /* switch */
X
X    settle_down();
X    goto reask;
X} /* get_near_miss */
X
X#endif /* EDIT_DISTANCE */
END_OF_FILE
if test 9643 -ne `wc -c <'nghash.c'`; then
    echo shar: \"'nghash.c'\" unpacked with wrong size!
fi
# end of 'nghash.c'
fi
if test -f 'nntp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'nntp.c'\"
else
echo shar: Extracting \"'nntp.c'\" \(7017 characters\)
sed "s/^X//" >'nntp.c' <<'END_OF_FILE'
X/* $Id: nntp.c,v 3.0 1991/11/22 04:12:21 davison Trn $
X*/
X/* The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "util.h"
X#include "init.h"
X#include "trn.h"
X#include "ngdata.h"
X#include "rcln.h"
X#include "cache.h"
X#include "bits.h"
X#include "head.h"
X#include "final.h"
X#include "nntp.h"
X
X#ifdef USE_NNTP
X
X/* try to access the specified group */
X
Xbool
Xnntp_group(group, num)
Xchar *group;
XNG_NUM num;
X{
X    sprintf(ser_line, "GROUP %s", group);
X    nntp_command(ser_line);
X    if (nntp_check(FALSE) != NNTP_CLASS_OK) {
X	int ser_int = atoi(ser_line);
X	if (ser_int != NNTP_NOSUCHGROUP_VAL
X	 && ser_int != NNTP_SYNTAX_VAL) {
X	    if (ser_int != NNTP_AUTH_NEEDED_VAL && ser_int != NNTP_ACCESS_VAL
X	     && ser_int != NNTP_AUTH_REJECT_VAL) {
X		fprintf(stderr, "\nServer's response to GROUP %s:\n%s\n",
X			group, ser_line);
X		finalize(1);
X	    }
X	}
X	return FALSE;
X    }
X    if (num >= 0) {
X	long count, first, last;
X
X	(void) sscanf(ser_line,"%*d%ld%ld%ld",&count,&first,&last);
X	/* NNTP mangles the high/low values when no articles are present. */
X	if (!count)
X	    abs1st[num] = ngmax[num]+1;
X	else {
X	    abs1st[num] = (ART_NUM)first;
X	    if (last > ngmax[num])
X		ngmax[num] = (ART_NUM)last;
X	}
X    }
X    return TRUE;
X}
X
X/* check on an article's existence */
X
Xbool
Xnntp_stat(artnum)
XART_NUM artnum;
X{
X    sprintf(ser_line, "STAT %ld", (long)artnum);
X    nntp_command(ser_line);
X    return (nntp_check(TRUE) == NNTP_CLASS_OK);
X}
X
X/* prepare to get the header */
X
Xbool
Xnntp_header(artnum)
XART_NUM artnum;
X{
X    sprintf(ser_line, "HEAD %ld", (long)artnum);
X    nntp_command(ser_line);
X    return (nntp_check(TRUE) == NNTP_CLASS_OK);
X}
X
X/* copy the body of an article to a temporary file */
X
XFILE *
Xnntp_body(artnum)
XART_NUM artnum;
X{
X    char *artname;
X    FILE *fp;
X
X    if (!parseheader(artnum))
X	return Nullfp;
X    artname = nntp_artname();
X    if (!(fp = fopen(artname, "w+"))) {
X	fprintf(stderr, "\nUnable to write temporary file: '%s'.\n",
X		artname);
X	finalize(1);
X    }
X    sprintf(ser_line, "BODY %ld", (long)artnum);
X    nntp_command(ser_line);
X    if (nntp_check(TRUE) != NNTP_CLASS_OK) {	/* and get it's reaction */
X	fclose(fp);
X	errno = ENOENT;			/* Simulate file-not-found */
X	return Nullfp;
X    }
X    fwrite(headbuf, 1, strlen(headbuf), fp);
X    for (;;) {
X	nntp_gets(ser_line, sizeof ser_line);
X	if (ser_line[0] == '.' && ser_line[1] == '\0')
X	    break;
X	fputs((ser_line[0] == '.' ? ser_line + 1 : ser_line), fp);
X	putc('\n', fp);
X    }
X    fseek(fp, 0L, 0);
X    return fp;
X}
X
X/* This is a 1-relative list */
Xstatic int maxdays[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
X
Xtime_t
Xnntp_time()
X{
X    char *s;
X    int year, month, day, hh, mm;
X    time_t ss;
X
X    nntp_command("DATE");
X    if (nntp_check(FALSE) != NNTP_CLASS_INF)
X	return time((time_t*)NULL);
X
X    s = ser_line + strlen(ser_line) - 1;
X
X    ss = (*s - '0') + (*--s - '0') * 10;
X    mm = (*--s - '0') + (*--s - '0') * 10;
X    hh = (*--s - '0') + (*--s - '0') * 10;
X    day = (*--s - '0') + (*--s - '0') * 10;
X    month = (*--s - '0') + (*--s - '0') * 10;
X    *s = '\0';
X    year = atoi(s-4);
X
X    /* This simple algorithm will be valid until the year 2400 */
X    if (year % 4)
X	maxdays[2] = 28;
X    else
X	maxdays[2] = 29;
X    if (month < 1 || month > 12 || day < 1 || day > maxdays[month]
X     || hh < 0 || hh > 23 || mm < 0 || mm > 59
X     || ss < 0 || ss > 59)
X	return time((time_t*)NULL);
X
X    for (month--; month; month--)
X	day += maxdays[month];
X
X    ss = ((((year-1970) * 365 + (year-1968)/4 + day - 1) * 24L + hh) * 60
X	  + mm) * 60 + ss;
X
X    return ss;
X}
X
Xbool
Xnntp_newgroups(t)
Xtime_t t;
X{
X    struct tm *ts;
X
X    ts = gmtime(&t);
X    sprintf(ser_line, "NEWGROUPS %02d%02d%02d %02d%02d%02d GMT",
X	ts->tm_year % 100, ts->tm_mon+1, ts->tm_mday,
X	ts->tm_hour, ts->tm_min, ts->tm_sec);
X    nntp_command(ser_line);
X    return (nntp_check(TRUE) == NNTP_CLASS_OK);
X}
X
Xbool
Xnntp_listgroup()
X{
X    static bool listgroup_works = TRUE;
X
X    if (!listgroup_works)
X	return FALSE;
X    nntp_command("LISTGROUP");
X    if (nntp_check(FALSE) != NNTP_CLASS_OK) {
X	listgroup_works = FALSE;
X	return FALSE;
X    }
X    return TRUE;
X}
X
X/* similar to nntp_gets, but will make the buffer bigger if necessary */
X
Xchar *
Xnntp_get_a_line(original_buffer,buffer_length)
Xchar *original_buffer;
Xregister int buffer_length;
X{
X    register int bufix = 0;
X    register int nextch;
X    register char *some_buffer_or_other = original_buffer;
X
X    do {
X	if (bufix >= buffer_length) {
X	    buffer_length *= 2;
X	    if (some_buffer_or_other == original_buffer) {
X					/* currently static? */
X		some_buffer_or_other = safemalloc((MEM_SIZE)buffer_length+1);
X		strncpy(some_buffer_or_other,original_buffer,buffer_length/2);
X					/* so we must copy it */
X	    }
X	    else {			/* just grow in place, if possible */
X		some_buffer_or_other = saferealloc(some_buffer_or_other,
X		    (MEM_SIZE)buffer_length+1);
X	    }
X	}
X	if ((nextch = getc(ser_rd_fp)) == EOF)
X	    return Nullch;
X	some_buffer_or_other[bufix++] = (char) nextch;
X    } while (nextch && nextch != '\n');
X    some_buffer_or_other[bufix] = '\0';
X    len_last_line_got = bufix;
X    buflen_last_line_got = buffer_length;
X    return some_buffer_or_other;
X}
X
Xchar *
Xnntp_artname()
X{
X    static char artname[20];
X    sprintf(artname,"rrn.%ld",our_pid);
X    return artname;
X}
X
X/* cleanup the odds and ends associated with NNTP usage */
X
Xvoid
Xnntp_cleanup()
X{
X    UNLINK(nntp_artname());
X    if (*active_name)
X	UNLINK(active_name);
X    nntp_close();
X}
X
X#ifdef USE_XTHREAD
X
Xstatic long rawbytes = -1;	/* bytes remaining to be transfered */
X
X/* nntp_readcheck -- get a line of text from the server, interpreting
X** it as a status message for a binary command.  Call this once
X** before calling nntp_read() for the actual data transfer.
X*/
Xlong
Xnntp_readcheck()
X{
X    /* try to get the status line and the status code */
X    if (nntp_check(FALSE) != NNTP_CLASS_OK)
X	return rawbytes = -1;
X
X    /* try to get the number of bytes being transfered */
X    if (sscanf(ser_line, "%*d%ld", &rawbytes) != 1)
X	return rawbytes = -1;
X    return rawbytes;
X}
X
X/* nntp_read -- read data from the server in binary format.  This call must
X** be preceeded by an appropriate binary command and an nntp_readcheck call.
X*/
Xlong
Xnntp_read(buf, n)
Xchar *buf;
Xlong n;
X{
X    /* if no bytes to read, then just return EOF */
X    if (rawbytes < 0)
X	return 0;
X
X#ifdef HAS_SIGHOLD
X    sighold(SIGINT);
X#endif
X
X    /* try to read some data from the server */
X    if (rawbytes) {
X	n = fread(buf, 1, n > rawbytes ? rawbytes : n, ser_rd_fp);
X	rawbytes -= n;
X    } else
X	n = 0;
X
X    /* if no more left, then fetch the end-of-command signature */
X    if (!rawbytes) {
X	char buf[5];	/* "\r\n.\r\n" */
X
X	fread(buf, 1, 5, ser_rd_fp);
X	rawbytes = -1;
X    }
X#ifdef HAS_SIGHOLD
X    sigrelse(SIGINT);
X#endif
X    return n;
X}
X#endif /* USE_XTHREAD */
X
X#endif /* USE_NNTP */
END_OF_FILE
if test 7017 -ne `wc -c <'nntp.c'`; then
    echo shar: \"'nntp.c'\" unpacked with wrong size!
fi
# end of 'nntp.c'
fi
if test -f 'nntpinit.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'nntpinit.c'\"
else
echo shar: Extracting \"'nntpinit.c'\" \(9682 characters\)
sed "s/^X//" >'nntpinit.c' <<'END_OF_FILE'
X/* $Id: nntpinit.c,v 3.0 1991/11/22 04:12:21 davison Trn $
X*/
X/* This software is Copyright 1992 by Stan Barber. 
X *
X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
X * use this software as long as: there is no monetary profit gained
X * specifically from the use or reproduction or this software, it is not
X * sold, rented, traded or otherwise marketed, and this copyright notice is
X * included prominently in any copy made. 
X *
X * The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
X#undef	DECNET    /* If you want decnet support */
X#undef	EXCELAN   /* Excelan EXOS 205 support */
X#undef	NONETDB	  /* Define if you're missing netdb.h */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "nntpclient.h"
X
X#ifdef USE_NNTP
X
X#include <sys/socket.h>
X#include <netinet/in.h>
X#ifdef NONETDB
X# define IPPORT_NNTP	((unsigned short) 119)
X#else
X# include <netdb.h>
X#endif /* !EXCELAN */
X
X#ifdef EXCELAN
Xint connect _((int, struct sockaddr *));
Xunsigned short htons _((unsigned short));
Xunsigned long rhost _((char **));
Xint rresvport p((int));
Xint socket _((int, struct sockproto *, struct sockaddr_in *, int));
X#endif /* EXCELAN */
X
X#ifdef DECNET
X#include <netdnet/dn.h>
X#include <netdnet/dnetdb.h>
X#endif /* DECNET */
X
Xunsigned long inet_addr _((char *x));
Xint get_tcp_socket _((char *machine));
X
Xint
Xserver_init(server)
Xchar *server;
X{
X    char line2[NNTP_STRLEN];
X    int sockt_rd, sockt_wr;
X#ifdef DECNET
X    char *cp;
X
X    cp = index(server, ':');
X
X    if (cp && cp[1] == ':') {
X	*cp = '\0';
X	sockt_rd = get_dnet_socket(server);
X    } else
X	sockt_rd = get_tcp_socket(server);
X#else /* !DECNET */
X    sockt_rd = get_tcp_socket(server);
X#endif
X
X    if (sockt_rd < 0)
X	return -1;
X    sockt_wr = dup(sockt_rd);
X
X    /* Now we'll make file pointers (i.e., buffered I/O) out of
X    ** the socket file descriptor.  Note that we can't just
X    ** open a fp for reading and writing -- we have to open
X    ** up two separate fp's, one for reading, one for writing. */
X    if ((ser_rd_fp = fdopen(sockt_rd, "r")) == NULL) {
X	perror("server_init: fdopen #1");
X	return -1;
X    }
X    if ((ser_wr_fp = fdopen(sockt_wr, "w")) == NULL) {
X	perror("server_init: fdopen #2");
X	ser_rd_fp = NULL;
X	return -1;
X    }
X
X    /* Now get the server's signon message */
X    nntp_check(FALSE);
X
X    if (*ser_line == NNTP_CLASS_OK) {
X	/* Send a MODE READER command in case we're talking to innd.
X	** If understood, use that reply. */
X	nntp_command("MODE READER");
X	nntp_gets(line2, sizeof line2);
X	if (atoi(line2) != NNTP_BAD_COMMAND_VAL)
X	    strcpy(ser_line, line2);
X    }
X    return atoi(ser_line);
X}
X
Xint
Xget_tcp_socket(server)
Xchar *server;
X{
X    int s;
X    struct sockaddr_in sin;
X#ifdef __hpux
X    int socksize = 0;
X    int socksizelen = sizeof socksize;
X#endif
X#ifdef NONETDB
X    bzero((char *) &sin, sizeof(sin));
X    sin.sin_family = AF_INET;
X#else
X    struct servent *getservbyname(), *sp;
X    struct hostent *gethostbyname(), *hp;
X#ifdef h_addr
X    int x = 0;
X    register char **cp;
X    static char *alist[1];
X#endif /* h_addr */
X    static struct hostent def;
X    static struct in_addr defaddr;
X    static char namebuf[ 256 ];
X
X    if ((sp = getservbyname("nntp", "tcp")) ==  NULL) {
X	fprintf(stderr, "nntp/tcp: Unknown service.\n");
X	return -1;
X    }
X    /* If not a raw ip address, try nameserver */
X    if (!isdigit(*server)
X     || (long)(defaddr.s_addr = inet_addr(server)) == -1)
X	hp = gethostbyname(server);
X    else {
X	/* Raw ip address, fake  */
X	(void) strcpy(namebuf, server);
X	def.h_name = namebuf;
X#ifdef h_addr
X	def.h_addr_list = alist;
X#endif
X	def.h_addr = (char *)&defaddr;
X	def.h_length = sizeof(struct in_addr);
X	def.h_addrtype = AF_INET;
X	def.h_aliases = 0;
X	hp = &def;
X    }
X    if (hp == NULL) {
X	fprintf(stderr, "%s: Unknown host.\n", server);
X	return -1;
X    }
X
X    bzero((char *) &sin, sizeof(sin));
X    sin.sin_family = hp->h_addrtype;
X    sin.sin_port = sp->s_port;
X#endif /* !NONETDB */
X
X    /* The following is kinda gross.  The name server under 4.3
X    ** returns a list of addresses, each of which should be tried
X    ** in turn if the previous one fails.  However, 4.2 hostent
X    ** structure doesn't have this list of addresses.
X    ** Under 4.3, h_addr is a #define to h_addr_list[0].
X    ** We use this to figure out whether to include the NS specific
X    ** code... */
X#ifdef h_addr
X    /* get a socket and initiate connection -- use multiple addresses */
X    for (cp = hp->h_addr_list; cp && *cp; cp++) {
X	s = socket(hp->h_addrtype, SOCK_STREAM, 0);
X	if (s < 0) {
X	    perror("socket");
X	    return -1;
X	}
X        bcopy(*cp, (char *)&sin.sin_addr, hp->h_length);
X		
X	if (x < 0)
X	    fprintf(stderr, "trying %s\n", inet_ntoa(sin.sin_addr));
X	x = connect(s, (struct sockaddr *)&sin, sizeof (sin));
X	if (x == 0)
X	    break;
X        fprintf(stderr, "connection to %s: ", inet_ntoa(sin.sin_addr));
X	perror("");
X	(void) close(s);
X    }
X    if (x < 0) {
X	fprintf(stderr, "giving up...\n");
X	return -1;
X    }
X#else /* no name server */
X#ifdef EXCELAN
X    s = socket(SOCK_STREAM, (struct sockproto *)NULL, &sin, SO_KEEPALIVE);
X    if (s < 0) {
X	/* Get the socket */
X	perror("socket");
X	return -1;
X    }
X    bzero((char *) &sin, sizeof(sin));
X    sin.sin_family = AF_INET;
X    sin.sin_port = htons(IPPORT_NNTP);
X
X    /* set up addr for the connect */
X    if ((sin.sin_addr.s_addr = rhost(&server)) == -1) {
X	fprintf(stderr, "%s: Unknown host.\n", server);
X	return -1;
X    }
X
X    /* And then connect */
X    if (connect(s, (struct sockaddr *)&sin) < 0) {
X	perror("connect");
X	(void) close(s);
X	return -1;
X    }
X#else /* not EXCELAN */
X    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
X	perror("socket");
X	return -1;
X    }
X
X    /* And then connect */
X
X    bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
X    if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
X	perror("connect");
X	(void) close(s);
X	return -1;
X    }
X
X#endif /* !EXCELAN */
X#endif /* !h_addr */
X#ifdef __hpux	/* recommended by raj@cup.hp.com */
X#define	HPSOCKSIZE 0x8000
X    getsockopt(s, SOL_SOCKET, SO_SNDBUF, (caddr_t)&socksize, (caddr_t)&socksizelen);
X    if (socksize < HPSOCKSIZE) {
X	socksize = HPSOCKSIZE;
X	setsockopt(s, SOL_SOCKET, SO_SNDBUF, (caddr_t)&socksize, sizeof(socksize));
X    }
X    socksize = 0;
X    socksizelen = sizeof(socksize);
X    getsockopt(s, SOL_SOCKET, SO_RCVBUF, (caddr_t)&socksize, (caddr_t)&socksizelen);
X    if (socksize < HPSOCKSIZE) {
X	socksize = HPSOCKSIZE;
X	setsockopt(s, SOL_SOCKET, SO_RCVBUF, (caddr_t)&socksize, sizeof(socksize));
X    }
X#endif
X    return s;
X}
X
X#ifdef DECNET
Xint
Xget_dnet_socket(server)
Xchar *server;
X{
X    int s, area, node;
X    struct sockaddr_dn sdn;
X    struct nodeent *getnodebyname(), *np;
X
X    bzero((char *) &sdn, sizeof(sdn));
X
X    switch (s = sscanf(server, "%d%*[.]%d", &area, &node)) {
X    case 1: 
X	node = area;
X	area = 0;
X    case 2: 
X	node += area*1024;
X	sdn.sdn_add.a_len = 2;
X	sdn.sdn_family = AF_DECnet;
X	sdn.sdn_add.a_addr[0] = node % 256;
X	sdn.sdn_add.a_addr[1] = node / 256;
X	break;
X    default:
X	if ((np = getnodebyname(server)) == NULL) {
X	    fprintf(stderr, "%s: Unknown host.\n", server);
X	    return -1;
X	} else {
X	    bcopy(np->n_addr, (char *) sdn.sdn_add.a_addr, np->n_length);
X	    sdn.sdn_add.a_len = np->n_length;
X	    sdn.sdn_family = np->n_addrtype;
X	}
X	break;
X    }
X    sdn.sdn_objnum = 0;
X    sdn.sdn_flags = 0;
X    sdn.sdn_objnamel = strlen("NNTP");
X    bcopy("NNTP", &sdn.sdn_objname[0], sdn.sdn_objnamel);
X
X    if ((s = socket(AF_DECnet, SOCK_STREAM, 0)) < 0) {
X	nerror("socket");
X	return -1;
X    }
X
X    /* And then connect */
X    if (connect(s, (struct sockaddr *) &sdn, sizeof(sdn)) < 0) {
X	nerror("connect");
X	close(s);
X	return -1;
X    }
X    return s;
X}
X#endif /* DECNET */
X
X#ifdef EXCELAN
X/*
X * inet_addr for EXCELAN (which does not have it!)
X *
X */
Xunsigned long
Xinet_addr(cp)
Xregister char   *cp;
X{
X	unsigned long val, base, n;
X	register char c;
X	unsigned long octet[4], *octetptr = octet;
X#ifndef htonl
X	extern  unsigned long   htonl();
X#endif  /* htonl */
Xagain:
X	/*
X	 * Collect number up to ``.''.
X	 * Values are specified as for C:
X	 * 0x=hex, 0=octal, other=decimal.
X	 */
X	val = 0; base = 10;
X	if (*cp == '0')
X		base = 8, cp++;
X	if (*cp == 'x' || *cp == 'X')
X		base = 16, cp++;
X	while (c = *cp) {
X		if (isdigit(c)) {
X			val = (val * base) + (c - '0');
X			cp++;
X			continue;
X		}
X		if (base == 16 && isxdigit(c)) {
X			val = (val << 4) + (c + 10 - (islower(c) ? 'a' : 'A'));
X			cp++;
X			continue;
X		}
X		break;
X	}
X	if (*cp == '.') {
X		/*
X		 * Internet format:
X		 *      a.b.c.d
X		 *      a.b.c   (with c treated as 16-bits)
X		 *      a.b     (with b treated as 24 bits)
X		 */
X		if (octetptr >= octet + 4)
X			return (-1);
X		*octetptr++ = val, cp++;
X		goto again;
X	}
X	/*
X	 * Check for trailing characters.
X	 */
X	if (*cp && !isspace(*cp))
X		return (-1);
X	*octetptr++ = val;
X	/*
X	 * Concoct the address according to
X	 * the number of octet specified.
X	 */
X	n = octetptr - octet;
X	switch (n) {
X
X	case 1:                         /* a -- 32 bits */
X		val = octet[0];
X		break;
X
X	case 2:                         /* a.b -- 8.24 bits */
X		val = (octet[0] << 24) | (octet[1] & 0xffffff);
X		break;
X
X	case 3:                         /* a.b.c -- 8.8.16 bits */
X		val = (octet[0] << 24) | ((octet[1] & 0xff) << 16) |
X			(octet[2] & 0xffff);
X		break;
X
X	case 4:                         /* a.b.c.d -- 8.8.8.8 bits */
X		val = (octet[0] << 24) | ((octet[1] & 0xff) << 16) |
X		      ((octet[2] & 0xff) << 8) | (octet[3] & 0xff);
X		break;
X
X	default:
X		return (-1);
X	}
X	val = htonl(val);
X	return (val);
X}
X#endif /* EXCELAN */
X
X#endif /* USE_NNTP */
END_OF_FILE
if test 9682 -ne `wc -c <'nntpinit.c'`; then
    echo shar: \"'nntpinit.c'\" unpacked with wrong size!
fi
# end of 'nntpinit.c'
fi
if test -f 'rt-ov.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rt-ov.c'\"
else
echo shar: Extracting \"'rt-ov.c'\" \(7099 characters\)
sed "s/^X//" >'rt-ov.c' <<'END_OF_FILE'
X/* $Id: rt-ov.c,v 3.0 1992/12/14 00:14:13 davison Trn $
X*/
X/* The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "trn.h"
X#include "cache.h"
X#include "bits.h"
X#include "head.h"
X#include "ngdata.h"
X#include "util.h"
X#include "ng.h"
X#include "nntp.h"
X#include "term.h"
X#include "final.h"
X#include "hash.h"
X#include "rthread.h"
X#include "rt-process.h"
X#include "rt-util.h"
X#include "overview.h"
X#include "INTERN.h"
X#include "rt-ov.h"
X
Xbool
Xov_init()
X{
X#ifdef USE_XOVER
X    /* Check if the server is XOVER complient (we're not in a group, BTW) */
X    sprintf(ser_line, "XOVER");
X    nntp_command(ser_line);
X    nntp_check(FALSE);
X    if (atoi(ser_line) == NNTP_BAD_COMMAND_VAL)
X	return FALSE;
X    /* paranoia reins supreme */
X    if (*ser_line == NNTP_CLASS_OK) {
X	do {
X	    nntp_gets(ser_line, sizeof ser_line);
X	} while (*ser_line != '.');
X    }
X#endif
X    return TRUE;
X}
X
X#ifndef OV_XREFS
X# ifdef OV_OTHERS_HAS_XREFS
Xbool ov_files_have_xrefs = TRUE;	/* set once per session */
X# else
Xbool ov_files_have_xrefs = FALSE;	/* set once per session */
X# endif
X#endif
X#ifdef USE_XOVER
XART_NUM ov_next_art;
X#else
XFILE *ov_in;
X#endif
X
X/* Process the data in the group's news-overview file.
X*/
Xbool
Xov_data(first, last, cheating)
XART_NUM first, last;
Xbool_int cheating;
X{
X    register ARTICLE *ap;
X    ART_NUM artnum, an;
X    char *line, *last_buf = buf;
X    MEM_SIZE last_buflen = LBUFLEN;
X    int cachemask;
X    bool success = TRUE;
X#ifdef USE_XOVER
X    ART_NUM real_first = first, real_last = last;
X
X    setspin(cheating? SPIN_BACKGROUND : SPIN_FOREGROUND);
Xbeginning:
X    if (ov_opened)
X	first = ov_next_art;
X    if (last < first)
X	goto exit;
X    if (last - first > OV_CHUNK_SIZE + OV_CHUNK_SIZE/2 - 1)
X	last = first + OV_CHUNK_SIZE - 1;
X    sprintf(ser_line, "XOVER %ld-%ld", (long)first, (long)last);
X    nntp_command(ser_line);
X    if (nntp_check(FALSE) != NNTP_CLASS_OK) {
X	success = FALSE;
X	goto exit;
X    }
X    if (!ov_opened)
X	printf("\nGetting overview file."), fflush(stdout);
X    ov_next_art = last+1;
X
X#else /* !USE_XOVER */
X
X    if (!ov_opened) {
X	if ((ov_in = fopen(ov_name(ngname), "r")) == Nullfp) {
X	    return FALSE;
X	}
X	printf("\nReading overview file."), fflush(stdout);
X    }
X    setspin(cheating? SPIN_BACKGROUND : SPIN_FOREGROUND);
X#endif /* !USE_XOVER */
X
X    ov_opened = TRUE;
X    artnum = first-1;
X    for (;;) {
X#ifdef USE_XOVER
X	line = nntp_get_a_line(last_buf, last_buflen);
X	if (*line == '.')
X	    break;
X#else
X	if (!(line = get_a_line(last_buf, last_buflen, ov_in)))
X	    break;
X#endif
X	last_buf = line;
X	last_buflen = buflen_last_line_got;
X	artnum = atol(line);
X	spin(100);
X#ifndef USE_XOVER
X	if (artnum < first)
X	    continue;
X	if (artnum > last) {
X	    artnum = last;
X	    break;
X	}
X#endif
X	if ((ap = ov_parse(line, artnum)) != Nullart) {
X#ifndef OV_XREFS
X	    if (ov_files_have_xrefs) {
X		if (!ap->xrefs)
X		    ap->xrefs = nullstr;
X	    } else if (ap->xrefs) {
X		register ART_UNREAD i;
X		register ARTICLE *ap2;
X		ap2 = article_ptr(first);
X		for (i = artnum-first; i; ap2++, i--)
X		    ap2->xrefs = nullstr;
X		ov_files_have_xrefs = TRUE;
X	    }
X#endif
X	    if (ThreadedGroup) {
X		if (valid_article(ap))
X		    thread_article(ap);
X	    } else if (!(ap->flags & AF_CACHED))
X		cache_article(ap);
X	    check_poster(ap);
X	}
X#ifdef USE_XOVER
X	if (int_count) {
X	    int_count = 0;
X	    success = FALSE;
X	}
X#else
X	if (int_count) {
X	    int_count = 0;
X	    success = FALSE;
X	    break;
X	}
X	if (cheating) {
X	    if (input_pending()) {
X		success = FALSE;
X		break;
X	    }
X	    if (curr_artp != sentinel_artp) {
X		pushchar('\f' | 0200);
X		success = FALSE;
X		break;
X	    }
X	}
X#endif /* !USE_XOVER */
X    }
X    cachemask = (ThreadedGroup? AF_THREADED : AF_CACHED);
X    for (an = first, ap = article_ptr(an); an <= artnum; an++, ap++) {
X	if (!(ap->flags & cachemask)) {
X#ifdef USE_NNTP
X	    onemissing(ap);
X#else
X	    (void) parseheader(an);
X#endif
X	}
X    }
X    if (artnum > last_cached && artnum >= first)
X	last_cached = artnum;
X#ifdef USE_XOVER
X    if (int_count || !success) {
X	int_count = 0;
X	success = FALSE;
X    } else if (cheating && curr_artp != sentinel_artp) {
X	pushchar('\f' | 0200);
X	success = FALSE;
X    } else if (last < real_last) {
X	if (!cheating || !input_pending()) {
X	    last = real_last;
X	    goto beginning;
X	}
X	success = FALSE;
X    } else
X	ov_next_art = absfirst;
X    if (success && real_first <= first_cached) {
X	first_cached = real_first;
X	cached_all_in_range = TRUE;
X    }
X  exit:
X#else
X    if (success && first <= first_cached) {
X	first_cached = first;
X	cached_all_in_range = TRUE;
X    }
X    if (!cheating)
X	fseek(ov_in, 0L, 0);	/* rewind it for the cheating phase */
X#endif
X    setspin(SPIN_POP);
X    if (last_buf != buf)
X	free(last_buf);
X    return success;
X}
X
Xstatic ARTICLE *
Xov_parse(line, artnum)
Xregister char *line;
XART_NUM artnum;
X{
X    register int nf;
X    register ARTICLE *article;
X    char *fields[OV_OTHERS+1], *cp;
X
X    article = article_ptr(artnum);
X    if (article->flags & AF_THREADED)
X	return Nullart;
X
X    if (len_last_line_got > 0 && line[len_last_line_got-1] == '\n')
X	line[len_last_line_got-1] = '\0';
X
X    cp = line;
X    
X    for (nf = 0; ; nf++) {
X	fields[nf] = cp;
X	if (nf == OV_OTHERS)
X	    break;
X	if (!(cp = index(cp, '\t'))) {
X	    if (nf < OV_OTHERS-1)	/* only "others" field is optional */
X		return Nullart;		/* skip this line */
X	    break;
X	}
X	*cp++ = '\0';
X    }
X
X    if (!article->subj)
X	set_subj_line(article, fields[OV_SUBJ], strlen(fields[OV_SUBJ]));
X    if (!article->msgid)
X	article->msgid = savestr(fields[OV_MSGID]);
X    if (!article->from)
X	article->from = savestr(fields[OV_FROM]);
X    if (!article->date)
X	article->date = parsedate(fields[OV_DATE]);
X    references = fields[OV_REFS];
X
X#ifdef OV_XREFS
X# ifdef OV_LAX_XREFS
X    if (!strncasecmp("xref: ", fields[OV_XREFS], 6))
X	article->xrefs = savestr(fields[OV_XREFS]+6);
X    else
X	article->xrefs = savestr(fields[OV_XREFS]);
X# else
X    article->xrefs = savestr(fields[OV_XREFS]);
X# endif
X#else
X    /* check the "others" field for an optional xrefs header */
X    if (nf == OV_OTHERS && !article->xrefs) {
X	register char *fld;
X	cp = fields[OV_OTHERS];
X	while (cp && *cp) {
X	    fld = cp;
X	    if ((cp = index(cp, '\t')) != Nullch)
X		*cp++ = '\0';
X	    if (!strncasecmp("xref: ", fld, 6)) {
X		article->xrefs = savestr(fld+6);
X		break;
X	    }
X	}
X    }
X#endif
X    return article;
X}
X
X#ifndef USE_XOVER
X/* Change a newsgroup name into the name of the overview data file.  We
X** subsitute any '.'s in the group name into '/'s, prepend the path, and
X** append the '/.overview' or '.ov') on to the end.
X*/
Xstatic char *
Xov_name(group)
Xchar *group;
X{
X    register char *cp;
X
X    cp = strcpy(buf, overviewdir) + strlen(overviewdir);
X    *cp++ = '/';
X    strcpy(cp, group);
X    while ((cp = index(cp, '.')))
X	*cp = '/';
X    strcat(buf, OV_FILE_NAME);
X    return buf;
X}
X#endif
X
Xvoid
Xov_close()
X{
X    if (ov_opened) {
X#ifndef USE_XOVER
X	(void) fclose(ov_in);
X#endif
X	ov_opened = FALSE;
X    }
X}
END_OF_FILE
if test 7099 -ne `wc -c <'rt-ov.c'`; then
    echo shar: \"'rt-ov.c'\" unpacked with wrong size!
fi
# end of 'rt-ov.c'
fi
if test -f 'term.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'term.h'\"
else
echo shar: Extracting \"'term.h'\" \(8440 characters\)
sed "s/^X//" >'term.h' <<'END_OF_FILE'
X/* $Id: term.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 circlebuf[PUSHSIZE];
XEXT int nextin INIT(0);
XEXT int nextout INIT(0);
XEXT char lastchar;
X#ifdef PENDING
X# ifdef FIONREAD
XEXT long iocount INIT(0);
X#  ifndef lint
X#define input_pending() (nextin!=nextout || (ioctl(0, FIONREAD, &iocount),(int)iocount))
X#  else
X#define input_pending() bizarre
X#  endif /* lint */
X# else /* FIONREAD */
X#  ifdef HAS_RDCHK
X#define input_pending() (nextin!=nextout || rdchk(0))
X#  else /*  HAS_RDCHK */
Xint circfill();
XEXT int devtty INIT(0);
X#   ifndef lint
X#define input_pending() (nextin!=nextout || circfill())
X#   else
X#define input_pending() bizarre
X#   endif /* lint */
X#  endif /* HAS_RDCHK */
X# endif /* FIONREAD */
X#else /* PENDING */
X# ifndef lint
X#define input_pending() (nextin!=nextout)
X# else
X#define input_pending() bizarre
X# endif /* lint */
X#endif /* PENDING */
X
X/* stuff wanted by terminal mode diddling routines */
X
X#ifdef I_TERMIO
XEXT struct termio _tty, _oldtty;
X#else
X# ifdef I_TERMIOS
XEXT struct termios _tty, _oldtty;
X# else
XEXT struct sgttyb _tty;
XEXT int _res_flg INIT(0);
X# endif
X#endif
X
XEXT int _tty_ch INIT(2);
XEXT bool bizarre INIT(FALSE);			/* do we need to restore terminal? */
X
X/* terminal mode diddling routines */
X
X#ifdef I_TERMIO
X
X#define crmode() ((bizarre=1),_tty.c_lflag &=~ICANON,_tty.c_cc[VMIN] = 1,ioctl(_tty_ch,TCSETAF,&_tty))
X#define nocrmode() ((bizarre=1),_tty.c_lflag |= ICANON,_tty.c_cc[VEOF] = CEOF,stty(_tty_ch,&_tty))
X#define echo()	 ((bizarre=1),_tty.c_lflag |= ECHO, ioctl(_tty_ch, TCSETA, &_tty))
X#define noecho() ((bizarre=1),_tty.c_lflag &=~ECHO, ioctl(_tty_ch, TCSETA, &_tty))
X#define nl()	 ((bizarre=1),_tty.c_iflag |= ICRNL,_tty.c_oflag |= ONLCR,ioctl(_tty_ch, TCSETAW, &_tty))
X#define nonl()	 ((bizarre=1),_tty.c_iflag &=~ICRNL,_tty.c_oflag &=~ONLCR,ioctl(_tty_ch, TCSETAW, &_tty))
X#define	savetty() (ioctl(_tty_ch, TCGETA, &_oldtty),ioctl(_tty_ch, TCGETA, &_tty))
X#define	resetty() ((bizarre=0),ioctl(_tty_ch, TCSETAF, &_oldtty))
X#define unflush_output()
X
X#else /* !I_TERMIO */
X# ifdef I_TERMIOS
X
X#define crmode() ((bizarre=1), _tty.c_lflag &= ~ICANON,_tty.c_cc[VMIN]=1,tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
X#define nocrmode() ((bizarre=1),_tty.c_lflag |= ICANON,_tty.c_cc[VEOF] = CEOF,tcsetattr(_tty_ch, TCSAFLUSH,&_tty))
X#define echo()	 ((bizarre=1),_tty.c_lflag |= ECHO, tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
X#define noecho() ((bizarre=1),_tty.c_lflag &=~ECHO, tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
X#define nl()	 ((bizarre=1),_tty.c_iflag |= ICRNL,_tty.c_oflag |= ONLCR,tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
X#define nonl()	 ((bizarre=1),_tty.c_iflag &=~ICRNL,_tty.c_oflag &=~ONLCR,tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
X#define	savetty() (tcgetattr(_tty_ch, &_oldtty),tcgetattr(_tty_ch, &_tty))
X#define	resetty() ((bizarre=0),tcsetattr(_tty_ch, TCSAFLUSH, &_oldtty))
X#define unflush_output()
X
X# else /* !I_TERMIOS */
X
X#define raw()	 ((bizarre=1),_tty.sg_flags|=RAW, stty(_tty_ch,&_tty))
X#define noraw()	 ((bizarre=1),_tty.sg_flags&=~RAW,stty(_tty_ch,&_tty))
X#define crmode() ((bizarre=1),_tty.sg_flags |= CBREAK, stty(_tty_ch,&_tty))
X#define nocrmode() ((bizarre=1),_tty.sg_flags &= ~CBREAK,stty(_tty_ch,&_tty))
X#define echo()	 ((bizarre=1),_tty.sg_flags |= ECHO, stty(_tty_ch, &_tty))
X#define noecho() ((bizarre=1),_tty.sg_flags &= ~ECHO, stty(_tty_ch, &_tty))
X#define nl()	 ((bizarre=1),_tty.sg_flags |= CRMOD,stty(_tty_ch, &_tty))
X#define nonl()	 ((bizarre=1),_tty.sg_flags &= ~CRMOD, stty(_tty_ch, &_tty))
X#define	savetty() (gtty(_tty_ch, &_tty), _res_flg = _tty.sg_flags)
X#define	resetty() ((bizarre=0),_tty.sg_flags = _res_flg, stty(_tty_ch, &_tty))
X#  ifdef LFLUSHO
X#   ifndef lint
XEXT int lflusho INIT(LFLUSHO);
X#   else
XEXT long lflusho INIT(LFLUSHO);
X#   endif /* lint */
X#define unflush_output() (ioctl(_tty_ch,TIOCLBIC,&lflusho))
X#  else
X#define unflush_output()
X#  endif /* LFLUSHO */
X# endif /* I_TERMIOS */
X
X#endif /* I_TERMIO */
X
X#ifdef TIOCSTI
X#ifdef lint
X#define forceme(c) ioctl(_tty_ch,TIOCSTI,Null(long*))	/* ghad! */
X#else
X#define forceme(c) ioctl(_tty_ch,TIOCSTI,c) /* pass character in " " */
X#endif /* lint */
X#else
X#define forceme(c)
X#endif
X
X/* termcap stuff */
X
X/*
X * NOTE: if you don't have termlib you'll either have to define these strings
X *    and the tputs routine, or you'll have to redefine the macros below
X */
X
X#ifdef HAS_TERMLIB
XEXT int GT;				/* hardware tabs */
XEXT char *BC INIT(Nullch);		/* backspace character */
XEXT char *UP INIT(Nullch);		/* move cursor up one line */
XEXT char *CR INIT(Nullch);		/* get to left margin, somehow */
XEXT char *VB INIT(Nullch);		/* visible bell */
XEXT char *CL INIT(Nullch);		/* home and clear screen */
XEXT char *CE INIT(Nullch);		/* clear to end of line */
XEXT char *TI INIT(Nullch);		/* initialize terminal */
XEXT char *TE INIT(Nullch);		/* reset terminal */
XEXT char *CM INIT(Nullch);		/* cursor motion */
XEXT char *HO INIT(Nullch);		/* home cursor */
X#ifdef CLEAREOL
XEXT char *CD INIT(Nullch);		/* clear to end of display */
X#endif /* CLEAREOL */
XEXT char *SO INIT(Nullch);		/* begin standout mode */
XEXT char *SE INIT(Nullch);		/* end standout mode */
XEXT int SG INIT(0);			/* blanks left by SO and SE */
XEXT char *US INIT(Nullch);		/* start underline mode */
XEXT char *UE INIT(Nullch);		/* end underline mode */
XEXT char *UC INIT(Nullch);		/* underline a character,
X						 if that's how it's done */
XEXT int UG INIT(0);			/* blanks left by US and UE */
XEXT bool AM INIT(FALSE);		/* does terminal have automatic
X								 margins? */
XEXT bool XN INIT(FALSE);		/* does it eat 1st newline after
X							 automatic wrap? */
XEXT char PC INIT(0);			/* pad character for use by tputs() */
X
X#ifdef _POSIX_SOURCE
XEXT speed_t outspeed INIT(0);		/* terminal output speed, */
X#else
XEXT long outspeed INIT(0);		/* 	for use by tputs() */
X#endif
X
XEXT int LINES INIT(0), COLS INIT(0);	/* size of screen */
XEXT int just_a_sec INIT(960);		/* 1 sec at current baud rate */
X					/* (number of nulls) */
X
X/* define a few handy macros */
X
X#define backspace() tputs(BC,0,putchr) FLUSH
X#define clear() tputs(CL,LINES,putchr) FLUSH
X#define erase_eol() tputs(CE,1,putchr) FLUSH
X#ifdef CLEAREOL
X#define clear_rest() tputs(CD,LINES,putchr) FLUSH
X#define maybe_eol() if(erase_screen&&can_home_clear)tputs(CE,1,putchr) FLUSH
X#endif /* CLEAREOL */
X#define underline() tputs(US,1,putchr) FLUSH
X#define un_underline() tputs(UE,1,putchr) FLUSH
X#define underchar() tputs(UC,0,putchr) FLUSH
X#define standout() tputs(SO,1,putchr) FLUSH
X#define un_standout() tputs(SE,1,putchr) FLUSH
X#define up_line() tputs(UP,1,putchr) FLUSH
X#define carriage_return() tputs(CR,1,putchr) FLUSH
X#define dingaling() tputs(VB,1,putchr) FLUSH
X#else
X  ????????		/* up to you */
X#endif
X
XEXT int page_line INIT(1);	/* line number for paging in
X						 print_line (origin 1) */
X
Xvoid	term_init _((void));
Xvoid	term_set _((char*));
Xvoid	pushchar _((char_int));
Xvoid	pushstring _((char*,char_int));
Xvoid	mac_init _((char*));
Xvoid	mac_line _((char*,char*,int));
Xvoid	show_macros _((void));
Xchar	putchr _((char_int));	/* routine for tputs to call */
Xbool	finish_command _((int));
Xbool	finish_dblchar _((void));
Xvoid	eat_typeahead _((void));
Xvoid	save_typeahead _((char*, int));
Xvoid	settle_down _((void));
X#ifdef HAS_TERMLIB
Xvoid	termlib_init _((void));
Xvoid	termlib_reset _((void));
X#endif
X#ifndef read_tty
Xint	read_tty _((char*,int));
X#endif
Xvoid	underprint _((char*));
X#ifdef NOFIREWORKS
Xvoid	no_sofire _((void));
Xvoid	no_ulfire _((void));
X#endif
Xvoid	getcmd _((char*));
Xint	get_anything _((void));
Xint	pause_getcmd _((void));
Xvoid	in_char _((char*,char_int));
Xvoid	in_answer _((char*,char_int));
Xint	print_lines _((char*,int));
Xvoid	page_init _((void));
Xvoid	pad _((int));
Xvoid	printcmd _((void));
Xvoid	rubout _((void));
Xvoid	reprint _((void));
Xvoid	home_cursor _((void));
Xvoid	goto_line _((int,int));
X#ifdef SIGWINCH
XSignal_t winch_catcher _((int));
X#endif /* SIGWINCH */
END_OF_FILE
if test 8440 -ne `wc -c <'term.h'`; then
    echo shar: \"'term.h'\" unpacked with wrong size!
fi
# end of 'term.h'
fi
if test -f 'uudecode.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'uudecode.c'\"
else
echo shar: Extracting \"'uudecode.c'\" \(9598 characters\)
sed "s/^X//" >'uudecode.c' <<'END_OF_FILE'
X/* $Id: uudecode.c,v 4.4.3.1 1992/02/01 03:09:32 $
X * 
X * Decode one or more uuencoded articles back to binary form.
X * Trn version created by Wayne Davison.
X * Formerly the nn version by Kim Storm.
X * From the Berkeley original, modified by MSD, RDR, JPHD & WLS.
X */
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 "respond.h"
X#include "decode.h"
X
X#define MAXCHAR 256
X#define NORMLEN 64	/* allows for 84 encoded chars per line */
X
X#define SEQMAX 'z'
X#define SEQMIN 'a'
X
Xstatic char seqc;
Xstatic int first, secnd, check, numl;
X
Xstatic char blank;
Xstatic int chtbl[MAXCHAR], cdlen[NORMLEN + 3];
Xstatic int state;
Xstatic bool Xflag;
Xstatic int expecting_part;
X
Xstatic int decode_line _((char *));
Xstatic void inittbls _((void));
Xstatic void gettable _((FILE *));
X
X#define	NO_ADVANCE		0x10
X
X#define	FIND_BEGIN		0x01
X#define	AFTER_ERROR_FIND_BEGIN	0x02
X#define	DECODE_TEXT		0x03
X#define	SKIP_TRAILING	       (0x04 | NO_ADVANCE)
X#define	SKIP_LEADING		0x05
X#define	FOUND_END	       (0x06 | NO_ADVANCE)
X#define DECODE_ERROR	       (0x07 | NO_ADVANCE)
X#define OTHER_ERROR	       (0x08 | NO_ADVANCE)
X#define NEW_BEGIN	       (0x09 | NO_ADVANCE)
X
Xvoid
Xuud_start()
X{
X    Xflag = FALSE;
X    expecting_part = 0;
X    seqc = SEQMAX;
X    check = 1;
X    first = 1;
X    secnd = 0;
X    state = FIND_BEGIN;
X}
X
Xint
Xuudecode(in)
XFILE *in;
X{
X    int mode, onedone, lens;
X    char buff[LBUFLEN];
X
X    numl = onedone = 0;
X
X    if (state == FIND_BEGIN)
X	inittbls();
X
X    /*
X     * search for header or translation table line.
X     */
X
X    while ((state & NO_ADVANCE) || fgets(buff, sizeof buff, in) != Nullch) {
X	numl++;
X
X	switch (state) {
X	 case NEW_BEGIN:
X	    if (decode_fp != Nullfp) {
X		if (expecting_part) {
X		    register int got_part = 0;
X
X		    if (strnEQ(buff + 6, "part ", 5)) {
X			register char *bp;
X
X			for (bp = buff + 11; islower(*bp); bp++)
X			    got_part = got_part * 26 + *bp - 'a';
X		    }
X		    if (expecting_part == got_part) {
X			state = DECODE_TEXT;
X			break;
X		    }
X		    printf("Expecting part %d; got part %d.\n",
X			 expecting_part + 1, got_part + 1);
X		    if (got_part) {
X			state = SKIP_LEADING;
X			return -1;
X		    }
X		}
X		decode_end();
X		sleep(2);
X		Xflag = FALSE;
X		expecting_part = 0;
X	    }
X	    state = FIND_BEGIN;
X	    /* fall thru */
X
X	 case FIND_BEGIN:
X	 case AFTER_ERROR_FIND_BEGIN:
X	    if (strnEQ(buff, "table", 5)) {
X		gettable(in);
X		continue;
X	    }
X
X	    if (strnEQ(buff, "begin ", 6)
X	     || strnEQ(buff, "Xbegin ", 7)) {
X		lens = strlen(buff)-1;
X		if (buff[lens] == '\n')
X		    buff[lens] = '\0';
X
X		if(sscanf(buff+6,"%o%s", &mode, decode_fname) != 2) {
X		    register char *bp = buff + 6;
X
X		    if (*bp == ' ')
X			bp++;
X		    if (strnEQ(bp, "part ", 5)) {
X			register int got_part = 0;
X
X			for (bp = bp + 5; islower(*bp); bp++)
X			    got_part = got_part * 26 + *bp - 'a';
X			printf("Expecting part 1; got part %d.\n",
X				got_part + 1);
X			return -1;
X		    }
X		    continue;
X		}
X
X		Xflag = (*buff == 'X');
X
X		sprintf(decode_dest, "%s/%s", extractdest, decode_fname);
X
X		if ((decode_fp = fopen(decode_dest, FOPEN_WB)) == Nullfp) {
X		    printf("Cannot create file: %s\n", decode_dest);
X		    goto err;
X		}
X		chmod(decode_dest, mode);
X		printf("Decoding: %s\n", decode_fname);
X		state = DECODE_TEXT;
X	    }
X	    continue;
X
X	 case SKIP_LEADING:
X	    state = decode_line(buff);
X	    continue;
X
X	 case DECODE_TEXT:
X	    state = decode_line(buff);
X	    onedone = 1;
X	    continue;
X
X	 case FOUND_END:
X	    fclose(decode_fp);
X	    decode_fp = Nullfp;
X	    Xflag = FALSE;
X	    expecting_part = 0;
X	    state = FIND_BEGIN;
X	    printf("Done.\n");
X	    continue;
X
X	 case SKIP_TRAILING:
X	    printf("(Continued)\n");
X	    state = SKIP_LEADING;
X	    return 0;
X
X	 case DECODE_ERROR:
X	    state = SKIP_TRAILING;
X	    continue;
X
X	 case OTHER_ERROR:
X	    fclose(decode_fp);
X	    decode_fp = Nullfp;
X	    Xflag = FALSE;
X	    expecting_part = 0;
X	    state = AFTER_ERROR_FIND_BEGIN;
X	    goto err;
X	}
X    }
X
X    if (onedone) {
X	if (state == DECODE_TEXT) {
X	    printf("(Continued)\n");
X	    state = SKIP_LEADING;
X	}
X	return 0;
X    }
X
X    if (state == AFTER_ERROR_FIND_BEGIN)
X	return -1;
X    printf("Couldn't find anything to decode.\n");
X
X err:
X    sleep(2);
X    return -1;
X}
X
X/*
X * decode one line and write it out using decode_fp
X */
X
Xstatic int
Xdecode_line(buff)
Xchar *buff;
X{
X    char outl[LBUFLEN];
X    register char *bp, *ut;
X    register int *trtbl = chtbl;
X    register int n;
X    register int blen;		/* binary length (from decoded file) */
X    register int rlen;		/* calculated input line length */
X    register int len;		/* actual input line length */
X    register int dash;		/* number of '-'s encountered on a line */
X				/* If it's too high, we reject the line */
X
X#   define REJECT(buf,rlen,len) \
X	((*buf == 'M' && len > rlen + 5) \
X	 || (*buf != 'M' && len != rlen && len != rlen+1) \
X	 || (strnEQ(buf, "BEGIN", 5)) \
X	 || (strnEQ(buf, "END", 3)))
X
X    if (Xflag) {
X	if (*buff == 'X')
X	    buff++;
X	else
X	    *buff = 'x';	/* force a mis-parse of a non-x'ed line */
X    }
X    len = strlen(buff);
X    if (--len <= 0)
X	return state;
X
X    buff[len] = '\0';
X
X    /*
X     * Get the binary line length.
X     */
X    if ((blen = trtbl[buff[0]]) < 0) {
X	if (state == SKIP_LEADING) {
X	    if (strnEQ(buff, "begin ", 6))
X		return NEW_BEGIN;
X
X	    return SKIP_LEADING;
X	}
X	/*
X	 * end of uuencoded file ?
X	 */
X	if (strnEQ(buff, "end", 3))
X	    return FOUND_END;
X
X	/*
X	 * end of current file ? : get next one.
X	 */
X	if (strnEQ(buff, "include ", 8)) {
X	    for (bp = buff + 8; *bp; bp++) {
X		if (bp[0] == '.' && bp[1] == 'u') {
X		    expecting_part = (bp[2] - 'a') * 26 + bp[3] - 'a';
X		    break;
X		}
X	    }
X	}
X
X	/*
X	 * trailing garbage
X	 */
X	return SKIP_TRAILING;
X    }
X
X    rlen = cdlen[blen];
X    if (state == SKIP_LEADING && REJECT(buff,rlen,len))
X	return SKIP_LEADING;
X
X    /*
X     * Is it the empty line before the end line ?
X     */
X    if (blen == 0)
X	return state;
X
X    if (REJECT(buff,rlen,len))
X	return SKIP_TRAILING;
X
X    /*
X     * Pad with blanks.
X     */
X    for (bp = buff + len, n = rlen - len; --n >= 0; )
X	*bp++ = blank;
X
X    /*
X     * Verify
X     */
X    for (n = rlen, bp = buff, dash = 0; --n >= 0; bp++) {
X	if (trtbl[*bp] < 0) {
X	    if (state == SKIP_LEADING)
X		return SKIP_LEADING;
X	    return DECODE_ERROR;
X	}
X	if (*bp == '-')
X	    dash++;
X    }
X    if (dash * 100 / rlen > 33)		/* more than 1/3 dashes? */
X	if (state == SKIP_LEADING)
X	    return SKIP_LEADING;	/* -> reject */
X	else
X	    return SKIP_TRAILING;
X
X    /*
X     * Check for uuencodes that append a 'z' to each line....
X     */
X    if (check)
X	if (secnd) {
X	    secnd = 0;
X	    if (buff[rlen] == SEQMAX)
X		check = 0;
X	} else if (first) {
X	    first = 0;
X	    secnd = 1;
X	    if (buff[rlen] != SEQMAX)
X		check = 0;
X	}
X
X    /*
X     * There we check.
X     */
X    if (check) {
X	if (buff[rlen] != seqc) {
X	    if (state == SKIP_LEADING)
X		return SKIP_LEADING;
X	    return DECODE_ERROR;
X	}
X
X	if (--seqc < SEQMIN)
X	    seqc = SEQMAX;
X    }
X
X    /*
X     * output a group of 3 bytes (4 input characters).
X     * the input chars are pointed to by p, they are to
X     * be output to file f. blen is used to tell us not to
X     * output all of them at the end of the file.
X     */
X    ut = outl;
X    n = blen;
X    bp = &buff[1];
X    while (--n >= 0) {
X	*(ut++) = trtbl[*bp] << 2 | trtbl[bp[1]] >> 4;
X	if (n > 0) {
X	    *(ut++) = (trtbl[bp[1]] << 4) | (trtbl[bp[2]] >> 2);
X	    n--;
X	}
X	if (n > 0) {
X	    *(ut++) = trtbl[bp[2]] << 6 | trtbl[bp[3]];
X	    n--;
X	}
X	bp += 4;
X    }
X    if (fwrite(outl, 1, blen, decode_fp) <= 0) {
X	printf("Error on writing decoded file\n");
X	return OTHER_ERROR;
X    }
X
X    return DECODE_TEXT;
X}
X
X
X
X/*
X * Install the table in memory for later use.
X */
Xstatic void
Xinittbls()
X{
X    register int i, j;
X
X    /*
X     * Set up the default translation table.
X     */
X    for (i = 0; i < ' '; i++)
X	chtbl[i] = -1;
X    for (i = ' ', j = 0; i < ' ' + 64; i++, j++)
X	chtbl[i] = j;
X    for (i = ' ' + 64; i < MAXCHAR; i++)
X	chtbl[i] = -1;
X    chtbl['`'] = chtbl[' '];	/* common mutation */
X    chtbl['~'] = chtbl['^'];	/* another common mutation */
X    blank = ' ';
X    /*
X     * set up the line length table, to avoid computing lotsa * and / ...
X     */
X    cdlen[0] = 1;
X    for (i = 1, j = 5; i <= NORMLEN; i += 3, j += 4)
X	cdlen[i] = (cdlen[i + 1] = (cdlen[i + 2] = j));
X}
X
Xstatic void
Xgettable(in)
XFILE *in;
X{
X    char buff[LBUFLEN];
X    register int c, n = 0;
X    register char *cpt;
X
X    for (c = 0; c < MAXCHAR; c++)
X	chtbl[c] = -1;
X
X    for (;;) {
X	if (fgets(buff, sizeof buff, in) == Nullch) {
X	    printf("EOF while in translation table.\n");
X	    return;
X	}
X	numl++;
X	if (strnEQ(buff, "begin", 5)) {
X	    printf("Incomplete translation table.\n");
X	    return;
X	}
X	cpt = buff + strlen(buff) - 1;
X	*cpt = ' ';
X	while (*cpt == ' ') {
X	    *cpt = 0;
X	    cpt--;
X	}
X	cpt = buff;
X	while (c = *cpt) {
X	    if (chtbl[c] != -1) {
X		printf("Duplicate char in translation table.\n");
X		return;
X	    }
X	    if (n == 0)
X		blank = c;
X	    chtbl[c] = n++;
X	    if (n >= 64)
X		return;
X	    cpt++;
X	}
X    }
X}
X
END_OF_FILE
if test 9598 -ne `wc -c <'uudecode.c'`; then
    echo shar: \"'uudecode.c'\" unpacked with wrong size!
fi
# end of 'uudecode.c'
fi
echo shar: End of archive 3 \(of 12\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 12 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
