walk: fix skipped files, simplify seen()

This commit is contained in:
Ori Bernstein 2024-12-12 20:17:51 +00:00
parent e59b7e28cd
commit b8797a1cc6

View file

@ -4,15 +4,21 @@
#include <String.h> #include <String.h>
#include <fcall.h> #include <fcall.h>
typedef struct Seen Seen;
struct Seen {
Seen *up;
Dir *d;
};
int Cflag = 0; int Cflag = 0;
int uflag = 0; int uflag = 0;
String *stfmt; String *stfmt;
/* should turn these flags into a mask */ /* should turn these flags into a mask */
int dflag = 1; int printdirs = 1;
int fflag = 1; int printfiles = 1;
int tflag = 0; int temponly = 0;
int xflag = 0; int execonly = 0;
long maxdepth = ~(1<<31); long maxdepth = ~(1<<31);
long mindepth = 0; long mindepth = 0;
@ -21,9 +27,6 @@ Dir *dotdir = nil;
Biobuf *bout; Biobuf *bout;
void unsee(Dir*);
int seen(Dir*);
void void
warn(char *fmt, ...) warn(char *fmt, ...)
{ {
@ -41,6 +44,17 @@ warn(char *fmt, ...)
fprint(2, "%s\n", buf); fprint(2, "%s\n", buf);
} }
int
seen(Seen *s, Dir *d)
{
for(; s != nil; s = s->up)
if(d->qid.path == s->d->qid.path
&& d->qid.type == s->d->qid.type
&& d->dev == s->d->dev)
return 1;
return 0;
}
void void
dofile(char *path, Dir *f, int pathonly) dofile(char *path, Dir *f, int pathonly)
{ {
@ -48,8 +62,8 @@ dofile(char *path, Dir *f, int pathonly)
if( if(
(f == dotdir) (f == dotdir)
|| (tflag && ! (f->qid.type & QTTMP)) || (temponly && ! (f->qid.type & QTTMP))
|| (xflag && ! (f->mode & DMEXEC)) || (execonly && ! (f->mode & DMEXEC))
) )
return; return;
@ -92,19 +106,21 @@ dofile(char *path, Dir *f, int pathonly)
} }
void void
walk(char *path, Dir *cf, long depth) walk(char *path, Dir *cf, Seen *up, long depth)
{ {
String *file; String *file;
Dir *dirs, *f, *fe; Dir *dirs, *f, *fe;
Seen s;
long n, fseen;
int fd; int fd;
long n;
if(cf == nil){ if(cf == nil){
warn("path: %s: %r", path); warn("path: %s: %r", path);
return; return;
} }
if(depth >= maxdepth) fseen = seen(up, cf);
if(depth >= maxdepth || fseen)
goto nodescend; goto nodescend;
if((fd = open(path, OREAD)) < 0){ if((fd = open(path, OREAD)) < 0){
@ -112,11 +128,13 @@ walk(char *path, Dir *cf, long depth)
return; return;
} }
s.d = cf;
s.up = up;
while((n = dirread(fd, &dirs)) > 0){ while((n = dirread(fd, &dirs)) > 0){
fe = dirs+n; fe = dirs+n;
for(f = dirs; f < fe; f++){ for(f = dirs; f < fe; f++){
if(!(f->qid.type & QTDIR)){ if(!(f->qid.type & QTDIR)){
if(fflag && depth >= mindepth) if(printfiles && depth >= mindepth)
dofile(path, f, 0); dofile(path, f, 0);
}else if(strcmp(f->name, ".") == 0 || strcmp(f->name, "..") == 0){ }else if(strcmp(f->name, ".") == 0 || strcmp(f->name, "..") == 0){
warn(". or .. named file: %s/%s", path, f->name); warn(". or .. named file: %s/%s", path, f->name);
@ -134,11 +152,7 @@ walk(char *path, Dir *cf, long depth)
s_putc(file, '/'); s_putc(file, '/');
} }
s_append(file, f->name); s_append(file, f->name);
if(seen(f)) walk(s_to_c(file), f, &s, depth+1);
dofile(s_to_c(file), f, 0);
else
walk(s_to_c(file), f, depth+1);
unsee(f);
s_free(file); s_free(file);
} }
} }
@ -150,7 +164,7 @@ walk(char *path, Dir *cf, long depth)
nodescend: nodescend:
depth--; depth--;
if(dflag && depth >= mindepth) if(printdirs && (depth >= mindepth || fseen))
dofile(path, cf, 0); dofile(path, cf, 0);
} }
@ -234,10 +248,10 @@ main(int argc, char **argv)
case 'C': Cflag++; break; /* undocumented; do not cleanname() the args */ case 'C': Cflag++; break; /* undocumented; do not cleanname() the args */
case 'u': uflag++; break; /* unbuffered output */ case 'u': uflag++; break; /* unbuffered output */
case 'd': dflag++; fflag = 0; break; /* only dirs */ case 'd': printfiles = 0; break; /* only dirs */
case 'f': fflag++; dflag = 0; break; /* only non-dirs */ case 'f': printdirs = 0; break; /* only non-dirs */
case 't': tflag++; break; /* only tmp files */ case 't': temponly = 1; break; /* only tmp files */
case 'x': xflag++; break; /* only executable permission */ case 'x': execonly = 1; break; /* only executable permission */
case 'n': elimdepth(EARGF(usage())); break; case 'n': elimdepth(EARGF(usage())); break;
case 'e': case 'e':
@ -267,7 +281,7 @@ main(int argc, char **argv)
maxdepth++; maxdepth++;
if(argc == 0){ if(argc == 0){
dotdir = dirstat("."); dotdir = dirstat(".");
walk(dotpath, dotdir, 1); walk(dotpath, dotdir, nil, 1);
}else for(i=0; i<argc; i++){ }else for(i=0; i<argc; i++){
if(strncmp(argv[i], "#/", 2) == 0) if(strncmp(argv[i], "#/", 2) == 0)
slashslash(argv[i]+2); slashslash(argv[i]+2);
@ -279,10 +293,9 @@ main(int argc, char **argv)
if((d = dirstat(argv[i])) == nil) if((d = dirstat(argv[i])) == nil)
continue; continue;
if(d->qid.type & QTDIR){ if(d->qid.type & QTDIR){
seen(d); walk(argv[i], d, nil, 1);
walk(argv[i], d, 1);
}else{ }else{
if(!dflag && !seen(d) && mindepth < 1) if(printfiles && mindepth < 1)
dofile(argv[i], d, 1); dofile(argv[i], d, 1);
} }
free(d); free(d);
@ -291,60 +304,3 @@ main(int argc, char **argv)
exits(nil); exits(nil);
} }
/* below pilfered from /sys/src/cmd/du.c
* NOTE: I did not check for bugs */
#define NCACHE 256 /* must be power of two */
typedef struct
{
Dir* cache;
int n;
int max;
} Cache;
Cache cache[NCACHE];
void
unsee(Dir *dir)
{
Dir *dp;
int i;
Cache *c;
c = &cache[dir->qid.path&(NCACHE-1)];
dp = c->cache;
for(i=0; i<c->n; i++, dp++){
if(dir->qid.path == dp->qid.path &&
dir->type == dp->type &&
dir->dev == dp->dev)
c->cache[i] = c->cache[--c->n];
}
}
int
seen(Dir *dir)
{
Dir *dp;
int i;
Cache *c;
c = &cache[dir->qid.path&(NCACHE-1)];
dp = c->cache;
for(i=0; i<c->n; i++, dp++)
if(dir->qid.path == dp->qid.path &&
dir->type == dp->type &&
dir->dev == dp->dev)
return 1;
if(c->n == c->max){
if (c->max == 0)
c->max = 8;
else
c->max += c->max/2;
c->cache = realloc(c->cache, c->max*sizeof(Dir));
if(c->cache == nil)
sysfatal("realloc: %r");
}
c->cache[c->n++] = *dir;
return 0;
}