Implement statistics display in checkpoint actions.
* NEWS: Update. * configure.ac: Version 1.27.90 * gnulib.modules: Add fprintftime. * doc/tar.texi: Document the "totals" action and new format specifiers for echo and ttyout checkpoint actions. * src/buffer.c (compute_duration): Return computed value. (print_stats): Don't print trailing newline. Return number of characters output. (format_total_stats): New function. (print_total_stats): Rewrite via format_total_stats. * src/checkpoint.c (checkpoint_opcode) <cop_totals>: New opcode. (checkpoint_compile_action): Handle cop_totals. (expand_checkpoint_string): Remove. (format_checkpoint_string): New function to be used instead of expand_checkpoint_string. All callers updated. * src/common.h (TF_READ,TF_WRITE) (TF_DELETED): New constants. (format_total_stats,print_total_stats): New protos.
This commit is contained in:
21
NEWS
21
NEWS
@@ -1,6 +1,25 @@
|
||||
GNU tar NEWS - User visible changes. 2013-11-17
|
||||
GNU tar NEWS - User visible changes. 2014-01-21
|
||||
Please send GNU tar bug reports to <bug-tar@gnu.org>
|
||||
|
||||
|
||||
version 1.27.90 (Git)
|
||||
|
||||
* New checkpoint action: totals
|
||||
|
||||
The --checkpoint-action=totals option instructs tar to output the
|
||||
total number of bytes transferred at each checkpoint.
|
||||
|
||||
* Extended checkpoint format specification.
|
||||
|
||||
New conversion specifiers are implemented:
|
||||
|
||||
%d - output number of seconds since tar started
|
||||
%T - output I/O totals
|
||||
%{FMT}t - output current local time using FMT as strftime(3) format
|
||||
If {FMT} is omitted, use %c
|
||||
%{N}* - pad output with spaces to the Nth column, or to the
|
||||
current screen width, if {N} is not given.
|
||||
|
||||
|
||||
version 1.27.1 - Sergey Poznyakoff, 2013-11-17
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
AC_INIT([GNU tar], [1.27.1], [bug-tar@gnu.org])
|
||||
AC_INIT([GNU tar], [1.27.90], [bug-tar@gnu.org])
|
||||
AC_CONFIG_SRCDIR([src/tar.c])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
67
doc/tar.texi
67
doc/tar.texi
@@ -3961,10 +3961,10 @@ e.g.:
|
||||
@end smallexample
|
||||
|
||||
The @samp{%s} and @samp{%u} in the above example are
|
||||
@dfn{meta-characters}. The @samp{%s} meta-character is replaced with
|
||||
@dfn{format specifiers}. The @samp{%s} specifier is replaced with
|
||||
the @dfn{type} of the checkpoint: @samp{write} or
|
||||
@samp{read} (or a corresponding translated version in locales other
|
||||
than @acronym{POSIX}). The @samp{%u} meta-character is replaced with
|
||||
than @acronym{POSIX}). The @samp{%u} specifier is replaced with
|
||||
the ordinal number of the checkpoint. Thus, the above example could
|
||||
produce the following output when used with the @option{--create}
|
||||
option:
|
||||
@@ -3975,7 +3975,46 @@ tar: Hit write checkpoint #20
|
||||
tar: Hit write checkpoint #30
|
||||
@end smallexample
|
||||
|
||||
Aside from meta-character expansion, the message string is subject to
|
||||
The complete list of available format specifiers follows:
|
||||
|
||||
@table @samp
|
||||
@item %s
|
||||
Print type of the checkpoint (@samp{write} or @samp{read}).
|
||||
|
||||
@item %u
|
||||
Print number of the checkpoint.
|
||||
|
||||
@item %T
|
||||
Print number of bytes transferred so far and approximate transfer
|
||||
speed. The number is preceded by @samp{W:}, when writing and by
|
||||
@samp{R:} when reading. If @command{tar} is performing delete
|
||||
operation (@pxref{delete}), three numbers are printed: number of bytes
|
||||
read, written and deleted, each of them prefixed by @samp{R:},
|
||||
@samp{W:} and @samp{D:} correspondingy. For example:
|
||||
|
||||
@example
|
||||
$ @kbd{tar --delete -f f.tar --checkpoint-action=echo="#%u: %T" main.c}
|
||||
tar: #1: R: 0 (0B, ?/s),W: 0 (0B, ?/s),D: 0
|
||||
tar: #2: R: 10240 (10KiB, 19MiB/s),W: 0 (0B, 0B/s),D: 10240
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
See also the @samp{totals} action, described below.
|
||||
|
||||
@item %@{@var{fmt}@}t
|
||||
Output current local time using @var{fmt} as format for @command{strftime}
|
||||
(@pxref{strftime, strftime,,strftime(3), strftime(3) man page}). The
|
||||
@samp{@{@var{fmt}@}} part is optional. If not present, the default
|
||||
format is @samp{%c}, i.e. the preferred date and time representation
|
||||
for the current locale.
|
||||
|
||||
@item %@{@var{n}@}*
|
||||
Pad output with spaces to the @var{n}th column. If the
|
||||
@samp{@{@var{n}@}} part is omitted, the current screen width
|
||||
is assumed.
|
||||
@end table
|
||||
|
||||
Aside from format expansion, the message string is subject to
|
||||
@dfn{unquoting}, during which the backslash @dfn{escape sequences} are
|
||||
replaced with their corresponding @acronym{ASCII} characters
|
||||
(@pxref{escape sequences}). E.g. the following action will produce an
|
||||
@@ -4002,9 +4041,23 @@ following action will print the checkpoint message at the same screen
|
||||
line, overwriting any previous message:
|
||||
|
||||
@smallexample
|
||||
--checkpoint-action="ttyout=\rHit %s checkpoint #%u"
|
||||
--checkpoint-action="ttyout=Hit %s checkpoint #%u%*\r"
|
||||
@end smallexample
|
||||
|
||||
@noindent
|
||||
Notice the use of @samp{%*} specifier to clear out any eventual
|
||||
remains of the prior output line. As as more complex example,
|
||||
consider this:
|
||||
|
||||
@smallexample
|
||||
--checkpoint-action=ttyout='%@{%Y-%m-%d %H:%M:%S@}t (%d sec): #%u, %T%*\r'
|
||||
@end smallexample
|
||||
|
||||
@noindent
|
||||
This prints the current local time, number of seconds expired since
|
||||
tar was started, the checkpoint ordinal number, transferred bytes and
|
||||
average computed I/O speed.
|
||||
|
||||
@cindex @code{dot}, checkpoint action
|
||||
Another available checkpoint action is @samp{dot} (or @samp{.}). It
|
||||
instructs @command{tar} to print a single dot on the standard listing
|
||||
@@ -4019,6 +4072,12 @@ For compatibility with previous @GNUTAR{} versions, this action can
|
||||
be abbreviated by placing a dot in front of the checkpoint frequency,
|
||||
as shown in the previous section.
|
||||
|
||||
@cindex @code{totals}, checkpoint action
|
||||
The @samp{totals} action prints the total number of bytes transferred
|
||||
so far. The format of the data is the same as for the
|
||||
@option{--totals} option (@pxref{totals}). See also @samp{%T} format
|
||||
specifier of the @samp{echo} or @samp{ttyout} action.
|
||||
|
||||
@cindex @code{sleep}, checkpoint action
|
||||
Yet another action, @samp{sleep}, pauses @command{tar} for a specified
|
||||
amount of seconds. The following example will stop for 30 seconds at each
|
||||
|
||||
@@ -38,6 +38,7 @@ fdopendir
|
||||
fdutimensat
|
||||
fileblocks
|
||||
fnmatch-gnu
|
||||
fprintftime
|
||||
fseeko
|
||||
fstatat
|
||||
full-write
|
||||
|
||||
82
src/buffer.c
82
src/buffer.c
@@ -247,7 +247,7 @@ set_volume_start_time (void)
|
||||
last_stat_time = volume_start_time;
|
||||
}
|
||||
|
||||
void
|
||||
double
|
||||
compute_duration (void)
|
||||
{
|
||||
struct timespec now;
|
||||
@@ -255,6 +255,7 @@ compute_duration (void)
|
||||
duration += ((now.tv_sec - last_stat_time.tv_sec)
|
||||
+ (now.tv_nsec - last_stat_time.tv_nsec) / 1e9);
|
||||
gettime (&last_stat_time);
|
||||
return duration;
|
||||
}
|
||||
|
||||
|
||||
@@ -488,8 +489,7 @@ open_compressed_archive (void)
|
||||
return archive;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
static int
|
||||
print_stats (FILE *fp, const char *text, tarlong numbytes)
|
||||
{
|
||||
char bytes[sizeof (tarlong) * CHAR_BIT];
|
||||
@@ -500,52 +500,86 @@ print_stats (FILE *fp, const char *text, tarlong numbytes)
|
||||
|
||||
sprintf (bytes, TARLONG_FORMAT, numbytes);
|
||||
|
||||
fprintf (fp, "%s: %s (%s, %s/s)\n",
|
||||
text, bytes,
|
||||
human_readable (numbytes, abbr, human_opts, 1, 1),
|
||||
(0 < duration && numbytes / duration < (uintmax_t) -1
|
||||
? human_readable (numbytes / duration, rate, human_opts, 1, 1)
|
||||
: "?"));
|
||||
return fprintf (fp, "%s: %s (%s, %s/s)",
|
||||
text, bytes,
|
||||
human_readable (numbytes, abbr, human_opts, 1, 1),
|
||||
(0 < duration && numbytes / duration < (uintmax_t) -1
|
||||
? human_readable (numbytes / duration, rate, human_opts, 1, 1)
|
||||
: "?"));
|
||||
}
|
||||
|
||||
void
|
||||
print_total_stats (void)
|
||||
/* Format totals to file FP. FORMATS is an array of strings to output
|
||||
before each data item (bytes read, written, deleted, in that order).
|
||||
EOR is a delimiter to output after each item (used only if deleting
|
||||
from the archive), EOL is a delimiter to add at the end of the output
|
||||
line. */
|
||||
int
|
||||
format_total_stats (FILE *fp, char **formats, int eor, int eol)
|
||||
{
|
||||
int n;
|
||||
|
||||
switch (subcommand_option)
|
||||
{
|
||||
case CREATE_SUBCOMMAND:
|
||||
case CAT_SUBCOMMAND:
|
||||
case UPDATE_SUBCOMMAND:
|
||||
case APPEND_SUBCOMMAND:
|
||||
/* Amanda 2.4.1p1 looks for "Total bytes written: [0-9][0-9]*". */
|
||||
print_stats (stderr, _("Total bytes written"),
|
||||
prev_written + bytes_written);
|
||||
n = print_stats (fp, _(formats[TF_WRITE]),
|
||||
prev_written + bytes_written);
|
||||
break;
|
||||
|
||||
case DELETE_SUBCOMMAND:
|
||||
{
|
||||
char buf[UINTMAX_STRSIZE_BOUND];
|
||||
print_stats (stderr, _("Total bytes read"),
|
||||
records_read * record_size);
|
||||
print_stats (stderr, _("Total bytes written"),
|
||||
prev_written + bytes_written);
|
||||
fprintf (stderr, _("Total bytes deleted: %s\n"),
|
||||
STRINGIFY_BIGINT ((records_read - records_skipped)
|
||||
* record_size
|
||||
- (prev_written + bytes_written), buf));
|
||||
n = print_stats (fp, _(formats[TF_READ]),
|
||||
records_read * record_size);
|
||||
|
||||
fputc (eor, fp);
|
||||
n++;
|
||||
|
||||
n += print_stats (fp, _(formats[TF_WRITE]),
|
||||
prev_written + bytes_written);
|
||||
|
||||
fputc (eor, fp);
|
||||
n++;
|
||||
|
||||
n += fprintf (fp, "%s: %s",
|
||||
_(formats[TF_DELETED]),
|
||||
STRINGIFY_BIGINT ((records_read - records_skipped)
|
||||
* record_size
|
||||
- (prev_written + bytes_written), buf));
|
||||
}
|
||||
break;
|
||||
|
||||
case EXTRACT_SUBCOMMAND:
|
||||
case LIST_SUBCOMMAND:
|
||||
case DIFF_SUBCOMMAND:
|
||||
print_stats (stderr, _("Total bytes read"),
|
||||
records_read * record_size);
|
||||
n = print_stats (fp, _(formats[TF_READ]),
|
||||
records_read * record_size);
|
||||
break;
|
||||
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
if (eol)
|
||||
{
|
||||
fputc (eol, fp);
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
char *default_total_format[] = {
|
||||
N_("Total bytes read"),
|
||||
/* Amanda 2.4.1p1 looks for "Total bytes written: [0-9][0-9]*". */
|
||||
N_("Total bytes written"),
|
||||
N_("Total bytes deleted")
|
||||
};
|
||||
|
||||
void
|
||||
print_total_stats (void)
|
||||
{
|
||||
format_total_stats (stderr, default_total_format, '\n', '\n');
|
||||
}
|
||||
|
||||
/* Compute and return the block ordinal at current_block. */
|
||||
|
||||
206
src/checkpoint.c
206
src/checkpoint.c
@@ -19,6 +19,9 @@
|
||||
|
||||
#include <system.h>
|
||||
#include "common.h"
|
||||
#include "wordsplit.h"
|
||||
#include <sys/ioctl.h>
|
||||
#include "fprintftime.h"
|
||||
|
||||
enum checkpoint_opcode
|
||||
{
|
||||
@@ -27,7 +30,8 @@ enum checkpoint_opcode
|
||||
cop_echo,
|
||||
cop_ttyout,
|
||||
cop_sleep,
|
||||
cop_exec
|
||||
cop_exec,
|
||||
cop_totals
|
||||
};
|
||||
|
||||
struct checkpoint_action
|
||||
@@ -110,6 +114,8 @@ checkpoint_compile_action (const char *str)
|
||||
act = alloc_action (cop_sleep);
|
||||
act->v.time = n;
|
||||
}
|
||||
else if (strcmp (str, "totals") == 0)
|
||||
alloc_action (cop_totals);
|
||||
else
|
||||
FATAL_ERROR ((0, 0, _("%s: unknown checkpoint action"), str));
|
||||
}
|
||||
@@ -128,61 +134,160 @@ checkpoint_finish_compile (void)
|
||||
checkpoint_option = DEFAULT_CHECKPOINT;
|
||||
}
|
||||
|
||||
static char *
|
||||
expand_checkpoint_string (const char *input, bool do_write, unsigned cpn)
|
||||
static char *checkpoint_total_format[] = {
|
||||
"R",
|
||||
"W",
|
||||
"D"
|
||||
};
|
||||
|
||||
static int
|
||||
getwidth(FILE *fp)
|
||||
{
|
||||
const char *opstr = do_write ? gettext ("write") : gettext ("read");
|
||||
size_t opstrlen = strlen (opstr);
|
||||
char uintbuf[UINTMAX_STRSIZE_BOUND];
|
||||
char *cps = STRINGIFY_BIGINT (cpn, uintbuf);
|
||||
size_t cpslen = strlen (cps);
|
||||
const char *ip;
|
||||
char *op;
|
||||
char *output;
|
||||
size_t outlen = strlen (input); /* Initial guess */
|
||||
struct winsize ws;
|
||||
|
||||
/* Fix the initial length guess */
|
||||
for (ip = input; (ip = strchr (ip, '%')) != NULL; )
|
||||
ws.ws_col = ws.ws_row = 0;
|
||||
if ((ioctl (fileno (fp), TIOCGWINSZ, (char *) &ws) < 0) || ws.ws_col == 0)
|
||||
{
|
||||
switch (ip[1])
|
||||
{
|
||||
case 'u':
|
||||
outlen += cpslen - 2;
|
||||
break;
|
||||
const char *col = getenv ("COLUMNS");
|
||||
if (col)
|
||||
return strtol (col, NULL, 10);
|
||||
else
|
||||
return 80;
|
||||
}
|
||||
return ws.ws_col;
|
||||
}
|
||||
|
||||
case 's':
|
||||
outlen += opstrlen - 2;
|
||||
static char *
|
||||
getarg (const char *input, const char ** endp, char **argbuf, size_t *arglen)
|
||||
{
|
||||
if (input[0] == '{')
|
||||
{
|
||||
char *p = strchr (input + 1, '}');
|
||||
if (p)
|
||||
{
|
||||
size_t n = p - input;
|
||||
if (n > *arglen)
|
||||
{
|
||||
*arglen = n;
|
||||
*argbuf = xrealloc (*argbuf, *arglen);
|
||||
}
|
||||
n--;
|
||||
memcpy (*argbuf, input + 1, n);
|
||||
(*argbuf)[n] = 0;
|
||||
*endp = p + 1;
|
||||
return *argbuf;
|
||||
}
|
||||
ip++;
|
||||
}
|
||||
|
||||
output = xmalloc (outlen + 1);
|
||||
for (ip = input, op = output; *ip; )
|
||||
*endp = input;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
format_checkpoint_string (FILE *fp, const char *input, bool do_write,
|
||||
unsigned cpn)
|
||||
{
|
||||
const char *opstr = do_write ? gettext ("write") : gettext ("read");
|
||||
char uintbuf[UINTMAX_STRSIZE_BOUND];
|
||||
char *cps = STRINGIFY_BIGINT (cpn, uintbuf);
|
||||
const char *ip;
|
||||
size_t len = 0;
|
||||
|
||||
static char *argbuf = NULL;
|
||||
static size_t arglen = 0;
|
||||
char *arg = NULL;
|
||||
|
||||
if (!input)
|
||||
{
|
||||
if (do_write)
|
||||
/* TRANSLATORS: This is a "checkpoint of write operation",
|
||||
*not* "Writing a checkpoint".
|
||||
E.g. in Spanish "Punto de comprobaci@'on de escritura",
|
||||
*not* "Escribiendo un punto de comprobaci@'on" */
|
||||
input = gettext ("Write checkpoint %u");
|
||||
else
|
||||
/* TRANSLATORS: This is a "checkpoint of read operation",
|
||||
*not* "Reading a checkpoint".
|
||||
E.g. in Spanish "Punto de comprobaci@'on de lectura",
|
||||
*not* "Leyendo un punto de comprobaci@'on" */
|
||||
input = gettext ("Read checkpoint %u");
|
||||
}
|
||||
|
||||
for (ip = input; *ip; ip++)
|
||||
{
|
||||
if (*ip == '%')
|
||||
{
|
||||
switch (*++ip)
|
||||
if (*++ip == '{')
|
||||
{
|
||||
arg = getarg (ip, &ip, &argbuf, &arglen);
|
||||
if (!arg)
|
||||
{
|
||||
fputc ('%', fp);
|
||||
fputc (*ip, fp);
|
||||
len += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
switch (*ip)
|
||||
{
|
||||
case 'u':
|
||||
op = stpcpy (op, cps);
|
||||
fputs (cps, fp);
|
||||
len += strlen (cps);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
op = stpcpy (op, opstr);
|
||||
fputs (opstr, fp);
|
||||
len += strlen (opstr);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
len += fprintf (fp, "%.0f", compute_duration ());
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
compute_duration ();
|
||||
len += format_total_stats (fp, checkpoint_total_format, ',', 0);
|
||||
break;
|
||||
|
||||
case 't':
|
||||
{
|
||||
struct timeval tv;
|
||||
struct tm *tm;
|
||||
char *fmt = arg ? arg : "%c";
|
||||
|
||||
gettimeofday (&tv, NULL);
|
||||
tm = localtime (&tv.tv_sec);
|
||||
len += fprintftime (fp, fmt, tm, 0, tv.tv_usec * 1000);
|
||||
}
|
||||
break;
|
||||
|
||||
case '*':
|
||||
{
|
||||
int w = arg ? strtoul (arg, NULL, 10) : getwidth (fp);
|
||||
for (; w > len; len++)
|
||||
fputc (' ', fp);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
*op++ = '%';
|
||||
*op++ = *ip;
|
||||
fputc ('%', fp);
|
||||
fputc (*ip, fp);
|
||||
len += 2;
|
||||
break;
|
||||
}
|
||||
ip++;
|
||||
arg = NULL;
|
||||
}
|
||||
else
|
||||
*op++ = *ip++;
|
||||
{
|
||||
fputc (*ip, fp);
|
||||
if (*ip == '\r')
|
||||
len = 0;
|
||||
else
|
||||
len++;
|
||||
}
|
||||
}
|
||||
*op = 0;
|
||||
return output;
|
||||
fflush (fp);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -211,28 +316,9 @@ run_checkpoint_actions (bool do_write)
|
||||
break;
|
||||
|
||||
case cop_echo:
|
||||
{
|
||||
char *tmp;
|
||||
const char *str = p->v.command;
|
||||
if (!str)
|
||||
{
|
||||
if (do_write)
|
||||
/* TRANSLATORS: This is a "checkpoint of write operation",
|
||||
*not* "Writing a checkpoint".
|
||||
E.g. in Spanish "Punto de comprobaci@'on de escritura",
|
||||
*not* "Escribiendo un punto de comprobaci@'on" */
|
||||
str = gettext ("Write checkpoint %u");
|
||||
else
|
||||
/* TRANSLATORS: This is a "checkpoint of read operation",
|
||||
*not* "Reading a checkpoint".
|
||||
E.g. in Spanish "Punto de comprobaci@'on de lectura",
|
||||
*not* "Leyendo un punto de comprobaci@'on" */
|
||||
str = gettext ("Read checkpoint %u");
|
||||
}
|
||||
tmp = expand_checkpoint_string (str, do_write, checkpoint);
|
||||
WARN ((0, 0, "%s", tmp));
|
||||
free (tmp);
|
||||
}
|
||||
fprintf (stderr, "%s: ", program_name);
|
||||
format_checkpoint_string (stderr, p->v.command, do_write, checkpoint);
|
||||
fputc ('\n', stderr);
|
||||
break;
|
||||
|
||||
case cop_ttyout:
|
||||
@@ -240,11 +326,9 @@ run_checkpoint_actions (bool do_write)
|
||||
tty = fopen ("/dev/tty", "w");
|
||||
if (tty)
|
||||
{
|
||||
char *tmp = expand_checkpoint_string (p->v.command, do_write,
|
||||
checkpoint);
|
||||
fprintf (tty, "%s", tmp);
|
||||
format_checkpoint_string (tty, p->v.command, do_write,
|
||||
checkpoint);
|
||||
fflush (tty);
|
||||
free (tmp);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -257,6 +341,10 @@ run_checkpoint_actions (bool do_write)
|
||||
archive_name_cursor[0],
|
||||
checkpoint);
|
||||
break;
|
||||
|
||||
case cop_totals:
|
||||
compute_duration ();
|
||||
print_total_stats ();
|
||||
}
|
||||
}
|
||||
if (tty)
|
||||
|
||||
@@ -427,7 +427,7 @@ size_t available_space_after (union block *pointer);
|
||||
off_t current_block_ordinal (void);
|
||||
void close_archive (void);
|
||||
void closeout_volume_number (void);
|
||||
void compute_duration (void);
|
||||
double compute_duration (void);
|
||||
union block *find_next_block (void);
|
||||
void flush_read (void);
|
||||
void flush_write (void);
|
||||
@@ -444,6 +444,12 @@ void archive_read_error (void);
|
||||
off_t seek_archive (off_t size);
|
||||
void set_start_time (void);
|
||||
|
||||
#define TF_READ 0
|
||||
#define TF_WRITE 1
|
||||
#define TF_DELETED 2
|
||||
int format_total_stats (FILE *fp, char **formats, int eor, int eol);
|
||||
void print_total_stats (void);
|
||||
|
||||
void mv_begin_write (const char *file_name, off_t totsize, off_t sizeleft);
|
||||
|
||||
void mv_begin_read (struct tar_stat_info *st);
|
||||
|
||||
Reference in New Issue
Block a user