48

I'm writing a program to check if something is a file or is a directory. Is there a better way to do it than this?

#include <stdio.h>

#include <sys/types.h>
#include <dirent.h>
#include <errno.h>

int isFile(const char* name)
{
    DIR* directory = opendir(name);

    if(directory != NULL)
    {
     closedir(directory);
     return 0;
    }

    if(errno == ENOTDIR)
    {
     return 1;
    }

    return -1;
}

int main(void)
{
    const char* file = "./testFile";
    const char* directory = "./";

    printf("Is %s a file? %s.\n", file,
     ((isFile(file) == 1) ? "Yes" : "No"));

    printf("Is %s a directory? %s.\n", directory,
     ((isFile(directory) == 0) ? "Yes" : "No"));

    return 0;
}
Jookia
  • 6,544
  • 13
  • 50
  • 60

4 Answers4

91

You can call the stat() function and use the S_ISREG() macro on the st_mode field of the stat structure in order to determine if your path points to a regular file:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int is_regular_file(const char *path)
{
    struct stat path_stat;
    stat(path, &path_stat);
    return S_ISREG(path_stat.st_mode);
}

Note that there are other file types besides regular and directory, like devices, pipes, symbolic links, sockets, etc. You might want to take those into account.

Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479
  • 8
    When considering symlink cases, use lstat() instead of stat() as it does not follow the symlink. – Sparky Dec 29 '10 at 12:13
  • 4
    There's at least a good discussion at [Checking if a directory exists](http://stackoverflow.com/questions/3828192/checking-if-a-directory-exists-in-unix-system-call/), where `stat()` and `lstat()` are discussed, and the complete set of POSIX file types is outlined. I'm tempted to duplicate this question to that one. The code should check the result of `stat()`, too, and handle errors appropriately. – Jonathan Leffler Mar 13 '15 at 22:49
  • @Jonathan, that answer is indeed more complete than mine, and was posted three months before mine, five years ago. And it's not like my own answer would disappear anyway. I see you wield Mjölnir in the `[c]` tag, so thank you for commenting before acting unilaterally. However, you're ultimately right. No worries, do what you have to do :) – Frédéric Hamidi Mar 13 '15 at 22:56
  • 1
    this does not work on osx for me... – v0d1ch Jan 17 '16 at 18:15
  • 1
    @vodish, maybe `stat()` is failing for you? What about the code in the duplicate, does it work for you? – Frédéric Hamidi Jan 17 '16 at 18:19
  • 1
    This code is brittle: you should check that `stat` succeeds. Use `return !stat(path, &path_stat) && S_ISREG(path_stat.st_mode);` – chqrlie Jul 15 '22 at 23:50
  • 1
    @chqrlie is right, if the file does not exists for instance, the structure is not modified by the system call, and Valgrind reports that the jump depends on uninitialized value. It took me a while to figure out that random failure. – fgiraldeau May 15 '23 at 02:10
40

Use the S_ISDIRmacro:

int isDirectory(const char *path) {
   struct stat statbuf;
   if (stat(path, &statbuf) != 0)
       return 0;
   return S_ISDIR(statbuf.st_mode);
}
ismail
  • 46,010
  • 9
  • 86
  • 95
  • You forgot to fill the `statbuf` struct with information. – RedX Dec 29 '10 at 09:52
  • 1
    This works fine as-is, stat function fills the info itself. – ismail Dec 29 '10 at 09:54
  • 4
    Ideally, the code would check that `stat` worked: `if (stat(path, &statbuf) != 0) return 0;` -- because a non-existent object is not a directory, and if you don't have permission to `stat()` it, it may as well not exist (even if the error reported is related to permissions). – Jonathan Leffler Mar 13 '15 at 22:48
  • 2
    @JonathanLeffler you are absolutely right, updated the code. – ismail Mar 14 '15 at 15:47
4

Yes, there is better. Check the stat or the fstat function

KARASZI István
  • 30,900
  • 8
  • 101
  • 128
3

Normally you want to perform this check atomically with using the result, so stat() is useless. Instead, open() the file read-only first and use fstat(). If it's a directory, you can then use fdopendir() to read it. Or you can try opening it for writing to begin with, and the open will fail if it's a directory. Some systems (POSIX 2008, Linux) also have an O_DIRECTORY extension to open which makes the call fail if the name is not a directory.

Your method with opendir() is also good if you want a directory, but you should not close it afterwards; you should go ahead and use it.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711