xref: /plan9-contrib/sys/src/cmd/walk.c (revision 6126a792bb0dbd81f004453253c57091c32d95b6)
1*6126a792SDavid du Colombier #include <u.h>
2*6126a792SDavid du Colombier #include <libc.h>
3*6126a792SDavid du Colombier #include <bio.h>
4*6126a792SDavid du Colombier #include <String.h>
5*6126a792SDavid du Colombier 
6*6126a792SDavid du Colombier int Cflag = 0;
7*6126a792SDavid du Colombier int uflag = 0;
8*6126a792SDavid du Colombier String *stfmt;
9*6126a792SDavid du Colombier 
10*6126a792SDavid du Colombier /* should turn these flags into a mask */
11*6126a792SDavid du Colombier int dflag = 1;
12*6126a792SDavid du Colombier int fflag = 1;
13*6126a792SDavid du Colombier int tflag = 0;
14*6126a792SDavid du Colombier int xflag = 0;
15*6126a792SDavid du Colombier long maxdepth = ~(1<<31);
16*6126a792SDavid du Colombier long mindepth = 0;
17*6126a792SDavid du Colombier 
18*6126a792SDavid du Colombier char *dotpath = ".";
19*6126a792SDavid du Colombier Dir *dotdir = nil;
20*6126a792SDavid du Colombier 
21*6126a792SDavid du Colombier Biobuf *bout;
22*6126a792SDavid du Colombier 
23*6126a792SDavid du Colombier int seen(Dir*);
24*6126a792SDavid du Colombier 
25*6126a792SDavid du Colombier void
warn(char * fmt,...)26*6126a792SDavid du Colombier warn(char *fmt, ...)
27*6126a792SDavid du Colombier {
28*6126a792SDavid du Colombier 	va_list arg;
29*6126a792SDavid du Colombier 	char buf[1024];	/* arbitrary */
30*6126a792SDavid du Colombier 	int n;
31*6126a792SDavid du Colombier 
32*6126a792SDavid du Colombier 	if((n = snprint(buf, sizeof(buf), "%s: ", argv0)) < 0)
33*6126a792SDavid du Colombier 		sysfatal("snprint: %r");
34*6126a792SDavid du Colombier 	va_start(arg, fmt);
35*6126a792SDavid du Colombier 	vseprint(buf+n, buf+sizeof(buf), fmt, arg);
36*6126a792SDavid du Colombier 	va_end(arg);
37*6126a792SDavid du Colombier 
38*6126a792SDavid du Colombier 	Bflush(bout);
39*6126a792SDavid du Colombier 	fprint(2, "%s\n", buf);
40*6126a792SDavid du Colombier }
41*6126a792SDavid du Colombier 
42*6126a792SDavid du Colombier void
dofile(char * path,Dir * f,int pathonly)43*6126a792SDavid du Colombier dofile(char *path, Dir *f, int pathonly)
44*6126a792SDavid du Colombier {
45*6126a792SDavid du Colombier 	char *p;
46*6126a792SDavid du Colombier 
47*6126a792SDavid du Colombier 	if(
48*6126a792SDavid du Colombier 		(f == dotdir)
49*6126a792SDavid du Colombier 		|| (tflag && ! (f->qid.type & QTTMP))
50*6126a792SDavid du Colombier 		|| (xflag && ! (f->mode & DMEXEC))
51*6126a792SDavid du Colombier 	)
52*6126a792SDavid du Colombier 		return;
53*6126a792SDavid du Colombier 
54*6126a792SDavid du Colombier 	for(p = s_to_c(stfmt); *p != '\0'; p++){
55*6126a792SDavid du Colombier 		switch(*p){
56*6126a792SDavid du Colombier 		case 'U': Bwrite(bout, f->uid, strlen(f->uid)); break;
57*6126a792SDavid du Colombier 		case 'G': Bwrite(bout, f->gid, strlen(f->gid)); break;
58*6126a792SDavid du Colombier 		case 'M': Bwrite(bout, f->muid, strlen(f->muid)); break;
59*6126a792SDavid du Colombier 		case 'a': Bprint(bout, "%uld", f->atime); break;
60*6126a792SDavid du Colombier 		case 'm': Bprint(bout, "%uld", f->mtime); break;
61*6126a792SDavid du Colombier 		case 'n': Bwrite(bout, f->name, strlen(f->name)); break;
62*6126a792SDavid du Colombier 		case 'p':
63*6126a792SDavid du Colombier 			if(path != dotpath)
64*6126a792SDavid du Colombier 				Bwrite(bout, path, strlen(path));
65*6126a792SDavid du Colombier 			if(! (f->qid.type & QTDIR) && !pathonly){
66*6126a792SDavid du Colombier 				if(path != dotpath)
67*6126a792SDavid du Colombier 					Bputc(bout, '/');
68*6126a792SDavid du Colombier 				Bwrite(bout, f->name, strlen(f->name));
69*6126a792SDavid du Colombier 			}
70*6126a792SDavid du Colombier 			break;
71*6126a792SDavid du Colombier 		case 'q': Bprint(bout, "%ullx.%uld.%.2uhhx", f->qid.path, f->qid.vers, f->qid.type); break;
72*6126a792SDavid du Colombier 		case 's': Bprint(bout, "%lld", f->length); break;
73*6126a792SDavid du Colombier 		case 'x': Bprint(bout, "%ulo", f->mode); break;
74*6126a792SDavid du Colombier 		default:
75*6126a792SDavid du Colombier 			abort();
76*6126a792SDavid du Colombier 		}
77*6126a792SDavid du Colombier 
78*6126a792SDavid du Colombier 		if(*(p+1) != '\0')
79*6126a792SDavid du Colombier 			Bputc(bout, ' ');
80*6126a792SDavid du Colombier 	}
81*6126a792SDavid du Colombier 
82*6126a792SDavid du Colombier 	Bputc(bout, '\n');
83*6126a792SDavid du Colombier 
84*6126a792SDavid du Colombier 	if(uflag)
85*6126a792SDavid du Colombier 		Bflush(bout);
86*6126a792SDavid du Colombier }
87*6126a792SDavid du Colombier 
88*6126a792SDavid du Colombier void
walk(char * path,Dir * cf,long depth)89*6126a792SDavid du Colombier walk(char *path, Dir *cf, long depth)
90*6126a792SDavid du Colombier {
91*6126a792SDavid du Colombier 	String *file;
92*6126a792SDavid du Colombier 	Dir *dirs, *f, *fe;
93*6126a792SDavid du Colombier 	int fd;
94*6126a792SDavid du Colombier 	long n;
95*6126a792SDavid du Colombier 
96*6126a792SDavid du Colombier 	if(cf == nil){
97*6126a792SDavid du Colombier 		warn("path: %s: %r", path);
98*6126a792SDavid du Colombier 		return;
99*6126a792SDavid du Colombier 	}
100*6126a792SDavid du Colombier 
101*6126a792SDavid du Colombier 	if(depth >= maxdepth)
102*6126a792SDavid du Colombier 		goto nodescend;
103*6126a792SDavid du Colombier 
104*6126a792SDavid du Colombier 	if((fd = open(path, OREAD)) < 0){
105*6126a792SDavid du Colombier 		warn("couldn't open %s: %r", path);
106*6126a792SDavid du Colombier 		return;
107*6126a792SDavid du Colombier 	}
108*6126a792SDavid du Colombier 
109*6126a792SDavid du Colombier 	while((n = dirread(fd, &dirs)) > 0){
110*6126a792SDavid du Colombier 		fe = dirs+n;
111*6126a792SDavid du Colombier 		for(f = dirs; f < fe; f++){
112*6126a792SDavid du Colombier 			if(seen(f))
113*6126a792SDavid du Colombier 				continue;
114*6126a792SDavid du Colombier 			if(! (f->qid.type & QTDIR)){
115*6126a792SDavid du Colombier 				if(fflag && depth >= mindepth)
116*6126a792SDavid du Colombier 					dofile(path, f, 0);
117*6126a792SDavid du Colombier 			} else if(strcmp(f->name, ".") == 0 || strcmp(f->name, "..") == 0){
118*6126a792SDavid du Colombier 				warn(". or .. named file: %s/%s", path, f->name);
119*6126a792SDavid du Colombier 			} else{
120*6126a792SDavid du Colombier 				if(depth+1 > maxdepth){
121*6126a792SDavid du Colombier 					dofile(path, f, 0);
122*6126a792SDavid du Colombier 					continue;
123*6126a792SDavid du Colombier 				} else if(path == dotpath){
124*6126a792SDavid du Colombier 					if((file = s_new()) == nil)
125*6126a792SDavid du Colombier 						sysfatal("s_new: %r");
126*6126a792SDavid du Colombier 				} else{
127*6126a792SDavid du Colombier 					if((file = s_copy(path)) == nil)
128*6126a792SDavid du Colombier 						sysfatal("s_copy: %r");
129*6126a792SDavid du Colombier 					if(s_len(file) != 1 || *s_to_c(file) != '/')
130*6126a792SDavid du Colombier 						s_putc(file, '/');
131*6126a792SDavid du Colombier 				}
132*6126a792SDavid du Colombier 				s_append(file, f->name);
133*6126a792SDavid du Colombier 
134*6126a792SDavid du Colombier 				walk(s_to_c(file), f, depth+1);
135*6126a792SDavid du Colombier 				s_free(file);
136*6126a792SDavid du Colombier 			}
137*6126a792SDavid du Colombier 		}
138*6126a792SDavid du Colombier 		free(dirs);
139*6126a792SDavid du Colombier 	}
140*6126a792SDavid du Colombier 	close(fd);
141*6126a792SDavid du Colombier 	if(n < 0)
142*6126a792SDavid du Colombier 		warn("%s: %r", path);
143*6126a792SDavid du Colombier 
144*6126a792SDavid du Colombier nodescend:
145*6126a792SDavid du Colombier 	depth--;
146*6126a792SDavid du Colombier 	if(dflag && depth >= mindepth)
147*6126a792SDavid du Colombier 		dofile(path, cf, 0);
148*6126a792SDavid du Colombier }
149*6126a792SDavid du Colombier 
150*6126a792SDavid du Colombier char*
slashslash(char * s)151*6126a792SDavid du Colombier slashslash(char *s)
152*6126a792SDavid du Colombier {
153*6126a792SDavid du Colombier 	char *p, *q;
154*6126a792SDavid du Colombier 
155*6126a792SDavid du Colombier 	for(p=q=s; *q; q++){
156*6126a792SDavid du Colombier 		if(q>s && *q=='/' && *(q-1)=='/')
157*6126a792SDavid du Colombier 			continue;
158*6126a792SDavid du Colombier 		if(p != q)
159*6126a792SDavid du Colombier 			*p = *q;
160*6126a792SDavid du Colombier 		p++;
161*6126a792SDavid du Colombier 	}
162*6126a792SDavid du Colombier 	do{
163*6126a792SDavid du Colombier 		*p-- = '\0';
164*6126a792SDavid du Colombier 	} while(p>s && *p=='/');
165*6126a792SDavid du Colombier 
166*6126a792SDavid du Colombier 	return s;
167*6126a792SDavid du Colombier }
168*6126a792SDavid du Colombier 
169*6126a792SDavid du Colombier long
estrtol(char * as,char ** aas,int base)170*6126a792SDavid du Colombier estrtol(char *as, char **aas, int base)
171*6126a792SDavid du Colombier {
172*6126a792SDavid du Colombier 	long n;
173*6126a792SDavid du Colombier 	char *p;
174*6126a792SDavid du Colombier 
175*6126a792SDavid du Colombier 	n = strtol(as, &p, base);
176*6126a792SDavid du Colombier 	if(p == as || *p != '\0')
177*6126a792SDavid du Colombier 		sysfatal("estrtol: bad input '%s'", as);
178*6126a792SDavid du Colombier 	else if(aas != nil)
179*6126a792SDavid du Colombier 		*aas = p;
180*6126a792SDavid du Colombier 
181*6126a792SDavid du Colombier 	return n;
182*6126a792SDavid du Colombier }
183*6126a792SDavid du Colombier 
184*6126a792SDavid du Colombier void
elimdepth(char * p)185*6126a792SDavid du Colombier elimdepth(char *p){
186*6126a792SDavid du Colombier 	char *q;
187*6126a792SDavid du Colombier 
188*6126a792SDavid du Colombier 	if(strlen(p) == 0)
189*6126a792SDavid du Colombier 		sysfatal("empty depth argument");
190*6126a792SDavid du Colombier 
191*6126a792SDavid du Colombier 	if(q = strchr(p, ',')){
192*6126a792SDavid du Colombier 		*q = '\0';
193*6126a792SDavid du Colombier 		if(p != q)
194*6126a792SDavid du Colombier 			mindepth = estrtol(p, nil, 0);
195*6126a792SDavid du Colombier 		p = q+1;
196*6126a792SDavid du Colombier 		if(*p == '\0')
197*6126a792SDavid du Colombier 			return;
198*6126a792SDavid du Colombier 	}
199*6126a792SDavid du Colombier 
200*6126a792SDavid du Colombier 	maxdepth = estrtol(p, nil, 0);
201*6126a792SDavid du Colombier }
202*6126a792SDavid du Colombier 
203*6126a792SDavid du Colombier void
usage(void)204*6126a792SDavid du Colombier usage(void)
205*6126a792SDavid du Colombier {
206*6126a792SDavid du Colombier 	fprint(2, "usage: %s [-udftx] [-n mind,maxd] [-e statfmt] [file ...]\n", argv0);
207*6126a792SDavid du Colombier 	exits("usage");
208*6126a792SDavid du Colombier }
209*6126a792SDavid du Colombier 
210*6126a792SDavid du Colombier /*
211*6126a792SDavid du Colombier 	Last I checked (commit 3dd6a31881535615389c24ab9a139af2798c462c),
212*6126a792SDavid du Colombier 	libString calls sysfatal when things go wrong; in my local
213*6126a792SDavid du Colombier 	copy of libString, failed calls return nil and errstr is set.
214*6126a792SDavid du Colombier 
215*6126a792SDavid du Colombier 	There are various nil checks in this code when calling libString
216*6126a792SDavid du Colombier 	functions, but since they are a no-op and libString needs
217*6126a792SDavid du Colombier 	a rework, I left them in - BurnZeZ
218*6126a792SDavid du Colombier */
219*6126a792SDavid du Colombier 
220*6126a792SDavid du Colombier Biobuf *
Bfdopen(int fd,int flag)221*6126a792SDavid du Colombier Bfdopen(int fd, int flag)
222*6126a792SDavid du Colombier {
223*6126a792SDavid du Colombier 	Biobuf *b;
224*6126a792SDavid du Colombier 
225*6126a792SDavid du Colombier 	b = malloc(sizeof(Biobuf));
226*6126a792SDavid du Colombier 	if(b && Binit(b, fd, flag) == 0)
227*6126a792SDavid du Colombier 		return b;
228*6126a792SDavid du Colombier 	free(b);
229*6126a792SDavid du Colombier 	return nil;
230*6126a792SDavid du Colombier }
231*6126a792SDavid du Colombier 
232*6126a792SDavid du Colombier 
233*6126a792SDavid du Colombier void
main(int argc,char ** argv)234*6126a792SDavid du Colombier main(int argc, char **argv)
235*6126a792SDavid du Colombier {
236*6126a792SDavid du Colombier 	long i;
237*6126a792SDavid du Colombier 	Dir *d;
238*6126a792SDavid du Colombier 
239*6126a792SDavid du Colombier 	stfmt = nil;
240*6126a792SDavid du Colombier 	ARGBEGIN{
241*6126a792SDavid du Colombier 	case 'C': Cflag++; break; /* undocumented; do not cleanname() the args */
242*6126a792SDavid du Colombier 	case 'u': uflag++; break; /* unbuffered output */
243*6126a792SDavid du Colombier 
244*6126a792SDavid du Colombier 	case 'd': dflag++; fflag = 0; break; /* only dirs */
245*6126a792SDavid du Colombier 	case 'f': fflag++; dflag = 0; break; /* only non-dirs */
246*6126a792SDavid du Colombier 	case 't': tflag++; break; /* only tmp files */
247*6126a792SDavid du Colombier 	case 'x': xflag++; break; /* only executable permission */
248*6126a792SDavid du Colombier 
249*6126a792SDavid du Colombier 	case 'n': elimdepth(EARGF(usage())); break;
250*6126a792SDavid du Colombier 	case 'e':
251*6126a792SDavid du Colombier 		if((stfmt = s_reset(stfmt)) == nil)
252*6126a792SDavid du Colombier 			sysfatal("s_reset: %r");
253*6126a792SDavid du Colombier 		s_append(stfmt, EARGF(usage()));
254*6126a792SDavid du Colombier 		i = strspn(s_to_c(stfmt), "UGMamnpqsx");
255*6126a792SDavid du Colombier 		if(i != s_len(stfmt))
256*6126a792SDavid du Colombier 			sysfatal("bad stfmt: %s\n", s_to_c(stfmt));
257*6126a792SDavid du Colombier 		break;
258*6126a792SDavid du Colombier 	default:
259*6126a792SDavid du Colombier 		usage();
260*6126a792SDavid du Colombier 	}ARGEND;
261*6126a792SDavid du Colombier 
262*6126a792SDavid du Colombier 	if((bout = Bfdopen(1, OWRITE)) == nil)
263*6126a792SDavid du Colombier 		sysfatal("Bfdopen: %r");
264*6126a792SDavid du Colombier 	if(stfmt == nil){
265*6126a792SDavid du Colombier 		if((stfmt = s_new()) == nil)
266*6126a792SDavid du Colombier 			sysfatal("s_new: %r");
267*6126a792SDavid du Colombier 		s_putc(stfmt, 'p');
268*6126a792SDavid du Colombier 		s_terminate(stfmt);
269*6126a792SDavid du Colombier 	}
270*6126a792SDavid du Colombier 	if(maxdepth != ~(1<<31))
271*6126a792SDavid du Colombier 		maxdepth++;
272*6126a792SDavid du Colombier 	if(argc == 0){
273*6126a792SDavid du Colombier 		dotdir = dirstat(".");
274*6126a792SDavid du Colombier 		walk(dotpath, dotdir, 1);
275*6126a792SDavid du Colombier 	} else for(i=0; i<argc; i++){
276*6126a792SDavid du Colombier 		if(strncmp(argv[i], "#/", 2) == 0)
277*6126a792SDavid du Colombier 			slashslash(argv[i]+2);
278*6126a792SDavid du Colombier 		else{
279*6126a792SDavid du Colombier 			if(!Cflag)
280*6126a792SDavid du Colombier 				cleanname(argv[i]);
281*6126a792SDavid du Colombier 			slashslash(argv[i]);
282*6126a792SDavid du Colombier 		}
283*6126a792SDavid du Colombier 		if((d = dirstat(argv[i])) != nil && ! (d->qid.type & QTDIR)){
284*6126a792SDavid du Colombier 			if(fflag && !seen(d) && mindepth < 1)
285*6126a792SDavid du Colombier 				dofile(argv[i], d, 1);
286*6126a792SDavid du Colombier 		} else
287*6126a792SDavid du Colombier 			walk(argv[i], d, 1);
288*6126a792SDavid du Colombier 		free(d);
289*6126a792SDavid du Colombier 	}
290*6126a792SDavid du Colombier 	Bterm(bout);
291*6126a792SDavid du Colombier 
292*6126a792SDavid du Colombier 	exits(nil);
293*6126a792SDavid du Colombier }
294*6126a792SDavid du Colombier 
295*6126a792SDavid du Colombier /* below pilfered from /sys/src/cmd/du.c
296*6126a792SDavid du Colombier  * NOTE: I did not check for bugs */
297*6126a792SDavid du Colombier 
298*6126a792SDavid du Colombier #define	NCACHE	256	/* must be power of two */
299*6126a792SDavid du Colombier 
300*6126a792SDavid du Colombier typedef struct
301*6126a792SDavid du Colombier {
302*6126a792SDavid du Colombier 	Dir*	cache;
303*6126a792SDavid du Colombier 	int	n;
304*6126a792SDavid du Colombier 	int	max;
305*6126a792SDavid du Colombier } Cache;
306*6126a792SDavid du Colombier Cache cache[NCACHE];
307*6126a792SDavid du Colombier 
308*6126a792SDavid du Colombier int
seen(Dir * dir)309*6126a792SDavid du Colombier seen(Dir *dir)
310*6126a792SDavid du Colombier {
311*6126a792SDavid du Colombier 	Dir *dp;
312*6126a792SDavid du Colombier 	int i;
313*6126a792SDavid du Colombier 	Cache *c;
314*6126a792SDavid du Colombier 
315*6126a792SDavid du Colombier 	c = &cache[dir->qid.path&(NCACHE-1)];
316*6126a792SDavid du Colombier 	dp = c->cache;
317*6126a792SDavid du Colombier 	for(i=0; i<c->n; i++, dp++)
318*6126a792SDavid du Colombier 		if(dir->qid.path == dp->qid.path &&
319*6126a792SDavid du Colombier 		   dir->type == dp->type &&
320*6126a792SDavid du Colombier 		   dir->dev == dp->dev)
321*6126a792SDavid du Colombier 			return 1;
322*6126a792SDavid du Colombier 	if(c->n == c->max){
323*6126a792SDavid du Colombier 		if (c->max == 0)
324*6126a792SDavid du Colombier 			c->max = 8;
325*6126a792SDavid du Colombier 		else
326*6126a792SDavid du Colombier 			c->max += c->max/2;
327*6126a792SDavid du Colombier 		c->cache = realloc(c->cache, c->max*sizeof(Dir));
328*6126a792SDavid du Colombier 		if(c->cache == nil)
329*6126a792SDavid du Colombier 			sysfatal("realloc: %r");
330*6126a792SDavid du Colombier 	}
331*6126a792SDavid du Colombier 	c->cache[c->n++] = *dir;
332*6126a792SDavid du Colombier 	return 0;
333*6126a792SDavid du Colombier }
334