home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Elysian Archive
/
AmigaElysianArchive.iso
/
compress
/
comprs16.lha
/
compress.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-10-18
|
30KB
|
1,150 lines
static char sccsid[] = "@(#)compress.c @(#)compress.c 5.9 (Berkeley) 5/11/86";
/*
* Compress - data compression program
*/
#define min(a,b) ((a>b) ? b : a)
#define HSIZE 69001 /* 95% occupancy */
/*
* a code_int must be able to hold 2**BITS values of type int, and also -1
*/
typedef long int code_int;
typedef long int count_int;
typedef unsigned char char_type;
char_type magic_header[] = { "\037\235" }; /* 1F 9D */
/* Defines for third byte of header */
#define BIT_MASK 0x1f
#define BLOCK_MASK 0x80
/* Masks 0x40 and 0x20 are free. I think 0x20 should mean that there is
a fourth header byte (for expansion).
*/
#define INIT_BITS 9 /* initial number of bits/code */
static char rcs_ident[] = "$Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $";
#include <stdio.h>
#include <ctype.h>
#include <stat.h>
#include <time.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <exec/io.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <functions.h>
#define ARGVAL() (*++(*argv) || (--argc && *++argv))
int n_bits; /* number of bits/code */
int maxbits = BITS; /* user settable max # bits/code */
code_int maxcode; /* maximum code, given n_bits */
code_int maxmaxcode = 1 << BITS; /* should NEVER generate this code */
#define MAXCODE(n_bits) ((1 << (n_bits)) - 1)
/* extern int errno; */
count_int *htab;
unsigned short *codetab;
#define htabof(i) htab[i]
#define codetabof(i) codetab[i]
code_int hsize = HSIZE; /* for dynamic table sizing */
count_int fsize;
/*
* To save much memory, we overlay the table used by compress() with those
* used by decompress(). The tab_prefix table is the same size and type
* as the codetab. The tab_suffix table needs 2**BITS characters. We
* get this from the beginning of htab. The output stack uses the rest
* of htab, and contains characters. There is plenty of room for any
* possible stack (stack used to be 8000 characters).
*/
#define tab_prefixof(i) codetabof(i)
#define tab_suffixof(i) ((char_type *)(htab))[i]
#define de_stack ((char_type *)&tab_suffixof(1<<BITS))
code_int free_ent = 0; /* first unused entry */
int exit_stat = 0; /* per-file status */
int perm_stat = 0; /* permanent status */
code_int getcode();
Usage() {
fprintf(stderr,"Usage: compress [-fvc] [-b maxbits] [file ...]\n");
}
int nomagic = 0; /* Use a 3-byte magic number header, unless old file */
int zcat_flg = 0; /* Write output on stdout, suppress messages */
int precious = 1; /* Don't unlink output file on interrupt */
int quiet = 1; /* don't tell me about compression */
/*
* block compression parameters -- after all codes are used up,
* and compression rate changes, start over.
*/
int block_compress = BLOCK_MASK;
int clear_flg = 0;
long int ratio = 0;
#define CHECK_GAP 10000 /* ratio check interval */
count_int checkpoint = CHECK_GAP;
/*
* 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 */
int force = 0;
char ofname [100];
int (*oldint)();
int do_decomp = 0;
/*****************************************************************
* TAG( main )
*
* Algorithm from "A Technique for High Performance Data Compression",
* Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19.
*
* Usage: compress [-dfvc] [-b bits] [file ...]
* Inputs:
* -d: If given, decompression is done instead.
*
* -c: Write output on stdout, don't remove original.
*
* -b: Parameter limits the max number of bits/code.
*
* -f: Forces output file to be generated, even if one already
* exists, and even if no space is saved by compressing.
* If -f is not used, the user will be prompted if stdin is
* a tty, otherwise, the output file will not be overwritten.
*
* -v: Write compression statistics
*
* file ...: Files to be compressed. If none specified, stdin
* is used.
* Outputs:
* file.Z: Compressed form of file with same mode, owner, and utimes
* or stdout (if stdin used as input)
*
* Assumptions:
* When filenames are given, replaces with the compressed version
* (.Z suffix) only if the file decreases in size.
* Algorithm:
* Modified Lempel-Ziv method (LZW). Basically finds common
* substrings and replaces them with a variable size code. This is
* deterministic, and can be done on the fly. Thus, the decompression
* procedure needs no input table, but tracks the way the table was built.
*/
main( argc, argv )
register int argc; char **argv;
{
int overwrite = 0; /* Do not overwrite unless given -f flag */
char tempname[100];
char **filelist, **fileptr;
char *cp, *rindex(), *strcpy(), *malloc();
struct stat statbuf;
extern onintr(), oops();
freopen("*","r+", stderr);
htab = (count_int *)malloc(HSIZE * sizeof(count_int));
codetab = (unsigned short *)malloc(HSIZE * sizeof(unsigned short));
if (htab == NULL || codetab == NULL) {
fprintf(stderr,"compress: out of memory\n");
exit(1);
}
filelist = fileptr = (char **)(malloc(argc * sizeof(*argv)));
*filelist = NULL;
if((cp = rindex(argv[0], '/')) != 0) {
cp++;
} else {
cp = argv[0];
}
if(strcmp(cp, "uncompress") == 0) {
do_decomp = 1;
} else if(strcmp(cp, "zcat") == 0) {
do_decomp = 1;
zcat_flg = 1;
}
/* Argument Processing
* All flags are optional.
* -D => debug
* -V => print Version; debug verbose
* -d => do_decomp
* -v => unquiet
* -f => force overwrite of output file
* -n => no header: useful to uncompress old files
* -b maxbits => maxbits. If -b is specified, then maxbits MUST be
* given also.
* -c => cat all output to stdout
* -C => generate output compatible with compress 2.0.
* if a string is left, must be an input filename.
*/
for (argc--, argv++; argc > 0; argc--, argv++) {
if (**argv == '-') { /* A flag argument */
while (*++(*argv)) { /* Process all flags in this arg */
switch (**argv) {
case 'V':
version();
break;
case 'v':
quiet = 0;
break;
case 'd':
do_decomp = 1;
break;
case 'f':
case 'F':
overwrite = 1;
force = 1;
break;
case 'n':
nomagic = 1;
break;
case 'C':
block_compress = 0;
break;
case 'b':
if (!ARGVAL()) {
fprintf(stderr, "Missing maxbits\n");
Usage();
exit(1);
}
maxbits = atoi(*argv);
goto nextarg;
case 'c':
zcat_flg = 1;
break;
case 'q':
quiet = 1;
break;
default:
fprintf(stderr, "Unknown flag: '%c'; ", **argv);
Usage();
exit(1);
}
}
} else { /* Input file name */
*fileptr++ = *argv; /* Build input file list */
*fileptr = NULL;
/* process nextarg; */
}
nextarg: continue;
}
if (maxbits < INIT_BITS) maxbits = INIT_BITS;
if (maxbits > BITS) maxbits = BITS;
maxmaxcode = 1 << maxbits;
if (*filelist != NULL) {
for (fileptr = filelist; *fileptr; fileptr++) {
exit_stat = 0;
if (do_decomp) { /* DECOMPRESSION */
/* Check for .Z suffix */
if (strcmp(*fileptr + strlen(*fileptr) - 2, ".Z") != 0) {
/* No .Z: tack one on */
strcpy(tempname, *fileptr);
strcat(tempname, ".Z");
*fileptr = tempname;
}
/* Open input file */
if ((freopen(*fileptr, "r", stdin)) == NULL) {
perror(*fileptr);
perm_stat = 1;
continue;
}
/* Check the magic number */
if (nomagic == 0) {
if ((getc(stdin) != (magic_header[0] & 0xFF))
|| (getc(stdin) != (magic_header[1] & 0xFF))) {
fprintf(stderr, "%s: not in compressed format\n",
*fileptr);
continue;
}
maxbits = getc(stdin); /* set -b from file */
block_compress = maxbits & BLOCK_MASK;
maxbits &= BIT_MASK;
maxmaxcode = 1 << maxbits;
if(maxbits > BITS) {
fprintf(stderr,
"%s: compressed with %d bits, can only handle %d bits\n",
*fileptr, maxbits, BITS);
continue;
}
}
/* Generate output filename */
strcpy(ofname, *fileptr);
ofname[strlen(*fileptr) - 2] = '\0'; /* Strip off .Z */
} else { /* COMPRESSION */
if (strcmp(*fileptr + strlen(*fileptr) - 2, ".Z") == 0) {
fprintf(stderr, "%s: already has .Z suffix -- no change\n",
*fileptr);
continue;
}
/* Open input file */
if ((freopen(*fileptr, "r", stdin)) == NULL) {
perror(*fileptr);
perm_stat = 1;
continue;
}
stat ( *fileptr, &statbuf );
fsize = (long) statbuf.st_size;
/*
* tune hash table size for small files -- ad hoc,
* but the sizes match earlier #defines, which
* serve as upper bounds on the number of output codes.
*/
hsize = HSIZE;
if ( fsize < (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 );
/* Generate output filename */
strcpy(ofname, *fileptr);
strcat(ofname, ".Z");
}
/* Check for overwrite of existing file */
if (overwrite == 0 && zcat_flg == 0) {
if (stat(ofname, &statbuf) == 0) {
char response[2];
response[0] = 'n';
fprintf(stderr, "%s already exists;", ofname);
if (isatty(2)) {
fprintf(stderr, " do you wish to overwrite %s (y or n)? ",
ofname);
fflush(stderr);
read(2, response, 2);
while (response[1] != '\n') {
if (read(2, response+1, 1) < 0) { /* Ack! */
perror("stderr"); break;
}
}
}
if (response[0] != 'y') {
fprintf(stderr, "\tnot overwritten\n");
continue;
}
}
}
if(zcat_flg == 0) { /* Open output file */
if (freopen(ofname, "w", stdout) == NULL) {
perror(ofname);
perm_stat = 1;
continue;
}
precious = 0;
if(!quiet)
fprintf(stderr, "%s: ", *fileptr);
}
/* Actually do the compression/decompression */
if (do_decomp == 0)
compress();
else
decompress();
if(zcat_flg == 0) {
copystat(*fileptr, ofname); /* Copy stats */
precious = 1;
if((exit_stat == 1) || (!quiet))
putc('\n', stderr);
}
}
} else { /* Standard input */
if (do_decomp == 0) {
compress();
if(!quiet)
putc('\n', stderr);
} else {
/* Check the magic number */
if (nomagic == 0) {
if ((getc(stdin)!=(magic_header[0] & 0xFF))
|| (getc(stdin)!=(magic_header[1] & 0xFF))) {
fprintf(stderr, "stdin: not in compressed format\n");
exit(1);
}
maxbits = getc(stdin); /* set -b from file */
block_compress = maxbits & BLOCK_MASK;
maxbits &= BIT_MASK;
maxmaxcode = 1 << maxbits;
fsize = 100000; /* assume stdin large for USERMEM */
if(maxbits > BITS) {
fprintf(stderr,
"stdin: compressed with %d bits, can only handle %d bits\n",
maxbits, BITS);
exit(1);
}
}
decompress();
}
}
exit(perm_stat ? perm_stat : exit_stat);
}
static int offset;
long int in_count = 1; /* length of input */
long int bytes_out; /* length of compressed output */
long int out_count = 0; /* # of codes output (for debugging) */
/*
* 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.
*/
compress() {
register long fcode;
register code_int i = 0;
register int c;
register code_int ent;
register int disp;
register code_int hsize_reg;
register int hshift;
if (nomagic == 0) {
putc(magic_header[0], stdout); putc(magic_header[1], stdout);
putc((char)(maxbits | block_compress), stdout);
if(ferror(stdout))
writeerr();
}
offset = 0;
bytes_out = 3; /* includes 3-byte header mojo */
out_count = 0;
clear_flg = 0;
ratio = 0;
in_count = 1;
checkpoint = CHECK_GAP;
maxcode = MAXCODE(n_bits = INIT_BITS);
free_ent = ((block_compress) ? FIRST : 256 );
ent = getc(stdin);
hshift = 0;
for ( fcode = (long) hsize; fcode < 65536L; fcode *= 2L )
hshift++;
hshift = 8 - hshift; /* set hash code range bound */
hsize_reg = hsize;
cl_hash( (count_int) hsize_reg); /* clear hash table */
while ((c = getc(stdin)) != EOF) {
in_count++;
fcode = (long) (((long) c << maxbits) + ent);
i = ((c << hshift) ^ ent); /* xor hashing */
if ( htabof (i) == fcode ) {
ent = codetabof (i);
continue;
} else if ((long)htabof (i) < 0) /* empty slot */
goto nomatch;
disp = hsize_reg - i; /* secondary hash (after G. Knott) */
if ( i == 0 )
disp = 1;
probe:
if ((i -= disp) < 0)
i += hsize_reg;
if (htabof (i) == fcode) {
ent = codetabof (i);
continue;
}
if ((long)htabof (i) > 0)
goto probe;
nomatch:
output ((code_int) ent);
out_count++;
ent = c;
if ( free_ent < maxmaxcode ) {
codetabof (i) = free_ent++; /* code -> hashtable */
htabof (i) = fcode;
}
else if ( (count_int)in_count >= checkpoint && block_compress )
cl_block ();
}
/*
* Put out the final code.
*/
output((code_int)ent);
out_count++;
output((code_int)-1);
/*
* Print out stats on stderr
*/
if(zcat_flg == 0 && !quiet) {
fprintf( stderr, "Compression: " );
prratio( stderr, in_count-bytes_out, in_count );
}
if(bytes_out > in_count) /* exit(2) if no savings */
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 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;
{
/*
* On the VAX, it is important to have the register declarations
* in exactly the order given, or the asm will break.
*/
register int r_off = offset, bits= n_bits;
register char * bp = buf;
if (code >= 0) {
/*
* byte/bit numbering on the VAX is simulated by the following code
*/
/*
* Get to the first byte.
*/
bp += (r_off >> 3);
r_off &= 7;
/*
* Since code is always >= 8 bits, only need to mask the first
* hunk on the left.
*/
*bp = (*bp & rmask[r_off]) | (code << r_off) & lmask[r_off];
bp++;
bits -= (8 - r_off);
code >>= 8 - r_off;
/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
if ( bits >= 8 ) {
*bp++ = code;
code >>= 8;
bits -= 8;
}
/* Last bits. */
if(bits)
*bp = code;
offset += n_bits;
if (offset == (n_bits << 3)) {
bp = buf;
bits = n_bits;
bytes_out += bits;
do
putc(*bp++, stdout);
while(--bits);
offset = 0;
}
/*
* If the next entry is going to be too big for the code size,
* then increase it, if possible.
*/
if (free_ent > maxcode || (clear_flg > 0))
{
/*
* Write the whole buffer, because the input side won't
* discover the size increase until after it has read it.
*/
if (offset > 0) {
if( fwrite( buf, 1, n_bits, stdout ) != n_bits)
writeerr();
bytes_out += n_bits;
}
offset = 0;
if ( clear_flg ) {
maxcode = MAXCODE (n_bits = INIT_BITS);
clear_flg = 0;
}
else {
n_bits++;
if ( n_bits == maxbits )
maxcode = maxmaxcode;
else
maxcode = MAXCODE(n_bits);
}
}
} else {
/*
* At EOF, write the rest of the buffer.
*/
if ( offset > 0 )
fwrite( buf, 1, (offset + 7) / 8, stdout );
bytes_out += (offset + 7) / 8;
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.
*/
decompress() {
register char_type *stackp;
register int finchar;
register code_int code, oldcode, incode;
/*
* As above, initialize the first 256 entries in the table.
*/
maxcode = MAXCODE(n_bits = INIT_BITS);
for ( code = 255; code >= 0; code-- ) {
tab_prefixof(code) = 0;
tab_suffixof(code) = (char_type)code;
}
free_ent = ((block_compress) ? FIRST : 256 );
finchar = oldcode = getcode();
if(oldcode == -1) /* EOF already? */
return; /* Get out of here */
putc((char)finchar, stdout); /* first code must be 8 bits = char */
if(ferror(stdout)) /* Crash if can't write */
writeerr();
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
putc(*--stackp, stdout);
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;
}
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.
*/
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 = fread( buf, 1, n_bits, stdin );
if ( size <= 0 )
return -1; /* end of file */
offset = 0;
/* Round size down to integral number of codes */
size = (size << 3) - (n_bits - 1);
}
r_off = offset;
bits = n_bits;
/*
* Get to the first byte.
*/
bp += (r_off >> 3);
r_off &= 7;
/* Get first part (low order bits) */
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;
}
writeerr()
{
perror ( ofname );
unlink ( ofname );
exit ( 1 );
}
copystat(ifname, ofname)
char *ifname, *ofname;
{
BOOL CopyFileDate();
fclose(stdout);
fclose(stdin);
if (exit_stat == 2 && (!force)) { /* No compression: remove file.Z */
if(!quiet)
fprintf(stderr, " -- file unchanged"), fflush(stderr);
} else { /* ***** Successful Compression ***** */
exit_stat = 0;
if (CopyFileAttr(ifname, ofname) || CopyFileDate(ifname, ofname))
fprintf(stderr, " -- couldn't copy file attributes"), fflush(stderr);
if (unlink(ifname)) /* Remove input file */
perror(ifname), fflush(stderr);
else if(!quiet)
fprintf(stderr, " -- replaced with %s", ofname), fflush(stderr);
return; /* Successful return */
}
/* Unsuccessful return -- one of the tests failed */
if (unlink(ofname))
perror(ofname), fflush(stderr);
}
onintr ( )
{
if (!precious)
unlink ( ofname );
exit ( 1 );
}
oops ( ) /* wild pointer -- assume bad input */
{
if ( do_decomp )
fprintf ( stderr, "uncompress: corrupt input\n" );
unlink ( ofname );
exit ( 1 );
}
cl_block () /* table clear for block compress */
{
register long int rat;
checkpoint = in_count + CHECK_GAP;
if(in_count > 0x007fffff) { /* shift will overflow */
rat = bytes_out >> 8;
if(rat == 0) { /* Don't divide by zero */
rat = 0x7fffffff;
} else {
rat = in_count / rat;
}
} else {
rat = (in_count << 8) / bytes_out; /* 8 fractional bits */
}
if ( rat > ratio ) {
ratio = rat;
} else {
ratio = 0;
cl_hash ( (count_int) hsize );
free_ent = FIRST;
clear_flg = 1;
output ( (code_int) CLEAR );
}
}
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;
}
prratio(stream, num, den)
FILE *stream;
long int num, den;
{
register int q; /* Doesn't need to be long */
if(num > 214748L) { /* 2147483647/10000 */
q = num / (den / 10000L);
} else {
q = 10000L * num / den; /* Long calculations, though */
}
if (q < 0) {
putc('-', stream);
q = -q;
}
fprintf(stream, "%d.%02d%%", q / 100, q % 100);
}
version()
{
fprintf(stderr, "%s, Berkeley 5.9 5/11/86\n", rcs_ident);
fprintf(stderr, "Options: ");
fprintf(stderr, "AMIGA, ");
fprintf(stderr, "BITS = %d\n", BITS);
}
/* Function:
* GetFileDate
*
* Called with:
* name: file name
* date: pointer to DateStamp structure
*
* Returns:
* result: 1 => got a date, 0 => didn't
*
* Description:
* GetFileDate attempts to get the creation/modification date
* of a file (unfortunately, they're one and the same) and stores
* it into the location pointed to by <date>. If the file doesn't
* exist or for some reason the date can't be obtained, <date>
* is set to zeros and a zero is returned. Otherwise, <date> is set
* to the file date and a 1 is returned.
*/
BOOL
GetFileDate(name, date)
char *name; struct DateStamp *date;
{
struct FileInfoBlock *Fib;
ULONG FLock;
int result = FALSE;
register struct DateStamp *d;
if ((FLock = (ULONG) Lock(name,(long)(ACCESS_READ)))== NULL)
goto exit1;
Fib = (struct FileInfoBlock *)
AllocMem((long)sizeof(struct FileInfoBlock),
(long)(MEMF_CHIP|MEMF_PUBLIC));
if (Fib == NULL )
result = FALSE;
else{
if (!Examine(FLock, Fib )) {
result = FALSE;
}
else
if (Fib->fib_DirEntryType > 0 )
result = FALSE; /* It's a directory */
else{
d = &Fib->fib_Date;
date->ds_Days = d->ds_Days;
date->ds_Minute = d->ds_Minute;
date->ds_Tick = d->ds_Tick;
result = TRUE;
}
FreeMem((void *)Fib,(long)sizeof(struct FileInfoBlock));
}
UnLock(FLock);
exit1:
if (! result ) {
date->ds_Days = 0;
date->ds_Minute = 0;
date->ds_Tick = 0;
}
return result;
}
/*---------------------------------------------------------------------*/
/* SetFileDate: datestamp the given file with the given date. */
/*---------------------------------------------------------------------*/
#define ACTION_SETDATE_MODE 34L /* Set creation date on file */
BOOL
SetFileDate( name, date )
char *name; struct DateStamp *date;
{
struct MsgPort *task; /* for process id handler */
ULONG arg[4]; /* array of arguments */
int nameleng;
char *bstr, *strcpy(); /* of file to be set */
long rc;
char *strchr();
int strlen();
rc = 0;
nameleng = strlen(name);
if (!(bstr = (char *)AllocMem((long) (nameleng + 2),MEMF_PUBLIC)))
goto exit2;
if (!(task = (struct MsgPort *)DeviceProc(name )))
goto exit1;
/* Dos Packet needs the filename in Bstring format */
(void) strcpy(bstr+1, name );
*bstr = nameleng;
arg[0]= (ULONG)NULL;
arg[1]= (ULONG)IoErr(); /* lock on parent director set by
DeviceProc() */
arg[2]= (ULONG)bstr >> 2;
arg[3]= (ULONG)date;
rc = sendpkt(task,ACTION_SETDATE_MODE,arg,4L );
exit1: if (bstr )
FreeMem((void *)bstr, (long) (nameleng + 2));
exit2: if (rc == DOSTRUE )
return TRUE;
else
return FALSE;
}
/* Copy the last modified date from one file to another.
* Called with:
* from: name of source file
* to: name of destination file
* Returns:
* 0 => success, 1 => failure
* Note:
* Dynamic memory allocation of the DateStamp struction is
* necessary to insure longword alignment.
*/
BOOL
CopyFileDate(from,to)
char *from, *to;
{
struct DateStamp *date;
int status = 1; /* default is fail code */
if (date = (struct DateStamp *)
AllocMem((long) sizeof(struct DateStamp), MEMF_PUBLIC)) {
if (GetFileDate(from,date))
if (SetFileDate(to,date))
status = 0;
FreeMem(date, (long) sizeof(struct DateStamp));
}
return status;
}
/****************************************************************************/
/* Function:
* CopyFileAttr - Copy File Attributes
*
* Called with:
* srcName: source file name
* dstName: destination file name
*
* Returns:
* status where 0 => success
*
* Description:
* CopyFileAttr is used by file copying functions to assign the
* attributes of the source file to the destination file.
*/
int
CopyFileAttr(srcName, dstName)
char *srcName, *dstName;
{
struct Lock *srcLock = NULL;
struct FileInfoBlock *srcFIB = NULL;
int status = 0;
if (! (srcFIB = AllocMem( (long) sizeof(*srcFIB), MEMF_FAST) ) ) {
nomem:
status = ERROR_NO_FREE_STORE;
goto done;
}
if (! (srcLock = (struct Lock *) Lock(srcName, ACCESS_READ) ) ) {
err:
status = IoErr();
goto done;
}
if (!Examine(srcLock, srcFIB)) goto err;
SetFileDate(dstName, &srcFIB->fib_Date);
if (srcFIB->fib_Comment[0])
SetComment(dstName, srcFIB->fib_Comment);
SetProtection(dstName, srcFIB->fib_Protection);
done:
if (srcLock) UnLock(srcLock);
if (srcFIB) FreeMem(srcFIB, (long) sizeof(*srcFIB));
return status;
}
LONG
sendpkt(id,type,args,nargs)
struct MsgPort *id; /* process indentifier ... (handler's
message port ) */
LONG type, /* packet type ... (what you want
handler to do ) */
args[], /* a pointer to argument list */
nargs; /* number of arguments in list */
{
struct MsgPort *replyport;
struct StandardPacket *packet;
LONG count,*pargs,res1=NULL;
if (!(replyport = (struct MsgPort *) CreatePort(NULL,NULL)))
return(NULL);
packet = (struct StandardPacket *)
AllocMem((LONG)sizeof(*packet),MEMF_PUBLIC|MEMF_CLEAR);
if (packet) {
packet->sp_Msg.mn_Node.ln_Name = &(packet->sp_Pkt);/* link packet */
packet->sp_Pkt.dp_Link = &(packet->sp_Msg);/* to message */
packet->sp_Pkt.dp_Port = replyport;/* set-up reply port */
packet->sp_Pkt.dp_Type = type;/* what to do... */
/* move all the arguments to the packet */
pargs = &(packet->sp_Pkt.dp_Arg1);/* address of first argument */
for (count=0; (count < nargs) && (count < 7); count++)
pargs[count] = args[count];
PutMsg(id,packet); /* send packet */
WaitPort(replyport); /* wait for packet to come back */
GetMsg(replyport); /* pull message */
res1 = packet->sp_Pkt.dp_Res1;/* get result */
FreeMem(packet,(LONG)sizeof(*packet));
}
DeletePort(replyport);
return(res1);
}