1041394f3SDevin Teske /*-
2041394f3SDevin Teske * Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
3041394f3SDevin Teske * All rights reserved.
4041394f3SDevin Teske *
5041394f3SDevin Teske * Redistribution and use in source and binary forms, with or without
6041394f3SDevin Teske * modification, are permitted provided that the following conditions
7041394f3SDevin Teske * are met:
8041394f3SDevin Teske * 1. Redistributions of source code must retain the above copyright
9041394f3SDevin Teske * notice, this list of conditions and the following disclaimer.
10041394f3SDevin Teske * 2. Redistributions in binary form must reproduce the above copyright
11041394f3SDevin Teske * notice, this list of conditions and the following disclaimer in the
12041394f3SDevin Teske * documentation and/or other materials provided with the distribution.
13041394f3SDevin Teske *
14041394f3SDevin Teske * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15041394f3SDevin Teske * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16041394f3SDevin Teske * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17041394f3SDevin Teske * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18041394f3SDevin Teske * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19041394f3SDevin Teske * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20041394f3SDevin Teske * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21041394f3SDevin Teske * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22041394f3SDevin Teske * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23041394f3SDevin Teske * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24041394f3SDevin Teske * SUCH DAMAGE.
25041394f3SDevin Teske */
26041394f3SDevin Teske
27041394f3SDevin Teske #include <sys/types.h>
28041394f3SDevin Teske
29041394f3SDevin Teske #define _BSD_SOURCE /* to get dprintf() prototype in stdio.h below */
30041394f3SDevin Teske #include <dialog.h>
31041394f3SDevin Teske #include <err.h>
32041394f3SDevin Teske #include <libutil.h>
33041394f3SDevin Teske #include <stdarg.h>
34041394f3SDevin Teske #include <stdio.h>
35041394f3SDevin Teske #include <stdlib.h>
36041394f3SDevin Teske #include <string.h>
37041394f3SDevin Teske #include <string_m.h>
38041394f3SDevin Teske #include <unistd.h>
39041394f3SDevin Teske
40041394f3SDevin Teske #include "dialog_util.h"
41041394f3SDevin Teske #include "dialogrc.h"
42041394f3SDevin Teske #include "dprompt.h"
43041394f3SDevin Teske #include "dpv.h"
44041394f3SDevin Teske #include "dpv_private.h"
45041394f3SDevin Teske
46041394f3SDevin Teske #define FLABEL_MAX 1024
47041394f3SDevin Teske
48041394f3SDevin Teske static int fheight = 0; /* initialized by dprompt_init() */
49041394f3SDevin Teske static char dprompt[PROMPT_MAX + 1] = "";
50041394f3SDevin Teske static char *dprompt_pos = (char *)(0); /* treated numerically */
51041394f3SDevin Teske
52041394f3SDevin Teske /* Display characteristics */
53041394f3SDevin Teske #define FM_DONE 0x01
54041394f3SDevin Teske #define FM_FAIL 0x02
55041394f3SDevin Teske #define FM_PEND 0x04
56041394f3SDevin Teske static uint8_t dprompt_free_mask;
57041394f3SDevin Teske static char *done = NULL;
58041394f3SDevin Teske static char *fail = NULL;
59041394f3SDevin Teske static char *pend = NULL;
60041394f3SDevin Teske int display_limit = DISPLAY_LIMIT_DEFAULT; /* Max entries to show */
61041394f3SDevin Teske int label_size = LABEL_SIZE_DEFAULT; /* Max width for labels */
62041394f3SDevin Teske int pbar_size = PBAR_SIZE_DEFAULT; /* Mini-progressbar size */
63041394f3SDevin Teske static int gauge_percent = 0;
64041394f3SDevin Teske static int done_size, done_lsize, done_rsize;
65041394f3SDevin Teske static int fail_size, fail_lsize, fail_rsize;
66041394f3SDevin Teske static int mesg_size, mesg_lsize, mesg_rsize;
67041394f3SDevin Teske static int pend_size, pend_lsize, pend_rsize;
68041394f3SDevin Teske static int pct_lsize, pct_rsize;
69041394f3SDevin Teske static void *gauge = NULL;
70041394f3SDevin Teske #define SPIN_SIZE 4
71041394f3SDevin Teske static char spin[SPIN_SIZE + 1] = "/-\\|";
72041394f3SDevin Teske static char msg[PROMPT_MAX + 1];
73041394f3SDevin Teske static char *spin_cp = spin;
74041394f3SDevin Teske
75041394f3SDevin Teske /* Function prototypes */
76041394f3SDevin Teske static char spin_char(void);
77041394f3SDevin Teske static int dprompt_add_files(struct dpv_file_node *file_list,
78041394f3SDevin Teske struct dpv_file_node *curfile, int pct);
79041394f3SDevin Teske
80041394f3SDevin Teske /*
81041394f3SDevin Teske * Returns a pointer to the current spin character in the spin string and
82041394f3SDevin Teske * advances the global position to the next character for the next call.
83041394f3SDevin Teske */
84041394f3SDevin Teske static char
spin_char(void)85041394f3SDevin Teske spin_char(void)
86041394f3SDevin Teske {
87041394f3SDevin Teske char ch;
88041394f3SDevin Teske
89*a0d7de3bSDevin Teske if (*spin_cp == '\0')
90041394f3SDevin Teske spin_cp = spin;
91041394f3SDevin Teske ch = *spin_cp;
92041394f3SDevin Teske
93041394f3SDevin Teske /* Advance the spinner to the next char */
94041394f3SDevin Teske if (++spin_cp >= (spin + SPIN_SIZE))
95041394f3SDevin Teske spin_cp = spin;
96041394f3SDevin Teske
97041394f3SDevin Teske return (ch);
98041394f3SDevin Teske }
99041394f3SDevin Teske
100041394f3SDevin Teske /*
101041394f3SDevin Teske * Initialize heights and widths based on various strings and environment
102041394f3SDevin Teske * variables (such as ENV_USE_COLOR).
103041394f3SDevin Teske */
104041394f3SDevin Teske void
dprompt_init(struct dpv_file_node * file_list)105041394f3SDevin Teske dprompt_init(struct dpv_file_node *file_list)
106041394f3SDevin Teske {
107041394f3SDevin Teske uint8_t nls = 0;
108041394f3SDevin Teske int len;
109041394f3SDevin Teske int max_cols;
110041394f3SDevin Teske int max_rows;
111041394f3SDevin Teske int nthfile;
112041394f3SDevin Teske int numlines;
113041394f3SDevin Teske struct dpv_file_node *curfile;
114041394f3SDevin Teske
115041394f3SDevin Teske /*
116041394f3SDevin Teske * Initialize dialog(3) `colors' support and draw backtitle
117041394f3SDevin Teske */
118041394f3SDevin Teske if (use_libdialog && !debug) {
119041394f3SDevin Teske init_dialog(stdin, stdout);
120041394f3SDevin Teske dialog_vars.colors = 1;
121041394f3SDevin Teske if (backtitle != NULL) {
122041394f3SDevin Teske dialog_vars.backtitle = (char *)backtitle;
123041394f3SDevin Teske dlg_put_backtitle();
124041394f3SDevin Teske }
125041394f3SDevin Teske }
126041394f3SDevin Teske
127041394f3SDevin Teske /* Calculate width of dialog(3) or [X]dialog(1) --gauge box */
128041394f3SDevin Teske dwidth = label_size + pbar_size + 9;
129041394f3SDevin Teske
130041394f3SDevin Teske /*
131041394f3SDevin Teske * Calculate height of dialog(3) or [X]dialog(1) --gauge box
132041394f3SDevin Teske */
133041394f3SDevin Teske dheight = 5;
134041394f3SDevin Teske max_rows = dialog_maxrows();
135041394f3SDevin Teske /* adjust max_rows for backtitle and/or dialog(3) statusLine */
136041394f3SDevin Teske if (backtitle != NULL)
137041394f3SDevin Teske max_rows -= use_shadow ? 3 : 2;
138041394f3SDevin Teske if (use_libdialog && use_shadow)
139041394f3SDevin Teske max_rows -= 2;
140041394f3SDevin Teske /* add lines for `-p text' */
141041394f3SDevin Teske numlines = dialog_prompt_numlines(pprompt, 0);
142041394f3SDevin Teske if (debug)
143041394f3SDevin Teske warnx("`-p text' is %i line%s long", numlines,
144041394f3SDevin Teske numlines == 1 ? "" : "s");
145041394f3SDevin Teske dheight += numlines;
146041394f3SDevin Teske /* adjust dheight for various implementations */
147041394f3SDevin Teske if (use_dialog) {
148041394f3SDevin Teske dheight -= dialog_prompt_nlstate(pprompt);
149041394f3SDevin Teske nls = dialog_prompt_nlstate(pprompt);
150041394f3SDevin Teske } else if (use_xdialog) {
151041394f3SDevin Teske if (pprompt == NULL || *pprompt == '\0')
152041394f3SDevin Teske dheight++;
153041394f3SDevin Teske } else if (use_libdialog) {
154041394f3SDevin Teske if (pprompt != NULL && *pprompt != '\0')
155041394f3SDevin Teske dheight--;
156041394f3SDevin Teske }
157041394f3SDevin Teske /* limit the number of display items (necessary per dialog(1,3)) */
158041394f3SDevin Teske if (display_limit == 0 || display_limit > DPV_DISPLAY_LIMIT)
159041394f3SDevin Teske display_limit = DPV_DISPLAY_LIMIT;
160041394f3SDevin Teske /* verify fheight will fit (stop if we hit 1) */
161041394f3SDevin Teske for (; display_limit > 0; display_limit--) {
162041394f3SDevin Teske nthfile = numlines = 0;
163041394f3SDevin Teske fheight = (int)dpv_nfiles > display_limit ?
164041394f3SDevin Teske (unsigned int)display_limit : dpv_nfiles;
165041394f3SDevin Teske for (curfile = file_list; curfile != NULL;
166041394f3SDevin Teske curfile = curfile->next) {
167041394f3SDevin Teske nthfile++;
168041394f3SDevin Teske numlines += dialog_prompt_numlines(curfile->name, nls);
169041394f3SDevin Teske if ((nthfile % display_limit) == 0) {
170041394f3SDevin Teske if (numlines > fheight)
171041394f3SDevin Teske fheight = numlines;
172041394f3SDevin Teske numlines = nthfile = 0;
173041394f3SDevin Teske }
174041394f3SDevin Teske }
175041394f3SDevin Teske if (numlines > fheight)
176041394f3SDevin Teske fheight = numlines;
177041394f3SDevin Teske if ((dheight + fheight +
178041394f3SDevin Teske (int)dialog_prompt_numlines(aprompt, use_dialog) -
179041394f3SDevin Teske (use_dialog ? (int)dialog_prompt_nlstate(aprompt) : 0))
180041394f3SDevin Teske <= max_rows)
181041394f3SDevin Teske break;
182041394f3SDevin Teske }
183041394f3SDevin Teske /* don't show any items if we run the risk of hitting a blank set */
184041394f3SDevin Teske if ((max_rows - (use_shadow ? 5 : 4)) >= fheight)
185041394f3SDevin Teske dheight += fheight;
186041394f3SDevin Teske else
187041394f3SDevin Teske fheight = 0;
188041394f3SDevin Teske /* add lines for `-a text' */
189041394f3SDevin Teske numlines = dialog_prompt_numlines(aprompt, use_dialog);
190041394f3SDevin Teske if (debug)
191041394f3SDevin Teske warnx("`-a text' is %i line%s long", numlines,
192041394f3SDevin Teske numlines == 1 ? "" : "s");
193041394f3SDevin Teske dheight += numlines;
194041394f3SDevin Teske
195041394f3SDevin Teske /* If using Xdialog(1), adjust accordingly (based on testing) */
196041394f3SDevin Teske if (use_xdialog)
197041394f3SDevin Teske dheight += dheight / 4;
198041394f3SDevin Teske
199041394f3SDevin Teske /* For wide mode, long prefix (`pprompt') or append (`aprompt')
200041394f3SDevin Teske * strings will bump width */
201041394f3SDevin Teske if (wide) {
202041394f3SDevin Teske len = (int)dialog_prompt_longestline(pprompt, 0); /* !nls */
203041394f3SDevin Teske if ((len + 4) > dwidth)
204041394f3SDevin Teske dwidth = len + 4;
205041394f3SDevin Teske len = (int)dialog_prompt_longestline(aprompt, 1); /* nls */
206041394f3SDevin Teske if ((len + 4) > dwidth)
207041394f3SDevin Teske dwidth = len + 4;
208041394f3SDevin Teske }
209041394f3SDevin Teske
210041394f3SDevin Teske /* Enforce width constraints to maximum values */
211041394f3SDevin Teske max_cols = dialog_maxcols();
212041394f3SDevin Teske if (max_cols > 0 && dwidth > max_cols)
213041394f3SDevin Teske dwidth = max_cols;
214041394f3SDevin Teske
215041394f3SDevin Teske /* Optimize widths to sane values*/
216041394f3SDevin Teske if (pbar_size > dwidth - 9) {
217041394f3SDevin Teske pbar_size = dwidth - 9;
218041394f3SDevin Teske label_size = 0;
219041394f3SDevin Teske /* -9 = "| - [" ... "] |" */
220041394f3SDevin Teske }
221041394f3SDevin Teske if (pbar_size < 0)
222041394f3SDevin Teske label_size = dwidth - 8;
223041394f3SDevin Teske /* -8 = "| " ... " - |" */
224041394f3SDevin Teske else if (label_size > (dwidth - pbar_size - 9) || wide)
225041394f3SDevin Teske label_size = no_labels ? 0 : dwidth - pbar_size - 9;
226041394f3SDevin Teske /* -9 = "| " ... " - [" ... "] |" */
227041394f3SDevin Teske
228041394f3SDevin Teske /* Hide labels if requested */
229041394f3SDevin Teske if (no_labels)
230041394f3SDevin Teske label_size = 0;
231041394f3SDevin Teske
232041394f3SDevin Teske /* Touch up the height (now that we know dwidth) */
233041394f3SDevin Teske dheight += dialog_prompt_wrappedlines(pprompt, dwidth - 4, 0);
234041394f3SDevin Teske dheight += dialog_prompt_wrappedlines(aprompt, dwidth - 4, 1);
235041394f3SDevin Teske
236041394f3SDevin Teske if (debug)
237041394f3SDevin Teske warnx("dheight = %i dwidth = %i fheight = %i",
238041394f3SDevin Teske dheight, dwidth, fheight);
239041394f3SDevin Teske
240041394f3SDevin Teske /* Calculate left/right portions of % */
241041394f3SDevin Teske pct_lsize = (pbar_size - 4) / 2; /* -4 == printf("%-3s%%", pct) */
242041394f3SDevin Teske pct_rsize = pct_lsize;
243041394f3SDevin Teske /* If not evenly divisible by 2, increment the right-side */
244041394f3SDevin Teske if ((pct_rsize + pct_rsize + 4) != pbar_size)
245041394f3SDevin Teske pct_rsize++;
246041394f3SDevin Teske
247041394f3SDevin Teske /* Initialize "Done" text */
248041394f3SDevin Teske if (done == NULL && (done = msg_done) == NULL) {
249041394f3SDevin Teske if ((done = getenv(ENV_MSG_DONE)) != NULL)
250041394f3SDevin Teske done_size = strlen(done);
251041394f3SDevin Teske else {
252041394f3SDevin Teske done_size = strlen(DPV_DONE_DEFAULT);
253041394f3SDevin Teske if ((done = malloc(done_size + 1)) == NULL)
254041394f3SDevin Teske errx(EXIT_FAILURE, "Out of memory?!");
255041394f3SDevin Teske dprompt_free_mask |= FM_DONE;
256041394f3SDevin Teske snprintf(done, done_size + 1, DPV_DONE_DEFAULT);
257041394f3SDevin Teske }
258041394f3SDevin Teske }
259041394f3SDevin Teske if (pbar_size < done_size) {
260041394f3SDevin Teske done_lsize = done_rsize = 0;
261041394f3SDevin Teske *(done + pbar_size) = '\0';
262041394f3SDevin Teske done_size = pbar_size;
263041394f3SDevin Teske } else {
264041394f3SDevin Teske /* Calculate left/right portions for mini-progressbar */
265041394f3SDevin Teske done_lsize = (pbar_size - done_size) / 2;
266041394f3SDevin Teske done_rsize = done_lsize;
267041394f3SDevin Teske /* If not evenly divisible by 2, increment the right-side */
268041394f3SDevin Teske if ((done_rsize + done_size + done_lsize) != pbar_size)
269041394f3SDevin Teske done_rsize++;
270041394f3SDevin Teske }
271041394f3SDevin Teske
272041394f3SDevin Teske /* Initialize "Fail" text */
273041394f3SDevin Teske if (fail == NULL && (fail = msg_fail) == NULL) {
274041394f3SDevin Teske if ((fail = getenv(ENV_MSG_FAIL)) != NULL)
275041394f3SDevin Teske fail_size = strlen(fail);
276041394f3SDevin Teske else {
277041394f3SDevin Teske fail_size = strlen(DPV_FAIL_DEFAULT);
278041394f3SDevin Teske if ((fail = malloc(fail_size + 1)) == NULL)
279041394f3SDevin Teske errx(EXIT_FAILURE, "Out of memory?!");
280041394f3SDevin Teske dprompt_free_mask |= FM_FAIL;
281041394f3SDevin Teske snprintf(fail, fail_size + 1, DPV_FAIL_DEFAULT);
282041394f3SDevin Teske }
283041394f3SDevin Teske }
284041394f3SDevin Teske if (pbar_size < fail_size) {
285041394f3SDevin Teske fail_lsize = fail_rsize = 0;
286041394f3SDevin Teske *(fail + pbar_size) = '\0';
287041394f3SDevin Teske fail_size = pbar_size;
288041394f3SDevin Teske } else {
289041394f3SDevin Teske /* Calculate left/right portions for mini-progressbar */
290041394f3SDevin Teske fail_lsize = (pbar_size - fail_size) / 2;
291041394f3SDevin Teske fail_rsize = fail_lsize;
292041394f3SDevin Teske /* If not evenly divisible by 2, increment the right-side */
293041394f3SDevin Teske if ((fail_rsize + fail_size + fail_lsize) != pbar_size)
294041394f3SDevin Teske fail_rsize++;
295041394f3SDevin Teske }
296041394f3SDevin Teske
297041394f3SDevin Teske /* Initialize "Pending" text */
298041394f3SDevin Teske if (pend == NULL && (pend = msg_pending) == NULL) {
299041394f3SDevin Teske if ((pend = getenv(ENV_MSG_PENDING)) != NULL)
300041394f3SDevin Teske pend_size = strlen(pend);
301041394f3SDevin Teske else {
302041394f3SDevin Teske pend_size = strlen(DPV_PENDING_DEFAULT);
303041394f3SDevin Teske if ((pend = malloc(pend_size + 1)) == NULL)
304041394f3SDevin Teske errx(EXIT_FAILURE, "Out of memory?!");
305041394f3SDevin Teske dprompt_free_mask |= FM_PEND;
306041394f3SDevin Teske snprintf(pend, pend_size + 1, DPV_PENDING_DEFAULT);
307041394f3SDevin Teske }
308041394f3SDevin Teske }
309041394f3SDevin Teske if (pbar_size < pend_size) {
310041394f3SDevin Teske pend_lsize = pend_rsize = 0;
311041394f3SDevin Teske *(pend + pbar_size) = '\0';
312041394f3SDevin Teske pend_size = pbar_size;
313041394f3SDevin Teske } else {
314041394f3SDevin Teske /* Calculate left/right portions for mini-progressbar */
315041394f3SDevin Teske pend_lsize = (pbar_size - pend_size) / 2;
316041394f3SDevin Teske pend_rsize = pend_lsize;
317041394f3SDevin Teske /* If not evenly divisible by 2, increment the right-side */
318041394f3SDevin Teske if ((pend_rsize + pend_lsize + pend_size) != pbar_size)
319041394f3SDevin Teske pend_rsize++;
320041394f3SDevin Teske }
321041394f3SDevin Teske
322041394f3SDevin Teske if (debug)
323041394f3SDevin Teske warnx("label_size = %i pbar_size = %i", label_size, pbar_size);
324041394f3SDevin Teske
325041394f3SDevin Teske dprompt_clear();
326041394f3SDevin Teske }
327041394f3SDevin Teske
328041394f3SDevin Teske /*
329041394f3SDevin Teske * Clear the [X]dialog(1) `--gauge' prompt buffer.
330041394f3SDevin Teske */
331041394f3SDevin Teske void
dprompt_clear(void)332041394f3SDevin Teske dprompt_clear(void)
333041394f3SDevin Teske {
334041394f3SDevin Teske
335041394f3SDevin Teske *dprompt = '\0';
336041394f3SDevin Teske dprompt_pos = dprompt;
337041394f3SDevin Teske }
338041394f3SDevin Teske
339041394f3SDevin Teske /*
340041394f3SDevin Teske * Append to the [X]dialog(1) `--gauge' prompt buffer. Syntax is like printf(3)
341041394f3SDevin Teske * and returns the number of bytes appended to the buffer.
342041394f3SDevin Teske */
343041394f3SDevin Teske int
dprompt_add(const char * format,...)344041394f3SDevin Teske dprompt_add(const char *format, ...)
345041394f3SDevin Teske {
346041394f3SDevin Teske int len;
347041394f3SDevin Teske va_list ap;
348041394f3SDevin Teske
349041394f3SDevin Teske if (dprompt_pos >= (dprompt + PROMPT_MAX))
350041394f3SDevin Teske return (0);
351041394f3SDevin Teske
352041394f3SDevin Teske va_start(ap, format);
353041394f3SDevin Teske len = vsnprintf(dprompt_pos, (size_t)(PROMPT_MAX -
354041394f3SDevin Teske (dprompt_pos - dprompt)), format, ap);
355041394f3SDevin Teske va_end(ap);
356041394f3SDevin Teske if (len == -1)
357041394f3SDevin Teske errx(EXIT_FAILURE, "%s: Oops, dprompt buffer overflow",
358041394f3SDevin Teske __func__);
359041394f3SDevin Teske
360041394f3SDevin Teske if ((dprompt_pos + len) < (dprompt + PROMPT_MAX))
361041394f3SDevin Teske dprompt_pos += len;
362041394f3SDevin Teske else
363041394f3SDevin Teske dprompt_pos = dprompt + PROMPT_MAX;
364041394f3SDevin Teske
365041394f3SDevin Teske return (len);
366041394f3SDevin Teske }
367041394f3SDevin Teske
368041394f3SDevin Teske /*
369041394f3SDevin Teske * Append active files to the [X]dialog(1) `--gauge' prompt buffer. Syntax
370041394f3SDevin Teske * requires a pointer to the head of the dpv_file_node linked-list. Returns the
371041394f3SDevin Teske * number of files processed successfully.
372041394f3SDevin Teske */
373041394f3SDevin Teske static int
dprompt_add_files(struct dpv_file_node * file_list,struct dpv_file_node * curfile,int pct)374041394f3SDevin Teske dprompt_add_files(struct dpv_file_node *file_list,
375041394f3SDevin Teske struct dpv_file_node *curfile, int pct)
376041394f3SDevin Teske {
377041394f3SDevin Teske char c;
378041394f3SDevin Teske char bold_code = 'b'; /* default: enabled */
379041394f3SDevin Teske char color_code = '4'; /* default: blue */
380041394f3SDevin Teske uint8_t after_curfile = curfile != NULL ? FALSE : TRUE;
381041394f3SDevin Teske uint8_t nls = 0;
382041394f3SDevin Teske char *cp;
383041394f3SDevin Teske char *lastline;
384041394f3SDevin Teske char *name;
385041394f3SDevin Teske const char *bg_code;
386041394f3SDevin Teske const char *estext;
387041394f3SDevin Teske const char *format;
388041394f3SDevin Teske enum dprompt_state dstate;
389041394f3SDevin Teske int estext_lsize;
390041394f3SDevin Teske int estext_rsize;
391041394f3SDevin Teske int flabel_size;
392041394f3SDevin Teske int hlen;
393041394f3SDevin Teske int lsize;
394041394f3SDevin Teske int nlines = 0;
395041394f3SDevin Teske int nthfile = 0;
396041394f3SDevin Teske int pwidth;
397041394f3SDevin Teske int rsize;
398041394f3SDevin Teske struct dpv_file_node *fp;
399041394f3SDevin Teske char flabel[FLABEL_MAX + 1];
400041394f3SDevin Teske char human[32];
401041394f3SDevin Teske char pbar[pbar_size + 16]; /* +15 for optional color */
402041394f3SDevin Teske char pbar_cap[sizeof(pbar)];
403041394f3SDevin Teske char pbar_fill[sizeof(pbar)];
404041394f3SDevin Teske
405041394f3SDevin Teske
406041394f3SDevin Teske /* Override color defaults with that of main progress bar */
407041394f3SDevin Teske if (use_colors || use_shadow) { /* NB: shadow enables color */
408041394f3SDevin Teske color_code = gauge_color[0];
409041394f3SDevin Teske /* NB: str[1] aka bg is unused */
410041394f3SDevin Teske bold_code = gauge_color[2];
411041394f3SDevin Teske }
412041394f3SDevin Teske
413041394f3SDevin Teske /*
414041394f3SDevin Teske * Create mini-progressbar for current file (if applicable)
415041394f3SDevin Teske */
416041394f3SDevin Teske *pbar = '\0';
417041394f3SDevin Teske if (pbar_size >= 0 && pct >= 0 && curfile != NULL &&
418041394f3SDevin Teske (curfile->length >= 0 || dialog_test)) {
419041394f3SDevin Teske snprintf(pbar, pbar_size + 1, "%*s%3u%%%*s", pct_lsize, "",
420041394f3SDevin Teske pct, pct_rsize, "");
421041394f3SDevin Teske if (use_color) {
422041394f3SDevin Teske /* Calculate the fill-width of progressbar */
423041394f3SDevin Teske pwidth = pct * pbar_size / 100;
424041394f3SDevin Teske /* Round up based on one-tenth of a percent */
425041394f3SDevin Teske if ((pct * pbar_size % 100) > 50)
426041394f3SDevin Teske pwidth++;
427041394f3SDevin Teske
428041394f3SDevin Teske /*
429041394f3SDevin Teske * Make two copies of pbar. Make one represent the fill
430041394f3SDevin Teske * and the other the remainder (cap). We'll insert the
431041394f3SDevin Teske * ANSI delimiter in between.
432041394f3SDevin Teske */
433041394f3SDevin Teske *pbar_fill = '\0';
434041394f3SDevin Teske *pbar_cap = '\0';
435041394f3SDevin Teske strncat(pbar_fill, (const char *)(pbar), dwidth);
436041394f3SDevin Teske *(pbar_fill + pwidth) = '\0';
437041394f3SDevin Teske strncat(pbar_cap, (const char *)(pbar+pwidth), dwidth);
438041394f3SDevin Teske
439041394f3SDevin Teske /* Finalize the mini [color] progressbar */
440041394f3SDevin Teske snprintf(pbar, sizeof(pbar),
441041394f3SDevin Teske "\\Z%c\\Zr\\Z%c%s%s%s\\Zn", bold_code, color_code,
442041394f3SDevin Teske pbar_fill, "\\ZR", pbar_cap);
443041394f3SDevin Teske }
444041394f3SDevin Teske }
445041394f3SDevin Teske
446041394f3SDevin Teske for (fp = file_list; fp != NULL; fp = fp->next) {
447041394f3SDevin Teske flabel_size = label_size;
448041394f3SDevin Teske name = fp->name;
449041394f3SDevin Teske nthfile++;
450041394f3SDevin Teske
451041394f3SDevin Teske /*
452041394f3SDevin Teske * Support multiline filenames (where the filename is taken as
453041394f3SDevin Teske * the last line and the text leading up to the last line can
454041394f3SDevin Teske * be used as (for example) a heading/separator between files.
455041394f3SDevin Teske */
456041394f3SDevin Teske if (use_dialog)
457041394f3SDevin Teske nls = dialog_prompt_nlstate(pprompt);
458041394f3SDevin Teske nlines += dialog_prompt_numlines(name, nls);
459041394f3SDevin Teske lastline = dialog_prompt_lastline(name, 1);
460041394f3SDevin Teske if (name != lastline) {
461041394f3SDevin Teske c = *lastline;
462041394f3SDevin Teske *lastline = '\0';
463041394f3SDevin Teske dprompt_add("%s", name);
464041394f3SDevin Teske *lastline = c;
465041394f3SDevin Teske name = lastline;
466041394f3SDevin Teske }
467041394f3SDevin Teske
468041394f3SDevin Teske /* Support color codes (for dialog(1,3)) in file names */
469041394f3SDevin Teske if ((use_dialog || use_libdialog) && use_color) {
470041394f3SDevin Teske cp = name;
471041394f3SDevin Teske while (*cp != '\0') {
472041394f3SDevin Teske if (*cp == '\\' && *(cp + 1) != '\0' &&
473041394f3SDevin Teske *(++cp) == 'Z' && *(cp + 1) != '\0') {
474041394f3SDevin Teske cp++;
475041394f3SDevin Teske flabel_size += 3;
476041394f3SDevin Teske }
477041394f3SDevin Teske cp++;
478041394f3SDevin Teske }
479041394f3SDevin Teske if (flabel_size > FLABEL_MAX)
480041394f3SDevin Teske flabel_size = FLABEL_MAX;
481041394f3SDevin Teske }
482041394f3SDevin Teske
483041394f3SDevin Teske /* If no mini-progressbar, increase label width */
484041394f3SDevin Teske if (pbar_size < 0 && flabel_size <= FLABEL_MAX - 2 &&
485041394f3SDevin Teske no_labels == FALSE)
486041394f3SDevin Teske flabel_size += 2;
487041394f3SDevin Teske
488041394f3SDevin Teske /* If name is too long, add an ellipsis */
489041394f3SDevin Teske if (snprintf(flabel, flabel_size + 1, "%s", name) >
490041394f3SDevin Teske flabel_size) sprintf(flabel + flabel_size - 3, "...");
491041394f3SDevin Teske
492041394f3SDevin Teske /*
493041394f3SDevin Teske * Append the label (processing the current file differently)
494041394f3SDevin Teske */
495041394f3SDevin Teske if (fp == curfile && pct < 100) {
496041394f3SDevin Teske /*
497041394f3SDevin Teske * Add an ellipsis to current file name if it will fit.
498041394f3SDevin Teske * There may be an ellipsis already from truncating the
499041394f3SDevin Teske * label (in which case, we already have one).
500041394f3SDevin Teske */
501041394f3SDevin Teske cp = flabel + strlen(flabel);
502041394f3SDevin Teske if (cp < (flabel + flabel_size))
503041394f3SDevin Teske snprintf(cp, flabel_size -
504041394f3SDevin Teske (cp - flabel) + 1, "...");
505041394f3SDevin Teske
506041394f3SDevin Teske /* Append label (with spinner and optional color) */
507041394f3SDevin Teske dprompt_add("%s%-*s%s %c", use_color ? "\\Zb" : "",
508041394f3SDevin Teske flabel_size, flabel, use_color ? "\\Zn" : "",
509041394f3SDevin Teske spin_char());
510041394f3SDevin Teske } else
511041394f3SDevin Teske dprompt_add("%-*s%s %s", flabel_size,
512041394f3SDevin Teske flabel, use_color ? "\\Zn" : "", " ");
513041394f3SDevin Teske
514041394f3SDevin Teske /*
515041394f3SDevin Teske * Append pbar/status (processing the current file differently)
516041394f3SDevin Teske */
517041394f3SDevin Teske dstate = DPROMPT_NONE;
518041394f3SDevin Teske if (fp->msg != NULL)
519041394f3SDevin Teske dstate = DPROMPT_CUSTOM_MSG;
520041394f3SDevin Teske else if (pbar_size < 0)
521041394f3SDevin Teske dstate = DPROMPT_NONE;
522041394f3SDevin Teske else if (pbar_size < 4)
523041394f3SDevin Teske dstate = DPROMPT_MINIMAL;
524041394f3SDevin Teske else if (after_curfile)
525041394f3SDevin Teske dstate = DPROMPT_PENDING;
526041394f3SDevin Teske else if (fp == curfile) {
527041394f3SDevin Teske if (*pbar == '\0') {
528041394f3SDevin Teske if (fp->length < 0)
529041394f3SDevin Teske dstate = DPROMPT_DETAILS;
530041394f3SDevin Teske else if (fp->status == DPV_STATUS_RUNNING)
531041394f3SDevin Teske dstate = DPROMPT_DETAILS;
532041394f3SDevin Teske else
533041394f3SDevin Teske dstate = DPROMPT_END_STATE;
534041394f3SDevin Teske }
535041394f3SDevin Teske else if (dialog_test) /* status/length ignored */
536041394f3SDevin Teske dstate = pct < 100 ?
537041394f3SDevin Teske DPROMPT_PBAR : DPROMPT_END_STATE;
538041394f3SDevin Teske else if (fp->status == DPV_STATUS_RUNNING)
539041394f3SDevin Teske dstate = fp->length < 0 ?
540041394f3SDevin Teske DPROMPT_DETAILS : DPROMPT_PBAR;
541041394f3SDevin Teske else /* not running */
542041394f3SDevin Teske dstate = fp->length < 0 ?
543041394f3SDevin Teske DPROMPT_DETAILS : DPROMPT_END_STATE;
544041394f3SDevin Teske } else { /* before curfile */
545041394f3SDevin Teske if (dialog_test)
546041394f3SDevin Teske dstate = DPROMPT_END_STATE;
547041394f3SDevin Teske else
548041394f3SDevin Teske dstate = fp->length < 0 ?
549041394f3SDevin Teske DPROMPT_DETAILS : DPROMPT_END_STATE;
550041394f3SDevin Teske }
551041394f3SDevin Teske format = use_color ?
552041394f3SDevin Teske " [\\Z%c%s%-*s%s%-*s\\Zn]\\n" :
553041394f3SDevin Teske " [%-*s%s%-*s]\\n";
554041394f3SDevin Teske if (fp->status == DPV_STATUS_FAILED) {
555041394f3SDevin Teske bg_code = "\\Zr\\Z1"; /* Red */
556041394f3SDevin Teske estext_lsize = fail_lsize;
557041394f3SDevin Teske estext_rsize = fail_rsize;
558041394f3SDevin Teske estext = fail;
559041394f3SDevin Teske } else { /* e.g., DPV_STATUS_DONE */
560041394f3SDevin Teske bg_code = "\\Zr\\Z2"; /* Green */
561041394f3SDevin Teske estext_lsize = done_lsize;
562041394f3SDevin Teske estext_rsize = done_rsize;
563041394f3SDevin Teske estext = done;
564041394f3SDevin Teske }
565041394f3SDevin Teske switch (dstate) {
566041394f3SDevin Teske case DPROMPT_PENDING: /* Future file(s) */
567041394f3SDevin Teske dprompt_add(" [%-*s%s%-*s]\\n",
568041394f3SDevin Teske pend_lsize, "", pend, pend_rsize, "");
569041394f3SDevin Teske break;
570041394f3SDevin Teske case DPROMPT_PBAR: /* Current file */
571041394f3SDevin Teske dprompt_add(" [%s]\\n", pbar);
572041394f3SDevin Teske break;
573041394f3SDevin Teske case DPROMPT_END_STATE: /* Past/Current file(s) */
574041394f3SDevin Teske if (use_color)
575041394f3SDevin Teske dprompt_add(format, bold_code, bg_code,
576041394f3SDevin Teske estext_lsize, "", estext,
577041394f3SDevin Teske estext_rsize, "");
578041394f3SDevin Teske else
579041394f3SDevin Teske dprompt_add(format,
580041394f3SDevin Teske estext_lsize, "", estext,
581041394f3SDevin Teske estext_rsize, "");
582041394f3SDevin Teske break;
583041394f3SDevin Teske case DPROMPT_DETAILS: /* Past/Current file(s) */
584041394f3SDevin Teske humanize_number(human, pbar_size + 2, fp->read, "",
585041394f3SDevin Teske HN_AUTOSCALE, HN_NOSPACE | HN_DIVISOR_1000);
586041394f3SDevin Teske
587041394f3SDevin Teske /* Calculate center alignment */
588041394f3SDevin Teske hlen = (int)strlen(human);
589041394f3SDevin Teske lsize = (pbar_size - hlen) / 2;
590041394f3SDevin Teske rsize = lsize;
591041394f3SDevin Teske if ((lsize+hlen+rsize) != pbar_size)
592041394f3SDevin Teske rsize++;
593041394f3SDevin Teske
594041394f3SDevin Teske if (use_color)
595041394f3SDevin Teske dprompt_add(format, bold_code, bg_code,
596041394f3SDevin Teske lsize, "", human, rsize, "");
597041394f3SDevin Teske else
598041394f3SDevin Teske dprompt_add(format,
599041394f3SDevin Teske lsize, "", human, rsize, "");
600041394f3SDevin Teske break;
601041394f3SDevin Teske case DPROMPT_CUSTOM_MSG: /* File-specific message override */
602041394f3SDevin Teske snprintf(msg, PROMPT_MAX + 1, "%s", fp->msg);
603041394f3SDevin Teske if (pbar_size < (mesg_size = strlen(msg))) {
604041394f3SDevin Teske mesg_lsize = mesg_rsize = 0;
605041394f3SDevin Teske *(msg + pbar_size) = '\0';
606041394f3SDevin Teske mesg_size = pbar_size;
607041394f3SDevin Teske } else {
608041394f3SDevin Teske mesg_lsize = (pbar_size - mesg_size) / 2;
609041394f3SDevin Teske mesg_rsize = mesg_lsize;
610041394f3SDevin Teske if ((mesg_rsize + mesg_size + mesg_lsize)
611041394f3SDevin Teske != pbar_size)
612041394f3SDevin Teske mesg_rsize++;
613041394f3SDevin Teske }
614041394f3SDevin Teske if (use_color)
615041394f3SDevin Teske dprompt_add(format, bold_code, bg_code,
616041394f3SDevin Teske mesg_lsize, "", msg, mesg_rsize, "");
617041394f3SDevin Teske else
618041394f3SDevin Teske dprompt_add(format, mesg_lsize, "", msg,
619041394f3SDevin Teske mesg_rsize, "");
620041394f3SDevin Teske break;
621041394f3SDevin Teske case DPROMPT_MINIMAL: /* Short progress bar, minimal room */
622041394f3SDevin Teske if (use_color)
623041394f3SDevin Teske dprompt_add(format, bold_code, bg_code,
624041394f3SDevin Teske pbar_size, "", "", 0, "");
625041394f3SDevin Teske else
626041394f3SDevin Teske dprompt_add(format, pbar_size, "", "", 0, "");
627041394f3SDevin Teske break;
628041394f3SDevin Teske case DPROMPT_NONE: /* pbar_size < 0 */
629041394f3SDevin Teske /* FALLTHROUGH */
630041394f3SDevin Teske default:
631041394f3SDevin Teske dprompt_add(" \\n");
632041394f3SDevin Teske /*
633041394f3SDevin Teske * NB: Leading space required for the case when
634041394f3SDevin Teske * spin_char() returns a single backslash [\] which
635041394f3SDevin Teske * without the space, changes the meaning of `\n'
636041394f3SDevin Teske */
637041394f3SDevin Teske }
638041394f3SDevin Teske
639041394f3SDevin Teske /* Stop building if we've hit the internal limit */
640041394f3SDevin Teske if (nthfile >= display_limit)
641041394f3SDevin Teske break;
642041394f3SDevin Teske
643041394f3SDevin Teske /* If this is the current file, all others are pending */
644041394f3SDevin Teske if (fp == curfile)
645041394f3SDevin Teske after_curfile = TRUE;
646041394f3SDevin Teske }
647041394f3SDevin Teske
648041394f3SDevin Teske /*
649041394f3SDevin Teske * Since we cannot change the height/width of the [X]dialog(1) widget
650041394f3SDevin Teske * after spawn, to make things look nice let's pad the height so that
651041394f3SDevin Teske * the `-a text' always appears in the same spot.
652041394f3SDevin Teske *
653041394f3SDevin Teske * NOTE: fheight is calculated in dprompt_init(). It represents the
654041394f3SDevin Teske * maximum height required to display the set of items (broken up into
655041394f3SDevin Teske * pieces of display_limit chunks) whose names contain the most
656041394f3SDevin Teske * newlines for any given set.
657041394f3SDevin Teske */
658041394f3SDevin Teske while (nlines < fheight) {
659041394f3SDevin Teske dprompt_add("\n");
660041394f3SDevin Teske nlines++;
661041394f3SDevin Teske }
662041394f3SDevin Teske
663041394f3SDevin Teske return (nthfile);
664041394f3SDevin Teske }
665041394f3SDevin Teske
666041394f3SDevin Teske /*
667041394f3SDevin Teske * Process the dpv_file_node linked-list of named files, re-generating the
668041394f3SDevin Teske * [X]dialog(1) `--gauge' prompt text for the current state of transfers.
669041394f3SDevin Teske */
670041394f3SDevin Teske void
dprompt_recreate(struct dpv_file_node * file_list,struct dpv_file_node * curfile,int pct)671041394f3SDevin Teske dprompt_recreate(struct dpv_file_node *file_list,
672041394f3SDevin Teske struct dpv_file_node *curfile, int pct)
673041394f3SDevin Teske {
674041394f3SDevin Teske size_t len;
675041394f3SDevin Teske
676041394f3SDevin Teske /*
677041394f3SDevin Teske * Re-Build the prompt text
678041394f3SDevin Teske */
679041394f3SDevin Teske dprompt_clear();
680041394f3SDevin Teske if (display_limit > 0)
681041394f3SDevin Teske dprompt_add_files(file_list, curfile, pct);
682041394f3SDevin Teske
683041394f3SDevin Teske /* Xdialog(1) requires newlines (a) escaped and (b) in triplicate */
684041394f3SDevin Teske if (use_xdialog) {
685041394f3SDevin Teske /* Replace `\n' with `\n\\n\n' in dprompt */
686041394f3SDevin Teske len = strlen(dprompt);
687041394f3SDevin Teske len += strcount(dprompt, "\\n") * 5; /* +5 chars per count */
688041394f3SDevin Teske if (len > PROMPT_MAX)
689041394f3SDevin Teske errx(EXIT_FAILURE, "%s: Oops, dprompt buffer overflow "
690041394f3SDevin Teske "(%zu > %i)", __func__, len, PROMPT_MAX);
691041394f3SDevin Teske if (replaceall(dprompt, "\\n", "\n\\n\n") < 0)
692041394f3SDevin Teske err(EXIT_FAILURE, "%s: replaceall()", __func__);
693041394f3SDevin Teske }
694041394f3SDevin Teske else if (use_libdialog)
695041394f3SDevin Teske strexpandnl(dprompt);
696041394f3SDevin Teske }
697041394f3SDevin Teske
698041394f3SDevin Teske /*
699041394f3SDevin Teske * Print the [X]dialog(1) `--gauge' prompt text to a buffer.
700041394f3SDevin Teske */
701041394f3SDevin Teske int
dprompt_sprint(char * restrict str,const char * prefix,const char * append)702041394f3SDevin Teske dprompt_sprint(char * restrict str, const char *prefix, const char *append)
703041394f3SDevin Teske {
704041394f3SDevin Teske
705041394f3SDevin Teske return (snprintf(str, PROMPT_MAX, "%s%s%s%s", use_color ? "\\Zn" : "",
706041394f3SDevin Teske prefix ? prefix : "", dprompt, append ? append : ""));
707041394f3SDevin Teske }
708041394f3SDevin Teske
709041394f3SDevin Teske /*
710041394f3SDevin Teske * Print the [X]dialog(1) `--gauge' prompt text to file descriptor fd (could
711041394f3SDevin Teske * be STDOUT_FILENO or a pipe(2) file descriptor to actual [X]dialog(1)).
712041394f3SDevin Teske */
713041394f3SDevin Teske void
dprompt_dprint(int fd,const char * prefix,const char * append,int overall)714041394f3SDevin Teske dprompt_dprint(int fd, const char *prefix, const char *append, int overall)
715041394f3SDevin Teske {
716041394f3SDevin Teske int percent = gauge_percent;
717041394f3SDevin Teske
718041394f3SDevin Teske if (overall >= 0 && overall <= 100)
719041394f3SDevin Teske gauge_percent = percent = overall;
720041394f3SDevin Teske dprintf(fd, "XXX\n%s%s%s%s\nXXX\n%i\n", use_color ? "\\Zn" : "",
721041394f3SDevin Teske prefix ? prefix : "", dprompt, append ? append : "", percent);
722041394f3SDevin Teske fsync(fd);
723041394f3SDevin Teske }
724041394f3SDevin Teske
725041394f3SDevin Teske /*
726041394f3SDevin Teske * Print the dialog(3) `gauge' prompt text using libdialog.
727041394f3SDevin Teske */
728041394f3SDevin Teske void
dprompt_libprint(const char * prefix,const char * append,int overall)729041394f3SDevin Teske dprompt_libprint(const char *prefix, const char *append, int overall)
730041394f3SDevin Teske {
731041394f3SDevin Teske int percent = gauge_percent;
732041394f3SDevin Teske char buf[DPV_PPROMPT_MAX + DPV_APROMPT_MAX + DPV_DISPLAY_LIMIT * 1024];
733041394f3SDevin Teske
734041394f3SDevin Teske dprompt_sprint(buf, prefix, append);
735041394f3SDevin Teske
736041394f3SDevin Teske if (overall >= 0 && overall <= 100)
737041394f3SDevin Teske gauge_percent = percent = overall;
738041394f3SDevin Teske gauge = dlg_reallocate_gauge(gauge, title == NULL ? "" : title,
739041394f3SDevin Teske buf, dheight, dwidth, percent);
740041394f3SDevin Teske dlg_update_gauge(gauge, percent);
741041394f3SDevin Teske }
742041394f3SDevin Teske
743041394f3SDevin Teske /*
744041394f3SDevin Teske * Free allocated items initialized by dprompt_init()
745041394f3SDevin Teske */
746041394f3SDevin Teske void
dprompt_free(void)747041394f3SDevin Teske dprompt_free(void)
748041394f3SDevin Teske {
749041394f3SDevin Teske if ((dprompt_free_mask & FM_DONE) != 0) {
750041394f3SDevin Teske dprompt_free_mask ^= FM_DONE;
751041394f3SDevin Teske free(done);
752041394f3SDevin Teske done = NULL;
753041394f3SDevin Teske }
754041394f3SDevin Teske if ((dprompt_free_mask & FM_FAIL) != 0) {
755041394f3SDevin Teske dprompt_free_mask ^= FM_FAIL;
756041394f3SDevin Teske free(fail);
757041394f3SDevin Teske fail = NULL;
758041394f3SDevin Teske }
759041394f3SDevin Teske if ((dprompt_free_mask & FM_PEND) != 0) {
760041394f3SDevin Teske dprompt_free_mask ^= FM_PEND;
761041394f3SDevin Teske free(pend);
762041394f3SDevin Teske pend = NULL;
763041394f3SDevin Teske }
764041394f3SDevin Teske }
765