summaryrefslogtreecommitdiffstats
path: root/zcat.cc
diff options
context:
space:
mode:
Diffstat (limited to 'zcat.cc')
-rw-r--r--zcat.cc147
1 files changed, 78 insertions, 69 deletions
diff --git a/zcat.cc b/zcat.cc
index 8ef1929..5d400ad 100644
--- a/zcat.cc
+++ b/zcat.cc
@@ -1,5 +1,5 @@
/* Zcat - decompress and concatenate files to standard output
- Copyright (C) 2010-2018 Antonio Diaz Diaz.
+ Copyright (C) 2010-2019 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
@@ -93,36 +93,39 @@ void show_help()
std::printf( "Zcat copies each given file to standard output. If any given file is\n"
"compressed, its decompressed content is used. If a given file does not\n"
"exist, and its name does not end with one of the known extensions, zcat\n"
- "tries the compressed file names corresponding to the supported formats.\n"
- "\nIf no files are specified, or if a file is specified as '-', data are\n"
- "read from standard input, decompressed if needed, and sent to standard\n"
- "output. Data read from standard input must be of the same type; all\n"
- "uncompressed or all in the same compression format.\n"
- "\nThe supported formats are bzip2, gzip, lzip and xz.\n"
+ "tries the compressed file names corresponding to the formats supported.\n"
+ "\nIf a file is specified as '-', data are read from standard input,\n"
+ "decompressed if needed, and sent to standard output. Data read from\n"
+ "standard input must be of the same type; all uncompressed or all in the\n"
+ "same compression format.\n"
+ "\nIf no files are specified, recursive searches examine the current\n"
+ "working directory, and nonrecursive searches read standard input.\n"
+ "\nThe formats supported are bzip2, gzip, lzip and xz.\n"
"\nUsage: zcat [options] [files]\n"
"\nExit status is 0 if no errors occurred, non-zero 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"
- " -M, --format=<list> process only the formats in <list>\n"
- " -n, --number number all output lines\n"
- " -N, --no-rcfile don't read runtime configuration file\n"
- " -O, --force-format=<fmt> force given format (bz2, gz, lz, xz)\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"
- " --bz2=<command> set compressor and options for bzip2 format\n"
- " --gz=<command> set compressor and options for gzip format\n"
- " --lz=<command> set compressor and options for lzip format\n"
- " --xz=<command> set compressor and options for xz format\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"
+ " -M, --format=<list> process only the formats in <list>\n"
+ " -n, --number number all output lines\n"
+ " -N, --no-rcfile don't read runtime configuration file\n"
+ " -O, --force-format=<fmt> force given format (bz2, gz, lz, xz)\n"
+ " -q, --quiet suppress all messages\n"
+ " -r, --recursive operate recursively on directories\n"
+ " -R, --dereference-recursive recursively follow symbolic links\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"
+ " --bz2=<command> set compressor and options for bzip2 format\n"
+ " --gz=<command> set compressor and options for gzip format\n"
+ " --lz=<command> set compressor and options for lzip format\n"
+ " --xz=<command> set compressor and options for xz format\n" );
show_help_addr();
}
@@ -153,7 +156,7 @@ int do_cat( const int infd, const int buffer_size,
rd = readblock( infd, inbuf, buffer_size );
if( rd != buffer_size && errno )
{
- show_error2( "Error reading file", input_filename.c_str() );
+ show_file_error( input_filename.c_str(), "Read error", errno );
return 1;
}
if( rd == 0 )
@@ -226,11 +229,13 @@ int do_cat( const int infd, const int buffer_size,
int cat( int infd, const int format_index, const std::string & input_filename,
const Cat_options & cat_options )
{
- enum { buffer_size = 4096 };
- // buffer with space for sentinel newline at the end
+ enum { buffer_size = 4096, outbuf_size = (5 * buffer_size) + 256 + 1 };
+ // buffer with space for sentinel newline at the end
uint8_t * const inbuf = new uint8_t[buffer_size+1];
- // buffer with space for character quoting and 255-digit line number
- uint8_t * const outbuf = new uint8_t[(4*buffer_size)+256];
+ // buffer with space for character quoting, 255-digit line number,
+ // worst case flushing respect to inbuf, and a canary byte.
+ uint8_t * const outbuf = new uint8_t[outbuf_size];
+ outbuf[outbuf_size-1] = 0;
int retval = 0;
Children children;
if( !set_data_feeder( input_filename, &infd, children, format_index ) )
@@ -243,6 +248,8 @@ int cat( int infd, const int format_index, const std::string & input_filename,
if( retval == 0 && close( infd ) != 0 )
{ show_close_error(); retval = 1; }
+ if( outbuf[outbuf_size-1] != 0 )
+ internal_error( "buffer overflow." );
delete[] outbuf; delete[] inbuf;
return retval;
}
@@ -253,10 +260,8 @@ int 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 };
- int infd = -1;
int format_index = -1;
- bool recursive = false;
- std::string input_filename;
+ int recursive = 0; // 1 = '-r', 2 = '-R'
std::list< std::string > filenames;
Cat_options cat_options;
invocation_name = argv[0];
@@ -264,33 +269,34 @@ int main( const int argc, const char * const argv[] )
const Arg_parser::Option options[] =
{
- { 'A', "show-all", Arg_parser::no }, // cat
- { 'b', "number-nonblank", Arg_parser::no }, // cat
- { 'c', "stdout", Arg_parser::no }, // gzip
- { 'd', "decompress", Arg_parser::no }, // gzip
- { 'e', 0, Arg_parser::no }, // cat
- { 'E', "show-ends", Arg_parser::no }, // cat
- { 'f', "force", Arg_parser::no }, // gzip
- { 'h', "help", Arg_parser::no },
- { 'l', "list", Arg_parser::no }, // gzip
- { 'L', "license", Arg_parser::no }, // gzip
- { 'M', "format", Arg_parser::yes },
- { 'n', "number", Arg_parser::no }, // cat
- { 'N', "no-rcfile", Arg_parser::no },
- { 'O', "force-format", Arg_parser::yes },
- { 'q', "quiet", Arg_parser::no },
- { 'r', "recursive", Arg_parser::no },
- { 's', "squeeze-blank", Arg_parser::no }, // cat
- { 't', 0, Arg_parser::no }, // cat
- { 'T', "show-tabs", Arg_parser::no }, // cat
- { 'v', "show-nonprinting", Arg_parser::no }, // cat
- { 'V', "version", Arg_parser::no },
- { 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 },
- { 0 , 0, Arg_parser::no } };
+ { 'A', "show-all", Arg_parser::no }, // cat
+ { 'b', "number-nonblank", Arg_parser::no }, // cat
+ { 'c', "stdout", Arg_parser::no }, // gzip
+ { 'd', "decompress", Arg_parser::no }, // gzip
+ { 'e', 0, Arg_parser::no }, // cat
+ { 'E', "show-ends", Arg_parser::no }, // cat
+ { 'f', "force", Arg_parser::no }, // gzip
+ { 'h', "help", Arg_parser::no },
+ { 'l', "list", Arg_parser::no }, // gzip
+ { 'L', "license", Arg_parser::no }, // gzip
+ { 'M', "format", Arg_parser::yes },
+ { 'n', "number", Arg_parser::no }, // cat
+ { 'N', "no-rcfile", Arg_parser::no },
+ { 'O', "force-format", Arg_parser::yes },
+ { 'q', "quiet", Arg_parser::no },
+ { 'r', "recursive", Arg_parser::no },
+ { 'R', "dereference-recursive", Arg_parser::no },
+ { 's', "squeeze-blank", Arg_parser::no }, // cat
+ { 't', 0, Arg_parser::no }, // cat
+ { 'T', "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 },
+ { 0 , 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option
@@ -312,7 +318,7 @@ int main( const int argc, const char * const argv[] )
case 'b': cat_options.number_lines = 1; break;
case 'c': break;
case 'd': break;
- case 'e': cat_options.show_nonprinting = true; // fall through
+ case 'e': cat_options.show_nonprinting = true; // fall through
case 'E': cat_options.show_ends = true; break;
case 'f': break;
case 'h': show_help(); return 0;
@@ -324,9 +330,10 @@ int main( const int argc, const char * const argv[] )
case 'N': break;
case 'O': format_index = parse_format_type( arg ); break;
case 'q': verbosity = -1; break;
- case 'r': recursive = true; break;
+ case 'r': recursive = 1; break;
+ case 'R': recursive = 2; break;
case 's': cat_options.squeeze_blank = true; break;
- case 't': cat_options.show_nonprinting = true; // fall through
+ case 't': cat_options.show_nonprinting = true; // fall through
case 'T': cat_options.show_tabs = true; break;
case 'v': cat_options.show_nonprinting = true; break;
case 'V': show_version(); return 0;
@@ -347,13 +354,15 @@ int main( const int argc, const char * const argv[] )
for( ; argind < parser.arguments(); ++argind )
filenames.push_back( parser.argument( argind ) );
- if( filenames.empty() ) filenames.push_back( "-" );
+ if( filenames.empty() ) filenames.push_back( recursive ? "." : "-" );
+ std::string input_filename;
int retval = 0;
bool error = false;
bool stdin_used = false;
while( next_filename( filenames, input_filename, error, recursive ) )
{
+ int infd;
if( input_filename.empty() )
{
if( stdin_used ) continue; else stdin_used = true;
@@ -368,12 +377,12 @@ int main( const int argc, const char * const argv[] )
const int tmp = cat( infd, format_index, input_filename, cat_options );
if( tmp > retval ) retval = tmp;
- if( input_filename.size() ) { close( infd ); infd = -1; }
+ if( input_filename.size() ) close( infd );
}
if( std::fclose( stdout ) != 0 )
{
- show_error( "Can't close stdout", errno );
+ show_error( "Error closing stdout", errno );
error = true;
}
if( error && retval == 0 ) retval = 1;