X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Ftar;a=blobdiff_plain;f=src%2Fcheckpoint.c;h=46f4702b5b1c6f67e89d6f9f421e7160fad3bedb;hp=eb83cd319137bd562d801c6ebe122407a64443fd;hb=45ccda119355a1087450039a250359c1d0de0d08;hpb=5bb04335079011dfbd8547151d402b07ed152291 diff --git a/src/checkpoint.c b/src/checkpoint.c index eb83cd3..46f4702 100644 --- a/src/checkpoint.c +++ b/src/checkpoint.c @@ -1,22 +1,27 @@ /* 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 { @@ -25,7 +30,8 @@ enum checkpoint_opcode cop_echo, cop_ttyout, cop_sleep, - cop_exec + cop_exec, + cop_totals }; struct checkpoint_action @@ -108,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)); } @@ -126,68 +134,206 @@ checkpoint_finish_compile (void) 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 = + "%{%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 == '{') { + 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': + { + 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) { struct checkpoint_action *p; - FILE *tty = NULL; for (p = checkpoint_action; p; p = p->next) { @@ -210,26 +356,10 @@ run_checkpoint_actions (bool do_write) 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; @@ -237,13 +367,8 @@ run_checkpoint_actions (bool do_write) 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: @@ -255,10 +380,37 @@ run_checkpoint_actions (bool do_write) 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 */; } } - if (tty) - fclose (tty); } void @@ -267,3 +419,14 @@ 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); + } +}