Changeset View
Standalone View
lib/libdpv/dpv.c
- This file was added.
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/*- | |||||
* 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 <dialog.h> | |||||
#include <ctype.h> | |||||
#include <err.h> | |||||
#include <limits.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include <string_m.h> | |||||
#include <sys/stat.h> | |||||
#include <sys/time.h> | |||||
#include <sys/types.h> | |||||
#include <sys/wait.h> | |||||
#include <unistd.h> | |||||
ngie: style(9): the headers need to be sorted | |||||
dteskeAuthorUnsubmitted Not Done Inline ActionsAddressed in Diff #1397 dteske: Addressed in Diff #1397 | |||||
#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; | |||||
ngieUnsubmitted Not Done Inline ActionsIf FALSE is 0, then this will be taken care of when the program is loaded as all BSS values are initialized to 0 (which includes NULL). ngie: If FALSE is 0, then this will be taken care of when the program is loaded as all BSS values are… | |||||
dteskeAuthorUnsubmitted Not Done Inline ActionsDiscussed with julian@ and he recommended keeping the explicit initial assignments. dteske: Discussed with julian@ and he recommended keeping the explicit initial assignments. | |||||
unsigned int dpv_nfiles = 0; | |||||
/* Data processing */ | |||||
long long 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 *pprompt = NULL; /* dpv_config.pprompt */ | |||||
char *msg_done = NULL; | |||||
char *msg_fail = NULL; | |||||
char *msg_pending = NULL; | |||||
/* Status-Line format for when using dialog(3) */ | |||||
char status_format_default[DPV_STATUS_FORMAT_MAX]; | |||||
const char *status_format_custom = NULL; | |||||
/* | |||||
* 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) | |||||
{ | |||||
uint8_t no_overrun = FALSE; | |||||
uint8_t pprompt_nls = 0; /* See dialog_prompt_nlstate() */ | |||||
uint8_t shrink_label_size = FALSE; | |||||
uint16_t options; | |||||
enum dpv_display display_type; | |||||
enum dpv_output output_type; | |||||
int (*action)(struct dpv_file_node *file, int out); | |||||
int dialog_out = STDOUT_FILENO; | |||||
int dialog_updates_per_second, dialog_update_usec = 0; | |||||
int files_left; | |||||
int max_cols; | |||||
int nthfile = 0, overall = 0; | |||||
int seconds, status_old_seconds = -1, dialog_old_seconds = -1; | |||||
int status_last_update = 0, dialog_last_update = 0; | |||||
int status_old_nthfile = 0, dialog_old_nthfile = 0; | |||||
int status_updates_per_second, status_update_usec = 0; | |||||
char *cp, *output; | |||||
const char *status_fmt, *path_fmt; | |||||
pid_t pid; | |||||
size_t len; | |||||
struct dpv_file_node *list_head, *curfile; | |||||
struct timeval start, now; | |||||
ngieUnsubmitted Not Done Inline Actionsstyle(9): these variables should be stack-aligned, pointers first, scalars last, largest to smallest. ngie: style(9): these variables should be stack-aligned, pointers first, scalars last, largest to… | |||||
dteskeAuthorUnsubmitted Not Done Inline ActionsAddressed in Diff #1397 dteske: Addressed in Diff #1397 | |||||
/* 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) | |||||
{ | |||||
ngieUnsubmitted Not Done Inline ActionsThe braces for multi-line conditional and loop blocks should be done like: if (config != NULL) { ngie: The braces for multi-line conditional and loop blocks should be done like:
if (config !=… | |||||
dteskeAuthorUnsubmitted Not Done Inline ActionsAddressed in Diff #1397 dteske: Addressed in Diff #1397 | |||||
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; | |||||
ngieUnsubmitted Not Done Inline Actionsstyle(9):
i.e. if (pprompt == NULL) return (-1); ngie: style(9):
1. single-line statements need to be on separate lines.
2. Return values need to be… | |||||
dteskeAuthorUnsubmitted Not Done Inline ActionsAddressed in Diff #1397 dteske: Addressed in Diff #1397 | |||||
} | |||||
snprintf(pprompt, DPV_APROMPT_MAX, "%s", | |||||
config->pprompt); | |||||
} | |||||
options = config->options; | |||||
action = config->action; | |||||
backtitle = config->backtitle; | |||||
debug = config->debug; | |||||
ngieUnsubmitted Not Done Inline ActionsSpurious whitespace between the "=". ngie: Spurious whitespace between the "=". | |||||
dteskeAuthorUnsubmitted Not Done Inline ActionsNot spurious whitespace; aligning rvalues. dteske: Not spurious whitespace; aligning rvalues. | |||||
julianUnsubmitted Not Done Inline ActionsI'll just reply to a single style comment to say that really this is close, but that yes, I noticed the things being pointed out.. I assume the indents are tabs and that phabricator is just showing them as about 2 spaces. julian: I'll just reply to a single style comment to say that really this is close, but that yes, I… | |||||
dteskeAuthorUnsubmitted Not Done Inline ActionsAddressed in Diff #1397 dteske: Addressed in Diff #1397 | |||||
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; | |||||
ngieUnsubmitted Not Done Inline Actionsstyle(9): please indent the code one more block here ngie: style(9): please indent the code one more block here | |||||
dteskeAuthorUnsubmitted Not Done Inline ActionsYou cite style(9) here, but style(9) actually says: switch (ch) { /* Indent the switch. */ aflag = 1; /* Indent case body one tab. */ I observe proper switch indentation according to style(9). dteske: You cite style(9) here, but style(9) actually says:
switch (ch) { /* Indent the… | |||||
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) | |||||
{ | |||||
int nls = 0; /* See dialog_prompt_nlstate() */ | |||||
char *last, *name = curfile->name; | |||||
if (curfile == file_list) nls = pprompt_nls; | |||||
last = (char *)dialog_prompt_lastline(name, nls); | |||||
if (use_dialog) { | |||||
char 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) { | |||||
int res; | |||||
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 ); | |||||
ngieUnsubmitted Not Done Inline ActionsNot sure if this needs space or "(" / ")" around the assignment ternary per style(9)... ngie: Not sure if this needs space or "(" / ")" around the assignment ternary per style(9)... | |||||
dteskeAuthorUnsubmitted Not Done Inline ActionsAddressed in Diff #1397 dteske: Addressed in Diff #1397 | |||||
/* 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) | |||||
{ | |||||
int backslash = 0; | |||||
char *fc; | |||||
/* 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) | |||||
{ | |||||
struct dpv_file_node *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) | |||||
{ | |||||
char init_prompt[PROMPT_MAX + 1] = ""; | |||||
/* 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 */ | |||||
dprompt_sprint(init_prompt, pprompt, aprompt); | |||||
if (use_libdialog) { | |||||
status_printf(""); | |||||
dprompt_libprint(pprompt, aprompt, 0); | |||||
} else { | |||||
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) */ | |||||
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) | |||||
{ | |||||
uint8_t keep_going = TRUE; | |||||
int output_out = -1; | |||||
int pct = 0; | |||||
pid_t output_pid; | |||||
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) | |||||
{ | |||||
mode_t 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) | |||||
{ | |||||
enum dpv_status status; | |||||
if (dialog_test) { | |||||
usleep(50000); | |||||
pct += increment; | |||||
overall_read += | |||||
(int)(random() / 512 / dpv_nfiles); | |||||
ngieUnsubmitted Not Done Inline ActionsWhy 512? ngie: Why 512? | |||||
dteskeAuthorUnsubmitted Not Done Inline ActionsTo keep the randomized fake readout in the megabytes range. Comment added in Diff #1397 dteske: To keep the randomized fake readout in the megabytes range. Comment added in Diff #1397 | |||||
} 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); | |||||
ngieUnsubmitted Not Done Inline ActionsShould the return code be checked? ngie: Should the return code be checked? | |||||
dteskeAuthorUnsubmitted Not Done Inline ActionsLooked at the man-page. Do you recommend an err(3) call if it returns bad value? or what? dteske: Looked at the man-page. Do you recommend an err(3) call if it returns bad value? or what? | |||||
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, overall_read, | |||||
(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__, overall_read); | |||||
if (dpv_interrupt || dpv_abort) | |||||
return -1; | |||||
else | |||||
return 0; | |||||
} | |||||
/* | |||||
* Free allocated items initialized by dpv() | |||||
*/ | |||||
void | |||||
dpv_free(void) | |||||
{ | |||||
if (aprompt != NULL) free(aprompt); | |||||
if (pprompt != NULL) free(pprompt); | |||||
} |
style(9): the headers need to be sorted