1
votes

I have written the function, don't know it that's correct but how do I return true and false using if condition?

Function bool save_book(Book, char)** Input Parameters: A pointer to a Book and a string representing a file name. Return Value: Return true if the book's contents were stored to a file successfully. Otherwise false. Note This function should create or open a file based on the file name and store each Line of the book in it.

Below is my code

bool save_book(Book *b, char* fileName){

FILE  *filePointer = fopen(fileName,"w");
for (int i = 0; i < pBook->lineCount; i++)
{
    fprintf(filePointer, "%s", b->lines[i]);
}
fclose(filePointer);

return true;
}

this is the struct I am using:

typedef struct _Book
{
   int characterCount;
   int lineCount;
   int maxLineCount;
   char **lines;
}Book;
 
1
Read documentation of C IO functions - Basile Starynkevitch
Check whether the fopen() works; check whether each fprintf() works; check whether fclose() works. If none of them report a problem, the chances are high that everything is OK. - Jonathan Leffler
@JonathanLeffler There can still be caching on the OS and the cache can be lost when a latter access to the disk fails. To be save a sync is needed. - 12431234123412341234123
@12431234123412341234123: yes, and disk drives can be removed, and … That's why the chances are 'high', not 'absolute'. If you're using file streams, getting the relevant sync options set is fiddly — doable on POSIX systems, probably doable on Windows. There are different levels of sync-safety too, with different ramifications for system performance. It isn't clear whether describing such details would benefit the OP, though. A lot depends on what the requirements are, but for most people (programs) most of the time, checking the I/O operations is sufficient — but not for everyone all the tie. - Jonathan Leffler
@shardul: Verifying that filePointer != NULL after the call to fopen is the most important check. That makes sure that the file was opened successfully. It is also possible that fopen will succeed and that fprintf will fail afterwards, but this is rather unlikely. Therefore, in my experience, it is always important to check the return value of fopen, but normally not necessary to check the return value of fprintf. Also, it is also normally not necessary to check fclose. - Andreas Wenzel

1 Answers

2
votes

You have to check every individual I/O operation.

To make it fun, they all return different things on error. fopen will return NULL. fprintf will return a negative number. fclose will return EOF.

Here it is annotated with a short list of what might go wrong at each step.

bool save_book(Book *b, char* fileName) {
    // Maybe the directory doesn't exist.
    // Maybe you don't have permission.
    // Maybe there's a disallowed character.
    // Maybe the disk is full.
    // Maybe it's a network drive and there's a network error.
    // Maybe the drive got unmounted.
    FILE  *filePointer = fopen(fileName,"w");
    if( filePointer == NULL ) {
        return false;
    }

    for (int i = 0; i < b->lineCount; i++)
    {
        // Maybe the disk is full.
        // Maybe it's a network drive and there's a network error.
        // Maybe the drive got unmounted.
        if( fprintf(filePointer, "%s", b->lines[i]) < 0 ) {
            // Even though the filePointer variable will be automatically freed
            // the underlying file handle will not be automatically closed.
            // There's a limit to how many open file handles one can have open.
            // No need to check for error, we know something has already gone wrong.
            fclose(filePointer);
            return false;
        }
    }
    // Maybe the disk is full.
    // Maybe it's a network drive and there's a network error.
    // Maybe the drive got unmounted.
    if( fclose(filePointer) != 0 ) {
        return false;
    }

    return true;
}

In reality you probably don't need to check fprintf, checking fclose should catch the same errors. But if you're writing a very large and expensive file you might want to know if you ran out of disk space sooner rather than later.

You can also optionally print the error. Each of those functions will set the global errno on failure. You can turn this into a human readable string with strerror.

    if( filePointer == NULL ) {
        fprintf(stderr, "Error while opening '%s' for writing: %s", fileName, strerror(errno));
        return false;
    }

Note that rather than checking for an exact error code, I tend to check for that which is not the success code. Rather than if( fclose(filePointer) == EOF ) I've checked for if( fclose(filePointer) != 0 ), the lack of a success code. This is a defense programming practice just in case the error is severe enough that it can't even return its correct error code (extremely unlikely in standard library code) or I didn't read the spec quite right.