coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [coreutils] stat: reverting recent %X %Y %Z change


From: Jim Meyering
Subject: Re: [coreutils] stat: reverting recent %X %Y %Z change
Date: Thu, 28 Oct 2010 13:03:03 +0200

Jim Meyering wrote:
...
> I think I have fixed things, finally.
> Here's how my latest works:
>
>   $ touch -d '2010-10-21 18:43:33.012345678' k
>   $ src/stat -c %3:X k
>   12345678
>   $ src/stat -c %3.3:X k
>    12
>   $ src/stat -c %03.3:X k
>   012
>   $ src/stat -c %:X k
>   012345678
>   $ src/stat -c %03.3:X k
>   012
>   $ src/stat -c %010.3:X k
>   0000000012
>   $ src/stat -c %-3.3:X k
>   12
>
> I'll post code when I have tests.

Here's an incremental change (only for reference) that adds support
for the above as well as tests.  The complete change (including
this incremental one) is below.  No rush on the review.
I'm unlikely to do anything more with this until Tuesday.

>From 1fa5ce9909bdb56665bd149fe598db740358ed16 Mon Sep 17 00:00:00 2001
From: Jim Meyering <address@hidden>
Date: Wed, 27 Oct 2010 12:04:31 +0200
Subject: [PATCH] honor precision, add tests

---
 src/stat.c                  |   57 ++++++++++++++++++++++++++++++++++++++++---
 tests/Makefile.am           |    1 +
 tests/misc/stat-nanoseconds |   35 ++++++++++++++++++++++++++
 3 files changed, 89 insertions(+), 4 deletions(-)
 create mode 100755 tests/misc/stat-nanoseconds

diff --git a/src/stat.c b/src/stat.c
index 9d3db1b..a132b5f 100644
--- a/src/stat.c
+++ b/src/stat.c
@@ -70,6 +70,7 @@
 #include "stat-time.h"
 #include "strftime.h"
 #include "find-mount-point.h"
+#include "xstrtol.h"
 #include "xvasprintf.h"

 #if USE_STATVFS
@@ -471,7 +472,36 @@ epoch_sec (struct timespec t)
   return timetostr (t.tv_sec, str);
 }

-/* Output the number of nanoseconds, ARG.tv_nsec.  */
+/* Return 10^N, for N in 0..9.  Otherwise, return 0.  */
+static unsigned int
+e10 (unsigned int n)
+{
+  static unsigned int pow10[] =
+    { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 
1000000000 };
+  return n < ARRAY_CARDINALITY (pow10) ? pow10[n] : 0;
+}
+
+/* Given the modifiers, FMT, of a format string, return
+   the precision, if specified, else 0.  */
+static unsigned int
+format_precision (char *fmt, size_t n_bytes)
+{
+  char const *decimal = memchr (fmt, '.', n_bytes);
+  if (decimal == NULL)
+    return 0;
+  char const *prec = decimal + 1;
+  unsigned long tmp;
+  fmt[n_bytes] = '\0';
+  if (xstrtoul (prec, NULL, 10, &tmp, NULL) != LONGINT_OK
+      || UINT_MAX < tmp)
+    return 0;
+
+  return tmp;
+}
+
+/* Output the number of nanoseconds, ARG.tv_nsec, honoring a
+   WIDTH.PRECISION format modifier, where PRECISION specifies
+   how many leading digits(on a field of 9) to print.  */
 static void
 out_ns (char *pformat, size_t prefix_len, struct timespec arg)
 {
@@ -481,9 +511,28 @@ out_ns (char *pformat, size_t prefix_len, struct timespec 
arg)
      This is to avoid the mistake of omitting the zero padding on
      a number with fewer digits than the field width: when printing
      nanoseconds after a decimal place, the resulting floating point
-     fraction would off by a factor of 10 or more.  */
-  strcpy (pformat + prefix_len, prefix_len == 1 ? "09ld" : "ld");
-  printf (pformat, arg.tv_nsec);
+     fraction would be off by a factor of 10 or more.  */
+  unsigned long int ns = arg.tv_nsec;
+  unsigned int prec;
+  char const *fmt;
+
+  if (prefix_len == 1)
+    fmt = "09lu";
+  else
+    {
+      fmt = "lu";
+      if ((prec = format_precision (pformat + 1, prefix_len - 1)) < 9
+          && prec != 0)
+        {
+          /* Honor precision, and then shorten prefix_len so that
+             we elide the ".PREC" suffix from the format string.  */
+          ns /= e10 (9 - prec);
+          prefix_len = strchr (pformat, '.') - pformat;
+        }
+    }
+
+  strcpy (pformat + prefix_len, fmt);
+  printf (pformat, ns);
 }

 static void
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 84db367..dd1c509 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -244,6 +244,7 @@ TESTS =                                             \
   misc/stat-fmt                                        \
   misc/stat-hyphen                             \
   misc/stat-mount                              \
+  misc/stat-nanoseconds                                \
   misc/stat-printf                             \
   misc/stat-slash                              \
   misc/stdbuf                                  \
diff --git a/tests/misc/stat-nanoseconds b/tests/misc/stat-nanoseconds
new file mode 100755
index 0000000..b7a5daf
--- /dev/null
+++ b/tests/misc/stat-nanoseconds
@@ -0,0 +1,35 @@
+#!/bin/sh
+# Exercise format strings involving %:X, %:Y, etc.
+
+# Copyright (C) 2010 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 <http://www.gnu.org/licenses/>.
+
+test "$VERBOSE" = yes && stat --version
+. "${srcdir=.}/init.sh"; path_prepend_ ../src
+
+touch -d '2010-10-21 18:43:33.023456789' k || framework_failure_
+
+ls --full-time | grep 18:43:33.023456789 \
+  || skip_ this file system does not support sub-second time stamps
+
+test "$(stat -c      %:X k)" = 023456789  || fail=1
+test "$(stat -c     %3:X k)" = 23456789   || fail=1
+test "$(stat -c   %3.3:X k)" = ' 23'      || fail=1
+test "$(stat -c  %03.3:X k)" = 023        || fail=1
+test "$(stat -c  %03.3:X k)" = 023        || fail=1
+test "$(stat -c  %-3.3:X k)" = '23 '      || fail=1
+test "$(stat -c %010.3:X k)" = 0000000023 || fail=1
+
+Exit $fail
--
1.7.3.2.191.g2d0e5


>From 4ee918f1ca742c9b02d00e703625b388b309bcc1 Mon Sep 17 00:00:00 2001
From: Jim Meyering <address@hidden>
Date: Thu, 21 Oct 2010 18:41:24 +0200
Subject: [PATCH] stat: revert %X-%Y-%Z change; use e.g., %:X to print 
fractional seconds

This reverts part of the recent commit 9069af45,
"stat: print timestamps to full resolution", which made %X, %Y, %Z
print floating point numbers.  We prefer to retain portability of
%X, %Y and %Z uses, while still providing access to full-resolution
time stamps via modified format strings.  Also make the new
%W consistent.
* src/stat.c: Include "xstrtol.h".
(print_it): Accept a new %...:[XYZ] format directive,
e.g., %:X, to print the nanoseconds portion of the corresponding time.
For example, %3.3:Y prints the zero-padded, truncated, milliseconds
part of the time of last modification.
(print_it): Update print_func signature to match.
(neg_to_zero): New helper function.
(epoch_time): Remove function; replace with...
(epoch_sec): New function; use timetostr.
(e10, format_precision): New functions.
(out_ns): New function.  Use "09" only when no other modifier
is specified.
(print_statfs): Change type of "m" to unsigned int,
now that it must accommodate values larger than 255.
(print_stat): Likewise.
Map :X to a code of 'X' + 256.  Likewise for Y, Z and W.
(usage): Update.
* tests/touch/60-seconds: Use %Y.%:Y in place of %Y.
* tests/misc/stat-nanoseconds: New file.
* tests/Makefile.am (TESTS): Add it.
* NEWS (Changes in behavior): Mention this.
Thanks to Andreas Schwab for raising the issue.
---
 NEWS                        |   13 ++++
 doc/coreutils.texi          |   11 +++-
 src/stat.c                  |  165 +++++++++++++++++++++++++++++++++----------
 tests/Makefile.am           |    1 +
 tests/misc/stat-nanoseconds |   35 +++++++++
 tests/touch/60-seconds      |    2 +-
 6 files changed, 187 insertions(+), 40 deletions(-)
 create mode 100755 tests/misc/stat-nanoseconds

diff --git a/NEWS b/NEWS
index dcb9745..596d2ec 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,19 @@ GNU coreutils NEWS                                    -*- 
outline -*-
   cp --attributes-only now completely overrides --reflink.
   Previously a reflink was needlessly attempted.

+  stat's %X, %Y, and %Z directives once again print only the integer
+  part of seconds since the epoch.  This reverts a change from
+  coreutils-8.6, that was deemed unnecessarily disruptive.  To obtain
+  the nanoseconds portion corresponding to %X, you may now use %:X.
+  I.e., to print the floating point number of seconds using maximum
+  precision, use this format string: %X.%:X.  Likewise for %Y, %Z and %W.
+
+  stat's new %W format directive would print floating point seconds.
+  However, with the above change to %X, %Y and %Z, we've made %W work
+  the same way:  %W now expands to seconds since the epoch (or 0 when
+  not supported), and %:W expands to the nanoseconds portion, or to
+  0 if not supported.
+

 * Noteworthy changes in release 8.6 (2010-10-15) [stable]

diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 0b5a3d3..530390e 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -10706,15 +10706,24 @@ stat invocation
 @item %u - User ID of owner
 @item %U - User name of owner
 @item %w - Time of file birth, or @samp{-} if unknown
-@item %W - Time of file birth as seconds since Epoch, or @samp{-}
+@item %W - Time of file birth as seconds since Epoch, or @samp{0}
+@item %:W - Time of file birth: nanoseconds remainder, or @samp{0}
 @item %x - Time of last access
 @item %X - Time of last access as seconds since Epoch
+@item %:X - Time of last access: nanoseconds remainder
 @item %y - Time of last modification
 @item %Y - Time of last modification as seconds since Epoch
+@item %:Y - Time of last modification: nanoseconds remainder
 @item %z - Time of last change
 @item %Z - Time of last change as seconds since Epoch
+@item %:Z - Time of last change: nanoseconds remainder
 @end itemize

+Note that each of @samp{%:W}, @samp{%:X}, @samp{%:Y} and @samp{%:Z}
+prints its zero-padded number of nanoseconds on a field of width 9.
+However, if you specify anything between the @samp{%} and @samp{:},
+e.g., @samp{%10:X}, your modifier replaces the default of @samp{09}.
+
 The mount point printed by @samp{%m} is similar to that output
 by @command{df}, except that:
 @itemize @bullet
diff --git a/src/stat.c b/src/stat.c
index fabbc17..a132b5f 100644
--- a/src/stat.c
+++ b/src/stat.c
@@ -70,6 +70,7 @@
 #include "stat-time.h"
 #include "strftime.h"
 #include "find-mount-point.h"
+#include "xstrtol.h"
 #include "xvasprintf.h"

 #if USE_STATVFS
@@ -462,24 +463,76 @@ human_time (struct timespec t)
   return str;
 }

+/* Return a string representation (in static storage)
+   of the number of seconds in T since the epoch.  */
 static char * ATTRIBUTE_WARN_UNUSED_RESULT
-epoch_time (struct timespec t)
+epoch_sec (struct timespec t)
 {
-  static char str[INT_STRLEN_BOUND (time_t) + sizeof ".NNNNNNNNN"];
-  /* Note that time_t can technically be a floating point value, such
-     that casting to [u]intmax_t could lose a fractional value or
-     suffer from overflow.  However, most porting targets have an
-     integral time_t; also, we know of no file systems that store
-     valid time values outside the bounds of intmax_t even if that
-     value were represented as a floating point.  Besides, the cost of
-     converting to struct tm just to use nstrftime (str, len, "%s.%N",
-     tm, 0, t.tv_nsec) is pointless, since nstrftime would have to
-     convert back to seconds as time_t.  */
-  if (TYPE_SIGNED (time_t))
-    sprintf (str, "%" PRIdMAX ".%09ld", (intmax_t) t.tv_sec, t.tv_nsec);
+  static char str[INT_BUFSIZE_BOUND (time_t)];
+  return timetostr (t.tv_sec, str);
+}
+
+/* Return 10^N, for N in 0..9.  Otherwise, return 0.  */
+static unsigned int
+e10 (unsigned int n)
+{
+  static unsigned int pow10[] =
+    { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 
1000000000 };
+  return n < ARRAY_CARDINALITY (pow10) ? pow10[n] : 0;
+}
+
+/* Given the modifiers, FMT, of a format string, return
+   the precision, if specified, else 0.  */
+static unsigned int
+format_precision (char *fmt, size_t n_bytes)
+{
+  char const *decimal = memchr (fmt, '.', n_bytes);
+  if (decimal == NULL)
+    return 0;
+  char const *prec = decimal + 1;
+  unsigned long tmp;
+  fmt[n_bytes] = '\0';
+  if (xstrtoul (prec, NULL, 10, &tmp, NULL) != LONGINT_OK
+      || UINT_MAX < tmp)
+    return 0;
+
+  return tmp;
+}
+
+/* Output the number of nanoseconds, ARG.tv_nsec, honoring a
+   WIDTH.PRECISION format modifier, where PRECISION specifies
+   how many leading digits(on a field of 9) to print.  */
+static void
+out_ns (char *pformat, size_t prefix_len, struct timespec arg)
+{
+  /* If no format modifier is specified, i.e., nothing between the
+     "%" and ":" of "%:X", then use the default of zero-padding and
+     a width of 9.  Otherwise, use the specified modifier(s).
+     This is to avoid the mistake of omitting the zero padding on
+     a number with fewer digits than the field width: when printing
+     nanoseconds after a decimal place, the resulting floating point
+     fraction would be off by a factor of 10 or more.  */
+  unsigned long int ns = arg.tv_nsec;
+  unsigned int prec;
+  char const *fmt;
+
+  if (prefix_len == 1)
+    fmt = "09lu";
   else
-    sprintf (str, "%" PRIuMAX ".%09ld", (uintmax_t) t.tv_sec, t.tv_nsec);
-  return str;
+    {
+      fmt = "lu";
+      if ((prec = format_precision (pformat + 1, prefix_len - 1)) < 9
+          && prec != 0)
+        {
+          /* Honor precision, and then shorten prefix_len so that
+             we elide the ".PREC" suffix from the format string.  */
+          ns /= e10 (9 - prec);
+          prefix_len = strchr (pformat, '.') - pformat;
+        }
+    }
+
+  strcpy (pformat + prefix_len, fmt);
+  printf (pformat, ns);
 }

 static void
@@ -539,7 +592,8 @@ out_file_context (char *pformat, size_t prefix_len, char 
const *filename)

 /* Print statfs info.  Return zero upon success, nonzero upon failure.  */
 static bool ATTRIBUTE_WARN_UNUSED_RESULT
-print_statfs (char *pformat, size_t prefix_len, char m, char const *filename,
+print_statfs (char *pformat, size_t prefix_len, unsigned int m,
+              char const *filename,
               void const *data)
 {
   STRUCT_STATVFS const *statfsbuf = data;
@@ -711,9 +765,19 @@ print_mount_point:
   return fail;
 }

+/* Map a TS with negative TS.tv_nsec to {0,0}.  */
+static inline struct
+timespec neg_to_zero (struct timespec ts)
+{
+  if (0 <= ts.tv_nsec)
+    return ts;
+  struct timespec z = {0, 0};
+  return z;
+}
+
 /* Print stat info.  Return zero upon success, nonzero upon failure.  */
 static bool
-print_stat (char *pformat, size_t prefix_len, char m,
+print_stat (char *pformat, size_t prefix_len, unsigned int m,
             char const *filename, void const *data)
 {
   struct stat *statbuf = (struct stat *) data;
@@ -815,31 +879,38 @@ print_stat (char *pformat, size_t prefix_len, char m,
       }
       break;
     case 'W':
-      {
-        struct timespec t = get_stat_birthtime (statbuf);
-        if (t.tv_nsec < 0)
-          out_string (pformat, prefix_len, "-");
-        else
-          out_string (pformat, prefix_len, epoch_time (t));
-      }
+      out_string (pformat, prefix_len,
+                  epoch_sec (neg_to_zero (get_stat_birthtime (statbuf))));
+      break;
+    case 'W' + 256:
+      out_ns (pformat, prefix_len, neg_to_zero (get_stat_birthtime (statbuf)));
       break;
     case 'x':
       out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
       break;
     case 'X':
-      out_string (pformat, prefix_len, epoch_time (get_stat_atime (statbuf)));
+      out_string (pformat, prefix_len, epoch_sec (get_stat_atime (statbuf)));
+      break;
+    case 'X' + 256:
+      out_ns (pformat, prefix_len, get_stat_atime (statbuf));
       break;
     case 'y':
       out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
       break;
     case 'Y':
-      out_string (pformat, prefix_len, epoch_time (get_stat_mtime (statbuf)));
+      out_string (pformat, prefix_len, epoch_sec (get_stat_mtime (statbuf)));
+      break;
+    case 'Y' + 256:
+      out_ns (pformat, prefix_len, get_stat_mtime (statbuf));
       break;
     case 'z':
       out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
       break;
     case 'Z':
-      out_string (pformat, prefix_len, epoch_time (get_stat_ctime (statbuf)));
+      out_string (pformat, prefix_len, epoch_sec (get_stat_ctime (statbuf)));
+      break;
+    case 'Z' + 256:
+      out_ns (pformat, prefix_len, get_stat_ctime (statbuf));
       break;
     case 'C':
       fail |= out_file_context (pformat, prefix_len, filename);
@@ -897,7 +968,8 @@ print_esc_char (char c)
    Return zero upon success, nonzero upon failure.  */
 static bool ATTRIBUTE_WARN_UNUSED_RESULT
 print_it (char const *format, char const *filename,
-          bool (*print_func) (char *, size_t, char, char const *, void const 
*),
+          bool (*print_func) (char *, size_t, unsigned int,
+                              char const *, void const *),
           void const *data)
 {
   bool fail = false;
@@ -922,10 +994,23 @@ print_it (char const *format, char const *filename,
           {
             size_t len = strspn (b + 1, "#-+.I 0123456789");
             char const *fmt_char = b + len + 1;
+            unsigned int fmt_code;
             memcpy (dest, b, len + 1);

+            /* The ":" modifier just before the letter in %W, %X, %Y, %Z
+               tells stat to print the nanoseconds portion of the date.  */
+            if (*fmt_char == ':' && strchr ("WXYZ", fmt_char[1]))
+              {
+                fmt_code = fmt_char[1] + 256;
+                ++fmt_char;
+              }
+            else
+              {
+                fmt_code = fmt_char[0];
+              }
+
             b = fmt_char;
-            switch (*fmt_char)
+            switch (fmt_code)
               {
               case '\0':
                 --b;
@@ -941,7 +1026,7 @@ print_it (char const *format, char const *filename,
                 putchar ('%');
                 break;
               default:
-                fail |= print_func (dest, len + 1, *fmt_char, filename, data);
+                fail |= print_func (dest, len + 1, fmt_code, filename, data);
                 break;
               }
             break;
@@ -1215,14 +1300,18 @@ The valid format sequences for files (without 
--file-system):\n\
       fputs (_("\
   %u   User ID of owner\n\
   %U   User name of owner\n\
-  %w   Time of file birth, or - if unknown\n\
-  %W   Time of file birth as seconds since Epoch, or - if unknown\n\
-  %x   Time of last access\n\
-  %X   Time of last access as seconds since Epoch\n\
-  %y   Time of last modification\n\
-  %Y   Time of last modification as seconds since Epoch\n\
-  %z   Time of last change\n\
-  %Z   Time of last change as seconds since Epoch\n\
+  %w   Time of file birth, human-readable; - if unknown\n\
+  %W   Time of file birth, seconds since Epoch; 0 if unknown\n\
+  %:W  Time of file birth, nanoseconds remainder; 0 if unknown\n\
+  %x   Time of last access, human-readable\n\
+  %X   Time of last access, seconds since Epoch\n\
+  %:X  Time of last access, nanoseconds remainder\n\
+  %y   Time of last modification, human-readable\n\
+  %Y   Time of last modification, seconds since Epoch\n\
+  %:Y  Time of last modification, nanoseconds remainder\n\
+  %z   Time of last change, human-readable\n\
+  %Z   Time of last change, seconds since Epoch\n\
+  %:Z  Time of last change, nanoseconds remainder\n\
 \n\
 "), stdout);

diff --git a/tests/Makefile.am b/tests/Makefile.am
index 84db367..dd1c509 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -244,6 +244,7 @@ TESTS =                                             \
   misc/stat-fmt                                        \
   misc/stat-hyphen                             \
   misc/stat-mount                              \
+  misc/stat-nanoseconds                                \
   misc/stat-printf                             \
   misc/stat-slash                              \
   misc/stdbuf                                  \
diff --git a/tests/misc/stat-nanoseconds b/tests/misc/stat-nanoseconds
new file mode 100755
index 0000000..b7a5daf
--- /dev/null
+++ b/tests/misc/stat-nanoseconds
@@ -0,0 +1,35 @@
+#!/bin/sh
+# Exercise format strings involving %:X, %:Y, etc.
+
+# Copyright (C) 2010 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 <http://www.gnu.org/licenses/>.
+
+test "$VERBOSE" = yes && stat --version
+. "${srcdir=.}/init.sh"; path_prepend_ ../src
+
+touch -d '2010-10-21 18:43:33.023456789' k || framework_failure_
+
+ls --full-time | grep 18:43:33.023456789 \
+  || skip_ this file system does not support sub-second time stamps
+
+test "$(stat -c      %:X k)" = 023456789  || fail=1
+test "$(stat -c     %3:X k)" = 23456789   || fail=1
+test "$(stat -c   %3.3:X k)" = ' 23'      || fail=1
+test "$(stat -c  %03.3:X k)" = 023        || fail=1
+test "$(stat -c  %03.3:X k)" = 023        || fail=1
+test "$(stat -c  %-3.3:X k)" = '23 '      || fail=1
+test "$(stat -c %010.3:X k)" = 0000000023 || fail=1
+
+Exit $fail
diff --git a/tests/touch/60-seconds b/tests/touch/60-seconds
index f98f0c5..d008296 100755
--- a/tests/touch/60-seconds
+++ b/tests/touch/60-seconds
@@ -30,7 +30,7 @@ echo 60.000000000 > exp || framework_failure
 # an `invalid date format'.  Specifying 60 seconds *is* valid.
 TZ=UTC0 touch -t 197001010000.60 f || fail=1

-stat --p='%Y\n' f > out || fail=1
+stat --p='%Y.%:Y\n' f > out || fail=1

 compare out exp || fail=1

--
1.7.3.2.191.g2d0e5



reply via email to

[Prev in Thread] Current Thread [Next in Thread]