/**************************************************************************************************
	$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 <bboy@bboy.net>

	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: */


syntax highlighted by Code2HTML, v. 0.9.1