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