home *** CD-ROM | disk | FTP | other *** search
- /*
- compress.c
-
- EnthΣlt Routine zum Komprimieren und Dekomprimieren, die kompatibel
- zum UN*X compress sind.
-
- Copyright (C) 1991 Ingo Feulner.
- Alle Rechte vorbehalten.
- */
-
- #include <string.h>
- #include "uucpbase.h"
- #include "uucpproto.h"
- #include "buffer.h"
-
- #define EOF (-1)
-
- /* Defines for third byte of header */
- #define BIT_MASK 0x1f
- #define BLOCK_MASK 0x80
-
- #define INIT_BITS 9 /* initial number of bits/code */
-
-
- #define MAXCODE(n_bits) ((1 << (n_bits)) - 1)
-
- #define htabof(i) (CompInfo->htab[i])
- #define codetabof(i) (CompInfo->codetab[i])
-
- #define tab_prefixof(i) codetabof(i)
- #define tab_suffixof(i) ((char_type *)(CompInfo->htab))[i]
- #define de_stack ((char_type *)&tab_suffixof(1<<CompInfo->maxbits))
-
- #define CHECK_GAP 10000 /* ratio check interval */
-
- #define FIRST 257 /* first free entry */
- #define CLEAR 256 /* table clear output code */
-
-
- typedef long int code_int;
- typedef long int count_int;
- typedef unsigned char char_type;
-
-
- // Prototypes
- STATIC BOOL __asm AllocCompMem(register __a6 struct UUCPBase *UUCPBase,
- register __a0 struct CompressInfo **CompInfo,
- register __d1 UWORD bits,
- register __d2 BOOL decomp_flag);
-
- STATIC VOID __asm FreeCompMem(register __a6 struct UUCPBase *UUCPBase,
- register __a0 struct CompressInfo *CompInfo);
-
- STATIC VOID __asm compress(register __a6 struct UUCPBase *UUCPBase,
- register __a0 struct CompressInfo *CompInfo);
-
- STATIC VOID __asm output(register __a6 struct UUCPBase *UUCPBase,
- register __a0 struct CompressInfo *CompInfo,
- register __d1 code_int code);
-
- STATIC VOID __asm decompress(register __a6 struct UUCPBase *UUCPBase,
- register __a0 struct CompressInfo *CompInfo);
-
- STATIC code_int __asm getcode(register __a6 struct UUCPBase *UUCPBase,
- register __a0 struct CompressInfo *CompInfo);
-
- STATIC VOID __asm cl_block(register __a6 struct UUCPBase *UUCPBase,
- register __a0 struct CompressInfo *CompInfo);
-
- STATIC VOID __asm cl_hash(register __a0 struct CompressInfo *CompInfo);
-
-
- STATIC const char_type magic_header[] = { "\x1F\x9D" };
- STATIC const char_type lmask[9] =
- {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00};
-
- STATIC const char_type rmask[9] =
- {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
-
-
- struct CompressInfo
- {
- struct datei *infile; /* File to read from */
- struct datei *outfile; /* File to write to */
- count_int *htab;
- unsigned short *codetab;
- 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 */
- code_int hsize; /* for dynamic table sizing */
- code_int free_ent; /* first unused entry */
- int exit_stat;
- int block_compress;
- int clear_flg;
- long int ratio;
- count_int checkpoint;
- int offset;
- int size;
- long int in_count; /* length of input */
- long int bytes_out; /* length of compressed output */
- char *buf;
- };
-
-
- /*
- * CompressFile(name, maxbits);
- *
- * Komprimiert das File, das durch den BPTR angegeben ist unter Verwendung
- * des UN*X Compress Algorithmus. Das komprimierte File wird in T: unter
- * einem einzigartigen Namen abgelegt.
- * Maxbits gibt die Anzahl der Bits an, die verwendet werden sollen.
- * Die Gr÷▀e des ben÷tigten Speichers richtet sich nach dieser Bitanzahl.
- *
- * Folgende Rⁿckgabewerte sind m÷glich:
- * - AUUCP_ERROR_OK: Kein Fehler aufgetreten
- * - AUUCP_ERROR_OUTOFMEM: Nicht ausreichend Arbeitsspeicher
- * - AUUCP_ERROR_COMPRESSED: Angegebenes File bereits komprimiert.
- *
- */
- UWORD __asm _CompressFile(register __a6 struct UUCPBase *UUCPBase,
- register __a0 UBYTE *name,
- register __d2 UWORD maxbits)
- {
- struct CompressInfo *CompInfo;
- UWORD error = AUUCP_ERROR_OK;
-
- if((AllocCompMem(UUCPBase, &CompInfo, maxbits, FALSE)) != FALSE)
- {
- if((CompInfo->infile = oeffne_datei(UUCPBase, name, MODE_OLDFILE)) != NULL)
- {
- if((CompInfo->outfile =
- oeffne_datei(UUCPBase, TempFileName(), MODE_NEWFILE)) != NULL)
- {
- compress(UUCPBase, CompInfo);
- schliesse_datei(UUCPBase, CompInfo->outfile);
- }
- schliesse_datei(UUCPBase, CompInfo->infile);
- }
- FreeCompMem(UUCPBase, CompInfo);
- }
- else
- {
- error = AUUCP_ERROR_OUTOFMEM;
- }
- return error;
- }
-
- /*
- * DeCompressFile(name);
- *
- * Entkomprimiert das angegebene File, und schreibt die entkomprimierten
- * Daten in eine temporΣre Datei, die in T: mit einem einzigartigen Namen
- * angelegt wird.
- *
- * Folgende Rⁿckgabewerte sind m÷glich:
- * - AUUCP_ERROR_OK: Kein Fehler aufgetreten
- * - AUUCP_ERROR_OUTOFMEM: Nicht ausreichend Arbeitsspeicher
- * - AUUCP_ERROR_NOTCOMPRESSED: Angegebene Datei ist nicht komprimiert.
- *
- */
- UWORD __asm _DeCompressFile(register __a6 struct UUCPBase *UUCPBase,
- register __a0 UBYTE *name)
- {
- struct CompressInfo *CompInfo;
- struct datei *infile;
- UWORD maxbits, bits;
- UWORD error = AUUCP_ERROR_OK;
-
- if((infile = oeffne_datei(UUCPBase, name, MODE_OLDFILE)) != NULL)
- {
- if( (hole_byte(UUCPBase, infile) == (magic_header[0] & 0xFF))
- && (hole_byte(UUCPBase, infile) == (magic_header[1] & 0xFF)) )
- {
- // Mit wieviel Bits compressed?
- bits = hole_byte(UUCPBase, infile);
- maxbits = bits & BIT_MASK;
-
- if((AllocCompMem(UUCPBase, &CompInfo, maxbits, TRUE)) != FALSE)
- {
- CompInfo->infile = infile;
- CompInfo->block_compress = bits & BLOCK_MASK;
-
- if((CompInfo->outfile =
- oeffne_datei(UUCPBase, TempFileName(), MODE_NEWFILE)) != NULL)
- {
- decompress(UUCPBase, CompInfo);
- schliesse_datei(UUCPBase, CompInfo->outfile);
- }
- FreeCompMem(UUCPBase, CompInfo);
- }
- else
- {
- error = AUUCP_ERROR_OUTOFMEM;
- }
- }
- else
- {
- error = AUUCP_ERROR_NOTCOMPRESSED;
- }
- schliesse_datei(UUCPBase, CompInfo->infile);
- }
- return error;
- }
-
-
- STATIC BOOL __asm AllocCompMem(register __a6 struct UUCPBase *UUCPBase,
- register __a0 struct CompressInfo **CompInfo,
- register __d1 UWORD bits,
- register __d2 BOOL decomp_flag)
- {
- ULONG hsize;
-
- switch(bits)
- {
- case 16: hsize = 69001; break;
- case 15: hsize = 35023; break;
- case 14: hsize = 18013; break;
- case 13: hsize = 9001; break;
- case 12:
- default: hsize = 5003; break;
- }
-
- if((*CompInfo =
- UUAllocMem((ULONG)sizeof(struct CompressInfo), MEMF_CLEAR | MEMF_PUBLIC)) != NULL)
- {
- if(((*CompInfo)->codetab =
- UUAllocMem(hsize * sizeof(unsigned short), MEMF_PUBLIC)) != NULL)
- {
- // Nicht von DeCompressFile() aus aufgerufen
- if(decomp_flag != TRUE)
- {
- (*CompInfo)->htab =
- UUAllocMem((ULONG)(hsize * sizeof(count_int)), MEMF_PUBLIC);
- }
- else
- {
- (*CompInfo)->htab = UUAllocMem((ULONG)((1L << 16) + 8000), MEMF_PUBLIC);
- }
-
- if((*CompInfo)->htab != NULL)
- {
- if(((*CompInfo)->buf = UUAllocMem(bits, MEMF_CLEAR | MEMF_PUBLIC)) != NULL)
- {
- // StartWerte setzen
- (*CompInfo)->maxbits = bits;
- (*CompInfo)->maxmaxcode = 1 << bits;
- (*CompInfo)->hsize = hsize;
- (*CompInfo)->block_compress = BLOCK_MASK;
- (*CompInfo)->checkpoint = CHECK_GAP;
- (*CompInfo)->in_count = 1;
-
- return TRUE;
- }
- UUFreeMem((*CompInfo)->htab);
- }
- UUFreeMem((*CompInfo)->codetab);
- }
- UUFreeMem((*CompInfo));
- }
- return FALSE;
- }
-
- STATIC VOID __asm FreeCompMem(register __a6 struct UUCPBase *UUCPBase,
- register __a0 struct CompressInfo *CompInfo)
- {
- UUFreeMem(CompInfo->buf);
- UUFreeMem(CompInfo->codetab);
- UUFreeMem(CompInfo->htab);
- UUFreeMem(CompInfo);
- }
-
- /*
- * compress stdin to stdout
- *
- * 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.
- */
-
- STATIC VOID __asm compress(register __a6 struct UUCPBase *UUCPBase,
- register __a0 struct CompressInfo *CompInfo)
- {
- long fcode;
- code_int i;
- int c;
- code_int ent;
- int disp;
- code_int hsize_reg;
- int hshift;
-
-
- schreibe_byte(UUCPBase, CompInfo->outfile, magic_header[0]);
- schreibe_byte(UUCPBase, CompInfo->outfile, magic_header[1]);
-
- schreibe_byte(UUCPBase, CompInfo->outfile, (CompInfo->maxbits | CompInfo->block_compress));
-
- // if(ferror(stdout))
- // writeerr();
-
- CompInfo->offset = 0;
- CompInfo->bytes_out = 3; /* includes 3-byte header mojo */
- CompInfo->clear_flg = 0;
- CompInfo->ratio = 0;
- CompInfo->in_count = 1;
- CompInfo->checkpoint = CHECK_GAP;
- CompInfo->maxcode = MAXCODE(CompInfo->n_bits = INIT_BITS);
- CompInfo->free_ent = ((CompInfo->block_compress) ? FIRST : 256 );
-
- ent = hole_byte(UUCPBase, CompInfo->infile);
-
- hshift = 0;
- for ( fcode = (long) CompInfo->hsize; fcode < 65536L; fcode *= 2L )
- hshift++;
-
- hshift = 8 - hshift; /* set hash code range bound */
-
- hsize_reg = CompInfo->hsize;
- cl_hash(CompInfo); /* clear hash table */
-
- while ( (c = hole_byte(UUCPBase, CompInfo->infile)) != EOF )
- {
- CompInfo->in_count++;
- fcode = (long) (((long) c << CompInfo->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 (UUCPBase, CompInfo, (code_int) ent );
- ent = c;
-
- if ( CompInfo->free_ent < CompInfo->maxmaxcode )
- {
- codetabof (i) = CompInfo->free_ent++; /* code -> hashtable */
- htabof (i) = fcode;
- }
- else if ( (count_int)CompInfo->in_count >= CompInfo->checkpoint && CompInfo->block_compress )
- cl_block(UUCPBase, CompInfo);
- }
- /*
- * Put out the final code.
- */
- output(UUCPBase, CompInfo, (code_int)ent);
- output(UUCPBase, CompInfo, (code_int)-1);
-
- if(CompInfo->bytes_out > CompInfo->in_count) /* exit(2) if no savings */
- CompInfo->exit_stat = 2;
- return;
- }
-
-
- /*****************************************************************
- * 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 VOID __asm output(register __a6 struct UUCPBase *UUCPBase,
- register __a0 struct CompressInfo *CompInfo,
- register __d1 code_int code)
- {
- int r_off = CompInfo->offset,
- bits = CompInfo->n_bits;
-
- char *bp = CompInfo->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;
-
- CompInfo->offset += CompInfo->n_bits;
- if ( CompInfo->offset == (CompInfo->n_bits << 3) )
- {
- bp = CompInfo->buf;
- bits = CompInfo->n_bits;
- CompInfo->bytes_out += bits;
- do
- schreibe_byte(UUCPBase, CompInfo->outfile, *bp++);
- while(--bits);
- CompInfo->offset = 0;
- }
-
- /*
- * If the next entry is going to be too big for the code size,
- * then increase it, if possible.
- */
- if ( CompInfo->free_ent > CompInfo->maxcode || (CompInfo->clear_flg > 0))
- {
- /*
- * Write the whole buffer, because the input side won't
- * discover the size increase until after it has read it.
- */
- if ( CompInfo->offset > 0 )
- {
- // Fehlerⁿberprⁿfung machen!
- schreibe_zeichen(UUCPBase, CompInfo->outfile, CompInfo->buf, CompInfo->n_bits);
- CompInfo->bytes_out += CompInfo->n_bits;
- }
- CompInfo->offset = 0;
-
- if ( CompInfo->clear_flg )
- {
- CompInfo->maxcode = MAXCODE (CompInfo->n_bits = INIT_BITS);
- CompInfo->clear_flg = 0;
- }
- else
- {
- CompInfo->n_bits++;
- if ( CompInfo->n_bits == CompInfo->maxbits )
- CompInfo->maxcode = CompInfo->maxmaxcode;
- else
- CompInfo->maxcode = MAXCODE(CompInfo->n_bits);
- }
- }
- }
- else
- {
- /*
- * At EOF, write the rest of the buffer.
- */
- if ( CompInfo->offset > 0 )
- schreibe_zeichen(UUCPBase,
- CompInfo->outfile, CompInfo->buf, (CompInfo->offset + 7) / 8);
-
- CompInfo->bytes_out += (CompInfo->offset + 7) / 8;
- CompInfo->offset = 0;
-
- // fflush( stdout );
-
-
- // if( ferror( stdout ) )
- // writeerr();
- }
- }
-
- /*
- * Decompress stdin to stdout. 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.
- */
-
- STATIC VOID __asm decompress(register __a6 struct UUCPBase *UUCPBase,
- register __a0 struct CompressInfo *CompInfo)
- {
- char_type *stackp;
- int finchar;
- code_int code, oldcode, incode;
-
- CompInfo->offset = 0;
- CompInfo->size = 0;
-
- /*
- * As above, initialize the first 256 entries in the table.
- */
- CompInfo->maxcode = MAXCODE(CompInfo->n_bits = INIT_BITS);
- for ( code = 255; code >= 0; code-- )
- {
- tab_prefixof(code) = 0;
- tab_suffixof(code) = (char_type)code;
- }
- CompInfo->free_ent = ((CompInfo->block_compress) ? FIRST : 256 );
-
- finchar = oldcode = getcode(UUCPBase, CompInfo);
- if(oldcode == -1) /* EOF already? */
- return; /* Get out of here */
- schreibe_byte(UUCPBase, CompInfo->outfile, finchar ); /* first code must be 8 bits = char */
-
- // if(ferror(stdout)) /* Crash if can't write */
- // writeerr();
-
- stackp = de_stack;
-
- while ( (code = getcode(UUCPBase, CompInfo)) > -1 )
- {
- if ( (code == CLEAR) && CompInfo->block_compress )
- {
- for ( code = 255; code >= 0; code-- )
- tab_prefixof(code) = 0;
- CompInfo->clear_flg = 1;
- CompInfo->free_ent = FIRST - 1;
- if ( (code = getcode (UUCPBase, CompInfo)) == -1 ) /* O, untimely death! */
- break;
- }
- incode = code;
-
- /*
- * Special case for KwKwK string.
- */
-
- if ( code >= CompInfo->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
- schreibe_byte(UUCPBase, CompInfo->outfile, *--stackp );
- while ( stackp > de_stack );
-
- /*
- * Generate the new entry.
- */
- if((code = CompInfo->free_ent) < CompInfo->maxmaxcode)
- {
- tab_prefixof(code) = (unsigned short)oldcode;
- tab_suffixof(code) = finchar;
- CompInfo->free_ent = code+1;
- }
- /*
- * Remember previous code.
- */
- oldcode = incode;
- }
-
- // fflush(stdout);
- // if(ferror(stdout))
- // writeerr();
- }
-
- /*****************************************************************
- * TAG( getcode )
- *
- * Read one code from the standard input. If EOF, return -1.
- * Inputs:
- * stdin
- * Outputs:
- * code or -1 is returned.
- */
-
- STATIC code_int __asm getcode(register __a6 struct UUCPBase *UUCPBase,
- register __a0 struct CompressInfo *CompInfo)
- {
- code_int code;
- int r_off, bits;
- char_type *bp = CompInfo->buf;
-
- if ( CompInfo->clear_flg > 0 || CompInfo->offset >= CompInfo->size || CompInfo->free_ent > CompInfo->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 ( CompInfo->free_ent > CompInfo->maxcode )
- {
- CompInfo->n_bits++;
- if ( CompInfo->n_bits == CompInfo->maxbits )
- CompInfo->maxcode = CompInfo->maxmaxcode; /* won't get any bigger now */
- else
- CompInfo->maxcode = MAXCODE(CompInfo->n_bits);
- }
- if ( CompInfo->clear_flg > 0)
- {
- CompInfo->maxcode = MAXCODE (CompInfo->n_bits = INIT_BITS);
- CompInfo->clear_flg = 0;
- }
-
- CompInfo->size =
- hole_zeichen(UUCPBase, CompInfo->infile, CompInfo->buf, CompInfo->n_bits);
- if ( CompInfo->size <= 0 )
- return -1; /* end of file */
- CompInfo->offset = 0;
- /* Round size down to integral number of codes */
- CompInfo->size = (CompInfo->size << 3) - (CompInfo->n_bits - 1);
- }
- r_off = CompInfo->offset;
- bits = CompInfo->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;
- CompInfo->offset += CompInfo->n_bits;
-
- return code;
- }
-
- /* table clear for block compress */
- STATIC VOID __asm cl_block(register __a6 struct UUCPBase *UUCPBase,
- register __a0 struct CompressInfo *CompInfo)
- {
- long int rat;
-
- CompInfo->checkpoint = CompInfo->in_count + CHECK_GAP;
-
- if(CompInfo->in_count > 0x007fffff) /* shift will overflow */
- {
- rat = CompInfo->bytes_out >> 8;
- if(rat == 0) /* Don't divide by zero */
- {
- rat = 0x7fffffff;
- }
- else
- {
- rat = CompInfo->in_count / rat;
- }
- }
- else
- {
- rat = (CompInfo->in_count << 8) / CompInfo->bytes_out; /* 8 fractional bits */
- }
-
- if ( rat > CompInfo->ratio )
- {
- CompInfo->ratio = rat;
- }
- else
- {
- CompInfo->ratio = 0;
-
- cl_hash(CompInfo);
- CompInfo->free_ent = FIRST;
- CompInfo->clear_flg = 1;
- output(UUCPBase, CompInfo, (code_int) CLEAR);
- }
- }
-
- /* reset code table */
- STATIC VOID __asm cl_hash(register __a0 struct CompressInfo *CompInfo)
- {
- count_int *htab_p = CompInfo->htab + CompInfo->hsize;
- const long m1 = -1;
- long i;
-
- i = CompInfo->hsize - 16;
- do
- {
- *(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;
- }
-