There are typically three ways to make a commandline program read a file. The most common is probably to just append the filename as an argument to your program.

programname filename.ext  

But there are two other ways to do it:

First a pipe into the program:

cat filename.ext | programname  

and second a redirected stdin:

programname < filename.ext  

But have you ever wondered how you make your c program work with all three input possibilities? I know I did. Turns out it is not that difficult.

The hard part is actually just knowing wether your program got a input from the outside, or if it should open the file itself. Apart from that, it is just plain old fopen/fgets. This means you can write a (dumbed down) version of cat in about 30 lines of code.

First the includes:

#include <unistd.h>
#include <stdio.h>

unistd is allows us to use isatty (more and that in a while) and stdio allows to work with files. That is all we use.

The program is then split into two parts:

  FILE *read_this;
  if (isatty(fileno(stdin))) {
    if(argc > 1) {
      printf("filename: %s\n", argv[1]);
      read_this = fopen(argv[1], "r");
    } else {
      puts("give me a file! Either stdin, a pipe or a filename after programname");
      return 1;
    }
  }
  else {
    read_this = stdin;
  }

which is responsible for giving us the correct file to read. The function isatty returns a bool to indicate wether a given filenumber represents a tty. If it does, we know it is not a file - and can thus proceed to try to read a filename from the program arguments. If it is not a tty, it can still be either a redirected stdin (the < version) or a pipe from another program/process (the | version) - but thankfully these work in exactly the same way (for our purposes anyway). After this block of code the read_this variable will be a file pointer (or null).

We then check to see if we got a valid file pointer:

  if (read_this == NULL) {
    fprintf(stderr, "Can't open input file\n");
    return 1;
  }

This will only happen if we try to open a file ourselves - since stdin will always be a valid file pointer.

Finally we can read the content of the file, and print it to the screen (or rather stdout - it could be piped ;-) ... )

  char buf[BUFSIZ];
  while(NULL != fgets(buf, sizeof buf, read_this)) {
      printf("%s", buf);
  }
  return 0;

That's about it. Go see the complete source code at this gist