[comp.sources.misc] Very small ZCAT program for decompressing *.Z files

karl@mstar.UUCP (05/14/87)

Sorry if this is too short -- I didn't know where else to put it.

This 'zcat' program works much like the standard public domain zcat
(which is usually linked to compress and uncompress), except that it
is only a filter -- no command line arguments are used.  We use it
frequently when shipping compressed software to customers over UUCP,
many of whom are not UNIX wizards and don't routinely get good stuff
like compress.c from usenet.

I was going to submit it to the Obfuscated C contest until I saw the
requirement about entries being original work.  This program is derived
directly from the public domain compress.c.  It requires that your
machine be able to support 16-bit decompression, which uses very large
arrays for hashing.

If somebody wanted to make a SHAR that could compress and uuencode
files, then include a small program (like this, but with uudecode,
too?) that could transform it back, I would kiss their feet.

#!/bin/sh
#
#    This is a SHAR archive.  To extract, remove everything
#    above the #!/bin/sh line and type "sh filename"
#
if `test ! -s ./zcat.c`
then
    echo "Writing ./zcat.c"
    sed 's/^X//' > ./zcat.c << '\End\of\File\ ./zcat.c'
X#include <stdio.h>
X#define d define
X#d G getchar()
X#d I if
X#d H(n) ((C*)h)[n]
X#d C char
X#d U unsigned long
X#d u unsigned short
X#d R register C*
X#d B(n) (1<<(n))
X#d V(a,b) (a&(B(b)-1))
X#d E {fprintf(stderr,"Bad COMPRESS file format\n");exit(1);}
Xint i,q,b,n,k=16,e=128,j=0,O=0,S=0;u t[69001];long o,v,c,m,M=B(16),f=0;U h[
X69001];C D[16];long g(){R p=D;I(j>0||O>=S||f>m){I(f>m)m= ++n==k?M:B(n)-1;I(j>0)
Xm=B(n=9)-1,j=0;S=fread(D,1,n,stdin);I(S<=0)return-1;O=0;S=(S<<3)-n+1;}q=O;b=n;p
X+=q>>3;q&=7;c=V(*p++>>q,8-q);q=8-q;b-=q;I(b>=8)c|=(*p++&255)<<q,q+=8,b-=8;c|=V(
X*p,b)<<q;O+=n;return c;}main(){I(G!=31||G!=157)E;k=G;e=k&128;k&=31;M=B(k);I(k>
X16)E;z();exit(0);}z(){long w;R s;m=B(n=9)-1;for(w=255;w>=0;w--)t[w]=0,H(w)=(C)w
X;f=e?257:256;i=o=g();I(o==-1)return;putchar((C)i);s=(C*)&H(B(16));while((w=g())
X>-1){I(w==256&&e){for(w=255;w>=0;w--)t[w]=0;j=1;f=256;I((w=g())==-1)break;}v=w;
XI(w>=f)*s++=i,w=o;while((U)w>=(U)256)*s++=H(w),w=t[w];*s++=i=H(w);do putchar(*
X--s);while(s>&H(B(16)));I((w=f)<M)t[w]=(u)o,H(w)=i,f=w+1;o=v;}}
\End\of\File\ ./zcat.c
else
    echo "Not overwriting ./zcat.c"
fi
echo "Finished archive 1 of 1"
exit
-- 
Karl Fox, Morning Star Technologies, Inc.    ...!{cbatt,cbosgd}!mstar!karl

jaw@aurora.UUCP (James A. Woods) (05/23/87)

# "Summer came and went, and I realized that the book was monstrous.
  [....] I thought of fire, but I feared that the burning of an 
  infinite book might likewise prove infinite and suffocate the
  planet with smoke." -- Jorge Luis Borges, "The Book of Sand"

     Some may doubt the efficacy of what Karl Fox suggested.
Though what I proffered is improved below, a self-decompressing shar
can pay off in CPU time as well as modem bandwidth.  For example,
using a 68020-class machine (Sun 3/160), 'shark'ing a 230KB /etc/hosts
took about 5 seconds to yield a 104KB file.  The inverse (old code)
required about 7 CPU (user+sys) seconds to compile (cc, no -O) the
decoders, 11 seconds to do the decoding (atob+zcat), and 11 seconds of shell
"here" document scanning (two passes).  The resulting 7KB per second
would likely beat 'uucp' processing time for the data if passed
by a Telebit modem, about the best that can be done for phones
(10-14 kbits/sec).  Bandwidth savings were knocked down to 40% by
the 25% expansion of 'btoa' (for the initiate, a base-85 converter by
Paul Rutter and Joe Orost, mapping 4->5 chars vs. the meager 3->4 of
'uuencode').  Bandwidth payoff for text starts at ~10KB, considering
the 2.5KB overhead.

     Well, I realized how much slop was still there, so I slimmed
'shark' (or 'sharc'? for 'arc' replacement) by another 250 bytes.
The code to follow uses 'tar' instead of 'shar' for speed (it's really just
'tarmail' with decoder source).  'The End' substitutes for 'ZZ'
for reliability (let's see, 128 exp 7 gives the rarity of a sabotaged
line, or better than disk CRC).  In addition to some other changes
(assuming globals start life at 0, tightened loops, shift replacement,
more quirky #defines, name shortening, etc.), you may dispense with
the recursive Capt. Beefheart "echo" for even more.  Go back to 'shar' if
you mail to non-Unix boxes w/o John Gilmore's PD 'tar'.

     -- jaw

-------- 'cut' here for a new 'shark' (do *not* shar -- keep intact) --------
#!/bin/sh
echo 'echo "Cleverly he dialed from within." -- D. Van Vliet, "Trout Mask Replica">/dev/null'
echo "echo decoding ..."
echo "cat>atob.c<<'ZZ'"
cat<<'TROUSER PRESS'
#include <stdio.h>
#define d define
#d R register
#d I if
#d E else
#d D {fprintf(stderr,"bad format or Csum to atob\n");exit(1);}
FILE *T;long X,M,C,w,B;
d(c)R c;{I(c=='z'){I(B)D E for(c=4;c--;)b(0);}E I(c>='!'&&(c<'!'+85))
{I(!B){w=c-'!';++B;}E I(B<4){w*=85;w+=c-'!';++B;}E{w=w*85+c-'!';b((int)((w>>24)
&255));b((int)((w>>16)&255));b((int)((w>>8)&255));b((int)(w&255));w=B=0;}}E D}
b(c)R c;{X^=c;M+=c;M++;I(C&0x80000000){C*=2;C++;}E{C*=2;}C+=c;putc(c,T);}main
(g,v)char **v;{R c;R long i;char n[99],Q[99];long A,B,x,s,r;I(g!=1){fprintf
(stderr,"bad args to %s\n",v[0]);exit(2);}sprintf(n,"/tmp/atob.%x",getpid());
T=fopen(n,"w+");I(!T)D unlink(n);for(;;){I(!fgets(Q,99,stdin))D I(!strcmp(Q,
"xbtoa Begin\n"))break;}while((c=getchar())!=EOF){I(c=='\n')continue;E I(c=='x')
break;E d(c);}I(scanf("btoa End N %ld %lx E %lx S %lx R %lx\n",&A,&B,&x,&s,&r)!=
5)D I(A!=B||x!=X||s!=M||r!=C)D E{fseek(T,0L,0);for(i=A;i--;)putchar(getc(T));}}
TROUSER PRESS
echo "ZZ"
echo "cc -o atob atob.c"
echo "cat>zcat.c<<'ZZ'"
cat<<'BAFFLE RHINOCEROSES'
#include <stdio.h>
#define d define
#d G getchar()
#d I if
#d H(n) ((C*)h)[n]
#d C char
#d U unsigned long
#d u unsigned short
#d R register C*
#d B(n) (1<<(n))
#d V(a,b) (a&(B(b)-1))
#d E {fprintf(stderr,"Bad COMPRESS file format\n");exit(1);}
#d N 256
#d L for(w=N;w--;)t[w]=0
int i,q,b,n,k=16,e=128,j,O,S;u t[69001];long o,v,c,m,M=B(16),f;U h[69001];C 
D[16];long g(){R p=D;I(j>0||O>=S||f>m){I(f>m)m= ++n==k?M:B(n)-1;I(j>0)m=B(n=9)
-1,j=0;S=fread(D,1,n,stdin);I(S<=0)return-1;O=0;S=S*8-n+1;}q=O;b=n;p+=q>>3;q&=7;
c=V(*p++>>q,8-q);q=8-q;b-=q;I(b>=8)c|=(*p++&255)<<q,q+=8,b-=8;c|=V(*p,b)<<q;
O+=n;return c;}main(){I(G!=31||G!=157)E;k=G;e=k&128;k&=31;M=B(k);I(k>16)E;z();}
z(){long w;R s;m=B(n=9)-1;L,H(w)=(C)w;f=e?257:N;i=o=g();I(o==-1)return;
putchar((C)i);s=(C*)&H(B(16));while((w=g())>-1){I(w==N&&e){L;j=1;f=N;I((w=g())
==-1)break;}v=w;I(w>=f)*s++=i,w=o;while((U)w>=(U)N)*s++=H(w),w=t[w];*s++=i=H(w);
do putchar(*--s);while(s>&H(B(16)));I((w=f)<M)t[w]=(u)o,H(w)=i,f=w+1;o=v;}}
BAFFLE RHINOCEROSES
echo "ZZ"
echo "cc -o zcat zcat.c"
echo "(atob|zcat|tar xvf - )<<'The End'"
tar cf - $* |compress|btoa
echo "The End"
echo "rm zcat* atob*"