/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ian Lance Taylor (ian@airs.com). The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Modified by Ian Lance Taylor for Taylor UUCP, June 1992, and October 1993. */ #include "uucp.h" #include "sysdep.h" #include #if HAVE_LIMITS_H #include #endif #if HAVE_SYS_PARAM_H #include #endif #if HAVE_OPENDIR #if HAVE_DIRENT_H #include #else /* ! HAVE_DIRENT_H */ #include #define dirent direct #endif /* ! HAVE_DIRENT_H */ #endif /* HAVE_OPENDIR */ #if HAVE_FTW_H #include #endif #ifndef PATH_MAX #ifdef MAXPATHLEN #define PATH_MAX MAXPATHLEN #else #define PATH_MAX 1024 #endif #endif /* Traverse one level of a directory tree. */ static int ftw_dir (dirs, level, descriptors, dir, len, func) DIR **dirs; int level; int descriptors; char *dir; size_t len; int (*func) P((const char *file, struct stat *status, int flag)); { int got; struct dirent *entry; got = 0; errno = 0; while ((entry = readdir (dirs[level])) != NULL) { size_t namlen; struct stat s; int flag, ret, newlev = 0; ++got; namlen = strlen (entry->d_name); if (entry->d_name[0] == '.' && (namlen == 1 || (namlen == 2 && entry->d_name[1] == '.'))) { errno = 0; continue; } if (namlen + len + 1 > PATH_MAX) { #ifdef ENAMETOOLONG errno = ENAMETOOLONG; #else errno = ENOMEM; #endif return -1; } dir[len] = '/'; memcpy ((dir + len + 1), entry->d_name, namlen + 1); if (stat (dir, &s) < 0) { if (errno != EACCES) return -1; flag = FTW_NS; } else if (S_ISDIR (s.st_mode)) { newlev = (level + 1) % descriptors; if (dirs[newlev] != NULL) closedir (dirs[newlev]); dirs[newlev] = opendir (dir); if (dirs[newlev] != NULL) flag = FTW_D; else { if (errno != EACCES) return -1; flag = FTW_DNR; } } else flag = FTW_F; ret = (*func) (dir, &s, flag); if (flag == FTW_D) { if (ret == 0) ret = ftw_dir (dirs, newlev, descriptors, dir, namlen + len + 1, func); if (dirs[newlev] != NULL) { int save; save = errno; closedir (dirs[newlev]); errno = save; dirs[newlev] = NULL; } } if (ret != 0) return ret; if (dirs[level] == NULL) { int skip; dir[len] = '\0'; dirs[level] = opendir (dir); if (dirs[level] == NULL) return -1; skip = got; while (skip-- != 0) { errno = 0; if (readdir (dirs[level]) == NULL) return errno == 0 ? 0 : -1; } } errno = 0; } return errno == 0 ? 0 : -1; } /* Call a function on every element in a directory tree. */ int ftw (dir, func, descriptors) const char *dir; int (*func) P((const char *file, struct stat *status, int flag)); int descriptors; { DIR **dirs; int c; DIR **p; size_t len; char buf[PATH_MAX + 1]; struct stat s; int flag, ret; if (descriptors <= 0) descriptors = 1; dirs = (DIR **) malloc (descriptors * sizeof (DIR *)); if (dirs == NULL) return -1; c = descriptors; p = dirs; while (c-- != 0) *p++ = NULL; len = strlen (dir); memcpy (buf, dir, len + 1); if (stat (dir, &s) < 0) { if (errno != EACCES) { free ((pointer) dirs); return -1; } flag = FTW_NS; } else if (S_ISDIR (s.st_mode)) { dirs[0] = opendir (dir); if (dirs[0] != NULL) flag = FTW_D; else { if (errno != EACCES) { free ((pointer) dirs); return -1; } flag = FTW_DNR; } } else flag = FTW_F; ret = (*func) (buf, &s, flag); if (flag == FTW_D) { if (ret == 0) { if (len == 1 && *buf == '/') len = 0; ret = ftw_dir (dirs, 0, descriptors, buf, len, func); } if (dirs[0] != NULL) { int save; save = errno; closedir (dirs[0]); errno = save; } } free ((pointer) dirs); return ret; }