1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
|
/* This file is part of the auxiliaries library.
Written by Dick Grune, Vrije Universiteit, Amsterdam.
$Id: ForEachFile.c,v 1.17 2013-05-12 09:58:34 Gebruiker Exp $
*/
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include "ForEachFile.h"
/*Library module source prelude */
#undef _FOREACHFILE_CODE_
#ifndef lint
#define _FOREACHFILE_CODE_
#endif
#ifdef LIB
#define _FOREACHFILE_CODE_
#endif
#ifdef _FOREACHFILE_CODE_
/* Library module source code */
struct ino_link { /* to detect loop in file system */
struct ino_link *next;
long il_ino;
long il_device;
};
static void do_FEF(
Fchar *fn,
void (*proc)(const Fchar *, const char *, const struct stat *),
int dev,
struct ino_link *inop,
Fchar separator,
int max_depth
);
static Fchar
get_separator(const Fchar *fn) {
#ifndef MSDOS
(void)(fn); /* use fn */
return '/';
#else
/* under MSDOS, conform to user's use, or use '\' */
Fchar sep = 0;
while (*fn) {
if (*fn == '/' || *fn == '\\') {
if (sep == 0) {
sep = *fn;
}
else
if (sep != *fn) return 0; /* bad mixed use */
}
fn++;
}
return (sep ? sep : '\\');
#endif
}
static void
clean_name(Fchar *fn, Fchar sep) {
Fchar *f1 = fn;
Fchar *f2 = fn;
/* remove multiple separators */
while (*f1) {
if (*f1 == sep && *(f1+1) == sep) {
f1++;
} else {
*f2++ = *f1++;
}
}
*f2 = '\0';
/* remove a trailing separator */
if (f2-1 > fn && *(f2-1) == sep) {
*(f2-1) = '\0';
}
}
static void
do_ForEachFile(
const Fchar *fn,
void (*proc)(const Fchar *, const char *, const struct stat *),
int max_depth
) {
Fchar fname[MAX_FILE_NAME_LENGTH];
Fchar separator;
Fnamecpy(fname, (!fn || !*fn) ? str2Fname(".") : fn);
separator = get_separator(fname);
if (!separator) {
(*proc)(fname, "both / and \\ used as separators", 0);
return;
}
clean_name(fname, separator);
do_FEF(fname, proc, -1, (struct ino_link *)0, separator, max_depth);
}
static int in_ino_list(const struct ino_link *inop, const struct stat *st);
static void link_ino_list(
struct ino_link *inop,
struct ino_link *ninop,
const struct stat *st
);
void
ForEachFile(
const Fchar *fn,
void (*proc)(const Fchar *, const char *, const struct stat *)
) {
do_ForEachFile(fn, proc, -1); /* infinitely deep */
}
void
ForEachLocalFile(
const Fchar *fn,
void (*proc)(const Fchar *, const char *, const struct stat *)
) {
do_ForEachFile(fn, proc, 1); /* one level deep */
}
#ifdef S_IFLNK /* system with symbolic links */
#define LSTAT lstat
#else /* S_IFLNK */
#define LSTAT Stat
#endif /* S_IFLNK */
static void
do_FEF(
Fchar *fn,
void (*proc)(const Fchar *, const char *, const struct stat *),
int dev,
struct ino_link *inop,
Fchar separator,
int max_depth
) {
struct stat fs;
Dir_t *dir;
if (proc == 0) return; /* just make sure */
if (LSTAT(fn, &fs) < 0) {
(*proc)(fn, strerror(errno), &fs);
return;
}
/* report on file fn */
(*proc)(fn, (char*)0, &fs);
if (max_depth == 0) return;
if ((fs.st_mode & S_IFMT) != S_IFDIR) return;
#ifdef S_IFLNK
/* don't follow links */
if ((fs.st_mode & S_IFMT) == S_IFLNK) return;
#endif
/* treat directory */
if (dev < 0) {
/* no device known yet */
dev = fs.st_dev;
}
if (fs.st_dev != dev) {
return;
}
dir = Opendir(fn);
if (dir == 0) {
(*proc)(fn, "directory not readable", &fs);
}
else {
/* scan new directory */
int fnl = Fnamelen(fn);
Dirent_t *dent;
struct ino_link ino;
/* worry about loops in the file system */
if (in_ino_list(inop, &fs)) {
(*proc)(fn, "loop in file system", &fs);
Closedir(dir);
return;
}
link_ino_list(inop, &ino, &fs);
/* shape up the directory name */
if (fn[fnl-1] != separator) {
/* append separator */
fn[fnl++] = separator;
fn[fnl] = '\0';
}
/* descend */
while ((dent = Readdir(dir)) != (Dirent_t *)0) {
if ( Fnamecmp(dent->d_name, str2Fname(".")) == 0
|| Fnamecmp(dent->d_name, str2Fname("..")) == 0
) continue;
if (Fnamecmp(dent->d_name, str2Fname("")) == 0) {
(*proc)(fn,
"directory contains empty file name",
&fs
);
continue;
}
/* append name */
Fnamecat(fn, dent->d_name);
do_FEF(fn, proc, dev, &ino, separator, max_depth-1);
/* remove name again*/
fn[fnl] = '\0';
}
Closedir(dir);
}
}
static int
in_ino_list(const struct ino_link *inop, const struct stat *st) {
while (inop) {
#ifdef UNIX
if ( inop->il_ino == st->st_ino
&& inop->il_device == st->st_dev
) return 1;
#else
#ifdef lint
st = st;
#endif
#endif
inop = inop->next;
}
return 0;
}
static void
link_ino_list(
struct ino_link *inop, struct ino_link *ninop, const struct stat *st
) {
ninop->next = inop;
ninop->il_ino = st->st_ino;
ninop->il_device = st->st_dev;
}
/* End library module source code */
#endif /* _FOREACHFILE_CODE_ */
#ifdef lint
static void
satisfy_lint(void *x) {
ForEachFile(0, 0);
ForEachLocalFile(0, 0);
satisfy_lint(x);
}
#endif /* lint */
|