Vnews part 1 (of 7)

sources-request at genrad.UUCP sources-request at genrad.UUCP
Thu Jan 24 23:01:04 AEST 1985


# From: ka at hou3c
#
# Welcome to release 2.11-B 1/17/85 of vnews.  The -B means that this is
# a prerelease of 2.11 vnews; a release 2.11 of vnews will not be issued
# until a complete release 2.11 of netnews is ready.  Most features pro-
# mised for release 2.11 are here.
# 
# This file is the first of seven files containing the vnews source.  To
# unpack these files, chdir to an empty directory and feed them into the
# shell.  Use the regular shell, not csh.

if test ! -d artfile
then	mkdir artfile
fi

cat > install.doc <<\!E!O!F!

                      HOW TO INSTALL VNEWS



This release is designed to run with release 2.10 or 2.10.1 of
netnews, but should also work with release 2.10.2.  The steps re-
quired to install vnews are:

1)  Modify the transmit routine in inews to support the H flag.
The H flag causes inews to replace a %s in the command with the
contents of the history line.  The directory artfile contains two
versions of the file ifuncs.c.  Ifuncs.c.10.1 is based on release
2.10.1 while ifuncs.c.10.2 is based on release 2.10.2.  In both
files, the changes can be found by grepping for KSA.  Rebuild
inews with the appropriate version of ifuncs.c.

2)  Vnews has its own configuration program called "setup" which
works on a file named config.  Change directory to lib, and run
the mkconfig program.  This program expects as its single argu-
ment the name of the directory containing the netnews source.
You should probably cat the resulting config file to make sure it
looks reasonable.  Then run the setup shell procedure.  Read
setup.doc if you have problems.

3)  If you have terminals with more than 24 lines or more than 80
columns, increase MAXPLEN and MAXLLEN in vnews/virtterm.c.

4)  The next step is to compile and install all the programs.
Changing directory back to .. and running the compile shell pro-
cedure will compile everything.  Compile will complain if you
have distributions which it doesn't have descriptions for.  If
this happens, add the descriptions to the file postnews/distlist
and run compile again.  Compile -i will install all the programs.
It will also add an entry to your sys file to invoke afinsert
when new articles arrive.

5)  Run /usr/lib/news/afbuild to create artfile and groupfile
from the history file.  (Put it in the background; it's slow.)
Since you don't want artfile to grow without bound, you must re-
build it periodically.  Modify crontab to invoke afbuild after it
calls expire.

6)  You may wish to install vnews/vnews.1 and postnews/postnm.1
in /usr/man.  Also, if you run Gosling's or Warren Montgomery's
EMACS, install postnews/gparent.ml or postnews/gparent.wm,
respectively, in some suitable spot.
                                Kenneth Almquist
!E!O!F!

cat > files.doc <<\!E!O!F!
.. Run this file throuhg nroff without any macro packages.
.hy
.de f
.sp
.ti 0
.if \\w'\\$2' \\$1, installed in \\$2
.if !\\w'\\$2' \\$1
.in 8
..
.de e
.in 0
..
This lists the programs and files which are part of vnews.
For each file, the directory where it is installed is given.
.f vnews/vnews /usr/bin
This is the vnews news reading program.
.f postnews/gparent /usr/bin
This simple shell procedure writes writes the body of the article
specified by $A to the standard output.  It is intended for writing
followups which reference the original article.  For example from vi
you can say ":r !gparent" to include the body of the original article
in your followup.  (This only works from vnews because postnews
does not yet set $A.)
.f postnews/postnews /usr/bin
This program allows users to post news items to the network.
This version of postnews is much more sophisticated than the
2.10.1 version of postnews.  It is rather similar to the 2.10.2
version except that it allows you to search for specific titles
or authors if you don't remember the number of an article you are
writng a followup to.
.f postnews/postnm /usr/bin
This program acts as a front end for inews.  To post news you should
use this program instead of invoking inews directly.
.f lib/cgtdate /usr/lib/news
This is program converts a date to UNIX* internal format.
It is invoked by vnews to process the -a option.
.f postnews/postreply /usr/lib/news
Thie program handles replies and followups for vnews.
.f postnews/distributions /usr/lib/news
This file lists all the legal distributions that users can specify
(world, na, usa, local, etc.).
.f postnews/moderators /usr/lib/news
This file lists the moderators of various moderated groups.
.f artfile/afbuild /usr/lib/news
This program rebuilds /usr/lib/news/artfile from the history file.
It also creates /usr/lib/news/groupfile if that file does not
already exist.  (/usr/lib/news/artfile and /usr/lib/news/groupfile
are described below.)
.f artfile/afinsert /usr/lib/news
This program is invoked by an entry in the sys file every time an
article is received.  Its single argument is the contents of the
history line for the article.  This program adds an entry for the
article to /usr/lib/news/artfile.
.f artfile/dumprec
This program writes out records in artfile.  Each record is specified
either by a message-ID or by its offset from the beginning of artfile.
This program is intended for debugging the artfile code.
.f artfile/dumpng
This program lists the contents of a newsgroup.  It is also intended
for debugging the artfile code, but the information may prove useful
for other purposes.
.f /usr/lib/news/artfile
This file contains one entry for each article in the system.
It can be accessed by various routines in lib/rlib.a; see lib/routines.doc
for details.
.f /usr/lib/news/groupfile
This file associates a small, unique integer with each newsgroup
on the system.
.f postnews/gparent.ml
This is a macro for Gosling's EMACS that reads the body of the article
specified by $A into the current buffer.  It is intended for writing
followups which reference the original article.  Normally, this macro
should be bound to ^X^Y.  Compile -i will not install this anywhere,
but if you run Gosling's EMACS you should install it wherever you have
your macro library.
.f postnews/gparent.wm
This file contains a version of the gparent macro rewritten to run
under Warren Montgomery's EMACS.
!E!O!F!

cat > compile <<\!E!O!F!
#!/bin/sh
: This file compiles all of vnews.
: With the -i option, it also installs everything
: The -f option forces a remake of everything.
: The -n option is passed to make.
: Arguments like DEBUG=-g are passed to make.

case "$0" in
../compile)	cd .. ;;
esac

. lib/makedefs

dirs=
install=
frc=
makedefs=
for x 
do	case "$x" in
	-i)	install=install ;;
	-f)	frc="FRC=FRC" ;;
	-n)	makedefs="-n|$makedefs" ;;
	*=*)	makedefs="$makedefs|$x" ;;
	-*)	echo 'Usage: compile [ -i ] [ directories ]'
		exit 1 ;;
	*)	dirs="$dirs $x" ;;
	esac
done
if test "$dirs" = ""
then	dirs=lib
	for x in *
	do	if test "$x" != lib -a -f $x/genmakefile
		then	dirs="$dirs $x"
		fi
	done
fi

cd lib
for x in $dirs
do	echo "Making $x:"
	cd ../$x
	if test "$frc" != "" -o ! -f ../lib/newer || ../lib/newer genmakefile makefile
	then	echo "	genmakefile"
		genmakefile
	fi
	IFS='|'
	echo "	make" $makedefs $frc $install
	make $makedefs $frc $install || exit
	IFS=' 	
'
done

if test "$install" = "" || grep '^artfile:' $LIBDIR/sys > /dev/null
then	:
else	echo adding artfile entry to sys file
	echo "artfile:all:H:$LIBDIR/afinsert \"%s\"" >> $LIBDIR/sys
	echo "Remember to update crontab to invoke $LIBDIR/afbuild every night"
fi
echo Compile all done
!E!O!F!
chmod +x compile

cat > artfile/addart.c <<\!E!O!F!
#include <stdio.h>
#include <sys/types.h>
#include "config.h"
#include "defs.h"
#include "arthead.h"
#include "af.h"


#define equal(s1, s2)	(strcmp(s1, s2) == 0)
char *rindex(), *savestr() ;
char *getaddr() ;
time_t cgtdate() ;
DPTR addrec() ;

extern int newgroup ;


/*
 * Add an article to the data base.
 * Glitches:
 *	Ignore the message id <>, since it is clearly wrong (rn bug).
 *	Ignore message ids without a trailing '>' since probably truncated.
 */

addart(h, fname, groups)
      struct arthead *h ;
      char *fname ;
      char *groups ;
      {
      struct artrec a ;
      char buf[128] ;
      char *parlist[32], **pp ;
      struct artgroup *gp ;
      register char *p, *q ;

      h->h_subtime = cgtdate(h->h_subdate) ;
      h->h_rectime = cgtdate(h->h_recdate) ;
      if (hset(h->h_expdate))
            h->h_exptime = cgtdate(h->h_expdate) ;
      else
            h->h_exptime = 0L ;

      p = groups; gp = a.a_group ;
      while (*p && gp < &a.a_group[MAXNG]) {
            q = buf ;
            while (*p == ' ')
                  p++ ;
            while (*p != '/') {
                  if (*p == '\0')
                        xerror("bad newsgroup list") ;
                  *q++ = *p++ ;
            }
            *q = '\0' ;
            if ((gp->a_ngnum = ngnum(buf)) <= 0) {
                  gp->a_ngnum = newng(buf) ;
                  newgroup++ ;
            }
            p++ ;
            gp->a_artno = atoi(p) ;
            while (*p != ' ' && *p)
                  p++ ;
            gp++ ;
      }
      a.a_ngroups = gp - a.a_group ;

      pp = parlist + 31 ; parlist[31] = NULL ;
      p = h->h_references ;
      while (p && *p) {
            if (pp <= parlist)
                  xerror("Too many references") ;
            q = buf ;
            while (*p && *p != ',' && *p != ' ')
                  *q++ = *p++ ;
            *q = '\0' ;
            tomsgid(buf) ;
            if (! equal(buf, "<>"))
                  *--pp = savestr(buf) ;
            while (*p == ' ' || *p == ',')
                  p++ ;
      }
      while (*pp && index(*pp, '>') == 0)
            pp++ ;
      if (strlen(h->h_title) > 127)
            h->h_title[127] = '\0' ;
      getaddr(h->h_from, buf) ;

      a.a_subtime = h->h_subtime ;
      a.a_rectime = h->h_rectime ;
      a.a_exptime = h->h_exptime ;
      a.a_parent = DNULL ;
      a.a_children = DNULL ;
      a.a_childchain = DNULL ;
      a.a_nlines = h->h_intnumlines ;
      a.a_flags = 0 ;
      a.a_ident = h->h_ident ;
      a.a_title = h->h_title ;
      a.a_from = buf ;
      a.a_file = fname ;
      a.a_nkwords = 0 ;		/* fix this eventually */
      addrec(&a, pp) ;

      while (*pp)
            free(*pp++) ;
}
!E!O!F!

cat > artfile/addrec.c <<\!E!O!F!
#include <stdio.h>
#include "af.h"


static int nchildren ;			/* number of orphaned children */
static DPTR child[200] ;		/* list of orphans */

long lseek() ;


/*
 * Add a record to the data base.
 */

DPTR
addrec(a, parlist)
      struct artrec *a ;
      char **parlist ;
      {
      DPTR pos ;
      DPTR idhash, thash ;
      DPTR ngptr[MAXNG] ;
      DPTR dp ;
      int i, j ;
      static char nuls[60] ;
      DPTR htitle();

      dorelatives(a, parlist) ;
      idhash = hashid(a->a_ident) ;
      a->a_idchain = readptr(idhash) ;
      if ((a->a_flags & A_DUMMY) == 0) {
            thash = htitle(a->a_title) ;
            a->a_titlechain = readptr(thash) ;
      }
      for (i = 0 ; i < a->a_ngroups ; i++) {
            struct artrec a2 ;
            int ng = a->a_group[i].a_ngnum ;

            /* insert record in proper place in chain */
            dp = readptr(ngptr[i] = ngchain(ng)) ;
            while (dp != DNULL) {
                  readrec(dp, &a2) ;
                  for (j = 0 ; ; j++) {
                        if (j >= a2.a_ngroups)
                              xerror("bad newsgroup chain for %d", ng) ;
                        if (a2.a_group[j].a_ngnum == ng)
                              break ;
                  }
                  if (a2.a_group[j].a_artno <= a->a_group[i].a_artno)
                        break ;
                  ngptr[i] = dp + GROUPOFF + stroff(a_ngchain, artgroup) + j * sizeof(struct artgroup) ;
                  dp = a2.a_group[j].a_ngchain ;
            }
            a->a_group[i].a_ngchain = dp ;
      }
      pos = afhd.af_free ;
      lseek(affd, (long)pos, 0) ;
      writerec(a, affd) ;
      afhd.af_free = lseek(affd, 0L, 1) ;
      write(affd, nuls, 60) ;		/* readrec tends to overrun */
      lseek(affd, 0L, 0) ;
      write(affd, (char *)&afhd, sizeof afhd) ;
      writeptr(pos, idhash) ;
      if ((a->a_flags & A_DUMMY) == 0)
            writeptr(pos, thash) ;
      if (a->a_parent)
            writeptr(pos, a->a_parent + CHILDRENOFF) ;
      for (i = 0 ; i < nchildren ; i++) {
            writeptr(pos, child[i] + PARENTOFF) ;
      }
      for (i = 0 ; i < a->a_ngroups ; i++) {
            writeptr(pos, ngptr[i]) ;
      }
      return pos ;
}



/*
 * Find the parents and children of the article.
 * If the references line is present,
 *    create a dummy entry for parent if necessary.
 * If no references line, guess at parent using title.
 * Look for articles without references lines whose titles indicate that
 *    this article might be the parent.
 */

dorelatives(a, parlist)
      register struct artrec *a ;
      char **parlist ;
      {
      struct artrec a2 ;
      DPTR dp, dp2, dummy ;
      int i ;

      /* first look for parent */
      if (parlist && parlist[0]) {
            if ((a->a_parent = lookart(parlist[0], &a2)) == DNULL) {
                  /* make dummy parent */
                  bzero((char *) &a2, sizeof a2) ;
                  a2.a_ident = parlist[0] ;
                  a2.a_flags = A_DUMMY ;
                  a2.a_rectime = a->a_rectime;
                  a2.a_subtime = a->a_subtime;
                  a->a_parent = addrec(&a2, parlist + 1) ;
            }
      } else if (a->a_title && isre(a->a_title)) {
            for (dp = readptr(htitle(a->a_title)) ; dp != NULL ; dp = a2.a_titlechain) {
                  readrec(dp, &a2) ;
                  if (tparent(&a2, a)) {
                        a->a_parent = dp ;
                        break ;
                  }
            }
      }
      /* check for loops in parent relation */
      if ((dummy = lookart(a->a_ident, &a2)) != DNULL) {
            /* we are replacing a dummy entry with a real one */
            i = 100 ;
            for (dp2 = a->a_parent ; dp2 != DNULL ; dp2 = a2.a_parent) {
                  readrec(dp2, &a2) ;
                  if (dp2 == dummy) {
                        a->a_parent = a2.a_parent ;
                        printf("loop in parent chain for %s found\n", a->a_ident) ;
                        break ;
                  }
                  if (--i < 0) {
                        printf("loop containing %s discovered while updating %s\n",
                              a2.a_ident, a->a_ident) ;
                        break ;
                  }
            }
      }

      /* now look for orphans */
      nchildren = 0 ;
      if (dummy != DNULL) {
            /* we are replacing a dummy entry with a real one */
            readrec(dummy, &a2) ;
            if (a2.a_parent != DNULL)
                  rmchain(a2.a_parent + CHILDRENOFF, CHILDCHOFF, dummy) ;
            for (dp = a2.a_children ; dp != DNULL ; dp = a2.a_childchain) {
                  child[nchildren++] = dp ;
                  readrec(dp, &a2) ;
            }
            writeptr(DNULL, dp + CHILDRENOFF) ;
      }
      if (a->a_parent != DNULL) {
            readrec(a->a_parent, &a2);
            a->a_childchain = a2.a_children ;
      } else
            a->a_childchain = DNULL ;
      if ((a->a_flags & A_DUMMY) == 0)
         for (dp = readptr(htitle(a->a_title)) ; dp != DNULL ; dp = a2.a_titlechain) {
            readrec(dp, &a2) ;
            if (a2.a_parent == DNULL && tparent(a, &a2))
                  child[nchildren++] = dp ;
      }

      /* link the orhpans together into the child chain */
      child[nchildren] = DNULL ;
      for (i = 0 ; i < nchildren ; i++) {
            DPTR pardp = readptr(child[i] + PARENTOFF) ;	/*DEBUG*/
            if (pardp != DNULL && pardp != dummy)
                  printf("bad orphan %ld: par %ld, dummy %ld\n", child[i], pardp, dummy) ;
            writeptr(child[i + 1], child[i] + CHILDCHOFF) ;
      }
      a->a_children = child[0] ;
}



/* remove an entry from a linked list */

rmchain(chain, offset, rec)
      DPTR chain ;
      DPTR rec ;
      {
      DPTR dp ;
      int nremoved = 0 ;	/*DEBUG*/
      DPTR svchain = chain ;	/*DEBUG*/

      dp = readptr(chain) ;
      while (dp != DNULL) {
            if (dp == rec) {
                  writeptr(readptr(dp + offset), chain) ;
                  nremoved++;
            }
            chain = dp + offset ;
            dp = readptr(chain) ;
      }
      if (nremoved == 0)
            printf("rmchain(%ld, %d, %ld) failed\n", svchain, offset, rec) ;
}
!E!O!F!

cat > artfile/afbuild.c <<\!E!O!F!
#include <stdio.h>
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include "newsdefs.h"
#include "af.h"


char *index() ;
FILE *ckfopen() ;

static int idsize = 10007 ;	/* default size of message ID hash table */
static int grpsize =  512 ;	/* default maximum newsgroup number */
static int ttlsize = 5003 ;	/* default size of title hash table */

char progname[] = "afbuild" ;
int newgroup ;


main(argc, argv)
      char **argv ;
      {
      int c ;
      char curname[64], newname[64] ;
      char buf[256] ;
      char histname[64] ;
      FILE *histfp ;
      extern char LIB[], SPOOL[] ;
      extern char *optarg ;

      setbuf(stdout, NULL) ;
#if BSDREL >= 41
      setbuf(stderr, NULL) ;
#endif
      pathinit() ;
      sprintf(histname, "%s/history", LIB) ;
      while ((c = getopt(argc, argv, "h:m:g:t:")) != EOF) switch (c) {
            case 'h':
                  scopyn(optarg, histname, 64) ;
                  break ;
            case 'm':
                  idsize = numarg(c) ;
                  break ;
            case 'g':
                  grpsize = numarg(c) ;
                  break ;
            case 't':
                  ttlsize = numarg(c) ;
                  break ;
      }
      sprintf(curname, "%s/artfile", LIB) ;
      sprintf(newname, "%s.tmp", curname) ;
      unlink(newname) ;
      makeaf(newname, idsize, grpsize, ttlsize) ;
      if (strcmp(histname, "none") != 0) {
            readngtab() ;
            genafopen(newname, "r+") ;
            if ((histfp = fopen(histname, "r")) == NULL)
                  xerror("Can't open history file") ;
            if (chdir(SPOOL) < 0)
                  xerror("Can't chdir to %s", SPOOL) ;
            while (fgets(buf, 256, histfp) != NULL)
                  doadd(buf, 0) ;
            if (newgroup)
                  writengfile() ;
      }
      aflock() ;
      sleep(10) ;		/* let the dust settle */
      while (fgets(buf, 256, histfp) != NULL)
            doadd(buf, 0) ;
      close(affd) ;
      if (rename(newname, curname) < 0)
            xerror("rename failed") ;
      afunlock() ;
      sync() ;
      exit(0) ;
}



numarg(c) {
      extern char *optarg ;
      register char *p ;

      for (p = optarg ; *p ; p++)
            if (*p < '0' || *p > '9')
                  xerror("Illegal number after -%c option", c) ;
      return atoi(optarg) ;
}



readngtab() {
      char file[FPATHLEN] ;
      FILE *fp ;
      struct stat statb ;
      char line[512] ;
      register char *p ;
      extern char LIB[] ;

      sprintf(file, "%s/groupfile", LIB) ;
      if (stat(file, &statb) >= 0 && statb.st_size > 0) {
            getngtab() ;
      } else {
            fprintf(stderr, "afbuild: reconstructing groupfile.\n") ;
            ngtinit() ;
            sprintf(file, "%s/active", LIB) ;
            fp = ckfopen(file, "r") ;
            while (fgets(line, 512, fp) != NULL) {
                  if (line[0] == '\n')
                        continue ;
                  if ((p = index(line, ' ')) != NULL)
                        *p = '\0' ;
                  if ((p = index(line, '\n')) != NULL)
                        *p = '\0' ;
                  newng(line) ;
                  newgroup++ ;
            }
      }
}



xerror(msg, a1, a2, a3, a4)
      char *msg ;
      {
      extern int errno ;
      int e = errno ;

      fputs("afbuild: ", stderr) ;
      fprintf(stderr, msg, a1, a2, a3, a4) ;
      putc('\n', stderr) ;
      fprintf(stderr, "errno=%d\n", e) ;
      fflush(stderr) ;		/* 4.2 BSD buffers stderr! */
      exit(2) ;
}
!E!O!F!

cat > artfile/afinsert.c <<\!E!O!F!
/*
 * This program inserts entries into the artfile.
 */

#include <stdio.h>
#include "af.h"
#include <sys/types.h>
#include "defs.h"

int newgroup ;
char progname[] = "afinsert" ;
extern char LIB[] ;
extern char SPOOL[] ;


main(argc, argv)
      char **argv ;
      {
      int c ;
      char afname[64] ;

      setbuf(stdout, NULL) ;
#if BSDREL >= 41
      setbuf(stderr, NULL) ;
#endif
      if (argc != 2)
            xerror("Usage: afinsert hist-line") ;
      sprintf(afname, "%s/artfile", LIB) ;
      genafopen(afname, "r+") ;
      getngtab() ;
      if (chdir(SPOOL) < 0)
            xerror("Can't chdir to spool") ;
      doadd(argv[1], 1) ;
      if (newgroup)
            writengfile() ;
      exit(0) ;
}



xerror(msg, a1, a2, a3, a4)
      char *msg ;
      {
      printf(msg, a1, a2, a3, a4) ;
      putchar('\n') ;
      abort() ;
}
!E!O!F!

cat > artfile/doadd.c <<\!E!O!F!
#include <stdio.h>
#include <sys/types.h>
#include "config.h"
#include "defs.h"
#include "arthead.h"


char *index() ;


doadd(hist, lockit)
      char *hist ;
      {
      FILE *artfp ;
      char fname[64] ;
      struct arthead h ;
      register char *p ;
      extern char progname[] ;

      bzero((char *)&h, sizeof h) ;
      if ((p = index(hist, '\t')) == NULL) {
bad:        printf("Bad history line") ;
            return ;
      }
      p++ ;
      if ((p = index(p, '\t')) == NULL)
            goto bad ;
      p++ ;
      nstrip(p) ;
      hist = p ;
      scopyn(p, fname, 64) ;
      if ((p = index(fname, ' ')) != NULL)
            *p = '\0' ;
      for (p = fname ; *p ; p++)
            if (*p == '.')
                  *p = '/' ;
      if ((artfp = fopen(fname, "r")) == NULL) {
            printf("%s: can't open %s\n", progname, fname) ;
            return ;
      }
      if (gethead(&h, artfp) == NULL) {
            printf("%s: bad header\n", fname) ;
            return ;
      }
      h.h_intnumlines = countlines(artfp) ;
      if (lockit)  aflock() ;
      addart(&h, fname, hist) ;
      if (lockit)  afunlock() ;
      fclose(artfp) ;
      hfree(&h) ;
}



countlines(fp)
      register FILE *fp ;
      {
      register c ;
      int curline, nlines ;

      curline = 1 ;
      nlines = 0 ;
      while ((c = getc(fp)) != EOF) {
            if (c == '\n')
                  curline++ ;
            else
                  nlines = curline ;
      }
      return nlines ;
}
!E!O!F!

cat > artfile/dumpng.c <<\!E!O!F!
#include <stdio.h>
#include "af.h"
#include "ng.h"
#include <setjmp.h>

struct jump {
      jmp_buf jbuf ;
      struct jump *nextj ;
} *jmp ;



main(argc, argv)
      char **argv ;
      {
      char **ap = argv + 1 ;
      char *gname ;
      int gnum ;
      struct artrec a ;
      DPTR dp ;
      char artfile[64] ;
      struct jump j ;
      extern int LIB[] ;
      long atol() ;

      jmp = &j ;
      if (setjmp(j.jbuf)) {
            exit(2) ;
      }
      if (argc < 2)
            xerror("Usage: dumpng -fartfile -n newsgroup...") ;
      pathinit() ;
      sprintf(artfile, "%s/artfile", LIB) ;
      if (strncmp(*ap, "-f", 2) == 0) {
            if (ap[0][2])     strcpy(artfile, *ap + 2) ;
            else              strcpy(artfile, *++ap) ;
            ap++ ;
      }
      genafopen(artfile, "r") ;
      gfopen() ;

      while ((gname = *ap++) != NULL) {
            if ((gnum = prtngheader(gname)) >= 0) {
                  prtngchain(gname, gnum) ;
            }
      }
      exit(0) ;
}



prtngheader(gname)
      char *gname ;
      {
      struct ngrec g ;

      ALL_GROUPS(g) {
            if (strcmp(g.g_name, gname) == 0)
                  goto found ;
      }
      printf("\nUnknown newsgroup %s\n", gname) ;
      return -1 ;

found:
      printf("\nNewsgroup %s (%d)", g.g_name, g.g_num) ;
      if (g.g_flags & G_MOD)
            printf("  moderated") ;
      printf("\n\n") ;
      return g.g_num ;
}



prtngchain(gname, gnum)
      char *gname ;
      {
      DPTR dp ;
      struct artrec a ;
      ARTNO an ;
      int i ;

      BKWD_GROUP(gnum, an, dp, a) {
            for (i = 0 ; ; i++) {
                  if (i >= a.a_ngroups) {
                        printf("???  ") ;
                        break ;
                  } else if (a.a_group[i].a_ngnum == gnum) {
                        printf("%-5d", a.a_group[i].a_artno) ;
                        break ;
                  }
            }
            printf("%-22.22s %-30.30s %.20s\n",
                   a.a_ident, a.a_title, a.a_from) ;
      }
}




xerror(msg, a1, a2, a3, a4)
      char *msg ;
      {
      putchar('\n') ;
      printf(msg, a1, a2, a3, a4) ;
      putchar('\n') ;
      longjmp(jmp->jbuf, 1) ;
}
!E!O!F!

cat > artfile/dumprec.c <<\!E!O!F!
#include <stdio.h>
#include "af.h"
#include <setjmp.h>

struct jump {
      jmp_buf jbuf ;
      struct jump *nextj ;
} *jmp ;



main(argc, argv)
      char **argv ;
      {
      char **ap = argv + 1 ;
      char *msgid ;
      struct artrec a ;
      DPTR dp ;
      char artfile[64] ;
      struct jump j ;
      extern int LIB[] ;
      long atol() ;

      jmp = &j ;
      if (setjmp(j.jbuf)) {
            exit(2) ;
      }
      if (argc < 2)
            xerror("Usage: dumprec -fartfile message-id...") ;
      pathinit() ;
      sprintf(artfile, "%s/artfile", LIB) ;
      if (strncmp(*ap, "-f", 2) == 0) {
            if (ap[0][2])     strcpy(artfile, *ap + 2) ;
            else              strcpy(artfile, *++ap) ;
            ap++ ;
      }
      genafopen(artfile, "r") ;
      setjmp(j.jbuf) ;
      while ((msgid = *ap++) != NULL) {
            if (msgid[0] >= '0' && msgid[0] <= '9')
                  readrec(dp = atol(msgid), &a) ;
            else if ((dp = lookart(msgid, &a)) == DNULL) {
                  printf("\nNo entry for %s\n", msgid) ;
                  continue ;
            }
            prtentry(&a, dp, msgid) ;
      }
      exit(0) ;
}



prtentry(a, dp, msgid)
      register struct artrec *a ;
      DPTR dp ;
      char *msgid ;
      {
      char *ctime() ;

      printf("\nArticle: %ld %s\n", dp, a->a_ident) ;
      if ((a->a_flags & A_DUMMY) == 0) {
            printf("Subject: %s\n", a->a_title) ;
            printf("From: %s\n", a->a_from) ;
            printf("File: %s\n", a->a_file) ;
      }
      printf("Posted: %s", ctime(&a->a_subtime)) ;
      printf("Received: %s", ctime(&a->a_rectime)) ;
      if (a->a_exptime)
            printf("Expires: %s", ctime(&a->a_exptime)) ;
      printf("Lines: %d\n", a->a_nlines) ;
      printf("Flags: %05o\n", a->a_flags) ;
      prtgroups(a) ;
      prtptr("Parent: ", a->a_parent) ;
      prtchildren(a->a_children) ;
      prtptr("Childchain: ", a->a_childchain) ;
      prtptr("Idchain: ", a->a_idchain) ;
      prtptr("Titlechain: ", a->a_titlechain) ;
}


prtchildren(dp)
      DPTR dp ;
      {
      struct artrec a ;
      char *msg ;

      msg = "Children: ";
      while (dp != DNULL) {
            prtptr(msg, dp) ;
            readrec(dp, &a) ;
            dp = a.a_childchain ;
            msg = "\t  " ;
      }
}


prtgroups(a)
      struct artrec *a ;
      {
      register int i ;
      char *msg = "Newsgroups: ";

      for (i = 0 ; i < a->a_ngroups ; i++) {
            printf("%s%d/%d", msg, a->a_group[i].a_ngnum, a->a_group[i].a_artno);
            prtptr(" ", a->a_group[i].a_ngchain) ;
            msg = "\t    ";
      }
}


prtptr(msg, dp)
      char *msg ;
      DPTR dp ;
      {
      struct artrec a ;

      printf("%s", msg) ;
      if (dp == DNULL)
            printf("null") ;
      else {
            printf("%ld ", dp) ;
            readrec(dp, &a) ;
            printf("%s", a.a_ident) ;
      }
      putchar('\n') ;
}



xerror(msg, a1, a2, a3, a4)
      char *msg ;
      {
      putchar('\n') ;
      printf(msg, a1, a2, a3, a4) ;
      putchar('\n') ;
      longjmp(jmp->jbuf, 1) ;
}
!E!O!F!

cat > artfile/dumptree.c <<\!E!O!F!
#include <stdio.h>
#include "artfile.h"
#include "libextern.h"

char *savestr();

char *seen[2000];
char **seenp = seen;



main(argc, argv)
      char **argv;
      {
      char artfile[64] ;
      struct artrec a;
      DPTR dp;
      int i;
      int rootflg = 0;
      struct article *ap;
      char *msgid;
      struct article *rootid;
      char *title;
      extern int optind;
      extern char *optarg;

      pathinit();
      sprintf(artfile, "%s/artfile", LIB) ;
      while ((i = getopt(argc, argv, "f:r")) != EOF) {
            switch (i) {
            case 'f':
                  strcpy(artfile, optarg);
                  break;
            case 'r':
                  rootflg++;
                  break;
            }
      }
      if (optind >= argc)
            xerror("Usage: prttree message-id\n");
      msgid = argv[optind];
      genafopen(artfile, "r") ;
      if ((dp = lookart(msgid, &a)) == DNULL)
            xerror("No entry for %s", msgid);
      rootid = msgid;
      if ((a.a_flags & A_DUMMY) == 0)
            title = savestr(a.a_title);
      else
            title = "";
      if (rootflg) {
            seenp = seen;
            while (dp != DNULL) {
                  readrec(dp, &a);
                  rootid = savestr(a.a_ident);
                  if (lookid(rootid)) {
                        printf("loop discovered while searching for parent:\n");
                        printf("	%ld %s\n", dp, rootid);
                        do {
                              readrec(dp = a.a_parent, &a);
                              printf("	%ld %s\n", dp, a.a_ident);
                        } while (strcmp(rootid, a.a_ident) != 0);
                        break;
                  }
                  *seenp++ = rootid;
                  dp = a.a_parent;
            }
      }
      if ((dp = lookart(rootid, &a)) == DNULL)
            xerror("rootid");
      if ((a.a_flags & A_DUMMY) == 0)
            title = a.a_title;
      seenp = seen;
      printf("Discussion tree for %s\n", title);
      prttree(dp, &a, 0, DNULL);
      exit(0);
}


prttree(dp, a, indent, parent)
      DPTR dp;
      struct artrec *a;
      int indent;
      DPTR parent;
      {
      int i;
      char *p;
      DPTR dp2;
      struct artrec a2;
      struct article *ap;

      i = indent;
      while (i >= 8) {
            putchar('\t');
            i -= 8;
      }
      while (i >= 1) {
            putchar(' ');
            i--;
      }
      indent += 3;
      printf("%ld %s", dp, a->a_ident);
      p = savestr(a->a_ident);
      if (a->a_parent != parent) {
            printf(" (parent=%ld!)", a->a_parent);
      }

      if (lookid(p)) {
            printf(" (duplicate)\n");
            return;
      }
      *seenp++ = p;
      putchar('\n');
      for (dp2 = a->a_children ; dp2 != DNULL ; dp2 = a2.a_childchain) {
            readrec(dp2, &a2);
            prttree(dp2, &a2, indent, dp);
      }
}



xerror(fmt, a1, a2, a3, a4)
      char *fmt;
      {
      fprintf(stderr, fmt, a1, a2, a3, a4);
      putc('\n', stderr);
      exit(1);
}



lookid(id)
      register char *id;
      {
      register char **p;

      for (p = seen ; p < seenp ; p++)
            if (**p == *id && strcmp(*p, id) == 0)
                  return 1;
      return 0;
}
!E!O!F!

cat > artfile/genmakefile <<\!E!O!F!
LIB=../lib
. $LIB/makedefs
exec > makefile
cat <<!
# makefile 6/30/84

LIB = $LIB
!
cat $LIB/makedefs
cat <<\!

DEBUG = -O
CFLAGS = $(DEBUG) -I$(LIB)
LFLAGS = $(DEBUG)

AFBUILD = afbuild.o doadd.o addart.o addrec.o makeaf.o ngtab.o title.o $(LIB)/rpathinit.o $(LIB)/rlib.a

AFINSERT = afinsert.o doadd.o addart.o addrec.o ngtab.o title.o $(LIB)/rpathinit.o $(LIB)/rlib.a

DUMPREC = dumprec.o $(LIB)/rpathinit.o $(LIB)/rlib.a

DUMPNG = dumpng.o $(LIB)/rpathinit.o $(LIB)/rlib.a

DUMPTREE = dumptree.o $(LIB)/rpathinit.o $(LIB)/rlib.a

all: makefile afbuild afinsert

makefile: genmakefile
	genmakefile
	@echo 'Makefile changes, so restart makefile.'
	@sh -c 'exit 22'

install: all
	/bin/cp afbuild afinsert $(LIBDIR)

afbuild: $(AFBUILD)
	$(CC) -o $@ $(LFLAGS) $(AFBUILD)

afinsert: $(AFINSERT)
	$(CC) -o $@ $(LFLAGS) $(AFINSERT)

dumprec: $(DUMPREC)
	$(CC) -o $@ $(LFLAGS) $(DUMPREC)

dumpng: $(DUMPNG)
	$(CC) -o $@ $(LFLAGS) $(DUMPNG)

dumptree: $(DUMPTREE)
	$(CC) -o $@ $(LFLAGS) $(DUMPTREE)

addart.o addrec.o afbuild.o afinsert.o dumpng.o dumprec.o \
dumpng.o makeaf.o title.o write.o: $(LIB)/artfile.h $(LIB)/af.h $(FRC)

doadd.o ngtab.o dumprec.o dumpng.o: $(FRC)


FRC:
!
!E!O!F!
chmod +x artfile/genmakefile

cat > artfile/ifuncs.c.10.1 <<\!E!O!F!
/*
 * ifuncs - functions used by inews.
 */

static char *SccsId = "@(#)ifuncs.c	2.21	3/31/83";

#include "iparams.h"

static char histline[256];	/* Assumed initially zero (moved here 6/18/84 KSA) */

/*
 * Transmit this article to all interested systems.
 */

#ifdef u370
static struct srec srec;
static struct hbuf h;
#endif

broadcast()
{
	register char *nptr, *hptr;
	register FILE *fp;
#ifndef u370
	struct srec srec;
	struct hbuf h;
#endif

	/* h is a local copy of the header we can scribble on */
	fp = xfopen(ARTICLE, "r");
	if (hread(&h, fp, TRUE) == NULL)
		xerror("Cannot reread article");
	fclose(fp);
	if (h.distribution[0])
		strcpy(h.nbuf, h.distribution);
	ngcat(h.nbuf);

	 /* break path into list of systems. */
	hptr = nptr = h.path;
	while (*hptr != '\0') {
		if (index(NETCHRS, *hptr)) {
			*hptr++ = '\0';
			nptr = hptr;
		} else
			hptr++;
	}
	*nptr = '\0';

	/* loop once per system. */
	lock();
	s_openr();
	while (s_read(&srec)) {
		if (strncmp(srec.s_name, FULLSYSNAME, SNLN) == 0)
			continue;
		hptr = h.path;
		while (*hptr != '\0') {
			if (strncmp(srec.s_name, hptr, SNLN) == 0)
				goto contin;
			while (*hptr++ != '\0')
				;
		}
		if (ngmatch(h.nbuf, srec.s_nbuf)) {
			transmit(&srec, xfopen(ARTICLE, "r"), 1);
		}
	contin:;
	}
	s_close();
	unlock();
}

/*
 * Transmit file to system.
 */
#define PROC 0004
transmit(sp, ifp, maynotify)
register struct srec *sp;
register FILE *ifp;
int maynotify;
{
	register FILE *ofp;
	register int c;
	register char *ptr;
	struct hbuf hh;
	char TRANS[BUFLEN];
	char *argv[20];
	register int pid, fd;
	extern char firstbufname[];

/* A:	afmt: the other machine runs an A news, so we xmit in A format */
	int afmt = (index(sp->s_flags, 'A') != NULL);
/* B:	use B format (this is the default - don't use this letter elsewise). */
/* F:	append name to file */
	int appfile = (index(sp->s_flags, 'F') != NULL);
/* H:	history: expand %s into the history line (KSA) */
	int history = (index(sp->s_flags, 'H') != NULL);  /* (6/18/84 KSA) */
/* L:	local: don't send the article unless it was generated locally */
	int local = (index(sp->s_flags, 'L') != NULL);
/* N:	notify: don't send the article, just tell him we have it */
	int notify = maynotify && (index(sp->s_flags, 'N') != NULL);
/* S:	noshell: don't fork a shell to execute the xmit command */
	int noshell = (index(sp->s_flags, 'S') != NULL);
/* U:	useexist: use the -c option to uux to use the existing copy */
	int useexist = (index(sp->s_flags, 'U') != NULL);

	if (local && mode == PROC)
		return;
#ifdef DEBUG
	printf("Transmitting to '%s'\n", sp->s_name);
#endif
	if (!appfile && !useexist && !history) {	/* (6/18/84 KSA) */
		if (hread(&hh, ifp, TRUE) == NULL) {
			fprintf(stderr, "Bad header, not transmitting\n");
			log("Bad header, not transmitting %s re %s to %s",
				hh.ident, hh.title, sp->s_name);
			return;
		}
		/* Taken out for obscure reasons - see the standard.
		ngsquash(hh.nbuf, sp->s_nbuf);
		*/
		if (hh.nbuf[0] == '\0') {
			printf("Article not subscribed to by %s\n", sp->s_name);
			return;
		}
		sprintf(TRANS, "%s/trXXXXXX", SPOOL);
	}

	if (notify) {
		char oldid[50];
		sprintf(hh.title, "ihave %s %s", hh.ident, FULLSYSNAME);
		sprintf(hh.nbuf, "to.%s.ctl", sp->s_name);
		strcpy(oldid, hh.ident);
		getident(&hh);
		log("tell %s about %s, notif. id %s",
			sp->s_name, oldid, hh.ident);
	} else
		log("xmit article %s to %s",
			hh.ident, sp->s_name);

	if (appfile) {
		if (firstbufname[0] == '\0')
			xerror("No file name to xmit from");
		ofp = fopen(sp->s_xmit, "a");
		if (ofp == NULL)
			xerror("Cannot append to %s", sp->s_xmit);
		fprintf(ofp, "%s\n", firstbufname);
		fclose(ofp);
		return;
	}
	else
#ifdef UXMIT
	if (useexist) {
		if (firstbufname[0] == '\0')
			xerror("No file name to xmit from");
		if (*sp->s_xmit == '\0')
			sprintf(bfr, UXMIT, sp->s_name, firstbufname);
		else
			sprintf(bfr, sp->s_xmit, firstbufname);
	} else
#endif
	if (history) {		/* (6/18/84 KSA) */
		if (*sp->s_xmit == '\0')
			xerror("no xmit command with H flag");
		sprintf(bfr, sp->s_xmit, histline);
	} else
	{
		ofp = xfopen(mktemp(TRANS), "w");
		if (afmt)
			ohwrite(&hh, ofp);
		else 
			hwrite(&hh, ofp);
		if (!notify)
			while ((c = getc(ifp)) != EOF)
				putc(c, ofp);
		fclose(ifp);
		fclose(ofp);
		if (*sp->s_xmit == '\0')
			sprintf(bfr, DFTXMIT, sp->s_name, TRANS);
		else
			sprintf(bfr, "(%s) < %s", sp->s_xmit, TRANS);
	}

	/* At this point, the command to be executed is in bfr. */
	if (noshell) {
		if (pid = fork())
			fwait(pid);
		else {
			close(0);
			open(TRANS, 0);
			ptr = sp->s_xmit;
			for (pid = 0; pid < 19; pid++) {
				while (isspace(*ptr))
					*ptr++ = 0;
				argv[pid] = ptr;
				while (!isspace(*++ptr) && *ptr)
					;
				if (!*ptr)
					break;
			}
			argv[++pid] = 0;
			execv(sp->s_xmit, argv);
			xerror("Can't execv\n");
		}
	} else
		system(bfr);
	if (!appfile && !useexist && !history)	/* 6/18/84 KSA) */
		unlink(TRANS);
}

typedef struct {
	char *dptr;
	int dsize;
} datum;

/*
 * Return TRUE if we have seen this file before, else FALSE.
 */
history(hp)
struct hbuf *hp;
{
	register FILE *hfp;
	register char *p;
	datum lhs, rhs;
	datum fetch();

#ifdef DEBUG
	fprintf(stderr,"history(%s)\n", hp->ident);
#endif
	idlock(hp->ident);
#ifdef DBM
	dbminit(ARTFILE);
	lhs.dptr = hp->ident;
	lhs.dsize = strlen(lhs.dptr) + 1;
	rhs = fetch(lhs);
	if (rhs.dptr)
		return(TRUE);
#else
	hfp = xfopen(ARTFILE, "r");
	while (fgets(bfr, BUFLEN, hfp) != NULL) {
		p = index(bfr, '\t');
		if (p == NULL)
			p = index(bfr, '\n');
		if (p != NULL)	/* can happen if nulls in file */
			*p = 0;
		if (strcmp(bfr, hp->ident)==0 ||
				hp->oident[0] && strcmp(bfr, hp->oident)==0) {
			fclose(hfp);
			idunlock();
#ifdef DEBUG
			fprintf(stderr,"history returns true\n");
#endif
			return(TRUE);
		}
	}
	fclose(hfp);
#endif
	addhist(hp->ident);
	addhist("\t");
#ifdef DEBUG
	fprintf(stderr,"history returns false\n");
#endif
	return(FALSE);
}

/* (deleted by KSA) static char histline[256];	/* Assumed initially zero */

addhist(msg)
char *msg;
{
	strcat(histline, msg);
}

savehist()
{
	register FILE *hfp;
	datum lhs, rhs;
	long fpos;
	register char *p;

	hfp = xfopen(ARTFILE, "a");
	fpos = ftell(hfp);
	fprintf(hfp, "%s\n", histline);
	fclose(hfp);
#ifdef DBM
	/* We assume that history has already been called, calling dbminit. */
	p = index(histline, '\t');
	if (p)
		*p = 0;
	lhs.dptr = histline;
	lhs.dsize = strlen(lhs.dptr) + 1;
	rhs.dptr = (char *) &fpos;
	rhs.dsize = sizeof fpos;
	store(lhs, rhs);
#endif
	histline[0] = 0;
	idunlock();
}

/*
 * Save partial news.
 */
newssave(fd, dummy)
FILE *fd, *dummy;
{
	register FILE *tofd, *fromfd;
	char sfname[BUFLEN];
	register int c;
	struct hbuf h;
	time_t tim;

	if (fd == NULL)
		fromfd = xfopen(INFILE, "r");
	else
		fromfd = fd;
	umask(savmask);
	setgid(gid);
	setuid(uid);

	sprintf(sfname, "%s/%s", userhome, PARTIAL);
	if ((tofd = fopen(sfname, "a")) == NULL)
		xerror("Cannot save partial news");
	time(&tim);
	fprintf(tofd, "----- News saved at %s\n", arpadate(&tim));
	while ((c = getc(fromfd)) != EOF)
		putc(c, tofd);
	fclose(fromfd);
	fclose(tofd);
	printf("News saved in %s\n", sfname);
	xxit(0);
}

/*
 * Handle dates in header.
 */

dates(hp)
struct hbuf *hp;
{
	long edt;

	time(&hp->rectime);
	nstrip(strcpy(hp->recdate, arpadate(&hp->rectime)));
	if (*hp->subdate) {
		if (cgtdate(hp->subdate) < 0) {
			log("Bad sub date '%s'", hp->subdate);
			xerror("Cannot parse submittal date");
		}
	} else
		strcpy(hp->subdate, hp->recdate);
	if (*hp->expdate) {
		if ((edt = cgtdate(hp->expdate)) < 0)
			xerror("Cannot parse expiration date");
		nstrip(strcpy(hp->expdate, arpadate(&edt)));
	} else {
		defexp = TRUE;
		/*
		 * Default is now applied in expire.c
		hp->exptime = hp->rectime + DFLTEXP;
		nstrip(strcpy(hp->expdate, arpadate(&hp->exptime)));
		*/
	}
}

/*
 *	Exit and cleanup.
 */
xxit(status)
int status;
{
	unlink(INFILE);
	unlink(ARTICLE);
	while (lockcount > 0)
		unlock();
	idunlock();
	exit(status);
}

xerror(message, arg1, arg2)
char *message;
int arg1, arg2;
{
	char buffer[128];

	fflush(stdout);
	sprintf(buffer, message, arg1, arg2);
	fprintf(stderr, "inews: %s.\n", buffer);
	log(buffer);
	xxit(1);
}

#ifdef	VMS

#define	SUBLOCK	"/tmp/netnews.lck.1"

/*
 * Newsystem locking.
 * These routines are different for VMS because we can not
 * effectively simulate links, and VMS supports multiple
 * version numbers of files
 */
lock()
{
	register int i;
	register int fd;

	if (lockcount++ == 0) {
		i = DEADTIME;
		while ((fd = creat(SUBLOCK, 0444)) < 0) {
			if (--i < 0) {
				unlink(SUBLOCK);
				log("News system locked up");
			}
			if (i < -3)
				xerror("Unable to unlock news system");
			sleep((unsigned)1);
		}
		close(fd);
	}
}

unlock()
{
	if (--lockcount == 0)
		unlink(SUBLOCK);
}

#else	VMS

/*
 * Newsystem locking.
 */

lock()
{
	register int i;

	if (lockcount++ == 0) {
		i = DEADTIME;
		while (link(SUBFILE, LOCKFILE)) {
			if (--i < 0)
				xerror("News system locked up");
			sleep((unsigned)1);
		}
	}
}

unlock()
{
	if (--lockcount == 0)
		unlink(LOCKFILE);
}
#endif	VMS

char lockname[80];
idlock(str)
char *str;
{
	register int i;
	char tempname[80];
	long now;
	struct stat sbuf;
	int fd;

#ifdef	VMS
	sprintf(lockname, "/tmp/%s.l.1", str);
	if ((fd = creat(lockname, 0444)) < 0) {
#else	VMS
	sprintf(tempname, "/tmp/LTMP.%d", getpid());
	sprintf(lockname, "/tmp/L%s", str);
#ifdef FOURTEENMAX
	lockname[5 /* /tmp/ */ + 14] = '\0';
#endif
	close(creat(tempname, 0666));
	while (link(tempname, lockname)) {
#endif	VMS
		time(&now);
		i = stat(lockname, &sbuf);
		if (i < 0) {
			xerror("Directory permission problem in /tmp");
		}
		if (sbuf.st_mtime + 10*60 < now) {
			unlink(lockname);
			log("Article %s locked up", str);
			continue;
		}
		log("waiting on lock for %s", lockname);
		sleep((unsigned)60);
	}
#ifdef VMS
	close(fd);
#else
	unlink(tempname);
#endif
	unlink(tempname);
}

idunlock()
{
	unlink(lockname);
}

/*
 * Put a unique name into header.ident.
 */
getident(hp)
struct hbuf *hp;
{
	long seqn;
	register FILE *fp;

	lock();
	fp = xfopen(SEQFILE, "r");
	fgets(bfr, BUFLEN, fp);
	fclose(fp);
	seqn = atol(bfr) + 1;
#ifdef	VMS
	unlink(SEQFILE);
#endif	VMS
	fp = xfopen(SEQFILE, "w");
	fprintf(fp, "%ld\n", seqn);
	fclose(fp);
	unlock();
	sprintf(hp->ident, "<%ld@%s%s>", seqn, FULLSYSNAME, MYDOMAIN);
}

/*
 * Log the given message, with printf strings and parameters allowed,
 * on the log file, if it can be written.  The date and an attempt at
 * figuring out the remote system name are also logged.
 */
log(fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
char *fmt;
{
	FILE *logfile;
	char msg[256];
	char *logtime, *p, *q;
	char rmtsys[256];
	char c;
	long t;

	if (header.relayversion[0]) {
		for (p=header.relayversion; p; p=index(p+1, 's'))
			if (strncmp(p, "site ", 5) == 0)
				break;
		if (p == NULL)
			goto crackpath;
		p += 4;
		while (*p == ' ' || *p == '\t')
			p++;
		for (q=p; *q && *q!=' ' && *q != '\t'; q++)
			;
		c = *q;
		strcpy(rmtsys, p);
		*q = c;
	} else {
crackpath:
		strcpy(rmtsys, header.path);
		p = index(rmtsys, '!');
		if (p == NULL)
			p = index(rmtsys, ':');
		if (p)
			*p = 0;
		else {
			p = rindex(rmtsys, '@');
			if (p)
				strcpy(rmtsys, p+1);
			else
				strcpy(rmtsys, "local");
		}
	}

	time(&t);
	logtime = ctime(&t);
	logtime[16] = 0;
	logtime += 4;

	sprintf(msg, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);

	lock();
	if (access(logfname, 0)) {
		unlock(0);
		return;
	}
	logfile = fopen(logfname, "a");
	if (logfile == NULL) {
		unlock(0);
		return;
	}
	fprintf(logfile, "%s %s\t%s\n", logtime, rmtsys, msg);
	fclose(logfile);
	unlock();
}

/*
 * Check if header.nbuf contains only valid newsgroup names;
 * exit with error if not valid.
 *
 * a == TRUE means header.nbuf is subscription list
 * a == FALSE means header.nbuf is newsgroup list
 */

ngfcheck(a)
int a;
{
	char ngcheck[NGFSIZ];	/* Hold NGFILE newsgroups */
	char tbuf[BUFLEN];	/* hold single header.nbuf news group */
	register char *s1, *s2;
	register FILE *f;

	s1 = ngcheck;
	f = xfopen(NGFILE, "r");
	while (fgets(bfr, BUFLEN, f) != NULL) {
		for (s2 = bfr; *s2 != '\0' &&
		    *s2 != ' ' && *s2 != '\t' &&
		    *s2 != ':' && *s2 != '\n';) {
			if (s1 >= &ngcheck[NGFSIZ-2])
				xerror("NGFILE too long");
			*s1++ = *s2++;
		}
		*s1++ = NGDELIM;
	}
	*s1 = '\0';
	fclose(f);
	for (s1 = header.nbuf; *s1 != '\0';) {
		if (*s1 == NEGCHAR)
			s1++;
		s2 = tbuf;
		while ((*s2++ = *s1++) != NGDELIM)
			if (s1[-1] == ':')
				xerror("Newsgroup cannot contain ':'");
		*s2 = '\0';
		s2 = tbuf;
		if (!ngmatch(s2, ngcheck) && (!a || !ngmatch(ngcheck, s2))) {
			ngdel(s2);
			sprintf(bfr, "Bad news group \"%s\"", s2);
			newssave(stdin, NULL);
			xerror(bfr);
		}
	}
}

/*
 * Figure out who posted the article (which is locally entered).
 * The results are placed in the header structure hp.
 */
gensender(hp, logname)
struct hbuf *hp;
char *logname;
{
	char *fn;
	static char buf[100];
	char buf2[100];
	char *fullname(), *getenv();
	char *p;
	int fd;

	fn = getenv("NAME");

	if (fn == NULL) {
		sprintf(buf, "%s/%s", getenv("HOME"), ".name");
		fd = open(buf, 0);
		if (fd >= 0) {
			read(fd, buf2, sizeof buf2);
			close(fd);
			if (buf2[0] >= 'A')
				fn = buf2;
			for (p=fn; *p; p++)
				if (*p < ' ')
					*p = 0;
		}
	}

	if (fn == NULL)
		fn = fullname(logname);

	sprintf(hp->path, "%s", logname);
	sprintf(hp->from, "%s@%s%s (%s)", logname, FULLSYSNAME, MYDOMAIN, fn);
}

/*
 * Trap interrupts.
 */
onsig(n)
int n;
{
	static int numsigs = 0;
	/*
	 * Most UNIX systems reset caught signals to SIG_DFL.
	 * This bad design requires that the trap be set again here.
	 * Unfortunately, if the signal recurs before the trap is set,
	 * the program will die, possibly leaving the lock in place.
	 */
	if (++numsigs > 100) {
		log("readnews ran away looping on signal %d", n);
		xxit(1);
	}
	signal(n, onsig);
	sigtrap = n;
}

/*
 * If the stdin begins with "#", we assume we have been fed a batched
 * shell script which looks like this:
 *	#! rnews 1234
 *	article with 1234 chars
 *	#! rnews 4321
 *	article with 4321 chars
 *
 * In this case we just exec the unbatcher and let it unpack and call us back.
 *
 * Note that there is a potential security hole here.  If the batcher is
 * /bin/sh, someone could ship you arbitrary stuff to run as shell commands.
 * The main protection you have is that the effective uid will be news, not
 * uucp and not the super user.  (That, plus the fact that BATCH is set to
 * "unbatch" as the system is distributed.)  If you want to run a batched link
 * and you are security concious, do not use /bin/sh as the unbatcher.
 * the thing to do is to change BATCH in your localize.sh file from /bin/sh
 * to some restricted shell which can only run rnews.
 */
checkbatch()
{
	int c;

#ifdef BATCH
	c = getc(stdin);
	ungetc(c, stdin);
	clearerr(stdin);
	if (c == '#') {
		reset_stdin();
		execl(BATCH, "news-unpack", 0);
		xerror("Unable to exec shell to unpack news.\n");
	}
#endif
}

/*
 * We've already done a read on stdin, and we want to seek back to the
 * beginning.  We want the real file descriptor (beyond buffers) to
 * reflect the true beginning.  Do whatever is necessary.
 */
reset_stdin()
{
	register FILE *ofd;
	register int c;
	char *ofdname;

	/* First try to seek back - if so, it's a cheap way back. */
	if (lseek(0, 0L, 0) == 0)
		return;

	/* Can't seek, so have to copy input to a file and use that. */
	ofdname = "/tmp/inewsXXXXX";
	mktemp(ofdname);
	ofd = fopen(ofdname, "w");
	while ((c=getc(stdin)) != EOF)
		putc(c, ofd);
	fclose(stdin);
	fclose(ofd);

	/* Now for a few lower level hacks to reopen stdin and make
	 * absolutely sure that the right fd's are done for the exec.
	 */
	close(0);		/* to make sure stdin is really closed. */
	open(ofdname, 0);	/* returns zero */
	unlink(ofdname);	/* to avoid cleaning it up later. */
}
!E!O!F!

cat > artfile/makeaf.c <<\!E!O!F!
#include <stdio.h>
#include "af.h"



/*
 * Create a new artfile.
 */

makeaf(name, idtlen, maxng, ttlen)
      char *name ;
      {
      register FILE *fp ;
      DPTR offset, count ;
      struct afheader afhd ;

      if ((fp = fopen(name, "w")) == NULL)
            xerror("Can't create %s", name) ;
      offset = count = sizeof afhd ;
      afhd.af_magic = AF_MAGIC ;
      afhd.af_version = AF_VERSION ;
      afhd.af_idtab = offset ;
      afhd.af_idtlen = idtlen ;
      offset += idtlen * sizeof (DPTR) ;
      afhd.af_nglist = offset ;
      afhd.af_maxng = maxng ;
      offset += maxng * sizeof (DPTR) ;
      afhd.af_titletab = offset ;
      afhd.af_ttlen = ttlen ;
      offset += ttlen * sizeof (DPTR) ;
      afhd.af_records = afhd.af_free = offset ;
      fwrite((char *)&afhd, sizeof afhd, 1, fp) ;
      while (count < offset) {
            putc('\0', fp) ;
            count++ ;
      }
      if (ferror(fp) || fclose(fp) == EOF)
            xerror("Write error in makeaf") ;
}
!E!O!F!

cat > artfile/mkngfile <<\!E!O!F!
# Construct the groupfile.
awk '{ print $1 " " NR }' ${1-/usr/lib/news/active} > /tmp/ng$$
wc -l < /tmp/ng$$
cat /tmp/ng$$
!E!O!F!
chmod +x artfile/mkngfile

cat > artfile/ngtab.c <<\!E!O!F!
#include <stdio.h>
#include "ng.h"


static char MODGROUPS[] = "all.announce,mod.all,all.mod,fa.all,control,junk" ;
struct ngrec *ngroup ;
extern int maxng ;



/*
 * Read in the newsgroup file.
 */

getngtab() {
      struct ngrec ng ;
      char *savestr(), *calloc(), *realloc() ;

      gfopen() ;
      if ((ngroup = calloc(sizeof(*ngroup), maxng)) == NULL)
            xerror("out of space") ;
      ALL_GROUPS(ng) {
            ngroup[ng.g_num - 1] = ng ;
      }
      gfclose() ;
}



/*
 * Initialize without reading in the newsgroup file.
 */

ngtinit() {
      char *ckmalloc() ;

      maxng = 0 ;
      ngroup = ckmalloc(sizeof(struct ngrec)) ;
}



/*
 * Convert a newsgroup number to a newsgroup name
 */

char *
ngname(num) {
      return ngroup[num - 1].g_name ;
}



/*
 * Convert a newsgroup name to a number
 */

int
ngnum(name)
      char *name ;
      {
      register i ;
      register struct ngrec *gp ;
      char n[MAXNGNAME] ;

      scopyn(name, n, MAXNGNAME) ;
      for (gp = ngroup, i = maxng ; --i >= 0 ; gp++)
            if (strcmp(gp->g_name, n) == 0)
                  return gp - ngroup + 1 ;
      printf("ngnum: %s not found\n", name) ;		/*DEBUG*/
      return -1 ;
}



newng(name)
      char *name ;
      {
      register int i ;

      for (i = 0 ; i < maxng ; i++) {
            if (ngroup[i].g_name[0] == '\0')
                  break ;
      }
      printf("newng %s assigns %d", name, i + 1) ;	/*DEBUG*/
      if (i == maxng) {
            if ((ngroup = realloc((char *)ngroup, (i + 1) * sizeof ngroup[0])) == NULL)
                  xerror("out of space") ;
            maxng++ ;
            printf(", maxng upped") ;			/*DEBUG*/
      }
      printf("\n") ;					/*DEBUG*/
      scopyn(name, ngroup[i].g_name, MAXNGNAME) ;
      ngroup[i].g_flags = ngmatch(name, MODGROUPS)? G_MOD : 0 ;
      return ngroup[i].g_num = i + 1 ;
}



writengfile() {
      char newname[64], curname[64], oldname[64] ;
      FILE *fp ;
      int i ;
      register struct ngrec *gp ;
      extern char LIB[] ;

      printf("writeng called\n") ;		/*DEBUG*/
      sprintf(curname, "%s/groupfile", LIB) ;
      sprintf(newname, "%s.tmp", curname) ;
      sprintf(oldname, "%s.bak", curname) ;
      unlink(newname) ;
      if ((fp = fopen(newname, "w")) == NULL)
            xerror("Can't create %s", newname) ;
      fprintf(fp, "%d\n", maxng) ;
      for (i = maxng, gp = ngroup ; --i >= 0 ; gp++) {
            fprintf(fp, "%s %d", gp->g_name, gp->g_num) ;
            if (gp->g_flags & G_MOD)
                  fputs(" -m", fp) ;
            fputc('\n', fp) ;
      }
      if (ferror(fp) || fclose(fp) == EOF)
            xerror("Write error on %s", newname) ;
      rename(curname, oldname) ;
      if (rename(newname, curname) < 0)
            xerror("rename %s failed", newname) ;
}
!E!O!F!

cat > artfile/title.c <<\!E!O!F!
#include <stdio.h>
#include "af.h"

#define MAXTITLE 256	/* where does this belong? */
#define equal(s1, s2)	(strcmp(s1, s2) == 0)


/*
 * Return a pointer to the hash chain for this title.
 */

DPTR
htitle(title)
      register char *title ;
      {
      char buf[MAXTITLE] ;

      while (isre(title))
            for (title += 3 ; *title == ' ' ; title++) ;
      strcpy(buf, title) ;
      rmnf(buf) ;
      return afhd.af_titletab + hash(buf, afhd.af_ttlen) * sizeof(DPTR) ;
}



/*
 * Determine if a1 is the parent of a2 using titles.
 */

tparent(a1, a2)
      struct artrec *a1, *a2 ;
      {
      char t1[MAXTITLE], t2[MAXTITLE] ;
      char *p ;

      if (a1->a_subtime > a2->a_subtime || !isre(a2->a_title))
            return 0 ;
      strcpy(t1, a1->a_title), strcpy(t2, a2->a_title) ;
      if (rmnf(t1)) {
            if (!rmnf(t2))
                  return 0 ;
      } else {
            rmnf(t2) ;
      }
      for (p = t2 + 3 ; *p == ' ' ; p++) ;	/* skip over "Re:" */
      if (!equal(t1, p))
            return 0 ;
      return 1 ;
}
!E!O!F!

echo Part 1 of 7 extracted.



More information about the Mod.sources mailing list