1 /*- 2 * Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/types.h> 31 32 #define _BSD_SOURCE /* to get dprintf() prototype in stdio.h below */ 33 #include <dialog.h> 34 #include <err.h> 35 #include <libutil.h> 36 #include <stdarg.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <string_m.h> 41 #include <unistd.h> 42 43 #include "dialog_util.h" 44 #include "dialogrc.h" 45 #include "dprompt.h" 46 #include "dpv.h" 47 #include "dpv_private.h" 48 49 #define FLABEL_MAX 1024 50 51 static int fheight = 0; /* initialized by dprompt_init() */ 52 static char dprompt[PROMPT_MAX + 1] = ""; 53 static char *dprompt_pos = (char *)(0); /* treated numerically */ 54 55 /* Display characteristics */ 56 #define FM_DONE 0x01 57 #define FM_FAIL 0x02 58 #define FM_PEND 0x04 59 static uint8_t dprompt_free_mask; 60 static char *done = NULL; 61 static char *fail = NULL; 62 static char *pend = NULL; 63 int display_limit = DISPLAY_LIMIT_DEFAULT; /* Max entries to show */ 64 int label_size = LABEL_SIZE_DEFAULT; /* Max width for labels */ 65 int pbar_size = PBAR_SIZE_DEFAULT; /* Mini-progressbar size */ 66 static int gauge_percent = 0; 67 static int done_size, done_lsize, done_rsize; 68 static int fail_size, fail_lsize, fail_rsize; 69 static int mesg_size, mesg_lsize, mesg_rsize; 70 static int pend_size, pend_lsize, pend_rsize; 71 static int pct_lsize, pct_rsize; 72 static void *gauge = NULL; 73 #define SPIN_SIZE 4 74 static char spin[SPIN_SIZE + 1] = "/-\\|"; 75 static char msg[PROMPT_MAX + 1]; 76 static char *spin_cp = spin; 77 78 /* Function prototypes */ 79 static char spin_char(void); 80 static int dprompt_add_files(struct dpv_file_node *file_list, 81 struct dpv_file_node *curfile, int pct); 82 83 /* 84 * Returns a pointer to the current spin character in the spin string and 85 * advances the global position to the next character for the next call. 86 */ 87 static char 88 spin_char(void) 89 { 90 char ch; 91 92 if (spin_cp == '\0') 93 spin_cp = spin; 94 ch = *spin_cp; 95 96 /* Advance the spinner to the next char */ 97 if (++spin_cp >= (spin + SPIN_SIZE)) 98 spin_cp = spin; 99 100 return (ch); 101 } 102 103 /* 104 * Initialize heights and widths based on various strings and environment 105 * variables (such as ENV_USE_COLOR). 106 */ 107 void 108 dprompt_init(struct dpv_file_node *file_list) 109 { 110 uint8_t nls = 0; 111 int len; 112 int max_cols; 113 int max_rows; 114 int nthfile; 115 int numlines; 116 struct dpv_file_node *curfile; 117 118 /* 119 * Initialize dialog(3) `colors' support and draw backtitle 120 */ 121 if (use_libdialog && !debug) { 122 init_dialog(stdin, stdout); 123 dialog_vars.colors = 1; 124 if (backtitle != NULL) { 125 dialog_vars.backtitle = (char *)backtitle; 126 dlg_put_backtitle(); 127 } 128 } 129 130 /* Calculate width of dialog(3) or [X]dialog(1) --gauge box */ 131 dwidth = label_size + pbar_size + 9; 132 133 /* 134 * Calculate height of dialog(3) or [X]dialog(1) --gauge box 135 */ 136 dheight = 5; 137 max_rows = dialog_maxrows(); 138 /* adjust max_rows for backtitle and/or dialog(3) statusLine */ 139 if (backtitle != NULL) 140 max_rows -= use_shadow ? 3 : 2; 141 if (use_libdialog && use_shadow) 142 max_rows -= 2; 143 /* add lines for `-p text' */ 144 numlines = dialog_prompt_numlines(pprompt, 0); 145 if (debug) 146 warnx("`-p text' is %i line%s long", numlines, 147 numlines == 1 ? "" : "s"); 148 dheight += numlines; 149 /* adjust dheight for various implementations */ 150 if (use_dialog) { 151 dheight -= dialog_prompt_nlstate(pprompt); 152 nls = dialog_prompt_nlstate(pprompt); 153 } else if (use_xdialog) { 154 if (pprompt == NULL || *pprompt == '\0') 155 dheight++; 156 } else if (use_libdialog) { 157 if (pprompt != NULL && *pprompt != '\0') 158 dheight--; 159 } 160 /* limit the number of display items (necessary per dialog(1,3)) */ 161 if (display_limit == 0 || display_limit > DPV_DISPLAY_LIMIT) 162 display_limit = DPV_DISPLAY_LIMIT; 163 /* verify fheight will fit (stop if we hit 1) */ 164 for (; display_limit > 0; display_limit--) { 165 nthfile = numlines = 0; 166 fheight = (int)dpv_nfiles > display_limit ? 167 (unsigned int)display_limit : dpv_nfiles; 168 for (curfile = file_list; curfile != NULL; 169 curfile = curfile->next) { 170 nthfile++; 171 numlines += dialog_prompt_numlines(curfile->name, nls); 172 if ((nthfile % display_limit) == 0) { 173 if (numlines > fheight) 174 fheight = numlines; 175 numlines = nthfile = 0; 176 } 177 } 178 if (numlines > fheight) 179 fheight = numlines; 180 if ((dheight + fheight + 181 (int)dialog_prompt_numlines(aprompt, use_dialog) - 182 (use_dialog ? (int)dialog_prompt_nlstate(aprompt) : 0)) 183 <= max_rows) 184 break; 185 } 186 /* don't show any items if we run the risk of hitting a blank set */ 187 if ((max_rows - (use_shadow ? 5 : 4)) >= fheight) 188 dheight += fheight; 189 else 190 fheight = 0; 191 /* add lines for `-a text' */ 192 numlines = dialog_prompt_numlines(aprompt, use_dialog); 193 if (debug) 194 warnx("`-a text' is %i line%s long", numlines, 195 numlines == 1 ? "" : "s"); 196 dheight += numlines; 197 198 /* If using Xdialog(1), adjust accordingly (based on testing) */ 199 if (use_xdialog) 200 dheight += dheight / 4; 201 202 /* For wide mode, long prefix (`pprompt') or append (`aprompt') 203 * strings will bump width */ 204 if (wide) { 205 len = (int)dialog_prompt_longestline(pprompt, 0); /* !nls */ 206 if ((len + 4) > dwidth) 207 dwidth = len + 4; 208 len = (int)dialog_prompt_longestline(aprompt, 1); /* nls */ 209 if ((len + 4) > dwidth) 210 dwidth = len + 4; 211 } 212 213 /* Enforce width constraints to maximum values */ 214 max_cols = dialog_maxcols(); 215 if (max_cols > 0 && dwidth > max_cols) 216 dwidth = max_cols; 217 218 /* Optimize widths to sane values*/ 219 if (pbar_size > dwidth - 9) { 220 pbar_size = dwidth - 9; 221 label_size = 0; 222 /* -9 = "| - [" ... "] |" */ 223 } 224 if (pbar_size < 0) 225 label_size = dwidth - 8; 226 /* -8 = "| " ... " - |" */ 227 else if (label_size > (dwidth - pbar_size - 9) || wide) 228 label_size = no_labels ? 0 : dwidth - pbar_size - 9; 229 /* -9 = "| " ... " - [" ... "] |" */ 230 231 /* Hide labels if requested */ 232 if (no_labels) 233 label_size = 0; 234 235 /* Touch up the height (now that we know dwidth) */ 236 dheight += dialog_prompt_wrappedlines(pprompt, dwidth - 4, 0); 237 dheight += dialog_prompt_wrappedlines(aprompt, dwidth - 4, 1); 238 239 if (debug) 240 warnx("dheight = %i dwidth = %i fheight = %i", 241 dheight, dwidth, fheight); 242 243 /* Calculate left/right portions of % */ 244 pct_lsize = (pbar_size - 4) / 2; /* -4 == printf("%-3s%%", pct) */ 245 pct_rsize = pct_lsize; 246 /* If not evenly divisible by 2, increment the right-side */ 247 if ((pct_rsize + pct_rsize + 4) != pbar_size) 248 pct_rsize++; 249 250 /* Initialize "Done" text */ 251 if (done == NULL && (done = msg_done) == NULL) { 252 if ((done = getenv(ENV_MSG_DONE)) != NULL) 253 done_size = strlen(done); 254 else { 255 done_size = strlen(DPV_DONE_DEFAULT); 256 if ((done = malloc(done_size + 1)) == NULL) 257 errx(EXIT_FAILURE, "Out of memory?!"); 258 dprompt_free_mask |= FM_DONE; 259 snprintf(done, done_size + 1, DPV_DONE_DEFAULT); 260 } 261 } 262 if (pbar_size < done_size) { 263 done_lsize = done_rsize = 0; 264 *(done + pbar_size) = '\0'; 265 done_size = pbar_size; 266 } else { 267 /* Calculate left/right portions for mini-progressbar */ 268 done_lsize = (pbar_size - done_size) / 2; 269 done_rsize = done_lsize; 270 /* If not evenly divisible by 2, increment the right-side */ 271 if ((done_rsize + done_size + done_lsize) != pbar_size) 272 done_rsize++; 273 } 274 275 /* Initialize "Fail" text */ 276 if (fail == NULL && (fail = msg_fail) == NULL) { 277 if ((fail = getenv(ENV_MSG_FAIL)) != NULL) 278 fail_size = strlen(fail); 279 else { 280 fail_size = strlen(DPV_FAIL_DEFAULT); 281 if ((fail = malloc(fail_size + 1)) == NULL) 282 errx(EXIT_FAILURE, "Out of memory?!"); 283 dprompt_free_mask |= FM_FAIL; 284 snprintf(fail, fail_size + 1, DPV_FAIL_DEFAULT); 285 } 286 } 287 if (pbar_size < fail_size) { 288 fail_lsize = fail_rsize = 0; 289 *(fail + pbar_size) = '\0'; 290 fail_size = pbar_size; 291 } else { 292 /* Calculate left/right portions for mini-progressbar */ 293 fail_lsize = (pbar_size - fail_size) / 2; 294 fail_rsize = fail_lsize; 295 /* If not evenly divisible by 2, increment the right-side */ 296 if ((fail_rsize + fail_size + fail_lsize) != pbar_size) 297 fail_rsize++; 298 } 299 300 /* Initialize "Pending" text */ 301 if (pend == NULL && (pend = msg_pending) == NULL) { 302 if ((pend = getenv(ENV_MSG_PENDING)) != NULL) 303 pend_size = strlen(pend); 304 else { 305 pend_size = strlen(DPV_PENDING_DEFAULT); 306 if ((pend = malloc(pend_size + 1)) == NULL) 307 errx(EXIT_FAILURE, "Out of memory?!"); 308 dprompt_free_mask |= FM_PEND; 309 snprintf(pend, pend_size + 1, DPV_PENDING_DEFAULT); 310 } 311 } 312 if (pbar_size < pend_size) { 313 pend_lsize = pend_rsize = 0; 314 *(pend + pbar_size) = '\0'; 315 pend_size = pbar_size; 316 } else { 317 /* Calculate left/right portions for mini-progressbar */ 318 pend_lsize = (pbar_size - pend_size) / 2; 319 pend_rsize = pend_lsize; 320 /* If not evenly divisible by 2, increment the right-side */ 321 if ((pend_rsize + pend_lsize + pend_size) != pbar_size) 322 pend_rsize++; 323 } 324 325 if (debug) 326 warnx("label_size = %i pbar_size = %i", label_size, pbar_size); 327 328 dprompt_clear(); 329 } 330 331 /* 332 * Clear the [X]dialog(1) `--gauge' prompt buffer. 333 */ 334 void 335 dprompt_clear(void) 336 { 337 338 *dprompt = '\0'; 339 dprompt_pos = dprompt; 340 } 341 342 /* 343 * Append to the [X]dialog(1) `--gauge' prompt buffer. Syntax is like printf(3) 344 * and returns the number of bytes appended to the buffer. 345 */ 346 int 347 dprompt_add(const char *format, ...) 348 { 349 int len; 350 va_list ap; 351 352 if (dprompt_pos >= (dprompt + PROMPT_MAX)) 353 return (0); 354 355 va_start(ap, format); 356 len = vsnprintf(dprompt_pos, (size_t)(PROMPT_MAX - 357 (dprompt_pos - dprompt)), format, ap); 358 va_end(ap); 359 if (len == -1) 360 errx(EXIT_FAILURE, "%s: Oops, dprompt buffer overflow", 361 __func__); 362 363 if ((dprompt_pos + len) < (dprompt + PROMPT_MAX)) 364 dprompt_pos += len; 365 else 366 dprompt_pos = dprompt + PROMPT_MAX; 367 368 return (len); 369 } 370 371 /* 372 * Append active files to the [X]dialog(1) `--gauge' prompt buffer. Syntax 373 * requires a pointer to the head of the dpv_file_node linked-list. Returns the 374 * number of files processed successfully. 375 */ 376 static int 377 dprompt_add_files(struct dpv_file_node *file_list, 378 struct dpv_file_node *curfile, int pct) 379 { 380 char c; 381 char bold_code = 'b'; /* default: enabled */ 382 char color_code = '4'; /* default: blue */ 383 uint8_t after_curfile = curfile != NULL ? FALSE : TRUE; 384 uint8_t nls = 0; 385 char *cp; 386 char *lastline; 387 char *name; 388 const char *bg_code; 389 const char *estext; 390 const char *format; 391 enum dprompt_state dstate; 392 int estext_lsize; 393 int estext_rsize; 394 int estext_size; 395 int flabel_size; 396 int hlen; 397 int lsize; 398 int nlines = 0; 399 int nthfile = 0; 400 int pwidth; 401 int rsize; 402 struct dpv_file_node *fp; 403 char flabel[FLABEL_MAX + 1]; 404 char human[32]; 405 char pbar[pbar_size + 16]; /* +15 for optional color */ 406 char pbar_cap[sizeof(pbar)]; 407 char pbar_fill[sizeof(pbar)]; 408 409 410 /* Override color defaults with that of main progress bar */ 411 if (use_colors || use_shadow) { /* NB: shadow enables color */ 412 color_code = gauge_color[0]; 413 /* NB: str[1] aka bg is unused */ 414 bold_code = gauge_color[2]; 415 } 416 417 /* 418 * Create mini-progressbar for current file (if applicable) 419 */ 420 *pbar = '\0'; 421 if (pbar_size >= 0 && pct >= 0 && curfile != NULL && 422 (curfile->length >= 0 || dialog_test)) { 423 snprintf(pbar, pbar_size + 1, "%*s%3u%%%*s", pct_lsize, "", 424 pct, pct_rsize, ""); 425 if (use_color) { 426 /* Calculate the fill-width of progressbar */ 427 pwidth = pct * pbar_size / 100; 428 /* Round up based on one-tenth of a percent */ 429 if ((pct * pbar_size % 100) > 50) 430 pwidth++; 431 432 /* 433 * Make two copies of pbar. Make one represent the fill 434 * and the other the remainder (cap). We'll insert the 435 * ANSI delimiter in between. 436 */ 437 *pbar_fill = '\0'; 438 *pbar_cap = '\0'; 439 strncat(pbar_fill, (const char *)(pbar), dwidth); 440 *(pbar_fill + pwidth) = '\0'; 441 strncat(pbar_cap, (const char *)(pbar+pwidth), dwidth); 442 443 /* Finalize the mini [color] progressbar */ 444 snprintf(pbar, sizeof(pbar), 445 "\\Z%c\\Zr\\Z%c%s%s%s\\Zn", bold_code, color_code, 446 pbar_fill, "\\ZR", pbar_cap); 447 } 448 } 449 450 for (fp = file_list; fp != NULL; fp = fp->next) { 451 flabel_size = label_size; 452 name = fp->name; 453 nthfile++; 454 455 /* 456 * Support multiline filenames (where the filename is taken as 457 * the last line and the text leading up to the last line can 458 * be used as (for example) a heading/separator between files. 459 */ 460 if (use_dialog) 461 nls = dialog_prompt_nlstate(pprompt); 462 nlines += dialog_prompt_numlines(name, nls); 463 lastline = dialog_prompt_lastline(name, 1); 464 if (name != lastline) { 465 c = *lastline; 466 *lastline = '\0'; 467 dprompt_add("%s", name); 468 *lastline = c; 469 name = lastline; 470 } 471 472 /* Support color codes (for dialog(1,3)) in file names */ 473 if ((use_dialog || use_libdialog) && use_color) { 474 cp = name; 475 while (*cp != '\0') { 476 if (*cp == '\\' && *(cp + 1) != '\0' && 477 *(++cp) == 'Z' && *(cp + 1) != '\0') { 478 cp++; 479 flabel_size += 3; 480 } 481 cp++; 482 } 483 if (flabel_size > FLABEL_MAX) 484 flabel_size = FLABEL_MAX; 485 } 486 487 /* If no mini-progressbar, increase label width */ 488 if (pbar_size < 0 && flabel_size <= FLABEL_MAX - 2 && 489 no_labels == FALSE) 490 flabel_size += 2; 491 492 /* If name is too long, add an ellipsis */ 493 if (snprintf(flabel, flabel_size + 1, "%s", name) > 494 flabel_size) sprintf(flabel + flabel_size - 3, "..."); 495 496 /* 497 * Append the label (processing the current file differently) 498 */ 499 if (fp == curfile && pct < 100) { 500 /* 501 * Add an ellipsis to current file name if it will fit. 502 * There may be an ellipsis already from truncating the 503 * label (in which case, we already have one). 504 */ 505 cp = flabel + strlen(flabel); 506 if (cp < (flabel + flabel_size)) 507 snprintf(cp, flabel_size - 508 (cp - flabel) + 1, "..."); 509 510 /* Append label (with spinner and optional color) */ 511 dprompt_add("%s%-*s%s %c", use_color ? "\\Zb" : "", 512 flabel_size, flabel, use_color ? "\\Zn" : "", 513 spin_char()); 514 } else 515 dprompt_add("%-*s%s %s", flabel_size, 516 flabel, use_color ? "\\Zn" : "", " "); 517 518 /* 519 * Append pbar/status (processing the current file differently) 520 */ 521 dstate = DPROMPT_NONE; 522 if (fp->msg != NULL) 523 dstate = DPROMPT_CUSTOM_MSG; 524 else if (pbar_size < 0) 525 dstate = DPROMPT_NONE; 526 else if (pbar_size < 4) 527 dstate = DPROMPT_MINIMAL; 528 else if (after_curfile) 529 dstate = DPROMPT_PENDING; 530 else if (fp == curfile) { 531 if (*pbar == '\0') { 532 if (fp->length < 0) 533 dstate = DPROMPT_DETAILS; 534 else if (fp->status == DPV_STATUS_RUNNING) 535 dstate = DPROMPT_DETAILS; 536 else 537 dstate = DPROMPT_END_STATE; 538 } 539 else if (dialog_test) /* status/length ignored */ 540 dstate = pct < 100 ? 541 DPROMPT_PBAR : DPROMPT_END_STATE; 542 else if (fp->status == DPV_STATUS_RUNNING) 543 dstate = fp->length < 0 ? 544 DPROMPT_DETAILS : DPROMPT_PBAR; 545 else /* not running */ 546 dstate = fp->length < 0 ? 547 DPROMPT_DETAILS : DPROMPT_END_STATE; 548 } else { /* before curfile */ 549 if (dialog_test) 550 dstate = DPROMPT_END_STATE; 551 else 552 dstate = fp->length < 0 ? 553 DPROMPT_DETAILS : DPROMPT_END_STATE; 554 } 555 format = use_color ? 556 " [\\Z%c%s%-*s%s%-*s\\Zn]\\n" : 557 " [%-*s%s%-*s]\\n"; 558 if (fp->status == DPV_STATUS_FAILED) { 559 bg_code = "\\Zr\\Z1"; /* Red */ 560 estext_lsize = fail_lsize; 561 estext_rsize = fail_rsize; 562 estext_size = fail_size; 563 estext = fail; 564 } else { /* e.g., DPV_STATUS_DONE */ 565 bg_code = "\\Zr\\Z2"; /* Green */ 566 estext_lsize = done_lsize; 567 estext_rsize = done_rsize; 568 estext_size = done_size; 569 estext = done; 570 } 571 switch (dstate) { 572 case DPROMPT_PENDING: /* Future file(s) */ 573 dprompt_add(" [%-*s%s%-*s]\\n", 574 pend_lsize, "", pend, pend_rsize, ""); 575 break; 576 case DPROMPT_PBAR: /* Current file */ 577 dprompt_add(" [%s]\\n", pbar); 578 break; 579 case DPROMPT_END_STATE: /* Past/Current file(s) */ 580 if (use_color) 581 dprompt_add(format, bold_code, bg_code, 582 estext_lsize, "", estext, 583 estext_rsize, ""); 584 else 585 dprompt_add(format, 586 estext_lsize, "", estext, 587 estext_rsize, ""); 588 break; 589 case DPROMPT_DETAILS: /* Past/Current file(s) */ 590 humanize_number(human, pbar_size + 2, fp->read, "", 591 HN_AUTOSCALE, HN_NOSPACE | HN_DIVISOR_1000); 592 593 /* Calculate center alignment */ 594 hlen = (int)strlen(human); 595 lsize = (pbar_size - hlen) / 2; 596 rsize = lsize; 597 if ((lsize+hlen+rsize) != pbar_size) 598 rsize++; 599 600 if (use_color) 601 dprompt_add(format, bold_code, bg_code, 602 lsize, "", human, rsize, ""); 603 else 604 dprompt_add(format, 605 lsize, "", human, rsize, ""); 606 break; 607 case DPROMPT_CUSTOM_MSG: /* File-specific message override */ 608 snprintf(msg, PROMPT_MAX + 1, "%s", fp->msg); 609 if (pbar_size < (mesg_size = strlen(msg))) { 610 mesg_lsize = mesg_rsize = 0; 611 *(msg + pbar_size) = '\0'; 612 mesg_size = pbar_size; 613 } else { 614 mesg_lsize = (pbar_size - mesg_size) / 2; 615 mesg_rsize = mesg_lsize; 616 if ((mesg_rsize + mesg_size + mesg_lsize) 617 != pbar_size) 618 mesg_rsize++; 619 } 620 if (use_color) 621 dprompt_add(format, bold_code, bg_code, 622 mesg_lsize, "", msg, mesg_rsize, ""); 623 else 624 dprompt_add(format, mesg_lsize, "", msg, 625 mesg_rsize, ""); 626 break; 627 case DPROMPT_MINIMAL: /* Short progress bar, minimal room */ 628 if (use_color) 629 dprompt_add(format, bold_code, bg_code, 630 pbar_size, "", "", 0, ""); 631 else 632 dprompt_add(format, pbar_size, "", "", 0, ""); 633 break; 634 case DPROMPT_NONE: /* pbar_size < 0 */ 635 /* FALLTHROUGH */ 636 default: 637 dprompt_add(" \\n"); 638 /* 639 * NB: Leading space required for the case when 640 * spin_char() returns a single backslash [\] which 641 * without the space, changes the meaning of `\n' 642 */ 643 } 644 645 /* Stop building if we've hit the internal limit */ 646 if (nthfile >= display_limit) 647 break; 648 649 /* If this is the current file, all others are pending */ 650 if (fp == curfile) 651 after_curfile = TRUE; 652 } 653 654 /* 655 * Since we cannot change the height/width of the [X]dialog(1) widget 656 * after spawn, to make things look nice let's pad the height so that 657 * the `-a text' always appears in the same spot. 658 * 659 * NOTE: fheight is calculated in dprompt_init(). It represents the 660 * maximum height required to display the set of items (broken up into 661 * pieces of display_limit chunks) whose names contain the most 662 * newlines for any given set. 663 */ 664 while (nlines < fheight) { 665 dprompt_add("\n"); 666 nlines++; 667 } 668 669 return (nthfile); 670 } 671 672 /* 673 * Process the dpv_file_node linked-list of named files, re-generating the 674 * [X]dialog(1) `--gauge' prompt text for the current state of transfers. 675 */ 676 void 677 dprompt_recreate(struct dpv_file_node *file_list, 678 struct dpv_file_node *curfile, int pct) 679 { 680 size_t len; 681 682 /* 683 * Re-Build the prompt text 684 */ 685 dprompt_clear(); 686 if (display_limit > 0) 687 dprompt_add_files(file_list, curfile, pct); 688 689 /* Xdialog(1) requires newlines (a) escaped and (b) in triplicate */ 690 if (use_xdialog) { 691 /* Replace `\n' with `\n\\n\n' in dprompt */ 692 len = strlen(dprompt); 693 len += strcount(dprompt, "\\n") * 5; /* +5 chars per count */ 694 if (len > PROMPT_MAX) 695 errx(EXIT_FAILURE, "%s: Oops, dprompt buffer overflow " 696 "(%zu > %i)", __func__, len, PROMPT_MAX); 697 if (replaceall(dprompt, "\\n", "\n\\n\n") < 0) 698 err(EXIT_FAILURE, "%s: replaceall()", __func__); 699 } 700 else if (use_libdialog) 701 strexpandnl(dprompt); 702 } 703 704 /* 705 * Print the [X]dialog(1) `--gauge' prompt text to a buffer. 706 */ 707 int 708 dprompt_sprint(char * restrict str, const char *prefix, const char *append) 709 { 710 711 return (snprintf(str, PROMPT_MAX, "%s%s%s%s", use_color ? "\\Zn" : "", 712 prefix ? prefix : "", dprompt, append ? append : "")); 713 } 714 715 /* 716 * Print the [X]dialog(1) `--gauge' prompt text to file descriptor fd (could 717 * be STDOUT_FILENO or a pipe(2) file descriptor to actual [X]dialog(1)). 718 */ 719 void 720 dprompt_dprint(int fd, const char *prefix, const char *append, int overall) 721 { 722 int percent = gauge_percent; 723 724 if (overall >= 0 && overall <= 100) 725 gauge_percent = percent = overall; 726 dprintf(fd, "XXX\n%s%s%s%s\nXXX\n%i\n", use_color ? "\\Zn" : "", 727 prefix ? prefix : "", dprompt, append ? append : "", percent); 728 fsync(fd); 729 } 730 731 /* 732 * Print the dialog(3) `gauge' prompt text using libdialog. 733 */ 734 void 735 dprompt_libprint(const char *prefix, const char *append, int overall) 736 { 737 int percent = gauge_percent; 738 char buf[DPV_PPROMPT_MAX + DPV_APROMPT_MAX + DPV_DISPLAY_LIMIT * 1024]; 739 740 dprompt_sprint(buf, prefix, append); 741 742 if (overall >= 0 && overall <= 100) 743 gauge_percent = percent = overall; 744 gauge = dlg_reallocate_gauge(gauge, title == NULL ? "" : title, 745 buf, dheight, dwidth, percent); 746 dlg_update_gauge(gauge, percent); 747 } 748 749 /* 750 * Free allocated items initialized by dprompt_init() 751 */ 752 void 753 dprompt_free(void) 754 { 755 if ((dprompt_free_mask & FM_DONE) != 0) { 756 dprompt_free_mask ^= FM_DONE; 757 free(done); 758 done = NULL; 759 } 760 if ((dprompt_free_mask & FM_FAIL) != 0) { 761 dprompt_free_mask ^= FM_FAIL; 762 free(fail); 763 fail = NULL; 764 } 765 if ((dprompt_free_mask & FM_PEND) != 0) { 766 dprompt_free_mask ^= FM_PEND; 767 free(pend); 768 pend = NULL; 769 } 770 } 771