ls.c - sbase - suckless unix tools | |
git clone git://git.suckless.org/sbase | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
ls.c (9566B) | |
--- | |
1 /* See LICENSE file for copyright and license details. */ | |
2 #include <sys/stat.h> | |
3 #include <sys/types.h> | |
4 #ifndef major | |
5 #include <sys/sysmacros.h> | |
6 #endif | |
7 | |
8 #include <dirent.h> | |
9 #include <grp.h> | |
10 #include <pwd.h> | |
11 #include <stdio.h> | |
12 #include <stdlib.h> | |
13 #include <string.h> | |
14 #include <time.h> | |
15 #include <unistd.h> | |
16 | |
17 #include "utf.h" | |
18 #include "util.h" | |
19 | |
20 struct entry { | |
21 char *name; | |
22 mode_t mode, tmode; | |
23 nlink_t nlink; | |
24 uid_t uid; | |
25 gid_t gid; | |
26 off_t size; | |
27 struct timespec t; | |
28 dev_t dev; | |
29 dev_t rdev; | |
30 ino_t ino, tino; | |
31 }; | |
32 | |
33 static struct { | |
34 dev_t dev; | |
35 ino_t ino; | |
36 } tree[PATH_MAX]; | |
37 | |
38 static int ret = 0; | |
39 static int Aflag = 0; | |
40 static int aflag = 0; | |
41 static int cflag = 0; | |
42 static int dflag = 0; | |
43 static int Fflag = 0; | |
44 static int fflag = 0; | |
45 static int Hflag = 0; | |
46 static int hflag = 0; | |
47 static int iflag = 0; | |
48 static int Lflag = 0; | |
49 static int lflag = 0; | |
50 static int nflag = 0; | |
51 static int pflag = 0; | |
52 static int qflag = 0; | |
53 static int Rflag = 0; | |
54 static int rflag = 0; | |
55 static int Uflag = 0; | |
56 static int uflag = 0; | |
57 static int first = 1; | |
58 static char sort = 0; | |
59 static int showdirs; | |
60 | |
61 static void ls(const char *, const struct entry *, int); | |
62 | |
63 static void | |
64 mkent(struct entry *ent, char *path, int dostat, int follow) | |
65 { | |
66 struct stat st; | |
67 | |
68 ent->name = path; | |
69 if (!dostat) | |
70 return; | |
71 if ((follow ? stat : lstat)(path, &st) < 0) | |
72 eprintf("%s %s:", follow ? "stat" : "lstat", path); | |
73 ent->mode = st.st_mode; | |
74 ent->nlink = st.st_nlink; | |
75 ent->uid = st.st_uid; | |
76 ent->gid = st.st_gid; | |
77 ent->size = st.st_size; | |
78 if (cflag) | |
79 ent->t = st.st_ctim; | |
80 else if (uflag) | |
81 ent->t = st.st_atim; | |
82 else | |
83 ent->t = st.st_mtim; | |
84 ent->dev = st.st_dev; | |
85 ent->rdev = st.st_rdev; | |
86 ent->ino = st.st_ino; | |
87 if (S_ISLNK(ent->mode)) { | |
88 if (stat(path, &st) == 0) { | |
89 ent->tmode = st.st_mode; | |
90 ent->dev = st.st_dev; | |
91 ent->tino = st.st_ino; | |
92 } else { | |
93 ent->tmode = ent->tino = 0; | |
94 } | |
95 } | |
96 } | |
97 | |
98 static char * | |
99 indicator(mode_t mode) | |
100 { | |
101 if (pflag || Fflag) | |
102 if (S_ISDIR(mode)) | |
103 return "/"; | |
104 | |
105 if (Fflag) { | |
106 if (S_ISLNK(mode)) | |
107 return "@"; | |
108 else if (S_ISFIFO(mode)) | |
109 return "|"; | |
110 else if (S_ISSOCK(mode)) | |
111 return "="; | |
112 else if (mode & S_IXUSR || mode & S_IXGRP || mode & S_IX… | |
113 return "*"; | |
114 } | |
115 | |
116 return ""; | |
117 } | |
118 | |
119 static void | |
120 printname(const char *name) | |
121 { | |
122 const char *c; | |
123 Rune r; | |
124 size_t l; | |
125 | |
126 for (c = name; *c; c += l) { | |
127 l = chartorune(&r, c); | |
128 if (!qflag || isprintrune(r)) | |
129 fwrite(c, 1, l, stdout); | |
130 else | |
131 putchar('?'); | |
132 } | |
133 } | |
134 | |
135 static void | |
136 output(const struct entry *ent) | |
137 { | |
138 struct group *gr; | |
139 struct passwd *pw; | |
140 struct tm *tm; | |
141 ssize_t len; | |
142 char *fmt, buf[BUFSIZ], pwname[_SC_LOGIN_NAME_MAX], | |
143 grname[_SC_LOGIN_NAME_MAX], mode[] = "----------"; | |
144 | |
145 if (iflag) | |
146 printf("%lu ", (unsigned long)ent->ino); | |
147 if (!lflag) { | |
148 printname(ent->name); | |
149 puts(indicator(ent->mode)); | |
150 return; | |
151 } | |
152 if (S_ISREG(ent->mode)) | |
153 mode[0] = '-'; | |
154 else if (S_ISBLK(ent->mode)) | |
155 mode[0] = 'b'; | |
156 else if (S_ISCHR(ent->mode)) | |
157 mode[0] = 'c'; | |
158 else if (S_ISDIR(ent->mode)) | |
159 mode[0] = 'd'; | |
160 else if (S_ISFIFO(ent->mode)) | |
161 mode[0] = 'p'; | |
162 else if (S_ISLNK(ent->mode)) | |
163 mode[0] = 'l'; | |
164 else if (S_ISSOCK(ent->mode)) | |
165 mode[0] = 's'; | |
166 else | |
167 mode[0] = '?'; | |
168 | |
169 if (ent->mode & S_IRUSR) mode[1] = 'r'; | |
170 if (ent->mode & S_IWUSR) mode[2] = 'w'; | |
171 if (ent->mode & S_IXUSR) mode[3] = 'x'; | |
172 if (ent->mode & S_IRGRP) mode[4] = 'r'; | |
173 if (ent->mode & S_IWGRP) mode[5] = 'w'; | |
174 if (ent->mode & S_IXGRP) mode[6] = 'x'; | |
175 if (ent->mode & S_IROTH) mode[7] = 'r'; | |
176 if (ent->mode & S_IWOTH) mode[8] = 'w'; | |
177 if (ent->mode & S_IXOTH) mode[9] = 'x'; | |
178 | |
179 if (ent->mode & S_ISUID) mode[3] = (mode[3] == 'x') ? 's' : 'S'; | |
180 if (ent->mode & S_ISGID) mode[6] = (mode[6] == 'x') ? 's' : 'S'; | |
181 if (ent->mode & S_ISVTX) mode[9] = (mode[9] == 'x') ? 't' : 'T'; | |
182 | |
183 if (!nflag && (pw = getpwuid(ent->uid))) | |
184 snprintf(pwname, sizeof(pwname), "%s", pw->pw_name); | |
185 else | |
186 snprintf(pwname, sizeof(pwname), "%d", ent->uid); | |
187 | |
188 if (!nflag && (gr = getgrgid(ent->gid))) | |
189 snprintf(grname, sizeof(grname), "%s", gr->gr_name); | |
190 else | |
191 snprintf(grname, sizeof(grname), "%d", ent->gid); | |
192 | |
193 if (time(NULL) > ent->t.tv_sec + (180 * 24 * 60 * 60)) /* 6 mont… | |
194 fmt = "%b %d %Y"; | |
195 else | |
196 fmt = "%b %d %H:%M"; | |
197 | |
198 if ((tm = localtime(&ent->t.tv_sec))) | |
199 strftime(buf, sizeof(buf), fmt, tm); | |
200 else | |
201 snprintf(buf, sizeof(buf), "%lld", (long long)(ent->t.tv… | |
202 printf("%s %4ld %-8.8s %-8.8s ", mode, (long)ent->nlink, pwname,… | |
203 | |
204 if (S_ISBLK(ent->mode) || S_ISCHR(ent->mode)) | |
205 printf("%4u, %4u ", major(ent->rdev), minor(ent->rdev)); | |
206 else if (hflag) | |
207 printf("%10s ", humansize(ent->size)); | |
208 else | |
209 printf("%10lu ", (unsigned long)ent->size); | |
210 printf("%s ", buf); | |
211 printname(ent->name); | |
212 fputs(indicator(ent->mode), stdout); | |
213 if (S_ISLNK(ent->mode)) { | |
214 if ((len = readlink(ent->name, buf, sizeof(buf) - 1)) < … | |
215 eprintf("readlink %s:", ent->name); | |
216 buf[len] = '\0'; | |
217 printf(" -> %s%s", buf, indicator(ent->tmode)); | |
218 } | |
219 putchar('\n'); | |
220 } | |
221 | |
222 static int | |
223 entcmp(const void *va, const void *vb) | |
224 { | |
225 int cmp = 0; | |
226 const struct entry *a = va, *b = vb; | |
227 | |
228 switch (sort) { | |
229 case 'S': | |
230 cmp = b->size - a->size; | |
231 break; | |
232 case 't': | |
233 if (!(cmp = b->t.tv_sec - a->t.tv_sec)) | |
234 cmp = b->t.tv_nsec - a->t.tv_nsec; | |
235 break; | |
236 } | |
237 | |
238 if (!cmp) | |
239 cmp = strcmp(a->name, b->name); | |
240 | |
241 return rflag ? 0 - cmp : cmp; | |
242 } | |
243 | |
244 static void | |
245 lsdir(const char *path, const struct entry *dir) | |
246 { | |
247 DIR *dp; | |
248 struct entry *ent, *ents = NULL; | |
249 struct dirent *d; | |
250 size_t i, n = 0; | |
251 char prefix[PATH_MAX]; | |
252 | |
253 if (!(dp = opendir(dir->name))) { | |
254 ret = 1; | |
255 weprintf("opendir %s%s:", path, dir->name); | |
256 return; | |
257 } | |
258 if (chdir(dir->name) < 0) | |
259 eprintf("chdir %s:", dir->name); | |
260 | |
261 while ((d = readdir(dp))) { | |
262 if (d->d_name[0] == '.' && !aflag && !Aflag) | |
263 continue; | |
264 else if (Aflag) | |
265 if (strcmp(d->d_name, ".") == 0 || | |
266 strcmp(d->d_name, "..") == 0) | |
267 continue; | |
268 | |
269 ents = ereallocarray(ents, ++n, sizeof(*ents)); | |
270 mkent(&ents[n - 1], estrdup(d->d_name), Fflag || iflag || | |
271 lflag || pflag || Rflag || sort, Lflag); | |
272 } | |
273 | |
274 closedir(dp); | |
275 | |
276 if (!Uflag) | |
277 qsort(ents, n, sizeof(*ents), entcmp); | |
278 | |
279 if (path[0] || showdirs) { | |
280 fputs(path, stdout); | |
281 printname(dir->name); | |
282 puts(":"); | |
283 } | |
284 for (i = 0; i < n; i++) | |
285 output(&ents[i]); | |
286 | |
287 if (Rflag) { | |
288 if (snprintf(prefix, PATH_MAX, "%s%s/", path, dir->name)… | |
289 PATH_MAX) | |
290 eprintf("path too long: %s%s\n", path, dir->name… | |
291 | |
292 for (i = 0; i < n; i++) { | |
293 ent = &ents[i]; | |
294 if (strcmp(ent->name, ".") == 0 || | |
295 strcmp(ent->name, "..") == 0) | |
296 continue; | |
297 if (S_ISLNK(ent->mode) && S_ISDIR(ent->tmode) &&… | |
298 continue; | |
299 | |
300 ls(prefix, ent, 1); | |
301 } | |
302 } | |
303 | |
304 for (i = 0; i < n; ++i) | |
305 free(ents[i].name); | |
306 free(ents); | |
307 } | |
308 | |
309 static int | |
310 visit(const struct entry *ent) | |
311 { | |
312 dev_t dev; | |
313 ino_t ino; | |
314 int i; | |
315 | |
316 dev = ent->dev; | |
317 ino = S_ISLNK(ent->mode) ? ent->tino : ent->ino; | |
318 | |
319 for (i = 0; i < PATH_MAX && tree[i].ino; ++i) { | |
320 if (ino == tree[i].ino && dev == tree[i].dev) | |
321 return -1; | |
322 } | |
323 | |
324 tree[i].ino = ino; | |
325 tree[i].dev = dev; | |
326 | |
327 return i; | |
328 } | |
329 | |
330 static void | |
331 ls(const char *path, const struct entry *ent, int listdir) | |
332 { | |
333 int treeind; | |
334 char cwd[PATH_MAX]; | |
335 | |
336 if (!listdir) { | |
337 output(ent); | |
338 } else if (S_ISDIR(ent->mode) || | |
339 (S_ISLNK(ent->mode) && S_ISDIR(ent->tmode))) { | |
340 if ((treeind = visit(ent)) < 0) { | |
341 ret = 1; | |
342 weprintf("%s%s: Already visited\n", path, ent->n… | |
343 return; | |
344 } | |
345 | |
346 if (!getcwd(cwd, PATH_MAX)) | |
347 eprintf("getcwd:"); | |
348 | |
349 if (first) | |
350 first = 0; | |
351 else | |
352 putchar('\n'); | |
353 | |
354 lsdir(path, ent); | |
355 tree[treeind].ino = 0; | |
356 | |
357 if (chdir(cwd) < 0) | |
358 eprintf("chdir %s:", cwd); | |
359 } | |
360 } | |
361 | |
362 static void | |
363 usage(void) | |
364 { | |
365 eprintf("usage: %s [-1AacdFfHhiLlnpqRrtUu] [file ...]\n", argv0); | |
366 } | |
367 | |
368 int | |
369 main(int argc, char *argv[]) | |
370 { | |
371 struct entry ent, *dents, *fents; | |
372 size_t i, ds, fs; | |
373 | |
374 ARGBEGIN { | |
375 case '1': | |
376 /* force output to 1 entry per line */ | |
377 qflag = 1; | |
378 break; | |
379 case 'A': | |
380 Aflag = 1; | |
381 break; | |
382 case 'a': | |
383 aflag = 1; | |
384 break; | |
385 case 'c': | |
386 cflag = 1; | |
387 uflag = 0; | |
388 break; | |
389 case 'd': | |
390 dflag = 1; | |
391 break; | |
392 case 'f': | |
393 aflag = 1; | |
394 fflag = 1; | |
395 Uflag = 1; | |
396 break; | |
397 case 'F': | |
398 Fflag = 1; | |
399 break; | |
400 case 'H': | |
401 Hflag = 1; | |
402 break; | |
403 case 'h': | |
404 hflag = 1; | |
405 break; | |
406 case 'i': | |
407 iflag = 1; | |
408 break; | |
409 case 'L': | |
410 Lflag = 1; | |
411 break; | |
412 case 'l': | |
413 lflag = 1; | |
414 break; | |
415 case 'n': | |
416 lflag = 1; | |
417 nflag = 1; | |
418 break; | |
419 case 'p': | |
420 pflag = 1; | |
421 break; | |
422 case 'q': | |
423 qflag = 1; | |
424 break; | |
425 case 'R': | |
426 Rflag = 1; | |
427 break; | |
428 case 'r': | |
429 rflag = 1; | |
430 break; | |
431 case 'S': | |
432 sort = 'S'; | |
433 break; | |
434 case 't': | |
435 sort = 't'; | |
436 break; | |
437 case 'U': | |
438 Uflag = 1; | |
439 break; | |
440 case 'u': | |
441 uflag = 1; | |
442 cflag = 0; | |
443 break; | |
444 default: | |
445 usage(); | |
446 } ARGEND | |
447 | |
448 switch (argc) { | |
449 case 0: /* fallthrough */ | |
450 *--argv = ".", ++argc; | |
451 case 1: | |
452 mkent(&ent, argv[0], 1, Hflag || Lflag); | |
453 ls("", &ent, (!dflag && S_ISDIR(ent.mode)) || | |
454 (S_ISLNK(ent.mode) && S_ISDIR(ent.tmode) && | |
455 !(dflag || Fflag || lflag))); | |
456 | |
457 break; | |
458 default: | |
459 for (i = ds = fs = 0, fents = dents = NULL; i < argc; ++… | |
460 mkent(&ent, argv[i], 1, Hflag || Lflag); | |
461 | |
462 if ((!dflag && S_ISDIR(ent.mode)) || | |
463 (S_ISLNK(ent.mode) && S_ISDIR(ent.tmode) && | |
464 !(dflag || Fflag || lflag))) { | |
465 dents = ereallocarray(dents, ++ds, sizeo… | |
466 memcpy(&dents[ds - 1], &ent, sizeof(ent)… | |
467 } else { | |
468 fents = ereallocarray(fents, ++fs, sizeo… | |
469 memcpy(&fents[fs - 1], &ent, sizeof(ent)… | |
470 } | |
471 } | |
472 | |
473 showdirs = ds > 1 || (ds && fs); | |
474 | |
475 qsort(fents, fs, sizeof(ent), entcmp); | |
476 qsort(dents, ds, sizeof(ent), entcmp); | |
477 | |
478 for (i = 0; i < fs; ++i) | |
479 ls("", &fents[i], 0); | |
480 free(fents); | |
481 if (fs && ds) | |
482 putchar('\n'); | |
483 for (i = 0; i < ds; ++i) | |
484 ls("", &dents[i], 1); | |
485 free(dents); | |
486 } | |
487 | |
488 return (fshut(stdout, "<stdout>") | ret); | |
489 } |