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