| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include <config.h> |
| #include <getopt.h> |
| #include <sys/types.h> |
| #include "argmatch.h" |
| #include "system.h" |
| #include "argv-iter.h" |
| #include "assure.h" |
| #include "di-set.h" |
| #include "exclude.h" |
| #include "human.h" |
| #include "mountlist.h" |
| #include "quote.h" |
| #include "show-date.h" |
| #include "stat-size.h" |
| #include "stat-time.h" |
| #include "stdio--.h" |
| #include "xfts.h" |
| #include "xstrtol.h" |
| #include "xstrtol-error.h" |
|
|
| |
| #define PROGRAM_NAME "du" |
|
|
| #define AUTHORS \ |
| proper_name_lite ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \ |
| proper_name ("David MacKenzie"), \ |
| proper_name ("Paul Eggert"), \ |
| proper_name ("Jim Meyering") |
|
|
| |
| |
| static struct di_set *di_files; |
|
|
| |
| static struct di_set *di_mnt; |
|
|
| |
| |
| static idx_t prev_level; |
|
|
| |
| struct duinfo |
| { |
| |
| uintmax_t size; |
|
|
| |
| uintmax_t inodes; |
|
|
| |
| |
| struct timespec tmax; |
| }; |
|
|
| |
| static inline void |
| duinfo_init (struct duinfo *a) |
| { |
| a->size = 0; |
| a->inodes = 0; |
| a->tmax.tv_sec = TYPE_MINIMUM (time_t); |
| a->tmax.tv_nsec = -1; |
| } |
|
|
| |
| static inline void |
| duinfo_set (struct duinfo *a, uintmax_t size, struct timespec tmax) |
| { |
| a->size = size; |
| a->inodes = 1; |
| a->tmax = tmax; |
| } |
|
|
| |
| static inline void |
| duinfo_add (struct duinfo *a, struct duinfo const *b) |
| { |
| uintmax_t sum = a->size + b->size; |
| a->size = a->size <= sum ? sum : UINTMAX_MAX; |
| a->inodes = a->inodes + b->inodes; |
| if (timespec_cmp (a->tmax, b->tmax) < 0) |
| a->tmax = b->tmax; |
| } |
|
|
| |
| struct dulevel |
| { |
| |
| struct duinfo ent; |
|
|
| |
| struct duinfo subdir; |
| }; |
|
|
| |
| static bool opt_all = false; |
|
|
| |
| |
| static bool apparent_size = false; |
|
|
| |
| static bool opt_count_all = false; |
|
|
| |
| static bool hash_all; |
|
|
| |
| static bool opt_nul_terminate_output = false; |
|
|
| |
| static bool print_grand_total = false; |
|
|
| |
| static bool opt_separate_dirs = false; |
|
|
| |
| |
| |
| static idx_t max_depth = IDX_MAX; |
|
|
| |
| |
| static intmax_t opt_threshold = 0; |
|
|
| |
| static int human_output_opts; |
|
|
| |
| static bool opt_inodes = false; |
|
|
| |
| static bool opt_time = false; |
|
|
| |
|
|
| enum time_type |
| { |
| time_mtime, |
| time_ctime, |
| time_atime |
| }; |
|
|
| static enum time_type time_type = time_mtime; |
|
|
| |
| static char const *time_style = nullptr; |
|
|
| |
| static char const *time_format = nullptr; |
|
|
| |
| static timezone_t localtz; |
|
|
| |
| static uintmax_t output_block_size; |
|
|
| |
| static struct exclude *exclude; |
|
|
| |
| static struct duinfo tot_dui; |
|
|
| #define IS_DIR_TYPE(Type) \ |
| ((Type) == FTS_DP \ |
| || (Type) == FTS_DNR) |
|
|
| |
| |
| enum |
| { |
| APPARENT_SIZE_OPTION = CHAR_MAX + 1, |
| EXCLUDE_OPTION, |
| FILES0_FROM_OPTION, |
| HUMAN_SI_OPTION, |
| #if GNULIB_FTS_DEBUG |
| FTS_DEBUG, |
| #endif |
| TIME_OPTION, |
| TIME_STYLE_OPTION, |
| INODES_OPTION |
| }; |
|
|
| static struct option const long_options[] = |
| { |
| {"all", no_argument, nullptr, 'a'}, |
| {"apparent-size", no_argument, nullptr, APPARENT_SIZE_OPTION}, |
| {"block-size", required_argument, nullptr, 'B'}, |
| {"bytes", no_argument, nullptr, 'b'}, |
| {"count-links", no_argument, nullptr, 'l'}, |
| #if GNULIB_FTS_DEBUG |
| {"-debug", no_argument, nullptr, FTS_DEBUG}, |
| #endif |
| {"dereference", no_argument, nullptr, 'L'}, |
| {"dereference-args", no_argument, nullptr, 'D'}, |
| {"exclude", required_argument, nullptr, EXCLUDE_OPTION}, |
| {"exclude-from", required_argument, nullptr, 'X'}, |
| {"files0-from", required_argument, nullptr, FILES0_FROM_OPTION}, |
| {"human-readable", no_argument, nullptr, 'h'}, |
| {"inodes", no_argument, nullptr, INODES_OPTION}, |
| {"si", no_argument, nullptr, HUMAN_SI_OPTION}, |
| {"max-depth", required_argument, nullptr, 'd'}, |
| {"null", no_argument, nullptr, '0'}, |
| {"no-dereference", no_argument, nullptr, 'P'}, |
| {"one-file-system", no_argument, nullptr, 'x'}, |
| {"separate-dirs", no_argument, nullptr, 'S'}, |
| {"summarize", no_argument, nullptr, 's'}, |
| {"total", no_argument, nullptr, 'c'}, |
| {"threshold", required_argument, nullptr, 't'}, |
| {"time", optional_argument, nullptr, TIME_OPTION}, |
| {"time-style", required_argument, nullptr, TIME_STYLE_OPTION}, |
| {GETOPT_HELP_OPTION_DECL}, |
| {GETOPT_VERSION_OPTION_DECL}, |
| {nullptr, 0, nullptr, 0} |
| }; |
|
|
| static char const *const time_args[] = |
| { |
| "atime", "access", "use", "ctime", "status", nullptr |
| }; |
| static enum time_type const time_types[] = |
| { |
| time_atime, time_atime, time_atime, time_ctime, time_ctime |
| }; |
| ARGMATCH_VERIFY (time_args, time_types); |
|
|
| |
| |
| |
| enum time_style |
| { |
| full_iso_time_style, |
| long_iso_time_style, |
| iso_time_style |
| }; |
|
|
| static char const *const time_style_args[] = |
| { |
| "full-iso", "long-iso", "iso", nullptr |
| }; |
| static enum time_style const time_style_types[] = |
| { |
| full_iso_time_style, long_iso_time_style, iso_time_style |
| }; |
| ARGMATCH_VERIFY (time_style_args, time_style_types); |
|
|
| void |
| usage (int status) |
| { |
| if (status != EXIT_SUCCESS) |
| emit_try_help (); |
| else |
| { |
| printf (_("\ |
| Usage: %s [OPTION]... [FILE]...\n\ |
| or: %s [OPTION]... --files0-from=F\n\ |
| "), program_name, program_name); |
| fputs (_("\ |
| Summarize device usage of the set of FILEs, recursively for directories.\n\ |
| "), stdout); |
|
|
| emit_mandatory_arg_note (); |
|
|
| fputs (_("\ |
| -0, --null end each output line with NUL, not newline\n\ |
| -a, --all write counts for all files, not just directories\n\ |
| --apparent-size print apparent sizes rather than device usage; although\ |
| \n\ |
| the apparent size is usually smaller, it may be\n\ |
| larger due to holes in ('sparse') files, internal\n\ |
| fragmentation, indirect blocks, and the like\n\ |
| "), stdout); |
| fputs (_("\ |
| -B, --block-size=SIZE scale sizes by SIZE before printing them; e.g.,\n\ |
| '-BM' prints sizes in units of 1,048,576 bytes;\n\ |
| see SIZE format below\n\ |
| -b, --bytes equivalent to '--apparent-size --block-size=1'\n\ |
| -c, --total produce a grand total\n\ |
| -D, --dereference-args dereference only symlinks that are listed on the\n\ |
| command line\n\ |
| -d, --max-depth=N print the total for a directory (or file, with --all)\n\ |
| only if it is N or fewer levels below the command\n\ |
| line argument; --max-depth=0 is the same as\n\ |
| --summarize\n\ |
| "), stdout); |
| fputs (_("\ |
| --files0-from=F summarize device usage of the\n\ |
| NUL-terminated file names specified in file F;\n\ |
| if F is -, then read names from standard input\n\ |
| -H equivalent to --dereference-args (-D)\n\ |
| -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\ |
| \n\ |
| --inodes list inode usage information instead of block usage\n\ |
| "), stdout); |
| fputs (_("\ |
| -k like --block-size=1K\n\ |
| -L, --dereference dereference all symbolic links\n\ |
| -l, --count-links count sizes many times if hard linked\n\ |
| -m like --block-size=1M\n\ |
| "), stdout); |
| fputs (_("\ |
| -P, --no-dereference don't follow any symbolic links (this is the default)\n\ |
| -S, --separate-dirs for directories do not include size of subdirectories\n\ |
| --si like -h, but use powers of 1000 not 1024\n\ |
| -s, --summarize display only a total for each argument\n\ |
| "), stdout); |
| fputs (_("\ |
| -t, --threshold=SIZE exclude entries smaller than SIZE if positive,\n\ |
| or entries greater than SIZE if negative\n\ |
| --time show time of the last modification of any file in the\n\ |
| directory, or any of its subdirectories\n\ |
| --time=WORD show time as WORD instead of modification time:\n\ |
| atime, access, use, ctime or status\n\ |
| --time-style=STYLE show times using STYLE, which can be:\n\ |
| full-iso, long-iso, iso, or +FORMAT;\n\ |
| FORMAT is interpreted like in 'date'\n\ |
| "), stdout); |
| fputs (_("\ |
| -X, --exclude-from=FILE exclude files that match any pattern in FILE\n\ |
| --exclude=PATTERN exclude files that match PATTERN\n\ |
| -x, --one-file-system skip directories on different file systems\n\ |
| "), stdout); |
| fputs (HELP_OPTION_DESCRIPTION, stdout); |
| fputs (VERSION_OPTION_DESCRIPTION, stdout); |
| emit_blocksize_note ("DU"); |
| emit_size_note (); |
| emit_ancillary_info (PROGRAM_NAME); |
| } |
| exit (status); |
| } |
|
|
| |
| |
| |
| static bool |
| hash_ins (struct di_set *di_set, ino_t ino, dev_t dev) |
| { |
| int inserted = di_set_insert (di_set, dev, ino); |
| if (inserted < 0) |
| xalloc_die (); |
| return inserted; |
| } |
|
|
| |
|
|
| static void |
| print_only_size (uintmax_t n_bytes) |
| { |
| char buf[LONGEST_HUMAN_READABLE + 1]; |
| fputs ((n_bytes == UINTMAX_MAX |
| ? _("Infinity") |
| : human_readable (n_bytes, buf, human_output_opts, |
| 1, output_block_size)), |
| stdout); |
| } |
|
|
| |
|
|
| static void |
| print_size (const struct duinfo *pdui, char const *string) |
| { |
| print_only_size (opt_inodes |
| ? pdui->inodes |
| : pdui->size); |
|
|
| if (opt_time) |
| { |
| putchar ('\t'); |
| bool ok = show_date (time_format, pdui->tmax, localtz); |
| if (!ok) |
| { |
| |
| char buf[INT_BUFSIZE_BOUND (intmax_t)]; |
| fputs (timetostr (pdui->tmax.tv_sec, buf), stdout); |
| } |
| } |
| printf ("\t%s%c", string, opt_nul_terminate_output ? '\0' : '\n'); |
| fflush (stdout); |
| } |
|
|
| |
|
|
| static void |
| fill_mount_table (void) |
| { |
| struct mount_entry *mnt_ent = read_file_system_list (false); |
| while (mnt_ent) |
| { |
| struct mount_entry *mnt_free; |
| if (!mnt_ent->me_remote && !mnt_ent->me_dummy) |
| { |
| struct stat buf; |
| if (!stat (mnt_ent->me_mountdir, &buf)) |
| hash_ins (di_mnt, buf.st_ino, buf.st_dev); |
| else |
| { |
| |
| |
| } |
| } |
|
|
| mnt_free = mnt_ent; |
| mnt_ent = mnt_ent->me_next; |
| free_mount_entry (mnt_free); |
| } |
| } |
|
|
| |
| |
|
|
| static bool |
| mount_point_in_fts_cycle (FTSENT const *ent) |
| { |
| FTSENT const *cycle_ent = ent->fts_cycle; |
|
|
| if (!di_mnt) |
| { |
| |
| di_mnt = di_set_alloc (); |
| if (!di_mnt) |
| xalloc_die (); |
|
|
| fill_mount_table (); |
| } |
|
|
| while (ent && ent != cycle_ent) |
| { |
| if (di_set_lookup (di_mnt, ent->fts_statp->st_dev, |
| ent->fts_statp->st_ino) > 0) |
| { |
| return true; |
| } |
| ent = ent->fts_parent; |
| } |
|
|
| return false; |
| } |
|
|
| |
| |
| |
| |
|
|
| static bool |
| process_file (FTS *fts, FTSENT *ent) |
| { |
| bool ok = true; |
| struct duinfo dui; |
| struct duinfo dui_to_print; |
| static idx_t n_alloc; |
| |
| |
| |
| |
| |
| |
| |
| |
| static struct dulevel *dulvl; |
|
|
| char const *file = ent->fts_path; |
| const struct stat *sb = ent->fts_statp; |
| int info = ent->fts_info; |
|
|
| if (info == FTS_DNR) |
| { |
| |
| error (0, ent->fts_errno, _("cannot read directory %s"), quoteaf (file)); |
| ok = false; |
| } |
| else if (info != FTS_DP) |
| { |
| bool excluded = excluded_file_name (exclude, file); |
| if (! excluded) |
| { |
| |
|
|
| if (info == FTS_NSOK) |
| { |
| fts_set (fts, ent, FTS_AGAIN); |
| MAYBE_UNUSED FTSENT const *e = fts_read (fts); |
| affirm (e == ent); |
| info = ent->fts_info; |
| } |
|
|
| if (info == FTS_NS || info == FTS_SLNONE) |
| { |
| error (0, ent->fts_errno, _("cannot access %s"), quoteaf (file)); |
| return false; |
| } |
|
|
| |
| |
| |
| |
| |
| if (fts->fts_options & FTS_XDEV |
| && FTS_ROOTLEVEL < ent->fts_level |
| && fts->fts_dev != sb->st_dev) |
| excluded = true; |
| } |
|
|
| if (excluded |
| || (! opt_count_all |
| && (hash_all || (! S_ISDIR (sb->st_mode) && 1 < sb->st_nlink)) |
| && ! hash_ins (di_files, sb->st_ino, sb->st_dev))) |
| { |
| |
| |
| |
| if (info == FTS_D) |
| { |
| fts_set (fts, ent, FTS_SKIP); |
| MAYBE_UNUSED FTSENT const *e = fts_read (fts); |
| affirm (e == ent); |
| } |
|
|
| return true; |
| } |
|
|
| switch (info) |
| { |
| case FTS_D: |
| return true; |
|
|
| case FTS_ERR: |
| |
| error (0, ent->fts_errno, "%s", quotef (file)); |
| ok = false; |
| break; |
|
|
| case FTS_DC: |
| |
| if (cycle_warning_required (fts, ent) |
| && ! mount_point_in_fts_cycle (ent)) |
| { |
| emit_cycle_warning (file); |
| return false; |
| } |
| return true; |
| } |
| } |
|
|
| duinfo_set (&dui, |
| (apparent_size |
| ? (usable_st_size (sb) ? MAX (0, sb->st_size) : 0) |
| : (uintmax_t) STP_NBLOCKS (sb) * ST_NBLOCKSIZE), |
| (time_type == time_mtime ? get_stat_mtime (sb) |
| : time_type == time_atime ? get_stat_atime (sb) |
| : get_stat_ctime (sb))); |
|
|
| idx_t level = ent->fts_level; |
| dui_to_print = dui; |
|
|
| if (n_alloc == 0) |
| { |
| n_alloc = level + 10; |
| dulvl = xcalloc (n_alloc, sizeof *dulvl); |
| } |
| else |
| { |
| if (level == prev_level) |
| { |
| |
| } |
| else if (level > prev_level) |
| { |
| |
| |
| |
| |
|
|
| if (n_alloc <= level) |
| dulvl = xpalloc (dulvl, &n_alloc, level - n_alloc + 1, -1, |
| sizeof *dulvl); |
|
|
| for (idx_t i = prev_level + 1; i <= level; i++) |
| { |
| duinfo_init (&dulvl[i].ent); |
| duinfo_init (&dulvl[i].subdir); |
| } |
| } |
| else |
| { |
| |
| |
| |
| |
| |
| |
| affirm (level == prev_level - 1); |
| duinfo_add (&dui_to_print, &dulvl[prev_level].ent); |
| if (!opt_separate_dirs) |
| duinfo_add (&dui_to_print, &dulvl[prev_level].subdir); |
| duinfo_add (&dulvl[level].subdir, &dulvl[prev_level].ent); |
| duinfo_add (&dulvl[level].subdir, &dulvl[prev_level].subdir); |
| } |
| } |
|
|
| prev_level = level; |
|
|
| |
| |
| if (! (opt_separate_dirs && IS_DIR_TYPE (info))) |
| duinfo_add (&dulvl[level].ent, &dui); |
|
|
| |
| |
| duinfo_add (&tot_dui, &dui); |
|
|
| if ((IS_DIR_TYPE (info) && level <= max_depth) |
| || (opt_all && level <= max_depth) |
| || level == 0) |
| { |
| |
| uintmax_t v = opt_inodes ? dui_to_print.inodes : dui_to_print.size; |
| if (opt_threshold < 0 |
| ? v <= -opt_threshold |
| : v >= opt_threshold) |
| print_size (&dui_to_print, file); |
| } |
|
|
| return ok; |
| } |
|
|
| |
| |
| |
| |
|
|
| static bool |
| du_files (char **files, int bit_flags) |
| { |
| bool ok = true; |
|
|
| if (*files) |
| { |
| FTS *fts = xfts_open (files, bit_flags, nullptr); |
|
|
| while (true) |
| { |
| FTSENT *ent; |
|
|
| ent = fts_read (fts); |
| if (ent == nullptr) |
| { |
| if (errno != 0) |
| { |
| error (0, errno, _("fts_read failed: %s"), |
| quotef (fts->fts_path)); |
| ok = false; |
| } |
|
|
| |
| |
| |
| prev_level = 0; |
| break; |
| } |
|
|
| #if GNULIB_FTS_DEBUG |
| if (fts_debug) |
| fts_cross_check (fts); |
| #endif |
|
|
| ok &= process_file (fts, ent); |
| } |
|
|
| if (fts_close (fts) != 0) |
| { |
| error (0, errno, _("fts_close failed")); |
| ok = false; |
| } |
| } |
|
|
| return ok; |
| } |
|
|
| int |
| main (int argc, char **argv) |
| { |
| char *cwd_only[2]; |
| bool max_depth_specified = false; |
| bool ok = true; |
| char *files_from = nullptr; |
|
|
| |
| int bit_flags = FTS_NOSTAT; |
|
|
| |
| |
| int symlink_deref_bits = FTS_PHYSICAL; |
|
|
| |
| bool opt_summarize_only = false; |
|
|
| cwd_only[0] = bad_cast ("."); |
| cwd_only[1] = nullptr; |
|
|
| initialize_main (&argc, &argv); |
| set_program_name (argv[0]); |
| setlocale (LC_ALL, ""); |
| bindtextdomain (PACKAGE, LOCALEDIR); |
| textdomain (PACKAGE); |
|
|
| atexit (close_stdout); |
|
|
| exclude = new_exclude (); |
|
|
| human_options (getenv ("DU_BLOCK_SIZE"), |
| &human_output_opts, &output_block_size); |
|
|
| while (true) |
| { |
| int oi = -1; |
| int c = getopt_long (argc, argv, "0abd:chHklmst:xB:DLPSX:", |
| long_options, &oi); |
| if (c == -1) |
| break; |
|
|
| switch (c) |
| { |
| #if GNULIB_FTS_DEBUG |
| case FTS_DEBUG: |
| fts_debug = true; |
| break; |
| #endif |
|
|
| case '0': |
| opt_nul_terminate_output = true; |
| break; |
|
|
| case 'a': |
| opt_all = true; |
| break; |
|
|
| case APPARENT_SIZE_OPTION: |
| apparent_size = true; |
| break; |
|
|
| case 'b': |
| apparent_size = true; |
| human_output_opts = 0; |
| output_block_size = 1; |
| break; |
|
|
| case 'c': |
| print_grand_total = true; |
| break; |
|
|
| case 'h': |
| human_output_opts = human_autoscale | human_SI | human_base_1024; |
| output_block_size = 1; |
| break; |
|
|
| case HUMAN_SI_OPTION: |
| human_output_opts = human_autoscale | human_SI; |
| output_block_size = 1; |
| break; |
|
|
| case 'k': |
| human_output_opts = 0; |
| output_block_size = 1024; |
| break; |
|
|
| case 'd': |
| { |
| intmax_t tmp; |
| if (xstrtoimax (optarg, nullptr, 0, &tmp, "") == LONGINT_OK |
| && tmp <= IDX_MAX) |
| { |
| max_depth_specified = true; |
| max_depth = tmp; |
| } |
| else |
| { |
| error (0, 0, _("invalid maximum depth %s"), |
| quote (optarg)); |
| ok = false; |
| } |
| } |
| break; |
|
|
| case 'm': |
| human_output_opts = 0; |
| output_block_size = 1024 * 1024; |
| break; |
|
|
| case 'l': |
| opt_count_all = true; |
| break; |
|
|
| case 's': |
| opt_summarize_only = true; |
| break; |
|
|
| case 't': |
| { |
| enum strtol_error e; |
| e = xstrtoimax (optarg, nullptr, 0, &opt_threshold, |
| "kKmMGTPEZYRQ0"); |
| if (e != LONGINT_OK) |
| xstrtol_fatal (e, oi, c, long_options, optarg); |
| if (opt_threshold == 0 && *optarg == '-') |
| { |
| |
| error (EXIT_FAILURE, 0, _("invalid --threshold argument '-0'")); |
| } |
| } |
| break; |
|
|
| case 'x': |
| bit_flags |= FTS_XDEV; |
| break; |
|
|
| case 'B': |
| { |
| enum strtol_error e = human_options (optarg, &human_output_opts, |
| &output_block_size); |
| if (e != LONGINT_OK) |
| xstrtol_fatal (e, oi, c, long_options, optarg); |
| } |
| break; |
|
|
| case 'H': |
| case 'D': |
| symlink_deref_bits = FTS_COMFOLLOW | FTS_PHYSICAL; |
| break; |
|
|
| case 'L': |
| symlink_deref_bits = FTS_LOGICAL; |
| break; |
|
|
| case 'P': |
| symlink_deref_bits = FTS_PHYSICAL; |
| break; |
|
|
| case 'S': |
| opt_separate_dirs = true; |
| break; |
|
|
| case 'X': |
| if (add_exclude_file (add_exclude, exclude, optarg, |
| EXCLUDE_WILDCARDS, '\n')) |
| { |
| error (0, errno, "%s", quotef (optarg)); |
| ok = false; |
| } |
| break; |
|
|
| case FILES0_FROM_OPTION: |
| files_from = optarg; |
| break; |
|
|
| case EXCLUDE_OPTION: |
| add_exclude (exclude, optarg, EXCLUDE_WILDCARDS); |
| break; |
|
|
| case INODES_OPTION: |
| opt_inodes = true; |
| break; |
|
|
| case TIME_OPTION: |
| opt_time = true; |
| time_type = |
| (optarg |
| ? XARGMATCH ("--time", optarg, time_args, time_types) |
| : time_mtime); |
| localtz = tzalloc (getenv ("TZ")); |
| break; |
|
|
| case TIME_STYLE_OPTION: |
| time_style = optarg; |
| break; |
|
|
| case_GETOPT_HELP_CHAR; |
|
|
| case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); |
|
|
| default: |
| ok = false; |
| } |
| } |
|
|
| if (!ok) |
| usage (EXIT_FAILURE); |
|
|
| if (opt_all && opt_summarize_only) |
| { |
| error (0, 0, _("cannot both summarize and show all entries")); |
| usage (EXIT_FAILURE); |
| } |
|
|
| if (opt_summarize_only && max_depth_specified && max_depth == 0) |
| { |
| error (0, 0, |
| _("warning: summarizing is the same as using --max-depth=0")); |
| } |
|
|
| if (opt_summarize_only && max_depth_specified && max_depth != 0) |
| { |
| error (0, 0, _("warning: summarizing conflicts with --max-depth=%td"), |
| max_depth); |
| usage (EXIT_FAILURE); |
| } |
|
|
| if (opt_summarize_only) |
| max_depth = 0; |
|
|
| if (opt_inodes) |
| { |
| if (apparent_size) |
| { |
| error (0, 0, _("warning: options --apparent-size and -b are " |
| "ineffective with --inodes")); |
| } |
| output_block_size = 1; |
| } |
|
|
| |
| if (opt_time) |
| { |
| if (! time_style) |
| { |
| time_style = getenv ("TIME_STYLE"); |
|
|
| |
| if (! time_style || streq (time_style, "locale")) |
| time_style = "long-iso"; |
| else if (*time_style == '+') |
| { |
| |
| |
| char *p = strchr (time_style, '\n'); |
| if (p) |
| *p = '\0'; |
| } |
| else |
| { |
| |
| static char const posix_prefix[] = "posix-"; |
| static const size_t prefix_len = sizeof posix_prefix - 1; |
| while (STREQ_LEN (time_style, posix_prefix, prefix_len)) |
| time_style += prefix_len; |
| } |
| } |
|
|
| if (*time_style == '+') |
| time_format = time_style + 1; |
| else |
| { |
| switch (x_timestyle_match (time_style, false, |
| time_style_args, |
| (char const *) time_style_types, |
| sizeof (*time_style_types), EXIT_FAILURE)) |
| { |
| case full_iso_time_style: |
| time_format = "%Y-%m-%d %H:%M:%S.%N %z"; |
| break; |
|
|
| case long_iso_time_style: |
| time_format = "%Y-%m-%d %H:%M"; |
| break; |
|
|
| case iso_time_style: |
| time_format = "%Y-%m-%d"; |
| break; |
| } |
| } |
| } |
|
|
| struct argv_iterator *ai; |
| if (files_from) |
| { |
| |
| |
| if (optind < argc) |
| { |
| error (0, 0, _("extra operand %s"), quote (argv[optind])); |
| fprintf (stderr, "%s\n", |
| _("file operands cannot be combined with --files0-from")); |
| usage (EXIT_FAILURE); |
| } |
|
|
| if (! (streq (files_from, "-") || freopen (files_from, "r", stdin))) |
| error (EXIT_FAILURE, errno, _("cannot open %s for reading"), |
| quoteaf (files_from)); |
|
|
| ai = argv_iter_init_stream (stdin); |
|
|
| |
| |
| hash_all = true; |
| } |
| else |
| { |
| char **files = (optind < argc ? argv + optind : cwd_only); |
| ai = argv_iter_init_argv (files); |
|
|
| |
| |
| |
| hash_all = (optind + 1 < argc || symlink_deref_bits == FTS_LOGICAL); |
| } |
|
|
| if (!ai) |
| xalloc_die (); |
|
|
| |
| di_files = di_set_alloc (); |
| if (!di_files) |
| xalloc_die (); |
|
|
| |
| |
| if (opt_count_all || ! hash_all) |
| bit_flags |= FTS_TIGHT_CYCLE_CHECK; |
|
|
| bit_flags |= symlink_deref_bits; |
| static char *temp_argv[] = { nullptr, nullptr }; |
|
|
| while (true) |
| { |
| bool skip_file = false; |
| enum argv_iter_err ai_err; |
| char *file_name = argv_iter (ai, &ai_err); |
| if (!file_name) |
| { |
| switch (ai_err) |
| { |
| case AI_ERR_EOF: |
| goto argv_iter_done; |
| case AI_ERR_READ: |
| error (0, errno, _("%s: read error"), |
| quotef (files_from)); |
| ok = false; |
| goto argv_iter_done; |
| case AI_ERR_MEM: |
| xalloc_die (); |
| case AI_ERR_OK: default: |
| affirm (!"unexpected error code from argv_iter"); |
| } |
| } |
| if (files_from && streq (files_from, "-") && streq (file_name, "-")) |
| { |
| |
| |
| error (0, 0, _("when reading file names from standard input, " |
| "no file name of %s allowed"), |
| quoteaf (file_name)); |
| skip_file = true; |
| } |
|
|
| |
| |
| |
| |
| if (!file_name[0]) |
| { |
| |
| |
| |
| |
| if (files_from == nullptr) |
| error (0, 0, "%s", _("invalid zero-length file name")); |
| else |
| { |
| |
| |
| |
| idx_t file_number = argv_iter_n_args (ai); |
| error (0, 0, "%s:%td: %s", quotef (files_from), |
| file_number, _("invalid zero-length file name")); |
| } |
| skip_file = true; |
| } |
|
|
| if (skip_file) |
| ok = false; |
| else |
| { |
| temp_argv[0] = file_name; |
| ok &= du_files (temp_argv, bit_flags); |
| } |
| } |
| argv_iter_done: |
|
|
| argv_iter_free (ai); |
| di_set_free (di_files); |
| if (di_mnt) |
| di_set_free (di_mnt); |
|
|
| if (files_from && (ferror (stdin) || fclose (stdin) != 0) && ok) |
| error (EXIT_FAILURE, 0, _("error reading %s"), quoteaf (files_from)); |
|
|
| if (print_grand_total) |
| print_size (&tot_dui, _("total")); |
|
|
| return ok ? EXIT_SUCCESS : EXIT_FAILURE; |
| } |
|
|