]> Dogcows Code - chaz/tar/blobdiff - src/checkpoint.c
Update copyright years.
[chaz/tar] / src / checkpoint.c
index eb83cd319137bd562d801c6ebe122407a64443fd..46f4702b5b1c6f67e89d6f9f421e7160fad3bedb 100644 (file)
@@ -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 <http://www.gnu.org/licenses/>. */
+   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 <http://www.gnu.org/licenses/>.  */
 
 #include <system.h>
 #include "common.h"
+#include "wordsplit.h"
+#include <sys/ioctl.h>
+#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);
+    }
+}
This page took 0.028528 seconds and 4 git commands to generate.