1
0
Fork 0

Merging upstream version 1.12~pre2.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-24 06:01:55 +01:00
parent 64fda76e2e
commit f4079ace26
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
19 changed files with 422 additions and 222 deletions

View file

@ -1,11 +1,26 @@
2022-04-12 Antonio Diaz Diaz <antonio@gnu.org>
* Version 1.12-pre2 released.
* zgrep.cc: Accept option '-Z, --null'. (Reported by Leah Neukirchen).
* zupdate.cc: New options '-e, --expand-extensions',
'-i, --ignore-errors'.
* Support compress'd (.Z) files through gzip in all utilities.
2022-03-06 Antonio Diaz Diaz <antonio@gnu.org>
* Version 1.12-pre1 released.
* zgrep.cc: Accept options '-G, --basic-regexp', '--label=<label>',
'--line-buffered', '-P, --perl-regexp', '--silent',
'-T, --initial-tab', '-U, --binary'. (Reported by Chris Jamboretz).
2022-01-25 Antonio Diaz Diaz <antonio@gnu.org> 2022-01-25 Antonio Diaz Diaz <antonio@gnu.org>
* Version 1.11 released. * Version 1.11 released.
* zcmp.cc, zdiff.cc (main): Fix race returning 1 instead of 2 when a * zcmp.cc, zdiff.cc (main): Fix race returning 1 instead of 2 when a
compressor is not found or when the wrong format is forced. compressor is not found or when the wrong format is forced.
* zcmp.cc (getnum): Show option name and valid range if error. * zcmp.cc (getnum): Show option name and valid range if error.
* All tools: Show option name if error in option argument. * All utilities: Show option name if error in option argument.
* Add support for zstd format to all tools. * Add support for zstd format to all utilities.
* 'zdiff -v -V' now prints the version of the diff program used. * 'zdiff -v -V' now prints the version of the diff program used.
* 'zgrep --verbose -V' now prints the version of the grep program used. * 'zgrep --verbose -V' now prints the version of the grep program used.
* zutils.texi: Document recompression of read-only files by linking. * zutils.texi: Document recompression of read-only files by linking.
@ -18,7 +33,7 @@
* zdiff.cc (set_fifonames): Encode pid in little endian order. * zdiff.cc (set_fifonames): Encode pid in little endian order.
* zupdate.cc (zupdate_file): Fix a portability issue with Solaris 10. * zupdate.cc (zupdate_file): Fix a portability issue with Solaris 10.
* zutils.texi: Document that 'zgrep -L' fails with GNU grep 3.2 to 3.4. * zutils.texi: Document that 'zgrep -L' fails with GNU grep 3.2 to 3.4.
* check.sh: Test empty input files with all tools except zupdate. * check.sh: Test empty input files with all utilities except zupdate.
2020-06-27 Antonio Diaz Diaz <antonio@gnu.org> 2020-06-27 Antonio Diaz Diaz <antonio@gnu.org>
@ -95,7 +110,7 @@
* Version 1.2 released. * Version 1.2 released.
* New utility; zupdate. * New utility; zupdate.
* Remove zutils executable. Utils are now independent executables. * Remove zutils executable. Utilities are now independent executables.
* zgrep.cc: Fix the exit status returned on error. * zgrep.cc: Fix the exit status returned on error.
* zutils.texinfo: Rename to zutils.texi. * zutils.texinfo: Rename to zutils.texi.

38
NEWS
View file

@ -1,30 +1,16 @@
Changes in version 1.11: Changes in version 1.12:
A race has been fixed in zcmp and zdiff that sometimes made them return 1 zgrep now also accepts the following options: '-G, --basic-regexp',
(files differ) instead of 2 (trouble) when a compressor is not found or when '--label=<label>', '--line-buffered', '-P, --perl-regexp', '--silent',
the wrong format is forced. '-T, --initial-tab', '-U, --binary', and '-Z, --null'.
(Reported by Chris Jamboretz and Leah Neukirchen).
In case of error in an argument to a command line option, all tools now show zupdate now accepts option '-e, --expand-extensions', which makes it expand
the name of the option. combined file name extensions; tgz -> tar.lz.
In case of error in a numerical argument to a command line option, zcmp zupdate now also accepts option '-i, --ignore-errors', which makes it ignore
now shows the name of the option and the range of valid values. non-fatal errors.
Support for the zstd format has been added to all tools. This allows, among All utilities now support compress'd (.Z) files through gzip. For this to
other things, zupdating zstd files to lzip format for long-term archiving, work, the gzip program used (for example GNU gzip) must be able to
and using zcmp along with the unzcrash tool (from the lziprecover package) decompress .Z files.
to test zstd files.
'zdiff --verbose --version' now prints the version of the diff program used
if it supports the option '--version'.
'zgrep --verbose --version' now prints the version of the grep program used
if it supports the option '--version'.
It has been documented in the manual how to recompress files with zupdate
from a read-only file system to another place by first linking the files
from the destination directory and then compressing the links:
'ln -s /src/foo.gz . && zupdate foo.gz'
The texinfo category of the manual has been changed from 'Data Compression'
to 'Compression' to match that of gzip. (Reported by Alfred M. Szmidt).

7
README
View file

@ -30,10 +30,9 @@ example, use the following command to search for the string 'foo' in
gzip and lzip files only: gzip and lzip files only:
'zgrep foo -r --format=gz,lz somedir somefile.tar'. 'zgrep foo -r --format=gz,lz somedir somefile.tar'.
FORMAT NOTE 2: If the option '--force-format' is given, the files are FORMAT NOTE 2: The standard POSIX compress format (.Z) is obsolete and is
passed to the corresponding decompressor without verifying their format, only supported through gzip. For this to work, the gzip program used (for
allowing for example the processing of compress'd (.Z) files with gzip: example GNU gzip) must be able to decompress .Z files.
'zcmp --force-format=gz file.Z file.lz'.
LANGUAGE NOTE: Uncompressed = not compressed = plain data; it may never have LANGUAGE NOTE: Uncompressed = not compressed = plain data; it may never have
been compressed. Decompressed is used to refer to data which have undergone been compressed. Decompressed is used to refer to data which have undergone

2
configure vendored
View file

@ -6,7 +6,7 @@
# to copy, distribute, and modify it. # to copy, distribute, and modify it.
pkgname=zutils pkgname=zutils
pkgversion=1.11 pkgversion=1.12-pre2
srctrigger=doc/${pkgname}.texi srctrigger=doc/${pkgname}.texi
# clear some things potentially inherited from environment. # clear some things potentially inherited from environment.

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.16. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.16.
.TH ZCAT "1" "January 2022" "zutils 1.11" "User Commands" .TH ZCAT "1" "April 2022" "zutils 1.12-pre2" "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

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.16. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.16.
.TH ZCMP "1" "January 2022" "zutils 1.11" "User Commands" .TH ZCMP "1" "April 2022" "zutils 1.12-pre2" "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

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.16. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.16.
.TH ZDIFF "1" "January 2022" "zutils 1.11" "User Commands" .TH ZDIFF "1" "April 2022" "zutils 1.12-pre2" "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
@ -98,7 +98,7 @@ verbose mode (for \fB\-\-version\fR)
ignore all white space ignore all white space
.TP .TP
\fB\-W\fR, \fB\-\-width=\fR<n> \fB\-W\fR, \fB\-\-width=\fR<n>
output at most <n> print columns output at most <n> print columns (for \fB\-y\fR)
.TP .TP
\fB\-y\fR, \fB\-\-side\-by\-side\fR \fB\-y\fR, \fB\-\-side\-by\-side\fR
output in two columns output in two columns

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.16. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.16.
.TH ZGREP "1" "January 2022" "zutils 1.11" "User Commands" .TH ZGREP "1" "April 2022" "zutils 1.12-pre2" "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
@ -69,6 +69,9 @@ obtain patterns from <file>
\fB\-F\fR, \fB\-\-fixed\-strings\fR \fB\-F\fR, \fB\-\-fixed\-strings\fR
<pattern> is a set of newline\-separated strings <pattern> is a set of newline\-separated strings
.TP .TP
\fB\-G\fR, \fB\-\-basic\-regexp\fR
<pattern> is a basic regular expression (default)
.TP
\fB\-h\fR, \fB\-\-no\-filename\fR \fB\-h\fR, \fB\-\-no\-filename\fR
suppress the prefixing file name on output suppress the prefixing file name on output
.TP .TP
@ -87,6 +90,12 @@ only print names of files containing matches
\fB\-L\fR, \fB\-\-files\-without\-match\fR \fB\-L\fR, \fB\-\-files\-without\-match\fR
only print names of files containing no matches only print names of files containing no matches
.TP .TP
\fB\-\-label=\fR<label>
use <label> as file name for standard input
.TP
\fB\-\-line\-buffered\fR
flush output on every line
.TP
\fB\-m\fR, \fB\-\-max\-count=\fR<n> \fB\-m\fR, \fB\-\-max\-count=\fR<n>
stop after <n> matches stop after <n> matches
.TP .TP
@ -105,7 +114,10 @@ show only the part of a line matching <pattern>
\fB\-O\fR, \fB\-\-force\-format=\fR<fmt> \fB\-O\fR, \fB\-\-force\-format=\fR<fmt>
force the format given (bz2, gz, lz, xz, zst) force the format given (bz2, gz, lz, xz, zst)
.TP .TP
\fB\-q\fR, \fB\-\-quiet\fR \fB\-P\fR, \fB\-\-perl\-regexp\fR
<pattern> is a Perl regular expression
.TP
\fB\-q\fR, \fB\-\-quiet\fR, \fB\-\-silent\fR
suppress all messages suppress all messages
.TP .TP
\fB\-r\fR, \fB\-\-recursive\fR \fB\-r\fR, \fB\-\-recursive\fR
@ -117,6 +129,12 @@ recursively follow symbolic links
\fB\-s\fR, \fB\-\-no\-messages\fR \fB\-s\fR, \fB\-\-no\-messages\fR
suppress error messages suppress error messages
.TP .TP
\fB\-T\fR, \fB\-\-initial\-tab\fR
make tabs line up (if needed)
.TP
\fB\-U\fR, \fB\-\-binary\fR
don't strip CR characters at EOL (DOS/Windows)
.TP
\fB\-v\fR, \fB\-\-invert\-match\fR \fB\-v\fR, \fB\-\-invert\-match\fR
select non\-matching lines select non\-matching lines
.TP .TP
@ -129,6 +147,9 @@ match only whole words
\fB\-x\fR, \fB\-\-line\-regexp\fR \fB\-x\fR, \fB\-\-line\-regexp\fR
match only whole lines match only whole lines
.TP .TP
\fB\-Z\fR, \fB\-\-null\fR
print 0 byte (ASCII NUL) after file name
.TP
\fB\-\-bz2=\fR<command> \fB\-\-bz2=\fR<command>
set compressor and options for bzip2 format set compressor and options for bzip2 format
.TP .TP

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.16. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.16.
.TH ZTEST "1" "January 2022" "zutils 1.11" "User Commands" .TH ZTEST "1" "April 2022" "zutils 1.12-pre2" "User Commands"
.SH NAME .SH NAME
ztest \- verify the integrity of compressed files ztest \- verify the integrity of compressed files
.SH SYNOPSIS .SH SYNOPSIS

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.16. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.16.
.TH ZUPDATE "1" "January 2022" "zutils 1.11" "User Commands" .TH ZUPDATE "1" "April 2022" "zutils 1.12-pre2" "User Commands"
.SH NAME .SH NAME
zupdate \- recompress bzip2, gzip, xz, zstd files to lzip format zupdate \- recompress bzip2, gzip, xz, zstd files to lzip format
.SH SYNOPSIS .SH SYNOPSIS
@ -26,12 +26,15 @@ compressed files are never overwritten nor deleted.
.PP .PP
The names of the original files must have one of the following extensions: The names of the original files must have one of the following extensions:
.PP .PP
\&'.bz2', '.gz', '.xz', or '.zst', which are recompressed to '.lz'. \&'.bz2', '.gz', '.xz', '.zst', or '.Z', which are recompressed to '.lz'.
.PP .PP
\&'.tbz', '.tbz2', '.tgz', '.txz', or '.tzst', which are recompressed to '.tlz'. \&'.tbz', '.tbz2', '.tgz', '.txz', or '.tzst', which are recompressed to '.tlz'.
.PP .PP
Exit status is 0 if all the compressed files were successfully recompressed Exit status is 0 if all the compressed files were successfully recompressed
(if needed), compared, and deleted (if requested). Non\-zero otherwise. (if needed), compared, and deleted (if requested). 1 if a non\-fatal error
occurred (file not found or not regular, or has invalid format, or can't be
deleted). 2 if a fatal error occurred (compressor can't be run, or
comparison fails).
.SH OPTIONS .SH OPTIONS
.TP .TP
\fB\-h\fR, \fB\-\-help\fR \fB\-h\fR, \fB\-\-help\fR
@ -40,9 +43,15 @@ display this help and exit
\fB\-V\fR, \fB\-\-version\fR \fB\-V\fR, \fB\-\-version\fR
output version information and exit output version information and exit
.TP .TP
\fB\-e\fR, \fB\-\-expand\-extensions\fR
expand combined extensions; tgz \-> tar.lz
.TP
\fB\-f\fR, \fB\-\-force\fR \fB\-f\fR, \fB\-\-force\fR
don't skip a file even if the .lz exists don't skip a file even if the .lz exists
.TP .TP
\fB\-i\fR, \fB\-\-ignore\-errors\fR
ignore non\-fatal errors
.TP
\fB\-k\fR, \fB\-\-keep\fR \fB\-k\fR, \fB\-\-keep\fR
keep (don't delete) input files keep (don't delete) input files
.TP .TP

View file

@ -11,7 +11,7 @@ File: zutils.info, Node: Top, Next: Introduction, Up: (dir)
Zutils Manual Zutils Manual
************* *************
This manual is for Zutils (version 1.11, 25 January 2022). This manual is for Zutils (version 1.12-pre2, 12 April 2022).
* Menu: * Menu:
@ -69,10 +69,9 @@ of formats in recursive mode and when trying compressed file names. For
example, use the following command to search for the string 'foo' in gzip example, use the following command to search for the string 'foo' in gzip
and lzip files only: 'zgrep foo -r --format=gz,lz somedir somefile.tar'. and lzip files only: 'zgrep foo -r --format=gz,lz somedir somefile.tar'.
FORMAT NOTE 2: If the option '--force-format' is given, the files are FORMAT NOTE 2: The standard POSIX compress format (.Z) is obsolete and is
passed to the corresponding decompressor without verifying their format, only supported through gzip. For this to work, the gzip program used (for
allowing for example the processing of compress'd (.Z) files with gzip: example GNU gzip) must be able to decompress .Z files.
'zcmp --force-format=gz file.Z file.lz'.
LANGUAGE NOTE: Uncompressed = not compressed = plain data; it may never LANGUAGE NOTE: Uncompressed = not compressed = plain data; it may never
have been compressed. Decompressed is used to refer to data which have have been compressed. Decompressed is used to refer to data which have
@ -131,7 +130,7 @@ here. *Note Argument syntax: (arg_parser)Argument syntax.
extensions: extensions:
bz2 enables .bz2 .tbz .tbz2 bz2 enables .bz2 .tbz .tbz2
gz enables .gz .tgz gz enables .gz .tgz .Z
lz enables .lz .tlz lz enables .lz .tlz
xz enables .xz .txz xz enables .xz .txz
zst enables .zst .tzst zst enables .zst .tzst
@ -468,6 +467,15 @@ program used supports them):
'--ignore-all-space' '--ignore-all-space'
Ignore all white space. Ignore all white space.
'-W COLUMNS'
'--width=COLUMNS'
Output at most the specified number of print columns per line in side
by side format.
'-y'
'--side-by-side'
Use the side by side output format.
 
File: zutils.info, Node: Zgrep, Next: Ztest, Prev: Zdiff, Up: Top File: zutils.info, Node: Zgrep, Next: Ztest, Prev: Zdiff, Up: Top
@ -496,8 +504,9 @@ directory, and nonrecursive searches read standard input.
An exit status of 0 means at least one match was found, 1 means no matches An exit status of 0 means at least one match was found, 1 means no matches
were found, and 2 means trouble. were found, and 2 means trouble.
zgrep supports the following options (some options only work if the grep zgrep supports the following options (Some options only work if the grep
program used supports them): program used supports them. Options -h, -H, -r, -R, and -Z are managed by
zgrep and not passed to grep):
'-a' '-a'
'--text' '--text'
@ -532,7 +541,7 @@ program used supports them):
'-E' '-E'
'--extended-regexp' '--extended-regexp'
Treat PATTERN as an extended regular expression. Interpret PATTERN as an extended regular expression (ERE).
'-f FILE' '-f FILE'
'--file=FILE' '--file=FILE'
@ -543,7 +552,12 @@ program used supports them):
'-F' '-F'
'--fixed-strings' '--fixed-strings'
Treat PATTERN as a set of newline-separated strings. Interpret PATTERN as a set of newline-separated strings.
'-G'
'--basic-regexp'
Interpret PATTERN as a basic regular expression (BRE). This is the
default.
'-h' '-h'
'--no-filename' '--no-filename'
@ -573,6 +587,13 @@ program used supports them):
of a wrong change in the exit status of grep, which was reverted in of a wrong change in the exit status of grep, which was reverted in
GNU grep 3.5. GNU grep 3.5.
'--label=LABEL'
Display input actually coming from standard input as input coming from
file LABEL.
'--line-buffered'
Use line buffering on output. This may cause a performance penalty.
'-m N' '-m N'
'--max-count=N' '--max-count=N'
Stop after N matches. Stop after N matches.
@ -593,8 +614,13 @@ program used supports them):
format, and the exact file name must be given. Other names won't be format, and the exact file name must be given. Other names won't be
tried. tried.
'-P'
'--perl-regexp'
Interpret PATTERN as a Perl-compatible regular expression (PCRE).
'-q' '-q'
'--quiet' '--quiet'
'--silent'
Suppress all messages. Exit immediately with zero status if any match Suppress all messages. Exit immediately with zero status if any match
is found, even if an error was detected. is found, even if an error was detected.
@ -613,6 +639,16 @@ program used supports them):
'--no-messages' '--no-messages'
Suppress error messages about nonexistent or unreadable files. Suppress error messages about nonexistent or unreadable files.
'-T'
'--initial-tab'
Make sure that the first character of actual line content lies on a tab
stop, so that the alignment of tabs looks normal.
'-U'
'--binary'
Use binary I/O on platforms affected by the bug known as "text mode
I/O". (MS-DOS, MS-Windows, OS/2).
'-v' '-v'
'--invert-match' '--invert-match'
Select non-matching lines. Select non-matching lines.
@ -629,6 +665,14 @@ program used supports them):
'--line-regexp' '--line-regexp'
Match only whole lines. Match only whole lines.
'-Z'
'--null'
Output a zero byte (the ASCII NUL character) instead of the character
that normally follows a file name. For example, 'zgrep -lZ' outputs a
zero byte after each file name instead of the usual newline. This
option makes the output unambiguous, even in the presence of file
names containing unusual characters like newlines.
 
File: zutils.info, Node: Ztest, Next: Zupdate, Prev: Zgrep, Up: Top File: zutils.info, Node: Ztest, Next: Zupdate, Prev: Zgrep, Up: Top
@ -675,9 +719,7 @@ compressed file is corrupt or invalid.
'gz', 'lz', 'xz', and 'zst'. If this option is used, the files are 'gz', 'lz', 'xz', and 'zst'. If this option is used, the files are
passed to the corresponding decompressor without verifying their passed to the corresponding decompressor without verifying their
format, and any files in a format that the decompressor can't format, and any files in a format that the decompressor can't
understand will fail. For example, '--force-format=gz' can test understand will fail.
gzipped (.gz) and compress'd (.Z) files if the compressor used is GNU
gzip.
'-q' '-q'
'--quiet' '--quiet'
@ -735,7 +777,7 @@ pair of files in a multiformat set of files.
The names of the original files must have one of the following The names of the original files must have one of the following
extensions: extensions:
'.bz2', '.gz', '.xz', or '.zst', which are recompressed to '.lz'; '.bz2', '.gz', '.xz', '.zst', or '.Z', which are recompressed to '.lz';
'.tbz', '.tbz2', '.tgz', '.txz', or '.tzst', which are recompressed to '.tbz', '.tbz2', '.tgz', '.txz', or '.tzst', which are recompressed to
'.tlz'. '.tlz'.
Keeping the combined extensions ('.tgz' -> '.tlz') may be useful when Keeping the combined extensions ('.tgz' -> '.tlz') may be useful when
@ -745,21 +787,29 @@ recompressing Slackware packages, for example.
If the decompressor for the xz or zstd formats is not found, the If the decompressor for the xz or zstd formats is not found, the
corresponding files are ignored. corresponding files are ignored.
Recompressing a file is much like copying or moving it; therefore zupdate Recompressing a file is much like copying or moving it. Therefore zupdate
preserves the access and modification dates, permissions, and, when preserves the access and modification dates, permissions, and, if you have
possible, ownership of the file just as 'cp -p' does. (If the user ID or appropriate privileges, ownership of the file just as 'cp -p' does. (If the
the group ID can't be duplicated, the file permission bits S_ISUID and user ID or the group ID can't be duplicated, the file permission bits
S_ISGID are cleared). S_ISUID and S_ISGID are cleared).
The format for running zupdate is: The format for running zupdate is:
zupdate [OPTIONS] [FILES] zupdate [OPTIONS] [FILES]
Exit status is 0 if all the compressed files were successfully recompressed Exit status is 0 if all the compressed files were successfully recompressed
(if needed), compared, and deleted (if requested). Non-zero otherwise. (if needed), compared, and deleted (if requested). 1 if a non-fatal error
occurred (file not found or not regular, or has invalid format, or can't be
deleted). 2 if a fatal error occurred (compressor can't be run, or
comparison fails).
zupdate supports the following options: zupdate supports the following options:
'-e'
'--expand-extensions'
Expand combined file name extensions; recompress '.tbz', '.tbz2',
'.tgz', '.txz', and '.tzst' to 'tar.lz'.
'-f' '-f'
'--force' '--force'
Don't skip a file for which a lzip compressed version already exists. Don't skip a file for which a lzip compressed version already exists.
@ -767,6 +817,10 @@ Exit status is 0 if all the compressed files were successfully recompressed
the existing lzip file and deletes the input file if both contents are the existing lzip file and deletes the input file if both contents are
identical. identical.
'-i'
'--ignore-errors'
Ignore non-fatal errors. (See exit status above).
'-k' '-k'
'--keep' '--keep'
Keep (don't delete) the input file after comparing it with the lzip Keep (don't delete) the input file after comparing it with the lzip
@ -845,18 +899,18 @@ Concept index
 
Tag Table: Tag Table:
Node: Top217 Node: Top217
Node: Introduction1147 Node: Introduction1150
Node: Common options3947 Node: Common options3897
Ref: compressor-requirements6181 Ref: compressor-requirements6134
Node: The zutilsrc file6576 Node: The zutilsrc file6529
Node: Zcat7544 Node: Zcat7497
Node: Zcmp10119 Node: Zcmp10072
Node: Zdiff12620 Node: Zdiff12573
Node: Zgrep15478 Node: Zgrep15623
Node: Ztest19819 Node: Ztest21115
Node: Zupdate22513 Node: Zupdate23681
Node: Problems26607 Node: Problems28191
Node: Concept index27141 Node: Concept index28725
 
End Tag Table End Tag Table

View file

@ -6,8 +6,8 @@
@finalout @finalout
@c %**end of header @c %**end of header
@set UPDATED 25 January 2022 @set UPDATED 12 April 2022
@set VERSION 1.11 @set VERSION 1.12-pre2
@dircategory Compression @dircategory Compression
@direntry @direntry
@ -94,10 +94,9 @@ example, use the following command to search for the string @samp{foo} in
gzip and lzip files only: gzip and lzip files only:
@w{@samp{zgrep foo -r --format=gz,lz somedir somefile.tar}}. @w{@samp{zgrep foo -r --format=gz,lz somedir somefile.tar}}.
FORMAT NOTE 2: If the option @samp{--force-format} is given, the files are FORMAT NOTE 2: The standard POSIX compress format (.Z) is obsolete and is
passed to the corresponding decompressor without verifying their format, only supported through gzip. For this to work, the gzip program used (for
allowing for example the processing of compress'd (.Z) files with gzip: example GNU gzip) must be able to decompress .Z files.
@w{@samp{zcmp --force-format=gz file.Z file.lz}}.
LANGUAGE NOTE: Uncompressed = not compressed = plain data; it may never have LANGUAGE NOTE: Uncompressed = not compressed = plain data; it may never have
been compressed. Decompressed is used to refer to data which have undergone been compressed. Decompressed is used to refer to data which have undergone
@ -163,7 +162,7 @@ extensions:
@multitable {bz2} {enables} {any other file name} @multitable {bz2} {enables} {any other file name}
@item bz2 @tab enables @tab .bz2 .tbz .tbz2 @item bz2 @tab enables @tab .bz2 .tbz .tbz2
@item gz @tab enables @tab .gz .tgz @item gz @tab enables @tab .gz .tgz .Z
@item lz @tab enables @tab .lz .tlz @item lz @tab enables @tab .lz .tlz
@item xz @tab enables @tab .xz .txz @item xz @tab enables @tab .xz .txz
@item zst @tab enables @tab .zst .tzst @item zst @tab enables @tab .zst .tzst
@ -531,6 +530,15 @@ program used.
@itemx --ignore-all-space @itemx --ignore-all-space
Ignore all white space. Ignore all white space.
@item -W @var{columns}
@itemx --width=@var{columns}
Output at most the specified number of print columns per line in side by
side format.
@item -y
@itemx --side-by-side
Use the side by side output format.
@end table @end table
@ -564,8 +572,9 @@ zgrep [@var{options}] @var{pattern} [@var{files}]
An exit status of 0 means at least one match was found, 1 means no An exit status of 0 means at least one match was found, 1 means no
matches were found, and 2 means trouble. matches were found, and 2 means trouble.
zgrep supports the following options (some options only work if the grep zgrep supports the following options (Some options only work if the grep
program used supports them): program used supports them. Options -h, -H, -r, -R, and -Z are managed by
zgrep and not passed to grep):
@table @code @table @code
@item -a @item -a
@ -602,7 +611,7 @@ Use @var{pattern} as the pattern to match.
@item -E @item -E
@itemx --extended-regexp @itemx --extended-regexp
Treat @var{pattern} as an extended regular expression. Interpret @var{pattern} as an extended regular expression (ERE).
@item -f @var{file} @item -f @var{file}
@itemx --file=@var{file} @itemx --file=@var{file}
@ -614,7 +623,12 @@ used with @samp{-e} to read @var{file} only once, for example if
@item -F @item -F
@itemx --fixed-strings @itemx --fixed-strings
Treat @var{pattern} as a set of newline-separated strings. Interpret @var{pattern} as a set of newline-separated strings.
@item -G
@itemx --basic-regexp
Interpret @var{pattern} as a basic regular expression (BRE). This is the
default.
@item -h @item -h
@itemx --no-filename @itemx --no-filename
@ -643,6 +657,13 @@ Note: option -L fails (prints wrong results, returns wrong status, and even
hangs) when using GNU grep versions 3.2 to 3.4 inclusive because of a wrong hangs) when using GNU grep versions 3.2 to 3.4 inclusive because of a wrong
change in the exit status of grep, which was reverted in GNU grep 3.5. change in the exit status of grep, which was reverted in GNU grep 3.5.
@item --label=@var{label}
Display input actually coming from standard input as input coming from file
@var{label}.
@item --line-buffered
Use line buffering on output. This may cause a performance penalty.
@item -m @var{n} @item -m @var{n}
@itemx --max-count=@var{n} @itemx --max-count=@var{n}
Stop after @var{n} matches. Stop after @var{n} matches.
@ -663,8 +684,13 @@ is used, the files are passed to the corresponding decompressor without
verifying their format, and the exact file name must be given. Other names verifying their format, and the exact file name must be given. Other names
won't be tried. won't be tried.
@item -P
@itemx --perl-regexp
Interpret @var{pattern} as a Perl-compatible regular expression (PCRE).
@item -q @item -q
@itemx --quiet @itemx --quiet
@itemx --silent
Suppress all messages. Exit immediately with zero status if any match is Suppress all messages. Exit immediately with zero status if any match is
found, even if an error was detected. found, even if an error was detected.
@ -683,6 +709,16 @@ recursively, following all symbolic links.
@itemx --no-messages @itemx --no-messages
Suppress error messages about nonexistent or unreadable files. Suppress error messages about nonexistent or unreadable files.
@item -T
@itemx --initial-tab
Make sure that the first character of actual line content lies on a tab
stop, so that the alignment of tabs looks normal.
@item -U
@itemx --binary
Use binary I/O on platforms affected by the bug known as "text mode I/O".
(MS-DOS, MS-Windows, OS/2).
@item -v @item -v
@itemx --invert-match @itemx --invert-match
Select non-matching lines. Select non-matching lines.
@ -699,6 +735,14 @@ Match only whole words.
@itemx --line-regexp @itemx --line-regexp
Match only whole lines. Match only whole lines.
@item -Z
@itemx --null
Output a zero byte (the ASCII NUL character) instead of the character that
normally follows a file name. For example, 'zgrep -lZ' outputs a zero byte
after each file name instead of the usual newline. This option makes the
output unambiguous, even in the presence of file names containing unusual
characters like newlines.
@end table @end table
@ -752,8 +796,7 @@ Force the compressed format given. Valid values for @var{format} are
@samp{bz2}, @samp{gz}, @samp{lz}, @samp{xz}, and @samp{zst}. If this option @samp{bz2}, @samp{gz}, @samp{lz}, @samp{xz}, and @samp{zst}. If this option
is used, the files are passed to the corresponding decompressor without is used, the files are passed to the corresponding decompressor without
verifying their format, and any files in a format that the decompressor verifying their format, and any files in a format that the decompressor
can't understand will fail. For example, @samp{--force-format=gz} can test can't understand will fail.
gzipped (.gz) and compress'd (.Z) files if the compressor used is GNU gzip.
@item -q @item -q
@itemx --quiet @itemx --quiet
@ -810,8 +853,8 @@ Combining the options @samp{--force} and @samp{--keep}, as in
between each pair of files in a multiformat set of files. between each pair of files in a multiformat set of files.
The names of the original files must have one of the following extensions:@* The names of the original files must have one of the following extensions:@*
@samp{.bz2}, @samp{.gz}, @samp{.xz}, or @samp{.zst}, which are recompressed @samp{.bz2}, @samp{.gz}, @samp{.xz}, @samp{.zst}, or @samp{.Z}, which are
to @samp{.lz};@* recompressed to @samp{.lz};@*
@samp{.tbz}, @samp{.tbz2}, @samp{.tgz}, @samp{.txz}, or @samp{.tzst}, which @samp{.tbz}, @samp{.tbz2}, @samp{.tgz}, @samp{.txz}, or @samp{.tzst}, which
are recompressed to @samp{.tlz}.@* are recompressed to @samp{.tlz}.@*
Keeping the combined extensions (@samp{.tgz} --> @samp{.tlz}) may be useful Keeping the combined extensions (@samp{.tgz} --> @samp{.tlz}) may be useful
@ -821,11 +864,11 @@ Bzip2, gzip, and lzip are the primary formats. Xz and zstd are optional. If
the decompressor for the xz or zstd formats is not found, the corresponding the decompressor for the xz or zstd formats is not found, the corresponding
files are ignored. files are ignored.
Recompressing a file is much like copying or moving it; therefore zupdate Recompressing a file is much like copying or moving it. Therefore zupdate
preserves the access and modification dates, permissions, and, when preserves the access and modification dates, permissions, and, if you have
possible, ownership of the file just as @w{@samp{cp -p}} does. (If the user ID or appropriate privileges, ownership of the file just as @w{@samp{cp -p}} does.
the group ID can't be duplicated, the file permission bits S_ISUID and (If the user ID or the group ID can't be duplicated, the file permission
S_ISGID are cleared). bits S_ISUID and S_ISGID are cleared).
The format for running zupdate is: The format for running zupdate is:
@ -835,11 +878,19 @@ zupdate [@var{options}] [@var{files}]
@noindent @noindent
Exit status is 0 if all the compressed files were successfully recompressed Exit status is 0 if all the compressed files were successfully recompressed
(if needed), compared, and deleted (if requested). Non-zero otherwise. (if needed), compared, and deleted (if requested). 1 if a non-fatal error
occurred (file not found or not regular, or has invalid format, or can't be
deleted). 2 if a fatal error occurred (compressor can't be run, or
comparison fails).
zupdate supports the following options: zupdate supports the following options:
@table @code @table @code
@item -e
@itemx --expand-extensions
Expand combined file name extensions; recompress @samp{.tbz}, @samp{.tbz2},
@samp{.tgz}, @samp{.txz}, and @samp{.tzst} to @samp{tar.lz}.
@item -f @item -f
@itemx --force @itemx --force
Don't skip a file for which a lzip compressed version already exists. Don't skip a file for which a lzip compressed version already exists.
@ -847,6 +898,10 @@ Don't skip a file for which a lzip compressed version already exists.
of the existing lzip file and deletes the input file if both contents of the existing lzip file and deletes the input file if both contents
are identical. are identical.
@item -i
@itemx --ignore-errors
Ignore non-fatal errors. (See exit status above).
@item -k @item -k
@itemx --keep @itemx --keep
Keep (don't delete) the input file after comparing it with the lzip file. Keep (don't delete) the input file after comparing it with the lzip file.

1
rc.cc
View file

@ -62,6 +62,7 @@ const struct { const char * from; const char * to; int format_index; }
{ ".txz", ".tar", fmt_xz }, { ".txz", ".tar", fmt_xz },
{ ".zst", "", fmt_zst }, { ".zst", "", fmt_zst },
{ ".tzst", ".tar", fmt_zst }, { ".tzst", ".tar", fmt_zst },
{ ".Z", "", fmt_gz },
{ 0, 0, -1 } }; { 0, 0, -1 } };

View file

@ -321,6 +321,8 @@ done
test_failed $LINENO test_failed $LINENO
"${ZGREP}" -N -l "GNU" in in.gz in.bz2 in.lz -- -in- > /dev/null || "${ZGREP}" -N -l "GNU" in in.gz in.bz2 in.lz -- -in- > /dev/null ||
test_failed $LINENO test_failed $LINENO
"${ZGREP}" -N -l -Z "GNU" in in.gz in.bz2 in.lz -- -in- > /dev/null ||
test_failed $LINENO
"${ZGREP}" -N -L "GNU" in in.gz in.bz2 in.lz -- -in- || test_failed $LINENO "${ZGREP}" -N -L "GNU" in in.gz in.bz2 in.lz -- -in- || test_failed $LINENO
"${ZGREP}" -N -l "nx_pattern" in in.gz in.bz2 in.lz -- -in- && "${ZGREP}" -N -l "nx_pattern" in in.gz in.bz2 in.lz -- -in- &&
test_failed $LINENO test_failed $LINENO
@ -402,15 +404,15 @@ cat in.gz > a.gz || framework_failure
"${ZUPDATE}" -N --gz='gzip --bad-option' a.gz 2> /dev/null "${ZUPDATE}" -N --gz='gzip --bad-option' a.gz 2> /dev/null
[ $? = 1 ] || test_failed $LINENO [ $? = 1 ] || test_failed $LINENO
"${ZUPDATE}" -Nq --lz=bad_command a.gz "${ZUPDATE}" -Nq --lz=bad_command a.gz
[ $? = 1 ] || test_failed $LINENO [ $? = 2 ] || test_failed $LINENO
"${ZUPDATE}" -N --lz='lzip --bad-option' a.gz 2> /dev/null "${ZUPDATE}" -N --lz='lzip --bad-option' a.gz 2> /dev/null
[ $? = 1 ] || test_failed $LINENO [ $? = 2 ] || test_failed $LINENO
"${ZUPDATE}" -N --bad-option 2> /dev/null "${ZUPDATE}" -N --bad-option 2> /dev/null
[ $? = 1 ] || test_failed $LINENO [ $? = 2 ] || test_failed $LINENO
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 ] || test_failed $LINENO [ $? = 2 ] || test_failed $LINENO
[ -e a.bz2 ] || test_failed $LINENO [ -e a.bz2 ] || test_failed $LINENO
[ -e a.gz ] || test_failed $LINENO [ -e a.gz ] || test_failed $LINENO
[ -e a.lz ] || test_failed $LINENO [ -e a.lz ] || test_failed $LINENO
@ -471,10 +473,30 @@ cat in.gz > c.tgz || framework_failure
[ -e c.tlz ] || test_failed $LINENO [ -e c.tlz ] || test_failed $LINENO
rm -f a.tlz b.tlz c.tlz || framework_failure rm -f a.tlz b.tlz c.tlz || framework_failure
cat in.bz2 > a.tbz || framework_failure # expand combined extensions
cat in.bz2 > b.tbz2 || framework_failure
cat in.gz > c.tgz || framework_failure
"${ZUPDATE}" -N -e a.tbz b.tbz2 c.tgz || test_failed $LINENO
[ ! -e a.tbz ] || test_failed $LINENO
[ ! -e b.tbz2 ] || test_failed $LINENO
[ ! -e c.tgz ] || test_failed $LINENO
[ ! -e a ] || test_failed $LINENO
[ ! -e b ] || test_failed $LINENO
[ ! -e c ] || test_failed $LINENO
[ -e a.tar.lz ] || test_failed $LINENO
[ -e b.tar.lz ] || test_failed $LINENO
[ -e c.tar.lz ] || test_failed $LINENO
[ ! -e a.tlz ] || test_failed $LINENO
[ ! -e b.tlz ] || test_failed $LINENO
[ ! -e c.tlz ] || test_failed $LINENO
rm -f a.tar.lz b.tar.lz c.tar.lz || framework_failure
# test decompression error
cat in.bz2 > a.bz2 || framework_failure cat in.bz2 > a.bz2 || framework_failure
cat "${bad0_gz}" > b.gz || framework_failure cat "${bad0_gz}" > b.gz || framework_failure
cat in.gz > c.gz || framework_failure cat in.gz > c.gz || framework_failure
"${ZUPDATE}" -N -f a.bz2 b.gz c.gz 2> /dev/null && test_failed $LINENO "${ZUPDATE}" -N -f a.bz2 b.gz c.gz 2> /dev/null
[ $? = 1 ] || test_failed $LINENO
[ ! -e a.bz2 ] || test_failed $LINENO [ ! -e a.bz2 ] || test_failed $LINENO
[ -e b.gz ] || test_failed $LINENO [ -e b.gz ] || test_failed $LINENO
[ -e c.gz ] || test_failed $LINENO [ -e c.gz ] || test_failed $LINENO
@ -482,6 +504,16 @@ cat in.gz > c.gz || framework_failure
[ ! -e b ] || test_failed $LINENO [ ! -e b ] || test_failed $LINENO
[ ! -e c ] || test_failed $LINENO [ ! -e c ] || test_failed $LINENO
[ -e a.lz ] || test_failed $LINENO [ -e a.lz ] || test_failed $LINENO
# ignore error
cat in.bz2 > a.bz2 || framework_failure
cat "${bad0_gz}" > b.gz || framework_failure
cat in.gz > c.gz || framework_failure
"${ZUPDATE}" -N -f -i a.bz2 b.gz c.gz 2> /dev/null
[ $? = 1 ] || test_failed $LINENO
[ ! -e a.bz2 ] || test_failed $LINENO
[ -e b.gz ] || test_failed $LINENO
[ ! -e c.gz ] || test_failed $LINENO
[ -e a.lz ] || test_failed $LINENO
rm -f a.lz b.gz c.gz || framework_failure rm -f a.lz b.gz c.gz || framework_failure
cat in.bz2 > a.bz2 || framework_failure cat in.bz2 > a.bz2 || framework_failure

23
zcmp.cc
View file

@ -95,7 +95,7 @@ const char * format_num3( long long num )
{ {
const char * const si_prefix = "kMGTPEZY"; const char * const si_prefix = "kMGTPEZY";
const char * const binary_prefix = "KMGTPEZY"; const char * const binary_prefix = "KMGTPEZY";
enum { buffers = 8, bufsize = 4 * sizeof (long long) }; enum { buffers = 8, bufsize = 4 * sizeof num };
static char buffer[buffers][bufsize]; // circle of static buffers for printf static char buffer[buffers][bufsize]; // circle of static buffers for printf
static int current = 0; static int current = 0;
@ -408,26 +408,27 @@ int main( const int argc, const char * const argv[] )
const int code = parser.code( argind ); const int code = parser.code( argind );
if( !code ) break; // no more options if( !code ) break; // no more options
const char * const pn = parser.parsed_name( argind ).c_str(); const char * const pn = parser.parsed_name( argind ).c_str();
const std::string & arg = parser.argument( argind ); const std::string & sarg = parser.argument( argind );
const char * const arg = sarg.c_str();
switch( code ) switch( code )
{ {
case 'b': print_bytes = true; break; case 'b': print_bytes = true; break;
case 'h': show_help(); return 0; case 'h': show_help(); return 0;
case 'i': parse_ignore_initial( arg.c_str(), pn, ignore_initial ); break; case 'i': parse_ignore_initial( arg, pn, ignore_initial ); break;
case 'l': verbosity = 1; break; case 'l': verbosity = 1; break;
case 'M': parse_format_list( arg, pn ); break; case 'M': parse_format_list( sarg, pn ); break;
case 'n': max_size = getnum( arg.c_str(), pn ); break; case 'n': max_size = getnum( arg, pn ); break;
case 'N': break; case 'N': break;
case 'O': parse_format_types2( arg, pn, format_types ); break; case 'O': parse_format_types2( sarg, pn, format_types ); break;
case 'q': case 'q':
case 's': verbosity = -1; break; case 's': verbosity = -1; break;
case 'v': verbosity = 1; break; case 'v': verbosity = 1; break;
case 'V': show_version(); return 0; case 'V': show_version(); return 0;
case bz2_opt: parse_compressor( arg, fmt_bz2 ); break; case bz2_opt: parse_compressor( sarg, fmt_bz2 ); break;
case gz_opt: parse_compressor( arg, fmt_gz ); break; case gz_opt: parse_compressor( sarg, fmt_gz ); break;
case lz_opt: parse_compressor( arg, fmt_lz ); break; case lz_opt: parse_compressor( sarg, fmt_lz ); break;
case xz_opt: parse_compressor( arg, fmt_xz ); break; case xz_opt: parse_compressor( sarg, fmt_xz ); break;
case zst_opt: parse_compressor( arg, fmt_zst ); break; case zst_opt: parse_compressor( sarg, fmt_zst ); break;
default : internal_error( "uncaught option." ); default : internal_error( "uncaught option." );
} }
} // end process options } // end process options

View file

@ -89,7 +89,7 @@ void show_help()
" -U, --unified=<n> same as -u but use <n> lines of context\n" " -U, --unified=<n> same as -u but use <n> lines of context\n"
" -v, --verbose verbose mode (for --version)\n" " -v, --verbose verbose mode (for --version)\n"
" -w, --ignore-all-space ignore all white space\n" " -w, --ignore-all-space ignore all white space\n"
" -W, --width=<n> output at most <n> print columns\n" " -W, --width=<n> output at most <n> print columns (for -y)\n"
" -y, --side-by-side output in two columns\n" " -y, --side-by-side output in two columns\n"
" --bz2=<command> set compressor and options for bzip2 format\n" " --bz2=<command> set compressor and options for bzip2 format\n"
" --gz=<command> set compressor and options for gzip format\n" " --gz=<command> set compressor and options for gzip format\n"
@ -317,7 +317,8 @@ int main( const int argc, const char * const argv[] )
const int code = parser.code( argind ); const int code = parser.code( argind );
if( !code ) break; // no more options if( !code ) break; // no more options
const char * const pn = parser.parsed_name( argind ).c_str(); const char * const pn = parser.parsed_name( argind ).c_str();
const std::string & arg = parser.argument( argind ); const std::string & sarg = parser.argument( argind );
const char * const arg = sarg.c_str();
switch( code ) switch( code )
{ {
case 'a': diff_args.push_back( "-a" ); break; case 'a': diff_args.push_back( "-a" ); break;
@ -325,14 +326,14 @@ int main( const int argc, const char * const argv[] )
case 'B': diff_args.push_back( "-B" ); break; case 'B': diff_args.push_back( "-B" ); break;
case 'c': diff_args.push_back( "-c" ); break; case 'c': diff_args.push_back( "-c" ); break;
case 'C': diff_args.push_back( "-C" ); case 'C': diff_args.push_back( "-C" );
diff_args.push_back( arg.c_str() ); break; diff_args.push_back( arg ); break;
case 'd': diff_args.push_back( "-d" ); break; case 'd': diff_args.push_back( "-d" ); break;
case 'E': diff_args.push_back( "-E" ); break; case 'E': diff_args.push_back( "-E" ); break;
case 'h': show_help(); return 0; case 'h': show_help(); return 0;
case 'i': diff_args.push_back( "-i" ); break; case 'i': diff_args.push_back( "-i" ); break;
case 'M': parse_format_list( arg, pn ); break; case 'M': parse_format_list( sarg, pn ); break;
case 'N': break; case 'N': break;
case 'O': parse_format_types2( arg, pn, format_types ); break; case 'O': parse_format_types2( sarg, pn, format_types ); break;
case 'p': diff_args.push_back( "-p" ); break; case 'p': diff_args.push_back( "-p" ); break;
case 'q': diff_args.push_back( "-q" ); break; case 'q': diff_args.push_back( "-q" ); break;
case 's': diff_args.push_back( "-s" ); break; case 's': diff_args.push_back( "-s" ); break;
@ -340,18 +341,18 @@ int main( const int argc, const char * const argv[] )
case 'T': diff_args.push_back( "-T" ); break; case 'T': diff_args.push_back( "-T" ); break;
case 'u': diff_args.push_back( "-u" ); break; case 'u': diff_args.push_back( "-u" ); break;
case 'U': diff_args.push_back( "-U" ); case 'U': diff_args.push_back( "-U" );
diff_args.push_back( arg.c_str() ); break; diff_args.push_back( arg ); break;
case 'v': verbosity = 1; break; case 'v': verbosity = 1; break;
case 'V': show_version( DIFF " --version" ); return 0; case 'V': show_version( DIFF " --version" ); return 0;
case 'w': diff_args.push_back( "-w" ); break; case 'w': diff_args.push_back( "-w" ); break;
case 'W': diff_args.push_back( "-W" ); case 'W': diff_args.push_back( "-W" );
diff_args.push_back( arg.c_str() ); break; diff_args.push_back( arg ); break;
case 'y': diff_args.push_back( "-y" ); break; case 'y': diff_args.push_back( "-y" ); break;
case bz2_opt: parse_compressor( arg, fmt_bz2 ); break; case bz2_opt: parse_compressor( sarg, fmt_bz2 ); break;
case gz_opt: parse_compressor( arg, fmt_gz ); break; case gz_opt: parse_compressor( sarg, fmt_gz ); break;
case lz_opt: parse_compressor( arg, fmt_lz ); break; case lz_opt: parse_compressor( sarg, fmt_lz ); break;
case xz_opt: parse_compressor( arg, fmt_xz ); break; case xz_opt: parse_compressor( sarg, fmt_xz ); break;
case zst_opt: parse_compressor( arg, fmt_zst ); break; case zst_opt: parse_compressor( sarg, fmt_zst ); break;
default : internal_error( "uncaught option." ); default : internal_error( "uncaught option." );
} }
} // end process options } // end process options

156
zgrep.cc
View file

@ -79,26 +79,33 @@ void show_help()
" -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"
" -G, --basic-regexp <pattern> is a basic regular expression (default)\n"
" -h, --no-filename suppress the prefixing file name on output\n" " -h, --no-filename suppress the prefixing file name on output\n"
" -H, --with-filename print the file name for each match\n" " -H, --with-filename print the file name 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"
" --label=<label> use <label> as file name for standard input\n"
" --line-buffered flush output on every line\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 the format given (bz2, gz, lz, xz, zst)\n" " -O, --force-format=<fmt> force the format given (bz2, gz, lz, xz, zst)\n"
" -q, --quiet suppress all messages\n" " -P, --perl-regexp <pattern> is a Perl regular expression\n"
" -q, --quiet, --silent suppress all messages\n"
" -r, --recursive operate recursively on directories\n" " -r, --recursive operate recursively on directories\n"
" -R, --dereference-recursive recursively follow symbolic links\n" " -R, --dereference-recursive recursively follow symbolic links\n"
" -s, --no-messages suppress error messages\n" " -s, --no-messages suppress error messages\n"
" -T, --initial-tab make tabs line up (if needed)\n"
" -U, --binary don't strip CR characters at EOL (DOS/Windows)\n"
" -v, --invert-match select non-matching lines\n" " -v, --invert-match select non-matching lines\n"
" --verbose verbose mode (show error messages)\n" " --verbose verbose mode (show error messages)\n"
" -w, --word-regexp match only whole words\n" " -w, --word-regexp match only whole words\n"
" -x, --line-regexp match only whole lines\n" " -x, --line-regexp match only whole lines\n"
" -Z, --null print 0 byte (ASCII NUL) after file name\n"
" --bz2=<command> set compressor and options for bzip2 format\n" " --bz2=<command> set compressor and options for bzip2 format\n"
" --gz=<command> set compressor and options for gzip format\n" " --gz=<command> set compressor and options for gzip format\n"
" --lz=<command> set compressor and options for lzip format\n" " --lz=<command> set compressor and options for lzip format\n"
@ -110,43 +117,12 @@ void show_help()
} }
int zgrep_stdin( int infd, const int format_index,
const std::vector< const char * > & grep_args )
{
Children children;
if( !set_data_feeder( "", &infd, children, format_index ) ) return 2;
const pid_t grep_pid = fork();
if( grep_pid == 0 ) // child (grep)
{
if( dup2( infd, STDIN_FILENO ) >= 0 && close( infd ) == 0 )
{
const char ** const argv = new const char *[grep_args.size()+2];
argv[0] = GREP;
for( unsigned i = 0; i < grep_args.size(); ++i )
argv[i+1] = grep_args[i];
argv[grep_args.size()+1] = 0;
execvp( argv[0], (char **)argv );
}
show_exec_error( GREP );
_exit( 2 );
}
if( grep_pid < 0 ) // parent
{ show_fork_error( GREP ); return 2; }
int retval = wait_for_child( grep_pid, GREP );
if( !good_status( children, retval == 1 ) ) retval = 2;
if( close( infd ) != 0 )
{ show_close_error(); return 2; }
return retval;
}
int zgrep_file( int infd, const int format_index, int zgrep_file( int infd, const int format_index,
const std::string & input_filename, const std::string & input_filename,
const std::vector< const char * > & grep_args, const std::vector< const char * > & grep_args,
const int list_mode, const bool show_name ) const int list_mode, const bool initial_tab,
const bool line_buffered, const bool show_name,
const bool z_null )
{ {
Children children; Children children;
if( !set_data_feeder( input_filename, &infd, children, format_index ) ) if( !set_data_feeder( input_filename, &infd, children, format_index ) )
@ -178,34 +154,48 @@ int zgrep_file( int infd, const int format_index,
enum { buffer_size = 256 }; enum { buffer_size = 256 };
uint8_t buffer[buffer_size]; uint8_t buffer[buffer_size];
bool line_begin = true; bool line_begin = true;
while( true ) bool at_eof = false;
while( !at_eof )
{ {
const int size = readblock( fda[0], buffer, buffer_size ); int size;
if( size != buffer_size && errno ) bool error = false;
{ show_error( "Read error", errno ); return 2; } if( line_buffered )
for( size = 0; size < buffer_size; )
{ if( readblock( fda[0], buffer + size, 1 ) == 1 )
{ ++size; if( buffer[size-1] == '\n' ) break; }
else { at_eof = true; if( errno ) { error = true; } break; } }
else
{ size = readblock( fda[0], buffer, buffer_size );
if( size < buffer_size ) { at_eof = true; if( errno ) error = true; } }
if( error )
{ std::fflush( stdout ); show_error( "Read error", errno ); return 2; }
if( size > 0 && !list_mode ) if( size > 0 && !list_mode )
{ {
if( show_name ) if( show_name ) // print the file name for each match
for( int i = 0; i < size; ++i ) for( int i = 0; i < size; ++i )
{ {
if( line_begin ) if( line_begin )
{ line_begin = false; std::printf( "%s:", input_filename.c_str() ); } { line_begin = false;
if( buffer[i] == '\n' ) line_begin = true; const int len = std::printf( "%s%c", input_filename.c_str(),
z_null ? 0 : ':' );
if( initial_tab && len > 0 && len % 8 ) putchar( '\t' ); }
putchar( buffer[i] ); putchar( buffer[i] );
if( buffer[i] == '\n' )
{ line_begin = true; if( line_buffered ) std::fflush( stdout ); }
} }
else if( std::fwrite( buffer, 1, size, stdout ) != (unsigned)size ) else if( std::fwrite( buffer, 1, size, stdout ) != (unsigned)size )
{ std::fflush( stdout ); show_error( "Write error", errno ); return 2; } { std::fflush( stdout ); show_error( "Write error", errno ); return 2; }
}
}
std::fflush( stdout ); std::fflush( stdout );
}
if( size < buffer_size ) break; // end of grep's output
}
int retval = wait_for_child( grep_pid, GREP ); int retval = wait_for_child( grep_pid, GREP );
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::fflush( stdout ); } { std::printf( "%s%c", input_filename.c_str(), z_null ? 0 : '\n' );
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 )
@ -218,16 +208,21 @@ int zgrep_file( int infd, const int format_index,
int main( const int argc, const char * const argv[] ) 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, label_opt, linebuf_opt,
bz2_opt, gz_opt, lz_opt, xz_opt, zst_opt }; bz2_opt, gz_opt, lz_opt, xz_opt, zst_opt };
int format_index = -1; int format_index = -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 recursive = 0; // 1 = '-r', 2 = '-R'
int show_name = -1; // tri-state bool int show_name = -1; // tri-state bool
bool initial_tab = false;
bool line_buffered = false;
bool no_messages = false; bool no_messages = false;
bool z_null = false; // for '-Z, --null'
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; // additional args to grep
std::string label_option;
std::string label = "(standard input)"; // prefix for standard input
program_name = "zgrep"; program_name = "zgrep";
invocation_name = ( argc > 0 ) ? argv[0] : program_name; invocation_name = ( argc > 0 ) ? argv[0] : program_name;
@ -243,6 +238,7 @@ int main( const int argc, const char * const argv[] )
{ '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
{ 'G', "basic-regexp", Arg_parser::no }, // grep GNU
{ '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
@ -255,17 +251,24 @@ int main( const int argc, const char * const argv[] )
{ '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 },
{ 'P', "perl-regexp", Arg_parser::no }, // grep GNU
{ 'q', "quiet", Arg_parser::no }, { 'q', "quiet", Arg_parser::no },
{ 'q', "silent", Arg_parser::no },
{ 'r', "recursive", Arg_parser::no }, { 'r', "recursive", Arg_parser::no },
{ 'R', "dereference-recursive", Arg_parser::no }, { 'R', "dereference-recursive", Arg_parser::no },
{ 's', "no-messages", Arg_parser::no }, // grep { 's', "no-messages", Arg_parser::no }, // grep
{ 'T', "initial-tab", Arg_parser::no }, // grep GNU
{ 'U', "binary", Arg_parser::no }, // grep GNU
{ 'v', "invert-match", Arg_parser::no }, // grep { 'v', "invert-match", Arg_parser::no }, // grep
{ 'V', "version", Arg_parser::no }, { 'V', "version", Arg_parser::no },
{ 'w', "word-regexp", Arg_parser::no }, // grep GNU { 'w', "word-regexp", Arg_parser::no }, // grep GNU
{ 'x', "line-regexp", Arg_parser::no }, // grep { 'x', "line-regexp", Arg_parser::no }, // grep
{ 'Z', "null", Arg_parser::no }, // grep GNU
{ help_opt, "help", Arg_parser::no }, { help_opt, "help", Arg_parser::no },
{ verbose_opt, "verbose", Arg_parser::no }, { verbose_opt, "verbose", Arg_parser::no },
{ color_opt, "color", Arg_parser::maybe }, { color_opt, "color", Arg_parser::maybe },
{ label_opt, "label", Arg_parser::yes },
{ linebuf_opt, "line-buffered", Arg_parser::no },
{ bz2_opt, "bz2", Arg_parser::yes }, { bz2_opt, "bz2", Arg_parser::yes },
{ gz_opt, "gz", Arg_parser::yes }, { gz_opt, "gz", Arg_parser::yes },
{ lz_opt, "lz", Arg_parser::yes }, { lz_opt, "lz", Arg_parser::yes },
@ -286,24 +289,26 @@ int main( const int argc, const char * const argv[] )
const int code = parser.code( argind ); const int code = parser.code( argind );
if( !code ) break; // no more options if( !code ) break; // no more options
const char * const pn = parser.parsed_name( argind ).c_str(); const char * const pn = parser.parsed_name( argind ).c_str();
const std::string & arg = parser.argument( argind ); const std::string & sarg = parser.argument( argind );
const char * const arg = sarg.c_str();
switch( code ) switch( code )
{ {
case 'a': grep_args.push_back( "-a" ); break; case 'a': grep_args.push_back( "-a" ); break;
case 'A': grep_args.push_back( "-A" ); case 'A': grep_args.push_back( "-A" );
grep_args.push_back( arg.c_str() ); break; grep_args.push_back( arg ); break;
case 'b': grep_args.push_back( "-b" ); break; case 'b': grep_args.push_back( "-b" ); break;
case 'B': grep_args.push_back( "-B" ); case 'B': grep_args.push_back( "-B" );
grep_args.push_back( arg.c_str() ); break; grep_args.push_back( arg ); break;
case 'c': grep_args.push_back( "-c" ); break; case 'c': grep_args.push_back( "-c" ); break;
case 'C': grep_args.push_back( "-C" ); case 'C': grep_args.push_back( "-C" );
grep_args.push_back( arg.c_str() ); break; grep_args.push_back( arg ); break;
case 'e': grep_args.push_back( "-e" ); case 'e': grep_args.push_back( "-e" );
grep_args.push_back( arg.c_str() ); pattern_found = true; break; grep_args.push_back( arg ); pattern_found = true; break;
case 'E': grep_args.push_back( "-E" ); break; case 'E': grep_args.push_back( "-E" ); break;
case 'f': grep_args.push_back( "-f" ); case 'f': grep_args.push_back( "-f" );
grep_args.push_back( arg.c_str() ); pattern_found = true; break; grep_args.push_back( arg ); pattern_found = true; break;
case 'F': grep_args.push_back( "-F" ); break; case 'F': grep_args.push_back( "-F" ); break;
case 'G': grep_args.push_back( "-G" ); break;
case 'h': show_name = false; break; case 'h': show_name = false; break;
case 'H': show_name = true; break; case 'H': show_name = true; break;
case 'i': grep_args.push_back( "-i" ); break; case 'i': grep_args.push_back( "-i" ); break;
@ -311,37 +316,46 @@ int main( const int argc, const char * const argv[] )
case 'l': grep_args.push_back( "-l" ); list_mode = 1; break; case 'l': grep_args.push_back( "-l" ); list_mode = 1; break;
case 'L': grep_args.push_back( "-L" ); list_mode = -1; break; case 'L': grep_args.push_back( "-L" ); list_mode = -1; break;
case 'm': grep_args.push_back( "-m" ); case 'm': grep_args.push_back( "-m" );
grep_args.push_back( arg.c_str() ); break; grep_args.push_back( arg ); break;
case 'M': parse_format_list( arg, pn ); break; case 'M': parse_format_list( sarg, pn ); break;
case 'n': grep_args.push_back( "-n" ); break; case 'n': grep_args.push_back( "-n" ); break;
case 'N': break; case 'N': break;
case 'o': grep_args.push_back( "-o" ); break; case 'o': grep_args.push_back( "-o" ); break;
case 'O': format_index = parse_format_type( arg, pn ); break; case 'O': format_index = parse_format_type( sarg, pn ); break;
case 'P': grep_args.push_back( "-P" ); break;
case 'q': grep_args.push_back( "-q" ); verbosity = -1; break; case 'q': grep_args.push_back( "-q" ); verbosity = -1; break;
case 'r': recursive = 1; break; case 'r': recursive = 1; break;
case 'R': recursive = 2; 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 'T': grep_args.push_back( "-T" ); initial_tab = true; break;
case 'U': grep_args.push_back( "-U" ); break;
case 'v': grep_args.push_back( "-v" ); break; case 'v': grep_args.push_back( "-v" ); break;
case 'V': show_version( GREP " --version" ); return 0; case 'V': show_version( GREP " --version" ); return 0;
case 'w': grep_args.push_back( "-w" ); break; case 'w': grep_args.push_back( "-w" ); break;
case 'x': grep_args.push_back( "-x" ); break; case 'x': grep_args.push_back( "-x" ); break;
case 'Z': z_null = true; break;
case help_opt: show_help(); return 0; case help_opt: show_help(); return 0;
case verbose_opt: no_messages = false; if( verbosity < 4 ) ++verbosity; case verbose_opt: no_messages = false; if( verbosity < 4 ) ++verbosity;
break; break;
case color_opt: color_option = "--color"; case color_opt: color_option = "--color";
if( !arg.empty() ) { color_option += '='; color_option += arg; } if( !sarg.empty() ) { color_option += '='; color_option += sarg; }
break; break;
case bz2_opt: parse_compressor( arg, fmt_bz2 ); break; case label_opt: label_option = label = sarg; break;
case gz_opt: parse_compressor( arg, fmt_gz ); break; case linebuf_opt: grep_args.push_back( "--line-buffered" );
case lz_opt: parse_compressor( arg, fmt_lz ); break; line_buffered = true; break;
case xz_opt: parse_compressor( arg, fmt_xz ); break; case bz2_opt: parse_compressor( sarg, fmt_bz2 ); break;
case zst_opt: parse_compressor( arg, fmt_zst ); break; case gz_opt: parse_compressor( sarg, fmt_gz ); break;
case lz_opt: parse_compressor( sarg, fmt_lz ); break;
case xz_opt: parse_compressor( sarg, fmt_xz ); break;
case zst_opt: parse_compressor( sarg, fmt_zst ); break;
default : internal_error( "uncaught option." ); default : internal_error( "uncaught option." );
} }
} // end process options } // end process options
if( !color_option.empty() ) // push the last value set if( !color_option.empty() ) // push the last value set
grep_args.push_back( color_option.c_str() ); grep_args.push_back( color_option.c_str() );
if( !label_option.empty() ) // for "Binary file <label> matches"
grep_args.push_back( label_option.insert( 0, "--label=" ).c_str() );
#if defined __MSVCRT__ || defined __OS2__ #if defined __MSVCRT__ || defined __OS2__
setmode( STDIN_FILENO, O_BINARY ); setmode( STDIN_FILENO, O_BINARY );
@ -352,9 +366,9 @@ int main( const int argc, const char * const argv[] )
{ {
if( argind >= parser.arguments() ) if( argind >= parser.arguments() )
{ show_error( "Pattern not found." ); return 2; } { show_error( "Pattern not found." ); return 2; }
const std::string & arg = parser.argument( argind++ ); const std::string & pat = parser.argument( argind++ );
if( arg.size() && arg[0] == '-' ) grep_args.push_back( "-e" ); if( pat.size() && pat[0] == '-' ) grep_args.push_back( "-e" );
grep_args.push_back( arg.c_str() ); grep_args.push_back( pat.c_str() );
} }
for( ; argind < parser.arguments(); ++argind ) for( ; argind < parser.arguments(); ++argind )
@ -375,7 +389,7 @@ int main( const int argc, const char * const argv[] )
if( input_filename == "." ) if( input_filename == "." )
{ {
if( stdin_used ) continue; else stdin_used = true; if( stdin_used ) continue; else stdin_used = true;
infd = STDIN_FILENO; input_filename = "-"; infd = STDIN_FILENO; input_filename = label;
} }
else else
{ {
@ -383,11 +397,9 @@ int main( const int argc, const char * const argv[] )
if( infd < 0 ) { error = true; continue; } if( infd < 0 ) { error = true; continue; }
} }
int tmp; const int tmp = zgrep_file( infd, format_index, input_filename, grep_args,
if( infd == STDIN_FILENO ) list_mode, initial_tab, line_buffered,
tmp = zgrep_stdin( infd, format_index, grep_args ); show_name, z_null );
else tmp = zgrep_file( infd, format_index, input_filename, grep_args,
list_mode, show_name );
if( tmp == 0 || ( tmp == 2 && retval == 1 ) ) retval = tmp; if( tmp == 0 || ( tmp == 2 && retval == 1 ) ) retval = tmp;
if( close( infd ) != 0 ) if( close( infd ) != 0 )

View file

@ -67,15 +67,20 @@ void show_help()
"to be safe and not cause any data loss. Therefore, existing lzip\n" "to be safe and not cause any data loss. Therefore, existing lzip\n"
"compressed files are never overwritten nor deleted.\n" "compressed files are never overwritten nor deleted.\n"
"\nThe names of the original files must have one of the following extensions:\n" "\nThe names of the original files must have one of the following extensions:\n"
"\n'.bz2', '.gz', '.xz', or '.zst', which are recompressed to '.lz'.\n" "\n'.bz2', '.gz', '.xz', '.zst', or '.Z', which are recompressed to '.lz'.\n"
"\n'.tbz', '.tbz2', '.tgz', '.txz', or '.tzst', which are recompressed to '.tlz'.\n" "\n'.tbz', '.tbz2', '.tgz', '.txz', or '.tzst', which are recompressed to '.tlz'.\n"
"\nUsage: zupdate [options] [files]\n" "\nUsage: zupdate [options] [files]\n"
"\nExit status is 0 if all the compressed files were successfully recompressed\n" "\nExit status is 0 if all the compressed files were successfully recompressed\n"
"(if needed), compared, and deleted (if requested). Non-zero otherwise.\n" "(if needed), compared, and deleted (if requested). 1 if a non-fatal error\n"
"occurred (file not found or not regular, or has invalid format, or can't be\n"
"deleted). 2 if a fatal error occurred (compressor can't be run, or\n"
"comparison fails).\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"
" -e, --expand-extensions expand combined extensions; tgz -> tar.lz\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"
" -i, --ignore-errors ignore non-fatal errors\n"
" -k, --keep keep (don't delete) input files\n" " -k, --keep keep (don't delete) input files\n"
" -l, --lzip-verbose pass one option -v to the lzip compressor\n" " -l, --lzip-verbose pass one option -v 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"
@ -94,7 +99,7 @@ void show_help()
} }
int cant_execute( const std::string & command, const int status ) void cant_execute( const std::string & command, const int status )
{ {
if( verbosity >= 0 ) if( verbosity >= 0 )
{ {
@ -105,7 +110,6 @@ int cant_execute( const std::string & command, const int status )
std::fprintf( stderr, "%s: Can't execute '%s'\n", std::fprintf( stderr, "%s: Can't execute '%s'\n",
program_name, command.c_str() ); program_name, command.c_str() );
} }
return 1;
} }
@ -130,11 +134,11 @@ void set_permissions( const char * const rname, const struct stat & in_stats )
} }
// Return 0 if success, -1 if file skipped, 1 if error. // Return value: 0 = success, -1 = file skipped, 1 = error, 2 = fatal error.
int zupdate_file( const std::string & name, const char * const lzip_name, int zupdate_file( const std::string & name, const char * const lzip_name,
const std::vector< std::string > & lzip_args2, const std::vector< std::string > & lzip_args2,
const bool force, const bool keep_input_files, const bool expand, const bool force,
const bool no_rcfile ) const bool keep_input_files, const bool no_rcfile )
{ {
// bzip2, gzip, and lzip are the primary formats. xz and zstd are optional. // bzip2, gzip, and lzip are the primary formats. xz and zstd are optional.
static int disable_xz = -1; // tri-state bool static int disable_xz = -1; // tri-state bool
@ -155,7 +159,7 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
} }
rname.assign( name, 0, name.size() - std::strlen( extension_from( eindex ) ) ); rname.assign( name, 0, name.size() - std::strlen( extension_from( eindex ) ) );
rname += ( std::strcmp( extension_to( eindex ), ".tar" ) == 0 ) ? rname += ( std::strcmp( extension_to( eindex ), ".tar" ) == 0 ) ?
".tlz" : ".lz"; // keep combined extension ( expand ? ".tar.lz" : ".tlz" ) : ".lz";
} }
const char * const compressor_name = get_compressor_name( format_index ); const char * const compressor_name = get_compressor_name( format_index );
if( !compressor_name ) if( !compressor_name )
@ -226,7 +230,7 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
std::fprintf( stderr, "Recompressing file '%s'\n", name.c_str() ); std::fprintf( stderr, "Recompressing file '%s'\n", name.c_str() );
int fda[2]; // pipe between decompressor and compressor int fda[2]; // pipe between decompressor and compressor
if( pipe( fda ) < 0 ) if( pipe( fda ) < 0 )
{ show_error( "Can't create pipe", errno ); return 1; } { show_error( "Can't create pipe", errno ); return 2; }
const pid_t pid = fork(); const pid_t pid = fork();
if( pid == 0 ) // child1 (decompressor) if( pid == 0 ) // child1 (decompressor)
@ -250,7 +254,7 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
_exit( 1 ); _exit( 1 );
} }
if( pid < 0 ) // parent if( pid < 0 ) // parent
{ show_fork_error( compressor_name ); return 1; } { show_fork_error( compressor_name ); return 2; }
const pid_t pid2 = fork(); const pid_t pid2 = fork();
if( pid2 == 0 ) // child2 (lzip compressor) if( pid2 == 0 ) // child2 (lzip compressor)
@ -276,19 +280,19 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
_exit( 1 ); _exit( 1 );
} }
if( pid2 < 0 ) // parent if( pid2 < 0 ) // parent
{ show_fork_error( lzip_name ); return 1; } { show_fork_error( lzip_name ); return 2; }
close( fda[0] ); close( fda[1] ); close( fda[0] ); close( fda[1] );
int retval = wait_for_child( pid, compressor_name ); const int retval = wait_for_child( pid, compressor_name );
int retval2 = wait_for_child( pid2, lzip_name ); const int retval2 = wait_for_child( pid2, lzip_name );
if( retval || retval2 ) if( retval || retval2 )
{ if( !lz_lz_exists ) std::remove( rname2.c_str() ); // lzip < 1.20 { if( !lz_lz_exists ) std::remove( rname2.c_str() ); // lzip < 1.20
std::remove( rname.c_str() ); return 1; } std::remove( rname.c_str() ); return retval2 ? 2 : 1; }
if( stat( rname.c_str(), &st ) != 0 && if( stat( rname.c_str(), &st ) != 0 &&
( lz_lz_exists || stat( rname2.c_str(), &st ) != 0 || ( lz_lz_exists || stat( rname2.c_str(), &st ) != 0 ||
std::rename( rname2.c_str(), rname.c_str() ) != 0 ) ) std::rename( rname2.c_str(), rname.c_str() ) != 0 ) )
{ show_file_error( rname.c_str(), "Error renaming output file", errno ); { show_file_error( rname.c_str(), "Error renaming output file", errno );
return 1; } // lzip < 1.11 return 2; } // lzip < 1.11
set_permissions( rname.c_str(), in_stats ); set_permissions( rname.c_str(), in_stats );
} }
@ -297,7 +301,7 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
std::fprintf( stderr, "Comparing file '%s'\n", name.c_str() ); std::fprintf( stderr, "Comparing file '%s'\n", name.c_str() );
std::string zcmp_command( invocation_name ); std::string zcmp_command( invocation_name );
unsigned i = zcmp_command.size(); unsigned i = zcmp_command.size();
while( i > 0 && zcmp_command[i-1] != '/' ) --i; while( i > 0 && zcmp_command[i-1] != '/' ) --i; // strip "zupdate"
zcmp_command.resize( i ); zcmp_command.insert( zcmp_command.begin(), '\'' ); zcmp_command.resize( i ); zcmp_command.insert( zcmp_command.begin(), '\'' );
zcmp_command += "zcmp' "; // '[dir/]zcmp' zcmp_command += "zcmp' "; // '[dir/]zcmp'
if( no_rcfile ) zcmp_command += "-N "; if( no_rcfile ) zcmp_command += "-N ";
@ -307,7 +311,7 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
int status = std::system( zcmp_command.c_str() ); int status = std::system( zcmp_command.c_str() );
if( status != 0 ) if( status != 0 )
{ if( !lz_exists ) std::remove( rname.c_str() ); { if( !lz_exists ) std::remove( rname.c_str() );
return cant_execute( zcmp_command, status ); } cant_execute( zcmp_command, status ); return 2; }
} }
if( !keep_input_files && std::remove( name.c_str() ) != 0 && errno != ENOENT ) if( !keep_input_files && std::remove( name.c_str() ) != 0 && errno != ENOENT )
@ -329,7 +333,9 @@ int main( const int argc, const char * const argv[] )
int recursive = 0; // 1 = '-r', 2 = '-R' 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 expand = false;
bool force = false; bool force = false;
bool ignore_errors = false;
bool keep_input_files = false; bool keep_input_files = false;
bool no_rcfile = false; bool no_rcfile = false;
program_name = "zupdate"; program_name = "zupdate";
@ -347,8 +353,10 @@ int main( const int argc, const char * const argv[] )
{ '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 },
{ 'e', "expand-extensions", Arg_parser::no },
{ 'f', "force", Arg_parser::no }, { 'f', "force", Arg_parser::no },
{ 'h', "help", Arg_parser::no }, { 'h', "help", Arg_parser::no },
{ 'i', "ignore-errors", 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 },
@ -367,7 +375,7 @@ int main( const int argc, const char * const argv[] )
const Arg_parser parser( argc, argv, options ); const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option if( parser.error().size() ) // bad option
{ show_error( parser.error().c_str(), 0, true ); return 1; } { show_error( parser.error().c_str(), 0, true ); return 2; }
maybe_process_config_file( parser ); maybe_process_config_file( parser );
@ -383,8 +391,10 @@ int main( const int argc, const char * const argv[] )
case '0': case '1': case '2': case '3': case '4': case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9': case '5': case '6': case '7': case '8': case '9':
lzip_args2.push_back( "-" ); lzip_args2.back() += code; break; lzip_args2.push_back( "-" ); lzip_args2.back() += code; break;
case 'e': expand = true; break;
case 'f': force = true; break; case 'f': force = true; break;
case 'h': show_help(); return 0; case 'h': show_help(); return 0;
case 'i': ignore_errors = true; break;
case 'k': keep_input_files = true; break; case 'k': keep_input_files = true; break;
case 'l': lzip_args2.push_back( "-v" ); break; case 'l': lzip_args2.push_back( "-v" ); break;
case 'M': parse_format_list( arg, pn ); break; case 'M': parse_format_list( arg, pn ); break;
@ -410,7 +420,7 @@ int main( const int argc, const char * const argv[] )
const char * const lzip_name = get_compressor_name( fmt_lz ); const char * const lzip_name = get_compressor_name( fmt_lz );
if( !lzip_name ) if( !lzip_name )
{ show_error( "Missing name of compressor for lzip format." ); return 1; } { show_error( "Missing name of compressor for lzip format." ); return 2; }
for( ; argind < parser.arguments(); ++argind ) for( ; argind < parser.arguments(); ++argind )
filenames.push_back( parser.argument( argind ) ); filenames.push_back( parser.argument( argind ) );
@ -422,11 +432,11 @@ int main( const int argc, const char * const argv[] )
bool error = false; bool error = false;
while( next_filename( filenames, input_filename, error, recursive, true ) ) while( next_filename( filenames, input_filename, error, recursive, true ) )
{ {
int tmp = zupdate_file( input_filename, lzip_name, lzip_args2, force, int tmp = zupdate_file( input_filename, lzip_name, lzip_args2, expand,
keep_input_files, no_rcfile ); force, keep_input_files, no_rcfile );
if( tmp < 0 ) error = true; if( tmp < 0 ) error = true;
if( tmp > retval ) retval = tmp; if( tmp > retval ) retval = tmp;
if( tmp > 0 ) break; if( tmp >= 2 || ( tmp == 1 && !ignore_errors ) ) break;
} }
if( error && retval == 0 ) retval = 1; if( error && retval == 0 ) retval = 1;
return retval; return retval;

View file

@ -255,11 +255,14 @@ int test_format( const int infd, uint8_t magic_data[],
gzip_magic_size = 2, gzip_magic_size = 2,
lzip_magic_size = 5, lzip_magic_size = 5,
xz_magic_size = 5, xz_magic_size = 5,
zstd_magic_size = 4 }; zstd_magic_size = 4,
compress_magic_size = 2 };
const uint8_t bzip2_magic[bzip2_magic_size] = const uint8_t bzip2_magic[bzip2_magic_size] =
{ 0x42, 0x5A, 0x68 }; // "BZh" { 0x42, 0x5A, 0x68 }; // "BZh"
const uint8_t gzip_magic[gzip_magic_size] = const uint8_t gzip_magic[gzip_magic_size] =
{ 0x1F, 0x8B }; { 0x1F, 0x8B };
const uint8_t compress_magic[compress_magic_size] =
{ 0x1F, 0x9D };
const uint8_t lzip_magic[lzip_magic_size] = const uint8_t lzip_magic[lzip_magic_size] =
{ 0x4C, 0x5A, 0x49, 0x50, 0x01 }; // "LZIP\001" { 0x4C, 0x5A, 0x49, 0x50, 0x01 }; // "LZIP\001"
const uint8_t xz_magic[xz_magic_size] = const uint8_t xz_magic[xz_magic_size] =
@ -277,7 +280,8 @@ int test_format( const int infd, uint8_t magic_data[],
magic_data[3] >= '1' && magic_data[3] <= '9' && magic_data[3] >= '1' && magic_data[3] <= '9' &&
std::memcmp( magic_data + 4, "1AY&SY", 6 ) == 0 ) std::memcmp( magic_data + 4, "1AY&SY", 6 ) == 0 )
return fmt_bz2; return fmt_bz2;
if( std::memcmp( magic_data, gzip_magic, gzip_magic_size ) == 0 ) if( std::memcmp( magic_data, gzip_magic, gzip_magic_size ) == 0 ||
std::memcmp( magic_data, compress_magic, compress_magic_size ) == 0 )
return fmt_gz; return fmt_gz;
if( std::memcmp( magic_data, zstd_magic, zstd_magic_size ) == 0 ) if( std::memcmp( magic_data, zstd_magic, zstd_magic_size ) == 0 )
return fmt_zst; return fmt_zst;