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