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