home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
OL.LZH
/
PROGS.LZH
/
PRESS.ICN
< prev
next >
Wrap
Text File
|
1991-07-13
|
12KB
|
416 lines
############################################################################
#
# Name: press.icn
#
# Title: LZW Compression and Decompression Utility
#
# Author: Robert J. Alexander
#
# Date: December 5, 1989
#
############################################################################
#
# Note: This program is designed primarily to demonstrate the LZW
# compression process. It contains a lot of tracing toward
# that end and is too slow for practical use.
#
############################################################################
#
# Usage: press [-t] -c [-s n] [-f <compressed file>] <file to compress>...
# press [-t] -x <compressed file>...
#
# -c perform compression
# -x expand (decompress) compressed file
# -f output file for compression -- if missing standard output used
# -s maximum string table size
# (for compression only -- default = 1024)
# -t output trace info to standard error file
#
# If the specified maximum table size is positive, the string table is
# discarded when the maximum size is reached and rebuilt (recommended).
# If negative, the original table is not discarded, which might produce
# better results in some circumstances.
#
############################################################################
#
# Features that might be nice to add someday:
#
# Allow decompress output to standard output.
#
# Handle heirarchies.
#
# Way to list files in archive, and access individual files
#
############################################################################
#
# Links: options
#
############################################################################
global inchars,outchars,tinchars,toutchars,lzw_recycles,
lzw_stringTable,lzw_trace,wr,wrs,rf,wf
link options
procedure main(arg)
local compr,expand,fn,maxT,maxTableSize,opt,outfile,wfn
#
# Initialize.
#
opt := options(arg,"ts+f:cx")
if *arg = 0 then arg := ["-"]
lzw_trace := opt["t"]
expand := opt["x"]
compr := opt["c"]
outfile := opt["f"]
maxTableSize := \opt["s"]
if (/expand & /compr) then Usage()
wr := write ; wrs := writes
inchars := outchars := tinchars := toutchars := lzw_recycles := 0
#
# Process compression.
#
if \compr then {
if \expand then Usage()
if \outfile then
wf := open(outfile,"w") | stop("Can't open output file ",outfile)
#
# Loop to process files on command line.
#
every fn := !arg do {
if fn === outfile then next
wr(&errout,"\nFile \"",fn,"\"")
rf := if fn ~== "-" then open(fn) | &null else &input
if /rf then {
write(&errout,"Can't open input file \"",fn,"\" -- skipped")
next
}
write(wf,tail(fn))
maxT := compress(r,w,maxTableSize)
close(rf)
stats(maxT)
}
}
#
# Process decompression.
#
else if \expand then {
if \(compr | outfile | maxTableSize) then Usage()
#
# Loop to process files on command line.
#
every fn := !arg do {
rf := if fn ~== "-" then open(fn) | &null else &input
if /rf then {
write(&errout,"Can't open input file \"",fn,"\" -- skipped")
next
}
while wfn := read(rf) do {
wr(&errout,"\nFile \"",wfn,"\"")
wf := open(wfn,"w") | &null
if /wf then {
write(&errout,"Can't open output file \"",wfn,"\" -- quitting")
exit(1)
}
maxT := decompress(r,w)
close(wf)
stats(maxT)
}
close(rf)
}
}
else Usage()
#
# Write statistics
#
wr(&errout,"\nTotals: ",
"\n input = ",tinchars,
"\n output = ",toutchars,
"\n compression factor = ",(real(toutchars) / real(0 < tinchars)) | "")
end
procedure stats(maxTableSize)
#
# Write statistics
#
wr(&errout,
" input = ",inchars,
"\n output = ",outchars,
"\n compression factor = ",(real(outchars) / real(0 < inchars)) | "",
"\n table size = ",*lzw_stringTable,"/",maxTableSize,
" (",lzw_recycles," recycles)")
tinchars +:= inchars
toutchars +:= outchars
inchars := outchars := lzw_recycles := 0
return
end
procedure r()
return 1(reads(rf),inchars +:= 1)
end
procedure w(s)
return 1(writes(wf,s),outchars +:= *s)
end
procedure Usage()
stop("_
# Usage: icompress [-t] -c [-s n] <file to compress>...\n_
# icompress [-t] -x <compressed file>...\n_
#\n_
# -c perform compression\n_
# -x expand (decompress) compressed file\n_
# -f output file for compression -- if missing standard output used\n_
# -s maximum string table size\n_
# (for compression only -- default = 1024)\n_
# -t output trace info to standard error file\n_
#")
end
procedure tail(fn)
local i
i := 0
every i := find("/",fn)
return fn[i + 1:0]
end
#
# compress() -- LZW compression
#
# Arguments:
#
# inproc a procedure that returns a single character from
# the input stream.
#
# outproc a procedure that writes a single character (its
# argument) to the output stream.
#
# maxTableSize the maximum size to which the string table
# is allowed to grow before something is done about it.
# If the size is positive, the table is discarded and
# a new one started. If negative, it is retained, but
# no new entries are added.
#
procedure compress(inproc,outproc,maxTableSize)
local EOF,c,charTable,junk1,junk2,outcode,s,t,
tossTable,x
#
# Initialize.
#
/maxTableSize := 1024 # 10 "bits"
every outproc(!string(maxTableSize))
outproc("\n")
tossTable := maxTableSize
/lzw_recycles := 0
if maxTableSize < 0 then maxTableSize := -maxTableSize
charTable := table()
every c := !&cset do charTable[c] := ord(c)
EOF := charTable[*charTable] := *charTable # reserve code=256 for EOF
lzw_stringTable := copy(charTable)
#
# Compress the input stream.
#
s := inproc() | return maxTableSize
if \lzw_trace then {
wr(&errout,"\nInput string\tOutput code\tNew table entry")
wrs(&errout,"\"",image(s)[2:-1])
}
while c := inproc() do {
if \lzw_trace then
wrs(&errout,image(c)[2:-1])
if \lzw_stringTable[t := s || c] then s := t
else {
compress_output(outproc,junk2 := lzw_stringTable[s],junk1 := *lzw_stringTable)
if *lzw_stringTable < maxTableSize then
lzw_stringTable[t] := *lzw_stringTable
else if tossTable >= 0 then {
lzw_stringTable := copy(charTable)
lzw_recycles +:= 1
}
if \lzw_trace then
wrs(&errout,"\"\t\t",
image(char(*&cset > junk2) | junk2),
"(",junk1,")\t\t",lzw_stringTable[t]," = ",image(t),"\n\"")
s := c
}
}
compress_output(outproc,lzw_stringTable[s],*lzw_stringTable)
if \lzw_trace then
wr(&errout,"\"\t\t",
image(char(*&cset > (x := \lzw_stringTable[s] | 0)) | x))
compress_output(outproc,EOF,*lzw_stringTable)
compress_output(outproc)
return maxTableSize
end
procedure compress_output(outproc,code,stringTableSize)
local outcode
static max,bits,buffer,bufferbits,lastSize
#
# Initialize.
#
initial {
lastSize := 1000000
buffer := bufferbits := 0
}
#
# If this is "close" call, flush buffer and reinitialize.
#
if /code then {
outcode := &null
if bufferbits > 0 then
outproc(char(outcode := ishift(buffer,8 - bufferbits)))
lastSize := 1000000
buffer := bufferbits := 0
return outcode
}
#
# Expand output code size if necessary.
#
if stringTableSize < lastSize then {
max := 1
bits := 0
}
while stringTableSize > max do {
max *:= 2
bits +:= 1
}
lastSize := stringTableSize
#
# Merge new code into buffer.
#
buffer := ior(ishift(buffer,bits),code)
bufferbits +:= bits
#
# Output bits.
#
while bufferbits >= 8 do {
outproc(char(outcode := ishift(buffer,8 - bufferbits)))
buffer := ixor(buffer,ishift(outcode,bufferbits - 8))
bufferbits -:= 8
}
return outcode
end
############################################################################
#
# decompress() -- LZW decompression of compressed stream created
# by compress()
#
# Arguments:
#
# inproc a procedure that returns a single character from
# the input stream.
#
# outproc a procedure that writes a single character (its
# argument) to the output stream.
#
procedure decompress(inproc,outproc)
local EOF,c,charSize,code,i,maxTableSize,new_code,old_strg,
strg,tossTable
#
# Initialize.
#
maxTableSize := ""
while (c := inproc()) ~== "\n" do maxTableSize ||:= c
maxTableSize := integer(maxTableSize) |
stop("Invalid file format -- max table size missing")
tossTable := maxTableSize
/lzw_recycles := 0
if maxTableSize < 0 then maxTableSize := -maxTableSize
maxTableSize -:= 1
lzw_stringTable := list(*&cset)
every i := 1 to *lzw_stringTable do lzw_stringTable[i] := char(i - 1)
put(lzw_stringTable,EOF := *lzw_stringTable) # reserve code=256 for EOF
charSize := *lzw_stringTable
if \lzw_trace then
wr(&errout,"\nInput code\tOutput string\tNew table entry")
#
# Decompress the input stream.
#
while old_strg :=
lzw_stringTable[decompress_read_code(inproc,*lzw_stringTable,EOF) + 1] do {
if \lzw_trace then
wr(&errout,image(old_strg),"(",*lzw_stringTable,")",
"\t",image(old_strg))
outproc(old_strg)
c := old_strg[1]
(while new_code := decompress_read_code(inproc,*lzw_stringTable + 1,EOF) do {
strg := lzw_stringTable[new_code + 1] | old_strg || c
outproc(strg)
c := strg[1]
if \lzw_trace then
wr(&errout,image(char(*&cset > new_code) \ 1 | new_code),
"(",*lzw_stringTable + 1,")","\t",
image(strg),"\t\t",
*lzw_stringTable," = ",image(old_strg || c))
if *lzw_stringTable < maxTableSize then
put(lzw_stringTable,old_strg || c)
else if tossTable >= 0 then {
lzw_stringTable := lzw_stringTable[1:charSize + 1]
lzw_recycles +:= 1
break
}
old_strg := strg
}) | break # exit outer loop if this loop completed
}
decompress_read_code()
return maxTableSize
end
procedure decompress_read_code(inproc,stringTableSize,EOF)
local code
static max,bits,buffer,bufferbits,lastSize
#
# Initialize.
#
initial {
lastSize := 1000000
buffer := bufferbits := 0
}
#
# Reinitialize if called with no arguments.
#
if /inproc then {
lastSize := 1000000
buffer := bufferbits := 0
return
}
#
# Expand code size if necessary.
#
if stringTableSize < lastSize then {
max := 1
bits := 0
}
while stringTableSize > max do {
max *:= 2
bits +:= 1
}
#
# Read in more data if necessary.
#
while bufferbits < bits do {
buffer := ior(ishift(buffer,8),ord(inproc())) |
stop("Premature end of file")
bufferbits +:= 8
}
#
# Extract code from buffer and return.
#
code := ishift(buffer,bits - bufferbits)
buffer := ixor(buffer,ishift(code,bufferbits - bits))
bufferbits -:= bits
return EOF ~= code
end