/************************************************************************************************** $Header: /pub/cvsroot/yencode/src/ypost/meter.c,v 1.2 2002/03/21 04:58:31 bboy Exp $ Routines used by `ypost' to output progress meters. Copyright (C) 2002 Don Moore 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA **************************************************************************************************/ #include "ypost.h" static int screen_width = -1; /* Var for holding screen width */ struct timeval meter_start; /* Time meter started counting */ struct timeval meter_last; /* Last time meter was displayed */ static char meter_text[METER_MAX + 1]; /* Current meter description */ static unsigned long meter_current = 0, meter_total = 0; /* Meter current/total */ static int meter_width = -1; /* Current meter width */ static void meter_redraw(void); /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SET_SCREEN_WIDTH Handler for SIGWINCH - sets the screen width. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ void set_screen_width(int signo) { screen_width = get_screen_width(); meter_redraw(); return; } /*--- set_screen_width() ------------------------------------------------------------------------*/ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ METER Outputs a progress meter. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ void meter(unsigned long current, unsigned long total, const char *fmt, ...) { struct timeval now; va_list ap; int textmsg_len; char meterbuf[METER_MAX + 1], dotbox[DOTBOX_MAX + 1]; int dotbox_width; int pct = 0, fill = 0; char speedbuf[30], etabuf[30]; if (!isatty(STDOUT_FILENO) || !err_verbose || !screen_width) return; if (screen_width == -1 && !(screen_width = get_screen_width())) return; gettimeofday(&now, NULL); if (tvdiff(&meter_last, &now) < 0.2) return; meter_last.tv_sec = now.tv_sec; meter_last.tv_usec = now.tv_usec; if (meter_width) fwrite("\r", sizeof(char), 1, stdout); if (!meter_start.tv_sec) gettimeofday(&meter_start, NULL); va_start(ap, fmt); textmsg_len = vsnprintf(meter_text, sizeof(meter_text), fmt, ap); va_end(ap); /* Set width of dotbox and make sure it's a reasonable value */ dotbox_width = (screen_width - 1) - textmsg_len - 10; if (dotbox_width < DOTBOX_MIN) return; if (dotbox_width > DOTBOX_MAX) dotbox_width = DOTBOX_MAX; *etabuf = '\0'; *speedbuf = '\0'; /* Get percentage complete */ pct = (total) ? (int)(100.0 * current / total) : 0; /* If we have plenty of room, also output other helpful stats */ if ((dotbox_width > DOTBOX_EXTRA_STATS) && total && (current > 1024)) { float elapsed = timer_elapsed(&meter_start); float k_sec = (elapsed) ? (float)((float)current / elapsed) / 1024.0 : 0.0; int eta_s = (elapsed) ? (int)((100.0 - pct) * (float)((float)elapsed / (float)pct)): 0; int eta_h, eta_m; if (elapsed) { if (k_sec > 1024.0) dotbox_width -= snprintf(speedbuf, sizeof(speedbuf), " %6.1fM/s", (float)(k_sec / 1024.0)); else dotbox_width -= snprintf(speedbuf, sizeof(speedbuf), " %6.1fk/s", k_sec); eta_h = eta_s / 3600, eta_s %= 3600; eta_m = eta_s / 60, eta_s %= 60; if (eta_s >= 0 && eta_h > 0) dotbox_width -= snprintf(etabuf, sizeof(etabuf), " ETA: %02d:%02d:%02d", eta_h, eta_m, eta_s); else if (eta_s >= 0) dotbox_width -= snprintf(etabuf, sizeof(etabuf), " ETA: %02d:%02d", eta_m, eta_s); } } /* Construct dotbox */ memset(dotbox, ' ', dotbox_width); dotbox[dotbox_width] = '\0'; if (total) { fill = (int)((dotbox_width / 100.0) * pct); if (fill > dotbox_width) fill = dotbox_width; memset(dotbox, '=', fill); } /* Save values */ meter_current = current; meter_total = total; /* Output meter */ meter_width = snprintf(meterbuf, sizeof(meterbuf), "%s [%s] %3d%%%s%s ", meter_text, dotbox, pct, speedbuf, etabuf); fwrite(meterbuf, meter_width * sizeof(char), 1, stdout); fflush(stdout); } /*--- meter() -----------------------------------------------------------------------------------*/ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ METER_CLEAR Clears the progress meter if any is on screen. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ void meter_clear(void) { char buf[METER_MAX + 3]; if (!isatty(STDOUT_FILENO) || !err_verbose || !screen_width || meter_width <= 0) return; memset(buf, ' ', meter_width); fwrite("\r", sizeof(char), 1, stdout); fwrite(buf, meter_width * sizeof(char), 1, stdout); fwrite("\r", sizeof(char), 1, stdout); fflush(stdout); meter_width = 0; meter_start.tv_sec = meter_start.tv_usec = 0; meter_last.tv_sec = meter_last.tv_usec = 0; meter_current = meter_total = 0; fflush(stdout); } /*--- meter_clear() -----------------------------------------------------------------------------*/ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ METER_REDRAW Redraws the current meter (if the window size changed) +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ void meter_redraw(void) { char buf[METER_MAX + 3]; if (!isatty(STDOUT_FILENO) || meter_width <= 0) return; memset(buf, ' ', meter_width); fwrite("\r", sizeof(char), 1, stdout); fwrite(buf, meter_width * sizeof(char), 1, stdout); fwrite("\r", sizeof(char), 1, stdout); fflush(stdout); meter_width = 0; meter_last.tv_sec = meter_last.tv_usec = 0; meter(meter_current, meter_total, "%s", meter_text); } /*--- meter_redraw() ----------------------------------------------------------------------------*/ /* vi:set ts=3: */