diff options
author | dteske <dteske@FreeBSD.org> | 2014-11-04 23:46:01 +0000 |
---|---|---|
committer | dteske <dteske@FreeBSD.org> | 2014-11-04 23:46:01 +0000 |
commit | bf764fc98274724d74ce3fe2184574f26b6ec47b (patch) | |
tree | 98a67555867e474c5f716bf62b6c1120a5cfc536 /lib/libdpv/dpv.c | |
parent | 95b02b5b83b0f6812368761db9c260b00d6e98bc (diff) | |
download | FreeBSD-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.c | 721 |
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(); +} |