#if defined(LIBC_SCCS) && !defined(lint)
static const char rcsid[] = "$OpenBSD: nftw.c,v 1.2 2003/07/21 21:15:32 millert Exp $";
#endif
#include <stdio.h>
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fts.h>
#include <ftw.h>
#include <limits.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
static int
both_ftw(const char *path,
int (*ofn)(const char *, const struct stat *, int),
int (*nfn)(const char *, const struct stat *, int, struct FTW *),
int nfds, int ftwflags)
{
const char *paths[2];
struct FTW ftw;
FTSENT *cur;
FTS *ftsp;
int ftsflags, fnflag, error, postorder, sverrno;
int cwd_fd = -1;
#if __DARWIN_UNIX03
#define SKIP_MOUNT if ((ftwflags & FTW_MOUNT) \
&& cur->fts_statp->st_dev != path_stat.st_dev) { \
continue; \
}
#else
#define SKIP_MOUNT
#endif
if (nfds < 1 || nfds > OPEN_MAX) {
errno = EINVAL;
return (-1);
}
ftsflags = FTS_COMFOLLOW;
if (!(ftwflags & FTW_CHDIR))
ftsflags |= FTS_NOCHDIR;
if (ftwflags & FTW_MOUNT)
ftsflags |= FTS_XDEV;
if (ftwflags & FTW_PHYS) {
ftsflags |= FTS_PHYSICAL;
} else {
ftsflags |= FTS_LOGICAL;
}
postorder = (ftwflags & FTW_DEPTH) != 0;
if (ftwflags & FTW_CHDIR) {
cwd_fd = open(".", O_RDONLY, 0);
if (cwd_fd < 0) {
return -1;
}
ftsflags |= FTS_NOCHDIR;
}
#if __DARWIN_UNIX03
struct stat path_stat;
{
int rc = stat(path, &path_stat);
int e = errno;
if (rc < 0
&& (errno == ELOOP || errno == ENAMETOOLONG || errno == ENOENT
|| errno == ENOTDIR || errno == EACCES)) {
return -1;
}
if (rc >= 0 && nfn) {
if (!S_ISDIR(path_stat.st_mode)) {
errno = ENOTDIR;
return -1;
}
}
}
#endif
paths[0] = path;
paths[1] = NULL;
ftsp = fts_open((char * const *)paths, ftsflags, NULL);
if (ftsp == NULL) {
return (-1);
}
error = 0;
while ((cur = fts_read(ftsp)) != NULL) {
switch (cur->fts_info) {
case FTS_D:
if (postorder)
continue;
SKIP_MOUNT;
if (access(cur->fts_path, R_OK) != 0)
continue;
fnflag = FTW_D;
break;
case FTS_DNR:
fnflag = FTW_DNR;
break;
case FTS_DP:
if (!postorder)
continue;
SKIP_MOUNT;
fnflag = FTW_DP;
break;
case FTS_F:
case FTS_DEFAULT:
fnflag = FTW_F;
break;
case FTS_NS:
case FTS_NSOK:
fnflag = FTW_NS;
break;
case FTS_SL:
fnflag = FTW_SL;
break;
case FTS_SLNONE:
fnflag = nfn ? FTW_SLN : FTW_SL;
#if __DARWIN_UNIX03
{
struct stat sb;
int rc = stat(cur->fts_path, &sb);
if (rc < 0 && errno == ELOOP) {
error = -1;
goto done;
}
}
#endif
break;
case FTS_DC:
#if __DARWIN_UNIX03
if (nfn && !(ftwflags & FTW_PHYS)) {
fnflag = FTW_D;
break;
}
#endif
errno = ELOOP;
default:
error = -1;
goto done;
}
if (cwd_fd >= 0) {
char *dir, *free_me = NULL;
if (fnflag == FTW_D) {
dir = cur->fts_path;
} else {
dir = free_me = strdup(cur->fts_path);
dir[cur->fts_pathlen - cur->fts_namelen] = '\0';
}
int rc = chdir(dir);
if (free_me) {
free(free_me);
}
if (rc < 0) {
error = -1;
goto done;
}
}
if (nfn) {
ftw.base = cur->fts_pathlen - cur->fts_namelen;
ftw.level = cur->fts_level;
error = nfn(cur->fts_path, cur->fts_statp, fnflag, &ftw);
} else {
error = ofn(cur->fts_path, cur->fts_statp, fnflag);
}
if (cwd_fd >= 0) {
if (fchdir(cwd_fd) < 0) {
error = -1;
goto done;
}
}
if (error != 0)
break;
}
done:
sverrno = errno;
(void) fts_close(ftsp);
errno = sverrno;
return (error);
}
int
ftw(const char *path, int (*fn)(const char *, const struct stat *, int),
int nfds)
{
return both_ftw(path, fn, NULL, nfds, FTW_PHYS);
}
int
nftw(const char *path, int (*fn)(const char *, const struct stat *, int,
struct FTW *), int nfds, int ftwflags)
{
return both_ftw(path, NULL, fn, nfds, ftwflags);
}