/* * BACKUP.C * * (C)Copyright 1986-88, 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 * * options: * * -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 * * -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 * * --------------------------------------------------------------------- * * 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 #include #include #define SDIR struct _SDIR #define SCOMP struct _SCOMP SCOMP { MNODE Node; uword Bytes; /* allocated bytes */ uword N; }; SDIR { MNODE Node; /* node in dir tree */ ubyte Type; /* XDDS or XVOL */ ubyte HaveFile; /* Something was backed up in here */ char *Element; /* path element */ }; #define XVOL 0x01 #define XDDS 0x02 #define XEND 0x03 #define XDAT 0x04 #define XPRO 0x05 #define XCOM 0x06 #define XFIL0 0x87 #define XFIL1 0x88 #define XHDR 0x09 #define XINC 0x0A ubyte Break; ubyte Restore; 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; uword NumPicks; uword NumDels; char *Picks[32]; char *Dels[32]; char DirPath[256]; short DPLen; long BufSize = 65536; long BufI; ubyte *Buf; long InBufSize = 8192; long InBufI, InBufN; ubyte *InBuf; MLIST VList; /* Volume list (-F), of NODEs */ MLIST DList; /* Directory Stack */ long CLen; /* Actual compressed file output length */ MLIST CList; /* List of memory buffers */ SCOMP *CWrite; /* Current memory buffer pointer */ extern int Enable_Abort; extern void seekinputend(); extern FIB *GetFileInfo(); extern SCOMP *NewSComp(); extern void *malloc(), *GetHead(), *GetTail(), *GetSucc(), *GetPred(); main(ac, av) char *av[]; { register short i, notdone; register char *str; NewList(&VList); NewList(&DList); NewList(&CList); Enable_Abort = 0; 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.01, (c)Copyright 1988 Matthew Dillon, All Rights Reserved\n", str); printf("%s -rbactlvASTU -d -p -f[#kb] -F -n<#kb> -ofile\n", str); } for (i = 1; i < ac; ++i) { str = av[i]; if (*str != '-') continue; notdone = 1; ++str; while (notdone && *str) { switch(*str) { case 'r': Restore = 1; break; case 'b': Restore = 0; break; case 'a': Append = 1; break; case 'c': Compress = 1; break; case 'd': Dels[NumDels++] = 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 */ register NODE *node = malloc(sizeof(NODE)+strlen(str)); node->ln_Name = (char *)(node+1); strcpy(node+1, str+1); AddTail(&VList, node); } notdone = 0; break; case 'n': BufSize = atoi(str+1) * 1024; if (BufSize <= 0) BufSize = 65536; notdone = 0; break; case 'o': OutFile = str + 1; notdone = 0; break; case 'p': Picks[NumPicks++] = str + 1; 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 'S': ShowFiles = 0; ShowDirs = 0; break; case 'T': TimeStampOnly = 1; break; case 'U': Update = 1; break; default: puts("failure"); exit(20); } ++str; } } 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(ac,av); else BackupFiles(ac,av); } long SaveLock; 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 = 1; i < ac; ++i) { str = av[i]; if (*str == '-') continue; /* * 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(-1); } UnLock(CurrentDir(SaveLock)); if (OutFile) closeoutput(); } DATESTAMP Date; 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 */ RestoreFiles(ac, av) char *av[]; { register short i; register char *str; char notdone; char havedate; char havepro; char havecom; char haveinc; long bytes; long actual; long lock; SaveLock = CurrentDir(lock = DupLock(((PROC *)FindTask(NULL))->pr_CurrentDir)); for (i = 1; i < ac; ++i) { str = av[i]; if (*str == '-') continue; if (openinput(str) == 0) { printf("Unable to open %s for input\n", str); continue; } notdone = 1; havedate = havepro = havecom = haveinc = 0; while (notdone) { short c = oreadchar(); short l = oreadchar(); switch(c) { case -1: notdone = 0; break; case XVOL: oread(Scr, l); Scr[l] = 0; if (OutFile) { /* Restore to OutFile instead */ register short j = strlen(OutFile); strcpy(Scr, OutFile); if (j && OutFile[j-1] == '/') { c = XDDS; Scr[j-1] = 0; } else if (j && OutFile[j-1] == ':') { c = XVOL; Scr[j-1] = 0; } else c = XDDS; } PushDir(Scr, c); if (ListOnly) break; lock = Lock(DirPath, SHARED_LOCK); /* DirPath incs ':' */ if (lock == NULL && c == XDDS) { if (lock = CreateDir(Scr)) /* Scr excludes '/' */ UnLock(lock); lock = Lock(Scr, SHARED_LOCK); } { SDIR *sd = GetTail(&DList); sd->HaveFile = 1; /* don't remove dir */ } if (lock == NULL) { printf("Unable to create directory %s\n", Scr); notdone = 0; } else { UnLock(CurrentDir(lock)); } break; case XDDS: oread(Scr, l); Scr[l] = 0; PushDir(Scr, XDDS); if (ListOnly) break; lock = Lock(Scr, SHARED_LOCK); if (lock == NULL) { if (lock = CreateDir(Scr)) UnLock(lock); lock = Lock(Scr, SHARED_LOCK); } else { SDIR *sd = GetTail(&DList); sd->HaveFile = 1; /* don't remove dir */ } 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; lock = ParentDir(lock); if (lock == NULL) { puts("Unable to ParentDir!"); notdone = 0; } else { if (c == 0) DeleteFile(Scr); UnLock(CurrentDir(lock)); } } break; case XDAT: if (l != sizeof(DATESTAMP)) { puts("expected sizeof datestamp"); notdone = 0; break; } oread(&Date, l); havedate = 1; break; case XPRO: if (l != 4) { puts("Expected 4 bytes for protection"); notdone = 0; break; } 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 (havedate) setfiledate(Scr, &Date); if (havecom && Comment[0]) SetComment(Scr, Comment); bend: havecom = havedate = havepro = haveinc = 0; break; case XHDR: if (l != sizeof(DATESTAMP)) { puts("expected sizeof datestamp"); notdone = 0; break; } 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 = 0; break; } oread(&IncSize, 4); oread(&IncSeek, 4); oread(&IncLen, 4); haveinc = 1; break; default: printf("Unknown Record Type: %02x\n", c); notdone = 0; break; } setinputbound(-1); if (mycheckbreak()) { Break = 1; notdone = 0; break; } } if (Break) break; } UnLock(CurrentDir(SaveLock)); } 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); } FreeFileInfo(fib, lock) FIB *fib; long lock; { if (fib) free(fib); if (lock) UnLock(lock); } 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 strcat(DirPath+DPLen, "/"); DPLen += strlen(DirPath+DPLen); } PopDirs(num) uword num; { register SDIR *sd, *sp; char lasthave = 0; while (num && (sd = GetTail(&DList))) { lasthave |= sd->HaveFile; if (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(lasthave); } /* * SCAN_DIRECTORY() (CORE OF BACKUP) */ scan_directory(dirfib, dirlock) FIB *dirfib; long dirlock; { register FIB *fib; long lock; long save = CurrentDir(dirlock); 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); lock = scan_directory(fib, lock); PopDirs(1); } else { lock = scan_file(fib, lock); } FreeFileInfo(fib, lock); if (Break || mycheckbreak()) { Break = 1; break; } } CurrentDir(save); return(dirlock); } mycheckbreak() { if (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. */ scan_file(fib, lock) FIB *fib; long lock; { long save; long n; char dbuf[32]; strcat(DirPath, fib->fib_FileName); if (Update && (fib->fib_Protection & FIBF_ARCHIVE)) goto nomatch; { register short i; for (i = 0; i < NumPicks; ++i) { if (wildcmp(Picks[i], DirPath)) break; } if (i && i == NumPicks) goto nomatch; for (i = 0; i < NumDels; ++i) { if (wildcmp(Dels[i], DirPath)) goto nomatch; } } if (ShowFiles) printf("%-40s %6ld %s\n", DirPath, fib->fib_Size, datetos(&fib->fib_Date, dbuf, NULL)); { 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); } } if (OutFile) { save = CurrentDir(lock); if (openinput("") == 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(fib->fib_Size); } 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); } 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); } writeheaders(fib) register FIB *fib; { 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); } /* * (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 */ 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); } read_file(type, fname, inbytes, outbytes) short type; char *fname; { char dbuf[32]; strcat(DirPath, fname); { register short i; for (i = 0; i < NumPicks; ++i) { if (wildcmp(Picks[i], DirPath)) break; } if (i && i == NumPicks) { if (Verbose) printf("%-40s (NOT ACCEPTED)\n", DirPath); goto nomatch; } for (i = 0; i < NumDels; ++i) { if (wildcmp(Dels[i], DirPath)) { if (Verbose) printf("%-40s (NOT ACCEPTED)\n", DirPath); goto nomatch; } } } 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: UnCompressFile(inbytes); transfer1(outbytes); 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; 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); if (!append) 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); } oputc(v) char v; { ++OutBytes; if (Outfd >= 0) { if (BufI == BufSize) dumpoutput(); Buf[BufI++] = v; } } owrite(buf, n) register char *buf; register long n; { register long avail; OutBytes += n; if (Outfd >= 0) { while (BufI + n > BufSize) { avail = BufSize - BufI; bmov(buf, Buf + BufI, avail); n -= avail; buf+= avail; BufI = BufSize; dumpoutput(); } bmov(buf, Buf + BufI, n); BufI += n; } } dumpoutput() { if (Outfd >= 0 && BufI) { write(Outfd, Buf, BufI); BufI = 0; } } closeoutput() { if (Outfd >= 0) { dumpoutput(); close(Outfd); Outfd = -1; } } outbytes() { return(OutBytes); } /* * */ outentry(type, len, buf) ubyte type; ubyte len; ubyte *buf; { OutBytes += len + 2; if (Outfd >= 0) { if (BufI + len + 2 >= BufSize) dumpoutput(); Buf[BufI+0] = type; Buf[BufI+1] = len; bmov(buf, Buf+BufI+2, len); BufI += len + 2; } } ulong OMax; openinput(name) char *name; { if (Infd >= 0) close(Infd); Infd = open(name, O_RDONLY); InBufI = InBufN = 0; OMax = -1; return(Infd >= 0); } 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; } setinputbound(max) { OMax = max; } oread(buf, n) char *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); bmov(InBuf + InBufN, buf, avail); OMax-= avail; n -= avail; buf += avail; x += avail; InBufI = read(Infd, InBuf, InBufSize); InBufN = 0; if (InBufI <= 0) { InBufI = 0; return(x); } } bmov(InBuf + InBufN, buf, n); InBufN += n; x += n; OMax -= n; return(x); } 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); } } return(InBuf[InBufN++]); } rollbackinput() { if (Infd >= 0) lseek(Infd, 0L, 0); InBufI = InBufN = 0; } 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(0); } sc->N = 0; CWrite = sc; } ((char *)(sc + 1))[sc->N++] = v; } mwrite(buf, n) char *buf; long n; { register SCOMP *sc = CWrite; register long avail; CLen += n; while ((avail = sc->Bytes - sc->N) < n) { bmov(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(0); } sc->N = 0; } bmov(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); } transfer0(n) long n; { register long len; if (Outfd < 0) return(n); 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; } } /* * Compression Routines * * transfer1(n) : Backup: copy compression buffer to output file */ transfer1(a) { register long len; register SCOMP *sc = GetHead(&CList); register ubyte *ptr; long n = CLen; if (Outfd < 0) return(n); for (sc = GetHead(&CList); sc && n; sc = GetSucc(sc)) { len = sc->Bytes; ptr = (ubyte *)(sc + 1); if (n < len) len = n; n -= len; while (len > BufSize - BufI) { bmov(ptr, Buf + BufI, BufSize - BufI); ptr += BufSize - BufI; len -= BufSize - BufI; OutBytes += BufSize - BufI; BufI = BufSize; dumpoutput(); } bmov(ptr, Buf + BufI, len); BufI += len; OutBytes += len; } if (n) puts("Unexpected EOF in compression file"); } #asm ; Taken from my DRES.LIBRARY so we don't have to open the ; library. public _GetHead public _GetTail public _GetSucc public _GetPred _GetSucc: _GetHead: move.l 4(sp),A0 move.l (A0),A0 tst.l (A0) bne .gh1 .ghz sub.l A0,A0 .gh1 move.l A0,D0 rts _GetTail: move.l 4(sp),A0 move.l 8(A0),A0 tst.l 4(A0) beq .ghz move.l A0,D0 rts _GetPred: move.l 4(sp),A0 move.l 4(A0),A0 tst.l 4(A0) beq .ghz move.l A0,D0 rts #endasm #define ngetchar() oreadchar() #define nputchar(n) mputc(n) #ifndef min #define min(a,b) ((a>b) ? b : a) #endif #define BITS 13 #if BITS == 16 #define HSIZE 69001 /* 95% occupancy */ #endif #if BITS == 15 #define HSIZE 35023 /* 94% occupancy */ #endif #if BITS == 14 #define HSIZE 18013 /* 91% occupancy */ #endif #if BITS == 13 #define HSIZE 9001 /* 91% occupancy */ #endif #if BITS <= 12 #define HSIZE 5003 /* 80% occupancy */ #endif typedef long code_int; typedef long count_int; typedef unsigned char char_type; #define MAXCODE(n_bits) ((1 << (n_bits)) - 1) #define INIT_BITS 9 /* initial number of bits/code */ int n_bits; /* number of bits/code */ int maxbits; /* user settable max # bits/code */ code_int maxcode; /* maximum code, given n_bits */ code_int maxmaxcode; /* should NEVER generate this code */ count_int htab[HSIZE]; uword codetab[HSIZE]; #define htabof(i) htab[i] #define codetabof(i) codetab[i] code_int hsize = HSIZE; /* for dynamic table sizing */ #define tab_prefixof(i) codetabof(i) #define tab_suffixof(i) ((char_type *)(htab))[i] #define de_stack ((char_type *)&tab_suffixof(1<N = 0; bzero(htab, sizeof(htab)); bzero(codetab, sizeof(codetab)); hsize = HSIZE; if ( fsize < (1 << 12) ) hsize = min ( 5003, HSIZE ); else if ( fsize < (1 << 13) ) hsize = min ( 9001, HSIZE ); else if ( fsize < (1 << 14) ) hsize = min ( 18013, HSIZE ); else if ( fsize < (1 << 15) ) hsize = min ( 35023, HSIZE ); else if ( fsize < 47000 ) hsize = min ( 50021, HSIZE ); offset = clear_flg = ratio = 0; in_count = 1; checkpoint = CHECK_GAP; n_bits = INIT_BITS; /* number of bits/code */ maxbits = BITS; /* user settable max # bits/code */ maxcode = MAXCODE(INIT_BITS); /* maximum code, given n_bits */ maxmaxcode = 1 << BITS; /* should NEVER generate this code */ free_ent = ((block_compress) ? FIRST : 256 ); ent = ngetchar(); 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 = ngetchar()) != 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); 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); output((code_int)-1); return(CLen < fsize); } 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; { register int r_off = offset, bits= n_bits; register char * bp = buf; if ( code >= 0 ) { /* * 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; mwrite(bp, bits); bp += bits; bits = 0; 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) mwrite(buf, n_bits); offset = 0; if (clear_flg) { n_bits = INIT_BITS; maxcode = MAXCODE(INIT_BITS); clear_flg = 0; } else { n_bits++; if (n_bits == maxbits) maxcode = maxmaxcode; else maxcode = MAXCODE(n_bits); } } } else { /* * At EOF, write the rest of the buffer. */ if (offset > 0) mwrite(buf, (offset + 7) / 8); offset = 0; } } char * xrindex(s, c) /* For those who don't have it in libc.a */ register char *s, c; { char *p; for (p = NULL; *s; s++) { if (*s == c) p = s; } return(p); } cl_block() /* table clear for block compress */ { register long int rat; checkpoint = in_count + CHECK_GAP; if (in_count > 0x007fffff) { /* shift will overflow */ rat = CLen >> 8; if (rat == 0) { /* Don't divide by zero */ rat = 0x7fffffff; } else { rat = in_count / rat; } } else { rat = (in_count << 8) / CLen; /* 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 ); } } 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; } UnCompressFile(insize) { register char_type *stackp; register int finchar; register code_int code, oldcode, incode; /* * As above, initialize the first 256 entries in the table. */ bzero(htab, sizeof(htab)); bzero(codetab, sizeof(codetab)); offset = clear_flg = ratio = 0; in_count = 1; checkpoint = CHECK_GAP; n_bits = INIT_BITS; /* number of bits/code */ maxbits = BITS; /* user settable max # bits/code */ maxcode = MAXCODE(INIT_BITS); /* maximum code, given n_bits */ maxmaxcode = 1 << BITS; /* should NEVER generate this code */ for ( code = 255; code >= 0; code-- ) { tab_prefixof(code) = 0; tab_suffixof(code) = (char_type)code; } free_ent = ((block_compress) ? FIRST : 256 ); finchar = oldcode = getcode(); if (oldcode == -1) /* EOF already? */ return; /* Get out of here */ oputc((char)finchar); /* first code must be 8 bits = char */ 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 oputc (*--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; } } code_int getcode() { /* * On the VAX, it is important to have the register declarations * in exactly the order given, or the asm will break. */ register code_int code; static int offset = 0, size = 0; static char_type buf[BITS]; register int r_off, bits; register char_type *bp = buf; if (clear_flg > 0 || offset >= size || free_ent > maxcode) { /* * If the next entry will be too big for the current code * size, then we must increase the size. This implies reading * a new buffer full, too. */ if ( free_ent > maxcode ) { n_bits++; if ( n_bits == maxbits ) maxcode = maxmaxcode; /* won't get any bigger now */ else maxcode = MAXCODE(n_bits); } if ( clear_flg > 0) { maxcode = MAXCODE (n_bits = INIT_BITS); clear_flg = 0; } size = oread(buf, n_bits); if (size <= 0) return -1; /* end of file */ offset = 0; 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) */ code = (*bp++ >> r_off); 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 ) { code |= *bp++ << r_off; r_off += 8; bits -= 8; } /* high order bits. */ code |= (*bp & rmask[bits]) << r_off; offset += n_bits; return code; }