1 /* du -- summarize disk usage
2    Copyright (C) 1988-2020 Free Software Foundation, Inc.
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 3 of the License, or
7    (at your option) 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, see <https://www.gnu.org/licenses/>.  */
16 
17 /* Differences from the Unix du:
18    * Doesn't simply ignore the names of regular files given as arguments
19      when -a is given.
20 
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.  */
25 
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"
47 
48 extern bool fts_debug;
49 
50 /* The official name of this program (e.g., no 'g' prefix).  */
51 #define PROGRAM_NAME "du"
52 
53 #define AUTHORS \
54   proper_name ("Torbjorn Granlund"), \
55   proper_name ("David MacKenzie"), \
56   proper_name ("Paul Eggert"), \
57   proper_name ("Jim Meyering")
58 
59 #if DU_DEBUG
60 # define FTS_CROSS_CHECK(Fts) fts_cross_check (Fts)
61 #else
62 # define FTS_CROSS_CHECK(Fts)
63 #endif
64 
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;
68 
69 /* A set containing a dev/ino pair for each local mount point directory.  */
70 static struct di_set *di_mnt;
71 
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;
75 
76 /* Define a class for collecting directory information. */
77 struct duinfo
78 {
79   /* Size of files in directory.  */
80   uintmax_t size;
81 
82   /* Number of inodes in directory.  */
83   uintmax_t inodes;
84 
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;
88 };
89 
90 /* Initialize directory data.  */
91 static inline void
duinfo_init(struct duinfo * a)92 duinfo_init (struct duinfo *a)
93 {
94   a->size = 0;
95   a->inodes = 0;
96   a->tmax.tv_sec = TYPE_MINIMUM (time_t);
97   a->tmax.tv_nsec = -1;
98 }
99 
100 /* Set directory data.  */
101 static inline void
duinfo_set(struct duinfo * a,uintmax_t size,struct timespec tmax)102 duinfo_set (struct duinfo *a, uintmax_t size, struct timespec tmax)
103 {
104   a->size = size;
105   a->inodes = 1;
106   a->tmax = tmax;
107 }
108 
109 /* Accumulate directory data.  */
110 static inline void
duinfo_add(struct duinfo * a,struct duinfo const * b)111 duinfo_add (struct duinfo *a, struct duinfo const *b)
112 {
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;
118 }
119 
120 /* A structure for per-directory level information.  */
121 struct dulevel
122 {
123   /* Entries in this directory.  */
124   struct duinfo ent;
125 
126   /* Total for subdirectories.  */
127   struct duinfo subdir;
128 };
129 
130 /* If true, display counts for all files, not just directories.  */
131 static bool opt_all = false;
132 
133 /* If true, rather than using the disk usage of each file,
134    use the apparent size (a la stat.st_size).  */
135 static bool apparent_size = false;
136 
137 /* If true, count each hard link of files with multiple links.  */
138 static bool opt_count_all = false;
139 
140 /* If true, hash all files to look for hard links.  */
141 static bool hash_all;
142 
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;
145 
146 /* If true, print a grand total at the end.  */
147 static bool print_grand_total = false;
148 
149 /* If nonzero, do not add sizes of subdirectories.  */
150 static bool opt_separate_dirs = false;
151 
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;
156 
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;
160 
161 /* Human-readable options for output.  */
162 static int human_output_opts;
163 
164 /* Output inodes count instead of blocks used.  */
165 static bool opt_inodes = false;
166 
167 /* If true, print most recently modified date, using the specified format.  */
168 static bool opt_time = false;
169 
170 /* Type of time to display. controlled by --time.  */
171 
172 enum time_type
173   {
174     time_mtime,			/* default */
175     time_ctime,
176     time_atime
177   };
178 
179 static enum time_type time_type = time_mtime;
180 
181 /* User specified date / time style */
182 static char const *time_style = NULL;
183 
184 /* Format used to display date / time. Controlled by --time-style */
185 static char const *time_format = NULL;
186 
187 /* The local time zone rules, as per the TZ environment variable.  */
188 static timezone_t localtz;
189 
190 /* The units to use when printing sizes.  */
191 static uintmax_t output_block_size;
192 
193 /* File name patterns to exclude.  */
194 static struct exclude *exclude;
195 
196 /* Grand total size of all args, in bytes. Also latest modified date. */
197 static struct duinfo tot_dui;
198 
199 #define IS_DIR_TYPE(Type)	\
200   ((Type) == FTS_DP		\
201    || (Type) == FTS_DNR)
202 
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
206 {
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
215 };
216 
217 static struct option const long_options[] =
218 {
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}
246 };
247 
248 static char const *const time_args[] =
249 {
250   "atime", "access", "use", "ctime", "status", NULL
251 };
252 static enum time_type const time_types[] =
253 {
254   time_atime, time_atime, time_atime, time_ctime, time_ctime
255 };
256 ARGMATCH_VERIFY (time_args, time_types);
257 
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
262   {
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 */
266   };
267 
268 static char const *const time_style_args[] =
269 {
270   "full-iso", "long-iso", "iso", NULL
271 };
272 static enum time_style const time_style_types[] =
273 {
274   full_iso_time_style, long_iso_time_style, iso_time_style
275 };
276 ARGMATCH_VERIFY (time_style_args, time_style_types);
277 
278 void
usage(int status)279 usage (int status)
280 {
281   if (status != EXIT_SUCCESS)
282     emit_try_help ();
283   else
284     {
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 disk usage of the set of FILEs, recursively for directories.\n\
291 "), stdout);
292 
293       emit_mandatory_arg_note ();
294 
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 disk usage; although\
299 \n\
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 disk 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)\
323 \n\
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);
359     }
360   exit (status);
361 }
362 
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
hash_ins(struct di_set * di_set,ino_t ino,dev_t dev)367 hash_ins (struct di_set *di_set, ino_t ino, dev_t dev)
368 {
369   int inserted = di_set_insert (di_set, dev, ino);
370   if (inserted < 0)
371     xalloc_die ();
372   return inserted;
373 }
374 
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.  */
378 
379 static void
show_date(const char * format,struct timespec when,timezone_t tz)380 show_date (const char *format, struct timespec when, timezone_t tz)
381 {
382   struct tm tm;
383   if (localtime_rz (tz, &when.tv_sec, &tm))
384     fprintftime (stdout, format, &tm, tz, when.tv_nsec);
385   else
386     {
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);
391     }
392 }
393 
394 /* Print N_BYTES.  Convert it to a readable value before printing.  */
395 
396 static void
print_only_size(uintmax_t n_bytes)397 print_only_size (uintmax_t n_bytes)
398 {
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);
405 }
406 
407 /* Print size (and optionally time) indicated by *PDUI, followed by STRING.  */
408 
409 static void
print_size(const struct duinfo * pdui,const char * string)410 print_size (const struct duinfo *pdui, const char *string)
411 {
412   print_only_size (opt_inodes
413                    ? pdui->inodes
414                    : pdui->size);
415 
416   if (opt_time)
417     {
418       putchar ('\t');
419       show_date (time_format, pdui->tmax, localtz);
420     }
421   printf ("\t%s%c", string, opt_nul_terminate_output ? '\0' : '\n');
422   fflush (stdout);
423 }
424 
425 /* Fill the di_mnt set with local mount point dev/ino pairs.  */
426 
427 static void
fill_mount_table(void)428 fill_mount_table (void)
429 {
430   struct mount_entry *mnt_ent = read_file_system_list (false);
431   while (mnt_ent)
432     {
433       struct mount_entry *mnt_free;
434       if (!mnt_ent->me_remote && !mnt_ent->me_dummy)
435         {
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
440             {
441               /* Ignore stat failure.  False positives are too common.
442                  E.g., "Permission denied" on /run/user/<name>/gvfs.  */
443             }
444         }
445 
446       mnt_free = mnt_ent;
447       mnt_ent = mnt_ent->me_next;
448       free_mount_entry (mnt_free);
449     }
450 }
451 
452 /* This function checks whether any of the directories in the cycle that
453    fts detected is a mount point.  */
454 
455 static bool
mount_point_in_fts_cycle(FTSENT const * ent)456 mount_point_in_fts_cycle (FTSENT const *ent)
457 {
458   FTSENT const *cycle_ent = ent->fts_cycle;
459 
460   if (!di_mnt)
461     {
462       /* Initialize the set of dev,inode pairs.  */
463       di_mnt = di_set_alloc ();
464       if (!di_mnt)
465         xalloc_die ();
466 
467       fill_mount_table ();
468     }
469 
470   while (ent && ent != cycle_ent)
471     {
472       if (di_set_lookup (di_mnt, ent->fts_statp->st_dev,
473                          ent->fts_statp->st_ino) > 0)
474         {
475           return true;
476         }
477       ent = ent->fts_parent;
478     }
479 
480   return false;
481 }
482 
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.  */
487 
488 static bool
process_file(FTS * fts,FTSENT * ent)489 process_file (FTS *fts, FTSENT *ent)
490 {
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 st_size values of all entries in the single directory
498      at the corresponding level.  Although this does include the st_size
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;
505 
506   const char *file = ent->fts_path;
507   const struct stat *sb = ent->fts_statp;
508   int info = ent->fts_info;
509 
510   if (info == FTS_DNR)
511     {
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;
515     }
516   else if (info != FTS_DP)
517     {
518       bool excluded = excluded_file_name (exclude, file);
519       if (! excluded)
520         {
521           /* Make the stat buffer *SB valid, or fail noisily.  */
522 
523           if (info == FTS_NSOK)
524             {
525               fts_set (fts, ent, FTS_AGAIN);
526               FTSENT const *e = fts_read (fts);
527               assert (e == ent);
528               info = ent->fts_info;
529             }
530 
531           if (info == FTS_NS || info == FTS_SLNONE)
532             {
533               error (0, ent->fts_errno, _("cannot access %s"), quoteaf (file));
534               return false;
535             }
536 
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;
546         }
547 
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)))
552         {
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)
557             {
558               fts_set (fts, ent, FTS_SKIP);
559               FTSENT const *e = fts_read (fts);
560               assert (e == ent);
561             }
562 
563           return true;
564         }
565 
566       switch (info)
567         {
568         case FTS_D:
569           return true;
570 
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;
576 
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))
581             {
582               emit_cycle_warning (file);
583               return false;
584             }
585           return true;
586         }
587     }
588 
589   duinfo_set (&dui,
590               (apparent_size
591                ? MAX (0, sb->st_size)
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)));
596 
597   level = ent->fts_level;
598   dui_to_print = dui;
599 
600   if (n_alloc == 0)
601     {
602       n_alloc = level + 10;
603       dulvl = xcalloc (n_alloc, sizeof *dulvl);
604     }
605   else
606     {
607       if (level == prev_level)
608         {
609           /* This is usually the most common case.  Do nothing.  */
610         }
611       else if (level > prev_level)
612         {
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.  */
617 
618           if (n_alloc <= level)
619             {
620               dulvl = xnrealloc (dulvl, level, 2 * sizeof *dulvl);
621               n_alloc = level * 2;
622             }
623 
624           for (size_t i = prev_level + 1; i <= level; i++)
625             {
626               duinfo_init (&dulvl[i].ent);
627               duinfo_init (&dulvl[i].subdir);
628             }
629         }
630       else /* level < prev_level */
631         {
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);
644         }
645     }
646 
647   prev_level = level;
648 
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);
653 
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);
657 
658   if ((IS_DIR_TYPE (info) && level <= max_depth)
659       || (opt_all && level <= max_depth)
660       || level == 0)
661     {
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);
668     }
669 
670   return ok;
671 }
672 
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.  */
677 
678 static bool
du_files(char ** files,int bit_flags)679 du_files (char **files, int bit_flags)
680 {
681   bool ok = true;
682 
683   if (*files)
684     {
685       FTS *fts = xfts_open (files, bit_flags, NULL);
686 
687       while (1)
688         {
689           FTSENT *ent;
690 
691           ent = fts_read (fts);
692           if (ent == NULL)
693             {
694               if (errno != 0)
695                 {
696                   error (0, errno, _("fts_read failed: %s"),
697                          quotef (fts->fts_path));
698                   ok = false;
699                 }
700 
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;
706             }
707           FTS_CROSS_CHECK (fts);
708 
709           ok &= process_file (fts, ent);
710         }
711 
712       if (fts_close (fts) != 0)
713         {
714           error (0, errno, _("fts_close failed"));
715           ok = false;
716         }
717     }
718 
719   return ok;
720 }
721 
722 int
main(int argc,char ** argv)723 main (int argc, char **argv)
724 {
725   char *cwd_only[2];
726   bool max_depth_specified = false;
727   bool ok = true;
728   char *files_from = NULL;
729 
730   /* Bit flags that control how fts works.  */
731   int bit_flags = FTS_NOSTAT;
732 
733   /* Select one of the three FTS_ options that control if/when
734      to follow a symlink.  */
735   int symlink_deref_bits = FTS_PHYSICAL;
736 
737   /* If true, display only a total for each argument. */
738   bool opt_summarize_only = false;
739 
740   cwd_only[0] = bad_cast (".");
741   cwd_only[1] = NULL;
742 
743   initialize_main (&argc, &argv);
744   set_program_name (argv[0]);
745   setlocale (LC_ALL, "");
746   bindtextdomain (PACKAGE, LOCALEDIR);
747   textdomain (PACKAGE);
748 
749   atexit (close_stdout);
750 
751   exclude = new_exclude ();
752 
753   human_options (getenv ("DU_BLOCK_SIZE"),
754                  &human_output_opts, &output_block_size);
755 
756   while (true)
757     {
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;
763 
764       switch (c)
765         {
766 #if DU_DEBUG
767         case FTS_DEBUG:
768           fts_debug = true;
769           break;
770 #endif
771 
772         case '0':
773           opt_nul_terminate_output = true;
774           break;
775 
776         case 'a':
777           opt_all = true;
778           break;
779 
780         case APPARENT_SIZE_OPTION:
781           apparent_size = true;
782           break;
783 
784         case 'b':
785           apparent_size = true;
786           human_output_opts = 0;
787           output_block_size = 1;
788           break;
789 
790         case 'c':
791           print_grand_total = true;
792           break;
793 
794         case 'h':
795           human_output_opts = human_autoscale | human_SI | human_base_1024;
796           output_block_size = 1;
797           break;
798 
799         case HUMAN_SI_OPTION:
800           human_output_opts = human_autoscale | human_SI;
801           output_block_size = 1;
802           break;
803 
804         case 'k':
805           human_output_opts = 0;
806           output_block_size = 1024;
807           break;
808 
809         case 'd':		/* --max-depth=N */
810           {
811             uintmax_t tmp;
812             if (xstrtoumax (optarg, NULL, 0, &tmp, "") == LONGINT_OK
813                 && tmp <= SIZE_MAX)
814               {
815                 max_depth_specified = true;
816                 max_depth = tmp;
817               }
818             else
819               {
820                 error (0, 0, _("invalid maximum depth %s"),
821                        quote (optarg));
822                 ok = false;
823               }
824           }
825           break;
826 
827         case 'm':
828           human_output_opts = 0;
829           output_block_size = 1024 * 1024;
830           break;
831 
832         case 'l':
833           opt_count_all = true;
834           break;
835 
836         case 's':
837           opt_summarize_only = true;
838           break;
839 
840         case 't':
841           {
842             enum strtol_error e;
843             e = xstrtoimax (optarg, NULL, 0, &opt_threshold, "kKmMGTPEZY0");
844             if (e != LONGINT_OK)
845               xstrtol_fatal (e, oi, c, long_options, optarg);
846             if (opt_threshold == 0 && *optarg == '-')
847               {
848                 /* Do not allow -0, as this wouldn't make sense anyway.  */
849                 die (EXIT_FAILURE, 0, _("invalid --threshold argument '-0'"));
850               }
851           }
852           break;
853 
854         case 'x':
855           bit_flags |= FTS_XDEV;
856           break;
857 
858         case 'B':
859           {
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);
864           }
865           break;
866 
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;
871 
872         case 'L': /* --dereference */
873           symlink_deref_bits = FTS_LOGICAL;
874           break;
875 
876         case 'P': /* --no-dereference */
877           symlink_deref_bits = FTS_PHYSICAL;
878           break;
879 
880         case 'S':
881           opt_separate_dirs = true;
882           break;
883 
884         case 'X':
885           if (add_exclude_file (add_exclude, exclude, optarg,
886                                 EXCLUDE_WILDCARDS, '\n'))
887             {
888               error (0, errno, "%s", quotef (optarg));
889               ok = false;
890             }
891           break;
892 
893         case FILES0_FROM_OPTION:
894           files_from = optarg;
895           break;
896 
897         case EXCLUDE_OPTION:
898           add_exclude (exclude, optarg, EXCLUDE_WILDCARDS);
899           break;
900 
901         case INODES_OPTION:
902           opt_inodes = true;
903           break;
904 
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;
913 
914         case TIME_STYLE_OPTION:
915           time_style = optarg;
916           break;
917 
918         case_GETOPT_HELP_CHAR;
919 
920         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
921 
922         default:
923           ok = false;
924         }
925     }
926 
927   if (!ok)
928     usage (EXIT_FAILURE);
929 
930   if (opt_all && opt_summarize_only)
931     {
932       error (0, 0, _("cannot both summarize and show all entries"));
933       usage (EXIT_FAILURE);
934     }
935 
936   if (opt_summarize_only && max_depth_specified && max_depth == 0)
937     {
938       error (0, 0,
939              _("warning: summarizing is the same as using --max-depth=0"));
940     }
941 
942   if (opt_summarize_only && max_depth_specified && max_depth != 0)
943     {
944       unsigned long int d = max_depth;
945       error (0, 0, _("warning: summarizing conflicts with --max-depth=%lu"), d);
946       usage (EXIT_FAILURE);
947     }
948 
949   if (opt_summarize_only)
950     max_depth = 0;
951 
952   if (opt_inodes)
953     {
954       if (apparent_size)
955         {
956           error (0, 0, _("warning: options --apparent-size and -b are "
957                          "ineffective with --inodes"));
958         }
959       output_block_size = 1;
960     }
961 
962   /* Process time style if printing last times.  */
963   if (opt_time)
964     {
965       if (! time_style)
966         {
967           time_style = getenv ("TIME_STYLE");
968 
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 == '+')
973             {
974               /* Ignore anything after a newline, for compatibility
975                  with ls.  */
976               char *p = strchr (time_style, '\n');
977               if (p)
978                 *p = '\0';
979             }
980           else
981             {
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;
987             }
988         }
989 
990       if (*time_style == '+')
991         time_format = time_style + 1;
992       else
993         {
994           switch (XARGMATCH ("time style", time_style,
995                              time_style_args, time_style_types))
996             {
997             case full_iso_time_style:
998               time_format = "%Y-%m-%d %H:%M:%S.%N %z";
999               break;
1000 
1001             case long_iso_time_style:
1002               time_format = "%Y-%m-%d %H:%M";
1003               break;
1004 
1005             case iso_time_style:
1006               time_format = "%Y-%m-%d";
1007               break;
1008             }
1009         }
1010     }
1011 
1012   struct argv_iterator *ai;
1013   if (files_from)
1014     {
1015       /* When using --files0-from=F, you may not specify any files
1016          on the command-line.  */
1017       if (optind < argc)
1018         {
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);
1023         }
1024 
1025       if (! (STREQ (files_from, "-") || freopen (files_from, "r", stdin)))
1026         die (EXIT_FAILURE, errno, _("cannot open %s for reading"),
1027              quoteaf (files_from));
1028 
1029       ai = argv_iter_init_stream (stdin);
1030 
1031       /* It's not easy here to count the arguments, so assume the
1032          worst.  */
1033       hash_all = true;
1034     }
1035   else
1036     {
1037       char **files = (optind < argc ? argv + optind : cwd_only);
1038       ai = argv_iter_init_argv (files);
1039 
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);
1044     }
1045 
1046   if (!ai)
1047     xalloc_die ();
1048 
1049   /* Initialize the set of dev,inode pairs.  */
1050   di_files = di_set_alloc ();
1051   if (!di_files)
1052     xalloc_die ();
1053 
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;
1058 
1059   bit_flags |= symlink_deref_bits;
1060   static char *temp_argv[] = { NULL, NULL };
1061 
1062   while (true)
1063     {
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)
1068         {
1069           switch (ai_err)
1070             {
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");
1082             }
1083         }
1084       if (files_from && STREQ (files_from, "-") && STREQ (file_name, "-"))
1085         {
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;
1092         }
1093 
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])
1099         {
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
1107             {
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"));
1114             }
1115           skip_file = true;
1116         }
1117 
1118       if (skip_file)
1119         ok = false;
1120       else
1121         {
1122           temp_argv[0] = file_name;
1123           ok &= du_files (temp_argv, bit_flags);
1124         }
1125     }
1126  argv_iter_done:
1127 
1128   argv_iter_free (ai);
1129   di_set_free (di_files);
1130   if (di_mnt)
1131     di_set_free (di_mnt);
1132 
1133   if (files_from && (ferror (stdin) || fclose (stdin) != 0) && ok)
1134     die (EXIT_FAILURE, 0, _("error reading %s"), quoteaf (files_from));
1135 
1136   if (print_grand_total)
1137     print_size (&tot_dui, _("total"));
1138 
1139   return ok ? EXIT_SUCCESS : EXIT_FAILURE;
1140 }
1141