summaryrefslogtreecommitdiffstats
path: root/zcat.cc
diff options
context:
space:
mode:
Diffstat (limited to 'zcat.cc')
-rw-r--r--zcat.cc213
1 files changed, 213 insertions, 0 deletions
diff --git a/zcat.cc b/zcat.cc
new file mode 100644
index 0000000..f813bbe
--- /dev/null
+++ b/zcat.cc
@@ -0,0 +1,213 @@
+/* Zcat - decompress and concatenate files to standard output
+ Copyright (C) 2010 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/>.
+*/
+
+struct Cat_options
+ {
+ int number_lines; // 0 = no, 1 = nonblank, 2 = all
+ bool show_ends;
+ bool show_nonprinting;
+ bool show_tabs;
+ bool squeeze_blank;
+
+ Cat_options() throw()
+ : number_lines( 0 ), show_ends( false ), show_nonprinting( false ),
+ show_tabs( false ), squeeze_blank( false ) {}
+ };
+
+
+class Line_number // unlimited size line counter
+ {
+ std::string str;
+ int 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 )
+ {
+ if( str[i] < '9' ) { ++str[i]; return; }
+ str[i] = '0';
+ }
+ if( first_digit_pos > 0 ) str[--first_digit_pos] = '1';
+ else str.insert( 0, 1, '1' );
+ }
+
+ int sprint( uint8_t * const buf )
+ {
+ std::memcpy( buf, str.c_str(), str.size() );
+ return str.size();
+ }
+ };
+
+Line_number line_number;
+
+
+void show_zcat_help() throw()
+ {
+ 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" );
+ show_help_addr();
+ }
+
+
+int do_cat( const int infd, const int buffer_size,
+ uint8_t * const inbuf, uint8_t * const outbuf,
+ const std::string & input_filename,
+ const Cat_options & cat_options )
+ {
+ static int at_bol = 1; // at begin of line. 0 = false, 1 = true,
+ // 2 = at begin of second blank line.
+ int inpos = 0; // positions in buffers
+ int outpos = 0;
+ int rd = -1; // bytes read by the last readblock
+ unsigned char c;
+
+ while( true )
+ {
+ do {
+ if( outpos >= buffer_size )
+ {
+ if( writeblock( STDOUT_FILENO, outbuf, outpos ) != outpos )
+ { show_error( "Write error", errno ); return 1; }
+ outpos = 0;
+ }
+ if( inpos > rd ) // inbuf is empty
+ {
+ rd = readblock( infd, inbuf, buffer_size );
+ if( rd != buffer_size && errno )
+ {
+ if( verbosity >= 0 )
+ std::fprintf( stderr, "%s: Error reading file `%s': %s.\n",
+ util_name, input_filename.c_str(),
+ std::strerror( errno ) );
+ return 1;
+ }
+ if( rd == 0 )
+ {
+ if( writeblock( STDOUT_FILENO, outbuf, outpos ) != outpos )
+ { show_error( "Write error", errno ); return 1; }
+ outpos = 0;
+ return 0;
+ }
+ inpos = 0;
+ inbuf[rd] = '\n'; // sentinel newline
+ }
+ else // a real newline was found
+ {
+ if( at_bol > 1 )
+ {
+ if( cat_options.squeeze_blank ) { c = inbuf[inpos++]; continue; }
+ }
+ else ++at_bol;
+ if( at_bol > 1 && cat_options.number_lines == 2 )
+ {
+ line_number.next();
+ outpos += line_number.sprint( &outbuf[outpos] );
+ }
+ if( cat_options.show_ends ) outbuf[outpos++] = '$';
+ outbuf[outpos++] = '\n'; // output the newline
+ }
+ c = inbuf[inpos++];
+ }
+ while( c == '\n' );
+
+ if( at_bol > 0 && cat_options.number_lines )
+ {
+ line_number.next();
+ outpos += line_number.sprint( &outbuf[outpos] );
+ }
+ at_bol = 0;
+
+ // the loops below continue until a newline (real or sentinel) is found
+
+ if( cat_options.show_nonprinting )
+ while( true )
+ {
+ if( c < 32 || c >= 127 )
+ {
+ if( c == '\n' ) break;
+ if( c != '\t' || cat_options.show_tabs )
+ {
+ if( c >= 128 )
+ { c -= 128; outbuf[outpos++] = 'M'; outbuf[outpos++] = '-'; }
+ if( c < 32 ) { c += 64; outbuf[outpos++] = '^'; }
+ else if( c == 127 ) { c = '?'; outbuf[outpos++] = '^'; }
+ }
+ }
+ outbuf[outpos++] = c;
+ c = inbuf[inpos++];
+ }
+ else // not quoting
+ while( c != '\n' )
+ {
+ if( c == '\t' && cat_options.show_tabs )
+ { c += 64; outbuf[outpos++] = '^'; }
+ outbuf[outpos++] = c;
+ c = inbuf[inpos++];
+ }
+ }
+ }
+
+
+int cat( int infd, const std::string & input_filename,
+ const Cat_options & cat_options )
+ {
+ enum { buffer_size = 4096 };
+ // 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];
+ pid_t pid = 0;
+ int retval = 0;
+ if( !set_data_feeder( &infd, &pid ) ) retval = 1;
+ else
+ {
+ retval = do_cat( infd, buffer_size, inbuf, outbuf,
+ input_filename, cat_options );
+ if( pid && wait_for_child( pid, "data feeder" ) != 0 ) retval = 1;
+ if( close( infd ) != 0 )
+ { show_error( "Can't close output of data feeder", errno ); retval = 1; }
+ }
+ delete[] inbuf; delete[] outbuf;
+ return retval;
+ }