#!/bin/sh
# This is a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 12/29/1991 08:09 UTC by devil@imp
# Source directory /home/devil/work/uubatch/bb
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#    379 -rw-r--r-- COPYRIGHT
#   2961 -rw-r--r-- Makefile
#  11367 -rw-r--r-- README
#  21640 -rw-r--r-- uubatch.c
#   1701 -rwxr-xr-x uucdebat
#   4534 -rw-r--r-- uucplock.c
#  10831 -rw-r--r-- uudebat.c
#   4569 -rw-rw-rw- uubatch.1
#   2227 -rw-rw-rw- uudebat.1
#     35 -rw-r--r-- patchlevel.h
#
# ============= COPYRIGHT ==============
if test -f 'COPYRIGHT' -a X"$1" != X"-c"; then
	echo 'x - skipping COPYRIGHT (File already exists)'
else
echo 'x - extracting COPYRIGHT (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'COPYRIGHT' &&
X       Other then the copyright notice in UUCPLOCK.C, we put no
X       limitation on the redistribution, use, or modification of any 
X       portion of this code.
X
X       THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X       IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X       WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
SHAR_EOF
chmod 0644 COPYRIGHT ||
echo 'restore of COPYRIGHT failed'
Wc_c="`wc -c < 'COPYRIGHT'`"
test 379 -eq "$Wc_c" ||
	echo 'COPYRIGHT: original size 379, current size' "$Wc_c"
fi
# ============= Makefile ==============
if test -f 'Makefile' -a X"$1" != X"-c"; then
	echo 'x - skipping Makefile (File already exists)'
else
echo 'x - extracting Makefile (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
#
# Makefile, uubatch version 1.0.5
#
# uubatch package Makefile
#
# Authors :	Gil Tene		(devil@hellnet.org)
#		Baruch Cochavy		(blue@dduster.hellnet.org)
#
#
X
#
# Defines:
#
# MIN_TIME_BEFORE_BATCH : The minimun time (in seconds) since the 
# time a job was queued before it can be batched. This is use to 
# avoid clashing with rmail jobs that are being queued at the same 
# time as the batching is done. Recommended more than 1 minutes
# (60 seconds to be sure...)
#
# UUCP_SPOOL_DIR : The directory under which individual nodes' spool
# directory files are (such as /usr/spool/uucp)
#
# _PATH_LOCKDIRNAME : describes (in printf format) the complete path 
# and name of a uucp lock file for a given node name.
#
# The follwoing is an example CONFIG_DEFS setting for a tipical instalation:
#
# SYS_DEFS = -DUUCP_SPOOL_DIR=\"/usr/spool/uucp\" \
#	   -D_PATH_LOCKDIRNAME=\"/usr/spool/locks/LCK..%s\"
#
#
# The following are configuration dependent:
#
CONFIG_DEFS = -DMIN_TIME_BEFORE_BATCH=60
#
# uubatch version
VER=1.0.5
#
# The following are system dependent:
######################################################################
# for Ultrix
#
#SYS_DEFS = -DUUCP_SPOOL_DIR=\"/usr/spool/uucp\" \
X	   -D_PATH_LOCKDIRNAME=\"/usr/spool/locks/LCK..%s\"
#CCFLAGS = -DBSD -O
#
######################################################################
# for Sun386i
#
#SYS_DEFS = -DUUCP_SPOOL_DIR=\"/usr/spool/uucp\" \
X	   -D_PATH_LOCKDIRNAME=\"/usr/spool/locks/LCK..%s\"
#CCFLAGS = -Dsun386i -O
#
######################################################################
# for SounOS 4.1
#
SYS_DEFS = -DUUCP_SPOOL_DIR=\"/usr/spool/uucp\" \
X	   -D_PATH_LOCKDIRNAME=\"/usr/spool/locks/LCK..%s\"
CCFLAGS = -O
#
######################################################################
# for SCO Unix with a gcc compiler:
#
#CC = gcc
#CCFLAGS = -O -DSCO
#SYS_DEFS = -DUUCP_SPOOL_DIR=\"/usr/spool/uucp\" \
X	   -D_PATH_LOCKDIRNAME=\"/usr/spool/uucp/LCK..%s\"
#
######################################################################
#
# for NeXT BSD4.3 (System 2.1) :
#
#SYS_DEFS = -DUUCP_SPOOL_DIR=\"/usr/spool/uucp\" \
X            -D_PATH_LOCKDIRNAME=\"/usr/spool/uucp/LCK..%s\"
#CCFLAGS = -object -O -g
#
######################################################################
# for OKI860
#
#SYS_DEFS = -DUUCP_SPOOL_DIR=\"/usr/spool/uucp\" \
X	   -D_PATH_LOCKDIRNAME=\"/usr/spool/locks/LCK..%s\"
#CCFLAGS = -DSCO -DHAS_STRING -O
#
######################################################################
X
X
all: uudebat uubatch uucplock
X
uudebat: uudebat.c
X	$(CC) $(CCFLAGS) $(SYS_DEFS) $(CONFIG_DEFS) uudebat.c -o uudebat
X
uubatch: uubatch.c
X	$(CC) $(CCFLAGS) $(SYS_DEFS) $(CONFIG_DEFS) uubatch.c -o uubatch
X
uucplock: uucplock.c
X	$(CC) $(CCFLAGS) $(SYS_DEFS) $(CONFIG_DEFS) uucplock.c -o uucplock
X
shar:
X	shar COPYRIGHT Makefile README uubatch.c uucdebat \
X		uucplock.c uudebat.c uubatch.1 uudebat.1 \
X	    patchlevel.h > uubatch.$(VER)
X
clean:
X	-rm -f uudebat uubatch uucplock
SHAR_EOF
chmod 0644 Makefile ||
echo 'restore of Makefile failed'
Wc_c="`wc -c < 'Makefile'`"
test 2961 -eq "$Wc_c" ||
	echo 'Makefile: original size 2961, current size' "$Wc_c"
fi
# ============= README ==============
if test -f 'README' -a X"$1" != X"-c"; then
	echo 'x - skipping README (File already exists)'
else
echo 'x - extracting README (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'README' &&
X
***** README file, uubatch version 1.0.5 *****
X
uubatch : A UUCP rmail batcher/debatcher package.
X
Authors :	Gil Tene		(devil@hellnet.org)
X		Baruch Cochavy		(blue@dduster.hellnet.org)
X
X
Problem definition and solution approach :
X
Problem definition : the uubatch package is aimed at increasing UUCP
e-mail transfer efficiency. The main efficiency drawback in UUCP e-mail
(via the rmail command) is the small UUCP job sizes. Most mail messages
tend to be short (less than 5K). Each such message is queued separately
by UUCP, and is transferred separately by uucico. This make for a very
high job-count/Kbytes ratio. Each job transfer (two files per job)
usually takes more than 8 seconds on most links regardless of job
size. On modems with UUCP spoofing the drawback is even more noticeable,
as they have a very high UUCP transfer efficiency WITHIN each
file transferred, but suffer just like any other modem on per-file
delays. This means that UUCP spoofing efficiency is not really
utilized on short e-mail transfers.
X
The solution is simple: reduce the job-count/Kbyte count, thereby
increasing efficiency. This is done by "batching" several e-mail
messages together into one UUCP job, thus suffering only one
per-job delay for each batch.
X
This solution has been implemented in other packages (Smail 3.1
compressed batched SMTP mail for one) by having the mailer NOT
deliver rmail jobs to UUCP immediately, but rather queue them 
separately and batch them together into one large job every once
in a while. This technique has two major drawbacks: It forces
the ***mailers*** on both sides to support the protocol, and
it causes delays in mail delivery (you balance efficiency against
delivery speed).
X
The uubatch package provides the solution by batching e-mail 
transfers AFTER they have been queued by UUCP as rmail commands,
and "debatching" them back on the other side as normal UUCP rmail 
commands. 
X
The uubatch package offers several advantages over other e-mail
batching schemes :
X
1)  It is independent of the mailer used. It will work on spooled
X    rmail commands, which are the same for all mailers.
X
2)  It doesn't cause any delay in mail delivery. It batches already
X    queued jobs. If it doesn't get a chance to batch a mail message
X    before a poll occurs, that message will be transferred as normal
X    unbatched rmail.
X
3)  It does re-batching. This makes sure that batched jobs are used
X    to their maximum efficiency. You can run the batching script as
X    often as you like without worrying about loosing transfer 
X    efficiency (it won't make lots of small batches).
X
4)  The code is SIMPLE, relatively short, easy to understand and to 
X    port. It will not interfere or clash with mailer configurations.
X
X
Implementation mechanism :
X
The mechanism is in two parts :
X
Sending end: the batcher : uubatch, uucplock (C programs)
X
X  The uubatch program is run periodically on [specified] outgoing spool 
X  directories. In each spool directory uubatch batches together any rmail 
X  jobs and any previous batches (uudebat or uucdebat jobs) it finds that 
X  are small enough (execution settable critical size).
X
X  The batching is very simple : Once a batchable job is identified, it's 
X  data file names (both local and final remote names) are parsed. The data 
X  files are then appended to a "uubatch" file with a simple envelope that 
X  includes their final remote names and enables simple extraction on the 
X  other side. In the case of re-batching of batch jobs (uudebat and uucdebat) 
X  the contents of the job's data file (uncompressed in the case of uucdebat) 
X  is simply appended to the "uubatch" file. Once all rmail/uudebat/uucdebat 
X  jobs are batched, or once the "uubatch" data file reaches a given critical 
X  size, the following command is executed :
X
X  uux - -r nodename!uudebat < batchname
X
X  (or compress -c | batchname uux - -r nodenanme!uucdebat)
X
X  Re-batching of batched jobs is ONLY done if there is at least one rmail
X  job in the queue, and only on jobs that are small enough to be re-batched.
X  This avoids avoids any unneeded re-batching.
X
X  The entire batching process is done while locking the UUCP node
X  via a uucp lock mechanism (I hacked this up from the BSD tip uucplock.c
X  code, you can hack it if your locking mechanism is non-HDB or non-BSD). 
X  This makes sure that no double or partial jobs are transmitted. 
X  The batching process will only batch files which have been modified more 
X  than a specified time ago, to avoid any possible synchronization clashes 
X  with any rmail jobs being created at the same time.
X  
Receiving end: the debatcher : uudebat (C program) and uucdebat (script)
X
X  The uucdebat script is trivial : "uncompress | uudebat".
X
X  uudebat: This VERY simple C program accepts as input a "uubatch" file.
X  The file contains destination file names and their file contents
X  (each line of the contents is superseded by a # char.). The 
X  extraction is trivial. It is done into the executing node's 
X  spool directory (the executing node is figured out from UU_MACHINE).
X  This is ALL that needs to be done, since the files will then be 
X  picked up by uuxqt just as if they were put there by uucico directly.
X  The file names and contents are EXACTLY what they were supposed to be,
X  and they will all look like normal rmail jobs to uuxqt.
X
X  The debatcher program is the one security people would need to look at 
X  for possible holes, and that is why it is a C program (efficiency is 
X  another reason). It will ONLY extract files into the connected node's 
X  incoming spool directory, and it can't be fooled (I believe) into doing
X  anything bad by hacking a bogus "uubatch" file and feeding it into 
X  uudebat.
X
X
Files:
X
X  README 	--	This file.
X
X  Makefile	--	A trivial Makefile, it makes the uubatch, uucplock,
X			and uudebat executables. Has several defines to 
X			look at.
X
X  uubatch.c 	--	The job batcher, see mechanism explanation above.
X
X  uucplock.c 	--	A locking mechanism. Based on the BSD tip uucplock.c
X			code (and heavily modified for HDB UUCP). See the 
X			BSD Copyright notice in this file.
X
X  uudebat.c	--	The job debatcher program, see mechanism explanation
X			above.
X
X  uucdebat	--	A simple shell script for compressed debatching,
X			see mechanism explanation above.
X
X  uubatch.1	--	man page. (read with : nroff -man uubatch.1 | more)
X
X  uudebat.1	--	man page. (read with : nroff -man uudebat.1 | more)
X
X
X
Installation:
X
- Unpack the package, decide on where you want to install the uudebat
X  and uucdebat commands (they need to be executable by remote via UUCP,
X  just like rmail). Check and modify the following parameters in the 
X  following files :
X
X  Makefile : 	Make sure it will make the files ok. (compiler, 
X		flags, etc.) Read the description of the defined
X		variables and edit their values if needed.
X
X  uucdebat :	Make sure that the pathnames for uncompress and uudebat
X		are correct. It is recommended that you use full path
X		names to avoid errors. A mistake here is the only place
X		we know of that causes messages to be silently 
X		bit-bucketed.
X
X  uucplock.c :	If you have a non-BSD and non-HDB UUCP system, try to
X		find out what UUCP locking mechanism is used in your
X		system and change this file if needed. 
X		Make sure PMAX is set to the maximum filename length 
X		on your system. If the program does not compile as-is
X		and you do not find a predefined symbol for this value,
X		define PMAX to be 255.
X
X  uubatch.c :	I don't expect any need to change this file.
X
X  uudebat.c :	Take a look at the definition of PMAX. It should be
X		equal to the maximum length of a path name on your
X		system. If the program does not compile as-is, and
X		you do not find a predefined symbol for this value,
X		define PMAX to be 255.
X
X  Make the uubatch, uucplock, and uudebat programs with make(1).
X
- Install the uudebat and uucdebat commands in a proper location, 
X  so that they will be able to execute as a remote UUCP command
X  (/usr/bin or /bin is the usual place). It is advisable to set the
X  owner uid and group of the files to be uucp.
X
- Make sure that the UUCP permissions file allows remote nodes
X  (with which you wish to do uubatch batching) to execute the
X  uudebat and uucdebat commands.
X
- Read the uubatch.1 man page for description of usage and meaning of 
X  flags in uubatch.
X
- Run some manual test batches to an adjacent node, manually invoking 
X  uubatch using syntax like:
X
X        uubatch foobar            
X    or: uubatch -c foobar  
X
X  (These runs will require supervisor or uucp access rights).
X
X	NOTE :
X
X	When running tests, and after instalation too, the uubatch and
X	uucplock binaries should be located in the same directory. uubatch
X	invokes uucplock, and expects it to be runnable with the same
X	path prefix it (uubatch) was run with. )
X
X  and read the resulting data file created in UUCP spool directory.
X  The file should be readable using 'cat {filename}' or 'zcat < {filename}'
X  for compressed batches. Once you are satisfied that the batching 
X  is done right, allow it to go through to the remote node, and verify
X  that it has been correctly debatched (It would help if the remote site
X  would allow uudebat and uucdebat execution. :-) 
X
X
- Edit uucp's crontab entry, and add an execution of :
X
X        /usr/local/uucp/uubatch node [node,...]
X
X  (with optional -c for compression, read description of other
X   flags in uubatch.1 man page for details on how to control batch 
X   file sizes)
X 
X  Set intervals for execution in any way you like. Remember that 
X  there is no need to open "large" time windows for batching, since
X  small jobs will be re-batched. (I use 30 or 60 minute intervals).
X
X  Don't redirect the output of uubatch to /dev/nul until you
X  have tested it for each node and seen it work. Do some loop mails
X  to make sure before running any crucial e-mail through it.
X
X  
Revision history :
X
1.0.2 :		01-Aug-1991	First distributed version.
X
1.0.3 :		13-Aug-1991	Fixed bugs :
X
X		uubatch.c : batching process now correctly calculates
X		modifications time and size of job file. time and size
X		considerations now handled properly.
X
X		uudebat.c : PMAX set to maximum path length on system
X		(instead of maximum filename length).
X
1.0.4 :		uudebat.c : Fixed possible security hole, and added 
X		better error recovery and security warnings.
X
X		uudebat.c : Added filtering of uubatch "escape" lines 
X		from X. files. This allows uubatch "protocol extensions"
X		in future versions, which may be needed to support
X		smarter stuff. Upward/Downward compatability will be
X		mantained through all future and past versions.
X
X		uudebat.c : Removed line length limitation. Any length
X		line can now be passed through it.
X
X		uubatch.c : Removed line length limitation. Any length
X		line can now be passed through it.
X
X		uubatch.c : Several performance improvements including :
X		- moved directory searchs into uubatch.c
X		- allow batching for multiple uucp nodes in one run.
X		- removed unneeded 'cat' commands in some places.
X
X		uubatch_run : removed this file. All batch execution 
X		is now performed entirely by uubatch.c, and uubatch.c
X		is the program to be called from cron from now on.
X
X		uubatch.c and uudebat.c : some readability touch-ups.
X
X		Makefile : Touched up a bit and added more tested 
X		machines.
X
X
1.0.5:	fix various uncontroled string copies.
X		Still in the works: mail bouncing, firewall.
SHAR_EOF
chmod 0644 README ||
echo 'restore of README failed'
Wc_c="`wc -c < 'README'`"
test 11367 -eq "$Wc_c" ||
	echo 'README: original size 11367, current size' "$Wc_c"
fi
# ============= uubatch.c ==============
if test -f 'uubatch.c' -a X"$1" != X"-c"; then
	echo 'x - skipping uubatch.c (File already exists)'
else
echo 'x - extracting uubatch.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'uubatch.c' &&
/*
X * uubatch.c, uubatch version 1.0.5
X *
X * uubatch : a uucp rmail batching program.
X *
X * Authors : Gil Tene 		(devil@hellnet.org).
X *           Baruch Cochavy 	(blue@dduster.hellnet.org)
X *
X * Usage : 
X *	uubatch [-c] [-B n] [-C n] [-S n] [-T n] node [node2...]
X *
X * This program will attempt to batch the given uucp nodes' queued
X * jobs into uudebat or uucdebat jobs. It takes care of the entire
X * batching process including uucp locking (via an external uucplock
X * program).
X *
X * External uubatch program binaries (specificaly uubatch) are assumed 
X * be runnable with the same path header uubatch was run with (as parsed 
X * from argv[0]). This means that uucplock should usually be in the same
X * directory as uubatch.
X *
X * The program scans each spool directory for C.<> files, which are
X * taken as the names of jobs to consider for batching (if they
X * meet the batching criteria below).
X *
X * uubatch accepts the following parameteres:
X *
X * node, node2... :
X *		  The uucp nodes to run on.
X *
X * -c		: compression on (default is off)
X *
X * -B n		: MAX_BYTES = n (default DF_MAX_BYTES)
X *		  MAX_BYTES is the maximum size (in bytes) of an rmail
X * 		  message or a uudebat job that may be added to a batch
X *		  Jobs larger than this parameter are not batched.
X *
X * -C n		: MAX_CBYTES = n (default DF_MAX_CBYTES)
X *		  MAX_FILE_CBYTES is the maximum size (in bytes) of a
X *		  uucdebat job's compressed data file that may be added
X *		  to a batch. uucdebat jobs with compressed data files
X *		  larger than this parameter are not rebatched.
X *
X * -S n		: CRITICAL_SIZE = n (default DF_CRITICAL_SIZE)
X *		  CRITICAL_BATCH_SIZE is the batch size (in bytes)
X *		  after which we send the batch off and start a new
X *		  one. Be aware of the fact that the actual MAXIMUM
X *		  batch size is actually the greater one of :
X *
X *		  CRITICAL_BATCH_SIZE + MAX_BYTES_FOR_BATCH
X *
X *		  CRITICAL_BATCH_SIZE +
X *			uncompressed(MAX_CBYTES_FOR_BATCH)
X *
X * -T n		: min_time_before_batch = n seconds 
X *				(defaults to MIN_TIME_BEFORE_BATCH)
X *		  This is the age of a job (in seconds) before
X *		  it may be considered for batching.
X *
X * Each queued job will be examined to see if the job should 
X * be batched. If a job need not be batched then the program simply
X * skips over it. If the job needs to be batched, and the batching
X * succeeds and the batch is sccessfully uuxe'd, the job's data files 
X * are deleted.
X *
X *
X * Batching criteria :
X *
X * rmail jobs with Data less than MAX_BYTES bytes 
X * and more than min_time_before_batch sec. old will be batched.
X *
X * uudebat jobs with Data less than MAX_BYTES and
X * more than min_time_before_batch sec. old will be batched.
X *
X * uucdebat jobs with compressed data less than MAX_CBYTES
X * and more than min_time_before_batch sec. old will be batched.
X *
X * Batching will ONLY be done if at least one rmail job is 
X * encountered in the node's spool directory. This saves
X * on unneeded re-batching of uudebat and uucdebat jobs.
X *
X */
X
# include <stdio.h>
#ifdef BSD
#include <strings.h>
#else
#define index(_a,_b) strchr(_a,_b)
#define rindex(_a,_b) strrchr(_a,_b)
#endif
X
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <dirent.h>
X
#include "patchlevel.h"
X
/* see main()'s flags description below for explanation of the meaning 	*/
/* of the following three default values :				*/
X
#define DF_MAX_BYTES 		60000
#define DF_MAX_CBYTES 		35000
#define DF_CRITICAL_SIZE	100000
X
X
#ifndef MIN_TIME_BEFORE_BATCH
#define MIN_TIME_BEFORE_BATCH	40
#endif
X
#ifndef LINE_LENGTH
#define LINE_LENGTH	512
#endif
X
#ifndef MAX_DF2_LINES
#define MAX_DF2_LINES		80
#endif
X
#ifndef MAX_JOBS_PER_BATCH
#define MAX_JOBS_PER_BATCH	100
#endif
X
#ifndef MAX_REGISTERED_BATCHES
#define MAX_REGISTERED_BATCHES	20
#endif
X
#ifndef MAX_FILE_NAME
#define MAX_FILE_NAME		((size_t)40)
#endif
X
char 	jobnames[MAX_REGISTERED_BATCHES][MAX_FILE_NAME+1];
char 	df1_names[MAX_REGISTERED_BATCHES][MAX_FILE_NAME+1];
char	df2_names[MAX_REGISTERED_BATCHES][MAX_FILE_NAME+1];
int	jobs_uucdebat[MAX_REGISTERED_BATCHES];
int	registered_batches;
X	
char	del_jobnames[MAX_JOBS_PER_BATCH][MAX_FILE_NAME+1];
char	del_df1_names[MAX_JOBS_PER_BATCH][MAX_FILE_NAME+1];
char	del_df2_names[MAX_JOBS_PER_BATCH][MAX_FILE_NAME+1];
int	jobs_this_batch;
X
char df2lines[MAX_DF2_LINES][LINE_LENGTH+1];
int  df2nlines;
X
typedef struct _node_rec { struct _node_rec *next; char *name; } node_rec;
node_rec *first_node=NULL,*curr_node=NULL,*last_node=NULL;
X
char batchname[L_tmpnam];
char uubatch_bin_path[256];
X
FILE *bfile = (FILE *) NULL;
FILE *cf = (FILE *) NULL;
FILE *df1 = (FILE *) NULL;
FILE *df2 = (FILE *) NULL;
X
int total_rmail_jobs;
X
long max_bytes, max_cbytes, critical_size, accumulated_size;
int min_time_before_batch = MIN_TIME_BEFORE_BATCH;
X
/* General exit and diagnostics routines : */
X
my_exit(n)
{
X	if (bfile) fclose(bfile);
X	unlink(batchname);
X	exit(n);
}
X
pe_exit(s,n)
char *s;
int n;
{
X	perror(s);
X	my_exit(n);
}
X
#define pe_ret(_st,_nn) 			\
X		{	 			\
X		perror(_st); 			\
X		if (cf) fclose(df1);		\
X		if (df1) fclose(df1);		\
X		if (df2) fclose(df2);		\
X		return(_nn); 			\
X		}
X 
pdn_exit(s,n,ne)
char *s;
int n,ne;
{
X	char str[120];
X	sprintf(str,s,n);
X	fprintf(stderr,"uubatch : Diagnostic %03d: %s\n",ne,str);
X	my_exit(ne);
}
X
#define pdn_ret(_st,_nn,_ne)				\
X        { 						\
X		char str[120];	 			\
X		sprintf(str,_st,_nn);				\
X		fprintf(stderr,"uubatch : Diagnostic %03d: %s\n",_ne,str); \
X		if (cf) fclose(df1);		\
X		if (df1) fclose(df1);		\
X		if (df2) fclose(df2);		\
X		return (_ne); 			\
X	}
X
pd(s,n)
char *s;
int n;
{
X	fprintf(stderr,"uubatch : Diagnostic %03d: %s\n",n,s);
}
X
pds(s,s1,n)
char *s;
int n;
{
X	fprintf(stderr,"uubatch : Diagnostic %03d: %s %s\n",n,s,s1);
}
X
#define pd_ret(_st,_nn) 				\
X	{					\
X		fprintf(stderr,"uubatch : Diagnostic %03d: %s\n",_nn,_st); \
X		if (cf) fclose(df1);		\
X		if (df1) fclose(df1);		\
X		if (df2) fclose(df2);		\
X		return _nn;			\
X	}
X
#define pds_ret(_st,_nn) 				\
X	{					\
X		fprintf(stderr,"uubatch : Diagnostic %03d: %s\n",_nn,_st); \
X		return _nn;			\
X	}
X
p_exit(s,n)
char *s;
int n;
{
X	fprintf(stderr,"uubatch : Error %03d: %s\n",n,s);
X	my_exit(n);
}
X
#define p_ret(_st,_n) 				\
X	{					\
X		fprintf(stderr,"uubatch : Error %03d: %s\n",_n,_st); \
X		if (cf) fclose(df1);		\
X		if (df1) fclose(df1);		\
X		if (df2) fclose(df2);		\
X		return(_n); 			\
X	}
X
/* Now for real code... */
X
#define str_eq(_s1,_s2) (!strncmp(_s1,_s2,strlen(_s2)))
X
/* add_node() : add a node name to a [global] linked list of node names : */
X
static int
add_nodename(name)
char *name;
{
X    if (last_node)
X    {
X	last_node->next = (node_rec *) malloc(sizeof(node_rec));
X	last_node = last_node->next;
X    }
X    else
X	first_node = last_node = (node_rec *) malloc(sizeof(node_rec));
X
X    last_node->next = NULL;
X    last_node->name = (char *) malloc(strlen(name)+1);
X    strcpy(last_node->name,name);
}
X
/* lock_node() : lock a uucp node : */
X
static int
lock_node(name)
char *name;
{
X	char command[256];
X	if (*uubatch_bin_path!='\0')
X		sprintf(command, "%s/uucplock lock %s 2> /dev/null",
X			uubatch_bin_path, name);
X	else
X		sprintf(command, "uucplock lock %s 2> /dev/null",
X			name);
X
X	return (system(command));
}
X
/* unlock_node() : unlock a uucp node : */
X
static int
unlock_node(name)
char *name;
{
X	char command[256];
X	if (*uubatch_bin_path!='\0')
X		sprintf(command, "%s/uucplock unlock %s 2> /dev/null",
X			uubatch_bin_path, name);
X	else
X		sprintf(command, "uucplock unlock %s 2> /dev/null",
X			name);
X
X	return (system(command));
}
X
X
/* get_names() :							*/
/* extracts a file's local and remote names from a C.<> file line 	*/
/* it checks the remote name for a proper prefix (passed to us).	*/
X
get_names(line,pname,premote_name,rname_prefix)
char *line,**pname,**premote_name,*rname_prefix;
{
X    char *end_of_names;
X    *pname = line+2;
X
X    if (!(*premote_name = index(*pname,' ')))
X	pds_ret("Oops, no space after first file name in command file.",112);
X
X    if (!str_eq((*premote_name)+1,rname_prefix))
X	pds_ret("Oops, wrong remote name prefix in command file.",113);
X
X    (*premote_name)[0] = '\0';
X    (*premote_name)++;
X
X    if (!(end_of_names = index(*premote_name,' ')))
X	pds_ret("Oops, remote file name with no end in command file...",114);
X
X    end_of_names[0] = '\0';
X
X    return 0;
}
X
X
X
/* batch_job() :							*/
/* This routine does most of the real work. It accepts a job name	*/
/* and figures out if it needs to be batched. If it is an rmail job	*/
/* it will batch it. If the job is a uudebat or uucdebat, it will	*/
/* register for later re-batching, IF at least one rmail job is		*/
/* found in the queue.							*/
X
batch_job(jobname)
char *jobname;
{
X    int i;
X    char *df1_name, *df1_remote_name;
X    char *df2_name, *df2_remote_name;
X    char line1[LINE_LENGTH],line2[LINE_LENGTH],line[LINE_LENGTH];
X    struct stat df1_status;
X    int is_rmail,is_uudebat,is_uucdebat,line_state;
X
X    cf = (FILE *) NULL;
X    df1 = (FILE *) NULL;
X    df2 = (FILE *) NULL;
X
X    /* Do some nit-picking checks : */
X
X    if (strlen(jobname) > MAX_FILE_NAME)
X	p_ret("jobname too long.",5);
X
X    if (!str_eq(jobname,"C."))
X	p_ret("jobname must be C.<jobid>",10);
X
X    if (!(cf = fopen(jobname,"r")))
X	pe_ret("Can't open specified job file",15);
X
X    if (fgets(line1,LINE_LENGTH,cf))
X    {
X	if (!fgets(line2,LINE_LENGTH,cf))
X	    pd_ret("Only one line in command file",20);
X    }
X    else	
X	p_ret("No lines in command file",25);
X
X    if (line1[0]!='S')
X	pd_ret("Oops, no S in line 1 of command file",30);
X
X    if (line2[0]!='S')
X	pd_ret("Oops, no S in line 2 of command file.",35);
X
X    /* Now figure out the command file's names (local and remote), and	*/
X    /* open it up :							*/
X
X    if (get_names(line2,&df2_name,&df2_remote_name,"X."))
X	return;
X
X    if (strlen(df2_name)>MAX_FILE_NAME)
X	p_ret("X. data file name too long.",40);
X
X    if (!(df2=fopen(df2_name,"r")))
X	pe_ret("Can't open X. data file.",45);
X
X
X    /* scan the command file for batchable, command, while queuing the */
X    /* lines for later use if it turns out we want to batch this file: */
X
X    for ( df2nlines=is_rmail=is_uudebat=is_uucdebat=0;
X	  (fgets(line,LINE_LENGTH,df2)) && df2nlines<MAX_DF2_LINES;)
X    {
X	strcpy(df2lines[df2nlines++],line);
X
X	/* only do checks on the beginning of file lines : */
X	if (line[strlen(line)-1] == '\n')
X	{
X	    if (str_eq(line,"C rmail"))		is_rmail++;
X	    if (str_eq(line,"C uudebat"))	is_uudebat++;
X	    if (str_eq(line,"C uucdebat"))	is_uucdebat++;
X	}
X    }
X
X    /* Figure out if all is kosher, and if there is a batchable command: */
X
X    if (df2nlines==MAX_DF2_LINES)
X	pd_ret("Too many lines in X. data file.",50);
X
X    if (!(is_rmail+is_uudebat+is_uucdebat))
X	pd_ret("No rmail, uudebat or uucdebat in X. data file...",55); 
X
X    if (is_rmail+is_uudebat+is_uucdebat>1)
X	pd_ret("More than one rmail or uudebat, I won't do it!",60); 
X
X    /* So it's a batchable command, let's figure out stuff about it's	*/
X    /* data file (name, remote name, age, size) :			*/
X
X    if (get_names(line1,&df1_name,&df1_remote_name,"D."))
X	return;
X
X    if (strlen(df1_name)>MAX_FILE_NAME)
X	p_ret("D. data fiel name too long.",65);
X
X    if (!(df1=fopen(df1_name,"r")))
X	pe_ret("Can't open D. data file.",70);
X
X    /* get status of df1 : */
X    fstat(fileno(df1), &df1_status);
X
X    /* Check if it is not too old :					*/
X
X    if (time(NULL) - df1_status.st_mtime < min_time_before_batch)
X	pdn_ret("Data file is too new (%d sec), next time maybe.", 
X		time(NULL) - df1_status.st_mtime,75);
X
X    /* Check if it is not too big :					*/
X
X    if ( ( (is_rmail || is_uudebat) && 
X           (df1_status.st_size>max_bytes)
X         ) ||
X         ( is_uucdebat && (df1_status.st_size>max_cbytes) 
X         )
X       )
X	pd_ret("file to big, not put in batch.",80);
X
X
X    /* If we get to this point, we know we want to batch the job. 	*/
X
X
X    /* BATCH it : */
X
X    /* rmail jobs need to have their name printed on top, and 		*/
X    /* their data file contents included with # preceding each		*/
X    /* line. For uudebat and uucdebat the we will register the		*/
X    /* batch for later rebatching, only done if at least one rmail	*/
X    /* job is found in the queue.					*/
X
X    if (is_rmail) 	/* for rmail jobs, batch the X. file */
X    {
X
X	/* write df2's name and contents in : */
X	fprintf(bfile,"%s\n",df2_remote_name);
X	accumulated_size += strlen(df2_remote_name) + 1;
X
X	line_state = 0;
X	for (i=0;i<df2nlines;i++)
X	{
X	    /* if we are at the BEGINNING of a line, add a '#' to it : */
X
X	    if (!line_state)
X		putc('#',bfile);
X
X	    fputs(df2lines[i],bfile);
X
X	    accumulated_size += strlen(df2lines[i]) + 1;
X
X	    /* set line state for next line: if this line was \n 	*/
X	    /* terminated, then the we are at the beginning of a line,  */
X	    /* otherwise we are in the middle of one :			*/
X
X	    line_state = index(df2lines[i],'\n') ? 0 : 1;
X	}
X
X	/* now write df1's name and contents in : */
X
X	fprintf(bfile,"%s\n",df1_remote_name);
X	accumulated_size += strlen(df1_remote_name) + 1;
X
X	line_state = 0;
X	while (fgets(line,LINE_LENGTH,df1))
X	{
X	    /* if we are at the BEGINNING of a line, add a '#' to it : */
X
X	    if (!line_state)
X		putc('#',bfile);
X
X	    fputs(line,bfile);
X
X	    accumulated_size += strlen(line) + 1;
X
X	    /* set line state for next line: if this line was \n 	*/
X	    /* terminated, then the we are at the beginning of a line,  */
X	    /* otherwise we are in the middle of one :			*/
X
X	    line_state = index(line,'\n') ? 0 : 1;		
X	}
X
X	/* mark job's files for deletion : */
X	strcpy(del_jobnames[jobs_this_batch],jobname);
X	strcpy(del_df1_names[jobs_this_batch],df1_name);
X	strcpy(del_df2_names[jobs_this_batch++],df2_name);
X
X	total_rmail_jobs++;
X    }
X    else 
X	if (registered_batches<MAX_REGISTERED_BATCHES)
X	{
X		/* Register the batch for later re-batching if usefull.	*/
X		/* Note that the are not likely to be many batch jobs	*/
X		/* eligable for rebatching (in terms of size and age)	*/
X		/* and that there is no harm in not registering some	*/
X		/* of them. They will be picked up by later runs	*/
X		/* if too many exist now...				*/
X
X		strcpy(jobnames[registered_batches],jobname);
X		strcpy(df1_names[registered_batches],df1_name);
X		strcpy(df2_names[registered_batches],df2_name);
X		jobs_uucdebat[registered_batches++] = is_uucdebat;
X	}
X
X    /* close cf,df1,df2 we don't them it any more : */
X
X    if (cf) fclose(cf);
X    if (df1) fclose(df1);
X    if (df2) fclose(df2);
X
X    return 0;
}
X
/* append_batch() :							*/
/* This function will re-batch a uudebat or uucdebat into the given	*/
/* batch (batchname). uucdebat jobs are uncompressed before they are	*/
/* appended to the batch.						*/
X
append_batch(jobname,df1_name,df2_name,is_uucdebat,batchname)
char	*df1_name,
X	*df2_name,
X	*batchname,
X	*jobname;
X
int	is_uucdebat;
{
X    char cmdstr[256];
X
X    /* Prepare the command to append batch contents to batch file :  */
X
X    if (is_uucdebat)
X	sprintf(cmdstr, "uncompress -c < %s >> %s", df1_name, batchname);
X    else
X	sprintf(cmdstr, "cat %s >> %s", df1_name, batchname);
X
X    /* execute the command, exit if non-zero exit status : */
X
X    if (system(cmdstr))
X	p_exit("error while uncompressing/appending batch",93);
X
X    /* mark job's files for deletion : */
X
X    strcpy(del_jobnames[jobs_this_batch],jobname);
X    strcpy(del_df1_names[jobs_this_batch],df1_name);
X    strcpy(del_df2_names[jobs_this_batch++],df2_name);
}
X
/* send_batch() :							*/
/* This function will send off a batch. It will handle both compressed	*/
/* and uncompressed batching, choosing between uudebat and uucdebat 	*/
/* as the uux'ed commands. Once successfully uux'ed, the batch's data	*/
/* file and the data files of all jobs batched in it are deleted.	*/
X
send_batch(batchname,nodename,compression_mode)
char *batchname,*nodename;
int compression_mode;
{
X    char cmdstr[256];
X    int i;
X
X    fprintf(stderr,"uubatch : sending batch...");
X
X    /* Prepare the uux command : */
X
X    if (compression_mode)
X	sprintf(cmdstr, "compress -c %s | uux - -r %s!uucdebat",
X			batchname, nodename);
X    else
X	sprintf(cmdstr, "uux - -r %s!uudebat < %s",
X			nodename, batchname);
X    
X    /* Execute it : */
X
X    if (system(cmdstr))
X	p_exit("error while uux'ing batch",115);
X
X    /* Remove batch temporary data file : */
X
X    if (unlink(batchname))
X	p_exit("error while removing batch file",120);
X
X    /* Remove all data files of jobs batched in this batch : */
X
X    for (i=0;i<jobs_this_batch;i++)
X    {
X	if (unlink(del_jobnames[i]) || unlink(del_df1_names[i]) ||
X	    unlink(del_df2_names[i]) )
X	    p_exit("error while deleting batched files",125);
X
X    }
X
X    /* restart job count : */
X
X    jobs_this_batch = 0;
X
X    fprintf(stderr,"batch sent.\n");
}
X
static int
digest_args(argc,argv,compression_mode)
int argc;
char **argv;
int *compression_mode;
{
X    int node_spec=0;
X    char c,*p;
X
X    /* the following externs are defined by getopt(3), look them up : */
X
X    extern char *optarg;
X    extern int optind;
X
X    *compression_mode = 0;
X    max_bytes = DF_MAX_BYTES;
X    max_cbytes = DF_MAX_CBYTES;
X    critical_size = DF_CRITICAL_SIZE;
X
X    /* extract the path to uubatch's binary directory. uucplock is 	*/
X    /* assumed to be there : 						*/
X
X    strcpy(uubatch_bin_path,argv[0]);
X    if (p = rindex(uubatch_bin_path,'/'))
X	*p='\0';
X
X    /* parse options : */
X
X    while((c = getopt(argc, argv, "cB:C:S:T:")) != -1)
X	switch(c) 
X	{
X	case 'c' : *compression_mode = 1; 			break;
X	case 'B' : max_bytes = atoi(optarg); 			break;
X	case 'C' : max_cbytes = atoi(optarg); 			break;
X	case 'S' : critical_size = atoi(optarg); 		break;
X	case 'T' : min_time_before_batch = atoi(optarg); 	break;
X	case '?' : return 1;
X	}
X
X    for (; optind < argc; optind++)
X    {
X	add_nodename(argv[optind]);
X	node_spec = 1;
X    }
X
X   /* return error (true value) if no node or no bin dir is specified : */
X
X   return (!node_spec); 
}
X
X
/* main() :								*/
/*									*/
/* This is the main program. See top of file to find out what it does	*/
/*									*/
/* Usage :								*/
/*	uubatch [-c] [-B n] [-C n] [-S n] [-T n] node [node2...]	*/
/*									*/
X
main(argc,argv)
int argc;
char **argv;
{
X    int i,compression_mode;
X    char *jobname,*nodename,pathname[256];
X    struct stat bfile_status;
X    DIR *dirp;
X    struct dirent *dp;
X
X    /* do nit-picking checks and argument extraction : */
X
X    if (digest_args(argc,argv,&compression_mode))
X	p_exit(
X	"Usage:\nuubatch [-c] [-B n] [-C n] [-S n] [-T n] node [node2...]"
X	,5);
X
X
X    /* loop on all node names specified in args, and do rmail batching on */
X    /* each one : 							  */
X
X    for(curr_node=first_node;curr_node;curr_node=curr_node->next)
X    {
X	nodename = curr_node->name;
X
X	fprintf(stderr,"uubatch : Doing node %s\n",nodename);
X
X	/* construct node's spool directory path : 			*/
X
X	strcpy(pathname,UUCP_SPOOL_DIR);
X	strcat(pathname,"/");
X	strcat(pathname,nodename);
X	    
X	/* cd to node's spool directory : 				*/
X
X	if (chdir(pathname))
X	    pe_exit("Cannot cd to node's spool directory",78);
X
X	/* put a uucp lock on the node, continue only if lock succeeds : */
X
X	if (!lock_node(nodename))
X	{
X	    
X	    tmpnam(batchname);	/* Get a name for the batch file.	*/
X	    
X	    accumulated_size = total_rmail_jobs = 
X		jobs_this_batch = registered_batches = 0;
X	    
X	    /* open the batch file : 					*/
X
X	    if (!(bfile=fopen(batchname,"w")))
X		pe_exit("Can't temporary batch file",82);
X	    
X	    /* open the spool directory in order to search it : 	*/
X
X	    dirp = opendir(pathname);
X	    
X	    /* loop on all jobs (searching the for C.* files): 		*/
X
X	    while ( dp = readdir(dirp) )
X		if (str_eq(jobname=dp->d_name,"C."))
X		{
X		    fprintf(stderr,"uubatch : Doing file %s\n",jobname);
X	    
X		    /* If batch file if "full", send it and reset it :	*/
X
X		    if ( (accumulated_size>critical_size) ||
X		         (jobs_this_batch>MAX_JOBS_PER_BATCH) )
X		    {
X			/* close the batch file, send it off, and open 	*/
X			/* a new one :		 			*/
X
X			if (fclose(bfile))
X			    pe_exit("error closing batch file",85);
X			send_batch(batchname,nodename,compression_mode);
X			if (!(bfile=fopen(batchname,"w")))
X			    pe_exit("Can't temporary batch file",90);
X			accumulated_size = 0;
X		    }
X	    
X		    /* Attempt to batch the current job in the loop :	*/
X
X		    batch_job(jobname);	
X		}
X	    
X	    /* now close the batch file : */
X
X	    if (fclose(bfile))
X		pe_exit("error closing batch file",95); 
X	    
X	    if (total_rmail_jobs) /* only re-batch registered jobs if needed.*/
X	    {
X		/* loop on all registered jobs (eligable for re-batching in  */
X		/* terms of size and age) and re-batch them :		     */
X
X		for (i=0;i<registered_batches;i++)
X		{
X		    stat(batchname,&bfile_status); /* get batch file stats. */
X	    
X		    /* If batch file if "full",send it off and reset it :   */
X
X		    if ( (bfile_status.st_size>critical_size) ||
X		         (jobs_this_batch>MAX_JOBS_PER_BATCH) )
X			send_batch(batchname,nodename,compression_mode);
X	    
X		    /* re-batch the current registered batch in the loop : */
X
X	    	    append_batch(jobnames[i],df1_names[i],df2_names[i],
X				    jobs_uucdebat[i],batchname);
X		}
X
X		stat(batchname,&bfile_status);	/* get batch file stats. */
X	    
X		/* send batch if there is anything left (usually there is) :*/
X
X		if (bfile_status.st_size>0)
X		    send_batch(batchname,nodename,compression_mode);
X	    }
X	    else  	/* if no rmail jobs, skipped batching : */
X		pd("No rmail jobs, skip batching.",100);
X	    
X	    if (unlock_node(nodename))
X		pds("unlock failed on node",nodename,110);
X
X	} /* of if on successfull lock .*/
X	else
X	    pds("lock failed on node",nodename,122);
X
X    unlink(batchname);
X
X    } /* of node loop. */
X
X    /* bye bye : */
X
X    my_exit(0);
}
SHAR_EOF
chmod 0644 uubatch.c ||
echo 'restore of uubatch.c failed'
Wc_c="`wc -c < 'uubatch.c'`"
test 21640 -eq "$Wc_c" ||
	echo 'uubatch.c: original size 21640, current size' "$Wc_c"
fi
# ============= uucdebat ==============
if test -f 'uucdebat' -a X"$1" != X"-c"; then
	echo 'x - skipping uucdebat (File already exists)'
else
echo 'x - extracting uucdebat (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'uucdebat' &&
#!/bin/sh
#
# uucdebat, uubatch version 1.0.5
#
# The command that should be executed is : "uncompress | uudebat".
# It is best to use absolute file name and paths here, since it is
# critical not to miss...
#
# Some check was addedd for those who install before they read the
# instructions. In that case, having some critical files in
# other-then-expected pathneme may cause uucdebat to silently eat all
# incomming mail, unless these checks are mnade.
#
# If you do read this, you may want to take out those checks, after verifying
# teh filenames. The only line that is neccsary from uucdabat functional
# point of view is:
#
#     /usr/ucb/uncompress | /usr/bin/uudebat
#
UNCOMP=/usr/ucb/uncompress
UNZIP=/usr/local/bin/unzip
UUDEBAT=/usr/bin/uudebat
FCAT=/usr/local/bin/fcat
X
#
# This is the user error is mailed to.
#
USER=uucp
X
report_error () { \
X	( \
X	echo "uucdebat : Incorrect configuration. Mail may have been lost.";
X	echo "           please check some pathnemss:" ;
X	echo "           specificaly, there is no compressor at $1";
X	echo "           or no uudebat at $UUDEBAT";
X	) \
X	| mail -s "uucdebat configuration error" $USER
};
X
case $1 in
unzip|zip|-unzip|-zip)
X	if test -x $UNZIP -a -x $UUDEBAT ;
X	then
X		$UNZIP -p | $UUDEBAT
X	else
X		report_error $UNZIP;
X	fi;;
X
freeze|melt|-freeze|-melt)
X	if test -x $FCAT -a -x $UUDEBAT ;
X	then
X		$FCAT -p | $UUDEBAT
X	else
X		report_error $FCAT;
X	fi;;
X
uncompress|compress|-uncompress|-compress|"")
X	if test -x $UNCOMP -a -x $UUDEBAT ;
X	then
X		 $UNCOMP -c | $UUDEBAT
X	else
X		report_error $UNCOMP;
X	fi;;
X
*)
X	( \
X	echo "uucdebat : Unknown compression scheme ($1). Mail may have been lost";
X	) \
X	| mail -s "uucdebat configuration error" $USER
esac
SHAR_EOF
chmod 0755 uucdebat ||
echo 'restore of uucdebat failed'
Wc_c="`wc -c < 'uucdebat'`"
test 1701 -eq "$Wc_c" ||
	echo 'uucdebat: original size 1701, current size' "$Wc_c"
fi
# ============= uucplock.c ==============
if test -f 'uucplock.c' -a X"$1" != X"-c"; then
	echo 'x - skipping uucplock.c (File already exists)'
else
echo 'x - extracting uucplock.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'uucplock.c' &&
X
/* 
X * uucplock.c, uubatch version 1.0.5 :
X *
X * This code is a modified version of the bsd tip uucplock.c source.
X * I got this source from UUNET and modified it a bit, making it 
X * a complete executable program that locks/unlocks an "object".
X * 
X * Below is the original Copyright found in the original file :
X *
X */
X
/*
X * Copyright (c) 1988 The Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that: (1) source distributions retain this entire copyright
X * notice and comment, and (2) distributions including binaries display
X * the following acknowledgement:  ``This product includes software
X * developed by the University of California, Berkeley and its contributors''
X * in the documentation or other materials provided with the distribution
X * and in all advertising materials mentioning features or use of this
X * software. Neither the name of the University nor the names of its
X * contributors may be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X */
X
#include <sys/types.h>
#include <sys/file.h>
#include <sys/dir.h>
#include <errno.h>
#ifndef NOHDB
#include <stdio.h>
#endif
X
#ifdef SCO
#include <limits.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define PMAX _POSIX_PATH_MAX
#define SetPosition SEEK_SET
#define index(_a,_b) strchr(_a,_b)
#else
#define PMAX MAXNAMLEN
#define SetPosition L_SET
#endif
X
#include "patchlevel.h"
X
/* 
X * uucp style locking routines
X * return: 0 - success
X * 	  -1 - failure
X */
X
X
uu_lock(objname)
X	char *objname;
{
X	extern int errno;
X	int fd, pid;
X	char spid[256];
X	char tbuf[sizeof(_PATH_LOCKDIRNAME) + PMAX];
X	off_t lseek();
#ifndef NOHDB
X	FILE *ffd;
#endif
X
X	(void)sprintf(tbuf, _PATH_LOCKDIRNAME, objname);
X	fd = open(tbuf, O_RDWR|O_CREAT|O_EXCL, 0666);
X	if (fd < 0) {
X		/*
X		 * file is already locked
X		 * check to see if the process holding the lock still exists
X		 */
X		fd = open(tbuf, O_RDWR, 0);
X		if (fd < 0) {
X			perror("lock open");
X			return(-1);
X		}
#ifdef NOHDB
X		/* this is old BSD code, it reads the number in binary */
X		/* form. This does not go well with HDB...		*/
X			if (read(fd, &pid, sizeof(pid)) != sizeof(pid)) {
#else
X		/* This is code for HDB UUCP, it puts the pid in as text : */
X			if (!(ffd=fdopen(fd,"r+"))) {
X				perror("fdopen : lock open");
X				return(-1);
X			}
X			if (!fscanf(ffd,"%d",&pid)) {
#endif
X				(void)close(fd);
X				perror("lock read");
X				return(-1);
X			}
X
X			if (kill(pid, 0) == 0 || errno != ESRCH) {
X				(void)close(fd);	/* process is still running */
X				return(-1);
X			}
X			/*
X			 * The process that locked the file isn't running, so
X			 * we'll lock it ourselves
X			 */
X			if (lseek(fd, 0L, SetPosition) < 0) {
X				(void)close(fd);
X				perror("lock lseek");
X				return(-1);
X			}
X			/* fall out and finish the locking process */
X		}
X	
X	pid = getppid();	/* Parent process is the shell */
#ifdef NOHDB
X	/* this is old BSD lock code, it writes the number in in binary */
X	/* form. This does not go well with HDB...			*/
X	if (write(fd, (char *)&pid, sizeof(pid)) != sizeof(pid)) {
X		(void)close(fd);
X		(void)unlink(tbuf);
X		perror("lock write");
X		return(-1);
X	}
#else
X	/* This is for HDB UUCP : it puts the pid in as text...		*/
X	sprintf(spid,"%10d\n",pid);
X	if (write(fd, spid, strlen(spid)) != strlen(spid)) {
X		(void)close(fd);
X		(void)unlink(tbuf);
X		perror("lock write");
X		return(-1);
X	}
#endif
X	(void)close(fd);
X	return(0);
}
X
uu_unlock(objname)
X	char *objname;
{
X	char tbuf[sizeof(_PATH_LOCKDIRNAME) + PMAX];
X
X	(void)sprintf(tbuf, _PATH_LOCKDIRNAME, objname);
X	return(unlink(tbuf));
}
X
p_usage(s)
char *s;
{
X	fprintf(stderr," FAILED. Wrong usage.\n");
X	fprintf(stderr," Usage : %s lock|unlock objname\n");
X	fprintf(stderr,"         Will return SUCCES or FAILED string.\n");
X	exit(-1);
}
X
main(argc,argv)
int argc;
char **argv;
{
X
X	int failed;
X	if (argc!=3)
X		p_usage(argv[0]);
X
X	if (!strcmp(argv[1],"lock"))
X	{
X		fprintf(stderr,"lock    : %s\n",
X			(failed=uu_lock(argv[2])) ? "FAILED" : "SUCCESS" );
X		exit(failed ? 1 : 0);
X	}
X	if (!strcmp(argv[1],"unlock"))
X	{
X		fprintf(stderr,"unlock  : %s\n",
X			(failed=uu_unlock(argv[2])) ? "FAILED" : "SUCCESS" );
X		exit(failed ? 1 : 0);
X	}
X
X	/* If we get here then wrong usage : */
X	p_usage(argv[0]);
}
SHAR_EOF
chmod 0644 uucplock.c ||
echo 'restore of uucplock.c failed'
Wc_c="`wc -c < 'uucplock.c'`"
test 4534 -eq "$Wc_c" ||
	echo 'uucplock.c: original size 4534, current size' "$Wc_c"
fi
# ============= uudebat.c ==============
if test -f 'uudebat.c' -a X"$1" != X"-c"; then
	echo 'x - skipping uudebat.c (File already exists)'
else
echo 'x - extracting uudebat.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'uudebat.c' &&
/*
X * uudebat.c, uubatch version 1.0.5
X *
X * uudebat: batched UUCP de-batcher
X *
X * Authors :
X *	     Baruch Cochavy 	(blue@dduster.hellnet.org)
X *	     Gil Tene 		(devil@hellnet.org).
X *
X *
X * This program will de-batch an incoming batch. Input file is fed via
X * stdin, and is composed of many UUCP files, with and added single 
X * character ('#') at the beginning of each line, each file data section
X * is preceded by the destination filename.
X *
X * This is where security comes in mind. This is one of the main reasons
X * the program is simple - it shouldn't take more then a couple of min. to 
X * figure out what the risks running it are. We have tried to cover all
X * possible security risks : Files are only opened in the uucp machine's
X * spool directory. The program checks to see that the destination
X * file name contains no "/" characters, and that the file is a valid
X * uucp file (only X. and D. files are allowed, no C. files are). 
X *
X * One may find this to be inadequate, and add some more security, sanity,
X * disk space checking and probably another zilions of checks. Fell free
X * to modify it in any way appropriate, but keep in mind your incoming
X * batched mail will be passing here, so be extra careful ro check it before
X * being installed.
X * 
X * Batch File format:
X * ------------------
X *                          file name #1
X *                          # line 1 of file 1
X *                          # line 2 of file 1
X *                          #      .
X *                          #      .
X *                          #      .
X *                          file name #2
X *                          # line 1 of file 2
X *                          # line 2 of file 2
X *                          #      .
X *                          #      .
X *                          #      .
X *
X * In version 1.0.4 and later we have added an optional "escape" line
X * syntax. The purpose of these lines is to allow more data about the
X * batched files to be added in later releases for "smarter" operation
X * (such as data for e-mail non-delivery bounces, etc.).
X * "escape" lines are lines in X. files which start with a "#!UUBATCH :" 
X * (in addition to the leading # char.). These lines, if encountered, 
X * are filtered out in uudebat 1.0.4 or later. The lines will not be 
X * filtered out in earlier versions, but they would also do no harm, 
X * since they are in uucp X. file comment format, and will be totaly 
X * ignored by uuxqt. Escape line length may NOT exceed 253 characters.
X *
X * We have also defined the future syntax of uubatch escape lines :
X *
X * #!UUBATCH : {uubatch facility} { parameter1 } { parameter2 } ...
X * 
X * Where uubatch facility is something like "DATE" (for e-mail date)
X * and each parameter is an ASCII string.
X *
X * All uubatch facility names NOT beginning with X- (e.g. X-FOO) are 
X * reserved by us for future use. If people wish to add their own 
X * extentions and features using the escape line feature, they should
X * use facility names beginning with X-.
X * 
X *
X */
X
#include <stdio.h>
#include <string.h>
#ifdef sun386i
extern char *getenv();
#else
#include <stdlib.h>
#endif
X
#ifdef SCO
#include <limits.h>
#define PMAX _POSIX_PATH_MAX
#define index(_a,_b) strchr(_a,_b)
#else
#include <sys/param.h>
#ifdef HAS_STRING
#include <string.h>
#else
#include <strings.h>
#endif
#define PMAX MAXPATHLEN
#endif
X
#include "patchlevel.h"
X
/* NOTICE: COMMAND is defined as the system executed string to result in
X * error message being mailed. You may like to change that.
X *
X * This string is used in a sprintf statment, and if changed, should 
X * include provisions for one string which represents the file name 
X * holding the error text.
X */
X
#define COMMAND "mail -s uudebat_error_report postmaster < %s\n"
X
/* UUBATCH escape perfix. Lines in the batch that begin with this prefix
X * won't get written to any output file. This line will be used in future
X * uubatch version to support various enhancments
X */
X
#define ESCAPE_PREFIX  "#!UUBATCH :"
#define ESCAPE_PREFIX2 "#%UUBATCH :"
X
#define EXIT_ERROR 1
#define EXIT_NO_ERROR 0
X
/* define line_state constants */
X
#define BEGINING_OF_LINE 0
#define MIDDLE_OF_LINE   1
#define ESCAPE_LINE   2
X
#define str_eq(_s1,_s2) (!strncmp(_s1,_s2,strlen(_s2)))
X
/* NOTE: the value of LINE_LENGTH below should never be set to anything 
X * less then a UUBATCH escape length, which is 12, to avoid spliting
X * the escape sequence amond two lines. Escape lines, however, can
X * bo of any liength.
X */
#define LINE_LENGTH 8192
X
static void notify_error();
static FILE * secure_fopen();
X
int main()
{
X	int
X		line_state,		/* state of input line being read: */
X
X		error_flag=0,	/* error flag for the file being   */
X						/* created.                        */
X
X		system_error=0;	/* global error flag               */
X
X	char
X		filename[PMAX],
X		output_filename[PMAX],
X		pathname[PMAX],
X		line[LINE_LENGTH],
X		*t,
X		*orig_system;
X
X	FILE
X		*fo;		/* output stream file pointer */
X
X
X	/* identify the machine this batch will come into : */
X
X	if (!(orig_system = getenv("UU_MACHINE")))
X	{
X		fprintf(stderr,"uudebat: must know originating mach. name\n");
X		exit(255);
X	}
X
X	/* cd to uucp machine's spool directory : */
X
X	sprintf(pathname,"%s/%s",UUCP_SPOOL_DIR,orig_system);
X	if (chdir(pathname))
X	{ 
X		perror("Cannot cd to node's spool directory"); 
X		exit(254); 
X	}
X
X	/* read the first line in the input stream. This line should  */
X	/* contain a file name :                                      */
X
X	t = fgets(filename, PMAX, stdin);
X
X	/* Loop until there are no more lines in stdin :              */
X
X	while (t)
X	{	
X		error_flag = 0;
X
X		/* line_state is used to control the hadling of lines as
X		 * they are read. UUdebat assumes no limit to line length. 
X		 * And therefor needs the line_state variable to distinguish
X		 * the following states :
X		 * 
X		 * BEGINING_OF_LINE - When at the begining of a new line.
X		 * MIDDLE_OF_LINE   - When in the middle of a line. Indicates it is
X		 *                    not needed to check for UUDEBAT line headers
X		 *                    or UUDEBAT escape line.
X         * ESCAPE_LINE      - When tossing escape line to the wind.
X		 */
X
X		line_state = BEGINING_OF_LINE;
X
X		/* Open the output file based on the requested file name,    */
X		/* while making sure security is not broken :                */
X
X		fo = secure_fopen(filename,output_filename,&error_flag);
X
X		/* Each input batch line is read, until that file is at end.
X		 * Each line is stripped of its first character, used to
X		 * identify it as belonging to that file, and special 
X		 * "escape" comment lines which may have been inserted in
X		 * X. files are filtered out :
X		 */
X
X		while (t=fgets(line, LINE_LENGTH, stdin)) 
X		{
X			if (line_state == BEGINING_OF_LINE )  /* first part of line */
X			{
X				if ( *t != '#' ) break;
X				t++;
X			}
X
X			if ( line_state == BEGINING_OF_LINE
X			  && ( str_eq(t, ESCAPE_PREFIX ) || str_eq(t, ESCAPE_PREFIX2) )
X			   )
X				line_state = ESCAPE_LINE;
X
X			/* if rest of line, or line does not begin with  */
X			/* UUBATCH escape, emit it.                      */
X
X			if ( (line_state != ESCAPE_LINE) || (filename[0]!='X') )
X				fputs(t,fo);
X
X			if ( line_state == BEGINING_OF_LINE  )
X                                 line_state = MIDDLE_OF_LINE;
X			if ( index(t,'\n') ) line_state = BEGINING_OF_LINE;
X		}
X
X		/* notify of possible error: */
X
X 		if (ferror(fo))
X		{
X			sprintf(line,"Write error(s) on \"%s\"", output_filename);
X			perror(line);
X			break;
X		}
X
X		/* since there are no more consecutive lines containing '#'
X		 * as their first character, this file has ended. We
X		 * therefore close it and start a new one, by moving the 
X		 * line read into the file name to be opened.
X		 */
X
X		if (fclose(fo))	 /* close file, notify of possible error: */
X		{
X			sprintf(line,"Close error on \"%s\"",output_filename);
X			perror(line);
X			break;
X		}
X
X		if (t)	
X		{
X		    if (strlen(line) > PMAX)
X		    {
X			fprintf(stderr,"Filename too long. Truncated.\n");
X			fprintf(stderr,"  Original filename: %s\n",line);
X		    }
X		    strncpy(filename,line,PMAX);
X		    filename[PMAX] = '\0';
X		}
X
X		/* now that the output file is closed, let's see what can we
X		 * do with it. If it's creation or writing met with some 
X		 * error, this is the time to tell somebody about it :
X		 */
X
X		if (error_flag) 
X			notify_error(output_filename);
X
X		/* make sure the exit status reflects an error if one 
X		 * occurs :
X		 */
X		system_error = ( error_flag ? EXIT_ERROR : EXIT_NO_ERROR );
X	}
X
X	exit(system_error);
}
X
/* notify_error : notify someone of an error, sending the data file... :
X */
static void
notify_error(output_filename)
char *output_filename;
{
X	int i;
X	char line[256];
X	sprintf(line, COMMAND, output_filename);
X	if (i = system(line))
X		fprintf(stderr, "uudebat: system error %d\n",i);
X	unlink(output_filename);
}
X
/* secure_fopen : Open the output file based on the requested file name,
X * while making sure security is not broken :
X */
static FILE *
secure_fopen(req_filename,output_filename,error_flag)
char *req_filename,*output_filename;
int *error_flag;
{
X	FILE
X        *fo;
X	char
X        line[128],
X        *t;
X	
X	if ( t=index( req_filename, '\n' ) ) *t='\0';
X
X	/* make sure file name begins with "D." or "X." : */
X
X	if ((req_filename[0] != 'D' && req_filename[0] != 'X')
X	  || req_filename[1] != '.')
X	{
X		fprintf(stderr,"Can only write D. and X. files: %s\n",
X			req_filename);
X		*error_flag = 4;
X	}
X
X	/* Check to verify no '/' is embedded. This will make sure the 
X	 * file is written in the spool directory, and not anywhere 
X	 * else ...
X	 */
X
X	if ( index(req_filename, '/') ) 
X	{
X		fprintf(stderr,"Cannot write to another directory: %s\n",
X			req_filename);
X		*error_flag = 5;
X	}
X
X	/* form the output filename : either it is the requested filename
X	 * (remember that we ARE already cd'ed to the system's uucp spool
X	 * directory), or we get a temporary filename if the requested
X	 * one is not legal :
X	 */
X
X	if (*error_flag)
X		tmpnam(output_filename);
X	else
X		strcpy(output_filename,req_filename);
X
X	/* We now open the output file : */
X
X	if (!(fo = fopen(output_filename,"w")))
X	{
X		sprintf(line,"Cannot open output file \"%s\"", output_filename);
X		perror(line);
X		
X		/* If file was not opened, open a temporary one : */
X
X		tmpnam(output_filename);
X
X		*error_flag = 6;
X
X		if (!(fo = fopen(output_filename,"w")))
X		{
X			fprintf(stderr,"Second open error in a row ... \n");
X			exit(255);
X		}
X	}
X
X	/* if there was an error, the file we are writing to will eventually
X	 * end up as mail to POSTMASTER, so we now put a header on it :
X	 */
X	
X	if (*error_flag)
X	{
X		fprintf(fo, "\nuudebat: error (%d)\n",*error_flag);
X		fprintf(fo, "Original file name requested was \"%s\"\n",
X			req_filename);
X		fprintf(fo, "\nMail body enclosed.\n");
X		fprintf(fo, "======================================\n");
X	}
X
X	return fo;
}
X
SHAR_EOF
chmod 0644 uudebat.c ||
echo 'restore of uudebat.c failed'
Wc_c="`wc -c < 'uudebat.c'`"
test 10831 -eq "$Wc_c" ||
	echo 'uudebat.c: original size 10831, current size' "$Wc_c"
fi
# ============= uubatch.1 ==============
if test -f 'uubatch.1' -a X"$1" != X"-c"; then
	echo 'x - skipping uubatch.1 (File already exists)'
else
echo 'x - extracting uubatch.1 (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'uubatch.1' &&
.TH uubatch 1 local
.SH NAME
uubatch \- mail jobs batcher
.SH SYNOPSIS
.B uubatch
[ -c ] [ -B n ] [ -C n ] [ -S n ] [ -T n ] node 
{ [ node2 ] ... }
.SH DESCRIPTION
This program will attempt to batch the given uucp nodes' queued
jobs into uudebat or uucdebat jobs. It takes care of the entire
batching process including uucp locking (via an external uucplock
program).
.PP
External uubatch program binaries (specificaly uucplock) are assumed
be runnable with the same path header uubatch was run with (as parsed
from argv[0]). This means that uucplock should usually be in the same
directory as uubatch.
.PP
The program scans each spool directory for C.<> files, which are
taken as the names of jobs to consider for batching (if they
meet the batching criteria below). 
Each queued job will be examined to see if the job should
be batched. If a job need not be batched then the program simply
skips over it. If the job needs to be batched, and the batching
succeeds and the batch is sccessfully uuxe'd, the job's data files
are deleted.
.SH BATCHING CRITERIA
rmail jobs with Data less than MAX_BYTES bytes
and more than min_time_before_batch min. old will be batched.
.PP
uudebat jobs with Data less than MAX_BYTES and
more than min_time_before_batch min. old will be batched.
.PP
uucdebat jobs with compressed data less than MAX_CBYTES
and more than min_time_before_batch min. old will be batched.
.PP
Only jobs created more than min_time_before_batch ago are
considered for batching, to avoid possible locking problems.
.PP
Batching will ONLY be done if at least one rmail job is
encountered in the node's spool directory. This saves
on unneeded re-batching of uudebat and uucdebat jobs.
.SH OPTIONS
.TP 8
.B \-c
compression on (default is off)
.TP 8
.B \-B n
MAX_BYTES = n (default DF_MAX_BYTES)
.IP
MAX_BYTES is the maximum size (in bytes) of an rmail
message or a uudebat job that may be added to a batch
Jobs larger than this parameter are not batched.
.TP 8
.B \-C n
MAX_CBYTES = n (default DF_MAX_CBYTES)
.IP
MAX_FILE_CBYTES is the maximum size (in bytes) of a
uucdebat job's compressed data file that may be added
to a batch. uucdebat jobs with compressed data files
larger than this parameter are not rebatched.
.TP 8
.B \-S n
CRITICAL_SIZE = n (default DF_CRITICAL_SIZE)
.IP
CRITICAL_BATCH_SIZE is the batch size (in bytes)
after which we send the batch off and start a new
one. Be aware of the fact that the actual MAXIMUM
batch size is actually the greater one of :
.IP
CRITICAL_BATCH_SIZE + MAX_BYTES_FOR_BATCH
.IP
.nf
CRITICAL_BATCH_SIZE\ +\ uncompressed(MAX_CBYTES_FOR_BATCH)
.fi
.TP 8
.B \-T n
min_time_before_batch\ =\ n\ seconds
(defaults to MIN_TIME_BEFORE_BATCH)
.IP
This is the age of a job (in seconds) before
it may be considered for batching.
.SH DIAGNOSTICS
uubatch Diagnostic
.B n s
.IP
.PP
uubatch Error 
.B n s
.IP
.TP 12
[5]
jobname too long.
.TP 12
[10]
jobname must be C.<jobid>
.TP 12
[15]
Can't open specified job file
.TP 12
[20]
Only one line in command file
.TP 12
[25]
No lines in command file
.TP 12
[30]
Oops, no S in line 1 of command file.
.TP 12
[35]
Oops, no S in line 2 of command file.
.TP 12
[40]
XX. data file name too long.
.TP 12
[45]
Can't open X. data file.
.TP 12
[50]
Too many lines in X. data file.
.TP 12
[55]
No rmail, uudebat or uucdebat in X. data file...
.TP 12
[60]
More than one rmail or uudebat, I won't do it! 
.TP 12
[65]
D. data file name too long.
.TP 12
[70]
Can't open D. data file.
.TP 12
[75]
Data file is too new (
.I n
sec), next time maybe. 
.TP 12
[78]
Cannot cd to node's spool directory
.IP
(Remember that uuclean on some systems removes empty uucp spool 
directories of valid uucp nodes).
.PP
.TP 12
[80]
file to big, not put in batch. 
.TP 12
[82]
Can't open temporary batch file
.TP 12
[85]
error closing batch file
.TP 12
[90]
Can't open temporary batch file
.TP 12
[93]
error while uncompressing/appending batch
.TP 12
[95]
error closing batch file
.TP 12
[100]
No rmail jobs, skip batching. 
.TP 12
[110]
unlock failed on node 
.TP 12
[112]
Oops, no space after first file name in command file. 
.TP 12
[113]
Oops, wrong remote name prefix in command file.
.TP 12
[114] 
Oops, remote file name with no end in command file...
.TP 12
[115]
error while uux'ing batch
.TP 12
[120]
lock failed on node 
.TP 12
[122]
error while removing batch file 
.TP 12
[125]
error while deleting batched files 
.SH SEE ALSO
uudebat(1) uucplock(1) uubatch(4)
.SH CURRENT VERSION
1.0.5
.SH AUTHORS
.TP 30
Gil Tene
devil@HellNet.org
.TP 30
Baruch Cochavy
blue@dduster.HellNet.org
.SH BUGS
Bugs ? What bugs ?
SHAR_EOF
chmod 0666 uubatch.1 ||
echo 'restore of uubatch.1 failed'
Wc_c="`wc -c < 'uubatch.1'`"
test 4569 -eq "$Wc_c" ||
	echo 'uubatch.1: original size 4569, current size' "$Wc_c"
fi
# ============= uudebat.1 ==============
if test -f 'uudebat.1' -a X"$1" != X"-c"; then
	echo 'x - skipping uudebat.1 (File already exists)'
else
echo 'x - extracting uudebat.1 (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'uudebat.1' &&
.TH uudebat 1 local
.SH NAME
uudebat \- mail batch de-batcher
.SH SYNOPSIS
.B uudebat
.SH DESCRIPTION
.I uudebat
is executed by
.I uuxqt.
It is invoked when a 
.I uudebat 
job is received by uucp. The batch
is created on the remote node, using 
.I uubatch
to join multiple 
.I rmail 
jobs into a single 
.I uudebat 
job. This batching/de-batching
scheme heavily reduces mail delivery cost.
.I uudebat 
de-batches the
.I rmail 
jobs onto the local incoming spool direcory, where they will be
picked up later by 
.I uuxqt 
and executed as normal 
.I rmail
jobs.
X
.SH DIAGNOSTICS
Cannot cd to node's spool directory
.IP
.I uudebat
could not cd to the machine's spool directory. This may suggest a configuration
error.
.PP
Write error(s) on "
.I file
"
.IP
An error occured while trying to write the output file. Recover is atempted,
but it may fail as this error suggests disk full conditions which can
hinder error recovery.
.PP
Close error on "
.I file
"
X
uudebat: system error
.I n
.IP
An error occured while delivering the extracted file to
.I uuxqt
for execution. This may also be an error code returned by 
.I uuxqt
itself.
.PP
Can only write D. and X. files: 
.I file
.IP
An illeagal name was requested for an extracted file in the batch.
.PP
Cannot write to another directory:
.I file
.IP
File name requested contains illegal characters that would result in writing
to some other directory than the designated spool directory for the specified
machine.
.PP
Cannot open output file "
.I file
"
.IP
A legal
.I uudebat
file name failed an open request. Error text follows.
.PP
Second open error in a row ...
.IP
Could not open temporary file to hold text when recovering from
an illegal file name condition.
.PP
uudebat: error (
.I n
)
.br
Original file name requested was "
.I file
"
.br
Mail body enclosed.
.br
======================================
.IP
This text is followed by the content of the file whose name was found
to be illegal. Usually, this text is mail to 
.I postmaster
but this can be changed at instalation time.
.SH SEE ALSO
uubatch(1) uucplock(1) uubatch(4)
.SH CURRENT VERSION
1.0.5
.SH AUTHORS
.TP 30
Gil Tene
devil@HellNet.org
.TP 30
Baruch Cochavy
blue@dduster.HellNet.org
.SH BUGS
Bugs ? What bugs ?
SHAR_EOF
chmod 0666 uudebat.1 ||
echo 'restore of uudebat.1 failed'
Wc_c="`wc -c < 'uudebat.1'`"
test 2227 -eq "$Wc_c" ||
	echo 'uudebat.1: original size 2227, current size' "$Wc_c"
fi
# ============= patchlevel.h ==============
if test -f 'patchlevel.h' -a X"$1" != X"-c"; then
	echo 'x - skipping patchlevel.h (File already exists)'
else
echo 'x - extracting patchlevel.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'patchlevel.h' &&
#define PATCHLEVEL "uubatch 1.0.5"
SHAR_EOF
chmod 0644 patchlevel.h ||
echo 'restore of patchlevel.h failed'
Wc_c="`wc -c < 'patchlevel.h'`"
test 35 -eq "$Wc_c" ||
	echo 'patchlevel.h: original size 35, current size' "$Wc_c"
fi
exit 0
