1 |
/* stat.c -- display file or file system status |
2 |
Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation. |
3 |
|
4 |
This program is free software; you can redistribute it and/or modify |
5 |
it under the terms of the GNU General Public License as published by |
6 |
the Free Software Foundation; either version 2, or (at your option) |
7 |
any later version. |
8 |
|
9 |
This program is distributed in the hope that it will be useful, |
10 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 |
GNU General Public License for more details. |
13 |
|
14 |
You should have received a copy of the GNU General Public License |
15 |
along with this program; if not, write to the Free Software Foundation, |
16 |
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 |
|
18 |
Written by Michael Meskes. */ |
19 |
|
20 |
#include <config.h> |
21 |
|
22 |
#include <stdio.h> |
23 |
#include <sys/types.h> |
24 |
#include <pwd.h> |
25 |
#include <grp.h> |
26 |
#if HAVE_SYS_STATVFS_H && HAVE_STRUCT_STATVFS_F_BASETYPE |
27 |
# include <sys/statvfs.h> |
28 |
#elif HAVE_SYS_VFS_H |
29 |
# include <sys/vfs.h> |
30 |
#elif HAVE_SYS_MOUNT_H && HAVE_SYS_PARAM_H |
31 |
/* NOTE: freebsd5.0 needs sys/param.h and sys/mount.h for statfs. |
32 |
It does have statvfs.h, but shouldn't use it, since it doesn't |
33 |
HAVE_STRUCT_STATVFS_F_BASETYPE. So find a clean way to fix it. */ |
34 |
/* NetBSD 1.5.2 needs these, for the declaration of struct statfs. */ |
35 |
# include <sys/param.h> |
36 |
# include <sys/mount.h> |
37 |
# if HAVE_NETINET_IN_H && HAVE_NFS_NFS_CLNT_H && HAVE_NFS_VFS_H |
38 |
/* Ultrix 4.4 needs these for the declaration of struct statfs. */ |
39 |
# include <netinet/in.h> |
40 |
# include <nfs/nfs_clnt.h> |
41 |
# include <nfs/vfs.h> |
42 |
# endif |
43 |
#endif |
44 |
|
45 |
#include "system.h" |
46 |
|
47 |
#include "error.h" |
48 |
#include "filemode.h" |
49 |
#include "file-type.h" |
50 |
#include "fs.h" |
51 |
#include "getopt.h" |
52 |
#include "inttostr.h" |
53 |
#include "quote.h" |
54 |
#include "quotearg.h" |
55 |
#include "strftime.h" |
56 |
#include "xreadlink.h" |
57 |
|
58 |
#define NAMEMAX_FORMAT PRIuMAX |
59 |
|
60 |
#if HAVE_STRUCT_STATVFS_F_BASETYPE |
61 |
# define STRUCT_STATVFS struct statvfs |
62 |
# define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS_F_TYPE |
63 |
# if HAVE_STRUCT_STATVFS_F_NAMEMAX |
64 |
# define SB_F_NAMEMAX(S) ((uintmax_t) ((S)->f_namemax)) |
65 |
# endif |
66 |
# if STAT_STATVFS |
67 |
# define STATFS statvfs |
68 |
# define STATFS_FRSIZE(S) ((S)->f_frsize) |
69 |
# endif |
70 |
#else |
71 |
# define STRUCT_STATVFS struct statfs |
72 |
# define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE |
73 |
# if HAVE_STRUCT_STATFS_F_NAMELEN |
74 |
# define SB_F_NAMEMAX(S) ((uintmax_t) ((S)->f_namelen)) |
75 |
# endif |
76 |
#endif |
77 |
|
78 |
#ifndef STATFS |
79 |
# define STATFS statfs |
80 |
# define STATFS_FRSIZE(S) 0 |
81 |
#endif |
82 |
|
83 |
#ifndef SB_F_NAMEMAX |
84 |
/* NetBSD 1.5.2 has neither f_namemax nor f_namelen. */ |
85 |
# define SB_F_NAMEMAX(S) "*" |
86 |
# undef NAMEMAX_FORMAT |
87 |
# define NAMEMAX_FORMAT "s" |
88 |
#endif |
89 |
|
90 |
#if HAVE_STRUCT_STATVFS_F_BASETYPE |
91 |
# define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype |
92 |
#else |
93 |
# if HAVE_STRUCT_STATFS_F_FSTYPENAME |
94 |
# define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename |
95 |
# endif |
96 |
#endif |
97 |
|
98 |
#define PROGRAM_NAME "stat" |
99 |
|
100 |
#define AUTHORS "Michael Meskes" |
101 |
|
102 |
static struct option const long_options[] = { |
103 |
{"link", no_argument, NULL, 'l'}, /* deprecated. FIXME: remove in 2003 */ |
104 |
{"dereference", no_argument, NULL, 'L'}, |
105 |
{"file-system", no_argument, NULL, 'f'}, |
106 |
{"filesystem", no_argument, NULL, 'f'}, /* obsolete and undocumented alias */ |
107 |
{"format", required_argument, NULL, 'c'}, |
108 |
{"terse", no_argument, NULL, 't'}, |
109 |
{GETOPT_HELP_OPTION_DECL}, |
110 |
{GETOPT_VERSION_OPTION_DECL}, |
111 |
{NULL, 0, NULL, 0} |
112 |
}; |
113 |
|
114 |
char *program_name; |
115 |
|
116 |
/* Return the type of the specified file system. |
117 |
Some systems have statfvs.f_basetype[FSTYPSZ]. (AIX, HP-UX, and Solaris) |
118 |
Others have statfs.f_fstypename[MFSNAMELEN]. (NetBSD 1.5.2) |
119 |
Still others have neither and have to get by with f_type (Linux). */ |
120 |
static char const * |
121 |
human_fstype (STRUCT_STATVFS const *statfsbuf) |
122 |
{ |
123 |
#ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME |
124 |
return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME; |
125 |
#else |
126 |
switch (statfsbuf->f_type) |
127 |
{ |
128 |
# if defined __linux__ |
129 |
|
130 |
/* IMPORTANT NOTE: Each of the following `case S_MAGIC_...:' |
131 |
statements must be followed by a hexadecimal constant in |
132 |
a comment. The S_MAGIC_... name and constant are automatically |
133 |
combined to produce the #define directives in fs.h. */ |
134 |
|
135 |
case S_MAGIC_AFFS: /* 0xADFF */ |
136 |
return "affs"; |
137 |
case S_MAGIC_DEVPTS: /* 0x1CD1 */ |
138 |
return "devpts"; |
139 |
case S_MAGIC_EXT: /* 0x137D */ |
140 |
return "ext"; |
141 |
case S_MAGIC_EXT2_OLD: /* 0xEF51 */ |
142 |
return "ext2"; |
143 |
case S_MAGIC_EXT2: /* 0xEF53 */ |
144 |
return "ext2/ext3"; |
145 |
case S_MAGIC_JFS: /* 0x3153464a */ |
146 |
return "jfs"; |
147 |
case S_MAGIC_XFS: /* 0x58465342 */ |
148 |
return "xfs"; |
149 |
case S_MAGIC_HPFS: /* 0xF995E849 */ |
150 |
return "hpfs"; |
151 |
case S_MAGIC_ISOFS: /* 0x9660 */ |
152 |
return "isofs"; |
153 |
case S_MAGIC_ISOFS_WIN: /* 0x4000 */ |
154 |
return "isofs"; |
155 |
case S_MAGIC_ISOFS_R_WIN: /* 0x4004 */ |
156 |
return "isofs"; |
157 |
case S_MAGIC_MINIX: /* 0x137F */ |
158 |
return "minix"; |
159 |
case S_MAGIC_MINIX_30: /* 0x138F */ |
160 |
return "minix (30 char.)"; |
161 |
case S_MAGIC_MINIX_V2: /* 0x2468 */ |
162 |
return "minix v2"; |
163 |
case S_MAGIC_MINIX_V2_30: /* 0x2478 */ |
164 |
return "minix v2 (30 char.)"; |
165 |
case S_MAGIC_MSDOS: /* 0x4d44 */ |
166 |
return "msdos"; |
167 |
case S_MAGIC_FAT: /* 0x4006 */ |
168 |
return "fat"; |
169 |
case S_MAGIC_NCP: /* 0x564c */ |
170 |
return "novell"; |
171 |
case S_MAGIC_NFS: /* 0x6969 */ |
172 |
return "nfs"; |
173 |
case S_MAGIC_PROC: /* 0x9fa0 */ |
174 |
return "proc"; |
175 |
case S_MAGIC_SMB: /* 0x517B */ |
176 |
return "smb"; |
177 |
case S_MAGIC_XENIX: /* 0x012FF7B4 */ |
178 |
return "xenix"; |
179 |
case S_MAGIC_SYSV4: /* 0x012FF7B5 */ |
180 |
return "sysv4"; |
181 |
case S_MAGIC_SYSV2: /* 0x012FF7B6 */ |
182 |
return "sysv2"; |
183 |
case S_MAGIC_COH: /* 0x012FF7B7 */ |
184 |
return "coh"; |
185 |
case S_MAGIC_UFS: /* 0x00011954 */ |
186 |
return "ufs"; |
187 |
case S_MAGIC_XIAFS: /* 0x012FD16D */ |
188 |
return "xia"; |
189 |
case S_MAGIC_NTFS: /* 0x5346544e */ |
190 |
return "ntfs"; |
191 |
case S_MAGIC_TMPFS: /* 0x1021994 */ |
192 |
return "tmpfs"; |
193 |
case S_MAGIC_REISERFS: /* 0x52654973 */ |
194 |
return "reiserfs"; |
195 |
case S_MAGIC_CRAMFS: /* 0x28cd3d45 */ |
196 |
return "cramfs"; |
197 |
case S_MAGIC_ROMFS: /* 0x7275 */ |
198 |
return "romfs"; |
199 |
case S_MAGIC_RAMFS: /* 0x858458f6 */ |
200 |
return "ramfs"; |
201 |
case S_MAGIC_SQUASHFS: /* 0x73717368 */ |
202 |
return "squashfs"; |
203 |
case S_MAGIC_SYSFS: /* 0x62656572 */ |
204 |
return "sysfs"; |
205 |
# elif __GNU__ |
206 |
case FSTYPE_UFS: |
207 |
return "ufs"; |
208 |
case FSTYPE_NFS: |
209 |
return "nfs"; |
210 |
case FSTYPE_GFS: |
211 |
return "gfs"; |
212 |
case FSTYPE_LFS: |
213 |
return "lfs"; |
214 |
case FSTYPE_SYSV: |
215 |
return "sysv"; |
216 |
case FSTYPE_FTP: |
217 |
return "ftp"; |
218 |
case FSTYPE_TAR: |
219 |
return "tar"; |
220 |
case FSTYPE_AR: |
221 |
return "ar"; |
222 |
case FSTYPE_CPIO: |
223 |
return "cpio"; |
224 |
case FSTYPE_MSLOSS: |
225 |
return "msloss"; |
226 |
case FSTYPE_CPM: |
227 |
return "cpm"; |
228 |
case FSTYPE_HFS: |
229 |
return "hfs"; |
230 |
case FSTYPE_DTFS: |
231 |
return "dtfs"; |
232 |
case FSTYPE_GRFS: |
233 |
return "grfs"; |
234 |
case FSTYPE_TERM: |
235 |
return "term"; |
236 |
case FSTYPE_DEV: |
237 |
return "dev"; |
238 |
case FSTYPE_PROC: |
239 |
return "proc"; |
240 |
case FSTYPE_IFSOCK: |
241 |
return "ifsock"; |
242 |
case FSTYPE_AFS: |
243 |
return "afs"; |
244 |
case FSTYPE_DFS: |
245 |
return "dfs"; |
246 |
case FSTYPE_PROC9: |
247 |
return "proc9"; |
248 |
case FSTYPE_SOCKET: |
249 |
return "socket"; |
250 |
case FSTYPE_MISC: |
251 |
return "misc"; |
252 |
case FSTYPE_EXT2FS: |
253 |
return "ext2/ext3"; |
254 |
case FSTYPE_HTTP: |
255 |
return "http"; |
256 |
case FSTYPE_MEMFS: |
257 |
return "memfs"; |
258 |
case FSTYPE_ISO9660: |
259 |
return "iso9660"; |
260 |
# endif |
261 |
default: |
262 |
{ |
263 |
unsigned long int type = statfsbuf->f_type; |
264 |
static char buf[sizeof "UNKNOWN (0x%lx)" - 3 |
265 |
+ (sizeof type * CHAR_BIT + 3) / 4]; |
266 |
sprintf (buf, "UNKNOWN (0x%lx)", type); |
267 |
return buf; |
268 |
} |
269 |
} |
270 |
#endif |
271 |
} |
272 |
|
273 |
static char * |
274 |
human_access (struct stat const *statbuf) |
275 |
{ |
276 |
static char modebuf[11]; |
277 |
mode_string (statbuf->st_mode, modebuf); |
278 |
modebuf[10] = 0; |
279 |
return modebuf; |
280 |
} |
281 |
|
282 |
static char * |
283 |
human_time (time_t t, int t_ns) |
284 |
{ |
285 |
static char str[MAX (INT_BUFSIZE_BOUND (intmax_t), |
286 |
(INT_STRLEN_BOUND (int) /* YYYY */ |
287 |
+ 1 /* because YYYY might equal INT_MAX + 1900 */ |
288 |
+ sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))]; |
289 |
struct tm const *tm = localtime (&t); |
290 |
if (tm == NULL) |
291 |
return (TYPE_SIGNED (time_t) |
292 |
? imaxtostr (t, str) |
293 |
: umaxtostr (t, str)); |
294 |
nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, t_ns); |
295 |
return str; |
296 |
} |
297 |
|
298 |
/* Like strcat, but don't return anything and do check that |
299 |
DEST_BUFSIZE is at least a long as strlen (DEST) + strlen (SRC) + 1. |
300 |
The signature is deliberately different from that of strncat. */ |
301 |
static void |
302 |
xstrcat (char *dest, size_t dest_bufsize, char const *src) |
303 |
{ |
304 |
size_t dest_len = strlen (dest); |
305 |
size_t src_len = strlen (src); |
306 |
if (dest_bufsize < dest_len + src_len + 1) |
307 |
abort (); |
308 |
memcpy (dest + dest_len, src, src_len + 1); |
309 |
} |
310 |
|
311 |
/* print statfs info */ |
312 |
static void |
313 |
print_statfs (char *pformat, size_t buf_len, char m, char const *filename, |
314 |
void const *data) |
315 |
{ |
316 |
STRUCT_STATVFS const *statfsbuf = data; |
317 |
|
318 |
switch (m) |
319 |
{ |
320 |
case 'n': |
321 |
xstrcat (pformat, buf_len, "s"); |
322 |
printf (pformat, filename); |
323 |
break; |
324 |
|
325 |
case 'i': |
326 |
#if HAVE_STRUCT_STATXFS_F_FSID___VAL |
327 |
xstrcat (pformat, buf_len, "x %-8x"); |
328 |
printf (pformat, statfsbuf->f_fsid.__val[0], /* u_long */ |
329 |
statfsbuf->f_fsid.__val[1]); |
330 |
#else |
331 |
xstrcat (pformat, buf_len, "Lx"); |
332 |
printf (pformat, statfsbuf->f_fsid); |
333 |
#endif |
334 |
break; |
335 |
|
336 |
case 'l': |
337 |
xstrcat (pformat, buf_len, NAMEMAX_FORMAT); |
338 |
printf (pformat, SB_F_NAMEMAX (statfsbuf)); |
339 |
break; |
340 |
case 't': |
341 |
#if HAVE_STRUCT_STATXFS_F_TYPE |
342 |
xstrcat (pformat, buf_len, "lx"); |
343 |
printf (pformat, |
344 |
(unsigned long int) (statfsbuf->f_type)); /* no equiv. */ |
345 |
#else |
346 |
fputc ('*', stdout); |
347 |
#endif |
348 |
break; |
349 |
case 'T': |
350 |
xstrcat (pformat, buf_len, "s"); |
351 |
printf (pformat, human_fstype (statfsbuf)); |
352 |
break; |
353 |
case 'b': |
354 |
xstrcat (pformat, buf_len, PRIdMAX); |
355 |
printf (pformat, (intmax_t) (statfsbuf->f_blocks)); |
356 |
break; |
357 |
case 'f': |
358 |
xstrcat (pformat, buf_len, PRIdMAX); |
359 |
printf (pformat, (intmax_t) (statfsbuf->f_bfree)); |
360 |
break; |
361 |
case 'a': |
362 |
xstrcat (pformat, buf_len, PRIdMAX); |
363 |
printf (pformat, (intmax_t) (statfsbuf->f_bavail)); |
364 |
break; |
365 |
case 's': |
366 |
xstrcat (pformat, buf_len, "lu"); |
367 |
printf (pformat, (unsigned long int) (statfsbuf->f_bsize)); |
368 |
break; |
369 |
case 'S': |
370 |
{ |
371 |
unsigned long int frsize = STATFS_FRSIZE (statfsbuf); |
372 |
if (! frsize) |
373 |
frsize = statfsbuf->f_bsize; |
374 |
xstrcat (pformat, buf_len, "lu"); |
375 |
printf (pformat, frsize); |
376 |
} |
377 |
break; |
378 |
case 'c': |
379 |
xstrcat (pformat, buf_len, PRIdMAX); |
380 |
printf (pformat, (intmax_t) (statfsbuf->f_files)); |
381 |
break; |
382 |
case 'd': |
383 |
xstrcat (pformat, buf_len, PRIdMAX); |
384 |
printf (pformat, (intmax_t) (statfsbuf->f_ffree)); |
385 |
break; |
386 |
|
387 |
default: |
388 |
xstrcat (pformat, buf_len, "c"); |
389 |
printf (pformat, m); |
390 |
break; |
391 |
} |
392 |
} |
393 |
|
394 |
/* print stat info */ |
395 |
static void |
396 |
print_stat (char *pformat, size_t buf_len, char m, |
397 |
char const *filename, void const *data) |
398 |
{ |
399 |
struct stat *statbuf = (struct stat *) data; |
400 |
struct passwd *pw_ent; |
401 |
struct group *gw_ent; |
402 |
|
403 |
switch (m) |
404 |
{ |
405 |
case 'n': |
406 |
xstrcat (pformat, buf_len, "s"); |
407 |
printf (pformat, filename); |
408 |
break; |
409 |
case 'N': |
410 |
xstrcat (pformat, buf_len, "s"); |
411 |
if (S_ISLNK (statbuf->st_mode)) |
412 |
{ |
413 |
char *linkname = xreadlink (filename, statbuf->st_size); |
414 |
if (linkname == NULL) |
415 |
{ |
416 |
error (0, errno, _("cannot read symbolic link %s"), |
417 |
quote (filename)); |
418 |
return; |
419 |
} |
420 |
/*printf("\"%s\" -> \"%s\"", filename, linkname); */ |
421 |
printf (pformat, quote (filename)); |
422 |
printf (" -> "); |
423 |
printf (pformat, quote (linkname)); |
424 |
} |
425 |
else |
426 |
{ |
427 |
printf (pformat, quote (filename)); |
428 |
} |
429 |
break; |
430 |
case 'd': |
431 |
xstrcat (pformat, buf_len, PRIuMAX); |
432 |
printf (pformat, (uintmax_t) statbuf->st_dev); |
433 |
break; |
434 |
case 'D': |
435 |
xstrcat (pformat, buf_len, PRIxMAX); |
436 |
printf (pformat, (uintmax_t) statbuf->st_dev); |
437 |
break; |
438 |
case 'i': |
439 |
xstrcat (pformat, buf_len, PRIuMAX); |
440 |
printf (pformat, (uintmax_t) statbuf->st_ino); |
441 |
break; |
442 |
case 'a': |
443 |
xstrcat (pformat, buf_len, "lo"); |
444 |
printf (pformat, |
445 |
(unsigned long int) (statbuf->st_mode & CHMOD_MODE_BITS)); |
446 |
break; |
447 |
case 'A': |
448 |
xstrcat (pformat, buf_len, "s"); |
449 |
printf (pformat, human_access (statbuf)); |
450 |
break; |
451 |
case 'f': |
452 |
xstrcat (pformat, buf_len, "lx"); |
453 |
printf (pformat, (unsigned long int) statbuf->st_mode); |
454 |
break; |
455 |
case 'F': |
456 |
xstrcat (pformat, buf_len, "s"); |
457 |
printf (pformat, file_type (statbuf)); |
458 |
break; |
459 |
case 'h': |
460 |
xstrcat (pformat, buf_len, "lu"); |
461 |
printf (pformat, (unsigned long int) statbuf->st_nlink); |
462 |
break; |
463 |
case 'u': |
464 |
xstrcat (pformat, buf_len, "lu"); |
465 |
printf (pformat, (unsigned long int) statbuf->st_uid); |
466 |
break; |
467 |
case 'U': |
468 |
xstrcat (pformat, buf_len, "s"); |
469 |
setpwent (); |
470 |
pw_ent = getpwuid (statbuf->st_uid); |
471 |
printf (pformat, (pw_ent != 0L) ? pw_ent->pw_name : "UNKNOWN"); |
472 |
break; |
473 |
case 'g': |
474 |
xstrcat (pformat, buf_len, "lu"); |
475 |
printf (pformat, (unsigned long int) statbuf->st_gid); |
476 |
break; |
477 |
case 'G': |
478 |
xstrcat (pformat, buf_len, "s"); |
479 |
setgrent (); |
480 |
gw_ent = getgrgid (statbuf->st_gid); |
481 |
printf (pformat, (gw_ent != 0L) ? gw_ent->gr_name : "UNKNOWN"); |
482 |
break; |
483 |
case 't': |
484 |
xstrcat (pformat, buf_len, "lx"); |
485 |
printf (pformat, (unsigned long int) major (statbuf->st_rdev)); |
486 |
break; |
487 |
case 'T': |
488 |
xstrcat (pformat, buf_len, "lx"); |
489 |
printf (pformat, (unsigned long int) minor (statbuf->st_rdev)); |
490 |
break; |
491 |
case 's': |
492 |
xstrcat (pformat, buf_len, PRIuMAX); |
493 |
printf (pformat, (uintmax_t) (statbuf->st_size)); |
494 |
break; |
495 |
case 'B': |
496 |
xstrcat (pformat, buf_len, "lu"); |
497 |
printf (pformat, (unsigned long int) ST_NBLOCKSIZE); |
498 |
break; |
499 |
case 'b': |
500 |
xstrcat (pformat, buf_len, PRIuMAX); |
501 |
printf (pformat, (uintmax_t) ST_NBLOCKS (*statbuf)); |
502 |
break; |
503 |
case 'o': |
504 |
xstrcat (pformat, buf_len, "lu"); |
505 |
printf (pformat, (unsigned long int) statbuf->st_blksize); |
506 |
break; |
507 |
case 'x': |
508 |
xstrcat (pformat, buf_len, "s"); |
509 |
printf (pformat, human_time (statbuf->st_atime, |
510 |
TIMESPEC_NS (statbuf->st_atim))); |
511 |
break; |
512 |
case 'X': |
513 |
xstrcat (pformat, buf_len, TYPE_SIGNED (time_t) ? "ld" : "lu"); |
514 |
printf (pformat, (unsigned long int) statbuf->st_atime); |
515 |
break; |
516 |
case 'y': |
517 |
xstrcat (pformat, buf_len, "s"); |
518 |
printf (pformat, human_time (statbuf->st_mtime, |
519 |
TIMESPEC_NS (statbuf->st_mtim))); |
520 |
break; |
521 |
case 'Y': |
522 |
xstrcat (pformat, buf_len, TYPE_SIGNED (time_t) ? "ld" : "lu"); |
523 |
printf (pformat, (unsigned long int) statbuf->st_mtime); |
524 |
break; |
525 |
case 'z': |
526 |
xstrcat (pformat, buf_len, "s"); |
527 |
printf (pformat, human_time (statbuf->st_ctime, |
528 |
TIMESPEC_NS (statbuf->st_ctim))); |
529 |
break; |
530 |
case 'Z': |
531 |
xstrcat (pformat, buf_len, TYPE_SIGNED (time_t) ? "ld" : "lu"); |
532 |
printf (pformat, (unsigned long int) statbuf->st_ctime); |
533 |
break; |
534 |
default: |
535 |
xstrcat (pformat, buf_len, "c"); |
536 |
printf (pformat, m); |
537 |
break; |
538 |
} |
539 |
} |
540 |
|
541 |
static void |
542 |
print_it (char const *masterformat, char const *filename, |
543 |
void (*print_func) (char *, size_t, char, char const *, void const *), |
544 |
void const *data) |
545 |
{ |
546 |
char *b; |
547 |
|
548 |
/* create a working copy of the format string */ |
549 |
char *format = xstrdup (masterformat); |
550 |
|
551 |
/* Add 2 to accommodate our conversion of the stat `%s' format string |
552 |
to the printf `%llu' one. */ |
553 |
size_t n_alloc = strlen (format) + 2 + 1; |
554 |
char *dest = xmalloc (n_alloc); |
555 |
|
556 |
b = format; |
557 |
while (b) |
558 |
{ |
559 |
char *p = strchr (b, '%'); |
560 |
if (p != NULL) |
561 |
{ |
562 |
size_t len; |
563 |
*p++ = '\0'; |
564 |
fputs (b, stdout); |
565 |
|
566 |
len = strspn (p, "#-+.I 0123456789"); |
567 |
dest[0] = '%'; |
568 |
memcpy (dest + 1, p, len); |
569 |
dest[1 + len] = 0; |
570 |
p += len; |
571 |
|
572 |
b = p + 1; |
573 |
switch (*p) |
574 |
{ |
575 |
case '\0': |
576 |
b = NULL; |
577 |
/* fall through */ |
578 |
case '%': |
579 |
putchar ('%'); |
580 |
break; |
581 |
default: |
582 |
print_func (dest, n_alloc, *p, filename, data); |
583 |
break; |
584 |
} |
585 |
} |
586 |
else |
587 |
{ |
588 |
fputs (b, stdout); |
589 |
b = NULL; |
590 |
} |
591 |
} |
592 |
free (format); |
593 |
free (dest); |
594 |
} |
595 |
|
596 |
/* Stat the file system and print what we find. */ |
597 |
static bool |
598 |
do_statfs (char const *filename, bool terse, char const *format) |
599 |
{ |
600 |
STRUCT_STATVFS statfsbuf; |
601 |
|
602 |
if (STATFS (filename, &statfsbuf) != 0) |
603 |
{ |
604 |
error (0, errno, _("cannot read file system information for %s"), |
605 |
quote (filename)); |
606 |
return false; |
607 |
} |
608 |
|
609 |
if (format == NULL) |
610 |
{ |
611 |
format = (terse |
612 |
? "%n %i %l %t %s %S %b %f %a %c %d\n" |
613 |
: " File: \"%n\"\n" |
614 |
" ID: %-8i Namelen: %-7l Type: %T\n" |
615 |
"Block size: %-10s Fundamental block size: %S\n" |
616 |
"Blocks: Total: %-10b Free: %-10f Available: %a\n" |
617 |
"Inodes: Total: %-10c Free: %d\n"); |
618 |
} |
619 |
|
620 |
print_it (format, filename, print_statfs, &statfsbuf); |
621 |
return true; |
622 |
} |
623 |
|
624 |
/* stat the file and print what we find */ |
625 |
static bool |
626 |
do_stat (char const *filename, bool follow_links, bool terse, |
627 |
char const *format) |
628 |
{ |
629 |
struct stat statbuf; |
630 |
|
631 |
if ((follow_links ? stat : lstat) (filename, &statbuf) != 0) |
632 |
{ |
633 |
error (0, errno, _("cannot stat %s"), quote (filename)); |
634 |
return false; |
635 |
} |
636 |
|
637 |
if (format == NULL) |
638 |
{ |
639 |
if (terse) |
640 |
{ |
641 |
format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n"; |
642 |
} |
643 |
else |
644 |
{ |
645 |
/* Temporary hack to match original output until conditional |
646 |
implemented. */ |
647 |
if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode)) |
648 |
{ |
649 |
format = |
650 |
" File: %N\n" |
651 |
" Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" |
652 |
"Device: %Dh/%dd\tInode: %-10i Links: %-5h" |
653 |
" Device type: %t,%T\n" |
654 |
"Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" |
655 |
"Access: %x\n" "Modify: %y\n" "Change: %z\n"; |
656 |
} |
657 |
else |
658 |
{ |
659 |
format = |
660 |
" File: %N\n" |
661 |
" Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" |
662 |
"Device: %Dh/%dd\tInode: %-10i Links: %h\n" |
663 |
"Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" |
664 |
"Access: %x\n" "Modify: %y\n" "Change: %z\n"; |
665 |
} |
666 |
} |
667 |
} |
668 |
print_it (format, filename, print_stat, &statbuf); |
669 |
return true; |
670 |
} |
671 |
|
672 |
void |
673 |
usage (int status) |
674 |
{ |
675 |
if (status != EXIT_SUCCESS) |
676 |
fprintf (stderr, _("Try `%s --help' for more information.\n"), |
677 |
program_name); |
678 |
else |
679 |
{ |
680 |
printf (_("Usage: %s [OPTION] FILE...\n"), program_name); |
681 |
fputs (_("\ |
682 |
Display file or file system status.\n\ |
683 |
\n\ |
684 |
-f, --file-system display file system status instead of file status\n\ |
685 |
-c --format=FORMAT use the specified FORMAT instead of the default\n\ |
686 |
-L, --dereference follow links\n\ |
687 |
-t, --terse print the information in terse form\n\ |
688 |
"), stdout); |
689 |
fputs (HELP_OPTION_DESCRIPTION, stdout); |
690 |
fputs (VERSION_OPTION_DESCRIPTION, stdout); |
691 |
|
692 |
fputs (_("\n\ |
693 |
The valid format sequences for files (without --file-system):\n\ |
694 |
\n\ |
695 |
%a Access rights in octal\n\ |
696 |
%A Access rights in human readable form\n\ |
697 |
%b Number of blocks allocated (see %B)\n\ |
698 |
%B The size in bytes of each block reported by %b\n\ |
699 |
"), stdout); |
700 |
fputs (_("\ |
701 |
%d Device number in decimal\n\ |
702 |
%D Device number in hex\n\ |
703 |
%f Raw mode in hex\n\ |
704 |
%F File type\n\ |
705 |
%g Group ID of owner\n\ |
706 |
%G Group name of owner\n\ |
707 |
"), stdout); |
708 |
fputs (_("\ |
709 |
%h Number of hard links\n\ |
710 |
%i Inode number\n\ |
711 |
%n File name\n\ |
712 |
%N Quoted file name with dereference if symbolic link\n\ |
713 |
%o I/O block size\n\ |
714 |
%s Total size, in bytes\n\ |
715 |
%t Major device type in hex\n\ |
716 |
%T Minor device type in hex\n\ |
717 |
"), stdout); |
718 |
fputs (_("\ |
719 |
%u User ID of owner\n\ |
720 |
%U User name of owner\n\ |
721 |
%x Time of last access\n\ |
722 |
%X Time of last access as seconds since Epoch\n\ |
723 |
%y Time of last modification\n\ |
724 |
%Y Time of last modification as seconds since Epoch\n\ |
725 |
%z Time of last change\n\ |
726 |
%Z Time of last change as seconds since Epoch\n\ |
727 |
\n\ |
728 |
"), stdout); |
729 |
|
730 |
fputs (_("\ |
731 |
Valid format sequences for file systems:\n\ |
732 |
\n\ |
733 |
%a Free blocks available to non-superuser\n\ |
734 |
%b Total data blocks in file system\n\ |
735 |
%c Total file nodes in file system\n\ |
736 |
%d Free file nodes in file system\n\ |
737 |
%f Free blocks in file system\n\ |
738 |
"), stdout); |
739 |
fputs (_("\ |
740 |
%i File System ID in hex\n\ |
741 |
%l Maximum length of filenames\n\ |
742 |
%n File name\n\ |
743 |
%s Block size (for faster transfers)\n\ |
744 |
%S Fundamental block size (for block counts)\n\ |
745 |
%t Type in hex\n\ |
746 |
%T Type in human readable form\n\ |
747 |
"), stdout); |
748 |
printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME); |
749 |
printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); |
750 |
} |
751 |
exit (status); |
752 |
} |
753 |
|
754 |
int |
755 |
main (int argc, char *argv[]) |
756 |
{ |
757 |
int c; |
758 |
int i; |
759 |
bool follow_links = false; |
760 |
bool fs = false; |
761 |
bool terse = false; |
762 |
char *format = NULL; |
763 |
bool ok = true; |
764 |
|
765 |
initialize_main (&argc, &argv); |
766 |
program_name = argv[0]; |
767 |
setlocale (LC_ALL, ""); |
768 |
bindtextdomain (PACKAGE, LOCALEDIR); |
769 |
textdomain (PACKAGE); |
770 |
|
771 |
atexit (close_stdout); |
772 |
|
773 |
while ((c = getopt_long (argc, argv, "c:fLlt", long_options, NULL)) != -1) |
774 |
{ |
775 |
switch (c) |
776 |
{ |
777 |
case 'c': |
778 |
format = optarg; |
779 |
break; |
780 |
|
781 |
case 'l': /* deprecated */ |
782 |
error (0, 0, _("Warning: `-l' is deprecated; use `-L' instead")); |
783 |
/* fall through */ |
784 |
case 'L': |
785 |
follow_links = true; |
786 |
break; |
787 |
|
788 |
case 'f': |
789 |
fs = true; |
790 |
break; |
791 |
|
792 |
case 't': |
793 |
terse = true; |
794 |
break; |
795 |
|
796 |
case_GETOPT_HELP_CHAR; |
797 |
|
798 |
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); |
799 |
|
800 |
default: |
801 |
usage (EXIT_FAILURE); |
802 |
} |
803 |
} |
804 |
|
805 |
if (argc == optind) |
806 |
{ |
807 |
error (0, 0, _("missing operand")); |
808 |
usage (EXIT_FAILURE); |
809 |
} |
810 |
|
811 |
for (i = optind; i < argc; i++) |
812 |
ok &= (fs |
813 |
? do_statfs (argv[i], terse, format) |
814 |
: do_stat (argv[i], follow_links, terse, format)); |
815 |
|
816 |
exit (ok ? EXIT_SUCCESS : EXIT_FAILURE); |
817 |
} |