[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Suggested enhancement to du command - show last modified date.
From: |
William Brendling |
Subject: |
Suggested enhancement to du command - show last modified date. |
Date: |
Thu, 23 Jun 2005 13:38:12 +0100 |
The second iteration of my patch to "du" to show last modified date follows.
> Internally, file time stamps should be maintained to nanosecond
> resolution, not just 1-second resolution.
This should be working. However my old system does not return nsec
time stamps, so I have not fully tested this.
I now use a class (well a struct and a few macros) to collect file /
directory information. This includes the nsec time stamp. I also no
longer assume that the minimum date is zero (1/1/1970). The class
should be extensible if anybody wants to collect additional
iinformation.
A second struct is used to optimise memory allocation. There is now
only one calloc/realloc.
> For consistency the time stamp format option should use the same
> option syntax as 'ls'. E.g.,
>
> du --last-time='modify' --time-style='+%Y-%m-%d'
I have adopted this (and a lot of code from "ls"). I have not bothered
with the two date formats (more or less than 6 months old), but
everything else "ls" recognises for time style should also be
recognised by "du". The TIME_STYLE environment variable is recognised,
taking only the first style if two are defined.
I have attempted to update the --help text and info file to reflect
the enhancements.
I will not have any further free time to work on this in the next few
months, so if my patch is thought worthwhile, I would appreciate it if
someone else takes up the batton at this point.
Patch follows:
Index: coreutils/ChangeLog
===================================================================
RCS file: /cvsroot/coreutils/coreutils/ChangeLog,v
retrieving revision 1.1314
diff -u -r1.1314 ChangeLog
--- coreutils/ChangeLog 2 Jun 2005 10:04:32 -0000 1.1314
+++ coreutils/ChangeLog 23 Jun 2005 11:37:47 -0000
@@ -1,3 +1,7 @@
+2005-06-14 William Brendling <address@hidden>
+
+ * src/du.c: Added --last-time and --time-style switches.
+
2005-06-02 Jim Meyering <address@hidden>
* Version 5.3.1.
Index: coreutils/doc/ChangeLog
===================================================================
RCS file: /cvsroot/coreutils/coreutils/doc/ChangeLog,v
retrieving revision 1.237
diff -u -r1.237 ChangeLog
--- coreutils/doc/ChangeLog 2 Jun 2005 05:00:50 -0000 1.237
+++ coreutils/doc/ChangeLog 23 Jun 2005 11:38:43 -0000
@@ -1,3 +1,7 @@
+2005-06-14 William Brendling <address@hidden>
+
+ * coreutils.texi (du invocation): New options --last-time and
--time-style.
+
2005-06-01 Paul Eggert <address@hidden>
Use "file name" when talking about file names, instead of "filename"
Index: coreutils/doc/coreutils.texi
===================================================================
RCS file: /cvsroot/coreutils/coreutils/doc/coreutils.texi,v
retrieving revision 1.259
diff -u -r1.259 coreutils.texi
--- coreutils/doc/coreutils.texi 2 Jun 2005 05:00:24 -0000 1.259
+++ coreutils/doc/coreutils.texi 23 Jun 2005 11:39:00 -0000
@@ -9006,6 +9006,30 @@
or directory that the link points to instead of the space used by
the link).
address@hidden --last-time
address@hidden --last-time
address@hidden last modified dates, displaying in @command{du}
+Show time of the most recent modification of any file in the directory,
+or any of its subdirectories.
+
address@hidden --last-time=ctime
address@hidden --last-time=status
address@hidden --last-time=use
address@hidden --last-time
address@hidden address@hidden, show the most recent}
address@hidden status address@hidden, show the most recent}
address@hidden use address@hidden, show the most recent}
+Show the most recent status change time (the @samp{ctime} in the inode) of
+any file in the directory, instead of the modification time.
+
address@hidden --last-time=atime
address@hidden --last-time=access
address@hidden --last-time
address@hidden address@hidden, show the most recent}
address@hidden access address@hidden, show the most recent}
+Show the most recent access time (the @samp{atime} in the inode) of
+any file in the directory, instead of the modification time.
+
@item -P
@itemx --no-dereference
@opindex -P
@@ -9051,6 +9075,78 @@
Report the size of each directory separately, not including the sizes
of subdirectories.
address@hidden address@hidden
address@hidden --time-style
address@hidden time style
+Implicity selects the option @option{--last-time} to show last modified
+date if an explicit @option{--last-time} option has not been selected.
+List timestamps in style @var{style}. The @var{style} should
+be one of the following:
+
address@hidden @samp
address@hidden address@hidden
address@hidden LC_TIME
+List timestamps using @var{format}, where @var{format} is interpreted
+like the format argument of @command{date} (@pxref{date invocation}).
+For example, @option{--time-style="+%Y-%m-%d %H:%M:%S"} causes
address@hidden to list timestamps like @samp{2002-03-30 23:45:56}. As
+with @command{date}, @var{format}'s interpretation is affected by the
address@hidden locale category.
+
address@hidden full-iso
+List timestamps in full using @acronym{ISO} 8601 date, time, and time zone
+format with nanosecond precision, e.g., @samp{2002-03-30
+23:45:56.477817180 -0700}. This style is equivalent to
address@hidden %H:%M:%S.%N %z}.
+
address@hidden long-iso
+List @acronym{ISO} 8601 date and time in minutes, e.g.,
address@hidden 23:45}. These timestamps are shorter than
address@hidden timestamps, and are usually good enough for everyday
+work. This style is equivalent to @samp{%Y-%m-%d %H:%M}.
+
address@hidden iso
+List @acronym{ISO} 8601 dates for timestamps
+
address@hidden locale
address@hidden LC_TIME
+List timestamps in a locale-dependent form. For example, a Finnish
+locale might list timestamps like @samp{maalis 30@ @ 2002}.
+Locale-dependent timestamps typically consume more space than @samp{iso}
+timestamps and are harder for programs to parse because locale
+conventions vary so widely, but they are easier for many people to read.
+
+The @env{LC_TIME} locale category specifies the timestamp format. The
+default @acronym{POSIX} locale uses timestamps like @samp{Mar 30@
+@ 2002}; in this locale, the following two @command{du} invocations are
+equivalent:
+
address@hidden
+du --last-time --time-style="+%b %e %Y"
+du --last-time --time-style="locale"
address@hidden example
+
+Other locales behave differently. For example, in a German locale,
address@hidden"locale"} might be equivalent to
address@hidden"+%e. %b %Y"}
+and might generate timestamps like @samp{30. M@"ar 2002@ }.
+
address@hidden address@hidden
address@hidden LC_TIME
+List @acronym{POSIX}-locale timestamps if the @env{LC_TIME} locale
+category is @acronym{POSIX}, @var{style} timestamps otherwise. For
+example, the default style, which is @samp{posix-long-iso}, lists
+timestamps like @samp{Mar 30@ @ 2002} when in
+the @acronym{POSIX} locale, and like @samp{2002-03-30 23:45} otherwise.
address@hidden table
+
address@hidden TIME_STYLE
+You can specify the default value of the @option{--time-style} option
+with the environment variable @env{TIME_STYLE}; if @env{TIME_STYLE} is not set
+the default style is @samp{posix-long-iso}. If @env{TIME_STYLE} contains two
+styles, separated by a newline (for @command{ls}), the @command{du} uses only
+the first of the two styles.
+
@item -x
@itemx --one-file-system
@opindex -x
Index: coreutils/src/du.c
===================================================================
RCS file: /cvsroot/coreutils/coreutils/src/du.c,v
retrieving revision 1.203
diff -u -r1.203 du.c
--- coreutils/src/du.c 2 Jun 2005 05:17:24 -0000 1.203
+++ coreutils/src/du.c 23 Jun 2005 11:39:06 -0000
@@ -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 @@
/* 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 @@
/* 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 @@
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 @@
{"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 @@
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 @@
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 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 @@
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 @@
/* 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 @@
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 @@
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 @@
/* 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 @@
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 @@
}
if (print_grand_total)
- print_size (tot_size, _("total"));
+ print_size (&tot_dui, _("total"));
return ok;
}
@@ -681,6 +851,17 @@
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 @@
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;
- Suggested enhancement to du command - show last modified date., William Brendling, 2005/06/03
- Re: Suggested enhancement to du command - show last modified date., James Youngman, 2005/06/03
- Re: Suggested enhancement to du command - show last modified date., Jim Meyering, 2005/06/04
- Re: Suggested enhancement to du command - show last modified date., William Brendling, 2005/06/07
- Re: Suggested enhancement to du command - show last modified date., Jim Meyering, 2005/06/07
- Re: Suggested enhancement to du command - show last modified date., William Brendling, 2005/06/08
- Re: Suggested enhancement to du command - show last modified date., Paul Eggert, 2005/06/08
- Re: Suggested enhancement to du command - show last modified date., William Brendling, 2005/06/09
- Re: Suggested enhancement to du command - show last modified date., Paul Eggert, 2005/06/09
- Message not available
- Suggested enhancement to du command - show last modified date.,
William Brendling <=
- Re: Suggested enhancement to du command - show last modified date., Jim Meyering, 2005/06/23
- Re: Suggested enhancement to du command - show last modified date., Paul Eggert, 2005/06/23
Re: Suggested enhancement to du command - show last modified date., Frederik Eaton, 2005/06/24