views:

133

answers:

3

I'm trying to check if a folder has any subfolders without iterating through its children, in Linux. The closest I've found so far is using ftw and stopping at the first subfolder - or using scandir and filtering through the results. Both, are, however, an overkill for my purposes, I simply want a yes/no.

On Windows, this is done by calling SHGetFileInfo and then testing dwAttributes & SFGAO_HASSUBFOLDER on the returned structure. Is there such an option on Linux?

A: 

Does getdirentries do want you want it to do? I think it shoudl return nothing if there are no directories. I would have tried this myself but am temporarily without access to a linux box :(

Gray Area
It, of course, returns subdirs AND files. Also, "CONFORMING TO Not in POSIX.1-2001. Present on the BSDs, and a few other systems. Use opendir(3) and readdir(3) instead."And filtering the `readdir(3)` output is the only way, because the "has not subdirectories" flag doesn't exist.
wRAR
+3  A: 

The standard answer is to call stat on the directory, then check the st_nlink field ("number of hard links"). On a standard filesystem, each directory is guaranteed to have 2 hard links (. and the link from the parent directory to the current directory), so each hard link beyond 2 indicates a subdirectory (specifically, the subdirectory's .. link to the current directory).

However, it's my understanding that filesystems aren't required to implement this (see, e.g., this mailing list posting), so it's not guaranteed to work.

Otherwise, you have to do as you're doing:

  1. Iterate over the directory's contents using glob with the GNU-specific GLOB_ONLYDIR flag, or scandir, or readdir.
  2. Call stat on each result and check S_ISDIR(s.st_mode) to verify that files found are directories. Or, nonportably, check struct dirent.d_type: if it's DT_DIR then it's a file, and if it's DT_UNKNOWN, you'll have to stat it after all.
Josh Kelley
+1, but `..` is not a link to the given directory; it's a link to the parent. However its reciprocal is one of the two.
Potatoswatter
`struct dirent.d_type`, though "not supported by all file system types", eliminates the need of `stat(2)`
wRAR
@Potatocorn, @wRAR - I updated my answer. Thanks.
Josh Kelley
It seems it can be reasonably determined if the `st_nlink` field is properly implemented. I'll use a combination of the two options you presented
laura
+2  A: 

The possibilities you've mentioned (as well as e.James's) seem to me like they're better suited to a shell script than a C++ program. Presuming the "C++" tag was intentional, I think you'd probably be better off using the POSIX API directly:

// warning: untested code.
bool has_subdir(char const *dir) { 
    std::string dot("."), dotdot("..");
    bool found_subdir = false;    
    DIR *directory;

    if (NULL == (directory = opendir(dir)))
        return false;

    struct dirent *entry;
    while (!found_subdir && ((entry = readdir(directory)) != NULL)) {
        if (entry->d_name != dot && entry->d_name != dotdot) {
            struct stat status;
            stat(entry->d_name, &status);
            found_subdir = S_ISDIR(status.st_mode);
        }
    }
    closedir(directory);
    return found_subdir;
}
Jerry Coffin
If you want to optimize the `string`s by hoisting from the loop, you might as well make them `static` too.
Potatoswatter
The C++ tag was intentional, although incorrect. I'll update it so it is relevant.
laura
@Potatocorn:Good point -- actually, it wouldn't hurt to make them `static const`.
Jerry Coffin