fish: Make progress bars into a mini library.
authorRichard W.M. Jones <rjones@redhat.com>
Fri, 26 Aug 2011 17:17:39 +0000 (18:17 +0100)
committerRichard W.M. Jones <rjones@redhat.com>
Fri, 26 Aug 2011 20:54:59 +0000 (21:54 +0100)
This library could now be called from other virt tools.

fish/Makefile.am
fish/fish.c
fish/fish.h
fish/progress.c
fish/progress.h [new file with mode: 0644]
fish/rmsd.h [deleted file]

index 3be14fa..dd0625d 100644 (file)
@@ -57,8 +57,8 @@ EXTRA_DIST = \
        virt-tar-in.pod \
        virt-tar-out.pod
 
-# These source files (all related to option parsing) are shared
-# between guestfish, guestmount and some other virt tools.  Keep a
+# These source files (mostly related to option parsing) are shared
+# between guestfish, guestmount and some other virt tools.  Keep a
 # convenient list here just so we know which ones are shared.  These
 # files must not include other guestfish files.
 SHARED_SOURCE_FILES = \
@@ -67,6 +67,8 @@ SHARED_SOURCE_FILES = \
        keys.c \
        options.h \
        options.c \
+       progress.h \
+       progress.c \
        virt.c
 
 guestfish_SOURCES = \
@@ -93,10 +95,8 @@ guestfish_SOURCES = \
        prep_fs.c \
        prep_lv.c \
        prep_boot.c \
-       progress.c \
        rc.c \
        reopen.c \
-       rmsd.h \
        setenv.c \
        supported.c \
        tilde.c \
index 1bc8447..3104c40 100644 (file)
@@ -40,6 +40,7 @@
 
 #include "fish.h"
 #include "options.h"
+#include "progress.h"
 
 #include "c-ctype.h"
 #include "closeout.h"
@@ -54,7 +55,6 @@ struct parsed_command {
 };
 
 static void user_cancel (int);
-static void set_up_terminal (void);
 static void prepare_drives (struct drv *drv);
 static int launch (void);
 static void interactive (void);
@@ -72,6 +72,7 @@ static void add_history_line (const char *);
 #endif
 
 static int override_progress_bars = -1;
+static struct progress_bar *bar = NULL;
 
 /* Currently open libguestfs handle. */
 guestfs_h *g = NULL;
@@ -88,8 +89,6 @@ int keys_from_stdin = 0;
 int echo_keys = 0;
 const char *libvirt_uri = NULL;
 int inspector = 0;
-int utf8_mode = 0;
-int have_terminfo = 0;
 int progress_bars = 0;
 int is_interactive = 0;
 const char *input_file = NULL;
@@ -166,8 +165,6 @@ main (int argc, char *argv[])
 
   parse_config ();
 
-  set_up_terminal ();
-
   enum { HELP_OPTION = CHAR_MAX + 1 };
 
   static const char *options = "a:c:d:Df:h::im:nN:rv?Vwx";
@@ -524,9 +521,16 @@ main (int argc, char *argv[])
     ? override_progress_bars
     : (optind >= argc && is_interactive);
 
-  if (progress_bars)
+  if (progress_bars) {
+    bar = progress_bar_init (0);
+    if (!bar) {
+      perror ("progress_bar_init");
+      exit (EXIT_FAILURE);
+    }
+
     guestfs_set_event_callback (g, progress_callback,
                                 GUESTFS_EVENT_PROGRESS, 0, NULL);
+  }
 
   /* Interactive, shell script, or command(s) on the command line? */
   if (optind >= argc) {
@@ -540,6 +544,9 @@ main (int argc, char *argv[])
 
   cleanup_readline ();
 
+  if (progress_bars)
+    progress_bar_free (bar);
+
   exit (EXIT_SUCCESS);
 }
 
@@ -550,35 +557,6 @@ user_cancel (int sig)
     guestfs_user_cancel (g);
 }
 
-/* The <term.h> header file which defines this has "issues". */
-extern int tgetent (char *, const char *);
-
-static void
-set_up_terminal (void)
-{
-  /* http://www.cl.cam.ac.uk/~mgk25/unicode.html#activate */
-  utf8_mode = STREQ (nl_langinfo (CODESET), "UTF-8");
-
-  char *term = getenv ("TERM");
-  if (term == NULL) {
-    //fprintf (stderr, _("guestfish: TERM (terminal type) not defined.\n"));
-    return;
-  }
-
-  int r = tgetent (NULL, term);
-  if (r == -1) {
-    fprintf (stderr, _("guestfish: could not access termcap or terminfo database.\n"));
-    return;
-  }
-  if (r == 0) {
-    fprintf (stderr, _("guestfish: terminal type \"%s\" not defined.\n"),
-             term);
-    return;
-  }
-
-  have_terminfo = 1;
-}
-
 static void
 prepare_drives (struct drv *drv)
 {
@@ -1058,7 +1036,8 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd,
   int pid = 0;
   int r;
 
-  reset_progress_bar ();
+  if (progress_bars)
+    progress_bar_reset (bar);
 
   /* This counts the commands issued, starting at 1. */
   command_num++;
@@ -1768,3 +1747,21 @@ file_out (const char *arg)
   }
   return ret;
 }
+
+/* Callback which displays a progress bar. */
+void
+progress_callback (guestfs_h *g, void *data,
+                   uint64_t event, int event_handle, int flags,
+                   const char *buf, size_t buf_len,
+                   const uint64_t *array, size_t array_len)
+{
+  if (array_len < 4)
+    return;
+
+  /*uint64_t proc_nr = array[0];*/
+  /*uint64_t serial = array[1];*/
+  uint64_t position = array[2];
+  uint64_t total = array[3];
+
+  progress_bar_set (bar, position, total);
+}
index 8cf14a8..8c75a75 100644 (file)
@@ -60,8 +60,6 @@ extern int read_only;
 extern int quit;
 extern int verbose;
 extern int command_num;
-extern int utf8_mode;
-extern int have_terminfo;
 extern int progress_bars;
 extern int remote_control_csh;
 extern const char *libvirt_uri;
@@ -82,6 +80,7 @@ extern char *file_in (const char *arg);
 extern void free_file_in (char *s);
 extern char *file_out (const char *arg);
 extern void extended_help_message (void);
+extern void progress_callback (guestfs_h *g, void *data, uint64_t event, int event_handle, int flags, const char *buf, size_t buf_len, const uint64_t *array, size_t array_len);
 
 /* in cmds.c (auto-generated) */
 extern void list_commands (void);
@@ -121,10 +120,6 @@ extern void free_prep_data (void *data);
 /* in prep_lv.c */
 extern int vg_lv_parse (const char *device, char **vg, char **lv);
 
-/* in progress.c */
-extern void reset_progress_bar (void);
-extern void progress_callback (guestfs_h *g, void *data, uint64_t event, int event_handle, int flags, const char *buf, size_t buf_len, const uint64_t *array, size_t array_len);
-
 /* in rc.c (remote control) */
 extern void rc_listen (void) __attribute__((noreturn));
 extern int rc_remote (int pid, const char *cmd, size_t argc, char *argv[],
index fea9384..623dd0e 100644 (file)
@@ -1,4 +1,4 @@
-/* guestfish - the filesystem interactive shell
+/* libguestfs - mini library for progress bars.
  * Copyright (C) 2010-2011 Red Hat Inc.
  *
  * This program is free software; you can redistribute it and/or modify
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <inttypes.h>
 #include <math.h>
 #include <sys/time.h>
+#include <langinfo.h>
 
-#include <guestfs.h>
-
-#include "fish.h"
-#include "rmsd.h"
+#include "progress.h"
 
 /* Include these last since they redefine symbols such as 'lines'
  * which seriously breaks other headers.
  */
 extern const char *UP;
 
+#define STREQ(a,b) (strcmp((a),(b)) == 0)
+
+/* Compute the running mean and standard deviation from the
+ * series of estimated values.
+ *
+ * Method:
+ * http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
+ * Checked in a test program against answers given by Wolfram Alpha.
+ */
+struct rmsd {
+  double a;                     /* mean */
+  double i;                     /* number of samples */
+  double q;
+};
+
+static void
+rmsd_init (struct rmsd *r)
+{
+  r->a = 0;
+  r->i = 1;
+  r->q = 0;
+}
+
+static void
+rmsd_add_sample (struct rmsd *r, double x)
+{
+  double a_next, q_next;
+
+  a_next = r->a + (x - r->a) / r->i;
+  q_next = r->q + (x - r->a) * (x - a_next);
+  r->a = a_next;
+  r->q = q_next;
+  r->i += 1.0;
+}
+
+static double
+rmsd_get_mean (const struct rmsd *r)
+{
+  return r->a;
+}
+
+static double
+rmsd_get_standard_deviation (const struct rmsd *r)
+{
+  return sqrt (r->q / (r->i - 1.0));
+}
+
+struct progress_bar {
+  double start;         /* start time of command */
+  int count;            /* number of progress notifications per cmd */
+  struct rmsd rmsd;     /* running mean and standard deviation */
+  int have_terminfo;
+  int utf8_mode;
+};
+
+struct progress_bar *
+progress_bar_init (unsigned flags)
+{
+  struct progress_bar *bar;
+  char *term;
+
+  bar = malloc (sizeof *bar);
+  if (bar == NULL)
+    return NULL;
+
+  bar->utf8_mode = STREQ (nl_langinfo (CODESET), "UTF-8");
+
+  bar->have_terminfo = 0;
+
+  term = getenv ("TERM");
+  if (term) {
+    if (tgetent (NULL, term) == 1)
+      bar->have_terminfo = 1;
+  }
+
+  /* Call this to ensure the other fields are in a reasonable state.
+   * It is still the caller's responsibility to reset the progress bar
+   * before each command.
+   */
+  progress_bar_reset (bar);
+
+  return bar;
+}
+
+void
+progress_bar_free (struct progress_bar *bar)
+{
+  free (bar);
+}
+
+/* This function is called just before we issue any command. */
+void
+progress_bar_reset (struct progress_bar *bar)
+{
+  /* The time at which this command was issued. */
+  struct timeval start_t;
+  gettimeofday (&start_t, NULL);
+
+  bar->start = start_t.tv_sec + start_t.tv_usec / 1000000.;
+
+  bar->count = 0;
+
+  rmsd_init (&bar->rmsd);
+}
+
 static const char *
-spinner (int count)
+spinner (struct progress_bar *bar, int count)
 {
   /* Choice of unicode spinners.
    *
@@ -76,7 +180,7 @@ spinner (int count)
   const char **s;
   size_t n;
 
-  if (utf8_mode) {
+  if (bar->utf8_mode) {
     s = us;
     n = sizeof us / sizeof us[0];
   }
@@ -88,25 +192,6 @@ spinner (int count)
   return s[count % n];
 }
 
-static double start;         /* start time of command */
-static int count;            /* number of progress notifications per cmd */
-static struct rmsd rmsd;     /* running mean and standard deviation */
-
-/* This function is called just before we issue any command. */
-void
-reset_progress_bar (void)
-{
-  /* The time at which this command was issued. */
-  struct timeval start_t;
-  gettimeofday (&start_t, NULL);
-
-  start = start_t.tv_sec + start_t.tv_usec / 1000000.;
-
-  count = 0;
-
-  rmsd_init (&rmsd);
-}
-
 /* Return remaining time estimate (in seconds) for current call.
  *
  * This returns the running mean estimate of remaining time, but if
@@ -116,7 +201,7 @@ reset_progress_bar (void)
  * when nothing should be printed).
  */
 static double
-estimate_remaining_time (double ratio)
+estimate_remaining_time (struct progress_bar *bar, double ratio)
 {
   if (ratio <= 0.)
     return -1.0;
@@ -126,17 +211,17 @@ estimate_remaining_time (double ratio)
 
   double now = now_t.tv_sec + now_t.tv_usec / 1000000.;
   /* We've done 'ratio' of the work in 'now - start' seconds. */
-  double time_passed = now - start;
+  double time_passed = now - bar->start;
 
   double total_time = time_passed / ratio;
 
   /* Add total_time to running mean and s.d. and then see if our
    * estimate of total time is meaningful.
    */
-  rmsd_add_sample (&rmsd, total_time);
+  rmsd_add_sample (&bar->rmsd, total_time);
 
-  double mean = rmsd_get_mean (&rmsd);
-  double sd = rmsd_get_standard_deviation (&rmsd);
+  double mean = rmsd_get_mean (&bar->rmsd);
+  double sd = rmsd_get_standard_deviation (&bar->rmsd);
   if (fabs (total_time - mean) >= 2.0*sd)
     return -1.0;
 
@@ -164,32 +249,21 @@ estimate_remaining_time (double ratio)
  */
 #define COLS_OVERHEAD 15
 
-/* Callback which displays a progress bar. */
 void
-progress_callback (guestfs_h *g, void *data,
-                   uint64_t event, int event_handle, int flags,
-                   const char *buf, size_t buf_len,
-                   const uint64_t *array, size_t array_len)
+progress_bar_set (struct progress_bar *bar,
+                  uint64_t position, uint64_t total)
 {
   int i, cols, pulse_mode;
   double ratio;
   const char *s_open, *s_dot, *s_dash, *s_close;
 
-  if (utf8_mode) {
+  if (bar->utf8_mode) {
     s_open = "\u27e6"; s_dot = "\u2589"; s_dash = "\u2550"; s_close = "\u27e7";
   } else {
     s_open = "["; s_dot = "#"; s_dash = "-"; s_close = "]";
   }
 
-  if (array_len < 4)
-    return;
-
-  /*uint64_t proc_nr = array[0];*/
-  /*uint64_t serial = array[1];*/
-  uint64_t position = array[2];
-  uint64_t total = array[3];
-
-  if (have_terminfo == 0) {
+  if (bar->have_terminfo == 0) {
   dumb:
     printf ("%" PRIu64 "/%" PRIu64 "\n", position, total);
   } else {
@@ -197,9 +271,9 @@ progress_callback (guestfs_h *g, void *data,
     if (cols < 32) goto dumb;
 
     /* Update an existing progress bar just printed? */
-    if (count > 0)
+    if (bar->count > 0)
       tputs (UP, 2, putchar);
-    count++;
+    bar->count++;
 
     /* Find out if we're in "pulse mode". */
     pulse_mode = position == 0 && total == 1;
@@ -208,11 +282,11 @@ progress_callback (guestfs_h *g, void *data,
     if (ratio < 0) ratio = 0; else if (ratio > 1) ratio = 1;
 
     if (pulse_mode) {
-      printf ("%s --- ", spinner (count));
+      printf ("%s --- ", spinner (bar, bar->count));
     }
     else if (ratio < 1) {
       int percent = 100.0 * ratio;
-      printf ("%s%3d%% ", spinner (count), percent);
+      printf ("%s%3d%% ", spinner (bar, bar->count), percent);
     }
     else {
       fputs (" 100% ", stdout);
@@ -230,7 +304,7 @@ progress_callback (guestfs_h *g, void *data,
     }
     else {           /* "Pulse mode": the progress bar just pulses. */
       for (i = 0; i < cols - COLS_OVERHEAD; ++i) {
-        int cc = (count * 3 - i) % (cols - COLS_OVERHEAD);
+        int cc = (bar->count * 3 - i) % (cols - COLS_OVERHEAD);
         if (cc >= 0 && cc <= 3)
           fputs (s_dot, stdout);
         else
@@ -242,7 +316,7 @@ progress_callback (guestfs_h *g, void *data,
     fputc (' ', stdout);
 
     /* Time estimate. */
-    double estimate = estimate_remaining_time (ratio);
+    double estimate = estimate_remaining_time (bar, ratio);
     if (estimate >= 100.0 * 60.0 * 60.0 /* >= 100 hours */) {
       /* Display hours<h> */
       estimate /= 60. * 60.;
diff --git a/fish/progress.h b/fish/progress.h
new file mode 100644 (file)
index 0000000..ad9d23a
--- /dev/null
@@ -0,0 +1,43 @@
+/* libguestfs - mini library for progress bars.
+ * Copyright (C) 2010-2011 Red Hat 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#ifndef PROGRESS_H
+#define PROGRESS_H
+
+struct progress_bar;
+
+/* Initialize the progress bar mini library.
+ *
+ * Function returns a handle, or NULL if there was an error.
+ */
+extern struct progress_bar *progress_bar_init (unsigned flags);
+
+/* This should be called at the start of each command. */
+extern void progress_bar_reset (struct progress_bar *);
+
+/* This should be called from the progress bar callback.  It displays
+ * the progress bar.
+ */
+extern void progress_bar_set (struct progress_bar *, uint64_t position, uint64_t total);
+
+/* Free up progress bar handle and resources. */
+extern void progress_bar_free (struct progress_bar *);
+
+#endif /* PROGRESS_H */
diff --git a/fish/rmsd.h b/fish/rmsd.h
deleted file mode 100644 (file)
index d4335bd..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/* libguestfs - guestfish shell
- * Copyright (C) 2010 Red Hat 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 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef FISH_RMSD_H
-#define FISH_RMSD_H
-
-/* Compute the running mean and standard deviation from the
- * series of estimated values.
- *
- * Method:
- * http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
- * Checked in a test program against answers given by Wolfram Alpha.
- */
-struct rmsd {
-  double a;                     /* mean */
-  double i;                     /* number of samples */
-  double q;
-};
-
-static void
-rmsd_init (struct rmsd *r)
-{
-  r->a = 0;
-  r->i = 1;
-  r->q = 0;
-}
-
-static void
-rmsd_add_sample (struct rmsd *r, double x)
-{
-  double a_next, q_next;
-
-  a_next = r->a + (x - r->a) / r->i;
-  q_next = r->q + (x - r->a) * (x - a_next);
-  r->a = a_next;
-  r->q = q_next;
-  r->i += 1.0;
-}
-
-static double
-rmsd_get_mean (const struct rmsd *r)
-{
-  return r->a;
-}
-
-static double
-rmsd_get_standard_deviation (const struct rmsd *r)
-{
-  return sqrt (r->q / (r->i - 1.0));
-}
-
-#endif /* FISH_RMSD_H */