[unix-pc.sources] Dsplit - splitting files across multiple floppies

cmv@cbnewsc.ATT.COM (C M Votava) (10/27/89)

Here is the source for dsplit. It's a command that allows you to split
files across multiple hard disks. For more information, see the README
file included in this shar.

-Craig Votava

att!ihlpf!cmv
att!ihlpm!cmv
att!cbnewsc!cmv

######################### CUT HERE ######################################

#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	Makefile
#	README
#	dsplit.c
echo x - extracting shar
sed 's/^X//' << 'SHAR_EOF' > Makefile
X#sccs "@(#)Makefile	1.1"
X
X# This makefile is an attempt at a general makefile template. The
X# TARGETS variable is a list of all targets that you want to build.
X# The LINKS variable is a list of all .o files needed by all targets.
X# The HEADS variable is a list of all .h files needed by all targets.
X# The commented out CC line is what you replace the LD line when you
X# want to create sdb-able output. You will also need to use the commented
X# out CFLAGS line for the sdb-able output too.
X#						6/21/89 -- cmv
X
Xinclude	$(MAKEINC)/Makepre.h
X
X# CFLAGS=-68010 -v -g
X
XTARGETS=dsplit
XLINKS=
XHEADS=
X
XDEPENDTS=$(HEADS) $(LINKS)
X
Xall:	$(TARGETS)
X
X$(TARGETS):	$(DEPENDTS) $$@.o
X	$(LD) $(LDFLAGS) $(SHAREDLIB) $(LINKS) -o $@ $@.o
X
X#	$(CC) -lg -o $@ $@.o
SHAR_EOF
echo x - extracting shar
sed 's/^X//' << 'SHAR_EOF' > README
X# @(#)README	1.2
X
XThis is the "dsplit" (device split) command, it's used to split output
Xover multiple removable media. Let's say, for example that you want to
Xback up your hard disk to a bunch of floppies. Using "dsplit" in the
Xfollowing way will save you a BUNCH of floppies:
X
X	$ find . -print | cpio -ocv | compress -c | dsplit -o
X
XTo restore your files from your set of backup floppies, try the command:
X
X	$ dsplit -i | uncompress -c | cpio -icduv
X
XThe syntax for the "dsplit" command is:
X
X	USAGE: dsplit [-i (default) | -o] [-v] [-d /dev/device]
X
XWhere:
X	-i		Input from device, pipe to stdout (default)
X	-o		Input from stdin, pipe to device
X	-v		Print lots of meaningless messages
X	-d /dev/*	Device to use (default is /dev/rfp021)
X
XREMEMBER: The good news about using "dsplit" with "cpio" and "compress" is
Xthat you can save a lot of floppies on a backup, but the bad news is that
Xif you "lose" even one bit, due to media degradation, the rest of the
Xbackup from that point on is garbage!
X
XThe "dsplit" command has a buffer size of 5120 bytes, similar to using
Xthe "-B" command on cpio. However, unlike cpio, it will write partial
Xbuffers (in 512 byte chunks) up to the very end of the media, before
Xgoing on to the next media.
X
XI've tried to make the command versatile enough for you brave souls out
Xthere to use it in new and different ways. Let me know when you find bugs,
Xso that I can keep a master copy up to date. Also, let me know of the
Xstrange new ways you find it useful.
X
XFuture versions of "dsplit" may have such things as:
X	[ My last minute comments are in brackets		-- cmv	]
X
X[	0.) A man page - Ok, so I'm a lazy documentator, so sue me! -- cmv ]
X
X	1.) Disk numbering - Similar to the disk numbering that "cpio" gives
X		you when you output to a floppy. The VHB for floppies on the
X		unixpc include a spot for cpio disk numbering.
X			[ This is machine dependent though	-- cmv	]
X
X	2.) Variable buffer size - So you'll be able to specify the size of
X		buffer you want. This makes writing the floppy go faster,
X		but the limiting factor really is using "compress" in the
X		pipeline. I'll leave it as an exercise for the reader to
X		come up with the optimal buffer size. Needless to say, the
X		buffer will be dynamically allocated too.
X
X	3.) I might take out that rotten "sleep" call that attempts to wait
X		for the pipe to back up before prompting the user for the
X		next media. The time needed seems to vary by a lot, and I
X		don't mind seeing file names printed after the prompt!
X			[ Let me know what you think!		-- cmv	]
X
X	4.) Other neat tricks that people suggest to me, that I like.
X		[ Pete Fales had ported this to the AT&T 6386 with	]
X		[ one small change, delete the "#sccs" line. Let me	]
X		[ know what other machines it gets ported to!	-- cmv	]
X
XBugs:
X
X	1.) Well, this is kind of a bug. When you eject the floppy while
X		writing is in progress, the driver thinks it's reached
X		the end of the media and asks you for the next one. The
X		only solution that I can come up with for now is
X		"Don't do this"!
X			[ The next version will solve this problem	]
X			[ by checking errno. Thanks Pete!	-- cmv	]
X
X	2.) When you hit <cr> a bunch of times, before you are prompted
X		to change your media, those <cr's> are buffered, and used
X		against you after the prompt. I'd like to have the code
X		clear away all of the buffered input just before I output
X		the prompt, but I can't think of an easy way to do this
X		(fflush doesn't seem to work right) any suggestions?
X			[ Never mind, Pete Fales answered this one	]
X			[ for me, it'll be in the next release. -- cmv	]
X
X[ Have fun, and send me mail! I like getting mail!	-- cmv ]
X
XCraig Votava
X[att!]ihlpz!cmv
SHAR_EOF
echo x - extracting shar
sed 's/^X//' << 'SHAR_EOF' > dsplit.c
X#sccs	"@(#)dsplit.c	1.2"
X
X/* This command can either read from or write to the floppy	*/
X/* disk, allowing for multi-volume files. It reads/writes to	*/
X/* stdout/stdin at the other side.				*/
X/*					6/25/89 -- cmv		*/
X
X#include <stdio.h>
X#include <fcntl.h>
X
X#define PUSAGE	"\tUSAGE: %s [-i (default) | -o] [-v] [-d /dev/device]\n",argv[0]
X
X#define FLOPPY	"/dev/rfp021"
X#define BUFF_SZ		5120				/* Buffer size */
X
X#define INCOMING	10
X#define OUTGOING	11
X
X#define STDIN		0
X#define STDOUT		1
X
X/* Some stuff to shut lint up */
Xchar *strcpy();
Xvoid perror(), exit();
Xunsigned sleep();
X
X/* Some global variables... */
Xchar Udevice[50];				/* device to use, it seemed */
X						/* like a good idea at the  */
X						/* time to make this global.*/
X
Xint Debug=0;					/* Global Debug Flag */
Xint Count=2;					/* media count */
Xextern int errno;
X
X/*
X * Main routine - It's job is to handle command line options, setup I/O
X *	file descriptors, and loop doing reads and writes until the job
X *	is completed.
X */
X
Xmain(argc,argv)
Xint     argc;
Xchar    *argv[];
X{
X	extern int optind;
X	extern char *optarg;
X
X	register int ret;			/* input return code */
X	register int i;
X
X	int errflg=0;				/* error flag */
X	int c;					/* Options processing */
X	int ofd;				/* output file descriptor */
X	int ifd;				/* input file descriptor */
X
X	short direction=INCOMING;		/* Direction flag */
X
X	unsigned char buff[BUFF_SZ];		/* Output buffer in bytes */
X	int bsize=BUFF_SZ;			/* buffer size */
X
X
X	/* Catch signals??? */
X
X	/* setup default device */
X	strcpy(Udevice,FLOPPY);
X
X	while((c = getopt(argc,argv,"?iovd:"))!=EOF)
X	{
X		switch(c)
X		{
X			case 'i':
X				direction=INCOMING;
X				break;
X			case 'o':
X				direction=OUTGOING;
X				break;
X			case 'd':
X				strcpy(Udevice,optarg);
X				if(Debug)
X					fprintf(stderr,
X						"Using device %s\n",Udevice);
X				break;
X			case 'v':
X				Debug++;
X				if(Debug)
X					fprintf(stderr,"Debug is on...\n");
X				break;
X			case '?':
X				errflg++;
X				break;
X			default:
X				fprintf(stderr,
X					"ASSERT1 - should never be here\n");
X				errflg++;
X				break;
X		}
X	}
X	if(errflg!=0)
X	{
X		fprintf(stderr,PUSAGE);
X		exit(1);
X	}
X
X	/* Check for input file name error conditions */
X	if(optind>argc)
X	{
X		fprintf(stderr,"Extra gunk on the command line...\n");
X		fprintf(stderr,PUSAGE);
X		exit(-1);
X	}
X
X	/* Set up the file descriptors for the given direction */
X	if(setup_io(&ifd,&ofd,direction) < 0)
X	{
X		if(Debug)
X			fprintf(stderr,"setup_io errno=%d\n",errno);
X		fprintf(stderr,"Trouble setting up I/O, goodbye...\n");
X		exit(-1);
X	}
X
X	do {
X		/* Fill up a buffer full of data */
X		if((ret=fill_buff(buff,bsize,&ifd,direction))<0)
X		{
X			fprintf(stderr,"read ret=%d errno=%d\n",ret,errno);
X			perror("Read");
X			ret=EOF;
X		}
X
X		if(ret>0)
X		{
X			if((ret=drain_buff(buff,ret,&ofd,direction))<0)
X			{
X				fprintf(stderr,"write ret=%d errno=%d\n",
X					ret,errno);
X				perror("Write");
X				ret=EOF;
X			}
X		}
X		/* A '0' return from fill_buff means EOF */
X		else
X			ret=EOF;
X
X		if(Debug)
X			fprintf(stderr,"ret=%d\n",ret);
X
X		for(i=0;i<BUFF_SZ;i++)
X			buff[i]=0;
X
X	} while(ret!=EOF);
X
X	/* close up shop */
X	close(ifd);
X	close(ofd);
X
X	if(Debug)
X		fprintf(stderr,"EOF\n");
X
X	return(0);
X}
X
X/*
X * setup_io - It's job is to setup the file descriptors, and open
X *	appropriate devices, depending on the direction of the I/O
X *	passed from main. Information messages are printed for any
X *	errors, and the error codes passed back to main.
X */
X
Xint
Xsetup_io(ifd,ofd,direction)
Xint *ifd;					/* input file descriptor */
Xint *ofd;					/* output file descriptor */
Xshort direction;				/* Direction flag */
X{
X	/* If we're reading from the Udevice... */
X	if(direction==INCOMING)
X	{
X		/* Duplicate stdout for output */
X		if((*ofd = dup(STDOUT)) < 0)
X		{
X			if(Debug)
X				fprintf(stderr,"dup errno=%d\n",errno);
X			perror("dup of stdout");
X			return(*ofd);
X		}
X		/* Open Udevice for input */
X		if((*ifd = open(Udevice,O_RDONLY)) < 0)
X		{
X			if(Debug)
X				fprintf(stderr,"open errno=%d\n",errno);
X			perror("input device open");
X			return(*ifd);
X		}
X	}
X	else					/* Writing to the Udevice */
X	{
X		/* Duplicate stdin for input */
X		if((*ifd = dup(STDIN)) < 0)
X		{
X			if(Debug)
X				fprintf(stderr,"dup errno=%d\n",errno);
X			perror("dup of stdin");
X			return(*ifd);
X		}
X		/* Open Udevice for output */
X		if((*ofd = open(Udevice,O_RDWR)) < 0)
X		{
X			if(Debug)
X				fprintf(stderr,"open errno=%d\n",errno);
X			perror("output device open");
X			return(*ofd);
X		}
X	}
X	return(0);
X}
X
X/*
X * fill_buff - It's job is to fill up an entire buffer with data.
X *	The direction indicates the general direction of the command so
X *	we can decide when to reload media. Upon errors, appropriate messages
X *	are printed out, and status is returned to the calling program.
X */
X
Xint
Xfill_buff(buff,size,fd,direction)
Xunsigned char *buff;				/* Pointer to buffer */
Xint size;					/* buffer size */
Xint *fd;					/* File descriptor */
Xshort direction;				/* direction flag */
X{
X
X	register int ret=0;			/* return value */
X	register int bytes=0;			/* total number of bytes read */
X
X	while(bytes<size)
X	{
X		/* size-bytes MUST (should) allways be a multiple of 512 here */
X		if((ret=read(*fd,(char *)&buff[bytes],size-bytes))<=0)
X		{
X			if(((ret==-1)||(ret==0))&&(direction==INCOMING))
X			{
X				if((ret = reload(fd))<0)
X				{
X					fprintf(stderr,"reload error\n");
X					return(ret);
X				}
X				if((ret=read(*fd,(char *)&buff[bytes],
X								size-bytes))<=0)
X				{
X					fprintf(stderr,"read - I give up...\n");
X					return(ret);
X				}
X			}
X			else if(ret==0)
X			{
X				/* This handles the case where we read in less
X				 * than a buffer full, then got EOF. We need to
X				 * return the amount of good buffer there is
X				 * left, next time around we'll send back EOF.
X				 */
X				if(bytes>0)
X				{
X					if(Debug)
X						fprintf(stderr,
X						"End Partial Buffer %d\n",
X						bytes);
X					return(bytes);
X				}
X				else
X				{
X					if(Debug)
X						fprintf(stderr,
X						"Returning EOF\n");
X					return(0);
X				}
X			}
X			/* We should never get here... */
X			else
X			{
X				fprintf(stderr,"ASSERT2 - ret=%d \n",ret);
X				perror("Read");
X				return(ret);
X			}
X	
X		}
X		bytes+=ret;
X		if(Debug)
X			fprintf(stderr,"read in %d bytes total=%d\n",
X					ret,bytes);
X	}
X
X	return(bytes);
X}
X
X/*
X * drain_buff - It's job is to empty out an entire buffer of data.
X *	The direction indicates the general direction of the command so
X *	we can decide when to reload media. Upon errors, appropriate messages
X *	are printed out, and status is returned to the calling program.
X */
X
Xint
Xdrain_buff(buff,size,fd,direction)
Xunsigned char *buff;				/* Pointer to buffer */
Xint size;					/* buffer size */
Xint *fd;					/* File descriptor */
Xshort direction;				/* direction flag */
X{
X
X	register int ret=0;			/* return value */
X	register int bytes=0;			/* total number of bytes read */
X
X	while(bytes<size)
X	{
X		/* size-bytes MUST (should) allways be a multiple of 512 here */
X		if((ret=write(*fd,(char *)&buff[bytes],size-bytes))<=0)
X		{
X			if(((ret==-1)||(ret==0))&&(direction==OUTGOING))
X			{
X				if((ret = reload(fd))<0)
X				{
X					fprintf(stderr,"reload error\n");
X					return(ret);
X				}
X				if((ret=write(*fd,(char *)&buff[bytes],
X								size-bytes))<=0)
X				{
X					fprintf(stderr,
X						"write - I give up...\n");
X					return(ret);
X				}
X			}
X			else if(ret==0)
X			{
X				if(Debug)
X					fprintf(stderr,"Returning EOF\n");
X				return(ret);
X			}
X			/* We should never get here... */
X			else
X			{
X				fprintf(stderr,"ASSERT3 - ret=%d \n",ret);
X				perror("Write");
X				return(ret);
X			}
X	
X		}
X		bytes+=ret;
X		if(Debug)
X			fprintf(stderr,"wrote out %d bytes total=%d\n",
X					ret,bytes);
X	}
X
X	return(bytes);
X}
X
X/*
X * reload - It's job is to have the user reload the media.
X *	This routine will not return until a successful open on the
X *	new media is returned.
X */
X
Xint
Xreload(fd)
Xint *fd;
X{
X	register int ret;				/* return value */
X
X	/* Close current device */
X	close(*fd);
X
X	/* Wait for the pipe to back up... */
X	sleep(2);
X
X	do {
X		/* Have user reload... */
X		if((ret=prompt_user())<0)
X		{
X			fprintf(stderr,"Can't prompt user, goodbye...\n");
X			break;
X		}
X
X	} while((*fd = ret = open(Udevice,O_RDWR)) < 0);
X
X	Count++;				/* Bump up media count */
X	return(ret);
X}
X
X/*
X * prompt_user - It's job is handle prompting the user for the response
X *	when fresh media is installed.
X *	Informative messages are printed upon error, and the error codes are
X *	returned to the calling proceedure.
X */
X
Xint
Xprompt_user()
X{
X	register int ret=0;
X	char kib[22];				/* keyboard input buffer */
X	FILE *keybd;				/* keyboard input */
X
X	/* Talk to user */
X	keybd = fopen("/dev/tty", "r");
X
X	fprintf(stderr,
X	"\nReload with fresh media number %d, hit return when ready...\n\n"
X	,Count);
X
X	fgets(kib, 20, keybd);
X
X	/* in case we wanna look at what the kid typed...
X	   kib[strlen(kib) - 1] = '\0';
X	*/
X
X	fclose(keybd);
X
X	return(ret);
X}
SHAR_EOF
exit