1274116Sdteske/*- 2274116Sdteske * Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org> 3274116Sdteske * All rights reserved. 4274116Sdteske * 5274116Sdteske * Redistribution and use in source and binary forms, with or without 6274116Sdteske * modification, are permitted provided that the following conditions 7274116Sdteske * are met: 8274116Sdteske * 1. Redistributions of source code must retain the above copyright 9274116Sdteske * notice, this list of conditions and the following disclaimer. 10274116Sdteske * 2. Redistributions in binary form must reproduce the above copyright 11274116Sdteske * notice, this list of conditions and the following disclaimer in the 12274116Sdteske * documentation and/or other materials provided with the distribution. 13274116Sdteske * 14274116Sdteske * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15274116Sdteske * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16274116Sdteske * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17274116Sdteske * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18274116Sdteske * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19274116Sdteske * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20274116Sdteske * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21274116Sdteske * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22274116Sdteske * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23274116Sdteske * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24274116Sdteske * SUCH DAMAGE. 25274116Sdteske */ 26274116Sdteske 27274116Sdteske#include <sys/cdefs.h> 28274116Sdteske__FBSDID("$FreeBSD: releng/10.2/lib/libdpv/dprompt.c 284714 2015-06-23 04:03:54Z dteske $"); 29274116Sdteske 30274116Sdteske#include <sys/types.h> 31274116Sdteske 32274116Sdteske#define _BSD_SOURCE /* to get dprintf() prototype in stdio.h below */ 33274116Sdteske#include <dialog.h> 34274116Sdteske#include <err.h> 35274116Sdteske#include <libutil.h> 36274116Sdteske#include <stdarg.h> 37274116Sdteske#include <stdio.h> 38274116Sdteske#include <stdlib.h> 39274116Sdteske#include <string.h> 40274116Sdteske#include <string_m.h> 41274116Sdteske#include <unistd.h> 42274116Sdteske 43274116Sdteske#include "dialog_util.h" 44274116Sdteske#include "dialogrc.h" 45274116Sdteske#include "dprompt.h" 46274116Sdteske#include "dpv.h" 47274116Sdteske#include "dpv_private.h" 48274116Sdteske 49274116Sdteske#define FLABEL_MAX 1024 50274116Sdteske 51274116Sdteskestatic int fheight = 0; /* initialized by dprompt_init() */ 52274116Sdteskestatic char dprompt[PROMPT_MAX + 1] = ""; 53274116Sdteskestatic char *dprompt_pos = (char *)(0); /* treated numerically */ 54274116Sdteske 55274116Sdteske/* Display characteristics */ 56274116Sdteske#define FM_DONE 0x01 57274116Sdteske#define FM_FAIL 0x02 58274116Sdteske#define FM_PEND 0x04 59274116Sdteskestatic uint8_t dprompt_free_mask; 60274116Sdteskestatic char *done = NULL; 61274116Sdteskestatic char *fail = NULL; 62274116Sdteskestatic char *pend = NULL; 63274116Sdteskeint display_limit = DISPLAY_LIMIT_DEFAULT; /* Max entries to show */ 64274116Sdteskeint label_size = LABEL_SIZE_DEFAULT; /* Max width for labels */ 65274116Sdteskeint pbar_size = PBAR_SIZE_DEFAULT; /* Mini-progressbar size */ 66274116Sdteskestatic int gauge_percent = 0; 67274116Sdteskestatic int done_size, done_lsize, done_rsize; 68274116Sdteskestatic int fail_size, fail_lsize, fail_rsize; 69274116Sdteskestatic int mesg_size, mesg_lsize, mesg_rsize; 70274116Sdteskestatic int pend_size, pend_lsize, pend_rsize; 71274116Sdteskestatic int pct_lsize, pct_rsize; 72274116Sdteskestatic void *gauge = NULL; 73274116Sdteske#define SPIN_SIZE 4 74274116Sdteskestatic char spin[SPIN_SIZE + 1] = "/-\\|"; 75274116Sdteskestatic char msg[PROMPT_MAX + 1]; 76274116Sdteskestatic char *spin_cp = spin; 77274116Sdteske 78274116Sdteske/* Function prototypes */ 79274116Sdteskestatic char spin_char(void); 80274116Sdteskestatic int dprompt_add_files(struct dpv_file_node *file_list, 81274116Sdteske struct dpv_file_node *curfile, int pct); 82274116Sdteske 83274116Sdteske/* 84274116Sdteske * Returns a pointer to the current spin character in the spin string and 85274116Sdteske * advances the global position to the next character for the next call. 86274116Sdteske */ 87274116Sdteskestatic char 88274116Sdteskespin_char(void) 89274116Sdteske{ 90274116Sdteske char ch; 91274116Sdteske 92274116Sdteske if (spin_cp == '\0') 93274116Sdteske spin_cp = spin; 94274116Sdteske ch = *spin_cp; 95274116Sdteske 96274116Sdteske /* Advance the spinner to the next char */ 97274116Sdteske if (++spin_cp >= (spin + SPIN_SIZE)) 98274116Sdteske spin_cp = spin; 99274116Sdteske 100274116Sdteske return (ch); 101274116Sdteske} 102274116Sdteske 103274116Sdteske/* 104274116Sdteske * Initialize heights and widths based on various strings and environment 105274116Sdteske * variables (such as ENV_USE_COLOR). 106274116Sdteske */ 107274116Sdteskevoid 108274116Sdteskedprompt_init(struct dpv_file_node *file_list) 109274116Sdteske{ 110274116Sdteske uint8_t nls = 0; 111274116Sdteske int len; 112274116Sdteske int max_cols; 113274116Sdteske int max_rows; 114274116Sdteske int nthfile; 115274116Sdteske int numlines; 116274116Sdteske struct dpv_file_node *curfile; 117274116Sdteske 118274116Sdteske /* 119274116Sdteske * Initialize dialog(3) `colors' support and draw backtitle 120274116Sdteske */ 121274116Sdteske if (use_libdialog && !debug) { 122274116Sdteske init_dialog(stdin, stdout); 123274116Sdteske dialog_vars.colors = 1; 124274116Sdteske if (backtitle != NULL) { 125274116Sdteske dialog_vars.backtitle = (char *)backtitle; 126274116Sdteske dlg_put_backtitle(); 127274116Sdteske } 128274116Sdteske } 129274116Sdteske 130274116Sdteske /* Calculate width of dialog(3) or [X]dialog(1) --gauge box */ 131274116Sdteske dwidth = label_size + pbar_size + 9; 132274116Sdteske 133274116Sdteske /* 134274116Sdteske * Calculate height of dialog(3) or [X]dialog(1) --gauge box 135274116Sdteske */ 136274116Sdteske dheight = 5; 137274116Sdteske max_rows = dialog_maxrows(); 138274116Sdteske /* adjust max_rows for backtitle and/or dialog(3) statusLine */ 139274116Sdteske if (backtitle != NULL) 140274116Sdteske max_rows -= use_shadow ? 3 : 2; 141274116Sdteske if (use_libdialog && use_shadow) 142274116Sdteske max_rows -= 2; 143274116Sdteske /* add lines for `-p text' */ 144274116Sdteske numlines = dialog_prompt_numlines(pprompt, 0); 145274116Sdteske if (debug) 146274116Sdteske warnx("`-p text' is %i line%s long", numlines, 147274116Sdteske numlines == 1 ? "" : "s"); 148274116Sdteske dheight += numlines; 149274116Sdteske /* adjust dheight for various implementations */ 150274116Sdteske if (use_dialog) { 151274116Sdteske dheight -= dialog_prompt_nlstate(pprompt); 152274116Sdteske nls = dialog_prompt_nlstate(pprompt); 153274116Sdteske } else if (use_xdialog) { 154274116Sdteske if (pprompt == NULL || *pprompt == '\0') 155274116Sdteske dheight++; 156274116Sdteske } else if (use_libdialog) { 157274116Sdteske if (pprompt != NULL && *pprompt != '\0') 158274116Sdteske dheight--; 159274116Sdteske } 160274116Sdteske /* limit the number of display items (necessary per dialog(1,3)) */ 161274116Sdteske if (display_limit == 0 || display_limit > DPV_DISPLAY_LIMIT) 162274116Sdteske display_limit = DPV_DISPLAY_LIMIT; 163274116Sdteske /* verify fheight will fit (stop if we hit 1) */ 164274116Sdteske for (; display_limit > 0; display_limit--) { 165274116Sdteske nthfile = numlines = 0; 166274116Sdteske fheight = (int)dpv_nfiles > display_limit ? 167274116Sdteske (unsigned int)display_limit : dpv_nfiles; 168274116Sdteske for (curfile = file_list; curfile != NULL; 169274116Sdteske curfile = curfile->next) { 170274116Sdteske nthfile++; 171274116Sdteske numlines += dialog_prompt_numlines(curfile->name, nls); 172274116Sdteske if ((nthfile % display_limit) == 0) { 173274116Sdteske if (numlines > fheight) 174274116Sdteske fheight = numlines; 175274116Sdteske numlines = nthfile = 0; 176274116Sdteske } 177274116Sdteske } 178274116Sdteske if (numlines > fheight) 179274116Sdteske fheight = numlines; 180274116Sdteske if ((dheight + fheight + 181274116Sdteske (int)dialog_prompt_numlines(aprompt, use_dialog) - 182274116Sdteske (use_dialog ? (int)dialog_prompt_nlstate(aprompt) : 0)) 183274116Sdteske <= max_rows) 184274116Sdteske break; 185274116Sdteske } 186274116Sdteske /* don't show any items if we run the risk of hitting a blank set */ 187274116Sdteske if ((max_rows - (use_shadow ? 5 : 4)) >= fheight) 188274116Sdteske dheight += fheight; 189274116Sdteske else 190274116Sdteske fheight = 0; 191274116Sdteske /* add lines for `-a text' */ 192274116Sdteske numlines = dialog_prompt_numlines(aprompt, use_dialog); 193274116Sdteske if (debug) 194274116Sdteske warnx("`-a text' is %i line%s long", numlines, 195274116Sdteske numlines == 1 ? "" : "s"); 196274116Sdteske dheight += numlines; 197274116Sdteske 198274116Sdteske /* If using Xdialog(1), adjust accordingly (based on testing) */ 199274116Sdteske if (use_xdialog) 200274116Sdteske dheight += dheight / 4; 201274116Sdteske 202274116Sdteske /* For wide mode, long prefix (`pprompt') or append (`aprompt') 203274116Sdteske * strings will bump width */ 204274116Sdteske if (wide) { 205274116Sdteske len = (int)dialog_prompt_longestline(pprompt, 0); /* !nls */ 206274116Sdteske if ((len + 4) > dwidth) 207274116Sdteske dwidth = len + 4; 208274116Sdteske len = (int)dialog_prompt_longestline(aprompt, 1); /* nls */ 209274116Sdteske if ((len + 4) > dwidth) 210274116Sdteske dwidth = len + 4; 211274116Sdteske } 212274116Sdteske 213274116Sdteske /* Enforce width constraints to maximum values */ 214274116Sdteske max_cols = dialog_maxcols(); 215274116Sdteske if (max_cols > 0 && dwidth > max_cols) 216274116Sdteske dwidth = max_cols; 217274116Sdteske 218274116Sdteske /* Optimize widths to sane values*/ 219274116Sdteske if (pbar_size > dwidth - 9) { 220274116Sdteske pbar_size = dwidth - 9; 221274116Sdteske label_size = 0; 222274116Sdteske /* -9 = "| - [" ... "] |" */ 223274116Sdteske } 224274116Sdteske if (pbar_size < 0) 225274116Sdteske label_size = dwidth - 8; 226274116Sdteske /* -8 = "| " ... " - |" */ 227274116Sdteske else if (label_size > (dwidth - pbar_size - 9) || wide) 228274116Sdteske label_size = no_labels ? 0 : dwidth - pbar_size - 9; 229274116Sdteske /* -9 = "| " ... " - [" ... "] |" */ 230274116Sdteske 231274116Sdteske /* Hide labels if requested */ 232274116Sdteske if (no_labels) 233274116Sdteske label_size = 0; 234274116Sdteske 235274116Sdteske /* Touch up the height (now that we know dwidth) */ 236274116Sdteske dheight += dialog_prompt_wrappedlines(pprompt, dwidth - 4, 0); 237274116Sdteske dheight += dialog_prompt_wrappedlines(aprompt, dwidth - 4, 1); 238274116Sdteske 239274116Sdteske if (debug) 240274116Sdteske warnx("dheight = %i dwidth = %i fheight = %i", 241274116Sdteske dheight, dwidth, fheight); 242274116Sdteske 243274116Sdteske /* Calculate left/right portions of % */ 244274116Sdteske pct_lsize = (pbar_size - 4) / 2; /* -4 == printf("%-3s%%", pct) */ 245274116Sdteske pct_rsize = pct_lsize; 246274116Sdteske /* If not evenly divisible by 2, increment the right-side */ 247274116Sdteske if ((pct_rsize + pct_rsize + 4) != pbar_size) 248274116Sdteske pct_rsize++; 249274116Sdteske 250274116Sdteske /* Initialize "Done" text */ 251274116Sdteske if (done == NULL && (done = msg_done) == NULL) { 252274116Sdteske if ((done = getenv(ENV_MSG_DONE)) != NULL) 253274116Sdteske done_size = strlen(done); 254274116Sdteske else { 255274116Sdteske done_size = strlen(DPV_DONE_DEFAULT); 256274116Sdteske if ((done = malloc(done_size + 1)) == NULL) 257274116Sdteske errx(EXIT_FAILURE, "Out of memory?!"); 258274116Sdteske dprompt_free_mask |= FM_DONE; 259274116Sdteske snprintf(done, done_size + 1, DPV_DONE_DEFAULT); 260274116Sdteske } 261274116Sdteske } 262274116Sdteske if (pbar_size < done_size) { 263274116Sdteske done_lsize = done_rsize = 0; 264274116Sdteske *(done + pbar_size) = '\0'; 265274116Sdteske done_size = pbar_size; 266274116Sdteske } else { 267274116Sdteske /* Calculate left/right portions for mini-progressbar */ 268274116Sdteske done_lsize = (pbar_size - done_size) / 2; 269274116Sdteske done_rsize = done_lsize; 270274116Sdteske /* If not evenly divisible by 2, increment the right-side */ 271274116Sdteske if ((done_rsize + done_size + done_lsize) != pbar_size) 272274116Sdteske done_rsize++; 273274116Sdteske } 274274116Sdteske 275274116Sdteske /* Initialize "Fail" text */ 276274116Sdteske if (fail == NULL && (fail = msg_fail) == NULL) { 277274116Sdteske if ((fail = getenv(ENV_MSG_FAIL)) != NULL) 278274116Sdteske fail_size = strlen(fail); 279274116Sdteske else { 280274116Sdteske fail_size = strlen(DPV_FAIL_DEFAULT); 281274116Sdteske if ((fail = malloc(fail_size + 1)) == NULL) 282274116Sdteske errx(EXIT_FAILURE, "Out of memory?!"); 283274116Sdteske dprompt_free_mask |= FM_FAIL; 284274116Sdteske snprintf(fail, fail_size + 1, DPV_FAIL_DEFAULT); 285274116Sdteske } 286274116Sdteske } 287274116Sdteske if (pbar_size < fail_size) { 288274116Sdteske fail_lsize = fail_rsize = 0; 289274116Sdteske *(fail + pbar_size) = '\0'; 290274116Sdteske fail_size = pbar_size; 291274116Sdteske } else { 292274116Sdteske /* Calculate left/right portions for mini-progressbar */ 293274116Sdteske fail_lsize = (pbar_size - fail_size) / 2; 294274116Sdteske fail_rsize = fail_lsize; 295274116Sdteske /* If not evenly divisible by 2, increment the right-side */ 296274116Sdteske if ((fail_rsize + fail_size + fail_lsize) != pbar_size) 297274116Sdteske fail_rsize++; 298274116Sdteske } 299274116Sdteske 300274116Sdteske /* Initialize "Pending" text */ 301274116Sdteske if (pend == NULL && (pend = msg_pending) == NULL) { 302274116Sdteske if ((pend = getenv(ENV_MSG_PENDING)) != NULL) 303274116Sdteske pend_size = strlen(pend); 304274116Sdteske else { 305274116Sdteske pend_size = strlen(DPV_PENDING_DEFAULT); 306274116Sdteske if ((pend = malloc(pend_size + 1)) == NULL) 307274116Sdteske errx(EXIT_FAILURE, "Out of memory?!"); 308274116Sdteske dprompt_free_mask |= FM_PEND; 309274116Sdteske snprintf(pend, pend_size + 1, DPV_PENDING_DEFAULT); 310274116Sdteske } 311274116Sdteske } 312274116Sdteske if (pbar_size < pend_size) { 313274116Sdteske pend_lsize = pend_rsize = 0; 314274116Sdteske *(pend + pbar_size) = '\0'; 315274116Sdteske pend_size = pbar_size; 316274116Sdteske } else { 317274116Sdteske /* Calculate left/right portions for mini-progressbar */ 318274116Sdteske pend_lsize = (pbar_size - pend_size) / 2; 319274116Sdteske pend_rsize = pend_lsize; 320274116Sdteske /* If not evenly divisible by 2, increment the right-side */ 321274116Sdteske if ((pend_rsize + pend_lsize + pend_size) != pbar_size) 322274116Sdteske pend_rsize++; 323274116Sdteske } 324274116Sdteske 325274116Sdteske if (debug) 326274116Sdteske warnx("label_size = %i pbar_size = %i", label_size, pbar_size); 327274116Sdteske 328274116Sdteske dprompt_clear(); 329274116Sdteske} 330274116Sdteske 331274116Sdteske/* 332274116Sdteske * Clear the [X]dialog(1) `--gauge' prompt buffer. 333274116Sdteske */ 334274116Sdteskevoid 335274116Sdteskedprompt_clear(void) 336274116Sdteske{ 337274116Sdteske 338274116Sdteske *dprompt = '\0'; 339274116Sdteske dprompt_pos = dprompt; 340274116Sdteske} 341274116Sdteske 342274116Sdteske/* 343274116Sdteske * Append to the [X]dialog(1) `--gauge' prompt buffer. Syntax is like printf(3) 344274116Sdteske * and returns the number of bytes appended to the buffer. 345274116Sdteske */ 346274116Sdteskeint 347274116Sdteskedprompt_add(const char *format, ...) 348274116Sdteske{ 349274116Sdteske int len; 350274116Sdteske va_list ap; 351274116Sdteske 352274116Sdteske if (dprompt_pos >= (dprompt + PROMPT_MAX)) 353274116Sdteske return (0); 354274116Sdteske 355274116Sdteske va_start(ap, format); 356274116Sdteske len = vsnprintf(dprompt_pos, (size_t)(PROMPT_MAX - 357274116Sdteske (dprompt_pos - dprompt)), format, ap); 358274116Sdteske va_end(ap); 359274116Sdteske if (len == -1) 360274116Sdteske errx(EXIT_FAILURE, "%s: Oops, dprompt buffer overflow", 361274116Sdteske __func__); 362274116Sdteske 363274116Sdteske if ((dprompt_pos + len) < (dprompt + PROMPT_MAX)) 364274116Sdteske dprompt_pos += len; 365274116Sdteske else 366274116Sdteske dprompt_pos = dprompt + PROMPT_MAX; 367274116Sdteske 368274116Sdteske return (len); 369274116Sdteske} 370274116Sdteske 371274116Sdteske/* 372274116Sdteske * Append active files to the [X]dialog(1) `--gauge' prompt buffer. Syntax 373274116Sdteske * requires a pointer to the head of the dpv_file_node linked-list. Returns the 374274116Sdteske * number of files processed successfully. 375274116Sdteske */ 376274116Sdteskestatic int 377274116Sdteskedprompt_add_files(struct dpv_file_node *file_list, 378274116Sdteske struct dpv_file_node *curfile, int pct) 379274116Sdteske{ 380274116Sdteske char c; 381274116Sdteske char bold_code = 'b'; /* default: enabled */ 382274116Sdteske char color_code = '4'; /* default: blue */ 383274116Sdteske uint8_t after_curfile = curfile != NULL ? FALSE : TRUE; 384274116Sdteske uint8_t nls = 0; 385274116Sdteske char *cp; 386274116Sdteske char *lastline; 387274116Sdteske char *name; 388274116Sdteske const char *bg_code; 389274116Sdteske const char *estext; 390274116Sdteske const char *format; 391274116Sdteske enum dprompt_state dstate; 392274116Sdteske int estext_lsize; 393274116Sdteske int estext_rsize; 394274116Sdteske int flabel_size; 395274116Sdteske int hlen; 396274116Sdteske int lsize; 397274116Sdteske int nlines = 0; 398274116Sdteske int nthfile = 0; 399274116Sdteske int pwidth; 400274116Sdteske int rsize; 401274116Sdteske struct dpv_file_node *fp; 402274116Sdteske char flabel[FLABEL_MAX + 1]; 403274116Sdteske char human[32]; 404274116Sdteske char pbar[pbar_size + 16]; /* +15 for optional color */ 405274116Sdteske char pbar_cap[sizeof(pbar)]; 406274116Sdteske char pbar_fill[sizeof(pbar)]; 407274116Sdteske 408274116Sdteske 409274116Sdteske /* Override color defaults with that of main progress bar */ 410274116Sdteske if (use_colors || use_shadow) { /* NB: shadow enables color */ 411274116Sdteske color_code = gauge_color[0]; 412274116Sdteske /* NB: str[1] aka bg is unused */ 413274116Sdteske bold_code = gauge_color[2]; 414274116Sdteske } 415274116Sdteske 416274116Sdteske /* 417274116Sdteske * Create mini-progressbar for current file (if applicable) 418274116Sdteske */ 419274116Sdteske *pbar = '\0'; 420274116Sdteske if (pbar_size >= 0 && pct >= 0 && curfile != NULL && 421274116Sdteske (curfile->length >= 0 || dialog_test)) { 422274116Sdteske snprintf(pbar, pbar_size + 1, "%*s%3u%%%*s", pct_lsize, "", 423274116Sdteske pct, pct_rsize, ""); 424274116Sdteske if (use_color) { 425274116Sdteske /* Calculate the fill-width of progressbar */ 426274116Sdteske pwidth = pct * pbar_size / 100; 427274116Sdteske /* Round up based on one-tenth of a percent */ 428274116Sdteske if ((pct * pbar_size % 100) > 50) 429274116Sdteske pwidth++; 430274116Sdteske 431274116Sdteske /* 432274116Sdteske * Make two copies of pbar. Make one represent the fill 433274116Sdteske * and the other the remainder (cap). We'll insert the 434274116Sdteske * ANSI delimiter in between. 435274116Sdteske */ 436274116Sdteske *pbar_fill = '\0'; 437274116Sdteske *pbar_cap = '\0'; 438274116Sdteske strncat(pbar_fill, (const char *)(pbar), dwidth); 439274116Sdteske *(pbar_fill + pwidth) = '\0'; 440274116Sdteske strncat(pbar_cap, (const char *)(pbar+pwidth), dwidth); 441274116Sdteske 442274116Sdteske /* Finalize the mini [color] progressbar */ 443274116Sdteske snprintf(pbar, sizeof(pbar), 444274116Sdteske "\\Z%c\\Zr\\Z%c%s%s%s\\Zn", bold_code, color_code, 445274116Sdteske pbar_fill, "\\ZR", pbar_cap); 446274116Sdteske } 447274116Sdteske } 448274116Sdteske 449274116Sdteske for (fp = file_list; fp != NULL; fp = fp->next) { 450274116Sdteske flabel_size = label_size; 451274116Sdteske name = fp->name; 452274116Sdteske nthfile++; 453274116Sdteske 454274116Sdteske /* 455274116Sdteske * Support multiline filenames (where the filename is taken as 456274116Sdteske * the last line and the text leading up to the last line can 457274116Sdteske * be used as (for example) a heading/separator between files. 458274116Sdteske */ 459274116Sdteske if (use_dialog) 460274116Sdteske nls = dialog_prompt_nlstate(pprompt); 461274116Sdteske nlines += dialog_prompt_numlines(name, nls); 462274116Sdteske lastline = dialog_prompt_lastline(name, 1); 463274116Sdteske if (name != lastline) { 464274116Sdteske c = *lastline; 465274116Sdteske *lastline = '\0'; 466274116Sdteske dprompt_add("%s", name); 467274116Sdteske *lastline = c; 468274116Sdteske name = lastline; 469274116Sdteske } 470274116Sdteske 471274116Sdteske /* Support color codes (for dialog(1,3)) in file names */ 472274116Sdteske if ((use_dialog || use_libdialog) && use_color) { 473274116Sdteske cp = name; 474274116Sdteske while (*cp != '\0') { 475274116Sdteske if (*cp == '\\' && *(cp + 1) != '\0' && 476274116Sdteske *(++cp) == 'Z' && *(cp + 1) != '\0') { 477274116Sdteske cp++; 478274116Sdteske flabel_size += 3; 479274116Sdteske } 480274116Sdteske cp++; 481274116Sdteske } 482274116Sdteske if (flabel_size > FLABEL_MAX) 483274116Sdteske flabel_size = FLABEL_MAX; 484274116Sdteske } 485274116Sdteske 486274116Sdteske /* If no mini-progressbar, increase label width */ 487274116Sdteske if (pbar_size < 0 && flabel_size <= FLABEL_MAX - 2 && 488274116Sdteske no_labels == FALSE) 489274116Sdteske flabel_size += 2; 490274116Sdteske 491274116Sdteske /* If name is too long, add an ellipsis */ 492274116Sdteske if (snprintf(flabel, flabel_size + 1, "%s", name) > 493274116Sdteske flabel_size) sprintf(flabel + flabel_size - 3, "..."); 494274116Sdteske 495274116Sdteske /* 496274116Sdteske * Append the label (processing the current file differently) 497274116Sdteske */ 498274116Sdteske if (fp == curfile && pct < 100) { 499274116Sdteske /* 500274116Sdteske * Add an ellipsis to current file name if it will fit. 501274116Sdteske * There may be an ellipsis already from truncating the 502274116Sdteske * label (in which case, we already have one). 503274116Sdteske */ 504274116Sdteske cp = flabel + strlen(flabel); 505274116Sdteske if (cp < (flabel + flabel_size)) 506274116Sdteske snprintf(cp, flabel_size - 507274116Sdteske (cp - flabel) + 1, "..."); 508274116Sdteske 509274116Sdteske /* Append label (with spinner and optional color) */ 510274116Sdteske dprompt_add("%s%-*s%s %c", use_color ? "\\Zb" : "", 511274116Sdteske flabel_size, flabel, use_color ? "\\Zn" : "", 512274116Sdteske spin_char()); 513274116Sdteske } else 514274116Sdteske dprompt_add("%-*s%s %s", flabel_size, 515274116Sdteske flabel, use_color ? "\\Zn" : "", " "); 516274116Sdteske 517274116Sdteske /* 518274116Sdteske * Append pbar/status (processing the current file differently) 519274116Sdteske */ 520274116Sdteske dstate = DPROMPT_NONE; 521274116Sdteske if (fp->msg != NULL) 522274116Sdteske dstate = DPROMPT_CUSTOM_MSG; 523274116Sdteske else if (pbar_size < 0) 524274116Sdteske dstate = DPROMPT_NONE; 525274116Sdteske else if (pbar_size < 4) 526274116Sdteske dstate = DPROMPT_MINIMAL; 527274116Sdteske else if (after_curfile) 528274116Sdteske dstate = DPROMPT_PENDING; 529274116Sdteske else if (fp == curfile) { 530274116Sdteske if (*pbar == '\0') { 531274116Sdteske if (fp->length < 0) 532274116Sdteske dstate = DPROMPT_DETAILS; 533274116Sdteske else if (fp->status == DPV_STATUS_RUNNING) 534274116Sdteske dstate = DPROMPT_DETAILS; 535274116Sdteske else 536274116Sdteske dstate = DPROMPT_END_STATE; 537274116Sdteske } 538274116Sdteske else if (dialog_test) /* status/length ignored */ 539274116Sdteske dstate = pct < 100 ? 540274116Sdteske DPROMPT_PBAR : DPROMPT_END_STATE; 541274116Sdteske else if (fp->status == DPV_STATUS_RUNNING) 542274116Sdteske dstate = fp->length < 0 ? 543274116Sdteske DPROMPT_DETAILS : DPROMPT_PBAR; 544274116Sdteske else /* not running */ 545274116Sdteske dstate = fp->length < 0 ? 546274116Sdteske DPROMPT_DETAILS : DPROMPT_END_STATE; 547274116Sdteske } else { /* before curfile */ 548274116Sdteske if (dialog_test) 549274116Sdteske dstate = DPROMPT_END_STATE; 550274116Sdteske else 551274116Sdteske dstate = fp->length < 0 ? 552274116Sdteske DPROMPT_DETAILS : DPROMPT_END_STATE; 553274116Sdteske } 554274116Sdteske format = use_color ? 555274116Sdteske " [\\Z%c%s%-*s%s%-*s\\Zn]\\n" : 556274116Sdteske " [%-*s%s%-*s]\\n"; 557274116Sdteske if (fp->status == DPV_STATUS_FAILED) { 558274116Sdteske bg_code = "\\Zr\\Z1"; /* Red */ 559274116Sdteske estext_lsize = fail_lsize; 560274116Sdteske estext_rsize = fail_rsize; 561274116Sdteske estext = fail; 562274116Sdteske } else { /* e.g., DPV_STATUS_DONE */ 563274116Sdteske bg_code = "\\Zr\\Z2"; /* Green */ 564274116Sdteske estext_lsize = done_lsize; 565274116Sdteske estext_rsize = done_rsize; 566274116Sdteske estext = done; 567274116Sdteske } 568274116Sdteske switch (dstate) { 569274116Sdteske case DPROMPT_PENDING: /* Future file(s) */ 570274116Sdteske dprompt_add(" [%-*s%s%-*s]\\n", 571274116Sdteske pend_lsize, "", pend, pend_rsize, ""); 572274116Sdteske break; 573274116Sdteske case DPROMPT_PBAR: /* Current file */ 574274116Sdteske dprompt_add(" [%s]\\n", pbar); 575274116Sdteske break; 576274116Sdteske case DPROMPT_END_STATE: /* Past/Current file(s) */ 577274116Sdteske if (use_color) 578274116Sdteske dprompt_add(format, bold_code, bg_code, 579274116Sdteske estext_lsize, "", estext, 580274116Sdteske estext_rsize, ""); 581274116Sdteske else 582274116Sdteske dprompt_add(format, 583274116Sdteske estext_lsize, "", estext, 584274116Sdteske estext_rsize, ""); 585274116Sdteske break; 586274116Sdteske case DPROMPT_DETAILS: /* Past/Current file(s) */ 587274116Sdteske humanize_number(human, pbar_size + 2, fp->read, "", 588274116Sdteske HN_AUTOSCALE, HN_NOSPACE | HN_DIVISOR_1000); 589274116Sdteske 590274116Sdteske /* Calculate center alignment */ 591274116Sdteske hlen = (int)strlen(human); 592274116Sdteske lsize = (pbar_size - hlen) / 2; 593274116Sdteske rsize = lsize; 594274116Sdteske if ((lsize+hlen+rsize) != pbar_size) 595274116Sdteske rsize++; 596274116Sdteske 597274116Sdteske if (use_color) 598274116Sdteske dprompt_add(format, bold_code, bg_code, 599274116Sdteske lsize, "", human, rsize, ""); 600274116Sdteske else 601274116Sdteske dprompt_add(format, 602274116Sdteske lsize, "", human, rsize, ""); 603274116Sdteske break; 604274116Sdteske case DPROMPT_CUSTOM_MSG: /* File-specific message override */ 605274116Sdteske snprintf(msg, PROMPT_MAX + 1, "%s", fp->msg); 606274116Sdteske if (pbar_size < (mesg_size = strlen(msg))) { 607274116Sdteske mesg_lsize = mesg_rsize = 0; 608274116Sdteske *(msg + pbar_size) = '\0'; 609274116Sdteske mesg_size = pbar_size; 610274116Sdteske } else { 611274116Sdteske mesg_lsize = (pbar_size - mesg_size) / 2; 612274116Sdteske mesg_rsize = mesg_lsize; 613274116Sdteske if ((mesg_rsize + mesg_size + mesg_lsize) 614274116Sdteske != pbar_size) 615274116Sdteske mesg_rsize++; 616274116Sdteske } 617274116Sdteske if (use_color) 618274116Sdteske dprompt_add(format, bold_code, bg_code, 619274116Sdteske mesg_lsize, "", msg, mesg_rsize, ""); 620274116Sdteske else 621274116Sdteske dprompt_add(format, mesg_lsize, "", msg, 622274116Sdteske mesg_rsize, ""); 623274116Sdteske break; 624274116Sdteske case DPROMPT_MINIMAL: /* Short progress bar, minimal room */ 625274116Sdteske if (use_color) 626274116Sdteske dprompt_add(format, bold_code, bg_code, 627274116Sdteske pbar_size, "", "", 0, ""); 628274116Sdteske else 629274116Sdteske dprompt_add(format, pbar_size, "", "", 0, ""); 630274116Sdteske break; 631274116Sdteske case DPROMPT_NONE: /* pbar_size < 0 */ 632274116Sdteske /* FALLTHROUGH */ 633274116Sdteske default: 634274116Sdteske dprompt_add(" \\n"); 635274116Sdteske /* 636274116Sdteske * NB: Leading space required for the case when 637274116Sdteske * spin_char() returns a single backslash [\] which 638274116Sdteske * without the space, changes the meaning of `\n' 639274116Sdteske */ 640274116Sdteske } 641274116Sdteske 642274116Sdteske /* Stop building if we've hit the internal limit */ 643274116Sdteske if (nthfile >= display_limit) 644274116Sdteske break; 645274116Sdteske 646274116Sdteske /* If this is the current file, all others are pending */ 647274116Sdteske if (fp == curfile) 648274116Sdteske after_curfile = TRUE; 649274116Sdteske } 650274116Sdteske 651274116Sdteske /* 652274116Sdteske * Since we cannot change the height/width of the [X]dialog(1) widget 653274116Sdteske * after spawn, to make things look nice let's pad the height so that 654274116Sdteske * the `-a text' always appears in the same spot. 655274116Sdteske * 656274116Sdteske * NOTE: fheight is calculated in dprompt_init(). It represents the 657274116Sdteske * maximum height required to display the set of items (broken up into 658274116Sdteske * pieces of display_limit chunks) whose names contain the most 659274116Sdteske * newlines for any given set. 660274116Sdteske */ 661274116Sdteske while (nlines < fheight) { 662274116Sdteske dprompt_add("\n"); 663274116Sdteske nlines++; 664274116Sdteske } 665274116Sdteske 666274116Sdteske return (nthfile); 667274116Sdteske} 668274116Sdteske 669274116Sdteske/* 670274116Sdteske * Process the dpv_file_node linked-list of named files, re-generating the 671274116Sdteske * [X]dialog(1) `--gauge' prompt text for the current state of transfers. 672274116Sdteske */ 673274116Sdteskevoid 674274116Sdteskedprompt_recreate(struct dpv_file_node *file_list, 675274116Sdteske struct dpv_file_node *curfile, int pct) 676274116Sdteske{ 677274116Sdteske size_t len; 678274116Sdteske 679274116Sdteske /* 680274116Sdteske * Re-Build the prompt text 681274116Sdteske */ 682274116Sdteske dprompt_clear(); 683274116Sdteske if (display_limit > 0) 684274116Sdteske dprompt_add_files(file_list, curfile, pct); 685274116Sdteske 686274116Sdteske /* Xdialog(1) requires newlines (a) escaped and (b) in triplicate */ 687274116Sdteske if (use_xdialog) { 688274116Sdteske /* Replace `\n' with `\n\\n\n' in dprompt */ 689274116Sdteske len = strlen(dprompt); 690274116Sdteske len += strcount(dprompt, "\\n") * 5; /* +5 chars per count */ 691274116Sdteske if (len > PROMPT_MAX) 692274116Sdteske errx(EXIT_FAILURE, "%s: Oops, dprompt buffer overflow " 693274116Sdteske "(%zu > %i)", __func__, len, PROMPT_MAX); 694274116Sdteske if (replaceall(dprompt, "\\n", "\n\\n\n") < 0) 695274116Sdteske err(EXIT_FAILURE, "%s: replaceall()", __func__); 696274116Sdteske } 697274116Sdteske else if (use_libdialog) 698274116Sdteske strexpandnl(dprompt); 699274116Sdteske} 700274116Sdteske 701274116Sdteske/* 702274116Sdteske * Print the [X]dialog(1) `--gauge' prompt text to a buffer. 703274116Sdteske */ 704274116Sdteskeint 705274116Sdteskedprompt_sprint(char * restrict str, const char *prefix, const char *append) 706274116Sdteske{ 707274116Sdteske 708274116Sdteske return (snprintf(str, PROMPT_MAX, "%s%s%s%s", use_color ? "\\Zn" : "", 709274116Sdteske prefix ? prefix : "", dprompt, append ? append : "")); 710274116Sdteske} 711274116Sdteske 712274116Sdteske/* 713274116Sdteske * Print the [X]dialog(1) `--gauge' prompt text to file descriptor fd (could 714274116Sdteske * be STDOUT_FILENO or a pipe(2) file descriptor to actual [X]dialog(1)). 715274116Sdteske */ 716274116Sdteskevoid 717274116Sdteskedprompt_dprint(int fd, const char *prefix, const char *append, int overall) 718274116Sdteske{ 719274116Sdteske int percent = gauge_percent; 720274116Sdteske 721274116Sdteske if (overall >= 0 && overall <= 100) 722274116Sdteske gauge_percent = percent = overall; 723274116Sdteske dprintf(fd, "XXX\n%s%s%s%s\nXXX\n%i\n", use_color ? "\\Zn" : "", 724274116Sdteske prefix ? prefix : "", dprompt, append ? append : "", percent); 725274116Sdteske fsync(fd); 726274116Sdteske} 727274116Sdteske 728274116Sdteske/* 729274116Sdteske * Print the dialog(3) `gauge' prompt text using libdialog. 730274116Sdteske */ 731274116Sdteskevoid 732274116Sdteskedprompt_libprint(const char *prefix, const char *append, int overall) 733274116Sdteske{ 734274116Sdteske int percent = gauge_percent; 735274116Sdteske char buf[DPV_PPROMPT_MAX + DPV_APROMPT_MAX + DPV_DISPLAY_LIMIT * 1024]; 736274116Sdteske 737274116Sdteske dprompt_sprint(buf, prefix, append); 738274116Sdteske 739274116Sdteske if (overall >= 0 && overall <= 100) 740274116Sdteske gauge_percent = percent = overall; 741274116Sdteske gauge = dlg_reallocate_gauge(gauge, title == NULL ? "" : title, 742274116Sdteske buf, dheight, dwidth, percent); 743274116Sdteske dlg_update_gauge(gauge, percent); 744274116Sdteske} 745274116Sdteske 746274116Sdteske/* 747274116Sdteske * Free allocated items initialized by dprompt_init() 748274116Sdteske */ 749274116Sdteskevoid 750274116Sdteskedprompt_free(void) 751274116Sdteske{ 752274116Sdteske if ((dprompt_free_mask & FM_DONE) != 0) { 753274116Sdteske dprompt_free_mask ^= FM_DONE; 754274116Sdteske free(done); 755274116Sdteske done = NULL; 756274116Sdteske } 757274116Sdteske if ((dprompt_free_mask & FM_FAIL) != 0) { 758274116Sdteske dprompt_free_mask ^= FM_FAIL; 759274116Sdteske free(fail); 760274116Sdteske fail = NULL; 761274116Sdteske } 762274116Sdteske if ((dprompt_free_mask & FM_PEND) != 0) { 763274116Sdteske dprompt_free_mask ^= FM_PEND; 764274116Sdteske free(pend); 765274116Sdteske pend = NULL; 766274116Sdteske } 767274116Sdteske} 768