style fixes
[platform/upstream/busybox.git] / coreutils / stat.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * stat -- display file or file system status
4  *
5  * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation.
6  * Copyright (C) 2005 by Erik Andersen <andersen@codepoet.org>
7  * Copyright (C) 2005 by Mike Frysinger <vapier@gentoo.org>
8  * Copyright (C) 2006 by Yoshinori Sato <ysato@users.sourceforge.jp>
9  *
10  * Written by Michael Meskes
11  * Taken from coreutils and turned into a busybox applet by Mike Frysinger
12  *
13  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
14  */
15
16 #include "busybox.h"
17
18 /* vars to control behavior */
19 #define OPT_FILESYS             (1<<0)
20 #define OPT_TERSE               (1<<1)
21 #define OPT_DEREFERENCE (1<<2)
22 #define OPT_SELINUX             (1<<3)
23
24 static char buf[sizeof("YYYY-MM-DD HH:MM:SS.000000000")];
25
26 static char const * file_type(struct stat const *st)
27 {
28         /* See POSIX 1003.1-2001 XCU Table 4-8 lines 17093-17107
29          * for some of these formats.
30          * To keep diagnostics grammatical in English, the
31          * returned string must start with a consonant.
32          */
33         if (S_ISREG(st->st_mode))  return st->st_size == 0 ? "regular empty file" : "regular file";
34         if (S_ISDIR(st->st_mode))  return "directory";
35         if (S_ISBLK(st->st_mode))  return "block special file";
36         if (S_ISCHR(st->st_mode))  return "character special file";
37         if (S_ISFIFO(st->st_mode)) return "fifo";
38         if (S_ISLNK(st->st_mode))  return "symbolic link";
39         if (S_ISSOCK(st->st_mode)) return "socket";
40         if (S_TYPEISMQ(st))        return "message queue";
41         if (S_TYPEISSEM(st))       return "semaphore";
42         if (S_TYPEISSHM(st))       return "shared memory object";
43 #ifdef S_TYPEISTMO
44         if (S_TYPEISTMO(st))       return "typed memory object";
45 #endif
46         return "weird file";
47 }
48
49 static char const *human_time(time_t t)
50 {
51         /* Old
52         static char *str;
53         str = ctime(&t);
54         str[strlen(str)-1] = '\0';
55         return str;
56         */
57         /* coreutils 6.3 compat: */
58
59         strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S.000000000", localtime(&t));
60         return buf;
61 }
62
63 /* Return the type of the specified file system.
64  * Some systems have statfvs.f_basetype[FSTYPSZ]. (AIX, HP-UX, and Solaris)
65  * Others have statfs.f_fstypename[MFSNAMELEN]. (NetBSD 1.5.2)
66  * Still others have neither and have to get by with f_type (Linux).
67  */
68 static char const *human_fstype(long f_type)
69 {
70         int i;
71         static const struct types {
72                 long type;
73                 const char * const fs;
74         } humantypes[] = {
75                 { 0xADFF,     "affs" },
76                 { 0x1Cd1,     "devpts" },
77                 { 0x137D,     "ext" },
78                 { 0xEF51,     "ext2" },
79                 { 0xEF53,     "ext2/ext3" },
80                 { 0x3153464a, "jfs" },
81                 { 0x58465342, "xfs" },
82                 { 0xF995E849, "hpfs" },
83                 { 0x9660,     "isofs" },
84                 { 0x4000,     "isofs" },
85                 { 0x4004,     "isofs" },
86                 { 0x137F,     "minix" },
87                 { 0x138F,     "minix (30 char.)" },
88                 { 0x2468,     "minix v2" },
89                 { 0x2478,     "minix v2 (30 char.)" },
90                 { 0x4d44,     "msdos" },
91                 { 0x4006,     "fat" },
92                 { 0x564c,     "novell" },
93                 { 0x6969,     "nfs" },
94                 { 0x9fa0,     "proc" },
95                 { 0x517B,     "smb" },
96                 { 0x012FF7B4, "xenix" },
97                 { 0x012FF7B5, "sysv4" },
98                 { 0x012FF7B6, "sysv2" },
99                 { 0x012FF7B7, "coh" },
100                 { 0x00011954, "ufs" },
101                 { 0x012FD16D, "xia" },
102                 { 0x5346544e, "ntfs" },
103                 { 0x1021994,  "tmpfs" },
104                 { 0x52654973, "reiserfs" },
105                 { 0x28cd3d45, "cramfs" },
106                 { 0x7275,     "romfs" },
107                 { 0x858458f6, "romfs" },
108                 { 0x73717368, "squashfs" },
109                 { 0x62656572, "sysfs" },
110                 { 0, "UNKNOWN" }
111         };
112         for (i = 0; humantypes[i].type; ++i)
113                 if (humantypes[i].type == f_type)
114                         break;
115         return humantypes[i].fs;
116 }
117
118 #if ENABLE_FEATURE_STAT_FORMAT
119 /* print statfs info */
120 static void print_statfs(char *pformat, const size_t buf_len, const char m,
121                          const char * const filename, void const *data
122                          USE_SELINUX(, security_context_t scontext))
123 {
124         struct statfs const *statfsbuf = data;
125         if (m == 'n') {
126                 strncat(pformat, "s", buf_len);
127                 printf(pformat, filename);
128         } else if (m == 'i') {
129                 strncat(pformat, "Lx", buf_len);
130                 printf(pformat, statfsbuf->f_fsid);
131         } else if (m == 'l') {
132                 strncat(pformat, "lu", buf_len);
133                 printf(pformat, statfsbuf->f_namelen);
134         } else if (m == 't') {
135                 strncat(pformat, "lx", buf_len);
136                 printf(pformat, (unsigned long) (statfsbuf->f_type)); /* no equiv */
137         } else if (m == 'T') {
138                 strncat(pformat, "s", buf_len);
139                 printf(pformat, human_fstype(statfsbuf->f_type));
140         } else if (m == 'b') {
141                 strncat(pformat, "jd", buf_len);
142                 printf(pformat, (intmax_t) (statfsbuf->f_blocks));
143         } else if (m == 'f') {
144                 strncat(pformat, "jd", buf_len);
145                 printf(pformat, (intmax_t) (statfsbuf->f_bfree));
146         } else if (m == 'a') {
147                 strncat(pformat, "jd", buf_len);
148                 printf(pformat, (intmax_t) (statfsbuf->f_bavail));
149         } else if (m == 's' || m == 'S') {
150                 strncat(pformat, "lu", buf_len);
151                 printf(pformat, (unsigned long) (statfsbuf->f_bsize));
152         } else if (m == 'c') {
153                 strncat(pformat, "jd", buf_len);
154                 printf(pformat, (intmax_t) (statfsbuf->f_files));
155         } else if (m == 'd') {
156                 strncat(pformat, "jd", buf_len);
157                 printf(pformat, (intmax_t) (statfsbuf->f_ffree));
158 #if ENABLE_SELINUX
159         } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) {
160                 strncat(pformat, "s", buf_len);
161                 printf(scontext);
162 #endif
163         } else {
164                 strncat(pformat, "c", buf_len);
165                 printf(pformat, m);
166         }
167 }
168
169 /* print stat info */
170 static void print_stat(char *pformat, const size_t buf_len, const char m,
171                        const char * const filename, void const *data
172                            USE_SELINUX(, security_context_t scontext))
173 {
174 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
175         struct stat *statbuf = (struct stat *) data;
176         struct passwd *pw_ent;
177         struct group *gw_ent;
178
179         if (m == 'n') {
180                 strncat(pformat, "s", buf_len);
181                 printf(pformat, filename);
182         } else if (m == 'N') {
183                 strncat(pformat, "s", buf_len);
184                 if (S_ISLNK(statbuf->st_mode)) {
185                         char *linkname = xmalloc_readlink_or_warn(filename);
186                         if (linkname == NULL) {
187                                 bb_perror_msg("cannot read symbolic link '%s'", filename);
188                                 return;
189                         }
190                         /*printf("\"%s\" -> \"%s\"", filename, linkname); */
191                         printf(pformat, filename);
192                         printf(" -> ");
193                         printf(pformat, linkname);
194                 } else {
195                         printf(pformat, filename);
196                 }
197         } else if (m == 'd') {
198                 strncat(pformat, "ju", buf_len);
199                 printf(pformat, (uintmax_t) statbuf->st_dev);
200         } else if (m == 'D') {
201                 strncat(pformat, "jx", buf_len);
202                 printf(pformat, (uintmax_t) statbuf->st_dev);
203         } else if (m == 'i') {
204                 strncat(pformat, "ju", buf_len);
205                 printf(pformat, (uintmax_t) statbuf->st_ino);
206         } else if (m == 'a') {
207                 strncat(pformat, "lo", buf_len);
208                 printf(pformat, (unsigned long) (statbuf->st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)));
209         } else if (m == 'A') {
210                 strncat(pformat, "s", buf_len);
211                 printf(pformat, bb_mode_string(statbuf->st_mode));
212         } else if (m == 'f') {
213                 strncat(pformat, "lx", buf_len);
214                 printf(pformat, (unsigned long) statbuf->st_mode);
215         } else if (m == 'F') {
216                 strncat(pformat, "s", buf_len);
217                 printf(pformat, file_type(statbuf));
218         } else if (m == 'h') {
219                 strncat(pformat, "lu", buf_len);
220                 printf(pformat, (unsigned long) statbuf->st_nlink);
221         } else if (m == 'u') {
222                 strncat(pformat, "lu", buf_len);
223                 printf(pformat, (unsigned long) statbuf->st_uid);
224         } else if (m == 'U') {
225                 strncat(pformat, "s", buf_len);
226                 setpwent();
227                 pw_ent = getpwuid(statbuf->st_uid);
228                 printf(pformat, (pw_ent != 0L) ? pw_ent->pw_name : "UNKNOWN");
229         } else if (m == 'g') {
230                 strncat(pformat, "lu", buf_len);
231                 printf(pformat, (unsigned long) statbuf->st_gid);
232         } else if (m == 'G') {
233                 strncat(pformat, "s", buf_len);
234                 setgrent();
235                 gw_ent = getgrgid(statbuf->st_gid);
236                 printf(pformat, (gw_ent != 0L) ? gw_ent->gr_name : "UNKNOWN");
237         } else if (m == 't') {
238                 strncat(pformat, "lx", buf_len);
239                 printf(pformat, (unsigned long) major(statbuf->st_rdev));
240         } else if (m == 'T') {
241                 strncat(pformat, "lx", buf_len);
242                 printf(pformat, (unsigned long) minor(statbuf->st_rdev));
243         } else if (m == 's') {
244                 strncat(pformat, "ju", buf_len);
245                 printf(pformat, (uintmax_t) (statbuf->st_size));
246         } else if (m == 'B') {
247                 strncat(pformat, "lu", buf_len);
248                 printf(pformat, (unsigned long) 512); //ST_NBLOCKSIZE
249         } else if (m == 'b') {
250                 strncat(pformat, "ju", buf_len);
251                 printf(pformat, (uintmax_t) statbuf->st_blocks);
252         } else if (m == 'o') {
253                 strncat(pformat, "lu", buf_len);
254                 printf(pformat, (unsigned long) statbuf->st_blksize);
255         } else if (m == 'x') {
256                 strncat(pformat, "s", buf_len);
257                 printf(pformat, human_time(statbuf->st_atime));
258         } else if (m == 'X') {
259                 strncat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu", buf_len);
260                 printf(pformat, (unsigned long) statbuf->st_atime);
261         } else if (m == 'y') {
262                 strncat(pformat, "s", buf_len);
263                 printf(pformat, human_time(statbuf->st_mtime));
264         } else if (m == 'Y') {
265                 strncat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu", buf_len);
266                 printf(pformat, (unsigned long) statbuf->st_mtime);
267         } else if (m == 'z') {
268                 strncat(pformat, "s", buf_len);
269                 printf(pformat, human_time(statbuf->st_ctime));
270         } else if (m == 'Z') {
271                 strncat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu", buf_len);
272                 printf(pformat, (unsigned long) statbuf->st_ctime);
273 #if ENABLE_SELINUX
274         } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) {
275                 strncat(pformat, "s", buf_len);
276                 printf(pformat, scontext);
277 #endif
278         } else {
279                 strncat(pformat, "c", buf_len);
280                 printf(pformat, m);
281         }
282 }
283
284 static void print_it(char const *masterformat, char const *filename,
285                      void (*print_func) (char *, size_t, char, char const *, void const *
286                                                                  USE_SELINUX(, security_context_t scontext)),
287                                          void const *data USE_SELINUX(, security_context_t scontext) )
288 {
289         char *b;
290
291         /* create a working copy of the format string */
292         char *format = xstrdup(masterformat);
293
294         /* Add 2 to accomodate our conversion of the stat '%s' format string
295          * to the printf '%llu' one.  */
296         size_t n_alloc = strlen(format) + 2 + 1;
297         char *dest = xmalloc(n_alloc);
298
299         b = format;
300         while (b) {
301                 size_t len;
302                 char *p = strchr(b, '%');
303                 if (!p) {
304                         /* coreutils 6.3 always print <cr> at the end */
305                         /*fputs(b, stdout);*/
306                         puts(b);
307                         break;
308                 }
309                 *p++ = '\0';
310                 fputs(b, stdout);
311
312                 len = strspn(p, "#-+.I 0123456789");
313                 dest[0] = '%';
314                 memcpy(dest + 1, p, len);
315                 dest[1 + len] = 0;
316                 p += len;
317
318                 b = p + 1;
319                 switch (*p) {
320                 case '\0':
321                         b = NULL;
322                         /* fall through */
323                 case '%':
324                         putchar('%');
325                         break;
326                 default:
327                         print_func(dest, n_alloc, *p, filename, data USE_SELINUX(,scontext));
328                         break;
329                 }
330         }
331
332         free(format);
333         free(dest);
334 }
335 #endif
336
337 /* Stat the file system and print what we find.  */
338 static bool do_statfs(char const *filename, char const *format)
339 {
340         struct statfs statfsbuf;
341 #if ENABLE_SELINUX
342         security_context_t scontext = NULL;
343
344         if (option_mask32 & OPT_SELINUX) {
345                 if ((option_mask32 & OPT_DEREFERENCE ? lgetfilecon(filename, scontext):
346                      getfilecon(filename, scontext))< 0) {
347                         bb_perror_msg(filename);
348                         return 0;
349                 }
350         }
351 #endif
352         if (statfs(filename, &statfsbuf) != 0) {
353                 bb_perror_msg("cannot read file system information for '%s'", filename);
354                 return 0;
355         }
356
357 #if ENABLE_FEATURE_STAT_FORMAT
358         if (format == NULL)
359 #if !ENABLE_SELINUX
360                 format = (option_mask32 & OPT_TERSE
361                         ? "%n %i %l %t %s %b %f %a %c %d\n"
362                         : "  File: \"%n\"\n"
363                           "    ID: %-8i Namelen: %-7l Type: %T\n"
364                           "Block size: %-10s\n"
365                           "Blocks: Total: %-10b Free: %-10f Available: %a\n"
366                           "Inodes: Total: %-10c Free: %d");
367         print_it(format, filename, print_statfs, &statfsbuf USE_SELINUX(, scontext));
368 #else
369         format = (option_mask32 & OPT_TERSE
370                         ? (option_mask32 & OPT_SELINUX ? "%n %i %l %t %s %b %f %a %c %d %C\n":
371                         "%n %i %l %t %s %b %f %a %c %d\n")
372                         : (option_mask32 & OPT_SELINUX ?
373                         "  File: \"%n\"\n"
374                         "    ID: %-8i Namelen: %-7l Type: %T\n"
375                         "Block size: %-10s\n"
376                         "Blocks: Total: %-10b Free: %-10f Available: %a\n"
377                         "Inodes: Total: %-10c Free: %d"
378                         "  S_context: %C\n":
379                         "  File: \"%n\"\n"
380                         "    ID: %-8i Namelen: %-7l Type: %T\n"
381                         "Block size: %-10s\n"
382                         "Blocks: Total: %-10b Free: %-10f Available: %a\n"
383                         "Inodes: Total: %-10c Free: %d\n")
384                         );
385         print_it(format, filename, print_statfs, &statfsbuf USE_SELINUX(, scontext));
386 #endif /* SELINUX */
387 #else /* FEATURE_STAT_FORMAT */
388         format = (option_mask32 & OPT_TERSE
389                 ? "%s %llx %lu "
390                 : "  File: \"%s\"\n"
391                   "    ID: %-8Lx Namelen: %-7lu ");
392         printf(format,
393                filename,
394                statfsbuf.f_fsid,
395                statfsbuf.f_namelen);
396
397         if (option_mask32 & OPT_TERSE)
398                 printf("%lx ", (unsigned long) (statfsbuf.f_type));
399         else
400                 printf("Type: %s\n", human_fstype(statfsbuf.f_type));
401
402 #if !ENABLE_SELINUX
403         format = (option_mask32 & OPT_TERSE
404                 ? "%lu %ld %ld %ld %ld %ld\n"
405                 : "Block size: %-10lu\n"
406                   "Blocks: Total: %-10jd Free: %-10jd Available: %jd\n"
407                   "Inodes: Total: %-10jd Free: %jd\n");
408         printf(format,
409                (unsigned long) (statfsbuf.f_bsize),
410                (intmax_t) (statfsbuf.f_blocks),
411                (intmax_t) (statfsbuf.f_bfree),
412                (intmax_t) (statfsbuf.f_bavail),
413                (intmax_t) (statfsbuf.f_files),
414                (intmax_t) (statfsbuf.f_ffree));
415 #else
416         format = (option_mask32 & OPT_TERSE
417                 ? (option_mask32 & OPT_SELINUX ? "%lu %ld %ld %ld %ld %ld %C\n":
418                 "%lu %ld %ld %ld %ld %ld\n")
419                 : (option_mask32 & OPT_SELINUX ?
420                 "Block size: %-10lu\n"
421                 "Blocks: Total: %-10jd Free: %-10jd Available: %jd\n"
422                 "Inodes: Total: %-10jd Free: %jd"
423                 "S_context: %C\n":
424                 "Block size: %-10lu\n"
425                 "Blocks: Total: %-10jd Free: %-10jd Available: %jd\n"
426                 "Inodes: Total: %-10jd Free: %jd\n"));
427         printf(format,
428                (unsigned long) (statfsbuf.f_bsize),
429                (intmax_t) (statfsbuf.f_blocks),
430                (intmax_t) (statfsbuf.f_bfree),
431                (intmax_t) (statfsbuf.f_bavail),
432                (intmax_t) (statfsbuf.f_files),
433                (intmax_t) (statfsbuf.f_ffree),
434                 scontext);
435
436         if (scontext)
437                 freecon(scontext);
438 #endif
439 #endif  /* FEATURE_STAT_FORMAT */
440         return 1;
441 }
442
443 /* stat the file and print what we find */
444 static bool do_stat(char const *filename, char const *format)
445 {
446         struct stat statbuf;
447 #if ENABLE_SELINUX
448         security_context_t scontext = NULL;
449
450         if (option_mask32 & OPT_SELINUX) {
451                 if ((option_mask32 & OPT_DEREFERENCE ? lgetfilecon(filename, scontext):
452                      getfilecon(filename, scontext))< 0) {
453                         bb_perror_msg (filename);
454                         return 0;
455                 }
456         }
457 #endif
458         if ((option_mask32 & OPT_DEREFERENCE ? stat : lstat) (filename, &statbuf) != 0) {
459                 bb_perror_msg("cannot stat '%s'", filename);
460                 return 0;
461         }
462
463 #if ENABLE_FEATURE_STAT_FORMAT
464         if (format == NULL) {
465 #if !ENABLE_SELINUX
466                 if (option_mask32 & OPT_TERSE) {
467                         format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o";
468                 } else {
469                         if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
470                                 format =
471                                         "  File: \"%N\"\n"
472                                         "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
473                                         "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
474                                         " Device type: %t,%T\n"
475                                         "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
476                                         "Access: %x\n" "Modify: %y\n" "Change: %z\n";
477                         } else {
478                                 format =
479                                         "  File: \"%N\"\n"
480                                         "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
481                                         "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
482                                         "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
483                                         "Access: %x\n" "Modify: %y\n" "Change: %z\n";
484                         }
485                 }
486 #else
487                 if (option_mask32 & OPT_TERSE) {
488                         format = (option_mask32 & OPT_SELINUX ?
489                                   "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o %C\n":
490                                   "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n");
491                 } else {
492                         if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
493                                 format = (option_mask32 & OPT_SELINUX ?
494                                           "  File: \"%N\"\n"
495                                           "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
496                                           "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
497                                           " Device type: %t,%T\n"
498                                           "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
499                                           "   S_Context: %C\n"
500                                           "Access: %x\n" "Modify: %y\n" "Change: %z\n":
501                                           "  File: \"%N\"\n"
502                                           "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
503                                           "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
504                                           " Device type: %t,%T\n"
505                                           "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
506                                           "Access: %x\n" "Modify: %y\n" "Change: %z\n");
507                         } else {
508                                 format = (option_mask32 & OPT_SELINUX ?
509                                           "  File: \"%N\"\n"
510                                           "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
511                                           "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
512                                           "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
513                                           "S_Context: %C\n"
514                                           "Access: %x\n" "Modify: %y\n" "Change: %z\n":
515                                           "  File: \"%N\"\n"
516                                           "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
517                                           "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
518                                           "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
519                                           "Access: %x\n" "Modify: %y\n" "Change: %z\n");
520                         }
521                 }
522 #endif
523         }
524         print_it(format, filename, print_stat, &statbuf USE_SELINUX(, scontext));
525 #else   /* FEATURE_STAT_FORMAT */
526         if (option_mask32 & OPT_TERSE) {
527                 printf("%s %ju %ju %lx %lu %lu %jx %ju %lu %lx %lx %lu %lu %lu %lu"
528                        SKIP_SELINUX("\n"),
529                        filename,
530                        (uintmax_t) (statbuf.st_size),
531                        (uintmax_t) statbuf.st_blocks,
532                        (unsigned long) statbuf.st_mode,
533                        (unsigned long) statbuf.st_uid,
534                        (unsigned long) statbuf.st_gid,
535                        (uintmax_t) statbuf.st_dev,
536                        (uintmax_t) statbuf.st_ino,
537                        (unsigned long) statbuf.st_nlink,
538                        (unsigned long) major(statbuf.st_rdev),
539                        (unsigned long) minor(statbuf.st_rdev),
540                        (unsigned long) statbuf.st_atime,
541                        (unsigned long) statbuf.st_mtime,
542                        (unsigned long) statbuf.st_ctime,
543                        (unsigned long) statbuf.st_blksize
544                 );
545 #if ENABLE_SELINUX
546                 if (option_mask32 & OPT_SELINUX)
547                         printf(" %lc\n", *scontext);
548                 else
549                         putchar('\n');
550 #endif
551         } else {
552                 char *linkname = NULL;
553
554                 struct passwd *pw_ent;
555                 struct group *gw_ent;
556                 setgrent();
557                 gw_ent = getgrgid(statbuf.st_gid);
558                 setpwent();
559                 pw_ent = getpwuid(statbuf.st_uid);
560
561                 if (S_ISLNK(statbuf.st_mode))
562                         linkname = xmalloc_readlink_or_warn(filename);
563                 if (linkname)
564                         printf("  File: \"%s\" -> \"%s\"\n", filename, linkname);
565                 else
566                         printf("  File: \"%s\"\n", filename);
567
568                 printf("  Size: %-10ju\tBlocks: %-10ju IO Block: %-6lu %s\n"
569                        "Device: %jxh/%jud\tInode: %-10ju  Links: %-5lu",
570                        (uintmax_t) (statbuf.st_size),
571                        (uintmax_t) statbuf.st_blocks,
572                        (unsigned long) statbuf.st_blksize,
573                        file_type(&statbuf),
574                        (uintmax_t) statbuf.st_dev,
575                        (uintmax_t) statbuf.st_dev,
576                        (uintmax_t) statbuf.st_ino,
577                        (unsigned long) statbuf.st_nlink);
578                 if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode))
579                         printf(" Device type: %lx,%lx\n",
580                                (unsigned long) major(statbuf.st_rdev),
581                                (unsigned long) minor(statbuf.st_rdev));
582                 else
583                         putchar('\n');
584                 printf("Access: (%04lo/%10.10s)  Uid: (%5lu/%8s)   Gid: (%5lu/%8s)\n",
585                        (unsigned long) (statbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)),
586                        bb_mode_string(statbuf.st_mode),
587                        (unsigned long) statbuf.st_uid,
588                        (pw_ent != 0L) ? pw_ent->pw_name : "UNKNOWN",
589                        (unsigned long) statbuf.st_gid,
590                        (gw_ent != 0L) ? gw_ent->gr_name : "UNKNOWN");
591 #if ENABLE_SELINUX
592                 printf("   S_Context: %lc\n", *scontext);
593 #endif
594                 printf("Access: %s\n" "Modify: %s\n" "Change: %s\n",
595                        human_time(statbuf.st_atime),
596                        human_time(statbuf.st_mtime),
597                        human_time(statbuf.st_ctime));
598         }
599 #endif  /* FEATURE_STAT_FORMAT */
600         return 1;
601 }
602
603 int stat_main(int argc, char **argv);
604 int stat_main(int argc, char **argv)
605 {
606         char *format = NULL;
607         int i;
608         int ok = 1;
609         bool (*statfunc)(char const *, char const *) = do_stat;
610
611         getopt32(argc, argv, "ftL"
612                 USE_SELINUX("Z")
613                 USE_FEATURE_STAT_FORMAT("c:", &format)
614         );
615
616         if (option_mask32 & OPT_FILESYS) /* -f */
617                 statfunc = do_statfs;
618         if (argc == optind)           /* files */
619                 bb_show_usage();
620
621 #if ENABLE_SELINUX
622         if (option_mask32 & OPT_SELINUX) {
623                 selinux_or_die();
624         }
625 #endif  /* ENABLE_SELINUX */
626         for (i = optind; i < argc; ++i)
627                 ok &= statfunc(argv[i], format);
628
629         return (ok ? EXIT_SUCCESS : EXIT_FAILURE);
630 }