I am making a program which is run in a Linux shell, and accepts an argument (a directory), and displays all the files in the directory, along with their type.
Output should be like this:
<< ./Program testDirectory
Dir directory1
lnk linkprogram.c
reg file.txt
If no argument is made, it uses the current directory. Here is my code:
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
int main(int argc, char *argv[])
{
struct stat info;
DIR *dirp;
struct dirent* dent;
//If no args
if (argc == 1)
{
argv[1] = ".";
dirp = opendir(argv[1]); // specify directory here: "." is the "current directory"
do
{
dent = readdir(dirp);
if (dent)
{
printf("%c ", dent->d_type);
printf("%s \n", dent->d_name);
/* if (!stat(dent->d_name, &info))
{
//printf("%u bytes\n", (unsigned int)info.st_size);
}*/
}
} while (dent);
closedir(dirp);
}
//If specified directory
if (argc > 1)
{
dirp = opendir(argv[1]); // specify directory here: "." is the "current directory"
do
{
dent = readdir(dirp);
if (dent)
{
printf("%c ", dent->d_type);
printf("%s \n", dent->d_name);
/* if (!stat(dent->d_name, &info))
{
printf("%u bytes\n", (unsigned int)info.st_size);
}*/
}
} while (dent);
closedir(dirp);
}
return 0;
}
For some reason dent->d_type
is not displaying the type of file. I'm not really sure what to do, any suggestions?
The
d_type
in the return struct gives a number for the type. You can't print that directly because the used values are not printable when interpreted as ASCII (for example they are 4 for dirs and 8 for files.).You can either print them as numbers like this:
Or compare them to the constants like
DT_DIR
and construct some meaningful output from that, like a char type:Print
d_type
as an integer like so:and you'll see meaningful values.
d_type
is a speed optimization to save onlstat(2)
calls, when it's supported.As the
readdir
(3) man page points out, not all filesystems return real info in thed_type
field (typically because it would take an extra disk seek to read the inode, as is the case for XFS if you didn't usemkfs.xfs -n ftype=1
(implied by-m crc=1
which is not yet the default). Filesystems that always setDT_UNKNOWN
are common in real life, and not something that you can ignore. XFS is not the only example.You always need code that will fall back to using
lstat
(2) ifd_type==DT_UNKNOWN
, if the filename alone isn't enough to decide it's uninteresting. (This is the case for some callers, likefind -name
or expanding globs like*.c
, which is whyreaddir
doesn't incur the overhead of filling it in if it would take an extra disk read.)The Linux
getdents(2)
man page has an example program that does what you're trying to do, including a chained-ternary-operator block to decode thed_type
field into text strings. (As the other answers point out, your mistake is printing it out as an character, rather than comparing it againstDT_REG
,DT_DIR
, etc.)Anyway, the other answers mostly covered things, but missed the critical detail that you NEED a fallback for the case when
d_type == DT_UNKNOWN
(0 on Linux.d_type
is stored in what used to be a padding byte, until Linux 2.6.4).To be portable, your code needs to check that
struct dirent
even HAS ad_type
field, if you use it, or your code won't even compile outside of GNU and BSD systems. (seereaddir(3)
)I wrote an example for finding directories with readdir, using
d_type
with a fallback tostat
when d_type isn't available at compile time, when it's DT_UNKNOWN, and for symlinks.