summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog14
-rw-r--r--INSTALL2
-rw-r--r--Makefile.in24
-rw-r--r--NEWS32
-rw-r--r--README9
-rwxr-xr-xconfigure18
-rw-r--r--doc/zcat.121
-rw-r--r--doc/zcmp.132
-rw-r--r--doc/zdiff.132
-rw-r--r--doc/zgrep.121
-rw-r--r--doc/ztest.123
-rw-r--r--doc/zutils.info202
-rw-r--r--doc/zutils.texinfo179
-rw-r--r--main.cc50
-rw-r--r--rc.cc229
-rw-r--r--rc.h27
-rwxr-xr-xtestsuite/check.sh253
-rw-r--r--zcat.cc29
-rw-r--r--zcmp.cc60
-rw-r--r--zcmpdiff.cc6
-rw-r--r--zdiff.cc152
-rw-r--r--zgrep.cc46
-rw-r--r--ztest.cc106
-rw-r--r--zutils.cc123
-rw-r--r--zutils.h28
-rw-r--r--zutilsrc16
26 files changed, 1151 insertions, 583 deletions
diff --git a/ChangeLog b/ChangeLog
index 7140457..250e1db 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2013-07-07 Antonio Diaz Diaz <antonio@gnu.org>
+
+ * Version 1.1-rc2 released.
+ * Fixed all uses of decompressed/uncompressed.
+
+2013-07-04 Antonio Diaz Diaz <antonio@gnu.org>
+
+ * Version 1.1-rc1 released.
+ * Added options '--bz2', '--gz', '--lz' and '--xz' to all utilities.
+ * Added runtime configuration file 'zutilsrc'.
+ * New function 'good_status' checks exit status of all children.
+
2013-05-31 Antonio Diaz Diaz <antonio@gnu.org>
* Version 1.0 released.
@@ -7,7 +19,7 @@
file and '--recursive' has not been selected.
* Zgrep: Fixed output of option '-L' (it behaved like '-l').
* zcmp.cc: Fixed deadlock when '-n' option is used.
- * zdiff.cc (set_data_feeder): Call decompressor with option "-q"
+ * zdiff.cc (set_data_feeder): Call compressor with option "-q"
only if verbosity < 0.
* zutils.cc (set_data_feeder): Likewise.
* Changed quote characters in messages as advised by GNU Standards.
diff --git a/INSTALL b/INSTALL
index 13f8af3..fc824b2 100644
--- a/INSTALL
+++ b/INSTALL
@@ -1,7 +1,7 @@
Requirements
------------
You will need a C++ compiler.
-I use gcc 4.8.0 and 3.3.6, but the code should compile with any
+I use gcc 4.8.1 and 3.3.6, but the code should compile with any
standards compliant compiler.
Gcc is available at http://gcc.gnu.org.
diff --git a/Makefile.in b/Makefile.in
index bb47d57..919ee36 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -7,9 +7,9 @@ INSTALL_DATA = $(INSTALL) -p -m 644
INSTALL_DIR = $(INSTALL) -d -m 755
SHELL = /bin/sh
-objs = arg_parser.o zutils.o main.o
-zcmp_objs = arg_parser.o zutils.o zcmp.o
-zdiff_objs = arg_parser.o zutils.o zdiff.o
+objs = arg_parser.o rc.o zutils.o main.o
+zcmp_objs = arg_parser.o rc.o zutils.o zcmp.o
+zdiff_objs = arg_parser.o rc.o zutils.o zdiff.o
scripts = zcat zegrep zfgrep zgrep ztest
@@ -57,6 +57,9 @@ main.o : main.cc
zdiff.o : zdiff.cc
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -DDIFF=\"$(DIFF)\" -c -o $@ $<
+rc.o : rc.cc
+ $(CXX) $(CPPFLAGS) $(CXXFLAGS) -DSYSCONFDIR=\"$(sysconfdir)\" -c -o $@ $<
+
zutils.o : zutils.cc
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $<
@@ -66,10 +69,11 @@ zutils.o : zutils.cc
$(objs) : Makefile
$(scripts) : Makefile
arg_parser.o : arg_parser.h
-main.o : arg_parser.h zutils.h zcat.cc zgrep.cc ztest.cc
-zcmp.o : arg_parser.h zutils.h zcmpdiff.cc Makefile
-zdiff.o : arg_parser.h zutils.h zcmpdiff.cc Makefile
-zutils.o : zutils.h
+main.o : arg_parser.h zutils.h rc.h zcat.cc zgrep.cc ztest.cc
+rc.o : arg_parser.h zutils.h rc.h
+zcmp.o : arg_parser.h zutils.h rc.h zcmpdiff.cc Makefile
+zdiff.o : arg_parser.h zutils.h rc.h zcmpdiff.cc Makefile
+zutils.o : zutils.h rc.h
doc : info man
@@ -120,6 +124,10 @@ install-bin : all
$(INSTALL_SCRIPT) ./zfgrep "$(DESTDIR)$(bindir)/zfgrep"
$(INSTALL_SCRIPT) ./zgrep "$(DESTDIR)$(bindir)/zgrep"
$(INSTALL_SCRIPT) ./ztest "$(DESTDIR)$(bindir)/ztest"
+ if [ ! -e "$(DESTDIR)$(sysconfdir)/$(pkgname)rc" ] ; then \
+ if [ ! -d "$(DESTDIR)$(sysconfdir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(sysconfdir)" ; fi ; \
+ $(INSTALL_DATA) $(VPATH)/$(pkgname)rc "$(DESTDIR)$(sysconfdir)/$(pkgname)rc" ; \
+ fi
install-info :
if [ ! -d "$(DESTDIR)$(infodir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(infodir)" ; fi
@@ -148,6 +156,7 @@ uninstall-bin :
-rm -f "$(DESTDIR)$(bindir)/zfgrep"
-rm -f "$(DESTDIR)$(bindir)/zgrep"
-rm -f "$(DESTDIR)$(bindir)/ztest"
+ -rm -f "$(DESTDIR)$(sysconfdir)/$(pkgname)rc"
uninstall-info :
-install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$(pkgname).info"
@@ -177,6 +186,7 @@ dist : doc
$(DISTNAME)/testsuite/check.sh \
$(DISTNAME)/testsuite/test.txt \
$(DISTNAME)/testsuite/test.txt.tar \
+ $(DISTNAME)/$(pkgname)rc \
$(DISTNAME)/*.h \
$(DISTNAME)/*.cc \
$(DISTNAME)/z*.in
diff --git a/NEWS b/NEWS
index aa99159..b43d32d 100644
--- a/NEWS
+++ b/NEWS
@@ -1,28 +1,12 @@
-Changes in version 1.0:
+Changes in version 1.1:
-The new option "--format" has been added to all utilities.
+The new options "--bz2", "--gz", "--lz" and "--xz" have been added to
+all utilities.
-Zgrep no more prefixes the file name to the output by default when
-searching one file and "--recursive" has not been selected.
+Zutils now provides the runtime configuration file "zutilsrc", which
+allows the user change the compressor to be used for each format.
-The output of "zgrep -L" has been fixed (it behaved like "zgrep -l").
+The checking of the exit status of compressors has been improved.
-A deadlock in zcmp, which happens when the "-n" option is used, has been
-fixed.
-
-Decompressors are now invoked without the "-q" option except if
-explicitly requested, as some simplified implementations do not accept
-it.
-
-Quote characters in messages have been changed as advised by GNU Coding
-Standards.
-
-"configure" now accepts options with a separate argument.
-
-Configure option "--datadir" has been renamed to "--datarootdir" to
-follow GNU Standards.
-
-The target "install-bin" has been added to the Makefile.
-
-Information has been added to the INSTALL file about how to install
-zutils along with GNU gzip.
+The use of "decompressed" and "uncompressed" in the documentation have
+been revised.
diff --git a/README b/README
index 1d5c7da..b47b5b2 100644
--- a/README
+++ b/README
@@ -1,8 +1,8 @@
Description
Zutils is a collection of utilities able to deal with any combination of
-compressed and non-compressed files transparently. If any given file,
-including standard input, is compressed, its uncompressed content is
+compressed and uncompressed files transparently. If any given file,
+including standard input, is compressed, its decompressed content is
used. Compressed files are decompressed on the fly; no temporary files
are created.
@@ -12,6 +12,7 @@ those utilities supporting it.
The provided utilities are zcat, zcmp, zdiff, zgrep and ztest.
The supported formats are bzip2, gzip, lzip and xz.
+The compressor to be used for each format is configurable at runtime.
Zcat, zcmp, zdiff, and zgrep are improved replacements for the shell
scripts provided with GNU gzip. Ztest is unique to zutils.
@@ -20,6 +21,10 @@ NOTE: Bzip2 and lzip provide well-defined values of exit status, which
makes them safe to use with zutils. Gzip and xz may return ambiguous
warning values, making them less reliable backends for zutils.
+LANGUAGE NOTE: Uncompressed = not compressed = plain data; it may never
+have been compressed. Decompressed is used to refer to data which has
+undergone the process of decompression.
+
Copyright (C) 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
diff --git a/configure b/configure
index 6447312..2d5898f 100755
--- a/configure
+++ b/configure
@@ -6,9 +6,9 @@
# to copy, distribute and modify it.
pkgname=zutils
-pkgversion=1.0
+pkgversion=1.1-rc2
progname=zutils
-srctrigger=zutils.h
+srctrigger=${pkgname}rc
# clear some things potentially inherited from environment.
LC_ALL=C
@@ -20,6 +20,7 @@ bindir='$(exec_prefix)/bin'
datarootdir='$(prefix)/share'
infodir='$(datarootdir)/info'
mandir='$(datarootdir)/man'
+sysconfdir='$(prefix)/etc'
CXX=g++
CPPFLAGS=
CXXFLAGS='-Wall -W -O2'
@@ -66,6 +67,7 @@ while [ $# != 0 ] ; do
echo " --datarootdir=DIR base directory for doc and data [${datarootdir}]"
echo " --infodir=DIR info files directory [${infodir}]"
echo " --mandir=DIR man pages directory [${mandir}]"
+ echo " --sysconfdir=DIR read-only single-machine data directory [${sysconfdir}]"
echo " CXX=COMPILER C++ compiler to use [g++]"
echo " CPPFLAGS=OPTIONS command line options for the preprocessor [${CPPFLAGS}]"
echo " CXXFLAGS=OPTIONS command line options for the C++ compiler [${CXXFLAGS}]"
@@ -84,6 +86,7 @@ while [ $# != 0 ] ; do
--datarootdir) datarootdir=$1 ; arg2=yes ;;
--infodir) infodir=$1 ; arg2=yes ;;
--mandir) mandir=$1 ; arg2=yes ;;
+ --sysconfdir) sysconfdir=$1 ; arg2=yes ;;
--srcdir=*) srcdir=${optarg} ;;
--prefix=*) prefix=${optarg} ;;
@@ -92,6 +95,7 @@ while [ $# != 0 ] ; do
--datarootdir=*) datarootdir=${optarg} ;;
--infodir=*) infodir=${optarg} ;;
--mandir=*) mandir=${optarg} ;;
+ --sysconfdir=*) sysconfdir=${optarg} ;;
--no-create) no_create=yes ;;
CXX=*) CXX=${optarg} ;;
@@ -106,7 +110,7 @@ while [ $# != 0 ] ; do
*=* | *-*-*) ;;
*)
echo "configure: unrecognized option: '${option}'" 1>&2
- echo "Try 'configure --help' for more information."
+ echo "Try 'configure --help' for more information." 1>&2
exit 1 ;;
esac
@@ -131,10 +135,8 @@ if [ -z "${srcdir}" ] ; then
fi
if [ ! -r "${srcdir}/${srctrigger}" ] ; then
- exec 1>&2
- echo
- echo "configure: Can't find sources in ${srcdir} ${srcdirtext}"
- echo "configure: (At least ${srctrigger} is missing)."
+ echo "configure: Can't find sources in ${srcdir} ${srcdirtext}" 1>&2
+ echo "configure: (At least ${srctrigger} is missing)." 1>&2
exit 1
fi
@@ -166,6 +168,7 @@ echo "bindir = ${bindir}"
echo "datarootdir = ${datarootdir}"
echo "infodir = ${infodir}"
echo "mandir = ${mandir}"
+echo "sysconfdir = ${sysconfdir}"
echo "CXX = ${CXX}"
echo "CPPFLAGS = ${CPPFLAGS}"
echo "CXXFLAGS = ${CXXFLAGS}"
@@ -191,6 +194,7 @@ bindir = ${bindir}
datarootdir = ${datarootdir}
infodir = ${infodir}
mandir = ${mandir}
+sysconfdir = ${sysconfdir}
CXX = ${CXX}
CPPFLAGS = ${CPPFLAGS}
CXXFLAGS = ${CXXFLAGS}
diff --git a/doc/zcat.1 b/doc/zcat.1
index 146e6e0..4312728 100644
--- a/doc/zcat.1
+++ b/doc/zcat.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1.
-.TH ZCAT "1" "May 2013" "Zcat (zutils) 1.0" "User Commands"
+.TH ZCAT "1" "July 2013" "Zcat (zutils) 1.1-rc2" "User Commands"
.SH NAME
Zcat \- decompress and concatenate files to standard output
.SH SYNOPSIS
@@ -7,7 +7,7 @@ Zcat \- decompress and concatenate files to standard output
[\fIoptions\fR] [\fIfiles\fR]
.SH DESCRIPTION
Zcat copies each given file ("\-" means standard input), to standard
-output. If any given file is compressed, its uncompressed content is
+output. If any given file is compressed, its decompressed content is
used. If a given file does not exist, and its name does not end with one
of the known extensions, zcat tries the compressed file names
corresponding to the supported formats. If no files are specified,
@@ -17,7 +17,7 @@ all uncompressed or all in the same compression format.
.PP
The supported formats are bzip2, gzip, lzip and xz.
.PP
-Exit status is 0 if no errors occurred, 1 otherwise.
+Exit status is 0 if no errors occurred, non\-zero otherwise.
.SH OPTIONS
.TP
\fB\-h\fR, \fB\-\-help\fR
@@ -44,6 +44,9 @@ force given format (bz2, gz, lz, xz)
\fB\-n\fR, \fB\-\-number\fR
number all output lines
.TP
+\fB\-N\fR, \fB\-\-no\-rcfile\fR
+don't read runtime configuration file
+.TP
\fB\-q\fR, \fB\-\-quiet\fR
suppress all messages
.TP
@@ -64,6 +67,18 @@ use '^' and 'M\-' notation, except for LF and TAB
.TP
\fB\-\-verbose\fR
verbose mode (show error messages)
+.TP
+\fB\-\-bz2=\fR<command>
+set compressor and options for bzip2 format
+.TP
+\fB\-\-gz=\fR<command>
+set compressor and options for gzip format
+.TP
+\fB\-\-lz=\fR<command>
+set compressor and options for lzip format
+.TP
+\fB\-\-xz=\fR<command>
+set compressor and options for xz format
.SH "REPORTING BUGS"
Report bugs to zutils\-bug@nongnu.org
.br
diff --git a/doc/zcmp.1 b/doc/zcmp.1
index 68f44fd..e171601 100644
--- a/doc/zcmp.1
+++ b/doc/zcmp.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1.
-.TH ZCMP "1" "May 2013" "Zcmp (zutils) 1.0" "User Commands"
+.TH ZCMP "1" "July 2013" "Zcmp (zutils) 1.1-rc2" "User Commands"
.SH NAME
Zcmp \- decompress and compare two files byte by byte
.SH SYNOPSIS
@@ -9,19 +9,22 @@ Zcmp \- decompress and compare two files byte by byte
Zcmp compares two files ("\-" means standard input), and if they
differ, tells the first byte and line number where they differ. Bytes
and lines are numbered starting with 1. If any given file is compressed,
-its uncompressed content is used. Compressed files are uncompressed on
+its decompressed content is used. Compressed files are decompressed on
the fly; no temporary files are created.
.PP
The supported formats are bzip2, gzip, lzip and xz.
.PP
Compares <file1> to <file2>. If <file2> is omitted zcmp tries the
following:
-If <file1> is compressed, compares <file1> to the file with the
-corresponding decompressed file name (removes the extension from
-<file1>).
-If <file1> is not compressed, compares <file1> to the uncompressed
+.IP
+1. If <file1> is compressed, compares its decompressed contents with
+the corresponding uncompressed file (the name of <file1> with the
+extension removed).
+.IP
+2. If <file1> is uncompressed, compares it with the decompressed
contents of <file1>.[lz|bz2|gz|xz] (the first one that is found).
-If no suitable file is found, compares <file1> to data read from
+.IP
+3. If no suitable file is found, compares <file1> with data read from
standard input.
.PP
Exit status is 0 if inputs are identical, 1 if different, 2 if trouble.
@@ -48,6 +51,9 @@ list position, value of all differing bytes
\fB\-n\fR, \fB\-\-bytes=\fR<n>
compare at most <n> bytes
.TP
+\fB\-N\fR, \fB\-\-no\-rcfile\fR
+don't read runtime configuration file
+.TP
\fB\-q\fR, \fB\-\-quiet\fR
suppress all messages
.TP
@@ -56,6 +62,18 @@ suppress all messages
.TP
\fB\-v\fR, \fB\-\-verbose\fR
verbose mode (same as \fB\-\-list\fR)
+.TP
+\fB\-\-bz2=\fR<command>
+set compressor and options for bzip2 format
+.TP
+\fB\-\-gz=\fR<command>
+set compressor and options for gzip format
+.TP
+\fB\-\-lz=\fR<command>
+set compressor and options for lzip format
+.TP
+\fB\-\-xz=\fR<command>
+set compressor and options for xz format
.PP
Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,
Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...
diff --git a/doc/zdiff.1 b/doc/zdiff.1
index be9d335..260d8dd 100644
--- a/doc/zdiff.1
+++ b/doc/zdiff.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1.
-.TH ZDIFF "1" "May 2013" "Zdiff (zutils) 1.0" "User Commands"
+.TH ZDIFF "1" "July 2013" "Zdiff (zutils) 1.1-rc2" "User Commands"
.SH NAME
Zdiff \- decompress and compare two files line by line
.SH SYNOPSIS
@@ -8,7 +8,7 @@ Zdiff \- decompress and compare two files line by line
.SH DESCRIPTION
Zdiff compares two files ("\-" means standard input), and if they
differ, shows the differences line by line. If any given file is
-compressed, its uncompressed content is used. Zdiff is a front end to
+compressed, its decompressed content is used. Zdiff is a front end to
the diff program and has the limitation that messages from diff refer to
temporary filenames instead of those specified.
.PP
@@ -16,12 +16,15 @@ The supported formats are bzip2, gzip, lzip and xz.
.PP
Compares <file1> to <file2>. If <file2> is omitted zdiff tries the
following:
-If <file1> is compressed, compares <file1> to the file with the
-corresponding decompressed file name (removes the extension from
-<file1>).
-If <file1> is not compressed, compares <file1> to the uncompressed
+.IP
+1. If <file1> is compressed, compares its decompressed contents with
+the corresponding uncompressed file (the name of <file1> with the
+extension removed).
+.IP
+2. If <file1> is uncompressed, compares it with the decompressed
contents of <file1>.[lz|bz2|gz|xz] (the first one that is found).
-If no suitable file is found, compares <file1> to data read from
+.IP
+3. If no suitable file is found, compares <file1> with data read from
standard input.
.PP
Exit status is 0 if inputs are identical, 1 if different, 2 if trouble.
@@ -60,6 +63,9 @@ force given formats (bz2, gz, lz, xz)
\fB\-i\fR, \fB\-\-ignore\-case\fR
ignore case differences in file contents
.TP
+\fB\-N\fR, \fB\-\-no\-rcfile\fR
+don't read runtime configuration file
+.TP
\fB\-p\fR, \fB\-\-show\-c\-function\fR
show which C function each change is in
.TP
@@ -83,6 +89,18 @@ same as \fB\-u\fR but use <n> lines of context
.TP
\fB\-w\fR, \fB\-\-ignore\-all\-space\fR
ignore all white space
+.TP
+\fB\-\-bz2=\fR<command>
+set compressor and options for bzip2 format
+.TP
+\fB\-\-gz=\fR<command>
+set compressor and options for gzip format
+.TP
+\fB\-\-lz=\fR<command>
+set compressor and options for lzip format
+.TP
+\fB\-\-xz=\fR<command>
+set compressor and options for xz format
.PP
Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,
Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...
diff --git a/doc/zgrep.1 b/doc/zgrep.1
index 452c26d..cc85d0a 100644
--- a/doc/zgrep.1
+++ b/doc/zgrep.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1.
-.TH ZGREP "1" "May 2013" "Zgrep (zutils) 1.0" "User Commands"
+.TH ZGREP "1" "July 2013" "Zgrep (zutils) 1.1-rc2" "User Commands"
.SH NAME
Zgrep \- search compressed files for a regular expression
.SH SYNOPSIS
@@ -7,8 +7,8 @@ Zgrep \- search compressed files for a regular expression
[\fIoptions\fR] \fI<pattern> \fR[\fIfiles\fR]
.SH DESCRIPTION
Zgrep is a front end to the grep program that allows transparent search
-on any combination of compressed and non\-compressed files. If any given
-file is compressed, its uncompressed content is used. If a given file
+on any combination of compressed and uncompressed files. If any given
+file is compressed, its decompressed content is used. If a given file
does not exist, and its name does not end with one of the known
extensions, zgrep tries the compressed file names corresponding to the
supported formats. If no files are specified, data is read from
@@ -84,6 +84,9 @@ stop after <n> matches
\fB\-n\fR, \fB\-\-line\-number\fR
print the line number of each line
.TP
+\fB\-N\fR, \fB\-\-no\-rcfile\fR
+don't read runtime configuration file
+.TP
\fB\-o\fR, \fB\-\-only\-matching\fR
show only the part of a line matching <pattern>
.TP
@@ -107,6 +110,18 @@ match only whole words
.TP
\fB\-x\fR, \fB\-\-line\-regexp\fR
match only whole lines
+.TP
+\fB\-\-bz2=\fR<command>
+set compressor and options for bzip2 format
+.TP
+\fB\-\-gz=\fR<command>
+set compressor and options for gzip format
+.TP
+\fB\-\-lz=\fR<command>
+set compressor and options for lzip format
+.TP
+\fB\-\-xz=\fR<command>
+set compressor and options for xz format
.SH "REPORTING BUGS"
Report bugs to zutils\-bug@nongnu.org
.br
diff --git a/doc/ztest.1 b/doc/ztest.1
index 7169021..f9c4825 100644
--- a/doc/ztest.1
+++ b/doc/ztest.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1.
-.TH ZTEST "1" "May 2013" "Ztest (zutils) 1.0" "User Commands"
+.TH ZTEST "1" "July 2013" "Ztest (zutils) 1.1-rc2" "User Commands"
.SH NAME
Ztest \- verify integrity of compressed files
.SH SYNOPSIS
@@ -7,9 +7,9 @@ Ztest \- verify integrity of compressed files
[\fIoptions\fR] [\fIfiles\fR]
.SH DESCRIPTION
Ztest verifies the integrity of the specified compressed files.
-Non\-compressed files are ignored. If no files are specified, the
-integrity of compressed data read from standard input is verified. Data
-read from standard input must be all in the same compression format.
+Uncompressed files are ignored. If no files are specified, the integrity
+of compressed data read from standard input is verified. Data read from
+standard input must be all in the same compression format.
.PP
The supported formats are bzip2, gzip, lzip and xz.
.PP
@@ -30,6 +30,9 @@ output version information and exit
\fB\-\-format=\fR<fmt>
force given format (bz2, gz, lz, xz)
.TP
+\fB\-N\fR, \fB\-\-no\-rcfile\fR
+don't read runtime configuration file
+.TP
\fB\-q\fR, \fB\-\-quiet\fR
suppress all messages
.TP
@@ -38,6 +41,18 @@ operate recursively on directories
.TP
\fB\-v\fR, \fB\-\-verbose\fR
be verbose (a 2nd \fB\-v\fR gives more)
+.TP
+\fB\-\-bz2=\fR<command>
+set compressor and options for bzip2 format
+.TP
+\fB\-\-gz=\fR<command>
+set compressor and options for gzip format
+.TP
+\fB\-\-lz=\fR<command>
+set compressor and options for lzip format
+.TP
+\fB\-\-xz=\fR<command>
+set compressor and options for xz format
.SH "REPORTING BUGS"
Report bugs to zutils\-bug@nongnu.org
.br
diff --git a/doc/zutils.info b/doc/zutils.info
index 77e2035..866d975 100644
--- a/doc/zutils.info
+++ b/doc/zutils.info
@@ -12,18 +12,20 @@ File: zutils.info, Node: Top, Next: Introduction, Up: (dir)
Zutils Manual
*************
-This manual is for Zutils (version 1.0, 31 May 2013).
+This manual is for Zutils (version 1.1-rc2, 7 July 2013).
* Menu:
* Introduction:: Purpose and features of zutils
+* Common options:: Common options
+* The zutilsrc file:: The zutils configuration file
* Zcat:: Concatenating compressed files
* Zcmp:: Comparing compressed files byte by byte
* Zdiff:: Comparing compressed files line by line
* Zgrep:: Searching inside compressed files
* Ztest:: Testing integrity of compressed files
* Problems:: Reporting bugs
-* Concept Index:: Index of concepts
+* Concept index:: Index of concepts
Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
@@ -32,14 +34,14 @@ This manual is for Zutils (version 1.0, 31 May 2013).
copy, distribute and modify it.

-File: zutils.info, Node: Introduction, Next: Zcat, Prev: Top, Up: Top
+File: zutils.info, Node: Introduction, Next: Common options, Prev: Top, Up: Top
1 Introduction
**************
Zutils is a collection of utilities able to deal with any combination of
-compressed and non-compressed files transparently. If any given file,
-including standard input, is compressed, its uncompressed content is
+compressed and uncompressed files transparently. If any given file,
+including standard input, is compressed, its decompressed content is
used. Compressed files are decompressed on the fly; no temporary files
are created.
@@ -49,6 +51,7 @@ in those utilities supporting it.
The provided utilities are zcat, zcmp, zdiff, zgrep and ztest.
The supported formats are bzip2, gzip, lzip and xz.
+The compressor to be used for each format is configurable at runtime.
Zcat, zcmp, zdiff, and zgrep are improved replacements for the shell
scripts provided with GNU gzip. Ztest is unique to zutils.
@@ -57,6 +60,10 @@ scripts provided with GNU gzip. Ztest is unique to zutils.
which makes them safe to use with zutils. Gzip and xz may return
ambiguous warning values, making them less reliable backends for zutils.
+ LANGUAGE NOTE: Uncompressed = not compressed = plain data; it may
+never have been compressed. Decompressed is used to refer to data which
+has undergone the process of decompression.
+
Numbers given as arguments to options (positions, sizes) may be
followed by a multiplier and an optional `B' for "byte".
@@ -74,13 +81,81 @@ Z zettabyte (10^21) | Zi zebibyte (2^70)
Y yottabyte (10^24) | Yi yobibyte (2^80)

-File: zutils.info, Node: Zcat, Next: Zcmp, Prev: Introduction, Up: Top
+File: zutils.info, Node: Common options, Next: The zutilsrc file, Prev: Introduction, Up: Top
+
+2 Common options
+****************
+
+The following options are available in all the utilities. Rather than
+writing identical descriptions for each of the programs, they are
+described here.
+
+`-h'
+`--help'
+ Print an informative help message describing the options and exit.
+ Zgrep only supports the `--help' form of this option.
+
+`-V'
+`--version'
+ Print the version number on the standard output and exit.
+
+`-N'
+`--no-rcfile'
+ Don't read the runtime configuration file `zutilsrc'.
+
+`--bz2=COMMAND'
+`--gz=COMMAND'
+`--lz=COMMAND'
+`--xz=COMMAND'
+ Set program (may include arguments) to be used as (de)compressor
+ for the given format. These options override the values set in
+ `zutilsrc'. The compression program used must meet three
+ requirements:
+
+ 1. When called with the `-d' option, it must read compressed
+ data from the standard input and produce decompressed data on
+ the standard output.
+
+ 2. If the `-q' option is passed to zutils, the compression
+ program must also accept it.
+
+ 3. It must return 0 if no errors occurred, and a non-zero value
+ otherwise.
-2 Zcat
+
+
+File: zutils.info, Node: The zutilsrc file, Next: Zcat, Prev: Common options, Up: Top
+
+3 The zutilsrc file
+*******************
+
+`zutilsrc' is the runtime configuration file for zutils. In it you may
+define the compressor name and options to be used for each format. The
+`zutilsrc' file is optional; you do not need to install it in order to
+run zutils.
+
+ The compressors specified in the command line override those
+specified in the `zutilsrc' file.
+
+ You may copy the system `zutilsrc' file `${sysconfdir}/zutilsrc' to
+`$HOME/.zutilsrc' and customize these options as you like. The file
+syntax is fairly obvious (and there are further instructions in it):
+
+ 1. Any line beginning with `#' is a comment line.
+
+ 2. Each non-comment line defines the command to be used for the given
+ format, with the syntax:
+ <format> = <compressor> [options]
+ where <format> is one of `bz2', `gz', `lz' or `xz'.
+
+
+File: zutils.info, Node: Zcat, Next: Zcmp, Prev: The zutilsrc file, Up: Top
+
+4 Zcat
******
Zcat copies each given file (`-' means standard input), to standard
-output. If any given file is compressed, its uncompressed content is
+output. If any given file is compressed, its decompressed content is
used. If a given file does not exist, and its name does not end with one
of the known extensions, zcat tries the compressed file names
corresponding to the supported formats.
@@ -94,18 +169,10 @@ same compression format.
zcat [OPTIONS] [FILES]
-Exit status is 0 if no errors occurred, 1 otherwise.
+Exit status is 0 if no errors occurred, non-zero otherwise.
Zcat supports the following options:
-`-h'
-`--help'
- Print an informative help message describing the options and exit.
-
-`-V'
-`--version'
- Print the version number of zcat on the standard output and exit.
-
`-A'
`--show-all'
Equivalent to `-vET'.
@@ -164,13 +231,13 @@ Exit status is 0 if no errors occurred, 1 otherwise.

File: zutils.info, Node: Zcmp, Next: Zdiff, Prev: Zcat, Up: Top
-3 Zcmp
+5 Zcmp
******
Zcmp compares two files (`-' means standard input), and if they differ,
tells the first byte and line number where they differ. Bytes and lines
are numbered starting with 1. If any given file is compressed, its
-uncompressed content is used. Compressed files are decompressed on the
+decompressed content is used. Compressed files are decompressed on the
fly; no temporary files are created.
The format for running zcmp is:
@@ -180,14 +247,14 @@ fly; no temporary files are created.
This compares FILE1 to FILE2. If FILE2 is omitted zcmp tries the
following:
- 1. If FILE1 is compressed, compares FILE1 to the file with the
- corresponding decompressed file name (removes the extension from
- FILE1).
+ 1. If FILE1 is compressed, compares its decompressed contents with
+ the corresponding uncompressed file (the name of FILE1 with the
+ extension removed).
- 2. If FILE1 is not compressed, compares FILE1 to the uncompressed
+ 2. If FILE1 is uncompressed, compares it with the decompressed
contents of FILE1.[lz|bz2|gz|xz] (the first one that is found).
- 3. If no suitable file is found, compares FILE1 to data read from
+ 3. If no suitable file is found, compares FILE1 with data read from
standard input.
An exit status of 0 means no differences were found, 1 means some
@@ -195,14 +262,6 @@ differences were found, and 2 means trouble.
Zcmp supports the following options:
-`-h'
-`--help'
- Print an informative help message describing the options and exit.
-
-`-V'
-`--version'
- Print the version number of zcmp on the standard output and exit.
-
`-b'
`--print-bytes'
Print the differing bytes. Print control bytes as a `^' followed by
@@ -247,12 +306,12 @@ differences were found, and 2 means trouble.

File: zutils.info, Node: Zdiff, Next: Zgrep, Prev: Zcmp, Up: Top
-4 Zdiff
+6 Zdiff
*******
Zdiff compares two files (`-' means standard input), and if they
differ, shows the differences line by line. If any given file is
-compressed, its uncompressed content is used. Zdiff is a front end to
+compressed, its decompressed content is used. Zdiff is a front end to
the diff program and has the limitation that messages from diff refer to
temporary filenames instead of those specified.
@@ -263,14 +322,14 @@ temporary filenames instead of those specified.
This compares FILE1 to FILE2. If FILE2 is omitted zdiff tries the
following:
- 1. If FILE1 is compressed, compares FILE1 to the file with the
- corresponding decompressed file name (removes the extension from
- FILE1).
+ 1. If FILE1 is compressed, compares its decompressed contents with
+ the corresponding uncompressed file (the name of FILE1 with the
+ extension removed).
- 2. If FILE1 is not compressed, compares FILE1 to the uncompressed
+ 2. If FILE1 is uncompressed, compares it with the decompressed
contents of FILE1.[lz|bz2|gz|xz] (the first one that is found).
- 3. If no suitable file is found, compares FILE1 to data read from
+ 3. If no suitable file is found, compares FILE1 with data read from
standard input.
An exit status of 0 means no differences were found, 1 means some
@@ -278,14 +337,6 @@ differences were found, and 2 means trouble.
Zdiff supports the following options:
-`-h'
-`--help'
- Print an informative help message describing the options and exit.
-
-`-V'
-`--version'
- Print the version number of zdiff on the standard output and exit.
-
`-a'
`--text'
Treat all files as text.
@@ -360,12 +411,12 @@ differences were found, and 2 means trouble.

File: zutils.info, Node: Zgrep, Next: Ztest, Prev: Zdiff, Up: Top
-5 Zgrep
+7 Zgrep
*******
Zgrep is a front end to the grep program that allows transparent search
-on any combination of compressed and non-compressed files. If any given
-file is compressed, its uncompressed content is used. If a given file
+on any combination of compressed and uncompressed files. If any given
+file is compressed, its decompressed content is used. If a given file
does not exist, and its name does not end with one of the known
extensions, zgrep tries the compressed file names corresponding to the
supported formats.
@@ -384,13 +435,6 @@ matches were found, and 2 means trouble.
Zgrep supports the following options:
-`--help'
- Print an informative help message describing the options and exit.
-
-`-V'
-`--version'
- Print the version number of zgrep on the standard output and exit.
-
`-a'
`--text'
Treat all files as text.
@@ -503,13 +547,13 @@ matches were found, and 2 means trouble.

File: zutils.info, Node: Ztest, Next: Problems, Prev: Zgrep, Up: Top
-6 Ztest
+8 Ztest
*******
Ztest verifies the integrity of the specified compressed files.
-Non-compressed files are ignored. If no files are specified, the
-integrity of compressed data read from standard input is verified. Data
-read from standard input must be all in the same compression format.
+Uncompressed files are ignored. If no files are specified, the integrity
+of compressed data read from standard input is verified. Data read from
+standard input must be all in the same compression format.
Note that some xz files lack integrity information, and therefore
can't be verified as reliably as the other formats can.
@@ -524,14 +568,6 @@ environmental problems (file not found, invalid flags, I/O errors, etc),
Ztest supports the following options:
-`-h'
-`--help'
- Print an informative help message describing the options and exit.
-
-`-V'
-`--version'
- Print the version number of ztest on the standard output and exit.
-
`--format=FMT'
Force the given compression format. Valid values for FMT are
`bz2', `gz', `lz' and `xz'. If this option is used, all files not
@@ -552,9 +588,9 @@ environmental problems (file not found, invalid flags, I/O errors, etc),

-File: zutils.info, Node: Problems, Next: Concept Index, Prev: Ztest, Up: Top
+File: zutils.info, Node: Problems, Next: Concept index, Prev: Ztest, Up: Top
-7 Reporting Bugs
+9 Reporting Bugs
****************
There are probably bugs in zutils. There are certainly errors and
@@ -567,17 +603,19 @@ for all eternity, if not longer.
by running `zutils --version'.

-File: zutils.info, Node: Concept Index, Prev: Problems, Up: Top
+File: zutils.info, Node: Concept index, Prev: Problems, Up: Top
-Concept Index
+Concept index
*************
* Menu:
* bugs: Problems. (line 6)
+* common options: Common options. (line 6)
* getting help: Problems. (line 6)
* introduction: Introduction. (line 6)
+* the zutilsrc file: The zutilsrc file. (line 6)
* zcat: Zcat. (line 6)
* zcmp: Zcmp. (line 6)
* zdiff: Zdiff. (line 6)
@@ -588,14 +626,16 @@ Concept Index

Tag Table:
Node: Top224
-Node: Introduction1000
-Node: Zcat2794
-Node: Zcmp4880
-Node: Zdiff7366
-Node: Zgrep10032
-Node: Ztest13043
-Node: Problems14447
-Node: Concept Index14976
+Node: Introduction1097
+Node: Common options3155
+Node: The zutilsrc file4404
+Node: Zcat5330
+Node: Zcmp7252
+Node: Zdiff9576
+Node: Zgrep12079
+Node: Ztest14916
+Node: Problems16141
+Node: Concept index16670

End Tag Table
diff --git a/doc/zutils.texinfo b/doc/zutils.texinfo
index ed65997..61a8762 100644
--- a/doc/zutils.texinfo
+++ b/doc/zutils.texinfo
@@ -6,8 +6,8 @@
@finalout
@c %**end of header
-@set UPDATED 31 May 2013
-@set VERSION 1.0
+@set UPDATED 7 July 2013
+@set VERSION 1.1-rc2
@dircategory Data Compression
@direntry
@@ -36,13 +36,15 @@ This manual is for Zutils (version @value{VERSION}, @value{UPDATED}).
@menu
* Introduction:: Purpose and features of zutils
+* Common options:: Common options
+* The zutilsrc file:: The zutils configuration file
* Zcat:: Concatenating compressed files
* Zcmp:: Comparing compressed files byte by byte
* Zdiff:: Comparing compressed files line by line
* Zgrep:: Searching inside compressed files
* Ztest:: Testing integrity of compressed files
* Problems:: Reporting bugs
-* Concept Index:: Index of concepts
+* Concept index:: Index of concepts
@end menu
@sp 1
@@ -58,8 +60,8 @@ to copy, distribute and modify it.
@cindex introduction
Zutils is a collection of utilities able to deal with any combination of
-compressed and non-compressed files transparently. If any given file,
-including standard input, is compressed, its uncompressed content is
+compressed and uncompressed files transparently. If any given file,
+including standard input, is compressed, its decompressed content is
used. Compressed files are decompressed on the fly; no temporary files
are created.
@@ -69,7 +71,8 @@ in those utilities supporting it.
@noindent
The provided utilities are zcat, zcmp, zdiff, zgrep and ztest.@*
-The supported formats are bzip2, gzip, lzip and xz.
+The supported formats are bzip2, gzip, lzip and xz.@*
+The compressor to be used for each format is configurable at runtime.
Zcat, zcmp, zdiff, and zgrep are improved replacements for the shell
scripts provided with GNU gzip. Ztest is unique to zutils.
@@ -78,6 +81,10 @@ NOTE: Bzip2 and lzip provide well-defined values of exit status, which
makes them safe to use with zutils. Gzip and xz may return ambiguous
warning values, making them less reliable backends for zutils.
+LANGUAGE NOTE: Uncompressed = not compressed = plain data; it may never
+have been compressed. Decompressed is used to refer to data which has
+undergone the process of decompression.
+
@sp 1
Numbers given as arguments to options (positions, sizes) may be followed
by a multiplier and an optional @samp{B} for "byte".
@@ -97,12 +104,85 @@ Table of SI and binary prefixes (unit multipliers):
@end multitable
+@node Common options
+@chapter Common options
+@cindex common options
+
+The following options are available in all the utilities. Rather than
+writing identical descriptions for each of the programs, they are
+described here.
+
+@table @samp
+@item -h
+@itemx --help
+Print an informative help message describing the options and exit. Zgrep
+only supports the @samp{--help} form of this option.
+
+@item -V
+@itemx --version
+Print the version number on the standard output and exit.
+
+@item -N
+@itemx --no-rcfile
+Don't read the runtime configuration file @samp{zutilsrc}.
+
+@item --bz2=@var{command}
+@itemx --gz=@var{command}
+@itemx --lz=@var{command}
+@itemx --xz=@var{command}
+Set program (may include arguments) to be used as (de)compressor for the
+given format. These options override the values set in @file{zutilsrc}.
+The compression program used must meet three requirements:
+
+@enumerate
+@item
+When called with the @samp{-d} option, it must read compressed data from
+the standard input and produce decompressed data on the standard output.
+@item
+If the @samp{-q} option is passed to zutils, the compression program
+must also accept it.
+@item
+It must return 0 if no errors occurred, and a non-zero value otherwise.
+@end enumerate
+
+@end table
+
+
+@node The zutilsrc file
+@chapter The zutilsrc file
+@cindex the zutilsrc file
+
+@file{zutilsrc} is the runtime configuration file for zutils. In it you
+may define the compressor name and options to be used for each format.
+The @file{zutilsrc} file is optional; you do not need to install it in
+order to run zutils.
+
+The compressors specified in the command line override those specified
+in the @file{zutilsrc} file.
+
+You may copy the system @file{zutilsrc} file
+@file{$@{sysconfdir@}/zutilsrc} to @file{$HOME/.zutilsrc} and customize
+these options as you like. The file syntax is fairly obvious (and there
+are further instructions in it):
+
+@enumerate
+@item
+Any line beginning with @samp{#} is a comment line.
+@item
+Each non-comment line defines the command to be used for the given
+format, with the syntax:
+@example
+<format> = <compressor> [options]
+@end example
+where <format> is one of @samp{bz2}, @samp{gz}, @samp{lz} or @samp{xz}.
+@end enumerate
+
@node Zcat
@chapter Zcat
@cindex zcat
Zcat copies each given file (@samp{-} means standard input), to standard
-output. If any given file is compressed, its uncompressed content is
+output. If any given file is compressed, its decompressed content is
used. If a given file does not exist, and its name does not end with one
of the known extensions, zcat tries the compressed file names
corresponding to the supported formats.
@@ -119,19 +199,11 @@ zcat [@var{options}] [@var{files}]
@end example
@noindent
-Exit status is 0 if no errors occurred, 1 otherwise.
+Exit status is 0 if no errors occurred, non-zero otherwise.
Zcat supports the following options:
@table @samp
-@item -h
-@itemx --help
-Print an informative help message describing the options and exit.
-
-@item -V
-@itemx --version
-Print the version number of zcat on the standard output and exit.
-
@item -A
@itemx --show-all
Equivalent to @samp{-vET}.
@@ -195,7 +267,7 @@ Verbose mode. Show error messages.
Zcmp compares two files (@samp{-} means standard input), and if they
differ, tells the first byte and line number where they differ. Bytes
and lines are numbered starting with 1. If any given file is compressed,
-its uncompressed content is used. Compressed files are decompressed on
+its decompressed content is used. Compressed files are decompressed on
the fly; no temporary files are created.
The format for running zcmp is:
@@ -210,15 +282,14 @@ tries the following:
@enumerate
@item
-If @var{file1} is compressed, compares @var{file1} to the file with the
-corresponding decompressed file name (removes the extension from
-@var{file1}).
+If @var{file1} is compressed, compares its decompressed contents with
+the corresponding uncompressed file (the name of @var{file1} with the
+extension removed).
@item
-If @var{file1} is not compressed, compares @var{file1} to the
-uncompressed contents of @var{file1}.[lz|bz2|gz|xz] (the first one that
-is found).
+If @var{file1} is uncompressed, compares it with the decompressed
+contents of @var{file1}.[lz|bz2|gz|xz] (the first one that is found).
@item
-If no suitable file is found, compares @var{file1} to data read from
+If no suitable file is found, compares @var{file1} with data read from
standard input.
@end enumerate
@@ -229,14 +300,6 @@ differences were found, and 2 means trouble.
Zcmp supports the following options:
@table @samp
-@item -h
-@itemx --help
-Print an informative help message describing the options and exit.
-
-@item -V
-@itemx --version
-Print the version number of zcmp on the standard output and exit.
-
@item -b
@itemx --print-bytes
Print the differing bytes. Print control bytes as a @samp{^} followed by
@@ -286,7 +349,7 @@ files differ.
Zdiff compares two files (@samp{-} means standard input), and if they
differ, shows the differences line by line. If any given file is
-compressed, its uncompressed content is used. Zdiff is a front end to
+compressed, its decompressed content is used. Zdiff is a front end to
the diff program and has the limitation that messages from diff refer to
temporary filenames instead of those specified.
@@ -302,15 +365,14 @@ zdiff tries the following:
@enumerate
@item
-If @var{file1} is compressed, compares @var{file1} to the file with the
-corresponding decompressed file name (removes the extension from
-@var{file1}).
+If @var{file1} is compressed, compares its decompressed contents with
+the corresponding uncompressed file (the name of @var{file1} with the
+extension removed).
@item
-If @var{file1} is not compressed, compares @var{file1} to the
-uncompressed contents of @var{file1}.[lz|bz2|gz|xz] (the first one that
-is found).
+If @var{file1} is uncompressed, compares it with the decompressed
+contents of @var{file1}.[lz|bz2|gz|xz] (the first one that is found).
@item
-If no suitable file is found, compares @var{file1} to data read from
+If no suitable file is found, compares @var{file1} with data read from
standard input.
@end enumerate
@@ -321,14 +383,6 @@ differences were found, and 2 means trouble.
Zdiff supports the following options:
@table @samp
-@item -h
-@itemx --help
-Print an informative help message describing the options and exit.
-
-@item -V
-@itemx --version
-Print the version number of zdiff on the standard output and exit.
-
@item -a
@itemx --text
Treat all files as text.
@@ -407,8 +461,8 @@ Ignore all white space.
@cindex zgrep
Zgrep is a front end to the grep program that allows transparent search
-on any combination of compressed and non-compressed files. If any given
-file is compressed, its uncompressed content is used. If a given file
+on any combination of compressed and uncompressed files. If any given
+file is compressed, its decompressed content is used. If a given file
does not exist, and its name does not end with one of the known
extensions, zgrep tries the compressed file names corresponding to the
supported formats.
@@ -431,13 +485,6 @@ matches were found, and 2 means trouble.
Zgrep supports the following options:
@table @samp
-@item --help
-Print an informative help message describing the options and exit.
-
-@item -V
-@itemx --version
-Print the version number of zgrep on the standard output and exit.
-
@item -a
@itemx --text
Treat all files as text.
@@ -554,9 +601,9 @@ Match only whole lines.
@cindex ztest
Ztest verifies the integrity of the specified compressed files.
-Non-compressed files are ignored. If no files are specified, the
-integrity of compressed data read from standard input is verified. Data
-read from standard input must be all in the same compression format.
+Uncompressed files are ignored. If no files are specified, the integrity
+of compressed data read from standard input is verified. Data read from
+standard input must be all in the same compression format.
Note that some xz files lack integrity information, and therefore can't
be verified as reliably as the other formats can.
@@ -575,14 +622,6 @@ environmental problems (file not found, invalid flags, I/O errors, etc),
Ztest supports the following options:
@table @samp
-@item -h
-@itemx --help
-Print an informative help message describing the options and exit.
-
-@item -V
-@itemx --version
-Print the version number of ztest on the standard output and exit.
-
@item --format=@var{fmt}
Force the given compression format. Valid values for @var{fmt} are
@samp{bz2}, @samp{gz}, @samp{lz} and @samp{xz}. If this option is used,
@@ -619,8 +658,8 @@ If you find a bug in zutils, please send electronic mail to
find by running @w{@samp{zutils --version}}.
-@node Concept Index
-@unnumbered Concept Index
+@node Concept index
+@unnumbered Concept index
@printindex cp
diff --git a/main.cc b/main.cc
index f0a66e1..672d159 100644
--- a/main.cc
+++ b/main.cc
@@ -37,6 +37,7 @@
#include "arg_parser.h"
#include "zutils.h"
+#include "rc.h"
#if CHAR_BIT != 8
#error "Environments where CHAR_BIT != 8 are not supported."
@@ -57,8 +58,8 @@ enum Mode { m_none, m_zcat, m_zgrep, m_ztest };
void show_help()
{
std::printf( "Zutils is a collection of utilities able to deal with any combination of\n"
- "compressed and non-compressed files transparently. If any given file,\n"
- "including standard input, is compressed, its uncompressed content is used.\n"
+ "compressed and uncompressed files transparently. If any given file,\n"
+ "including standard input, is compressed, its decompressed content is used.\n"
"\nThe supported formats are bzip2, gzip, lzip and xz.\n"
"\nUsage: %s <operation> [options] [files]\n", invocation_name );
std::printf( "\nTry '%s <operation> --help' for more specific help.\n", invocation_name );
@@ -118,10 +119,11 @@ int open_instream( std::string & input_filename, const Mode program_mode,
int main( const int argc, const char * const argv[] )
{
enum { format_opt = 256, help_opt, verbose_opt,
+ bz2_opt, gz_opt, lz_opt, xz_opt,
zcat_opt, zgrep_opt, ztest_opt };
const Arg_parser::Option * options = 0;
int infd = -1;
- int format_type = -1;
+ int format_index = -1;
Mode program_mode = m_none;
bool recursive = false;
std::string input_filename;
@@ -144,6 +146,7 @@ int main( const int argc, const char * const argv[] )
{ 'l', "list", Arg_parser::no }, // gzip
{ 'L', "license", Arg_parser::no }, // gzip
{ 'n', "number", Arg_parser::no }, // cat
+ { 'N', "no-rcfile", Arg_parser::no },
{ 'q', "quiet", Arg_parser::no },
{ 'r', "recursive", Arg_parser::no },
{ 's', "squeeze-blank", Arg_parser::no }, // cat
@@ -153,6 +156,10 @@ int main( const int argc, const char * const argv[] )
{ 'V', "version", Arg_parser::no },
{ format_opt, "format", Arg_parser::yes },
{ verbose_opt, "verbose", Arg_parser::no },
+ { bz2_opt, "bz2", Arg_parser::yes },
+ { gz_opt, "gz", Arg_parser::yes },
+ { lz_opt, "lz", Arg_parser::yes },
+ { xz_opt, "xz", Arg_parser::yes },
{ zcat_opt, "zcat", Arg_parser::no },
{ 0 , 0, Arg_parser::no } };
@@ -176,6 +183,7 @@ int main( const int argc, const char * const argv[] )
{ 'L', "files-without-match", Arg_parser::no }, // grep GNU
{ 'm', "max-count", Arg_parser::yes }, // grep GNU
{ 'n', "line-number", Arg_parser::no }, // grep
+ { 'N', "no-rcfile", Arg_parser::no },
{ 'o', "only-matching", Arg_parser::no }, // grep
{ 'q', "quiet", Arg_parser::no },
{ 'r', "recursive", Arg_parser::no },
@@ -187,17 +195,26 @@ int main( const int argc, const char * const argv[] )
{ format_opt, "format", Arg_parser::yes },
{ help_opt, "help", Arg_parser::no },
{ verbose_opt, "verbose", Arg_parser::no },
+ { bz2_opt, "bz2", Arg_parser::yes },
+ { gz_opt, "gz", Arg_parser::yes },
+ { lz_opt, "lz", Arg_parser::yes },
+ { xz_opt, "xz", Arg_parser::yes },
{ zgrep_opt, "zgrep", Arg_parser::no },
{ 0 , 0, Arg_parser::no } };
const Arg_parser::Option m_ztest_options[] =
{
{ 'h', "help", Arg_parser::no },
+ { 'N', "no-rcfile", Arg_parser::no },
{ 'q', "quiet", Arg_parser::no },
{ 'r', "recursive", Arg_parser::no },
{ 'v', "verbose", Arg_parser::no },
{ 'V', "version", Arg_parser::no },
{ format_opt, "format", Arg_parser::yes },
+ { bz2_opt, "bz2", Arg_parser::yes },
+ { gz_opt, "gz", Arg_parser::yes },
+ { lz_opt, "lz", Arg_parser::yes },
+ { xz_opt, "xz", Arg_parser::yes },
{ ztest_opt, "ztest", Arg_parser::no },
{ 0 , 0, Arg_parser::no } };
@@ -244,10 +261,12 @@ int main( const int argc, const char * const argv[] )
}
} // end parse operation
+ const int eretval = ( program_mode == m_zgrep ) ? 2 : 1;
const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option
- { show_error( parser.error().c_str(), 0, true );
- return ( program_mode == m_zcat || program_mode == m_ztest ) ? 1 : 2; }
+ { show_error( parser.error().c_str(), 0, true ); return eretval; }
+
+ maybe_process_config_file( parser );
int argind = 0;
int grep_show_name = -1;
@@ -263,9 +282,14 @@ int main( const int argc, const char * const argv[] )
{ grep_args.push_back( arg ); grep_pattern_found = true; continue; }
else break; // no more options
}
- if( code == format_opt )
+ switch( code ) // common options
{
- format_type = get_format_type( arg ); continue;
+ case 'N': continue;
+ case format_opt: format_index = parse_format_type( arg ); continue;
+ case bz2_opt: parse_compressor( arg, fmt_bz2, eretval ); continue;
+ case gz_opt: parse_compressor( arg, fmt_gz, eretval ); continue;
+ case lz_opt: parse_compressor( arg, fmt_lz, eretval ); continue;
+ case xz_opt: parse_compressor( arg, fmt_xz, eretval ); continue;
}
switch( program_mode )
{
@@ -396,7 +420,7 @@ int main( const int argc, const char * const argv[] )
continue;
}
}
- infd = open_instream( input_filename, program_mode, format_type < 0 );
+ infd = open_instream( input_filename, program_mode, format_index < 0 );
if( infd < 0 ) { if( retval < 1 ) retval = 1; continue; }
}
@@ -406,18 +430,18 @@ int main( const int argc, const char * const argv[] )
case m_none:
break;
case m_zcat:
- tmp = cat( infd, format_type, input_filename, cat_options );
+ tmp = cat( infd, format_index, input_filename, cat_options );
break;
case m_zgrep:
if( infd == STDIN_FILENO )
- tmp = zgrep_stdin( infd, format_type, grep_args );
- else tmp = zgrep_file( infd, format_type, input_filename, grep_args,
+ tmp = zgrep_stdin( infd, format_index, grep_args );
+ else tmp = zgrep_file( infd, format_index, input_filename, grep_args,
grep_list_mode, grep_show_name );
break;
case m_ztest:
if( infd == STDIN_FILENO )
- tmp = ztest_stdin( infd, format_type, ztest_args );
- else tmp = ztest_file( infd, format_type, input_filename, ztest_args );
+ tmp = ztest_stdin( infd, format_index, ztest_args );
+ else tmp = ztest_file( infd, format_index, input_filename, ztest_args );
break;
}
if( program_mode == m_zgrep )
diff --git a/rc.cc b/rc.cc
new file mode 100644
index 0000000..dac205c
--- /dev/null
+++ b/rc.cc
@@ -0,0 +1,229 @@
+/* Zutils - Utilities dealing with compressed files
+ Copyright (C) 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define _FILE_OFFSET_BITS 64
+
+#include <cstdio>
+#include <cstdlib>
+#include <string>
+#include <vector>
+#include <stdint.h>
+
+#include "arg_parser.h"
+#include "zutils.h"
+#include "rc.h"
+
+
+namespace {
+
+std::string compressor_names[num_formats] =
+ { "bzip2", "gzip", "lzip", "xz" }; // default compressor names
+
+// args to compressors, maybe empty
+std::vector< std::string > compressor_args[num_formats];
+
+
+int my_fgetc( FILE * const f )
+ {
+ int ch;
+ bool comment = false;
+
+ do {
+ ch = std::fgetc( f );
+ if( ch == '#' ) comment = true;
+ else if( ch == '\n' || ch == EOF ) comment = false;
+ else if( ch == '\\' && comment )
+ {
+ const int c = std::fgetc( f );
+ if( c == '\n' ) { std::ungetc( c, f ); comment = false; }
+ }
+ }
+ while( comment );
+ return ch;
+ }
+
+
+// Returns the parity of escapes (backslashes) at the end of a string.
+bool trailing_escape( const std::string & s )
+ {
+ unsigned len = s.size();
+ bool odd_escape = false;
+ while( len > 0 && s[--len] == '\\' ) odd_escape = !odd_escape;
+ return odd_escape;
+ }
+
+
+// Read a line discarding comments, leading whitespace and blank lines.
+// Escaped newlines are discarded.
+// Returns the empty string if at EOF.
+//
+const std::string & my_fgets( FILE * const f, int & linenum )
+ {
+ static std::string s;
+ bool strip = true; // strip leading whitespace
+ s.clear();
+
+ while( true )
+ {
+ int ch = my_fgetc( f );
+ if( strip )
+ {
+ strip = false;
+ while( std::isspace( ch ) )
+ { if( ch == '\n' ) { ++linenum; } ch = my_fgetc( f ); }
+ }
+ if( ch == EOF ) { if( s.size() > 0 ) { ++linenum; } break; }
+ else if( ch == '\n' )
+ {
+ ++linenum; strip = true;
+ if( trailing_escape( s ) ) s.erase( s.size() - 1 );
+ else if( s.size() > 0 ) break;
+ }
+ else s += ch;
+ }
+ return s;
+ }
+
+
+bool parse_rc_line( const std::string & line,
+ const char * const filename, const int linenum )
+ {
+ const int len = line.size();
+ int i = 0;
+ while( i < len && std::isspace( line[i] ) ) ++i; // strip spaces
+ int l = i;
+ while( i < len && line[i] != '=' && !std::isspace( line[i] ) ) ++i;
+ if( l >= i )
+ { if( verbosity >= 0 )
+ std::fprintf( stderr, "%s %d: missing format name.\n", filename, linenum );
+ return false; }
+ const std::string name( line, l, i - l );
+ int format_index = -1;
+ for( int i = 0; i < num_formats; ++i )
+ if( name == format_names[i] ) { format_index = i; break; }
+ if( format_index < 0 )
+ { if( verbosity >= 0 )
+ std::fprintf( stderr, "%s %d: bad format name '%s'\n",
+ filename, linenum, name.c_str() );
+ return false; }
+
+ while( i < len && std::isspace( line[i] ) ) ++i; // strip spaces
+ if( i <= 0 || i >= len || line[i] != '=' )
+ { if( verbosity >= 0 )
+ std::fprintf( stderr, "%s %d: missing '='.\n", filename, linenum );
+ return false; }
+ ++i; // skip the '='
+ while( i < len && std::isspace( line[i] ) ) ++i; // strip spaces
+ l = i;
+ while( i < len && !std::isspace( line[i] ) ) ++i;
+ if( l >= i )
+ { if( verbosity >= 0 )
+ std::fprintf( stderr, "%s %d: missing compressor name.\n", filename, linenum );
+ return false; }
+ compressor_names[format_index].assign( line, l, i - l );
+
+ compressor_args[format_index].clear();
+ while( i < len )
+ {
+ while( i < len && std::isspace( line[i] ) ) ++i; // strip spaces
+ l = i;
+ while( i < len && !std::isspace( line[i] ) ) ++i;
+ if( l < i )
+ compressor_args[format_index].push_back( std::string( line, l, i - l ) );
+ }
+ return true;
+ }
+
+
+ // Returns 0 for success, 1 for file not found, 2 for syntax error.
+int process_rcfile( const std::string & name )
+ {
+ FILE * const f = std::fopen( name.c_str(), "r" );
+ if( !f ) return 1;
+
+ int linenum = 0;
+ int retval = 0;
+
+ while( true )
+ {
+ const std::string & line = my_fgets( f, linenum );
+ if( line.size() == 0 ) break; // EOF
+ if( !parse_rc_line( line, name.c_str(), linenum ) )
+ { retval = 2; break; }
+ }
+ std::fclose( f );
+ return retval;
+ }
+
+} // end namespace
+
+
+void maybe_process_config_file( const Arg_parser & parser )
+ {
+ for( int i = 0; i < parser.arguments(); ++i )
+ if( parser.code( i ) == 'N' ) return;
+ std::string name;
+ const char * p = std::getenv( "HOME" ); if( p ) name = p;
+ if( name.size() )
+ {
+ name += "/."; name += config_file_name;
+ const int retval = process_rcfile( name );
+ if( retval == 0 ) return;
+ if( retval == 2 ) std::exit( 2 );
+ }
+ name = SYSCONFDIR; name += '/'; name += config_file_name;
+ const int retval = process_rcfile( name );
+ if( retval == 2 ) std::exit( 2 );
+ }
+
+
+void parse_compressor( const std::string & arg, const int format_index,
+ const int eretval )
+ {
+ const int len = arg.size();
+ int i = 0;
+ while( i < len && std::isspace( arg[i] ) ) ++i; // strip spaces
+ int l = i;
+ while( i < len && !std::isspace( arg[i] ) ) ++i;
+ if( l >= i )
+ { show_error( "Missing compressor name." ); std::exit( eretval ); }
+ compressor_names[format_index].assign( arg, l, i - l );
+
+ compressor_args[format_index].clear();
+ while( i < len )
+ {
+ while( i < len && std::isspace( arg[i] ) ) ++i; // strip spaces
+ l = i;
+ while( i < len && !std::isspace( arg[i] ) ) ++i;
+ if( l < i )
+ compressor_args[format_index].push_back( std::string( arg, l, i - l ) );
+ }
+ }
+
+
+const char * get_compressor_name( const int format_index )
+ {
+ if( format_index >= 0 && format_index < num_formats )
+ return compressor_names[format_index].c_str();
+ return 0;
+ }
+
+
+const std::vector< std::string > & get_compressor_args( const int format_index )
+ {
+ return compressor_args[format_index];
+ }
diff --git a/rc.h b/rc.h
new file mode 100644
index 0000000..2f65800
--- /dev/null
+++ b/rc.h
@@ -0,0 +1,27 @@
+/* Zutils - Utilities dealing with compressed files
+ Copyright (C) 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+class Arg_parser;
+
+void maybe_process_config_file( const Arg_parser & parser );
+
+void parse_compressor( const std::string & arg, const int format_index,
+ const int eretval = 2 );
+
+const char * get_compressor_name( const int format_index );
+
+const std::vector< std::string > & get_compressor_args( const int format_index );
diff --git a/testsuite/check.sh b/testsuite/check.sh
index 2faa2b7..fe8dbff 100755
--- a/testsuite/check.sh
+++ b/testsuite/check.sh
@@ -50,280 +50,293 @@ printf "testing zutils-%s..." "$2"
printf "\ntesting zcat-%s..." "$2"
for i in ${extensions}; do
- "${ZCAT}" in.$i > copy || fail=1
+ "${ZCAT}" -N in.$i > copy || fail=1
cmp in copy || fail=1
printf .
- "${ZCAT}" --format=$i in.$i > copy || fail=1
+ "${ZCAT}" -N --format=$i in.$i > copy || fail=1
cmp in copy || fail=1
printf .
- "${ZCAT}" in.$i | dd bs=1000 count=1 > copy 2> /dev/null || fail=1
+ "${ZCAT}" -N in.$i | dd bs=1000 count=1 > copy 2> /dev/null || fail=1
dd if=in bs=1000 count=1 2> /dev/null | cmp - copy || fail=1
printf .
done
-"${ZCAT}" < in > copy || fail=1
+"${ZCAT}" -N < in > copy || fail=1
cmp in copy || fail=1
printf .
-"${ZCAT}" < in.gz > copy || fail=1
+"${ZCAT}" -N < in.gz > copy || fail=1
cmp in copy || fail=1
printf .
-"${ZCAT}" < in.bz2 > copy || fail=1
+"${ZCAT}" -N < in.bz2 > copy || fail=1
cmp in copy || fail=1
printf .
-"${ZCAT}" < in.lz > copy || fail=1
+"${ZCAT}" -N < in.lz > copy || fail=1
cmp in copy || fail=1
printf .
-"${ZCAT}" in > copy || fail=1
+"${ZCAT}" -N --lz='lzip -q' < in.lz > copy || fail=1
cmp in copy || fail=1
printf .
-"${ZCAT}" lz_only > copy || fail=1
+"${ZCAT}" -N in > copy || fail=1
cmp in copy || fail=1
printf .
-"${ZCAT}" in in.gz in.bz2 in.lz -- -in- -in-.lz > copy6 || fail=1
+"${ZCAT}" -N lz_only > copy || fail=1
+cmp in copy || fail=1
+printf .
+"${ZCAT}" -N in in.gz in.bz2 in.lz -- -in- -in-.lz > copy6 || fail=1
cmp in6 copy6 || fail=1
printf .
-"${ZCAT}" --format=gz in.bz2 2> /dev/null
+"${ZCAT}" -N --format=gz in.bz2 2> /dev/null
if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZCAT}" --format=bz2 in.lz 2> /dev/null
+"${ZCAT}" -N --format=bz2 in.lz 2> /dev/null
if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZCAT}" --format=lz in.gz 2> /dev/null
+"${ZCAT}" -N --format=lz in.gz 2> /dev/null
if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZCAT}" --bad-option 2> /dev/null
+"${ZCAT}" -N --bad-option 2> /dev/null
if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
printf "\ntesting zcmp-%s..." "$2"
for i in ${extensions}; do
- "${ZCMP}" in.$i || fail=1
+ "${ZCMP}" -N in.$i || fail=1
printf .
- "${ZCMP}" in in.$i || fail=1
+ "${ZCMP}" -N in in.$i || fail=1
printf .
- "${ZCMP}" -i 100 -n 500 in6 in.$i || fail=1
+ "${ZCMP}" -N -i 100 -n 500 in6 in.$i || fail=1
printf .
- "${ZCMP}" in in.$i --format=,$i || fail=1
+ "${ZCMP}" -N in in.$i --format=,$i || fail=1
printf .
- "${ZCMP}" in.$i in || fail=1
+ "${ZCMP}" -N in.$i in || fail=1
printf .
- "${ZCMP}" -i 1000:1000 -n 50 in.$i in6 || fail=1
+ "${ZCMP}" -N -i 1000:1000 -n 50 in.$i in6 || fail=1
printf .
- "${ZCMP}" in.$i in --format=$i || fail=1
+ "${ZCMP}" -N in.$i in --format=$i || fail=1
printf .
done
-"${ZCMP}" in in6 2> /dev/null
+"${ZCMP}" -N in in6 2> /dev/null
if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZCMP}" -n 0 in in6 || fail=1
-"${ZCMP}" -n 100 in in6 || fail=1
-"${ZCMP}" -n 1000 in in6 || fail=1
-"${ZCMP}" -n 10000 in in6 || fail=1
+"${ZCMP}" -N -n 0 in in6 || fail=1
+"${ZCMP}" -N -n 100 in in6 || fail=1
+"${ZCMP}" -N -n 1000 in in6 || fail=1
+"${ZCMP}" -N -n 10000 in in6 || fail=1
printf .
-"${ZCMP}" in.tar pin.tar > /dev/null
+"${ZCMP}" -N in.tar pin.tar > /dev/null
if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZCMP}" -i 0,11 in.tar pin.tar 2> /dev/null
+"${ZCMP}" -N -i 0,11 in.tar pin.tar 2> /dev/null
if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZCMP}" -i 0,11 -n 0 in.tar pin.tar || fail=1
-"${ZCMP}" -i 0,11 -n 100 in.tar pin.tar || fail=1
-"${ZCMP}" -i 0,11 -n 1000 in.tar pin.tar || fail=1
-"${ZCMP}" -i 0,11 -n 10000 in.tar pin.tar || fail=1
+"${ZCMP}" -N -i 0,11 -n 0 in.tar pin.tar || fail=1
+"${ZCMP}" -N -i 0,11 -n 100 in.tar pin.tar || fail=1
+"${ZCMP}" -N -i 0,11 -n 1000 in.tar pin.tar || fail=1
+"${ZCMP}" -N -i 0,11 -n 10000 in.tar pin.tar || fail=1
printf .
-"${ZCMP}" - || fail=1
+"${ZCMP}" -N - || fail=1
printf .
-"${ZCMP}" in in || fail=1
+"${ZCMP}" -N in in || fail=1
printf .
-"${ZCMP}" in || fail=1
+"${ZCMP}" -N in || fail=1
printf .
-"${ZCMP}" in.lz in.gz || fail=1
+"${ZCMP}" -N in.lz in.gz || fail=1
printf .
-"${ZCMP}" in.gz -- -in-.lz || fail=1
+"${ZCMP}" -N --lz='lzip -q' in.lz in.gz || fail=1
printf .
-"${ZCMP}" -- -in-.lz in.gz || fail=1
+"${ZCMP}" -N in.gz -- -in-.lz || fail=1
printf .
-"${ZCMP}" in -- -in-.lz || fail=1
+"${ZCMP}" -N -- -in-.lz in.gz || fail=1
printf .
-"${ZCMP}" -- -in- in.lz || fail=1
+"${ZCMP}" -N in -- -in-.lz || fail=1
printf .
-"${ZCMP}" in.lz -- -in- || fail=1
+"${ZCMP}" -N -- -in- in.lz || fail=1
printf .
-"${ZCMP}" -- -in-.lz in || fail=1
+"${ZCMP}" -N in.lz -- -in- || fail=1
printf .
-"${ZCMP}" -- -in- in || fail=1
+"${ZCMP}" -N -- -in-.lz in || fail=1
printf .
-"${ZCMP}" in -- -in- || fail=1
+"${ZCMP}" -N -- -in- in || fail=1
printf .
-"${ZCMP}" lz_only.lz < in || fail=1
+"${ZCMP}" -N in -- -in- || fail=1
printf .
-"${ZCMP}" in.lz - < in || fail=1
+"${ZCMP}" -N lz_only.lz < in || fail=1
printf .
-"${ZCMP}" - in.lz < in || fail=1
+"${ZCMP}" -N in.lz - < in || fail=1
printf .
-"${ZCMP}" in - < in.lz || fail=1
+"${ZCMP}" -N - in.lz < in || fail=1
printf .
-"${ZCMP}" - in < in.lz || fail=1
+"${ZCMP}" -N in - < in.lz || fail=1
printf .
-"${ZCMP}" -q --format=lz in.lz
+"${ZCMP}" -N - in < in.lz || fail=1
+printf .
+"${ZCMP}" -N -q --format=lz in.lz
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZCMP}" --format=lz in.gz in.lz 2> /dev/null
+"${ZCMP}" -N --format=lz in.gz in.lz 2> /dev/null
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZCMP}" -n -1 in in 2> /dev/null
+"${ZCMP}" -N -n -1 in in 2> /dev/null
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZCMP}" --bad-option in in 2> /dev/null
+"${ZCMP}" -N --bad-option in in 2> /dev/null
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
printf "\ntesting zdiff-%s..." "$2"
for i in ${extensions}; do
- "${ZDIFF}" in.$i || fail=1
+ "${ZDIFF}" -N in.$i > /dev/null || fail=1
printf .
- "${ZDIFF}" in in.$i || fail=1
+ "${ZDIFF}" -N in in.$i > /dev/null || fail=1
printf .
- "${ZDIFF}" --format=,$i in in.$i || fail=1
+ "${ZDIFF}" -N --format=,$i in in.$i > /dev/null || fail=1
printf .
- "${ZDIFF}" in.$i in || fail=1
+ "${ZDIFF}" -N in.$i in > /dev/null || fail=1
printf .
- "${ZDIFF}" --format=$i, in.$i in || fail=1
+ "${ZDIFF}" -N --format=$i, in.$i in > /dev/null || fail=1
printf .
done
-"${ZDIFF}" in in6 > /dev/null
+"${ZDIFF}" -N in in6 > /dev/null
if [ $? != 0 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZDIFF}" in.tar pin.tar > /dev/null
+"${ZDIFF}" -N in.tar pin.tar > /dev/null
if [ $? != 0 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZDIFF}" - || fail=1
+"${ZDIFF}" -N - || fail=1
+printf .
+"${ZDIFF}" -N in in || fail=1
printf .
-"${ZDIFF}" in in || fail=1
+"${ZDIFF}" -N in > /dev/null || fail=1
printf .
-"${ZDIFF}" in || fail=1
+"${ZDIFF}" -N in.lz in.gz > /dev/null || fail=1
printf .
-"${ZDIFF}" in.lz in.gz || fail=1
+"${ZDIFF}" -N --lz='lzip -q' in.lz in.gz > /dev/null || fail=1
printf .
-"${ZDIFF}" in.gz -- -in-.lz || fail=1
+"${ZDIFF}" -N in.gz -- -in-.lz > /dev/null || fail=1
printf .
-"${ZDIFF}" -- -in-.lz in.gz || fail=1
+"${ZDIFF}" -N -- -in-.lz in.gz > /dev/null || fail=1
printf .
-"${ZDIFF}" in -- -in-.lz || fail=1
+"${ZDIFF}" -N in -- -in-.lz > /dev/null || fail=1
printf .
-"${ZDIFF}" -- -in- in.lz || fail=1
+"${ZDIFF}" -N -- -in- in.lz > /dev/null || fail=1
printf .
-"${ZDIFF}" in.lz -- -in- || fail=1
+"${ZDIFF}" -N in.lz -- -in- > /dev/null || fail=1
printf .
-"${ZDIFF}" -- -in-.lz in || fail=1
+"${ZDIFF}" -N -- -in-.lz in > /dev/null || fail=1
printf .
-"${ZDIFF}" -- -in- in || fail=1
+"${ZDIFF}" -N -- -in- in > /dev/null || fail=1
printf .
-"${ZDIFF}" in -- -in- || fail=1
+"${ZDIFF}" -N in -- -in- > /dev/null || fail=1
printf .
-"${ZDIFF}" lz_only.lz < in || fail=1
+"${ZDIFF}" -N lz_only.lz < in > /dev/null || fail=1
printf .
-"${ZDIFF}" in.lz - < in || fail=1
+"${ZDIFF}" -N in.lz - < in > /dev/null || fail=1
printf .
-"${ZDIFF}" - in.lz < in || fail=1
+"${ZDIFF}" -N - in.lz < in > /dev/null || fail=1
printf .
-"${ZDIFF}" in - < in.lz || fail=1
+"${ZDIFF}" -N in - < in.lz > /dev/null || fail=1
printf .
-"${ZDIFF}" - in < in.lz || fail=1
+"${ZDIFF}" -N - in < in.lz > /dev/null || fail=1
printf .
-"${ZDIFF}" -q --format=bz2 in.bz2 2> /dev/null
+"${ZDIFF}" -N -q --format=bz2 in.bz2 2> /dev/null
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZDIFF}" -q --format=,lz in.lz in.bz2 > /dev/null 2> /dev/null
+"${ZDIFF}" -N -q --format=,lz in.lz in.bz2 > /dev/null 2> /dev/null
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZDIFF}" --bad-option 2> /dev/null
+"${ZDIFF}" -N --bad-option 2> /dev/null
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
printf "\ntesting zgrep-%s..." "$2"
for i in ${extensions}; do
- "${ZGREP}" "GNU" in.$i > /dev/null || fail=1
+ "${ZGREP}" -N "GNU" in.$i > /dev/null || fail=1
printf .
- "${ZGREP}" -l "GNU" in.$i > /dev/null || fail=1
+ "${ZGREP}" -N -l "GNU" in.$i > /dev/null || fail=1
printf .
- "${ZGREP}" -L "GNU" in.$i || fail=1
+ "${ZGREP}" -N -L "GNU" in.$i || fail=1
printf .
- "${ZGREP}" --format=$i "GNU" in.$i > /dev/null || fail=1
+ "${ZGREP}" -N --format=$i "GNU" in.$i > /dev/null || fail=1
printf .
- "${ZGREP}" -v "nx_pattern" in.$i > /dev/null || fail=1
+ "${ZGREP}" -N -v "nx_pattern" in.$i > /dev/null || fail=1
printf .
- "${ZGREP}" "nx_pattern" in.$i
+ "${ZGREP}" -N "nx_pattern" in.$i
if [ $? != 0 ] ; then printf . ; else printf - ; fail=1 ; fi
- "${ZGREP}" -l "nx_pattern" in.$i
+ "${ZGREP}" -N -l "nx_pattern" in.$i
if [ $? != 0 ] ; then printf . ; else printf - ; fail=1 ; fi
- "${ZGREP}" -L "nx_pattern" in.$i > /dev/null
+ "${ZGREP}" -N -L "nx_pattern" in.$i > /dev/null
if [ $? != 0 ] ; then printf . ; else printf - ; fail=1 ; fi
- "${ZGREP}" --format=$i "GNU" in 2> /dev/null
+ "${ZGREP}" -N --format=$i "GNU" in 2> /dev/null
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
done
-"${ZGREP}" "GNU" < pin.tar > /dev/null || fail=1
+"${ZGREP}" -N "GNU" < pin.tar > /dev/null || fail=1
printf .
-"${ZGREP}" "GNU" pin.tar > /dev/null || fail=1
+"${ZGREP}" -N "GNU" pin.tar > /dev/null || fail=1
printf .
-"${ZGREP}" -r "GNU" . > /dev/null || fail=1
+"${ZGREP}" -N -r "GNU" . > /dev/null || fail=1
printf .
-"${ZGREP}" "nx_pattern" -r . in > /dev/null
+"${ZGREP}" -N "nx_pattern" -r . in > /dev/null
if [ $? != 0 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZGREP}" "GNU" < in > /dev/null || fail=1
+"${ZGREP}" -N "GNU" < in > /dev/null || fail=1
printf .
-"${ZGREP}" "GNU" < in.gz > /dev/null || fail=1
+"${ZGREP}" -N "GNU" < in.gz > /dev/null || fail=1
printf .
-"${ZGREP}" "GNU" < in.bz2 > /dev/null || fail=1
+"${ZGREP}" -N "GNU" < in.bz2 > /dev/null || fail=1
printf .
-"${ZGREP}" "GNU" < in.lz > /dev/null || fail=1
+"${ZGREP}" -N "GNU" < in.lz > /dev/null || fail=1
printf .
-"${ZGREP}" "GNU" in > /dev/null || fail=1
+"${ZGREP}" -N "GNU" --lz='lzip -q' < in.lz > /dev/null || fail=1
printf .
-"${ZGREP}" "GNU" -- -in- > /dev/null || fail=1
+"${ZGREP}" -N "GNU" in > /dev/null || fail=1
printf .
-"${ZGREP}" "GNU" -- -in-.lz > /dev/null || fail=1
+"${ZGREP}" -N "GNU" -- -in- > /dev/null || fail=1
printf .
-"${ZGREP}" "GNU" in in.gz in.bz2 in.lz -- -in- > /dev/null || fail=1
+"${ZGREP}" -N "GNU" -- -in-.lz > /dev/null || fail=1
printf .
-"${ZGREP}" -l "GNU" in in.gz in.bz2 in.lz -- -in- > /dev/null || fail=1
+"${ZGREP}" -N "GNU" in in.gz in.bz2 in.lz -- -in- > /dev/null || fail=1
printf .
-"${ZGREP}" -L "GNU" in in.gz in.bz2 in.lz -- -in- || fail=1
+"${ZGREP}" -N -l "GNU" in in.gz in.bz2 in.lz -- -in- > /dev/null || fail=1
printf .
-"${ZGREP}" -l "nx_pattern" in in.gz in.bz2 in.lz -- -in-
+"${ZGREP}" -N -L "GNU" in in.gz in.bz2 in.lz -- -in- || fail=1
+printf .
+"${ZGREP}" -N -l "nx_pattern" in in.gz in.bz2 in.lz -- -in-
if [ $? != 0 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZGREP}" -L "nx_pattern" in in.gz in.bz2 in.lz -- -in- > /dev/null
+"${ZGREP}" -N -L "nx_pattern" in in.gz in.bz2 in.lz -- -in- > /dev/null
if [ $? != 0 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZGREP}" --bad-option 2> /dev/null
+"${ZGREP}" -N --bad-option 2> /dev/null
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZEGREP}" "GNU" in > /dev/null || fail=1
+"${ZEGREP}" -N "GNU" in > /dev/null || fail=1
printf .
-"${ZFGREP}" "GNU" in > /dev/null || fail=1
+"${ZFGREP}" -N "GNU" in > /dev/null || fail=1
printf .
printf "\ntesting ztest-%s..." "$2"
for i in ${extensions}; do
- "${ZTEST}" --format=$i < in.$i || fail=1
+ "${ZTEST}" -N --format=$i < in.$i || fail=1
printf .
- "${ZTEST}" --format=$i < in 2> /dev/null
+ "${ZTEST}" -N --format=$i < in 2> /dev/null
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
- "${ZTEST}" --format=$i in 2> /dev/null
+ "${ZTEST}" -N --format=$i in 2> /dev/null
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
done
-"${ZTEST}" in in.gz in.bz2 in.lz -- -in- || fail=1
+"${ZTEST}" -N in in.gz in.bz2 in.lz -- -in- || fail=1
+printf .
+"${ZTEST}" -N < in.gz || fail=1
printf .
-"${ZTEST}" < in.gz || fail=1
+"${ZTEST}" -N < in.bz2 || fail=1
printf .
-"${ZTEST}" < in.bz2 || fail=1
+"${ZTEST}" -N < in.lz || fail=1
printf .
-"${ZTEST}" < in.lz || fail=1
+"${ZTEST}" -N --lz='lzip -q' < in.lz || fail=1
printf .
-"${ZTEST}" -r . || fail=1
+"${ZTEST}" -N -r . || fail=1
printf .
-"${ZTEST}" < in 2> /dev/null
+"${ZTEST}" -N < in 2> /dev/null
+if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
+dd if=in.lz bs=1000 count=1 2> /dev/null | "${ZTEST}" -N -q
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
-dd if=in.lz bs=1000 count=1 2> /dev/null | "${ZTEST}" -q
+"${ZTEST}" -N --format=lz in.bz2 2> /dev/null
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZTEST}" --format=lz in.bz2 2> /dev/null
+"${ZTEST}" -N --lz='lzip --bad-option' in.lz 2> /dev/null
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
-"${ZTEST}" --bad-option 2> /dev/null
+"${ZTEST}" -N --bad-option 2> /dev/null
if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
echo
diff --git a/zcat.cc b/zcat.cc
index 42e821e..366a860 100644
--- a/zcat.cc
+++ b/zcat.cc
@@ -61,7 +61,7 @@ Line_number line_number;
void show_zcat_help()
{
std::printf( "Zcat copies each given file (\"-\" means standard input), to standard\n"
- "output. If any given file is compressed, its uncompressed content is\n"
+ "output. If any given file is compressed, its decompressed content is\n"
"used. If a given file does not exist, and its name does not end with one\n"
"of the known extensions, zcat tries the compressed file names\n"
"corresponding to the supported formats. If no files are specified,\n"
@@ -70,7 +70,7 @@ void show_zcat_help()
"all uncompressed or all in the same compression format.\n"
"\nThe supported formats are bzip2, gzip, lzip and xz.\n"
"\nUsage: zcat [options] [files]\n"
- "\nExit status is 0 if no errors occurred, 1 otherwise.\n"
+ "\nExit status is 0 if no errors occurred, non-zero otherwise.\n"
"\nOptions:\n"
" -h, --help display this help and exit\n"
" -V, --version output version information and exit\n"
@@ -80,13 +80,18 @@ void show_zcat_help()
" -E, --show-ends display '$' at end of each line\n"
" --format=<fmt> force given format (bz2, gz, lz, xz)\n"
" -n, --number number all output lines\n"
+ " -N, --no-rcfile don't read runtime configuration file\n"
" -q, --quiet suppress all messages\n"
" -r, --recursive operate recursively on directories\n"
" -s, --squeeze-blank never more than one single blank line\n"
" -t equivalent to '-vT'\n"
" -T, --show-tabs display TAB characters as '^I'\n"
" -v, --show-nonprinting use '^' and 'M-' notation, except for LF and TAB\n"
- " --verbose verbose mode (show error messages)\n" );
+ " --verbose verbose mode (show error messages)\n"
+ " --bz2=<command> set compressor and options for bzip2 format\n"
+ " --gz=<command> set compressor and options for gzip format\n"
+ " --lz=<command> set compressor and options for lzip format\n"
+ " --xz=<command> set compressor and options for xz format\n" );
show_help_addr();
}
@@ -187,7 +192,7 @@ int do_cat( const int infd, const int buffer_size,
}
-int cat( int infd, const int format_type, const std::string & input_filename,
+int cat( int infd, const int format_index, const std::string & input_filename,
const Cat_options & cat_options )
{
enum { buffer_size = 4096 };
@@ -195,17 +200,17 @@ int cat( int infd, const int format_type, const std::string & input_filename,
uint8_t * const inbuf = new uint8_t[buffer_size+1];
// buffer with space for character quoting and 255-digit line number
uint8_t * const outbuf = new uint8_t[(4*buffer_size)+256];
- pid_t pid = 0;
int retval = 0;
- if( !set_data_feeder( &infd, &pid, format_type ) ) retval = 1;
+ Children children;
+ if( !set_data_feeder( &infd, children, format_index ) ) retval = 1;
else
retval = do_cat( infd, buffer_size, inbuf, outbuf,
input_filename, cat_options );
- if( retval == 0 )
- if( pid && wait_for_child( pid, "data feeder" ) != 0 ) retval = 1;
- if( retval == 0 )
- if( close( infd ) != 0 )
- { show_close_error( "data feeder" ); retval = 1; }
- delete[] inbuf; delete[] outbuf;
+
+ if( !good_status( children, retval == 0 ) ) retval = 1;
+
+ if( retval == 0 && close( infd ) != 0 )
+ { show_close_error( "data feeder" ); retval = 1; }
+ delete[] outbuf; delete[] inbuf;
return retval;
}
diff --git a/zcmp.cc b/zcmp.cc
index 0e5b781..48731a3 100644
--- a/zcmp.cc
+++ b/zcmp.cc
@@ -37,6 +37,7 @@
#include "arg_parser.h"
#include "zutils.h"
+#include "rc.h"
#if CHAR_BIT != 8
#error "Environments where CHAR_BIT != 8 are not supported."
@@ -57,19 +58,19 @@ void show_help()
std::printf( "Zcmp compares two files (\"-\" means standard input), and if they\n"
"differ, tells the first byte and line number where they differ. Bytes\n"
"and lines are numbered starting with 1. If any given file is compressed,\n"
- "its uncompressed content is used. Compressed files are uncompressed on\n"
+ "its decompressed content is used. Compressed files are decompressed on\n"
"the fly; no temporary files are created.\n"
"\nThe supported formats are bzip2, gzip, lzip and xz.\n"
"\nUsage: zcmp [options] file1 [file2]\n"
"\nCompares <file1> to <file2>. If <file2> is omitted zcmp tries the\n"
"following:\n"
- "If <file1> is compressed, compares <file1> to the file with the\n"
- "corresponding decompressed file name (removes the extension from\n"
- "<file1>).\n"
- "If <file1> is not compressed, compares <file1> to the uncompressed\n"
- "contents of <file1>.[lz|bz2|gz|xz] (the first one that is found).\n"
- "If no suitable file is found, compares <file1> to data read from\n"
- "standard input.\n"
+ "\n 1. If <file1> is compressed, compares its decompressed contents with\n"
+ " the corresponding uncompressed file (the name of <file1> with the\n"
+ " extension removed).\n"
+ "\n 2. If <file1> is uncompressed, compares it with the decompressed\n"
+ " contents of <file1>.[lz|bz2|gz|xz] (the first one that is found).\n"
+ "\n 3. If no suitable file is found, compares <file1> with data read from\n"
+ " standard input.\n"
"\nExit status is 0 if inputs are identical, 1 if different, 2 if trouble.\n"
"\nOptions:\n"
" -h, --help display this help and exit\n"
@@ -79,9 +80,14 @@ void show_help()
" -i, --ignore-initial=<n>[,<n2>] ignore differences in the first <n> bytes\n"
" -l, --list list position, value of all differing bytes\n"
" -n, --bytes=<n> compare at most <n> bytes\n"
+ " -N, --no-rcfile don't read runtime configuration file\n"
" -q, --quiet suppress all messages\n"
" -s, --silent (same as --quiet)\n"
" -v, --verbose verbose mode (same as --list)\n"
+ " --bz2=<command> set compressor and options for bzip2 format\n"
+ " --gz=<command> set compressor and options for gzip format\n"
+ " --lz=<command> set compressor and options for lzip format\n"
+ " --xz=<command> set compressor and options for xz format\n"
"Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n"
"Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" );
show_help_addr();
@@ -310,7 +316,7 @@ int cmp( const long long max_size, const int infd[2],
int main( const int argc, const char * const argv[] )
{
- enum { format_opt = 256 };
+ enum { format_opt = 256, bz2_opt, gz_opt, lz_opt, xz_opt };
// number of initial bytes ignored for each file
long long ignore_initial[2] = { 0, 0 };
long long max_size = -1; // < 0 means unlimited size
@@ -326,17 +332,24 @@ int main( const int argc, const char * const argv[] )
{ 'i', "ignore-initial", Arg_parser::yes },
{ 'l', "list", Arg_parser::no },
{ 'n', "bytes", Arg_parser::yes },
+ { 'N', "no-rcfile", Arg_parser::no },
{ 'q', "quiet", Arg_parser::no },
{ 's', "silent", Arg_parser::no },
{ 'v', "verbose", Arg_parser::no },
{ 'V', "version", Arg_parser::no },
{ format_opt, "format", Arg_parser::yes },
+ { bz2_opt, "bz2", Arg_parser::yes },
+ { gz_opt, "gz", Arg_parser::yes },
+ { lz_opt, "lz", Arg_parser::yes },
+ { xz_opt, "xz", Arg_parser::yes },
{ 0 , 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option
{ show_error( parser.error().c_str(), 0, true ); return 2; }
+ maybe_process_config_file( parser );
+
int argind = 0;
for( ; argind < parser.arguments(); ++argind )
{
@@ -350,11 +363,16 @@ int main( const int argc, const char * const argv[] )
case 'i': parse_ignore_initial( arg, ignore_initial ); break;
case 'l': verbosity = 1; break;
case 'n': max_size = getnum( arg ); break;
+ case 'N': break;
case 'q':
case 's': verbosity = -1; break;
case 'v': verbosity = 1; break;
case 'V': show_version( "Zcmp" ); return 0;
- case format_opt: get_format_types( arg, format_types ); break;
+ case format_opt: parse_format_types( arg, format_types ); break;
+ case bz2_opt: parse_compressor( arg, fmt_bz2 ); break;
+ case gz_opt: parse_compressor( arg, fmt_gz ); break;
+ case lz_opt: parse_compressor( arg, fmt_lz ); break;
+ case xz_opt: parse_compressor( arg, fmt_xz ); break;
default : internal_error( "uncaught option" );
}
} // end process options
@@ -405,9 +423,9 @@ int main( const int argc, const char * const argv[] )
int old_infd[2]; // copy of file descriptors of the two files
old_infd[0] = infd[0]; old_infd[1] = infd[1];
- pid_t pid[2];
- if( !set_data_feeder( &infd[0], &pid[0], format_types[0] ) ||
- !set_data_feeder( &infd[1], &pid[1], format_types[1] ) )
+ Children children[2];
+ if( !set_data_feeder( &infd[0], children[0], format_types[0] ) ||
+ !set_data_feeder( &infd[1], children[1], format_types[1] ) )
return 2;
for( int i = 0; i < 2; ++i )
@@ -419,20 +437,8 @@ int main( const int argc, const char * const argv[] )
int retval = cmp( max_size, infd, filenames, print_bytes );
- if( retval != 0 || max_size >= 0 )
- {
- for( int i = 0; i < 2; ++i )
- if( pid[i] )
- {
- const int tmp = child_status( pid[i], "data feeder" );
- if( tmp < 0 ) kill( pid[i], SIGTERM ); // child not terminated
- else if( tmp != 0 ) retval = 2; // child status != 0
- }
- }
- else
- if( ( pid[0] && wait_for_child( pid[0], "data feeder" ) != 0 ) ||
- ( pid[1] && wait_for_child( pid[1], "data feeder" ) != 0 ) )
- retval = 2;
+ for( int i = 0; i < 2; ++i )
+ if( !good_status( children[i], retval == 0 && max_size < 0 ) ) retval = 2;
for( int i = 0; i < 2; ++i )
{
diff --git a/zcmpdiff.cc b/zcmpdiff.cc
index 6212314..71bcec6 100644
--- a/zcmpdiff.cc
+++ b/zcmpdiff.cc
@@ -66,13 +66,13 @@ int open_other_instream( std::string & name )
}
-void get_format_types( const std::string & arg, int format_types[2] )
+void parse_format_types( const std::string & arg, int format_types[2] )
{
const unsigned i = std::min( arg.find( ',' ), arg.size() );
- if( i > 0 ) format_types[0] = get_format_type( arg.substr( 0, i ) );
+ if( i > 0 ) format_types[0] = parse_format_type( arg.substr( 0, i ) );
else format_types[0] = -1;
if( i + 1 < arg.size() ) format_types[1] =
- get_format_type( arg.substr( i + 1 ) );
+ parse_format_type( arg.substr( i + 1 ) );
else format_types[1] = -1;
}
diff --git a/zdiff.cc b/zdiff.cc
index 14b679e..521d55e 100644
--- a/zdiff.cc
+++ b/zdiff.cc
@@ -37,6 +37,7 @@
#include "arg_parser.h"
#include "zutils.h"
+#include "rc.h"
#if CHAR_BIT != 8
#error "Environments where CHAR_BIT != 8 are not supported."
@@ -54,20 +55,20 @@ void show_help()
{
std::printf( "Zdiff compares two files (\"-\" means standard input), and if they\n"
"differ, shows the differences line by line. If any given file is\n"
- "compressed, its uncompressed content is used. Zdiff is a front end to\n"
+ "compressed, its decompressed content is used. Zdiff is a front end to\n"
"the diff program and has the limitation that messages from diff refer to\n"
"temporary filenames instead of those specified.\n"
"\nThe supported formats are bzip2, gzip, lzip and xz.\n"
"\nUsage: zdiff [options] file1 [file2]\n"
"\nCompares <file1> to <file2>. If <file2> is omitted zdiff tries the\n"
"following:\n"
- "If <file1> is compressed, compares <file1> to the file with the\n"
- "corresponding decompressed file name (removes the extension from\n"
- "<file1>).\n"
- "If <file1> is not compressed, compares <file1> to the uncompressed\n"
- "contents of <file1>.[lz|bz2|gz|xz] (the first one that is found).\n"
- "If no suitable file is found, compares <file1> to data read from\n"
- "standard input.\n"
+ "\n 1. If <file1> is compressed, compares its decompressed contents with\n"
+ " the corresponding uncompressed file (the name of <file1> with the\n"
+ " extension removed).\n"
+ "\n 2. If <file1> is uncompressed, compares it with the decompressed\n"
+ " contents of <file1>.[lz|bz2|gz|xz] (the first one that is found).\n"
+ "\n 3. If no suitable file is found, compares <file1> with data read from\n"
+ " standard input.\n"
"\nExit status is 0 if inputs are identical, 1 if different, 2 if trouble.\n"
"\nOptions:\n"
" -h, --help display this help and exit\n"
@@ -81,6 +82,7 @@ void show_help()
" -E, --ignore-tab-expansion ignore changes due to tab expansion\n"
" --format=[<fmt1>][,<fmt2>] force given formats (bz2, gz, lz, xz)\n"
" -i, --ignore-case ignore case differences in file contents\n"
+ " -N, --no-rcfile don't read runtime configuration file\n"
" -p, --show-c-function show which C function each change is in\n"
" -q, --brief output only whether files differ\n"
" -s, --report-identical-files report when two files are identical\n"
@@ -89,6 +91,10 @@ void show_help()
" -u use the unified output format\n"
" -U, --unified=<n> same as -u but use <n> lines of context\n"
" -w, --ignore-all-space ignore all white space\n"
+ " --bz2=<command> set compressor and options for bzip2 format\n"
+ " --gz=<command> set compressor and options for gzip format\n"
+ " --lz=<command> set compressor and options for lzip format\n"
+ " --xz=<command> set compressor and options for xz format\n"
"Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n"
"Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" );
show_help_addr();
@@ -151,59 +157,70 @@ bool set_fifonames( const std::string filenames[2] )
bool set_data_feeder( const std::string & fifoname, const int infd,
- pid_t * const pidp, const int format_type )
+ Children & children, int format_index )
{
const uint8_t * magic_data = 0;
int magic_size = 0;
- const char * const decompressor_name = ( format_type >= 0 ) ?
- decompressor_names[format_type] :
- test_format( infd, &magic_data, &magic_size );
+ if( format_index < 0 )
+ format_index = test_format( infd, &magic_data, &magic_size );
+ children.compressor_name = get_compressor_name( format_index );
- if( decompressor_name ) // compressed
+ if( children.compressor_name ) // compressed
{
- int fda[2]; // pipe from feeder to decompressor
+ int fda[2]; // pipe from feeder to compressor
if( pipe( fda ) < 0 )
{ show_error( "Can't create pipe", errno ); return false; }
const pid_t pid = fork();
- if( pid == 0 ) // child (decompressor feeder)
+ if( pid == 0 ) // child 1 (compressor feeder)
{
- const pid_t pid2 = fork();
- if( pid2 == 0 ) // grandchild (decompressor)
- {
- const int outfd = open( fifoname.c_str(), O_WRONLY | o_binary );
- if( outfd < 0 )
- {
- if( verbosity >= 0 )
- std::fprintf( stderr, "%s: Can't open FIFO '%s' for writing: %s.\n",
- util_name, fifoname.c_str(), std::strerror( errno ) );
- _exit( 2 );
- }
- if( dup2( fda[0], STDIN_FILENO ) >= 0 &&
- dup2( outfd, STDOUT_FILENO ) >= 0 &&
- close( fda[0] ) == 0 && close( fda[1] ) == 0 &&
- close( outfd ) == 0 )
- execlp( decompressor_name, decompressor_name,
- (verbosity >= 0) ? "-d" : "-dq", (char *)0 );
- show_exec_error( decompressor_name );
- _exit( 2 );
- }
- if( pid2 < 0 )
- { show_fork_error( decompressor_name ); _exit( 2 ); }
-
if( close( fda[0] ) != 0 ||
!feed_data( infd, fda[1], magic_data, magic_size ) )
_exit( 2 );
if( close( fda[1] ) != 0 )
{ show_close_error( "data feeder" ); _exit( 2 ); }
- _exit( wait_for_child( pid2, decompressor_name ) );
+ _exit( 0 );
}
- // parent
+ if( pid < 0 ) // parent
+ { show_fork_error( "data feeder" ); return false; }
+
+ const pid_t pid2 = fork();
+ if( pid2 == 0 ) // child 2 (compressor)
+ {
+ const int outfd = open( fifoname.c_str(), O_WRONLY | o_binary );
+ if( outfd < 0 )
+ {
+ if( verbosity >= 0 )
+ std::fprintf( stderr, "%s: Can't open FIFO '%s' for writing: %s.\n",
+ util_name, fifoname.c_str(), std::strerror( errno ) );
+ _exit( 2 );
+ }
+ if( dup2( fda[0], STDIN_FILENO ) >= 0 &&
+ dup2( outfd, STDOUT_FILENO ) >= 0 &&
+ close( fda[0] ) == 0 && close( fda[1] ) == 0 &&
+ close( outfd ) == 0 )
+ {
+ const std::vector< std::string > & compressor_args =
+ get_compressor_args( format_index );
+ const int size = compressor_args.size();
+ const char ** const argv = new const char *[size+3];
+ argv[0] = children.compressor_name;
+ for( int i = 0; i < size; ++i )
+ argv[i+1] = compressor_args[i].c_str();
+ argv[size+1] = ( verbosity >= 0 ) ? "-d" : "-dq";
+ argv[size+2] = 0;
+ execvp( argv[0], (char **)argv );
+ }
+ show_exec_error( children.compressor_name );
+ _exit( 2 );
+ }
+ if( pid2 < 0 ) // parent
+ { show_fork_error( children.compressor_name ); return false; }
+
close( fda[0] ); close( fda[1] );
- if( pid < 0 )
- { show_fork_error( "decompressor feeder" ); return false; }
- *pidp = pid;
+ children.pid[0] = pid;
+ children.pid[1] = pid2;
}
- else // not compressed
+ else // uncompressed
{
const pid_t pid = fork();
if( pid == 0 ) // child (feeder)
@@ -222,10 +239,10 @@ bool set_data_feeder( const std::string & fifoname, const int infd,
{ show_close_error( "data feeder" ); _exit( 2 ); }
_exit( 0 );
}
- // parent
- if( pid < 0 )
+ if( pid < 0 ) // parent
{ show_fork_error( "data feeder" ); return false; }
- *pidp = pid;
+ children.pid[0] = pid;
+ children.pid[1] = 0;
}
return true;
}
@@ -251,7 +268,7 @@ void set_signals()
int main( const int argc, const char * const argv[] )
{
- enum { format_opt = 256 };
+ enum { format_opt = 256, bz2_opt, gz_opt, lz_opt, xz_opt };
std::vector< const char * > diff_args; // args to diff, maybe empty
int format_types[2] = { -1, -1 };
invocation_name = argv[0];
@@ -268,6 +285,7 @@ int main( const int argc, const char * const argv[] )
{ 'E', "ignore-tab-expansion", Arg_parser::no },
{ 'h', "help", Arg_parser::no },
{ 'i', "ignore-case", Arg_parser::no },
+ { 'N', "no-rcfile", Arg_parser::no },
{ 'p', "show-c-function", Arg_parser::no },
{ 'q', "brief", Arg_parser::no },
{ 's', "report-identical-files", Arg_parser::no },
@@ -278,12 +296,18 @@ int main( const int argc, const char * const argv[] )
{ 'V', "version", Arg_parser::no },
{ 'w', "ignore-all-space", Arg_parser::no },
{ format_opt, "format", Arg_parser::yes },
+ { bz2_opt, "bz2", Arg_parser::yes },
+ { gz_opt, "gz", Arg_parser::yes },
+ { lz_opt, "lz", Arg_parser::yes },
+ { xz_opt, "xz", Arg_parser::yes },
{ 0 , 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option
{ show_error( parser.error().c_str(), 0, true ); return 2; }
+ maybe_process_config_file( parser );
+
int argind = 0;
for( ; argind < parser.arguments(); ++argind )
{
@@ -301,6 +325,7 @@ int main( const int argc, const char * const argv[] )
case 'E': diff_args.push_back( "-E" ); break;
case 'h': show_help(); return 0;
case 'i': diff_args.push_back( "-i" ); break;
+ case 'N': break;
case 'p': diff_args.push_back( "-p" ); break;
case 'q': diff_args.push_back( "-q" ); break;
case 's': diff_args.push_back( "-s" ); break;
@@ -310,7 +335,11 @@ int main( const int argc, const char * const argv[] )
case 'U': diff_args.push_back( "-U" ); diff_args.push_back( arg ); break;
case 'V': show_version( "Zdiff" ); return 0;
case 'w': diff_args.push_back( "-w" ); break;
- case format_opt: get_format_types( arg, format_types ); break;
+ case format_opt: parse_format_types( arg, format_types ); break;
+ case bz2_opt: parse_compressor( arg, fmt_bz2 ); break;
+ case gz_opt: parse_compressor( arg, fmt_gz ); break;
+ case lz_opt: parse_compressor( arg, fmt_lz ); break;
+ case xz_opt: parse_compressor( arg, fmt_xz ); break;
default : internal_error( "uncaught option" );
}
} // end process options
@@ -375,31 +404,18 @@ int main( const int argc, const char * const argv[] )
show_exec_error( DIFF );
_exit( 2 );
}
- // parent
- if( diff_pid < 0 )
+ if( diff_pid < 0 ) // parent
{ show_fork_error( DIFF ); return 2; }
- pid_t pid[2];
- if( !set_data_feeder( fifonames[0], infd[0], &pid[0], format_types[0] ) ||
- !set_data_feeder( fifonames[1], infd[1], &pid[1], format_types[1] ) )
+ Children children[2];
+ if( !set_data_feeder( fifonames[0], infd[0], children[0], format_types[0] ) ||
+ !set_data_feeder( fifonames[1], infd[1], children[1], format_types[1] ) )
return 2;
int retval = wait_for_child( diff_pid, DIFF );
- if( retval != 0 )
- {
- for( int i = 0; i < 2; ++i )
- if( pid[i] )
- {
- const int tmp = child_status( pid[i], "data feeder" );
- if( tmp < 0 ) kill( pid[i], SIGTERM ); // child not terminated
- else if( tmp != 0 ) retval = 2; // child status != 0
- }
- }
- else
- if( ( pid[0] && wait_for_child( pid[0], "data feeder" ) != 0 ) ||
- ( pid[1] && wait_for_child( pid[1], "data feeder" ) != 0 ) )
- retval = 2;
+ for( int i = 0; i < 2; ++i )
+ if( !good_status( children[i], retval == 0 ) ) retval = 2;
for( int i = 0; i < 2; ++i )
if( filenames[i] != "-" && close( infd[i] ) != 0 )
diff --git a/zgrep.cc b/zgrep.cc
index 7bbb448..0c78504 100644
--- a/zgrep.cc
+++ b/zgrep.cc
@@ -18,8 +18,8 @@
void show_zgrep_help()
{
std::printf( "Zgrep is a front end to the grep program that allows transparent search\n"
- "on any combination of compressed and non-compressed files. If any given\n"
- "file is compressed, its uncompressed content is used. If a given file\n"
+ "on any combination of compressed and uncompressed files. If any given\n"
+ "file is compressed, its decompressed content is used. If a given file\n"
"does not exist, and its name does not end with one of the known\n"
"extensions, zgrep tries the compressed file names corresponding to the\n"
"supported formats. If no files are specified, data is read from\n"
@@ -51,6 +51,7 @@ void show_zgrep_help()
" -L, --files-without-match only print names of files containing no matches\n"
" -m, --max-count=<n> stop after <n> matches\n"
" -n, --line-number print the line number of each line\n"
+ " -N, --no-rcfile don't read runtime configuration file\n"
" -o, --only-matching show only the part of a line matching <pattern>\n"
" -q, --quiet suppress all messages\n"
" -r, --recursive operate recursively on directories\n"
@@ -58,16 +59,20 @@ void show_zgrep_help()
" -v, --invert-match select non-matching lines\n"
" --verbose verbose mode (show error messages)\n"
" -w, --word-regexp match only whole words\n"
- " -x, --line-regexp match only whole lines\n" );
+ " -x, --line-regexp match only whole lines\n"
+ " --bz2=<command> set compressor and options for bzip2 format\n"
+ " --gz=<command> set compressor and options for gzip format\n"
+ " --lz=<command> set compressor and options for lzip format\n"
+ " --xz=<command> set compressor and options for xz format\n" );
show_help_addr();
}
-int zgrep_stdin( int infd, const int format_type,
+int zgrep_stdin( int infd, const int format_index,
const std::vector< const char * > & grep_args )
{
- pid_t pid;
- if( !set_data_feeder( &infd, &pid, format_type ) ) return 2;
+ Children children;
+ if( !set_data_feeder( &infd, children, format_index ) ) return 2;
const pid_t grep_pid = fork();
if( grep_pid == 0 ) // child (grep)
{
@@ -83,28 +88,26 @@ int zgrep_stdin( int infd, const int format_type,
show_exec_error( GREP );
_exit( 2 );
}
- // parent
- if( grep_pid < 0 )
+ if( grep_pid < 0 ) // parent
{ show_fork_error( GREP ); return 2; }
int retval = wait_for_child( grep_pid, GREP );
- if( retval != 1 )
- { if( pid ) kill( pid, SIGTERM ); }
- else
- if( pid && wait_for_child( pid, "data feeder" ) != 0 ) retval = 2;
+
+ if( !good_status( children, retval == 1 ) ) retval = 2;
+
if( close( infd ) != 0 )
{ show_close_error( "data feeder" ); return 2; }
return retval;
}
-int zgrep_file( int infd, const int format_type,
+int zgrep_file( int infd, const int format_index,
const std::string & input_filename,
const std::vector< const char * > & grep_args,
const int grep_list_mode, const bool grep_show_name )
{
- pid_t pid;
- if( !set_data_feeder( &infd, &pid, format_type ) ) return 2;
+ Children children;
+ if( !set_data_feeder( &infd, children, format_index ) ) return 2;
int fda[2]; // pipe from grep
if( pipe( fda ) < 0 )
{ show_error( "Can't create pipe", errno ); return 2; }
@@ -125,10 +128,10 @@ int zgrep_file( int infd, const int format_type,
show_exec_error( GREP );
_exit( 2 );
}
- // parent
- close( fda[1] );
- if( grep_pid < 0 )
+ if( grep_pid < 0 ) // parent
{ show_fork_error( GREP ); return 2; }
+
+ close( fda[1] );
enum { buffer_size = 256 };
uint8_t buffer[buffer_size];
bool line_begin = true;
@@ -154,10 +157,9 @@ int zgrep_file( int infd, const int format_type,
}
int retval = wait_for_child( grep_pid, GREP );
- if( retval != 1 )
- { if( pid ) kill( pid, SIGTERM ); }
- else
- if( pid && wait_for_child( pid, "data feeder" ) != 0 ) retval = 2;
+
+ if( !good_status( children, retval == 1 ) ) retval = 2;
+
if( grep_list_mode && (retval == 0) == (grep_list_mode == 1) )
std::printf( "%s\n", input_filename.c_str() );
if( close( infd ) != 0 )
diff --git a/ztest.cc b/ztest.cc
index b731191..20296b9 100644
--- a/ztest.cc
+++ b/ztest.cc
@@ -18,9 +18,9 @@
void show_ztest_help()
{
std::printf( "Ztest verifies the integrity of the specified compressed files.\n"
- "Non-compressed files are ignored. If no files are specified, the\n"
- "integrity of compressed data read from standard input is verified. Data\n"
- "read from standard input must be all in the same compression format.\n"
+ "Uncompressed files are ignored. If no files are specified, the integrity\n"
+ "of compressed data read from standard input is verified. Data read from\n"
+ "standard input must be all in the same compression format.\n"
"\nThe supported formats are bzip2, gzip, lzip and xz.\n"
"\nNote that some xz files lack integrity information, and therefore can't\n"
"be verified as reliably as the other formats can.\n"
@@ -32,100 +32,114 @@ void show_ztest_help()
" -h, --help display this help and exit\n"
" -V, --version output version information and exit\n"
" --format=<fmt> force given format (bz2, gz, lz, xz)\n"
+ " -N, --no-rcfile don't read runtime configuration file\n"
" -q, --quiet suppress all messages\n"
" -r, --recursive operate recursively on directories\n"
- " -v, --verbose be verbose (a 2nd -v gives more)\n" );
+ " -v, --verbose be verbose (a 2nd -v gives more)\n"
+ " --bz2=<command> set compressor and options for bzip2 format\n"
+ " --gz=<command> set compressor and options for gzip format\n"
+ " --lz=<command> set compressor and options for lzip format\n"
+ " --xz=<command> set compressor and options for xz format\n" );
show_help_addr();
}
-int ztest_stdin( const int infd, const int format_type,
+int ztest_stdin( const int infd, int format_index,
const std::vector< const char * > & ztest_args )
{
const uint8_t * magic_data = 0;
int magic_size = 0;
- const char * const decompressor_name = ( format_type >= 0 ) ?
- decompressor_names[format_type] :
- test_format( infd, &magic_data, &magic_size );
- if( !decompressor_name )
+ if( format_index < 0 )
+ format_index = test_format( infd, &magic_data, &magic_size );
+ const char * const compressor_name = get_compressor_name( format_index );
+ if( !compressor_name )
{ show_error( "Unknown data format read from stdin." ); return 2; }
int fda[2]; // pipe from feeder
if( pipe( fda ) < 0 )
{ show_error( "Can't create pipe", errno ); return 1; }
const pid_t pid = fork();
- if( pid == 0 ) // child1 (decompressor)
+ if( pid == 0 ) // child1 (compressor)
{
if( dup2( fda[0], STDIN_FILENO ) >= 0 &&
close( fda[0] ) == 0 && close( fda[1] ) == 0 )
{
- const char ** const argv = new const char *[ztest_args.size()+3];
- argv[0] = decompressor_name;
- for( unsigned i = 0; i < ztest_args.size(); ++i )
- argv[i+1] = ztest_args[i];
- argv[ztest_args.size()+1] = "-t";
- argv[ztest_args.size()+2] = 0;
+ const std::vector< std::string > & compressor_args =
+ get_compressor_args( format_index );
+ const int size = compressor_args.size();
+ const int size2 = ztest_args.size();
+ const char ** const argv = new const char *[size+size2+3];
+ argv[0] = compressor_name;
+ for( int i = 0; i < size; ++i )
+ argv[i+1] = compressor_args[i].c_str();
+ for( int i = 0; i < size2; ++i )
+ argv[i+size+1] = ztest_args[i];
+ argv[size+size2+1] = "-t";
+ argv[size+size2+2] = 0;
execvp( argv[0], (char **)argv );
}
- show_exec_error( decompressor_name );
+ show_exec_error( compressor_name );
_exit( 1 );
}
- // parent
- if( pid < 0 )
- { show_fork_error( decompressor_name ); return 1; }
+ if( pid < 0 ) // parent
+ { show_fork_error( compressor_name ); return 1; }
const pid_t pid2 = fork();
- if( pid2 == 0 ) // child2 (decompressor feeder)
+ if( pid2 == 0 ) // child2 (compressor feeder)
{
if( close( fda[0] ) != 0 ||
!feed_data( infd, fda[1], magic_data, magic_size ) )
_exit( 1 );
if( close( fda[1] ) != 0 )
- { show_close_error( "decompressor feeder" ); _exit( 1 ); }
+ { show_close_error( "data feeder" ); _exit( 1 ); }
_exit( 0 );
}
- // parent
- if( pid2 < 0 )
- { show_fork_error( "decompressor feeder" ); return 1; }
+ if( pid2 < 0 ) // parent
+ { show_fork_error( "data feeder" ); return 1; }
close( fda[0] ); close( fda[1] );
- int retval = wait_for_child( pid, decompressor_name, 1 );
- if( retval == 0 && wait_for_child( pid2, "decompressor feeder" ) != 0 )
+ int retval = wait_for_child( pid, compressor_name, 1 );
+ if( retval == 0 && wait_for_child( pid2, "data feeder" ) != 0 )
retval = 1;
return retval;
}
-int ztest_file( const int infd, const int format_type,
+int ztest_file( const int infd, int format_index,
const std::string & input_filename,
const std::vector< const char * > & ztest_args )
{
const uint8_t * magic_data = 0;
int magic_size = 0;
- const char * const decompressor_name = ( format_type >= 0 ) ?
- decompressor_names[format_type] :
- test_format( infd, &magic_data, &magic_size );
- if( !decompressor_name )
+ if( format_index < 0 )
+ format_index = test_format( infd, &magic_data, &magic_size );
+ const char * const compressor_name = get_compressor_name( format_index );
+ if( !compressor_name )
return 0; // skip this file
const pid_t pid = fork();
- if( pid == 0 ) // child (decompressor)
+ if( pid == 0 ) // child (compressor)
{
- const char ** const argv = new const char *[ztest_args.size()+5];
- argv[0] = decompressor_name;
- for( unsigned i = 0; i < ztest_args.size(); ++i )
- argv[i+1] = ztest_args[i];
- argv[ztest_args.size()+1] = "-t";
- argv[ztest_args.size()+2] = "--";
- argv[ztest_args.size()+3] = input_filename.c_str();
- argv[ztest_args.size()+4] = 0;
+ const std::vector< std::string > & compressor_args =
+ get_compressor_args( format_index );
+ const int size = compressor_args.size();
+ const int size2 = ztest_args.size();
+ const char ** const argv = new const char *[size+size2+5];
+ argv[0] = compressor_name;
+ for( int i = 0; i < size; ++i )
+ argv[i+1] = compressor_args[i].c_str();
+ for( int i = 0; i < size2; ++i )
+ argv[i+size+1] = ztest_args[i];
+ argv[size+size2+1] = "-t";
+ argv[size+size2+2] = "--";
+ argv[size+size2+3] = input_filename.c_str();
+ argv[size+size2+4] = 0;
execvp( argv[0], (char **)argv );
- show_exec_error( decompressor_name );
+ show_exec_error( compressor_name );
_exit( 1 );
}
- // parent
- if( pid < 0 )
- { show_fork_error( decompressor_name ); return 1; }
+ if( pid < 0 ) // parent
+ { show_fork_error( compressor_name ); return 1; }
- return wait_for_child( pid, decompressor_name, 1 );
+ return wait_for_child( pid, compressor_name, 1 );
}
diff --git a/zutils.cc b/zutils.cc
index 850504a..bf6fe9a 100644
--- a/zutils.cc
+++ b/zutils.cc
@@ -22,11 +22,13 @@
#include <cstdlib>
#include <cstring>
#include <string>
+#include <vector>
#include <stdint.h>
#include <unistd.h>
#include <sys/wait.h>
#include "zutils.h"
+#include "rc.h"
const char * invocation_name = 0;
@@ -35,7 +37,7 @@ const char * util_name = program_name;
int verbosity = 0;
-int get_format_type( const std::string & arg )
+int parse_format_type( const std::string & arg )
{
for( int i = 0; i < num_formats; ++i )
if( arg == format_names[i] )
@@ -102,56 +104,90 @@ bool feed_data( const int infd, const int outfd,
}
-bool set_data_feeder( int * const infdp, pid_t * const pidp,
- const int format_type )
+bool good_status( const Children & children, const bool finished )
+ {
+ bool error = false;
+ for( int i = 0; i < 2; ++i )
+ {
+ const pid_t pid = children.pid[i];
+ if( pid )
+ {
+ const char * const msg =
+ ( i & 1 ) ? children.compressor_name : "data feeder";
+ if( !finished )
+ {
+ const int tmp = child_status( pid, msg );
+ if( tmp < 0 ) kill( pid, SIGTERM ); // child not terminated
+ else if( tmp != 0 ) error = true; // child status != 0
+ }
+ else
+ if( wait_for_child( pid, msg ) != 0 ) error = true;
+ }
+ }
+ return !error;
+ }
+
+
+bool set_data_feeder( int * const infdp, Children & children, int format_index )
{
const uint8_t * magic_data = 0;
int magic_size = 0;
- const char * const decompressor_name = ( format_type >= 0 ) ?
- decompressor_names[format_type] :
- test_format( *infdp, &magic_data, &magic_size );
+ if( format_index < 0 )
+ format_index = test_format( *infdp, &magic_data, &magic_size );
+ children.compressor_name = get_compressor_name( format_index );
- if( decompressor_name ) // compressed
+ if( children.compressor_name ) // compressed
{
int fda[2]; // pipe from feeder
- int fda2[2]; // pipe from decompressor
+ int fda2[2]; // pipe from compressor
if( pipe( fda ) < 0 || pipe( fda2 ) < 0 )
{ show_error( "Can't create pipe", errno ); return false; }
const int old_infd = *infdp;
*infdp = fda2[0];
const pid_t pid = fork();
- if( pid == 0 ) // child (decompressor feeder)
+ if( pid == 0 ) // child 1 (compressor feeder)
{
- const pid_t pid2 = fork();
- if( pid2 == 0 ) // grandchild (decompressor)
- {
- if( dup2( fda[0], STDIN_FILENO ) >= 0 &&
- dup2( fda2[1], STDOUT_FILENO ) >= 0 &&
- close( fda[0] ) == 0 && close( fda[1] ) == 0 &&
- close( fda2[0] ) == 0 && close( fda2[1] ) == 0 )
- execlp( decompressor_name, decompressor_name,
- (verbosity >= 0) ? "-d" : "-dq", (char *)0 );
- show_exec_error( decompressor_name );
- _exit( 2 );
- }
- if( pid2 < 0 )
- { show_fork_error( decompressor_name ); _exit( 2 ); }
-
if( close( fda[0] ) != 0 ||
close( fda2[0] ) != 0 || close( fda2[1] ) != 0 ||
!feed_data( old_infd, fda[1], magic_data, magic_size ) )
_exit( 2 );
if( close( fda[1] ) != 0 )
- { show_close_error( "decompressor feeder" ); _exit( 2 ); }
- _exit( wait_for_child( pid2, decompressor_name ) );
+ { show_close_error( "data feeder" ); _exit( 2 ); }
+ _exit( 0 );
+ }
+ if( pid < 0 ) // parent
+ { show_fork_error( "data feeder" ); return false; }
+
+ const pid_t pid2 = fork();
+ if( pid2 == 0 ) // child 2 (compressor)
+ {
+ if( dup2( fda[0], STDIN_FILENO ) >= 0 &&
+ dup2( fda2[1], STDOUT_FILENO ) >= 0 &&
+ close( fda[0] ) == 0 && close( fda[1] ) == 0 &&
+ close( fda2[0] ) == 0 && close( fda2[1] ) == 0 )
+ {
+ const std::vector< std::string > & compressor_args =
+ get_compressor_args( format_index );
+ const int size = compressor_args.size();
+ const char ** const argv = new const char *[size+3];
+ argv[0] = children.compressor_name;
+ for( int i = 0; i < size; ++i )
+ argv[i+1] = compressor_args[i].c_str();
+ argv[size+1] = ( verbosity >= 0 ) ? "-d" : "-dq";
+ argv[size+2] = 0;
+ execvp( argv[0], (char **)argv );
+ }
+ show_exec_error( children.compressor_name );
+ _exit( 2 );
}
- // parent
+ if( pid2 < 0 ) // parent
+ { show_fork_error( children.compressor_name ); return false; }
+
close( fda[0] ); close( fda[1] ); close( fda2[1] );
- if( pid < 0 )
- { show_fork_error( "decompressor feeder" ); return false; }
- *pidp = pid;
+ children.pid[0] = pid;
+ children.pid[1] = pid2;
}
- else // not compressed
+ else // uncompressed
{
int fda[2]; // pipe from feeder
if( pipe( fda ) < 0 )
@@ -168,11 +204,11 @@ bool set_data_feeder( int * const infdp, pid_t * const pidp,
{ show_close_error( "data feeder" ); _exit( 2 ); }
_exit( 0 );
}
- // parent
- close( fda[1] );
- if( pid < 0 )
+ if( pid < 0 ) // parent
{ show_fork_error( "data feeder" ); return false; }
- *pidp = pid;
+ close( fda[1] );
+ children.pid[0] = pid;
+ children.pid[1] = 0;
}
return true;
}
@@ -187,7 +223,7 @@ void show_help_addr()
void show_version( const char * const Util_name )
{
- if( !Util_name || !*Util_name )
+ if( !Util_name || !Util_name[0] )
std::printf( "%s %s\n", Program_name, PROGVERSION );
else
std::printf( "%s (%s) %s\n", Util_name, program_name, PROGVERSION );
@@ -256,9 +292,8 @@ void internal_error( const char * const msg )
}
-const char * test_format( const int infd,
- const uint8_t ** const magic_datap,
- int * const magic_sizep )
+int test_format( const int infd, const uint8_t ** const magic_datap,
+ int * const magic_sizep )
{
enum { buf_size = 5 };
static uint8_t buf[buf_size];
@@ -271,13 +306,13 @@ const char * test_format( const int infd,
if( readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == bzip2_magic[1] &&
readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == bzip2_magic[2] )
{ *magic_datap = bzip2_magic; *magic_sizep = bzip2_magic_size;
- return decompressor_names[fmt_bz2]; }
+ return fmt_bz2; }
}
else if( buf[0] == gzip_magic[0] )
{
if( readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == gzip_magic[1] )
{ *magic_datap = gzip_magic; *magic_sizep = gzip_magic_size;
- return decompressor_names[fmt_gz]; }
+ return fmt_gz; }
}
else if( buf[0] == lzip_magic[0] )
{
@@ -285,7 +320,7 @@ const char * test_format( const int infd,
readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == lzip_magic[2] &&
readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == lzip_magic[3] )
{ *magic_datap = lzip_magic; *magic_sizep = lzip_magic_size;
- return decompressor_names[fmt_lz]; }
+ return fmt_lz; }
}
else if( buf[0] == xz_magic[0] )
{
@@ -294,11 +329,11 @@ const char * test_format( const int infd,
readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == xz_magic[3] &&
readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == xz_magic[4] )
{ *magic_datap = xz_magic; *magic_sizep = xz_magic_size;
- return decompressor_names[fmt_xz]; }
+ return fmt_xz; }
}
}
*magic_datap = buf; *magic_sizep = i;
- return 0;
+ return -1;
}
diff --git a/zutils.h b/zutils.h
index 1a2bb93..238dea8 100644
--- a/zutils.h
+++ b/zutils.h
@@ -15,9 +15,10 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-const char * const Program_name = "Zutils";
-const char * const program_name = "zutils";
-const char * const program_year = "2013";
+const char * const Program_name = "Zutils";
+const char * const program_name = "zutils";
+const char * const config_file_name = "zutilsrc";
+const char * const program_year = "2013";
extern const char * invocation_name;
extern const char * util_name;
@@ -27,8 +28,6 @@ enum { fmt_bz2, fmt_gz, fmt_lz, fmt_xz, num_formats };
const char * const format_names[num_formats] = { "bz2", "gz", "lz", "xz" };
const char * const simple_extensions[num_formats] =
{ ".bz2", ".gz", ".lz", ".xz" };
-const char * const decompressor_names[num_formats] =
- { "bzip2", "gzip", "lzip", "xz" };
const int8_t format_order[num_formats] =
{ fmt_lz, fmt_bz2, fmt_gz, fmt_xz }; // search order
@@ -47,14 +46,20 @@ const uint8_t xz_magic[xz_magic_size] =
{ 0xFD, 0x37, 0x7A, 0x58, 0x5A }; // 0xFD, "7zXZ"
-int get_format_type( const std::string & arg );
+int parse_format_type( const std::string & arg );
int readblock( const int fd, uint8_t * const buf, const int size );
int writeblock( const int fd, const uint8_t * const buf, const int size );
bool feed_data( const int infd, const int outfd,
const uint8_t * magic_data, const int magic_size );
-bool set_data_feeder( int * const infdp, pid_t * const pidp,
- const int format_type );
+
+struct Children
+ {
+ const char * compressor_name;
+ pid_t pid[2]; // data feeder, compressor
+ };
+bool good_status( const Children & children, const bool finished );
+bool set_data_feeder( int * const infdp, Children & children, int format_index );
void show_help_addr();
void show_version( const char * const Util_name = 0 );
@@ -66,9 +71,10 @@ void show_exec_error( const char * const prog_name );
void show_fork_error( const char * const prog_name );
void internal_error( const char * const msg );
-const char * test_format( const int infd,
- const uint8_t ** const magic_datap,
- int * const magic_sizep );
+// Returns format index or -1 if uncompressed
+//
+int test_format( const int infd, const uint8_t ** const magic_datap,
+ int * const magic_sizep );
// Returns exit status of child process 'pid', or 'eretval' in case of error.
//
diff --git a/zutilsrc b/zutilsrc
new file mode 100644
index 0000000..04a1d69
--- /dev/null
+++ b/zutilsrc
@@ -0,0 +1,16 @@
+#
+# Runtime Configuration file for Zutils
+#
+# Zutils looks for this file in:
+# 1 - $HOME/.zutilsrc
+# 2 - ${sysconfdir}/zutilsrc
+
+# This file sets the compressor and options to be used for each format.
+# The command line options override compressors specified in this file.
+# Syntax: <format> = <compressor> [options]
+# Uncomment each line you want to take effect.
+
+# bz2 = lbzip2 -n2
+# gz = pigz -p2
+# lz = plzip -n2
+# xz = pixz -p2