[net.sources] nbatcher re-posting

espo@bpa.BELL-ATL.COM (Bob Esposito) (08/18/86)

	This is a re-posting of the nbatcher source for spooling
	news articles.  The fixes are mostly the inclusion of
	newlines where needed, but the most important one is to
	prevent copies of the news articles being written to the
	logfile, is some cases.


	A copy was also sent to rs@mirror (mod.sources) for archive
	purposes.

--
        //////////////////////////////////////////
        * Bob Esposito  ...espo@bpa.bell-atl.com *
        //////////////////////////////////////////

---------cut-here------------cut-here--------------cut-here--------------------
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	define.c
#	main.c
#	parse.c
#	checkwork.c
#	nbatcher.c
#	logger.c
#	nbatcher.h
#	bst.c
#	README
#	nbatcher.ctl
#	nbatcher.doc
#	nbatcher.1
#	Makefile
echo x - extracting define.c
sed 's/^X//' << 'SHAR_EOF' > define.c
X/* char	sccsid[] = "@(#)define.c 1.4 8/14/86"; */
X
X/*******************************************************
X *	define.c - global defines for nbatcher.
X *
X *	R.J. Esposito
X *	Bell of Penna.
X *	June 1986
X *
X ******************************************************/
X
X#include <stdio.h>
X#include "nbatcher.h"
X
XFILE	*lfp,
X	*tfp,
X	*log = NULL;
X
Xlong	n_bytes,
X	cu_bytes;
X
Xchar	*tfile = NULL;
X
Xshort	vflg = 0,
X	nfiles = 10;
X
Xint	fcnt = 0,
X	scnt = 0;
X
SHAR_EOF
echo x - extracting main.c
sed 's/^X//' << 'SHAR_EOF' > main.c
Xchar	sccsid[] = "nbatcher 1.4 8/14/86";
X
X/********************************************
X *
X *	main.c - for nbatcher
X *
X *	R.J. Esposito
X *	Bell of Penna.
X *	June 1986
X *
X ********************************************/
X
X#include <stdio.h>
X#include "nbatcher.h"
X
Xmain()
X{
X	int	uid, nowork;
X	FILE	*cfp;
X	char	fbuf[BUFSIZ];
X
X	uid = getuid();
X
X	if (uid && uid != NEWSUID)
X		xerror ("permission denied - not NEWSUSER\n");
X
X	if (chdir(LIBDIR) < 0)
X		xerror ("can't chdir to %s\n", LIBDIR);
X
X	if ((cfp=fopen("nbatcher.ctl", "r")) == NULL)
X		xerror ("no `batcher.ctl' file found\n");
X
X	if (isatty(0)) {
X		vflg = TRUE;
X		(void) fprintf(stderr, "%s\n", sccsid);
X	}
X
X	nowork = TRUE;
X	while ((fgets(fbuf, sizeof(fbuf), cfp)) != NULL) {
X		if (fbuf[0] == '*' || fbuf[0] == '\n')
X			continue;
X		parse_entry (fbuf);
X		if (!work_to_do())
X			continue;
X		batch_it ();
X		nowork = FALSE;
X	}
X
X	fclose (cfp);
X	fclose (tfp);
X	unlink (tfile);
X	if (vflg == TRUE && nowork == TRUE)
X		(void) fprintf(stderr, "no work to do\n");
X	exit (0);
X}
X	
SHAR_EOF
echo x - extracting parse.c
sed 's/^X//' << 'SHAR_EOF' > parse.c
X/* char	sccsid[] = "@(#)parse.c 1.4 8/14/86"; */
X
X/****************************************************************
X *
X *	parse.c - nbatcher line parser for the control file
X *
X *
X *	R.J. Esposito
X *	Bell of Penna.
X *	June 1986
X *
X ****************************************************************/
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <ctype.h>
X#include "nbatcher.h"
X
X#define MAX_BYTES	1000000L	/* max allowable bytes */
X
Xparse_entry (line)
Xchar *line;
X{
X	register char	*p;
X	short	num, upper;
X	short	lower, dash;
X	long	l_num;
X
X	upper = 23;	/* upper hour limit */
X	lower = 0;	/* lower hour limit */
X	dash = 0;
X
X	clear_entry (&ep);	/* zero out the structure */
X
X	p = (char *) ep.site;
X
X	/* get the site name and copy
X	   it to the structure */
X
X	while (*line && *line != COLON)
X		*p++ = *line++;
X	*p = '\0';
X	if (*++line == '\n' || *line == '\0')
X		xerror ("illegal number of fields\n");
X
X	/* check that its valid */
X
X	if (ep.site[0] == '\0')
X		xerror ("null site name in control file\n");
X
X	/* now, parse the hour string and check
X	   for valid syntax */
X
X	p = (char *) ep.hour;
X	while (*line && *line != COLON)
X		*p++ = *line++;
X
X	*p = '\0';
X	if (*++line == '\n' || *line == '\0')
X		xerror ("illegal number of fields\n");
X
X	if (ep.hour[0] == '\0')
X		xerror ("null hour string in control file\n");
X
X	/* now re-scan the hour in structure and
X	   weed out the badies */
X
X	if (ep.hour[0] == '*' && ep.hour[1] != '\0')
X		xerror ("invalid hour string syntax: %s\n", ep.hour);
X	else if (ep.hour[0] == '*')
X		goto h_skip;
X
X	if (strcmp(ep.hour, "off", 3) == 0 && ep.hour[3] != '\0')
X		xerror ("invalid hour string syntax: %s\n", ep.hour);
X	else if (strncmp(ep.hour, "off", 3) == 0)
X		goto h_skip;
X
X	p = (char *) ep.hour;
X	if (!isdigit(*p))
X		xerror ("non-numeric char in hour string: %c\n", *p);
X
X	while (*p) {
X		num = 0;
X		do {
X			num = num*10 + (*p - '0');
X		} while (isdigit(*++p));
X
X		if (num < lower || num > upper)
X			xerror ("illegal hour: %d\n", num);
X
X		if (!*p)
X			break;
X
X		if (*p == '-' && dash)
X			xerror ("syntax error in hour field\n");
X		else if (*p == '-')
X			dash = TRUE;
X
X		if (*p != ',' && *p != '-')
X			xerror ("non-numeric char in hour string: %c\n", *p);
X		else if (!isdigit(*++p))
X			xerror ("syntax error in hour field\n");
X
X	}
X
X	/* now that thats over with, let do the compression
X	   field.  Only 9-16 is allowed, except a null field
X	   indicates no compression for this site. */
X
Xh_skip:
X	num = 0;
X	while (*line && *line != COLON) {
X		if (!isdigit(*line))
X			xerror ("non-numeric in compression field\n");
X		num = num*10 + (*line++ - '0');
X	}
X	if (*++line == '\n' || *line == '\0')
X		xerror ("illegal number of fields\n");
X
X	if (num != 0 && (num < 9 || num > 16))
X		xerror ("illegal compression bits: %d\n", num);
X
X	ep.c_bits = num;
X
X	/* now check the max. bytes for UUCP queue.
X	   Note: There is a max. allowable # of bytes
X		 here, set at 1MB.  Change it at your
X		 own risk.
X	*/
X
X	l_num = 0;
X	while (*line && *line != COLON) {
X		if (!isdigit(*line))
X			xerror ("non-numeric in max. bytes field\n");
X
X		l_num = l_num*10 + (*line++ - '0');
X	}
X
X	if (l_num > MAX_BYTES)
X		xerror ("%ld max. bytes exceeds allowable maximun\n", l_num);
X
X	if (l_num != 0)
X		ep.m_bytes = l_num;
X	else
X		ep.m_bytes = DFL_BYTES;
X
X	/* and finally the command line (if there is one) */
X
X	p = (char *) ep.command;
X
X	if (*++line != '\n' && *line != '\0') {
X		while (*line && *line != '\n')
X			*p++ = *line++;
X
X		*p = '\0';
X	}
X}
X
X#ifdef USE_PORT_CODE
Xxerror (fmt, a1, a2)
Xchar *fmt;
Xchar *a1, *a2;
X{
X	char	buf[BUFSIZ];
X
X	sprintf (buf, fmt, a1, a2);
X	printf ("\nnbatcher: %s\n", fmt);
X	exit (99);
X}
X
X#else
Xxerror (fmt, argp)
Xchar *fmt;
Xint argp;
X{
X	char	buf[BUFSIZ];
X	char	fbuf[BUFSIZ];
X	FILE	prwbuf;
X	register char	*cp;
X	
X	prwbuf._flag = _IOWRT;
X	prwbuf._file = _NFILE;
X	prwbuf._cnt = 32767;
X	prwbuf._ptr = (unsigned char *)buf;
X	prwbuf._base = (unsigned char *)buf;
X	sprintf (fbuf, "\n%s: %s", "nbatcher", fmt);
X	_doprnt (fbuf, (char *)&argp, &prwbuf);
X	putc ('\0', &prwbuf);
X	for (cp = buf; *cp != '\0'; cp++)
X		putchar (*cp);
X
X	exit (99);
X}
X#endif	/* USE_PORT_CODE */
X
Xclear_entry (s)
Xchar *s;
X{
X	register int i;
X
X	for (i=0; i<sizeof(struct file_entry); *s++ = '\0', i++)
X				;
X
X}
SHAR_EOF
echo x - extracting checkwork.c
sed 's/^X//' << 'SHAR_EOF' > checkwork.c
X/* char	sccsid[] = "@(#)checkwork.c 1.4 8/14/86"; */
X
X/************************************************************
X *
X *	checkwork.c - look to see if there's any work
X *		      to do for a site.
X *
X *	R.J. Esposito
X *	Bell of Penna.
X *	June 1986
X *
X ************************************************************/
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/utsname.h>
X#include <sys/dir.h>
X#include <ctype.h>
X#include <time.h>
X#include "nbatcher.h"
X
Xwork_to_do ()
X{
X	register char	*p;
X	struct tm	*localtime(), *tp;
X	struct stat	st;
X	char	buf[BUFSIZ];
X	long	time(), clock;
X	int	hour;
X	short	num, upper, lower;
X
X	sprintf (buf, "%s/%s", BATCHDIR, ep.site);
X
X	if (stat(buf, &st) < 0)
X		xerror ("bad stat on %s\n", buf);
X
X	/* if the size of the batch file is
X	   zero, return FALSE
X	*/
X
X	if (st.st_size == 0)
X		return (FALSE);
X
X	/* now see if it time to do anything */
X
X	clock = time ((long *)0);
X	tp = localtime (&clock);
X	hour = tp->tm_hour;
X
X	p = (char *) ep.hour;
X
X	if (*p == '*')		/* match any hour */
X		return (check_uucp());
X
X	if (strncmp(p, "off", 3) == 0)	/* just what it says, off */
X		return (FALSE);
X
X	/* parse thru hour field to see if
X	   this is the hour to do work */
X
X	num = 0;
X	do {
X		num = num*10 + (*p - '0');
X	} while (isdigit(*++p));
X	if (num == hour)
X		return (check_uucp());
X
X	if (*p == '-') {
X		lower = num;
X		p++;
X		num = 0;
X		do {
X			num = num*10 + (*p - '0');
X		} while (isdigit(*++p));
X		upper = num;
X
X		if (lower < upper) {	/* normal hour range */
X			if (hour >= lower && hour <= upper)
X				return (check_uucp());
X		} else if (lower > upper) {	/* 24 hr. cycle thru */
X			if (hour >= lower || hour <= upper)
X				return (TRUE);
X		} else
X			return (FALSE);
X	}
X
X	if (*p == ',') {
X		p++;
X		while (*p) {
X			num = 0;
X			do {
X				num = num*10 + (*p - '0');
X			} while (isdigit(*++p));
X			if (num == hour)
X				return (check_uucp());
X			p++;
X		}
X	}
X
X	return (FALSE);
X}
X
X/*	If check_uucp cannot find the remote site
X *	directory, just bypass the byte counting
X *	routine.  This is necessary because the
X *	uucpcleanup daemon, on some sites, removes
X *	the site directory when there's nothing there.
X */
X
Xcheck_uucp()
X{
X	struct utsname	utsn;
X	struct direct	dp;
X	struct stat	st;
X	FILE	*dfp;
X	char	u_name[9], buf[80];
X	short	prefix_len;
X
X	if (uname(&utsn) < 0)
X		xerror ("can't get local nodename\n");
X
X	sprintf (buf, "%s/%s", UUCPDIR, ep.site);
X	if (chdir(buf) < 0) {
X	   fprintf (stderr, "\nnbatcher: can't chdir to %s - bypassing UUCP check\n", buf);
X	   return (TRUE);
X	}
X
X	if ((dfp=fopen(".", "r")) == NULL) {
X	   fprintf (stderr, "\nnbatcher: fopen error on %s - bypassing UUCP check\n", UUCPDIR);
X	   return (TRUE);
X	}
X
X	sprintf (buf, "D.%s", utsn.nodename);
X	prefix_len = (short) strlen(buf);
X	n_bytes = 0;
X	while ((fread((char *)&dp, sizeof(dp), 1, dfp)) == 1) {
X		if (dp.d_ino == 0 || dp.d_name[0] == '.')
X			continue;
X		if (strncmp(dp.d_name, buf, prefix_len))
X			continue;
X		if (stat(dp.d_name, &st) < 0) {
X		  fprintf (stderr, "\nnbatcher: bad stat on UUCP_file %s - bypassing\n", dp.d_name);
X		  continue;
X		}
X		n_bytes += st.st_size;
X		if (n_bytes > ep.m_bytes) {
X			fclose (dfp);
X			return (FALSE);
X		}
X	}
X	fclose (dfp);
X	if (chdir(LIBDIR) < 0)
X		xerror ("can't chdir back to %s\n", LIBDIR);
X
X	return (TRUE);
X}
SHAR_EOF
echo x - extracting nbatcher.c
sed 's/^X//' << 'SHAR_EOF' > nbatcher.c
X/* char	sccsid[] = "@(#)nbatcher.c 1.4 8/14/86"; */
X
X/****************************************************
X *
X *	nbatcher.c - where it really happens.
X *
X *	R.J. Esposito
X *	Bell of Penna.
X *	June 1986
X *
X ***************************************************/
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <time.h>
X#include "nbatcher.h"
X
Xbatch_it ()
X{
X	struct stat	st;
X	FILE	*bfp, *afp;
X	char	fbuf[BUFSIZ], lckfile[40];
X	char	tbuf[80];
X	short	count;
X	int	c;
X
X	if (chdir(BATCHDIR) < 0)
X		xerror ("can't chdir to %s\n", BATCHDIR);
X
X	/* we create a lock file for two purposes,
X	   first to make sure a previous nbatcher
X	   didn't blowup and leave the lock file
X	   laying around, and second to put the
X	   remaining news article filenames when
X	   we go over the max UUCP bytes and there's
X	   still files remaining for batching.
X	*/
X
X	sprintf (lckfile, ".%s.lock", ep.site);
X	if (!access(lckfile, 0))
X		xerror ("lockfile already exists for %s\n", ep.site);
X
X	if ((lfp=fopen(lckfile, "w")) == NULL)
X		xerror ("can't create lockfile for %s\n", ep.site);
X
X	/* now that we've locked ourselves for this site,
X	   lets carry on */
X
X	if ((bfp=fopen(ep.site, "r")) == NULL)
X		xerror ("can't open %s/%s for reading\n", BATCHDIR, ep.site);
X
X	if (tfile == NULL) {
X		tfile = mktemp("/tmp/bnewsXXXXXX");
X		if ((tfp=fopen(tfile, "w")) == NULL)
X			xerror ("can't open %s for writing\n", tfile);
X	}
X
X	count = fcnt = scnt = 0;
X	cu_bytes = 0;
X	while ((fgets(fbuf, sizeof(fbuf), bfp)) != NULL) {
X		fbuf[strlen(fbuf)-1] = '\0';	/* remove the newline */
X		if ((afp=fopen(fbuf, "r")) == NULL) {
X		   fprintf (stderr, "\nbypassing article %s: can't read it\n",
X			    fbuf);
X			continue;
X		}
X		if (fstat(fileno(afp), &st) < 0)
X			xerror ("fstat failed on %s\n", fbuf);
X
X		cu_bytes += st.st_size;
X
X		/* if the max byte count is exceeded,
X		   save the remaining files for later */
X
X		if ((cu_bytes + n_bytes) > ep.m_bytes) {
X			fprintf (lfp, "%s\n", fbuf); /* put the '\n' back */
X			while ((fgets(fbuf, sizeof(fbuf), bfp)) != NULL)
X				fputs (fbuf, lfp);
X			fclose (bfp);
X			fclose (lfp);
X			fclose (afp);
X			unlink (ep.site);
X			if (link(lckfile, ep.site) < 0)
X			   xerror ("can't link lockfile to %s\n", ep.site);
X			unlink (lckfile);
X			chown (ep.site, NEWSUID, NEWSGID);
X			if (count)
X				spoolit ();
X			if (cu_bytes - st.st_size)
X				log_it (cu_bytes - st.st_size);
X
X			return;
X		}
X		sprintf (tbuf, "#! rnews %ld\n", st.st_size);
X		fputs (tbuf, tfp);
X		while ((c=getc(afp)) != EOF)
X			putc (c, tfp);
X		fclose (afp);
X
X		if (++count == nfiles) {
X			spoolit ();
X			count = 0;
X		}
X		fcnt++;
X	}
X
X	/* The final spool if lest than nfiles
X	   is encountered.  Then zero out the
X	   batchfile and unlink the lock file */
X
X	spoolit ();
X	fclose (bfp);
X	fclose (lfp);
X	close (creat(ep.site, 0664));
X	chown (ep.site, NEWSUID, NEWSGID);
X	unlink (lckfile);
X
X	/* here we log what we've done, and
X	   if vflg is set, a copy to stdout
X	   as well */
X
X	log_it (0);
X	if (chdir(LIBDIR) < 0)
X		xerror ("can't chdir back to %s\n", LIBDIR);
X
X}
X
Xspoolit ()
X{
X	struct stat	st;
X	char	cmd[BUFSIZ], cfile[80];
X	FILE	*pfp;
X	int	c;
X
X	fclose (tfp);
X	stat (tfile, &st);
X
X	/* if for some reason the temp file
X	   is zero, just return */
X
X	if (st.st_size == 0)
X		return;
X
X	/* if ep.c_bits is set use COMPRESS to compress
X	   the temp file first
X	*/
X
X	if (ep.c_bits) {
X		sprintf (cmd, "%s -b%d %s", COMPRESS, ep.c_bits, tfile);
X		if (system(cmd) != 0)
X			xerror ("system(%s) failed\n", cmd);
X
X		strcpy (cfile, tfile);
X		strcat (cfile, ".Z");
X		if ((tfp=fopen(cfile, "r")) == NULL)
X			xerror ("can't open %s for reading\n", cfile);
X
X		/* if ep.command has a specific command
X		   for UUCP spooling, use it.  If not,
X		   use UUX.
X		*/
X
X		if (ep.command[0] != '\0')
X			strcpy (cmd, ep.command);
X		else
X			sprintf (cmd, "%s %s!rnews", UUX, ep.site);
X
X		/* now popen the command for writing
X		   and send it the contents of tempfile */
X
X		if ((pfp=popen(cmd, "w")) == NULL)
X			xerror ("popen failed on %s\n", cmd);
X
X		/********************************************
X		 * for version 2.10.3 and above,
X		 * prepend `#! cunbatch'.
X		 *
X		 * NOTE: The remote site MUST be able to
X		 *       except this format, or it will
X		 *       be lost!!!
X		 *******************************************/
X
X		fputs ("#! cunbatch\n", pfp);
X		while ((c=getc(tfp)) !=  EOF)
X			putc (c, pfp);
X
X		pclose (pfp);
X		fclose (tfp);
X		unlink (cfile);
X	} else {			/* regular batching here */
X		if ((tfp=fopen(tfile, "r")) == NULL)
X			xerror ("can't open %s for reading\n", tfile);
X
X		/* if ep.command has a specific command
X		   for UUCP spooling, use it.  If not,
X		   use UUX.
X		*/
X
X		if (ep.command[0] != '\0')
X			strcpy (cmd, ep.command);
X		else
X			sprintf (cmd, "%s %s!rnews", UUX, ep.site);
X
X		if ((pfp=popen(cmd, "w")) == NULL)
X			xerror ("popen failed on %s\n", cmd);
X
X		while ((c=getc(tfp)) != EOF)
X			putc (c, pfp);
X
X		pclose (pfp);
X		fclose (tfp);
X	}
X	if ((tfp=fopen(tfile, "w")) == NULL)
X		xerror ("can't re-open %s\n", tfile);
X
X	scnt++;
X}
SHAR_EOF
echo x - extracting logger.c
sed 's/^X//' << 'SHAR_EOF' > logger.c
X/* char	sccsid[] = "@(#)logger.c 1.4 8/14/86"; */
X
X/***************************************************
X *
X *	logger.c - log info about nbatcher
X *
X *	R.J. Esposito
X *	Bell of Penna.
X *	June 1986
X *
X **************************************************/
X
X#include <stdio.h>
X#include <time.h>
X#include "nbatcher.h"
X
Xlog_it (bytes)
Xlong	bytes;
X{
X	struct tm	*localtime(), *tp;
X	long	time(), clock;
X	char	logfile[80], buf[BUFSIZ];
X	char	pbuf[BUFSIZ];
X
X	sprintf (logfile, "%s/%s", LIBDIR, "nbatcher.log");
X	if (log == NULL) {
X		if ((log=fopen(logfile, "a")) == NULL)
X		   fprintf (stderr, "\ncan't append to logfile\n");
X	}
X
X	if (log != NULL)
X		rewind (log, 0L, 2);	/* just incase */
X
X	clock = time ((long *)0);
X	tp = localtime (&clock);
X	sprintf (buf, "%.2d/%.2d %.2d:%.2d %s: %d %s batched, %d %s queued\n",
X	   tp->tm_mon+1, tp->tm_mday, tp->tm_hour, tp->tm_min, ep.site,
X	   fcnt, (fcnt==1 ? "file" : "files"), scnt,
X	   (scnt==1 ? "file" : "files"));
X
X	if (bytes)
X	   sprintf (pbuf, "%s\tmax bytes reached.  UUCP bytes was %ld, byte count = %ld\n",
X			buf, n_bytes, bytes);
X	else
X		sprintf (pbuf, "%s", buf);
X
X	if (vflg)
X		fprintf (stdout, "%s",pbuf);
X
X	if (log != NULL)
X		fputs (pbuf, log);
X
X}
SHAR_EOF
echo x - extracting nbatcher.h
sed 's/^X//' << 'SHAR_EOF' > nbatcher.h
X/* char	sccsid[] = "@(#)nbatcher.h 1.4 8/14/86"; */
X
X/******************************************************
X *	nbatcher.h - defines for nbatcher source
X *
X *
X *	R.J. Esposito
X *	Bell of Penna.
X *	June 1986
X *
X *****************************************************/
X
X/* things you might want to change */
X
X#define UUCPDIR		"/usr/spool/uucp"	/* uucp spool directory */
X#define COMPRESS	"/usr/bin/compress -F -q" /* where compress resides */
X#define UUX		"/usr/bin/uux - -r"	/* default command */
X#define DFL_BYTES	100000L			/* default max. UUCP bytes */
X#define NEWSUID		47			/* USENET used id */
X#define NEWSGID		80			/* USENET group id */
X
X/* things you shouldn't change */
X
X#define FALSE		0
X#define TRUE		1
X#define COLON		':'
X
Xstruct file_entry {		/* structure of control file */
X	char	site[30];	/* name of remote site */
X	char	hour[80];	/* string for hour to batch */
X	short	c_bits;		/* # of compression bits */
X	long	m_bytes;	/* max. # of bytes on UUCP queue */
X	char	command[128];	/* command string */
X} ep;
X
Xextern FILE	*lfp,			/* lockfile pointer */
X		*tfp,			/* tempfile pointer */
X		*log;			/* logfile pointer */
X
Xextern long	n_bytes;		/* # of bytes already on UUCP queue */
Xextern long	cu_bytes;		/* cumculative bytes of batch files */
X
Xchar	*mktemp(),
X	*strcpy(),
X	*strcat();
X
Xextern char	*tfile;			/* temp file */
X
Xextern short	vflg,			/* verbose flag */
X		nfiles;			/* number of news articles per
X					   UUCP batch file */
X
Xint	system();
X
Xextern int	fcnt,			/* number of files batched */
X		scnt;			/* spool count */
SHAR_EOF
echo x - extracting bst.c
sed 's/^X//' << 'SHAR_EOF' > bst.c
X/* char	sccsid[] = "@(#)bst.c 1.4 8/14/86"; */
X
X/****************************************************************
X *	bst.c - a utility for indicating how many
X *		news articles are ready for batching
X *		for each site in the BACTHDIR directory.
X *
X *	R.J. Esposito
X *	Bell of Penna.
X *	June 1986
X *
X ****************************************************************/
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X
X
Xchar buf[512];
XFILE *dd;
Xstruct direct dp;
Xstruct stat st;
X
Xmain()
X{
X	int fd, j;
X	int bcnt, lcnt;
X
X	if(chdir(BATCHDIR) != 0) {
X		perror(BATCHDIR);
X		exit(1);
X	}
X
X	if((dd=fopen(".", "r")) == NULL) {
X		printf("can't open %s\n", BATCHDIR);
X		exit(1);
X	}
X
X	while((fread((char *)&dp, sizeof(dp), 1, dd)) == 1) {
X		if(dp.d_ino == 0 || dp.d_name[0] == '.')
X			continue;
X		if(stat(dp.d_name, &st) != 0) {
X			printf("can't stat %s\n", dp.d_name);
X			exit(1);
X		}
X		if(st.st_size <= 0 )
X			continue;
X		if((fd=open(dp.d_name, 0)) < 0) {
X			printf("can't open %s\n", dp.d_name);
X			exit(1);
X		}
X		lcnt = 0;
X		while((bcnt=read(fd,buf,512)) > 0) {
X			for(j=0; j<=bcnt; j++)
X				if(buf[j] == '\n')
X					lcnt += 1;
X		}
X		close(fd);
X		printf("%s\t  %d article", dp.d_name, lcnt);
X		printf("%c\n", lcnt > 1 ? 's' : ' ');
X	}
X}
SHAR_EOF
echo x - extracting README
sed 's/^X//' << 'SHAR_EOF' > README
XThe program provides a better way to handling batching of USENET news
Xto neighboring sites.  IT works with 2.10B and later, including
X2.10.3B, which provides compression batching.
X
XPlease send any questions and bug reports to me (bpa!espo) for
Xdistribution to the net.  This software is public domain, and cannot be
Xsold for any profit.
X
XNbatcher was written for UNIX (trademark of AT&T) System V, but should
Xbe compatible with most version currently being used today.  It is
Xpresently running on a VAX (trademark of DEC) 11/780 feeding 5 remote
Xsites with news.  Two important issues MUST be noted here.
X
XSince nbatcher checks the UUCP directory for each site listed in the
Xcontrol file, directory configuration should be UUCPDIR/sitename, e.g.
X/usr/spool/uucp/foo for site foo.  But since not everyone is generic,
XI've allowed nbatcher to skip over the UUCP byte counting routine if
Xthis is not so.  In addition to this, some uucpcleanup daemons remove
Xthe site directory if there's nothing spooled there.  When this
Xhappens, nbatcher will notify you of this and continue on.
X
XThe other issue is that you MUST configure your NEWSLIB/sys file using the
Xbatch syntax as described in the USENET Installation document.  Field 3
Xshould contain the `F' flag indicating a batched transmission for that
Xsite and field 4 should contain the filename where news article's full
Xpathname will be appended to.  Nbatcher requires that the filename in
Xfield 4 match the sitename in the control file.  For example, the entry:
X	foo:net,mod,usa,na,to.foo:F:/usr/spool/batchnews/foo
Xsays that all news articles going to site `foo' will be batched, using
Xthe file `foo' in /usr/spool/batchnews.  Nbatcher's control file entry:
X	foo:3,14,22:16:150000:
Xwill get the news article's full pathname from /usr/spool/batchnews/foo
Xfor batching.  This requirement MUST be adhered to for nbatcher to work
Xcorrectly.  I believe most sites that batch news use this type of
Xconfiguration, since its easy to keep track of a remote site's work.
XNote that the name of the directory "/usr/spool/batchnews" is set
Xin the Makefile.
X
XThe manual page describes the control file, as does nbatcher.doc.  Note
Xthat even though nbatcher indicates max bytes has been reached, if that
Xsite gets compressed batches, the next time nbatcher runs for that site
Xwork could get scheduled.
X
XAlso included is a utility program called "bst," BatchSTatus, which
Xshows who many news articles are currently in the batch file for each
Xsystem.  Just type "make bst" for that and copy it to where you want,
Xusually LIBDIR.  Note that bst does reads on the directory structure
Xdirectly.
X
XInstallation is simple:  look at the first few #define's in nbatcher.h;
Xalso edit the Makefile for the appropriate BATCHDIR and LIBDIR for your
Xsite.  Type "make" or "make install."
X
XNOTE:  Read the comments in parse.c concerning MAX_BYTES.  This is the
Xmaximum amount of bytes per site that's allowed in the control file for
Xm_bytes.  Change it at your own risk!
X
X    Bob Esposito   bpa!espo
X    Bell of Penna.
SHAR_EOF
echo x - extracting nbatcher.ctl
sed 's/^X//' << 'SHAR_EOF' > nbatcher.ctl
X*
X*	NBATCHER.CTL
X*	Edit and install in your NEWSLIB directory.
X*
X*  Comments start with *; data lines look like:
X*	site:hours:bits:queue_size:command
X*  Where
X*	site		= name of the remote site
X*	hour		= when to do work (* is all, off is never, 22-4 is ok)
X*	bits		= passed on to compress via -b; null gets default
X*	queue_size	= Max # bytes allowed in UUCP queue before postponing
X*	command		= optional command line
X*
X*  See manpage and README for more info.
SHAR_EOF
echo x - extracting nbatcher.doc
sed 's/^X//' << 'SHAR_EOF' > nbatcher.doc
X
XDETAILED DESCRIPTION OF nbatcher.ctl FILE
X
X			   Document for nbatcher
X
XAs distributed, the nbatcher.ctl contains a terse summary of its
Xformat.  For each site you feed news to, providing that site uses the
X":F:BATCHDIR/site" batching syntax in the sys file, a corresponding
Xentry should exist in nbatcher.ctl  Each line is a five-field,
Xcolon-separated entry indicating what to do for that site.
X
XThe format is:
X	site:hour:c_bits:m_bytes:command
XThe site field is the name of the UUCP site that will get the batched
Xnews.  There MUST be a file in the BATCHDIR (in my case
X/usr/spool/batchnews) matching this name that contains a listing of
Xfiles to batch.  This is where rnews puts the news article filenames
Xwhen batching is used.  Nbatcher will complain about mismatches.
X
XNext is the hour field.  The syntax directly imitates the crontab entry
Xfor hour.  If hour = "*", than assume a match for every hour.  If hour
X= "off", then no work will ever be spooled for this site.  (This is the
Xonly difference from crontab).  You can specify a specific hour, like 8
Xor 09 or 22.  Or a range of hours, like 10-15, meaning check for work
Xfrom 10 AM thru 3 PM.  (you can also cycle thru a 24 hr. period by
Xsaying 22-4, which will assume a match for 10PM thru 4AM).  Also, hours
Xcomma separated like 7,14,21, says check for work at 7 AM, 4PM and 9PM
Xonly.  This gives lots of flexibility for sites that feed multiple
Xremotes.
X
XNext is the c_bits field, which has a value of 9-16, or may be left
Xblank.  If non-null, this field is directly passed on to compress with
Xthe -b flag.  If it is null, then no compression is done for this
Xsite.
X
XThe m_bytes field is the maximum number of bytes allowed on the UUCP
Xqueue at any time.  It defaults to 100K bytes, and if its greater than
X1MB (see parse.c about MAX_BYTES), than it uses 1MB as the default
Xnumber of bytes.
X
XWhat nbatcher does is first check the UUCPDIR for that site and sums up
Xthe number of bytes already on-queue.  The size of each news article is
Xaccumulated prior to batching to the tempfile, and this accumulation +
Xthe UUCP on-queue bytes are checked to see if they surpass the m_bytes
Xvalue.  If not, things proceed normally.  But if it would exceed the
Xmax value, then spool whats already been batched, and save the
Xremainder of the articles for the next scheduled batch.
X
XThis is so noted in the nbatcher.log file, showing the UUCP bytes that
Xwere on-queue (if any), and the number of bytes that were spooled.
XAlso, if nbatcher is run from a terminal instead of from cron, a copy
Xof what gets logged is sent to the terminal.
X
XThe last field is the command field.  This optional field is used for
Xspecific UUCP command execution.  As written, it defaults to "uux - -r
Xsite!rnews".  Since my uux doesn't require the -z option for rnews, I
Xleft it out of the UUX define in nbatcher.h  This can easily be changed
Xfor your own taste, or just use the command field.
X
XExample:
X	site-A:3,10:16:450000:
XCheck for work for site-A at 3AM and 10AM.  Use compress with
X16 bits and only spool up to 450K bytes, using "uux - -r site-A!rnews".
X
XAnother example:
X	foo:23-4::300000:uux - -r -z foo!rnews
XCheck for work for foo between 11PM and 4AM.  No compression is used
Xand spool up to 300K bytes using the command field.
X
XThis format should help ease UUCP congestion on the local site for
Xnews.  I currently feed 5 remote sites, compressed and non-compressed
Xformats, and have noticed a vast improvement in disk space as well as
Xout port availability for UUCP.  It has allowed me to check for work
Xfor all sites every hour, since there's a limit on how much gets
Xqueued.  And if the remote doesn't answer on the hourly UUCP poll, so
Xwhat!!  Nbatcher just won't spool any more until the m_bytes limit
Xon-queue is reduced.
X
XThe best results is to run nbatcher, via cron as the USENET
Xadministrator (NEWSUID), at least once per hour.
X
XNbatcher was designed to weed out any illegal syntax in the control
Xfile, but its not bug-proof, so use some discretion.
X
XBob Esposito
XBell of Penna.
XJune 1986
SHAR_EOF
echo x - extracting nbatcher.1
sed 's/^X//' << 'SHAR_EOF' > nbatcher.1
X.TH NBATCHER 1 LOCAL
X.SH NAME
Xnbatcher  \- new batching system for news
X.SH SYNOPSIS
X.B nbatcher
X.IR "(run out of " cron ".)"
X.SH DESCRIPTION
X.PP
X.I Nbatcher
Xis a program designed to send batched USENET data out
Xin an orderly and controlled fashion, while providing alternative
Xtransmission methods.  As such, it is a replacement for
X.I csendbatch
Xand the like, which typically require many entries in
X.IR crontab .
X.I Nbatcher
Xis intended to be run hourly out of
X.I cron
Xas the USENET administrator (NEWSID).
X.PP
X.I Nbatcher
Xscans the file
X.I nbatcher.ctl
Xin the NEWSLIB directory to determine if work should be spooled
Xfor a system.  In the control file, lines starting with asterisks
Xare ignored; data lines are comprised of five colon\-separated
Xfields:
X.RS
Xsite:hour:bits:queue_size:command
X.RE
X.TP
XThe ``site'' field is the USENET neighbor; it is the same as the site in the
Xnews
X.I sys
Xfile.
X.TP
XThe ``hours'' field is patterned after
X.IR cron's.
XIf the hour is ``off,'' no work is spooled for the site.  An hour of
X``*'' matches every hour.  It is also possible to specify specific
Xhours, (e.g., 8, 09, or 22), a comma-separated list (e.g., 8,09,22),
Xor a twenty\-four range, like 10-\15 for 10am through 3pm and 22\-4,
Xfor 10pm through 4am.
X.TP
XThe ``bits'' field specifies the number of bits to use in compression;
Xit should be a number between nine and 16, inclusive, or null.  If
Xa number is specified, it is passed on to the
X.I compress
Xprogram via the ``\-b'' flag.
X.TP
XThe ``queue_size'' field specifies the maximum number of bytes allowed
Xin the UUCP queue for this site.  The default is 100K.
XThe UUCP queue size is determined by lookin in the
X.IR /usr/spool/uucp/ site
Xdirectory; if it doesn't exist, the check is bypassed.  If there is
Xdata in the UUCP queue,
X.I nbatcher
Xwill only queue up as many USENET transfers as will fit within the limit
Xspecified by the ``queue_size'' field.
X.TP
XThe ``command'' field is used to specific the UUCP command that should
Xbe used to queue the job; the default is ``uux \- \-r site!rnews'';
Xnote the absence of the ``\-z'' flag.
X.PP
XTo set up a USENET neighbor to be controlled by nbatcher, the news
X.I sys
Xentry for the neighbor must be modified to specify the ``F'' flag,
Xand the file used to contain the article names must be
X.RI BATCHDIR/sysname ,
Xwhere BATCHDIR is set in the Makefile as distributed.
X.SH "FILES"
XNEWSLIB/nbatcher.log
X.br
X.in +1i
Xa logfile of failures, postponements, etc.
X.in -1i
X.SH BUGS
XParsing of the control file is fairly robust, but not perfect.
SHAR_EOF
echo x - extracting Makefile
sed 's/^X//' << 'SHAR_EOF' > Makefile
X#
X#	Makefile for nbatcher Version 1.4
X#
X#	R.J. Esposito
X#	Bell of Penna.
X#	June 1986
X#
X
X#	You MUST define BATCHDIR as the place where USENET
X#	puts the articles to be batched.
X#
X#	LIBDIR is where you USENET library is and also
X#	MUST be defined.
X#
X#	Define PCODE for parse.c if you don't have/can't use
X#	_doprnt.
X
XBATCHDIR = /usr/spool/batchnews
XLIBDIR = /misc/lib/usenet
X
XCFLAGS = -O -c
XLFLAGS = -s
XDFLAGS = -DBATCHDIR='"$(BATCHDIR)"' -DLIBDIR='"$(LIBDIR)"'
X# PCODE = -DUSE_PORT_CODE
X
XOBJS = define.o main.o parse.o checkwork.o nbatcher.o logger.o
X
XSRC = define.c main.c parse.c checkwork.c nbatcher.c logger.c nbatcher.h bst.c
XDOCS = README nbatcher.ctl nbatcher.doc nbatcher.1
X
Xall: $(OBJS)
X	$(CC) $(DFLAGS) $(LFLAGS) -o nbatcher $(OBJS)
X	chmod 0755 nbatcher
X
Xinstall: all
X	cp nbatcher $(LIBDIR)
X	chmod 0755 $(LIBDIR)/nbatcher
X	cp nbatcher.ctl $(LIBDIR)/nbatcher.ctl
X	chmod 0644 $(LIBDIR)/nbatcher.ctl
X
Xdefine.o: nbatcher.h define.c
X	$(CC) $(CFLAGS) $(DFLAGS) define.c
X
Xmain.o: nbatcher.h main.c
X	$(CC) $(CFLAGS) $(DFLAGS) main.c
X
Xparse.o: nbatcher.h parse.c
X	$(CC) $(CFLAGS) $(DFLAGS) $(PCODE) parse.c
X
Xcheckwork.o: nbatcher.h checkwork.c
X	$(CC) $(CFLAGS) $(DFLAGS) checkwork.c
X
Xnbatcher.o: nbatcher.h nbatcher.c
X	$(CC) $(CFLAGS) $(DFLAGS) nbatcher.c
X
Xlogger.o: nbatcher.h logger.c
X	$(CC) $(CFLAGS) $(DFLAGS) logger.c
X
Xbst: bst.c
X	$(CC) $(CFLAGS) $(DFLAGS) bst.c
X	$(CC) $(LFLAGS) -o bst bst.o
X
Xclean:
X	rm -f *.o *.shar
X
Xclobber: clean
X	rm -f nbatcher
X
X
Xshar:
X	shar -v $(SRC) $(DOCS) Makefile > nbatcher.shar
SHAR_EOF
exit

espo@bpa.BELL-ATL.COM (Bob Esposito) (08/20/86)

        This is a re-posting of the nbatcher source for spooling
        news articles.  The fixes are mostly the inclusion of
        newlines where needed, but the most important one is to
        prevent copies of the news articles being written to the
        logfile, is some cases.


        A copy was also sent to rs@mirror (mod.sources) for archive
        purposes.

--
        //////////////////////////////////////////
        * Bob Esposito  ...espo@bpa.bell-atl.com *
        //////////////////////////////////////////

---------cut-here------------cut-here--------------cut-here--------------------
#!/bin/sh
# shar: Shell Archiver
#       Run the following text with /bin/sh to create:
#       define.c
#       main.c
#       parse.c
#       checkwork.c
#       nbatcher.c
#       logger.c
#       nbatcher.h
#       bst.c
#       README
#       nbatcher.ctl
#       nbatcher.doc
#       nbatcher.1
#       Makefile
echo x - extracting define.c
sed 's/^X//' << 'SHAR_EOF' > define.c
X/* char        sccsid[] = "@(#)define.c 1.4 8/14/86"; */
X
X/*******************************************************
X *     define.c - global defines for nbatcher.
X *
X *     R.J. Esposito
X *     Bell of Penna.
X *     June 1986
X *
X ******************************************************/
X
X#include <stdio.h>
X#include "nbatcher.h"
X
XFILE   *lfp,
X       *tfp,
X       *log = NULL;
X
Xlong   n_bytes,
X       cu_bytes;
X
Xchar   *tfile = NULL;
X
Xshort  vflg = 0,
X       nfiles = 10;
X
Xint    fcnt = 0,
X       scnt = 0;
X
SHAR_EOF
echo x - extracting main.c
sed 's/^X//' << 'SHAR_EOF' > main.c
Xchar   sccsid[] = "nbatcher 1.4 8/14/86";
X
X/********************************************
X *
X *     main.c - for nbatcher
X *
X *     R.J. Esposito
X *     Bell of Penna.
X *     June 1986
X *
X ********************************************/
X
X#include <stdio.h>
X#include "nbatcher.h"
X
Xmain()
X{
X       int     uid, nowork;
X       FILE    *cfp;
X       char    fbuf[BUFSIZ];
X
X       uid = getuid();
X
X       if (uid && uid != NEWSUID)
X               xerror ("permission denied - not NEWSUSER\n");
X
X       if (chdir(LIBDIR) < 0)
X               xerror ("can't chdir to %s\n", LIBDIR);
X
X       if ((cfp=fopen("nbatcher.ctl", "r")) == NULL)
X               xerror ("no `batcher.ctl' file found\n");
X
X       if (isatty(0)) {
X               vflg = TRUE;
X               (void) fprintf(stderr, "%s\n", sccsid);
X       }
X
X       nowork = TRUE;
X       while ((fgets(fbuf, sizeof(fbuf), cfp)) != NULL) {
X               if (fbuf[0] == '*' || fbuf[0] == '\n')
X                       continue;
X               parse_entry (fbuf);
X               if (!work_to_do())
X                       continue;
X               batch_it ();
X               nowork = FALSE;
X       }
X
X       fclose (cfp);
X       fclose (tfp);
X       unlink (tfile);
X       if (vflg == TRUE && nowork == TRUE)
X               (void) fprintf(stderr, "no work to do\n");
X       exit (0);
X}
X
SHAR_EOF
echo x - extracting parse.c
sed 's/^X//' << 'SHAR_EOF' > parse.c
X/* char        sccsid[] = "@(#)parse.c 1.4 8/14/86"; */
X
X/****************************************************************
X *
X *     parse.c - nbatcher line parser for the control file
X *
X *
X *     R.J. Esposito
X *     Bell of Penna.
X *     June 1986
X *
X ****************************************************************/
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <ctype.h>
X#include "nbatcher.h"
X
X#define MAX_BYTES      1000000L        /* max allowable bytes */
X
Xparse_entry (line)
Xchar *line;
X{
X       register char   *p;
X       short   num, upper;
X       short   lower, dash;
X       long    l_num;
X
X       upper = 23;     /* upper hour limit */
X       lower = 0;      /* lower hour limit */
X       dash = 0;
X
X       clear_entry (&ep);      /* zero out the structure */
X
X       p = (char *) ep.site;
X
X       /* get the site name and copy
X          it to the structure */
X
X       while (*line && *line != COLON)
X               *p++ = *line++;
X       *p = '\0';
X       if (*++line == '\n' || *line == '\0')
X               xerror ("illegal number of fields\n");
X
X       /* check that its valid */
X
X       if (ep.site[0] == '\0')
X               xerror ("null site name in control file\n");
X
X       /* now, parse the hour string and check
X          for valid syntax */
X
X       p = (char *) ep.hour;
X       while (*line && *line != COLON)
X               *p++ = *line++;
X
X       *p = '\0';
X       if (*++line == '\n' || *line == '\0')
X               xerror ("illegal number of fields\n");
X
X       if (ep.hour[0] == '\0')
X               xerror ("null hour string in control file\n");
X
X       /* now re-scan the hour in structure and
X          weed out the badies */
X
X       if (ep.hour[0] == '*' && ep.hour[1] != '\0')
X               xerror ("invalid hour string syntax: %s\n", ep.hour);
X       else if (ep.hour[0] == '*')
X               goto h_skip;
X
X       if (strcmp(ep.hour, "off", 3) == 0 && ep.hour[3] != '\0')
X               xerror ("invalid hour string syntax: %s\n", ep.hour);
X       else if (strncmp(ep.hour, "off", 3) == 0)
X               goto h_skip;
X
X       p = (char *) ep.hour;
X       if (!isdigit(*p))
X               xerror ("non-numeric char in hour string: %c\n", *p);
X
X       while (*p) {
X               num = 0;
X               do {
X                       num = num*10 + (*p - '0');
X               } while (isdigit(*++p));
X
X               if (num < lower || num > upper)
X                       xerror ("illegal hour: %d\n", num);
X
X               if (!*p)
X                       break;
X
X               if (*p == '-' && dash)
X                       xerror ("syntax error in hour field\n");
X               else if (*p == '-')
X                       dash = TRUE;
X
X               if (*p != ',' && *p != '-')
X                       xerror ("non-numeric char in hour string: %c\n", *p);
X               else if (!isdigit(*++p))
X                       xerror ("syntax error in hour field\n");
X
X       }
X
X       /* now that thats over with, let do the compression
X          field.  Only 9-16 is allowed, except a null field
X          indicates no compression for this site. */
X
Xh_skip:
X       num = 0;
X       while (*line && *line != COLON) {
X               if (!isdigit(*line))
X                       xerror ("non-numeric in compression field\n");
X               num = num*10 + (*line++ - '0');
X       }
X       if (*++line == '\n' || *line == '\0')
X               xerror ("illegal number of fields\n");
X
X       if (num != 0 && (num < 9 || num > 16))
X               xerror ("illegal compression bits: %d\n", num);
X
X       ep.c_bits = num;
X
X       /* now check the max. bytes for UUCP queue.
X          Note: There is a max. allowable # of bytes
X                here, set at 1MB.  Change it at your
X                own risk.
X       */
X
X       l_num = 0;
X       while (*line && *line != COLON) {
X               if (!isdigit(*line))
X                       xerror ("non-numeric in max. bytes field\n");
X
X               l_num = l_num*10 + (*line++ - '0');
X       }
X
X       if (l_num > MAX_BYTES)
X               xerror ("%ld max. bytes exceeds allowable maximun\n", l_num);
X
X       if (l_num != 0)
X               ep.m_bytes = l_num;
X       else
X               ep.m_bytes = DFL_BYTES;
X
X       /* and finally the command line (if there is one) */
X
X       p = (char *) ep.command;
X
X       if (*++line != '\n' && *line != '\0') {
X               while (*line && *line != '\n')
X                       *p++ = *line++;
X
X               *p = '\0';
X       }
X}
X
X#ifdef USE_PORT_CODE
Xxerror (fmt, a1, a2)
Xchar *fmt;
Xchar *a1, *a2;
X{
X       char    buf[BUFSIZ];
X
X       sprintf (buf, fmt, a1, a2);
X       printf ("\nnbatcher: %s\n", fmt);
X       exit (99);
X}
X
X#else
Xxerror (fmt, argp)
Xchar *fmt;
Xint argp;
X{
X       char    buf[BUFSIZ];
X       char    fbuf[BUFSIZ];
X       FILE    prwbuf;
X       register char   *cp;
X
X       prwbuf._flag = _IOWRT;
X       prwbuf._file = _NFILE;
X       prwbuf._cnt = 32767;
X       prwbuf._ptr = (unsigned char *)buf;
X       prwbuf._base = (unsigned char *)buf;
X       sprintf (fbuf, "\n%s: %s", "nbatcher", fmt);
X       _doprnt (fbuf, (char *)&argp, &prwbuf);
X       putc ('\0', &prwbuf);
X       for (cp = buf; *cp != '\0'; cp++)
X               putchar (*cp);
X
X       exit (99);
X}
X#endif /* USE_PORT_CODE */
X
Xclear_entry (s)
Xchar *s;
X{
X       register int i;
X
X       for (i=0; i<sizeof(struct file_entry); *s++ = '\0', i++)
X                               ;
X
X}
SHAR_EOF
echo x - extracting checkwork.c
sed 's/^X//' << 'SHAR_EOF' > checkwork.c
X/* char        sccsid[] = "@(#)checkwork.c 1.4 8/14/86"; */
X
X/************************************************************
X *
X *     checkwork.c - look to see if there's any work
X *                   to do for a site.
X *
X *     R.J. Esposito
X *     Bell of Penna.
X *     June 1986
X *
X ************************************************************/
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/utsname.h>
X#include <sys/dir.h>
X#include <ctype.h>
X#include <time.h>
X#include "nbatcher.h"
X
Xwork_to_do ()
X{
X       register char   *p;
X       struct tm       *localtime(), *tp;
X       struct stat     st;
X       char    buf[BUFSIZ];
X       long    time(), clock;
X       int     hour;
X       short   num, upper, lower;
X
X       sprintf (buf, "%s/%s", BATCHDIR, ep.site);
X
X       if (stat(buf, &st) < 0)
X               xerror ("bad stat on %s\n", buf);
X
X       /* if the size of the batch file is
X          zero, return FALSE
X       */
X
X       if (st.st_size == 0)
X               return (FALSE);
X
X       /* now see if it time to do anything */
X
X       clock = time ((long *)0);
X       tp = localtime (&clock);
X       hour = tp->tm_hour;
X
X       p = (char *) ep.hour;
X
X       if (*p == '*')          /* match any hour */
X               return (check_uucp());
X
X       if (strncmp(p, "off", 3) == 0)  /* just what it says, off */
X               return (FALSE);
X
X       /* parse thru hour field to see if
X          this is the hour to do work */
X
X       num = 0;
X       do {
X               num = num*10 + (*p - '0');
X       } while (isdigit(*++p));
X       if (num == hour)
X               return (check_uucp());
X
X       if (*p == '-') {
X               lower = num;
X               p++;
X               num = 0;
X               do {
X                       num = num*10 + (*p - '0');
X               } while (isdigit(*++p));
X               upper = num;
X
X               if (lower < upper) {    /* normal hour range */
X                       if (hour >= lower && hour <= upper)
X                               return (check_uucp());
X               } else if (lower > upper) {     /* 24 hr. cycle thru */
X                       if (hour >= lower || hour <= upper)
X                               return (TRUE);
X               } else
X                       return (FALSE);
X       }
X
X       if (*p == ',') {
X               p++;
X               while (*p) {
X                       num = 0;
X                       do {
X                               num = num*10 + (*p - '0');
X                       } while (isdigit(*++p));
X                       if (num == hour)
X                               return (check_uucp());
X                       p++;
X               }
X       }
X
X       return (FALSE);
X}
X
X/*     If check_uucp cannot find the remote site
X *     directory, just bypass the byte counting
X *     routine.  This is necessary because the
X *     uucpcleanup daemon, on some sites, removes
X *     the site directory when there's nothing there.
X */
X
Xcheck_uucp()
X{
X       struct utsname  utsn;
X       struct direct   dp;
X       struct stat     st;
X       FILE    *dfp;
X       char    u_name[9], buf[80];
X       short   prefix_len;
X
X       if (uname(&utsn) < 0)
X               xerror ("can't get local nodename\n");
X
X       sprintf (buf, "%s/%s", UUCPDIR, ep.site);
X       if (chdir(buf) < 0) {
X          fprintf (stderr, "\nnbatcher: can't chdir to %s - bypassing UUCP check\n", buf);
X          return (TRUE);
X       }
X
X       if ((dfp=fopen(".", "r")) == NULL) {
X          fprintf (stderr, "\nnbatcher: fopen error on %s - bypassing UUCP check\n", UUCPDIR);
X          return (TRUE);
X       }
X
X       sprintf (buf, "D.%s", utsn.nodename);
X       prefix_len = (short) strlen(buf);
X       n_bytes = 0;
X       while ((fread((char *)&dp, sizeof(dp), 1, dfp)) == 1) {
X               if (dp.d_ino == 0 || dp.d_name[0] == '.')
X                       continue;
X               if (strncmp(dp.d_name, buf, prefix_len))
X                       continue;
X               if (stat(dp.d_name, &st) < 0) {
X                 fprintf (stderr, "\nnbatcher: bad stat on UUCP_file %s - bypassing\n", dp.d_name);
X                 continue;
X               }
X               n_bytes += st.st_size;
X               if (n_bytes > ep.m_bytes) {
X                       fclose (dfp);
X                       return (FALSE);
X               }
X       }
X       fclose (dfp);
X       if (chdir(LIBDIR) < 0)
X               xerror ("can't chdir back to %s\n", LIBDIR);
X
X       return (TRUE);
X}
SHAR_EOF
echo x - extracting nbatcher.c
sed 's/^X//' << 'SHAR_EOF' > nbatcher.c
X/* char        sccsid[] = "@(#)nbatcher.c 1.4 8/14/86"; */
X
X/****************************************************
X *
X *     nbatcher.c - where it really happens.
X *
X *     R.J. Esposito
X *     Bell of Penna.
X *     June 1986
X *
X ***************************************************/
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <time.h>
X#include "nbatcher.h"
X
Xbatch_it ()
X{
X       struct stat     st;
X       FILE    *bfp, *afp;
X       char    fbuf[BUFSIZ], lckfile[40];
X       char    tbuf[80];
X       short   count;
X       int     c;
X
X       if (chdir(BATCHDIR) < 0)
X               xerror ("can't chdir to %s\n", BATCHDIR);
X
X       /* we create a lock file for two purposes,
X          first to make sure a previous nbatcher
X          didn't blowup and leave the lock file
X          laying around, and second to put the
X          remaining news article filenames when
X          we go over the max UUCP bytes and there's
X          still files remaining for batching.
X       */
X
X       sprintf (lckfile, ".%s.lock", ep.site);
X       if (!access(lckfile, 0))
X               xerror ("lockfile already exists for %s\n", ep.site);
X
X       if ((lfp=fopen(lckfile, "w")) == NULL)
X               xerror ("can't create lockfile for %s\n", ep.site);
X
X       /* now that we've locked ourselves for this site,
X          lets carry on */
X
X       if ((bfp=fopen(ep.site, "r")) == NULL)
X               xerror ("can't open %s/%s for reading\n", BATCHDIR, ep.site);
X
X       if (tfile == NULL) {
X               tfile = mktemp("/tmp/bnewsXXXXXX");
X               if ((tfp=fopen(tfile, "w")) == NULL)
X                       xerror ("can't open %s for writing\n", tfile);
X       }
X
X       count = fcnt = scnt = 0;
X       cu_bytes = 0;
X       while ((fgets(fbuf, sizeof(fbuf), bfp)) != NULL) {
X               fbuf[strlen(fbuf)-1] = '\0';    /* remove the newline */
X               if ((afp=fopen(fbuf, "r")) == NULL) {
X                  fprintf (stderr, "\nbypassing article %s: can't read it\n",
X                           fbuf);
X                       continue;
X               }
X               if (fstat(fileno(afp), &st) < 0)
X                       xerror ("fstat failed on %s\n", fbuf);
X
X               cu_bytes += st.st_size;
X
X               /* if the max byte count is exceeded,
X                  save the remaining files for later */
X
X               if ((cu_bytes + n_bytes) > ep.m_bytes) {
X                       fprintf (lfp, "%s\n", fbuf); /* put the '\n' back */
X                       while ((fgets(fbuf, sizeof(fbuf), bfp)) != NULL)
X                               fputs (fbuf, lfp);
X                       fclose (bfp);
X                       fclose (lfp);
X                       fclose (afp);
X                       unlink (ep.site);
X                       if (link(lckfile, ep.site) < 0)
X                          xerror ("can't link lockfile to %s\n", ep.site);
X                       unlink (lckfile);
X                       chown (ep.site, NEWSUID, NEWSGID);
X                       if (count)
X                               spoolit ();
X                       if (cu_bytes - st.st_size)
X                               log_it (cu_bytes - st.st_size);
X
X                       return;
X               }
X               sprintf (tbuf, "