/* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ /* ftw() for DJGPP. * * Recursively descent the directory hierarchy rooted in DIR, * calling FUNC for each object in the hierarchy. * * Copyright (c) 1995 Eli Zaretskii * * This software may be used freely as long as this copyright notice is * left intact. There is no warranty on this software. * */ #include #include #include #include #include #include #include #include #include #include static int walk_dir(char *path, int (*func)(const char *, struct stat *, int)) { DIR *dp; struct dirent *de; struct stat stbuf; int flag; int e = errno; int pathlen = strlen(path); if ((dp = opendir(path)) == 0) return -1; for (errno = 0; (de = readdir(dp)) != 0; errno = 0) { int func_result; char lastc = de->d_name[de->d_namlen - 1]; /* Skip `.' and `..' entries. Checking the last char is enough, because readdir will never return a filename which ends with a dot. */ if (lastc == '.') continue; /* Append found name to directory path. */ if (pathlen + de->d_namlen + 1 > FILENAME_MAX) { (void)closedir(dp); errno = ENAMETOOLONG; return -1; } if (path[pathlen-1] == '/' || path[pathlen-1] == '\\') pathlen--; path[pathlen] = '/'; strcpy(path + pathlen + 1, de->d_name); if (stat(path, &stbuf) < 0) flag = FTW_NS; else if (S_ISDIR(stbuf.st_mode)) flag = FTW_D; else if (S_ISLABEL(stbuf.st_mode)) flag = FTW_VL; else flag = FTW_F; /* Invoke FUNC() on this object. */ errno = e; if ((func_result = (*func)(path, &stbuf, flag)) != 0) { (void)closedir(dp); return func_result; } /* If this is a directory, walk its siblings. */ if (flag == FTW_D) { int subwalk_result; errno = e; if ((subwalk_result = walk_dir(path, func)) != 0) { (void)closedir(dp); return subwalk_result; } } /* Erase D_NAME[] from PATH. */ path[pathlen] = '\0'; } (void)closedir(dp); if (errno == 0) /* normal case: this subtree exhausted */ { errno = e;/* restore errno from previous syscall */ return 0; } else return -1; /* with whatever errno was set by readdir() */ } int ftw(const char *dir, int (*func)(const char *, struct stat *, int), int ignored) { int flag; char pathbuf[FILENAME_MAX]; char real_path[FILENAME_MAX]; int dirattr; int len; int e = errno; // ignored = ignored; /* pacify -Wall */ if (dir == 0 || func == 0) { errno = EFAULT; return -1; } if (*dir == '\0') { errno = ENOENT; return -1; } strcpy(pathbuf, dir); len = strlen(pathbuf); if (pathbuf[len-1] == ':') { pathbuf[len++] = '.'; pathbuf[len] = '\0'; } /* Fail for non-directories. */ errno = 0; /* __chmod() does not recognize symlinks, */ /* so we give real name instead. */ if (!__solve_symlinks(pathbuf, real_path)) { /* The errno is set by __solve_symlinks() */ return -1; } /* Note that symlink-clean name is not used further: * functions that are called from here understand symlinks anyway. */ dirattr = _chmod(real_path, 0, 0); if (errno == ENOENT) return -1; else if ((dirattr & 0x10) != 0x10) { errno = ENOTDIR; return -1; } else { int func_result; struct stat stbuf; if (stat(pathbuf, &stbuf) < 0) flag = FTW_NS; else flag = FTW_D; errno = e; if ((func_result = (*func)(pathbuf, &stbuf, flag)) != 0) return func_result; return walk_dir(pathbuf, func); } } #ifdef TEST #include int ftw_walker(const char *path, struct stat *sb, int flag) { char *base; printf("%s:\t%u\t", path, sb->st_size); if (S_ISLABEL(sb->st_mode)) printf("V"); if (S_ISDIR(sb->st_mode)) printf("D"); if (S_ISCHR(sb->st_mode)) printf("C"); if (sb->st_mode & S_IRUSR) printf("r"); if (sb->st_mode & S_IWUSR) printf("w"); if (sb->st_mode & S_IXUSR) printf("x"); if (flag == FTW_NS) printf(" !!no_stat!!"); printf("\n"); base = strrchr(path, '/'); if (base == 0) base = strrchr(path, '\\'); if (base == 0) base = strrchr(path, ':'); if (strcmp(base == 0 ? path : base + 1, "xxxxx") == 0) return 8; return 0; } int main(int argc, char *argv[]) { if (argc > 1) { char msg[80]; sprintf(msg, "file_tree_walk: %d", ftw(argv[1], ftw_walker, 0)); if (errno) perror(msg); else puts(msg); } else printf("Usage: %s dir\n", argv[0]); return 0; } #endif