/* * BACKUP.C * * (C)Copyright 1986-90, Matthew Dillon, All Rights Reserved. * Permission is granted to distribute for non-profit only. * * Thanks to Jan Sven Trabandt for finding some major bugs! * * This program will backup a filesystem or directory, creating a single * output file which can later be RESTORE'd from. It is a quick way to * backup your work to a 'backup' disk. * * backup [options] path path path ... path [-ooutputfile] * * NOTE: if -o is not specified, not output file will be created * * NOTE: Any files/directories containing the keyword NOBACKUP in * its comment field will not be backed up. * * options: * * -0 (Restore): Cause all restored files to be placed * in the current (or -o) directory. No directory * structure is restored at all * * -A ARCHIVE. Clear the archive bit on backed up files * * -U UPDATE. Backup only those files which have the archive bit * cleared. * * -f[#KB] Floppy... actually, this option is used to force the backup * program to automatically split up the backup files in #KB * sections (default 800). I.E. -f with nothing else will * use 800KB chunks. -f200 would use 200KB chunks, etc... * * PLEASE SEE THE DOCS FOR MORE INFORMATION ON FLOPPY BACKUP * AND RESTORE * * -Fvol example: -FDF0: -FDF1: This command specifies the ordering * and number of (floppy) drives to backup to. This allows * one to change floppies in one drive while it is backing up * to another. It automatically cycles through the drives but * you still must specify an initial output file (-ofile) to * determine the base name for the files. * * This command also forces user prompting. * * PLEASE SEE THE DOCS FOR MORE INFORMATION ON FLOPPY BACKUP * AND RESTORE * * -b backup (default if executable name is 'backup') * * -r restore (default if executable nam is 'restore') * * -a append to the destination file. * * -c Compress files during backup * * -Cpat add file pattern to those which will not * be compressed. * * -s Only display directories as we go along. * * -l/-t (either option) Causes a RESTORE to only LIST the files, * not do an actual restore. * * -T (Restore) TIMESTAMP, COMMENT, AND PROTECTION BITS ONLY. * NO files are created. It is assumed the files already * exist. The timestamp and other fields are transfered * from the backup file to the already-restored files (useful * if the files were restored with a copy command that did * non copy the timestamps. Even if the backup file is old, * you can recover most of your time data). * * -S Silent. Don't display files or directories. * * -nKB Set buffer size to use, in KB. * * -v Verbose... Additionaly, display those files which will NOT * be backed up. * * -pPAT only file-paths matching this pattern are backed up. You * may specify more than one '-p' option. * * -dPAT file-paths matching this pattern are NOT backed up. you * may specify more than one '-d' option. * * -ofile Output File * * -Ooffset Set Offset (manual recover on restore) * * --------------------------------------------------------------------- * * destination file format: * * HDR = -Backup Date * VOL = -VOLUME base * DDS = -down-directory * END = <0.B> -up directory * DAT = -datestamp for file * PRO = <4.B> -protection for file * COM = -comment for file * FIL0= -uncompressed file * FIL1= -compressed form #1 * INC = <12.B> -next file is part of an * incomplete file */ #include "defs.h" #include #include ubyte Break; ubyte Restore; ubyte NoStructure; ubyte ListOnly; ubyte ShowFiles = 1; ubyte ShowDirs = 1; ubyte Verbose; ubyte Archive; ubyte Update; ubyte Append; ubyte Compress; ubyte TimeStampOnly; char *OutFile; long BacBytes; short BacCnt = 1; short MyBreak; char DirPath[256]; short DPLen; char *BadFormat; long BufSize = 65536; long BufI; ubyte *Buf; long InBufSize = 8192; long InBufI, InBufN; ubyte *InBuf; char Overide; MLIST VList; /* Volume list (-F), of NODEs */ MLIST DList; /* Directory Stack */ MLIST CSList; /* compression patterns */ MLIST PSList; /* pick patterns */ MLIST DSList; /* don't pick patterns */ long CLen; /* Actual compressed file output length */ MLIST CList; /* List of memory buffers */ SCOMP *CWrite; /* Current memory buffer pointer */ extern void *GetHead(MLIST *); extern void *GetTail(MLIST *); extern void *GetSucc(MNODE *); extern void *GetPred(MNODE *); void AddPattern (MLIST *, char *); void BackupFiles (int, char **); void RestoreFiles(int, char **, long); FIB *GetFileInfo(char *, long *); void FreeFileInfo(FIB *, long); void PushDir (char *, long); int PopDirs (uword); void BackupFlagHasFile(void); long scan_directory(FIB *, long); int mycheckbreak(void); long scan_file (FIB *, long); int match_file (char *); void writeheaders(FIB *); int newfile (void); int read_file (short, char *, long, long); int openoutput (char *, int, int); void oputc (char); void outlwatseek (long, long); void owrite (void *, long); void dumpoutput (void); void dumpcrc (void); void closeoutput (void); long outbytes (void); void outentry (ubyte, int, void *); int openinput (char *, long); void closeinput (void); void seekinputend(void); void setinputbound(long); long oread (void *, long); void AppendCrc (ubyte *, long); int oreadchar (void); void rollbackinput(void); void mputc (char); void mwrite (char *, long); SCOMP *NewSComp (void); void transfer0 (long); void transfer1 (void); int brk (void); void UnCompressFile(long); long CompressFile(char *, long); int brk() { MyBreak = 1; return(0); } main(ac, av) char **av; { register short i, notdone; register char *str; long manOffset = 0; short xac = 0; static char *Xav[256]; BadFormat = "Bad Format"; SetSignal(0, SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D); onbreak(brk); NewList(&VList); NewList(&DList); NewList(&CList); NewList(&PSList); NewList(&DSList); NewList(&CSList); AddPattern(&CSList, "*.Z"); AddPattern(&CSList, "*.ARC"); AddPattern(&CSList, "*.ZOO"); AddPattern(&CSList, "*.LZH"); for (str = av[0] + strlen(av[0]); str >= av[0] && *str != '/' && *str != ':'; --str); ++str; if ((*str|0x20) == 'r') Restore = 1; if (ac == 1) { printf("Backup/Restore V2.06, (c)Copyright 1988,1989 Matthew Dillon, All Rights Reserved\n", str); printf("Backup/Restore is useful for HD backup and file transfers\n"); printf("%s -rbactlvASTU -d -p -f[#kb] -F -n<#kb> -ofile \n", str); puts("\nread the docs for more info. Example for use w/ file transfer:"); puts("\t1> backup -c file/dir -oout.bak -create archive"); puts("\t1> restore -t out.bak -list archive"); puts("\t1> restore out.bak [-odest] -restore archive"); } for (i = 1; i < ac; ++i) { str = av[i]; if (*str != '-') { if (xac == sizeof(Xav)/sizeof(Xav[0])) { printf("Maximum %d files/dirs specifiable on command line\n", sizeof(Xav)/sizeof(Xav[0])); exit(15); } Xav[xac++] = str; continue; } notdone = 1; ++str; while (notdone && *str) { switch(*str) { case '0': NoStructure = 1; break; case 'r': Restore = 1; break; case 'b': Restore = 0; break; case 'a': Append = 1; break; case 'c': Compress = 1; break; case 'd': AddPattern(&DSList, str + 1); notdone = 0; break; case 'f': BacBytes = 800 * 1024; if (str[1] >= '0' && str[1] <= '9') { BacBytes = atoi(str+1) * 1024; notdone = 0; } break; case 'F': { /* strlen(str+1)+1 */ char *name = (str[1]) ? str + 1 : av[++i]; NODE *node = malloc(sizeof(NODE) + strlen(name) + 1); node->ln_Name = (char *)(node + 1); strcpy((char *)(node + 1), name); AddTail(&VList, node); } notdone = 0; break; case 'O': { char *name = (str[1]) ? str + 1 : av[++i]; manOffset = atoi(name); } Overide = 1; notdone = 0; break; case 'n': { char *name = (str[1]) ? str + 1 : av[++i]; BufSize = atoi(name) * 1024; } if (BufSize <= 0) BufSize = 65536; notdone = 0; break; case 'o': { char *name = (str[1]) ? str + 1 : av[++i]; OutFile = name; } notdone = 0; break; case 'p': { char *name = (str[1]) ? str + 1 : av[++i]; AddPattern(&PSList, name); } notdone = 0; break; case 's': ShowFiles = 0; break; case 't': case 'l': ListOnly = 1; break; case 'v': Verbose= 1; break; case 'A': Archive= 1; break; case 'C': { char *name = (str[1]) ? str + 1 : av[++i]; AddPattern(&CSList, name); } notdone = 0; break; case 'S': ShowFiles = 0; ShowDirs = 0; break; case 'T': TimeStampOnly = 1; break; case 'U': Update = 1; break; default: puts("failure, backup w/ no args for help"); exit(20); } ++str; } } if (i > ac) { puts("Expected argument to last option!"); exit(20); } Buf = malloc(BufSize); if (Buf == NULL) { printf("Unable to malloc %ld bytes\n", BufSize); exit(20); } if (ListOnly) InBufSize = 512; /* small buffer to avoid read overhead */ /* since we are skipping the meat */ InBuf = malloc(InBufSize); if (InBuf == NULL) { printf("Unable to malloc %ld bytes\n", InBufSize); exit(20); } if (Restore) RestoreFiles(xac, Xav, manOffset); else BackupFiles(xac, Xav); return(0); } void AddPattern(list, str) MLIST *list; char *str; { register NODE *node = malloc(sizeof(NODE)); AddTail(list, node); node->ln_Name = str; } long SaveLock; void BackupFiles(ac, av) char **av; { register short i; register char *str, *ptr; char notdone; if (OutFile && openoutput(OutFile, Append, ((BacBytes)?1:0)) == 0) exit(20); if (OutFile) { /* write header */ DATESTAMP Date; DateStamp(&Date); outentry(XHDR, sizeof(DATESTAMP), &Date); } SaveLock = CurrentDir(DupLock(((PROC *)FindTask(NULL))->pr_CurrentDir)); for (i = 0; i < ac; ++i) { str = av[i]; /* * Push DDS entries for each name segment of the path */ notdone = 1; while (notdone) { for (ptr = str; *ptr && *ptr != ':' && *ptr != '/'; ++ptr); switch(*ptr) { case '/': /* normal directory */ *ptr = 0; PushDir(str, XDDS); str = ptr + 1; *ptr = '/'; break; case ':': /* volume */ *ptr = 0; PushDir(str, XVOL); str = ptr + 1; *ptr = ':'; break; default: /* directory or file */ { char *path = av[i]; FIB *fib; long lock; if (fib = GetFileInfo(path, &lock)) { if (fib->fib_DirEntryType > 0) { if (str[0]) PushDir(str, XDDS); lock = scan_directory(fib, lock); if (str[0]) PopDirs(1); } else { lock = scan_file(fib, lock); } FreeFileInfo(fib, lock); } else { printf("Unable to get info for %s\n", av[i]); } } notdone = 0; break; } } PopDirs((uword)-1); } UnLock(CurrentDir(SaveLock)); if (OutFile) closeoutput(); } DATESTAMP Date; ulong Crc; /* compare w/ */ ulong CrcGen; ulong CrcSeek; /* (backup) where in file is CrcGen lw? */ char CrcEna; char Comment[256]; char Scr[256]; long Protection; long IncSize; /* Size of entire file */ long IncSeek; /* Seek offset into file */ long IncLen; /* # bytes in this segment */ void RestoreFiles(ac, av, startoffset) char **av; long startoffset; { register short i; register char *str; char notdone; char havedate; char havecrc; char havepro; char havecom; char haveinc; long bytes; long actual; long baselock; long lock; PROC *proc = (PROC *)FindTask(NULL); if (OutFile) { lock = Lock(OutFile, SHARED_LOCK); if (lock == NULL && (lock = CreateDir(OutFile))) { UnLock(lock); lock = Lock(OutFile, SHARED_LOCK); } } else { lock = Lock("", SHARED_LOCK); } if (!lock) { printf("Unable to lock/create %s\n", (OutFile) ? OutFile : ""); return; } baselock = lock; SaveLock = CurrentDir(DupLock(baselock)); for (i = 0; i < ac; ++i) { str = av[i]; lock = CurrentDir(SaveLock); if (openinput(str, startoffset) == 0) { startoffset = 0; printf("Unable to open %s for input\n", str); CurrentDir(lock); continue; } startoffset = 0; CurrentDir(lock); lock = DupLock(baselock); UnLock(CurrentDir(lock)); notdone = 1; havedate = havecrc = havepro = havecom = haveinc = 0; while (notdone) { short c = oreadchar(); short l = oreadchar(); recover: switch(c) { case -1: /* EOF */ notdone = 0; break; case 0: /* NUL */ case 'z'&0x1F: /* ^Z */ if (Overide == 0) notdone = 0; break; case XVOL: case XDDS: oread(Scr, l); Scr[l] = 0; /* * In the replacement of the first * argument case, we are already in * the proper directory. */ if (EMPTYLIST(DList) && OutFile) { register short j = strlen(OutFile); UnLock(CurrentDir(DupLock(SaveLock))); c = XDDS; strcpy(Scr, OutFile); if (j--) { if (OutFile[j] == ':') { Scr[j] = 0; c = XVOL; } if (OutFile[j] == '/') Scr[j] = 0; } } PushDir(Scr, c); if (ShowDirs) printf("%-40s\n", DirPath); if (ListOnly) break; if (NoStructure == 0) { if (c == XVOL) { lock = Lock(DirPath, SHARED_LOCK); /* DirPath incs ':' */ } else { lock = Lock(Scr, SHARED_LOCK); if (lock == NULL && (lock = CreateDir(Scr))) { UnLock(lock); lock = Lock(Scr, SHARED_LOCK); } } } { SDIR *sd = GetTail(&DList); sd->HaveFile = 1; /* don't remove dir */ } if (NoStructure == 0) { if (lock == NULL) { printf("Unable to create directory %s\n", Scr); notdone = 0; } else { UnLock(CurrentDir(lock)); } } break; case XEND: { SDIR *sd = GetTail(&DList); ubyte type; c = 1; if (!sd) break; type = sd->Type; strcpy(Scr, sd->Element); c = PopDirs(1); if (ListOnly) break; if (type == XVOL) /* no parent directory */ break; if (NoStructure == 0) { lock = ParentDir(proc->pr_CurrentDir); if (lock == NULL) { puts("Unable to ParentDir!"); notdone = 0; } else { UnLock(CurrentDir(lock)); /* if (c == 0) DeleteFile(Scr); */ } } } break; case XCRC: if (l != 4) { puts(BadFormat); notdone = SkipBad(&c, &l); goto recover; } oread(&Crc, l); havecrc = 1; break; case XDAT: if (l != sizeof(DATESTAMP)) { puts(BadFormat); notdone = SkipBad(&c, &l); goto recover; } oread(&Date, l); havedate = 1; break; case XPRO: if (l != 4) { puts("Expected 4 bytes for protection"); notdone = SkipBad(&c, &l); goto recover; } oread(&Protection, l); havepro = 1; break; case XCOM: oread(Comment, l); Comment[l] = 0; havecom = 1; break; case XFIL0: case XFIL1: if (!havepro) Protection = 0; if (!havecom) Comment[0] = 0; if (!havedate) DateStamp(&Date); if (!haveinc) IncSize = 0; oread(Scr, l); Scr[l] = 0; oread(&bytes, 4); /* length of file */ actual = bytes; if (c == XFIL1) { oread(&actual, 4); bytes -= 4; } setinputbound(bytes); { short res = read_file(c, Scr, bytes, actual); seekinputend(); if (res < 0) goto bend; } if (ListOnly) goto bend; if (Archive) SetProtection(Scr, Protection|FIBF_ARCHIVE); else SetProtection(Scr, Protection&~FIBF_ARCHIVE); if (havecrc && Crc != CrcGen) printf("WARNING, Crc failed! %s\n", Scr); if (havedate) setfiledate(Scr, &Date); if (havecom && Comment[0]) SetComment(Scr, Comment); bend: havecom = havecrc = havedate = havepro = haveinc = 0; break; case XHDR: if (l != sizeof(DATESTAMP)) { puts("expected sizeof datestamp"); notdone = SkipBad(&c, &l); goto recover; } oread(&Date, l); printf(" ----- BACKUP ----- BACKUP DATE: %s\n", datetos(&Date, Scr, NULL)); break; case XINC: if (l != 12) { puts("expected 12 bytes for XINC"); notdone = SkipBad(&c, &l); goto recover; } oread(&IncSize, 4); oread(&IncSeek, 4); oread(&IncLen, 4); haveinc = 1; break; default: printf("Unknown Record Type: %02x\n", c); notdone = SkipBad(&c, &l); goto recover; } setinputbound(-1); if (mycheckbreak()) { Break = 1; notdone = 0; break; } } if (Break) break; } UnLock(baselock); UnLock(CurrentDir(SaveLock)); } SkipBad(pc, pl) short *pc; short *pl; { long skip = 0; long offset = otellread(); short notdone = 1; short c; short l; loop: while ((c = oreadchar()) >= 0 && c != XDAT) ++skip; if (c == XDAT) { l = oreadchar(); ++skip; if (l != sizeof(DATESTAMP)) goto loop; } printf("Error at offset %d, skipping %d bytes", offset, skip); if (c < 0) { printf(" (EOF reached!)"); notdone = 0; } printf("\n"); *pc = c; *pl = l; return((int)notdone); } FIB * GetFileInfo(path, plock) char *path; long *plock; { register long lock; register FIB *fib; *plock = NULL; if (lock = Lock(path, SHARED_LOCK)) { if (fib = malloc(sizeof(FIB))) { if (Examine(lock, fib)) { *plock = lock; return(fib); } free(fib); } UnLock(lock); } return(NULL); } void FreeFileInfo(fib, lock) FIB *fib; long lock; { if (fib) free(fib); if (lock) UnLock(lock); } void PushDir(element, type) char *element; { register SDIR *sd = malloc(sizeof(SDIR)); register char *str = malloc(strlen(element)+1); strcpy(str, element); sd->Type = type; sd->HaveFile = 0; sd->Element = str; AddTail(&DList, sd); strcat(DirPath+DPLen, str); if (type == XVOL) strcat(DirPath+DPLen, ":"); else if (type == XDDS) strcat(DirPath+DPLen, "/"); DPLen += strlen(DirPath+DPLen); } int PopDirs(num) uword num; { register SDIR *sd, *sp; char lasthave = 0; while (num && (sd = GetTail(&DList))) { lasthave |= sd->HaveFile; if (!Restore && sd->HaveFile) /* MUST write end-block */ outentry(XEND, 0, NULL); if (sp = GetPred(sd)) sp->HaveFile |= sd->HaveFile; Remove(sd); DPLen -= strlen(sd->Element) + 1; if (DPLen < 0) { puts("DPLEN ERROR"); DPLen = 0; } DirPath[DPLen] = 0; free(sd->Element); free(sd); --num; } return((int)lasthave); } void BackupFlagHasFile() { register SDIR *sd; SDIR *sdb = NULL; for (sd = GetTail(&DList); sd; sd = GetPred(sd)) { if (sd->HaveFile == 0) sdb = sd; } for (sd = sdb; sd; sd = GetSucc(sd)) { sd->HaveFile = 1; outentry(sd->Type, strlen(sd->Element), sd->Element); } } /* * SCAN_DIRECTORY() (CORE OF BACKUP) */ long scan_directory(dirfib, dirlock) FIB *dirfib; long dirlock; { register FIB *fib; long lock; long save = CurrentDir(dirlock); if (Update == 0) /* force save entire tree */ BackupFlagHasFile(); while (ExNext(dirlock, dirfib) && (fib = GetFileInfo(dirfib->fib_FileName, &lock))) { if (fib->fib_DirEntryType > 0) { PushDir(fib->fib_FileName, XDDS); if (ShowDirs) printf("%-40s (DIR)\n", DirPath); if (NoBack(fib) == 0) lock = scan_directory(fib, lock); PopDirs(1); } else { if (NoBack(fib) == 0) lock = scan_file(fib, lock); } FreeFileInfo(fib, lock); if (Break || mycheckbreak()) { Break = 1; break; } } CurrentDir(save); return(dirlock); } int mycheckbreak() { if (MyBreak || (SetSignal(0, (SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D)) & (SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D))) { puts(" ***** BREAK *****"); return(1); } return(0); } /* * SCAN_FILE() * * If the file is accepted, write out pending directory entries, do * compression if any, and write out the file. */ long scan_file(fib, lock) FIB *fib; long lock; { long save; char dbuf[32]; strcat(DirPath, fib->fib_FileName); if (Update && (fib->fib_Protection & FIBF_ARCHIVE)) goto nomatch; if (!match_file(DirPath)) goto nomatch; if (ShowFiles) printf("%-40s %6ld %s\n", DirPath, fib->fib_Size, datetos(&fib->fib_Date, dbuf, NULL)); BackupFlagHasFile(); if (OutFile) { save = CurrentDir(lock); if (openinput("", 0) == 0) { CurrentDir(save); printf("Unable to open %s\n", fib->fib_FileName); goto nomatch; } if (Compress && CompressFile(fib->fib_FileName, fib->fib_Size)) { if (OutFile && BacBytes && outbytes() + CLen > BacBytes) { if (newfile() == 0) goto skip1; } writeheaders(fib); outentry(XFIL1, strlen(fib->fib_FileName), fib->fib_FileName); CLen += 4; owrite(&CLen, 4); CLen -= 4; owrite(&fib->fib_Size, 4); transfer1(); } else { if (OutFile && BacBytes && outbytes() + fib->fib_Size > BacBytes) { if (newfile() == 0) goto skip1; } if (Compress) rollbackinput(); writeheaders(fib); outentry(XFIL0, strlen(fib->fib_FileName), fib->fib_FileName); owrite(&fib->fib_Size, 4); transfer0(fib->fib_Size); } outlwatseek(CrcSeek, CrcGen); skip1: closeinput(); CurrentDir(save); } if (Break) goto nomatch; if (Archive && !(fib->fib_Protection & FIBF_ARCHIVE)) { if (save = ParentDir(lock)) { UnLock(lock); save = CurrentDir(save); SetProtection(fib->fib_FileName, fib->fib_Protection | FIBF_ARCHIVE); lock = CurrentDir(save); } } DirPath[DPLen] = 0; return(lock); nomatch: if (Verbose) printf("%-40s (NOT ACCEPTED)\n", DirPath, fib->fib_Size, datetos(&fib->fib_Date, dbuf, NULL)); DirPath[DPLen] = 0; return(lock); } int match_file(name) char *name; { register NODE *node; for (node = (NODE *)PSList.mlh_Head; node->ln_Succ; node = node->ln_Succ) { if (WildCmp(node->ln_Name, name)) break; } if (node->ln_Succ == NULL && !EMPTYLIST(PSList)) return(0); for (node = (NODE *)DSList.mlh_Head; node->ln_Succ; node = node->ln_Succ) { if (WildCmp(node->ln_Name, name)) return(0); } return(1); } void writeheaders(fib) register FIB *fib; { long dummy = 0; outentry(XDAT, sizeof(DATESTAMP), &fib->fib_Date); outentry(XPRO, 4, &fib->fib_Protection); if (fib->fib_Comment[0]) outentry(XCOM, strlen(fib->fib_Comment), fib->fib_Comment); outentry(XCRC, 4, &dummy); CrcSeek = outbytes() - 4; } /* * (1) Write out XEND's to finish this archive, * (2) Open a new output file * (3) Write out XDDS's to build back to the current position */ int newfile() { { register SDIR *sd; for (sd = GetTail(&DList); sd; sd = GetPred(sd)) { if (sd->HaveFile) outentry(XEND, 0, NULL); } } ++BacCnt; if (OutFile && openoutput(OutFile, Append, 1) == 0) { Break = 1; return(0); } { register SDIR *sd; DATESTAMP Date; DateStamp(&Date); outentry(XHDR, sizeof(DATESTAMP), &Date); for (sd = GetHead(&DList); sd; sd = GetSucc(sd)) { if (sd->HaveFile) outentry(sd->Type, strlen(sd->Element), sd->Element); } } return(1); } int read_file(type, fname, inbytes, outbytes) short type; char *fname; long inbytes; long outbytes; { char dbuf[32]; long save; strcat(DirPath, fname); if (!match_file(DirPath)) { if (Verbose) printf("%-40s (NOT ACCEPTED)\n", DirPath); goto nomatch; } if (ShowFiles) printf("%-40s %6ld %6ld %s %s\n", DirPath, inbytes, outbytes, datetos(&Date, dbuf, NULL), Comment); if (ListOnly) goto nomatch; if (TimeStampOnly) goto matchskip; openoutput(fname, 0, 0); switch(type) { case XFIL0: transfer0(inbytes); break; case XFIL1: CrcGen = 0; CrcEna = 1; UnCompressFile(inbytes); CrcEna = 0; save = CrcGen; transfer1(); /* ??? needed ??? */ CrcGen = save; break; } closeoutput(); matchskip: DirPath[DPLen] = 0; return(1); nomatch: DirPath[DPLen] = 0; return(-1); } /* * FILE SUPPORT */ static int Infd = -1; static int Outfd = -1; static long OutBytes; int openoutput(name, append, enabtail) char *name; { char *ptr = name; static NODE *VNode; /* Volume node */ long lock; extern int errno; if (Outfd >= 0) { dumpoutput(); close(Outfd); Outfd = -1; } if (enabtail) { if (VNode) VNode = GetSucc(VNode); if (!VNode) VNode = GetHead(&VList); if (VNode) { ptr = malloc(strlen(VNode->ln_Name)+strlen(name)+8); sprintf(ptr, "%s%s.%02ld", VNode->ln_Name, name, BacCnt); } else { ptr = malloc(strlen(name)+8); sprintf(ptr, "%s.%02ld", name, BacCnt); } } OutBytes = 0; while (GetHead(&VList)) { short c; short d; fprintf(stderr, "Ready for %s (y=go,n=abort) -", ptr); fflush(stderr); if ((c = getc(stdin)) == EOF) { fprintf(stderr, "EOF, aborted\n"); c = 'n'; } while ((d = getc(stdin)) != EOF && d != '\n'); if ((c|0x20) == 'y') break; if ((c|0x20) == 'n') goto skip; } if (enabtail && SaveLock) /* original directory */ lock = CurrentDir(SaveLock); if (append) { Outfd = open(ptr, O_WRONLY|O_CREAT|O_APPEND); if (Outfd >= 0) { OutBytes = lseek(Outfd, 0L, 2); /* lseek(Outfd, 0L, 0); */ } } else { Outfd = open(ptr, O_WRONLY|O_CREAT|O_TRUNC); } if (enabtail && SaveLock) /* back to before */ CurrentDir(lock); if (Outfd < 0) printf("Unable to open output file %s (%ld)\n", ptr, errno); skip: BufI = 0; if (enabtail) free(ptr); return(Outfd >= 0); } void oputc(v) char v; { ++OutBytes; if (Outfd >= 0) { if (BufI == BufSize) dumpoutput(); Buf[BufI++] = v; } } void outlwatseek(pos, lw) long pos; long lw; { long index = BufI + pos - OutBytes; /* index into buffer */ if (index > BufI - 4) { puts("SW ERROR"); return; } if (index >= 0) { Buf[index+0] = lw >> 24; Buf[index+1] = lw >> 16; Buf[index+2] = lw >> 8; Buf[index+3] = lw; } else { /* uh oh */ if (Outfd >= 0) { long old = lseek(Outfd, 0, 1); /* save current pos */ lseek(Outfd, pos, 0); /* seek back */ write(Outfd, (char *)&lw, 4); /* write data */ lseek(Outfd, old, 0); /* seek to end */ } } } void owrite(buf, n) void *buf; long n; { register long avail; OutBytes += n; if (Outfd >= 0) { while (BufI + n > BufSize) { avail = BufSize - BufI; movmem(buf, Buf + BufI, avail); n -= avail; buf = (void *)((char *)buf + avail); BufI = BufSize; dumpoutput(); } movmem(buf, Buf + BufI, n); BufI += n; } } void dumpoutput() { if (Outfd >= 0 && BufI) { write(Outfd, Buf, BufI); BufI = 0; } } void closeoutput() { if (Outfd >= 0) { dumpoutput(); close(Outfd); Outfd = -1; } } long outbytes() { return(OutBytes); } /* * */ void outentry(type, len, buf) ubyte type; int len; void *buf; { OutBytes += len + 2; if (Outfd >= 0) { if (BufI + len + 2 >= BufSize) dumpoutput(); Buf[BufI+0] = type; Buf[BufI+1] = len; movmem(buf, Buf+BufI+2, len); BufI += len + 2; } } ulong OMax; int openinput(name, offset) char *name; long offset; { if (Infd >= 0) close(Infd); Infd = open(name, O_RDONLY); InBufI = InBufN = 0; OMax = -1; if (Infd >= 0 && offset) lseek(Infd, offset, 0); return(Infd >= 0); } void closeinput() { if (Infd >= 0) close(Infd); Infd = -1; } void seekinputend() { register long inbuf = InBufI - InBufN; register long forward = OMax; if (forward > inbuf) { lseek(Infd, forward - inbuf, 1); OMax = InBufI = InBufN = 0; return; } InBufN += forward; } void setinputbound(max) long max; { OMax = max; } long oread(buf, n) void *buf; long n; { long x = 0; long avail; if (Infd < 0) return(0); if (n > OMax) n = OMax; while (n > (avail = InBufI - InBufN)) { if (InBufN == -1) return(0); movmem(InBuf + InBufN, buf, avail); AppendCrc(buf, avail); OMax-= avail; n -= avail; buf = (void *)((char *)buf + avail); x += avail; InBufI = read(Infd, InBuf, InBufSize); InBufN = 0; if (InBufI <= 0) { InBufI = 0; return(x); } } movmem(InBuf + InBufN, buf, n); AppendCrc(buf, n); InBufN += n; x += n; OMax -= n; return(x); } long otellread() { long pos = 0; if (Infd >= 0) pos = lseek(Infd, 0L, 1) - (InBufI - InBufN); return(pos); } int oreadchar() { if (!OMax || Infd < 0) return(-1); if (InBufN == InBufI) { if (InBufN < 0) return(EOF); InBufI = read(Infd, InBuf, InBufSize); InBufN = 0; if (InBufI == 0) { InBufN = InBufI = -1; return(-1); } } if (CrcEna) CrcGen = (CrcGen << 3) ^ InBuf[InBufN] ^ (CrcGen >> 29); return((int)InBuf[InBufN++]); } void AppendCrc(buf, bytes) register ubyte *buf; register long bytes; { if (CrcEna) { while (bytes--) CrcGen = (CrcGen << 3) ^ *buf++ ^ (CrcGen >> 29); } } void rollbackinput() { if (Infd >= 0) lseek(Infd, 0L, 0); InBufI = InBufN = 0; } void mputc(v) char v; { register SCOMP *sc = CWrite; ++CLen; if (sc->N == sc->Bytes) { sc = GetSucc(sc); if (sc == NULL); sc = NewSComp(); if (sc == NULL) { puts("SCOMP FAILED"); return; } sc->N = 0; CWrite = sc; } ((char *)(sc + 1))[sc->N++] = v; } void mwrite(buf, n) char *buf; long n; { register SCOMP *sc = CWrite; register long avail; CLen += n; while ((avail = sc->Bytes - sc->N) < n) { movmem(buf, (char *)(sc + 1) + sc->N, avail); buf += avail; n -= avail; sc->N = sc->Bytes; sc = GetSucc(sc); if (sc == NULL) sc = NewSComp(); if (sc == NULL) { puts("SCOMP FAILED"); return; } sc->N = 0; } movmem(buf, (char *)(sc + 1) + sc->N, n); sc->N += n; CWrite = sc; } SCOMP * NewSComp() { register SCOMP *sc = malloc(sizeof(SCOMP) + 8192); if (sc) { sc->Bytes = 8192; sc->N = 0; AddTail(&CList, sc); } return(sc); } void transfer0(n) long n; { register long len; if (Outfd < 0) return; CrcGen = 0; CrcEna = 1; for (len = BufSize - BufI; n; len = BufSize - BufI) { if (len == 0) { dumpoutput(); len = BufSize; } if (n < len) len = n; oread(Buf + BufI, len); BufI += len; n -= len; OutBytes += len; } CrcEna = 0; } /* * Compression Routines * * transfer1() : Backup: copy compression buffer to output file */ void transfer1() { register long len; register SCOMP *sc = GetHead(&CList); register ubyte *ptr; long n = CLen; if (Outfd < 0) return; CrcGen = 0; CrcEna = 1; for (sc = GetHead(&CList); sc && n; sc = GetSucc(sc)) { len = sc->Bytes; ptr = (ubyte *)(sc + 1); if (n < len) len = n; n -= len; AppendCrc(ptr, len); while (len > BufSize - BufI) { movmem(ptr, Buf + BufI, BufSize - BufI); ptr += BufSize - BufI; len -= BufSize - BufI; OutBytes += BufSize - BufI; BufI = BufSize; dumpoutput(); } movmem(ptr, Buf + BufI, len); BufI += len; OutBytes += len; } CrcEna = 0; if (n) puts("Unexpected EOF in compression file"); } /* * NoBack() returns true if 'NOBACK' is found in the fib_Comment * field. */ NoBack(fib) FIB *fib; { char *str = fib->fib_Comment; while (*str) { if (*str == 'N' && strncmp(str, "NOBACK", 6) == 0) return(1); ++str; } return(0); } /* * quick hack to fix some macro bugs... */ long CTOB(cptr) void *cptr; { return((long)cptr >> 2); } void * BTOC(bptr) long bptr; { return((void *)(bptr << 2)); } void * GetHead(list) MLIST *list; { MNODE *node = list->mlh_Head; if (node->mln_Succ) return(node); return(NULL); } void * GetTail(list) MLIST *list; { MNODE *node = list->mlh_TailPred; if (node->mln_Pred) return(node); return(NULL); } void * GetSucc(node) MNODE *node; { node = node->mln_Succ; if (node->mln_Succ) return(node); return(NULL); } void * GetPred(node) MNODE *node; { node = node->mln_Pred; if (node->mln_Pred) return(node); return(NULL); }