Unfortunately, because C99 only defines EDOM, ERANGE, and EILSEQ errno macros, with all others defined outside the ISO C standard, in pure C99 you can only determine whether you can or cannot open a file. (Determined by fopen() returning a non-NULL file handle.)

When fopen() returns NULL, you can obtain the relevant error message using strerror(errno) (which is C99), or print an error message to standard error using perror() (also defined in C99).

Basically, in C99, all you can do is
Code:
/** savefile() - Save a struct info to a file
 * @path - File name or path
 * @info - struct info to save to file
 * This creates or truncates the file to zero bytes,
 * then writes the struct info to it.
 * Returns 0 if successful, nonzero if an error occurs.
*/
int savefile(const char *const path, const struct info *const info)
{
    FILE *out;

    if (path == NULL) {
        fprintf(stderr, "savefile(): No path specified.\n");
        return -1;
    }
    if (*path == '\0') {
        fprintf(stderr, "savefile(): Empty path specified.\n");
        return -2;
    }
    if (info == NULL) {
        fprintf(stderr, "savefile(): No data to save to '%s'.\n", path);
        return -3;
    }

    out = fopen(path, "wb");
    if (out == NULL) {
        fprintf(stderr, "savefile(): '%s': %s.\n", path, strerror(errno));
        return -4;
    }

    if (fwrite(info, sizeof *info, (size_t)1, out) != (size_t)1) {
        fprintf(stderr, "savefile(): Error writing to file '%s'.\n", path);
        return -5;
    }

    if (fflush(out)) {
        fprintf(stderr, "savefile(): A delayed error writing to file '%s'.\n", path);
        return -6;
    }

    if (fclose(out)) {
        fprintf(stderr, "savefile(): Error closing file '%s'.\n", path);
        return -7;
    }

    /* Success. */
    return 0;
}
If you want to append to the file instead of replacing the comments, use out = fopen(path, "ab"); instead. C99 defines modes r w a rb wb ab r+ w+ a+ r+b w+b a+b (and aliases rb+ wb+ ab+ for the last three).

In my opinion, especially considering that Microsoft will not implement full C99 support in its compilers, relying on the C99 standard features is rather stupid. The compilers that support C99, also have POSIX.1-2001 or POSIX.1-2008 C libraries. The existence of standardized errno values, getline(), stat(), readdir(), glob(), regcomp() and regex(), and iconv() character set conversion facilities alone make it much more useful than ISO C. Heck, I can use the above to write a simple grep example in a couple of dozen lines of C99+POSIX.1-2008 code -- without any arbitrary line length limits, either.

I really don't understand why one would rely on C99 but not on POSIX.1. Makes no sense to me.