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