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.