1
0
Fork 0

Merging upstream version 1.1~rc2.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-24 05:43:06 +01:00
parent 24e2ea3a41
commit e13f4df619
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
26 changed files with 1151 additions and 583 deletions

View file

@ -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> 2013-05-31 Antonio Diaz Diaz <antonio@gnu.org>
* Version 1.0 released. * Version 1.0 released.
@ -7,7 +19,7 @@
file and '--recursive' has not been selected. file and '--recursive' has not been selected.
* Zgrep: Fixed output of option '-L' (it behaved like '-l'). * Zgrep: Fixed output of option '-L' (it behaved like '-l').
* zcmp.cc: Fixed deadlock when '-n' option is used. * 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. only if verbosity < 0.
* zutils.cc (set_data_feeder): Likewise. * zutils.cc (set_data_feeder): Likewise.
* Changed quote characters in messages as advised by GNU Standards. * Changed quote characters in messages as advised by GNU Standards.

View file

@ -1,7 +1,7 @@
Requirements Requirements
------------ ------------
You will need a C++ compiler. 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. standards compliant compiler.
Gcc is available at http://gcc.gnu.org. Gcc is available at http://gcc.gnu.org.

View file

@ -7,9 +7,9 @@ INSTALL_DATA = $(INSTALL) -p -m 644
INSTALL_DIR = $(INSTALL) -d -m 755 INSTALL_DIR = $(INSTALL) -d -m 755
SHELL = /bin/sh SHELL = /bin/sh
objs = arg_parser.o zutils.o main.o objs = arg_parser.o rc.o zutils.o main.o
zcmp_objs = arg_parser.o zutils.o zcmp.o zcmp_objs = arg_parser.o rc.o zutils.o zcmp.o
zdiff_objs = arg_parser.o zutils.o zdiff.o zdiff_objs = arg_parser.o rc.o zutils.o zdiff.o
scripts = zcat zegrep zfgrep zgrep ztest scripts = zcat zegrep zfgrep zgrep ztest
@ -57,6 +57,9 @@ main.o : main.cc
zdiff.o : zdiff.cc zdiff.o : zdiff.cc
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -DDIFF=\"$(DIFF)\" -c -o $@ $< $(CXX) $(CPPFLAGS) $(CXXFLAGS) -DDIFF=\"$(DIFF)\" -c -o $@ $<
rc.o : rc.cc
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -DSYSCONFDIR=\"$(sysconfdir)\" -c -o $@ $<
zutils.o : zutils.cc zutils.o : zutils.cc
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $< $(CXX) $(CPPFLAGS) $(CXXFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $<
@ -66,10 +69,11 @@ zutils.o : zutils.cc
$(objs) : Makefile $(objs) : Makefile
$(scripts) : Makefile $(scripts) : Makefile
arg_parser.o : arg_parser.h arg_parser.o : arg_parser.h
main.o : arg_parser.h zutils.h zcat.cc zgrep.cc ztest.cc main.o : arg_parser.h zutils.h rc.h zcat.cc zgrep.cc ztest.cc
zcmp.o : arg_parser.h zutils.h zcmpdiff.cc Makefile rc.o : arg_parser.h zutils.h rc.h
zdiff.o : arg_parser.h zutils.h zcmpdiff.cc Makefile zcmp.o : arg_parser.h zutils.h rc.h zcmpdiff.cc Makefile
zutils.o : zutils.h zdiff.o : arg_parser.h zutils.h rc.h zcmpdiff.cc Makefile
zutils.o : zutils.h rc.h
doc : info man doc : info man
@ -120,6 +124,10 @@ install-bin : all
$(INSTALL_SCRIPT) ./zfgrep "$(DESTDIR)$(bindir)/zfgrep" $(INSTALL_SCRIPT) ./zfgrep "$(DESTDIR)$(bindir)/zfgrep"
$(INSTALL_SCRIPT) ./zgrep "$(DESTDIR)$(bindir)/zgrep" $(INSTALL_SCRIPT) ./zgrep "$(DESTDIR)$(bindir)/zgrep"
$(INSTALL_SCRIPT) ./ztest "$(DESTDIR)$(bindir)/ztest" $(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 : install-info :
if [ ! -d "$(DESTDIR)$(infodir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(infodir)" ; fi 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)/zfgrep"
-rm -f "$(DESTDIR)$(bindir)/zgrep" -rm -f "$(DESTDIR)$(bindir)/zgrep"
-rm -f "$(DESTDIR)$(bindir)/ztest" -rm -f "$(DESTDIR)$(bindir)/ztest"
-rm -f "$(DESTDIR)$(sysconfdir)/$(pkgname)rc"
uninstall-info : uninstall-info :
-install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$(pkgname).info" -install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$(pkgname).info"
@ -177,6 +186,7 @@ dist : doc
$(DISTNAME)/testsuite/check.sh \ $(DISTNAME)/testsuite/check.sh \
$(DISTNAME)/testsuite/test.txt \ $(DISTNAME)/testsuite/test.txt \
$(DISTNAME)/testsuite/test.txt.tar \ $(DISTNAME)/testsuite/test.txt.tar \
$(DISTNAME)/$(pkgname)rc \
$(DISTNAME)/*.h \ $(DISTNAME)/*.h \
$(DISTNAME)/*.cc \ $(DISTNAME)/*.cc \
$(DISTNAME)/z*.in $(DISTNAME)/z*.in

32
NEWS
View file

@ -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 Zutils now provides the runtime configuration file "zutilsrc", which
searching one file and "--recursive" has not been selected. 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 The use of "decompressed" and "uncompressed" in the documentation have
fixed. been revised.
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.

9
README
View file

@ -1,8 +1,8 @@
Description Description
Zutils is a collection of utilities able to deal with any combination of Zutils is a collection of utilities able to deal with any combination of
compressed and non-compressed files transparently. If any given file, compressed and uncompressed files transparently. If any given file,
including standard input, is compressed, its uncompressed content is including standard input, is compressed, its decompressed content is
used. Compressed files are decompressed on the fly; no temporary files used. Compressed files are decompressed on the fly; no temporary files
are created. are created.
@ -12,6 +12,7 @@ those utilities supporting it.
The provided utilities are zcat, zcmp, zdiff, zgrep and ztest. 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 Zcat, zcmp, zdiff, and zgrep are improved replacements for the shell
scripts provided with GNU gzip. Ztest is unique to zutils. 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 makes them safe to use with zutils. Gzip and xz may return ambiguous
warning values, making them less reliable backends for zutils. 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. Copyright (C) 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz.

18
configure vendored
View file

@ -6,9 +6,9 @@
# to copy, distribute and modify it. # to copy, distribute and modify it.
pkgname=zutils pkgname=zutils
pkgversion=1.0 pkgversion=1.1-rc2
progname=zutils progname=zutils
srctrigger=zutils.h srctrigger=${pkgname}rc
# clear some things potentially inherited from environment. # clear some things potentially inherited from environment.
LC_ALL=C LC_ALL=C
@ -20,6 +20,7 @@ bindir='$(exec_prefix)/bin'
datarootdir='$(prefix)/share' datarootdir='$(prefix)/share'
infodir='$(datarootdir)/info' infodir='$(datarootdir)/info'
mandir='$(datarootdir)/man' mandir='$(datarootdir)/man'
sysconfdir='$(prefix)/etc'
CXX=g++ CXX=g++
CPPFLAGS= CPPFLAGS=
CXXFLAGS='-Wall -W -O2' CXXFLAGS='-Wall -W -O2'
@ -66,6 +67,7 @@ while [ $# != 0 ] ; do
echo " --datarootdir=DIR base directory for doc and data [${datarootdir}]" echo " --datarootdir=DIR base directory for doc and data [${datarootdir}]"
echo " --infodir=DIR info files directory [${infodir}]" echo " --infodir=DIR info files directory [${infodir}]"
echo " --mandir=DIR man pages directory [${mandir}]" 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 " CXX=COMPILER C++ compiler to use [g++]"
echo " CPPFLAGS=OPTIONS command line options for the preprocessor [${CPPFLAGS}]" echo " CPPFLAGS=OPTIONS command line options for the preprocessor [${CPPFLAGS}]"
echo " CXXFLAGS=OPTIONS command line options for the C++ compiler [${CXXFLAGS}]" echo " CXXFLAGS=OPTIONS command line options for the C++ compiler [${CXXFLAGS}]"
@ -84,6 +86,7 @@ while [ $# != 0 ] ; do
--datarootdir) datarootdir=$1 ; arg2=yes ;; --datarootdir) datarootdir=$1 ; arg2=yes ;;
--infodir) infodir=$1 ; arg2=yes ;; --infodir) infodir=$1 ; arg2=yes ;;
--mandir) mandir=$1 ; arg2=yes ;; --mandir) mandir=$1 ; arg2=yes ;;
--sysconfdir) sysconfdir=$1 ; arg2=yes ;;
--srcdir=*) srcdir=${optarg} ;; --srcdir=*) srcdir=${optarg} ;;
--prefix=*) prefix=${optarg} ;; --prefix=*) prefix=${optarg} ;;
@ -92,6 +95,7 @@ while [ $# != 0 ] ; do
--datarootdir=*) datarootdir=${optarg} ;; --datarootdir=*) datarootdir=${optarg} ;;
--infodir=*) infodir=${optarg} ;; --infodir=*) infodir=${optarg} ;;
--mandir=*) mandir=${optarg} ;; --mandir=*) mandir=${optarg} ;;
--sysconfdir=*) sysconfdir=${optarg} ;;
--no-create) no_create=yes ;; --no-create) no_create=yes ;;
CXX=*) CXX=${optarg} ;; CXX=*) CXX=${optarg} ;;
@ -106,7 +110,7 @@ while [ $# != 0 ] ; do
*=* | *-*-*) ;; *=* | *-*-*) ;;
*) *)
echo "configure: unrecognized option: '${option}'" 1>&2 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 ;; exit 1 ;;
esac esac
@ -131,10 +135,8 @@ if [ -z "${srcdir}" ] ; then
fi fi
if [ ! -r "${srcdir}/${srctrigger}" ] ; then if [ ! -r "${srcdir}/${srctrigger}" ] ; then
exec 1>&2 echo "configure: Can't find sources in ${srcdir} ${srcdirtext}" 1>&2
echo echo "configure: (At least ${srctrigger} is missing)." 1>&2
echo "configure: Can't find sources in ${srcdir} ${srcdirtext}"
echo "configure: (At least ${srctrigger} is missing)."
exit 1 exit 1
fi fi
@ -166,6 +168,7 @@ echo "bindir = ${bindir}"
echo "datarootdir = ${datarootdir}" echo "datarootdir = ${datarootdir}"
echo "infodir = ${infodir}" echo "infodir = ${infodir}"
echo "mandir = ${mandir}" echo "mandir = ${mandir}"
echo "sysconfdir = ${sysconfdir}"
echo "CXX = ${CXX}" echo "CXX = ${CXX}"
echo "CPPFLAGS = ${CPPFLAGS}" echo "CPPFLAGS = ${CPPFLAGS}"
echo "CXXFLAGS = ${CXXFLAGS}" echo "CXXFLAGS = ${CXXFLAGS}"
@ -191,6 +194,7 @@ bindir = ${bindir}
datarootdir = ${datarootdir} datarootdir = ${datarootdir}
infodir = ${infodir} infodir = ${infodir}
mandir = ${mandir} mandir = ${mandir}
sysconfdir = ${sysconfdir}
CXX = ${CXX} CXX = ${CXX}
CPPFLAGS = ${CPPFLAGS} CPPFLAGS = ${CPPFLAGS}
CXXFLAGS = ${CXXFLAGS} CXXFLAGS = ${CXXFLAGS}

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1. .\" 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 .SH NAME
Zcat \- decompress and concatenate files to standard output Zcat \- decompress and concatenate files to standard output
.SH SYNOPSIS .SH SYNOPSIS
@ -7,7 +7,7 @@ Zcat \- decompress and concatenate files to standard output
[\fIoptions\fR] [\fIfiles\fR] [\fIoptions\fR] [\fIfiles\fR]
.SH DESCRIPTION .SH DESCRIPTION
Zcat copies each given file ("\-" means standard input), to standard 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 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 of the known extensions, zcat tries the compressed file names
corresponding to the supported formats. If no files are specified, corresponding to the supported formats. If no files are specified,
@ -17,7 +17,7 @@ all uncompressed or all in the same compression format.
.PP .PP
The supported formats are bzip2, gzip, lzip and xz. The supported formats are bzip2, gzip, lzip and xz.
.PP .PP
Exit status is 0 if no errors occurred, 1 otherwise. Exit status is 0 if no errors occurred, non\-zero otherwise.
.SH OPTIONS .SH OPTIONS
.TP .TP
\fB\-h\fR, \fB\-\-help\fR \fB\-h\fR, \fB\-\-help\fR
@ -44,6 +44,9 @@ force given format (bz2, gz, lz, xz)
\fB\-n\fR, \fB\-\-number\fR \fB\-n\fR, \fB\-\-number\fR
number all output lines number all output lines
.TP .TP
\fB\-N\fR, \fB\-\-no\-rcfile\fR
don't read runtime configuration file
.TP
\fB\-q\fR, \fB\-\-quiet\fR \fB\-q\fR, \fB\-\-quiet\fR
suppress all messages suppress all messages
.TP .TP
@ -64,6 +67,18 @@ use '^' and 'M\-' notation, except for LF and TAB
.TP .TP
\fB\-\-verbose\fR \fB\-\-verbose\fR
verbose mode (show error messages) 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" .SH "REPORTING BUGS"
Report bugs to zutils\-bug@nongnu.org Report bugs to zutils\-bug@nongnu.org
.br .br

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1. .\" 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 .SH NAME
Zcmp \- decompress and compare two files byte by byte Zcmp \- decompress and compare two files byte by byte
.SH SYNOPSIS .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 Zcmp compares two files ("\-" means standard input), and if they
differ, tells the first byte and line number where they differ. Bytes 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, 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. the fly; no temporary files are created.
.PP .PP
The supported formats are bzip2, gzip, lzip and xz. The supported formats are bzip2, gzip, lzip and xz.
.PP .PP
Compares <file1> to <file2>. If <file2> is omitted zcmp tries the Compares <file1> to <file2>. If <file2> is omitted zcmp tries the
following: following:
If <file1> is compressed, compares <file1> to the file with the .IP
corresponding decompressed file name (removes the extension from 1. If <file1> is compressed, compares its decompressed contents with
<file1>). the corresponding uncompressed file (the name of <file1> with the
If <file1> is not compressed, compares <file1> to the uncompressed 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). 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. standard input.
.PP .PP
Exit status is 0 if inputs are identical, 1 if different, 2 if trouble. 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> \fB\-n\fR, \fB\-\-bytes=\fR<n>
compare at most <n> bytes compare at most <n> bytes
.TP .TP
\fB\-N\fR, \fB\-\-no\-rcfile\fR
don't read runtime configuration file
.TP
\fB\-q\fR, \fB\-\-quiet\fR \fB\-q\fR, \fB\-\-quiet\fR
suppress all messages suppress all messages
.TP .TP
@ -56,6 +62,18 @@ suppress all messages
.TP .TP
\fB\-v\fR, \fB\-\-verbose\fR \fB\-v\fR, \fB\-\-verbose\fR
verbose mode (same as \fB\-\-list\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 .PP
Numbers may be followed by a multiplier: k = kB = 10^3 = 1000, 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... Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1. .\" 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 .SH NAME
Zdiff \- decompress and compare two files line by line Zdiff \- decompress and compare two files line by line
.SH SYNOPSIS .SH SYNOPSIS
@ -8,7 +8,7 @@ Zdiff \- decompress and compare two files line by line
.SH DESCRIPTION .SH DESCRIPTION
Zdiff compares two files ("\-" means standard input), and if they Zdiff compares two files ("\-" means standard input), and if they
differ, shows the differences line by line. If any given file is 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 the diff program and has the limitation that messages from diff refer to
temporary filenames instead of those specified. temporary filenames instead of those specified.
.PP .PP
@ -16,12 +16,15 @@ The supported formats are bzip2, gzip, lzip and xz.
.PP .PP
Compares <file1> to <file2>. If <file2> is omitted zdiff tries the Compares <file1> to <file2>. If <file2> is omitted zdiff tries the
following: following:
If <file1> is compressed, compares <file1> to the file with the .IP
corresponding decompressed file name (removes the extension from 1. If <file1> is compressed, compares its decompressed contents with
<file1>). the corresponding uncompressed file (the name of <file1> with the
If <file1> is not compressed, compares <file1> to the uncompressed 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). 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. standard input.
.PP .PP
Exit status is 0 if inputs are identical, 1 if different, 2 if trouble. 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 \fB\-i\fR, \fB\-\-ignore\-case\fR
ignore case differences in file contents ignore case differences in file contents
.TP .TP
\fB\-N\fR, \fB\-\-no\-rcfile\fR
don't read runtime configuration file
.TP
\fB\-p\fR, \fB\-\-show\-c\-function\fR \fB\-p\fR, \fB\-\-show\-c\-function\fR
show which C function each change is in show which C function each change is in
.TP .TP
@ -83,6 +89,18 @@ same as \fB\-u\fR but use <n> lines of context
.TP .TP
\fB\-w\fR, \fB\-\-ignore\-all\-space\fR \fB\-w\fR, \fB\-\-ignore\-all\-space\fR
ignore all white space 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 .PP
Numbers may be followed by a multiplier: k = kB = 10^3 = 1000, 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... Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1. .\" 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 .SH NAME
Zgrep \- search compressed files for a regular expression Zgrep \- search compressed files for a regular expression
.SH SYNOPSIS .SH SYNOPSIS
@ -7,8 +7,8 @@ Zgrep \- search compressed files for a regular expression
[\fIoptions\fR] \fI<pattern> \fR[\fIfiles\fR] [\fIoptions\fR] \fI<pattern> \fR[\fIfiles\fR]
.SH DESCRIPTION .SH DESCRIPTION
Zgrep is a front end to the grep program that allows transparent search 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 on any combination of compressed and uncompressed files. If any given
file is compressed, its uncompressed content is used. If a given file 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 does not exist, and its name does not end with one of the known
extensions, zgrep tries the compressed file names corresponding to the extensions, zgrep tries the compressed file names corresponding to the
supported formats. If no files are specified, data is read from 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 \fB\-n\fR, \fB\-\-line\-number\fR
print the line number of each line print the line number of each line
.TP .TP
\fB\-N\fR, \fB\-\-no\-rcfile\fR
don't read runtime configuration file
.TP
\fB\-o\fR, \fB\-\-only\-matching\fR \fB\-o\fR, \fB\-\-only\-matching\fR
show only the part of a line matching <pattern> show only the part of a line matching <pattern>
.TP .TP
@ -107,6 +110,18 @@ match only whole words
.TP .TP
\fB\-x\fR, \fB\-\-line\-regexp\fR \fB\-x\fR, \fB\-\-line\-regexp\fR
match only whole lines 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" .SH "REPORTING BUGS"
Report bugs to zutils\-bug@nongnu.org Report bugs to zutils\-bug@nongnu.org
.br .br

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1. .\" 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 .SH NAME
Ztest \- verify integrity of compressed files Ztest \- verify integrity of compressed files
.SH SYNOPSIS .SH SYNOPSIS
@ -7,9 +7,9 @@ Ztest \- verify integrity of compressed files
[\fIoptions\fR] [\fIfiles\fR] [\fIoptions\fR] [\fIfiles\fR]
.SH DESCRIPTION .SH DESCRIPTION
Ztest verifies the integrity of the specified compressed files. Ztest verifies the integrity of the specified compressed files.
Non\-compressed files are ignored. If no files are specified, the Uncompressed files are ignored. If no files are specified, the integrity
integrity of compressed data read from standard input is verified. Data of compressed data read from standard input is verified. Data read from
read from standard input must be all in the same compression format. standard input must be all in the same compression format.
.PP .PP
The supported formats are bzip2, gzip, lzip and xz. The supported formats are bzip2, gzip, lzip and xz.
.PP .PP
@ -30,6 +30,9 @@ output version information and exit
\fB\-\-format=\fR<fmt> \fB\-\-format=\fR<fmt>
force given format (bz2, gz, lz, xz) force given format (bz2, gz, lz, xz)
.TP .TP
\fB\-N\fR, \fB\-\-no\-rcfile\fR
don't read runtime configuration file
.TP
\fB\-q\fR, \fB\-\-quiet\fR \fB\-q\fR, \fB\-\-quiet\fR
suppress all messages suppress all messages
.TP .TP
@ -38,6 +41,18 @@ operate recursively on directories
.TP .TP
\fB\-v\fR, \fB\-\-verbose\fR \fB\-v\fR, \fB\-\-verbose\fR
be verbose (a 2nd \fB\-v\fR gives more) 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" .SH "REPORTING BUGS"
Report bugs to zutils\-bug@nongnu.org Report bugs to zutils\-bug@nongnu.org
.br .br

View file

@ -12,18 +12,20 @@ File: zutils.info, Node: Top, Next: Introduction, Up: (dir)
Zutils Manual 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: * Menu:
* Introduction:: Purpose and features of zutils * Introduction:: Purpose and features of zutils
* Common options:: Common options
* The zutilsrc file:: The zutils configuration file
* Zcat:: Concatenating compressed files * Zcat:: Concatenating compressed files
* Zcmp:: Comparing compressed files byte by byte * Zcmp:: Comparing compressed files byte by byte
* Zdiff:: Comparing compressed files line by line * Zdiff:: Comparing compressed files line by line
* Zgrep:: Searching inside compressed files * Zgrep:: Searching inside compressed files
* Ztest:: Testing integrity of compressed files * Ztest:: Testing integrity of compressed files
* Problems:: Reporting bugs * Problems:: Reporting bugs
* Concept Index:: Index of concepts * Concept index:: Index of concepts
Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz. 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. 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 1 Introduction
************** **************
Zutils is a collection of utilities able to deal with any combination of Zutils is a collection of utilities able to deal with any combination of
compressed and non-compressed files transparently. If any given file, compressed and uncompressed files transparently. If any given file,
including standard input, is compressed, its uncompressed content is including standard input, is compressed, its decompressed content is
used. Compressed files are decompressed on the fly; no temporary files used. Compressed files are decompressed on the fly; no temporary files
are created. are created.
@ -49,6 +51,7 @@ in those utilities supporting it.
The provided utilities are zcat, zcmp, zdiff, zgrep and ztest. 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 Zcat, zcmp, zdiff, and zgrep are improved replacements for the shell
scripts provided with GNU gzip. Ztest is unique to zutils. 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 which makes them safe to use with zutils. Gzip and xz may return
ambiguous warning values, making them less reliable backends for zutils. 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 Numbers given as arguments to options (positions, sizes) may be
followed by a multiplier and an optional `B' for "byte". 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) 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 Zcat 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.

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 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 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 of the known extensions, zcat tries the compressed file names
corresponding to the supported formats. corresponding to the supported formats.
@ -94,18 +169,10 @@ same compression format.
zcat [OPTIONS] [FILES] 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: 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' `-A'
`--show-all' `--show-all'
Equivalent to `-vET'. 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 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, Zcmp compares two files (`-' means standard input), and if they differ,
tells the first byte and line number where they differ. Bytes and lines 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 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. fly; no temporary files are created.
The format for running zcmp is: 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 This compares FILE1 to FILE2. If FILE2 is omitted zcmp tries the
following: following:
1. If FILE1 is compressed, compares FILE1 to the file with the 1. If FILE1 is compressed, compares its decompressed contents with
corresponding decompressed file name (removes the extension from the corresponding uncompressed file (the name of FILE1 with the
FILE1). 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). 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. standard input.
An exit status of 0 means no differences were found, 1 means some 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: 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' `-b'
`--print-bytes' `--print-bytes'
Print the differing bytes. Print control bytes as a `^' followed by 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 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 Zdiff compares two files (`-' means standard input), and if they
differ, shows the differences line by line. If any given file is 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 the diff program and has the limitation that messages from diff refer to
temporary filenames instead of those specified. 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 This compares FILE1 to FILE2. If FILE2 is omitted zdiff tries the
following: following:
1. If FILE1 is compressed, compares FILE1 to the file with the 1. If FILE1 is compressed, compares its decompressed contents with
corresponding decompressed file name (removes the extension from the corresponding uncompressed file (the name of FILE1 with the
FILE1). 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). 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. standard input.
An exit status of 0 means no differences were found, 1 means some 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: 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' `-a'
`--text' `--text'
Treat all files as 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 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 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 on any combination of compressed and uncompressed files. If any given
file is compressed, its uncompressed content is used. If a given file 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 does not exist, and its name does not end with one of the known
extensions, zgrep tries the compressed file names corresponding to the extensions, zgrep tries the compressed file names corresponding to the
supported formats. supported formats.
@ -384,13 +435,6 @@ matches were found, and 2 means trouble.
Zgrep supports the following options: 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' `-a'
`--text' `--text'
Treat all files as 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 File: zutils.info, Node: Ztest, Next: Problems, Prev: Zgrep, Up: Top
6 Ztest 8 Ztest
******* *******
Ztest verifies the integrity of the specified compressed files. Ztest verifies the integrity of the specified compressed files.
Non-compressed files are ignored. If no files are specified, the Uncompressed files are ignored. If no files are specified, the integrity
integrity of compressed data read from standard input is verified. Data of compressed data read from standard input is verified. Data read from
read from standard input must be all in the same compression format. standard input must be all in the same compression format.
Note that some xz files lack integrity information, and therefore Note that some xz files lack integrity information, and therefore
can't be verified as reliably as the other formats can. 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: 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' `--format=FMT'
Force the given compression format. Valid values for FMT are Force the given compression format. Valid values for FMT are
`bz2', `gz', `lz' and `xz'. If this option is used, all files not `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 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'. 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
************* *************
[index] [index]
* Menu: * Menu:
* bugs: Problems. (line 6) * bugs: Problems. (line 6)
* common options: Common options. (line 6)
* getting help: Problems. (line 6) * getting help: Problems. (line 6)
* introduction: Introduction. (line 6) * introduction: Introduction. (line 6)
* the zutilsrc file: The zutilsrc file. (line 6)
* zcat: Zcat. (line 6) * zcat: Zcat. (line 6)
* zcmp: Zcmp. (line 6) * zcmp: Zcmp. (line 6)
* zdiff: Zdiff. (line 6) * zdiff: Zdiff. (line 6)
@ -588,14 +626,16 @@ Concept Index
 
Tag Table: Tag Table:
Node: Top224 Node: Top224
Node: Introduction1000 Node: Introduction1097
Node: Zcat2794 Node: Common options3155
Node: Zcmp4880 Node: The zutilsrc file4404
Node: Zdiff7366 Node: Zcat5330
Node: Zgrep10032 Node: Zcmp7252
Node: Ztest13043 Node: Zdiff9576
Node: Problems14447 Node: Zgrep12079
Node: Concept Index14976 Node: Ztest14916
Node: Problems16141
Node: Concept index16670
 
End Tag Table End Tag Table

View file

@ -6,8 +6,8 @@
@finalout @finalout
@c %**end of header @c %**end of header
@set UPDATED 31 May 2013 @set UPDATED 7 July 2013
@set VERSION 1.0 @set VERSION 1.1-rc2
@dircategory Data Compression @dircategory Data Compression
@direntry @direntry
@ -36,13 +36,15 @@ This manual is for Zutils (version @value{VERSION}, @value{UPDATED}).
@menu @menu
* Introduction:: Purpose and features of zutils * Introduction:: Purpose and features of zutils
* Common options:: Common options
* The zutilsrc file:: The zutils configuration file
* Zcat:: Concatenating compressed files * Zcat:: Concatenating compressed files
* Zcmp:: Comparing compressed files byte by byte * Zcmp:: Comparing compressed files byte by byte
* Zdiff:: Comparing compressed files line by line * Zdiff:: Comparing compressed files line by line
* Zgrep:: Searching inside compressed files * Zgrep:: Searching inside compressed files
* Ztest:: Testing integrity of compressed files * Ztest:: Testing integrity of compressed files
* Problems:: Reporting bugs * Problems:: Reporting bugs
* Concept Index:: Index of concepts * Concept index:: Index of concepts
@end menu @end menu
@sp 1 @sp 1
@ -58,8 +60,8 @@ to copy, distribute and modify it.
@cindex introduction @cindex introduction
Zutils is a collection of utilities able to deal with any combination of Zutils is a collection of utilities able to deal with any combination of
compressed and non-compressed files transparently. If any given file, compressed and uncompressed files transparently. If any given file,
including standard input, is compressed, its uncompressed content is including standard input, is compressed, its decompressed content is
used. Compressed files are decompressed on the fly; no temporary files used. Compressed files are decompressed on the fly; no temporary files
are created. are created.
@ -69,7 +71,8 @@ in those utilities supporting it.
@noindent @noindent
The provided utilities are zcat, zcmp, zdiff, zgrep and ztest.@* 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 Zcat, zcmp, zdiff, and zgrep are improved replacements for the shell
scripts provided with GNU gzip. Ztest is unique to zutils. 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 makes them safe to use with zutils. Gzip and xz may return ambiguous
warning values, making them less reliable backends for zutils. 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 @sp 1
Numbers given as arguments to options (positions, sizes) may be followed Numbers given as arguments to options (positions, sizes) may be followed
by a multiplier and an optional @samp{B} for "byte". by a multiplier and an optional @samp{B} for "byte".
@ -97,12 +104,85 @@ Table of SI and binary prefixes (unit multipliers):
@end multitable @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 @node Zcat
@chapter Zcat @chapter Zcat
@cindex zcat @cindex zcat
Zcat copies each given file (@samp{-} means standard input), to standard 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 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 of the known extensions, zcat tries the compressed file names
corresponding to the supported formats. corresponding to the supported formats.
@ -119,19 +199,11 @@ zcat [@var{options}] [@var{files}]
@end example @end example
@noindent @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: Zcat supports the following options:
@table @samp @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 @item -A
@itemx --show-all @itemx --show-all
Equivalent to @samp{-vET}. Equivalent to @samp{-vET}.
@ -195,7 +267,7 @@ Verbose mode. Show error messages.
Zcmp compares two files (@samp{-} means standard input), and if they Zcmp compares two files (@samp{-} means standard input), and if they
differ, tells the first byte and line number where they differ. Bytes 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, 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 fly; no temporary files are created.
The format for running zcmp is: The format for running zcmp is:
@ -210,15 +282,14 @@ tries the following:
@enumerate @enumerate
@item @item
If @var{file1} is compressed, compares @var{file1} to the file with the If @var{file1} is compressed, compares its decompressed contents with
corresponding decompressed file name (removes the extension from the corresponding uncompressed file (the name of @var{file1} with the
@var{file1}). extension removed).
@item @item
If @var{file1} is not compressed, compares @var{file1} to the If @var{file1} is uncompressed, compares it with the decompressed
uncompressed contents of @var{file1}.[lz|bz2|gz|xz] (the first one that contents of @var{file1}.[lz|bz2|gz|xz] (the first one that is found).
is found).
@item @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. standard input.
@end enumerate @end enumerate
@ -229,14 +300,6 @@ differences were found, and 2 means trouble.
Zcmp supports the following options: Zcmp supports the following options:
@table @samp @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 @item -b
@itemx --print-bytes @itemx --print-bytes
Print the differing bytes. Print control bytes as a @samp{^} followed by 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 Zdiff compares two files (@samp{-} means standard input), and if they
differ, shows the differences line by line. If any given file is 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 the diff program and has the limitation that messages from diff refer to
temporary filenames instead of those specified. temporary filenames instead of those specified.
@ -302,15 +365,14 @@ zdiff tries the following:
@enumerate @enumerate
@item @item
If @var{file1} is compressed, compares @var{file1} to the file with the If @var{file1} is compressed, compares its decompressed contents with
corresponding decompressed file name (removes the extension from the corresponding uncompressed file (the name of @var{file1} with the
@var{file1}). extension removed).
@item @item
If @var{file1} is not compressed, compares @var{file1} to the If @var{file1} is uncompressed, compares it with the decompressed
uncompressed contents of @var{file1}.[lz|bz2|gz|xz] (the first one that contents of @var{file1}.[lz|bz2|gz|xz] (the first one that is found).
is found).
@item @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. standard input.
@end enumerate @end enumerate
@ -321,14 +383,6 @@ differences were found, and 2 means trouble.
Zdiff supports the following options: Zdiff supports the following options:
@table @samp @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 @item -a
@itemx --text @itemx --text
Treat all files as text. Treat all files as text.
@ -407,8 +461,8 @@ Ignore all white space.
@cindex zgrep @cindex zgrep
Zgrep is a front end to the grep program that allows transparent search 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 on any combination of compressed and uncompressed files. If any given
file is compressed, its uncompressed content is used. If a given file 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 does not exist, and its name does not end with one of the known
extensions, zgrep tries the compressed file names corresponding to the extensions, zgrep tries the compressed file names corresponding to the
supported formats. supported formats.
@ -431,13 +485,6 @@ matches were found, and 2 means trouble.
Zgrep supports the following options: Zgrep supports the following options:
@table @samp @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 @item -a
@itemx --text @itemx --text
Treat all files as text. Treat all files as text.
@ -554,9 +601,9 @@ Match only whole lines.
@cindex ztest @cindex ztest
Ztest verifies the integrity of the specified compressed files. Ztest verifies the integrity of the specified compressed files.
Non-compressed files are ignored. If no files are specified, the Uncompressed files are ignored. If no files are specified, the integrity
integrity of compressed data read from standard input is verified. Data of compressed data read from standard input is verified. Data read from
read from standard input must be all in the same compression format. standard input must be all in the same compression format.
Note that some xz files lack integrity information, and therefore can't Note that some xz files lack integrity information, and therefore can't
be verified as reliably as the other formats can. 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: Ztest supports the following options:
@table @samp @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} @item --format=@var{fmt}
Force the given compression format. Valid values for @var{fmt} are 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, @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}}. find by running @w{@samp{zutils --version}}.
@node Concept Index @node Concept index
@unnumbered Concept Index @unnumbered Concept index
@printindex cp @printindex cp

50
main.cc
View file

@ -37,6 +37,7 @@
#include "arg_parser.h" #include "arg_parser.h"
#include "zutils.h" #include "zutils.h"
#include "rc.h"
#if CHAR_BIT != 8 #if CHAR_BIT != 8
#error "Environments where CHAR_BIT != 8 are not supported." #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() void show_help()
{ {
std::printf( "Zutils is a collection of utilities able to deal with any combination of\n" 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" "compressed and uncompressed files transparently. If any given file,\n"
"including standard input, is compressed, its uncompressed content is used.\n" "including standard input, is compressed, its decompressed content is used.\n"
"\nThe supported formats are bzip2, gzip, lzip and xz.\n" "\nThe supported formats are bzip2, gzip, lzip and xz.\n"
"\nUsage: %s <operation> [options] [files]\n", invocation_name ); "\nUsage: %s <operation> [options] [files]\n", invocation_name );
std::printf( "\nTry '%s <operation> --help' for more specific help.\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[] ) int main( const int argc, const char * const argv[] )
{ {
enum { format_opt = 256, help_opt, verbose_opt, enum { format_opt = 256, help_opt, verbose_opt,
bz2_opt, gz_opt, lz_opt, xz_opt,
zcat_opt, zgrep_opt, ztest_opt }; zcat_opt, zgrep_opt, ztest_opt };
const Arg_parser::Option * options = 0; const Arg_parser::Option * options = 0;
int infd = -1; int infd = -1;
int format_type = -1; int format_index = -1;
Mode program_mode = m_none; Mode program_mode = m_none;
bool recursive = false; bool recursive = false;
std::string input_filename; std::string input_filename;
@ -144,6 +146,7 @@ int main( const int argc, const char * const argv[] )
{ 'l', "list", Arg_parser::no }, // gzip { 'l', "list", Arg_parser::no }, // gzip
{ 'L', "license", Arg_parser::no }, // gzip { 'L', "license", Arg_parser::no }, // gzip
{ 'n', "number", Arg_parser::no }, // cat { 'n', "number", Arg_parser::no }, // cat
{ 'N', "no-rcfile", Arg_parser::no },
{ 'q', "quiet", Arg_parser::no }, { 'q', "quiet", Arg_parser::no },
{ 'r', "recursive", Arg_parser::no }, { 'r', "recursive", Arg_parser::no },
{ 's', "squeeze-blank", Arg_parser::no }, // cat { '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 }, { 'V', "version", Arg_parser::no },
{ format_opt, "format", Arg_parser::yes }, { format_opt, "format", Arg_parser::yes },
{ verbose_opt, "verbose", 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 },
{ zcat_opt, "zcat", Arg_parser::no }, { zcat_opt, "zcat", Arg_parser::no },
{ 0 , 0, 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 { 'L', "files-without-match", Arg_parser::no }, // grep GNU
{ 'm', "max-count", Arg_parser::yes }, // grep GNU { 'm', "max-count", Arg_parser::yes }, // grep GNU
{ 'n', "line-number", Arg_parser::no }, // grep { 'n', "line-number", Arg_parser::no }, // grep
{ 'N', "no-rcfile", Arg_parser::no },
{ 'o', "only-matching", Arg_parser::no }, // grep { 'o', "only-matching", Arg_parser::no }, // grep
{ 'q', "quiet", Arg_parser::no }, { 'q', "quiet", Arg_parser::no },
{ 'r', "recursive", 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 }, { format_opt, "format", Arg_parser::yes },
{ help_opt, "help", Arg_parser::no }, { help_opt, "help", Arg_parser::no },
{ verbose_opt, "verbose", 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 }, { zgrep_opt, "zgrep", Arg_parser::no },
{ 0 , 0, Arg_parser::no } }; { 0 , 0, Arg_parser::no } };
const Arg_parser::Option m_ztest_options[] = const Arg_parser::Option m_ztest_options[] =
{ {
{ 'h', "help", Arg_parser::no }, { 'h', "help", Arg_parser::no },
{ 'N', "no-rcfile", Arg_parser::no },
{ 'q', "quiet", Arg_parser::no }, { 'q', "quiet", Arg_parser::no },
{ 'r', "recursive", Arg_parser::no }, { 'r', "recursive", Arg_parser::no },
{ 'v', "verbose", Arg_parser::no }, { 'v', "verbose", Arg_parser::no },
{ 'V', "version", Arg_parser::no }, { 'V', "version", Arg_parser::no },
{ format_opt, "format", Arg_parser::yes }, { 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 }, { ztest_opt, "ztest", Arg_parser::no },
{ 0 , 0, Arg_parser::no } }; { 0 , 0, Arg_parser::no } };
@ -244,10 +261,12 @@ int main( const int argc, const char * const argv[] )
} }
} // end parse operation } // end parse operation
const int eretval = ( program_mode == m_zgrep ) ? 2 : 1;
const Arg_parser parser( argc, argv, options ); const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option if( parser.error().size() ) // bad option
{ show_error( parser.error().c_str(), 0, true ); { show_error( parser.error().c_str(), 0, true ); return eretval; }
return ( program_mode == m_zcat || program_mode == m_ztest ) ? 1 : 2; }
maybe_process_config_file( parser );
int argind = 0; int argind = 0;
int grep_show_name = -1; 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; } { grep_args.push_back( arg ); grep_pattern_found = true; continue; }
else break; // no more options 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 ) switch( program_mode )
{ {
@ -396,7 +420,7 @@ int main( const int argc, const char * const argv[] )
continue; 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; } 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: case m_none:
break; break;
case m_zcat: case m_zcat:
tmp = cat( infd, format_type, input_filename, cat_options ); tmp = cat( infd, format_index, input_filename, cat_options );
break; break;
case m_zgrep: case m_zgrep:
if( infd == STDIN_FILENO ) if( infd == STDIN_FILENO )
tmp = zgrep_stdin( infd, format_type, grep_args ); tmp = zgrep_stdin( infd, format_index, grep_args );
else tmp = zgrep_file( infd, format_type, input_filename, grep_args, else tmp = zgrep_file( infd, format_index, input_filename, grep_args,
grep_list_mode, grep_show_name ); grep_list_mode, grep_show_name );
break; break;
case m_ztest: case m_ztest:
if( infd == STDIN_FILENO ) if( infd == STDIN_FILENO )
tmp = ztest_stdin( infd, format_type, ztest_args ); tmp = ztest_stdin( infd, format_index, ztest_args );
else tmp = ztest_file( infd, format_type, input_filename, ztest_args ); else tmp = ztest_file( infd, format_index, input_filename, ztest_args );
break; break;
} }
if( program_mode == m_zgrep ) if( program_mode == m_zgrep )

229
rc.cc Normal file
View file

@ -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];
}

27
rc.h Normal file
View file

@ -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 );

View file

@ -50,280 +50,293 @@ printf "testing zutils-%s..." "$2"
printf "\ntesting zcat-%s..." "$2" printf "\ntesting zcat-%s..." "$2"
for i in ${extensions}; do for i in ${extensions}; do
"${ZCAT}" in.$i > copy || fail=1 "${ZCAT}" -N in.$i > copy || fail=1
cmp in copy || fail=1 cmp in copy || fail=1
printf . printf .
"${ZCAT}" --format=$i in.$i > copy || fail=1 "${ZCAT}" -N --format=$i in.$i > copy || fail=1
cmp in copy || fail=1 cmp in copy || fail=1
printf . 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 dd if=in bs=1000 count=1 2> /dev/null | cmp - copy || fail=1
printf . printf .
done done
"${ZCAT}" < in > copy || fail=1 "${ZCAT}" -N < in > copy || fail=1
cmp in copy || fail=1 cmp in copy || fail=1
printf . printf .
"${ZCAT}" < in.gz > copy || fail=1 "${ZCAT}" -N < in.gz > copy || fail=1
cmp in copy || fail=1 cmp in copy || fail=1
printf . printf .
"${ZCAT}" < in.bz2 > copy || fail=1 "${ZCAT}" -N < in.bz2 > copy || fail=1
cmp in copy || fail=1 cmp in copy || fail=1
printf . printf .
"${ZCAT}" < in.lz > copy || fail=1 "${ZCAT}" -N < in.lz > copy || fail=1
cmp in copy || fail=1 cmp in copy || fail=1
printf . printf .
"${ZCAT}" in > copy || fail=1 "${ZCAT}" -N --lz='lzip -q' < in.lz > copy || fail=1
cmp in copy || fail=1 cmp in copy || fail=1
printf . printf .
"${ZCAT}" lz_only > copy || fail=1 "${ZCAT}" -N in > copy || fail=1
cmp in copy || fail=1 cmp in copy || fail=1
printf . 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 cmp in6 copy6 || fail=1
printf . 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 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 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 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 if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
printf "\ntesting zcmp-%s..." "$2" printf "\ntesting zcmp-%s..." "$2"
for i in ${extensions}; do for i in ${extensions}; do
"${ZCMP}" in.$i || fail=1 "${ZCMP}" -N in.$i || fail=1
printf . printf .
"${ZCMP}" in in.$i || fail=1 "${ZCMP}" -N in in.$i || fail=1
printf . printf .
"${ZCMP}" -i 100 -n 500 in6 in.$i || fail=1 "${ZCMP}" -N -i 100 -n 500 in6 in.$i || fail=1
printf . printf .
"${ZCMP}" in in.$i --format=,$i || fail=1 "${ZCMP}" -N in in.$i --format=,$i || fail=1
printf . printf .
"${ZCMP}" in.$i in || fail=1 "${ZCMP}" -N in.$i in || fail=1
printf . 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 . printf .
"${ZCMP}" in.$i in --format=$i || fail=1 "${ZCMP}" -N in.$i in --format=$i || fail=1
printf . printf .
done done
"${ZCMP}" in in6 2> /dev/null "${ZCMP}" -N in in6 2> /dev/null
if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
"${ZCMP}" -n 0 in in6 || fail=1 "${ZCMP}" -N -n 0 in in6 || fail=1
"${ZCMP}" -n 100 in in6 || fail=1 "${ZCMP}" -N -n 100 in in6 || fail=1
"${ZCMP}" -n 1000 in in6 || fail=1 "${ZCMP}" -N -n 1000 in in6 || fail=1
"${ZCMP}" -n 10000 in in6 || fail=1 "${ZCMP}" -N -n 10000 in in6 || fail=1
printf . 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 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 if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
"${ZCMP}" -i 0,11 -n 0 in.tar pin.tar || fail=1 "${ZCMP}" -N -i 0,11 -n 0 in.tar pin.tar || fail=1
"${ZCMP}" -i 0,11 -n 100 in.tar pin.tar || fail=1 "${ZCMP}" -N -i 0,11 -n 100 in.tar pin.tar || fail=1
"${ZCMP}" -i 0,11 -n 1000 in.tar pin.tar || fail=1 "${ZCMP}" -N -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 10000 in.tar pin.tar || fail=1
printf . printf .
"${ZCMP}" - || fail=1 "${ZCMP}" -N - || fail=1
printf . printf .
"${ZCMP}" in in || fail=1 "${ZCMP}" -N in in || fail=1
printf . printf .
"${ZCMP}" in || fail=1 "${ZCMP}" -N in || fail=1
printf . printf .
"${ZCMP}" in.lz in.gz || fail=1 "${ZCMP}" -N in.lz in.gz || fail=1
printf . printf .
"${ZCMP}" in.gz -- -in-.lz || fail=1 "${ZCMP}" -N --lz='lzip -q' in.lz in.gz || fail=1
printf . printf .
"${ZCMP}" -- -in-.lz in.gz || fail=1 "${ZCMP}" -N in.gz -- -in-.lz || fail=1
printf . printf .
"${ZCMP}" in -- -in-.lz || fail=1 "${ZCMP}" -N -- -in-.lz in.gz || fail=1
printf . printf .
"${ZCMP}" -- -in- in.lz || fail=1 "${ZCMP}" -N in -- -in-.lz || fail=1
printf . printf .
"${ZCMP}" in.lz -- -in- || fail=1 "${ZCMP}" -N -- -in- in.lz || fail=1
printf . printf .
"${ZCMP}" -- -in-.lz in || fail=1 "${ZCMP}" -N in.lz -- -in- || fail=1
printf . printf .
"${ZCMP}" -- -in- in || fail=1 "${ZCMP}" -N -- -in-.lz in || fail=1
printf . printf .
"${ZCMP}" in -- -in- || fail=1 "${ZCMP}" -N -- -in- in || fail=1
printf . printf .
"${ZCMP}" lz_only.lz < in || fail=1 "${ZCMP}" -N in -- -in- || fail=1
printf . printf .
"${ZCMP}" in.lz - < in || fail=1 "${ZCMP}" -N lz_only.lz < in || fail=1
printf . printf .
"${ZCMP}" - in.lz < in || fail=1 "${ZCMP}" -N in.lz - < in || fail=1
printf . printf .
"${ZCMP}" in - < in.lz || fail=1 "${ZCMP}" -N - in.lz < in || fail=1
printf . printf .
"${ZCMP}" - in < in.lz || fail=1 "${ZCMP}" -N in - < in.lz || fail=1
printf . 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 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 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 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 if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
printf "\ntesting zdiff-%s..." "$2" printf "\ntesting zdiff-%s..." "$2"
for i in ${extensions}; do for i in ${extensions}; do
"${ZDIFF}" in.$i || fail=1 "${ZDIFF}" -N in.$i > /dev/null || fail=1
printf . printf .
"${ZDIFF}" in in.$i || fail=1 "${ZDIFF}" -N in in.$i > /dev/null || fail=1
printf . printf .
"${ZDIFF}" --format=,$i in in.$i || fail=1 "${ZDIFF}" -N --format=,$i in in.$i > /dev/null || fail=1
printf . printf .
"${ZDIFF}" in.$i in || fail=1 "${ZDIFF}" -N in.$i in > /dev/null || fail=1
printf . printf .
"${ZDIFF}" --format=$i, in.$i in || fail=1 "${ZDIFF}" -N --format=$i, in.$i in > /dev/null || fail=1
printf . printf .
done done
"${ZDIFF}" in in6 > /dev/null "${ZDIFF}" -N in in6 > /dev/null
if [ $? != 0 ] ; then printf . ; else printf - ; fail=1 ; fi 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 if [ $? != 0 ] ; then printf . ; else printf - ; fail=1 ; fi
"${ZDIFF}" - || fail=1 "${ZDIFF}" -N - || fail=1
printf . printf .
"${ZDIFF}" in in || fail=1 "${ZDIFF}" -N in in || fail=1
printf . printf .
"${ZDIFF}" in || fail=1 "${ZDIFF}" -N in > /dev/null || fail=1
printf . printf .
"${ZDIFF}" in.lz in.gz || fail=1 "${ZDIFF}" -N in.lz in.gz > /dev/null || fail=1
printf . printf .
"${ZDIFF}" in.gz -- -in-.lz || fail=1 "${ZDIFF}" -N --lz='lzip -q' in.lz in.gz > /dev/null || fail=1
printf . printf .
"${ZDIFF}" -- -in-.lz in.gz || fail=1 "${ZDIFF}" -N in.gz -- -in-.lz > /dev/null || fail=1
printf . printf .
"${ZDIFF}" in -- -in-.lz || fail=1 "${ZDIFF}" -N -- -in-.lz in.gz > /dev/null || fail=1
printf . printf .
"${ZDIFF}" -- -in- in.lz || fail=1 "${ZDIFF}" -N in -- -in-.lz > /dev/null || fail=1
printf . printf .
"${ZDIFF}" in.lz -- -in- || fail=1 "${ZDIFF}" -N -- -in- in.lz > /dev/null || fail=1
printf . printf .
"${ZDIFF}" -- -in-.lz in || fail=1 "${ZDIFF}" -N in.lz -- -in- > /dev/null || fail=1
printf . printf .
"${ZDIFF}" -- -in- in || fail=1 "${ZDIFF}" -N -- -in-.lz in > /dev/null || fail=1
printf . printf .
"${ZDIFF}" in -- -in- || fail=1 "${ZDIFF}" -N -- -in- in > /dev/null || fail=1
printf . printf .
"${ZDIFF}" lz_only.lz < in || fail=1 "${ZDIFF}" -N in -- -in- > /dev/null || fail=1
printf . printf .
"${ZDIFF}" in.lz - < in || fail=1 "${ZDIFF}" -N lz_only.lz < in > /dev/null || fail=1
printf . printf .
"${ZDIFF}" - in.lz < in || fail=1 "${ZDIFF}" -N in.lz - < in > /dev/null || fail=1
printf . printf .
"${ZDIFF}" in - < in.lz || fail=1 "${ZDIFF}" -N - in.lz < in > /dev/null || fail=1
printf . printf .
"${ZDIFF}" - in < in.lz || fail=1 "${ZDIFF}" -N in - < in.lz > /dev/null || fail=1
printf . printf .
"${ZDIFF}" -q --format=bz2 in.bz2 2> /dev/null "${ZDIFF}" -N - in < in.lz > /dev/null || fail=1
printf .
"${ZDIFF}" -N -q --format=bz2 in.bz2 2> /dev/null
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi 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 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 if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
printf "\ntesting zgrep-%s..." "$2" printf "\ntesting zgrep-%s..." "$2"
for i in ${extensions}; do for i in ${extensions}; do
"${ZGREP}" "GNU" in.$i > /dev/null || fail=1 "${ZGREP}" -N "GNU" in.$i > /dev/null || fail=1
printf . printf .
"${ZGREP}" -l "GNU" in.$i > /dev/null || fail=1 "${ZGREP}" -N -l "GNU" in.$i > /dev/null || fail=1
printf . printf .
"${ZGREP}" -L "GNU" in.$i || fail=1 "${ZGREP}" -N -L "GNU" in.$i || fail=1
printf . printf .
"${ZGREP}" --format=$i "GNU" in.$i > /dev/null || fail=1 "${ZGREP}" -N --format=$i "GNU" in.$i > /dev/null || fail=1
printf . printf .
"${ZGREP}" -v "nx_pattern" in.$i > /dev/null || fail=1 "${ZGREP}" -N -v "nx_pattern" in.$i > /dev/null || fail=1
printf . printf .
"${ZGREP}" "nx_pattern" in.$i "${ZGREP}" -N "nx_pattern" in.$i
if [ $? != 0 ] ; then printf . ; else printf - ; fail=1 ; fi 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 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 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 if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
done done
"${ZGREP}" "GNU" < pin.tar > /dev/null || fail=1 "${ZGREP}" -N "GNU" < pin.tar > /dev/null || fail=1
printf . printf .
"${ZGREP}" "GNU" pin.tar > /dev/null || fail=1 "${ZGREP}" -N "GNU" pin.tar > /dev/null || fail=1
printf . printf .
"${ZGREP}" -r "GNU" . > /dev/null || fail=1 "${ZGREP}" -N -r "GNU" . > /dev/null || fail=1
printf . 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 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 . printf .
"${ZGREP}" "GNU" < in.gz > /dev/null || fail=1 "${ZGREP}" -N "GNU" < in.gz > /dev/null || fail=1
printf . printf .
"${ZGREP}" "GNU" < in.bz2 > /dev/null || fail=1 "${ZGREP}" -N "GNU" < in.bz2 > /dev/null || fail=1
printf . printf .
"${ZGREP}" "GNU" < in.lz > /dev/null || fail=1 "${ZGREP}" -N "GNU" < in.lz > /dev/null || fail=1
printf . printf .
"${ZGREP}" "GNU" in > /dev/null || fail=1 "${ZGREP}" -N "GNU" --lz='lzip -q' < in.lz > /dev/null || fail=1
printf . printf .
"${ZGREP}" "GNU" -- -in- > /dev/null || fail=1 "${ZGREP}" -N "GNU" in > /dev/null || fail=1
printf . printf .
"${ZGREP}" "GNU" -- -in-.lz > /dev/null || fail=1 "${ZGREP}" -N "GNU" -- -in- > /dev/null || fail=1
printf . 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 . 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 . 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 . 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 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 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 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 . printf .
"${ZFGREP}" "GNU" in > /dev/null || fail=1 "${ZFGREP}" -N "GNU" in > /dev/null || fail=1
printf . printf .
printf "\ntesting ztest-%s..." "$2" printf "\ntesting ztest-%s..." "$2"
for i in ${extensions}; do for i in ${extensions}; do
"${ZTEST}" --format=$i < in.$i || fail=1 "${ZTEST}" -N --format=$i < in.$i || fail=1
printf . 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 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 if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
done 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 . printf .
"${ZTEST}" < in.gz || fail=1 "${ZTEST}" -N < in.gz || fail=1
printf . printf .
"${ZTEST}" < in.bz2 || fail=1 "${ZTEST}" -N < in.bz2 || fail=1
printf . printf .
"${ZTEST}" < in.lz || fail=1 "${ZTEST}" -N < in.lz || fail=1
printf . printf .
"${ZTEST}" -r . || fail=1 "${ZTEST}" -N --lz='lzip -q' < in.lz || fail=1
printf . printf .
"${ZTEST}" < in 2> /dev/null "${ZTEST}" -N -r . || fail=1
printf .
"${ZTEST}" -N < in 2> /dev/null
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
dd if=in.lz bs=1000 count=1 2> /dev/null | "${ZTEST}" -q dd if=in.lz bs=1000 count=1 2> /dev/null | "${ZTEST}" -N -q
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
"${ZTEST}" --format=lz in.bz2 2> /dev/null "${ZTEST}" -N --format=lz in.bz2 2> /dev/null
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
"${ZTEST}" --bad-option 2> /dev/null "${ZTEST}" -N --lz='lzip --bad-option' in.lz 2> /dev/null
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
"${ZTEST}" -N --bad-option 2> /dev/null
if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
echo echo

29
zcat.cc
View file

@ -61,7 +61,7 @@ Line_number line_number;
void show_zcat_help() void show_zcat_help()
{ {
std::printf( "Zcat copies each given file (\"-\" means standard input), to standard\n" 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" "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" "of the known extensions, zcat tries the compressed file names\n"
"corresponding to the supported formats. If no files are specified,\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" "all uncompressed or all in the same compression format.\n"
"\nThe supported formats are bzip2, gzip, lzip and xz.\n" "\nThe supported formats are bzip2, gzip, lzip and xz.\n"
"\nUsage: zcat [options] [files]\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" "\nOptions:\n"
" -h, --help display this help and exit\n" " -h, --help display this help and exit\n"
" -V, --version output version information 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" " -E, --show-ends display '$' at end of each line\n"
" --format=<fmt> force given format (bz2, gz, lz, xz)\n" " --format=<fmt> force given format (bz2, gz, lz, xz)\n"
" -n, --number number all output lines\n" " -n, --number number all output lines\n"
" -N, --no-rcfile don't read runtime configuration file\n"
" -q, --quiet suppress all messages\n" " -q, --quiet suppress all messages\n"
" -r, --recursive operate recursively on directories\n" " -r, --recursive operate recursively on directories\n"
" -s, --squeeze-blank never more than one single blank line\n" " -s, --squeeze-blank never more than one single blank line\n"
" -t equivalent to '-vT'\n" " -t equivalent to '-vT'\n"
" -T, --show-tabs display TAB characters as '^I'\n" " -T, --show-tabs display TAB characters as '^I'\n"
" -v, --show-nonprinting use '^' and 'M-' notation, except for LF and TAB\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(); 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 ) const Cat_options & cat_options )
{ {
enum { buffer_size = 4096 }; 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]; uint8_t * const inbuf = new uint8_t[buffer_size+1];
// buffer with space for character quoting and 255-digit line number // buffer with space for character quoting and 255-digit line number
uint8_t * const outbuf = new uint8_t[(4*buffer_size)+256]; uint8_t * const outbuf = new uint8_t[(4*buffer_size)+256];
pid_t pid = 0;
int retval = 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 else
retval = do_cat( infd, buffer_size, inbuf, outbuf, retval = do_cat( infd, buffer_size, inbuf, outbuf,
input_filename, cat_options ); input_filename, cat_options );
if( retval == 0 )
if( pid && wait_for_child( pid, "data feeder" ) != 0 ) retval = 1; if( !good_status( children, retval == 0 ) ) retval = 1;
if( retval == 0 )
if( close( infd ) != 0 ) if( retval == 0 && close( infd ) != 0 )
{ show_close_error( "data feeder" ); retval = 1; } { show_close_error( "data feeder" ); retval = 1; }
delete[] inbuf; delete[] outbuf; delete[] outbuf; delete[] inbuf;
return retval; return retval;
} }

60
zcmp.cc
View file

@ -37,6 +37,7 @@
#include "arg_parser.h" #include "arg_parser.h"
#include "zutils.h" #include "zutils.h"
#include "rc.h"
#if CHAR_BIT != 8 #if CHAR_BIT != 8
#error "Environments where CHAR_BIT != 8 are not supported." #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" 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" "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" "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" "the fly; no temporary files are created.\n"
"\nThe supported formats are bzip2, gzip, lzip and xz.\n" "\nThe supported formats are bzip2, gzip, lzip and xz.\n"
"\nUsage: zcmp [options] file1 [file2]\n" "\nUsage: zcmp [options] file1 [file2]\n"
"\nCompares <file1> to <file2>. If <file2> is omitted zcmp tries the\n" "\nCompares <file1> to <file2>. If <file2> is omitted zcmp tries the\n"
"following:\n" "following:\n"
"If <file1> is compressed, compares <file1> to the file with the\n" "\n 1. If <file1> is compressed, compares its decompressed contents with\n"
"corresponding decompressed file name (removes the extension from\n" " the corresponding uncompressed file (the name of <file1> with the\n"
"<file1>).\n" " extension removed).\n"
"If <file1> is not compressed, compares <file1> to the uncompressed\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" " 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" "\n 3. If no suitable file is found, compares <file1> with data read from\n"
"standard input.\n" " standard input.\n"
"\nExit status is 0 if inputs are identical, 1 if different, 2 if trouble.\n" "\nExit status is 0 if inputs are identical, 1 if different, 2 if trouble.\n"
"\nOptions:\n" "\nOptions:\n"
" -h, --help display this help and exit\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" " -i, --ignore-initial=<n>[,<n2>] ignore differences in the first <n> bytes\n"
" -l, --list list position, value of all differing bytes\n" " -l, --list list position, value of all differing bytes\n"
" -n, --bytes=<n> compare at most <n> 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" " -q, --quiet suppress all messages\n"
" -s, --silent (same as --quiet)\n" " -s, --silent (same as --quiet)\n"
" -v, --verbose verbose mode (same as --list)\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" "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" ); "Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" );
show_help_addr(); 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[] ) 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 // number of initial bytes ignored for each file
long long ignore_initial[2] = { 0, 0 }; long long ignore_initial[2] = { 0, 0 };
long long max_size = -1; // < 0 means unlimited size 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 }, { 'i', "ignore-initial", Arg_parser::yes },
{ 'l', "list", Arg_parser::no }, { 'l', "list", Arg_parser::no },
{ 'n', "bytes", Arg_parser::yes }, { 'n', "bytes", Arg_parser::yes },
{ 'N', "no-rcfile", Arg_parser::no },
{ 'q', "quiet", Arg_parser::no }, { 'q', "quiet", Arg_parser::no },
{ 's', "silent", Arg_parser::no }, { 's', "silent", Arg_parser::no },
{ 'v', "verbose", Arg_parser::no }, { 'v', "verbose", Arg_parser::no },
{ 'V', "version", Arg_parser::no }, { 'V', "version", Arg_parser::no },
{ format_opt, "format", Arg_parser::yes }, { 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 } }; { 0 , 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options ); const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option if( parser.error().size() ) // bad option
{ show_error( parser.error().c_str(), 0, true ); return 2; } { show_error( parser.error().c_str(), 0, true ); return 2; }
maybe_process_config_file( parser );
int argind = 0; int argind = 0;
for( ; argind < parser.arguments(); ++argind ) 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 'i': parse_ignore_initial( arg, ignore_initial ); break;
case 'l': verbosity = 1; break; case 'l': verbosity = 1; break;
case 'n': max_size = getnum( arg ); break; case 'n': max_size = getnum( arg ); break;
case 'N': break;
case 'q': case 'q':
case 's': verbosity = -1; break; case 's': verbosity = -1; break;
case 'v': verbosity = 1; break; case 'v': verbosity = 1; break;
case 'V': show_version( "Zcmp" ); return 0; 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" ); default : internal_error( "uncaught option" );
} }
} // end process options } // 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 int old_infd[2]; // copy of file descriptors of the two files
old_infd[0] = infd[0]; old_infd[1] = infd[1]; old_infd[0] = infd[0]; old_infd[1] = infd[1];
pid_t pid[2]; Children children[2];
if( !set_data_feeder( &infd[0], &pid[0], format_types[0] ) || if( !set_data_feeder( &infd[0], children[0], format_types[0] ) ||
!set_data_feeder( &infd[1], &pid[1], format_types[1] ) ) !set_data_feeder( &infd[1], children[1], format_types[1] ) )
return 2; return 2;
for( int i = 0; i < 2; ++i ) 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 ); int retval = cmp( max_size, infd, filenames, print_bytes );
if( retval != 0 || max_size >= 0 ) 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 )
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 ) for( int i = 0; i < 2; ++i )
{ {

View file

@ -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() ); 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; else format_types[0] = -1;
if( i + 1 < arg.size() ) format_types[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; else format_types[1] = -1;
} }

152
zdiff.cc
View file

@ -37,6 +37,7 @@
#include "arg_parser.h" #include "arg_parser.h"
#include "zutils.h" #include "zutils.h"
#include "rc.h"
#if CHAR_BIT != 8 #if CHAR_BIT != 8
#error "Environments where CHAR_BIT != 8 are not supported." #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" 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" "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" "the diff program and has the limitation that messages from diff refer to\n"
"temporary filenames instead of those specified.\n" "temporary filenames instead of those specified.\n"
"\nThe supported formats are bzip2, gzip, lzip and xz.\n" "\nThe supported formats are bzip2, gzip, lzip and xz.\n"
"\nUsage: zdiff [options] file1 [file2]\n" "\nUsage: zdiff [options] file1 [file2]\n"
"\nCompares <file1> to <file2>. If <file2> is omitted zdiff tries the\n" "\nCompares <file1> to <file2>. If <file2> is omitted zdiff tries the\n"
"following:\n" "following:\n"
"If <file1> is compressed, compares <file1> to the file with the\n" "\n 1. If <file1> is compressed, compares its decompressed contents with\n"
"corresponding decompressed file name (removes the extension from\n" " the corresponding uncompressed file (the name of <file1> with the\n"
"<file1>).\n" " extension removed).\n"
"If <file1> is not compressed, compares <file1> to the uncompressed\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" " 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" "\n 3. If no suitable file is found, compares <file1> with data read from\n"
"standard input.\n" " standard input.\n"
"\nExit status is 0 if inputs are identical, 1 if different, 2 if trouble.\n" "\nExit status is 0 if inputs are identical, 1 if different, 2 if trouble.\n"
"\nOptions:\n" "\nOptions:\n"
" -h, --help display this help and exit\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" " -E, --ignore-tab-expansion ignore changes due to tab expansion\n"
" --format=[<fmt1>][,<fmt2>] force given formats (bz2, gz, lz, xz)\n" " --format=[<fmt1>][,<fmt2>] force given formats (bz2, gz, lz, xz)\n"
" -i, --ignore-case ignore case differences in file contents\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" " -p, --show-c-function show which C function each change is in\n"
" -q, --brief output only whether files differ\n" " -q, --brief output only whether files differ\n"
" -s, --report-identical-files report when two files are identical\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 use the unified output format\n"
" -U, --unified=<n> same as -u but use <n> lines of context\n" " -U, --unified=<n> same as -u but use <n> lines of context\n"
" -w, --ignore-all-space ignore all white space\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" "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" ); "Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" );
show_help_addr(); 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, 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; const uint8_t * magic_data = 0;
int magic_size = 0; int magic_size = 0;
const char * const decompressor_name = ( format_type >= 0 ) ? if( format_index < 0 )
decompressor_names[format_type] : format_index = test_format( infd, &magic_data, &magic_size );
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 ) if( pipe( fda ) < 0 )
{ show_error( "Can't create pipe", errno ); return false; } { show_error( "Can't create pipe", errno ); return false; }
const pid_t pid = fork(); 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 || if( close( fda[0] ) != 0 ||
!feed_data( infd, fda[1], magic_data, magic_size ) ) !feed_data( infd, fda[1], magic_data, magic_size ) )
_exit( 2 ); _exit( 2 );
if( close( fda[1] ) != 0 ) if( close( fda[1] ) != 0 )
{ show_close_error( "data feeder" ); _exit( 2 ); } { 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] ); close( fda[0] ); close( fda[1] );
if( pid < 0 ) children.pid[0] = pid;
{ show_fork_error( "decompressor feeder" ); return false; } children.pid[1] = pid2;
*pidp = pid;
} }
else // not compressed else // uncompressed
{ {
const pid_t pid = fork(); const pid_t pid = fork();
if( pid == 0 ) // child (feeder) 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 ); } { show_close_error( "data feeder" ); _exit( 2 ); }
_exit( 0 ); _exit( 0 );
} }
// parent if( pid < 0 ) // parent
if( pid < 0 )
{ show_fork_error( "data feeder" ); return false; } { show_fork_error( "data feeder" ); return false; }
*pidp = pid; children.pid[0] = pid;
children.pid[1] = 0;
} }
return true; return true;
} }
@ -251,7 +268,7 @@ void set_signals()
int main( const int argc, const char * const argv[] ) 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 std::vector< const char * > diff_args; // args to diff, maybe empty
int format_types[2] = { -1, -1 }; int format_types[2] = { -1, -1 };
invocation_name = argv[0]; invocation_name = argv[0];
@ -268,6 +285,7 @@ int main( const int argc, const char * const argv[] )
{ 'E', "ignore-tab-expansion", Arg_parser::no }, { 'E', "ignore-tab-expansion", Arg_parser::no },
{ 'h', "help", Arg_parser::no }, { 'h', "help", Arg_parser::no },
{ 'i', "ignore-case", Arg_parser::no }, { 'i', "ignore-case", Arg_parser::no },
{ 'N', "no-rcfile", Arg_parser::no },
{ 'p', "show-c-function", Arg_parser::no }, { 'p', "show-c-function", Arg_parser::no },
{ 'q', "brief", Arg_parser::no }, { 'q', "brief", Arg_parser::no },
{ 's', "report-identical-files", 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 }, { 'V', "version", Arg_parser::no },
{ 'w', "ignore-all-space", Arg_parser::no }, { 'w', "ignore-all-space", Arg_parser::no },
{ format_opt, "format", Arg_parser::yes }, { 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 } }; { 0 , 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options ); const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option if( parser.error().size() ) // bad option
{ show_error( parser.error().c_str(), 0, true ); return 2; } { show_error( parser.error().c_str(), 0, true ); return 2; }
maybe_process_config_file( parser );
int argind = 0; int argind = 0;
for( ; argind < parser.arguments(); ++argind ) 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 'E': diff_args.push_back( "-E" ); break;
case 'h': show_help(); return 0; case 'h': show_help(); return 0;
case 'i': diff_args.push_back( "-i" ); break; case 'i': diff_args.push_back( "-i" ); break;
case 'N': break;
case 'p': diff_args.push_back( "-p" ); break; case 'p': diff_args.push_back( "-p" ); break;
case 'q': diff_args.push_back( "-q" ); break; case 'q': diff_args.push_back( "-q" ); break;
case 's': diff_args.push_back( "-s" ); 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 'U': diff_args.push_back( "-U" ); diff_args.push_back( arg ); break;
case 'V': show_version( "Zdiff" ); return 0; case 'V': show_version( "Zdiff" ); return 0;
case 'w': diff_args.push_back( "-w" ); break; 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" ); default : internal_error( "uncaught option" );
} }
} // end process options } // end process options
@ -375,31 +404,18 @@ int main( const int argc, const char * const argv[] )
show_exec_error( DIFF ); show_exec_error( DIFF );
_exit( 2 ); _exit( 2 );
} }
// parent if( diff_pid < 0 ) // parent
if( diff_pid < 0 )
{ show_fork_error( DIFF ); return 2; } { show_fork_error( DIFF ); return 2; }
pid_t pid[2]; Children children[2];
if( !set_data_feeder( fifonames[0], infd[0], &pid[0], format_types[0] ) || if( !set_data_feeder( fifonames[0], infd[0], children[0], format_types[0] ) ||
!set_data_feeder( fifonames[1], infd[1], &pid[1], format_types[1] ) ) !set_data_feeder( fifonames[1], infd[1], children[1], format_types[1] ) )
return 2; return 2;
int retval = wait_for_child( diff_pid, DIFF ); int retval = wait_for_child( diff_pid, DIFF );
if( retval != 0 ) for( int i = 0; i < 2; ++i )
{ if( !good_status( children[i], retval == 0 ) ) retval = 2;
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 ) for( int i = 0; i < 2; ++i )
if( filenames[i] != "-" && close( infd[i] ) != 0 ) if( filenames[i] != "-" && close( infd[i] ) != 0 )

View file

@ -18,8 +18,8 @@
void show_zgrep_help() void show_zgrep_help()
{ {
std::printf( "Zgrep is a front end to the grep program that allows transparent search\n" 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" "on any combination of compressed and uncompressed files. If any given\n"
"file is compressed, its uncompressed content is used. If a given file\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" "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" "extensions, zgrep tries the compressed file names corresponding to the\n"
"supported formats. If no files are specified, data is read from\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" " -L, --files-without-match only print names of files containing no matches\n"
" -m, --max-count=<n> stop after <n> matches\n" " -m, --max-count=<n> stop after <n> matches\n"
" -n, --line-number print the line number of each line\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" " -o, --only-matching show only the part of a line matching <pattern>\n"
" -q, --quiet suppress all messages\n" " -q, --quiet suppress all messages\n"
" -r, --recursive operate recursively on directories\n" " -r, --recursive operate recursively on directories\n"
@ -58,16 +59,20 @@ void show_zgrep_help()
" -v, --invert-match select non-matching lines\n" " -v, --invert-match select non-matching lines\n"
" --verbose verbose mode (show error messages)\n" " --verbose verbose mode (show error messages)\n"
" -w, --word-regexp match only whole words\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(); 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 ) const std::vector< const char * > & grep_args )
{ {
pid_t pid; Children children;
if( !set_data_feeder( &infd, &pid, format_type ) ) return 2; if( !set_data_feeder( &infd, children, format_index ) ) return 2;
const pid_t grep_pid = fork(); const pid_t grep_pid = fork();
if( grep_pid == 0 ) // child (grep) if( grep_pid == 0 ) // child (grep)
{ {
@ -83,28 +88,26 @@ int zgrep_stdin( int infd, const int format_type,
show_exec_error( GREP ); show_exec_error( GREP );
_exit( 2 ); _exit( 2 );
} }
// parent if( grep_pid < 0 ) // parent
if( grep_pid < 0 )
{ show_fork_error( GREP ); return 2; } { show_fork_error( GREP ); return 2; }
int retval = wait_for_child( grep_pid, GREP ); int retval = wait_for_child( grep_pid, GREP );
if( retval != 1 )
{ if( pid ) kill( pid, SIGTERM ); } if( !good_status( children, retval == 1 ) ) retval = 2;
else
if( pid && wait_for_child( pid, "data feeder" ) != 0 ) retval = 2;
if( close( infd ) != 0 ) if( close( infd ) != 0 )
{ show_close_error( "data feeder" ); return 2; } { show_close_error( "data feeder" ); return 2; }
return retval; 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::string & input_filename,
const std::vector< const char * > & grep_args, const std::vector< const char * > & grep_args,
const int grep_list_mode, const bool grep_show_name ) const int grep_list_mode, const bool grep_show_name )
{ {
pid_t pid; Children children;
if( !set_data_feeder( &infd, &pid, format_type ) ) return 2; if( !set_data_feeder( &infd, children, format_index ) ) return 2;
int fda[2]; // pipe from grep int fda[2]; // pipe from grep
if( pipe( fda ) < 0 ) if( pipe( fda ) < 0 )
{ show_error( "Can't create pipe", errno ); return 2; } { 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 ); show_exec_error( GREP );
_exit( 2 ); _exit( 2 );
} }
// parent if( grep_pid < 0 ) // parent
close( fda[1] );
if( grep_pid < 0 )
{ show_fork_error( GREP ); return 2; } { show_fork_error( GREP ); return 2; }
close( fda[1] );
enum { buffer_size = 256 }; enum { buffer_size = 256 };
uint8_t buffer[buffer_size]; uint8_t buffer[buffer_size];
bool line_begin = true; 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 ); int retval = wait_for_child( grep_pid, GREP );
if( retval != 1 )
{ if( pid ) kill( pid, SIGTERM ); } if( !good_status( children, retval == 1 ) ) retval = 2;
else
if( pid && wait_for_child( pid, "data feeder" ) != 0 ) retval = 2;
if( grep_list_mode && (retval == 0) == (grep_list_mode == 1) ) if( grep_list_mode && (retval == 0) == (grep_list_mode == 1) )
std::printf( "%s\n", input_filename.c_str() ); std::printf( "%s\n", input_filename.c_str() );
if( close( infd ) != 0 ) if( close( infd ) != 0 )

106
ztest.cc
View file

@ -18,9 +18,9 @@
void show_ztest_help() void show_ztest_help()
{ {
std::printf( "Ztest verifies the integrity of the specified compressed files.\n" std::printf( "Ztest verifies the integrity of the specified compressed files.\n"
"Non-compressed files are ignored. If no files are specified, the\n" "Uncompressed files are ignored. If no files are specified, the integrity\n"
"integrity of compressed data read from standard input is verified. Data\n" "of compressed data read from standard input is verified. Data read from\n"
"read from standard input must be all in the same compression format.\n" "standard input must be all in the same compression format.\n"
"\nThe supported formats are bzip2, gzip, lzip and xz.\n" "\nThe supported formats are bzip2, gzip, lzip and xz.\n"
"\nNote that some xz files lack integrity information, and therefore can't\n" "\nNote that some xz files lack integrity information, and therefore can't\n"
"be verified as reliably as the other formats can.\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" " -h, --help display this help and exit\n"
" -V, --version output version information and exit\n" " -V, --version output version information and exit\n"
" --format=<fmt> force given format (bz2, gz, lz, xz)\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" " -q, --quiet suppress all messages\n"
" -r, --recursive operate recursively on directories\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(); 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 std::vector< const char * > & ztest_args )
{ {
const uint8_t * magic_data = 0; const uint8_t * magic_data = 0;
int magic_size = 0; int magic_size = 0;
const char * const decompressor_name = ( format_type >= 0 ) ? if( format_index < 0 )
decompressor_names[format_type] : format_index = test_format( infd, &magic_data, &magic_size );
test_format( infd, &magic_data, &magic_size ); const char * const compressor_name = get_compressor_name( format_index );
if( !decompressor_name ) if( !compressor_name )
{ show_error( "Unknown data format read from stdin." ); return 2; } { show_error( "Unknown data format read from stdin." ); return 2; }
int fda[2]; // pipe from feeder int fda[2]; // pipe from feeder
if( pipe( fda ) < 0 ) if( pipe( fda ) < 0 )
{ show_error( "Can't create pipe", errno ); return 1; } { show_error( "Can't create pipe", errno ); return 1; }
const pid_t pid = fork(); const pid_t pid = fork();
if( pid == 0 ) // child1 (decompressor) if( pid == 0 ) // child1 (compressor)
{ {
if( dup2( fda[0], STDIN_FILENO ) >= 0 && if( dup2( fda[0], STDIN_FILENO ) >= 0 &&
close( fda[0] ) == 0 && close( fda[1] ) == 0 ) close( fda[0] ) == 0 && close( fda[1] ) == 0 )
{ {
const char ** const argv = new const char *[ztest_args.size()+3]; const std::vector< std::string > & compressor_args =
argv[0] = decompressor_name; get_compressor_args( format_index );
for( unsigned i = 0; i < ztest_args.size(); ++i ) const int size = compressor_args.size();
argv[i+1] = ztest_args[i]; const int size2 = ztest_args.size();
argv[ztest_args.size()+1] = "-t"; const char ** const argv = new const char *[size+size2+3];
argv[ztest_args.size()+2] = 0; 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 ); execvp( argv[0], (char **)argv );
} }
show_exec_error( decompressor_name ); show_exec_error( compressor_name );
_exit( 1 ); _exit( 1 );
} }
// parent if( pid < 0 ) // parent
if( pid < 0 ) { show_fork_error( compressor_name ); return 1; }
{ show_fork_error( decompressor_name ); return 1; }
const pid_t pid2 = fork(); const pid_t pid2 = fork();
if( pid2 == 0 ) // child2 (decompressor feeder) if( pid2 == 0 ) // child2 (compressor feeder)
{ {
if( close( fda[0] ) != 0 || if( close( fda[0] ) != 0 ||
!feed_data( infd, fda[1], magic_data, magic_size ) ) !feed_data( infd, fda[1], magic_data, magic_size ) )
_exit( 1 ); _exit( 1 );
if( close( fda[1] ) != 0 ) if( close( fda[1] ) != 0 )
{ show_close_error( "decompressor feeder" ); _exit( 1 ); } { show_close_error( "data feeder" ); _exit( 1 ); }
_exit( 0 ); _exit( 0 );
} }
// parent if( pid2 < 0 ) // parent
if( pid2 < 0 ) { show_fork_error( "data feeder" ); return 1; }
{ show_fork_error( "decompressor feeder" ); return 1; }
close( fda[0] ); close( fda[1] ); close( fda[0] ); close( fda[1] );
int retval = wait_for_child( pid, decompressor_name, 1 ); int retval = wait_for_child( pid, compressor_name, 1 );
if( retval == 0 && wait_for_child( pid2, "decompressor feeder" ) != 0 ) if( retval == 0 && wait_for_child( pid2, "data feeder" ) != 0 )
retval = 1; retval = 1;
return retval; 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::string & input_filename,
const std::vector< const char * > & ztest_args ) const std::vector< const char * > & ztest_args )
{ {
const uint8_t * magic_data = 0; const uint8_t * magic_data = 0;
int magic_size = 0; int magic_size = 0;
const char * const decompressor_name = ( format_type >= 0 ) ? if( format_index < 0 )
decompressor_names[format_type] : format_index = test_format( infd, &magic_data, &magic_size );
test_format( infd, &magic_data, &magic_size ); const char * const compressor_name = get_compressor_name( format_index );
if( !decompressor_name ) if( !compressor_name )
return 0; // skip this file return 0; // skip this file
const pid_t pid = fork(); 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]; const std::vector< std::string > & compressor_args =
argv[0] = decompressor_name; get_compressor_args( format_index );
for( unsigned i = 0; i < ztest_args.size(); ++i ) const int size = compressor_args.size();
argv[i+1] = ztest_args[i]; const int size2 = ztest_args.size();
argv[ztest_args.size()+1] = "-t"; const char ** const argv = new const char *[size+size2+5];
argv[ztest_args.size()+2] = "--"; argv[0] = compressor_name;
argv[ztest_args.size()+3] = input_filename.c_str(); for( int i = 0; i < size; ++i )
argv[ztest_args.size()+4] = 0; 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 ); execvp( argv[0], (char **)argv );
show_exec_error( decompressor_name ); show_exec_error( compressor_name );
_exit( 1 ); _exit( 1 );
} }
// parent if( pid < 0 ) // parent
if( pid < 0 ) { show_fork_error( compressor_name ); return 1; }
{ show_fork_error( decompressor_name ); return 1; }
return wait_for_child( pid, decompressor_name, 1 ); return wait_for_child( pid, compressor_name, 1 );
} }

123
zutils.cc
View file

@ -22,11 +22,13 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <string> #include <string>
#include <vector>
#include <stdint.h> #include <stdint.h>
#include <unistd.h> #include <unistd.h>
#include <sys/wait.h> #include <sys/wait.h>
#include "zutils.h" #include "zutils.h"
#include "rc.h"
const char * invocation_name = 0; const char * invocation_name = 0;
@ -35,7 +37,7 @@ const char * util_name = program_name;
int verbosity = 0; 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 ) for( int i = 0; i < num_formats; ++i )
if( arg == format_names[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, bool good_status( const Children & children, const bool finished )
const int format_type ) {
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; const uint8_t * magic_data = 0;
int magic_size = 0; int magic_size = 0;
const char * const decompressor_name = ( format_type >= 0 ) ? if( format_index < 0 )
decompressor_names[format_type] : format_index = test_format( *infdp, &magic_data, &magic_size );
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 fda[2]; // pipe from feeder
int fda2[2]; // pipe from decompressor int fda2[2]; // pipe from compressor
if( pipe( fda ) < 0 || pipe( fda2 ) < 0 ) if( pipe( fda ) < 0 || pipe( fda2 ) < 0 )
{ show_error( "Can't create pipe", errno ); return false; } { show_error( "Can't create pipe", errno ); return false; }
const int old_infd = *infdp; const int old_infd = *infdp;
*infdp = fda2[0]; *infdp = fda2[0];
const pid_t pid = fork(); 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 || if( close( fda[0] ) != 0 ||
close( fda2[0] ) != 0 || close( fda2[1] ) != 0 || close( fda2[0] ) != 0 || close( fda2[1] ) != 0 ||
!feed_data( old_infd, fda[1], magic_data, magic_size ) ) !feed_data( old_infd, fda[1], magic_data, magic_size ) )
_exit( 2 ); _exit( 2 );
if( close( fda[1] ) != 0 ) if( close( fda[1] ) != 0 )
{ show_close_error( "decompressor feeder" ); _exit( 2 ); } { 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)
{
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 );
}
if( pid2 < 0 ) // parent
{ show_fork_error( children.compressor_name ); return false; }
close( fda[0] ); close( fda[1] ); close( fda2[1] ); close( fda[0] ); close( fda[1] ); close( fda2[1] );
if( pid < 0 ) children.pid[0] = pid;
{ show_fork_error( "decompressor feeder" ); return false; } children.pid[1] = pid2;
*pidp = pid;
} }
else // not compressed else // uncompressed
{ {
int fda[2]; // pipe from feeder int fda[2]; // pipe from feeder
if( pipe( fda ) < 0 ) 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 ); } { show_close_error( "data feeder" ); _exit( 2 ); }
_exit( 0 ); _exit( 0 );
} }
// parent if( pid < 0 ) // parent
close( fda[1] );
if( pid < 0 )
{ show_fork_error( "data feeder" ); return false; } { show_fork_error( "data feeder" ); return false; }
*pidp = pid; close( fda[1] );
children.pid[0] = pid;
children.pid[1] = 0;
} }
return true; return true;
} }
@ -187,7 +223,7 @@ void show_help_addr()
void show_version( const char * const Util_name ) 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 ); std::printf( "%s %s\n", Program_name, PROGVERSION );
else else
std::printf( "%s (%s) %s\n", Util_name, program_name, PROGVERSION ); 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, int test_format( const int infd, const uint8_t ** const magic_datap,
const uint8_t ** const magic_datap, int * const magic_sizep )
int * const magic_sizep )
{ {
enum { buf_size = 5 }; enum { buf_size = 5 };
static uint8_t buf[buf_size]; 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] && if( readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == bzip2_magic[1] &&
readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == bzip2_magic[2] ) readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == bzip2_magic[2] )
{ *magic_datap = bzip2_magic; *magic_sizep = bzip2_magic_size; { *magic_datap = bzip2_magic; *magic_sizep = bzip2_magic_size;
return decompressor_names[fmt_bz2]; } return fmt_bz2; }
} }
else if( buf[0] == gzip_magic[0] ) else if( buf[0] == gzip_magic[0] )
{ {
if( readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == gzip_magic[1] ) if( readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == gzip_magic[1] )
{ *magic_datap = gzip_magic; *magic_sizep = gzip_magic_size; { *magic_datap = gzip_magic; *magic_sizep = gzip_magic_size;
return decompressor_names[fmt_gz]; } return fmt_gz; }
} }
else if( buf[0] == lzip_magic[0] ) 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[2] &&
readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == lzip_magic[3] ) readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == lzip_magic[3] )
{ *magic_datap = lzip_magic; *magic_sizep = lzip_magic_size; { *magic_datap = lzip_magic; *magic_sizep = lzip_magic_size;
return decompressor_names[fmt_lz]; } return fmt_lz; }
} }
else if( buf[0] == xz_magic[0] ) 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[3] &&
readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == xz_magic[4] ) readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == xz_magic[4] )
{ *magic_datap = xz_magic; *magic_sizep = xz_magic_size; { *magic_datap = xz_magic; *magic_sizep = xz_magic_size;
return decompressor_names[fmt_xz]; } return fmt_xz; }
} }
} }
*magic_datap = buf; *magic_sizep = i; *magic_datap = buf; *magic_sizep = i;
return 0; return -1;
} }

View file

@ -15,9 +15,10 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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_name = "zutils"; const char * const program_name = "zutils";
const char * const program_year = "2013"; const char * const config_file_name = "zutilsrc";
const char * const program_year = "2013";
extern const char * invocation_name; extern const char * invocation_name;
extern const char * util_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 format_names[num_formats] = { "bz2", "gz", "lz", "xz" };
const char * const simple_extensions[num_formats] = const char * const simple_extensions[num_formats] =
{ ".bz2", ".gz", ".lz", ".xz" }; { ".bz2", ".gz", ".lz", ".xz" };
const char * const decompressor_names[num_formats] =
{ "bzip2", "gzip", "lzip", "xz" };
const int8_t format_order[num_formats] = const int8_t format_order[num_formats] =
{ fmt_lz, fmt_bz2, fmt_gz, fmt_xz }; // search order { 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" { 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 readblock( const int fd, uint8_t * const buf, const int size );
int writeblock( const int fd, const 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, bool feed_data( const int infd, const int outfd,
const uint8_t * magic_data, const int magic_size ); 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_help_addr();
void show_version( const char * const Util_name = 0 ); 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 show_fork_error( const char * const prog_name );
void internal_error( const char * const msg ); void internal_error( const char * const msg );
const char * test_format( const int infd, // Returns format index or -1 if uncompressed
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 );
// Returns exit status of child process 'pid', or 'eretval' in case of error. // Returns exit status of child process 'pid', or 'eretval' in case of error.
// //

16
zutilsrc Normal file
View file

@ -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