[comp.sources.amiga] v02i023: MRBackUp

mark@s.cc.purdue.edu.UUCP (09/17/87)

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# Xshar: Extended Shell Archiver.
# This is part  1 out of  2.
# This archive created: Wed Sep 16 20:51:21 1987
# By: Craig Norborg (Purdue University Computing Center)
#	Run the following text with /bin/sh to create:
#	About.c
#	Compress.c
#	Gadget.h
#	MRBackup.DOC
#	Makefile
#	Menu.c
#	Menu.h
#	MiscRequest.c
#	ReadME
#	Restore.c
cat << \SHAR_EOF > About.c
/* About.c: Tell the user a little about MRBackup. */

static char *abouts[] = {
"This is MRBackup, a hard disk backup utility written by Mark Rinfret.\n",
"This program allows you to perform complete or incremental backups.\n",
"Data compression / decompression are provided to economize on floppies.\n",
"If you find this program useful or if you have any suggestions,\n",
"Send the author mail at the following addresses:\n",
"    Mark R. Rinfret\n",
"    348 Indian Avenue\n",
"    Portsmouth, Rhode Island 02871\n\n",
"    or, UseNet: mark@unisec.USI.COM\n",
(char *) 0L};

About()
{
	int i;

	for (i = 0;abouts[i];++i) {
		TypeAndSpeak(abouts[i]);
	}
}
SHAR_EOF
cat << \SHAR_EOF > Compress.c
/* 
 * Compress.c - data compression/decompression routines.
 * This is an adaptation of the public domain Un*x compress v4.0.
 */
#include <stdio.h>
#include <ctype.h>

#define	min(a,b)	((a>b) ? b : a)

#ifdef DEBUG
#define DBG(x) x
#else
#define DBG(x)
#endif

extern int errno;

/*
 * Set USERMEM to the maximum amount of physical user memory available
 * in bytes.  USERMEM is used to determine the maximum BITS that can be used
 * for compression.
 *
 * SACREDMEM is the amount of physical memory saved for others; compress
 * will hog the rest.
 */
#ifndef SACREDMEM
#define SACREDMEM	0
#endif

#ifndef USERMEM
# define USERMEM 	450000			/* default user memory */
#endif

# define MAXFILES       100			/* arbitrary maximum - see note below */
# define BITS		12				/* > 12 crashes system (?) */
# undef USERMEM
long    filesize();
char   *scdir();

#ifdef USERMEM
# if USERMEM >= (433484+SACREDMEM)
#  define PBITS	16
# else
#  if USERMEM >= (229600+SACREDMEM)
#   define PBITS	15
#  else
#   if USERMEM >= (127536+SACREDMEM)
#    define PBITS	14
#   else
#    if USERMEM >= (73464+SACREDMEM)
#     define PBITS	13
#    else
#     define PBITS	12
#    endif
#   endif
#  endif
# endif
# undef USERMEM
#endif								/* USERMEM */

#ifdef PBITS						/* Preferred BITS for this memory size */
# ifndef BITS
#  define BITS PBITS
# endif BITS
#endif								/* PBITS */

#if BITS == 16
# define HSIZE	69001L				/* 95% occupancy */
#endif
#if BITS == 15
# define HSIZE	35023L				/* 94% occupancy */
#endif
#if BITS == 14
# define HSIZE	18013L				/* 91% occupancy */
#endif
#if BITS == 13
# define HSIZE	9001L				/* 91% occupancy */
#endif
#if BITS <= 12
# define HSIZE	5003L				/* 80% occupancy */
#endif

/*
 * a code_int must be able to hold 2**BITS values of type int, and also -1
 */
#if BITS > 15
typedef long int    code_int;
#else
typedef int     code_int;
#endif

#ifdef SIGNED_COMPARE_SLOW
typedef unsigned long int   count_int;
typedef unsigned short int      count_short;
#else
typedef long int    count_int;
#endif

#ifdef NO_UCHAR
typedef char    char_type;
#else
typedef unsigned char   char_type;
#endif								/* UCHAR */
char_type magic_header[]= {
	"\037\235" 
};									/* 1F 9D */

/* Defines for third byte of header */
#define BIT_MASK	0x1f
#define BLOCK_MASK	0x80
/* Masks 0x40 and 0x20 are free.  I think 0x20 should mean that there is
   a fourth header byte (for expansion).
*/
#define INIT_BITS 9					/* initial number of bits/code */

/*
 * compress.c - File compression ala IEEE Computer, June 1984.
 *
 * Authors:	
 *		Spencer W. Thomas	(decvax!harpo!utah-cs!utah-gr!thomas)
 *		Jim McKie			(decvax!mcvax!jim)
 *		Steve Davies		(decvax!vax135!petsd!peora!srd)
 *		Ken Turkowski		(decvax!decwrl!turtlevax!ken)
 *		James A. Woods		(decvax!ihnp4!ames!jaw)
 *		Joe Orost			(decvax!vax135!petsd!joe)
 *		Mark R. Rinfret		(mods) (mark@unisec.USI.COM)
 */


int n_bits;							/* number of bits/code */
int     maxbits = BITS;				/* user settable max # bits/code */
code_int maxcode;					/* maximum code, given n_bits */
code_int maxmaxcode = 1 << BITS;	/* NEVER generate this code */
#define MAXCODE(n_bits)	((1 << (n_bits)) - 1)

count_int htab [HSIZE];
unsigned short      codetab [HSIZE];
#define htabof(i)	htab[i]
#define codetabof(i)	codetab[i]

code_int hsize = HSIZE;				/* for dynamic table sizing */
count_int fsize;

static FILE *infile, *outfile;
static char iname[256], oname[256];

/*
 * To save much memory, we overlay the table used by compress() with those
 * used by decompress().  The tab_prefix table is the same size and type
 * as the codetab.  The tab_suffix table needs 2**BITS characters.  We
 * get this from the beginning of htab.  The output stack uses the rest
 * of htab, and contains characters.  There is plenty of room for any
 * possible stack (stack used to be 8000 characters).
 */

#define tab_prefixof(i)	codetabof(i)
#define tab_suffixof(i)	((char_type *)(htab))[i]
#define de_stack		((char_type *)&tab_suffixof(1<<BITS))

code_int free_ent = 0;				/* first unused entry */
int     exit_stat = 0;

code_int getcode();

int     nomagic = 0;				/* Use a 3-byte magic number header,
									   unless old file */
int     zcat_flg = 0;				/* Write output on stdout, suppress
									   messages */
int     quiet = 1;					/* don't tell me about compression */

/*
 * block compression parameters -- after all codes are used up,
 * and compression rate changes, start over.
 */
int     block_compress = BLOCK_MASK;
int     clear_flg = 0;
long int    ratio = 0;
#define CHECK_GAP 10000				/* ratio check interval */
count_int checkpoint = CHECK_GAP;
/*
 * the next two codes should not be changed lightly, as they must not
 * lie within the contiguous general code space.
 */
#define FIRST	257					/* first free entry */
#define	CLEAR	256					/* table clear output code */

int     force = 0;
char    ofname [100];
#ifdef DEBUG
int     verbose = 0;
#endif								/* DEBUG */
int     (*bgnd_flag)();

int     do_decomp = 0;

/*****************************************************************
 * TAG( main )
 *
 * Algorithm from "A Technique for High Performance Data Compression",
 * Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19.
 *
 * Outputs:
 *	file.Z:	    Compressed form of file with same mode, owner, and utimes
 *
 * Assumptions:
 *	When filenames are given, replaces with the compressed version
 *	(.Z suffix) only if the file decreases in size.
 * Algorithm:
 * 	Modified Lempel-Ziv method (LZW).  Basically finds common
 * substrings and replaces them with a variable size code.  This is
 * deterministic, and can be done on the fly.  Thus, the decompression
 * procedure needs no input table, but tracks the way the table was built.
 */

comp_init()
{
	if(maxbits < INIT_BITS)
		maxbits = INIT_BITS;
	if (maxbits > BITS)
		maxbits = BITS;
	maxmaxcode = 1 << maxbits;
}

static int offset;
static long int in_count = 1;		/* length of input */
static long int bytes_out;			/* length of compressed output */
static long int out_count = 0;		/* # of codes output (for debugging) */

/*
 * compress "from" to "to"
 *
 * Algorithm:  use open addressing double hashing (no chaining) on the 
 * prefix code / next character combination.  We do a variant of Knuth's
 * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
 * secondary probe.  Here, the modular division first probe is gives way
 * to a faster exclusive-or manipulation.  Also do block compression with
 * an adaptive reset, whereby the code table is cleared when the compression
 * ratio decreases, but after the table fills.  The variable-length output
 * codes are re-sized at this point, and a special CLEAR code is generated
 * for the decompressor.  Late addition:  construct the table according to
 * file size for noticeable speed improvement on small files.  Please direct
 * questions about this implementation to ames!jaw.
 */

int 
compress(from, to)
	char *from, *to;
{
	register long   fcode;
	register code_int i = 0;
	register int    c;
	register code_int ent;
	register int    disp;
	register code_int hsize_reg;
	register int    hshift;
	int fail = 0;
	char *s;
	char *err_msg[256];

	comp_init();

	/* 
	 * tune hash table size for small files -- ad hoc,
	 * but the sizes match earlier #defines, which
	 * serve as upper bounds on the number of output codes. 
	 */
		hsize = HSIZE;
		if (fsize < (1L << 12))
			hsize = min (5003L,HSIZE );
		else 
			if (fsize < (1L << 13))
				hsize = min (9001L,HSIZE );
			else 
				if (fsize < (1L << 14))
					hsize = min (18013L,HSIZE );
				else 
					if (fsize < (1L << 15))
						hsize = min (35023L,HSIZE );
					else 
						if (fsize < 47000L )
							hsize = min (50021L,HSIZE );

	if (!(infile = fopen(from,"r"))) {
		perror(from);
		return 1;
	}

	if (!(outfile = fopen(to,"w"))) {
		perror(to);
		fclose(infile);
		return 1;
	}

	if (nomagic == 0){
		putc(magic_header[0],outfile);
		putc(magic_header[1],outfile);
		putc((char)(maxbits | block_compress),outfile);
		if(ferror(outfile)) {
			writeerr();
			++fail;
cleanup:
			fclose(infile);
			fclose(outfile);
			return fail;
		}
	}

	offset = 0;
	bytes_out = 3;					/* includes 3-byte header mojo */
	out_count = 0;
	clear_flg = 0;
	ratio = 0L;
	in_count = 1;
	checkpoint = CHECK_GAP;
	maxcode = MAXCODE(n_bits = INIT_BITS);
	free_ent = ((block_compress)?FIRST :256 );

	ent = getc (infile);

	hshift = 0;
	for (fcode = (long)hsize;fcode < 65536L;fcode *= 2L )
		hshift++;
	hshift = 8 - hshift;			/* set hash code range bound */

	hsize_reg = hsize;
	cl_hash((count_int)hsize_reg);	/* clear hash table */

	while ((c = getc(infile))!= EOF ){
		in_count++;
		fcode = (long)(((long)c << maxbits)+ ent);
		i = ((c << hshift)^ ent);	/* xor hashing */

		if (htabof (i)== fcode ){
			ent = codetabof (i);
			continue;
		}
		else 
			if ((long)htabof (i)< 0 )/* empty slot */
				goto nomatch;
		disp = hsize_reg - i;		/* secondary hash (after G. Knott) */
		if (i == 0 )
			disp = 1;
probe: 
		if ((i -= disp)< 0 )
			i += hsize_reg;

		if (htabof (i)== fcode ){
			ent = codetabof (i);
			continue;
		}
		if ((long)htabof (i)> 0 )
			goto probe;
nomatch: 
		output ((code_int)ent );
		out_count++;
		ent = c;
		if (free_ent < maxmaxcode ){
			codetabof (i)= free_ent++;/* code -> hashtable */
			htabof (i)= fcode;
		}
		else 
			if ((count_int)in_count >= checkpoint && block_compress )
				cl_block ();
	}
 /* 
  * Put out the final code.
  */
	output((code_int)ent );
	out_count++;
	output((code_int)-1 );

 /* 
  * Print out stats on stderr
  */
	if(zcat_flg == 0 && !quiet){
#ifdef DEBUG
		fprintf(stderr,
			"%ld chars in, %ld codes (%ld bytes) out, compression factor: ",
			in_count,out_count,bytes_out );
		prratio(stderr,in_count,bytes_out );
		fprintf(stderr,"\n");
		fprintf(stderr,"\tCompression as in compact: " );
		prratio(stderr,in_count-bytes_out,in_count );
		fprintf(stderr,"\n");
		fprintf(stderr,"\tLargest code (of last block) was %d (%d bits)\n",
				free_ent - 1,n_bits );
#else								/* !DEBUG */
		fprintf(stderr,"Compression: " );
		prratio(stderr,in_count-bytes_out,in_count );
#endif								/* DEBUG */
	}
	if(bytes_out > in_count)		/* exit(2) if no savings */
		exit_stat = 2;
	goto cleanup;
}

/*****************************************************************
 * TAG( output )
 *
 * Output the given code.
 * Inputs:
 * 	code:	A n_bits-bit integer.  If == -1, then EOF.  This assumes
 *		that n_bits =< (long)wordsize - 1.
 * Outputs:
 * 	Outputs code to the file.
 * Assumptions:
 *	Chars are 8 bits long.
 * Algorithm:
 * 	Maintain a BITS character long buffer (so that 8 codes will
 * fit in it exactly).  Use the VAX insv instruction to insert each
 * code in turn.  When the buffer fills up empty it and start over.
 */

static char     buf[BITS];

char_type lmask[9]= {
	0xff,0xfe,0xfc,0xf8,0xf0,0xe0,0xc0,0x80,0x00
};
char_type rmask[9]= {
	0x00,0x01,0x03,0x07,0x0f,0x1f,0x3f,0x7f,0xff
};

output(code )
code_int code;
{
#ifdef DEBUG
	static int      col = 0;
#endif								/* DEBUG */

	register int    r_off = offset,bits= n_bits;
	register char * bp = buf;

#ifdef DEBUG
	if (verbose )
		fprintf(stderr,"%5d%c",code,
				(col+=6)>= 74 ?(col = 0,'\n'):' ' );
#endif								/* DEBUG */
	if (code >= 0 ){
/* 
 * byte/bit numbering on the VAX is simulated by the following code
 */
	/* 
	 * Get to the first byte.
	 */
		bp += (r_off >> 3);
		r_off &= 7;
	/* 
	 * Since code is always >= 8 bits, only need to mask the first
	 * hunk on the left.
	 */
		*bp = (*bp & rmask[r_off])| (code << r_off)& lmask[r_off];
		bp++;
		bits -= (8 - r_off);
		code >>= 8 - r_off;
	/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
		if (bits >= 8 ){
			*bp++= code;
			code >>= 8;
			bits -= 8;
		}
	/* Last bits. */
		if(bits)
			*bp = code;
		offset += n_bits;
		if (offset == (n_bits << 3)){
			bp = buf;
			bits = n_bits;
			bytes_out += bits;
			do
				putc(*bp++, outfile);
			while(--bits);
			offset = 0;
		}

	/* 
	 * If the next entry is going to be too big for the code size,
	 * then increase it, if possible.
	 */
		if (free_ent > maxcode || (clear_flg > 0)){
		/* 
		 * Write the whole buffer, because the input side won't
		 * discover the size increase until after it has read it.
		 */
			if (offset > 0 ){
				if(fwrite(buf,1,n_bits,outfile)!= n_bits) {
					writeerr();
					return 1;
				}
				bytes_out += n_bits;
			}
			offset = 0;

			if (clear_flg ){
				maxcode = MAXCODE (n_bits = INIT_BITS);
				clear_flg = 0;
			}
			else {
				n_bits++;
				if (n_bits == maxbits )
					maxcode = maxmaxcode;
				else
					maxcode = MAXCODE(n_bits);
			}
#ifdef DEBUG
			if (debug ){
				fprintf(stderr,"\nChange to %d bits\n",n_bits );
				col = 0;
			}
#endif								/* DEBUG */
		}
	}
	else {
	/* 
	 * At EOF, write the rest of the buffer.
	 */
		if (offset > 0 )
			fwrite(buf,1,(offset + 7)/ 8,outfile);
		bytes_out += (offset + 7)/ 8;
		offset = 0;
		fflush(outfile);
#ifdef DEBUG
		if (verbose )
			fprintf(stderr,"\n" );
#endif								/* DEBUG */
		if(ferror(outfile)) {
			writeerr();
			return 1;
		}
	}
	return 0;
}

/*
 * Decompress infile to outfile.  This routine adapts to the codes in the
 * file building the "string" table on-the-fly; requiring no table to
 * be stored in the compressed file.  The tables used herein are shared
 * with those of the compress() routine.  See the definitions above.
 */

int
decompress(from, to)
	char *from, *to;
{
	register    char_type *stackp;
	register int    finchar;
	register    code_int code,oldcode,incode;
	int fail = 0;

	if (!(infile = fopen(from, "r"))) {
		printf("decompress: %s failed to open for input!\n",from);
		return errno;
	}
	if (!(outfile = fopen(to, "w"))) {
		printf("decompress: %s failed to open for output!\n",to);
		fclose(infile);
		return errno;
	}

	if (nomagic == 0){
		if ((getc(infile)!= (magic_header[0]& 0xFF))
				|| (getc(infile)!= (magic_header[1]& 0xFF))){
			fprintf(stderr,"decompress: %s not in compressed format\n",
					from);
			++fail;
cleanup:
			fclose(infile);
			fclose(outfile);
			return fail;
		}
		maxbits = getc(infile);/* set -b from file */
		block_compress = maxbits & BLOCK_MASK;
		maxbits &= BIT_MASK;
		maxmaxcode = 1 << maxbits;
		if(maxbits > BITS){
			fprintf(stderr,
			"%s: compressed with %d bits, can only handle %d bits\n",
					from,maxbits,BITS);
			++fail;
			goto cleanup;
		}
	}
 /* 
  * As above, initialize the first 256 entries in the table.
  */
	maxcode = MAXCODE(n_bits = INIT_BITS);
	for (code = 255;code >= 0;code--){
		tab_prefixof(code)= 0;
		tab_suffixof(code)= (char_type)code;
	}
	free_ent = ((block_compress)?FIRST :256 );

	finchar = oldcode = getcode();
	if(oldcode == -1)				/* EOF already? */
		/*return ;*/					/* Get out of here */
		goto cleanup;

	putc((char)finchar,outfile );	/* first code must be 8 bits = char */
	if(ferror(outfile))	{			/* Crash if can't write */
		writeerr ();
		++fail;
		goto cleanup;
	}
	stackp = de_stack;

	while ((code = getcode())> -1 ){

		if ((code == CLEAR)&& block_compress ){
			for (code = 255;code >= 0;code--)
				tab_prefixof(code)= 0;
			clear_flg = 1;
			free_ent = FIRST - 1;
			if ((code = getcode ())== -1 )/* O, untimely death! */
				break ;
		}
		incode = code;
	/* 
	 * Special case for KwKwK string.
	 */
		if (code >= free_ent ){
			*stackp++= finchar;
			code = oldcode;
		}

	/* 
	 * Generate output characters in reverse order
	 */
		while (code >= 256 ){
			*stackp++= tab_suffixof(code);
			code = tab_prefixof(code);
		}
		*stackp++= finchar = tab_suffixof(code);

	/* 
	 * And put them out in forward order
	 */
		do
			putc (*--stackp , outfile);
		while (stackp > de_stack );

	/* 
	 * Generate the new entry.
	 */
		if ((code=free_ent)< maxmaxcode ){
			tab_prefixof(code)= (unsigned short)oldcode;
			tab_suffixof(code)= finchar;
			free_ent = code+1;
		}
	/* 
	 * Remember previous code.
	 */
		oldcode = incode;
	}
	fflush(outfile);
	if(ferror(outfile)) {
		writeerr();
		++fail;
	}
	goto cleanup;
}

/*****************************************************************
 * TAG( getcode )
 *
 * Read one code from the standard input.  If EOF, return -1.
 * Inputs:
 * 	infile
 * Outputs:
 * 	code or -1 is returned.
 */

code_int
getcode(){
 /* 
  * On the VAX, it is important to have the register declarations
  * in exactly the order given, or the asm will break.
  */
	register    code_int code;
	static int      offset = 0,size = 0;
	static      char_type buf[BITS];
	register int    r_off,bits;
	register    char_type *bp = buf;

	if (clear_flg > 0 || offset >= size || free_ent > maxcode ){
	/* 
	 * If the next entry will be too big for the current code
	 * size, then we must increase the size.  This implies reading
	 * a new buffer full, too.
	 */
		if (free_ent > maxcode ){
			n_bits++;
			if (n_bits == maxbits )
				maxcode = maxmaxcode;/* won't get any bigger now */
			else
				maxcode = MAXCODE(n_bits);
		}
		if (clear_flg > 0){
			maxcode = MAXCODE (n_bits = INIT_BITS);
			clear_flg = 0;
		}
		size = fread(buf,1,n_bits,infile);
		if (size <= 0 )
			return -1;				/* end of file */
		offset = 0;
	/* Round size down to integral number of codes */
		size = (size << 3)- (n_bits - 1);
	}
	r_off = offset;
	bits = n_bits;
 /* 
  * Get to the first byte.
  */
	bp += (r_off >> 3);
	r_off &= 7;
 /* Get first part (low order bits) */
#ifdef NO_UCHAR
	code = ((*bp++>> r_off)& rmask[8 - r_off])& 0xff;
#else
	code = (*bp++>> r_off);
#endif								/* NO_UCHAR */
	bits -= (8 - r_off);
	r_off = 8 - r_off;				/* now, offset into code word */
 /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
	if (bits >= 8 ){
#ifdef NO_UCHAR
		code |= (*bp++& 0xff)<< r_off;
#else
		code |= *bp++<< r_off;
#endif								/* NO_UCHAR */
		r_off += 8;
		bits -= 8;
	}
 /* high order bits. */
	code |= (*bp & rmask[bits])<< r_off;
	offset += n_bits;

	return code;
}

char   *
        rindex(s,c)					/* For those who don't have it in libc.a 
									*/
register char  *s,c;
{
	char   *p;
	for (p = NULL;*s;s++)
		if (*s == c)
			p = s;
	return(p);
}

writeerr(){
	perror (ofname );
	unlink (ofname );
}

/*
 * This routine returns 1 if we are running in the foreground and stderr
 * is a tty.
 */
foreground(){
 /* I don't know how to test for background, yet. */
	return (isatty(2));
}

onintr (){
	unlink (ofname );
	exit (1 );
}

oops (){							/* wild pointer -- assume bad input */
	if (do_decomp == 1 )
		fprintf (stderr,"uncompress: corrupt input\n" );
	unlink (ofname );
	exit (1 );
}

cl_block (){						/* table clear for block compress */
	register long int   rat;

	checkpoint = in_count + CHECK_GAP;
#ifdef DEBUG
	if (debug ){
		fprintf (stderr,"count: %ld, ratio: ",in_count );
		prratio (stderr,in_count,bytes_out );
		fprintf (stderr,"\n");
	}
#endif								/* DEBUG */

	if(in_count > 0x007fffff){		/* shift will overflow */
		rat = bytes_out >> 8;
		if(rat == 0){				/* Don't divide by zero */
			rat = 0x7fffffff;
		}
		else {
			rat = in_count / rat;
		}
	}
	else {
		rat = (in_count << 8)/ bytes_out;/* 8 fractional bits */
	}
	if (rat > ratio ){
		ratio = rat;
	}
	else {
		ratio = 0;
#ifdef DEBUG
		if(verbose)
			dump_tab();				/* dump string table */
#endif
		cl_hash ((count_int)hsize );
		free_ent = FIRST;
		clear_flg = 1;
		output ((code_int)CLEAR );
#ifdef DEBUG
		if(debug)
			fprintf (stderr,"clear\n" );
#endif								/* DEBUG */
	}
}

cl_hash(hsize)						/* reset code table */
register    count_int hsize;
{
	register    count_int *htab_p = htab+hsize;
	register long   i;
	register long   m1 = -1;

	i = hsize - 16;
	do {							/* might use Sys V memset(3) here */
		*(htab_p-16)= m1;
		*(htab_p-15)= m1;
		*(htab_p-14)= m1;
		*(htab_p-13)= m1;
		*(htab_p-12)= m1;
		*(htab_p-11)= m1;
		*(htab_p-10)= m1;
		*(htab_p-9)= m1;
		*(htab_p-8)= m1;
		*(htab_p-7)= m1;
		*(htab_p-6)= m1;
		*(htab_p-5)= m1;
		*(htab_p-4)= m1;
		*(htab_p-3)= m1;
		*(htab_p-2)= m1;
		*(htab_p-1)= m1;
		htab_p -= 16;
	}while ((i -= 16)>= 0);
	for (i += 16;i > 0;i--)
		*--htab_p = m1;
}

prratio(stream,num,den)
FILE *stream;
long int    num,den;
{
	register int    q;				/* Doesn't need to be long */

	if(num > 214748L){				/* 2147483647/10000 */
		q = num / (den / 10000L);
	}
	else {
		q = (10000L*num)/ den;		/* Long calculations, though */
	}
	if (q < 0){
		putc('-',stream);
		q = -q;
	}
	fprintf(stream,"%d.%02d%%",q / 100,q % 100);
}
SHAR_EOF
cat << \SHAR_EOF > Gadget.h

/*  Definitions for Gadget ID numbers               */ 

#define  HOMEPATHGAD 0
#define  BACKPATHGAD 1
#define  LISTPATHGAD 2
#define  STOPGAD	 3
SHAR_EOF
cat << \SHAR_EOF > MRBackup.DOC






          
          
          
          
          
          
          
          
          
          
                                MRBackup Version 1.0
          
                             A Hard Disk Backup Utility
                                       for the
                                   Commodore Amiga
          
          
                                   6 August, 1987
          
                               Author: Mark R. Rinfret
          
          












































          
          
          Introduction
          
               This document describes the first release of a program which
          will backup an Amiga hard disk  drive  to  floppy disk media.  It
          also represents the  author's  first  serious  (are you kidding?)
          attempt to write an Amiga program using Intuition.
          
               MRBackup is  reasonably  flexible,  allowing  you  to backup
          individual directories, directory trees or a whole disk.  You can
          backup from  one  directory  hierarchy  and  restore  to another.
          Incremental backups can be  performed  based on file modification
          dates.  Just for fun, MRBackup also talks.  Though this is mostly
          frivolous, the speech  capability  provides a method for alerting
          you that a new output disk is required for formatting.
          
               MRBackup is not fast.  When  choosing  a  method for packing
          the backup data, a  fast-scan  approach  with  specialized backup
          directory  structure  was   considered.   However,  there  is  an
          inherent danger in  committing  multiple  files  to a specialized
          file system which can only be  accessed  through a solitary piece
          of software.  I decided  to  maintain  the  AmigaDOS  file system
          structure which has a  great  deal  of  integrity  and allows the
          backup media to be accessed by standard software. 
          
               The user should take a serious and organized approach to his 
          backup methods, however.  I highly recommend that backup listings
          be kept in a safe  place  (I  use  a  3-ring  binder)  and backup
          floppies be stored safe  from  magnetic  damage  or other hazards
          (like spilled coffee - argh!).  A truly committed individual will
          backup his entire disk once  a  month  (date  = 00/00/00), once a
          week (automatic default) and "areas of interest" once a day.  One
          shortcoming(?) of the  current  version  of  MRBackup is that all
          directory  nodes  from  the  specified  home  path  and  down are
          preserved on the backup media,  even  if some of those nodes have
          no files eligible for backup by way  of the backup date.  Perhaps
          a later version of this program will address this issue.
          
               MRBackup attempts to  economize  on  output  media  usage by
          using data compression/decompression (at the cost of time).  This
          is an option which can be  enabled/disabled  via  menu selection.
          The compression routine used was lifted  from the Un*x "compress"
          program. 
          
          
          Operation
          
               To use MRBackup, click on the program  icon or type MRBackup
          at the CLI prompt.  A new window will  open in which you will see
          a STOP sign and a couple  of  embedded  windows.   The  "Pathname
          Specifications" window is where  you  tell MRBackup where data is
          coming from and going  to.   It  is  important  to  note that the
          meanings of "Home Path" and  "Backup  Path" remain the same for a












          backup or a restore operation.   That  is, the "Home Path" always
          refers to the hard disk pathname and  "Backup Path" always refers
          to the floppy disk pathname.   The  "Listing  Path" refers to the
          destination of a detailed  listing  of the contents of the backup
          floppies and may specify the  printer  (default) or a file on the
          hard   disk.    The   listing   is   an   option   which  may  be
          enabled/disabled via menu selection.
          
          
          Backing Up a Disk
          
               To back up your hard disk, or a section  of it, first get an
          indication of the size of the area  with the Info command, ls (if
          you have it), List, etc.   If  you  multiply  the total number of
          bytes (blocks * 512) by 0.65,  then  divide by 800000, you should
          get a very rough estimate (probably high) of the number of floppy
          disks required to back up that  area.   The floppies selected for
          backup need not be preformatted, as  MRBackup will format them as
          needed.  You should  be  sure,  however,  that  no  valuable data
          exists on them since  it  will  be  destroyed  by  the formatting
          process. 
          
               Once your floppies  are  ready  and  stacked  neatly  within
          reach, the fun begins.  First, modify the pathname specifications
          according to  your  requirements.   The  "Home  Path" may include
          device names DH0..DH9 (depending upon your configuration) and may
          optionally specify a directory  component,  as well.  If only the
          device name is specified, all  files  on the drive will be backed
          up.  If a directory is  specified,  the backup will be limited to
          all files and subdirectories at or  below that level.  The backup
          path should be  the  DEVICE  name  of  one  of your floppy drives
          (DF0..DF3 are the  allowed  values  here).   Finally, the listing
          path may be set to the printer device  (PRT:) or to the name of a
          file on a hard drive or any available floppy drive not being used
          by the backup.  The listing  path  will be ignored if you set the
          No Listing option in the Flags menu.
          
               Make sure that you have set the desired options in the Flags
          menu, then select Backup  from  the  Project  menu.   You will be
          prompted with a  date  requester.   The  default  date value that
          appears is one week earlier  than  the  current date.  If that is
          satisfactory, simply depress  the  RETURN key and the backup will
          commence.  If you desire to change  the date, edit the date value
          as necessary, using  the  standard  Amiga  conventions.  Remember
          that to clear the gadget  you  may  press  right-Amiga-x.  A date
          value of 00/00/00 is allowed should  you want to backup all files
          in the home path, regardless of their  creation date.  Otherwise,
          the required date and time  format  is MM/DD/YY HH:MM:SS (24 hour
          clock), the time specification being optional.
          
               Once  the  date  has  been  entered,   the  rest  is  fairly
          automatic.  You will be  prompted  immediately for a blank floppy
          disk.  Insert it into the floppy drive  that you specified in the
          backup path and the disk requester will be satisfied.  Should you












          want to abort, simply hit  the  CANCEL  gadget  in the requester.
          Also, you may abort the backup process at any time by hitting the
          STOP gadget which appears  in  the  top  left area of the screen.
          This gadget is only checked  prior  to the start of the next file
          copy operation, so be patient if it does not respond immediately.
          
               As each floppy disk is filled, you should promptly label it.
          MRBackup automatically generates volume names of the form:
          
               Backup MM-DD-YY.<sequence number>
          
          Also to be noted is the fact  that  the files on the backup media
          retain the creation/modification date  (I wish there were two) of
          their counterparts on the home device.
          
               You will find  that  the  Amiga's  multitasking  environment
          shines when using this program.  For those long-haul backups, get
          everything started, then shove all the windows to the back and go
          start something  useful.   MRBackup  will  carry  on without your
          watchful eye.  When it needs a disk,  the disk requester will pop
          out in front of everything and  MRBackup  will  ask (out loud, if
          you allow it) for another disk.  Having something else to do will 
          make the time pass faster.
          
          
          Restoring the Backups
          
               No, this isn't always the bad part.   Backup and restore can
          also be useful when your  hard  disk  is  crowded and you have to
          "juggle" data around.  The restoration  process is quite similar,
          mechanically, to the  backup  process  -  it's just reversed.  In
          addition, the meanings of the pathname specifications are altered
          somewhat.  The "Home  path"  describes  the  destination  for the
          restored files and, as with the  backup  process, may specify the
          hard drive only or a directory specification.   The "Backup path"
          describes the file  or  files  to  be  restored  and  thus may be
          defined down to the file level (1 file).
          
               Note that on a restore operation,  the file creation date of
          the backup file is compared to  its  matching file (if it exists)
          on the home device.  If the file on the home device is newer, the
          file will not be replaced.  If  this  is desired, the file on the
          home device must  be  deleted  first.   A  later  version of this
          program will probably offer  a  "force"  or  "force  with prompt"
          option.  
          
          
          Warranties
          
               There are no warranties, either  expressed  or implied, with
          regard to the use of this  program.   You  didn't pay for it (did
          you?), so how you gonna' get  you  money  back?  Seriously, folk,
          I've tested this beastie fairly thoroughly (I intend to USE it!),
          but you know  how  things  go...there  may  be  a  bugaboo or two












          lurking in there.  Please exercise  it a little before committing
          your life (your precious data) to its care.
          
          
          Permissions
          
               This program is contributed to the public domain, as a token 
          of gratitude for the  many  wonderful  programs  I  have received
          through the same channels.  Feel free  to enhance, destroy, sell,
          distribute  or  otherwise  benefit  from  the  legal  use of this
          program.  I would love to hear  from  those  of you who find this
          either useful or useless (with specific  criticisms in the latter
          case).  If you make any  enhancements  (there's room for PLENTY),
          please share them with me and  the  rest  of the world.  I can be
          reached as:
          
               mark@unisec.USI.COM (at least a couple more months)
          
               or
          
               Mark R. Rinfret
               348 Indian Avenue
               Portsmouth, RI 02871
               401-846-7639 (home)
               401-849-4174 (work)
          
          
          
































SHAR_EOF
cat << \SHAR_EOF > Makefile
CFLAGS = -b
OBJ = About.o Backup.o Compress.o Menu.o MiscRequest.o Restore.o 

# The requester/gadgets module is treated separately.  It must be loaded
# into chip memory since it contains image data.
CHIPOBJ = pathrequest.o

# Designate the directory which holds the source and object for the
# miscellaneous support routines.  Actually, it would be nice to
# collect these into a library...real soon now...

MISC = sys:src/misc

MISCOBJ = $(MISC)/CopyFile.o $(MISC)/DateRequest.o $(MISC)/Dates.o \
	$(MISC)/DiskMisc.o $(MISC)/FileMisc.o $(MISC)/FormatDisk.o \
	$(MISC)/sendpkt.o $(MISC)/Speech.o

$(MISC)/CopyFile.o: $(MISC)/CopyFile.c
	cc $(CFLAGS) -o $(MISC)/CopyFile.o $(MISC)/CopyFile.c

$(MISC)/DateRequest.o: $(MISC)/DateRequest.c
	cc $(CFLAGS) -o $(MISC)/DateRequest.o $(MISC)/DateRequest.c

$(MISC)/Dates.o: $(MISC)/Dates.c
	cc $(CFLAGS) -o $(MISC)/Dates.o $(MISC)/Dates.c

$(MISC)/DiskMisc.o: $(MISC)/DiskMisc.c
	cc $(CFLAGS) -o $(MISC)/DiskMisc.o $(MISC)/DiskMisc.c

$(MISC)/FileMisc.o: $(MISC)/FileMisc.c
	cc $(CFLAGS) -o $(MISC)/FileMisc.o $(MISC)/FileMisc.c

$(MISC)/FormatDisk.o: $(MISC)/FormatDisk.c
	cc $(CFLAGS) -o $(MISC)/FormatDisk.o $(MISC)/FormatDisk.c

$(MISC)/sendpkt.o: $(MISC)/sendpkt.c
	cc $(CFLAGS) -o $(MISC)/sendpkt.o $(MISC)/sendpkt.c

$(MISC)/Speech.o: $(MISC)/Speech.c
	cc $(CFLAGS) -o $(MISC)/Speech.o $(MISC)/Speech.c

BackUp.o: menu.h gadget.h

Menu.o: Menu.h

PathRequest.o: Gadget.h

# Note: $(CHIPOBJ) must follow the +C option to insure that its image
#		data gets loaded into chip ram.

MRBackup: $(OBJ) $(MISCOBJ) $(CHIPOBJ)
	ln -w -o MRBackUp $(OBJ) $(MISCOBJ) +C $(CHIPOBJ) -lc

SHAR_EOF
cat << \SHAR_EOF > Menu.c
/*****************************************************
 *                  Menu Definitions                 *
 *                                                   *
 *             Created with Menu Creator             *
 *                        by                         *
 *                   David Pehrson                   *
 *                                                   *
 *         Copyright (C) 1986  David Pehrson         *
 *                 Mantis Development                *
 *                                                   *
 *****************************************************/

#include <exec/types.h>
#include <intuition/intuition.h>

char stTopaz[] = "topaz.font";

struct TextAttr taPlain =
{
	(STRPTR) stTopaz, 8, FS_NORMAL, FPF_ROMFONT
};

struct TextAttr taBIU =
{
	(STRPTR) stTopaz, 8, FSF_BOLD | FSF_ITALIC | FSF_UNDERLINED, FPF_ROMFONT
};

struct TextAttr taBU =
{
	(STRPTR) stTopaz, 8, FSF_BOLD | FSF_UNDERLINED, FPF_ROMFONT
};

struct TextAttr taBI =
{
	(STRPTR) stTopaz, 8, FSF_BOLD | FSF_ITALIC, FPF_ROMFONT
};

struct TextAttr taB =
{
	(STRPTR) stTopaz, 8, FSF_BOLD, FPF_ROMFONT
};

struct TextAttr taIU =
{
	(STRPTR) stTopaz, 8, FSF_ITALIC | FSF_UNDERLINED, FPF_ROMFONT
};

struct TextAttr taI =
{
	(STRPTR) stTopaz, 8, FSF_ITALIC, FPF_ROMFONT
};

struct TextAttr taU =
{
	(STRPTR) stTopaz, 8, FSF_UNDERLINED, FPF_ROMFONT
};

struct IntuiText ItemText[] =
{
	{ 0, 1, JAM1, 0, 1, &taPlain, "Backup", NULL },
	{ 0, 1, JAM1, 0, 1, &taPlain, "Restore", NULL },
	{ 0, 1, JAM1, 0, 1, &taPlain, "About", NULL },
	{ 0, 1, JAM1, 0, 1, &taPlain, "Quit", NULL },
	{ 0, 1, JAM1, 0, 1, &taPlain, "   Compression", NULL },
	{ 0, 1, JAM1, 0, 1, &taPlain, "   No Compression", NULL },
	{ 0, 1, JAM1, 0, 1, &taPlain, "   Listing", NULL },
	{ 0, 1, JAM1, 0, 1, &taPlain, "   No Listing", NULL },
	{ 0, 1, JAM1, 0, 1, &taPlain, "   Speech", NULL },
	{ 0, 1, JAM1, 0, 1, &taPlain, "   No Speech", NULL },
};

struct MenuItem Items[] =
{
	{	/* Backup */
		&Items[1], 0, 0, 72, 10, 
			ITEMENABLED | ITEMTEXT | HIGHCOMP ,
			0, (APTR)&ItemText[0], NULL, NULL, NULL, NULL 
	},
	{
		&Items[2], 0, 10, 72, 10, 
			ITEMENABLED | ITEMTEXT | HIGHCOMP ,
			0, (APTR)&ItemText[1], NULL, NULL, NULL, NULL 
	},
	{
		&Items[3], 0, 20, 72, 10, 
			ITEMENABLED | ITEMTEXT | HIGHCOMP,
			0, (APTR)&ItemText[2], NULL, NULL, NULL, NULL 
	},
	{
		NULL, 0, 30, 72, 10, 
			ITEMENABLED | ITEMTEXT | HIGHCOMP,
			0, (APTR)&ItemText[3], NULL, NULL, NULL, NULL 
	},
	{
		&Items[5], 0, 0, 152, 10, 		/* Compression */
			ITEMENABLED | ITEMTEXT | CHECKIT | HIGHCOMP | CHECKED,
			2, (APTR)&ItemText[4], NULL, NULL, NULL, NULL 
	},
	{
		&Items[6], 0, 10, 152, 10, 		/* No Compression */
			ITEMENABLED | ITEMTEXT | CHECKIT | HIGHCOMP,
			1, (APTR)&ItemText[5], NULL, NULL, NULL, NULL 
	},
	{
		&Items[7], 0, 20, 152, 10, 		/* Listing */
			ITEMENABLED | ITEMTEXT | CHECKIT | HIGHCOMP | CHECKED,
			8, (APTR)&ItemText[6], NULL, NULL, NULL, NULL 
	},
	{
		&Items[8], 0, 30, 152, 10, 		/* No Listing */
			ITEMENABLED | ITEMTEXT | CHECKIT | HIGHCOMP,
			4, (APTR)&ItemText[7], NULL, NULL, NULL, NULL 
	},
	{
		&Items[9], 0, 40, 152, 10, 		/* Speech */
			ITEMENABLED | ITEMTEXT | CHECKIT | HIGHCOMP | CHECKED,
			32, (APTR)&ItemText[8], NULL, NULL, NULL, NULL 
	},
	{
		NULL, 0, 50, 152, 10, 			/* No Speech */
			ITEMENABLED | ITEMTEXT | CHECKIT | HIGHCOMP,
			16, (APTR)&ItemText[9], NULL, NULL, NULL, NULL 
	},
};

struct Menu Titles[] =
{
	{ &Titles[1], 3, 0, 70, 0, MENUENABLED, "Project", &Items[0],0,0,0,0 },
	{ NULL, 72, 0, 54, 0, MENUENABLED, "Flags", &Items[4],0,0,0,0 },
};

SHAR_EOF
cat << \SHAR_EOF > Menu.h
/* Menu constant definitions */

#define MENU_PROJECT		0
#define		ITEM_BACKUP			0
#define 	ITEM_RESTORE		1
#define		ITEM_ABOUT			2
#define		ITEM_QUIT			3

#define MENU_FLAGS			1
#define		ITEM_COMPRESS		0
#define		ITEM_NOCOMPRESS 	1
#define		ITEM_LIST			2
#define		ITEM_NOLIST			3
#define		ITEM_SPEECH			4
#define		ITEM_NOSPEECH		5
SHAR_EOF
cat << \SHAR_EOF > MiscRequest.c
/* Miscellaneous requester support routines. */

#include <exec/memory.h>
#include <intuition/intuition.h> 
#include <intuition/intuitionbase.h> 
#include <libraries/dosextens.h> 
#include <graphics/text.h> 
#include <functions.h>
#include <ctype.h> 


struct IntuiText diskreq_body_text = {
	AUTOFRONTPEN,			/* FrontPen */
	AUTOBACKPEN,			/* BackPen */
	AUTODRAWMODE,			/* DrawMode */
	AUTOLEFTEDGE,    		/* LeftEdge */
	AUTOTOPEDGE, 			/* TopEdge */
	NULL,					/* ITextFont Pointer */ 
	(UBYTE *)"",			/* IText */
	NULL					/* NextText */
	};


struct IntuiText diskreq_pos_text = {
	AUTOFRONTPEN,			/* FrontPen */
	AUTOBACKPEN,			/* BackPen */
	AUTODRAWMODE,			/* DrawMode */
	AUTOLEFTEDGE,    		/* LeftEdge */
	AUTOTOPEDGE, 			/* TopEdge */
	NULL,					/* ITextFont Pointer */ 
	(UBYTE *)"OK",			/* IText */
	NULL					/* NextText */
	};

struct IntuiText diskreq_neg_text = {
	AUTOFRONTPEN,			/* FrontPen */
	AUTOBACKPEN,			/* BackPen */
	AUTODRAWMODE,			/* DrawMode */
	AUTOLEFTEDGE,    		/* LeftEdge */
	AUTOTOPEDGE, 			/* TopEdge */
	NULL,					/* ITextFont Pointer */ 
	(UBYTE *)"Cancel",		/* IText */
	NULL					/* NextText */
	};


/* Generate an autorequest for the insertion of a floppy disk.
 * Called with:
 *		window:			pointer to window for request
 *		drivespec:		the disk drive name
 */
int
RequestDisk(window, drivespec)
	struct Window *window; char *drivespec;
{
	UBYTE prompt[41];

	strcpy(prompt,"Insert blank disk in ");
	strncat(prompt,drivespec,(sizeof(prompt)-strlen(prompt)-1));
	diskreq_body_text.IText = prompt;
	return AutoRequest(
		window, &diskreq_body_text, &diskreq_pos_text,
		&diskreq_neg_text, DISKINSERTED, NULL, 300L, 50L);
}
SHAR_EOF
cat << \SHAR_EOF > ReadME

This is MRBackup, version 1.0.  This program was written under Manx
Aztec C, version 3.40a, to run under KickStart 1.2.  The source was
written with 4 character tab settings, using Z.  If you would like a
copy of my file retab utility to alter the tab settings, just ask.

											Mark Rinfret
											mark@unisec.USI.COM
SHAR_EOF
cat << \SHAR_EOF > Restore.c
/* Filename:	restore.c
 * Author:		Mark R. Rinfret
 * Date:		08/02/87
 * Description:	Restore processing module for MRBackup
 */


#include <libraries/dosextens.h>
#include <exec/memory.h>
#include <functions.h>

extern char conmsg[];
extern char backpath[], homepath[];
#ifdef DEBUG
extern char debugmsg[];
#endif
extern char destpath[], destvol[];
extern unsigned do_compress;
extern struct DateStamp *since;
extern char srcpath[], srcvol[];

static char fullbackpath[256], fullhomepath[256];

static unsigned home_is_device;		/* true => home is "DH<N>:" */
static char temp[256];

/* Restore files from floppy disk. */

int
Restore()
{
	int status = 0;

	Speak("And away we go!");

	BreakPath(backpath, srcvol, srcpath);

	if (homepath[strlen(homepath)-1] == ':')
		home_is_device = 1;
	else
		home_is_device = 0;

	status = RestoreFile(srcpath);
	if (status == 0) {
		sprintf(conmsg,"Restore completed successfully\n");
		Speak("I have completed my mission successfully.\n");
	}
	else {
		sprintf(conmsg,"Restore terminated with status %d\n",status);
		Speak("Perhaps you should check things out and try it again.\n");
	}
	WriteConsole(conmsg);
}
^L
/* Restore all the files in a directory.
 * Called with:
 *		lock:		lock on the directory
 *		fib:		pointer to file info block
 *		path:		directory pathname (without volume)
 * Returns:
 *		status (0 => success)
 */
int
RestoreDir(lock, fib, path)
	struct Lock *lock; struct FileInfoBlock *fib; char *path;
{
	struct Lock *dirlock = NULL, *filelock = NULL;
	char newpath[256];
	int status = 0;

	strcpy(temp, homepath);
	if (*path) {
		if (!home_is_device) strcat(temp, "/");
		strcat(temp, path);
	}
#ifdef DEBUG
	sprintf(debugmsg,"Checking for directory %s\n",temp);
	DebugWrite(debugmsg);
#endif
	if (!(dirlock = Lock(temp, SHARED_LOCK))) {
		if ((status = IoErr()) == ERROR_OBJECT_NOT_FOUND) {
#ifdef DEBUG
			sprintf(debugmsg,"Creating directory %s\n",temp);
			DebugWrite(debugmsg);
#endif
			if (!(dirlock = CreateDir(temp))) {
				status = IoErr();
				sprintf(conmsg,"Unable to create directory %s: %d\n",
						temp, status);
				TypeAndSpeak(conmsg);
				goto done;
			}
			status = 0;
		}
		else {
			sprintf(conmsg,"RestoreDir cannot lock %s: %d\n",temp, status);
			TypeAndSpeak(conmsg);
			return status;
		}
	}
	if (dirlock) UnLock(dirlock);

	while (ExNext(lock,fib)) {
		strcpy(newpath, path);
		if (*newpath)
			strcat(newpath, "/");
		strcat(newpath, fib->fib_FileName);
		if (status = RestoreFile(newpath)) {

			/* filter out "permissable:" errors */

			if (status == ERROR_OBJECT_IN_USE ||
				status == ERROR_WRITE_PROTECTED)
				status = 0;
			else
				break;
		}
	}
done:
	return status;
}
^L
/* Restore one or more files according to the calling pathname.
 * The path argument does not contain the backup volume name.
 */
int
RestoreFile(path)
	char *path;
{
	struct FileInfoBlock *fib = NULL, *fib2 = NULL;
	UBYTE exists = FALSE, ignore = FALSE;
	struct Lock *lock = NULL;
	USHORT namelength;
	UBYTE savechar;
	int status = 0;

	if (status = CheckStop()) return status;

	if (!(fib = (struct FileInfoBlock *)
		AllocMem((long) sizeof (struct FileInfoBlock), 
					MEMF_PUBLIC | MEMF_CHIP))) {
		TypeAndSpeak("RestoreFile could not allocate FIB!\n");
		return ERROR_NO_FREE_STORE;
	}

	sprintf(fullbackpath, "%s:%s",srcvol,path);
	strcpy(fullhomepath, homepath);
	if (*path) {
		if (!home_is_device) strcat(fullhomepath, "/");
		strcat(fullhomepath, path);
	}

#ifdef DEBUG
		sprintf(conmsg,"fullbackpath = %s\n",fullbackpath);
		DebugWrite(conmsg);
		sprintf(conmsg,"fullhomepath = %s\n",fullhomepath);
		DebugWrite(conmsg);
#endif

	if (!(lock = Lock(fullbackpath, SHARED_LOCK))) {
		status = IoErr();
		sprintf(conmsg, "RestoreFile: can't lock %s: %d\n",
				fullbackpath, status);
		TypeAndSpeak(conmsg);
		goto done;
	}

	if (!Examine(lock, fib)) {
		status = IoErr();
		sprintf(conmsg, "RestoreFile can't examine %s: %d\n", 
				fullbackpath, status);
		TypeAndSpeak(conmsg);
		goto done;
	}

	if (fib->fib_DirEntryType > 0) {	/* path is a directory */
		status = RestoreDir(lock, fib, path);
		UnLock(lock);
		lock = NULL;
	}
	else {
		UnLock(lock);
		lock = NULL;
/*#define NOCOPY*/
#ifndef NOCOPY
		/* If this file exists, then check its modification date.  If
		 * it's newer than the backup, don't replace it.
		 */

		if ((lock = Lock(fullhomepath, SHARED_LOCK))) {

			if (!(fib2 = (struct FileInfoBlock *)
				AllocMem((long) sizeof (struct FileInfoBlock), 
							MEMF_PUBLIC | MEMF_CHIP))) {
				TypeAndSpeak("RestoreFile could not allocate FIB!\n");
				status = ERROR_NO_FREE_STORE;
				goto done;
			}
			Examine(lock, fib2);
			UnLock(lock);
			lock = NULL;
			if (CompareDS(&fib2->fib_Date, &fib->fib_Date) >= 0)
				ignore = TRUE;
		}
		if (ignore) {
			sprintf(conmsg,"Skipping %s.  Current version is newer.\n",
					path);
			TypeAndSpeak(conmsg);
		}
		else {
			if (!do_compress || !IsCompressed(fullhomepath)) {
copyfile:
				sprintf(conmsg,"Copying %s\n", fullbackpath);
				WriteConsole(conmsg);
				status = CopyFile(fullbackpath, fullhomepath);
			}
			else {
				/* truncate the destination pathname (remove ".z") */

				namelength = strlen(fullhomepath);
				fullhomepath[namelength-2] = '\0';
				sprintf(conmsg, "Decompressing %s\n", fullbackpath);
				WriteConsole(conmsg);
				if (decompress(fullbackpath, fullhomepath)) {
					sprintf(conmsg, 
						"Decompression of %s failed - will copy.\n",
						fullbackpath);
					TypeAndSpeak(conmsg);
					/* restore ".z" to name */
					fullhomepath[namelength-2] = '.'; 
					goto copyfile;
				}
				CopyFileDate(fullbackpath, fullhomepath);
			}
		}
#endif
	}
done:
	if (lock) UnLock(lock);
	if (fib) FreeMem(fib, (long) sizeof(struct FileInfoBlock));
	if (fib2) FreeMem(fib2, (long) sizeof(struct FileInfoBlock));
	return status;
}
SHAR_EOF