[Ocfs2-tools-devel] [PATCH 03/18] libtools-internal: Create an API for displaying a progress bar.

Mark Fasheh mfasheh at suse.com
Wed Jan 14 14:45:32 PST 2009


Looks good, fixes included.

Signed-off-by: Mark Fasheh <mfasheh at suse.com>


On Mon, Jan 05, 2009 at 06:33:39PM -0800, Joel Becker wrote:
> libtools-internal gets an API for displaying progress in a progress bar.
> The progress bar has some nice features:
> 
> - It checks the terminal size and adjusts the output accordingly
> - It updates only when something has changed, and only every 1/8s.
> - It is integrated with verbosef() and tools_interact(), such that it
>   is cleared before they write something, and redisplayed when they are
>   done.
> 
> See include/tools-internal/progress.h for information on using the API.
> 
> Signed-off-by: Joel Becker <joel.becker at oracle.com>
> ---
>  include/tools-internal/Makefile       |    2 +-
>  include/tools-internal/progress.h     |   86 +++++
>  libtools-internal/.gitignore          |    1 +
>  libtools-internal/Makefile            |    3 +-
>  libtools-internal/libtools-internal.h |   30 ++
>  libtools-internal/progress.c          |  648 +++++++++++++++++++++++++++++++++
>  libtools-internal/verbose.c           |   31 ++-
>  7 files changed, 798 insertions(+), 3 deletions(-)
>  create mode 100644 include/tools-internal/progress.h
>  create mode 100644 libtools-internal/libtools-internal.h
>  create mode 100644 libtools-internal/progress.c
> 
> diff --git a/include/tools-internal/Makefile b/include/tools-internal/Makefile
> index 374f18b..3e5649e 100644
> --- a/include/tools-internal/Makefile
> +++ b/include/tools-internal/Makefile
> @@ -2,7 +2,7 @@ TOPDIR = ../..
>  
>  include $(TOPDIR)/Preamble.make
>  
> -HFILES = verbose.h
> +HFILES = verbose.h progress.h
>  
>  DIST_FILES = $(HFILES)
>  
> diff --git a/include/tools-internal/progress.h b/include/tools-internal/progress.h
> new file mode 100644
> index 0000000..a5e2dd2
> --- /dev/null
> +++ b/include/tools-internal/progress.h
> @@ -0,0 +1,86 @@
> +/* -*- mode: c; c-basic-offset: 8; -*-
> + * vim: noexpandtab sw=8 ts=8 sts=0:
> + *
> + * progress.h
> + *
> + * Internal progress output functions.
> + *
> + * Copyright (C) 2008 Oracle.  All rights reserved.
> + *
> + * 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.
> + *
> + * 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.
> + */
> +
> +#ifndef _INTERNAL_PROGRESS_H
> +#define _INTERNAL_PROGRESS_H
> +
> +struct tools_progress;
> +
> +/*
> + * Enables progress display.  This is generally called when a program
> + * is passed the '--progress' option.
> + */
> +void tools_progress_enable(void);
> +/*
> + * Disable progress display.  This is a true disable.  Users do not need
> + * to disable by hand when using verbosef() and friends for output.  They
> + * interact correctly with the progress display.
> + */
> +void tools_progress_disable(void);
> +
> +/*
> + * Callers should use the progress API unconditionally.  If the progress
> + * display is not enabled, the functions are no-ops.
> + *
> + * The progress bar can contain multiple progress displays.  A toplevel
> + * action may run a sub-action that takes time.  Thus, the bar displays
> + * both the progress state of the toplevel action and the sub-action.
> + * 
> + * The sub-action can start its own progress display without knowledge
> + * of the toplevel.  The progress code understands how to handle it.
> + */
> +
> +/*
> + * Start a new progress item.
> + *
> + * The long name should be no longer than 25 characters or so.  The
> + * short name really wants to be no more than 8.  When displaying the
> + * progress bar, the progress code sees if the long names of all
> + * registered progress items can fit.  If not, it will use the short
> + * names, starting at the outermost progress item.  If the short names
> + * don't fit, it will truncate the short names.
> + *
> + * count is how many steps are required for completion of this progress
> + * item.  If count is non-zero, the progress bar will display a
> + * completion percentage.  If count is zero, the item is considered
> + * unbounded, and the progress bar will display a simple spinner.
> + *
> + * A new progress item is returned.  If NULL is returned, its because
> + * there is no memory available.
> + */
> +struct tools_progress *tools_progress_start(const char *long_name,
> +					    const char *short_name,
> +					    uint64_t count);
> +/*
> + * Increment the progress item.
> + *
> + * step is the number of steps to add to the completed count.  This will
> + * update the progress bar.  As an optimization, the bar is changed at
> + * most once every 1/8s.  In addition, it will not be updated if the
> + * completion percentage has not changed.
> + */
> +void tools_progress_step(struct tools_progress *prog, unsigned int step);
> +
> +/*
> + * Stop this progress item.  This will free the item and remove it from the
> + * progress bar.
> + */
> +void tools_progress_stop(struct tools_progress *prog);
> +#endif  /* _INTERNAL_PROGRESS_H */
> diff --git a/libtools-internal/.gitignore b/libtools-internal/.gitignore
> index f1b4775..fb0f781 100644
> --- a/libtools-internal/.gitignore
> +++ b/libtools-internal/.gitignore
> @@ -1,3 +1,4 @@
>  cscope.*
>  libtools-internal.a
>  *.d
> +debug_*
> diff --git a/libtools-internal/Makefile b/libtools-internal/Makefile
> index e1ebf36..7b6bd42 100644
> --- a/libtools-internal/Makefile
> +++ b/libtools-internal/Makefile
> @@ -28,7 +28,8 @@ debug_%: debug_%.o
>  
>  endif
>  
> -CFILES = verbose.c
> +CFILES = verbose.c progress.c
> +HFILES = libtools-internal.h
>  
>  OBJS = $(subst .c,.o,$(CFILES))
>  
> diff --git a/libtools-internal/libtools-internal.h b/libtools-internal/libtools-internal.h
> new file mode 100644
> index 0000000..f77f846
> --- /dev/null
> +++ b/libtools-internal/libtools-internal.h
> @@ -0,0 +1,30 @@
> +/* -*- mode: c; c-basic-offset: 8; -*-
> + * vim: noexpandtab sw=8 ts=8 sts=0:
> + *
> + * libtools-internal.h
> + *
> + * Internal header for libtools-internal.
> + *
> + * Copyright (C) 2008 Oracle.  All rights reserved.
> + *
> + * 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.
> + *
> + * 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.
> + */
> +
> +#ifndef _LIBTOOLS_INTERNAL_H
> +#define _LIBTOOLS_INTERNAL_H
> +
> +int tools_verbosity(void);
> +int tools_is_interactive(void);
> +void tools_progress_clear(void);
> +void tools_progress_restore(void);
> +int tools_progress_enabled(void);
> +
> +#endif  /* _LIBTOOLS_INTERNAL_H */
> diff --git a/libtools-internal/progress.c b/libtools-internal/progress.c
> new file mode 100644
> index 0000000..d294117
> --- /dev/null
> +++ b/libtools-internal/progress.c
> @@ -0,0 +1,648 @@
> +/* -*- mode: c; c-basic-offset: 8; -*-
> + * vim: noexpandtab sw=8 ts=8 sts=0:
> + *
> + * progress.c
> + *
> + * Internal routines progress output.
> + *
> + * Copyright (C) 2008 Oracle.  All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public
> + * License version 2 as published by the Free Software Foundation.
> + *
> + * 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.
> + */
> +
> +#define _LARGEFILE64_SOURCE
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/time.h>
> +#include <assert.h>
> +#include <unistd.h>
> +#include <inttypes.h>
> +
> +#include "ocfs2-kernel/kernel-list.h"
> +#include "tools-internal/progress.h"
> +#include "libtools-internal.h"
> +
> +enum progress_length {
> +	PROGRESS_TRUNC,
> +	PROGRESS_SHORT,
> +	PROGRESS_LONG,
> +};
> +
> +#define TRUNC_LEN 3
> +#define PERCENTAGE_LEN 5
> +#define SPINNER_LEN 2
> +
> +struct tools_progress {
> +	struct list_head p_list;
> +	enum progress_length p_len;
> +	char *p_long_name;
> +	unsigned int p_long_name_len;
> +	char *p_short_name;
> +	unsigned int p_short_name_len;
> +	uint64_t p_current;
> +	uint64_t p_count;
> +	unsigned int p_percent;
> +	int p_spinner_pos;
> +};
> +
> +#define PROGRESS_OPEN "["
> +#define PROGRESS_SEP " > "
> +#define PROGRESS_CLOSE "]"
> +#define PROGRESS_ELIPS "... "
> +
> +static const char spinner[] = "\\|/-";
> +static char nextline;
> +
> +static LIST_HEAD(progresses);
> +
> +/* When did we last update the progress */
> +static unsigned int last_tick;
> +
> +/* Are we displaying progress statistics */
> +static int progress_on = 0;
> +
> +/* A fake progress structure to pass around when we're disabled */
> +static struct tools_progress disabled_prog;
> +
> +/*
> + * A buffer for storing the current progress output.  That way, we can
> + * replay it.
> + *
> + * If the terminal is 80 characters or less, or we can't allocate an
> + * appropriately sized progbuf, we use a static one.  The extra 2 characters
> + * are for nextline and the NUL.
> + */
> +#define DEFAULT_WIDTH 80
> +#define PROGBUF_EXTRA 2
> +static char static_progbuf[DEFAULT_WIDTH + PROGBUF_EXTRA];
> +static char *progbuf = static_progbuf;
> +static unsigned int progbuf_len = DEFAULT_WIDTH + PROGBUF_EXTRA;
> +
> +/*
> + * If we've updated the progress within the last 1/8th of a second, there
> + * is no point in doing it again.  Tick algorithm stolen from e2fsck.
> + */
> +static int check_tick(void)
> +{
> +	unsigned int tick;
> +	struct timeval tv;
> +
> +	gettimeofday(&tv, NULL);
> +	tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8));
> +	if (tick == last_tick)
> +		return 0;
> +
> +	last_tick = tick;
> +
> +	return 1;
> +}
> +
> +static unsigned int calc_percent(uint64_t num, uint64_t dem)
> +{
> +	double percent = ((double)num) / ((double)dem);
> +
> +	return (unsigned int)((100.0 * percent) + 0.5);
> +}
> +
> +/* If the visual percentage hasn't change, there's no point in updating. */
> +static int check_percent(struct tools_progress *prog)
> +{
> +	unsigned int new_percent;
> +
> +	/* An unbounded progress always steps */
> +	if (!prog->p_count)
> +		return 1;
> +
> +	if (prog->p_current >= prog->p_count)
> +		prog->p_current = prog->p_count;
> +
> +	new_percent = calc_percent(prog->p_current, prog->p_count);
> +
> +	if (new_percent == prog->p_percent)
> +		return 0;
> +
> +	prog->p_percent = new_percent;
> +	return 1;
> +}
> +
> +static void step_spinner(struct tools_progress *prog)
> +{
> +	prog->p_spinner_pos = (prog->p_spinner_pos + 1) & 3;
> +}
> +
> +static void progress_length_reset(void)
> +{
> +	struct list_head *p;
> +	struct tools_progress *prog;
> +
> +	list_for_each(p, &progresses) {
> +		prog = list_entry(p, struct tools_progress, p_list);
> +		prog->p_len = PROGRESS_LONG;
> +	}
> +}
> +
> +static size_t length_one_prog(struct tools_progress *prog)
> +{
> +	size_t len = 0;
> +
> +	switch (prog->p_len) {
> +		case PROGRESS_LONG:
> +			len += prog->p_long_name_len;
> +			break;
> +		case PROGRESS_SHORT:
> +			len += prog->p_short_name_len;
> +			break;
> +		case PROGRESS_TRUNC:
> +			len += TRUNC_LEN;
> +			break;
> +		default:
> +			assert(0);
> +			break;
> +	}
> +
> +	if (prog->p_count)
> +		len += PERCENTAGE_LEN;
> +	else
> +		len += SPINNER_LEN;
> +
> +	return len;
> +}
> +
> +static unsigned int progress_length_check(void)
> +{
> +	unsigned int len = 0;
> +	int first = 1;
> +	struct list_head *p;
> +	struct tools_progress *prog;
> +
> +	assert(!list_empty(&progresses));
> +
> +	list_for_each(p, &progresses) {
> +		prog = list_entry(p, struct tools_progress, p_list);
> +
> +		if (first) {
> +			len += strlen(PROGRESS_OPEN);
> +			first = 0;
> +		} else
> +			len += strlen(PROGRESS_SEP);
> +
> +		len += length_one_prog(prog);
> +	}
> +	len += strlen(PROGRESS_CLOSE);
> +
> +	return len;
> +}
> +
> +static int progress_length_shrink(void)
> +{
> +	struct list_head *p;
> +	struct tools_progress *prog = NULL;
> +	enum progress_length len = PROGRESS_LONG;
> +
> +	/*
> +	 * We start from the longest length.  We lower that max length
> +	 * if we see a shorter one.  When we then see the boundary (a
> +	 * longer length after a shorter one), we break out.
> +	 */
> +	list_for_each(p, &progresses) {
> +		prog = list_entry(p, struct tools_progress, p_list);
> +		if (len > prog->p_len)
> +			len = prog->p_len;
> +		else if (len < prog->p_len)
> +			break;
> +		prog = NULL;
> +	}
> +
> +	/*
> +	 * If there was no boundary, all progresses had the same length.
> +	 * shrink the first one.
> +	 */
> +	if (!prog)
> +		prog = list_entry(progresses.next, struct tools_progress,
> +				  p_list);
> +
> +	/*
> +	 * If the one we want to shrink already is at PROGRESS_TRUNC, we
> +	 * can shrink no more.  Return false.
> +	 */
> +	if (prog->p_len == PROGRESS_TRUNC)
> +		return 0;
> +
> +	prog->p_len--;
> +	return 1;
> +}
> +
> +
> +static unsigned int check_display(void)
> +{
> +	char *cols = getenv("COLUMNS");
> +	char *tmpbuf;
> +	unsigned int tmp, columns = DEFAULT_WIDTH;
> +
> +	if (cols) {
> +		tmp = atoi(cols);
> +		if (tmp)
> +			columns = tmp;
> +	}
> +
> +	tmp = columns + PROGBUF_EXTRA;
> +	/* Do we need more space for this width? */
> +	if (tmp > progbuf_len) {
> +		tmpbuf = malloc(sizeof(char) * tmp);
> +		if (tmpbuf) {
> +			progbuf_len = tmp;
> +			memset(tmpbuf, 0, tmp);
> +			if (progbuf != static_progbuf)
> +				free(progbuf);
> +			progbuf = tmpbuf;
> +			/*
> +			 * We just grew the buffer, so try long progress
> +			 * output again.
> +			 */
> +			progress_length_reset();
> +		} else {
> +			/*
> +			 * We couldn't allocate enough space, so report
> +			 * what we can actually use.
> +			 */
> +			columns = progbuf_len - PROGBUF_EXTRA;
> +		}
> +	}
> +
> +	return columns;
> +}
> +
> +static size_t print_one_prog(struct tools_progress *prog, char *buf,
> +			     size_t len)
> +{
> +	int offset = 0;
> +	size_t ret;
> +
> +	switch (prog->p_len) {
> +		case PROGRESS_LONG:
> +			ret = snprintf(buf + offset, len - offset,
> +				       "%s", prog->p_long_name);
> +			break;
> +		case PROGRESS_SHORT:
> +			ret = snprintf(buf + offset, len - offset,
> +				       "%s", prog->p_short_name);
> +			break;
> +		case PROGRESS_TRUNC:
> +			ret = snprintf(buf + offset, len - offset,
> +				       "%.*s", TRUNC_LEN,
> +				       prog->p_short_name);
> +			break;
> +		default:
> +			assert(0);
> +			break;
> +	}
> +	offset += ret;
> +
> +	if (prog->p_count)
> +		ret = snprintf(buf + offset, len - offset,
> +			       " %3u%%", prog->p_percent);
> +	else
> +		ret = snprintf(buf + offset, len - offset, " %c",
> +			       spinner[prog->p_spinner_pos & 3]);
> +	offset += ret;
> +
> +	return offset;
> +}
> +
> +static void print_trailer(char *buf, size_t len)
> +{
> +	size_t ret;
> +	unsigned int offset = 0;
> +
> +	ret = snprintf(buf + offset, len - offset, "%s",
> +		       PROGRESS_CLOSE);
> +	offset += ret;
> +	assert(offset <= len);
> +	ret = snprintf(buf + offset, len - offset, "%c", nextline);
> +	assert(ret < (len - offset));
> +}
> +
> +static void progress_printf(unsigned int columns)
> +{
> +	unsigned int offset = 0;
> +	size_t ret;
> +	int first = 1;
> +	struct list_head *p;
> +	struct tools_progress *prog = NULL;
> +
> +	if (list_empty(&progresses))
> +		return;
> +
> +	list_for_each(p, &progresses) {
> +		prog = list_entry(p, struct tools_progress, p_list);
> +
> +		if (first) {
> +			ret = snprintf(progbuf + offset,
> +				       columns - offset,
> +				       "%s", PROGRESS_OPEN);
> +			first = 0;
> +		} else
> +			ret = snprintf(progbuf + offset,
> +				       columns - offset,
> +				       "%s", PROGRESS_SEP);
> +		offset += ret;
> +
> +		offset += print_one_prog(prog, progbuf + offset,
> +					 columns - offset);
> +		assert(offset < columns);
> +	}
> +
> +	/*
> +	 * From here on out, we use progbuf_len instead of columns.  Our
> +	 * earlier calculations should have gotten this right.
> +	 */
> +	assert(offset < columns);
> +	print_trailer(progbuf + offset, progbuf_len - offset);
> +}
> +
> +static void truncate_printf(unsigned int columns)
> +{
> +	struct tools_progress *last =
> +		list_entry(progresses.prev, struct tools_progress, p_list);
> +	size_t ret, len = length_one_prog(last);
> +	unsigned int offset = 0;
> +
> +	if ((len + strlen(PROGRESS_CLOSE) + strlen(PROGRESS_ELIPS)) <=
> +	    columns) {
> +		ret = snprintf(progbuf + offset, columns - offset, "%s",
> +			       PROGRESS_ELIPS);
> +		offset += ret;
> +		ret = print_one_prog(last, progbuf + offset,
> +				     columns - offset);
> +		offset += ret;
> +		assert(offset < columns);
> +		print_trailer(progbuf + offset, progbuf_len - offset);
> +	} else {
> +		/* Give up, no progress */
> +		progbuf[0] = '\0';
> +	}
> +}
> +
> +static void progress_compute(void)
> +{
> +	unsigned int columns = check_display();
> +	int truncate = 0;
> +
> +	while (progress_length_check() > columns) {
> +		truncate = !progress_length_shrink();
> +		if (truncate)
> +			break;
> +	}
> +
> +	if (truncate)
> +		truncate_printf(columns);
> +	else
> +		progress_printf(columns);
> +}
> +
> +static void progress_clear(void)
> +{
> +	unsigned int columns = check_display();
> +
> +	memset(progbuf, ' ', columns);
> +	snprintf(progbuf + columns, progbuf_len - columns, "%c", nextline);
> +}
> +
> +static void progress_write(void)
> +{
> +	printf("%s", progbuf);
> +}
> +
> +static void tools_progress_free(struct tools_progress *prog)
> +{
> +	if (prog->p_long_name)
> +		free(prog->p_long_name);
> +	if (prog->p_short_name)
> +		free(prog->p_short_name);
> +	free(prog);
> +}
> +
> +static struct tools_progress *tools_progress_alloc(const char *long_name,
> +						   const char *short_name,
> +						   uint64_t count)
> +{
> +	struct tools_progress *prog;
> +
> +	prog = malloc(sizeof(struct tools_progress));
> +	if (!prog)
> +		goto out;
> +
> +	memset(prog, 0, sizeof(struct tools_progress));
> +	prog->p_long_name = strdup(long_name ? long_name : "");
> +	prog->p_short_name = strdup(short_name ? short_name : long_name);
> +	if (!prog->p_long_name || !prog->p_short_name) {
> +		tools_progress_free(prog);
> +		prog = NULL;
> +		goto out;
> +	}
> +
> +	prog->p_long_name_len = strlen(prog->p_long_name);
> +	prog->p_short_name_len = strlen(prog->p_short_name);
> +	prog->p_count = count;
> +
> +out:
> +	return prog;
> +}
> +
> +
> +/*
> + * API for libtools-internal only
> + */
> +
> +void tools_progress_clear(void)
> +{
> +	if (!progress_on)
> +		return;
> +
> +	if (list_empty(&progresses))
> +		return;
> +
> +	/*
> +	 * We only need to wipe the line if are doing terminal-based
> +	 * progress.
> +	 */
> +	if (nextline != '\r')
> +		return;
> +
> +	progress_clear();
> +	progress_write();
> +}
> +
> +void tools_progress_restore(void)
> +{
> +	if (!progress_on)
> +		return;
> +
> +	/* Same here */
> +	if (list_empty(&progresses))
> +		return;
> +	if (nextline != '\r')
> +		return;
> +
> +	progress_compute();
> +	progress_write();
> +}
> +
> +int tools_progress_enabled(void)
> +{
> +	return progress_on;
> +}
> +
> +
> +/*
> + * Public API
> + */
> +
> +void tools_progress_enable(void)
> +{
> +	progress_on = 1;
> +
> +	if (!list_empty(&progresses))
> +		return;
> +
> +	if (isatty(STDOUT_FILENO))
> +		nextline = '\r';
> +	else
> +		nextline = '\n';
> +
> +}
> +
> +void tools_progress_disable(void)
> +{
> +	progress_on = 0;
> +}
> +
> +struct tools_progress *tools_progress_start(const char *long_name,
> +					    const char *short_name,
> +					    uint64_t count)
> +{
> +	struct tools_progress *prog;
> +
> +	if (!progress_on)
> +		return &disabled_prog;
> +
> +	prog = tools_progress_alloc(long_name, short_name, count);
> +	if (!prog)
> +		goto out;
> +
> +	list_add_tail(&prog->p_list, &progresses);
> +	tools_progress_clear();
> +	progress_length_reset();
> +	progress_compute();
> +	progress_write();
> +
> +out:
> +	return prog;
> +}
> +
> +void tools_progress_step(struct tools_progress *prog, unsigned int step)
> +{
> +	if (prog == &disabled_prog)
> +		return;
> +
> +	prog->p_current += step;
> +
> +	if (!check_percent(prog))
> +		return;
> +	if (!check_tick() &&
> +	    (prog->p_percent != 0) && (prog->p_percent != 100))
> +		return;
> +
> +	if (!prog->p_count)
> +		step_spinner(prog);
> +
> +	progress_compute();
> +	progress_write();
> +}
> +
> +void tools_progress_stop(struct tools_progress *prog)
> +{
> +	if (prog == &disabled_prog)
> +		return;
> +
> +	tools_progress_clear();
> +
> +	list_del(&prog->p_list);
> +	tools_progress_free(prog);
> +
> +	if (!list_empty(&progresses)) {
> +		progress_length_reset();
> +		tools_progress_restore();
> +	}
> +}
> +
> +#ifdef DEBUG_EXE
> +#include <time.h>
> +
> +static int run_steps(const char *ln, const char *sn, int count,
> +		     int (*func)(void))
> +{
> +	int i, ret = 0;
> +	struct tools_progress *prog;
> +	struct timespec ts = {
> +		.tv_nsec = 100000000,
> +	};
> +
> +	prog = tools_progress_start(ln, sn, count > 0 ? count : 0);
> +	if (!prog)
> +		return 1;
> +
> +	if (count < 0)
> +		count = -count;
> +	for (i = 0; i < count; i++) {
> +		if (func)
> +			ret = func();
> +		if (ret)
> +			break;
> +		tools_progress_step(prog, 1);
> +		nanosleep(&ts, NULL);
> +	}
> +	tools_progress_stop(prog);
> +
> +	return ret;
> +}
> +
> +static int middle(void)
> +{
> +	static int try = 0;
> +	char lbuf[100], sbuf[100];
> +
> +	try++;
> +	snprintf(lbuf, 100, "This is middle %d", try);
> +	snprintf(sbuf, 100, "middle%d", try);
> +	return run_steps(lbuf, sbuf, -7, NULL);
> +}
> +
> +static int outer(void)
> +{
> +	static int try = 0;
> +	char lbuf[100], sbuf[100];
> +
> +	try++;
> +	snprintf(lbuf, 100, "This is outer %d", try);
> +	snprintf(sbuf, 100, "outer%d", try);
> +	return run_steps(lbuf, sbuf, 10, middle);
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +	setbuf(stdout, NULL);
> +	setbuf(stderr, NULL);
> +	tools_progress_enable();
> +	return run_steps("This is a test", "thisis", 5, outer);
> +}
> +#endif
> diff --git a/libtools-internal/verbose.c b/libtools-internal/verbose.c
> index 0ed7b64..c9eb88d 100644
> --- a/libtools-internal/verbose.c
> +++ b/libtools-internal/verbose.c
> @@ -26,8 +26,11 @@
>  #include <ctype.h>
>  #include <limits.h>
>  #include <et/com_err.h>
> +#include <inttypes.h>
>  
>  #include "tools-internal/verbose.h"
> +#include "tools-internal/progress.h"
> +#include "libtools-internal.h"
>  
>  static char progname[PATH_MAX] = "(Unknown)";
>  static int verbosity = 1;
> @@ -66,10 +69,18 @@ void tools_quiet(void)
>  	verbosity--;
>  }
>  
> +int tools_verbosity(void)
> +{
> +	return verbosity;
> +}
> +
>  static void vfverbosef(FILE *f, int level, const char *fmt, va_list args)
>  {
> -	if (level <= verbosity)
> +	if (level <= verbosity) {
> +		tools_progress_clear();
>  		vfprintf(f, fmt, args);
> +		tools_progress_restore();
> +	}
>  }
>  
>  static void fverbosef(FILE *f, int level, const char *fmt, ...)
> @@ -112,15 +123,23 @@ void tcom_err(errcode_t code, const char *fmt, ...)
>  {
>  	va_list args;
>  
> +	tools_progress_clear();
>  	va_start(args, fmt);
>  	com_err_va(progname, code, fmt, args);
>  	va_end(args);
> +	tools_progress_restore();
>  }
>  
>  static int vtools_interact(enum tools_verbosity_level level,
>  			   const char *fmt, va_list args)
>  {
>  	char *s, buffer[NAME_MAX];
> +	int progress_enabled = tools_progress_enabled();
> +
> +	if (progress_enabled) {
> +		tools_progress_clear();
> +		tools_progress_disable();
> +	}
>  
>  	vfverbosef(stderr, level, fmt, args);
>  
> @@ -131,6 +150,11 @@ static int vtools_interact(enum tools_verbosity_level level,
>  	} else
>  		s = fgets(buffer, sizeof(buffer), stdin);
>  
> +	if (progress_enabled) {
> +		tools_progress_enable();
> +		tools_progress_restore();
> +	}
> +
>  	if (s && *s) {
>  		*s = tolower(*s);
>  		if (*s == 'y')
> @@ -155,6 +179,11 @@ void tools_interactive_no(void)
>  	interactive_answer = 'n';
>  }
>  
> +int tools_is_interactive(void)
> +{
> +	return interactive;
> +}
> +
>  /* Pass this a question without a newline. */
>  int tools_interact(const char *fmt, ...)
>  {
> -- 
> 1.5.6.5
--
Mark Fasheh



More information about the Ocfs2-tools-devel mailing list