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.