TYPE is boring, let's do Unix's cat instead:
Code:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **parse_options ( char *argv[] );
void read_file ( FILE *in );
void version ( void );
void help ( void );
#define BLANK_LINES 1
#define ALL_LINES 2
static int control; /* Show control characters as ^ */
static int show_count; /* Show line count */
static int show_end; /* End lines with $ */
static int show_tab; /* Show tabs as ^I */
static int squeeze; /* Change multiple blank lines into one */
static int line = 1;
int main ( int argc, char *argv[] )
{
argv = parse_options ( argv );
if ( *argv == NULL )
read_file ( stdin );
else {
FILE *in;
for ( ; *argv != NULL; *++argv ) {
if ( **argv == '-' )
in = stdin;
else {
if ( ( in = fopen ( *argv, "r" ) ) == NULL )
continue;
}
read_file ( in );
}
}
return 0;
}
char **parse_options ( char *argv[] )
{
while ( *++argv != NULL && **argv == '-' ) {
/* Terminating options */
if ( strcmp ( *argv, "--version" ) == 0 )
version();
if ( strcmp ( *argv, "--help" ) == 0 )
help();
/* Behavioral switches */
while ( *++(*argv) != '\0' ) {
switch ( **argv ) {
case 'b': show_count = BLANK_LINES; break;
case 'e': control = show_end = 1; break;
case 'n': show_count = ALL_LINES; break;
case 's': squeeze = 1; break;
case 't': control = show_tab = 1; break;
case 'u': /* No-op for Unix compatibility */ break;
case 'v': control = 1; break;
case 'A': control = show_end = show_tab = 1; break;
case 'E': show_end = 1; break;
case 'T': show_tab = 1; break;
default: /* Ignore unrecognized switches */ break;
}
}
}
return argv;
}
void read_file ( FILE *in )
{
int ch, last;
for ( last = '\n'; ( ch = getc ( in ) ) != EOF; last = ch ) {
if ( last == '\n' ) {
line_count:
if ( show_count == ALL_LINES )
printf ( "%-5d", line++ );
else if ( show_count == BLANK_LINES && ch != '\n' )
printf ( "%-5d", line++ );
if ( squeeze && ch == '\n' ) {
if ( show_end )
putchar ( '$' );
putchar ( '\n' );
/* Ignore extra empty lines */
while ( ( ch = getc ( in ) ) != EOF && ch == '\n' )
;
if ( ch == EOF )
break;
else
goto line_count; /* Print line # first */
}
}
if ( show_tab && ch == '\t' )
printf ( "^I" );
else if ( control && iscntrl ( ch ) )
putchar ( '^' );
else if ( show_end && ch == '\n' )
printf ( "$\n" );
else
putchar ( ch );
}
}
void version ( void )
{
printf ( "cat clone v1.0\n" );
exit ( EXIT_SUCCESS );
}
void help ( void )
{
printf ( "cat [-benstuvAET] [--help] [--version] [file...]\n" );
exit ( EXIT_SUCCESS );
}
Of course, that's C. I just grabbed something lying around on my hard drive. A conversion to C++ using iostreams is fairly trivial:
Code:
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <limits>
namespace {
const int BLANK_LINES = 1;
const int ALL_LINES = 2;
int control; /* Show control characters as ^ */
int show_count; /* Show line count */
int show_end; /* End lines with $ */
int show_tab; /* Show tabs as ^I */
int squeeze; /* Change multiple blank lines into one */
int line = 1;
char **parse_options ( char *argv[] );
void read_file ( std::istream& in );
void version ( void );
void help ( void );
}
int main ( int argc, char *argv[] )
{
argv = parse_options ( argv );
if ( *argv == 0 )
read_file ( std::cin );
else {
std::ifstream in;
for ( ; *argv != NULL; *++argv ) {
if ( **argv == '-' )
read_file ( std::cin );
else {
in.open ( *argv );
if ( !in )
continue;
read_file ( in );
}
}
}
}
namespace {
char **parse_options ( char *argv[] )
{
while ( *++argv != NULL && **argv == '-' ) {
/* Terminating options */
if ( std::strcmp ( *argv, "--version" ) == 0 )
version();
if ( std::strcmp ( *argv, "--help" ) == 0 )
help();
/* Behavioral switches */
while ( *++(*argv) != '\0' ) {
switch ( **argv ) {
case 'b': show_count = BLANK_LINES; break;
case 'e': control = show_end = 1; break;
case 'n': show_count = ALL_LINES; break;
case 's': squeeze = 1; break;
case 't': control = show_tab = 1; break;
case 'u': /* No-op for Unix compatibility */ break;
case 'v': control = 1; break;
case 'A': control = show_end = show_tab = 1; break;
case 'E': show_end = 1; break;
case 'T': show_tab = 1; break;
default: /* Ignore unrecognized switches */ break;
}
}
}
return argv;
}
void read_file ( std::istream& in )
{
char ch, last;
for ( last = '\n'; in.get ( ch ); last = ch ) {
if ( last == '\n' ) {
line_count:
if ( show_count == ALL_LINES )
std::cout<< std::left << std::setw ( 5 ) << line++;
else if ( show_count == BLANK_LINES && ch != '\n' )
std::cout<< std::left << std::setw ( 5 ) << line++;
if ( squeeze && ch == '\n' ) {
if ( show_end )
std::cout.put ( '$' );
std::cout.put ( '\n' );
/* Ignore extra empty lines */
in.ignore ( std::numeric_limits<std::streamsize>::max(), '\n' );
if ( in.eof() )
break;
else
goto line_count; /* Print line # first */
}
}
if ( show_tab && ch == '\t' )
std::cout<<"^I";
else if ( control && std::iscntrl ( ch ) )
std::cout<<'^';
else if ( show_end && ch == '\n' )
std::cout<<"$\n";
else
std::cout.put ( ch );
}
}
void version ( void )
{
std::cout<<"cat clone v1.0\n";
std::exit ( EXIT_SUCCESS );
}
void help ( void )
{
std::cout<<"cat [-benstuvAET] [--help] [--version] [file...]\n";
std::exit ( EXIT_SUCCESS );
}
}
It compiles and links cleanly, but I haven't tested the C++ version. So if it doesn't work, you're on your own.