aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDuncaen <mail@duncano.de>2017-11-21 19:19:30 +0100
committerDuncaen <mail@duncano.de>2017-11-21 19:19:30 +0100
commitc55407717e8223639fe9efc5c9438d13fbc3e9f5 (patch)
treedb64d9914861b72e10b25ccfeb4b6c90524828bb
parentc8358ed5dcdecc07be381249e7f1464bcde1a295 (diff)
downloadlobase-c55407717e8223639fe9efc5c9438d13fbc3e9f5.tar.gz
usr.bin/compress: import from OPENBSD_6_2
-rw-r--r--usr.bin/Makefile2
-rw-r--r--usr.bin/compress/Makefile25
-rw-r--r--usr.bin/compress/compress.1375
-rw-r--r--usr.bin/compress/compress.h83
-rw-r--r--usr.bin/compress/gzexe209
-rw-r--r--usr.bin/compress/gzexe.177
-rw-r--r--usr.bin/compress/gzip.1381
-rw-r--r--usr.bin/compress/gzopen.c539
-rw-r--r--usr.bin/compress/main.c961
-rw-r--r--usr.bin/compress/nullopen.c162
-rw-r--r--usr.bin/compress/zdiff108
-rw-r--r--usr.bin/compress/zdiff.1109
-rw-r--r--usr.bin/compress/zforce52
-rw-r--r--usr.bin/compress/zforce.152
-rw-r--r--usr.bin/compress/zmore78
-rw-r--r--usr.bin/compress/zmore.197
-rw-r--r--usr.bin/compress/znew135
-rw-r--r--usr.bin/compress/znew.170
-rw-r--r--usr.bin/compress/zopen.c795
19 files changed, 4309 insertions, 1 deletions
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
index c03f183..459491c 100644
--- a/usr.bin/Makefile
+++ b/usr.bin/Makefile
@@ -6,6 +6,6 @@ SUBDIR= apply awk basename bc biff cal calendar cmp colrm col column comm \
renice rev rs sed shar sort spell split stat tee time touch tr true \
tsort tty ul units uname unexpand uniq unvis uudecode uuencode vis wc \
what which xinstall htpasswd cu newsyslog sdiff banner cap_mkdb signify \
- unifdef whois xargs csplit
+ unifdef whois xargs csplit compress
SKIPDIR=file ftp cu
include ${.TOPDIR}/mk/bsd.subdir.mk
diff --git a/usr.bin/compress/Makefile b/usr.bin/compress/Makefile
new file mode 100644
index 0000000..0e798a0
--- /dev/null
+++ b/usr.bin/compress/Makefile
@@ -0,0 +1,25 @@
+# $OpenBSD: Makefile,v 1.22 2016/03/30 06:38:45 jmc Exp $
+
+.TOPDIR?=../..
+
+PROG= compress
+SRCS= main.c zopen.c gzopen.c nullopen.c
+MAN= compress.1 gzexe.1 gzip.1 zdiff.1 zforce.1 zmore.1 znew.1
+LINKS= ${BINDIR}/compress ${BINDIR}/uncompress \
+ ${BINDIR}/compress ${BINDIR}/zcat \
+ ${BINDIR}/compress ${BINDIR}/gzip \
+ ${BINDIR}/compress ${BINDIR}/gunzip \
+ ${BINDIR}/compress ${BINDIR}/gzcat \
+ ${BINDIR}/zdiff ${BINDIR}/zcmp \
+ ${BINDIR}/zmore ${BINDIR}/zless
+
+LDADD=-lz
+DPADD=${LIBZ}
+
+#afterinstall:
+# install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+# ${.CURDIR}/zmore ${.CURDIR}/zdiff \
+# ${.CURDIR}/zforce ${.CURDIR}/gzexe ${.CURDIR}/znew \
+# ${DESTDIR}${BINDIR}
+
+include ${.TOPDIR}/mk/bsd.prog.mk
diff --git a/usr.bin/compress/compress.1 b/usr.bin/compress/compress.1
new file mode 100644
index 0000000..cf84be1
--- /dev/null
+++ b/usr.bin/compress/compress.1
@@ -0,0 +1,375 @@
+.\" $OpenBSD: compress.1,v 1.48 2014/03/17 14:23:50 jmc Exp $
+.\" $NetBSD: compress.1,v 1.5 1995/03/26 09:44:34 glass Exp $
+.\"
+.\" Copyright (c) 1986, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" James A. Woods, derived from original work by Spencer Thomas
+.\" and Joseph Orost.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)compress.1 8.2 (Berkeley) 4/18/94
+.\"
+.Dd $Mdocdate: March 17 2014 $
+.Dt COMPRESS 1
+.Os
+.Sh NAME
+.Nm compress ,
+.Nm uncompress ,
+.Nm zcat
+.Nd compress and expand data (compress mode)
+.Sh SYNOPSIS
+.Nm compress
+.Op Fl 123456789cdfghlNnOqrtv
+.Op Fl b Ar bits
+.Op Fl o Ar filename
+.Op Fl S Ar suffix
+.Op Ar
+.Nm uncompress
+.Op Fl cfhlNnqrtv
+.Op Fl o Ar filename
+.Op Ar
+.Nm zcat
+.Op Fl fghqr
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility
+reduces the size of the named files using adaptive Lempel-Ziv coding,
+in compress mode.
+If invoked as
+.Nm compress Fl g ,
+the deflate mode of compression is chosen;
+see
+.Xr gzip 1
+for more information.
+Each file is renamed to the same name plus the extension
+.Dq .Z .
+As many of the modification time, access time, file flags, file mode,
+user ID, and group ID as allowed by permissions are retained in the
+new file.
+If compression would not reduce the size of a file,
+the file is ignored (unless
+.Fl f
+is used).
+.Pp
+The
+.Nm uncompress
+utility restores compressed files to their original form, renaming the
+files by removing the extension (or by using the stored name if the
+.Fl N
+flag is specified).
+It has the ability to restore files compressed by both
+.Nm
+and
+.Xr gzip 1 ,
+recognising the following extensions:
+.Dq .Z ,
+.Dq -Z ,
+.Dq _Z ,
+.Dq .gz ,
+.Dq -gz ,
+.Dq _gz ,
+.Dq .tgz ,
+.Dq -tgz ,
+.Dq _tgz ,
+.Dq .taz ,
+.Dq -taz ,
+and
+.Dq _taz .
+Extensions ending in
+.Dq tgz
+and
+.Dq taz
+are not removed when decompressing, instead they are converted to
+.Dq tar .
+.Pp
+The
+.Nm zcat
+command is equivalent in functionality to
+.Nm uncompress
+.Fl c .
+.Pp
+If renaming the files would cause files to be overwritten and the standard
+input device is a terminal, the user is prompted (on the standard error
+output) for confirmation.
+If prompting is not possible or confirmation is not received, the files
+are not overwritten.
+.Pp
+If no files are specified, the standard input is compressed or uncompressed
+to the standard output.
+If either the input or output files are not regular files, the checks for
+reduction in size and file overwriting are not performed, the input file is
+not removed, and the attributes of the input file are not retained.
+.Pp
+By default, when compressing using the deflate scheme
+.Pf ( Fl g ) ,
+the original file name and time stamp are stored in the compressed file.
+When uncompressing, this information is not used.
+Instead, the uncompressed file inherits the time stamp of the
+compressed version and the uncompressed file name is generated from
+the name of the compressed file as described above.
+These defaults may be overridden by the
+.Fl N
+and
+.Fl n
+flags, described below.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl 1...9
+Use the deflate scheme, with compression factor of
+.Fl 1
+to
+.Fl 9 .
+Compression factor
+.Fl 1
+is the fastest, but provides a poorer level of compression.
+Compression factor
+.Fl 9
+provides the best level of compression, but is relatively slow.
+The default is
+.Fl 6 .
+This option implies
+.Fl g .
+.It Fl b Ar bits
+Specify the
+.Ar bits
+code limit
+.Pq see below .
+.It Fl c
+Compressed or uncompressed output is written to the standard output.
+No files are modified (force
+.Nm zcat
+mode).
+.It Fl d
+Decompress the source files instead of compressing them (force
+.Nm uncompress
+mode).
+.It Fl f
+Force compression of
+.Ar file ,
+even if it is not actually reduced in size.
+Additionally, files are overwritten without prompting for confirmation.
+If the input data is not in a format recognized by
+.Nm
+and if the option
+.Fl c
+is also given, copy the input data without change
+to the standard output: let
+.Nm zcat
+behave as
+.Xr cat 1 .
+.It Fl g
+Use the deflate scheme, which reportedly provides better compression rates
+(force
+.Xr gzip 1
+mode).
+.It Fl h
+Print a short help message.
+.It Fl l
+List information for the specified compressed files.
+The following information is listed:
+.Bl -tag -width "compression ratio"
+.It compressed size
+Size of the compressed file.
+.It uncompressed size
+Size of the file when uncompressed.
+.It compression ratio
+Ratio of the difference between the compressed and uncompressed
+sizes to the uncompressed size.
+.It uncompressed name
+Name the file will be saved as when uncompressing.
+.El
+.Pp
+If the
+.Fl v
+option is specified, the following additional information is printed:
+.Bl -tag -width "compression method"
+.It compression method
+Name of the method used to compress the file.
+.It crc
+32-bit CRC
+.Pq cyclic redundancy code
+of the uncompressed file.
+.It "time stamp"
+Date and time corresponding to the last data modification time
+(mtime) of the compressed file (if the
+.Fl n
+option is specified, the time stamp stored in the compressed file
+is printed instead).
+.El
+.It Fl N
+When uncompressing or listing, use the time stamp and file name stored
+in the compressed file, if any, for the uncompressed version.
+This information is only available when the deflate scheme
+.Pf ( Fl g )
+is used.
+.It Fl n
+When compressing, do not store the original file name and time stamp
+in the header of the compressed file.
+.It Fl O
+Use compress mode
+(the default).
+.It Fl o Ar filename
+Set the output file name.
+.It Fl q
+Be quiet: suppress all messages.
+.It Fl r
+Recursive mode:
+.Nm
+will descend into specified directories.
+.It Fl S Ar suffix
+Set the suffix for compressed files.
+.It Fl t
+Test the integrity of each file leaving any files intact.
+.It Fl v
+Print the percentage reduction of each file and other information.
+.El
+.Pp
+.Nm
+uses a modified Lempel-Ziv algorithm
+.Pq LZW .
+Common substrings in the file are first replaced by 9-bit codes 257 and up.
+When code 512 is reached, the algorithm switches to 10-bit codes and
+continues to use more bits until the
+limit specified by the
+.Fl b
+flag is reached.
+.Ar bits
+must be between 9 and 16
+.Pq the default is 16 .
+.Pp
+After the
+.Ar bits
+limit is reached,
+.Nm
+periodically checks the compression ratio.
+If it is increasing,
+.Nm
+continues to use the existing code dictionary.
+However, if the compression ratio decreases,
+.Nm
+discards the table of substrings and rebuilds it from scratch.
+This allows the algorithm to adapt to the next
+.Dq block
+of the file.
+.Pp
+The
+.Fl b
+flag is omitted for
+.Nm uncompress
+since the
+.Ar bits
+parameter specified during compression
+is encoded within the output, along with
+a magic number to ensure that neither decompression of random data nor
+recompression of compressed data is attempted.
+.Pp
+The amount of compression obtained depends on the size of the
+input, the number of
+.Ar bits
+per code, and the distribution of common substrings.
+Typically, text such as source code or English is reduced by 50 \- 60% using
+.Nm .
+Compression is generally much better than that achieved by Huffman
+coding (as used in the historical command pack), or adaptive Huffman
+coding (as used in the historical command compact), and takes less
+time to compute.
+.Sh EXIT STATUS
+The
+.Nm compress
+utility exits with one of the following values:
+.Pp
+.Bl -tag -width Ds -offset indent -compact
+.It 0
+Success.
+.It 1
+An error occurred.
+.It 2
+At least one of the specified files was not compressed since
+.Fl f
+was not specified and compression would have resulted in a size
+increase.
+.It \*(Gt2
+An error occurred.
+.El
+.Pp
+.Ex -std uncompress zcat
+.Sh SEE ALSO
+.Xr gzexe 1 ,
+.Xr gzip 1 ,
+.Xr zdiff 1 ,
+.Xr zforce 1 ,
+.Xr zmore 1 ,
+.Xr znew 1 ,
+.Xr compress 3
+.Rs
+.%A Welch, Terry A.
+.%D June, 1984
+.%T "A Technique for High Performance Data Compression"
+.%J "IEEE Computer"
+.%V 17:6
+.%P pp. 8\-19
+.Re
+.Sh STANDARDS
+The
+.Nm ,
+.Nm uncompress ,
+and
+.Nm zcat
+utilities are compliant with the
+X/Open System Interfaces option of the
+.St -p1003.1-2008
+specification.
+.Pp
+The
+.Nm
+flags
+.Op Fl 123456789dghlNnOoqrSt ,
+.Nm uncompress
+flags
+.Op Fl hlNnoqrt ,
+and the
+.Nm zcat
+flags
+.Op Fl fghqr
+are extensions to that specification.
+.Pp
+.St -p1003.1-2008
+specifies a maximum bits limit
+.Pq Fl b
+of 14 to
+.Qq achieve portability to all systems .
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
+Deflate compression support was added in
+.Ox 2.1 .
diff --git a/usr.bin/compress/compress.h b/usr.bin/compress/compress.h
new file mode 100644
index 0000000..b197bc8
--- /dev/null
+++ b/usr.bin/compress/compress.h
@@ -0,0 +1,83 @@
+/* $OpenBSD: compress.h,v 1.13 2016/09/03 11:41:10 tedu Exp $ */
+
+/*
+ * Copyright (c) 1997 Michael Shalayeff
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/stat.h>
+
+struct z_info {
+ u_int32_t mtime; /* timestamp */
+ u_int32_t crc; /* crc */
+ u_int32_t hlen; /* header length */
+ u_int64_t total_in; /* # bytes in */
+ u_int64_t total_out; /* # bytes out */
+};
+
+/*
+ * making it any bigger does not affect perfomance very much.
+ * actually this value is just a little bit better than 8192.
+ */
+#define Z_BUFSIZE 16384
+
+enum program_mode {
+ MODE_COMP,
+ MODE_DECOMP,
+ MODE_CAT
+} pmode;
+
+/*
+ * exit codes for compress
+ */
+#define SUCCESS 0
+#define FAILURE 1
+#define WARNING 2
+
+extern char null_magic[];
+
+extern void *z_ropen(int, char *, int);
+extern void *z_wopen(int, char *, int, u_int32_t);
+extern FILE *zopen(const char *, const char *,int);
+extern int zread(void *, char *, int);
+extern int zwrite(void *, const char *, int);
+extern int z_close(void *, struct z_info *, const char *, struct stat *);
+
+
+extern void *gz_ropen(int, char *, int);
+extern void *gz_wopen(int, char *, int, u_int32_t);
+extern int gz_read(void *, char *, int);
+extern int gz_write(void *, const char *, int);
+extern int gz_close(void *, struct z_info *, const char *, struct stat *);
+extern int gz_flush(void *, int);
+
+extern void *null_ropen(int, char *, int);
+extern void *null_wopen(int, char *, int, u_int32_t);
+extern int null_read(void *, char *, int);
+extern int null_write(void *, const char *, int);
+extern int null_close(void *, struct z_info *, const char *, struct stat *);
+extern int null_flush(void *, int);
+
+extern void setfile(const char *, int, struct stat *);
diff --git a/usr.bin/compress/gzexe b/usr.bin/compress/gzexe
new file mode 100644
index 0000000..84ee633
--- /dev/null
+++ b/usr.bin/compress/gzexe
@@ -0,0 +1,209 @@
+#!/bin/sh -
+#
+# $OpenBSD: gzexe,v 1.4 2005/09/30 06:50:44 otto Exp $
+#
+# Copyright (c) 2003 Otto Moerbeek <otto@drijf.net>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+# The number of lines plus one in the on-the-fly decompression script
+lines=19
+
+# A simple string to recognize already compressed files
+magic="# compressed by gzexe"
+
+# Write the decompression script to stdout
+header () {
+ typeset prog tmp
+ # first section needs variable expansion, second not
+ cat <<- EOF
+ #!/bin/sh -
+ $magic
+ lines=$lines
+ EOF
+ cat <<- 'EOF'
+ prog=`/usr/bin/basename "$0"`
+ tmp=`/usr/bin/mktemp -d /tmp/gzexeXXXXXXXXXX` || {
+ /bin/echo "$prog: cannot create tmp dir"; exit 1
+ }
+ trap '/bin/rm -rf "$tmp"' 0
+ if /usr/bin/tail +$lines "$0" |
+ /usr/bin/gzip -dc > "$tmp/$prog" 2> /dev/null; then
+ /bin/chmod u+x "$tmp/$prog"
+ "$tmp/$prog" ${1+"$@"}
+ ret=$?
+ else
+ /bin/echo "$prog: cannot decompress $0"
+ ret=1
+ fi
+ exit $ret
+ EOF
+}
+
+# Test if a file is compressed by checking the magic line
+compressed () {
+ test "X`sed -n 2p "$1" 2> /dev/null`" = "X$magic"
+}
+
+# Decompress a file
+decompress () {
+ tmp=`mktemp /tmp/gzexeXXXXXXXXXX` || {
+ echo "$prog: cannot create tmp file"
+ return 1
+ }
+ if ! cp "$1" "$tmp"; then
+ echo "$prog: cannot copy $1 to $tmp"
+ rm -f "$tmp"
+ return 1
+ fi
+ if ! tail +$lines "$tmp" | gzip -vdc > "$1"; then
+ echo "$prog: cannot decompress $1"
+ cp "$tmp" "$1"
+ rm -f "$tmp"
+ return 1
+ fi
+}
+
+# Perform some sanity checks on the file
+check () {
+ if test ! -e "$1"; then
+ echo "$prog: cannot compress non-existing file $1"
+ return 1
+ fi
+
+ if test ! -f "$1"; then
+ echo "$prog: cannot compress non-regular file $1"
+ return 1
+ fi
+
+ case `basename "$1"` in
+ sh | mktemp | rm | echo | tail | gzip | chmod | basename)
+ echo "$prog: cannot compress $1, I depend on it"
+ return 1
+ esac
+
+ if test ! -x "$1"; then
+ echo "$prog: cannot compress $1, it is not executable"
+ return 1
+ fi
+
+ if test -u "$1" -o -g "$1"; then
+ echo "$prog: cannot compress $1, it has an s bit set"
+ return 1
+ fi
+
+ # Build a list of files we should not compress.
+ # * files we need to decompress
+ CHECK_LIST="
+ /bin/chmod
+ /bin/echo
+ /bin/sh
+ /bin/rm
+ /usr/bin/basename
+ /usr/bin/gzip
+ /usr/bin/mktemp
+ /usr/bin/tail
+ "
+ # * files in /bin and /sbin (decompression fails if /usr/bin is not mounted)
+ # (You could skip these if /usr/bin is always mounted on the same mount point.)
+ CHECK_LIST="$CHECK_LIST
+ /bin/*
+ /sbin/*
+ "
+ # See if the program we are trying to compress is in the list.
+ # To avoid compressing hardlinked files (eg compress & gzip)
+ # we compare the device & inode.
+ PROG_STAT_INFO=`stat -f '%d %i' "$1"`
+ for CHECK in $CHECK_LIST; do
+ if test -f "$CHECK"; then
+ CHECK_STAT_INFO=`stat -f '%d %i' "$CHECK"`
+ if test "X$PROG_STAT_INFO" == "X$CHECK_STAT_INFO"; then
+ echo "$prog: cannot compress $1, it is the same file as $CHECK"
+ return 1
+ fi
+ fi
+ done
+}
+
+# Compress a file
+compress () {
+ tmp=`mktemp /tmp/gzexeXXXXXXXXXX` || {
+ echo "$prog: cannot create tmp file"
+ return 1
+ }
+ if ! cp "$1" "$tmp"; then
+ echo "$prog: cannot copy $1 to $tmp"
+ rm -f "$tmp"
+ return 1
+ fi
+ if ! cp "$1" "$1"~; then
+ echo "$prog: cannot create backup copy $1~"
+ rm -f "$1"~ "$tmp"
+ return 1
+ fi
+
+ # Use cp to overwrite the existing file preserving mode and owner
+ # if possible. If the file is not writable, this will produce an
+ # error.
+
+ if header "$1" > "$tmp" && gzip -vc "$1" >> "$tmp"; then
+ if ! cp "$tmp" "$1"; then
+ echo "$prog: cannot copy $tmp to $1"
+ rm -f "$tmp"
+ return 1
+ fi
+ else
+ echo "$prog: cannot compress $1"
+ rm -f "$1"~ "$tmp"
+ return 1
+ fi
+}
+
+# Is the -d flag specified?
+dflag=
+
+# Return value
+rc=0
+
+if test "X$1" = X-d; then
+ dflag=1
+ shift
+fi
+
+prog=`basename "$0"`
+USAGE="usage: $prog [-d] file ..."
+if test $# -eq 0; then
+ echo $USAGE
+ exit 1
+fi
+
+while test $# -ne 0; do
+ if test $dflag; then
+ if ! compressed "$1"; then
+ echo "$prog: $1 is not compressed"
+ rc=1;
+ elif ! decompress "$1"; then
+ rc=$?
+ fi
+ else
+ if compressed "$1"; then
+ echo "$prog: $1 is already compressed"
+ rc=1;
+ elif ! check "$1" || ! compress "$1"; then
+ rc=$?
+ fi
+ fi
+ shift
+done
+exit $rc
diff --git a/usr.bin/compress/gzexe.1 b/usr.bin/compress/gzexe.1
new file mode 100644
index 0000000..b94c922
--- /dev/null
+++ b/usr.bin/compress/gzexe.1
@@ -0,0 +1,77 @@
+.\" $OpenBSD: gzexe.1,v 1.6 2013/08/12 14:19:53 jmc Exp $
+.\"
+.\" Copyright (c) 2003 Otto Moerbeek <otto@drijf.net>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: August 12 2013 $
+.Dt GZEXE 1
+.Os
+.Sh NAME
+.Nm gzexe
+.Nd create auto-decompressing executables
+.Sh SYNOPSIS
+.Nm gzexe
+.Op Fl d
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility uses
+.Xr gzip 1
+to compress executables, producing executables that decompress on-the-fly
+when executed.
+This saves disk space, at the cost of slower execution times.
+The original executables are saved by copying each of them to a file with
+the same name with a
+.Sq ~
+suffix appended.
+After verifying that the compressed executables work as expected, the backup
+files can be removed.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Decompress executables previously compressed by
+.Nm .
+.El
+.Pp
+The
+.Nm
+program refuses to compress non-regular or non-executable files,
+files with a setuid or setgid bit set, files that are already
+compressed using
+.Nm ,
+files in
+.Pa /bin
+or
+.Pa /sbin ,
+or programs it needs to perform on-the-fly decompression:
+.Xr sh 1 ,
+.Xr basename 1 ,
+.Xr mktemp 1 ,
+.Xr rm 1 ,
+.Xr echo 1 ,
+.Xr tail 1 ,
+.Xr gzip 1 ,
+and
+.Xr chmod 1 .
+.Sh SEE ALSO
+.Xr compress 1 ,
+.Xr gzip 1
+.Sh CAVEATS
+The
+.Nm
+utility replaces files by overwriting them with the generated
+compressed executable.
+To be able to do this, it is required that the original files are writable.
diff --git a/usr.bin/compress/gzip.1 b/usr.bin/compress/gzip.1
new file mode 100644
index 0000000..7f48fe9
--- /dev/null
+++ b/usr.bin/compress/gzip.1
@@ -0,0 +1,381 @@
+.\" $OpenBSD: gzip.1,v 1.14 2014/10/07 21:06:30 deraadt Exp $
+.\"
+.\" Copyright (c) 1986, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" James A. Woods, derived from original work by Spencer Thomas
+.\" and Joseph Orost.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)compress.1 8.2 (Berkeley) 4/18/94
+.\"
+.Dd $Mdocdate: October 7 2014 $
+.Dt GZIP 1
+.Os
+.Sh NAME
+.Nm gzip ,
+.Nm gunzip ,
+.Nm gzcat
+.Nd compress and expand data (deflate mode)
+.Sh SYNOPSIS
+.Nm gzip
+.Op Fl 123456789cdfhLlNnOqrtVv
+.Op Fl b Ar bits
+.Op Fl o Ar filename
+.Op Fl S Ar suffix
+.Op Ar
+.Nm gunzip
+.Op Fl cfhLlNnqrtVv
+.Op Fl o Ar filename
+.Op Ar
+.Nm gzcat
+.Op Fl fhqr
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility
+reduces the size of the named files using adaptive Lempel-Ziv coding,
+in deflate mode.
+If invoked as
+.Nm gzip -O ,
+the compress mode of compression is chosen;
+see
+.Xr compress 1
+for more information.
+Each file is renamed to the same name plus the extension
+.Dq .gz .
+As many of the modification time, access time, file flags, file mode,
+user ID, and group ID as allowed by permissions are retained in the
+new file.
+If compression would not reduce the size of a file,
+the file is ignored (unless
+.Fl f
+is used).
+.Pp
+The
+.Nm gunzip
+utility restores compressed files to their original form, renaming the
+files by removing the extension (or by using the stored name if the
+.Fl N
+flag is specified).
+It has the ability to restore files compressed by both
+.Nm
+and
+.Xr compress 1 ,
+recognising the following extensions:
+.Dq .Z ,
+.Dq -Z ,
+.Dq _Z ,
+.Dq .gz ,
+.Dq -gz ,
+.Dq _gz ,
+.Dq .tgz ,
+.Dq -tgz ,
+.Dq _tgz ,
+.Dq .taz ,
+.Dq -taz ,
+and
+.Dq _taz .
+Extensions ending in
+.Dq tgz
+and
+.Dq taz
+are not removed when decompressing, instead they are converted to
+.Dq tar .
+.Pp
+The
+.Nm gzcat
+command is equivalent in functionality to
+.Nm gunzip
+.Fl c .
+.Pp
+If renaming the files would cause files to be overwritten and the standard
+input device is a terminal, the user is prompted (on the standard error
+output) for confirmation.
+If prompting is not possible or confirmation is not received, the files
+are not overwritten.
+.Pp
+If no files are specified, the standard input is compressed or uncompressed
+to the standard output.
+If either the input or output files are not regular files, the checks for
+reduction in size and file overwriting are not performed, the input file is
+not removed, and the attributes of the input file are not retained.
+.Pp
+By default, when compressing, the original file name and time stamp
+are stored in the compressed file.
+When uncompressing, this information is not used.
+Instead, the uncompressed file inherits the time stamp of the
+compressed version and the uncompressed file name is generated from
+the name of the compressed file as described above.
+These defaults may be overridden by the
+.Fl N
+and
+.Fl n
+flags, described below.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl 1...9
+Use the deflate scheme, with compression factor of
+.Fl 1
+to
+.Fl 9 .
+Compression factor
+.Fl 1
+is the fastest, but provides a poorer level of compression.
+Compression factor
+.Fl 9
+provides the best level of compression, but is relatively slow.
+The default is
+.Fl 6 .
+.It Fl b Ar bits
+Specify the
+.Ar bits
+code limit
+.Pq see below .
+.It Fl c
+Compressed or uncompressed output is written to the standard output.
+No files are modified (force
+.Nm gzcat
+mode).
+.It Fl d
+Decompress the source files instead of compressing them (force
+.Nm gunzip
+mode).
+.It Fl f
+Force compression of
+.Ar file ,
+even if it is not actually reduced in size.
+Additionally, files are overwritten without prompting for confirmation.
+If the input data is not in a format recognized by
+.Nm
+and if the option
+.Fl c
+is also given, copy the input data without change
+to the standard output: let
+.Nm gzcat
+behave as
+.Xr cat 1 .
+.It Fl h
+Print a short help message.
+.It Fl L
+A no-op which exists for compatibility only.
+On GNU gzip, it displays the program's license.
+.It Fl l
+List information for the specified compressed files.
+The following information is listed:
+.Bl -tag -width "compression ratio"
+.It compressed size
+Size of the compressed file.
+.It uncompressed size
+Size of the file when uncompressed.
+.It compression ratio
+Ratio of the difference between the compressed and uncompressed
+sizes to the uncompressed size.
+.It uncompressed name
+Name the file will be saved as when uncompressing.
+.El
+.Pp
+If the
+.Fl v
+option is specified, the following additional information is printed:
+.Bl -tag -width "compression method"
+.It compression method
+Name of the method used to compress the file.
+.It crc
+32-bit CRC
+.Pq cyclic redundancy code
+of the uncompressed file.
+.It "time stamp"
+Date and time corresponding to the last data modification time
+(mtime) of the compressed file (if the
+.Fl n
+option is specified, the time stamp stored in the compressed file
+is printed instead).
+.El
+.It Fl N
+When uncompressing or listing, use the time stamp and file name stored
+in the compressed file, if any, for the uncompressed version.
+.It Fl n
+When compressing, do not store the original file name and time stamp
+in the
+.Nm
+header.
+.It Fl O
+Use old compression method
+(force
+.Xr compress 1
+mode).
+.It Fl o Ar filename
+Set the output file name.
+.It Fl q
+Be quiet: suppress all messages.
+.It Fl r
+Recursive mode:
+.Nm
+will descend into specified directories.
+.It Fl S Ar suffix
+Set the suffix for compressed files.
+.It Fl t
+Test the integrity of each file leaving any files intact.
+.It Fl V
+A no-op which exists for compatibility only.
+On GNU gzip, it displays version information.
+.It Fl v
+Print the percentage reduction of each file and other information.
+.El
+.Pp
+.Nm
+uses a modified Lempel-Ziv algorithm
+.Pq LZW .
+Common substrings are replaced by pointers to previous strings,
+and are found using a hash table.
+Unique substrings are emitted as a string of literal bytes,
+and compressed as Huffman trees.
+When code 512 is reached, the algorithm switches to 10-bit codes and
+continues to use more bits until the
+limit specified by the
+.Fl b
+flag is reached.
+.Ar bits
+must be between 9 and 16
+.Pq the default is 16 .
+.Pp
+After the
+.Ar bits
+limit is reached,
+.Nm
+periodically checks the compression ratio.
+If it is increasing,
+.Nm
+continues to use the existing code dictionary.
+However, if the compression ratio decreases,
+.Nm
+discards the table of substrings and rebuilds it from scratch.
+This allows the algorithm to adapt to the next
+.Dq block
+of the file.
+.Pp
+The
+.Fl b
+flag is omitted for
+.Nm gunzip
+since the
+.Ar bits
+parameter specified during compression
+is encoded within the output, along with
+a magic number to ensure that neither decompression of random data nor
+recompression of compressed data is attempted.
+.Pp
+The amount of compression obtained depends on the size of the
+input, the number of
+.Ar bits
+per code, and the distribution of common substrings.
+Typically, text such as source code or English is reduced by 60 \- 70% using
+.Nm .
+Compression is generally much better than that achieved by Huffman
+coding (as used in the historical command pack), or adaptive Huffman
+coding (as used in the historical command compact), and takes less
+time to compute.
+.Sh ENVIRONMENT
+.Bl -tag -width Ds
+.It Ev GZIP
+Options which are passed to
+.Nm ,
+.Nm gunzip ,
+and
+.Nm gzcat
+automatically.
+.El
+.Sh EXIT STATUS
+The
+.Nm gzip
+utility exits with one of the following values:
+.Pp
+.Bl -tag -width Ds -offset indent -compact
+.It 0
+Success.
+.It 1
+An error occurred.
+.It 2
+At least one of the specified files was not compressed since
+.Fl f
+was not specified and compression would have resulted in a size
+increase.
+.It \*(Gt2
+An error occurred.
+.El
+.Pp
+The
+.Nm gunzip
+and
+.Nm gzcat
+utilities exit 0 on success,
+and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr compress 1 ,
+.Xr gzexe 1 ,
+.Xr zdiff 1 ,
+.Xr zforce 1 ,
+.Xr zmore 1 ,
+.Xr znew 1 ,
+.Xr compress 3
+.Sh STANDARDS
+.Rs
+.%A P. Deutsch
+.%A J-L. Gailly
+.%D May 1996
+.%R RFC 1950
+.%T ZLIB Compressed Data Format Specification version 3.3
+.Re
+.Pp
+.Rs
+.%A P. Deutsch
+.%D May 1996
+.%R RFC 1951
+.%T DEFLATE Compressed Data Format Specification version 1.3
+.Re
+.Pp
+.Rs
+.%A P. Deutsch
+.%D May 1996
+.%R RFC 1952
+.%T GZIP file format specification version 4.3
+.Re
+.Sh HISTORY
+.Nm gzip
+compatibility was added to
+.Xr compress 1
+in
+.Ox 3.4 .
+The
+.Sq g
+in this version of
+.Nm gzip
+stands for
+.Dq gratis .
diff --git a/usr.bin/compress/gzopen.c b/usr.bin/compress/gzopen.c
new file mode 100644
index 0000000..7fd848c
--- /dev/null
+++ b/usr.bin/compress/gzopen.c
@@ -0,0 +1,539 @@
+/* $OpenBSD: gzopen.c,v 1.34 2016/09/03 12:29:30 tedu Exp $ */
+
+/*
+ * Copyright (c) 1997 Michael Shalayeff
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+/* this is partially derived from the zlib's gzio.c file, so the notice: */
+/*
+ zlib.h -- interface of the 'zlib' general purpose compression library
+ version 1.0.4, Jul 24th, 1996.
+
+ Copyright (C) 1995-1996 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ gzip@prep.ai.mit.edu madler@alumni.caltech.edu
+
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt
+ (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+*/
+
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <zlib.h>
+#include "compress.h"
+
+/* gzip flag byte */
+#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
+#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
+#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
+#define COMMENT 0x10 /* bit 4 set: file comment present */
+#define RESERVED 0xE0 /* bits 5..7: reserved */
+
+#define DEF_MEM_LEVEL 8
+#define OS_CODE 0x03 /* unix */
+
+typedef
+struct gz_stream {
+ int z_fd; /* .gz file */
+ int z_eof; /* set if end of input file */
+ z_stream z_stream; /* libz stream */
+ u_char z_buf[Z_BUFSIZE]; /* i/o buffer */
+ char z_mode; /* 'w' or 'r' */
+ u_int32_t z_time; /* timestamp (mtime) */
+ u_int32_t z_crc; /* crc32 of uncompressed data */
+ u_int32_t z_hlen; /* length of the gz header */
+ u_int64_t z_total_in; /* # bytes in */
+ u_int64_t z_total_out; /* # bytes out */
+} gz_stream;
+
+static const u_char gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
+
+static u_int32_t get_int32(gz_stream *);
+static int get_header(gz_stream *, char *, int);
+static int get_byte(gz_stream *);
+
+void *
+gz_ropen(int fd, char *name, int gotmagic)
+{
+ gz_stream *s;
+
+ if (fd < 0)
+ return NULL;
+
+ if ((s = calloc(1, sizeof(gz_stream))) == NULL)
+ return NULL;
+
+ s->z_stream.zalloc = (alloc_func)0;
+ s->z_stream.zfree = (free_func)0;
+ s->z_stream.opaque = (voidpf)0;
+ s->z_stream.next_in = Z_NULL;
+ s->z_stream.next_out = Z_NULL;
+ s->z_stream.avail_in = s->z_stream.avail_out = 0;
+ s->z_fd = 0;
+ s->z_eof = 0;
+ s->z_time = 0;
+ s->z_hlen = 0;
+ s->z_total_in = 0;
+ s->z_total_out = 0;
+ s->z_crc = crc32(0L, Z_NULL, 0);
+ s->z_mode = 'r';
+
+ if (inflateInit2(&(s->z_stream), -MAX_WBITS) != Z_OK) {
+ free (s);
+ return NULL;
+ }
+ s->z_stream.next_in = s->z_buf;
+ s->z_stream.avail_out = Z_BUFSIZE;
+
+ errno = 0;
+ s->z_fd = fd;
+
+ /* read the .gz header */
+ if (get_header(s, name, gotmagic) != 0) {
+ gz_close(s, NULL, NULL, NULL);
+ s = NULL;
+ }
+
+ return s;
+}
+
+static int
+get_byte(gz_stream *s)
+{
+ if (s->z_eof)
+ return EOF;
+
+ if (s->z_stream.avail_in == 0) {
+ errno = 0;
+ s->z_stream.avail_in = read(s->z_fd, s->z_buf, Z_BUFSIZE);
+ if ((int)s->z_stream.avail_in <= 0) {
+ s->z_eof = 1;
+ return EOF;
+ }
+ s->z_stream.next_in = s->z_buf;
+ }
+ s->z_stream.avail_in--;
+ return *s->z_stream.next_in++;
+}
+
+static u_int32_t
+get_int32(gz_stream *s)
+{
+ u_int32_t x;
+
+ x = ((u_int32_t)(get_byte(s) & 0xff));
+ x |= ((u_int32_t)(get_byte(s) & 0xff))<<8;
+ x |= ((u_int32_t)(get_byte(s) & 0xff))<<16;
+ x |= ((u_int32_t)(get_byte(s) & 0xff))<<24;
+ return x;
+}
+
+static int
+get_header(gz_stream *s, char *name, int gotmagic)
+{
+ int method; /* method byte */
+ int flags; /* flags byte */
+ char *ep;
+ uInt len;
+ int c;
+
+ /* Check the gzip magic header */
+ if (!gotmagic) {
+ for (len = 0; len < 2; len++) {
+ c = get_byte(s);
+ if (c != gz_magic[len]) {
+ errno = EFTYPE;
+ return -1;
+ }
+ }
+ }
+
+ method = get_byte(s);
+ flags = get_byte(s);
+ if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
+ errno = EFTYPE;
+ return -1;
+ }
+
+ /* Stash timestamp (mtime) */
+ s->z_time = get_int32(s);
+
+ /* Discard xflags and OS code */
+ (void)get_byte(s);
+ (void)get_byte(s);
+
+ s->z_hlen += 10; /* magic, method, flags, time, xflags, OS code */
+ if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
+ len = (uInt)get_byte(s);
+ len += ((uInt)get_byte(s))<<8;
+ s->z_hlen += 2;
+ /* len is garbage if EOF but the loop below will quit anyway */
+ while (len-- != 0 && get_byte(s) != EOF)
+ s->z_hlen++;
+ }
+
+ if ((flags & ORIG_NAME) != 0) { /* read/save the original file name */
+ if ((ep = name) != NULL)
+ ep += PATH_MAX - 1;
+ while ((c = get_byte(s)) != EOF) {
+ s->z_hlen++;
+ if (c == '\0')
+ break;
+ if (name < ep)
+ *name++ = c;
+ }
+ if (name != NULL)
+ *name = '\0';
+ }
+
+ if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
+ while ((c = get_byte(s)) != EOF) {
+ s->z_hlen++;
+ if (c == '\0')
+ break;
+ }
+ }
+
+ if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
+ (void)get_byte(s);
+ (void)get_byte(s);
+ s->z_hlen += 2;
+ }
+
+ if (s->z_eof) {
+ errno = EFTYPE;
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+gz_read(void *cookie, char *buf, int len)
+{
+ gz_stream *s = (gz_stream*)cookie;
+ u_char *start = buf; /* starting point for crc computation */
+ int error = Z_OK;
+
+ s->z_stream.next_out = buf;
+ s->z_stream.avail_out = len;
+
+ while (error == Z_OK && !s->z_eof && s->z_stream.avail_out != 0) {
+
+ if (s->z_stream.avail_in == 0) {
+
+ errno = 0;
+ s->z_stream.avail_in = read(s->z_fd, s->z_buf,
+ Z_BUFSIZE);
+ if ((int)s->z_stream.avail_in <= 0)
+ s->z_eof = 1;
+ s->z_stream.next_in = s->z_buf;
+ }
+
+ error = inflate(&(s->z_stream), Z_NO_FLUSH);
+
+ if (error == Z_DATA_ERROR) {
+ errno = EINVAL;
+ goto bad;
+ }
+ if (error == Z_BUF_ERROR) {
+ errno = EIO;
+ goto bad;
+ }
+ if (error == Z_STREAM_END) {
+ /* Check CRC and original size */
+ s->z_crc = crc32(s->z_crc, start,
+ (uInt)(s->z_stream.next_out - start));
+ start = s->z_stream.next_out;
+
+ if (get_int32(s) != s->z_crc) {
+ errno = EINVAL;
+ goto bad;
+ }
+ if (get_int32(s) != (u_int32_t)s->z_stream.total_out) {
+ errno = EIO;
+ return -1;
+ }
+ s->z_hlen += 2 * sizeof(int32_t);
+
+ /* Add byte counts from the finished stream. */
+ s->z_total_in += s->z_stream.total_in;
+ s->z_total_out += s->z_stream.total_out;
+
+ /* Check for the existence of an appended file. */
+ if (get_header(s, NULL, 0) != 0) {
+ s->z_eof = 1;
+ break;
+ }
+ inflateReset(&(s->z_stream));
+ s->z_crc = crc32(0L, Z_NULL, 0);
+ error = Z_OK;
+ }
+ }
+ s->z_crc = crc32(s->z_crc, start,
+ (uInt)(s->z_stream.next_out - start));
+ len -= s->z_stream.avail_out;
+
+ return (len);
+bad:
+ /* Add byte counts from the finished stream. */
+ s->z_total_in += s->z_stream.total_in;
+ s->z_total_out += s->z_stream.total_out;
+ return (-1);
+}
+
+#ifndef SMALL
+static int
+put_int32(gz_stream *s, u_int32_t x)
+{
+ u_int32_t y = htole32(x);
+
+ if (write(s->z_fd, &y, sizeof(y)) != sizeof(y))
+ return Z_ERRNO;
+ return 0;
+}
+
+static int
+put_header(gz_stream *s, char *name, u_int32_t mtime, int bits)
+{
+ struct iovec iov[2];
+ u_char buf[10];
+
+ buf[0] = gz_magic[0];
+ buf[1] = gz_magic[1];
+ buf[2] = Z_DEFLATED;
+ buf[3] = name ? ORIG_NAME : 0;
+ buf[4] = mtime & 0xff;
+ buf[5] = (mtime >> 8) & 0xff;
+ buf[6] = (mtime >> 16) & 0xff;
+ buf[7] = (mtime >> 24) & 0xff;
+ buf[8] = bits == 1 ? 4 : bits == 9 ? 2 : 0; /* xflags */
+ buf[9] = OS_CODE;
+ iov[0].iov_base = buf;
+ iov[0].iov_len = sizeof(buf);
+ s->z_hlen = sizeof(buf);
+
+ if (name != NULL) {
+ iov[1].iov_base = name;
+ iov[1].iov_len = strlen(name) + 1;
+ s->z_hlen += iov[1].iov_len;
+ }
+ if (writev(s->z_fd, iov, name ? 2 : 1) == -1)
+ return (-1);
+ return (0);
+}
+
+void *
+gz_wopen(int fd, char *name, int bits, u_int32_t mtime)
+{
+ gz_stream *s;
+
+ if (fd < 0)
+ return NULL;
+
+ if (bits < 0 || bits > Z_BEST_COMPRESSION) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((s = calloc(1, sizeof(gz_stream))) == NULL)
+ return NULL;
+
+ s->z_stream.zalloc = (alloc_func)0;
+ s->z_stream.zfree = (free_func)0;
+ s->z_stream.opaque = (voidpf)0;
+ s->z_stream.next_in = Z_NULL;
+ s->z_stream.next_out = Z_NULL;
+ s->z_stream.avail_in = s->z_stream.avail_out = 0;
+ s->z_fd = 0;
+ s->z_eof = 0;
+ s->z_time = 0;
+ s->z_hlen = 0;
+ s->z_total_in = 0;
+ s->z_total_out = 0;
+ s->z_crc = crc32(0L, Z_NULL, 0);
+ s->z_mode = 'w';
+
+ /* windowBits is passed < 0 to suppress zlib header */
+ if (deflateInit2(&(s->z_stream), bits, Z_DEFLATED,
+ -MAX_WBITS, DEF_MEM_LEVEL, 0) != Z_OK) {
+ free (s);
+ return NULL;
+ }
+ s->z_stream.next_out = s->z_buf;
+ s->z_stream.avail_out = Z_BUFSIZE;
+
+ errno = 0;
+ s->z_fd = fd;
+
+ /* write the .gz header */
+ if (put_header(s, name, mtime, bits) != 0) {
+ gz_close(s, NULL, NULL, NULL);
+ s = NULL;
+ }
+
+ return s;
+}
+int
+gz_write(void *cookie, const char *buf, int len)
+{
+ gz_stream *s = (gz_stream*)cookie;
+
+ s->z_stream.next_in = (char *)buf;
+ s->z_stream.avail_in = len;
+
+ while (s->z_stream.avail_in != 0) {
+ if (s->z_stream.avail_out == 0) {
+ if (write(s->z_fd, s->z_buf, Z_BUFSIZE) != Z_BUFSIZE)
+ break;
+ s->z_stream.next_out = s->z_buf;
+ s->z_stream.avail_out = Z_BUFSIZE;
+ }
+ if (deflate(&(s->z_stream), Z_NO_FLUSH) != Z_OK)
+ break;
+ }
+ s->z_crc = crc32(s->z_crc, buf, len);
+
+ return (int)(len - s->z_stream.avail_in);
+}
+
+int
+gz_flush(void *cookie, int flush)
+{
+ gz_stream *s = (gz_stream*)cookie;
+ size_t len;
+ int done = 0;
+ int err;
+
+ if (s == NULL || s->z_mode != 'w') {
+ errno = EBADF;
+ return Z_ERRNO;
+ }
+
+ s->z_stream.avail_in = 0; /* should be zero already anyway */
+
+ for (;;) {
+ len = Z_BUFSIZE - s->z_stream.avail_out;
+
+ if (len != 0) {
+ if (write(s->z_fd, s->z_buf, len) != len)
+ return Z_ERRNO;
+ s->z_stream.next_out = s->z_buf;
+ s->z_stream.avail_out = Z_BUFSIZE;
+ }
+ if (done)
+ break;
+ if ((err = deflate(&(s->z_stream), flush)) != Z_OK &&
+ err != Z_STREAM_END)
+ return err;
+
+ /* deflate has finished flushing only when it hasn't
+ * used up all the available space in the output buffer
+ */
+ done = (s->z_stream.avail_out != 0 || err == Z_STREAM_END);
+ }
+ return 0;
+}
+#endif
+
+int
+gz_close(void *cookie, struct z_info *info, const char *name, struct stat *sb)
+{
+ gz_stream *s = (gz_stream*)cookie;
+ int err = 0;
+
+ if (s == NULL)
+ return -1;
+
+#ifndef SMALL
+ if (s->z_mode == 'w' && (err = gz_flush (s, Z_FINISH)) == Z_OK) {
+ if ((err = put_int32 (s, s->z_crc)) == Z_OK) {
+ s->z_hlen += sizeof(int32_t);
+ if ((err = put_int32 (s, s->z_stream.total_in)) == Z_OK)
+ s->z_hlen += sizeof(int32_t);
+ }
+ }
+#endif
+ if (!err && s->z_stream.state != NULL) {
+ if (s->z_mode == 'w')
+#ifndef SMALL
+ err = deflateEnd(&s->z_stream);
+#else
+ err = -1;
+#endif
+ else if (s->z_mode == 'r')
+ err = inflateEnd(&s->z_stream);
+ }
+
+ if (info != NULL) {
+ info->mtime = s->z_time;
+ info->crc = s->z_crc;
+ info->hlen = s->z_hlen;
+ if (s->z_mode == 'r') {
+ info->total_in = s->z_total_in;
+ info->total_out = s->z_total_out;
+ } else {
+ info->total_in = s->z_stream.total_in;
+ info->total_out = s->z_stream.total_out;
+ }
+
+ }
+
+ setfile(name, s->z_fd, sb);
+ if (!err)
+ err = close(s->z_fd);
+ else
+ (void)close(s->z_fd);
+
+ free(s);
+
+ return err;
+}
+
diff --git a/usr.bin/compress/main.c b/usr.bin/compress/main.c
new file mode 100644
index 0000000..9152b31
--- /dev/null
+++ b/usr.bin/compress/main.c
@@ -0,0 +1,961 @@
+/* $OpenBSD: main.c,v 1.94 2016/09/03 13:26:50 tedu Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1997-2002 Michael Shalayeff
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <getopt.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <paths.h>
+#include "compress.h"
+
+#define min(a,b) ((a) < (b)? (a) : (b))
+
+int cat, decomp, pipin, force, verbose, testmode, list, recurse, storename;
+extern char *__progname;
+
+const struct compressor {
+ const char *name;
+ const char *suffix;
+ const u_char *magic;
+ const char *comp_opts;
+ const char *decomp_opts;
+ const char *cat_opts;
+ void *(*ropen)(int, char *, int);
+ int (*read)(void *, char *, int);
+#ifndef SMALL
+ void *(*wopen)(int, char *, int, u_int32_t);
+ int (*write)(void *, const char *, int);
+#endif
+ int (*close)(void *, struct z_info *, const char *, struct stat *);
+} c_table[] = {
+#define M_DEFLATE (&c_table[0])
+ {
+ "deflate",
+ ".gz",
+ "\037\213",
+ "123456789ab:cdfhLlNnOo:qrS:tVv",
+ "cfhLlNno:qrtVv",
+ "fhqr",
+ gz_ropen,
+ gz_read,
+#ifndef SMALL
+ gz_wopen,
+ gz_write,
+#endif
+ gz_close
+ },
+#define M_COMPRESS (&c_table[1])
+#ifndef SMALL
+ {
+ "compress",
+ ".Z",
+ "\037\235",
+ "123456789ab:cdfghlNnOo:qrS:tv",
+ "cfhlNno:qrtv",
+ "fghqr",
+ z_ropen,
+ zread,
+ z_wopen,
+ zwrite,
+ z_close
+ },
+#endif /* SMALL */
+ { NULL }
+};
+
+#ifndef SMALL
+const struct compressor null_method = {
+ "null",
+ ".nul",
+ "XX",
+ "123456789ab:cdfghlNnOo:qrS:tv",
+ "cfhlNno:qrtv",
+ "fghqr",
+ null_ropen,
+ null_read,
+ null_wopen,
+ null_write,
+ null_close
+};
+#endif /* SMALL */
+
+int permission(const char *);
+__dead void usage(int);
+int docompress(const char *, char *, const struct compressor *,
+ int, struct stat *);
+int dodecompress(const char *, char *, struct stat *);
+const struct compressor *check_method(int);
+const char *check_suffix(const char *);
+char *set_outfile(const char *, char *, size_t);
+void list_stats(const char *, const struct compressor *, struct z_info *);
+void verbose_info(const char *, off_t, off_t, u_int32_t);
+
+const struct option longopts[] = {
+#ifndef SMALL
+ { "ascii", no_argument, 0, 'a' },
+ { "stdout", no_argument, 0, 'c' },
+ { "to-stdout", no_argument, 0, 'c' },
+ { "decompress", no_argument, 0, 'd' },
+ { "uncompress", no_argument, 0, 'd' },
+ { "force", no_argument, 0, 'f' },
+ { "help", no_argument, 0, 'h' },
+ { "list", no_argument, 0, 'l' },
+ { "license", no_argument, 0, 'L' },
+ { "no-name", no_argument, 0, 'n' },
+ { "name", no_argument, 0, 'N' },
+ { "quiet", no_argument, 0, 'q' },
+ { "recursive", no_argument, 0, 'r' },
+ { "suffix", required_argument, 0, 'S' },
+ { "test", no_argument, 0, 't' },
+ { "verbose", no_argument, 0, 'v' },
+ { "version", no_argument, 0, 'V' },
+ { "fast", no_argument, 0, '1' },
+ { "best", no_argument, 0, '9' },
+#endif /* SMALL */
+ { NULL }
+};
+
+int
+main(int argc, char *argv[])
+{
+ FTS *ftsp;
+ FTSENT *entry;
+ const struct compressor *method;
+ const char *optstr, *s;
+ char *p, *infile;
+ char outfile[PATH_MAX], _infile[PATH_MAX], suffix[16];
+ int bits, ch, error, rc, cflag, oflag;
+
+ if (pledge("stdio rpath wpath cpath fattr chown", NULL) == -1)
+ err(1, "pledge");
+
+ bits = cflag = oflag = 0;
+ storename = -1;
+ p = __progname;
+ if (p[0] == 'g') {
+ method = M_DEFLATE;
+ bits = 6;
+ p++;
+ } else {
+#ifdef SMALL
+ method = M_DEFLATE;
+#else
+ method = M_COMPRESS;
+#endif /* SMALL */
+ }
+ optstr = method->comp_opts;
+
+ decomp = 0;
+ pmode = MODE_COMP;
+ if (!strcmp(p, "zcat")) {
+ decomp++;
+ cflag = 1;
+ pmode = MODE_CAT;
+ } else {
+ if (p[0] == 'u' && p[1] == 'n') {
+ p += 2;
+ decomp++;
+ pmode = MODE_DECOMP;
+ }
+
+ if (strcmp(p, "zip") &&
+ strcmp(p, "compress"))
+ errx(1, "unknown program name");
+ }
+
+ strlcpy(suffix, method->suffix, sizeof(suffix));
+
+ if (method == M_DEFLATE && (p = getenv("GZIP")) != NULL) {
+ char *evbuf, *last, **nargv = NULL;
+ int argc_extra = 0, nargc = 0;
+
+ if ((evbuf = strdup(p)) == NULL)
+ err(1, NULL);
+ for ((p = strtok_r(evbuf, " ", &last)); p != NULL;
+ (p = strtok_r(NULL, " ", &last))) {
+ if (nargc + 1 >= argc_extra) {
+ argc_extra += 1024;
+ nargv = reallocarray(nargv,
+ argc + argc_extra + 1, sizeof(char *));
+ if (nargv == NULL)
+ err(1, NULL);
+ }
+ nargv[++nargc] = p;
+ }
+ if (nargv != NULL) {
+ nargv[0] = *argv++;
+ while ((nargv[++nargc] = *argv++))
+ ;
+ argv = nargv;
+ argc = nargc;
+ }
+ }
+
+ optstr += pmode;
+ while ((ch = getopt_long(argc, argv, optstr, longopts, NULL)) != -1)
+ switch (ch) {
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ method = M_DEFLATE;
+ strlcpy(suffix, method->suffix, sizeof(suffix));
+ bits = ch - '0';
+ break;
+ case 'a':
+ warnx("option -a is ignored on this system");
+ break;
+ case 'b':
+ bits = strtol(optarg, &p, 10);
+ /*
+ * POSIX 1002.3 says 9 <= bits <= 14 for portable
+ * apps, but says the implementation may allow
+ * greater.
+ */
+ if (*p)
+ errx(1, "illegal bit count -- %s", optarg);
+ break;
+ case 'c':
+ cflag = 1;
+ break;
+ case 'd': /* Backward compatible. */
+ decomp++;
+ break;
+ case 'f':
+ force++;
+ break;
+ case 'g':
+ method = M_DEFLATE;
+ strlcpy(suffix, method->suffix, sizeof(suffix));
+ bits = 6;
+ break;
+ case 'l':
+ list++;
+ testmode = 1;
+ decomp++;
+ break;
+ case 'n':
+ storename = 0;
+ break;
+ case 'N':
+ storename = 1;
+ break;
+#ifndef SMALL
+ case 'O':
+ method = M_COMPRESS;
+ strlcpy(suffix, method->suffix, sizeof(suffix));
+ break;
+#endif /* SMALL */
+ case 'o':
+ if (strlcpy(outfile, optarg,
+ sizeof(outfile)) >= sizeof(outfile))
+ errx(1, "-o argument is too long");
+ oflag = 1;
+ break;
+ case 'q':
+ verbose = -1;
+ break;
+ case 'S':
+ p = suffix;
+ if (optarg[0] != '.')
+ *p++ = '.';
+ strlcpy(p, optarg, sizeof(suffix) - (p - suffix));
+ p = optarg;
+ break;
+ case 't':
+ testmode = 1;
+ decomp++;
+ break;
+ case 'V':
+ exit (0);
+ case 'v':
+ verbose++;
+ break;
+ case 'L':
+ exit (0);
+ case 'r':
+ recurse++;
+ break;
+
+ case 'h':
+ usage(0);
+ break;
+ default:
+ usage(1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (cflag || testmode || (!oflag && argc == 0))
+ if (pledge("stdio rpath", NULL) == -1)
+ err(1, "pledge");
+
+ if (argc == 0) {
+ argv = calloc(2, sizeof(char *));
+ if (argv == NULL)
+ err(1, NULL);
+ argv[0] = "-";
+ argc = 1;
+ }
+ if (oflag && (recurse || argc > 1))
+ errx(1, "-o option may only be used with a single input file");
+
+ if ((cat && argc) + testmode + oflag > 1)
+ errx(1, "may not mix -o, -c, or -t options");
+ /*
+ * By default, when compressing store the original name and timestamp
+ * in the header. Do not restore these when decompressing unless
+ * the -N option is given.
+ */
+ if (storename == -1)
+ storename = !decomp;
+
+ if ((ftsp = fts_open(argv, FTS_PHYSICAL|FTS_NOCHDIR, 0)) == NULL)
+ err(1, NULL);
+ for (rc = SUCCESS; (entry = fts_read(ftsp)) != NULL;) {
+ cat = cflag;
+ pipin = 0;
+ infile = entry->fts_path;
+ if (infile[0] == '-' && infile[1] == '\0') {
+ infile = "stdin";
+ pipin++;
+ if (!oflag)
+ cat = 1;
+ }
+ else
+ switch (entry->fts_info) {
+ case FTS_D:
+ if (!recurse) {
+ warnx("%s is a directory: ignored",
+ infile);
+ fts_set(ftsp, entry, FTS_SKIP);
+ }
+ continue;
+ case FTS_DP:
+ continue;
+ case FTS_NS:
+ /*
+ * If file does not exist and has no suffix,
+ * tack on the default suffix and try that.
+ */
+ if (entry->fts_errno == ENOENT) {
+ p = strrchr(entry->fts_accpath, '.');
+ if ((p == NULL ||
+ strcmp(p, suffix) != 0) &&
+ snprintf(_infile, sizeof(_infile),
+ "%s%s", infile, suffix) <
+ sizeof(_infile) &&
+ stat(_infile, entry->fts_statp) ==
+ 0 &&
+ S_ISREG(entry->fts_statp->st_mode)) {
+ infile = _infile;
+ break;
+ }
+ }
+ case FTS_ERR:
+ case FTS_DNR:
+ warnx("%s: %s", infile,
+ strerror(entry->fts_errno));
+ rc = rc ? rc : WARNING;
+ continue;
+ default:
+ if (!S_ISREG(entry->fts_statp->st_mode) &&
+ !(S_ISLNK(entry->fts_statp->st_mode) &&
+ cat)) {
+ warnx("%s not a regular file%s",
+ infile, cat ? "" : ": unchanged");
+ rc = rc ? rc : WARNING;
+ continue;
+ }
+ break;
+ }
+
+ if (!decomp && !pipin && (s = check_suffix(infile)) != NULL) {
+ warnx("%s already has %s suffix -- unchanged",
+ infile, s);
+ rc = rc ? rc : WARNING;
+ continue;
+ }
+
+ if (!oflag) {
+ if (cat)
+ strlcpy(outfile, "stdout", sizeof(outfile));
+ else if (decomp) {
+ if (set_outfile(infile, outfile,
+ sizeof outfile) == NULL) {
+ if (!recurse) {
+ warnx("%s: unknown suffix: "
+ "ignored", infile);
+ rc = rc ? rc : WARNING;
+ }
+ continue;
+ }
+ } else {
+ if (snprintf(outfile, sizeof(outfile),
+ "%s%s", infile, suffix) >= sizeof(outfile)) {
+ warnx("%s%s: name too long",
+ infile, suffix);
+ rc = rc ? rc : WARNING;
+ continue;
+ }
+ }
+ }
+
+ if (verbose > 0 && !pipin && !list)
+ fprintf(stderr, "%s:\t", infile);
+
+ if (decomp)
+ error = dodecompress(infile, outfile, entry->fts_statp);
+ else
+ error = docompress(infile, outfile, method, bits, entry->fts_statp);
+
+ switch (error) {
+ case SUCCESS:
+ if (!cat && !testmode) {
+ if (!pipin && unlink(infile) && verbose >= 0)
+ warn("input: %s", infile);
+ }
+ break;
+ case WARNING:
+ rc = rc ? rc : WARNING;
+ break;
+ default:
+ rc = FAILURE;
+ break;
+ }
+ }
+ if (list)
+ list_stats(NULL, NULL, NULL);
+ fts_close(ftsp);
+ exit(rc);
+}
+
+int
+docompress(const char *in, char *out, const struct compressor *method,
+ int bits, struct stat *sb)
+{
+#ifndef SMALL
+ u_char buf[Z_BUFSIZE];
+ char *name;
+ int error, ifd, ofd, oreg;
+ void *cookie;
+ ssize_t nr;
+ u_int32_t mtime;
+ struct z_info info;
+ struct stat osb;
+
+ mtime = 0;
+ oreg = 0;
+ error = SUCCESS;
+ name = NULL;
+ cookie = NULL;
+
+ if (pipin)
+ ifd = dup(STDIN_FILENO);
+ else
+ ifd = open(in, O_RDONLY);
+ if (ifd < 0) {
+ if (verbose >= 0)
+ warn("%s", in);
+ return (FAILURE);
+ }
+
+ if (cat)
+ ofd = dup(STDOUT_FILENO);
+ else {
+ if (stat(out, &osb) == 0) {
+ oreg = S_ISREG(osb.st_mode);
+ if (!force && oreg && !permission(out)) {
+ (void) close(ifd);
+ return (WARNING);
+ }
+ }
+ ofd = open(out, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR);
+ }
+ if (ofd < 0) {
+ if (verbose >= 0)
+ warn("%s", out);
+ (void) close(ifd);
+ return (FAILURE);
+ }
+
+ if (method != M_COMPRESS && !force && isatty(ofd)) {
+ if (verbose >= 0)
+ warnx("%s: won't write compressed data to terminal",
+ out);
+ (void) close(ofd);
+ (void) close(ifd);
+ return (FAILURE);
+ }
+
+ if (!pipin && storename) {
+ name = basename(in);
+ mtime = (u_int32_t)sb->st_mtime;
+ }
+ if ((cookie = method->wopen(ofd, name, bits, mtime)) == NULL) {
+ if (verbose >= 0)
+ warn("%s", out);
+ if (oreg)
+ (void) unlink(out);
+ (void) close(ofd);
+ (void) close(ifd);
+ return (FAILURE);
+ }
+
+ while ((nr = read(ifd, buf, sizeof(buf))) > 0)
+ if (method->write(cookie, buf, nr) != nr) {
+ if (verbose >= 0)
+ warn("%s", out);
+ error = FAILURE;
+ break;
+ }
+
+ if (!error && nr < 0) {
+ if (verbose >= 0)
+ warn("%s", in);
+ error = FAILURE;
+ }
+
+ if (method->close(cookie, &info, out, sb)) {
+ if (!error && verbose >= 0)
+ warn("%s", out);
+ error = FAILURE;
+ }
+
+ if (close(ifd)) {
+ if (!error && verbose >= 0)
+ warn("%s", in);
+ error = FAILURE;
+ }
+
+ if (!force && !cat && info.total_out >= info.total_in) {
+ if (verbose > 0)
+ fprintf(stderr, "file would grow; left unmodified\n");
+ (void) unlink(out);
+ error = WARNING;
+ }
+
+ if (error) {
+ if (oreg)
+ (void) unlink(out);
+ } else if (verbose > 0)
+ verbose_info(out, info.total_out, info.total_in, info.hlen);
+
+ return (error);
+#else
+ warnx("compression not supported");
+ return (FAILURE);
+#endif
+}
+
+const struct compressor *
+check_method(int fd)
+{
+ const struct compressor *method;
+ u_char magic[2];
+
+ if (read(fd, magic, sizeof(magic)) != 2)
+ return (NULL);
+ for (method = &c_table[0]; method->name != NULL; method++) {
+ if (magic[0] == method->magic[0] &&
+ magic[1] == method->magic[1])
+ return (method);
+ }
+#ifndef SMALL
+ if (force && cat) {
+ null_magic[0] = magic[0];
+ null_magic[1] = magic[1];
+ return (&null_method);
+ }
+#endif /* SMALL */
+ return (NULL);
+}
+
+int
+dodecompress(const char *in, char *out, struct stat *sb)
+{
+ const struct compressor *method;
+ u_char buf[Z_BUFSIZE];
+ char oldname[PATH_MAX];
+ int error, oreg, ifd, ofd;
+ void *cookie;
+ ssize_t nr;
+ struct z_info info;
+ struct stat osb;
+
+ oreg = 0;
+ error = SUCCESS;
+ cookie = NULL;
+
+ if (pipin)
+ ifd = dup(STDIN_FILENO);
+ else
+ ifd = open(in, O_RDONLY);
+ if (ifd < 0) {
+ if (verbose >= 0)
+ warn("%s", in);
+ return -1;
+ }
+
+ if (!force && isatty(ifd)) {
+ if (verbose >= 0)
+ warnx("%s: won't read compressed data from terminal",
+ in);
+ close (ifd);
+ return -1;
+ }
+
+ if ((method = check_method(ifd)) == NULL) {
+ if (verbose >= 0)
+ warnx("%s: unrecognized file format", in);
+ close (ifd);
+ return -1;
+ }
+
+ /* XXX - open constrains outfile to MAXPATHLEN so this is safe */
+ oldname[0] = '\0';
+ if ((cookie = method->ropen(ifd, oldname, 1)) == NULL) {
+ if (verbose >= 0)
+ warn("%s", in);
+ close (ifd);
+ return (FAILURE);
+ }
+ if (storename && oldname[0] != '\0') {
+ char *cp = strrchr(out, '/');
+ if (cp != NULL) {
+ *(cp + 1) = '\0';
+ strlcat(out, oldname, PATH_MAX);
+ } else
+ strlcpy(out, oldname, PATH_MAX);
+ cat = 0; /* XXX should -c override? */
+ }
+
+ if (testmode)
+ ofd = -1;
+ else {
+ if (cat)
+ ofd = dup(STDOUT_FILENO);
+ else {
+ if (stat(out, &osb) == 0) {
+ oreg = S_ISREG(osb.st_mode);
+ if (!force && oreg && !permission(out)) {
+ (void) close(ifd);
+ return (WARNING);
+ }
+ }
+ ofd = open(out, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR);
+ }
+ if (ofd < 0) {
+ if (verbose >= 0)
+ warn("%s", in);
+ method->close(cookie, NULL, NULL, NULL);
+ return (FAILURE);
+ }
+ }
+
+ while ((nr = method->read(cookie, buf, sizeof(buf))) > 0) {
+ if (ofd != -1 && write(ofd, buf, nr) != nr) {
+ if (verbose >= 0)
+ warn("%s", out);
+ error = FAILURE;
+ break;
+ }
+ }
+
+ if (!error && nr < 0) {
+ if (verbose >= 0)
+ warnx("%s: %s", in,
+ errno == EINVAL ? "crc error" : strerror(errno));
+ error = errno == EINVAL ? WARNING : FAILURE;
+ }
+
+ if (method->close(cookie, &info, NULL, NULL)) {
+ if (!error && verbose >= 0)
+ warnx("%s", in);
+ error = FAILURE;
+ }
+ if (storename && !cat) {
+ if (info.mtime != 0) {
+ sb->st_mtimespec.tv_sec =
+ sb->st_atimespec.tv_sec = info.mtime;
+ sb->st_mtimespec.tv_nsec =
+ sb->st_atimespec.tv_nsec = 0;
+ } else
+ storename = 0; /* no timestamp to restore */
+ }
+ if (error == SUCCESS)
+ setfile(out, ofd, sb);
+
+ if (ofd != -1 && close(ofd)) {
+ if (!error && verbose >= 0)
+ warn("%s", out);
+ error = FAILURE;
+ }
+
+ if (!error) {
+ if (list) {
+ if (info.mtime == 0)
+ info.mtime = (u_int32_t)sb->st_mtime;
+ list_stats(out, method, &info);
+ } else if (verbose > 0) {
+ verbose_info(out, info.total_in, info.total_out,
+ info.hlen);
+ }
+ }
+
+ /* On error, clean up the file we created but preserve errno. */
+ if (error && oreg)
+ unlink(out);
+
+ return (error);
+}
+
+void
+setfile(const char *name, int fd, struct stat *fs)
+{
+ struct timespec ts[2];
+
+ if (name == NULL || cat || testmode)
+ return;
+
+ /*
+ * If input was a pipe we don't have any info to restore but we
+ * must set the mode since the current mode on the file is 0200.
+ */
+ if (pipin) {
+ mode_t mask = umask(022);
+ fchmod(fd, DEFFILEMODE & ~mask);
+ umask(mask);
+ return;
+ }
+
+ /*
+ * Changing the ownership probably won't succeed, unless we're root
+ * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid bits are not
+ * allowed.
+ */
+ fs->st_mode &= ACCESSPERMS;
+ if (fchown(fd, fs->st_uid, fs->st_gid)) {
+ if (errno != EPERM)
+ warn("fchown: %s", name);
+ fs->st_mode &= ~(S_ISUID|S_ISGID);
+ }
+ if (fchmod(fd, fs->st_mode))
+ warn("fchmod: %s", name);
+
+#ifdef HAVE_CHFLAGS
+ if (fs->st_flags && fchflags(fd, fs->st_flags))
+ warn("fchflags: %s", name);
+#endif
+
+ ts[0] = fs->st_atim;
+ ts[1] = fs->st_mtim;
+ if (futimens(fd, ts))
+ warn("futimens: %s", name);
+}
+
+int
+permission(const char *fname)
+{
+ int ch, first;
+
+ if (!isatty(fileno(stderr)))
+ return (0);
+ (void)fprintf(stderr, "overwrite %s? ", fname);
+ first = ch = getchar();
+ while (ch != '\n' && ch != EOF)
+ ch = getchar();
+ return (first == 'y');
+}
+
+/*
+ * Check infile for a known suffix and return the suffix portion or NULL.
+ */
+const char *
+check_suffix(const char *infile)
+{
+ int i;
+ char *suf, *sep, *separators = ".-_";
+ static char *suffixes[] = { "Z", "gz", "z", "tgz", "taz", NULL };
+
+ for (sep = separators; *sep != '\0'; sep++) {
+ if ((suf = strrchr(infile, *sep)) == NULL)
+ continue;
+ suf++;
+
+ for (i = 0; suffixes[i] != NULL; i++) {
+ if (strcmp(suf, suffixes[i]) == 0)
+ return (suf - 1);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * Set outfile based on the suffix. In most cases we just strip
+ * off the suffix but things like .tgz and .taz are special.
+ */
+char *
+set_outfile(const char *infile, char *outfile, size_t osize)
+{
+ const char *s;
+ char *cp;
+
+ if ((s = check_suffix(infile)) == NULL)
+ return (NULL);
+
+ (void)strlcpy(outfile, infile, osize);
+ cp = outfile + (s - infile) + 1;
+ /*
+ * Convert tgz and taz -> tar, else drop the suffix.
+ */
+ if (strcmp(cp, "tgz") == 0) {
+ cp[1] = 'a';
+ cp[2] = 'r';
+ } else if (strcmp(cp, "taz") == 0)
+ cp[2] = 'r';
+ else
+ cp[-1] = '\0';
+ return (outfile);
+}
+
+/*
+ * Print output for the -l option.
+ */
+void
+list_stats(const char *name, const struct compressor *method,
+ struct z_info *info)
+{
+ static off_t compressed_total, uncompressed_total, header_total;
+ static u_int nruns;
+ char *timestr;
+
+ if (nruns == 0) {
+ if (verbose >= 0) {
+ if (verbose > 0)
+ fputs("method crc date time ", stdout);
+ puts("compressed uncompressed ratio uncompressed_name");
+ }
+ }
+ nruns++;
+
+ if (name != NULL) {
+ if (verbose > 0) {
+ time_t t = info->mtime; /* XXX 32 bit mtime */
+
+ timestr = ctime(&t) + 4;
+ timestr[12] = '\0';
+ if (timestr[4] == ' ')
+ timestr[4] = '0';
+ printf("%-7.7s %08x %s ", method->name, info->crc,
+ timestr);
+ }
+ printf("%10lld %10lld %4.1f%% %s\n",
+ (long long)(info->total_in + info->hlen),
+ (long long)info->total_out,
+ ((long long)info->total_out - (long long)info->total_in) *
+ 100.0 / info->total_out, name);
+ compressed_total += info->total_in;
+ uncompressed_total += info->total_out;
+ header_total += info->hlen;
+ } else if (verbose >= 0) {
+ if (nruns < 3) /* only do totals for > 1 files */
+ return;
+ if (verbose > 0)
+ fputs(" ", stdout);
+ printf("%10lld %10lld %4.1f%% (totals)\n",
+ (long long)(compressed_total + header_total),
+ (long long)uncompressed_total,
+ (uncompressed_total - compressed_total) *
+ 100.0 / uncompressed_total);
+ }
+}
+
+void
+verbose_info(const char *file, off_t compressed, off_t uncompressed,
+ u_int32_t hlen)
+{
+ if (testmode) {
+ fputs("OK\n", stderr);
+ return;
+ }
+ if (!pipin) {
+ fprintf(stderr, "\t%4.1f%% -- replaced with %s\n",
+ (uncompressed - compressed) * 100.0 / uncompressed, file);
+ }
+ compressed += hlen;
+ fprintf(stderr, "%lld bytes in, %lld bytes out\n",
+ (long long)(decomp ? compressed : uncompressed),
+ (long long)(decomp ? uncompressed : compressed));
+}
+
+__dead void
+usage(int status)
+{
+ const bool gzip = (__progname[0] == 'g');
+
+ switch (pmode) {
+ case MODE_COMP:
+ fprintf(stderr, "usage: %s [-123456789cdf%sh%slNnOqrt%sv] "
+ "[-b bits] [-o filename] [-S suffix]\n"
+ " %*s [file ...]\n", __progname,
+ !gzip ? "g" : "", gzip ? "L" : "", gzip ? "V" : "",
+ (int)strlen(__progname), "");
+ break;
+ case MODE_DECOMP:
+ fprintf(stderr, "usage: %s [-cfh%slNnqrt%sv] [-o filename] "
+ "[file ...]\n", __progname,
+ gzip ? "L" : "", gzip ? "V" : "");
+ break;
+ case MODE_CAT:
+ fprintf(stderr, "usage: %s [-f%shqr] [file ...]\n",
+ __progname, gzip ? "" : "g");
+ break;
+ }
+ exit(status);
+}
diff --git a/usr.bin/compress/nullopen.c b/usr.bin/compress/nullopen.c
new file mode 100644
index 0000000..f883c61
--- /dev/null
+++ b/usr.bin/compress/nullopen.c
@@ -0,0 +1,162 @@
+/* $OpenBSD: nullopen.c,v 1.6 2016/09/03 11:41:10 tedu Exp $ */
+
+/*
+ * Copyright (c) 2003 Can Erkin Acar
+ * Copyright (c) 1997 Michael Shalayeff
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include "compress.h"
+
+typedef struct {
+ off_t total_in;
+ off_t total_out;
+ int fd;
+ int gotmagic;
+ char mode;
+} null_stream;
+
+char null_magic[2];
+
+
+void *
+null_ropen(int fd, char *name, int gotmagic)
+{
+ null_stream *s;
+
+ if (fd < 0)
+ return NULL;
+
+ if ((s = calloc(1, sizeof(null_stream))) == NULL)
+ return NULL;
+
+ s->fd = fd;
+ s->gotmagic = gotmagic;
+ s->total_in = s->total_out = 0;
+ s->mode = 'r';
+
+ return s;
+}
+
+void *
+null_wopen(int fd, char *name, int bits, u_int32_t mtime)
+{
+ null_stream *s;
+
+ if (fd < 0)
+ return NULL;
+
+ if ((s = calloc(1, sizeof(null_stream))) == NULL)
+ return NULL;
+
+ s->fd = fd;
+ s->gotmagic = 0;
+ s->total_in = s->total_out = 0;
+ s->mode = 'w';
+
+ return s;
+}
+
+int
+null_close(void *cookie, struct z_info *info, const char *name, struct stat *sb)
+{
+ null_stream *s = (null_stream*) cookie;
+ int err = 0;
+
+ if (s == NULL)
+ return -1;
+
+
+ if (info != NULL) {
+ info->mtime = 0;
+ info->crc = (u_int32_t)-1;
+ info->hlen = 0;
+ info->total_in = (off_t) s->total_in;
+ info->total_out = (off_t) s->total_out;
+ }
+
+ setfile(name, s->fd, sb);
+ err = close(s->fd);
+
+ free(s);
+
+ return err;
+}
+
+int
+null_flush(void *cookie, int flush)
+{
+ null_stream *s = (null_stream*)cookie;
+
+ if (s == NULL || s->mode != 'w') {
+ errno = EBADF;
+ return (-1);
+ }
+
+ return 0;
+}
+
+int
+null_read(void *cookie, char *buf, int len)
+{
+ null_stream *s = (null_stream*)cookie;
+
+ if (s == NULL) {
+ errno = EBADF;
+ return (-1);
+ }
+ if (s->gotmagic) {
+ if (len < 2) {
+ errno = EBADF;
+ return (-1);
+ }
+
+ buf[0] = null_magic[0];
+ buf[1] = null_magic[1];
+ s->gotmagic = 0;
+
+ return (2);
+ }
+
+ return read(s->fd, buf, len);
+}
+
+int
+null_write(void *cookie, const char *buf, int len)
+{
+ null_stream *s = (null_stream*)cookie;
+
+ if (s == NULL) {
+ errno = EBADF;
+ return (-1);
+ }
+
+ return write(s->fd, buf, len);
+}
diff --git a/usr.bin/compress/zdiff b/usr.bin/compress/zdiff
new file mode 100644
index 0000000..51fb0e7
--- /dev/null
+++ b/usr.bin/compress/zdiff
@@ -0,0 +1,108 @@
+#!/bin/sh -
+#
+# $OpenBSD: zdiff,v 1.2 2003/07/29 07:42:44 otto Exp $
+#
+# Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Sponsored in part by the Defense Advanced Research Projects
+# Agency (DARPA) and Air Force Research Laboratory, Air Force
+# Materiel Command, USAF, under agreement number F39502-99-1-0512.
+#
+
+# Set $prog based on $0
+case $0 in
+ *cmp) prog=cmp
+ ;;
+ *) prog=diff
+ ;;
+esac
+USAGE="usage: z$prog [options] file1 [file2]"
+
+# Pull out any command line flags so we can pass them to diff/cmp
+# XXX - assumes there is no optarg
+flags=
+while test $# -ne 0; do
+ case "$1" in
+ --)
+ shift
+ break
+ ;;
+ -*)
+ flags="$flags $1"
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+if [ $# -eq 1 ]; then
+ # One file given, compare compressed to uncompressed
+ files="$1"
+ case "$1" in
+ *[._-][Zz])
+ files="${1%??}"
+ ;;
+ *[._-]gz)
+ files="${1%???}"
+ ;;
+ *.t[ag]z)
+ files="${1%??}"ar
+ ;;
+ *) echo "z$prog: unknown suffix" 1>&2
+ exit 1
+ esac
+ compress -cdfq "$1" | $prog $flags - "$files"
+ status=$?
+elif [ $# -eq 2 ]; then
+ # Two files given, compare the two uncompressing as needed
+ case "$1" in
+ *[._-][Zz]|*[._-]gz|*.t[ag]z)
+ files=-
+ filt="compress -cdfq $1"
+ ;;
+ *)
+ files="$1"
+ ;;
+ esac
+ case "$2" in
+ *[._-][Zz]|*[._-]gz|*.t[ag]z)
+ if [ "$files" = "-" ]; then
+ tmp=`mktemp -t z$prog.XXXXXXXXXX` || exit 1
+ trap "rm -f $tmp" 0 1 2 3 13 15
+ compress -cdfq "$2" > $tmp
+ files="$files $tmp"
+ else
+ files="$files -"
+ filt="compress -cdfq $2"
+ fi
+ ;;
+ *)
+ files="$files $2"
+ ;;
+ esac
+ if [ -n "$filt" ]; then
+ $filt | $prog $flags $files
+ else
+ $prog $flags $files
+ fi
+ status=$?
+else
+ echo "$USAGE" 1>&2
+ exit 1
+fi
+
+exit $status
diff --git a/usr.bin/compress/zdiff.1 b/usr.bin/compress/zdiff.1
new file mode 100644
index 0000000..7bc88ee
--- /dev/null
+++ b/usr.bin/compress/zdiff.1
@@ -0,0 +1,109 @@
+.\" $OpenBSD: zdiff.1,v 1.6 2007/05/31 19:20:08 jmc Exp $
+.\"
+.\" Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Sponsored in part by the Defense Advanced Research Projects
+.\" Agency (DARPA) and Air Force Research Laboratory, Air Force
+.\" Materiel Command, USAF, under agreement number F39502-99-1-0512.
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt ZDIFF 1
+.Os
+.Sh NAME
+.Nm zcmp ,
+.Nm zdiff
+.Nd compare compressed files
+.Sh SYNOPSIS
+.Nm zcmp
+.Op Ar options
+.Ar file1
+.Op Ar file2
+.Nm zdiff
+.Op Ar options
+.Ar file1
+.Op Ar file2
+.Sh DESCRIPTION
+.Nm zcmp
+and
+.Nm zdiff
+are filters that invoke
+.Xr cmp 1
+or
+.Xr diff 1
+respectively to compare compressed files.
+Such files generally have a
+.Dq Z
+or
+.Dq gz
+extension (both the
+.Xr compress 1
+and
+.Xr gzip 1
+formats are supported).
+Any
+.Ar options
+that are specified are passed to
+.Xr cmp 1
+or
+.Xr diff 1 .
+.Pp
+If only
+.Ar file1
+is specified, it is compared against a file with the same name, but
+with the extension removed.
+When both
+.Ar file1
+or
+.Ar file2
+are specified, either file may be compressed.
+.Sh ENVIRONMENT
+.Bl -tag -width "TMPDIR"
+.It Ev TMPDIR
+Directory in which to place temporary files.
+If unset,
+.Pa /tmp
+is used.
+.El
+.Sh FILES
+.Bl -tag -width "/tmp/zdiff.XXXXXXXXXX" -compact
+.It Pa /tmp/zcmp.XXXXXXXXXX
+Temporary file for
+.Nm zcmp .
+.It Pa /tmp/zdiff.XXXXXXXXXX
+Temporary file for
+.Nm zdiff .
+.El
+.Sh SEE ALSO
+.Xr cmp 1 ,
+.Xr compress 1 ,
+.Xr diff 1 ,
+.Xr gzip 1
+.Sh CAVEATS
+.Nm zcmp
+and
+.Nm zdiff
+rely solely on the file extension to determine what is, or is not,
+a compressed file.
+Consequently, the following are not supported as arguments:
+.Pp
+.Bl -dash -offset indent -compact
+.It
+directories
+.It
+device special files
+.It
+filenames indicating the standard input
+.Pq Sq -
+.El
diff --git a/usr.bin/compress/zforce b/usr.bin/compress/zforce
new file mode 100644
index 0000000..d991da0
--- /dev/null
+++ b/usr.bin/compress/zforce
@@ -0,0 +1,52 @@
+#!/bin/sh -
+#
+# $OpenBSD: zforce,v 1.2 2003/08/05 18:22:17 deraadt Exp $
+#
+# Copyright (c) 2003 Otto Moerbeek <otto@drijf.net>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+prog=`basename $0`
+USAGE="usage: $prog file ..."
+if test $# -eq 0; then
+ echo $USAGE
+ exit 1
+fi
+
+ret=0
+
+while test $# -ne 0; do
+ case "$1" in
+ *[._-]gz)
+ shift
+ ;;
+ *.t[ag]z)
+ shift
+ ;;
+ *)
+ if file "$1" |
+ grep -q "gzip compressed data" 2> /dev/null
+ then
+ n="$1".gz
+ if mv "$1" "$n" 2> /dev/null; then
+ echo "$1" -- renamed to "$n"
+ else
+ ret=1
+ echo $prog: cannot rename "$1" to "$n"
+ fi
+ fi
+ shift
+ ;;
+ esac
+done
+exit $ret
diff --git a/usr.bin/compress/zforce.1 b/usr.bin/compress/zforce.1
new file mode 100644
index 0000000..d4bc178
--- /dev/null
+++ b/usr.bin/compress/zforce.1
@@ -0,0 +1,52 @@
+.\" $OpenBSD: zforce.1,v 1.3 2007/05/31 19:20:09 jmc Exp $
+.\"
+.\" Copyright (c) 2003 Otto Moerbeek <otto@drijf.net>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt ZFORCE 1
+.Os
+.Sh NAME
+.Nm zforce
+.Nd force gzip files to have a .gz suffix
+.Sh SYNOPSIS
+.Nm zforce
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility renames
+.Xr gzip 1
+files to have a
+.Sq .gz
+suffix, so that
+.Xr gzip 1
+will not compress them twice.
+This can be useful if file names were truncated during a file transfer.
+Files that have an existing
+.Sq .gz ,
+.Sq -gz ,
+.Sq _gz ,
+.Sq .tgz
+or
+.Sq .taz
+suffix, or that have not been compressed by
+.Xr gzip 1 ,
+are ignored.
+.Sh SEE ALSO
+.Xr compress 1 ,
+.Xr gzip 1
+.Sh CAVEATS
+.Nm
+overwrites existing files without warning.
diff --git a/usr.bin/compress/zmore b/usr.bin/compress/zmore
new file mode 100644
index 0000000..fc75672
--- /dev/null
+++ b/usr.bin/compress/zmore
@@ -0,0 +1,78 @@
+#!/bin/sh -
+#
+# $OpenBSD: zmore,v 1.8 2015/04/09 19:37:35 millert Exp $
+#
+# Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Sponsored in part by the Defense Advanced Research Projects
+# Agency (DARPA) and Air Force Research Laboratory, Air Force
+# Materiel Command, USAF, under agreement number F39502-99-1-0512.
+#
+
+# Pull out any command line flags so we can pass them to more/less
+flags=
+while test $# -ne 0; do
+ case "$1" in
+ --)
+ shift
+ break
+ ;;
+ -*|+*)
+ flags="$flags $1"
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+if [ `basename $0` == "zless" ] ; then
+ pager=less
+else
+ pager=more
+fi
+
+# No files means read from stdin
+if [ $# -eq 0 ]; then
+ compress -cdf 2>&1 | $pager $flags
+ exit 0
+fi
+
+oterm=`stty -g 2>/dev/null`
+while test $# -ne 0; do
+ compress -cdf "$1" 2>&1 | $pager $flags
+ prev="$1"
+ shift
+ if tty -s && test -n "$oterm" -a $# -gt 0; then
+ #echo -n "--More--(Next file: $1)"
+ echo -n "$prev (END) - Next: $1 "
+ trap "stty $oterm 2>/dev/null" 0 1 2 3 13 15
+ stty cbreak -echo 2>/dev/null
+ REPLY=`dd bs=1 count=1 2>/dev/null`
+ stty $oterm 2>/dev/null
+ trap - 0 1 2 3 13 15
+ echo
+ case "$REPLY" in
+ s)
+ shift
+ ;;
+ e|q)
+ break
+ ;;
+ esac
+ fi
+done
+exit 0
diff --git a/usr.bin/compress/zmore.1 b/usr.bin/compress/zmore.1
new file mode 100644
index 0000000..0686a9e
--- /dev/null
+++ b/usr.bin/compress/zmore.1
@@ -0,0 +1,97 @@
+.\" $OpenBSD: zmore.1,v 1.12 2014/05/08 06:22:07 jmc Exp $
+.\"
+.\" Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Sponsored in part by the Defense Advanced Research Projects
+.\" Agency (DARPA) and Air Force Research Laboratory, Air Force
+.\" Materiel Command, USAF, under agreement number F39502-99-1-0512.
+.\"
+.Dd $Mdocdate: May 8 2014 $
+.Dt ZMORE 1
+.Os
+.Sh NAME
+.Nm zmore ,
+.Nm zless
+.Nd view compressed files
+.Sh SYNOPSIS
+.Nm zmore
+.Op Ar flags
+.Op Ar
+.Nm zless
+.Op Ar flags
+.Op Ar
+.Sh DESCRIPTION
+.Nm
+is a filter that allows the viewing of files compressed with Lempel-Ziv
+encoding.
+Such files generally have a
+.Dq Z
+or
+.Dq gz
+extension (both the
+.Xr compress 1
+and
+.Xr gzip 1
+formats are supported).
+Any
+.Ar flags
+that are specified are passed to
+.Xr more 1
+or
+.Xr less 1 ,
+respectively.
+.Pp
+.Nm zless
+is equivalent to
+.Nm zmore
+but uses
+.Xr less 1
+as a pager instead of
+.Xr more 1 .
+.Pp
+When multiple files are specified,
+.Nm
+will pause at the end of each file and present the following prompt to the user:
+.Bd -literal -offset indent
+prev_file (END) - Next: next_file
+.Ed
+.Pp
+Where
+.Sy prev_file
+is the file that was just displayed and
+.Sy next_file
+is the next file to be displayed.
+The following keys are recognized at the prompt:
+.Bl -tag -width "e or q" -offset indent
+.It Ic e No or Ic q
+quit
+.Nm zmore .
+.It Ic s
+skip the next file (or exit if the next file is the last).
+.El
+.Pp
+If no files are specified,
+.Nm
+will read from the standard input.
+In this mode
+.Nm
+will assume
+.Xr gzip 1
+style compression since there is no suffix on which to make a decision.
+.Sh SEE ALSO
+.Xr compress 1 ,
+.Xr gzip 1 ,
+.Xr less 1 ,
+.Xr more 1
diff --git a/usr.bin/compress/znew b/usr.bin/compress/znew
new file mode 100644
index 0000000..001f689
--- /dev/null
+++ b/usr.bin/compress/znew
@@ -0,0 +1,135 @@
+#!/bin/ksh -
+#
+# $OpenBSD: znew,v 1.3 2005/07/22 09:34:16 jmc Exp $
+#
+# Copyright (c) 2003 Otto Moerbeek <otto@drijf.net>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+# Return 0 if the first arg file size is smaller than the second, 1 otherwise.
+smaller () {
+ a=`du -k "$1" | awk '{ print $1 }'`
+ b=`du -k "$2" | awk '{ print $1 }'`
+ test $a -lt $b
+}
+
+# Check gzip integrity if the -t flag is specified
+checkfile () {
+ if test $tflag -eq 1; then
+ gzip -qt < "$1"
+ fi
+}
+
+# Decompress a file and then gzip it
+process () {
+ prefix="${1%.Z}"
+ filez="$prefix".Z
+ filegz="$prefix".gz
+
+ if test ! -e "$filez"; then
+ echo "$prog: $filez does not exist"
+ return 1
+ fi
+ if test ! -f "$filez"; then
+ echo "$prog: $filez is not a regular file"
+ return 1
+ fi
+ if test -e "$filegz" -a $fflag -eq 0; then
+ echo "$prog: $filegz already exists"
+ return 1
+ fi
+
+ tmp=`mktemp /tmp/znewXXXXXXXXXX` || {
+ echo "$prog: cannot create tmp file"
+ return 1
+ }
+ trap 'rm -f "$tmp"; exit 1' HUP INT QUIT PIPE TERM
+
+ # Do the actual work, producing a file "$tmp"
+ if uncompress -f -c < "$filez" | gzip -f $gzipflags -o "$tmp"; then
+
+ if test $kflag -eq 1 && smaller "$filez" "$tmp"; then
+ echo -n "$prog: $filez is smaller than $filegz"
+ echo "; keeping it"
+ rm -f "$tmp"
+ return 0
+ fi
+ if ! checkfile "$tmp"; then
+ echo "$prog: integrity check of $tmp failed"
+ rm -f "$tmp"
+ return 1;
+ fi
+
+ # Try to keep the mode of the original file
+ if ! cp -fp "$filez" "$filegz"; then
+ echo "$prog: warning: could not keep mode of $filez"
+ fi
+ if ! cp "$tmp" "$filegz" 2> /dev/null; then
+ echo "$prog: warning: could not keep mode of $filez"
+ if ! cp -f "$tmp" "$filegz" 2> /dev/null; then
+ echo "$prog: could not copy $tmp to $filegz"
+ rm -f "$filegz" "$tmp"
+ return 1
+ fi
+ fi
+ if ! touch -fr "$filez" "$filegz"; then
+ echo -n "$prog: warning: could not keep timestamp of "
+ echo "$filez"
+ fi
+ rm -f "$filez" "$tmp"
+ else
+ echo "$prog: failed to process $filez"
+ rm -f "$tmp"
+ return 1
+ fi
+}
+
+prog=`basename "$0"`
+usage="usage: $prog [-9fKtv] file ..."
+
+fflag=0
+tflag=0
+kflag=0
+gzipflags=
+
+# -P flag is recognized to maintain compatibility, but ignored. Pipe mode is
+# always used
+while getopts :ftv9PK i; do
+ case $i in
+ f) fflag=1;;
+ t) tflag=1;;
+ v) gzipflags="-v $gzipflags";;
+ 9) gzipflags="-9 $gzipflags";;
+ P) ;;
+ K) kflag=1;;
+ \?) echo "$usage"; exit 1;;
+ esac
+done
+
+shift OPTIND-1
+
+if test $# -eq 0; then
+ echo "$usage"
+ exit 1
+fi
+
+rc=0
+
+while test $# -ne 0; do
+ if ! process "$1"; then
+ rc=$?
+ fi
+ shift
+done
+exit $rc
diff --git a/usr.bin/compress/znew.1 b/usr.bin/compress/znew.1
new file mode 100644
index 0000000..325fcaf
--- /dev/null
+++ b/usr.bin/compress/znew.1
@@ -0,0 +1,70 @@
+.\" $OpenBSD: znew.1,v 1.4 2007/05/31 19:20:09 jmc Exp $
+.\"
+.\" Copyright (c) 2003 Otto Moerbeek <otto@drijf.net>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt ZNEW 1
+.Os
+.Sh NAME
+.Nm znew
+.Nd convert compressed files to gzipped files
+.Sh SYNOPSIS
+.Nm
+.Op Fl 9fKtv
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility uncompresses files compressed by
+.Xr compress 1
+and recompresses them with
+.Xr gzip 1 .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl 9
+Use the -9 mode of
+.Xr gzip 1 ,
+achieving better compression at the cost of slower execution.
+.It Fl f
+Overwrite existing
+.Sq .gz
+files.
+Unless this option is specified,
+.Nm
+refuses to overwrite existing files.
+.It Fl K
+Keep the original
+.Sq .Z
+file if it uses less disk blocks than the gzipped one.
+A disk block is 1024 bytes.
+.It Fl t
+Test integrity of the gzipped file before deleting the original file.
+If the integrity check fails, the original
+.Sq .Z
+file is not removed.
+.It Fl v
+Print a report specifying the achieved compression ratios.
+.El
+.Sh SEE ALSO
+.Xr compress 1 ,
+.Xr gzip 1
+.Sh CAVEATS
+The
+.Nm
+utility tries to maintain the file mode of the original file.
+If the original file is not writable, it will be unable to do so and
+.Nm
+will print a warning.
diff --git a/usr.bin/compress/zopen.c b/usr.bin/compress/zopen.c
new file mode 100644
index 0000000..e25f310
--- /dev/null
+++ b/usr.bin/compress/zopen.c
@@ -0,0 +1,795 @@
+/* $OpenBSD: zopen.c,v 1.22 2017/05/29 14:41:16 fcambus Exp $ */
+/* $NetBSD: zopen.c,v 1.5 1995/03/26 09:44:53 glass Exp $ */
+
+/*-
+ * Copyright (c) 1985, 1986, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis and James A. Woods, derived from original
+ * work by Spencer Thomas and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: @(#)zopen.c 8.1 (Berkeley) 6/27/93
+ */
+
+/*-
+ * fcompress.c - File compression ala IEEE Computer, June 1984.
+ *
+ * Compress authors:
+ * Spencer W. Thomas (decvax!utah-cs!thomas)
+ * Jim McKie (decvax!mcvax!jim)
+ * Steve Davies (decvax!vax135!petsd!peora!srd)
+ * Ken Turkowski (decvax!decwrl!turtlevax!ken)
+ * James A. Woods (decvax!ihnp4!ames!jaw)
+ * Joe Orost (decvax!vax135!petsd!joe)
+ *
+ * Cleaned up and converted to library returning I/O streams by
+ * Diomidis Spinellis <dds@doc.ic.ac.uk>.
+ *
+ * zopen(filename, mode, bits)
+ * Returns a FILE * that can be used for read or write. The modes
+ * supported are only "r" and "w". Seeking is not allowed. On
+ * reading the file is decompressed, on writing it is compressed.
+ * The output is compatible with compress(1) with 16 bit tables.
+ * Any file produced by compress(1) can be read.
+ */
+
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "compress.h"
+
+#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
+
+#define BITS 16 /* Default bits. */
+#define HSIZE 69001 /* 95% occupancy */
+#define ZBUFSIZ 8192 /* I/O buffer size */
+
+/* A code_int must be able to hold 2**BITS values of type int, and also -1. */
+typedef long code_int;
+typedef long count_int;
+
+static const u_char z_magic[] =
+ {'\037', '\235'}; /* 1F 9D */
+
+#define BIT_MASK 0x1f /* Defines for third byte of header. */
+#define BLOCK_MASK 0x80
+
+/*
+ * Masks 0x40 and 0x20 are free. I think 0x20 should mean that there is
+ * a fourth header byte (for expansion).
+ */
+#define INIT_BITS 9 /* Initial number of bits/code. */
+
+#define MAXCODE(n_bits) ((1 << (n_bits)) - 1)
+
+struct s_zstate {
+ int zs_fd; /* File stream for I/O */
+ char zs_mode; /* r or w */
+ enum {
+ S_START, S_MAGIC, S_MIDDLE, S_EOF
+ } zs_state; /* State of computation */
+ int zs_n_bits; /* Number of bits/code. */
+ int zs_maxbits; /* User settable max # bits/code. */
+ code_int zs_maxcode; /* Maximum code, given n_bits. */
+ code_int zs_maxmaxcode; /* Should NEVER generate this code. */
+ count_int zs_htab[HSIZE];
+ u_short zs_codetab[HSIZE];
+ code_int zs_hsize; /* For dynamic table sizing. */
+ code_int zs_free_ent; /* First unused entry. */
+ /*
+ * Block compression parameters -- after all codes are used up,
+ * and compression rate changes, start over.
+ */
+ int zs_block_compress;
+ int zs_clear_flg;
+ long zs_ratio;
+ count_int zs_checkpoint;
+ long zs_in_count; /* Length of input. */
+ long zs_bytes_out; /* Length of output. */
+ long zs_out_count; /* # of codes output (for debugging).*/
+ u_char zs_buf[ZBUFSIZ]; /* I/O buffer */
+ u_char *zs_bp; /* Current I/O window in the zs_buf */
+ int zs_offset; /* Number of bits in the zs_buf */
+ union {
+ struct {
+ long zs_fcode;
+ code_int zs_ent;
+ code_int zs_hsize_reg;
+ int zs_hshift;
+ } w; /* Write parameters */
+ struct {
+ u_char *zs_stackp, *zs_ebp;
+ int zs_finchar;
+ code_int zs_code, zs_oldcode, zs_incode;
+ int zs_size;
+ } r; /* Read parameters */
+ } u;
+};
+
+/* Definitions to retain old variable names */
+#define zs_fcode u.w.zs_fcode
+#define zs_ent u.w.zs_ent
+#define zs_hsize_reg u.w.zs_hsize_reg
+#define zs_hshift u.w.zs_hshift
+#define zs_stackp u.r.zs_stackp
+#define zs_finchar u.r.zs_finchar
+#define zs_code u.r.zs_code
+#define zs_oldcode u.r.zs_oldcode
+#define zs_incode u.r.zs_incode
+#define zs_size u.r.zs_size
+#define zs_ebp u.r.zs_ebp
+
+/*
+ * To save much memory, we overlay the table used by compress() with those
+ * used by decompress(). The tab_prefix table is the same size and type as
+ * the codetab. The tab_suffix table needs 2**BITS characters. We get this
+ * from the beginning of htab. The output stack uses the rest of htab, and
+ * contains characters. There is plenty of room for any possible stack
+ * (stack used to be 8000 characters).
+ */
+
+#define htabof(i) zs->zs_htab[i]
+#define codetabof(i) zs->zs_codetab[i]
+
+#define tab_prefixof(i) codetabof(i)
+#define tab_suffixof(i) ((u_char *)(zs->zs_htab))[i]
+#define de_stack ((u_char *)&tab_suffixof(1 << BITS))
+
+#define CHECK_GAP 10000 /* Ratio check interval. */
+
+/*
+ * the next two codes should not be changed lightly, as they must not
+ * lie within the contiguous general code space.
+ */
+#define FIRST 257 /* First free entry. */
+#define CLEAR 256 /* Table clear output code. */
+
+static int cl_block(struct s_zstate *);
+static void cl_hash(struct s_zstate *, count_int);
+static code_int getcode(struct s_zstate *);
+static int output(struct s_zstate *, code_int);
+
+/*-
+ * Algorithm from "A Technique for High Performance Data Compression",
+ * Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19.
+ *
+ * Algorithm:
+ * Modified Lempel-Ziv method (LZW). Basically finds common
+ * substrings and replaces them with a variable size code. This is
+ * deterministic, and can be done on the fly. Thus, the decompression
+ * procedure needs no input table, but tracks the way the table was built.
+ */
+
+/*-
+ * compress write
+ *
+ * Algorithm: use open addressing double hashing (no chaining) on the
+ * prefix code / next character combination. We do a variant of Knuth's
+ * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
+ * secondary probe. Here, the modular division first probe is gives way
+ * to a faster exclusive-or manipulation. Also do block compression with
+ * an adaptive reset, whereby the code table is cleared when the compression
+ * ratio decreases, but after the table fills. The variable-length output
+ * codes are re-sized at this point, and a special CLEAR code is generated
+ * for the decompressor. Late addition: construct the table according to
+ * file size for noticeable speed improvement on small files. Please direct
+ * questions about this implementation to ames!jaw.
+ */
+int
+zwrite(void *cookie, const char *wbp, int num)
+{
+ code_int i;
+ int c, disp;
+ struct s_zstate *zs;
+ const u_char *bp;
+ u_char tmp;
+ int count;
+
+ zs = cookie;
+ count = num;
+ bp = (u_char *)wbp;
+ switch (zs->zs_state) {
+ case S_MAGIC:
+ return -1;
+ case S_EOF:
+ return 0;
+ case S_START:
+ zs->zs_state = S_MIDDLE;
+
+ zs->zs_maxmaxcode = 1L << zs->zs_maxbits;
+ if (write(zs->zs_fd, z_magic, sizeof(z_magic)) !=
+ sizeof(z_magic))
+ return (-1);
+ tmp = (u_char)(zs->zs_maxbits | zs->zs_block_compress);
+ if (write(zs->zs_fd, &tmp, sizeof(tmp)) != sizeof(tmp))
+ return (-1);
+
+ zs->zs_bp = zs->zs_buf;
+ zs->zs_offset = 0;
+ zs->zs_bytes_out = 3; /* Includes 3-byte header mojo. */
+ zs->zs_out_count = 0;
+ zs->zs_clear_flg = 0;
+ zs->zs_ratio = 0;
+ zs->zs_in_count = 1;
+ zs->zs_checkpoint = CHECK_GAP;
+ zs->zs_maxcode = MAXCODE(zs->zs_n_bits = INIT_BITS);
+ zs->zs_free_ent = ((zs->zs_block_compress) ? FIRST : 256);
+
+ zs->zs_ent = *bp++;
+ --count;
+
+ zs->zs_hshift = 0;
+ for (zs->zs_fcode = (long)zs->zs_hsize; zs->zs_fcode < 65536L;
+ zs->zs_fcode *= 2L)
+ zs->zs_hshift++;
+ /* Set hash code range bound. */
+ zs->zs_hshift = 8 - zs->zs_hshift;
+
+ zs->zs_hsize_reg = zs->zs_hsize;
+ /* Clear hash table. */
+ cl_hash(zs, (count_int)zs->zs_hsize_reg);
+
+ case S_MIDDLE:
+ for (i = 0; count-- > 0;) {
+ c = *bp++;
+ zs->zs_in_count++;
+ zs->zs_fcode = (long)(((long)c << zs->zs_maxbits) +
+ zs->zs_ent);
+ /* Xor hashing. */
+ i = ((c << zs->zs_hshift) ^ zs->zs_ent);
+
+ if (htabof(i) == zs->zs_fcode) {
+ zs->zs_ent = codetabof(i);
+ continue;
+ } else if ((long)htabof(i) < 0) /* Empty slot. */
+ goto nomatch;
+ /* Secondary hash (after G. Knott). */
+ disp = zs->zs_hsize_reg - i;
+ if (i == 0)
+ disp = 1;
+probe: if ((i -= disp) < 0)
+ i += zs->zs_hsize_reg;
+
+ if (htabof(i) == zs->zs_fcode) {
+ zs->zs_ent = codetabof(i);
+ continue;
+ }
+ if ((long)htabof(i) >= 0)
+ goto probe;
+nomatch: if (output(zs, (code_int) zs->zs_ent) == -1)
+ return (-1);
+ zs->zs_out_count++;
+ zs->zs_ent = c;
+ if (zs->zs_free_ent < zs->zs_maxmaxcode) {
+ /* code -> hashtable */
+ codetabof(i) = zs->zs_free_ent++;
+ htabof(i) = zs->zs_fcode;
+ } else if ((count_int)zs->zs_in_count >=
+ zs->zs_checkpoint && zs->zs_block_compress) {
+ if (cl_block(zs) == -1)
+ return (-1);
+ }
+ }
+ }
+ return (num);
+}
+
+int
+z_close(void *cookie, struct z_info *info, const char *name, struct stat *sb)
+{
+ struct s_zstate *zs;
+ int rval;
+
+ zs = cookie;
+ if (zs->zs_mode == 'w') { /* Put out the final code. */
+ if (output(zs, (code_int) zs->zs_ent) == -1) {
+ (void)close(zs->zs_fd);
+ free(zs);
+ return (-1);
+ }
+ zs->zs_out_count++;
+ if (output(zs, (code_int) - 1) == -1) {
+ (void)close(zs->zs_fd);
+ free(zs);
+ return (-1);
+ }
+ }
+
+ if (info != NULL) {
+ info->mtime = 0;
+ info->crc = (u_int32_t)-1;
+ info->hlen = 0;
+ info->total_in = (off_t)zs->zs_in_count;
+ info->total_out = (off_t)zs->zs_bytes_out;
+ }
+
+#ifndef SAVECORE
+ setfile(name, zs->zs_fd, sb);
+#endif
+ rval = close(zs->zs_fd);
+ free(zs);
+ return (rval);
+}
+
+/*-
+ * Output the given code.
+ * Inputs:
+ * code: A n_bits-bit integer. If == -1, then EOF. This assumes
+ * that n_bits =< (long)wordsize - 1.
+ * Outputs:
+ * Outputs code to the file.
+ * Assumptions:
+ * Chars are 8 bits long.
+ * Algorithm:
+ * Maintain a BITS character long buffer (so that 8 codes will
+ * fit in it exactly). Use the VAX insv instruction to insert each
+ * code in turn. When the buffer fills up empty it and start over.
+ */
+
+static const u_char lmask[9] =
+ {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00};
+static const u_char rmask[9] =
+ {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
+
+static int
+output(struct s_zstate *zs, code_int ocode)
+{
+ int bits;
+
+ if (ocode >= 0) {
+ int r_off;
+ u_char *bp;
+
+ /* Get to the first byte. */
+ bp = zs->zs_bp + (zs->zs_offset >> 3);
+ r_off = zs->zs_offset & 7;
+ bits = zs->zs_n_bits;
+
+ /*
+ * Since ocode is always >= 8 bits, only need to mask the first
+ * hunk on the left.
+ */
+ *bp = (*bp & rmask[r_off]) | ((ocode << r_off) & lmask[r_off]);
+ bp++;
+ bits -= (8 - r_off);
+ ocode >>= 8 - r_off;
+ /* Get any 8 bit parts in the middle (<=1 for up to 16 bits) */
+ if (bits >= 8) {
+ *bp++ = ocode;
+ ocode >>= 8;
+ bits -= 8;
+ }
+ /* Last bits. */
+ if (bits)
+ *bp = ocode;
+ zs->zs_offset += zs->zs_n_bits;
+ if (zs->zs_offset == (zs->zs_n_bits << 3)) {
+ zs->zs_bp += zs->zs_n_bits;
+ zs->zs_offset = 0;
+ }
+ /*
+ * If the next entry is going to be too big for the ocode size,
+ * then increase it, if possible.
+ */
+ if (zs->zs_free_ent > zs->zs_maxcode ||
+ (zs->zs_clear_flg > 0)) {
+ /*
+ * Write the whole buffer, because the input side won't
+ * discover the size increase until after it has read it
+ */
+ if (zs->zs_offset > 0) {
+ zs->zs_bp += zs->zs_n_bits;
+ zs->zs_offset = 0;
+ }
+
+ if (zs->zs_clear_flg) {
+ zs->zs_maxcode =
+ MAXCODE(zs->zs_n_bits = INIT_BITS);
+ zs->zs_clear_flg = 0;
+ } else {
+ zs->zs_n_bits++;
+ if (zs->zs_n_bits == zs->zs_maxbits)
+ zs->zs_maxcode = zs->zs_maxmaxcode;
+ else
+ zs->zs_maxcode =
+ MAXCODE(zs->zs_n_bits);
+ }
+ }
+
+ if (zs->zs_bp + zs->zs_n_bits > &zs->zs_buf[ZBUFSIZ]) {
+ bits = zs->zs_bp - zs->zs_buf;
+ if (write(zs->zs_fd, zs->zs_buf, bits) != bits)
+ return (-1);
+ zs->zs_bytes_out += bits;
+ if (zs->zs_offset > 0)
+ fprintf (stderr, "zs_offset != 0\n");
+ zs->zs_bp = zs->zs_buf;
+ }
+ } else {
+ /* At EOF, write the rest of the buffer. */
+ if (zs->zs_offset > 0)
+ zs->zs_bp += (zs->zs_offset + 7) / 8;
+ if (zs->zs_bp > zs->zs_buf) {
+ bits = zs->zs_bp - zs->zs_buf;
+ if (write(zs->zs_fd, zs->zs_buf, bits) != bits)
+ return (-1);
+ zs->zs_bytes_out += bits;
+ }
+ zs->zs_offset = 0;
+ zs->zs_bp = zs->zs_buf;
+ }
+ return (0);
+}
+
+/*
+ * Decompress read. This routine adapts to the codes in the file building
+ * the "string" table on-the-fly; requiring no table to be stored in the
+ * compressed file. The tables used herein are shared with those of the
+ * compress() routine. See the definitions above.
+ */
+int
+zread(void *cookie, char *rbp, int num)
+{
+ u_int count;
+ struct s_zstate *zs;
+ u_char *bp, header[3];
+
+ if (num == 0)
+ return (0);
+
+ zs = cookie;
+ count = num;
+ bp = (u_char *)rbp;
+ switch (zs->zs_state) {
+ case S_START:
+ zs->zs_state = S_MIDDLE;
+ zs->zs_bp = zs->zs_buf;
+ header[0] = header[1] = header[2] = '\0';
+ read(zs->zs_fd, header, sizeof(header));
+ break;
+ case S_MAGIC:
+ zs->zs_state = S_MIDDLE;
+ zs->zs_bp = zs->zs_buf;
+ header[0] = z_magic[0];
+ header[1] = z_magic[1];
+ header[2] = '\0';
+ read(zs->zs_fd, &header[2], 1);
+ break;
+ case S_MIDDLE:
+ goto middle;
+ case S_EOF:
+ goto eof;
+ }
+
+ /* Check the magic number */
+ if (header[0] != z_magic[0] || header[1] != z_magic[1]) {
+ errno = EFTYPE;
+ return (-1);
+ }
+ zs->zs_maxbits = header[2]; /* Set -b from file. */
+ zs->zs_in_count += sizeof(header);
+ zs->zs_block_compress = zs->zs_maxbits & BLOCK_MASK;
+ zs->zs_maxbits &= BIT_MASK;
+ zs->zs_maxmaxcode = 1L << zs->zs_maxbits;
+ if (zs->zs_maxbits > BITS) {
+ errno = EFTYPE;
+ return (-1);
+ }
+ /* As above, initialize the first 256 entries in the table. */
+ zs->zs_maxcode = MAXCODE(zs->zs_n_bits = INIT_BITS);
+ for (zs->zs_code = 255; zs->zs_code >= 0; zs->zs_code--) {
+ tab_prefixof(zs->zs_code) = 0;
+ tab_suffixof(zs->zs_code) = (u_char) zs->zs_code;
+ }
+ zs->zs_free_ent = zs->zs_block_compress ? FIRST : 256;
+
+ zs->zs_finchar = zs->zs_oldcode = getcode(zs);
+ if (zs->zs_oldcode == -1) /* EOF already? */
+ return (0); /* Get out of here */
+
+ /* First code must be 8 bits = char. */
+ *bp++ = (u_char)zs->zs_finchar;
+ count--;
+ zs->zs_stackp = de_stack;
+
+ while ((zs->zs_code = getcode(zs)) > -1) {
+
+ if ((zs->zs_code == CLEAR) && zs->zs_block_compress) {
+ for (zs->zs_code = 255; zs->zs_code >= 0;
+ zs->zs_code--)
+ tab_prefixof(zs->zs_code) = 0;
+ zs->zs_clear_flg = 1;
+ zs->zs_free_ent = FIRST - 1;
+ if ((zs->zs_code = getcode(zs)) == -1) /* O, untimely death! */
+ break;
+ }
+ zs->zs_incode = zs->zs_code;
+
+ /* Special case for KwKwK string. */
+ if (zs->zs_code >= zs->zs_free_ent) {
+ *zs->zs_stackp++ = zs->zs_finchar;
+ zs->zs_code = zs->zs_oldcode;
+ }
+
+ /* Generate output characters in reverse order. */
+ while (zs->zs_code >= 256) {
+ /*
+ * Bad input file may cause zs_stackp to overflow
+ * zs_htab; check here and abort decompression,
+ * that's better than dumping core.
+ */
+ if (zs->zs_stackp >= (u_char *)&zs->zs_htab[HSIZE]) {
+ errno = EINVAL;
+ return (-1);
+ }
+ *zs->zs_stackp++ = tab_suffixof(zs->zs_code);
+ zs->zs_code = tab_prefixof(zs->zs_code);
+ }
+ *zs->zs_stackp++ = zs->zs_finchar = tab_suffixof(zs->zs_code);
+
+ /* And put them out in forward order. */
+middle: do {
+ if (count-- == 0) {
+ zs->zs_bytes_out += num;
+ return (num);
+ }
+ *bp++ = *--zs->zs_stackp;
+ } while (zs->zs_stackp > de_stack);
+
+ /* Generate the new entry. */
+ if ((zs->zs_code = zs->zs_free_ent) < zs->zs_maxmaxcode) {
+ tab_prefixof(zs->zs_code) = (u_short) zs->zs_oldcode;
+ tab_suffixof(zs->zs_code) = zs->zs_finchar;
+ zs->zs_free_ent = zs->zs_code + 1;
+ }
+
+ /* Remember previous code. */
+ zs->zs_oldcode = zs->zs_incode;
+ }
+ zs->zs_state = S_EOF;
+ zs->zs_bytes_out += num - count;
+eof: return (num - count);
+}
+
+/*-
+ * Read one code from the standard input. If EOF, return -1.
+ * Inputs:
+ * stdin
+ * Outputs:
+ * code or -1 is returned.
+ */
+static code_int
+getcode(struct s_zstate *zs)
+{
+ code_int gcode;
+ int r_off, bits;
+ u_char *bp;
+
+ if (zs->zs_clear_flg > 0 || zs->zs_offset >= zs->zs_size ||
+ zs->zs_free_ent > zs->zs_maxcode) {
+
+ zs->zs_bp += zs->zs_n_bits;
+ /*
+ * If the next entry will be too big for the current gcode
+ * size, then we must increase the size. This implies reading
+ * a new buffer full, too.
+ */
+ if (zs->zs_free_ent > zs->zs_maxcode) {
+ zs->zs_n_bits++;
+ if (zs->zs_n_bits == zs->zs_maxbits) {
+ /* Won't get any bigger now. */
+ zs->zs_maxcode = zs->zs_maxmaxcode;
+ } else
+ zs->zs_maxcode = MAXCODE(zs->zs_n_bits);
+ }
+ if (zs->zs_clear_flg > 0) {
+ zs->zs_maxcode = MAXCODE(zs->zs_n_bits = INIT_BITS);
+ zs->zs_clear_flg = 0;
+ }
+
+ /* fill the buffer up to the neck */
+ if (zs->zs_bp + zs->zs_n_bits > zs->zs_ebp) {
+ for (bp = zs->zs_buf; zs->zs_bp < zs->zs_ebp;
+ *bp++ = *zs->zs_bp++);
+ if ((bits = read(zs->zs_fd, bp, ZBUFSIZ -
+ (bp - zs->zs_buf))) < 0)
+ return -1;
+ zs->zs_in_count += bits;
+ zs->zs_bp = zs->zs_buf;
+ zs->zs_ebp = bp + bits;
+ }
+ zs->zs_offset = 0;
+ zs->zs_size = MINIMUM(zs->zs_n_bits, zs->zs_ebp - zs->zs_bp);
+ if (zs->zs_size == 0)
+ return -1;
+ /* Round size down to integral number of codes. */
+ zs->zs_size = (zs->zs_size << 3) - (zs->zs_n_bits - 1);
+ }
+
+ bp = zs->zs_bp;
+ r_off = zs->zs_offset;
+ bits = zs->zs_n_bits;
+
+ /* Get to the first byte. */
+ bp += (r_off >> 3);
+ r_off &= 7;
+
+ /* Get first part (low order bits). */
+ gcode = (*bp++ >> r_off);
+ bits -= (8 - r_off);
+ r_off = 8 - r_off; /* Now, roffset into gcode word. */
+
+ /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
+ if (bits >= 8) {
+ gcode |= *bp++ << r_off;
+ r_off += 8;
+ bits -= 8;
+ }
+
+ /* High order bits. */
+ gcode |= (*bp & rmask[bits]) << r_off;
+ zs->zs_offset += zs->zs_n_bits;
+
+ return (gcode);
+}
+
+/* Table clear for block compress. */
+static int
+cl_block(struct s_zstate *zs)
+{
+ long rat;
+
+ zs->zs_checkpoint = zs->zs_in_count + CHECK_GAP;
+
+ if (zs->zs_in_count > 0x007fffff) { /* Shift will overflow. */
+ rat = zs->zs_bytes_out >> 8;
+ if (rat == 0) /* Don't divide by zero. */
+ rat = 0x7fffffff;
+ else
+ rat = zs->zs_in_count / rat;
+ } else {
+ /* 8 fractional bits. */
+ rat = (zs->zs_in_count << 8) / zs->zs_bytes_out;
+ }
+ if (rat > zs->zs_ratio)
+ zs->zs_ratio = rat;
+ else {
+ zs->zs_ratio = 0;
+ cl_hash(zs, (count_int) zs->zs_hsize);
+ zs->zs_free_ent = FIRST;
+ zs->zs_clear_flg = 1;
+ if (output(zs, (code_int) CLEAR) == -1)
+ return (-1);
+ }
+ return (0);
+}
+
+/* Reset code table. */
+static void
+cl_hash(struct s_zstate *zs, count_int cl_hsize)
+{
+ count_int *htab_p;
+ long i, m1;
+
+ m1 = -1;
+ htab_p = zs->zs_htab + cl_hsize;
+ i = cl_hsize - 16;
+ do { /* Might use Sys V memset(3) here. */
+ *(htab_p - 16) = m1;
+ *(htab_p - 15) = m1;
+ *(htab_p - 14) = m1;
+ *(htab_p - 13) = m1;
+ *(htab_p - 12) = m1;
+ *(htab_p - 11) = m1;
+ *(htab_p - 10) = m1;
+ *(htab_p - 9) = m1;
+ *(htab_p - 8) = m1;
+ *(htab_p - 7) = m1;
+ *(htab_p - 6) = m1;
+ *(htab_p - 5) = m1;
+ *(htab_p - 4) = m1;
+ *(htab_p - 3) = m1;
+ *(htab_p - 2) = m1;
+ *(htab_p - 1) = m1;
+ htab_p -= 16;
+ } while ((i -= 16) >= 0);
+ for (i += 16; i > 0; i--)
+ *--htab_p = m1;
+}
+
+void *
+z_wopen(int fd, char *name, int bits, u_int32_t mtime)
+{
+ struct s_zstate *zs;
+
+ if (bits < 0 || bits > BITS) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if ((zs = calloc(1, sizeof(struct s_zstate))) == NULL)
+ return (NULL);
+
+ /* User settable max # bits/code. */
+ zs->zs_maxbits = bits ? bits : BITS;
+ /* Should NEVER generate this code. */
+ zs->zs_maxmaxcode = 1 << zs->zs_maxbits;
+ zs->zs_hsize = HSIZE; /* For dynamic table sizing. */
+ zs->zs_free_ent = 0; /* First unused entry. */
+ zs->zs_block_compress = BLOCK_MASK;
+ zs->zs_clear_flg = 0;
+ zs->zs_ratio = 0;
+ zs->zs_checkpoint = CHECK_GAP;
+ zs->zs_in_count = 0; /* Length of input. */
+ zs->zs_out_count = 0; /* # of codes output (for debugging).*/
+ zs->zs_state = S_START;
+ zs->zs_offset = 0;
+ zs->zs_size = 0;
+ zs->zs_mode = 'w';
+ zs->zs_bp = zs->zs_ebp = zs->zs_buf;
+
+ zs->zs_fd = fd;
+ return zs;
+}
+
+void *
+z_ropen(int fd, char *name, int gotmagic)
+{
+ struct s_zstate *zs;
+
+ if ((zs = calloc(1, sizeof(struct s_zstate))) == NULL)
+ return (NULL);
+
+ /* User settable max # bits/code. */
+ zs->zs_maxbits = BITS;
+ /* Should NEVER generate this code. */
+ zs->zs_maxmaxcode = 1 << zs->zs_maxbits;
+ zs->zs_hsize = HSIZE; /* For dynamic table sizing. */
+ zs->zs_free_ent = 0; /* First unused entry. */
+ zs->zs_block_compress = BLOCK_MASK;
+ zs->zs_clear_flg = 0;
+ zs->zs_ratio = 0;
+ zs->zs_checkpoint = CHECK_GAP;
+ zs->zs_in_count = 0; /* Length of input. */
+ zs->zs_out_count = 0; /* # of codes output (for debugging).*/
+ zs->zs_state = gotmagic ? S_MAGIC : S_START;
+ zs->zs_offset = 0;
+ zs->zs_size = 0;
+ zs->zs_mode = 'r';
+ zs->zs_bp = zs->zs_ebp = zs->zs_buf;
+
+ zs->zs_fd = fd;
+ return zs;
+}