1 /* du -- summarize disk usage
2    Copyright (C) 1988-2018 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 
47 extern bool fts_debug;
48 
49 /* The official name of this program (e.g., no 'g' prefix).  */
50 #define PROGRAM_NAME "du"
51 
52 #define AUTHORS \
53   proper_name ("Torbjorn Granlund"), \
54   proper_name ("David MacKenzie"), \
55   proper_name ("Paul Eggert"), \
56   proper_name ("Jim Meyering")
57 
58 #if DU_DEBUG
59 # define FTS_CROSS_CHECK(Fts) fts_cross_check (Fts)
60 #else
61 # define FTS_CROSS_CHECK(Fts)
62 #endif
63 
64 /* A set of dev/ino pairs to help identify files and directories
65    whose sizes have already been counted.  */
66 static struct di_set *di_files;
67 
68 /* A set containing a dev/ino pair for each local mount point directory.  */
69 static struct di_set *di_mnt;
70 
71 /* Keep track of the preceding "level" (depth in hierarchy)
72    from one call of process_file to the next.  */
73 static size_t prev_level;
74 
75 /* Define a class for collecting directory information. */
76 struct duinfo
77 {
78   /* Size of files in directory.  */
79   uintmax_t size;
80 
81   /* Number of inodes in directory.  */
82   uintmax_t inodes;
83 
84   /* Latest timestamp found.  If tmax.tv_sec == TYPE_MINIMUM (time_t)
85      && tmax.tv_nsec < 0, no timestamp has been found.  */
86   struct timespec tmax;
87 };
88 
89 /* Initialize directory data.  */
90 static inline void
duinfo_init(struct duinfo * a)91 duinfo_init (struct duinfo *a)
92 {
93   a->size = 0;
94   a->inodes = 0;
95   a->tmax.tv_sec = TYPE_MINIMUM (time_t);
96   a->tmax.tv_nsec = -1;
97 }
98 
99 /* Set directory data.  */
100 static inline void
duinfo_set(struct duinfo * a,uintmax_t size,struct timespec tmax)101 duinfo_set (struct duinfo *a, uintmax_t size, struct timespec tmax)
102 {
103   a->size = size;
104   a->inodes = 1;
105   a->tmax = tmax;
106 }
107 
108 /* Accumulate directory data.  */
109 static inline void
duinfo_add(struct duinfo * a,struct duinfo const * b)110 duinfo_add (struct duinfo *a, struct duinfo const *b)
111 {
112   uintmax_t sum = a->size + b->size;
113   a->size = a->size <= sum ? sum : UINTMAX_MAX;
114   a->inodes = a->inodes + b->inodes;
115   if (timespec_cmp (a->tmax, b->tmax) < 0)
116     a->tmax = b->tmax;
117 }
118 
119 /* A structure for per-directory level information.  */
120 struct dulevel
121 {
122   /* Entries in this directory.  */
123   struct duinfo ent;
124 
125   /* Total for subdirectories.  */
126   struct duinfo subdir;
127 };
128 
129 /* If true, display counts for all files, not just directories.  */
130 static bool opt_all = false;
131 
132 /* If true, rather than using the disk usage of each file,
133    use the apparent size (a la stat.st_size).  */
134 static bool apparent_size = false;
135 
136 /* If true, count each hard link of files with multiple links.  */
137 static bool opt_count_all = false;
138 
139 /* If true, hash all files to look for hard links.  */
140 static bool hash_all;
141 
142 /* If true, output the NUL byte instead of a newline at the end of each line. */
143 static bool opt_nul_terminate_output = false;
144 
145 /* If true, print a grand total at the end.  */
146 static bool print_grand_total = false;
147 
148 /* If nonzero, do not add sizes of subdirectories.  */
149 static bool opt_separate_dirs = false;
150 
151 /* Show the total for each directory (and file if --all) that is at
152    most MAX_DEPTH levels down from the root of the hierarchy.  The root
153    is at level 0, so 'du --max-depth=0' is equivalent to 'du -s'.  */
154 static size_t max_depth = SIZE_MAX;
155 
156 /* Only output entries with at least this SIZE if positive,
157    or at most if negative.  See --threshold option.  */
158 static intmax_t opt_threshold = 0;
159 
160 /* Human-readable options for output.  */
161 static int human_output_opts;
162 
163 /* Output inodes count instead of blocks used.  */
164 static bool opt_inodes = false;
165 
166 /* If true, print most recently modified date, using the specified format.  */
167 static bool opt_time = false;
168 
169 /* Type of time to display. controlled by --time.  */
170 
171 enum time_type
172   {
173     time_mtime,			/* default */
174     time_ctime,
175     time_atime
176   };
177 
178 static enum time_type time_type = time_mtime;
179 
180 /* User specified date / time style */
181 static char const *time_style = NULL;
182 
183 /* Format used to display date / time. Controlled by --time-style */
184 static char const *time_format = NULL;
185 
186 /* The local time zone rules, as per the TZ environment variable.  */
187 static timezone_t localtz;
188 
189 /* The units to use when printing sizes.  */
190 static uintmax_t output_block_size;
191 
192 /* File name patterns to exclude.  */
193 static struct exclude *exclude;
194 
195 /* Grand total size of all args, in bytes. Also latest modified date. */
196 static struct duinfo tot_dui;
197 
198 #define IS_DIR_TYPE(Type)	\
199   ((Type) == FTS_DP		\
200    || (Type) == FTS_DNR)
201 
202 /* For long options that have no equivalent short option, use a
203    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
204 enum
205 {
206   APPARENT_SIZE_OPTION = CHAR_MAX + 1,
207   EXCLUDE_OPTION,
208   FILES0_FROM_OPTION,
209   HUMAN_SI_OPTION,
210   FTS_DEBUG,
211   TIME_OPTION,
212   TIME_STYLE_OPTION,
213   INODES_OPTION
214 };
215 
216 static struct option const long_options[] =
217 {
218   {"all", no_argument, NULL, 'a'},
219   {"apparent-size", no_argument, NULL, APPARENT_SIZE_OPTION},
220   {"block-size", required_argument, NULL, 'B'},
221   {"bytes", no_argument, NULL, 'b'},
222   {"count-links", no_argument, NULL, 'l'},
223   /* {"-debug", no_argument, NULL, FTS_DEBUG}, */
224   {"dereference", no_argument, NULL, 'L'},
225   {"dereference-args", no_argument, NULL, 'D'},
226   {"exclude", required_argument, NULL, EXCLUDE_OPTION},
227   {"exclude-from", required_argument, NULL, 'X'},
228   {"files0-from", required_argument, NULL, FILES0_FROM_OPTION},
229   {"human-readable", no_argument, NULL, 'h'},
230   {"inodes", no_argument, NULL, INODES_OPTION},
231   {"si", no_argument, NULL, HUMAN_SI_OPTION},
232   {"max-depth", required_argument, NULL, 'd'},
233   {"null", no_argument, NULL, '0'},
234   {"no-dereference", no_argument, NULL, 'P'},
235   {"one-file-system", no_argument, NULL, 'x'},
236   {"separate-dirs", no_argument, NULL, 'S'},
237   {"summarize", no_argument, NULL, 's'},
238   {"total", no_argument, NULL, 'c'},
239   {"threshold", required_argument, NULL, 't'},
240   {"time", optional_argument, NULL, TIME_OPTION},
241   {"time-style", required_argument, NULL, TIME_STYLE_OPTION},
242   {GETOPT_HELP_OPTION_DECL},
243   {GETOPT_VERSION_OPTION_DECL},
244   {NULL, 0, NULL, 0}
245 };
246 
247 static char const *const time_args[] =
248 {
249   "atime", "access", "use", "ctime", "status", NULL
250 };
251 static enum time_type const time_types[] =
252 {
253   time_atime, time_atime, time_atime, time_ctime, time_ctime
254 };
255 ARGMATCH_VERIFY (time_args, time_types);
256 
257 /* 'full-iso' uses full ISO-style dates and times.  'long-iso' uses longer
258    ISO-style timestamps, though shorter than 'full-iso'.  'iso' uses shorter
259    ISO-style timestamps.  */
260 enum time_style
261   {
262     full_iso_time_style,       /* --time-style=full-iso */
263     long_iso_time_style,       /* --time-style=long-iso */
264     iso_time_style	       /* --time-style=iso */
265   };
266 
267 static char const *const time_style_args[] =
268 {
269   "full-iso", "long-iso", "iso", NULL
270 };
271 static enum time_style const time_style_types[] =
272 {
273   full_iso_time_style, long_iso_time_style, iso_time_style
274 };
275 ARGMATCH_VERIFY (time_style_args, time_style_types);
276 
277 void
usage(int status)278 usage (int status)
279 {
280   if (status != EXIT_SUCCESS)
281     emit_try_help ();
282   else
283     {
284       printf (_("\
285 Usage: %s [OPTION]... [FILE]...\n\
286   or:  %s [OPTION]... --files0-from=F\n\
287 "), program_name, program_name);
288       fputs (_("\
289 Summarize disk usage of the set of FILEs, recursively for directories.\n\
290 "), stdout);
291 
292       emit_mandatory_arg_note ();
293 
294       fputs (_("\
295   -0, --null            end each output line with NUL, not newline\n\
296   -a, --all             write counts for all files, not just directories\n\
297       --apparent-size   print apparent sizes, rather than disk usage; although\
298 \n\
299                           the apparent size is usually smaller, it may be\n\
300                           larger due to holes in ('sparse') files, internal\n\
301                           fragmentation, indirect blocks, and the like\n\
302 "), stdout);
303       fputs (_("\
304   -B, --block-size=SIZE  scale sizes by SIZE before printing them; e.g.,\n\
305                            '-BM' prints sizes in units of 1,048,576 bytes;\n\
306                            see SIZE format below\n\
307   -b, --bytes           equivalent to '--apparent-size --block-size=1'\n\
308   -c, --total           produce a grand total\n\
309   -D, --dereference-args  dereference only symlinks that are listed on the\n\
310                           command line\n\
311   -d, --max-depth=N     print the total for a directory (or file, with --all)\n\
312                           only if it is N or fewer levels below the command\n\
313                           line argument;  --max-depth=0 is the same as\n\
314                           --summarize\n\
315 "), stdout);
316       fputs (_("\
317       --files0-from=F   summarize disk usage of the\n\
318                           NUL-terminated file names specified in file F;\n\
319                           if F is -, then read names from standard input\n\
320   -H                    equivalent to --dereference-args (-D)\n\
321   -h, --human-readable  print sizes in human readable format (e.g., 1K 234M 2G)\
322 \n\
323       --inodes          list inode usage information instead of block usage\n\
324 "), stdout);
325       fputs (_("\
326   -k                    like --block-size=1K\n\
327   -L, --dereference     dereference all symbolic links\n\
328   -l, --count-links     count sizes many times if hard linked\n\
329   -m                    like --block-size=1M\n\
330 "), stdout);
331       fputs (_("\
332   -P, --no-dereference  don't follow any symbolic links (this is the default)\n\
333   -S, --separate-dirs   for directories do not include size of subdirectories\n\
334       --si              like -h, but use powers of 1000 not 1024\n\
335   -s, --summarize       display only a total for each argument\n\
336 "), stdout);
337       fputs (_("\
338   -t, --threshold=SIZE  exclude entries smaller than SIZE if positive,\n\
339                           or entries greater than SIZE if negative\n\
340       --time            show time of the last modification of any file in the\n\
341                           directory, or any of its subdirectories\n\
342       --time=WORD       show time as WORD instead of modification time:\n\
343                           atime, access, use, ctime or status\n\
344       --time-style=STYLE  show times using STYLE, which can be:\n\
345                             full-iso, long-iso, iso, or +FORMAT;\n\
346                             FORMAT is interpreted like in 'date'\n\
347 "), stdout);
348       fputs (_("\
349   -X, --exclude-from=FILE  exclude files that match any pattern in FILE\n\
350       --exclude=PATTERN    exclude files that match PATTERN\n\
351   -x, --one-file-system    skip directories on different file systems\n\
352 "), stdout);
353       fputs (HELP_OPTION_DESCRIPTION, stdout);
354       fputs (VERSION_OPTION_DESCRIPTION, stdout);
355       emit_blocksize_note ("DU");
356       emit_size_note ();
357       emit_ancillary_info (PROGRAM_NAME);
358     }
359   exit (status);
360 }
361 
362 /* Try to insert the INO/DEV pair into DI_SET.
363    Return true if the pair is successfully inserted,
364    false if the pair was already there.  */
365 static bool
hash_ins(struct di_set * di_set,ino_t ino,dev_t dev)366 hash_ins (struct di_set *di_set, ino_t ino, dev_t dev)
367 {
368   int inserted = di_set_insert (di_set, dev, ino);
369   if (inserted < 0)
370     xalloc_die ();
371   return inserted;
372 }
373 
374 /* FIXME: this code is nearly identical to code in date.c  */
375 /* Display the date and time in WHEN according to the format specified
376    in FORMAT.  */
377 
378 static void
show_date(const char * format,struct timespec when,timezone_t tz)379 show_date (const char *format, struct timespec when, timezone_t tz)
380 {
381   struct tm tm;
382   if (localtime_rz (tz, &when.tv_sec, &tm))
383     fprintftime (stdout, format, &tm, tz, when.tv_nsec);
384   else
385     {
386       char buf[INT_BUFSIZE_BOUND (intmax_t)];
387       char *when_str = timetostr (when.tv_sec, buf);
388       error (0, 0, _("time %s is out of range"), quote (when_str));
389       fputs (when_str, stdout);
390     }
391 }
392 
393 /* Print N_BYTES.  Convert it to a readable value before printing.  */
394 
395 static void
print_only_size(uintmax_t n_bytes)396 print_only_size (uintmax_t n_bytes)
397 {
398   char buf[LONGEST_HUMAN_READABLE + 1];
399   fputs ((n_bytes == UINTMAX_MAX
400           ? _("Infinity")
401           : human_readable (n_bytes, buf, human_output_opts,
402                             1, output_block_size)),
403          stdout);
404 }
405 
406 /* Print size (and optionally time) indicated by *PDUI, followed by STRING.  */
407 
408 static void
print_size(const struct duinfo * pdui,const char * string)409 print_size (const struct duinfo *pdui, const char *string)
410 {
411   print_only_size (opt_inodes
412                    ? pdui->inodes
413                    : pdui->size);
414 
415   if (opt_time)
416     {
417       putchar ('\t');
418       show_date (time_format, pdui->tmax, localtz);
419     }
420   printf ("\t%s%c", string, opt_nul_terminate_output ? '\0' : '\n');
421   fflush (stdout);
422 }
423 
424 /* Fill the di_mnt set with local mount point dev/ino pairs.  */
425 
426 static void
fill_mount_table(void)427 fill_mount_table (void)
428 {
429   struct mount_entry *mnt_ent = read_file_system_list (false);
430   while (mnt_ent)
431     {
432       struct mount_entry *mnt_free;
433       if (!mnt_ent->me_remote && !mnt_ent->me_dummy)
434         {
435           struct stat buf;
436           if (!stat (mnt_ent->me_mountdir, &buf))
437             hash_ins (di_mnt, buf.st_ino, buf.st_dev);
438           else
439             {
440               /* Ignore stat failure.  False positives are too common.
441                  E.g., "Permission denied" on /run/user/<name>/gvfs.  */
442             }
443         }
444 
445       mnt_free = mnt_ent;
446       mnt_ent = mnt_ent->me_next;
447       free_mount_entry (mnt_free);
448     }
449 }
450 
451 /* This function checks whether any of the directories in the cycle that
452    fts detected is a mount point.  */
453 
454 static bool
mount_point_in_fts_cycle(FTSENT const * ent)455 mount_point_in_fts_cycle (FTSENT const *ent)
456 {
457   FTSENT const *cycle_ent = ent->fts_cycle;
458 
459   if (!di_mnt)
460     {
461       /* Initialize the set of dev,inode pairs.  */
462       di_mnt = di_set_alloc ();
463       if (!di_mnt)
464         xalloc_die ();
465 
466       fill_mount_table ();
467     }
468 
469   while (ent && ent != cycle_ent)
470     {
471       if (di_set_lookup (di_mnt, ent->fts_statp->st_dev,
472                          ent->fts_statp->st_ino) > 0)
473         {
474           return true;
475         }
476       ent = ent->fts_parent;
477     }
478 
479   return false;
480 }
481 
482 /* This function is called once for every file system object that fts
483    encounters.  fts does a depth-first traversal.  This function knows
484    that and accumulates per-directory totals based on changes in
485    the depth of the current entry.  It returns true on success.  */
486 
487 static bool
process_file(FTS * fts,FTSENT * ent)488 process_file (FTS *fts, FTSENT *ent)
489 {
490   bool ok = true;
491   struct duinfo dui;
492   struct duinfo dui_to_print;
493   size_t level;
494   static size_t n_alloc;
495   /* First element of the structure contains:
496      The sum of the st_size values of all entries in the single directory
497      at the corresponding level.  Although this does include the st_size
498      corresponding to each subdirectory, it does not include the size of
499      any file in a subdirectory. Also corresponding last modified date.
500      Second element of the structure contains:
501      The sum of the sizes of all entries in the hierarchy at or below the
502      directory at the specified level.  */
503   static struct dulevel *dulvl;
504 
505   const char *file = ent->fts_path;
506   const struct stat *sb = ent->fts_statp;
507   int info = ent->fts_info;
508 
509   if (info == FTS_DNR)
510     {
511       /* An error occurred, but the size is known, so count it.  */
512       error (0, ent->fts_errno, _("cannot read directory %s"), quoteaf (file));
513       ok = false;
514     }
515   else if (info != FTS_DP)
516     {
517       bool excluded = excluded_file_name (exclude, file);
518       if (! excluded)
519         {
520           /* Make the stat buffer *SB valid, or fail noisily.  */
521 
522           if (info == FTS_NSOK)
523             {
524               fts_set (fts, ent, FTS_AGAIN);
525               FTSENT const *e = fts_read (fts);
526               assert (e == ent);
527               info = ent->fts_info;
528             }
529 
530           if (info == FTS_NS || info == FTS_SLNONE)
531             {
532               error (0, ent->fts_errno, _("cannot access %s"), quoteaf (file));
533               return false;
534             }
535 
536           /* The --one-file-system (-x) option cannot exclude anything
537              specified on the command-line.  By definition, it can exclude
538              a file or directory only when its device number is different
539              from that of its just-processed parent directory, and du does
540              not process the parent of a command-line argument.  */
541           if (fts->fts_options & FTS_XDEV
542               && FTS_ROOTLEVEL < ent->fts_level
543               && fts->fts_dev != sb->st_dev)
544             excluded = true;
545         }
546 
547       if (excluded
548           || (! opt_count_all
549               && (hash_all || (! S_ISDIR (sb->st_mode) && 1 < sb->st_nlink))
550               && ! hash_ins (di_files, sb->st_ino, sb->st_dev)))
551         {
552           /* If ignoring a directory in preorder, skip its children.
553              Ignore the next fts_read output too, as it's a postorder
554              visit to the same directory.  */
555           if (info == FTS_D)
556             {
557               fts_set (fts, ent, FTS_SKIP);
558               FTSENT const *e = fts_read (fts);
559               assert (e == ent);
560             }
561 
562           return true;
563         }
564 
565       switch (info)
566         {
567         case FTS_D:
568           return true;
569 
570         case FTS_ERR:
571           /* An error occurred, but the size is known, so count it.  */
572           error (0, ent->fts_errno, "%s", quotef (file));
573           ok = false;
574           break;
575 
576         case FTS_DC:
577           /* If not following symlinks and not a (bind) mount point.  */
578           if (cycle_warning_required (fts, ent)
579               && ! mount_point_in_fts_cycle (ent))
580             {
581               emit_cycle_warning (file);
582               return false;
583             }
584           return true;
585         }
586     }
587 
588   duinfo_set (&dui,
589               (apparent_size
590                ? MAX (0, sb->st_size)
591                : (uintmax_t) ST_NBLOCKS (*sb) * ST_NBLOCKSIZE),
592               (time_type == time_mtime ? get_stat_mtime (sb)
593                : time_type == time_atime ? get_stat_atime (sb)
594                : get_stat_ctime (sb)));
595 
596   level = ent->fts_level;
597   dui_to_print = dui;
598 
599   if (n_alloc == 0)
600     {
601       n_alloc = level + 10;
602       dulvl = xcalloc (n_alloc, sizeof *dulvl);
603     }
604   else
605     {
606       if (level == prev_level)
607         {
608           /* This is usually the most common case.  Do nothing.  */
609         }
610       else if (level > prev_level)
611         {
612           /* Descending the hierarchy.
613              Clear the accumulators for *all* levels between prev_level
614              and the current one.  The depth may change dramatically,
615              e.g., from 1 to 10.  */
616 
617           if (n_alloc <= level)
618             {
619               dulvl = xnrealloc (dulvl, level, 2 * sizeof *dulvl);
620               n_alloc = level * 2;
621             }
622 
623           for (size_t i = prev_level + 1; i <= level; i++)
624             {
625               duinfo_init (&dulvl[i].ent);
626               duinfo_init (&dulvl[i].subdir);
627             }
628         }
629       else /* level < prev_level */
630         {
631           /* Ascending the hierarchy.
632              Process a directory only after all entries in that
633              directory have been processed.  When the depth decreases,
634              propagate sums from the children (prev_level) to the parent.
635              Here, the current level is always one smaller than the
636              previous one.  */
637           assert (level == prev_level - 1);
638           duinfo_add (&dui_to_print, &dulvl[prev_level].ent);
639           if (!opt_separate_dirs)
640             duinfo_add (&dui_to_print, &dulvl[prev_level].subdir);
641           duinfo_add (&dulvl[level].subdir, &dulvl[prev_level].ent);
642           duinfo_add (&dulvl[level].subdir, &dulvl[prev_level].subdir);
643         }
644     }
645 
646   prev_level = level;
647 
648   /* Let the size of a directory entry contribute to the total for the
649      containing directory, unless --separate-dirs (-S) is specified.  */
650   if (! (opt_separate_dirs && IS_DIR_TYPE (info)))
651     duinfo_add (&dulvl[level].ent, &dui);
652 
653   /* Even if this directory is unreadable or we can't chdir into it,
654      do let its size contribute to the total. */
655   duinfo_add (&tot_dui, &dui);
656 
657   if ((IS_DIR_TYPE (info) && level <= max_depth)
658       || (opt_all && level <= max_depth)
659       || level == 0)
660     {
661       /* Print or elide this entry according to the --threshold option.  */
662       uintmax_t v = opt_inodes ? dui_to_print.inodes : dui_to_print.size;
663       if (opt_threshold < 0
664           ? v <= -opt_threshold
665           : v >= opt_threshold)
666         print_size (&dui_to_print, file);
667     }
668 
669   return ok;
670 }
671 
672 /* Recursively print the sizes of the directories (and, if selected, files)
673    named in FILES, the last entry of which is NULL.
674    BIT_FLAGS controls how fts works.
675    Return true if successful.  */
676 
677 static bool
du_files(char ** files,int bit_flags)678 du_files (char **files, int bit_flags)
679 {
680   bool ok = true;
681 
682   if (*files)
683     {
684       FTS *fts = xfts_open (files, bit_flags, NULL);
685 
686       while (1)
687         {
688           FTSENT *ent;
689 
690           ent = fts_read (fts);
691           if (ent == NULL)
692             {
693               if (errno != 0)
694                 {
695                   error (0, errno, _("fts_read failed: %s"),
696                          quotef (fts->fts_path));
697                   ok = false;
698                 }
699 
700               /* When exiting this loop early, be careful to reset the
701                  global, prev_level, used in process_file.  Otherwise, its
702                  (level == prev_level - 1) assertion could fail.  */
703               prev_level = 0;
704               break;
705             }
706           FTS_CROSS_CHECK (fts);
707 
708           ok &= process_file (fts, ent);
709         }
710 
711       if (fts_close (fts) != 0)
712         {
713           error (0, errno, _("fts_close failed"));
714           ok = false;
715         }
716     }
717 
718   return ok;
719 }
720 
721 int
main(int argc,char ** argv)722 main (int argc, char **argv)
723 {
724   char *cwd_only[2];
725   bool max_depth_specified = false;
726   bool ok = true;
727   char *files_from = NULL;
728 
729   /* Bit flags that control how fts works.  */
730   int bit_flags = FTS_NOSTAT;
731 
732   /* Select one of the three FTS_ options that control if/when
733      to follow a symlink.  */
734   int symlink_deref_bits = FTS_PHYSICAL;
735 
736   /* If true, display only a total for each argument. */
737   bool opt_summarize_only = false;
738 
739   cwd_only[0] = bad_cast (".");
740   cwd_only[1] = NULL;
741 
742   initialize_main (&argc, &argv);
743   set_program_name (argv[0]);
744   setlocale (LC_ALL, "");
745   bindtextdomain (PACKAGE, LOCALEDIR);
746   textdomain (PACKAGE);
747 
748   atexit (close_stdout);
749 
750   exclude = new_exclude ();
751 
752   human_options (getenv ("DU_BLOCK_SIZE"),
753                  &human_output_opts, &output_block_size);
754 
755   while (true)
756     {
757       int oi = -1;
758       int c = getopt_long (argc, argv, "0abd:chHklmst:xB:DLPSX:",
759                            long_options, &oi);
760       if (c == -1)
761         break;
762 
763       switch (c)
764         {
765 #if DU_DEBUG
766         case FTS_DEBUG:
767           fts_debug = true;
768           break;
769 #endif
770 
771         case '0':
772           opt_nul_terminate_output = true;
773           break;
774 
775         case 'a':
776           opt_all = true;
777           break;
778 
779         case APPARENT_SIZE_OPTION:
780           apparent_size = true;
781           break;
782 
783         case 'b':
784           apparent_size = true;
785           human_output_opts = 0;
786           output_block_size = 1;
787           break;
788 
789         case 'c':
790           print_grand_total = true;
791           break;
792 
793         case 'h':
794           human_output_opts = human_autoscale | human_SI | human_base_1024;
795           output_block_size = 1;
796           break;
797 
798         case HUMAN_SI_OPTION:
799           human_output_opts = human_autoscale | human_SI;
800           output_block_size = 1;
801           break;
802 
803         case 'k':
804           human_output_opts = 0;
805           output_block_size = 1024;
806           break;
807 
808         case 'd':		/* --max-depth=N */
809           {
810             unsigned long int tmp_ulong;
811             if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) == LONGINT_OK
812                 && tmp_ulong <= SIZE_MAX)
813               {
814                 max_depth_specified = true;
815                 max_depth = tmp_ulong;
816               }
817             else
818               {
819                 error (0, 0, _("invalid maximum depth %s"),
820                        quote (optarg));
821                 ok = false;
822               }
823           }
824           break;
825 
826         case 'm':
827           human_output_opts = 0;
828           output_block_size = 1024 * 1024;
829           break;
830 
831         case 'l':
832           opt_count_all = true;
833           break;
834 
835         case 's':
836           opt_summarize_only = true;
837           break;
838 
839         case 't':
840           {
841             enum strtol_error e;
842             e = xstrtoimax (optarg, NULL, 0, &opt_threshold, "kKmMGTPEZY0");
843             if (e != LONGINT_OK)
844               xstrtol_fatal (e, oi, c, long_options, optarg);
845             if (opt_threshold == 0 && *optarg == '-')
846               {
847                 /* Do not allow -0, as this wouldn't make sense anyway.  */
848                 die (EXIT_FAILURE, 0, _("invalid --threshold argument '-0'"));
849               }
850           }
851           break;
852 
853         case 'x':
854           bit_flags |= FTS_XDEV;
855           break;
856 
857         case 'B':
858           {
859             enum strtol_error e = human_options (optarg, &human_output_opts,
860                                                  &output_block_size);
861             if (e != LONGINT_OK)
862               xstrtol_fatal (e, oi, c, long_options, optarg);
863           }
864           break;
865 
866         case 'H':  /* NOTE: before 2008-12, -H was equivalent to --si.  */
867         case 'D':
868           symlink_deref_bits = FTS_COMFOLLOW | FTS_PHYSICAL;
869           break;
870 
871         case 'L': /* --dereference */
872           symlink_deref_bits = FTS_LOGICAL;
873           break;
874 
875         case 'P': /* --no-dereference */
876           symlink_deref_bits = FTS_PHYSICAL;
877           break;
878 
879         case 'S':
880           opt_separate_dirs = true;
881           break;
882 
883         case 'X':
884           if (add_exclude_file (add_exclude, exclude, optarg,
885                                 EXCLUDE_WILDCARDS, '\n'))
886             {
887               error (0, errno, "%s", quotef (optarg));
888               ok = false;
889             }
890           break;
891 
892         case FILES0_FROM_OPTION:
893           files_from = optarg;
894           break;
895 
896         case EXCLUDE_OPTION:
897           add_exclude (exclude, optarg, EXCLUDE_WILDCARDS);
898           break;
899 
900         case INODES_OPTION:
901           opt_inodes = true;
902           break;
903 
904         case TIME_OPTION:
905           opt_time = true;
906           time_type =
907             (optarg
908              ? XARGMATCH ("--time", optarg, time_args, time_types)
909              : time_mtime);
910           localtz = tzalloc (getenv ("TZ"));
911           break;
912 
913         case TIME_STYLE_OPTION:
914           time_style = optarg;
915           break;
916 
917         case_GETOPT_HELP_CHAR;
918 
919         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
920 
921         default:
922           ok = false;
923         }
924     }
925 
926   if (!ok)
927     usage (EXIT_FAILURE);
928 
929   if (opt_all && opt_summarize_only)
930     {
931       error (0, 0, _("cannot both summarize and show all entries"));
932       usage (EXIT_FAILURE);
933     }
934 
935   if (opt_summarize_only && max_depth_specified && max_depth == 0)
936     {
937       error (0, 0,
938              _("warning: summarizing is the same as using --max-depth=0"));
939     }
940 
941   if (opt_summarize_only && max_depth_specified && max_depth != 0)
942     {
943       unsigned long int d = max_depth;
944       error (0, 0, _("warning: summarizing conflicts with --max-depth=%lu"), d);
945       usage (EXIT_FAILURE);
946     }
947 
948   if (opt_summarize_only)
949     max_depth = 0;
950 
951   if (opt_inodes)
952     {
953       if (apparent_size)
954         {
955           error (0, 0, _("warning: options --apparent-size and -b are "
956                          "ineffective with --inodes"));
957         }
958       output_block_size = 1;
959     }
960 
961   /* Process time style if printing last times.  */
962   if (opt_time)
963     {
964       if (! time_style)
965         {
966           time_style = getenv ("TIME_STYLE");
967 
968           /* Ignore TIMESTYLE="locale", for compatibility with ls.  */
969           if (! time_style || STREQ (time_style, "locale"))
970             time_style = "long-iso";
971           else if (*time_style == '+')
972             {
973               /* Ignore anything after a newline, for compatibility
974                  with ls.  */
975               char *p = strchr (time_style, '\n');
976               if (p)
977                 *p = '\0';
978             }
979           else
980             {
981               /* Ignore "posix-" prefix, for compatibility with ls.  */
982               static char const posix_prefix[] = "posix-";
983               static const size_t prefix_len = sizeof posix_prefix - 1;
984               while (STREQ_LEN (time_style, posix_prefix, prefix_len))
985                 time_style += prefix_len;
986             }
987         }
988 
989       if (*time_style == '+')
990         time_format = time_style + 1;
991       else
992         {
993           switch (XARGMATCH ("time style", time_style,
994                              time_style_args, time_style_types))
995             {
996             case full_iso_time_style:
997               time_format = "%Y-%m-%d %H:%M:%S.%N %z";
998               break;
999 
1000             case long_iso_time_style:
1001               time_format = "%Y-%m-%d %H:%M";
1002               break;
1003 
1004             case iso_time_style:
1005               time_format = "%Y-%m-%d";
1006               break;
1007             }
1008         }
1009     }
1010 
1011   struct argv_iterator *ai;
1012   if (files_from)
1013     {
1014       /* When using --files0-from=F, you may not specify any files
1015          on the command-line.  */
1016       if (optind < argc)
1017         {
1018           error (0, 0, _("extra operand %s"), quote (argv[optind]));
1019           fprintf (stderr, "%s\n",
1020                    _("file operands cannot be combined with --files0-from"));
1021           usage (EXIT_FAILURE);
1022         }
1023 
1024       if (! (STREQ (files_from, "-") || freopen (files_from, "r", stdin)))
1025         die (EXIT_FAILURE, errno, _("cannot open %s for reading"),
1026              quoteaf (files_from));
1027 
1028       ai = argv_iter_init_stream (stdin);
1029 
1030       /* It's not easy here to count the arguments, so assume the
1031          worst.  */
1032       hash_all = true;
1033     }
1034   else
1035     {
1036       char **files = (optind < argc ? argv + optind : cwd_only);
1037       ai = argv_iter_init_argv (files);
1038 
1039       /* Hash all dev,ino pairs if there are multiple arguments, or if
1040          following non-command-line symlinks, because in either case a
1041          file with just one hard link might be seen more than once.  */
1042       hash_all = (optind + 1 < argc || symlink_deref_bits == FTS_LOGICAL);
1043     }
1044 
1045   if (!ai)
1046     xalloc_die ();
1047 
1048   /* Initialize the set of dev,inode pairs.  */
1049   di_files = di_set_alloc ();
1050   if (!di_files)
1051     xalloc_die ();
1052 
1053   /* If not hashing everything, process_file won't find cycles on its
1054      own, so ask fts_read to check for them accurately.  */
1055   if (opt_count_all || ! hash_all)
1056     bit_flags |= FTS_TIGHT_CYCLE_CHECK;
1057 
1058   bit_flags |= symlink_deref_bits;
1059   static char *temp_argv[] = { NULL, NULL };
1060 
1061   while (true)
1062     {
1063       bool skip_file = false;
1064       enum argv_iter_err ai_err;
1065       char *file_name = argv_iter (ai, &ai_err);
1066       if (!file_name)
1067         {
1068           switch (ai_err)
1069             {
1070             case AI_ERR_EOF:
1071               goto argv_iter_done;
1072             case AI_ERR_READ:
1073               error (0, errno, _("%s: read error"),
1074                      quotef (files_from));
1075               ok = false;
1076               goto argv_iter_done;
1077             case AI_ERR_MEM:
1078               xalloc_die ();
1079             default:
1080               assert (!"unexpected error code from argv_iter");
1081             }
1082         }
1083       if (files_from && STREQ (files_from, "-") && STREQ (file_name, "-"))
1084         {
1085           /* Give a better diagnostic in an unusual case:
1086              printf - | du --files0-from=- */
1087           error (0, 0, _("when reading file names from stdin, "
1088                          "no file name of %s allowed"),
1089                  quoteaf (file_name));
1090           skip_file = true;
1091         }
1092 
1093       /* Report and skip any empty file names before invoking fts.
1094          This works around a glitch in fts, which fails immediately
1095          (without looking at the other file names) when given an empty
1096          file name.  */
1097       if (!file_name[0])
1098         {
1099           /* Diagnose a zero-length file name.  When it's one
1100              among many, knowing the record number may help.
1101              FIXME: currently print the record number only with
1102              --files0-from=FILE.  Maybe do it for argv, too?  */
1103           if (files_from == NULL)
1104             error (0, 0, "%s", _("invalid zero-length file name"));
1105           else
1106             {
1107               /* Using the standard 'filename:line-number:' prefix here is
1108                  not totally appropriate, since NUL is the separator, not NL,
1109                  but it might be better than nothing.  */
1110               unsigned long int file_number = argv_iter_n_args (ai);
1111               error (0, 0, "%s:%lu: %s", quotef (files_from),
1112                      file_number, _("invalid zero-length file name"));
1113             }
1114           skip_file = true;
1115         }
1116 
1117       if (skip_file)
1118         ok = false;
1119       else
1120         {
1121           temp_argv[0] = file_name;
1122           ok &= du_files (temp_argv, bit_flags);
1123         }
1124     }
1125  argv_iter_done:
1126 
1127   argv_iter_free (ai);
1128   di_set_free (di_files);
1129   if (di_mnt)
1130     di_set_free (di_mnt);
1131 
1132   if (files_from && (ferror (stdin) || fclose (stdin) != 0) && ok)
1133     die (EXIT_FAILURE, 0, _("error reading %s"), quoteaf (files_from));
1134 
1135   if (print_grand_total)
1136     print_size (&tot_dui, _("total"));
1137 
1138   return ok ? EXIT_SUCCESS : EXIT_FAILURE;
1139 }
1140