00001 #include "config.h"
00002
00003 #include "headers.h"
00004 #include "types.h"
00005 #include "error.h"
00006 #include "io.h"
00007 #include "display.h"
00008
00009 #ifdef HAVE_UNISTD_H
00010 # ifdef HAVE_SIGNAL_H
00011 # ifdef HAVE_TERMIOS_H
00012 # ifdef HAVE_SYS_IOCTL_H
00013 # define CAN_RESIZE
00014 # endif
00015 # endif
00016 # endif
00017 #endif
00018
00019 display d;
00020
00021 #ifdef CAN_RESIZE
00022 static void displayGetSize(void)
00023 {
00024 struct winsize size;
00025
00026 if (d.manual_width)
00027 return;
00028 if (ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&size) < 0)
00029 return;
00030 d.screen_width = size.ws_col;
00031 if (d.screen_width_minus_one)
00032 d.screen_width--;
00033 }
00034
00035 int displaySetSigWinch(void);
00036
00037 static void sig_winch(int signo)
00038 {
00039 displayGetSize();
00040 displaySetSigWinch();
00041 }
00042
00043 int displaySetSigWinch(void)
00044 {
00045 if (isatty(STDERR_FILENO) != 0) {
00046 if (signal(SIGWINCH, sig_winch) == SIG_ERR) {
00047 return(1);
00048 }
00049 }
00050 return(0);
00051 }
00052 #endif
00053
00054 int displayInit(void)
00055 {
00056 int c;
00057
00058 d.start_time = 0;
00059 d.total_time = 0;
00060 d.current_time = 0;
00061 d.elapsed_time = 0;
00062 d.percent_complete = 0.0;
00063 d.display_interval = 1;
00064 d.overtime_flag = 0;
00065 d.k = 1024;
00066 d.twiddle = '-';
00067 for (c = 0; c < 80; c++)
00068 d.title[c] = 0;
00069 d.screen_width = 79;
00070 d.manual_width = 0;
00071 d.screen_width_minus_one = DEFAULT_SW_MINUS_ONE;
00072 d.display_twiddle = DEFAULT_DISPLAY_TWIDDLE;
00073 d.display_title = 1;
00074 d.display_datacount = 1;
00075 d.display_throughput = 1;
00076 d.display_time = 1;
00077 d.display_elapsed_only = 0;
00078 d.display_percent = 1;
00079 d.display_bar = 1;
00080 d.display_summary = 1;
00081 d.display_ansi = 0;
00082 d.display_throughput_bits = 0;
00083 d.display_count_bits = 0;
00084 d.space_bg_color = 0;
00085 d.twiddle_fg_color = "[32m";
00086 d.twiddle_bg_color = 0;
00087 d.twiddle_fg_bold = 0;
00088 d.title_fg_color = "[33m";
00089 d.title_bg_color = 0;
00090 d.title_fg_bold = 0;
00091 d.datacount_fg_color = "[32m";
00092 d.datacount_bg_color = 0;
00093 d.datacount_fg_bold = 1;
00094 d.throughput_label_fg_color = 0;
00095 d.throughput_label_bg_color = 0;
00096 d.throughput_label_fg_bold = 0;
00097 d.throughput_fg_color = "[32m";
00098 d.throughput_bg_color = 0;
00099 d.throughput_fg_bold = 1;
00100 d.time_label_fg_color = 0;
00101 d.time_label_bg_color = 0;
00102 d.time_label_fg_bold = 0;
00103 d.time_fg_color = "[32m";
00104 d.time_bg_color = 0;
00105 d.time_fg_bold = 1;
00106 d.percent_fg_color = "[32m";
00107 d.percent_bg_color = 0;
00108 d.percent_fg_bold = 1;
00109 d.bar_fg_color = "[33m";
00110 d.bar_bg_color = 0;
00111 d.bar_fg_bold = 1;
00112 d.barbrace_fg_color = "[31m";
00113 d.barbrace_bg_color = 0;
00114 d.barbrace_fg_bold = 0;
00115 d.total_display_percent = 1;
00116 return(0);
00117 }
00118
00119 int displayBegin(void)
00120 {
00121 d.start_time = time(0);
00122 d.current_time = time(0);
00123
00124 #ifdef CAN_RESIZE
00125 displayGetSize();
00126 if (displaySetSigWinch() != 0) {
00127 print_error(stderr, "Could not install SIGWINCH signal handler");
00128 print_esup(stderr, "Window resize events will be ignored");
00129 }
00130 #endif
00131
00132 return(0);
00133 }
00134
00135 #define sec_per_hour 3600
00136 #define sec_per_minute 60
00137
00138 int calculateCountDisplay(
00139 uint64 *total_count,
00140 char **total_count_units,
00141 float *short_count,
00142 char **short_count_units
00143 )
00144 {
00145 uint64 num = 0;
00146 uint64 div = 1;
00147
00148 *total_count = io.total_write;
00149 if (d.display_count_bits)
00150 *total_count_units = "b";
00151 else
00152 *total_count_units = "B";
00153
00154 num = io.total_write;
00155
00156 if (d.display_count_bits) {
00157 *short_count_units = "b ";
00158 num *= 8;
00159 }
00160 else {
00161 *short_count_units = "B ";
00162 }
00163
00164 *short_count = 0.0;
00165
00166 if (num >= d.k) {
00167 if (d.display_count_bits)
00168 *short_count_units = "Kb";
00169 else
00170 *short_count_units = "KB";
00171 div = d.k;
00172 }
00173 if (num >= (d.k * d.k)) {
00174 if (d.display_count_bits)
00175 *short_count_units = "Mb";
00176 else
00177 *short_count_units = "MB";
00178 num /= d.k;
00179 }
00180 if (num >= (d.k * d.k)) {
00181 if (d.display_count_bits)
00182 *short_count_units = "Gb";
00183 else
00184 *short_count_units = "GB";
00185 num /= d.k;
00186 }
00187 if (num >= (d.k * d.k)) {
00188 if (d.display_count_bits)
00189 *short_count_units = "Tb";
00190 else
00191 *short_count_units = "TB";
00192 num /= d.k;
00193 }
00194 if (num >= (d.k * d.k)) {
00195 if (d.display_count_bits)
00196 *short_count_units = "Pb";
00197 else
00198 *short_count_units = "PB";
00199 num /= d.k;
00200 }
00201 if (num >= (d.k * d.k)) {
00202 if (d.display_count_bits)
00203 *short_count_units = "Eb";
00204 else
00205 *short_count_units = "EB";
00206 num /= d.k;
00207 }
00208
00209 *short_count = ((float)num / div);
00210 return(0);
00211 }
00212
00213 int calculateThroughputDisplay(
00214 uint64 *total_throughput,
00215 char **total_throughput_units,
00216 float *short_throughput,
00217 char **short_throughput_units
00218 )
00219 {
00220 uint64 num = 0;
00221 uint64 div = 1;
00222
00223 if (d.elapsed_time > 0) {
00224 *total_throughput = io.total_write / d.elapsed_time;
00225 num = io.total_write / d.elapsed_time;
00226 }
00227 else {
00228 *total_throughput = 0;
00229 num = 0;
00230 }
00231 if (d.display_throughput_bits) {
00232 *total_throughput *= 8;
00233 *total_throughput_units = "b";
00234 *short_throughput_units = "b/s ";
00235 num *= 8;
00236 }
00237 else {
00238 *total_throughput_units = "B";
00239 *short_throughput_units = "B/s ";
00240 }
00241
00242 *short_throughput = 0.0;
00243
00244 if (num >= d.k) {
00245 if (d.display_throughput_bits)
00246 *short_throughput_units = "Kb/s";
00247 else
00248 *short_throughput_units = "KB/s";
00249 div = d.k;
00250 }
00251 if (num >= (d.k * d.k)) {
00252 if (d.display_throughput_bits)
00253 *short_throughput_units = "Mb/s";
00254 else
00255 *short_throughput_units = "MB/s";
00256 num /= d.k;
00257 }
00258 if (num >= (d.k * d.k)) {
00259 if (d.display_throughput_bits)
00260 *short_throughput_units = "Gb/s";
00261 else
00262 *short_throughput_units = "GB/s";
00263 num /= d.k;
00264 }
00265 if (num >= (d.k * d.k)) {
00266 if (d.display_throughput_bits)
00267 *short_throughput_units = "Tb/s";
00268 else
00269 *short_throughput_units = "TB/s";
00270 num /= d.k;
00271 }
00272 *short_throughput = ((float)num / div);
00273 return(0);
00274 }
00275
00276 int calculateTimeDisplay(uint64 *sptr, uint64 *mptr, uint64 *hptr)
00277 {
00278 if (*sptr >= sec_per_hour) {
00279 *hptr = *sptr / sec_per_hour;
00280 *sptr -= *hptr * sec_per_hour;
00281 }
00282 if (*sptr >= sec_per_minute) {
00283 *mptr = *sptr / sec_per_minute;
00284 *sptr -= *mptr * sec_per_minute;
00285 }
00286 return(0);
00287 }
00288
00289 int calculatePercentComplete(void)
00290 {
00291 if (io.total_size_known && (io.total_size > 0)) {
00292 d.percent_complete = (float)(io.total_write * 100.0 / io.total_size);
00293 if (d.percent_complete < 0) {
00294 d.percent_complete = 9999.9;
00295 }
00296 }
00297 return(0);
00298 }
00299
00300 void displayAnsiNormal(void)
00301 {
00302 if (d.display_ansi) {
00303 fprintf(stderr, "[0m");
00304 }
00305 }
00306
00307 void displayAnsi(char *fg, char *bg, int b)
00308 {
00309 if (d.display_ansi) {
00310 if (fg != 0) {
00311 fprintf(stderr, fg);
00312 }
00313 if (bg != 0) {
00314 fprintf(stderr, bg);
00315 }
00316 if (b) {
00317 fprintf(stderr, "[1m");
00318 }
00319 }
00320 }
00321
00322 void displayTwiddle(void)
00323 {
00324 static uint64 last_write = 0;
00325 static time_t last_time = 0;
00326
00327 if (last_time == 0) last_time = time(0);
00328 if (last_time == time(0)) return;
00329 if (last_write == io.total_write) return;
00330
00331 last_time = time(0);
00332 last_write = io.total_write;
00333
00334 switch (d.twiddle) {
00335 case '\\': d.twiddle = '|'; break;
00336 case '|': d.twiddle = '/'; break;
00337 case '/': d.twiddle = '-'; break;
00338 case '-': d.twiddle = '\\'; break;
00339 }
00340 displayAnsi(d.twiddle_fg_color, d.twiddle_bg_color, d.twiddle_fg_bold);
00341 fprintf(stderr, "%c\r", d.twiddle);
00342 }
00343
00344 int displayPrint(void)
00345 {
00346 uint64 eta = 0;
00347 uint64 total_throughput = 0;
00348 char *total_throughput_units = "";
00349 float short_throughput = 0.0;
00350 char* short_throughput_units = "";
00351 uint64 hours = 0;
00352 uint64 minutes = 0;
00353 uint64 seconds = 0;
00354 uint64 total_count = 0;
00355 char *total_count_units = "";
00356 float short_count = 0.0;
00357 char *short_count_units = "";
00358 char *time_title = "eta";
00359 int screen_used = 0;
00360 int this_width = 0;
00361
00362 d.current_time = time(0);
00363 d.elapsed_time = d.current_time - d.start_time;
00364
00365 if (
00366 calculateThroughputDisplay(
00367 &total_throughput,
00368 &total_throughput_units,
00369 &short_throughput,
00370 &short_throughput_units
00371 )
00372 != 0
00373 )
00374 {
00375 return(1);
00376 }
00377
00378 if (calculatePercentComplete() != 0) {
00379 return(1);
00380 }
00381
00382 if ((io.total_size_known == 1) && (!d.display_elapsed_only)) {
00383 if (io.total_write > 0) {
00384 if (io.total_size >= io.total_write) {
00385 if (d.percent_complete > 0.0) {
00386 eta =
00387 (uint64)(
00388 100 * d.elapsed_time / d.percent_complete
00389 )
00390 - d.elapsed_time;
00391 }
00392 else {
00393 eta = (uint64)(-1);
00394 }
00395 }
00396 else {
00397 if (!d.overtime_flag) {
00398 d.overtime_flag = 1;
00399 d.total_time = d.elapsed_time;
00400 }
00401 eta = d.elapsed_time - d.total_time;
00402 time_title = "ovr";
00403 }
00404 }
00405 else {
00406 eta = 0;
00407 }
00408 seconds = eta;
00409 }
00410 else
00411 {
00412 seconds = d.elapsed_time;
00413 time_title = "elapsed";
00414 }
00415 if (calculateTimeDisplay(&seconds, &minutes, &hours) != 0) {
00416 return(1);
00417 }
00418
00419 if (
00420 calculateCountDisplay(
00421 &total_count,
00422 &total_count_units,
00423 &short_count,
00424 &short_count_units)
00425 != 0)
00426 {
00427 return(1);
00428 }
00429
00430
00431
00432
00433 this_width = 1;
00434 if ((d.display_twiddle) && (screen_used+this_width < d.screen_width)) {
00435 displayAnsi(
00436 d.twiddle_fg_color,
00437 d.twiddle_bg_color,
00438 d.twiddle_fg_bold);
00439 fprintf(stderr, "%c", d.twiddle);
00440 screen_used += this_width;
00441 }
00442
00443
00444
00445
00446 if ((d.display_title) && (d.title[0] != 0)) {
00447 this_width = strlen(d.title);
00448 if (screen_used > 0) {
00449 displayAnsiNormal();
00450 displayAnsi(0,d.space_bg_color,0);
00451 fprintf(stderr, " ");
00452 this_width++;
00453 }
00454 displayAnsiNormal();
00455 displayAnsi(
00456 d.title_fg_color,
00457 d.title_bg_color,
00458 d.title_fg_bold);
00459 fprintf(stderr, "%s", d.title);
00460 screen_used += this_width;
00461 }
00462
00463
00464
00465
00466 this_width = 8;
00467 if (screen_used > 0)
00468 this_width++;
00469 if ((d.display_datacount) && (screen_used+this_width < d.screen_width)) {
00470 if (screen_used > 0) {
00471 displayAnsiNormal();
00472 displayAnsi(0,d.space_bg_color,0);
00473 fprintf(stderr, " ");
00474 }
00475 displayAnsiNormal();
00476 displayAnsi(
00477 d.datacount_fg_color,
00478 d.datacount_bg_color,
00479 d.datacount_fg_bold);
00480 if (short_count > 9999.9) {
00481 fprintf(stderr, "+999.9%2.2s", short_count_units);
00482 }
00483 else {
00484 fprintf(stderr, "%6.1f%2.2s", short_count, short_count_units);
00485 }
00486 screen_used += this_width;
00487 }
00488
00489
00490
00491
00492 this_width = 13;
00493 if (!d.display_datacount)
00494 this_width -= 3;
00495 if (screen_used > 0)
00496 this_width++;
00497 if ((d.display_throughput) && (screen_used+this_width < d.screen_width)) {
00498 if (screen_used > 0) {
00499 displayAnsiNormal();
00500 displayAnsi(0,d.space_bg_color,0);
00501 fprintf(stderr, " ");
00502 }
00503 if (d.display_datacount) {
00504 displayAnsiNormal();
00505 displayAnsi(
00506 d.throughput_label_fg_color,
00507 d.throughput_label_bg_color,
00508 d.throughput_label_fg_bold);
00509 fprintf(stderr, "at");
00510 displayAnsiNormal();
00511 displayAnsi(0,d.space_bg_color,0);
00512 fprintf(stderr, " ");
00513 }
00514 displayAnsiNormal();
00515 displayAnsi(
00516 d.throughput_fg_color,
00517 d.throughput_bg_color,
00518 d.throughput_fg_bold);
00519 fprintf(stderr, "%6.1f%4.4s", short_throughput, short_throughput_units);
00520 screen_used += this_width;
00521 }
00522
00523
00524
00525
00526 this_width = 11+strlen(time_title);
00527 if (screen_used > 0)
00528 this_width += 2;
00529 if ((d.display_time) && (screen_used+this_width < d.screen_width)) {
00530 if (screen_used > 0) {
00531 displayAnsiNormal();
00532 displayAnsi(0,d.space_bg_color,0);
00533 fprintf(stderr, " ");
00534 }
00535 displayAnsiNormal();
00536 displayAnsi(
00537 d.time_label_fg_color,
00538 d.time_label_bg_color,
00539 d.time_label_fg_bold);
00540 fprintf(stderr, "%s:", time_title);
00541 displayAnsiNormal();
00542 displayAnsi(0,d.space_bg_color,0);
00543 fprintf(stderr, " ");
00544 displayAnsiNormal();
00545 displayAnsi(
00546 d.time_fg_color,
00547 d.time_bg_color,
00548 d.time_fg_bold);
00549 if (hours > 99) {
00550 fprintf(stderr, "+99:99:99");
00551 }
00552 else
00553 fprintf(stderr, "%3u:%2.2u:%2.2u",
00554 (unsigned int)hours,
00555 (unsigned int)minutes,
00556 (unsigned int)seconds);
00557 screen_used += this_width;
00558 }
00559
00560
00561
00562
00563 this_width = 5;
00564 if (screen_used > 0)
00565 this_width++;
00566 if ((d.display_percent) && (io.total_size_known)
00567 && (screen_used+this_width < d.screen_width))
00568 {
00569 if (screen_used > 0) {
00570 displayAnsiNormal();
00571 displayAnsi(0,d.space_bg_color,0);
00572 fprintf(stderr, " ");
00573 }
00574 displayAnsiNormal();
00575 displayAnsi(
00576 d.percent_fg_color,
00577 d.percent_bg_color,
00578 d.percent_fg_bold);
00579 if (d.percent_complete > 999) {
00580 fprintf(stderr, "+999%%");
00581 }
00582 else {
00583 fprintf(stderr, "%4d%%", (int)d.percent_complete);
00584 }
00585 screen_used += this_width;
00586 }
00587
00588
00589
00590
00591 this_width = 5;
00592 if (screen_used > 0)
00593 this_width++;
00594 if ((d.display_bar) && (io.total_size_known)
00595 && (screen_used+this_width < d.screen_width))
00596 {
00597 int c;
00598 int line_length;
00599 int completed_length = 0;
00600
00601 if (screen_used > 0) {
00602 displayAnsiNormal();
00603 displayAnsi(0,d.space_bg_color,0);
00604 fprintf(stderr, " ");
00605 screen_used++;
00606 }
00607 this_width = d.screen_width - screen_used + 1;
00608 line_length = this_width - 3;
00609 completed_length = line_length * d.percent_complete / 100;
00610 displayAnsiNormal();
00611 displayAnsi(
00612 d.barbrace_fg_color,
00613 d.barbrace_bg_color,
00614 d.barbrace_fg_bold);
00615 fprintf(stderr, "[");
00616 displayAnsiNormal();
00617 displayAnsi(
00618 d.bar_fg_color,
00619 d.bar_bg_color,
00620 d.bar_fg_bold);
00621 for (c = 0; c < line_length; c++) {
00622 if (c <= completed_length) {
00623 fprintf(stderr, "=");
00624 }
00625 else {
00626 fprintf(stderr, " ");
00627 }
00628 }
00629 displayAnsiNormal();
00630 displayAnsi(
00631 d.barbrace_fg_color,
00632 d.barbrace_bg_color,
00633 d.barbrace_fg_bold);
00634 fprintf(stderr, "]");
00635 }
00636
00637 displayAnsiNormal();
00638 fprintf(stderr, "\r");
00639
00640 return(0);
00641 }
00642
00643 int displayUpdate(void)
00644 {
00645 if (d.display_twiddle) displayTwiddle();
00646 if (time(0) < d.current_time + d.display_interval) return(0);
00647 if (displayPrint() != 0) return(1);
00648 return(0);
00649 }
00650
00651 int displayEnd(void)
00652 {
00653 uint64 total_throughput = 0;
00654 char *total_throughput_units = "";
00655 float short_throughput = 0.0;
00656 char *short_throughput_units = "";
00657 uint64 total_count = 0;
00658 char *total_count_units = "";
00659 float short_count = 0.0;
00660 char *short_count_units = "";
00661 uint64 hours = 0;
00662 uint64 minutes = 0;
00663 uint64 seconds = 0;
00664
00665 displayPrint();
00666 d.elapsed_time = d.current_time - d.start_time;
00667
00668 if (calculatePercentComplete() != 0) {
00669 return(1);
00670 }
00671
00672 if (
00673 calculateCountDisplay(
00674 &total_count,
00675 &total_count_units,
00676 &short_count,
00677 &short_count_units
00678 )
00679 != 0
00680 )
00681 {
00682 return(1);
00683 }
00684
00685 if (
00686 calculateThroughputDisplay(
00687 &total_throughput,
00688 &total_throughput_units,
00689 &short_throughput,
00690 &short_throughput_units
00691 )
00692 != 0
00693 )
00694 {
00695 return(1);
00696 }
00697
00698 seconds = d.elapsed_time;
00699 if (calculateTimeDisplay(&seconds, &minutes, &hours) != 0) {
00700 return(1);
00701 }
00702
00703 fprintf(stderr, "\n");
00704 if (d.display_summary) {
00705 fprintf(stderr, "Copied: %llu%s (%.1f%s)",
00706 UINT64_CTYPE(total_count),
00707 total_count_units,
00708 short_count,
00709 short_count_units
00710 );
00711 if (io.total_size_known && d.total_display_percent) {
00712 fprintf(stderr, " (%d%% of expected input)", (int)d.percent_complete);
00713 }
00714 fprintf(stderr, "\n");
00715
00716 fprintf(stderr, "Time: ");
00717 if (hours > 0) {
00718 fprintf(stderr, "%3u:%2.2u:%2.2u",
00719 (unsigned int)hours,
00720 (unsigned int)minutes,
00721 (unsigned int)seconds);
00722 }
00723 else if (minutes > 0) {
00724 fprintf(stderr, "%2.2u:%2.2u",
00725 (unsigned int)minutes,
00726 (unsigned int)seconds);
00727 }
00728 else {
00729 fprintf(stderr, "%2u seconds",
00730 (unsigned int)seconds);
00731 }
00732 fprintf(stderr, "\n");
00733
00734 if ((hours != 0) || (minutes != 0) || (seconds != 0)) {
00735 fprintf(stderr, "Throughput: %llu%s (%.1f%s)\n\n",
00736 UINT64_CTYPE(total_throughput),
00737 total_throughput_units,
00738 short_throughput,
00739 short_throughput_units
00740 );
00741 }
00742 else {
00743 fprintf(stderr, "Throughput: (infinite)\n\n");
00744 }
00745 }
00746
00747 d.start_time = 0;
00748
00749 return(0);
00750 }