[comp.sources.amiga] MRBackup

ain@j.cc.purdue.edu (Patrick White) (01/29/88)

Program Name:	MRBackup
Submitted By:	uiucdcs!amanpt1.ZONE1.COM!mrr@ee.ecn.purdue.edu (Mark Rinfret)
Summary:	A hard disk backup utility.
Poster Boy:  Pat White  (ain@j.cc.purdue.edu)
Untested.

NOTES:
   I reshar'ed all of the source to get rid of some explicit path references
in where the files get put.  Now it will unshar on unix and the Amiga into
the current directory.
   I did *not* change the explicit path references to include files in the
.c files.
   I also included the docs that were sent with the binary version, so these
4 parts are complete source with docs.


-- Pat White   (co-moderator comp.sources/binaries.amiga)
UUCP: j.cc.purdue.edu!ain  BITNET: PATWHITE@PURCCVM   PHONE: (317) 743-8421
U.S.  Mail:  320 Brown St. apt. 406,    West Lafayette, IN 47906

========================================


#	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
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	MRBackup.TXT
#	ReadMe
#	AboutTheSource
#	ToDo
#	MRBackup.init
#	MRBackup.xcld
#	Makefile
#	AmigaFunctions.h
#	Console.h
#	ConsoleStuff.h
#	DiskMisc.h
#	Gadget.h
#	MRBackup.h
#	MRDates.h
#	Menu.h
#	Timer.h
# This archive created: Thu Jan 28 10:58:52 1988
# By:	Patrick White (PUCC Land, USA)
echo shar: extracting MRBackup.TXT '(16232 characters)'
cat << \SHAR_EOF > MRBackup.TXT
















                      MRBackup Version 2.0

                   A Hard Disk Backup Utility
                             for the
                         Commodore Amiga


                         2 January 1988

                     Author: Mark R. Rinfret
                     















































Introduction

     This  document   describes   a   program,   named   MRBackup
(pronounced M R Backup, not  Mister  Backup :-), which will allow
you to backup an Amiga hard  disk  drive to floppy disk media.  I
wrote MRBackup primarily out of my  own need.  I also saw it as a
means for learning more about programming  the Amiga.  If you are
already familiar with this  program,  you may want to jump to the
"Changes" section at the end of  this  document.  Otherwise, read
on.

     MRBackup is  reasonably  flexible,  allowing  you to back up
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.  When I do it
again, I'll probably  go  with  the  specialized  approach  - the
current method is just too darned slow.

     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,  once  a week and "areas of
interest" once a day (default).  

     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 but has been  adapted  to  employ  buffering and AmigaDOS
I/O. 


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,  some  other  gadgetry,   and  a  couple of embedded
windows.  If you click with the right  mouse button and drag from












left to right across the menu bar, you'll see the various program  
menus which control MRBackup's operation.


     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 files' normal "home" and "Backup Path"
always refers to the location where the copies are kept.  

     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.  

     The "Exclude Path",  if  specified,  is  the  name of a file
which contains lines describing files  which should not be backed
up. Any filename matching an entry  in this list will be excluded
from  the   backup.    The   exclude   file   consists   of  file
specifications, one per line,  or  comments (a line with a number
sign  - # - in column 1).   Blank  lines  are ignored.  Also, the
specifications  are  case-insensitive.   That  is,  "SYSTEM"  and
"system" are equivalent.  Examples:

# Exclude all object files
*.o
# Exclude all files beginning with "System":
system*
# Exclude all ".info" files:
*.info
# Exlude the "include" directory and all subdirectories:
include

     You can customize the operation of  MRBackup through the use
of   an   initialization   file.    This   file   must  be  named
S:MRBackup.init.  With it, you may  set your desired defaults and
Flags.  Each line in the file  consists  of an option setting, of
the form:

OPTION = VALUE

or a comment.  Comment  lines  begin  with  a number sign (#).  A
sample initialization file is included  which illustrates the use
of all possible parameter settings.


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, if formatting is enabled.

     Once your floppies  are  ready  and  stacked  neatly  within
reach, the fun begins.  First, modify the pathname specifications
according to your requirements.  Normally,  the "Home Path" would
be the device name of your hard  disk  or a directory on it (e.g.
DH0:src) while the "Backup Path" would specify the device name of
one of your floppy drives (e.g. DF0:).  While earlier versions of
MRBackup were pretty rigid  in  this  regard,  this  version will
allow  you  to   specify   ANY   disk   device   name  in  either
specification.  PLEASE  BE  CAREFUL!   Many  users asked for this
increased flexibility.  Just be aware  that it carries with it an
increased risk that  you  now  have  more  "opportunity"  to burn
yourself with improper pathname specifications.  

     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 disable  the  Generate  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 day  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 null
date value (all blanks) is allowed, should you want to backup all
files in the  home  path,  regardless  of  their  creation dates.
Otherwise, the required date and time format is MM/DD/YY HH:MM:SS
(24 hour clock), the time specification being optional.  You will
notice upward and  downward  pointing  "arrows"  above  each date
component.  Clicking on the  appropriate  arrow will increment or
decrement the respective portion of  the date.  Though the values
normally  will   wrap   around,   not   affecting  adjacent  date
components, certain situations  will  arise  where incrementing a
day value may cause the month to increment (incrementing February
28 in a non-leap-year will  yield  March 31).  I just didn't feel
the extra logic was worth it (I got lazy).

     Once  the  date  has  been  entered,   the  rest  is  fairly
automatic.  You will be  prompted  immediately for a 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.
If formatting is enabled, 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 they were distinct)
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 MRBackup's  screen 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
speech is enabled ) 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:

     mrr@amanpt1.ZONE1.COM
     ...rayssd!galaxia!amanpt1!mrr

     or

     Mark R. Rinfret
     348 Indian Avenue
     Portsmouth, RI 02871
     401-846-7639 (home)
     401-849-8900 (work)


Changes Since the Last Release

     This section lists  changes  that  have  been  introduced in
version 2.0.  Though I will  attempt  to be thorough, some things
may slip through the cracks.  Please forgive any oversights.


New User Interface

     MRBackup has undergone a major facelift.  It now has its own
screen and color palette and  newly  designed menus and gadgetry.
All  of  this  is  due  to  my   use  of  PowerWindows  2.0  from
Inovatronics, a very worthwhile tool for Intuition programming.

     You will notice some new gadgetry on the screen.  I've added
a "fuel  gauge"  which  indicates  the  "fullness"  of the output
floppy.  There's a new  error  count  gadget  which  tracks total
errors encountered and  a  "Current  Backup  Volume  Name" gadget
which keeps you  informed  as  to  which  disk  you're  currently
archiving to or restoring from.

     The Pathname  Specifications  window  has  a close box which
will make the window go away.   A  new menu, Windows, has an item
Pathnames which will reopen the window for you.

     A new command,  Save  Preferences,  allows  you to save your
current  pathname  specs  and   flags  to  the  preferences  file
(S:MRBackup.init).  The Flags menu has been totally redone.  This 
menu previously consisted of item pairs (<feature>/No <feature> -












pretty dumb, I must admit -  I  think  I  was intrigued by mutual
exclusion at the time).   The  new  Flags  menu  just consists of
check-marked items.   If  an  item  is  checked,  the  feature is
enabled.


Improved Error Handling

     This is usually the hardest  task  in  any programming chore
and usually gets  the  least  attention.   Previous  versions  of
MRBackup were no  exception.   Version  2.0,  however, has come a
long  way  in   this   direction,   especially  in  the  area  of
recoverability.  A new requester has  been introduced which lists
your recovery (or abort)  options  when  certain errors have been
detected.  Most significantly,  new  context-saving code has been
added which will allow you  to  restart  a  backup  disk from its
beginning,  should   a   non-recoverable   output   error  occur.
Currently, this context information  is saved in memory.  I would
like to save it to a file on the system disk.  This, coupled with  
a new command (Resume Backup),  would  allow recovery even from a
power failure.


Increased Flexibility

     Previous versions of MRBackup were  fairly rigid with regard
to home and backup path  specifications.   Version 2.0 allows ANY
disk device to be referenced in either location.  On systems with
dual floppy disks, you can even backup from floppy to floppy!

     MRBackup now allows you to suppress formatting of the backup
disk.  This allows you to  "freshen"  a previously created backup
set.  Though this option should be  used with care, it does speed
things up and enhances MRBackup's partial backup capability.


Big File Handling

     This is truly a last-minute item!  I personally don't have a
file big enough to require this, so I didn't address the problem.
However, enough users have  requested  this that I took a stab at
it.  "Big" files, according  to  MRBackup,  are  files  which are
larger than the  formatted  capacity  of  the  output media.  Iff
(sic) you enable the "Do Big Files" and "Format Destination" Flag
menu items, MRBackup will split big  files across multiple floppy
disks.  If these options  are  not  enabled,  MRBackup  will just
complain that the file is too big to back up and go on.

     Some of you will probably think  that  the method used to do
this is somewhat kludgy, but it's  consistent with my approach to
total AmigaDOS  compatibility.   When  backing  up  a big file, a
special information  file,  MRBackup.bigfile,  is  written to the
output disk.  The information  file  contains stuff like the file
name, the "chunk number" (which piece of the file is this?),  the












chunk size, and a flag which  indicates  whether this is the last
chunk.  This information  is  used  by  the  restore operation to
insure that the file chunks get reassembled in the correct order.

     In order to test this, I had to write  a program to create a
big file (slightly larger  than  1  floppy  disk), archive it and
restore it, then compare the two files.  To do all this, I had to
first clean house on my 20 MB  hard  drive!   The big file backup
appears to work OK.  I  think  it  could  use  some more testing,
however, and would  encourage  you  to  test  drive it before you
commit to it.


   














































SHAR_EOF
if test 16232 -ne "`wc -c MRBackup.TXT`"
then
echo shar: error transmitting MRBackup.TXT '(should have been 16232 characters)'
fi
echo shar: extracting ReadMe '(3242 characters)'
cat << \SHAR_EOF > ReadMe

This is MRBackup, version 2.0.  This program was written under Manx
Aztec C, version 3.40b, to run under KickStart 1.2.  The source was
written with 4 character tab settings, using Z, a vi-like editor
packaged with the Aztec C compiler.  If you would like a
copy of my file retab utility to remove or alter the tabs, just ask.  
Also, if you did not receive source with this distribution, it is
available for the asking.  See the instructions in the About menu.

Be sure to copy the files MRBackup.init and MRBackup.xcld to the S: 
directory.  

											Mark R. Rinfret
											mrr@amanpt1.ZONE1.COM
---------------------------------------------------------------------

About the program...

MRBackup evolved from a simple-minded program with a tty-style interface
to the version you see here.  Though I'm a true believer in structured 
programming techniques, good design and consistent style, you'll see
plenty of "holes" in the program where expedience won out over good
design (design == time, time == scarce_resource).  This version has been
groomed significantly and should be much more robust.  The code is
much more consistent and readable, though there is probably a significant
amount of "fat" that could be trimmed, especially in the realm of error
handling and reporting.

I also am a true believer in "using someone else's wheel" (if it's free)
rather than inventing my own.  I've incorporated several public domain
sources into MRBackup, each having its own particular style.  I've made
every effort to give credit where credit is due.  If I've slighted anyone,
please help me to correct the problem.  The evolution of MRBackup from 
version 1.3 to 2.0 was a continuing learning experience for me.  The
Intuition programming was made a whole lot easier through the use of
the improved PowerWindows 2.0 (no affiliation - just a satisfied customer).

There is nothing particularly clever about any of the disk-related stuff.
I've adhered to vanilla AmigaDOS throughout.  As I mentioned in the
ToDo file, I'll probably abandon this approach in a new version
(different name, not free, but cheap) that I'm considering.  The ability
to handle files larger than the capacity of a floppy was a last-minute
addition.  I was going to cop out again and not do it, but I just couldn't.
Don't go getting all excited, now.  You may not find my implementation to
your liking (that will make two of us).  However, it seems to work (if you
are careful) and you get what you pay for :-).  I also added the ability
to back up without formatting.  This could get messy, if not judiciously
used.

Please feel free to modify this program and re-release it.  If you make 
substantial changes, I'd like to hear about them.  I'd also like you to 
change the name of the program, perhaps to <your initials>Backup, as I've 
done.  Funny how many people call it "Mister Backup"...

'Nuff yak.  I've received a surprising number of long distance phone calls
regarding MRBackup and have mailed a lot of diskettes to people requesting
the source.  Thanks to all who took the time (and expense) to give me
encouragement.  Thanks, also, to the UseNet "heavies" and CATS for their
technical support.

                                              Mark

SHAR_EOF
if test 3242 -ne "`wc -c ReadMe`"
then
echo shar: error transmitting ReadMe '(should have been 3242 characters)'
fi
echo shar: extracting AboutTheSource '(811 characters)'
cat << \SHAR_EOF > AboutTheSource

MRBackup consists of two source libraries - the main source and a set of
support packages.  The original file structure during development looked
like:

	main source -  :src/utility/mrbackpu
	support     -  :src/lib

There are specific include references to :src/lib, so you should either
maintain this structure or edit all references as appropriate.  I've made
a conscious effort to get everything to compile relative to the current
disk, so you should be able to stuff this all onto a floppy, CD to the
directory containing the main source and "make mrbackup".  

This program was created for Aztec C, and I've done little to insure its
portability to Lattice (wish I could afford Lattice 4.0).  If anyone makes
the necessary modifications, please send me the sources or the diffs to
your changes.  Thanks.
SHAR_EOF
if test 811 -ne "`wc -c AboutTheSource`"
then
echo shar: error transmitting AboutTheSource '(should have been 811 characters)'
fi
echo shar: extracting ToDo '(529 characters)'
cat << \SHAR_EOF > ToDo

	"To do" list for MRBackup:


	MRBackup has gone about as far as I intend to take it in its public
domain form (greed rears its ugly head!).  Alas, though I would like to
stay with the "native" filesystem format, the performance of the program
suffers too much.  I have some ideas for a very fast version which, if I
pursue, I'll probably try to sell.  I'll continue to support MRBackup in
its current form and might even be pursuaded to add some trivial features
and ALWAYS will accept bug reports and try to get them fixed.


SHAR_EOF
if test 529 -ne "`wc -c ToDo`"
then
echo shar: error transmitting ToDo '(should have been 529 characters)'
fi
echo shar: extracting MRBackup.init '(806 characters)'
cat << \SHAR_EOF > MRBackup.init
# This is the user preferences file for MRBackup.  This file MUST be
# copied to "S:MRBackup.init" in order to be successfully used.
# This file should serve as an example for all possible preference
# settings.  Any line starting with "#" is assumed to be a comment and
# is ignored.  Note that if you use "Save Preferences", you will lose
# these comments.
#
# Note:  No provision is currently made for quoted parameters, so it is
#        not currently possible to do things like
#            LIST = "A name with embedded spaces".
#        If the outcry is great, I'll add that capability.
#
# This file is compatible with version 2.0.
#
home = 		DH0:
backup = 		DF1:
list = 		s:MRBackup.list
exclude = 		S:MRBackup.xcld
compression = 		YES
listing = 		NO
speech = 		YES
format = 		YES
bigfiles = 		YES
SHAR_EOF
if test 806 -ne "`wc -c MRBackup.init`"
then
echo shar: error transmitting MRBackup.init '(should have been 806 characters)'
fi
echo shar: extracting MRBackup.xcld '(140 characters)'
cat << \SHAR_EOF > MRBackup.xcld
# The terminating asterisk in some names supresses the ".info" files.
AmigaBasic*
c
devs
Empty*
l
lib
libs
Preferences*
System*
t
Trashcan*
SHAR_EOF
if test 140 -ne "`wc -c MRBackup.xcld`"
then
echo shar: error transmitting MRBackup.xcld '(should have been 140 characters)'
fi
echo shar: extracting Makefile '(3176 characters)'
cat << \SHAR_EOF > Makefile
CFLAGS = -b +IMRbackup.dmp
MFLAGS = -b
# Select one of the following definitions.  Select the first one if
# you want debugging code added.
# DEBUG = -DDEBUG
DEBUG = 

SRC = About.c Backup.c BigFiles.c Compress.c Console.c CopyFile.c Error.c \
	IntuiHandler.c List.c MiscRequest.c Restore.c UserPrefs.c

OBJ = About.o Backup.o BigFiles.o Compress.o Console.o CopyFile.o Error.o \
	IntuiHandler.o List.o MiscRequest.o Restore.o UserPrefs.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...

LIB = :src/lib

LIBSRC = $(LIB)/DateRequest.c $(LIB)/MRDates.c \
	$(LIB)/DiskMisc.c $(LIB)/FileMisc.c $(LIB)/FormatDisk.c \
	$(LIB)/sendpkt.c $(LIB)/Speech.c $(LIB)/Timer.c $(LIB)/unixwild.c

LIBOBJ = $(LIB)/DateRequest.o $(LIB)/MRDates.o \
	$(LIB)/DiskMisc.o $(LIB)/FileMisc.o $(LIB)/FormatDisk.o \
	$(LIB)/sendpkt.o $(LIB)/Speech.o $(LIB)/Timer.o $(LIB)/unixwild.o

$(LIB)/DateRequest.o: $(LIB)/DateRequest.c
	cc $(MFLAGS) -o $(LIB)/DateRequest.o $(LIB)/DateRequest.c

$(LIB)/MRDates.o: $(LIB)/MRDates.c
	cc $(MFLAGS) -o $(LIB)/MRDates.o $(LIB)/MRDates.c

$(LIB)/DiskMisc.o: $(LIB)/DiskMisc.c
	cc $(MFLAGS) -o $(LIB)/DiskMisc.o $(LIB)/DiskMisc.c

$(LIB)/FileMisc.o: $(LIB)/FileMisc.c
	cc $(MFLAGS) -o $(LIB)/FileMisc.o $(LIB)/FileMisc.c

$(LIB)/FormatDisk.o: $(LIB)/FormatDisk.c
	cc $(MFLAGS) -o $(LIB)/FormatDisk.o $(LIB)/FormatDisk.c

$(LIB)/sendpkt.o: $(LIB)/sendpkt.c
	cc $(MFLAGS) -o $(LIB)/sendpkt.o $(LIB)/sendpkt.c

$(LIB)/Speech.o: $(LIB)/Speech.c
	cc $(MFLAGS) -o $(LIB)/Speech.o $(LIB)/Speech.c

$(LIB)/Timer.o: $(LIB)/Timer.h $(LIB)/Timer.c
    cc $(MFLAGS) -o $(LIB)/Timer.o $(LIB)/Timer.c

$(LIB)/UnixWild.o: $(LIB)/UnixWild.c
	cc $(MFLAGS) -o $(LIB)/UnixWild.o $(LIB)/UnixWild.c

Main.o: MRBackup.h Console.h
	cc $(DEBUG) -b Main.c

IntuiHandler.o: MRBackup.dmp Menu.h Screen.c

$(OBJ): MRBackup.dmp

MRBackup.dmp: MRBackup.h Gadget.h Console.h Menu.h
	cc $(DEBUG) +HMRBackup.dmp MRBackup.h


MRBackup: Main.o $(OBJ) $(LIBOBJ)
	ln -w -o MRBackUp +CDB Main.o $(OBJ) $(LIBOBJ) -lc

MRBackup.TXT: MRBackup.DOC

zoo: MRBackup MRBackup.TXT README ToDo MRBackup.init MRBackup.xcld \
	zoo -add MRBackup MRBackup MRBackup.TXT README ToDo S:MRBackup.init
	MRBackup.xcld

MRBackup.uue: MRBackup
	uuencode > MRBackup.uue MRBackup MRBackup

MRBackup.info.uue: MRBackup.info
	uuencode > MRBackup.info.uue MRBackup.info MRBackup.info

EXECFILES = ReadMe MRBackup.info.uue MRBackup.TXT MRBackup.init \
			MRBackup.xcld ToDo

execshar: $(EXECFILES) MRBackup.uue
	echo "Making shar of executables."
	uusplit MRBackup.uue
	delete MRBackup.uue
	makekit -n EXEC $(EXECFILES) MRBackup.uue*
	delete MRBackup.uue#?
	date > execshar

SRCFILES = AboutTheSource $(SRC) Makefile

srcshar: $(SRCFILES)
	echo "Making shar of sources."
	makekit -n SRC *.h $(SRCFILES)
	date > srcshar

libshar: $(LIBSRC)
	echo "Making shar of library sources."
	makekit -n LIB $(LIB)/*.h $(LIBSRC)
	date > libshar

shar: execshar srcshar libshar
	echo "Archives have been built"
	date > shar
 > libshar

shar: execshar srcshar libshar
	echo "Archives have been built"
	date > shar
SHAR_EOF
if test 3176 -ne "`wc -c Makefile`"
then
echo shar: error transmitting Makefile '(should have been 3176 characters)'
fi
echo shar: extracting AmigaFunctions.h '(104 characters)'
cat << \SHAR_EOF > AmigaFunctions.h
#ifdef AZTEC_C
#	ifndef AMIGAFUNCTIONS
#		include <functions.h>
#		define AMIGAFUNCTIONS
#	endif
#endif
SHAR_EOF
if test 104 -ne "`wc -c AmigaFunctions.h`"
then
echo shar: error transmitting AmigaFunctions.h '(should have been 104 characters)'
fi
echo shar: extracting Console.h '(568 characters)'
cat << \SHAR_EOF > Console.h
/* Console I/O Routines Header File
 * Filename: ConsoleStuff.h
 *
 * This header file defines information used by routines supplied in
 * Rob Peck's "Programmer's Guide to the Amiga", 1987.
 */

#include <exec/io.h>

typedef struct ConIOBlocks {
	struct IOStdReq *writeReq;			/* I/O write request */
	struct IOStdReq *readReq;			/* I/O read request */
	struct MsgPort *tpr;				/* pointer to ReplyPort */
										/* for the console read */
	} ConIOBlocks;

void ConWrite();
ConIOBlocks *CreateConsole();
int DeleteConsole();
void EnqueueRead();
int CGetCharacter();
SHAR_EOF
if test 568 -ne "`wc -c Console.h`"
then
echo shar: error transmitting Console.h '(should have been 568 characters)'
fi
echo shar: extracting ConsoleStuff.h '(572 characters)'
cat << \SHAR_EOF > ConsoleStuff.h
/* Console I/O Routines Header File
 * Filename: ConsoleStuff.h
 *
 * This header file defines information used by routines supplied in
 * Rob Peck's "Programmer's Guide to the Amiga", 1987.
 */

#include <exec/io.h>

typedef struct ConIOBlocks {
	struct IOStdReq *writeReq;			/* I/O write request */
	struct IOStdReq *readReq;			/* I/O read request */
	struct MsgPort *tpr;				/* pointer to ReplyPort */
										/* for the console read */
	} ConIOBlocks;

ConIOBlocks *CreateConsole();
int DeleteConsole();
void EnqueueRead();
void WriteConsole();
int CGetCharacter();
SHAR_EOF
if test 572 -ne "`wc -c ConsoleStuff.h`"
then
echo shar: error transmitting ConsoleStuff.h '(should have been 572 characters)'
fi
echo shar: extracting DiskMisc.h '(201 characters)'
cat << \SHAR_EOF > DiskMisc.h
/* Declarations for the DiskMisc package.
 * Filename:	DiskMisc.h
 * Date:		11/24/87
 */

LONG 			DiskBlocksLeft();
struct InfoData *GetDiskInfo();
char 			*GetVolumeName();
LONG 			TotalDiskBlocks();
SHAR_EOF
if test 201 -ne "`wc -c DiskMisc.h`"
then
echo shar: error transmitting DiskMisc.h '(should have been 201 characters)'
fi
echo shar: extracting Gadget.h '(257 characters)'
cat << \SHAR_EOF > Gadget.h

/* Definitions for Gadget ID numbers */ 

#define HOMEPATH 	0
#define BACKPATH	1
#define LISTPATH 	2
#define XCLDPATH 	3

#define STOP		4
#define GAUGE		5
#define CURVOLUME	6

#define ABORT		7
#define FILERETRY	8
#define DISKRESTART 9
#define FILESKIP	10

SHAR_EOF
if test 257 -ne "`wc -c Gadget.h`"
then
echo shar: error transmitting Gadget.h '(should have been 257 characters)'
fi
echo shar: extracting MRBackup.h '(5265 characters)'
cat << \SHAR_EOF > MRBackup.h
/* MRBackup - include file for global data and definitions.
 * Filename:	MRBackup.h
 * Date:		08/22/87
 *
 * History:		(most recent change first)
 *
 * 11/19/87 -MRR- V2.0: Changed window version and date.
 */

/* Main.c defines MAIN.  It should not defined elsewhere. */

#ifdef MAIN
#define EXTERN 
#else
#define EXTERN extern
#endif

#include <exec/memory.h>
#include <exec/types.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <stdio.h>
#include <ctype.h>
#include <setjmp.h>
#include <time.h>
#include <functions.h>

#include "gadget.h"
#include "menu.h"
#include "Console.h"

/* Constants */

#define false	0		/* for short parameter requirements */
#define true	1		/* for short parameter requirements */
#define BUFMAX (32L * 1024L) /* max size for copy/compress buffer */
#define LINES_PER_PAGE  60

#define VOLUME_MAX	32	/* max characters in volume name */
#define PATH_MAX	256 /* max characters in pathname (very arbitrary) */

/* Define error recovery constants.  Note that these are all powers
 * of 2 to allow creating 'sets' of allowable options during the
 * recovery prompt.
 */

#define NERRCODE			5	/* number of error recovery codes */

#define ERR_NONE			0	/* what we want ALL the time :-) */
#define ERR_ABORT			1	/* give up the ship */
#define ERR_RETRY_FILE		2	/* let's try that file one more time */
#define ERR_RESTART_VOLUME 	4	/* for media errors on output floppy */
#define ERR_IGNORE			8	/* ignore this error and trudge on */

/* Macros */

/* determine if a menu item is "checked" */

#define GadgetString(g) ((struct StringInfo *) g->SpecialInfo)->Buffer
#define IsChecked(item) ((item->Flags & CHECKED) == CHECKED)

typedef struct t_pattern {
	struct t_pattern * next_pattern;
	char *pattern;
	} T_PATTERN;

/* The following structure is used to link file and directory node
 * information into a doubly-linked list.  This provides a way to
 * defer processing of sub-directory nodes until all files in a
 * current directory are processed.  As nodes are "consumed", they 
 * are returned to free memory.
 */

typedef struct t_file {
	struct t_file  *previous,*next;
	char   			*filename;
	USHORT			blocks;
	BOOL 			is_dir;			/* TRUE => it's a directory */
	} T_FILE;

/* The following structure links lists of T_FILE nodes. */

typedef struct t_file_list {
	T_FILE *first_file;
	T_FILE *last_file;
	} T_FILE_LIST;

/* External and forward function declarations */

extern char   	*calloc(), *index(), *rindex();
extern long   	DiskBlocks();
extern int	  	errno;
T_FILE 		  	*FindFile();

/* External data */

extern struct Gadget stopGadget;

/* Global data */

#ifdef DEBUG
EXTERN ConIOBlocks *debugConsole;
EXTERN struct Window *debugWindow;
EXTERN char debugMsg[512];
#endif

EXTERN UBYTE *buffer;				/* file copy/cmprs buffer (AllocMem) */
EXTERN ULONG bufSize;				/* size of buffer allocated */
EXTERN ConIOBlocks *progressConsole;/* for informative messages */
EXTERN struct Window *progressWindow;
EXTERN char conmsg[512];
EXTERN T_FILE *currentDir = NULL;	/* current directory node */

EXTERN char	destPath[PATH_MAX+1];
EXTERN char destVol[VOLUME_MAX+1];
EXTERN short diskNumber;			/* backup disk serial number */
EXTERN USHORT errorCount;			/* count of errors on backup/restore */
EXTERN char *errName;               /* file name associated with error */
EXTERN BOOL excludeHasChanged;   	/* true when new path specified */
EXTERN T_PATTERN *excludeList, *lastExclude;
EXTERN char excludePath[81]; 		/* list of file patterns to exclude */
EXTERN struct FileInfoBlock *fibWork;	/* working file info block */
EXTERN struct GfxBase *GfxBase;		/* graphics library handle */
EXTERN struct IntuitionBase *IntuitionBase;

EXTERN USHORT level;				/* file nesting level */
EXTERN USHORT lineCount;			/* number of lines in listing */
EXTERN FILE *listing;
EXTERN T_FILE_LIST mainList;
EXTERN struct Screen *mainScreen;
EXTERN struct Window *mainWindow;

EXTERN struct DateStamp *now, *since; /* for date comparisons */
EXTERN struct Window *pathWindow;	/* pathname specification window */
EXTERN LONG sizeLeft;				/* floppy blocks remaining */
EXTERN char	srcPath[PATH_MAX];
EXTERN char	srcVol[VOLUME_MAX+1];	/* source volume name */
EXTERN char temp[256];
EXTERN LONG totalSize;				/* capacity of output device */

/* The following flags suppress repetition of spoken
 * messages.  After all, let's not over-do it.
 */

EXTERN UBYTE atYourService;

/* Preset data */

#ifdef MAIN

char backPath[81] = "DF0:";			/* where backups go and restores
									   come from. */
char destDrive[5] = "DF0:";
USHORT doBigFiles = 1;				/* Allow big file backups. */
USHORT doCompress = 1;				/* Perform file compression. */
USHORT doFormat = 1;				/* Format output disks */
USHORT doListing = 1;				/* Generate listing. */
USHORT doSpeech = 1;				/* Talk to the user. */

char homePath[81] = "DH0:";			/* Backup/Restore from/to here. */
char listPath[81] = "PRT:";			/* Listings go here. */
#else
/* Declare preset external data without the presets. */
extern char backPath[81];
extern char destDrive[5];
extern USHORT doBigFiles;
extern USHORT doCompress;
extern USHORT doFormat;
extern USHORT doListing;
extern USHORT doSpeech;
extern char homePath[81];
extern char listPath[81];
#endif
SHAR_EOF
if test 5265 -ne "`wc -c MRBackup.h`"
then
echo shar: error transmitting MRBackup.h '(should have been 5265 characters)'
fi
echo shar: extracting MRDates.h '(663 characters)'
cat << \SHAR_EOF > MRDates.h
/* Filename:	MRDates.h
 * Various definitions used to support the MRDates package.
 * 
 * Important note:
 *		If the preprocessor symbol MRDATES is undefined, certain
 *      useful structures will be declared for use by the module
 *      including this file.
 */

#include <exec/types.h>

typedef struct {
	short year,month,day,hour,minute,second;
	} UnpackedDS;

#ifndef MRDATES				/* This is not MRDates.c? */

extern char *daynames[7];	/* day name strings, [0] = "Sunday" */

extern USHORT monthdays[12];/* Number of days preceding each month, 
							 * requires diddling for leap years. 
							 */

extern char *monthnames[12];/* [0] = January */

#endif
SHAR_EOF
if test 663 -ne "`wc -c MRDates.h`"
then
echo shar: error transmitting MRDates.h '(should have been 663 characters)'
fi
echo shar: extracting Menu.h '(406 characters)'
cat << \SHAR_EOF > Menu.h
/* Menu constant definitions */

#define MENU_PROJECT		0
#define		ITEM_ABOUT			0
#define		ITEM_BACKUP			1
#define 	ITEM_RESTORE		2
#define		ITEM_LOADPREFS		3
#define		ITEM_SAVEPREFS		4
#define		ITEM_QUIT			5

#define MENU_FLAGS			1
#define		ITEM_COMPRESS		0
#define		ITEM_BIGFILES		1
#define		ITEM_LIST			2
#define		ITEM_SPEECH			3
#define		ITEM_FORMAT			4

#define MENU_WINDOWS		2
#define		ITEM_PATHS			0
SHAR_EOF
if test 406 -ne "`wc -c Menu.h`"
then
echo shar: error transmitting Menu.h '(should have been 406 characters)'
fi
echo shar: extracting Timer.h '(267 characters)'
cat << \SHAR_EOF > Timer.h
/* Timer support routine declarations.
 * Filename:	Timer.h
 */

#include <exec/types.h>
#include <exec/io.h>
#include <devices/timer.h>
#include ":src/lib/AmigaFunctions.h"

struct timerequest *CreateTimer();
void DeleteTimer();
void StartTimer();
void StopTimer();
SHAR_EOF
if test 267 -ne "`wc -c Timer.h`"
then
echo shar: error transmitting Timer.h '(should have been 267 characters)'
fi
#	End of shell archive
exit 0

ain@j.cc.purdue.edu (Patrick White) (01/29/88)

Program Name:	MRBackup
Submitted By:	uiucdcs!amanpt1.ZONE1.COM!mrr@ee.ecn.purdue.edu (Mark Rinfret)
Summary:	A hard disk backup utility.
Poster Boy:  Pat White  (ain@j.cc.purdue.edu)
Untested.

NOTES:
   I reshar'ed all of the source to get rid of some explicit path references
in where the files get put.  Now it will unshar on unix and the Amiga into
the current directory.
   I did *not* change the explicit path references to include files in the
.c files.
   I also included the docs that were sent with the binary version, so these
4 parts are complete source with docs.


-- Pat White   (co-moderator comp.sources/binaries.amiga)
UUCP: j.cc.purdue.edu!ain  BITNET: PATWHITE@PURCCVM   PHONE: (317) 743-8421
U.S.  Mail:  320 Brown St. apt. 406,    West Lafayette, IN 47906

========================================


#	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
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	About.c
#	Backup.c
#	BigFiles.c
#	Compress.c
# This archive created: Thu Jan 28 11:03:59 1988
# By:	Patrick White (PUCC Land, USA)
echo shar: extracting About.c '(996 characters)'
cat << \SHAR_EOF > About.c
/* About.c: Tell the user a little about MRBackup. */

static char *abouts[] = {
"This is M R Backup, a hard disk backup utility written by Mark Rinfret.\n",
"This program allows you to perform complete or incremental backups.\n",
"Data compression and 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 Rinfret\n",
"    348 Indian Avenue\n",
"    Portsmouth, Rhode Island  02871\n\n",
"    or, mrr@amanpt1.ZONE1.COM\n"
"    or, rayssd!galaxia!amanpt1!mrr.\n\n",
"You may obtain the source for this program from Usenet archives,\n",
"by sending a floppy disk and postpaid mailer to the above address,\n",
"or by sending five dollars to the above address.\n",
(char *) 0L};

About()
{
	int i;

	for (i = 0; i < 5; ++i) WriteConsole("\n");
	for (i = 0;abouts[i];++i) {
		TypeAndSpeak(abouts[i]);
		if (!doSpeech) Delay(30L); /* little over 1/2 second */
	}
}
SHAR_EOF
if test 996 -ne "`wc -c About.c`"
then
echo shar: error transmitting About.c '(should have been 996 characters)'
fi
echo shar: extracting Backup.c '(22792 characters)'
cat << \SHAR_EOF > Backup.c
/* MRBackup: Backup Routines
 * Filename:	Backup.c
 * Author:		Mark R. Rinfret
 * Date:		09/04/87
 *
 * History:		(most recent change first)
 *
 * 12/29/87 -MRR- Enable "big file" backup.
 *
 * 11/19/87 -MRR- Add the listing file name to the exclusion list.
 *                Also, add some more error handling.
 *
 * 09/04/87 -MRR- This package was (finally) extracted from Main.c.
 */

#define MAIN

#include "MRBackup.h"
#include ":src/lib/DiskMisc.h"

T_FILE *AddFile();
void DisposeList();
USHORT FileSize();
T_FILE *MakeFileNode();

static unsigned filesInDir;		/* number of files in current directory */
T_FILE *savedDir;				/* Current directory at start of volume */
T_FILE_LIST savedList;			/* File list at start of volume */

^L
/* Add a filename to the list of excluded files.
 * Called with:
 *		name:		filename to be excluded
 */
AddExclude(name)
	char *name;
{
	T_PATTERN *p;

	p = (T_PATTERN *) calloc(sizeof(T_PATTERN), 1);
	p->pattern = calloc(strlen(name)+1, 1);
	strcpy(p->pattern, name);
	if ( ! excludeList ) 
		excludeList = p;
	else
		lastExclude->next_pattern = p;
	lastExclude = p;
}
^L
/* Add a file/directory name to the list.
 * Called with:
 *		name:		file/dir name string
 *		blocks:		size of file/directory in blocks (dir => 1)	
 *		isDir:		true => this is a directory name
 *		list:		address of list header
 * Returns:
 *		pointer to new node or NULL (out of memory)
 * Notes:
 *		The filename string MUST NOT contain the volume component,
 *		since it is used to build both source and destination
 *		pathnames.  Also note that this routine inserts the name
 *		into the list in sorted order (case sensitive).  Though this
 *		changes the "natural order" of the files, it makes them 
 *		easier to locate on the backup disks.
 */

T_FILE *
AddFile(name, blocks, isDir, list)
	char *name; USHORT blocks; BOOL isDir; T_FILE_LIST *list;
{
	T_FILE *fnode, *tnode;

	if (!(fnode = MakeFileNode(name)))
		return NULL;

	fnode->blocks = blocks;
	fnode->is_dir = isDir;

	if (!list->first_file) {			/* file list is empty? */
		list->first_file = fnode;		/* this is the new head */
	}
	else {
		/* Find the insertion point for this file. */

		for (tnode = list->first_file; tnode; tnode = tnode->next) {
			if (strcmp(fnode->filename, tnode->filename) <= 0) {
				fnode->next = tnode;	/* insert it here */
				if (tnode->previous)
					tnode->previous->next = fnode;
				fnode->previous = tnode->previous;
				tnode->previous = fnode;
				if (list->first_file == tnode)
					list->first_file = fnode;
				return fnode;
			}
		}

		/* Append file node to the end of the list. */

		fnode->previous = list->last_file;
		list->last_file->next = fnode;
	}
	list->last_file = fnode;
	return fnode;
}
^L
/* Main backup routine. */

Backup()
{
	int compare_flag, status = 0;

	errorCount = 0;

	Speak("O K,  let's get to work.");
	DateStamp(now);
	mainList.first_file = mainList.last_file = currentDir = NULL;

	if (doListing) {
		if ( OpenList(listPath) ) return;
	}

	/* Get the file comparison date.  Only files created on or after
	 * this date are backed up.
	 */
	do {
		*since = *now;
		since->ds_Days -= 1;		/* default interval is 1 day */
		since->ds_Minute = 0;
		since->ds_Tick = 0;
		Speak("Enter the date since your last backup.");
		DateRequest(mainWindow, "Backup files since:",since, since);
		if ( (compare_flag = CompareDS(since, now) ) >= 0) 
			DisplayBeep(NULL);
	} while (compare_flag >= 0);

	BreakPath(homePath, srcVol, srcPath);
	strcat(srcVol, ":");
	BreakPath(backPath, destDrive, destPath);

#ifdef DEBUG
	sprintf(debugMsg, "destDrive = %s, destPath = %s\n",destDrive,destPath);
	DebugWrite(debugMsg);
	sprintf(debugMsg, "srcVol = %s, srcPath = %s\n", srcVol,srcPath);
	DebugWrite(debugMsg);
#endif

	if (*destPath) {
		TypeAndSpeak("The backup path must be a device name.");
		status = ERR_ABORT;
		goto done;
	}
	strcat(destDrive,":");

/* !!! Failure of GetExcludes should not prevent backup. */

	if (*excludePath)
		if (GetExcludes()) goto done;

	AddExclude(listPath);		/* don't try to backup list (if file) */

	level = 0;
	diskNumber = 0;
	sizeLeft = 0;
	totalSize = 0;
	savedList.first_file = NULL;
	SetGauge(sizeLeft, totalSize);

	/* Force a new disk right away. */

	if (status = CheckSize(false)) goto done;

	if (*srcPath) {				/* starting path is a directory */
		if (!AddFile(srcPath, 1, true, &mainList))
			status = ERROR_NO_FREE_STORE;
	}
	else						/* starting path is a device */
		status = CollectFiles(srcPath,&mainList);

	if (!status) {
restart:
		while (mainList.first_file) {	/* while something in the list */
			if (status = BackupFiles(&mainList)) 	break;
			if (status = BackupDirs(&mainList)) 	break;
		}
	}
done:
	if (status == 0) {
		TypeAndSpeak("I am done, and everything seems to be O K.\n");
		TypeAndSpeak("It was a pleasure working with you.\n");
	}
	else {
		if (status == ERR_RESTART_VOLUME) {
			if ( ! ( status = RestoreContext() ) ) {
				sizeLeft = 0;
				SetGauge(sizeLeft, totalSize);
				NewLine(2);
				ListLine("*** Restarting volume ***");
				goto restart;
			}
		}
		else if (status != ERR_ABORT) {
			TypeAndSpeak("Things are not well, my friend.\n");
			sprintf(conmsg,"Your backup terminated with error %d.\n",status);
			TypeAndSpeak(conmsg);
		}
	}
	DisposeList(&mainList);
	DisposeList(&savedList);
	SetCurVolumeGadget("");
}

^L
/* Process the next directory in the file list.
 * This routine is recursive.
 * Called with:
 *		list:		file list to be processed
 * Returns:
 *		status code	(0 => success)
 */

int
BackupDirs(list)
	T_FILE_LIST *list;
{
	T_FILE *dirNode;
	T_FILE *saved_currentDir;
	int 	status = 0;
	T_FILE_LIST sublist;				/* subdirectory list header */

	sublist.first_file = sublist.last_file = NULL;
	saved_currentDir = currentDir;	/* remember current context */

/* There are a couple of things to note here.  The first is that once
 * we have reached here, there should be NO simple file nodes in "list".
 * That currently is not handled as an error, but probably should be.
 * Second, since this scan modifies the list, a removal of a directory
 * node starts the scan at the beginning of the list since we shouldn't
 * reference the links in the node we're removing.  Since we should be
 * removing the first node in the list anyway, who cares?
 */
	for (dirNode = list->first_file; dirNode; ) {
		if (dirNode->is_dir) {			/* found one */
			currentDir = dirNode;		/* set current directory */
			RemFile(dirNode, list);

/*			if (status = NewDir(currentDir->filename)) break; */

			if (status = CollectFiles(currentDir->filename,&sublist)) 
				break;
			if (status = BackupFiles(&sublist)) break;
			if (status = BackupDirs(&sublist))  break;
			dirNode = list->first_file;
		}
		else							/* should never get here !!! */
			dirNode = dirNode->next;
	}
	currentDir = saved_currentDir;
	return status;
}
^L
/* Backup all simple files in the current list.
 * Called with:
 *		list:		file list header
 * Returns:
 *		status code (0 => success)
 */
int
BackupFiles(list)
	T_FILE_LIST *list;
{
	T_FILE *primary, *secondary;
	int status = 0;

/* The following loop continually scans the file list (from the front),
 * looking for more file entries to process.  If the primary choice
 * fails, an attempt is made to find a file which will fit in the
 * space remaining.  If that attempt fails, then a new disk is formatted.
 */

	filesInDir = 0;					/* Nothing in directory yet */

	while (primary = FindFile(list,false)) { /* Find next file to process. */
		if (primary->blocks >= totalSize) { /* It's a biggy! */
			if ( ! (doBigFiles || doFormat) )  {

				/* "Big files" may only be backed up if "Allow Big Files" is
				 * enabled along with "Format Destination".
				 */
				sprintf(conmsg,"%s is too big to back up!\n",
						primary->filename);
				TypeAndSpeak(conmsg);
				TypeAndSpeak(
"In order to back up big files, you will have to select the Allow Big Files"
					);
				TypeAndSpeak(
					"and Format Destination options in the Flags menu."
					);
			} else {						/* Start crankin'! */
				status = DoFile(primary);
			}
		}
		else {
			if (primary->blocks >= sizeLeft) { 	/* file doesn't fit */
				if (!(secondary = FindFile(list,true))) {
					/* At this point, we know that there's at least one
					 * file to back up, but none that fit.  Start a new
					 * disk.
					 */
					if (status = NewDisk(true)) return status;
					continue;				/* try that file again */
				}
			primary = secondary;		/* use second choice */
			}
			if (status = DoFile(primary)) return status;
		}
		RemFile(primary,list);			/* delete the node */
	}
	if (currentDir) {					/* forget current directory */
		FreeFile(currentDir);
		currentDir = NULL;
	}
	return status;
}
^L
/* Check the file name about to be added against the exclude patterns.
 * Called with:
 *		name:	pathname to be checked
 * Returns:
 *		0 => no match
 *		1 => name was matched, ignore it.
 */

int
CheckExclude(name)
	char *name;
{
	int match = 0;
	T_PATTERN *p;

	for (p = excludeList; p; p = p->next_pattern) {
		if (match = wildcmp(p->pattern, name)) { 
			sprintf(conmsg,"Excluding %s\n", name);
			WriteConsole(conmsg);
			break;
		}
	}
	return match;
}

^L
/* Check the current number of disk blocks (sizeLeft) available.  If
 * less than 1, it's time to format a new disk.
 * Called with:
 *		create_dir:		true => OK to create continuation directory
 * Returns:
 *		0 => success
 *		1 => failure
 */

int
CheckSize(create_dir)
	int create_dir;
{
	if (sizeLeft > 0) return 0;
	return NewDisk(create_dir);
}
^L
/* Collect file names from a starting path.
 * Called with:
 *		name:		starting pathname
 *						Note that name may be a null string when copying
 *						the entire home device.
 *		list:		pointer to file list header
 * Returns:
 *		status code (0 => success)
 * Notes:
 *		CollectFiles attempts to collect all file and directory names
 *		for a given level, starting with "name".  If a simple filename
 *		is given as a starting path, only that name will be collected.
 *		If a directory name is given, then all pathnames contained
 *		within that directory (only) will be collected.  For each
 *		directory name collected, CollectFiles will be called again to
 *		collect files for that particular directory.  This iterative
 *		approach (vs. recursive) saves memory and allows us to maintain
 *		order at each directory level.
 */

int
CollectFiles(name, list)
	char *name; T_FILE_LIST *list;
{
	int     status = 0;
	struct FileInfoBlock   *FIB = NULL;
	T_FILE *fnode;			/* file descriptor node */
    struct Lock *lock = NULL;	
	char path[PATH_MAX+1];
	USHORT top_level;

#ifdef DEBUG
	sprintf(debugMsg,"Collecting files from %s\n",name);
	DebugWrite(debugMsg);
#endif

	if (CheckStop()) return ERR_ABORT;

	top_level = (*name == '\0');	/* empty name implies top level */

	if (!(FIB =
		AllocMem((long)sizeof(struct FileInfoBlock),
				 MEMF_CHIP|MEMF_CLEAR))) {
		TypeAndSpeak("CollectFiles: Can not allocate memory for FIB\n");
		return ERROR_NO_FREE_STORE;
	}

	strcpy(path,srcVol);			/* rebuild current home path */
	strcat(path, name);

	if (!(lock = (struct Lock *) Lock( path, SHARED_LOCK ))) {
		status = IoErr();
		sprintf(conmsg,"CollectFiles can not lock %s; error %d\n",
			path, status);
		TypeAndSpeak(conmsg);
		goto out2;
	}

	if ((Examine(lock,FIB))==0){
		status = IoErr();
		sprintf(conmsg,"CollectFiles can not examine %s; error: %d\n",
				name, status);
		TypeAndSpeak(conmsg);
		goto out2;
	}

	if (FIB->fib_DirEntryType > 0){	/* "name" is a directory */

		while(!status && ExNext(lock, FIB)) {
			if (CheckStop()) {
				status = ERR_ABORT;
				goto out2;
			}
			/* First, check the file against the exclusion list. */
			if (CheckExclude(FIB->fib_FileName))
				continue;

			if (FIB->fib_DirEntryType < 0) {

				/* Check the file date. */

				if (CompareDS(&FIB->fib_Date, since) < 0) {
#ifdef DEBUG
					sprintf(debugMsg,"Skipping %s\n",&FIB->fib_FileName[0]);
					DebugWrite(debugMsg);
#endif
					continue;
				}
			}
			if (top_level)
				*path = '\0';
			else {
				strcpy(path,name);
				strcat(path,"/");
			}
			strcat(path,FIB->fib_FileName);
			if (!AddFile(path, FileSize(FIB), 
							(FIB->fib_DirEntryType >= 0), list))
				status = ERROR_NO_FREE_STORE;
		}
		/* !!! Need check here for ERROR_NO_MORE_ENTRIES */
	}
	else {
		if ( ! CheckExclude(FIB->fib_FileName &&
			 (CompareDS(&FIB->fib_Date, since ) >= 0) ) ) {
			if( ! AddFile(name, FileSize(FIB),
						  (FIB->fib_DirEntryType >= 0), list))
				status = ERROR_NO_FREE_STORE;
			}
		}
out2: 
	UnLock(lock);
out1: 
	FreeMem(FIB, (long) sizeof(struct FileInfoBlock) );

	/* Don't give up if somebody else is using the file - just
	 * ignore the error.  The user can cancel us if there's a
	 * problem.
	 */

	if (status == ERROR_OBJECT_IN_USE) status = 0;
	return status;
}
^L
/* Dispose of a file info list.
 * Called with:
 *		list:		pointer to list structure
 */
void
DisposeList(list)
	T_FILE_LIST *list;
{
	while (list->first_file) RemFile(list->first_file, list);
}

^L
/* Process one file.
 * Called with:
 *		fnode:			node describing file
 * Returns:
 *		0	=> success
 *		1	=> failure
 */
int
DoFile(fnode)
	T_FILE *fnode;
{
#define MAX_RETRY	3

	int     status = ERR_NONE;
	char    destName[PATH_MAX+1];
	BOOL	fileIsBig;
	int		oldSize;
	char	srcName[PATH_MAX+1];

	if (++filesInDir == 1 && currentDir) {	

		/* Create directory on first file. */

		if (status = NewDir(currentDir->filename))
			return status;
		/* Directory info is listed in NewDir, need not be done here. */
	}

	/* If we got here with a big file, it's because doBigFiles is true.
	 * If we can't get at least 20 blocks of the file onto this disk,
	 * then allow CheckSize to ask for a new one.
	 */

	fileIsBig = (fnode->blocks >= totalSize);
	oldSize = sizeLeft;
	sizeLeft -= fnode->blocks;		/* deduct blocks for file */

	if (!fileIsBig || (oldSize < 20))
		if ( CheckSize(true) )			/* check disk space */
			return ERR_ABORT;

	if (sizeLeft > oldSize)			/* we just formatted */
		oldSize = sizeLeft;

/*#define NOCOPY*/						/* for fast debugging */
	do {
		if (status = CheckStop())	/* does user want out? */
			return status;

		sprintf(srcName,"%s%s",srcVol,fnode->filename);
		sprintf(conmsg,"Blocks left: %5d   ",oldSize);
		WriteConsole(conmsg);

		if ( !doCompress || 			/* not compressing files? */
			 IsCompressed(srcName) || 	/* file already compressed? */
			 fileIsBig || 				/* file too big to compress? */
			 fnode->blocks < 4	 		/* file too small to compress? */
	        ) {
			sprintf(destName,"%s%s",destVol,fnode->filename);
#ifndef NOCOPY
			if (fileIsBig) {
				sprintf(conmsg,
				"Doing multi-volume backup of %s\n",fnode->filename);
				WriteConsole(conmsg);
				status = BackupBigFile(fnode->filename);
			}
			else {
				sprintf(conmsg,"Copying %s\n",fnode->filename);
				WriteConsole(conmsg);
				status = CopyFile(srcName,destName);
			}
#endif
		}
		else {
			sprintf(destName,"%s%s.Z",destVol,fnode->filename);
			sprintf(conmsg,"Compressing %s\n",fnode->filename);
			WriteConsole(conmsg);
#ifndef NOCOPY
			if (!(status = compress(srcName,destName)))
				status = CopyFileDate(srcName,destName);
#endif
		}
		if (status) {
			sprintf(conmsg,
				"Oh darn it!  I got error %d on file %s.\n", status,
				fnode->filename);
			TypeAndSpeak(conmsg);
			NewLine(1);
			ListLine(conmsg);
			NewLine(1);
			unlink(destName);
			++errorCount;
			SetErrorGadget();		/* Update error counter */
			status = GetErrOpt(
						ERR_ABORT | ERR_IGNORE | 
						ERR_RETRY_FILE | ERR_RESTART_VOLUME);
		}
		else
			ListFileInfo(destName);

	} while (status == ERR_RETRY_FILE);

	if (status == ERR_IGNORE) status = ERR_NONE;

#ifndef NOCOPY
	if ( !status ){
		if ((sizeLeft = DiskBlocksLeft(destVol)) < 0)/* update blocks left */
			status = -sizeLeft;
		else
			SetGauge(sizeLeft, totalSize);
	}
#endif
	return status;
}

/* Compute the size of a file, in blocks, from its file information block.
 * If the FIB pointer is NULL, assume that the file is a directory.
 * Called with:
 *		FIB:		file information block (can be NULL)
 * Returns:
 *		Number of disk blocks required by file.
 */

USHORT
FileSize(FIB)
	struct FileInfoBlock *FIB;
{
	USHORT blocks;
	if (!FIB) {
		blocks = 1;
	}
	else if ( FIB->fib_DirEntryType >= 0 )
		blocks = 1;				/* assume 1 block for directory */
	else {
		blocks = ((FIB->fib_Size/488)+2); /* 488 = bytesinBlock - ovhd */
		blocks += (blocks/70);
	}
	return blocks;
}
^L
/* Attempt to find a file node in the file list.  If the check_size
 * parameter is true, only look at files which will fit in the space
 * remaining on the disk.
 * Called with:
 *		list:		pointer to file list to be searched
 *		check_size: false => find first file in the list
 *					true  => test space available
 * Returns:
 *		file node or NULL (not found)
 */

T_FILE *FindFile(list,check_size)
	T_FILE_LIST *list; int check_size;
{
	T_FILE *fnode;

	for (fnode = list->first_file; fnode; fnode = fnode->next) {
		if (!fnode->is_dir) {		/* don't consider directory nodes */
			if (!check_size) break;	/* take this one */
			if (fnode->blocks < sizeLeft) break;
		}
	}
	return fnode;
}

/* Free memory allocated to a file node.
 * Called with:
 *		node:	file node
 */
FreeFile(node)
	T_FILE *node;
{

	free(node->filename);
	free(node);
}
^L
/* An exclude file pathname has been specified.  Get the patterns it
 * contains.
 * Returns:
 *		status:	0 => success, failure otherwise
 */
int
GetExcludes()
{
	USHORT i, length, nonwild;
	T_PATTERN *p;
	int status = 0;
	char str[PATH_MAX+1];
	FILE *xcld;

	if (! excludeHasChanged)
		return 0;

	if (!(xcld = fopen(excludePath, "r"))) {
		sprintf(conmsg, 
			"I couldn't open the exclude pattern file: error %d.", errno);
		TypeAndSpeak(conmsg);
		return errno;
	}

	/* Release any previous exclude list. */

	for (p = excludeList; p; p = p->next_pattern) {
		free(p->pattern);
		free(p);
	}
	excludeList = lastExclude = NULL;

	while (fgets(str, PATH_MAX, xcld)) {
		if (length = strlen(str)) {
			--length;
			str[length] = '\0';
		}
		if (length && *str != '#') {	/* ignore blank lines and comments */
			nonwild = 0;
			for (i = 0; i < length; ++i) {
				if (str[i] != '*' && str[i] != '?' && str[i] != '/')
					++nonwild;
			}
			if (! nonwild ) {
				sprintf(conmsg,
				"Very funny!  %s will exclude everything!  Ha ha ha!\n",
					str);
				TypeAndSpeak(conmsg);
				status = ERR_ABORT;
				goto done;
			}
			AddExclude(str);
		}
	}
done:
	fclose(xcld);
	if (! status )
		excludeHasChanged = false;
	return status;
}

/* Create a new file information node.
 * Called with:
 *		name:		name of file/directory
 * Returns:
 *		pointer to file node or NULL (out of memory)
 */

T_FILE *
MakeFileNode(name)
	char *name;
{
	T_FILE *fnode;

	if (!(fnode = (T_FILE *) calloc(1,sizeof(T_FILE)))) {
nomem:
		TypeAndSpeak("I have run out of memory!\n");
	}
	else {
		if ( ! (fnode->filename = calloc(1, strlen(name)+1) ) ) 
			goto nomem;
		strcpy(fnode->filename, name);		/* copy the filename */
	}
	return fnode;
}

^L
/* Format a new diskette.
 * Called with:
 *		create_dir:	true => create continuation directory, if necessary
 * Returns:
 *		false => success
 *		true  => failure
 */

int
NewDisk(create_dir)
	int create_dir;
{
	char datestr[20];
	char *diskPrompt;
	int status = 0;

	if (diskNumber)						/* not first disk? */
		Delay(TICKS_PER_SECOND * 3L);	/* let disk buffers flush */

	++diskNumber;						/* Increment the volume ID */
	if (doFormat) {
		Speak("Attention!  I am ready to format a new disk.");
		diskPrompt = "Insert a disk to be formatted in ";
	}
	else {
		Speak("Hi ho!  I am ready for the next backup disk.");
		diskPrompt = "Insert the next backup disk in ";
	}

	do {
		if (doFormat) {
			Inhibit(destDrive, 1);		/* Inhibit disk validation. */
			if (!RequestDisk(mainWindow, destDrive, diskPrompt)) {
				Inhibit(destDrive, 0);	/* Uninhibit the drive. */
				return ERR_ABORT;
			}

		 	/* Don't put the colon in the volume name - 
			 * FormatDisk will happily use it as part of 
			 * the name rather than as a delimiter. 
			 */

			DS2Str(datestr, "%02m-%02d-%02y", now);
			sprintf(destVol,"Backup %s.%d", datestr, diskNumber);

			if (status = FormatDisk(destDrive, destVol)) {
				sprintf(conmsg,
					"I got error %d while formatting.  Sorry about that.\n",
					status);
				TypeAndSpeak(conmsg);
				++errorCount;
				SetErrorGadget();
			}
		}
		else {							/* Don't format disk. */
			if (!RequestDisk(mainWindow, destDrive, diskPrompt)) {
				return ERR_ABORT;
			}
			if (GetVolumeName(destDrive, destVol) && *destVol)
				status = 0;
			else
				status = ERROR_NO_DISK;
		}
	} while (status);

	strcat(destVol, ":");			/* add colon */

	if ( (totalSize = TotalDiskBlocks(destVol)) < 0)
		status = -totalSize;
	else if ((sizeLeft = DiskBlocksLeft(destVol)) < 0)
		status = -sizeLeft;
	else {
		SetGauge(sizeLeft, totalSize);
		SaveContext();
		if (create_dir && (currentDir != NULL) )
			status = NewDir(currentDir->filename);
	}
	if (!status) {
		Header();
		SetCurVolumeGadget(destVol);
	}
	return status;
}
^L
/* Remove a file node from the list.
 * Called with:
 *		node:		file node pointer
 *		list:		file list header
 * Returns:
 *		nothing
 */
RemFile(node,list)
	T_FILE *node; T_FILE_LIST *list;
{
	if (node->previous)
		node->previous->next = node->next;

	if (node->next)
		node->next->previous = node->previous;

	if (node == list->first_file)
		list->first_file = node->next;

	if (node == list->last_file)
		list->last_file = node->previous;

	if (!node->is_dir) FreeFile(node);
}

/* Restore the disk context to what it was when we started this volume. */

int
RestoreContext()
{
	T_FILE *newNode, *oldNode;
	int status = ERR_NONE;

	
	currentDir = NULL;
	DisposeList(&mainList);				/* free up main list */

	for (oldNode = savedList.first_file; oldNode; oldNode = oldNode->next) {
		if (! (newNode = AddFile(oldNode->filename, oldNode->blocks,
				oldNode->is_dir, &mainList))) {
			status = ERROR_NO_FREE_STORE;
			break;
		}

		/* If the old node is the saved current directory, the new node is
		 * then the current directory node.
		 */
		if (oldNode == savedDir)	currentDir = newNode;
	}

	return status;
}

/* Save the context (file list, etc.) of the current volume in case we
 * have an error and must restart.  
 */
int
SaveContext()
{
	T_FILE *newNode, *oldNode;
	int status = ERR_NONE;

	
	savedDir = NULL;
	DisposeList(&savedList);				/* free up old list */

	for (oldNode = mainList.first_file; oldNode; oldNode = oldNode->next) {
		if (! (newNode = AddFile(oldNode->filename, oldNode->blocks,
				oldNode->is_dir, &savedList))) {
			status = ERROR_NO_FREE_STORE;
			break;
		}

		/* If the old node is the current directory, the new node is
		 * then its counterpart.
		 */
		if (oldNode == currentDir) 	savedDir = newNode;
	}
	return status;
}
SHAR_EOF
if test 22792 -ne "`wc -c Backup.c`"
then
echo shar: error transmitting Backup.c '(should have been 22792 characters)'
fi
echo shar: extracting BigFiles.c '(12552 characters)'
cat << \SHAR_EOF > BigFiles.c
/* MRBackup: 	Big File Handler 
 * Date:		12/26/87
 * Description:
 *
 *		This package contains routines which support the backup and 
 * restoration of files which are larger than the capacity of the output 
 * media.
 *
 * Design philosophy:
 *
 *		When a file is encountered whose total size exceeds the capacity
 * of the backup media, MRBackup will attempt to split the file into 
 * pieces, spanning several diskettes, if necessary.  Prior to doing this,
 * a special information file is written (name == BIGINFONAME).  This file
 * contains the following information, written as newline-terminated ASCII
 * strings:
 *
 *		file name:	1..255 characters
 *		file size:	1..NNNNNNNN
 *      sequence:   1..NNN
 *		more:		Y or N (Y => another piece follows this one)
 *
 *		When files are restored, their names are checked agains the
 * "big file" name (if any).  If the big file is encountered, RestoreBigFile
 * is called to restore it.  RestoreBigFile will request each diskette, in
 * sequence, until the full file is restored or an error occurs.
 *
 *		The user must have enabled the "Allow Big Files" and "Format
 * Destination" program options in order for multi-volume backups to occur.
 *
 * History:		(most recent change first)
 *
 */

#include "MRBackup.h"

/* 
 * Copy a "big" file, preserving date and writing the big file information.  
 * Depends upon file date routines in FileMisc.c 
 */

#include <stdio.h>
#include <exec/types.h>
#include <libraries/dos.h>
#include <exec/memory.h>
#include <functions.h>
#include ":src/lib/DiskMisc.h"

#define BIGINFONAME "MRBackup.bigfile"
#define MAXSTR 127
 
extern char fullBackPath[], fullHomePath[];
extern BOOL homeIsDevice;

extern long Chk_Abort();
extern BOOL CopyFileDate();
extern BOOL GetFileDate(), SetFileDate();
extern long IoErr();

struct {
	char 	fileName[PATH_MAX+1];
	ULONG 	size;						/* file size in bytes */
	USHORT 	seqNbr;						/* sequence number (1..n) */
	char	more[4]; 					/* last piece of file? */
	} bigFileInfo;

/* Function:
 *		BackupBigFile
 *
 * Description:
 * 		This routine splits a very large file across a sequence of diskettes.
 * 		On each of these disks, a special information file is written which
 *		specifies the name of the file, its size and its sequence number.
 *
 * Called with:
 *		fileName:	file name (less volume) to be copied
 *
 * Returns:
 *		status, where 0 => successful backup
 */

int     
BackupBigFile(fileName)
	char *fileName;
{
	long count, startCount;
	BOOL done = false;
	char *errMsg = NULL;
	struct FileHandle *fin = NULL, *fout = NULL;
	char from[PATH_MAX+1], to[PATH_MAX+1];
	struct InfoData *info;
	struct FileLock *lock = NULL;
	BOOL needNewDisk;
	long status = 0;

	/* First, check to see if this disk currently has a "big file" on it.
	 * Since we can only record 1 of these per disk, we need to know this.
	 */
	status = GetBigFileInfo(destVol);
	if (status == 0)
		needNewDisk = false;
	else if (status == 1)
		needNewDisk = true;
	else if (status == -1) {
		errMsg = "Error while attempting to get big file information.\n";
		status = ERR_ABORT;
	}
	setmem(&bigFileInfo, sizeof(bigFileInfo), 0);
	strcat(bigFileInfo.fileName, fileName);

	strcpy(from, srcVol);
	strcat(from, fileName);

	if (! (fin = Open(from, MODE_OLDFILE) ) ) {
		status = IoErr();
		goto endit;
	}
	strcpy(bigFileInfo.fileName, fileName);
	startCount = 0;

	while (! (done | status) ) {
		if (needNewDisk)
			if (status = NewDisk(true)) break;

		/* Start new segment by incrementing sequence number. */

		++bigFileInfo.seqNbr;
		bigFileInfo.size = 0;

		/* Write dummy info file.  This assures us that we will have a
		 * block reserved for the big file information file.  Since the
		 * information kept is small, 1 block is all we will need.
		 */

		if (status = PutBigFileInfo(destVol)) continue;
		strcpy(to, destVol);
		strcat(to, fileName);

		if ( !(fout = Open(to, MODE_NEWFILE)) ) {
			status = IoErr();
			sprintf(conmsg,"Failed to open %s for output; status = %ld\n",
					to, status);
			errMsg = conmsg;
			goto endit;
		}

		/* Position to the correct offset in the source file. */

		if (Seek(fin, startCount, OFFSET_BEGINNING) == -1L) {
			status = IoErr();
			errMsg = "BackupBigFile: Seek failed!\n";
			goto endit;
		}

		/* Read blocks of data from the input file and write them to the
		 * output file.  One of the following events will terminate this 
		 * loop:
		 *
		 *		1. End of file on input (count < bufSize)
		 *		2. Input error
		 *		3. Disk full on output (status == ERROR_DISK_FULL)
		 *		4. Other disk write error (non-recoverable).
		 *
		 */

		while ( ! (status || done) ) {
			if (status = CheckStop()) break;
			count = Read( fin, buffer, bufSize );
			if (count < bufSize) done = true;
			if ( count && (Write(fout, buffer, count) != count)) {
				status = IoErr();
				if (status == ERROR_DISK_FULL) {
					status = 0;
				}
				break;
			}
		}

		if (status) break;

		Close(fout);
		fout = NULL;

		if (status = CopyFileDate(from, to)) {
			errMsg = "Failed to set file date!\n";
			break;
		}

		if (! (lock = (struct FileLock *) Lock(to, ACCESS_READ))) {
			sprintf(conmsg,"Could not lock %s\n", to);
			errMsg = conmsg;
			break;
		}
		if (!Examine(lock, fibWork)) {
			status = IoErr();
			sprintf(conmsg,"Could not examine %s\n", to);
			errMsg = conmsg;
			break;
		}
		UnLock(lock);
		lock = NULL;

		startCount += fibWork->fib_Size;
		bigFileInfo.size = fibWork->fib_Size;
		bigFileInfo.more[0] = (done ? 'N' : 'Y');
		if (status = PutBigFileInfo(destVol)) break;
		needNewDisk = !done;			/* Need another disk? */
	}
endit:
	if (lock) UnLock(lock);
	if (fin) Close(fin);
	if (fout) Close(fout);
	if (errMsg) TypeAndSpeak(errMsg);
	return (int) status;
}
^L
/* Function:
 *		GetBigFileInfo
 *
 * Called with:
 *		volume:	name of volume to retrive info from
 *
 * Returns:
 *	  > 0 => sequence number of big file on this disk
 *		0 => no big file info on disk
 *	  < 0 => had trouble reading big file info
 *
 * Description:
 * 		See if the current diskette has "big file" information.
 */
BOOL
GetBigFileInfo(volume)
	char *volume;
{
	char bigFileInfoName[PATH_MAX+1];
	char *errMsg = NULL;
	FILE *f;
	int status = 0;

	MakeBigInfoName(volume, bigFileInfoName);
	if (f = fopen(bigFileInfoName, "r")) {
		if (fgets(bigFileInfo.fileName, PATH_MAX+1, f) == NULL) {
			errMsg = "Couldn't get big file name!\n";
			status = -errno;
		}
		else if (fscanf(f, "%d%ld\n", 
					&bigFileInfo.seqNbr, &bigFileInfo.size) != 2) {
			errMsg = "Couldn't get big file size or sequence number!\n";
			status = -errno;
		}
		else if (fgets(bigFileInfo.more, 3, f) == NULL) {
			errMsg = "Couldn't get big file continuation flag!\n";
			status = -errno;
		}
		else {
			status = bigFileInfo.seqNbr;
			bigFileInfo.fileName[strlen(bigFileInfo.fileName)-1] = '\0';
		}
	}
	if (f) fclose(f);
	if (errMsg) TypeAndSpeak(errMsg);
	return status;
}
^L
/* Function:
 *		ThisIsBigFile
 *
 * Called with:
 *		name:		filename to test
 *
 * Returns:
 *		true  => filename is the name of a "big file"
 *		false => this filename is not that of a "big file"
 *
 * Description:
 *		The <name> is compared against the current big file 
 *		information, if any.  If <name> matches the big file name, 
 *		true is returned, otherwise false is returned.  This routine 
 *		provides a functional interface to the BigFiles package while 
 *		allowing us to hide low-level implementation details.
 */

BOOL
ThisIsBigFile(name)
	char *name;
{
	BOOL result = false;

	if (bigFileInfo.seqNbr && !strcmp(name, bigFileInfo.fileName))
		result = true;
	return result;
}

/* Function:
 *		MakeBigInfoName
 *
 * Called with:
 *		volume:		disk volume name where big file resides
 *		name:		result name (modified)
 *
 * Description:
 *		This routine simply catenates the destination <volume> name with
 *		the root name of the information file where data about the big
 *		file is kept, returning it in <name>.
 */

MakeBigInfoName(volume, name)
	char *volume, *name;
{
	strcpy(name, volume);
	strcat(name, BIGINFONAME);
}
^L
/* Function:
 *		PutBigFileInfo
 *
 * Called with:
 *		volume:		volume name of destination
 *
 * Returns:
 *		status, where 0 => OK
 *
 * Description:
 *		This routine writes a special information file to the destination
 *		disk.  This file describes 
 *			- the name of the file, 
 *			- the sequence number of the chunk that resides on the disk, 
 *			- the size of this chunk
 *			- a flag (Y/N) indicating if more chunks exist
 */
int
PutBigFileInfo(volume)
	char *volume;
{
	FILE *f;
	char bigFileInfoName[PATH_MAX+1];
	int status = 0;

	MakeBigInfoName(volume, bigFileInfoName);

	if (! (f = fopen(bigFileInfoName, "w")) ) {
		status = errno;
		sprintf(conmsg,
				"I couldn't create the big file information block: %s\n", 
				bigFileInfoName);
		TypeAndSpeak(conmsg);
	}
	else {
		fprintf(f, "%s\n%d\n%ld\n%s\n",
				bigFileInfo.fileName,
				bigFileInfo.seqNbr,
				bigFileInfo.size,
				bigFileInfo.more
				);
		fclose(f);
	}
	return status;
}
^L
/* Function:
 *		RestoreBigFile
 *
 * Returns:
 *		status, where 0 => OK
 *
 * Description:
 *		RestoreBigFile is called by Restore() AFTER all other files on the
 *		current diskette have been restored.  This function will reassemble
 *		a "big" file (size > backup media size) onto the home device, 
 *		prompting the user as necessary for subsequent disks.
 */

int
RestoreBigFile()
{
	int compare;					/* date comparison result */
	long count;
	char savedName[PATH_MAX+1];
	BOOL done = false, eof;
	long fileByteCount;
	struct FileHandle *fin, *fout;
	USHORT seqNbr;
	int status = 0;

	strcpy(savedName, bigFileInfo.fileName);

	if ( (seqNbr = bigFileInfo.seqNbr) != 1) {
		TypeAndSpeak(
		"Oops!  RestoreBigFile was called when it shouldn't have been.\n");
		return ERR_ABORT;
	}

	strcpy(fullHomePath, homePath);
	if (!homeIsDevice) strcat(fullHomePath, "/");
	strcat(fullHomePath, savedName);

	strcpy(fullBackPath, srcVol);
	strcat(fullBackPath, savedName);

	compare = CompareFileDates(fullHomePath, fullBackPath);
	if (compare >= 0) {
		sprintf(conmsg,"Skipping %s since home file is current.\n",
				savedName);
		WriteConsole(conmsg);
		return 0;
	}

	if (! (fout = Open(fullHomePath, MODE_NEWFILE))) {
		status = IoErr();
		sprintf(conmsg,"Can't open %s for output!\n", fullHomePath);
		TypeAndSpeak(conmsg);
		return status;
	}

	done = false;
	while (! (done || status) ) {
		sprintf(conmsg, " Restoring chunk %d of %s\n ",
				bigFileInfo.seqNbr, savedName);
		WriteConsole(conmsg);
		if (! (fin = Open(fullBackPath, MODE_OLDFILE))) {
			sprintf(conmsg, "Can't open %s for input!\n", fullBackPath);
			TypeAndSpeak(conmsg);
			break;
		}
		eof = false;
		fileByteCount = 0;
		while (! (eof || status) ) {
			count = Read(fin, buffer, bufSize);
			if (count == -1L) {
				status = IoErr();
				WriteConsole("--ERROR--\n");
			}
			else {
				fileByteCount += count;

	/* The next bit of hackery is necessary since AmigaDOS apparently 
	 * doesn't do a good job of handling file size information on files
	 * which experience DISK_FULL when being written.  An Examine gives
	 * the size as of the last successful Write, while reading to EOF
	 * yields the exact number of bytes that were successfully written.
	 * Thus, we must use the file size that we recorded in the big file
	 * information file.  If this situation gets corrected in KickStart
	 * 1.3 or later, this code should still work correctly.
	 */

				if (fileByteCount > bigFileInfo.size) {
					/* Ignore "excess" data. */
					count -= (fileByteCount - bigFileInfo.size);
				}
				WriteConsole(".");
				if (count < bufSize) {
					WriteConsole("--EOF--\n");
					sprintf(conmsg," %ld bytes in chunk %d\n",
							fileByteCount, seqNbr);
					WriteConsole(conmsg);
					eof = true;
					Close(fin);
					fin = NULL;
				}
				if (count && ( Write(fout, buffer, count) != count) )
					status = IoErr();
			}
		}
		if (status) break;

		if (toupper(bigFileInfo.more[0]) == 'Y') {
			++seqNbr;
			/* Request the next floppy disk. */
			while (bigFileInfo.seqNbr != seqNbr) {
				sprintf(conmsg, "I need the disk for chunk %d of %s\n",
						seqNbr, savedName);
				TypeAndSpeak(conmsg);
				if (!RequestDisk(
						mainWindow, srcVol, "Insert next disk in ")) {
					status = ERR_ABORT;
					break;
				}
				else {
					if ((status = GetBigFileInfo(srcVol)) == seqNbr &&
						ThisIsBigFile(savedName))
						break;
				}
			}
		}
		else
			done = true;
	}
	if (fout) Close(fout);
	if (fin) Close(fin);
	if (!status) CopyFileDate(fullBackPath, fullHomePath);
	return status;
}
SHAR_EOF
if test 12552 -ne "`wc -c BigFiles.c`"
then
echo shar: error transmitting BigFiles.c '(should have been 12552 characters)'
fi
echo shar: extracting Compress.c '(21789 characters)'
cat << \SHAR_EOF > Compress.c
/* MRBackup: File Compression/Decompression Routines
 * Filename:	Compress.c
 * History:		(most recent change first)
 *
 * 09/19/87 -MRR- Fixed bugs in decompression routine, introduced by
 *                new buffering scheme.
 *
 * 09/04/87 -MRR- This package now uses MRBackup's buffer to hold the
 *                compressed data and uses AmigaDOS I/O.
 */

/* 
 * Compress.c - data compression/decompression routines.
 * This is an adaptation of the public domain Un*x compress v4.0.
 */

#include "MRBackup.h"

#define	min(a,b)	((a>b) ? b : a)
#define INBUFSIZE	4L*1024L		/* input buffer size */

#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)
 */

static unsigned debug = 0;			/* true => execute debug code */
static unsigned endInput;			/* true => input file exhausted */
static int n_bits;					/* number of bits/code */
static int maxbits = BITS;			/* user settable max # bits/code */
static code_int maxcode;			/* maximum code, given n_bits */
static code_int maxmaxcode = 1 << BITS;	/* NEVER generate this code */
#define MAXCODE(n_bits)	((1 << (n_bits)) - 1)

static count_int htab [HSIZE];
static USHORT codetab [HSIZE];
#define htabof(i)		htab[i]
#define codetabof(i)	codetab[i]

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

static struct FileHandle *infile, *outfile;
static char iname[256], oname[256];
static UBYTE inbuf[INBUFSIZE], *inbufptr;
static LONG inbufsize;
static UBYTE *outbufptr;

/*
 * 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))

static code_int free_ent = 0;		/* first unused entry */
static int status = 0;

code_int getcode();

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

/*
 * block compression parameters -- after all codes are used up,
 * and compression rate changes, start over.
 */
static int block_compress = BLOCK_MASK;
static int clear_flg = 0;
static long int ratio = 0;
#define CHECK_GAP 10000				/* ratio check interval */
static 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 */

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

static int do_decomp = 0;
static int offset;
static LONG in_count;				/* length of input */
static LONG bytes_out;				/* length of compressed output */
static LONG out_count;				/* # of codes output (for debugging) */
static LONG outbufcount;			/* # bytes in output buffer */
^L
/*****************************************************************
 *
 * Algorithm from "A Technique for High Performance Data Compression",
 * Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19.
 *
 * 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.
 */

static void
comp_init()
{
	if(maxbits < INIT_BITS)
		maxbits = INIT_BITS;
	if (maxbits > BITS)
		maxbits = BITS;
	maxmaxcode = 1 << maxbits;
	outbufcount = 0;
	outbufptr = buffer;
}


/*
 * 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;
	char *s;
	char *err_msg[256];

	comp_init();
	inbufsize = 0;
	endInput = false;

	/* 
	 * 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 = Open(from, MODE_OLDFILE))) {
		return IoErr();
	}

	if (!(outfile = Open(to, MODE_NEWFILE))) {
		status = IoErr();
		Close(infile);
		return status;
	}

	if (nomagic == 0){
		putcbuf(magic_header[0]);
		putcbuf(magic_header[1]);
		putcbuf((char)(maxbits | block_compress));
		if(status) {
cleanup:
			Close(infile);
			Close(outfile);
			return status;
		}
	}

	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 = ReadChar();

	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 ){*/
	while ( ( c = ReadChar() ) != 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 );
#endif								/* DEBUG */
	}
	flushbuf();						/* dump the last block */
	if(bytes_out > in_count)		/* exit(2) if no savings */
		status = 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;
	int count;

#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
				putcbuf(*bp++);
			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 ){
				putmcbuf(buf, n_bits);
				if (status) 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.
	 */
	 	count = (offset + 7) / 8;
		if (offset > 0 ) {
			putmcbuf(buf, count);
		}
		bytes_out += count;
		offset = 0;

#ifdef DEBUG
		if (verbose )
			fprintf(stderr,"\n" );
#endif								/* DEBUG */

		if(status) 
			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 c1, c2;

	outbufcount = 0;
	outbufptr = buffer;
	inbufsize = 0;
	endInput = false;

	if (!(infile = Open(from, MODE_OLDFILE))) {
		status = IoErr();
		return status;
	}
	if (!(outfile = Open(to, MODE_NEWFILE))) {
		status = IoErr();
		Close(infile);
		return status;
	}

	if (nomagic == 0){
		if (((c1 = ReadChar()) != (magic_header[0] & 0xFF)) || 
		    ((c2 = ReadChar()) != (magic_header[1] & 0xFF))) {
			status = -2;			/* unknown format */
			sprintf(conmsg, "Bad header? c1/c2 == %02x/%02x\n",c1,c2);
			WriteConsole(conmsg);
cleanup:
			Close(infile);
			Close(outfile);
			return status;
		}
		maxbits = ReadChar();		/* set -b from file */
		block_compress = maxbits & BLOCK_MASK;
		maxbits &= BIT_MASK;
		maxmaxcode = 1 << maxbits;
		if(maxbits > BITS){
			status = -3;			/* too many bits to handle */
			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 ( status )					/* EOF already? */
		goto cleanup;

	putcbuf(finchar);				/* first code must be 8 bits = char */
	if(status)						/* Crash if can't write */
		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
			putcbuf(*--stackp);
		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;
	}
	flushbuf();
	goto cleanup;
}

/*****************************************************************
 * 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;
	int c;

	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 = 0;
		while (size < n_bits) {
			if ((c = ReadChar()) == EOF || status ) break;
			buf[size++] = c;
		}
		if (size == 0  || status) {
			return EOF;				/* 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;
}

#ifndef AZTEC_C
char *
rindex(s,c)
	register char *s,c;
{
	char   *p;
	for (p = NULL;*s;s++)
		if (*s == c)
			p = s;
	return(p);
}
#endif

/*
 * 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 );
}

/* wild pointer -- assume bad input */

oops ()
{							
	if ( do_decomp == 1 )
		fprintf (stderr,"uncompress: corrupt input\n" );
	unlink ( ofname );
	exit ( 1 );
}

/* table clear for block compress */

cl_block ()
{						
	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;
		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;
}
#ifdef DEBUG
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);
}
#endif

/* Flush the output buffer. */

flushbuf()
{
	if (Write(outfile, buffer, outbufcount) != outbufcount) {
		status = IoErr();
	}
	outbufcount = 0;
	outbufptr = buffer;
}

/* Put a character into the output buffer.  If the buffer is full,
 * output the buffer, then reset its count to zero.
 * Called with:
 *		c:		character to be output
 * Returns:
 *		status code (0 => OK)
 */

int
putcbuf(c)
{
	if (outbufcount >= bufSize) 	flushbuf();
	*outbufptr++ = c;
	++outbufcount;
	return status;
}

/* Put multiple characters in the output buffer.
 * Called with:
 *		buf:		address of character array
 *		count:		number of characters to output
 * Returns:
 *		status 
 */

putmcbuf(buf, count)
	char *buf; int count;
{
	for (; count; --count) 	if ( putcbuf( *buf++ ) ) break;
	return status;
}

/* Read 1 character from the input file.
 * Returns:
 *   character code or -1 (EOF)
 */

int
ReadChar()
{
again:
	if (inbufsize == 0) {
		if (endInput)
			return EOF;

		if ((inbufsize = Read(infile, inbuf, INBUFSIZE)) == -1L) {
			status = IoErr();
			return EOF;
		}

		inbufptr = inbuf;				/* reset buffer pointer */

		if (inbufsize < INBUFSIZE) {
			endInput = true;
			goto again;
		}
	}
	++in_count;
	--inbufsize;
	return (*inbufptr++);
}
SHAR_EOF
if test 21789 -ne "`wc -c Compress.c`"
then
echo shar: error transmitting Compress.c '(should have been 21789 characters)'
fi
#	End of shell archive
exit 0

ain@j.cc.purdue.edu (Patrick White) (01/29/88)

Program Name:	MRBackup
Submitted By:	uiucdcs!amanpt1.ZONE1.COM!mrr@ee.ecn.purdue.edu (Mark Rinfret)
Summary:	A hard disk backup utility.
Poster Boy:  Pat White  (ain@j.cc.purdue.edu)
Untested.

NOTES:
   I reshar'ed all of the source to get rid of some explicit path references
in where the files get put.  Now it will unshar on unix and the Amiga into
the current directory.
   I did *not* change the explicit path references to include files in the
.c files.
   I also included the docs that were sent with the binary version, so these
4 parts are complete source with docs.


-- Pat White   (co-moderator comp.sources/binaries.amiga)
UUCP: j.cc.purdue.edu!ain  BITNET: PATWHITE@PURCCVM   PHONE: (317) 743-8421
U.S.  Mail:  320 Brown St. apt. 406,    West Lafayette, IN 47906

========================================


#	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
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	Console.c
#	CopyFile.c
#	DateRequest.c
#	DiskMisc.c
#	Error.c
#	FileMisc.c
#	FormatDisk.c
# This archive created: Thu Jan 28 11:10:10 1988
# By:	Patrick White (PUCC Land, USA)
echo shar: extracting Console.c '(3138 characters)'
cat << \SHAR_EOF > Console.c
/* MRBackup Console Routines
 * Filename:	Console.c
 * Date:		11/22/87
 *
 * Console I/O Routines from Rob Peck's "Programmer's Guide to the Amiga".
 */

#include "MRBackup.h"

ConIOBlocks *
CreateConsole(window)
	struct Window *window;
{
	ConIOBlocks *c;
	struct MsgPort *tpw;

	if (!(c = AllocMem((long) sizeof(ConIOBlocks), 
						MEMF_CLEAR | MEMF_PUBLIC)))
		goto cleanup1;

	if (!(tpw = CreatePort(0L, 0L)))
		goto cleanup2;

	if (!(c->tpr = CreatePort(0L, 0L)))
		goto cleanup3;

	if (!(c->writeReq = CreateStdIO(tpw)))
		goto cleanup4;

	if (!(c->readReq = CreateStdIO(c->tpr)))
		goto cleanup5;

	c->writeReq->io_Data = (APTR)window;
	c->writeReq->io_Length = sizeof(struct Window);

	if (OpenDevice("console.device", 0L, c->writeReq, 0L))
		goto cleanup6;

	/* Read and write are both talking to the same instance of a console. */
	c->readReq->io_Device = c->writeReq->io_Device;
	c->readReq->io_Unit   = c->writeReq->io_Unit;
	return c;

cleanup6:
	DeleteStdIO(c->readReq);
cleanup5:
	DeletePort(c->tpr);
cleanup4:
	DeleteStdIO(c->writeReq);
cleanup3:
	DeletePort(tpw);
cleanup2:
	FreeMem(c, (long) sizeof(ConIOBlocks));
cleanup1:
	return NULL;
}

int
DeleteConsole(c)
	ConIOBlocks *c;
{
	struct MsgPort *mp;

	AbortIO(c->readReq);		/* abort any read in progress */
	CloseDevice(c->writeReq);	/* close the console device */

	mp = c->writeReq->io_Message.mn_ReplyPort;

	DeleteStdIO(c->writeReq);
	DeletePort(mp);

	mp = c->readReq->io_Message.mn_ReplyPort;

	DeleteStdIO(c->readReq);
	DeletePort(mp);

	FreeMem(c, (long) sizeof(ConIOBlocks));
	return 0;
}

#define CONREAD c->readReq
#define CONWRITE c->writeReq

void
EnqueueRead(c, location)
	ConIOBlocks *c; char *location;
{
	struct IOStdReq *conr;

	conr = CONREAD;
	conr->io_Command = CMD_READ;
	conr->io_Length = 1;
	conr->io_Data = (APTR) location;
	SendIO(conr);				/* asynchronous read request */
}

/* Write a specified number of characters from a buffer to a particular
 * console device. 
 */

void
ConWrite(c, data, length)
	ConIOBlocks *c; char *data; WORD length;
{
	struct IOStdReq *conw;

	if (length == 0) length = strlen(data);
	conw = CONWRITE;
	conw->io_Command = CMD_WRITE;
	conw->io_Length = length;
	conw->io_Data = (APTR) data;
	DoIO(conw);					/* synchronous write request */
}

/* Get a character from the console, with optional wait.
 * Called with:
 *		c:		console control structure
 *		wait:	0 => asynchronous read (no wait)
 *				1 => synchronous read (wait)
 * Returns:
 *		character or -1 (async and no character)
 */
int
CGetCharacter(c, wait)
	ConIOBlocks *c; BOOL wait;
{
	struct MsgPort *mp;
	struct IOStdReq *conr;
	char *dataAddr;
	int temp;

	mp = c->tpr;
	if (wait) {
		WaitPort(mp);
	}
	conr = (struct IOStdReq *) GetMsg(mp);
	if (conr == NULL)
		return -1;
	else {
		dataAddr = (char *)conr->io_Data;
		temp = *dataAddr;			/* get the value */
		EnqueueRead(c, dataAddr);	/* continue the read */
		return temp;
	}
}

#ifdef DEBUG
DebugWrite(msg)
	char *msg;
{
	ConWrite(debugConsole, msg, strlen(msg));
}
#endif

/* Write string to progress window. */

WriteConsole(msg)
	char *msg;
{
	ConWrite(progressConsole, msg, strlen(msg));
}
SHAR_EOF
if test 3138 -ne "`wc -c Console.c`"
then
echo shar: error transmitting Console.c '(should have been 3138 characters)'
fi
echo shar: extracting CopyFile.c '(2405 characters)'
cat << \SHAR_EOF > CopyFile.c
/* MRBackup: File Copy Routine.
 * Date:		09/04/87
 * Notes:
 *		To enhance the performance of MRBackup, this package was copied
 * from my Misc library and modified to couple it more tightly with
 * MRBackup.  It now uses a global buffer area allocated during
 * initialization.
 *
 * History:		(most recent change first)
 *
 * 09/04/87 -MRR- CopyFile, upon failure, now returns the error status
 *                code from IoErr().
 */

#include "MRBackup.h"

/* 
 * Copy file, preserving date.  
 * Depends upon file date routines in FileMisc.c 
 * Original author: Jeff Lydiatt, Vancouver, Canada
 */

#include <stdio.h>
#include <exec/types.h>
#include <libraries/dos.h>
#include <exec/memory.h>
#include <functions.h>

#define MAXSTR 127
 
extern long Chk_Abort();
extern BOOL GetFileDate(), SetFileDate();
extern long IoErr();

/* Copy the last modified date from one file to another.
 * Called with:
 *		from:		name of source file
 *		to:			name of destination file
 * Returns:
 *		0 => success, 1 => failure
 * Note:
 *		Dynamic memory allocation of the DateStamp struction is
 *		necessary to insure longword alignment.
 */

BOOL 
CopyFileDate(from,to)
	char *from, *to;
{
	struct DateStamp *date;
	int status = 1;				/* default is fail code */

	if (date = (struct DateStamp *) 
		AllocMem((long) sizeof(struct DateStamp), MEMF_PUBLIC)) {
		if (GetFileDate(from,date))
			if (SetFileDate(to,date))
				status = 0;
		FreeMem(date, (long) sizeof(struct DateStamp));
	}
	return status;
}

int     
CopyFile(from, to)
	char *from, *to;
{
	long status, count;
	struct FileHandle *fin = NULL, *fout = NULL;

#ifdef DEBUG
	char	errmsg[256];
	static char *errfmt = "CopyFile I/O error %ld, file %s\n";
#endif

	if (! (fin = Open(from, MODE_OLDFILE) ) ) {
badcopy:
		status = IoErr();
#ifdef DEBUG
		sprintf(errmsg, errfmt, status, from);
print_err:
		DebugWrite(errmsg);
#endif
		if (fin) Close(fin);
		if (fout) {
			Close(fout);
			unlink(to);				/* delete the bad copy */
		}
		return (int) status;
	}

	if ( !(fout = Open(to, MODE_NEWFILE)) ) goto badcopy;

	status = 0;
	while ( !status && (count = Read( fin, buffer, bufSize )) == bufSize )
		if ( Write(fout, buffer, count) != count)
				status = IoErr();

	if (!status && count > 0 ) {
		if (Write(fout, buffer, count) != count)
			status = IoErr();
	}

	if (status) goto badcopy;

	Close(fin);
	Close(fout);

	return CopyFileDate(from, to);
}
SHAR_EOF
if test 2405 -ne "`wc -c CopyFile.c`"
then
echo shar: error transmitting CopyFile.c '(should have been 2405 characters)'
fi
echo shar: extracting DateRequest.c '(21886 characters)'
cat << \SHAR_EOF > DateRequest.c
/* Date Requester Package
 * Author:		Mark R. Rinfret
 * Description:
 *		This source file contains a canned date requester routine which
 * supports time and date entry in MM/DD/YY HH:MM:SS notation (time is
 * optional) to a DateStamp structure.  This package is dependent upon
 * the package "MRDates.c" (same author) for DateStamp/string conversions.
 *
 * History:		(most recent change first)
 *
 * 08/18/87 -MRR- Added up/down arrows to gadgetry
 * 07/20/87 -MRR- Created this file.
 */


#include <intuition/intuition.h> 
#include <intuition/intuitionbase.h> 
#include <libraries/dosextens.h> 
#include <graphics/gfxbase.h> 
#include <graphics/gfx.h> 
#include <graphics/display.h> 

#include <graphics/text.h> 
#include <functions.h>

#include <ctype.h> 
#include <:src/lib/MRDates.h>

#define DATEREQWIDTH	260
#define DATEREQHEIGHT	80

/**********************************************************************
 *                    Gadget Structure Definitions
 * 
 * Some of the following structures were defined using EGAD! 
 * (by the Programmer's Network) and further modified by the author.
 * 
 **********************************************************************/

/*  Gadget ID Definitions */

#define DATEGAD 		0
#define UPMONTHGAD		1
#define UPDAYGAD		2	
#define UPYEARGAD		3
#define UPHOURGAD		4
#define UPMINGAD		5
#define UPSECGAD		6
#define DOWNMONTHGAD	7
#define DOWNDAYGAD		8
#define DOWNYEARGAD		9
#define DOWNHOURGAD		10
#define DOWNMINGAD		11
#define DOWNSECGAD		12
#define NOWGAD			13
#define OKGAD			14

/**********************************************************************
 *  Text attribute structures used in rendering IntuiTexts
 **********************************************************************/


static char def_font[] ="topaz.font";

static struct TextAttr TxtAt_Plain = { 
	(UBYTE *)def_font, 8, FS_NORMAL, FPF_ROMFONT
	};




/**********************************************************************
 *  String information for the dategad string gadget.
 **********************************************************************/

static UBYTE dategad_sbuf_1[19] = "00/00/00 00:00:00";
static UBYTE dategad_ubuf_1[19];

static struct StringInfo dategad_txstr_1 = {
  dategad_sbuf_1, dategad_ubuf_1, 	/* Buffer, UndoBuffer  */
  0, 18, 0,  						/* BufferPos, MaxChars, DispPos   */
  0, 18,      						/* UndoPos, NumChars */
  0, 0, 0,  						/* DispCount, CLeft, CTop */
  0x0, 0,   						/* LayerPtr, LongInt */
  0x0        						/* AltKeyMap */
};


/**********************************************************************
 *  Gadget Structure definition for the dategad gadget.
 **********************************************************************/

static struct Gadget dategad = {
  NULL,     /* NextGadget pointer */
  64, 50,    /* LeftEdge, TopEdge  */
  8 * 17,						/* Width (8 pixels * 17 characters) */
  8,    						/* Height      */
   GADGHCOMP, 					/* Gadget Flags */
	RELVERIFY , 				/* Activation Flags */
  REQGADGET | STRGADGET, 		/* GadgetType */
	NULL,						/* GadgetRender - no border */
  NULL,    						/* SelectRender */
   NULL,    					/* GadgetText */
  0x0,    						/* MutualExclude */
  (APTR)&dategad_txstr_1,   	/* SpecialInfo */
  DATEGAD,    					/* GadgetID */
  NULL							/* UserData Pointer */
};

static struct IntuiText nowtext = {
	1, 0,						/* FrontPen / BackPen */
	JAM2,						/* DrawMode */
	0,							/* LeftEdge */
	0,							/* TopEdge */
	&TxtAt_Plain,				/* ITextFont */
	(UBYTE *) "NOW",			/* IText */
	NULL						/* NextText */
	};

static struct Gadget nowgad = {
  &dategad,	     				/* NextGadget pointer */
  220, 50,    					/* LeftEdge, TopEdge  */
  24, 8,    					/* Width, Height      */
   GADGHCOMP,					/* Gadget Flags */
	RELVERIFY , 				/* Activation Flags */
  REQGADGET | BOOLGADGET,		/* GadgetType */
  NULL,							/* GadgetRender */
  NULL,							/* SelectRender */
  &nowtext,						/* GadgetText */
  0x0,    						/* MutualExclude */
  NULL,   						/* SpecialInfo */
  NOWGAD,  						/* GadgetID */
  NULL							/* UserData Pointer */
};

static struct IntuiText oktext = {
	1, 0,						/* FrontPen / BackPen */
	JAM2,						/* DrawMode */
	0,							/* LeftEdge */
	0,							/* TopEdge */
	&TxtAt_Plain,				/* ITextFont */
	(UBYTE *) "OK",						/* IText */
	NULL						/* NextText */
	};
		
static struct Gadget okgad = {
  &nowgad,	     				/* NextGadget pointer */
  30, 50,    					/* LeftEdge, TopEdge  */
  16, 8,    					/* Width, Height      */
   GADGHNONE,					/* Gadget Flags */
	RELVERIFY , 				/* Activation Flags */
  REQGADGET | BOOLGADGET,		/* GadgetType */
  NULL,							/* GadgetRender */
  NULL,							/* SelectRender */
   &oktext,    					/* GadgetText */
  0x0,    						/* MutualExclude */
  NULL,   						/* SpecialInfo */
  OKGAD,   						/* GadgetID */
  NULL							/* UserData Pointer */
};

/* These coordinate pairs define the border of the up-arrow gadgets. */

static SHORT upborder_pairs[] = {
	7, 0,
	14, 7,
	0, 7,
	7, 0
	};


/* The up-arrow border definition. */

static struct Border upborder = {
	-1, -1,	/* LeftEdge, TopEdge */
	1, 2,   /* FrontPen, BackPen */
	JAM1,	/* DrawMode */
	4, 		/* number of pairs */
	(SHORT *) &upborder_pairs,
	NULL	/* NextBorder */
	};


/* These coordinate pairs define the border of the down-arrow gadgets. */

static SHORT downborder_pairs[] = {
	0, 0,
	14, 0,
	7, 7,
	0, 0
	};

/* The down-arrow border definition. */

static struct Border downborder = {
	-1, -1,	/* LeftEdge, TopEdge */
	1, 2,   /* FrontPen, BackPen */
	JAM1,	/* DrawMode */
	4, 		/* number of pairs */
	(SHORT *) &downborder_pairs,
	NULL	/* NextBorder */
	};


/**********************************************************************
 *  Gadget Structure definition for the upsec gadget.
 **********************************************************************/

/* Each component of the date and time can be incremented/decremented
 * by clicking on the appropriate up or down arrow gadgets.
 */

static struct Gadget downsecgad = {
  &okgad,     					/* NextGadget pointer */
  184, 60,    					/* LeftEdge, TopEdge  */
  15, 8,    					/* Width, Height      */
   NULL,						/* Gadget Flags */
	RELVERIFY , 				/* Activation Flags */
  REQGADGET | BOOLGADGET,		/* GadgetType */
  (APTR) &downborder,			/* GadgetRender */
  NULL,							/* SelectRender */
   NULL,    					/* GadgetText */
  0x0,    						/* MutualExclude */
  NULL,   						/* SpecialInfo */
  DOWNSECGAD,   				/* GadgetID */
  NULL							/* UserData Pointer */
};

/**********************************************************************
 *  Gadget Structure definition for the upmin gadget.
 **********************************************************************/

static struct Gadget downmingad = {
  &downsecgad,     				/* NextGadget pointer */
  160, 60,    					/* LeftEdge, TopEdge  */
  15, 8,    					/* Width, Height      */
   NULL,					/* Gadget Flags */
	RELVERIFY , 				/* Activation Flags */
  REQGADGET | BOOLGADGET,		/* GadgetType */
  (APTR) &downborder,			    /* GadgetRender */
  NULL,							/* SelectRender */
   NULL,    					/* GadgetText */
  0x0,    						/* MutualExclude */
  NULL,   						/* SpecialInfo */
  DOWNMINGAD,   					/* GadgetID */
  NULL							/* UserData Pointer */
};

/**********************************************************************
 *  Gadget Structure definition for the uphour gadget.
 **********************************************************************/

static struct Gadget downhourgad = {
  &downmingad,     				/* NextGadget pointer */
  136, 60,    					/* LeftEdge, TopEdge  */
  15, 8,    					/* Width, Height      */
   NULL,					/* Gadget Flags */
	RELVERIFY , 				/* Activation Flags */
  REQGADGET | BOOLGADGET,		/* GadgetType */
  (APTR) &downborder,			    /* GadgetRender */
  NULL,							/* SelectRender */
   NULL,    					/* GadgetText */
  0x0,    						/* MutualExclude */
  NULL,   						/* SpecialInfo */
  DOWNHOURGAD,   					/* GadgetID */
  NULL							/* UserData Pointer */
};

/**********************************************************************
 *  Gadget Structure definition for the upyear gadget.
 **********************************************************************/

static struct Gadget downyeargad = {
  &downhourgad,     				/* NextGadget pointer */
  112, 60,    					/* LeftEdge, TopEdge  */
  15, 8,    					/* Width, Height      */
   NULL,					/* Gadget Flags */
	RELVERIFY , 				/* Activation Flags */
  REQGADGET | BOOLGADGET,		/* GadgetType */
  (APTR) &downborder,			    /* GadgetRender */
  NULL,							/* SelectRender */
   NULL,    					/* GadgetText */
  0x0,    						/* MutualExclude */
  NULL,   						/* SpecialInfo */
  DOWNYEARGAD,   					/* GadgetID */
  NULL							/* UserData Pointer */
};

/**********************************************************************
 *  Gadget Structure definition for the upday gadget.
 **********************************************************************/

static struct Gadget downdaygad = {
  &downyeargad,     				/* NextGadget pointer */
  88, 60,    					/* LeftEdge, TopEdge  */
  15, 8,    					/* Width, Height      */
   NULL,					/* Gadget Flags */
	RELVERIFY , 				/* Activation Flags */
  REQGADGET | BOOLGADGET,		/* GadgetType */
  (APTR) &downborder,			    /* GadgetRender */
  NULL,							/* SelectRender */
   NULL,    					/* GadgetText */
  0x0,    						/* MutualExclude */
  NULL,   						/* SpecialInfo */
  DOWNDAYGAD,   					/* GadgetID */
  NULL							/* UserData Pointer */
};

/**********************************************************************
 *  Gadget Structure definition for the upmonth gadget.
 **********************************************************************/

static struct Gadget downmonthgad = {
  &downdaygad,     /* NextGadget pointer */
  64, 60,    /* LeftEdge, TopEdge  */
  15, 8,    /* Width, Height      */
  /* Gadget Flags */
   NULL,
	RELVERIFY , 				/* Activation Flags */
  REQGADGET | BOOLGADGET,		/* GadgetType */
  (APTR) &downborder,			    /* GadgetRender */
  NULL,							/* SelectRender */
   NULL,    					/* GadgetText */
  0x0,    						/* MutualExclude */
  NULL,   						/* SpecialInfo */
  DOWNMONTHGAD,   					/* GadgetID */
  NULL							/* UserData Pointer */
};

/**********************************************************************
 *  Gadget Structure definition for the upsec gadget.
 **********************************************************************/

static struct Gadget upsecgad = {
  &downmonthgad,    				/* NextGadget pointer */
  184, 40,    					/* LeftEdge, TopEdge  */
  15, 8,    					/* Width, Height      */
   NULL,					/* Gadget Flags */
	RELVERIFY , 				/* Activation Flags */
  REQGADGET | BOOLGADGET,		/* GadgetType */
  (APTR) &upborder,			    /* GadgetRender */
  NULL,							/* SelectRender */
   NULL,    					/* GadgetText */
  0x0,    						/* MutualExclude */
  NULL,   						/* SpecialInfo */
  UPSECGAD,   					/* GadgetID */
  NULL							/* UserData Pointer */
};

/**********************************************************************
 *  Gadget Structure definition for the upmin gadget.
 **********************************************************************/

static struct Gadget upmingad = {
  &upsecgad,     				/* NextGadget pointer */
  160, 40,    					/* LeftEdge, TopEdge  */
  15, 8,    					/* Width, Height      */
   NULL,					/* Gadget Flags */
	RELVERIFY , 				/* Activation Flags */
  REQGADGET | BOOLGADGET,		/* GadgetType */
  (APTR) &upborder,			    /* GadgetRender */
  NULL,							/* SelectRender */
   NULL,    					/* GadgetText */
  0x0,    						/* MutualExclude */
  NULL,   						/* SpecialInfo */
  UPMINGAD,   					/* GadgetID */
  NULL							/* UserData Pointer */
};
/**********************************************************************
 *  Gadget Structure definition for the uphour gadget.
 **********************************************************************/

static struct Gadget uphourgad = {
  &upmingad,     				/* NextGadget pointer */
  136, 40,    					/* LeftEdge, TopEdge  */
  15, 8,    					/* Width, Height      */
   NULL,					/* Gadget Flags */
	RELVERIFY , 				/* Activation Flags */
  REQGADGET | BOOLGADGET,		/* GadgetType */
  (APTR) &upborder,			    /* GadgetRender */
  NULL,							/* SelectRender */
   NULL,    					/* GadgetText */
  0x0,    						/* MutualExclude */
  NULL,   						/* SpecialInfo */
  UPHOURGAD,   					/* GadgetID */
  NULL							/* UserData Pointer */
};

/**********************************************************************
 *  Gadget Structure definition for the upyear gadget.
 **********************************************************************/

static struct Gadget upyeargad = {
  &uphourgad,     				/* NextGadget pointer */
  112, 40,    					/* LeftEdge, TopEdge  */
  15, 8,    					/* Width, Height      */
   NULL,					/* Gadget Flags */
	RELVERIFY , 				/* Activation Flags */
  REQGADGET | BOOLGADGET,		/* GadgetType */
  (APTR) &upborder,			    /* GadgetRender */
  NULL,							/* SelectRender */
   NULL,    					/* GadgetText */
  0x0,    						/* MutualExclude */
  NULL,   						/* SpecialInfo */
  UPYEARGAD,   					/* GadgetID */
  NULL							/* UserData Pointer */
};

/**********************************************************************
 *  Gadget Structure definition for the upday gadget.
 **********************************************************************/

static struct Gadget updaygad = {
  &upyeargad,     				/* NextGadget pointer */
  88, 40,    					/* LeftEdge, TopEdge  */
  15, 8,    					/* Width, Height      */
   NULL,					/* Gadget Flags */
	RELVERIFY , 				/* Activation Flags */
  REQGADGET | BOOLGADGET,		/* GadgetType */
  (APTR) &upborder,			    /* GadgetRender */
  NULL,							/* SelectRender */
   NULL,    					/* GadgetText */
  0x0,    						/* MutualExclude */
  NULL,   						/* SpecialInfo */
  UPDAYGAD,   					/* GadgetID */
  NULL							/* UserData Pointer */
};

/**********************************************************************
 *  Gadget Structure definition for the upmonth gadget.
 **********************************************************************/

static struct Gadget upmonthgad = {
  &updaygad,     /* NextGadget pointer */
  64, 40,    /* LeftEdge, TopEdge  */
  15, 8,    /* Width, Height      */
  /* Gadget Flags */
   NULL,
	RELVERIFY , 				/* Activation Flags */
  REQGADGET | BOOLGADGET,		/* GadgetType */
  (APTR) &upborder,			    /* GadgetRender */
  NULL,							/* SelectRender */
   NULL,    					/* GadgetText */
  0x0,    						/* MutualExclude */
  NULL,   						/* SpecialInfo */
  UPMONTHGAD,   					/* GadgetID */
  NULL							/* UserData Pointer */
};

static struct Requester daterequest;	/* requester for the date */

/* Date requester border 2 coordinate pairs. */

static SHORT datereq_pairs2[] = {
	8, 4, 
	DATEREQWIDTH - 8, 4, 
	DATEREQWIDTH - 8, DATEREQHEIGHT - 4,
	8, DATEREQHEIGHT - 4,
	8, 4
};

/* Date requester border 2 definition */

static struct Border datereq_bord2 = {
  -1,  -1,       /* LeftEdge, TopEdge */
  0,  2,  JAM2,  /* FrontPen, BackPen, DrawMode*/
  5,             /* Count of XY pairs */  
  (SHORT *)&datereq_pairs2, /* XY pairs */
  NULL           /* Next Border */
};

/* Date requester border 1 coordinate pairs. */

static SHORT datereq_pairs1[] = {
	4, 2, 
	DATEREQWIDTH - 4, 2, 
	DATEREQWIDTH - 4, DATEREQHEIGHT - 2,
	4, DATEREQHEIGHT - 2,
	4, 2
};

/* Date requester border 1 definition. */

static struct Border datereq_bord1 = {
  -1,  -1,       /* LeftEdge, TopEdge */
  0,  2,  JAM2,  /* FrontPen, BackPen, DrawMode*/
  5,             /* Count of XY pairs */  
  (SHORT *)&datereq_pairs1, /* XY pairs */
  &datereq_bord2 /* Next Border */
};

/* This is the definition for the prompt string (user-supplied) which
 * appears in the requester.
 */

static struct IntuiText datereqtext = {
	1, 0,						/* FrontPen / BackPen */
	JAM2,						/* DrawMode */
	20,							/* LeftEdge */
	15,							/* TopEdge */
	&TxtAt_Plain,				/* ITextFont */
	NULL,						/* IText */
	NULL						/* NextText */
	};

static initialized = 0;			/* set to 1 after initialization */

static InitDateRequest()
{
	InitRequester(&daterequest);
	daterequest.LeftEdge = 60;
	daterequest.TopEdge = 10;
	daterequest.Width = DATEREQWIDTH;
	daterequest.Height = DATEREQHEIGHT;
	daterequest.ReqGadget = &upmonthgad;
	daterequest.ReqText = &datereqtext;
	daterequest.ReqBorder = &datereq_bord1;
	daterequest.BackFill = 2;
	++initialized;
}

/* Request a date from the user.
 * Called with:
 *		window:			pointer to window structure
 *		prompt:			string to be used as the prompt text
 *		default_date:	pointer to default date or NULL
 *		date:			pointer to date result (returned)
 * Returns:
 *		status code:	0 => success, 1 => failure
 *		(actually, always returns 0 - can't leave without good date)
 */

DateRequest(window,prompt,default_date,date)
	struct Window *window; char *prompt; 
	struct DateStamp *default_date,*date;
{
	ULONG class;				/* message class */
	USHORT code;				/* message code */
	struct Gadget *gadget;		/* pointer to gadget affected */
	USHORT gadgid;				/* gadget ID */
	struct IntuiMessage *msg;	/* Intuition message pointer */
	UnpackedDS upd;				/* unpacked version of date */
	SHORT x,y;					/* mouse x and y position */

	if (!initialized)
		InitDateRequest();
	datereqtext.IText = (UBYTE *) prompt;
	if (default_date)			/* write default date in requester? */
		DS2Str(dategad_sbuf_1,"%02m/%02d/%02y %02h:%02n:%02s",
			default_date);
	else
		*dategad_sbuf_1 = '\0';

	Request(&daterequest, window);

	for (;;) {
		Wait(1L << window->UserPort->mp_SigBit);
		while (msg = (struct IntuiMessage *) GetMsg(window->UserPort)) {
			class = msg->Class;
			code = msg->Code;
			gadget = (struct Gadget *) msg->IAddress;
			x = msg->MouseX;
			y = msg->MouseY;
			ReplyMsg(msg);		/* acknowledge the message */

			switch (class) {

			case REQSET: 
				ActivateGadget(&dategad,window,&daterequest);
				break;
			
			case GADGETUP:
				gadgid = gadget->GadgetID;
				if (gadgid == NOWGAD)
					DateStamp(date);
				else if (gadgid == OKGAD)
					gadgid = DATEGAD;

				UnpackDS(date, &upd);

				switch (gadgid) {
				case DATEGAD:
					if ( ! Str2DS(dategad_sbuf_1, date) ) {
						EndRequest(&daterequest, window);
						return 0;
					}
					else
						DisplayBeep(window->WScreen); /* bad conversion */
					break;

				case UPYEARGAD:
					++upd.year;
					break;

				case DOWNYEARGAD:
					if (upd.year > 1978) --upd.year;
					break;

				case UPMONTHGAD:
					if (++upd.month > 12) upd.month = 1;
					break;

				case DOWNMONTHGAD:
					if (--upd.month < 1) upd.month = 12;
					break;

				case UPDAYGAD:
					if (upd.day < 31) ++upd.day;
					break;

				case DOWNDAYGAD:
					if (upd.day > 1) --upd.day;
					break;

				case UPHOURGAD:
					if (++upd.hour > 23) upd.hour = 0;
					break;

				case DOWNHOURGAD:
					if (--upd.hour < 0) upd.hour = 23;
					break;

				case UPMINGAD:
					if (++upd.minute > 59) upd.minute = 0;
					break;

				case DOWNMINGAD:
					if (--upd.minute < 0) upd.minute = 59;
					break;

				case UPSECGAD:
					if (++upd.second > 59) upd.second = 0;
					break;

				case DOWNSECGAD:
					if (--upd.second < 0) upd.second = 59;
					break;

				default:
					break;
				}					/* end switch(gadgid) */

				/* Reformat the new date value. */

				PackDS(date, &upd);
				if (upd.year)
					DS2Str(dategad_sbuf_1,"%02m/%02d/%02y %02h:%02n:%02s",
						date);
				else
					strcpy(dategad_sbuf_1,"00/00/00 00:00:00");
				RefreshGadgets(&dategad, window, &daterequest);
				break;

			default:
				break;			/* ignore the rest */
			}					/* end switch(class) */
		}
	}
}

/*#define DEBUG */

#ifdef DEBUG

/* --- Only compiled in the debug version --- */

#include <exec/memory.h>

/* New window structure */

struct NewWindow newwindow = {
	0,0,640,200,0,1,

/* IDCMP Flags */

	MOUSEMOVE | MENUPICK | MOUSEBUTTONS | 
	CLOSEWINDOW | GADGETDOWN | GADGETUP | REQSET, 

/* Flags */
	WINDOWCLOSE | WINDOWDEPTH | ACTIVATE | RMBTRAP | REPORTMOUSE,

	NULL,							/* First gadget */
	NULL,							/* Checkmark */
	(UBYTE *)"Date Requester Test Program",	/* Window title */
	NULL,							/* No custom streen */
	NULL,							/* Not a super bitmap window */
	0,0,640,200,					/* Not used, but set up anyway */
	WBENCHSCREEN
};
struct IntuitionBase *IntuitionBase;
struct Window *mywindow;
struct DateStamp *ds;

static struct IntuiText MoreText = {
	AUTOFRONTPEN,				/* FrontPen */
	AUTOBACKPEN,				/* BackPen */
	JAM2,						/* DrawMode */
	AUTOLEFTEDGE,				/* LeftEdge */
	AUTOTOPEDGE,				/* TopEdge */
	AUTOITEXTFONT,				/* ITextFont */
	(UBYTE *) "Want to play some more?", /* IText */
	NULL						/* NextText */
	};

static struct IntuiText YesText = {
	AUTOFRONTPEN,				/* FrontPen */
	AUTOBACKPEN,				/* BackPen */
	AUTODRAWMODE,				/* DrawMode */
	AUTOLEFTEDGE,				/* LeftEdge */
	AUTOTOPEDGE,				/* TopEdge */
	AUTOITEXTFONT,				/* ITextFont */
	(UBYTE *) "Sure!", 			/* IText */
	NULL						/* NextText */
	};

static struct IntuiText NoText = {
	AUTOFRONTPEN,				/* FrontPen */
	AUTOBACKPEN,				/* BackPen */
	JAM2,						/* DrawMode */
	AUTOLEFTEDGE,				/* LeftEdge */
	AUTOTOPEDGE,				/* TopEdge */
	AUTOITEXTFONT,				/* ITextFont */
	(UBYTE *) "Nope!", 			/* IText */
	NULL						/* NextText */
	};

main()
{
	short keep_going;

	if (!(IntuitionBase = (struct IntuitionBase *)
		OpenLibrary("intuition.library",33L))) {
		puts("Failed to open Intuition library!");
		exit(1);
	}

	ds = (struct DateStamp *)
		AllocMem((long) sizeof(struct DateStamp), MEMF_PUBLIC);
	DateStamp(ds);				/* get current date/time */

	mywindow = OpenWindow(&newwindow);

	do {
		DateRequest(mywindow, "Enter date and time", ds, ds);
		keep_going = AutoRequest(mywindow, &MoreText, &YesText, &NoText,
						NULL, NULL, 220L, 50L);
	} while (keep_going);

	if (mywindow)
		CloseWindow(mywindow);

	if (IntuitionBase)
		CloseLibrary(IntuitionBase);
}

#endif
SHAR_EOF
if test 21886 -ne "`wc -c DateRequest.c`"
then
echo shar: error transmitting DateRequest.c '(should have been 21886 characters)'
fi
echo shar: extracting DiskMisc.c '(8658 characters)'
cat << \SHAR_EOF > DiskMisc.c
/* DiskMisc.c - miscellaneous disk support routines.
 * Mark Rinfret (et al), 1987
 *
 * History:		(most recent change first)
 *
 * 12/15/87 -MRR- Added two new routines, FindDevice and GetVolumeName.
 *                These are based largely on the "Info" program by
 *                Chuck McManis.
 *
 * 11/24/87 -MRR- The routine "DiskBlocks" was changed to "DiskBlocksLeft".
 *	              A new routine, "TotalDiskBlocks", returns the capacity,
 *				  in disk blocks, of the drive associated with a pathname.
 *				  A new routine, "GetDiskInfo", returns InfoData on a given
 *                pathname.
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <exec/io.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <libraries/filehandler.h>
#include <functions.h>
#include <ctype.h>
#include <stdio.h>
#include <clib/macros.h>
#include ":src/lib/DiskMisc.h"

extern LONG 	sendpkt();
static int errCode;


extern struct DosLibrary *DOSBase;

/* These are some macros that help in dealing with BCPL pointers and strings
 * the first is macro converts a BPTR to a C pointer of type struct DeviceList *
 * The second two provide the length of a BSTR * and a pointer to it's text.
 */

/* Convert BCPL DeviceList pointer to C pointer. */
#define DLPTR(x)	((struct DeviceList *)BADDR(x))

/* Get the length of a BCPL string. */
#define LENGTH(x)	(unsigned) (*(UBYTE *)BADDR(x))

/* Get address of BCPL string, adjusted for leading length byte. */
#define STRING(x)	(((char *)BADDR(x))+1)

^L
/* 
 * Find the first device node for a specified device type.
 * Called with:
 *		dList:		device list 
 *		dType:		device type mnemonic
 * Returns:
 *		pointer to device node or NULL
 *
 * Note: by passing the dl_Next field on successive calls, you may scan
 *	     the entire device list.
 */

struct DeviceList *
FindDevice(dList, dType)

struct DeviceList *dList;		/* Pointer to a device list structure */
long dType;						/* A device type as defined in dos.h  */

{
struct DeviceList	*t; 		/* A temporary pointer */

for (t = dList; ((t != NULL) && (t->dl_Type != dType)); 
	   t = DLPTR(t->dl_Next));
return(t);
}

char *
GetVolumeName(deviceName, volumeName)
	char *deviceName, *volumeName;
{
	struct RootNode 	*rootNode;
	struct DeviceList 	*devList, *t, *t2;
	struct DeviceNode 	*devNode;
	struct DosInfo		*dosInfo;

	char dName[31];
	int i, nameLength;
	struct InfoData	*info;
	struct Process	*myProcess;
	APTR			savedWindow;
	struct Lock *l;
	char *vName = NULL;			/* set non-null on success */

	*volumeName = '\0';			/* initialize name to null */

	/* Make a copy of the device name string. */
	strncpy(dName, deviceName, sizeof(dName)-1);

	/* Make sure that the device name is not colon-terminated.   Also
	 * insure that the device name is all upper case. 
	 */
	nameLength = strlen(dName);
	for (i = 0; i < nameLength; ++i) {
		if (dName[i] == ':') {
			dName[i] = '\0';
			break;
		}
		else
			dName[i] = toupper(dName[i]);
	}
	nameLength = strlen(dName);		/* Get device name length. */

	if (! (info = (struct InfoData *) 
		AllocMem((long) sizeof(struct InfoData), MEMF_PUBLIC|MEMF_CLEAR))) {
		return vName;				/* Will be NULL if unsuccessful. */
	}

	/* Get the DOS root node. */

	rootNode = (struct RootNode *)DOSBase->dl_Root;

	/* Get the DOS info node. */

	dosInfo = (struct DosInfo *)BADDR(rootNode->rn_Info);

	/* devList becomes the anchor point that we always start from */

	devList = (struct DeviceList *)BADDR(dosInfo->di_DevInfo);  

	/* Disable requesters if no disk present */
	  myProcess = (struct Process *) FindTask(NULL);
	  savedWindow = myProcess->pr_WindowPtr;
	  myProcess->pr_WindowPtr = (APTR) -1L;

	  for (t = FindDevice(DLPTR(dosInfo->di_DevInfo), DLT_DEVICE); 
	  	   t != NULL;
	       t = FindDevice(DLPTR(t->dl_Next), DLT_DEVICE)) {
		devNode = (struct DeviceNode *) t;

	    /* A non-null task pointer indicates a disk device. */

	    if (devNode->dn_Task) { 
#ifdef DEBUG
			char tempString[31];
			strncpy(tempString, STRING(devNode->dn_Name),
					LENGTH(devNode->dn_Name) );
			printf("Testing %s\n", tempString);
#endif
			if (!strncmp(dName, STRING(devNode->dn_Name),
			      		 MAX(LENGTH(devNode->dn_Name),nameLength) ) ) {

				/* We've found a match!  Now, add a terminating colon to the
				 * device name so we can pass it to Lock. 
				 */
				dName[nameLength++] = ':';
				dName[nameLength] = '\0';

#ifdef DEBUG
				printf("Matched %s\n", deviceName);
#endif
				l = (struct Lock *) Lock(dName, ACCESS_READ);
				if (l) {						/* disk inserted? */
					Info(l, info);
					t2 = DLPTR(info->id_VolumeNode);
					if (t2 != NULL)  {
						strncpy(volumeName, 
								STRING(t2->dl_Name), 
								LENGTH(t2->dl_Name) );
						volumeName[LENGTH(t2->dl_Name)] = '\0';
					}
#ifdef DEBUG
					else
						DebugWrite("Null volume name!\n");
#endif
					UnLock(l);
				}
#ifdef DEBUG
				else
					printf("Unable to lock %s!\n", dName);
#endif
				vName = volumeName;				/* set 'success' */
				break;
			}
	    } 										/* end of disk device test */
	} 										/* end for */
	myProcess->pr_WindowPtr = savedWindow;	/* restore window pointer */
cleanup:
	if (info) FreeMem(info, (long) sizeof(struct InfoData));
	return vName;
}

/* This routine returns the number of disk blocks remaining on the
 * drive specified by 'name'.  Though 'name' would typically be the
 * drive name or volume name, it can also be the name of any file
 * on the disk drive.
 * Called with:
 *		name:		disk device or volume name
 * Returns:
 *		> 0 => number of blocks available
 *		< 0 => error status
 */

LONG
DiskBlocksLeft(name)
	char *name;
{
	LONG blocks = -1L;
	struct InfoData *info = NULL;

	if ( !( info = GetDiskInfo(name) ) ) 
		return -errCode;

	blocks = info->id_NumBlocks - info->id_NumBlocksUsed;

	FreeMem(info, (long) sizeof(struct InfoData));
	return blocks;					/* bad status indicator */
}

/* Get disk info, given a pathname.
 * Called with:
 *		name:	file pathname
 * Returns:
 *		a pointer to an InfoData structure
 * Note:
 *		It is the user application's responsibility to free the memory
 *		allocated for the InfoData structure.
 */

struct InfoData *
GetDiskInfo( name )
	char *name;
{
	struct InfoData *info = NULL;
	struct FileLock *lock = NULL;

	errCode = 0;

	if (lock = (struct FileLock *) Lock(name, ACCESS_READ)) {
		if (info = AllocMem((long)sizeof(struct InfoData),MEMF_PUBLIC)) {
			if ( ! Info(lock,info))  errCode = IoErr();
		}
		else 
			errCode = ERROR_NO_FREE_STORE;
		UnLock(lock);
	}
	else 
		errCode = IoErr();

	if (errCode) {
		if (info) FreeMem( info, (long) sizeof( struct InfoData ) );
		info = NULL;
	}
	return info;
}

/* Disk ACTION_INHIBIT support routine.
 * Author:		Mark R. Rinfret
 * Date:		06/29/87
 * 
 * This routine provides support for user-written disk formatting, copy
 * operations which benefit from suppressing/restoring disk validation.
 */

int
Inhibit(drivename, code)
	char *drivename; int code;
{
	struct MsgPort     *task;
	LONG 	arg[2];
	LONG 	rc;

	if (!(task=(struct MsgPort *) DeviceProc(drivename))) 
		return 1;						/* fail, darn it! */

	arg[0] = code;

	/* Now, cross all your fingers and toes... */

	return ( !sendpkt(task,ACTION_INHIBIT,arg,1));
}

/* This routine returns the total number of disk blocks on the
 * drive specified by 'name'.  Though 'name' would typically be the
 * drive name or volume name, it can also be the name of any file
 * on the disk drive.
 * Called with:
 *		name:		disk device, volume or path name
 * Returns:
 *		> 0 => total number of blocks on drive
 *		< 0 => error status
 */

LONG
TotalDiskBlocks(name)
	char *name;
{
	struct InfoData *info = NULL;
	long int blocks = -1L;

	if ( ! ( info = GetDiskInfo(name) ) ) 
		return -errCode;

	blocks = info->id_NumBlocks;

	FreeMem(info, (long) sizeof(struct InfoData));
	return blocks;					/* bad status indicator */
}

#ifdef DEBUG
main()
{
	long blocks;

	char deviceName[81], volumeName[81];

	if ((blocks = TotalDiskBlocks("df0:")) < 0) {
		printf("Bad status from TotalDiskBlocks() => %ld\n", -blocks);
		exit();
	}
	else
		printf("Total disk blocks on DF0: => %ld\n", blocks);

	if ( (blocks = DiskBlocksLeft("df0:") ) < 0) 
		printf("Bad status from DiskBlocks() => %ld\n", -blocks);
	else
		printf("Disk blocks left on df0: => %ld\n",blocks);

	for (;;) {
		puts("Enter a disk DEVICE name (dh0, df0, etc.).");
		puts("Just hit RETURN to quit.");
		gets(deviceName);
		if (*deviceName == '\0') break;
		if (!GetVolumeName(deviceName, volumeName)) 
			puts("Sorry - I couldn't find that device in my system.");
		else
			printf("The volume name is %s\n",volumeName);
	}
}
#endif
SHAR_EOF
if test 8658 -ne "`wc -c DiskMisc.c`"
then
echo shar: error transmitting DiskMisc.c '(should have been 8658 characters)'
fi
echo shar: extracting Error.c '(2495 characters)'
cat << \SHAR_EOF > Error.c
/* MRBackup Error handling routines.
 * Filename:	Error.c
 * Date:		11/20/87
 * 
 * History:		(most recent change first)
 *
 * 11/20/87 -MRR- V1.4 This package was created, along with ErrorRequest.c,
 *				  to provide extended error handling features.
 */

#include "MRBackup.h"
#include "Gadget.h"

struct Requester RequesterStructure2;

/* Get an error handling option.  This is done by putting up a requester
 * and waiting for the user to select one of the option gadgets.
 * Called with:
 *		flags:	error recovery flags
 *			The flags are various ERR_ codes, OR'ed together.  For example,
 *			ERR_ABORT | ERR_RETRY_FILE | ERR_IGNORE
 *			would give the option of allowing the user to abort the
 *			operation, retry the current file or simply ignore the error.
 * Returns:
 *		error recovery code (ERR_ABORT, ERR_RETRY_FILE, etc.)
 */
GetErrOpt(flags)
	unsigned flags;
{
	ULONG class;
	unsigned enable;
	struct Gadget *gadget;
	struct IntuiMessage *msg;
	int status = ERR_ABORT;

	if (!flags) 	flags = ERR_ABORT;

	for (gadget = RequesterStructure2.ReqGadget; gadget;
		 gadget = gadget->NextGadget) {
		switch (gadget->GadgetID) {
		case ABORT:
			enable = flags & ERR_ABORT;
			break;
		case FILERETRY:
			enable = flags & ERR_RETRY_FILE;
			break;
		case DISKRESTART:
			enable = flags & ERR_RESTART_VOLUME;
			break;
		case FILESKIP:
			enable = flags & ERR_IGNORE;
			break;
		default:			/* Some other gadgetry; default to enabled. */
			enable = 1;
		}
		if (enable)
			gadget->Flags &= ~GADGDISABLED;
		else
			gadget->Flags |= GADGDISABLED;
	}

	RequesterStructure2.LeftEdge = 5;
	RequesterStructure2.TopEdge = 15;
	if (!Request(&RequesterStructure2, mainWindow)) {
		TypeAndSpeak("I could not put up my error requester.\n");
		TypeAndSpeak("I will have to abort this operation.\n");
		status = ERR_ABORT;
	}
	else {
		WindowToFront(mainWindow);
		for (class = 0; class != GADGETDOWN; ) { 
			Wait(1L << mainWindow->UserPort->mp_SigBit);
			msg = (struct IntuiMessage *) GetMsg(mainWindow->UserPort);
			if (msg) {
				class = msg->Class;
				gadget = (struct Gadget *) msg->IAddress;
				ReplyMsg(msg);
			}
		}
		switch (gadget->GadgetID) {
		case ABORT:
			status = ERR_ABORT;
			break;
		case FILERETRY:
			status = ERR_RETRY_FILE;
			break;
		case DISKRESTART:
			status = ERR_RESTART_VOLUME;
			break;
		case FILESKIP:
			status = ERR_IGNORE;
			break;
		default:
			TypeAndSpeak("I have a bug in my error requester!\n");
		}
	}
	WindowToBack(mainWindow);
	return status;
}
SHAR_EOF
if test 2495 -ne "`wc -c Error.c`"
then
echo shar: error transmitting Error.c '(should have been 2495 characters)'
fi
echo shar: extracting FileMisc.c '(4940 characters)'
cat << \SHAR_EOF > FileMisc.c
/* File date routines.
 * Filename:	FileDates.c
 * History:		(most recent change first)
 *
 * 01/01/88 -MRR- Happy new year!  Added a new function, CompareFileDates.
 * 09/24/87 -MRR- SetFileDate was allocating a BSTR of 68 bytes, rather
 *                than using the length of the file name string.  I don't
 *                have the foggiest notion why (that was DUMB!).
 */

#include "exec/types.h"
#include "exec/ports.h"
#include "exec/io.h"
#include "exec/memory.h"
#include "libraries/dosextens.h"
#include <stdio.h>
#define AZTEC 1
#ifdef AZTEC
#include "functions.h"				/* aztec C include */
#endif

#define ACTION_SETDATE_MODE 34L		/* Set creation date on file */
#define DOSTRUE 	    -1L			/* AmigaDos TRUE */
#define MAXARGS          7L			/* limit in packet structure
									   (dosextens.h) */
#define NARGS		     4L			/* Number of args for setdate */

long    sendpkt();

/* Function:
 *		CompareFileDates
 *
 * Called with:
 *		name1, name2: filenames for which dates are to be compared
 *
 * Returns:
 *		0 => dates are equal or both files don't exist
 *     -1 => date of <name1> < date of <name2> or <name1> doesn't exist
 *      1 => date of <name1> > date of <name2> or <name2> doesn't exist
 *
 * Description:
 *		CompareFileDates attempts to compare the file creation/modification
 *		dates of two files.
 */
int
CompareFileDates(name1, name2)
	char *name1, *name2;
{
	struct DateStamp d1, d2;

	GetFileDate(name1, &d1);
	GetFileDate(name2, &d2);
	return CompareDS(&d1, &d2);
}
^L
/* Function:
 *		GetFileDate
 * 
 * Called with:
 *		name:	file name
 *		date:	pointer to DateStamp structure
 *
 * Returns:
 *		result:	1 => got a date, 0 => didn't
 *
 * Description:
 *		GetFileDate attempts to get the creation/modification date
 *		of a file (unfortunately, they're one and the same) and stores
 *		it into the location pointed to by <date>.  If the file doesn't
 *		exist or for some reason the date can't be obtained, <date>
 *      is set to zeros and a zero is returned.  Otherwise, <date> is set
 *		to the file date and a 1 is returned.
 */

BOOL 
GetFileDate(name, date)
	char   *name; struct DateStamp *date;
{
	struct FileInfoBlock *Fib;
	ULONG   FLock;
	int     result = FALSE;
	register struct DateStamp  *d;

	if ((FLock = (ULONG) Lock(name,(long)(ACCESS_READ)))== NULL) 
		goto exit1;

	Fib = (struct FileInfoBlock *)
		AllocMem((long)sizeof(struct FileInfoBlock),
					(long)(MEMF_CHIP|MEMF_PUBLIC));

	if (Fib == NULL )
		result = FALSE;
	else{
		if (!Examine(FLock, Fib )) {
			result = FALSE;
		}
		else 
			if (Fib->fib_DirEntryType > 0 )
				result = FALSE;		/* It's a directory */
			else{
				d = &Fib->fib_Date;
				date->ds_Days = d->ds_Days;
				date->ds_Minute = d->ds_Minute;
				date->ds_Tick = d->ds_Tick;
				result = TRUE;
			}
		FreeMem((void *)Fib,(long)sizeof(struct FileInfoBlock));
	}

	UnLock(FLock);
exit1:
	if (! result ) {
		date->ds_Days = 0;
		date->ds_Minute = 0;
		date->ds_Tick = 0;
	}
	return result;
}
^L
/* Function:
 *		IsDir
 *
 * Called with:
 *		name:		pathname specification
 *
 * Returns:
 *		0 =>		no
 *		1 => 		yes
 *	   <0 =>		-<system error number>
 *
 * Description:
 *
 * 		Examine a pathname to determine if it is a device or directory name.
 */
int
IsDir(name)
	char *name;
{
	struct FileInfoBlock   *FIB = NULL;
    struct Lock *lock = NULL;	
	int status;


	if (!(FIB =
		AllocMem((long)sizeof(struct FileInfoBlock),
				 MEMF_CHIP|MEMF_CLEAR))) {
		return -ERROR_NO_FREE_STORE;
	}

	if (!(lock = (struct Lock *) Lock(name,SHARED_LOCK))) {
		status = -IoErr();
		goto done;
	}

	if ((Examine(lock,FIB))==0){
		status = -IoErr();
		goto done;
	}

	status = (FIB->fib_DirEntryType > 0);
done:
	if (FIB)
		FreeMem(FIB, (long) sizeof(struct FileInfoBlock));
	UnLock(lock);
	return status;
}
^L
/*---------------------------------------------------------------------*/
/*  SetFileDate: datestamp the given file with the given date.	       */
/*---------------------------------------------------------------------*/

BOOL SetFileDate(name,date )
char   *name;
struct DateStamp   *date;
{
	struct MsgPort     *task;		/* for process id handler */
	ULONG   arg[4];					/* array of arguments     */
	int nameleng;
	char   *bstr, *strcpy();		/* of file to be set      */
	long    rc;
	char   *strchr();
	int     strlen();

	rc = 0;

	nameleng = strlen(name);
	if (!(bstr = (char *)AllocMem((long) (nameleng + 2),MEMF_PUBLIC)))
		goto exit2;

	if (!(task = (struct MsgPort *)DeviceProc(name )))
		goto exit1;

 /* Dos Packet needs the filename in Bstring format */

	(void) strcpy(bstr+1, name );
	*bstr = nameleng;

	arg[0]= (ULONG)NULL;
	arg[1]= (ULONG)IoErr();			/* lock on parent director set by
									   DeviceProc() */
	arg[2]= (ULONG)bstr >> 2;
	arg[3]= (ULONG)date;
	rc = sendpkt(task,ACTION_SETDATE_MODE,arg,4L );

exit1: if (bstr )
		FreeMem((void *)bstr, (long) (nameleng + 2));
exit2: if (rc == DOSTRUE )
		return TRUE;
	else
		return FALSE;
}
SHAR_EOF
if test 4940 -ne "`wc -c FileMisc.c`"
then
echo shar: error transmitting FileMisc.c '(should have been 4940 characters)'
fi
echo shar: extracting FormatDisk.c '(9938 characters)'
cat << \SHAR_EOF > FormatDisk.c
/* Format a floppy disk (880k drive).
 * Author:		Mark R. Rinfret
 * Date:		06/28/87
 * Description:
 *		This set of routines may be incorporated into a program which
 *	has need of formatting a floppy disk.  I wrote it to support my
 *	hard disk backup utility.
 *
 * History:		(most recent change first)
 *
 * 12/08/87 -MRR- My floppy drive (at least) will occaisionally "hang"
 *                during formatting.  I don't know what the source of
 *                the problem is, but for now I've added a timer and
 *                switched to a SendIO/Wait combo to handle this.
 *
 * 08/26/87 -MRR- Modified FormatDisk to delay 5 seconds after
 *				  uninhibiting the drive.  This should give enough time
 *                for the validator to do its thing and prevent the
 *				  "Insert disk..." requester from rearing its ugly head
 *                if the application attempts to access the disk as
 *                soon as we return.
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <exec/devices.h>
#include <devices/trackdisk.h>
#include <libraries/dosextens.h>
#include ":src/lib/AmigaFunctions.h"

#include ":src/lib/Timer.h"

extern struct IOExtTD *CreateExtIO();

static int CkIOErr();

#define MAX_NAME	30L
#define TD_WRITE	CMD_WRITE
#define TRACKSIZE	NUMSECS * TD_SECTOR


/* Format a floppy disk - hardwired for the 3.5" 880k floppy drives.
 * Called with:
 *		drivename:	device name (DF0, etc.)
 *      name:		new volume name
 * Returns:
 *		Zero on success, 1 on failure
 * Note:
 *		This routine does not currently perform a verification, as
 *		recommended by the RKM.  Perhaps later...
 *		I also discovered that there's some erroneous crap in 
 *		"The Amiga Programmer's Workbook, Vol. II", by 
 *		Eugene P. Mortimore.  On page 339, he states that only 512
 *		bytes of track data are required for formatting.  The RKM
 *		correctly states that a "track's worth of data" is required.
 *		It took some playing with DiskEd to discover this error.
 */

int 
FormatDisk(drivename,name)
	char *drivename; char *name;
{
	long checkSum;
	char *dosID = "DOS";
	long dosWord = 0;
	short error;
	long diskBit, timerBit;
	struct MsgPort *diskPort = NULL;
	struct IOExtTD *diskRequest = NULL;
	ULONG diskChangeCount;
	USHORT retries, status = 0, i, timeout, track;
	long signals;						/* signal bits from Wait() */
	struct timerequest *timeRequest;		/* timeout timer request */
	int unit; 
	char *volname;

	char *diskBuffer;
	ULONG *diskBlock;			/* alias for diskBuffer, ULONG type */

	if (strlen(name) >= MAX_NAME) {
#ifdef DEBUG
		printf("Disk name is too long!\n");
#endif
		status = ERROR_INVALID_COMPONENT_NAME;	
		goto cleanup;
	}

	if ((unit = (drivename[2]-'0')) < 0 || unit >= NUMUNITS) {
#ifdef DEBUG
		printf("FormatDisk: invalid drive specification!\n");
#endif
		status = ERROR_INVALID_COMPONENT_NAME;
		goto cleanup;
	}

	if (!(diskBuffer = 
		AllocMem((long) TRACKSIZE, MEMF_PUBLIC | MEMF_CHIP))) {
		status = ERROR_NO_FREE_STORE;
		goto cleanup;
	}

	timeRequest = CreateTimer(0);	/* create uHERZ timer */
	if (timeRequest == NULL) {
		status = ERROR_NO_FREE_STORE;	/* would IoError be valid? */
		goto cleanup;
	}

	/* Store DOS "magic word" in disk block to be written during
	 * formatting. 
	 */
	diskBlock = (ULONG *) diskBuffer;/* we'll need this later */
	for (i = 0; i < 3; ++i)
		dosWord = (dosWord << 8) | dosID[i];
	dosWord = dosWord << 8;

#ifdef DEBUG
	printf("dosWord is %lx\n",dosWord);
#endif
	for (i = 0; i < TRACKSIZE / 4; ++i)
		diskBlock[i] = (dosWord | (long) (i & 0xff));

	if ((diskPort = CreatePort(0L, 0L)) == NULL) {
#ifdef DEBUG
		printf("FormatDisk can't create port!\n");
#endif
		status = 1;						/* is there a better error code? */
		goto cleanup;
	}

	if (!(diskRequest = (struct IOExtTD *) 
		CreateExtIO(diskPort, (long) sizeof(struct IOExtTD)))) {
		status = 1;
		goto cleanup;
	}

	if (status = OpenDevice(TD_NAME, (long) unit, diskRequest, 0L)) {
#ifdef DEBUG
		printf("FormatDisk: OpenDevice error: %d\n",error);
#endif
		goto cleanup;
	}

	if (status = Inhibit(drivename, 1)) {
#ifdef DEBUG
		printf("FormatDisk: unable to inhibit drive!\n");
#endif
		goto cleanup;
	}

/* Get the current disk change count.  This allows the trackdisk
 * driver to detect unwanted disk changes later on.
 */

	diskRequest->iotd_Req.io_Command = TD_CHANGENUM;
	DoIO(diskRequest);

/* Save a copy of the disk change count. */

	diskChangeCount = diskRequest->iotd_Req.io_Actual;

#ifdef DEBUG
	printf("Current disk change count is %ld\n", diskChangeCount);
#endif


	/* Format the disk, one track at a time.  This operation seems
	 * susceptible to timing out (infrequently) on my system, so we'll
	 * use a combination of SendIO and Wait, rather than DoIO.
	 */

	diskBit = 
		1L << diskRequest->iotd_Req.io_Message.mn_ReplyPort->mp_SigBit;
	timerBit = 	
		1L << timeRequest->tr_node.io_Message.mn_ReplyPort->mp_SigBit;

#ifdef DEBUG
	printf("diskBit => %08lx,  timerBit => %08lx\n",
			diskBit, timerBit);
#endif

	for (track = 0; track < NUMTRACKS; ++track) {
		retries = 0;
retry:
		diskRequest->iotd_Req.io_Command = TD_FORMAT;
		diskRequest->iotd_Req.io_Flags = 0;
		diskRequest->iotd_Req.io_Data = (APTR) diskBuffer;
		diskRequest->iotd_Count = diskChangeCount;
		diskRequest->iotd_Req.io_Length = NUMSECS * TD_SECTOR; 
		diskRequest->iotd_Req.io_Offset = track * NUMSECS * TD_SECTOR;
		SendIO(diskRequest);

		StartTimer(timeRequest, 5L, 0L); /* Start 5 second disk timer. */
#ifdef DEBUG
		if (timeRequest->tr_node.io_Error) {
			printf("Error on StartTimer: %d\n",
					timeRequest->tr_node.io_Error);
		}
#endif
		while (TRUE) {
			timeout = 0;
			signals = Wait(diskBit | timerBit);
			if (signals & timerBit) {
				if (CheckIO(timeRequest)) {	/* timer I/O complete? */
					WaitIO(timeRequest);	/* complete timer processing */
					AbortIO(diskRequest);	/* kill disk I/O */
					timeout = 1;
#ifdef DEBUG
					puts("--TIMEOUT--");
#endif
					break;
				}
			}

			if (signals & diskBit) {	/* Did disk complete? */
				if (CheckIO(diskRequest)) {
					WaitIO(diskRequest); /* complete disk processing */
					StopTimer(timeRequest);
					break;
				}
			}
		}							/* end while(TRUE) */

		/* Retry a timeout 3 times before giving up. */

		if (timeout && (++retries < 4) ) goto retry;

		if (status = CkIOErr(diskRequest,"Formatting error")) {
#ifdef DEBUG
			printf("  Track: %d\n",track);
#endif
			goto cleanup;
		}
	}

	/* Now comes some real KLUDGING.  Fill in the root block and the
	 * first hash block.  The information for this was gathered from
	 * the "AmigaDos Technical Reference Manual" and some sleuthing
	 * with DiskEd.
	 */

	for (i = 0; i < 128; ++i)
		diskBlock[i] = 0;

	diskBlock[0] = 2;			/* T.SHORT (type) */
	diskBlock[3] = 128 - 56;	/* hashtable size */
	diskBlock[78] = 0xffffffff; /* BMFLAG */
	diskBlock[79] = 881;		/* first bitmap block */
	DateStamp(&diskBlock[105]);	/* volume last altered date/time */
	DateStamp(&diskBlock[121]); /* volume creation date/time */
	volname = (char *) &diskBlock[108];
	/* convert input name to BSTR */
	*volname = strlen(name);
	for (i = 0; i < *volname; ++i)
		*(volname + 1 + i) = *(name + i);

	diskBlock[127] = 1;			/* ST.ROOT (secondary type) */

	checkSum = 0;
	for (i = 0; i < 128; ++i)
		checkSum += diskBlock[i];

	diskBlock[5] = - checkSum;

	/* Write the root block out to the disk. */

	diskRequest->iotd_Req.io_Command = TD_WRITE;
	diskRequest->iotd_Req.io_Length = TD_SECTOR;
	diskRequest->iotd_Req.io_Offset = TD_SECTOR * 880L;
	DoIO(diskRequest);
	if (status = CkIOErr(diskRequest, "Error writing root block")) {
		goto cleanup;
	}

	/* Write the first bitmap block. */

	for (i = 0; i < 56; ++i)
		diskBlock[i] = 0xffffffff;

	for (i = 56; i < 128; ++i)
		diskBlock[i] = 0;

	diskBlock[0] = 0xc000c037;	/* hint: x37 = 55 (last word of map?) */
	diskBlock[28] = 0xffff3fff; /* blocks 880, 881 used */
	diskBlock[55] = 0x3fffffff; /* blocks 1760, 1761 used? */

	diskRequest->iotd_Req.io_Length = TD_SECTOR;
	diskRequest->iotd_Req.io_Offset = 881L * TD_SECTOR;
	DoIO(diskRequest);					/* write out the bitmap */
	if (status = CkIOErr(diskRequest, "Error writing bitmap")) {
		goto cleanup;
	}

	diskRequest->iotd_Req.io_Command = ETD_UPDATE;
	diskRequest->iotd_Req.io_Flags = 0;
	DoIO(diskRequest);

	/* Turn the disk motor off. */

	diskRequest->iotd_Req.io_Command = TD_MOTOR;
	diskRequest->iotd_Req.io_Length = 0;
	DoIO(diskRequest);
	Inhibit(drivename, 0);				/* enable disk validator */

	Delay(3L * TICKS_PER_SECOND);		/* Give it a chance */

cleanup:
	CloseDevice(diskRequest);
	if (diskBuffer) FreeMem(diskBuffer, (long) TRACKSIZE);
	if (diskRequest) DeleteExtIO(diskRequest, (long) sizeof(*diskRequest));
	if (diskPort) DeletePort(diskPort);
	if (timeRequest) DeleteTimer(timeRequest);
	return status;
}

/* Check the disk request block for an error code.  If an error
 * occurred, print the argument string.
 * Called with:
 *		req:	pointer to I/O request structure
 *		msg:	error message string
 * Returns:
 *		error code from request structure
 */
static int CkIOErr(req, msg)
	struct IOStdReq *req; char *msg;
{
	register int code;

	if (code = req->io_Error) {
#ifdef DEBUG
		printf("%s, code: %d\n",msg,code);
#endif
	}
	return code;
}

#ifdef DEBUG
main(argc, argv)
	int argc; char *argv[];
{
	char *diskname;
	char *volname;

	int unit;

	if (argc < 3)
		volname = "GoodJob!";
	else
		volname = argv[2];

	if (argc < 2)
		diskname = "DF1:";
	else {
		diskname = argv[1];
		if (strlen(diskname) != 4 || 
			(strncmp(diskname,"df",2) && strncmp(diskname,"DF",2))) {
bad_drive:
			printf("Drive name may only be df0: through df3:!\n");
			exit(1);
		}
		if ((unit = (diskname[2] - '0')) < 0 || unit > 3)
			goto bad_drive;

	}
	printf("Insert disk in %s, then hit return\n",diskname);
	while (getchar() != '\n');
	if (FormatDisk(diskname,volname))
		printf("FormatDisk failed\n");
	else {
		printf("FormatDisk succeeded\n");
	}
}
#endif
SHAR_EOF
if test 9938 -ne "`wc -c FormatDisk.c`"
then
echo shar: error transmitting FormatDisk.c '(should have been 9938 characters)'
fi
#	End of shell archive
exit 0

ain@j.cc.purdue.edu (Patrick White) (01/29/88)

Program Name:	MRBackup
Submitted By:	uiucdcs!amanpt1.ZONE1.COM!mrr@ee.ecn.purdue.edu (Mark Rinfret)
Summary:	A hard disk backup utility.
Poster Boy:  Pat White  (ain@j.cc.purdue.edu)
Untested.

NOTES:
   I reshar'ed all of the source to get rid of some explicit path references
in where the files get put.  Now it will unshar on unix and the Amiga into
the current directory.
   I did *not* change the explicit path references to include files in the
.c files.
   I also included the docs that were sent with the binary version, so these
4 parts are complete source with docs.


-- Pat White   (co-moderator comp.sources/binaries.amiga)
UUCP: j.cc.purdue.edu!ain  BITNET: PATWHITE@PURCCVM   PHONE: (317) 743-8421
U.S.  Mail:  320 Brown St. apt. 406,    West Lafayette, IN 47906

========================================


#	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
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	IntuiHandler.c
#	List.c
#	MRDates.c
#	MiscRequest.c
#	Restore.c
#	Speech.c
#	Timer.c
#	UserPrefs.c
#	sendpkt.c
#	unixwild.c
# This archive created: Thu Jan 28 11:10:19 1988
# By:	Patrick White (PUCC Land, USA)
echo shar: extracting IntuiHandler.c '(9716 characters)'
cat << \SHAR_EOF > IntuiHandler.c
/* Intuition handler for MRBackup
 * Filename:	IntuiHandler.c
 * Date:		11/21/87
 *
 * History:		(most recent change first)
 *
 * 11/21/87 -MRR- This file was created for version 2.0.
 */

#include "MRBackup.h"
#include "Screen.c"

extern struct GfxBase *GfxBase;

extern struct Gadget curVolumeGadget;
extern struct Gadget errorGadget;

struct Window *OpenPathWindow();

/* Close all Intuition items. */

CloseIntuition()
{
	if ( IntuitionBase ) {
		if ( mainScreen ) {
			if ( mainWindow ) {
				ClearMenuStrip( mainWindow );
				CloseWindow( mainWindow );
			}
			if ( pathWindow )	CloseWindow( pathWindow );
			CloseScreen( mainScreen );
		}
		if ( progressConsole ) DeleteConsole( progressConsole );
		if ( progressWindow ) CloseWindow( progressWindow );

#ifdef DEBUG
		if ( debugConsole ) DeleteConsole( debugConsole );
		if ( debugWindow ) CloseWindow( debugWindow );
#endif

		if ( GfxBase ) CloseLibrary( GfxBase );
		CloseLibrary( IntuitionBase );
	}						/* end if IntuitionBase */
}
^L
/* Handle a gadget action.
 * Called with:
 *      window:		window that gadget is displayed in
 *		class:		message class (GADGETUP/GADGETDOWN/?)
 *		addr:		pointer to gadget structure
 */
DoGadget(window, class, addr)
	struct Window *window; ULONG class; struct Gadget *addr;
{
	USHORT id;					/* gadget identifier */
	struct Gadget *upGad;
	ULONG upClass;
	struct IntuiMessage *upMsg;	/* require gadget up to complete */

	id = addr->GadgetID;

	if (class == GADGETUP) {
#ifdef DEBUG
		sprintf(debugMsg,"GADGETUP: %d\n",id);
		DebugWrite(debugMsg);
#endif
		switch (id) {
			case HOMEPATH:
				GetHomePath(addr);
				break;
			case BACKPATH:
				GetBackPath(addr);
				break;
			case LISTPATH:
				GetListPath(addr);
				break;
			case XCLDPATH:
				GetXcldPath(addr);
				break;
			case STOP:
				TypeAndSpeak(
	"Use the STOP gadget during backup and restore operations.\n");
				break;
			default:
#ifdef DEBUG
				sprintf(conmsg, "Unknown gadget, ID %d.  Program error!\n",
						id);
				TypeAndSpeak(conmsg);
#endif
				break;
		}
	}
}
int
InitIntuition()
{
	struct ViewPort vP;
	struct RastPort *rpG;
 
	IntuitionBase = (struct IntuitionBase *)
		OpenLibrary("intuition.library", 0L);
	if ( ! IntuitionBase ) {
		puts("I can't open Intuition!\n");
		return ERR_ABORT;
	}

	GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0L);
	if ( ! GfxBase ) {
		puts("I can't open the graphics library!\n");
		return ERR_ABORT;
	}

	mainScreen = OpenScreen(&NewScreenStructure);
	if ( ! mainScreen ) {
		puts("I can't open the screen!\n");
		return ERR_ABORT;
	}

	vP = mainScreen->ViewPort;
	LoadRGB4(&vP, &Palette, (long) PaletteColorCount);

	NewWindowStructure1.Screen = mainScreen;
	mainWindow = OpenWindow(&NewWindowStructure1);	/* open the window */
	if ( mainWindow == NULL )
	{
		puts("I can't open the main window!\n");
		return ERR_ABORT;
	}

	if (!OpenPathWindow()) return ERR_ABORT;



	rpG = mainWindow->RPort;	/* get a rastport pointer for the window */

	SetMenuStrip(mainWindow, &MenuList1);

	/* Open the console(s) and related windows. */

	NewWindowStructure4.Screen = mainScreen;
	if ( ! (progressWindow = OpenWindow( &NewWindowStructure4 ) ) ) {
		puts("I can't open the progress window!\n");
		return ERR_ABORT;
	}

	if ( ! (progressConsole = CreateConsole( progressWindow ) ) ) {
		puts("I can't create the progress window console!\n");
		return ERR_ABORT;
	}

#ifdef DEBUG
	NewWindowStructure5.Screen = mainScreen;
	if ( ! (debugWindow = OpenWindow( &NewWindowStructure5 ) ) ) {
		puts("I can't open the debug window!\n");
		return ERR_ABORT;
	}

	if ( ! (debugConsole = CreateConsole( debugWindow ) ) ) {
		puts("I can't create the debug window console!\n");
		return ERR_ABORT;
	}
#endif

#ifdef IntuiTextList1
	PrintIText(rpG,&IntuiTextList1,0L,0L);	/* Print the text if any. */
#endif

#ifdef BorderList1
	DrawBorder(rpG,&BorderList1,0L,0L);	/* Draw the borders if any */
#endif

#ifdef ImageList1
	DrawImage(rpG,&ImageList1,0L,0L);	/* Draw the images if any. */
#endif
	return ERR_NONE;
}

/* Open the pathname specification window.
 * Returns:
 *		pointer to new window (success) or NULL (failure)
 * Side-Effects:
 *		sets pathWindow to new window pointer
 */

struct Window *
OpenPathWindow()
{
	if (!pathWindow) {				/* only if not already open */
		NewWindowStructure3.Screen = mainScreen;
		if (! ( pathWindow = OpenWindow(&NewWindowStructure3)) ) {
			TypeAndSpeak(
				"I can't open the pathname specifications window!\n");
		}
	}
	return pathWindow;
}

/* Set the current backup volume name into the curVolumeGadget. */

SetCurVolumeGadget(name)
	char *name;
{
	long position;
	char *s;

	position = RemoveGadget(mainWindow, &curVolumeGadget);
	s = GadgetString((&curVolumeGadget));
	strncpy(s, name, 30);
	AddGadget(mainWindow, &curVolumeGadget, position);
	RefreshGList(&curVolumeGadget, mainWindow, NULL, 1L);
}

^L
/* Set the current errorCount value into the errorGadget. */

SetErrorGadget()
{
	ULONG position;
	UBYTE *buffer;

	position = RemoveGadget(mainWindow, &errorGadget);
	buffer = GadgetString((&errorGadget));
	sprintf(buffer, "%4u", errorCount);
	AddGadget(mainWindow, &errorGadget, position);
	RefreshGList(&errorGadget, mainWindow, NULL, 1L);
}

/* Adjust the disk "gauge" to show how full it currently is.
 * Called with:
 * 		current:	number of blocks currently in use
 *		maxValue:	total number of blocks available
 */

SetGauge(current, maxValue)
	LONG current, maxValue;
{
	ULONG hPot;
	struct PropInfo *oldProp;
	LONG range, used;

#ifdef DEBUG
	sprintf(debugMsg,"SetGauge(%ld,%ld)\n",current,maxValue);
	DebugWrite(debugMsg);
#endif
	oldProp = (struct PropInfo *) gaugeGadget.SpecialInfo;

	range = maxValue + 1;
	used = maxValue - current;
	hPot = (used << 16L) / range;
	ModifyProp(
		&gaugeGadget, 				/* gadget pointer */
		mainWindow,					/* window pointer */
		NULL, 						/* requester */
		(ULONG) oldProp->Flags, 	/* flags */
		hPot, 						/* horizPot */
		0L,							/* vertPot */
		(ULONG) oldProp->HorizBody, /* horizBody */
		(ULONG) oldProp->VertBody	/* vertBody */
		);
}

/* Handle IDCMP messages generated by user actions. */

User()
{
	ULONG class;				/* message class */
	USHORT code;				/* message code */
	USHORT gadgetID;				/* gadget ID */
	APTR Iadr;					/* address field from message */
	struct IntuiMessage *msg;	/* Intuition message pointer */
	struct Window *msgWindow;	/* window message occurred in */
	USHORT quit = 0;
	SHORT x,y;					/* mouse x and y position */
	ULONG waitBits;


#ifdef DEBUG
	sprintf(debugMsg,"User: waitBits = %08lx\n", waitBits);
	DebugWrite(debugMsg);
#endif

	while (!quit) {
		ActivateWindow(mainWindow);

		waitBits = (1L << mainWindow->UserPort->mp_SigBit);
		if (pathWindow)
			waitBits = waitBits | (1L << pathWindow->UserPort->mp_SigBit);
			Wait(waitBits);

		while (!quit) {
			if (!(msg = (struct IntuiMessage *) 
				GetMsg(mainWindow->UserPort)))

			if ( ! (pathWindow && 
				    (msg = (struct IntuiMessage *)
					 GetMsg(pathWindow->UserPort) ) ) ) 
				break;

			class = msg->Class;
			code = msg->Code;
			Iadr = msg->IAddress;
			x = msg->MouseX;
			y = msg->MouseY;
			msgWindow = msg->IDCMPWindow;
			ReplyMsg(msg);		/* acknowledge the message */
#ifdef DEBUG
			sprintf(debugMsg,"Message class: 0x%lx, code: 0x%x\n",
				class, code);
#endif
			switch (class) {
			case CLOSEWINDOW:
				if (msgWindow == mainWindow)
					++quit;
				else if (msgWindow == pathWindow) {
					CloseWindow(pathWindow);
					pathWindow = NULL;
				}
				break;

			case GADGETUP:
			case GADGETDOWN:
				DoGadget(msgWindow, class, Iadr);
				break;

			case MENUPICK:
				quit = UserMenu(code);
				break;

			default:
				break;			/* ignore the rest */
			}					/* end switch(class) */
		}
	}
}
/* Handle a menu selection. 
 * Called with:
 *		xcode:		menu selection code
 * Returns:
 *		status code (1 => Quit was selected)
 */

int
UserMenu(xcode)
	USHORT xcode;
{
	USHORT code = xcode;
	struct MenuItem *item;
	USHORT itemNum,menuNum;

	while (code != MENUNULL) {
		menuNum = MENUNUM(code);
		itemNum = ITEMNUM(code);
		item = ItemAddress(&MenuList1, (long) code);

#ifdef DEBUG
		sprintf(debugMsg,
				"menu = %d, item = %d, flags = %04x\n",
				menuNum, itemNum, item->Flags);
		DebugWrite(debugMsg);
#endif

		switch (menuNum) {
		case MENU_PROJECT:			/* Project Menu */
			switch (itemNum) {
			case ITEM_BACKUP:		/* Backup */
				Backup();
				break;
			case ITEM_RESTORE:		/* Restore */
				Restore();
				break;
			case ITEM_LOADPREFS:	/* Load Preferences */
				GetUserPrefs();
				break;
			case ITEM_SAVEPREFS:	/* Save Preferences */
				PutUserPrefs();
				break;
			case ITEM_ABOUT:		/* About */
				About();
				break;
			case ITEM_QUIT:			/* Quit */
				return 1;			
			default:
				DisplayBeep(NULL);
				break;
			}
			break;

		case MENU_FLAGS:			/* Flags Menu */
			switch ( itemNum ) {
			case ITEM_COMPRESS:		/* Compression */
				doCompress = IsChecked( item ); 
				break;
			case ITEM_BIGFILES:		/* Do Big Files */
				doBigFiles = IsChecked( item );
				break;
			case ITEM_LIST:			/* Listing */
				doListing = IsChecked( item );
				break;
			case ITEM_SPEECH:		/* Speech */
				doSpeech = IsChecked( item );
				SetSpeech();
				break;
			case ITEM_FORMAT:		/* Format output disks */
				doFormat = IsChecked( item );
				break;
			default:
				DisplayBeep(NULL);
				break;
			}
			break;

		case MENU_WINDOWS:			/* Windows Menu */
			OpenPathWindow();		/* There's only one item. */
			break;
		}
#define EXTENDED_SELECT_WORKS
#ifdef EXTENDED_SELECT_WORKS
		code = item->NextSelect; 
		/* This next line is a kludge.  Testing has revealed that
		 * the NextSelect field is returning 0000x.  Why?
		 */
		if (!code) break;
#else
		break;
#endif
	}
	return 0;
}
SHAR_EOF
if test 9716 -ne "`wc -c IntuiHandler.c`"
then
echo shar: error transmitting IntuiHandler.c '(should have been 9716 characters)'
fi
echo shar: extracting List.c '(3040 characters)'
cat << \SHAR_EOF > List.c
/* MRBackup:	Listing support routines
 * Filename:	List.c
 * Author:		Mark R. Rinfret
 * Date:		11/27/87
 *
 * History:		(most recent changes first)
 *
 * 11/27/87 -MRR- This module was created for version 2.0.
 *
 */

#define MAIN

#include "MRBackup.h"

static char listDate[20] = "??/??/?? ??:??:??";

^L
/* Output a new header to the listing file. */

Header()
{
	if (doListing) {
		if (doFormat) {				/* A new disk? */
			fprintf(listing,
				"\f    MRBackup Listing of Volume %s created on %s\n\n", 
				destVol, listDate);
		}
		else {
			fprintf(listing,
			"\f    MRBackup Partial Listing of Volume %s updated on %s\n\n",
				destVol, listDate);
		}
		lineCount = 2;
	}
}
^L
/* List information about the file or directory we just created.
 * Called with:
 *		name:		destination filename string
 */

void
ListFileInfo(name)
	char *name;
{
	struct Lock *lock;
	char dateStr[20], infoLine[255];

	if (!doListing) return;

	if (! (lock = (struct Lock *) Lock(name, SHARED_LOCK))) {
		sprintf(infoLine, "ListFileInfo: could not lock %s", name);
		ListLine(infoLine);
		return;
	}

	if (Examine(lock, fibWork)) {
		if (fibWork->fib_DirEntryType >=0) {
			NewLine(2);
			sprintf(infoLine,"Directory:  %s", name);
			NewLine(1);
		}
		else {
			DS2Str(dateStr, "%02m/%02d/%02y %02h:%02n:%02s", 
					&fibWork->fib_Date);
			/* It is my understanding that the maximum filename length for
			 * a particular node is 30 characters.  The following code will
			 * probably never execute but it makes me feel warm and safe.
			 */

			if (strlen(fibWork->fib_FileName) > 40) {
				fibWork->fib_FileName[40] = '*';
				fibWork->fib_FileName[41] = '\0';
			}
			sprintf(infoLine,"  %s %8ld %5ld %s",
					dateStr, fibWork->fib_Size, fibWork->fib_NumBlocks,
					fibWork->fib_FileName);
		}
	}
	else {
		sprintf(infoLine,"ListFileInfo: can't examine %s", name);
	}
	UnLock(lock);
	ListLine(infoLine);
}
^L
/* Output a line to the listing file.  Start a new line if necessary.
 * The output string is assumed to have no form control characters.
 * Called with:
 *		str:		string to be output
 */
ListLine(str)
	char *str;
{	
	if (doListing) {
		if (lineCount >= LINES_PER_PAGE) Header();
		fprintf(listing,"  %s\n",str);
		fflush(listing);
		++lineCount;
	}
}
^L
/* Skip 'n' lines in the listing file.
 * Called with:
 *		n:		number of lines to skip
 */

NewLine(n)
	int n;
{
	if (doListing) {
		if (n + lineCount >= LINES_PER_PAGE)
			Header();
		else while (n--) {
			fputc('\n', listing);
			++lineCount;
		}
	}
}
^L
/* Open the listing file.
 * Called with:
 *		name:		file or device pathname
 * Returns:
 *		status (0 => success)
 */
int
OpenList(name)
	char *name;
{
	int status = 0;

	if (listing) fclose(listing);		/* prior listing file open? */
	if (!(listing = fopen(name, "w"))) {
		status = errno;
		sprintf(conmsg,
				"I can't open the listing file \"%s\", error %d.\n",
				name, status);
		TypeAndSpeak(conmsg);
	}
	else {
		DS2Str(listDate, "%02m/%02d/%02y %02h:%02n:%02s",now); 
		lineCount = LINES_PER_PAGE;
	}
	return status;
}
SHAR_EOF
if test 3040 -ne "`wc -c List.c`"
then
echo shar: error transmitting List.c '(should have been 3040 characters)'
fi
echo shar: extracting MRDates.c '(11553 characters)'
cat << \SHAR_EOF > MRDates.c

/* General routines to provide date support for the DateStamp
 * date format.
 *
 * Author:		Mark R. Rinfret	(mark@unisec.usi.com)
 * Date:		07/18/87
 *
 * This source is released to the public domain by the author, without
 * restrictions.  However, it is requested that you give credit where
 * credit is due and share any bug fixes or enhancements with him.
 *
 * History:		(most recent change first)
 *
 * 12/30/87 -MRR- I experienced some problems with dates past 12/28/87 and
 *                started investigating.  After spending entirely too much
 *                time on the problem, I stole the code from ShowDate in
 *                Rob Peck's book and stuck it in UnpackDS.  I've left my
 *                buggy code intact, but surrounded by conditional compile
 *                brackets which aren't turned on.  The current algorithm
 *                only supports dates back to 03/01/84 while the system
 *                date format begins with 01/01/78.  The current algorithm
 *                is also quite unreadable - too many unexplained constants.
 *
 * 08/20/87 -MRR- Gack!  I wrote this?  In the interests of orthogonality,
 *                I have replaced SetDateStamp with PackDS, the inverse
 *                of UnpackDS.  I have also renamed the package MRDates
 *                to provide a unique name, hopefully relieving conflicts
 *                with other packages providing date/time functions.  The
 *				  UnpackedDS type is now defined in MRDates.h and can
 *                be included by modules which require PackDS and UnpackDS.
 */

/* The following #define MUST be set to inhibit certain declarations in
 * the include file MRDates.h:
 */

#define MRDATES

#include <libraries/dos.h>
#include <exec/memory.h>
#include <ctype.h>
#include <functions.h>
#include ":src/lib/MRDates.h"

/* #define DEBUG */


char *daynames[] = {
	"Sunday", "Monday", "Tuesday", "Wednesday", 
	"Thursday", "Friday", "Saturday"
	};

USHORT monthDays[12] =  {	0,		/* JAN */
							31,		/* FEB */
							59,		/* MAR */
							90,		/* APR */
							120,	/* MAY */
							151,	/* JUN */
							181,	/* JUL */
							212,	/* AUG */
							243,	/* SEP */
							273,	/* OCT */
							304,	/* NOV */
							334 	/* DEC */
							};

char *monthnames[12] = {
	"January", "February", "March", "April", "May", "June",
	"July", "August", "September", "October", "November", "December"
	};

/* Compare two DateStamp values.
 * Called with:
 *		d1,d2:		pointers to DateStamp structs
 * 
 * Returns:
 *		< 0 => d1 < d2
 *		  0 => d1 == d2
 *		> 0 => d1 > d2
 *
 * Note:
 *		This routine makes an assumption about the DateStamp structure,
 *		specifically that it can be viewed as an array of 3 long integers
 *		in days, minutes and ticks order.
 */

int 
CompareDS(d1, d2)
	long *d1, *d2;
{
	USHORT i;
	long compare;

	for (i = 0; i < 3; ++i) {
		if (compare = (d1[i] - d2[i])) {
			if (compare < 0) return -1;
			return 1;
		}
	}
	return 0;						/* dates match */
}

/* Convert a DateStamp to a formatted string.
 * Called with:
 *		fmt:	format string
 *			The format of the format string is very similar to that
 *			for printf, with the exception that the following letters
 *			have special significance:
 *				y => year minus 1900
 *				Y => full year value
 *				m => month value as integer
 *				M => month name
 *				d => day of month (1..31)
 *				D => day name ("Monday".."Sunday")
 *				h => hour in twenty-four hour notation
 *				H => hour in twelve hour notation
 *				i => 12 hour indicator for H notation (AM or PM)
 *				I => same as i
 *				n => minutes	(sorry...conflict with m = months)
 *				N => same as n
 *				s => seconds
 *				S => same as s
 *
 *			All other characters are passed through as part of the normal
 *			formatting process.  The following are some examples with
 *			Saturday, July 18, 1987, 13:53 as an input date:
 *
 *				"%y/%m/%d"			=> 87/7/18
 *				"%02m/%02d/%2y"		=> 07/18/87
 *				"%D, %M %d, %Y"		=> Saturday, July 18, 1987
 *				"%02H:%02m i"		=> 01:53 PM
 *				"Time now: %h%m"	=> Time now: 13:53
 *
 *		str:	string to write date on
 *		d:		pointer to DateStamp structure
 *		
 */
void
DS2Str(str,fmt,d)
	char *str, *fmt; struct DateStamp *d;
{
	UnpackedDS date;
	char fc,*fs,*out;
	USHORT ivalue;
	char new_fmt[256];			/* make it big to be "safe" */
	USHORT new_fmt_lng;
	char *svalue;

	UnpackDS(d, &date);			/* convert DateStamp to unpacked format */

	*str = '\0';				/* insure output is empty */
	out = str;
	fs = fmt;					/* make copy of format string pointer */

	while (fc = *fs++) {		/* get format characters */
		if (fc == '%') {		/* formatting meta-character? */
			new_fmt_lng = 0;
			new_fmt[new_fmt_lng++] = fc;
			/* copy width information */
			while (isdigit(fc = *fs++) || fc == '-')
				new_fmt[new_fmt_lng++] = fc;

			switch (fc) {		/* what are we trying to do? */
			case 'y':			/* year - 1980 */
				ivalue = date.year - 1900;
write_int:
				new_fmt[new_fmt_lng++] = 'd';
				new_fmt[new_fmt_lng] = '\0';
				sprintf(out,new_fmt,ivalue);
				out = str + strlen(str);
				break;
			case 'Y':			/* full year value */
				ivalue = date.year;
				goto write_int;

			case 'm':			/* month */
				ivalue = date.month;
				goto write_int;

			case 'M':			/* month name */
				svalue = monthnames[date.month - 1];
write_str:
				new_fmt[new_fmt_lng++] = 's';
				new_fmt[new_fmt_lng] = '\0';
				sprintf(out,new_fmt,svalue);
				out = str + strlen(str);
				break;

			case 'd':			/* day */
				ivalue = date.day;
				goto write_int;

			case 'D':			/* day name */
				svalue = daynames[d->ds_Days % 7];
				goto write_str;

			case 'h':			/* hour */
				ivalue = date.hour;
				goto write_int;

			case 'H':			/* hour in 12 hour notation */
				ivalue = date.hour;
				if (ivalue >= 12) ivalue -= 12;
				goto write_int;

			case 'i':			/* AM/PM indicator */
			case 'I':
				if (date.hour >= 12)
					svalue = "PM";
				else
					svalue = "AM";
				goto write_str;

			case 'n':			/* minutes */
			case 'N':
				ivalue = date.minute;
				goto write_int;

			case 's':			/* seconds */
			case 'S':
				ivalue = date.second;
				goto write_int;

			default:
				/* We are in deep caca - don't know what to do with this
				 * format character.  Copy the raw format string to the
				 * output as debugging information.
				 */
				new_fmt[new_fmt_lng++] = fc;
				new_fmt[new_fmt_lng] = '\0';
				strcat(out, new_fmt);
				out = out + strlen(out);	/* advance string pointer */
				break;
			}
		}
		else
			*out++ = fc;		/* copy literal character */
	}
	*out = '\0';				/* terminating null */
}

/* Convert a string to a DateStamp.
 * Called with:
 *		str:	string containing date in MM/DD/YY format		
 *		d:		pointer to DateStamp structure
 * Returns:
 *	    status code (0 => success, 1 => failure)
 */

int
Str2DS(str, d)
	char *str; struct DateStamp *d;
{
	register char c;
	int count;
	int i, item;
	UnpackedDS upd;				/* unpacked DateStamp */
	char *s;

	int values[3];
	int value;
	

	s = str;
	for (item = 0; item < 2; ++item) {	/* item = date, then time */
		for (i = 0; i < 3; ++i) values[i] = 0;
		count = 0;
		while (c = *s++) {			/* get date value */
			if (c <= ' ') 
				break;

			if (isdigit(c)) {
				value = 0;
				do {
					value = value*10 + c - '0';
					c = *s++;
				} while (isdigit(c));
				if (count == 3) {
	bad_value:
#ifdef DEBUG
					puts("Error in date-time format.\n");
					printf("at %s: values(%d) = %d, %d, %d\n",
						s, count, values[0], values[1], values[2]);
#endif
					return 1;
				}
				values[count++] = value;
				if (c <= ' ')
					break;
			}
		}							/* end while */
		if (item) {					/* getting time? */
			upd.hour = values[0];
			upd.minute = values[1];
			upd.second = values[2];
		}
		else {						/* getting date? */

/* It's OK to have a null date string, but it's not OK to specify only
 * 1 or 2 of the date components.
 */
			if (count && count != 3)
				goto bad_value;
			upd.month = values[0];
			upd.day = values[1];
			upd.year = values[2];
		}
	}								/* end for */
	PackDS(d,&upd);
	return 0;
}


/* Set a DateStamp structure, given the date/time components.
 * Called with:
 *		d:			pointer to DateStamp
 *      upd:		pointer to UnpackedDS
 */
PackDS(d,upd)
	struct DateStamp *d; UnpackedDS *upd;
{
	USHORT leapyear;
	short year,month,day,hour,minute,second;

	year = upd->year;			/* copy date components to locals */
	month = upd->month;
	day = upd->day;
	hour = upd->hour;
	minute = upd->minute;
	second = upd->second;

	if (year > 1900)
		year = year - 1900;

	leapyear = (year % 4 ? 0 : 1);

	year = year - 78;
	if (month < 1 || month > 12)	/* somebody goofed? */
		month = 1;

	day = day - 1 + monthDays[month-1];
	if (leapyear && (month > 2))
		++day;

	d->ds_Days = year * 365 + (year + 1) / 4 + day;
	d->ds_Minute = hour * 60 + minute;
	d->ds_Tick = second * TICKS_PER_SECOND;
}

/* Unpack a DateStamp structure into an UnpackedDS structure. 
 * Called with:
 *		ds:		pointer to DateStamp structure
 *		du:		pointer to UnpackedDS structure
 */
UnpackDS(ds, du)
	struct DateStamp *ds; UnpackedDS *du;
{
#ifdef OLDCODE
	USHORT i, leap, leapYears, temp, testValue, year;

	/* Compute the year as an offset from 1978.  This is an integer
	 * approach to "year = days / 365.25" (since there are 365.25 days
	 * in a year).
	 */

	year = ( ((ds->ds_Days + 1) * 100) / 36525);
	leapYears = (year + 1) / 4;

	du->year = year + 1978;

#ifdef DEBUG
	printf("\nDays == %ld, Years == %d, Leapyears == %d\n", 
			ds->ds_Days, year, leapYears);
#endif

	/* Is current year a leapyear? */
	leap = ( (year % 4) == 0); 

	/* Get the days into the year. */
	temp = (ds->ds_Days - (year * 365) - leapYears);

	/* Find the month. */

	du->month = 0;
	du->day = 0;
	for (i = 11; i >= 0; --i) {
		testValue = monthDays[i];
		if (i >= 2) testValue += leap;
		if (temp >= testValue) {
			du->month = i + 1;
			du->day = temp - testValue + 1;
			break;
		}
	}
#else
	long n; int m,d,y;

	n = ds->ds_Days - 2251;
	y = (4 * n + 3) / 1461;
	n -= 1461 * (long) y / 4;
	y += 1984;
	m = (5 * n + 2) / 153;
	d = n - (153 * m + 2) / 5 + 1;
	m += 3;
	if (m > 12) {
		++y;
		m -= 12;
	}
	du->year = y;
	du->month = m;
	du->day = d;
#endif
	du->hour = ds->ds_Minute / 60;
	du->minute = ds->ds_Minute % 60;
	du->second = ds->ds_Tick / TICKS_PER_SECOND;

}


#ifdef DEBUG
main()
{
	int compflag;
	char datestr[81], instr[81];
	struct DateStamp *ds, *now;
	UnpackedDS du;

	now = (struct DateStamp *) 
		AllocMem((long) sizeof(struct DateStamp), MEMF_PUBLIC);

	ds = (struct DateStamp *) 
		AllocMem((long) sizeof(struct DateStamp), MEMF_PUBLIC);

	puts("Enter a date string and I will convert it.  To quit, hit RETURN");
	while (1) {
		DateStamp(now);
		UnpackDS(now, &du);

		printf("\nCurrent date and time: %02d/%02d/%02d %02d:%02d:%02d\n",
			du.month,du.day,du.year,du.hour,du.minute,du.second);

		puts("\nEnter the date [and time]:");
		gets(instr);
		if (*instr == '\0') break;
		if (Str2DS(instr,ds))
			puts("Error encountered in input string");
		else {
			DS2Str(datestr, "%02m/%02d/%02y %02h:%02n:%02s", ds);
			puts(datestr);

			DS2Str(datestr, "%D, %M %d, %Y", ds);
			puts(datestr);

			DS2Str(datestr, "The time entered is %02H:%02N %i", ds);
			puts(datestr);

			compflag = CompareDS(ds,now);
			printf("The date input is ");
			if (compflag < 0)
				printf("earlier than");
			else if (compflag == 0)
				printf("the same as");
			else
				printf("later than");
			puts(" the current date.");
		}
	}

	FreeMem(ds, (long) sizeof(struct DateStamp));
	FreeMem(now, (long) sizeof(struct DateStamp));
}
#endif
SHAR_EOF
if test 11553 -ne "`wc -c MRDates.c`"
then
echo shar: error transmitting MRDates.c '(should have been 11553 characters)'
fi
echo shar: extracting MiscRequest.c '(1733 characters)'
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, msg)
	struct Window *window; char *drivespec, *msg;
{
	UBYTE prompt[71];
	ULONG width;

	strncpy(prompt, msg, sizeof(prompt)-strlen(drivespec)-2);
	strncat(prompt,drivespec,(sizeof(prompt)-strlen(prompt)-2));
	width = 8 * strlen(prompt) + 48;
	diskreq_body_text.IText = prompt;
	return AutoRequest(
		window, &diskreq_body_text, &diskreq_pos_text,
		&diskreq_neg_text, NULL, NULL, width, 50L);
}
SHAR_EOF
if test 1733 -ne "`wc -c MiscRequest.c`"
then
echo shar: error transmitting MiscRequest.c '(should have been 1733 characters)'
fi
echo shar: extracting Restore.c '(9995 characters)'
cat << \SHAR_EOF > Restore.c
/* Filename:	restore.c
 * Author:		Mark R. Rinfret
 * Date:		08/02/87
 * Description:	Restore processing module for MRBackup
 *
 * History:		(most recent change first)
 *
 * 12/18/87 -MRR- Restore was using function IsCompressed() to determine
 *                candidates for decompression.  Unfortunately, it also
 *                tried to decompress .ARC and .ZOO files.
 *
 *				  Restore now requests the next disk to be restored from.
 *
 * 11/22/87 -MRR- Modifications for version 2.0.
 *
 * 09/19/87 -MRR- Added NewHomeDir() which creates subdirectories as
 *                necessary when the initial backup path specifies a
 *                subdirectory.
 */


#include "MRBackup.h"
#include ":src/lib/DiskMisc.h"

char fullBackPath[PATH_MAX+1], fullHomePath[PATH_MAX+1];
static BOOL bigFileSeqNbr;		/* >0 => big file sequence number */
BOOL homeIsDevice;		/* true => home is "DH<N>:" */

^L
/* Create a new directory on the home device.
 * Called with:
 *		name:		directory pathname
 * Returns:
 *		false => success
 *		true  => failure
 */
int
NewHomeDir(name)
	char   *name;
{
	char c;
	struct Lock *dirLock;
	int dirLeng;
	int errnum;
	char dirname[256];
	int nameindx = 0, nameleng;

	*dirname = '\0';
	dirLeng = 0;
	nameleng = strlen(name);

	/* Parse the pathname, one directory node at a time, creating
	 * directories as needed.
	 */

	while (nameindx < nameleng) {
		if (nameindx)				/* 2nd - nth pass? */
			dirname[dirLeng++] = '/'; /* directory separator */
		while ((c = name[nameindx++]) && c != '/')
			dirname[dirLeng++] = c;
		dirname[dirLeng] = '\0';	/* terminate with null */
		if (dirLock = (struct Lock *)
			Lock(dirname,SHARED_LOCK)) /* subdir exists? */
			UnLock(dirLock);
		else {						/* create subdirectory */
			if ((dirLock = (struct Lock *) CreateDir(dirname))== NULL){
				if ((errnum = IoErr())== ERROR_DIRECTORY_NOT_EMPTY){
					sprintf(conmsg,
						"Directory %s already exists!\n",dirname);
					TypeAndSpeak(conmsg);
				}
				else {
					sprintf(conmsg,
						"ERROR %d: Unable to create directory %s\n",
						errnum,dirname);
					TypeAndSpeak(conmsg);
					return errnum;
				}
			}
			else
				UnLock(dirLock);
		}
	}								/* endwhile */
	return 0;
}
/* Restore files from floppy disk. */

int
Restore()
{
	int disks=0, status = 0;
	char volumeName[31];

	Speak("And away we go!");

	BreakPath(backPath, srcVol, srcPath);
	strcat(srcVol, ":");

	homeIsDevice = (homePath[strlen(homePath)-1] == ':');
	BreakPath(homePath, destVol, destPath);
	strcat(destVol, ":");

	while ( !status ) {
		TypeAndSpeak("I am ready to read the next disk.\n");
		if (!RequestDisk(mainWindow, srcVol, 
						 "Insert the next disk to be restored in ")) 
			break;

		if (!IsDir(backPath)) {
			TypeAndSpeak(
				"Backup path must be a device or directory name!\n");
			return ERR_ABORT;
		}
		GetVolumeName(srcVol, volumeName);
		SetCurVolumeGadget(volumeName);
		if ( (totalSize = TotalDiskBlocks(destVol)) < 0)
			status = -totalSize;
		else if ((bigFileSeqNbr = GetBigFileInfo(srcVol)) < 0)
			status = -bigFileSeqNbr;
		else {
			status = RestoreFile(srcPath);
		}
		if (bigFileSeqNbr) {		/* we have a big file to restore? */
			status = RestoreBigFile();
		}
/*!!! Need GetErrOpt right here... */

		++disks;
	}
	if (status == 0) {
		if (disks)
			TypeAndSpeak("Your restoration project is completed.\n");
		else
			TypeAndSpeak("Maybe you will let me do it next time.\n");
	}
	else {
		sprintf(conmsg,"Restore terminated with status %d.\n",status);
		TypeAndSpeak(conmsg);
		TypeAndSpeak(
			"Perhaps you should check things out and try it again.\n");
	}

	SetCurVolumeGadget("");
	return status;
}
^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 (!homeIsDevice) strcat(temp, "/");
		strcat(temp, path);
	}
#ifdef DEBUG
	sprintf(debugMsg,"Checking for directory %s\n",temp);
	DebugWrite(debugMsg);
#endif
	if (!(dirLock = (struct Lock *) Lock(temp, SHARED_LOCK))) {
		if ((status = IoErr()) == ERROR_OBJECT_NOT_FOUND) {
#ifdef DEBUG
			sprintf(debugMsg,"Creating directory %s\n",temp);
			DebugWrite(debugMsg);
#endif
			if (status = NewHomeDir(temp))
				return status;
		}
		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;
	unsigned isCompressed;
	struct Lock *lock = NULL;
	USHORT nameLength;
	char *s;
	UBYTE savechar;
	int status = 0;

	if (status = CheckStop()) return status;

	/* Don't restore the big file here...it's done last. */

	if (ThisIsBigFile(path)) 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 (!homeIsDevice) 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 ((sizeLeft = DiskBlocksLeft(destVol)) < 0) {
		status = -sizeLeft;
		sprintf(conmsg,
			"Can't determine disk blocks left on %s; error %d.\n",
			destVol, status);
		TypeAndSpeak(conmsg);
		goto done;
	}
	SetGauge(sizeLeft, totalSize);

	do {
		status = 0;
		if (!(lock = (struct Lock *) Lock(fullBackPath, SHARED_LOCK))) {
			status = IoErr();
			sprintf(conmsg, "RestoreFile can't lock %s; error %d\n",
					fullBackPath, status);
			TypeAndSpeak(conmsg);
			goto checkStatus;
		}

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

		if (fib->fib_DirEntryType > 0) {	/* path is a directory */
			status = RestoreDir(lock, fib, path);
			UnLock(lock);
			lock = NULL;
		}
		else {
			UnLock(lock);
			lock = NULL;

			/* Note: though we can use the function IsCompressed to test
			 * for potentially compressed files in Backup(), ONLY files
			 * compressed with LZW (ending in .z) are candidates for
			 * decompression.  The following test looks for these files
			 * only.
			 */
			isCompressed = false;
			if (doCompress && (s = rindex(fullHomePath,'.'))) {
				/* look for ".z" ONLY! */
				if (!strcmpc(s, ".z")) {
					isCompressed = true;

					/* truncate the destination pathname (remove ".z") */

					nameLength = strlen(fullHomePath);
					fullHomePath[nameLength-2] = '\0';
				}
			}
	/*#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 = (struct 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, since home file is current.\n",
						path);
				WriteConsole(conmsg);
			}
			else {
				if (! (doCompress && isCompressed) ) {
	copyfile:
					sprintf(conmsg,"Copying %s\n", fullBackPath);
					WriteConsole(conmsg);
					status = CopyFile(fullBackPath, fullHomePath);
				}
				else {
					sprintf(conmsg, "Decompressing %s\n", fullBackPath);
					WriteConsole(conmsg);
					if (status = decompress(fullBackPath, fullHomePath)) {
						sprintf(conmsg, 
							"Decompression of %s failed;  status is %d.\n",
							fullBackPath, status);
						TypeAndSpeak(conmsg);
						TypeAndSpeak(
							"I will try to copy the file, instead.\n");
						/* restore ".z" to name */
						fullHomePath[nameLength-2] = '.'; 
						goto copyfile;
					}
					CopyFileDate(fullBackPath, fullHomePath);
				}
			}
	#endif
		}
checkStatus:
		if (status && status != ERR_ABORT) {
			++errorCount;
			SetErrorGadget();
			if (status == ERROR_DISK_FULL) {
					TypeAndSpeak("The destination disk is full.\n");
	/* The following test is pretty kludgy.  It boils down to
	 * "If the drive is a DF<x> device, it's removable, therefore the
	 *  user can recover from this error by inserting a new diskette."
	 */
				if (toupper(destVol[0]) == 'D' && 
				    toupper(destVol[1]) == 'F') {
					TypeAndSpeak(
			"Put a new disk in the destination drive and try again.\n");
				}
				else {
					TypeAndSpeak(
						"You may have to delete some files to continue.");
				}
			}
			status = GetErrOpt(ERR_ABORT|ERR_RETRY_FILE|ERR_IGNORE);
			if (status == ERR_IGNORE) status = 0;
		}
	} while (status == ERR_RETRY_FILE);
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
if test 9995 -ne "`wc -c Restore.c`"
then
echo shar: error transmitting Restore.c '(should have been 9995 characters)'
fi
echo shar: extracting Speech.c '(3297 characters)'
cat << \SHAR_EOF > Speech.c

/* Minimal-capability speech support package.
 * Author:		Mark R. Rinfret
 * Date:		07/30/87
 * Description:
 *
 *		This package provides a quick and dirty means for adding
 *		speech to C language programs.  In order to use it, observe
 *		the following:
 *
 *			1. Call SpeechOn - return parameter of 0 => success.
 *			2. Call Say(<your message in English>) as often as the
 *			   application requires.
 *			3. Call SpeechOff to close libraries/ports/devices.
 */

#include <exec/types.h>
#include <exec/exec.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <devices/narrator.h>
#include <libraries/translator.h>
#include <functions.h>

/*#define DEBUG */


#define PHONEME_MAX 1024L		/* size of phoneme buffer */

/* Which audio channels to use */

BYTE audio_chan[] = {3, 5, 10, 12};

struct Library *TranslatorBase = NULL;
struct narrator_rb voice_io;	/* Narrator I/O request block */
struct MsgPort *voice_port = NULL;
ULONG narrator_status = -1L,translate_error;

UBYTE Phonemes[PHONEME_MAX];	/* Phoneme text buffer */

/******************
 *    ROUTINES    *
 ******************/

/* Enable speech capability. */

SpeechOn(on)
	int on;
{

   if (!(TranslatorBase = (struct Library *)
    	OpenLibrary("translator.library", (long) LIBRARY_VERSION))) {
#ifdef DEBUG
      	DebugWrite("Can't open the translator library!\n");
#endif
fail:
	    SpeechOff();			/* close whatever's open */
      	return 1;
   }

	/* Open a reply port for the narrator. */

	if (!(voice_port = CreatePort(0L, 0L))) {
#ifdef DEBUG
		DebugWrite("Can't create narrator reply port!\n");
#endif
		goto fail;
	}

	voice_io.message.io_Message.mn_ReplyPort = voice_port;

   /* Open the device */
   if ((narrator_status = 
   			OpenDevice("narrator.device", 0L, &voice_io, 0L))) {
#ifdef DEBUG
      DebugWrite("Can't open the Narrator device!\n");
#endif
      goto fail;
   }


   return 0;
}

SpeechOff()
{
	if (voice_port) 
		DeletePort(voice_port);

   	if (!narrator_status)
      	CloseDevice(&voice_io);

   	if (TranslatorBase)
      	CloseLibrary(TranslatorBase);

   	return(0);
}

Say(English)
	char *English;
{
	if (!TranslatorBase) {
		if (SpeechOn())	return 1;
	}

    if (translate_error = 
		Translate(English, (long) strlen(English), 
				  Phonemes, PHONEME_MAX)) {
#ifdef DEBUG
         DebugWrite("Translator error!\n");
#endif
      }

/* Set up the write channel information */

	voice_io.ch_masks = (UBYTE *) (audio_chan);
	voice_io.nm_masks = sizeof(audio_chan);
	voice_io.mouths = 0;
	voice_io.message.io_Command = CMD_WRITE;
	voice_io.message.io_Offset = 0;
	voice_io.message.io_Data = (APTR)Phonemes;
	voice_io.message.io_Message.mn_Length = sizeof(voice_io);
	voice_io.message.io_Length = strlen(Phonemes);
	DoIO(&voice_io);
} 


#ifdef DEBUG
DebugWrite(msg)
	char *msg;
{
	puts(msg);
}

main()
{
	short i;
 	char *s;

static char *text[] = {
	"I am an Amiga computer.  I am currently testing my voice ability.",
	"This is pretty incredible, don't you agree?  I thought you would.",
	"This package is really a set of routines to be used by an application.",
	"All you have to do is call Speech On first, ",
	"then call Say as often as you like.",
	NULL
	};

	SpeechOn();
	for (i = 0;s = text[i]; ++i) {
		Say(s);
		Delay(30L);
	}
	SpeechOff();
}
#endif
SHAR_EOF
if test 3297 -ne "`wc -c Speech.c`"
then
echo shar: error transmitting Speech.c '(should have been 3297 characters)'
fi
echo shar: extracting Timer.c '(4175 characters)'
cat << \SHAR_EOF > Timer.c
/* Timer device support routines.
 * Filename:	Timer.c
 * Author:		Mark R. Rinfret
 * Date:		11/29/87
 *
 */

#include ":src/lib/Timer.h"
#include <exec/memory.h>

/* Allocate and prepare a timer request structure. 
 * Called with:
 *		vBlank:	1 => vertical blanking timer, microhertz timer otherwise
 * Returns:
 *		pointer to timer request or NULL (failed)
 */

struct timerequest *
CreateTimer(vBlank)
	BOOL vBlank;
{
	int status = 0;
	struct MsgPort  *timerPort = NULL;
	struct timerequest *timeRequest = NULL;
	ULONG timerType;

	timerType = (vBlank ? UNIT_VBLANK : UNIT_MICROHZ);

	if ( timerPort = CreatePort(0L, 0L) ) {
		if ( ! (timeRequest = (struct timerequest *)
			AllocMem((long) sizeof(struct timerequest),
					 MEMF_CLEAR | MEMF_PUBLIC) ) ) {
killport:
			DeletePort(timerPort);
		}
		else {
			timeRequest->tr_node.io_Message.mn_Node.ln_Type = NT_MESSAGE;
			timeRequest->tr_node.io_Message.mn_Node.ln_Pri = 0;
			timeRequest->tr_node.io_Message.mn_ReplyPort = timerPort;
			if (OpenDevice(TIMERNAME, timerType, timeRequest, 1L) ) {
				DeleteTimer(timeRequest);
				timeRequest = NULL;
				goto killport;
			}
		}
	}
	return timeRequest;
}

/* Delete a timer request created by CreateTimer().
 * Called with:
 *		timeRequest:	pointer to timer request structure
 */

void
DeleteTimer(timeRequest)
	struct timerequest *timeRequest;
{
	struct MsgPort *msgPort;

	msgPort = timeRequest->tr_node.io_Message.mn_ReplyPort;

	if (timeRequest->tr_node.io_Device) {
		AbortIO(timeRequest);
		GetMsg(msgPort);
		CloseDevice(timeRequest);
	}
	FreeMem(timeRequest, (long) sizeof(timeRequest));
	DeletePort(msgPort);
}

/* Start an asynchronous timer request.  The user application detects
 * the expiration of the timer with Wait(timeRequest->ReplyPort->mp_SigBit)
 * or WaitIO(timeRequest).
 * Called with:
 *		timeRequest:	pointer to timer I/O request block
 *		seconds:		number of seconds in time interval
 *		microSeconds:	number of uSecs in time interval
 *
 * Caution:	seconds, microSeconds are ULONG parameters!
 */

void
StartTimer(timeRequest, seconds, microSeconds)
	struct timerequest *timeRequest; ULONG seconds, microSeconds;
{
	timeRequest->tr_time.tv_secs = seconds;
	timeRequest->tr_time.tv_micro = microSeconds;
	timeRequest->tr_node.io_Command = TR_ADDREQUEST;
	timeRequest->tr_node.io_Flags = 0;
	timeRequest->tr_node.io_Error = 0;
	SendIO(timeRequest);				/* start the timer */
}

/* Stop an asynchronous timer request.
 * Called with:
 *		timeRequest:	pointer to timer I/O request block
 */

void
StopTimer(timeRequest)
	struct timerequest *timeRequest;
{
	AbortIO(timeRequest);
	WaitIO(timeRequest);
}

#ifdef DEBUG
#define SHORTBIT (1L<<shortTimer->tr_node.io_Message.mn_ReplyPort->mp_SigBit)
#define LONGBIT  (1L<<longTimer->tr_node.io_Message.mn_ReplyPort->mp_SigBit)
main()
{
	unsigned intervals;
	struct timerequest *shortTimer, *longTimer;
	ULONG signals;

	/* This simple program example sets up two timers of different
	 * intervals and reports their expirations.
	 */

	puts("This example defines two timers.  The first has an interval of");
	puts("five seconds, while the second has an interval of 10 seconds.");
	puts("The expiration of each timer is reported to the screen until");
	puts("10 intervals have been detected.\n");

	if (! (shortTimer = CreateTimer(0) ) ) {
		puts("Failed to create short interval timer!");
		exit();
	}

	if (! (longTimer = CreateTimer(0) ) ) {
		puts("Failed to create long interval timer!");
		DeleteTimer(shortTimer);
		exit();
	}

	StartTimer(shortTimer, 5L, 0L);
	StartTimer(longTimer, 10L, 0L);
	for (intervals = 0; intervals < 10; ) {
		printf("Begin wait %2d ... ", intervals);
		signals = Wait(SHORTBIT | LONGBIT);
		puts("end wait.");
		if (signals & SHORTBIT) {
			GetMsg(shortTimer->tr_node.io_Message.mn_ReplyPort);
			++intervals;
			puts("  Short interval timer expired.");
			StartTimer(shortTimer, 5L, 0L);
		}
		if (signals & LONGBIT) {
			GetMsg(longTimer->tr_node.io_Message.mn_ReplyPort);
			++intervals;
			puts("   Long interval timer expired.");
			StartTimer(longTimer, 10L, 0L);
		}
	}
	DeleteTimer(shortTimer);
	DeleteTimer(longTimer);
	puts("\nEnd of timer demo.\n");
}
#endif
SHAR_EOF
if test 4175 -ne "`wc -c Timer.c`"
then
echo shar: error transmitting Timer.c '(should have been 4175 characters)'
fi
echo shar: extracting UserPrefs.c '(5916 characters)'
cat << \SHAR_EOF > UserPrefs.c
/* MRBackup user preferences processing.
 * Filename:	UserPrefs.c
 * Date:		08/23/87
 *
 * History:		(most recent change first)
 *
 * 12/20/87 -MRR- Added PutUserPrefs to allow saving new preferences.
 * 11/23/87 -MRR- Version 2.0.
 *
 */

#include "MRBackup.h"

char *fgets();

extern struct Gadget backPathGadget;
extern struct Gadget xcldPathGadget;
extern struct Gadget homePathGadget;
extern struct Gadget listPathGadget;
extern struct Menu Menu1;

#define KEYMAX		20				/* max length of a keyword */
#define NKEYWORD	9				/* number of keywords supported */
#define PREFERENCES_FILE "S:MRBackup.init"

#define KW_HOME			0
#define KW_BACKUP		1
#define KW_LIST			2
#define KW_EXCLUDE		3
#define KW_COMPRESSION	4
#define KW_LISTING      5
#define KW_SPEECH		6
#define KW_FORMAT		7
#define KW_BIGFILES		8

static char *keywords[NKEYWORD] = {
	"home", "backup", "list", "exclude", 
	"compression", "listing", "speech",
	"format","bigfiles"
	};

/* Process the user preferences file. */

GetUserPrefs()
{
	char c;
	USHORT i;
	char keyword[KEYMAX+1];
	short keyindex;
	USHORT keyleng;

	FILE *prefs;
	char s[81];
	char *s1, *s2, *s3;

	if (!(prefs = fopen(PREFERENCES_FILE,"r"))) {
		WriteConsole("I couldn't get your preferences.\n");
		return;
	}

	while (fgets(s, 80, prefs)) {
		if (*s == '#') continue;
		WriteConsole(s);
		s1 = s;
		keyleng = 0;
		while ((c = *s1++) && isalpha(c)) {
			if (keyleng < KEYMAX) keyword[keyleng++] = tolower(c);
			else {
badkey:
				WriteConsole("Keyword error in preferences file.\n");
err:
				goto done;
			}
		}
		keyword[keyleng] = '\0';
		for (keyindex = -1, i = 0; i < NKEYWORD; ++i) {
			if (!strcmp(keyword, keywords[i])) {
				keyindex = i;		/* found it */
				break;
			}
		}
		if (keyindex < 0) goto badkey;
		while (c == ' ' || c == '\t') c = *s1++;
		if (c != '=') {
badsyn:
			WriteConsole("Syntax error in preferences file.\n");
			goto done;
		}

		/* Get the parameter field, minus any leading or trailing
		 * blanks.
		 */

		while ((c = *s1) == ' ' || c == '\t') ++s1; /* skip leading blanks */
		/* Delete trailing blanks. */
		s2 = s3 = s1;
		while (c && c != '\n') {
			if (c != ' ' && c != '\t') s2 = s3;	/* record non-blank end */
			c = *s3++;
		}
		*s2 = '\0';					/* truncate the string here */
		SetUserPref(keyindex, s1);
	}
done:
	fclose(prefs);
}

/* Output a boolean preference setting.
 * Called with:
 *		f:		preferences file descriptor
 *		value:	boolean value
 */

PutBoolPref(f, value)
	FILE *f; unsigned value;
{
	char *s;

	s = (value ? "YES" : "NO");

	fputs(s, f);
}

/* Save the current program settings in the user preferences file. */

PutUserPrefs()
{
	char backupName[81];		/* backup path name */
	unsigned keyIndex;
	FILE *prefs;

	strcpy(backupName, PREFERENCES_FILE);
	strcat(backupName, ".bak");
	if ( CopyFile(PREFERENCES_FILE, backupName) )
		TypeAndSpeak(
		"I couldn't back up the current preferences but I'll go on.\n");

	if (!(prefs = fopen(PREFERENCES_FILE, "w") ) ) {
		sprintf(conmsg,
"I couldn't open the preferences file, %s, for output;\n error %d\n",
		PREFERENCES_FILE, errno);
		return;
	}
	for (keyIndex = 0; keyIndex < NKEYWORD; ++keyIndex) {
		fprintf(prefs,"%s = \t\t", keywords[keyIndex]);
		switch (keyIndex) {
		case KW_HOME:
			fputs(homePath,prefs);
			break;
		case KW_BACKUP:
			fputs(backPath,prefs);
			break;
		case KW_LIST:
			fputs(listPath,prefs);
			break;
		case KW_EXCLUDE:
			fputs(excludePath,prefs);
			break;
		case KW_COMPRESSION:
			PutBoolPref(prefs, doFormat);
			break;
		case KW_LISTING:
			PutBoolPref(prefs, doListing);
			break;
		case KW_SPEECH:
			PutBoolPref(prefs, doSpeech);
			break;
		case KW_FORMAT:
			PutBoolPref(prefs, doFormat);
			break;
		case KW_BIGFILES:
			PutBoolPref(prefs, doBigFiles);
			break;
		default:
			sprintf(conmsg,
"Program error in PutUserPrefs for keyword \"%s\".  Please report this.\n",
					keywords[keyIndex]);
			TypeAndSpeak(conmsg);
		}
		fputs("\n", prefs);
	}
	fclose(prefs);
}

/* Set/Clear checkmarks according to the item being set. 
 * Called with:
 *		thisItem:	menu item number
 *		value:		character string containing some form of YES or NO
 */

BOOL 
SetMenuItem(itemNumber, value)
	USHORT itemNumber; char *value;
{
	BOOL boolean;
	LONG menu;
	struct MenuItem *item;
	short i;

	/* Get pointers and menu numbers for the affected items. */

	menu = SHIFTITEM((long) itemNumber) | SHIFTMENU((long) MENU_FLAGS);
	item = ItemAddress(&Menu1, menu);

	if (item == NULL) {
		WriteConsole("Null menu item in SetMenuItem - call Mark!!\n");
		return false;
	}

	boolean = ( tolower(*value) == 'y' );
	if (boolean) {				/* true? */
		item->Flags |= CHECKED;
	}
	else {						/* false */
		item->Flags &= ~CHECKED;
	}
	return boolean;
}

void
SetStringGadget(gadget, value)
	struct Gadget *gadget; char *value;
{
	UBYTE *gs;							/* pointer to gadget string */

	RemoveGadget(pathWindow, gadget);
	gs = (UBYTE *) GadgetString(gadget);
	strcpy(gs, value);
	ResetStringInfo(gadget->SpecialInfo);
	AddGadget(pathWindow, gadget, -1L);
	DoGadget(pathWindow, GADGETUP, gadget);	/* simulate GADGETUP */
}

/* Set one user preference item. 
 * Called with:
 *		kw:			keyword index
 *		parm:		parameter string
 */

SetUserPref(kw, parm)
	USHORT kw; char *parm;
{

	switch (kw) {
	case KW_HOME:
		SetStringGadget(&homePathGadget, parm);
		break;

	case KW_BACKUP:
		SetStringGadget(&backPathGadget, parm);
		break;

	case KW_LIST:
		SetStringGadget(&listPathGadget, parm);
		break;

	case KW_LISTING:
		doListing = SetMenuItem(ITEM_LIST, parm);
		break;

	case KW_EXCLUDE:
		SetStringGadget(&xcldPathGadget, parm);
		break;

	case KW_COMPRESSION:
		doCompress = SetMenuItem(ITEM_COMPRESS, parm);
		break;

	case KW_SPEECH:
		doSpeech = SetMenuItem(ITEM_SPEECH, parm);
		break;

	case KW_FORMAT:
		doFormat = SetMenuItem(ITEM_FORMAT, parm);
		break;

	case KW_BIGFILES:
		doBigFiles = SetMenuItem(ITEM_BIGFILES, parm);
		break;
	default:
		break;
	}
}

SHAR_EOF
if test 5916 -ne "`wc -c UserPrefs.c`"
then
echo shar: error transmitting UserPrefs.c '(should have been 5916 characters)'
fi
echo shar: extracting sendpkt.c '(1756 characters)'
cat << \SHAR_EOF > sendpkt.c
/* Filename:	sendpkt.c
 * Authors:		Andy Finkel, Phil Lindsay, Commodore-Amiga
 * Date:		06/29/87
 *
 * This package was derived from "touch.c", written by Andy Finkel
 * and Phil Lindsay of Commodore-Amiga.  
 */

#include "exec/types.h"
#include "exec/ports.h"
#include "exec/io.h"
#include "exec/memory.h"
#include "libraries/dos.h"
#include "libraries/dosextens.h"

#ifdef AZTEC_C
#include <functions.h>
#endif

LONG 
sendpkt(id,type,args,nargs)
	struct MsgPort *id;				/* process indentifier ... (handler's
									   message port ) */
	LONG type,						/* packet type ... (what you want 
									   handler to do )   */
	args[],							/* a pointer to argument list */
	nargs;							/* number of arguments in list  */
{

	struct MsgPort     *replyport;
	struct StandardPacket  *packet;

	LONG count,*pargs,res1=NULL;

	if (!(replyport = (struct MsgPort   *) CreatePort(NULL,NULL)))
		return(NULL);

	packet = (struct StandardPacket *)
	           AllocMem((LONG)sizeof(*packet),MEMF_PUBLIC|MEMF_CLEAR);

	if (packet) {
		packet->sp_Msg.mn_Node.ln_Name = &(packet->sp_Pkt);/* link packet */
		packet->sp_Pkt.dp_Link = &(packet->sp_Msg);/* to message    */
		packet->sp_Pkt.dp_Port = replyport;/* set-up reply port   */
		packet->sp_Pkt.dp_Type = type;/* what to do... */

	/* move all the arguments to the packet */
		pargs = &(packet->sp_Pkt.dp_Arg1);/* address of first argument */
		for (count=0; (count < nargs) && (count < 7); count++)
			pargs[count] = args[count];

		PutMsg(id,packet);			/* send packet */
		WaitPort(replyport);		/* wait for packet to come back */
		GetMsg(replyport);			/* pull message */

		res1 = packet->sp_Pkt.dp_Res1;/* get result */
		FreeMem(packet,(LONG)sizeof(*packet));

	}
	DeletePort(replyport);
	return(res1);
}
SHAR_EOF
if test 1756 -ne "`wc -c sendpkt.c`"
then
echo shar: error transmitting sendpkt.c '(should have been 1756 characters)'
fi
echo shar: extracting unixwild.c '(2064 characters)'
cat << \SHAR_EOF > unixwild.c
/* unixwild.c
 * Unix (tm) wildcard support routines. 
 * Created on 08/07/87
 */

/* Text a filename for wildcard content.
 * Called with:
 *		name:		pathname
 * Returns:
 *		0 => no wild characters
 *		1 => 1 or more wild characters
 */
int
iswild(name)
	char *name;
{
	register char c, *s;

	for (s = name; c = *s++; )
		if (c == '*' || c == '?') return 1;

	return 0;
}


/*
 * Compare a wild card name with a normal name.
 * Called with:
 *		wild:		name with "wild" content
 *		name:		"normal" name
 * Returns:
 *		0 => names don't match
 *		1 => names match
 *
 * This source was lifted from Steve Drew's version of Matt Dillon's
 * shell, version 2.06m.
 * 
 */

#define MAXB   8

int
wildcmp(wild,name)
	char *wild,*name;
{
	register char  *w = wild;
	register char  *n = name;
	char   *back[MAXB][2];
	register char   s1,s2;
	int     bi = 0;

	while (*n || *w){
		switch (*w){
			case '*': 
				if (bi == MAXB){
#ifdef DEBUG
					printf("Too many levels of '*'\n");
#endif
					return (0);
				}
				back[bi][0]= w;
				back[bi][1]= n;
				++bi;
				++w;
				continue;
		goback: 
				--bi;
				while (bi >= 0 && *back[bi][1]== '\0')
					--bi;
				if (bi < 0)
					return (0);
				w = back[bi][0]+ 1;
				n = ++back[bi][1];
				++bi;
				continue;
			case '?': 
				if (!*n){
					if (bi)
						goto goback;
					return (0);
				}
				break;
			default: 
				s1 = (*n >= 'A' && *n <= 'Z')?*n - 'A' + 'a' :*n;
				s2 = (*w >= 'A' && *w <= 'Z')?*w - 'A' + 'a' :*w;
				if (s1 != s2){
					if (bi)
						goto goback;
					return (0);
				}
				break;
		}
		if (*n)
			++n;
		if (*w)
			++w;
	}
	return (1);
}
#ifdef DEBUG
char normal[81], wild[81];
main()
{
	puts("Terminate this program by entering 'quit'");
	for (;;) {
		puts("Enter the non-wild pathname:");
		gets(normal);
		if (!strcmp(normal,"quit")) break;
		if (iswild(normal)) {
			puts("No, idiot!  Enter a non-wild filename!");
			continue;
		}
		puts("Enter a wild pathname:");
		gets(wild);
		if (wildcmp(wild,normal))
			puts("Yup, they match.");
		else
			puts("No match here.");
	}
}
#endif
SHAR_EOF
if test 2064 -ne "`wc -c unixwild.c`"
then
echo shar: error transmitting unixwild.c '(should have been 2064 characters)'
fi
#	End of shell archive
exit 0