Search code examples
c++linuxdirectoryio

opendir() failing on name with a space, running on linux


My program explores the current directory and all sub directories to print all file names. However, it produces a segmentation fault when a directory has a space in its name. This only occurs on linux - windows it works fine.

The name is stored in dirent.d_name, which is a char[256]. I have tried using it, converting it to a c-string using c_str(), I've tried hard coding the directory name into the code, I've tried escaping the space (although I don't think I'm doing it correctly).

int main()
{
    struct dirent *direntry;
    dir = opendir( "hello\ world" );
    print_dir_rec( dir, direntry );
    return 0;
}

void print_dir_rec( DIR *dir, struct dirent *direntry )
{
    while( direntry = readdir(dir) )
    {
        switch( direntry->d_type )
        {
            case DT_DIR:
                DIR *sub_dir = opendir( direntry->d_name );
                print_dir_rec( sub_dir , direntry );
                break;
        }
    }
    return;
}

Solution

  • You are running into problems by passing a DIR* pointer and struct dirent* pointer as parameters instead of simply forming and passing the next path to open. You want to handle the opening of the directory within the recursive function itself, not as a single pointer passed from main(), e.g.

    void print_dir_rec(const char *name)
    {
        DIR *dir;
        struct dirent *entry;
    
        if (!(dir = opendir(name)))
            return;
    
        while ((entry = readdir(dir)) != NULL) {
            if (entry->d_type == DT_DIR) {
                char path[1024];
                if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
                    continue;
                snprintf(path, sizeof(path), "%s/%s", name, entry->d_name);
                printf("[%s]\n", entry->d_name);
                print_dir_rec(path);
            }
        }
        closedir(dir);
    }
    

    (note: you can adjust the number of chars provided for path as needed or by using the PATH_MAX macro)

    That way, there is no single pointer that is reused throughout every recursive call causing problems. A short example that lists all directories under the current could be:

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <dirent.h>
    #include <sys/types.h>
    
    void print_dir_rec(const char *name)
    {
        DIR *dir;
        struct dirent *entry;
    
        if (!(dir = opendir(name)))
            return;
    
        while ((entry = readdir(dir)) != NULL) {
            if (entry->d_type == DT_DIR) {
                char path[1024];
                if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
                    continue;
                snprintf(path, sizeof(path), "%s/%s", name, entry->d_name);
                printf("[%s]\n", entry->d_name);
                print_dir_rec(path);
            }
        }
        closedir(dir);
    }
    
    int main(void) {
        print_dir_rec(".");
        return 0;
    }
    

    Look at how the opening and reading is handled and what information is required to be passed. By providing storage for path within each recursive call, you guarantee the name remains in scope until that recursive call returns.

    Let me know if you have further questions.