1
0
Fork 0

Merging upstream version 1.0~rc6.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-24 05:40:46 +01:00
parent c9cf79d40a
commit 7a527f6c7c
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
25 changed files with 1114 additions and 772 deletions

View file

@ -1,14 +1,54 @@
2013-03-15 Antonio Diaz Diaz <ant_diaz@teleline.es>
* Version 1.0-rc6 released.
* zdiff.cc (set_data_feeder): Call decompressor with option "-q"
only if verbosity < 0.
* zutils.cc (set_data_feeder): Likewise.
2013-02-21 Antonio Diaz Diaz <ant_diaz@teleline.es>
* Version 1.0-rc5 released.
* main.cc (main): Zgrep now shows file names by default when
'--recursive' is selected.
* zcmp.cc: Fixed deadlock when '-n' option is used.
* main.cc: Use 'setmode' instead of '_setmode' on Windows and OS/2.
2012-12-04 Antonio Diaz Diaz <ant_diaz@teleline.es>
* Version 1.0-rc4 released.
* main.cc (main): Make 'grep_show_name' tri-state so that file name
is no prefixed to output by default when searching one file.
* zcat.cc (Line_number): Fixed a portability issue with Solaris 9.
2012-10-26 Antonio Diaz Diaz <ant_diaz@teleline.es>
* Version 1.0-rc3 released.
* Added new option '--format' to all utilities.
* Makefile.in: Added new target 'install-bin'.
2012-04-30 Antonio Diaz Diaz <ant_diaz@teleline.es>
* Version 1.0-rc2 released.
* Minor fixes.
* configure: 'datadir' renamed to 'datarootdir'.
2012-01-18 Antonio Diaz Diaz <ant_diaz@teleline.es>
* Version 1.0-rc1 released.
* Changed quote characters in messages as advised by GNU Standards.
* INSTALL: Document installing zutils along with GNU gzip.
2011-01-11 Antonio Diaz Diaz <ant_diaz@teleline.es> 2011-01-11 Antonio Diaz Diaz <ant_diaz@teleline.es>
* Version 0.9 released. * Version 0.9 released.
* configure: Added new options `DIFF' and `GREP'. * configure: Added new options 'DIFF' and 'GREP'.
* zcmp.cc: Fixed deadlock when files differ. * zcmp.cc: Fixed deadlock when files differ.
* zgrep.cc: Fixed deadlock when binary file matches. * zgrep.cc: Fixed deadlock when binary file matches.
2010-11-15 Antonio Diaz Diaz <ant_diaz@teleline.es> 2010-11-15 Antonio Diaz Diaz <ant_diaz@teleline.es>
* Version 0.8 released. * Version 0.8 released.
* main.cc: Added new options `--zcat', `--zgrep' and `--ztest'. * main.cc: Added new options '--zcat', '--zgrep' and '--ztest'.
* zcat.cc: New file implementing zcat+cat functionality in C++. * zcat.cc: New file implementing zcat+cat functionality in C++.
* zcmp.cc: New file implementing zcmp+cmp functionality in C++. * zcmp.cc: New file implementing zcmp+cmp functionality in C++.
* doc/zcmp.1: New file. * doc/zcmp.1: New file.
@ -20,7 +60,7 @@
documentation because egrep and fgrep are deprecated. documentation because egrep and fgrep are deprecated.
* ztest.cc: New file implementing ztest functionality in C++. * ztest.cc: New file implementing ztest functionality in C++.
* Makefile.in: Added quotes to directory names. * Makefile.in: Added quotes to directory names.
* testsuite/check.sh: Use `test.txt' instead of `COPYING' for testing. * testsuite/check.sh: Use 'test.txt' instead of 'COPYING' for testing.
* Removed environment safeguards from configure as requested by * Removed environment safeguards from configure as requested by
Richard Stallman. Now environment variables affect configure. Richard Stallman. Now environment variables affect configure.
@ -28,14 +68,14 @@
* Version 0.7 released. * Version 0.7 released.
* Added new utility; ztest. * Added new utility; ztest.
* zcat.in: Added new option `--recursive'. * zcat.in: Added new option '--recursive'.
2009-10-05 Antonio Diaz Diaz <ant_diaz@teleline.es> 2009-10-05 Antonio Diaz Diaz <ant_diaz@teleline.es>
* Version 0.6 released. * Version 0.6 released.
* zcat.in, zgrep.in: Removed again default compressor. Format of * zcat.in, zgrep.in: Removed again default compressor. Format of
data read from stdin is now automatically detected. data read from stdin is now automatically detected.
* Makefile.in: Added `--name' option to help2man invocation. * Makefile.in: Added '--name' option to help2man invocation.
2009-10-01 Antonio Diaz Diaz <ant_diaz@teleline.es> 2009-10-01 Antonio Diaz Diaz <ant_diaz@teleline.es>
@ -66,7 +106,7 @@
* Version 0.1 released. * Version 0.1 released.
Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. Copyright (C) 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
This file is a collection of facts, and thus it is not copyrightable, This file is a collection of facts, and thus it is not copyrightable,
but just in case, you have unlimited permission to copy, distribute and but just in case, you have unlimited permission to copy, distribute and

39
INSTALL
View file

@ -1,10 +1,19 @@
Requirements Requirements
------------ ------------
You will need a C++ compiler. You will need a C++ compiler.
I use gcc 4.3.5 and 3.3.6, but the code should compile with any I use gcc 4.7.2 and 3.3.6, but the code should compile with any
standards compliant compiler. standards compliant compiler.
Gcc is available at http://gcc.gnu.org. Gcc is available at http://gcc.gnu.org.
If you are installing zutils along with GNU gzip and want to keep the
gzip scripts, the recommended method is to configure gzip as follows:
./configure --program-transform-name='s/^z/gz/'
This renames, at installation time, the gzip scripts and man pages to
'gzcat', 'gzcat.1', etc, avoiding the name clashing with the programs
and man pages from zutils.
Procedure Procedure
--------- ---------
@ -18,7 +27,7 @@ This creates the directory ./zutils[version] containing the source from
the main archive. the main archive.
2. Change to zutils directory and run configure. 2. Change to zutils directory and run configure.
(Try `configure --help' for usage instructions). (Try 'configure --help' for usage instructions).
cd zutils[version] cd zutils[version]
./configure ./configure
@ -27,30 +36,34 @@ the main archive.
make make
4. Optionally, type `make check' to run the tests that come with zutils. 4. Optionally, type 'make check' to run the tests that come with zutils.
5. Type `make install' to install the programs and any data files and 5. Type 'make install' to install the programs and any data files and
documentation. documentation.
You can install only the programs, the info manual or the man pages
typing 'make install-bin', 'make install-info' or 'make install-man'
respectively.
Another way Another way
----------- -----------
You can also compile zutils into a separate directory. To do this, you You can also compile zutils into a separate directory. To do this, you
must use a version of `make' that supports the `VPATH' variable, such must use a version of 'make' that supports the 'VPATH' variable, such
as GNU `make'. `cd' to the directory where you want the object files as GNU 'make'. 'cd' to the directory where you want the object files
and executables to go and run the `configure' script. `configure' and executables to go and run the 'configure' script. 'configure'
automatically checks for the source code in `.', in `..' and in the automatically checks for the source code in '.', in '..' and in the
directory that `configure' is in. directory that 'configure' is in.
`configure' recognizes the option `--srcdir=DIR' to control where to 'configure' recognizes the option '--srcdir=DIR' to control where to
look for the sources. Usually `configure' can determine that directory look for the sources. Usually 'configure' can determine that directory
automatically. automatically.
After running `configure', you can run `make' and `make install' as After running 'configure', you can run 'make' and 'make install' as
explained above. explained above.
Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. Copyright (C) 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
This file is free documentation: you have unlimited permission to copy, This file is free documentation: you have unlimited permission to copy,
distribute and modify it. distribute and modify it.

View file

@ -13,8 +13,8 @@ zdiff_objs = arg_parser.o zutils.o zdiff.o
scripts = zcat zegrep zfgrep zgrep ztest scripts = zcat zegrep zfgrep zgrep ztest
.PHONY : all install install-info install-man install-strip \ .PHONY : all install install-bin install-info install-man install-strip \
uninstall uninstall-info uninstall-man \ uninstall uninstall-bin uninstall-info uninstall-man \
doc info man check dist clean distclean doc info man check dist clean distclean
all : $(progname) zcmp zdiff $(scripts) all : $(progname) zcmp zdiff $(scripts)
@ -67,10 +67,11 @@ $(objs) : Makefile
$(scripts) : Makefile $(scripts) : Makefile
arg_parser.o : arg_parser.h arg_parser.o : arg_parser.h
main.o : arg_parser.h zutils.h zcat.cc zgrep.cc ztest.cc main.o : arg_parser.h zutils.h zcat.cc zgrep.cc ztest.cc
zcmp.o : arg_parser.h zutils.h Makefile zcmp.o : arg_parser.h zutils.h zcmpdiff.cc Makefile
zdiff.o : arg_parser.h zutils.h Makefile zdiff.o : arg_parser.h zutils.h zcmpdiff.cc Makefile
zutils.o : zutils.h zutils.o : zutils.h
doc : info man doc : info man
info : $(VPATH)/doc/$(pkgname).info info : $(VPATH)/doc/$(pkgname).info
@ -107,7 +108,9 @@ Makefile : $(VPATH)/configure $(VPATH)/Makefile.in
check : all check : all
@$(VPATH)/testsuite/check.sh $(VPATH)/testsuite $(pkgversion) @$(VPATH)/testsuite/check.sh $(VPATH)/testsuite $(pkgversion)
install : all install-info install-man install : install-bin install-info install-man
install-bin : all
if [ ! -d "$(DESTDIR)$(bindir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(bindir)" ; fi if [ ! -d "$(DESTDIR)$(bindir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(bindir)" ; fi
$(INSTALL_PROGRAM) ./$(progname) "$(DESTDIR)$(bindir)/$(progname)" $(INSTALL_PROGRAM) ./$(progname) "$(DESTDIR)$(bindir)/$(progname)"
$(INSTALL_SCRIPT) ./zcat "$(DESTDIR)$(bindir)/zcat" $(INSTALL_SCRIPT) ./zcat "$(DESTDIR)$(bindir)/zcat"
@ -121,7 +124,7 @@ install : all install-info install-man
install-info : install-info :
if [ ! -d "$(DESTDIR)$(infodir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(infodir)" ; fi if [ ! -d "$(DESTDIR)$(infodir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(infodir)" ; fi
$(INSTALL_DATA) $(VPATH)/doc/$(pkgname).info "$(DESTDIR)$(infodir)/$(pkgname).info" $(INSTALL_DATA) $(VPATH)/doc/$(pkgname).info "$(DESTDIR)$(infodir)/$(pkgname).info"
-install-info --info-dir="$(DESTDIR)$(infodir)" $(DESTDIR)$(infodir)/$(pkgname).info -install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$(pkgname).info"
install-man : install-man :
if [ ! -d "$(DESTDIR)$(mandir)/man1" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1" ; fi if [ ! -d "$(DESTDIR)$(mandir)/man1" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1" ; fi
@ -129,14 +132,14 @@ install-man :
$(INSTALL_DATA) $(VPATH)/doc/zcmp.1 "$(DESTDIR)$(mandir)/man1/zcmp.1" $(INSTALL_DATA) $(VPATH)/doc/zcmp.1 "$(DESTDIR)$(mandir)/man1/zcmp.1"
$(INSTALL_DATA) $(VPATH)/doc/zdiff.1 "$(DESTDIR)$(mandir)/man1/zdiff.1" $(INSTALL_DATA) $(VPATH)/doc/zdiff.1 "$(DESTDIR)$(mandir)/man1/zdiff.1"
$(INSTALL_DATA) $(VPATH)/doc/zgrep.1 "$(DESTDIR)$(mandir)/man1/zgrep.1" $(INSTALL_DATA) $(VPATH)/doc/zgrep.1 "$(DESTDIR)$(mandir)/man1/zgrep.1"
-rm -f "$(DESTDIR)$(mandir)/man1/zegrep.1"
-rm -f "$(DESTDIR)$(mandir)/man1/zfgrep.1"
$(INSTALL_DATA) $(VPATH)/doc/ztest.1 "$(DESTDIR)$(mandir)/man1/ztest.1" $(INSTALL_DATA) $(VPATH)/doc/ztest.1 "$(DESTDIR)$(mandir)/man1/ztest.1"
install-strip : all install-strip : all
$(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install
uninstall : uninstall-info uninstall-man uninstall : uninstall-bin uninstall-info uninstall-man
uninstall-bin :
-rm -f "$(DESTDIR)$(bindir)/$(progname)" -rm -f "$(DESTDIR)$(bindir)/$(progname)"
-rm -f "$(DESTDIR)$(bindir)/zcat" -rm -f "$(DESTDIR)$(bindir)/zcat"
-rm -f "$(DESTDIR)$(bindir)/zcmp" -rm -f "$(DESTDIR)$(bindir)/zcmp"
@ -154,8 +157,6 @@ uninstall-man :
-rm -f "$(DESTDIR)$(mandir)/man1/zcat.1" -rm -f "$(DESTDIR)$(mandir)/man1/zcat.1"
-rm -f "$(DESTDIR)$(mandir)/man1/zcmp.1" -rm -f "$(DESTDIR)$(mandir)/man1/zcmp.1"
-rm -f "$(DESTDIR)$(mandir)/man1/zdiff.1" -rm -f "$(DESTDIR)$(mandir)/man1/zdiff.1"
-rm -f "$(DESTDIR)$(mandir)/man1/zegrep.1"
-rm -f "$(DESTDIR)$(mandir)/man1/zfgrep.1"
-rm -f "$(DESTDIR)$(mandir)/man1/zgrep.1" -rm -f "$(DESTDIR)$(mandir)/man1/zgrep.1"
-rm -f "$(DESTDIR)$(mandir)/man1/ztest.1" -rm -f "$(DESTDIR)$(mandir)/man1/ztest.1"

28
NEWS
View file

@ -1,8 +1,26 @@
Changes in version 0.9: Changes in version 1.0:
Configure now allows the selection of diff and grep programs to use. The new option "--format" has been added to all utilities.
A deadlock in zcmp.cc, which happened when files differ, has been fixed. Zgrep no more prefixes the file name to the output by default when
searching one file and "--recursive" has not been selected.
A deadlock in zgrep.cc, which happened when a binary file matches, has A deadlock in zcmp, which happens when the "-n" option is used, has been
been fixed. fixed.
Decompressors are now invoked without the "-q" option except if
explicitly requested, as some simplified implementations do not accept
it.
Minor fixes.
Quote characters in messages have been changed as advised by GNU Coding
Standards.
Configure option "--datadir" has been renamed to "--datarootdir" to
follow GNU Standards.
The target "install-bin" has been added to the Makefile.
Information has been added to the INSTALL file about how to install
zutils along with GNU gzip.

15
README
View file

@ -4,15 +4,20 @@ Zutils is a collection of utilities able to deal with any combination of
compressed and non-compressed files transparently. If any given file, compressed and non-compressed files transparently. If any given file,
including standard input, is compressed, its uncompressed content is including standard input, is compressed, its uncompressed content is
used. Compressed files are decompressed on the fly; no temporary files used. Compressed files are decompressed on the fly; no temporary files
are created. These utilities are not wrapper scripts but safer and more are created.
efficient C++ programs. In particular the "--recursive" option is very
efficient in those utilities supporting it. These utilities are not wrapper scripts but safer and more efficient C++
programs. In particular the "--recursive" option is very efficient in
those utilities supporting it.
The provided utilities are zcat, zcmp, zdiff, zgrep and ztest. The provided utilities are zcat, zcmp, zdiff, zgrep and ztest.
The supported compressors are bzip2, gzip, lzip and xz. The supported formats are bzip2, gzip, lzip and xz.
Zcat, zcmp, zdiff, and zgrep are improved replacements for the shell
scripts provided with GNU gzip. Ztest is unique to zutils.
Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. Copyright (C) 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
This file is free documentation: you have unlimited permission to copy, This file is free documentation: you have unlimited permission to copy,
distribute and modify it. distribute and modify it.

View file

@ -1,5 +1,6 @@
/* Arg_parser - A POSIX/GNU command line argument parser. (C++ version) /* Arg_parser - POSIX/GNU command line argument parser. (C++ version)
Copyright (C) 2006, 2007, 2008, 2009, 2010 Antonio Diaz Diaz. Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
Antonio Diaz Diaz.
This library is free software: you can redistribute it and/or modify This library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -35,7 +36,7 @@
bool Arg_parser::parse_long_option( const char * const opt, const char * const arg, bool Arg_parser::parse_long_option( const char * const opt, const char * const arg,
const Option options[], int & argind ) const Option options[], int & argind )
{ {
unsigned int len; unsigned len;
int index = -1; int index = -1;
bool exact = false, ambig = false; bool exact = false, ambig = false;
@ -43,7 +44,7 @@ bool Arg_parser::parse_long_option( const char * const opt, const char * const a
// Test all long options for either exact match or abbreviated matches. // Test all long options for either exact match or abbreviated matches.
for( int i = 0; options[i].code != 0; ++i ) for( int i = 0; options[i].code != 0; ++i )
if( options[i].name && !std::strncmp( options[i].name, &opt[2], len ) ) if( options[i].name && std::strncmp( options[i].name, &opt[2], len ) == 0 )
{ {
if( std::strlen( options[i].name ) == len ) // Exact match found if( std::strlen( options[i].name ) == len ) // Exact match found
{ index = i; exact = true; break; } { index = i; exact = true; break; }
@ -55,30 +56,30 @@ bool Arg_parser::parse_long_option( const char * const opt, const char * const a
if( ambig && !exact ) if( ambig && !exact )
{ {
error_ = "option `"; error_ += opt; error_ += "' is ambiguous"; error_ = "option '"; error_ += opt; error_ += "' is ambiguous";
return false; return false;
} }
if( index < 0 ) // nothing found if( index < 0 ) // nothing found
{ {
error_ = "unrecognized option `"; error_ += opt; error_ += '\''; error_ = "unrecognized option '"; error_ += opt; error_ += '\'';
return false; return false;
} }
++argind; ++argind;
data.push_back( Record( options[index].code ) ); data.push_back( Record( options[index].code ) );
if( opt[len+2] ) // `--<long_option>=<argument>' syntax if( opt[len+2] ) // '--<long_option>=<argument>' syntax
{ {
if( options[index].has_arg == no ) if( options[index].has_arg == no )
{ {
error_ = "option `--"; error_ += options[index].name; error_ = "option '--"; error_ += options[index].name;
error_ += "' doesn't allow an argument"; error_ += "' doesn't allow an argument";
return false; return false;
} }
if( options[index].has_arg == yes && !opt[len+3] ) if( options[index].has_arg == yes && !opt[len+3] )
{ {
error_ = "option `--"; error_ += options[index].name; error_ = "option '--"; error_ += options[index].name;
error_ += "' requires an argument"; error_ += "' requires an argument";
return false; return false;
} }
@ -90,7 +91,7 @@ bool Arg_parser::parse_long_option( const char * const opt, const char * const a
{ {
if( !arg || !arg[0] ) if( !arg || !arg[0] )
{ {
error_ = "option `--"; error_ += options[index].name; error_ = "option '--"; error_ += options[index].name;
error_ += "' requires an argument"; error_ += "' requires an argument";
return false; return false;
} }
@ -177,7 +178,7 @@ Arg_parser::Arg_parser( const int argc, const char * const argv[],
if( error_.size() ) data.clear(); if( error_.size() ) data.clear();
else else
{ {
for( unsigned int i = 0; i < non_options.size(); ++i ) for( unsigned i = 0; i < non_options.size(); ++i )
{ data.push_back( Record() ); data.back().argument.swap( non_options[i] ); } { data.push_back( Record() ); data.back().argument.swap( non_options[i] ); }
while( argind < argc ) while( argind < argc )
{ data.push_back( Record() ); data.back().argument = argv[argind++]; } { data.push_back( Record() ); data.back().argument = argv[argind++]; }

View file

@ -1,5 +1,6 @@
/* Arg_parser - A POSIX/GNU command line argument parser. (C++ version) /* Arg_parser - POSIX/GNU command line argument parser. (C++ version)
Copyright (C) 2006, 2007, 2008, 2009, 2010 Antonio Diaz Diaz. Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
Antonio Diaz Diaz.
This library is free software: you can redistribute it and/or modify This library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -25,12 +26,12 @@
Public License. Public License.
*/ */
/* Arg_parser reads the arguments in `argv' and creates a number of /* Arg_parser reads the arguments in 'argv' and creates a number of
option codes, option arguments and non-option arguments. option codes, option arguments and non-option arguments.
In case of error, `error' returns a non-empty error message. In case of error, 'error' returns a non-empty error message.
`options' is an array of `struct Option' terminated by an element 'options' is an array of 'struct Option' terminated by an element
containing a code which is zero. A null name means a short-only containing a code which is zero. A null name means a short-only
option. A code value outside the unsigned char range means a option. A code value outside the unsigned char range means a
long-only option. long-only option.
@ -39,13 +40,13 @@
were specified before all the non-option arguments for the purposes were specified before all the non-option arguments for the purposes
of parsing, even if the user of your program intermixed option and of parsing, even if the user of your program intermixed option and
non-option arguments. If you want the arguments in the exact order non-option arguments. If you want the arguments in the exact order
the user typed them, call `Arg_parser' with `in_order' = true. the user typed them, call 'Arg_parser' with 'in_order' = true.
The argument `--' terminates all options; any following arguments are The argument '--' terminates all options; any following arguments are
treated as non-option arguments, even if they begin with a hyphen. treated as non-option arguments, even if they begin with a hyphen.
The syntax for optional option arguments is `-<short_option><argument>' The syntax for optional option arguments is '-<short_option><argument>'
(without whitespace), or `--<long_option>=<argument>'. (without whitespace), or '--<long_option>=<argument>'.
*/ */
class Arg_parser class Arg_parser
@ -65,7 +66,7 @@ private:
{ {
int code; int code;
std::string argument; std::string argument;
Record( const int c = 0 ) : code( c ) {} explicit Record( const int c = 0 ) : code( c ) {}
}; };
std::string error_; std::string error_;
@ -84,20 +85,20 @@ public:
Arg_parser( const char * const opt, const char * const arg, Arg_parser( const char * const opt, const char * const arg,
const Option options[] ); const Option options[] );
const std::string & error() const throw() { return error_; } const std::string & error() const { return error_; }
// The number of arguments parsed (may be different from argc) // The number of arguments parsed (may be different from argc)
int arguments() const throw() { return data.size(); } int arguments() const { return data.size(); }
// If code( i ) is 0, argument( i ) is a non-option. // If code( i ) is 0, argument( i ) is a non-option.
// Else argument( i ) is the option's argument (or empty). // Else argument( i ) is the option's argument (or empty).
int code( const int i ) const throw() int code( const int i ) const
{ {
if( i >= 0 && i < arguments() ) return data[i].code; if( i >= 0 && i < arguments() ) return data[i].code;
else return 0; else return 0;
} }
const std::string & argument( const int i ) const throw() const std::string & argument( const int i ) const
{ {
if( i >= 0 && i < arguments() ) return data[i].argument; if( i >= 0 && i < arguments() ) return data[i].argument;
else return error_; else return error_;

99
configure vendored
View file

@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# configure script for Zutils - Utilities dealing with compressed files # configure script for Zutils - Utilities dealing with compressed files
# Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. # Copyright (C) 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
# #
# This configure script is free software: you have unlimited permission # This configure script is free software: you have unlimited permission
# to copy, distribute and modify it. # to copy, distribute and modify it.
@ -8,7 +8,7 @@
args= args=
no_create= no_create=
pkgname=zutils pkgname=zutils
pkgversion=0.9 pkgversion=1.0-rc6
progname=zutils progname=zutils
srctrigger=zutils.h srctrigger=zutils.h
@ -19,18 +19,23 @@ srcdir=
prefix=/usr/local prefix=/usr/local
exec_prefix='$(prefix)' exec_prefix='$(prefix)'
bindir='$(exec_prefix)/bin' bindir='$(exec_prefix)/bin'
datadir='$(prefix)/share' datarootdir='$(prefix)/share'
infodir='$(datadir)/info' infodir='$(datarootdir)/info'
mandir='$(datadir)/man' mandir='$(datarootdir)/man'
sysconfdir='$(prefix)/etc' CXX=g++
CPPFLAGS=
CXXFLAGS='-Wall -W -O2'
LDFLAGS=
DIFF=diff DIFF=diff
GREP=grep GREP=grep
if [ -n "${CXX}" ] ; then args="${args} CXX=\"${CXX}\"" ; fi # checking whether we are using GNU C++.
if [ -n "${CPPFLAGS}" ] ; then args="${args} CPPFLAGS=\"${CPPFLAGS}\"" ; fi if [ ! -x /bin/g++ ] &&
if [ -n "${CXXFLAGS}" ] ; then args="${args} CXXFLAGS=\"${CXXFLAGS}\"" [ ! -x /usr/bin/g++ ] &&
else CXXFLAGS='-Wall -W -O2' ; fi [ ! -x /usr/local/bin/g++ ] ; then
if [ -n "${LDFLAGS}" ] ; then args="${args} LDFLAGS=\"${LDFLAGS}\"" ; fi CXX=c++
CXXFLAGS='-W -O2'
fi
# Loop over all args # Loop over all args
while [ -n "$1" ] ; do while [ -n "$1" ] ; do
@ -44,12 +49,12 @@ while [ -n "$1" ] ; do
# Split out the argument for options that take them # Split out the argument for options that take them
case ${option} in case ${option} in
*=*) optarg=`echo ${option} | sed -e 's,^[^=]*=,,'` ;; *=*) optarg=`echo ${option} | sed -e 's,^[^=]*=,,;s,/$,,'` ;;
esac esac
# Process the options # Process the options
case ${option} in case ${option} in
--help | --he* | -h) --help | -h)
echo "Usage: configure [options]" echo "Usage: configure [options]"
echo echo
echo "Options: [defaults in brackets]" echo "Options: [defaults in brackets]"
@ -59,10 +64,9 @@ while [ -n "$1" ] ; do
echo " --prefix=DIR install into DIR [${prefix}]" echo " --prefix=DIR install into DIR [${prefix}]"
echo " --exec-prefix=DIR base directory for arch-dependent files [${exec_prefix}]" echo " --exec-prefix=DIR base directory for arch-dependent files [${exec_prefix}]"
echo " --bindir=DIR user executables directory [${bindir}]" echo " --bindir=DIR user executables directory [${bindir}]"
echo " --datadir=DIR base directory for doc and data [${datadir}]" echo " --datarootdir=DIR base directory for doc and data [${datarootdir}]"
echo " --infodir=DIR info files directory [${infodir}]" echo " --infodir=DIR info files directory [${infodir}]"
echo " --mandir=DIR man pages directory [${mandir}]" echo " --mandir=DIR man pages directory [${mandir}]"
echo " --sysconfdir=DIR read-only single-machine data directory [${sysconfdir}]"
echo " CXX=COMPILER C++ compiler to use [g++]" echo " CXX=COMPILER C++ compiler to use [g++]"
echo " CPPFLAGS=OPTIONS command line options for the preprocessor [${CPPFLAGS}]" echo " CPPFLAGS=OPTIONS command line options for the preprocessor [${CPPFLAGS}]"
echo " CXXFLAGS=OPTIONS command line options for the C++ compiler [${CXXFLAGS}]" echo " CXXFLAGS=OPTIONS command line options for the C++ compiler [${CXXFLAGS}]"
@ -71,34 +75,24 @@ while [ -n "$1" ] ; do
echo " GREP=NAME grep program to use with zgrep [grep]" echo " GREP=NAME grep program to use with zgrep [grep]"
echo echo
exit 0 ;; exit 0 ;;
--version | --ve* | -V) --version | -V)
echo "Configure script for ${pkgname} version ${pkgversion}" echo "Configure script for ${pkgname} version ${pkgversion}"
exit 0 ;; exit 0 ;;
--srcdir* | --sr*) --srcdir=*) srcdir=${optarg} ;;
srcdir=`echo ${optarg} | sed -e 's,/$,,'` ;; --prefix=*) prefix=${optarg} ;;
--prefix* | --pr*) --exec-prefix=*) exec_prefix=${optarg} ;;
prefix=`echo ${optarg} | sed -e 's,/$,,'` ;; --bindir=*) bindir=${optarg} ;;
--exec-prefix* | --ex*) --datarootdir=*) datarootdir=${optarg} ;;
exec_prefix=`echo ${optarg} | sed -e 's,/$,,'` ;; --infodir=*) infodir=${optarg} ;;
--bindir* | --bi*) --mandir=*) mandir=${optarg} ;;
bindir=`echo ${optarg} | sed -e 's,/$,,'` ;; --no-create) no_create=yes ;;
--datadir* | --da*)
datadir=`echo ${optarg} | sed -e 's,/$,,'` ;;
--infodir* | --inf*)
infodir=`echo ${optarg} | sed -e 's,/$,,'` ;;
--mandir* | --ma*)
mandir=`echo ${optarg} | sed -e 's,/$,,'` ;;
--sysconfdir* | --sy*)
sysconfdir=`echo ${optarg} | sed -e 's,/$,,'` ;;
--no-create | --no-c*)
no_create=yes ;;
CXX=*) CXX=${optarg} ;; CXX=*) CXX=${optarg} ;;
CPPFLAGS=*) CPPFLAGS=${optarg} ;; CPPFLAGS=*) CPPFLAGS=${optarg} ;;
CXXFLAGS=*) CXXFLAGS=${optarg} ;; CXXFLAGS=*) CXXFLAGS=${optarg} ;;
LDFLAGS=*) LDFLAGS=${optarg} ;; LDFLAGS=*) LDFLAGS=${optarg} ;;
DIFF=*) DIFF=${optarg} ;; DIFF=*) DIFF=${optarg} ;;
GREP=*) GREP=${optarg} ;; GREP=*) GREP=${optarg} ;;
--* | *=* | *-*-*) ;; --* | *=* | *-*-*) ;;
*) *)
@ -111,14 +105,14 @@ done
srcdirtext= srcdirtext=
if [ -z "${srcdir}" ] ; then if [ -z "${srcdir}" ] ; then
srcdirtext="or . or .." ; srcdir=. srcdirtext="or . or .." ; srcdir=.
if [ ! -r ${srcdir}/${srctrigger} ] ; then srcdir=.. ; fi if [ ! -r "${srcdir}/${srctrigger}" ] ; then srcdir=.. ; fi
if [ ! -r ${srcdir}/${srctrigger} ] ; then if [ ! -r "${srcdir}/${srctrigger}" ] ; then
## the sed command below emulates the dirname command ## the sed command below emulates the dirname command
srcdir=`echo $0 | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` srcdir=`echo $0 | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
fi fi
fi fi
if [ ! -r ${srcdir}/${srctrigger} ] ; then if [ ! -r "${srcdir}/${srctrigger}" ] ; then
exec 1>&2 exec 1>&2
echo echo
echo "configure: Can't find sources in ${srcdir} ${srcdirtext}" echo "configure: Can't find sources in ${srcdir} ${srcdirtext}"
@ -127,18 +121,7 @@ if [ ! -r ${srcdir}/${srctrigger} ] ; then
fi fi
# Set srcdir to . if that's what it is. # Set srcdir to . if that's what it is.
if [ "`pwd`" = "`cd ${srcdir} ; pwd`" ] ; then srcdir=. ; fi if [ "`pwd`" = "`cd "${srcdir}" ; pwd`" ] ; then srcdir=. ; fi
# checking whether we are using GNU C++.
if [ -z "${CXX}" ] ; then # Let the user override the test.
if [ -x /bin/g++ ] ||
[ -x /usr/bin/g++ ] ||
[ -x /usr/local/bin/g++ ] ; then
CXX="g++"
else
CXX="c++"
fi
fi
echo echo
if [ -z "${no_create}" ] ; then if [ -z "${no_create}" ] ; then
@ -162,10 +145,9 @@ echo "VPATH = ${srcdir}"
echo "prefix = ${prefix}" echo "prefix = ${prefix}"
echo "exec_prefix = ${exec_prefix}" echo "exec_prefix = ${exec_prefix}"
echo "bindir = ${bindir}" echo "bindir = ${bindir}"
echo "datadir = ${datadir}" echo "datarootdir = ${datarootdir}"
echo "infodir = ${infodir}" echo "infodir = ${infodir}"
echo "mandir = ${mandir}" echo "mandir = ${mandir}"
echo "sysconfdir = ${sysconfdir}"
echo "CXX = ${CXX}" echo "CXX = ${CXX}"
echo "CPPFLAGS = ${CPPFLAGS}" echo "CPPFLAGS = ${CPPFLAGS}"
echo "CXXFLAGS = ${CXXFLAGS}" echo "CXXFLAGS = ${CXXFLAGS}"
@ -175,7 +157,7 @@ echo "GREP = ${GREP}"
rm -f Makefile rm -f Makefile
cat > Makefile << EOF cat > Makefile << EOF
# Makefile for Zutils - Utilities dealing with compressed files # Makefile for Zutils - Utilities dealing with compressed files
# Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. # Copyright (C) 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
# This file was generated automatically by configure. Do not edit. # This file was generated automatically by configure. Do not edit.
# #
# This Makefile is free software: you have unlimited permission # This Makefile is free software: you have unlimited permission
@ -188,10 +170,9 @@ VPATH = ${srcdir}
prefix = ${prefix} prefix = ${prefix}
exec_prefix = ${exec_prefix} exec_prefix = ${exec_prefix}
bindir = ${bindir} bindir = ${bindir}
datadir = ${datadir} datarootdir = ${datarootdir}
infodir = ${infodir} infodir = ${infodir}
mandir = ${mandir} mandir = ${mandir}
sysconfdir = ${sysconfdir}
CXX = ${CXX} CXX = ${CXX}
CPPFLAGS = ${CPPFLAGS} CPPFLAGS = ${CPPFLAGS}
CXXFLAGS = ${CXXFLAGS} CXXFLAGS = ${CXXFLAGS}
@ -199,6 +180,6 @@ LDFLAGS = ${LDFLAGS}
DIFF = ${DIFF} DIFF = ${DIFF}
GREP = ${GREP} GREP = ${GREP}
EOF EOF
cat ${srcdir}/Makefile.in >> Makefile cat "${srcdir}/Makefile.in" >> Makefile
echo "OK. Now you can run make." echo "OK. Now you can run make."

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1.
.TH ZCAT "1" "January 2011" "Zcat (zutils) 0.9" "User Commands" .TH ZCAT "1" "March 2013" "Zcat (zutils) 1.0-rc6" "User Commands"
.SH NAME .SH NAME
Zcat \- decompress and concatenate files to standard output Zcat \- decompress and concatenate files to standard output
.SH SYNOPSIS .SH SYNOPSIS
@ -10,11 +10,12 @@ Zcat copies each given file ("\-" means standard input), to standard
output. If any given file is compressed, its uncompressed content is output. If any given file is compressed, its uncompressed content is
used. If a given file does not exist, and its name does not end with one used. If a given file does not exist, and its name does not end with one
of the known extensions, zcat tries the compressed file names of the known extensions, zcat tries the compressed file names
corresponding to the supported compressors. If no files are specified, corresponding to the supported formats. If no files are specified,
data is read from standard input, decompressed if needed, and sent to data is read from standard input, decompressed if needed, and sent to
standard output. Data read from standard input must be of the same type; standard output. Data read from standard input must be of the same type;
all uncompressed or all compressed with the same compressor. all uncompressed or all in the same compression format.
The supported compressors are bzip2, gzip, lzip and xz. .PP
The supported formats are bzip2, gzip, lzip and xz.
.PP .PP
Exit status is 0 if no errors occurred, 1 otherwise. Exit status is 0 if no errors occurred, 1 otherwise.
.SH OPTIONS .SH OPTIONS
@ -26,16 +27,19 @@ display this help and exit
output version information and exit output version information and exit
.TP .TP
\fB\-A\fR, \fB\-\-show\-all\fR \fB\-A\fR, \fB\-\-show\-all\fR
equivalent to `\-vET' equivalent to '\-vET'
.TP .TP
\fB\-b\fR, \fB\-\-number\-nonblank\fR \fB\-b\fR, \fB\-\-number\-nonblank\fR
number nonblank output lines number nonblank output lines
.TP .TP
\fB\-e\fR \fB\-e\fR
equivalent to `\-vE' equivalent to '\-vE'
.TP .TP
\fB\-E\fR, \fB\-\-show\-ends\fR \fB\-E\fR, \fB\-\-show\-ends\fR
display `$' at end of each line display '$' at end of each line
.TP
\fB\-\-format=\fR<fmt>
force given format (bz2, gz, lz, xz)
.TP .TP
\fB\-n\fR, \fB\-\-number\fR \fB\-n\fR, \fB\-\-number\fR
number all output lines number all output lines
@ -50,13 +54,13 @@ operate recursively on directories
never more than one single blank line never more than one single blank line
.TP .TP
\fB\-t\fR \fB\-t\fR
equivalent to `\-vT' equivalent to '\-vT'
.TP .TP
\fB\-T\fR, \fB\-\-show\-tabs\fR \fB\-T\fR, \fB\-\-show\-tabs\fR
display TAB characters as `^I' display TAB characters as '^I'
.TP .TP
\fB\-v\fR, \fB\-\-show\-nonprinting\fR \fB\-v\fR, \fB\-\-show\-nonprinting\fR
use `^' and `M\-' notation, except for LF and TAB use '^' and 'M\-' notation, except for LF and TAB
.TP .TP
\fB\-\-verbose\fR \fB\-\-verbose\fR
verbose mode (show error messages) verbose mode (show error messages)
@ -65,7 +69,7 @@ Report bugs to zutils\-bug@nongnu.org
.br .br
Zutils home page: http://www.nongnu.org/zutils/zutils.html Zutils home page: http://www.nongnu.org/zutils/zutils.html
.SH COPYRIGHT .SH COPYRIGHT
Copyright \(co 2011 Antonio Diaz Diaz. Copyright \(co 2013 Antonio Diaz Diaz.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
.br .br
This is free software: you are free to change and redistribute it. This is free software: you are free to change and redistribute it.

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1.
.TH ZCMP "1" "January 2011" "Zcmp (zutils) 0.9" "User Commands" .TH ZCMP "1" "March 2013" "Zcmp (zutils) 1.0-rc6" "User Commands"
.SH NAME .SH NAME
Zcmp \- decompress and compare two files byte by byte Zcmp \- decompress and compare two files byte by byte
.SH SYNOPSIS .SH SYNOPSIS
@ -11,7 +11,8 @@ differ, tells the first byte and line number where they differ. Bytes
and lines are numbered starting with 1. If any given file is compressed, and lines are numbered starting with 1. If any given file is compressed,
its uncompressed content is used. Compressed files are uncompressed on its uncompressed content is used. Compressed files are uncompressed on
the fly; no temporary files are created. the fly; no temporary files are created.
The supported compressors are bzip2, gzip, lzip and xz. .PP
The supported formats are bzip2, gzip, lzip and xz.
.PP .PP
Compares <file1> to <file2>. If <file2> is omitted zcmp tries the Compares <file1> to <file2>. If <file2> is omitted zcmp tries the
following: following:
@ -35,6 +36,9 @@ output version information and exit
\fB\-b\fR, \fB\-\-print\-bytes\fR \fB\-b\fR, \fB\-\-print\-bytes\fR
print differing bytes print differing bytes
.TP .TP
\fB\-\-format\fR=\fI[\fR<fmt1>][,<fmt2>]
force given formats (bz2, gz, lz, xz)
.TP
\fB\-i\fR, \fB\-\-ignore\-initial=\fR<n>[,<n2>] \fB\-i\fR, \fB\-\-ignore\-initial=\fR<n>[,<n2>]
ignore differences in the first <n> bytes ignore differences in the first <n> bytes
.TP .TP
@ -60,7 +64,7 @@ Report bugs to zutils\-bug@nongnu.org
.br .br
Zutils home page: http://www.nongnu.org/zutils/zutils.html Zutils home page: http://www.nongnu.org/zutils/zutils.html
.SH COPYRIGHT .SH COPYRIGHT
Copyright \(co 2011 Antonio Diaz Diaz. Copyright \(co 2013 Antonio Diaz Diaz.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
.br .br
This is free software: you are free to change and redistribute it. This is free software: you are free to change and redistribute it.

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1.
.TH ZDIFF "1" "January 2011" "Zdiff (zutils) 0.9" "User Commands" .TH ZDIFF "1" "March 2013" "Zdiff (zutils) 1.0-rc6" "User Commands"
.SH NAME .SH NAME
Zdiff \- decompress and compare two files line by line Zdiff \- decompress and compare two files line by line
.SH SYNOPSIS .SH SYNOPSIS
@ -11,7 +11,8 @@ differ, shows the differences line by line. If any given file is
compressed, its uncompressed content is used. Zdiff is a front end to compressed, its uncompressed content is used. Zdiff is a front end to
the diff program and has the limitation that messages from diff refer to the diff program and has the limitation that messages from diff refer to
temporary filenames instead of those specified. temporary filenames instead of those specified.
The supported compressors are bzip2, gzip, lzip and xz. .PP
The supported formats are bzip2, gzip, lzip and xz.
.PP .PP
Compares <file1> to <file2>. If <file2> is omitted zdiff tries the Compares <file1> to <file2>. If <file2> is omitted zdiff tries the
following: following:
@ -53,6 +54,9 @@ try hard to find a smaller set of changes
\fB\-E\fR, \fB\-\-ignore\-tab\-expansion\fR \fB\-E\fR, \fB\-\-ignore\-tab\-expansion\fR
ignore changes due to tab expansion ignore changes due to tab expansion
.TP .TP
\fB\-\-format\fR=\fI[\fR<fmt1>][,<fmt2>]
force given formats (bz2, gz, lz, xz)
.TP
\fB\-i\fR, \fB\-\-ignore\-case\fR \fB\-i\fR, \fB\-\-ignore\-case\fR
ignore case differences in file contents ignore case differences in file contents
.TP .TP
@ -87,7 +91,7 @@ Report bugs to zutils\-bug@nongnu.org
.br .br
Zutils home page: http://www.nongnu.org/zutils/zutils.html Zutils home page: http://www.nongnu.org/zutils/zutils.html
.SH COPYRIGHT .SH COPYRIGHT
Copyright \(co 2011 Antonio Diaz Diaz. Copyright \(co 2013 Antonio Diaz Diaz.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
.br .br
This is free software: you are free to change and redistribute it. This is free software: you are free to change and redistribute it.

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1.
.TH ZGREP "1" "January 2011" "Zgrep (zutils) 0.9" "User Commands" .TH ZGREP "1" "March 2013" "Zgrep (zutils) 1.0-rc6" "User Commands"
.SH NAME .SH NAME
Zgrep \- search compressed files for a regular expression Zgrep \- search compressed files for a regular expression
.SH SYNOPSIS .SH SYNOPSIS
@ -11,11 +11,12 @@ on any combination of compressed and non\-compressed files. If any given
file is compressed, its uncompressed content is used. If a given file file is compressed, its uncompressed content is used. If a given file
does not exist, and its name does not end with one of the known does not exist, and its name does not end with one of the known
extensions, zgrep tries the compressed file names corresponding to the extensions, zgrep tries the compressed file names corresponding to the
supported compressors. If no files are specified, data is read from supported formats. If no files are specified, data is read from
standard input, decompressed if needed, and fed to grep. Data read from standard input, decompressed if needed, and fed to grep. Data read from
standard input must be of the same type; all uncompressed or all standard input must be of the same type; all uncompressed or all
compressed with the same compressor. in the same compression format.
The supported compressors are bzip2, gzip, lzip and xz. .PP
The supported formats are bzip2, gzip, lzip and xz.
.PP .PP
Exit status is 0 if match, 1 if no match, 2 if trouble. Exit status is 0 if match, 1 if no match, 2 if trouble.
.SH OPTIONS .SH OPTIONS
@ -56,6 +57,9 @@ obtain patterns from <file>
\fB\-F\fR, \fB\-\-fixed\-strings\fR \fB\-F\fR, \fB\-\-fixed\-strings\fR
<pattern> is a set of newline\-separated strings <pattern> is a set of newline\-separated strings
.TP .TP
\fB\-\-format=\fR<fmt>
force given format (bz2, gz, lz, xz)
.TP
\fB\-h\fR, \fB\-\-no\-filename\fR \fB\-h\fR, \fB\-\-no\-filename\fR
suppress the prefixing filename on output suppress the prefixing filename on output
.TP .TP
@ -108,7 +112,7 @@ Report bugs to zutils\-bug@nongnu.org
.br .br
Zutils home page: http://www.nongnu.org/zutils/zutils.html Zutils home page: http://www.nongnu.org/zutils/zutils.html
.SH COPYRIGHT .SH COPYRIGHT
Copyright \(co 2011 Antonio Diaz Diaz. Copyright \(co 2013 Antonio Diaz Diaz.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
.br .br
This is free software: you are free to change and redistribute it. This is free software: you are free to change and redistribute it.

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1.
.TH ZTEST "1" "January 2011" "Ztest (zutils) 0.9" "User Commands" .TH ZTEST "1" "March 2013" "Ztest (zutils) 1.0-rc6" "User Commands"
.SH NAME .SH NAME
Ztest \- verify integrity of compressed files Ztest \- verify integrity of compressed files
.SH SYNOPSIS .SH SYNOPSIS
@ -9,10 +9,16 @@ Ztest \- verify integrity of compressed files
Ztest verifies the integrity of the specified compressed files. Ztest verifies the integrity of the specified compressed files.
Non\-compressed files are ignored. If no files are specified, the Non\-compressed files are ignored. If no files are specified, the
integrity of compressed data read from standard input is verified. Data integrity of compressed data read from standard input is verified. Data
read from standard input must be all compressed with the same compressor. read from standard input must be all in the same compression format.
The supported compressors are bzip2, gzip, lzip and xz.
.PP .PP
Exit status is 2 if any compressed file is corrupt, 0 otherwise. The supported formats are bzip2, gzip, lzip and xz.
.PP
Note that some xz files lack integrity information, and therefore can't
be verified as reliably as the other formats can.
.PP
Exit status is 0 if all compressed files verify OK, 1 if environmental
problems (file not found, invalid flags, I/O errors, etc), 2 if any
compressed file is corrupt or invalid.
.SH OPTIONS .SH OPTIONS
.TP .TP
\fB\-h\fR, \fB\-\-help\fR \fB\-h\fR, \fB\-\-help\fR
@ -21,6 +27,9 @@ display this help and exit
\fB\-V\fR, \fB\-\-version\fR \fB\-V\fR, \fB\-\-version\fR
output version information and exit output version information and exit
.TP .TP
\fB\-\-format=\fR<fmt>
force given format (bz2, gz, lz, xz)
.TP
\fB\-q\fR, \fB\-\-quiet\fR \fB\-q\fR, \fB\-\-quiet\fR
suppress all messages suppress all messages
.TP .TP
@ -34,7 +43,7 @@ Report bugs to zutils\-bug@nongnu.org
.br .br
Zutils home page: http://www.nongnu.org/zutils/zutils.html Zutils home page: http://www.nongnu.org/zutils/zutils.html
.SH COPYRIGHT .SH COPYRIGHT
Copyright \(co 2011 Antonio Diaz Diaz. Copyright \(co 2013 Antonio Diaz Diaz.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
.br .br
This is free software: you are free to change and redistribute it. This is free software: you are free to change and redistribute it.

View file

@ -12,7 +12,7 @@ File: zutils.info, Node: Top, Next: Introduction, Up: (dir)
Zutils Manual Zutils Manual
************* *************
This manual is for Zutils (version 0.9, 11 January 2011). This manual is for Zutils (version 1.0-rc6, 15 March 2013).
* Menu: * Menu:
@ -26,7 +26,7 @@ This manual is for Zutils (version 0.9, 11 January 2011).
* Concept Index:: Index of concepts * Concept Index:: Index of concepts
Copyright (C) 2008, 2009, 2010, 2011 Antonio Diaz Diaz. Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
This manual is free documentation: you have unlimited permission to This manual is free documentation: you have unlimited permission to
copy, distribute and modify it. copy, distribute and modify it.
@ -41,12 +41,17 @@ Zutils is a collection of utilities able to deal with any combination of
compressed and non-compressed files transparently. If any given file, compressed and non-compressed files transparently. If any given file,
including standard input, is compressed, its uncompressed content is including standard input, is compressed, its uncompressed content is
used. Compressed files are decompressed on the fly; no temporary files used. Compressed files are decompressed on the fly; no temporary files
are created. These utilities are not wrapper scripts but safer and more are created.
efficient C++ programs. In particular the `--recursive' option is very
efficient in those utilities supporting it. These utilities are not wrapper scripts but safer and more efficient
C++ programs. In particular the `--recursive' option is very efficient
in those utilities supporting it.
The provided utilities are zcat, zcmp, zdiff, zgrep and ztest. The provided utilities are zcat, zcmp, zdiff, zgrep and ztest.
The supported compressors are bzip2, gzip, lzip and xz. The supported formats are bzip2, gzip, lzip and xz.
Zcat, zcmp, zdiff, and zgrep are improved replacements for the shell
scripts provided with GNU gzip. Ztest is unique to zutils.
Numbers given as arguments to options (positions, sizes) may be Numbers given as arguments to options (positions, sizes) may be
@ -74,10 +79,12 @@ Zcat copies each given file (`-' means standard input), to standard
output. If any given file is compressed, its uncompressed content is output. If any given file is compressed, its uncompressed content is
used. If a given file does not exist, and its name does not end with one used. If a given file does not exist, and its name does not end with one
of the known extensions, zcat tries the compressed file names of the known extensions, zcat tries the compressed file names
corresponding to the supported compressors. If no files are specified, corresponding to the supported formats.
data is read from standard input, decompressed if needed, and sent to
standard output. Data read from standard input must be of the same type; If no files are specified, data is read from standard input,
all uncompressed or all compressed with the same compressor. decompressed if needed, and sent to standard output. Data read from
standard input must be of the same type; all uncompressed or all in the
same compression format.
The format for running zcat is: The format for running zcat is:
@ -111,6 +118,11 @@ Exit status is 0 if no errors occurred, 1 otherwise.
`--show-ends' `--show-ends'
Print a `$' after the end of each line. Print a `$' after the end of each line.
`--format=FMT'
Force the given compression format. Valid values for FMT are
`bz2', `gz', `lz' and `xz'. If this option is used, the exact file
name must be given. Other names won't be tried.
`-n' `-n'
`--number' `--number'
Number all output lines, starting with 1. The line count is Number all output lines, starting with 1. The line count is
@ -161,13 +173,18 @@ fly; no temporary files are created.
zcmp [OPTIONS] FILE1 [FILE2] zcmp [OPTIONS] FILE1 [FILE2]
Compares FILE1 to FILE2. If FILE2 is omitted zcmp tries the following: This compares FILE1 to FILE2. If FILE2 is omitted zcmp tries the
If FILE1 is compressed, compares FILE1 to the file with the following:
corresponding decompressed file name (removes the extension from FILE1).
If FILE1 is not compressed, compares FILE1 to the uncompressed contents 1. If FILE1 is compressed, compares FILE1 to the file with the
of FILE1.[bz2|gz|lz|xz] (the first one that is found). corresponding decompressed file name (removes the extension from
If no suitable file is found, compares FILE1 to data read from standard FILE1).
input.
2. If FILE1 is not compressed, compares FILE1 to the uncompressed
contents of FILE1.[bz2|gz|lz|xz] (the first one that is found).
3. If no suitable file is found, compares FILE1 to data read from
standard input.
An exit status of 0 means no differences were found, 1 means some An exit status of 0 means no differences were found, 1 means some
differences were found, and 2 means trouble. differences were found, and 2 means trouble.
@ -188,6 +205,14 @@ differences were found, and 2 means trouble.
a letter, and precede bytes larger than 127 with `M-' (which stands a letter, and precede bytes larger than 127 with `M-' (which stands
for "meta"). for "meta").
`--format=[FMT1][,FMT2]'
Force the given compression formats. Any of FMT1 or FMT2 may be
omitted and the corresponding format will be automatically
detected. Valid values for FMT are `bz2', `gz', `lz' and `xz'. If
at least one format is specified with this option, the exact file
names of both FILE1 and FILE2 must be given. Other names won't be
tried.
`-i SIZE' `-i SIZE'
`--ignore-initial=SIZE' `--ignore-initial=SIZE'
Ignore any differences in the first SIZE bytes of the input files. Ignore any differences in the first SIZE bytes of the input files.
@ -231,13 +256,18 @@ temporary filenames instead of those specified.
zdiff [OPTIONS] FILE1 [FILE2] zdiff [OPTIONS] FILE1 [FILE2]
Compares FILE1 to FILE2. If FILE2 is omitted zdiff tries the following: This compares FILE1 to FILE2. If FILE2 is omitted zdiff tries the
If FILE1 is compressed, compares FILE1 to the file with the following:
corresponding decompressed file name (removes the extension from FILE1).
If FILE1 is not compressed, compares FILE1 to the uncompressed contents 1. If FILE1 is compressed, compares FILE1 to the file with the
of FILE1.[bz2|gz|lz|xz] (the first one that is found). corresponding decompressed file name (removes the extension from
If no suitable file is found, compares FILE1 to data read from standard FILE1).
input.
2. If FILE1 is not compressed, compares FILE1 to the uncompressed
contents of FILE1.[bz2|gz|lz|xz] (the first one that is found).
3. If no suitable file is found, compares FILE1 to data read from
standard input.
An exit status of 0 means no differences were found, 1 means some An exit status of 0 means no differences were found, 1 means some
differences were found, and 2 means trouble. differences were found, and 2 means trouble.
@ -279,6 +309,14 @@ differences were found, and 2 means trouble.
`--ignore-tab-expansion' `--ignore-tab-expansion'
Ignore changes due to tab expansion. Ignore changes due to tab expansion.
`--format=[FMT1][,FMT2]'
Force the given compression formats. Any of FMT1 or FMT2 may be
omitted and the corresponding format will be automatically
detected. Valid values for FMT are `bz2', `gz', `lz' and `xz'. If
at least one format is specified with this option, the exact file
names of both FILE1 and FILE2 must be given. Other names won't be
tried.
`-i' `-i'
`--ignore-case' `--ignore-case'
Ignore case differences in file contents. Ignore case differences in file contents.
@ -326,10 +364,12 @@ on any combination of compressed and non-compressed files. If any given
file is compressed, its uncompressed content is used. If a given file file is compressed, its uncompressed content is used. If a given file
does not exist, and its name does not end with one of the known does not exist, and its name does not end with one of the known
extensions, zgrep tries the compressed file names corresponding to the extensions, zgrep tries the compressed file names corresponding to the
supported compressors. If no files are specified, data is read from supported formats.
standard input, decompressed if needed, and fed to grep. Data read from
standard input must be of the same type; all uncompressed or all If no files are specified, data is read from standard input,
compressed with the same compressor. decompressed if needed, and fed to grep. Data read from standard input
must be of the same type; all uncompressed or all in the same
compression format.
The format for running zgrep is: The format for running zgrep is:
@ -387,9 +427,15 @@ matches were found, and 2 means trouble.
`--fixed-strings' `--fixed-strings'
Treat PATTERN as a set of newline-separated strings. Treat PATTERN as a set of newline-separated strings.
`--format=FMT'
Force the given compression format. Valid values for FMT are
`bz2', `gz', `lz' and `xz'. If this option is used, the exact file
name must be given. Other names won't be tried.
`-h' `-h'
`--no-filename' `--no-filename'
Suppress the prefixing filename on output. Suppress the prefixing of filenames on output when multiple files
are searched.
`-H' `-H'
`--with-filename' `--with-filename'
@ -459,16 +505,18 @@ File: zutils.info, Node: Ztest, Next: Problems, Prev: Zgrep, Up: Top
Ztest verifies the integrity of the specified compressed files. Ztest verifies the integrity of the specified compressed files.
Non-compressed files are ignored. If no files are specified, the Non-compressed files are ignored. If no files are specified, the
integrity of compressed data read from standard input is verified. Data integrity of compressed data read from standard input is verified. Data
read from standard input must be all compressed with the same read from standard input must be all in the same compression format.
compressor.
Note that some xz files lack integrity information, and therefore
can't be verified as reliably as the other formats can.
The format for running ztest is: The format for running ztest is:
ztest [OPTIONS] [FILES] ztest [OPTIONS] [FILES]
The exit status is 0 if all files verify OK, 1 for environmental The exit status is 0 if all compressed files verify OK, 1 if
problems (file not found, invalid flags, I/O errors, etc), 2 to indicate environmental problems (file not found, invalid flags, I/O errors, etc),
a corrupt or invalid input file. 2 if any compressed file is corrupt or invalid.
Ztest supports the following options: Ztest supports the following options:
@ -480,6 +528,11 @@ a corrupt or invalid input file.
`--version' `--version'
Print the version number of ztest on the standard output and exit. Print the version number of ztest on the standard output and exit.
`--format=FMT'
Force the given compression format. Valid values for FMT are
`bz2', `gz', `lz' and `xz'. If this option is used, all files not
in the given format will fail.
`-q' `-q'
`--quiet' `--quiet'
Quiet operation. Suppress all messages. Quiet operation. Suppress all messages.
@ -531,13 +584,18 @@ Concept Index
 
Tag Table: Tag Table:
Node: Top224 Node: Top224
Node: Introduction992 Node: Introduction1006
Node: Zcat2447 Node: Zcat2593
Node: Zcmp4331 Node: Zcmp4679
Node: Zdiff6388 Node: Zdiff7165
Node: Zgrep8625 Node: Zgrep9831
Node: Ztest11392 Node: Ztest12842
Node: Problems12475 Node: Problems14246
Node: Concept Index13004 Node: Concept Index14775
 
End Tag Table End Tag Table

Local Variables:
coding: iso-8859-15
End:

View file

@ -1,12 +1,13 @@
\input texinfo @c -*-texinfo-*- \input texinfo @c -*-texinfo-*-
@c %**start of header @c %**start of header
@setfilename zutils.info @setfilename zutils.info
@documentencoding ISO-8859-15
@settitle Zutils Manual @settitle Zutils Manual
@finalout @finalout
@c %**end of header @c %**end of header
@set UPDATED 11 January 2011 @set UPDATED 15 March 2013
@set VERSION 0.9 @set VERSION 1.0-rc6
@dircategory Data Compression @dircategory Data Compression
@direntry @direntry
@ -45,7 +46,8 @@ This manual is for Zutils (version @value{VERSION}, @value{UPDATED}).
@end menu @end menu
@sp 1 @sp 1
Copyright @copyright{} 2008, 2009, 2010, 2011 Antonio Diaz Diaz. Copyright @copyright{} 2008, 2009, 2010, 2011, 2012, 2013
Antonio Diaz Diaz.
This manual is free documentation: you have unlimited permission This manual is free documentation: you have unlimited permission
to copy, distribute and modify it. to copy, distribute and modify it.
@ -59,13 +61,18 @@ Zutils is a collection of utilities able to deal with any combination of
compressed and non-compressed files transparently. If any given file, compressed and non-compressed files transparently. If any given file,
including standard input, is compressed, its uncompressed content is including standard input, is compressed, its uncompressed content is
used. Compressed files are decompressed on the fly; no temporary files used. Compressed files are decompressed on the fly; no temporary files
are created. These utilities are not wrapper scripts but safer and more are created.
efficient C++ programs. In particular the @samp{--recursive} option is
very efficient in those utilities supporting it. These utilities are not wrapper scripts but safer and more efficient C++
programs. In particular the @samp{--recursive} option is very efficient
in those utilities supporting it.
@noindent @noindent
The provided utilities are zcat, zcmp, zdiff, zgrep and ztest.@* The provided utilities are zcat, zcmp, zdiff, zgrep and ztest.@*
The supported compressors are bzip2, gzip, lzip and xz. The supported formats are bzip2, gzip, lzip and xz.
Zcat, zcmp, zdiff, and zgrep are improved replacements for the shell
scripts provided with GNU gzip. Ztest is unique to zutils.
@sp 1 @sp 1
Numbers given as arguments to options (positions, sizes) may be followed Numbers given as arguments to options (positions, sizes) may be followed
@ -94,10 +101,12 @@ Zcat copies each given file (@samp{-} means standard input), to standard
output. If any given file is compressed, its uncompressed content is output. If any given file is compressed, its uncompressed content is
used. If a given file does not exist, and its name does not end with one used. If a given file does not exist, and its name does not end with one
of the known extensions, zcat tries the compressed file names of the known extensions, zcat tries the compressed file names
corresponding to the supported compressors. If no files are specified, corresponding to the supported formats.
data is read from standard input, decompressed if needed, and sent to
standard output. Data read from standard input must be of the same type; If no files are specified, data is read from standard input,
all uncompressed or all compressed with the same compressor. decompressed if needed, and sent to standard output. Data read from
standard input must be of the same type; all uncompressed or all in the
same compression format.
The format for running zcat is: The format for running zcat is:
@ -135,6 +144,11 @@ Equivalent to @samp{-vE}.
@itemx --show-ends @itemx --show-ends
Print a @samp{$} after the end of each line. Print a @samp{$} after the end of each line.
@item --format=@var{fmt}
Force the given compression format. Valid values for @var{fmt} are
@samp{bz2}, @samp{gz}, @samp{lz} and @samp{xz}. If this option is used,
the exact file name must be given. Other names won't be tried.
@item -n @item -n
@itemx --number @itemx --number
Number all output lines, starting with 1. The line count is unlimited. Number all output lines, starting with 1. The line count is unlimited.
@ -187,16 +201,22 @@ zcmp [@var{options}] @var{file1} [@var{file2}]
@end example @end example
@noindent @noindent
Compares @var{file1} to @var{file2}. If @var{file2} is omitted zcmp This compares @var{file1} to @var{file2}. If @var{file2} is omitted zcmp
tries the following:@* tries the following:
@enumerate
@item
If @var{file1} is compressed, compares @var{file1} to the file with the If @var{file1} is compressed, compares @var{file1} to the file with the
corresponding decompressed file name (removes the extension from corresponding decompressed file name (removes the extension from
@var{file1}).@* @var{file1}).
@item
If @var{file1} is not compressed, compares @var{file1} to the If @var{file1} is not compressed, compares @var{file1} to the
uncompressed contents of @var{file1}.[bz2|gz|lz|xz] (the first one that uncompressed contents of @var{file1}.[bz2|gz|lz|xz] (the first one that
is found).@* is found).
@item
If no suitable file is found, compares @var{file1} to data read from If no suitable file is found, compares @var{file1} to data read from
standard input. standard input.
@end enumerate
@noindent @noindent
An exit status of 0 means no differences were found, 1 means some An exit status of 0 means no differences were found, 1 means some
@ -219,6 +239,14 @@ Print the differing bytes. Print control bytes as a @samp{^} followed by
a letter, and precede bytes larger than 127 with @samp{M-} (which stands a letter, and precede bytes larger than 127 with @samp{M-} (which stands
for "meta"). for "meta").
@item --format=[@var{fmt1}][,@var{fmt2}]
Force the given compression formats. Any of @var{fmt1} or @var{fmt2} may
be omitted and the corresponding format will be automatically detected.
Valid values for @var{fmt} are @samp{bz2}, @samp{gz}, @samp{lz} and
@samp{xz}. If at least one format is specified with this option, the
exact file names of both @var{file1} and @var{file2} must be given.
Other names won't be tried.
@item -i @var{size} @item -i @var{size}
@itemx --ignore-initial=@var{size} @itemx --ignore-initial=@var{size}
Ignore any differences in the first @var{size} bytes of the input files. Ignore any differences in the first @var{size} bytes of the input files.
@ -265,16 +293,22 @@ zdiff [@var{options}] @var{file1} [@var{file2}]
@end example @end example
@noindent @noindent
Compares @var{file1} to @var{file2}. If @var{file2} is omitted zdiff This compares @var{file1} to @var{file2}. If @var{file2} is omitted
tries the following:@* zdiff tries the following:
@enumerate
@item
If @var{file1} is compressed, compares @var{file1} to the file with the If @var{file1} is compressed, compares @var{file1} to the file with the
corresponding decompressed file name (removes the extension from corresponding decompressed file name (removes the extension from
@var{file1}).@* @var{file1}).
@item
If @var{file1} is not compressed, compares @var{file1} to the If @var{file1} is not compressed, compares @var{file1} to the
uncompressed contents of @var{file1}.[bz2|gz|lz|xz] (the first one that uncompressed contents of @var{file1}.[bz2|gz|lz|xz] (the first one that
is found).@* is found).
@item
If no suitable file is found, compares @var{file1} to data read from If no suitable file is found, compares @var{file1} to data read from
standard input. standard input.
@end enumerate
@noindent @noindent
An exit status of 0 means no differences were found, 1 means some An exit status of 0 means no differences were found, 1 means some
@ -318,6 +352,14 @@ Try hard to find a smaller set of changes.
@itemx --ignore-tab-expansion @itemx --ignore-tab-expansion
Ignore changes due to tab expansion. Ignore changes due to tab expansion.
@item --format=[@var{fmt1}][,@var{fmt2}]
Force the given compression formats. Any of @var{fmt1} or @var{fmt2} may
be omitted and the corresponding format will be automatically detected.
Valid values for @var{fmt} are @samp{bz2}, @samp{gz}, @samp{lz} and
@samp{xz}. If at least one format is specified with this option, the
exact file names of both @var{file1} and @var{file2} must be given.
Other names won't be tried.
@item -i @item -i
@itemx --ignore-case @itemx --ignore-case
Ignore case differences in file contents. Ignore case differences in file contents.
@ -365,10 +407,12 @@ on any combination of compressed and non-compressed files. If any given
file is compressed, its uncompressed content is used. If a given file file is compressed, its uncompressed content is used. If a given file
does not exist, and its name does not end with one of the known does not exist, and its name does not end with one of the known
extensions, zgrep tries the compressed file names corresponding to the extensions, zgrep tries the compressed file names corresponding to the
supported compressors. If no files are specified, data is read from supported formats.
standard input, decompressed if needed, and fed to grep. Data read from
standard input must be of the same type; all uncompressed or all If no files are specified, data is read from standard input,
compressed with the same compressor. decompressed if needed, and fed to grep. Data read from standard input
must be of the same type; all uncompressed or all in the same
compression format.
The format for running zgrep is: The format for running zgrep is:
@ -430,9 +474,15 @@ Obtain patterns from @var{file}, one per line.
@itemx --fixed-strings @itemx --fixed-strings
Treat @var{pattern} as a set of newline-separated strings. Treat @var{pattern} as a set of newline-separated strings.
@item --format=@var{fmt}
Force the given compression format. Valid values for @var{fmt} are
@samp{bz2}, @samp{gz}, @samp{lz} and @samp{xz}. If this option is used,
the exact file name must be given. Other names won't be tried.
@item -h @item -h
@itemx --no-filename @itemx --no-filename
Suppress the prefixing filename on output. Suppress the prefixing of filenames on output when multiple files are
searched.
@item -H @item -H
@itemx --with-filename @itemx --with-filename
@ -502,7 +552,10 @@ Match only whole lines.
Ztest verifies the integrity of the specified compressed files. Ztest verifies the integrity of the specified compressed files.
Non-compressed files are ignored. If no files are specified, the Non-compressed files are ignored. If no files are specified, the
integrity of compressed data read from standard input is verified. Data integrity of compressed data read from standard input is verified. Data
read from standard input must be all compressed with the same compressor. read from standard input must be all in the same compression format.
Note that some xz files lack integrity information, and therefore can't
be verified as reliably as the other formats can.
The format for running ztest is: The format for running ztest is:
@ -511,9 +564,9 @@ ztest [@var{options}] [@var{files}]
@end example @end example
@noindent @noindent
The exit status is 0 if all files verify OK, 1 for environmental The exit status is 0 if all compressed files verify OK, 1 if
problems (file not found, invalid flags, I/O errors, etc), 2 to indicate environmental problems (file not found, invalid flags, I/O errors, etc),
a corrupt or invalid input file. 2 if any compressed file is corrupt or invalid.
Ztest supports the following options: Ztest supports the following options:
@ -526,6 +579,11 @@ Print an informative help message describing the options and exit.
@itemx --version @itemx --version
Print the version number of ztest on the standard output and exit. Print the version number of ztest on the standard output and exit.
@item --format=@var{fmt}
Force the given compression format. Valid values for @var{fmt} are
@samp{bz2}, @samp{gz}, @samp{lz} and @samp{xz}. If this option is used,
all files not in the given format will fail.
@item -q @item -q
@itemx --quiet @itemx --quiet
Quiet operation. Suppress all messages. Quiet operation. Suppress all messages.

160
main.cc
View file

@ -1,5 +1,5 @@
/* Zutils - Utilities dealing with compressed files /* Zutils - Utilities dealing with compressed files
Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. Copyright (C) 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -54,27 +54,27 @@ const int o_binary = 0;
enum Mode { m_none, m_zcat, m_zgrep, m_ztest }; enum Mode { m_none, m_zcat, m_zgrep, m_ztest };
void show_help() throw() void show_help()
{ {
std::printf( "Zutils is a collection of utilities able to deal with any combination of\n" ); std::printf( "Zutils is a collection of utilities able to deal with any combination of\n"
std::printf( "compressed and non-compressed files transparently. If any given file,\n" ); "compressed and non-compressed files transparently. If any given file,\n"
std::printf( "including standard input, is compressed, its uncompressed content is used.\n" ); "including standard input, is compressed, its uncompressed content is used.\n"
std::printf( "The supported compressors are bzip2, gzip, lzip and xz.\n" ); "\nThe supported formats are bzip2, gzip, lzip and xz.\n"
std::printf( "\nUsage: %s <operation> [options] [files]\n", invocation_name ); "\nUsage: %s <operation> [options] [files]\n", invocation_name );
std::printf( "\nTry `%s <operation> --help' for more specific help.\n", invocation_name ); std::printf( "\nTry '%s <operation> --help' for more specific help.\n", invocation_name );
std::printf( "\nOperations:\n" ); std::printf( "\nOperations:\n"
std::printf( " -h, --help display this help and exit\n" ); " -h, --help display this help and exit\n"
std::printf( " -V, --version output version information and exit\n" ); " -V, --version output version information and exit\n"
std::printf( " --zcat zcat operation\n" ); " --zcat zcat operation\n"
std::printf( " --zgrep zgrep operation\n" ); " --zgrep zgrep operation\n"
std::printf( " --ztest ztest operation\n" ); " --ztest ztest operation\n" );
show_help_addr(); show_help_addr();
} }
int simple_extension_index( const std::string & name ) throw() int simple_extension_index( const std::string & name )
{ {
for( int i = 0; simple_extensions[i]; ++i ) for( int i = 0; i < num_formats; ++i )
{ {
const std::string ext( simple_extensions[i] ); const std::string ext( simple_extensions[i] );
if( name.size() > ext.size() && if( name.size() > ext.size() &&
@ -85,17 +85,19 @@ int simple_extension_index( const std::string & name ) throw()
} }
int open_instream( std::string & input_filename, const Mode program_mode ) throw() int open_instream( std::string & input_filename, const Mode program_mode,
const bool search )
{ {
int infd = open( input_filename.c_str(), O_RDONLY | o_binary ); int infd = open( input_filename.c_str(), O_RDONLY | o_binary );
if( infd < 0 ) if( infd < 0 )
{ {
if( ( program_mode == m_zcat || program_mode == m_zgrep ) if( search && ( program_mode == m_zcat || program_mode == m_zgrep ) &&
&& simple_extension_index( input_filename ) < 0 ) simple_extension_index( input_filename ) < 0 )
{ {
for( int i = 0; simple_extensions[i]; ++i ) for( int i = 0; i < num_formats; ++i )
{ {
const std::string name( input_filename + simple_extensions[i] ); const std::string name( input_filename +
simple_extensions[format_order[i]] );
infd = open( name.c_str(), O_RDONLY | o_binary ); infd = open( name.c_str(), O_RDONLY | o_binary );
if( infd >= 0 ) { input_filename = name; break; } if( infd >= 0 ) { input_filename = name; break; }
} }
@ -115,9 +117,11 @@ int open_instream( std::string & input_filename, const Mode program_mode ) throw
int main( const int argc, const char * const argv[] ) int main( const int argc, const char * const argv[] )
{ {
enum { help_opt = 256, verbose_opt, zcat_opt, zgrep_opt, ztest_opt }; enum { format_opt = 256, help_opt, verbose_opt,
zcat_opt, zgrep_opt, ztest_opt };
const Arg_parser::Option * options = 0; const Arg_parser::Option * options = 0;
int infd = -1; int infd = -1;
int format_type = -1;
Mode program_mode = m_none; Mode program_mode = m_none;
bool recursive = false; bool recursive = false;
std::string input_filename; std::string input_filename;
@ -147,52 +151,55 @@ int main( const int argc, const char * const argv[] )
{ 'T', "show-tabs", Arg_parser::no }, // cat { 'T', "show-tabs", Arg_parser::no }, // cat
{ 'v', "show-nonprinting", Arg_parser::no }, // cat { 'v', "show-nonprinting", Arg_parser::no }, // cat
{ 'V', "version", Arg_parser::no }, { 'V', "version", Arg_parser::no },
{ format_opt, "format", Arg_parser::yes },
{ verbose_opt, "verbose", Arg_parser::no }, { verbose_opt, "verbose", Arg_parser::no },
{ zcat_opt, "zcat", Arg_parser::no }, { zcat_opt, "zcat", Arg_parser::no },
{ 0 , 0, Arg_parser::no } }; { 0 , 0, Arg_parser::no } };
const Arg_parser::Option m_zgrep_options[] = const Arg_parser::Option m_zgrep_options[] =
{ {
{ 'a', "text", Arg_parser::no }, // grep GNU { 'a', "text", Arg_parser::no }, // grep GNU
{ 'A', "after-context", Arg_parser::yes }, // grep GNU { 'A', "after-context", Arg_parser::yes }, // grep GNU
{ 'b', "byte-offset", Arg_parser::no }, // grep GNU { 'b', "byte-offset", Arg_parser::no }, // grep GNU
{ 'B', "before-context", Arg_parser::yes }, // grep GNU { 'B', "before-context", Arg_parser::yes }, // grep GNU
{ 'c', "count", Arg_parser::no }, // grep { 'c', "count", Arg_parser::no }, // grep
{ 'C', "context", Arg_parser::yes }, // grep GNU { 'C', "context", Arg_parser::yes }, // grep GNU
{ 'e', "regexp", Arg_parser::yes }, // grep { 'e', "regexp", Arg_parser::yes }, // grep
{ 'E', "extended-regexp", Arg_parser::no }, // grep { 'E', "extended-regexp", Arg_parser::no }, // grep
{ 'f', "file ", Arg_parser::yes }, // grep { 'f', "file ", Arg_parser::yes }, // grep
{ 'F', "fixed-strings", Arg_parser::no }, // grep { 'F', "fixed-strings", Arg_parser::no }, // grep
{ 'h', "no-filename", Arg_parser::no }, // grep GNU { 'h', "no-filename", Arg_parser::no }, // grep GNU
{ 'H', "with-filename", Arg_parser::no }, // grep GNU { 'H', "with-filename", Arg_parser::no }, // grep GNU
{ 'i', "ignore-case", Arg_parser::no }, // grep { 'i', "ignore-case", Arg_parser::no }, // grep
{ 'I', 0, Arg_parser::no }, // grep GNU { 'I', 0, Arg_parser::no }, // grep GNU
{ 'l', "files-with-matches", Arg_parser::no }, // grep { 'l', "files-with-matches", Arg_parser::no }, // grep
{ 'L', "files-without-match", Arg_parser::no }, // grep GNU { 'L', "files-without-match", Arg_parser::no }, // grep GNU
{ 'm', "max-count", Arg_parser::yes }, // grep GNU { 'm', "max-count", Arg_parser::yes }, // grep GNU
{ 'n', "line-number", Arg_parser::no }, // grep { 'n', "line-number", Arg_parser::no }, // grep
{ 'o', "only-matching", Arg_parser::no }, // grep { 'o', "only-matching", Arg_parser::no }, // grep
{ 'q', "quiet", Arg_parser::no }, { 'q', "quiet", Arg_parser::no },
{ 'r', "recursive", Arg_parser::no }, { 'r', "recursive", Arg_parser::no },
{ 's', "no-messages", Arg_parser::no }, // grep { 's', "no-messages", Arg_parser::no }, // grep
{ 'v', "invert-match", Arg_parser::no }, // grep { 'v', "invert-match", Arg_parser::no }, // grep
{ 'V', "version", Arg_parser::no }, { 'V', "version", Arg_parser::no },
{ 'w', "word-regexp", Arg_parser::no }, // grep GNU { 'w', "word-regexp", Arg_parser::no }, // grep GNU
{ 'x', "line-regexp", Arg_parser::no }, // grep { 'x', "line-regexp", Arg_parser::no }, // grep
{ help_opt, "help", Arg_parser::no }, { format_opt, "format", Arg_parser::yes },
{ verbose_opt, "verbose", Arg_parser::no }, { help_opt, "help", Arg_parser::no },
{ zgrep_opt, "zgrep", Arg_parser::no }, { verbose_opt, "verbose", Arg_parser::no },
{ 0 , 0, Arg_parser::no } }; { zgrep_opt, "zgrep", Arg_parser::no },
{ 0 , 0, Arg_parser::no } };
const Arg_parser::Option m_ztest_options[] = const Arg_parser::Option m_ztest_options[] =
{ {
{ 'h', "help", Arg_parser::no }, { 'h', "help", Arg_parser::no },
{ 'q', "quiet", Arg_parser::no }, { 'q', "quiet", Arg_parser::no },
{ 'r', "recursive", Arg_parser::no }, { 'r', "recursive", Arg_parser::no },
{ 'v', "verbose", Arg_parser::no }, { 'v', "verbose", Arg_parser::no },
{ 'V', "version", Arg_parser::no }, { 'V', "version", Arg_parser::no },
{ ztest_opt, "ztest", Arg_parser::no }, { format_opt, "format", Arg_parser::yes },
{ 0 , 0, Arg_parser::no } }; { ztest_opt, "ztest", Arg_parser::no },
{ 0 , 0, Arg_parser::no } };
{ // parse operation { // parse operation
const Arg_parser::Option operations[] = const Arg_parser::Option operations[] =
@ -226,8 +233,8 @@ int main( const int argc, const char * const argv[] )
} }
#if defined(__MSVCRT__) || defined(__OS2__) #if defined(__MSVCRT__) || defined(__OS2__)
_setmode( STDIN_FILENO, O_BINARY ); setmode( STDIN_FILENO, O_BINARY );
_setmode( STDOUT_FILENO, O_BINARY ); setmode( STDOUT_FILENO, O_BINARY );
#endif #endif
if( program_mode == m_none ) if( program_mode == m_none )
@ -240,11 +247,11 @@ int main( const int argc, const char * const argv[] )
const Arg_parser parser( argc, argv, options ); const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option if( parser.error().size() ) // bad option
{ show_error( parser.error().c_str(), 0, true ); { show_error( parser.error().c_str(), 0, true );
return ( program_mode == m_zcat ) ? 1 : 2; } return ( program_mode == m_zcat || program_mode == m_ztest ) ? 1 : 2; }
int argind = 0; int argind = 0;
int grep_show_name = -1;
bool grep_list = false; bool grep_list = false;
bool grep_show_name = true;
bool grep_pattern_found = false; bool grep_pattern_found = false;
for( ; argind < parser.arguments(); ++argind ) for( ; argind < parser.arguments(); ++argind )
{ {
@ -256,9 +263,13 @@ int main( const int argc, const char * const argv[] )
{ grep_args.push_back( arg ); grep_pattern_found = true; continue; } { grep_args.push_back( arg ); grep_pattern_found = true; continue; }
else break; // no more options else break; // no more options
} }
if( code == format_opt )
{
format_type = get_format_type( arg ); continue;
}
switch( program_mode ) switch( program_mode )
{ {
case m_none: internal_error( "invalid operation" ); case m_none: internal_error( "invalid operation" ); break;
case m_zcat: case m_zcat:
switch( code ) switch( code )
{ {
@ -339,17 +350,16 @@ int main( const int argc, const char * const argv[] )
} // end process options } // end process options
if( program_mode == m_zgrep && !grep_pattern_found ) if( program_mode == m_zgrep && !grep_pattern_found )
{ show_error( "Pattern not found.", 0, true ); return 2; } { show_error( "Pattern not found." ); return 2; }
bool filenames_given = false;
for( ; argind < parser.arguments(); ++argind ) for( ; argind < parser.arguments(); ++argind )
{
if( parser.argument( argind ) != "-" ) filenames_given = true;
filenames.push_back( parser.argument( argind ) ); filenames.push_back( parser.argument( argind ) );
}
if( filenames.empty() ) filenames.push_back("-"); if( filenames.empty() ) filenames.push_back("-");
if( grep_show_name < 0 )
grep_show_name = ( filenames.size() != 1 || recursive );
int retval = ( ( program_mode == m_zgrep ) ? 1 : 0 ); int retval = ( ( program_mode == m_zgrep ) ? 1 : 0 );
while( !filenames.empty() ) while( !filenames.empty() )
{ {
@ -386,7 +396,7 @@ int main( const int argc, const char * const argv[] )
continue; continue;
} }
} }
infd = open_instream( input_filename, program_mode ); infd = open_instream( input_filename, program_mode, format_type < 0 );
if( infd < 0 ) { if( retval < 1 ) retval = 1; continue; } if( infd < 0 ) { if( retval < 1 ) retval = 1; continue; }
} }
@ -396,18 +406,18 @@ int main( const int argc, const char * const argv[] )
case m_none: case m_none:
break; break;
case m_zcat: case m_zcat:
tmp = cat( infd, input_filename, cat_options ); tmp = cat( infd, format_type, input_filename, cat_options );
break; break;
case m_zgrep: case m_zgrep:
if( infd == STDIN_FILENO ) if( infd == STDIN_FILENO )
tmp = zgrep_stdin( infd, grep_args ); tmp = zgrep_stdin( infd, format_type, grep_args );
else tmp = zgrep_file( infd, input_filename, grep_args, else tmp = zgrep_file( infd, format_type, input_filename, grep_args,
grep_list, grep_show_name ); grep_list, grep_show_name );
break; break;
case m_ztest: case m_ztest:
if( infd == STDIN_FILENO ) if( infd == STDIN_FILENO )
tmp = ztest_stdin( infd, ztest_args ); tmp = ztest_stdin( infd, format_type, ztest_args );
else tmp = ztest_file( infd, input_filename, ztest_args ); else tmp = ztest_file( infd, format_type, input_filename, ztest_args );
break; break;
} }
if( program_mode == m_zgrep ) if( program_mode == m_zgrep )

View file

@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# check script for Zutils - Utilities dealing with compressed files # check script for Zutils - Utilities dealing with compressed files
# Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. # Copyright (C) 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
# #
# This script is free software: you have unlimited permission # This script is free software: you have unlimited permission
# to copy, distribute and modify it. # to copy, distribute and modify it.
@ -18,8 +18,8 @@ ZFGREP="${objdir}"/zfgrep
ZTEST="${objdir}"/ztest ZTEST="${objdir}"/ztest
compressors="bzip2 gzip lzip" compressors="bzip2 gzip lzip"
extensions="bz2 gz lz" extensions="bz2 gz lz"
framework_failure() { echo "failure in testing framework" ; exit 1 ; }
compressor_needed() { echo "${compressors} are needed to run tests" ; exit 1 ; } compressor_needed() { echo "${compressors} are needed to run tests" ; exit 1 ; }
framework_failure() { echo "failure in testing framework" ; exit 1 ; }
if [ ! -x "${ZCAT}" ] ; then if [ ! -x "${ZCAT}" ] ; then
echo "${ZCAT}: cannot execute" echo "${ZCAT}: cannot execute"
@ -35,7 +35,6 @@ for i in ${compressors}; do
$i in || compressor_needed $i in || compressor_needed
done done
printf "testing zutils-%s..." "$2"
cat "${testdir}"/test.txt > in || framework_failure cat "${testdir}"/test.txt > in || framework_failure
cat "${testdir}"/test.txt.tar > in.tar || framework_failure cat "${testdir}"/test.txt.tar > in.tar || framework_failure
printf "01234567890" > pin.tar || framework_failure printf "01234567890" > pin.tar || framework_failure
@ -46,12 +45,20 @@ cat in.lz > lz_only.lz || framework_failure
cat in in in in in in > in6 || framework_failure cat in in in in in in > in6 || framework_failure
fail=0 fail=0
printf "testing zutils-%s..." "$2"
printf "\ntesting zcat-%s..." "$2" printf "\ntesting zcat-%s..." "$2"
for i in ${extensions}; do for i in ${extensions}; do
"${ZCAT}" in.$i > copy || fail=1 "${ZCAT}" in.$i > copy || fail=1
cmp in copy || fail=1 cmp in copy || fail=1
printf . printf .
"${ZCAT}" --format=$i in.$i > copy || fail=1
cmp in copy || fail=1
printf .
"${ZCAT}" in.$i | dd bs=1000 count=1 > copy 2> /dev/null || fail=1
dd if=in bs=1000 count=1 2> /dev/null | cmp - copy || fail=1
printf .
done done
"${ZCAT}" < in > copy || fail=1 "${ZCAT}" < in > copy || fail=1
@ -75,20 +82,51 @@ printf .
"${ZCAT}" in in.gz in.bz2 in.lz -- -in- -in-.lz > copy6 || fail=1 "${ZCAT}" in in.gz in.bz2 in.lz -- -in- -in-.lz > copy6 || fail=1
cmp in6 copy6 || fail=1 cmp in6 copy6 || fail=1
printf . printf .
"${ZCAT}" --format=gz in.bz2 2> /dev/null
if [ $? != 1 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZCAT}" --format=bz2 in.lz 2> /dev/null
if [ $? != 1 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZCAT}" --format=lz in.gz 2> /dev/null
if [ $? != 1 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZCAT}" --bad-option 2> /dev/null
if [ $? != 1 ] ; then fail=1 ; printf - ; else printf . ; fi
printf "\ntesting zcmp-%s..." "$2" printf "\ntesting zcmp-%s..." "$2"
for i in ${extensions}; do for i in ${extensions}; do
"${ZCMP}" in.$i || fail=1 "${ZCMP}" in.$i || fail=1
printf . printf .
"${ZCMP}" in in.$i || fail=1 "${ZCMP}" in in.$i || fail=1
printf . printf .
"${ZCMP}" -i 100 -n 500 in6 in.$i || fail=1
printf .
"${ZCMP}" in in.$i --format=,$i || fail=1
printf .
"${ZCMP}" in.$i in || fail=1 "${ZCMP}" in.$i in || fail=1
printf . printf .
"${ZCMP}" -i 1000:1000 -n 50 in.$i in6 || fail=1
printf .
"${ZCMP}" in.$i in --format=$i || fail=1
printf .
done done
"${ZCMP}" in in6 2> /dev/null
if [ $? != 1 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZCMP}" -n 0 in in6 || fail=1
"${ZCMP}" -n 100 in in6 || fail=1
"${ZCMP}" -n 1000 in in6 || fail=1
"${ZCMP}" -n 10000 in in6 || fail=1
printf .
"${ZCMP}" in.tar pin.tar > /dev/null "${ZCMP}" in.tar pin.tar > /dev/null
if [ $? != 1 ] ; then fail=1 ; printf - ; else printf . ; fi if [ $? != 1 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZCMP}" -i 0,11 in.tar pin.tar 2> /dev/null
if [ $? != 1 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZCMP}" -i 0,11 -n 0 in.tar pin.tar || fail=1
"${ZCMP}" -i 0,11 -n 100 in.tar pin.tar || fail=1
"${ZCMP}" -i 0,11 -n 1000 in.tar pin.tar || fail=1
"${ZCMP}" -i 0,11 -n 10000 in.tar pin.tar || fail=1
printf .
"${ZCMP}" - || fail=1 "${ZCMP}" - || fail=1
printf . printf .
"${ZCMP}" in in || fail=1 "${ZCMP}" in in || fail=1
@ -123,20 +161,35 @@ printf .
printf . printf .
"${ZCMP}" - in < in.lz || fail=1 "${ZCMP}" - in < in.lz || fail=1
printf . printf .
"${ZCMP}" -q --format=lz in.lz
if [ $? != 2 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZCMP}" --format=lz in.gz in.lz 2> /dev/null
if [ $? != 2 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZCMP}" -n -1 in in 2> /dev/null
if [ $? != 2 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZCMP}" --bad-option in in 2> /dev/null
if [ $? != 2 ] ; then fail=1 ; printf - ; else printf . ; fi
printf "\ntesting zdiff-%s..." "$2" printf "\ntesting zdiff-%s..." "$2"
for i in ${extensions}; do for i in ${extensions}; do
"${ZDIFF}" in.$i || fail=1 "${ZDIFF}" in.$i || fail=1
printf . printf .
"${ZDIFF}" in in.$i || fail=1 "${ZDIFF}" in in.$i || fail=1
printf . printf .
"${ZDIFF}" --format=,$i in in.$i || fail=1
printf .
"${ZDIFF}" in.$i in || fail=1 "${ZDIFF}" in.$i in || fail=1
printf . printf .
"${ZDIFF}" --format=$i, in.$i in || fail=1
printf .
done done
#"${ZDIFF}" in.tar pin.tar > /dev/null "${ZDIFF}" in in6 > /dev/null
#if [ $? != 1 ] ; then fail=1 ; printf - ; else printf . ; fi if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZDIFF}" in.tar pin.tar > /dev/null
if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZDIFF}" - || fail=1 "${ZDIFF}" - || fail=1
printf . printf .
"${ZDIFF}" in in || fail=1 "${ZDIFF}" in in || fail=1
@ -171,13 +224,27 @@ printf .
printf . printf .
"${ZDIFF}" - in < in.lz || fail=1 "${ZDIFF}" - in < in.lz || fail=1
printf . printf .
"${ZDIFF}" -q --format=bz2 in.bz2 2> /dev/null
if [ $? != 2 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZDIFF}" -q --format=,lz in.lz in.bz2 > /dev/null 2> /dev/null
if [ $? != 2 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZDIFF}" --bad-option 2> /dev/null
if [ $? != 2 ] ; then fail=1 ; printf - ; else printf . ; fi
printf "\ntesting zgrep-%s..." "$2" printf "\ntesting zgrep-%s..." "$2"
for i in ${extensions}; do for i in ${extensions}; do
"${ZGREP}" "GNU" in.$i > /dev/null || fail=1 "${ZGREP}" "GNU" in.$i > /dev/null || fail=1
"${ZGREP}" "nx_pattern" in.$i > /dev/null && fail=1
printf . printf .
"${ZGREP}" --format=$i "GNU" in.$i > /dev/null || fail=1
printf .
"${ZGREP}" -v "nx_pattern" in.$i > /dev/null || fail=1
printf .
"${ZGREP}" "nx_pattern" in.$i > /dev/null
if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZGREP}" --format=$i "GNU" in 2> /dev/null
if [ $? != 2 ] ; then fail=1 ; printf - ; else printf . ; fi
done done
"${ZGREP}" "GNU" < pin.tar > /dev/null || fail=1 "${ZGREP}" "GNU" < pin.tar > /dev/null || fail=1
@ -186,8 +253,8 @@ printf .
printf . printf .
"${ZGREP}" -r "GNU" . > /dev/null || fail=1 "${ZGREP}" -r "GNU" . > /dev/null || fail=1
printf . printf .
"${ZGREP}" "nx_pattern" -r . in > /dev/null && fail=1 "${ZGREP}" "nx_pattern" -r . in > /dev/null
printf . if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZGREP}" "GNU" < in > /dev/null || fail=1 "${ZGREP}" "GNU" < in > /dev/null || fail=1
printf . printf .
"${ZGREP}" "GNU" < in.gz > /dev/null || fail=1 "${ZGREP}" "GNU" < in.gz > /dev/null || fail=1
@ -204,6 +271,11 @@ printf .
printf . printf .
"${ZGREP}" "GNU" in in.gz in.bz2 in.lz -- -in- > /dev/null || fail=1 "${ZGREP}" "GNU" in in.gz in.bz2 in.lz -- -in- > /dev/null || fail=1
printf . printf .
"${ZGREP}" -l "GNU" in in.gz in.bz2 in.lz -- -in- > /dev/null || fail=1
printf .
"${ZGREP}" --bad-option 2> /dev/null
if [ $? != 2 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZEGREP}" "GNU" in > /dev/null || fail=1 "${ZEGREP}" "GNU" in > /dev/null || fail=1
printf . printf .
"${ZFGREP}" "GNU" in > /dev/null || fail=1 "${ZFGREP}" "GNU" in > /dev/null || fail=1
@ -211,6 +283,16 @@ printf .
printf "\ntesting ztest-%s..." "$2" printf "\ntesting ztest-%s..." "$2"
for i in ${extensions}; do
"${ZTEST}" --format=$i < in.$i || fail=1
printf .
"${ZTEST}" --format=$i < in 2> /dev/null
if [ $? != 2 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZTEST}" --format=$i in 2> /dev/null
if [ $? != 2 ] ; then fail=1 ; printf - ; else printf . ; fi
done
"${ZTEST}" in in.gz in.bz2 in.lz -- -in- || fail=1 "${ZTEST}" in in.gz in.bz2 in.lz -- -in- || fail=1
printf . printf .
"${ZTEST}" < in.gz || fail=1 "${ZTEST}" < in.gz || fail=1
@ -221,10 +303,14 @@ printf .
printf . printf .
"${ZTEST}" -r . || fail=1 "${ZTEST}" -r . || fail=1
printf . printf .
"${ZTEST}" < in 2>/dev/null "${ZTEST}" < in 2> /dev/null
if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi if [ $? != 2 ] ; then fail=1 ; printf - ; else printf . ; fi
printf "LZIPvs0000000000000000000000" | "${ZTEST}" 2>/dev/null dd if=in.lz bs=1000 count=1 2> /dev/null | "${ZTEST}" -q
if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi if [ $? != 2 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZTEST}" --format=lz in.bz2 2> /dev/null
if [ $? != 2 ] ; then fail=1 ; printf - ; else printf . ; fi
"${ZTEST}" --bad-option 2> /dev/null
if [ $? != 1 ] ; then fail=1 ; printf - ; else printf . ; fi
echo echo
if [ ${fail} = 0 ] ; then if [ ${fail} = 0 ] ; then

71
zcat.cc
View file

@ -1,5 +1,5 @@
/* Zcat - decompress and concatenate files to standard output /* Zcat - decompress and concatenate files to standard output
Copyright (C) 2010, 2011 Antonio Diaz Diaz. Copyright (C) 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -23,7 +23,7 @@ struct Cat_options
bool show_tabs; bool show_tabs;
bool squeeze_blank; bool squeeze_blank;
Cat_options() throw() Cat_options()
: number_lines( 0 ), show_ends( false ), show_nonprinting( false ), : number_lines( 0 ), show_ends( false ), show_nonprinting( false ),
show_tabs( false ), squeeze_blank( false ) {} show_tabs( false ), squeeze_blank( false ) {}
}; };
@ -32,20 +32,20 @@ struct Cat_options
class Line_number // unlimited size line counter class Line_number // unlimited size line counter
{ {
std::string str; std::string str;
int first_digit_pos; unsigned first_digit_pos;
public: public:
Line_number() : str( " 0\t" ), first_digit_pos( 5 ) {} Line_number() : str( " 0\t" ), first_digit_pos( 5 ) {}
void next() void next()
{ {
for( int i = str.size() - 2; i >= first_digit_pos; --i ) for( unsigned i = str.size() - 1; i > first_digit_pos; )
{ {
if( str[i] < '9' ) { ++str[i]; return; } if( str[--i] < '9' ) { ++str[i]; return; }
str[i] = '0'; str[i] = '0';
} }
if( first_digit_pos > 0 ) str[--first_digit_pos] = '1'; if( first_digit_pos > 0 ) str[--first_digit_pos] = '1';
else str.insert( 0U, 1, '1' ); else str.insert( first_digit_pos, 1, '1' );
} }
int sprint( uint8_t * const buf ) int sprint( uint8_t * const buf )
@ -58,34 +58,35 @@ public:
Line_number line_number; Line_number line_number;
void show_zcat_help() throw() void show_zcat_help()
{ {
std::printf( "Zcat copies each given file (\"-\" means standard input), to standard\n" ); std::printf( "Zcat copies each given file (\"-\" means standard input), to standard\n"
std::printf( "output. If any given file is compressed, its uncompressed content is\n" ); "output. If any given file is compressed, its uncompressed content is\n"
std::printf( "used. If a given file does not exist, and its name does not end with one\n" ); "used. If a given file does not exist, and its name does not end with one\n"
std::printf( "of the known extensions, zcat tries the compressed file names\n" ); "of the known extensions, zcat tries the compressed file names\n"
std::printf( "corresponding to the supported compressors. If no files are specified,\n" ); "corresponding to the supported formats. If no files are specified,\n"
std::printf( "data is read from standard input, decompressed if needed, and sent to\n" ); "data is read from standard input, decompressed if needed, and sent to\n"
std::printf( "standard output. Data read from standard input must be of the same type;\n" ); "standard output. Data read from standard input must be of the same type;\n"
std::printf( "all uncompressed or all compressed with the same compressor.\n" ); "all uncompressed or all in the same compression format.\n"
std::printf( "The supported compressors are bzip2, gzip, lzip and xz.\n" ); "\nThe supported formats are bzip2, gzip, lzip and xz.\n"
std::printf( "\nUsage: zcat [options] [files]\n" ); "\nUsage: zcat [options] [files]\n"
std::printf( "\nExit status is 0 if no errors occurred, 1 otherwise.\n" ); "\nExit status is 0 if no errors occurred, 1 otherwise.\n"
std::printf( "\nOptions:\n" ); "\nOptions:\n"
std::printf( " -h, --help display this help and exit\n" ); " -h, --help display this help and exit\n"
std::printf( " -V, --version output version information and exit\n" ); " -V, --version output version information and exit\n"
std::printf( " -A, --show-all equivalent to `-vET'\n" ); " -A, --show-all equivalent to '-vET'\n"
std::printf( " -b, --number-nonblank number nonblank output lines\n" ); " -b, --number-nonblank number nonblank output lines\n"
std::printf( " -e equivalent to `-vE'\n" ); " -e equivalent to '-vE'\n"
std::printf( " -E, --show-ends display `$' at end of each line\n" ); " -E, --show-ends display '$' at end of each line\n"
std::printf( " -n, --number number all output lines\n" ); " --format=<fmt> force given format (bz2, gz, lz, xz)\n"
std::printf( " -q, --quiet suppress all messages\n" ); " -n, --number number all output lines\n"
std::printf( " -r, --recursive operate recursively on directories\n" ); " -q, --quiet suppress all messages\n"
std::printf( " -s, --squeeze-blank never more than one single blank line\n" ); " -r, --recursive operate recursively on directories\n"
std::printf( " -t equivalent to `-vT'\n" ); " -s, --squeeze-blank never more than one single blank line\n"
std::printf( " -T, --show-tabs display TAB characters as `^I'\n" ); " -t equivalent to '-vT'\n"
std::printf( " -v, --show-nonprinting use `^' and `M-' notation, except for LF and TAB\n" ); " -T, --show-tabs display TAB characters as '^I'\n"
std::printf( " --verbose verbose mode (show error messages)\n" ); " -v, --show-nonprinting use '^' and 'M-' notation, except for LF and TAB\n"
" --verbose verbose mode (show error messages)\n" );
show_help_addr(); show_help_addr();
} }
@ -186,7 +187,7 @@ int do_cat( const int infd, const int buffer_size,
} }
int cat( int infd, const std::string & input_filename, int cat( int infd, const int format_type, const std::string & input_filename,
const Cat_options & cat_options ) const Cat_options & cat_options )
{ {
enum { buffer_size = 4096 }; enum { buffer_size = 4096 };
@ -196,7 +197,7 @@ int cat( int infd, const std::string & input_filename,
uint8_t * const outbuf = new uint8_t[(4*buffer_size)+256]; uint8_t * const outbuf = new uint8_t[(4*buffer_size)+256];
pid_t pid = 0; pid_t pid = 0;
int retval = 0; int retval = 0;
if( !set_data_feeder( &infd, &pid ) ) retval = 1; if( !set_data_feeder( &infd, &pid, format_type ) ) retval = 1;
else else
retval = do_cat( infd, buffer_size, inbuf, outbuf, retval = do_cat( infd, buffer_size, inbuf, outbuf,
input_filename, cat_options ); input_filename, cat_options );

197
zcmp.cc
View file

@ -1,5 +1,5 @@
/* Zcmp - decompress and compare two files byte by byte /* Zcmp - decompress and compare two files byte by byte
Copyright (C) 2010, 2011 Antonio Diaz Diaz. Copyright (C) 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -17,6 +17,7 @@
#define _FILE_OFFSET_BITS 64 #define _FILE_OFFSET_BITS 64
#include <algorithm>
#include <cctype> #include <cctype>
#include <cerrno> #include <cerrno>
#include <climits> #include <climits>
@ -30,9 +31,6 @@
#include <stdint.h> #include <stdint.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#if defined(__MSVCRT__) || defined(__OS2__)
#include <io.h>
#endif
#include "arg_parser.h" #include "arg_parser.h"
#include "zutils.h" #include "zutils.h"
@ -44,73 +42,52 @@
#ifndef LLONG_MAX #ifndef LLONG_MAX
#define LLONG_MAX 0x7FFFFFFFFFFFFFFFLL #define LLONG_MAX 0x7FFFFFFFFFFFFFFFLL
#endif #endif
#ifndef LLONG_MIN
#define LLONG_MIN (-LLONG_MAX - 1LL)
#endif
#ifndef ULLONG_MAX
#define ULLONG_MAX 0xFFFFFFFFFFFFFFFFULL
#endif
namespace { namespace {
#ifdef O_BINARY #include "zcmpdiff.cc"
const int o_binary = O_BINARY;
#else
const int o_binary = 0;
#endif
struct { const char * from; const char * to; } const known_extensions[] = {
{ ".bz2", "" },
{ ".tbz", ".tar" },
{ ".tbz2", ".tar" },
{ ".gz", "" },
{ ".tgz", ".tar" },
{ ".lz", "" },
{ ".tlz", ".tar" },
{ ".xz", "" },
{ ".txz", ".tar" },
{ 0, 0 } };
void show_help() throw() void show_help()
{ {
std::printf( "Zcmp compares two files (\"-\" means standard input), and if they\n" ); std::printf( "Zcmp compares two files (\"-\" means standard input), and if they\n"
std::printf( "differ, tells the first byte and line number where they differ. Bytes\n" ); "differ, tells the first byte and line number where they differ. Bytes\n"
std::printf( "and lines are numbered starting with 1. If any given file is compressed,\n" ); "and lines are numbered starting with 1. If any given file is compressed,\n"
std::printf( "its uncompressed content is used. Compressed files are uncompressed on\n" ); "its uncompressed content is used. Compressed files are uncompressed on\n"
std::printf( "the fly; no temporary files are created.\n" ); "the fly; no temporary files are created.\n"
std::printf( "The supported compressors are bzip2, gzip, lzip and xz.\n" ); "\nThe supported formats are bzip2, gzip, lzip and xz.\n"
std::printf( "\nUsage: zcmp [options] file1 [file2]\n" ); "\nUsage: zcmp [options] file1 [file2]\n"
std::printf( "\nCompares <file1> to <file2>. If <file2> is omitted zcmp tries the\n" ); "\nCompares <file1> to <file2>. If <file2> is omitted zcmp tries the\n"
std::printf( "following:\n" ); "following:\n"
std::printf( "If <file1> is compressed, compares <file1> to the file with the\n" ); "If <file1> is compressed, compares <file1> to the file with the\n"
std::printf( "corresponding decompressed file name (removes the extension from\n" ); "corresponding decompressed file name (removes the extension from\n"
std::printf( "<file1>).\n" ); "<file1>).\n"
std::printf( "If <file1> is not compressed, compares <file1> to the uncompressed\n" ); "If <file1> is not compressed, compares <file1> to the uncompressed\n"
std::printf( "contents of <file1>.[bz2|gz|lz|xz] (the first one that is found).\n" ); "contents of <file1>.[bz2|gz|lz|xz] (the first one that is found).\n"
std::printf( "If no suitable file is found, compares <file1> to data read from\n" ); "If no suitable file is found, compares <file1> to data read from\n"
std::printf( "standard input.\n" ); "standard input.\n"
std::printf( "\nExit status is 0 if inputs are identical, 1 if different, 2 if trouble.\n" ); "\nExit status is 0 if inputs are identical, 1 if different, 2 if trouble.\n"
std::printf( "\nOptions:\n" ); "\nOptions:\n"
std::printf( " -h, --help display this help and exit\n" ); " -h, --help display this help and exit\n"
std::printf( " -V, --version output version information and exit\n" ); " -V, --version output version information and exit\n"
std::printf( " -b, --print-bytes print differing bytes\n" ); " -b, --print-bytes print differing bytes\n"
std::printf( " -i, --ignore-initial=<n>[,<n2>] ignore differences in the first <n> bytes\n" ); " --format=[<fmt1>][,<fmt2>] force given formats (bz2, gz, lz, xz)\n"
std::printf( " -l, --list list position, value of all differing bytes\n" ); " -i, --ignore-initial=<n>[,<n2>] ignore differences in the first <n> bytes\n"
std::printf( " -n, --bytes=<n> compare at most <n> bytes\n" ); " -l, --list list position, value of all differing bytes\n"
std::printf( " -q, --quiet suppress all messages\n" ); " -n, --bytes=<n> compare at most <n> bytes\n"
std::printf( " -s, --silent (same as --quiet)\n" ); " -q, --quiet suppress all messages\n"
std::printf( " -v, --verbose verbose mode (same as --list)\n" ); " -s, --silent (same as --quiet)\n"
std::printf( "Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n" ); " -v, --verbose verbose mode (same as --list)\n"
std::printf( "Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" ); "Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n"
"Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" );
show_help_addr(); show_help_addr();
} }
long long getnum( const char * const ptr, const char ** const tailp = 0, long long getnum( const char * const ptr, const char ** const tailp = 0,
const long long llimit = LLONG_MIN + 1, const long long llimit = 0,
const long long ulimit = LLONG_MAX ) throw() const long long ulimit = LLONG_MAX )
{ {
errno = 0; errno = 0;
char * tail; char * tail;
@ -120,6 +97,7 @@ long long getnum( const char * const ptr, const char ** const tailp = 0,
show_error( "Bad or missing numerical argument.", 0, true ); show_error( "Bad or missing numerical argument.", 0, true );
std::exit( 2 ); std::exit( 2 );
} }
if( result < 0 ) errno = ERANGE;
if( !errno && tail[0] && std::isalpha( tail[0] ) ) if( !errno && tail[0] && std::isalpha( tail[0] ) )
{ {
@ -148,7 +126,7 @@ long long getnum( const char * const ptr, const char ** const tailp = 0,
} }
for( int i = 0; i < exponent; ++i ) for( int i = 0; i < exponent; ++i )
{ {
if( LLONG_MAX / factor >= llabs( result ) ) result *= factor; if( ulimit / factor >= result ) result *= factor;
else { errno = ERANGE; break; } else { errno = ERANGE; break; }
} }
} }
@ -163,53 +141,12 @@ long long getnum( const char * const ptr, const char ** const tailp = 0,
} }
int open_instream( const std::string & input_filename ) throw()
{
int infd = open( input_filename.c_str(), O_RDONLY | o_binary );
if( infd < 0 )
show_error2( "Can't open input file", input_filename.c_str() );
return infd;
}
int open_other_instream( std::string & name ) throw()
{
for( int i = 0; known_extensions[i].from; ++i )
{ // search uncompressed version
const std::string from( known_extensions[i].from );
if( name.size() > from.size() &&
name.compare( name.size() - from.size(), from.size(), from ) == 0 )
{
name.resize( name.size() - from.size() );
name += known_extensions[i].to;
return open( name.c_str(), O_RDONLY | o_binary );
}
}
for( int i = 0; simple_extensions[i]; ++i )
{ // search compressed version
const std::string s( name + simple_extensions[i] );
const int infd = open( s.c_str(), O_RDONLY | o_binary );
if( infd >= 0 ) { name = s; return infd; }
}
return -1;
}
bool check_identical( const char * const name1, const char * const name2 ) throw()
{
if( !std::strcmp( name1, name2 ) ) return true;
struct stat stat1, stat2;
if( stat( name1, &stat1 ) || stat( name2, &stat2 ) ) return false;
return ( stat1.st_ino == stat2.st_ino && stat1.st_dev == stat2.st_dev );
}
void parse_ignore_initial( const char * const arg, long long ignore_initial[2] ) void parse_ignore_initial( const char * const arg, long long ignore_initial[2] )
{ {
const char * tail; const char * tail;
ignore_initial[0] = getnum( arg, &tail, 0 ); ignore_initial[0] = getnum( arg, &tail );
if( *tail == ',' || *tail == ':' ) if( *tail == ',' || *tail == ':' )
ignore_initial[1] = getnum( ++tail, 0, 0 ); ignore_initial[1] = getnum( ++tail );
else ignore_initial[1] = ignore_initial[0]; else ignore_initial[1] = ignore_initial[0];
} }
@ -253,10 +190,11 @@ void sprintc( char * const buf, unsigned char c )
int block_compare( const uint8_t * const buffer0, int block_compare( const uint8_t * const buffer0,
const uint8_t * const buffer1, const uint8_t * const buffer1,
long long * line_numberp ) unsigned long long * const line_numberp )
{ {
const uint8_t * p0 = buffer0; const uint8_t * p0 = buffer0;
const uint8_t * p1 = buffer1; const uint8_t * p1 = buffer1;
if( verbosity == 0 ) if( verbosity == 0 )
{ {
int nl_count = 0; int nl_count = 0;
@ -272,10 +210,11 @@ int block_compare( const uint8_t * const buffer0,
int cmp( const long long max_size, const int infd[2], int cmp( const long long max_size, const int infd[2],
const std::string filenames[2], const bool print_bytes ) const std::string filenames[2], const bool print_bytes )
{ {
enum { buffer_size = 4096 }; const int buffer_size = 4096;
long long byte_number = 1; unsigned long long byte_number = 1;
long long line_number = 1; unsigned long long line_number = 1;
long long rest = max_size; // remaining number of bytes to compare // remaining number of bytes to compare
long long rest = ( ( max_size >= 0 ) ? max_size : buffer_size );
// buffers with space for sentinels at the end // buffers with space for sentinels at the end
uint8_t * const buffer0 = new uint8_t[2*(buffer_size+1)]; uint8_t * const buffer0 = new uint8_t[2*(buffer_size+1)];
uint8_t * const buffer1 = buffer0 + buffer_size + 1; uint8_t * const buffer1 = buffer0 + buffer_size + 1;
@ -285,7 +224,8 @@ int cmp( const long long max_size, const int infd[2],
while( rest > 0 ) while( rest > 0 )
{ {
const int size = std::min( rest, (long long)buffer_size ); const int size = std::min( (long long)buffer_size, rest );
if( max_size >= 0 ) rest -= size;
int rd[2]; // number of bytes read from each file int rd[2]; // number of bytes read from each file
for( int i = 0; i < 2; ++i ) for( int i = 0; i < 2; ++i )
{ {
@ -296,7 +236,6 @@ int cmp( const long long max_size, const int infd[2],
return 2; return 2;
} }
} }
rest -= size;
buffer0[rd[0]] = ~buffer1[rd[0]]; // sentinels for the block compare buffer0[rd[0]] = ~buffer1[rd[0]]; // sentinels for the block compare
buffer1[rd[1]] = ~buffer0[rd[1]]; buffer1[rd[1]] = ~buffer0[rd[1]];
@ -311,7 +250,7 @@ int cmp( const long long max_size, const int infd[2],
if( verbosity == 0 ) // show first difference if( verbosity == 0 ) // show first difference
{ {
if( !print_bytes ) if( !print_bytes )
std::printf( "%s %s differ: byte %lld, line %lld\n", std::printf( "%s %s differ: byte %llu, line %llu\n",
filenames[0].c_str(), filenames[1].c_str(), filenames[0].c_str(), filenames[1].c_str(),
byte_number, line_number ); byte_number, line_number );
else else
@ -320,7 +259,7 @@ int cmp( const long long max_size, const int infd[2],
const unsigned char c1 = buffer1[first_diff]; const unsigned char c1 = buffer1[first_diff];
char buf0[5], buf1[5]; char buf0[5], buf1[5];
sprintc( buf0, c0 ); sprintc( buf1, c1 ); sprintc( buf0, c0 ); sprintc( buf1, c1 );
std::printf( "%s %s differ: byte %lld, line %lld is %3o %s %3o %s\n", std::printf( "%s %s differ: byte %llu, line %llu is %3o %s %3o %s\n",
filenames[0].c_str(), filenames[1].c_str(), filenames[0].c_str(), filenames[1].c_str(),
byte_number, line_number, c0, buf0, c1, buf1 ); byte_number, line_number, c0, buf0, c1, buf1 );
} }
@ -336,12 +275,12 @@ int cmp( const long long max_size, const int infd[2],
if( c0 != c1 ) if( c0 != c1 )
{ {
if( !print_bytes ) if( !print_bytes )
std::printf( "%lld %3o %3o\n", byte_number, c0, c1 ); std::printf( "%llu %3o %3o\n", byte_number, c0, c1 );
else else
{ {
char buf0[5], buf1[5]; char buf0[5], buf1[5];
sprintc( buf0, c0 ); sprintc( buf1, c1 ); sprintc( buf0, c0 ); sprintc( buf1, c1 );
std::printf( "%lld %3o %-4s %3o %s\n", std::printf( "%llu %3o %-4s %3o %s\n",
byte_number, c0, buf0, c1, buf1 ); byte_number, c0, buf0, c1, buf1 );
} }
} }
@ -368,9 +307,11 @@ int cmp( const long long max_size, const int infd[2],
int main( const int argc, const char * const argv[] ) int main( const int argc, const char * const argv[] )
{ {
enum { format_opt = 256 };
// number of initial bytes ignored for each file // number of initial bytes ignored for each file
long long ignore_initial[2] = { 0, 0 }; long long ignore_initial[2] = { 0, 0 };
long long max_size = LLONG_MAX; long long max_size = -1; // < 0 means unlimited size
int format_types[2] = { -1, -1 };
bool print_bytes = false; bool print_bytes = false;
invocation_name = argv[0]; invocation_name = argv[0];
util_name = "zcmp"; util_name = "zcmp";
@ -386,6 +327,7 @@ int main( const int argc, const char * const argv[] )
{ 's', "silent", Arg_parser::no }, { 's', "silent", Arg_parser::no },
{ 'v', "verbose", Arg_parser::no }, { 'v', "verbose", Arg_parser::no },
{ 'V', "version", Arg_parser::no }, { 'V', "version", Arg_parser::no },
{ format_opt, "format", Arg_parser::yes },
{ 0 , 0, Arg_parser::no } }; { 0 , 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options ); const Arg_parser parser( argc, argv, options );
@ -404,18 +346,19 @@ int main( const int argc, const char * const argv[] )
case 'h': show_help(); return 0; case 'h': show_help(); return 0;
case 'i': parse_ignore_initial( arg, ignore_initial ); break; case 'i': parse_ignore_initial( arg, ignore_initial ); break;
case 'l': verbosity = 1; break; case 'l': verbosity = 1; break;
case 'n': max_size = getnum( arg, 0, 0 ); break; case 'n': max_size = getnum( arg ); break;
case 'q': case 'q':
case 's': verbosity = -1; break; case 's': verbosity = -1; break;
case 'v': verbosity = 1; break; case 'v': verbosity = 1; break;
case 'V': show_version( "Zcmp" ); return 0; case 'V': show_version( "Zcmp" ); return 0;
case format_opt: get_format_types( arg, format_types ); break;
default : internal_error( "uncaught option" ); default : internal_error( "uncaught option" );
} }
} // end process options } // end process options
#if defined(__MSVCRT__) || defined(__OS2__) #if defined(__MSVCRT__) || defined(__OS2__)
_setmode( STDIN_FILENO, O_BINARY ); _fsetmode( stdin, "b" );
_setmode( STDOUT_FILENO, O_BINARY ); _fsetmode( stdout, "b" );
#endif #endif
if( argind >= parser.arguments() ) if( argind >= parser.arguments() )
@ -449,6 +392,9 @@ int main( const int argc, const char * const argv[] )
} }
else else
{ {
if( format_types[0] >= 0 || format_types[1] >= 0 )
{ show_error( "Two files must be given when format is specified.", 0, true );
return 2; }
filenames[1] = filenames[0]; filenames[1] = filenames[0];
infd[1] = open_other_instream( filenames[1] ); infd[1] = open_other_instream( filenames[1] );
if( infd[1] < 0 ) { infd[1] = STDIN_FILENO; filenames[1] = "-"; } if( infd[1] < 0 ) { infd[1] = STDIN_FILENO; filenames[1] = "-"; }
@ -457,8 +403,8 @@ int main( const int argc, const char * const argv[] )
int old_infd[2]; // copy of file descriptors of the two files int old_infd[2]; // copy of file descriptors of the two files
old_infd[0] = infd[0]; old_infd[1] = infd[1]; old_infd[0] = infd[0]; old_infd[1] = infd[1];
pid_t pid[2]; pid_t pid[2];
if( !set_data_feeder( &infd[0], &pid[0] ) || if( !set_data_feeder( &infd[0], &pid[0], format_types[0] ) ||
!set_data_feeder( &infd[1], &pid[1] ) ) !set_data_feeder( &infd[1], &pid[1], format_types[1] ) )
return 2; return 2;
for( int i = 0; i < 2; ++i ) for( int i = 0; i < 2; ++i )
@ -470,10 +416,15 @@ int main( const int argc, const char * const argv[] )
int retval = cmp( max_size, infd, filenames, print_bytes ); int retval = cmp( max_size, infd, filenames, print_bytes );
if( retval != 0 ) if( retval != 0 || max_size >= 0 )
{ {
if( pid[0] ) kill( pid[0], SIGTERM ); for( int i = 0; i < 2; ++i )
if( pid[1] ) kill( pid[1], SIGTERM ); if( pid[i] )
{
const int tmp = child_status( pid[i], "data feeder" );
if( tmp < 0 ) kill( pid[i], SIGTERM ); // child not terminated
else if( tmp != 0 ) retval = 2; // child status != 0
}
} }
else else
if( ( pid[0] && wait_for_child( pid[0], "data feeder" ) != 0 ) || if( ( pid[0] && wait_for_child( pid[0], "data feeder" ) != 0 ) ||

86
zcmpdiff.cc Normal file
View file

@ -0,0 +1,86 @@
/* Common code for Zcmp and Zdiff
Copyright (C) 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef O_BINARY
const int o_binary = O_BINARY;
#else
const int o_binary = 0;
#endif
struct { const char * from; const char * to; } const known_extensions[] = {
{ ".bz2", "" },
{ ".tbz", ".tar" },
{ ".tbz2", ".tar" },
{ ".gz", "" },
{ ".tgz", ".tar" },
{ ".lz", "" },
{ ".tlz", ".tar" },
{ ".xz", "" },
{ ".txz", ".tar" },
{ 0, 0 } };
int open_instream( const std::string & input_filename )
{
int infd = open( input_filename.c_str(), O_RDONLY | o_binary );
if( infd < 0 )
show_error2( "Can't open input file", input_filename.c_str() );
return infd;
}
int open_other_instream( std::string & name )
{
for( int i = 0; known_extensions[i].from; ++i )
{ // search uncompressed version
const std::string from( known_extensions[i].from );
if( name.size() > from.size() &&
name.compare( name.size() - from.size(), from.size(), from ) == 0 )
{
name.resize( name.size() - from.size() );
name += known_extensions[i].to;
return open( name.c_str(), O_RDONLY | o_binary );
}
}
for( int i = 0; i < num_formats; ++i )
{ // search compressed version
const std::string s( name + simple_extensions[format_order[i]] );
const int infd = open( s.c_str(), O_RDONLY | o_binary );
if( infd >= 0 ) { name = s; return infd; }
}
return -1;
}
void get_format_types( const std::string & arg, int format_types[2] )
{
const unsigned i = std::min( arg.find( ',' ), arg.size() );
if( i > 0 ) format_types[0] = get_format_type( arg.substr( 0, i ) );
else format_types[0] = -1;
if( i + 1 < arg.size() ) format_types[1] =
get_format_type( arg.substr( i + 1 ) );
else format_types[1] = -1;
}
bool check_identical( const char * const name1, const char * const name2 )
{
if( !std::strcmp( name1, name2 ) ) return true;
struct stat stat1, stat2;
if( stat( name1, &stat1 ) || stat( name2, &stat2 ) ) return false;
return ( stat1.st_ino == stat2.st_ino && stat1.st_dev == stat2.st_dev );
}

217
zdiff.cc
View file

@ -1,5 +1,5 @@
/* Zdiff - decompress and compare two files line by line /* Zdiff - decompress and compare two files line by line
Copyright (C) 2010, 2011 Antonio Diaz Diaz. Copyright (C) 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -17,6 +17,7 @@
#define _FILE_OFFSET_BITS 64 #define _FILE_OFFSET_BITS 64
#include <algorithm>
#include <cctype> #include <cctype>
#include <cerrno> #include <cerrno>
#include <climits> #include <climits>
@ -30,9 +31,6 @@
#include <stdint.h> #include <stdint.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#if defined(__MSVCRT__) || defined(__OS2__)
#include <io.h>
#endif
#include "arg_parser.h" #include "arg_parser.h"
#include "zutils.h" #include "zutils.h"
@ -41,126 +39,60 @@
#error "Environments where CHAR_BIT != 8 are not supported." #error "Environments where CHAR_BIT != 8 are not supported."
#endif #endif
#ifndef LLONG_MAX
#define LLONG_MAX 0x7FFFFFFFFFFFFFFFLL
#endif
#ifndef LLONG_MIN
#define LLONG_MIN (-LLONG_MAX - 1LL)
#endif
#ifndef ULLONG_MAX
#define ULLONG_MAX 0xFFFFFFFFFFFFFFFFULL
#endif
namespace { namespace {
std::string fifonames[2]; // names of the two fifos passed to diff std::string fifonames[2]; // names of the two fifos passed to diff
#ifdef O_BINARY #include "zcmpdiff.cc"
const int o_binary = O_BINARY;
#else
const int o_binary = 0;
#endif
struct { const char * from; const char * to; } const known_extensions[] = {
{ ".bz2", "" },
{ ".tbz", ".tar" },
{ ".tbz2", ".tar" },
{ ".gz", "" },
{ ".tgz", ".tar" },
{ ".lz", "" },
{ ".tlz", ".tar" },
{ ".xz", "" },
{ ".txz", ".tar" },
{ 0, 0 } };
void show_help() throw() void show_help()
{ {
std::printf( "Zdiff compares two files (\"-\" means standard input), and if they\n" ); std::printf( "Zdiff compares two files (\"-\" means standard input), and if they\n"
std::printf( "differ, shows the differences line by line. If any given file is\n" ); "differ, shows the differences line by line. If any given file is\n"
std::printf( "compressed, its uncompressed content is used. Zdiff is a front end to\n" ); "compressed, its uncompressed content is used. Zdiff is a front end to\n"
std::printf( "the diff program and has the limitation that messages from diff refer to\n" ); "the diff program and has the limitation that messages from diff refer to\n"
std::printf( "temporary filenames instead of those specified.\n" ); "temporary filenames instead of those specified.\n"
std::printf( "The supported compressors are bzip2, gzip, lzip and xz.\n" ); "\nThe supported formats are bzip2, gzip, lzip and xz.\n"
std::printf( "\nUsage: zdiff [options] file1 [file2]\n" ); "\nUsage: zdiff [options] file1 [file2]\n"
std::printf( "\nCompares <file1> to <file2>. If <file2> is omitted zdiff tries the\n" ); "\nCompares <file1> to <file2>. If <file2> is omitted zdiff tries the\n"
std::printf( "following:\n" ); "following:\n"
std::printf( "If <file1> is compressed, compares <file1> to the file with the\n" ); "If <file1> is compressed, compares <file1> to the file with the\n"
std::printf( "corresponding decompressed file name (removes the extension from\n" ); "corresponding decompressed file name (removes the extension from\n"
std::printf( "<file1>).\n" ); "<file1>).\n"
std::printf( "If <file1> is not compressed, compares <file1> to the uncompressed\n" ); "If <file1> is not compressed, compares <file1> to the uncompressed\n"
std::printf( "contents of <file1>.[bz2|gz|lz|xz] (the first one that is found).\n" ); "contents of <file1>.[bz2|gz|lz|xz] (the first one that is found).\n"
std::printf( "If no suitable file is found, compares <file1> to data read from\n" ); "If no suitable file is found, compares <file1> to data read from\n"
std::printf( "standard input.\n" ); "standard input.\n"
std::printf( "\nExit status is 0 if inputs are identical, 1 if different, 2 if trouble.\n" ); "\nExit status is 0 if inputs are identical, 1 if different, 2 if trouble.\n"
std::printf( "\nOptions:\n" ); "\nOptions:\n"
std::printf( " -h, --help display this help and exit\n" ); " -h, --help display this help and exit\n"
std::printf( " -V, --version output version information and exit\n" ); " -V, --version output version information and exit\n"
std::printf( " -a, --text treat all files as text\n" ); " -a, --text treat all files as text\n"
std::printf( " -b, --ignore-space-change ignore changes in the amount of white space\n" ); " -b, --ignore-space-change ignore changes in the amount of white space\n"
std::printf( " -B, --ignore-blank-lines ignore changes whose lines are all blank\n" ); " -B, --ignore-blank-lines ignore changes whose lines are all blank\n"
std::printf( " -c use the context output format\n" ); " -c use the context output format\n"
std::printf( " -C, --context=<n> same as -c but use <n> lines of context\n" ); " -C, --context=<n> same as -c but use <n> lines of context\n"
std::printf( " -d, --minimal try hard to find a smaller set of changes\n" ); " -d, --minimal try hard to find a smaller set of changes\n"
std::printf( " -E, --ignore-tab-expansion ignore changes due to tab expansion\n" ); " -E, --ignore-tab-expansion ignore changes due to tab expansion\n"
std::printf( " -i, --ignore-case ignore case differences in file contents\n" ); " --format=[<fmt1>][,<fmt2>] force given formats (bz2, gz, lz, xz)\n"
std::printf( " -p, --show-c-function show which C function each change is in\n" ); " -i, --ignore-case ignore case differences in file contents\n"
std::printf( " -q, --brief output only whether files differ\n" ); " -p, --show-c-function show which C function each change is in\n"
std::printf( " -s, --report-identical-files report when two files are identical\n" ); " -q, --brief output only whether files differ\n"
std::printf( " -t, --expand-tabs expand tabs to spaces in output\n" ); " -s, --report-identical-files report when two files are identical\n"
std::printf( " -T, --initial-tab make tabs line up by prepending a tab\n" ); " -t, --expand-tabs expand tabs to spaces in output\n"
std::printf( " -u use the unified output format\n" ); " -T, --initial-tab make tabs line up by prepending a tab\n"
std::printf( " -U, --unified=<n> same as -u but use <n> lines of context\n" ); " -u use the unified output format\n"
std::printf( " -w, --ignore-all-space ignore all white space\n" ); " -U, --unified=<n> same as -u but use <n> lines of context\n"
std::printf( "Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n" ); " -w, --ignore-all-space ignore all white space\n"
std::printf( "Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" ); "Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n"
"Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" );
show_help_addr(); show_help_addr();
} }
int open_instream( const std::string & input_filename ) throw() const char * my_basename( const char * filename )
{
int infd = open( input_filename.c_str(), O_RDONLY | o_binary );
if( infd < 0 )
show_error2( "Can't open input file", input_filename.c_str() );
return infd;
}
int open_other_instream( std::string & name ) throw()
{
for( int i = 0; known_extensions[i].from; ++i )
{ // search uncompressed version
const std::string from( known_extensions[i].from );
if( name.size() > from.size() &&
name.compare( name.size() - from.size(), from.size(), from ) == 0 )
{
name.resize( name.size() - from.size() );
name += known_extensions[i].to;
return open( name.c_str(), O_RDONLY | o_binary );
}
}
for( int i = 0; simple_extensions[i]; ++i )
{ // search compressed version
const std::string s( name + simple_extensions[i] );
const int infd = open( s.c_str(), O_RDONLY | o_binary );
if( infd >= 0 ) { name = s; return infd; }
}
return -1;
}
bool check_identical( const char * const name1, const char * const name2 ) throw()
{
if( !std::strcmp( name1, name2 ) ) return true;
struct stat stat1, stat2;
if( stat( name1, &stat1 ) || stat( name2, &stat2 ) ) return false;
return ( stat1.st_ino == stat2.st_ino && stat1.st_dev == stat2.st_dev );
}
const char * my_basename( const char * filename ) throw()
{ {
const char * c = filename; const char * c = filename;
while( *c ) { if( *c == '/' ) { filename = c + 1; } ++c; } while( *c ) { if( *c == '/' ) { filename = c + 1; } ++c; }
@ -168,7 +100,7 @@ const char * my_basename( const char * filename ) throw()
} }
extern "C" void remove_fifos() throw() extern "C" void remove_fifos()
{ {
if( fifonames[0].size() ) if( fifonames[0].size() )
{ std::remove( fifonames[0].c_str() ); fifonames[0].clear(); } { std::remove( fifonames[0].c_str() ); fifonames[0].clear(); }
@ -191,7 +123,7 @@ bool set_fifonames( const std::string filenames[2] )
if( p ) fifonames[i] = p; else fifonames[i] = "/tmp"; if( p ) fifonames[i] = p; else fifonames[i] = "/tmp";
fifonames[i] += '/'; fifonames[i] += '/';
int n = ( 2 * pid ) + i; int n = ( 2 * pid ) + i;
const unsigned int pos = fifonames[i].size(); const unsigned pos = fifonames[i].size();
do { fifonames[i].insert( pos, 1, codes[n % num_codes] ); do { fifonames[i].insert( pos, 1, codes[n % num_codes] );
n /= num_codes; } n /= num_codes; }
while( n ); while( n );
@ -216,15 +148,15 @@ bool set_fifonames( const std::string filenames[2] )
bool set_data_feeder( const std::string & fifoname, const int infd, bool set_data_feeder( const std::string & fifoname, const int infd,
pid_t * const pidp ) pid_t * const pidp, const int format_type )
{ {
std::string file_type; const uint8_t * magic_data = 0;
const uint8_t * magic_data; int magic_size = 0;
int magic_size; const char * const decompressor_name = ( format_type >= 0 ) ?
const bool compressed = decompressor_names[format_type] :
test_format( infd, file_type, &magic_data, &magic_size ); test_format( infd, &magic_data, &magic_size );
if( compressed ) // compressed with `file_type' if( decompressor_name ) // compressed
{ {
int fda[2]; // pipe from feeder to decompressor int fda[2]; // pipe from feeder to decompressor
if( pipe( fda ) < 0 ) if( pipe( fda ) < 0 )
@ -239,7 +171,7 @@ bool set_data_feeder( const std::string & fifoname, const int infd,
if( outfd < 0 ) if( outfd < 0 )
{ {
if( verbosity >= 0 ) if( verbosity >= 0 )
std::fprintf( stderr, "%s: Can't open FIFO `%s' for writing: %s.\n", std::fprintf( stderr, "%s: Can't open FIFO '%s' for writing: %s.\n",
util_name, fifoname.c_str(), std::strerror( errno ) ); util_name, fifoname.c_str(), std::strerror( errno ) );
_exit( 2 ); _exit( 2 );
} }
@ -247,19 +179,20 @@ bool set_data_feeder( const std::string & fifoname, const int infd,
dup2( outfd, STDOUT_FILENO ) >= 0 && dup2( outfd, STDOUT_FILENO ) >= 0 &&
close( fda[0] ) == 0 && close( fda[1] ) == 0 && close( fda[0] ) == 0 && close( fda[1] ) == 0 &&
close( outfd ) == 0 ) close( outfd ) == 0 )
execlp( file_type.c_str(), file_type.c_str(), "-cdfq", (char *)0 ); execlp( decompressor_name, decompressor_name,
show_exec_error( file_type.c_str() ); (verbosity >= 0) ? "-d" : "-dq", (char *)0 );
show_exec_error( decompressor_name );
_exit( 2 ); _exit( 2 );
} }
if( pid2 < 0 ) if( pid2 < 0 )
{ show_fork_error( file_type.c_str() ); _exit( 2 ); } { show_fork_error( decompressor_name ); _exit( 2 ); }
if( close( fda[0] ) != 0 || if( close( fda[0] ) != 0 ||
!feed_data( infd, fda[1], magic_data, magic_size ) ) !feed_data( infd, fda[1], magic_data, magic_size ) )
_exit( 2 ); _exit( 2 );
if( close( fda[1] ) != 0 ) if( close( fda[1] ) != 0 )
{ show_close_error( "data feeder" ); _exit( 2 ); } { show_close_error( "data feeder" ); _exit( 2 ); }
_exit( wait_for_child( pid2, file_type.c_str() ) ); _exit( wait_for_child( pid2, decompressor_name ) );
} }
// parent // parent
close( fda[0] ); close( fda[1] ); close( fda[0] ); close( fda[1] );
@ -276,7 +209,7 @@ bool set_data_feeder( const std::string & fifoname, const int infd,
if( outfd < 0 ) if( outfd < 0 )
{ {
if( verbosity >= 0 ) if( verbosity >= 0 )
std::fprintf( stderr, "%s: Can't open FIFO `%s' for writing: %s.\n", std::fprintf( stderr, "%s: Can't open FIFO '%s' for writing: %s.\n",
util_name, fifoname.c_str(), std::strerror( errno ) ); util_name, fifoname.c_str(), std::strerror( errno ) );
_exit( 2 ); _exit( 2 );
} }
@ -295,7 +228,7 @@ bool set_data_feeder( const std::string & fifoname, const int infd,
} }
extern "C" void signal_handler( int sig ) throw() extern "C" void signal_handler( int sig )
{ {
remove_fifos(); remove_fifos();
std::signal( sig, SIG_DFL ); std::signal( sig, SIG_DFL );
@ -303,7 +236,7 @@ extern "C" void signal_handler( int sig ) throw()
} }
void set_signals() throw() void set_signals()
{ {
std::signal( SIGHUP, signal_handler ); std::signal( SIGHUP, signal_handler );
std::signal( SIGINT, signal_handler ); std::signal( SIGINT, signal_handler );
@ -315,7 +248,9 @@ void set_signals() throw()
int main( const int argc, const char * const argv[] ) int main( const int argc, const char * const argv[] )
{ {
enum { format_opt = 256 };
std::vector< const char * > diff_args; // args to diff, maybe empty std::vector< const char * > diff_args; // args to diff, maybe empty
int format_types[2] = { -1, -1 };
invocation_name = argv[0]; invocation_name = argv[0];
util_name = "zdiff"; util_name = "zdiff";
@ -339,6 +274,7 @@ int main( const int argc, const char * const argv[] )
{ 'U', "unified", Arg_parser::yes }, { 'U', "unified", Arg_parser::yes },
{ 'V', "version", Arg_parser::no }, { 'V', "version", Arg_parser::no },
{ 'w', "ignore-all-space", Arg_parser::no }, { 'w', "ignore-all-space", Arg_parser::no },
{ format_opt, "format", Arg_parser::yes },
{ 0 , 0, Arg_parser::no } }; { 0 , 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options ); const Arg_parser parser( argc, argv, options );
@ -371,13 +307,14 @@ int main( const int argc, const char * const argv[] )
case 'U': diff_args.push_back( "-U" ); diff_args.push_back( arg ); break; case 'U': diff_args.push_back( "-U" ); diff_args.push_back( arg ); break;
case 'V': show_version( "Zdiff" ); return 0; case 'V': show_version( "Zdiff" ); return 0;
case 'w': diff_args.push_back( "-w" ); break; case 'w': diff_args.push_back( "-w" ); break;
case format_opt: get_format_types( arg, format_types ); break;
default : internal_error( "uncaught option" ); default : internal_error( "uncaught option" );
} }
} // end process options } // end process options
#if defined(__MSVCRT__) || defined(__OS2__) #if defined(__MSVCRT__) || defined(__OS2__)
_setmode( STDIN_FILENO, O_BINARY ); _fsetmode( stdin, "b" );
_setmode( STDOUT_FILENO, O_BINARY ); _fsetmode( stdout, "b" );
#endif #endif
if( argind >= parser.arguments() ) if( argind >= parser.arguments() )
@ -408,6 +345,9 @@ int main( const int argc, const char * const argv[] )
} }
else else
{ {
if( format_types[0] >= 0 || format_types[1] >= 0 )
{ show_error( "Two files must be given when format is specified.", 0, true );
return 2; }
filenames[1] = filenames[0]; filenames[1] = filenames[0];
infd[1] = open_other_instream( filenames[1] ); infd[1] = open_other_instream( filenames[1] );
if( infd[1] < 0 ) { infd[1] = STDIN_FILENO; filenames[1] = "-"; } if( infd[1] < 0 ) { infd[1] = STDIN_FILENO; filenames[1] = "-"; }
@ -422,7 +362,7 @@ int main( const int argc, const char * const argv[] )
{ {
const char ** const argv = new const char *[diff_args.size()+5]; const char ** const argv = new const char *[diff_args.size()+5];
argv[0] = DIFF; argv[0] = DIFF;
for( unsigned int i = 0; i < diff_args.size(); ++i ) for( unsigned i = 0; i < diff_args.size(); ++i )
argv[i+1] = diff_args[i]; argv[i+1] = diff_args[i];
argv[diff_args.size()+1] = "--"; argv[diff_args.size()+1] = "--";
argv[diff_args.size()+2] = fifonames[0].c_str(); argv[diff_args.size()+2] = fifonames[0].c_str();
@ -437,16 +377,21 @@ int main( const int argc, const char * const argv[] )
{ show_fork_error( DIFF ); return 2; } { show_fork_error( DIFF ); return 2; }
pid_t pid[2]; pid_t pid[2];
if( !set_data_feeder( fifonames[0], infd[0], &pid[0] ) || if( !set_data_feeder( fifonames[0], infd[0], &pid[0], format_types[0] ) ||
!set_data_feeder( fifonames[1], infd[1], &pid[1] ) ) !set_data_feeder( fifonames[1], infd[1], &pid[1], format_types[1] ) )
return 2; return 2;
int retval = wait_for_child( diff_pid, DIFF ); int retval = wait_for_child( diff_pid, DIFF );
if( retval != 0 ) if( retval != 0 )
{ {
if( pid[0] ) kill( pid[0], SIGTERM ); for( int i = 0; i < 2; ++i )
if( pid[1] ) kill( pid[1], SIGTERM ); if( pid[i] )
{
const int tmp = child_status( pid[i], "data feeder" );
if( tmp < 0 ) kill( pid[i], SIGTERM ); // child not terminated
else if( tmp != 0 ) retval = 2; // child status != 0
}
} }
else else
if( ( pid[0] && wait_for_child( pid[0], "data feeder" ) != 0 ) || if( ( pid[0] && wait_for_child( pid[0], "data feeder" ) != 0 ) ||

103
zgrep.cc
View file

@ -1,5 +1,5 @@
/* Zgrep - search compressed files for a regular expression /* Zgrep - search compressed files for a regular expression
Copyright (C) 2010, 2011 Antonio Diaz Diaz. Copyright (C) 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -15,57 +15,59 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
void show_zgrep_help() throw() void show_zgrep_help()
{ {
std::printf( "Zgrep is a front end to the grep program that allows transparent search\n" ); std::printf( "Zgrep is a front end to the grep program that allows transparent search\n"
std::printf( "on any combination of compressed and non-compressed files. If any given\n" ); "on any combination of compressed and non-compressed files. If any given\n"
std::printf( "file is compressed, its uncompressed content is used. If a given file\n" ); "file is compressed, its uncompressed content is used. If a given file\n"
std::printf( "does not exist, and its name does not end with one of the known\n" ); "does not exist, and its name does not end with one of the known\n"
std::printf( "extensions, zgrep tries the compressed file names corresponding to the\n" ); "extensions, zgrep tries the compressed file names corresponding to the\n"
std::printf( "supported compressors. If no files are specified, data is read from\n" ); "supported formats. If no files are specified, data is read from\n"
std::printf( "standard input, decompressed if needed, and fed to grep. Data read from\n" ); "standard input, decompressed if needed, and fed to grep. Data read from\n"
std::printf( "standard input must be of the same type; all uncompressed or all\n" ); "standard input must be of the same type; all uncompressed or all\n"
std::printf( "compressed with the same compressor.\n" ); "in the same compression format.\n"
std::printf( "The supported compressors are bzip2, gzip, lzip and xz.\n" ); "\nThe supported formats are bzip2, gzip, lzip and xz.\n"
std::printf( "\nUsage: zgrep [options] <pattern> [files]\n" ); "\nUsage: zgrep [options] <pattern> [files]\n"
std::printf( "\nExit status is 0 if match, 1 if no match, 2 if trouble.\n" ); "\nExit status is 0 if match, 1 if no match, 2 if trouble.\n"
std::printf( "\nOptions:\n" ); "\nOptions:\n"
std::printf( " --help display this help and exit\n" ); " --help display this help and exit\n"
std::printf( " -V, --version output version information and exit\n" ); " -V, --version output version information and exit\n"
std::printf( " -a, --text treat all files as text\n" ); " -a, --text treat all files as text\n"
std::printf( " -A, --after-context=<n> print <n> lines of trailing context\n" ); " -A, --after-context=<n> print <n> lines of trailing context\n"
std::printf( " -b, --byte-offset print the byte offset of each line\n" ); " -b, --byte-offset print the byte offset of each line\n"
std::printf( " -B, --before-context=<n> print <n> lines of leading context\n" ); " -B, --before-context=<n> print <n> lines of leading context\n"
std::printf( " -c, --count only print a count of matching lines per file\n" ); " -c, --count only print a count of matching lines per file\n"
std::printf( " -C, --context=<n> print <n> lines of output context\n" ); " -C, --context=<n> print <n> lines of output context\n"
std::printf( " -e, --regexp=<pattern> use <pattern> as the pattern to match\n" ); " -e, --regexp=<pattern> use <pattern> as the pattern to match\n"
std::printf( " -E, --extended-regexp <pattern> is an extended regular expression\n" ); " -E, --extended-regexp <pattern> is an extended regular expression\n"
std::printf( " -f, --file=<file> obtain patterns from <file>\n" ); " -f, --file=<file> obtain patterns from <file>\n"
std::printf( " -F, --fixed-strings <pattern> is a set of newline-separated strings\n" ); " -F, --fixed-strings <pattern> is a set of newline-separated strings\n"
std::printf( " -h, --no-filename suppress the prefixing filename on output\n" ); " --format=<fmt> force given format (bz2, gz, lz, xz)\n"
std::printf( " -H, --with-filename print the filename for each match\n" ); " -h, --no-filename suppress the prefixing filename on output\n"
std::printf( " -i, --ignore-case ignore case distinctions\n" ); " -H, --with-filename print the filename for each match\n"
std::printf( " -I ignore binary files\n" ); " -i, --ignore-case ignore case distinctions\n"
std::printf( " -l, --files-with-matches only print names of files containing matches\n" ); " -I ignore binary files\n"
std::printf( " -L, --files-without-match only print names of files containing no matches\n" ); " -l, --files-with-matches only print names of files containing matches\n"
std::printf( " -m, --max-count=<n> stop after <n> matches\n" ); " -L, --files-without-match only print names of files containing no matches\n"
std::printf( " -n, --line-number print the line number of each line\n" ); " -m, --max-count=<n> stop after <n> matches\n"
std::printf( " -o, --only-matching show only the part of a line matching <pattern>\n" ); " -n, --line-number print the line number of each line\n"
std::printf( " -q, --quiet suppress all messages\n" ); " -o, --only-matching show only the part of a line matching <pattern>\n"
std::printf( " -r, --recursive operate recursively on directories\n" ); " -q, --quiet suppress all messages\n"
std::printf( " -s, --no-messages suppress error messages\n" ); " -r, --recursive operate recursively on directories\n"
std::printf( " -v, --invert-match select non-matching lines\n" ); " -s, --no-messages suppress error messages\n"
std::printf( " --verbose verbose mode (show error messages)\n" ); " -v, --invert-match select non-matching lines\n"
std::printf( " -w, --word-regexp match only whole words\n" ); " --verbose verbose mode (show error messages)\n"
std::printf( " -x, --line-regexp match only whole lines\n" ); " -w, --word-regexp match only whole words\n"
" -x, --line-regexp match only whole lines\n" );
show_help_addr(); show_help_addr();
} }
int zgrep_stdin( int infd, const std::vector< const char * > & grep_args ) int zgrep_stdin( int infd, const int format_type,
const std::vector< const char * > & grep_args )
{ {
pid_t pid; pid_t pid;
if( !set_data_feeder( &infd, &pid ) ) return 2; if( !set_data_feeder( &infd, &pid, format_type ) ) return 2;
const pid_t grep_pid = fork(); const pid_t grep_pid = fork();
if( grep_pid == 0 ) // child (grep) if( grep_pid == 0 ) // child (grep)
{ {
@ -73,7 +75,7 @@ int zgrep_stdin( int infd, const std::vector< const char * > & grep_args )
{ {
const char ** const argv = new const char *[grep_args.size()+2]; const char ** const argv = new const char *[grep_args.size()+2];
argv[0] = GREP; argv[0] = GREP;
for( unsigned int i = 0; i < grep_args.size(); ++i ) for( unsigned i = 0; i < grep_args.size(); ++i )
argv[i+1] = grep_args[i]; argv[i+1] = grep_args[i];
argv[grep_args.size()+1] = 0; argv[grep_args.size()+1] = 0;
execvp( argv[0], (char **)argv ); execvp( argv[0], (char **)argv );
@ -96,12 +98,13 @@ int zgrep_stdin( int infd, const std::vector< const char * > & grep_args )
} }
int zgrep_file( int infd, const std::string & input_filename, int zgrep_file( int infd, const int format_type,
const std::string & input_filename,
const std::vector< const char * > & grep_args, const std::vector< const char * > & grep_args,
const bool grep_list, const bool grep_show_name ) const bool grep_list, const bool grep_show_name )
{ {
pid_t pid; pid_t pid;
if( !set_data_feeder( &infd, &pid ) ) return 2; if( !set_data_feeder( &infd, &pid, format_type ) ) return 2;
int fda[2]; // pipe from grep int fda[2]; // pipe from grep
if( pipe( fda ) < 0 ) if( pipe( fda ) < 0 )
{ show_error( "Can't create pipe", errno ); return 2; } { show_error( "Can't create pipe", errno ); return 2; }
@ -114,7 +117,7 @@ int zgrep_file( int infd, const std::string & input_filename,
{ {
const char ** const argv = new const char *[grep_args.size()+2]; const char ** const argv = new const char *[grep_args.size()+2];
argv[0] = GREP; argv[0] = GREP;
for( unsigned int i = 0; i < grep_args.size(); ++i ) for( unsigned i = 0; i < grep_args.size(); ++i )
argv[i+1] = grep_args[i]; argv[i+1] = grep_args[i];
argv[grep_args.size()+1] = 0; argv[grep_args.size()+1] = 0;
execvp( argv[0], (char **)argv ); execvp( argv[0], (char **)argv );
@ -144,7 +147,7 @@ int zgrep_file( int infd, const std::string & input_filename,
if( buffer[i] == '\n' ) line_begin = true; if( buffer[i] == '\n' ) line_begin = true;
putchar( buffer[i] ); putchar( buffer[i] );
} }
else if( std::fwrite( buffer, 1, size, stdout ) != (unsigned int)size ) else if( std::fwrite( buffer, 1, size, stdout ) != (unsigned)size )
{ show_error( "Write error", errno ); return 2; } { show_error( "Write error", errno ); return 2; }
} }
if( size < buffer_size ) break; if( size < buffer_size ) break;

View file

@ -1,5 +1,5 @@
/* Ztest - verify integrity of compressed files /* Ztest - verify integrity of compressed files
Copyright (C) 2010, 2011 Antonio Diaz Diaz. Copyright (C) 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -15,32 +15,39 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
void show_ztest_help() throw() void show_ztest_help()
{ {
std::printf( "Ztest verifies the integrity of the specified compressed files.\n" ); std::printf( "Ztest verifies the integrity of the specified compressed files.\n"
std::printf( "Non-compressed files are ignored. If no files are specified, the\n" ); "Non-compressed files are ignored. If no files are specified, the\n"
std::printf( "integrity of compressed data read from standard input is verified. Data\n" ); "integrity of compressed data read from standard input is verified. Data\n"
std::printf( "read from standard input must be all compressed with the same compressor.\n" ); "read from standard input must be all in the same compression format.\n"
std::printf( "The supported compressors are bzip2, gzip, lzip and xz.\n" ); "\nThe supported formats are bzip2, gzip, lzip and xz.\n"
std::printf( "\nUsage: ztest [options] [files]\n" ); "\nNote that some xz files lack integrity information, and therefore can't\n"
std::printf( "\nExit status is 2 if any compressed file is corrupt, 0 otherwise.\n" ); "be verified as reliably as the other formats can.\n"
std::printf( "\nOptions:\n" ); "\nUsage: ztest [options] [files]\n"
std::printf( " -h, --help display this help and exit\n" ); "\nExit status is 0 if all compressed files verify OK, 1 if environmental\n"
std::printf( " -V, --version output version information and exit\n" ); "problems (file not found, invalid flags, I/O errors, etc), 2 if any\n"
std::printf( " -q, --quiet suppress all messages\n" ); "compressed file is corrupt or invalid.\n"
std::printf( " -r, --recursive operate recursively on directories\n" ); "\nOptions:\n"
std::printf( " -v, --verbose be verbose (a 2nd -v gives more)\n" ); " -h, --help display this help and exit\n"
" -V, --version output version information and exit\n"
" --format=<fmt> force given format (bz2, gz, lz, xz)\n"
" -q, --quiet suppress all messages\n"
" -r, --recursive operate recursively on directories\n"
" -v, --verbose be verbose (a 2nd -v gives more)\n" );
show_help_addr(); show_help_addr();
} }
int ztest_stdin( const int infd, int ztest_stdin( const int infd, const int format_type,
const std::vector< const char * > & ztest_args ) const std::vector< const char * > & ztest_args )
{ {
std::string file_type; const uint8_t * magic_data = 0;
const uint8_t * magic_data;
int magic_size = 0; int magic_size = 0;
if( !test_format( infd, file_type, &magic_data, &magic_size ) ) const char * const decompressor_name = ( format_type >= 0 ) ?
decompressor_names[format_type] :
test_format( infd, &magic_data, &magic_size );
if( !decompressor_name )
{ show_error( "Unknown data format read from stdin." ); return 2; } { show_error( "Unknown data format read from stdin." ); return 2; }
int fda[2]; // pipe from feeder int fda[2]; // pipe from feeder
if( pipe( fda ) < 0 ) if( pipe( fda ) < 0 )
@ -53,19 +60,19 @@ int ztest_stdin( const int infd,
close( fda[0] ) == 0 && close( fda[1] ) == 0 ) close( fda[0] ) == 0 && close( fda[1] ) == 0 )
{ {
const char ** const argv = new const char *[ztest_args.size()+3]; const char ** const argv = new const char *[ztest_args.size()+3];
argv[0] = file_type.c_str(); argv[0] = decompressor_name;
for( unsigned int i = 0; i < ztest_args.size(); ++i ) for( unsigned i = 0; i < ztest_args.size(); ++i )
argv[i+1] = ztest_args[i]; argv[i+1] = ztest_args[i];
argv[ztest_args.size()+1] = "-t"; argv[ztest_args.size()+1] = "-t";
argv[ztest_args.size()+2] = 0; argv[ztest_args.size()+2] = 0;
execvp( argv[0], (char **)argv ); execvp( argv[0], (char **)argv );
} }
show_exec_error( file_type.c_str() ); show_exec_error( decompressor_name );
_exit( 1 ); _exit( 1 );
} }
// parent // parent
if( pid < 0 ) if( pid < 0 )
{ show_fork_error( file_type.c_str() ); return 1; } { show_fork_error( decompressor_name ); return 1; }
const pid_t pid2 = fork(); const pid_t pid2 = fork();
if( pid2 == 0 ) // child2 (decompressor feeder) if( pid2 == 0 ) // child2 (decompressor feeder)
@ -82,40 +89,43 @@ int ztest_stdin( const int infd,
{ show_fork_error( "decompressor feeder" ); return 1; } { show_fork_error( "decompressor feeder" ); return 1; }
close( fda[0] ); close( fda[1] ); close( fda[0] ); close( fda[1] );
int retval = wait_for_child( pid, file_type.c_str(), 1 ); int retval = wait_for_child( pid, decompressor_name, 1 );
if( retval == 0 && wait_for_child( pid2, "decompressor feeder" ) != 0 ) if( retval == 0 && wait_for_child( pid2, "decompressor feeder" ) != 0 )
retval = 1; retval = 1;
return retval; return retval;
} }
int ztest_file( const int infd, const std::string & input_filename, int ztest_file( const int infd, const int format_type,
const std::string & input_filename,
const std::vector< const char * > & ztest_args ) const std::vector< const char * > & ztest_args )
{ {
std::string file_type; const uint8_t * magic_data = 0;
const uint8_t * magic_data;
int magic_size = 0; int magic_size = 0;
if( !test_format( infd, file_type, &magic_data, &magic_size ) ) const char * const decompressor_name = ( format_type >= 0 ) ?
return 0; // ignored decompressor_names[format_type] :
test_format( infd, &magic_data, &magic_size );
if( !decompressor_name )
return 0; // skip this file
const pid_t pid = fork(); const pid_t pid = fork();
if( pid == 0 ) // child (decompressor) if( pid == 0 ) // child (decompressor)
{ {
const char ** const argv = new const char *[ztest_args.size()+5]; const char ** const argv = new const char *[ztest_args.size()+5];
argv[0] = file_type.c_str(); argv[0] = decompressor_name;
for( unsigned int i = 0; i < ztest_args.size(); ++i ) for( unsigned i = 0; i < ztest_args.size(); ++i )
argv[i+1] = ztest_args[i]; argv[i+1] = ztest_args[i];
argv[ztest_args.size()+1] = "-t"; argv[ztest_args.size()+1] = "-t";
argv[ztest_args.size()+2] = "--"; argv[ztest_args.size()+2] = "--";
argv[ztest_args.size()+3] = input_filename.c_str(); argv[ztest_args.size()+3] = input_filename.c_str();
argv[ztest_args.size()+4] = 0; argv[ztest_args.size()+4] = 0;
execvp( argv[0], (char **)argv ); execvp( argv[0], (char **)argv );
show_exec_error( file_type.c_str() ); show_exec_error( decompressor_name );
_exit( 1 ); _exit( 1 );
} }
// parent // parent
if( pid < 0 ) if( pid < 0 )
{ show_fork_error( file_type.c_str() ); return 1; } { show_fork_error( decompressor_name ); return 1; }
return wait_for_child( pid, file_type.c_str(), 1 ); return wait_for_child( pid, decompressor_name, 1 );
} }

151
zutils.cc
View file

@ -1,5 +1,5 @@
/* Zutils - Utilities dealing with compressed files /* Zutils - Utilities dealing with compressed files
Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. Copyright (C) 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -35,10 +35,20 @@ const char * util_name = program_name;
int verbosity = 0; int verbosity = 0;
int get_format_type( const std::string & arg )
{
for( int i = 0; i < num_formats; ++i )
if( arg == format_names[i] )
return i;
show_error( "Bad argument for '--format' option." );
std::exit( 1 );
}
// Returns the number of bytes really read. // Returns the number of bytes really read.
// If (returned value < size) and (errno == 0), means EOF was reached. // If (returned value < size) and (errno == 0), means EOF was reached.
// //
int readblock( const int fd, uint8_t * const buf, const int size ) throw() int readblock( const int fd, uint8_t * const buf, const int size )
{ {
int rest = size; int rest = size;
errno = 0; errno = 0;
@ -47,7 +57,7 @@ int readblock( const int fd, uint8_t * const buf, const int size ) throw()
errno = 0; errno = 0;
const int n = read( fd, buf + size - rest, rest ); const int n = read( fd, buf + size - rest, rest );
if( n > 0 ) rest -= n; if( n > 0 ) rest -= n;
else if( n == 0 ) break; else if( n == 0 ) break; // EOF
else if( errno != EINTR && errno != EAGAIN ) break; else if( errno != EINTR && errno != EAGAIN ) break;
} }
return ( rest > 0 ) ? size - rest : size; return ( rest > 0 ) ? size - rest : size;
@ -57,7 +67,7 @@ int readblock( const int fd, uint8_t * const buf, const int size ) throw()
// Returns the number of bytes really written. // Returns the number of bytes really written.
// If (returned value < size), it is always an error. // If (returned value < size), it is always an error.
// //
int writeblock( const int fd, const uint8_t * const buf, const int size ) throw() int writeblock( const int fd, const uint8_t * const buf, const int size )
{ {
int rest = size; int rest = size;
errno = 0; errno = 0;
@ -66,7 +76,7 @@ int writeblock( const int fd, const uint8_t * const buf, const int size ) throw(
errno = 0; errno = 0;
const int n = write( fd, buf + size - rest, rest ); const int n = write( fd, buf + size - rest, rest );
if( n > 0 ) rest -= n; if( n > 0 ) rest -= n;
else if( errno && errno != EINTR && errno != EAGAIN ) break; else if( n < 0 && errno != EINTR && errno != EAGAIN ) break;
} }
return ( rest > 0 ) ? size - rest : size; return ( rest > 0 ) ? size - rest : size;
} }
@ -75,7 +85,7 @@ int writeblock( const int fd, const uint8_t * const buf, const int size ) throw(
bool feed_data( const int infd, const int outfd, bool feed_data( const int infd, const int outfd,
const uint8_t * magic_data, const int magic_size ) const uint8_t * magic_data, const int magic_size )
{ {
if( writeblock( outfd, magic_data, magic_size ) != magic_size ) if( magic_size && writeblock( outfd, magic_data, magic_size ) != magic_size )
{ show_error( "Write error", errno ); return false; } { show_error( "Write error", errno ); return false; }
enum { buffer_size = 4096 }; enum { buffer_size = 4096 };
uint8_t buffer[buffer_size]; uint8_t buffer[buffer_size];
@ -92,15 +102,16 @@ bool feed_data( const int infd, const int outfd,
} }
bool set_data_feeder( int * const infdp, pid_t * const pidp ) bool set_data_feeder( int * const infdp, pid_t * const pidp,
const int format_type )
{ {
std::string file_type; const uint8_t * magic_data = 0;
const uint8_t * magic_data; int magic_size = 0;
int magic_size; const char * const decompressor_name = ( format_type >= 0 ) ?
const bool compressed = decompressor_names[format_type] :
test_format( *infdp, file_type, &magic_data, &magic_size ); test_format( *infdp, &magic_data, &magic_size );
if( compressed ) // compressed with `file_type' if( decompressor_name ) // compressed
{ {
int fda[2]; // pipe from feeder int fda[2]; // pipe from feeder
int fda2[2]; // pipe from decompressor int fda2[2]; // pipe from decompressor
@ -118,12 +129,13 @@ bool set_data_feeder( int * const infdp, pid_t * const pidp )
dup2( fda2[1], STDOUT_FILENO ) >= 0 && dup2( fda2[1], STDOUT_FILENO ) >= 0 &&
close( fda[0] ) == 0 && close( fda[1] ) == 0 && close( fda[0] ) == 0 && close( fda[1] ) == 0 &&
close( fda2[0] ) == 0 && close( fda2[1] ) == 0 ) close( fda2[0] ) == 0 && close( fda2[1] ) == 0 )
execlp( file_type.c_str(), file_type.c_str(), "-cdfq", (char *)0 ); execlp( decompressor_name, decompressor_name,
show_exec_error( file_type.c_str() ); (verbosity >= 0) ? "-d" : "-dq", (char *)0 );
show_exec_error( decompressor_name );
_exit( 2 ); _exit( 2 );
} }
if( pid2 < 0 ) if( pid2 < 0 )
{ show_fork_error( file_type.c_str() ); _exit( 2 ); } { show_fork_error( decompressor_name ); _exit( 2 ); }
if( close( fda[0] ) != 0 || if( close( fda[0] ) != 0 ||
close( fda2[0] ) != 0 || close( fda2[1] ) != 0 || close( fda2[0] ) != 0 || close( fda2[1] ) != 0 ||
@ -131,7 +143,7 @@ bool set_data_feeder( int * const infdp, pid_t * const pidp )
_exit( 2 ); _exit( 2 );
if( close( fda[1] ) != 0 ) if( close( fda[1] ) != 0 )
{ show_close_error( "decompressor feeder" ); _exit( 2 ); } { show_close_error( "decompressor feeder" ); _exit( 2 ); }
_exit( wait_for_child( pid2, file_type.c_str() ) ); _exit( wait_for_child( pid2, decompressor_name ) );
} }
// parent // parent
close( fda[0] ); close( fda[1] ); close( fda2[1] ); close( fda[0] ); close( fda[1] ); close( fda2[1] );
@ -166,28 +178,27 @@ bool set_data_feeder( int * const infdp, pid_t * const pidp )
} }
void show_help_addr() throw() void show_help_addr()
{ {
std::printf( "\nReport bugs to zutils-bug@nongnu.org\n" ); std::printf( "\nReport bugs to zutils-bug@nongnu.org\n"
std::printf( "Zutils home page: http://www.nongnu.org/zutils/zutils.html\n" ); "Zutils home page: http://www.nongnu.org/zutils/zutils.html\n" );
} }
void show_version( const char * const Util_name ) throw() void show_version( const char * const Util_name )
{ {
if( !Util_name || !*Util_name ) if( !Util_name || !*Util_name )
std::printf( "%s %s\n", Program_name, PROGVERSION ); std::printf( "%s %s\n", Program_name, PROGVERSION );
else else
std::printf( "%s (%s) %s\n", Util_name, program_name, PROGVERSION ); std::printf( "%s (%s) %s\n", Util_name, program_name, PROGVERSION );
std::printf( "Copyright (C) %s Antonio Diaz Diaz.\n", program_year ); std::printf( "Copyright (C) %s Antonio Diaz Diaz.\n", program_year );
std::printf( "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n" ); std::printf( "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n"
std::printf( "This is free software: you are free to change and redistribute it.\n" ); "This is free software: you are free to change and redistribute it.\n"
std::printf( "There is NO WARRANTY, to the extent permitted by law.\n" ); "There is NO WARRANTY, to the extent permitted by law.\n" );
} }
void show_error( const char * const msg, const int errcode, void show_error( const char * const msg, const int errcode, const bool help )
const bool help ) throw()
{ {
if( verbosity >= 0 ) if( verbosity >= 0 )
{ {
@ -198,22 +209,22 @@ void show_error( const char * const msg, const int errcode,
std::fprintf( stderr, ": %s", std::strerror( errcode ) ); std::fprintf( stderr, ": %s", std::strerror( errcode ) );
std::fprintf( stderr, "\n" ); std::fprintf( stderr, "\n" );
} }
if( help && invocation_name && invocation_name[0] ) if( help )
std::fprintf( stderr, "Try `%s --help' for more information.\n", std::fprintf( stderr, "Try '%s --help' for more information.\n",
invocation_name ); invocation_name );
} }
} }
void show_error2( const char * const msg, const char * const name ) throw() void show_error2( const char * const msg, const char * const name )
{ {
if( verbosity >= 0 ) if( verbosity >= 0 )
std::fprintf( stderr, "%s: %s `%s': %s.\n", std::fprintf( stderr, "%s: %s '%s': %s.\n",
util_name, msg, name, std::strerror( errno ) ); util_name, msg, name, std::strerror( errno ) );
} }
void show_close_error( const char * const prog_name ) throw() void show_close_error( const char * const prog_name )
{ {
if( verbosity >= 0 ) if( verbosity >= 0 )
std::fprintf( stderr, "%s: Can't close output of %s: %s.\n", std::fprintf( stderr, "%s: Can't close output of %s: %s.\n",
@ -221,18 +232,18 @@ void show_close_error( const char * const prog_name ) throw()
} }
void show_exec_error( const char * const prog_name ) throw() void show_exec_error( const char * const prog_name )
{ {
if( verbosity >= 0 ) if( verbosity >= 0 )
std::fprintf( stderr, "%s: Can't exec `%s': %s.\n", std::fprintf( stderr, "%s: Can't exec '%s': %s.\n",
util_name, prog_name, std::strerror( errno ) ); util_name, prog_name, std::strerror( errno ) );
} }
void show_fork_error( const char * const prog_name ) throw() void show_fork_error( const char * const prog_name )
{ {
if( verbosity >= 0 ) if( verbosity >= 0 )
std::fprintf( stderr, "%s: Can't fork `%s': %s.\n", std::fprintf( stderr, "%s: Can't fork '%s': %s.\n",
util_name, prog_name, std::strerror( errno ) ); util_name, prog_name, std::strerror( errno ) );
} }
@ -245,21 +256,13 @@ void internal_error( const char * const msg )
} }
unsigned char xdigit( const int value ) throw() const char * test_format( const int infd,
{ const uint8_t ** const magic_datap,
if( value >= 0 && value <= 9 ) return '0' + value; int * const magic_sizep )
if( value >= 10 && value <= 15 ) return 'A' + ( value - 10 );
return 0;
}
bool test_format( const int infd, std::string & file_type,
const uint8_t ** const magic_datap, int * const magic_sizep )
{ {
enum { buf_size = 5 }; enum { buf_size = 5 };
static uint8_t buf[buf_size]; static uint8_t buf[buf_size];
int i = 0; int i = 0;
file_type.clear();
if( readblock( infd, buf, 1 ) == 1 ) if( readblock( infd, buf, 1 ) == 1 )
{ {
++i; ++i;
@ -267,22 +270,22 @@ bool test_format( const int infd, std::string & file_type,
{ {
if( readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == bzip2_magic[1] && if( readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == bzip2_magic[1] &&
readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == bzip2_magic[2] ) readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == bzip2_magic[2] )
{ file_type = "bzip2"; { *magic_datap = bzip2_magic; *magic_sizep = bzip2_magic_size;
*magic_datap = bzip2_magic; *magic_sizep = bzip2_magic_size; } return decompressor_names[fmt_bz2]; }
} }
else if( buf[0] == gzip_magic[0] ) else if( buf[0] == gzip_magic[0] )
{ {
if( readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == gzip_magic[1] ) if( readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == gzip_magic[1] )
{ file_type = "gzip"; { *magic_datap = gzip_magic; *magic_sizep = gzip_magic_size;
*magic_datap = gzip_magic; *magic_sizep = gzip_magic_size; } return decompressor_names[fmt_gz]; }
} }
else if( buf[0] == lzip_magic[0] ) else if( buf[0] == lzip_magic[0] )
{ {
if( readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == lzip_magic[1] && if( readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == lzip_magic[1] &&
readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == lzip_magic[2] && readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == lzip_magic[2] &&
readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == lzip_magic[3] ) readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == lzip_magic[3] )
{ file_type = "lzip"; { *magic_datap = lzip_magic; *magic_sizep = lzip_magic_size;
*magic_datap = lzip_magic; *magic_sizep = lzip_magic_size; } return decompressor_names[fmt_lz]; }
} }
else if( buf[0] == xz_magic[0] ) else if( buf[0] == xz_magic[0] )
{ {
@ -290,18 +293,12 @@ bool test_format( const int infd, std::string & file_type,
readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == xz_magic[2] && readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == xz_magic[2] &&
readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == xz_magic[3] && readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == xz_magic[3] &&
readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == xz_magic[4] ) readblock( infd, &buf[i], 1 ) == 1 && buf[i++] == xz_magic[4] )
{ file_type = "xz"; { *magic_datap = xz_magic; *magic_sizep = xz_magic_size;
*magic_datap = xz_magic; *magic_sizep = xz_magic_size; } return decompressor_names[fmt_xz]; }
} }
} }
if( file_type.size() ) return true;
for( int j = 0; j < i; ++j )
{
file_type += xdigit( buf[j] >> 4 );
file_type += xdigit( buf[j] & 0x0F );
}
*magic_datap = buf; *magic_sizep = i; *magic_datap = buf; *magic_sizep = i;
return false; return 0;
} }
@ -314,12 +311,40 @@ int wait_for_child( const pid_t pid, const char * const name,
if( errno != EINTR ) if( errno != EINTR )
{ {
if( verbosity >= 0 ) if( verbosity >= 0 )
std::fprintf( stderr, "%s: Error waiting termination of `%s': %s.\n", std::fprintf( stderr, "%s: Error waiting termination of '%s': %s.\n",
util_name, name, std::strerror( errno ) ); util_name, name, std::strerror( errno ) );
_exit( eretval ); _exit( eretval );
} }
} }
if( WIFEXITED( status ) )
{
const int tmp = WEXITSTATUS( status );
if( eretval == 1 && tmp == 1 ) return 2; // for ztest
return tmp;
}
return eretval;
}
int child_status( const pid_t pid, const char * const name,
const int eretval )
{
int status;
while( true )
{
const int tmp = waitpid( pid, &status, WNOHANG );
if( tmp == -1 && errno != EINTR )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Error checking status of '%s': %s.\n",
util_name, name, std::strerror( errno ) );
_exit( eretval );
}
if( tmp == 0 ) return -1; // child not terminated
if( tmp == pid ) break; // child terminated
}
if( WIFEXITED( status ) ) return WEXITSTATUS( status ); if( WIFEXITED( status ) ) return WEXITSTATUS( status );
return eretval; return eretval;
} }

View file

@ -1,5 +1,5 @@
/* Zutils - Utilities dealing with compressed files /* Zutils - Utilities dealing with compressed files
Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz. Copyright (C) 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -17,42 +17,66 @@
const char * const Program_name = "Zutils"; const char * const Program_name = "Zutils";
const char * const program_name = "zutils"; const char * const program_name = "zutils";
const char * const program_year = "2011"; const char * const program_year = "2013";
extern const char * invocation_name; extern const char * invocation_name;
extern const char * util_name; extern const char * util_name;
extern int verbosity; extern int verbosity;
const char * const simple_extensions[] = { ".gz", ".lz", ".bz2", ".xz", 0 }; enum { fmt_bz2, fmt_gz, fmt_lz, fmt_xz, num_formats };
const char * const format_names[num_formats] = { "bz2", "gz", "lz", "xz" };
const char * const simple_extensions[num_formats] =
{ ".bz2", ".gz", ".lz", ".xz" };
const char * const decompressor_names[num_formats] =
{ "bzip2", "gzip", "lzip", "xz" };
const int8_t format_order[num_formats] =
{ fmt_gz, fmt_lz, fmt_bz2, fmt_xz }; // search order
enum { bzip2_magic_size = 3 }; // first magic byte must be different among formats
enum { gzip_magic_size = 2 }; enum { bzip2_magic_size = 3,
enum { lzip_magic_size = 4 }; gzip_magic_size = 2,
enum { xz_magic_size = 5 }; lzip_magic_size = 4,
const uint8_t bzip2_magic[bzip2_magic_size] = { 'B', 'Z', 'h' }; xz_magic_size = 5 };
const uint8_t gzip_magic[ gzip_magic_size] = { '\x1F', '\x8B' }; const uint8_t bzip2_magic[bzip2_magic_size] =
const uint8_t lzip_magic[ lzip_magic_size] = { 'L', 'Z', 'I', 'P' }; { 0x42, 0x5A, 0x68 }; // "BZh"
const uint8_t xz_magic[ xz_magic_size] = { '\xFD', '7', 'z', 'X', 'Z' }; const uint8_t gzip_magic[gzip_magic_size] =
{ 0x1F, 0x8B };
const uint8_t lzip_magic[lzip_magic_size] =
{ 0x4C, 0x5A, 0x49, 0x50 }; // "LZIP"
const uint8_t xz_magic[xz_magic_size] =
{ 0xFD, 0x37, 0x7A, 0x58, 0x5A }; // 0xFD, "7zXZ"
int readblock( const int fd, uint8_t * const buf, const int size ) throw(); int get_format_type( const std::string & arg );
int writeblock( const int fd, const uint8_t * const buf, const int size ) throw();
int readblock( const int fd, uint8_t * const buf, const int size );
int writeblock( const int fd, const uint8_t * const buf, const int size );
bool feed_data( const int infd, const int outfd, bool feed_data( const int infd, const int outfd,
const uint8_t * magic_data, const int magic_size ); const uint8_t * magic_data, const int magic_size );
bool set_data_feeder( int * const infdp, pid_t * const pidp ); bool set_data_feeder( int * const infdp, pid_t * const pidp,
const int format_type );
void show_help_addr() throw(); void show_help_addr();
void show_version( const char * const Util_name = 0 ) throw(); void show_version( const char * const Util_name = 0 );
void show_error( const char * const msg, const int errcode = 0, void show_error( const char * const msg, const int errcode = 0,
const bool help = false ) throw(); const bool help = false );
void show_error2( const char * const msg, const char * const name ) throw(); void show_error2( const char * const msg, const char * const name );
void show_close_error( const char * const prog_name ) throw(); void show_close_error( const char * const prog_name );
void show_exec_error( const char * const prog_name ) throw(); void show_exec_error( const char * const prog_name );
void show_fork_error( const char * const prog_name ) throw(); void show_fork_error( const char * const prog_name );
void internal_error( const char * const msg ); void internal_error( const char * const msg );
bool test_format( const int infd, std::string & file_type, const char * test_format( const int infd,
const uint8_t ** magic_data, int * magic_sizep ); const uint8_t ** const magic_datap,
int * const magic_sizep );
// Returns exit status of child process 'pid', or 'eretval' in case of error.
//
int wait_for_child( const pid_t pid, const char * const name, int wait_for_child( const pid_t pid, const char * const name,
const int eretval = 2 ); const int eretval = 2 );
// Returns -1 if child not terminated, 'eretval' in case of error, or
// exit status of child process 'pid'.
//
int child_status( const pid_t pid, const char * const name,
const int eretval = 2 );