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 flabel_size; 395 int hlen; 396 int lsize; 397 int nlines = 0; 398 int nthfile = 0; 399 int pwidth; 400 int rsize; 401 struct dpv_file_node *fp; 402 char flabel[FLABEL_MAX + 1]; 403 char human[32]; 404 char pbar[pbar_size + 16]; /* +15 for optional color */ 405 char pbar_cap[sizeof(pbar)]; 406 char pbar_fill[sizeof(pbar)]; 407 408 409 /* Override color defaults with that of main progress bar */ 410 if (use_colors || use_shadow) { /* NB: shadow enables color */ 411 color_code = gauge_color[0]; 412 /* NB: str[1] aka bg is unused */ 413 bold_code = gauge_color[2]; 414 } 415 416 /* 417 * Create mini-progressbar for current file (if applicable) 418 */ 419 *pbar = '\0'; 420 if (pbar_size >= 0 && pct >= 0 && curfile != NULL && 421 (curfile->length >= 0 || dialog_test)) { 422 snprintf(pbar, pbar_size + 1, "%*s%3u%%%*s", pct_lsize, "", 423 pct, pct_rsize, ""); 424 if (use_color) { 425 /* Calculate the fill-width of progressbar */ 426 pwidth = pct * pbar_size / 100; 427 /* Round up based on one-tenth of a percent */ 428 if ((pct * pbar_size % 100) > 50) 429 pwidth++; 430 431 /* 432 * Make two copies of pbar. Make one represent the fill 433 * and the other the remainder (cap). We'll insert the 434 * ANSI delimiter in between. 435 */ 436 *pbar_fill = '\0'; 437 *pbar_cap = '\0'; 438 strncat(pbar_fill, (const char *)(pbar), dwidth); 439 *(pbar_fill + pwidth) = '\0'; 440 strncat(pbar_cap, (const char *)(pbar+pwidth), dwidth); 441 442 /* Finalize the mini [color] progressbar */ 443 snprintf(pbar, sizeof(pbar), 444 "\\Z%c\\Zr\\Z%c%s%s%s\\Zn", bold_code, color_code, 445 pbar_fill, "\\ZR", pbar_cap); 446 } 447 } 448 449 for (fp = file_list; fp != NULL; fp = fp->next) { 450 flabel_size = label_size; 451 name = fp->name; 452 nthfile++; 453 454 /* 455 * Support multiline filenames (where the filename is taken as 456 * the last line and the text leading up to the last line can 457 * be used as (for example) a heading/separator between files. 458 */ 459 if (use_dialog) 460 nls = dialog_prompt_nlstate(pprompt); 461 nlines += dialog_prompt_numlines(name, nls); 462 lastline = dialog_prompt_lastline(name, 1); 463 if (name != lastline) { 464 c = *lastline; 465 *lastline = '\0'; 466 dprompt_add("%s", name); 467 *lastline = c; 468 name = lastline; 469 } 470 471 /* Support color codes (for dialog(1,3)) in file names */ 472 if ((use_dialog || use_libdialog) && use_color) { 473 cp = name; 474 while (*cp != '\0') { 475 if (*cp == '\\' && *(cp + 1) != '\0' && 476 *(++cp) == 'Z' && *(cp + 1) != '\0') { 477 cp++; 478 flabel_size += 3; 479 } 480 cp++; 481 } 482 if (flabel_size > FLABEL_MAX) 483 flabel_size = FLABEL_MAX; 484 } 485 486 /* If no mini-progressbar, increase label width */ 487 if (pbar_size < 0 && flabel_size <= FLABEL_MAX - 2 && 488 no_labels == FALSE) 489 flabel_size += 2; 490 491 /* If name is too long, add an ellipsis */ 492 if (snprintf(flabel, flabel_size + 1, "%s", name) > 493 flabel_size) sprintf(flabel + flabel_size - 3, "..."); 494 495 /* 496 * Append the label (processing the current file differently) 497 */ 498 if (fp == curfile && pct < 100) { 499 /* 500 * Add an ellipsis to current file name if it will fit. 501 * There may be an ellipsis already from truncating the 502 * label (in which case, we already have one). 503 */ 504 cp = flabel + strlen(flabel); 505 if (cp < (flabel + flabel_size)) 506 snprintf(cp, flabel_size - 507 (cp - flabel) + 1, "..."); 508 509 /* Append label (with spinner and optional color) */ 510 dprompt_add("%s%-*s%s %c", use_color ? "\\Zb" : "", 511 flabel_size, flabel, use_color ? "\\Zn" : "", 512 spin_char()); 513 } else 514 dprompt_add("%-*s%s %s", flabel_size, 515 flabel, use_color ? "\\Zn" : "", " "); 516 517 /* 518 * Append pbar/status (processing the current file differently) 519 */ 520 dstate = DPROMPT_NONE; 521 if (fp->msg != NULL) 522 dstate = DPROMPT_CUSTOM_MSG; 523 else if (pbar_size < 0) 524 dstate = DPROMPT_NONE; 525 else if (pbar_size < 4) 526 dstate = DPROMPT_MINIMAL; 527 else if (after_curfile) 528 dstate = DPROMPT_PENDING; 529 else if (fp == curfile) { 530 if (*pbar == '\0') { 531 if (fp->length < 0) 532 dstate = DPROMPT_DETAILS; 533 else if (fp->status == DPV_STATUS_RUNNING) 534 dstate = DPROMPT_DETAILS; 535 else 536 dstate = DPROMPT_END_STATE; 537 } 538 else if (dialog_test) /* status/length ignored */ 539 dstate = pct < 100 ? 540 DPROMPT_PBAR : DPROMPT_END_STATE; 541 else if (fp->status == DPV_STATUS_RUNNING) 542 dstate = fp->length < 0 ? 543 DPROMPT_DETAILS : DPROMPT_PBAR; 544 else /* not running */ 545 dstate = fp->length < 0 ? 546 DPROMPT_DETAILS : DPROMPT_END_STATE; 547 } else { /* before curfile */ 548 if (dialog_test) 549 dstate = DPROMPT_END_STATE; 550 else 551 dstate = fp->length < 0 ? 552 DPROMPT_DETAILS : DPROMPT_END_STATE; 553 } 554 format = use_color ? 555 " [\\Z%c%s%-*s%s%-*s\\Zn]\\n" : 556 " [%-*s%s%-*s]\\n"; 557 if (fp->status == DPV_STATUS_FAILED) { 558 bg_code = "\\Zr\\Z1"; /* Red */ 559 estext_lsize = fail_lsize; 560 estext_rsize = fail_rsize; 561 estext = fail; 562 } else { /* e.g., DPV_STATUS_DONE */ 563 bg_code = "\\Zr\\Z2"; /* Green */ 564 estext_lsize = done_lsize; 565 estext_rsize = done_rsize; 566 estext = done; 567 } 568 switch (dstate) { 569 case DPROMPT_PENDING: /* Future file(s) */ 570 dprompt_add(" [%-*s%s%-*s]\\n", 571 pend_lsize, "", pend, pend_rsize, ""); 572 break; 573 case DPROMPT_PBAR: /* Current file */ 574 dprompt_add(" [%s]\\n", pbar); 575 break; 576 case DPROMPT_END_STATE: /* Past/Current file(s) */ 577 if (use_color) 578 dprompt_add(format, bold_code, bg_code, 579 estext_lsize, "", estext, 580 estext_rsize, ""); 581 else 582 dprompt_add(format, 583 estext_lsize, "", estext, 584 estext_rsize, ""); 585 break; 586 case DPROMPT_DETAILS: /* Past/Current file(s) */ 587 humanize_number(human, pbar_size + 2, fp->read, "", 588 HN_AUTOSCALE, HN_NOSPACE | HN_DIVISOR_1000); 589 590 /* Calculate center alignment */ 591 hlen = (int)strlen(human); 592 lsize = (pbar_size - hlen) / 2; 593 rsize = lsize; 594 if ((lsize+hlen+rsize) != pbar_size) 595 rsize++; 596 597 if (use_color) 598 dprompt_add(format, bold_code, bg_code, 599 lsize, "", human, rsize, ""); 600 else 601 dprompt_add(format, 602 lsize, "", human, rsize, ""); 603 break; 604 case DPROMPT_CUSTOM_MSG: /* File-specific message override */ 605 snprintf(msg, PROMPT_MAX + 1, "%s", fp->msg); 606 if (pbar_size < (mesg_size = strlen(msg))) { 607 mesg_lsize = mesg_rsize = 0; 608 *(msg + pbar_size) = '\0'; 609 mesg_size = pbar_size; 610 } else { 611 mesg_lsize = (pbar_size - mesg_size) / 2; 612 mesg_rsize = mesg_lsize; 613 if ((mesg_rsize + mesg_size + mesg_lsize) 614 != pbar_size) 615 mesg_rsize++; 616 } 617 if (use_color) 618 dprompt_add(format, bold_code, bg_code, 619 mesg_lsize, "", msg, mesg_rsize, ""); 620 else 621 dprompt_add(format, mesg_lsize, "", msg, 622 mesg_rsize, ""); 623 break; 624 case DPROMPT_MINIMAL: /* Short progress bar, minimal room */ 625 if (use_color) 626 dprompt_add(format, bold_code, bg_code, 627 pbar_size, "", "", 0, ""); 628 else 629 dprompt_add(format, pbar_size, "", "", 0, ""); 630 break; 631 case DPROMPT_NONE: /* pbar_size < 0 */ 632 /* FALLTHROUGH */ 633 default: 634 dprompt_add(" \\n"); 635 /* 636 * NB: Leading space required for the case when 637 * spin_char() returns a single backslash [\] which 638 * without the space, changes the meaning of `\n' 639 */ 640 } 641 642 /* Stop building if we've hit the internal limit */ 643 if (nthfile >= display_limit) 644 break; 645 646 /* If this is the current file, all others are pending */ 647 if (fp == curfile) 648 after_curfile = TRUE; 649 } 650 651 /* 652 * Since we cannot change the height/width of the [X]dialog(1) widget 653 * after spawn, to make things look nice let's pad the height so that 654 * the `-a text' always appears in the same spot. 655 * 656 * NOTE: fheight is calculated in dprompt_init(). It represents the 657 * maximum height required to display the set of items (broken up into 658 * pieces of display_limit chunks) whose names contain the most 659 * newlines for any given set. 660 */ 661 while (nlines < fheight) { 662 dprompt_add("\n"); 663 nlines++; 664 } 665 666 return (nthfile); 667 } 668 669 /* 670 * Process the dpv_file_node linked-list of named files, re-generating the 671 * [X]dialog(1) `--gauge' prompt text for the current state of transfers. 672 */ 673 void 674 dprompt_recreate(struct dpv_file_node *file_list, 675 struct dpv_file_node *curfile, int pct) 676 { 677 size_t len; 678 679 /* 680 * Re-Build the prompt text 681 */ 682 dprompt_clear(); 683 if (display_limit > 0) 684 dprompt_add_files(file_list, curfile, pct); 685 686 /* Xdialog(1) requires newlines (a) escaped and (b) in triplicate */ 687 if (use_xdialog) { 688 /* Replace `\n' with `\n\\n\n' in dprompt */ 689 len = strlen(dprompt); 690 len += strcount(dprompt, "\\n") * 5; /* +5 chars per count */ 691 if (len > PROMPT_MAX) 692 errx(EXIT_FAILURE, "%s: Oops, dprompt buffer overflow " 693 "(%zu > %i)", __func__, len, PROMPT_MAX); 694 if (replaceall(dprompt, "\\n", "\n\\n\n") < 0) 695 err(EXIT_FAILURE, "%s: replaceall()", __func__); 696 } 697 else if (use_libdialog) 698 strexpandnl(dprompt); 699 } 700 701 /* 702 * Print the [X]dialog(1) `--gauge' prompt text to a buffer. 703 */ 704 int 705 dprompt_sprint(char * restrict str, const char *prefix, const char *append) 706 { 707 708 return (snprintf(str, PROMPT_MAX, "%s%s%s%s", use_color ? "\\Zn" : "", 709 prefix ? prefix : "", dprompt, append ? append : "")); 710 } 711 712 /* 713 * Print the [X]dialog(1) `--gauge' prompt text to file descriptor fd (could 714 * be STDOUT_FILENO or a pipe(2) file descriptor to actual [X]dialog(1)). 715 */ 716 void 717 dprompt_dprint(int fd, const char *prefix, const char *append, int overall) 718 { 719 int percent = gauge_percent; 720 721 if (overall >= 0 && overall <= 100) 722 gauge_percent = percent = overall; 723 dprintf(fd, "XXX\n%s%s%s%s\nXXX\n%i\n", use_color ? "\\Zn" : "", 724 prefix ? prefix : "", dprompt, append ? append : "", percent); 725 fsync(fd); 726 } 727 728 /* 729 * Print the dialog(3) `gauge' prompt text using libdialog. 730 */ 731 void 732 dprompt_libprint(const char *prefix, const char *append, int overall) 733 { 734 int percent = gauge_percent; 735 char buf[DPV_PPROMPT_MAX + DPV_APROMPT_MAX + DPV_DISPLAY_LIMIT * 1024]; 736 737 dprompt_sprint(buf, prefix, append); 738 739 if (overall >= 0 && overall <= 100) 740 gauge_percent = percent = overall; 741 gauge = dlg_reallocate_gauge(gauge, title == NULL ? "" : title, 742 buf, dheight, dwidth, percent); 743 dlg_update_gauge(gauge, percent); 744 } 745 746 /* 747 * Free allocated items initialized by dprompt_init() 748 */ 749 void 750 dprompt_free(void) 751 { 752 if ((dprompt_free_mask & FM_DONE) != 0) { 753 dprompt_free_mask ^= FM_DONE; 754 free(done); 755 done = NULL; 756 } 757 if ((dprompt_free_mask & FM_FAIL) != 0) { 758 dprompt_free_mask ^= FM_FAIL; 759 free(fail); 760 fail = NULL; 761 } 762 if ((dprompt_free_mask & FM_PEND) != 0) { 763 dprompt_free_mask ^= FM_PEND; 764 free(pend); 765 pend = NULL; 766 } 767 } 768