]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/walk.c
snoopy(8): avoid extra spaces in dhcp filter output
[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                 default:
75                         abort();
76                 }
77
78                 if(*(p+1) != '\0')
79                         Bputc(bout, ' ');
80         }
81
82         Bputc(bout, '\n');
83
84         if(uflag)
85                 Bflush(bout);
86 }
87
88 void
89 walk(char *path, Dir *cf, long depth)
90 {
91         String *file;
92         Dir *dirs, *f, *fe;
93         int fd;
94         long n;
95
96         if(cf == nil){
97                 warn("path: %s: %r", path);
98                 return;
99         }
100
101         if(depth >= maxdepth)
102                 goto nodescend;
103
104         if((fd = open(path, OREAD)) < 0){
105                 warn("couldn't open %s: %r", path);
106                 return;
107         }
108
109         while((n = dirread(fd, &dirs)) > 0){
110                 fe = dirs+n;
111                 for(f = dirs; f < fe; f++){
112                         if(seen(f))
113                                 continue;
114                         if(! (f->qid.type & QTDIR)){
115                                 if(fflag && depth >= mindepth)
116                                         dofile(path, f, 0);
117                         } else if(strcmp(f->name, ".") == 0 || strcmp(f->name, "..") == 0){
118                                 warn(". or .. named file: %s/%s", path, f->name);
119                         } else{
120                                 if(depth+1 > maxdepth){
121                                         dofile(path, f, 0);
122                                         continue;
123                                 } else if(path == dotpath){
124                                         if((file = s_new()) == nil)
125                                                 sysfatal("s_new: %r");
126                                 } else{
127                                         if((file = s_copy(path)) == nil)
128                                                 sysfatal("s_copy: %r");
129                                         if(s_len(file) != 1 || *s_to_c(file) != '/')
130                                                 s_putc(file, '/');
131                                 }
132                                 s_append(file, f->name);
133
134                                 walk(s_to_c(file), f, depth+1); 
135                                 s_free(file);
136                         }
137                 }
138                 free(dirs);
139         }
140         close(fd);
141         if(n < 0)
142                 warn("%s: %r", path);
143
144 nodescend:
145         depth--;
146         if(dflag && depth >= mindepth)
147                 dofile(path, cf, 0);
148 }
149
150 char*
151 slashslash(char *s)
152 {
153         char *p, *q;
154
155         for(p=q=s; *q; q++){
156                 if(q>s && *q=='/' && *(q-1)=='/')
157                         continue;
158                 if(p != q)
159                         *p = *q;
160                 p++;
161         }
162         do{
163                 *p-- = '\0';
164         } while(p>s && *p=='/');
165
166         return s;
167 }
168
169 long
170 estrtol(char *as, char **aas, int base)
171 {
172         long n;
173         char *p;
174
175         n = strtol(as, &p, base);
176         if(p == as || *p != '\0')
177                 sysfatal("estrtol: bad input '%s'", as);
178         else if(aas != nil)
179                 *aas = p;
180
181         return n;
182 }
183
184 void
185 elimdepth(char *p){
186         char *q;
187
188         if(strlen(p) == 0)
189                 sysfatal("empty depth argument");
190
191         if(q = strchr(p, ',')){
192                 *q = '\0';
193                 if(p != q)
194                         mindepth = estrtol(p, nil, 0);
195                 p = q+1;
196                 if(*p == '\0')
197                         return;
198         }
199
200         maxdepth = estrtol(p, nil, 0);
201 }
202
203 void
204 usage(void)
205 {
206         fprint(2, "usage: %s [-udftx] [-n mind,maxd] [-e statfmt] [file ...]\n", argv0);
207         exits("usage");
208 }
209
210 /*
211         Last I checked (commit 3dd6a31881535615389c24ab9a139af2798c462c),
212         libString calls sysfatal when things go wrong; in my local
213         copy of libString, failed calls return nil and errstr is set.
214
215         There are various nil checks in this code when calling libString
216         functions, but since they are a no-op and libString needs
217         a rework, I left them in - BurnZeZ
218 */
219
220 void
221 main(int argc, char **argv)
222 {
223         long i;
224         Dir *d;
225
226         stfmt = nil;
227         ARGBEGIN{
228         case 'C': Cflag++; break; /* undocumented; do not cleanname() the args */
229         case 'u': uflag++; break; /* unbuffered output */
230
231         case 'd': dflag++; fflag = 0; break; /* only dirs */
232         case 'f': fflag++; dflag = 0; break; /* only non-dirs */
233         case 't': tflag++; break; /* only tmp files */
234         case 'x': xflag++; break; /* only executable permission */
235
236         case 'n': elimdepth(EARGF(usage())); break;
237         case 'e':
238                 if((stfmt = s_reset(stfmt)) == nil)
239                         sysfatal("s_reset: %r");
240                 s_append(stfmt, EARGF(usage()));
241                 i = strspn(s_to_c(stfmt), "UGMamnpqsx");
242                 if(i != s_len(stfmt))
243                         sysfatal("bad stfmt: %s\n", s_to_c(stfmt));
244                 break;
245         default:
246                 usage();
247         }ARGEND;
248
249         if((bout = Bfdopen(1, OWRITE)) == nil)
250                 sysfatal("Bfdopen: %r");
251         Blethal(bout, nil);
252         if(stfmt == nil){
253                 if((stfmt = s_new()) == nil)
254                         sysfatal("s_new: %r");
255                 s_putc(stfmt, 'p');
256                 s_terminate(stfmt);
257         }
258         if(maxdepth != ~(1<<31))
259                 maxdepth++;
260         if(argc == 0){
261                 dotdir = dirstat(".");
262                 walk(dotpath, dotdir, 1);
263         } else for(i=0; i<argc; i++){
264                 if(strncmp(argv[i], "#/", 2) == 0)
265                         slashslash(argv[i]+2);
266                 else{
267                         if(!Cflag)
268                                 cleanname(argv[i]);
269                         slashslash(argv[i]);
270                 }
271                 if((d = dirstat(argv[i])) != nil && ! (d->qid.type & QTDIR)){
272                         if(fflag && !seen(d) && mindepth < 1)
273                                 dofile(argv[i], d, 1);
274                 } else
275                         walk(argv[i], d, 1);
276                 free(d);
277         }
278         Bterm(bout);
279
280         exits(nil);
281 }
282
283 /* below pilfered from /sys/src/cmd/du.c
284  * NOTE: I did not check for bugs */
285
286 #define NCACHE  256     /* must be power of two */
287
288 typedef struct
289 {
290         Dir*    cache;
291         int     n;
292         int     max;
293 } Cache;
294 Cache cache[NCACHE];
295
296 int
297 seen(Dir *dir)
298 {
299         Dir *dp;
300         int i;
301         Cache *c;
302
303         c = &cache[dir->qid.path&(NCACHE-1)];
304         dp = c->cache;
305         for(i=0; i<c->n; i++, dp++)
306                 if(dir->qid.path == dp->qid.path &&
307                    dir->type == dp->type &&
308                    dir->dev == dp->dev)
309                         return 1;
310         if(c->n == c->max){
311                 if (c->max == 0)
312                         c->max = 8;
313                 else
314                         c->max += c->max/2;
315                 c->cache = realloc(c->cache, c->max*sizeof(Dir));
316                 if(c->cache == nil)
317                         sysfatal("realloc: %r");
318         }
319         c->cache[c->n++] = *dir;
320         return 0;
321 }