[mod.sources] v07i13: Multivol V1.00 - multivolume backup utility, part02/02

sources-request@munnari.OZ (08/29/86)

Submitted-by: Tony O'Hagan <tony%uqcspe.oz@seismo.css.gov>
Mod.sources: Volume 17, Issue 13
Archive-name: multivol

# this is a shell archive, extract with sh, not csh
echo x - "ask.c" 2>&1
sed "s/^X//" >"ask.c" <<'!The!End!'
X#include <stdio.h>
X#define	bool	short
X#define	TRUE	1
X#define FALSE	0
X
Xchar *
Xask(prompt)
X	char	*prompt;
X{
X	static	char	ans[256];
X	static	bool	ttyclosed = TRUE;
X	static	FILE	*tty;
X
X	if (ttyclosed)
X		if ((tty = fopen("/dev/tty", "r")) == NULL) {
X			perror("/dev/tty");
X			exit(1);
X		} else
X			ttyclosed = FALSE;
X
X	fprintf(stderr, prompt);
X	fgets(ans, sizeof(ans), tty);
X	/* fclose(tty); */
X	return ans;
X}
X
X/* VARARGS1 */
Xchar *
Xaskf(format, val1, val2, val3, val4)
X	char	*format;
X	int	val1, val2, val3, val4;
X{
X	char	prompt[80];
X
X	sprintf(prompt, format, val1, val2, val3, val4);
X	return ask(prompt);
X}
X
Xbool
Xy_or_n(prompt)
X	char	*prompt;
X{
X	char	*ans;
X	bool	yes, no;
X
X	char	*ask();
X
X	do {
X		ans = ask(prompt);
X		no  = ans[1] == '\n' && (ans[0]  == 'n' || ans[0]  == 'N' );
X		yes = ans[1] == '\n' && (ans[0]  == 'y' || ans[0]  == 'Y' );
X	} while (! (yes || no) );
X	return(yes);
X}
!The!End!

echo x - "bcopy.c" 2>&1
sed "s/^X//" >"bcopy.c" <<'!The!End!'
X#ifdef vax
Xbcopy(from, to, len)
X	char *from, *to;
X	int len;
X{
X	asm("	movc3	12(ap),*4(ap),*8(ap)");
X}
X#else 
X#ifdef	SYSV
X/*
X * Added by moderator, bcopy.c for Sys V.
X *
X */
X
Xextern char *memcpy();
X
Xchar *
Xbcopy(from, to, count)
X	char *from;
X	char *to;
X	int count;
X{
X	return (memcpy(to, from, count));
X}
X#else
Xbcopy(from, to, len)
X	register char	*to, *from;
X	register int	len;
X{
X	if (from > to) {
X		while(len--)
X			*to++ = *from++;
X	} else {	/* not handled in standard version of bcopy() */
X		to	+= len;
X		from	+= len;
X		while(len--)
X			*--to = *--from;
X	}
X}
X#endif
X#endif
!The!End!

echo x - "error.c" 2>&1
sed "s/^X//" >"error.c" <<'!The!End!'
X#include <stdio.h>
X	extern	int	errno;
X	extern	char	*O_name;
X/*VARARGS1*/
Xfatal(fmt, arg1, arg2, arg3)
X	char	*fmt;
X	int	arg1, arg2, arg3;
X{
X	warning(fmt, arg1, arg2, arg3);
X#ifdef	DEBUG
X	abort();
X#else
X	exit(1);
X#endif
X}
X
Xsfatal(str)
X	char	*str;
X{
X	swarning(str);
X#ifdef	DEBUG
X	abort();
X#else
X	exit(1);
X#endif
X}
X
X/*VARARGS1*/
Xwarning(fmt, arg1, arg2, arg3)
X	char	*fmt;
X	int	arg1, arg2, arg3;
X{
X	fprintf(stderr, "%s: ", O_name);
X	fprintf(stderr, fmt, arg1, arg2, arg3);
X	fprintf(stderr, "\n");
X}
X
Xswarning(str)
X	char	*str;
X{
X#ifdef	DEBUG
X	fprintf(stderr, "(%d)  ", errno);
X#endif
X	fprintf(stderr, "%s: ", O_name);
X	perror(str);
X}
!The!End!

echo x - "mount.c" 2>&1
sed "s/^X//" >"mount.c" <<'!The!End!'
X/* multivol  V1.00  5-Jun-85  Tony O'Hagan
X * multivol (8) - handle multivolume files
X * wrvols() - function of multivol main()
X */
X
X#include <stdio.h>
X#include <ctype.h>
X
X#include "multivol.h"
X
X/*  Globals */
X	extern	bool	dsp_vols, vfy_vols, rd_flag, wr_flag;
X	extern	char	*label;
X
Xup_vhdr *
Xmount_vol(vol_nam, vol_num, timestamp)
X	/* returns header read on mounted volume */
X	char	*vol_nam;
X	int	vol_num;
X	long	timestamp;
X{
X	up_vhdr	*upvh; /* returned value */
X	bool	ok;
X
X	bool	vol_ok();
X	void	print_vhdr();
X	bool	y_or_n();
X	up_vhdr *rd_vhdr();
X	char	*askf();
X#ifdef	REWIND
X	char	*ask();
X#endif
X
X	tracef((tr, "+ mount_vol(%s, %d, %ld)", vol_nam, vol_num, timestamp));
X
X	do {	
X		askf("Mount volume #%d and type <return> ", vol_num);
X
X		if (rd_flag || dsp_vols || vfy_vols)
X			upvh = rd_vhdr(vol_nam); /* Read volume header */
X		else {
X			upvh = NULVHD; 
X			trace("goto no_vhdr");
X			goto no_vhdr;  /* no volume header was read */
X		}
X
X		if (upvh == NULVHD) {
X			ok = !rd_flag; /* ok unless attempting to read volume */
X		} else {
X			if (dsp_vols || vfy_vols) {
X		/* Print header & check correct volume is read/overwritten */
X				putc('\n', stderr);
X				print_vhdr(upvh);
X				putc('\n', stderr);
X			}
X			ok = vol_ok(upvh, vol_num, timestamp);
X		}
X		tracef((tr, "vfy_vols=%d, ok=%d upvh=x%x", vfy_vols, ok, upvh));
X	} while ( (vfy_vols || !ok) && y_or_n("Remount required [y/n] ? ") );
X
X#ifdef	REWIND
X	ask("Ensure volume is rewound and type <return> ");
X#endif
X
Xno_vhdr:
X	trace("- mount_vol()");
X	return(upvh);
X}
X
Xbool
Xvol_ok(upvh, vol_num, timestamp)
Xup_vhdr	*upvh;
X	int	vol_num;
X	long	timestamp;
X/* Check correct volume is mounted
X * vol_num	= volume no. expected on this volume
X * timestamp	= timestamp on volumes being written
X * upvh		= unpacked volume header on volume about to be read/overwritten.
X */
X{
X	bool	ok = TRUE;	/* returns ok */
X	static	up_vhdr	up_1st;	/* first volume header read */
X
X	trace("+ vol_ok()");
X	if (wr_flag) {	/* if writing volumes */
X		if (timestamp == upvh->up_timestamp) {
X			fprintf(stderr, 
X			"** This clobbers a volume just written !\n");
X			ok = FALSE;
X		}
X	} else if (rd_flag) { /* if reading volumes */
X		if (vol_num == 1) { /* record header of first volume */
X			bcopy((char *)upvh, (char *)&up_1st, sizeof(up_vhdr));
X			if (label != NULL &&strcmp(label, upvh->up_label) != 0){
X				fprintf(stderr, "** Label does not match\n");
X				ok = FALSE;
X			}
X		}
X		if (vol_num >= 2 && up_1st.up_timestamp != upvh->up_timestamp) {
X			fprintf(stderr, "** Volume not part of volume set\n");
X			ok = FALSE;
X		} else if (upvh->up_vol_num != vol_num) {
X			fprintf(stderr, "** Volume no. out of sequence\n");
X			ok = FALSE;
X		}
X	}
X	trace("- vol_ok()");
X	return(ok);
X}
!The!End!

echo x - "multivol.c" 2>&1
sed "s/^X//" >"multivol.c" <<'!The!End!'
X/* multivol  V1.00  5-Jun-85  Tony O'Hagan
X * multivol (8) - handle multivolume files
X * multivol [-i] [-o] [-v] [-w] [-t] [-b blocksize]
X *      [-n blocks-per-vol] [-l label] [device]
X * Source files:
X *   ask.c bcopy.c error.c mount.c multivol.c rd.c testdata.c trace.c vhdr.c wr.c
X *   multivol.h options.h trace.h
X * Other files:
X *   Makefile READ_ME multivol.8 testdata.c
X */
X#include <stdio.h>
X#include <ctype.h>
X#include "options.h"
X#include "multivol.h"
X
X#define	ACCREAD	4
X#define ACCWRIT	2
X
X#ifndef DEFBSIZ
X#	include <sys/param.h>
X#	undef	bcopy
X#	define DEFBSIZ	BSIZE	/* default block size */
X#endif
X
X#ifndef DEFBLIM
X#	define DEFBLIM	NOLIMIT	/* default block limit (no. per volume) */
X#endif
X
X#define DEFLABEL	"no_label"	/* Default label */
X
X#ifndef DEFDEV  /* may be cc -DDEFDEV= */
X#	define DEFDEV	"/dev/multivol"
X#endif
X
X/*  Globals */
X	export	char	*prog_name, *label = DEFLABEL, *device = DEFDEV;
X	export	bool	dsp_blks, dsp_vols, vfy_vols, rd_flag , wr_flag;
X	export	long	blk_lim = DEFBLIM;
X	export	long	blk_siz = DEFBSIZ;
X
Xvoid
Xmain(argc, argv)	/* multivol main() */
X	int	argc;
X	char	*argv[];
X{
X	char	*bsizstr;
X	up_vhdr	*upvh;
X
X	void	rd_vols(), wrvols(), print_vhdr();
X	up_vhdr *rd_vhdr(); 
X	long	kval(), atol();
X	char	*vol_file();
X	int	t_int;
X	bool	y_or_n();
X
X	trace( "+ main()");
X
X	rd_flag = wr_flag = dsp_vols = dsp_blks = vfy_vols = FALSE;
X	OPTIONS ("[-i] [-o] [-v] [-w] [-t] [-b blocksize] [-n blocks-per-vol] [-l label] [device]")
X		FLAG	('i', rd_flag)
X#ifdef DEBUG
X		FLAG	('D', tron)
X#endif
X		FLAG	('o', wr_flag)
X		FLAG	('t', dsp_vols)
X		FLAG	('v', dsp_blks)
X		FLAG	('w', vfy_vols)
X		STRING	('b', bsizstr)
X			if ((blk_siz = kval(bsizstr)) == -1) 
X				fatal("** Illegal block size %s", bsizstr);
X			else if (blk_siz < MINBLK)
X				fatal("** Minimum block size is %d", MINBLK);
X			else { /* test if blk_siz value fits in int */
X				t_int = blk_siz;
X				if (blk_siz != t_int)
X					fatal("** Block size too big");
X			}
X		NUMBER	('n', blk_lim)
X		STRING	('l', label)
X			if (index(label, ' ') != 0)
X				fatal("** Label must not contain white space");
X	ENDOPTS
X	prog_name = argv[0];
X	if (argc > 1)
X		device = argv[1];
X
X	tracef((tr, "BHDSIZ = %d   VHDSIZ = %d", BHDSIZ, VHDSIZ));
X	tracef((tr, "prog_name:%s  label:%s  blk_siz:%ld",
X		prog_name, label, blk_siz));
X	tracef((tr, "blk_lim:%ld  dsp_blks:%d  dsp_vols:%d  vfy_vols:%d",
X		blk_lim, dsp_blks, dsp_vols, vfy_vols));
X	
X	if (rd_flag && wr_flag)
X		fatal("** Only -i or -o may be specified");
X	else if (rd_flag) {
X		if (access(device, ACCREAD) == -1)
X			sfatal(device);
X		rd_vols();
X	} else if (wr_flag) {
X		if (access(device, ACCREAD & ACCWRIT) == -1)
X			sfatal(device);
X		wrvols();
X	} else {
X		/* Print details of volume header */
X		/* Note: No prompt to mount volume */
X		if (access(device, ACCREAD) == -1)
X			sfatal(device);
X		if ((upvh = rd_vhdr(vol_file(device, 1))) != NULVHD)
X			print_vhdr(upvh);
X	}
X
X	trace("- main()");
X	exit(0);
X}
X
Xlong
Xkval(kstr) 
X	char *kstr;
X{
X	long	n, atol();  /* return n */
X	char	c;
X
X	trace("+ kstr()");
X	n = atol(kstr);
X	c = kstr[strlen(kstr)-1];
X	if (! isdigit(c)) {
X		switch (c) {
X		case 'b':case 'B': 
X			n *= 512; break;
X		case 'k':case 'K': 
X			n *= 1024; break;
X	/*	case 'm':case 'M': 
X			n *= 1024*1024; break;	*/
X		default:
X			n = -1;
X		}
X	}
X	trace("- kstr()");
X	return(n);
X}
!The!End!

echo x - "rd.c" 2>&1
sed "s/^X//" >"rd.c" <<'!The!End!'
X/* multivol(8)  V1.00  5-Jun-85  Tony O'Hagan
X * Handles multivolume files
X */
X#include <stdio.h>
X#include "multivol.h"
X
X/*  Globals */
X	extern	char	*label, *device;
X	extern	long	blk_lim;
X	extern	long	blk_siz;
X
X	/* TRUE when block containing ENDBLK block type has been read */
X	static	bool	END_read;  
X	static	long	blk_num;
X
Xvoid
Xrd_vols()
X{
X	char	*dta_ptr;
X	long	dta_got;
X
X	void	show_blks();
X	long	rd_get_dta(), rd_put_dta();
X
X	trace("+ rd_vols()");
X
X	END_read = FALSE;
X	do {
X		dta_got = rd_get_dta(&dta_ptr);
X		tracef((tr, "dta_got = %ld", dta_got));
X		if (dta_got > 0L)
X			rd_put_dta(dta_ptr, dta_got);
X	} while (!END_read);
X
X	show_blks(-1L);	/* terminate display of blocks */
X	trace("- rd_vols()");
X	return;
X}
X
Xlong
Xrd_put_dta(dta_ptr, dta_len)
X	char	*dta_ptr;
X	long	dta_len;
X{
X	long	n_writ;
X	trace("+ rd_put_dta()");
X
X	if ((n_writ = write(fileno(stdout), dta_ptr, (int)dta_len)) == -1L) {
X		n_writ = 0L;
X		swarning("stdout");
X	} else if (n_writ < dta_len)
X		warning("** Only output %ld bytes of %ld bytes in block #%ld",
X			n_writ, dta_len, blk_num);
X
X	tracef((tr, "- rd_put_dta() n_writ = %ld", n_writ));
X	return n_writ ;
X}
X
X/* Controls when volumes require mounting and
X * allocates buffer space for the 1st volume.
X */
Xlong
Xrd_get_dta(dta_ptr)
X	char	**dta_ptr;
X{
X	void	show_blks();
X	long	rd_blk();
X	char	*new_buf(), *vol_file();
X	up_vhdr	*mount_vol();
X
X	char	*vol_nam;
X	long	n_read;
X	up_vhdr	*upvh;
X
X	static	char	*blk_ptr;
X	static	int	dev, vol_num = 0;
X	static	bool	new_vol = TRUE;
X
X	tracef((tr, "+ rd_get_dta(%x)", *dta_ptr));
X
X	if (new_vol) {
X		blk_num = 0L;
X		new_vol = FALSE;
X		vol_nam = vol_file(device, ++vol_num);
X		if (vol_num > 1) {
X			show_blks(-1L);	
X			trace("close");
X			close(dev);	/* Last volume is closed by exit() */
X		}
X		if ((upvh = mount_vol(vol_nam, vol_num, 0L )) == NULVHD) {
X			END_read = TRUE;
X			trace("- rd_get_dta()");
X			return 0L;
X		} 
X
X		trace("set blk_siz & blk_lim from vol header");
X		blk_siz = upvh->up_siz_blk;
X		blk_lim = upvh->up_lim_blk;
X
X		if (vol_num == 1) {
X			blk_ptr = new_buf(blk_siz + VHDSIZ);
X		}
X
X		if ((dev = open(vol_nam, READ)) == -1)
X			sfatal(vol_nam);
X
X		/* setup pointer to data in block */
X		*dta_ptr = blk_ptr + BHDSIZ + VHDSIZ;
X	} else
X		*dta_ptr = blk_ptr + BHDSIZ;
X
X	if ((n_read = rd_blk(dev, blk_ptr)) == 0L) {
X		/* Couldn't read last block on current volume */
X		new_vol = TRUE;
X		if (blk_lim != NOLIMIT && !END_read)
X			fprintf(stderr,
X			"** Could only read %ld blocks on volume", blk_num);
X		trace("- rd_get_dta()");
X		return 0L ;
X	} else {
X		if (blk_lim != NOLIMIT && blk_num >= blk_lim)
X			new_vol = TRUE;
X		trace("- rd_get_dta()");
X		/* return amount of data written (excl. headers - See diag.) */
X		return(n_read - (*dta_ptr - blk_ptr));
X	}
X}
X
Xlong
Xrd_blk(dev, blk_ptr)
X	int	dev;
X	char	*blk_ptr;
X#define bh_ptr	((blk_hdr *) blk_ptr)
X{
X	int	chk_sum;
X	char	xor_sum();
X	long	blk_len, n_read;
X
X	trace("+ rd_blk()");
X	n_read = read(dev, blk_ptr, (int)blk_siz);
X	if (n_read == blk_siz) {
X		show_blks(++blk_num);
X	} else {
X		if (n_read == -1L)
X			swarning(device);
X		else
X			warning("** Block #%ld is only %ld bytes",
X				blk_num+1, n_read);
X		/* END_read = TRUE;	- abort gracefully */
X		trace("- rd_blk()");
X		return 0L ;
X	}
X
X	END_read = bh_ptr->bh_blktyp == ENDBLK;
X
X	sscanf(bh_ptr->bh_dtalen, "%6ld", &blk_len);
X	tracef((tr, "blk_len = %ld", blk_len));
X
X		/* Calc. checksum */
X	if (chk_sum = xor_sum(blk_ptr, blk_siz) != '\0')
X		warning("** Block #%ld check sum error (sum = %x hex)\n",
X			blk_num, chk_sum);
X
X	trace("- rd_blk()");
X	return(blk_len);
X}
!The!End!

echo x - "testdata.c" 2>&1
sed "s/^X//" >"testdata.c" <<'!The!End!'
X#include <stdio.h>
Xmain(argc, argv)	/* garbo main() */
X	int	argc;
X	char	*argv[];
X{
X	long	atol();
X	register long	j;
X	register char	*str;
X	register int	no_eoln, i;
X	
X	if (no_eoln = (*argv[1] == '-' && *++argv[1] == 'n')) {
X		argv++;
X		argc--;
X	}
X	for (i = 1; i < argc; i += 2) {
X		str = argv[i+1];
X		for (j = atol(argv[i]); j--; ) {
X			printf("%s", str);
X			if (!no_eoln)
X				putc('\n', stdout);
X		}
X	}
X}
!The!End!

echo x - "trace.c" 2>&1
sed "s/^X//" >"trace.c" <<'!The!End!'
X#ifdef DEBUG	/* Only used if DEBUG is defined */
X#include <stdio.h>
X	int	tron;	/* global = trace on */
X	char	tr[160];
X
Xstrace(file, line, step)
X	char	*file, *step;
X	int	line;
X{
X	static int	indent = 0;
X	register	i;
X
X	if (*step == '-')
X		indent--; 
X	if (tron) {
X		fprintf(stderr, "%14s: %5d: ", file, line);
X		for (i = indent; i--; )
X			fprintf(stderr, ".  ");
X		fprintf(stderr, "%s\n", step);
X	}
X	if (*step == '+')
X		indent++; 
X}
X#endif
!The!End!

echo x - "vhdr.c" 2>&1
sed "s/^X//" >"vhdr.c" <<'!The!End!'
X/* multivol  V1.00  5-Jun-85  Tony O'Hagan
X * multivol (8) - handle multivolume files
X * vhdr.c - functions performed on volume header
X */
X#include <stdio.h>
X#include "multivol.h"
X
X	extern	long	blk_siz, blk_lim;
X	extern	char	*label;
X
Xup_vhdr *
Xrd_vhdr(devname)
X	char	*devname;
X/* Read volume header from the first block and rewind (close()) the volume */
X{
X	up_vhdr *ans;
X	int	dev;
X	int	n_read;
X	char	*blk;
X
X	char	*new_buf();
X	up_vhdr	*unpack_vhdr();
X	void	free();
X
X	tracef((tr, "+ rd_vhdr(%s)", devname));
X	if ((dev = open(devname, READ)) == -1) {
X		swarning(devname);
X		trace("- rd_vhdr()");
X		return(NULVHD);
X	}
X
X	blk = new_buf(blk_siz);
X	trace("reading volume header");
X
X	tracef((tr, "read(%d, %x, %d)", dev, blk, (int)blk_siz));
X	if ((n_read = read(dev, blk, (int)blk_siz) ) == -1) {
X		swarning(devname);
X		ans = NULVHD;
X	} else {
X		if (n_read == 0) {
X			fprintf(stderr, "** No header found on volume\n");
X			ans = NULVHD;
X		} else if (n_read < MINBLK) {
X			fprintf(stderr,
X				"** Header block is only %d bytes\n", n_read);
X			ans = NULVHD;
X		} else {	/* vol_hdr after blk_hdr */
X			tracef((tr, "blk+BHDSIZ = %x",
X				(vol_hdr *)(blk + BHDSIZ)));
X			if (NULVHD ==
X				(ans = unpack_vhdr((vol_hdr *)(blk + BHDSIZ))) )
X				fprintf(stderr, "** Invalid volume header\n");
X		}
X	}
X	close(dev);
X	trace("free(blk)");
X	free(blk);
X	trace("- rd_vhdr()");
X	return ans ;
X}
X
Xvoid
Xprint_vhdr(up)
X	up_vhdr	*up;
X/* Print volume header details */
X{
X#ifndef	time_t
X#	include <sys/types.h>
X#endif
X	char	*ctime();
X	time_t	t_stamp;
X
X	trace("+print_vhdr()");
X	t_stamp = (time_t) up->up_timestamp;
X
X	/* Note: ctime() returns date & time with a \n following */
X	fprintf(stderr, 
X		"Written using: %s V%s  Label: %s  %s", up->up_prog_name,
X		up->up_version, up->up_label, ctime(&t_stamp) );
X	fprintf(stderr, 
X		"Vol. no.: %d   Block size: %ld   Blocks per volume: %ld\n",
X		up->up_vol_num, up->up_siz_blk, up->up_lim_blk);
X
X	trace("-print_vhdr()");
X}
X
Xvoid
Xpack_vhdr(pk, vol_num, timestamp)
X	char	*pk;
X	int	vol_num;
X	long	timestamp;
X{
X	sprintf(pk, VHDPACK,
X		vol_num, PROGNAM, VERSION, label, timestamp, blk_siz, blk_lim);
X}
X
Xup_vhdr *
Xunpack_vhdr(pk)
X	vol_hdr		*pk;
X{
X	static	up_vhdr		up;
X	
X	tracef((tr, "+ unpack_vhdr(pk = %x)", pk));
X	if(VHDFIELDS == sscanf((char *)pk, VHDUNPACK,
X		&up.up_vol_num, up.up_prog_name, up.up_version,
X		up.up_label, &up.up_timestamp, &up.up_siz_blk, &up.up_lim_blk)) {
X		trace("- unpack_vhdr");
X		return &up;
X	} else {
X		trace("- unpack_vhdr = NULVHD");
X		return NULVHD;
X	}
X}
!The!End!

echo x - "wr.c" 2>&1
sed "s/^X//" >"wr.c" <<'!The!End!'
X/* multivol(8)  V1.00  5-Jun-85  Tony O'Hagan */
X#include <stdio.h>
X#include <ctype.h>
X#include <errno.h>
X	extern	int	errno;
X
X#include "multivol.h"
X
X/*  Globals */
X	extern	bool	dsp_blks;
X	extern	char	*label, *device;
X	extern	long	blk_lim;
X	extern	long	blk_siz;
X
X	/* TRUE when ENDBLK block type has been written */
X	static	bool	END_written;
X	static	long	blk_num;
X
Xvoid
Xwrvols()
X{
X	char	*buf_ptr, *dta_ptr;
X	long	usu_dta, dta_left, dta_put;
X
X	void	bcopy(), show_blks();
X	char	*new_buf();
X	long	wr_get_dta(), wr_put_dta();
X
X	trace("+ wrvols()");
X
X	buf_ptr = new_buf(blk_siz+VHDSIZ);
X
X	dta_left = 0L;
X	END_written = FALSE;
X	dta_ptr = buf_ptr + BHDSIZ + VHDSIZ; /* See diag. of buffer */
X	usu_dta = blk_siz - BHDSIZ;	/* usual amount of data per block */
X
X	while ( TRUE ) {
X			/* Attempt to fill up data buffer to 'usu_dta' chars &
X				append new data after data left over */
X		dta_left += wr_get_dta(dta_ptr + dta_left, usu_dta - dta_left);
X		tracef((tr, "after wr_get_dta dta_left = %ld", dta_left));
X
X		if (dta_left <= 0L && END_written)
X			break;
X
X			/* Attempt to dispose of whatever is in data buffer */
X		dta_left -= (dta_put = wr_put_dta(dta_ptr, dta_left));
X		tracef((tr, "after wr_put_dta dta_left = %ld", dta_left));
X
X			/* if any left, copy to top of data buffer */
X		if (dta_left > 0L && dta_put > 0L)
X			bcopy(dta_ptr + dta_put, dta_ptr, (int)dta_left);
X	}
X	show_blks(-1L);	/* terminate display of blocks */
X	if (dsp_blks)
X		fprintf(stderr, "%ld blocks on last volume", blk_num);
X	trace("- wrvols()");
X}
X
Xlong
Xwr_get_dta(dta_ptr, dta_req)
X	char	*dta_ptr;
X	long	dta_req;
X{
X	long	n_read, tot_read;
X
X	tracef((tr, "+ wr_get_dta(%x, %ld)", dta_ptr, dta_req));
X
X	/* Get at least the required amount of data */
X	for (tot_read = 0L; tot_read < dta_req; tot_read += n_read) {
X		n_read = read(fileno(stdin),
X			dta_ptr + tot_read, (int)(dta_req - tot_read));
X		tracef((tr, "n_read = %ld", n_read));
X		if (n_read == -1) {
X			n_read = 0L;
X			swarning("stdin");
X		}
X		if (n_read == 0L)
X			break;
X	}
X
X	tracef((tr, "- wr_get_dta() tot_read = %ld", tot_read));
X	return tot_read ;
X}
X
X/* Controls when volumes require mounting */
Xlong
Xwr_put_dta(dta_ptr, dta_len)
X	char	*dta_ptr;
X	long	dta_len;
X{
X	void	wr_vol(), show_blks();
X	char	*blk_ptr;
X	long	wr_blk(), n_writ, blk_len;
X
X	static	int	dev;
X	static	bool	new_vol = TRUE;
X
X	tracef((tr, "+ wr_put_dta(%x, %ld)", dta_ptr, dta_len));
X
X	blk_ptr = dta_ptr;
X	blk_len = dta_len;
X
X	if (new_vol) {
X		blk_ptr -= VHDSIZ;
X		blk_len += VHDSIZ;
X		/* mount next volume & pre-pend volume header to data buffer */
X		wr_vol(&dev, blk_ptr);
X		blk_num = 0L;
X		new_vol = FALSE;
X	}
X
X	blk_ptr -= BHDSIZ;
X	blk_len += BHDSIZ;
X	if ((n_writ = wr_blk(dev, blk_ptr, blk_len)) == 0L) { 
X		/* last block won't fit on current volume */
X		new_vol = TRUE;
X		if (blk_lim != NOLIMIT)
X			fprintf(stderr,
X			"\n** Could only fit %ld block(s) on volume\n",blk_num);
X		trace("- wr_put_dta()");
X		return(0L);
X	} else { 
X		show_blks(++blk_num);
X		/* test volume block limit */
X		if (blk_lim != NOLIMIT && blk_num >= blk_lim)
X			new_vol = TRUE;
X		/* return amount of data written (excl. headers - See diag.) */
X		trace("- wr_put_dta()");
X		return(n_writ - (dta_ptr - blk_ptr));
X	}
X}
X
Xlong
Xwr_blk(dev, blk_ptr, blk_len)
X	int	dev;
X	char	*blk_ptr;
X	long	blk_len;
X{
X#define BSZDIG	6	/* number of digits for block size string */
X
X	char	 	len_str[BSZDIG+1];
X	char	xor_sum(), *strncpy();
X	blk_hdr	*bhd_ptr;
X	long	n_writ, nulls;
X
X	tracef((tr, "+ wr_blk(%d, %x, %ld)", dev, blk_ptr, blk_len));
X
X	if (blk_len > blk_siz)
X		blk_len = blk_siz;	/* write at most blk_siz chars */
X
X	bhd_ptr = (blk_hdr *) blk_ptr;
X
X	sprintf(len_str, "%6ld", blk_len);
X	strncpy(bhd_ptr->bh_dtalen, len_str, BSZDIG);
X
X	if (blk_len < blk_siz) {
X		/* move nulls to end of last block of last volume */
X		register char	*p;
X		nulls = blk_siz - blk_len;
X		p = blk_ptr + blk_len;
X		while (blk_len++ < blk_siz)
X			*p++ = '\0';
X		bhd_ptr->bh_blktyp = ENDBLK;
X		END_written = TRUE;
X	} else {
X		nulls = 0L;
X		bhd_ptr->bh_blktyp = DTABLK;
X	}
X	tracef((tr, "blk_typ = %c", bhd_ptr->bh_blktyp));
X		
X		/* Calc. checksum */
X	bhd_ptr->bh_chksum = 0;
X	bhd_ptr->bh_chksum = xor_sum(blk_ptr, blk_siz);
X
X	tracef((tr, "dev=%d,  blk_ptr=%x  blk_siz=%d",
X		dev, blk_ptr, (int)blk_siz));
X
X	n_writ = write(dev, blk_ptr, (int)blk_siz);
X	tracef((tr, "n_writ = %ld", n_writ));
X	if (n_writ < blk_siz ) {
X		/* Assume can't fit last block & need new volume */
X		if (n_writ == -1)
X			swarning(device);
X		n_writ = 0L;
X		END_written = FALSE;
X		errno = 0;
X	} else 
X		/* return size of data & headers written
X		less nulls used to fill the last block */
X		n_writ -= nulls;
X
X	trace("- wr_blk()");
X	return (n_writ);	
X}
X
Xvoid
Xwr_vol(dev, vhd_ptr)
X	int	*dev;
X	char	*vhd_ptr;
X{
X#ifndef	time_t
X#	include	<sys/types.h>
X#endif
X	/* mount and open next volume for writing */
X	void	pack_vhdr();
X	up_vhdr	*mount_vol();
X	char	*vol_file(), *vol_nam;
X	time_t	time();
X
X	static	int	vol_num = 0;
X	static	long	timestamp;
X
X	tracef((tr, "+ wr_vol(%x, %x)", dev, vhd_ptr));
X	if (++vol_num == 1)
X		timestamp = time((time_t *) 0);
X	else {	/* Note: the last volume is closed by exit() */
X		show_blks(-1L);	
X		trace("close");
X		close(*dev);
X	}
X	
X	pack_vhdr(vhd_ptr, vol_num, timestamp);
X	
X	vol_nam = vol_file(device, vol_num);
X
X	mount_vol(vol_nam, vol_num, timestamp);
X
X	if ((*dev = open(vol_nam, WRITE)) == -1)
X		sfatal(vol_nam);
X
X	trace("- wr_vol()");
X}
X
X/* ARGSUSED */
Xchar *
Xvol_file(devname, vol_num)
X	char	*devname;
X	int	vol_num;
X{
X	static	char	vol_name[15];
X
X#ifdef	VOLNUM
X	/* used for testing when writting to files pretending to be volumes */
X	sprintf(vol_name, "%s.%d", devname, vol_num);
X#else
X	sprintf(vol_name, "%s", devname);
X#endif
X	return((char *)vol_name);
X}
X
Xchar
Xxor_sum(ptr, siz)
X	char	*ptr;
X	long	siz;
X{
X	register char	sum;
X	register int	i;
X
X	sum = 0;
X	for (i = siz; i-- ; )
X		sum ^= *ptr++;
X	return sum;
X}
X
X/* show_blks() is called from rd.c and wr.c */
Xvoid
Xshow_blks(blk_num)
X	long	blk_num;
X{
X	bool	y_or_n();
X#define	DOTBLKS	10
X#define	DOTCH	'.'
X	/* Every DOTBLKS blocks print a dot */
X	tracef((tr, "show_blks(#%ld)", blk_num));
X	if (dsp_blks) {
X		if (blk_num == -1L) 
X			putc('\n', stderr);
X		else if (blk_num % DOTBLKS == 0)	
X#ifdef	DEBUG
X			fprintf(stderr, "%8ld", blk_num);
X#else
X			putc(DOTCH, stderr);
X#endif
X	}
X}
X
Xchar *
Xnew_buf(buf_siz)
X	long	buf_siz;
X{
X	char	*buf_ptr;
X	char	*malloc();
X
X	tracef((tr, "+ new_buf(%ld)", buf_siz));
X	if ((buf_ptr = malloc( (unsigned)buf_siz )) == (char *)NULL) {
X		fatal("Insufficient memory for block size");
X	}
X	trace("- new_buf()");
X	return buf_ptr;
X}
!The!End!
exit