Introduction
Introduction Statistics Contact Development Disclaimer Help
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 }
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.