[comp.compression] Problem with compress

kent@sparky.IMD.Sterling.COM (Kent Landfield) (05/14/91)

I received an interesting question today, one I had never really considered.
"How does compress deal with symbolic links ?"  This was one of those
questions that you thought you knew, at least I did.  My first reaction
was to want to respond with "Why, it fails to compress the file, of course."
Before I said it, I realized that I really did not know.  I tried it out
and I was *suprised*.  It created a compressed copy of the file that the 
symlink had pointed to!

Here is what I did on a Sun 4/490 running 4.1.1 although a quick glance
at the source to compress let me know that this is a common problem...

lrwxrwxrwx   1 kent  kent  18 May 13 12:11 index -> /usenet/misc/index

When I typed "compress index"

index: Compression: 59.82% -- replaced with index.Z

-rw-rw-r--   1 kent     kent      57411 May 13 09:57 index.Z

OOPS!!....  It ignored the fact that the file was a symbolic link and created 
a compressed copy of the file. It did not touch the file that the symbolic link
referenced.  This is *NOT* what I wanted it to do... Compress fails on hard 
links but ignores symlinks altogether... ?? :-(

Have I missed something here ? Is this a known problem that some consider
a feature ?  I would much prefer that compress fail on symlinks as it does
on hard links.  How often have you "strolled" into a directory when space
was becoming a problem and typed "compress *" without even considering
which were symlinks and which were regular files ? (only 1/4 :-))

Wasn't the idea to save space instead of generate more disk usage ?
Has someone fixed this already ?

			-Kent+
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (05/14/91)

(I'm not sure whether this is appropriate for comp.compression.)

In article <1991May14.044431.23932@sparky.IMD.Sterling.COM> kent@sparky.IMD.Sterling.COM (Kent Landfield) writes:
> I received an interesting question today, one I had never really considered.
> "How does compress deal with symbolic links ?"

compress isn't a BSD program. It uses stat(), and its behavior makes
perfect sense.

I think compressors should ignore problems like this. They shouldn't
open files, they shouldn't close files, they shouldn't do anything but
read data and write compressed data. This also makes them more portable.
A separate, system-dependent program can do the dirty work.

---Dan

madler@nntp-server.caltech.edu (Mark Adler) (05/15/91)

In article <26085:May1416:52:3491@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
>I think compressors should ignore problems like this. They shouldn't
>open files, they shouldn't close files, they shouldn't do anything but
>read data and write compressed data. This also makes them more portable.
>A separate, system-dependent program can do the dirty work.

I agree completely.  That's what tar is for.  If you do something like:

     tar covf - foo | compress -c > foo.tar.Z

any symbolic links in the directory foo will be stored as such, and
hard links will only be stored once.

Despite my agreement though, I'm involved in writing a Zip program for
Unix, which tries to cover the functions of tar and compress, and do
them better.   Hypocrisy makes life easier.  :-)

Mark Adler
madler@pooh.caltech.edu

stolcke@ICSI.Berkeley.EDU (Andreas Stolcke) (05/15/91)

In article <26085:May1416:52:3491@kramden.acf.nyu.edu>, brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
|> (I'm not sure whether this is appropriate for comp.compression.)
|> 
|> In article <1991May14.044431.23932@sparky.IMD.Sterling.COM> kent@sparky.IMD.Sterling.COM (Kent Landfield) writes:
|> > I received an interesting question today, one I had never really considered.
|> > "How does compress deal with symbolic links ?"
|> 
|> compress isn't a BSD program. It uses stat(), and its behavior makes
|> perfect sense.
|> 
|> I think compressors should ignore problems like this. They shouldn't
|> open files, they shouldn't close files, they shouldn't do anything but
|> read data and write compressed data. This also makes them more portable.
|> A separate, system-dependent program can do the dirty work.

It so happens that there is such a command that I think is fairly widely
available (if not it should be).  It is called compressdir and recursively
traverses a directory tree attempting to compress every plain file it
finds.  I it picked up as part of the TeX distribution, a copy is 
appended.  There is a also a companion script called uncompressdir.

-- 
Andreas Stolcke					stolcke@icsi.berkeley.edu
International Computer Science Institute	stolcke@ucbicsi.bitnet
1957 Center St., Suite 600, Berkeley, CA 94704	(415) 642-4274 ext. 126
---
OPTIONS=
FILES=
for ARG
do
        case "$ARG" in
        -*)     OPTIONS="$OPTIONS $ARG";;
        *)      FILES="$FILES $ARG";;
        esac
done
if test -z "$FILES"; then
        FILES="."
fi
set $FILES
find $@ -type f -links 1 -exec test -r {} -a -s {} \; \
-exec expr '(' {} : '.*\.Z' ')' '=' 0 \; \
-exec compress $OPTIONS {} \; >/dev/null

kent@uunet.UU.NET (Kent Landfield - comp.sources.misc) (05/15/91)

In article <26085:May1416:52:3491@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
>(I'm not sure whether this is appropriate for comp.compression.)

??? I must be confused about the charter of comp.compression. Sorry...

>In article <1991May14.044431.23932@sparky.IMD.Sterling.COM> kent@sparky.IMD.Sterling.COM (Kent Landfield) writes:
>> I received an interesting question today, one I had never really considered.
>> "How does compress deal with symbolic links ?"
>
>compress isn't a BSD program. It uses stat(), and its behavior makes
>perfect sense.

It is not a "BSD" program in that it does not use lstat to detect symlinks,
that is correct and the problem....  Compress has come into wide use on BSD 
based environments and is and has been delivered from vendors of those
environments... I am not sure when AT&T started delivering it... :-)

I cannot agree with the "perfect sense" statement. Why does compress not
deal with hard links ? Because it would have to know the location of the 
other link(s) so that it could rename it/them with a .Z compression suffix. 
This was too much for a compression program to deal with so compress does not.
Why is a symbolic link any different in this case ? I can and do have symlinks
that point to other symlinks... Compress actually alters the file type from a
symlink to a regular file in no time flat... I don't see why, after all the
years that compress has been around that this is not in the BUGS section
of the man page. At least it is not in the man pages from the vendors I
have in house and the sources I have from the net...

>I think compressors should ignore problems like this. They shouldn't
>open files, they shouldn't close files, they shouldn't do anything but
>read data and write compressed data. This also makes them more portable.
>A separate, system-dependent program can do the dirty work.

I was not talking about the way it should be. I was talking about a bug,
as far as I am concerned, in the way compress exists today.  I was hoping
that someone had fixed this and I would not have to... :-( I am still
hoping. (hint, hint)
			-Kent+
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.

brad@looking.on.ca (Brad Templeton) (05/15/91)

It's a tough problem, actually.   It is nice to take the software tools
approach of splitting archiver and compressor, but this makes it more
difficult to do some of the cute things you can do when you combine them.

The simplest of which is the fairly standard archive of compressed
files, with each file compressed individually and extractable without
decompressing the entire archive -- a serious problem if it is a
multi-media archive or very large.

Of course, a compiler that could do this, and which called a compressor
as a filter, would not be too hard to make.  But tar, cpio and others do
not have any support for this.
-- 
Brad Templeton, ClariNet Communications Corp. -- Waterloo, Ontario 519/884-7473

peter@ficc.ferranti.com (Peter da Silva) (05/15/91)

In article <1991May15.062504.28369@looking.on.ca> brad@looking.on.ca (Brad Templeton) writes:
> The simplest of which is the fairly standard archive of compressed
> files, with each file compressed individually and extractable without
> decompressing the entire archive -- a serious problem if it is a
> multi-media archive or very large.

And of course you want to be able to mount this archive as a read-only file
system, no?

(yes)
-- 
Peter da Silva; Ferranti International Controls Corporation; +1 713 274 5180;
Sugar Land, TX  77487-5012;         `-_-' "Have you hugged your wolf, today?"

wombat@infinite.abo.dec.com (Christopher M. Conway) (05/15/91)

In article <1991May15.062504.28369@looking.on.ca>, brad@looking.on.ca (Brad Templeton) writes:
-It's a tough problem, actually.   It is nice to take the software tools
-approach of splitting archiver and compressor, but this makes it more
-difficult to do some of the cute things you can do when you combine them.
-
-The simplest of which is the fairly standard archive of compressed
-files, with each file compressed individually and extractable without
-decompressing the entire archive -- a serious problem if it is a
-multi-media archive or very large.
-
-Of course, a compiler that could do this, and which called a compressor
-as a filter, would not be too hard to make.  But tar, cpio and others do
-not have any support for this.
--- 
-Brad Templeton, ClariNet Communications Corp. -- Waterloo, Ontario 519/884-7473
-
Actually, the example you gave is quite easy. Compress the files individually,
e.g. compress * or for a directory hierarchy the compressdir tool just
posted or something similar; tar the result. Voila, one archive of compressed
files, with each compressed file individually extractable without decompressing
anything else.

There is nothing that can be done in one monolithic tool that can't be
done more flexibly (although possibly more complex) with lots of small
tools. Count me as a vote for tar and some kind of compress (I use Yabba
now, myself).
--
Christopher M. Conway		| U*ix and C Guru
wombat@nfinit.enet.dec.com      | The Second Amendment is ABOUT military
wombat@jupiter.nmt.edu          | weapons. We have the RIGHT and DUTY to
wombat@juliet.ll.mit.edu        | overthrow a tyrannical government.

rickert@mp.cs.niu.edu (Neil Rickert) (05/16/91)

In article <1991May15.060236.26763@uunet.uu.net> kent@uunet.UU.NET (Kent Landfield - comp.sources.misc) writes:

>>> "How does compress deal with symbolic links ?"
>
>I was not talking about the way it should be. I was talking about a bug,
>as far as I am concerned, in the way compress exists today.  I was hoping
>that someone had fixed this and I would not have to... :-( I am still
>hoping. (hint, hint)

 Am I alone in thinking that the way compress handles symlinks is just fine.
What I don't like is the way it handles hard links.  What would be wrong with
'compress foo' just creating the compressed 'foo.Z' and deleting 'foo', and
'uncompress foo.Z' creating the uncompressed 'foo' and removing 'foo.Z'?

If I want to keep a copy of both compressed and uncompressed for my own
reasons, why shouldn't I be able to do that by making a hard link before I
compress or uncompress?  Making me copy the file is just plain stupid.

-- 
=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=
  Neil W. Rickert, Computer Science               <rickert@cs.niu.edu>
  Northern Illinois Univ.
  DeKalb, IL 60115                                   +1-815-753-6940

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (05/16/91)

In article <1991May15.062504.28369@looking.on.ca> brad@looking.on.ca (Brad Templeton) writes:
> It's a tough problem, actually.   It is nice to take the software tools
> approach of splitting archiver and compressor, but this makes it more
> difficult to do some of the cute things you can do when you combine them.
> The simplest of which is the fairly standard archive of compressed
> files, with each file compressed individually and extractable without
> decompressing the entire archive -- a serious problem if it is a
> multi-media archive or very large.

This is really getting away from comp.compression into
comp.unix.programmer, but anyway: All you have to do to make this work
is define a format for encoding multiple files into one stream, then
have your compressor (and, while you're at it, all your other tools)
work on such a stream. Let's say, for instance, that you use cpio
format, and have compress -f understand that format. Then you just pipe
cpio through compress -f and poof! all the files are individually
compressed.

I don't think cpio (or tar or extended cpio) is a particularly good
format for this: it's too restricted for some tasks (e.g., when you want
to multiplex two arbitrary-length streams) and too complex for others
(e.g., when you don't need or want filenames). So I've been working on
yet another format and a set of routines for applications to use that
format. Once it's done it'll solve a lot of old problems: grepping
through compressed files, for instance, or compressing each file in an
archive individually.

---Dan

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (05/16/91)

In article <1991May15.173739.29874@mp.cs.niu.edu> rickert@mp.cs.niu.edu (Neil Rickert) writes:
>  Am I alone in thinking that the way compress handles symlinks is just fine.
> What I don't like is the way it handles hard links.  What would be wrong with
> 'compress foo' just creating the compressed 'foo.Z' and deleting 'foo', and
> 'uncompress foo.Z' creating the uncompressed 'foo' and removing 'foo.Z'?

I'm sure someone would complain that uncompress didn't exactly reverse
the effect of compress. It doesn't in the symlink case either, but
anything can be sacrificed to the Great God Portability.

Peter hinted at the optimal solution: compress should replace the file
contents without changing any of its links, and when the file is read,
the filesystem should automatically realize that the file contents are
compressed and uncompress them for you. (Now that I'm talking about the
Plan 9 filesystem I'm sure that this belongs in comp.unix.programmer.)

---Dan

kent@uunet.UU.NET (Kent Landfield - comp.sources.misc) (05/16/91)

In article <1991May15.173739.29874@mp.cs.niu.edu> rickert@mp.cs.niu.edu (Neil Rickert) writes:
> Am I alone in thinking that the way compress handles symlinks is just fine.
>What I don't like is the way it handles hard links.  What would be wrong with
>'compress foo' just creating the compressed 'foo.Z' and deleting 'foo', and
 'uncompress foo.Z' creating the uncompressed 'foo' and removing 'foo.Z'?

"I can fix that bug in three characters.. I can fix that bug in two characters.
..I can fix that bug in one character. Fix That BUG!" :-)

If you wish to fix/break compress, depending on your point of view, edit
compress.c and change the stat() call in the copystat() function to lstat().
A one character change... Then when you try to compress/uncompress a symbolic
link it gives you the following results...

index:  -- not a regular file: unchanged

If you want something done, you have to do it yourself... :-) :-)

			-Kent+
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.

rickert@mp.cs.niu.edu (Neil Rickert) (05/17/91)

In article <1991May15.173739.29874@mp.cs.niu.edu> rickert@mp.cs.niu.edu (Neil Rickert) writes:
> Am I alone in thinking that the way compress handles symlinks is just fine.
>What I don't like is the way it handles hard links.  What would be wrong with

 Since I wrote that I have received several email messages, either agreeing
or at least partially agreeing.

 One respondent did defend the current handling of hard links, suggesting that
you shouldn't want to keep both a compressed and an uncompressed version.  In
my view, that misses the point.  The software shouldn't make that decision
for me - I should be able to make it myself.

 What I would often like to be able to do is:

	mkdir temp
	ln *.Z temp
	cd temp
	uncompress *.Z
	   --------- browse through the uncompressed files -------------
	cd ..
	rm -rf temp

 The current handling of hard links makes this a pain, although fortunately
we have symlinks, so there is a work around.

 ----

 Finally, apologies to comp.compression.  I guess this discussion doesn't
really belong there.  It doesn't really belong in comp.sources.bugs either,
although that is how I came upon it.  Shortly after voting for
comp.compression, I found myself unsubscribing due to the high noise level.
And here I am contributing noise.

-- 
=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=
  Neil W. Rickert, Computer Science               <rickert@cs.niu.edu>
  Northern Illinois Univ.
  DeKalb, IL 60115                                   +1-815-753-6940

madler@nntp-server.caltech.edu (Mark Adler) (05/18/91)

Neil Rickert mentions:
>> Shortly after voting for comp.compression, I found myself unsubscribing
>> due to the high noise level.

Really?  I've in general been impressed with this newsgroup.  Of all the
groups I read, comp.compression easily has the highest signal-to-noise
ratio.

Then again, one man's signal is another man's noise ...

Mark Adler
madler@tybalt.caltech.edu

chip@tct.com (Chip Salzenberg) (05/19/91)

According to rickert@mp.cs.niu.edu (Neil Rickert):
> Am I alone in thinking that the way compress handles symlinks is just fine.

Apparently so.

>What I don't like is the way it handles hard links.  What would be wrong with
>'compress foo' just creating the compressed 'foo.Z' and deleting 'foo', and
>'uncompress foo.Z' creating the uncompressed 'foo' and removing 'foo.Z'?

Compress has that behavior when invoked with the "-f" flag.
-- 
Brand X Industries Custodial, Refurbishing and Containment Service:
         When You Never, Ever Want To See It Again [tm]
     Chip Salzenberg   <chip@tct.com>, <uunet!pdn!tct!chip>

csu@alembic.acs.com (Dave Mack) (05/20/91)

In article <1991May14.044431.23932@sparky.IMD.Sterling.COM> kent@sparky.IMD.Sterling.COM (Kent Landfield) writes:
>Have I missed something here ? Is this a known problem that some consider
>a feature ?  I would much prefer that compress fail on symlinks as it does
>on hard links.  How often have you "strolled" into a directory when space
>was becoming a problem and typed "compress *" without even considering
>which were symlinks and which were regular files ? (only 1/4 :-))
>
>Wasn't the idea to save space instead of generate more disk usage ?
>Has someone fixed this already ?

Yes.

I've been using this version of compress for 4 years without
problems. It has a "-r" flag to do recursive compression of
directory trees (*much* faster than compressdir.) As a side-
effect, it ignores anything that isn't a regular file, including
symlinks. WARNING: this hasn't been tested on anything but BSD
machines - use on SYSV at your own risk. I'm posting the shar
file because it's reasonably short. If you think it's of sufficient
interest, feel free to pump it through comp.sources.misc.

Dave Mack

-------------------------------- cut here --------------------------------
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README Makefile usermem compress.c compress.1
# Wrapped by csu@alembic on Sun May 19 13:09:52 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(3682 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XModifications for version 4.1:
X	o Added -r command line flag to allow recursive compression/
X	  decompression. As a side-effect, compress no longer tries
X	  to compress/decompress anything that isn't a regular file.
X	o Decompression silently ignores any file that doesn't
X	  end in ".Z". The previous behavior (appending a .Z and
X	  continuing) was wrong in too many cases.
X
XWARNING: version 4.1 has NOT been tested on System V, Xenix or any
Xother non-Berkeleyoid Unix. Watch your step.
X
XCompress version 4.0 improvements:
X	o compress() speedup (10-50%) by changing division hash to xor
X	o decompress() speedup (5-10%)
X	o Memory requirements reduced (3-30%)
X	o Stack requirements reduced to less than 4kb
X	o Removed 'Big+Fast' compress code (FBITS) because of compress speedup
X    	o Portability mods for Z8000 and PC/XT (but not zeus 3.2)
X	o Default to 'quiet' mode
X	o Unification of 'force' flags
X	o Manual page overhaul
X	o Portability enhancement for M_XENIX
X	o Removed text on #else and #endif
X	o Added "-V" switch to print version and options
X	o Added #defines for SIGNED_COMPARE_SLOW
X	o Added Makefile and "usermem" program
X	o Removed all floating point computations
X	o New programs:
X		compressdir - compress all files on a directory
X		uncompressdir - uncompress all files on a directory
X		zcmp - cmp compressed files
X		zdiff - diff compressed files
X	  The following are with thanks to philabs!per:
X		btoa - convert binary to ascii for mailing
X		atob - convert ascii to binary with checksum
X		tarmail - tar, compress, btoa, and mail files
X		untarmail - restore "tarmail" files
X
X		WARNING: These last few programs are not compatible 
X		with the original ones from the net.  The encoding
X		has changed.  See btoa.c for more info.
X
XThe "usermem" script attempts to determine the maximum process size.  Some
Xediting of the script may be necessary (see the comments).  If you can't get
Xit to work at all, just create file "USERMEM" containing the maximum process
Xsize in decimal.
X
XThe following preprocessor symbols control the compilation of "compress.c":
X
X	o USERMEM		Maximum process memory on the system
X	o SACREDMEM		Amount to reserve for other proceses
X	o SIGNED_COMPARE_SLOW	Unsigned compare instructions are faster
X	o NO_UCHAR		Don't use "unsigned char" types
X	o BITS			Overrules default set by USERMEM-SACREDMEM
X	o vax			Generate inline assembler
X	o interdata		Defines SIGNED_COMPARE_SLOW
X	o M_XENIX		Makes arrays < 65536 bytes each
X	o pdp11			BITS=12, NO_UCHAR
X	o z8000			BITS=12
X	o pcxt			BITS=12
X	o BSD4_2		Allow long filenames ( > 14 characters) &
X				Call setlinebuf(stderr)
X
XThe difference "usermem-sacredmem" determines the maximum BITS that can be
Xspecified with the "-b" flag.
X
Xmemory: at least		BITS
X------  -- -----                ----
X     433,484			 16
X     229,600			 15
X     127,536			 14
X      73,464			 13
X           0			 12
X
XThe default is BITS=16.
X
XThe maximum bits can be overrulled by specifying "-DBITS=bits" at
Xcompilation time.
X
XWARNING: files compressed on a large machine with more bits than allowed by 
Xa version of compress on a smaller machine cannot be decompressed!  Use the
X"-b12" flag to generate a file on a large machine that can be uncompressed 
Xon a 16-bit machine.
X
XThe output of compress 4.0 is fully compatible with that of compress 3.0.
XIn other words, the output of compress 4.0 may be fed into uncompress 3.0 or
Xthe output of compress 3.0 may be fed into uncompress 4.0.
X
XThe output of compress 4.0 not compatable with that of
Xcompress 2.0.  However, compress 4.0 still accepts the output of
Xcompress 2.0.  To generate output that is compatable with compress
X2.0, use the undocumented "-C" flag.
X
XCheck the Makefile, then "make".
END_OF_FILE
if test 3682 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(1634 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#
X# Makefile for compress version 4.1
X#
XCC=cc
X#
X# set your compile flags.
X# The BSD4_2 flag attempts to compensate for the SYSV short filename
X# deficiency. Since I've never tested this under SYSV, I don't know
X# how well it works.
X# The README file describes other flags you may need.
X#
XCFLAGS=-O -DBSD4_2
X#
X# BIN is where the compress executable and the uncompress and zcat
X# links will be installed
X#
XBIN=/usr/local/bin
X#
X# MAN says where to install the man page
X#
XMAN=/usr/man/manl
X#
X# MANSUF is the suffix the installed manual page should have
X#
XMANSUF=l
X#
X# LN is how to make links (hard or symbolic) on your system
X#
XLN=ln -s
X#
X# LIBS contains any additional libraries you may need to link.
X# In particular, you may need -lndir or equiv. to get the
X# public domain directory access routines if they aren't in your libc.
X#
XLIBS=
X
X
XSHARFILES=README Makefile usermem compress.c compress.1
X
Xall: compress
X
Xcompress: USERMEM compress.c
X	$(CC) $(CFLAGS) -DUSERMEM=`cat USERMEM` -o compress compress.c $(LIBS)
X
X# USERMEM may have to be set by hand.  It should contain the amount of
X# available user memory in bytes.  See the README file for more info.
XUSERMEM:
X	sh usermem > USERMEM
X
Xinstall: compress compress.1
X	cp compress $(BIN)
X	rm -f $(BIN)/uncompress $(BIN)/zcat
X	$(LN) $(BIN)/compress $(BIN)/uncompress
X	$(LN) $(BIN)/compress $(BIN)/zcat
X	cp compress.1 $(MAN)/compress.$(MANSUF)
X	rm -f $(MAN)/uncompress.$(MANSUF) $(MAN)/zcat.$(MANSUF)
X	$(LN) $(MAN)/compress.$(MANSUF) $(MAN)/uncompress.$(MANSUF)
X	$(LN) $(MAN)/compress.$(MANSUF) $(MAN)/zcat.$(MANSUF)
X
Xclean:
X	rm -f compress core
X
Xshar:
X	shar -o compress41.shar $(SHARFILES)
END_OF_FILE
if test 1634 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'usermem' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'usermem'\"
else
echo shar: Extracting \"'usermem'\" \(1748 characters\)
sed "s/^X//" >'usermem' <<'END_OF_FILE'
X: This shell script snoops around to find the maximum amount of available
X: user memory.  These variables need to be set only if there is no
X: /usr/adm/messages.  KMEM, UNIX, and CLICKSIZE can be set on the command
X: line, if desired, e.g. UNIX=/unix
XKMEM=/dev/kmem		# User needs read access to KMEM
XUNIX=
X# VAX			CLICKSIZE=512,	UNIX=/vmunix
X# PDP-11		CLICKSIZE=64,	UNIX=/unix
X# CADLINC 68000		CLICKSIZE=4096,	UNIX=/unix
X# Perkin-Elmer 3205	CLICKSIZE=4096,	UNIX=/edition7
X# Perkin-Elmer all others, CLICKSIZE=2048, UNIX=/edition7
XCLICKSIZE=512
Xeval $*
X
XSIZE=0
Xif test -r /usr/adm/messages	# probably the most transportable
Xthen
X    SIZE=`grep avail /usr/adm/messages | sed -n '$s/.*[ 	]//p'`
Xfi
X
Xif test 0$SIZE -le 0		# no SIZE in /usr/adm/messages
Xthen
X    if test -r $KMEM		# Readable KMEM
X    then
X	if test -n "$UNIX"
X	then
X	    : User must have specified it already.
X	elif test -r /vmunix
X	then
X	    UNIX=/vmunix
X	    CLICKSIZE=512	# Probably VAX
X	elif test -r /edition7
X	then
X	    UNIX=/edition7
X	    CLICKSIZE=2048	# Perkin-Elmer: change to 4096 on a 3205
X	elif test -r /unix
X	then
X	    UNIX=/unix		# Could be anything
X	fi
X	if test -n "$UNIX"
X	then
X	    SIZE=`echo maxmem/D | adb $UNIX $KMEM | sed -n '$s/.*[ 	]//p'`
X	    if test 0$SIZE -le 0
X	    then
X		SIZE=`echo physmem/D | adb $UNIX $KMEM | sed -n '$s/.*[ 	]//p'`
X	    fi
X	    SIZE=`expr 0$SIZE '*' $CLICKSIZE`
X	fi
X    fi
Xfi
X
Xcase $UNIX in
X    /vmunix)		# Assume 4.2bsd: check for resource limits
X	MAXSIZE=`csh -c limit | awk 'BEGIN	{ MAXSIZE = 1000000 }
X/datasize|memoryuse/ && NF == 3	{ if ($2 < MAXSIZE) MAXSIZE = $2 }
XEND	{ print MAXSIZE * 1000 }'`
X	if test $MAXSIZE -lt $SIZE
X	then
X	    SIZE=$MAXSIZE
X	fi
X	;;
Xesac
X
Xif test 0$SIZE -le 0
Xthen
X    echo 0;exit 1
Xelse
X    echo $SIZE
Xfi
END_OF_FILE
if test 1748 -ne `wc -c <'usermem'`; then
    echo shar: \"'usermem'\" unpacked with wrong size!
fi
chmod +x 'usermem'
# end of 'usermem'
fi
if test -f 'compress.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'compress.c'\"
else
echo shar: Extracting \"'compress.c'\" \(41053 characters\)
sed "s/^X//" >'compress.c' <<'END_OF_FILE'
X/* 
X * Compress - data compression program 
X */
X#define	min(a,b)	((a>b) ? b : a)
X
X/*
X * machine variants which require cc -Dmachine:  pdp11, z8000, pcxt
X */
X
X/*
X * Set USERMEM to the maximum amount of physical user memory available
X * in bytes.  USERMEM is used to determine the maximum BITS that can be used
X * for compression.
X *
X * SACREDMEM is the amount of physical memory saved for others; compress
X * will hog the rest.
X */
X#ifndef SACREDMEM
X#define SACREDMEM	0
X#endif
X
X#ifndef USERMEM
X# define USERMEM 	450000	/* default user memory */
X#endif
X
X#ifdef interdata		/* (Perkin-Elmer) */
X#define SIGNED_COMPARE_SLOW	/* signed compare is slower than unsigned */
X#endif
X
X#ifdef pdp11
X# define BITS 	12	/* max bits/code for 16-bit machine */
X# define NO_UCHAR	/* also if "unsigned char" functions as signed char */
X# undef USERMEM 
X#endif /* pdp11 */	/* don't forget to compile with -i */
X
X#ifdef z8000
X# define BITS 	12
X# undef vax		/* weird preprocessor */
X# undef USERMEM 
X#endif /* z8000 */
X
X#ifdef pcxt
X# define BITS   12
X# undef USERMEM
X#endif /* pcxt */
X
X#ifdef USERMEM
X# if USERMEM >= (433484+SACREDMEM)
X#  define PBITS	16
X# else
X#  if USERMEM >= (229600+SACREDMEM)
X#   define PBITS	15
X#  else
X#   if USERMEM >= (127536+SACREDMEM)
X#    define PBITS	14
X#   else
X#    if USERMEM >= (73464+SACREDMEM)
X#     define PBITS	13
X#    else
X#     define PBITS	12
X#    endif
X#   endif
X#  endif
X# endif
X# undef USERMEM
X#endif /* USERMEM */
X
X#ifdef PBITS		/* Preferred BITS for this memory size */
X# ifndef BITS
X#  define BITS PBITS
X# endif BITS
X#endif /* PBITS */
X
X#if BITS == 16
X# define HSIZE	69001		/* 95% occupancy */
X#endif
X#if BITS == 15
X# define HSIZE	35023		/* 94% occupancy */
X#endif
X#if BITS == 14
X# define HSIZE	18013		/* 91% occupancy */
X#endif
X#if BITS == 13
X# define HSIZE	9001		/* 91% occupancy */
X#endif
X#if BITS <= 12
X# define HSIZE	5003		/* 80% occupancy */
X#endif
X
X#ifdef M_XENIX			/* Stupid compiler can't handle arrays with */
X# if BITS == 16			/* more than 65535 bytes - so we fake it */
X#  define XENIX_16
X# else
X#  if BITS > 13			/* Code only handles BITS = 12, 13, or 16 */
X#   define BITS	13
X#  endif
X# endif
X#endif
X
X/*
X * a code_int must be able to hold 2**BITS values of type int, and also -1
X */
X#if BITS > 15
Xtypedef long int	code_int;
X#else
Xtypedef int		code_int;
X#endif
X
X#ifdef SIGNED_COMPARE_SLOW
Xtypedef unsigned long int count_int;
Xtypedef unsigned short int count_short;
X#else
Xtypedef long int	  count_int;
X#endif
X
X#ifdef NO_UCHAR
X typedef char	char_type;
X#else
X typedef	unsigned char	char_type;
X#endif /* UCHAR */
Xchar_type magic_header[] = { "\037\235" };	/* 1F 9D */
X
X/* Defines for third byte of header */
X#define BIT_MASK	0x1f
X#define BLOCK_MASK	0x80
X/* Masks 0x40 and 0x20 are free.  I think 0x20 should mean that there is
X   a fourth header byte (for expansion).
X*/
X#define INIT_BITS 9			/* initial number of bits/code */
X
X/*
X * compress.c - File compression ala IEEE Computer, June 1984.
X *
X * Authors:	Spencer W. Thomas	(decvax!harpo!utah-cs!utah-gr!thomas)
X *		Jim McKie		(decvax!mcvax!jim)
X *		Steve Davies		(decvax!vax135!petsd!peora!srd)
X *		Ken Turkowski		(decvax!decwrl!turtlevax!ken)
X *		James A. Woods		(decvax!ihnp4!ames!jaw)
X *		Joe Orost		(decvax!vax135!petsd!joe)
X *
X * $Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $
X * $Log:	compress.c,v $
X * Revision 4.1   87/05/29  now		inco!mack
X * Modified to recursively compress directories ('r' flag). As a side
X * effect, compress will no longer attempt to compress things that
X * aren't "regular" files.
X *
X * Revision 4.0  85/07/30  12:50:00  joe
X * Removed ferror() calls in output routine on every output except first.
X * Prepared for release to the world.
X * 
X * Revision 3.6  85/07/04  01:22:21  joe
X * Remove much wasted storage by overlaying hash table with the tables
X * used by decompress: tab_suffix[1<<BITS], stack[8000].  Updated USERMEM
X * computations.  Fixed dump_tab() DEBUG routine.
X *
X * Revision 3.5  85/06/30  20:47:21  jaw
X * Change hash function to use exclusive-or.  Rip out hash cache.  These
X * speedups render the megamemory version defunct, for now.  Make decoder
X * stack global.  Parts of the RCS trunks 2.7, 2.6, and 2.1 no longer apply.
X *
X * Revision 3.4  85/06/27  12:00:00  ken
X * Get rid of all floating-point calculations by doing all compression ratio
X * calculations in fixed point.
X *
X * Revision 3.3  85/06/24  21:53:24  joe
X * Incorporate portability suggestion for M_XENIX.  Got rid of text on #else
X * and #endif lines.  Cleaned up #ifdefs for vax and interdata.
X *
X * Revision 3.2  85/06/06  21:53:24  jaw
X * Incorporate portability suggestions for Z8000, IBM PC/XT from mailing list.
X * Default to "quiet" output (no compression statistics).
X *
X * Revision 3.1  85/05/12  18:56:13  jaw
X * Integrate decompress() stack speedups (from early pointer mods by McKie).
X * Repair multi-file USERMEM gaffe.  Unify 'force' flags to mimic semantics
X * of SVR2 'pack'.  Streamline block-compress table clear logic.  Increase 
X * output byte count by magic number size.
X * 
X * Revision 3.0   84/11/27  11:50:00  petsd!joe
X * Set HSIZE depending on BITS.  Set BITS depending on USERMEM.  Unrolled
X * loops in clear routines.  Added "-C" flag for 2.0 compatibility.  Used
X * unsigned compares on Perkin-Elmer.  Fixed foreground check.
X *
X * Revision 2.7   84/11/16  19:35:39  ames!jaw
X * Cache common hash codes based on input statistics; this improves
X * performance for low-density raster images.  Pass on #ifdef bundle
X * from Turkowski.
X *
X * Revision 2.6   84/11/05  19:18:21  ames!jaw
X * Vary size of hash tables to reduce time for small files.
X * Tune PDP-11 hash function.
X *
X * Revision 2.5   84/10/30  20:15:14  ames!jaw
X * Junk chaining; replace with the simpler (and, on the VAX, faster)
X * double hashing, discussed within.  Make block compression standard.
X *
X * Revision 2.4   84/10/16  11:11:11  ames!jaw
X * Introduce adaptive reset for block compression, to boost the rate
X * another several percent.  (See mailing list notes.)
X *
X * Revision 2.3   84/09/22  22:00:00  petsd!joe
X * Implemented "-B" block compress.  Implemented REVERSE sorting of tab_next.
X * Bug fix for last bits.  Changed fwrite to putchar loop everywhere.
X *
X * Revision 2.2   84/09/18  14:12:21  ames!jaw
X * Fold in news changes, small machine typedef from thomas,
X * #ifdef interdata from joe.
X *
X * Revision 2.1   84/09/10  12:34:56  ames!jaw
X * Configured fast table lookup for 32-bit machines.
X * This cuts user time in half for b <= FBITS, and is useful for news batching
X * from VAX to PDP sites.  Also sped up decompress() [fwrite->putc] and
X * added signal catcher [plus beef in writeerr()] to delete effluvia.
X *
X * Revision 2.0   84/08/28  22:00:00  petsd!joe
X * Add check for foreground before prompting user.  Insert maxbits into
X * compressed file.  Force file being uncompressed to end with ".Z".
X * Added "-c" flag and "zcat".  Prepared for release.
X *
X * Revision 1.10  84/08/24  18:28:00  turtlevax!ken
X * Will only compress regular files (no directories), added a magic number
X * header (plus an undocumented -n flag to handle old files without headers),
X * added -f flag to force overwriting of possibly existing destination file,
X * otherwise the user is prompted for a response.  Will tack on a .Z to a
X * filename if it doesn't have one when decompressing.  Will only replace
X * file if it was compressed.
X *
X * Revision 1.9  84/08/16  17:28:00  turtlevax!ken
X * Removed scanargs(), getopt(), added .Z extension and unlimited number of
X * filenames to compress.  Flags may be clustered (-Ddvb12) or separated
X * (-D -d -v -b 12), or combination thereof.  Modes and other status is
X * copied with copystat().  -O bug for 4.2 seems to have disappeared with
X * 1.8.
X *
X * Revision 1.8  84/08/09  23:15:00  joe
X * Made it compatible with vax version, installed jim's fixes/enhancements
X *
X * Revision 1.6  84/08/01  22:08:00  joe
X * Sped up algorithm significantly by sorting the compress chain.
X *
X * Revision 1.5  84/07/13  13:11:00  srd
X * Added C version of vax asm routines.  Changed structure to arrays to
X * save much memory.  Do unsigned compares where possible (faster on
X * Perkin-Elmer)
X *
X * Revision 1.4  84/07/05  03:11:11  thomas
X * Clean up the code a little and lint it.  (Lint complains about all
X * the regs used in the asm, but I'm not going to "fix" this.)
X *
X * Revision 1.3  84/07/05  02:06:54  thomas
X * Minor fixes.
X *
X * Revision 1.2  84/07/05  00:27:27  thomas
X * Add variable bit length output.
X *
X */
Xstatic char rcs_ident[] = "$Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $";
X
X#include <stdio.h>
X#include <ctype.h>
X#include <signal.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#ifdef BSD4_2
X#include <sys/dir.h>
X#endif
X
X#define ARGVAL() (*++(*argv) || (--argc && *++argv))
X
Xint n_bits;				/* number of bits/code */
Xint maxbits = BITS;			/* user settable max # bits/code */
Xcode_int maxcode;			/* maximum code, given n_bits */
Xcode_int maxmaxcode = 1 << BITS;	/* should NEVER generate this code */
X#ifdef COMPATIBLE		/* But wrong! */
X# define MAXCODE(n_bits)	(1 << (n_bits) - 1)
X#else
X# define MAXCODE(n_bits)	((1 << (n_bits)) - 1)
X#endif /* COMPATIBLE */
X
X#ifdef XENIX_16
Xcount_int htab0[8192];
Xcount_int htab1[8192];
Xcount_int htab2[8192];
Xcount_int htab3[8192];
Xcount_int htab4[8192];
Xcount_int htab5[8192];
Xcount_int htab6[8192];
Xcount_int htab7[8192];
Xcount_int htab8[HSIZE-65536];
Xcount_int * htab[9] = {
X	htab0, htab1, htab2, htab3, htab4, htab5, htab6, htab7, htab8 };
X
X#define htabof(i)	(htab[(i) >> 13][(i) & 0x1fff])
Xunsigned short code0tab[16384];
Xunsigned short code1tab[16384];
Xunsigned short code2tab[16384];
Xunsigned short code3tab[16384];
Xunsigned short code4tab[16384];
Xunsigned short * codetab[5] = {
X	code0tab, code1tab, code2tab, code3tab, code4tab };
X
X#define codetabof(i)	(codetab[(i) >> 14][(i) & 0x3fff])
X
X#else	/* Normal machine */
Xcount_int htab [HSIZE];
Xunsigned short codetab [HSIZE];
X#define htabof(i)	htab[i]
X#define codetabof(i)	codetab[i]
X#endif	/* XENIX_16 */
Xcode_int hsize = HSIZE;			/* for dynamic table sizing */
Xcount_int fsize;
X
X/*
X * To save much memory, we overlay the table used by compress() with those
X * used by decompress().  The tab_prefix table is the same size and type
X * as the codetab.  The tab_suffix table needs 2**BITS characters.  We
X * get this from the beginning of htab.  The output stack uses the rest
X * of htab, and contains characters.  There is plenty of room for any
X * possible stack (stack used to be 8000 characters).
X */
X
X#define tab_prefixof(i)	codetabof(i)
X#ifdef XENIX_16
X# define tab_suffixof(i)	((char_type *)htab[(i)>>15])[(i) & 0x7fff]
X# define de_stack		((char_type *)(htab2))
X#else	/* Normal machine */
X# define tab_suffixof(i)	((char_type *)(htab))[i]
X# define de_stack		((char_type *)&tab_suffixof(1<<BITS))
X#endif	/* XENIX_16 */
X
Xcode_int free_ent = 0;			/* first unused entry */
Xint exit_stat = 0;
X
Xcode_int getcode();
X
XUsage() {
X#ifdef DEBUG
Xfprintf(stderr,"Usage: compress [-dDVfcr] [-b maxbits] [file ...]\n");
X}
Xint debug = 0;
X#else
Xfprintf(stderr,"Usage: compress [-dfvcVr] [-b maxbits] [file ...]\n");
X}
X#endif /* DEBUG */
Xint nomagic = 0;	/* Use a 3-byte magic number header, unless old file */
Xint zcat_flg = 0;	/* Write output on stdout, suppress messages */
Xint quiet = 1;		/* don't tell me about compression */
X
X/*
X * block compression parameters -- after all codes are used up,
X * and compression rate changes, start over.
X */
Xint block_compress = BLOCK_MASK;
Xint clear_flg = 0;
Xlong int ratio = 0;
X#define CHECK_GAP 10000	/* ratio check interval */
Xcount_int checkpoint = CHECK_GAP;
X/*
X * the next two codes should not be changed lightly, as they must not
X * lie within the contiguous general code space.
X */ 
X#define FIRST	257	/* first free entry */
X#define	CLEAR	256	/* table clear output code */
X
Xint force = 0;
Xchar ofname [100];
X#ifdef DEBUG
Xint verbose = 0;
X#endif /* DEBUG */
Xint (*bgnd_flag)();
X
Xint do_decomp = 0;
X
X/*****************************************************************
X * TAG( main )
X *
X * Algorithm from "A Technique for High Performance Data Compression",
X * Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19.
X *
X * Usage: compress [-dfvc] [-b bits] [file ...]
X * Inputs:
X *	-d:	    If given, decompression is done instead.
X *
X *      -c:         Write output on stdout, don't remove original.
X *
X *      -b:         Parameter limits the max number of bits/code.
X *
X *	-f:	    Forces output file to be generated, even if one already
X *		    exists, and even if no space is saved by compressing.
X *		    If -f is not used, the user will be prompted if stdin is
X *		    a tty, otherwise, the output file will not be overwritten.
X *
X *      -v:	    Write compression statistics
X *
X *	-r:		Recursive. If a filename is a directory, descend
X *			into it and compress everything in it.
X *
X * 	file ...:   Files to be compressed.  If none specified, stdin
X *		    is used.
X * Outputs:
X *	file.Z:	    Compressed form of file with same mode, owner, and utimes
X * 	or stdout   (if stdin used as input)
X *
X * Assumptions:
X *	When filenames are given, replaces with the compressed version
X *	(.Z suffix) only if the file decreases in size.
X * Algorithm:
X * 	Modified Lempel-Ziv method (LZW).  Basically finds common
X * substrings and replaces them with a variable size code.  This is
X * deterministic, and can be done on the fly.  Thus, the decompression
X * procedure needs no input table, but tracks the way the table was built.
X */
X
Xint overwrite = 0;	/* Do not overwrite unless given -f flag */
Xint recursive = 0;  /* compress directories */
Xmain( argc, argv )
Xregister int argc; char **argv;
X{
X    char **filelist, **fileptr;
X    char *cp, *rindex(), *malloc();
X    extern onintr(), oops();
X
X
X    if ( (bgnd_flag = signal ( SIGINT, SIG_IGN )) != SIG_IGN ) {
X	signal ( SIGINT, onintr );
X	signal ( SIGSEGV, oops );
X    }
X
X#ifdef COMPATIBLE
X    nomagic = 1;	/* Original didn't have a magic number */
X#endif /* COMPATIBLE */
X
X    filelist = fileptr = (char **)(malloc(argc * sizeof(*argv)));
X    *filelist = NULL;
X
X    if((cp = rindex(argv[0], '/')) != 0) {
X	cp++;
X    } else {
X	cp = argv[0];
X    }
X    if(strcmp(cp, "uncompress") == 0) {
X	do_decomp = 1;
X    } else if(strcmp(cp, "zcat") == 0) {
X	do_decomp = 1;
X	zcat_flg = 1;
X    }
X
X#ifdef BSD4_2
X    /* 4.2BSD dependent - take it out if not */
X    setlinebuf( stderr );
X#endif /* BSD4_2 */
X
X    /* Argument Processing
X     * All flags are optional.
X     * -D => debug
X     * -V => print Version; debug verbose
X     * -d => do_decomp
X     * -v => unquiet
X     * -f => force overwrite of output file
X     * -n => no header: useful to uncompress old files
X     * -b maxbits => maxbits.  If -b is specified, then maxbits MUST be
X     *	    given also.
X     * -c => cat all output to stdout
X     * -C => generate output compatible with compress 2.0.
X     * -r => recursively compress directories
X     * if a string is left, must be an input filename.
X     */
X    for (argc--, argv++; argc > 0; argc--, argv++) {
X	if (**argv == '-') {	/* A flag argument */
X	    while (*++(*argv)) {	/* Process all flags in this arg */
X		switch (**argv) {
X#ifdef DEBUG
X		    case 'D':
X			debug = 1;
X			break;
X		    case 'V':
X			verbose = 1;
X			version();
X			break;
X#else
X		    case 'V':
X			version();
X			break;
X#endif /* DEBUG */
X		    case 'v':
X			quiet = 0;
X			break;
X		    case 'd':
X			do_decomp = 1;
X			break;
X		    case 'f':
X		    case 'F':
X			overwrite = 1;
X			force = 1;
X			break;
X		    case 'n':
X			nomagic = 1;
X			break;
X		    case 'C':
X			block_compress = 0;
X			break;
X		    case 'b':
X			if (!ARGVAL()) {
X			    fprintf(stderr, "Missing maxbits\n");
X			    Usage();
X			    exit(1);
X			}
X			maxbits = atoi(*argv);
X			goto nextarg;
X		    case 'c':
X			zcat_flg = 1;
X			break;
X		    case 'q':
X			quiet = 1;
X			break;
X		    case 'r':
X		    case 'R':
X			recursive = 1;
X			break;
X		    default:
X			fprintf(stderr, "Unknown flag: '%c'; ", **argv);
X			Usage();
X			exit(1);
X		}
X	    }
X	}
X	else {		/* Input file name */
X	    *fileptr++ = *argv;	/* Build input file list */
X	    *fileptr = NULL;
X	    /* process nextarg; */
X	}
X	nextarg: continue;
X    }
X
X    if(maxbits < INIT_BITS) maxbits = INIT_BITS;
X    if (maxbits > BITS) maxbits = BITS;
X    maxmaxcode = 1 << maxbits;
X
X    if (*filelist != NULL) {
X	for (fileptr = filelist; *fileptr; fileptr++) {
X		comprexx(fileptr);
X	}
X	} else {		/* Standard input */
X	if (do_decomp == 0) {
X		compress();
X		if(!quiet)
X			putc('\n', stderr);
X	} else {
X		/* Check the magic number */
X		if (nomagic == 0) {
X		if ((getchar()!=(magic_header[0] & 0xFF))
X		 || (getchar()!=(magic_header[1] & 0xFF))) {
X			fprintf(stderr, "stdin: not in compressed format\n");
X			exit(1);
X		}
X		maxbits = getchar();	/* set -b from file */
X		block_compress = maxbits & BLOCK_MASK;
X		maxbits &= BIT_MASK;
X		maxmaxcode = 1 << maxbits;
X		fsize = 100000;		/* assume stdin large for USERMEM */
X		if(maxbits > BITS) {
X			fprintf(stderr,
X			"stdin: compressed with %d bits, can only handle %d bits\n",
X			maxbits, BITS);
X			exit(1);
X		}
X		}
X#ifndef DEBUG
X		decompress();
X#else   DEBUG
X		if (debug == 0)	decompress();
X		else		printcodes();
X		if (verbose)	dump_tab();
X#endif DEBUG
X	}
X	}
X	exit(exit_stat);
X}
Xcomprexx(fileptr)
Xchar **fileptr;
X{
X	struct stat statbuf,insbuf;
X	char tempname[100];
X	if (lstat(*fileptr,&insbuf) == -1) {
X		perror(*fileptr);
X		return;
X	}
X	switch (insbuf.st_mode & S_IFMT) {
X
X	case S_IFDIR:
X		if (recursive)
X			compdir(*fileptr);
X		break;
X
X	case S_IFREG:
X	    exit_stat = 0;
X	    if (do_decomp != 0) {			/* DECOMPRESSION */
X		/* Check for .Z suffix */
X		if (strcmp(*fileptr + strlen(*fileptr) - 2, ".Z") != 0) {
X/*
X** I don't believe this is the correct behavior (or at least, I don't 
X** like it.) - DWM
X*/
X#ifdef notdef
X		    /* No .Z: tack one on */
X		    strcpy(tempname, *fileptr);
X		    strcat(tempname, ".Z");
X		    *fileptr = tempname;
X#endif notdef
X		    return;
X		}
X		/* Open input file */
X		if ((freopen(*fileptr, "r", stdin)) == NULL) {
X				perror(*fileptr); return;
X		}
X		/* Check the magic number */
X		if (nomagic == 0) {
X		    if ((getchar() != (magic_header[0] & 0xFF))
X		     || (getchar() != (magic_header[1] & 0xFF))) {
X			fprintf(stderr, "%s: not in compressed format\n",
X			    *fileptr);
X					return;
X		    }
X		    maxbits = getchar();	/* set -b from file */
X		    block_compress = maxbits & BLOCK_MASK;
X		    maxbits &= BIT_MASK;
X		    maxmaxcode = 1 << maxbits;
X		    if(maxbits > BITS) {
X			fprintf(stderr,
X			"%s: compressed with %d bits, can only handle %d bits\n",
X			*fileptr, maxbits, BITS);
X					return;
X		    }
X		}
X		/* Generate output filename */
X		strcpy(ofname, *fileptr);
X		/* Check for .Z suffix */
X		if (strcmp(*fileptr + strlen(*fileptr) - 2, ".Z") == 0) {
X		  ofname[strlen(*fileptr) - 2] = '\0';  /* Strip off .Z */
X		}
X	    } else {					/* COMPRESSION */
X		if (strcmp(*fileptr + strlen(*fileptr) - 2, ".Z") == 0) {
X		    	fprintf(stderr, "%s: already has .Z suffix -- no change\n",
X			    *fileptr);
X		    return;
X		}
X		/* Open input file */
X		if ((freopen(*fileptr, "r", stdin)) == NULL) {
X		    perror(*fileptr); return;
X		}
X		stat ( *fileptr, &statbuf );
X		fsize = (long) statbuf.st_size;
X		/*
X		 * tune hash table size for small files -- ad hoc,
X		 * but the sizes match earlier #defines, which
X		 * serve as upper bounds on the number of output codes. 
X		 */
X		hsize = HSIZE;
X		if ( fsize < (1 << 12) )
X		    hsize = min ( 5003, HSIZE );
X		else if ( fsize < (1 << 13) )
X		    hsize = min ( 9001, HSIZE );
X		else if ( fsize < (1 << 14) )
X		    hsize = min ( 18013, HSIZE );
X		else if ( fsize < (1 << 15) )
X		    hsize = min ( 35023, HSIZE );
X		else if ( fsize < 47000 )
X		    hsize = min ( 50021, HSIZE );
X
X		/* Generate output filename */
X		strcpy(ofname, *fileptr);
X#ifndef BSD4_2		/* Short filenames */
X		if ((cp=rindex(ofname,'/')) != NULL)	cp++;
X		else					cp = ofname;
X		if (strlen(cp) > 12) {
X		    fprintf(stderr,"%s: filename too long to tack on .Z\n",cp);
X				return;
X		}
X#endif  /* BSD4_2		Long filenames allowed */
X		strcat(ofname, ".Z");
X	    }
X	    /* Check for overwrite of existing file */
X	    if (overwrite == 0 && zcat_flg == 0) {
X		if (stat(ofname, &statbuf) == 0) {
X		    char response[2];
X		    response[0] = 'n';
X		    fprintf(stderr, "%s already exists;", ofname);
X		    if (foreground()) {
X			fprintf(stderr, " do you wish to overwrite %s (y or n)? ",
X			ofname);
X			fflush(stderr);
X			read(2, response, 2);
X			while (response[1] != '\n') {
X			    if (read(2, response+1, 1) < 0) {	/* Ack! */
X				perror("stderr"); break;
X			    }
X			}
X		    }
X		    if (response[0] != 'y') {
X			fprintf(stderr, "\tnot overwritten\n");
X			return;
X		    }
X		}
X	    }
X	    if(zcat_flg == 0) {		/* Open output file */
X		if (freopen(ofname, "w", stdout) == NULL) {
X		    perror(ofname);
X			return;
X		}
X		if(!quiet)
X			fprintf(stderr, "%s: ", *fileptr);
X	    }
X
X	    /* Actually do the compression/decompression */
X	    if (do_decomp == 0)	compress();
X#ifndef DEBUG
X	    else			decompress();
X#else
X	    else if (debug == 0)	decompress();
X	    else			printcodes();
X	    if (verbose)		dump_tab();
X#endif /* DEBUG */
X	    if(zcat_flg == 0) {
X		copystat(*fileptr, ofname);	/* Copy stats */
X		if((exit_stat == 1) || (!quiet))
X			putc('\n', stderr);
X	    }
X	default:
X		break;
X	} /* end switch */
X	return;
X} /* end comprexx */
X
Xcompdir(dir)
Xchar *dir;
X{
X	DIR *dirp;
X	register struct direct *dp;
X	char nbuf[1024];
X	char *nptr = nbuf;
X	dirp = opendir(dir);
X	if (dirp == NULL) {
X		printf("%s unreadable\n", dir);		/* not stderr! */
X		return ;
X	}
X	while (dp = readdir(dirp)) {
X		if (dp->d_ino == 0)
X			continue;
X		if (strcmp(dp->d_name,".") == 0 || strcmp(dp->d_name,"..") == 0)
X			continue;
X		strcpy(nbuf,dir);
X		strcat(nbuf,"/");
X		strcat(nbuf,dp->d_name);
X		comprexx(&nptr);
X  	}
X	closedir(dirp);
X	return;
X} /* end compdir */
X
Xstatic int offset;
Xlong int in_count = 1;			/* length of input */
Xlong int bytes_out;			/* length of compressed output */
Xlong int out_count = 0;			/* # of codes output (for debugging) */
X
X/*
X * compress stdin to stdout
X *
X * Algorithm:  use open addressing double hashing (no chaining) on the 
X * prefix code / next character combination.  We do a variant of Knuth's
X * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
X * secondary probe.  Here, the modular division first probe is gives way
X * to a faster exclusive-or manipulation.  Also do block compression with
X * an adaptive reset, whereby the code table is cleared when the compression
X * ratio decreases, but after the table fills.  The variable-length output
X * codes are re-sized at this point, and a special CLEAR code is generated
X * for the decompressor.  Late addition:  construct the table according to
X * file size for noticeable speed improvement on small files.  Please direct
X * questions about this implementation to ames!jaw.
X */
X
Xcompress() {
X    register long fcode;
X    register code_int i = 0;
X    register int c;
X    register code_int ent;
X#ifdef XENIX_16
X    register code_int disp;
X#else	/* Normal machine */
X    register int disp;
X#endif
X    register code_int hsize_reg;
X    register int hshift;
X
X#ifndef COMPATIBLE
X    if (nomagic == 0) {
X	putchar(magic_header[0]); putchar(magic_header[1]);
X	putchar((char)(maxbits | block_compress));
X	if(ferror(stdout))
X		writeerr();
X    }
X#endif /* COMPATIBLE */
X
X    offset = 0;
X    bytes_out = 3;		/* includes 3-byte header mojo */
X    out_count = 0;
X    clear_flg = 0;
X    ratio = 0;
X    in_count = 1;
X    checkpoint = CHECK_GAP;
X    maxcode = MAXCODE(n_bits = INIT_BITS);
X    free_ent = ((block_compress) ? FIRST : 256 );
X
X    ent = getchar ();
X
X    hshift = 0;
X    for ( fcode = (long) hsize;  fcode < 65536L; fcode *= 2L )
X    	hshift++;
X    hshift = 8 - hshift;		/* set hash code range bound */
X
X    hsize_reg = hsize;
X    cl_hash( (count_int) hsize_reg);		/* clear hash table */
X
X#ifdef SIGNED_COMPARE_SLOW
X    while ( (c = getchar()) != (unsigned) EOF ) {
X#else
X    while ( (c = getchar()) != EOF ) {
X#endif
X	in_count++;
X	fcode = (long) (((long) c << maxbits) + ent);
X 	i = ((c << hshift) ^ ent);	/* xor hashing */
X
X	if ( htabof (i) == fcode ) {
X	    ent = codetabof (i);
X	    continue;
X	} else if ( (long)htabof (i) < 0 )	/* empty slot */
X	    goto nomatch;
X 	disp = hsize_reg - i;		/* secondary hash (after G. Knott) */
X	if ( i == 0 )
X	    disp = 1;
Xprobe:
X	if ( (i -= disp) < 0 )
X	    i += hsize_reg;
X
X	if ( htabof (i) == fcode ) {
X	    ent = codetabof (i);
X	    continue;
X	}
X	if ( (long)htabof (i) > 0 ) 
X	    goto probe;
Xnomatch:
X	output ( (code_int) ent );
X	out_count++;
X 	ent = c;
X#ifdef SIGNED_COMPARE_SLOW
X	if ( (unsigned) free_ent < (unsigned) maxmaxcode) {
X#else
X	if ( free_ent < maxmaxcode ) {
X#endif
X 	    codetabof (i) = free_ent++;	/* code -> hashtable */
X	    htabof (i) = fcode;
X	}
X	else if ( (count_int)in_count >= checkpoint && block_compress )
X	    cl_block ();
X    }
X    /*
X     * Put out the final code.
X     */
X    output( (code_int)ent );
X    out_count++;
X    output( (code_int)-1 );
X
X    /*
X     * Print out stats on stderr
X     */
X    if(zcat_flg == 0 && !quiet) {
X#ifdef DEBUG
X	fprintf( stderr,
X		"%ld chars in, %ld codes (%ld bytes) out, compression factor: ",
X		in_count, out_count, bytes_out );
X	prratio( stderr, in_count, bytes_out );
X	fprintf( stderr, "\n");
X	fprintf( stderr, "\tCompression as in compact: " );
X	prratio( stderr, in_count-bytes_out, in_count );
X	fprintf( stderr, "\n");
X	fprintf( stderr, "\tLargest code (of last block) was %d (%d bits)\n",
X		free_ent - 1, n_bits );
X#else /* !DEBUG */
X	fprintf( stderr, "Compression: " );
X	prratio( stderr, in_count-bytes_out, in_count );
X#endif /* DEBUG */
X    }
X    if(bytes_out > in_count)	/* exit(2) if no savings */
X	exit_stat = 2;
X    return;
X}
X
X/*****************************************************************
X * TAG( output )
X *
X * Output the given code.
X * Inputs:
X * 	code:	A n_bits-bit integer.  If == -1, then EOF.  This assumes
X *		that n_bits =< (long)wordsize - 1.
X * Outputs:
X * 	Outputs code to the file.
X * Assumptions:
X *	Chars are 8 bits long.
X * Algorithm:
X * 	Maintain a BITS character long buffer (so that 8 codes will
X * fit in it exactly).  Use the VAX insv instruction to insert each
X * code in turn.  When the buffer fills up empty it and start over.
X */
X
Xstatic char buf[BITS];
X
X#ifndef vax
Xchar_type lmask[9] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00};
Xchar_type rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
X#endif /* vax */
X
Xoutput( code )
Xcode_int  code;
X{
X#ifdef DEBUG
X    static int col = 0;
X#endif /* DEBUG */
X
X    /*
X     * On the VAX, it is important to have the register declarations
X     * in exactly the order given, or the asm will break.
X     */
X    register int r_off = offset, bits= n_bits;
X    register char * bp = buf;
X
X#ifdef DEBUG
X	if ( verbose )
X	    fprintf( stderr, "%5d%c", code,
X		    (col+=6) >= 74 ? (col = 0, '\n') : ' ' );
X#endif /* DEBUG */
X    if ( code >= 0 ) {
X#ifdef vax
X	/* VAX DEPENDENT!! Implementation on other machines is below.
X	 *
X	 * Translation: Insert BITS bits from the argument starting at
X	 * offset bits from the beginning of buf.
X	 */
X	0;	/* Work around for pcc -O bug with asm and if stmt */
X	asm( "insv	4(ap),r11,r10,(r9)" );
X#else /* not a vax */
X/* 
X * byte/bit numbering on the VAX is simulated by the following code
X */
X	/*
X	 * Get to the first byte.
X	 */
X	bp += (r_off >> 3);
X	r_off &= 7;
X	/*
X	 * Since code is always >= 8 bits, only need to mask the first
X	 * hunk on the left.
X	 */
X	*bp = (*bp & rmask[r_off]) | (code << r_off) & lmask[r_off];
X	bp++;
X	bits -= (8 - r_off);
X	code >>= 8 - r_off;
X	/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
X	if ( bits >= 8 ) {
X	    *bp++ = code;
X	    code >>= 8;
X	    bits -= 8;
X	}
X	/* Last bits. */
X	if(bits)
X	    *bp = code;
X#endif /* vax */
X	offset += n_bits;
X	if ( offset == (n_bits << 3) ) {
X	    bp = buf;
X	    bits = n_bits;
X	    bytes_out += bits;
X	    do
X		putchar(*bp++);
X	    while(--bits);
X	    offset = 0;
X	}
X
X	/*
X	 * If the next entry is going to be too big for the code size,
X	 * then increase it, if possible.
X	 */
X	if ( free_ent > maxcode || (clear_flg > 0))
X	{
X	    /*
X	     * Write the whole buffer, because the input side won't
X	     * discover the size increase until after it has read it.
X	     */
X	    if ( offset > 0 ) {
X		if( fwrite( buf, 1, n_bits, stdout ) != n_bits)
X			writeerr();
X		bytes_out += n_bits;
X	    }
X	    offset = 0;
X
X	    if ( clear_flg ) {
X    	        maxcode = MAXCODE (n_bits = INIT_BITS);
X	        clear_flg = 0;
X	    }
X	    else {
X	    	n_bits++;
X	    	if ( n_bits == maxbits )
X		    maxcode = maxmaxcode;
X	    	else
X		    maxcode = MAXCODE(n_bits);
X	    }
X#ifdef DEBUG
X	    if ( debug ) {
X		fprintf( stderr, "\nChange to %d bits\n", n_bits );
X		col = 0;
X	    }
X#endif /* DEBUG */
X	}
X    } else {
X	/*
X	 * At EOF, write the rest of the buffer.
X	 */
X	if ( offset > 0 )
X	    fwrite( buf, 1, (offset + 7) / 8, stdout );
X	bytes_out += (offset + 7) / 8;
X	offset = 0;
X	fflush( stdout );
X#ifdef DEBUG
X	if ( verbose )
X	    fprintf( stderr, "\n" );
X#endif /* DEBUG */
X	if( ferror( stdout ) )
X		writeerr();
X    }
X}
X
X/*
X * Decompress stdin to stdout.  This routine adapts to the codes in the
X * file building the "string" table on-the-fly; requiring no table to
X * be stored in the compressed file.  The tables used herein are shared
X * with those of the compress() routine.  See the definitions above.
X */
X
Xdecompress() {
X    register char_type *stackp;
X    register int finchar;
X    register code_int code, oldcode, incode;
X
X    /*
X     * As above, initialize the first 256 entries in the table.
X     */
X    maxcode = MAXCODE(n_bits = INIT_BITS);
X    for ( code = 255; code >= 0; code-- ) {
X	tab_prefixof(code) = 0;
X	tab_suffixof(code) = (char_type)code;
X    }
X    free_ent = ((block_compress) ? FIRST : 256 );
X
X    finchar = oldcode = getcode();
X    if(oldcode == -1)	/* EOF already? */
X	return;			/* Get out of here */
X    putchar( (char)finchar );		/* first code must be 8 bits = char */
X    if(ferror(stdout))		/* Crash if can't write */
X	writeerr();
X    stackp = de_stack;
X
X    while ( (code = getcode()) > -1 ) {
X
X	if ( (code == CLEAR) && block_compress ) {
X	    for ( code = 255; code >= 0; code-- )
X		tab_prefixof(code) = 0;
X	    clear_flg = 1;
X	    free_ent = FIRST - 1;
X	    if ( (code = getcode ()) == -1 )	/* O, untimely death! */
X		break;
X	}
X	incode = code;
X	/*
X	 * Special case for KwKwK string.
X	 */
X	if ( code >= free_ent ) {
X            *stackp++ = finchar;
X	    code = oldcode;
X	}
X
X	/*
X	 * Generate output characters in reverse order
X	 */
X#ifdef SIGNED_COMPARE_SLOW
X	while ( ((unsigned long)code) >= ((unsigned long)256) ) {
X#else
X	while ( code >= 256 ) {
X#endif
X	    *stackp++ = tab_suffixof(code);
X	    code = tab_prefixof(code);
X	}
X	*stackp++ = finchar = tab_suffixof(code);
X
X	/*
X	 * And put them out in forward order
X	 */
X	do
X	    putchar ( *--stackp );
X	while ( stackp > de_stack );
X
X	/*
X	 * Generate the new entry.
X	 */
X	if ( (code=free_ent) < maxmaxcode ) {
X	    tab_prefixof(code) = (unsigned short)oldcode;
X	    tab_suffixof(code) = finchar;
X	    free_ent = code+1;
X	} 
X	/*
X	 * Remember previous code.
X	 */
X	oldcode = incode;
X    }
X    fflush( stdout );
X    if(ferror(stdout))
X	writeerr();
X}
X
X/*****************************************************************
X * TAG( getcode )
X *
X * Read one code from the standard input.  If EOF, return -1.
X * Inputs:
X * 	stdin
X * Outputs:
X * 	code or -1 is returned.
X */
X
Xcode_int
Xgetcode() {
X    /*
X     * On the VAX, it is important to have the register declarations
X     * in exactly the order given, or the asm will break.
X     */
X    register code_int code;
X    static int offset = 0, size = 0;
X    static char_type buf[BITS];
X    register int r_off, bits;
X    register char_type *bp = buf;
X
X    if ( clear_flg > 0 || offset >= size || free_ent > maxcode ) {
X	/*
X	 * If the next entry will be too big for the current code
X	 * size, then we must increase the size.  This implies reading
X	 * a new buffer full, too.
X	 */
X	if ( free_ent > maxcode ) {
X	    n_bits++;
X	    if ( n_bits == maxbits )
X		maxcode = maxmaxcode;	/* won't get any bigger now */
X	    else
X		maxcode = MAXCODE(n_bits);
X	}
X	if ( clear_flg > 0) {
X    	    maxcode = MAXCODE (n_bits = INIT_BITS);
X	    clear_flg = 0;
X	}
X	size = fread( buf, 1, n_bits, stdin );
X	if ( size <= 0 )
X	    return -1;			/* end of file */
X	offset = 0;
X	/* Round size down to integral number of codes */
X	size = (size << 3) - (n_bits - 1);
X    }
X    r_off = offset;
X    bits = n_bits;
X#ifdef vax
X    asm( "extzv   r10,r9,(r8),r11" );
X#else /* not a vax */
X	/*
X	 * Get to the first byte.
X	 */
X	bp += (r_off >> 3);
X	r_off &= 7;
X	/* Get first part (low order bits) */
X#ifdef NO_UCHAR
X	code = ((*bp++ >> r_off) & rmask[8 - r_off]) & 0xff;
X#else
X	code = (*bp++ >> r_off);
X#endif /* NO_UCHAR */
X	bits -= (8 - r_off);
X	r_off = 8 - r_off;		/* now, offset into code word */
X	/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
X	if ( bits >= 8 ) {
X#ifdef NO_UCHAR
X	    code |= (*bp++ & 0xff) << r_off;
X#else
X	    code |= *bp++ << r_off;
X#endif /* NO_UCHAR */
X	    r_off += 8;
X	    bits -= 8;
X	}
X	/* high order bits. */
X	code |= (*bp & rmask[bits]) << r_off;
X#endif /* vax */
X    offset += n_bits;
X
X    return code;
X}
X
Xchar *
Xrindex(s, c)		/* For those who don't have it in libc.a */
Xregister char *s, c;
X{
X	char *p;
X	for (p = NULL; *s; s++)
X	    if (*s == c)
X		p = s;
X	return(p);
X}
X
X#ifdef DEBUG
Xprintcodes()
X{
X    /*
X     * Just print out codes from input file.  For debugging.
X     */
X    code_int code;
X    int col = 0, bits;
X
X    bits = n_bits = INIT_BITS;
X    maxcode = MAXCODE(n_bits);
X    free_ent = ((block_compress) ? FIRST : 256 );
X    while ( ( code = getcode() ) >= 0 ) {
X	if ( (code == CLEAR) && block_compress ) {
X   	    free_ent = FIRST - 1;
X   	    clear_flg = 1;
X	}
X	else if ( free_ent < maxmaxcode )
X	    free_ent++;
X	if ( bits != n_bits ) {
X	    fprintf(stderr, "\nChange to %d bits\n", n_bits );
X	    bits = n_bits;
X	    col = 0;
X	}
X	fprintf(stderr, "%5d%c", code, (col+=6) >= 74 ? (col = 0, '\n') : ' ' );
X    }
X    putc( '\n', stderr );
X    exit( 0 );
X}
X
Xcode_int sorttab[1<<BITS];	/* sorted pointers into htab */
X
Xdump_tab()	/* dump string table */
X{
X    register int i, first;
X    register ent;
X#define STACK_SIZE	15000
X    int stack_top = STACK_SIZE;
X    register c;
X
X    if(do_decomp == 0) {	/* compressing */
X	register int flag = 1;
X
X	for(i=0; i<hsize; i++) {	/* build sort pointers */
X		if((long)htabof(i) >= 0) {
X			sorttab[codetabof(i)] = i;
X		}
X	}
X	first = block_compress ? FIRST : 256;
X	for(i = first; i < free_ent; i++) {
X		fprintf(stderr, "%5d: \"", i);
X		de_stack[--stack_top] = '\n';
X		de_stack[--stack_top] = '"';
X		stack_top = in_stack((htabof(sorttab[i])>>maxbits)&0xff, 
X                                     stack_top);
X		for(ent=htabof(sorttab[i]) & ((1<<maxbits)-1);
X		    ent > 256;
X		    ent=htabof(sorttab[ent]) & ((1<<maxbits)-1)) {
X			stack_top = in_stack(htabof(sorttab[ent]) >> maxbits,
X						stack_top);
X		}
X		stack_top = in_stack(ent, stack_top);
X		fwrite( &de_stack[stack_top], 1, STACK_SIZE-stack_top, stderr);
X	   	stack_top = STACK_SIZE;
X	}
X   } else if(!debug) {	/* decompressing */
X
X       for ( i = 0; i < free_ent; i++ ) {
X	   ent = i;
X	   c = tab_suffixof(ent);
X	   if ( isascii(c) && isprint(c) )
X	       fprintf( stderr, "%5d: %5d/'%c'  \"",
X			   ent, tab_prefixof(ent), c );
X	   else
X	       fprintf( stderr, "%5d: %5d/\\%03o \"",
X			   ent, tab_prefixof(ent), c );
X	   de_stack[--stack_top] = '\n';
X	   de_stack[--stack_top] = '"';
X	   for ( ; ent != NULL;
X		   ent = (ent >= FIRST ? tab_prefixof(ent) : NULL) ) {
X	       stack_top = in_stack(tab_suffixof(ent), stack_top);
X	   }
X	   fwrite( &de_stack[stack_top], 1, STACK_SIZE - stack_top, stderr );
X	   stack_top = STACK_SIZE;
X       }
X    }
X}
X
Xint
Xin_stack(c, stack_top)
X	register c, stack_top;
X{
X	if ( (isascii(c) && isprint(c) && c != '\\') || c == ' ' ) {
X	    de_stack[--stack_top] = c;
X	} else {
X	    switch( c ) {
X	    case '\n': de_stack[--stack_top] = 'n'; break;
X	    case '\t': de_stack[--stack_top] = 't'; break;
X	    case '\b': de_stack[--stack_top] = 'b'; break;
X	    case '\f': de_stack[--stack_top] = 'f'; break;
X	    case '\r': de_stack[--stack_top] = 'r'; break;
X	    case '\\': de_stack[--stack_top] = '\\'; break;
X	    default:
X	 	de_stack[--stack_top] = '0' + c % 8;
X	 	de_stack[--stack_top] = '0' + (c / 8) % 8;
X	 	de_stack[--stack_top] = '0' + c / 64;
X	 	break;
X	    }
X	    de_stack[--stack_top] = '\\';
X	}
X	return stack_top;
X}
X#endif /* DEBUG */
X
Xwriteerr()
X{
X    perror ( ofname );
X    unlink ( ofname );
X    exit ( 1 );
X}
X
Xcopystat(ifname, ofname)
Xchar *ifname, *ofname;
X{
X    struct stat statbuf;
X    int mode;
X    time_t timep[2];
X
X    fclose(stdout);
X    if (stat(ifname, &statbuf)) {		/* Get stat on input file */
X	perror(ifname);
X	return;
X    }
X    if ((statbuf.st_mode & S_IFMT/*0170000*/) != S_IFREG/*0100000*/) {
X	if(quiet)
X	    	fprintf(stderr, "%s: ", ifname);
X	fprintf(stderr, " -- not a regular file: unchanged");
X	exit_stat = 1;
X    } else if (statbuf.st_nlink > 1) {
X	if(quiet)
X	    	fprintf(stderr, "%s: ", ifname);
X	fprintf(stderr, " -- has %d other links: unchanged",
X		statbuf.st_nlink - 1);
X	exit_stat = 1;
X    } else if (exit_stat == 2 && (!force)) { /* No compression: remove file.Z */
X	if(!quiet)
X		fprintf(stderr, " -- file unchanged");
X    } else {			/* ***** Successful Compression ***** */
X	exit_stat = 0;
X	mode = statbuf.st_mode & 07777;
X	if (chmod(ofname, mode))		/* Copy modes */
X	    perror(ofname);
X	chown(ofname, statbuf.st_uid, statbuf.st_gid);	/* Copy ownership */
X	timep[0] = statbuf.st_atime;
X	timep[1] = statbuf.st_mtime;
X	utime(ofname, timep);	/* Update last accessed and modified times */
X	if (unlink(ifname))	/* Remove input file */
X	    perror(ifname);
X	if(!quiet)
X		fprintf(stderr, " -- replaced with %s", ofname);
X	return;		/* Successful return */
X    }
X
X    /* Unsuccessful return -- one of the tests failed */
X    if (unlink(ofname))
X	perror(ofname);
X}
X/*
X * This routine returns 1 if we are running in the foreground and stderr
X * is a tty.
X */
Xforeground()
X{
X	if(bgnd_flag) {	/* background? */
X		return(0);
X	} else {			/* foreground */
X		if(isatty(2)) {		/* and stderr is a tty */
X			return(1);
X		} else {
X			return(0);
X		}
X	}
X}
X
Xonintr ( )
X{
X    unlink ( ofname );
X    exit ( 1 );
X}
X
Xoops ( )	/* wild pointer -- assume bad input */
X{
X    if ( do_decomp == 1 ) 
X    	fprintf ( stderr, "uncompress: corrupt input\n" );
X    unlink ( ofname );
X    exit ( 1 );
X}
X
Xcl_block ()		/* table clear for block compress */
X{
X    register long int rat;
X
X    checkpoint = in_count + CHECK_GAP;
X#ifdef DEBUG
X	if ( debug ) {
X    		fprintf ( stderr, "count: %ld, ratio: ", in_count );
X     		prratio ( stderr, in_count, bytes_out );
X		fprintf ( stderr, "\n");
X	}
X#endif /* DEBUG */
X
X    if(in_count > 0x007fffff) {	/* shift will overflow */
X	rat = bytes_out >> 8;
X	if(rat == 0) {		/* Don't divide by zero */
X	    rat = 0x7fffffff;
X	} else {
X	    rat = in_count / rat;
X	}
X    } else {
X	rat = (in_count << 8) / bytes_out;	/* 8 fractional bits */
X    }
X    if ( rat > ratio ) {
X	ratio = rat;
X    } else {
X	ratio = 0;
X#ifdef DEBUG
X	if(verbose)
X		dump_tab();	/* dump string table */
X#endif
X 	cl_hash ( (count_int) hsize );
X	free_ent = FIRST;
X	clear_flg = 1;
X	output ( (code_int) CLEAR );
X#ifdef DEBUG
X	if(debug)
X    		fprintf ( stderr, "clear\n" );
X#endif /* DEBUG */
X    }
X}
X
Xcl_hash(hsize)		/* reset code table */
X	register count_int hsize;
X{
X#ifndef XENIX_16	/* Normal machine */
X	register count_int *htab_p = htab+hsize;
X#else
X	register j;
X	register long k = hsize;
X	register count_int *htab_p;
X#endif
X	register long i;
X	register long m1 = -1;
X
X#ifdef XENIX_16
X    for(j=0; j<=8 && k>=0; j++,k-=8192) {
X	i = 8192;
X	if(k < 8192) {
X		i = k;
X	}
X	htab_p = &(htab[j][i]);
X	i -= 16;
X	if(i > 0) {
X#else
X	i = hsize - 16;
X#endif
X 	do {				/* might use Sys V memset(3) here */
X		*(htab_p-16) = m1;
X		*(htab_p-15) = m1;
X		*(htab_p-14) = m1;
X		*(htab_p-13) = m1;
X		*(htab_p-12) = m1;
X		*(htab_p-11) = m1;
X		*(htab_p-10) = m1;
X		*(htab_p-9) = m1;
X		*(htab_p-8) = m1;
X		*(htab_p-7) = m1;
X		*(htab_p-6) = m1;
X		*(htab_p-5) = m1;
X		*(htab_p-4) = m1;
X		*(htab_p-3) = m1;
X		*(htab_p-2) = m1;
X		*(htab_p-1) = m1;
X		htab_p -= 16;
X	} while ((i -= 16) >= 0);
X#ifdef XENIX_16
X	}
X    }
X#endif
X    	for ( i += 16; i > 0; i-- )
X		*--htab_p = m1;
X}
X
Xprratio(stream, num, den)
XFILE *stream;
Xlong int num, den;
X{
X	register int q;			/* Doesn't need to be long */
X
X	if(num > 214748L) {		/* 2147483647/10000 */
X		q = num / (den / 10000L);
X	} else {
X		q = 10000L * num / den;		/* Long calculations, though */
X	}
X	if (q < 0) {
X		putc('-', stream);
X		q = -q;
X	}
X	fprintf(stream, "%d.%02d%%", q / 100, q % 100);
X}
X
Xversion()
X{
X	fprintf(stderr, "%s\n", rcs_ident);
X	fprintf(stderr, "Options: ");
X#ifdef vax
X	fprintf(stderr, "vax, ");
X#endif
X#ifdef NO_UCHAR
X	fprintf(stderr, "NO_UCHAR, ");
X#endif
X#ifdef SIGNED_COMPARE_SLOW
X	fprintf(stderr, "SIGNED_COMPARE_SLOW, ");
X#endif
X#ifdef XENIX_16
X	fprintf(stderr, "XENIX_16, ");
X#endif
X#ifdef COMPATIBLE
X	fprintf(stderr, "COMPATIBLE, ");
X#endif
X#ifdef DEBUG
X	fprintf(stderr, "DEBUG, ");
X#endif
X#ifdef BSD4_2
X	fprintf(stderr, "BSD4_2, ");
X#endif
X	fprintf(stderr, "BITS = %d\n", BITS);
X}
END_OF_FILE
if test 41053 -ne `wc -c <'compress.c'`; then
    echo shar: \"'compress.c'\" unpacked with wrong size!
fi
# end of 'compress.c'
fi
if test -f 'compress.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'compress.1'\"
else
echo shar: Extracting \"'compress.1'\" \(5394 characters\)
sed "s/^X//" >'compress.1' <<'END_OF_FILE'
X.PU
X.TH COMPRESS 1 local
X.SH NAME
Xcompress, uncompress, zcat \- compress and expand data
X.SH SYNOPSIS
X.ll +8
X.B compress
X[
X.B \-f
X] [
X.B \-v
X] [
X.B \-c
X] [
X.B \-V
X] [
X.B \-r
X] [
X.B \-b
X.I bits
X] [
X.I "name \&..."
X]
X.ll -8
X.br
X.B uncompress
X[
X.B \-f
X] [
X.B \-v
X] [
X.B \-c
X] [
X.B \-V
X] [
X.I "name \&..."
X]
X.br
X.B zcat
X[
X.B \-V
X] [
X.I "name \&..."
X]
X.SH DESCRIPTION
X.I Compress
Xreduces the size of the named files using adaptive Lempel-Ziv coding.
XWhenever possible,
Xeach file is replaced by one with the extension
X.B "\&.Z,"
Xwhile keeping the same ownership modes, access and modification times.
XIf no files are specified, the standard input is compressed to the
Xstandard output.
XCompressed files can be restored to their original form using
X.I uncompress
Xor
X.I zcat.
X.PP
XThe
X.B \-f
Xoption will force compression of
X.I name.
XThis is useful for compressing an entire directory,
Xeven if some of the files do not actually shrink.
XIf
X.B \-f
Xis not given and
X.I compress
Xis run in the foreground,
Xthe user is prompted as to whether an existing file should be overwritten.
X.PP
XThe
X.B \-c
Xoption makes
X.I compress/uncompress
Xwrite to the standard output; no files are changed.
XThe nondestructive behavior of
X.I zcat
Xis identical to that of
X.I uncompress
X.B \-c.
X.PP
XIf the
X.B \-r
Xflag is specified, 
X.I compress
Xwill operate recursively. If any of the file names specified on the command
Xline are directories, 
X.I compress
Xwill descend into the directory and compress all the files it finds there.
X.PP
X.I Compress
Xuses the modified Lempel-Ziv algorithm popularized in
X"A Technique for High Performance Data Compression",
XTerry A. Welch,
X.I "IEEE Computer,"
Xvol. 17, no. 6 (June 1984), pp. 8-19.
XCommon substrings in the file are first replaced by 9-bit codes 257 and up.
XWhen code 512 is reached, the algorithm switches to 10-bit codes and
Xcontinues to use more bits until the
Xlimit specified by the
X.B \-b
Xflag is reached (default 16).
X.I Bits
Xmust be between 9 and 16.  The default can be changed in the source to allow
X.I compress
Xto be run on a smaller machine.
X.PP
XAfter the
X.I bits
Xlimit is attained,
X.I compress
Xperiodically checks the compression ratio.  If it is increasing,
X.I compress
Xcontinues to use the existing code dictionary.  However,
Xif the compression ratio decreases,
X.I compress
Xdiscards the table of substrings and rebuilds it from scratch.  This allows
Xthe algorithm to adapt to the next "block" of the file.
X.PP
XNote that the
X.B \-b
Xflag is omitted for
X.I uncompress,
Xsince the 
X.I bits
Xparameter specified during compression
Xis encoded within the output, along with
Xa magic number to ensure that neither decompression of random data nor
Xrecompression of compressed data is attempted. 
X.PP
X.ne 8
XThe amount of compression obtained depends on the size of the
Xinput, the number of
X.I bits
Xper code, and the distribution of common substrings.
XTypically, text such as source code or English
Xis reduced by 50\-60%.
XCompression is generally much better than that achieved by
XHuffman coding (as used in
X.IR pack ),
Xor adaptive Huffman coding
X.RI ( compact ),
Xand takes less time to compute.
X.PP
XUnder the
X.B \-v
Xoption,
Xa message is printed yielding the percentage of
Xreduction for each file compressed.
X.PP
XIf the
X.B \-V
Xoption is specified, the current version and compile options are printed on
Xstderr.
X.PP
XExit status is normally 0;
Xif the last file is larger after (attempted) compression, the status is 2;
Xif an error occurs, exit status is 1.
X.SH "SEE ALSO"
Xpack(1), compact(1)
X.SH "DIAGNOSTICS"
XUsage: compress [\-dfvcV] [\-b maxbits] [file ...]
X.in +8
XInvalid options were specified on the command line.
X.in -8
XMissing maxbits
X.in +8
XMaxbits must follow
X.BR \-b \.
X.in -8
X.IR file :
Xnot in compressed format
X.in +8
XThe file specified to
X.I uncompress
Xhas not been compressed.
X.in -8
X.IR file :
Xcompressed with 
X.I xx
Xbits, can only handle 
X.I yy
Xbits
X.in +8
X.I File
Xwas compressed by a program that could deal with
Xmore 
X.I bits
Xthan the compress code on this machine.
XRecompress the file with smaller
X.IR bits \.
X.in -8
X.IR file :
Xalready has .Z suffix -- no change
X.in +8
XThe file is assumed to be already compressed.
XRename the file and try again.
X.in -8
X.IR file :
Xfilename too long to tack on .Z
X.in +8
XThe file cannot be compressed because its name is longer than
X12 characters.
XRename and try again.
XThis message does not occur on BSD systems.
X.in -8
X.I file
Xalready exists; do you wish to overwrite (y or n)?
X.in +8
XRespond "y" if you want the output file to be replaced; "n" if not.
X.in -8
Xuncompress: corrupt input
X.in +8
XA SIGSEGV violation was detected which usually means that the input file has
Xbeen corrupted.
X.in -8
XCompression: 
X.I "xx.xx%"
X.in +8
XPercentage of the input saved by compression.
X(Relevant only for
X.BR \-v \.)
X.in -8
X-- not a regular file: unchanged
X.in +8
XWhen the input file is not a regular file,
X(e.g. a directory), it is
Xleft unaltered.
X.in -8
X-- has 
X.I xx 
Xother links: unchanged
X.in +8
XThe input file has links; it is left unchanged.  See
X.IR ln "(1)"
Xfor more information.
X.in -8
X-- file unchanged
X.in +8
XNo savings is achieved by
Xcompression.  The input remains virgin.
X.in -8
X.SH "BUGS"
XAlthough compressed files are compatible between machines with large memory,
X.BR \-b \12
Xshould be used for file transfer to architectures with 
Xa small process data space (64KB or less, as exhibited by the DEC PDP
Xseries, the Intel 80286, etc.)
END_OF_FILE
if test 5394 -ne `wc -c <'compress.1'`; then
    echo shar: \"'compress.1'\" unpacked with wrong size!
fi
# end of 'compress.1'
fi
echo shar: End of shell archive.
exit 0