Also, fix some rounding errors while we’re in the neighborhood.
* src/buffer.c (duration_ns, compute_duration_ns): Rename from
‘duration’ and ‘compute_duration’, and count ns rather than s, to
lessen rounding error. All uses changed.
(compute_duration_ns): Work even if the clock moves backward
and time_t is unsigned.
(print_stats): Don’t worry about null or empty TEXT, as that
cannot happen. Compare double to UINTMAX_MAX + 1.0, not
to UINTMAX_MAX, so that the comparison is exact.
Handle the unlikely case that numbytes >= UINTMAX_MAX.
* src/tar.c (parse_opt): Treat -L hugenumber as effectively
infinity rather than erroring out.
Prefer ckd_add to checking overflow by hand.
\f
/* Time-related functions */
-static double duration;
+/* Time consumed during run. It is counted in ns to lessen rounding error. */
+static double duration_ns;
void
set_start_time (void)
}
double
-compute_duration (void)
+compute_duration_ns (void)
{
- struct timespec now;
- gettime (&now);
- duration += ((now.tv_sec - last_stat_time.tv_sec)
- + (now.tv_nsec - last_stat_time.tv_nsec) / 1e9);
- gettime (&last_stat_time);
- return duration;
+ struct timespec now = current_timespec ();
+
+ /* If the clock moves back, treat it as duration 0.
+ This works even if time_t is unsigned. */
+ if (timespec_cmp (last_stat_time, now) < 0)
+ duration_ns += (1e9 * (now.tv_sec - last_stat_time.tv_sec)
+ + (now.tv_nsec - last_stat_time.tv_nsec));
+
+ last_stat_time = current_timespec ();
+ return duration_ns;
}
\f
print_stats (FILE *fp, const char *text, tarlong numbytes)
{
char abbr[LONGEST_HUMAN_READABLE + 1];
- char rate[LONGEST_HUMAN_READABLE + 1];
- int n = 0;
-
int human_opts = human_autoscale | human_base_1024 | human_SI | human_B;
+ double ulim = UINTMAX_MAX + 1.0;
+
+ int n = fprintf (fp, "%s: "TARLONG_FORMAT" (", gettext (text), numbytes);
+
+ if (numbytes < ulim)
+ n += fprintf (fp, "%s", human_readable (numbytes, abbr, human_opts, 1, 1));
+ else
+ n += fprintf (fp, "%g", numbytes);
- if (text && text[0])
- n += fprintf (fp, "%s: ", gettext (text));
- return n + fprintf (fp, TARLONG_FORMAT " (%s, %s/s)",
- numbytes,
- human_readable (numbytes, abbr, human_opts, 1, 1),
- (0 < duration && numbytes / duration < (uintmax_t) -1
- ? human_readable (numbytes / duration, rate, human_opts, 1, 1)
- : "?"));
+ if (!duration_ns)
+ n += fprintf (fp, ")");
+ else
+ {
+ double rate = 1e9 * numbytes / duration_ns;
+ if (rate < ulim)
+ n += fprintf (fp, ", %s/s)",
+ human_readable (rate, abbr, human_opts, 1, 1));
+ else
+ n += fprintf (fp, ", %g/s)", rate);
+ }
+
+ return n;
}
/* Format totals to file FP. FORMATS is an array of strings to output
while (current_block > record_start);
}
- compute_duration ();
+ compute_duration_ns ();
if (verify_option)
verify_volume ();
break;
case 'd':
- len += fprintf (fp, "%.0f", compute_duration ());
+ len += fprintf (fp, "%.0f", compute_duration_ns () / BILLION);
break;
case 'T':
{
const char **fmt = checkpoint_total_format, *fmtbuf[3];
struct wordsplit ws;
- compute_duration ();
+ compute_duration_ns ();
if (arg)
{
break;
case cop_totals:
- compute_duration ();
+ compute_duration_ns ();
print_total_stats ();
break;
off_t current_block_ordinal (void);
void close_archive (void);
void closeout_volume_number (void);
-double compute_duration (void);
+double compute_duration_ns (void);
union block *find_next_block (void);
void flush_read (void);
void flush_write (void);
static void
sigstat (int signo)
{
- compute_duration ();
+ compute_duration_ns ();
print_total_stats ();
#ifndef HAVE_SIGACTION
signal (signo, sigstat);
uintmax_t u;
char *p;
- if (xstrtoumax (arg, &p, 10, &u, TAR_SIZE_SUFFIXES) != LONGINT_OK)
- USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (arg),
- _("Invalid tape length")));
- if (p > arg && !strchr (TAR_SIZE_SUFFIXES, p[-1]))
- tape_length_option = 1024 * (tarlong) u;
- else
- tape_length_option = (tarlong) u;
+ switch (xstrtoumax (arg, &p, 10, &u, TAR_SIZE_SUFFIXES))
+ {
+ case LONGINT_OK:
+ tape_length_option = u;
+ if (arg < p && !strchr (TAR_SIZE_SUFFIXES, p[-1]))
+ tape_length_option *= 1024;
+ break;
+
+ case LONGINT_OVERFLOW:
+ /* Treat enormous values as effectively infinity. */
+ tape_length_option = 0;
+ break;
+
+ default:
+ USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (arg),
+ _("Invalid tape length")));
+ }
+
multi_volume_option = true;
}
break;
uintmax_t u;
if (! (xstrtoumax (arg, NULL, 10, &u, TAR_SIZE_SUFFIXES) == LONGINT_OK
- && u == (size_t) u))
+ && !ckd_add (&record_size, u, 0)))
USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (arg),
_("Invalid record size")));
- record_size = u;
if (record_size % BLOCKSIZE != 0)
USAGE_ERROR ((0, 0, _("Record size must be a multiple of %d."),
BLOCKSIZE));
{
uintmax_t u;
if (! (xstrtoumax (arg, 0, 10, &u, "") == LONGINT_OK
- && u == (size_t) u))
+ && !ckd_add (&strip_name_components, u, 0)))
USAGE_ERROR ((0, 0, "%s: %s", quotearg_colon (arg),
_("Invalid number of elements")));
- strip_name_components = u;
}
break;