1
2
3#include "config.h"
4#include "util.h"
5
6#include <grp.h>
7#include <inttypes.h>
8#include <pwd.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <sys/stat.h>
12#include <sys/sysmacros.h>
13#include <sys/types.h>
14#include <sys/vfs.h>
15#include <time.h>
16#include <unistd.h>
17
18static void
19mode_to_str(mode_t mode, char *buf)
20{
21 buf[0] = S_ISDIR(mode) ? 'd'
22 : S_ISLNK(mode) ? 'l'
23 : S_ISCHR(mode) ? 'c'
24 : S_ISBLK(mode) ? 'b'
25 : S_ISFIFO(mode) ? 'p'
26 : S_ISSOCK(mode) ? 's'
27 : '-';
28 buf[1] = (mode & S_IRUSR) ? 'r' : '-';
29 buf[2] = (mode & S_IWUSR) ? 'w' : '-';
30 buf[3] = (mode & S_IXUSR) ? ((mode & S_ISUID) ? 's' : 'x') : ((mode & S_ISUID) ? 'S' : '-');
31 buf[4] = (mode & S_IRGRP) ? 'r' : '-';
32 buf[5] = (mode & S_IWGRP) ? 'w' : '-';
33 buf[6] = (mode & S_IXGRP) ? ((mode & S_ISGID) ? 's' : 'x') : ((mode & S_ISGID) ? 'S' : '-');
34 buf[7] = (mode & S_IROTH) ? 'r' : '-';
35 buf[8] = (mode & S_IWOTH) ? 'w' : '-';
36 buf[9] = (mode & S_IXOTH) ? ((mode & S_ISVTX) ? 't' : 'x') : ((mode & S_ISVTX) ? 'T' : '-');
37 buf[10] = '\0';
38}
39
40#if FEATURE_STAT_FORMAT
41static void
42print_custom(const char *file, struct stat *st, const char *fmt)
43{
44 const char *p;
45 char fmt_spec[64];
46 int fmt_len;
47 char buf[32];
48 struct passwd *pw;
49 struct group *gr;
50
51 for (p = fmt; *p;) {
52 if (*p == '\\') {
53 p++;
54 switch (*p) {
55 case 'n':
56 putchar('\n');
57 break;
58 // ?man -t: sort or specify timestamp
59 case 't':
60 putchar('\t');
61 break;
62 case 'r':
63 putchar('\r');
64 break;
65 case 'b':
66 putchar('\b');
67 break;
68 // ?man -f: force the operation
69 case 'f':
70 putchar('\f');
71 break;
72 case '\\':
73 putchar('\\');
74 break;
75 case '\0':
76 return;
77 default:
78 putchar(*p);
79 break;
80 }
81 p++;
82 continue;
83 }
84 if (*p != '%') {
85 putchar(*p);
86 p++;
87 continue;
88 }
89 p++;
90 if (*p == '%') {
91 putchar('%');
92 p++;
93 continue;
94 }
95 fmt_spec[0] = '%';
96 fmt_len = 1;
97 while (*p == '-' || *p == '+' || *p == '0' || *p == ' ' || *p == '#') {
98 if (fmt_len < 60)
99 fmt_spec[fmt_len++] = *p;
100 p++;
101 }
102 while (*p >= '0' && *p <= '9') {
103 if (fmt_len < 60)
104 fmt_spec[fmt_len++] = *p;
105 p++;
106 }
107 switch (*p) {
108 case 'a':
109 fmt_spec[fmt_len++] = 'o';
110 fmt_spec[fmt_len] = '\0';
111 printf(fmt_spec, (unsigned int)(st->st_mode & 0777));
112 break;
113 case 'A':
114 fmt_spec[fmt_len++] = 's';
115 fmt_spec[fmt_len] = '\0';
116 mode_to_str(st->st_mode, buf);
117 printf(fmt_spec, buf);
118 break;
119 case 'b':
120 fmt_spec[fmt_len++] = 'l';
121 fmt_spec[fmt_len++] = 'l';
122 fmt_spec[fmt_len++] = 'u';
123 fmt_spec[fmt_len] = '\0';
124 printf(fmt_spec, (unsigned long long)st->st_blocks);
125 break;
126 case 'B':
127 fmt_spec[fmt_len++] = 'u';
128 fmt_spec[fmt_len] = '\0';
129 printf(fmt_spec, 512U);
130 break;
131 case 'd':
132 fmt_spec[fmt_len++] = 'l';
133 fmt_spec[fmt_len++] = 'l';
134 fmt_spec[fmt_len++] = 'u';
135 fmt_spec[fmt_len] = '\0';
136 printf(fmt_spec, (unsigned long long)st->st_dev);
137 break;
138 case 'D':
139 fmt_spec[fmt_len++] = 'l';
140 fmt_spec[fmt_len++] = 'l';
141 fmt_spec[fmt_len++] = 'x';
142 fmt_spec[fmt_len] = '\0';
143 printf(fmt_spec, (unsigned long long)st->st_dev);
144 break;
145 case 'g':
146 fmt_spec[fmt_len++] = 'u';
147 fmt_spec[fmt_len] = '\0';
148 printf(fmt_spec, st->st_gid);
149 break;
150 case 'G':
151 fmt_spec[fmt_len++] = 's';
152 fmt_spec[fmt_len] = '\0';
153 gr = getgrgid(st->st_gid);
154 printf(fmt_spec, gr ? gr->gr_name : "");
155 break;
156 case 'h':
157 fmt_spec[fmt_len++] = 'l';
158 fmt_spec[fmt_len++] = 'u';
159 fmt_spec[fmt_len] = '\0';
160 printf(fmt_spec, (unsigned long)st->st_nlink);
161 break;
162 case 'i':
163 fmt_spec[fmt_len++] = 'l';
164 fmt_spec[fmt_len++] = 'u';
165 fmt_spec[fmt_len] = '\0';
166 printf(fmt_spec, (unsigned long)st->st_ino);
167 break;
168 case 'n':
169 fmt_spec[fmt_len++] = 's';
170 fmt_spec[fmt_len] = '\0';
171 printf(fmt_spec, file);
172 break;
173 case 'o':
174 fmt_spec[fmt_len++] = 'l';
175 fmt_spec[fmt_len++] = 'u';
176 fmt_spec[fmt_len] = '\0';
177 printf(fmt_spec, (unsigned long)st->st_blksize);
178 break;
179 case 's':
180 fmt_spec[fmt_len++] = 'l';
181 fmt_spec[fmt_len++] = 'l';
182 fmt_spec[fmt_len++] = 'd';
183 fmt_spec[fmt_len] = '\0';
184 printf(fmt_spec, (long long)st->st_size);
185 break;
186 // ?man -t: sort or specify timestamp
187 case 't':
188 fmt_spec[fmt_len++] = 'x';
189 fmt_spec[fmt_len] = '\0';
190 printf(fmt_spec, major(st->st_rdev));
191 break;
192 case 'T':
193 fmt_spec[fmt_len++] = 'x';
194 fmt_spec[fmt_len] = '\0';
195 printf(fmt_spec, minor(st->st_rdev));
196 break;
197 case 'u':
198 fmt_spec[fmt_len++] = 'u';
199 fmt_spec[fmt_len] = '\0';
200 printf(fmt_spec, st->st_uid);
201 break;
202 case 'U':
203 fmt_spec[fmt_len++] = 's';
204 fmt_spec[fmt_len] = '\0';
205 pw = getpwuid(st->st_uid);
206 printf(fmt_spec, pw ? pw->pw_name : "");
207 break;
208 case 'X':
209 fmt_spec[fmt_len++] = 'l';
210 fmt_spec[fmt_len++] = 'd';
211 fmt_spec[fmt_len] = '\0';
212 printf(fmt_spec, (long)st->st_atime);
213 break;
214 case 'Y':
215 fmt_spec[fmt_len++] = 'l';
216 fmt_spec[fmt_len++] = 'd';
217 fmt_spec[fmt_len] = '\0';
218 printf(fmt_spec, (long)st->st_mtime);
219 break;
220 case 'Z':
221 fmt_spec[fmt_len++] = 'l';
222 fmt_spec[fmt_len++] = 'd';
223 fmt_spec[fmt_len] = '\0';
224 printf(fmt_spec, (long)st->st_ctime);
225 break;
226 default:
227 putchar('%');
228 if (*p) {
229 putchar(*p);
230 } else {
231 return;
232 }
233 break;
234 }
235 p++;
236 }
237 putchar('\n');
238}
239#endif
240
241#if FEATURE_STAT_FILESYSTEM
242static const char *
243fs_type_name(long type)
244{
245 switch (type) {
246 case 0x9fa0:
247 return "proc";
248 case 0x62656567:
249 return "beegfs";
250 case 0xef53:
251 return "ext2/ext3";
252 case 0x1cd1:
253 return "devpctld";
254 case 0xf15f:
255 return "ecryptfs";
256 case 0x65737566:
257 return "fuse";
258 case 0x4d44:
259 return "msdos";
260 case 0x564c:
261 return "novell";
262 case 0x6969:
263 return "nfs";
264 case 0x534f4654:
265 return "overlay";
266 case 0x73717368:
267 return "squashfs";
268 case 0x01021994:
269 return "tmpfs";
270 case 0x858458f6:
271 return "ramfs";
272 default:
273 return "unknown";
274 }
275}
276
277static void
278show_statfs(const char *file, struct statfs *st)
279{
280 printf(" File: \"%s\"\n", file);
281 printf(
282 " ID: %llx Namelen: %ld Type: %s\n",
283 (unsigned long long)st->f_fsid.__val[0] | ((unsigned long long)st->f_fsid.__val[1] << 32),
284 (long)st->f_namelen,
285 fs_type_name(st->f_type)
286 );
287 printf(
288 "Block size: %-10ld Fundamental block size: %ld\n",
289 (long)st->f_bsize,
290 (long)st->f_frsize ? (long)st->f_frsize : (long)st->f_bsize
291 );
292 printf(
293 "Blocks: Total: %-10lld Free: %-10lld Available: %lld\n",
294 (long long)st->f_blocks,
295 (long long)st->f_bfree,
296 (long long)st->f_bavail
297 );
298 printf("Inodes: Total: %-10lld Free: %lld\n", (long long)st->f_files, (long long)st->f_ffree);
299}
300
301#if FEATURE_STAT_FORMAT
302static void
303print_fs_custom(const char *file, struct statfs *st, const char *fmt)
304{
305 const char *p;
306 char fmt_spec[64];
307 int fmt_len;
308
309 for (p = fmt; *p;) {
310 if (*p == '\\') {
311 p++;
312 switch (*p) {
313 case 'n':
314 putchar('\n');
315 break;
316 // ?man -t: sort or specify timestamp
317 case 't':
318 putchar('\t');
319 break;
320 case 'r':
321 putchar('\r');
322 break;
323 case 'b':
324 putchar('\b');
325 break;
326 // ?man -f: force the operation
327 case 'f':
328 putchar('\f');
329 break;
330 case '\\':
331 putchar('\\');
332 break;
333 case '\0':
334 return;
335 default:
336 putchar(*p);
337 break;
338 }
339 p++;
340 continue;
341 }
342 if (*p != '%') {
343 putchar(*p);
344 p++;
345 continue;
346 }
347 p++;
348 if (*p == '%') {
349 putchar('%');
350 p++;
351 continue;
352 }
353 fmt_spec[0] = '%';
354 fmt_len = 1;
355 while (*p == '-' || *p == '+' || *p == '0' || *p == ' ' || *p == '#') {
356 if (fmt_len < 60)
357 fmt_spec[fmt_len++] = *p;
358 p++;
359 }
360 while (*p >= '0' && *p <= '9') {
361 if (fmt_len < 60)
362 fmt_spec[fmt_len++] = *p;
363 p++;
364 }
365 switch (*p) {
366 case 'a':
367 fmt_spec[fmt_len++] = 'l';
368 fmt_spec[fmt_len++] = 'l';
369 fmt_spec[fmt_len++] = 'd';
370 fmt_spec[fmt_len] = '\0';
371 printf(fmt_spec, (long long)st->f_bavail);
372 break;
373 case 'b':
374 fmt_spec[fmt_len++] = 'l';
375 fmt_spec[fmt_len++] = 'l';
376 fmt_spec[fmt_len++] = 'd';
377 fmt_spec[fmt_len] = '\0';
378 printf(fmt_spec, (long long)st->f_blocks);
379 break;
380 // ?man -c: print count or perform stdout action
381 case 'c':
382 fmt_spec[fmt_len++] = 'l';
383 fmt_spec[fmt_len++] = 'l';
384 fmt_spec[fmt_len++] = 'd';
385 fmt_spec[fmt_len] = '\0';
386 printf(fmt_spec, (long long)st->f_files);
387 break;
388 case 'd':
389 fmt_spec[fmt_len++] = 'l';
390 fmt_spec[fmt_len++] = 'l';
391 fmt_spec[fmt_len++] = 'd';
392 fmt_spec[fmt_len] = '\0';
393 printf(fmt_spec, (long long)st->f_ffree);
394 break;
395 // ?man -f: force the operation
396 case 'f':
397 fmt_spec[fmt_len++] = 'l';
398 fmt_spec[fmt_len++] = 'l';
399 fmt_spec[fmt_len++] = 'd';
400 fmt_spec[fmt_len] = '\0';
401 printf(fmt_spec, (long long)st->f_bfree);
402 break;
403 case 'i':
404 fmt_spec[fmt_len++] = 'l';
405 fmt_spec[fmt_len++] = 'l';
406 fmt_spec[fmt_len++] = 'x';
407 fmt_spec[fmt_len] = '\0';
408 printf(
409 fmt_spec,
410 (unsigned long long)st->f_fsid.__val[0]
411 | ((unsigned long long)st->f_fsid.__val[1] << 32)
412 );
413 break;
414 case 'l':
415 fmt_spec[fmt_len++] = 'l';
416 fmt_spec[fmt_len++] = 'd';
417 fmt_spec[fmt_len] = '\0';
418 printf(fmt_spec, (long)st->f_namelen);
419 break;
420 case 'n':
421 fmt_spec[fmt_len++] = 's';
422 fmt_spec[fmt_len] = '\0';
423 printf(fmt_spec, file);
424 break;
425 case 's':
426 fmt_spec[fmt_len++] = 'l';
427 fmt_spec[fmt_len++] = 'd';
428 fmt_spec[fmt_len] = '\0';
429 printf(fmt_spec, (long)st->f_bsize);
430 break;
431 case 'S':
432 fmt_spec[fmt_len++] = 'l';
433 fmt_spec[fmt_len++] = 'd';
434 fmt_spec[fmt_len] = '\0';
435 printf(fmt_spec, (long)st->f_frsize ? (long)st->f_frsize : (long)st->f_bsize);
436 break;
437 // ?man -t: sort or specify timestamp
438 case 't':
439 fmt_spec[fmt_len++] = 'l';
440 fmt_spec[fmt_len++] = 'x';
441 fmt_spec[fmt_len] = '\0';
442 printf(fmt_spec, (long)st->f_type);
443 break;
444 case 'T':
445 fmt_spec[fmt_len++] = 's';
446 fmt_spec[fmt_len] = '\0';
447 printf(fmt_spec, fs_type_name(st->f_type));
448 break;
449 default:
450 putchar('%');
451 if (*p) {
452 putchar(*p);
453 } else {
454 return;
455 }
456 break;
457 }
458 p++;
459 }
460 putchar('\n');
461}
462#endif
463#endif
464
465static void
466show_stat_terse(const char *file, struct stat *st)
467{
468 printf("%s ", file);
469 printf("%lu %lu ", (unsigned long)st->st_size, (unsigned long)st->st_blocks);
470 printf("%04o %u %u ", st->st_mode & 0777, st->st_uid, st->st_gid);
471 printf("%llx ", (unsigned long long)st->st_dev);
472 printf("%lu %lu ", (unsigned long)st->st_ino, (unsigned long)st->st_nlink);
473 printf("%d %d ", major(st->st_rdev), minor(st->st_rdev));
474 printf("%ld %ld %ld ", st->st_atime, st->st_mtime, st->st_ctime);
475 printf("%lu\n", (unsigned long)st->st_blksize);
476}
477
478static void
479show_stat(const char *file, struct stat *st)
480{
481 char buf[100];
482
483 printf(" File: ā%sā\n", file);
484 printf(
485 " Size: %lu\tBlocks: %lu\tIO Block: %lu\n",
486 (unsigned long)st->st_size,
487 (unsigned long)st->st_blocks,
488 (unsigned long)st->st_blksize
489 );
490 printf(
491 "Device: %xh/%ud\tInode: %lu\tLinks %lu\n",
492 major(st->st_dev),
493 minor(st->st_dev),
494 (unsigned long)st->st_ino,
495 (unsigned long)st->st_nlink
496 );
497 printf("Access: %04o\tUid: %u\tGid: %u\n", st->st_mode & 0777, st->st_uid, st->st_gid);
498 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&st->st_atime));
499 printf("Access: %s\n", buf);
500 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&st->st_mtime));
501 printf("Modify: %s\n", buf);
502 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&st->st_ctime));
503 printf("Change: %s\n", buf);
504}
505
506static void
507usage(void)
508{
509 eprintf(
510 "usage: %s [-L] [-t]"
511#if FEATURE_STAT_FILESYSTEM
512 " [-f]"
513#endif
514#if FEATURE_STAT_FORMAT
515 " [-c format]"
516#endif
517 " [file...]\n",
518 argv0
519 );
520}
521
522// ?man stat: display file status
523// ?man display file or filesystem status information
524int
525main(int argc, char *argv[])
526{
527 struct stat st;
528#if FEATURE_STAT_FILESYSTEM
529 struct statfs stfs;
530 int fflag = 0;
531#endif
532#if FEATURE_STAT_FORMAT
533 char *format = NULL;
534#endif
535 int i, ret = 0;
536 int (*fn)(const char *, struct stat *) = lstat;
537 char *fnname = "lstat";
538 void (*showstat)(const char *, struct stat *) = show_stat;
539
540 ARGBEGIN
541 {
542 // ?man -L: specify option flag
543 case 'L':
544 fn = stat;
545 fnname = "stat";
546 break;
547 // ?man -t: sort or specify timestamp
548 case 't':
549 showstat = show_stat_terse;
550 break;
551#if FEATURE_STAT_FILESYSTEM
552 // ?man -f: force the operation
553 case 'f':
554 fflag = 1;
555 break;
556#endif
557#if FEATURE_STAT_FORMAT
558 // ?man -c:str: print count or perform stdout action
559 case 'c':
560 format = EARGF(usage());
561 break;
562#endif
563 default:
564 usage();
565 }
566 ARGEND;
567
568 if (argc == 0) {
569#if FEATURE_STAT_FILESYSTEM
570 if (fflag) {
571 if (fstatfs(0, &stfs) < 0)
572 eprintf("fstatfs <stdin>:");
573#if FEATURE_STAT_FORMAT
574 if (format)
575 print_fs_custom("<stdin>", &stfs, format);
576 else
577#endif
578 show_statfs("<stdin>", &stfs);
579 } else {
580#endif
581 if (fstat(0, &st) < 0)
582 eprintf("fstat <stdin>:");
583#if FEATURE_STAT_FORMAT
584 if (format)
585 print_custom("<stdin>", &st, format);
586 else
587#endif
588 show_stat("<stdin>", &st);
589#if FEATURE_STAT_FILESYSTEM
590 }
591#endif
592 }
593
594 for (i = 0; i < argc; i++) {
595#if FEATURE_STAT_FILESYSTEM
596 if (fflag) {
597 if (statfs(argv[i], &stfs) == -1) {
598 weprintf("statfs %s:", argv[i]);
599 ret = 1;
600 continue;
601 }
602#if FEATURE_STAT_FORMAT
603 if (format)
604 print_fs_custom(argv[i], &stfs, format);
605 else
606#endif
607 show_statfs(argv[i], &stfs);
608 } else {
609#endif
610 if (fn(argv[i], &st) == -1) {
611 weprintf("%s %s:", fnname, argv[i]);
612 ret = 1;
613 continue;
614 }
615#if FEATURE_STAT_FORMAT
616 if (format)
617 print_custom(argv[i], &st, format);
618 else
619#endif
620 showstat(argv[i], &st);
621#if FEATURE_STAT_FILESYSTEM
622 }
623#endif
624 }
625
626 return ret;
627}