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