1/* stat.c : display file or file system status 2 * Copyright 2012 <warior.linux@gmail.com> 3 * Copyright 2013 <anand.sinha85@gmail.com> 4 5USE_STAT(NEWTOY(stat, "<1c:fLt", TOYFLAG_BIN)) 6 7config STAT 8 bool stat 9 default y 10 help 11 usage: stat [-tfL] [-c FORMAT] FILE... 12 13 Display status of files or filesystems. 14 15 -c Output specified FORMAT string instead of default 16 -f display filesystem status instead of file status 17 -L Follow symlinks 18 -t terse (-c "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o") 19 (with -f = -c "%n %i %l %t %s %S %b %f %a %c %d") 20 21 The valid format escape sequences for files: 22 %a Access bits (octal) |%A Access bits (flags)|%b Size/512 23 %B Bytes per %b (512) |%d Device ID (dec) |%D Device ID (hex) 24 %f All mode bits (hex) |%F File type |%g Group ID 25 %G Group name |%h Hard links |%i Inode 26 %m Mount point |%n Filename |%N Long filename 27 %o I/O block size |%s Size (bytes) |%t Devtype major (hex) 28 %T Devtype minor (hex) |%u User ID |%U User name 29 %x Access time |%X Access unix time |%y File write time 30 %Y File write unix time|%z Dir change time |%Z Dir change unix time 31 32 The valid format escape sequences for filesystems: 33 %a Available blocks |%b Total blocks |%c Total inodes 34 %d Free inodes |%f Free blocks |%i File system ID 35 %l Max filename length |%n File name |%s Fragment size 36 %S Best transfer size |%t FS type (hex) |%T FS type (driver name) 37*/ 38 39#define FOR_stat 40#include "toys.h" 41 42GLOBALS( 43 char *fmt; 44 45 union { 46 struct stat st; 47 struct statfs sf; 48 } stat; 49 char *file, *pattern; 50 int patlen; 51) 52 53// Force numeric output to long long instead of manually typecasting everything 54// and safely parse length prefix 55static void out(char c, long long val) 56{ 57 sprintf(toybuf, "%.*sll%c", TT.patlen, TT.pattern, c); 58 printf(toybuf, val); 59} 60 61// Output string with parsed length prefix 62static void strout(char *val) 63{ 64 sprintf(toybuf, "%.*ss", TT.patlen, TT.pattern); 65 printf(toybuf, val); 66} 67 68static void date_stat_format(struct timespec *ts) 69{ 70 char *s = toybuf+128; 71 strftime(s, sizeof(toybuf)-128, "%Y-%m-%d %H:%M:%S", 72 localtime(&(ts->tv_sec))); 73 sprintf(s+strlen(s), ".%09ld", ts->tv_nsec); 74 strout(s); 75} 76 77static void print_stat(char type) 78{ 79 struct stat *stat = (struct stat *)&TT.stat; 80 81 if (type == 'a') out('o', stat->st_mode&~S_IFMT); 82 else if (type == 'A') { 83 char str[11]; 84 85 mode_to_string(stat->st_mode, str); 86 strout(str); 87 } else if (type == 'b') out('u', stat->st_blocks); 88 else if (type == 'B') out('d', 512); 89 else if (type == 'd') out('d', stat->st_dev); 90 else if (type == 'D') out('x', stat->st_dev); 91 else if (type == 'f') out('x', stat->st_mode); 92 else if (type == 'F') { 93 char *t = "character device\0directory\0block device\0" \ 94 "regular file\0symbolic link\0socket\0FIFO (named pipe)"; 95 int i, filetype = stat->st_mode & S_IFMT; 96 97 for (i = 1; filetype != (i*8192) && i < 7; i++) t += strlen(t)+1; 98 if (!stat->st_size && filetype == S_IFREG) t = "regular empty file"; 99 strout(t); 100 } else if (type == 'g') out('u', stat->st_gid); 101 else if (type == 'G') strout(getgroupname(stat->st_gid)); 102 else if (type == 'h') out('u', stat->st_nlink); 103 else if (type == 'i') out('u', stat->st_ino); 104 else if (type == 'm') { 105 struct mtab_list *mt = xgetmountlist(0); 106 dev_t dev = stat->st_rdev ? stat->st_rdev : stat->st_dev; 107 108 // This mount point could exist multiple times, so show oldest. 109 for (dlist_terminate(mt); mt; mt = mt->next) if (mt->stat.st_dev == dev) { 110 strout(mt->dir); 111 break; 112 } 113 llist_traverse(mt, free); 114 } else if (type == 'N') { 115 xprintf("`%s'", TT.file); 116 if (S_ISLNK(stat->st_mode)) 117 if (readlink0(TT.file, toybuf, sizeof(toybuf))) 118 xprintf(" -> `%s'", toybuf); 119 } else if (type == 'o') out('u', stat->st_blksize); 120 else if (type == 's') out('u', stat->st_size); 121 else if (type == 't') out('x', dev_major(stat->st_rdev)); 122 else if (type == 'T') out('x', dev_minor(stat->st_rdev)); 123 else if (type == 'u') out('u', stat->st_uid); 124 else if (type == 'U') strout(getusername(stat->st_uid)); 125 else if (type == 'x') date_stat_format(&stat->st_atim); 126 else if (type == 'X') out('u', stat->st_atime); 127 else if (type == 'y') date_stat_format(&stat->st_mtim); 128 else if (type == 'Y') out('u', stat->st_mtime); 129 else if (type == 'z') date_stat_format(&stat->st_ctim); 130 else if (type == 'Z') out('u', stat->st_ctime); 131 else xprintf("?"); 132} 133 134static void print_statfs(char type) { 135 struct statfs *statfs = (struct statfs *)&TT.stat; 136 137 if (type == 'a') out('u', statfs->f_bavail); 138 else if (type == 'b') out('u', statfs->f_blocks); 139 else if (type == 'c') out('u', statfs->f_files); 140 else if (type == 'd') out('u', statfs->f_ffree); 141 else if (type == 'f') out('u', statfs->f_bfree); 142 else if (type == 'l') out('d', statfs->f_namelen); 143 else if (type == 't') out('x', statfs->f_type); 144 else if (type == 'T') { 145 char *s = "unknown"; 146 struct {unsigned num; char *name;} nn[] = { 147 {0xADFF, "affs"}, {0x5346544e, "ntfs"}, {0x1Cd1, "devpts"}, 148 {0x137D, "ext"}, {0xEF51, "ext2"}, {0xEF53, "ext3"}, 149 {0x1BADFACE, "bfs"}, {0x9123683E, "btrfs"}, {0x28cd3d45, "cramfs"}, 150 {0x3153464a, "jfs"}, {0x7275, "romfs"}, {0x01021994, "tmpfs"}, 151 {0x3434, "nilfs"}, {0x6969, "nfs"}, {0x9fa0, "proc"}, 152 {0x534F434B, "sockfs"}, {0x62656572, "sysfs"}, {0x517B, "smb"}, 153 {0x4d44, "msdos"}, {0x4006, "fat"}, {0x43415d53, "smackfs"}, 154 {0x73717368, "squashfs"} 155 }; 156 int i; 157 158 for (i=0; i<ARRAY_LEN(nn); i++) 159 if (nn[i].num == statfs->f_type) s = nn[i].name; 160 strout(s); 161 } else if (type == 'i') { 162 char buf[32]; 163 164 sprintf(buf, "%08x%08x", statfs->f_fsid.__val[0], statfs->f_fsid.__val[1]); 165 strout(buf); 166 } else if (type == 's') out('d', statfs->f_frsize); 167 else if (type == 'S') out('d', statfs->f_bsize); 168 else strout("?"); 169} 170 171void stat_main(void) 172{ 173 int flagf = toys.optflags & FLAG_f, i; 174 char *format, *f; 175 176 if (toys.optflags&FLAG_t) { 177 format = flagf ? "%n %i %l %t %s %S %b %f %a %c %d" : 178 "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o"; 179 } else format = flagf 180 ? " File: \"%n\"\n ID: %i Namelen: %l Type: %t\n" 181 "Block Size: %s Fundamental block size: %S\n" 182 "Blocks: Total: %b\tFree: %f\tAvailable: %a\n" 183 "Inodes: Total: %c\tFree: %d" 184 : " File: %N\n Size: %s\t Blocks: %b\t IO Blocks: %B\t%F\n" 185 "Device: %Dh/%dd\t Inode: %i\t Links: %h\n" 186 "Access: (%a/%A)\tUid: (%5u/%8U)\tGid: (%5g/%8G)\n" 187 "Access: %x\nModify: %y\nChange: %z"; 188 189 if (toys.optflags & FLAG_c) format = TT.fmt; 190 191 for (i = 0; toys.optargs[i]; i++) { 192 int L = toys.optflags & FLAG_L; 193 194 TT.file = toys.optargs[i]; 195 if (flagf && !statfs(TT.file, (void *)&TT.stat)); 196 else if (flagf || (L ? stat : lstat)(TT.file, (void *)&TT.stat)) { 197 perror_msg("'%s'", TT.file); 198 continue; 199 } 200 201 for (f = format; *f; f++) { 202 if (*f != '%') putchar(*f); 203 else { 204 f = next_printf(f, &TT.pattern); 205 TT.patlen = f-TT.pattern; 206 if (TT.patlen>99) error_exit("bad %s", TT.pattern); 207 if (*f == 'n') strout(TT.file); 208 else if (flagf) print_statfs(*f); 209 else print_stat(*f); 210 } 211 } 212 xputc('\n'); 213 } 214} 215