7

I am using fopen() to determine whether I'm opening a file or a directory like this:

FILE *f;

f = fopen(path, "r");

if (f != NULL){

// found file

}

else {

// found a directory

}

And path is currently a path pointing to a directory, not a file and it looks something like this:

/home/me/Desktop/newfolder

However, when I run the code, it says that it found a file even though it's pointing to a folder and thus it should return NULL pointer, or not?

I'm working on Ubuntu.

Rachid K.
  • 4,490
  • 3
  • 11
  • 30
Daeto
  • 440
  • 1
  • 6
  • 19

3 Answers3

8

The C standard doesn't mention anything about fopening directories.

But http://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html says the following (emphasis mine):

The fopen() function will fail if:
[…]
[EISDIR] The named file is a directory and mode requires write access.

So if you change fopen(path, "r") to e.g. fopen(path, "r+") then it should fail if path refers to a directory:

bool isDirectory(const char* path) {
    FILE *f = fopen(path, "r+");
    if (f) {
        fclose(f);
        return false;
    }
    return errno == EISDIR;
}
Emil Laine
  • 41,598
  • 9
  • 101
  • 157
  • 4
    This works... but it's a bad solution because it will cause your program to fail to open any file that you only have read access to. – Evan Oct 20 '19 at 21:09
3

Try this first:

#include <dirent.h>

DIR* dir = opendir(path);
if (dir)
{
    /* Directory exists. Do stuff. */
    closedir(dir);
}
if(errno == ENOTDIR)
{   
    /* Exists but is not a directory */
}
jhh
  • 663
  • 4
  • 6
  • The problem is, I need to determine not just whether the directory exists but also whether the input is a file or not. – Daeto Mar 18 '17 at 15:39
  • So check to see if it is a file after you know that it is not a directory :) But that shouldn't be necessary. I believe `errno == ENOTDIR` implies that something is there but not a directory, which means a file. – jhh Mar 18 '17 at 16:11
  • what if it is a symbolic link? – Yossarian42 Jan 28 '21 at 05:31
2

show the code first

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

int is_reg_file(const char *path)
{
    struct stat statbuf;
    stat(path, &statbuf);
    // test for a regular file
    if (S_ISREG(statbuf.st_mode))
        return 1;
    return 0;
}

int is_dir(const char *path)
{
    struct stat statbuf;
    stat(path, &statbuf);
    if (S_ISDIR(statbuf.st_mode))
        return 1;
    return 0;
}

stat structure is defined as following, details can be found in stat(2) manual.

struct stat {
    dev_t     st_dev;         /* ID of device containing file */
    ino_t     st_ino;         /* Inode number */
    mode_t    st_mode;        /* File type and mode */
    nlink_t   st_nlink;       /* Number of hard links */
    uid_t     st_uid;         /* User ID of owner */
    gid_t     st_gid;         /* Group ID of owner */
    dev_t     st_rdev;        /* Device ID (if special file) */
    off_t     st_size;        /* Total size, in bytes */
    blksize_t st_blksize;     /* Block size for filesystem I/O */
    blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */
    ...
};

The file type defined in st_mode can be found in inode(7) manual along with some macros.

  POSIX  refers  to the stat.st_mode bits corresponding to the mask S_IFMT (see below)
       as the file type, the 12 bits corresponding to the mask 07777 as the file mode  bits
       and the least significant 9 bits (0777) as the file permission bits.

       The following mask values are defined for the file type:

           S_IFMT     0170000   bit mask for the file type bit field

           S_IFSOCK   0140000   socket
           S_IFLNK    0120000   symbolic link
           S_IFREG    0100000   regular file
           S_IFBLK    0060000   block device
           S_IFDIR    0040000   directory
           S_IFCHR    0020000   character device
           S_IFIFO    0010000   FIFO

Yossarian42
  • 1,950
  • 17
  • 14