summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--COPYING3
-rw-r--r--ChangeLog9
-rw-r--r--Makefile.in5
-rw-r--r--NEWS22
-rw-r--r--README9
-rw-r--r--arg_parser.cc15
-rw-r--r--arg_parser.h10
-rwxr-xr-xconfigure4
-rw-r--r--doc/zcat.18
-rw-r--r--doc/zcmp.12
-rw-r--r--doc/zdiff.12
-rw-r--r--doc/zgrep.12
-rw-r--r--doc/ztest.12
-rw-r--r--doc/zupdate.14
-rw-r--r--doc/zutils.info172
-rw-r--r--doc/zutils.texi159
-rwxr-xr-xtestsuite/check.sh94
-rw-r--r--testsuite/test.txt6
-rw-r--r--testsuite/test.txt.tarbin40960 -> 37888 bytes
-rw-r--r--testsuite/test_bad_crc.lzbin7376 -> 7341 bytes
-rw-r--r--zcat.cc16
-rw-r--r--zcmp.cc6
-rw-r--r--zdiff.cc6
-rw-r--r--zgrep.cc8
-rw-r--r--ztest.cc2
-rw-r--r--zupdate.cc53
26 files changed, 387 insertions, 232 deletions
diff --git a/COPYING b/COPYING
index 4ad17ae..42fe735 100644
--- a/COPYING
+++ b/COPYING
@@ -1,8 +1,7 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
diff --git a/ChangeLog b/ChangeLog
index 459ca03..1b897de 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2024-11-29 Antonio Diaz Diaz <antonio@gnu.org>
+
+ * Version 1.14-rc1 released.
+ * zupdate.cc: '-r -d' now keeps last component of dir arguments.
+ * zutils.texi: New chapter 'Syntax of command-line arguments'.
+ * check.sh: Use 'cp' instead of 'cat'.
+
2024-01-23 Antonio Diaz Diaz <antonio@gnu.org>
* Version 1.13 released.
@@ -110,7 +117,7 @@
* zdiff.cc (set_fifonames): Use '_' if both names are different.
* configure: Avoid warning on some shells when testing for g++.
* Makefile.in: Detect the existence of install-info.
- * check.sh: A POSIX shell is required to run the tests.
+ * check.sh: Require a POSIX shell.
2015-05-29 Antonio Diaz Diaz <antonio@gnu.org>
diff --git a/Makefile.in b/Makefile.in
index b8984fd..7583316 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -3,8 +3,8 @@ DISTNAME = $(pkgname)-$(pkgversion)
INSTALL = install
INSTALL_PROGRAM = $(INSTALL) -m 755
INSTALL_SCRIPT = $(INSTALL) -m 755
-INSTALL_DATA = $(INSTALL) -m 644
INSTALL_DIR = $(INSTALL) -d -m 755
+INSTALL_DATA = $(INSTALL) -m 644
SHELL = /bin/sh
CAN_RUN_INSTALLINFO = $(SHELL) -c "install-info --version" > /dev/null 2>&1
@@ -68,7 +68,8 @@ zgrep.o : zgrep.cc
# prevent 'make' from trying to remake source files
$(VPATH)/configure $(VPATH)/Makefile.in $(VPATH)/doc/$(pkgname).texi : ;
-%.h %.cc : ;
+MAKEFLAGS += -r
+.SUFFIXES :
$(objs) : Makefile
$(scripts) : Makefile
diff --git a/NEWS b/NEWS
index 0f83afd..3ca3953 100644
--- a/NEWS
+++ b/NEWS
@@ -1,18 +1,8 @@
-Changes in version 1.13:
+Changes in version 1.14:
-The detection of bzip2 files with no compressed blocks has been fixed.
-(Error introduced in version 1.9).
+'zupdate --recursive --destdir=dir' now keeps the file name component
+following the last slash in directory arguments;
+'../a' recompresses the file ../a/b.gz to dir/a/b.lz, while
+'../a/' recompresses the file ../a/b.gz to dir/b.lz.
-When zcat, zcmp, zdiff, or zgrep need to try compressed file names, gzip
-(.gz) is now tried before bzip2 (.bz2).
-
-When only one compressed file is passed to zcmp or zdiff, they now try to
-compare it with a compressed file of any of the remaining formats if the
-corresponding uncompressed file does not exist.
-
-zcmp now reports EOF on empty file like GNU cmp:
-"zcmp: EOF on FILE which is empty".
-
-File diagnostics in zupdate have been reformatted as 'PROGRAM: FILE: MESSAGE'.
-
-The variable MAKEINFO has been added to configure and Makefile.in.
+The chapter 'Syntax of command-line arguments' has been added to the manual.
diff --git a/README b/README
index c9043c3..6725a54 100644
--- a/README
+++ b/README
@@ -1,3 +1,5 @@
+See the file INSTALL for compilation and installation instructions.
+
Description
Zutils is a collection of utilities able to process any combination of
@@ -8,8 +10,8 @@ created. Data format is detected by its identifier string (magic bytes), not
by the file name extension. Empty files are considered uncompressed.
These utilities are not wrapper scripts but safer and more efficient C++
-programs. In particular the option '--recursive' is very efficient in
-those utilities supporting it.
+programs. In particular the option '--recursive' is efficient in those
+utilities supporting it.
The utilities provided are zcat, zcmp, zdiff, zgrep, ztest, and zupdate.
The formats supported are bzip2, gzip, lzip, xz, and zstd.
@@ -39,6 +41,9 @@ LANGUAGE NOTE: Uncompressed = not compressed = plain data; it may never have
been compressed. Decompressed is used to refer to data which have undergone
the process of decompression.
+Zutils uses Arg_parser for command-line argument parsing:
+http://www.nongnu.org/arg-parser/arg_parser.html
+
Copyright (C) 2009-2024 Antonio Diaz Diaz.
diff --git a/arg_parser.cc b/arg_parser.cc
index 0c04d8e..0c528b2 100644
--- a/arg_parser.cc
+++ b/arg_parser.cc
@@ -75,19 +75,19 @@ bool Arg_parser::parse_long_option( const char * const opt, const char * const a
error_ += "' requires an argument";
return false;
}
- data.back().argument = &opt[len+3];
+ data.back().argument = &opt[len+3]; // argument may be empty
return true;
}
- if( options[index].has_arg == yes )
+ if( options[index].has_arg == yes || options[index].has_arg == yme )
{
- if( !arg || !arg[0] )
+ if( !arg || ( options[index].has_arg == yes && !arg[0] ) )
{
error_ = "option '--"; error_ += options[index].long_name;
error_ += "' requires an argument";
return false;
}
- ++argind; data.back().argument = arg;
+ ++argind; data.back().argument = arg; // argument may be empty
return true;
}
@@ -123,15 +123,16 @@ bool Arg_parser::parse_short_option( const char * const opt, const char * const
{
data.back().argument = &opt[cind]; ++argind; cind = 0;
}
- else if( options[index].has_arg == yes )
+ else if( options[index].has_arg == yes || options[index].has_arg == yme )
{
- if( !arg || !arg[0] )
+ if( !arg || ( options[index].has_arg == yes && !arg[0] ) )
{
error_ = "option requires an argument -- '"; error_ += c;
error_ += '\'';
return false;
}
- data.back().argument = arg; ++argind; cind = 0;
+ ++argind; cind = 0;
+ data.back().argument = arg; // argument may be empty
}
}
return true;
diff --git a/arg_parser.h b/arg_parser.h
index 1eeec9a..ab77fc5 100644
--- a/arg_parser.h
+++ b/arg_parser.h
@@ -36,14 +36,18 @@
The argument '--' terminates all options; any following arguments are
treated as non-option arguments, even if they begin with a hyphen.
- The syntax for optional option arguments is '-<short_option><argument>'
- (without whitespace), or '--<long_option>=<argument>'.
+ The syntax of options with an optional argument is
+ '-<short_option><argument>' (without whitespace), or
+ '--<long_option>=<argument>'.
+
+ The syntax of options with an empty argument is '-<short_option> ""',
+ '--<long_option> ""', or '--<long_option>=""'.
*/
class Arg_parser
{
public:
- enum Has_arg { no, yes, maybe };
+ enum Has_arg { no, yes, maybe, yme }; // yme = yes but maybe empty
struct Option
{
diff --git a/configure b/configure
index 6bf4999..ea5f09e 100755
--- a/configure
+++ b/configure
@@ -6,7 +6,7 @@
# to copy, distribute, and modify it.
pkgname=zutils
-pkgversion=1.13
+pkgversion=1.14-rc1
srctrigger=doc/${pkgname}.texi
# clear some things potentially inherited from environment.
@@ -118,7 +118,7 @@ while [ $# != 0 ] ; do
exit 1 ;;
esac
- # Check if the option took a separate argument
+ # Check whether the option took a separate argument
if [ "${arg2}" = yes ] ; then
if [ $# != 0 ] ; then args="${args} \"$1\"" ; shift
else echo "configure: Missing argument to '${option}'" 1>&2
diff --git a/doc/zcat.1 b/doc/zcat.1
index a27254b..ba03226 100644
--- a/doc/zcat.1
+++ b/doc/zcat.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
-.TH ZCAT "1" "January 2024" "zutils 1.13" "User Commands"
+.TH ZCAT "1" "November 2024" "zutils 1.14-rc1" "User Commands"
.SH NAME
zcat \- decompress and concatenate files to standard output
.SH SYNOPSIS
@@ -38,6 +38,12 @@ equivalent to '\-vET'
\fB\-b\fR, \fB\-\-number\-nonblank\fR
number nonblank output lines
.TP
+\fB\-c\fR, \fB\-\-stdout\fR
+ignored, for gzip compatibility
+.TP
+\fB\-d\fR, \fB\-\-decompress\fR
+ignored, for gzip compatibility
+.TP
\fB\-e\fR
equivalent to '\-vE'
.TP
diff --git a/doc/zcmp.1 b/doc/zcmp.1
index 09af2a5..ccf4063 100644
--- a/doc/zcmp.1
+++ b/doc/zcmp.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
-.TH ZCMP "1" "January 2024" "zutils 1.13" "User Commands"
+.TH ZCMP "1" "November 2024" "zutils 1.14-rc1" "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 bbcdc94..034c3f7 100644
--- a/doc/zdiff.1
+++ b/doc/zdiff.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
-.TH ZDIFF "1" "January 2024" "zutils 1.13" "User Commands"
+.TH ZDIFF "1" "November 2024" "zutils 1.14-rc1" "User Commands"
.SH NAME
zdiff \- decompress and compare two files line by line
.SH SYNOPSIS
diff --git a/doc/zgrep.1 b/doc/zgrep.1
index 605f1cb..0d75504 100644
--- a/doc/zgrep.1
+++ b/doc/zgrep.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
-.TH ZGREP "1" "January 2024" "zutils 1.13" "User Commands"
+.TH ZGREP "1" "November 2024" "zutils 1.14-rc1" "User Commands"
.SH NAME
zgrep \- search compressed files for a regular expression
.SH SYNOPSIS
diff --git a/doc/ztest.1 b/doc/ztest.1
index caa0b2a..fedc2cf 100644
--- a/doc/ztest.1
+++ b/doc/ztest.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
-.TH ZTEST "1" "January 2024" "zutils 1.13" "User Commands"
+.TH ZTEST "1" "November 2024" "zutils 1.14-rc1" "User Commands"
.SH NAME
ztest \- check the integrity of compressed files
.SH SYNOPSIS
diff --git a/doc/zupdate.1 b/doc/zupdate.1
index c50a2df..965c6f6 100644
--- a/doc/zupdate.1
+++ b/doc/zupdate.1
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
-.TH ZUPDATE "1" "January 2024" "zutils 1.13" "User Commands"
+.TH ZUPDATE "1" "November 2024" "zutils 1.14-rc1" "User Commands"
.SH NAME
zupdate \- recompress bzip2, gzip, xz, zstd files to lzip format
.SH SYNOPSIS
@@ -97,7 +97,7 @@ set compressor and options for xz format
\fB\-\-zst=\fR<command>
set compressor and options for zstd format
.PP
-Valid formats for option '\-M' are 'bz2', 'gz', 'lz', 'xz', and 'zst'.
+Valid formats for option '\-M' are 'bz2', 'gz', 'xz', and 'zst'.
.SH "REPORTING BUGS"
Report bugs to zutils\-bug@nongnu.org
.br
diff --git a/doc/zutils.info b/doc/zutils.info
index d7f9cb4..36a94cb 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.13, 23 January 2024).
+This manual is for Zutils (version 1.14-rc1, 29 November 2024).
* Menu:
@@ -24,6 +24,7 @@ This manual is for Zutils (version 1.13, 23 January 2024).
* Zgrep:: Searching inside compressed files
* Ztest:: Testing the integrity of compressed files
* Zupdate:: Recompressing files to lzip format
+* Argument syntax:: By convention, options start with a hyphen
* Problems:: Reporting bugs
* Concept index:: Index of concepts
@@ -47,7 +48,7 @@ created. Data format is detected by its identifier string (magic bytes), not
by the file name extension. Empty files are considered uncompressed.
These utilities are not wrapper scripts but safer and more efficient C++
-programs. In particular the option '--recursive' is very efficient in those
+programs. In particular the option '--recursive' is efficient in those
utilities supporting it.
The utilities provided are 'zcat', 'zcmp', 'zdiff', 'zgrep', 'ztest', and
@@ -89,10 +90,10 @@ File: zutils.info, Node: Common options, Next: Configuration, Prev: Introduct
2 Common options
****************
-The following options: are available in all the utilities. Rather than
+The following options are available in all the utilities. Rather than
writing identical descriptions for each of the programs, they are described
here. Remember to prepend './' to any file name beginning with a hyphen, or
-use '--'. *Note Argument syntax: (arg_parser)Argument syntax.
+use '--'. *Note Argument syntax::.
'-h'
'--help'
@@ -113,7 +114,7 @@ use '--'. *Note Argument syntax: (arg_parser)Argument syntax.
'--format=FORMAT_LIST'
Process only the formats listed in the comma-separated FORMAT_LIST.
Valid formats are 'bz2', 'gz', 'lz', 'xz', 'zst', and 'un' for
- 'uncompressed', meaning "any file name without a known extension".
+ 'uncompressed', meaning 'any file name without a known extension'.
This option excludes files based on extension, instead of format,
because it is more efficient. The exclusion only applies to names
generated automatically (for example when adding extensions to a file
@@ -153,7 +154,7 @@ use '--'. *Note Argument syntax: (arg_parser)Argument syntax.
2. If the option '-q' is passed to zutils, the compression program
must also accept it.
- 3. It must return 0 if no errors occurred, and a non-zero value
+ 3. It must return 0 if no errors occurred, and a nonzero value
otherwise.
@@ -164,6 +165,7 @@ and may be followed by a multiplier and an optional 'B' for "byte".
Table of SI and binary prefixes (unit multipliers):
Prefix Value | Prefix Value
+----------------------------------------------------------------------
k kilobyte (10^3 = 1000) | Ki kibibyte (2^10 = 1024)
M megabyte (10^6) | Mi mebibyte (2^20)
G gigabyte (10^9) | Gi gibibyte (2^30)
@@ -228,7 +230,7 @@ directory, and nonrecursive searches read standard input.
Exit status is 0 if no errors occurred, 1 otherwise.
- 'zcat' supports the following options:
+'zcat' supports the following options:
'-A'
'--show-all'
@@ -239,6 +241,12 @@ Exit status is 0 if no errors occurred, 1 otherwise.
Number all nonblank output lines, starting with 1. The line count is
unlimited.
+'-c'
+'--stdout'
+'-d'
+'--decompress'
+ Ignored, for gzip compatibility.
+
'-e'
Equivalent to '-vE'.
@@ -287,8 +295,8 @@ Exit status is 0 if no errors occurred, 1 otherwise.
'-v'
'--show-nonprinting'
Print control characters except for LF (newline) and TAB using '^'
- notation and precede characters larger than 127 with 'M-' (which
- stands for "meta").
+ notation and precede characters larger than 127 with 'M-' (which stands
+ for "meta").
'--verbose'
Verbose mode. Show error messages. Repeating it increases the verbosity
@@ -321,7 +329,7 @@ until one is found. *Note search-order::.
An exit status of 0 means no differences were found, 1 means some
differences were found, and 2 means trouble.
- 'zcmp' supports the following options:
+'zcmp' supports the following options:
'-b'
'--print-bytes'
@@ -356,10 +364,10 @@ differences were found, and 2 means trouble.
Force the compressed formats given. If FORMAT1 or FORMAT2 is omitted,
the corresponding format is automatically detected. Valid values for
FORMAT are 'bz2', 'gz', 'lz', 'xz', 'zst', and 'un' for
- 'uncompressed'. If at least one format is specified with this option,
- the file is passed to the corresponding decompressor (or transmitted
- unmodified) without checking its format, and the exact file names of
- both FILE1 and FILE2 must be given. Other names are not tried.
+ 'uncompressed'. If this option is specified, the corresponding file is
+ passed to the decompressor (or transmitted unmodified) without
+ checking its format, and the exact file names of both FILE1 and FILE2
+ must be given. Other names are not tried.
'-q'
'--quiet'
@@ -409,8 +417,8 @@ remaining formats until one is found. *Note search-order::.
An exit status of 0 means no differences were found, 1 means some
differences were found, and 2 means trouble.
- 'zdiff' supports the following options (some options only work if the
-diff program used supports them):
+'zdiff' supports the following options (some options only work if the diff
+program used supports them):
'-a'
'--text'
@@ -449,10 +457,10 @@ diff program used supports them):
Force the compressed formats given. If FORMAT1 or FORMAT2 is omitted,
the corresponding format is automatically detected. Valid values for
FORMAT are 'bz2', 'gz', 'lz', 'xz', 'zst', and 'un' for
- 'uncompressed'. If at least one format is specified with this option,
- the file is passed to the corresponding decompressor (or transmitted
- unmodified) without checking its format, and the exact file names of
- both FILE1 and FILE2 must be given. Other names are not tried.
+ 'uncompressed'. If this option is specified, the corresponding file is
+ passed to the decompressor (or transmitted unmodified) without
+ checking its format, and the exact file names of both FILE1 and FILE2
+ must be given. Other names are not tried.
'-p'
'--show-c-function'
@@ -536,9 +544,9 @@ with a nonzero status because base64 cannot write to its output pipe after
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. Options -h, -H, -r, -R, and -Z are managed
-by 'zgrep' and not passed to grep):
+'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'
@@ -680,8 +688,8 @@ by 'zgrep' and not passed to grep):
'-U'
'--binary'
- Use binary I/O on platforms affected by the bug known as "text mode
- I/O". (MS-DOS, MS-Windows, OS/2).
+ 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'
@@ -749,7 +757,7 @@ problems (file not found, invalid command-line options, I/O errors, etc), 2
if any compressed file is corrupt or invalid, or if any file has an
incorrect file name extension.
- 'ztest' supports the following options:
+'ztest' supports the following options:
'-O FORMAT'
'--force-format=FORMAT'
@@ -781,7 +789,7 @@ incorrect file name extension.

-File: zutils.info, Node: Zupdate, Next: Problems, Prev: Ztest, Up: Top
+File: zutils.info, Node: Zupdate, Next: Argument syntax, Prev: Ztest, Up: Top
9 Zupdate
*********
@@ -837,19 +845,30 @@ occurred (file not found or not regular, or has invalid format, or can't be
deleted). 2 if a fatal error occurred (invalid command-line options,
compressor can't be run, or comparison fails).
- 'zupdate' supports the following options:
+'zupdate' supports the following options:
'-d DIR'
'--destdir=DIR'
Write recompressed files to another directory, using DIR as base
directory, instead of writing them in the same directory as the
- original files. In recursive mode, this is done by replacing each
- directory specified in the command line with DIR to produce the
- recompressed file names. For example, 'zupdate -r -d DIR ../a'
- recompresses a file named '../a/b/c.gz' to 'DIR/b/c.lz'. Regular files
- specified in the command line are recompressed directly into DIR. For
- example, 'zupdate -d DIR ../a/b/c.gz' writes the recompressed file to
- 'DIR/c.lz'.
+ original files. This is done by removing the (possibly empty) prefix
+ preceding the last slash (if any) of each FILE specified in the
+ command line, and then prepending DIR to produce the recompressed file
+ names.
+
+ In recursive mode, if FILE ends with a slash and names a directory, it
+ is completely replaced with DIR. Therefore, if FILE ends with a slash,
+ all the files in FILE are recompressed directly into DIR, but if FILE
+ does not end with a slash, the files in FILE are recompressed into the
+ subdirectory DIR/`basename( FILE )`. 'FILE/' is thus equivalent to
+ 'FILE/*', but without the danger of exceeding the length limit of the
+ command line.
+
+ For example, 'zupdate -r -d DIR ../a' recompresses the file
+ '../a/b.gz' to 'DIR/a/b.lz', while 'zupdate -r -d DIR ../a/'
+ recompresses the same file to 'DIR/b.lz'. Regular files specified in
+ the command line are recompressed directly into DIR. For example,
+ 'zupdate -d DIR ../a/b.gz' writes the recompressed file to 'DIR/b.lz'.
This option allows recompressing files from a read-only file system to
another place without the need to copy or link them to the destination
@@ -925,9 +944,56 @@ compressor can't be run, or comparison fails).

-File: zutils.info, Node: Problems, Next: Concept index, Prev: Zupdate, Up: Top
+File: zutils.info, Node: Argument syntax, Next: Problems, Prev: Zupdate, Up: Top
+
+10 Syntax of command-line arguments
+***********************************
+
+POSIX recommends these conventions for command-line arguments.
+
+ * A command-line argument is an option if it begins with a hyphen ('-').
+
+ * Option names are single alphanumeric characters.
+
+ * Certain options require an argument.
+
+ * An option and its argument may or may not appear as separate tokens.
+ (In other words, the whitespace separating them is optional). Thus,
+ '-o foo' and '-ofoo' are equivalent.
+
+ * One or more options without arguments, followed by at most one option
+ that takes an argument, may follow a hyphen in a single token. Thus,
+ '-abc' is equivalent to '-a -b -c'.
+
+ * Options typically precede other non-option arguments.
+
+ * The argument '--' terminates all options; any following arguments are
+ treated as non-option arguments, even if they begin with a hyphen.
+
+ * A token consisting of a single hyphen character is interpreted as an
+ ordinary non-option argument. By convention, it is used to specify
+ standard input, standard output, or a file named '-'.
+
+GNU adds "long options" to these conventions:
+
+ * A long option consists of two hyphens ('--') followed by a name made
+ of alphanumeric characters and hyphens. Option names are typically one
+ to three words long, with hyphens to separate words. Abbreviations can
+ be used for the long option names as long as the abbreviations are
+ unique.
+
+ * A long option and its argument may or may not appear as separate
+ tokens. In the latter case they must be separated by an equal sign '='.
+ Thus, '--foo bar' and '--foo=bar' are equivalent.
+
+The syntax of options with an optional argument is
+'-<short_option><argument>' (without whitespace), or
+'--<long_option>=<argument>'.
+
+
+File: zutils.info, Node: Problems, Next: Concept index, Prev: Argument syntax, Up: Top
-10 Reporting bugs
+11 Reporting bugs
*****************
There are probably bugs in zutils. There are certainly errors and omissions
@@ -948,6 +1014,7 @@ Concept index
* Menu:
+* argument syntax: Argument syntax. (line 6)
* bugs: Problems. (line 6)
* common options: Common options. (line 6)
* getting help: Problems. (line 6)
@@ -964,21 +1031,22 @@ Concept index

Tag Table:
Node: Top217
-Node: Introduction1152
-Ref: search-order2304
-Node: Common options3461
-Ref: version4027
-Ref: compressor-requirements5978
-Node: Configuration7367
-Node: Zcat8400
-Node: Zcmp11139
-Node: Zdiff14407
-Node: Zgrep17490
-Node: Ztest23637
-Node: Zupdate26430
-Ref: lz-compressor31838
-Node: Problems32539
-Node: Concept index33073
+Node: Introduction1224
+Ref: search-order2371
+Node: Common options3528
+Ref: version4066
+Ref: compressor-requirements6017
+Node: Configuration7476
+Node: Zcat8509
+Node: Zcmp11320
+Node: Zdiff14560
+Node: Zgrep17615
+Node: Ztest23759
+Node: Zupdate26549
+Ref: lz-compressor32564
+Node: Argument syntax33265
+Node: Problems35157
+Node: Concept index35699

End Tag Table
diff --git a/doc/zutils.texi b/doc/zutils.texi
index 1646e7d..3f523d2 100644
--- a/doc/zutils.texi
+++ b/doc/zutils.texi
@@ -6,8 +6,8 @@
@finalout
@c %**end of header
-@set UPDATED 23 January 2024
-@set VERSION 1.13
+@set UPDATED 29 November 2024
+@set VERSION 1.14-rc1
@dircategory Compression
@direntry
@@ -45,6 +45,7 @@ This manual is for Zutils (version @value{VERSION}, @value{UPDATED}).
* Zgrep:: Searching inside compressed files
* Ztest:: Testing the integrity of compressed files
* Zupdate:: Recompressing files to lzip format
+* Argument syntax:: By convention, options start with a hyphen
* Problems:: Reporting bugs
* Concept index:: Index of concepts
@end menu
@@ -70,8 +71,8 @@ created. Data format is detected by its identifier string (magic bytes), not
by the file name extension. Empty files are considered uncompressed.
These utilities are not wrapper scripts but safer and more efficient C++
-programs. In particular the option @option{--recursive} is very efficient in
-those utilities supporting it.
+programs. In particular the option @option{--recursive} is efficient in those
+utilities supporting it.
@noindent
The utilities provided are @command{zcat}, @command{zcmp}, @command{zdiff},
@@ -116,14 +117,10 @@ the process of decompression.
@chapter Common options
@cindex common options
-The following
-@uref{http://www.nongnu.org/arg-parser/manual/arg_parser_manual.html#Argument-syntax,,options}:
-are available in all the utilities. Rather than writing identical
-descriptions for each of the programs, they are described here. Remember to
-prepend @file{./} to any file name beginning with a hyphen, or use @samp{--}.
-@ifnothtml
-@xref{Argument syntax,,,arg_parser}.
-@end ifnothtml
+The following options are available in all the utilities. Rather than
+writing identical descriptions for each of the programs, they are described
+here. Remember to prepend @file{./} to any file name beginning with a
+hyphen, or use @samp{--}. @xref{Argument syntax}.
@table @code
@item -h
@@ -146,8 +143,8 @@ compressors used must support the option @option{-V} for this to work).
@itemx --format=@var{format_list}
Process only the formats listed in the comma-separated @var{format_list}.
Valid formats are @samp{bz2}, @samp{gz}, @samp{lz}, @samp{xz}, @samp{zst},
-and @samp{un} for @samp{uncompressed}, meaning "any file name without a
-known extension". This option excludes files based on extension, instead of
+and @samp{un} for @samp{uncompressed}, meaning 'any file name without a
+known extension'. This option excludes files based on extension, instead of
format, because it is more efficient. The exclusion only applies to names
generated automatically (for example when adding extensions to a file name
or when operating recursively on directories). Files given in the command
@@ -192,7 +189,7 @@ standard output.
If the option @option{-q} is passed to zutils, the compression program must
also accept it.
@item
-It must return 0 if no errors occurred, and a non-zero value otherwise.
+It must return 0 if no errors occurred, and a nonzero value otherwise.
@end enumerate
@end table
@@ -204,7 +201,7 @@ and may be followed by a multiplier and an optional @samp{B} for "byte".
Table of SI and binary prefixes (unit multipliers):
@multitable {Prefix} {kilobyte (10^3 = 1000)} {|} {Prefix} {kibibyte (2^10 = 1024)}
-@item Prefix @tab Value @tab | @tab Prefix @tab Value
+@headitem Prefix @tab Value @tab | @tab Prefix @tab Value
@item k @tab kilobyte (10^3 = 1000) @tab | @tab Ki @tab kibibyte (2^10 = 1024)
@item M @tab megabyte (10^6) @tab | @tab Mi @tab mebibyte (2^20)
@item G @tab gigabyte (10^9) @tab | @tab Gi @tab gibibyte (2^30)
@@ -278,6 +275,7 @@ zcat [@var{options}] [@var{files}]
@noindent
Exit status is 0 if no errors occurred, 1 otherwise.
+@noindent
@command{zcat} supports the following options:
@table @code
@@ -290,6 +288,12 @@ Equivalent to @option{-vET}.
Number all nonblank output lines, starting with 1. The line count is
unlimited.
+@item -c
+@itemx --stdout
+@itemx -d
+@itemx --decompress
+Ignored, for gzip compatibility.
+
@item -e
Equivalent to @option{-vE}.
@@ -339,8 +343,8 @@ Print TAB characters as @samp{^I}.
@item -v
@itemx --show-nonprinting
Print control characters except for LF (newline) and TAB using @samp{^}
-notation and precede characters larger than 127 with @samp{M-} (which
-stands for "meta").
+notation and precede characters larger than 127 with @samp{M-} (which stands
+for "meta").
@item --verbose
Verbose mode. Show error messages. Repeating it increases the verbosity
@@ -378,6 +382,7 @@ corresponding compressed files of the remaining formats until one is found.
An exit status of 0 means no differences were found, 1 means some
differences were found, and 2 means trouble.
+@noindent
@command{zcmp} supports the following options:
@table @code
@@ -414,11 +419,11 @@ Compare at most @var{count} input bytes.
Force the compressed formats given. If @var{format1} or @var{format2} is
omitted, the corresponding format is automatically detected. Valid values
for @var{format} are @samp{bz2}, @samp{gz}, @samp{lz}, @samp{xz},
-@samp{zst}, and @samp{un} for @samp{uncompressed}. If at least one format is
-specified with this option, the file is passed to the corresponding
-decompressor (or transmitted unmodified) without checking its format, and
-the exact file names of both @var{file1} and @var{file2} must be given.
-Other names are not tried.
+@samp{zst}, and @samp{un} for @samp{uncompressed}. If this option is
+specified, the corresponding file is passed to the decompressor (or
+transmitted unmodified) without checking its format, and the exact file
+names of both @var{file1} and @var{file2} must be given. Other names are not
+tried.
@item -q
@itemx --quiet
@@ -473,6 +478,7 @@ corresponding compressed files of the remaining formats until one is found.
An exit status of 0 means no differences were found, 1 means some
differences were found, and 2 means trouble.
+@noindent
@command{zdiff} supports the following options (some options only work if
the diff program used supports them):
@@ -513,11 +519,11 @@ Ignore case differences. Consider uppercase and lowercase letters equivalent.
Force the compressed formats given. If @var{format1} or @var{format2} is
omitted, the corresponding format is automatically detected. Valid values
for @var{format} are @samp{bz2}, @samp{gz}, @samp{lz}, @samp{xz},
-@samp{zst}, and @samp{un} for @samp{uncompressed}. If at least one format is
-specified with this option, the file is passed to the corresponding
-decompressor (or transmitted unmodified) without checking its format, and
-the exact file names of both @var{file1} and @var{file2} must be given.
-Other names are not tried.
+@samp{zst}, and @samp{un} for @samp{uncompressed}. If this option is
+specified, the corresponding file is passed to the decompressor (or
+transmitted unmodified) without checking its format, and the exact file
+names of both @var{file1} and @var{file2} must be given. Other names are not
+tried.
@item -p
@itemx --show-c-function
@@ -603,6 +609,7 @@ 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.
+@noindent
@command{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 @command{zgrep} and not passed to grep):
@@ -750,7 +757,7 @@ 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".
+Use binary I/O on platforms affected by the bug known as 'text mode I/O'.
(MS-DOS, MS-Windows, OS/2).
@item -v
@@ -825,6 +832,7 @@ problems (file not found, invalid command-line options, I/O errors, etc),
2 if any compressed file is corrupt or invalid, or if any file has an
incorrect file name extension.
+@noindent
@command{ztest} supports the following options:
@table @code
@@ -887,11 +895,11 @@ Combining the options @option{--force} and @option{--keep}, as in
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}, @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 @w{(@samp{.tgz} ---> @samp{.tlz})} may be
+@file{.bz2}, @file{.gz}, @file{.xz}, @file{.zst}, or @file{.Z}, which are
+recompressed to @file{.lz};@*
+@file{.tbz}, @file{.tbz2}, @file{.tgz}, @file{.txz}, or @file{.tzst}, which
+are recompressed to @file{.tlz}.@*
+Keeping the combined extensions @w{(@file{.tgz} ---> @file{.tlz})} may be
useful when recompressing Slackware packages, for example.
Bzip2, gzip, and lzip are the primary formats. Xz and zstd are optional. If
@@ -917,6 +925,7 @@ occurred (file not found or not regular, or has invalid format, or can't be
deleted). 2 if a fatal error occurred (invalid command-line options,
compressor can't be run, or comparison fails).
+@noindent
@command{zupdate} supports the following options:
@table @code
@@ -924,13 +933,25 @@ compressor can't be run, or comparison fails).
@itemx --destdir=@var{dir}
Write recompressed files to another directory, using @var{dir} as base
directory, instead of writing them in the same directory as the original
-files. In recursive mode, this is done by replacing each directory specified
-in the command line with @var{dir} to produce the recompressed file names.
-For example, @w{@samp{zupdate -r -d @var{dir} ../a}} recompresses a file
-named @file{../a/b/c.gz} to @file{@var{dir}/b/c.lz}. Regular files specified
-in the command line are recompressed directly into @var{dir}. For example,
-@w{@samp{zupdate -d @var{dir} ../a/b/c.gz}} writes the recompressed file to
-@file{@var{dir}/c.lz}.
+files. This is done by removing the (possibly empty) prefix preceding the
+last slash (if any) of each @var{file} specified in the command line, and
+then prepending @var{dir} to produce the recompressed file names.
+
+In recursive mode, if @var{file} ends with a slash and names a directory, it
+is completely replaced with @var{dir}. Therefore, if @var{file} ends with a
+slash, all the files in @var{file} are recompressed directly into @var{dir},
+but if @var{file} does not end with a slash, the files in @var{file} are
+recompressed into the subdirectory @w{@var{dir}/`basename( @var{file} )`}.
+@samp{@var{file/}} is thus equivalent to @samp{@var{file/*}}, but without
+the danger of exceeding the length limit of the command line.
+
+For example, @w{@samp{zupdate -r -d @var{dir} ../a}} recompresses the file
+@file{../a/b.gz} to @file{@var{dir}/a/b.lz}, while
+@w{@samp{zupdate -r -d @var{dir} ../a/}} recompresses the same file to
+@file{@var{dir}/b.lz}. Regular files specified in the command line are
+recompressed directly into @var{dir}. For example,
+@w{@samp{zupdate -d @var{dir} ../a/b.gz}} writes the recompressed file to
+@file{@var{dir}/b.lz}.
This option allows recompressing files from a read-only file system to
another place without the need to copy or link them to the destination
@@ -939,8 +960,8 @@ read-only files to avoid warnings about files that can't be deleted).
@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}.
+Expand combined file name extensions; recompress @file{.tbz}, @file{.tbz2},
+@file{.tgz}, @file{.txz}, and @file{.tzst} to @file{tar.lz}.
@item -f
@itemx --force
@@ -1011,6 +1032,58 @@ archives by using a command like
@end table
+@node Argument syntax
+@chapter Syntax of command-line arguments
+@cindex argument syntax
+
+POSIX recommends these conventions for command-line arguments.
+
+@itemize @bullet
+@item A command-line argument is an option if it begins with a hyphen
+(@samp{-}).
+
+@item Option names are single alphanumeric characters.
+
+@item Certain options require an argument.
+
+@item An option and its argument may or may not appear as separate tokens.
+(In other words, the whitespace separating them is optional).
+Thus, @w{@option{-o foo}} and @option{-ofoo} are equivalent.
+
+@item One or more options without arguments, followed by at most one option
+that takes an argument, may follow a hyphen in a single token.
+Thus, @option{-abc} is equivalent to @w{@option{-a -b -c}}.
+
+@item Options typically precede other non-option arguments.
+
+@item The argument @samp{--} terminates all options; any following arguments
+are treated as non-option arguments, even if they begin with a hyphen.
+
+@item A token consisting of a single hyphen character is interpreted as an
+ordinary non-option argument. By convention, it is used to specify standard
+input, standard output, or a file named @samp{-}.
+@end itemize
+
+@noindent
+GNU adds @dfn{long options} to these conventions:
+
+@itemize @bullet
+@item A long option consists of two hyphens (@samp{--}) followed by a name
+made of alphanumeric characters and hyphens. Option names are typically one
+to three words long, with hyphens to separate words. Abbreviations can be
+used for the long option names as long as the abbreviations are unique.
+
+@item A long option and its argument may or may not appear as separate
+tokens. In the latter case they must be separated by an equal sign @samp{=}.
+Thus, @w{@option{--foo bar}} and @option{--foo=bar} are equivalent.
+@end itemize
+
+@noindent
+The syntax of options with an optional argument is
+@option{-<short_option><argument>} (without whitespace), or
+@option{--<long_option>=<argument>}.
+
+
@node Problems
@chapter Reporting bugs
@cindex bugs
diff --git a/testsuite/check.sh b/testsuite/check.sh
index 730dfa6..76e617f 100755
--- a/testsuite/check.sh
+++ b/testsuite/check.sh
@@ -39,7 +39,7 @@ mkdir tmp
cd "${objdir}"/tmp || framework_failure
for i in ${compressors}; do
- cat "${testdir}"/test.txt > in || framework_failure
+ cp "${testdir}"/test.txt in || framework_failure
$i in || compressor_needed
printf "Hello World!\n" > hello || framework_failure
$i hello || compressor_needed
@@ -47,13 +47,13 @@ for i in ${compressors}; do
$i zero || compressor_needed
done
-cat "${testdir}"/test.txt > in || framework_failure
-cat "${testdir}"/test.txt.tar > in.tar || framework_failure
+cp "${testdir}"/test.txt in || framework_failure
+cp "${testdir}"/test.txt.tar in.tar || framework_failure
printf "01234567890" > pin.tar4 || framework_failure
cat in.tar in.tar in.tar in.tar >> pin.tar4 || framework_failure
-cat in > -in- || framework_failure
-cat in.lz > -in-.lz || framework_failure
-cat in.lz > lz_only.lz || framework_failure
+cp in -- -in- || framework_failure
+cp in.lz -- -in-.lz || framework_failure
+cp in.lz lz_only.lz || framework_failure
cat in in in in in in > in6 || framework_failure
bad0_lz="${testdir}"/zero_bad_crc.lz
bad0_gz="${testdir}"/zero_bad_crc.gz
@@ -94,9 +94,9 @@ printf "LZIP\001-.............................." | "${ZCAT}" -N > /dev/null 2>&1
printf "LZIPxxxxxx" | "${ZCAT}" -N > /dev/null || test_failed $LINENO
printf "BZh9xxxxxx" | "${ZCAT}" -N > /dev/null || test_failed $LINENO
"${ZCAT}" -N -v -s "${testdir}"/zcat_vs.dat > /dev/null || test_failed $LINENO
-"${ZCAT}" -N < in > out || test_failed $LINENO
+"${ZCAT}" -N -c < in > out || test_failed $LINENO
cmp in out || test_failed $LINENO
-"${ZCAT}" -N < in.gz > out || test_failed $LINENO
+"${ZCAT}" -N -d < in.gz > out || test_failed $LINENO
cmp in out || test_failed $LINENO
"${ZCAT}" -N < in.bz2 > out || test_failed $LINENO
cmp in out || test_failed $LINENO
@@ -227,8 +227,8 @@ cat in.lz | "${ZCMP}" -N -O un,un in.lz - || test_failed $LINENO
[ $? = 2 ] || test_failed $LINENO
"${ZCMP}" -N --bad-option in in 2> /dev/null
[ $? = 2 ] || test_failed $LINENO
-cat in.gz > a.gz || framework_failure
-cat in.lz > a.lz || framework_failure
+cp in.gz a.gz || framework_failure
+cp in.lz a.lz || framework_failure
"${ZCMP}" -N a.gz || test_failed $LINENO
"${ZCMP}" -N a.lz || test_failed $LINENO
@@ -294,8 +294,8 @@ cat in.gz | "${ZDIFF}" -N -O un,un - in.gz || test_failed $LINENO
[ $? = 2 ] || test_failed $LINENO
mkdir tmp2
-cat in > tmp2/a || framework_failure
-cat in.lz > tmp2/a.lz || framework_failure
+cp in tmp2/a || framework_failure
+cp in.lz tmp2/a.lz || framework_failure
"${ZDIFF}" -N --format=bz2 tmp2/a 2> /dev/null
[ $? = 2 ] || test_failed $LINENO
"${ZDIFF}" -N --format=gz tmp2/a 2> /dev/null
@@ -405,10 +405,10 @@ rm -f empty.bz2 empty.gz empty.lz || framework_failure
"${ZTEST}" -N empty || test_failed $LINENO
# test wrong compressed extensions
-cat in.bz2 > in_bz2.gz || framework_failure
-cat in.gz > in_gz.lz || framework_failure
-cat in.lz > in_lz.bz2 || framework_failure
-cat in > in_un.lz || framework_failure
+cp in.bz2 in_bz2.gz || framework_failure
+cp in.gz in_gz.lz || framework_failure
+cp in.lz in_lz.bz2 || framework_failure
+cp in in_un.lz || framework_failure
"${ZTEST}" -Nq in_bz2.gz
[ $? = 2 ] || test_failed $LINENO
"${ZTEST}" -Nq in_gz.lz
@@ -446,8 +446,8 @@ dd if=in.lz bs=1000 count=1 2> /dev/null | "${ZTEST}" -N -q
printf "\ntesting zupdate-%s..." "$2"
"${ZUPDATE}" -N "" || test_failed $LINENO
-cat in.bz2 > a.bz2 || framework_failure
-cat in.gz > a.gz || framework_failure
+cp in.bz2 a.bz2 || framework_failure
+cp in.gz a.gz || framework_failure
"${ZUPDATE}" -Nq --bz2=bad_command a.bz2
[ $? = 1 ] || test_failed $LINENO
"${ZUPDATE}" -N --bz2='bzip2 --bad-option' a.bz2 > /dev/null 2>&1
@@ -492,8 +492,8 @@ rm -f a.lz || framework_failure
[ -e a.lz ] || test_failed $LINENO
rm -f a.lz || framework_failure
-cat in.bz2 > a.bz2 || framework_failure
-cat in.gz > a.gz || framework_failure
+cp in.bz2 a.bz2 || framework_failure
+cp in.gz a.gz || framework_failure
"${ZUPDATE}" -N -q -0 a.bz2 a.gz
[ $? = 1 ] || test_failed $LINENO
[ ! -e a.bz2 ] || test_failed $LINENO
@@ -501,16 +501,16 @@ cat in.gz > a.gz || framework_failure
[ -e a.lz ] || test_failed $LINENO
rm -f a.lz || framework_failure
-cat in.bz2 > a.bz2 || framework_failure
-cat in.gz > a.gz || framework_failure
+cp in.bz2 a.bz2 || framework_failure
+cp in.gz a.gz || framework_failure
"${ZUPDATE}" -N -0 -f -k a.bz2 a.gz || test_failed $LINENO
[ -e a.bz2 ] || test_failed $LINENO
[ -e a.gz ] || test_failed $LINENO
[ -e a.lz ] || test_failed $LINENO
rm -f a.lz || framework_failure
-cat in.bz2 > a.bz2 || framework_failure
-cat in.gz > a.gz || framework_failure
+cp in.bz2 a.bz2 || framework_failure
+cp in.gz a.gz || framework_failure
"${ZUPDATE}" -N -0 -f a.bz2 a.gz || test_failed $LINENO
[ ! -e a.bz2 ] || test_failed $LINENO
[ ! -e a.gz ] || test_failed $LINENO
@@ -518,9 +518,9 @@ cat in.gz > a.gz || framework_failure
[ -e a.lz ] || test_failed $LINENO
rm -f a.lz || framework_failure
-cat in.bz2 > a.tbz || framework_failure # keep combined extensions
-cat in.bz2 > b.tbz2 || framework_failure
-cat in.gz > c.tgz || framework_failure
+cp in.bz2 a.tbz || framework_failure # keep combined extensions
+cp in.bz2 b.tbz2 || framework_failure
+cp in.gz c.tgz || framework_failure
"${ZUPDATE}" -N -0 a.tbz b.tbz2 c.tgz || test_failed $LINENO
[ ! -e a.tbz ] || test_failed $LINENO
[ ! -e b.tbz2 ] || test_failed $LINENO
@@ -536,9 +536,9 @@ 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
+cp in.bz2 a.tbz || framework_failure # expand combined extensions
+cp in.bz2 b.tbz2 || framework_failure
+cp in.gz c.tgz || framework_failure
"${ZUPDATE}" -N -0 -e a.tbz b.tbz2 c.tgz || test_failed $LINENO
[ ! -e a.tbz ] || test_failed $LINENO
[ ! -e b.tbz2 ] || test_failed $LINENO
@@ -555,9 +555,9 @@ cat in.gz > c.tgz || framework_failure
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
+cp in.bz2 a.bz2 || framework_failure
+cp "${bad0_gz}" b.gz || framework_failure
+cp in.gz c.gz || framework_failure
"${ZUPDATE}" -N -0 -f a.bz2 b.gz c.gz 2> /dev/null
[ $? = 1 ] || test_failed $LINENO
[ ! -e a.bz2 ] || test_failed $LINENO
@@ -568,9 +568,9 @@ cat in.gz > c.gz || framework_failure
[ ! -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
+cp in.bz2 a.bz2 || framework_failure
+cp "${bad0_gz}" b.gz || framework_failure
+cp in.gz c.gz || framework_failure
"${ZUPDATE}" -N -0 -f -i a.bz2 b.gz c.gz 2> /dev/null
[ $? = 1 ] || test_failed $LINENO
[ ! -e a.bz2 ] || test_failed $LINENO
@@ -579,20 +579,20 @@ cat in.gz > c.gz || framework_failure
[ -e a.lz ] || test_failed $LINENO
rm -f a.lz b.gz c.gz || framework_failure
-cat in.bz2 > a.bz2 || framework_failure
+cp in.bz2 a.bz2 || framework_failure
"${ZUPDATE}" -N -0 -q a.bz2 || test_failed $LINENO
[ ! -e a.bz2 ] || test_failed $LINENO
[ -e a.lz ] || test_failed $LINENO
rm -f a.lz || framework_failure
-cat in.gz > 'name with spaces.gz' || framework_failure
+cp in.gz 'name with spaces.gz' || framework_failure
"${ZUPDATE}" -N -0 -q 'name with spaces.gz' || test_failed $LINENO
[ ! -e 'name with spaces.gz' ] || test_failed $LINENO
[ -e 'name with spaces.lz' ] || test_failed $LINENO
"${ZCMP}" -N in 'name with spaces.lz' || test_failed $LINENO
rm -f 'name with spaces.lz' || framework_failure
-cat zero.gz > z.gz || framework_failure
+cp zero.gz z.gz || framework_failure
"${ZUPDATE}" -N -0 -q z.gz || test_failed $LINENO
[ ! -e z.gz ] || test_failed $LINENO
[ -e z.lz ] || test_failed $LINENO
@@ -601,14 +601,14 @@ rm -f empty z.lz || framework_failure
mkdir tmp2
mkdir tmp2/tmp3
-cat in.bz2 > tmp2/tmp3/a.bz2 || framework_failure
-cat in.gz > tmp2/tmp3/a.gz || framework_failure
+cp in.bz2 tmp2/tmp3/a.bz2 || framework_failure
+cp in.gz tmp2/tmp3/a.gz || framework_failure
# test recursive to destdir
"${ZUPDATE}" -N -0 -k -r --format=gz --destdir=ddir1 tmp2 || test_failed $LINENO
[ -e tmp2/tmp3/a.bz2 ] || test_failed $LINENO
[ -e tmp2/tmp3/a.gz ] || test_failed $LINENO
-[ -e ddir1/tmp3/a.lz ] || test_failed $LINENO
-"${ZUPDATE}" -N -0 -k -r --format=bz2 --destdir="${objdir}"/tmp/ddir2 tmp2 ||
+[ -e ddir1/tmp2/tmp3/a.lz ] || test_failed $LINENO
+"${ZUPDATE}" -N -0 -k -r --format=bz2 --destdir="${objdir}"/tmp/ddir2 tmp2/ ||
test_failed $LINENO
[ -e tmp2/tmp3/a.bz2 ] || test_failed $LINENO
[ -e tmp2/tmp3/a.gz ] || test_failed $LINENO
@@ -618,11 +618,11 @@ cat in.gz > tmp2/tmp3/a.gz || framework_failure
[ -e tmp2/tmp3/a.bz2 ] || test_failed $LINENO
[ -e tmp2/tmp3/a.gz ] || test_failed $LINENO
[ -e ddir3/a.lz ] || test_failed $LINENO
-"${ZUPDATE}" -N -0 -k --destdir=ddir4/tmp2/tmp3 tmp2/tmp3/a.gz ||
+"${ZUPDATE}" -N -0 -k --destdir=ddir4/tmp0/tmp1 tmp2/tmp3/a.gz ||
test_failed $LINENO
[ -e tmp2/tmp3/a.bz2 ] || test_failed $LINENO
[ -e tmp2/tmp3/a.gz ] || test_failed $LINENO
-[ -e ddir4/tmp2/tmp3/a.lz ] || test_failed $LINENO
+[ -e ddir4/tmp0/tmp1/a.lz ] || test_failed $LINENO
rm -rf ddir1 ddir2 ddir3 ddir4 || framework_failure
# test recursive in place
"${ZUPDATE}" -N -0 -r --format=gz tmp2 || test_failed $LINENO
@@ -635,8 +635,8 @@ rm -f tmp2/tmp3/a.lz || framework_failure
[ ! -e tmp2/tmp3/a.gz ] || test_failed $LINENO
[ -e tmp2/tmp3/a.lz ] || test_failed $LINENO
rm -f tmp2/tmp3/a.lz || framework_failure
-cat in.bz2 > tmp2/tmp3/a.bz2 || framework_failure
-cat in.gz > tmp2/tmp3/a.gz || framework_failure
+cp in.bz2 tmp2/tmp3/a.bz2 || framework_failure
+cp in.gz tmp2/tmp3/a.gz || framework_failure
cd tmp2 || framework_failure
"${ZUPDATE}" -N -0 -r -k -f . || test_failed $LINENO
[ -e tmp3/a.bz2 ] || test_failed $LINENO
diff --git a/testsuite/test.txt b/testsuite/test.txt
index 9196a3a..423f0c0 100644
--- a/testsuite/test.txt
+++ b/testsuite/test.txt
@@ -1,8 +1,7 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -339,8 +338,7 @@ Public License instead of this License.
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
diff --git a/testsuite/test.txt.tar b/testsuite/test.txt.tar
index 92d6f1b..151ab1e 100644
--- a/testsuite/test.txt.tar
+++ b/testsuite/test.txt.tar
Binary files differ
diff --git a/testsuite/test_bad_crc.lz b/testsuite/test_bad_crc.lz
index c7d5bc9..d952c77 100644
--- a/testsuite/test_bad_crc.lz
+++ b/testsuite/test_bad_crc.lz
Binary files differ
diff --git a/zcat.cc b/zcat.cc
index e055fea..a87601c 100644
--- a/zcat.cc
+++ b/zcat.cc
@@ -110,6 +110,8 @@ void show_help()
" -V, --version output version information and exit\n"
" -A, --show-all equivalent to '-vET'\n"
" -b, --number-nonblank number nonblank output lines\n"
+ " -c, --stdout ignored, for gzip compatibility\n"
+ " -d, --decompress ignored, for gzip compatibility\n"
" -e equivalent to '-vE'\n"
" -E, --show-ends display '$' at end of each line\n"
" -M, --format=<list> process only the formats in <list>\n"
@@ -273,7 +275,7 @@ int main( const int argc, const char * const argv[] )
{ 'b', "number-nonblank", Arg_parser::no }, // cat
{ 'c', "stdout", Arg_parser::no }, // gzip
{ 'd', "decompress", Arg_parser::no }, // gzip
- { 'e', 0, Arg_parser::no }, // cat
+ { 'e', 0, Arg_parser::no }, // cat
{ 'E', "show-ends", Arg_parser::no }, // cat
{ 'f', "force", Arg_parser::no }, // gzip
{ 'h', "help", Arg_parser::no },
@@ -287,7 +289,7 @@ int main( const int argc, const char * const argv[] )
{ 'r', "recursive", Arg_parser::no },
{ 'R', "dereference-recursive", Arg_parser::no },
{ 's', "squeeze-blank", Arg_parser::no }, // cat
- { 't', 0, Arg_parser::no }, // cat
+ { 't', 0, Arg_parser::no }, // cat
{ 'T', "show-tabs", Arg_parser::no }, // cat
{ 'v', "show-nonprinting", Arg_parser::no }, // cat
{ 'V', "version", Arg_parser::no },
@@ -297,7 +299,7 @@ int main( const int argc, const char * const argv[] )
{ lz_opt, "lz", Arg_parser::yes },
{ xz_opt, "xz", Arg_parser::yes },
{ zst_opt, "zst", Arg_parser::yes },
- { 0, 0, Arg_parser::no } };
+ { 0, 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option
@@ -318,14 +320,14 @@ int main( const int argc, const char * const argv[] )
cat_options.show_nonprinting = true;
cat_options.show_tabs = true; break;
case 'b': cat_options.number_lines = 1; break;
- case 'c': break;
- case 'd': break;
+ case 'c': break; // ignored
+ case 'd': break; // ignored
case 'e': cat_options.show_nonprinting = true; // fall through
case 'E': cat_options.show_ends = true; break;
case 'f': break;
case 'h': show_help(); return 0;
- case 'l': break;
- case 'L': break;
+ case 'l': break; // ignored
+ case 'L': break; // ignored
case 'M': parse_format_list( arg, pn ); break;
case 'n': if( cat_options.number_lines == 0 )
{ cat_options.number_lines = 2; } break;
diff --git a/zcmp.cc b/zcmp.cc
index 5336a13..038ea9e 100644
--- a/zcmp.cc
+++ b/zcmp.cc
@@ -105,7 +105,7 @@ const char * format_num3( long long num )
char * p = buf + bufsize - 1; // fill the buffer backwards
*p = 0; // terminator
const bool negative = num < 0;
- if( num > 1024 || num < -1024 )
+ if( num > 9999 || num < -9999 )
{
char prefix = 0; // try binary first, then si
for( int i = 0; i < n && num != 0 && num % 1024 == 0; ++i )
@@ -405,7 +405,7 @@ int main( const int argc, const char * const argv[] )
{ lz_opt, "lz", Arg_parser::yes },
{ xz_opt, "xz", Arg_parser::yes },
{ zst_opt, "zst", Arg_parser::yes },
- { 0, 0, Arg_parser::no } };
+ { 0, 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option
@@ -430,7 +430,7 @@ int main( const int argc, const char * const argv[] )
case 'l': list = true; break;
case 'M': parse_format_list( sarg, pn ); break;
case 'n': max_size = getnum( arg, pn ); break;
- case 'N': break;
+ case 'N': break; // already processed
case 'O': parse_format_types2( sarg, pn, format_types ); break;
case 'q': verbosity = -1; break;
case 's': scripted = true; break;
diff --git a/zdiff.cc b/zdiff.cc
index a601459..8d8cd72 100644
--- a/zdiff.cc
+++ b/zdiff.cc
@@ -268,7 +268,7 @@ int main( const int argc, const char * const argv[] )
{ 'a', "text", Arg_parser::no },
{ 'b', "ignore-space-change", Arg_parser::no },
{ 'B', "ignore-blank-lines", Arg_parser::no },
- { 'c', 0, Arg_parser::no },
+ { 'c', 0, Arg_parser::no },
{ 'C', "context", Arg_parser::yes },
{ 'd', "minimal", Arg_parser::no },
{ 'E', "ignore-tab-expansion", Arg_parser::no },
@@ -282,7 +282,7 @@ int main( const int argc, const char * const argv[] )
{ 's', "report-identical-files", Arg_parser::no },
{ 't', "expand-tabs", Arg_parser::no },
{ 'T', "initial-tab", Arg_parser::no },
- { 'u', 0, Arg_parser::no },
+ { 'u', 0, Arg_parser::no },
{ 'U', "unified", Arg_parser::yes },
{ 'v', "verbose", Arg_parser::no },
{ 'V', "version", Arg_parser::no },
@@ -294,7 +294,7 @@ int main( const int argc, const char * const argv[] )
{ lz_opt, "lz", Arg_parser::yes },
{ xz_opt, "xz", Arg_parser::yes },
{ zst_opt, "zst", Arg_parser::yes },
- { 0, 0, Arg_parser::no } };
+ { 0, 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option
diff --git a/zgrep.cc b/zgrep.cc
index 8f4bc9d..229a640 100644
--- a/zgrep.cc
+++ b/zgrep.cc
@@ -244,7 +244,7 @@ int main( const int argc, const char * const argv[] )
{ 'h', "no-filename", Arg_parser::no }, // grep GNU
{ 'H', "with-filename", Arg_parser::no }, // grep GNU
{ 'i', "ignore-case", Arg_parser::no }, // grep
- { 'I', 0, Arg_parser::no }, // grep GNU
+ { 'I', 0, Arg_parser::no }, // grep GNU
{ 'l', "files-with-matches", Arg_parser::no }, // grep
{ 'L', "files-without-match", Arg_parser::no }, // grep GNU
{ 'm', "max-count", Arg_parser::yes }, // grep GNU
@@ -276,7 +276,7 @@ int main( const int argc, const char * const argv[] )
{ lz_opt, "lz", Arg_parser::yes },
{ xz_opt, "xz", Arg_parser::yes },
{ zst_opt, "zst", Arg_parser::yes },
- { 0, 0, Arg_parser::no } };
+ { 0, 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option
@@ -381,8 +381,8 @@ int main( const int argc, const char * const argv[] )
int retval = 1;
bool error = false;
bool stdin_used = false;
- while( next_filename( filenames, input_filename, error, recursive,
- false, no_messages ) )
+ while( next_filename( filenames, input_filename, error, recursive, false,
+ no_messages ) )
{
int infd;
if( input_filename == "." )
diff --git a/ztest.cc b/ztest.cc
index 5f74c20..f2cc68f 100644
--- a/ztest.cc
+++ b/ztest.cc
@@ -271,7 +271,7 @@ int main( const int argc, const char * const argv[] )
{ lz_opt, "lz", Arg_parser::yes },
{ xz_opt, "xz", Arg_parser::yes },
{ zst_opt, "zst", Arg_parser::yes },
- { 0, 0, Arg_parser::no } };
+ { 0, 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option
diff --git a/zupdate.cc b/zupdate.cc
index 64ca0d3..0d03b65 100644
--- a/zupdate.cc
+++ b/zupdate.cc
@@ -35,6 +35,10 @@
#include <sys/wait.h>
#if defined __MSVCRT__ || defined __OS2__
#include <io.h>
+#if defined __MSVCRT__
+#include <direct.h>
+#define mkdir(name,mode) _mkdir(name)
+#endif
#endif
#include "arg_parser.h"
@@ -96,24 +100,21 @@ void show_help()
" --lz=<command> set compressor and options for lzip format\n"
" --xz=<command> set compressor and options for xz format\n"
" --zst=<command> set compressor and options for zstd format\n"
- "\nValid formats for option '-M' are 'bz2', 'gz', 'lz', 'xz', and 'zst'.\n" );
+ "\nValid formats for option '-M' are 'bz2', 'gz', 'xz', and 'zst'.\n" );
show_help_addr();
}
-void extract_srcdir_name( const std::string & name, std::string & srcdir )
+/* if name contains slash(es), copy name into srcdir up to the last slash,
+ removing a leading dot followed by slash(es) */
+void extract_dirname( const std::string & name, std::string & srcdir )
{
- if( name.empty() || name == "." ) return; // leave srcdir empty
- if( name[name.size()-1] == '/' ) // remove last slash
- { srcdir.assign( name, 0, name.size() - 1 ); return; }
- struct stat st;
- if( stat( name.c_str(), &st ) == 0 && S_ISDIR( st.st_mode ) )
- { srcdir = name; return; }
-
- unsigned size = 0; // size of srcdir without last slash nor basename
- for( unsigned i = name.size(); i > 0; --i )
- if( name[i-1] == '/' ) { size = i - 1; break; }
- if( size > 0 ) srcdir.assign( name, 0, size );
+ unsigned i = 0;
+ unsigned j = name.size();
+ if( j >= 2 && name[0] == '.' && name[1] == '/' ) // remove leading "./"
+ for( i = 2; i < j && name[i] == '/'; ) ++i;
+ while( j > i && name[j-1] != '/' ) --j; // remove last component if any
+ if( j > i ) srcdir.assign( name, i, j - i );
}
@@ -214,7 +215,7 @@ int zupdate_file( const std::string & name, const char * const lzip_name,
if( srcdir.size() && name.compare( 0, srcdir.size(), srcdir ) != 0 )
internal_error( "srcdir mismatch." );
rname = destdir;
- if( rname[rname.size()-1] != '/' && name[srcdir.size()] != '/' )
+ if( rname.end()[-1] != '/' && name[srcdir.size()] != '/' )
rname += '/';
rname.append( name, srcdir.size(), name.size() - srcdir.size() -
std::strlen( extension_from( eindex ) ) );
@@ -395,16 +396,16 @@ int main( const int argc, const char * const argv[] )
const Arg_parser::Option options[] =
{
- { '0', 0, Arg_parser::no },
- { '1', 0, Arg_parser::no },
- { '2', 0, Arg_parser::no },
- { '3', 0, Arg_parser::no },
- { '4', 0, Arg_parser::no },
- { '5', 0, Arg_parser::no },
- { '6', 0, Arg_parser::no },
- { '7', 0, Arg_parser::no },
- { '8', 0, Arg_parser::no },
- { '9', 0, Arg_parser::no },
+ { '0', 0, Arg_parser::no },
+ { '1', 0, Arg_parser::no },
+ { '2', 0, Arg_parser::no },
+ { '3', 0, Arg_parser::no },
+ { '4', 0, Arg_parser::no },
+ { '5', 0, Arg_parser::no },
+ { '6', 0, Arg_parser::no },
+ { '7', 0, Arg_parser::no },
+ { '8', 0, Arg_parser::no },
+ { '9', 0, Arg_parser::no },
{ 'd', "destdir", Arg_parser::yes },
{ 'e', "expand-extensions", Arg_parser::no },
{ 'f', "force", Arg_parser::no },
@@ -424,7 +425,7 @@ int main( const int argc, const char * const argv[] )
{ lz_opt, "lz", Arg_parser::yes },
{ xz_opt, "xz", Arg_parser::yes },
{ zst_opt, "zst", Arg_parser::yes },
- { 0, 0, Arg_parser::no } };
+ { 0, 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option
@@ -488,7 +489,7 @@ int main( const int argc, const char * const argv[] )
while( true )
{
std::string srcdir; // dirname to be replaced by destdir
- if( destdir.size() ) extract_srcdir_name( filenames.front(), srcdir );
+ if( destdir.size() ) extract_dirname( filenames.front(), srcdir );
while( next_filename( filenames, input_filename, error, recursive, true ) )
{
int tmp = zupdate_file( input_filename, lzip_name, lzip_args2, srcdir,