0

I'll preface this with the standard: I am very new to C programming so please be gentle.

I'm writing a C program that should be able to take a file path/filename as a command line arg, failing that, it should accept user input. I have the argv[1] filename working, but I don't know how to get it to switch to stdin if the user doesn't add a file name as an arg. The input should be raw data, not a filename. here's my (very newbie) code. As a new programmer I may need some extrapolation of explanation and I apologize in advance for this.

int main(int argc, char* argv[]) {
    
    #ifndef NDEBUG
        printf("DBG: argc = %d\n", argc);
        for (int i = 1; i < argc; ++i)
            printf("DBG: argv[%d] = \"%s\"\n", i, argv[i]);
    #endif

    FILE* stream = fopen(argv[1], "r");
    
    char ch = 0;
    size_t cline = 0;
    
    char filename[MAX_FILE_NAME];
    filename[MAX_FILE_NAME - 1] = 0;
    
    
    if (argc == 2) {
        stream = fopen(argv[1], "r");
        if (stream == NULL) {
            printf("error, <%s> ", argv[1]);
            perror(" ");
            return EXIT_FAILURE;
        }
    }
    else if (argc ==1)
    
        printf("Enter a list of whitespace-separated real numbers terminated by EOF or \'end\'\n");
    
    //continue with program using user-input numbers 
  • Please format your code properly – Jabberwocky Feb 22 '22 at 16:48
  • I'm sorry, I'm not sure what you mean, is there a better way to format it? – newdevislost Feb 22 '22 at 16:51
  • 2
    If you have a filename, `fopen` that and check whether it succeeds. If you don't read from `stdin`, which is a predefined input `FILE *` which you shouldn't modify or `fclose`. You can assign `stream = stdin` and the code to scan the data from `stream` (which you haven't shown) will be the same for both cases. – M Oehm Feb 22 '22 at 16:55
  • Now formatting is OK. – Jabberwocky Feb 22 '22 at 16:55
  • 2
    (Of course you shouldn't `fopen` the `stream` until you know you have a filename. Your code tries to open the file anyway, which is an error, because `argv[1]` micht be null.) – M Oehm Feb 22 '22 at 16:56
  • @MOehm are you sure that fclosing stdin at the very end is a problem (see my answer below)? – Jabberwocky Feb 22 '22 at 16:59
  • @Jabberwocky: No, I'm not absolutely sure that is a problem, but I usually check and only `fclose` f'opened files. That may be overcautious. – M Oehm Feb 22 '22 at 17:01
  • @MOehm apparently it's not a problem on posix systems: https://stackoverflow.com/a/288097/898348 – Jabberwocky Feb 22 '22 at 17:04
  • @Jabberwocky: Aha, good to know. Thanks. – M Oehm Feb 22 '22 at 17:06
  • An alternative to using `fopen` (once the code has decided to open a file rather than use standard input) is to use `freopen` on the `stdin` stream like this: `stdin = freopen(argv[1], "r", stdin);`. The old `stdin` will be closed then associated with the newly opened file. On error, the return value (in `stdin`) will be `NULL` (same as for `fopen`), so check for that. – Ian Abbott Feb 22 '22 at 17:20

1 Answers1

1

Your code is overly complicated and wrong. You're doing things in the wrong order. You first need to check if an argument is there and try to open the file only in that case.

You want something like this:

#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>

int main(int argc, char* argv[]) {

  FILE* input = stdin;   // stdin is standard input
                         // so if no arguments are given we simply read
                         // from standard input (which is normally your keyboard)

  if (argc == 2) {
    input = fopen(argv[1], "r");
    if (input == NULL) {
      fprintf(stderr, "error, <%s> ", argv[1]);
      perror(" ");
      return EXIT_FAILURE;
    }
  }
  else
    printf("Enter a list of whitespace-separated real numbers terminated by EOF or \'end\'\n");

  double number;
  while (fscanf(input, "%lf", &number) == 1)
  {
    // do whatever needs to be done with number
    printf("number = %f\n", number);
  }

  fclose(input);
}
Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
  • @MOehm yes, of course, edited – Jabberwocky Feb 22 '22 at 17:00
  • 2
    That `perror` call might see a stale `errno` value due to the call to `printf` before it. (That is in OP's original code too.) – Ian Abbott Feb 22 '22 at 17:04
  • @IanAbbott true, but is `fprintf(stderr, "error, <%s> ", argv[1])` likely to change `errno`? – Jabberwocky Feb 22 '22 at 17:09
  • This solved it, thank you for setting me straight and helping me understand this concept more thoroughly, your help is very much appreciated. – newdevislost Feb 22 '22 at 17:11
  • 2
    @Jabberwocky Absolutely. This is even [an FAQ](http://c-faq.com/stdio/printferrno.html). You need to either call `perror` *immediately* after the call that failed, or else use `strerror(errno)` in the fprintf call immediately after the call that failed. – Steve Summit Feb 22 '22 at 17:14