home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frozen Fish 1: Amiga
/
FrozenFish-Apr94.iso
/
bbs
/
alib
/
d1xx
/
d168
/
dillonstuff.lha
/
src
/
com
/
backup.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-11-22
|
37KB
|
1,795 lines
/*
* 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 = <HDR><N.B><datestamp> -Backup Date
* VOL = <VOL><name_size.B><name> -VOLUME base
* DDS = <DDS><name_size.B><name> -down-directory
* END = <END><0.B> -up directory
* DAT = <DAT><N.B><datestamp> -datestamp for file
* PRO = <PRO><4.B><protection> -protection for file
* COM = <COM><N.B><comment> -comment for file
* FIL0= <FIL0><N.B><name><size.L><data> -uncompressed file
* FIL1= <FIL1><N.B><name><size.L><usize.L><data> -compressed form #1
* INC = <INC><12.B><ttlsize><strt><segsize> -next file is part of an
* incomplete file
*/
#include <stdio.h>
#include <fcntl.h>
#include <local/typedefs.h>
#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<pat> -p<pat> -f[#kb] -F<vol> -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);
}
/*
* <type><len><buf>
*/
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<<BITS))
code_int free_ent; /* first unused entry */
code_int getcode();
#define CHECK_GAP 10000 /* ratio check interval */
int block_compress = 1;
int clear_flg;
long ratio;
count_int checkpoint;
/*
* the next two codes should not be changed lightly, as they must not
* lie within the contiguous general code space.
*/
#define FIRST 257 /* first free entry */
#define CLEAR 256 /* table clear output code */
static int offset;
long int in_count = 1; /* length of input */
/*
* Compress a file to memory-buffers and return TRUE if the compressed
* size is smaller than the actual size.
*/
CompressFile(name, fsize)
{
long fcode;
code_int i = 0;
int c;
code_int ent;
int disp;
code_int hsize_reg;
int hshift;
if (wildcmp("*.Z", name) || wildcmp("*.ARC", name) || wildcmp("*.ZOO", name)) {
printf(" Will not compress %s\n", name);
return(0);
}
CLen = 0;
CWrite = GetHead(&CList);
if (CWrite == NULL)
CWrite = NewSComp();
CWrite->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;
}