diff options
Diffstat (limited to '')
-rw-r--r-- | ChangeLog | 52 | ||||
-rw-r--r-- | INSTALL | 41 | ||||
-rw-r--r-- | Makefile.in | 23 | ||||
-rw-r--r-- | NEWS | 28 | ||||
-rw-r--r-- | README | 15 | ||||
-rw-r--r-- | arg_parser.cc | 23 | ||||
-rw-r--r-- | arg_parser.h | 29 | ||||
-rwxr-xr-x | configure | 101 | ||||
-rw-r--r-- | doc/zcat.1 | 26 | ||||
-rw-r--r-- | doc/zcmp.1 | 10 | ||||
-rw-r--r-- | doc/zdiff.1 | 10 | ||||
-rw-r--r-- | doc/zgrep.1 | 14 | ||||
-rw-r--r-- | doc/ztest.1 | 19 | ||||
-rw-r--r-- | doc/zutils.info | 142 | ||||
-rw-r--r-- | doc/zutils.texinfo | 114 | ||||
-rw-r--r-- | main.cc | 160 | ||||
-rwxr-xr-x | testsuite/check.sh | 110 | ||||
-rw-r--r-- | zcat.cc | 71 | ||||
-rw-r--r-- | zcmp.cc | 197 | ||||
-rw-r--r-- | zcmpdiff.cc | 86 | ||||
-rw-r--r-- | zdiff.cc | 217 | ||||
-rw-r--r-- | zgrep.cc | 103 | ||||
-rw-r--r-- | ztest.cc | 78 | ||||
-rw-r--r-- | zutils.cc | 151 | ||||
-rw-r--r-- | zutils.h | 70 |
25 files changed, 1116 insertions, 774 deletions
@@ -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> * 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. * zgrep.cc: Fixed deadlock when binary file matches. 2010-11-15 Antonio Diaz Diaz <ant_diaz@teleline.es> * 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++. * zcmp.cc: New file implementing zcmp+cmp functionality in C++. * doc/zcmp.1: New file. @@ -20,7 +60,7 @@ documentation because egrep and fgrep are deprecated. * ztest.cc: New file implementing ztest functionality in C++. * 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 Richard Stallman. Now environment variables affect configure. @@ -28,14 +68,14 @@ * Version 0.7 released. * 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> * Version 0.6 released. * zcat.in, zgrep.in: Removed again default compressor. Format of 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> @@ -66,7 +106,7 @@ * 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, but just in case, you have unlimited permission to copy, distribute and @@ -1,10 +1,19 @@ Requirements ------------ 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. 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 --------- @@ -18,7 +27,7 @@ This creates the directory ./zutils[version] containing the source from the main archive. 2. Change to zutils directory and run configure. - (Try `configure --help' for usage instructions). + (Try 'configure --help' for usage instructions). cd zutils[version] ./configure @@ -27,30 +36,34 @@ the main archive. 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. + 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 ----------- 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 -as GNU `make'. `cd' to the directory where you want the object files -and executables to go and run the `configure' script. `configure' -automatically checks for the source code in `.', in `..' and in the -directory that `configure' is in. - -`configure' recognizes the option `--srcdir=DIR' to control where to -look for the sources. Usually `configure' can determine that directory +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 +and executables to go and run the 'configure' script. 'configure' +automatically checks for the source code in '.', in '..' and in the +directory that 'configure' is in. + +'configure' recognizes the option '--srcdir=DIR' to control where to +look for the sources. Usually 'configure' can determine that directory 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. -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, distribute and modify it. diff --git a/Makefile.in b/Makefile.in index cb987ca..bb47d57 100644 --- a/Makefile.in +++ b/Makefile.in @@ -13,8 +13,8 @@ zdiff_objs = arg_parser.o zutils.o zdiff.o scripts = zcat zegrep zfgrep zgrep ztest -.PHONY : all install install-info install-man install-strip \ - uninstall uninstall-info uninstall-man \ +.PHONY : all install install-bin install-info install-man install-strip \ + uninstall uninstall-bin uninstall-info uninstall-man \ doc info man check dist clean distclean all : $(progname) zcmp zdiff $(scripts) @@ -67,10 +67,11 @@ $(objs) : Makefile $(scripts) : Makefile arg_parser.o : arg_parser.h main.o : arg_parser.h zutils.h zcat.cc zgrep.cc ztest.cc -zcmp.o : arg_parser.h zutils.h Makefile -zdiff.o : arg_parser.h zutils.h Makefile +zcmp.o : arg_parser.h zutils.h zcmpdiff.cc Makefile +zdiff.o : arg_parser.h zutils.h zcmpdiff.cc Makefile zutils.o : zutils.h + doc : info man info : $(VPATH)/doc/$(pkgname).info @@ -107,7 +108,9 @@ Makefile : $(VPATH)/configure $(VPATH)/Makefile.in check : all @$(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 $(INSTALL_PROGRAM) ./$(progname) "$(DESTDIR)$(bindir)/$(progname)" $(INSTALL_SCRIPT) ./zcat "$(DESTDIR)$(bindir)/zcat" @@ -121,7 +124,7 @@ install : all install-info install-man install-info : if [ ! -d "$(DESTDIR)$(infodir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(infodir)" ; fi $(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 : 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/zdiff.1 "$(DESTDIR)$(mandir)/man1/zdiff.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-strip : all $(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)/zcat" -rm -f "$(DESTDIR)$(bindir)/zcmp" @@ -154,8 +157,6 @@ uninstall-man : -rm -f "$(DESTDIR)$(mandir)/man1/zcat.1" -rm -f "$(DESTDIR)$(mandir)/man1/zcmp.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/ztest.1" @@ -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 -been fixed. +A deadlock in zcmp, which happens when the "-n" option is used, has been +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. @@ -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, including standard input, is compressed, its uncompressed content is used. Compressed files are decompressed on the fly; no temporary files -are created. 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. +are created. + +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 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, distribute and modify it. diff --git a/arg_parser.cc b/arg_parser.cc index cc9f87d..a28d2ba 100644 --- a/arg_parser.cc +++ b/arg_parser.cc @@ -1,5 +1,6 @@ -/* Arg_parser - A POSIX/GNU command line argument parser. (C++ version) - Copyright (C) 2006, 2007, 2008, 2009, 2010 Antonio Diaz Diaz. +/* Arg_parser - POSIX/GNU command line argument parser. (C++ version) + 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 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, const Option options[], int & argind ) { - unsigned int len; + unsigned len; int index = -1; 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. 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 { 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 ) { - error_ = "option `"; error_ += opt; error_ += "' is ambiguous"; + error_ = "option '"; error_ += opt; error_ += "' is ambiguous"; return false; } if( index < 0 ) // nothing found { - error_ = "unrecognized option `"; error_ += opt; error_ += '\''; + error_ = "unrecognized option '"; error_ += opt; error_ += '\''; return false; } ++argind; 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 ) { - error_ = "option `--"; error_ += options[index].name; + error_ = "option '--"; error_ += options[index].name; error_ += "' doesn't allow an argument"; return false; } 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"; return false; } @@ -90,7 +91,7 @@ bool Arg_parser::parse_long_option( const char * const opt, const char * const a { if( !arg || !arg[0] ) { - error_ = "option `--"; error_ += options[index].name; + error_ = "option '--"; error_ += options[index].name; error_ += "' requires an argument"; return false; } @@ -177,7 +178,7 @@ Arg_parser::Arg_parser( const int argc, const char * const argv[], if( error_.size() ) data.clear(); 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] ); } while( argind < argc ) { data.push_back( Record() ); data.back().argument = argv[argind++]; } diff --git a/arg_parser.h b/arg_parser.h index da1cc94..5248cb1 100644 --- a/arg_parser.h +++ b/arg_parser.h @@ -1,5 +1,6 @@ -/* Arg_parser - A POSIX/GNU command line argument parser. (C++ version) - Copyright (C) 2006, 2007, 2008, 2009, 2010 Antonio Diaz Diaz. +/* Arg_parser - POSIX/GNU command line argument parser. (C++ version) + 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 it under the terms of the GNU General Public License as published by @@ -25,12 +26,12 @@ 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. - 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 option. A code value outside the unsigned char range means a long-only option. @@ -39,13 +40,13 @@ were specified before all the non-option arguments for the purposes of parsing, even if the user of your program intermixed option and 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. - The syntax for optional option arguments is `-<short_option><argument>' - (without whitespace), or `--<long_option>=<argument>'. + The syntax for optional option arguments is '-<short_option><argument>' + (without whitespace), or '--<long_option>=<argument>'. */ class Arg_parser @@ -65,7 +66,7 @@ private: { int code; std::string argument; - Record( const int c = 0 ) : code( c ) {} + explicit Record( const int c = 0 ) : code( c ) {} }; std::string error_; @@ -84,20 +85,20 @@ public: Arg_parser( const char * const opt, const char * const arg, 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) - int arguments() const throw() { return data.size(); } + int arguments() const { return data.size(); } // If code( i ) is 0, argument( i ) is a non-option. // 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; 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; else return error_; @@ -1,6 +1,6 @@ #! /bin/sh # 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 # to copy, distribute and modify it. @@ -8,7 +8,7 @@ args= no_create= pkgname=zutils -pkgversion=0.9 +pkgversion=1.0-rc6 progname=zutils srctrigger=zutils.h @@ -19,18 +19,23 @@ srcdir= prefix=/usr/local exec_prefix='$(prefix)' bindir='$(exec_prefix)/bin' -datadir='$(prefix)/share' -infodir='$(datadir)/info' -mandir='$(datadir)/man' -sysconfdir='$(prefix)/etc' +datarootdir='$(prefix)/share' +infodir='$(datarootdir)/info' +mandir='$(datarootdir)/man' +CXX=g++ +CPPFLAGS= +CXXFLAGS='-Wall -W -O2' +LDFLAGS= DIFF=diff GREP=grep -if [ -n "${CXX}" ] ; then args="${args} CXX=\"${CXX}\"" ; fi -if [ -n "${CPPFLAGS}" ] ; then args="${args} CPPFLAGS=\"${CPPFLAGS}\"" ; fi -if [ -n "${CXXFLAGS}" ] ; then args="${args} CXXFLAGS=\"${CXXFLAGS}\"" -else CXXFLAGS='-Wall -W -O2' ; fi -if [ -n "${LDFLAGS}" ] ; then args="${args} LDFLAGS=\"${LDFLAGS}\"" ; fi +# checking whether we are using GNU C++. +if [ ! -x /bin/g++ ] && + [ ! -x /usr/bin/g++ ] && + [ ! -x /usr/local/bin/g++ ] ; then + CXX=c++ + CXXFLAGS='-W -O2' +fi # Loop over all args while [ -n "$1" ] ; do @@ -44,12 +49,12 @@ while [ -n "$1" ] ; do # Split out the argument for options that take them case ${option} in - *=*) optarg=`echo ${option} | sed -e 's,^[^=]*=,,'` ;; + *=*) optarg=`echo ${option} | sed -e 's,^[^=]*=,,;s,/$,,'` ;; esac # Process the options case ${option} in - --help | --he* | -h) + --help | -h) echo "Usage: configure [options]" echo echo "Options: [defaults in brackets]" @@ -59,10 +64,9 @@ while [ -n "$1" ] ; do echo " --prefix=DIR install into DIR [${prefix}]" echo " --exec-prefix=DIR base directory for arch-dependent files [${exec_prefix}]" 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 " --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 " CPPFLAGS=OPTIONS command line options for the preprocessor [${CPPFLAGS}]" 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 exit 0 ;; - --version | --ve* | -V) + --version | -V) echo "Configure script for ${pkgname} version ${pkgversion}" exit 0 ;; - --srcdir* | --sr*) - srcdir=`echo ${optarg} | sed -e 's,/$,,'` ;; - --prefix* | --pr*) - prefix=`echo ${optarg} | sed -e 's,/$,,'` ;; - --exec-prefix* | --ex*) - exec_prefix=`echo ${optarg} | sed -e 's,/$,,'` ;; - --bindir* | --bi*) - bindir=`echo ${optarg} | sed -e 's,/$,,'` ;; - --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} ;; + --srcdir=*) srcdir=${optarg} ;; + --prefix=*) prefix=${optarg} ;; + --exec-prefix=*) exec_prefix=${optarg} ;; + --bindir=*) bindir=${optarg} ;; + --datarootdir=*) datarootdir=${optarg} ;; + --infodir=*) infodir=${optarg} ;; + --mandir=*) mandir=${optarg} ;; + --no-create) no_create=yes ;; + + CXX=*) CXX=${optarg} ;; CPPFLAGS=*) CPPFLAGS=${optarg} ;; CXXFLAGS=*) CXXFLAGS=${optarg} ;; - LDFLAGS=*) LDFLAGS=${optarg} ;; - DIFF=*) DIFF=${optarg} ;; - GREP=*) GREP=${optarg} ;; + LDFLAGS=*) LDFLAGS=${optarg} ;; + DIFF=*) DIFF=${optarg} ;; + GREP=*) GREP=${optarg} ;; --* | *=* | *-*-*) ;; *) @@ -111,14 +105,14 @@ done srcdirtext= if [ -z "${srcdir}" ] ; then srcdirtext="or . or .." ; srcdir=. - if [ ! -r ${srcdir}/${srctrigger} ] ; then srcdir=.. ; fi - if [ ! -r ${srcdir}/${srctrigger} ] ; then + if [ ! -r "${srcdir}/${srctrigger}" ] ; then srcdir=.. ; fi + if [ ! -r "${srcdir}/${srctrigger}" ] ; then ## the sed command below emulates the dirname command srcdir=`echo $0 | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` fi fi -if [ ! -r ${srcdir}/${srctrigger} ] ; then +if [ ! -r "${srcdir}/${srctrigger}" ] ; then exec 1>&2 echo echo "configure: Can't find sources in ${srcdir} ${srcdirtext}" @@ -127,18 +121,7 @@ if [ ! -r ${srcdir}/${srctrigger} ] ; then fi # Set srcdir to . if that's what it is. -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 +if [ "`pwd`" = "`cd "${srcdir}" ; pwd`" ] ; then srcdir=. ; fi echo if [ -z "${no_create}" ] ; then @@ -162,10 +145,9 @@ echo "VPATH = ${srcdir}" echo "prefix = ${prefix}" echo "exec_prefix = ${exec_prefix}" echo "bindir = ${bindir}" -echo "datadir = ${datadir}" +echo "datarootdir = ${datarootdir}" echo "infodir = ${infodir}" echo "mandir = ${mandir}" -echo "sysconfdir = ${sysconfdir}" echo "CXX = ${CXX}" echo "CPPFLAGS = ${CPPFLAGS}" echo "CXXFLAGS = ${CXXFLAGS}" @@ -175,7 +157,7 @@ echo "GREP = ${GREP}" rm -f Makefile cat > Makefile << EOF # 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 Makefile is free software: you have unlimited permission @@ -188,10 +170,9 @@ VPATH = ${srcdir} prefix = ${prefix} exec_prefix = ${exec_prefix} bindir = ${bindir} -datadir = ${datadir} +datarootdir = ${datarootdir} infodir = ${infodir} mandir = ${mandir} -sysconfdir = ${sysconfdir} CXX = ${CXX} CPPFLAGS = ${CPPFLAGS} CXXFLAGS = ${CXXFLAGS} @@ -199,6 +180,6 @@ LDFLAGS = ${LDFLAGS} DIFF = ${DIFF} GREP = ${GREP} EOF -cat ${srcdir}/Makefile.in >> Makefile +cat "${srcdir}/Makefile.in" >> Makefile echo "OK. Now you can run make." @@ -1,5 +1,5 @@ .\" 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 Zcat \- decompress and concatenate files to standard output .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 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 -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 standard output. Data read from standard input must be of the same type; -all uncompressed or all compressed with the same compressor. -The supported compressors are bzip2, gzip, lzip and xz. +all uncompressed or all in the same compression format. +.PP +The supported formats are bzip2, gzip, lzip and xz. .PP Exit status is 0 if no errors occurred, 1 otherwise. .SH OPTIONS @@ -26,16 +27,19 @@ display this help and exit output version information and exit .TP \fB\-A\fR, \fB\-\-show\-all\fR -equivalent to `\-vET' +equivalent to '\-vET' .TP \fB\-b\fR, \fB\-\-number\-nonblank\fR number nonblank output lines .TP \fB\-e\fR -equivalent to `\-vE' +equivalent to '\-vE' .TP \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 \fB\-n\fR, \fB\-\-number\fR number all output lines @@ -50,13 +54,13 @@ operate recursively on directories never more than one single blank line .TP \fB\-t\fR -equivalent to `\-vT' +equivalent to '\-vT' .TP \fB\-T\fR, \fB\-\-show\-tabs\fR -display TAB characters as `^I' +display TAB characters as '^I' .TP \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 \fB\-\-verbose\fR verbose mode (show error messages) @@ -65,7 +69,7 @@ Report bugs to zutils\-bug@nongnu.org .br Zutils home page: http://www.nongnu.org/zutils/zutils.html .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> .br This is free software: you are free to change and redistribute it. @@ -1,5 +1,5 @@ .\" 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 Zcmp \- decompress and compare two files byte by byte .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, its uncompressed content is used. Compressed files are uncompressed on 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 Compares <file1> to <file2>. If <file2> is omitted zcmp tries the following: @@ -35,6 +36,9 @@ output version information and exit \fB\-b\fR, \fB\-\-print\-bytes\fR print differing bytes .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>] ignore differences in the first <n> bytes .TP @@ -60,7 +64,7 @@ Report bugs to zutils\-bug@nongnu.org .br Zutils home page: http://www.nongnu.org/zutils/zutils.html .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> .br This is free software: you are free to change and redistribute it. diff --git a/doc/zdiff.1 b/doc/zdiff.1 index dc4ad49..65c9a84 100644 --- a/doc/zdiff.1 +++ b/doc/zdiff.1 @@ -1,5 +1,5 @@ .\" 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 Zdiff \- decompress and compare two files line by line .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 the diff program and has the limitation that messages from diff refer to 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 Compares <file1> to <file2>. If <file2> is omitted zdiff tries the following: @@ -53,6 +54,9 @@ try hard to find a smaller set of changes \fB\-E\fR, \fB\-\-ignore\-tab\-expansion\fR ignore changes due to tab expansion .TP +\fB\-\-format\fR=\fI[\fR<fmt1>][,<fmt2>] +force given formats (bz2, gz, lz, xz) +.TP \fB\-i\fR, \fB\-\-ignore\-case\fR ignore case differences in file contents .TP @@ -87,7 +91,7 @@ Report bugs to zutils\-bug@nongnu.org .br Zutils home page: http://www.nongnu.org/zutils/zutils.html .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> .br This is free software: you are free to change and redistribute it. diff --git a/doc/zgrep.1 b/doc/zgrep.1 index 7bdfb1a..a56ace6 100644 --- a/doc/zgrep.1 +++ b/doc/zgrep.1 @@ -1,5 +1,5 @@ .\" 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 Zgrep \- search compressed files for a regular expression .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 does not exist, and its name does not end with one of the known 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 must be of the same type; all uncompressed or all -compressed with the same compressor. -The supported compressors are bzip2, gzip, lzip and xz. +in the same compression format. +.PP +The supported formats are bzip2, gzip, lzip and xz. .PP Exit status is 0 if match, 1 if no match, 2 if trouble. .SH OPTIONS @@ -56,6 +57,9 @@ obtain patterns from <file> \fB\-F\fR, \fB\-\-fixed\-strings\fR <pattern> is a set of newline\-separated strings .TP +\fB\-\-format=\fR<fmt> +force given format (bz2, gz, lz, xz) +.TP \fB\-h\fR, \fB\-\-no\-filename\fR suppress the prefixing filename on output .TP @@ -108,7 +112,7 @@ Report bugs to zutils\-bug@nongnu.org .br Zutils home page: http://www.nongnu.org/zutils/zutils.html .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> .br This is free software: you are free to change and redistribute it. diff --git a/doc/ztest.1 b/doc/ztest.1 index 616420b..74d7102 100644 --- a/doc/ztest.1 +++ b/doc/ztest.1 @@ -1,5 +1,5 @@ .\" 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 Ztest \- verify integrity of compressed files .SH SYNOPSIS @@ -9,10 +9,16 @@ Ztest \- verify integrity of compressed files Ztest verifies the integrity of the specified compressed files. Non\-compressed files are ignored. If no files are specified, the integrity of compressed data read from standard input is verified. Data -read from standard input must be all compressed with the same compressor. -The supported compressors are bzip2, gzip, lzip and xz. +read from standard input must be all in the same compression format. .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 .TP \fB\-h\fR, \fB\-\-help\fR @@ -21,6 +27,9 @@ display this help and exit \fB\-V\fR, \fB\-\-version\fR output version information and exit .TP +\fB\-\-format=\fR<fmt> +force given format (bz2, gz, lz, xz) +.TP \fB\-q\fR, \fB\-\-quiet\fR suppress all messages .TP @@ -34,7 +43,7 @@ Report bugs to zutils\-bug@nongnu.org .br Zutils home page: http://www.nongnu.org/zutils/zutils.html .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> .br This is free software: you are free to change and redistribute it. diff --git a/doc/zutils.info b/doc/zutils.info index 24f9bf4..9c27308 100644 --- a/doc/zutils.info +++ b/doc/zutils.info @@ -12,7 +12,7 @@ File: zutils.info, Node: Top, Next: Introduction, Up: (dir) 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: @@ -26,7 +26,7 @@ This manual is for Zutils (version 0.9, 11 January 2011). * 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 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, including standard input, is compressed, its uncompressed content is used. Compressed files are decompressed on the fly; no temporary files -are created. 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. +are created. + + 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 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 @@ -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 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 -corresponding to the supported compressors. If no files are specified, -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; -all uncompressed or all compressed with the same compressor. +corresponding to the supported formats. + + If no files are specified, 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; all uncompressed or all in the +same compression format. The format for running zcat is: @@ -111,6 +118,11 @@ Exit status is 0 if no errors occurred, 1 otherwise. `--show-ends' 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' `--number' 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] -Compares FILE1 to FILE2. If FILE2 is omitted zcmp tries the following: -If FILE1 is compressed, compares FILE1 to the file with the -corresponding decompressed file name (removes the extension from FILE1). -If FILE1 is not compressed, compares FILE1 to the uncompressed contents -of FILE1.[bz2|gz|lz|xz] (the first one that is found). -If no suitable file is found, compares FILE1 to data read from standard -input. +This compares FILE1 to FILE2. If FILE2 is omitted zcmp tries the +following: + + 1. If FILE1 is compressed, compares FILE1 to the file with the + corresponding decompressed file name (removes the extension from + FILE1). + + 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 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 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' `--ignore-initial=SIZE' 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] -Compares FILE1 to FILE2. If FILE2 is omitted zdiff tries the following: -If FILE1 is compressed, compares FILE1 to the file with the -corresponding decompressed file name (removes the extension from FILE1). -If FILE1 is not compressed, compares FILE1 to the uncompressed contents -of FILE1.[bz2|gz|lz|xz] (the first one that is found). -If no suitable file is found, compares FILE1 to data read from standard -input. +This compares FILE1 to FILE2. If FILE2 is omitted zdiff tries the +following: + + 1. If FILE1 is compressed, compares FILE1 to the file with the + corresponding decompressed file name (removes the extension from + FILE1). + + 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 differences were found, and 2 means trouble. @@ -279,6 +309,14 @@ differences were found, and 2 means trouble. `--ignore-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' `--ignore-case' 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 does not exist, and its name does not end with one of the known extensions, zgrep tries the compressed file names corresponding to the -supported compressors. If no files are specified, data is 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 -compressed with the same compressor. +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 +must be of the same type; all uncompressed or all in the same +compression format. The format for running zgrep is: @@ -387,9 +427,15 @@ matches were found, and 2 means trouble. `--fixed-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' `--no-filename' - Suppress the prefixing filename on output. + Suppress the prefixing of filenames on output when multiple files + are searched. `-H' `--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. Non-compressed files are ignored. If no files are specified, the 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: ztest [OPTIONS] [FILES] -The exit status is 0 if all files verify OK, 1 for environmental -problems (file not found, invalid flags, I/O errors, etc), 2 to indicate -a corrupt or invalid input file. +The 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. Ztest supports the following options: @@ -480,6 +528,11 @@ a corrupt or invalid input file. `--version' 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' `--quiet' Quiet operation. Suppress all messages. @@ -531,13 +584,18 @@ Concept Index Tag Table: Node: Top224 -Node: Introduction992 -Node: Zcat2447 -Node: Zcmp4331 -Node: Zdiff6388 -Node: Zgrep8625 -Node: Ztest11392 -Node: Problems12475 -Node: Concept Index13004 +Node: Introduction1006 +Node: Zcat2593 +Node: Zcmp4679 +Node: Zdiff7165 +Node: Zgrep9831 +Node: Ztest12842 +Node: Problems14246 +Node: Concept Index14775 End Tag Table + + +Local Variables: +coding: iso-8859-15 +End: diff --git a/doc/zutils.texinfo b/doc/zutils.texinfo index b847830..af1fd90 100644 --- a/doc/zutils.texinfo +++ b/doc/zutils.texinfo @@ -1,12 +1,13 @@ \input texinfo @c -*-texinfo-*- @c %**start of header @setfilename zutils.info +@documentencoding ISO-8859-15 @settitle Zutils Manual @finalout @c %**end of header -@set UPDATED 11 January 2011 -@set VERSION 0.9 +@set UPDATED 15 March 2013 +@set VERSION 1.0-rc6 @dircategory Data Compression @direntry @@ -45,7 +46,8 @@ This manual is for Zutils (version @value{VERSION}, @value{UPDATED}). @end menu @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 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, including standard input, is compressed, its uncompressed content is used. Compressed files are decompressed on the fly; no temporary files -are created. 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. +are created. + +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 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 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 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 -corresponding to the supported compressors. If no files are specified, -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; -all uncompressed or all compressed with the same compressor. +corresponding to the supported formats. + +If no files are specified, 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; all uncompressed or all in the +same compression format. The format for running zcat is: @@ -135,6 +144,11 @@ Equivalent to @samp{-vE}. @itemx --show-ends 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 @itemx --number 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 @noindent -Compares @var{file1} to @var{file2}. If @var{file2} is omitted zcmp -tries the following:@* +This compares @var{file1} to @var{file2}. If @var{file2} is omitted zcmp +tries the following: + +@enumerate +@item If @var{file1} is compressed, compares @var{file1} to the file with the corresponding decompressed file name (removes the extension from -@var{file1}).@* +@var{file1}). +@item If @var{file1} is not compressed, compares @var{file1} to the 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 standard input. +@end enumerate @noindent 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 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} @itemx --ignore-initial=@var{size} 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 @noindent -Compares @var{file1} to @var{file2}. If @var{file2} is omitted zdiff -tries the following:@* +This compares @var{file1} to @var{file2}. If @var{file2} is omitted +zdiff tries the following: + +@enumerate +@item If @var{file1} is compressed, compares @var{file1} to the file with the corresponding decompressed file name (removes the extension from -@var{file1}).@* +@var{file1}). +@item If @var{file1} is not compressed, compares @var{file1} to the 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 standard input. +@end enumerate @noindent 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 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 @itemx --ignore-case 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 does not exist, and its name does not end with one of the known extensions, zgrep tries the compressed file names corresponding to the -supported compressors. If no files are specified, data is 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 -compressed with the same compressor. +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 +must be of the same type; all uncompressed or all in the same +compression format. The format for running zgrep is: @@ -430,9 +474,15 @@ Obtain patterns from @var{file}, one per line. @itemx --fixed-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 @itemx --no-filename -Suppress the prefixing filename on output. +Suppress the prefixing of filenames on output when multiple files are +searched. @item -H @itemx --with-filename @@ -502,7 +552,10 @@ Match only whole lines. Ztest verifies the integrity of the specified compressed files. Non-compressed files are ignored. If no files are specified, the 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: @@ -511,9 +564,9 @@ ztest [@var{options}] [@var{files}] @end example @noindent -The exit status is 0 if all files verify OK, 1 for environmental -problems (file not found, invalid flags, I/O errors, etc), 2 to indicate -a corrupt or invalid input file. +The 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. Ztest supports the following options: @@ -526,6 +579,11 @@ Print an informative help message describing the options and exit. @itemx --version 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 @itemx --quiet Quiet operation. Suppress all messages. @@ -1,5 +1,5 @@ /* 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 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 }; -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( "compressed and non-compressed files transparently. If any given file,\n" ); - std::printf( "including standard input, is compressed, its uncompressed content is used.\n" ); - std::printf( "The supported compressors are bzip2, gzip, lzip and xz.\n" ); - std::printf( "\nUsage: %s <operation> [options] [files]\n", invocation_name ); - std::printf( "\nTry `%s <operation> --help' for more specific help.\n", invocation_name ); - std::printf( "\nOperations:\n" ); - std::printf( " -h, --help display this help and exit\n" ); - std::printf( " -V, --version output version information and exit\n" ); - std::printf( " --zcat zcat operation\n" ); - std::printf( " --zgrep zgrep operation\n" ); - std::printf( " --ztest ztest operation\n" ); + std::printf( "Zutils is a collection of utilities able to deal with any combination of\n" + "compressed and non-compressed files transparently. If any given file,\n" + "including standard input, is compressed, its uncompressed content is used.\n" + "\nThe supported formats are bzip2, gzip, lzip and xz.\n" + "\nUsage: %s <operation> [options] [files]\n", invocation_name ); + std::printf( "\nTry '%s <operation> --help' for more specific help.\n", invocation_name ); + std::printf( "\nOperations:\n" + " -h, --help display this help and exit\n" + " -V, --version output version information and exit\n" + " --zcat zcat operation\n" + " --zgrep zgrep operation\n" + " --ztest ztest operation\n" ); 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] ); 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 ); if( infd < 0 ) { - if( ( program_mode == m_zcat || program_mode == m_zgrep ) - && simple_extension_index( input_filename ) < 0 ) + if( search && ( program_mode == m_zcat || program_mode == m_zgrep ) && + 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 ); 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[] ) { - 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; int infd = -1; + int format_type = -1; Mode program_mode = m_none; bool recursive = false; std::string input_filename; @@ -147,52 +151,55 @@ int main( const int argc, const char * const argv[] ) { 'T', "show-tabs", Arg_parser::no }, // cat { 'v', "show-nonprinting", Arg_parser::no }, // cat { 'V', "version", Arg_parser::no }, + { format_opt, "format", Arg_parser::yes }, { verbose_opt, "verbose", Arg_parser::no }, { zcat_opt, "zcat", Arg_parser::no }, { 0 , 0, Arg_parser::no } }; const Arg_parser::Option m_zgrep_options[] = { - { 'a', "text", Arg_parser::no }, // grep GNU - { 'A', "after-context", Arg_parser::yes }, // grep GNU - { 'b', "byte-offset", Arg_parser::no }, // grep GNU - { 'B', "before-context", Arg_parser::yes }, // grep GNU - { 'c', "count", Arg_parser::no }, // grep - { 'C', "context", Arg_parser::yes }, // grep GNU - { 'e', "regexp", Arg_parser::yes }, // grep - { 'E', "extended-regexp", Arg_parser::no }, // grep - { 'f', "file ", Arg_parser::yes }, // grep - { 'F', "fixed-strings", Arg_parser::no }, // grep - { 'h', "no-filename", Arg_parser::no }, // grep GNU - { 'H', "with-filename", Arg_parser::no }, // grep GNU - { 'i', "ignore-case", Arg_parser::no }, // grep - { 'I', 0, Arg_parser::no }, // grep GNU - { 'l', "files-with-matches", Arg_parser::no }, // grep - { 'L', "files-without-match", Arg_parser::no }, // grep GNU - { 'm', "max-count", Arg_parser::yes }, // grep GNU - { 'n', "line-number", Arg_parser::no }, // grep - { 'o', "only-matching", Arg_parser::no }, // grep - { 'q', "quiet", Arg_parser::no }, - { 'r', "recursive", Arg_parser::no }, - { 's', "no-messages", Arg_parser::no }, // grep - { 'v', "invert-match", Arg_parser::no }, // grep - { 'V', "version", Arg_parser::no }, - { 'w', "word-regexp", Arg_parser::no }, // grep GNU - { 'x', "line-regexp", Arg_parser::no }, // grep - { help_opt, "help", Arg_parser::no }, - { verbose_opt, "verbose", Arg_parser::no }, - { zgrep_opt, "zgrep", Arg_parser::no }, - { 0 , 0, Arg_parser::no } }; + { 'a', "text", Arg_parser::no }, // grep GNU + { 'A', "after-context", Arg_parser::yes }, // grep GNU + { 'b', "byte-offset", Arg_parser::no }, // grep GNU + { 'B', "before-context", Arg_parser::yes }, // grep GNU + { 'c', "count", Arg_parser::no }, // grep + { 'C', "context", Arg_parser::yes }, // grep GNU + { 'e', "regexp", Arg_parser::yes }, // grep + { 'E', "extended-regexp", Arg_parser::no }, // grep + { 'f', "file ", Arg_parser::yes }, // grep + { 'F', "fixed-strings", Arg_parser::no }, // grep + { 'h', "no-filename", Arg_parser::no }, // grep GNU + { 'H', "with-filename", Arg_parser::no }, // grep GNU + { 'i', "ignore-case", Arg_parser::no }, // grep + { 'I', 0, Arg_parser::no }, // grep GNU + { 'l', "files-with-matches", Arg_parser::no }, // grep + { 'L', "files-without-match", Arg_parser::no }, // grep GNU + { 'm', "max-count", Arg_parser::yes }, // grep GNU + { 'n', "line-number", Arg_parser::no }, // grep + { 'o', "only-matching", Arg_parser::no }, // grep + { 'q', "quiet", Arg_parser::no }, + { 'r', "recursive", Arg_parser::no }, + { 's', "no-messages", Arg_parser::no }, // grep + { 'v', "invert-match", Arg_parser::no }, // grep + { 'V', "version", Arg_parser::no }, + { 'w', "word-regexp", Arg_parser::no }, // grep GNU + { 'x', "line-regexp", Arg_parser::no }, // grep + { format_opt, "format", Arg_parser::yes }, + { help_opt, "help", Arg_parser::no }, + { verbose_opt, "verbose", Arg_parser::no }, + { zgrep_opt, "zgrep", Arg_parser::no }, + { 0 , 0, Arg_parser::no } }; const Arg_parser::Option m_ztest_options[] = { - { 'h', "help", Arg_parser::no }, - { 'q', "quiet", Arg_parser::no }, - { 'r', "recursive", Arg_parser::no }, - { 'v', "verbose", Arg_parser::no }, - { 'V', "version", Arg_parser::no }, - { ztest_opt, "ztest", Arg_parser::no }, - { 0 , 0, Arg_parser::no } }; + { 'h', "help", Arg_parser::no }, + { 'q', "quiet", Arg_parser::no }, + { 'r', "recursive", Arg_parser::no }, + { 'v', "verbose", Arg_parser::no }, + { 'V', "version", Arg_parser::no }, + { format_opt, "format", Arg_parser::yes }, + { ztest_opt, "ztest", Arg_parser::no }, + { 0 , 0, Arg_parser::no } }; { // parse operation const Arg_parser::Option operations[] = @@ -226,8 +233,8 @@ int main( const int argc, const char * const argv[] ) } #if defined(__MSVCRT__) || defined(__OS2__) - _setmode( STDIN_FILENO, O_BINARY ); - _setmode( STDOUT_FILENO, O_BINARY ); + setmode( STDIN_FILENO, O_BINARY ); + setmode( STDOUT_FILENO, O_BINARY ); #endif 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 ); if( parser.error().size() ) // bad option { 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 grep_show_name = -1; bool grep_list = false; - bool grep_show_name = true; bool grep_pattern_found = false; 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; } else break; // no more options } + if( code == format_opt ) + { + format_type = get_format_type( arg ); continue; + } switch( program_mode ) { - case m_none: internal_error( "invalid operation" ); + case m_none: internal_error( "invalid operation" ); break; case m_zcat: switch( code ) { @@ -339,17 +350,16 @@ int main( const int argc, const char * const argv[] ) } // end process options 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 ) - { - if( parser.argument( argind ) != "-" ) filenames_given = true; filenames.push_back( parser.argument( argind ) ); - } 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 ); while( !filenames.empty() ) { @@ -386,7 +396,7 @@ int main( const int argc, const char * const argv[] ) 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; } } @@ -396,18 +406,18 @@ int main( const int argc, const char * const argv[] ) case m_none: break; case m_zcat: - tmp = cat( infd, input_filename, cat_options ); + tmp = cat( infd, format_type, input_filename, cat_options ); break; case m_zgrep: if( infd == STDIN_FILENO ) - tmp = zgrep_stdin( infd, grep_args ); - else tmp = zgrep_file( infd, input_filename, grep_args, + tmp = zgrep_stdin( infd, format_type, grep_args ); + else tmp = zgrep_file( infd, format_type, input_filename, grep_args, grep_list, grep_show_name ); break; case m_ztest: if( infd == STDIN_FILENO ) - tmp = ztest_stdin( infd, ztest_args ); - else tmp = ztest_file( infd, input_filename, ztest_args ); + tmp = ztest_stdin( infd, format_type, ztest_args ); + else tmp = ztest_file( infd, format_type, input_filename, ztest_args ); break; } if( program_mode == m_zgrep ) diff --git a/testsuite/check.sh b/testsuite/check.sh index 61058ab..85217fc 100755 --- a/testsuite/check.sh +++ b/testsuite/check.sh @@ -1,6 +1,6 @@ #! /bin/sh # 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 # to copy, distribute and modify it. @@ -18,8 +18,8 @@ ZFGREP="${objdir}"/zfgrep ZTEST="${objdir}"/ztest compressors="bzip2 gzip lzip" extensions="bz2 gz lz" -framework_failure() { echo "failure in testing framework" ; 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 echo "${ZCAT}: cannot execute" @@ -35,7 +35,6 @@ for i in ${compressors}; do $i in || compressor_needed done -printf "testing zutils-%s..." "$2" cat "${testdir}"/test.txt > in || framework_failure cat "${testdir}"/test.txt.tar > in.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 fail=0 +printf "testing zutils-%s..." "$2" printf "\ntesting zcat-%s..." "$2" + for i in ${extensions}; do "${ZCAT}" in.$i > copy || fail=1 cmp in copy || fail=1 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 "${ZCAT}" < in > copy || fail=1 @@ -75,20 +82,51 @@ printf . "${ZCAT}" in in.gz in.bz2 in.lz -- -in- -in-.lz > copy6 || fail=1 cmp in6 copy6 || fail=1 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" + for i in ${extensions}; do "${ZCMP}" in.$i || fail=1 printf . "${ZCMP}" in in.$i || fail=1 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 printf . + "${ZCMP}" -i 1000:1000 -n 50 in.$i in6 || fail=1 + printf . + "${ZCMP}" in.$i in --format=$i || fail=1 + printf . 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 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 printf . "${ZCMP}" in in || fail=1 @@ -123,20 +161,35 @@ printf . printf . "${ZCMP}" - in < in.lz || fail=1 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" + for i in ${extensions}; do "${ZDIFF}" in.$i || fail=1 printf . "${ZDIFF}" in in.$i || fail=1 printf . + "${ZDIFF}" --format=,$i in in.$i || fail=1 + printf . "${ZDIFF}" in.$i in || fail=1 printf . + "${ZDIFF}" --format=$i, in.$i in || fail=1 + printf . done -#"${ZDIFF}" in.tar pin.tar > /dev/null -#if [ $? != 1 ] ; then fail=1 ; printf - ; else printf . ; fi +"${ZDIFF}" in in6 > /dev/null +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 printf . "${ZDIFF}" in in || fail=1 @@ -171,13 +224,27 @@ printf . printf . "${ZDIFF}" - in < in.lz || fail=1 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" + for i in ${extensions}; do "${ZGREP}" "GNU" in.$i > /dev/null || fail=1 - "${ZGREP}" "nx_pattern" in.$i > /dev/null && fail=1 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 "${ZGREP}" "GNU" < pin.tar > /dev/null || fail=1 @@ -186,8 +253,8 @@ printf . printf . "${ZGREP}" -r "GNU" . > /dev/null || fail=1 printf . -"${ZGREP}" "nx_pattern" -r . in > /dev/null && fail=1 -printf . +"${ZGREP}" "nx_pattern" -r . in > /dev/null +if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi "${ZGREP}" "GNU" < in > /dev/null || fail=1 printf . "${ZGREP}" "GNU" < in.gz > /dev/null || fail=1 @@ -204,6 +271,11 @@ printf . printf . "${ZGREP}" "GNU" in in.gz in.bz2 in.lz -- -in- > /dev/null || fail=1 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 printf . "${ZFGREP}" "GNU" in > /dev/null || fail=1 @@ -211,6 +283,16 @@ printf . 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 printf . "${ZTEST}" < in.gz || fail=1 @@ -221,10 +303,14 @@ printf . printf . "${ZTEST}" -r . || fail=1 printf . -"${ZTEST}" < in 2>/dev/null -if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi -printf "LZIPvs0000000000000000000000" | "${ZTEST}" 2>/dev/null -if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi +"${ZTEST}" < in 2> /dev/null +if [ $? != 2 ] ; then fail=1 ; printf - ; else printf . ; fi +dd if=in.lz bs=1000 count=1 2> /dev/null | "${ZTEST}" -q +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 if [ ${fail} = 0 ] ; then @@ -1,5 +1,5 @@ /* 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 it under the terms of the GNU General Public License as published by @@ -23,7 +23,7 @@ struct Cat_options bool show_tabs; bool squeeze_blank; - Cat_options() throw() + Cat_options() : number_lines( 0 ), show_ends( false ), show_nonprinting( false ), show_tabs( false ), squeeze_blank( false ) {} }; @@ -32,20 +32,20 @@ struct Cat_options class Line_number // unlimited size line counter { std::string str; - int first_digit_pos; + unsigned first_digit_pos; public: Line_number() : str( " 0\t" ), first_digit_pos( 5 ) {} 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'; } 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 ) @@ -58,34 +58,35 @@ public: 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( "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" ); - std::printf( "of the known extensions, zcat tries the compressed file names\n" ); - std::printf( "corresponding to the supported compressors. If no files are specified,\n" ); - std::printf( "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" ); - std::printf( "all uncompressed or all compressed with the same compressor.\n" ); - std::printf( "The supported compressors are bzip2, gzip, lzip and xz.\n" ); - std::printf( "\nUsage: zcat [options] [files]\n" ); - std::printf( "\nExit status is 0 if no errors occurred, 1 otherwise.\n" ); - std::printf( "\nOptions:\n" ); - std::printf( " -h, --help display this help and exit\n" ); - std::printf( " -V, --version output version information and exit\n" ); - std::printf( " -A, --show-all equivalent to `-vET'\n" ); - std::printf( " -b, --number-nonblank number nonblank output lines\n" ); - std::printf( " -e equivalent to `-vE'\n" ); - std::printf( " -E, --show-ends display `$' at end of each line\n" ); - std::printf( " -n, --number number all output lines\n" ); - std::printf( " -q, --quiet suppress all messages\n" ); - std::printf( " -r, --recursive operate recursively on directories\n" ); - std::printf( " -s, --squeeze-blank never more than one single blank line\n" ); - std::printf( " -t equivalent to `-vT'\n" ); - std::printf( " -T, --show-tabs display TAB characters as `^I'\n" ); - std::printf( " -v, --show-nonprinting use `^' and `M-' notation, except for LF and TAB\n" ); - std::printf( " --verbose verbose mode (show error messages)\n" ); + std::printf( "Zcat copies each given file (\"-\" means standard input), to standard\n" + "output. If any given file is compressed, its uncompressed content is\n" + "used. If a given file does not exist, and its name does not end with one\n" + "of the known extensions, zcat tries the compressed file names\n" + "corresponding to the supported formats. If no files are specified,\n" + "data is read from standard input, decompressed if needed, and sent to\n" + "standard output. Data read from standard input must be of the same type;\n" + "all uncompressed or all in the same compression format.\n" + "\nThe supported formats are bzip2, gzip, lzip and xz.\n" + "\nUsage: zcat [options] [files]\n" + "\nExit status is 0 if no errors occurred, 1 otherwise.\n" + "\nOptions:\n" + " -h, --help display this help and exit\n" + " -V, --version output version information and exit\n" + " -A, --show-all equivalent to '-vET'\n" + " -b, --number-nonblank number nonblank output lines\n" + " -e equivalent to '-vE'\n" + " -E, --show-ends display '$' at end of each line\n" + " --format=<fmt> force given format (bz2, gz, lz, xz)\n" + " -n, --number number all output lines\n" + " -q, --quiet suppress all messages\n" + " -r, --recursive operate recursively on directories\n" + " -s, --squeeze-blank never more than one single blank line\n" + " -t equivalent to '-vT'\n" + " -T, --show-tabs display TAB characters as '^I'\n" + " -v, --show-nonprinting use '^' and 'M-' notation, except for LF and TAB\n" + " --verbose verbose mode (show error messages)\n" ); 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 ) { 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]; pid_t pid = 0; int retval = 0; - if( !set_data_feeder( &infd, &pid ) ) retval = 1; + if( !set_data_feeder( &infd, &pid, format_type ) ) retval = 1; else retval = do_cat( infd, buffer_size, inbuf, outbuf, input_filename, cat_options ); @@ -1,5 +1,5 @@ /* 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 it under the terms of the GNU General Public License as published by @@ -17,6 +17,7 @@ #define _FILE_OFFSET_BITS 64 +#include <algorithm> #include <cctype> #include <cerrno> #include <climits> @@ -30,9 +31,6 @@ #include <stdint.h> #include <unistd.h> #include <sys/stat.h> -#if defined(__MSVCRT__) || defined(__OS2__) -#include <io.h> -#endif #include "arg_parser.h" #include "zutils.h" @@ -44,73 +42,52 @@ #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 { -#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 } }; +#include "zcmpdiff.cc" -void show_help() throw() +void show_help() { - 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" ); - std::printf( "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" ); - std::printf( "the fly; no temporary files are created.\n" ); - std::printf( "The supported compressors are bzip2, gzip, lzip and xz.\n" ); - std::printf( "\nUsage: zcmp [options] file1 [file2]\n" ); - std::printf( "\nCompares <file1> to <file2>. If <file2> is omitted zcmp tries the\n" ); - std::printf( "following:\n" ); - std::printf( "If <file1> is compressed, compares <file1> to the file with the\n" ); - std::printf( "corresponding decompressed file name (removes the extension from\n" ); - std::printf( "<file1>).\n" ); - std::printf( "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" ); - std::printf( "If no suitable file is found, compares <file1> to data read from\n" ); - std::printf( "standard input.\n" ); - std::printf( "\nExit status is 0 if inputs are identical, 1 if different, 2 if trouble.\n" ); - std::printf( "\nOptions:\n" ); - std::printf( " -h, --help display this help and exit\n" ); - std::printf( " -V, --version output version information and exit\n" ); - std::printf( " -b, --print-bytes print differing bytes\n" ); - std::printf( " -i, --ignore-initial=<n>[,<n2>] ignore differences in the first <n> bytes\n" ); - std::printf( " -l, --list list position, value of all differing bytes\n" ); - std::printf( " -n, --bytes=<n> compare at most <n> bytes\n" ); - std::printf( " -q, --quiet suppress all messages\n" ); - std::printf( " -s, --silent (same as --quiet)\n" ); - std::printf( " -v, --verbose verbose mode (same as --list)\n" ); - std::printf( "Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n" ); - std::printf( "Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" ); + std::printf( "Zcmp compares two files (\"-\" means standard input), and if they\n" + "differ, tells the first byte and line number where they differ. Bytes\n" + "and lines are numbered starting with 1. If any given file is compressed,\n" + "its uncompressed content is used. Compressed files are uncompressed on\n" + "the fly; no temporary files are created.\n" + "\nThe supported formats are bzip2, gzip, lzip and xz.\n" + "\nUsage: zcmp [options] file1 [file2]\n" + "\nCompares <file1> to <file2>. If <file2> is omitted zcmp tries the\n" + "following:\n" + "If <file1> is compressed, compares <file1> to the file with the\n" + "corresponding decompressed file name (removes the extension from\n" + "<file1>).\n" + "If <file1> is not compressed, compares <file1> to the uncompressed\n" + "contents of <file1>.[bz2|gz|lz|xz] (the first one that is found).\n" + "If no suitable file is found, compares <file1> to data read from\n" + "standard input.\n" + "\nExit status is 0 if inputs are identical, 1 if different, 2 if trouble.\n" + "\nOptions:\n" + " -h, --help display this help and exit\n" + " -V, --version output version information and exit\n" + " -b, --print-bytes print differing bytes\n" + " --format=[<fmt1>][,<fmt2>] force given formats (bz2, gz, lz, xz)\n" + " -i, --ignore-initial=<n>[,<n2>] ignore differences in the first <n> bytes\n" + " -l, --list list position, value of all differing bytes\n" + " -n, --bytes=<n> compare at most <n> bytes\n" + " -q, --quiet suppress all messages\n" + " -s, --silent (same as --quiet)\n" + " -v, --verbose verbose mode (same as --list)\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(); } long long getnum( const char * const ptr, const char ** const tailp = 0, - const long long llimit = LLONG_MIN + 1, - const long long ulimit = LLONG_MAX ) throw() + const long long llimit = 0, + const long long ulimit = LLONG_MAX ) { errno = 0; 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 ); std::exit( 2 ); } + if( result < 0 ) errno = ERANGE; 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 ) { - if( LLONG_MAX / factor >= llabs( result ) ) result *= factor; + if( ulimit / factor >= result ) result *= factor; 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] ) { const char * tail; - ignore_initial[0] = getnum( arg, &tail, 0 ); + ignore_initial[0] = getnum( arg, &tail ); if( *tail == ',' || *tail == ':' ) - ignore_initial[1] = getnum( ++tail, 0, 0 ); + ignore_initial[1] = getnum( ++tail ); 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, const uint8_t * const buffer1, - long long * line_numberp ) + unsigned long long * const line_numberp ) { const uint8_t * p0 = buffer0; const uint8_t * p1 = buffer1; + if( verbosity == 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], const std::string filenames[2], const bool print_bytes ) { - enum { buffer_size = 4096 }; - long long byte_number = 1; - long long line_number = 1; - long long rest = max_size; // remaining number of bytes to compare + const int buffer_size = 4096; + unsigned long long byte_number = 1; + unsigned long long line_number = 1; + // remaining number of bytes to compare + long long rest = ( ( max_size >= 0 ) ? max_size : buffer_size ); // buffers with space for sentinels at the end uint8_t * const buffer0 = new uint8_t[2*(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 ) { - 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 for( int i = 0; i < 2; ++i ) { @@ -296,7 +236,6 @@ int cmp( const long long max_size, const int infd[2], return 2; } } - rest -= size; buffer0[rd[0]] = ~buffer1[rd[0]]; // sentinels for the block compare 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( !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(), byte_number, line_number ); else @@ -320,7 +259,7 @@ int cmp( const long long max_size, const int infd[2], const unsigned char c1 = buffer1[first_diff]; char buf0[5], buf1[5]; 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(), 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( !print_bytes ) - std::printf( "%lld %3o %3o\n", byte_number, c0, c1 ); + std::printf( "%llu %3o %3o\n", byte_number, c0, c1 ); else { char buf0[5], buf1[5]; 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 ); } } @@ -368,9 +307,11 @@ int cmp( const long long max_size, const int infd[2], int main( const int argc, const char * const argv[] ) { + enum { format_opt = 256 }; // number of initial bytes ignored for each file 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; invocation_name = argv[0]; util_name = "zcmp"; @@ -386,6 +327,7 @@ int main( const int argc, const char * const argv[] ) { 's', "silent", Arg_parser::no }, { 'v', "verbose", Arg_parser::no }, { 'V', "version", Arg_parser::no }, + { format_opt, "format", Arg_parser::yes }, { 0 , 0, Arg_parser::no } }; 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 'i': parse_ignore_initial( arg, ignore_initial ); 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 's': verbosity = -1; break; case 'v': verbosity = 1; break; case 'V': show_version( "Zcmp" ); return 0; + case format_opt: get_format_types( arg, format_types ); break; default : internal_error( "uncaught option" ); } } // end process options #if defined(__MSVCRT__) || defined(__OS2__) - _setmode( STDIN_FILENO, O_BINARY ); - _setmode( STDOUT_FILENO, O_BINARY ); + _fsetmode( stdin, "b" ); + _fsetmode( stdout, "b" ); #endif if( argind >= parser.arguments() ) @@ -449,6 +392,9 @@ int main( const int argc, const char * const argv[] ) } 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]; infd[1] = open_other_instream( 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 old_infd[0] = infd[0]; old_infd[1] = infd[1]; pid_t pid[2]; - if( !set_data_feeder( &infd[0], &pid[0] ) || - !set_data_feeder( &infd[1], &pid[1] ) ) + if( !set_data_feeder( &infd[0], &pid[0], format_types[0] ) || + !set_data_feeder( &infd[1], &pid[1], format_types[1] ) ) return 2; 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 ); - if( retval != 0 ) + if( retval != 0 || max_size >= 0 ) { - if( pid[0] ) kill( pid[0], SIGTERM ); - if( pid[1] ) kill( pid[1], SIGTERM ); + for( int i = 0; i < 2; ++i ) + 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 if( ( pid[0] && wait_for_child( pid[0], "data feeder" ) != 0 ) || diff --git a/zcmpdiff.cc b/zcmpdiff.cc new file mode 100644 index 0000000..6212314 --- /dev/null +++ b/zcmpdiff.cc @@ -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 ); + } @@ -1,5 +1,5 @@ /* 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 it under the terms of the GNU General Public License as published by @@ -17,6 +17,7 @@ #define _FILE_OFFSET_BITS 64 +#include <algorithm> #include <cctype> #include <cerrno> #include <climits> @@ -30,9 +31,6 @@ #include <stdint.h> #include <unistd.h> #include <sys/stat.h> -#if defined(__MSVCRT__) || defined(__OS2__) -#include <io.h> -#endif #include "arg_parser.h" #include "zutils.h" @@ -41,126 +39,60 @@ #error "Environments where CHAR_BIT != 8 are not supported." #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 { std::string fifonames[2]; // names of the two fifos passed to diff -#ifdef O_BINARY -const int o_binary = O_BINARY; -#else -const int o_binary = 0; -#endif +#include "zcmpdiff.cc" -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( "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" ); - std::printf( "the diff program and has the limitation that messages from diff refer to\n" ); - std::printf( "temporary filenames instead of those specified.\n" ); - std::printf( "The supported compressors are bzip2, gzip, lzip and xz.\n" ); - std::printf( "\nUsage: zdiff [options] file1 [file2]\n" ); - std::printf( "\nCompares <file1> to <file2>. If <file2> is omitted zdiff tries the\n" ); - std::printf( "following:\n" ); - std::printf( "If <file1> is compressed, compares <file1> to the file with the\n" ); - std::printf( "corresponding decompressed file name (removes the extension from\n" ); - std::printf( "<file1>).\n" ); - std::printf( "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" ); - std::printf( "If no suitable file is found, compares <file1> to data read from\n" ); - std::printf( "standard input.\n" ); - std::printf( "\nExit status is 0 if inputs are identical, 1 if different, 2 if trouble.\n" ); - std::printf( "\nOptions:\n" ); - std::printf( " -h, --help display this help and exit\n" ); - std::printf( " -V, --version output version information and exit\n" ); - std::printf( " -a, --text treat all files as text\n" ); - std::printf( " -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" ); - std::printf( " -c use the context output format\n" ); - std::printf( " -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" ); - std::printf( " -E, --ignore-tab-expansion ignore changes due to tab expansion\n" ); - std::printf( " -i, --ignore-case ignore case differences in file contents\n" ); - std::printf( " -p, --show-c-function show which C function each change is in\n" ); - std::printf( " -q, --brief output only whether files differ\n" ); - std::printf( " -s, --report-identical-files report when two files are identical\n" ); - std::printf( " -t, --expand-tabs expand tabs to spaces in output\n" ); - std::printf( " -T, --initial-tab make tabs line up by prepending a tab\n" ); - std::printf( " -u use the unified output format\n" ); - std::printf( " -U, --unified=<n> same as -u but use <n> lines of context\n" ); - std::printf( " -w, --ignore-all-space ignore all white space\n" ); - std::printf( "Numbers may be followed by a multiplier: k = kB = 10^3 = 1000,\n" ); - std::printf( "Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" ); + std::printf( "Zdiff compares two files (\"-\" means standard input), and if they\n" + "differ, shows the differences line by line. If any given file is\n" + "compressed, its uncompressed content is used. Zdiff is a front end to\n" + "the diff program and has the limitation that messages from diff refer to\n" + "temporary filenames instead of those specified.\n" + "\nThe supported formats are bzip2, gzip, lzip and xz.\n" + "\nUsage: zdiff [options] file1 [file2]\n" + "\nCompares <file1> to <file2>. If <file2> is omitted zdiff tries the\n" + "following:\n" + "If <file1> is compressed, compares <file1> to the file with the\n" + "corresponding decompressed file name (removes the extension from\n" + "<file1>).\n" + "If <file1> is not compressed, compares <file1> to the uncompressed\n" + "contents of <file1>.[bz2|gz|lz|xz] (the first one that is found).\n" + "If no suitable file is found, compares <file1> to data read from\n" + "standard input.\n" + "\nExit status is 0 if inputs are identical, 1 if different, 2 if trouble.\n" + "\nOptions:\n" + " -h, --help display this help and exit\n" + " -V, --version output version information and exit\n" + " -a, --text treat all files as text\n" + " -b, --ignore-space-change ignore changes in the amount of white space\n" + " -B, --ignore-blank-lines ignore changes whose lines are all blank\n" + " -c use the context output format\n" + " -C, --context=<n> same as -c but use <n> lines of context\n" + " -d, --minimal try hard to find a smaller set of changes\n" + " -E, --ignore-tab-expansion ignore changes due to tab expansion\n" + " --format=[<fmt1>][,<fmt2>] force given formats (bz2, gz, lz, xz)\n" + " -i, --ignore-case ignore case differences in file contents\n" + " -p, --show-c-function show which C function each change is in\n" + " -q, --brief output only whether files differ\n" + " -s, --report-identical-files report when two files are identical\n" + " -t, --expand-tabs expand tabs to spaces in output\n" + " -T, --initial-tab make tabs line up by prepending a tab\n" + " -u use the unified output format\n" + " -U, --unified=<n> same as -u but use <n> lines of context\n" + " -w, --ignore-all-space ignore all white space\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(); } -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 ); - } - - -const char * my_basename( const char * filename ) throw() +const char * my_basename( const char * filename ) { const char * c = filename; 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() ) { 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"; fifonames[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] ); n /= num_codes; } 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, - pid_t * const pidp ) + pid_t * const pidp, const int format_type ) { - std::string file_type; - const uint8_t * magic_data; - int magic_size; - const bool compressed = - test_format( infd, file_type, &magic_data, &magic_size ); + const uint8_t * magic_data = 0; + int magic_size = 0; + const char * const decompressor_name = ( format_type >= 0 ) ? + decompressor_names[format_type] : + 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 if( pipe( fda ) < 0 ) @@ -239,7 +171,7 @@ bool set_data_feeder( const std::string & fifoname, const int infd, if( outfd < 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 ) ); _exit( 2 ); } @@ -247,19 +179,20 @@ bool set_data_feeder( const std::string & fifoname, const int infd, dup2( outfd, STDOUT_FILENO ) >= 0 && close( fda[0] ) == 0 && close( fda[1] ) == 0 && close( outfd ) == 0 ) - execlp( file_type.c_str(), file_type.c_str(), "-cdfq", (char *)0 ); - show_exec_error( file_type.c_str() ); + execlp( decompressor_name, decompressor_name, + (verbosity >= 0) ? "-d" : "-dq", (char *)0 ); + show_exec_error( decompressor_name ); _exit( 2 ); } if( pid2 < 0 ) - { show_fork_error( file_type.c_str() ); _exit( 2 ); } + { show_fork_error( decompressor_name ); _exit( 2 ); } if( close( fda[0] ) != 0 || !feed_data( infd, fda[1], magic_data, magic_size ) ) _exit( 2 ); if( close( fda[1] ) != 0 ) { show_close_error( "data feeder" ); _exit( 2 ); } - _exit( wait_for_child( pid2, file_type.c_str() ) ); + _exit( wait_for_child( pid2, decompressor_name ) ); } // parent 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( 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 ) ); _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(); 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( SIGINT, signal_handler ); @@ -315,7 +248,9 @@ void set_signals() throw() int main( const int argc, const char * const argv[] ) { + enum { format_opt = 256 }; std::vector< const char * > diff_args; // args to diff, maybe empty + int format_types[2] = { -1, -1 }; invocation_name = argv[0]; util_name = "zdiff"; @@ -339,6 +274,7 @@ int main( const int argc, const char * const argv[] ) { 'U', "unified", Arg_parser::yes }, { 'V', "version", Arg_parser::no }, { 'w', "ignore-all-space", Arg_parser::no }, + { format_opt, "format", Arg_parser::yes }, { 0 , 0, Arg_parser::no } }; 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 'V': show_version( "Zdiff" ); return 0; case 'w': diff_args.push_back( "-w" ); break; + case format_opt: get_format_types( arg, format_types ); break; default : internal_error( "uncaught option" ); } } // end process options #if defined(__MSVCRT__) || defined(__OS2__) - _setmode( STDIN_FILENO, O_BINARY ); - _setmode( STDOUT_FILENO, O_BINARY ); + _fsetmode( stdin, "b" ); + _fsetmode( stdout, "b" ); #endif if( argind >= parser.arguments() ) @@ -408,6 +345,9 @@ int main( const int argc, const char * const argv[] ) } 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]; infd[1] = open_other_instream( 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]; 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[diff_args.size()+1] = "--"; 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; } pid_t pid[2]; - if( !set_data_feeder( fifonames[0], infd[0], &pid[0] ) || - !set_data_feeder( fifonames[1], infd[1], &pid[1] ) ) + if( !set_data_feeder( fifonames[0], infd[0], &pid[0], format_types[0] ) || + !set_data_feeder( fifonames[1], infd[1], &pid[1], format_types[1] ) ) return 2; int retval = wait_for_child( diff_pid, DIFF ); if( retval != 0 ) { - if( pid[0] ) kill( pid[0], SIGTERM ); - if( pid[1] ) kill( pid[1], SIGTERM ); + for( int i = 0; i < 2; ++i ) + 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 if( ( pid[0] && wait_for_child( pid[0], "data feeder" ) != 0 ) || @@ -1,5 +1,5 @@ /* 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 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/>. */ -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( "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" ); - std::printf( "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" ); - std::printf( "supported compressors. 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" ); - std::printf( "standard input must be of the same type; all uncompressed or all\n" ); - std::printf( "compressed with the same compressor.\n" ); - std::printf( "The supported compressors are bzip2, gzip, lzip and xz.\n" ); - std::printf( "\nUsage: zgrep [options] <pattern> [files]\n" ); - std::printf( "\nExit status is 0 if match, 1 if no match, 2 if trouble.\n" ); - std::printf( "\nOptions:\n" ); - std::printf( " --help display this help and exit\n" ); - std::printf( " -V, --version output version information and exit\n" ); - std::printf( " -a, --text treat all files as text\n" ); - std::printf( " -A, --after-context=<n> print <n> lines of trailing context\n" ); - std::printf( " -b, --byte-offset print the byte offset of each line\n" ); - std::printf( " -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" ); - std::printf( " -C, --context=<n> print <n> lines of output context\n" ); - std::printf( " -e, --regexp=<pattern> use <pattern> as the pattern to match\n" ); - std::printf( " -E, --extended-regexp <pattern> is an extended regular expression\n" ); - std::printf( " -f, --file=<file> obtain patterns from <file>\n" ); - std::printf( " -F, --fixed-strings <pattern> is a set of newline-separated strings\n" ); - std::printf( " -h, --no-filename suppress the prefixing filename on output\n" ); - std::printf( " -H, --with-filename print the filename for each match\n" ); - std::printf( " -i, --ignore-case ignore case distinctions\n" ); - std::printf( " -I ignore binary files\n" ); - std::printf( " -l, --files-with-matches only print names of files containing matches\n" ); - std::printf( " -L, --files-without-match only print names of files containing no matches\n" ); - std::printf( " -m, --max-count=<n> stop after <n> matches\n" ); - std::printf( " -n, --line-number print the line number of each line\n" ); - std::printf( " -o, --only-matching show only the part of a line matching <pattern>\n" ); - std::printf( " -q, --quiet suppress all messages\n" ); - std::printf( " -r, --recursive operate recursively on directories\n" ); - std::printf( " -s, --no-messages suppress error messages\n" ); - std::printf( " -v, --invert-match select non-matching lines\n" ); - std::printf( " --verbose verbose mode (show error messages)\n" ); - std::printf( " -w, --word-regexp match only whole words\n" ); - std::printf( " -x, --line-regexp match only whole lines\n" ); + std::printf( "Zgrep is a front end to the grep program that allows transparent search\n" + "on any combination of compressed and non-compressed files. If any given\n" + "file is compressed, its uncompressed content is used. If a given file\n" + "does not exist, and its name does not end with one of the known\n" + "extensions, zgrep tries the compressed file names corresponding to the\n" + "supported formats. If no files are specified, data is read from\n" + "standard input, decompressed if needed, and fed to grep. Data read from\n" + "standard input must be of the same type; all uncompressed or all\n" + "in the same compression format.\n" + "\nThe supported formats are bzip2, gzip, lzip and xz.\n" + "\nUsage: zgrep [options] <pattern> [files]\n" + "\nExit status is 0 if match, 1 if no match, 2 if trouble.\n" + "\nOptions:\n" + " --help display this help and exit\n" + " -V, --version output version information and exit\n" + " -a, --text treat all files as text\n" + " -A, --after-context=<n> print <n> lines of trailing context\n" + " -b, --byte-offset print the byte offset of each line\n" + " -B, --before-context=<n> print <n> lines of leading context\n" + " -c, --count only print a count of matching lines per file\n" + " -C, --context=<n> print <n> lines of output context\n" + " -e, --regexp=<pattern> use <pattern> as the pattern to match\n" + " -E, --extended-regexp <pattern> is an extended regular expression\n" + " -f, --file=<file> obtain patterns from <file>\n" + " -F, --fixed-strings <pattern> is a set of newline-separated strings\n" + " --format=<fmt> force given format (bz2, gz, lz, xz)\n" + " -h, --no-filename suppress the prefixing filename on output\n" + " -H, --with-filename print the filename for each match\n" + " -i, --ignore-case ignore case distinctions\n" + " -I ignore binary files\n" + " -l, --files-with-matches only print names of files containing matches\n" + " -L, --files-without-match only print names of files containing no matches\n" + " -m, --max-count=<n> stop after <n> matches\n" + " -n, --line-number print the line number of each line\n" + " -o, --only-matching show only the part of a line matching <pattern>\n" + " -q, --quiet suppress all messages\n" + " -r, --recursive operate recursively on directories\n" + " -s, --no-messages suppress error messages\n" + " -v, --invert-match select non-matching lines\n" + " --verbose verbose mode (show error messages)\n" + " -w, --word-regexp match only whole words\n" + " -x, --line-regexp match only whole lines\n" ); 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; - if( !set_data_feeder( &infd, &pid ) ) return 2; + if( !set_data_feeder( &infd, &pid, format_type ) ) return 2; const pid_t grep_pid = fork(); 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]; 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[grep_args.size()+1] = 0; 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 bool grep_list, const bool grep_show_name ) { 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 if( pipe( fda ) < 0 ) { 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]; 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[grep_args.size()+1] = 0; 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; 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; } } if( size < buffer_size ) break; @@ -1,5 +1,5 @@ /* 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 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/>. */ -void show_ztest_help() throw() +void show_ztest_help() { - 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" ); - std::printf( "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" ); - std::printf( "The supported compressors are bzip2, gzip, lzip and xz.\n" ); - std::printf( "\nUsage: ztest [options] [files]\n" ); - std::printf( "\nExit status is 2 if any compressed file is corrupt, 0 otherwise.\n" ); - std::printf( "\nOptions:\n" ); - std::printf( " -h, --help display this help and exit\n" ); - std::printf( " -V, --version output version information and exit\n" ); - std::printf( " -q, --quiet suppress all messages\n" ); - std::printf( " -r, --recursive operate recursively on directories\n" ); - std::printf( " -v, --verbose be verbose (a 2nd -v gives more)\n" ); + std::printf( "Ztest verifies the integrity of the specified compressed files.\n" + "Non-compressed files are ignored. If no files are specified, the\n" + "integrity of compressed data read from standard input is verified. Data\n" + "read from standard input must be all in the same compression format.\n" + "\nThe supported formats are bzip2, gzip, lzip and xz.\n" + "\nNote that some xz files lack integrity information, and therefore can't\n" + "be verified as reliably as the other formats can.\n" + "\nUsage: ztest [options] [files]\n" + "\nExit status is 0 if all compressed files verify OK, 1 if environmental\n" + "problems (file not found, invalid flags, I/O errors, etc), 2 if any\n" + "compressed file is corrupt or invalid.\n" + "\nOptions:\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(); } -int ztest_stdin( const int infd, +int ztest_stdin( const int infd, const int format_type, const std::vector< const char * > & ztest_args ) { - std::string file_type; - const uint8_t * magic_data; + const uint8_t * magic_data = 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; } int fda[2]; // pipe from feeder if( pipe( fda ) < 0 ) @@ -53,19 +60,19 @@ int ztest_stdin( const int infd, close( fda[0] ) == 0 && close( fda[1] ) == 0 ) { const char ** const argv = new const char *[ztest_args.size()+3]; - argv[0] = file_type.c_str(); - for( unsigned int i = 0; i < ztest_args.size(); ++i ) + argv[0] = decompressor_name; + for( unsigned i = 0; i < ztest_args.size(); ++i ) argv[i+1] = ztest_args[i]; argv[ztest_args.size()+1] = "-t"; argv[ztest_args.size()+2] = 0; execvp( argv[0], (char **)argv ); } - show_exec_error( file_type.c_str() ); + show_exec_error( decompressor_name ); _exit( 1 ); } // parent if( pid < 0 ) - { show_fork_error( file_type.c_str() ); return 1; } + { show_fork_error( decompressor_name ); return 1; } const pid_t pid2 = fork(); if( pid2 == 0 ) // child2 (decompressor feeder) @@ -82,40 +89,43 @@ int ztest_stdin( const int infd, { show_fork_error( "decompressor feeder" ); return 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 ) retval = 1; 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 ) { - std::string file_type; - const uint8_t * magic_data; + const uint8_t * magic_data = 0; int magic_size = 0; - if( !test_format( infd, file_type, &magic_data, &magic_size ) ) - return 0; // ignored + const char * const decompressor_name = ( format_type >= 0 ) ? + decompressor_names[format_type] : + test_format( infd, &magic_data, &magic_size ); + if( !decompressor_name ) + return 0; // skip this file const pid_t pid = fork(); if( pid == 0 ) // child (decompressor) { const char ** const argv = new const char *[ztest_args.size()+5]; - argv[0] = file_type.c_str(); - for( unsigned int i = 0; i < ztest_args.size(); ++i ) + argv[0] = decompressor_name; + for( unsigned i = 0; i < ztest_args.size(); ++i ) argv[i+1] = ztest_args[i]; argv[ztest_args.size()+1] = "-t"; argv[ztest_args.size()+2] = "--"; argv[ztest_args.size()+3] = input_filename.c_str(); argv[ztest_args.size()+4] = 0; execvp( argv[0], (char **)argv ); - show_exec_error( file_type.c_str() ); + show_exec_error( decompressor_name ); _exit( 1 ); } // parent 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 ); } @@ -1,5 +1,5 @@ /* 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 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 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. // 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; errno = 0; @@ -47,7 +57,7 @@ int readblock( const int fd, uint8_t * const buf, const int size ) throw() errno = 0; const int n = read( fd, buf + size - rest, rest ); if( n > 0 ) rest -= n; - else if( n == 0 ) break; + else if( n == 0 ) break; // EOF else if( errno != EINTR && errno != EAGAIN ) break; } 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. // 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; errno = 0; @@ -66,7 +76,7 @@ int writeblock( const int fd, const uint8_t * const buf, const int size ) throw( errno = 0; const int n = write( fd, buf + size - rest, rest ); 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; } @@ -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, 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; } enum { buffer_size = 4096 }; 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; - int magic_size; - const bool compressed = - test_format( *infdp, file_type, &magic_data, &magic_size ); + const uint8_t * magic_data = 0; + int magic_size = 0; + const char * const decompressor_name = ( format_type >= 0 ) ? + decompressor_names[format_type] : + test_format( *infdp, &magic_data, &magic_size ); - if( compressed ) // compressed with `file_type' + if( decompressor_name ) // compressed { int fda[2]; // pipe from feeder 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 && close( fda[0] ) == 0 && close( fda[1] ) == 0 && close( fda2[0] ) == 0 && close( fda2[1] ) == 0 ) - execlp( file_type.c_str(), file_type.c_str(), "-cdfq", (char *)0 ); - show_exec_error( file_type.c_str() ); + execlp( decompressor_name, decompressor_name, + (verbosity >= 0) ? "-d" : "-dq", (char *)0 ); + show_exec_error( decompressor_name ); _exit( 2 ); } if( pid2 < 0 ) - { show_fork_error( file_type.c_str() ); _exit( 2 ); } + { show_fork_error( decompressor_name ); _exit( 2 ); } if( close( fda[0] ) != 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 ); if( close( fda[1] ) != 0 ) { show_close_error( "decompressor feeder" ); _exit( 2 ); } - _exit( wait_for_child( pid2, file_type.c_str() ) ); + _exit( wait_for_child( pid2, decompressor_name ) ); } // parent 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( "Zutils home page: http://www.nongnu.org/zutils/zutils.html\n" ); + std::printf( "\nReport bugs to zutils-bug@nongnu.org\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 ) std::printf( "%s %s\n", Program_name, PROGVERSION ); else std::printf( "%s (%s) %s\n", Util_name, program_name, PROGVERSION ); 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( "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" ); + std::printf( "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n" ); } -void show_error( const char * const msg, const int errcode, - const bool help ) throw() +void show_error( const char * const msg, const int errcode, const bool help ) { 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, "\n" ); } - if( help && invocation_name && invocation_name[0] ) - std::fprintf( stderr, "Try `%s --help' for more information.\n", + if( help ) + std::fprintf( stderr, "Try '%s --help' for more information.\n", 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 ) - std::fprintf( stderr, "%s: %s `%s': %s.\n", + std::fprintf( stderr, "%s: %s '%s': %s.\n", 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 ) 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 ) - 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 ) ); } -void show_fork_error( const char * const prog_name ) throw() +void show_fork_error( const char * const prog_name ) { 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 ) ); } @@ -245,21 +256,13 @@ void internal_error( const char * const msg ) } -unsigned char xdigit( const int value ) throw() - { - if( value >= 0 && value <= 9 ) return '0' + value; - 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 ) +const char * test_format( const int infd, + const uint8_t ** const magic_datap, + int * const magic_sizep ) { enum { buf_size = 5 }; static uint8_t buf[buf_size]; int i = 0; - file_type.clear(); if( readblock( infd, buf, 1 ) == 1 ) { ++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] && 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] ) { 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] ) { 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[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] ) { @@ -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[3] && 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; - return false; + return 0; } @@ -314,10 +311,38 @@ int wait_for_child( const pid_t pid, const char * const name, if( errno != EINTR ) { if( verbosity >= 0 ) - std::fprintf( stderr, "%s: Error waiting termination of `%s': %s.\n", - util_name, name, std::strerror( errno ) ); + std::fprintf( stderr, "%s: Error waiting termination of '%s': %s.\n", + util_name, name, std::strerror( errno ) ); + _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 ); @@ -1,5 +1,5 @@ /* 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 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_year = "2011"; +const char * const program_year = "2013"; extern const char * invocation_name; extern const char * util_name; 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 }; -enum { gzip_magic_size = 2 }; -enum { lzip_magic_size = 4 }; -enum { xz_magic_size = 5 }; -const uint8_t bzip2_magic[bzip2_magic_size] = { 'B', 'Z', 'h' }; -const uint8_t gzip_magic[ gzip_magic_size] = { '\x1F', '\x8B' }; -const uint8_t lzip_magic[ lzip_magic_size] = { 'L', 'Z', 'I', 'P' }; -const uint8_t xz_magic[ xz_magic_size] = { '\xFD', '7', 'z', 'X', 'Z' }; +// first magic byte must be different among formats +enum { bzip2_magic_size = 3, + gzip_magic_size = 2, + lzip_magic_size = 4, + xz_magic_size = 5 }; +const uint8_t bzip2_magic[bzip2_magic_size] = + { 0x42, 0x5A, 0x68 }; // "BZh" +const uint8_t gzip_magic[gzip_magic_size] = + { 0x1F, 0x8B }; +const uint8_t 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 writeblock( const int fd, const uint8_t * const buf, const int size ) throw(); +int get_format_type( const std::string & arg ); + +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, 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_version( const char * const Util_name = 0 ) throw(); +void show_help_addr(); +void show_version( const char * const Util_name = 0 ); void show_error( const char * const msg, const int errcode = 0, - const bool help = false ) throw(); -void show_error2( const char * const msg, const char * const name ) throw(); -void show_close_error( const char * const prog_name ) throw(); -void show_exec_error( const char * const prog_name ) throw(); -void show_fork_error( const char * const prog_name ) throw(); + const bool help = false ); +void show_error2( const char * const msg, const char * const name ); +void show_close_error( const char * const prog_name ); +void show_exec_error( const char * const prog_name ); +void show_fork_error( const char * const prog_name ); void internal_error( const char * const msg ); -bool test_format( const int infd, std::string & file_type, - const uint8_t ** magic_data, int * magic_sizep ); +const char * test_format( const int infd, + 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, 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 ); |