1041394f3SDevin Teske /*- 2041394f3SDevin Teske * Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org> 3041394f3SDevin Teske * All rights reserved. 4041394f3SDevin Teske * 5041394f3SDevin Teske * Redistribution and use in source and binary forms, with or without 6041394f3SDevin Teske * modification, are permitted provided that the following conditions 7041394f3SDevin Teske * are met: 8041394f3SDevin Teske * 1. Redistributions of source code must retain the above copyright 9041394f3SDevin Teske * notice, this list of conditions and the following disclaimer. 10041394f3SDevin Teske * 2. Redistributions in binary form must reproduce the above copyright 11041394f3SDevin Teske * notice, this list of conditions and the following disclaimer in the 12041394f3SDevin Teske * documentation and/or other materials provided with the distribution. 13041394f3SDevin Teske * 14041394f3SDevin Teske * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15041394f3SDevin Teske * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16041394f3SDevin Teske * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17041394f3SDevin Teske * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18041394f3SDevin Teske * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19041394f3SDevin Teske * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20041394f3SDevin Teske * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21041394f3SDevin Teske * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22041394f3SDevin Teske * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23041394f3SDevin Teske * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24041394f3SDevin Teske * SUCH DAMAGE. 25041394f3SDevin Teske */ 26041394f3SDevin Teske 27041394f3SDevin Teske #include <sys/cdefs.h> 28041394f3SDevin Teske __FBSDID("$FreeBSD$"); 29041394f3SDevin Teske 30041394f3SDevin Teske #include <sys/stat.h> 31041394f3SDevin Teske #include <sys/time.h> 32041394f3SDevin Teske #include <sys/types.h> 33041394f3SDevin Teske #include <sys/wait.h> 34041394f3SDevin Teske 35041394f3SDevin Teske #include <ctype.h> 36041394f3SDevin Teske #include <dialog.h> 37041394f3SDevin Teske #include <err.h> 38041394f3SDevin Teske #include <limits.h> 39041394f3SDevin Teske #include <stdio.h> 40041394f3SDevin Teske #include <stdlib.h> 41041394f3SDevin Teske #include <string.h> 42041394f3SDevin Teske #include <string_m.h> 43041394f3SDevin Teske #include <unistd.h> 44041394f3SDevin Teske 45041394f3SDevin Teske #include "dialog_util.h" 46041394f3SDevin Teske #include "dialogrc.h" 47041394f3SDevin Teske #include "dprompt.h" 48041394f3SDevin Teske #include "dpv.h" 49041394f3SDevin Teske #include "dpv_private.h" 50041394f3SDevin Teske #include "status.h" 51041394f3SDevin Teske #include "util.h" 52041394f3SDevin Teske 53041394f3SDevin Teske /* Test Mechanics (Only used when dpv_config.options |= DPV_TEST_MODE) */ 54041394f3SDevin Teske #define INCREMENT 1 /* Increment % per-pass test-mode */ 55041394f3SDevin Teske #define XDIALOG_INCREMENT 15 /* different for slower Xdialog(1) */ 56041394f3SDevin Teske static uint8_t increment = INCREMENT; 57041394f3SDevin Teske 58041394f3SDevin Teske /* Debugging */ 59041394f3SDevin Teske uint8_t debug = FALSE; 60041394f3SDevin Teske 61041394f3SDevin Teske /* Data to process */ 62041394f3SDevin Teske int dpv_interrupt = FALSE; 63041394f3SDevin Teske int dpv_abort = FALSE; 64041394f3SDevin Teske unsigned int dpv_nfiles = 0; 65041394f3SDevin Teske 66041394f3SDevin Teske /* Data processing */ 67041394f3SDevin Teske long long dpv_overall_read = 0; 68041394f3SDevin Teske static char pathbuf[PATH_MAX]; 69041394f3SDevin Teske 70041394f3SDevin Teske /* Extra display information */ 71041394f3SDevin Teske uint8_t no_labels = FALSE; /* dpv_config.options & DPV_NO_LABELS */ 72041394f3SDevin Teske uint8_t wide = FALSE; /* dpv_config.options & DPV_WIDE_MODE */ 73041394f3SDevin Teske char *aprompt = NULL; /* dpv_config.aprompt */ 74041394f3SDevin Teske char *msg_done = NULL; /* dpv_config.msg_done */ 75041394f3SDevin Teske char *msg_fail = NULL; /* dpv_config.msg_fail */ 76041394f3SDevin Teske char *msg_pending = NULL; /* dpv_config.msg_pending */ 77041394f3SDevin Teske char *pprompt = NULL; /* dpv_config.pprompt */ 78041394f3SDevin Teske 79041394f3SDevin Teske /* Status-Line format for when using dialog(3) */ 80492b8271SDevin Teske static const char *status_format_custom = NULL; 81492b8271SDevin Teske static char status_format_default[DPV_STATUS_FORMAT_MAX]; 82041394f3SDevin Teske 83041394f3SDevin Teske /* 84041394f3SDevin Teske * Takes a pointer to a dpv_config structure containing layout details and 85041394f3SDevin Teske * pointer to initial element in a linked-list of dpv_file_node structures, 86041394f3SDevin Teske * each presenting a file to process. Executes the `action' function passed-in 87041394f3SDevin Teske * as a member to the `config' structure argument. 88041394f3SDevin Teske */ 89041394f3SDevin Teske int 90041394f3SDevin Teske dpv(struct dpv_config *config, struct dpv_file_node *file_list) 91041394f3SDevin Teske { 92041394f3SDevin Teske char c; 93041394f3SDevin Teske uint8_t keep_going; 94041394f3SDevin Teske uint8_t nls = FALSE; /* See dialog_prompt_nlstate() */ 95041394f3SDevin Teske uint8_t no_overrun = FALSE; 96041394f3SDevin Teske uint8_t pprompt_nls = FALSE; /* See dialog_prompt_nlstate() */ 97041394f3SDevin Teske uint8_t shrink_label_size = FALSE; 98041394f3SDevin Teske mode_t mask; 99041394f3SDevin Teske uint16_t options; 100041394f3SDevin Teske char *cp; 101041394f3SDevin Teske char *fc; 102041394f3SDevin Teske char *last; 103041394f3SDevin Teske char *name; 104041394f3SDevin Teske char *output; 105041394f3SDevin Teske const char *status_fmt; 106041394f3SDevin Teske const char *path_fmt; 107041394f3SDevin Teske enum dpv_display display_type; 108041394f3SDevin Teske enum dpv_output output_type; 109041394f3SDevin Teske enum dpv_status status; 110041394f3SDevin Teske int (*action)(struct dpv_file_node *file, int out); 111041394f3SDevin Teske int backslash; 112041394f3SDevin Teske int dialog_last_update = 0; 113041394f3SDevin Teske int dialog_old_nthfile = 0; 114041394f3SDevin Teske int dialog_old_seconds = -1; 115041394f3SDevin Teske int dialog_out = STDOUT_FILENO; 116041394f3SDevin Teske int dialog_update_usec = 0; 117041394f3SDevin Teske int dialog_updates_per_second; 118041394f3SDevin Teske int files_left; 119041394f3SDevin Teske int max_cols; 120041394f3SDevin Teske int nthfile = 0; 121041394f3SDevin Teske int output_out; 122041394f3SDevin Teske int overall = 0; 123041394f3SDevin Teske int pct; 124041394f3SDevin Teske int res; 125041394f3SDevin Teske int seconds; 126041394f3SDevin Teske int status_last_update = 0; 127041394f3SDevin Teske int status_old_nthfile = 0; 128041394f3SDevin Teske int status_old_seconds = -1; 129041394f3SDevin Teske int status_update_usec = 0; 130041394f3SDevin Teske int status_updates_per_second; 131041394f3SDevin Teske pid_t output_pid; 132041394f3SDevin Teske pid_t pid; 133041394f3SDevin Teske size_t len; 134041394f3SDevin Teske struct dpv_file_node *curfile; 135041394f3SDevin Teske struct dpv_file_node *first_file; 136041394f3SDevin Teske struct dpv_file_node *list_head; 137041394f3SDevin Teske struct timeval now; 138041394f3SDevin Teske struct timeval start; 139041394f3SDevin Teske char init_prompt[PROMPT_MAX + 1] = ""; 140041394f3SDevin Teske 141041394f3SDevin Teske /* Initialize globals to default values */ 142041394f3SDevin Teske aprompt = NULL; 143041394f3SDevin Teske pprompt = NULL; 144041394f3SDevin Teske options = 0; 145041394f3SDevin Teske action = NULL; 146041394f3SDevin Teske backtitle = NULL; 147041394f3SDevin Teske debug = FALSE; 148041394f3SDevin Teske dialog_test = FALSE; 149041394f3SDevin Teske dialog_updates_per_second = DIALOG_UPDATES_PER_SEC; 150041394f3SDevin Teske display_limit = DISPLAY_LIMIT_DEFAULT; 151041394f3SDevin Teske display_type = DPV_DISPLAY_LIBDIALOG; 152041394f3SDevin Teske label_size = LABEL_SIZE_DEFAULT; 153041394f3SDevin Teske msg_done = NULL; 154041394f3SDevin Teske msg_fail = NULL; 155041394f3SDevin Teske msg_pending = NULL; 156041394f3SDevin Teske no_labels = FALSE; 157041394f3SDevin Teske output = NULL; 158041394f3SDevin Teske output_type = DPV_OUTPUT_NONE; 159041394f3SDevin Teske pbar_size = PBAR_SIZE_DEFAULT; 160041394f3SDevin Teske status_format_custom = NULL; 161041394f3SDevin Teske status_updates_per_second = STATUS_UPDATES_PER_SEC; 162041394f3SDevin Teske title = NULL; 163041394f3SDevin Teske wide = FALSE; 164041394f3SDevin Teske 165041394f3SDevin Teske /* Process config options (overriding defaults) */ 166041394f3SDevin Teske if (config != NULL) { 167041394f3SDevin Teske if (config->aprompt != NULL) { 168041394f3SDevin Teske if (aprompt == NULL) { 169041394f3SDevin Teske aprompt = malloc(DPV_APROMPT_MAX); 170041394f3SDevin Teske if (aprompt == NULL) 171041394f3SDevin Teske return (-1); 172041394f3SDevin Teske } 173041394f3SDevin Teske snprintf(aprompt, DPV_APROMPT_MAX, "%s", 174041394f3SDevin Teske config->aprompt); 175041394f3SDevin Teske } 176041394f3SDevin Teske if (config->pprompt != NULL) { 177041394f3SDevin Teske if (pprompt == NULL) { 178041394f3SDevin Teske pprompt = malloc(DPV_PPROMPT_MAX + 2); 179041394f3SDevin Teske /* +2 is for implicit "\n" appended later */ 180041394f3SDevin Teske if (pprompt == NULL) 181041394f3SDevin Teske return (-1); 182041394f3SDevin Teske } 183041394f3SDevin Teske snprintf(pprompt, DPV_APROMPT_MAX, "%s", 184041394f3SDevin Teske config->pprompt); 185041394f3SDevin Teske } 186041394f3SDevin Teske 187041394f3SDevin Teske options = config->options; 188041394f3SDevin Teske action = config->action; 189041394f3SDevin Teske backtitle = config->backtitle; 190041394f3SDevin Teske debug = config->debug; 191041394f3SDevin Teske dialog_test = ((options & DPV_TEST_MODE) != 0); 192041394f3SDevin Teske dialog_updates_per_second = config->dialog_updates_per_second; 193041394f3SDevin Teske display_limit = config->display_limit; 194041394f3SDevin Teske display_type = config->display_type; 195041394f3SDevin Teske label_size = config->label_size; 196041394f3SDevin Teske msg_done = (char *)config->msg_done; 197041394f3SDevin Teske msg_fail = (char *)config->msg_fail; 198041394f3SDevin Teske msg_pending = (char *)config->msg_pending; 199041394f3SDevin Teske no_labels = ((options & DPV_NO_LABELS) != 0); 200041394f3SDevin Teske no_overrun = ((options & DPV_NO_OVERRUN) != 0); 201041394f3SDevin Teske output = config->output; 202041394f3SDevin Teske output_type = config->output_type; 203041394f3SDevin Teske pbar_size = config->pbar_size; 204041394f3SDevin Teske status_updates_per_second = config->status_updates_per_second; 205041394f3SDevin Teske title = config->title; 206041394f3SDevin Teske wide = ((options & DPV_WIDE_MODE) != 0); 207041394f3SDevin Teske 208041394f3SDevin Teske /* Enforce some minimums (pedantic) */ 209041394f3SDevin Teske if (display_limit < -1) 210041394f3SDevin Teske display_limit = -1; 211041394f3SDevin Teske if (label_size < -1) 212041394f3SDevin Teske label_size = -1; 213041394f3SDevin Teske if (pbar_size < -1) 214041394f3SDevin Teske pbar_size = -1; 215041394f3SDevin Teske 216041394f3SDevin Teske /* For the mini-pbar, -1 means hide, zero is invalid unless 217041394f3SDevin Teske * only one file is given */ 218041394f3SDevin Teske if (pbar_size == 0) { 219041394f3SDevin Teske if (file_list == NULL || file_list->next == NULL) 220041394f3SDevin Teske pbar_size = -1; 221041394f3SDevin Teske else 222041394f3SDevin Teske pbar_size = PBAR_SIZE_DEFAULT; 223041394f3SDevin Teske } 224041394f3SDevin Teske 225041394f3SDevin Teske /* For the label, -1 means auto-size, zero is invalid unless 226041394f3SDevin Teske * specifically requested through the use of options flag */ 227041394f3SDevin Teske if (label_size == 0 && no_labels == FALSE) 228041394f3SDevin Teske label_size = LABEL_SIZE_DEFAULT; 229041394f3SDevin Teske 230041394f3SDevin Teske /* Status update should not be zero */ 231041394f3SDevin Teske if (status_updates_per_second == 0) 232041394f3SDevin Teske status_updates_per_second = STATUS_UPDATES_PER_SEC; 233041394f3SDevin Teske } /* config != NULL */ 234041394f3SDevin Teske 235041394f3SDevin Teske /* Process the type of display we've been requested to produce */ 236041394f3SDevin Teske switch (display_type) { 237041394f3SDevin Teske case DPV_DISPLAY_STDOUT: 238041394f3SDevin Teske debug = TRUE; 239041394f3SDevin Teske use_color = FALSE; 240041394f3SDevin Teske use_dialog = FALSE; 241041394f3SDevin Teske use_libdialog = FALSE; 242041394f3SDevin Teske use_xdialog = FALSE; 243041394f3SDevin Teske break; 244041394f3SDevin Teske case DPV_DISPLAY_DIALOG: 245041394f3SDevin Teske use_color = TRUE; 246041394f3SDevin Teske use_dialog = TRUE; 247041394f3SDevin Teske use_libdialog = FALSE; 248041394f3SDevin Teske use_xdialog = FALSE; 249041394f3SDevin Teske break; 250041394f3SDevin Teske case DPV_DISPLAY_XDIALOG: 251041394f3SDevin Teske snprintf(dialog, PATH_MAX, XDIALOG); 252041394f3SDevin Teske use_color = FALSE; 253041394f3SDevin Teske use_dialog = FALSE; 254041394f3SDevin Teske use_libdialog = FALSE; 255041394f3SDevin Teske use_xdialog = TRUE; 256041394f3SDevin Teske break; 257041394f3SDevin Teske default: 258041394f3SDevin Teske use_color = TRUE; 259041394f3SDevin Teske use_dialog = FALSE; 260041394f3SDevin Teske use_libdialog = TRUE; 261041394f3SDevin Teske use_xdialog = FALSE; 262041394f3SDevin Teske break; 263041394f3SDevin Teske } /* display_type */ 264041394f3SDevin Teske 265041394f3SDevin Teske /* Enforce additional minimums that require knowing our display type */ 266041394f3SDevin Teske if (dialog_updates_per_second == 0) 267041394f3SDevin Teske dialog_updates_per_second = use_xdialog ? 268041394f3SDevin Teske XDIALOG_UPDATES_PER_SEC : DIALOG_UPDATES_PER_SEC; 269041394f3SDevin Teske 270041394f3SDevin Teske /* Allow forceful override of use_color */ 271041394f3SDevin Teske if (config != NULL && (config->options & DPV_USE_COLOR) != 0) 272041394f3SDevin Teske use_color = TRUE; 273041394f3SDevin Teske 274041394f3SDevin Teske /* Count the number of files in provided list of dpv_file_node's */ 275041394f3SDevin Teske if (use_dialog && pprompt != NULL && *pprompt != '\0') 276041394f3SDevin Teske pprompt_nls = dialog_prompt_nlstate(pprompt); 277041394f3SDevin Teske 278041394f3SDevin Teske max_cols = dialog_maxcols(); 279041394f3SDevin Teske if (label_size == -1) 280041394f3SDevin Teske shrink_label_size = TRUE; 281041394f3SDevin Teske 282041394f3SDevin Teske /* Process file arguments */ 283041394f3SDevin Teske for (curfile = file_list; curfile != NULL; curfile = curfile->next) { 284041394f3SDevin Teske dpv_nfiles++; 285041394f3SDevin Teske 286041394f3SDevin Teske /* dialog(3) only expands literal newlines */ 287041394f3SDevin Teske if (use_libdialog) strexpandnl(curfile->name); 288041394f3SDevin Teske 289041394f3SDevin Teske /* Optionally calculate label size for file */ 290041394f3SDevin Teske if (shrink_label_size) { 291041394f3SDevin Teske nls = FALSE; 292041394f3SDevin Teske name = curfile->name; 293041394f3SDevin Teske if (curfile == file_list) 294041394f3SDevin Teske nls = pprompt_nls; 295041394f3SDevin Teske last = (char *)dialog_prompt_lastline(name, nls); 296041394f3SDevin Teske if (use_dialog) { 297041394f3SDevin Teske c = *last; 298041394f3SDevin Teske *last = '\0'; 299041394f3SDevin Teske nls = dialog_prompt_nlstate(name); 300041394f3SDevin Teske *last = c; 301041394f3SDevin Teske } 302041394f3SDevin Teske len = dialog_prompt_longestline(last, nls); 303041394f3SDevin Teske if ((int)len > (label_size - 3)) { 304041394f3SDevin Teske if (label_size > 0) 305041394f3SDevin Teske label_size += 3; 306041394f3SDevin Teske label_size = len; 307041394f3SDevin Teske /* Room for ellipsis (unless NULL) */ 308041394f3SDevin Teske if (label_size > 0) 309041394f3SDevin Teske label_size += 3; 310041394f3SDevin Teske } 311041394f3SDevin Teske 312041394f3SDevin Teske if (max_cols > 0 && label_size > (max_cols - pbar_size 313041394f3SDevin Teske - 9)) 314041394f3SDevin Teske label_size = max_cols - pbar_size - 9; 315041394f3SDevin Teske } 316041394f3SDevin Teske 317041394f3SDevin Teske if (debug) 318041394f3SDevin Teske warnx("label=[%s] path=[%s] size=%lli", 319041394f3SDevin Teske curfile->name, curfile->path, curfile->length); 320041394f3SDevin Teske } /* file_list */ 321041394f3SDevin Teske 322041394f3SDevin Teske /* Optionally process the contents of DIALOGRC (~/.dialogrc) */ 323041394f3SDevin Teske if (use_dialog) { 324041394f3SDevin Teske res = parse_dialogrc(); 325041394f3SDevin Teske if (debug && res == 0) { 326041394f3SDevin Teske warnx("Successfully read `%s' config file", DIALOGRC); 327041394f3SDevin Teske warnx("use_shadow = %i (Boolean)", use_shadow); 328041394f3SDevin Teske warnx("use_colors = %i (Boolean)", use_colors); 329041394f3SDevin Teske warnx("gauge_color=[%s] (FBH)", gauge_color); 330041394f3SDevin Teske } 331041394f3SDevin Teske } else if (use_libdialog) { 332041394f3SDevin Teske init_dialog(stdin, stdout); 333041394f3SDevin Teske use_shadow = dialog_state.use_shadow; 334041394f3SDevin Teske use_colors = dialog_state.use_colors; 335041394f3SDevin Teske gauge_color[0] = 48 + dlg_color_table[GAUGE_ATTR].fg; 336041394f3SDevin Teske gauge_color[1] = 48 + dlg_color_table[GAUGE_ATTR].bg; 337041394f3SDevin Teske gauge_color[2] = dlg_color_table[GAUGE_ATTR].hilite ? 338041394f3SDevin Teske 'b' : 'B'; 339041394f3SDevin Teske gauge_color[3] = '\0'; 340041394f3SDevin Teske end_dialog(); 341041394f3SDevin Teske if (debug) { 342041394f3SDevin Teske warnx("Finished initializing dialog(3) library"); 343041394f3SDevin Teske warnx("use_shadow = %i (Boolean)", use_shadow); 344041394f3SDevin Teske warnx("use_colors = %i (Boolean)", use_colors); 345041394f3SDevin Teske warnx("gauge_color=[%s] (FBH)", gauge_color); 346041394f3SDevin Teske } 347041394f3SDevin Teske } 348041394f3SDevin Teske 349041394f3SDevin Teske /* Enable mini progress bar automatically for stdin streams if unable 350041394f3SDevin Teske * to calculate progress (missing `lines:' syntax). */ 351041394f3SDevin Teske if (dpv_nfiles <= 1 && file_list != NULL && file_list->length < 0 && 352041394f3SDevin Teske !dialog_test) 353041394f3SDevin Teske pbar_size = PBAR_SIZE_DEFAULT; 354041394f3SDevin Teske 355041394f3SDevin Teske /* If $USE_COLOR is set and non-NULL enable color; otherwise disable */ 356041394f3SDevin Teske if ((cp = getenv(ENV_USE_COLOR)) != 0) 357041394f3SDevin Teske use_color = *cp != '\0' ? 1 : 0; 358041394f3SDevin Teske 359041394f3SDevin Teske /* Print error and return `-1' if not given at least one name */ 360041394f3SDevin Teske if (dpv_nfiles == 0) { 361041394f3SDevin Teske warnx("%s: no labels provided", __func__); 362041394f3SDevin Teske return (-1); 363041394f3SDevin Teske } else if (debug) 364041394f3SDevin Teske warnx("%s: %u label%s provided", __func__, dpv_nfiles, 365041394f3SDevin Teske dpv_nfiles == 1 ? "" : "s"); 366041394f3SDevin Teske 367041394f3SDevin Teske /* If only one file and pbar size is zero, default to `-1' */ 368041394f3SDevin Teske if (dpv_nfiles <= 1 && pbar_size == 0) 369041394f3SDevin Teske pbar_size = -1; 370041394f3SDevin Teske 371041394f3SDevin Teske /* Print some debugging information */ 372041394f3SDevin Teske if (debug) { 373041394f3SDevin Teske warnx("%s: %s(%i) max rows x cols = %i x %i", 374041394f3SDevin Teske __func__, use_xdialog ? XDIALOG : DIALOG, 375041394f3SDevin Teske use_libdialog ? 3 : 1, dialog_maxrows(), 376041394f3SDevin Teske dialog_maxcols()); 377041394f3SDevin Teske } 378041394f3SDevin Teske 379041394f3SDevin Teske /* Xdialog(1) updates a lot slower than dialog(1) */ 380041394f3SDevin Teske if (dialog_test && use_xdialog) 381041394f3SDevin Teske increment = XDIALOG_INCREMENT; 382041394f3SDevin Teske 383041394f3SDevin Teske /* Always add implicit newline to pprompt (when specified) */ 384041394f3SDevin Teske if (pprompt != NULL && *pprompt != '\0') { 385041394f3SDevin Teske len = strlen(pprompt); 386041394f3SDevin Teske /* 387041394f3SDevin Teske * NOTE: pprompt = malloc(PPROMPT_MAX + 2) 388041394f3SDevin Teske * NOTE: (see getopt(2) section above for pprompt allocation) 389041394f3SDevin Teske */ 390041394f3SDevin Teske pprompt[len++] = '\\'; 391041394f3SDevin Teske pprompt[len++] = 'n'; 392041394f3SDevin Teske pprompt[len++] = '\0'; 393041394f3SDevin Teske } 394041394f3SDevin Teske 395041394f3SDevin Teske /* Xdialog(1) requires newlines (a) escaped and (b) in triplicate */ 396041394f3SDevin Teske if (use_xdialog && pprompt != NULL) { 397041394f3SDevin Teske /* Replace `\n' with `\n\\n\n' in pprompt */ 398041394f3SDevin Teske len = strlen(pprompt); 399041394f3SDevin Teske len += strcount(pprompt, "\\n") * 2; 400041394f3SDevin Teske if (len > DPV_PPROMPT_MAX) 401041394f3SDevin Teske errx(EXIT_FAILURE, "%s: Oops, pprompt buffer overflow " 402041394f3SDevin Teske "(%zu > %i)", __func__, len, DPV_PPROMPT_MAX); 403041394f3SDevin Teske if (replaceall(pprompt, "\\n", "\n\\n\n") < 0) 404041394f3SDevin Teske err(EXIT_FAILURE, "%s: replaceall()", __func__); 405041394f3SDevin Teske } 406041394f3SDevin Teske /* libdialog requires literal newlines */ 407041394f3SDevin Teske else if (use_libdialog && pprompt != NULL) 408041394f3SDevin Teske strexpandnl(pprompt); 409041394f3SDevin Teske 410041394f3SDevin Teske /* Xdialog(1) requires newlines (a) escaped and (b) in triplicate */ 411041394f3SDevin Teske if (use_xdialog && aprompt != NULL) { 412041394f3SDevin Teske /* Replace `\n' with `\n\\n\n' in aprompt */ 413041394f3SDevin Teske len = strlen(aprompt); 414041394f3SDevin Teske len += strcount(aprompt, "\\n") * 2; 415041394f3SDevin Teske if (len > DPV_APROMPT_MAX) 416041394f3SDevin Teske errx(EXIT_FAILURE, "%s: Oops, aprompt buffer overflow " 417041394f3SDevin Teske " (%zu > %i)", __func__, len, DPV_APROMPT_MAX); 418041394f3SDevin Teske if (replaceall(aprompt, "\\n", "\n\\n\n") < 0) 419041394f3SDevin Teske err(EXIT_FAILURE, "%s: replaceall()", __func__); 420041394f3SDevin Teske } 421041394f3SDevin Teske /* libdialog requires literal newlines */ 422041394f3SDevin Teske else if (use_libdialog && aprompt != NULL) 423041394f3SDevin Teske strexpandnl(aprompt); 424041394f3SDevin Teske 425041394f3SDevin Teske /* 426041394f3SDevin Teske * Warn user about an obscure dialog(1) bug (neither Xdialog(1) nor 427041394f3SDevin Teske * libdialog are affected) in the `--gauge' widget. If the first non- 428041394f3SDevin Teske * whitespace letter of "{new_prompt}" in "XXX\n{new_prompt}\nXXX\n" 429041394f3SDevin Teske * is a number, the number can sometimes be mistaken for a percentage 430041394f3SDevin Teske * to the overall progressbar. Other nasty side-effects such as the 431041394f3SDevin Teske * entire prompt not displaying or displaying improperly are caused by 432041394f3SDevin Teske * this bug too. 433041394f3SDevin Teske * 434041394f3SDevin Teske * NOTE: When we can use color, we have a work-around... prefix the 435041394f3SDevin Teske * output with `\Zn' (used to terminate ANSI and reset to normal). 436041394f3SDevin Teske */ 437041394f3SDevin Teske if (use_dialog && !use_color) { 438041394f3SDevin Teske backslash = 0; 439041394f3SDevin Teske 440041394f3SDevin Teske /* First, check pprompt (falls through if NULL) */ 441041394f3SDevin Teske fc = pprompt; 442041394f3SDevin Teske while (fc != NULL && *fc != '\0') { 443041394f3SDevin Teske if (*fc == '\n') /* leading literal newline OK */ 444041394f3SDevin Teske break; 445041394f3SDevin Teske if (!isspace(*fc) && *fc != '\\' && backslash == 0) 446041394f3SDevin Teske break; 447041394f3SDevin Teske else if (backslash > 0 && *fc != 'n') 448041394f3SDevin Teske break; 449041394f3SDevin Teske else if (*fc == '\\') { 450041394f3SDevin Teske backslash++; 451041394f3SDevin Teske if (backslash > 2) 452041394f3SDevin Teske break; /* we're safe */ 453041394f3SDevin Teske } 454041394f3SDevin Teske fc++; 455041394f3SDevin Teske } 456041394f3SDevin Teske /* First non-whitespace character that dialog(1) will see */ 457041394f3SDevin Teske if (fc != NULL && *fc >= '0' && *fc <= '9') 458041394f3SDevin Teske warnx("%s: WARNING! text argument to `-p' begins with " 459041394f3SDevin Teske "a number (not recommended)", __func__); 460041394f3SDevin Teske else if (fc > pprompt) 461041394f3SDevin Teske warnx("%s: WARNING! text argument to `-p' begins with " 462041394f3SDevin Teske "whitespace (not recommended)", __func__); 463041394f3SDevin Teske 464041394f3SDevin Teske /* 465041394f3SDevin Teske * If no pprompt or pprompt is all whitespace, check the first 466041394f3SDevin Teske * file name provided to make sure it is alright too. 467041394f3SDevin Teske */ 468041394f3SDevin Teske if ((pprompt == NULL || *fc == '\0') && file_list != NULL) { 469041394f3SDevin Teske first_file = file_list; 470041394f3SDevin Teske fc = first_file->name; 471041394f3SDevin Teske while (fc != NULL && *fc != '\0' && isspace(*fc)) 472041394f3SDevin Teske fc++; 473041394f3SDevin Teske /* First non-whitespace char that dialog(1) will see */ 474041394f3SDevin Teske if (fc != NULL && *fc >= '0' && *fc <= '9') 475041394f3SDevin Teske warnx("%s: WARNING! File name `%s' begins " 476041394f3SDevin Teske "with a number (use `-p text' for safety)", 477041394f3SDevin Teske __func__, first_file->name); 478041394f3SDevin Teske } 479041394f3SDevin Teske } 480041394f3SDevin Teske 481041394f3SDevin Teske dprompt_init(file_list); 482041394f3SDevin Teske /* Reads: label_size pbar_size pprompt aprompt dpv_nfiles */ 483041394f3SDevin Teske /* Inits: dheight and dwidth */ 484041394f3SDevin Teske 485041394f3SDevin Teske if (!debug) { 486041394f3SDevin Teske /* Internally create the initial `--gauge' prompt text */ 487041394f3SDevin Teske dprompt_recreate(file_list, (struct dpv_file_node *)NULL, 0); 488041394f3SDevin Teske 489041394f3SDevin Teske /* Spawn [X]dialog(1) `--gauge', returning pipe descriptor */ 490041394f3SDevin Teske if (use_libdialog) { 491041394f3SDevin Teske status_printf(""); 492041394f3SDevin Teske dprompt_libprint(pprompt, aprompt, 0); 493041394f3SDevin Teske } else { 494041394f3SDevin Teske dprompt_sprint(init_prompt, pprompt, aprompt); 495041394f3SDevin Teske dialog_out = dialog_spawn_gauge(init_prompt, &pid); 496041394f3SDevin Teske dprompt_dprint(dialog_out, pprompt, aprompt, 0); 497041394f3SDevin Teske } 498041394f3SDevin Teske } /* !debug */ 499041394f3SDevin Teske 500041394f3SDevin Teske /* Seed the random(3) generator */ 501041394f3SDevin Teske if (dialog_test) 502041394f3SDevin Teske srandom(0xf1eeface); 503041394f3SDevin Teske 504041394f3SDevin Teske /* Set default/custom status line format */ 505041394f3SDevin Teske if (dpv_nfiles > 1) { 506041394f3SDevin Teske snprintf(status_format_default, DPV_STATUS_FORMAT_MAX, "%s", 507041394f3SDevin Teske DPV_STATUS_MANY); 508041394f3SDevin Teske status_format_custom = config->status_many; 509041394f3SDevin Teske } else { 510041394f3SDevin Teske snprintf(status_format_default, DPV_STATUS_FORMAT_MAX, "%s", 511041394f3SDevin Teske DPV_STATUS_SOLO); 512041394f3SDevin Teske status_format_custom = config->status_solo; 513041394f3SDevin Teske } 514041394f3SDevin Teske 515041394f3SDevin Teske /* Add test mode identifier to default status line if enabled */ 516041394f3SDevin Teske if (dialog_test && (strlen(status_format_default) + 12) < 517041394f3SDevin Teske DPV_STATUS_FORMAT_MAX) 518041394f3SDevin Teske strcat(status_format_default, " [TEST MODE]"); 519041394f3SDevin Teske 520041394f3SDevin Teske /* Verify custom status format */ 521041394f3SDevin Teske status_fmt = fmtcheck(status_format_custom, status_format_default); 522041394f3SDevin Teske if (status_format_custom != NULL && 523041394f3SDevin Teske status_fmt == status_format_default) { 524041394f3SDevin Teske warnx("WARNING! Invalid status_format configuration `%s'", 525041394f3SDevin Teske status_format_custom); 526041394f3SDevin Teske warnx("Default status_format `%s'", status_format_default); 527041394f3SDevin Teske } 528041394f3SDevin Teske 529041394f3SDevin Teske /* Record when we started (used to prevent updating too quickly) */ 530041394f3SDevin Teske (void)gettimeofday(&start, (struct timezone *)NULL); 531041394f3SDevin Teske 532041394f3SDevin Teske /* Calculate number of microseconds in-between sub-second updates */ 533041394f3SDevin Teske if (status_updates_per_second != 0) 534041394f3SDevin Teske status_update_usec = 1000000 / status_updates_per_second; 535041394f3SDevin Teske if (dialog_updates_per_second != 0) 536041394f3SDevin Teske dialog_update_usec = 1000000 / dialog_updates_per_second; 537041394f3SDevin Teske 538041394f3SDevin Teske /* 539041394f3SDevin Teske * Process the file list [serially] (one for each argument passed) 540041394f3SDevin Teske */ 541041394f3SDevin Teske files_left = dpv_nfiles; 542041394f3SDevin Teske list_head = file_list; 543041394f3SDevin Teske for (curfile = file_list; curfile != NULL; curfile = curfile->next) { 544041394f3SDevin Teske keep_going = TRUE; 545041394f3SDevin Teske output_out = -1; 546041394f3SDevin Teske pct = 0; 547041394f3SDevin Teske nthfile++; 548041394f3SDevin Teske files_left--; 549041394f3SDevin Teske 550041394f3SDevin Teske if (dpv_interrupt) 551041394f3SDevin Teske break; 552041394f3SDevin Teske if (dialog_test) 553041394f3SDevin Teske pct = 0 - increment; 554041394f3SDevin Teske 555041394f3SDevin Teske /* Attempt to spawn output program for this file */ 556041394f3SDevin Teske if (!dialog_test && output != NULL) { 557041394f3SDevin Teske mask = umask(0022); 558041394f3SDevin Teske (void)umask(mask); 559041394f3SDevin Teske 560041394f3SDevin Teske switch (output_type) { 561041394f3SDevin Teske case DPV_OUTPUT_SHELL: 562041394f3SDevin Teske output_out = shell_spawn_pipecmd(output, 563041394f3SDevin Teske curfile->name, &output_pid); 564041394f3SDevin Teske break; 565041394f3SDevin Teske case DPV_OUTPUT_FILE: 566041394f3SDevin Teske path_fmt = fmtcheck(output, "%s"); 567041394f3SDevin Teske if (path_fmt == output) 568041394f3SDevin Teske len = snprintf(pathbuf, 569041394f3SDevin Teske PATH_MAX, output, curfile->name); 570041394f3SDevin Teske else 571041394f3SDevin Teske len = snprintf(pathbuf, 572041394f3SDevin Teske PATH_MAX, "%s", output); 573041394f3SDevin Teske if (len >= PATH_MAX) { 574041394f3SDevin Teske warnx("%s:%d:%s: pathbuf[%u] too small" 575041394f3SDevin Teske "to hold output argument", 576041394f3SDevin Teske __FILE__, __LINE__, __func__, 577041394f3SDevin Teske PATH_MAX); 578041394f3SDevin Teske return (-1); 579041394f3SDevin Teske } 580041394f3SDevin Teske if ((output_out = open(pathbuf, 581041394f3SDevin Teske O_CREAT|O_WRONLY, DEFFILEMODE & ~mask)) 582041394f3SDevin Teske < 0) { 583041394f3SDevin Teske warn("%s", pathbuf); 584041394f3SDevin Teske return (-1); 585041394f3SDevin Teske } 586041394f3SDevin Teske break; 587041394f3SDevin Teske default: 588041394f3SDevin Teske break; 589041394f3SDevin Teske } 590041394f3SDevin Teske } 591041394f3SDevin Teske 592041394f3SDevin Teske while (!dpv_interrupt && keep_going) { 593041394f3SDevin Teske if (dialog_test) { 594041394f3SDevin Teske usleep(50000); 595041394f3SDevin Teske pct += increment; 596041394f3SDevin Teske dpv_overall_read += 597041394f3SDevin Teske (int)(random() / 512 / dpv_nfiles); 598041394f3SDevin Teske /* 512 limits fake readout to Megabytes */ 599041394f3SDevin Teske } else if (action != NULL) 600041394f3SDevin Teske pct = action(curfile, output_out); 601041394f3SDevin Teske 602041394f3SDevin Teske if (no_overrun || dialog_test) 603041394f3SDevin Teske keep_going = (pct < 100); 604041394f3SDevin Teske else { 605041394f3SDevin Teske status = curfile->status; 606041394f3SDevin Teske keep_going = (status == DPV_STATUS_RUNNING); 607041394f3SDevin Teske } 608041394f3SDevin Teske 609041394f3SDevin Teske /* Get current time and calculate seconds elapsed */ 610041394f3SDevin Teske gettimeofday(&now, (struct timezone *)NULL); 611041394f3SDevin Teske now.tv_sec = now.tv_sec - start.tv_sec; 612041394f3SDevin Teske now.tv_usec = now.tv_usec - start.tv_usec; 613041394f3SDevin Teske if (now.tv_usec < 0) 614041394f3SDevin Teske now.tv_sec--, now.tv_usec += 1000000; 615041394f3SDevin Teske seconds = now.tv_sec + (now.tv_usec / 1000000.0); 616041394f3SDevin Teske 617041394f3SDevin Teske /* Update dialog (be it dialog(3), dialog(1), etc.) */ 618041394f3SDevin Teske if ((dialog_updates_per_second != 0 && 619041394f3SDevin Teske ( 620041394f3SDevin Teske seconds != dialog_old_seconds || 621041394f3SDevin Teske now.tv_usec - dialog_last_update >= 622041394f3SDevin Teske dialog_update_usec || 623041394f3SDevin Teske nthfile != dialog_old_nthfile 624041394f3SDevin Teske )) || pct == 100 625041394f3SDevin Teske ) { 626041394f3SDevin Teske /* Calculate overall progress (rounding up) */ 627041394f3SDevin Teske overall = (100 * nthfile - 100 + pct) / 628041394f3SDevin Teske dpv_nfiles; 629041394f3SDevin Teske if (((100 * nthfile - 100 + pct) * 10 / 630041394f3SDevin Teske dpv_nfiles % 100) > 50) 631041394f3SDevin Teske overall++; 632041394f3SDevin Teske 633041394f3SDevin Teske dprompt_recreate(list_head, curfile, pct); 634041394f3SDevin Teske 635041394f3SDevin Teske if (use_libdialog && !debug) { 636041394f3SDevin Teske /* Update dialog(3) widget */ 637041394f3SDevin Teske dprompt_libprint(pprompt, aprompt, 638041394f3SDevin Teske overall); 639041394f3SDevin Teske } else { 640041394f3SDevin Teske /* stdout, dialog(1), or Xdialog(1) */ 641041394f3SDevin Teske dprompt_dprint(dialog_out, pprompt, 642041394f3SDevin Teske aprompt, overall); 643041394f3SDevin Teske fsync(dialog_out); 644041394f3SDevin Teske } 645041394f3SDevin Teske dialog_old_seconds = seconds; 646041394f3SDevin Teske dialog_old_nthfile = nthfile; 647041394f3SDevin Teske dialog_last_update = now.tv_usec; 648041394f3SDevin Teske } 649041394f3SDevin Teske 650041394f3SDevin Teske /* Update the status line */ 651041394f3SDevin Teske if ((use_libdialog && !debug) && 652041394f3SDevin Teske status_updates_per_second != 0 && 653041394f3SDevin Teske ( 654041394f3SDevin Teske keep_going != TRUE || 655041394f3SDevin Teske seconds != status_old_seconds || 656041394f3SDevin Teske now.tv_usec - status_last_update >= 657041394f3SDevin Teske status_update_usec || 658041394f3SDevin Teske nthfile != status_old_nthfile 659041394f3SDevin Teske ) 660041394f3SDevin Teske ) { 661041394f3SDevin Teske status_printf(status_fmt, dpv_overall_read, 662041394f3SDevin Teske (dpv_overall_read / (seconds == 0 ? 1 : 663041394f3SDevin Teske seconds) * 1.0), 664041394f3SDevin Teske 1, /* XXX until we add parallelism XXX */ 665041394f3SDevin Teske files_left); 666041394f3SDevin Teske status_old_seconds = seconds; 667041394f3SDevin Teske status_old_nthfile = nthfile; 668041394f3SDevin Teske status_last_update = now.tv_usec; 669041394f3SDevin Teske } 670041394f3SDevin Teske } 671041394f3SDevin Teske 672041394f3SDevin Teske if (!dialog_test && output_out >= 0) { 673041394f3SDevin Teske close(output_out); 674041394f3SDevin Teske waitpid(output_pid, (int *)NULL, 0); 675041394f3SDevin Teske } 676041394f3SDevin Teske 677041394f3SDevin Teske if (dpv_abort) 678041394f3SDevin Teske break; 679041394f3SDevin Teske 680041394f3SDevin Teske /* Advance head of list when we hit the max display lines */ 681041394f3SDevin Teske if (display_limit > 0 && nthfile % display_limit == 0) 682041394f3SDevin Teske list_head = curfile->next; 683041394f3SDevin Teske } 684041394f3SDevin Teske 685041394f3SDevin Teske if (!debug) { 686041394f3SDevin Teske if (use_libdialog) 687041394f3SDevin Teske end_dialog(); 688041394f3SDevin Teske else { 689041394f3SDevin Teske close(dialog_out); 690041394f3SDevin Teske waitpid(pid, (int *)NULL, 0); 691041394f3SDevin Teske } 692041394f3SDevin Teske if (!dpv_interrupt) 693041394f3SDevin Teske printf("\n"); 694041394f3SDevin Teske } else 695*324c11a1SDevin Teske warnx("%s: %lli overall read", __func__, dpv_overall_read); 696041394f3SDevin Teske 697041394f3SDevin Teske if (dpv_interrupt || dpv_abort) 698041394f3SDevin Teske return (-1); 699041394f3SDevin Teske else 700041394f3SDevin Teske return (0); 701041394f3SDevin Teske } 702041394f3SDevin Teske 703041394f3SDevin Teske /* 704041394f3SDevin Teske * Free allocated items initialized by dpv() 705041394f3SDevin Teske */ 706041394f3SDevin Teske void 707041394f3SDevin Teske dpv_free(void) 708041394f3SDevin Teske { 709041394f3SDevin Teske dialogrc_free(); 710041394f3SDevin Teske dprompt_free(); 711041394f3SDevin Teske dialog_maxsize_free(); 712041394f3SDevin Teske if (aprompt != NULL) { 713041394f3SDevin Teske free(aprompt); 714041394f3SDevin Teske aprompt = NULL; 715041394f3SDevin Teske } 716041394f3SDevin Teske if (pprompt != NULL) { 717041394f3SDevin Teske free(pprompt); 718041394f3SDevin Teske pprompt = NULL; 719041394f3SDevin Teske } 720041394f3SDevin Teske status_free(); 721041394f3SDevin Teske } 722