0

Motivation

If I am sure that a given input file exists, I would like to loop until creation of a "file stream" for the file does not fail.

Question

I understand that with the following check we can tell if opening an input failed (and therefore, said file exists locally) in C,

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

int main()
{
    FILE *rec_in_file = fopen("filename.txt", "r");

    // If file cannot be opened, report error and exit
    if (rec_in_file == NULL)
    {
        fprintf(stderr, "Could not open input file!");
        exit(1);
    }

    return 0;
}

but how can I tell if the file exists but something went wrong while creating the "file stream"? In preliminary research for the question, I could file no such mention of handling this case.

JDQ
  • 443
  • 7
  • 11
  • 2
    `errno` should be set according to the error `fopen()` encountered. An easy way to use this is to `perror()` after the `if (!rec_in_file)`. – EOF Apr 07 '16 at 23:50
  • 1
    ^ You should post this as answer EOF. – ronakg Apr 07 '16 at 23:52
  • 2
    It's very unlikely that something would go wrong while creating the file stream. `fopen` failure is almost always due to a problem with the file, either it doesn't exist or you don't have correct permissions. `errno` should clarify. – Barmar Apr 07 '16 at 23:57
  • @ronakg: There's already a question about this: http://stackoverflow.com/q/5685973/3185968 – EOF Apr 08 '16 at 00:02
  • @Barmar - most modern systems allow files to be protected. This can cause opening a file to fail, even if it exists. A file that is protected to prevent read access cannot be opened for reading. A file that is protected to prevent write access might (or might not) be able to be opened for reading, but not for writing. Unfortunately, in standard C, there is no way to resolve such things. – Peter Apr 08 '16 at 00:07
  • @Peter That's what I meant by "you don't have correct permissions". – Barmar Apr 08 '16 at 00:11
  • @Barmar: Since stdio is buffered, `fopen()` can also fail if it is unable to allocate memory for the buffer. – EOF Apr 08 '16 at 00:13
  • @Barma - my point is that your claim that it is "very unlikely that something would go wrong when creating the file stream" is false. – Peter Apr 08 '16 at 00:14
  • I interpreted that to mean that the problem is in the C runtime library, not an issue with the file itself. – Barmar Apr 08 '16 at 00:14
  • @Barmar What I meant was a system-level failure (permissions or otherwise). – JDQ Apr 08 '16 at 00:34
  • @ronakg: Assuming what Peter answered below is valid, the question you linked may "overlap", but it doesn't answer my question (again, assuming Peter's answer is correct). – JDQ Apr 08 '16 at 00:38

2 Answers2

3

Checking errno or a call of perror() can be used to get information after the fopen() fails. The problem with that is that these means are not required to produce any information related to I/O errors. Implementations often do, but they are not required to, and the information varies.

Generally speaking, you will need to use functions outside standard C, such as access() or stat() (supported by most Unix libraries), PathFileExists() (Windows API), or some other means provided by the host system.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Peter
  • 35,646
  • 4
  • 32
  • 74
3

The fopen function may, but might not, set errno to indicate the specific cause of failure. If it doesn't, then the cause is likely not detectable, or you're dealing with a very poor implementation of the standard library.

Why wouldn't the implementor of fopen not set errno, the standard C error-cause-reporting mechanism, unless for some reason it were difficult, or couldn't be done at all? If fopen can determine a specific cause for why the file cannot be opened, it's as simple matter to set errno to some value. If this is so hard that the C library implementor ducks out of it, it's probably not going to be easy for you, either, on that platform.

Because fopen might not set errno (ISO C doesn't require it), this creates an ambiguity: if fopen fails, and errno is nonzero, we don't know whether fopen set that value, or whether it was already nonzero. The solution is: assign zero to errno before trying fopen.

Tip: if you're working in an older dialect of C, or in a style which avoids mixed declarations and statements, you can still do this with a comma expression:

FILE *f = (errno = 0, fopen(...));

the value and type of the comma expression is that of the right operand.

Then, in the case that fopen fails, test whether errno has changed to nonzero. If so, it probably contains information pertaining to the fopen. You can make an error message more informative, or take some special actions:

if (fp == NULL) {
    if (errno != 0)
      fprintf(stderr, "Could not open input file, for this reason: %s\n!",
              strerror(errno));
    else
      fprintf(stderr, "Could not open input file, for some reason.\n");
}

Tip: you can use #ifdef to see whether specific errno constants exist. For instance if you want to check for and do something special for the POSIX EPERM, wrap that code with #ifdef EPERM ... #endif.

Note that the above errno approach works fine on Microsoft Windows using the MSVCRT (Microsoft Visual C Run Time) C library (no Unix emulation layer). Microsoft's fopen nicely sets errno to 2, and the strerror string for that is "No such file or directory". This 2 corresponds to the ENOENT constant, which Microsoft provides. EPERM is there and has value 1. The behavior of setting errno is documented; the following quote is straight out of MSDN: "[i]f an error occurs, the global variable errno is set and may be used to obtain specific error information."

Microsoft's fopen will also fail and set errno to EINVAL if you pass it null strings, and the invalid parameter handler is configured to allow execution to proceed.

Kaz
  • 55,781
  • 9
  • 100
  • 149