Two Simple-minded Utility Programs: catf and wcf
I wrote two very simple programs (oh please, if you really call them that way.) as an exercise for my ongoing C programming study. One is my version of cat
and another is wc
.
cat
and wc
are simple command-line utilities shipped with Linux.
cat
can be used to concatenate (namely cat
) files and print them to the standard output (normally, the screen). And wc
is a word-counting program.
IMHO, my version of cat
and wc
are slightly "better" in functionalities. To distinguish with the original ones, I will suffix them with letter 'f' ('f' for "fake"), namely catf
and wcf
.
catf
catf
supports 4 forms of syntax:
catf
input fromstdin
(line-buffered), output tostdout
.catf file1.in file2.in ...
concatenate*.in
files and output tostdout
.catf -o file.out
input fromstdin
and output tofile.out
.catf -o file.out file1.in file2.in ...
input from*.in
files and output tofile.out
.
Isn't that surreal?
Notice that, using catf
, not only can you output concatenated files to stdout
, you can also output them to another file. (Oh yeah, some REAL improvement.)
Without further description, I'll simply paste the source code. (Please forgive the naïveté of an incompetent C programmer. Any harsh criticism is welcomed.)
/* a simple `cat` program
* aim:
* 1. catf
* 2. catf file_in1 file_in2 ...
* 3. catf -o file_out
* 4. catf -o file_out file_in1 file_in2 ...
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define LEN 4096
//read in the content of stream `in`, write to stream `out`.
void ReadAndWriteFile(FILE * in, FILE * out);
//close stream `pf`. the `name` of file is passed to assist printing error message
void CloseFile(FILE * pf, char * name);
int main(int argc, char *argv[])
{
FILE *in, *out; //in/out file pointer
int i;
if( argc==1 ) //func 1: if only `ccat` is given, read in from `stdin` and print out to `stdout`.
{
ReadAndWriteFile(stdin, stdout);
}
else if( argv[1][0]=='-' ) //func 3,4: if `-` arg is given, prepare a file for output
{
if( strcmp(argv[1], "-o")!=0 ) //check validity
{
fprintf(stderr, "%s: argument '%s' can not be recognized.\n" , argv[0], argv[1]);
exit(EXIT_FAILURE);
}
if( (out=fopen(argv[2], "a"))==NULL ) //open the output file in `append` mode
{
fprintf(stderr, "can not open file '%s' for appending.\n" , argv[2]);
exit(EXIT_FAILURE);
}
if( argc==3 ) //func 3: if no input file, read from stdin
{
ReadAndWriteFile(stdin, out);
CloseFile(out, argv[2]);
}
else //func 4: otherwise read in every input file and write its content to output file
{
for( i=3 ; i<argc ; i++ )
{
if( (in=fopen(argv[i], "r"))==NULL ) //open every input file in `read` mode
{
fprintf(stderr, "can not open file '%s' for reading.\n" , argv[i]);
exit(EXIT_FAILURE);
}
ReadAndWriteFile(in, out);
CloseFile(in, argv[i]);
}
CloseFile(out, argv[2]);
}
}
else //func 2: if no output file, open input files for reading and write to stdout
{
for( i=1 ; i<argc ; i++ )
{
if( (in=fopen(argv[i], "r"))==NULL )
{
fprintf(stderr, "can not open file '%s' for reading.\n" , argv[i]);
exit(EXIT_FAILURE);
}
ReadAndWriteFile(in, stdout);
CloseFile(in, argv[i]);
}
}
return 0;
}
void CloseFile(FILE * pf, char * name)
{
if( fclose(pf)!=0 )
{
fprintf(stderr, "file '%s' didn't closed successfully.\n" , name);
exit(EXIT_FAILURE);
}
}
void ReadAndWriteFile(FILE * in, FILE * out)
{
static char buffer[LEN];
while( fgets(buffer, LEN, in)!=NULL )
{
fputs(buffer, out);
}
}
wcf
wcf
supports two forms of syntax:
1. wcf
input from stdin, output to stdout.
2. wcf file.in
input from file.in
, output to stdout.
I had a hard time to design a word-count program with both efficiency and a clean flow of logic. That said, I do not like my version at all.
Here is the source code.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main(int argc, char **argv)
{
FILE *file; //file stream to be counted
int cc=0, wc=0, lc=0; //char, word, line count
char ch; //store input char
if( argc<2 ) //if no input file, use stdin
{
file=stdin;
}
else if( (file=fopen(argv[1], "r"))==NULL) //if there is input file, open it
{
fprintf(stderr, "file %s open error.\n" , argv[1]);
exit(EXIT_FAILURE);
}
//cc. every byte counts for a character
//wc. every alpha-numeric combination counts for a word
//lc. every '\n' indicates a complete line
while ( (ch=getc(file))!=EOF )
{
if( isalnum(ch) ) //idea is: encountering an alnum char indicates entering a word, wc++,
{ //then characters of the word is counted by inner loop
wc++; //other characters are counted by outer loop
} //lines and EOF are checked by outer loop
while( isalnum(ch) )
{
cc++;
ch=getc(file);
}
if( ch=='\n' )
{
lc++;
}
if( ch==EOF )
break;
cc++;
}
fclose(file);
printf( "characters: %d\nwords: %d\nlines: %d\n" , cc, wc, lc );
return 0;
}
Afterthought
I, the creator of these pieces of software, use catf
and wcf
all the time. But, I would still gentlely advise anyone as sane as me, to use the version that is shipped with their distribution. And you can get their source code via GNU's ftp.
That's it. Life well wasted.