[net.sources] Another News Batching Program

jim@hwcs.UUCP (Jim Crammond) (10/17/84)

#!/bin/sh
export PATH || (echo using sh to extract; /bin/sh $0; kill $$)

echo x - README
cat > README <<'FunkyStuff'
                Yet Another Batchnews Program
		--- ------- --------- -------

     This set  of  batch  and  unbatch  programs  have  been
adapted  from  those  written by Jim McKie (mcvax!jim). They
are basically the same  except  that  they  conform  to  the
'standard' described in  "Standard for Interchange of USENET
Messages" by Mark Horton and allow different compaction pro-
grams to be used for sites that have alternative programs to
Berkeley's "compact".


1.  Features
-   --------
     Batched news can be compacted using alternative compact
programs  to  Berkeley's  (un)compact(1) program. Some sites
use pack(1) or compress(1)  rather  than  compact(1).  These
programs  allows a site to use different compact programs to
different sites and similiarly receive  different  types  of
compacted news.

     Both compacted and non-compacted forms conform  to  the
USENET specification so that the batched news can be sent to
rnews(1) on the remote site, which then calls the unbatcher.

     The size of (uncompacted) batch files can  be  limited.
This  is  useful  when  the  transfer  media  (such as long-
distance phone calls) imposes limitations on the  length  of
files transferred.


2.  Description
-   -----------
     Batch reads a list of filenames from standard input  of
articles  to  be  batched and creates batch file(s) which it
then passes to uux(1) to forward  to  rnews  at  the  remote
site.

     It can optionally compact the batch files  to  save  on
transmission time and costs.

     Batch generates batch files in  the  USENET  'standard'
format:

            #! rnews 1234
            article containing 1234 characters
            #! rnews 432
            article containing 432 characters
            ....


     If the batched file is compacted then batch generates a
file in the following format:

            ##! compactprog
            compacted data

where compactprog is the (base) name of the compact  program
used.  Unbatch reads the first line to determine if the file
is compacted using a recognised compact program.  It  has  a
table  of valid compactprog names along with the correspond-
ing commands to uncompact the batch file.

     The resulting (uncompacted) batch file is in the USENET
'standard' format.

     Since both of these formats begin with a `#'  they  are
recognised by rnews to be batched files. Hence "batch" sends
its batches to rnews at the remote site which passes them on
to  the  unbatcher.  The unbatcher does not need to be known
by uux at the remote site.

     Sendbatch is a 'frontend' shell script to batch. It  is
normally called from crontab.


3.  Compatability
-   -------------
     Unless compacting, batch produces batch files using the
same  format  as the original batch program distributed with
news_2.10.1. Hence a site using the old unbatch program  can
unbatch articles generated by this batcher.

     The unbatch program can understand batched files in the
standard  format generated by the original news_2.10.1 batch
program.  In addition it can understand batched  files  gen-
erated  by Jim McKie's 'batchnews' and if unbatch is invoked
with the -C flag it assumes the  file  to  be  compacted  by
'batchnews' using the default compact program ( usually com-
pact(1) ).


4.  Installation Instructions
-   -------------------------
1.   Unbatch.c contains a table of batch program names  with
     their corresponding unbatch program. e.g.

                 "compact",      "/usr/ucb/uncompact"
                 "compress",     "/usr/local/compress -d"

     This may need to be edited to suit local  requirements.
     Note  that  only the basename of the compact program is
     required, but the full path name of the uncompact  pro-
     gram should be given.

     The default compact program in batch.c may also need to
     be  changed  if  your  Berkeley compact program doesn't
     live in /usr/ucb or you don't have it.

2.   Depending on whether you are on a V7 or 4.xBSD  system,
     do  "make  v7" or "make bsd" as appropriate.  Then do a
     "make install" to get batch,  unbatch,  and  sendbatch.
     You need to do this as "root".

3.   If you receive batched files  from  a  site  using  Jim
     McKie's  batchnews  then  do  "make  compat" to replace
     /usr/bin/unbatchnews with a link to unbatch.

4.   At the sending site, change the sys  file  entries  for
     sites you batch to:

      <site>:net.all,fa.all:F:/usr/spool/news/batched/<site>

     This causes 2.10 news to list news items for <site>  in
     the named file.

5.   At  the  sending  site,  add  a  line  like   this   to
     /usr/lib/crontab

      45 0,6,12,18 * * * /usr/lib/news/sendbatch <site1> <site2>

     to forward the batched news as often as you like.

     If the sender polls the receiver, run this program when
     news  is  to go out.  If the receiver polls the sender,
     run this program shortly before the poll is expected.

6.   If you want the forwarded batched news to be  compacted
     then add the "-Ccompact" flag to sendbatch above, where
     "compact" is the path name of the compact program:

      .... /usr/lib/news/sendbatch -C/usr/ucb/compat <site1> ...

     compact must be  a  program  which  the  remote  sites'
     unbatch  understands.   If  compactprog is not specfied
     then it defaults to Berkeley 4.xBSD's compact.

7.   The size of the uncompacted batch file can  be  limited
     by  the "-mSIZE" flag to sendbatch above, where SIZE is
     some number of bytes > 1000.  The default is 35000.

Notes
-----
     Steps 4 to 7 are unnecessary if  you  already  use  Jim
McKie's 'batchnews'.

     If you don't have "BATCH" defined in rnews  (defs.h  to
be precise) then this needs to be set to the pathname of the
unbatch program.


        Bugs/Comments to:

Jim Crammond   Heriot-Watt University, Edinburgh  ...hwcs!jim  8/10/84
----------------------------------------------------------------------
FunkyStuff
echo x - Makefile
cat > Makefile <<'FunkyStuff'
#
# Definitions
#
LIBDIR = /usr/lib/news
BINDIR = /usr/bin
CFLAGS = -O
LFLAGS = -s
OWNER = news
GROUP = news

FILES   = README Makefile batch.c unbatch.c sendbatch.v7 sendbatch.bsd
OBJECTS = batch unbatch

all:  ${OBJECTS}

bsd:
	cp sendbatch.bsd sendbatch.sh

v7:
	cp sendbatch.v7 sendbatch.sh

batch:	batch.c
	cc -o batch $(CFLAGS) $(LFLAGS) batch.c

unbatch:	unbatch.c
	cc -o unbatch $(CFLAGS) $(LFLAGS) unbatch.c

install: all
	cp batch unbatch $(LIBDIR)
	cp sendbatch.sh $(LIBDIR)/sendbatch
	chown $(OWNER) $(LIBDIR)/batch $(LIBDIR)/unbatch $(LIBDIR)/sendbatch
	chgrp $(GROUP) $(LIBDIR)/batch $(LIBDIR)/unbatch $(LIBDIR)/sendbatch
	chmod 755 $(LIBDIR)/batch $(LIBDIR)/unbatch $(LIBDIR)/sendbatch

compat: install
	rm -f $(BINDIR)/unbatchnews
	ln $(LIBDIR)/unbatch $(BINDIR)/unbatchnews

clean:
	rm -f ${OBJECTS} sendbatch.sh
FunkyStuff
echo x - batch.c
cat > batch.c <<'FunkyStuff'
/*
 * batchnews
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

#define SINKCMD		"uux - -r -z -gN %s!rnews"
#define TESTCMD		"cat -u >> b.out"

#define COMPACT		"/usr/ucb/compact"

#define TRACE(fmt, arg)	if (tracing) fprintf(stderr, fmt, arg)

main(argc, argv)
int argc;
char *argv[];
{
	FILE *sinkfp, *sourcefp, *popen();
	char *host;
	char *compact = NULL;
	int testing = 0;
	int tracing = 0;
	struct stat statb;
	long l, inbytes, infiles, outfiles;
	long maxbytes = 35000L, atol();
	int status = 0;
	char buf[BUFSIZ], sink[BUFSIZ], *sprintf(), *basename();
	char *ap, *argv0 = argv[0];

	while ((--argc > 0) && ((*++argv)[0] == '-'))
	{	ap = argv[0];
		switch (*++ap)
		{	case 'C':
				if (*++ap != '\0')
					compact = ap;
				else
					compact = COMPACT;
				TRACE("compact = %s\n", compact);
				break;

			case 'm':
				if ((l = atol(*++ap)) > 0 && l > 1000)
					maxbytes = l;
				TRACE("maxbytes = %ld\n", maxbytes);
				break;

			case 't':
				testing = 1;
				break;

			case 'x':
				tracing = 1;
				break;

			default:
				break;
		}
	}

	if (argc != 1)
	{	ERROR("%s: usage %s [-Ccompactprog] host\n", argv0, argv0);
		exit(1);
	}
	host = *argv;

	if (compact)
		sprintf(buf, "(echo '##! %s'; %s) | %s", basename(compact),
				compact, (!testing) ? SINKCMD : TESTCMD);
	else
		strcpy(buf, (!testing) ? SINKCMD : TESTCMD);
	sprintf(sink, buf, host);
	TRACE("sink = %s\n", sink);

	l = inbytes = infiles = outfiles = 0L;
	sinkfp = NULL;
	while (gets(buf) != NULL)
	{	TRACE("source = %s\n", buf);
		if ((sourcefp = fopen(buf, "r")) == NULL)
		{	ERROR("%s: <%s> fails\n", argv0, buf);
			continue;
		}
		if (fstat(fileno(sourcefp), &statb) == -1)
		{	ERROR("%s: can't stat %s\n", argv0, buf);
			fclose(sourcefp);
			continue;
		}
		if (sinkfp == NULL)
		{	if ((sinkfp = popen(sink, "w")) == NULL)
			{	ERROR("%s: <%s> fails\n", argv0, sink);
				exit(1);
			}
			outfiles++;
		}

		TRACE("size = %ld bytes\n", (long)statb.st_size);
		inbytes += (long)statb.st_size;
		l += (long)statb.st_size;
		infiles++;
		fprintf(sinkfp, "#! rnews %ld\n", (long)statb.st_size);

		while (statb.st_size--)
		{	register int c;

			if ((c = getc(sourcefp)) == EOF)
				break;
			putc(c, sinkfp);
		}

		fclose(sourcefp);
		fflush(stdout);
		printf("\t%s\n", buf);

		if (l > maxbytes)
		{	TRACE("batched: %ld bytes\n", l);
			status |= pclose(sinkfp);
			sinkfp = NULL;
			l = 0L;
			TRACE("status = %d\n", status);
		}
	}
	fflush(stdout);
	if (sinkfp != NULL)
	{	status |= pclose(sinkfp);
		TRACE("status = %d\n", status);
	}
	printf("\t%ldb/%ldf -> %ld files\n", inbytes, infiles, outfiles);
	exit(status);
}

char	*
basename(s)
char	*s;
{
	char *p = s;

	while (*p)
		if (*p++ == '/')
			s = p;
	return(s);
}


ERROR(fmt, arg1, arg2)
register char *fmt;
{
	fprintf(stderr, fmt, arg1, arg2);
}
FunkyStuff
echo x - unbatch.c
cat > unbatch.c <<'FunkyStuff'
/*
 * unbatchnews
 */
#include <stdio.h>

#define SINKCMD		"rnews"
#define TESTCMD		"cat -u"

#define TRACE(fmt, arg)		if(tracing) fprintf(stderr, fmt, arg)

struct	comprog
{	char	*comp;		/*  name of compacter  */
	char	*uncomp;	/*  name of uncompacter  */
} comprogs[] =
{
	"compact",	"/usr/ucb/uncompact",
	"compress",	"/usr/local/compress -d",
	0, 
};


#define UNCOMPACT	comprogs[0].uncomp	/*  for compatability */

char	cbuf[BUFSIZ];

main(argc, argv)
int argc;
char *argv[];
{
	FILE *sinkfp, *sourcefp, *popen();
	char *uncompact = NULL;
	int testing = 0;
	int tracing = 0;
	char buf[BUFSIZ], sink[BUFSIZ], *sprintf(), *scanline();
	char *ap, *argv0 = argv[0];

	argv0 = *argv;
	while ((--argc > 0) && ((*++argv)[0] == '-'))
	{	ap = argv[0];

		switch (*++ap)
		{	
#ifdef UNCOMPACT
			case 'C':
				uncompact = UNCOMPACT;
				break;
#endif

			case 't':
				testing = 1;
				break;

			case 'x':
				tracing = 1;
				break;

			default:
				break;
		}
	}

	if (argc != 0)
	{	ERROR("%s: usage %s < source\n", argv0, argv0);
		exit(1);
	}

	sprintf(sink, "%s", !testing ? SINKCMD: TESTCMD);
	TRACE("sink = %s\n", sink);

#ifdef UNCOMPACT
	if (!uncompact)
#endif
	{
		setbuf(stdin, NULL);
		if (gets(buf) == NULL)
			exit(0);
		uncompact = scanline(buf);
	}

	if (uncompact)
	{	TRACE("uncompacting with %s\n", uncompact);
		if((sourcefp = popen(uncompact, "r")) == NULL)
		{	ERROR("%s: <%s> fails\n", argv0, uncompact);
			exit(1);
		}
		if (fgets(buf, BUFSIZ-1, sourcefp) == NULL)
			exit(0);
	}
	else
	{	setbuf(stdin, cbuf);	/* restore buffered input */
		sourcefp = stdin;
	}

	do {
		register int c;
		long size, atol();

		if (sscanf(buf, "#! rnews %ld", &size) != 1)
#ifdef UNCOMPACT
			if((size = atol(buf)) <= 0)
#endif UNCOMPACT
				break;

		TRACE("size = %ld\n", size);

		if((sinkfp = popen(sink, "w")) == NULL){
			ERROR("%s: <%s> fails\n", argv0, sink);
			exit(1);
		}

		while(size-- && (c = getc(sourcefp)) != EOF)
			putc(c, sinkfp);
		pclose(sinkfp);

	}  while(fgets(buf, BUFSIZ-1, sourcefp) != NULL);

	if (uncompact)
		pclose(sourcefp);
	TRACE("All done\n", "");
	exit(0);
}

char	*
scanline(line)
char	*line;
{
	struct	comprog	*cpp;
	char	progname[BUFSIZ];
	
	if ( sscanf(line, "##! %s", progname) == 1 )
	{	for (cpp= &comprogs[0]; cpp->comp != 0; cpp++)
		{	if (strcmp(cpp->comp, progname) == 0)
				return( cpp->uncomp );
		}
	}

	return(NULL);
}


ERROR(fmt, arg1, arg2)
register char *fmt;
{
	fprintf(stderr, fmt, arg1, arg2);
}
FunkyStuff
echo x - sendbatch.bsd
cat > sendbatch.bsd <<'FunkyStuff'
#!/bin/sh
#
# Send accumulated batched news to sites
#
BatchDir=/usr/spool/news/batched
Log=BLOG
BatchCmd=/usr/lib/news/batch
Args=
Cflg=
Mflg=
Xflg=

cd $BatchDir

for i in $*
do

	case $i in

	-C)	Cflg=$i
		;;

	-x)	Xflg=$i
		;;

	-m*)	Mflg=$i
		;;

	*)	Args="$Args $i"
		;;

	esac
done

for Site in $Args
do
	date >> $Log.$Site
	if [ -r $Site ]
	then
		mv $Site $Site.$$
		$BatchCmd $Xflg $Cflg $Mflg $Site < $Site.$$ >> $Log.$Site 2>&1
		rm -f $Site.$$
	else
		echo "	No batched news for $Site" >> $Log.$Site
	fi
done
FunkyStuff
echo x - sendbatch.v7
cat > sendbatch.v7 <<'FunkyStuff'
: #!/bin/sh
: #
: # Send accumulated batched news to sites
: #
BatchDir=/usr/spool/news/batched
Log=BLOG
BatchCmd=/usr/lib/news/batch
Args=
Cflg=
Mflg=
Xflg=

cd $BatchDir

for i in $*
do

	case $i in

	-C)	Cflg=$i
		;;

	-x)	Xflg=$i
		;;

	-m*)	Mflg=$i
		;;

	*)	Args="$Args $i"
		;;

	esac
done

for Site in $Args
do
	date >> $Log.$Site
	if [ -r $Site ]
	then
		mv $Site $Site.$$
		$BatchCmd $Xflg $Cflg $Mflg $Site < $Site.$$ >> $Log.$Site 2>&1
		rm -f $Site.$$
	else
		echo "	No batched news for $Site" >> $Log.$Site
	fi
done
FunkyStuff
echo Files Extracted