summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <mail@daniel-baumann.ch>2015-11-08 04:29:40 +0000
committerDaniel Baumann <mail@daniel-baumann.ch>2015-11-08 04:29:40 +0000
commit57d1e4420d7f7981a5e55ad5f66cb1534af30851 (patch)
tree7de98d0106a3940da1e3c5eb773d5a1de186a905
parentAdding upstream version 1.1. (diff)
downloadzutils-57d1e4420d7f7981a5e55ad5f66cb1534af30851.tar.xz
zutils-57d1e4420d7f7981a5e55ad5f66cb1534af30851.zip
Adding upstream version 1.2~pre2.upstream/1.2_pre2
Signed-off-by: Daniel Baumann <mail@daniel-baumann.ch>
-rw-r--r--ChangeLog5
-rw-r--r--INSTALL3
-rw-r--r--Makefile.in19
-rw-r--r--NEWS14
-rw-r--r--arg_parser.cc4
-rwxr-xr-xconfigure2
-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.176
-rw-r--r--doc/zutils.info106
-rw-r--r--doc/zutils.texinfo88
-rw-r--r--main.cc4
-rwxr-xr-xtestsuite/check.sh85
-rw-r--r--zcmp.cc6
-rw-r--r--zdiff.cc10
-rw-r--r--ztest.cc38
-rw-r--r--zupdate.cc415
-rw-r--r--zutils.cc4
-rw-r--r--zutils.h2
22 files changed, 804 insertions, 87 deletions
diff --git a/ChangeLog b/ChangeLog
index 52ee031..e7f2c1a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2013-09-04 Antonio Diaz Diaz <antonio@gnu.org>
+
+ * Version 1.2-pre2 released.
+ * Added new utility; zupdate.
+
2013-08-02 Antonio Diaz Diaz <antonio@gnu.org>
* Version 1.1 released.
diff --git a/INSTALL b/INSTALL
index ddb4949..19febd5 100644
--- a/INSTALL
+++ b/INSTALL
@@ -1,6 +1,7 @@
Requirements
------------
-You will need a C++ compiler.
+You will need a C++ compiler, (de)compressors for bzip2 and gzip
+formats, and a compressor for the lzip format.
I use gcc 4.8.1 and 3.3.6, but the code should compile with any
standards compliant compiler.
Gcc is available at http://gcc.gnu.org.
diff --git a/Makefile.in b/Makefile.in
index 919ee36..68b2e6b 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -10,6 +10,7 @@ SHELL = /bin/sh
objs = arg_parser.o rc.o zutils.o main.o
zcmp_objs = arg_parser.o rc.o zutils.o zcmp.o
zdiff_objs = arg_parser.o rc.o zutils.o zdiff.o
+zupdate_objs = arg_parser.o rc.o zutils.o zupdate.o
scripts = zcat zegrep zfgrep zgrep ztest
@@ -17,7 +18,7 @@ scripts = zcat zegrep zfgrep zgrep ztest
uninstall uninstall-bin uninstall-info uninstall-man \
doc info man check dist clean distclean
-all : $(progname) zcmp zdiff $(scripts)
+all : $(progname) zcmp zdiff zupdate $(scripts)
$(progname) : $(objs)
$(CXX) $(LDFLAGS) -o $@ $(objs)
@@ -51,6 +52,9 @@ ztest : ztest.in
cat $(VPATH)/ztest.in > $@
chmod a+x ztest
+zupdate : $(zupdate_objs)
+ $(CXX) $(LDFLAGS) -o $@ $(zupdate_objs)
+
main.o : main.cc
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -DGREP=\"$(GREP)\" -c -o $@ $<
@@ -73,6 +77,7 @@ 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
@@ -84,7 +89,7 @@ $(VPATH)/doc/$(pkgname).info : $(VPATH)/doc/$(pkgname).texinfo
cd $(VPATH)/doc && makeinfo $(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/zgrep.1 $(VPATH)/doc/ztest.1 $(VPATH)/doc/zupdate.1
$(VPATH)/doc/zcat.1 : $(progname) zcat
help2man -n 'decompress and concatenate files to standard output' \
@@ -106,6 +111,10 @@ $(VPATH)/doc/ztest.1 : $(progname) ztest
help2man -n 'verify integrity of compressed files' \
-o $@ --no-info ./ztest
+$(VPATH)/doc/zupdate.1 : zupdate
+ help2man -n 'recompress bzip2, gzip, xz files to lzip files' \
+ -o $@ --no-info ./zupdate
+
Makefile : $(VPATH)/configure $(VPATH)/Makefile.in
./config.status
@@ -124,6 +133,7 @@ install-bin : all
$(INSTALL_SCRIPT) ./zfgrep "$(DESTDIR)$(bindir)/zfgrep"
$(INSTALL_SCRIPT) ./zgrep "$(DESTDIR)$(bindir)/zgrep"
$(INSTALL_SCRIPT) ./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 ; \
$(INSTALL_DATA) $(VPATH)/$(pkgname)rc "$(DESTDIR)$(sysconfdir)/$(pkgname)rc" ; \
@@ -141,6 +151,7 @@ install-man :
$(INSTALL_DATA) $(VPATH)/doc/zdiff.1 "$(DESTDIR)$(mandir)/man1/zdiff.1"
$(INSTALL_DATA) $(VPATH)/doc/zgrep.1 "$(DESTDIR)$(mandir)/man1/zgrep.1"
$(INSTALL_DATA) $(VPATH)/doc/ztest.1 "$(DESTDIR)$(mandir)/man1/ztest.1"
+ $(INSTALL_DATA) $(VPATH)/doc/zupdate.1 "$(DESTDIR)$(mandir)/man1/zupdate.1"
install-strip : all
$(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install
@@ -156,6 +167,7 @@ uninstall-bin :
-rm -f "$(DESTDIR)$(bindir)/zfgrep"
-rm -f "$(DESTDIR)$(bindir)/zgrep"
-rm -f "$(DESTDIR)$(bindir)/ztest"
+ -rm -f "$(DESTDIR)$(bindir)/zupdate"
-rm -f "$(DESTDIR)$(sysconfdir)/$(pkgname)rc"
uninstall-info :
@@ -168,6 +180,7 @@ uninstall-man :
-rm -f "$(DESTDIR)$(mandir)/man1/zdiff.1"
-rm -f "$(DESTDIR)$(mandir)/man1/zgrep.1"
-rm -f "$(DESTDIR)$(mandir)/man1/ztest.1"
+ -rm -f "$(DESTDIR)$(mandir)/man1/zupdate.1"
dist : doc
ln -sf $(VPATH) $(DISTNAME)
@@ -195,7 +208,7 @@ dist : doc
clean :
-rm -f $(progname) $(progname)_profiled $(objs)
- -rm -f zcmp zcmp.o zdiff zdiff.o $(scripts)
+ -rm -f zcmp zcmp.o zdiff zdiff.o zupdate zupdate.o $(scripts)
distclean : clean
-rm -f Makefile config.status *.tar *.tar.lz
diff --git a/NEWS b/NEWS
index 045e9f9..202144b 100644
--- a/NEWS
+++ b/NEWS
@@ -1,12 +1,4 @@
-Changes in version 1.1:
+Changes in version 1.2:
-The new options "--bz2", "--gz", "--lz" and "--xz" have been added to
-all utilities.
-
-Zutils now provides the runtime configuration file "zutilsrc", which
-allows the user change the compressor to be used for each format.
-
-The checking of the exit status of compressors has been improved.
-
-The use of "decompressed" and "uncompressed" in the documentation has
-been revised.
+The new utility zupdate, which recompresses bzip2, gzip and xz files to
+lzip format, has been added.
diff --git a/arg_parser.cc b/arg_parser.cc
index a28d2ba..5cb98a9 100644
--- a/arg_parser.cc
+++ b/arg_parser.cc
@@ -156,12 +156,12 @@ Arg_parser::Arg_parser( const int argc, const char * const argv[],
while( argind < argc )
{
const unsigned char ch1 = argv[argind][0];
- const unsigned char ch2 = ( ch1 ? argv[argind][1] : 0 );
+ const unsigned char ch2 = ch1 ? argv[argind][1] : 0;
if( ch1 == '-' && ch2 ) // we found an option
{
const char * const opt = argv[argind];
- const char * const arg = (argind + 1 < argc) ? argv[argind+1] : 0;
+ const char * const arg = ( argind + 1 < argc ) ? argv[argind+1] : 0;
if( ch2 == '-' )
{
if( !argv[argind][2] ) { ++argind; break; } // we found "--"
diff --git a/configure b/configure
index 4976e51..f93d9fd 100755
--- a/configure
+++ b/configure
@@ -6,7 +6,7 @@
# to copy, distribute and modify it.
pkgname=zutils
-pkgversion=1.1
+pkgversion=1.2-pre2
progname=zutils
srctrigger=doc/${pkgname}.texinfo
diff --git a/doc/zcat.1 b/doc/zcat.1
index fce9e04..7b0af25 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" "August 2013" "Zcat (zutils) 1.1" "User Commands"
+.TH ZCAT "1" "September 2013" "Zcat (zutils) 1.2-pre2" "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 ccb6692..dae6a3b 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" "August 2013" "Zcmp (zutils) 1.1" "User Commands"
+.TH ZCMP "1" "September 2013" "Zcmp (zutils) 1.2-pre2" "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 4cdcdb3..7ef4be9 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" "August 2013" "Zdiff (zutils) 1.1" "User Commands"
+.TH ZDIFF "1" "September 2013" "Zdiff (zutils) 1.2-pre2" "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 275221f..7293471 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" "August 2013" "Zgrep (zutils) 1.1" "User Commands"
+.TH ZGREP "1" "September 2013" "Zgrep (zutils) 1.2-pre2" "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 02b15e2..e0de3f2 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" "August 2013" "Ztest (zutils) 1.1" "User Commands"
+.TH ZTEST "1" "September 2013" "Ztest (zutils) 1.2-pre2" "User Commands"
.SH NAME
Ztest \- verify integrity of compressed files
.SH SYNOPSIS
diff --git a/doc/zupdate.1 b/doc/zupdate.1
new file mode 100644
index 0000000..947e73a
--- /dev/null
+++ b/doc/zupdate.1
@@ -0,0 +1,76 @@
+.\" 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"
+.SH NAME
+Zupdate \- recompress bzip2, gzip, xz files to lzip files
+.SH SYNOPSIS
+.B zupdate
+[\fIoptions\fR] [\fIfiles\fR]
+.SH DESCRIPTION
+Zupdate recompresses files from bzip2, gzip, and xz formats to lzip format.
+The originals are compared with the new files and then deleted.
+Only regular files with standard file name extensions are recompressed,
+other files are ignored.
+Compressed files are decompressed and then recompressed on the fly; no
+temporary files are created.
+The lzip format is chosen as destination because it is by far the most
+appropriate for long\-term data archiving.
+.PP
+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
+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.
+.PP
+Exit status is 0 if all the compressed files were successfully
+recompressed (if needed), compared and deleted. Non\-zero otherwise.
+.SH OPTIONS
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+display this help and exit
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+output version information and exit
+.TP
+\fB\-f\fR, \fB\-\-force\fR
+do not skip a file even if the .lz exists
+.TP
+\fB\-l\fR, \fB\-\-lzip\-verbose\fR
+pass a \fB\-v\fR option to the lzip compressor
+.TP
+\fB\-N\fR, \fB\-\-no\-rcfile\fR
+don't read runtime configuration file
+.TP
+\fB\-q\fR, \fB\-\-quiet\fR
+suppress all messages
+.TP
+\fB\-r\fR, \fB\-\-recursive\fR
+operate recursively on directories
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+be verbose (a 2nd \fB\-v\fR gives more)
+.TP
+\fB\-0\fR .. \fB\-9\fR
+set compression level [default 9]
+.TP
+\fB\-\-bz2=\fR<command>
+set compressor and options for bzip2 format
+.TP
+\fB\-\-gz=\fR<command>
+set compressor and options for gzip format
+.TP
+\fB\-\-lz=\fR<command>
+set compressor and options for lzip format
+.TP
+\fB\-\-xz=\fR<command>
+set compressor and options for xz format
+.SH "REPORTING BUGS"
+Report bugs to zutils\-bug@nongnu.org
+.br
+Zutils home page: http://www.nongnu.org/zutils/zutils.html
+.SH COPYRIGHT
+Copyright \(co 2013 Antonio Diaz Diaz.
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
+.br
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
diff --git a/doc/zutils.info b/doc/zutils.info
index 4cba8af..b8a77da 100644
--- a/doc/zutils.info
+++ b/doc/zutils.info
@@ -12,7 +12,7 @@ File: zutils.info, Node: Top, Next: Introduction, Up: (dir)
Zutils Manual
*************
-This manual is for Zutils (version 1.1, 2 August 2013).
+This manual is for Zutils (version 1.2-pre2, 4 September 2013).
* Menu:
@@ -24,11 +24,12 @@ This manual is for Zutils (version 1.1, 2 August 2013).
* Zdiff:: Comparing compressed files line by line
* Zgrep:: Searching inside compressed files
* Ztest:: Testing integrity of compressed files
+* Zupdate:: Recompressing files to lzip format
* Problems:: Reporting bugs
* Concept index:: Index of concepts
- Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
+ Copyright (C) 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
This manual is free documentation: you have unlimited permission to
copy, distribute and modify it.
@@ -49,12 +50,13 @@ are created.
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
@@ -545,7 +547,7 @@ matches were found, and 2 means trouble.

-File: zutils.info, Node: Ztest, Next: Problems, Prev: Zgrep, Up: Top
+File: zutils.info, Node: Ztest, Next: Zupdate, Prev: Zgrep, Up: Top
8 Ztest
*******
@@ -588,10 +590,76 @@ environmental problems (file not found, invalid flags, I/O errors, etc),

-File: zutils.info, Node: Problems, Next: Concept index, Prev: Ztest, Up: Top
+File: zutils.info, Node: Zupdate, Next: Problems, Prev: Ztest, Up: Top
-9 Reporting Bugs
-****************
+9 Zupdate
+*********
+
+Zupdate recompresses files from bzip2, gzip, and xz formats to lzip
+format. The originals are compared with the new files and then deleted.
+Only regular files with standard file name extensions are recompressed,
+other files are ignored. Compressed files are decompressed and then
+recompressed on the fly; no temporary files are created. The lzip format
+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
+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'.
+
+ The format for running zupdate is:
+
+ zupdate [OPTIONS] [FILES]
+
+Exit status is 0 if all the compressed files were successfully
+recompressed (if needed), compared and deleted. Non-zero otherwise.
+
+ Zupdate supports the following options:
+
+`-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
+ 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
+ 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.
+
+`-q'
+`--quiet'
+ Quiet operation. Suppress all messages.
+
+`-r'
+`--recursive'
+ Operate recursively on directories.
+
+`-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'
+ to lzip.
+
+
+
+File: zutils.info, Node: Problems, Next: Concept index, Prev: Zupdate, Up: Top
+
+10 Reporting bugs
+*****************
There are probably bugs in zutils. There are certainly errors and
omissions in this manual. If you report them, they will get fixed. If
@@ -621,21 +689,23 @@ Concept index
* zdiff: Zdiff. (line 6)
* zgrep: Zgrep. (line 6)
* ztest: Ztest. (line 6)
+* zupdate: Zupdate. (line 6)

Tag Table:
Node: Top224
-Node: Introduction1095
-Node: Common options3153
-Node: The zutilsrc file4402
-Node: Zcat5328
-Node: Zcmp7250
-Node: Zdiff9574
-Node: Zgrep12077
-Node: Ztest14914
-Node: Problems16139
-Node: Concept index16668
+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

End Tag Table
diff --git a/doc/zutils.texinfo b/doc/zutils.texinfo
index ea635ba..5bbc692 100644
--- a/doc/zutils.texinfo
+++ b/doc/zutils.texinfo
@@ -6,8 +6,8 @@
@finalout
@c %**end of header
-@set UPDATED 2 August 2013
-@set VERSION 1.1
+@set UPDATED 4 September 2013
+@set VERSION 1.2-pre2
@dircategory Data Compression
@direntry
@@ -43,13 +43,13 @@ This manual is for Zutils (version @value{VERSION}, @value{UPDATED}).
* Zdiff:: Comparing compressed files line by line
* Zgrep:: Searching inside compressed files
* Ztest:: Testing integrity of compressed files
+* Zupdate:: Recompressing files to lzip format
* Problems:: Reporting bugs
* Concept index:: Index of concepts
@end menu
@sp 1
-Copyright @copyright{} 2008, 2009, 2010, 2011, 2012, 2013
-Antonio Diaz Diaz.
+Copyright @copyright{} 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
This manual is free documentation: you have unlimited permission
to copy, distribute and modify it.
@@ -70,12 +70,13 @@ programs. In particular the @samp{--recursive} option is very efficient
in those utilities supporting it.
@noindent
-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
@@ -638,14 +639,85 @@ Operate recursively on directories.
@item -v
@itemx --verbose
-Verbose mode. Show the verify status for each file processed.
+Verbose mode. Show the verify status for each file processed.@*
Further -v's increase the verbosity level.
@end table
+@node Zupdate
+@chapter Zupdate
+@cindex zupdate
+
+Zupdate recompresses files from bzip2, gzip, and xz formats to lzip
+format. The originals are compared with the new files and then deleted.
+Only regular files with standard file name extensions are recompressed,
+other files are ignored. Compressed files are decompressed and then
+recompressed on the fly; no temporary files are created. The lzip format
+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 @samp{--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: @samp{.bz2}, @samp{.tbz}, @samp{.tbz2}, @samp{.gz},
+@samp{.tgz}, @samp{.xz}, @samp{.txz}. The files produced have the
+extensions @samp{.lz} or @samp{.tar.lz}.
+
+The format for running zupdate is:
+
+@example
+zupdate [@var{options}] [@var{files}]
+@end example
+
+@noindent
+Exit status is 0 if all the compressed files were successfully
+recompressed (if needed), compared and deleted. Non-zero otherwise.
+
+Zupdate supports the following options:
+
+@table @samp
+@item -f
+@itemx --force
+Do not skip a file for which a lzip compressed version already exists.
+@samp{--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.
+
+@item -l
+@itemx --lzip-verbose
+Pass a @samp{-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 @samp{-l} shows the progress of compression. Use it together with
+@samp{-v} to see the name of the file.
+
+@item -q
+@itemx --quiet
+Quiet operation. Suppress all messages.
+
+@item -r
+@itemx --recursive
+Operate recursively on directories.
+
+@item -v
+@itemx --verbose
+Verbose mode. Show the files being processed. A second @samp{-v} also
+shows the files being ignored.
+
+@item -0 .. -9
+Set the compression level of lzip. By default zupdate passes @samp{-9}
+to lzip.
+
+@end table
+
+
@node Problems
-@chapter Reporting Bugs
+@chapter Reporting bugs
@cindex bugs
@cindex getting help
diff --git a/main.cc b/main.cc
index e6d2d18..04956b6 100644
--- a/main.cc
+++ b/main.cc
@@ -275,7 +275,7 @@ int main( const int argc, const char * const argv[] )
for( ; argind < parser.arguments(); ++argind )
{
const int code = parser.code( argind );
- const char * arg = parser.argument( argind ).c_str();
+ const char * const arg = parser.argument( argind ).c_str();
if( !code )
{
if( program_mode == m_zgrep && !grep_pattern_found )
@@ -399,7 +399,7 @@ int main( const int argc, const char * const argv[] )
if( recursive )
{
struct stat st;
- if( !stat( input_filename.c_str(), &st ) && S_ISDIR( st.st_mode ) )
+ if( stat( input_filename.c_str(), &st ) == 0 && S_ISDIR( st.st_mode ) )
{
DIR * const dirp = opendir( input_filename.c_str() );
if( !dirp )
diff --git a/testsuite/check.sh b/testsuite/check.sh
index fe8dbff..cd01c46 100755
--- a/testsuite/check.sh
+++ b/testsuite/check.sh
@@ -16,6 +16,7 @@ ZGREP="${objdir}"/zgrep
ZEGREP="${objdir}"/zegrep
ZFGREP="${objdir}"/zfgrep
ZTEST="${objdir}"/ztest
+ZUPDATE="${objdir}"/zupdate
compressors="bzip2 gzip lzip"
extensions="bz2 gz lz"
compressor_needed() { echo "${compressors} are needed to run tests" ; exit 1 ; }
@@ -45,9 +46,9 @@ 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 "testing zutils-%s..." "$2"
-printf "\ntesting zcat-%s..." "$2"
+printf "\ntesting zcat-%s..." "$2"
for i in ${extensions}; do
"${ZCAT}" -N in.$i > copy || fail=1
@@ -95,7 +96,7 @@ if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
-printf "\ntesting zcmp-%s..." "$2"
+printf "\ntesting zcmp-%s..." "$2"
for i in ${extensions}; do
"${ZCMP}" -N in.$i || fail=1
@@ -176,7 +177,7 @@ if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
-printf "\ntesting zdiff-%s..." "$2"
+printf "\ntesting zdiff-%s..." "$2"
for i in ${extensions}; do
"${ZDIFF}" -N in.$i > /dev/null || fail=1
@@ -239,7 +240,7 @@ if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
-printf "\ntesting zgrep-%s..." "$2"
+printf "\ntesting zgrep-%s..." "$2"
for i in ${extensions}; do
"${ZGREP}" -N "GNU" in.$i > /dev/null || fail=1
@@ -305,7 +306,7 @@ printf .
printf .
-printf "\ntesting ztest-%s..." "$2"
+printf "\ntesting ztest-%s..." "$2"
for i in ${extensions}; do
"${ZTEST}" -N --format=$i < in.$i || fail=1
@@ -335,10 +336,80 @@ if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
"${ZTEST}" -N --format=lz in.bz2 2> /dev/null
if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
"${ZTEST}" -N --lz='lzip --bad-option' in.lz 2> /dev/null
-if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi
+if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
"${ZTEST}" -N --bad-option 2> /dev/null
if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
+
+printf "\ntesting zupdate-%s..." "$2"
+
+cat in.bz2 > x.bz2 || framework_failure
+cat in.gz > x.gz || framework_failure
+"${ZUPDATE}" -N --bz2=bad_command x.bz2 2> /dev/null
+if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
+"${ZUPDATE}" -N --bz2='bzip2 --bad-option' x.bz2 > /dev/null 2> /dev/null
+if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
+"${ZUPDATE}" -N --gz=bad_command x.gz 2> /dev/null
+if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
+"${ZUPDATE}" -N --gz='gzip --bad-option' x.gz 2> /dev/null
+if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
+"${ZUPDATE}" -N --lz=bad_command x.gz 2> /dev/null
+if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
+"${ZUPDATE}" -N --lz='lzip --bad-option' x.gz 2> /dev/null
+if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
+"${ZUPDATE}" -N --bad-option 2> /dev/null
+if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi
+
+cat in.lz in.lz > x.lz || framework_failure
+"${ZUPDATE}" -N -f x.bz2 x.gz 2> /dev/null
+if [ $? = 1 ] && [ -e x.bz2 ] && [ -e x.gz ] && [ -e x.lz ] ; then printf .
+else printf - ; fail=1
+fi
+rm -f x.lz || framework_failure
+
+"${ZUPDATE}" -N x.bz2 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
+"${ZUPDATE}" -N 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
+
+cat in.bz2 > x.bz2 || framework_failure
+cat in.gz > x.gz || framework_failure
+"${ZUPDATE}" -N x.bz2 x.gz 2> /dev/null
+if [ $? = 1 ] && [ ! -e x.bz2 ] && [ -e x.gz ] && [ -e x.lz ] ; then printf .
+else printf - ; fail=1
+fi
+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 .
+else printf - ; fail=1
+fi
+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 ] && [ -e x.lz ] ; then printf .
+else printf - ; fail=1
+fi
+rm -f x.lz || framework_failure
+
+cat in.bz2 > x.bz2 || framework_failure
+"${ZUPDATE}" -N -6 -q x.bz2
+if [ $? = 0 ] && [ ! -e x.bz2 ] && [ -e x.lz ] ; then printf .
+else printf - ; fail=1
+fi
+rm -f x.lz || framework_failure
+
echo
if [ ${fail} = 0 ] ; then
echo "tests completed successfully."
diff --git a/zcmp.cc b/zcmp.cc
index 8f362c3..3522329 100644
--- a/zcmp.cc
+++ b/zcmp.cc
@@ -246,12 +246,12 @@ int cmp( const long long max_size, const int infd[2],
}
}
- buffer0[rd[0]] = ~buffer1[rd[0]]; // sentinels for the block compare
- buffer1[rd[1]] = ~buffer0[rd[1]];
+ const int min_rd = std::min( rd[0], rd[1] );
+ buffer0[min_rd] = 0; // sentinels for the block compare
+ buffer1[min_rd] = 1;
int first_diff = block_compare( buffer0, buffer1, &line_number );
byte_number += first_diff;
- const int min_rd = std::min( rd[0], rd[1] );
if( first_diff < min_rd )
{
diff --git a/zdiff.cc b/zdiff.cc
index 521d55e..3f0d07f 100644
--- a/zdiff.cc
+++ b/zdiff.cc
@@ -389,6 +389,11 @@ int main( const int argc, const char * const argv[] )
set_signals();
if( !set_fifonames( filenames ) ) return 2;
+ Children children[2];
+ if( !set_data_feeder( fifonames[0], infd[0], children[0], format_types[0] ) ||
+ !set_data_feeder( fifonames[1], infd[1], children[1], format_types[1] ) )
+ return 2;
+
const pid_t diff_pid = fork();
if( diff_pid == 0 ) // child (diff)
{
@@ -407,11 +412,6 @@ int main( const int argc, const char * const argv[] )
if( diff_pid < 0 ) // parent
{ show_fork_error( DIFF ); return 2; }
- Children children[2];
- if( !set_data_feeder( fifonames[0], infd[0], children[0], format_types[0] ) ||
- !set_data_feeder( fifonames[1], infd[1], children[1], format_types[1] ) )
- return 2;
-
int retval = wait_for_child( diff_pid, DIFF );
for( int i = 0; i < 2; ++i )
diff --git a/ztest.cc b/ztest.cc
index 20296b9..79f32dd 100644
--- a/ztest.cc
+++ b/ztest.cc
@@ -59,7 +59,20 @@ int ztest_stdin( const int infd, int format_index,
{ show_error( "Can't create pipe", errno ); return 1; }
const pid_t pid = fork();
- if( pid == 0 ) // child1 (compressor)
+ if( pid == 0 ) // child1 (compressor feeder)
+ {
+ if( close( fda[0] ) != 0 ||
+ !feed_data( infd, fda[1], magic_data, magic_size ) )
+ _exit( 1 );
+ if( close( fda[1] ) != 0 )
+ { show_close_error( "data feeder" ); _exit( 1 ); }
+ _exit( 0 );
+ }
+ if( pid < 0 ) // parent
+ { show_fork_error( "data feeder" ); return 1; }
+
+ const pid_t pid2 = fork();
+ if( pid2 == 0 ) // child2 (compressor)
{
if( dup2( fda[0], STDIN_FILENO ) >= 0 &&
close( fda[0] ) == 0 && close( fda[1] ) == 0 )
@@ -81,25 +94,13 @@ int ztest_stdin( const int infd, int format_index,
show_exec_error( compressor_name );
_exit( 1 );
}
- if( pid < 0 ) // parent
- { show_fork_error( compressor_name ); return 1; }
-
- const pid_t pid2 = fork();
- if( pid2 == 0 ) // child2 (compressor feeder)
- {
- if( close( fda[0] ) != 0 ||
- !feed_data( infd, fda[1], magic_data, magic_size ) )
- _exit( 1 );
- if( close( fda[1] ) != 0 )
- { show_close_error( "data feeder" ); _exit( 1 ); }
- _exit( 0 );
- }
if( pid2 < 0 ) // parent
- { show_fork_error( "data feeder" ); return 1; }
+ { show_fork_error( compressor_name ); return 1; }
close( fda[0] ); close( fda[1] );
- int retval = wait_for_child( pid, compressor_name, 1 );
- if( retval == 0 && wait_for_child( pid2, "data feeder" ) != 0 )
+ const bool isgzxz = ( format_index == fmt_gz || format_index == fmt_xz );
+ int retval = wait_for_child( pid2, compressor_name, 1, isgzxz );
+ if( retval == 0 && wait_for_child( pid, "data feeder" ) != 0 )
retval = 1;
return retval;
}
@@ -141,5 +142,6 @@ int ztest_file( const int infd, int format_index,
if( pid < 0 ) // parent
{ show_fork_error( compressor_name ); return 1; }
- return wait_for_child( pid, compressor_name, 1 );
+ const bool isgzxz = ( format_index == fmt_gz || format_index == fmt_xz );
+ return wait_for_child( pid, compressor_name, 1, isgzxz );
}
diff --git a/zupdate.cc b/zupdate.cc
new file mode 100644
index 0000000..02dfe29
--- /dev/null
+++ b/zupdate.cc
@@ -0,0 +1,415 @@
+/* Zupdate - recompress bzip2, gzip, xz files to lzip files
+ Copyright (C) 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 <utime.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
+
+
+void show_zupdate_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"
+ "Only regular files with standard file name extensions are recompressed,\n"
+ "other files are ignored.\n"
+ "Compressed files are decompressed and then recompressed on the fly; no\n"
+ "temporary files are created.\n"
+ "The lzip format is chosen as destination because it is by far the most\n"
+ "appropriate for long-term data archiving.\n"
+ "\nIf the lzip compressed version of a file already exists, the file is\n"
+ "skipped unless the '--force' option is given. In this case, if the\n"
+ "comparison fails, an error is returned and the original file is not\n"
+ "deleted. The operation of zupdate is meant to be safe and not produce\n"
+ "any data loss. Therefore, existing lzip compressed files are never\n"
+ "overwritten nor deleted.\n"
+ "\nUsage: zupdate [options] [files]\n"
+ "\nExit status is 0 if all the compressed files were successfully\n"
+ "recompressed (if needed), compared and deleted. Non-zero otherwise.\n"
+ "\nOptions:\n"
+ " -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"
+ " -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"
+ " -r, --recursive operate recursively on directories\n"
+ " -v, --verbose be verbose (a 2nd -v gives more)\n"
+ " -0 .. -9 set compression level [default 9]\n"
+ " --bz2=<command> set compressor and options for bzip2 format\n"
+ " --gz=<command> set compressor and options for gzip format\n"
+ " --lz=<command> set compressor and options for lzip format\n"
+ " --xz=<command> set compressor and options for xz format\n" );
+ show_help_addr();
+ }
+
+
+int cant_execute( const std::string & command, const int status )
+ {
+ if( verbosity >= 0 )
+ {
+ if( WIFEXITED( status ) )
+ std::fprintf( stderr, "%s: Error executing '%s'. Exit status = %d.\n",
+ util_name, command.c_str(), WEXITSTATUS( status ) );
+ else
+ std::fprintf( stderr, "%s: Can't execute '%s'.\n",
+ util_name, command.c_str() );
+ }
+ return 1;
+ }
+
+
+ // Set permissions, owner and times.
+void set_permissions( const char * const rname, const struct stat & in_stats )
+ {
+ bool warning = false;
+ // fchown will in many cases return with EPERM, which can be safely ignored.
+ if( ( chown( rname, in_stats.st_uid, in_stats.st_gid ) != 0 &&
+ errno != EPERM ) ||
+ chmod( rname, in_stats.st_mode ) != 0 ) warning = true;
+ struct utimbuf t;
+ t.actime = in_stats.st_atime;
+ t.modtime = in_stats.st_mtime;
+ if( utime( rname, &t ) != 0 ) warning = true;
+ if( warning && verbosity >= 2 )
+ show_error( "Can't change output file attributes." );
+ }
+
+
+struct { const char * from; const char * to; int format_index; } const
+ known_extensions[] = {
+ { ".bz2", "", fmt_bz2 },
+ { ".tbz", ".tar", fmt_bz2 },
+ { ".tbz2", ".tar", fmt_bz2 },
+ { ".gz", "", fmt_gz },
+ { ".tgz", ".tar", fmt_gz },
+ { ".lz", "", fmt_lz },
+ { ".tlz", ".tar", fmt_lz },
+ { ".xz", "", fmt_xz },
+ { ".txz", ".tar", fmt_xz },
+ { 0, 0, -1 } };
+
+
+ // 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 )
+ {
+ int format_index = -1;
+ std::string dname; // decompressed_name
+
+ for( int i = 0; known_extensions[i].from; ++i ) // search extension
+ {
+ const std::string from( known_extensions[i].from );
+ if( name.size() > from.size() &&
+ name.compare( name.size() - from.size(), from.size(), from ) == 0 )
+ {
+ dname.assign( name, 0, name.size() - from.size() );
+ dname += known_extensions[i].to;
+ format_index = known_extensions[i].format_index;
+ if( format_index == fmt_lz )
+ {
+ if( verbosity >= 2 )
+ std::fprintf( stderr, "%s: Input file '%s' already has '%s' suffix.\n",
+ util_name, name.c_str(), known_extensions[i].from );
+ return 0; // ignore this file
+ }
+ break;
+ }
+ }
+ const char * const compressor_name = get_compressor_name( format_index );
+ if( !compressor_name || !compressor_name[0] )
+ {
+ if( verbosity >= 2 )
+ std::fprintf( stderr, "%s: Unknown extension in file name '%s' -- ignored.\n",
+ util_name, name.c_str() );
+ return 0; // ignore this file
+ }
+
+ struct stat in_stats;
+ if( stat( name.c_str(), &in_stats ) != 0 ) // check input file
+ {
+ if( verbosity >= 0 )
+ std::fprintf( stderr, "%s: Can't stat input file '%s': %s.\n",
+ util_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() );
+ return 1;
+ }
+
+ struct stat st;
+ std::string rname( dname ); rname += ".lz"; // recompressed_name
+ const bool lz_exists = ( stat( rname.c_str(), &st ) == 0 );
+ if( lz_exists && !force )
+ {
+ if( verbosity >= 0 )
+ std::fprintf( stderr, "%s: Output file '%s' already exists, skipping.\n",
+ util_name, rname.c_str() );
+ return -1;
+ }
+
+ if( !lz_exists ) // recompress
+ {
+ if( verbosity >= 1 )
+ std::fprintf( stderr, "Recompressing file '%s'.\n", name.c_str() );
+ int fda[2]; // pipe between decompressor and compressor
+ if( pipe( fda ) < 0 )
+ { show_error( "Can't create pipe", errno ); return 1; }
+
+ const pid_t pid = fork();
+ if( pid == 0 ) // child1 (decompressor)
+ {
+ if( dup2( fda[1], STDOUT_FILENO ) >= 0 &&
+ close( fda[0] ) == 0 && close( fda[1] ) == 0 )
+ {
+ const std::vector< std::string > & compressor_args =
+ get_compressor_args( format_index );
+ const int size = compressor_args.size();
+ const char ** const argv = new const char *[size+5];
+ argv[0] = compressor_name;
+ for( int i = 0; i < size; ++i ) argv[i+1] = compressor_args[i].c_str();
+ argv[size+1] = "-cd";
+ argv[size+2] = "--";
+ argv[size+3] = name.c_str();
+ argv[size+4] = 0;
+ execvp( argv[0], (char **)argv );
+ }
+ show_exec_error( compressor_name );
+ _exit( 1 );
+ }
+ if( pid < 0 ) // parent
+ { show_fork_error( compressor_name ); return 1; }
+
+ const pid_t pid2 = fork();
+ if( pid2 == 0 ) // child2 (lzip compressor)
+ {
+ if( dup2( fda[0], STDIN_FILENO ) >= 0 &&
+ close( fda[0] ) == 0 && close( fda[1] ) == 0 )
+ {
+ const std::vector< std::string > & lzip_args =
+ get_compressor_args( fmt_lz );
+ const int size = lzip_args.size();
+ const int size2 = lzip_args2.size();
+ const char ** const argv = new const char *[size+size2+5];
+ argv[0] = lzip_name;
+ argv[1] = "-9";
+ for( int i = 0; i < size; ++i ) argv[i+2] = lzip_args[i].c_str();
+ for( int i = 0; i < size2; ++i ) argv[i+size+2] = lzip_args2[i].c_str();
+ argv[size+size2+2] = "-o";
+ argv[size+size2+3] = dname.c_str();
+ argv[size+size2+4] = 0;
+ execvp( argv[0], (char **)argv );
+ }
+ show_exec_error( lzip_name );
+ _exit( 1 );
+ }
+ if( pid2 < 0 ) // parent
+ { show_fork_error( lzip_name ); return 1; }
+
+ close( fda[0] ); close( fda[1] );
+ int retval = wait_for_child( pid, compressor_name );
+ int retval2 = wait_for_child( pid2, lzip_name );
+ if( retval || retval2 ) { std::remove( rname.c_str() ); return 1; }
+ set_permissions( rname.c_str(), in_stats );
+ }
+
+ {
+ if( lz_exists && verbosity >= 1 )
+ std::fprintf( stderr, "Comparing file '%s'.\n", name.c_str() );
+ std::string zcmp_command( invocation_name );
+ unsigned i = zcmp_command.size();
+ while( i > 0 && zcmp_command[i-1] != '/' ) --i;
+ zcmp_command.resize( i );
+ zcmp_command += "zcmp "; // ${bindir}zcmp
+ zcmp_command += name; zcmp_command += ' '; zcmp_command += rname;
+ int status = std::system( zcmp_command.c_str() );
+ if( status != 0 )
+ { if( !lz_exists ) std::remove( rname.c_str() );
+ return cant_execute( zcmp_command, status ); }
+ }
+
+ if( 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 ) );
+ return 1;
+ }
+ return 0;
+ }
+
+} // end namespace
+
+
+int main( const int argc, const char * const argv[] )
+ {
+ enum { bz2_opt = 256, gz_opt, lz_opt, xz_opt };
+ std::string input_filename;
+ std::list< std::string > filenames;
+ std::vector< std::string > lzip_args2; // args to lzip, maybe empty
+ bool force = false;
+ bool recursive = false;
+ invocation_name = argv[0];
+ util_name = "zupdate";
+
+ const Arg_parser::Option options[] =
+ {
+ { '0', 0, Arg_parser::no },
+ { '1', 0, Arg_parser::no },
+ { '2', 0, Arg_parser::no },
+ { '3', 0, Arg_parser::no },
+ { '4', 0, Arg_parser::no },
+ { '5', 0, Arg_parser::no },
+ { '6', 0, Arg_parser::no },
+ { '7', 0, Arg_parser::no },
+ { '8', 0, Arg_parser::no },
+ { '9', 0, Arg_parser::no },
+ { 'f', "force", Arg_parser::no },
+ { 'h', "help", Arg_parser::no },
+ { 'l', "lzip-verbose", 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 },
+ { 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 ) // common options
+ {
+ 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 'l': lzip_args2.push_back( "-v" ); break;
+ case 'N': continue;
+ 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;
+ default : internal_error( "uncaught option" );
+ }
+ } // end process options
+
+#if defined(__MSVCRT__) || defined(__OS2__)
+ setmode( STDIN_FILENO, O_BINARY );
+ setmode( STDOUT_FILENO, O_BINARY );
+#endif
+
+ const char * const lzip_name = get_compressor_name( fmt_lz );
+ if( !lzip_name || !lzip_name[0] )
+ { show_error( "Missing name of compressor for lzip format." ); return 1; }
+
+ for( ; argind < parser.arguments(); ++argind )
+ filenames.push_back( parser.argument( argind ) );
+
+ int retval = 0;
+ while( !filenames.empty() )
+ {
+ 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;
+ if( tmp > retval ) retval = tmp;
+ if( tmp > 0 ) break;
+ }
+ return retval;
+ }
diff --git a/zutils.cc b/zutils.cc
index bf6fe9a..8961376 100644
--- a/zutils.cc
+++ b/zutils.cc
@@ -338,7 +338,7 @@ int test_format( const int infd, const uint8_t ** const magic_datap,
int wait_for_child( const pid_t pid, const char * const name,
- const int eretval )
+ const int eretval, const bool isgzxz )
{
int status;
while( waitpid( pid, &status, 0 ) == -1 )
@@ -355,7 +355,7 @@ int wait_for_child( const pid_t pid, const char * const name,
if( WIFEXITED( status ) )
{
const int tmp = WEXITSTATUS( status );
- if( eretval == 1 && tmp == 1 ) return 2; // for ztest
+ if( isgzxz && eretval == 1 && tmp == 1 ) return 2; // for ztest
return tmp;
}
return eretval;
diff --git a/zutils.h b/zutils.h
index 238dea8..5ff302c 100644
--- a/zutils.h
+++ b/zutils.h
@@ -79,7 +79,7 @@ int test_format( const int infd, const uint8_t ** const magic_datap,
// 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 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'.