X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=fish%2Fprogress.c;h=b812a57865ccca6b6b31b598966bf77af23c8f44;hp=f196786688230d8358d17ab600b775915c6b3a72;hb=04ea1375c55aa67df4e7fc61dbb534111767f3b6;hpb=54837f6d7ba83178625e2f0c3c063457d9f3f79c diff --git a/fish/progress.c b/fish/progress.c index f196786..b812a57 100644 --- a/fish/progress.c +++ b/fish/progress.c @@ -1,5 +1,5 @@ -/* guestfish - the filesystem interactive shell - * Copyright (C) 2010 Red Hat Inc. +/* 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 @@ -13,20 +13,20 @@ * * 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. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #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. @@ -39,8 +39,122 @@ */ 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; + int machine_readable; +}; + +struct progress_bar * +progress_bar_init (unsigned flags) +{ + struct progress_bar *bar; + char *term; + + bar = malloc (sizeof *bar); + if (bar == NULL) + return NULL; + + if (flags & PROGRESS_BAR_MACHINE_READABLE) { + bar->machine_readable = 1; + bar->utf8_mode = 0; + bar->have_terminfo = 0; + } else { + bar->machine_readable = 0; + + 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. * @@ -75,7 +189,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]; } @@ -87,25 +201,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 @@ -115,7 +210,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; @@ -125,17 +220,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; @@ -163,54 +258,77 @@ estimate_remaining_time (double ratio) */ #define COLS_OVERHEAD 15 -/* Callback which displays a progress bar. */ void -progress_callback (guestfs_h *g, void *data, - int proc_nr, int serial, - uint64_t position, uint64_t total) +progress_bar_set (struct progress_bar *bar, + uint64_t position, uint64_t total) { - if (have_terminfo == 0) { + int i, cols, pulse_mode; + double ratio; + const char *s_open, *s_dot, *s_dash, *s_close; + + if (bar->machine_readable || bar->have_terminfo == 0) { dumb: printf ("%" PRIu64 "/%" PRIu64 "\n", position, total); } else { - int cols = tgetnum ((char *) "co"); + cols = tgetnum ((char *) "co"); 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++; - double ratio = (double) position / total; + /* Find out if we're in "pulse mode". */ + pulse_mode = position == 0 && total == 1; + + ratio = (double) position / total; if (ratio < 0) ratio = 0; else if (ratio > 1) ratio = 1; - if (ratio < 1) { + if (pulse_mode) { + printf ("%s --- ", spinner (bar, bar->count)); + } + else if (ratio < 1) { int percent = 100.0 * ratio; - printf ("%s%3d%% ", spinner (count), percent); - } else { + printf ("%s%3d%% ", spinner (bar, bar->count), percent); + } + else { fputs (" 100% ", stdout); } - int dots = ratio * (double) (cols - COLS_OVERHEAD); - - const char *s_open, *s_dot, *s_dash, *s_close; - if (utf8_mode) { - s_open = "\u27e6"; s_dot = "\u2589"; s_dash = "\u2550"; s_close = "\u27e7"; + if (bar->utf8_mode) { + s_open = "\u27e6"; + s_dot = "\u2593"; + s_dash = "\u2550"; + s_close = "\u27e7"; } else { s_open = "["; s_dot = "#"; s_dash = "-"; s_close = "]"; } fputs (s_open, stdout); - int i; - for (i = 0; i < dots; ++i) - fputs (s_dot, stdout); - for (i = dots; i < cols - COLS_OVERHEAD; ++i) - fputs (s_dash, stdout); + + if (!pulse_mode) { + int dots = ratio * (double) (cols - COLS_OVERHEAD); + + for (i = 0; i < dots; ++i) + fputs (s_dot, stdout); + for (i = dots; i < cols - COLS_OVERHEAD; ++i) + fputs (s_dash, stdout); + } + else { /* "Pulse mode": the progress bar just pulses. */ + for (i = 0; i < cols - COLS_OVERHEAD; ++i) { + int cc = (bar->count * 3 - i) % (cols - COLS_OVERHEAD); + if (cc >= 0 && cc <= 3) + fputs (s_dot, stdout); + else + fputs (s_dash, stdout); + } + } + fputs (s_close, stdout); 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.; @@ -236,4 +354,5 @@ progress_callback (guestfs_h *g, void *data, fputc ('\n', stdout); } + fflush (stdout); }