Path: utzoo!mnetor!uunet!husc6!purdue!i.cc.purdue.edu!j.cc.purdue.edu!ain From: ain@j.cc.purdue.edu (Patrick White) Newsgroups: comp.sources.amiga Subject: MRBackup (part 2 of 4) Message-ID: <6291@j.cc.purdue.edu> Date: 28 Jan 88 16:26:02 GMT Organization: PUCC Land, USA Lines: 2479 Keywords: MRBackup, untested, source, part 2 of 4. Approved: ain@j.cc.purdue.edu (Pat White) 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 #include #include #include #include #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 is compared against the current big file * information, if any. If 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 name with * the root name of the information file where data about the big * file is kept, returning it in . */ 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) 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