summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <mail@daniel-baumann.ch>2015-11-08 04:30:25 +0000
committerDaniel Baumann <mail@daniel-baumann.ch>2015-11-08 04:30:25 +0000
commitb4e85477b84918c0fb9da281cebe4eff2a50f002 (patch)
tree545a8391c25b7e98c76f8deb43c3df7919e00111
parentAdding debian version 1.2~pre2-1. (diff)
downloadzutils-b4e85477b84918c0fb9da281cebe4eff2a50f002.tar.xz
zutils-b4e85477b84918c0fb9da281cebe4eff2a50f002.zip
Merging upstream version 1.2~pre3.
Signed-off-by: Daniel Baumann <mail@daniel-baumann.ch>
-rw-r--r--ChangeLog8
-rw-r--r--Makefile.in76
-rw-r--r--NEWS5
-rw-r--r--README7
-rwxr-xr-xconfigure4
-rw-r--r--doc/zcat.12
-rw-r--r--doc/zcmp.12
-rw-r--r--doc/zdiff.12
-rw-r--r--doc/zgrep.12
-rw-r--r--doc/ztest.12
-rw-r--r--doc/zupdate.15
-rw-r--r--doc/zutils.info417
-rw-r--r--doc/zutils.texinfo17
-rw-r--r--main.cc467
-rw-r--r--rc.cc112
-rw-r--r--rc.h27
-rw-r--r--recursive.cc61
-rwxr-xr-xtestsuite/check.sh10
-rw-r--r--zcat.cc198
-rw-r--r--zcat.in3
-rw-r--r--zcmp.cc13
-rw-r--r--zcmpdiff.cc12
-rw-r--r--zdiff.cc21
-rw-r--r--zegrep.in2
-rw-r--r--zfgrep.in2
-rw-r--r--zgrep.cc247
-rw-r--r--zgrep.in3
-rw-r--r--ztest.cc145
-rw-r--r--ztest.in3
-rw-r--r--zupdate.cc103
-rw-r--r--zutils.cc175
-rw-r--r--zutils.h52
32 files changed, 1171 insertions, 1034 deletions
diff --git a/ChangeLog b/ChangeLog
index e7f2c1a..aa10218 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2013-10-11 Antonio Diaz Diaz <antonio@gnu.org>
+
+ * Version 1.2-pre3 released.
+ * Removed zutils executable. Utils are now independent executables.
+ * zupdate.cc: Added new option '-k, --keep'.
+ * zupdate.cc: Ignore xz files if xz compressor is not installed.
+ * zgrep.cc: Fixed the exit status returned on error.
+
2013-09-04 Antonio Diaz Diaz <antonio@gnu.org>
* Version 1.2-pre2 released.
diff --git a/Makefile.in b/Makefile.in
index 68b2e6b..98d2f24 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -7,28 +7,26 @@ INSTALL_DATA = $(INSTALL) -p -m 644
INSTALL_DIR = $(INSTALL) -d -m 755
SHELL = /bin/sh
-objs = arg_parser.o rc.o zutils.o main.o
+objs = arg_parser.o rc.o zutils.o \
+ zcat.o zcmp.o zdiff.o zgrep.o ztest.o zupdate.o
+zcat_objs = arg_parser.o rc.o zutils.o zcat.o
zcmp_objs = arg_parser.o rc.o zutils.o zcmp.o
zdiff_objs = arg_parser.o rc.o zutils.o zdiff.o
-zupdate_objs = arg_parser.o rc.o zutils.o zupdate.o
-scripts = zcat zegrep zfgrep zgrep ztest
+zgrep_objs = arg_parser.o rc.o zutils.o zgrep.o
+ztest_objs = arg_parser.o rc.o zutils.o ztest.o
+zupdate_objs = arg_parser.o rc.o zupdate.o
+programs = zcat zcmp zdiff zgrep ztest zupdate
+scripts = zegrep zfgrep
.PHONY : all install install-bin install-info install-man install-strip \
uninstall uninstall-bin uninstall-info uninstall-man \
doc info man check dist clean distclean
-all : $(progname) zcmp zdiff zupdate $(scripts)
+all : $(programs) $(scripts)
-$(progname) : $(objs)
- $(CXX) $(LDFLAGS) -o $@ $(objs)
-
-$(progname)_profiled : $(objs)
- $(CXX) $(LDFLAGS) -pg -o $@ $(objs)
-
-zcat : zcat.in
- cat $(VPATH)/zcat.in > $@
- chmod a+x zcat
+zcat : $(zcat_objs)
+ $(CXX) $(LDFLAGS) -o $@ $(zcat_objs)
zcmp : $(zcmp_objs)
$(CXX) $(LDFLAGS) -o $@ $(zcmp_objs)
@@ -44,28 +42,23 @@ zfgrep : zfgrep.in
cat $(VPATH)/zfgrep.in > $@
chmod a+x zfgrep
-zgrep : zgrep.in
- cat $(VPATH)/zgrep.in > $@
- chmod a+x zgrep
+zgrep : $(zgrep_objs)
+ $(CXX) $(LDFLAGS) -o $@ $(zgrep_objs)
-ztest : ztest.in
- cat $(VPATH)/ztest.in > $@
- chmod a+x ztest
+ztest : $(ztest_objs)
+ $(CXX) $(LDFLAGS) -o $@ $(ztest_objs)
zupdate : $(zupdate_objs)
$(CXX) $(LDFLAGS) -o $@ $(zupdate_objs)
-main.o : main.cc
- $(CXX) $(CPPFLAGS) $(CXXFLAGS) -DGREP=\"$(GREP)\" -c -o $@ $<
+rc.o : rc.cc
+ $(CXX) $(CPPFLAGS) $(CXXFLAGS) -DPROGVERSION=\"$(pkgversion)\" -DSYSCONFDIR=\"$(sysconfdir)\" -c -o $@ $<
zdiff.o : zdiff.cc
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -DDIFF=\"$(DIFF)\" -c -o $@ $<
-rc.o : rc.cc
- $(CXX) $(CPPFLAGS) $(CXXFLAGS) -DSYSCONFDIR=\"$(sysconfdir)\" -c -o $@ $<
-
-zutils.o : zutils.cc
- $(CXX) $(CPPFLAGS) $(CXXFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $<
+zgrep.o : zgrep.cc
+ $(CXX) $(CPPFLAGS) $(CXXFLAGS) -DGREP=\"$(GREP)\" -c -o $@ $<
%.o : %.cc
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
@@ -73,12 +66,14 @@ zutils.o : zutils.cc
$(objs) : Makefile
$(scripts) : Makefile
arg_parser.o : arg_parser.h
-main.o : arg_parser.h zutils.h rc.h zcat.cc zgrep.cc ztest.cc
-rc.o : arg_parser.h zutils.h rc.h
-zcmp.o : arg_parser.h zutils.h rc.h zcmpdiff.cc Makefile
-zdiff.o : arg_parser.h zutils.h rc.h zcmpdiff.cc Makefile
-zupdate.o : arg_parser.h zutils.h rc.h Makefile
-zutils.o : zutils.h rc.h
+rc.o : arg_parser.h rc.h
+zcat.o : arg_parser.h rc.h zutils.h recursive.cc
+zcmp.o : arg_parser.h rc.h zutils.h zcmpdiff.cc
+zdiff.o : arg_parser.h rc.h zutils.h zcmpdiff.cc
+zgrep.o : arg_parser.h rc.h zutils.h recursive.cc
+ztest.o : arg_parser.h rc.h zutils.h recursive.cc
+zupdate.o : arg_parser.h rc.h recursive.cc
+zutils.o : rc.h zutils.h
doc : info man
@@ -91,7 +86,7 @@ $(VPATH)/doc/$(pkgname).info : $(VPATH)/doc/$(pkgname).texinfo
man : $(VPATH)/doc/zcat.1 $(VPATH)/doc/zcmp.1 $(VPATH)/doc/zdiff.1 \
$(VPATH)/doc/zgrep.1 $(VPATH)/doc/ztest.1 $(VPATH)/doc/zupdate.1
-$(VPATH)/doc/zcat.1 : $(progname) zcat
+$(VPATH)/doc/zcat.1 : zcat
help2man -n 'decompress and concatenate files to standard output' \
-o $@ --no-info ./zcat
@@ -103,11 +98,11 @@ $(VPATH)/doc/zdiff.1 : zdiff
help2man -n 'decompress and compare two files line by line' \
-o $@ --no-info ./zdiff
-$(VPATH)/doc/zgrep.1 : $(progname) zgrep
+$(VPATH)/doc/zgrep.1 : zgrep
help2man -n 'search compressed files for a regular expression' \
-o $@ --no-info ./zgrep
-$(VPATH)/doc/ztest.1 : $(progname) ztest
+$(VPATH)/doc/ztest.1 : ztest
help2man -n 'verify integrity of compressed files' \
-o $@ --no-info ./ztest
@@ -125,14 +120,13 @@ install : install-bin install-info install-man
install-bin : all
if [ ! -d "$(DESTDIR)$(bindir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(bindir)" ; fi
- $(INSTALL_PROGRAM) ./$(progname) "$(DESTDIR)$(bindir)/$(progname)"
- $(INSTALL_SCRIPT) ./zcat "$(DESTDIR)$(bindir)/zcat"
+ $(INSTALL_PROGRAM) ./zcat "$(DESTDIR)$(bindir)/zcat"
$(INSTALL_PROGRAM) ./zcmp "$(DESTDIR)$(bindir)/zcmp"
$(INSTALL_PROGRAM) ./zdiff "$(DESTDIR)$(bindir)/zdiff"
$(INSTALL_SCRIPT) ./zegrep "$(DESTDIR)$(bindir)/zegrep"
$(INSTALL_SCRIPT) ./zfgrep "$(DESTDIR)$(bindir)/zfgrep"
- $(INSTALL_SCRIPT) ./zgrep "$(DESTDIR)$(bindir)/zgrep"
- $(INSTALL_SCRIPT) ./ztest "$(DESTDIR)$(bindir)/ztest"
+ $(INSTALL_PROGRAM) ./zgrep "$(DESTDIR)$(bindir)/zgrep"
+ $(INSTALL_PROGRAM) ./ztest "$(DESTDIR)$(bindir)/ztest"
$(INSTALL_PROGRAM) ./zupdate "$(DESTDIR)$(bindir)/zupdate"
if [ ! -e "$(DESTDIR)$(sysconfdir)/$(pkgname)rc" ] ; then \
if [ ! -d "$(DESTDIR)$(sysconfdir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(sysconfdir)" ; fi ; \
@@ -159,7 +153,6 @@ install-strip : all
uninstall : uninstall-bin uninstall-info uninstall-man
uninstall-bin :
- -rm -f "$(DESTDIR)$(bindir)/$(progname)"
-rm -f "$(DESTDIR)$(bindir)/zcat"
-rm -f "$(DESTDIR)$(bindir)/zcmp"
-rm -f "$(DESTDIR)$(bindir)/zdiff"
@@ -207,8 +200,7 @@ dist : doc
lzip -v -9 $(DISTNAME).tar
clean :
- -rm -f $(progname) $(progname)_profiled $(objs)
- -rm -f zcmp zcmp.o zdiff zdiff.o zupdate zupdate.o $(scripts)
+ -rm -f $(programs) $(scripts) $(objs)
distclean : clean
-rm -f Makefile config.status *.tar *.tar.lz
diff --git a/NEWS b/NEWS
index 202144b..682e97b 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,9 @@
Changes in version 1.2:
+The zutils executable has been removed. All utilities are now
+independent executables.
+
The new utility zupdate, which recompresses bzip2, gzip and xz files to
lzip format, has been added.
+
+The exit status of zgrep should now be identical to that of grep.
diff --git a/README b/README
index b47b5b2..1142876 100644
--- a/README
+++ b/README
@@ -10,16 +10,17 @@ These utilities are not wrapper scripts but safer and more efficient C++
programs. In particular the "--recursive" option is very efficient in
those utilities supporting it.
-The provided utilities are zcat, zcmp, zdiff, zgrep and ztest.
+The provided utilities are zcat, zcmp, zdiff, zgrep, ztest and zupdate.
The supported formats are bzip2, gzip, lzip and xz.
The compressor to be used for each format is configurable at runtime.
Zcat, zcmp, zdiff, and zgrep are improved replacements for the shell
-scripts provided with GNU gzip. Ztest is unique to zutils.
+scripts provided with GNU gzip. Ztest is unique to zutils. Zupdate is
+similar to gzip's znew.
NOTE: Bzip2 and lzip provide well-defined values of exit status, which
makes them safe to use with zutils. Gzip and xz may return ambiguous
-warning values, making them less reliable backends for zutils.
+warning values, making them less reliable back ends for zutils.
LANGUAGE NOTE: Uncompressed = not compressed = plain data; it may never
have been compressed. Decompressed is used to refer to data which has
diff --git a/configure b/configure
index f93d9fd..cf9c582 100755
--- a/configure
+++ b/configure
@@ -6,8 +6,7 @@
# to copy, distribute and modify it.
pkgname=zutils
-pkgversion=1.2-pre2
-progname=zutils
+pkgversion=1.2-pre3
srctrigger=doc/${pkgname}.texinfo
# clear some things potentially inherited from environment.
@@ -186,7 +185,6 @@ cat > Makefile << EOF
pkgname = ${pkgname}
pkgversion = ${pkgversion}
-progname = ${progname}
VPATH = ${srcdir}
prefix = ${prefix}
exec_prefix = ${exec_prefix}
diff --git a/doc/zcat.1 b/doc/zcat.1
index 7b0af25..1ceda79 100644
--- a/doc/zcat.1
+++ b/doc/zcat.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1.
-.TH ZCAT "1" "September 2013" "Zcat (zutils) 1.2-pre2" "User Commands"
+.TH ZCAT "1" "October 2013" "Zcat (zutils) 1.2-pre3" "User Commands"
.SH NAME
Zcat \- decompress and concatenate files to standard output
.SH SYNOPSIS
diff --git a/doc/zcmp.1 b/doc/zcmp.1
index dae6a3b..f07b725 100644
--- a/doc/zcmp.1
+++ b/doc/zcmp.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1.
-.TH ZCMP "1" "September 2013" "Zcmp (zutils) 1.2-pre2" "User Commands"
+.TH ZCMP "1" "October 2013" "Zcmp (zutils) 1.2-pre3" "User Commands"
.SH NAME
Zcmp \- decompress and compare two files byte by byte
.SH SYNOPSIS
diff --git a/doc/zdiff.1 b/doc/zdiff.1
index 7ef4be9..ce89018 100644
--- a/doc/zdiff.1
+++ b/doc/zdiff.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1.
-.TH ZDIFF "1" "September 2013" "Zdiff (zutils) 1.2-pre2" "User Commands"
+.TH ZDIFF "1" "October 2013" "Zdiff (zutils) 1.2-pre3" "User Commands"
.SH NAME
Zdiff \- decompress and compare two files line by line
.SH SYNOPSIS
diff --git a/doc/zgrep.1 b/doc/zgrep.1
index 7293471..8041fe0 100644
--- a/doc/zgrep.1
+++ b/doc/zgrep.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1.
-.TH ZGREP "1" "September 2013" "Zgrep (zutils) 1.2-pre2" "User Commands"
+.TH ZGREP "1" "October 2013" "Zgrep (zutils) 1.2-pre3" "User Commands"
.SH NAME
Zgrep \- search compressed files for a regular expression
.SH SYNOPSIS
diff --git a/doc/ztest.1 b/doc/ztest.1
index e0de3f2..cdf40f5 100644
--- a/doc/ztest.1
+++ b/doc/ztest.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1.
-.TH ZTEST "1" "September 2013" "Ztest (zutils) 1.2-pre2" "User Commands"
+.TH ZTEST "1" "October 2013" "Ztest (zutils) 1.2-pre3" "User Commands"
.SH NAME
Ztest \- verify integrity of compressed files
.SH SYNOPSIS
diff --git a/doc/zupdate.1 b/doc/zupdate.1
index 947e73a..63696ac 100644
--- a/doc/zupdate.1
+++ b/doc/zupdate.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1.
-.TH ZUPDATE "1" "September 2013" "Zupdate (zutils) 1.2-pre2" "User Commands"
+.TH ZUPDATE "1" "October 2013" "Zupdate (zutils) 1.2-pre3" "User Commands"
.SH NAME
Zupdate \- recompress bzip2, gzip, xz files to lzip files
.SH SYNOPSIS
@@ -35,6 +35,9 @@ output version information and exit
\fB\-f\fR, \fB\-\-force\fR
do not skip a file even if the .lz exists
.TP
+\fB\-k\fR, \fB\-\-keep\fR
+keep (don't delete) input files
+.TP
\fB\-l\fR, \fB\-\-lzip\-verbose\fR
pass a \fB\-v\fR option to the lzip compressor
.TP
diff --git a/doc/zutils.info b/doc/zutils.info
index b8a77da..59c7af2 100644
--- a/doc/zutils.info
+++ b/doc/zutils.info
@@ -1,4 +1,4 @@
-This is zutils.info, produced by makeinfo version 4.13 from
+This is zutils.info, produced by makeinfo version 4.13+ from
zutils.texinfo.
INFO-DIR-SECTION Data Compression
@@ -12,7 +12,7 @@ File: zutils.info, Node: Top, Next: Introduction, Up: (dir)
Zutils Manual
*************
-This manual is for Zutils (version 1.2-pre2, 4 September 2013).
+This manual is for Zutils (version 1.2-pre3, 11 October 2013).
* Menu:
@@ -47,7 +47,7 @@ used. Compressed files are decompressed on the fly; no temporary files
are created.
These utilities are not wrapper scripts but safer and more efficient
-C++ programs. In particular the `--recursive' option is very efficient
+C++ programs. In particular the '--recursive' option is very efficient
in those utilities supporting it.
The provided utilities are zcat, zcmp, zdiff, zgrep, ztest and zupdate.
@@ -60,7 +60,8 @@ similar to gzip's znew.
NOTE: Bzip2 and lzip provide well-defined values of exit status,
which makes them safe to use with zutils. Gzip and xz may return
-ambiguous warning values, making them less reliable backends for zutils.
+ambiguous warning values, making them less reliable back ends for
+zutils.
LANGUAGE NOTE: Uncompressed = not compressed = plain data; it may
never have been compressed. Decompressed is used to refer to data which
@@ -68,7 +69,7 @@ has undergone the process of decompression.
Numbers given as arguments to options (positions, sizes) may be
-followed by a multiplier and an optional `B' for "byte".
+followed by a multiplier and an optional 'B' for "byte".
Table of SI and binary prefixes (unit multipliers):
@@ -92,33 +93,33 @@ 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'
+'-h'
+'--help'
Print an informative help message describing the options and exit.
- Zgrep only supports the `--help' form of this option.
+ Zgrep only supports the '--help' form of this option.
-`-V'
-`--version'
+'-V'
+'--version'
Print the version number on the standard output and exit.
-`-N'
-`--no-rcfile'
- Don't read the runtime configuration file `zutilsrc'.
+'-N'
+'--no-rcfile'
+ Don't read the runtime configuration file 'zutilsrc'.
-`--bz2=COMMAND'
-`--gz=COMMAND'
-`--lz=COMMAND'
-`--xz=COMMAND'
+'--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
+ 'zutilsrc'. The compression program used must meet three
requirements:
- 1. When called with the `-d' option, it must read compressed
+ 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
+ 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
@@ -131,24 +132,24 @@ File: zutils.info, Node: The zutilsrc file, Next: Zcat, Prev: Common options,
3 The zutilsrc file
*******************
-`zutilsrc' is the runtime configuration file for zutils. In it you may
+'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
+'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.
+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
+ 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.
+ 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'.
+ where <format> is one of 'bz2', 'gz', 'lz' or 'xz'.

File: zutils.info, Node: Zcat, Next: Zcmp, Prev: The zutilsrc file, Up: Top
@@ -156,7 +157,7 @@ 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 decompressed content is
used. If a given file does not exist, and its name does not end with one
of the known extensions, zcat tries the compressed file names
@@ -175,58 +176,58 @@ Exit status is 0 if no errors occurred, non-zero otherwise.
Zcat supports the following options:
-`-A'
-`--show-all'
- Equivalent to `-vET'.
+'-A'
+'--show-all'
+ Equivalent to '-vET'.
-`-b'
-`--number-nonblank'
+'-b'
+'--number-nonblank'
Number all nonblank output lines, starting with 1. The line count
is unlimited.
-`-e'
- Equivalent to `-vE'.
+'-e'
+ Equivalent to '-vE'.
-`-E'
-`--show-ends'
- Print a `$' after the end of each line.
+'-E'
+'--show-ends'
+ Print a '$' after the end of each line.
-`--format=FMT'
+'--format=FMT'
Force the given compression format. Valid values for FMT are
- `bz2', `gz', `lz' and `xz'. If this option is used, the exact file
+ 'bz2', 'gz', 'lz' and 'xz'. If this option is used, the exact file
name must be given. Other names won't be tried.
-`-n'
-`--number'
+'-n'
+'--number'
Number all output lines, starting with 1. The line count is
unlimited.
-`-q'
-`--quiet'
+'-q'
+'--quiet'
Quiet operation. Suppress all messages.
-`-r'
-`--recursive'
+'-r'
+'--recursive'
Operate recursively on directories.
-`-s'
-`--squeeze-blank'
+'-s'
+'--squeeze-blank'
Replace multiple adjacent blank lines with a single blank line.
-`-t'
- Equivalent to `-vT'.
+'-t'
+ Equivalent to '-vT'.
-`-T'
-`--show-tabs'
- Print TAB characters as `^I'.
+'-T'
+'--show-tabs'
+ Print TAB characters as '^I'.
-`-v'
-`--show-nonprinting'
- Print control characters except for LF (newline) and TAB using `^'
- notation and precede characters larger than 127 with `M-' (which
+'-v'
+'--show-nonprinting'
+ Print control characters except for LF (newline) and TAB using '^'
+ notation and precede characters larger than 127 with 'M-' (which
stands for "meta").
-`--verbose'
+'--verbose'
Verbose mode. Show error messages.
@@ -236,7 +237,7 @@ File: zutils.info, Node: Zcmp, Next: Zdiff, Prev: Zcat, Up: Top
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
are numbered starting with 1. If any given file is compressed, its
decompressed content is used. Compressed files are decompressed on the
@@ -264,43 +265,43 @@ differences were found, and 2 means trouble.
Zcmp supports the following options:
-`-b'
-`--print-bytes'
- Print the differing bytes. Print control bytes as a `^' followed by
- a letter, and precede bytes larger than 127 with `M-' (which stands
+'-b'
+'--print-bytes'
+ Print the differing bytes. Print control bytes as a '^' followed by
+ a letter, and precede bytes larger than 127 with 'M-' (which stands
for "meta").
-`--format=[FMT1][,FMT2]'
+'--format=[FMT1][,FMT2]'
Force the given compression formats. Any of FMT1 or FMT2 may be
omitted and the corresponding format will be automatically
- detected. Valid values for FMT are `bz2', `gz', `lz' and `xz'. If
+ detected. Valid values for FMT are 'bz2', 'gz', 'lz' and 'xz'. If
at least one format is specified with this option, the exact file
names of both FILE1 and FILE2 must be given. Other names won't be
tried.
-`-i SIZE'
-`--ignore-initial=SIZE'
+'-i SIZE'
+'--ignore-initial=SIZE'
Ignore any differences in the first SIZE bytes of the input files.
Treat files with fewer than SIZE bytes as if they were empty. If
- SIZE is in the form `SIZE1,SIZE2', ignore the first SIZE1 bytes of
+ SIZE is in the form 'SIZE1,SIZE2', ignore the first SIZE1 bytes of
the first input file and the first SIZE2 bytes of the second input
file.
-`-l'
-`-v'
-`--list'
-`--verbose'
+'-l'
+'-v'
+'--list'
+'--verbose'
Print the byte numbers (in decimal) and values (in octal) of all
differing bytes.
-`-n COUNT'
-`--bytes=COUNT'
+'-n COUNT'
+'--bytes=COUNT'
Compare at most COUNT input bytes.
-`-q'
-`-s'
-`--quiet'
-`--silent'
+'-q'
+'-s'
+'--quiet'
+'--silent'
Do not print anything; only return an exit status indicating
whether the files differ.
@@ -311,7 +312,7 @@ File: zutils.info, Node: Zdiff, Next: Zgrep, Prev: Zcmp, Up: Top
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
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
@@ -339,74 +340,74 @@ differences were found, and 2 means trouble.
Zdiff supports the following options:
-`-a'
-`--text'
+'-a'
+'--text'
Treat all files as text.
-`-b'
-`--ignore-space-change'
+'-b'
+'--ignore-space-change'
Ignore changes in the amount of white space.
-`-B'
-`--ignore-blank-lines'
+'-B'
+'--ignore-blank-lines'
Ignore changes whose lines are all blank.
-`-c'
+'-c'
Use the context output format.
-`-C N'
-`--context=N'
+'-C N'
+'--context=N'
Same as -c but use N lines of context.
-`-d'
-`--minimal'
+'-d'
+'--minimal'
Try hard to find a smaller set of changes.
-`-E'
-`--ignore-tab-expansion'
+'-E'
+'--ignore-tab-expansion'
Ignore changes due to tab expansion.
-`--format=[FMT1][,FMT2]'
+'--format=[FMT1][,FMT2]'
Force the given compression formats. Any of FMT1 or FMT2 may be
omitted and the corresponding format will be automatically
- detected. Valid values for FMT are `bz2', `gz', `lz' and `xz'. If
+ detected. Valid values for FMT are 'bz2', 'gz', 'lz' and 'xz'. If
at least one format is specified with this option, the exact file
names of both FILE1 and FILE2 must be given. Other names won't be
tried.
-`-i'
-`--ignore-case'
+'-i'
+'--ignore-case'
Ignore case differences in file contents.
-`-p'
-`--show-c-function'
+'-p'
+'--show-c-function'
Show which C function each change is in.
-`-q'
-`--brief'
+'-q'
+'--brief'
Output only whether files differ.
-`-s'
-`--report-identical-files'
+'-s'
+'--report-identical-files'
Report when two files are identical.
-`-t'
-`--expand-tabs'
+'-t'
+'--expand-tabs'
Expand tabs to spaces in output.
-`-T'
-`--initial-tab'
+'-T'
+'--initial-tab'
Make tabs line up by prepending a tab.
-`-u'
+'-u'
Use the unified output format.
-`-U N'
-`--unified=N'
+'-U N'
+'--unified=N'
Same as -u but use N lines of context.
-`-w'
-`--ignore-all-space'
+'-w'
+'--ignore-all-space'
Ignore all white space.
@@ -437,112 +438,113 @@ matches were found, and 2 means trouble.
Zgrep supports the following options:
-`-a'
-`--text'
+'-a'
+'--text'
Treat all files as text.
-`-A N'
-`--after-context=N'
+'-A N'
+'--after-context=N'
Print N lines of trailing context.
-`-b'
-`--byte-offset'
+'-b'
+'--byte-offset'
Print the byte offset of each line.
-`-B N'
-`--before-context=N'
+'-B N'
+'--before-context=N'
Print N lines of leading context.
-`-c'
-`--count'
+'-c'
+'--count'
Only print a count of matching lines per file.
-`-C N'
-`--context=N'
+'-C N'
+'--context=N'
Print N lines of output context.
-`-e PATTERN'
-`--regexp=PATTERN'
+'-e PATTERN'
+'--regexp=PATTERN'
Use PATTERN as the pattern to match.
-`-E'
-`--extended-regexp'
+'-E'
+'--extended-regexp'
Treat PATTERN as an extended regular expression.
-`-f FILE'
-`--file=FILE'
+'-f FILE'
+'--file=FILE'
Obtain patterns from FILE, one per line.
-`-F'
-`--fixed-strings'
+'-F'
+'--fixed-strings'
Treat PATTERN as a set of newline-separated strings.
-`--format=FMT'
+'--format=FMT'
Force the given compression format. Valid values for FMT are
- `bz2', `gz', `lz' and `xz'. If this option is used, the exact file
+ 'bz2', 'gz', 'lz' and 'xz'. If this option is used, the exact file
name must be given. Other names won't be tried.
-`-h'
-`--no-filename'
+'-h'
+'--no-filename'
Suppress the prefixing of filenames on output when multiple files
are searched.
-`-H'
-`--with-filename'
+'-H'
+'--with-filename'
Print the filename for each match.
-`-i'
-`--ignore-case'
+'-i'
+'--ignore-case'
Ignore case distinctions.
-`-I'
+'-I'
Ignore binary files.
-`-l'
-`--files-with-matches'
+'-l'
+'--files-with-matches'
Only print names of files containing at least one match.
-`-L'
-`--files-without-match'
+'-L'
+'--files-without-match'
Only print names of files not containing any matches.
-`-m N'
-`--max-count=N'
+'-m N'
+'--max-count=N'
Stop after N matches.
-`-n'
-`--line-number'
+'-n'
+'--line-number'
Prefix each matched line with its line number in the input file.
-`-o'
-`--only-matching'
+'-o'
+'--only-matching'
Show only the part of matching lines that actually matches PATTERN.
-`-q'
-`--quiet'
- Suppress all messages.
+'-q'
+'--quiet'
+ Suppress all messages. Exit immediately with zero status if any
+ match is found, even if an error was detected.
-`-r'
-`--recursive'
+'-r'
+'--recursive'
Operate recursively on directories.
-`-s'
-`--no-messages'
- Suppress error messages.
+'-s'
+'--no-messages'
+ Suppress error messages about nonexistent or unreadable files.
-`-v'
-`--invert-match'
+'-v'
+'--invert-match'
Select non-matching lines.
-`--verbose'
+'--verbose'
Verbose mode. Show error messages.
-`-w'
-`--word-regexp'
+'-w'
+'--word-regexp'
Match only whole words.
-`-x'
-`--line-regexp'
+'-x'
+'--line-regexp'
Match only whole lines.
@@ -570,21 +572,21 @@ environmental problems (file not found, invalid flags, I/O errors, etc),
Ztest supports the following options:
-`--format=FMT'
+'--format=FMT'
Force the given compression format. Valid values for FMT are
- `bz2', `gz', `lz' and `xz'. If this option is used, all files not
+ 'bz2', 'gz', 'lz' and 'xz'. If this option is used, all files not
in the given format will fail.
-`-q'
-`--quiet'
+'-q'
+'--quiet'
Quiet operation. Suppress all messages.
-`-r'
-`--recursive'
+'-r'
+'--recursive'
Operate recursively on directories.
-`-v'
-`--verbose'
+'-v'
+'--verbose'
Verbose mode. Show the verify status for each file processed.
Further -v's increase the verbosity level.
@@ -604,15 +606,15 @@ is chosen as destination because it is by far the most appropriate for
long-term data archiving.
If the lzip compressed version of a file already exists, the file is
-skipped unless the `--force' option is given. In this case, if the
+skipped unless the '--force' option is given. In this case, if the
comparison fails, an error is returned and the original file is not
deleted. The operation of zupdate is meant to be safe and not produce
any data loss. Therefore, existing lzip compressed files are never
overwritten nor deleted.
The names of the original files must have one of the following
-extensions: `.bz2', `.tbz', `.tbz2', `.gz', `.tgz', `.xz', `.txz'. The
-files produced have the extensions `.lz' or `.tar.lz'.
+extensions: '.bz2', '.tbz', '.tbz2', '.gz', '.tgz', '.xz', '.txz'. The
+files produced have the extensions '.lz' or '.tar.lz'.
The format for running zupdate is:
@@ -623,35 +625,40 @@ recompressed (if needed), compared and deleted. Non-zero otherwise.
Zupdate supports the following options:
-`-f'
-`--force'
+'-f'
+'--force'
Do not skip a file for which a lzip compressed version already
- exists. `--force' compares the content of the input file with the
+ exists. '--force' compares the content of the input file with the
content of the lzip file and deletes the input file if both
contents are identical.
-`-l'
-`--lzip-verbose'
- Pass a `-v' option to the lzip compressor so that it shows the
+'-k'
+'--keep'
+ Keep (don't delete) the input file after comparing it with the
+ lzip file.
+
+'-l'
+'--lzip-verbose'
+ Pass a '-v' option to the lzip compressor so that it shows the
compression ratio for each file processed. Using lzip 1.15 and
- newer, a second `-l' shows the progress of compression. Use it
- together with `-v' to see the name of the file.
+ newer, a second '-l' shows the progress of compression. Use it
+ together with '-v' to see the name of the file.
-`-q'
-`--quiet'
+'-q'
+'--quiet'
Quiet operation. Suppress all messages.
-`-r'
-`--recursive'
+'-r'
+'--recursive'
Operate recursively on directories.
-`-v'
-`--verbose'
- Verbose mode. Show the files being processed. A second `-v' also
+'-v'
+'--verbose'
+ Verbose mode. Show the files being processed. A second '-v' also
shows the files being ignored.
-`-0 .. -9'
- Set the compression level of lzip. By default zupdate passes `-9'
+'-0 .. -9'
+ Set the compression level of lzip. By default zupdate passes '-9'
to lzip.
@@ -668,7 +675,7 @@ for all eternity, if not longer.
If you find a bug in zutils, please send electronic mail to
<zutils-bug@nongnu.org>. Include the version number, which you can find
-by running `zutils --version'.
+by running 'zcmp --version'.

File: zutils.info, Node: Concept index, Prev: Problems, Up: Top
@@ -694,18 +701,18 @@ Concept index

Tag Table:
-Node: Top224
+Node: Top225
Node: Introduction1156
-Node: Common options3258
-Node: The zutilsrc file4507
-Node: Zcat5433
-Node: Zcmp7355
-Node: Zdiff9679
-Node: Zgrep12182
-Node: Ztest15019
-Node: Zupdate16243
-Node: Problems18477
-Node: Concept index19010
+Node: Common options3259
+Node: The zutilsrc file4508
+Node: Zcat5434
+Node: Zcmp7356
+Node: Zdiff9680
+Node: Zgrep12183
+Node: Ztest15151
+Node: Zupdate16375
+Node: Problems18708
+Node: Concept index19239

End Tag Table
diff --git a/doc/zutils.texinfo b/doc/zutils.texinfo
index 5bbc692..e39b51f 100644
--- a/doc/zutils.texinfo
+++ b/doc/zutils.texinfo
@@ -6,8 +6,8 @@
@finalout
@c %**end of header
-@set UPDATED 4 September 2013
-@set VERSION 1.2-pre2
+@set UPDATED 11 October 2013
+@set VERSION 1.2-pre3
@dircategory Data Compression
@direntry
@@ -80,7 +80,7 @@ similar to gzip's znew.
NOTE: Bzip2 and lzip provide well-defined values of exit status, which
makes them safe to use with zutils. Gzip and xz may return ambiguous
-warning values, making them less reliable backends for zutils.
+warning values, making them less reliable back ends for zutils.
LANGUAGE NOTE: Uncompressed = not compressed = plain data; it may never
have been compressed. Decompressed is used to refer to data which has
@@ -570,7 +570,8 @@ Show only the part of matching lines that actually matches @var{pattern}.
@item -q
@itemx --quiet
-Suppress all messages.
+Suppress all messages. Exit immediately with zero status if any match is
+found, even if an error was detected.
@item -r
@itemx --recursive
@@ -578,7 +579,7 @@ Operate recursively on directories.
@item -s
@itemx --no-messages
-Suppress error messages.
+Suppress error messages about nonexistent or unreadable files.
@item -v
@itemx --invert-match
@@ -689,6 +690,10 @@ Do not skip a file for which a lzip compressed version already exists.
of the lzip file and deletes the input file if both contents are
identical.
+@item -k
+@itemx --keep
+Keep (don't delete) the input file after comparing it with the lzip file.
+
@item -l
@itemx --lzip-verbose
Pass a @samp{-v} option to the lzip compressor so that it shows the
@@ -728,7 +733,7 @@ for all eternity, if not longer.
If you find a bug in zutils, please send electronic mail to
@email{zutils-bug@@nongnu.org}. Include the version number, which you can
-find by running @w{@samp{zutils --version}}.
+find by running @w{@samp{zcmp --version}}.
@node Concept index
diff --git a/main.cc b/main.cc
deleted file mode 100644
index 04956b6..0000000
--- a/main.cc
+++ /dev/null
@@ -1,467 +0,0 @@
-/* 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 <cerrno>
-#include <climits>
-#include <csignal>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <list>
-#include <string>
-#include <vector>
-#include <dirent.h>
-#include <fcntl.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#if defined(__MSVCRT__) || defined(__OS2__)
-#include <io.h>
-#endif
-
-#include "arg_parser.h"
-#include "zutils.h"
-#include "rc.h"
-
-#if CHAR_BIT != 8
-#error "Environments where CHAR_BIT != 8 are not supported."
-#endif
-
-
-namespace {
-
-#ifdef O_BINARY
-const int o_binary = O_BINARY;
-#else
-const int o_binary = 0;
-#endif
-
-enum Mode { m_none, m_zcat, m_zgrep, m_ztest };
-
-
-void show_help()
- {
- std::printf( "Zutils is a collection of utilities able to deal with any combination of\n"
- "compressed and uncompressed files transparently. If any given file,\n"
- "including standard input, is compressed, its decompressed content is used.\n"
- "\nThe supported formats are bzip2, gzip, lzip and xz.\n"
- "\nUsage: %s <operation> [options] [files]\n", invocation_name );
- std::printf( "\nTry '%s <operation> --help' for more specific help.\n", invocation_name );
- std::printf( "\nOperations:\n"
- " -h, --help display this help and exit\n"
- " -V, --version output version information and exit\n"
- " --zcat zcat operation\n"
- " --zgrep zgrep operation\n"
- " --ztest ztest operation\n" );
- show_help_addr();
- }
-
-
-int simple_extension_index( const std::string & name )
- {
- for( int i = 0; i < num_formats; ++i )
- {
- const std::string ext( simple_extensions[i] );
- if( name.size() > ext.size() &&
- name.compare( name.size() - ext.size(), ext.size(), ext ) == 0 )
- return i;
- }
- return -1;
- }
-
-
-int open_instream( std::string & input_filename, const Mode program_mode,
- const bool search )
- {
- int infd = open( input_filename.c_str(), O_RDONLY | o_binary );
- if( infd < 0 )
- {
- if( search && ( program_mode == m_zcat || program_mode == m_zgrep ) &&
- simple_extension_index( input_filename ) < 0 )
- {
- for( int i = 0; i < num_formats; ++i )
- {
- const std::string name( input_filename +
- simple_extensions[format_order[i]] );
- infd = open( name.c_str(), O_RDONLY | o_binary );
- if( infd >= 0 ) { input_filename = name; break; }
- }
- }
- if( infd < 0 )
- show_error2( "Can't open input file", input_filename.c_str() );
- }
- return infd;
- }
-
-#include "zcat.cc"
-#include "zgrep.cc"
-#include "ztest.cc"
-
-} // end namespace
-
-
-int main( const int argc, const char * const argv[] )
- {
- enum { format_opt = 256, help_opt, verbose_opt,
- bz2_opt, gz_opt, lz_opt, xz_opt,
- zcat_opt, zgrep_opt, ztest_opt };
- const Arg_parser::Option * options = 0;
- int infd = -1;
- int format_index = -1;
- Mode program_mode = m_none;
- bool recursive = false;
- std::string input_filename;
- std::list< std::string > filenames;
- Cat_options cat_options;
- std::vector< const char * > grep_args; // args to grep, maybe empty
- std::vector< const char * > ztest_args; // args to ztest, maybe empty
- invocation_name = argv[0];
-
- const Arg_parser::Option m_zcat_options[] =
- {
- { 'A', "show-all", Arg_parser::no }, // cat
- { 'b', "number-nonblank", Arg_parser::no }, // cat
- { 'c', "stdout", Arg_parser::no }, // gzip
- { 'd', "decompress", Arg_parser::no }, // gzip
- { 'e', 0, Arg_parser::no }, // cat
- { 'E', "show-ends", Arg_parser::no }, // cat
- { 'f', "force", Arg_parser::no }, // gzip
- { 'h', "help", Arg_parser::no },
- { 'l', "list", Arg_parser::no }, // gzip
- { 'L', "license", Arg_parser::no }, // gzip
- { 'n', "number", Arg_parser::no }, // cat
- { 'N', "no-rcfile", Arg_parser::no },
- { 'q', "quiet", Arg_parser::no },
- { 'r', "recursive", Arg_parser::no },
- { 's', "squeeze-blank", Arg_parser::no }, // cat
- { 't', 0, Arg_parser::no }, // cat
- { 'T', "show-tabs", Arg_parser::no }, // cat
- { 'v', "show-nonprinting", Arg_parser::no }, // cat
- { 'V', "version", Arg_parser::no },
- { format_opt, "format", Arg_parser::yes },
- { verbose_opt, "verbose", Arg_parser::no },
- { bz2_opt, "bz2", Arg_parser::yes },
- { gz_opt, "gz", Arg_parser::yes },
- { lz_opt, "lz", Arg_parser::yes },
- { xz_opt, "xz", Arg_parser::yes },
- { zcat_opt, "zcat", Arg_parser::no },
- { 0 , 0, Arg_parser::no } };
-
- const Arg_parser::Option m_zgrep_options[] =
- {
- { 'a', "text", Arg_parser::no }, // grep GNU
- { 'A', "after-context", Arg_parser::yes }, // grep GNU
- { 'b', "byte-offset", Arg_parser::no }, // grep GNU
- { 'B', "before-context", Arg_parser::yes }, // grep GNU
- { 'c', "count", Arg_parser::no }, // grep
- { 'C', "context", Arg_parser::yes }, // grep GNU
- { 'e', "regexp", Arg_parser::yes }, // grep
- { 'E', "extended-regexp", Arg_parser::no }, // grep
- { 'f', "file ", Arg_parser::yes }, // grep
- { 'F', "fixed-strings", Arg_parser::no }, // grep
- { 'h', "no-filename", Arg_parser::no }, // grep GNU
- { 'H', "with-filename", Arg_parser::no }, // grep GNU
- { 'i', "ignore-case", Arg_parser::no }, // grep
- { 'I', 0, Arg_parser::no }, // grep GNU
- { 'l', "files-with-matches", Arg_parser::no }, // grep
- { 'L', "files-without-match", Arg_parser::no }, // grep GNU
- { 'm', "max-count", Arg_parser::yes }, // grep GNU
- { 'n', "line-number", Arg_parser::no }, // grep
- { 'N', "no-rcfile", Arg_parser::no },
- { 'o', "only-matching", Arg_parser::no }, // grep
- { 'q', "quiet", Arg_parser::no },
- { 'r', "recursive", Arg_parser::no },
- { 's', "no-messages", Arg_parser::no }, // grep
- { 'v', "invert-match", Arg_parser::no }, // grep
- { 'V', "version", Arg_parser::no },
- { 'w', "word-regexp", Arg_parser::no }, // grep GNU
- { 'x', "line-regexp", Arg_parser::no }, // grep
- { format_opt, "format", Arg_parser::yes },
- { help_opt, "help", Arg_parser::no },
- { verbose_opt, "verbose", Arg_parser::no },
- { bz2_opt, "bz2", Arg_parser::yes },
- { gz_opt, "gz", Arg_parser::yes },
- { lz_opt, "lz", Arg_parser::yes },
- { xz_opt, "xz", Arg_parser::yes },
- { zgrep_opt, "zgrep", Arg_parser::no },
- { 0 , 0, Arg_parser::no } };
-
- const Arg_parser::Option m_ztest_options[] =
- {
- { 'h', "help", Arg_parser::no },
- { 'N', "no-rcfile", Arg_parser::no },
- { 'q', "quiet", Arg_parser::no },
- { 'r', "recursive", Arg_parser::no },
- { 'v', "verbose", Arg_parser::no },
- { 'V', "version", Arg_parser::no },
- { format_opt, "format", Arg_parser::yes },
- { bz2_opt, "bz2", Arg_parser::yes },
- { gz_opt, "gz", Arg_parser::yes },
- { lz_opt, "lz", Arg_parser::yes },
- { xz_opt, "xz", Arg_parser::yes },
- { ztest_opt, "ztest", Arg_parser::no },
- { 0 , 0, Arg_parser::no } };
-
- { // parse operation
- const Arg_parser::Option operations[] =
- {
- { 'h', "help", Arg_parser::no },
- { 'V', "version", Arg_parser::no },
- { zcat_opt, "zcat", Arg_parser::no },
- { zgrep_opt, "zgrep", Arg_parser::no },
- { ztest_opt, "ztest", Arg_parser::no },
- { 0 , 0, Arg_parser::no } };
-
- const Arg_parser parser( argv[1], ( argc > 2 ) ? argv[2] : 0, operations );
- if( parser.error().size() ) // bad operation
- { show_error( parser.error().c_str(), 0, true ); return 1; }
-
- if( parser.arguments() > 0 )
- {
- switch( parser.code( 0 ) )
- {
- case 0 : break;
- case 'h': show_help(); return 0;
- case 'V': show_version(); return 0;
- case zcat_opt : program_mode = m_zcat; options = m_zcat_options;
- util_name = "zcat"; break;
- case zgrep_opt : program_mode = m_zgrep; options = m_zgrep_options;
- util_name = "zgrep"; break;
- case ztest_opt : program_mode = m_ztest; options = m_ztest_options;
- util_name = "ztest"; break;
- default : internal_error( "uncaught option" );
- }
- }
-
-#if defined(__MSVCRT__) || defined(__OS2__)
- setmode( STDIN_FILENO, O_BINARY );
- setmode( STDOUT_FILENO, O_BINARY );
-#endif
-
- if( program_mode == m_none )
- {
- show_error( "You must specify the operation to be performed.", 0, true );
- return 1;
- }
- } // end parse operation
-
- const int eretval = ( program_mode == m_zgrep ) ? 2 : 1;
- const Arg_parser parser( argc, argv, options );
- if( parser.error().size() ) // bad option
- { show_error( parser.error().c_str(), 0, true ); return eretval; }
-
- maybe_process_config_file( parser );
-
- int argind = 0;
- int grep_show_name = -1;
- int grep_list_mode = 0; // 1 = list matches, -1 = list non matches
- bool grep_pattern_found = false;
- for( ; argind < parser.arguments(); ++argind )
- {
- const int code = parser.code( argind );
- const char * const arg = parser.argument( argind ).c_str();
- if( !code )
- {
- if( program_mode == m_zgrep && !grep_pattern_found )
- { grep_args.push_back( arg ); grep_pattern_found = true; continue; }
- else break; // no more options
- }
- switch( code ) // common options
- {
- 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 )
- {
- case m_none: internal_error( "invalid operation" ); break;
- case m_zcat:
- switch( code )
- {
- case 'A': cat_options.show_ends = true;
- cat_options.show_nonprinting = true;
- cat_options.show_tabs = true; break;
- case 'b': cat_options.number_lines = 1; break;
- case 'c': break;
- case 'd': break;
- case 'e': cat_options.show_nonprinting = true; // fall through
- case 'E': cat_options.show_ends = true; break;
- case 'f': break;
- case 'h': show_zcat_help(); return 0;
- case 'l': break;
- case 'L': break;
- case 'n': if( cat_options.number_lines == 0 )
- { cat_options.number_lines = 2; } break;
- case 'q': verbosity = -1; break;
- case 'r': recursive = true; break;
- case 's': cat_options.squeeze_blank = true; break;
- case 't': cat_options.show_nonprinting = true; // fall through
- case 'T': cat_options.show_tabs = true; break;
- case 'v': cat_options.show_nonprinting = true; break;
- case 'V': show_version( "Zcat" ); return 0;
- case verbose_opt : if( verbosity < 4 ) ++verbosity; break;
- case zcat_opt : break;
- default : internal_error( "uncaught option" );
- } break;
- case m_zgrep:
- switch( code )
- {
- case 'a': grep_args.push_back( "-a" ); break;
- case 'A': grep_args.push_back( "-A" ); grep_args.push_back( arg ); break;
- case 'b': grep_args.push_back( "-b" ); break;
- case 'B': grep_args.push_back( "-B" ); grep_args.push_back( arg ); break;
- case 'c': grep_args.push_back( "-c" ); break;
- case 'C': grep_args.push_back( "-C" ); grep_args.push_back( arg ); break;
- case 'e': grep_args.push_back( "-e" ); grep_args.push_back( arg );
- grep_pattern_found = true; break;
- case 'E': grep_args.push_back( "-E" ); break;
- case 'f': grep_args.push_back( "-f" ); grep_args.push_back( arg );
- grep_pattern_found = true; break;
- case 'F': grep_args.push_back( "-F" ); break;
- case 'h': grep_show_name = false; break;
- case 'H': grep_show_name = true; break;
- case 'i': grep_args.push_back( "-i" ); break;
- case 'I': grep_args.push_back( "-I" ); break;
- case 'l': grep_args.push_back( "-l" ); grep_list_mode = 1; break;
- case 'L': grep_args.push_back( "-L" ); grep_list_mode = -1; break;
- case 'm': grep_args.push_back( "-m" ); grep_args.push_back( arg ); break;
- case 'n': grep_args.push_back( "-n" ); break;
- case 'o': grep_args.push_back( "-o" ); break;
- case 'q': grep_args.push_back( "-q" ); verbosity = -1; break;
- case 'r': recursive = true; break;
- case 's': grep_args.push_back( "-s" ); verbosity = -1; break;
- case 'v': grep_args.push_back( "-v" ); break;
- case 'V': show_version( "Zgrep" ); return 0;
- case 'w': grep_args.push_back( "-w" ); break;
- case 'x': grep_args.push_back( "-x" ); break;
- case help_opt : show_zgrep_help(); return 0;
- case verbose_opt : if( verbosity < 4 ) ++verbosity; break;
- case zgrep_opt : break;
- default : internal_error( "uncaught option" );
- } break;
- case m_ztest:
- switch( code )
- {
- case 'h': show_ztest_help(); return 0;
- case 'q': verbosity = -1; ztest_args.push_back( "-q" ); break;
- case 'r': recursive = true; break;
- case 'v': if( verbosity < 4 ) ++verbosity;
- ztest_args.push_back( "-v" ); break;
- case 'V': show_version( "Ztest" ); return 0;
- case ztest_opt : break;
- default : internal_error( "uncaught option" );
- } break;
- }
- } // end process options
-
- if( program_mode == m_zgrep && !grep_pattern_found )
- { show_error( "Pattern not found." ); return 2; }
-
- for( ; argind < parser.arguments(); ++argind )
- filenames.push_back( parser.argument( argind ) );
-
- if( filenames.empty() ) filenames.push_back("-");
-
- if( grep_show_name < 0 )
- grep_show_name = ( filenames.size() != 1 || recursive );
-
- int retval = ( program_mode == m_zgrep ) ? 1 : 0;
- while( !filenames.empty() )
- {
- input_filename = filenames.front();
- filenames.pop_front();
- if( !input_filename.size() || input_filename == "-" )
- {
- input_filename.clear();
- infd = STDIN_FILENO;
- }
- else
- {
- if( recursive )
- {
- struct stat st;
- if( stat( input_filename.c_str(), &st ) == 0 && S_ISDIR( st.st_mode ) )
- {
- DIR * const dirp = opendir( input_filename.c_str() );
- if( !dirp )
- {
- show_error2( "Can't open directory", input_filename.c_str() );
- if( retval < 1 ) retval = 1; continue;
- }
- std::list< std::string > tmp_list;
- while( true )
- {
- const struct dirent * const entryp = readdir( dirp );
- if( !entryp ) { closedir( dirp ); break; }
- std::string tmp_name( entryp->d_name );
- if( tmp_name != "." && tmp_name != ".." )
- tmp_list.push_back( input_filename + "/" + tmp_name );
- }
- filenames.splice( filenames.begin(), tmp_list );
- continue;
- }
- }
- infd = open_instream( input_filename, program_mode, format_index < 0 );
- if( infd < 0 ) { if( retval < 1 ) retval = 1; continue; }
- }
-
- int tmp = 0;
- switch( program_mode )
- {
- case m_none:
- break;
- case m_zcat:
- tmp = cat( infd, format_index, input_filename, cat_options );
- break;
- case m_zgrep:
- if( infd == STDIN_FILENO )
- tmp = zgrep_stdin( infd, format_index, grep_args );
- else tmp = zgrep_file( infd, format_index, input_filename, grep_args,
- grep_list_mode, grep_show_name );
- break;
- case m_ztest:
- if( infd == STDIN_FILENO )
- tmp = ztest_stdin( infd, format_index, ztest_args );
- else tmp = ztest_file( infd, format_index, input_filename, ztest_args );
- break;
- }
- if( program_mode == m_zgrep )
- { if( tmp == 0 || ( tmp == 2 && retval == 1 ) ) retval = tmp; }
- else if( tmp > retval ) retval = tmp;
-
- if( input_filename.size() )
- { close( infd ); infd = -1; }
- }
-
- if( std::fclose( stdout ) != 0 )
- {
- show_error( "Can't close stdout", errno );
- switch( program_mode )
- {
- case m_none: break;
- case m_zcat: retval = 1; break;
- case m_zgrep: if( retval != 0 || verbosity >= 0 ) retval = 2; break;
- case m_ztest: if( retval == 0 ) retval = 1; break;
- }
- }
- return retval;
- }
diff --git a/rc.cc b/rc.cc
index dac205c..3d991fc 100644
--- a/rc.cc
+++ b/rc.cc
@@ -17,19 +17,28 @@
#define _FILE_OFFSET_BITS 64
+#include <cerrno>
#include <cstdio>
#include <cstdlib>
+#include <cstring>
#include <string>
#include <vector>
-#include <stdint.h>
+#include <unistd.h>
+#include <sys/wait.h>
#include "arg_parser.h"
-#include "zutils.h"
#include "rc.h"
+const char * invocation_name = 0;
+const char * program_name = 0;
+int verbosity = 0;
+
namespace {
+const char * const config_file_name = "zutilsrc";
+const char * const program_year = "2013";
+
std::string compressor_names[num_formats] =
{ "bzip2", "gzip", "lzip", "xz" }; // default compressor names
@@ -227,3 +236,102 @@ const std::vector< std::string > & get_compressor_args( const int format_index )
{
return compressor_args[format_index];
}
+
+
+void show_help_addr()
+ {
+ std::printf( "\nReport bugs to zutils-bug@nongnu.org\n"
+ "Zutils home page: http://www.nongnu.org/zutils/zutils.html\n" );
+ }
+
+
+void show_version( const char * const Program_name )
+ {
+ std::printf( "%s (zutils) %s\n", Program_name, PROGVERSION );
+ std::printf( "Copyright (C) %s Antonio Diaz Diaz.\n", program_year );
+ std::printf( "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n" );
+ }
+
+
+void show_error( const char * const msg, const int errcode, const bool help )
+ {
+ if( verbosity >= 0 )
+ {
+ if( msg && msg[0] )
+ {
+ std::fprintf( stderr, "%s: %s", program_name, msg );
+ if( errcode > 0 )
+ std::fprintf( stderr, ": %s", std::strerror( errcode ) );
+ std::fprintf( stderr, "\n" );
+ }
+ if( help )
+ std::fprintf( stderr, "Try '%s --help' for more information.\n",
+ invocation_name );
+ }
+ }
+
+
+void show_error2( const char * const msg, const char * const name )
+ {
+ if( verbosity >= 0 )
+ std::fprintf( stderr, "%s: %s '%s': %s.\n",
+ program_name, msg, name, std::strerror( errno ) );
+ }
+
+
+void internal_error( const char * const msg )
+ {
+ if( verbosity >= 0 )
+ std::fprintf( stderr, "%s: internal error: %s.\n", program_name, msg );
+ std::exit( 3 );
+ }
+
+
+void show_close_error( const char * const prog_name )
+ {
+ if( verbosity >= 0 )
+ std::fprintf( stderr, "%s: Can't close output of %s: %s.\n",
+ program_name, prog_name, std::strerror( errno ) );
+ }
+
+
+void show_exec_error( const char * const prog_name )
+ {
+ if( verbosity >= 0 )
+ std::fprintf( stderr, "%s: Can't exec '%s': %s.\n",
+ program_name, prog_name, std::strerror( errno ) );
+ }
+
+
+void show_fork_error( const char * const prog_name )
+ {
+ if( verbosity >= 0 )
+ std::fprintf( stderr, "%s: Can't fork '%s': %s.\n",
+ program_name, prog_name, std::strerror( errno ) );
+ }
+
+
+int wait_for_child( const pid_t pid, const char * const name,
+ const int eretval, const bool isgzxz )
+ {
+ int status;
+ while( waitpid( pid, &status, 0 ) == -1 )
+ {
+ if( errno != EINTR )
+ {
+ if( verbosity >= 0 )
+ std::fprintf( stderr, "%s: Error waiting termination of '%s': %s.\n",
+ program_name, name, std::strerror( errno ) );
+ _exit( eretval );
+ }
+ }
+ if( WIFEXITED( status ) )
+ {
+ const int tmp = WEXITSTATUS( status );
+ if( isgzxz && eretval == 1 && tmp == 1 ) return 2; // for ztest
+ return tmp;
+ }
+ return eretval;
+ }
diff --git a/rc.h b/rc.h
index 2f65800..9c28e9c 100644
--- a/rc.h
+++ b/rc.h
@@ -15,6 +15,17 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+enum { fmt_bz2, fmt_gz, fmt_lz, fmt_xz, num_formats };
+const char * const format_names[num_formats] = { "bz2", "gz", "lz", "xz" };
+const char * const simple_extensions[num_formats] =
+ { ".bz2", ".gz", ".lz", ".xz" };
+const int format_order[num_formats] =
+ { fmt_lz, fmt_bz2, fmt_gz, fmt_xz }; // search order
+
+extern const char * invocation_name;
+extern const char * program_name;
+extern int verbosity;
+
class Arg_parser;
void maybe_process_config_file( const Arg_parser & parser );
@@ -23,5 +34,19 @@ 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 );
+
+void show_help_addr();
+void show_version( const char * const Program_name );
+void show_error( const char * const msg, const int errcode = 0,
+ const bool help = false );
+void show_error2( const char * const msg, const char * const name );
+void internal_error( const char * const msg );
+void show_close_error( const char * const prog_name = "data feeder" );
+void show_exec_error( const char * const prog_name );
+void show_fork_error( const char * const prog_name );
+
+// Returns exit status of child process 'pid', or 'eretval' in case of error.
+//
+int wait_for_child( const pid_t pid, const char * const name,
+ const int eretval = 2, const bool isgzxz = false );
diff --git a/recursive.cc b/recursive.cc
new file mode 100644
index 0000000..09aa095
--- /dev/null
+++ b/recursive.cc
@@ -0,0 +1,61 @@
+/* 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/>.
+*/
+
+bool next_filename( std::list< std::string > & filenames,
+ std::string & input_filename, bool & error,
+ const bool recursive, const bool ignore_stdin = false,
+ const bool no_messages = false )
+ {
+ while( !filenames.empty() )
+ {
+ input_filename = filenames.front();
+ filenames.pop_front();
+ if( input_filename.empty() || input_filename == "-" )
+ {
+ if( ignore_stdin ) continue;
+ input_filename.clear(); return true;
+ }
+ if( recursive )
+ {
+ struct stat st;
+ if( stat( input_filename.c_str(), &st ) == 0 && S_ISDIR( st.st_mode ) )
+ {
+ DIR * const dirp = opendir( input_filename.c_str() );
+ if( !dirp )
+ {
+ if( !no_messages )
+ show_error2( "Can't open directory", input_filename.c_str() );
+ error = true; continue;
+ }
+ std::list< std::string > tmp_list;
+ while( true )
+ {
+ const struct dirent * const entryp = readdir( dirp );
+ if( !entryp ) { closedir( dirp ); break; }
+ std::string tmp_name( entryp->d_name );
+ if( tmp_name != "." && tmp_name != ".." )
+ tmp_list.push_back( input_filename + "/" + tmp_name );
+ }
+ filenames.splice( filenames.begin(), tmp_list );
+ continue;
+ }
+ }
+ return true;
+ }
+ input_filename.clear();
+ return false;
+ }
diff --git a/testsuite/check.sh b/testsuite/check.sh
index cd01c46..faf9dd3 100755
--- a/testsuite/check.sh
+++ b/testsuite/check.sh
@@ -46,9 +46,7 @@ cat in.lz > lz_only.lz || framework_failure
cat in in in in in in > in6 || framework_failure
fail=0
-printf "testing zutils-%s..." "$2"
-
-printf "\ntesting zcat-%s..." "$2"
+printf "testing zcat-%s..." "$2"
for i in ${extensions}; do
"${ZCAT}" -N in.$i > copy || fail=1
@@ -299,6 +297,8 @@ if [ $? != 0 ] ; then printf . ; else printf - ; fail=1 ; fi
if [ $? != 0 ] ; then printf . ; else printf - ; fail=1 ; fi
"${ZGREP}" -N --bad-option 2> /dev/null
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
+"${ZGREP}" -N "GNU" -s nx_file
+if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
"${ZEGREP}" -N "GNU" in > /dev/null || fail=1
printf .
@@ -388,8 +388,8 @@ rm -f x.lz || framework_failure
cat in.bz2 > x.bz2 || framework_failure
cat in.gz > x.gz || framework_failure
-"${ZUPDATE}" -N -f x.bz2 x.gz 2> /dev/null
-if [ $? = 0 ] && [ ! -e x.bz2 ] && [ ! -e x.gz ] && [ -e x.lz ] ; then printf .
+"${ZUPDATE}" -N -f -k x.bz2 x.gz 2> /dev/null
+if [ $? = 0 ] && [ -e x.bz2 ] && [ -e x.gz ] && [ -e x.lz ] ; then printf .
else printf - ; fail=1
fi
rm -f x.lz || framework_failure
diff --git a/zcat.cc b/zcat.cc
index 366a860..a20d514 100644
--- a/zcat.cc
+++ b/zcat.cc
@@ -15,6 +15,39 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#define _FILE_OFFSET_BITS 64
+
+#include <cerrno>
+#include <climits>
+#include <csignal>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <list>
+#include <string>
+#include <vector>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#if defined(__MSVCRT__) || defined(__OS2__)
+#include <io.h>
+#endif
+
+#include "arg_parser.h"
+#include "rc.h"
+#include "zutils.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+
+namespace {
+
+#include "recursive.cc"
+
struct Cat_options
{
int number_lines; // 0 = no, 1 = nonblank, 2 = all
@@ -58,7 +91,7 @@ public:
Line_number line_number;
-void show_zcat_help()
+void show_help()
{
std::printf( "Zcat copies each given file (\"-\" means standard input), to standard\n"
"output. If any given file is compressed, its decompressed content is\n"
@@ -96,6 +129,41 @@ void show_zcat_help()
}
+int simple_extension_index( const std::string & name )
+ {
+ for( int i = 0; i < num_formats; ++i )
+ {
+ const std::string ext( simple_extensions[i] );
+ if( name.size() > ext.size() &&
+ name.compare( name.size() - ext.size(), ext.size(), ext ) == 0 )
+ return i;
+ }
+ return -1;
+ }
+
+
+int open_instream( std::string & input_filename, const bool search )
+ {
+ int infd = open( input_filename.c_str(), O_RDONLY | O_BINARY );
+ if( infd < 0 )
+ {
+ if( search && simple_extension_index( input_filename ) < 0 )
+ {
+ for( int i = 0; i < num_formats; ++i )
+ {
+ const std::string name( input_filename +
+ simple_extensions[format_order[i]] );
+ infd = open( name.c_str(), O_RDONLY | O_BINARY );
+ if( infd >= 0 ) { input_filename = name; break; }
+ }
+ }
+ if( infd < 0 )
+ show_error2( "Can't open input file", input_filename.c_str() );
+ }
+ return infd;
+ }
+
+
int do_cat( const int infd, const int buffer_size,
uint8_t * const inbuf, uint8_t * const outbuf,
const std::string & input_filename,
@@ -210,7 +278,133 @@ int cat( int infd, const int format_index, const std::string & input_filename,
if( !good_status( children, retval == 0 ) ) retval = 1;
if( retval == 0 && close( infd ) != 0 )
- { show_close_error( "data feeder" ); retval = 1; }
+ { show_close_error(); retval = 1; }
delete[] outbuf; delete[] inbuf;
return retval;
}
+
+} // end namespace
+
+
+int main( const int argc, const char * const argv[] )
+ {
+ enum { format_opt = 256, verbose_opt, bz2_opt, gz_opt, lz_opt, xz_opt };
+ int infd = -1;
+ int format_index = -1;
+ bool recursive = false;
+ std::string input_filename;
+ std::list< std::string > filenames;
+ Cat_options cat_options;
+ invocation_name = argv[0];
+ program_name = "zcat";
+
+ const Arg_parser::Option options[] =
+ {
+ { 'A', "show-all", Arg_parser::no }, // cat
+ { 'b', "number-nonblank", Arg_parser::no }, // cat
+ { 'c', "stdout", Arg_parser::no }, // gzip
+ { 'd', "decompress", Arg_parser::no }, // gzip
+ { 'e', 0, Arg_parser::no }, // cat
+ { 'E', "show-ends", Arg_parser::no }, // cat
+ { 'f', "force", Arg_parser::no }, // gzip
+ { 'h', "help", Arg_parser::no },
+ { 'l', "list", Arg_parser::no }, // gzip
+ { 'L', "license", Arg_parser::no }, // gzip
+ { 'n', "number", Arg_parser::no }, // cat
+ { 'N', "no-rcfile", Arg_parser::no },
+ { 'q', "quiet", Arg_parser::no },
+ { 'r', "recursive", Arg_parser::no },
+ { 's', "squeeze-blank", Arg_parser::no }, // cat
+ { 't', 0, Arg_parser::no }, // cat
+ { 'T', "show-tabs", Arg_parser::no }, // cat
+ { 'v', "show-nonprinting", Arg_parser::no }, // cat
+ { 'V', "version", Arg_parser::no },
+ { format_opt, "format", Arg_parser::yes },
+ { verbose_opt, "verbose", Arg_parser::no },
+ { bz2_opt, "bz2", Arg_parser::yes },
+ { gz_opt, "gz", Arg_parser::yes },
+ { lz_opt, "lz", Arg_parser::yes },
+ { xz_opt, "xz", Arg_parser::yes },
+ { 0 , 0, Arg_parser::no } };
+
+ const Arg_parser parser( argc, argv, options );
+ if( parser.error().size() ) // bad option
+ { show_error( parser.error().c_str(), 0, true ); return 1; }
+
+ maybe_process_config_file( parser );
+
+ int argind = 0;
+ for( ; argind < parser.arguments(); ++argind )
+ {
+ const int code = parser.code( argind );
+ if( !code ) break; // no more options
+ const char * const arg = parser.argument( argind ).c_str();
+ switch( code )
+ {
+ case 'A': cat_options.show_ends = true;
+ cat_options.show_nonprinting = true;
+ cat_options.show_tabs = true; break;
+ case 'b': cat_options.number_lines = 1; break;
+ case 'c': break;
+ case 'd': break;
+ case 'e': cat_options.show_nonprinting = true; // fall through
+ case 'E': cat_options.show_ends = true; break;
+ case 'f': break;
+ case 'h': show_help(); return 0;
+ case 'l': break;
+ case 'L': break;
+ case 'n': if( cat_options.number_lines == 0 )
+ { cat_options.number_lines = 2; } break;
+ case 'N': break;
+ case 'q': verbosity = -1; break;
+ case 'r': recursive = true; break;
+ case 's': cat_options.squeeze_blank = true; break;
+ case 't': cat_options.show_nonprinting = true; // fall through
+ case 'T': cat_options.show_tabs = true; break;
+ case 'v': cat_options.show_nonprinting = true; break;
+ case 'V': show_version( "Zcat" ); return 0;
+ case format_opt: format_index = parse_format_type( arg ); break;
+ case verbose_opt: if( verbosity < 4 ) ++verbosity; break;
+ case bz2_opt: parse_compressor( arg, fmt_bz2, 1 ); break;
+ case gz_opt: parse_compressor( arg, fmt_gz, 1 ); break;
+ case lz_opt: parse_compressor( arg, fmt_lz, 1 ); break;
+ case xz_opt: parse_compressor( arg, fmt_xz, 1 ); break;
+ default : internal_error( "uncaught option" );
+ }
+ } // end process options
+
+#if defined(__MSVCRT__) || defined(__OS2__)
+ setmode( STDIN_FILENO, O_BINARY );
+ setmode( STDOUT_FILENO, O_BINARY );
+#endif
+
+ for( ; argind < parser.arguments(); ++argind )
+ filenames.push_back( parser.argument( argind ) );
+
+ if( filenames.empty() ) filenames.push_back("-");
+
+ int retval = 0;
+ bool error = false;
+ while( next_filename( filenames, input_filename, error, recursive ) )
+ {
+ if( input_filename.empty() ) infd = STDIN_FILENO;
+ else
+ {
+ infd = open_instream( input_filename, format_index < 0 );
+ if( infd < 0 ) { error = true; continue; }
+ }
+
+ const int tmp = cat( infd, format_index, input_filename, cat_options );
+ if( tmp > retval ) retval = tmp;
+
+ if( input_filename.size() ) { close( infd ); infd = -1; }
+ }
+
+ if( std::fclose( stdout ) != 0 )
+ {
+ show_error( "Can't close stdout", errno );
+ error = true;
+ }
+ if( error && retval == 0 ) retval = 1;
+ return retval;
+ }
diff --git a/zcat.in b/zcat.in
deleted file mode 100644
index d88eef1..0000000
--- a/zcat.in
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /bin/sh
-bindir=`echo "$0" | sed -e 's,[^/]*$,,'`
-exec "${bindir}"zutils --zcat "$@"
diff --git a/zcmp.cc b/zcmp.cc
index 3522329..761e48e 100644
--- a/zcmp.cc
+++ b/zcmp.cc
@@ -36,12 +36,8 @@
#endif
#include "arg_parser.h"
-#include "zutils.h"
#include "rc.h"
-
-#if CHAR_BIT != 8
-#error "Environments where CHAR_BIT != 8 are not supported."
-#endif
+#include "zutils.h"
#ifndef LLONG_MAX
#define LLONG_MAX 0x7FFFFFFFFFFFFFFFLL
@@ -52,7 +48,6 @@ namespace {
#include "zcmpdiff.cc"
-
void show_help()
{
std::printf( "Zcmp compares two files (\"-\" means standard input), and if they\n"
@@ -301,7 +296,7 @@ int cmp( const long long max_size, const int infd[2],
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: EOF on %s\n",
- util_name, filenames[rd[1]<rd[0]].c_str() );
+ program_name, filenames[rd[1]<rd[0]].c_str() );
return 1;
}
if( min_rd != buffer_size ) break;
@@ -323,7 +318,7 @@ int main( const int argc, const char * const argv[] )
int format_types[2] = { -1, -1 };
bool print_bytes = false;
invocation_name = argv[0];
- util_name = "zcmp";
+ program_name = "zcmp";
const Arg_parser::Option options[] =
{
@@ -443,7 +438,7 @@ int main( const int argc, const char * const argv[] )
for( int i = 0; i < 2; ++i )
{
if( close( infd[i] ) != 0 )
- { show_close_error( "data feeder" ); retval = 2; }
+ { show_close_error(); retval = 2; }
if( filenames[i] != "-" && close( old_infd[i] ) != 0 )
{
show_error2( "Can't close input file", filenames[i].c_str() );
diff --git a/zcmpdiff.cc b/zcmpdiff.cc
index 71bcec6..d02a85f 100644
--- a/zcmpdiff.cc
+++ b/zcmpdiff.cc
@@ -15,10 +15,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifdef O_BINARY
-const int o_binary = O_BINARY;
-#else
-const int o_binary = 0;
+#ifndef O_BINARY
+#define O_BINARY 0
#endif
struct { const char * from; const char * to; } const known_extensions[] = {
@@ -36,7 +34,7 @@ struct { const char * from; const char * to; } const known_extensions[] = {
int open_instream( const std::string & input_filename )
{
- int infd = open( input_filename.c_str(), O_RDONLY | o_binary );
+ int infd = open( input_filename.c_str(), O_RDONLY | O_BINARY );
if( infd < 0 )
show_error2( "Can't open input file", input_filename.c_str() );
return infd;
@@ -53,13 +51,13 @@ int open_other_instream( std::string & name )
{
name.resize( name.size() - from.size() );
name += known_extensions[i].to;
- return open( name.c_str(), O_RDONLY | o_binary );
+ return open( name.c_str(), O_RDONLY | O_BINARY );
}
}
for( int i = 0; i < num_formats; ++i )
{ // search compressed version
const std::string s( name + simple_extensions[format_order[i]] );
- const int infd = open( s.c_str(), O_RDONLY | o_binary );
+ const int infd = open( s.c_str(), O_RDONLY | O_BINARY );
if( infd >= 0 ) { name = s; return infd; }
}
return -1;
diff --git a/zdiff.cc b/zdiff.cc
index 3f0d07f..a57630a 100644
--- a/zdiff.cc
+++ b/zdiff.cc
@@ -36,12 +36,8 @@
#endif
#include "arg_parser.h"
-#include "zutils.h"
#include "rc.h"
-
-#if CHAR_BIT != 8
-#error "Environments where CHAR_BIT != 8 are not supported."
-#endif
+#include "zutils.h"
namespace {
@@ -50,7 +46,6 @@ std::string fifonames[2]; // names of the two fifos passed to diff
#include "zcmpdiff.cc"
-
void show_help()
{
std::printf( "Zdiff compares two files (\"-\" means standard input), and if they\n"
@@ -177,7 +172,7 @@ bool set_data_feeder( const std::string & fifoname, const int infd,
!feed_data( infd, fda[1], magic_data, magic_size ) )
_exit( 2 );
if( close( fda[1] ) != 0 )
- { show_close_error( "data feeder" ); _exit( 2 ); }
+ { show_close_error(); _exit( 2 ); }
_exit( 0 );
}
if( pid < 0 ) // parent
@@ -186,12 +181,12 @@ bool set_data_feeder( const std::string & fifoname, const int infd,
const pid_t pid2 = fork();
if( pid2 == 0 ) // child 2 (compressor)
{
- const int outfd = open( fifoname.c_str(), O_WRONLY | o_binary );
+ 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 ) );
+ program_name, fifoname.c_str(), std::strerror( errno ) );
_exit( 2 );
}
if( dup2( fda[0], STDIN_FILENO ) >= 0 &&
@@ -225,18 +220,18 @@ bool set_data_feeder( const std::string & fifoname, const int infd,
const pid_t pid = fork();
if( pid == 0 ) // child (feeder)
{
- const int outfd = open( fifoname.c_str(), O_WRONLY | o_binary );
+ 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 ) );
+ program_name, fifoname.c_str(), std::strerror( errno ) );
_exit( 2 );
}
if( !feed_data( infd, outfd, magic_data, magic_size ) )
_exit( 2 );
if( close( outfd ) != 0 )
- { show_close_error( "data feeder" ); _exit( 2 ); }
+ { show_close_error(); _exit( 2 ); }
_exit( 0 );
}
if( pid < 0 ) // parent
@@ -272,7 +267,7 @@ int main( const int argc, const char * const argv[] )
std::vector< const char * > diff_args; // args to diff, maybe empty
int format_types[2] = { -1, -1 };
invocation_name = argv[0];
- util_name = "zdiff";
+ program_name = "zdiff";
const Arg_parser::Option options[] =
{
diff --git a/zegrep.in b/zegrep.in
index e01f655..0cac12e 100644
--- a/zegrep.in
+++ b/zegrep.in
@@ -1,3 +1,3 @@
#! /bin/sh
bindir=`echo "$0" | sed -e 's,[^/]*$,,'`
-exec "${bindir}"zutils --zgrep -E "$@"
+exec "${bindir}"zgrep -E "$@"
diff --git a/zfgrep.in b/zfgrep.in
index a3d54f3..c1a96d8 100644
--- a/zfgrep.in
+++ b/zfgrep.in
@@ -1,3 +1,3 @@
#! /bin/sh
bindir=`echo "$0" | sed -e 's,[^/]*$,,'`
-exec "${bindir}"zutils --zgrep -F "$@"
+exec "${bindir}"zgrep -F "$@"
diff --git a/zgrep.cc b/zgrep.cc
index 0c78504..47f5983 100644
--- a/zgrep.cc
+++ b/zgrep.cc
@@ -15,7 +15,40 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-void show_zgrep_help()
+#define _FILE_OFFSET_BITS 64
+
+#include <cerrno>
+#include <climits>
+#include <csignal>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <list>
+#include <string>
+#include <vector>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#if defined(__MSVCRT__) || defined(__OS2__)
+#include <io.h>
+#endif
+
+#include "arg_parser.h"
+#include "rc.h"
+#include "zutils.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+
+namespace {
+
+#include "recursive.cc"
+
+void show_help()
{
std::printf( "Zgrep is a front end to the grep program that allows transparent search\n"
"on any combination of compressed and uncompressed files. If any given\n"
@@ -68,6 +101,42 @@ void show_zgrep_help()
}
+int simple_extension_index( const std::string & name )
+ {
+ for( int i = 0; i < num_formats; ++i )
+ {
+ const std::string ext( simple_extensions[i] );
+ if( name.size() > ext.size() &&
+ name.compare( name.size() - ext.size(), ext.size(), ext ) == 0 )
+ return i;
+ }
+ return -1;
+ }
+
+
+int open_instream( std::string & input_filename,
+ const bool no_messages, const bool search )
+ {
+ int infd = open( input_filename.c_str(), O_RDONLY | O_BINARY );
+ if( infd < 0 )
+ {
+ if( search && simple_extension_index( input_filename ) < 0 )
+ {
+ for( int i = 0; i < num_formats; ++i )
+ {
+ const std::string name( input_filename +
+ simple_extensions[format_order[i]] );
+ infd = open( name.c_str(), O_RDONLY | O_BINARY );
+ if( infd >= 0 ) { input_filename = name; break; }
+ }
+ }
+ if( infd < 0 && !no_messages )
+ show_error2( "Can't open input file", input_filename.c_str() );
+ }
+ return infd;
+ }
+
+
int zgrep_stdin( int infd, const int format_index,
const std::vector< const char * > & grep_args )
{
@@ -96,7 +165,7 @@ int zgrep_stdin( int infd, const int format_index,
if( !good_status( children, retval == 1 ) ) retval = 2;
if( close( infd ) != 0 )
- { show_close_error( "data feeder" ); return 2; }
+ { show_close_error(); return 2; }
return retval;
}
@@ -104,7 +173,7 @@ int zgrep_stdin( int infd, const int format_index,
int zgrep_file( int infd, const int format_index,
const std::string & input_filename,
const std::vector< const char * > & grep_args,
- const int grep_list_mode, const bool grep_show_name )
+ const int list_mode, const bool show_name )
{
Children children;
if( !set_data_feeder( &infd, children, format_index ) ) return 2;
@@ -140,9 +209,9 @@ int zgrep_file( int infd, const int format_index,
const int size = readblock( fda[0], buffer, buffer_size );
if( size != buffer_size && errno )
{ show_error( "Read error", errno ); return 2; }
- if( size > 0 && !grep_list_mode )
+ if( size > 0 && !list_mode )
{
- if( grep_show_name )
+ if( show_name )
for( int i = 0; i < size; ++i )
{
if( line_begin )
@@ -160,11 +229,175 @@ int zgrep_file( int infd, const int format_index,
if( !good_status( children, retval == 1 ) ) retval = 2;
- if( grep_list_mode && (retval == 0) == (grep_list_mode == 1) )
+ if( list_mode && (retval == 0) == (list_mode == 1) )
std::printf( "%s\n", input_filename.c_str() );
if( close( infd ) != 0 )
- { show_close_error( "data feeder" ); return 2; }
+ { show_close_error(); return 2; }
if( close( fda[0] ) != 0 )
{ show_close_error( GREP ); return 2; }
return retval;
}
+
+} // end namespace
+
+
+int main( const int argc, const char * const argv[] )
+ {
+ enum { format_opt = 256, help_opt, verbose_opt,
+ bz2_opt, gz_opt, lz_opt, xz_opt };
+ int format_index = -1;
+ int infd = -1;
+ int list_mode = 0; // 1 = list matches, -1 = list non matches
+ int show_name = -1; // tri-state bool
+ bool error = false;
+ bool no_messages = false;
+ bool recursive = false;
+ std::string input_filename;
+ std::list< std::string > filenames;
+ std::vector< const char * > grep_args; // args to grep, maybe empty
+ invocation_name = argv[0];
+ program_name = "zgrep";
+
+ const Arg_parser::Option options[] =
+ {
+ { 'a', "text", Arg_parser::no }, // grep GNU
+ { 'A', "after-context", Arg_parser::yes }, // grep GNU
+ { 'b', "byte-offset", Arg_parser::no }, // grep GNU
+ { 'B', "before-context", Arg_parser::yes }, // grep GNU
+ { 'c', "count", Arg_parser::no }, // grep
+ { 'C', "context", Arg_parser::yes }, // grep GNU
+ { 'e', "regexp", Arg_parser::yes }, // grep
+ { 'E', "extended-regexp", Arg_parser::no }, // grep
+ { 'f', "file ", Arg_parser::yes }, // grep
+ { 'F', "fixed-strings", Arg_parser::no }, // grep
+ { 'h', "no-filename", Arg_parser::no }, // grep GNU
+ { 'H', "with-filename", Arg_parser::no }, // grep GNU
+ { 'i', "ignore-case", Arg_parser::no }, // grep
+ { 'I', 0, Arg_parser::no }, // grep GNU
+ { 'l', "files-with-matches", Arg_parser::no }, // grep
+ { 'L', "files-without-match", Arg_parser::no }, // grep GNU
+ { 'm', "max-count", Arg_parser::yes }, // grep GNU
+ { 'n', "line-number", Arg_parser::no }, // grep
+ { 'N', "no-rcfile", Arg_parser::no },
+ { 'o', "only-matching", Arg_parser::no }, // grep
+ { 'q', "quiet", Arg_parser::no },
+ { 'r', "recursive", Arg_parser::no },
+ { 's', "no-messages", Arg_parser::no }, // grep
+ { 'v', "invert-match", Arg_parser::no }, // grep
+ { 'V', "version", Arg_parser::no },
+ { 'w', "word-regexp", Arg_parser::no }, // grep GNU
+ { 'x', "line-regexp", Arg_parser::no }, // grep
+ { format_opt, "format", Arg_parser::yes },
+ { help_opt, "help", Arg_parser::no },
+ { verbose_opt, "verbose", Arg_parser::no },
+ { bz2_opt, "bz2", Arg_parser::yes },
+ { gz_opt, "gz", Arg_parser::yes },
+ { lz_opt, "lz", Arg_parser::yes },
+ { xz_opt, "xz", Arg_parser::yes },
+ { 0 , 0, Arg_parser::no } };
+
+ const Arg_parser parser( argc, argv, options );
+ if( parser.error().size() ) // bad option
+ { show_error( parser.error().c_str(), 0, true ); return 2; }
+
+ maybe_process_config_file( parser );
+
+ int argind = 0;
+ bool pattern_found = false;
+ for( ; argind < parser.arguments(); ++argind )
+ {
+ const int code = parser.code( argind );
+ const char * const arg = parser.argument( argind ).c_str();
+ if( !code )
+ {
+ if( !pattern_found )
+ { grep_args.push_back( arg ); pattern_found = true; continue; }
+ else break; // no more options
+ }
+ switch( code )
+ {
+ case 'a': grep_args.push_back( "-a" ); break;
+ case 'A': grep_args.push_back( "-A" ); grep_args.push_back( arg ); break;
+ case 'b': grep_args.push_back( "-b" ); break;
+ case 'B': grep_args.push_back( "-B" ); grep_args.push_back( arg ); break;
+ case 'c': grep_args.push_back( "-c" ); break;
+ case 'C': grep_args.push_back( "-C" ); grep_args.push_back( arg ); break;
+ case 'e': grep_args.push_back( "-e" ); grep_args.push_back( arg );
+ pattern_found = true; break;
+ case 'E': grep_args.push_back( "-E" ); break;
+ case 'f': grep_args.push_back( "-f" ); grep_args.push_back( arg );
+ pattern_found = true; break;
+ case 'F': grep_args.push_back( "-F" ); break;
+ case 'h': show_name = false; break;
+ case 'H': show_name = true; break;
+ case 'i': grep_args.push_back( "-i" ); break;
+ case 'I': grep_args.push_back( "-I" ); break;
+ case 'l': grep_args.push_back( "-l" ); list_mode = 1; break;
+ case 'L': grep_args.push_back( "-L" ); list_mode = -1; break;
+ case 'm': grep_args.push_back( "-m" ); grep_args.push_back( arg ); break;
+ case 'n': grep_args.push_back( "-n" ); break;
+ case 'N': break;
+ case 'o': grep_args.push_back( "-o" ); break;
+ case 'q': grep_args.push_back( "-q" ); verbosity = -1; break;
+ case 'r': recursive = true; break;
+ case 's': grep_args.push_back( "-s" ); no_messages = true; break;
+ case 'v': grep_args.push_back( "-v" ); break;
+ case 'V': show_version( "Zgrep" ); return 0;
+ case 'w': grep_args.push_back( "-w" ); break;
+ case 'x': grep_args.push_back( "-x" ); break;
+ case format_opt : format_index = parse_format_type( arg ); break;
+ case help_opt : show_help(); return 0;
+ case verbose_opt: if( verbosity < 4 ) ++verbosity;
+ no_messages = false; break;
+ case bz2_opt: parse_compressor( arg, fmt_bz2 ); break;
+ case gz_opt: parse_compressor( arg, fmt_gz ); break;
+ case lz_opt: parse_compressor( arg, fmt_lz ); break;
+ case xz_opt: parse_compressor( arg, fmt_xz ); break;
+ default : internal_error( "uncaught option" );
+ }
+ } // end process options
+
+#if defined(__MSVCRT__) || defined(__OS2__)
+ setmode( STDIN_FILENO, O_BINARY );
+ setmode( STDOUT_FILENO, O_BINARY );
+#endif
+
+ if( !pattern_found ) { show_error( "Pattern not found." ); return 2; }
+
+ for( ; argind < parser.arguments(); ++argind )
+ filenames.push_back( parser.argument( argind ) );
+
+ if( filenames.empty() ) filenames.push_back("-");
+
+ if( show_name < 0 ) show_name = ( filenames.size() != 1 || recursive );
+
+ int retval = 1;
+ while( next_filename( filenames, input_filename, error, recursive,
+ false, no_messages ) )
+ {
+ if( input_filename.empty() ) infd = STDIN_FILENO;
+ else
+ {
+ infd = open_instream( input_filename, no_messages, format_index < 0 );
+ if( infd < 0 ) { error = true; continue; }
+ }
+
+ int tmp;
+ if( infd == STDIN_FILENO )
+ tmp = zgrep_stdin( infd, format_index, grep_args );
+ else tmp = zgrep_file( infd, format_index, input_filename, grep_args,
+ list_mode, show_name );
+ if( tmp == 0 || ( tmp == 2 && retval == 1 ) ) retval = tmp;
+
+ if( input_filename.size() ) { close( infd ); infd = -1; }
+ if( retval == 0 && verbosity < 0 ) break;
+ }
+
+ if( std::fclose( stdout ) != 0 )
+ {
+ show_error( "Can't close stdout", errno );
+ error = true;
+ }
+ if( error && ( retval != 0 || verbosity >= 0 ) ) retval = 2;
+ return retval;
+ }
diff --git a/zgrep.in b/zgrep.in
deleted file mode 100644
index bfbc490..0000000
--- a/zgrep.in
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /bin/sh
-bindir=`echo "$0" | sed -e 's,[^/]*$,,'`
-exec "${bindir}"zutils --zgrep "$@"
diff --git a/ztest.cc b/ztest.cc
index 79f32dd..c0147a6 100644
--- a/ztest.cc
+++ b/ztest.cc
@@ -15,7 +15,40 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-void show_ztest_help()
+#define _FILE_OFFSET_BITS 64
+
+#include <cerrno>
+#include <climits>
+#include <csignal>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <list>
+#include <string>
+#include <vector>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#if defined(__MSVCRT__) || defined(__OS2__)
+#include <io.h>
+#endif
+
+#include "arg_parser.h"
+#include "rc.h"
+#include "zutils.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+
+namespace {
+
+#include "recursive.cc"
+
+void show_help()
{
std::printf( "Ztest verifies the integrity of the specified compressed files.\n"
"Uncompressed files are ignored. If no files are specified, the integrity\n"
@@ -44,6 +77,15 @@ void show_ztest_help()
}
+int open_instream( std::string & input_filename )
+ {
+ int infd = open( input_filename.c_str(), O_RDONLY | O_BINARY );
+ if( infd < 0 )
+ show_error2( "Can't open input file", input_filename.c_str() );
+ return infd;
+ }
+
+
int ztest_stdin( const int infd, int format_index,
const std::vector< const char * > & ztest_args )
{
@@ -65,7 +107,7 @@ int ztest_stdin( const int infd, int format_index,
!feed_data( infd, fda[1], magic_data, magic_size ) )
_exit( 1 );
if( close( fda[1] ) != 0 )
- { show_close_error( "data feeder" ); _exit( 1 ); }
+ { show_close_error(); _exit( 1 ); }
_exit( 0 );
}
if( pid < 0 ) // parent
@@ -145,3 +187,102 @@ int ztest_file( const int infd, int format_index,
const bool isgzxz = ( format_index == fmt_gz || format_index == fmt_xz );
return wait_for_child( pid, compressor_name, 1, isgzxz );
}
+
+} // end namespace
+
+
+int main( const int argc, const char * const argv[] )
+ {
+ enum { format_opt = 256, bz2_opt, gz_opt, lz_opt, xz_opt };
+ int infd = -1;
+ int format_index = -1;
+ bool recursive = false;
+ std::string input_filename;
+ std::list< std::string > filenames;
+ std::vector< const char * > ztest_args; // args to ztest, maybe empty
+ invocation_name = argv[0];
+ program_name = "ztest";
+
+ const Arg_parser::Option options[] =
+ {
+ { 'h', "help", Arg_parser::no },
+ { 'N', "no-rcfile", Arg_parser::no },
+ { 'q', "quiet", Arg_parser::no },
+ { 'r', "recursive", Arg_parser::no },
+ { 'v', "verbose", Arg_parser::no },
+ { 'V', "version", Arg_parser::no },
+ { format_opt, "format", Arg_parser::yes },
+ { bz2_opt, "bz2", Arg_parser::yes },
+ { gz_opt, "gz", Arg_parser::yes },
+ { lz_opt, "lz", Arg_parser::yes },
+ { xz_opt, "xz", Arg_parser::yes },
+ { 0 , 0, Arg_parser::no } };
+
+ const Arg_parser parser( argc, argv, options );
+ if( parser.error().size() ) // bad option
+ { show_error( parser.error().c_str(), 0, true ); return 1; }
+
+ maybe_process_config_file( parser );
+
+ int argind = 0;
+ for( ; argind < parser.arguments(); ++argind )
+ {
+ const int code = parser.code( argind );
+ if( !code ) break; // no more options
+ const char * const arg = parser.argument( argind ).c_str();
+ switch( code )
+ {
+ case 'h': show_help(); return 0;
+ case 'N': break;
+ case 'q': verbosity = -1; ztest_args.push_back( "-q" ); break;
+ case 'r': recursive = true; break;
+ case 'v': if( verbosity < 4 ) ++verbosity;
+ ztest_args.push_back( "-v" ); break;
+ case 'V': show_version( "Ztest" ); return 0;
+ case format_opt: format_index = parse_format_type( arg ); break;
+ case bz2_opt: parse_compressor( arg, fmt_bz2, 1 ); break;
+ case gz_opt: parse_compressor( arg, fmt_gz, 1 ); break;
+ case lz_opt: parse_compressor( arg, fmt_lz, 1 ); break;
+ case xz_opt: parse_compressor( arg, fmt_xz, 1 ); break;
+ default : internal_error( "uncaught option" );
+ }
+ } // end process options
+
+#if defined(__MSVCRT__) || defined(__OS2__)
+ setmode( STDIN_FILENO, O_BINARY );
+ setmode( STDOUT_FILENO, O_BINARY );
+#endif
+
+ for( ; argind < parser.arguments(); ++argind )
+ filenames.push_back( parser.argument( argind ) );
+
+ if( filenames.empty() ) filenames.push_back("-");
+
+ int retval = 0;
+ bool error = false;
+ while( next_filename( filenames, input_filename, error, recursive ) )
+ {
+ if( input_filename.empty() ) infd = STDIN_FILENO;
+ else
+ {
+ infd = open_instream( input_filename );
+ if( infd < 0 ) { error = true; continue; }
+ }
+
+ int tmp;
+ if( infd == STDIN_FILENO )
+ tmp = ztest_stdin( infd, format_index, ztest_args );
+ else tmp = ztest_file( infd, format_index, input_filename, ztest_args );
+ if( tmp > retval ) retval = tmp;
+
+ if( input_filename.size() ) { close( infd ); infd = -1; }
+ }
+
+ if( std::fclose( stdout ) != 0 )
+ {
+ show_error( "Can't close stdout", errno );
+ error = true;
+ }
+ if( error && retval == 0 ) retval = 1;
+ return retval;
+ }
diff --git a/ztest.in b/ztest.in
deleted file mode 100644
index cc7dc9b..0000000
--- a/ztest.in
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /bin/sh
-bindir=`echo "$0" | sed -e 's,[^/]*$,,'`
-exec "${bindir}"zutils --ztest "$@"
diff --git a/zupdate.cc b/zupdate.cc
index 02dfe29..2b88c79 100644
--- a/zupdate.cc
+++ b/zupdate.cc
@@ -37,24 +37,18 @@
#endif
#include "arg_parser.h"
-#include "zutils.h"
#include "rc.h"
-#if CHAR_BIT != 8
-#error "Environments where CHAR_BIT != 8 are not supported."
+#ifndef O_BINARY
+#define O_BINARY 0
#endif
namespace {
-#ifdef O_BINARY
-const int o_binary = O_BINARY;
-#else
-const int o_binary = 0;
-#endif
-
+#include "recursive.cc"
-void show_zupdate_help()
+void show_help()
{
std::printf( "Zupdate recompresses files from bzip2, gzip, and xz formats to lzip format.\n"
"The originals are compared with the new files and then deleted.\n"
@@ -77,6 +71,7 @@ void show_zupdate_help()
" -h, --help display this help and exit\n"
" -V, --version output version information and exit\n"
" -f, --force do not skip a file even if the .lz exists\n"
+ " -k, --keep keep (don't delete) input files\n"
" -l, --lzip-verbose pass a -v option to the lzip compressor\n"
" -N, --no-rcfile don't read runtime configuration file\n"
" -q, --quiet suppress all messages\n"
@@ -97,10 +92,10 @@ int cant_execute( const std::string & command, const int status )
{
if( WIFEXITED( status ) )
std::fprintf( stderr, "%s: Error executing '%s'. Exit status = %d.\n",
- util_name, command.c_str(), WEXITSTATUS( status ) );
+ program_name, command.c_str(), WEXITSTATUS( status ) );
else
std::fprintf( stderr, "%s: Can't execute '%s'.\n",
- util_name, command.c_str() );
+ program_name, command.c_str() );
}
return 1;
}
@@ -136,11 +131,13 @@ struct { const char * from; const char * to; int format_index; } const
{ ".txz", ".tar", fmt_xz },
{ 0, 0, -1 } };
+int disable_xz = -1; // tri-state bool
+
// Returns 0 for success, -1 for file skipped, 1 for error.
int zupdate_file( const std::string & name, const char * const lzip_name,
const std::vector< std::string > & lzip_args2,
- const bool force )
+ const bool force, const bool keep_input_files )
{
int format_index = -1;
std::string dname; // decompressed_name
@@ -158,7 +155,7 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
{
if( verbosity >= 2 )
std::fprintf( stderr, "%s: Input file '%s' already has '%s' suffix.\n",
- util_name, name.c_str(), known_extensions[i].from );
+ program_name, name.c_str(), known_extensions[i].from );
return 0; // ignore this file
}
break;
@@ -169,7 +166,7 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
{
if( verbosity >= 2 )
std::fprintf( stderr, "%s: Unknown extension in file name '%s' -- ignored.\n",
- util_name, name.c_str() );
+ program_name, name.c_str() );
return 0; // ignore this file
}
@@ -178,14 +175,14 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Can't stat input file '%s': %s.\n",
- util_name, name.c_str(), std::strerror( errno ) );
+ program_name, name.c_str(), std::strerror( errno ) );
return 1;
}
if( !S_ISREG( in_stats.st_mode ) )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Input file '%s' is not a regular file.\n",
- util_name, name.c_str() );
+ program_name, name.c_str() );
return 1;
}
@@ -196,10 +193,20 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Output file '%s' already exists, skipping.\n",
- util_name, rname.c_str() );
+ program_name, rname.c_str() );
return -1;
}
+ if( format_index == fmt_xz )
+ {
+ if( disable_xz < 0 )
+ {
+ std::string command( compressor_name ); command += " -V > /dev/null";
+ disable_xz = ( std::system( command.c_str() ) != 0 );
+ }
+ if( disable_xz ) return 0; // ignore this file if no xz installed
+ }
+
if( !lz_exists ) // recompress
{
if( verbosity >= 1 )
@@ -280,11 +287,11 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
return cant_execute( zcmp_command, status ); }
}
- if( std::remove( name.c_str() ) != 0 && errno != ENOENT )
+ if( !keep_input_files && std::remove( name.c_str() ) != 0 && errno != ENOENT )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Can't delete input file '%s': %s.\n",
- util_name, name.c_str(), std::strerror( errno ) );
+ program_name, name.c_str(), std::strerror( errno ) );
return 1;
}
return 0;
@@ -300,9 +307,10 @@ int main( const int argc, const char * const argv[] )
std::list< std::string > filenames;
std::vector< std::string > lzip_args2; // args to lzip, maybe empty
bool force = false;
+ bool keep_input_files = false;
bool recursive = false;
invocation_name = argv[0];
- util_name = "zupdate";
+ program_name = "zupdate";
const Arg_parser::Option options[] =
{
@@ -318,6 +326,7 @@ int main( const int argc, const char * const argv[] )
{ '9', 0, Arg_parser::no },
{ 'f', "force", Arg_parser::no },
{ 'h', "help", Arg_parser::no },
+ { 'k', "keep", Arg_parser::no },
{ 'l', "lzip-verbose", Arg_parser::no },
{ 'N', "no-rcfile", Arg_parser::no },
{ 'q', "quiet", Arg_parser::no },
@@ -342,23 +351,24 @@ int main( const int argc, const char * const argv[] )
const int code = parser.code( argind );
if( !code ) break; // no more options
const char * const arg = parser.argument( argind ).c_str();
- switch( code ) // common options
+ switch( code )
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
lzip_args2.push_back( "-" ); lzip_args2.back() += code; break;
case 'f': force = true; break;
- case 'h': show_zupdate_help(); return 0;
+ case 'h': show_help(); return 0;
+ case 'k': keep_input_files = true; break;
case 'l': lzip_args2.push_back( "-v" ); break;
- case 'N': continue;
+ case 'N': break;
case 'q': verbosity = -1; lzip_args2.push_back( "-q" ); break;
case 'r': recursive = true; break;
case 'v': if( verbosity < 4 ) ++verbosity; break;
case 'V': show_version( "Zupdate" ); return 0;
- case bz2_opt: parse_compressor( arg, fmt_bz2, 1 ); continue;
- case gz_opt: parse_compressor( arg, fmt_gz, 1 ); continue;
- case lz_opt: parse_compressor( arg, fmt_lz, 1 ); continue;
- case xz_opt: parse_compressor( arg, fmt_xz, 1 ); continue;
+ case bz2_opt: parse_compressor( arg, fmt_bz2, 1 ); break;
+ case gz_opt: parse_compressor( arg, fmt_gz, 1 ); break;
+ case lz_opt: parse_compressor( arg, fmt_lz, 1 ); break;
+ case xz_opt: parse_compressor( arg, fmt_xz, 1 ); break;
default : internal_error( "uncaught option" );
}
} // end process options
@@ -376,40 +386,15 @@ int main( const int argc, const char * const argv[] )
filenames.push_back( parser.argument( argind ) );
int retval = 0;
- while( !filenames.empty() )
+ bool error = false;
+ while( next_filename( filenames, input_filename, error, recursive, true ) )
{
- input_filename = filenames.front();
- filenames.pop_front();
- if( !input_filename.size() || input_filename == "-" ) continue;
- if( recursive )
- {
- struct stat st;
- if( stat( input_filename.c_str(), &st ) == 0 && S_ISDIR( st.st_mode ) )
- {
- DIR * const dirp = opendir( input_filename.c_str() );
- if( !dirp )
- {
- show_error2( "Can't open directory", input_filename.c_str() );
- if( retval < 1 ) retval = 1; continue;
- }
- std::list< std::string > tmp_list;
- while( true )
- {
- const struct dirent * const entryp = readdir( dirp );
- if( !entryp ) { closedir( dirp ); break; }
- std::string tmp_name( entryp->d_name );
- if( tmp_name != "." && tmp_name != ".." )
- tmp_list.push_back( input_filename + "/" + tmp_name );
- }
- filenames.splice( filenames.begin(), tmp_list );
- continue;
- }
- }
-
- int tmp = zupdate_file( input_filename, lzip_name, lzip_args2, force );
- if( tmp < 0 && retval < 1 ) retval = 1;
+ int tmp = zupdate_file( input_filename, lzip_name, lzip_args2, force,
+ keep_input_files );
+ if( tmp < 0 ) error = true;
if( tmp > retval ) retval = tmp;
if( tmp > 0 ) break;
}
+ if( error && retval == 0 ) retval = 1;
return retval;
}
diff --git a/zutils.cc b/zutils.cc
index 8961376..afce7fc 100644
--- a/zutils.cc
+++ b/zutils.cc
@@ -27,14 +27,51 @@
#include <unistd.h>
#include <sys/wait.h>
-#include "zutils.h"
#include "rc.h"
+#include "zutils.h"
-const char * invocation_name = 0;
-const char * util_name = program_name;
+namespace {
-int verbosity = 0;
+// first magic byte must be different among formats
+enum { bzip2_magic_size = 3,
+ gzip_magic_size = 2,
+ lzip_magic_size = 4,
+ xz_magic_size = 5 };
+const uint8_t bzip2_magic[bzip2_magic_size] =
+ { 0x42, 0x5A, 0x68 }; // "BZh"
+const uint8_t gzip_magic[gzip_magic_size] =
+ { 0x1F, 0x8B };
+const uint8_t lzip_magic[lzip_magic_size] =
+ { 0x4C, 0x5A, 0x49, 0x50 }; // "LZIP"
+const uint8_t xz_magic[xz_magic_size] =
+ { 0xFD, 0x37, 0x7A, 0x58, 0x5A }; // 0xFD, "7zXZ"
+
+
+// Returns -1 if child not terminated, 2 in case of error, or
+// exit status of child process 'pid'.
+//
+int child_status( const pid_t pid, const char * const name )
+ {
+ int status;
+ while( true )
+ {
+ const int tmp = waitpid( pid, &status, WNOHANG );
+ if( tmp == -1 && errno != EINTR )
+ {
+ if( verbosity >= 0 )
+ std::fprintf( stderr, "%s: Error checking status of '%s': %s.\n",
+ program_name, name, std::strerror( errno ) );
+ _exit( 2 );
+ }
+ if( tmp == 0 ) return -1; // child not terminated
+ if( tmp == pid ) break; // child terminated
+ }
+ if( WIFEXITED( status ) ) return WEXITSTATUS( status );
+ return 2;
+ }
+
+} // end namespace
int parse_format_type( const std::string & arg )
@@ -152,7 +189,7 @@ bool set_data_feeder( int * const infdp, Children & children, int format_index )
!feed_data( old_infd, fda[1], magic_data, magic_size ) )
_exit( 2 );
if( close( fda[1] ) != 0 )
- { show_close_error( "data feeder" ); _exit( 2 ); }
+ { show_close_error(); _exit( 2 ); }
_exit( 0 );
}
if( pid < 0 ) // parent
@@ -201,7 +238,7 @@ bool set_data_feeder( int * const infdp, Children & children, int format_index )
!feed_data( old_infd, fda[1], magic_data, magic_size ) )
_exit( 2 );
if( close( fda[1] ) != 0 )
- { show_close_error( "data feeder" ); _exit( 2 ); }
+ { show_close_error(); _exit( 2 ); }
_exit( 0 );
}
if( pid < 0 ) // parent
@@ -214,84 +251,6 @@ bool set_data_feeder( int * const infdp, Children & children, int format_index )
}
-void show_help_addr()
- {
- std::printf( "\nReport bugs to zutils-bug@nongnu.org\n"
- "Zutils home page: http://www.nongnu.org/zutils/zutils.html\n" );
- }
-
-
-void show_version( const char * const Util_name )
- {
- if( !Util_name || !Util_name[0] )
- std::printf( "%s %s\n", Program_name, PROGVERSION );
- else
- std::printf( "%s (%s) %s\n", Util_name, program_name, PROGVERSION );
- std::printf( "Copyright (C) %s Antonio Diaz Diaz.\n", program_year );
- std::printf( "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n"
- "This is free software: you are free to change and redistribute it.\n"
- "There is NO WARRANTY, to the extent permitted by law.\n" );
- }
-
-
-void show_error( const char * const msg, const int errcode, const bool help )
- {
- if( verbosity >= 0 )
- {
- if( msg && msg[0] )
- {
- std::fprintf( stderr, "%s: %s", util_name, msg );
- if( errcode > 0 )
- std::fprintf( stderr, ": %s", std::strerror( errcode ) );
- std::fprintf( stderr, "\n" );
- }
- if( help )
- std::fprintf( stderr, "Try '%s --help' for more information.\n",
- invocation_name );
- }
- }
-
-
-void show_error2( const char * const msg, const char * const name )
- {
- if( verbosity >= 0 )
- std::fprintf( stderr, "%s: %s '%s': %s.\n",
- util_name, msg, name, std::strerror( errno ) );
- }
-
-
-void show_close_error( const char * const prog_name )
- {
- if( verbosity >= 0 )
- std::fprintf( stderr, "%s: Can't close output of %s: %s.\n",
- util_name, prog_name, std::strerror( errno ) );
- }
-
-
-void show_exec_error( const char * const prog_name )
- {
- if( verbosity >= 0 )
- std::fprintf( stderr, "%s: Can't exec '%s': %s.\n",
- util_name, prog_name, std::strerror( errno ) );
- }
-
-
-void show_fork_error( const char * const prog_name )
- {
- if( verbosity >= 0 )
- std::fprintf( stderr, "%s: Can't fork '%s': %s.\n",
- util_name, prog_name, std::strerror( errno ) );
- }
-
-
-void internal_error( const char * const msg )
- {
- if( verbosity >= 0 )
- std::fprintf( stderr, "%s: internal error: %s.\n", util_name, msg );
- std::exit( 3 );
- }
-
-
int test_format( const int infd, const uint8_t ** const magic_datap,
int * const magic_sizep )
{
@@ -335,51 +294,3 @@ int test_format( const int infd, const uint8_t ** const magic_datap,
*magic_datap = buf; *magic_sizep = i;
return -1;
}
-
-
-int wait_for_child( const pid_t pid, const char * const name,
- const int eretval, const bool isgzxz )
- {
- int status;
- while( waitpid( pid, &status, 0 ) == -1 )
- {
- if( errno != EINTR )
- {
- if( verbosity >= 0 )
- std::fprintf( stderr, "%s: Error waiting termination of '%s': %s.\n",
- util_name, name, std::strerror( errno ) );
- _exit( eretval );
- }
- }
-
- if( WIFEXITED( status ) )
- {
- const int tmp = WEXITSTATUS( status );
- if( isgzxz && eretval == 1 && tmp == 1 ) return 2; // for ztest
- return tmp;
- }
- return eretval;
- }
-
-
-int child_status( const pid_t pid, const char * const name,
- const int eretval )
- {
- int status;
- while( true )
- {
- const int tmp = waitpid( pid, &status, WNOHANG );
- if( tmp == -1 && errno != EINTR )
- {
- if( verbosity >= 0 )
- std::fprintf( stderr, "%s: Error checking status of '%s': %s.\n",
- util_name, name, std::strerror( errno ) );
- _exit( eretval );
- }
- if( tmp == 0 ) return -1; // child not terminated
- if( tmp == pid ) break; // child terminated
- }
-
- if( WIFEXITED( status ) ) return WEXITSTATUS( status );
- return eretval;
- }
diff --git a/zutils.h b/zutils.h
index 5ff302c..511ee04 100644
--- a/zutils.h
+++ b/zutils.h
@@ -15,37 +15,6 @@
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 config_file_name = "zutilsrc";
-const char * const program_year = "2013";
-extern const char * invocation_name;
-extern const char * util_name;
-
-extern int verbosity;
-
-enum { fmt_bz2, fmt_gz, fmt_lz, fmt_xz, num_formats };
-const char * const format_names[num_formats] = { "bz2", "gz", "lz", "xz" };
-const char * const simple_extensions[num_formats] =
- { ".bz2", ".gz", ".lz", ".xz" };
-const int8_t format_order[num_formats] =
- { fmt_lz, fmt_bz2, fmt_gz, fmt_xz }; // search order
-
-// first magic byte must be different among formats
-enum { bzip2_magic_size = 3,
- gzip_magic_size = 2,
- lzip_magic_size = 4,
- xz_magic_size = 5 };
-const uint8_t bzip2_magic[bzip2_magic_size] =
- { 0x42, 0x5A, 0x68 }; // "BZh"
-const uint8_t gzip_magic[gzip_magic_size] =
- { 0x1F, 0x8B };
-const uint8_t lzip_magic[lzip_magic_size] =
- { 0x4C, 0x5A, 0x49, 0x50 }; // "LZIP"
-const uint8_t xz_magic[xz_magic_size] =
- { 0xFD, 0x37, 0x7A, 0x58, 0x5A }; // 0xFD, "7zXZ"
-
-
int parse_format_type( const std::string & arg );
int readblock( const int fd, uint8_t * const buf, const int size );
@@ -61,28 +30,7 @@ struct Children
bool good_status( const Children & children, const bool finished );
bool set_data_feeder( int * const infdp, Children & children, int format_index );
-void show_help_addr();
-void show_version( const char * const Util_name = 0 );
-void show_error( const char * const msg, const int errcode = 0,
- const bool help = false );
-void show_error2( const char * const msg, const char * const name );
-void show_close_error( const char * const prog_name );
-void show_exec_error( const char * const prog_name );
-void show_fork_error( const char * const prog_name );
-void internal_error( const char * const msg );
-
// Returns format index or -1 if uncompressed
//
int test_format( const int infd, const uint8_t ** const magic_datap,
int * const magic_sizep );
-
-// Returns exit status of child process 'pid', or 'eretval' in case of error.
-//
-int wait_for_child( const pid_t pid, const char * const name,
- const int eretval = 2, const bool isgzxz = false );
-
-// Returns -1 if child not terminated, 'eretval' in case of error, or
-// exit status of child process 'pid'.
-//
-int child_status( const pid_t pid, const char * const name,
- const int eretval = 2 );