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