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 (9612B)
---
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;
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 tree = ereallocarray(NULL, PATH_MAX, sizeof(*tree));
375
376 ARGBEGIN {
377 case '1':
378 /* force output to 1 entry per line */
379 qflag = 1;
380 break;
381 case 'A':
382 Aflag = 1;
383 break;
384 case 'a':
385 aflag = 1;
386 break;
387 case 'c':
388 cflag = 1;
389 uflag = 0;
390 break;
391 case 'd':
392 dflag = 1;
393 break;
394 case 'f':
395 aflag = 1;
396 fflag = 1;
397 Uflag = 1;
398 break;
399 case 'F':
400 Fflag = 1;
401 break;
402 case 'H':
403 Hflag = 1;
404 break;
405 case 'h':
406 hflag = 1;
407 break;
408 case 'i':
409 iflag = 1;
410 break;
411 case 'L':
412 Lflag = 1;
413 break;
414 case 'l':
415 lflag = 1;
416 break;
417 case 'n':
418 lflag = 1;
419 nflag = 1;
420 break;
421 case 'p':
422 pflag = 1;
423 break;
424 case 'q':
425 qflag = 1;
426 break;
427 case 'R':
428 Rflag = 1;
429 break;
430 case 'r':
431 rflag = 1;
432 break;
433 case 'S':
434 sort = 'S';
435 break;
436 case 't':
437 sort = 't';
438 break;
439 case 'U':
440 Uflag = 1;
441 break;
442 case 'u':
443 uflag = 1;
444 cflag = 0;
445 break;
446 default:
447 usage();
448 } ARGEND
449
450 switch (argc) {
451 case 0: /* fallthrough */
452 *--argv = ".", ++argc;
453 case 1:
454 mkent(&ent, argv[0], 1, Hflag || Lflag);
455 ls("", &ent, (!dflag && S_ISDIR(ent.mode)) ||
456 (S_ISLNK(ent.mode) && S_ISDIR(ent.tmode) &&
457 !(dflag || Fflag || lflag)));
458
459 break;
460 default:
461 for (i = ds = fs = 0, fents = dents = NULL; i < argc; ++…
462 mkent(&ent, argv[i], 1, Hflag || Lflag);
463
464 if ((!dflag && S_ISDIR(ent.mode)) ||
465 (S_ISLNK(ent.mode) && S_ISDIR(ent.tmode) &&
466 !(dflag || Fflag || lflag))) {
467 dents = ereallocarray(dents, ++ds, sizeo…
468 memcpy(&dents[ds - 1], &ent, sizeof(ent)…
469 } else {
470 fents = ereallocarray(fents, ++fs, sizeo…
471 memcpy(&fents[fs - 1], &ent, sizeof(ent)…
472 }
473 }
474
475 showdirs = ds > 1 || (ds && fs);
476
477 qsort(fents, fs, sizeof(ent), entcmp);
478 qsort(dents, ds, sizeof(ent), entcmp);
479
480 for (i = 0; i < fs; ++i)
481 ls("", &fents[i], 0);
482 free(fents);
483 if (fs && ds)
484 putchar('\n');
485 for (i = 0; i < ds; ++i)
486 ls("", &dents[i], 1);
487 free(dents);
488 }
489
490 return (fshut(stdout, "<stdout>") | ret);
491 }
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.