2

The way I'm using just involves trying to fopen() the file to be checked,

/* --- does file exist??? --- */
char    fname[999] = "whatever";        /* constructed during execution */
FILE    *fp = NULL;                     /* try to fopen(fname,"r") */
int     isfilefound = 0;                /* set true if fopen() succeeds */
if ( (fp = fopen(fname,"r"))            /* try to fopen() for read */
!=    NULL ) {                          /* succeeded */
  isfilefound = 1;                      /* set file found flag */
  fclose(fp); }                         /* and just close the file */

Is there a quicker, less resource-intensive, way?... A specific way for unix/linux? A Windows way? And preferably, a portable posix-compliant way (as above presumably is)? It's being done lots (1000's) of times, so I'd prefer not to be unnecessarily opening and closing files for no good reason.

-----------------------------------------------------------------
Edit Okay, based on answers below, I put together the following little function intended to check whether or not file (already:) exists in a posix,windows,other portable way...

/* ==========================================================================
 * Function:    isfilexists ( path )
 * Purpose:     check whether file at path exists
 * --------------------------------------------------------------------------
 * Arguments:   path (I)        pointer to null-terminated char string
 *                              containing "path/filename.ext" of
 *                              file whose existence is to be determined
 *                              (path is relative to pwd unless explicitly
 *                              absolute by initial '/' or other syntax)
 * --------------------------------------------------------------------------
 * Returns:     ( int )         1 if file at path exists, or 0 if not
 * --------------------------------------------------------------------------
 * Notes:       o conditional compiles for various systems,
 *                depending on whether POSIX or WINDOWS is #define'ed...
 *              o ...method used:
 *                1: use access() on Posix systems,
 *                2: PathFileExists() on Windows systems,
 *                3: fopen() on any other systems.
 * ======================================================================= */
/* --- entry point --- */
int     isfilexists ( char *path )
{
/* ---
 * allocations and declarations
 * ------------------------------- */
int     isexists = 0;                   /* set true if file at path exists */
FILE    *fp = NULL;                     /* fopen() for non-posix,windows */
#define POSIX                           /* just for testing */
/* ---
 * determine whether file at path already exists
 * ------------------------------------------------ */
#if defined(POSIX)                      /* posix-compliant system... */
  #include <unistd.h>
  if ( access(path,F_OK) == 0 )         /* file at path exists */
    isexists = 1;                       /* so set file exists flag */
#else
  #if defined(WINDOWS)                  /* Windows system... */
    isexists = PathFileExists(path);    /* set flag if file at path exists */
  #else
    /* --- fopen() for any other non-posix, non-windows system --- */
    if ( (fp = fopen(path,"r"))         /* try to fopen() for read */
    != NULL ) {                         /* succeeded */
      isexists = 1;                     /* set file exists flag */
      fclose(fp); }                     /* and just close the file */
  #endif
#endif
return ( isexists );    /* back to caller with 1 if file at path exists */
} /* --- end-of-function isfilexists() --- */

The access() and fopen() methods tested and work okay. Unable to test PathFileExists() for windows. And I still want to figure out what #define'ed symbols to automatically and unambiguously check for conditional compiles.

John Forkosh
  • 502
  • 4
  • 14
  • 4
    There *are* other ways, e.g. on a POSIX system `access()` or `stat()`. But the question is: *why* do you want to do this check? Checking for existence of the file is quite often the wrong approach. –  Jun 24 '17 at 22:01
  • @FelixPalmen I think you could post that as an answer. – melpomene Jun 24 '17 at 22:03
  • Thanks, @FelixPalmen I'll definitely try that replacement (post as an answer if you'd like a "check":). Reason is as described in previous question at https://stackoverflow.com/questions/44719626/ (and if you've got a better way, I'm all ears -- post it there for two "checks":) – John Forkosh Jun 24 '17 at 22:06
  • @MartinR ...Oh, yeah. That would be an exact duplicate, no "possibly" about it. I didn't notice that as a suggested answer while writing the question. Not sure whether I just missed it (and I did look at the changing list as I was writing), or if it just strangely wasn't suggested at all. Thanks. – John Forkosh Jun 24 '17 at 22:09
  • @MartinR Then I must have missed it, though I did look at the list of suggested answers while writing, and that question seems >>very obviously<< duplicate. – John Forkosh Jun 24 '17 at 22:15
  • @ScottStensland Gee, thanks for taking out "already" from the original Subject that used to read "...already exists". But note that there's a fine (sometimes very fine) line between "redundant" and "pedantic" :) – John Forkosh Jun 24 '17 at 22:45

2 Answers2

5

You are thinking about the problem the wrong way. You shouldn't ever "check whether a file already exists", because that has an inherent TOCTOU race — in between the time you check whether the file exists, and the time you act on that information, another process may come along and change whether the file exists, rendering the check invalid.

What you do instead depends on why you want to know. One very common case is that you only want to create the file if it doesn't already exist, in which case you use the lower-level open function in O_EXCL mode:

int fd = open("whatever", O_WRONLY|O_CREAT|O_EXCL, 0666);
if (fd == -1 && errno == EEXIST) {
    /* the file already exists */
} else if (fd == -1) {
    /* report that some other error happened */
} else {
    FILE *fp = fdopen(fd, "w");
    /* write data to fp here */
}

Another very common case is that you want to create the file if it doesn't exist, or append new data to the file if it does; this can be done with the "a" mode to fopen or O_APPEND flag to open.

zwol
  • 135,547
  • 38
  • 252
  • 361
  • Thanks, zwoi. Goofy and hard-to-guess reason "why you [I] want to know" is described at https://stackoverflow.com/questions/44719626/ (too long to reproduce on a comment). Briefly, I want browsers to client-side cache 's generated by a server-side script. But running the script from a tag makes the browser think it's a dynamic tag, and the returned data doesn't get client-side cached. That preceding link describes one (overly elaborate) solution involving the check-if-exists question I'm asking here. Thanks again. – John Forkosh Jun 24 '17 at 23:06
  • @JohnForkosh I think your question has been answered that is why you marked it. If you want to ask another question or want solutions to another question i suggest you don't do that in a comment. Is either you want for your other question to be answered or you add a Bounty on it if is that serious. This is how SO works – Seek Addo Jun 24 '17 at 23:19
  • @SeekAddo I wasn't asking another question -- as you say, all my questions (except the one about winning lottery numbers) have already been answered. I was just clarifying to zwoi why I asked it in the first place. He went to lots of effort explaining why I probably shouldn't be doing what I was doing, so I just wanted to clarify the unusual reason why I was doing it, and why it should be done in this situation. And anyway, I'm not even seeing anything that could be interpreted as "another question" in my preceding comment. – John Forkosh Jun 24 '17 at 23:30
  • @JohnForkosh Unfortunately, I don't know enough about HTTP and caching to answer your other question, but I have to believe there's a simpler approach. – zwol Jun 25 '17 at 03:12
  • @zwoi Yeah, it seems there actually may be a simpler approach -- involving using PATH_INFO environment variable rather than query_strings, and possibly etag's https://en.wikipedia.org/wiki/HTTP_ETag and similar stuff. I wrote some small tests, and they work at avoiding query_strings, but the browser's still treating them as dynamic tags, at least with everything I've tried. But I'm still hoping that'll somehow eventually work. – John Forkosh Jun 27 '17 at 06:52
4

On Windows, there is PathFileExists().

On a POSIX system, you have stat() or access().

That said, if you check for existence of the file because your code needs the file, this is the wrong approach -- file systems are out of your program's control, so this would be a race condition, the only correct way would be to properly handle errors when opening the file.

  • Thanks, Felix (I'll "check" shortly -- tried just now, but it said "you can accept in 4 minutes"). But situation's "goofier" than you're thinking, and I'm not seeing any alternative. Check that link https://stackoverflow.com/questions/44719626/ for details. – John Forkosh Jun 24 '17 at 22:11
  • 1
    @JohnForkosh uhm why not do image generation and returning the filename in the same program? You could just use `open()` with `O_WRONLY` (on POSIX) and if it succeeds, write your image data (if not, the file was already there, check for `errno==EEXIST` to be sure) before returning the cached name ... –  Jun 24 '17 at 22:17
  • Yeah, that's what the server-side cgi program is actually doing already. But running any server-side program requires a dynamic tag, in which case the browser never caches the returned data on the client side. So the filenames returned by this little program are never cached either, but that's only a few dozen bytes which are re-downloaded time-after-time. But running the image-generation program and not caching the returned image data means re-downloading many MB's time-after-time. – John Forkosh Jun 24 '17 at 22:25
  • 1
    @JohnForkosh I don't get the problem. You're generating the IMG url with a script, right? so this script could just *call* your C program that returns the filename and, if the file doesn't exist yet, creates it? –  Jun 24 '17 at 22:28
  • Oh, yeah, then that's pretty much exactly what the script already does -- it's just factored/decomposed slightly differently. With your factoring I always need two tags -- (a) first a dynamic tag to return the filename (and generate it if it doesn't already exist), and then (b) a static tag with src="filename" to download and locally cache the now-guaranteed-to-exist filename. I'm doing it with one tag by just (a) first curl_exec()'ing a short program to check whether the file already exists, and then (b) php-echo'ing either a static tag if it exists, or a dynamic tag if not. Six-of-one, etc. – John Forkosh Jun 24 '17 at 22:38