From: Richard W.M. Jones Date: Fri, 26 Aug 2011 17:17:39 +0000 (+0100) Subject: fish: Make progress bars into a mini library. X-Git-Tag: 1.13.9~6 X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=commitdiff_plain;h=6146412f06c2f6f33c3ea7d571f16d4fe71dddb2 fish: Make progress bars into a mini library. This library could now be called from other virt tools. --- diff --git a/fish/Makefile.am b/fish/Makefile.am index 3be14fa..dd0625d 100644 --- a/fish/Makefile.am +++ b/fish/Makefile.am @@ -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 C 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 \ diff --git a/fish/fish.c b/fish/fish.c index 1bc8447..3104c40 100644 --- a/fish/fish.c +++ b/fish/fish.c @@ -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 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); +} diff --git a/fish/fish.h b/fish/fish.h index 8cf14a8..8c75a75 100644 --- a/fish/fish.h +++ b/fish/fish.h @@ -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[], diff --git a/fish/progress.c b/fish/progress.c index fea9384..623dd0e 100644 --- a/fish/progress.c +++ b/fish/progress.c @@ -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 @@ -20,14 +20,13 @@ #include #include +#include #include #include #include +#include -#include - -#include "fish.h" -#include "rmsd.h" +#include "progress.h" /* Include these last since they redefine symbols such as 'lines' * which seriously breaks other headers. @@ -40,8 +39,113 @@ */ 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 */ estimate /= 60. * 60.; diff --git a/fish/progress.h b/fish/progress.h new file mode 100644 index 0000000..ad9d23a --- /dev/null +++ b/fish/progress.h @@ -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 + +#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 index d4335bd..0000000 --- a/fish/rmsd.h +++ /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 */