1 /*-
2 * Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/stat.h>
31 #include <sys/time.h>
32 #include <sys/types.h>
33 #include <sys/wait.h>
34
35 #include <ctype.h>
36 #include <dialog.h>
37 #include <err.h>
38 #include <limits.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <string_m.h>
43 #include <unistd.h>
44
45 #include "dialog_util.h"
46 #include "dialogrc.h"
47 #include "dprompt.h"
48 #include "dpv.h"
49 #include "dpv_private.h"
50 #include "status.h"
51 #include "util.h"
52
53 /* Test Mechanics (Only used when dpv_config.options |= DPV_TEST_MODE) */
54 #define INCREMENT 1 /* Increment % per-pass test-mode */
55 #define XDIALOG_INCREMENT 15 /* different for slower Xdialog(1) */
56 static uint8_t increment = INCREMENT;
57
58 /* Debugging */
59 uint8_t debug = FALSE;
60
61 /* Data to process */
62 int dpv_interrupt = FALSE;
63 int dpv_abort = FALSE;
64 unsigned int dpv_nfiles = 0;
65
66 /* Data processing */
67 long long dpv_overall_read = 0;
68 static char pathbuf[PATH_MAX];
69
70 /* Extra display information */
71 uint8_t no_labels = FALSE; /* dpv_config.options & DPV_NO_LABELS */
72 uint8_t wide = FALSE; /* dpv_config.options & DPV_WIDE_MODE */
73 char *aprompt = NULL; /* dpv_config.aprompt */
74 char *msg_done = NULL; /* dpv_config.msg_done */
75 char *msg_fail = NULL; /* dpv_config.msg_fail */
76 char *msg_pending = NULL; /* dpv_config.msg_pending */
77 char *pprompt = NULL; /* dpv_config.pprompt */
78
79 /* Status-Line format for when using dialog(3) */
80 static const char *status_format_custom = NULL;
81 static char status_format_default[DPV_STATUS_FORMAT_MAX];
82
83 /*
84 * Takes a pointer to a dpv_config structure containing layout details and
85 * pointer to initial element in a linked-list of dpv_file_node structures,
86 * each presenting a file to process. Executes the `action' function passed-in
87 * as a member to the `config' structure argument.
88 */
89 int
dpv(struct dpv_config * config,struct dpv_file_node * file_list)90 dpv(struct dpv_config *config, struct dpv_file_node *file_list)
91 {
92 char c;
93 uint8_t keep_going;
94 uint8_t nls = FALSE; /* See dialog_prompt_nlstate() */
95 uint8_t no_overrun = FALSE;
96 uint8_t pprompt_nls = FALSE; /* See dialog_prompt_nlstate() */
97 uint8_t shrink_label_size = FALSE;
98 mode_t mask;
99 uint16_t options;
100 char *cp;
101 char *fc;
102 char *last;
103 char *name;
104 char *output;
105 const char *status_fmt;
106 const char *path_fmt;
107 enum dpv_display display_type;
108 enum dpv_output output_type;
109 enum dpv_status status;
110 int (*action)(struct dpv_file_node *file, int out);
111 int backslash;
112 int dialog_last_update = 0;
113 int dialog_old_nthfile = 0;
114 int dialog_old_seconds = -1;
115 int dialog_out = STDOUT_FILENO;
116 int dialog_update_usec = 0;
117 int dialog_updates_per_second;
118 int files_left;
119 int max_cols;
120 int nthfile = 0;
121 int output_out;
122 int overall = 0;
123 int pct;
124 int res;
125 int seconds;
126 int status_last_update = 0;
127 int status_old_nthfile = 0;
128 int status_old_seconds = -1;
129 int status_update_usec = 0;
130 int status_updates_per_second;
131 pid_t output_pid;
132 pid_t pid;
133 size_t len;
134 struct dpv_file_node *curfile;
135 struct dpv_file_node *first_file;
136 struct dpv_file_node *list_head;
137 struct timeval now;
138 struct timeval start;
139 char init_prompt[PROMPT_MAX + 1] = "";
140
141 /* Initialize globals to default values */
142 aprompt = NULL;
143 pprompt = NULL;
144 options = 0;
145 action = NULL;
146 backtitle = NULL;
147 debug = FALSE;
148 dialog_test = FALSE;
149 dialog_updates_per_second = DIALOG_UPDATES_PER_SEC;
150 display_limit = DISPLAY_LIMIT_DEFAULT;
151 display_type = DPV_DISPLAY_LIBDIALOG;
152 label_size = LABEL_SIZE_DEFAULT;
153 msg_done = NULL;
154 msg_fail = NULL;
155 msg_pending = NULL;
156 no_labels = FALSE;
157 output = NULL;
158 output_type = DPV_OUTPUT_NONE;
159 pbar_size = PBAR_SIZE_DEFAULT;
160 status_format_custom = NULL;
161 status_updates_per_second = STATUS_UPDATES_PER_SEC;
162 title = NULL;
163 wide = FALSE;
164
165 /* Process config options (overriding defaults) */
166 if (config != NULL) {
167 if (config->aprompt != NULL) {
168 if (aprompt == NULL) {
169 aprompt = malloc(DPV_APROMPT_MAX);
170 if (aprompt == NULL)
171 return (-1);
172 }
173 snprintf(aprompt, DPV_APROMPT_MAX, "%s",
174 config->aprompt);
175 }
176 if (config->pprompt != NULL) {
177 if (pprompt == NULL) {
178 pprompt = malloc(DPV_PPROMPT_MAX + 2);
179 /* +2 is for implicit "\n" appended later */
180 if (pprompt == NULL)
181 return (-1);
182 }
183 snprintf(pprompt, DPV_APROMPT_MAX, "%s",
184 config->pprompt);
185 }
186
187 options = config->options;
188 action = config->action;
189 backtitle = config->backtitle;
190 debug = config->debug;
191 dialog_test = ((options & DPV_TEST_MODE) != 0);
192 dialog_updates_per_second = config->dialog_updates_per_second;
193 display_limit = config->display_limit;
194 display_type = config->display_type;
195 label_size = config->label_size;
196 msg_done = (char *)config->msg_done;
197 msg_fail = (char *)config->msg_fail;
198 msg_pending = (char *)config->msg_pending;
199 no_labels = ((options & DPV_NO_LABELS) != 0);
200 no_overrun = ((options & DPV_NO_OVERRUN) != 0);
201 output = config->output;
202 output_type = config->output_type;
203 pbar_size = config->pbar_size;
204 status_updates_per_second = config->status_updates_per_second;
205 title = config->title;
206 wide = ((options & DPV_WIDE_MODE) != 0);
207
208 /* Enforce some minimums (pedantic) */
209 if (display_limit < -1)
210 display_limit = -1;
211 if (label_size < -1)
212 label_size = -1;
213 if (pbar_size < -1)
214 pbar_size = -1;
215
216 /* For the mini-pbar, -1 means hide, zero is invalid unless
217 * only one file is given */
218 if (pbar_size == 0) {
219 if (file_list == NULL || file_list->next == NULL)
220 pbar_size = -1;
221 else
222 pbar_size = PBAR_SIZE_DEFAULT;
223 }
224
225 /* For the label, -1 means auto-size, zero is invalid unless
226 * specifically requested through the use of options flag */
227 if (label_size == 0 && no_labels == FALSE)
228 label_size = LABEL_SIZE_DEFAULT;
229
230 /* Status update should not be zero */
231 if (status_updates_per_second == 0)
232 status_updates_per_second = STATUS_UPDATES_PER_SEC;
233 } /* config != NULL */
234
235 /* Process the type of display we've been requested to produce */
236 switch (display_type) {
237 case DPV_DISPLAY_STDOUT:
238 debug = TRUE;
239 use_color = FALSE;
240 use_dialog = FALSE;
241 use_libdialog = FALSE;
242 use_xdialog = FALSE;
243 break;
244 case DPV_DISPLAY_DIALOG:
245 use_color = TRUE;
246 use_dialog = TRUE;
247 use_libdialog = FALSE;
248 use_xdialog = FALSE;
249 break;
250 case DPV_DISPLAY_XDIALOG:
251 snprintf(dialog, PATH_MAX, XDIALOG);
252 use_color = FALSE;
253 use_dialog = FALSE;
254 use_libdialog = FALSE;
255 use_xdialog = TRUE;
256 break;
257 default:
258 use_color = TRUE;
259 use_dialog = FALSE;
260 use_libdialog = TRUE;
261 use_xdialog = FALSE;
262 break;
263 } /* display_type */
264
265 /* Enforce additional minimums that require knowing our display type */
266 if (dialog_updates_per_second == 0)
267 dialog_updates_per_second = use_xdialog ?
268 XDIALOG_UPDATES_PER_SEC : DIALOG_UPDATES_PER_SEC;
269
270 /* Allow forceful override of use_color */
271 if (config != NULL && (config->options & DPV_USE_COLOR) != 0)
272 use_color = TRUE;
273
274 /* Count the number of files in provided list of dpv_file_node's */
275 if (use_dialog && pprompt != NULL && *pprompt != '\0')
276 pprompt_nls = dialog_prompt_nlstate(pprompt);
277
278 max_cols = dialog_maxcols();
279 if (label_size == -1)
280 shrink_label_size = TRUE;
281
282 /* Process file arguments */
283 for (curfile = file_list; curfile != NULL; curfile = curfile->next) {
284 dpv_nfiles++;
285
286 /* dialog(3) only expands literal newlines */
287 if (use_libdialog) strexpandnl(curfile->name);
288
289 /* Optionally calculate label size for file */
290 if (shrink_label_size) {
291 nls = FALSE;
292 name = curfile->name;
293 if (curfile == file_list)
294 nls = pprompt_nls;
295 last = (char *)dialog_prompt_lastline(name, nls);
296 if (use_dialog) {
297 c = *last;
298 *last = '\0';
299 nls = dialog_prompt_nlstate(name);
300 *last = c;
301 }
302 len = dialog_prompt_longestline(last, nls);
303 if ((int)len > (label_size - 3)) {
304 if (label_size > 0)
305 label_size += 3;
306 label_size = len;
307 /* Room for ellipsis (unless NULL) */
308 if (label_size > 0)
309 label_size += 3;
310 }
311
312 if (max_cols > 0 && label_size > (max_cols - pbar_size
313 - 9))
314 label_size = max_cols - pbar_size - 9;
315 }
316
317 if (debug)
318 warnx("label=[%s] path=[%s] size=%lli",
319 curfile->name, curfile->path, curfile->length);
320 } /* file_list */
321
322 /* Optionally process the contents of DIALOGRC (~/.dialogrc) */
323 if (use_dialog) {
324 res = parse_dialogrc();
325 if (debug && res == 0) {
326 warnx("Successfully read `%s' config file", DIALOGRC);
327 warnx("use_shadow = %i (Boolean)", use_shadow);
328 warnx("use_colors = %i (Boolean)", use_colors);
329 warnx("gauge_color=[%s] (FBH)", gauge_color);
330 }
331 } else if (use_libdialog) {
332 init_dialog(stdin, stdout);
333 use_shadow = dialog_state.use_shadow;
334 use_colors = dialog_state.use_colors;
335 gauge_color[0] = 48 + dlg_color_table[GAUGE_ATTR].fg;
336 gauge_color[1] = 48 + dlg_color_table[GAUGE_ATTR].bg;
337 gauge_color[2] = dlg_color_table[GAUGE_ATTR].hilite ?
338 'b' : 'B';
339 gauge_color[3] = '\0';
340 end_dialog();
341 if (debug) {
342 warnx("Finished initializing dialog(3) library");
343 warnx("use_shadow = %i (Boolean)", use_shadow);
344 warnx("use_colors = %i (Boolean)", use_colors);
345 warnx("gauge_color=[%s] (FBH)", gauge_color);
346 }
347 }
348
349 /* Enable mini progress bar automatically for stdin streams if unable
350 * to calculate progress (missing `lines:' syntax). */
351 if (dpv_nfiles <= 1 && file_list != NULL && file_list->length < 0 &&
352 !dialog_test)
353 pbar_size = PBAR_SIZE_DEFAULT;
354
355 /* If $USE_COLOR is set and non-NULL enable color; otherwise disable */
356 if ((cp = getenv(ENV_USE_COLOR)) != 0)
357 use_color = *cp != '\0' ? 1 : 0;
358
359 /* Print error and return `-1' if not given at least one name */
360 if (dpv_nfiles == 0) {
361 warnx("%s: no labels provided", __func__);
362 return (-1);
363 } else if (debug)
364 warnx("%s: %u label%s provided", __func__, dpv_nfiles,
365 dpv_nfiles == 1 ? "" : "s");
366
367 /* If only one file and pbar size is zero, default to `-1' */
368 if (dpv_nfiles <= 1 && pbar_size == 0)
369 pbar_size = -1;
370
371 /* Print some debugging information */
372 if (debug) {
373 warnx("%s: %s(%i) max rows x cols = %i x %i",
374 __func__, use_xdialog ? XDIALOG : DIALOG,
375 use_libdialog ? 3 : 1, dialog_maxrows(),
376 dialog_maxcols());
377 }
378
379 /* Xdialog(1) updates a lot slower than dialog(1) */
380 if (dialog_test && use_xdialog)
381 increment = XDIALOG_INCREMENT;
382
383 /* Always add implicit newline to pprompt (when specified) */
384 if (pprompt != NULL && *pprompt != '\0') {
385 len = strlen(pprompt);
386 /*
387 * NOTE: pprompt = malloc(PPROMPT_MAX + 2)
388 * NOTE: (see getopt(2) section above for pprompt allocation)
389 */
390 pprompt[len++] = '\\';
391 pprompt[len++] = 'n';
392 pprompt[len++] = '\0';
393 }
394
395 /* Xdialog(1) requires newlines (a) escaped and (b) in triplicate */
396 if (use_xdialog && pprompt != NULL) {
397 /* Replace `\n' with `\n\\n\n' in pprompt */
398 len = strlen(pprompt);
399 len += strcount(pprompt, "\\n") * 2;
400 if (len > DPV_PPROMPT_MAX)
401 errx(EXIT_FAILURE, "%s: Oops, pprompt buffer overflow "
402 "(%zu > %i)", __func__, len, DPV_PPROMPT_MAX);
403 if (replaceall(pprompt, "\\n", "\n\\n\n") < 0)
404 err(EXIT_FAILURE, "%s: replaceall()", __func__);
405 }
406 /* libdialog requires literal newlines */
407 else if (use_libdialog && pprompt != NULL)
408 strexpandnl(pprompt);
409
410 /* Xdialog(1) requires newlines (a) escaped and (b) in triplicate */
411 if (use_xdialog && aprompt != NULL) {
412 /* Replace `\n' with `\n\\n\n' in aprompt */
413 len = strlen(aprompt);
414 len += strcount(aprompt, "\\n") * 2;
415 if (len > DPV_APROMPT_MAX)
416 errx(EXIT_FAILURE, "%s: Oops, aprompt buffer overflow "
417 " (%zu > %i)", __func__, len, DPV_APROMPT_MAX);
418 if (replaceall(aprompt, "\\n", "\n\\n\n") < 0)
419 err(EXIT_FAILURE, "%s: replaceall()", __func__);
420 }
421 /* libdialog requires literal newlines */
422 else if (use_libdialog && aprompt != NULL)
423 strexpandnl(aprompt);
424
425 /*
426 * Warn user about an obscure dialog(1) bug (neither Xdialog(1) nor
427 * libdialog are affected) in the `--gauge' widget. If the first non-
428 * whitespace letter of "{new_prompt}" in "XXX\n{new_prompt}\nXXX\n"
429 * is a number, the number can sometimes be mistaken for a percentage
430 * to the overall progressbar. Other nasty side-effects such as the
431 * entire prompt not displaying or displaying improperly are caused by
432 * this bug too.
433 *
434 * NOTE: When we can use color, we have a work-around... prefix the
435 * output with `\Zn' (used to terminate ANSI and reset to normal).
436 */
437 if (use_dialog && !use_color) {
438 backslash = 0;
439
440 /* First, check pprompt (falls through if NULL) */
441 fc = pprompt;
442 while (fc != NULL && *fc != '\0') {
443 if (*fc == '\n') /* leading literal newline OK */
444 break;
445 if (!isspace(*fc) && *fc != '\\' && backslash == 0)
446 break;
447 else if (backslash > 0 && *fc != 'n')
448 break;
449 else if (*fc == '\\') {
450 backslash++;
451 if (backslash > 2)
452 break; /* we're safe */
453 }
454 fc++;
455 }
456 /* First non-whitespace character that dialog(1) will see */
457 if (fc != NULL && *fc >= '0' && *fc <= '9')
458 warnx("%s: WARNING! text argument to `-p' begins with "
459 "a number (not recommended)", __func__);
460 else if (fc > pprompt)
461 warnx("%s: WARNING! text argument to `-p' begins with "
462 "whitespace (not recommended)", __func__);
463
464 /*
465 * If no pprompt or pprompt is all whitespace, check the first
466 * file name provided to make sure it is alright too.
467 */
468 if ((pprompt == NULL || *fc == '\0') && file_list != NULL) {
469 first_file = file_list;
470 fc = first_file->name;
471 while (fc != NULL && *fc != '\0' && isspace(*fc))
472 fc++;
473 /* First non-whitespace char that dialog(1) will see */
474 if (fc != NULL && *fc >= '0' && *fc <= '9')
475 warnx("%s: WARNING! File name `%s' begins "
476 "with a number (use `-p text' for safety)",
477 __func__, first_file->name);
478 }
479 }
480
481 dprompt_init(file_list);
482 /* Reads: label_size pbar_size pprompt aprompt dpv_nfiles */
483 /* Inits: dheight and dwidth */
484
485 if (!debug) {
486 /* Internally create the initial `--gauge' prompt text */
487 dprompt_recreate(file_list, (struct dpv_file_node *)NULL, 0);
488
489 /* Spawn [X]dialog(1) `--gauge', returning pipe descriptor */
490 if (use_libdialog) {
491 status_printf("");
492 dprompt_libprint(pprompt, aprompt, 0);
493 } else {
494 dprompt_sprint(init_prompt, pprompt, aprompt);
495 dialog_out = dialog_spawn_gauge(init_prompt, &pid);
496 dprompt_dprint(dialog_out, pprompt, aprompt, 0);
497 }
498 } /* !debug */
499
500 /* Seed the random(3) generator */
501 if (dialog_test)
502 srandom(0xf1eeface);
503
504 /* Set default/custom status line format */
505 if (dpv_nfiles > 1) {
506 snprintf(status_format_default, DPV_STATUS_FORMAT_MAX, "%s",
507 DPV_STATUS_MANY);
508 status_format_custom = config->status_many;
509 } else {
510 snprintf(status_format_default, DPV_STATUS_FORMAT_MAX, "%s",
511 DPV_STATUS_SOLO);
512 status_format_custom = config->status_solo;
513 }
514
515 /* Add test mode identifier to default status line if enabled */
516 if (dialog_test && (strlen(status_format_default) + 12) <
517 DPV_STATUS_FORMAT_MAX)
518 strcat(status_format_default, " [TEST MODE]");
519
520 /* Verify custom status format */
521 status_fmt = fmtcheck(status_format_custom, status_format_default);
522 if (status_format_custom != NULL &&
523 status_fmt == status_format_default) {
524 warnx("WARNING! Invalid status_format configuration `%s'",
525 status_format_custom);
526 warnx("Default status_format `%s'", status_format_default);
527 }
528
529 /* Record when we started (used to prevent updating too quickly) */
530 (void)gettimeofday(&start, (struct timezone *)NULL);
531
532 /* Calculate number of microseconds in-between sub-second updates */
533 if (status_updates_per_second != 0)
534 status_update_usec = 1000000 / status_updates_per_second;
535 if (dialog_updates_per_second != 0)
536 dialog_update_usec = 1000000 / dialog_updates_per_second;
537
538 /*
539 * Process the file list [serially] (one for each argument passed)
540 */
541 files_left = dpv_nfiles;
542 list_head = file_list;
543 for (curfile = file_list; curfile != NULL; curfile = curfile->next) {
544 keep_going = TRUE;
545 output_out = -1;
546 pct = 0;
547 nthfile++;
548 files_left--;
549
550 if (dpv_interrupt)
551 break;
552 if (dialog_test)
553 pct = 0 - increment;
554
555 /* Attempt to spawn output program for this file */
556 if (!dialog_test && output != NULL) {
557 mask = umask(0022);
558 (void)umask(mask);
559
560 switch (output_type) {
561 case DPV_OUTPUT_SHELL:
562 output_out = shell_spawn_pipecmd(output,
563 curfile->name, &output_pid);
564 break;
565 case DPV_OUTPUT_FILE:
566 path_fmt = fmtcheck(output, "%s");
567 if (path_fmt == output)
568 len = snprintf(pathbuf,
569 PATH_MAX, output, curfile->name);
570 else
571 len = snprintf(pathbuf,
572 PATH_MAX, "%s", output);
573 if (len >= PATH_MAX) {
574 warnx("%s:%d:%s: pathbuf[%u] too small"
575 "to hold output argument",
576 __FILE__, __LINE__, __func__,
577 PATH_MAX);
578 return (-1);
579 }
580 if ((output_out = open(pathbuf,
581 O_CREAT|O_WRONLY, DEFFILEMODE & ~mask))
582 < 0) {
583 warn("%s", pathbuf);
584 return (-1);
585 }
586 break;
587 default:
588 break;
589 }
590 }
591
592 while (!dpv_interrupt && keep_going) {
593 if (dialog_test) {
594 usleep(50000);
595 pct += increment;
596 dpv_overall_read +=
597 (int)(random() / 512 / dpv_nfiles);
598 /* 512 limits fake readout to Megabytes */
599 } else if (action != NULL)
600 pct = action(curfile, output_out);
601
602 if (no_overrun || dialog_test)
603 keep_going = (pct < 100);
604 else {
605 status = curfile->status;
606 keep_going = (status == DPV_STATUS_RUNNING);
607 }
608
609 /* Get current time and calculate seconds elapsed */
610 gettimeofday(&now, (struct timezone *)NULL);
611 now.tv_sec = now.tv_sec - start.tv_sec;
612 now.tv_usec = now.tv_usec - start.tv_usec;
613 if (now.tv_usec < 0)
614 now.tv_sec--, now.tv_usec += 1000000;
615 seconds = now.tv_sec + (now.tv_usec / 1000000.0);
616
617 /* Update dialog (be it dialog(3), dialog(1), etc.) */
618 if ((dialog_updates_per_second != 0 &&
619 (
620 seconds != dialog_old_seconds ||
621 now.tv_usec - dialog_last_update >=
622 dialog_update_usec ||
623 nthfile != dialog_old_nthfile
624 )) || pct == 100
625 ) {
626 /* Calculate overall progress (rounding up) */
627 overall = (100 * nthfile - 100 + pct) /
628 dpv_nfiles;
629 if (((100 * nthfile - 100 + pct) * 10 /
630 dpv_nfiles % 100) > 50)
631 overall++;
632
633 dprompt_recreate(list_head, curfile, pct);
634
635 if (use_libdialog && !debug) {
636 /* Update dialog(3) widget */
637 dprompt_libprint(pprompt, aprompt,
638 overall);
639 } else {
640 /* stdout, dialog(1), or Xdialog(1) */
641 dprompt_dprint(dialog_out, pprompt,
642 aprompt, overall);
643 fsync(dialog_out);
644 }
645 dialog_old_seconds = seconds;
646 dialog_old_nthfile = nthfile;
647 dialog_last_update = now.tv_usec;
648 }
649
650 /* Update the status line */
651 if ((use_libdialog && !debug) &&
652 status_updates_per_second != 0 &&
653 (
654 keep_going != TRUE ||
655 seconds != status_old_seconds ||
656 now.tv_usec - status_last_update >=
657 status_update_usec ||
658 nthfile != status_old_nthfile
659 )
660 ) {
661 status_printf(status_fmt, dpv_overall_read,
662 (dpv_overall_read / (seconds == 0 ? 1 :
663 seconds) * 1.0),
664 1, /* XXX until we add parallelism XXX */
665 files_left);
666 status_old_seconds = seconds;
667 status_old_nthfile = nthfile;
668 status_last_update = now.tv_usec;
669 }
670 }
671
672 if (!dialog_test && output_out >= 0) {
673 close(output_out);
674 waitpid(output_pid, (int *)NULL, 0);
675 }
676
677 if (dpv_abort)
678 break;
679
680 /* Advance head of list when we hit the max display lines */
681 if (display_limit > 0 && nthfile % display_limit == 0)
682 list_head = curfile->next;
683 }
684
685 if (!debug) {
686 if (use_libdialog)
687 end_dialog();
688 else {
689 close(dialog_out);
690 waitpid(pid, (int *)NULL, 0);
691 }
692 if (!dpv_interrupt)
693 printf("\n");
694 } else
695 warnx("%s: %lli lines read", __func__, dpv_overall_read);
696
697 if (dpv_interrupt || dpv_abort)
698 return (-1);
699 else
700 return (0);
701 }
702
703 /*
704 * Free allocated items initialized by dpv()
705 */
706 void
dpv_free(void)707 dpv_free(void)
708 {
709 dialogrc_free();
710 dprompt_free();
711 dialog_maxsize_free();
712 if (aprompt != NULL) {
713 free(aprompt);
714 aprompt = NULL;
715 }
716 if (pprompt != NULL) {
717 free(pprompt);
718 pprompt = NULL;
719 }
720 status_free();
721 }
722