master xplshn/aruu / cmd / pseudo / stat.c
  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}