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
spin_char(void)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
dprompt_init(struct dpv_file_node * file_list)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
dprompt_clear(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
dprompt_add(const char * format,...)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
dprompt_add_files(struct dpv_file_node * file_list,struct dpv_file_node * curfile,int pct)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
dprompt_recreate(struct dpv_file_node * file_list,struct dpv_file_node * curfile,int pct)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
dprompt_sprint(char * restrict str,const char * prefix,const char * append)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
dprompt_dprint(int fd,const char * prefix,const char * append,int overall)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
dprompt_libprint(const char * prefix,const char * append,int overall)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
dprompt_free(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