diff options
author | Jim Meyering <jim@meyering.net> | 2005-06-23 14:20:16 +0000 |
---|---|---|
committer | Jim Meyering <jim@meyering.net> | 2005-06-23 14:20:16 +0000 |
commit | 3cd6ee7518100773ff9dddf4559b095f62c38799 (patch) | |
tree | 884dd1bfbc0ad4eae3b913a94bf4cd0d5c548bdb /src | |
parent | fba73bf7414b698c1f011420291c5f9bd5b1ac86 (diff) | |
download | coreutils-3cd6ee7518100773ff9dddf4559b095f62c38799.tar.xz |
2005-06-14 William Brendling <wbrendling@gmail.com>
* src/du.c: Add --last-time and --time-style options.
Diffstat (limited to 'src')
-rw-r--r-- | src/du.c | 308 |
1 files changed, 265 insertions, 43 deletions
@@ -29,13 +29,14 @@ #include <getopt.h> #include <sys/types.h> #include <assert.h> - #include "system.h" +#include "argmatch.h" #include "dirname.h" /* for strip_trailing_slashes */ #include "error.h" #include "exclude.h" #include "hash.h" #include "human.h" +#include "inttostr.h" #include "quote.h" #include "quotearg.h" #include "readtokens0.h" @@ -74,6 +75,48 @@ struct entry /* A set of dev/ino pairs. */ static Hash_table *htab; +/* Define a class for collecting directory information. */ + +struct duinfo +{ + uintmax_t size; /* Size of files in directory */ + time_t dmax; /* Last modified date */ + int nsec; /* Nanoseconds part of date */ + int valid; /* Indicates that date is valid */ +}; + +/* DUINFO_INI (struct duinfo a); - Initialise duinfo structure. */ +#define DUINFO_INI(a) { (a).size = 0; (a).dmax = 0; (a).nsec = 0; (a).valid = 0; } + +/* DUINFO_SET (struct duinfo a, uintmax_t size, time_t date, int nsec) - Set structure data. */ +#define DUINFO_SET(a, fsize, fdmax, fnsec) \ + { (a).size = (fsize); (a).dmax = (fdmax); (a).nsec = fnsec; (a).valid = 1; } + +/* DUINFO_ADD (struct duinfo a, const struct duinfo b) - Accumulate directory data. */ +#define DUINFO_ADD(a, b) \ + { if ( (b).valid ) \ + { \ + (a).size += (b).size; \ + if ( ( ! (a).valid ) || ( (b).dmax > (a).dmax ) ) \ + { \ + (a).dmax = (b).dmax; \ + (a).nsec = (b).nsec; \ + (a).valid = 1; \ + } \ + else if ( ( (b).dmax == (a).dmax ) && ( (b).nsec > (a).nsec ) ) \ + { \ + (a).nsec = (b).nsec; \ + } \ + } \ + } + +/* A structure for per-directory level information */ +struct dulevel +{ + struct duinfo ent; /* Entries in this directory */ + struct duinfo subdir; /* Total for subdirectories */ +}; + /* Name under which this program was invoked. */ char *program_name; @@ -104,14 +147,34 @@ static size_t max_depth = SIZE_MAX; /* Human-readable options for output. */ static int human_output_opts; +/* If option non-zero, print most recently modified date, using the specified format */ +static int opt_last_time = 0; + +/* Type of time to display. controlled by --last-time */ + +enum time_type + { + time_mtime, /* default */ + time_ctime, + time_atime + }; + +static enum time_type time_type = time_mtime; + +/* User specified date / time style */ +static char *time_style = NULL; + +/* Format used to display date / time. Controlled by --time-style */ +static char *time_format = NULL; + /* The units to use when printing sizes. */ static uintmax_t output_block_size; /* File name patterns to exclude. */ static struct exclude *exclude; -/* Grand total size of all args, in bytes. */ -static uintmax_t tot_size = 0; +/* Grand total size of all args, in bytes. Also latest modified date. */ +static struct duinfo tot_dui = { 0, 0, 0, 0 }; #define IS_DIR_TYPE(Type) \ ((Type) == FTS_DP \ @@ -125,7 +188,9 @@ enum EXCLUDE_OPTION, FILES0_FROM_OPTION, HUMAN_SI_OPTION, - MAX_DEPTH_OPTION + MAX_DEPTH_OPTION, + LAST_TIME_OPTION, + TIME_STYLE_OPTION }; static struct option const long_options[] = @@ -151,11 +216,47 @@ static struct option const long_options[] = {"separate-dirs", no_argument, NULL, 'S'}, {"summarize", no_argument, NULL, 's'}, {"total", no_argument, NULL, 'c'}, + {"last-time", optional_argument, NULL, LAST_TIME_OPTION}, + {"time-style", required_argument, NULL, TIME_STYLE_OPTION}, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, {NULL, 0, NULL, 0} }; +static char const *const time_args[] = +{ + "atime", "access", "use", "ctime", "status", 0 +}; + +static enum time_type const time_types[] = +{ + time_atime, time_atime, time_atime, time_ctime, time_ctime +}; + +/* `full-iso' uses full ISO-style dates and times. `long-iso' uses longer + ISO-style time stamps, though shorter than `full-iso'. `iso' uses shorter + ISO-style time stamps. `locale' uses locale-dependent time stamps. */ +enum time_style + { + full_iso_time_style, /* --time-style=full-iso */ + long_iso_time_style, /* --time-style=long-iso */ + iso_time_style, /* --time-style=iso */ + locale_time_style /* --time-style=locale */ + }; + +static char const *const time_style_args[] = +{ + "full-iso", "long-iso", "iso", "locale", 0 +}; + +static enum time_style const time_style_types[] = +{ + full_iso_time_style, long_iso_time_style, iso_time_style, + locale_time_style, 0 +}; + +static char const posix_prefix[] = "posix-"; + void usage (int status) { @@ -212,6 +313,17 @@ Mandatory arguments to long options are mandatory for short options too.\n\ line argument; --max-depth=0 is the same as\n\ --summarize\n\ "), stdout); + fputs (_("\ + --last-time show time of the most recent modification of any\n\ + file in the directory, or any of its\n\ + subdirectories\n\ + --last-time=WORD show time as WORD instead of modification time:\n\ + atime, access, use, ctime or status; use\n\ + --time-style=STYLE show times using style STYLE:\n\ + full-iso, long-iso, iso, locale, +FORMAT\n\ + FORMAT is interpreted like `date';\n\ + implicity implies --last-time\n\ +"), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); fputs (_("\n\ @@ -285,6 +397,57 @@ hash_init (void) xalloc_die (); } +/* Display the date and time in PDUI according to the format specified + in TIME_FORMAT. If TIME_FORMAT is NULL, use the standard output format. + Return zero if successful. +*/ + +static int +show_date (const char *time_format, time_t when, int nsec) +{ + struct tm *tm; + char *out = NULL; + size_t out_length = 0; + + if ((time_format == NULL)||(*time_format == '\0')) + { + time_format = "%Y-%m-%d %H:%M"; + } + + tm = localtime (&when); + if (! tm) + { + char buf[INT_BUFSIZE_BOUND (intmax_t)]; + error (0, 0, _("time %s is out of range"), + (TYPE_SIGNED (time_t) + ? imaxtostr (when, buf) + : umaxtostr (when, buf))); + fputs (buf, stdout); + return 1; + } + + while (1) + { + int done; + out = x2nrealloc (out, &out_length, sizeof *out); + + /* Mark the first byte of the buffer so we can detect the case + of nstrftime producing an empty string. Otherwise, this loop + would not terminate when date was invoked like this + `LANG=de date +%p' on a system with good language support. */ + out[0] = '\1'; + + done = (nstrftime (out, out_length, time_format, tm, 0, nsec) + || out[0] == '\0'); + + if (done) break; + } + + fputs (out, stdout); + free (out); + return 0; +} + /* Print N_BYTES. Convert it to a readable value before printing. */ static void @@ -296,12 +459,18 @@ print_only_size (uintmax_t n_bytes) } /* Print N_BYTES followed by STRING on a line. + Optionally include last modified date. Convert N_BYTES to a readable value before printing. */ static void -print_size (uintmax_t n_bytes, const char *string) +print_size (const struct duinfo *pdui, const char *string) { - print_only_size (n_bytes); + print_only_size (pdui->size); + if ( opt_last_time ) + { + putchar ('\t'); + show_date (time_format, pdui->dmax, pdui->nsec); + } printf ("\t%s%c", string, opt_nul_terminate_output ? '\0' : '\n'); fflush (stdout); } @@ -315,20 +484,20 @@ static bool process_file (FTS *fts, FTSENT *ent) { bool ok; - uintmax_t size; - uintmax_t size_to_print; + struct duinfo dui; + struct duinfo dui_to_print; size_t level; static size_t prev_level; static size_t n_alloc; - /* 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. */ - static uintmax_t *sum_ent; - - /* The sum of the sizes of all entries in the hierarchy at or below the - directory at the specified level. */ - static uintmax_t *sum_subdir; + /* 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; bool print = true; const char *file = ent->fts_path; @@ -380,24 +549,30 @@ process_file (FTS *fts, FTSENT *ent) /* Note that we must not simply return here. We still have to update prev_level and maybe propagate some sums up the hierarchy. */ - size = 0; + DUINFO_INI (dui) print = false; } else { - size = (apparent_size - ? sb->st_size - : ST_NBLOCKS (*sb) * ST_NBLOCKSIZE); - } + DUINFO_SET (dui, + (apparent_size + ? sb->st_size + : ST_NBLOCKS (*sb) * ST_NBLOCKSIZE), + ( time_type == time_ctime ) ? sb->st_ctime : + ( time_type == time_atime ) ? sb->st_atime : + sb->st_mtime, + ( time_type == time_ctime ) ? TIMESPEC_NS (sb->st_ctim) : + ( time_type == time_atime ) ? TIMESPEC_NS (sb->st_atim) : + TIMESPEC_NS (sb->st_mtim)) + } level = ent->fts_level; - size_to_print = size; + dui_to_print = dui; if (n_alloc == 0) { n_alloc = level + 10; - sum_ent = xcalloc (n_alloc, sizeof *sum_ent); - sum_subdir = xcalloc (n_alloc, sizeof *sum_subdir); + dulvl = xcalloc (n_alloc, sizeof *dulvl); } else { @@ -415,16 +590,14 @@ process_file (FTS *fts, FTSENT *ent) if (n_alloc <= level) { - sum_ent = xnrealloc (sum_ent, level, 2 * sizeof *sum_ent); - sum_subdir = xnrealloc (sum_subdir, level, - 2 * sizeof *sum_subdir); + dulvl = xnrealloc (dulvl, level, 2 * sizeof *dulvl); n_alloc = level * 2; } for (i = prev_level + 1; i <= level; i++) - { - sum_ent[i] = 0; - sum_subdir[i] = 0; + { + DUINFO_INI (dulvl[i].ent) + DUINFO_INI (dulvl[i].subdir) } } else /* level < prev_level */ @@ -435,11 +608,12 @@ process_file (FTS *fts, FTSENT *ent) 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); - size_to_print += sum_ent[prev_level]; + assert (level == prev_level - 1); + DUINFO_ADD (dui_to_print, dulvl[prev_level].ent) if (!opt_separate_dirs) - size_to_print += sum_subdir[prev_level]; - sum_subdir[level] += sum_ent[prev_level] + sum_subdir[prev_level]; + 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) } } @@ -448,11 +622,11 @@ process_file (FTS *fts, FTSENT *ent) /* 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 (ent->fts_info))) - sum_ent[level] += size; + DUINFO_ADD (dulvl[level].ent, dui) /* Even if this directory is unreadable or we can't chdir into it, do let its size contribute to the total, ... */ - tot_size += size; + DUINFO_ADD (tot_dui, dui) /* ... but don't print out a total for it, since without the size(s) of any potential entries, it could be very misleading. */ @@ -468,11 +642,7 @@ process_file (FTS *fts, FTSENT *ent) if ((IS_DIR_TYPE (ent->fts_info) && level <= max_depth) || ((opt_all && level <= max_depth) || level == 0)) { - print_only_size (size_to_print); - fputc ('\t', stdout); - fputs (file, stdout); - fputc (opt_nul_terminate_output ? '\0' : '\n', stdout); - fflush (stdout); + print_size (&dui_to_print, file); } return ok; @@ -519,7 +689,7 @@ du_files (char **files, int bit_flags) } if (print_grand_total) - print_size (tot_size, _("total")); + print_size (&tot_dui, _("total")); return ok; } @@ -681,6 +851,17 @@ main (int argc, char **argv) add_exclude (exclude, optarg, EXCLUDE_WILDCARDS); break; + case LAST_TIME_OPTION: + opt_last_time = 1; + if ( optarg ) + time_type = XARGMATCH ("--last-time", optarg, time_args, time_types); + break; + + case TIME_STYLE_OPTION: + opt_last_time = 1; + time_style = optarg; + break; + case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); @@ -715,6 +896,47 @@ main (int argc, char **argv) if (opt_summarize_only) max_depth = 0; + /* Process time style if printing last times */ + if ( opt_last_time ) + { + if (! time_style ) + if (! (time_style = getenv ("TIME_STYLE"))) + time_style = "posix-long-iso"; + + while (strncmp (time_style, posix_prefix, sizeof posix_prefix - 1) == 0) + { + time_style += sizeof posix_prefix - 1; + } + + if (*time_style == '+') + { + time_format = time_style + 1; + } + else + { + switch (XARGMATCH ("time style", time_style, + time_style_args, time_style_types)) + { + 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; + + case locale_time_style: + if (hard_locale (LC_TIME)) + time_format = + dcgettext (NULL, time_format, LC_TIME); + } + } + } + if (files_from) { FILE *istream; |