/* Zcat - decompress and concatenate files to standard output Copyright (C) 2010, 2011 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 . */ 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( 0U, 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 ) { show_error2( "Error reading file", input_filename.c_str() ); 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( retval == 0 ) if( pid && wait_for_child( pid, "data feeder" ) != 0 ) retval = 1; if( retval == 0 ) if( close( infd ) != 0 ) { show_close_error( "data feeder" ); retval = 1; } delete[] inbuf; delete[] outbuf; return retval; }