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