dprompt.c 21 KB

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