[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