dpv.c revision 274116
1274116Sdteske/*- 2274116Sdteske * Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org> 3274116Sdteske * All rights reserved. 4274116Sdteske * 5274116Sdteske * Redistribution and use in source and binary forms, with or without 6274116Sdteske * modification, are permitted provided that the following conditions 7274116Sdteske * are met: 8274116Sdteske * 1. Redistributions of source code must retain the above copyright 9274116Sdteske * notice, this list of conditions and the following disclaimer. 10274116Sdteske * 2. Redistributions in binary form must reproduce the above copyright 11274116Sdteske * notice, this list of conditions and the following disclaimer in the 12274116Sdteske * documentation and/or other materials provided with the distribution. 13274116Sdteske * 14274116Sdteske * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15274116Sdteske * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16274116Sdteske * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17274116Sdteske * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18274116Sdteske * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19274116Sdteske * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20274116Sdteske * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21274116Sdteske * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22274116Sdteske * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23274116Sdteske * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24274116Sdteske * SUCH DAMAGE. 25274116Sdteske */ 26274116Sdteske 27274116Sdteske#include <sys/cdefs.h> 28274116Sdteske__FBSDID("$FreeBSD: head/lib/libdpv/dpv.c 274116 2014-11-04 23:46:01Z dteske $"); 29274116Sdteske 30274116Sdteske#include <sys/stat.h> 31274116Sdteske#include <sys/time.h> 32274116Sdteske#include <sys/types.h> 33274116Sdteske#include <sys/wait.h> 34274116Sdteske 35274116Sdteske#include <ctype.h> 36274116Sdteske#include <dialog.h> 37274116Sdteske#include <err.h> 38274116Sdteske#include <limits.h> 39274116Sdteske#include <stdio.h> 40274116Sdteske#include <stdlib.h> 41274116Sdteske#include <string.h> 42274116Sdteske#include <string_m.h> 43274116Sdteske#include <unistd.h> 44274116Sdteske 45274116Sdteske#include "dialog_util.h" 46274116Sdteske#include "dialogrc.h" 47274116Sdteske#include "dprompt.h" 48274116Sdteske#include "dpv.h" 49274116Sdteske#include "dpv_private.h" 50274116Sdteske#include "status.h" 51274116Sdteske#include "util.h" 52274116Sdteske 53274116Sdteske/* Test Mechanics (Only used when dpv_config.options |= DPV_TEST_MODE) */ 54274116Sdteske#define INCREMENT 1 /* Increment % per-pass test-mode */ 55274116Sdteske#define XDIALOG_INCREMENT 15 /* different for slower Xdialog(1) */ 56274116Sdteskestatic uint8_t increment = INCREMENT; 57274116Sdteske 58274116Sdteske/* Debugging */ 59274116Sdteskeuint8_t debug = FALSE; 60274116Sdteske 61274116Sdteske/* Data to process */ 62274116Sdteskeint dpv_interrupt = FALSE; 63274116Sdteskeint dpv_abort = FALSE; 64274116Sdteskeunsigned int dpv_nfiles = 0; 65274116Sdteske 66274116Sdteske/* Data processing */ 67274116Sdteskelong long dpv_overall_read = 0; 68274116Sdteskestatic char pathbuf[PATH_MAX]; 69274116Sdteske 70274116Sdteske/* Extra display information */ 71274116Sdteskeuint8_t no_labels = FALSE; /* dpv_config.options & DPV_NO_LABELS */ 72274116Sdteskeuint8_t wide = FALSE; /* dpv_config.options & DPV_WIDE_MODE */ 73274116Sdteskechar *aprompt = NULL; /* dpv_config.aprompt */ 74274116Sdteskechar *msg_done = NULL; /* dpv_config.msg_done */ 75274116Sdteskechar *msg_fail = NULL; /* dpv_config.msg_fail */ 76274116Sdteskechar *msg_pending = NULL; /* dpv_config.msg_pending */ 77274116Sdteskechar *pprompt = NULL; /* dpv_config.pprompt */ 78274116Sdteske 79274116Sdteske/* Status-Line format for when using dialog(3) */ 80274116Sdteskeconst char *status_format_custom = NULL; 81274116Sdteskechar status_format_default[DPV_STATUS_FORMAT_MAX]; 82274116Sdteske 83274116Sdteske/* 84274116Sdteske * Takes a pointer to a dpv_config structure containing layout details and 85274116Sdteske * pointer to initial element in a linked-list of dpv_file_node structures, 86274116Sdteske * each presenting a file to process. Executes the `action' function passed-in 87274116Sdteske * as a member to the `config' structure argument. 88274116Sdteske */ 89274116Sdteskeint 90274116Sdteskedpv(struct dpv_config *config, struct dpv_file_node *file_list) 91274116Sdteske{ 92274116Sdteske char c; 93274116Sdteske uint8_t keep_going; 94274116Sdteske uint8_t nls = FALSE; /* See dialog_prompt_nlstate() */ 95274116Sdteske uint8_t no_overrun = FALSE; 96274116Sdteske uint8_t pprompt_nls = FALSE; /* See dialog_prompt_nlstate() */ 97274116Sdteske uint8_t shrink_label_size = FALSE; 98274116Sdteske mode_t mask; 99274116Sdteske uint16_t options; 100274116Sdteske char *cp; 101274116Sdteske char *fc; 102274116Sdteske char *last; 103274116Sdteske char *name; 104274116Sdteske char *output; 105274116Sdteske const char *status_fmt; 106274116Sdteske const char *path_fmt; 107274116Sdteske enum dpv_display display_type; 108274116Sdteske enum dpv_output output_type; 109274116Sdteske enum dpv_status status; 110274116Sdteske int (*action)(struct dpv_file_node *file, int out); 111274116Sdteske int backslash; 112274116Sdteske int dialog_last_update = 0; 113274116Sdteske int dialog_old_nthfile = 0; 114274116Sdteske int dialog_old_seconds = -1; 115274116Sdteske int dialog_out = STDOUT_FILENO; 116274116Sdteske int dialog_update_usec = 0; 117274116Sdteske int dialog_updates_per_second; 118274116Sdteske int files_left; 119274116Sdteske int max_cols; 120274116Sdteske int nthfile = 0; 121274116Sdteske int output_out; 122274116Sdteske int overall = 0; 123274116Sdteske int pct; 124274116Sdteske int res; 125274116Sdteske int seconds; 126274116Sdteske int status_last_update = 0; 127274116Sdteske int status_old_nthfile = 0; 128274116Sdteske int status_old_seconds = -1; 129274116Sdteske int status_update_usec = 0; 130274116Sdteske int status_updates_per_second; 131274116Sdteske pid_t output_pid; 132274116Sdteske pid_t pid; 133274116Sdteske size_t len; 134274116Sdteske struct dpv_file_node *curfile; 135274116Sdteske struct dpv_file_node *first_file; 136274116Sdteske struct dpv_file_node *list_head; 137274116Sdteske struct timeval now; 138274116Sdteske struct timeval start; 139274116Sdteske char init_prompt[PROMPT_MAX + 1] = ""; 140274116Sdteske 141274116Sdteske /* Initialize globals to default values */ 142274116Sdteske aprompt = NULL; 143274116Sdteske pprompt = NULL; 144274116Sdteske options = 0; 145274116Sdteske action = NULL; 146274116Sdteske backtitle = NULL; 147274116Sdteske debug = FALSE; 148274116Sdteske dialog_test = FALSE; 149274116Sdteske dialog_updates_per_second = DIALOG_UPDATES_PER_SEC; 150274116Sdteske display_limit = DISPLAY_LIMIT_DEFAULT; 151274116Sdteske display_type = DPV_DISPLAY_LIBDIALOG; 152274116Sdteske label_size = LABEL_SIZE_DEFAULT; 153274116Sdteske msg_done = NULL; 154274116Sdteske msg_fail = NULL; 155274116Sdteske msg_pending = NULL; 156274116Sdteske no_labels = FALSE; 157274116Sdteske output = NULL; 158274116Sdteske output_type = DPV_OUTPUT_NONE; 159274116Sdteske pbar_size = PBAR_SIZE_DEFAULT; 160274116Sdteske status_format_custom = NULL; 161274116Sdteske status_updates_per_second = STATUS_UPDATES_PER_SEC; 162274116Sdteske title = NULL; 163274116Sdteske wide = FALSE; 164274116Sdteske 165274116Sdteske /* Process config options (overriding defaults) */ 166274116Sdteske if (config != NULL) { 167274116Sdteske if (config->aprompt != NULL) { 168274116Sdteske if (aprompt == NULL) { 169274116Sdteske aprompt = malloc(DPV_APROMPT_MAX); 170274116Sdteske if (aprompt == NULL) 171274116Sdteske return (-1); 172274116Sdteske } 173274116Sdteske snprintf(aprompt, DPV_APROMPT_MAX, "%s", 174274116Sdteske config->aprompt); 175274116Sdteske } 176274116Sdteske if (config->pprompt != NULL) { 177274116Sdteske if (pprompt == NULL) { 178274116Sdteske pprompt = malloc(DPV_PPROMPT_MAX + 2); 179274116Sdteske /* +2 is for implicit "\n" appended later */ 180274116Sdteske if (pprompt == NULL) 181274116Sdteske return (-1); 182274116Sdteske } 183274116Sdteske snprintf(pprompt, DPV_APROMPT_MAX, "%s", 184274116Sdteske config->pprompt); 185274116Sdteske } 186274116Sdteske 187274116Sdteske options = config->options; 188274116Sdteske action = config->action; 189274116Sdteske backtitle = config->backtitle; 190274116Sdteske debug = config->debug; 191274116Sdteske dialog_test = ((options & DPV_TEST_MODE) != 0); 192274116Sdteske dialog_updates_per_second = config->dialog_updates_per_second; 193274116Sdteske display_limit = config->display_limit; 194274116Sdteske display_type = config->display_type; 195274116Sdteske label_size = config->label_size; 196274116Sdteske msg_done = (char *)config->msg_done; 197274116Sdteske msg_fail = (char *)config->msg_fail; 198274116Sdteske msg_pending = (char *)config->msg_pending; 199274116Sdteske no_labels = ((options & DPV_NO_LABELS) != 0); 200274116Sdteske no_overrun = ((options & DPV_NO_OVERRUN) != 0); 201274116Sdteske output = config->output; 202274116Sdteske output_type = config->output_type; 203274116Sdteske pbar_size = config->pbar_size; 204274116Sdteske status_updates_per_second = config->status_updates_per_second; 205274116Sdteske title = config->title; 206274116Sdteske wide = ((options & DPV_WIDE_MODE) != 0); 207274116Sdteske 208274116Sdteske /* Enforce some minimums (pedantic) */ 209274116Sdteske if (display_limit < -1) 210274116Sdteske display_limit = -1; 211274116Sdteske if (label_size < -1) 212274116Sdteske label_size = -1; 213274116Sdteske if (pbar_size < -1) 214274116Sdteske pbar_size = -1; 215274116Sdteske 216274116Sdteske /* For the mini-pbar, -1 means hide, zero is invalid unless 217274116Sdteske * only one file is given */ 218274116Sdteske if (pbar_size == 0) { 219274116Sdteske if (file_list == NULL || file_list->next == NULL) 220274116Sdteske pbar_size = -1; 221274116Sdteske else 222274116Sdteske pbar_size = PBAR_SIZE_DEFAULT; 223274116Sdteske } 224274116Sdteske 225274116Sdteske /* For the label, -1 means auto-size, zero is invalid unless 226274116Sdteske * specifically requested through the use of options flag */ 227274116Sdteske if (label_size == 0 && no_labels == FALSE) 228274116Sdteske label_size = LABEL_SIZE_DEFAULT; 229274116Sdteske 230274116Sdteske /* Status update should not be zero */ 231274116Sdteske if (status_updates_per_second == 0) 232274116Sdteske status_updates_per_second = STATUS_UPDATES_PER_SEC; 233274116Sdteske } /* config != NULL */ 234274116Sdteske 235274116Sdteske /* Process the type of display we've been requested to produce */ 236274116Sdteske switch (display_type) { 237274116Sdteske case DPV_DISPLAY_STDOUT: 238274116Sdteske debug = TRUE; 239274116Sdteske use_color = FALSE; 240274116Sdteske use_dialog = FALSE; 241274116Sdteske use_libdialog = FALSE; 242274116Sdteske use_xdialog = FALSE; 243274116Sdteske break; 244274116Sdteske case DPV_DISPLAY_DIALOG: 245274116Sdteske use_color = TRUE; 246274116Sdteske use_dialog = TRUE; 247274116Sdteske use_libdialog = FALSE; 248274116Sdteske use_xdialog = FALSE; 249274116Sdteske break; 250274116Sdteske case DPV_DISPLAY_XDIALOG: 251274116Sdteske snprintf(dialog, PATH_MAX, XDIALOG); 252274116Sdteske use_color = FALSE; 253274116Sdteske use_dialog = FALSE; 254274116Sdteske use_libdialog = FALSE; 255274116Sdteske use_xdialog = TRUE; 256274116Sdteske break; 257274116Sdteske default: 258274116Sdteske use_color = TRUE; 259274116Sdteske use_dialog = FALSE; 260274116Sdteske use_libdialog = TRUE; 261274116Sdteske use_xdialog = FALSE; 262274116Sdteske break; 263274116Sdteske } /* display_type */ 264274116Sdteske 265274116Sdteske /* Enforce additional minimums that require knowing our display type */ 266274116Sdteske if (dialog_updates_per_second == 0) 267274116Sdteske dialog_updates_per_second = use_xdialog ? 268274116Sdteske XDIALOG_UPDATES_PER_SEC : DIALOG_UPDATES_PER_SEC; 269274116Sdteske 270274116Sdteske /* Allow forceful override of use_color */ 271274116Sdteske if (config != NULL && (config->options & DPV_USE_COLOR) != 0) 272274116Sdteske use_color = TRUE; 273274116Sdteske 274274116Sdteske /* Count the number of files in provided list of dpv_file_node's */ 275274116Sdteske if (use_dialog && pprompt != NULL && *pprompt != '\0') 276274116Sdteske pprompt_nls = dialog_prompt_nlstate(pprompt); 277274116Sdteske 278274116Sdteske max_cols = dialog_maxcols(); 279274116Sdteske if (label_size == -1) 280274116Sdteske shrink_label_size = TRUE; 281274116Sdteske 282274116Sdteske /* Process file arguments */ 283274116Sdteske for (curfile = file_list; curfile != NULL; curfile = curfile->next) { 284274116Sdteske dpv_nfiles++; 285274116Sdteske 286274116Sdteske /* dialog(3) only expands literal newlines */ 287274116Sdteske if (use_libdialog) strexpandnl(curfile->name); 288274116Sdteske 289274116Sdteske /* Optionally calculate label size for file */ 290274116Sdteske if (shrink_label_size) { 291274116Sdteske nls = FALSE; 292274116Sdteske name = curfile->name; 293274116Sdteske if (curfile == file_list) 294274116Sdteske nls = pprompt_nls; 295274116Sdteske last = (char *)dialog_prompt_lastline(name, nls); 296274116Sdteske if (use_dialog) { 297274116Sdteske c = *last; 298274116Sdteske *last = '\0'; 299274116Sdteske nls = dialog_prompt_nlstate(name); 300274116Sdteske *last = c; 301274116Sdteske } 302274116Sdteske len = dialog_prompt_longestline(last, nls); 303274116Sdteske if ((int)len > (label_size - 3)) { 304274116Sdteske if (label_size > 0) 305274116Sdteske label_size += 3; 306274116Sdteske label_size = len; 307274116Sdteske /* Room for ellipsis (unless NULL) */ 308274116Sdteske if (label_size > 0) 309274116Sdteske label_size += 3; 310274116Sdteske } 311274116Sdteske 312274116Sdteske if (max_cols > 0 && label_size > (max_cols - pbar_size 313274116Sdteske - 9)) 314274116Sdteske label_size = max_cols - pbar_size - 9; 315274116Sdteske } 316274116Sdteske 317274116Sdteske if (debug) 318274116Sdteske warnx("label=[%s] path=[%s] size=%lli", 319274116Sdteske curfile->name, curfile->path, curfile->length); 320274116Sdteske } /* file_list */ 321274116Sdteske 322274116Sdteske /* Optionally process the contents of DIALOGRC (~/.dialogrc) */ 323274116Sdteske if (use_dialog) { 324274116Sdteske res = parse_dialogrc(); 325274116Sdteske if (debug && res == 0) { 326274116Sdteske warnx("Successfully read `%s' config file", DIALOGRC); 327274116Sdteske warnx("use_shadow = %i (Boolean)", use_shadow); 328274116Sdteske warnx("use_colors = %i (Boolean)", use_colors); 329274116Sdteske warnx("gauge_color=[%s] (FBH)", gauge_color); 330274116Sdteske } 331274116Sdteske } else if (use_libdialog) { 332274116Sdteske init_dialog(stdin, stdout); 333274116Sdteske use_shadow = dialog_state.use_shadow; 334274116Sdteske use_colors = dialog_state.use_colors; 335274116Sdteske gauge_color[0] = 48 + dlg_color_table[GAUGE_ATTR].fg; 336274116Sdteske gauge_color[1] = 48 + dlg_color_table[GAUGE_ATTR].bg; 337274116Sdteske gauge_color[2] = dlg_color_table[GAUGE_ATTR].hilite ? 338274116Sdteske 'b' : 'B'; 339274116Sdteske gauge_color[3] = '\0'; 340274116Sdteske end_dialog(); 341274116Sdteske if (debug) { 342274116Sdteske warnx("Finished initializing dialog(3) library"); 343274116Sdteske warnx("use_shadow = %i (Boolean)", use_shadow); 344274116Sdteske warnx("use_colors = %i (Boolean)", use_colors); 345274116Sdteske warnx("gauge_color=[%s] (FBH)", gauge_color); 346274116Sdteske } 347274116Sdteske } 348274116Sdteske 349274116Sdteske /* Enable mini progress bar automatically for stdin streams if unable 350274116Sdteske * to calculate progress (missing `lines:' syntax). */ 351274116Sdteske if (dpv_nfiles <= 1 && file_list != NULL && file_list->length < 0 && 352274116Sdteske !dialog_test) 353274116Sdteske pbar_size = PBAR_SIZE_DEFAULT; 354274116Sdteske 355274116Sdteske /* If $USE_COLOR is set and non-NULL enable color; otherwise disable */ 356274116Sdteske if ((cp = getenv(ENV_USE_COLOR)) != 0) 357274116Sdteske use_color = *cp != '\0' ? 1 : 0; 358274116Sdteske 359274116Sdteske /* Print error and return `-1' if not given at least one name */ 360274116Sdteske if (dpv_nfiles == 0) { 361274116Sdteske warnx("%s: no labels provided", __func__); 362274116Sdteske return (-1); 363274116Sdteske } else if (debug) 364274116Sdteske warnx("%s: %u label%s provided", __func__, dpv_nfiles, 365274116Sdteske dpv_nfiles == 1 ? "" : "s"); 366274116Sdteske 367274116Sdteske /* If only one file and pbar size is zero, default to `-1' */ 368274116Sdteske if (dpv_nfiles <= 1 && pbar_size == 0) 369274116Sdteske pbar_size = -1; 370274116Sdteske 371274116Sdteske /* Print some debugging information */ 372274116Sdteske if (debug) { 373274116Sdteske warnx("%s: %s(%i) max rows x cols = %i x %i", 374274116Sdteske __func__, use_xdialog ? XDIALOG : DIALOG, 375274116Sdteske use_libdialog ? 3 : 1, dialog_maxrows(), 376274116Sdteske dialog_maxcols()); 377274116Sdteske } 378274116Sdteske 379274116Sdteske /* Xdialog(1) updates a lot slower than dialog(1) */ 380274116Sdteske if (dialog_test && use_xdialog) 381274116Sdteske increment = XDIALOG_INCREMENT; 382274116Sdteske 383274116Sdteske /* Always add implicit newline to pprompt (when specified) */ 384274116Sdteske if (pprompt != NULL && *pprompt != '\0') { 385274116Sdteske len = strlen(pprompt); 386274116Sdteske /* 387274116Sdteske * NOTE: pprompt = malloc(PPROMPT_MAX + 2) 388274116Sdteske * NOTE: (see getopt(2) section above for pprompt allocation) 389274116Sdteske */ 390274116Sdteske pprompt[len++] = '\\'; 391274116Sdteske pprompt[len++] = 'n'; 392274116Sdteske pprompt[len++] = '\0'; 393274116Sdteske } 394274116Sdteske 395274116Sdteske /* Xdialog(1) requires newlines (a) escaped and (b) in triplicate */ 396274116Sdteske if (use_xdialog && pprompt != NULL) { 397274116Sdteske /* Replace `\n' with `\n\\n\n' in pprompt */ 398274116Sdteske len = strlen(pprompt); 399274116Sdteske len += strcount(pprompt, "\\n") * 2; 400274116Sdteske if (len > DPV_PPROMPT_MAX) 401274116Sdteske errx(EXIT_FAILURE, "%s: Oops, pprompt buffer overflow " 402274116Sdteske "(%zu > %i)", __func__, len, DPV_PPROMPT_MAX); 403274116Sdteske if (replaceall(pprompt, "\\n", "\n\\n\n") < 0) 404274116Sdteske err(EXIT_FAILURE, "%s: replaceall()", __func__); 405274116Sdteske } 406274116Sdteske /* libdialog requires literal newlines */ 407274116Sdteske else if (use_libdialog && pprompt != NULL) 408274116Sdteske strexpandnl(pprompt); 409274116Sdteske 410274116Sdteske /* Xdialog(1) requires newlines (a) escaped and (b) in triplicate */ 411274116Sdteske if (use_xdialog && aprompt != NULL) { 412274116Sdteske /* Replace `\n' with `\n\\n\n' in aprompt */ 413274116Sdteske len = strlen(aprompt); 414274116Sdteske len += strcount(aprompt, "\\n") * 2; 415274116Sdteske if (len > DPV_APROMPT_MAX) 416274116Sdteske errx(EXIT_FAILURE, "%s: Oops, aprompt buffer overflow " 417274116Sdteske " (%zu > %i)", __func__, len, DPV_APROMPT_MAX); 418274116Sdteske if (replaceall(aprompt, "\\n", "\n\\n\n") < 0) 419274116Sdteske err(EXIT_FAILURE, "%s: replaceall()", __func__); 420274116Sdteske } 421274116Sdteske /* libdialog requires literal newlines */ 422274116Sdteske else if (use_libdialog && aprompt != NULL) 423274116Sdteske strexpandnl(aprompt); 424274116Sdteske 425274116Sdteske /* 426274116Sdteske * Warn user about an obscure dialog(1) bug (neither Xdialog(1) nor 427274116Sdteske * libdialog are affected) in the `--gauge' widget. If the first non- 428274116Sdteske * whitespace letter of "{new_prompt}" in "XXX\n{new_prompt}\nXXX\n" 429274116Sdteske * is a number, the number can sometimes be mistaken for a percentage 430274116Sdteske * to the overall progressbar. Other nasty side-effects such as the 431274116Sdteske * entire prompt not displaying or displaying improperly are caused by 432274116Sdteske * this bug too. 433274116Sdteske * 434274116Sdteske * NOTE: When we can use color, we have a work-around... prefix the 435274116Sdteske * output with `\Zn' (used to terminate ANSI and reset to normal). 436274116Sdteske */ 437274116Sdteske if (use_dialog && !use_color) { 438274116Sdteske backslash = 0; 439274116Sdteske 440274116Sdteske /* First, check pprompt (falls through if NULL) */ 441274116Sdteske fc = pprompt; 442274116Sdteske while (fc != NULL && *fc != '\0') { 443274116Sdteske if (*fc == '\n') /* leading literal newline OK */ 444274116Sdteske break; 445274116Sdteske if (!isspace(*fc) && *fc != '\\' && backslash == 0) 446274116Sdteske break; 447274116Sdteske else if (backslash > 0 && *fc != 'n') 448274116Sdteske break; 449274116Sdteske else if (*fc == '\\') { 450274116Sdteske backslash++; 451274116Sdteske if (backslash > 2) 452274116Sdteske break; /* we're safe */ 453274116Sdteske } 454274116Sdteske fc++; 455274116Sdteske } 456274116Sdteske /* First non-whitespace character that dialog(1) will see */ 457274116Sdteske if (fc != NULL && *fc >= '0' && *fc <= '9') 458274116Sdteske warnx("%s: WARNING! text argument to `-p' begins with " 459274116Sdteske "a number (not recommended)", __func__); 460274116Sdteske else if (fc > pprompt) 461274116Sdteske warnx("%s: WARNING! text argument to `-p' begins with " 462274116Sdteske "whitespace (not recommended)", __func__); 463274116Sdteske 464274116Sdteske /* 465274116Sdteske * If no pprompt or pprompt is all whitespace, check the first 466274116Sdteske * file name provided to make sure it is alright too. 467274116Sdteske */ 468274116Sdteske if ((pprompt == NULL || *fc == '\0') && file_list != NULL) { 469274116Sdteske first_file = file_list; 470274116Sdteske fc = first_file->name; 471274116Sdteske while (fc != NULL && *fc != '\0' && isspace(*fc)) 472274116Sdteske fc++; 473274116Sdteske /* First non-whitespace char that dialog(1) will see */ 474274116Sdteske if (fc != NULL && *fc >= '0' && *fc <= '9') 475274116Sdteske warnx("%s: WARNING! File name `%s' begins " 476274116Sdteske "with a number (use `-p text' for safety)", 477274116Sdteske __func__, first_file->name); 478274116Sdteske } 479274116Sdteske } 480274116Sdteske 481274116Sdteske dprompt_init(file_list); 482274116Sdteske /* Reads: label_size pbar_size pprompt aprompt dpv_nfiles */ 483274116Sdteske /* Inits: dheight and dwidth */ 484274116Sdteske 485274116Sdteske if (!debug) { 486274116Sdteske /* Internally create the initial `--gauge' prompt text */ 487274116Sdteske dprompt_recreate(file_list, (struct dpv_file_node *)NULL, 0); 488274116Sdteske 489274116Sdteske /* Spawn [X]dialog(1) `--gauge', returning pipe descriptor */ 490274116Sdteske if (use_libdialog) { 491274116Sdteske status_printf(""); 492274116Sdteske dprompt_libprint(pprompt, aprompt, 0); 493274116Sdteske } else { 494274116Sdteske dprompt_sprint(init_prompt, pprompt, aprompt); 495274116Sdteske dialog_out = dialog_spawn_gauge(init_prompt, &pid); 496274116Sdteske dprompt_dprint(dialog_out, pprompt, aprompt, 0); 497274116Sdteske } 498274116Sdteske } /* !debug */ 499274116Sdteske 500274116Sdteske /* Seed the random(3) generator */ 501274116Sdteske if (dialog_test) 502274116Sdteske srandom(0xf1eeface); 503274116Sdteske 504274116Sdteske /* Set default/custom status line format */ 505274116Sdteske if (dpv_nfiles > 1) { 506274116Sdteske snprintf(status_format_default, DPV_STATUS_FORMAT_MAX, "%s", 507274116Sdteske DPV_STATUS_MANY); 508274116Sdteske status_format_custom = config->status_many; 509274116Sdteske } else { 510274116Sdteske snprintf(status_format_default, DPV_STATUS_FORMAT_MAX, "%s", 511274116Sdteske DPV_STATUS_SOLO); 512274116Sdteske status_format_custom = config->status_solo; 513274116Sdteske } 514274116Sdteske 515274116Sdteske /* Add test mode identifier to default status line if enabled */ 516274116Sdteske if (dialog_test && (strlen(status_format_default) + 12) < 517274116Sdteske DPV_STATUS_FORMAT_MAX) 518274116Sdteske strcat(status_format_default, " [TEST MODE]"); 519274116Sdteske 520274116Sdteske /* Verify custom status format */ 521274116Sdteske status_fmt = fmtcheck(status_format_custom, status_format_default); 522274116Sdteske if (status_format_custom != NULL && 523274116Sdteske status_fmt == status_format_default) { 524274116Sdteske warnx("WARNING! Invalid status_format configuration `%s'", 525274116Sdteske status_format_custom); 526274116Sdteske warnx("Default status_format `%s'", status_format_default); 527274116Sdteske } 528274116Sdteske 529274116Sdteske /* Record when we started (used to prevent updating too quickly) */ 530274116Sdteske (void)gettimeofday(&start, (struct timezone *)NULL); 531274116Sdteske 532274116Sdteske /* Calculate number of microseconds in-between sub-second updates */ 533274116Sdteske if (status_updates_per_second != 0) 534274116Sdteske status_update_usec = 1000000 / status_updates_per_second; 535274116Sdteske if (dialog_updates_per_second != 0) 536274116Sdteske dialog_update_usec = 1000000 / dialog_updates_per_second; 537274116Sdteske 538274116Sdteske /* 539274116Sdteske * Process the file list [serially] (one for each argument passed) 540274116Sdteske */ 541274116Sdteske files_left = dpv_nfiles; 542274116Sdteske list_head = file_list; 543274116Sdteske for (curfile = file_list; curfile != NULL; curfile = curfile->next) { 544274116Sdteske keep_going = TRUE; 545274116Sdteske output_out = -1; 546274116Sdteske pct = 0; 547274116Sdteske nthfile++; 548274116Sdteske files_left--; 549274116Sdteske 550274116Sdteske if (dpv_interrupt) 551274116Sdteske break; 552274116Sdteske if (dialog_test) 553274116Sdteske pct = 0 - increment; 554274116Sdteske 555274116Sdteske /* Attempt to spawn output program for this file */ 556274116Sdteske if (!dialog_test && output != NULL) { 557274116Sdteske mask = umask(0022); 558274116Sdteske (void)umask(mask); 559274116Sdteske 560274116Sdteske switch (output_type) { 561274116Sdteske case DPV_OUTPUT_SHELL: 562274116Sdteske output_out = shell_spawn_pipecmd(output, 563274116Sdteske curfile->name, &output_pid); 564274116Sdteske break; 565274116Sdteske case DPV_OUTPUT_FILE: 566274116Sdteske path_fmt = fmtcheck(output, "%s"); 567274116Sdteske if (path_fmt == output) 568274116Sdteske len = snprintf(pathbuf, 569274116Sdteske PATH_MAX, output, curfile->name); 570274116Sdteske else 571274116Sdteske len = snprintf(pathbuf, 572274116Sdteske PATH_MAX, "%s", output); 573274116Sdteske if (len >= PATH_MAX) { 574274116Sdteske warnx("%s:%d:%s: pathbuf[%u] too small" 575274116Sdteske "to hold output argument", 576274116Sdteske __FILE__, __LINE__, __func__, 577274116Sdteske PATH_MAX); 578274116Sdteske return (-1); 579274116Sdteske } 580274116Sdteske if ((output_out = open(pathbuf, 581274116Sdteske O_CREAT|O_WRONLY, DEFFILEMODE & ~mask)) 582274116Sdteske < 0) { 583274116Sdteske warn("%s", pathbuf); 584274116Sdteske return (-1); 585274116Sdteske } 586274116Sdteske break; 587274116Sdteske default: 588274116Sdteske break; 589274116Sdteske } 590274116Sdteske } 591274116Sdteske 592274116Sdteske while (!dpv_interrupt && keep_going) { 593274116Sdteske if (dialog_test) { 594274116Sdteske usleep(50000); 595274116Sdteske pct += increment; 596274116Sdteske dpv_overall_read += 597274116Sdteske (int)(random() / 512 / dpv_nfiles); 598274116Sdteske /* 512 limits fake readout to Megabytes */ 599274116Sdteske } else if (action != NULL) 600274116Sdteske pct = action(curfile, output_out); 601274116Sdteske 602274116Sdteske if (no_overrun || dialog_test) 603274116Sdteske keep_going = (pct < 100); 604274116Sdteske else { 605274116Sdteske status = curfile->status; 606274116Sdteske keep_going = (status == DPV_STATUS_RUNNING); 607274116Sdteske } 608274116Sdteske 609274116Sdteske /* Get current time and calculate seconds elapsed */ 610274116Sdteske gettimeofday(&now, (struct timezone *)NULL); 611274116Sdteske now.tv_sec = now.tv_sec - start.tv_sec; 612274116Sdteske now.tv_usec = now.tv_usec - start.tv_usec; 613274116Sdteske if (now.tv_usec < 0) 614274116Sdteske now.tv_sec--, now.tv_usec += 1000000; 615274116Sdteske seconds = now.tv_sec + (now.tv_usec / 1000000.0); 616274116Sdteske 617274116Sdteske /* Update dialog (be it dialog(3), dialog(1), etc.) */ 618274116Sdteske if ((dialog_updates_per_second != 0 && 619274116Sdteske ( 620274116Sdteske seconds != dialog_old_seconds || 621274116Sdteske now.tv_usec - dialog_last_update >= 622274116Sdteske dialog_update_usec || 623274116Sdteske nthfile != dialog_old_nthfile 624274116Sdteske )) || pct == 100 625274116Sdteske ) { 626274116Sdteske /* Calculate overall progress (rounding up) */ 627274116Sdteske overall = (100 * nthfile - 100 + pct) / 628274116Sdteske dpv_nfiles; 629274116Sdteske if (((100 * nthfile - 100 + pct) * 10 / 630274116Sdteske dpv_nfiles % 100) > 50) 631274116Sdteske overall++; 632274116Sdteske 633274116Sdteske dprompt_recreate(list_head, curfile, pct); 634274116Sdteske 635274116Sdteske if (use_libdialog && !debug) { 636274116Sdteske /* Update dialog(3) widget */ 637274116Sdteske dprompt_libprint(pprompt, aprompt, 638274116Sdteske overall); 639274116Sdteske } else { 640274116Sdteske /* stdout, dialog(1), or Xdialog(1) */ 641274116Sdteske dprompt_dprint(dialog_out, pprompt, 642274116Sdteske aprompt, overall); 643274116Sdteske fsync(dialog_out); 644274116Sdteske } 645274116Sdteske dialog_old_seconds = seconds; 646274116Sdteske dialog_old_nthfile = nthfile; 647274116Sdteske dialog_last_update = now.tv_usec; 648274116Sdteske } 649274116Sdteske 650274116Sdteske /* Update the status line */ 651274116Sdteske if ((use_libdialog && !debug) && 652274116Sdteske status_updates_per_second != 0 && 653274116Sdteske ( 654274116Sdteske keep_going != TRUE || 655274116Sdteske seconds != status_old_seconds || 656274116Sdteske now.tv_usec - status_last_update >= 657274116Sdteske status_update_usec || 658274116Sdteske nthfile != status_old_nthfile 659274116Sdteske ) 660274116Sdteske ) { 661274116Sdteske status_printf(status_fmt, dpv_overall_read, 662274116Sdteske (dpv_overall_read / (seconds == 0 ? 1 : 663274116Sdteske seconds) * 1.0), 664274116Sdteske 1, /* XXX until we add parallelism XXX */ 665274116Sdteske files_left); 666274116Sdteske status_old_seconds = seconds; 667274116Sdteske status_old_nthfile = nthfile; 668274116Sdteske status_last_update = now.tv_usec; 669274116Sdteske } 670274116Sdteske } 671274116Sdteske 672274116Sdteske if (!dialog_test && output_out >= 0) { 673274116Sdteske close(output_out); 674274116Sdteske waitpid(output_pid, (int *)NULL, 0); 675274116Sdteske } 676274116Sdteske 677274116Sdteske if (dpv_abort) 678274116Sdteske break; 679274116Sdteske 680274116Sdteske /* Advance head of list when we hit the max display lines */ 681274116Sdteske if (display_limit > 0 && nthfile % display_limit == 0) 682274116Sdteske list_head = curfile->next; 683274116Sdteske } 684274116Sdteske 685274116Sdteske if (!debug) { 686274116Sdteske if (use_libdialog) 687274116Sdteske end_dialog(); 688274116Sdteske else { 689274116Sdteske close(dialog_out); 690274116Sdteske waitpid(pid, (int *)NULL, 0); 691274116Sdteske } 692274116Sdteske if (!dpv_interrupt) 693274116Sdteske printf("\n"); 694274116Sdteske } else 695274116Sdteske warnx("%s: %lli lines read", __func__, dpv_overall_read); 696274116Sdteske 697274116Sdteske if (dpv_interrupt || dpv_abort) 698274116Sdteske return (-1); 699274116Sdteske else 700274116Sdteske return (0); 701274116Sdteske} 702274116Sdteske 703274116Sdteske/* 704274116Sdteske * Free allocated items initialized by dpv() 705274116Sdteske */ 706274116Sdteskevoid 707274116Sdteskedpv_free(void) 708274116Sdteske{ 709274116Sdteske dialogrc_free(); 710274116Sdteske dprompt_free(); 711274116Sdteske dialog_maxsize_free(); 712274116Sdteske if (aprompt != NULL) { 713274116Sdteske free(aprompt); 714274116Sdteske aprompt = NULL; 715274116Sdteske } 716274116Sdteske if (pprompt != NULL) { 717274116Sdteske free(pprompt); 718274116Sdteske pprompt = NULL; 719274116Sdteske } 720274116Sdteske status_free(); 721274116Sdteske} 722