[mod.sources] v06i052: Usenet news batcher control program

sources-request@mirror.UUCP (07/14/86)

Submitted by: cca!caip!cbmvax!bpa!espo (Bob Esposito)
Mod.sources: Volume 6, Issue 52
Archive-name: newbatchA

[  This is the first of two batched news utility programs (BNUP, to coin
   an acronym).  It was written on a SystemV machine, but it looks like
   it would be fairly easy to convert to BSD and, e.g., the directory
   library.  Stuff dealing with the UUCP queue is much less portable,
   of course, but Bob nicely provides a default mechanism.  Take a look
   at the check_uucp routine, however -- I think you may want a different
   default value.  Also, I added a modified xerror routine that doesn't
   depend on knowing the internal's of the FILE struct.  I haven't
   tested it, but a "grep xerror *." seems to say I'm ok; add -DUSE_PORT_CODE
   for my version.  --r$ ]

#!/bin/sh
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
# Contents:  README nbatcher.1 nbatcher.ctl nbatcher.doc Makefile
#	bst.c checkwork.c define.c logger.c main.c nbatcher.c parse.c
 
echo x - README
sed 's/^XX//' > "README" <<'@//E*O*F README//'
XXThe program provides a better way to handling batching of USENET news
XXto neighboring sites.  It works with 2.10B and later, including
XX2.10.3B, which provides compression batching.

XXPlease send any questions and bug reports to me (bpa!espo) for
XXdistribution to the net.  This software is public domain, and cannot be
XXsold for any profit.

XXNbatcher was written for UNIX (trademark of AT&T) System V, but should
XXbe compatible with most version currently being used today.  It is
XXpresently running on a VAX (trademark of DEC) 11/780 feeding 5 remote
XXsites with news.  Two important issues MUST be noted here.

XXSince nbatcher checks the UUCP directory for each site listed in the
XXcontrol file, directory configuration should be UUCPDIR/sitename, e.g.
XX/usr/spool/uucp/foo for site foo.  But since not everyone is generic,
XXI've allowed nbatcher to skip over the UUCP byte counting routine if
XXthis is not so.  In addition to this, some uucpcleanup daemons remove
XXthe site directory if there's nothing spooled there.  When this
XXhappens, nbatcher will notify you of this and continue on.

XXThe other issue is that you MUST configure your NEWSLIB/sys file using the
XXbatch syntax as described in the USENET Installation document.  Field 3
XXshould contain the `F' flag indicating a batched transmission for that
XXsite and field 4 should contain the filename where news article's full
XXpathname will be appended to.  Nbatcher requires that the filename in
XXfield 4 match the sitename in the control file.  For example, the entry:
XX	foo:net,mod,usa,na,to.foo:F:/usr/spool/batchnews/foo
XXsays that all news articles going to site `foo' will be batched, using
XXthe file `foo' in /usr/spool/batchnews.  Nbatcher's control file entry:
XX	foo:3,14,22:16:150000:
XXwill get the news article's full pathname from /usr/spool/batchnews/foo
XXfor batching.  This requirement MUST be adhered to for nbatcher to work
XXcorrectly.  I believe most sites that batch news use this type of
XXconfiguration, since its easy to keep track of a remote site's work.
XXNote that the name of the directory "/usr/spool/batchnews" is set
XXin the Makefile.

XXThe manual page describes the control file, as does nbatcher.doc.  Note
XXthat even though nbatcher indicates max bytes has been reached, if that
XXsite gets compressed batches, the next time nbatcher runs for that site
XXwork could get scheduled.

XXAlso included is a utility program called "bst," BatchSTatus, which
XXshows who many news articles are currently in the batch file for each
XXsystem.  Just type "make bst" for that and copy it to where you want,
XXusually LIBDIR.  Note that bst does reads on the directory structure
XXdirectly.

XXInstallation is simple:  look at the first few #define's in nbatcher.h;
XXalso edit the Makefile for the appropriate BATCHDIR and LIBDIR for your
XXsite.  Type "make" or "make install."

XXNOTE:  Read the comments in parse.c concerning MAX_BYTES.  This is the
XXmaximum amount of bytes per site that's allowed in the control file for
XXqueue_size.  Change it at your own risk!

XX    Bob Esposito
XX    bpa!espo
XX    Bell of Penna.
@//E*O*F README//
chmod u=rw,g=rw,o=rw README
 
echo x - nbatcher.1
sed 's/^XX//' > "nbatcher.1" <<'@//E*O*F nbatcher.1//'
XX.TH NBATCHER 1 LOCAL
XX.SH NAME
XXnbatcher  \- new batching system for news
XX.SH SYNOPSIS
XX.B nbatcher
XX.IR "(run out of " cron ".)"
XX.SH DESCRIPTION
XX.PP
XX.I Nbatcher
XXis a program designed to send batched USENET data out in an orderly and
XXcontrolled fashion, while providing alternative transmission methods.
XXAs such, it is a replacement for
XX.I csendbatch
XXand the like, which typically require many entries in
XX.IR crontab .
XX.I Nbatcher
XXis intended to be run hourly out of
XX.I cron
XXas the USENET administrator (NEWSID).
XX.PP
XX.I Nbatcher
XXscans the file
XX.I nbatcher.ctl
XXin the NEWSLIB directory to determine if work should be spooled for a system.
XXIn the control file, lines starting with asterisks are ignored; data lines
XXare comprised of five colon\-separated fields:
XX.RS
XXsite:hour:bits:queue_size:command
XX.RE
XX.TP ``site''
XXName of the USENET neighbor; it is the same as the site in the news
XX.I sys
XXfile.
XX.TP ``hours''
XXThis field is patterned after
XX.IR cron 's.
XXIf the hour is ``off,'' no work is spooled for the site.
XXAn hour of ``*'' matches every hour.
XXIt is also possible to specify specific hours, (e.g., 8, 09, or 22), a
XXcomma\-separated list (e.g., 8,09,22), or a twenty\-four range, like
XX1-\15 for 10am through 3pm and 22\-4, for 10pm through 4am.
XX.TP ``bits''
XXThis field specifies the number of bits to use in compression; it should be
XXa number between nine and 16, inclusive, or null.
XXIf a number is specified, it is passed on to the
XX.I compress
XXprogram via the ``\-b'' flag.
XX.TP ``queue_size''
XXThis field specifies the maximum number of bytes allowed in the UUCP queue
XXfor this site.
XXThe default is 100K.
XXThe UUCP queue size is determined by looking in the
XX.IR /usr/spool/uucp/ site
XXdirectory; if it doesn't exist, the check is bypassed.
XXIf there is data in the UUCP queue,
XX.I nbatcher
XXwill only queue up as many USENET transfers as will fit within the limit
XXspecified by the ``queue_size'' field.
XX.TP ``command''
XXThis field is used to specific the UUCP command that should be used to queue
XXthe job; as distributed, the default is ``uux \- \-r site!rnews''; note the
XXabsence of the ``\-z'' flag.
XX.PP
XXTo set up a USENET neighbor to be controlled by
XX.IR nbatcher ,
XXthe news
XX.I sys
XXentry for the neighbor must be modified to specify the ``F'' flag, and the
XXfile used to contain the article names must be
XX.RI BATCHDIR/sysname ,
XXwhere BATCHDIR is set in the Makefile as distributed.
XX.SH FILES
XX.TP NEWSLIB/nbatcher.log
XXA logfile of failures, postponements, etc.
XX.SH BUGS
XXParsing of the control file is fairly robust, but not perfect.
@//E*O*F nbatcher.1//
chmod u=rw,g=rw,o=rw nbatcher.1
 
echo x - nbatcher.ctl
sed 's/^XX//' > "nbatcher.ctl" <<'@//E*O*F nbatcher.ctl//'
XX*
XX*	NBATCHER.CTL
XX*	Edit and install in your NEWSLIB directory.
XX*
XX*  Comments start with *; data lines look like:
XX*	site:hours:bits:queue_size:command
XX*  Where
XX*	site		= name of the remote site
XX*	hour		= when to do work (* is all, off is never, 22-4 is ok)
XX*	bits		= passed on to compress via -b; null gets default
XX*	queue_size	= Max # bytes allowed in UUCP queue before postponing
XX*	command		= optional command line
XX*
XX*  See manpage and README for more info.
@//E*O*F nbatcher.ctl//
chmod u=rw,g=rw,o=rw nbatcher.ctl
 
echo x - nbatcher.doc
sed 's/^XX//' > "nbatcher.doc" <<'@//E*O*F nbatcher.doc//'

XXDETAILED DESCRIPTION OF "NBATCHER.CTL" FILE
XX-------------------------------------------

XXAs distributed, nbatcher.ctl contains just a terse summary of its
XXformat.  For each site you feed news to, providing that site uses the
XX":F:BATCHDIR/site" batching syntax in the sys file, a corresponding
XXentry should exist in nbatcher.ctl  Each line is a five-field,
XXcolon-separated entry indicating what to do for that site.  The default
XXfor BATCHDIR is /usr/spool/batchnews.

XXThe format is:
XX	site:hour:bits:queue_size:command
XXThe site field is the name of the UUCP site that will get the batched
XXnews.  There MUST be a file in the BATCHDIR directory with the
XXsitename.  Nbatcher will complain about mismatches.  This file contains
XXa full-path listing of the files to batch.

XXNext is the hour field.  The syntax directly imitates the crontab entry
XXfor hour.  If hour = "*", than assume a match for every hour.  If hour
XX= "off", then no work is ever spooled for this site.  (This is the only
XXdifference from crontab).  You can specify a specific hour, like 8 or
XX09 or 22.  Or a range of hours, like 10-15, meaning check for work from
XX10am thru 3pm.  You can also cycle thru a 24-hour period by saying
XX22-4, which matches for 10pm thru 4am.  Also, hours comma separated
XXlike 7,14,21, says check for work at 7 AM, 4PM and 9PM only.  This
XXgives lots of flexibility for sites that feed multiple remotes.

XXNext is the bits field, which has a value of 9-16, or may be left blank.
XXIf non-null, this field is directly passed on to compress with the -b
XXflag.  If it is null, then no compression is done for this site.

XXThe queue_size field is the maximum number of bytes allowed on the UUCP
XXqueue at any time.  It defaults to 100K bytes, and if it's greater than
XX1MB (see parse.c about MAX_BYTES), than it uses 1MB as the default
XXnumber of bytes.

XXWhat nbatcher does is first check the UUCPDIR for that site and sums up
XXthe number of bytes already on-queue.  The size of each news article is
XXaccumulated prior to batching to the tempfile, and this accumulation +
XXthe UUCP on-queue bytes are checked to see if they surpass the queue_size
XXvalue.  If not, things proceed normally.  But if it would exceed the
XXmax value, then spool what's already been batched, and save the
XXremainder of the articles for the next scheduled batch.

XXThis is so noted in the nbatcher.log file, showing the UUCP bytes that
XXwere on-queue (if any), and the number of bytes that were spooled.
XXAlso, if nbatcher is run from a terminal instead of from cron, a copy
XXof what gets logged is sent to the terminal.

XXThe last field is the command field.  This optional field is used for
XXspecific UUCP command execution.  As written, it defaults to "uux - -r
XXsite!rnews".  Since my uux doesn't require the -z option for rnews, I
XXleft it out of the UUX define in nbatcher.h  This can easily be changed
XXfor your own taste, or just use the command field.

XXExample:
XX	site-A:3,10:16:450000:
XXCheck for work for site-A at 3AM and 10AM.  Use compress with
XX16 bits and only spool up to 450K bytes, using "uux - -r site-A!rnews".

XXAnother example:
XX	foo:23-4::300000:uux - -r -z foo!rnews
XXCheck for work for foo between 11PM and 4AM.  No compression is used
XXand spool up to 300K bytes using the command field.

XXThis format should help ease UUCP congestion on the local site for
XXnews.  I currently feed 5 remote sites, compressed and non-compressed
XXformats, and have noticed a vast improvement in disk space as well as
XXout port availability for UUCP.  It has allowed me to check for work
XXfor all sites every hour, since there's a limit on how much gets
XXqueued.  And if the remote doesn't answer on the hourly UUCP poll, so
XXwhat!  Nbatcher just won't spool any more until the queue_size limit
XXon-queue is reduced.

XXNbatcher was designed to weed out any illegal syntax in the control
XXfile, but it's not bug-proof, so use some discretion.

XXBob Esposito
XXBell of Penna.
XXJune 1986
@//E*O*F nbatcher.doc//
chmod u=rw,g=rw,o=rw nbatcher.doc
 
echo x - Makefile
sed 's/^XX//' > "Makefile" <<'@//E*O*F Makefile//'
XX#
XX#	Makefile for nbatcher
XX#
XX#	R.J. Esposito
XX#	Bell of Penna.
XX#	June 1986
XX#

XX#	You MUST define BATCHDIR as the place where USENET
XX#	puts the articles to be batched.
XX#
XX#	LIBDIR is where you USENET library is and also
XX#	MUST be defined.

XXBATCHDIR = /usr/spool/batchnews
XXLIBDIR = /misc/lib/usenet

XXCFLAGS = -O -c
XXLFLAGS = -s
XXDFLAGS = -DBATCHDIR='"$(BATCHDIR)"' -DLIBDIR='"$(LIBDIR)"'

XXOBJS = define.o main.o parse.o checkwork.o nbatcher.o logger.o

XXSRC = define.c main.c parse.c checkwork.c nbatcher.c logger.c nbatcher.h bst.c
XXDOCS = README nbatcher.ctl nbatcher.doc

XXall: $(OBJS)
XX	$(CC) $(DFLAGS) $(LFLAGS) -o nbatcher $(OBJS)
XX	chmod 0755 nbatcher

XXinstall: all
XX	cp nbatcher $(LIBDIR)
XX	chmod 0755 $(LIBDIR)/nbatcher
XX	cp nbatcher.ctl $(LIBDIR)/nbatcher.ctl
XX	chmod 0644 $(LIBDIR)/nbatcher.ctl

XXdefine.o: nbatcher.h define.c
XX	$(CC) $(CFLAGS) $(DFLAGS) define.c

XXmain.o: nbatcher.h main.c
XX	$(CC) $(CFLAGS) $(DFLAGS) main.c

XXparse.o: nbatcher.h parse.c
XX	$(CC) $(CFLAGS) $(DFLAGS) parse.c

XXcheckwork.o: nbatcher.h checkwork.c
XX	$(CC) $(CFLAGS) $(DFLAGS) checkwork.c

XXnbatcher.o: nbatcher.h nbatcher.c
XX	$(CC) $(CFLAGS) $(DFLAGS) nbatcher.c

XXlogger.o: nbatcher.h logger.c
XX	$(CC) $(CFLAGS) $(DFLAGS) logger.c

XXbst: bst.c
XX	$(CC) $(CFLAGS) $(DFLAGS) bst.c
XX	$(CC) $(LFLAGS) -o bst bst.o

XXclean:
XX	rm -f *.o *.shar

XXclobber: clean
XX	rm -f nbatcher


XXshar:
XX	shar -v $(SRC) $(DOCS) Makefile > nbatcher.shar
@//E*O*F Makefile//
chmod u=rw,g=rw,o=rw Makefile
 
echo x - bst.c
sed 's/^XX//' > "bst.c" <<'@//E*O*F bst.c//'
XX/*
XX *	bst.c - a utility for indicating how many
XX *		news articles are ready for batching
XX *		for each site in the BACTHDIR directory.
XX *
XX *	R.J. Esposito
XX *	Bell of Penna.
XX *	June 1986
XX *
XX */

XX#include <stdio.h>
XX#include <sys/types.h>
XX#include <sys/stat.h>
XX#include <sys/dir.h>


XXchar buf[512];
XXFILE *dd;
XXstruct direct dp;
XXstruct stat st;

XXmain()
XX{
XX	int fd, j;
XX	int bcnt, lcnt;

XX	if(chdir(BATCHDIR) != 0) {
XX		perror(BATCHDIR);
XX		exit(1);
XX	}

XX	if((dd=fopen(".", "r")) == NULL) {
XX		printf("can't open %s\n", BATCHDIR);
XX		exit(1);
XX	}

XX	while((fread((char *)&dp, sizeof(dp), 1, dd)) == 1) {
XX		if(dp.d_ino == 0 || dp.d_name[0] == '.')
XX			continue;
XX		if(stat(dp.d_name, &st) != 0) {
XX			printf("can't stat %s\n", dp.d_name);
XX			exit(1);
XX		}
XX		if(st.st_size <= 0 )
XX			continue;
XX		if((fd=open(dp.d_name, 0)) < 0) {
XX			printf("can't open %s\n", dp.d_name);
XX			exit(1);
XX		}
XX		lcnt = 0;
XX		while((bcnt=read(fd,buf,512)) > 0) {
XX			for(j=0; j<=bcnt; j++)
XX				if(buf[j] == '\n')
XX					lcnt += 1;
XX		}
XX		close(fd);
XX		printf("%s\t  %d article", dp.d_name, lcnt);
XX		printf("%c\n", lcnt > 1 ? 's' : ' ');
XX	}
XX}
@//E*O*F bst.c//
chmod u=rw,g=rw,o=rw bst.c
 
echo x - checkwork.c
sed 's/^XX//' > "checkwork.c" <<'@//E*O*F checkwork.c//'
XX/*
XX *
XX *	checkwork.c - look to see if there's any work
XX *		      to do for a site.
XX *
XX *	R.J. Esposito
XX *	Bell of Penna.
XX *	June 1986
XX *
XX */

XX#include <stdio.h>
XX#include <sys/types.h>
XX#include <sys/stat.h>
XX#include <sys/utsname.h>
XX#include <sys/dir.h>
XX#include <ctype.h>
XX#include <time.h>
XX#include "nbatcher.h"

XXwork_to_do ()
XX{
XX	register char	*p;
XX	struct tm	*localtime(), *tp;
XX	struct stat	st;
XX	char	buf[BUFSIZ];
XX	long	time(), clock;
XX	int	hour;
XX	short	num, upper, lower;

XX	sprintf (buf, "%s/%s", BATCHDIR, ep.site);

XX	if (stat(buf, &st) < 0)
XX		xerror ("bad stat on %s\n", buf);

XX	/* if the size of the batch file is
XX	   zero, return FALSE
XX	*/

XX	if (st.st_size == 0)
XX		return (FALSE);

XX	/* now see if it time to do anything */

XX	clock = time ((long *)0);
XX	tp = localtime (&clock);
XX	hour = tp->tm_hour;

XX	p = (char *) ep.hour;

XX	if (*p == '*')		/* match any hour */
XX		return (check_uucp());

XX	if (strncmp(p, "off", 3) == 0)	/* just what it says, off */
XX		return (FALSE);

XX	/* parse thru hour field to see if
XX	   this is the hour to do work */

XX	num = 0;
XX	do {
XX		num = num*10 + (*p - '0');
XX	} while (isdigit(*++p));
XX	if (num == hour)
XX		return (check_uucp());

XX	if (*p == '-') {
XX		lower = num;
XX		p++;
XX		num = 0;
XX		do {
XX			num = num*10 + (*p - '0');
XX		} while (isdigit(*++p));
XX		upper = num;

XX		if (lower < upper) {	/* normal hour range */
XX			if (hour >= lower && hour <= upper)
XX				return (check_uucp());
XX		} else if (lower > upper) {	/* 24 hr. cycle thru */
XX			if (hour >= lower || hour <= upper)
XX				return (TRUE);
XX		} else
XX			return (FALSE);
XX	}

XX	if (*p == ',') {
XX		p++;
XX		while (*p) {
XX			num = 0;
XX			do {
XX				num = num*10 + (*p - '0');
XX			} while (isdigit(*++p));
XX			if (num == hour)
XX				return (check_uucp());
XX			p++;
XX		}
XX	}

XX	return (FALSE);
XX}

XX/*	If check_uucp cannot find the remote site
XX *	directory, just bypass the byte counting
XX *	routine.  This is necessary because the
XX *	uucpcleanup daemon, on some sites, removes
XX *	the site directory when there's nothing there.
XX */

XXcheck_uucp()
XX{
XX	struct utsname	utsn;
XX	struct direct	dp;
XX	struct stat	st;
XX	FILE	*dfp;
XX	char	u_name[9], buf[80];
XX	short	prefix_len;

XX	if (uname(&utsn) < 0)
XX		xerror ("can't get local nodename\n");

XX	sprintf (buf, "%s/%s", UUCPDIR, ep.site);
XX	if (chdir(buf) < 0) {
XX	   fprintf (stderr, "nbatcher: can't chdir to %s - bypassing UUCP check\n", buf);
XX	   return (TRUE);
XX	}

XX	if ((dfp=fopen(".", "r")) == NULL) {
XX	   fprintf (stderr, "nbatcher: fopen error on %s - bypassing UUCP check\n", UUCPDIR);
XX	   return (TRUE);
XX	}

XX	sprintf (buf, "D.%s", utsn.nodename);
XX	prefix_len = (short) strlen(buf);
XX	n_bytes = 0;
XX	while ((fread((char *)&dp, sizeof(dp), 1, dfp)) == 1) {
XX		if (dp.d_ino == 0 || dp.d_name[0] == '.')
XX			continue;
XX		if (strncmp(dp.d_name, buf, prefix_len))
XX			continue;
XX		if (stat(dp.d_name, &st) < 0) {
XX		  fprintf (stderr, "nbatcher: bad stat on UUCP_file %s - bypassing\n", dp.d_name);
XX		  continue;
XX		}
XX		n_bytes += st.st_size;
XX		if (n_bytes > ep.m_bytes) {
XX			fclose (dfp);
XX			return (FALSE);
XX		}
XX	}
XX	fclose (dfp);
XX	if (chdir(LIBDIR) < 0)
XX		xerror ("can't chdir back to %s\n", LIBDIR);

XX	return (TRUE);
XX}
@//E*O*F checkwork.c//
chmod u=rw,g=rw,o=rw checkwork.c
 
echo x - define.c
sed 's/^XX//' > "define.c" <<'@//E*O*F define.c//'
XX/*
XX *	define.c - global defines for nbatcher.
XX *
XX *	R.J. Esposito
XX *	Bell of Penna.
XX *	June 1986
XX *
XX */

XX#include <stdio.h>
XX#include "nbatcher.h"

XXFILE	*lfp,
XX	*tfp,
XX	*log = NULL;

XXlong	n_bytes,
XX	cu_bytes;

XXchar	*tfile = NULL;

XXshort	vflg = 0,
XX	nfiles = 10;

XXint	fcnt = 0,
XX	scnt = 0;

@//E*O*F define.c//
chmod u=rw,g=rw,o=rw define.c
 
echo x - logger.c
sed 's/^XX//' > "logger.c" <<'@//E*O*F logger.c//'
XX/*
XX *
XX *	logger.c - log info about nbatcher
XX *
XX *	R.J. Esposito
XX *	Bell of Penna.
XX *	June 1986
XX *
XX */

XX#include <stdio.h>
XX#include <time.h>
XX#include "nbatcher.h"

XXlog_it (bytes)
XXlong	bytes;
XX{
XX	struct tm	*localtime(), *tp;
XX	long	time(), clock;
XX	char	logfile[80], buf[BUFSIZ];
XX	char	pbuf[BUFSIZ];

XX	sprintf (logfile, "%s/%s", LIBDIR, "nbatcher.log");
XX	if (log == NULL) {
XX		if ((log=fopen(logfile, "a")) == NULL)
XX		   fprintf (stderr, "can't append to logfile\n");
XX	}

XX	rewind (log, 0L, 2);	/* just incase */

XX	clock = time ((long *)0);
XX	tp = localtime (&clock);
XX	sprintf (buf, "%.2d/%.2d %.2d:%.2d %s: %d %s batched, %d %s queued\n",
XX	   tp->tm_mon+1, tp->tm_mday, tp->tm_hour, tp->tm_min, ep.site,
XX	   fcnt, (fcnt==1 ? "file" : "files"), scnt,
XX	   (scnt==1 ? "file" : "files"));

XX	if (bytes)
XX	   sprintf (pbuf, "%s\tmax bytes reached.  UUCP bytes was %ld, byte count = %ld\n",
XX			buf, n_bytes, bytes);
XX	else
XX		sprintf (pbuf, "%s", buf);

XX	if (vflg)
XX		fprintf (stdout, "%s",pbuf);

XX	if (log != NULL)
XX		fputs (pbuf, log);

XX}
@//E*O*F logger.c//
chmod u=rw,g=rw,o=rw logger.c
 
echo x - main.c
sed 's/^XX//' > "main.c" <<'@//E*O*F main.c//'
XX/*
XX *
XX *	main.c - for nbatcher
XX *
XX *	R.J. Esposito
XX *	Bell of Penna.
XX *	June 1986
XX *
XX */

XX#include <stdio.h>
XX#include "nbatcher.h"

XXmain()
XX{
XX	int	uid;
XX	FILE	*cfp;
XX	char	fbuf[BUFSIZ];

XX	uid = getuid();

XX	if (uid && uid != NEWSUID)
XX		xerror ("permission denied - not NEWSUSER\n");

XX	if (chdir(LIBDIR) < 0)
XX		xerror ("can't chdir to %s\n", LIBDIR);

XX	if ((cfp=fopen("nbatcher.ctl", "r")) == NULL)
XX		xerror ("no `batcher.ctl' file found\n");

XX	if (isatty(0))
XX		vflg = TRUE;

XX	while ((fgets(fbuf, sizeof(fbuf), cfp)) != NULL) {
XX		if (fbuf[0] == '*' || fbuf[0] == '\n')
XX			continue;
XX		parse_entry (fbuf);
XX		if (!work_to_do())
XX			continue;
XX		batch_it ();
XX	}

XX	fclose (cfp);
XX	unlink (tfile);
XX	exit (0);
XX}
XX	
@//E*O*F main.c//
chmod u=rw,g=rw,o=rw main.c
 
echo x - nbatcher.c
sed 's/^XX//' > "nbatcher.c" <<'@//E*O*F nbatcher.c//'
XX/*
XX *
XX *	nbatcher.c - where it really happens.
XX *
XX *	R.J. Esposito
XX *	Bell of Penna.
XX *	June 1986
XX *
XX */

XX#include <stdio.h>
XX#include <sys/types.h>
XX#include <sys/stat.h>
XX#include <time.h>
XX#include "nbatcher.h"

XXbatch_it ()
XX{
XX	struct stat	st;
XX	FILE	*bfp, *afp;
XX	char	fbuf[BUFSIZ], lckfile[40];
XX	char	tbuf[80];
XX	short	count;
XX	int	c;

XX	if (chdir(BATCHDIR) < 0)
XX		xerror ("can't chdir to %s\n", BATCHDIR);

XX	/* we create a lock file for two purposes,
XX	   first to make sure a previous nbatcher
XX	   didn't blowup and leave the lock file
XX	   laying around, and second to put the
XX	   remaining news article filenames when
XX	   we go over the max UUCP bytes and there's
XX	   still files remaining for batching.
XX	*/

XX	sprintf (lckfile, ".%s.lock", ep.site);
XX	if (!access(lckfile, 0))
XX		xerror ("lockfile already exists for %s\n", ep.site);

XX	if ((lfp=fopen(lckfile, "w")) == NULL)
XX		xerror ("can't create lockfile for %s\n", ep.site);

XX	/* now that we've locked ourselves for this site,
XX	   lets carry on */

XX	if ((bfp=fopen(ep.site, "r")) == NULL)
XX		xerror ("can't open %s/%s for reading\n", BATCHDIR, ep.site);

XX	if (tfile == NULL) {
XX		tfile = mktemp("/tmp/bnewsXXXXXX");
XX		if ((tfp=fopen(tfile, "w")) == NULL)
XX			xerror ("can't open %s for writing\n", tfile);
XX	}

XX	count = fcnt = scnt = 0;
XX	cu_bytes = 0;
XX	while ((fgets(fbuf, sizeof(fbuf), bfp)) != NULL) {
XX		fbuf[strlen(fbuf)-1] = '\0';	/* remove the newline */
XX		if ((afp=fopen(fbuf, "r")) == NULL) {
XX		   fprintf (stderr, "bypassing article %s: can't read it\n",
XX			    fbuf);
XX			continue;
XX		}
XX		if (fstat (fileno(afp), &st) < 0)
XX 			xerror ("fstat failed on %s\n", fbuf);
XX		cu_bytes += st.st_size;

XX		/* if the max byte count is exceeded,
XX		   save the remaining files for later */

XX		if ((cu_bytes + n_bytes) > ep.m_bytes) {
XX			fprintf (lfp, "%s\n", fbuf); /* put the '\n' back */
XX			while ((fgets(fbuf, sizeof(fbuf), bfp)) != NULL)
XX				fputs (fbuf, lfp);
XX			fclose (bfp);
XX			fclose (lfp);
XX			fclose (afp);
XX			unlink (ep.site);
XX			if (link(lckfile, ep.site) < 0)
XX			   xerror ("can't link lockfile to %s\n", ep.site);
XX			unlink (lckfile);
XX			chown (ep.site, NEWSUID, NEWSGID);
XX			if (count)
XX				spoolit ();
XX 			if (cu_bytes - st.st_size)
XX 				log_it (cu_bytes - st.st_size);
XX			return;
XX		}
XX		sprintf (tbuf, "#! rnews %ld\n", st.st_size);
XX		fputs (tbuf, tfp);
XX		while ((c=getc(afp)) != EOF)
XX			putc (c, tfp);
XX		fclose (afp);

XX		if (++count == nfiles) {
XX			spoolit ();
XX			count = 0;
XX		}
XX		fcnt++;
XX	}

XX	/* The final spool if less than nfiles
XX	   is encountered.  The zero out the
XX	   batchfile and unlink the lock file */

XX	spoolit ();
XX	close (creat(ep.site, 0664));
XX	chown (ep.site, NEWSUID, NEWSGID);
XX	unlink (lckfile);

XX	/* here we log what we've done, and
XX	   if vflg is set, a copy to stdout
XX	   as well */

XX	log_it (0);
XX	if (chdir(LIBDIR) < 0)
XX		xerror ("can't chdir back to %s\n", LIBDIR);

XX}

XXspoolit ()
XX{
XX	struct stat	st;
XX	char	cmd[BUFSIZ], cfile[80];
XX	FILE	*pfp;
XX	int	c;

XX	fclose (tfp);
XX	stat (tfile, &st);

XX	/* if for some reason the temp file
XX	   is zero, just return */

XX	if (st.st_size == 0)
XX		return;

XX	/* if ep.c_bits is set use COMPRESS to compress
XX	   the temp file first
XX	*/

XX	if (ep.c_bits) {
XX		sprintf (cmd, "%s -b%d %s", COMPRESS, ep.c_bits, tfile);
XX		if (system(cmd) != 0)
XX			xerror ("system(%s) failed\n", cmd);

XX		strcpy (cfile, tfile);
XX		strcat (cfile, ".Z");
XX		if ((tfp=fopen(cfile, "r")) == NULL)
XX			xerror ("can't open %s for reading\n", cfile);

XX		/* if ep.command has a specific command
XX		   for UUCP spooling, use it.  If not,
XX		   use UUX.
XX		*/

XX		if (ep.command[0] != '\0')
XX			strcpy (cmd, ep.command);
XX		else
XX			sprintf (cmd, "%s %s!rnews", UUX, ep.site);

XX		/* now popen the command for writing
XX		   and send it the contents of tempfile */

XX		if ((pfp=popen(cmd, "w")) == NULL)
XX			xerror ("popen failed on %s\n", cmd);

XX		/********************************************
XX		 * for version 2.10.3 and above,
XX		 * prepend `#! cunbatch'.
XX		 *
XX		 * NOTE: The remote site MUST be able to
XX		 *       except this format, or it will
XX		 *       be lost!!!
XX		 *******************************************/

XX		fputs ("#! cunbatch\n", pfp);
XX		while ((c=getc(tfp)) !=  EOF)
XX			putc (c, pfp);

XX		pclose (pfp);
XX		fclose (tfp);
XX		unlink (cfile);
XX	} else {			/* regular batching here */
XX		if ((tfp=fopen(tfile, "r")) == NULL)
XX			xerror ("can't open %s for reading\n", tfile);

XX		/* if ep.command has a specific command
XX		   for UUCP spooling, use it.  If not,
XX		   use UUX.
XX		*/

XX		if (ep.command[0] != '\0')
XX			strcpy (cmd, ep.command);
XX		else
XX			sprintf (cmd, "%s %s!rnews", UUX, ep.site);

XX		if ((pfp=popen(cmd, "w")) == NULL)
XX			xerror ("popen failed on %s\n", cmd);

XX		while ((c=getc(tfp)) != EOF)
XX			putc (c, pfp);

XX		pclose (pfp);
XX		fclose (tfp);
XX	}
XX	if ((tfp=fopen(tfile, "w")) == NULL)
XX		xerror ("can't re-open %s\n", tfile);

XX	scnt++;
XX}
@//E*O*F nbatcher.c//
chmod u=rw,g=rw,o=rw nbatcher.c
 
echo x - parse.c
sed 's/^XX//' > "parse.c" <<'@//E*O*F parse.c//'
XX/*
XX *
XX *	parse.c - nbatcher line parser for the control file
XX *
XX *
XX *	R.J. Esposito
XX *	Bell of Penna.
XX *	June 1986
XX *
XX */

XX#include <stdio.h>
XX#include <sys/types.h>
XX#include <ctype.h>
XX#include "nbatcher.h"

XX#define MAX_BYTES	1000000L	/* max allowable bytes */

XXparse_entry (line)
XXchar *line;
XX{
XX	register char	*p;
XX	short	num, upper;
XX	short	lower, dash;
XX	long	l_num;

XX	upper = 23;	/* upper hour limit */
XX	lower = 0;	/* lower hour limit */
XX	dash = 0;

XX	clear_entry (&ep);	/* zero out the structure */

XX	p = (char *) ep.site;

XX	/* get the site name and copy
XX	   it to the structure */

XX	while (*line && *line != COLON)
XX		*p++ = *line++;
XX	*p = '\0';
XX	if (*++line == '\n' || *line == '\0')
XX		xerror ("illegal number of fields\n");

XX	/* check that its valid */

XX	if (ep.site[0] == '\0')
XX		xerror ("null site name in control file\n");

XX	/* now, parse the hour string and check
XX	   for valid syntax */

XX	p = (char *) ep.hour;
XX	while (*line && *line != COLON)
XX		*p++ = *line++;

XX	*p = '\0';
XX	if (*++line == '\n' || *line == '\0')
XX		xerror ("illegal number of fields\n");

XX	if (ep.hour[0] == '\0')
XX		xerror ("null hour string in control file\n");

XX	/* now re-scan the hour in structure and
XX	   weed out the badies */

XX	if (ep.hour[0] == '*' && ep.hour[1] != '\0')
XX		xerror ("invalid hour string syntax: %s\n", ep.hour);
XX	else if (ep.hour[0] == '*')
XX		goto h_skip;

XX	if (strcmp(ep.hour, "off", 3) == 0 && ep.hour[3] != '\0')
XX		xerror ("invalid hour string syntax: %s\n", ep.hour);
XX	else if (strncmp(ep.hour, "off", 3) == 0)
XX		goto h_skip;

XX	p = (char *) ep.hour;
XX	if (!isdigit(*p))
XX		xerror ("non-numeric char in hour string: %c\n", *p);

XX	while (*p) {
XX		num = 0;
XX		do {
XX			num = num*10 + (*p - '0');
XX		} while (isdigit(*++p));

XX		if (num < lower || num > upper)
XX			xerror ("illegal hour: %d\n", num);

XX		if (!*p)
XX			break;

XX		if (*p == '-' && dash)
XX			xerror ("syntax error in hour field\n");
XX		else if (*p == '-')
XX			dash = TRUE;

XX		if (*p != ',' && *p != '-')
XX			xerror ("non-numeric char in hour string: %c\n", *p);
XX		else if (!isdigit(*++p))
XX			xerror ("syntax error in hour field\n");

XX	}

XX	/* now that thats over with, let do the compression
XX	   field.  Only 9-16 is allowed, except a null field
XX	   indicates no compression for this site. */

XXh_skip:
XX	num = 0;
XX	while (*line && *line != COLON) {
XX		if (!isdigit(*line))
XX			xerror ("non-numeric in compression field\n");
XX		num = num*10 + (*line++ - '0');
XX	}
XX	if (*++line == '\n' || *line == '\0')
XX		xerror ("illegal number of fields\n");

XX	if (num != 0 && (num < 9 || num > 16))
XX		xerror ("illegal compression bits: %d\n", num);

XX	ep.c_bits = num;

XX	/* now check the max. bytes for UUCP queue.
XX	   Note: There is a max. allowable # of bytes
XX		 here, set at 1MB.  Change it at your
XX		 own risk.
XX	*/

XX	l_num = 0;
XX	while (*line && *line != COLON) {
XX		if (!isdigit(*line))
XX			xerror ("non-numeric in max. bytes field\n");

XX		l_num = l_num*10 + (*line++ - '0');
XX	}

XX	if (l_num > MAX_BYTES)
XX		xerror ("%ld max. bytes exceeds allowable maximun\n", l_num);

XX	if (l_num != 0)
XX		ep.m_bytes = l_num;
XX	else
XX		ep.m_bytes = DFL_BYTES;

XX	/* and finally the command line (if there is one) */

XX	p = (char *) ep.command;

XX	if (*++line != '\n' && *line != '\0') {
XX		while (*line && *line != '\n')
XX			*p++ = *line++;

XX		*p = '\0';
XX	}
XX}

XX#ifdef USE_PORT_CODE
XXxerror (fmt, a1, a2)
XXchar *fmt;
XXchar *a1, *a2;
XX{
XX	char	buf[BUFSIZ];

XX	sprintf (buf, fmt, a1, a2);
XX	printf ("nbatcher: %s\n", fmt);
XX	exit (99);
XX}

XX#else
XXxerror (fmt, argp)
XXchar *fmt;
XXint argp;
XX{
XX	char	buf[BUFSIZ];
XX	char	fbuf[BUFSIZ];
XX	FILE	prwbuf;
XX	register char	*cp;
XX	
XX	prwbuf._flag = _IOWRT;
XX	prwbuf._file = _NFILE;
XX	prwbuf._cnt = 32767;
XX	prwbuf._ptr = (unsigned char *)buf;
XX	prwbuf._base = (unsigned char *)buf;
XX	sprintf (fbuf, "%s: %s", "nbatcher", fmt);
XX	_doprnt (fbuf, (char *)&argp, &prwbuf);
XX	putc ('\0', &prwbuf);
XX	for (cp = buf; *cp != '\0'; cp++)
XX		putchar (*cp);

XX	exit (99);
XX}
XX#endif	/* USE_PORT_CODE */

XXclear_entry (s)
XXchar *s;
XX{
XX	register int i;

XX	for (i=0; i<sizeof(struct file_entry); *s++ = '\0', i++)
XX				;

XX}
@//E*O*F parse.c//
chmod u=rw,g=rw,o=rw parse.c
 
echo Inspecting for damage in transit...
temp=/tmp/sharin$$; dtemp=/tmp/sharout$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
      60     480    3043 README
      76     441    2526 nbatcher.1
      14      88     459 nbatcher.ctl
      81     678    3907 nbatcher.doc
      67     185    1387 Makefile
      61     159    1088 bst.c
     155     501    3094 checkwork.c
      27      52     284 define.c
      50     148    1022 logger.c
      47     103     699 main.c
     212     696    4805 nbatcher.c
     202     668    4011 parse.c
    1052    4199   26325 total
!!!
wc  README nbatcher.1 nbatcher.ctl nbatcher.doc Makefile bst.c checkwork.c define.c logger.c main.c nbatcher.c parse.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if test -s $dtemp
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0