summaryrefslogtreecommitdiffstats
path: root/lib/libdpv/dpv.c
diff options
context:
space:
mode:
authordteske <dteske@FreeBSD.org>2014-11-04 23:46:01 +0000
committerdteske <dteske@FreeBSD.org>2014-11-04 23:46:01 +0000
commitbf764fc98274724d74ce3fe2184574f26b6ec47b (patch)
tree98a67555867e474c5f716bf62b6c1120a5cfc536 /lib/libdpv/dpv.c
parent95b02b5b83b0f6812368761db9c260b00d6e98bc (diff)
downloadFreeBSD-src-bf764fc98274724d74ce3fe2184574f26b6ec47b.zip
FreeBSD-src-bf764fc98274724d74ce3fe2184574f26b6ec47b.tar.gz
Add new libraries/utilities for data throughput visualization.
dpv(3): dialog progress view library dpv(1): stream data from stdin or multiple paths with dialog progress view figpar(3): configuration file parsing library Reviews: D714 Reviewed by: jelischer, shurd Discussed at: MeetBSD California 2014 Vendor/Dev Summit Discussed on: -current MFC after: 21 days X-MFC-to: stable/10 stable/9
Diffstat (limited to 'lib/libdpv/dpv.c')
-rw-r--r--lib/libdpv/dpv.c721
1 files changed, 721 insertions, 0 deletions
diff --git a/lib/libdpv/dpv.c b/lib/libdpv/dpv.c
new file mode 100644
index 0000000..0aac082
--- /dev/null
+++ b/lib/libdpv/dpv.c
@@ -0,0 +1,721 @@
+/*-
+ * Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <dialog.h>
+#include <err.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string_m.h>
+#include <unistd.h>
+
+#include "dialog_util.h"
+#include "dialogrc.h"
+#include "dprompt.h"
+#include "dpv.h"
+#include "dpv_private.h"
+#include "status.h"
+#include "util.h"
+
+/* Test Mechanics (Only used when dpv_config.options |= DPV_TEST_MODE) */
+#define INCREMENT 1 /* Increment % per-pass test-mode */
+#define XDIALOG_INCREMENT 15 /* different for slower Xdialog(1) */
+static uint8_t increment = INCREMENT;
+
+/* Debugging */
+uint8_t debug = FALSE;
+
+/* Data to process */
+int dpv_interrupt = FALSE;
+int dpv_abort = FALSE;
+unsigned int dpv_nfiles = 0;
+
+/* Data processing */
+long long dpv_overall_read = 0;
+static char pathbuf[PATH_MAX];
+
+/* Extra display information */
+uint8_t no_labels = FALSE; /* dpv_config.options & DPV_NO_LABELS */
+uint8_t wide = FALSE; /* dpv_config.options & DPV_WIDE_MODE */
+char *aprompt = NULL; /* dpv_config.aprompt */
+char *msg_done = NULL; /* dpv_config.msg_done */
+char *msg_fail = NULL; /* dpv_config.msg_fail */
+char *msg_pending = NULL; /* dpv_config.msg_pending */
+char *pprompt = NULL; /* dpv_config.pprompt */
+
+/* Status-Line format for when using dialog(3) */
+const char *status_format_custom = NULL;
+char status_format_default[DPV_STATUS_FORMAT_MAX];
+
+/*
+ * Takes a pointer to a dpv_config structure containing layout details and
+ * pointer to initial element in a linked-list of dpv_file_node structures,
+ * each presenting a file to process. Executes the `action' function passed-in
+ * as a member to the `config' structure argument.
+ */
+int
+dpv(struct dpv_config *config, struct dpv_file_node *file_list)
+{
+ char c;
+ uint8_t keep_going;
+ uint8_t nls = FALSE; /* See dialog_prompt_nlstate() */
+ uint8_t no_overrun = FALSE;
+ uint8_t pprompt_nls = FALSE; /* See dialog_prompt_nlstate() */
+ uint8_t shrink_label_size = FALSE;
+ mode_t mask;
+ uint16_t options;
+ char *cp;
+ char *fc;
+ char *last;
+ char *name;
+ char *output;
+ const char *status_fmt;
+ const char *path_fmt;
+ enum dpv_display display_type;
+ enum dpv_output output_type;
+ enum dpv_status status;
+ int (*action)(struct dpv_file_node *file, int out);
+ int backslash;
+ int dialog_last_update = 0;
+ int dialog_old_nthfile = 0;
+ int dialog_old_seconds = -1;
+ int dialog_out = STDOUT_FILENO;
+ int dialog_update_usec = 0;
+ int dialog_updates_per_second;
+ int files_left;
+ int max_cols;
+ int nthfile = 0;
+ int output_out;
+ int overall = 0;
+ int pct;
+ int res;
+ int seconds;
+ int status_last_update = 0;
+ int status_old_nthfile = 0;
+ int status_old_seconds = -1;
+ int status_update_usec = 0;
+ int status_updates_per_second;
+ pid_t output_pid;
+ pid_t pid;
+ size_t len;
+ struct dpv_file_node *curfile;
+ struct dpv_file_node *first_file;
+ struct dpv_file_node *list_head;
+ struct timeval now;
+ struct timeval start;
+ char init_prompt[PROMPT_MAX + 1] = "";
+
+ /* Initialize globals to default values */
+ aprompt = NULL;
+ pprompt = NULL;
+ options = 0;
+ action = NULL;
+ backtitle = NULL;
+ debug = FALSE;
+ dialog_test = FALSE;
+ dialog_updates_per_second = DIALOG_UPDATES_PER_SEC;
+ display_limit = DISPLAY_LIMIT_DEFAULT;
+ display_type = DPV_DISPLAY_LIBDIALOG;
+ label_size = LABEL_SIZE_DEFAULT;
+ msg_done = NULL;
+ msg_fail = NULL;
+ msg_pending = NULL;
+ no_labels = FALSE;
+ output = NULL;
+ output_type = DPV_OUTPUT_NONE;
+ pbar_size = PBAR_SIZE_DEFAULT;
+ status_format_custom = NULL;
+ status_updates_per_second = STATUS_UPDATES_PER_SEC;
+ title = NULL;
+ wide = FALSE;
+
+ /* Process config options (overriding defaults) */
+ if (config != NULL) {
+ if (config->aprompt != NULL) {
+ if (aprompt == NULL) {
+ aprompt = malloc(DPV_APROMPT_MAX);
+ if (aprompt == NULL)
+ return (-1);
+ }
+ snprintf(aprompt, DPV_APROMPT_MAX, "%s",
+ config->aprompt);
+ }
+ if (config->pprompt != NULL) {
+ if (pprompt == NULL) {
+ pprompt = malloc(DPV_PPROMPT_MAX + 2);
+ /* +2 is for implicit "\n" appended later */
+ if (pprompt == NULL)
+ return (-1);
+ }
+ snprintf(pprompt, DPV_APROMPT_MAX, "%s",
+ config->pprompt);
+ }
+
+ options = config->options;
+ action = config->action;
+ backtitle = config->backtitle;
+ debug = config->debug;
+ dialog_test = ((options & DPV_TEST_MODE) != 0);
+ dialog_updates_per_second = config->dialog_updates_per_second;
+ display_limit = config->display_limit;
+ display_type = config->display_type;
+ label_size = config->label_size;
+ msg_done = (char *)config->msg_done;
+ msg_fail = (char *)config->msg_fail;
+ msg_pending = (char *)config->msg_pending;
+ no_labels = ((options & DPV_NO_LABELS) != 0);
+ no_overrun = ((options & DPV_NO_OVERRUN) != 0);
+ output = config->output;
+ output_type = config->output_type;
+ pbar_size = config->pbar_size;
+ status_updates_per_second = config->status_updates_per_second;
+ title = config->title;
+ wide = ((options & DPV_WIDE_MODE) != 0);
+
+ /* Enforce some minimums (pedantic) */
+ if (display_limit < -1)
+ display_limit = -1;
+ if (label_size < -1)
+ label_size = -1;
+ if (pbar_size < -1)
+ pbar_size = -1;
+
+ /* For the mini-pbar, -1 means hide, zero is invalid unless
+ * only one file is given */
+ if (pbar_size == 0) {
+ if (file_list == NULL || file_list->next == NULL)
+ pbar_size = -1;
+ else
+ pbar_size = PBAR_SIZE_DEFAULT;
+ }
+
+ /* For the label, -1 means auto-size, zero is invalid unless
+ * specifically requested through the use of options flag */
+ if (label_size == 0 && no_labels == FALSE)
+ label_size = LABEL_SIZE_DEFAULT;
+
+ /* Status update should not be zero */
+ if (status_updates_per_second == 0)
+ status_updates_per_second = STATUS_UPDATES_PER_SEC;
+ } /* config != NULL */
+
+ /* Process the type of display we've been requested to produce */
+ switch (display_type) {
+ case DPV_DISPLAY_STDOUT:
+ debug = TRUE;
+ use_color = FALSE;
+ use_dialog = FALSE;
+ use_libdialog = FALSE;
+ use_xdialog = FALSE;
+ break;
+ case DPV_DISPLAY_DIALOG:
+ use_color = TRUE;
+ use_dialog = TRUE;
+ use_libdialog = FALSE;
+ use_xdialog = FALSE;
+ break;
+ case DPV_DISPLAY_XDIALOG:
+ snprintf(dialog, PATH_MAX, XDIALOG);
+ use_color = FALSE;
+ use_dialog = FALSE;
+ use_libdialog = FALSE;
+ use_xdialog = TRUE;
+ break;
+ default:
+ use_color = TRUE;
+ use_dialog = FALSE;
+ use_libdialog = TRUE;
+ use_xdialog = FALSE;
+ break;
+ } /* display_type */
+
+ /* Enforce additional minimums that require knowing our display type */
+ if (dialog_updates_per_second == 0)
+ dialog_updates_per_second = use_xdialog ?
+ XDIALOG_UPDATES_PER_SEC : DIALOG_UPDATES_PER_SEC;
+
+ /* Allow forceful override of use_color */
+ if (config != NULL && (config->options & DPV_USE_COLOR) != 0)
+ use_color = TRUE;
+
+ /* Count the number of files in provided list of dpv_file_node's */
+ if (use_dialog && pprompt != NULL && *pprompt != '\0')
+ pprompt_nls = dialog_prompt_nlstate(pprompt);
+
+ max_cols = dialog_maxcols();
+ if (label_size == -1)
+ shrink_label_size = TRUE;
+
+ /* Process file arguments */
+ for (curfile = file_list; curfile != NULL; curfile = curfile->next) {
+ dpv_nfiles++;
+
+ /* dialog(3) only expands literal newlines */
+ if (use_libdialog) strexpandnl(curfile->name);
+
+ /* Optionally calculate label size for file */
+ if (shrink_label_size) {
+ nls = FALSE;
+ name = curfile->name;
+ if (curfile == file_list)
+ nls = pprompt_nls;
+ last = (char *)dialog_prompt_lastline(name, nls);
+ if (use_dialog) {
+ c = *last;
+ *last = '\0';
+ nls = dialog_prompt_nlstate(name);
+ *last = c;
+ }
+ len = dialog_prompt_longestline(last, nls);
+ if ((int)len > (label_size - 3)) {
+ if (label_size > 0)
+ label_size += 3;
+ label_size = len;
+ /* Room for ellipsis (unless NULL) */
+ if (label_size > 0)
+ label_size += 3;
+ }
+
+ if (max_cols > 0 && label_size > (max_cols - pbar_size
+ - 9))
+ label_size = max_cols - pbar_size - 9;
+ }
+
+ if (debug)
+ warnx("label=[%s] path=[%s] size=%lli",
+ curfile->name, curfile->path, curfile->length);
+ } /* file_list */
+
+ /* Optionally process the contents of DIALOGRC (~/.dialogrc) */
+ if (use_dialog) {
+ res = parse_dialogrc();
+ if (debug && res == 0) {
+ warnx("Successfully read `%s' config file", DIALOGRC);
+ warnx("use_shadow = %i (Boolean)", use_shadow);
+ warnx("use_colors = %i (Boolean)", use_colors);
+ warnx("gauge_color=[%s] (FBH)", gauge_color);
+ }
+ } else if (use_libdialog) {
+ init_dialog(stdin, stdout);
+ use_shadow = dialog_state.use_shadow;
+ use_colors = dialog_state.use_colors;
+ gauge_color[0] = 48 + dlg_color_table[GAUGE_ATTR].fg;
+ gauge_color[1] = 48 + dlg_color_table[GAUGE_ATTR].bg;
+ gauge_color[2] = dlg_color_table[GAUGE_ATTR].hilite ?
+ 'b' : 'B';
+ gauge_color[3] = '\0';
+ end_dialog();
+ if (debug) {
+ warnx("Finished initializing dialog(3) library");
+ warnx("use_shadow = %i (Boolean)", use_shadow);
+ warnx("use_colors = %i (Boolean)", use_colors);
+ warnx("gauge_color=[%s] (FBH)", gauge_color);
+ }
+ }
+
+ /* Enable mini progress bar automatically for stdin streams if unable
+ * to calculate progress (missing `lines:' syntax). */
+ if (dpv_nfiles <= 1 && file_list != NULL && file_list->length < 0 &&
+ !dialog_test)
+ pbar_size = PBAR_SIZE_DEFAULT;
+
+ /* If $USE_COLOR is set and non-NULL enable color; otherwise disable */
+ if ((cp = getenv(ENV_USE_COLOR)) != 0)
+ use_color = *cp != '\0' ? 1 : 0;
+
+ /* Print error and return `-1' if not given at least one name */
+ if (dpv_nfiles == 0) {
+ warnx("%s: no labels provided", __func__);
+ return (-1);
+ } else if (debug)
+ warnx("%s: %u label%s provided", __func__, dpv_nfiles,
+ dpv_nfiles == 1 ? "" : "s");
+
+ /* If only one file and pbar size is zero, default to `-1' */
+ if (dpv_nfiles <= 1 && pbar_size == 0)
+ pbar_size = -1;
+
+ /* Print some debugging information */
+ if (debug) {
+ warnx("%s: %s(%i) max rows x cols = %i x %i",
+ __func__, use_xdialog ? XDIALOG : DIALOG,
+ use_libdialog ? 3 : 1, dialog_maxrows(),
+ dialog_maxcols());
+ }
+
+ /* Xdialog(1) updates a lot slower than dialog(1) */
+ if (dialog_test && use_xdialog)
+ increment = XDIALOG_INCREMENT;
+
+ /* Always add implicit newline to pprompt (when specified) */
+ if (pprompt != NULL && *pprompt != '\0') {
+ len = strlen(pprompt);
+ /*
+ * NOTE: pprompt = malloc(PPROMPT_MAX + 2)
+ * NOTE: (see getopt(2) section above for pprompt allocation)
+ */
+ pprompt[len++] = '\\';
+ pprompt[len++] = 'n';
+ pprompt[len++] = '\0';
+ }
+
+ /* Xdialog(1) requires newlines (a) escaped and (b) in triplicate */
+ if (use_xdialog && pprompt != NULL) {
+ /* Replace `\n' with `\n\\n\n' in pprompt */
+ len = strlen(pprompt);
+ len += strcount(pprompt, "\\n") * 2;
+ if (len > DPV_PPROMPT_MAX)
+ errx(EXIT_FAILURE, "%s: Oops, pprompt buffer overflow "
+ "(%zu > %i)", __func__, len, DPV_PPROMPT_MAX);
+ if (replaceall(pprompt, "\\n", "\n\\n\n") < 0)
+ err(EXIT_FAILURE, "%s: replaceall()", __func__);
+ }
+ /* libdialog requires literal newlines */
+ else if (use_libdialog && pprompt != NULL)
+ strexpandnl(pprompt);
+
+ /* Xdialog(1) requires newlines (a) escaped and (b) in triplicate */
+ if (use_xdialog && aprompt != NULL) {
+ /* Replace `\n' with `\n\\n\n' in aprompt */
+ len = strlen(aprompt);
+ len += strcount(aprompt, "\\n") * 2;
+ if (len > DPV_APROMPT_MAX)
+ errx(EXIT_FAILURE, "%s: Oops, aprompt buffer overflow "
+ " (%zu > %i)", __func__, len, DPV_APROMPT_MAX);
+ if (replaceall(aprompt, "\\n", "\n\\n\n") < 0)
+ err(EXIT_FAILURE, "%s: replaceall()", __func__);
+ }
+ /* libdialog requires literal newlines */
+ else if (use_libdialog && aprompt != NULL)
+ strexpandnl(aprompt);
+
+ /*
+ * Warn user about an obscure dialog(1) bug (neither Xdialog(1) nor
+ * libdialog are affected) in the `--gauge' widget. If the first non-
+ * whitespace letter of "{new_prompt}" in "XXX\n{new_prompt}\nXXX\n"
+ * is a number, the number can sometimes be mistaken for a percentage
+ * to the overall progressbar. Other nasty side-effects such as the
+ * entire prompt not displaying or displaying improperly are caused by
+ * this bug too.
+ *
+ * NOTE: When we can use color, we have a work-around... prefix the
+ * output with `\Zn' (used to terminate ANSI and reset to normal).
+ */
+ if (use_dialog && !use_color) {
+ backslash = 0;
+
+ /* First, check pprompt (falls through if NULL) */
+ fc = pprompt;
+ while (fc != NULL && *fc != '\0') {
+ if (*fc == '\n') /* leading literal newline OK */
+ break;
+ if (!isspace(*fc) && *fc != '\\' && backslash == 0)
+ break;
+ else if (backslash > 0 && *fc != 'n')
+ break;
+ else if (*fc == '\\') {
+ backslash++;
+ if (backslash > 2)
+ break; /* we're safe */
+ }
+ fc++;
+ }
+ /* First non-whitespace character that dialog(1) will see */
+ if (fc != NULL && *fc >= '0' && *fc <= '9')
+ warnx("%s: WARNING! text argument to `-p' begins with "
+ "a number (not recommended)", __func__);
+ else if (fc > pprompt)
+ warnx("%s: WARNING! text argument to `-p' begins with "
+ "whitespace (not recommended)", __func__);
+
+ /*
+ * If no pprompt or pprompt is all whitespace, check the first
+ * file name provided to make sure it is alright too.
+ */
+ if ((pprompt == NULL || *fc == '\0') && file_list != NULL) {
+ first_file = file_list;
+ fc = first_file->name;
+ while (fc != NULL && *fc != '\0' && isspace(*fc))
+ fc++;
+ /* First non-whitespace char that dialog(1) will see */
+ if (fc != NULL && *fc >= '0' && *fc <= '9')
+ warnx("%s: WARNING! File name `%s' begins "
+ "with a number (use `-p text' for safety)",
+ __func__, first_file->name);
+ }
+ }
+
+ dprompt_init(file_list);
+ /* Reads: label_size pbar_size pprompt aprompt dpv_nfiles */
+ /* Inits: dheight and dwidth */
+
+ if (!debug) {
+ /* Internally create the initial `--gauge' prompt text */
+ dprompt_recreate(file_list, (struct dpv_file_node *)NULL, 0);
+
+ /* Spawn [X]dialog(1) `--gauge', returning pipe descriptor */
+ if (use_libdialog) {
+ status_printf("");
+ dprompt_libprint(pprompt, aprompt, 0);
+ } else {
+ dprompt_sprint(init_prompt, pprompt, aprompt);
+ dialog_out = dialog_spawn_gauge(init_prompt, &pid);
+ dprompt_dprint(dialog_out, pprompt, aprompt, 0);
+ }
+ } /* !debug */
+
+ /* Seed the random(3) generator */
+ if (dialog_test)
+ srandom(0xf1eeface);
+
+ /* Set default/custom status line format */
+ if (dpv_nfiles > 1) {
+ snprintf(status_format_default, DPV_STATUS_FORMAT_MAX, "%s",
+ DPV_STATUS_MANY);
+ status_format_custom = config->status_many;
+ } else {
+ snprintf(status_format_default, DPV_STATUS_FORMAT_MAX, "%s",
+ DPV_STATUS_SOLO);
+ status_format_custom = config->status_solo;
+ }
+
+ /* Add test mode identifier to default status line if enabled */
+ if (dialog_test && (strlen(status_format_default) + 12) <
+ DPV_STATUS_FORMAT_MAX)
+ strcat(status_format_default, " [TEST MODE]");
+
+ /* Verify custom status format */
+ status_fmt = fmtcheck(status_format_custom, status_format_default);
+ if (status_format_custom != NULL &&
+ status_fmt == status_format_default) {
+ warnx("WARNING! Invalid status_format configuration `%s'",
+ status_format_custom);
+ warnx("Default status_format `%s'", status_format_default);
+ }
+
+ /* Record when we started (used to prevent updating too quickly) */
+ (void)gettimeofday(&start, (struct timezone *)NULL);
+
+ /* Calculate number of microseconds in-between sub-second updates */
+ if (status_updates_per_second != 0)
+ status_update_usec = 1000000 / status_updates_per_second;
+ if (dialog_updates_per_second != 0)
+ dialog_update_usec = 1000000 / dialog_updates_per_second;
+
+ /*
+ * Process the file list [serially] (one for each argument passed)
+ */
+ files_left = dpv_nfiles;
+ list_head = file_list;
+ for (curfile = file_list; curfile != NULL; curfile = curfile->next) {
+ keep_going = TRUE;
+ output_out = -1;
+ pct = 0;
+ nthfile++;
+ files_left--;
+
+ if (dpv_interrupt)
+ break;
+ if (dialog_test)
+ pct = 0 - increment;
+
+ /* Attempt to spawn output program for this file */
+ if (!dialog_test && output != NULL) {
+ mask = umask(0022);
+ (void)umask(mask);
+
+ switch (output_type) {
+ case DPV_OUTPUT_SHELL:
+ output_out = shell_spawn_pipecmd(output,
+ curfile->name, &output_pid);
+ break;
+ case DPV_OUTPUT_FILE:
+ path_fmt = fmtcheck(output, "%s");
+ if (path_fmt == output)
+ len = snprintf(pathbuf,
+ PATH_MAX, output, curfile->name);
+ else
+ len = snprintf(pathbuf,
+ PATH_MAX, "%s", output);
+ if (len >= PATH_MAX) {
+ warnx("%s:%d:%s: pathbuf[%u] too small"
+ "to hold output argument",
+ __FILE__, __LINE__, __func__,
+ PATH_MAX);
+ return (-1);
+ }
+ if ((output_out = open(pathbuf,
+ O_CREAT|O_WRONLY, DEFFILEMODE & ~mask))
+ < 0) {
+ warn("%s", pathbuf);
+ return (-1);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ while (!dpv_interrupt && keep_going) {
+ if (dialog_test) {
+ usleep(50000);
+ pct += increment;
+ dpv_overall_read +=
+ (int)(random() / 512 / dpv_nfiles);
+ /* 512 limits fake readout to Megabytes */
+ } else if (action != NULL)
+ pct = action(curfile, output_out);
+
+ if (no_overrun || dialog_test)
+ keep_going = (pct < 100);
+ else {
+ status = curfile->status;
+ keep_going = (status == DPV_STATUS_RUNNING);
+ }
+
+ /* Get current time and calculate seconds elapsed */
+ gettimeofday(&now, (struct timezone *)NULL);
+ now.tv_sec = now.tv_sec - start.tv_sec;
+ now.tv_usec = now.tv_usec - start.tv_usec;
+ if (now.tv_usec < 0)
+ now.tv_sec--, now.tv_usec += 1000000;
+ seconds = now.tv_sec + (now.tv_usec / 1000000.0);
+
+ /* Update dialog (be it dialog(3), dialog(1), etc.) */
+ if ((dialog_updates_per_second != 0 &&
+ (
+ seconds != dialog_old_seconds ||
+ now.tv_usec - dialog_last_update >=
+ dialog_update_usec ||
+ nthfile != dialog_old_nthfile
+ )) || pct == 100
+ ) {
+ /* Calculate overall progress (rounding up) */
+ overall = (100 * nthfile - 100 + pct) /
+ dpv_nfiles;
+ if (((100 * nthfile - 100 + pct) * 10 /
+ dpv_nfiles % 100) > 50)
+ overall++;
+
+ dprompt_recreate(list_head, curfile, pct);
+
+ if (use_libdialog && !debug) {
+ /* Update dialog(3) widget */
+ dprompt_libprint(pprompt, aprompt,
+ overall);
+ } else {
+ /* stdout, dialog(1), or Xdialog(1) */
+ dprompt_dprint(dialog_out, pprompt,
+ aprompt, overall);
+ fsync(dialog_out);
+ }
+ dialog_old_seconds = seconds;
+ dialog_old_nthfile = nthfile;
+ dialog_last_update = now.tv_usec;
+ }
+
+ /* Update the status line */
+ if ((use_libdialog && !debug) &&
+ status_updates_per_second != 0 &&
+ (
+ keep_going != TRUE ||
+ seconds != status_old_seconds ||
+ now.tv_usec - status_last_update >=
+ status_update_usec ||
+ nthfile != status_old_nthfile
+ )
+ ) {
+ status_printf(status_fmt, dpv_overall_read,
+ (dpv_overall_read / (seconds == 0 ? 1 :
+ seconds) * 1.0),
+ 1, /* XXX until we add parallelism XXX */
+ files_left);
+ status_old_seconds = seconds;
+ status_old_nthfile = nthfile;
+ status_last_update = now.tv_usec;
+ }
+ }
+
+ if (!dialog_test && output_out >= 0) {
+ close(output_out);
+ waitpid(output_pid, (int *)NULL, 0);
+ }
+
+ if (dpv_abort)
+ break;
+
+ /* Advance head of list when we hit the max display lines */
+ if (display_limit > 0 && nthfile % display_limit == 0)
+ list_head = curfile->next;
+ }
+
+ if (!debug) {
+ if (use_libdialog)
+ end_dialog();
+ else {
+ close(dialog_out);
+ waitpid(pid, (int *)NULL, 0);
+ }
+ if (!dpv_interrupt)
+ printf("\n");
+ } else
+ warnx("%s: %lli lines read", __func__, dpv_overall_read);
+
+ if (dpv_interrupt || dpv_abort)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Free allocated items initialized by dpv()
+ */
+void
+dpv_free(void)
+{
+ dialogrc_free();
+ dprompt_free();
+ dialog_maxsize_free();
+ if (aprompt != NULL) {
+ free(aprompt);
+ aprompt = NULL;
+ }
+ if (pprompt != NULL) {
+ free(pprompt);
+ pprompt = NULL;
+ }
+ status_free();
+}
OpenPOWER on IntegriCloud