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