1 /* du -- summarize device usage
2 Copyright (C) 1988-2023 Free Software Foundation, Inc.
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 3 of the License, or
7 (at your option) any later version.
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.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Differences from the Unix du:
18 * Doesn't simply ignore the names of regular files given as arguments
21 By tege@sics.se, Torbjorn Granlund,
22 and djm@ai.mit.edu, David MacKenzie.
23 Variable blocks added by lm@sgi.com and eggert@twinsun.com.
24 Rewritten to use nftw, then to use fts by Jim Meyering. */
28 #include <sys/types.h>
32 #include "argv-iter.h"
37 #include "fprintftime.h"
39 #include "mountlist.h"
41 #include "stat-size.h"
42 #include "stat-time.h"
46 #include "xstrtol-error.h"
48 extern bool fts_debug
;
50 /* The official name of this program (e.g., no 'g' prefix). */
51 #define PROGRAM_NAME "du"
54 proper_name ("Torbjorn Granlund"), \
55 proper_name ("David MacKenzie"), \
56 proper_name ("Paul Eggert"), \
57 proper_name ("Jim Meyering")
60 # define FTS_CROSS_CHECK(Fts) fts_cross_check (Fts)
62 # define FTS_CROSS_CHECK(Fts)
65 /* A set of dev/ino pairs to help identify files and directories
66 whose sizes have already been counted. */
67 static struct di_set
*di_files
;
69 /* A set containing a dev/ino pair for each local mount point directory. */
70 static struct di_set
*di_mnt
;
72 /* Keep track of the preceding "level" (depth in hierarchy)
73 from one call of process_file to the next. */
74 static size_t prev_level
;
76 /* Define a class for collecting directory information. */
79 /* Size of files in directory. */
82 /* Number of inodes in directory. */
85 /* Latest timestamp found. If tmax.tv_sec == TYPE_MINIMUM (time_t)
86 && tmax.tv_nsec < 0, no timestamp has been found. */
90 /* Initialize directory data. */
92 duinfo_init (struct duinfo
*a
)
96 a
->tmax
.tv_sec
= TYPE_MINIMUM (time_t);
100 /* Set directory data. */
102 duinfo_set (struct duinfo
*a
, uintmax_t size
, struct timespec tmax
)
109 /* Accumulate directory data. */
111 duinfo_add (struct duinfo
*a
, struct duinfo
const *b
)
113 uintmax_t sum
= a
->size
+ b
->size
;
114 a
->size
= a
->size
<= sum
? sum
: UINTMAX_MAX
;
115 a
->inodes
= a
->inodes
+ b
->inodes
;
116 if (timespec_cmp (a
->tmax
, b
->tmax
) < 0)
120 /* A structure for per-directory level information. */
123 /* Entries in this directory. */
126 /* Total for subdirectories. */
127 struct duinfo subdir
;
130 /* If true, display counts for all files, not just directories. */
131 static bool opt_all
= false;
133 /* If true, rather than using the device usage of each file,
134 use the apparent size (stat.st_size if usable, 0 otherwise). */
135 static bool apparent_size
= false;
137 /* If true, count each hard link of files with multiple links. */
138 static bool opt_count_all
= false;
140 /* If true, hash all files to look for hard links. */
141 static bool hash_all
;
143 /* If true, output the NUL byte instead of a newline at the end of each line. */
144 static bool opt_nul_terminate_output
= false;
146 /* If true, print a grand total at the end. */
147 static bool print_grand_total
= false;
149 /* If nonzero, do not add sizes of subdirectories. */
150 static bool opt_separate_dirs
= false;
152 /* Show the total for each directory (and file if --all) that is at
153 most MAX_DEPTH levels down from the root of the hierarchy. The root
154 is at level 0, so 'du --max-depth=0' is equivalent to 'du -s'. */
155 static size_t max_depth
= SIZE_MAX
;
157 /* Only output entries with at least this SIZE if positive,
158 or at most if negative. See --threshold option. */
159 static intmax_t opt_threshold
= 0;
161 /* Human-readable options for output. */
162 static int human_output_opts
;
164 /* Output inodes count instead of blocks used. */
165 static bool opt_inodes
= false;
167 /* If true, print most recently modified date, using the specified format. */
168 static bool opt_time
= false;
170 /* Type of time to display. controlled by --time. */
174 time_mtime
, /* default */
179 static enum time_type time_type
= time_mtime
;
181 /* User specified date / time style */
182 static char const *time_style
= NULL
;
184 /* Format used to display date / time. Controlled by --time-style */
185 static char const *time_format
= NULL
;
187 /* The local time zone rules, as per the TZ environment variable. */
188 static timezone_t localtz
;
190 /* The units to use when printing sizes. */
191 static uintmax_t output_block_size
;
193 /* File name patterns to exclude. */
194 static struct exclude
*exclude
;
196 /* Grand total size of all args, in bytes. Also latest modified date. */
197 static struct duinfo tot_dui
;
199 #define IS_DIR_TYPE(Type) \
201 || (Type) == FTS_DNR)
203 /* For long options that have no equivalent short option, use a
204 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
207 APPARENT_SIZE_OPTION
= CHAR_MAX
+ 1,
217 static struct option
const long_options
[] =
219 {"all", no_argument
, NULL
, 'a'},
220 {"apparent-size", no_argument
, NULL
, APPARENT_SIZE_OPTION
},
221 {"block-size", required_argument
, NULL
, 'B'},
222 {"bytes", no_argument
, NULL
, 'b'},
223 {"count-links", no_argument
, NULL
, 'l'},
224 /* {"-debug", no_argument, NULL, FTS_DEBUG}, */
225 {"dereference", no_argument
, NULL
, 'L'},
226 {"dereference-args", no_argument
, NULL
, 'D'},
227 {"exclude", required_argument
, NULL
, EXCLUDE_OPTION
},
228 {"exclude-from", required_argument
, NULL
, 'X'},
229 {"files0-from", required_argument
, NULL
, FILES0_FROM_OPTION
},
230 {"human-readable", no_argument
, NULL
, 'h'},
231 {"inodes", no_argument
, NULL
, INODES_OPTION
},
232 {"si", no_argument
, NULL
, HUMAN_SI_OPTION
},
233 {"max-depth", required_argument
, NULL
, 'd'},
234 {"null", no_argument
, NULL
, '0'},
235 {"no-dereference", no_argument
, NULL
, 'P'},
236 {"one-file-system", no_argument
, NULL
, 'x'},
237 {"separate-dirs", no_argument
, NULL
, 'S'},
238 {"summarize", no_argument
, NULL
, 's'},
239 {"total", no_argument
, NULL
, 'c'},
240 {"threshold", required_argument
, NULL
, 't'},
241 {"time", optional_argument
, NULL
, TIME_OPTION
},
242 {"time-style", required_argument
, NULL
, TIME_STYLE_OPTION
},
243 {GETOPT_HELP_OPTION_DECL
},
244 {GETOPT_VERSION_OPTION_DECL
},
248 static char const *const time_args
[] =
250 "atime", "access", "use", "ctime", "status", NULL
252 static enum time_type
const time_types
[] =
254 time_atime
, time_atime
, time_atime
, time_ctime
, time_ctime
256 ARGMATCH_VERIFY (time_args
, time_types
);
258 /* 'full-iso' uses full ISO-style dates and times. 'long-iso' uses longer
259 ISO-style timestamps, though shorter than 'full-iso'. 'iso' uses shorter
260 ISO-style timestamps. */
263 full_iso_time_style
, /* --time-style=full-iso */
264 long_iso_time_style
, /* --time-style=long-iso */
265 iso_time_style
/* --time-style=iso */
268 static char const *const time_style_args
[] =
270 "full-iso", "long-iso", "iso", NULL
272 static enum time_style
const time_style_types
[] =
274 full_iso_time_style
, long_iso_time_style
, iso_time_style
276 ARGMATCH_VERIFY (time_style_args
, time_style_types
);
281 if (status
!= EXIT_SUCCESS
)
286 Usage: %s [OPTION]... [FILE]...\n\
287 or: %s [OPTION]... --files0-from=F\n\
288 "), program_name
, program_name
);
290 Summarize device usage of the set of FILEs, recursively for directories.\n\
293 emit_mandatory_arg_note ();
296 -0, --null end each output line with NUL, not newline\n\
297 -a, --all write counts for all files, not just directories\n\
298 --apparent-size print apparent sizes rather than device usage; although\
300 the apparent size is usually smaller, it may be\n\
301 larger due to holes in ('sparse') files, internal\n\
302 fragmentation, indirect blocks, and the like\n\
305 -B, --block-size=SIZE scale sizes by SIZE before printing them; e.g.,\n\
306 '-BM' prints sizes in units of 1,048,576 bytes;\n\
307 see SIZE format below\n\
308 -b, --bytes equivalent to '--apparent-size --block-size=1'\n\
309 -c, --total produce a grand total\n\
310 -D, --dereference-args dereference only symlinks that are listed on the\n\
312 -d, --max-depth=N print the total for a directory (or file, with --all)\n\
313 only if it is N or fewer levels below the command\n\
314 line argument; --max-depth=0 is the same as\n\
318 --files0-from=F summarize device usage of the\n\
319 NUL-terminated file names specified in file F;\n\
320 if F is -, then read names from standard input\n\
321 -H equivalent to --dereference-args (-D)\n\
322 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\
324 --inodes list inode usage information instead of block usage\n\
327 -k like --block-size=1K\n\
328 -L, --dereference dereference all symbolic links\n\
329 -l, --count-links count sizes many times if hard linked\n\
330 -m like --block-size=1M\n\
333 -P, --no-dereference don't follow any symbolic links (this is the default)\n\
334 -S, --separate-dirs for directories do not include size of subdirectories\n\
335 --si like -h, but use powers of 1000 not 1024\n\
336 -s, --summarize display only a total for each argument\n\
339 -t, --threshold=SIZE exclude entries smaller than SIZE if positive,\n\
340 or entries greater than SIZE if negative\n\
341 --time show time of the last modification of any file in the\n\
342 directory, or any of its subdirectories\n\
343 --time=WORD show time as WORD instead of modification time:\n\
344 atime, access, use, ctime or status\n\
345 --time-style=STYLE show times using STYLE, which can be:\n\
346 full-iso, long-iso, iso, or +FORMAT;\n\
347 FORMAT is interpreted like in 'date'\n\
350 -X, --exclude-from=FILE exclude files that match any pattern in FILE\n\
351 --exclude=PATTERN exclude files that match PATTERN\n\
352 -x, --one-file-system skip directories on different file systems\n\
354 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
355 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
356 emit_blocksize_note ("DU");
358 emit_ancillary_info (PROGRAM_NAME
);
363 /* Try to insert the INO/DEV pair into DI_SET.
364 Return true if the pair is successfully inserted,
365 false if the pair was already there. */
367 hash_ins (struct di_set
*di_set
, ino_t ino
, dev_t dev
)
369 int inserted
= di_set_insert (di_set
, dev
, ino
);
375 /* FIXME: this code is nearly identical to code in date.c */
376 /* Display the date and time in WHEN according to the format specified
380 show_date (char const *format
, struct timespec when
, timezone_t tz
)
383 if (localtime_rz (tz
, &when
.tv_sec
, &tm
))
384 fprintftime (stdout
, format
, &tm
, tz
, when
.tv_nsec
);
387 char buf
[INT_BUFSIZE_BOUND (intmax_t)];
388 char *when_str
= timetostr (when
.tv_sec
, buf
);
389 error (0, 0, _("time %s is out of range"), quote (when_str
));
390 fputs (when_str
, stdout
);
394 /* Print N_BYTES. Convert it to a readable value before printing. */
397 print_only_size (uintmax_t n_bytes
)
399 char buf
[LONGEST_HUMAN_READABLE
+ 1];
400 fputs ((n_bytes
== UINTMAX_MAX
402 : human_readable (n_bytes
, buf
, human_output_opts
,
403 1, output_block_size
)),
407 /* Print size (and optionally time) indicated by *PDUI, followed by STRING. */
410 print_size (const struct duinfo
*pdui
, char const *string
)
412 print_only_size (opt_inodes
419 show_date (time_format
, pdui
->tmax
, localtz
);
421 printf ("\t%s%c", string
, opt_nul_terminate_output
? '\0' : '\n');
425 /* Fill the di_mnt set with local mount point dev/ino pairs. */
428 fill_mount_table (void)
430 struct mount_entry
*mnt_ent
= read_file_system_list (false);
433 struct mount_entry
*mnt_free
;
434 if (!mnt_ent
->me_remote
&& !mnt_ent
->me_dummy
)
437 if (!stat (mnt_ent
->me_mountdir
, &buf
))
438 hash_ins (di_mnt
, buf
.st_ino
, buf
.st_dev
);
441 /* Ignore stat failure. False positives are too common.
442 E.g., "Permission denied" on /run/user/<name>/gvfs. */
447 mnt_ent
= mnt_ent
->me_next
;
448 free_mount_entry (mnt_free
);
452 /* This function checks whether any of the directories in the cycle that
453 fts detected is a mount point. */
456 mount_point_in_fts_cycle (FTSENT
const *ent
)
458 FTSENT
const *cycle_ent
= ent
->fts_cycle
;
462 /* Initialize the set of dev,inode pairs. */
463 di_mnt
= di_set_alloc ();
470 while (ent
&& ent
!= cycle_ent
)
472 if (di_set_lookup (di_mnt
, ent
->fts_statp
->st_dev
,
473 ent
->fts_statp
->st_ino
) > 0)
477 ent
= ent
->fts_parent
;
483 /* This function is called once for every file system object that fts
484 encounters. fts does a depth-first traversal. This function knows
485 that and accumulates per-directory totals based on changes in
486 the depth of the current entry. It returns true on success. */
489 process_file (FTS
*fts
, FTSENT
*ent
)
493 struct duinfo dui_to_print
;
495 static size_t n_alloc
;
496 /* First element of the structure contains:
497 The sum of the sizes of all entries in the single directory
498 at the corresponding level. Although this does include the sizes
499 corresponding to each subdirectory, it does not include the size of
500 any file in a subdirectory. Also corresponding last modified date.
501 Second element of the structure contains:
502 The sum of the sizes of all entries in the hierarchy at or below the
503 directory at the specified level. */
504 static struct dulevel
*dulvl
;
506 char const *file
= ent
->fts_path
;
507 const struct stat
*sb
= ent
->fts_statp
;
508 int info
= ent
->fts_info
;
512 /* An error occurred, but the size is known, so count it. */
513 error (0, ent
->fts_errno
, _("cannot read directory %s"), quoteaf (file
));
516 else if (info
!= FTS_DP
)
518 bool excluded
= excluded_file_name (exclude
, file
);
521 /* Make the stat buffer *SB valid, or fail noisily. */
523 if (info
== FTS_NSOK
)
525 fts_set (fts
, ent
, FTS_AGAIN
);
526 FTSENT
const *e
= fts_read (fts
);
528 info
= ent
->fts_info
;
531 if (info
== FTS_NS
|| info
== FTS_SLNONE
)
533 error (0, ent
->fts_errno
, _("cannot access %s"), quoteaf (file
));
537 /* The --one-file-system (-x) option cannot exclude anything
538 specified on the command-line. By definition, it can exclude
539 a file or directory only when its device number is different
540 from that of its just-processed parent directory, and du does
541 not process the parent of a command-line argument. */
542 if (fts
->fts_options
& FTS_XDEV
543 && FTS_ROOTLEVEL
< ent
->fts_level
544 && fts
->fts_dev
!= sb
->st_dev
)
550 && (hash_all
|| (! S_ISDIR (sb
->st_mode
) && 1 < sb
->st_nlink
))
551 && ! hash_ins (di_files
, sb
->st_ino
, sb
->st_dev
)))
553 /* If ignoring a directory in preorder, skip its children.
554 Ignore the next fts_read output too, as it's a postorder
555 visit to the same directory. */
558 fts_set (fts
, ent
, FTS_SKIP
);
559 FTSENT
const *e
= fts_read (fts
);
572 /* An error occurred, but the size is known, so count it. */
573 error (0, ent
->fts_errno
, "%s", quotef (file
));
578 /* If not following symlinks and not a (bind) mount point. */
579 if (cycle_warning_required (fts
, ent
)
580 && ! mount_point_in_fts_cycle (ent
))
582 emit_cycle_warning (file
);
591 ? (usable_st_size (sb
) ? MAX (0, sb
->st_size
) : 0)
592 : (uintmax_t) ST_NBLOCKS (*sb
) * ST_NBLOCKSIZE
),
593 (time_type
== time_mtime
? get_stat_mtime (sb
)
594 : time_type
== time_atime
? get_stat_atime (sb
)
595 : get_stat_ctime (sb
)));
597 level
= ent
->fts_level
;
602 n_alloc
= level
+ 10;
603 dulvl
= xcalloc (n_alloc
, sizeof *dulvl
);
607 if (level
== prev_level
)
609 /* This is usually the most common case. Do nothing. */
611 else if (level
> prev_level
)
613 /* Descending the hierarchy.
614 Clear the accumulators for *all* levels between prev_level
615 and the current one. The depth may change dramatically,
616 e.g., from 1 to 10. */
618 if (n_alloc
<= level
)
620 dulvl
= xnrealloc (dulvl
, level
, 2 * sizeof *dulvl
);
624 for (size_t i
= prev_level
+ 1; i
<= level
; i
++)
626 duinfo_init (&dulvl
[i
].ent
);
627 duinfo_init (&dulvl
[i
].subdir
);
630 else /* level < prev_level */
632 /* Ascending the hierarchy.
633 Process a directory only after all entries in that
634 directory have been processed. When the depth decreases,
635 propagate sums from the children (prev_level) to the parent.
636 Here, the current level is always one smaller than the
638 assert (level
== prev_level
- 1);
639 duinfo_add (&dui_to_print
, &dulvl
[prev_level
].ent
);
640 if (!opt_separate_dirs
)
641 duinfo_add (&dui_to_print
, &dulvl
[prev_level
].subdir
);
642 duinfo_add (&dulvl
[level
].subdir
, &dulvl
[prev_level
].ent
);
643 duinfo_add (&dulvl
[level
].subdir
, &dulvl
[prev_level
].subdir
);
649 /* Let the size of a directory entry contribute to the total for the
650 containing directory, unless --separate-dirs (-S) is specified. */
651 if (! (opt_separate_dirs
&& IS_DIR_TYPE (info
)))
652 duinfo_add (&dulvl
[level
].ent
, &dui
);
654 /* Even if this directory is unreadable or we can't chdir into it,
655 do let its size contribute to the total. */
656 duinfo_add (&tot_dui
, &dui
);
658 if ((IS_DIR_TYPE (info
) && level
<= max_depth
)
659 || (opt_all
&& level
<= max_depth
)
662 /* Print or elide this entry according to the --threshold option. */
663 uintmax_t v
= opt_inodes
? dui_to_print
.inodes
: dui_to_print
.size
;
664 if (opt_threshold
< 0
665 ? v
<= -opt_threshold
666 : v
>= opt_threshold
)
667 print_size (&dui_to_print
, file
);
673 /* Recursively print the sizes of the directories (and, if selected, files)
674 named in FILES, the last entry of which is NULL.
675 BIT_FLAGS controls how fts works.
676 Return true if successful. */
679 du_files (char **files
, int bit_flags
)
685 FTS
*fts
= xfts_open (files
, bit_flags
, NULL
);
691 ent
= fts_read (fts
);
696 error (0, errno
, _("fts_read failed: %s"),
697 quotef (fts
->fts_path
));
701 /* When exiting this loop early, be careful to reset the
702 global, prev_level, used in process_file. Otherwise, its
703 (level == prev_level - 1) assertion could fail. */
707 FTS_CROSS_CHECK (fts
);
709 ok
&= process_file (fts
, ent
);
712 if (fts_close (fts
) != 0)
714 error (0, errno
, _("fts_close failed"));
723 main (int argc
, char **argv
)
726 bool max_depth_specified
= false;
728 char *files_from
= NULL
;
730 /* Bit flags that control how fts works. */
731 int bit_flags
= FTS_NOSTAT
;
733 /* Select one of the three FTS_ options that control if/when
734 to follow a symlink. */
735 int symlink_deref_bits
= FTS_PHYSICAL
;
737 /* If true, display only a total for each argument. */
738 bool opt_summarize_only
= false;
740 cwd_only
[0] = bad_cast (".");
743 initialize_main (&argc
, &argv
);
744 set_program_name (argv
[0]);
745 setlocale (LC_ALL
, "");
746 bindtextdomain (PACKAGE
, LOCALEDIR
);
747 textdomain (PACKAGE
);
749 atexit (close_stdout
);
751 exclude
= new_exclude ();
753 human_options (getenv ("DU_BLOCK_SIZE"),
754 &human_output_opts
, &output_block_size
);
759 int c
= getopt_long (argc
, argv
, "0abd:chHklmst:xB:DLPSX:",
773 opt_nul_terminate_output
= true;
780 case APPARENT_SIZE_OPTION
:
781 apparent_size
= true;
785 apparent_size
= true;
786 human_output_opts
= 0;
787 output_block_size
= 1;
791 print_grand_total
= true;
795 human_output_opts
= human_autoscale
| human_SI
| human_base_1024
;
796 output_block_size
= 1;
799 case HUMAN_SI_OPTION
:
800 human_output_opts
= human_autoscale
| human_SI
;
801 output_block_size
= 1;
805 human_output_opts
= 0;
806 output_block_size
= 1024;
809 case 'd': /* --max-depth=N */
812 if (xstrtoumax (optarg
, NULL
, 0, &tmp
, "") == LONGINT_OK
815 max_depth_specified
= true;
820 error (0, 0, _("invalid maximum depth %s"),
828 human_output_opts
= 0;
829 output_block_size
= 1024 * 1024;
833 opt_count_all
= true;
837 opt_summarize_only
= true;
843 e
= xstrtoimax (optarg
, NULL
, 0, &opt_threshold
, "kKmMGTPEZYRQ0");
845 xstrtol_fatal (e
, oi
, c
, long_options
, optarg
);
846 if (opt_threshold
== 0 && *optarg
== '-')
848 /* Do not allow -0, as this wouldn't make sense anyway. */
849 die (EXIT_FAILURE
, 0, _("invalid --threshold argument '-0'"));
855 bit_flags
|= FTS_XDEV
;
860 enum strtol_error e
= human_options (optarg
, &human_output_opts
,
863 xstrtol_fatal (e
, oi
, c
, long_options
, optarg
);
867 case 'H': /* NOTE: before 2008-12, -H was equivalent to --si. */
869 symlink_deref_bits
= FTS_COMFOLLOW
| FTS_PHYSICAL
;
872 case 'L': /* --dereference */
873 symlink_deref_bits
= FTS_LOGICAL
;
876 case 'P': /* --no-dereference */
877 symlink_deref_bits
= FTS_PHYSICAL
;
881 opt_separate_dirs
= true;
885 if (add_exclude_file (add_exclude
, exclude
, optarg
,
886 EXCLUDE_WILDCARDS
, '\n'))
888 error (0, errno
, "%s", quotef (optarg
));
893 case FILES0_FROM_OPTION
:
898 add_exclude (exclude
, optarg
, EXCLUDE_WILDCARDS
);
909 ? XARGMATCH ("--time", optarg
, time_args
, time_types
)
911 localtz
= tzalloc (getenv ("TZ"));
914 case TIME_STYLE_OPTION
:
918 case_GETOPT_HELP_CHAR
;
920 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
928 usage (EXIT_FAILURE
);
930 if (opt_all
&& opt_summarize_only
)
932 error (0, 0, _("cannot both summarize and show all entries"));
933 usage (EXIT_FAILURE
);
936 if (opt_summarize_only
&& max_depth_specified
&& max_depth
== 0)
939 _("warning: summarizing is the same as using --max-depth=0"));
942 if (opt_summarize_only
&& max_depth_specified
&& max_depth
!= 0)
944 unsigned long int d
= max_depth
;
945 error (0, 0, _("warning: summarizing conflicts with --max-depth=%lu"), d
);
946 usage (EXIT_FAILURE
);
949 if (opt_summarize_only
)
956 error (0, 0, _("warning: options --apparent-size and -b are "
957 "ineffective with --inodes"));
959 output_block_size
= 1;
962 /* Process time style if printing last times. */
967 time_style
= getenv ("TIME_STYLE");
969 /* Ignore TIMESTYLE="locale", for compatibility with ls. */
970 if (! time_style
|| STREQ (time_style
, "locale"))
971 time_style
= "long-iso";
972 else if (*time_style
== '+')
974 /* Ignore anything after a newline, for compatibility
976 char *p
= strchr (time_style
, '\n');
982 /* Ignore "posix-" prefix, for compatibility with ls. */
983 static char const posix_prefix
[] = "posix-";
984 static const size_t prefix_len
= sizeof posix_prefix
- 1;
985 while (STREQ_LEN (time_style
, posix_prefix
, prefix_len
))
986 time_style
+= prefix_len
;
990 if (*time_style
== '+')
991 time_format
= time_style
+ 1;
994 switch (XARGMATCH ("time style", time_style
,
995 time_style_args
, time_style_types
))
997 case full_iso_time_style
:
998 time_format
= "%Y-%m-%d %H:%M:%S.%N %z";
1001 case long_iso_time_style
:
1002 time_format
= "%Y-%m-%d %H:%M";
1005 case iso_time_style
:
1006 time_format
= "%Y-%m-%d";
1012 struct argv_iterator
*ai
;
1015 /* When using --files0-from=F, you may not specify any files
1016 on the command-line. */
1019 error (0, 0, _("extra operand %s"), quote (argv
[optind
]));
1020 fprintf (stderr
, "%s\n",
1021 _("file operands cannot be combined with --files0-from"));
1022 usage (EXIT_FAILURE
);
1025 if (! (STREQ (files_from
, "-") || freopen (files_from
, "r", stdin
)))
1026 die (EXIT_FAILURE
, errno
, _("cannot open %s for reading"),
1027 quoteaf (files_from
));
1029 ai
= argv_iter_init_stream (stdin
);
1031 /* It's not easy here to count the arguments, so assume the
1037 char **files
= (optind
< argc
? argv
+ optind
: cwd_only
);
1038 ai
= argv_iter_init_argv (files
);
1040 /* Hash all dev,ino pairs if there are multiple arguments, or if
1041 following non-command-line symlinks, because in either case a
1042 file with just one hard link might be seen more than once. */
1043 hash_all
= (optind
+ 1 < argc
|| symlink_deref_bits
== FTS_LOGICAL
);
1049 /* Initialize the set of dev,inode pairs. */
1050 di_files
= di_set_alloc ();
1054 /* If not hashing everything, process_file won't find cycles on its
1055 own, so ask fts_read to check for them accurately. */
1056 if (opt_count_all
|| ! hash_all
)
1057 bit_flags
|= FTS_TIGHT_CYCLE_CHECK
;
1059 bit_flags
|= symlink_deref_bits
;
1060 static char *temp_argv
[] = { NULL
, NULL
};
1064 bool skip_file
= false;
1065 enum argv_iter_err ai_err
;
1066 char *file_name
= argv_iter (ai
, &ai_err
);
1072 goto argv_iter_done
;
1074 error (0, errno
, _("%s: read error"),
1075 quotef (files_from
));
1077 goto argv_iter_done
;
1081 assert (!"unexpected error code from argv_iter");
1084 if (files_from
&& STREQ (files_from
, "-") && STREQ (file_name
, "-"))
1086 /* Give a better diagnostic in an unusual case:
1087 printf - | du --files0-from=- */
1088 error (0, 0, _("when reading file names from stdin, "
1089 "no file name of %s allowed"),
1090 quoteaf (file_name
));
1094 /* Report and skip any empty file names before invoking fts.
1095 This works around a glitch in fts, which fails immediately
1096 (without looking at the other file names) when given an empty
1100 /* Diagnose a zero-length file name. When it's one
1101 among many, knowing the record number may help.
1102 FIXME: currently print the record number only with
1103 --files0-from=FILE. Maybe do it for argv, too? */
1104 if (files_from
== NULL
)
1105 error (0, 0, "%s", _("invalid zero-length file name"));
1108 /* Using the standard 'filename:line-number:' prefix here is
1109 not totally appropriate, since NUL is the separator, not NL,
1110 but it might be better than nothing. */
1111 unsigned long int file_number
= argv_iter_n_args (ai
);
1112 error (0, 0, "%s:%lu: %s", quotef (files_from
),
1113 file_number
, _("invalid zero-length file name"));
1122 temp_argv
[0] = file_name
;
1123 ok
&= du_files (temp_argv
, bit_flags
);
1128 argv_iter_free (ai
);
1129 di_set_free (di_files
);
1131 di_set_free (di_mnt
);
1133 if (files_from
&& (ferror (stdin
) || fclose (stdin
) != 0) && ok
)
1134 die (EXIT_FAILURE
, 0, _("error reading %s"), quoteaf (files_from
));
1136 if (print_grand_total
)
1137 print_size (&tot_dui
, _("total"));
1139 return ok
? EXIT_SUCCESS
: EXIT_FAILURE
;