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/types.h> 31041394f3SDevin Teske 32041394f3SDevin Teske #define _BSD_SOURCE /* to get dprintf() prototype in stdio.h below */ 33041394f3SDevin Teske #include <dialog.h> 34041394f3SDevin Teske #include <err.h> 35041394f3SDevin Teske #include <libutil.h> 36041394f3SDevin Teske #include <stdarg.h> 37041394f3SDevin Teske #include <stdio.h> 38041394f3SDevin Teske #include <stdlib.h> 39041394f3SDevin Teske #include <string.h> 40041394f3SDevin Teske #include <string_m.h> 41041394f3SDevin Teske #include <unistd.h> 42041394f3SDevin Teske 43041394f3SDevin Teske #include "dialog_util.h" 44041394f3SDevin Teske #include "dialogrc.h" 45041394f3SDevin Teske #include "dprompt.h" 46041394f3SDevin Teske #include "dpv.h" 47041394f3SDevin Teske #include "dpv_private.h" 48041394f3SDevin Teske 49041394f3SDevin Teske #define FLABEL_MAX 1024 50041394f3SDevin Teske 51041394f3SDevin Teske static int fheight = 0; /* initialized by dprompt_init() */ 52041394f3SDevin Teske static char dprompt[PROMPT_MAX + 1] = ""; 53041394f3SDevin Teske static char *dprompt_pos = (char *)(0); /* treated numerically */ 54041394f3SDevin Teske 55041394f3SDevin Teske /* Display characteristics */ 56041394f3SDevin Teske #define FM_DONE 0x01 57041394f3SDevin Teske #define FM_FAIL 0x02 58041394f3SDevin Teske #define FM_PEND 0x04 59041394f3SDevin Teske static uint8_t dprompt_free_mask; 60041394f3SDevin Teske static char *done = NULL; 61041394f3SDevin Teske static char *fail = NULL; 62041394f3SDevin Teske static char *pend = NULL; 63041394f3SDevin Teske int display_limit = DISPLAY_LIMIT_DEFAULT; /* Max entries to show */ 64041394f3SDevin Teske int label_size = LABEL_SIZE_DEFAULT; /* Max width for labels */ 65041394f3SDevin Teske int pbar_size = PBAR_SIZE_DEFAULT; /* Mini-progressbar size */ 66041394f3SDevin Teske static int gauge_percent = 0; 67041394f3SDevin Teske static int done_size, done_lsize, done_rsize; 68041394f3SDevin Teske static int fail_size, fail_lsize, fail_rsize; 69041394f3SDevin Teske static int mesg_size, mesg_lsize, mesg_rsize; 70041394f3SDevin Teske static int pend_size, pend_lsize, pend_rsize; 71041394f3SDevin Teske static int pct_lsize, pct_rsize; 72041394f3SDevin Teske static void *gauge = NULL; 73041394f3SDevin Teske #define SPIN_SIZE 4 74041394f3SDevin Teske static char spin[SPIN_SIZE + 1] = "/-\\|"; 75041394f3SDevin Teske static char msg[PROMPT_MAX + 1]; 76041394f3SDevin Teske static char *spin_cp = spin; 77041394f3SDevin Teske 78041394f3SDevin Teske /* Function prototypes */ 79041394f3SDevin Teske static char spin_char(void); 80041394f3SDevin Teske static int dprompt_add_files(struct dpv_file_node *file_list, 81041394f3SDevin Teske struct dpv_file_node *curfile, int pct); 82041394f3SDevin Teske 83041394f3SDevin Teske /* 84041394f3SDevin Teske * Returns a pointer to the current spin character in the spin string and 85041394f3SDevin Teske * advances the global position to the next character for the next call. 86041394f3SDevin Teske */ 87041394f3SDevin Teske static char 88041394f3SDevin Teske spin_char(void) 89041394f3SDevin Teske { 90041394f3SDevin Teske char ch; 91041394f3SDevin Teske 92*a0d7de3bSDevin Teske if (*spin_cp == '\0') 93041394f3SDevin Teske spin_cp = spin; 94041394f3SDevin Teske ch = *spin_cp; 95041394f3SDevin Teske 96041394f3SDevin Teske /* Advance the spinner to the next char */ 97041394f3SDevin Teske if (++spin_cp >= (spin + SPIN_SIZE)) 98041394f3SDevin Teske spin_cp = spin; 99041394f3SDevin Teske 100041394f3SDevin Teske return (ch); 101041394f3SDevin Teske } 102041394f3SDevin Teske 103041394f3SDevin Teske /* 104041394f3SDevin Teske * Initialize heights and widths based on various strings and environment 105041394f3SDevin Teske * variables (such as ENV_USE_COLOR). 106041394f3SDevin Teske */ 107041394f3SDevin Teske void 108041394f3SDevin Teske dprompt_init(struct dpv_file_node *file_list) 109041394f3SDevin Teske { 110041394f3SDevin Teske uint8_t nls = 0; 111041394f3SDevin Teske int len; 112041394f3SDevin Teske int max_cols; 113041394f3SDevin Teske int max_rows; 114041394f3SDevin Teske int nthfile; 115041394f3SDevin Teske int numlines; 116041394f3SDevin Teske struct dpv_file_node *curfile; 117041394f3SDevin Teske 118041394f3SDevin Teske /* 119041394f3SDevin Teske * Initialize dialog(3) `colors' support and draw backtitle 120041394f3SDevin Teske */ 121041394f3SDevin Teske if (use_libdialog && !debug) { 122041394f3SDevin Teske init_dialog(stdin, stdout); 123041394f3SDevin Teske dialog_vars.colors = 1; 124041394f3SDevin Teske if (backtitle != NULL) { 125041394f3SDevin Teske dialog_vars.backtitle = (char *)backtitle; 126041394f3SDevin Teske dlg_put_backtitle(); 127041394f3SDevin Teske } 128041394f3SDevin Teske } 129041394f3SDevin Teske 130041394f3SDevin Teske /* Calculate width of dialog(3) or [X]dialog(1) --gauge box */ 131041394f3SDevin Teske dwidth = label_size + pbar_size + 9; 132041394f3SDevin Teske 133041394f3SDevin Teske /* 134041394f3SDevin Teske * Calculate height of dialog(3) or [X]dialog(1) --gauge box 135041394f3SDevin Teske */ 136041394f3SDevin Teske dheight = 5; 137041394f3SDevin Teske max_rows = dialog_maxrows(); 138041394f3SDevin Teske /* adjust max_rows for backtitle and/or dialog(3) statusLine */ 139041394f3SDevin Teske if (backtitle != NULL) 140041394f3SDevin Teske max_rows -= use_shadow ? 3 : 2; 141041394f3SDevin Teske if (use_libdialog && use_shadow) 142041394f3SDevin Teske max_rows -= 2; 143041394f3SDevin Teske /* add lines for `-p text' */ 144041394f3SDevin Teske numlines = dialog_prompt_numlines(pprompt, 0); 145041394f3SDevin Teske if (debug) 146041394f3SDevin Teske warnx("`-p text' is %i line%s long", numlines, 147041394f3SDevin Teske numlines == 1 ? "" : "s"); 148041394f3SDevin Teske dheight += numlines; 149041394f3SDevin Teske /* adjust dheight for various implementations */ 150041394f3SDevin Teske if (use_dialog) { 151041394f3SDevin Teske dheight -= dialog_prompt_nlstate(pprompt); 152041394f3SDevin Teske nls = dialog_prompt_nlstate(pprompt); 153041394f3SDevin Teske } else if (use_xdialog) { 154041394f3SDevin Teske if (pprompt == NULL || *pprompt == '\0') 155041394f3SDevin Teske dheight++; 156041394f3SDevin Teske } else if (use_libdialog) { 157041394f3SDevin Teske if (pprompt != NULL && *pprompt != '\0') 158041394f3SDevin Teske dheight--; 159041394f3SDevin Teske } 160041394f3SDevin Teske /* limit the number of display items (necessary per dialog(1,3)) */ 161041394f3SDevin Teske if (display_limit == 0 || display_limit > DPV_DISPLAY_LIMIT) 162041394f3SDevin Teske display_limit = DPV_DISPLAY_LIMIT; 163041394f3SDevin Teske /* verify fheight will fit (stop if we hit 1) */ 164041394f3SDevin Teske for (; display_limit > 0; display_limit--) { 165041394f3SDevin Teske nthfile = numlines = 0; 166041394f3SDevin Teske fheight = (int)dpv_nfiles > display_limit ? 167041394f3SDevin Teske (unsigned int)display_limit : dpv_nfiles; 168041394f3SDevin Teske for (curfile = file_list; curfile != NULL; 169041394f3SDevin Teske curfile = curfile->next) { 170041394f3SDevin Teske nthfile++; 171041394f3SDevin Teske numlines += dialog_prompt_numlines(curfile->name, nls); 172041394f3SDevin Teske if ((nthfile % display_limit) == 0) { 173041394f3SDevin Teske if (numlines > fheight) 174041394f3SDevin Teske fheight = numlines; 175041394f3SDevin Teske numlines = nthfile = 0; 176041394f3SDevin Teske } 177041394f3SDevin Teske } 178041394f3SDevin Teske if (numlines > fheight) 179041394f3SDevin Teske fheight = numlines; 180041394f3SDevin Teske if ((dheight + fheight + 181041394f3SDevin Teske (int)dialog_prompt_numlines(aprompt, use_dialog) - 182041394f3SDevin Teske (use_dialog ? (int)dialog_prompt_nlstate(aprompt) : 0)) 183041394f3SDevin Teske <= max_rows) 184041394f3SDevin Teske break; 185041394f3SDevin Teske } 186041394f3SDevin Teske /* don't show any items if we run the risk of hitting a blank set */ 187041394f3SDevin Teske if ((max_rows - (use_shadow ? 5 : 4)) >= fheight) 188041394f3SDevin Teske dheight += fheight; 189041394f3SDevin Teske else 190041394f3SDevin Teske fheight = 0; 191041394f3SDevin Teske /* add lines for `-a text' */ 192041394f3SDevin Teske numlines = dialog_prompt_numlines(aprompt, use_dialog); 193041394f3SDevin Teske if (debug) 194041394f3SDevin Teske warnx("`-a text' is %i line%s long", numlines, 195041394f3SDevin Teske numlines == 1 ? "" : "s"); 196041394f3SDevin Teske dheight += numlines; 197041394f3SDevin Teske 198041394f3SDevin Teske /* If using Xdialog(1), adjust accordingly (based on testing) */ 199041394f3SDevin Teske if (use_xdialog) 200041394f3SDevin Teske dheight += dheight / 4; 201041394f3SDevin Teske 202041394f3SDevin Teske /* For wide mode, long prefix (`pprompt') or append (`aprompt') 203041394f3SDevin Teske * strings will bump width */ 204041394f3SDevin Teske if (wide) { 205041394f3SDevin Teske len = (int)dialog_prompt_longestline(pprompt, 0); /* !nls */ 206041394f3SDevin Teske if ((len + 4) > dwidth) 207041394f3SDevin Teske dwidth = len + 4; 208041394f3SDevin Teske len = (int)dialog_prompt_longestline(aprompt, 1); /* nls */ 209041394f3SDevin Teske if ((len + 4) > dwidth) 210041394f3SDevin Teske dwidth = len + 4; 211041394f3SDevin Teske } 212041394f3SDevin Teske 213041394f3SDevin Teske /* Enforce width constraints to maximum values */ 214041394f3SDevin Teske max_cols = dialog_maxcols(); 215041394f3SDevin Teske if (max_cols > 0 && dwidth > max_cols) 216041394f3SDevin Teske dwidth = max_cols; 217041394f3SDevin Teske 218041394f3SDevin Teske /* Optimize widths to sane values*/ 219041394f3SDevin Teske if (pbar_size > dwidth - 9) { 220041394f3SDevin Teske pbar_size = dwidth - 9; 221041394f3SDevin Teske label_size = 0; 222041394f3SDevin Teske /* -9 = "| - [" ... "] |" */ 223041394f3SDevin Teske } 224041394f3SDevin Teske if (pbar_size < 0) 225041394f3SDevin Teske label_size = dwidth - 8; 226041394f3SDevin Teske /* -8 = "| " ... " - |" */ 227041394f3SDevin Teske else if (label_size > (dwidth - pbar_size - 9) || wide) 228041394f3SDevin Teske label_size = no_labels ? 0 : dwidth - pbar_size - 9; 229041394f3SDevin Teske /* -9 = "| " ... " - [" ... "] |" */ 230041394f3SDevin Teske 231041394f3SDevin Teske /* Hide labels if requested */ 232041394f3SDevin Teske if (no_labels) 233041394f3SDevin Teske label_size = 0; 234041394f3SDevin Teske 235041394f3SDevin Teske /* Touch up the height (now that we know dwidth) */ 236041394f3SDevin Teske dheight += dialog_prompt_wrappedlines(pprompt, dwidth - 4, 0); 237041394f3SDevin Teske dheight += dialog_prompt_wrappedlines(aprompt, dwidth - 4, 1); 238041394f3SDevin Teske 239041394f3SDevin Teske if (debug) 240041394f3SDevin Teske warnx("dheight = %i dwidth = %i fheight = %i", 241041394f3SDevin Teske dheight, dwidth, fheight); 242041394f3SDevin Teske 243041394f3SDevin Teske /* Calculate left/right portions of % */ 244041394f3SDevin Teske pct_lsize = (pbar_size - 4) / 2; /* -4 == printf("%-3s%%", pct) */ 245041394f3SDevin Teske pct_rsize = pct_lsize; 246041394f3SDevin Teske /* If not evenly divisible by 2, increment the right-side */ 247041394f3SDevin Teske if ((pct_rsize + pct_rsize + 4) != pbar_size) 248041394f3SDevin Teske pct_rsize++; 249041394f3SDevin Teske 250041394f3SDevin Teske /* Initialize "Done" text */ 251041394f3SDevin Teske if (done == NULL && (done = msg_done) == NULL) { 252041394f3SDevin Teske if ((done = getenv(ENV_MSG_DONE)) != NULL) 253041394f3SDevin Teske done_size = strlen(done); 254041394f3SDevin Teske else { 255041394f3SDevin Teske done_size = strlen(DPV_DONE_DEFAULT); 256041394f3SDevin Teske if ((done = malloc(done_size + 1)) == NULL) 257041394f3SDevin Teske errx(EXIT_FAILURE, "Out of memory?!"); 258041394f3SDevin Teske dprompt_free_mask |= FM_DONE; 259041394f3SDevin Teske snprintf(done, done_size + 1, DPV_DONE_DEFAULT); 260041394f3SDevin Teske } 261041394f3SDevin Teske } 262041394f3SDevin Teske if (pbar_size < done_size) { 263041394f3SDevin Teske done_lsize = done_rsize = 0; 264041394f3SDevin Teske *(done + pbar_size) = '\0'; 265041394f3SDevin Teske done_size = pbar_size; 266041394f3SDevin Teske } else { 267041394f3SDevin Teske /* Calculate left/right portions for mini-progressbar */ 268041394f3SDevin Teske done_lsize = (pbar_size - done_size) / 2; 269041394f3SDevin Teske done_rsize = done_lsize; 270041394f3SDevin Teske /* If not evenly divisible by 2, increment the right-side */ 271041394f3SDevin Teske if ((done_rsize + done_size + done_lsize) != pbar_size) 272041394f3SDevin Teske done_rsize++; 273041394f3SDevin Teske } 274041394f3SDevin Teske 275041394f3SDevin Teske /* Initialize "Fail" text */ 276041394f3SDevin Teske if (fail == NULL && (fail = msg_fail) == NULL) { 277041394f3SDevin Teske if ((fail = getenv(ENV_MSG_FAIL)) != NULL) 278041394f3SDevin Teske fail_size = strlen(fail); 279041394f3SDevin Teske else { 280041394f3SDevin Teske fail_size = strlen(DPV_FAIL_DEFAULT); 281041394f3SDevin Teske if ((fail = malloc(fail_size + 1)) == NULL) 282041394f3SDevin Teske errx(EXIT_FAILURE, "Out of memory?!"); 283041394f3SDevin Teske dprompt_free_mask |= FM_FAIL; 284041394f3SDevin Teske snprintf(fail, fail_size + 1, DPV_FAIL_DEFAULT); 285041394f3SDevin Teske } 286041394f3SDevin Teske } 287041394f3SDevin Teske if (pbar_size < fail_size) { 288041394f3SDevin Teske fail_lsize = fail_rsize = 0; 289041394f3SDevin Teske *(fail + pbar_size) = '\0'; 290041394f3SDevin Teske fail_size = pbar_size; 291041394f3SDevin Teske } else { 292041394f3SDevin Teske /* Calculate left/right portions for mini-progressbar */ 293041394f3SDevin Teske fail_lsize = (pbar_size - fail_size) / 2; 294041394f3SDevin Teske fail_rsize = fail_lsize; 295041394f3SDevin Teske /* If not evenly divisible by 2, increment the right-side */ 296041394f3SDevin Teske if ((fail_rsize + fail_size + fail_lsize) != pbar_size) 297041394f3SDevin Teske fail_rsize++; 298041394f3SDevin Teske } 299041394f3SDevin Teske 300041394f3SDevin Teske /* Initialize "Pending" text */ 301041394f3SDevin Teske if (pend == NULL && (pend = msg_pending) == NULL) { 302041394f3SDevin Teske if ((pend = getenv(ENV_MSG_PENDING)) != NULL) 303041394f3SDevin Teske pend_size = strlen(pend); 304041394f3SDevin Teske else { 305041394f3SDevin Teske pend_size = strlen(DPV_PENDING_DEFAULT); 306041394f3SDevin Teske if ((pend = malloc(pend_size + 1)) == NULL) 307041394f3SDevin Teske errx(EXIT_FAILURE, "Out of memory?!"); 308041394f3SDevin Teske dprompt_free_mask |= FM_PEND; 309041394f3SDevin Teske snprintf(pend, pend_size + 1, DPV_PENDING_DEFAULT); 310041394f3SDevin Teske } 311041394f3SDevin Teske } 312041394f3SDevin Teske if (pbar_size < pend_size) { 313041394f3SDevin Teske pend_lsize = pend_rsize = 0; 314041394f3SDevin Teske *(pend + pbar_size) = '\0'; 315041394f3SDevin Teske pend_size = pbar_size; 316041394f3SDevin Teske } else { 317041394f3SDevin Teske /* Calculate left/right portions for mini-progressbar */ 318041394f3SDevin Teske pend_lsize = (pbar_size - pend_size) / 2; 319041394f3SDevin Teske pend_rsize = pend_lsize; 320041394f3SDevin Teske /* If not evenly divisible by 2, increment the right-side */ 321041394f3SDevin Teske if ((pend_rsize + pend_lsize + pend_size) != pbar_size) 322041394f3SDevin Teske pend_rsize++; 323041394f3SDevin Teske } 324041394f3SDevin Teske 325041394f3SDevin Teske if (debug) 326041394f3SDevin Teske warnx("label_size = %i pbar_size = %i", label_size, pbar_size); 327041394f3SDevin Teske 328041394f3SDevin Teske dprompt_clear(); 329041394f3SDevin Teske } 330041394f3SDevin Teske 331041394f3SDevin Teske /* 332041394f3SDevin Teske * Clear the [X]dialog(1) `--gauge' prompt buffer. 333041394f3SDevin Teske */ 334041394f3SDevin Teske void 335041394f3SDevin Teske dprompt_clear(void) 336041394f3SDevin Teske { 337041394f3SDevin Teske 338041394f3SDevin Teske *dprompt = '\0'; 339041394f3SDevin Teske dprompt_pos = dprompt; 340041394f3SDevin Teske } 341041394f3SDevin Teske 342041394f3SDevin Teske /* 343041394f3SDevin Teske * Append to the [X]dialog(1) `--gauge' prompt buffer. Syntax is like printf(3) 344041394f3SDevin Teske * and returns the number of bytes appended to the buffer. 345041394f3SDevin Teske */ 346041394f3SDevin Teske int 347041394f3SDevin Teske dprompt_add(const char *format, ...) 348041394f3SDevin Teske { 349041394f3SDevin Teske int len; 350041394f3SDevin Teske va_list ap; 351041394f3SDevin Teske 352041394f3SDevin Teske if (dprompt_pos >= (dprompt + PROMPT_MAX)) 353041394f3SDevin Teske return (0); 354041394f3SDevin Teske 355041394f3SDevin Teske va_start(ap, format); 356041394f3SDevin Teske len = vsnprintf(dprompt_pos, (size_t)(PROMPT_MAX - 357041394f3SDevin Teske (dprompt_pos - dprompt)), format, ap); 358041394f3SDevin Teske va_end(ap); 359041394f3SDevin Teske if (len == -1) 360041394f3SDevin Teske errx(EXIT_FAILURE, "%s: Oops, dprompt buffer overflow", 361041394f3SDevin Teske __func__); 362041394f3SDevin Teske 363041394f3SDevin Teske if ((dprompt_pos + len) < (dprompt + PROMPT_MAX)) 364041394f3SDevin Teske dprompt_pos += len; 365041394f3SDevin Teske else 366041394f3SDevin Teske dprompt_pos = dprompt + PROMPT_MAX; 367041394f3SDevin Teske 368041394f3SDevin Teske return (len); 369041394f3SDevin Teske } 370041394f3SDevin Teske 371041394f3SDevin Teske /* 372041394f3SDevin Teske * Append active files to the [X]dialog(1) `--gauge' prompt buffer. Syntax 373041394f3SDevin Teske * requires a pointer to the head of the dpv_file_node linked-list. Returns the 374041394f3SDevin Teske * number of files processed successfully. 375041394f3SDevin Teske */ 376041394f3SDevin Teske static int 377041394f3SDevin Teske dprompt_add_files(struct dpv_file_node *file_list, 378041394f3SDevin Teske struct dpv_file_node *curfile, int pct) 379041394f3SDevin Teske { 380041394f3SDevin Teske char c; 381041394f3SDevin Teske char bold_code = 'b'; /* default: enabled */ 382041394f3SDevin Teske char color_code = '4'; /* default: blue */ 383041394f3SDevin Teske uint8_t after_curfile = curfile != NULL ? FALSE : TRUE; 384041394f3SDevin Teske uint8_t nls = 0; 385041394f3SDevin Teske char *cp; 386041394f3SDevin Teske char *lastline; 387041394f3SDevin Teske char *name; 388041394f3SDevin Teske const char *bg_code; 389041394f3SDevin Teske const char *estext; 390041394f3SDevin Teske const char *format; 391041394f3SDevin Teske enum dprompt_state dstate; 392041394f3SDevin Teske int estext_lsize; 393041394f3SDevin Teske int estext_rsize; 394041394f3SDevin Teske int flabel_size; 395041394f3SDevin Teske int hlen; 396041394f3SDevin Teske int lsize; 397041394f3SDevin Teske int nlines = 0; 398041394f3SDevin Teske int nthfile = 0; 399041394f3SDevin Teske int pwidth; 400041394f3SDevin Teske int rsize; 401041394f3SDevin Teske struct dpv_file_node *fp; 402041394f3SDevin Teske char flabel[FLABEL_MAX + 1]; 403041394f3SDevin Teske char human[32]; 404041394f3SDevin Teske char pbar[pbar_size + 16]; /* +15 for optional color */ 405041394f3SDevin Teske char pbar_cap[sizeof(pbar)]; 406041394f3SDevin Teske char pbar_fill[sizeof(pbar)]; 407041394f3SDevin Teske 408041394f3SDevin Teske 409041394f3SDevin Teske /* Override color defaults with that of main progress bar */ 410041394f3SDevin Teske if (use_colors || use_shadow) { /* NB: shadow enables color */ 411041394f3SDevin Teske color_code = gauge_color[0]; 412041394f3SDevin Teske /* NB: str[1] aka bg is unused */ 413041394f3SDevin Teske bold_code = gauge_color[2]; 414041394f3SDevin Teske } 415041394f3SDevin Teske 416041394f3SDevin Teske /* 417041394f3SDevin Teske * Create mini-progressbar for current file (if applicable) 418041394f3SDevin Teske */ 419041394f3SDevin Teske *pbar = '\0'; 420041394f3SDevin Teske if (pbar_size >= 0 && pct >= 0 && curfile != NULL && 421041394f3SDevin Teske (curfile->length >= 0 || dialog_test)) { 422041394f3SDevin Teske snprintf(pbar, pbar_size + 1, "%*s%3u%%%*s", pct_lsize, "", 423041394f3SDevin Teske pct, pct_rsize, ""); 424041394f3SDevin Teske if (use_color) { 425041394f3SDevin Teske /* Calculate the fill-width of progressbar */ 426041394f3SDevin Teske pwidth = pct * pbar_size / 100; 427041394f3SDevin Teske /* Round up based on one-tenth of a percent */ 428041394f3SDevin Teske if ((pct * pbar_size % 100) > 50) 429041394f3SDevin Teske pwidth++; 430041394f3SDevin Teske 431041394f3SDevin Teske /* 432041394f3SDevin Teske * Make two copies of pbar. Make one represent the fill 433041394f3SDevin Teske * and the other the remainder (cap). We'll insert the 434041394f3SDevin Teske * ANSI delimiter in between. 435041394f3SDevin Teske */ 436041394f3SDevin Teske *pbar_fill = '\0'; 437041394f3SDevin Teske *pbar_cap = '\0'; 438041394f3SDevin Teske strncat(pbar_fill, (const char *)(pbar), dwidth); 439041394f3SDevin Teske *(pbar_fill + pwidth) = '\0'; 440041394f3SDevin Teske strncat(pbar_cap, (const char *)(pbar+pwidth), dwidth); 441041394f3SDevin Teske 442041394f3SDevin Teske /* Finalize the mini [color] progressbar */ 443041394f3SDevin Teske snprintf(pbar, sizeof(pbar), 444041394f3SDevin Teske "\\Z%c\\Zr\\Z%c%s%s%s\\Zn", bold_code, color_code, 445041394f3SDevin Teske pbar_fill, "\\ZR", pbar_cap); 446041394f3SDevin Teske } 447041394f3SDevin Teske } 448041394f3SDevin Teske 449041394f3SDevin Teske for (fp = file_list; fp != NULL; fp = fp->next) { 450041394f3SDevin Teske flabel_size = label_size; 451041394f3SDevin Teske name = fp->name; 452041394f3SDevin Teske nthfile++; 453041394f3SDevin Teske 454041394f3SDevin Teske /* 455041394f3SDevin Teske * Support multiline filenames (where the filename is taken as 456041394f3SDevin Teske * the last line and the text leading up to the last line can 457041394f3SDevin Teske * be used as (for example) a heading/separator between files. 458041394f3SDevin Teske */ 459041394f3SDevin Teske if (use_dialog) 460041394f3SDevin Teske nls = dialog_prompt_nlstate(pprompt); 461041394f3SDevin Teske nlines += dialog_prompt_numlines(name, nls); 462041394f3SDevin Teske lastline = dialog_prompt_lastline(name, 1); 463041394f3SDevin Teske if (name != lastline) { 464041394f3SDevin Teske c = *lastline; 465041394f3SDevin Teske *lastline = '\0'; 466041394f3SDevin Teske dprompt_add("%s", name); 467041394f3SDevin Teske *lastline = c; 468041394f3SDevin Teske name = lastline; 469041394f3SDevin Teske } 470041394f3SDevin Teske 471041394f3SDevin Teske /* Support color codes (for dialog(1,3)) in file names */ 472041394f3SDevin Teske if ((use_dialog || use_libdialog) && use_color) { 473041394f3SDevin Teske cp = name; 474041394f3SDevin Teske while (*cp != '\0') { 475041394f3SDevin Teske if (*cp == '\\' && *(cp + 1) != '\0' && 476041394f3SDevin Teske *(++cp) == 'Z' && *(cp + 1) != '\0') { 477041394f3SDevin Teske cp++; 478041394f3SDevin Teske flabel_size += 3; 479041394f3SDevin Teske } 480041394f3SDevin Teske cp++; 481041394f3SDevin Teske } 482041394f3SDevin Teske if (flabel_size > FLABEL_MAX) 483041394f3SDevin Teske flabel_size = FLABEL_MAX; 484041394f3SDevin Teske } 485041394f3SDevin Teske 486041394f3SDevin Teske /* If no mini-progressbar, increase label width */ 487041394f3SDevin Teske if (pbar_size < 0 && flabel_size <= FLABEL_MAX - 2 && 488041394f3SDevin Teske no_labels == FALSE) 489041394f3SDevin Teske flabel_size += 2; 490041394f3SDevin Teske 491041394f3SDevin Teske /* If name is too long, add an ellipsis */ 492041394f3SDevin Teske if (snprintf(flabel, flabel_size + 1, "%s", name) > 493041394f3SDevin Teske flabel_size) sprintf(flabel + flabel_size - 3, "..."); 494041394f3SDevin Teske 495041394f3SDevin Teske /* 496041394f3SDevin Teske * Append the label (processing the current file differently) 497041394f3SDevin Teske */ 498041394f3SDevin Teske if (fp == curfile && pct < 100) { 499041394f3SDevin Teske /* 500041394f3SDevin Teske * Add an ellipsis to current file name if it will fit. 501041394f3SDevin Teske * There may be an ellipsis already from truncating the 502041394f3SDevin Teske * label (in which case, we already have one). 503041394f3SDevin Teske */ 504041394f3SDevin Teske cp = flabel + strlen(flabel); 505041394f3SDevin Teske if (cp < (flabel + flabel_size)) 506041394f3SDevin Teske snprintf(cp, flabel_size - 507041394f3SDevin Teske (cp - flabel) + 1, "..."); 508041394f3SDevin Teske 509041394f3SDevin Teske /* Append label (with spinner and optional color) */ 510041394f3SDevin Teske dprompt_add("%s%-*s%s %c", use_color ? "\\Zb" : "", 511041394f3SDevin Teske flabel_size, flabel, use_color ? "\\Zn" : "", 512041394f3SDevin Teske spin_char()); 513041394f3SDevin Teske } else 514041394f3SDevin Teske dprompt_add("%-*s%s %s", flabel_size, 515041394f3SDevin Teske flabel, use_color ? "\\Zn" : "", " "); 516041394f3SDevin Teske 517041394f3SDevin Teske /* 518041394f3SDevin Teske * Append pbar/status (processing the current file differently) 519041394f3SDevin Teske */ 520041394f3SDevin Teske dstate = DPROMPT_NONE; 521041394f3SDevin Teske if (fp->msg != NULL) 522041394f3SDevin Teske dstate = DPROMPT_CUSTOM_MSG; 523041394f3SDevin Teske else if (pbar_size < 0) 524041394f3SDevin Teske dstate = DPROMPT_NONE; 525041394f3SDevin Teske else if (pbar_size < 4) 526041394f3SDevin Teske dstate = DPROMPT_MINIMAL; 527041394f3SDevin Teske else if (after_curfile) 528041394f3SDevin Teske dstate = DPROMPT_PENDING; 529041394f3SDevin Teske else if (fp == curfile) { 530041394f3SDevin Teske if (*pbar == '\0') { 531041394f3SDevin Teske if (fp->length < 0) 532041394f3SDevin Teske dstate = DPROMPT_DETAILS; 533041394f3SDevin Teske else if (fp->status == DPV_STATUS_RUNNING) 534041394f3SDevin Teske dstate = DPROMPT_DETAILS; 535041394f3SDevin Teske else 536041394f3SDevin Teske dstate = DPROMPT_END_STATE; 537041394f3SDevin Teske } 538041394f3SDevin Teske else if (dialog_test) /* status/length ignored */ 539041394f3SDevin Teske dstate = pct < 100 ? 540041394f3SDevin Teske DPROMPT_PBAR : DPROMPT_END_STATE; 541041394f3SDevin Teske else if (fp->status == DPV_STATUS_RUNNING) 542041394f3SDevin Teske dstate = fp->length < 0 ? 543041394f3SDevin Teske DPROMPT_DETAILS : DPROMPT_PBAR; 544041394f3SDevin Teske else /* not running */ 545041394f3SDevin Teske dstate = fp->length < 0 ? 546041394f3SDevin Teske DPROMPT_DETAILS : DPROMPT_END_STATE; 547041394f3SDevin Teske } else { /* before curfile */ 548041394f3SDevin Teske if (dialog_test) 549041394f3SDevin Teske dstate = DPROMPT_END_STATE; 550041394f3SDevin Teske else 551041394f3SDevin Teske dstate = fp->length < 0 ? 552041394f3SDevin Teske DPROMPT_DETAILS : DPROMPT_END_STATE; 553041394f3SDevin Teske } 554041394f3SDevin Teske format = use_color ? 555041394f3SDevin Teske " [\\Z%c%s%-*s%s%-*s\\Zn]\\n" : 556041394f3SDevin Teske " [%-*s%s%-*s]\\n"; 557041394f3SDevin Teske if (fp->status == DPV_STATUS_FAILED) { 558041394f3SDevin Teske bg_code = "\\Zr\\Z1"; /* Red */ 559041394f3SDevin Teske estext_lsize = fail_lsize; 560041394f3SDevin Teske estext_rsize = fail_rsize; 561041394f3SDevin Teske estext = fail; 562041394f3SDevin Teske } else { /* e.g., DPV_STATUS_DONE */ 563041394f3SDevin Teske bg_code = "\\Zr\\Z2"; /* Green */ 564041394f3SDevin Teske estext_lsize = done_lsize; 565041394f3SDevin Teske estext_rsize = done_rsize; 566041394f3SDevin Teske estext = done; 567041394f3SDevin Teske } 568041394f3SDevin Teske switch (dstate) { 569041394f3SDevin Teske case DPROMPT_PENDING: /* Future file(s) */ 570041394f3SDevin Teske dprompt_add(" [%-*s%s%-*s]\\n", 571041394f3SDevin Teske pend_lsize, "", pend, pend_rsize, ""); 572041394f3SDevin Teske break; 573041394f3SDevin Teske case DPROMPT_PBAR: /* Current file */ 574041394f3SDevin Teske dprompt_add(" [%s]\\n", pbar); 575041394f3SDevin Teske break; 576041394f3SDevin Teske case DPROMPT_END_STATE: /* Past/Current file(s) */ 577041394f3SDevin Teske if (use_color) 578041394f3SDevin Teske dprompt_add(format, bold_code, bg_code, 579041394f3SDevin Teske estext_lsize, "", estext, 580041394f3SDevin Teske estext_rsize, ""); 581041394f3SDevin Teske else 582041394f3SDevin Teske dprompt_add(format, 583041394f3SDevin Teske estext_lsize, "", estext, 584041394f3SDevin Teske estext_rsize, ""); 585041394f3SDevin Teske break; 586041394f3SDevin Teske case DPROMPT_DETAILS: /* Past/Current file(s) */ 587041394f3SDevin Teske humanize_number(human, pbar_size + 2, fp->read, "", 588041394f3SDevin Teske HN_AUTOSCALE, HN_NOSPACE | HN_DIVISOR_1000); 589041394f3SDevin Teske 590041394f3SDevin Teske /* Calculate center alignment */ 591041394f3SDevin Teske hlen = (int)strlen(human); 592041394f3SDevin Teske lsize = (pbar_size - hlen) / 2; 593041394f3SDevin Teske rsize = lsize; 594041394f3SDevin Teske if ((lsize+hlen+rsize) != pbar_size) 595041394f3SDevin Teske rsize++; 596041394f3SDevin Teske 597041394f3SDevin Teske if (use_color) 598041394f3SDevin Teske dprompt_add(format, bold_code, bg_code, 599041394f3SDevin Teske lsize, "", human, rsize, ""); 600041394f3SDevin Teske else 601041394f3SDevin Teske dprompt_add(format, 602041394f3SDevin Teske lsize, "", human, rsize, ""); 603041394f3SDevin Teske break; 604041394f3SDevin Teske case DPROMPT_CUSTOM_MSG: /* File-specific message override */ 605041394f3SDevin Teske snprintf(msg, PROMPT_MAX + 1, "%s", fp->msg); 606041394f3SDevin Teske if (pbar_size < (mesg_size = strlen(msg))) { 607041394f3SDevin Teske mesg_lsize = mesg_rsize = 0; 608041394f3SDevin Teske *(msg + pbar_size) = '\0'; 609041394f3SDevin Teske mesg_size = pbar_size; 610041394f3SDevin Teske } else { 611041394f3SDevin Teske mesg_lsize = (pbar_size - mesg_size) / 2; 612041394f3SDevin Teske mesg_rsize = mesg_lsize; 613041394f3SDevin Teske if ((mesg_rsize + mesg_size + mesg_lsize) 614041394f3SDevin Teske != pbar_size) 615041394f3SDevin Teske mesg_rsize++; 616041394f3SDevin Teske } 617041394f3SDevin Teske if (use_color) 618041394f3SDevin Teske dprompt_add(format, bold_code, bg_code, 619041394f3SDevin Teske mesg_lsize, "", msg, mesg_rsize, ""); 620041394f3SDevin Teske else 621041394f3SDevin Teske dprompt_add(format, mesg_lsize, "", msg, 622041394f3SDevin Teske mesg_rsize, ""); 623041394f3SDevin Teske break; 624041394f3SDevin Teske case DPROMPT_MINIMAL: /* Short progress bar, minimal room */ 625041394f3SDevin Teske if (use_color) 626041394f3SDevin Teske dprompt_add(format, bold_code, bg_code, 627041394f3SDevin Teske pbar_size, "", "", 0, ""); 628041394f3SDevin Teske else 629041394f3SDevin Teske dprompt_add(format, pbar_size, "", "", 0, ""); 630041394f3SDevin Teske break; 631041394f3SDevin Teske case DPROMPT_NONE: /* pbar_size < 0 */ 632041394f3SDevin Teske /* FALLTHROUGH */ 633041394f3SDevin Teske default: 634041394f3SDevin Teske dprompt_add(" \\n"); 635041394f3SDevin Teske /* 636041394f3SDevin Teske * NB: Leading space required for the case when 637041394f3SDevin Teske * spin_char() returns a single backslash [\] which 638041394f3SDevin Teske * without the space, changes the meaning of `\n' 639041394f3SDevin Teske */ 640041394f3SDevin Teske } 641041394f3SDevin Teske 642041394f3SDevin Teske /* Stop building if we've hit the internal limit */ 643041394f3SDevin Teske if (nthfile >= display_limit) 644041394f3SDevin Teske break; 645041394f3SDevin Teske 646041394f3SDevin Teske /* If this is the current file, all others are pending */ 647041394f3SDevin Teske if (fp == curfile) 648041394f3SDevin Teske after_curfile = TRUE; 649041394f3SDevin Teske } 650041394f3SDevin Teske 651041394f3SDevin Teske /* 652041394f3SDevin Teske * Since we cannot change the height/width of the [X]dialog(1) widget 653041394f3SDevin Teske * after spawn, to make things look nice let's pad the height so that 654041394f3SDevin Teske * the `-a text' always appears in the same spot. 655041394f3SDevin Teske * 656041394f3SDevin Teske * NOTE: fheight is calculated in dprompt_init(). It represents the 657041394f3SDevin Teske * maximum height required to display the set of items (broken up into 658041394f3SDevin Teske * pieces of display_limit chunks) whose names contain the most 659041394f3SDevin Teske * newlines for any given set. 660041394f3SDevin Teske */ 661041394f3SDevin Teske while (nlines < fheight) { 662041394f3SDevin Teske dprompt_add("\n"); 663041394f3SDevin Teske nlines++; 664041394f3SDevin Teske } 665041394f3SDevin Teske 666041394f3SDevin Teske return (nthfile); 667041394f3SDevin Teske } 668041394f3SDevin Teske 669041394f3SDevin Teske /* 670041394f3SDevin Teske * Process the dpv_file_node linked-list of named files, re-generating the 671041394f3SDevin Teske * [X]dialog(1) `--gauge' prompt text for the current state of transfers. 672041394f3SDevin Teske */ 673041394f3SDevin Teske void 674041394f3SDevin Teske dprompt_recreate(struct dpv_file_node *file_list, 675041394f3SDevin Teske struct dpv_file_node *curfile, int pct) 676041394f3SDevin Teske { 677041394f3SDevin Teske size_t len; 678041394f3SDevin Teske 679041394f3SDevin Teske /* 680041394f3SDevin Teske * Re-Build the prompt text 681041394f3SDevin Teske */ 682041394f3SDevin Teske dprompt_clear(); 683041394f3SDevin Teske if (display_limit > 0) 684041394f3SDevin Teske dprompt_add_files(file_list, curfile, pct); 685041394f3SDevin Teske 686041394f3SDevin Teske /* Xdialog(1) requires newlines (a) escaped and (b) in triplicate */ 687041394f3SDevin Teske if (use_xdialog) { 688041394f3SDevin Teske /* Replace `\n' with `\n\\n\n' in dprompt */ 689041394f3SDevin Teske len = strlen(dprompt); 690041394f3SDevin Teske len += strcount(dprompt, "\\n") * 5; /* +5 chars per count */ 691041394f3SDevin Teske if (len > PROMPT_MAX) 692041394f3SDevin Teske errx(EXIT_FAILURE, "%s: Oops, dprompt buffer overflow " 693041394f3SDevin Teske "(%zu > %i)", __func__, len, PROMPT_MAX); 694041394f3SDevin Teske if (replaceall(dprompt, "\\n", "\n\\n\n") < 0) 695041394f3SDevin Teske err(EXIT_FAILURE, "%s: replaceall()", __func__); 696041394f3SDevin Teske } 697041394f3SDevin Teske else if (use_libdialog) 698041394f3SDevin Teske strexpandnl(dprompt); 699041394f3SDevin Teske } 700041394f3SDevin Teske 701041394f3SDevin Teske /* 702041394f3SDevin Teske * Print the [X]dialog(1) `--gauge' prompt text to a buffer. 703041394f3SDevin Teske */ 704041394f3SDevin Teske int 705041394f3SDevin Teske dprompt_sprint(char * restrict str, const char *prefix, const char *append) 706041394f3SDevin Teske { 707041394f3SDevin Teske 708041394f3SDevin Teske return (snprintf(str, PROMPT_MAX, "%s%s%s%s", use_color ? "\\Zn" : "", 709041394f3SDevin Teske prefix ? prefix : "", dprompt, append ? append : "")); 710041394f3SDevin Teske } 711041394f3SDevin Teske 712041394f3SDevin Teske /* 713041394f3SDevin Teske * Print the [X]dialog(1) `--gauge' prompt text to file descriptor fd (could 714041394f3SDevin Teske * be STDOUT_FILENO or a pipe(2) file descriptor to actual [X]dialog(1)). 715041394f3SDevin Teske */ 716041394f3SDevin Teske void 717041394f3SDevin Teske dprompt_dprint(int fd, const char *prefix, const char *append, int overall) 718041394f3SDevin Teske { 719041394f3SDevin Teske int percent = gauge_percent; 720041394f3SDevin Teske 721041394f3SDevin Teske if (overall >= 0 && overall <= 100) 722041394f3SDevin Teske gauge_percent = percent = overall; 723041394f3SDevin Teske dprintf(fd, "XXX\n%s%s%s%s\nXXX\n%i\n", use_color ? "\\Zn" : "", 724041394f3SDevin Teske prefix ? prefix : "", dprompt, append ? append : "", percent); 725041394f3SDevin Teske fsync(fd); 726041394f3SDevin Teske } 727041394f3SDevin Teske 728041394f3SDevin Teske /* 729041394f3SDevin Teske * Print the dialog(3) `gauge' prompt text using libdialog. 730041394f3SDevin Teske */ 731041394f3SDevin Teske void 732041394f3SDevin Teske dprompt_libprint(const char *prefix, const char *append, int overall) 733041394f3SDevin Teske { 734041394f3SDevin Teske int percent = gauge_percent; 735041394f3SDevin Teske char buf[DPV_PPROMPT_MAX + DPV_APROMPT_MAX + DPV_DISPLAY_LIMIT * 1024]; 736041394f3SDevin Teske 737041394f3SDevin Teske dprompt_sprint(buf, prefix, append); 738041394f3SDevin Teske 739041394f3SDevin Teske if (overall >= 0 && overall <= 100) 740041394f3SDevin Teske gauge_percent = percent = overall; 741041394f3SDevin Teske gauge = dlg_reallocate_gauge(gauge, title == NULL ? "" : title, 742041394f3SDevin Teske buf, dheight, dwidth, percent); 743041394f3SDevin Teske dlg_update_gauge(gauge, percent); 744041394f3SDevin Teske } 745041394f3SDevin Teske 746041394f3SDevin Teske /* 747041394f3SDevin Teske * Free allocated items initialized by dprompt_init() 748041394f3SDevin Teske */ 749041394f3SDevin Teske void 750041394f3SDevin Teske dprompt_free(void) 751041394f3SDevin Teske { 752041394f3SDevin Teske if ((dprompt_free_mask & FM_DONE) != 0) { 753041394f3SDevin Teske dprompt_free_mask ^= FM_DONE; 754041394f3SDevin Teske free(done); 755041394f3SDevin Teske done = NULL; 756041394f3SDevin Teske } 757041394f3SDevin Teske if ((dprompt_free_mask & FM_FAIL) != 0) { 758041394f3SDevin Teske dprompt_free_mask ^= FM_FAIL; 759041394f3SDevin Teske free(fail); 760041394f3SDevin Teske fail = NULL; 761041394f3SDevin Teske } 762041394f3SDevin Teske if ((dprompt_free_mask & FM_PEND) != 0) { 763041394f3SDevin Teske dprompt_free_mask ^= FM_PEND; 764041394f3SDevin Teske free(pend); 765041394f3SDevin Teske pend = NULL; 766041394f3SDevin Teske } 767041394f3SDevin Teske } 768