Note that POSIX 2008 introduces fstatat()
and related functions (system calls), all distinguished by the at
suffix to a familiar function name. It also defines dirfd()
to get the file descriptor associated with a directory stream.
The *at()
functions take one (or two in the case of renameat()
) open file descriptors that refer to a directory. This means that another way of coding this, on a system that supports fstatat()
would be:
const char *name = argv[i];
DIR *dp = opendir(dirname);
if (dp == NULL)
{
fprintf(stderr, "failed to open directory %s (%d: %s)\n",
name, errno, strerror(errno));
return -1;
}
int dfd = dirfd(dp); /* Very, very unlikely to fail */
struct dirent *dirp;
while ((dirp = readdir(dp)) != NULL)
{
struct stat sb;
if (fstatat(dfd, dirp->d_name, &sb, 0) == -1) {
fprintf(stderr, "fstatat(\"%s/%s\") failed (%d: %s)\n",
name, dirp->d_name, errno, strerror(errno));
}
else
printf("%-20s %s/%s\n", "File name:", name, dirp->d_name);
}
Using fstatat()
and related functions allows you to use relative pathnames without using chdir()
(which is dangerous; it is hard to get back to where you started without using fchdir()
), or concatenating names as shown in the main accepted answer. For portability, it is probably still advisable to use concatenation anyway — but I was able to test this on Mac OS X (10.10.1) and Linux (Ubuntu 14.04), using the code below.
Developed into a complete program (test-fstatat.c
):
/* SO 0512-5919 */
#define _XOPEN_SOURCE 700 /* POSIX 2008 plus ... */
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char **argv)
{
if (argc < 2)
{
fprintf(stderr, "Usage: %s directory [...]\n", argv[0]);
return -1;
}
for (int i = 1; i < argc; i++)
{
const char *name = argv[i];
DIR *dp = opendir(name);
if (dp == NULL)
{
fprintf(stderr, "failed to open directory %s (%d: %s)\n",
name, errno, strerror(errno));
return -1;
}
int dfd = dirfd(dp); /* Very, very unlikely to fail */
printf("%-20s %s\n", "Directory:", name);
struct dirent *dirp;
while ((dirp = readdir(dp)) != NULL)
{
struct stat sb;
if (fstatat(dfd, dirp->d_name, &sb, 0) == -1) {
fprintf(stderr, "fstatat(\"%s/%s\") failed (%d: %s)\n",
name, dirp->d_name, errno, strerror(errno));
}
else
printf("%-20s %s/%s\n", "File name:", name, dirp->d_name);
}
closedir(dp);
}
return 0;
}
Sample run:
$ test-fstatat ~/bin/JLSS-Dist/RCS ../../../src/sqltools/idsmon
Directory: /Users/jonathanleffler/bin/JLSS-Dist/RCS
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/.
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/..
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/mkbod.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/jlssdist.jdc,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/old.msd.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/chksumtool.pl,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/msd2nmd.pl,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/mknmd.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/publictimestamp.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/new.mknmd.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/PRODCODE,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/prodverstamp.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/md5.create.pl,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/jdcrelease.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/JLSS-Dist.mk,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/PRODUCT,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/msd.create.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/distribution.mk,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/nmd.create.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/jlss.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/VERSION,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/cvtjdc.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/redonmd.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/updmsd.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/setnmd.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/list2msd.pl,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/chkmsdnmd.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/vercmp.pl,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/MSD.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/setjdcversion.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/sortnmd.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/gennmd.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/md5.verify.pl,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/setbomversion.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/chkbodlst.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/updnmd.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/domsd.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/md5.chksum.pl,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/bomrelease.pl,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/mkmsd.sh,v
File name: /Users/jonathanleffler/bin/JLSS-Dist/RCS/fixnmd.sh,v
Directory: ../../../src/sqltools/idsmon
File name: ../../../src/sqltools/idsmon/.
File name: ../../../src/sqltools/idsmon/..
File name: ../../../src/sqltools/idsmon/idstest.c
File name: ../../../src/sqltools/idsmon/idslen
File name: ../../../src/sqltools/idsmon/install-sh
File name: ../../../src/sqltools/idsmon/scatterinfo
File name: ../../../src/sqltools/idsmon/ltmain.sh
File name: ../../../src/sqltools/idsmon/idsmon.msd
File name: ../../../src/sqltools/idsmon/idsmon.o
File name: ../../../src/sqltools/idsmon/configure
File name: ../../../src/sqltools/idsmon/genscatter
File name: ../../../src/sqltools/idsmon/config
File name: ../../../src/sqltools/idsmon/idspacket
File name: ../../../src/sqltools/idsmon/genconnpacket
File name: ../../../src/sqltools/idsmon/Makefile
File name: ../../../src/sqltools/idsmon/config.h.in
File name: ../../../src/sqltools/idsmon/config.guess
File name: ../../../src/sqltools/idsmon/depcomp
File name: ../../../src/sqltools/idsmon/sqlihexdump.o
File name: ../../../src/sqltools/idsmon/missing
File name: ../../../src/sqltools/idsmon/install.mk
File name: ../../../src/sqltools/idsmon/sqlihexdump
File name: ../../../src/sqltools/idsmon/RCS
File name: ../../../src/sqltools/idsmon/Makefile.am
File name: ../../../src/sqltools/idsmon/test.istar.logs.tar.gz
File name: ../../../src/sqltools/idsmon/idstest.o
File name: ../../../src/sqltools/idsmon/esqlc.mk
File name: ../../../src/sqltools/idsmon/config.sub
File name: ../../../src/sqltools/idsmon/idspacket.o
File name: ../../../src/sqltools/idsmon/compile
File name: ../../../src/sqltools/idsmon/Old.Releases
File name: ../../../src/sqltools/idsmon/esqlc-nosfx.mk
File name: ../../../src/sqltools/idsmon/osiris_11
File name: ../../../src/sqltools/idsmon/config.h.in~
File name: ../../../src/sqltools/idsmon/idstest
File name: ../../../src/sqltools/idsmon/idsmon.c
File name: ../../../src/sqltools/idsmon/acr.decode
File name: ../../../src/sqltools/idsmon/sqlihexdump.dSYM
File name: ../../../src/sqltools/idsmon/toru.istar
File name: ../../../src/sqltools/idsmon/dumpdblflt
File name: ../../../src/sqltools/idsmon/sqlipacket.c
File name: ../../../src/sqltools/idsmon/toru
File name: ../../../src/sqltools/idsmon/data.info.tgz
File name: ../../../src/sqltools/idsmon/idsmon.nmd
File name: ../../../src/sqltools/idsmon/idsmon.jdc
File name: ../../../src/sqltools/idsmon/idsmon
File name: ../../../src/sqltools/idsmon/idsmon.black_19
File name: ../../../src/sqltools/idsmon/Makefile.in
File name: ../../../src/sqltools/idsmon/aclocal.m4
File name: ../../../src/sqltools/idsmon/sqlihexdump.c
File name: ../../../src/sqltools/idsmon/dumpdblflt.dSYM
File name: ../../../src/sqltools/idsmon/dumpdblflt.c
$
d_name
for the invalid files? Also, what are all of the files in that directory anyway? Ie, we should see which files are missing. – chrisaycock