cfileerror-handlingstdoutfreopen

freopen() creates a file when given an invalid path


I have a program that gets two paths as command line arguments. The first argument (actually the second, as the first one is the command name itself) is the path to the file the program reads from (input file). The second one is the path to the file the program writes to (output file).

int main(int argc, char *argv[])
{
    int num;
    /*if there are too many arguments print an error message*/
    if(argc > 3)
    {
        perror("too many arguments");
        return EXIT_FAILURE;
    }
    /*if there is at least one argument use it as path to input file*/
    if(argc > 1)
        if (!freopen(argv[1], "r", stdin)) {
            perror("Path of input file is Invalid");
            return EXIT_FAILURE;
        }
    /*if there are two arguments use the second one as output*/
    if(argc == 3)
        if (!freopen(argv[2], "w", stdout))
        {
            perror("Path of output file is Invalid");
            return EXIT_FAILURE;
        }
/*more code....*/
}
/*(compiled to run file "dtoa.out")*/

The program works fine: if provided with valid input and path output paths it will read and write from the files, if provided with too many arguments or if the path to the input file is invalid, the program will print an error message and exit.

The problem is when provided with invalid output file path:

$./dtoa ./tests/ExistingInput1.txt ./tests/NonExistingOutput.txt

in such case, the program will just create the missing output file instead of returning NULL and printing an error message, which is an unwanted behavior. How can I change it, so that when the file is not found, the method will return NULL instead of creating a new file?


Solution

  • The problem is when provided with invalid output file path [...] the program will just create the missing output file instead of returning NULL

    This is the documented behavior for opening a file with mode w (or any other mode based on w or a). If that's not what you want -- and you should consider whether that's in fact the case -- then you need to use a different mode, at least initially. All the r modes cause fopen() and freopen to fail if the file does not already exist, as you request. Some of them open the file in a manner that permits both reading and writing, so, for example,

        if (argc == 3) {
            if (!freopen(argv[2], "r+", stdout)) {
                perror("Path of output file is Invalid");
                return EXIT_FAILURE;
            }
        }
    

    If you want to ensure that stdout's mode does not permit writing, and/or if you want to ensure that the target file is truncated even if nothing is written to it, then you can reopen it twice, first in a read-based mode and, if that succeeds, in write-only mode:

        if (argc == 3) {
            if (!freopen(argv[2], "r+", stdout)) {
                perror("freopen (output)");
                return EXIT_FAILURE;
            }
            if (!freopen(NULL, "w", stdout)) {
                perror("freopen (output)");
                return EXIT_FAILURE;
            }
        }