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