1
0
Fork 0

Adding upstream version 1.8.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-24 05:57:59 +01:00
parent 350226fc6e
commit 6e7259427c
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
34 changed files with 858 additions and 455 deletions

View file

@ -1,3 +1,20 @@
2019-01-01 Antonio Diaz Diaz <antonio@gnu.org>
* Version 1.8 released.
* zcat.cc: Fixed a buffer overflow on outbuf when '-v' is used.
* zcat.cc (cat): A canary byte has been added to outbuf.
* Added new option '-R, --dereference-recursive'.
* Option '-r, --recursive' now skips symlinks.
* If no files and recursive, examine current working directory.
* recursive.cc (test_full_name): Detect directory loops.
* recursive.cc: Ignore directories if not --recursive.
* recursive.cc: Remove extra trailing slashes from directory args.
* zcatgrep.cc (open_instream): Show correct errno.
* zutils.cc (good_status): Wait for killed child.
* Test and document continuation or exit of zcat, zgrep, ztest
and zupdate in case of error.
* configure: Accept appending to CXXFLAGS, 'CXXFLAGS+=OPTIONS'.
2018-02-13 Antonio Diaz Diaz <antonio@gnu.org> 2018-02-13 Antonio Diaz Diaz <antonio@gnu.org>
* Version 1.7 released. * Version 1.7 released.
@ -140,7 +157,7 @@
* Version 0.1 released. * Version 0.1 released.
Copyright (C) 2009-2018 Antonio Diaz Diaz. Copyright (C) 2009-2019 Antonio Diaz Diaz.
This file is a collection of facts, and thus it is not copyrightable, This file is a collection of facts, and thus it is not copyrightable,
but just in case, you have unlimited permission to copy, distribute and but just in case, you have unlimited permission to copy, distribute and

View file

@ -1,12 +1,11 @@
Requirements Requirements
------------ ------------
You will need a C++ compiler. You will need a C++ compiler.
I use gcc 5.3.0 and 4.1.2, but the code should compile with any I use gcc 5.3.0 and 4.1.2, but the code should compile with any standards
standards compliant compiler. compliant compiler.
Gcc is available at http://gcc.gnu.org. Gcc is available at http://gcc.gnu.org.
Compressors for bzip2, gzip and lzip formats are required to run the Compressors for bzip2, gzip and lzip formats are required to run the tests.
tests.
If you are installing zutils along with GNU gzip and want to keep the If you are installing zutils along with GNU gzip and want to keep the
gzip scripts, the recommended method is to configure gzip as follows: gzip scripts, the recommended method is to configure gzip as follows:
@ -70,7 +69,7 @@ After running 'configure', you can run 'make' and 'make install' as
explained above. explained above.
Copyright (C) 2009-2018 Antonio Diaz Diaz. Copyright (C) 2009-2019 Antonio Diaz Diaz.
This file is free documentation: you have unlimited permission to copy, This file is free documentation: you have unlimited permission to copy,
distribute and modify it. distribute and modify it.

View file

@ -223,7 +223,11 @@ dist : doc
$(DISTNAME)/z*.in \ $(DISTNAME)/z*.in \
$(DISTNAME)/testsuite/check.sh \ $(DISTNAME)/testsuite/check.sh \
$(DISTNAME)/testsuite/test.txt \ $(DISTNAME)/testsuite/test.txt \
$(DISTNAME)/testsuite/test.txt.tar $(DISTNAME)/testsuite/test.txt.tar \
$(DISTNAME)/testsuite/zcat_vs.dat \
$(DISTNAME)/testsuite/test_bad_crc.lz \
$(DISTNAME)/testsuite/zero_bad_crc.lz \
$(DISTNAME)/testsuite/zero_bad_crc.gz
rm -f $(DISTNAME) rm -f $(DISTNAME)
lzip -v -9 $(DISTNAME).tar lzip -v -9 $(DISTNAME).tar

40
NEWS
View file

@ -1,4 +1,38 @@
Changes in version 1.7: Changes in version 1.8:
zgrep now passes the '--color' option to grep. (But it only works if the A buffer overflow has been fixed in zcat which happened sometimes when
grep program used supports it). the '-v, --show-nonprinting' option was used (or indirectly enabled).
A canary byte has been added to the output buffer to prevent the buffer
overflow from happening again.
The option '-R, --dereference-recursive', which recursively follows
symbolic links, has been added to zcat, zgrep, ztest and zupdate.
The option '-r, --recursive' now skips symlinks that are encountered
recursively.
If no files are given to zcat, zgrep, ztest and zupdate, a recursive
search will now examine the current working directory.
Recursive directory loops are now detected.
zcat and zgrep now ignore directories given in the command line if
'--recursive' is not specified, instead of reporting an error.
Extra trailing slashes are now removed from directories given in the
command line before recursing into them.
zcat and zgrep now show the right error when they can't open an input
file instead of showing "No such file or directory".
Killed decompressors are now waited for, preventing failure caused by
too many open pipes.
Test and document that if a file fails to decompress, zcat, zgrep and
ztest continue processing the rest of the files.
Test and document that if an error happens while recompressing a file,
zupdate exits immediately without recompressing the rest of the files.
The configure script now accepts appending options to CXXFLAGS using the
syntax 'CXXFLAGS+=OPTIONS'.

6
README
View file

@ -10,8 +10,8 @@ These utilities are not wrapper scripts but safer and more efficient C++
programs. In particular the '--recursive' option is very efficient in programs. In particular the '--recursive' option is very efficient in
those utilities supporting it. those utilities supporting it.
The provided utilities are zcat, zcmp, zdiff, zgrep, ztest and zupdate. The utilities provided are zcat, zcmp, zdiff, zgrep, ztest and zupdate.
The supported formats are bzip2, gzip, lzip and xz. The formats supported are bzip2, gzip, lzip and xz.
Zutils uses external compressors. The compressor to be used for each Zutils uses external compressors. The compressor to be used for each
format is configurable at runtime. format is configurable at runtime.
@ -37,7 +37,7 @@ have been compressed. Decompressed is used to refer to data which have
undergone the process of decompression. undergone the process of decompression.
Copyright (C) 2009-2018 Antonio Diaz Diaz. Copyright (C) 2009-2019 Antonio Diaz Diaz.
This file is free documentation: you have unlimited permission to copy, This file is free documentation: you have unlimited permission to copy,
distribute and modify it. distribute and modify it.

View file

@ -1,5 +1,5 @@
/* Arg_parser - POSIX/GNU command line argument parser. (C++ version) /* Arg_parser - POSIX/GNU command line argument parser. (C++ version)
Copyright (C) 2006-2018 Antonio Diaz Diaz. Copyright (C) 2006-2019 Antonio Diaz Diaz.
This library is free software. Redistribution and use in source and This library is free software. Redistribution and use in source and
binary forms, with or without modification, are permitted provided binary forms, with or without modification, are permitted provided

View file

@ -1,5 +1,5 @@
/* Arg_parser - POSIX/GNU command line argument parser. (C++ version) /* Arg_parser - POSIX/GNU command line argument parser. (C++ version)
Copyright (C) 2006-2018 Antonio Diaz Diaz. Copyright (C) 2006-2019 Antonio Diaz Diaz.
This library is free software. Redistribution and use in source and This library is free software. Redistribution and use in source and
binary forms, with or without modification, are permitted provided binary forms, with or without modification, are permitted provided

20
configure vendored
View file

@ -1,12 +1,12 @@
#! /bin/sh #! /bin/sh
# configure script for Zutils - Utilities dealing with compressed files # configure script for Zutils - Utilities dealing with compressed files
# Copyright (C) 2009-2018 Antonio Diaz Diaz. # Copyright (C) 2009-2019 Antonio Diaz Diaz.
# #
# This configure script is free software: you have unlimited permission # This configure script is free software: you have unlimited permission
# to copy, distribute and modify it. # to copy, distribute and modify it.
pkgname=zutils pkgname=zutils
pkgversion=1.7 pkgversion=1.8
srctrigger=doc/${pkgname}.texi srctrigger=doc/${pkgname}.texi
# clear some things potentially inherited from environment. # clear some things potentially inherited from environment.
@ -73,6 +73,7 @@ while [ $# != 0 ] ; do
echo " CXX=COMPILER C++ compiler to use [${CXX}]" echo " CXX=COMPILER C++ compiler to use [${CXX}]"
echo " CPPFLAGS=OPTIONS command line options for the preprocessor [${CPPFLAGS}]" echo " CPPFLAGS=OPTIONS command line options for the preprocessor [${CPPFLAGS}]"
echo " CXXFLAGS=OPTIONS command line options for the C++ compiler [${CXXFLAGS}]" echo " CXXFLAGS=OPTIONS command line options for the C++ compiler [${CXXFLAGS}]"
echo " CXXFLAGS+=OPTIONS append options to the current value of CXXFLAGS"
echo " LDFLAGS=OPTIONS command line options for the linker [${LDFLAGS}]" echo " LDFLAGS=OPTIONS command line options for the linker [${LDFLAGS}]"
echo " DIFF=NAME diff program to use with zdiff [${DIFF}]" echo " DIFF=NAME diff program to use with zdiff [${DIFF}]"
echo " GREP=NAME grep program to use with zgrep [${GREP}]" echo " GREP=NAME grep program to use with zgrep [${GREP}]"
@ -100,12 +101,13 @@ while [ $# != 0 ] ; do
--sysconfdir=*) sysconfdir=${optarg} ;; --sysconfdir=*) sysconfdir=${optarg} ;;
--no-create) no_create=yes ;; --no-create) no_create=yes ;;
CXX=*) CXX=${optarg} ;; CXX=*) CXX=${optarg} ;;
CPPFLAGS=*) CPPFLAGS=${optarg} ;; CPPFLAGS=*) CPPFLAGS=${optarg} ;;
CXXFLAGS=*) CXXFLAGS=${optarg} ;; CXXFLAGS=*) CXXFLAGS=${optarg} ;;
LDFLAGS=*) LDFLAGS=${optarg} ;; CXXFLAGS+=*) CXXFLAGS="${CXXFLAGS} ${optarg}" ;;
DIFF=*) DIFF=${optarg} ;; LDFLAGS=*) LDFLAGS=${optarg} ;;
GREP=*) GREP=${optarg} ;; DIFF=*) DIFF=${optarg} ;;
GREP=*) GREP=${optarg} ;;
--*) --*)
echo "configure: WARNING: unrecognized option: '${option}'" 1>&2 ;; echo "configure: WARNING: unrecognized option: '${option}'" 1>&2 ;;
@ -180,7 +182,7 @@ echo "GREP = ${GREP}"
rm -f Makefile rm -f Makefile
cat > Makefile << EOF cat > Makefile << EOF
# Makefile for Zutils - Utilities dealing with compressed files # Makefile for Zutils - Utilities dealing with compressed files
# Copyright (C) 2009-2018 Antonio Diaz Diaz. # Copyright (C) 2009-2019 Antonio Diaz Diaz.
# This file was generated automatically by configure. Don't edit. # This file was generated automatically by configure. Don't edit.
# #
# This Makefile is free software: you have unlimited permission # This Makefile is free software: you have unlimited permission

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1.
.TH ZCAT "1" "February 2018" "zcat (zutils) 1.7" "User Commands" .TH ZCAT "1" "January 2019" "zcat (zutils) 1.8" "User Commands"
.SH NAME .SH NAME
zcat \- decompress and concatenate files to standard output zcat \- decompress and concatenate files to standard output
.SH SYNOPSIS .SH SYNOPSIS
@ -9,14 +9,17 @@ zcat \- decompress and concatenate files to standard output
Zcat copies each given file to standard output. If any given file is Zcat copies each given file to standard output. If any given file is
compressed, its decompressed content is used. If a given file does not 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 exist, and its name does not end with one of the known extensions, zcat
tries the compressed file names corresponding to the supported formats. tries the compressed file names corresponding to the formats supported.
.PP .PP
If no files are specified, or if a file is specified as '\-', data are If a file is specified as '\-', data are read from standard input,
read from standard input, decompressed if needed, and sent to standard decompressed if needed, and sent to standard output. Data read from
output. Data read from standard input must be of the same type; all standard input must be of the same type; all uncompressed or all in the
uncompressed or all in the same compression format. same compression format.
.PP .PP
The supported formats are bzip2, gzip, lzip and xz. If no files are specified, recursive searches examine the current
working directory, and nonrecursive searches read standard input.
.PP
The formats supported are bzip2, gzip, lzip and xz.
.PP .PP
Exit status is 0 if no errors occurred, non\-zero otherwise. Exit status is 0 if no errors occurred, non\-zero otherwise.
.SH OPTIONS .SH OPTIONS
@ -57,6 +60,9 @@ suppress all messages
\fB\-r\fR, \fB\-\-recursive\fR \fB\-r\fR, \fB\-\-recursive\fR
operate recursively on directories operate recursively on directories
.TP .TP
\fB\-R\fR, \fB\-\-dereference\-recursive\fR
recursively follow symbolic links
.TP
\fB\-s\fR, \fB\-\-squeeze\-blank\fR \fB\-s\fR, \fB\-\-squeeze\-blank\fR
never more than one single blank line never more than one single blank line
.TP .TP
@ -88,7 +94,7 @@ Report bugs to zutils\-bug@nongnu.org
.br .br
Zutils home page: http://www.nongnu.org/zutils/zutils.html Zutils home page: http://www.nongnu.org/zutils/zutils.html
.SH COPYRIGHT .SH COPYRIGHT
Copyright \(co 2018 Antonio Diaz Diaz. Copyright \(co 2019 Antonio Diaz Diaz.
License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html> License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>
.br .br
This is free software: you are free to change and redistribute it. This is free software: you are free to change and redistribute it.

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1.
.TH ZCMP "1" "February 2018" "zcmp (zutils) 1.7" "User Commands" .TH ZCMP "1" "January 2019" "zcmp (zutils) 1.8" "User Commands"
.SH NAME .SH NAME
zcmp \- decompress and compare two files byte by byte zcmp \- decompress and compare two files byte by byte
.SH SYNOPSIS .SH SYNOPSIS
@ -12,7 +12,7 @@ are numbered starting with 1. If any given file is compressed, its
decompressed content is used. Compressed files are decompressed on the decompressed content is used. Compressed files are decompressed on the
fly; no temporary files are created. fly; no temporary files are created.
.PP .PP
The supported formats are bzip2, gzip, lzip and xz. The formats supported are bzip2, gzip, lzip and xz.
.PP .PP
Zcmp compares file1 to file2. If file2 is omitted zcmp tries the Zcmp compares file1 to file2. If file2 is omitted zcmp tries the
following: following:
@ -85,7 +85,7 @@ Report bugs to zutils\-bug@nongnu.org
.br .br
Zutils home page: http://www.nongnu.org/zutils/zutils.html Zutils home page: http://www.nongnu.org/zutils/zutils.html
.SH COPYRIGHT .SH COPYRIGHT
Copyright \(co 2018 Antonio Diaz Diaz. Copyright \(co 2019 Antonio Diaz Diaz.
License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html> License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>
.br .br
This is free software: you are free to change and redistribute it. This is free software: you are free to change and redistribute it.

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1.
.TH ZDIFF "1" "February 2018" "zdiff (zutils) 1.7" "User Commands" .TH ZDIFF "1" "January 2019" "zdiff (zutils) 1.8" "User Commands"
.SH NAME .SH NAME
zdiff \- decompress and compare two files line by line zdiff \- decompress and compare two files line by line
.SH SYNOPSIS .SH SYNOPSIS
@ -12,7 +12,7 @@ compressed, its decompressed content is used. Zdiff is a front end to
the diff program and has the limitation that messages from diff refer to the diff program and has the limitation that messages from diff refer to
temporary filenames instead of those specified. temporary filenames instead of those specified.
.PP .PP
The supported formats are bzip2, gzip, lzip and xz. The formats supported are bzip2, gzip, lzip and xz.
.PP .PP
Zdiff compares file1 to file2. If file2 is omitted zdiff tries the Zdiff compares file1 to file2. If file2 is omitted zdiff tries the
following: following:
@ -109,7 +109,7 @@ Report bugs to zutils\-bug@nongnu.org
.br .br
Zutils home page: http://www.nongnu.org/zutils/zutils.html Zutils home page: http://www.nongnu.org/zutils/zutils.html
.SH COPYRIGHT .SH COPYRIGHT
Copyright \(co 2018 Antonio Diaz Diaz. Copyright \(co 2019 Antonio Diaz Diaz.
License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html> License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>
.br .br
This is free software: you are free to change and redistribute it. This is free software: you are free to change and redistribute it.

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1.
.TH ZGREP "1" "February 2018" "zgrep (zutils) 1.7" "User Commands" .TH ZGREP "1" "January 2019" "zgrep (zutils) 1.8" "User Commands"
.SH NAME .SH NAME
zgrep \- search compressed files for a regular expression zgrep \- search compressed files for a regular expression
.SH SYNOPSIS .SH SYNOPSIS
@ -11,14 +11,17 @@ on any combination of compressed and uncompressed files. If any given
file is compressed, its decompressed content is used. If a given file file is compressed, its decompressed content is used. If a given file
does not exist, and its name does not end with one of the known does not exist, and its name does not end with one of the known
extensions, zgrep tries the compressed file names corresponding to the extensions, zgrep tries the compressed file names corresponding to the
supported formats. formats supported.
.PP .PP
If no files are specified, or if a file is specified as '\-', data are If a file is specified as '\-', data are read from standard input,
read from standard input, decompressed if needed, and fed to grep. Data decompressed if needed, and fed to grep. Data read from standard input
read from standard input must be of the same type; all uncompressed or must be of the same type; all uncompressed or all in the same
all in the same compression format. compression format.
.PP .PP
The supported formats are bzip2, gzip, lzip and xz. If no files are specified, recursive searches examine the current
working directory, and nonrecursive searches read standard input.
.PP
The formats supported are bzip2, gzip, lzip and xz.
.PP .PP
Exit status is 0 if match, 1 if no match, 2 if trouble. Exit status is 0 if match, 1 if no match, 2 if trouble.
.SH OPTIONS .SH OPTIONS
@ -104,6 +107,9 @@ suppress all messages
\fB\-r\fR, \fB\-\-recursive\fR \fB\-r\fR, \fB\-\-recursive\fR
operate recursively on directories operate recursively on directories
.TP .TP
\fB\-R\fR, \fB\-\-dereference\-recursive\fR
recursively follow symbolic links
.TP
\fB\-s\fR, \fB\-\-no\-messages\fR \fB\-s\fR, \fB\-\-no\-messages\fR
suppress error messages suppress error messages
.TP .TP
@ -138,7 +144,7 @@ Report bugs to zutils\-bug@nongnu.org
.br .br
Zutils home page: http://www.nongnu.org/zutils/zutils.html Zutils home page: http://www.nongnu.org/zutils/zutils.html
.SH COPYRIGHT .SH COPYRIGHT
Copyright \(co 2018 Antonio Diaz Diaz. Copyright \(co 2019 Antonio Diaz Diaz.
License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html> License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>
.br .br
This is free software: you are free to change and redistribute it. This is free software: you are free to change and redistribute it.

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1.
.TH ZTEST "1" "February 2018" "ztest (zutils) 1.7" "User Commands" .TH ZTEST "1" "January 2019" "ztest (zutils) 1.8" "User Commands"
.SH NAME .SH NAME
ztest \- verify the integrity of compressed files ztest \- verify the integrity of compressed files
.SH SYNOPSIS .SH SYNOPSIS
@ -7,12 +7,14 @@ ztest \- verify the integrity of compressed files
[\fI\,options\/\fR] [\fI\,files\/\fR] [\fI\,options\/\fR] [\fI\,files\/\fR]
.SH DESCRIPTION .SH DESCRIPTION
Ztest verifies the integrity of the specified compressed files. Ztest verifies the integrity of the specified compressed files.
Uncompressed files are ignored. If no files are specified, or if a file Uncompressed files are ignored. If a file is specified as '\-', the
is specified as '\-', the integrity of compressed data read from standard integrity of compressed data read from standard input is verified. Data
input is verified. Data read from standard input must be all in the same read from standard input must be all in the same compression format.
compression format.
.PP .PP
The supported formats are bzip2, gzip, lzip and xz. If no files are specified, recursive searches examine the current
working directory, and nonrecursive searches read standard input.
.PP
The formats supported are bzip2, gzip, lzip and xz.
.PP .PP
Note that error detection in the xz format is broken. First, some xz Note that error detection in the xz format is broken. First, some xz
files lack integrity information. Second, not all xz decompressors can files lack integrity information. Second, not all xz decompressors can
@ -47,6 +49,9 @@ suppress all messages
\fB\-r\fR, \fB\-\-recursive\fR \fB\-r\fR, \fB\-\-recursive\fR
operate recursively on directories operate recursively on directories
.TP .TP
\fB\-R\fR, \fB\-\-dereference\-recursive\fR
recursively follow symbolic links
.TP
\fB\-v\fR, \fB\-\-verbose\fR \fB\-v\fR, \fB\-\-verbose\fR
be verbose (a 2nd \fB\-v\fR gives more) be verbose (a 2nd \fB\-v\fR gives more)
.TP .TP
@ -66,7 +71,7 @@ Report bugs to zutils\-bug@nongnu.org
.br .br
Zutils home page: http://www.nongnu.org/zutils/zutils.html Zutils home page: http://www.nongnu.org/zutils/zutils.html
.SH COPYRIGHT .SH COPYRIGHT
Copyright \(co 2018 Antonio Diaz Diaz. Copyright \(co 2019 Antonio Diaz Diaz.
License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html> License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>
.br .br
This is free software: you are free to change and redistribute it. This is free software: you are free to change and redistribute it.

View file

@ -1,19 +1,21 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1.
.TH ZUPDATE "1" "February 2018" "zupdate (zutils) 1.7" "User Commands" .TH ZUPDATE "1" "January 2019" "zupdate (zutils) 1.8" "User Commands"
.SH NAME .SH NAME
zupdate \- recompress bzip2, gzip, xz files to lzip format zupdate \- recompress bzip2, gzip, xz files to lzip format
.SH SYNOPSIS .SH SYNOPSIS
.B zupdate .B zupdate
[\fI\,options\/\fR] [\fI\,files\/\fR] [\fI\,options\/\fR] [\fI\,files\/\fR]
.SH DESCRIPTION .SH DESCRIPTION
Zupdate recompresses files from bzip2, gzip, and xz formats to lzip format. Zupdate recompresses files from bzip2, gzip, and xz formats to lzip
The originals are compared with the new files and then deleted. format. Each original is compared with the new file and then deleted.
Only regular files with standard file name extensions are recompressed, Only regular files with standard file name extensions are recompressed,
other files are ignored. other files are ignored. Compressed files are decompressed and then
Compressed files are decompressed and then recompressed on the fly; no recompressed on the fly; no temporary files are created. The lzip format
temporary files are created. is chosen as destination because it is the most appropriate for
The lzip format is chosen as destination because it is by far the most long\-term data archiving.
appropriate for long\-term data archiving. .PP
If no files are specified, recursive searches examine the current
working directory, and nonrecursive searches do nothing.
.PP .PP
If the lzip compressed version of a file already exists, the file is 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
@ -54,6 +56,9 @@ suppress all messages
\fB\-r\fR, \fB\-\-recursive\fR \fB\-r\fR, \fB\-\-recursive\fR
operate recursively on directories operate recursively on directories
.TP .TP
\fB\-R\fR, \fB\-\-dereference\-recursive\fR
recursively follow symbolic links
.TP
\fB\-v\fR, \fB\-\-verbose\fR \fB\-v\fR, \fB\-\-verbose\fR
be verbose (a 2nd \fB\-v\fR gives more) be verbose (a 2nd \fB\-v\fR gives more)
.TP .TP
@ -76,7 +81,7 @@ Report bugs to zutils\-bug@nongnu.org
.br .br
Zutils home page: http://www.nongnu.org/zutils/zutils.html Zutils home page: http://www.nongnu.org/zutils/zutils.html
.SH COPYRIGHT .SH COPYRIGHT
Copyright \(co 2018 Antonio Diaz Diaz. Copyright \(co 2019 Antonio Diaz Diaz.
License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html> License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>
.br .br
This is free software: you are free to change and redistribute it. This is free software: you are free to change and redistribute it.

View file

@ -12,7 +12,7 @@ File: zutils.info, Node: Top, Next: Introduction, Up: (dir)
Zutils Manual Zutils Manual
************* *************
This manual is for Zutils (version 1.7, 13 February 2018). This manual is for Zutils (version 1.8, 1 January 2019).
* Menu: * Menu:
@ -29,7 +29,7 @@ This manual is for Zutils (version 1.7, 13 February 2018).
* Concept index:: Index of concepts * Concept index:: Index of concepts
Copyright (C) 2009-2018 Antonio Diaz Diaz. Copyright (C) 2009-2019 Antonio Diaz Diaz.
This manual is free documentation: you have unlimited permission to This manual is free documentation: you have unlimited permission to
copy, distribute and modify it. copy, distribute and modify it.
@ -50,8 +50,8 @@ are created.
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. in those utilities supporting it.
The provided utilities are zcat, zcmp, zdiff, zgrep, ztest and zupdate. The utilities provided are zcat, zcmp, zdiff, zgrep, ztest and zupdate.
The supported formats are bzip2, gzip, lzip and xz. The formats supported are bzip2, gzip, lzip and xz.
Zutils uses external compressors. The compressor to be used for each Zutils uses external compressors. The compressor to be used for each
format is configurable at runtime. format is configurable at runtime.
@ -110,7 +110,8 @@ described here.
'-V' '-V'
'--version' '--version'
Print the version number on the standard output and exit. Print the version number on the standard output and exit. This
version number should be included in all bug reports.
'-M FORMAT_LIST' '-M FORMAT_LIST'
'--format=FORMAT_LIST' '--format=FORMAT_LIST'
@ -190,12 +191,17 @@ File: zutils.info, Node: Zcat, Next: Zcmp, Prev: The zutilsrc file, Up: Top
zcat copies each given file to standard output. If any given file is zcat copies each given file to standard output. If any given file is
compressed, its decompressed content is used. If a given file does not 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 exist, and its name does not end with one of the known extensions, zcat
tries the compressed file names corresponding to the supported formats. tries the compressed file names corresponding to the formats supported.
If a file fails to decompress, zcat continues copying the rest of the
files.
If no files are specified, or if a file is specified as '-', data If a file is specified as '-', data are read from standard input,
are read from standard input, decompressed if needed, and sent to decompressed if needed, and sent to standard output. Data read from
standard output. Data read from standard input must be of the same type; standard input must be of the same type; all uncompressed or all in the
all uncompressed or all in the same compression format. same compression format.
If no files are specified, recursive searches examine the current
working directory, and nonrecursive searches read standard input.
The format for running zcat is: The format for running zcat is:
@ -240,7 +246,14 @@ Exit status is 0 if no errors occurred, non-zero otherwise.
'-r' '-r'
'--recursive' '--recursive'
Operate recursively on directories. For each directory operand, read and process all files in that
directory, recursively. Follow symbolic links in the command line,
but skip symlinks that are encountered recursively.
'-R'
'--dereference-recursive'
For each directory operand, read and process all files in that
directory, recursively, following all symbolic links.
'-s' '-s'
'--squeeze-blank' '--squeeze-blank'
@ -459,12 +472,16 @@ on any combination of compressed and uncompressed files. If any given
file is compressed, its decompressed content is used. If a given file file is compressed, its decompressed content is used. If a given file
does not exist, and its name does not end with one of the known does not exist, and its name does not end with one of the known
extensions, zgrep tries the compressed file names corresponding to the extensions, zgrep tries the compressed file names corresponding to the
supported formats. formats supported. If a file fails to decompress, zgrep continues
searching the rest of the files.
If no files are specified, or if a file is specified as '-', data If a file is specified as '-', data are read from standard input,
are read from standard input, decompressed if needed, and fed to grep. decompressed if needed, and fed to grep. Data read from standard input
Data read from standard input must be of the same type; all uncompressed must be of the same type; all uncompressed or all in the same
or all in the same compression format. compression format.
If no files are specified, recursive searches examine the current
working directory, and nonrecursive searches read standard input.
The format for running zgrep is: The format for running zgrep is:
@ -573,7 +590,14 @@ grep program used supports them):
'-r' '-r'
'--recursive' '--recursive'
Operate recursively on directories. For each directory operand, read and process all files in that
directory, recursively. Follow symbolic links in the command line,
but skip symlinks that are encountered recursively.
'-R'
'--dereference-recursive'
For each directory operand, read and process all files in that
directory, recursively, following all symbolic links.
'-s' '-s'
'--no-messages' '--no-messages'
@ -602,10 +626,14 @@ File: zutils.info, Node: Ztest, Next: Zupdate, Prev: Zgrep, Up: Top
******* *******
ztest verifies the integrity of the specified compressed files. ztest verifies the integrity of the specified compressed files.
Uncompressed files are ignored. If no files are specified, or if a file Uncompressed files are ignored. If a file is specified as '-', the
is specified as '-', the integrity of compressed data read from integrity of compressed data read from standard input is verified. Data
standard input is verified. Data read from standard input must be all in read from standard input must be all in the same compression format. If
the same compression format. a file fails to decompress, ztest continues verifying the rest of the
files.
If no files are specified, recursive searches examine the current
working directory, and nonrecursive searches read standard input.
Note that error detection in the xz format is broken. First, some xz Note that error detection in the xz format is broken. First, some xz
files lack integrity information. Second, not all xz decompressors can files lack integrity information. Second, not all xz decompressors can
@ -640,7 +668,14 @@ environmental problems (file not found, invalid flags, I/O errors, etc),
'-r' '-r'
'--recursive' '--recursive'
Operate recursively on directories. For each directory operand, read and process all files in that
directory, recursively. Follow symbolic links in the command line,
but skip symlinks that are encountered recursively.
'-R'
'--dereference-recursive'
For each directory operand, read and process all files in that
directory, recursively, following all symbolic links.
'-v' '-v'
'--verbose' '--verbose'
@ -658,9 +693,14 @@ zupdate recompresses files from bzip2, gzip, and xz formats to lzip
format. Each original is compared with the new file and then deleted. format. Each original is compared with the new file and then deleted.
Only regular files with standard file name extensions are recompressed, Only regular files with standard file name extensions are recompressed,
other files are ignored. Compressed files are decompressed and then other files are ignored. Compressed files are decompressed and then
recompressed on the fly; no temporary files are created. The lzip format recompressed on the fly; no temporary files are created. If an error
is chosen as destination because it is by far the most appropriate for happens while recompressing a file, zupdate exits immediately without
long-term data archiving. recompressing the rest of the files. The lzip format is chosen as
destination because it is the most appropriate for long-term data
archiving.
If no files are specified, recursive searches examine the current
working directory, and nonrecursive searches do nothing.
If the lzip compressed version of a file already exists, the file is 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
@ -679,7 +719,7 @@ files produced have the extensions '.lz' or '.tar.lz'.
Recompressing a file is much like copying or moving it; therefore Recompressing a file is much like copying or moving it; therefore
zupdate preserves the access and modification dates, permissions, and, zupdate preserves the access and modification dates, permissions, and,
when possible, ownership of the file just as "cp -p" does. (If the user when possible, ownership of the file just as 'cp -p' does. (If the user
ID or the group ID can't be duplicated, the file permission bits S_ISUID ID or the group ID can't be duplicated, the file permission bits S_ISUID
and S_ISGID are cleared). and S_ISGID are cleared).
@ -718,7 +758,14 @@ otherwise.
'-r' '-r'
'--recursive' '--recursive'
Operate recursively on directories. For each directory operand, read and process all files in that
directory, recursively. Follow symbolic links in the command line,
but skip symlinks that are encountered recursively.
'-R'
'--dereference-recursive'
For each directory operand, read and process all files in that
directory, recursively, following all symbolic links.
'-v' '-v'
'--verbose' '--verbose'
@ -770,18 +817,18 @@ Concept index
 
Tag Table: Tag Table:
Node: Top222 Node: Top222
Node: Introduction1151 Node: Introduction1149
Node: Common options3775 Node: Common options3773
Ref: compressor-requirements5533 Ref: compressor-requirements5596
Node: The zutilsrc file5905 Node: The zutilsrc file5968
Node: Zcat6830 Node: Zcat6893
Node: Zcmp8884 Node: Zcmp9445
Node: Zdiff11343 Node: Zdiff11904
Node: Zgrep14047 Node: Zgrep14608
Node: Ztest17541 Node: Ztest18603
Node: Zupdate19375 Node: Zupdate20938
Node: Problems22247 Node: Problems24364
Node: Concept index22781 Node: Concept index24898
 
End Tag Table End Tag Table

View file

@ -6,8 +6,8 @@
@finalout @finalout
@c %**end of header @c %**end of header
@set UPDATED 13 February 2018 @set UPDATED 1 January 2019
@set VERSION 1.7 @set VERSION 1.8
@dircategory Data Compression @dircategory Data Compression
@direntry @direntry
@ -49,7 +49,7 @@ This manual is for Zutils (version @value{VERSION}, @value{UPDATED}).
@end menu @end menu
@sp 1 @sp 1
Copyright @copyright{} 2009-2018 Antonio Diaz Diaz. Copyright @copyright{} 2009-2019 Antonio Diaz Diaz.
This manual is free documentation: you have unlimited permission This manual is free documentation: you have unlimited permission
to copy, distribute and modify it. to copy, distribute and modify it.
@ -70,8 +70,8 @@ programs. In particular the @samp{--recursive} option is very efficient
in those utilities supporting it. in those utilities supporting it.
@noindent @noindent
The provided utilities are zcat, zcmp, zdiff, zgrep, ztest and zupdate.@* The utilities provided are zcat, zcmp, zdiff, zgrep, ztest and zupdate.@*
The supported formats are bzip2, gzip, lzip and xz.@* The formats supported are bzip2, gzip, lzip and xz.@*
Zutils uses external compressors. The compressor to be used for each Zutils uses external compressors. The compressor to be used for each
format is configurable at runtime. format is configurable at runtime.
@ -133,6 +133,7 @@ only supports the @samp{--help} form of this option.
@item -V @item -V
@itemx --version @itemx --version
Print the version number on the standard output and exit. Print the version number on the standard output and exit.
This version number should be included in all bug reports.
@item -M @var{format_list} @item -M @var{format_list}
@itemx --format=@var{format_list} @itemx --format=@var{format_list}
@ -221,12 +222,17 @@ where <format> is one of @samp{bz2}, @samp{gz}, @samp{lz} or @samp{xz}.
zcat copies each given file to standard output. If any given file is zcat copies each given file to standard output. If any given file is
compressed, its decompressed content is used. If a given file does not 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 exist, and its name does not end with one of the known extensions, zcat
tries the compressed file names corresponding to the supported formats. tries the compressed file names corresponding to the formats supported.
If a file fails to decompress, zcat continues copying the rest of the
files.
If no files are specified, or if a file is specified as @samp{-}, data If a file is specified as @samp{-}, data are read from standard input,
are read from standard input, decompressed if needed, and sent to decompressed if needed, and sent to standard output. Data read from
standard output. Data read from standard input must be of the same type; standard input must be of the same type; all uncompressed or all in the
all uncompressed or all in the same compression format. same compression format.
If no files are specified, recursive searches examine the current
working directory, and nonrecursive searches read standard input.
The format for running zcat is: The format for running zcat is:
@ -274,7 +280,14 @@ Quiet operation. Suppress all messages.
@item -r @item -r
@itemx --recursive @itemx --recursive
Operate recursively on directories. For each directory operand, read and process all files in that
directory, recursively. Follow symbolic links in the command line, but
skip symlinks that are encountered recursively.
@item -R
@itemx --dereference-recursive
For each directory operand, read and process all files in that
directory, recursively, following all symbolic links.
@item -s @item -s
@itemx --squeeze-blank @itemx --squeeze-blank
@ -509,12 +522,16 @@ on any combination of compressed and uncompressed files. If any given
file is compressed, its decompressed content is used. If a given file file is compressed, its decompressed content is used. If a given file
does not exist, and its name does not end with one of the known does not exist, and its name does not end with one of the known
extensions, zgrep tries the compressed file names corresponding to the extensions, zgrep tries the compressed file names corresponding to the
supported formats. formats supported. If a file fails to decompress, zgrep continues
searching the rest of the files.
If no files are specified, or if a file is specified as @samp{-}, data If a file is specified as @samp{-}, data are read from standard input,
are read from standard input, decompressed if needed, and fed to grep. decompressed if needed, and fed to grep. Data read from standard input
Data read from standard input must be of the same type; all uncompressed must be of the same type; all uncompressed or all in the same
or all in the same compression format. compression format.
If no files are specified, recursive searches examine the current
working directory, and nonrecursive searches read standard input.
The format for running zgrep is: The format for running zgrep is:
@ -629,7 +646,14 @@ found, even if an error was detected.
@item -r @item -r
@itemx --recursive @itemx --recursive
Operate recursively on directories. For each directory operand, read and process all files in that
directory, recursively. Follow symbolic links in the command line, but
skip symlinks that are encountered recursively.
@item -R
@itemx --dereference-recursive
For each directory operand, read and process all files in that
directory, recursively, following all symbolic links.
@item -s @item -s
@itemx --no-messages @itemx --no-messages
@ -658,10 +682,14 @@ Match only whole lines.
@cindex ztest @cindex ztest
ztest verifies the integrity of the specified compressed files. ztest verifies the integrity of the specified compressed files.
Uncompressed files are ignored. If no files are specified, or if a file Uncompressed files are ignored. If a file is specified as @samp{-}, the
is specified as @samp{-}, the integrity of compressed data read from integrity of compressed data read from standard input is verified. Data
standard input is verified. Data read from standard input must be all in read from standard input must be all in the same compression format. If
the same compression format. a file fails to decompress, ztest continues verifying the rest of the
files.
If no files are specified, recursive searches examine the current
working directory, and nonrecursive searches read standard input.
Note that error detection in the xz format is broken. First, some xz Note that error detection in the xz format is broken. First, some xz
files lack integrity information. Second, not all xz decompressors can files lack integrity information. Second, not all xz decompressors can
@ -703,7 +731,14 @@ Quiet operation. Suppress all messages.
@item -r @item -r
@itemx --recursive @itemx --recursive
Operate recursively on directories. For each directory operand, read and process all files in that
directory, recursively. Follow symbolic links in the command line, but
skip symlinks that are encountered recursively.
@item -R
@itemx --dereference-recursive
For each directory operand, read and process all files in that
directory, recursively, following all symbolic links.
@item -v @item -v
@itemx --verbose @itemx --verbose
@ -721,9 +756,14 @@ zupdate recompresses files from bzip2, gzip, and xz formats to lzip
format. Each original is compared with the new file and then deleted. format. Each original is compared with the new file and then deleted.
Only regular files with standard file name extensions are recompressed, Only regular files with standard file name extensions are recompressed,
other files are ignored. Compressed files are decompressed and then other files are ignored. Compressed files are decompressed and then
recompressed on the fly; no temporary files are created. The lzip format recompressed on the fly; no temporary files are created. If an error
is chosen as destination because it is by far the most appropriate for happens while recompressing a file, zupdate exits immediately without
long-term data archiving. recompressing the rest of the files. The lzip format is chosen as
destination because it is the most appropriate for long-term data
archiving.
If no files are specified, recursive searches examine the current
working directory, and nonrecursive searches do nothing.
If the lzip compressed version of a file already exists, the file is 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 skipped unless the @samp{--force} option is given. In this case, if the
@ -743,7 +783,7 @@ extensions @samp{.lz} or @samp{.tar.lz}.
Recompressing a file is much like copying or moving it; therefore Recompressing a file is much like copying or moving it; therefore
zupdate preserves the access and modification dates, permissions, and, zupdate preserves the access and modification dates, permissions, and,
when possible, ownership of the file just as "cp -p" does. (If the user when possible, ownership of the file just as @samp{cp -p} does. (If the user
ID or the group ID can't be duplicated, the file permission bits S_ISUID ID or the group ID can't be duplicated, the file permission bits S_ISUID
and S_ISGID are cleared). and S_ISGID are cleared).
@ -785,7 +825,14 @@ Quiet operation. Suppress all messages.
@item -r @item -r
@itemx --recursive @itemx --recursive
Operate recursively on directories. For each directory operand, read and process all files in that
directory, recursively. Follow symbolic links in the command line, but
skip symlinks that are encountered recursively.
@item -R
@itemx --dereference-recursive
For each directory operand, read and process all files in that
directory, recursively, following all symbolic links.
@item -v @item -v
@itemx --verbose @itemx --verbose

32
rc.cc
View file

@ -1,5 +1,5 @@
/* Zutils - Utilities dealing with compressed files /* Zutils - Utilities dealing with compressed files
Copyright (C) 2009-2018 Antonio Diaz Diaz. Copyright (C) 2009-2019 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -37,7 +37,7 @@ int verbosity = 0;
namespace { namespace {
const char * const config_file_name = "zutilsrc"; const char * const config_file_name = "zutilsrc";
const char * const program_year = "2018"; const char * const program_year = "2019";
std::string compressor_names[num_formats] = std::string compressor_names[num_formats] =
{ "bzip2", "gzip", "lzip", "xz" }; // default compressor names { "bzip2", "gzip", "lzip", "xz" }; // default compressor names
@ -211,8 +211,8 @@ int process_rcfile( const std::string & name )
bool enabled_format( const int format_index ) bool enabled_format( const int format_index )
{ {
if( enabled_formats.size() <= num_formats ) return true; if( enabled_formats.size() <= num_formats ) return true; // all enabled
if( format_index < 0 ) return enabled_formats[num_formats]; if( format_index < 0 ) return enabled_formats[num_formats]; // uncompressed
return enabled_formats[format_index]; return enabled_formats[format_index];
} }
@ -336,11 +336,9 @@ void show_error( const char * const msg, const int errcode, const bool help )
{ {
if( verbosity < 0 ) return; if( verbosity < 0 ) return;
if( msg && msg[0] ) if( msg && msg[0] )
{ std::fprintf( stderr, "%s: %s%s%s\n", program_name, msg,
std::fprintf( stderr, "%s: %s", program_name, msg ); ( errcode > 0 ) ? ": " : "",
if( errcode > 0 ) std::fprintf( stderr, ": %s", std::strerror( errcode ) ); ( errcode > 0 ) ? std::strerror( errcode ) : "" );
std::fputc( '\n', stderr );
}
if( help ) if( help )
std::fprintf( stderr, "Try '%s --help' for more information.\n", std::fprintf( stderr, "Try '%s --help' for more information.\n",
invocation_name ); invocation_name );
@ -350,18 +348,10 @@ void show_error( const char * const msg, const int errcode, const bool help )
void show_file_error( const char * const filename, const char * const msg, void show_file_error( const char * const filename, const char * const msg,
const int errcode ) const int errcode )
{ {
if( verbosity < 0 ) return;
std::fprintf( stderr, "%s: %s: %s", program_name, filename, msg );
if( errcode > 0 ) std::fprintf( stderr, ": %s", std::strerror( errcode ) );
std::fputc( '\n', stderr );
}
void show_error2( const char * const msg, const char * const name )
{
if( verbosity >= 0 ) if( verbosity >= 0 )
std::fprintf( stderr, "%s: %s '%s': %s\n", std::fprintf( stderr, "%s: %s: %s%s%s\n", program_name, filename, msg,
program_name, msg, name, std::strerror( errno ) ); ( errcode > 0 ) ? ": " : "",
( errcode > 0 ) ? std::strerror( errcode ) : "" );
} }
@ -376,7 +366,7 @@ void internal_error( const char * const msg )
void show_close_error( const char * const prog_name ) void show_close_error( const char * const prog_name )
{ {
if( verbosity >= 0 ) if( verbosity >= 0 )
std::fprintf( stderr, "%s: Can't close output of %s: %s\n", std::fprintf( stderr, "%s: Error closing output of %s: %s\n",
program_name, prog_name, std::strerror( errno ) ); program_name, prog_name, std::strerror( errno ) );
} }

3
rc.h
View file

@ -1,5 +1,5 @@
/* Zutils - Utilities dealing with compressed files /* Zutils - Utilities dealing with compressed files
Copyright (C) 2009-2018 Antonio Diaz Diaz. Copyright (C) 2009-2019 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -51,7 +51,6 @@ void show_error( const char * const msg, const int errcode = 0,
const bool help = false ); const bool help = false );
void show_file_error( const char * const filename, const char * const msg, void show_file_error( const char * const filename, const char * const msg,
const int errcode = 0 ); const int errcode = 0 );
void show_error2( const char * const msg, const char * const name );
void internal_error( const char * const msg ); void internal_error( const char * const msg );
void show_close_error( const char * const prog_name = "data feeder" ); void show_close_error( const char * const prog_name = "data feeder" );
void show_exec_error( const char * const prog_name ); void show_exec_error( const char * const prog_name );

View file

@ -1,5 +1,5 @@
/* Zutils - Utilities dealing with compressed files /* Zutils - Utilities dealing with compressed files
Copyright (C) 2009-2018 Antonio Diaz Diaz. Copyright (C) 2009-2019 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -15,9 +15,41 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
// Returns true if full_name is a regular file with an enabled extension
// or (a link to) a directory.
bool test_full_name( const std::string & full_name, const struct stat * stp,
const bool follow )
{
struct stat st, st2;
if( follow && stat( full_name.c_str(), &st ) != 0 ) return false;
if( !follow && lstat( full_name.c_str(), &st ) != 0 ) return false;
if( S_ISREG( st.st_mode ) ) // regular file
return enabled_format( extension_format( extension_index( full_name ) ) );
if( !S_ISDIR( st.st_mode ) ) return false;
std::string prev_dir( full_name );
bool loop = ( stp && st.st_ino == stp->st_ino && st.st_dev == stp->st_dev );
if( !loop )
for( unsigned i = prev_dir.size(); i > 1; )
{
while( i > 0 && prev_dir[i-1] != '/' ) --i;
if( i == 0 ) break;
if( i > 1 ) --i; // remove trailing slash except at root dir
prev_dir.resize( i );
if( stat( prev_dir.c_str(), &st2 ) != 0 || !S_ISDIR( st2.st_mode ) ||
( st.st_ino == st2.st_ino && st.st_dev == st2.st_dev ) )
{ loop = true; break; }
}
if( loop ) // full_name already visited or above tree
show_file_error( full_name.c_str(), "warning: Recursive directory loop" );
return !loop; // (link to) directory
}
// Returns in input_filename the next filename.
bool next_filename( std::list< std::string > & filenames, bool next_filename( std::list< std::string > & filenames,
std::string & input_filename, bool & error, std::string & input_filename, bool & error,
const bool recursive, const bool ignore_stdin = false, const int recursive, const bool ignore_stdin = false,
const bool no_messages = false ) const bool no_messages = false )
{ {
while( !filenames.empty() ) while( !filenames.empty() )
@ -29,18 +61,30 @@ bool next_filename( std::list< std::string > & filenames,
if( ignore_stdin ) continue; if( ignore_stdin ) continue;
input_filename.clear(); return true; input_filename.clear(); return true;
} }
if( recursive ) struct stat st;
if( stat( input_filename.c_str(), &st ) == 0 && S_ISDIR( st.st_mode ) )
{ {
struct stat st; if( recursive )
if( stat( input_filename.c_str(), &st ) == 0 && S_ISDIR( st.st_mode ) )
{ {
DIR * const dirp = opendir( input_filename.c_str() ); DIR * const dirp = opendir( input_filename.c_str() );
if( !dirp ) if( !dirp )
{ {
if( !no_messages ) if( !no_messages )
show_error2( "Can't open directory", input_filename.c_str() ); show_file_error( input_filename.c_str(), "Can't open directory", errno );
error = true; continue; error = true; continue;
} }
for( unsigned i = input_filename.size();
i > 1 && input_filename[i-1] == '/'; --i )
input_filename.resize( i - 1 ); // remove trailing slashes
struct stat stdot, *stdotp = 0;
if( input_filename[0] != '/' ) // relative path
{
if( input_filename == "." ) input_filename.clear();
if( stat( ".", &stdot ) == 0 && S_ISDIR( stdot.st_mode ) )
stdotp = &stdot;
}
if( input_filename.size() && input_filename != "/" )
input_filename += '/';
std::list< std::string > tmp_list; std::list< std::string > tmp_list;
while( true ) while( true )
{ {
@ -48,14 +92,13 @@ bool next_filename( std::list< std::string > & filenames,
if( !entryp ) { closedir( dirp ); break; } if( !entryp ) { closedir( dirp ); break; }
const std::string tmp_name( entryp->d_name ); const std::string tmp_name( entryp->d_name );
if( tmp_name == "." || tmp_name == ".." ) continue; if( tmp_name == "." || tmp_name == ".." ) continue;
const std::string full_name( input_filename + "/" + tmp_name ); const std::string full_name( input_filename + tmp_name );
if( enabled_format( extension_format( extension_index( tmp_name ) ) ) || if( test_full_name( full_name, stdotp, recursive == 2 ) )
( stat( full_name.c_str(), &st ) == 0 && S_ISDIR( st.st_mode ) ) )
tmp_list.push_back( full_name ); tmp_list.push_back( full_name );
} }
filenames.splice( filenames.begin(), tmp_list ); filenames.splice( filenames.begin(), tmp_list );
continue;
} }
continue;
} }
return true; return true;
} }

View file

@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# check script for Zutils - Utilities dealing with compressed files # check script for Zutils - Utilities dealing with compressed files
# Copyright (C) 2009-2018 Antonio Diaz Diaz. # Copyright (C) 2009-2019 Antonio Diaz Diaz.
# #
# This script is free software: you have unlimited permission # This script is free software: you have unlimited permission
# to copy, distribute and modify it. # to copy, distribute and modify it.
@ -53,6 +53,9 @@ cat in > -in- || framework_failure
cat in.lz > -in-.lz || framework_failure cat in.lz > -in-.lz || framework_failure
cat in.lz > lz_only.lz || framework_failure cat in.lz > lz_only.lz || framework_failure
cat in in in in in in > in6 || framework_failure cat in in in in in in > in6 || framework_failure
bad0_lz="${testdir}"/zero_bad_crc.lz
bad0_gz="${testdir}"/zero_bad_crc.gz
bad1_lz="${testdir}"/test_bad_crc.lz
fail=0 fail=0
test_failed() { fail=1 ; printf " $1" ; [ -z "$2" ] || printf "($2)" ; } test_failed() { fail=1 ; printf " $1" ; [ -z "$2" ] || printf "($2)" ; }
@ -71,6 +74,7 @@ for i in ${extensions}; do
test_failed $LINENO $i test_failed $LINENO $i
done done
"${ZCAT}" -N -v -s "${testdir}"/zcat_vs.dat > /dev/null || test_failed $LINENO
"${ZCAT}" -N < in > copy || test_failed $LINENO "${ZCAT}" -N < in > copy || test_failed $LINENO
cmp in copy || test_failed $LINENO cmp in copy || test_failed $LINENO
"${ZCAT}" -N < in.gz > copy || test_failed $LINENO "${ZCAT}" -N < in.gz > copy || test_failed $LINENO
@ -89,6 +93,18 @@ cmp in copy || test_failed $LINENO
cmp in copy || test_failed $LINENO cmp in copy || test_failed $LINENO
"${ZCAT}" -N in in.gz in.bz2 in.lz -- -in- -in-.lz > copy || test_failed $LINENO "${ZCAT}" -N in in.gz in.bz2 in.lz -- -in- -in-.lz > copy || test_failed $LINENO
cmp in6 copy || test_failed $LINENO cmp in6 copy || test_failed $LINENO
"${ZCAT}" -Nq in in.gz in.bz2 in.lz "${bad0_lz}" -- -in- -in-.lz > copy
[ $? = 1 ] || test_failed $LINENO
cmp in6 copy || test_failed $LINENO
"${ZCAT}" -Nq "${bad1_lz}" -- -in-.lz in in.gz in.bz2 in.lz > copy
[ $? = 1 ] || test_failed $LINENO
cmp in6 copy || test_failed $LINENO
"${ZCAT}" -N . || test_failed $LINENO
"${ZCAT}" -N -r . > /dev/null || test_failed $LINENO
"${ZCAT}" -N -r > /dev/null || test_failed $LINENO
"${ZCAT}" -N -R . > /dev/null || test_failed $LINENO
"${ZCAT}" -N -R > /dev/null || test_failed $LINENO
"${ZCAT}" -Nq --format=, in.lz "${ZCAT}" -Nq --format=, in.lz
[ $? = 1 ] || test_failed $LINENO [ $? = 1 ] || test_failed $LINENO
"${ZCAT}" -Nq --format=,lz in.lz "${ZCAT}" -Nq --format=,lz in.lz
@ -258,6 +274,9 @@ done
"${ZGREP}" -N pin.tar -e "GNU" > /dev/null || test_failed $LINENO "${ZGREP}" -N pin.tar -e "GNU" > /dev/null || test_failed $LINENO
"${ZGREP}" -N "GNU" < pin.tar > /dev/null || test_failed $LINENO "${ZGREP}" -N "GNU" < pin.tar > /dev/null || test_failed $LINENO
"${ZGREP}" -N -r "GNU" . > /dev/null || test_failed $LINENO "${ZGREP}" -N -r "GNU" . > /dev/null || test_failed $LINENO
"${ZGREP}" -N -r "GNU" > /dev/null || test_failed $LINENO
"${ZGREP}" -N -R "GNU" . > /dev/null || test_failed $LINENO
"${ZGREP}" -N -R "GNU" > /dev/null || test_failed $LINENO
"${ZGREP}" -N "nx_pattern" -r . in > /dev/null && test_failed $LINENO "${ZGREP}" -N "nx_pattern" -r . in > /dev/null && test_failed $LINENO
"${ZGREP}" -N -e "GNU" in > /dev/null || test_failed $LINENO "${ZGREP}" -N -e "GNU" in > /dev/null || test_failed $LINENO
"${ZGREP}" -N "GNU" < in > /dev/null || test_failed $LINENO "${ZGREP}" -N "GNU" < in > /dev/null || test_failed $LINENO
@ -275,6 +294,12 @@ done
test_failed $LINENO test_failed $LINENO
"${ZGREP}" -N -L "nx_pattern" in in.gz in.bz2 in.lz -- -in- > /dev/null && "${ZGREP}" -N -L "nx_pattern" in in.gz in.bz2 in.lz -- -in- > /dev/null &&
test_failed $LINENO test_failed $LINENO
"${ZGREP}" -Nq -l "01234567890" in "${bad1_lz}" in.lz && test_failed $LINENO
"${ZGREP}" -Nq -l "01234567890" in "${bad1_lz}" in.lz pin.tar > /dev/null ||
test_failed $LINENO
"${ZGREP}" -N "GNU" .
[ $? = 1 ] || test_failed $LINENO
"${ZGREP}" -N --bad-option 2> /dev/null "${ZGREP}" -N --bad-option 2> /dev/null
[ $? = 2 ] || test_failed $LINENO [ $? = 2 ] || test_failed $LINENO
"${ZGREP}" -N "GNU" -s nx_file "${ZGREP}" -N "GNU" -s nx_file
@ -300,9 +325,20 @@ done
"${ZTEST}" -N < in.gz || test_failed $LINENO "${ZTEST}" -N < in.gz || test_failed $LINENO
"${ZTEST}" -N < in.bz2 || test_failed $LINENO "${ZTEST}" -N < in.bz2 || test_failed $LINENO
"${ZTEST}" -N < in.lz || test_failed $LINENO "${ZTEST}" -N < in.lz || test_failed $LINENO
"${ZTEST}" -N - < in.lz || test_failed $LINENO
"${ZTEST}" -N - in.gz - < in.lz || test_failed $LINENO "${ZTEST}" -N - in.gz - < in.lz || test_failed $LINENO
"${ZTEST}" -N --lz='lzip -q' < in.lz || test_failed $LINENO "${ZTEST}" -N --lz='lzip -q' < in.lz || test_failed $LINENO
"${ZTEST}" -N -r . || test_failed $LINENO "${ZTEST}" -N -r . || test_failed $LINENO
"${ZTEST}" -N -r || test_failed $LINENO
"${ZTEST}" -N -R . || test_failed $LINENO
"${ZTEST}" -N -R || test_failed $LINENO
"${ZTEST}" -Nq in.gz "${bad0_lz}" in.bz2 "${bad1_lz}" in.lz
[ $? = 2 ] || test_failed $LINENO
lines=`"${ZTEST}" -N in.gz "${bad0_lz}" in.bz2 "${bad1_lz}" in.lz 2>&1 | wc -l`
[ "${lines}" -eq 2 ] || test_failed $LINENO
lines=`"${ZTEST}" -Nv in.gz "${bad0_lz}" in.bz2 "${bad1_lz}" in.lz 2>&1 | wc -l`
[ "${lines}" -eq 5 ] || test_failed $LINENO
"${ZTEST}" -Nq < in "${ZTEST}" -Nq < in
[ $? = 2 ] || test_failed $LINENO [ $? = 2 ] || test_failed $LINENO
dd if=in.lz bs=1000 count=1 2> /dev/null | "${ZTEST}" -N -q dd if=in.lz bs=1000 count=1 2> /dev/null | "${ZTEST}" -N -q
@ -336,58 +372,122 @@ cat in.gz > a.gz || framework_failure
cat in.lz in.lz > a.lz || framework_failure cat in.lz in.lz > a.lz || framework_failure
"${ZUPDATE}" -Nq -f a.bz2 a.gz "${ZUPDATE}" -Nq -f a.bz2 a.gz
{ [ $? = 1 ] && [ -e a.bz2 ] && [ -e a.gz ] && [ -e a.lz ] ; } || [ $? = 1 ] || test_failed $LINENO
test_failed $LINENO [ -e a.bz2 ] || test_failed $LINENO
[ -e a.gz ] || test_failed $LINENO
[ -e a.lz ] || test_failed $LINENO
rm -f a.lz || framework_failure rm -f a.lz || framework_failure
"${ZUPDATE}" -N a.bz2 "${ZUPDATE}" -N a.bz2 || test_failed $LINENO
{ [ $? = 0 ] && [ ! -e a.bz2 ] && [ -e a.gz ] && [ -e a.lz ] ; } || [ ! -e a.bz2 ] || test_failed $LINENO
test_failed $LINENO [ -e a.gz ] || test_failed $LINENO
[ -e a.lz ] || test_failed $LINENO
rm -f a.lz || framework_failure rm -f a.lz || framework_failure
"${ZUPDATE}" -N a.gz "${ZUPDATE}" -N a.gz || test_failed $LINENO
{ [ $? = 0 ] && [ ! -e a.bz2 ] && [ ! -e a.gz ] && [ -e a.lz ] ; } || [ ! -e a.bz2 ] || test_failed $LINENO
test_failed $LINENO [ ! -e a.gz ] || test_failed $LINENO
[ -e a.lz ] || test_failed $LINENO
rm -f a.lz || framework_failure rm -f a.lz || framework_failure
cat in.bz2 > a.bz2 || framework_failure cat in.bz2 > a.bz2 || framework_failure
cat in.gz > a.gz || framework_failure cat in.gz > a.gz || framework_failure
"${ZUPDATE}" -Nq a.bz2 a.gz "${ZUPDATE}" -Nq a.bz2 a.gz
{ [ $? = 1 ] && [ ! -e a.bz2 ] && [ -e a.gz ] && [ -e a.lz ] ; } || [ $? = 1 ] || test_failed $LINENO
test_failed $LINENO [ ! -e a.bz2 ] || test_failed $LINENO
[ -e a.gz ] || test_failed $LINENO
[ -e a.lz ] || test_failed $LINENO
rm -f a.lz || framework_failure rm -f a.lz || framework_failure
cat in.bz2 > a.bz2 || framework_failure cat in.bz2 > a.bz2 || framework_failure
cat in.gz > a.gz || framework_failure cat in.gz > a.gz || framework_failure
"${ZUPDATE}" -N -f -k a.bz2 a.gz "${ZUPDATE}" -N -f -k a.bz2 a.gz || test_failed $LINENO
{ [ $? = 0 ] && [ -e a.bz2 ] && [ -e a.gz ] && [ -e a.lz ] ; } || [ -e a.bz2 ] || test_failed $LINENO
test_failed $LINENO [ -e a.gz ] || test_failed $LINENO
[ -e a.lz ] || test_failed $LINENO
rm -f a.lz || framework_failure rm -f a.lz || framework_failure
cat in.bz2 > a.bz2 || framework_failure cat in.bz2 > a.bz2 || framework_failure
cat in.gz > a.gz || framework_failure cat in.gz > a.gz || framework_failure
"${ZUPDATE}" -N -f a.bz2 a.gz "${ZUPDATE}" -N -f a.bz2 a.gz || test_failed $LINENO
{ [ $? = 0 ] && [ ! -e a.bz2 ] && [ ! -e a.gz ] && [ ! -e a ] && [ ! -e a.bz2 ] || test_failed $LINENO
[ -e a.lz ] ; } || test_failed $LINENO [ ! -e a.gz ] || test_failed $LINENO
[ ! -e a ] || test_failed $LINENO
[ -e a.lz ] || test_failed $LINENO
rm -f a.lz || framework_failure rm -f a.lz || framework_failure
cat in.bz2 > a.bz2 || framework_failure cat in.bz2 > a.bz2 || framework_failure
"${ZUPDATE}" -N -1 -q a.bz2 cat "${bad0_gz}" > b.gz || framework_failure
{ [ $? = 0 ] && [ ! -e a.bz2 ] && [ -e a.lz ] ; } || test_failed $LINENO cat in.gz > c.gz || framework_failure
"${ZUPDATE}" -N -f a.bz2 b.gz c.gz 2> /dev/null && test_failed $LINENO
[ ! -e a.bz2 ] || test_failed $LINENO
[ -e b.gz ] || test_failed $LINENO
[ -e c.gz ] || test_failed $LINENO
[ ! -e a ] || test_failed $LINENO
[ ! -e b ] || test_failed $LINENO
[ ! -e c ] || test_failed $LINENO
[ -e a.lz ] || test_failed $LINENO
rm -f a.lz b.gz c.gz || framework_failure
cat in.bz2 > a.bz2 || framework_failure
"${ZUPDATE}" -N -1 -q a.bz2 || test_failed $LINENO
[ ! -e a.bz2 ] || test_failed $LINENO
[ -e a.lz ] || test_failed $LINENO
rm -f a.lz || framework_failure rm -f a.lz || framework_failure
mkdir tmp2 mkdir tmp2
mkdir tmp2/tmp3 mkdir tmp2/tmp3
cat in.bz2 > tmp2/tmp3/a.bz2 || framework_failure cat in.bz2 > tmp2/tmp3/a.bz2 || framework_failure
cat in.gz > tmp2/tmp3/a.gz || framework_failure cat in.gz > tmp2/tmp3/a.gz || framework_failure
"${ZUPDATE}" -N -r --format=gz tmp2 "${ZUPDATE}" -N -r --format=gz tmp2 || test_failed $LINENO
{ [ $? = 0 ] && [ -e tmp2/tmp3/a.bz2 ] && [ ! -e tmp2/tmp3/a.gz ] && [ -e tmp2/tmp3/a.bz2 ] || test_failed $LINENO
[ -e tmp2/tmp3/a.lz ] ; } || test_failed $LINENO [ ! -e tmp2/tmp3/a.gz ] || test_failed $LINENO
[ -e tmp2/tmp3/a.lz ] || test_failed $LINENO
rm -f tmp2/tmp3/a.lz || framework_failure rm -f tmp2/tmp3/a.lz || framework_failure
"${ZUPDATE}" -N -r --format=bz2 tmp2 "${ZUPDATE}" -N -r --format=bz2 tmp2 || test_failed $LINENO
{ [ $? = 0 ] && [ ! -e tmp2/tmp3/a.bz2 ] && [ ! -e tmp2/tmp3/a.gz ] && [ ! -e tmp2/tmp3/a.bz2 ] || test_failed $LINENO
[ -e tmp2/tmp3/a.lz ] ; } || test_failed $LINENO [ ! -e tmp2/tmp3/a.gz ] || test_failed $LINENO
[ -e tmp2/tmp3/a.lz ] || test_failed $LINENO
rm -f tmp2/tmp3/a.lz || framework_failure
cat in.bz2 > tmp2/tmp3/a.bz2 || framework_failure
cat in.gz > tmp2/tmp3/a.gz || framework_failure
cd tmp2 || framework_failure
"${ZUPDATE}" -N -r -k -f . || test_failed $LINENO
[ -e tmp3/a.bz2 ] || test_failed $LINENO
[ -e tmp3/a.gz ] || test_failed $LINENO
[ -e tmp3/a.lz ] || test_failed $LINENO
rm -f tmp3/a.lz || framework_failure
"${ZUPDATE}" -N -r -k -f || test_failed $LINENO
[ -e tmp3/a.bz2 ] || test_failed $LINENO
[ -e tmp3/a.gz ] || test_failed $LINENO
[ -e tmp3/a.lz ] || test_failed $LINENO
rm -f tmp3/a.lz || framework_failure
"${ZUPDATE}" -N -R -k -f . || test_failed $LINENO
[ -e tmp3/a.bz2 ] || test_failed $LINENO
[ -e tmp3/a.gz ] || test_failed $LINENO
[ -e tmp3/a.lz ] || test_failed $LINENO
rm -f tmp3/a.lz || framework_failure
"${ZUPDATE}" -N -R -k -f || test_failed $LINENO
[ -e tmp3/a.bz2 ] || test_failed $LINENO
[ -e tmp3/a.gz ] || test_failed $LINENO
[ -e tmp3/a.lz ] || test_failed $LINENO
rm -f tmp3/a.lz || framework_failure
"${ZUPDATE}" -N -r -f . || test_failed $LINENO
[ ! -e tmp3/a.bz2 ] || test_failed $LINENO
[ ! -e tmp3/a.gz ] || test_failed $LINENO
[ -e tmp3/a.lz ] || test_failed $LINENO
cd .. || framework_failure
rm -r tmp2 || framework_failure rm -r tmp2 || framework_failure
if ln -s '.' slink 2> /dev/null ; then
"${ZCAT}" -N -r slink > /dev/null || test_failed $LINENO
"${ZGREP}" -N -r "GNU" slink > /dev/null || test_failed $LINENO
"${ZTEST}" -N -r slink || test_failed $LINENO
"${ZUPDATE}" -N -r -f slink || test_failed $LINENO
else
printf "\nwarning: skipping link test: 'ln' does not work on your system."
fi
rm -f slink || framework_failure
echo echo
if [ ${fail} = 0 ] ; then if [ ${fail} = 0 ] ; then
echo "tests completed successfully." echo "tests completed successfully."

BIN
testsuite/test_bad_crc.lz Normal file

Binary file not shown.

68
testsuite/zcat_vs.dat Normal file
View file

@ -0,0 +1,68 @@
Worst case test file for zcat -vs.
First 4096 input bytes produce 4095 output bytes because of -s.
Next 4096 input bytes produce 16384 output bytes, accumulating a total
of 20479 bytes in the output buffer.
----------------------------------------------
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
...............................................................
€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€

BIN
testsuite/zero_bad_crc.gz Normal file

Binary file not shown.

BIN
testsuite/zero_bad_crc.lz Normal file

Binary file not shown.

147
zcat.cc
View file

@ -1,5 +1,5 @@
/* Zcat - decompress and concatenate files to standard output /* Zcat - decompress and concatenate files to standard output
Copyright (C) 2010-2018 Antonio Diaz Diaz. Copyright (C) 2010-2019 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -93,36 +93,39 @@ void show_help()
std::printf( "Zcat copies each given file to standard output. If any given file is\n" std::printf( "Zcat copies each given file to standard output. If any given file is\n"
"compressed, its decompressed content is used. If a given file does not\n" "compressed, its decompressed content is used. If a given file does not\n"
"exist, and its name does not end with one of the known extensions, zcat\n" "exist, and its name does not end with one of the known extensions, zcat\n"
"tries the compressed file names corresponding to the supported formats.\n" "tries the compressed file names corresponding to the formats supported.\n"
"\nIf no files are specified, or if a file is specified as '-', data are\n" "\nIf a file is specified as '-', data are read from standard input,\n"
"read from standard input, decompressed if needed, and sent to standard\n" "decompressed if needed, and sent to standard output. Data read from\n"
"output. Data read from standard input must be of the same type; all\n" "standard input must be of the same type; all uncompressed or all in the\n"
"uncompressed or all in the same compression format.\n" "same compression format.\n"
"\nThe supported formats are bzip2, gzip, lzip and xz.\n" "\nIf no files are specified, recursive searches examine the current\n"
"working directory, and nonrecursive searches read standard input.\n"
"\nThe formats supported are bzip2, gzip, lzip and xz.\n"
"\nUsage: zcat [options] [files]\n" "\nUsage: zcat [options] [files]\n"
"\nExit status is 0 if no errors occurred, non-zero otherwise.\n" "\nExit status is 0 if no errors occurred, non-zero otherwise.\n"
"\nOptions:\n" "\nOptions:\n"
" -h, --help display this help and exit\n" " -h, --help display this help and exit\n"
" -V, --version output version information and exit\n" " -V, --version output version information and exit\n"
" -A, --show-all equivalent to '-vET'\n" " -A, --show-all equivalent to '-vET'\n"
" -b, --number-nonblank number nonblank output lines\n" " -b, --number-nonblank number nonblank output lines\n"
" -e equivalent to '-vE'\n" " -e equivalent to '-vE'\n"
" -E, --show-ends display '$' at end of each line\n" " -E, --show-ends display '$' at end of each line\n"
" -M, --format=<list> process only the formats in <list>\n" " -M, --format=<list> process only the formats in <list>\n"
" -n, --number number all output lines\n" " -n, --number number all output lines\n"
" -N, --no-rcfile don't read runtime configuration file\n" " -N, --no-rcfile don't read runtime configuration file\n"
" -O, --force-format=<fmt> force given format (bz2, gz, lz, xz)\n" " -O, --force-format=<fmt> force given format (bz2, gz, lz, xz)\n"
" -q, --quiet suppress all messages\n" " -q, --quiet suppress all messages\n"
" -r, --recursive operate recursively on directories\n" " -r, --recursive operate recursively on directories\n"
" -s, --squeeze-blank never more than one single blank line\n" " -R, --dereference-recursive recursively follow symbolic links\n"
" -t equivalent to '-vT'\n" " -s, --squeeze-blank never more than one single blank line\n"
" -T, --show-tabs display TAB characters as '^I'\n" " -t equivalent to '-vT'\n"
" -v, --show-nonprinting use '^' and 'M-' notation, except for LF and TAB\n" " -T, --show-tabs display TAB characters as '^I'\n"
" --verbose verbose mode (show error messages)\n" " -v, --show-nonprinting use '^' and 'M-' notation, except for LF and TAB\n"
" --bz2=<command> set compressor and options for bzip2 format\n" " --verbose verbose mode (show error messages)\n"
" --gz=<command> set compressor and options for gzip format\n" " --bz2=<command> set compressor and options for bzip2 format\n"
" --lz=<command> set compressor and options for lzip format\n" " --gz=<command> set compressor and options for gzip format\n"
" --xz=<command> set compressor and options for xz format\n" ); " --lz=<command> set compressor and options for lzip format\n"
" --xz=<command> set compressor and options for xz format\n" );
show_help_addr(); show_help_addr();
} }
@ -153,7 +156,7 @@ int do_cat( const int infd, const int buffer_size,
rd = readblock( infd, inbuf, buffer_size ); rd = readblock( infd, inbuf, buffer_size );
if( rd != buffer_size && errno ) if( rd != buffer_size && errno )
{ {
show_error2( "Error reading file", input_filename.c_str() ); show_file_error( input_filename.c_str(), "Read error", errno );
return 1; return 1;
} }
if( rd == 0 ) if( rd == 0 )
@ -226,11 +229,13 @@ int do_cat( const int infd, const int buffer_size,
int cat( int infd, const int format_index, const std::string & input_filename, int cat( int infd, const int format_index, const std::string & input_filename,
const Cat_options & cat_options ) const Cat_options & cat_options )
{ {
enum { buffer_size = 4096 }; enum { buffer_size = 4096, outbuf_size = (5 * buffer_size) + 256 + 1 };
// buffer with space for sentinel newline at the end // buffer with space for sentinel newline at the end
uint8_t * const inbuf = new uint8_t[buffer_size+1]; uint8_t * const inbuf = new uint8_t[buffer_size+1];
// buffer with space for character quoting and 255-digit line number // buffer with space for character quoting, 255-digit line number,
uint8_t * const outbuf = new uint8_t[(4*buffer_size)+256]; // worst case flushing respect to inbuf, and a canary byte.
uint8_t * const outbuf = new uint8_t[outbuf_size];
outbuf[outbuf_size-1] = 0;
int retval = 0; int retval = 0;
Children children; Children children;
if( !set_data_feeder( input_filename, &infd, children, format_index ) ) if( !set_data_feeder( input_filename, &infd, children, format_index ) )
@ -243,6 +248,8 @@ int cat( int infd, const int format_index, const std::string & input_filename,
if( retval == 0 && close( infd ) != 0 ) if( retval == 0 && close( infd ) != 0 )
{ show_close_error(); retval = 1; } { show_close_error(); retval = 1; }
if( outbuf[outbuf_size-1] != 0 )
internal_error( "buffer overflow." );
delete[] outbuf; delete[] inbuf; delete[] outbuf; delete[] inbuf;
return retval; return retval;
} }
@ -253,10 +260,8 @@ int cat( int infd, const int format_index, const std::string & input_filename,
int main( const int argc, const char * const argv[] ) int main( const int argc, const char * const argv[] )
{ {
enum { verbose_opt = 256, bz2_opt, gz_opt, lz_opt, xz_opt }; enum { verbose_opt = 256, bz2_opt, gz_opt, lz_opt, xz_opt };
int infd = -1;
int format_index = -1; int format_index = -1;
bool recursive = false; int recursive = 0; // 1 = '-r', 2 = '-R'
std::string input_filename;
std::list< std::string > filenames; std::list< std::string > filenames;
Cat_options cat_options; Cat_options cat_options;
invocation_name = argv[0]; invocation_name = argv[0];
@ -264,33 +269,34 @@ int main( const int argc, const char * const argv[] )
const Arg_parser::Option options[] = const Arg_parser::Option options[] =
{ {
{ 'A', "show-all", Arg_parser::no }, // cat { 'A', "show-all", Arg_parser::no }, // cat
{ 'b', "number-nonblank", Arg_parser::no }, // cat { 'b', "number-nonblank", Arg_parser::no }, // cat
{ 'c', "stdout", Arg_parser::no }, // gzip { 'c', "stdout", Arg_parser::no }, // gzip
{ 'd', "decompress", Arg_parser::no }, // gzip { 'd', "decompress", Arg_parser::no }, // gzip
{ 'e', 0, Arg_parser::no }, // cat { 'e', 0, Arg_parser::no }, // cat
{ 'E', "show-ends", Arg_parser::no }, // cat { 'E', "show-ends", Arg_parser::no }, // cat
{ 'f', "force", Arg_parser::no }, // gzip { 'f', "force", Arg_parser::no }, // gzip
{ 'h', "help", Arg_parser::no }, { 'h', "help", Arg_parser::no },
{ 'l', "list", Arg_parser::no }, // gzip { 'l', "list", Arg_parser::no }, // gzip
{ 'L', "license", Arg_parser::no }, // gzip { 'L', "license", Arg_parser::no }, // gzip
{ 'M', "format", Arg_parser::yes }, { 'M', "format", Arg_parser::yes },
{ 'n', "number", Arg_parser::no }, // cat { 'n', "number", Arg_parser::no }, // cat
{ 'N', "no-rcfile", Arg_parser::no }, { 'N', "no-rcfile", Arg_parser::no },
{ 'O', "force-format", Arg_parser::yes }, { 'O', "force-format", Arg_parser::yes },
{ 'q', "quiet", Arg_parser::no }, { 'q', "quiet", Arg_parser::no },
{ 'r', "recursive", Arg_parser::no }, { 'r', "recursive", Arg_parser::no },
{ 's', "squeeze-blank", Arg_parser::no }, // cat { 'R', "dereference-recursive", Arg_parser::no },
{ 't', 0, Arg_parser::no }, // cat { 's', "squeeze-blank", Arg_parser::no }, // cat
{ 'T', "show-tabs", Arg_parser::no }, // cat { 't', 0, Arg_parser::no }, // cat
{ 'v', "show-nonprinting", Arg_parser::no }, // cat { 'T', "show-tabs", Arg_parser::no }, // cat
{ 'V', "version", Arg_parser::no }, { 'v', "show-nonprinting", Arg_parser::no }, // cat
{ verbose_opt, "verbose", Arg_parser::no }, { 'V', "version", Arg_parser::no },
{ bz2_opt, "bz2", Arg_parser::yes }, { verbose_opt, "verbose", Arg_parser::no },
{ gz_opt, "gz", Arg_parser::yes }, { bz2_opt, "bz2", Arg_parser::yes },
{ lz_opt, "lz", Arg_parser::yes }, { gz_opt, "gz", Arg_parser::yes },
{ xz_opt, "xz", Arg_parser::yes }, { lz_opt, "lz", Arg_parser::yes },
{ 0 , 0, Arg_parser::no } }; { xz_opt, "xz", Arg_parser::yes },
{ 0 , 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options ); const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option if( parser.error().size() ) // bad option
@ -312,7 +318,7 @@ int main( const int argc, const char * const argv[] )
case 'b': cat_options.number_lines = 1; break; case 'b': cat_options.number_lines = 1; break;
case 'c': break; case 'c': break;
case 'd': break; case 'd': break;
case 'e': cat_options.show_nonprinting = true; // fall through case 'e': cat_options.show_nonprinting = true; // fall through
case 'E': cat_options.show_ends = true; break; case 'E': cat_options.show_ends = true; break;
case 'f': break; case 'f': break;
case 'h': show_help(); return 0; case 'h': show_help(); return 0;
@ -324,9 +330,10 @@ int main( const int argc, const char * const argv[] )
case 'N': break; case 'N': break;
case 'O': format_index = parse_format_type( arg ); break; case 'O': format_index = parse_format_type( arg ); break;
case 'q': verbosity = -1; break; case 'q': verbosity = -1; break;
case 'r': recursive = true; break; case 'r': recursive = 1; break;
case 'R': recursive = 2; break;
case 's': cat_options.squeeze_blank = true; break; case 's': cat_options.squeeze_blank = true; break;
case 't': cat_options.show_nonprinting = true; // fall through case 't': cat_options.show_nonprinting = true; // fall through
case 'T': cat_options.show_tabs = true; break; case 'T': cat_options.show_tabs = true; break;
case 'v': cat_options.show_nonprinting = true; break; case 'v': cat_options.show_nonprinting = true; break;
case 'V': show_version(); return 0; case 'V': show_version(); return 0;
@ -347,13 +354,15 @@ int main( const int argc, const char * const argv[] )
for( ; argind < parser.arguments(); ++argind ) for( ; argind < parser.arguments(); ++argind )
filenames.push_back( parser.argument( argind ) ); filenames.push_back( parser.argument( argind ) );
if( filenames.empty() ) filenames.push_back( "-" ); if( filenames.empty() ) filenames.push_back( recursive ? "." : "-" );
std::string input_filename;
int retval = 0; int retval = 0;
bool error = false; bool error = false;
bool stdin_used = false; bool stdin_used = false;
while( next_filename( filenames, input_filename, error, recursive ) ) while( next_filename( filenames, input_filename, error, recursive ) )
{ {
int infd;
if( input_filename.empty() ) if( input_filename.empty() )
{ {
if( stdin_used ) continue; else stdin_used = true; if( stdin_used ) continue; else stdin_used = true;
@ -368,12 +377,12 @@ int main( const int argc, const char * const argv[] )
const int tmp = cat( infd, format_index, input_filename, cat_options ); const int tmp = cat( infd, format_index, input_filename, cat_options );
if( tmp > retval ) retval = tmp; if( tmp > retval ) retval = tmp;
if( input_filename.size() ) { close( infd ); infd = -1; } if( input_filename.size() ) close( infd );
} }
if( std::fclose( stdout ) != 0 ) if( std::fclose( stdout ) != 0 )
{ {
show_error( "Can't close stdout", errno ); show_error( "Error closing stdout", errno );
error = true; error = true;
} }
if( error && retval == 0 ) retval = 1; if( error && retval == 0 ) retval = 1;

View file

@ -1,5 +1,5 @@
/* Common code for zcat and zgrep /* Common code for zcat and zgrep
Copyright (C) 2010-2018 Antonio Diaz Diaz. Copyright (C) 2010-2019 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -39,6 +39,7 @@ int open_instream( std::string & input_filename, const bool search,
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 ) if( infd < 0 )
{ {
const int saved_errno = errno;
if( search && simple_extension_index( input_filename ) < 0 ) if( search && simple_extension_index( input_filename ) < 0 )
{ {
for( int i = 0; i < num_formats; ++i ) for( int i = 0; i < num_formats; ++i )
@ -51,7 +52,8 @@ int open_instream( std::string & input_filename, const bool search,
} }
} }
if( infd < 0 && !no_messages ) if( infd < 0 && !no_messages )
show_error2( "Can't open input file", input_filename.c_str() ); show_file_error( input_filename.c_str(), "Can't open input file",
saved_errno );
} }
return infd; return infd;
} }

15
zcmp.cc
View file

@ -1,5 +1,5 @@
/* Zcmp - decompress and compare two files byte by byte /* Zcmp - decompress and compare two files byte by byte
Copyright (C) 2010-2018 Antonio Diaz Diaz. Copyright (C) 2010-2019 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -55,7 +55,7 @@ void show_help()
"are numbered starting with 1. If any given file is compressed, its\n" "are numbered starting with 1. If any given file is compressed, its\n"
"decompressed content is used. Compressed files are decompressed on the\n" "decompressed content is used. Compressed files are decompressed on the\n"
"fly; no temporary files are created.\n" "fly; no temporary files are created.\n"
"\nThe supported formats are bzip2, gzip, lzip and xz.\n" "\nThe formats supported are bzip2, gzip, lzip and xz.\n"
"\nUsage: zcmp [options] file1 [file2]\n" "\nUsage: zcmp [options] file1 [file2]\n"
"\nZcmp compares file1 to file2. If file2 is omitted zcmp tries the\n" "\nZcmp compares file1 to file2. If file2 is omitted zcmp tries the\n"
"following:\n" "following:\n"
@ -243,7 +243,7 @@ int cmp( const long long max_size, const int infd[2],
rd[i] = readblock( infd[i], buffer[i], size ); rd[i] = readblock( infd[i], buffer[i], size );
if( rd[i] != size && errno ) if( rd[i] != size && errno )
{ {
show_error2( "Error reading file", filenames[i].c_str() ); show_file_error( filenames[i].c_str(), "Read error", errno );
return 2; return 2;
} }
} }
@ -274,6 +274,7 @@ int cmp( const long long max_size, const int infd[2],
filenames[0].c_str(), filenames[1].c_str(), filenames[0].c_str(), filenames[1].c_str(),
byte_number, line_number, c0, buf0, c1, buf1 ); byte_number, line_number, c0, buf0, c1, buf1 );
} }
std::fflush( stdout );
return 1; return 1;
} }
else // verbosity > 0 ; show all differences else // verbosity > 0 ; show all differences
@ -296,6 +297,7 @@ int cmp( const long long max_size, const int infd[2],
} }
} }
} }
std::fflush( stdout );
} }
} }
@ -435,7 +437,8 @@ int main( const int argc, const char * const argv[] )
for( int i = 0; i < 2; ++i ) for( int i = 0; i < 2; ++i )
if( !skip_ignore_initial( ignore_initial[i], infd[i] ) ) if( !skip_ignore_initial( ignore_initial[i], infd[i] ) )
{ {
show_error2( "Can't skip initial bytes from file", filenames[i].c_str() ); show_file_error( filenames[i].c_str(),
"Read error skipping initial bytes", errno );
return 2; return 2;
} }
@ -450,13 +453,13 @@ int main( const int argc, const char * const argv[] )
{ show_close_error(); retval = 2; } { show_close_error(); retval = 2; }
if( filenames[i] != "-" && close( old_infd[i] ) != 0 ) if( filenames[i] != "-" && close( old_infd[i] ) != 0 )
{ {
show_error2( "Can't close input file", filenames[i].c_str() ); show_file_error( filenames[i].c_str(), "Error closing input file", errno );
retval = 2; retval = 2;
} }
} }
if( std::fclose( stdout ) != 0 ) if( std::fclose( stdout ) != 0 )
{ {
show_error( "Can't close stdout", errno ); show_error( "Error closing stdout", errno );
retval = 2; retval = 2;
} }

View file

@ -1,5 +1,5 @@
/* Common code for zcmp and zdiff /* Common code for zcmp and zdiff
Copyright (C) 2010-2018 Antonio Diaz Diaz. Copyright (C) 2010-2019 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -24,7 +24,7 @@ int open_instream( const std::string & input_filename )
{ {
const int infd = open( input_filename.c_str(), O_RDONLY | O_BINARY ); const int infd = open( input_filename.c_str(), O_RDONLY | O_BINARY );
if( infd < 0 ) if( infd < 0 )
show_error2( "Can't open input file", input_filename.c_str() ); show_file_error( input_filename.c_str(), "Can't open input file", errno );
return infd; return infd;
} }

View file

@ -1,5 +1,5 @@
/* Zdiff - decompress and compare two files line by line /* Zdiff - decompress and compare two files line by line
Copyright (C) 2010-2018 Antonio Diaz Diaz. Copyright (C) 2010-2019 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -53,7 +53,7 @@ void show_help()
"compressed, its decompressed content is used. Zdiff is a front end to\n" "compressed, its decompressed content is used. Zdiff is a front end to\n"
"the diff program and has the limitation that messages from diff refer to\n" "the diff program and has the limitation that messages from diff refer to\n"
"temporary filenames instead of those specified.\n" "temporary filenames instead of those specified.\n"
"\nThe supported formats are bzip2, gzip, lzip and xz.\n" "\nThe formats supported are bzip2, gzip, lzip and xz.\n"
"\nUsage: zdiff [options] file1 [file2]\n" "\nUsage: zdiff [options] file1 [file2]\n"
"\nZdiff compares file1 to file2. If file2 is omitted zdiff tries the\n" "\nZdiff compares file1 to file2. If file2 is omitted zdiff tries the\n"
"following:\n" "following:\n"
@ -141,7 +141,7 @@ bool set_fifonames( const std::string filenames[2] )
if( mkfifo( fifonames[i].c_str(), S_IRUSR | S_IWUSR ) == 0 ) if( mkfifo( fifonames[i].c_str(), S_IRUSR | S_IWUSR ) == 0 )
continue; continue;
} }
show_error2( "Can't create FIFO", fifonames[i].c_str() ); show_file_error( fifonames[i].c_str(), "Can't create FIFO", errno );
return false; return false;
} }
return true; return true;
@ -419,7 +419,7 @@ int main( const int argc, const char * const argv[] )
for( int i = 0; i < 2; ++i ) for( int i = 0; i < 2; ++i )
if( filenames[i] != "-" && close( infd[i] ) != 0 ) if( filenames[i] != "-" && close( infd[i] ) != 0 )
{ {
show_error2( "Can't close input file", filenames[i].c_str() ); show_file_error( filenames[i].c_str(), "Error closing input file", errno );
retval = 2; retval = 2;
} }

184
zgrep.cc
View file

@ -1,5 +1,5 @@
/* Zgrep - search compressed files for a regular expression /* Zgrep - search compressed files for a regular expression
Copyright (C) 2010-2018 Antonio Diaz Diaz. Copyright (C) 2010-2019 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -52,51 +52,54 @@ void show_help()
"file is compressed, its decompressed content is used. If a given file\n" "file is compressed, its decompressed content is used. If a given file\n"
"does not exist, and its name does not end with one of the known\n" "does not exist, and its name does not end with one of the known\n"
"extensions, zgrep tries the compressed file names corresponding to the\n" "extensions, zgrep tries the compressed file names corresponding to the\n"
"supported formats.\n" "formats supported.\n"
"\nIf no files are specified, or if a file is specified as '-', data are\n" "\nIf a file is specified as '-', data are read from standard input,\n"
"read from standard input, decompressed if needed, and fed to grep. Data\n" "decompressed if needed, and fed to grep. Data read from standard input\n"
"read from standard input must be of the same type; all uncompressed or\n" "must be of the same type; all uncompressed or all in the same\n"
"all in the same compression format.\n" "compression format.\n"
"\nThe supported formats are bzip2, gzip, lzip and xz.\n" "\nIf no files are specified, recursive searches examine the current\n"
"working directory, and nonrecursive searches read standard input.\n"
"\nThe formats supported are bzip2, gzip, lzip and xz.\n"
"\nUsage: zgrep [options] <pattern> [files]\n" "\nUsage: zgrep [options] <pattern> [files]\n"
"\nExit status is 0 if match, 1 if no match, 2 if trouble.\n" "\nExit status is 0 if match, 1 if no match, 2 if trouble.\n"
"\nOptions:\n" "\nOptions:\n"
" --help display this help and exit\n" " --help display this help and exit\n"
" -V, --version output version information and exit\n" " -V, --version output version information and exit\n"
" -a, --text treat all files as text\n" " -a, --text treat all files as text\n"
" -A, --after-context=<n> print <n> lines of trailing context\n" " -A, --after-context=<n> print <n> lines of trailing context\n"
" -b, --byte-offset print the byte offset of each line\n" " -b, --byte-offset print the byte offset of each line\n"
" -B, --before-context=<n> print <n> lines of leading context\n" " -B, --before-context=<n> print <n> lines of leading context\n"
" -c, --count only print a count of matching lines per file\n" " -c, --count only print a count of matching lines per file\n"
" -C, --context=<n> print <n> lines of output context\n" " -C, --context=<n> print <n> lines of output context\n"
" --color[=<when>] show matched strings in color\n" " --color[=<when>] show matched strings in color\n"
" -e, --regexp=<pattern> use <pattern> as the pattern to match\n" " -e, --regexp=<pattern> use <pattern> as the pattern to match\n"
" -E, --extended-regexp <pattern> is an extended regular expression\n" " -E, --extended-regexp <pattern> is an extended regular expression\n"
" -f, --file=<file> obtain patterns from <file>\n" " -f, --file=<file> obtain patterns from <file>\n"
" -F, --fixed-strings <pattern> is a set of newline-separated strings\n" " -F, --fixed-strings <pattern> is a set of newline-separated strings\n"
" -h, --no-filename suppress the prefixing filename on output\n" " -h, --no-filename suppress the prefixing filename on output\n"
" -H, --with-filename print the filename for each match\n" " -H, --with-filename print the filename for each match\n"
" -i, --ignore-case ignore case distinctions\n" " -i, --ignore-case ignore case distinctions\n"
" -I ignore binary files\n" " -I ignore binary files\n"
" -l, --files-with-matches only print names of files containing matches\n" " -l, --files-with-matches only print names of files containing matches\n"
" -L, --files-without-match only print names of files containing no matches\n" " -L, --files-without-match only print names of files containing no matches\n"
" -m, --max-count=<n> stop after <n> matches\n" " -m, --max-count=<n> stop after <n> matches\n"
" -M, --format=<list> process only the formats in <list>\n" " -M, --format=<list> process only the formats in <list>\n"
" -n, --line-number print the line number of each line\n" " -n, --line-number print the line number of each line\n"
" -N, --no-rcfile don't read runtime configuration file\n" " -N, --no-rcfile don't read runtime configuration file\n"
" -o, --only-matching show only the part of a line matching <pattern>\n" " -o, --only-matching show only the part of a line matching <pattern>\n"
" -O, --force-format=<fmt> force given format (bz2, gz, lz, xz)\n" " -O, --force-format=<fmt> force given format (bz2, gz, lz, xz)\n"
" -q, --quiet suppress all messages\n" " -q, --quiet suppress all messages\n"
" -r, --recursive operate recursively on directories\n" " -r, --recursive operate recursively on directories\n"
" -s, --no-messages suppress error messages\n" " -R, --dereference-recursive recursively follow symbolic links\n"
" -v, --invert-match select non-matching lines\n" " -s, --no-messages suppress error messages\n"
" --verbose verbose mode (show error messages)\n" " -v, --invert-match select non-matching lines\n"
" -w, --word-regexp match only whole words\n" " --verbose verbose mode (show error messages)\n"
" -x, --line-regexp match only whole lines\n" " -w, --word-regexp match only whole words\n"
" --bz2=<command> set compressor and options for bzip2 format\n" " -x, --line-regexp match only whole lines\n"
" --gz=<command> set compressor and options for gzip format\n" " --bz2=<command> set compressor and options for bzip2 format\n"
" --lz=<command> set compressor and options for lzip format\n" " --gz=<command> set compressor and options for gzip format\n"
" --xz=<command> set compressor and options for xz format\n" " --lz=<command> set compressor and options for lzip format\n"
" --xz=<command> set compressor and options for xz format\n"
"Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n" "Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n"
"Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" ); "Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" );
show_help_addr(); show_help_addr();
@ -187,7 +190,8 @@ int zgrep_file( int infd, const int format_index,
putchar( buffer[i] ); putchar( buffer[i] );
} }
else if( std::fwrite( buffer, 1, size, stdout ) != (unsigned)size ) else if( std::fwrite( buffer, 1, size, stdout ) != (unsigned)size )
{ show_error( "Write error", errno ); return 2; } { std::fflush( stdout ); show_error( "Write error", errno ); return 2; }
std::fflush( stdout );
} }
if( size < buffer_size ) break; if( size < buffer_size ) break;
} }
@ -197,7 +201,7 @@ int zgrep_file( int infd, const int format_index,
if( !good_status( children, retval == 1 ) ) retval = 2; if( !good_status( children, retval == 1 ) ) retval = 2;
if( list_mode && (retval == 0) == (list_mode == 1) ) if( list_mode && (retval == 0) == (list_mode == 1) )
std::printf( "%s\n", input_filename.c_str() ); { std::printf( "%s\n", input_filename.c_str() ); std::fflush( stdout ); }
if( close( infd ) != 0 ) if( close( infd ) != 0 )
{ show_close_error(); return 2; } { show_close_error(); return 2; }
if( close( fda[0] ) != 0 ) if( close( fda[0] ) != 0 )
@ -213,12 +217,10 @@ int main( const int argc, const char * const argv[] )
enum { help_opt = 256, verbose_opt, color_opt, enum { help_opt = 256, verbose_opt, color_opt,
bz2_opt, gz_opt, lz_opt, xz_opt }; bz2_opt, gz_opt, lz_opt, xz_opt };
int format_index = -1; int format_index = -1;
int infd = -1;
int list_mode = 0; // 1 = list matches, -1 = list non matches int list_mode = 0; // 1 = list matches, -1 = list non matches
int recursive = 0; // 1 = '-r', 2 = '-R'
int show_name = -1; // tri-state bool int show_name = -1; // tri-state bool
bool no_messages = false; bool no_messages = false;
bool recursive = false;
std::string input_filename;
std::list< std::string > filenames; std::list< std::string > filenames;
std::vector< const char * > grep_args; // args to grep, maybe empty std::vector< const char * > grep_args; // args to grep, maybe empty
std::string color_option; // needed because of optional arg std::string color_option; // needed because of optional arg
@ -227,43 +229,44 @@ int main( const int argc, const char * const argv[] )
const Arg_parser::Option options[] = const Arg_parser::Option options[] =
{ {
{ 'a', "text", Arg_parser::no }, // grep GNU { 'a', "text", Arg_parser::no }, // grep GNU
{ 'A', "after-context", Arg_parser::yes }, // grep GNU { 'A', "after-context", Arg_parser::yes }, // grep GNU
{ 'b', "byte-offset", Arg_parser::no }, // grep GNU { 'b', "byte-offset", Arg_parser::no }, // grep GNU
{ 'B', "before-context", Arg_parser::yes }, // grep GNU { 'B', "before-context", Arg_parser::yes }, // grep GNU
{ 'c', "count", Arg_parser::no }, // grep { 'c', "count", Arg_parser::no }, // grep
{ 'C', "context", Arg_parser::yes }, // grep GNU { 'C', "context", Arg_parser::yes }, // grep GNU
{ 'e', "regexp", Arg_parser::yes }, // grep { 'e', "regexp", Arg_parser::yes }, // grep
{ 'E', "extended-regexp", Arg_parser::no }, // grep { 'E', "extended-regexp", Arg_parser::no }, // grep
{ 'f', "file ", Arg_parser::yes }, // grep { 'f', "file ", Arg_parser::yes }, // grep
{ 'F', "fixed-strings", Arg_parser::no }, // grep { 'F', "fixed-strings", Arg_parser::no }, // grep
{ 'h', "no-filename", Arg_parser::no }, // grep GNU { 'h', "no-filename", Arg_parser::no }, // grep GNU
{ 'H', "with-filename", Arg_parser::no }, // grep GNU { 'H', "with-filename", Arg_parser::no }, // grep GNU
{ 'i', "ignore-case", Arg_parser::no }, // grep { 'i', "ignore-case", Arg_parser::no }, // grep
{ 'I', 0, Arg_parser::no }, // grep GNU { 'I', 0, Arg_parser::no }, // grep GNU
{ 'l', "files-with-matches", Arg_parser::no }, // grep { 'l', "files-with-matches", Arg_parser::no }, // grep
{ 'L', "files-without-match", Arg_parser::no }, // grep GNU { 'L', "files-without-match", Arg_parser::no }, // grep GNU
{ 'm', "max-count", Arg_parser::yes }, // grep GNU { 'm', "max-count", Arg_parser::yes }, // grep GNU
{ 'M', "format", Arg_parser::yes }, { 'M', "format", Arg_parser::yes },
{ 'n', "line-number", Arg_parser::no }, // grep { 'n', "line-number", Arg_parser::no }, // grep
{ 'N', "no-rcfile", Arg_parser::no }, { 'N', "no-rcfile", Arg_parser::no },
{ 'o', "only-matching", Arg_parser::no }, // grep { 'o', "only-matching", Arg_parser::no }, // grep
{ 'O', "force-format", Arg_parser::yes }, { 'O', "force-format", Arg_parser::yes },
{ 'q', "quiet", Arg_parser::no }, { 'q', "quiet", Arg_parser::no },
{ 'r', "recursive", Arg_parser::no }, { 'r', "recursive", Arg_parser::no },
{ 's', "no-messages", Arg_parser::no }, // grep { 'R', "dereference-recursive", Arg_parser::no },
{ 'v', "invert-match", Arg_parser::no }, // grep { 's', "no-messages", Arg_parser::no }, // grep
{ 'V', "version", Arg_parser::no }, { 'v', "invert-match", Arg_parser::no }, // grep
{ 'w', "word-regexp", Arg_parser::no }, // grep GNU { 'V', "version", Arg_parser::no },
{ 'x', "line-regexp", Arg_parser::no }, // grep { 'w', "word-regexp", Arg_parser::no }, // grep GNU
{ help_opt, "help", Arg_parser::no }, { 'x', "line-regexp", Arg_parser::no }, // grep
{ verbose_opt, "verbose", Arg_parser::no }, { help_opt, "help", Arg_parser::no },
{ color_opt, "color", Arg_parser::maybe }, { verbose_opt, "verbose", Arg_parser::no },
{ bz2_opt, "bz2", Arg_parser::yes }, { color_opt, "color", Arg_parser::maybe },
{ gz_opt, "gz", Arg_parser::yes }, { bz2_opt, "bz2", Arg_parser::yes },
{ lz_opt, "lz", Arg_parser::yes }, { gz_opt, "gz", Arg_parser::yes },
{ xz_opt, "xz", Arg_parser::yes }, { lz_opt, "lz", Arg_parser::yes },
{ 0 , 0, Arg_parser::no } }; { xz_opt, "xz", Arg_parser::yes },
{ 0 , 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options ); const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option if( parser.error().size() ) // bad option
@ -309,7 +312,8 @@ int main( const int argc, const char * const argv[] )
case 'o': grep_args.push_back( "-o" ); break; case 'o': grep_args.push_back( "-o" ); break;
case 'O': format_index = parse_format_type( arg ); break; case 'O': format_index = parse_format_type( arg ); break;
case 'q': grep_args.push_back( "-q" ); verbosity = -1; break; case 'q': grep_args.push_back( "-q" ); verbosity = -1; break;
case 'r': recursive = true; break; case 'r': recursive = 1; break;
case 'R': recursive = 2; break;
case 's': grep_args.push_back( "-s" ); no_messages = true; break; case 's': grep_args.push_back( "-s" ); no_messages = true; break;
case 'v': grep_args.push_back( "-v" ); break; case 'v': grep_args.push_back( "-v" ); break;
case 'V': show_version(); return 0; case 'V': show_version(); return 0;
@ -349,16 +353,18 @@ int main( const int argc, const char * const argv[] )
for( ; argind < parser.arguments(); ++argind ) for( ; argind < parser.arguments(); ++argind )
filenames.push_back( parser.argument( argind ) ); filenames.push_back( parser.argument( argind ) );
if( filenames.empty() ) filenames.push_back( "-" ); if( filenames.empty() ) filenames.push_back( recursive ? "." : "-" );
if( show_name < 0 ) show_name = ( filenames.size() != 1 || recursive ); if( show_name < 0 ) show_name = ( filenames.size() != 1 || recursive );
std::string input_filename;
int retval = 1; int retval = 1;
bool error = false; bool error = false;
bool stdin_used = false; bool stdin_used = false;
while( next_filename( filenames, input_filename, error, recursive, while( next_filename( filenames, input_filename, error, recursive,
false, no_messages ) ) false, no_messages ) )
{ {
int infd;
if( input_filename.empty() ) if( input_filename.empty() )
{ {
if( stdin_used ) continue; else stdin_used = true; if( stdin_used ) continue; else stdin_used = true;
@ -377,13 +383,13 @@ int main( const int argc, const char * const argv[] )
list_mode, show_name ); list_mode, show_name );
if( tmp == 0 || ( tmp == 2 && retval == 1 ) ) retval = tmp; if( tmp == 0 || ( tmp == 2 && retval == 1 ) ) retval = tmp;
if( input_filename.size() ) { close( infd ); infd = -1; } if( input_filename.size() ) close( infd );
if( retval == 0 && verbosity < 0 ) break; if( retval == 0 && verbosity < 0 ) break;
} }
if( std::fclose( stdout ) != 0 ) if( std::fclose( stdout ) != 0 )
{ {
show_error( "Can't close stdout", errno ); show_error( "Error closing stdout", errno );
error = true; error = true;
} }
if( error && ( retval != 0 || verbosity >= 0 ) ) retval = 2; if( error && ( retval != 0 || verbosity >= 0 ) ) retval = 2;

View file

@ -1,5 +1,5 @@
/* Ztest - verify the integrity of compressed files /* Ztest - verify the integrity of compressed files
Copyright (C) 2010-2018 Antonio Diaz Diaz. Copyright (C) 2010-2019 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -51,11 +51,12 @@ namespace {
void show_help() void show_help()
{ {
std::printf( "Ztest verifies the integrity of the specified compressed files.\n" std::printf( "Ztest verifies the integrity of the specified compressed files.\n"
"Uncompressed files are ignored. If no files are specified, or if a file\n" "Uncompressed files are ignored. If a file is specified as '-', the\n"
"is specified as '-', the integrity of compressed data read from standard\n" "integrity of compressed data read from standard input is verified. Data\n"
"input is verified. Data read from standard input must be all in the same\n" "read from standard input must be all in the same compression format.\n"
"compression format.\n" "\nIf no files are specified, recursive searches examine the current\n"
"\nThe supported formats are bzip2, gzip, lzip and xz.\n" "working directory, and nonrecursive searches read standard input.\n"
"\nThe formats supported are bzip2, gzip, lzip and xz.\n"
"\nNote that error detection in the xz format is broken. First, some xz\n" "\nNote that error detection in the xz format is broken. First, some xz\n"
"files lack integrity information. Second, not all xz decompressors can\n" "files lack integrity information. Second, not all xz decompressors can\n"
"verify the integrity of all xz files. Third, section 2.1.1.2 'Stream\n" "verify the integrity of all xz files. Third, section 2.1.1.2 'Stream\n"
@ -67,18 +68,19 @@ void show_help()
"problems (file not found, invalid flags, I/O errors, etc), 2 if any\n" "problems (file not found, invalid flags, I/O errors, etc), 2 if any\n"
"compressed file is corrupt or invalid.\n" "compressed file is corrupt or invalid.\n"
"\nOptions:\n" "\nOptions:\n"
" -h, --help display this help and exit\n" " -h, --help display this help and exit\n"
" -V, --version output version information and exit\n" " -V, --version output version information and exit\n"
" -M, --format=<list> process only the formats in <list>\n" " -M, --format=<list> process only the formats in <list>\n"
" -N, --no-rcfile don't read runtime configuration file\n" " -N, --no-rcfile don't read runtime configuration file\n"
" -O, --force-format=<fmt> force given format (bz2, gz, lz, xz)\n" " -O, --force-format=<fmt> force given format (bz2, gz, lz, xz)\n"
" -q, --quiet suppress all messages\n" " -q, --quiet suppress all messages\n"
" -r, --recursive operate recursively on directories\n" " -r, --recursive operate recursively on directories\n"
" -v, --verbose be verbose (a 2nd -v gives more)\n" " -R, --dereference-recursive recursively follow symbolic links\n"
" --bz2=<command> set compressor and options for bzip2 format\n" " -v, --verbose be verbose (a 2nd -v gives more)\n"
" --gz=<command> set compressor and options for gzip format\n" " --bz2=<command> set compressor and options for bzip2 format\n"
" --lz=<command> set compressor and options for lzip format\n" " --gz=<command> set compressor and options for gzip format\n"
" --xz=<command> set compressor and options for xz format\n" ); " --lz=<command> set compressor and options for lzip format\n"
" --xz=<command> set compressor and options for xz format\n" );
show_help_addr(); show_help_addr();
} }
@ -87,7 +89,7 @@ int open_instream( const std::string & input_filename )
{ {
const int infd = open( input_filename.c_str(), O_RDONLY | O_BINARY ); const int infd = open( input_filename.c_str(), O_RDONLY | O_BINARY );
if( infd < 0 ) if( infd < 0 )
show_error2( "Can't open input file", input_filename.c_str() ); show_file_error( input_filename.c_str(), "Can't open input file", errno );
return infd; return infd;
} }
@ -211,10 +213,8 @@ int ztest_file( const int infd, int format_index,
int main( const int argc, const char * const argv[] ) int main( const int argc, const char * const argv[] )
{ {
enum { bz2_opt = 256, gz_opt, lz_opt, xz_opt }; enum { bz2_opt = 256, gz_opt, lz_opt, xz_opt };
int infd = -1;
int format_index = -1; int format_index = -1;
bool recursive = false; int recursive = 0; // 1 = '-r', 2 = '-R'
std::string input_filename;
std::list< std::string > filenames; std::list< std::string > filenames;
std::vector< const char * > ztest_args; // args to ztest, maybe empty std::vector< const char * > ztest_args; // args to ztest, maybe empty
invocation_name = argv[0]; invocation_name = argv[0];
@ -222,19 +222,20 @@ int main( const int argc, const char * const argv[] )
const Arg_parser::Option options[] = const Arg_parser::Option options[] =
{ {
{ 'h', "help", Arg_parser::no }, { 'h', "help", Arg_parser::no },
{ 'M', "format", Arg_parser::yes }, { 'M', "format", Arg_parser::yes },
{ 'N', "no-rcfile", Arg_parser::no }, { 'N', "no-rcfile", Arg_parser::no },
{ 'O', "force-format", Arg_parser::yes }, { 'O', "force-format", Arg_parser::yes },
{ 'q', "quiet", Arg_parser::no }, { 'q', "quiet", Arg_parser::no },
{ 'r', "recursive", Arg_parser::no }, { 'r', "recursive", Arg_parser::no },
{ 'v', "verbose", Arg_parser::no }, { 'R', "dereference-recursive", Arg_parser::no },
{ 'V', "version", Arg_parser::no }, { 'v', "verbose", Arg_parser::no },
{ bz2_opt, "bz2", Arg_parser::yes }, { 'V', "version", Arg_parser::no },
{ gz_opt, "gz", Arg_parser::yes }, { bz2_opt, "bz2", Arg_parser::yes },
{ lz_opt, "lz", Arg_parser::yes }, { gz_opt, "gz", Arg_parser::yes },
{ xz_opt, "xz", Arg_parser::yes }, { lz_opt, "lz", Arg_parser::yes },
{ 0 , 0, Arg_parser::no } }; { xz_opt, "xz", Arg_parser::yes },
{ 0 , 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options ); const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option if( parser.error().size() ) // bad option
@ -255,7 +256,8 @@ int main( const int argc, const char * const argv[] )
case 'N': break; case 'N': break;
case 'O': format_index = parse_format_type( arg ); break; case 'O': format_index = parse_format_type( arg ); break;
case 'q': verbosity = -1; ztest_args.push_back( "-q" ); break; case 'q': verbosity = -1; ztest_args.push_back( "-q" ); break;
case 'r': recursive = true; break; case 'r': recursive = 1; break;
case 'R': recursive = 2; break;
case 'v': if( verbosity < 4 ) ++verbosity; case 'v': if( verbosity < 4 ) ++verbosity;
ztest_args.push_back( "-v" ); break; ztest_args.push_back( "-v" ); break;
case 'V': show_version(); return 0; case 'V': show_version(); return 0;
@ -275,13 +277,15 @@ int main( const int argc, const char * const argv[] )
for( ; argind < parser.arguments(); ++argind ) for( ; argind < parser.arguments(); ++argind )
filenames.push_back( parser.argument( argind ) ); filenames.push_back( parser.argument( argind ) );
if( filenames.empty() ) filenames.push_back( "-" ); if( filenames.empty() ) filenames.push_back( recursive ? "." : "-" );
std::string input_filename;
int retval = 0; int retval = 0;
bool error = false; bool error = false;
bool stdin_used = false; bool stdin_used = false;
while( next_filename( filenames, input_filename, error, recursive ) ) while( next_filename( filenames, input_filename, error, recursive ) )
{ {
int infd;
if( input_filename.empty() ) if( input_filename.empty() )
{ {
if( stdin_used ) continue; else stdin_used = true; if( stdin_used ) continue; else stdin_used = true;
@ -299,12 +303,12 @@ int main( const int argc, const char * const argv[] )
else tmp = ztest_file( infd, format_index, input_filename, ztest_args ); else tmp = ztest_file( infd, format_index, input_filename, ztest_args );
if( tmp > retval ) retval = tmp; if( tmp > retval ) retval = tmp;
if( input_filename.size() ) { close( infd ); infd = -1; } if( input_filename.size() ) close( infd );
} }
if( std::fclose( stdout ) != 0 ) if( std::fclose( stdout ) != 0 ) // in case decompressor writes to stdout
{ {
show_error( "Can't close stdout", errno ); show_error( "Error closing stdout", errno );
error = true; error = true;
} }
if( error && retval == 0 ) retval = 1; if( error && retval == 0 ) retval = 1;

View file

@ -1,5 +1,5 @@
/* Zupdate - recompress bzip2, gzip, xz files to lzip format /* Zupdate - recompress bzip2, gzip, xz files to lzip format
Copyright (C) 2013-2018 Antonio Diaz Diaz. Copyright (C) 2013-2019 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -51,14 +51,15 @@ namespace {
void show_help() void show_help()
{ {
std::printf( "Zupdate recompresses files from bzip2, gzip, and xz formats to lzip format.\n" std::printf( "Zupdate recompresses files from bzip2, gzip, and xz formats to lzip\n"
"The originals are compared with the new files and then deleted.\n" "format. Each original is compared with the new file and then deleted.\n"
"Only regular files with standard file name extensions are recompressed,\n" "Only regular files with standard file name extensions are recompressed,\n"
"other files are ignored.\n" "other files are ignored. Compressed files are decompressed and then\n"
"Compressed files are decompressed and then recompressed on the fly; no\n" "recompressed on the fly; no temporary files are created. The lzip format\n"
"temporary files are created.\n" "is chosen as destination because it is the most appropriate for\n"
"The lzip format is chosen as destination because it is by far the most\n" "long-term data archiving.\n"
"appropriate for long-term data archiving.\n" "\nIf no files are specified, recursive searches examine the current\n"
"working directory, and nonrecursive searches do nothing.\n"
"\nIf the lzip compressed version of a file already exists, the file is\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" "skipped unless the '--force' option is given. In this case, if the\n"
"comparison with the existing lzip version fails, an error is returned\n" "comparison with the existing lzip version fails, an error is returned\n"
@ -70,21 +71,22 @@ void show_help()
"recompressed (if needed), compared and deleted (if requested). Non-zero\n" "recompressed (if needed), compared and deleted (if requested). Non-zero\n"
"otherwise.\n" "otherwise.\n"
"\nOptions:\n" "\nOptions:\n"
" -h, --help display this help and exit\n" " -h, --help display this help and exit\n"
" -V, --version output version information and exit\n" " -V, --version output version information and exit\n"
" -f, --force don't skip a file even if the .lz exists\n" " -f, --force don't skip a file even if the .lz exists\n"
" -k, --keep keep (don't delete) input files\n" " -k, --keep keep (don't delete) input files\n"
" -l, --lzip-verbose pass a -v option to the lzip compressor\n" " -l, --lzip-verbose pass a -v option to the lzip compressor\n"
" -M, --format=<list> process only the formats in <list>\n" " -M, --format=<list> process only the formats in <list>\n"
" -N, --no-rcfile don't read runtime configuration file\n" " -N, --no-rcfile don't read runtime configuration file\n"
" -q, --quiet suppress all messages\n" " -q, --quiet suppress all messages\n"
" -r, --recursive operate recursively on directories\n" " -r, --recursive operate recursively on directories\n"
" -v, --verbose be verbose (a 2nd -v gives more)\n" " -R, --dereference-recursive recursively follow symbolic links\n"
" -0 .. -9 set compression level [default 9]\n" " -v, --verbose be verbose (a 2nd -v gives more)\n"
" --bz2=<command> set compressor and options for bzip2 format\n" " -0 .. -9 set compression level [default 9]\n"
" --gz=<command> set compressor and options for gzip format\n" " --bz2=<command> set compressor and options for bzip2 format\n"
" --lz=<command> set compressor and options for lzip format\n" " --gz=<command> set compressor and options for gzip format\n"
" --xz=<command> set compressor and options for xz format\n" ); " --lz=<command> set compressor and options for lzip format\n"
" --xz=<command> set compressor and options for xz format\n" );
show_help_addr(); show_help_addr();
} }
@ -293,43 +295,43 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
int main( const int argc, const char * const argv[] ) int main( const int argc, const char * const argv[] )
{ {
enum { bz2_opt = 256, gz_opt, lz_opt, xz_opt }; enum { bz2_opt = 256, gz_opt, lz_opt, xz_opt };
std::string input_filename; int recursive = 0; // 1 = '-r', 2 = '-R'
std::list< std::string > filenames; std::list< std::string > filenames;
std::vector< std::string > lzip_args2; // args to lzip, maybe empty std::vector< std::string > lzip_args2; // args to lzip, maybe empty
bool force = false; bool force = false;
bool keep_input_files = false; bool keep_input_files = false;
bool no_rcfile = false; bool no_rcfile = false;
bool recursive = false;
invocation_name = argv[0]; invocation_name = argv[0];
program_name = "zupdate"; program_name = "zupdate";
const Arg_parser::Option options[] = const Arg_parser::Option options[] =
{ {
{ '0', 0, Arg_parser::no }, { '0', 0, Arg_parser::no },
{ '1', 0, Arg_parser::no }, { '1', 0, Arg_parser::no },
{ '2', 0, Arg_parser::no }, { '2', 0, Arg_parser::no },
{ '3', 0, Arg_parser::no }, { '3', 0, Arg_parser::no },
{ '4', 0, Arg_parser::no }, { '4', 0, Arg_parser::no },
{ '5', 0, Arg_parser::no }, { '5', 0, Arg_parser::no },
{ '6', 0, Arg_parser::no }, { '6', 0, Arg_parser::no },
{ '7', 0, Arg_parser::no }, { '7', 0, Arg_parser::no },
{ '8', 0, Arg_parser::no }, { '8', 0, Arg_parser::no },
{ '9', 0, Arg_parser::no }, { '9', 0, Arg_parser::no },
{ 'f', "force", Arg_parser::no }, { 'f', "force", Arg_parser::no },
{ 'h', "help", Arg_parser::no }, { 'h', "help", Arg_parser::no },
{ 'k', "keep", Arg_parser::no }, { 'k', "keep", Arg_parser::no },
{ 'l', "lzip-verbose", Arg_parser::no }, { 'l', "lzip-verbose", Arg_parser::no },
{ 'M', "format", Arg_parser::yes }, { 'M', "format", Arg_parser::yes },
{ 'N', "no-rcfile", Arg_parser::no }, { 'N', "no-rcfile", Arg_parser::no },
{ 'q', "quiet", Arg_parser::no }, { 'q', "quiet", Arg_parser::no },
{ 'r', "recursive", Arg_parser::no }, { 'r', "recursive", Arg_parser::no },
{ 'v', "verbose", Arg_parser::no }, { 'R', "dereference-recursive", Arg_parser::no },
{ 'V', "version", Arg_parser::no }, { 'v', "verbose", Arg_parser::no },
{ bz2_opt, "bz2", Arg_parser::yes }, { 'V', "version", Arg_parser::no },
{ gz_opt, "gz", Arg_parser::yes }, { bz2_opt, "bz2", Arg_parser::yes },
{ lz_opt, "lz", Arg_parser::yes }, { gz_opt, "gz", Arg_parser::yes },
{ xz_opt, "xz", Arg_parser::yes }, { lz_opt, "lz", Arg_parser::yes },
{ 0 , 0, Arg_parser::no } }; { xz_opt, "xz", Arg_parser::yes },
{ 0 , 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options ); const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option if( parser.error().size() ) // bad option
@ -355,7 +357,8 @@ int main( const int argc, const char * const argv[] )
case 'M': parse_format_list( arg ); break; case 'M': parse_format_list( arg ); break;
case 'N': no_rcfile = true; break; case 'N': no_rcfile = true; break;
case 'q': verbosity = -1; lzip_args2.push_back( "-q" ); break; case 'q': verbosity = -1; lzip_args2.push_back( "-q" ); break;
case 'r': recursive = true; break; case 'r': recursive = 1; break;
case 'R': recursive = 2; break;
case 'v': if( verbosity < 4 ) ++verbosity; break; case 'v': if( verbosity < 4 ) ++verbosity; break;
case 'V': show_version(); return 0; case 'V': show_version(); return 0;
case bz2_opt: parse_compressor( arg, fmt_bz2, 1 ); break; case bz2_opt: parse_compressor( arg, fmt_bz2, 1 ); break;
@ -378,6 +381,9 @@ int main( const int argc, const char * const argv[] )
for( ; argind < parser.arguments(); ++argind ) for( ; argind < parser.arguments(); ++argind )
filenames.push_back( parser.argument( argind ) ); filenames.push_back( parser.argument( argind ) );
if( filenames.empty() && recursive ) filenames.push_back( "." );
std::string input_filename;
int retval = 0; int retval = 0;
bool error = false; bool error = false;
while( next_filename( filenames, input_filename, error, recursive, true ) ) while( next_filename( filenames, input_filename, error, recursive, true ) )

View file

@ -1,5 +1,5 @@
/* Zutils - Utilities dealing with compressed files /* Zutils - Utilities dealing with compressed files
Copyright (C) 2009-2018 Antonio Diaz Diaz. Copyright (C) 2009-2019 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -143,16 +143,17 @@ bool good_status( const Children & children, const bool finished )
const pid_t pid = children.pid[i]; const pid_t pid = children.pid[i];
if( pid ) if( pid )
{ {
const char * const msg = const char * const name =
( i & 1 ) ? children.compressor_name : "data feeder"; ( i & 1 ) ? children.compressor_name : "data feeder";
if( !finished ) if( !finished )
{ {
const int tmp = child_status( pid, msg ); const int tmp = child_status( pid, name );
if( tmp < 0 ) kill( pid, SIGTERM ); // child not terminated if( tmp < 0 ) // child not terminated
{ kill( pid, SIGTERM ); wait_for_child( pid, name ); }
else if( tmp != 0 ) error = true; // child status != 0 else if( tmp != 0 ) error = true; // child status != 0
} }
else else
if( wait_for_child( pid, msg ) != 0 ) error = true; if( wait_for_child( pid, name ) != 0 ) error = true;
} }
} }
return !error; return !error;

View file

@ -1,5 +1,5 @@
/* Zutils - Utilities dealing with compressed files /* Zutils - Utilities dealing with compressed files
Copyright (C) 2009-2018 Antonio Diaz Diaz. Copyright (C) 2009-2019 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by