1

I wrote a file parser for a project that parses a file provided on the command line.

However, I would like to allow the user to enter their input via stdin as well, but exclusively through redirection via the command line.

Using a Linux based command prompt, the following commands should yield the same results:

  1. ./check infile.txt (Entering filename via command line)
  2. ./check < infile.txt
  3. cat infile.txt | ./check

The executable should accept a filename as the first and only command-line argument. If no filename is specified, it should read from standard input.

Edit: I realized how simple it really was, and posted an answer. I will leave this up for anyone else who might need it at some point.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Serus
  • 144
  • 2
  • 11
  • If it should be exclusively through redirection, why do you allow `./check infile.txt`? That's not redirection. – Barmar Feb 11 '20 at 01:35
  • Just read from `stdin` until you get to EOF. – Barmar Feb 11 '20 at 01:35
  • You read from `stdin` the same way you read from a file that you opened with `fopen()`. – Barmar Feb 11 '20 at 01:36
  • When reading from stdin it should only be through redirection, otherwise it is from the command line. So i can fgets from stdin? Which makes sense, do I then check the characters coming in to see if they are EOF? – Serus Feb 11 '20 at 01:42
  • So you want to prevent stdin from being a terminal? There's no standard way to do that in C, but POSIX has `isatty()` which can tell if stdin is a terminal or not. – Barmar Feb 11 '20 at 01:47
  • The "do not allow terminal input" rule is not very sensible. The main trick is to write the processing function so that it is passed a file stream (`FILE *`) and it reads from that stream. It should either write normal output to `stdout` or to another file stream also passed to the processing function. Then the processing function really doesn't care whether its input comes from a file, a pipe, the terminal or any other source. The code in `main()` can set up the appropriate file stream for input (and for the output if relevant). It may be feasible to have the program process many files. – Jonathan Leffler Feb 11 '20 at 02:22

2 Answers2

0

I guess my brain is fried because this was a very basic question and I realized it right after I posted it. I will leave it up for others who might need it.

ANSWER:

You can fgets from stdin, then to check for the end of the file you can still use feof for stdin by using the following:

while(!feof(stdin))
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Serus
  • 144
  • 2
  • 11
0

This is dangerously close to "Please write my program for me". Or perhaps it even crossed that line. Still, it's a pretty simple program.

We assume that you have a parser which takes a single FILE* argument and parses that file. (If you wrote a parsing function which takes a const char* filename, then this is by way of explaining why that's a bad idea. Functions should only do one thing, and "open a file and then parse it" is two things. As soon as you write a function which does two unrelated things, you will immediately hit a situation where you really only wanted to do one of them (like just parse a stream without opening the file.)

So that leaves us with:

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "myparser.h"
/* Assume that myparser.h includes
 *    int parseFile(FILE* input);
 * which returns non-zero on failure.
 */

int main(int argc, char* argv[]) {
  FILE* input = stdin;  /* If nothing changes, this is what we parse */
  if (argc > 1) {
    if (argc > 2) {
      /* Too many arguments */
      fprintf(stderr, "Usage: %s [FILE]\n", argv[0]);
      exit(1);
    }
    /* The convention is that using `-` as a filename is the same as
     * specifying stdin. Just in case it matters, follow the convention.
     */
    if (strcmp(argv[1], "-") != 0) {
      /* It's not -. Try to open the named file. */
      input = fopen(argv[1], "r");
      if (input == NULL) {
        fprintf(stderr, "Could not open '%s': %s\n", argv[1], strerror(errno));
        exit(1);
      }
    }
  }

  return parse(input);
}

It would probably have been better to have packaged most of the above into a function which takes a filename and returns an open FILE*.

rici
  • 234,347
  • 28
  • 237
  • 341
  • I guess I should have specified, I wrote the entire program, including the conditionals checking if a source file was provided. I just spaced on the fact that fgets works for stdin – Serus Feb 11 '20 at 06:01