#include <system.h>
#include "common.h"
+#include "wordsplit.h"
+#include <sys/ioctl.h>
+#include "fprintftime.h"
enum checkpoint_opcode
{
cop_echo,
cop_ttyout,
cop_sleep,
- cop_exec
+ cop_exec,
+ cop_totals
};
struct checkpoint_action
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));
}
checkpoint_option = DEFAULT_CHECKPOINT;
}
+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 *
-expand_checkpoint_string (const char *input, bool do_write, unsigned cpn)
+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 = "%s: %t: %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 == '{')
+ {
+ 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':
- 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;
+ 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)
{
struct checkpoint_action *p;
- FILE *tty = NULL;
for (p = checkpoint_action; p; p = p->next)
{
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;
if (!tty)
tty = fopen ("/dev/tty", "w");
if (tty)
- {
- char *tmp = expand_checkpoint_string (p->v.command, do_write,
- checkpoint);
- fprintf (tty, "%s", tmp);
- fflush (tty);
- free (tmp);
- }
+ format_checkpoint_string (tty, 0, p->v.command, do_write,
+ checkpoint);
break;
case cop_sleep:
archive_name_cursor[0],
checkpoint);
break;
+
+ case cop_totals:
+ compute_duration ();
+ print_total_stats ();
+ }
+ }
+}
+
+static void
+finish_checkpoint_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);
+ }
+ break;
+ default:
+ /* nothing */;
}
}
if (tty)
if (checkpoint_option && !(++checkpoint % checkpoint_option))
run_checkpoint_actions (do_write);
}
+
+void
+checkpoint_finish (void)
+{
+ if (checkpoint_option)
+ finish_checkpoint_actions ();
+}