summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2022-07-26 05:52:19 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2022-07-26 05:52:47 +0000
commit274c86b0397198011756479218dd14262d9421ff (patch)
tree47cd5cedf3cfcbba77d127200852a21d03502e7c
parentReleasing debian version 1.11-5. (diff)
downloadzutils-274c86b0397198011756479218dd14262d9421ff.tar.xz
zutils-274c86b0397198011756479218dd14262d9421ff.zip
Merging upstream version 1.12~pre2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--ChangeLog23
-rw-r--r--NEWS38
-rw-r--r--README7
-rwxr-xr-xconfigure2
-rw-r--r--doc/zcat.12
-rw-r--r--doc/zcmp.12
-rw-r--r--doc/zdiff.14
-rw-r--r--doc/zgrep.125
-rw-r--r--doc/ztest.12
-rw-r--r--doc/zupdate.115
-rw-r--r--doc/zutils.info118
-rw-r--r--doc/zutils.texi97
-rw-r--r--rc.cc1
-rwxr-xr-xtestsuite/check.sh42
-rw-r--r--zcmp.cc23
-rw-r--r--zdiff.cc25
-rw-r--r--zgrep.cc154
-rw-r--r--zupdate.cc56
-rw-r--r--zutils.cc8
19 files changed, 422 insertions, 222 deletions
diff --git a/ChangeLog b/ChangeLog
index 148bc9e..4f12393 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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>
* Version 1.11 released.
* 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.
* zcmp.cc (getnum): Show option name and valid range if error.
- * All tools: Show option name if error in option argument.
- * Add support for zstd format to all tools.
+ * All utilities: Show option name if error in option argument.
+ * Add support for zstd format to all utilities.
* 'zdiff -v -V' now prints the version of the diff program used.
* 'zgrep --verbose -V' now prints the version of the grep program used.
* zutils.texi: Document recompression of read-only files by linking.
@@ -18,7 +33,7 @@
* zdiff.cc (set_fifonames): Encode pid in little endian order.
* 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.
- * 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>
@@ -95,7 +110,7 @@
* Version 1.2 released.
* 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.
* zutils.texinfo: Rename to zutils.texi.
diff --git a/NEWS b/NEWS
index 249e8cf..a4bee06 100644
--- a/NEWS
+++ b/NEWS
@@ -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
-(files differ) instead of 2 (trouble) when a compressor is not found or when
-the wrong format is forced.
+zgrep now also accepts the following options: '-G, --basic-regexp',
+'--label=<label>', '--line-buffered', '-P, --perl-regexp', '--silent',
+'-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
-the name of the option.
+zupdate now accepts option '-e, --expand-extensions', which makes it expand
+combined file name extensions; tgz -> tar.lz.
-In case of error in a numerical argument to a command line option, zcmp
-now shows the name of the option and the range of valid values.
+zupdate now also accepts option '-i, --ignore-errors', which makes it ignore
+non-fatal errors.
-Support for the zstd format has been added to all tools. This allows, among
-other things, zupdating zstd files to lzip format for long-term archiving,
-and using zcmp along with the unzcrash tool (from the lziprecover package)
-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).
+All utilities now support compress'd (.Z) files through gzip. For this to
+work, the gzip program used (for example GNU gzip) must be able to
+decompress .Z files.
diff --git a/README b/README
index 73d6348..57fccff 100644
--- a/README
+++ b/README
@@ -30,10 +30,9 @@ 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'.
-FORMAT NOTE 2: If the option '--force-format' is given, the files are
-passed to the corresponding decompressor without verifying their format,
-allowing for example the processing of compress'd (.Z) files with gzip:
-'zcmp --force-format=gz file.Z file.lz'.
+FORMAT NOTE 2: The standard POSIX compress format (.Z) is obsolete and is
+only supported through gzip. For this to work, the gzip program used (for
+example GNU gzip) must be able to decompress .Z files.
LANGUAGE NOTE: Uncompressed = not compressed = plain data; it may never have
been compressed. Decompressed is used to refer to data which have undergone
diff --git a/configure b/configure
index a9746d0..4f1c2a3 100755
--- a/configure
+++ b/configure
@@ -6,7 +6,7 @@
# to copy, distribute, and modify it.
pkgname=zutils
-pkgversion=1.11
+pkgversion=1.12-pre2
srctrigger=doc/${pkgname}.texi
# clear some things potentially inherited from environment.
diff --git a/doc/zcat.1 b/doc/zcat.1
index 67b580c..b78342f 100644
--- a/doc/zcat.1
+++ b/doc/zcat.1
@@ -1,5 +1,5 @@
.\" 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
zcat \- decompress and concatenate files to standard output
.SH SYNOPSIS
diff --git a/doc/zcmp.1 b/doc/zcmp.1
index 3f23287..58a0197 100644
--- a/doc/zcmp.1
+++ b/doc/zcmp.1
@@ -1,5 +1,5 @@
.\" 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
zcmp \- decompress and compare two files byte by byte
.SH SYNOPSIS
diff --git a/doc/zdiff.1 b/doc/zdiff.1
index 784ef7b..55342c6 100644
--- a/doc/zdiff.1
+++ b/doc/zdiff.1
@@ -1,5 +1,5 @@
.\" 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
zdiff \- decompress and compare two files line by line
.SH SYNOPSIS
@@ -98,7 +98,7 @@ verbose mode (for \fB\-\-version\fR)
ignore all white space
.TP
\fB\-W\fR, \fB\-\-width=\fR<n>
-output at most <n> print columns
+output at most <n> print columns (for \fB\-y\fR)
.TP
\fB\-y\fR, \fB\-\-side\-by\-side\fR
output in two columns
diff --git a/doc/zgrep.1 b/doc/zgrep.1
index f3177bb..bbe311c 100644
--- a/doc/zgrep.1
+++ b/doc/zgrep.1
@@ -1,5 +1,5 @@
.\" 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
zgrep \- search compressed files for a regular expression
.SH SYNOPSIS
@@ -69,6 +69,9 @@ obtain patterns from <file>
\fB\-F\fR, \fB\-\-fixed\-strings\fR
<pattern> is a set of newline\-separated strings
.TP
+\fB\-G\fR, \fB\-\-basic\-regexp\fR
+<pattern> is a basic regular expression (default)
+.TP
\fB\-h\fR, \fB\-\-no\-filename\fR
suppress the prefixing file name on output
.TP
@@ -87,6 +90,12 @@ only print names of files containing matches
\fB\-L\fR, \fB\-\-files\-without\-match\fR
only print names of files containing no matches
.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>
stop after <n> matches
.TP
@@ -105,7 +114,10 @@ show only the part of a line matching <pattern>
\fB\-O\fR, \fB\-\-force\-format=\fR<fmt>
force the format given (bz2, gz, lz, xz, zst)
.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
.TP
\fB\-r\fR, \fB\-\-recursive\fR
@@ -117,6 +129,12 @@ recursively follow symbolic links
\fB\-s\fR, \fB\-\-no\-messages\fR
suppress error messages
.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
select non\-matching lines
.TP
@@ -129,6 +147,9 @@ match only whole words
\fB\-x\fR, \fB\-\-line\-regexp\fR
match only whole lines
.TP
+\fB\-Z\fR, \fB\-\-null\fR
+print 0 byte (ASCII NUL) after file name
+.TP
\fB\-\-bz2=\fR<command>
set compressor and options for bzip2 format
.TP
diff --git a/doc/ztest.1 b/doc/ztest.1
index 210948e..2f51e51 100644
--- a/doc/ztest.1
+++ b/doc/ztest.1
@@ -1,5 +1,5 @@
.\" 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
ztest \- verify the integrity of compressed files
.SH SYNOPSIS
diff --git a/doc/zupdate.1 b/doc/zupdate.1
index b1e6472..8519088 100644
--- a/doc/zupdate.1
+++ b/doc/zupdate.1
@@ -1,5 +1,5 @@
.\" 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
zupdate \- recompress bzip2, gzip, xz, zstd files to lzip format
.SH SYNOPSIS
@@ -26,12 +26,15 @@ compressed files are never overwritten nor deleted.
.PP
The names of the original files must have one of the following extensions:
.PP
-\&'.bz2', '.gz', '.xz', or '.zst', which are recompressed to '.lz'.
+\&'.bz2', '.gz', '.xz', '.zst', or '.Z', which are recompressed to '.lz'.
.PP
\&'.tbz', '.tbz2', '.tgz', '.txz', or '.tzst', which are recompressed to '.tlz'.
.PP
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
.TP
\fB\-h\fR, \fB\-\-help\fR
@@ -40,9 +43,15 @@ display this help and exit
\fB\-V\fR, \fB\-\-version\fR
output version information and exit
.TP
+\fB\-e\fR, \fB\-\-expand\-extensions\fR
+expand combined extensions; tgz \-> tar.lz
+.TP
\fB\-f\fR, \fB\-\-force\fR
don't skip a file even if the .lz exists
.TP
+\fB\-i\fR, \fB\-\-ignore\-errors\fR
+ignore non\-fatal errors
+.TP
\fB\-k\fR, \fB\-\-keep\fR
keep (don't delete) input files
.TP
diff --git a/doc/zutils.info b/doc/zutils.info
index a75b183..9a9ca05 100644
--- a/doc/zutils.info
+++ b/doc/zutils.info
@@ -11,7 +11,7 @@ File: zutils.info, Node: Top, Next: Introduction, Up: (dir)
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:
@@ -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
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
-passed to the corresponding decompressor without verifying their format,
-allowing for example the processing of compress'd (.Z) files with gzip:
-'zcmp --force-format=gz file.Z file.lz'.
+ FORMAT NOTE 2: The standard POSIX compress format (.Z) is obsolete and is
+only supported through gzip. For this to work, the gzip program used (for
+example GNU gzip) must be able to decompress .Z files.
LANGUAGE NOTE: Uncompressed = not compressed = plain data; it may never
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:
bz2 enables .bz2 .tbz .tbz2
- gz enables .gz .tgz
+ gz enables .gz .tgz .Z
lz enables .lz .tlz
xz enables .xz .txz
zst enables .zst .tzst
@@ -468,6 +467,15 @@ program used supports them):
'--ignore-all-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
@@ -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
were found, and 2 means trouble.
- zgrep supports the following options (some options only work if the grep
-program used supports them):
+ zgrep supports the following options (Some options only work if the grep
+program used supports them. Options -h, -H, -r, -R, and -Z are managed by
+zgrep and not passed to grep):
'-a'
'--text'
@@ -532,7 +541,7 @@ program used supports them):
'-E'
'--extended-regexp'
- Treat PATTERN as an extended regular expression.
+ Interpret PATTERN as an extended regular expression (ERE).
'-f FILE'
'--file=FILE'
@@ -543,7 +552,12 @@ program used supports them):
'-F'
'--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'
'--no-filename'
@@ -573,6 +587,13 @@ program used supports them):
of a wrong change in the exit status of grep, which was reverted in
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'
'--max-count=N'
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
tried.
+'-P'
+'--perl-regexp'
+ Interpret PATTERN as a Perl-compatible regular expression (PCRE).
+
'-q'
'--quiet'
+'--silent'
Suppress all messages. Exit immediately with zero status if any match
is found, even if an error was detected.
@@ -613,6 +639,16 @@ program used supports them):
'--no-messages'
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'
'--invert-match'
Select non-matching lines.
@@ -629,6 +665,14 @@ program used supports them):
'--line-regexp'
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
@@ -675,9 +719,7 @@ compressed file is corrupt or invalid.
'gz', 'lz', 'xz', and 'zst'. If this option is used, the files are
passed to the corresponding decompressor without verifying their
format, and any files in a format that the decompressor can't
- understand will fail. For example, '--force-format=gz' can test
- gzipped (.gz) and compress'd (.Z) files if the compressor used is GNU
- gzip.
+ understand will fail.
'-q'
'--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
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
'.tlz'.
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
corresponding files are ignored.
- Recompressing a file is much like copying or moving it; therefore zupdate
-preserves the access and modification dates, permissions, and, 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 and
-S_ISGID are cleared).
+ Recompressing a file is much like copying or moving it. Therefore zupdate
+preserves the access and modification dates, permissions, and, if you have
+appropriate privileges, 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 and S_ISGID are cleared).
The format for running zupdate is:
zupdate [OPTIONS] [FILES]
Exit status is 0 if all the compressed files were successfully recompressed
-(if needed), compared, and deleted (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:
+'-e'
+'--expand-extensions'
+ Expand combined file name extensions; recompress '.tbz', '.tbz2',
+ '.tgz', '.txz', and '.tzst' to 'tar.lz'.
+
'-f'
'--force'
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
identical.
+'-i'
+'--ignore-errors'
+ Ignore non-fatal errors. (See exit status above).
+
'-k'
'--keep'
Keep (don't delete) the input file after comparing it with the lzip
@@ -845,18 +899,18 @@ Concept index

Tag Table:
Node: Top217
-Node: Introduction1147
-Node: Common options3947
-Ref: compressor-requirements6181
-Node: The zutilsrc file6576
-Node: Zcat7544
-Node: Zcmp10119
-Node: Zdiff12620
-Node: Zgrep15478
-Node: Ztest19819
-Node: Zupdate22513
-Node: Problems26607
-Node: Concept index27141
+Node: Introduction1150
+Node: Common options3897
+Ref: compressor-requirements6134
+Node: The zutilsrc file6529
+Node: Zcat7497
+Node: Zcmp10072
+Node: Zdiff12573
+Node: Zgrep15623
+Node: Ztest21115
+Node: Zupdate23681
+Node: Problems28191
+Node: Concept index28725

End Tag Table
diff --git a/doc/zutils.texi b/doc/zutils.texi
index 34a3128..6324814 100644
--- a/doc/zutils.texi
+++ b/doc/zutils.texi
@@ -6,8 +6,8 @@
@finalout
@c %**end of header
-@set UPDATED 25 January 2022
-@set VERSION 1.11
+@set UPDATED 12 April 2022
+@set VERSION 1.12-pre2
@dircategory Compression
@direntry
@@ -94,10 +94,9 @@ example, use the following command to search for the string @samp{foo} in
gzip and lzip files only:
@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
-passed to the corresponding decompressor without verifying their format,
-allowing for example the processing of compress'd (.Z) files with gzip:
-@w{@samp{zcmp --force-format=gz file.Z file.lz}}.
+FORMAT NOTE 2: The standard POSIX compress format (.Z) is obsolete and is
+only supported through gzip. For this to work, the gzip program used (for
+example GNU gzip) must be able to decompress .Z files.
LANGUAGE NOTE: Uncompressed = not compressed = plain data; it may never have
been compressed. Decompressed is used to refer to data which have undergone
@@ -163,7 +162,7 @@ extensions:
@multitable {bz2} {enables} {any other file name}
@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 xz @tab enables @tab .xz .txz
@item zst @tab enables @tab .zst .tzst
@@ -531,6 +530,15 @@ program used.
@itemx --ignore-all-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
@@ -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
matches were found, and 2 means trouble.
-zgrep supports the following options (some options only work if the grep
-program used supports them):
+zgrep supports the following options (Some options only work if the grep
+program used supports them. Options -h, -H, -r, -R, and -Z are managed by
+zgrep and not passed to grep):
@table @code
@item -a
@@ -602,7 +611,7 @@ Use @var{pattern} as the pattern to match.
@item -E
@itemx --extended-regexp
-Treat @var{pattern} as an extended regular expression.
+Interpret @var{pattern} as an extended regular expression (ERE).
@item -f @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
@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
@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
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}
@itemx --max-count=@var{n}
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
won't be tried.
+@item -P
+@itemx --perl-regexp
+Interpret @var{pattern} as a Perl-compatible regular expression (PCRE).
+
@item -q
@itemx --quiet
+@itemx --silent
Suppress all messages. Exit immediately with zero status if any match is
found, even if an error was detected.
@@ -683,6 +709,16 @@ recursively, following all symbolic links.
@itemx --no-messages
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
@itemx --invert-match
Select non-matching lines.
@@ -699,6 +735,14 @@ Match only whole words.
@itemx --line-regexp
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
@@ -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
is used, the files are passed to the corresponding decompressor without
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
-gzipped (.gz) and compress'd (.Z) files if the compressor used is GNU gzip.
+can't understand will fail.
@item -q
@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.
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
-to @samp{.lz};@*
+@samp{.bz2}, @samp{.gz}, @samp{.xz}, @samp{.zst}, or @samp{.Z}, which are
+recompressed to @samp{.lz};@*
@samp{.tbz}, @samp{.tbz2}, @samp{.tgz}, @samp{.txz}, or @samp{.tzst}, which
are recompressed to @samp{.tlz}.@*
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
files are ignored.
-Recompressing a file is much like copying or moving it; therefore zupdate
-preserves the access and modification dates, permissions, and, when
-possible, ownership of the file just as @w{@samp{cp -p}} does. (If the user ID or
-the group ID can't be duplicated, the file permission bits S_ISUID and
-S_ISGID are cleared).
+Recompressing a file is much like copying or moving it. Therefore zupdate
+preserves the access and modification dates, permissions, and, if you have
+appropriate privileges, ownership of the file just as @w{@samp{cp -p}} does.
+(If the user ID or the group ID can't be duplicated, the file permission
+bits S_ISUID and S_ISGID are cleared).
The format for running zupdate is:
@@ -835,11 +878,19 @@ zupdate [@var{options}] [@var{files}]
@noindent
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:
@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
@itemx --force
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
are identical.
+@item -i
+@itemx --ignore-errors
+Ignore non-fatal errors. (See exit status above).
+
@item -k
@itemx --keep
Keep (don't delete) the input file after comparing it with the lzip file.
diff --git a/rc.cc b/rc.cc
index e244a0e..33cd504 100644
--- a/rc.cc
+++ b/rc.cc
@@ -62,6 +62,7 @@ const struct { const char * from; const char * to; int format_index; }
{ ".txz", ".tar", fmt_xz },
{ ".zst", "", fmt_zst },
{ ".tzst", ".tar", fmt_zst },
+ { ".Z", "", fmt_gz },
{ 0, 0, -1 } };
diff --git a/testsuite/check.sh b/testsuite/check.sh
index b97abd7..5ad9d5b 100755
--- a/testsuite/check.sh
+++ b/testsuite/check.sh
@@ -321,6 +321,8 @@ done
test_failed $LINENO
"${ZGREP}" -N -l "GNU" in in.gz in.bz2 in.lz -- -in- > /dev/null ||
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 "nx_pattern" in in.gz in.bz2 in.lz -- -in- &&
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
[ $? = 1 ] || test_failed $LINENO
"${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
-[ $? = 1 ] || test_failed $LINENO
+[ $? = 2 ] || test_failed $LINENO
"${ZUPDATE}" -N --bad-option 2> /dev/null
-[ $? = 1 ] || test_failed $LINENO
+[ $? = 2 ] || test_failed $LINENO
cat in.lz in.lz > a.lz || framework_failure
"${ZUPDATE}" -Nq -f a.bz2 a.gz
-[ $? = 1 ] || test_failed $LINENO
+[ $? = 2 ] || test_failed $LINENO
[ -e a.bz2 ] || test_failed $LINENO
[ -e a.gz ] || 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
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 "${bad0_gz}" > b.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 b.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 c ] || 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
cat in.bz2 > a.bz2 || framework_failure
diff --git a/zcmp.cc b/zcmp.cc
index f0603af..99a1540 100644
--- a/zcmp.cc
+++ b/zcmp.cc
@@ -95,7 +95,7 @@ const char * format_num3( long long num )
{
const char * const si_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 int current = 0;
@@ -408,26 +408,27 @@ int main( const int argc, const char * const argv[] )
const int code = parser.code( argind );
if( !code ) break; // no more options
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 )
{
case 'b': print_bytes = true; break;
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 'M': parse_format_list( arg, pn ); break;
- case 'n': max_size = getnum( arg.c_str(), pn ); break;
+ case 'M': parse_format_list( sarg, pn ); break;
+ case 'n': max_size = getnum( arg, pn ); 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 's': verbosity = -1; break;
case 'v': verbosity = 1; break;
case 'V': show_version(); return 0;
- case bz2_opt: parse_compressor( arg, fmt_bz2 ); break;
- case gz_opt: parse_compressor( arg, fmt_gz ); break;
- case lz_opt: parse_compressor( arg, fmt_lz ); break;
- case xz_opt: parse_compressor( arg, fmt_xz ); break;
- case zst_opt: parse_compressor( arg, fmt_zst ); break;
+ case bz2_opt: parse_compressor( sarg, fmt_bz2 ); 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." );
}
} // end process options
diff --git a/zdiff.cc b/zdiff.cc
index a82f336..ba77b2a 100644
--- a/zdiff.cc
+++ b/zdiff.cc
@@ -89,7 +89,7 @@ void show_help()
" -U, --unified=<n> same as -u but use <n> lines of context\n"
" -v, --verbose verbose mode (for --version)\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"
" --bz2=<command> set compressor and options for bzip2 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 );
if( !code ) break; // no more options
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 )
{
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 'c': diff_args.push_back( "-c" ); break;
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 'E': diff_args.push_back( "-E" ); break;
case 'h': show_help(); return 0;
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 '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 'q': diff_args.push_back( "-q" ); 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 'u': diff_args.push_back( "-u" ); break;
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': show_version( DIFF " --version" ); return 0;
case 'w': diff_args.push_back( "-w" ); break;
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 bz2_opt: parse_compressor( arg, fmt_bz2 ); break;
- case gz_opt: parse_compressor( arg, fmt_gz ); break;
- case lz_opt: parse_compressor( arg, fmt_lz ); break;
- case xz_opt: parse_compressor( arg, fmt_xz ); break;
- case zst_opt: parse_compressor( arg, fmt_zst ); break;
+ case bz2_opt: parse_compressor( sarg, fmt_bz2 ); 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." );
}
} // end process options
diff --git a/zgrep.cc b/zgrep.cc
index 2ed569b..ef8db6b 100644
--- a/zgrep.cc
+++ b/zgrep.cc
@@ -79,26 +79,33 @@ void show_help()
" -E, --extended-regexp <pattern> is an extended regular expression\n"
" -f, --file=<file> obtain patterns from <file>\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, --with-filename print the file name for each match\n"
" -i, --ignore-case ignore case distinctions\n"
" -I ignore binary files\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"
+ " --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, --format=<list> process only the formats in <list>\n"
" -n, --line-number print the line number of each line\n"
" -N, --no-rcfile don't read runtime configuration file\n"
" -o, --only-matching show only the part of a line matching <pattern>\n"
" -O, --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, --dereference-recursive recursively follow symbolic links\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"
" --verbose verbose mode (show error messages)\n"
" -w, --word-regexp match only whole words\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"
" --gz=<command> set compressor and options for gzip 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,
const std::string & input_filename,
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;
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 };
uint8_t buffer[buffer_size];
bool line_begin = true;
- while( true )
+ bool at_eof = false;
+ while( !at_eof )
{
- const int size = readblock( fda[0], buffer, buffer_size );
- if( size != buffer_size && errno )
- { show_error( "Read error", errno ); return 2; }
+ int size;
+ bool error = false;
+ 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( show_name )
+ if( show_name ) // print the file name for each match
for( int i = 0; i < size; ++i )
{
if( line_begin )
- { line_begin = false; std::printf( "%s:", input_filename.c_str() ); }
- if( buffer[i] == '\n' ) line_begin = true;
+ { line_begin = false;
+ 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] );
+ if( buffer[i] == '\n' )
+ { line_begin = true; if( line_buffered ) std::fflush( stdout ); }
}
else if( std::fwrite( buffer, 1, size, stdout ) != (unsigned)size )
{ std::fflush( stdout ); show_error( "Write error", errno ); return 2; }
- std::fflush( stdout );
}
- if( size < buffer_size ) break; // end of grep's output
}
+ std::fflush( stdout );
int retval = wait_for_child( grep_pid, GREP );
if( !good_status( children, retval == 1 ) ) retval = 2;
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 )
{ show_close_error(); return 2; }
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[] )
{
- 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 };
int format_index = -1;
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
+ bool initial_tab = false;
+ bool line_buffered = false;
bool no_messages = false;
+ bool z_null = false; // for '-Z, --null'
std::list< std::string > filenames;
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";
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
{ 'f', "file ", Arg_parser::yes }, // 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', "with-filename", Arg_parser::no }, // grep GNU
{ '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 },
{ 'o', "only-matching", Arg_parser::no }, // grep
{ 'O', "force-format", Arg_parser::yes },
+ { 'P', "perl-regexp", Arg_parser::no }, // grep GNU
{ 'q', "quiet", Arg_parser::no },
+ { 'q', "silent", Arg_parser::no },
{ 'r', "recursive", Arg_parser::no },
{ 'R', "dereference-recursive", Arg_parser::no },
{ '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', "version", Arg_parser::no },
{ 'w', "word-regexp", Arg_parser::no }, // grep GNU
{ 'x', "line-regexp", Arg_parser::no }, // grep
+ { 'Z', "null", Arg_parser::no }, // grep GNU
{ help_opt, "help", Arg_parser::no },
{ verbose_opt, "verbose", Arg_parser::no },
{ 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 },
{ gz_opt, "gz", 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 );
if( !code ) break; // no more options
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 )
{
case 'a': grep_args.push_back( "-a" ); break;
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" );
- 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" );
- grep_args.push_back( arg.c_str() ); break;
+ grep_args.push_back( arg ); break;
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 '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 'G': grep_args.push_back( "-G" ); break;
case 'h': show_name = false; break;
case 'H': show_name = true; 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 'm': grep_args.push_back( "-m" );
- grep_args.push_back( arg.c_str() ); break;
- case 'M': parse_format_list( arg, pn ); break;
+ grep_args.push_back( arg ); break;
+ case 'M': parse_format_list( sarg, pn ); break;
case 'n': grep_args.push_back( "-n" ); break;
case 'N': 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 'r': recursive = 1; break;
case 'R': recursive = 2; 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': show_version( GREP " --version" ); return 0;
case 'w': grep_args.push_back( "-w" ); break;
case 'x': grep_args.push_back( "-x" ); break;
+ case 'Z': z_null = true; break;
case help_opt: show_help(); return 0;
case verbose_opt: no_messages = false; if( verbosity < 4 ) ++verbosity;
break;
case color_opt: color_option = "--color";
- if( !arg.empty() ) { color_option += '='; color_option += arg; }
+ if( !sarg.empty() ) { color_option += '='; color_option += sarg; }
break;
- case bz2_opt: parse_compressor( arg, fmt_bz2 ); break;
- case gz_opt: parse_compressor( arg, fmt_gz ); break;
- case lz_opt: parse_compressor( arg, fmt_lz ); break;
- case xz_opt: parse_compressor( arg, fmt_xz ); break;
- case zst_opt: parse_compressor( arg, fmt_zst ); break;
+ case label_opt: label_option = label = sarg; break;
+ case linebuf_opt: grep_args.push_back( "--line-buffered" );
+ line_buffered = true; break;
+ case bz2_opt: parse_compressor( sarg, fmt_bz2 ); 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." );
}
} // end process options
if( !color_option.empty() ) // push the last value set
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__
setmode( STDIN_FILENO, O_BINARY );
@@ -352,9 +366,9 @@ int main( const int argc, const char * const argv[] )
{
if( argind >= parser.arguments() )
{ show_error( "Pattern not found." ); return 2; }
- const std::string & arg = parser.argument( argind++ );
- if( arg.size() && arg[0] == '-' ) grep_args.push_back( "-e" );
- grep_args.push_back( arg.c_str() );
+ const std::string & pat = parser.argument( argind++ );
+ if( pat.size() && pat[0] == '-' ) grep_args.push_back( "-e" );
+ grep_args.push_back( pat.c_str() );
}
for( ; argind < parser.arguments(); ++argind )
@@ -375,7 +389,7 @@ int main( const int argc, const char * const argv[] )
if( input_filename == "." )
{
if( stdin_used ) continue; else stdin_used = true;
- infd = STDIN_FILENO; input_filename = "-";
+ infd = STDIN_FILENO; input_filename = label;
}
else
{
@@ -383,11 +397,9 @@ int main( const int argc, const char * const argv[] )
if( infd < 0 ) { error = true; continue; }
}
- int tmp;
- if( infd == STDIN_FILENO )
- tmp = zgrep_stdin( infd, format_index, grep_args );
- else tmp = zgrep_file( infd, format_index, input_filename, grep_args,
- list_mode, show_name );
+ const int tmp = zgrep_file( infd, format_index, input_filename, grep_args,
+ list_mode, initial_tab, line_buffered,
+ show_name, z_null );
if( tmp == 0 || ( tmp == 2 && retval == 1 ) ) retval = tmp;
if( close( infd ) != 0 )
diff --git a/zupdate.cc b/zupdate.cc
index e87d9e1..fce00b3 100644
--- a/zupdate.cc
+++ b/zupdate.cc
@@ -67,15 +67,20 @@ void show_help()
"to be safe and not cause any data loss. Therefore, existing lzip\n"
"compressed files are never overwritten nor deleted.\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"
"\nUsage: zupdate [options] [files]\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"
" -h, --help display this help 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"
+ " -i, --ignore-errors ignore non-fatal errors\n"
" -k, --keep keep (don't delete) input files\n"
" -l, --lzip-verbose pass one option -v to the lzip compressor\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 )
{
@@ -105,7 +110,6 @@ int cant_execute( const std::string & command, const int status )
std::fprintf( stderr, "%s: Can't execute '%s'\n",
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,
const std::vector< std::string > & lzip_args2,
- const bool force, const bool keep_input_files,
- const bool no_rcfile )
+ const bool expand, const bool force,
+ const bool keep_input_files, const bool no_rcfile )
{
// bzip2, gzip, and lzip are the primary formats. xz and zstd are optional.
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 += ( 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 );
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() );
int fda[2]; // pipe between decompressor and compressor
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();
if( pid == 0 ) // child1 (decompressor)
@@ -250,7 +254,7 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
_exit( 1 );
}
if( pid < 0 ) // parent
- { show_fork_error( compressor_name ); return 1; }
+ { show_fork_error( compressor_name ); return 2; }
const pid_t pid2 = fork();
if( pid2 == 0 ) // child2 (lzip compressor)
@@ -276,19 +280,19 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
_exit( 1 );
}
if( pid2 < 0 ) // parent
- { show_fork_error( lzip_name ); return 1; }
+ { show_fork_error( lzip_name ); return 2; }
close( fda[0] ); close( fda[1] );
- int retval = wait_for_child( pid, compressor_name );
- int retval2 = wait_for_child( pid2, lzip_name );
+ const int retval = wait_for_child( pid, compressor_name );
+ const int retval2 = wait_for_child( pid2, lzip_name );
if( retval || retval2 )
{ 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 &&
( lz_lz_exists || stat( rname2.c_str(), &st ) != 0 ||
std::rename( rname2.c_str(), rname.c_str() ) != 0 ) )
{ 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 );
}
@@ -296,8 +300,8 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
if( lz_exists && verbosity >= 1 )
std::fprintf( stderr, "Comparing file '%s'\n", name.c_str() );
std::string zcmp_command( invocation_name );
- unsigned i = zcmp_command.size();
- while( i > 0 && zcmp_command[i-1] != '/' ) --i;
+ unsigned i = zcmp_command.size();
+ while( i > 0 && zcmp_command[i-1] != '/' ) --i; // strip "zupdate"
zcmp_command.resize( i ); zcmp_command.insert( zcmp_command.begin(), '\'' );
zcmp_command += "zcmp' "; // '[dir/]zcmp'
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() );
if( status != 0 )
{ 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 )
@@ -329,7 +333,9 @@ int main( const int argc, const char * const argv[] )
int recursive = 0; // 1 = '-r', 2 = '-R'
std::list< std::string > filenames;
std::vector< std::string > lzip_args2; // args to lzip, maybe empty
+ bool expand = false;
bool force = false;
+ bool ignore_errors = false;
bool keep_input_files = false;
bool no_rcfile = false;
program_name = "zupdate";
@@ -347,8 +353,10 @@ int main( const int argc, const char * const argv[] )
{ '7', 0, Arg_parser::no },
{ '8', 0, Arg_parser::no },
{ '9', 0, Arg_parser::no },
+ { 'e', "expand-extensions", Arg_parser::no },
{ 'f', "force", Arg_parser::no },
{ 'h', "help", Arg_parser::no },
+ { 'i', "ignore-errors", Arg_parser::no },
{ 'k', "keep", Arg_parser::no },
{ 'l', "lzip-verbose", Arg_parser::no },
{ '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 );
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 );
@@ -383,8 +391,10 @@ int main( const int argc, const char * const argv[] )
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
lzip_args2.push_back( "-" ); lzip_args2.back() += code; break;
+ case 'e': expand = true; break;
case 'f': force = true; break;
case 'h': show_help(); return 0;
+ case 'i': ignore_errors = true; break;
case 'k': keep_input_files = true; break;
case 'l': lzip_args2.push_back( "-v" ); 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 );
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 )
filenames.push_back( parser.argument( argind ) );
@@ -422,11 +432,11 @@ int main( const int argc, const char * const argv[] )
bool error = false;
while( next_filename( filenames, input_filename, error, recursive, true ) )
{
- int tmp = zupdate_file( input_filename, lzip_name, lzip_args2, force,
- keep_input_files, no_rcfile );
+ int tmp = zupdate_file( input_filename, lzip_name, lzip_args2, expand,
+ force, keep_input_files, no_rcfile );
if( tmp < 0 ) error = true;
if( tmp > retval ) retval = tmp;
- if( tmp > 0 ) break;
+ if( tmp >= 2 || ( tmp == 1 && !ignore_errors ) ) break;
}
if( error && retval == 0 ) retval = 1;
return retval;
diff --git a/zutils.cc b/zutils.cc
index ad5cd3e..1daa283 100644
--- a/zutils.cc
+++ b/zutils.cc
@@ -255,11 +255,14 @@ int test_format( const int infd, uint8_t magic_data[],
gzip_magic_size = 2,
lzip_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] =
{ 0x42, 0x5A, 0x68 }; // "BZh"
const uint8_t gzip_magic[gzip_magic_size] =
{ 0x1F, 0x8B };
+ const uint8_t compress_magic[compress_magic_size] =
+ { 0x1F, 0x9D };
const uint8_t lzip_magic[lzip_magic_size] =
{ 0x4C, 0x5A, 0x49, 0x50, 0x01 }; // "LZIP\001"
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' &&
std::memcmp( magic_data + 4, "1AY&SY", 6 ) == 0 )
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;
if( std::memcmp( magic_data, zstd_magic, zstd_magic_size ) == 0 )
return fmt_zst;