3

So I have a project where I need to build a small simple text shell that can run, edit, and read files from a directory. I have a small prototype that should work, except when I compile, I receive an error about d_type not found within the struct dirent used in the dirent.h header file.

d = opendir( "." );
c = 0;
while ((de = readdir(d))){
    if ((de->de_type) & DT_DIR)
    printf( " ( %d Directory: %s ) \n", c++, de->de_name);
}

the variable "de" is of type struct dirent* and is being checked for it's type, and I get the error: 'struct dirent' has no member named 'de_type'

Here's where I'm really stumped and confused: I've compiled this code on both windows(using dev C++), and on Ubuntu(using gcc). I have recieved the same error on both OS's, and when I checked the library used, which is the normal gnu C library I believe, there is a variable there named d_type:

https://www.gnu.org/software/libc/manual/html_node/Directory-Entries.html

I have found other references to a dirent.h file that doesn't because one is in a different library, and if that is the case, how do I load that library so I can compile the code?

Sorry for the long post and many thanks to all who answer!

JustaRedShirt
  • 185
  • 1
  • 8

1 Answers1

9

From man readdir(3):

The only fields in the dirent structure that are mandated by POSIX.1 are: d_name[], of unspecified size, with at most NAME_MAX characters preceding the terminating null byte; and (as an XSI extension) d_ino. The other fields are unstandardized, and not present on all systems; see NOTES below for some further details.

then continues

Only the fields d_name and d_ino are specified in POSIX.1-2001. The remaining fields are available on many, but not all systems. Under glibc, programs can check for the availability of the fields not defined in POSIX.1 by testing whether the macros _DIRENT_HAVE_D_NAMLEN, _DIRENT_HAVE_D_RECLEN, _DIRENT_HAVE_D_OFF, or _DIRENT_HAVE_D_TYPE are defined.

Other than Linux, the d_type field is available mainly only on BSD systems. This field makes it possible to avoid the expense of calling lstat(2) if further actions depend on the type of the file. If the _BSD_SOURCE feature test macro is defined, then glibc defines the following macro constants for the value returned in d_type:

So I suggest to just go ahead and use stat() to check the type of the entry. (Or lstat() to not follow symlinks.) The struct stat contains the field st_mode which can be checked using the POSIX macro S_ISDIR(m) to test if it is a directory.


Addendum: See @R..'s comments below and this answer. In summary:

  1. Use the right thing. Add -D_FILE_OFFSET_BITS=64 to your compiler flags and build with 64-bit file offsets.
  2. Check if you have d_type with the preprocessor macro _DIRENT_HAVE_D_TYPE. If so, use d_type. Getting the information you want from the directory table (if available) will be more efficient than seeking to & reading all the inodes for the files.
  3. As a fall back measure, (do as above) use stat to read the inodes and check the st_mode with the S_ISDIR() macro (or similar check).
Community
  • 1
  • 1
e0k
  • 6,961
  • 2
  • 23
  • 30
  • 1
    There's a detail that you missed - the reason `d_type` is missing for the OP is that it's missing in the legacy 32-bit-`off_t` version of `dirent` that you **should never be using**. Build with `-D_FILE_OFFSET_BITS=64` and everything will be fine. – R.. GitHub STOP HELPING ICE Feb 05 '16 at 03:59
  • @R.. For compatibility, would it be better to just `stat` the entries? – e0k Feb 05 '16 at 04:04
  • 3
    Using `stat` is going to be an order of magnitude slower or worse because runtime for code like this is roughly proportional to the number of syscalls made, and `stat` is a rather heavy syscall on top of that. That's before you even get to the IO burden: by using `stat` you perform IO on the inodes for each dir entry (heavy random access) rather than just reading the directory table (compact linear read). You should provide a portable fallback to `stat` if `_DIRENT_HAVE_D_TYPE` is not defined, but otherwise **definitely use `d_type`** if you have it. – R.. GitHub STOP HELPING ICE Feb 05 '16 at 04:34