]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/walk.c
rio: fix goodrect() bug (thanks mike)
[plan9front.git] / sys / src / cmd / walk.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <String.h>
5
6 int Cflag = 0;
7 int uflag = 0;
8 String *stfmt;
9
10 /* should turn these flags into a mask */
11 int dflag = 1;
12 int fflag = 1;
13 int tflag = 0;
14 int xflag = 0;
15 long maxdepth = ~(1<<31);
16 long mindepth = 0;
17
18 char *dotpath = ".";
19 Dir *dotdir = nil;
20
21 Biobuf *bout;
22
23 int seen(Dir*);
24
25 void
26 warn(char *fmt, ...)
27 {
28         va_list arg;
29         char buf[1024]; /* arbitrary */
30         int n;
31
32         if((n = snprint(buf, sizeof(buf), "%s: ", argv0)) < 0)
33                 sysfatal("snprint: %r");
34         va_start(arg, fmt);
35         vseprint(buf+n, buf+sizeof(buf), fmt, arg);
36         va_end(arg);
37
38         Bflush(bout);
39         fprint(2, "%s\n", buf);
40 }
41
42 void
43 dofile(char *path, Dir *f, int pathonly)
44 {
45         char *p;
46
47         if(
48                 (f == dotdir)
49                 || (tflag && ! (f->qid.type & QTTMP))
50                 || (xflag && ! (f->mode & DMEXEC))
51         )
52                 return;
53
54         for(p = s_to_c(stfmt); *p != '\0'; p++){
55                 switch(*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;
62                 case 'p':
63                         if(path != dotpath)
64                                 Bwrite(bout, path, strlen(path));
65                         if(! (f->qid.type & QTDIR) && !pathonly){
66                                 if(path != dotpath)
67                                         Bputc(bout, '/');
68                                 Bwrite(bout, f->name, strlen(f->name));
69                         }
70                         break;
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;
74
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;
78                 default:
79                         abort();
80                 }
81
82                 if(*(p+1) != '\0')
83                         Bputc(bout, ' ');
84         }
85
86         Bputc(bout, '\n');
87
88         if(uflag)
89                 Bflush(bout);
90 }
91
92 void
93 walk(char *path, Dir *cf, long depth)
94 {
95         String *file;
96         Dir *dirs, *f, *fe;
97         int fd;
98         long n;
99
100         if(cf == nil){
101                 warn("path: %s: %r", path);
102                 return;
103         }
104
105         if(depth >= maxdepth)
106                 goto nodescend;
107
108         if((fd = open(path, OREAD)) < 0){
109                 warn("couldn't open %s: %r", path);
110                 return;
111         }
112
113         while((n = dirread(fd, &dirs)) > 0){
114                 fe = dirs+n;
115                 for(f = dirs; f < fe; f++){
116                         if(seen(f))
117                                 continue;
118                         if(! (f->qid.type & QTDIR)){
119                                 if(fflag && depth >= mindepth)
120                                         dofile(path, f, 0);
121                         } else if(strcmp(f->name, ".") == 0 || strcmp(f->name, "..") == 0){
122                                 warn(". or .. named file: %s/%s", path, f->name);
123                         } else{
124                                 if(depth+1 > maxdepth){
125                                         dofile(path, f, 0);
126                                         continue;
127                                 } else if(path == dotpath){
128                                         if((file = s_new()) == nil)
129                                                 sysfatal("s_new: %r");
130                                 } else{
131                                         if((file = s_copy(path)) == nil)
132                                                 sysfatal("s_copy: %r");
133                                         if(s_len(file) != 1 || *s_to_c(file) != '/')
134                                                 s_putc(file, '/');
135                                 }
136                                 s_append(file, f->name);
137
138                                 walk(s_to_c(file), f, depth+1); 
139                                 s_free(file);
140                         }
141                 }
142                 free(dirs);
143         }
144         close(fd);
145         if(n < 0)
146                 warn("%s: %r", path);
147
148 nodescend:
149         depth--;
150         if(dflag && depth >= mindepth)
151                 dofile(path, cf, 0);
152 }
153
154 char*
155 slashslash(char *s)
156 {
157         char *p, *q;
158
159         for(p=q=s; *q; q++){
160                 if(q>s && *q=='/' && *(q-1)=='/')
161                         continue;
162                 if(p != q)
163                         *p = *q;
164                 p++;
165         }
166         do{
167                 *p-- = '\0';
168         } while(p>s && *p=='/');
169
170         return s;
171 }
172
173 long
174 estrtol(char *as, char **aas, int base)
175 {
176         long n;
177         char *p;
178
179         n = strtol(as, &p, base);
180         if(p == as || *p != '\0')
181                 sysfatal("estrtol: bad input '%s'", as);
182         else if(aas != nil)
183                 *aas = p;
184
185         return n;
186 }
187
188 void
189 elimdepth(char *p){
190         char *q;
191
192         if(strlen(p) == 0)
193                 sysfatal("empty depth argument");
194
195         if(q = strchr(p, ',')){
196                 *q = '\0';
197                 if(p != q)
198                         mindepth = estrtol(p, nil, 0);
199                 p = q+1;
200                 if(*p == '\0')
201                         return;
202         }
203
204         maxdepth = estrtol(p, nil, 0);
205 }
206
207 void
208 usage(void)
209 {
210         fprint(2, "usage: %s [-udftx] [-n mind,maxd] [-e statfmt] [file ...]\n", argv0);
211         exits("usage");
212 }
213
214 /*
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.
218
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
222 */
223
224 void
225 main(int argc, char **argv)
226 {
227         long i;
228         Dir *d;
229
230         stfmt = nil;
231         ARGBEGIN{
232         case 'C': Cflag++; break; /* undocumented; do not cleanname() the args */
233         case 'u': uflag++; break; /* unbuffered output */
234
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 */
239
240         case 'n': elimdepth(EARGF(usage())); break;
241         case 'e':
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));
248                 break;
249         default:
250                 usage();
251         }ARGEND;
252
253         if((bout = Bfdopen(1, OWRITE)) == nil)
254                 sysfatal("Bfdopen: %r");
255         Blethal(bout, nil);
256         if(stfmt == nil){
257                 if((stfmt = s_new()) == nil)
258                         sysfatal("s_new: %r");
259                 s_putc(stfmt, 'p');
260                 s_terminate(stfmt);
261         }
262         if(maxdepth != ~(1<<31))
263                 maxdepth++;
264         if(argc == 0){
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);
270                 else{
271                         if(!Cflag)
272                                 cleanname(argv[i]);
273                         slashslash(argv[i]);
274                 }
275                 if((d = dirstat(argv[i])) != nil && ! (d->qid.type & QTDIR)){
276                         if(fflag && !seen(d) && mindepth < 1)
277                                 dofile(argv[i], d, 1);
278                 } else
279                         walk(argv[i], d, 1);
280                 free(d);
281         }
282         Bterm(bout);
283
284         exits(nil);
285 }
286
287 /* below pilfered from /sys/src/cmd/du.c
288  * NOTE: I did not check for bugs */
289
290 #define NCACHE  256     /* must be power of two */
291
292 typedef struct
293 {
294         Dir*    cache;
295         int     n;
296         int     max;
297 } Cache;
298 Cache cache[NCACHE];
299
300 int
301 seen(Dir *dir)
302 {
303         Dir *dp;
304         int i;
305         Cache *c;
306
307         c = &cache[dir->qid.path&(NCACHE-1)];
308         dp = c->cache;
309         for(i=0; i<c->n; i++, dp++)
310                 if(dir->qid.path == dp->qid.path &&
311                    dir->type == dp->type &&
312                    dir->dev == dp->dev)
313                         return 1;
314         if(c->n == c->max){
315                 if (c->max == 0)
316                         c->max = 8;
317                 else
318                         c->max += c->max/2;
319                 c->cache = realloc(c->cache, c->max*sizeof(Dir));
320                 if(c->cache == nil)
321                         sysfatal("realloc: %r");
322         }
323         c->cache[c->n++] = *dir;
324         return 0;
325 }