0

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?

avivgood2
  • 227
  • 3
  • 19

4 Answers4

2

The "r+" mode trick (given in the comments of the question) could be sufficient but makes stdout available for reading too.
(is this a problem?)

Instead of using freopen() you could use open() without the O_CREAT flag.
Then the obtained file descriptor can be substituted to the standard output with dup2().
http://man7.org/linux/man-pages/man2/open.2.html
http://man7.org/linux/man-pages/man2/dup.2.html

Here is a minimal example.

$ rm -f output.txt
$ ./prog_c output.txt
before
after
$ touch output.txt
$ ./prog_c output.txt
before
$ cat output.txt 
after
$ 

The source code:


/**
  gcc -std=c99 -o prog_c prog_c.c \
      -pedantic -Wall -Wextra -Wconversion \
      -Wc++-compat -Wwrite-strings -Wold-style-definition -Wvla \
      -g -O0 -UNDEBUG -fsanitize=address,undefined
**/

// for printf()
#include<stdio.h>

// for open()
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#if defined _MSC_VER // compiling with visual-studio
# include <io.h>
#endif

// for dup2() and close()
#include <unistd.h>

int
main(int argc,
     char **argv)
{
  printf("before\n");
  fflush(stdout); // in case something is pending in buffer...
  if(argc>1)
  {
#if defined _MSC_VER // compiling with visual-studio
    int fd=_open(argv[1], _O_WRONLY|_O_TRUNC); // no _O_CREAT
#else
    int fd=open(argv[1], O_WRONLY|O_TRUNC); // no O_CREAT
#endif
    if(fd!=-1)
    {
      dup2(fd, STDOUT_FILENO);
      close(fd);
    }
  }
  printf("after\n");
  return 0;
}
prog-fh
  • 13,492
  • 1
  • 15
  • 30
  • ... supposing that the OP, *has* `open()`. That function is specified by POSIX, and is not part of the base language standard. The Windows C runtime library does not have it, for example. (Technically. Windows has `_open()`, instead, which is for all intents and purposes the same thing under a different name, but it needn't have even that as far as the C language specifications are concerned.) – John Bollinger Mar 29 '20 at 14:12
  • 1
    The OP used ``$./dtoa ./tests/ExistingInput1.txt ./tests/NonExistingOutput.txt`` (``$`` prompt and forward slashes) so ``open()`` is probably available. – prog-fh Mar 29 '20 at 14:19
  • Probably so, but the issue is more about *generality* than about whether the OP can use the approach described. Additionally, it's inelegant (but not inherently wrong) to mix I/O suites in this way. – John Bollinger Mar 29 '20 at 14:29
  • @JohnBollinger By the way, what is the original purpose of system calls like ``dup2()`` if not changing the behavior of higher I/O operations? We could just deal with low level file descriptors directly, without this kind of *trick*. Am I missing something? – prog-fh Mar 29 '20 at 14:51
1

That is the documented beheaviour. If the file does not exist, it is created.

Marc Balmer
  • 1,780
  • 1
  • 11
  • 18
1

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;
        }
    }
John Bollinger
  • 160,171
  • 8
  • 81
  • 157
0

Try using fopen with mode 'r' and check if the file pointer is NULL, that way you can know if the file exists or not. Example:

char path[256];
scanf("%s", path);
FILE* fp = NULL;

fp = fopen(path, "r");
if (!fp)
{
    printf("The file doesen't exist!");
}
Dejan
  • 113
  • 1
  • 9