du: --apparent counts only symlinks and regular
[coreutils.git] / src / du.c
blob025a587d791c3392922a54b172e3e635dbdff85e
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
19 when -a is given.
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. */
26 #include <config.h>
27 #include <getopt.h>
28 #include <sys/types.h>
29 #include <assert.h>
30 #include "system.h"
31 #include "argmatch.h"
32 #include "argv-iter.h"
33 #include "di-set.h"
34 #include "die.h"
35 #include "error.h"
36 #include "exclude.h"
37 #include "fprintftime.h"
38 #include "human.h"
39 #include "mountlist.h"
40 #include "quote.h"
41 #include "stat-size.h"
42 #include "stat-time.h"
43 #include "stdio--.h"
44 #include "xfts.h"
45 #include "xstrtol.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"
53 #define AUTHORS \
54 proper_name ("Torbjorn Granlund"), \
55 proper_name ("David MacKenzie"), \
56 proper_name ("Paul Eggert"), \
57 proper_name ("Jim Meyering")
59 #if DU_DEBUG
60 # define FTS_CROSS_CHECK(Fts) fts_cross_check (Fts)
61 #else
62 # define FTS_CROSS_CHECK(Fts)
63 #endif
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. */
77 struct duinfo
79 /* Size of files in directory. */
80 uintmax_t size;
82 /* Number of inodes in directory. */
83 uintmax_t inodes;
85 /* Latest timestamp found. If tmax.tv_sec == TYPE_MINIMUM (time_t)
86 && tmax.tv_nsec < 0, no timestamp has been found. */
87 struct timespec tmax;
90 /* Initialize directory data. */
91 static inline void
92 duinfo_init (struct duinfo *a)
94 a->size = 0;
95 a->inodes = 0;
96 a->tmax.tv_sec = TYPE_MINIMUM (time_t);
97 a->tmax.tv_nsec = -1;
100 /* Set directory data. */
101 static inline void
102 duinfo_set (struct duinfo *a, uintmax_t size, struct timespec tmax)
104 a->size = size;
105 a->inodes = 1;
106 a->tmax = tmax;
109 /* Accumulate directory data. */
110 static inline void
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)
117 a->tmax = b->tmax;
120 /* A structure for per-directory level information. */
121 struct dulevel
123 /* Entries in this directory. */
124 struct duinfo ent;
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. */
172 enum time_type
174 time_mtime, /* default */
175 time_ctime,
176 time_atime
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) \
200 ((Type) == FTS_DP \
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. */
205 enum
207 APPARENT_SIZE_OPTION = CHAR_MAX + 1,
208 EXCLUDE_OPTION,
209 FILES0_FROM_OPTION,
210 HUMAN_SI_OPTION,
211 FTS_DEBUG,
212 TIME_OPTION,
213 TIME_STYLE_OPTION,
214 INODES_OPTION
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},
245 {NULL, 0, NULL, 0}
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. */
261 enum time_style
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);
278 void
279 usage (int status)
281 if (status != EXIT_SUCCESS)
282 emit_try_help ();
283 else
285 printf (_("\
286 Usage: %s [OPTION]... [FILE]...\n\
287 or: %s [OPTION]... --files0-from=F\n\
288 "), program_name, program_name);
289 fputs (_("\
290 Summarize device usage of the set of FILEs, recursively for directories.\n\
291 "), stdout);
293 emit_mandatory_arg_note ();
295 fputs (_("\
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\
303 "), stdout);
304 fputs (_("\
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\
311 command line\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\
315 --summarize\n\
316 "), stdout);
317 fputs (_("\
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\
325 "), stdout);
326 fputs (_("\
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\
331 "), stdout);
332 fputs (_("\
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\
337 "), stdout);
338 fputs (_("\
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\
348 "), stdout);
349 fputs (_("\
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\
353 "), stdout);
354 fputs (HELP_OPTION_DESCRIPTION, stdout);
355 fputs (VERSION_OPTION_DESCRIPTION, stdout);
356 emit_blocksize_note ("DU");
357 emit_size_note ();
358 emit_ancillary_info (PROGRAM_NAME);
360 exit (status);
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. */
366 static bool
367 hash_ins (struct di_set *di_set, ino_t ino, dev_t dev)
369 int inserted = di_set_insert (di_set, dev, ino);
370 if (inserted < 0)
371 xalloc_die ();
372 return inserted;
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
377 in FORMAT. */
379 static void
380 show_date (char const *format, struct timespec when, timezone_t tz)
382 struct tm tm;
383 if (localtime_rz (tz, &when.tv_sec, &tm))
384 fprintftime (stdout, format, &tm, tz, when.tv_nsec);
385 else
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. */
396 static void
397 print_only_size (uintmax_t n_bytes)
399 char buf[LONGEST_HUMAN_READABLE + 1];
400 fputs ((n_bytes == UINTMAX_MAX
401 ? _("Infinity")
402 : human_readable (n_bytes, buf, human_output_opts,
403 1, output_block_size)),
404 stdout);
407 /* Print size (and optionally time) indicated by *PDUI, followed by STRING. */
409 static void
410 print_size (const struct duinfo *pdui, char const *string)
412 print_only_size (opt_inodes
413 ? pdui->inodes
414 : pdui->size);
416 if (opt_time)
418 putchar ('\t');
419 show_date (time_format, pdui->tmax, localtz);
421 printf ("\t%s%c", string, opt_nul_terminate_output ? '\0' : '\n');
422 fflush (stdout);
425 /* Fill the di_mnt set with local mount point dev/ino pairs. */
427 static void
428 fill_mount_table (void)
430 struct mount_entry *mnt_ent = read_file_system_list (false);
431 while (mnt_ent)
433 struct mount_entry *mnt_free;
434 if (!mnt_ent->me_remote && !mnt_ent->me_dummy)
436 struct stat buf;
437 if (!stat (mnt_ent->me_mountdir, &buf))
438 hash_ins (di_mnt, buf.st_ino, buf.st_dev);
439 else
441 /* Ignore stat failure. False positives are too common.
442 E.g., "Permission denied" on /run/user/<name>/gvfs. */
446 mnt_free = mnt_ent;
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. */
455 static bool
456 mount_point_in_fts_cycle (FTSENT const *ent)
458 FTSENT const *cycle_ent = ent->fts_cycle;
460 if (!di_mnt)
462 /* Initialize the set of dev,inode pairs. */
463 di_mnt = di_set_alloc ();
464 if (!di_mnt)
465 xalloc_die ();
467 fill_mount_table ();
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)
475 return true;
477 ent = ent->fts_parent;
480 return false;
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. */
488 static bool
489 process_file (FTS *fts, FTSENT *ent)
491 bool ok = true;
492 struct duinfo dui;
493 struct duinfo dui_to_print;
494 size_t level;
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;
510 if (info == FTS_DNR)
512 /* An error occurred, but the size is known, so count it. */
513 error (0, ent->fts_errno, _("cannot read directory %s"), quoteaf (file));
514 ok = false;
516 else if (info != FTS_DP)
518 bool excluded = excluded_file_name (exclude, file);
519 if (! excluded)
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);
527 assert (e == ent);
528 info = ent->fts_info;
531 if (info == FTS_NS || info == FTS_SLNONE)
533 error (0, ent->fts_errno, _("cannot access %s"), quoteaf (file));
534 return false;
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)
545 excluded = true;
548 if (excluded
549 || (! opt_count_all
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. */
556 if (info == FTS_D)
558 fts_set (fts, ent, FTS_SKIP);
559 FTSENT const *e = fts_read (fts);
560 assert (e == ent);
563 return true;
566 switch (info)
568 case FTS_D:
569 return true;
571 case FTS_ERR:
572 /* An error occurred, but the size is known, so count it. */
573 error (0, ent->fts_errno, "%s", quotef (file));
574 ok = false;
575 break;
577 case FTS_DC:
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);
583 return false;
585 return true;
589 duinfo_set (&dui,
590 (apparent_size
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;
598 dui_to_print = dui;
600 if (n_alloc == 0)
602 n_alloc = level + 10;
603 dulvl = xcalloc (n_alloc, sizeof *dulvl);
605 else
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);
621 n_alloc = level * 2;
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
637 previous one. */
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);
647 prev_level = level;
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)
660 || level == 0)
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);
670 return ok;
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. */
678 static bool
679 du_files (char **files, int bit_flags)
681 bool ok = true;
683 if (*files)
685 FTS *fts = xfts_open (files, bit_flags, NULL);
687 while (true)
689 FTSENT *ent;
691 ent = fts_read (fts);
692 if (ent == NULL)
694 if (errno != 0)
696 error (0, errno, _("fts_read failed: %s"),
697 quotef (fts->fts_path));
698 ok = false;
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. */
704 prev_level = 0;
705 break;
707 FTS_CROSS_CHECK (fts);
709 ok &= process_file (fts, ent);
712 if (fts_close (fts) != 0)
714 error (0, errno, _("fts_close failed"));
715 ok = false;
719 return ok;
723 main (int argc, char **argv)
725 char *cwd_only[2];
726 bool max_depth_specified = false;
727 bool ok = true;
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 (".");
741 cwd_only[1] = NULL;
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);
756 while (true)
758 int oi = -1;
759 int c = getopt_long (argc, argv, "0abd:chHklmst:xB:DLPSX:",
760 long_options, &oi);
761 if (c == -1)
762 break;
764 switch (c)
766 #if DU_DEBUG
767 case FTS_DEBUG:
768 fts_debug = true;
769 break;
770 #endif
772 case '0':
773 opt_nul_terminate_output = true;
774 break;
776 case 'a':
777 opt_all = true;
778 break;
780 case APPARENT_SIZE_OPTION:
781 apparent_size = true;
782 break;
784 case 'b':
785 apparent_size = true;
786 human_output_opts = 0;
787 output_block_size = 1;
788 break;
790 case 'c':
791 print_grand_total = true;
792 break;
794 case 'h':
795 human_output_opts = human_autoscale | human_SI | human_base_1024;
796 output_block_size = 1;
797 break;
799 case HUMAN_SI_OPTION:
800 human_output_opts = human_autoscale | human_SI;
801 output_block_size = 1;
802 break;
804 case 'k':
805 human_output_opts = 0;
806 output_block_size = 1024;
807 break;
809 case 'd': /* --max-depth=N */
811 uintmax_t tmp;
812 if (xstrtoumax (optarg, NULL, 0, &tmp, "") == LONGINT_OK
813 && tmp <= SIZE_MAX)
815 max_depth_specified = true;
816 max_depth = tmp;
818 else
820 error (0, 0, _("invalid maximum depth %s"),
821 quote (optarg));
822 ok = false;
825 break;
827 case 'm':
828 human_output_opts = 0;
829 output_block_size = 1024 * 1024;
830 break;
832 case 'l':
833 opt_count_all = true;
834 break;
836 case 's':
837 opt_summarize_only = true;
838 break;
840 case 't':
842 enum strtol_error e;
843 e = xstrtoimax (optarg, NULL, 0, &opt_threshold, "kKmMGTPEZYRQ0");
844 if (e != LONGINT_OK)
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'"));
852 break;
854 case 'x':
855 bit_flags |= FTS_XDEV;
856 break;
858 case 'B':
860 enum strtol_error e = human_options (optarg, &human_output_opts,
861 &output_block_size);
862 if (e != LONGINT_OK)
863 xstrtol_fatal (e, oi, c, long_options, optarg);
865 break;
867 case 'H': /* NOTE: before 2008-12, -H was equivalent to --si. */
868 case 'D':
869 symlink_deref_bits = FTS_COMFOLLOW | FTS_PHYSICAL;
870 break;
872 case 'L': /* --dereference */
873 symlink_deref_bits = FTS_LOGICAL;
874 break;
876 case 'P': /* --no-dereference */
877 symlink_deref_bits = FTS_PHYSICAL;
878 break;
880 case 'S':
881 opt_separate_dirs = true;
882 break;
884 case 'X':
885 if (add_exclude_file (add_exclude, exclude, optarg,
886 EXCLUDE_WILDCARDS, '\n'))
888 error (0, errno, "%s", quotef (optarg));
889 ok = false;
891 break;
893 case FILES0_FROM_OPTION:
894 files_from = optarg;
895 break;
897 case EXCLUDE_OPTION:
898 add_exclude (exclude, optarg, EXCLUDE_WILDCARDS);
899 break;
901 case INODES_OPTION:
902 opt_inodes = true;
903 break;
905 case TIME_OPTION:
906 opt_time = true;
907 time_type =
908 (optarg
909 ? XARGMATCH ("--time", optarg, time_args, time_types)
910 : time_mtime);
911 localtz = tzalloc (getenv ("TZ"));
912 break;
914 case TIME_STYLE_OPTION:
915 time_style = optarg;
916 break;
918 case_GETOPT_HELP_CHAR;
920 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
922 default:
923 ok = false;
927 if (!ok)
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)
938 error (0, 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)
950 max_depth = 0;
952 if (opt_inodes)
954 if (apparent_size)
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. */
963 if (opt_time)
965 if (! time_style)
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
975 with ls. */
976 char *p = strchr (time_style, '\n');
977 if (p)
978 *p = '\0';
980 else
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;
992 else
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";
999 break;
1001 case long_iso_time_style:
1002 time_format = "%Y-%m-%d %H:%M";
1003 break;
1005 case iso_time_style:
1006 time_format = "%Y-%m-%d";
1007 break;
1012 struct argv_iterator *ai;
1013 if (files_from)
1015 /* When using --files0-from=F, you may not specify any files
1016 on the command-line. */
1017 if (optind < argc)
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
1032 worst. */
1033 hash_all = true;
1035 else
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);
1046 if (!ai)
1047 xalloc_die ();
1049 /* Initialize the set of dev,inode pairs. */
1050 di_files = di_set_alloc ();
1051 if (!di_files)
1052 xalloc_die ();
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 };
1062 while (true)
1064 bool skip_file = false;
1065 enum argv_iter_err ai_err;
1066 char *file_name = argv_iter (ai, &ai_err);
1067 if (!file_name)
1069 switch (ai_err)
1071 case AI_ERR_EOF:
1072 goto argv_iter_done;
1073 case AI_ERR_READ:
1074 error (0, errno, _("%s: read error"),
1075 quotef (files_from));
1076 ok = false;
1077 goto argv_iter_done;
1078 case AI_ERR_MEM:
1079 xalloc_die ();
1080 default:
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));
1091 skip_file = true;
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
1097 file name. */
1098 if (!file_name[0])
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"));
1106 else
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"));
1115 skip_file = true;
1118 if (skip_file)
1119 ok = false;
1120 else
1122 temp_argv[0] = file_name;
1123 ok &= du_files (temp_argv, bit_flags);
1126 argv_iter_done:
1128 argv_iter_free (ai);
1129 di_set_free (di_files);
1130 if (di_mnt)
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;