X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fcheckpoint.c;h=46f4702b5b1c6f67e89d6f9f421e7160fad3bedb;hb=87063014799dc78af45710ba5f40cd5dfe341a53;hp=5eaaea550cfa5e97d8695a6ae334e75b2caec2d7;hpb=17cbd4862c3d44e55248195421c59e41a55e9165;p=chaz%2Ftar
diff --git a/src/checkpoint.c b/src/checkpoint.c
index 5eaaea5..46f4702 100644
--- a/src/checkpoint.c
+++ b/src/checkpoint.c
@@ -1,29 +1,37 @@
/* Checkpoint management for tar.
- Copyright (C) 2007 Free Software Foundation, Inc.
+ Copyright 2007, 2013-2014 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, or (at your option) any later
- version.
+ This file is part of GNU tar.
- 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.
+ GNU tar 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.
- You should have received a copy of the GNU General Public License along
- with this program. If not, see . */
+ GNU tar 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 . */
#include
#include "common.h"
+#include "wordsplit.h"
+#include
+#include "fprintftime.h"
enum checkpoint_opcode
{
cop_dot,
+ cop_bell,
cop_echo,
+ cop_ttyout,
cop_sleep,
- cop_exec
+ cop_exec,
+ cop_totals
};
struct checkpoint_action
@@ -75,9 +83,11 @@ void
checkpoint_compile_action (const char *str)
{
struct checkpoint_action *act;
-
+
if (strcmp (str, ".") == 0 || strcmp (str, "dot") == 0)
alloc_action (cop_dot);
+ else if (strcmp (str, "bell") == 0)
+ alloc_action (cop_bell);
else if (strcmp (str, "echo") == 0)
alloc_action (cop_echo);
else if (strncmp (str, "echo=", 5) == 0)
@@ -90,6 +100,11 @@ checkpoint_compile_action (const char *str)
act = alloc_action (cop_exec);
act->v.command = copy_string_unquote (str + 5);
}
+ else if (strncmp (str, "ttyout=", 7) == 0)
+ {
+ act = alloc_action (cop_ttyout);
+ act->v.command = copy_string_unquote (str + 7);
+ }
else if (strncmp (str, "sleep=", 6) == 0)
{
char *p;
@@ -99,81 +114,222 @@ 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));
}
void
-checkpoint_finish_compile ()
+checkpoint_finish_compile (void)
{
if (checkpoint_option)
{
if (!checkpoint_action)
/* Provide a historical default */
- checkpoint_compile_action ("echo");
+ checkpoint_compile_action ("echo");
}
else if (checkpoint_action)
/* Otherwise, set default checkpoint rate */
checkpoint_option = DEFAULT_CHECKPOINT;
}
-char *
-expand_checkpoint_string (const char *input, bool do_write, unsigned cpn)
+static const char *checkpoint_total_format[] = {
+ "R",
+ "W",
+ "D"
+};
+
+static int
+getwidth (FILE *fp)
+{
+ struct winsize ws;
+
+ ws.ws_col = ws.ws_row = 0;
+ if ((ioctl (fileno (fp), TIOCGWINSZ, (char *) &ws) < 0) || ws.ws_col == 0)
+ {
+ const char *col = getenv ("COLUMNS");
+ if (col)
+ return strtol (col, NULL, 10);
+ else
+ return 80;
+ }
+ return ws.ws_col;
+}
+
+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;
+ }
+ }
+
+ *endp = input;
+ return NULL;
+}
+
+static int tty_cleanup;
+
+static const char *def_format =
+ "%{%Y-%m-%d %H:%M:%S}t: %ds, %{read,wrote}T%*\r";
+
+static int
+format_checkpoint_string (FILE *fp, size_t len,
+ const char *input, bool do_write,
+ unsigned cpn)
{
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 */
- /* Fix the initial length guess */
- for (ip = input; (ip = strchr (ip, '%')) != NULL; )
+ static char *argbuf = NULL;
+ static size_t arglen = 0;
+ char *arg = NULL;
+
+ if (!input)
{
- switch (ip[1])
- {
- case 'u':
- outlen += cpslen - 2;
- break;
-
- case 's':
- outlen += opstrlen - 2;
- }
- ip++;
+ 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");
}
-
- output = xmalloc (outlen + 1);
- for (ip = input, op = output; *ip; )
+
+ for (ip = input; *ip; ip++)
{
if (*ip == '%')
{
- switch (*++ip)
+ if (*++ip == '{')
{
- case 'u':
- op = stpcpy (op, cps);
+ arg = getarg (ip, &ip, &argbuf, &arglen);
+ if (!arg)
+ {
+ fputc ('%', fp);
+ fputc (*ip, fp);
+ len += 2;
+ continue;
+ }
+ }
+ switch (*ip)
+ {
+ case 'c':
+ len += format_checkpoint_string (fp, len, def_format, do_write,
+ cpn);
break;
+ case 'u':
+ 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':
+ {
+ const char **fmt = checkpoint_total_format, *fmtbuf[3];
+ struct wordsplit ws;
+ compute_duration ();
+
+ if (arg)
+ {
+ ws.ws_delim = ",";
+ if (wordsplit (arg, &ws, WRDSF_NOVAR | WRDSF_NOCMD |
+ WRDSF_QUOTE | WRDSF_DELIM))
+ ERROR ((0, 0, _("cannot split string '%s': %s"),
+ arg, wordsplit_strerror (&ws)));
+ else
+ {
+ int i;
+
+ for (i = 0; i < ws.ws_wordc; i++)
+ fmtbuf[i] = ws.ws_wordv[i];
+ for (; i < 3; i++)
+ fmtbuf[i] = NULL;
+ fmt = fmtbuf;
+ }
+ }
+ len += format_total_stats (fp, fmt, ',', 0);
+ if (arg)
+ wordsplit_free (&ws);
+ }
+ break;
+
+ case 't':
+ {
+ struct timeval tv;
+ struct tm *tm;
+ const 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;
+ tty_cleanup = 1;
+ }
+ else
+ len++;
+ }
}
- *op = 0;
- return output;
+ fflush (fp);
+ return len;
}
+static FILE *tty = NULL;
+
static void
run_checkpoint_actions (bool do_write)
{
@@ -187,41 +343,72 @@ run_checkpoint_actions (bool do_write)
fputc ('.', stdlis);
fflush (stdlis);
break;
-
+
+ case cop_bell:
+ if (!tty)
+ tty = fopen ("/dev/tty", "w");
+ if (tty)
+ {
+ fputc ('\a', tty);
+ fflush (tty);
+ }
+ 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);
+ int n = fprintf (stderr, "%s: ", program_name);
+ format_checkpoint_string (stderr, n, p->v.command, do_write,
+ checkpoint);
+ fputc ('\n', stderr);
}
break;
-
+
+ case cop_ttyout:
+ if (!tty)
+ tty = fopen ("/dev/tty", "w");
+ if (tty)
+ format_checkpoint_string (tty, 0, p->v.command, do_write,
+ checkpoint);
+ break;
+
case cop_sleep:
sleep (p->v.time);
break;
-
+
case cop_exec:
sys_exec_checkpoint_script (p->v.command,
archive_name_cursor[0],
checkpoint);
break;
+
+ case cop_totals:
+ compute_duration ();
+ print_total_stats ();
+ }
+ }
+}
+
+void
+checkpoint_flush_actions (void)
+{
+ struct checkpoint_action *p;
+
+ for (p = checkpoint_action; p; p = p->next)
+ {
+ switch (p->opcode)
+ {
+ case cop_ttyout:
+ if (tty && tty_cleanup)
+ {
+ int w = getwidth (tty);
+ while (w--)
+ fputc (' ', tty);
+ fputc ('\r', tty);
+ fflush (tty);
+ }
+ break;
+ default:
+ /* nothing */;
}
}
}
@@ -231,5 +418,15 @@ checkpoint_run (bool do_write)
{
if (checkpoint_option && !(++checkpoint % checkpoint_option))
run_checkpoint_actions (do_write);
-}
+}
+void
+checkpoint_finish (void)
+{
+ if (checkpoint_option)
+ {
+ checkpoint_flush_actions ();
+ if (tty)
+ fclose (tty);
+ }
+}