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