Writing my own cat command

Guys,

I am pretty new to C programming and this is the third week of class and we have already had this program as a project and it's due pretty soon like this Sunday.

So the forms of the cat command my program must implement are:
1. cat
2. cat -n(like the real cat -n)
3. cat filename
4. cat -n filename

So my logic is, identify the user input first,

if the agrument does not have -n then go to next if else statement: see if it has a filename in it if not then just echo the input. If it has a filename then output the file, which basically copy stdin to stdout.

if the argument has -n in it then go to the if else statement: if the argument has a filename included then copy the contents of the file to stdout and add line numbers. If not then just echo the input and number all output lines.

And so my problems are:
1. what would be the way that echo the input?
2. how to identify if the argument is a filename or not?
3. how to check if there is a -n in the argument?
4. how to number the output?
5. and how to add line number in front of each output from the file?

so for question 3 i figured "-n" must be the second argument after the first argument "cat." So in my if else statement can i just say if(argc[2]!="-n")? or do I need some stuff like "%d"?
So my logic is, identify the user input first,
Remember int main(int argc, char* argv[])

argc is the number of args
You first check the number of args. If there are args, and the first is -n, you need to print line numbers on each line. The remaining args are the file names to write out.

If there are no filenames, you read from stdin.

1. what would be the way that echo the input?
You echo a line by writing it to stdou; puts, printf, fprintf(stdout, ... all write to stdout in C, and std::cout writes to stdout in C++.

2. how to identify if the argument is a filename or not?
The sequence above should help you with this.

3. how to check if there is a -n in the argument?
argv is an array of strings, or length argc. In C, you compare strings with strcmp.
http://en.cppreference.com/w/c/string/byte/strcmp

4. how to number the output?
printf("%d", 3); writes 3 to stdout.
http://en.cppreference.com/w/c/io/fprintf
Last edited on
I don't understand the second answer. What's the sequence?
And let me show you my code, it's still incomplete.

1. #include<stdlib.h>
2. #include<stdio.h>
3. #define LINELEN 256

4. void send_to_stdout(FILE*);
5. void numberLines();

6. int main(int argc, char *argv[])
7. {
8. char cpr[]= "-n";
9. int input;
10. /*scan the input into input*/
11. scanf("%d", &input);

12. /*if the input does not contain -n*/
13.   if(strcmp(cpr,argc[1])=0)
14.  {

15. /*If input contains a filename*/
16.       if( )
17.       {
18.       File *fp;

19.          if((fp= fopen(argv[1], "r"))!=NULL)
20.          {
21.           send_to_stdout(fp);
22.           fclose(fp);
23.          }

24.          else
25.          {
26.           perror("Could not open the file.");
27.           exit(1);
28.          }
29.       }

30./*else input does not contain a filename*/
31.       else
32.       {
33.        send_to_stdout(stdin);
34.       }
35.   }

36./*else input must contains -n*/
37.   else
38.   {
39./*if input only has -n then just echo and number the output lines*/
40.      if(  )
41.      {
42.       numberLines();
43.      }
44./*else just output files and number all the lines*/
45.      else
46.      {


47.      }
48.   }



49. return 0;
50. }


51. void send_to_stdout(FILE *fp)
52. {
53. char line[LINELEN];
54.    while(fgets(line, LINELEN, fp))
55.    {
56.       if(fputs(line, stdout)==EOF)
57.      {
58.       perror("Write to stdout failed.");
59.       exit(1);
60.      }
61.    }
62. }


63. void numberLines()
64. {
65.  int i;
66.    for( i = 1; i<= EOF; i++)
67.    {
68.     printf("%d",i);
69.    }
70. }

71. void numberFileLines(FILE *fp)
72. {
73.  char line[LINELEN];
74.   while(fgets(line, LINELEN, fp))
75.    {
76.      if(fputs(line, stdout)==EOF)
77.      {
78.       perror("Write to stdout failed.");
79.      exit(1);
80.      }
81.      else
82.      {
83.       int i;
84.         for( i = 1; i <= LINELEN; i++)
85.         {
86.        //  printf("%d", i);
87.         // char cbStr[100];
88.          sprintf(i,

89.         }

90.      }



91.    }
92. }
Last edited on
I don't understand the second answer. What's the sequence?
This sequence:
argc is the number of args
You first check the number of args. If there are args, and the first is -n, you need to print line numbers on each line. The remaining args are the file names to write out.


Please use the code format tags to format your code. It'll show line numbers and make it easier to comment on.

Take a look at my comment again, it should be possible to distil your pseudo code.

send_to_stdout is good, you'll need that.

You don't really care about the number of lines stuff. You just initialise a counter to 1 and increment it each time a line is written. That could be done in sent_to_stdout.
I did use the program output format but it's just never came out with line numbers so i just added them by myself. Anyway.

Yes I do understand the sequence, if there is an argc[2] then it must be a filename. But I was talking about if there is no -n and the argc[1] is either a normal input or a filename. That's what I was trying to figure out in line 15.

For the number of lines, in the numberLine method(line 63) that's the counter i wrote. But my professor told me the same thing that the line numbers could be done in the sent_to_stdout. Would you mind go though more details about it?

Sorry I am just not very talented on this.
Ok, there are two parts to your code:
1. There's parsing the command line args and deciding what file to print and how
2. There's the actual printing of the file

Let's look at step 2, since it's almost complete. You have a function send_to_stdout(). Do you know how it works? If you can explain it, I can help you modify it to write line numbers along with the line.

Once that's done, we'll get back to step 1. That seems quite complicated on the outset, but it's fairly straigtforward.
I got the step 2. Here is my code with printing files and line numbers.
void numberFileLines(FILE *fp)
{
 char line[LINELEN];
   while(fgets(line, LINELEN, fp))
   {
     if(fputs(line, stdout)==EOF)
     {
      perror("Write to stdout failed.");
      exit(1);
     }
     else
     {
      int i;
        for( i = 1; i <= LINELEN; i++)
        {
         printf("%d\t", i);
         fputs(line,stdout);
        }
     }

   }
}
Last edited on
This way I can print the number before output the line. So here is my code now
#include<stdlib.h>
#include<stdio.h>
#define LINELEN 256

void send_to_stdout(FILE*);
void numberLines();

int main(int argc, char *argv[])
{
 char cpr[]= "-n";
 int input;
/*scan the input into input*/
 scanf("%d", &input);

/*if the input does not contain -n*/
   if(strcmp(cpr,argc[1])==0)
   {

/*If input contains a filename*/
/*Here is the problem */      
        if( )
       {
       File *fp;

          if((fp= fopen(argv[1], "r"))!=NULL)
          {
           send_to_stdout(fp);
           fclose(fp);
          }

          else
          {
           perror("Could not open the file.");
           exit(1);
          }
       }
/*else input does not contain a filename*/
       else
       {
        send_to_stdout(stdin);
       }
   }
/*else input must contains -n*/
   else
   {
/*if input only has -n then just echo and number the output lines*/
      if(argc[2]==-1)
      {
       numberFileLines(FILE *fp);
      }
/*else just output files and number all the lines*/
      else
      {
       numberFileLines(stdin);
      }
   }



return 0;
}


void send_to_stdout(FILE *fp)
{
 char line[LINELEN];
   while(fgets(line, LINELEN, fp))
   {
      if(fputs(line, stdout)==EOF)
      {
       perror("Write to stdout failed.");
       exit(1);
      }
   }
}


void numberFileLines(FILE *fp)
{
 char line[LINELEN];
   while(fgets(line, LINELEN, fp))
   {
     if(fputs(line, stdout)==EOF)
     {
      perror("Write to stdout failed.");
      exit(1);
     else
     {
      int i;
        for( i = 1; i <= LINELEN; i++)
        {
         printf("%d\t", i);
         fputs(line,stdout);
        }
     }

   }
}


So i marked out the problem. The only problem would be identifying the filename. My professor was saying that "The only way to see if you have a filename is to try to use the parameter to open a file. If the file does not open, then it was not a good file name."

I don't really understand what he said tho
Or actually using this way to identify filename:
/*if the input does not contain -n*/
   if(strcmp(cpr,argc[1])==0)
   {
       File *fp;

          if((fp= fopen(argv[1], "r"))!=NULL)
          {
           send_to_stdout(fp);
           fclose(fp);
          }

        /*if file couldn't be opened then it probably is just a user input*/
          else
          {
           send_to_stdout(stdin);
          }
        /*else it's nothing.*/
          else
          {
           perror("Could not open the file.");
           exit(1);
          }
       }

Would this work?
(Disclosure: I have not read the whole thread.)

Close, but your indentation is throwing you off.

If there is an argument, it should specify a file name.
  If the file can be opened, use it
  else complain that the file couldn't be opened.
else use standard input

Hope this helps.
I got the step 2. Here is my code with printing files and line numbers...
You don't need multiple write functions, you just need one that takes parameters that describe what it ought to do.

For example, you have:
1
2
3
4
5
6
7
8
9
10
11
12
void send_to_stdout(FILE *fp)
{
    char line[LINELEN];
    while (fgets(line, LINELEN, fp))
    {
        if (fputs(line, stdout)==EOF)
        {
            perror("Write to stdout failed.");
            exit(1);
        }
    }
}

and you have:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void numberFileLines(FILE *fp)
{
    char line[LINELEN];
    while(fgets(line, LINELEN, fp))
    {
        if(fputs(line, stdout)==EOF)
        {
            perror("Write to stdout failed.");
            exit(1);
        }  /* I added this for you */
        else
        {
            int i;
            for( i = 1; i <= LINELEN; i++)
            {
                printf("%d\t", i);
                fputs(line,stdout);
            }
        }
    }
}


But if you had a single function that was parameterised, you could just call it once with the appropriate parameters.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void copy_file(FILE *fin, FILE* fout, int writeLineNumbers)
{
    char line[LINELEN];
    int lineno = 0;

    while (fgets(line, LINELEN, fin))
    {
        if (writeLineNumbers)
            printf("%d: ", lineno);

        if (fputs(line, fout) == EOF)
        {
            perror("Write to stdout failed.");
            /* It's probably best to not terminate the program here
             * exit(1);
             */
           return;
        }

        ++lineno;
    }
}


So, the next part is dealing with the command line args and calling copy_file with the right args. The arguments come in from the command line as an array of strings. Let's consider a few cases.

No args:
argc = 1
You're expected to read from stdin

A filename as an arg:
argc = 2
argv[1] = "/tmp/results.txt"

Two filenames with -n
argc = 4
argv[1] = "-n"
argv[2] = "/tmp/results.txt"
argv[3] = "/tmp/query.txt"

So, you need to traverse the array and processes each in turn. Something like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int main(int argc, char* argv[])
{
    int writeLineNumbers = 0;
    int nFiles = 0;
    int i;

    for (i = 1; i < argc; ++i)
    {
        if (strcmp(argv[i], "-n") == 0)
        {
            writeLineNumbers = 1;
            continue;
        }

        /* You can fill in the file handling yourself */
        ++nFiles;
    }

    /* I'll do the stdin handling for you */
    if (nFiles == 0)
        copy_file(stdin, stdout, writeLineNumbers);

    return 0;
}
Last edited on
Ollie, Like Duoas, I haven't read the whole thing, just skimming when I noticed a typo in your code. There are some lines when you need to use the array argv, but you're accidentaly using the integer argc.

For example you used:
"if(strcmp(cpr,argc[1])==0)"
when you meant:
"if(strcmp(cpr,argv[1])==0)"

To recap, argc is an int which tells you how many command line arguements you have, while argv is an array of strings, argc in number, the first of which is the name of the program itself.

SO, if your program is called "cat", and invoked as follows:
"cat -n test.txt"
Then in main, you'll have:
argc = 3
argv = {"cat", "-n", "test.txt", NULL}
argv[0] = "cat"
argv[1] = "-n"
argv[2] = "test.txt"
kbw thanks for the copy_file method! It really is helpful. I got a segmentation fault after I complete my program and it keeps warning everytime until I find out that I should check if there is an argument first. And here is the complete program:
/********************************************************************
Mayingze(Oliver) Han
Discription: Writing Your Own Version of the
cat Command.
********************************************************************/
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#define LINELEN 256

void copy_file(FILE *fin, FILE* fout, int writeLineNumbers);

int main(int argc, char *argv[])
{
    char cpr[]= "-n";
    FILE *fp;
    /*scan the input into input*/
//    printf("Trace ... about to compare arguments\n");
    /*if the input does not contain -n*/

    if(argc >1)
    if(strcmp(cpr,argv[1])!=0)
    {

//        printf("Trace ... about to open the file\n");
          if((fp= fopen(argv[1], "r"))!=NULL)
          {
             copy_file(fp, stdout, 0);
             fclose(fp);
             exit(1);
          }
          /*else it's nothing.*/
          else
          {
//              printf("Trace ... about to perror\n");
              perror("could not open the file.");
              exit(1);
          }
    }
    /*if the user only type in one command then echo input*/
//    printf("Trace ... about to test argc\n");
    if(argc==1)
    {
//      printf("Trace ... about to send_to_stdout(stdin)\n");
     // send_to_stdout(stdin);
        copy_file(stdin, stdout, 0);
        exit(1);
    }


   /*else input must contains -n*/
    else
    {
        /*if input only has -n then just echo and number the output lines*/
        if(argc==2)
        {
//             printf("Trace ... about to do numberFileLines(stdin)\n");
        //     numberFileLines(stdin);
               copy_file(stdin, stdout, 1);
               e8xit(1);
        }
        /*else just output files and number all the lines*/
        else
        {
             if((fp= fopen(argv[2], "r"))!=NULL)
             {
//               printf("Trace ... about to do numberFileLines(fp)\n");
                 copy_file(fp, stdout, 1);
                 fclose(fp);
                 exit(1);
             }
             else
             {
                perror("could not open the file.");
                exit(1);
             }
        }
    }

   return 0;
}

void copy_file(FILE *fin, FILE* fout, int writeLineNumbers)
{
    char line[LINELEN];
    int lineno = 1;

    while (fgets(line, LINELEN, fin))
    {
        if (writeLineNumbers)
            printf("%d ", lineno);

        if (fputs(line, fout) == EOF)
        {
            perror("Write to stdout failed.");
            return;
        }

        ++lineno;
    }
}
               
And a huge THANK YOU to everyone provides help!!!!
Topic archived. No new replies allowed.