Cat

Another minimal implementation of cat.The gnu cat implementation read the file in the dynamically allocated buffer by using the formula (OUTSIZE – 1 + INSIZE * 4 + LINE_COUNTER_BUF_LEN).As you can see the buffer allocated is very large when compared to file.If the file is hugeeee then it can create problem on a low memory machine.So i thought i should create a cat implementation which reads the file in chunks in a fixed size buffer rather than whole file at once.At the first sight it looked easy but it had to care of previous and upcoming states to determine its output!
Here is my mini cat.Supported command options:-
-n //number all output lines
-b //number non-blank output lines
-t //display tabs as ^I
-e //display end-of-line characters as $

Mini Cat:-

#include <stdio.h>
#include <string.h>
#define  ISFTAB(x) x=='\t' || x=='\n'                 /*Non-printable characters format tags*/

enum{OPT_N=0x8,OPT_E=0x4,OPT_B=0x2,OPT_T=0x1};
enum{NEWF,TABF};                                      /*You can add more format tags(for non-print chars) here*/
int lcount=1;

/*Get's the next line from the input stream*/
int getline(char *buff,int max,FILE *fp)
{
    if(fgets(buff,max,fp)==0)
      return 0;
    return strlen(buff);
}

/*Returns the format tag for the non printable character*,it is extensible*/
char *fnonprint(char *f,char c)
{
    static char ftab[][5]={"$\n","^I"};
    int t=-1;
    if(c=='\n')
      t=NEWF;
    else if(c=='\t')
      t=TABF;
    return strcpy(f,ftab[t]);
}

void cat(const char *in,int opt)
{
    const char *p=in;
    char fc[10];
    if(*p=='\n'){
        if(opt&OPT_N)
          fprintf(stdout,"%d.",lcount++);
        if(opt&OPT_E)
          fprintf(stdout,"%s",fnonprint(fc,'\n'));
        else
          putchar('\n');
        return;                     /*The line is empty so no need of further processing and return*/
    }
    if(opt&OPT_N || opt&OPT_B)
      fprintf(stdout,"%d.",lcount++);       /*-b and -n option is to print line number for non-blank and all lines,both of which is
                                    true here because non empty line has been checked before*/

    while(*p){
        if(ISFTAB(*p)){
          if(opt&OPT_E || opt&OPT_T)
            fprintf(stdout,"%s",fnonprint(fc,*p));
          else
            putchar(*p);
        }
          else
            putchar(*p);
        p++;
    }
}

int main(int argc,char *argv[])
{
    int nfiles=0;
    int opt=0,i=0;
    FILE *fp;
    char in[512],c;

    while((++argv)[0] && argv[0][0]=='-'){
        while(c=*++argv[0])
            switch(c){
                case 'e':
                 opt|=OPT_E;
                 break;
                case 'b':
                 opt|=OPT_B;
                 break;
                case 't':
                 opt|=OPT_T;
                 break;
                case 'n':
                 opt|=OPT_N;
                 break;
                default:
                 fprintf(stderr,"cat:Unknown option %c",c);
                 return 1;
            }
    }

    while(argv[0]){
        nfiles++;
        fp=fopen(argv[0],"r");
        if(!fp){
          fprintf(stderr,"cat:Unable to open file %s",argv[0]);
          return 1;
        }
        while(i=getline(in,sizeof in,fp))
             cat(in,opt);

        fclose(fp);
        argv++;
    }
      /*take input from stdin*/
     if(nfiles==0){
        fp=stdin;
        while(i=getline(in,sizeof in,fp))
             cat(in,opt);
    }
    return 0;
}

2 Comments

Leave a comment