2025-02-24 05:04:07 +01:00
|
|
|
/* Zcat - decompress and concatenate files to standard output
|
2025-02-24 05:40:46 +01:00
|
|
|
Copyright (C) 2010, 2011, 2012, 2013 Antonio Diaz Diaz.
|
2025-02-24 05:04:07 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2025-02-24 05:40:46 +01:00
|
|
|
Cat_options()
|
2025-02-24 05:04:07 +01:00
|
|
|
: 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;
|
2025-02-24 05:40:46 +01:00
|
|
|
unsigned first_digit_pos;
|
2025-02-24 05:04:07 +01:00
|
|
|
|
|
|
|
public:
|
|
|
|
Line_number() : str( " 0\t" ), first_digit_pos( 5 ) {}
|
|
|
|
|
|
|
|
void next()
|
|
|
|
{
|
2025-02-24 05:40:46 +01:00
|
|
|
for( unsigned i = str.size() - 1; i > first_digit_pos; )
|
2025-02-24 05:04:07 +01:00
|
|
|
{
|
2025-02-24 05:40:46 +01:00
|
|
|
if( str[--i] < '9' ) { ++str[i]; return; }
|
2025-02-24 05:04:07 +01:00
|
|
|
str[i] = '0';
|
|
|
|
}
|
|
|
|
if( first_digit_pos > 0 ) str[--first_digit_pos] = '1';
|
2025-02-24 05:40:46 +01:00
|
|
|
else str.insert( first_digit_pos, 1, '1' );
|
2025-02-24 05:04:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int sprint( uint8_t * const buf )
|
|
|
|
{
|
|
|
|
std::memcpy( buf, str.c_str(), str.size() );
|
|
|
|
return str.size();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Line_number line_number;
|
|
|
|
|
|
|
|
|
2025-02-24 05:40:46 +01:00
|
|
|
void show_zcat_help()
|
2025-02-24 05:04:07 +01:00
|
|
|
{
|
2025-02-24 05:40:46 +01:00
|
|
|
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" );
|
2025-02-24 05:04:07 +01:00
|
|
|
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 )
|
|
|
|
{
|
2025-02-24 05:04:40 +01:00
|
|
|
show_error2( "Error reading file", input_filename.c_str() );
|
2025-02-24 05:04:07 +01:00
|
|
|
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++];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-02-24 05:40:46 +01:00
|
|
|
int cat( int infd, const int format_type, const std::string & input_filename,
|
2025-02-24 05:04:07 +01:00
|
|
|
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;
|
2025-02-24 05:40:46 +01:00
|
|
|
if( !set_data_feeder( &infd, &pid, format_type ) ) retval = 1;
|
2025-02-24 05:04:07 +01:00
|
|
|
else
|
|
|
|
retval = do_cat( infd, buffer_size, inbuf, outbuf,
|
|
|
|
input_filename, cat_options );
|
2025-02-24 05:07:04 +01:00
|
|
|
if( retval == 0 )
|
2025-02-24 05:04:07 +01:00
|
|
|
if( pid && wait_for_child( pid, "data feeder" ) != 0 ) retval = 1;
|
2025-02-24 05:07:04 +01:00
|
|
|
if( retval == 0 )
|
2025-02-24 05:04:07 +01:00
|
|
|
if( close( infd ) != 0 )
|
2025-02-24 05:04:40 +01:00
|
|
|
{ show_close_error( "data feeder" ); retval = 1; }
|
2025-02-24 05:04:07 +01:00
|
|
|
delete[] inbuf; delete[] outbuf;
|
|
|
|
return retval;
|
|
|
|
}
|