1 /*
2 * Display functions.
3 */
4
5 #include "pv-internal.h"
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <stdarg.h>
10 #include <string.h>
11 #include <ctype.h>
12 #include <errno.h>
13 #include <time.h>
14 #include <unistd.h>
15 #include <termios.h>
16 #include <sys/ioctl.h>
17
18
19 /*
20 * Output an error message. If we've displayed anything to the terminal
21 * already, then put a newline before our error so we don't write over what
22 * we've written.
23 */
pv_error(pvstate_t state,char * format,...)24 void pv_error(pvstate_t state, char *format, ...)
25 {
26 va_list ap;
27 if (state->display_visible)
28 fprintf(stderr, "\n");
29 fprintf(stderr, "%s: ", state->program_name);
30 va_start(ap, format);
31 (void) vfprintf(stderr, format, ap);
32 va_end(ap);
33 fprintf(stderr, "\n");
34 }
35
36
37 /*
38 * Return true if we are the foreground process on the terminal, or if we
39 * aren't outputting to a terminal; false otherwise.
40 */
pv_in_foreground(void)41 bool pv_in_foreground(void)
42 {
43 pid_t our_process_group;
44 pid_t tty_process_group;
45
46 if (0 == isatty(STDERR_FILENO))
47 return true;
48
49 our_process_group = getpgrp();
50 tty_process_group = tcgetpgrp(STDERR_FILENO);
51 if (our_process_group == tty_process_group)
52 return true;
53
54 return false;
55 }
56
57
58 /*
59 * Fill in *width and *height with the current terminal size,
60 * if possible.
61 */
pv_screensize(unsigned int * width,unsigned int * height)62 void pv_screensize(unsigned int *width, unsigned int *height)
63 {
64 #ifdef TIOCGWINSZ
65 struct winsize wsz;
66
67 if (0 != isatty(STDERR_FILENO)) {
68 if (0 == ioctl(STDERR_FILENO, TIOCGWINSZ, &wsz)) {
69 *width = wsz.ws_col;
70 *height = wsz.ws_row;
71 }
72 }
73 #endif
74 }
75
76
77 /*
78 * Calculate the percentage transferred so far and return it.
79 */
pv__calc_percentage(long long so_far,const long long total)80 static long pv__calc_percentage(long long so_far, const long long total)
81 {
82 if (total < 1)
83 return 0;
84
85 so_far *= 100;
86 so_far /= total;
87
88 return (long) so_far;
89 }
90
91
92 /*
93 * Given how many bytes have been transferred, the total byte count to
94 * transfer, and how long it's taken so far in seconds, return the estimated
95 * number of seconds until completion.
96 */
pv__calc_eta(const long long so_far,const long long total,const long elapsed)97 static long pv__calc_eta(const long long so_far, const long long total,
98 const long elapsed)
99 {
100 long long amount_left;
101
102 if (so_far < 1)
103 return 0;
104
105 amount_left = total - so_far;
106 amount_left *= (long long) elapsed;
107 amount_left /= so_far;
108
109 return (long) amount_left;
110 }
111
112 /*
113 * Given a long double value, it is divided or multiplied by the ratio until
114 * a value in the range 1.0 to 999.999... is found. The string "prefix" to
115 * is updated to the corresponding SI prefix.
116 *
117 * If "is_bytes" is 1, then the second byte of "prefix" is set to "i" to
118 * denote MiB etc (IEEE1541). Thus "prefix" should be at least 3 bytes long
119 * (to include the terminating null).
120 *
121 * Submitted by Henry Gebhardt <hsggebhardt@googlemail.com> and then
122 * modified. Further changed after input from Thomas Rachel; changed still
123 * further after Debian bug #706175.
124 */
pv__si_prefix(long double * value,char * prefix,const long double ratio,int is_bytes)125 static void pv__si_prefix(long double *value, char *prefix,
126 const long double ratio, int is_bytes)
127 {
128 static char *pfx_000 = NULL; /* kilo, mega, etc */
129 static char *pfx_024 = NULL; /* kibi, mibi, etc */
130 static char const *pfx_middle_000 = NULL;
131 static char const *pfx_middle_024 = NULL;
132 char *pfx;
133 char const *pfx_middle;
134 char const *i;
135 long double cutoff;
136
137 if (NULL == pfx_000) {
138 pfx_000 = _("yzafpnum kMGTPEZY");
139 pfx_middle_000 = strchr(pfx_000, ' ');
140 }
141
142 if (NULL == pfx_024) {
143 pfx_024 = _("yzafpnum KMGTPEZY");
144 pfx_middle_024 = strchr(pfx_024, ' ');
145 }
146
147 pfx = pfx_000;
148 pfx_middle = pfx_middle_000;
149 if (is_bytes) {
150 pfx = pfx_024;
151 pfx_middle = pfx_middle_024;
152 }
153
154 i = pfx_middle;
155
156 prefix[0] = ' '; /* Make the prefix start blank. */
157 prefix[1] = 0;
158
159 /*
160 * Force an empty prefix if the value is zero to avoid "0yB".
161 */
162 if (0.0 == *value)
163 return;
164
165 cutoff = ratio * 0.97;
166
167 while ((*value > cutoff) && (*(i += 1) != '\0')) {
168 *value /= ratio;
169 prefix[0] = *i;
170 }
171
172 while ((*value < 1.0) && ((i -= 1) != (pfx - 1))) {
173 *value *= ratio;
174 prefix[0] = *i;
175 }
176
177 if (is_bytes && prefix[0] != ' ') {
178 prefix[1] = 'i';
179 prefix[2] = 0;
180 }
181 }
182
183
184 /*
185 * Put a string in "buffer" (max length "bufsize") containing "amount"
186 * formatted such that it's 3 or 4 digits followed by an SI suffix and then
187 * whichever of "suffix_basic" or "suffix_bytes" is appropriate (whether
188 * "is_bytes" is 0 for non-byte amounts or 1 for byte amounts). If
189 * "is_bytes" is 1 then the SI units are KiB, MiB etc and the divisor is
190 * 1024 instead of 1000.
191 *
192 * The "format" string is in sprintf format and must contain exactly one %
193 * parameter (a %s) which will expand to the string described above.
194 */
pv__sizestr(char * buffer,int bufsize,char * format,long double amount,char * suffix_basic,char * suffix_bytes,int is_bytes)195 static void pv__sizestr(char *buffer, int bufsize, char *format,
196 long double amount, char *suffix_basic,
197 char *suffix_bytes, int is_bytes)
198 {
199 char sizestr_buffer[256];
200 char si_prefix[8] = " ";
201 long double divider;
202 long double display_amount;
203 char *suffix;
204
205 if (is_bytes) {
206 suffix = suffix_bytes;
207 divider = 1024.0;
208 } else {
209 suffix = suffix_basic;
210 divider = 1000.0;
211 }
212
213 display_amount = amount;
214
215 pv__si_prefix(&display_amount, si_prefix, divider, is_bytes);
216
217 /* Make sure we don't overrun our buffer. */
218 if (display_amount > 100000)
219 display_amount = 100000;
220
221 /* Fix for display of "1.01e+03" instead of "1010" */
222 if (display_amount > 99.9) {
223 sprintf(sizestr_buffer, "%4ld%.2s%.16s",
224 (long) display_amount, si_prefix, suffix);
225 } else {
226 /*
227 * AIX blows up with %4.3Lg%.2s%.16s for some reason, so we
228 * write display_amount separately first.
229 */
230 char str_disp[64];
231 /* # to get 13.0GB instead of 13GB (#1477) */
232 sprintf(str_disp, "%#4.3Lg", display_amount);
233 sprintf(sizestr_buffer, "%s%.2s%.16s",
234 str_disp, si_prefix, suffix);
235 }
236
237 #ifdef HAVE_SNPRINTF
238 snprintf(buffer, bufsize, format, sizestr_buffer);
239 #else
240 sprintf(buffer, format, sizestr_buffer);
241 #endif
242 }
243
244
245 /*
246 * Initialise the output format structure, based on the current options.
247 */
pv__format_init(pvstate_t state)248 static void pv__format_init(pvstate_t state)
249 {
250 const char *formatstr;
251 const char *searchptr;
252 int strpos;
253 int segment;
254
255 if (NULL == state)
256 return;
257
258 state->str_name[0] = 0;
259 state->str_transferred[0] = 0;
260 state->str_timer[0] = 0;
261 state->str_rate[0] = 0;
262 state->str_average_rate[0] = 0;
263 state->str_progress[0] = 0;
264 state->str_eta[0] = 0;
265 memset(state->format, 0, sizeof(state->format));
266
267 if (state->name) {
268 sprintf(state->str_name, "%9.500s:", state->name);
269 }
270
271 formatstr =
272 state->format_string ? state->
273 format_string : state->default_format;
274
275 state->components_used = 0;
276
277 /*
278 * Split the format string into segments. Each segment consists
279 * of a string pointer and a length.
280 *
281 * A length of zero indicates that the segment is a fixed-size
282 * component updated by pv__format(), so it is a pointer to one
283 * of the state->str_* buffers that pv__format() updates.
284 *
285 * A length below zero indicates that the segment is a variable
286 * sized component which will be recalculated by pv__format()
287 * after the length of all fixed-size segments is known, and so
288 * the string is a pointer to another state->str_* buffer
289 * (currently it will only ever be state->str_progress).
290 *
291 * A length above zero indicates that the segment is a constant
292 * string of the given length (not necessarily null terminated).
293 *
294 * In pv__format(), after the state->str_* buffers have all been
295 * filled in, the output string is generated by sticking all of
296 * these segments together.
297 */
298 segment = 0;
299 for (strpos = 0; formatstr[strpos] != 0 && segment < 99;
300 strpos++, segment++) {
301 if ('%' == formatstr[strpos]) {
302 unsigned int num;
303 strpos++;
304 num = 0;
305 while (isdigit(formatstr[strpos])) {
306 num = num * 10;
307 num += formatstr[strpos] - '0';
308 strpos++;
309 }
310 switch (formatstr[strpos]) {
311 case 'p':
312 state->format[segment].string =
313 state->str_progress;
314 state->format[segment].length = -1;
315 state->components_used |=
316 PV_DISPLAY_PROGRESS;
317 break;
318 case 't':
319 state->format[segment].string =
320 state->str_timer;
321 state->format[segment].length = 0;
322 state->components_used |= PV_DISPLAY_TIMER;
323 break;
324 case 'e':
325 state->format[segment].string =
326 state->str_eta;
327 state->format[segment].length = 0;
328 state->components_used |= PV_DISPLAY_ETA;
329 break;
330 case 'I':
331 state->format[segment].string =
332 state->str_fineta;
333 state->format[segment].length = 0;
334 state->components_used |=
335 PV_DISPLAY_FINETA;
336 break;
337 case 'A':
338 state->format[segment].string =
339 state->str_lastoutput;
340 state->format[segment].length = 0;
341 if (num > sizeof(state->lastoutput_buffer))
342 num =
343 sizeof
344 (state->lastoutput_buffer);
345 if (num < 1)
346 num = 1;
347 state->lastoutput_length = num;
348 state->components_used |=
349 PV_DISPLAY_OUTPUTBUF;
350 break;
351 case 'r':
352 state->format[segment].string =
353 state->str_rate;
354 state->format[segment].length = 0;
355 state->components_used |= PV_DISPLAY_RATE;
356 break;
357 case 'a':
358 state->format[segment].string =
359 state->str_average_rate;
360 state->format[segment].length = 0;
361 state->components_used |=
362 PV_DISPLAY_AVERAGERATE;
363 break;
364 case 'b':
365 state->format[segment].string =
366 state->str_transferred;
367 state->format[segment].length = 0;
368 state->components_used |= PV_DISPLAY_BYTES;
369 break;
370 case 'T':
371 state->format[segment].string =
372 state->str_bufpercent;
373 state->format[segment].length = 0;
374 state->components_used |=
375 PV_DISPLAY_BUFPERCENT;
376 break;
377 case 'N':
378 state->format[segment].string =
379 state->str_name;
380 state->format[segment].length =
381 strlen(state->str_name);
382 state->components_used |= PV_DISPLAY_NAME;
383 break;
384 case '%':
385 /* %% => % */
386 state->format[segment].string =
387 &(formatstr[strpos]);
388 state->format[segment].length = 1;
389 break;
390 case 0:
391 /* % at end => just % */
392 state->format[segment].string =
393 &(formatstr[--strpos]);
394 state->format[segment].length = 1;
395 break;
396 default:
397 /* %z (unknown) => %z */
398 state->format[segment].string =
399 &(formatstr[--strpos]);
400 state->format[segment].length = 2;
401 strpos++;
402 break;
403 }
404 } else {
405 int foundlength;
406 searchptr = strchr(&(formatstr[strpos]), '%');
407 if (NULL == searchptr) {
408 foundlength = strlen(&(formatstr[strpos]));
409 } else {
410 foundlength =
411 searchptr - &(formatstr[strpos]);
412 }
413 state->format[segment].string =
414 &(formatstr[strpos]);
415 state->format[segment].length = foundlength;
416 strpos += foundlength - 1;
417 }
418 }
419
420 state->format[segment].string = 0;
421 state->format[segment].length = 0;
422 }
423
424 /*
425 * Return the original value x so that it has been clamped between
426 * [min..max]
427 */
bound_long(long x,long min,long max)428 static long bound_long(long x, long min, long max)
429 {
430 return x < min ? min : x > max ? max : x;
431 }
432
433
434 /*
435 * Return a pointer to a string (which must not be freed), containing status
436 * information formatted according to the state held within the given
437 * structure, where "elapsed_sec" is the seconds elapsed since the transfer
438 * started, "bytes_since_last" is the number of bytes transferred since the
439 * last update, and "total_bytes" is the total number of bytes transferred
440 * so far.
441 *
442 * If "bytes_since_last" is negative, this is the final update so the rate
443 * is given as an an average over the whole transfer; otherwise the current
444 * rate is shown.
445 *
446 * In line mode, "bytes_since_last" and "total_bytes" are in lines, not bytes.
447 *
448 * If "total_bytes" is negative, then free all allocated memory and return
449 * NULL.
450 */
pv__format(pvstate_t state,long double elapsed_sec,long long bytes_since_last,long long total_bytes)451 static char *pv__format(pvstate_t state,
452 long double elapsed_sec,
453 long long bytes_since_last, long long total_bytes)
454 {
455 long double time_since_last, rate, average_rate;
456 long eta;
457 int static_portion_size;
458 int segment;
459 int output_length;
460 int display_string_length;
461
462 /* Quick sanity check - state must exist */
463 if (NULL == state)
464 return NULL;
465
466 /* Negative total transfer - free memory and exit */
467 if (total_bytes < 0) {
468 if (state->display_buffer)
469 free(state->display_buffer);
470 state->display_buffer = NULL;
471 return NULL;
472 }
473
474 /*
475 * In case the time since the last update is very small, we keep
476 * track of amount transferred since the last update, and just keep
477 * adding to that until a reasonable amount of time has passed to
478 * avoid rate spikes or division by zero.
479 */
480 time_since_last = elapsed_sec - state->prev_elapsed_sec;
481 if (time_since_last <= 0.01) {
482 rate = state->prev_rate;
483 state->prev_trans += bytes_since_last;
484 } else {
485 rate =
486 ((long double) bytes_since_last +
487 state->prev_trans) / time_since_last;
488 state->prev_elapsed_sec = elapsed_sec;
489 state->prev_trans = 0;
490 }
491 state->prev_rate = rate;
492
493 /*
494 * We only calculate the overall average rate if this is the last
495 * update or if the average rate display is enabled. Otherwise it's
496 * not worth the extra CPU cycles.
497 */
498 average_rate = 0;
499 if ((bytes_since_last < 0)
500 || ((state->components_used & PV_DISPLAY_AVERAGERATE) != 0)) {
501 /* Sanity check to avoid division by zero */
502 if (elapsed_sec < 0.000001)
503 elapsed_sec = 0.000001;
504 average_rate =
505 (((long double) total_bytes) -
506 ((long double) state->initial_offset)) /
507 (long double) elapsed_sec;
508 if (bytes_since_last < 0)
509 rate = average_rate;
510 }
511
512 if (state->size <= 0) {
513 /*
514 * If we don't know the total size of the incoming data,
515 * then for a percentage, we gradually increase the
516 * percentage completion as data arrives, to a maximum of
517 * 200, then reset it - we use this if we can't calculate
518 * it, so that the numeric percentage output will go
519 * 0%-100%, 100%-0%, 0%-100%, and so on.
520 */
521 if (rate > 0)
522 state->percentage += 2;
523 if (state->percentage > 199)
524 state->percentage = 0;
525 } else if (state->numeric
526 || ((state->components_used & PV_DISPLAY_PROGRESS) !=
527 0)) {
528 /*
529 * If we do know the total size, and we're going to show
530 * the percentage (numeric mode or a progress bar),
531 * calculate the percentage completion.
532 */
533 state->percentage =
534 pv__calc_percentage(total_bytes, state->size);
535 }
536
537 /*
538 * Reallocate output buffer if width changes.
539 */
540 if (state->display_buffer != NULL
541 && state->display_buffer_size < (state->width * 2)) {
542 free(state->display_buffer);
543 state->display_buffer = NULL;
544 state->display_buffer_size = 0;
545 }
546
547 /*
548 * Allocate output buffer if there isn't one.
549 */
550 if (NULL == state->display_buffer) {
551 state->display_buffer_size = (2 * state->width) + 80;
552 if (state->name)
553 state->display_buffer_size += strlen(state->name);
554 state->display_buffer =
555 malloc(state->display_buffer_size + 16);
556 if (NULL == state->display_buffer) {
557 pv_error(state, "%s: %s",
558 _("buffer allocation failed"),
559 strerror(errno));
560 state->exit_status |= 64;
561 return NULL;
562 }
563 state->display_buffer[0] = 0;
564 }
565
566 /*
567 * In numeric output mode, our output is just a number.
568 *
569 * Patch from Sami Liedes:
570 * With --timer we prefix the output with the elapsed time.
571 * With --bytes we output the bytes transferred so far instead
572 * of the percentage. (Or lines, if --lines was given with --bytes).
573 */
574 if (state->numeric) {
575 char numericprefix[128];
576
577 numericprefix[0] = 0;
578
579 if ((state->components_used & PV_DISPLAY_TIMER) != 0)
580 sprintf(numericprefix, "%.4Lf ", elapsed_sec);
581
582 if ((state->components_used & PV_DISPLAY_BYTES) != 0) {
583 sprintf(state->display_buffer, "%.99s%lld\n",
584 numericprefix, total_bytes);
585 } else if (state->percentage > 100) {
586 /* As mentioned above, we go 0-100, then 100-0. */
587 sprintf(state->display_buffer, "%.99s%ld\n",
588 numericprefix, 200 - state->percentage);
589 } else {
590 sprintf(state->display_buffer, "%.99s%ld\n",
591 numericprefix, state->percentage);
592 }
593
594 return state->display_buffer;
595 }
596
597 /*
598 * First, work out what components we will be putting in the output
599 * buffer, and for those that don't depend on the total width
600 * available (i.e. all but the progress bar), prepare their strings
601 * to be placed in the output buffer.
602 */
603
604 state->str_transferred[0] = 0;
605 state->str_bufpercent[0] = 0;
606 state->str_timer[0] = 0;
607 state->str_rate[0] = 0;
608 state->str_average_rate[0] = 0;
609 state->str_progress[0] = 0;
610 state->str_lastoutput[0] = 0;
611 state->str_eta[0] = 0;
612 state->str_fineta[0] = 0;
613
614 /* If we're showing bytes transferred, set up the display string. */
615 if ((state->components_used & PV_DISPLAY_BYTES) != 0) {
616 pv__sizestr(state->str_transferred,
617 sizeof(state->str_transferred), "%s",
618 (long double) total_bytes, "", _("B"),
619 state->linemode ? 0 : 1);
620 }
621
622 /* Transfer buffer percentage - set up the display string. */
623 if ((state->components_used & PV_DISPLAY_BUFPERCENT) != 0) {
624 if (state->buffer_size > 0)
625 sprintf(state->str_bufpercent, "{%3ld%%}",
626 pv__calc_percentage(state->read_position -
627 state->write_position,
628 state->buffer_size));
629 #ifdef HAVE_SPLICE
630 if (state->splice_used)
631 strcpy(state->str_bufpercent, "{----}");
632 #endif
633 }
634
635 /* Timer - set up the display string. */
636 if ((state->components_used & PV_DISPLAY_TIMER) != 0) {
637 /*
638 * Bounds check, so we don't overrun the prefix buffer. This
639 * does mean that the timer will stop at a 100,000 hours,
640 * but since that's 11 years, it shouldn't be a problem.
641 */
642 if (elapsed_sec > (long double) 360000000.0L)
643 elapsed_sec = (long double) 360000000.0L;
644
645 sprintf(state->str_timer, "%ld:%02ld:%02ld",
646 ((long) elapsed_sec) / 3600,
647 (((long) elapsed_sec) / 60) % 60,
648 ((long) elapsed_sec) % 60);
649 }
650
651 /* Rate - set up the display string. */
652 if ((state->components_used & PV_DISPLAY_RATE) != 0) {
653 pv__sizestr(state->str_rate, sizeof(state->str_rate),
654 "[%s]", rate, _("/s"), _("B/s"),
655 state->linemode ? 0 : 1);
656 }
657
658 /* Average rate - set up the display string. */
659 if ((state->components_used & PV_DISPLAY_AVERAGERATE) != 0) {
660 pv__sizestr(state->str_average_rate,
661 sizeof(state->str_average_rate), "[%s]",
662 average_rate, _("/s"), _("B/s"),
663 state->linemode ? 0 : 1);
664 }
665
666 /* Last output bytes - set up the display string. */
667 if ((state->components_used & PV_DISPLAY_OUTPUTBUF) != 0) {
668 int idx;
669 for (idx = 0; idx < state->lastoutput_length; idx++) {
670 int c;
671 c = state->lastoutput_buffer[idx];
672 state->str_lastoutput[idx] = isprint(c) ? c : '.';
673 }
674 state->str_lastoutput[idx] = 0;
675 }
676
677 /* ETA (only if size is known) - set up the display string. */
678 if (((state->components_used & PV_DISPLAY_ETA) != 0)
679 && (state->size > 0)) {
680 eta =
681 pv__calc_eta(total_bytes - state->initial_offset,
682 state->size - state->initial_offset,
683 elapsed_sec);
684
685 /*
686 * Bounds check, so we don't overrun the suffix buffer. This
687 * means the ETA will always be less than 100,000 hours.
688 */
689 eta = bound_long(eta, 0, (long) 360000000L);
690
691 /*
692 * If the ETA is more than a day, include a day count as
693 * well as hours, minutes, and seconds.
694 */
695 if (eta > 86400L) {
696 sprintf(state->str_eta,
697 "%.16s %ld:%02ld:%02ld:%02ld", _("ETA"),
698 eta / 86400, (eta / 3600) % 24,
699 (eta / 60) % 60, eta % 60);
700 } else {
701 sprintf(state->str_eta, "%.16s %ld:%02ld:%02ld",
702 _("ETA"), eta / 3600, (eta / 60) % 60,
703 eta % 60);
704 }
705
706 /*
707 * If this is the final update, show a blank space where the
708 * ETA used to be.
709 */
710 if (bytes_since_last < 0) {
711 unsigned int i;
712 for (i = 0; i < sizeof(state->str_eta)
713 && state->str_eta[i] != 0; i++) {
714 state->str_eta[i] = ' ';
715 }
716 }
717 }
718
719 /* ETA as clock time (as above) - set up the display string. */
720 if (((state->components_used & PV_DISPLAY_FINETA) != 0)
721 && (state->size > 0)) {
722 /*
723 * The ETA may be hidden by a failed ETA string
724 * generation.
725 */
726 int show_eta = 1;
727 time_t now = time(NULL);
728 time_t then;
729 struct tm *time_ptr;
730 char *time_format = NULL;
731
732 eta =
733 pv__calc_eta(total_bytes - state->initial_offset,
734 state->size - state->initial_offset,
735 elapsed_sec);
736
737 /*
738 * Bounds check, so we don't overrun the suffix buffer. This
739 * means the ETA will always be less than 100,000 hours.
740 */
741 eta = bound_long(eta, 0, (long) 360000000L);
742
743 /*
744 * Only include the date if the ETA is more than 6 hours
745 * away.
746 */
747 if (eta > (long) (6 * 3600)) {
748 time_format = "%Y-%m-%d %H:%M:%S";
749 } else {
750 time_format = "%H:%M:%S";
751 }
752
753 then = now + eta;
754 time_ptr = localtime(&then);
755
756 if (NULL == time_ptr) {
757 show_eta = 0;
758 } else {
759 /* Localtime keeps data stored in a
760 * static buffer that gets overwritten
761 * by time functions. */
762 struct tm time = *time_ptr;
763
764 sprintf(state->str_fineta, "%.16s ", _("ETA"));
765 strftime(state->str_fineta +
766 strlen(state->str_fineta),
767 sizeof(state->str_fineta) - 1 -
768 strlen(state->str_fineta),
769 time_format, &time);
770 }
771
772 if (!show_eta) {
773 unsigned int i;
774 for (i = 0; i < sizeof(state->str_fineta)
775 && state->str_fineta[i] != 0; i++) {
776 state->str_fineta[i] = ' ';
777 }
778 }
779 }
780
781 /*
782 * Now go through all the static portions of the format to work
783 * out how much space will be left for any dynamic portions
784 * (i.e. the progress bar).
785 */
786 static_portion_size = 0;
787 for (segment = 0; state->format[segment].string; segment++) {
788 if (state->format[segment].length < 0) {
789 continue;
790 } else if (state->format[segment].length > 0) {
791 static_portion_size +=
792 state->format[segment].length;
793 } else {
794 static_portion_size +=
795 strlen(state->format[segment].string);
796 }
797 }
798
799 debug("static_portion_size: %d", static_portion_size);
800
801 /*
802 * Assemble the progress bar now we know how big it should be.
803 */
804 if ((state->components_used & PV_DISPLAY_PROGRESS) != 0) {
805 char pct[16];
806 int available_width, i;
807
808 strcpy(state->str_progress, "[");
809
810 if (state->size > 0) {
811 if (state->percentage < 0)
812 state->percentage = 0;
813 if (state->percentage > 100000)
814 state->percentage = 100000;
815 sprintf(pct, "%2ld%%", state->percentage);
816
817 available_width =
818 state->width - static_portion_size -
819 strlen(pct) - 3;
820
821 if (available_width < 0)
822 available_width = 0;
823
824 if (available_width >
825 (int) (sizeof(state->str_progress)) - 16)
826 available_width =
827 sizeof(state->str_progress) - 16;
828
829 for (i = 0;
830 i <
831 (available_width * state->percentage) / 100 -
832 1; i++) {
833 if (i < available_width)
834 strcat(state->str_progress, "=");
835 }
836 if (i < available_width) {
837 strcat(state->str_progress, ">");
838 i++;
839 }
840 for (; i < available_width; i++) {
841 strcat(state->str_progress, " ");
842 }
843 strcat(state->str_progress, "] ");
844 strcat(state->str_progress, pct);
845 } else {
846 int p = state->percentage;
847
848 available_width =
849 state->width - static_portion_size - 5;
850
851 if (available_width < 0)
852 available_width = 0;
853
854 if (available_width >
855 (int) (sizeof(state->str_progress)) - 16)
856 available_width =
857 sizeof(state->str_progress) - 16;
858
859 debug("available_width: %d", available_width);
860
861 if (p > 100)
862 p = 200 - p;
863 for (i = 0; i < (available_width * p) / 100; i++) {
864 if (i < available_width)
865 strcat(state->str_progress, " ");
866 }
867 strcat(state->str_progress, "<=>");
868 for (; i < available_width; i++) {
869 strcat(state->str_progress, " ");
870 }
871 strcat(state->str_progress, "]");
872 }
873
874 /*
875 * If the progress bar won't fit, drop it.
876 */
877 if (strlen(state->str_progress) + static_portion_size >
878 state->width)
879 state->str_progress[0] = 0;
880 }
881
882 /*
883 * We can now build the output string using the format structure.
884 */
885
886 state->display_buffer[0] = 0;
887 display_string_length = 0;
888 for (segment = 0; state->format[segment].string; segment++) {
889 int segment_length;
890 if (state->format[segment].length > 0) {
891 segment_length = state->format[segment].length;
892 } else {
893 segment_length =
894 strlen(state->format[segment].string);
895 }
896 /* Skip empty segments */
897 if (segment_length == 0)
898 continue;
899 /*
900 * Truncate segment if it would make the display string
901 * overflow the buffer
902 */
903 if (segment_length + display_string_length >
904 state->display_buffer_size - 2)
905 segment_length =
906 state->display_buffer_size -
907 display_string_length - 2;
908 if (segment_length < 1)
909 break;
910 /* Skip segment if it would make the display too wide */
911 if (segment_length + display_string_length >
912 (int) (state->width))
913 break;
914 strncat(state->display_buffer,
915 state->format[segment].string, segment_length);
916 display_string_length += segment_length;
917 }
918
919 /*
920 * If the size of our output shrinks, we need to keep appending
921 * spaces at the end, so that we don't leave dangling bits behind.
922 */
923 output_length = strlen(state->display_buffer);
924 if ((output_length < state->prev_length)
925 && ((int) (state->width) >= state->prev_width)) {
926 char spaces[32];
927 int spaces_to_add;
928 spaces_to_add = state->prev_length - output_length;
929 /* Upper boundary on number of spaces */
930 if (spaces_to_add > 15) {
931 spaces_to_add = 15;
932 }
933 output_length += spaces_to_add;
934 spaces[spaces_to_add] = 0;
935 while (--spaces_to_add >= 0) {
936 spaces[spaces_to_add] = ' ';
937 }
938 strcat(state->display_buffer, spaces);
939 }
940 state->prev_width = state->width;
941 state->prev_length = output_length;
942
943 return state->display_buffer;
944 }
945
946
947 /*
948 * Output status information on standard error, where "esec" is the seconds
949 * elapsed since the transfer started, "sl" is the number of bytes transferred
950 * since the last update, and "tot" is the total number of bytes transferred
951 * so far.
952 *
953 * If "sl" is negative, this is the final update so the rate is given as an
954 * an average over the whole transfer; otherwise the current rate is shown.
955 *
956 * In line mode, "sl" and "tot" are in lines, not bytes.
957 */
pv_display(pvstate_t state,long double esec,long long sl,long long tot)958 void pv_display(pvstate_t state, long double esec, long long sl,
959 long long tot)
960 {
961 char *display;
962
963 if (NULL == state)
964 return;
965
966 /*
967 * If the display options need reparsing, do so to generate new
968 * formatting parameters.
969 */
970 if (state->reparse_display) {
971 pv__format_init(state);
972 state->reparse_display = 0;
973 }
974
975 pv_sig_checkbg();
976
977 display = pv__format(state, esec, sl, tot);
978 if (NULL == display)
979 return;
980
981 if (state->numeric) {
982 write(STDERR_FILENO, display, strlen(display));
983 } else if (state->cursor) {
984 if (pv_in_foreground()) {
985 pv_crs_update(state, display);
986 state->display_visible = true;
987 }
988 } else {
989 if (pv_in_foreground()) {
990 write(STDERR_FILENO, display, strlen(display));
991 write(STDERR_FILENO, "\r", 1);
992 state->display_visible = true;
993 }
994 }
995
996 debug("%s: [%s]", "display", display);
997 }
998
999 /* EOF */
1000