10 /* should turn these flags into a mask */
15 long maxdepth = ~(1<<31);
29 char buf[1024]; /* arbitrary */
32 if((n = snprint(buf, sizeof(buf), "%s: ", argv0)) < 0)
33 sysfatal("snprint: %r");
35 vseprint(buf+n, buf+sizeof(buf), fmt, arg);
39 fprint(2, "%s\n", buf);
43 dofile(char *path, Dir *f, int pathonly)
49 || (tflag && ! (f->qid.type & QTTMP))
50 || (xflag && ! (f->mode & DMEXEC))
54 for(p = s_to_c(stfmt); *p != '\0'; p++){
56 case 'U': Bwrite(bout, f->uid, strlen(f->uid)); break;
57 case 'G': Bwrite(bout, f->gid, strlen(f->gid)); break;
58 case 'M': Bwrite(bout, f->muid, strlen(f->muid)); break;
59 case 'a': Bprint(bout, "%uld", f->atime); break;
60 case 'm': Bprint(bout, "%uld", f->mtime); break;
61 case 'n': Bwrite(bout, f->name, strlen(f->name)); break;
64 Bwrite(bout, path, strlen(path));
65 if(! (f->qid.type & QTDIR) && !pathonly){
68 Bwrite(bout, f->name, strlen(f->name));
71 case 'q': Bprint(bout, "%ullx.%uld.%.2uhhx", f->qid.path, f->qid.vers, f->qid.type); break;
72 case 's': Bprint(bout, "%lld", f->length); break;
73 case 'x': Bprint(bout, "%ulo", f->mode); break;
75 /* These two are slightly different, as they tell us about the fileserver instead of the file */
76 case 'D': Bprint(bout, "%ud", f->dev); break;
77 case 'T': Bprint(bout, "%C", f->type); break;
93 walk(char *path, Dir *cf, long depth)
101 warn("path: %s: %r", path);
105 if(depth >= maxdepth)
108 if((fd = open(path, OREAD)) < 0){
109 warn("couldn't open %s: %r", path);
113 while((n = dirread(fd, &dirs)) > 0){
115 for(f = dirs; f < fe; f++){
118 if(! (f->qid.type & QTDIR)){
119 if(fflag && depth >= mindepth)
121 } else if(strcmp(f->name, ".") == 0 || strcmp(f->name, "..") == 0){
122 warn(". or .. named file: %s/%s", path, f->name);
124 if(depth+1 > maxdepth){
127 } else if(path == dotpath){
128 if((file = s_new()) == nil)
129 sysfatal("s_new: %r");
131 if((file = s_copy(path)) == nil)
132 sysfatal("s_copy: %r");
133 if(s_len(file) != 1 || *s_to_c(file) != '/')
136 s_append(file, f->name);
138 walk(s_to_c(file), f, depth+1);
146 warn("%s: %r", path);
150 if(dflag && depth >= mindepth)
160 if(q>s && *q=='/' && *(q-1)=='/')
168 } while(p>s && *p=='/');
174 estrtol(char *as, char **aas, int base)
179 n = strtol(as, &p, base);
180 if(p == as || *p != '\0')
181 sysfatal("estrtol: bad input '%s'", as);
193 sysfatal("empty depth argument");
195 if(q = strchr(p, ',')){
198 mindepth = estrtol(p, nil, 0);
204 maxdepth = estrtol(p, nil, 0);
210 fprint(2, "usage: %s [-udftx] [-n mind,maxd] [-e statfmt] [file ...]\n", argv0);
215 Last I checked (commit 3dd6a31881535615389c24ab9a139af2798c462c),
216 libString calls sysfatal when things go wrong; in my local
217 copy of libString, failed calls return nil and errstr is set.
219 There are various nil checks in this code when calling libString
220 functions, but since they are a no-op and libString needs
221 a rework, I left them in - BurnZeZ
225 main(int argc, char **argv)
232 case 'C': Cflag++; break; /* undocumented; do not cleanname() the args */
233 case 'u': uflag++; break; /* unbuffered output */
235 case 'd': dflag++; fflag = 0; break; /* only dirs */
236 case 'f': fflag++; dflag = 0; break; /* only non-dirs */
237 case 't': tflag++; break; /* only tmp files */
238 case 'x': xflag++; break; /* only executable permission */
240 case 'n': elimdepth(EARGF(usage())); break;
242 if((stfmt = s_reset(stfmt)) == nil)
243 sysfatal("s_reset: %r");
244 s_append(stfmt, EARGF(usage()));
245 i = strspn(s_to_c(stfmt), "UGMamnpqsxDT");
246 if(i != s_len(stfmt))
247 sysfatal("bad stfmt: %s", s_to_c(stfmt));
253 if((bout = Bfdopen(1, OWRITE)) == nil)
254 sysfatal("Bfdopen: %r");
257 if((stfmt = s_new()) == nil)
258 sysfatal("s_new: %r");
262 if(maxdepth != ~(1<<31))
265 dotdir = dirstat(".");
266 walk(dotpath, dotdir, 1);
267 } else for(i=0; i<argc; i++){
268 if(strncmp(argv[i], "#/", 2) == 0)
269 slashslash(argv[i]+2);
275 if((d = dirstat(argv[i])) != nil && ! (d->qid.type & QTDIR)){
276 if(fflag && !seen(d) && mindepth < 1)
277 dofile(argv[i], d, 1);
287 /* below pilfered from /sys/src/cmd/du.c
288 * NOTE: I did not check for bugs */
290 #define NCACHE 256 /* must be power of two */
307 c = &cache[dir->qid.path&(NCACHE-1)];
309 for(i=0; i<c->n; i++, dp++)
310 if(dir->qid.path == dp->qid.path &&
311 dir->type == dp->type &&
319 c->cache = realloc(c->cache, c->max*sizeof(Dir));
321 sysfatal("realloc: %r");
323 c->cache[c->n++] = *dir;