[Ocfs2-tools-devel] [PATCH 03/18] libtools-internal: Create an API for displaying a progress bar.
Joel Becker
joel.becker at oracle.com
Mon Jan 5 18:33:39 PST 2009
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
More information about the Ocfs2-tools-devel
mailing list