Directory Entry File Types

Not every file listed in a directory is the same. Some entries represent other directories, subdirectories, for example. To determine which entries are mere mortal files and which are directories, you must examine the file type.

The good news is that file type information is held in the same dirent structure that yields the file’s name. The structure member is d_type. The bad news is that not every operating system features the d_type member in its dirent structure.

The following code modifies the second example from last week’s Lesson to display the d_type member’s value for the current directory:

#include <stdio.h>
#include <dirent.h>

int main()
{
    DIR *folder;
    struct dirent *entry;
    int files = 0;

    folder = opendir(".");
    if(folder == NULL)
    {
        perror("Unable to read directory");
        return(1);
    }

    while( (entry=readdir(folder)) )
    {
        files++;
        printf("File %3d: %s, %d\n",
                files,
                entry->d_name,
                entry->d_type
              );
    }

    closedir(folder);

    return(0);
}

The only modification to this code from what was shown last week is the printf() statement starting at Line 20. I added the entry->d_type item and used the %d placeholder to display its integer value. Here’s sample output:

File   1: .,4
File   2: ..,4
File   3: 0825a.c,8
File   4: 0825b.c,8
File   5: 08exercise-a.c,8
File   6: 08exercise-b.c,8
File   7: 0901.c,8
File   8: 0908.c,8
File   9: a.out,8
File  10: gettysburg.txt,8
File  11: sto,4

The values returned are specific to the operating system. Under Unix, value 8 is a regular file and 4 is a directory. On my PC, the MinGW compiler’s C library lacks the dirent->d_type member. So the code doesn’t compile. That’s okay, because a better way is available to read whether a directory entry is a file or directory: Use the stat() function, as defined in the sys/stat.h header file:

int stat(const char *restrict path, struct stat *restrict buf);

The stat() function requires two arguments. The first is the name (or pathname) to a filename. The second argument is the address of a stat structure. This structure is filled with oodles of good info about a directory entry and it’s consistent across all file systems.

The sys/stat.h header file also defines macros to test for file type, which work similarly to the ctype.h macros that examine characters. For a directory entry, the S_ISDIR macro is used:

#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>

int main()
{
    DIR *folder;
    struct dirent *entry;
    struct stat filestat;

    folder = opendir(".");
    if(folder == NULL)
    {
        perror("Unable to read directory");
        return(1);
    }

    /* Read directory entries */
    while( (entry=readdir(folder)) )
    {
        stat(entry->d_name,&filestat);
        if( S_ISDIR(filestat.st_mode) )
            printf("%4s: %s\n","Dir",entry->d_name);
        else
            printf("%4s: %s\n","File",entry->d_name);
    }
    closedir(folder);

    return(0);
}

The stat() function appears at Line 21. Its arguments are the name of the file located in the directory (entry->d_name) and the address of the stat structure filestat, declared at Line 9.

In Line 22, the S_ISDIR macro tests the filestat.st_mode member. If true, the entry is a directory and it’s flagged as such in the output. Otherwise, the else statement flags the entry as a standard file.

Here’s sample output:

 Dir: .
 Dir: ..
File: 0825a.c
File: 0825b.c
File: 08exercise-a.c
File: 08exercise-b.c
File: 0901a.c
File: 0901b.c
File: 0908.c
File: a.out
File: gettysburg.txt
 Dir: sto

This code runs on both the PC and Unix systems, thanks to the stat() function.

You can pull more details from the stat() function, including file size and timestamps. I’ll cover that process in next week’s Lesson.

Leave a Reply