diff --git a/ChangeLog b/ChangeLog index 41e5deb..6cc39f8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2025-05-27 Antonio Diaz Diaz + + * Version 1.15 released. + * zcat, ztest, zupdate: New option '-x, --exclude'. + * zgrep: New option '--exclude'. + * zgrep.cc: Exit with status 2 if write error on stdout. + 2025-01-05 Antonio Diaz Diaz * Version 1.14 released. diff --git a/Makefile.in b/Makefile.in index 7583316..0d6bcd6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -75,12 +75,12 @@ $(objs) : Makefile $(scripts) : Makefile arg_parser.o : arg_parser.h rc.o : arg_parser.h rc.h -zcat.o : arg_parser.h rc.h zutils.h recursive.cc zcatgrep.cc +zcat.o : arg_parser.h rc.h zutils.h exclude.cc recursive.cc zcatgrep.cc zcmp.o : arg_parser.h rc.h zutils.h zcmpdiff.cc zdiff.o : arg_parser.h rc.h zutils.h zcmpdiff.cc -zgrep.o : arg_parser.h rc.h zutils.h recursive.cc zcatgrep.cc -ztest.o : arg_parser.h rc.h zutils.h recursive.cc -zupdate.o : arg_parser.h rc.h recursive.cc +zgrep.o : arg_parser.h rc.h zutils.h exclude.cc recursive.cc zcatgrep.cc +ztest.o : arg_parser.h rc.h zutils.h exclude.cc recursive.cc +zupdate.o : arg_parser.h rc.h exclude.cc recursive.cc zutils.o : rc.h zutils.h doc : info man @@ -230,8 +230,8 @@ dist : doc $(DISTNAME)/testsuite/test.txt.tar \ $(DISTNAME)/testsuite/zcat_vs.dat \ $(DISTNAME)/testsuite/test_bad_crc.lz \ - $(DISTNAME)/testsuite/zero_bad_crc.lz \ - $(DISTNAME)/testsuite/zero_bad_crc.gz + $(DISTNAME)/testsuite/em_bad_crc.lz \ + $(DISTNAME)/testsuite/em_bad_crc.gz rm -f $(DISTNAME) lzip -v -9 $(DISTNAME).tar diff --git a/NEWS b/NEWS index 3ca3953..8c136e9 100644 --- a/NEWS +++ b/NEWS @@ -1,8 +1,7 @@ -Changes in version 1.14: +Changes in version 1.15: -'zupdate --recursive --destdir=dir' now keeps the file name component -following the last slash in directory arguments; -'../a' recompresses the file ../a/b.gz to dir/a/b.lz, while -'../a/' recompresses the file ../a/b.gz to dir/b.lz. +The new option '-x, --exclude' has been added to zcat, ztest, and zupdate. +The new option '--exclude' has been added to zgrep. +It excludes files matching a shell pattern (for example '*.o'). -The chapter 'Syntax of command-line arguments' has been added to the manual. +zgrep now exits with status 2 if a write error happens on stdout. diff --git a/configure b/configure index f061060..7656b86 100755 --- a/configure +++ b/configure @@ -6,7 +6,7 @@ # to copy, distribute, and modify it. pkgname=zutils -pkgversion=1.14 +pkgversion=1.15 srctrigger=doc/${pkgname}.texi # clear some things potentially inherited from environment. diff --git a/doc/zcat.1 b/doc/zcat.1 index c5274da..c2460b7 100644 --- a/doc/zcat.1 +++ b/doc/zcat.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2. -.TH ZCAT "1" "January 2025" "zutils 1.14" "User Commands" +.TH ZCAT "1" "May 2025" "zutils 1.15" "User Commands" .SH NAME zcat \- decompress and concatenate files to standard output .SH SYNOPSIS @@ -86,6 +86,9 @@ use '^' and 'M\-' notation, except for LF and TAB \fB\-\-verbose\fR verbose mode (show error messages) .TP +\fB\-x\fR, \fB\-\-exclude=\fR +exclude files matching a shell pattern +.TP \fB\-\-bz2=\fR set compressor and options for bzip2 format .TP diff --git a/doc/zcmp.1 b/doc/zcmp.1 index a0fe500..16ed3ed 100644 --- a/doc/zcmp.1 +++ b/doc/zcmp.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2. -.TH ZCMP "1" "January 2025" "zutils 1.14" "User Commands" +.TH ZCMP "1" "May 2025" "zutils 1.15" "User Commands" .SH NAME zcmp \- decompress and compare two files byte by byte .SH SYNOPSIS diff --git a/doc/zdiff.1 b/doc/zdiff.1 index 1085cac..5058fe5 100644 --- a/doc/zdiff.1 +++ b/doc/zdiff.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2. -.TH ZDIFF "1" "January 2025" "zutils 1.14" "User Commands" +.TH ZDIFF "1" "May 2025" "zutils 1.15" "User Commands" .SH NAME zdiff \- decompress and compare two files line by line .SH SYNOPSIS diff --git a/doc/zgrep.1 b/doc/zgrep.1 index 772ee63..c717385 100644 --- a/doc/zgrep.1 +++ b/doc/zgrep.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2. -.TH ZGREP "1" "January 2025" "zutils 1.14" "User Commands" +.TH ZGREP "1" "May 2025" "zutils 1.15" "User Commands" .SH NAME zgrep \- search compressed files for a regular expression .SH SYNOPSIS @@ -63,6 +63,9 @@ use as the pattern to match \fB\-E\fR, \fB\-\-extended\-regexp\fR is an extended regular expression .TP +\fB\-\-exclude=\fR +exclude files matching a shell pattern +.TP \fB\-f\fR, \fB\-\-file=\fR obtain patterns from .TP diff --git a/doc/ztest.1 b/doc/ztest.1 index 5a30654..821b62c 100644 --- a/doc/ztest.1 +++ b/doc/ztest.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2. -.TH ZTEST "1" "January 2025" "zutils 1.14" "User Commands" +.TH ZTEST "1" "May 2025" "zutils 1.15" "User Commands" .SH NAME ztest \- check the integrity of compressed files .SH SYNOPSIS @@ -62,6 +62,9 @@ recursively follow symbolic links \fB\-v\fR, \fB\-\-verbose\fR be verbose (a 2nd \fB\-v\fR gives more) .TP +\fB\-x\fR, \fB\-\-exclude=\fR +exclude files matching a shell pattern +.TP \fB\-\-bz2=\fR set compressor and options for bzip2 format .TP diff --git a/doc/zupdate.1 b/doc/zupdate.1 index bfe0ecd..79b8247 100644 --- a/doc/zupdate.1 +++ b/doc/zupdate.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2. -.TH ZUPDATE "1" "January 2025" "zutils 1.14" "User Commands" +.TH ZUPDATE "1" "May 2025" "zutils 1.15" "User Commands" .SH NAME zupdate \- recompress bzip2, gzip, xz, zstd files to lzip format .SH SYNOPSIS @@ -79,6 +79,9 @@ recursively follow symbolic links \fB\-v\fR, \fB\-\-verbose\fR be verbose (a 2nd \fB\-v\fR gives more) .TP +\fB\-x\fR, \fB\-\-exclude=\fR +exclude files matching a shell pattern +.TP \fB\-0\fR .. \fB\-9\fR set compression level [default 9] .TP diff --git a/doc/zutils.info b/doc/zutils.info index a755e6b..7c80846 100644 --- a/doc/zutils.info +++ b/doc/zutils.info @@ -11,7 +11,7 @@ File: zutils.info, Node: Top, Next: Introduction, Up: (dir) Zutils Manual ************* -This manual is for Zutils (version 1.14, 5 January 2025). +This manual is for Zutils (version 1.15, 27 May 2025). * Menu: @@ -302,6 +302,15 @@ Exit status is 0 if no errors occurred, 1 otherwise. Verbose mode. Show error messages. Repeating it increases the verbosity level. *Note version::. +'-x PATTERN' +'--exclude=PATTERN' + Exclude files matching a shell pattern like '*.o', even if the files + are specified in the command line. A file is considered to match if any + component of the file name matches. For example, '*.o' matches + 'foo.o', 'foo.o/bar' and 'foo/bar.o'. If PATTERN contains a '/', it + matches a corresponding '/' in the file name. For example, 'foo/*.o' + matches 'foo/bar.o'. Multiple '--exclude' options can be specified. +  File: zutils.info, Node: Zcmp, Next: Zdiff, Prev: Zcat, Up: Top @@ -545,8 +554,8 @@ An exit status of 0 means at least one match was found, 1 means no matches were found, and 2 means trouble. 'zgrep' supports the following options (Some options only work if the grep -program used supports them. Options -h, -H, -r, -R, and -Z are managed by -'zgrep' and not passed to grep): +program used supports them. Options '--exclude', '-h', '-H', '-r', '-R', +and '-Z' are managed by 'zgrep' and not passed to grep): '-a' '--text' @@ -583,6 +592,14 @@ program used supports them. Options -h, -H, -r, -R, and -Z are managed by '--extended-regexp' Interpret PATTERN as an extended regular expression (ERE). +'--exclude=PATTERN' + Exclude files matching a shell pattern like '*.o', even if the files + are specified in the command line. A file is considered to match if any + component of the file name matches. For example, '*.o' matches + 'foo.o', 'foo.o/bar' and 'foo/bar.o'. If PATTERN contains a '/', it + matches a corresponding '/' in the file name. For example, 'foo/*.o' + matches 'foo/bar.o'. Multiple '--exclude' options can be specified. + '-f FILE' '--file=FILE' Obtain patterns from FILE, one per line. @@ -787,6 +804,15 @@ incorrect file name extension. Verbose mode. Show the check status for each file processed. Further -v's increase the verbosity level. *Note version::. +'-x PATTERN' +'--exclude=PATTERN' + Exclude files matching a shell pattern like '*.o', even if the files + are specified in the command line. A file is considered to match if any + component of the file name matches. For example, '*.o' matches + 'foo.o', 'foo.o/bar' and 'foo/bar.o'. If PATTERN contains a '/', it + matches a corresponding '/' in the file name. For example, 'foo/*.o' + matches 'foo/bar.o'. Multiple '--exclude' options can be specified. +  File: zutils.info, Node: Zupdate, Next: Argument syntax, Prev: Ztest, Up: Top @@ -925,6 +951,15 @@ compressor can't be run, or comparison fails). the files being ignored and increases the verbosity level. *Note version::. +'-x PATTERN' +'--exclude=PATTERN' + Exclude files matching a shell pattern like '*.o', even if the files + are specified in the command line. A file is considered to match if any + component of the file name matches. For example, '*.o' matches + 'foo.o', 'foo.o/bar' and 'foo/bar.o'. If PATTERN contains a '/', it + matches a corresponding '/' in the file name. For example, 'foo/*.o' + matches 'foo/bar.o'. Multiple '--exclude' options can be specified. + '-0 .. -9' Set the compression level of lzip. By default 'zupdate' passes '-9' to lzip. Custom compression options can be passed to lzip with the option @@ -1031,22 +1066,22 @@ Concept index  Tag Table: Node: Top217 -Node: Introduction1218 -Ref: search-order2365 -Node: Common options3522 -Ref: version4060 -Ref: compressor-requirements6011 -Node: Configuration7470 -Node: Zcat8503 -Node: Zcmp11314 -Node: Zdiff14554 -Node: Zgrep17609 -Node: Ztest23753 -Node: Zupdate26543 -Ref: lz-compressor32558 -Node: Argument syntax33259 -Node: Problems35151 -Node: Concept index35693 +Node: Introduction1215 +Ref: search-order2362 +Node: Common options3519 +Ref: version4057 +Ref: compressor-requirements6008 +Node: Configuration7467 +Node: Zcat8500 +Node: Zcmp11784 +Node: Zdiff15024 +Node: Zgrep18079 +Node: Ztest24706 +Node: Zupdate27969 +Ref: lz-compressor34457 +Node: Argument syntax35158 +Node: Problems37050 +Node: Concept index37592  End Tag Table diff --git a/doc/zutils.texi b/doc/zutils.texi index ea312dc..d0bf05e 100644 --- a/doc/zutils.texi +++ b/doc/zutils.texi @@ -6,8 +6,8 @@ @finalout @c %**end of header -@set UPDATED 5 January 2025 -@set VERSION 1.14 +@set UPDATED 27 May 2025 +@set VERSION 1.15 @dircategory Compression @direntry @@ -350,6 +350,16 @@ for "meta"). Verbose mode. Show error messages. Repeating it increases the verbosity level. @xref{version}. +@item -x @var{pattern} +@itemx --exclude=@var{pattern} +Exclude files matching a shell pattern like @file{*.o}, even if the files +are specified in the command line. A file is considered to match if any +component of the file name matches. For example, @file{*.o} matches +@file{foo.o}, @file{foo.o/bar} and @file{foo/bar.o}. If @var{pattern} +contains a @samp{/}, it matches a corresponding @samp{/} in the file name. +For example, @file{foo/*.o} matches @file{foo/bar.o}. Multiple +@option{--exclude} options can be specified. + @end table @@ -611,7 +621,8 @@ matches were found, and 2 means trouble. @noindent @command{zgrep} supports the following options (Some options only work if -the grep program used supports them. Options -h, -H, -r, -R, and -Z are +the grep program used supports them. Options @option{--exclude}, +@option{-h}, @option{-H}, @option{-r}, @option{-R}, and @option{-Z} are managed by @command{zgrep} and not passed to grep): @table @code @@ -651,6 +662,15 @@ Use @var{pattern} as the pattern to match. @itemx --extended-regexp Interpret @var{pattern} as an extended regular expression (ERE). +@item --exclude=@var{pattern} +Exclude files matching a shell pattern like @file{*.o}, even if the files +are specified in the command line. A file is considered to match if any +component of the file name matches. For example, @file{*.o} matches +@file{foo.o}, @file{foo.o/bar} and @file{foo/bar.o}. If @var{pattern} +contains a @samp{/}, it matches a corresponding @samp{/} in the file name. +For example, @file{foo/*.o} matches @file{foo/bar.o}. Multiple +@option{--exclude} options can be specified. + @item -f @var{file} @itemx --file=@var{file} Obtain patterns from @var{file}, one per line.@* @@ -812,7 +832,7 @@ files are ignored. Note that error detection in the xz format is broken. First, some xz files lack integrity information. Second, not all xz decompressors can -@uref{http://www.nongnu.org/lzip/xz_inadequate.html#fragmented,,check the integrity} +@uref{http://www.nongnu.org/lzip/xz_inadequate.html#checking,,check the integrity} of all xz files. Third, section 2.1.1.2 'Stream Flags' of the @uref{http://tukaani.org/xz/xz-file-format.txt,,xz format specification} allows xz decompressors to produce garbage output without issuing any @@ -864,6 +884,16 @@ recursively, following all symbolic links. Verbose mode. Show the check status for each file processed. Further -v's increase the verbosity level. @xref{version}. +@item -x @var{pattern} +@itemx --exclude=@var{pattern} +Exclude files matching a shell pattern like @file{*.o}, even if the files +are specified in the command line. A file is considered to match if any +component of the file name matches. For example, @file{*.o} matches +@file{foo.o}, @file{foo.o/bar} and @file{foo/bar.o}. If @var{pattern} +contains a @samp{/}, it matches a corresponding @samp{/} in the file name. +For example, @file{foo/*.o} matches @file{foo/bar.o}. Multiple +@option{--exclude} options can be specified. + @end table @@ -1007,6 +1037,16 @@ recursively, following all symbolic links. Verbose mode. Show the files being processed. A second @option{-v} also shows the files being ignored and increases the verbosity level. @xref{version}. +@item -x @var{pattern} +@itemx --exclude=@var{pattern} +Exclude files matching a shell pattern like @file{*.o}, even if the files +are specified in the command line. A file is considered to match if any +component of the file name matches. For example, @file{*.o} matches +@file{foo.o}, @file{foo.o/bar} and @file{foo/bar.o}. If @var{pattern} +contains a @samp{/}, it matches a corresponding @samp{/} in the file name. +For example, @file{foo/*.o} matches @file{foo/bar.o}. Multiple +@option{--exclude} options can be specified. + @item -0 .. -9 Set the compression level of lzip. By default @command{zupdate} passes @option{-9} to lzip. Custom compression options can be passed to lzip with diff --git a/exclude.cc b/exclude.cc new file mode 100644 index 0000000..d07a43c --- /dev/null +++ b/exclude.cc @@ -0,0 +1,49 @@ +/* Zutils - Utilities dealing with compressed files + Copyright (C) 2013-2025 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 2 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 . +*/ + +#include + + +namespace Exclude { + +std::vector< std::string > patterns; // list of patterns + + +void add_pattern( const std::string & arg ) + { patterns.push_back( arg ); } + + +bool excluded( const char * const filename ) + { + if( patterns.empty() ) return false; + const char * p = filename; + do { + for( unsigned i = 0; i < patterns.size(); ++i ) + // ignore a trailing sequence starting with '/' in filename +#ifdef FNM_LEADING_DIR + if( fnmatch( patterns[i].c_str(), p, FNM_LEADING_DIR ) == 0 ) return true; +#else + if( fnmatch( patterns[i].c_str(), p, 0 ) == 0 || + fnmatch( ( patterns[i] + "/*" ).c_str(), p, 0 ) == 0 ) return true; +#endif + while( *p && *p != '/' ) ++p; // skip component + while( *p == '/' ) ++p; // skip slashes + } while( *p ); + return false; + } + +} // end namespace Exclude diff --git a/rc.h b/rc.h index d3b5adb..4c9b830 100644 --- a/rc.h +++ b/rc.h @@ -61,6 +61,8 @@ void show_error( const char * const msg, const int errcode = 0, const bool help = false ); void show_file_error( const char * const filename, const char * const msg, const int errcode = 0 ); +inline void show_stdout_error( const int errcode = 0 ) + { show_file_error( "(stdout)", "Write error", errcode ); } void internal_error( const char * const msg ); void show_option_error( const char * const arg, const char * const msg, const char * const option_name ); diff --git a/recursive.cc b/recursive.cc index cb1f2f7..089653d 100644 --- a/recursive.cc +++ b/recursive.cc @@ -63,6 +63,7 @@ bool next_filename( std::list< std::string > & filenames, if( ignore_stdin ) continue; input_filename = "."; return true; } + if( Exclude::excluded( input_filename.c_str() ) ) continue; // skip file struct stat st; if( stat( input_filename.c_str(), &st ) == 0 && S_ISDIR( st.st_mode ) ) { diff --git a/testsuite/check.sh b/testsuite/check.sh index ba7b7c7..07930cd 100755 --- a/testsuite/check.sh +++ b/testsuite/check.sh @@ -43,8 +43,8 @@ for i in ${compressors}; do $i in || compressor_needed printf "Hello World!\n" > hello || framework_failure $i hello || compressor_needed - touch zero || framework_failure - $i zero || compressor_needed + touch em || framework_failure + $i em || compressor_needed done cp "${testdir}"/test.txt in || framework_failure @@ -55,8 +55,8 @@ cp in -- -in- || framework_failure cp in.lz -- -in-.lz || framework_failure cp in.lz lz_only.lz || framework_failure cat in in in in in in > in6 || framework_failure -bad0_lz="${testdir}"/zero_bad_crc.lz -bad0_gz="${testdir}"/zero_bad_crc.gz +bad0_lz="${testdir}"/em_bad_crc.lz +bad0_gz="${testdir}"/em_bad_crc.gz bad1_lz="${testdir}"/test_bad_crc.lz touch empty empty.bz2 empty.gz empty.lz || framework_failure fail=0 @@ -75,9 +75,9 @@ for i in ${extensions}; do cmp in out || test_failed $LINENO $i "${ZCAT}" -N in.$i empty.$i > out || test_failed $LINENO $i cmp in out || test_failed $LINENO $i - "${ZCAT}" -N zero.$i in.$i > out || test_failed $LINENO $i + "${ZCAT}" -N em.$i in.$i > out || test_failed $LINENO $i cmp in out || test_failed $LINENO $i - "${ZCAT}" -N in.$i zero.$i > out || test_failed $LINENO $i + "${ZCAT}" -N in.$i em.$i > out || test_failed $LINENO $i cmp in out || test_failed $LINENO $i "${ZCAT}" -N --format=un in.$i > out || test_failed $LINENO $i cmp in out || test_failed $LINENO $i @@ -163,7 +163,7 @@ for i in ${extensions}; do "${ZCMP}" -N -i 1kB:1000 -n 500 in6 in.$i || test_failed $LINENO $i "${ZCMP}" -N -i 1KiB:1024 -n 50 in.$i in6 || test_failed $LINENO $i "${ZCMP}" -N empty empty.$i || test_failed $LINENO $i - "${ZCMP}" -N empty zero.$i || test_failed $LINENO $i + "${ZCMP}" -N empty em.$i || test_failed $LINENO $i done "${ZCMP}" -N -q in in6 @@ -248,7 +248,7 @@ for i in ${extensions}; do "${ZDIFF}" -N in.$i in --force-format=$i, > /dev/null || test_failed $LINENO $i "${ZDIFF}" -N empty empty.$i > /dev/null || test_failed $LINENO $i - "${ZDIFF}" -N empty zero.$i > /dev/null || test_failed $LINENO $i + "${ZDIFF}" -N empty em.$i > /dev/null || test_failed $LINENO $i done "${ZDIFF}" -N in in6 > /dev/null @@ -329,9 +329,12 @@ for i in ${extensions}; do "${ZGREP}" -N --force-format=$i "GNU" in 2> /dev/null [ $? = 2 ] || test_failed $LINENO $i "${ZGREP}" -N "nx_pattern" empty.$i && test_failed $LINENO $i - "${ZGREP}" -N "nx_pattern" zero.$i && test_failed $LINENO $i + "${ZGREP}" -N "nx_pattern" em.$i && test_failed $LINENO $i done +printf "GNU\n" > patfile || framework_failure +"${ZGREP}" -N -f patfile in > /dev/null || test_failed $LINENO +rm -f patfile || framework_failure "${ZGREP}" -N "nx_pattern" empty && test_failed $LINENO "${ZGREP}" -N pin.tar4 -e "GNU" > /dev/null || test_failed $LINENO "${ZGREP}" -N "GNU" < pin.tar4 > /dev/null || test_failed $LINENO @@ -592,7 +595,7 @@ cp in.gz 'name with spaces.gz' || framework_failure "${ZCMP}" -N in 'name with spaces.lz' || test_failed $LINENO rm -f 'name with spaces.lz' || framework_failure -cp zero.gz z.gz || framework_failure +cp em.gz z.gz || framework_failure "${ZUPDATE}" -N -0 -q z.gz || test_failed $LINENO [ ! -e z.gz ] || test_failed $LINENO [ -e z.lz ] || test_failed $LINENO diff --git a/testsuite/zero_bad_crc.gz b/testsuite/em_bad_crc.gz similarity index 100% rename from testsuite/zero_bad_crc.gz rename to testsuite/em_bad_crc.gz diff --git a/testsuite/zero_bad_crc.lz b/testsuite/em_bad_crc.lz similarity index 100% rename from testsuite/zero_bad_crc.lz rename to testsuite/em_bad_crc.lz diff --git a/zcat.cc b/zcat.cc index 58a6888..5756517 100644 --- a/zcat.cc +++ b/zcat.cc @@ -42,6 +42,7 @@ namespace { +#include "exclude.cc" #include "recursive.cc" #include "zcatgrep.cc" @@ -126,6 +127,7 @@ void show_help() " -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" + " -x, --exclude= exclude files matching a shell pattern\n" " --bz2= set compressor and options for bzip2 format\n" " --gz= set compressor and options for gzip format\n" " --lz= set compressor and options for lzip format\n" @@ -155,7 +157,7 @@ bool do_cat( const int infd, const int buffer_size, if( outpos >= buffer_size ) { if( writeblock( STDOUT_FILENO, outbuf, outpos ) != outpos ) - { show_error( "Write error", errno ); return false; } + { show_stdout_error( errno ); return false; } outpos = 0; } if( inpos > rd ) // inbuf is empty @@ -169,7 +171,7 @@ bool do_cat( const int infd, const int buffer_size, if( rd == 0 ) { if( writeblock( STDOUT_FILENO, outbuf, outpos ) != outpos ) - { show_error( "Write error", errno ); return false; } + { show_stdout_error( errno ); return false; } outpos = 0; return true; } @@ -261,7 +263,6 @@ bool cat( int infd, const int format_index, const std::string & input_filename, int main( const int argc, const char * const argv[] ) { - enum { verbose_opt = 256, bz2_opt, gz_opt, lz_opt, xz_opt, zst_opt }; int format_index = -1; // undefined int recursive = 0; // 1 = '-r', 2 = '-R' std::list< std::string > filenames; @@ -269,6 +270,7 @@ int main( const int argc, const char * const argv[] ) program_name = "zcat"; invocation_name = ( argc > 0 ) ? argv[0] : program_name; + enum { opt_bz2 = 256, opt_gz, opt_lz, opt_verbose, opt_xz, opt_zst }; const Arg_parser::Option options[] = { { 'A', "show-all", Arg_parser::no }, // cat @@ -293,12 +295,13 @@ 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 }, - { verbose_opt, "verbose", Arg_parser::no }, - { bz2_opt, "bz2", Arg_parser::yes }, - { gz_opt, "gz", Arg_parser::yes }, - { lz_opt, "lz", Arg_parser::yes }, - { xz_opt, "xz", Arg_parser::yes }, - { zst_opt, "zst", Arg_parser::yes }, + { 'x', "exclude", Arg_parser::yes }, + { opt_verbose, "verbose", Arg_parser::no }, + { opt_bz2, "bz2", Arg_parser::yes }, + { opt_gz, "gz", Arg_parser::yes }, + { opt_lz, "lz", Arg_parser::yes }, + { opt_xz, "xz", Arg_parser::yes }, + { opt_zst, "zst", Arg_parser::yes }, { 0, 0, Arg_parser::no } }; const Arg_parser parser( argc, argv, options ); @@ -341,12 +344,13 @@ int main( const int argc, const char * const argv[] ) case 'T': cat_options.show_tabs = true; break; case 'v': cat_options.show_nonprinting = true; break; case 'V': show_version(); return 0; - case verbose_opt: if( verbosity < 4 ) ++verbosity; break; - case bz2_opt: parse_compressor( arg, pn, fmt_bz2, 1 ); break; - case gz_opt: parse_compressor( arg, pn, fmt_gz, 1 ); break; - case lz_opt: parse_compressor( arg, pn, fmt_lz, 1 ); break; - case xz_opt: parse_compressor( arg, pn, fmt_xz, 1 ); break; - case zst_opt: parse_compressor( arg, pn, fmt_zst, 1 ); break; + case 'x': Exclude::add_pattern( arg ); break; + case opt_verbose: if( verbosity < 4 ) ++verbosity; break; + case opt_bz2: parse_compressor( arg, pn, fmt_bz2, 1 ); break; + case opt_gz: parse_compressor( arg, pn, fmt_gz, 1 ); break; + case opt_lz: parse_compressor( arg, pn, fmt_lz, 1 ); break; + case opt_xz: parse_compressor( arg, pn, fmt_xz, 1 ); break; + case opt_zst: parse_compressor( arg, pn, fmt_zst, 1 ); break; default: internal_error( "uncaught option." ); } } // end process options diff --git a/zcmp.cc b/zcmp.cc index 3bc873d..6f96efb 100644 --- a/zcmp.cc +++ b/zcmp.cc @@ -93,17 +93,17 @@ void show_help() // separate numbers of 5 or more digits in groups of 3 digits using '_' -const char * format_num3( long long num ) +const char * format_num3p( long long num ) { enum { buffers = 8, bufsize = 4 * sizeof num, n = 10 }; const char * const si_prefix = "kMGTPEZYRQ"; const char * const binary_prefix = "KMGTPEZYRQ"; - static char buffer[buffers][bufsize]; // circle of static buffers for printf + static char buffer[buffers][bufsize]; // circle of buffers for printf static int current = 0; char * const buf = buffer[current++]; current %= buffers; char * p = buf + bufsize - 1; // fill the buffer backwards - *p = 0; // terminator + *p = 0; // terminator const bool negative = num < 0; if( num > 9999 || num < -9999 ) { @@ -180,8 +180,8 @@ long long getnum( const char * const arg, const char * const option_name, { if( verbosity >= 0 ) std::fprintf( stderr, "%s: '%s': Value out of limits [%s,%s] in " - "option '%s'.\n", program_name, arg, format_num3( llimit ), - format_num3( ulimit ), option_name ); + "option '%s'.\n", program_name, arg, format_num3p( llimit ), + format_num3p( ulimit ), option_name ); std::exit( 2 ); } if( tailp ) *tailp = tail; @@ -372,7 +372,6 @@ done: int main( const int argc, const char * const argv[] ) { - enum { bz2_opt = 256, gz_opt, lz_opt, xz_opt, zst_opt }; // number of initial bytes ignored for each file long long ignore_initial[2] = { 0, 0 }; long long max_size = -1; // < 0 means unlimited size @@ -384,6 +383,7 @@ int main( const int argc, const char * const argv[] ) program_name = "zcmp"; invocation_name = ( argc > 0 ) ? argv[0] : program_name; + enum { opt_bz2 = 256, opt_gz, opt_lz, opt_xz, opt_zst }; const Arg_parser::Option options[] = { { 'b', "print-bytes", Arg_parser::no }, @@ -400,11 +400,11 @@ int main( const int argc, const char * const argv[] ) { 's', "script", Arg_parser::no }, { 'v', "verbose", Arg_parser::no }, { 'V', "version", Arg_parser::no }, - { bz2_opt, "bz2", Arg_parser::yes }, - { gz_opt, "gz", Arg_parser::yes }, - { lz_opt, "lz", Arg_parser::yes }, - { xz_opt, "xz", Arg_parser::yes }, - { zst_opt, "zst", Arg_parser::yes }, + { opt_bz2, "bz2", Arg_parser::yes }, + { opt_gz, "gz", Arg_parser::yes }, + { opt_lz, "lz", Arg_parser::yes }, + { opt_xz, "xz", Arg_parser::yes }, + { opt_zst, "zst", Arg_parser::yes }, { 0, 0, Arg_parser::no } }; const Arg_parser parser( argc, argv, options ); @@ -436,11 +436,11 @@ int main( const int argc, const char * const argv[] ) case 's': scripted = true; break; case 'v': if( verbosity < 4 ) ++verbosity; break; case 'V': show_version(); return 0; - case bz2_opt: parse_compressor( sarg, pn, fmt_bz2 ); break; - case gz_opt: parse_compressor( sarg, pn, fmt_gz ); break; - case lz_opt: parse_compressor( sarg, pn, fmt_lz ); break; - case xz_opt: parse_compressor( sarg, pn, fmt_xz ); break; - case zst_opt: parse_compressor( sarg, pn, fmt_zst ); break; + case opt_bz2: parse_compressor( sarg, pn, fmt_bz2 ); break; + case opt_gz: parse_compressor( sarg, pn, fmt_gz ); break; + case opt_lz: parse_compressor( sarg, pn, fmt_lz ); break; + case opt_xz: parse_compressor( sarg, pn, fmt_xz ); break; + case opt_zst: parse_compressor( sarg, pn, fmt_zst ); break; default: internal_error( "uncaught option." ); } } // end process options diff --git a/zdiff.cc b/zdiff.cc index b4282e3..9c1f3d9 100644 --- a/zdiff.cc +++ b/zdiff.cc @@ -257,12 +257,12 @@ void set_signals() int main( const int argc, const char * const argv[] ) { - enum { bz2_opt = 256, gz_opt, lz_opt, xz_opt, zst_opt }; std::vector< const char * > diff_args; // args to diff, maybe empty int format_types[2] = { -1, -1 }; // < 0 means undefined program_name = "zdiff"; invocation_name = ( argc > 0 ) ? argv[0] : program_name; + enum { opt_bz2 = 256, opt_gz, opt_lz, opt_xz, opt_zst }; const Arg_parser::Option options[] = { { 'a', "text", Arg_parser::no }, @@ -289,11 +289,11 @@ int main( const int argc, const char * const argv[] ) { 'w', "ignore-all-space", Arg_parser::no }, { 'W', "width", Arg_parser::yes }, { 'y', "side-by-side", Arg_parser::no }, - { bz2_opt, "bz2", Arg_parser::yes }, - { gz_opt, "gz", Arg_parser::yes }, - { lz_opt, "lz", Arg_parser::yes }, - { xz_opt, "xz", Arg_parser::yes }, - { zst_opt, "zst", Arg_parser::yes }, + { opt_bz2, "bz2", Arg_parser::yes }, + { opt_gz, "gz", Arg_parser::yes }, + { opt_lz, "lz", Arg_parser::yes }, + { opt_xz, "xz", Arg_parser::yes }, + { opt_zst, "zst", Arg_parser::yes }, { 0, 0, Arg_parser::no } }; const Arg_parser parser( argc, argv, options ); @@ -336,11 +336,11 @@ int main( const int argc, const char * const argv[] ) case 'w': diff_args.push_back( "-w" ); break; case 'W': diff_args.push_back( "-W" ); diff_args.push_back( arg ); break; case 'y': diff_args.push_back( "-y" ); break; - case bz2_opt: parse_compressor( sarg, pn, fmt_bz2 ); break; - case gz_opt: parse_compressor( sarg, pn, fmt_gz ); break; - case lz_opt: parse_compressor( sarg, pn, fmt_lz ); break; - case xz_opt: parse_compressor( sarg, pn, fmt_xz ); break; - case zst_opt: parse_compressor( sarg, pn, fmt_zst ); break; + case opt_bz2: parse_compressor( sarg, pn, fmt_bz2 ); break; + case opt_gz: parse_compressor( sarg, pn, fmt_gz ); break; + case opt_lz: parse_compressor( sarg, pn, fmt_lz ); break; + case opt_xz: parse_compressor( sarg, pn, fmt_xz ); break; + case opt_zst: parse_compressor( sarg, pn, fmt_zst ); break; default: internal_error( "uncaught option." ); } } // end process options diff --git a/zgrep.cc b/zgrep.cc index a7e498f..a0e96de 100644 --- a/zgrep.cc +++ b/zgrep.cc @@ -42,6 +42,7 @@ namespace { +#include "exclude.cc" #include "recursive.cc" #include "zcatgrep.cc" @@ -77,6 +78,7 @@ void show_help() " --color[=] show matched strings in color\n" " -e, --regexp= use as the pattern to match\n" " -E, --extended-regexp is an extended regular expression\n" + " --exclude= exclude files matching a shell pattern\n" " -f, --file= obtain patterns from \n" " -F, --fixed-strings is a set of newline-separated strings\n" " -G, --basic-regexp is a basic regular expression (default)\n" @@ -186,7 +188,7 @@ int zgrep_file( int infd, const int format_index, { line_begin = true; if( line_buffered ) std::fflush( stdout ); } } else if( std::fwrite( buffer, 1, size, stdout ) != (unsigned)size ) - { std::fflush( stdout ); show_error( "Write error", errno ); return 2; } + { std::fflush( stdout ); show_stdout_error( errno ); return 2; } } } std::fflush( stdout ); @@ -202,6 +204,8 @@ int zgrep_file( int infd, const int format_index, { show_close_error(); return 2; } if( close( fda[0] ) != 0 ) { show_close_error( GREP ); return 2; } + if( retval == 0 && std::ferror( stdout ) ) + { show_stdout_error(); retval = 2; } return retval; } @@ -210,8 +214,6 @@ int zgrep_file( int infd, const int format_index, int main( const int argc, const char * const argv[] ) { - enum { help_opt = 256, verbose_opt, color_opt, label_opt, linebuf_opt, - bz2_opt, gz_opt, lz_opt, xz_opt, zst_opt }; int format_index = -1; // undefined int list_mode = 0; // 1 = list matches, -1 = list non-matches int recursive = 0; // 1 = '-r', 2 = '-R' @@ -228,6 +230,8 @@ int main( const int argc, const char * const argv[] ) program_name = "zgrep"; invocation_name = ( argc > 0 ) ? argv[0] : program_name; + enum { opt_bz2 = 256, opt_color, opt_exc, opt_gz, opt_help, opt_label, + opt_linebuf, opt_lz, opt_verbose, opt_xz, opt_zst }; const Arg_parser::Option options[] = { { 'a', "text", Arg_parser::no }, // grep GNU @@ -238,7 +242,7 @@ int main( const int argc, const char * const argv[] ) { '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', "file", Arg_parser::yes }, // grep { 'F', "fixed-strings", Arg_parser::no }, // grep { 'G', "basic-regexp", Arg_parser::no }, // grep GNU { 'h', "no-filename", Arg_parser::no }, // grep GNU @@ -266,16 +270,17 @@ int main( const int argc, const char * const argv[] ) { 'w', "word-regexp", Arg_parser::no }, // grep GNU { 'x', "line-regexp", Arg_parser::no }, // grep { 'Z', "null", Arg_parser::no }, // grep GNU - { help_opt, "help", Arg_parser::no }, - { verbose_opt, "verbose", Arg_parser::no }, - { color_opt, "color", Arg_parser::maybe }, - { label_opt, "label", Arg_parser::yes }, - { linebuf_opt, "line-buffered", Arg_parser::no }, - { bz2_opt, "bz2", Arg_parser::yes }, - { gz_opt, "gz", Arg_parser::yes }, - { lz_opt, "lz", Arg_parser::yes }, - { xz_opt, "xz", Arg_parser::yes }, - { zst_opt, "zst", Arg_parser::yes }, + { opt_color, "color", Arg_parser::maybe }, + { opt_exc, "exclude", Arg_parser::yes }, + { opt_help, "help", Arg_parser::no }, + { opt_label, "label", Arg_parser::yes }, + { opt_linebuf, "line-buffered", Arg_parser::no }, + { opt_verbose, "verbose", Arg_parser::no }, + { opt_bz2, "bz2", Arg_parser::yes }, + { opt_gz, "gz", Arg_parser::yes }, + { opt_lz, "lz", Arg_parser::yes }, + { opt_xz, "xz", Arg_parser::yes }, + { opt_zst, "zst", Arg_parser::yes }, { 0, 0, Arg_parser::no } }; const Arg_parser parser( argc, argv, options ); @@ -332,21 +337,22 @@ int main( const int argc, const char * const argv[] ) case 'w': grep_args.push_back( "-w" ); break; case 'x': grep_args.push_back( "-x" ); break; case 'Z': z_null = true; break; - case help_opt: show_help(); return 0; - case verbose_opt: no_messages = false; if( verbosity < 4 ) ++verbosity; + case opt_help: show_help(); return 0; + case opt_exc: Exclude::add_pattern( sarg ); break; + case opt_verbose: no_messages = false; if( verbosity < 4 ) ++verbosity; break; - case color_opt: color_option = "--color"; + case opt_color: color_option = "--color"; if( !sarg.empty() ) { color_option += '='; color_option += sarg; } break; - case label_opt: label_option = "--label="; label_option += sarg; + case opt_label: label_option = "--label="; label_option += sarg; label = arg; break; - case linebuf_opt: grep_args.push_back( "--line-buffered" ); + case opt_linebuf: grep_args.push_back( "--line-buffered" ); line_buffered = true; break; - case bz2_opt: parse_compressor( sarg, pn, fmt_bz2 ); break; - case gz_opt: parse_compressor( sarg, pn, fmt_gz ); break; - case lz_opt: parse_compressor( sarg, pn, fmt_lz ); break; - case xz_opt: parse_compressor( sarg, pn, fmt_xz ); break; - case zst_opt: parse_compressor( sarg, pn, fmt_zst ); break; + case opt_bz2: parse_compressor( sarg, pn, fmt_bz2 ); break; + case opt_gz: parse_compressor( sarg, pn, fmt_gz ); break; + case opt_lz: parse_compressor( sarg, pn, fmt_lz ); break; + case opt_xz: parse_compressor( sarg, pn, fmt_xz ); break; + case opt_zst: parse_compressor( sarg, pn, fmt_zst ); break; default: internal_error( "uncaught option." ); } } // end process options @@ -404,14 +410,12 @@ int main( const int argc, const char * const argv[] ) if( close( infd ) != 0 ) { show_file_error( input_filename.c_str(), "Error closing input file", errno ); error = true; } - if( retval == 0 && verbosity < 0 ) break; + if( ( retval == 0 && verbosity < 0 ) || // match + ( retval == 2 && verbosity >= 0 ) ) break; // error } - if( std::fclose( stdout ) != 0 ) - { - show_error( "Error closing stdout", errno ); - error = true; - } + if( std::fclose( stdout ) != 0 && !error ) + { show_error( "Error closing stdout", errno ); error = true; } if( error && ( retval != 0 || verbosity >= 0 ) ) retval = 2; return retval; } diff --git a/ztest.cc b/ztest.cc index 9394276..44fe0ba 100644 --- a/ztest.cc +++ b/ztest.cc @@ -46,6 +46,7 @@ namespace { +#include "exclude.cc" #include "recursive.cc" void show_help() @@ -84,6 +85,7 @@ void show_help() " -r, --recursive operate recursively on directories\n" " -R, --dereference-recursive recursively follow symbolic links\n" " -v, --verbose be verbose (a 2nd -v gives more)\n" + " -x, --exclude= exclude files matching a shell pattern\n" " --bz2= set compressor and options for bzip2 format\n" " --gz= set compressor and options for gzip format\n" " --lz= set compressor and options for lzip format\n" @@ -247,7 +249,6 @@ int ztest_file( const int infd, int format_index, int main( const int argc, const char * const argv[] ) { - enum { bz2_opt = 256, gz_opt, lz_opt, xz_opt, zst_opt }; int format_index = -1; // undefined int recursive = 0; // 1 = '-r', 2 = '-R' std::list< std::string > filenames; @@ -255,6 +256,7 @@ int main( const int argc, const char * const argv[] ) program_name = "ztest"; invocation_name = ( argc > 0 ) ? argv[0] : program_name; + enum { opt_bz2 = 256, opt_gz, opt_lz, opt_xz, opt_zst }; const Arg_parser::Option options[] = { { 'h', "help", Arg_parser::no }, @@ -266,11 +268,12 @@ int main( const int argc, const char * const argv[] ) { 'R', "dereference-recursive", Arg_parser::no }, { 'v', "verbose", Arg_parser::no }, { 'V', "version", Arg_parser::no }, - { bz2_opt, "bz2", Arg_parser::yes }, - { gz_opt, "gz", Arg_parser::yes }, - { lz_opt, "lz", Arg_parser::yes }, - { xz_opt, "xz", Arg_parser::yes }, - { zst_opt, "zst", Arg_parser::yes }, + { 'x', "exclude", Arg_parser::yes }, + { opt_bz2, "bz2", Arg_parser::yes }, + { opt_gz, "gz", Arg_parser::yes }, + { opt_lz, "lz", Arg_parser::yes }, + { opt_xz, "xz", Arg_parser::yes }, + { opt_zst, "zst", Arg_parser::yes }, { 0, 0, Arg_parser::no } }; const Arg_parser parser( argc, argv, options ); @@ -298,11 +301,12 @@ int main( const int argc, const char * const argv[] ) case 'v': if( verbosity < 4 ) ++verbosity; ztest_args.push_back( "-v" ); break; case 'V': show_version(); return 0; - case bz2_opt: parse_compressor( arg, pn, fmt_bz2, 1 ); break; - case gz_opt: parse_compressor( arg, pn, fmt_gz, 1 ); break; - case lz_opt: parse_compressor( arg, pn, fmt_lz, 1 ); break; - case xz_opt: parse_compressor( arg, pn, fmt_xz, 1 ); break; - case zst_opt: parse_compressor( arg, pn, fmt_zst, 1 ); break; + case 'x': Exclude::add_pattern( arg ); break; + case opt_bz2: parse_compressor( arg, pn, fmt_bz2, 1 ); break; + case opt_gz: parse_compressor( arg, pn, fmt_gz, 1 ); break; + case opt_lz: parse_compressor( arg, pn, fmt_lz, 1 ); break; + case opt_xz: parse_compressor( arg, pn, fmt_xz, 1 ); break; + case opt_zst: parse_compressor( arg, pn, fmt_zst, 1 ); break; default: internal_error( "uncaught option." ); } } // end process options diff --git a/zupdate.cc b/zupdate.cc index 21108e1..178d9b4 100644 --- a/zupdate.cc +++ b/zupdate.cc @@ -51,6 +51,7 @@ namespace { +#include "exclude.cc" #include "recursive.cc" void show_help() @@ -94,6 +95,7 @@ void show_help() " -r, --recursive operate recursively on directories\n" " -R, --dereference-recursive recursively follow symbolic links\n" " -v, --verbose be verbose (a 2nd -v gives more)\n" + " -x, --exclude= exclude files matching a shell pattern\n" " -0 .. -9 set compression level [default 9]\n" " --bz2= set compressor and options for bzip2 format\n" " --gz= set compressor and options for gzip format\n" @@ -382,7 +384,6 @@ int zupdate_file( const std::string & name, const char * const lzip_name, int main( const int argc, const char * const argv[] ) { - enum { bz2_opt = 256, gz_opt, lz_opt, xz_opt, zst_opt }; int recursive = 0; // 1 = '-r', 2 = '-R' std::string destdir; // write recompressed files here std::vector< std::string > lzip_args2; // args to lzip, maybe empty @@ -394,6 +395,7 @@ int main( const int argc, const char * const argv[] ) program_name = "zupdate"; invocation_name = ( argc > 0 ) ? argv[0] : program_name; + enum { opt_bz2 = 256, opt_gz, opt_lz, opt_xz, opt_zst }; const Arg_parser::Option options[] = { { '0', 0, Arg_parser::no }, @@ -420,11 +422,12 @@ int main( const int argc, const char * const argv[] ) { 'R', "dereference-recursive", Arg_parser::no }, { 'v', "verbose", Arg_parser::no }, { 'V', "version", Arg_parser::no }, - { bz2_opt, "bz2", Arg_parser::yes }, - { gz_opt, "gz", Arg_parser::yes }, - { lz_opt, "lz", Arg_parser::yes }, - { xz_opt, "xz", Arg_parser::yes }, - { zst_opt, "zst", Arg_parser::yes }, + { 'x', "exclude", Arg_parser::yes }, + { opt_bz2, "bz2", Arg_parser::yes }, + { opt_gz, "gz", Arg_parser::yes }, + { opt_lz, "lz", Arg_parser::yes }, + { opt_xz, "xz", Arg_parser::yes }, + { opt_zst, "zst", Arg_parser::yes }, { 0, 0, Arg_parser::no } }; const Arg_parser parser( argc, argv, options ); @@ -459,11 +462,12 @@ int main( const int argc, const char * const argv[] ) case 'R': recursive = 2; break; case 'v': if( verbosity < 4 ) ++verbosity; break; case 'V': show_version(); return 0; - case bz2_opt: parse_compressor( arg, pn, fmt_bz2, 1 ); break; - case gz_opt: parse_compressor( arg, pn, fmt_gz, 1 ); break; - case lz_opt: parse_compressor( arg, pn, fmt_lz, 1 ); break; - case xz_opt: parse_compressor( arg, pn, fmt_xz, 1 ); break; - case zst_opt: parse_compressor( arg, pn, fmt_zst, 1 ); break; + case 'x': Exclude::add_pattern( arg ); break; + case opt_bz2: parse_compressor( arg, pn, fmt_bz2, 1 ); break; + case opt_gz: parse_compressor( arg, pn, fmt_gz, 1 ); break; + case opt_lz: parse_compressor( arg, pn, fmt_lz, 1 ); break; + case opt_xz: parse_compressor( arg, pn, fmt_xz, 1 ); break; + case opt_zst: parse_compressor( arg, pn, fmt_zst, 1 ); break; default: internal_error( "uncaught option." ); } } // end process options