/* du -- summarize disk usage This is the du utility
Copyright (C) 1988-2018 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */ The GNUv3 license
/* Differences from the Unix du:
* Doesn't simply ignore the names of regular files given as arguments
when -a is given.
By tege@sics.se, Torbjorn Granlund,
and djm@ai.mit.edu, David MacKenzie.
Variable blocks added by lm@sgi.com and eggert@twinsun.com.
Rewritten to use nftw, then to use fts by Jim Meyering. */
#include <config.h> Provides system specific information
#include <getopt.h> ...!includes auto-comment...
#include <sys/types.h> Provides system data types
#include <assert.h> ...!includes auto-comment...
#include "system.h" ...!includes auto-comment...
#include "argmatch.h" ...!includes auto-comment...
#include "argv-iter.h" ...!includes auto-comment...
#include "di-set.h" ...!includes auto-comment...
#include "die.h" ...!includes auto-comment...
#include "error.h" ...!includes auto-comment...
#include "exclude.h" ...!includes auto-comment...
#include "fprintftime.h" ...!includes auto-comment...
#include "human.h" ...!includes auto-comment...
#include "mountlist.h" ...!includes auto-comment...
#include "quote.h" ...!includes auto-comment...
#include "stat-size.h" ...!includes auto-comment...
#include "stat-time.h" ...!includes auto-comment...
#include "stdio--.h" ...!includes auto-comment...
#include "xfts.h" ...!includes auto-comment...
#include "xstrtol.h" ...!includes auto-comment...
extern bool fts_debug; Line 47
/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "du" Line 50
#define AUTHORS \ Line 52
proper_name ("Torbjorn Granlund"), \ Line 53
proper_name ("David MacKenzie"), \ Line 54
proper_name ("Paul Eggert"), \ Line 55
proper_name ("Jim Meyering") Line 56
#if DU_DEBUG Line 58
# define FTS_CROSS_CHECK(Fts) fts_cross_check (Fts) Line 59
#else Line 60
# define FTS_CROSS_CHECK(Fts) Line 61
#endif Line 62
/* A set of dev/ino pairs to help identify files and directories
whose sizes have already been counted. */
static struct di_set *di_files; Line 66
/* A set containing a dev/ino pair for each local mount point directory. */
static struct di_set *di_mnt; Line 69
/* Keep track of the preceding "level" (depth in hierarchy)
from one call of process_file to the next. */
static size_t prev_level; Line 73
/* Define a class for collecting directory information. */
struct duinfo Line 76
{
/* Size of files in directory. */
uintmax_t size; Line 79
/* Number of inodes in directory. */
uintmax_t inodes; Line 82
/* Latest timestamp found. If tmax.tv_sec == TYPE_MINIMUM (time_t)
&& tmax.tv_nsec < 0, no timestamp has been found. */
struct timespec tmax; Line 86
};
/* Initialize directory data. */
static inline void Line 90
duinfo_init (struct duinfo *a) Line 91
{
a->size = 0; Line 93
a->inodes = 0; Line 94
a->tmax.tv_sec = TYPE_MINIMUM (time_t); Line 95
a->tmax.tv_nsec = -1; Line 96
} Block 2
/* Set directory data. */
static inline void Line 100
duinfo_set (struct duinfo *a, uintmax_t size, struct timespec tmax) Line 101
{
a->size = size; Line 103
a->inodes = 1; Line 104
a->tmax = tmax; Line 105
} Block 3
/* Accumulate directory data. */
static inline void Line 109
duinfo_add (struct duinfo *a, struct duinfo const *b) Line 110
{
uintmax_t sum = a->size + b->size; Line 112
a->size = a->size <= sum ? sum : UINTMAX_MAX; Line 113
a->inodes = a->inodes + b->inodes; Line 114
if (timespec_cmp (a->tmax, b->tmax) < 0) Line 115
a->tmax = b->tmax; Line 116
} Block 4
/* A structure for per-directory level information. */
struct dulevel Line 120
{
/* Entries in this directory. */
struct duinfo ent; Line 123
/* Total for subdirectories. */
struct duinfo subdir; Line 126
};
/* If true, display counts for all files, not just directories. */
static bool opt_all = false; Line 130
/* If true, rather than using the disk usage of each file,
use the apparent size (a la stat.st_size). */
static bool apparent_size = false; Line 134
/* If true, count each hard link of files with multiple links. */
static bool opt_count_all = false; Line 137
/* If true, hash all files to look for hard links. */
static bool hash_all; Line 140
/* If true, output the NUL byte instead of a newline at the end of each line. */
static bool opt_nul_terminate_output = false; Line 143
/* If true, print a grand total at the end. */
static bool print_grand_total = false; Line 146
/* If nonzero, do not add sizes of subdirectories. */
static bool opt_separate_dirs = false; Line 149
/* Show the total for each directory (and file if --all) that is at
most MAX_DEPTH levels down from the root of the hierarchy. The root
is at level 0, so 'du --max-depth=0' is equivalent to 'du -s'. */
static size_t max_depth = SIZE_MAX; Line 154
/* Only output entries with at least this SIZE if positive,
or at most if negative. See --threshold option. */
static intmax_t opt_threshold = 0; Line 158
/* Human-readable options for output. */
static int human_output_opts; Line 161
/* Output inodes count instead of blocks used. */
static bool opt_inodes = false; Line 164
/* If true, print most recently modified date, using the specified format. */
static bool opt_time = false; Line 167
/* Type of time to display. controlled by --time. */
enum time_type Line 171
{
time_mtime, /* default */ Line 173
time_ctime, Line 174
time_atime Line 175
}; Block 6
static enum time_type time_type = time_mtime; Line 178
/* User specified date / time style */
static char const *time_style = NULL; Line 181
/* Format used to display date / time. Controlled by --time-style */
static char const *time_format = NULL; Line 184
/* The local time zone rules, as per the TZ environment variable. */
static timezone_t localtz; Line 187
/* The units to use when printing sizes. */
static uintmax_t output_block_size; Line 190
/* File name patterns to exclude. */
static struct exclude *exclude; Line 193
/* Grand total size of all args, in bytes. Also latest modified date. */
static struct duinfo tot_dui; Line 196
#define IS_DIR_TYPE(Type) \ Line 198
((Type) == FTS_DP \ Line 199
|| (Type) == FTS_DNR) Line 200
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum Line 204
{
APPARENT_SIZE_OPTION = CHAR_MAX + 1, Line 206
EXCLUDE_OPTION, Line 207
FILES0_FROM_OPTION, Line 208
HUMAN_SI_OPTION, Line 209
FTS_DEBUG, Line 210
TIME_OPTION, Line 211
TIME_STYLE_OPTION, Line 212
INODES_OPTION Line 213
}; Block 7
static struct option const long_options[] = Line 216
{
{"all", no_argument, NULL, 'a'}, Line 218
{"apparent-size", no_argument, NULL, APPARENT_SIZE_OPTION}, Line 219
{"block-size", required_argument, NULL, 'B'}, Line 220
{"bytes", no_argument, NULL, 'b'}, Line 221
{"count-links", no_argument, NULL, 'l'}, Line 222
/* {"-debug", no_argument, NULL, FTS_DEBUG}, */
{"dereference", no_argument, NULL, 'L'}, Line 224
{"dereference-args", no_argument, NULL, 'D'}, Line 225
{"exclude", required_argument, NULL, EXCLUDE_OPTION}, Line 226
{"exclude-from", required_argument, NULL, 'X'}, Line 227
{"files0-from", required_argument, NULL, FILES0_FROM_OPTION}, Line 228
{"human-readable", no_argument, NULL, 'h'}, Line 229
{"inodes", no_argument, NULL, INODES_OPTION}, Line 230
{"si", no_argument, NULL, HUMAN_SI_OPTION}, Line 231
{"max-depth", required_argument, NULL, 'd'}, Line 232
{"null", no_argument, NULL, '0'}, Line 233
{"no-dereference", no_argument, NULL, 'P'}, Line 234
{"one-file-system", no_argument, NULL, 'x'}, Line 235
{"separate-dirs", no_argument, NULL, 'S'}, Line 236
{"summarize", no_argument, NULL, 's'}, Line 237
{"total", no_argument, NULL, 'c'}, Line 238
{"threshold", required_argument, NULL, 't'}, Line 239
{"time", optional_argument, NULL, TIME_OPTION}, Line 240
{"time-style", required_argument, NULL, TIME_STYLE_OPTION}, Line 241
{GETOPT_HELP_OPTION_DECL}, Line 242
{GETOPT_VERSION_OPTION_DECL}, Line 243
{NULL, 0, NULL, 0} Line 244
}; Block 8
static char const *const time_args[] = Line 247
{
"atime", "access", "use", "ctime", "status", NULL Line 249
}; Block 9
static enum time_type const time_types[] = Line 251
{
time_atime, time_atime, time_atime, time_ctime, time_ctime Line 253
}; Block 10
ARGMATCH_VERIFY (time_args, time_types); Line 255
/* 'full-iso' uses full ISO-style dates and times. 'long-iso' uses longer
ISO-style timestamps, though shorter than 'full-iso'. 'iso' uses shorter
ISO-style timestamps. */
enum time_style Line 260
{
full_iso_time_style, /* --time-style=full-iso */ Line 262
long_iso_time_style, /* --time-style=long-iso */ Line 263
iso_time_style /* --time-style=iso */ Line 264
}; Block 11
static char const *const time_style_args[] = Line 267
{
"full-iso", "long-iso", "iso", NULL Line 269
}; Block 12
static enum time_style const time_style_types[] = Line 271
{
full_iso_time_style, long_iso_time_style, iso_time_style Line 273
}; Block 13
ARGMATCH_VERIFY (time_style_args, time_style_types); Line 275
void Line 277
usage (int status) Line 278
{
if (status != EXIT_SUCCESS) Line 280
emit_try_help (); ...!common auto-comment...
else Line 282
{
printf (_("\ Line 284
Usage: %s [OPTION]... [FILE]...\n\ Line 285
or: %s [OPTION]... --files0-from=F\n\ Line 286
"), program_name, program_name); Line 287
fputs (_("\ Line 288
Summarize disk usage of the set of FILEs, recursively for directories.\n\ Line 289
"), stdout); Line 290
emit_mandatory_arg_note (); ...!common auto-comment...
fputs (_("\ Line 294
-0, --null end each output line with NUL, not newline\n\ Line 295
-a, --all write counts for all files, not just directories\n\ Line 296
--apparent-size print apparent sizes, rather than disk usage; although\ Line 297
\n\
the apparent size is usually smaller, it may be\n\ Line 299
larger due to holes in ('sparse') files, internal\n\ Line 300
fragmentation, indirect blocks, and the like\n\ Line 301
"), stdout); Line 302
fputs (_("\ Line 303
-B, --block-size=SIZE scale sizes by SIZE before printing them; e.g.,\n\ Line 304
'-BM' prints sizes in units of 1,048,576 bytes;\n\ Line 305
see SIZE format below\n\ Line 306
-b, --bytes equivalent to '--apparent-size --block-size=1'\n\ Line 307
-c, --total produce a grand total\n\ Line 308
-D, --dereference-args dereference only symlinks that are listed on the\n\ Line 309
command line\n\ Line 310
-d, --max-depth=N print the total for a directory (or file, with --all)\n\Line 311
only if it is N or fewer levels below the command\n\ Line 312
line argument; --max-depth=0 is the same as\n\ Line 313
--summarize\n\ Line 314
"), stdout); Line 315
fputs (_("\ Line 316
--files0-from=F summarize disk usage of the\n\ Line 317
NUL-terminated file names specified in file F;\n\ Line 318
if F is -, then read names from standard input\n\ Line 319
-H equivalent to --dereference-args (-D)\n\ Line 320
-h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\Line 321
\n\
--inodes list inode usage information instead of block usage\n\ Line 323
"), stdout); Line 324
fputs (_("\ Line 325
-k like --block-size=1K\n\ Line 326
-L, --dereference dereference all symbolic links\n\ Line 327
-l, --count-links count sizes many times if hard linked\n\ Line 328
-m like --block-size=1M\n\ Line 329
"), stdout); Line 330
fputs (_("\ Line 331
-P, --no-dereference don't follow any symbolic links (this is the default)\n\Line 332
-S, --separate-dirs for directories do not include size of subdirectories\n\Line 333
--si like -h, but use powers of 1000 not 1024\n\ Line 334
-s, --summarize display only a total for each argument\n\ Line 335
"), stdout); Line 336
fputs (_("\ Line 337
-t, --threshold=SIZE exclude entries smaller than SIZE if positive,\n\ Line 338
or entries greater than SIZE if negative\n\ Line 339
--time show time of the last modification of any file in the\n\Line 340
directory, or any of its subdirectories\n\ Line 341
--time=WORD show time as WORD instead of modification time:\n\ Line 342
atime, access, use, ctime or status\n\ Line 343
--time-style=STYLE show times using STYLE, which can be:\n\ Line 344
full-iso, long-iso, iso, or +FORMAT;\n\ Line 345
FORMAT is interpreted like in 'date'\n\ Line 346
"), stdout); Line 347
fputs (_("\ Line 348
-X, --exclude-from=FILE exclude files that match any pattern in FILE\n\ Line 349
--exclude=PATTERN exclude files that match PATTERN\n\ Line 350
-x, --one-file-system skip directories on different file systems\n\ Line 351
"), stdout); Line 352
fputs (HELP_OPTION_DESCRIPTION, stdout); Line 353
fputs (VERSION_OPTION_DESCRIPTION, stdout); Line 354
emit_blocksize_note ("DU"); Line 355
emit_size_note (); Line 356
emit_ancillary_info (PROGRAM_NAME); Line 357
}
exit (status); Line 359
} Block 14
/* Try to insert the INO/DEV pair into DI_SET.
Return true if the pair is successfully inserted,
false if the pair was already there. */
static bool Line 365
hash_ins (struct di_set *di_set, ino_t ino, dev_t dev) Line 366
{
int inserted = di_set_insert (di_set, dev, ino); Line 368
if (inserted < 0) Line 369
xalloc_die (); ...!common auto-comment...
return inserted; Line 371
} Block 15
/* FIXME: this code is nearly identical to code in date.c */
/* Display the date and time in WHEN according to the format specified
in FORMAT. */
static void Line 378
show_date (const char *format, struct timespec when, timezone_t tz) Line 379
{
struct tm tm; Line 381
if (localtime_rz (tz, &when.tv_sec, &tm)) Line 382
fprintftime (stdout, format, &tm, tz, when.tv_nsec); Line 383
else Line 384
{
char buf[INT_BUFSIZE_BOUND (intmax_t)]; Line 386
char *when_str = timetostr (when.tv_sec, buf); Line 387
error (0, 0, _("time %s is out of range"), quote (when_str)); Line 388
fputs (when_str, stdout); Line 389
}
} Block 16
/* Print N_BYTES. Convert it to a readable value before printing. */
static void Line 395
print_only_size (uintmax_t n_bytes) Line 396
{
char buf[LONGEST_HUMAN_READABLE + 1]; Line 398
fputs ((n_bytes == UINTMAX_MAX Line 399
? _("Infinity") Line 400
: human_readable (n_bytes, buf, human_output_opts, Line 401
1, output_block_size)), Line 402
stdout); Line 403
} Block 17
/* Print size (and optionally time) indicated by *PDUI, followed by STRING. */
static void Line 408
print_size (const struct duinfo *pdui, const char *string) Line 409
{
print_only_size (opt_inodes Line 411
? pdui->inodes Line 412
: pdui->size); Line 413
if (opt_time) Line 415
{
putchar ('\t'); Line 417
show_date (time_format, pdui->tmax, localtz); Line 418
}
printf ("\t%s%c", string, opt_nul_terminate_output ? '\0' : '\n'); Line 420
fflush (stdout); Line 421
} Block 18
/* Fill the di_mnt set with local mount point dev/ino pairs. */
static void Line 426
fill_mount_table (void) Line 427
{
struct mount_entry *mnt_ent = read_file_system_list (false); Line 429
while (mnt_ent) Line 430
{
struct mount_entry *mnt_free; Line 432
if (!mnt_ent->me_remote && !mnt_ent->me_dummy) Line 433
{
struct stat buf; Line 435
if (!stat (mnt_ent->me_mountdir, &buf)) Line 436...!syscalls auto-comment...
hash_ins (di_mnt, buf.st_ino, buf.st_dev); Line 437
else Line 438
{
/* Ignore stat failure. False positives are too common.
E.g., "Permission denied" on /run/user/<name>/gvfs. */
}
}
mnt_free = mnt_ent; Line 445
mnt_ent = mnt_ent->me_next; Line 446
free_mount_entry (mnt_free); Line 447
}
} Block 19
/* This function checks whether any of the directories in the cycle that
fts detected is a mount point. */
static bool Line 454
mount_point_in_fts_cycle (FTSENT const *ent) Line 455
{
FTSENT const *cycle_ent = ent->fts_cycle; Line 457
if (!di_mnt) Line 459
{
/* Initialize the set of dev,inode pairs. */
di_mnt = di_set_alloc (); Line 462
if (!di_mnt) Line 463
xalloc_die (); ...!common auto-comment...
fill_mount_table (); Line 466
}
while (ent && ent != cycle_ent) Line 469
{
if (di_set_lookup (di_mnt, ent->fts_statp->st_dev, Line 471
ent->fts_statp->st_ino) > 0) Line 472
{
return true; Line 474
}
ent = ent->fts_parent; Line 476
}
return false; Line 479
} Block 20
/* This function is called once for every file system object that fts
encounters. fts does a depth-first traversal. This function knows
that and accumulates per-directory totals based on changes in
the depth of the current entry. It returns true on success. */
static bool Line 487
process_file (FTS *fts, FTSENT *ent) Line 488
{
bool ok = true; Line 490
struct duinfo dui; Line 491
struct duinfo dui_to_print; Line 492
size_t level; Line 493
static size_t n_alloc; Line 494
/* First element of the structure contains:
The sum of the st_size values of all entries in the single directory
at the corresponding level. Although this does include the st_size
corresponding to each subdirectory, it does not include the size of
any file in a subdirectory. Also corresponding last modified date.
Second element of the structure contains:
The sum of the sizes of all entries in the hierarchy at or below the
directory at the specified level. */
static struct dulevel *dulvl; Line 503
const char *file = ent->fts_path; Line 505
const struct stat *sb = ent->fts_statp; Line 506
int info = ent->fts_info; Line 507
if (info == FTS_DNR) Line 509
{
/* An error occurred, but the size is known, so count it. */
error (0, ent->fts_errno, _("cannot read directory %s"), quoteaf (file)); Line 512
ok = false; Line 513
}
else if (info != FTS_DP) Line 515
{
bool excluded = excluded_file_name (exclude, file); Line 517
if (! excluded) Line 518
{
/* Make the stat buffer *SB valid, or fail noisily. */
if (info == FTS_NSOK) Line 522
{
fts_set (fts, ent, FTS_AGAIN); Line 524
FTSENT const *e = fts_read (fts); Line 525...!syscalls auto-comment...
assert (e == ent); Line 526
info = ent->fts_info; Line 527
}
if (info == FTS_NS || info == FTS_SLNONE) Line 530
{
error (0, ent->fts_errno, _("cannot access %s"), quoteaf (file)); Line 532
return false; Line 533
}
/* The --one-file-system (-x) option cannot exclude anything
specified on the command-line. By definition, it can exclude
a file or directory only when its device number is different
from that of its just-processed parent directory, and du does
not process the parent of a command-line argument. */
if (fts->fts_options & FTS_XDEV Line 541
&& FTS_ROOTLEVEL < ent->fts_level Line 542
&& fts->fts_dev != sb->st_dev) Line 543
excluded = true; Line 544
}
if (excluded Line 547
|| (! opt_count_all Line 548
&& (hash_all || (! S_ISDIR (sb->st_mode) && 1 < sb->st_nlink)) Line 549
&& ! hash_ins (di_files, sb->st_ino, sb->st_dev))) Line 550
{
/* If ignoring a directory in preorder, skip its children.
Ignore the next fts_read output too, as it's a postorder
visit to the same directory. */
if (info == FTS_D) Line 555
{
fts_set (fts, ent, FTS_SKIP); Line 557
FTSENT const *e = fts_read (fts); Line 558...!syscalls auto-comment...
assert (e == ent); Line 559
}
return true; Line 562
}
switch (info) Line 565
{
case FTS_D: Line 567
return true; Line 568
case FTS_ERR: Line 570
/* An error occurred, but the size is known, so count it. */
error (0, ent->fts_errno, "%s", quotef (file)); Line 572
ok = false; Line 573
break; Line 574
case FTS_DC: Line 576
/* If not following symlinks and not a (bind) mount point. */
if (cycle_warning_required (fts, ent) Line 578
&& ! mount_point_in_fts_cycle (ent)) Line 579
{
emit_cycle_warning (file); Line 581
return false; Line 582
}
return true; Line 584
}
}
duinfo_set (&dui, Line 588
(apparent_size Line 589
? MAX (0, sb->st_size) Line 590
: (uintmax_t) ST_NBLOCKS (*sb) * ST_NBLOCKSIZE), Line 591
(time_type == time_mtime ? get_stat_mtime (sb) Line 592
: time_type == time_atime ? get_stat_atime (sb) Line 593
: get_stat_ctime (sb))); Line 594
level = ent->fts_level; Line 596
dui_to_print = dui; Line 597
if (n_alloc == 0) Line 599
{
n_alloc = level + 10; Line 601
dulvl = xcalloc (n_alloc, sizeof *dulvl); Line 602
}
else Line 604
{
if (level == prev_level) Line 606
{
/* This is usually the most common case. Do nothing. */
}
else if (level > prev_level) Line 610
{
/* Descending the hierarchy.
Clear the accumulators for *all* levels between prev_level
and the current one. The depth may change dramatically,
e.g., from 1 to 10. */
if (n_alloc <= level) Line 617
{
dulvl = xnrealloc (dulvl, level, 2 * sizeof *dulvl); Line 619
n_alloc = level * 2; Line 620
}
for (size_t i = prev_level + 1; i <= level; i++) Line 623
{
duinfo_init (&dulvl[i].ent); Line 625
duinfo_init (&dulvl[i].subdir); Line 626
}
}
else /* level < prev_level */ Line 629
{
/* Ascending the hierarchy.
Process a directory only after all entries in that
directory have been processed. When the depth decreases,
propagate sums from the children (prev_level) to the parent.
Here, the current level is always one smaller than the
previous one. */
assert (level == prev_level - 1); Line 637
duinfo_add (&dui_to_print, &dulvl[prev_level].ent); Line 638
if (!opt_separate_dirs) Line 639
duinfo_add (&dui_to_print, &dulvl[prev_level].subdir); Line 640
duinfo_add (&dulvl[level].subdir, &dulvl[prev_level].ent); Line 641
duinfo_add (&dulvl[level].subdir, &dulvl[prev_level].subdir); Line 642
}
}
prev_level = level; Line 646
/* Let the size of a directory entry contribute to the total for the
containing directory, unless --separate-dirs (-S) is specified. */
if (! (opt_separate_dirs && IS_DIR_TYPE (info))) Line 650
duinfo_add (&dulvl[level].ent, &dui); Line 651
/* Even if this directory is unreadable or we can't chdir into it,
do let its size contribute to the total. */
duinfo_add (&tot_dui, &dui); Line 655
if ((IS_DIR_TYPE (info) && level <= max_depth) Line 657
|| (opt_all && level <= max_depth) Line 658
|| level == 0) Line 659
{
/* Print or elide this entry according to the --threshold option. */
uintmax_t v = opt_inodes ? dui_to_print.inodes : dui_to_print.size; Line 662
if (opt_threshold < 0 Line 663
? v <= -opt_threshold Line 664
: v >= opt_threshold) Line 665
print_size (&dui_to_print, file); Line 666
}
return ok; Line 669
} Block 21
/* Recursively print the sizes of the directories (and, if selected, files)
named in FILES, the last entry of which is NULL.
BIT_FLAGS controls how fts works.
Return true if successful. */
static bool Line 677
du_files (char **files, int bit_flags) Line 678
{
bool ok = true; Line 680
if (*files) Line 682
{
FTS *fts = xfts_open (files, bit_flags, NULL); Line 684...!syscalls auto-comment...
while (1) Line 686
{
FTSENT *ent; Line 688
ent = fts_read (fts); Line 690...!syscalls auto-comment...
if (ent == NULL) Line 691
{
if (errno != 0) Line 693
{
error (0, errno, _("fts_read failed: %s"), Line 695
quotef (fts->fts_path)); Line 696
ok = false; Line 697
}
/* When exiting this loop early, be careful to reset the
global, prev_level, used in process_file. Otherwise, its
(level == prev_level - 1) assertion could fail. */
prev_level = 0; Line 703
break; Line 704
}
FTS_CROSS_CHECK (fts); Line 706
ok &= process_file (fts, ent); Line 708
}
if (fts_close (fts) != 0) Line 711...!syscalls auto-comment...
{
error (0, errno, _("fts_close failed")); Line 713
ok = false; Line 714
}
}
return ok; Line 718
} Block 22
int
main (int argc, char **argv) Line 722
{
char *cwd_only[2]; Line 724
bool max_depth_specified = false; Line 725
bool ok = true; Line 726
char *files_from = NULL; Line 727
/* Bit flags that control how fts works. */
int bit_flags = FTS_NOSTAT; Line 730
/* Select one of the three FTS_ options that control if/when
to follow a symlink. */
int symlink_deref_bits = FTS_PHYSICAL; Line 734
/* If true, display only a total for each argument. */
bool opt_summarize_only = false; Line 737
cwd_only[0] = bad_cast ("."); Line 739
cwd_only[1] = NULL; Line 740
initialize_main (&argc, &argv); VMS-specific entry point handling wildcard expansion
set_program_name (argv[0]); Retains program name and discards path
setlocale (LC_ALL, ""); Sets up internationalization (i18n)
bindtextdomain (PACKAGE, LOCALEDIR); Assigns i18n directorySets text domain for _() [gettext()] function
textdomain (PACKAGE); Sets text domain for _() [gettext()] function
atexit (close_stdout); Close stdout on exit (see gnulib)
exclude = new_exclude (); Line 750
human_options (getenv ("DU_BLOCK_SIZE"), Line 752
&human_output_opts, &output_block_size); Line 753
while (true) Line 755
{
int oi = -1; Line 757
int c = getopt_long (argc, argv, "0abd:chHklmst:xB:DLPSX:", Line 758
long_options, &oi); Line 759
if (c == -1) Line 760
break; Line 761
switch (c) Line 763
{
#if DU_DEBUG Line 765
case FTS_DEBUG: Line 766
fts_debug = true; Line 767
break; Line 768
#endif Line 769
case '0': Line 771
opt_nul_terminate_output = true; Line 772
break; Line 773
case 'a': Line 775
opt_all = true; Line 776
break; Line 777
case APPARENT_SIZE_OPTION: Line 779
apparent_size = true; Line 780
break; Line 781
case 'b': Line 783
apparent_size = true; Line 784
human_output_opts = 0; Line 785
output_block_size = 1; Line 786
break; Line 787
case 'c': Line 789
print_grand_total = true; Line 790
break; Line 791
case 'h': Line 793
human_output_opts = human_autoscale | human_SI | human_base_1024; Line 794
output_block_size = 1; Line 795
break; Line 796
case HUMAN_SI_OPTION: Line 798
human_output_opts = human_autoscale | human_SI; Line 799
output_block_size = 1; Line 800
break; Line 801
case 'k': Line 803
human_output_opts = 0; Line 804
output_block_size = 1024; Line 805
break; Line 806
case 'd': /* --max-depth=N */ Line 808
{
unsigned long int tmp_ulong; Line 810
if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) == LONGINT_OK Line 811
&& tmp_ulong <= SIZE_MAX) Line 812
{
max_depth_specified = true; Line 814
max_depth = tmp_ulong; Line 815
}
else Line 817
{
error (0, 0, _("invalid maximum depth %s"), Line 819
quote (optarg)); Line 820
ok = false; Line 821
}
}
break; Line 824
case 'm': Line 826
human_output_opts = 0; Line 827
output_block_size = 1024 * 1024; Line 828
break; Line 829
case 'l': Line 831
opt_count_all = true; Line 832
break; Line 833
case 's': Line 835
opt_summarize_only = true; Line 836
break; Line 837
case 't': Line 839
{
enum strtol_error e; Line 841
e = xstrtoimax (optarg, NULL, 0, &opt_threshold, "kKmMGTPEZY0"); Line 842
if (e != LONGINT_OK) Line 843
xstrtol_fatal (e, oi, c, long_options, optarg); Line 844
if (opt_threshold == 0 && *optarg == '-') Line 845
{
/* Do not allow -0, as this wouldn't make sense anyway. */
die (EXIT_FAILURE, 0, _("invalid --threshold argument '-0'")); Line 848
}
}
break; Line 851
case 'x': Line 853
bit_flags |= FTS_XDEV; Line 854
break; Line 855
case 'B': Line 857
{
enum strtol_error e = human_options (optarg, &human_output_opts, Line 859
&output_block_size); Line 860
if (e != LONGINT_OK) Line 861
xstrtol_fatal (e, oi, c, long_options, optarg); Line 862
}
break; Line 864
case 'H': /* NOTE: before 2008-12, -H was equivalent to --si. */ Line 866
case 'D': Line 867
symlink_deref_bits = FTS_COMFOLLOW | FTS_PHYSICAL; Line 868
break; Line 869
case 'L': /* --dereference */ Line 871
symlink_deref_bits = FTS_LOGICAL; Line 872
break; Line 873
case 'P': /* --no-dereference */ Line 875
symlink_deref_bits = FTS_PHYSICAL; Line 876
break; Line 877
case 'S': Line 879
opt_separate_dirs = true; Line 880
break; Line 881
case 'X': Line 883
if (add_exclude_file (add_exclude, exclude, optarg, Line 884
EXCLUDE_WILDCARDS, '\n')) Line 885
{
error (0, errno, "%s", quotef (optarg)); Line 887
ok = false; Line 888
}
break; Line 890
case FILES0_FROM_OPTION: Line 892
files_from = optarg; Line 893
break; Line 894
case EXCLUDE_OPTION: Line 896
add_exclude (exclude, optarg, EXCLUDE_WILDCARDS); Line 897
break; Line 898
case INODES_OPTION: Line 900
opt_inodes = true; Line 901
break; Line 902
case TIME_OPTION: Line 904
opt_time = true; Line 905
time_type = Line 906
(optarg Line 907
? XARGMATCH ("--time", optarg, time_args, time_types) Line 908
: time_mtime); Line 909
localtz = tzalloc (getenv ("TZ")); Line 910
break; Line 911
case TIME_STYLE_OPTION: Line 913
time_style = optarg; Line 914
break; Line 915
case_GETOPT_HELP_CHAR; Line 917
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); Line 919
default: Line 921
ok = false; Line 922
}
}
if (!ok) Line 926
usage (EXIT_FAILURE); Line 927
if (opt_all && opt_summarize_only) Line 929
{
error (0, 0, _("cannot both summarize and show all entries")); Line 931
usage (EXIT_FAILURE); Line 932
}
if (opt_summarize_only && max_depth_specified && max_depth == 0) Line 935
{
error (0, 0, Line 937
_("warning: summarizing is the same as using --max-depth=0")); Line 938
}
if (opt_summarize_only && max_depth_specified && max_depth != 0) Line 941
{
unsigned long int d = max_depth; Line 943
error (0, 0, _("warning: summarizing conflicts with --max-depth=%lu"), d);Line 944
usage (EXIT_FAILURE); Line 945
}
if (opt_summarize_only) Line 948
max_depth = 0; Line 949
if (opt_inodes) Line 951
{
if (apparent_size) Line 953
{
error (0, 0, _("warning: options --apparent-size and -b are " Line 955
"ineffective with --inodes")); Line 956
}
output_block_size = 1; Line 958
}
/* Process time style if printing last times. */
if (opt_time) Line 962
{
if (! time_style) Line 964
{
time_style = getenv ("TIME_STYLE"); Line 966
/* Ignore TIMESTYLE="locale", for compatibility with ls. */
if (! time_style || STREQ (time_style, "locale")) Line 969
time_style = "long-iso"; Line 970
else if (*time_style == '+') Line 971
{
/* Ignore anything after a newline, for compatibility
with ls. */
char *p = strchr (time_style, '\n'); Line 975
if (p) Line 976
*p = '\0'; Line 977
}
else Line 979
{
/* Ignore "posix-" prefix, for compatibility with ls. */
static char const posix_prefix[] = "posix-"; Line 982
static const size_t prefix_len = sizeof posix_prefix - 1; Line 983
while (STREQ_LEN (time_style, posix_prefix, prefix_len)) Line 984
time_style += prefix_len; Line 985
}
}
if (*time_style == '+') Line 989
time_format = time_style + 1; Line 990
else Line 991
{
switch (XARGMATCH ("time style", time_style, Line 993
time_style_args, time_style_types)) Line 994
{
case full_iso_time_style: Line 996
time_format = "%Y-%m-%d %H:%M:%S.%N %z"; Line 997
break; Line 998
case long_iso_time_style: Line 1000
time_format = "%Y-%m-%d %H:%M"; Line 1001
break; Line 1002
case iso_time_style: Line 1004
time_format = "%Y-%m-%d"; Line 1005
break; Line 1006
}
}
}
struct argv_iterator *ai; Line 1011
if (files_from) Line 1012
{
/* When using --files0-from=F, you may not specify any files
on the command-line. */
if (optind < argc) Line 1016
{
error (0, 0, _("extra operand %s"), quote (argv[optind])); Line 1018
fprintf (stderr, "%s\n", Line 1019
_("file operands cannot be combined with --files0-from")); Line 1020
usage (EXIT_FAILURE); Line 1021
}
if (! (STREQ (files_from, "-") || freopen (files_from, "r", stdin))) Line 1024...!syscalls auto-comment...
die (EXIT_FAILURE, errno, _("cannot open %s for reading"), Line 1025
quoteaf (files_from)); Line 1026
ai = argv_iter_init_stream (stdin); Line 1028
/* It's not easy here to count the arguments, so assume the
worst. */
hash_all = true; Line 1032
}
else Line 1034
{
char **files = (optind < argc ? argv + optind : cwd_only); Line 1036
ai = argv_iter_init_argv (files); Line 1037
/* Hash all dev,ino pairs if there are multiple arguments, or if
following non-command-line symlinks, because in either case a
file with just one hard link might be seen more than once. */
hash_all = (optind + 1 < argc || symlink_deref_bits == FTS_LOGICAL); Line 1042
}
if (!ai) Line 1045
xalloc_die (); ...!common auto-comment...
/* Initialize the set of dev,inode pairs. */
di_files = di_set_alloc (); Line 1049
if (!di_files) Line 1050
xalloc_die (); ...!common auto-comment...
/* If not hashing everything, process_file won't find cycles on its
own, so ask fts_read to check for them accurately. */
if (opt_count_all || ! hash_all) Line 1055
bit_flags |= FTS_TIGHT_CYCLE_CHECK; Line 1056
bit_flags |= symlink_deref_bits; Line 1058
static char *temp_argv[] = { NULL, NULL }; Line 1059
while (true) Line 1061
{
bool skip_file = false; Line 1063
enum argv_iter_err ai_err; Line 1064
char *file_name = argv_iter (ai, &ai_err); Line 1065
if (!file_name) Line 1066
{
switch (ai_err) Line 1068
{
case AI_ERR_EOF: Line 1070
goto argv_iter_done; Line 1071
case AI_ERR_READ: Line 1072
error (0, errno, _("%s: read error"), Line 1073
quotef (files_from)); Line 1074
ok = false; Line 1075
goto argv_iter_done; Line 1076
case AI_ERR_MEM: Line 1077
xalloc_die (); ...!common auto-comment...
default: Line 1079
assert (!"unexpected error code from argv_iter"); Line 1080
}
}
if (files_from && STREQ (files_from, "-") && STREQ (file_name, "-")) Line 1083
{
/* Give a better diagnostic in an unusual case:
printf - | du --files0-from=- */
error (0, 0, _("when reading file names from stdin, " Line 1087
"no file name of %s allowed"), Line 1088
quoteaf (file_name)); Line 1089
skip_file = true; Line 1090
}
/* Report and skip any empty file names before invoking fts.
This works around a glitch in fts, which fails immediately
(without looking at the other file names) when given an empty
file name. */
if (!file_name[0]) Line 1097
{
/* Diagnose a zero-length file name. When it's one
among many, knowing the record number may help.
FIXME: currently print the record number only with
--files0-from=FILE. Maybe do it for argv, too? */
if (files_from == NULL) Line 1103
error (0, 0, "%s", _("invalid zero-length file name")); Line 1104
else Line 1105
{
/* Using the standard 'filename:line-number:' prefix here is
not totally appropriate, since NUL is the separator, not NL,
but it might be better than nothing. */
unsigned long int file_number = argv_iter_n_args (ai); Line 1110
error (0, 0, "%s:%lu: %s", quotef (files_from), Line 1111
file_number, _("invalid zero-length file name")); Line 1112
}
skip_file = true; Line 1114
}
if (skip_file) Line 1117
ok = false; Line 1118
else Line 1119
{
temp_argv[0] = file_name; Line 1121
ok &= du_files (temp_argv, bit_flags); Line 1122
}
}
argv_iter_done: Line 1125
argv_iter_free (ai); Line 1127
di_set_free (di_files); Line 1128
if (di_mnt) Line 1129
di_set_free (di_mnt); Line 1130
if (files_from && (ferror (stdin) || fclose (stdin) != 0) && ok) Line 1132...!syscalls auto-comment...
die (EXIT_FAILURE, 0, _("error reading %s"), quoteaf (files_from)); Line 1133
if (print_grand_total) Line 1135
print_size (&tot_dui, _("total")); Line 1136
return ok ? EXIT_SUCCESS : EXIT_FAILURE; Line 1138
} Block 23