Move the formatter functions into separate files so that display.c is less of a monolith; and replace the ctype.h macros with internal versions.
This commit is contained in:
+11
@@ -22,6 +22,17 @@ src/pv/cursor.c \
|
||||
src/pv/display.c \
|
||||
src/pv/elapsedtime.c \
|
||||
src/pv/file.c \
|
||||
src/pv/format/averagerate.c \
|
||||
src/pv/format/bufferpercent.c \
|
||||
src/pv/format/bytes.c \
|
||||
src/pv/format/eta.c \
|
||||
src/pv/format/fineta.c \
|
||||
src/pv/format/lastwritten.c \
|
||||
src/pv/format/name.c \
|
||||
src/pv/format/previousline.c \
|
||||
src/pv/format/progressbar.c \
|
||||
src/pv/format/rate.c \
|
||||
src/pv/format/timer.c \
|
||||
src/pv/loop.c \
|
||||
src/pv/number.c \
|
||||
src/pv/proctitle.c \
|
||||
|
||||
@@ -351,6 +351,23 @@ struct pvstate_s {
|
||||
} transfer;
|
||||
};
|
||||
|
||||
typedef struct pvdisplay_s *pvdisplay_t;
|
||||
typedef struct pvdisplay_segment_s *pvdisplay_segment_t;
|
||||
|
||||
|
||||
/* Pointer to a formatter function. */
|
||||
typedef size_t (*pvdisplay_formatter_t)(pvstate_t, pvdisplay_t, pvdisplay_segment_t, char *, size_t, size_t);
|
||||
|
||||
|
||||
/*
|
||||
* Structure defining a format string sequence following a %.
|
||||
*/
|
||||
struct pvdisplay_component_s {
|
||||
/*@null@ */ const char *match; /* string to match */
|
||||
/*@null@ */ pvdisplay_formatter_t function; /* function to call */
|
||||
bool dynamic; /* whether it can scale with screen size */
|
||||
};
|
||||
|
||||
|
||||
struct pvwatchfd_s {
|
||||
#ifdef __APPLE__
|
||||
@@ -371,13 +388,53 @@ struct pvwatchfd_s {
|
||||
};
|
||||
typedef struct pvwatchfd_s *pvwatchfd_t;
|
||||
|
||||
typedef struct pvdisplay_s *pvdisplay_t;
|
||||
typedef struct pvdisplay_segment_s *pvdisplay_segment_t;
|
||||
|
||||
void pv_error(pvstate_t, char *, ...);
|
||||
|
||||
int pv_main_loop(pvstate_t);
|
||||
void pv_calculate_transfer_rate(pvstate_t, bool);
|
||||
|
||||
long pv_bound_long(long, long, long);
|
||||
long pv_seconds_remaining(const off_t, const off_t, const long double);
|
||||
void pv_si_prefix(long double *, char *, const long double, pvtransfercount_t);
|
||||
void pv_describe_amount(char *, size_t, char *, long double, char *, char *, pvtransfercount_t);
|
||||
|
||||
size_t pv_formatter_segmentcontent(char *, pvdisplay_segment_t, char *, size_t, size_t);
|
||||
|
||||
/*
|
||||
* Formatting functions.
|
||||
*
|
||||
* Each formatting function takes a state, the current display, and the
|
||||
* segment it's for; it also takes a buffer, with a particular size, and an
|
||||
* offset at which to start writing to the buffer.
|
||||
*
|
||||
* If the component is dynamically sized (such as a progress bar with no
|
||||
* chosen_size constraint), the segment's "width" is expected to have
|
||||
* already been populated by the caller, with the target width.
|
||||
*
|
||||
* The function writes the appropriate string to the buffer at the offset,
|
||||
* and updates the segment's "offset" and "bytes". The number of bytes
|
||||
* written ("bytes") is also returned; it will be 0 if the string would not
|
||||
* fit into the buffer.
|
||||
*
|
||||
* The caller is expected to update the segment's "width".
|
||||
*
|
||||
* If called with a buffer size of 0, only the side effects occur (such as
|
||||
* setting flags like display->showing_timer).
|
||||
*/
|
||||
size_t pv_formatter_progress(pvstate_t, pvdisplay_t, pvdisplay_segment_t, char *, size_t, size_t);
|
||||
size_t pv_formatter_progress_bar_only(pvstate_t, pvdisplay_t, pvdisplay_segment_t, char *, size_t, size_t);
|
||||
size_t pv_formatter_progress_amount_only(pvstate_t, pvdisplay_t, pvdisplay_segment_t, char *, size_t, size_t);
|
||||
size_t pv_formatter_timer(pvstate_t, pvdisplay_t, pvdisplay_segment_t, char *, size_t, size_t);
|
||||
size_t pv_formatter_eta(pvstate_t, pvdisplay_t, pvdisplay_segment_t, char *, size_t, size_t);
|
||||
size_t pv_formatter_fineta(pvstate_t, pvdisplay_t, pvdisplay_segment_t, char *, size_t, size_t);
|
||||
size_t pv_formatter_rate(pvstate_t, pvdisplay_t, pvdisplay_segment_t, char *, size_t, size_t);
|
||||
size_t pv_formatter_average_rate(pvstate_t, pvdisplay_t, pvdisplay_segment_t, char *, size_t, size_t);
|
||||
size_t pv_formatter_bytes(pvstate_t, pvdisplay_t, pvdisplay_segment_t, char *, size_t, size_t);
|
||||
size_t pv_formatter_buffer_percent(pvstate_t, pvdisplay_t, pvdisplay_segment_t, char *, size_t, size_t);
|
||||
size_t pv_formatter_last_written(pvstate_t, pvdisplay_t, pvdisplay_segment_t, char *, size_t, size_t);
|
||||
size_t pv_formatter_previous_line(pvstate_t, pvdisplay_t, pvdisplay_segment_t, char *, size_t, size_t);
|
||||
size_t pv_formatter_name(pvstate_t, pvdisplay_t, pvdisplay_segment_t, char *, size_t, size_t);
|
||||
|
||||
bool pv_format(pvstate_t, /*@null@*/ const char *, pvdisplay_t, bool, bool);
|
||||
void pv_display(pvstate_t, bool);
|
||||
ssize_t pv_transfer(pvstate_t, int, bool *, bool *, off_t, long *);
|
||||
|
||||
@@ -54,6 +54,11 @@ typedef enum {
|
||||
* Simple string functions for processing numbers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Return true if the character is an ASCII digit.
|
||||
*/
|
||||
extern bool pv_isdigit(char);
|
||||
|
||||
/*
|
||||
* Return the given string converted to a double, for use as a time
|
||||
* interval.
|
||||
@@ -120,6 +125,11 @@ extern size_t pv_strlcat(char *, const char *, size_t);
|
||||
*/
|
||||
extern size_t pv_strwidth(const char *, size_t);
|
||||
|
||||
/*
|
||||
* Return true if the character is printable.
|
||||
*/
|
||||
extern bool pv_isprint(char);
|
||||
|
||||
/*
|
||||
* Functions relating to elapsed time.
|
||||
*/
|
||||
|
||||
+50
-801
@@ -14,7 +14,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
@@ -171,7 +170,7 @@ void pv_screensize(unsigned int *width, unsigned int *height)
|
||||
* Return the original value x so that it has been clamped between
|
||||
* [min..max]
|
||||
*/
|
||||
static long bound_long(long x, long min, long max)
|
||||
long pv_bound_long(long x, long min, long max)
|
||||
{
|
||||
return x < min ? min : x > max ? max : x;
|
||||
}
|
||||
@@ -182,7 +181,7 @@ static long bound_long(long x, long min, long max)
|
||||
* transfer, and the current average transfer rate, return the estimated
|
||||
* number of seconds until completion.
|
||||
*/
|
||||
static long pv__seconds_remaining(const off_t so_far, const off_t total, const long double rate)
|
||||
long pv_seconds_remaining(const off_t so_far, const off_t total, const long double rate)
|
||||
{
|
||||
long double amount_left;
|
||||
|
||||
@@ -203,7 +202,7 @@ static long pv__seconds_remaining(const off_t so_far, const off_t total, const l
|
||||
* "prefix" is set to "i" to denote MiB etc (IEEE1541). Thus "prefix"
|
||||
* should be at least 3 bytes long (to include the terminating null).
|
||||
*/
|
||||
static void pv__si_prefix(long double *value, char *prefix, const long double ratio, pvtransfercount_t count_type)
|
||||
void pv_si_prefix(long double *value, char *prefix, const long double ratio, pvtransfercount_t count_type)
|
||||
{
|
||||
static char *pfx_000 = NULL; /* kilo, mega, etc */
|
||||
static char *pfx_024 = NULL; /* kibi, mibi, etc */
|
||||
@@ -362,7 +361,7 @@ static void pv__si_prefix(long double *value, char *prefix, const long double ra
|
||||
* The "format" string is in sprintf format and must contain exactly one %
|
||||
* parameter (a %s) which will expand to the string described above.
|
||||
*/
|
||||
static void pv__sizestr(char *buffer, size_t bufsize, char *format,
|
||||
void pv_describe_amount(char *buffer, size_t bufsize, char *format,
|
||||
long double amount, char *suffix_basic, char *suffix_bytes, pvtransfercount_t count_type)
|
||||
{
|
||||
char sizestr_buffer[256]; /* flawfinder: ignore */
|
||||
@@ -375,7 +374,7 @@ static void pv__sizestr(char *buffer, size_t bufsize, char *format,
|
||||
* flawfinder: sizestr_buffer and si_prefix are explicitly zeroed;
|
||||
* sizestr_buffer is only ever used with pv_snprintf() along with
|
||||
* its buffer size; si_prefix is only populated by pv_snprintf()
|
||||
* along with its size, and by pv__si_prefix() which explicitly only
|
||||
* along with its size, and by pv_si_prefix() which explicitly only
|
||||
* needs 3 bytes.
|
||||
*/
|
||||
|
||||
@@ -397,7 +396,7 @@ static void pv__sizestr(char *buffer, size_t bufsize, char *format,
|
||||
|
||||
display_amount = amount;
|
||||
|
||||
pv__si_prefix(&display_amount, si_prefix, divider, count_type);
|
||||
pv_si_prefix(&display_amount, si_prefix, divider, count_type);
|
||||
|
||||
/* Make sure we don't overrun our buffer. */
|
||||
if (display_amount > 100000)
|
||||
@@ -425,35 +424,13 @@ static void pv__sizestr(char *buffer, size_t bufsize, char *format,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Formatting functions.
|
||||
*
|
||||
* Each formatting function takes a state, the current display, and the
|
||||
* segment it's for; it also takes a buffer, with a particular size, and an
|
||||
* offset at which to start writing to the buffer.
|
||||
*
|
||||
* If the component is dynamically sized (such as a progress bar with no
|
||||
* chosen_size constraint), the segment's "width" is expected to have
|
||||
* already been populated by the caller, with the target width.
|
||||
*
|
||||
* The function writes the appropriate string to the buffer at the offset,
|
||||
* and updates the segment's "offset" and "bytes". The number of bytes
|
||||
* written ("bytes") is also returned; it will be 0 if the string would not
|
||||
* fit into the buffer.
|
||||
*
|
||||
* The caller is expected to update the segment's "width".
|
||||
*
|
||||
* If called with a buffer size of 0, only the side effects occur (such as
|
||||
* setting flags like display->showing_timer).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Add a null-terminated string to the buffer if there is room for it,
|
||||
* updating the segment's offset and bytes values and returning the bytes
|
||||
* value, or treating the byte count as zero if there's insufficient space.
|
||||
*/
|
||||
static size_t pv__format_segmentcontent(char *content, pvdisplay_segment_t segment, char *buffer, size_t buffer_size,
|
||||
size_t offset)
|
||||
size_t pv_formatter_segmentcontent(char *content, pvdisplay_segment_t segment, char *buffer, size_t buffer_size,
|
||||
size_t offset)
|
||||
{
|
||||
size_t bytes;
|
||||
|
||||
@@ -477,739 +454,6 @@ static size_t pv__format_segmentcontent(char *content, pvdisplay_segment_t segme
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write a progress bar to a buffer, in known-size or rate-gauge mode - a
|
||||
* bar, and a percentage (size) or max rate (gauge). The total width of the
|
||||
* content is bounded to the given width. Returns the number of bytes
|
||||
* written to the buffer.
|
||||
*
|
||||
* If "bar_sides" is false, only the bar itself is rendered, not the opening
|
||||
* and closing characters.
|
||||
*
|
||||
* If "include_bar" is false, the bar is omitted entirely.
|
||||
*
|
||||
* If "include_amount" is false, the percentage or rate after the bar is
|
||||
* omitted.
|
||||
*
|
||||
* This is only called by pv__format_progress().
|
||||
*/
|
||||
static size_t pv__format_progress_knownsize(pvstate_t state, pvdisplay_t display, char *buffer, size_t buffer_size,
|
||||
size_t width, bool bar_sides, bool include_bar, bool include_amount)
|
||||
{
|
||||
char after_bar[32]; /* flawfinder: ignore - only populated by pv_snprintf(). */
|
||||
size_t after_bar_bytes, after_bar_width;
|
||||
size_t bar_area_width, filled_bar_width, buffer_offset, pad_count;
|
||||
int bar_percentage;
|
||||
|
||||
buffer[0] = '\0';
|
||||
|
||||
memset(after_bar, 0, sizeof(after_bar));
|
||||
|
||||
if (state->control.size > 0) {
|
||||
/* Percentage of data transferred. */
|
||||
bar_percentage = state->calc.percentage;
|
||||
(void) pv_snprintf(after_bar, sizeof(after_bar), " %3ld%%", bar_percentage);
|
||||
} else {
|
||||
/* Current rate vs max rate. */
|
||||
bar_percentage = 0;
|
||||
if (state->calc.rate_max > 0) {
|
||||
bar_percentage = (int) (100.0 * state->calc.transfer_rate / state->calc.rate_max);
|
||||
}
|
||||
|
||||
/*@-mustfreefresh@ */
|
||||
if (state->control.bits && !state->control.linemode) {
|
||||
/* bits per second */
|
||||
pv__sizestr(after_bar, sizeof(after_bar), "/%s",
|
||||
8.0 * state->calc.rate_max, "", _("b/s"), display->count_type);
|
||||
} else {
|
||||
/* bytes or lines per second */
|
||||
pv__sizestr(after_bar, sizeof(after_bar),
|
||||
"/%s", state->calc.rate_max, _("/s"), _("B/s"), display->count_type);
|
||||
}
|
||||
/*@+mustfreefresh@ *//* splint: see above about gettext(). */
|
||||
}
|
||||
|
||||
if (!include_amount)
|
||||
after_bar[0] = '\0';
|
||||
|
||||
after_bar_bytes = strlen(after_bar); /* flawfinder: ignore */
|
||||
/* flawfinder: always \0-terminated by pv_snprintf() and the earlier memset(). */
|
||||
after_bar_width = pv_strwidth(after_bar, after_bar_bytes);
|
||||
|
||||
if (!include_bar) {
|
||||
if (buffer_size < after_bar_bytes)
|
||||
return 0;
|
||||
if (after_bar_bytes > 1) {
|
||||
/* NB we skip the leading space. */
|
||||
memmove(buffer, after_bar + 1, after_bar_bytes - 1);
|
||||
buffer[after_bar_bytes - 1] = '\0';
|
||||
return after_bar_bytes - 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bar_sides) {
|
||||
if (width < (after_bar_width + 2))
|
||||
return 0;
|
||||
bar_area_width = width - after_bar_width - 2;
|
||||
} else {
|
||||
if (width < after_bar_width)
|
||||
return 0;
|
||||
bar_area_width = width - after_bar_width;
|
||||
}
|
||||
|
||||
if (bar_area_width > buffer_size - 16)
|
||||
bar_area_width = buffer_size - 16;
|
||||
|
||||
filled_bar_width = (size_t) (bar_area_width * bar_percentage) / 100;
|
||||
/* Leave room for the tip of the bar. */
|
||||
if (filled_bar_width > 0)
|
||||
filled_bar_width--;
|
||||
|
||||
debug("width=%d bar_area_width=%d filled_bar_width=%d", width, bar_area_width, filled_bar_width);
|
||||
|
||||
buffer_offset = 0;
|
||||
|
||||
if (bar_sides) {
|
||||
/* The opening of the bar area. */
|
||||
buffer[buffer_offset++] = '[';
|
||||
}
|
||||
|
||||
/* The bar portion. */
|
||||
for (pad_count = 0; pad_count < filled_bar_width && buffer_offset < buffer_size - 1; pad_count++) {
|
||||
if (pad_count < bar_area_width)
|
||||
buffer[buffer_offset++] = '=';
|
||||
}
|
||||
|
||||
/* The tip of the bar, if not at 100%. */
|
||||
if (pad_count < bar_area_width) {
|
||||
if (buffer_offset < buffer_size - 1)
|
||||
buffer[buffer_offset++] = '>';
|
||||
pad_count++;
|
||||
}
|
||||
|
||||
/* The spaces after the bar. */
|
||||
for (; pad_count < bar_area_width; pad_count++) {
|
||||
if (buffer_offset < buffer_size - 1)
|
||||
buffer[buffer_offset++] = ' ';
|
||||
}
|
||||
|
||||
if (bar_sides) {
|
||||
/* The closure of the bar area. */
|
||||
if (buffer_offset < buffer_size - 1)
|
||||
buffer[buffer_offset++] = ']';
|
||||
}
|
||||
|
||||
/* The percentage. */
|
||||
if (after_bar_bytes > 0 && after_bar_bytes < (buffer_size - 1 - buffer_offset)) {
|
||||
memmove(buffer + buffer_offset, after_bar, after_bar_bytes);
|
||||
buffer_offset += after_bar_bytes;
|
||||
}
|
||||
|
||||
buffer[buffer_offset] = '\0';
|
||||
|
||||
return buffer_offset;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write a progress bar to a buffer, in unknown-size mode - just a moving
|
||||
* indicator. The total width of the content is bounded to the given width.
|
||||
* Returns the number of bytes written to the buffer.
|
||||
*
|
||||
* If "bar_sides" is false, only the bar itself is rendered, not the opening
|
||||
* and closing characters.
|
||||
*
|
||||
* This is only called by pv__format_progress().
|
||||
*/
|
||||
static size_t pv__format_progress_unknownsize(pvstate_t state, /*@unused@ */
|
||||
__attribute__((unused)) pvdisplay_t display, char *buffer,
|
||||
size_t buffer_size, size_t width, bool bar_sides)
|
||||
{
|
||||
size_t bar_area_width, buffer_offset, pad_count;
|
||||
size_t indicator_position;
|
||||
|
||||
buffer[0] = '\0';
|
||||
|
||||
if (bar_sides) {
|
||||
if (width < 6)
|
||||
return 0;
|
||||
bar_area_width = width - 5;
|
||||
} else {
|
||||
if (width < 5)
|
||||
return 0;
|
||||
bar_area_width = width - 3;
|
||||
}
|
||||
|
||||
if (bar_area_width > buffer_size - 16)
|
||||
bar_area_width = buffer_size - 16;
|
||||
|
||||
/*
|
||||
* Note that pv_calculate_transfer_rate() sets the percentage when
|
||||
* the size is unknown to a value that goes 0 - 200 and resets, so
|
||||
* here we make values above 100 send the indicator back down again,
|
||||
* so it moves back and forth.
|
||||
*/
|
||||
indicator_position = (size_t) (state->calc.percentage);
|
||||
if (indicator_position > 200)
|
||||
indicator_position = indicator_position % 200;
|
||||
if (indicator_position > 100 && indicator_position <= 200)
|
||||
indicator_position = 200 - indicator_position;
|
||||
|
||||
buffer_offset = 0;
|
||||
|
||||
if (bar_sides) {
|
||||
/* The opening of the bar area. */
|
||||
buffer[buffer_offset++] = '[';
|
||||
}
|
||||
|
||||
/* The spaces before the indicator. */
|
||||
for (pad_count = 0; pad_count < (bar_area_width * indicator_position) / 100; pad_count++) {
|
||||
if (pad_count < bar_area_width && buffer_offset < buffer_size - 1)
|
||||
buffer[buffer_offset++] = ' ';
|
||||
}
|
||||
|
||||
/* The indicator. */
|
||||
if (buffer_offset < buffer_size - 4) {
|
||||
buffer[buffer_offset++] = '<';
|
||||
buffer[buffer_offset++] = '=';
|
||||
buffer[buffer_offset++] = '>';
|
||||
}
|
||||
|
||||
/* The spaces after the indicator. */
|
||||
for (; pad_count < bar_area_width; pad_count++) {
|
||||
if (pad_count < bar_area_width && buffer_offset < buffer_size - 1)
|
||||
buffer[buffer_offset++] = ' ';
|
||||
}
|
||||
|
||||
if (bar_sides) {
|
||||
/* The closure of the bar area. */
|
||||
if (buffer_offset < buffer_size - 1)
|
||||
buffer[buffer_offset++] = ']';
|
||||
}
|
||||
|
||||
buffer[buffer_offset] = '\0';
|
||||
|
||||
return buffer_offset;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Progress bar.
|
||||
*/
|
||||
static size_t pv__format_progress(pvstate_t state, pvdisplay_t display, pvdisplay_segment_t segment, char *buffer,
|
||||
size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[1024]; /* flawfinder: ignore - always bounded */
|
||||
size_t bytes;
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
if (state->control.size > 0 || state->control.rate_gauge) {
|
||||
/* Known size or rate gauge - bar with percentage. */
|
||||
bytes =
|
||||
pv__format_progress_knownsize(state, display, content, sizeof(content), segment->width, true, true,
|
||||
true);
|
||||
} else {
|
||||
/* Unknown size - back-and-forth moving indicator. */
|
||||
bytes = pv__format_progress_unknownsize(state, display, content, sizeof(content), segment->width, true);
|
||||
}
|
||||
|
||||
content[bytes] = '\0';
|
||||
|
||||
return pv__format_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Progress bar, without sides and without a number afterwards.
|
||||
*/
|
||||
static size_t pv__format_progress_bar_only(pvstate_t state, pvdisplay_t display, pvdisplay_segment_t segment,
|
||||
char *buffer, size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[1024]; /* flawfinder: ignore - always bounded */
|
||||
size_t bytes;
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
if (state->control.size > 0 || state->control.rate_gauge) {
|
||||
/* Known size or rate gauge - bar with percentage. */
|
||||
bytes =
|
||||
pv__format_progress_knownsize(state, display, content, sizeof(content), segment->width, false, true,
|
||||
false);
|
||||
} else {
|
||||
/* Unknown size - back-and-forth moving indicator. */
|
||||
bytes =
|
||||
pv__format_progress_unknownsize(state, display, content, sizeof(content), segment->width, false);
|
||||
}
|
||||
|
||||
content[bytes] = '\0';
|
||||
|
||||
return pv__format_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The number after the progress bar.
|
||||
*/
|
||||
static size_t pv__format_progress_amount_only(pvstate_t state, pvdisplay_t display, pvdisplay_segment_t segment,
|
||||
char *buffer, size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[256]; /* flawfinder: ignore - always bounded */
|
||||
size_t bytes;
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
if (state->control.size > 0 || state->control.rate_gauge) {
|
||||
/* Known size or rate gauge - percentage or rate. */
|
||||
bytes =
|
||||
pv__format_progress_knownsize(state, display, content, sizeof(content), segment->width, false,
|
||||
false, true);
|
||||
} else {
|
||||
/* Unknown size - no number. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
content[bytes] = '\0';
|
||||
|
||||
return pv__format_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Elapsed time.
|
||||
*/
|
||||
static size_t pv__format_timer(pvstate_t state, pvdisplay_t display, pvdisplay_segment_t segment, char *buffer,
|
||||
size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[128]; /* flawfinder: ignore - always bounded */
|
||||
|
||||
display->showing_timer = true;
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Bounds check, so we don't overrun the prefix buffer. This does
|
||||
* mean that the timer will stop at a 100,000 hours, but since
|
||||
* that's 11 years, it shouldn't be a problem.
|
||||
*/
|
||||
if (state->transfer.elapsed_seconds > (long double) 360000000.0L)
|
||||
state->transfer.elapsed_seconds = (long double) 360000000.0L;
|
||||
|
||||
/*
|
||||
* If the elapsed time is more than a day, include a day count as
|
||||
* well as hours, minutes, and seconds.
|
||||
*/
|
||||
if (state->transfer.elapsed_seconds > (long double) 86400.0L) {
|
||||
(void) pv_snprintf(content,
|
||||
sizeof(content),
|
||||
"%ld:%02ld:%02ld:%02ld",
|
||||
((long) (state->transfer.elapsed_seconds)) / 86400,
|
||||
(((long) (state->transfer.elapsed_seconds)) / 3600) %
|
||||
24, (((long) (state->transfer.elapsed_seconds)) / 60) % 60,
|
||||
((long) (state->transfer.elapsed_seconds)) % 60);
|
||||
} else {
|
||||
(void) pv_snprintf(content,
|
||||
sizeof(content),
|
||||
"%ld:%02ld:%02ld",
|
||||
((long) (state->transfer.elapsed_seconds)) / 3600,
|
||||
(((long) (state->transfer.elapsed_seconds)) / 60) % 60,
|
||||
((long) (state->transfer.elapsed_seconds)) % 60);
|
||||
}
|
||||
|
||||
return pv__format_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Estimated time until completion.
|
||||
*/
|
||||
static size_t pv__format_eta(pvstate_t state, pvdisplay_t display, pvdisplay_segment_t segment, char *buffer,
|
||||
size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[128]; /* flawfinder: ignore - always bounded */
|
||||
long eta;
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
/*
|
||||
* Don't try to calculate this if the size is not known.
|
||||
*/
|
||||
if (state->control.size < 1)
|
||||
return 0;
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
eta =
|
||||
pv__seconds_remaining((state->transfer.transferred - display->initial_offset),
|
||||
state->control.size - display->initial_offset, state->calc.current_avg_rate);
|
||||
|
||||
/*
|
||||
* Bounds check, so we don't overrun the suffix buffer. This means
|
||||
* the ETA will always be less than 100,000 hours.
|
||||
*/
|
||||
eta = bound_long(eta, 0, (long) 360000000L);
|
||||
|
||||
/*
|
||||
* If the ETA is more than a day, include a day count as well as
|
||||
* hours, minutes, and seconds.
|
||||
*/
|
||||
/*@-mustfreefresh@ */
|
||||
if (eta > 86400L) {
|
||||
(void) pv_snprintf(content,
|
||||
sizeof(content),
|
||||
"%.16s %ld:%02ld:%02ld:%02ld",
|
||||
_("ETA"), eta / 86400, (eta / 3600) % 24, (eta / 60) % 60, eta % 60);
|
||||
} else {
|
||||
(void) pv_snprintf(content,
|
||||
sizeof(content),
|
||||
"%.16s %ld:%02ld:%02ld", _("ETA"), eta / 3600, (eta / 60) % 60, eta % 60);
|
||||
}
|
||||
/*@+mustfreefresh@ *//* splint: see above. */
|
||||
|
||||
/*
|
||||
* If this is the final update, show a blank space where the ETA
|
||||
* used to be.
|
||||
*/
|
||||
if (display->final_update) {
|
||||
size_t erase_idx;
|
||||
for (erase_idx = 0; erase_idx < sizeof(content) && content[erase_idx] != '\0'; erase_idx++) {
|
||||
content[erase_idx] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return pv__format_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Estimated local time of completion.
|
||||
*/
|
||||
static size_t pv__format_fineta(pvstate_t state, pvdisplay_t display, pvdisplay_segment_t segment, char *buffer,
|
||||
size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[128]; /* flawfinder: ignore - always bounded */
|
||||
time_t now, then;
|
||||
struct tm *time_ptr;
|
||||
long eta;
|
||||
const char *time_format;
|
||||
bool show_fineta;
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
/*
|
||||
* Don't try to calculate this if the size is not known.
|
||||
*/
|
||||
if (state->control.size < 1)
|
||||
return 0;
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
now = time(NULL);
|
||||
show_fineta = true;
|
||||
time_format = NULL;
|
||||
|
||||
/*
|
||||
* The completion clock time may be hidden by a failed localtime
|
||||
* lookup.
|
||||
*/
|
||||
|
||||
eta = pv__seconds_remaining(state->transfer.transferred - display->initial_offset,
|
||||
state->control.size - display->initial_offset, state->calc.current_avg_rate);
|
||||
|
||||
/* Bounds check - see pv__format_eta(). */
|
||||
eta = bound_long(eta, 0, (long) 360000000L);
|
||||
|
||||
/*
|
||||
* Only include the date if the ETA is more than 6 hours
|
||||
* away.
|
||||
*/
|
||||
if (eta > (long) (6 * 3600)) {
|
||||
time_format = "%Y-%m-%d %H:%M:%S";
|
||||
} else {
|
||||
time_format = "%H:%M:%S";
|
||||
}
|
||||
|
||||
then = now + eta;
|
||||
time_ptr = localtime(&then);
|
||||
|
||||
if (NULL == time_ptr) {
|
||||
show_fineta = false;
|
||||
} else {
|
||||
/*
|
||||
* The localtime() function keeps data stored in a static
|
||||
* buffer that gets overwritten by time functions.
|
||||
*/
|
||||
struct tm time = *time_ptr;
|
||||
size_t content_bytes;
|
||||
|
||||
/*@-mustfreefresh@ */
|
||||
(void) pv_snprintf(content, sizeof(content), "%.16s ", _("FIN"));
|
||||
/*@+mustfreefresh@ *//* splint: see above. */
|
||||
content_bytes = strlen(content); /* flawfinder: ignore */
|
||||
/* flawfinder: always bounded with \0 by pv_snprintf(). */
|
||||
(void) strftime(content + content_bytes, sizeof(content) - 1 - content_bytes, time_format, &time);
|
||||
}
|
||||
|
||||
if (!show_fineta) {
|
||||
size_t erase_idx;
|
||||
for (erase_idx = 0; erase_idx < sizeof(content) && content[erase_idx] != '\0'; erase_idx++) {
|
||||
content[erase_idx] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return pv__format_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Transfer rate.
|
||||
*/
|
||||
static size_t pv__format_rate(pvstate_t state, pvdisplay_t display, pvdisplay_segment_t segment, char *buffer,
|
||||
size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[128]; /* flawfinder: ignore - always bounded */
|
||||
|
||||
display->showing_rate = true;
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
/*@-mustfreefresh@ */
|
||||
if (state->control.bits && !state->control.linemode) {
|
||||
/* bits per second */
|
||||
pv__sizestr(content, sizeof(content), "[%s]",
|
||||
8 * state->calc.transfer_rate, "", _("b/s"), display->count_type);
|
||||
} else {
|
||||
/* bytes or lines per second */
|
||||
pv__sizestr(content, sizeof(content),
|
||||
"[%s]", state->calc.transfer_rate, _("/s"), _("B/s"), display->count_type);
|
||||
}
|
||||
/*@+mustfreefresh@ *//* splint: see above. */
|
||||
|
||||
return pv__format_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Average transfer rate.
|
||||
*/
|
||||
static size_t pv__format_average_rate(pvstate_t state, pvdisplay_t display, pvdisplay_segment_t segment, char *buffer,
|
||||
size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[128]; /* flawfinder: ignore - always bounded */
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
/*@-mustfreefresh@ */
|
||||
if (state->control.bits && !state->control.linemode) {
|
||||
/* bits per second */
|
||||
pv__sizestr(content, sizeof(content),
|
||||
"(%s)", 8 * state->calc.average_rate, "", _("b/s"), display->count_type);
|
||||
} else {
|
||||
/* bytes or lines per second */
|
||||
pv__sizestr(content,
|
||||
sizeof(content), "(%s)", state->calc.average_rate, _("/s"), _("B/s"), display->count_type);
|
||||
}
|
||||
/*@+mustfreefresh@ *//* splint: see above. */
|
||||
|
||||
return pv__format_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Number of bytes or lines transferred.
|
||||
*/
|
||||
static size_t pv__format_bytes(pvstate_t state, pvdisplay_t display, pvdisplay_segment_t segment, char *buffer,
|
||||
size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[128]; /* flawfinder: ignore - always bounded */
|
||||
|
||||
display->showing_bytes = true;
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
/*@-mustfreefresh@ */
|
||||
if (state->control.bits && !state->control.linemode) {
|
||||
pv__sizestr(content, sizeof(content), "%s",
|
||||
(long double) (state->transfer.transferred * 8), "", _("b"), display->count_type);
|
||||
} else {
|
||||
pv__sizestr(content, sizeof(content), "%s",
|
||||
(long double) (state->transfer.transferred), "", _("B"), display->count_type);
|
||||
}
|
||||
/*@+mustfreefresh@ *//* splint - false positive from gettext(). */
|
||||
|
||||
return pv__format_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Percentage transfer buffer utilisation.
|
||||
*/
|
||||
static size_t pv__format_buffer_percent(pvstate_t state, /*@unused@ */ __attribute__((unused)) pvdisplay_t display,
|
||||
pvdisplay_segment_t segment, char *buffer, size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[16]; /* flawfinder: ignore - always bounded */
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
if (state->transfer.buffer_size > 0) {
|
||||
int pct_used = pv_percentage((off_t)
|
||||
(state->transfer.read_position - state->transfer.write_position),
|
||||
(off_t)
|
||||
(state->transfer.buffer_size));
|
||||
(void) pv_snprintf(content, sizeof(content), "{%3d%%}", pct_used);
|
||||
}
|
||||
#ifdef HAVE_SPLICE
|
||||
if (state->transfer.splice_used)
|
||||
(void) pv_snprintf(content, sizeof(content), "{%s}", "----");
|
||||
#endif
|
||||
|
||||
return pv__format_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Display the last few bytes written.
|
||||
*
|
||||
* As a side effect, this sets display->lastwritten_bytes to the segment's
|
||||
* chosen_size, if it was previously smaller than that.
|
||||
*/
|
||||
static size_t pv__format_last_written( /*@unused@ */
|
||||
__attribute__((unused)) pvstate_t state, pvdisplay_t display,
|
||||
pvdisplay_segment_t segment, char *buffer, size_t buffer_size,
|
||||
size_t offset)
|
||||
{
|
||||
size_t bytes_to_show, read_offset, remaining;
|
||||
|
||||
display->showing_last_written = true;
|
||||
|
||||
bytes_to_show = segment->chosen_size;
|
||||
if (0 == bytes_to_show)
|
||||
bytes_to_show = segment->width;
|
||||
if (0 == bytes_to_show)
|
||||
return 0;
|
||||
|
||||
if (bytes_to_show > PV_SIZEOF_LASTWRITTEN_BUFFER)
|
||||
bytes_to_show = PV_SIZEOF_LASTWRITTEN_BUFFER;
|
||||
if (bytes_to_show > display->lastwritten_bytes)
|
||||
display->lastwritten_bytes = bytes_to_show;
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
if (offset + bytes_to_show >= buffer_size)
|
||||
return 0;
|
||||
|
||||
segment->offset = offset;
|
||||
segment->bytes = bytes_to_show;
|
||||
|
||||
read_offset = display->lastwritten_bytes - bytes_to_show;
|
||||
for (remaining = bytes_to_show; remaining > 0; remaining--) {
|
||||
int display_char = (int) (display->lastwritten_buffer[read_offset++]);
|
||||
buffer[offset++] = isprint(display_char) ? (char) display_char : '.';
|
||||
}
|
||||
|
||||
return bytes_to_show;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Display the previously written line.
|
||||
*/
|
||||
static size_t pv__format_previous_line( /*@unused@ */
|
||||
__attribute__((unused)) pvstate_t state, pvdisplay_t display,
|
||||
pvdisplay_segment_t segment, char *buffer, size_t buffer_size,
|
||||
size_t offset)
|
||||
{
|
||||
size_t bytes_to_show, read_offset, remaining;
|
||||
|
||||
display->showing_previous_line = true;
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
bytes_to_show = segment->chosen_size;
|
||||
if (0 == bytes_to_show)
|
||||
bytes_to_show = segment->width;
|
||||
if (0 == bytes_to_show)
|
||||
return 0;
|
||||
|
||||
if (bytes_to_show > PV_SIZEOF_PREVLINE_BUFFER)
|
||||
bytes_to_show = PV_SIZEOF_PREVLINE_BUFFER;
|
||||
|
||||
if (offset + bytes_to_show >= buffer_size)
|
||||
return 0;
|
||||
|
||||
segment->offset = offset;
|
||||
segment->bytes = bytes_to_show;
|
||||
|
||||
read_offset = 0;
|
||||
for (remaining = bytes_to_show; remaining > 0; remaining--) {
|
||||
int display_char = (int) (display->previous_line[read_offset++]);
|
||||
buffer[offset++] = isprint(display_char) ? (char) display_char : ' ';
|
||||
}
|
||||
|
||||
return bytes_to_show;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Display the transfer's name.
|
||||
*/
|
||||
static size_t pv__format_name(pvstate_t state, /*@unused@ */ __attribute__((unused)) pvdisplay_t display,
|
||||
pvdisplay_segment_t segment, char *buffer, size_t buffer_size, size_t offset)
|
||||
{
|
||||
char string_format[32]; /* flawfinder: ignore - always bounded */
|
||||
char content[512]; /* flawfinder: ignore - always bounded */
|
||||
size_t field_width;
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
field_width = segment->chosen_size;
|
||||
if (field_width < 1)
|
||||
field_width = 9;
|
||||
if (field_width > 500)
|
||||
field_width = 500;
|
||||
|
||||
memset(string_format, 0, sizeof(string_format));
|
||||
(void) pv_snprintf(string_format, sizeof(string_format), "%%%d.500s:", field_width);
|
||||
|
||||
content[0] = '\0';
|
||||
if (state->control.name) {
|
||||
(void) pv_snprintf(content, sizeof(content), string_format, state->control.name);
|
||||
}
|
||||
|
||||
return pv__format_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Populate the display buffer for numeric-output mode.
|
||||
*
|
||||
@@ -1288,45 +532,41 @@ static bool pv__format_numeric(pvstate_t state, pvdisplay_t display)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Format sequence lookup table.
|
||||
*/
|
||||
typedef size_t (*pvdisplay_function_t)(pvstate_t, pvdisplay_t, pvdisplay_segment_t, char *, size_t, size_t);
|
||||
static struct pvdisplay_component_s {
|
||||
/*@null@ */ const char *match;
|
||||
/* string to match */
|
||||
/*@null@ */ pvdisplay_function_t function;
|
||||
/* function to call */
|
||||
bool dynamic; /* whether it can scale with screen size */
|
||||
} format_component[] = {
|
||||
{ "p", &pv__format_progress, true },
|
||||
{ "{progress}", &pv__format_progress, true },
|
||||
{ "{progress-bar-only}", &pv__format_progress_bar_only, true },
|
||||
{ "{progress-amount-only}", &pv__format_progress_amount_only, false },
|
||||
{ "t", &pv__format_timer, false },
|
||||
{ "{timer}", &pv__format_timer, false },
|
||||
{ "e", &pv__format_eta, false },
|
||||
{ "{eta}", &pv__format_eta, false },
|
||||
{ "I", &pv__format_fineta, false },
|
||||
{ "{fineta}", &pv__format_fineta, false },
|
||||
{ "r", &pv__format_rate, false },
|
||||
{ "{rate}", &pv__format_rate, false },
|
||||
{ "a", &pv__format_average_rate, false },
|
||||
{ "{average-rate}", &pv__format_average_rate, false },
|
||||
{ "b", &pv__format_bytes, false },
|
||||
{ "{bytes}", &pv__format_bytes, false },
|
||||
{ "{transferred}", &pv__format_bytes, false },
|
||||
{ "T", &pv__format_buffer_percent, false },
|
||||
{ "{buffer-percent}", &pv__format_buffer_percent, false },
|
||||
{ "A", &pv__format_last_written, false },
|
||||
{ "{last-written}", &pv__format_last_written, false },
|
||||
{ "L", &pv__format_previous_line, true },
|
||||
{ "{previous-line}", &pv__format_previous_line, true },
|
||||
{ "N", &pv__format_name, false },
|
||||
{ "{name}", &pv__format_name, false },
|
||||
{ NULL, NULL, false }
|
||||
};
|
||||
/*@keep@ */ static struct pvdisplay_component_s *pv_format_components(void)
|
||||
{
|
||||
/*@keep@ */ static struct pvdisplay_component_s format_components[] = {
|
||||
{ "p", &pv_formatter_progress, true },
|
||||
{ "{progress}", &pv_formatter_progress, true },
|
||||
{ "{progress-bar-only}", &pv_formatter_progress_bar_only, true },
|
||||
{ "{progress-amount-only}", &pv_formatter_progress_amount_only, false },
|
||||
{ "t", &pv_formatter_timer, false },
|
||||
{ "{timer}", &pv_formatter_timer, false },
|
||||
{ "e", &pv_formatter_eta, false },
|
||||
{ "{eta}", &pv_formatter_eta, false },
|
||||
{ "I", &pv_formatter_fineta, false },
|
||||
{ "{fineta}", &pv_formatter_fineta, false },
|
||||
{ "r", &pv_formatter_rate, false },
|
||||
{ "{rate}", &pv_formatter_rate, false },
|
||||
{ "a", &pv_formatter_average_rate, false },
|
||||
{ "{average-rate}", &pv_formatter_average_rate, false },
|
||||
{ "b", &pv_formatter_bytes, false },
|
||||
{ "{bytes}", &pv_formatter_bytes, false },
|
||||
{ "{transferred}", &pv_formatter_bytes, false },
|
||||
{ "T", &pv_formatter_buffer_percent, false },
|
||||
{ "{buffer-percent}", &pv_formatter_buffer_percent, false },
|
||||
{ "A", &pv_formatter_last_written, false },
|
||||
{ "{last-written}", &pv_formatter_last_written, false },
|
||||
{ "L", &pv_formatter_previous_line, true },
|
||||
{ "{previous-line}", &pv_formatter_previous_line, true },
|
||||
{ "N", &pv_formatter_name, false },
|
||||
{ "{name}", &pv_formatter_name, false },
|
||||
{ NULL, NULL, false }
|
||||
};
|
||||
return format_components;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@@ -1337,8 +577,11 @@ static struct pvdisplay_component_s {
|
||||
char *pv_format_sequences(void)
|
||||
{
|
||||
size_t component_idx, buffer_size, offset;
|
||||
struct pvdisplay_component_s *format_component;
|
||||
char *buffer;
|
||||
|
||||
format_component = pv_format_components();
|
||||
|
||||
buffer_size = 0;
|
||||
for (component_idx = 0; NULL != format_component[component_idx].match; component_idx++) {
|
||||
size_t component_sequence_length = strlen(format_component[component_idx].match); /* flawfinder: ignore */
|
||||
@@ -1370,6 +613,7 @@ char *pv_format_sequences(void)
|
||||
*/
|
||||
static void pv__format_init(pvstate_t state, /*@null@ */ const char *format_supplied, pvdisplay_t display)
|
||||
{
|
||||
struct pvdisplay_component_s *format_component;
|
||||
const char *display_format;
|
||||
size_t strpos;
|
||||
size_t segment;
|
||||
@@ -1379,6 +623,8 @@ static void pv__format_init(pvstate_t state, /*@null@ */ const char *format_supp
|
||||
if (NULL == display)
|
||||
return;
|
||||
|
||||
format_component = pv_format_components();
|
||||
|
||||
display->format_segment_count = 0;
|
||||
memset(display->format, 0, PV_FORMAT_ARRAY_MAX * sizeof(display->format[0]));
|
||||
|
||||
@@ -1447,7 +693,7 @@ static void pv__format_init(pvstate_t state, /*@null@ */ const char *format_supp
|
||||
strpos += (number_end_ptr - &(display_format[strpos]));
|
||||
}
|
||||
#else /* !HAVE_STRTOUL */
|
||||
while (isdigit((int) (display_format[strpos]))) {
|
||||
while (pv_isdigit(display_format[strpos])) {
|
||||
number_prefix = number_prefix * 10;
|
||||
number_prefix += display_format[strpos] - '0';
|
||||
strpos++;
|
||||
@@ -1580,6 +826,7 @@ static void pv__format_init(pvstate_t state, /*@null@ */ const char *format_supp
|
||||
bool pv_format(pvstate_t state, /*@null@ */ const char *format_supplied, pvdisplay_t display, bool reinitialise,
|
||||
bool final)
|
||||
{
|
||||
struct pvdisplay_component_s *format_component;
|
||||
char display_segments[1024]; /* flawfinder: ignore - always bounded */
|
||||
size_t display_segment_offset;
|
||||
size_t segment_idx, dynamic_segment_count;
|
||||
@@ -1596,6 +843,8 @@ bool pv_format(pvstate_t state, /*@null@ */ const char *format_supplied, pvdispl
|
||||
if (NULL == display)
|
||||
return false;
|
||||
|
||||
format_component = pv_format_components();
|
||||
|
||||
/* Populate the display's "final" flag, for formatters. */
|
||||
display->final_update = final;
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Formatter function for average rate.
|
||||
*
|
||||
* Copyright 2024 Andrew Wood
|
||||
*
|
||||
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "pv.h"
|
||||
#include "pv-internal.h"
|
||||
|
||||
|
||||
/*
|
||||
* Average transfer rate.
|
||||
*/
|
||||
size_t pv_formatter_average_rate(pvstate_t state, pvdisplay_t display, pvdisplay_segment_t segment, char *buffer,
|
||||
size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[128]; /* flawfinder: ignore - always bounded */
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
/*@-mustfreefresh@ */
|
||||
if (state->control.bits && !state->control.linemode) {
|
||||
/* bits per second */
|
||||
pv_describe_amount(content, sizeof(content),
|
||||
"(%s)", 8 * state->calc.average_rate, "", _("b/s"), display->count_type);
|
||||
} else {
|
||||
/* bytes or lines per second */
|
||||
pv_describe_amount(content,
|
||||
sizeof(content), "(%s)", state->calc.average_rate, _("/s"), _("B/s"), display->count_type);
|
||||
}
|
||||
/*@+mustfreefresh@ *//* splint: see above. */
|
||||
|
||||
return pv_formatter_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Formatter function for transfer buffer percentage utilisation.
|
||||
*
|
||||
* Copyright 2024 Andrew Wood
|
||||
*
|
||||
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "pv.h"
|
||||
#include "pv-internal.h"
|
||||
|
||||
|
||||
/*
|
||||
* Percentage transfer buffer utilisation.
|
||||
*/
|
||||
size_t pv_formatter_buffer_percent(pvstate_t state, /*@unused@ */ __attribute__((unused)) pvdisplay_t display,
|
||||
pvdisplay_segment_t segment, char *buffer, size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[16]; /* flawfinder: ignore - always bounded */
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
if (state->transfer.buffer_size > 0) {
|
||||
int pct_used = pv_percentage((off_t)
|
||||
(state->transfer.read_position - state->transfer.write_position),
|
||||
(off_t)
|
||||
(state->transfer.buffer_size));
|
||||
(void) pv_snprintf(content, sizeof(content), "{%3d%%}", pct_used);
|
||||
}
|
||||
#ifdef HAVE_SPLICE
|
||||
if (state->transfer.splice_used)
|
||||
(void) pv_snprintf(content, sizeof(content), "{%s}", "----");
|
||||
#endif
|
||||
|
||||
return pv_formatter_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Formatter function for bytes or lines transferred.
|
||||
*
|
||||
* Copyright 2024 Andrew Wood
|
||||
*
|
||||
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "pv.h"
|
||||
#include "pv-internal.h"
|
||||
|
||||
|
||||
/*
|
||||
* Number of bytes or lines transferred.
|
||||
*/
|
||||
size_t pv_formatter_bytes(pvstate_t state, pvdisplay_t display, pvdisplay_segment_t segment, char *buffer,
|
||||
size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[128]; /* flawfinder: ignore - always bounded */
|
||||
|
||||
display->showing_bytes = true;
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
/*@-mustfreefresh@ */
|
||||
if (state->control.bits && !state->control.linemode) {
|
||||
pv_describe_amount(content, sizeof(content), "%s",
|
||||
(long double) (state->transfer.transferred * 8), "", _("b"), display->count_type);
|
||||
} else {
|
||||
pv_describe_amount(content, sizeof(content), "%s",
|
||||
(long double) (state->transfer.transferred), "", _("B"), display->count_type);
|
||||
}
|
||||
/*@+mustfreefresh@ *//* splint - false positive from gettext(). */
|
||||
|
||||
return pv_formatter_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Formatter function for ETA display.
|
||||
*
|
||||
* Copyright 2024 Andrew Wood
|
||||
*
|
||||
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "pv.h"
|
||||
#include "pv-internal.h"
|
||||
|
||||
|
||||
/*
|
||||
* Estimated time until completion.
|
||||
*/
|
||||
size_t pv_formatter_eta(pvstate_t state, pvdisplay_t display, pvdisplay_segment_t segment, char *buffer,
|
||||
size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[128]; /* flawfinder: ignore - always bounded */
|
||||
long eta;
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
/*
|
||||
* Don't try to calculate this if the size is not known.
|
||||
*/
|
||||
if (state->control.size < 1)
|
||||
return 0;
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
eta =
|
||||
pv_seconds_remaining((state->transfer.transferred - display->initial_offset),
|
||||
state->control.size - display->initial_offset, state->calc.current_avg_rate);
|
||||
|
||||
/*
|
||||
* Bounds check, so we don't overrun the suffix buffer. This means
|
||||
* the ETA will always be less than 100,000 hours.
|
||||
*/
|
||||
eta = pv_bound_long(eta, 0, (long) 360000000L);
|
||||
|
||||
/*
|
||||
* If the ETA is more than a day, include a day count as well as
|
||||
* hours, minutes, and seconds.
|
||||
*/
|
||||
/*@-mustfreefresh@ */
|
||||
if (eta > 86400L) {
|
||||
(void) pv_snprintf(content,
|
||||
sizeof(content),
|
||||
"%.16s %ld:%02ld:%02ld:%02ld",
|
||||
_("ETA"), eta / 86400, (eta / 3600) % 24, (eta / 60) % 60, eta % 60);
|
||||
} else {
|
||||
(void) pv_snprintf(content,
|
||||
sizeof(content),
|
||||
"%.16s %ld:%02ld:%02ld", _("ETA"), eta / 3600, (eta / 60) % 60, eta % 60);
|
||||
}
|
||||
/*@+mustfreefresh@ *//* splint: see above. */
|
||||
|
||||
/*
|
||||
* If this is the final update, show a blank space where the ETA
|
||||
* used to be.
|
||||
*/
|
||||
if (display->final_update) {
|
||||
size_t erase_idx;
|
||||
for (erase_idx = 0; erase_idx < sizeof(content) && content[erase_idx] != '\0'; erase_idx++) {
|
||||
content[erase_idx] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return pv_formatter_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Formatter function for the local time at which the transfer is expected
|
||||
* to complete.
|
||||
*
|
||||
* Copyright 2024 Andrew Wood
|
||||
*
|
||||
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "pv.h"
|
||||
#include "pv-internal.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
/*
|
||||
* Estimated local time of completion.
|
||||
*/
|
||||
size_t pv_formatter_fineta(pvstate_t state, pvdisplay_t display, pvdisplay_segment_t segment, char *buffer,
|
||||
size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[128]; /* flawfinder: ignore - always bounded */
|
||||
time_t now, then;
|
||||
struct tm *time_ptr;
|
||||
long eta;
|
||||
const char *time_format;
|
||||
bool show_fineta;
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
/*
|
||||
* Don't try to calculate this if the size is not known.
|
||||
*/
|
||||
if (state->control.size < 1)
|
||||
return 0;
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
now = time(NULL);
|
||||
show_fineta = true;
|
||||
time_format = NULL;
|
||||
|
||||
/*
|
||||
* The completion clock time may be hidden by a failed localtime
|
||||
* lookup.
|
||||
*/
|
||||
|
||||
eta = pv_seconds_remaining(state->transfer.transferred - display->initial_offset,
|
||||
state->control.size - display->initial_offset, state->calc.current_avg_rate);
|
||||
|
||||
/* Bounds check - see pv_formatter_eta(). */
|
||||
eta = pv_bound_long(eta, 0, (long) 360000000L);
|
||||
|
||||
/*
|
||||
* Only include the date if the ETA is more than 6 hours
|
||||
* away.
|
||||
*/
|
||||
if (eta > (long) (6 * 3600)) {
|
||||
time_format = "%Y-%m-%d %H:%M:%S";
|
||||
} else {
|
||||
time_format = "%H:%M:%S";
|
||||
}
|
||||
|
||||
then = now + eta;
|
||||
time_ptr = localtime(&then);
|
||||
|
||||
if (NULL == time_ptr) {
|
||||
show_fineta = false;
|
||||
} else {
|
||||
/*
|
||||
* The localtime() function keeps data stored in a static
|
||||
* buffer that gets overwritten by time functions.
|
||||
*/
|
||||
struct tm time = *time_ptr;
|
||||
size_t content_bytes;
|
||||
|
||||
/*@-mustfreefresh@ */
|
||||
(void) pv_snprintf(content, sizeof(content), "%.16s ", _("FIN"));
|
||||
/*@+mustfreefresh@ *//* splint: see above. */
|
||||
content_bytes = strlen(content); /* flawfinder: ignore */
|
||||
/* flawfinder: always bounded with \0 by pv_snprintf(). */
|
||||
(void) strftime(content + content_bytes, sizeof(content) - 1 - content_bytes, time_format, &time);
|
||||
}
|
||||
|
||||
if (!show_fineta) {
|
||||
size_t erase_idx;
|
||||
for (erase_idx = 0; erase_idx < sizeof(content) && content[erase_idx] != '\0'; erase_idx++) {
|
||||
content[erase_idx] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return pv_formatter_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Formatter function for showing the last bytes written.
|
||||
*
|
||||
* Copyright 2024 Andrew Wood
|
||||
*
|
||||
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "pv.h"
|
||||
#include "pv-internal.h"
|
||||
|
||||
|
||||
/*
|
||||
* Display the last few bytes written.
|
||||
*
|
||||
* As a side effect, this sets display->lastwritten_bytes to the segment's
|
||||
* chosen_size, if it was previously smaller than that.
|
||||
*/
|
||||
size_t pv_formatter_last_written( /*@unused@ */
|
||||
__attribute__((unused)) pvstate_t state, pvdisplay_t display,
|
||||
pvdisplay_segment_t segment, char *buffer, size_t buffer_size,
|
||||
size_t offset)
|
||||
{
|
||||
size_t bytes_to_show, read_offset, remaining;
|
||||
|
||||
display->showing_last_written = true;
|
||||
|
||||
bytes_to_show = segment->chosen_size;
|
||||
if (0 == bytes_to_show)
|
||||
bytes_to_show = segment->width;
|
||||
if (0 == bytes_to_show)
|
||||
return 0;
|
||||
|
||||
if (bytes_to_show > PV_SIZEOF_LASTWRITTEN_BUFFER)
|
||||
bytes_to_show = PV_SIZEOF_LASTWRITTEN_BUFFER;
|
||||
if (bytes_to_show > display->lastwritten_bytes)
|
||||
display->lastwritten_bytes = bytes_to_show;
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
if (offset + bytes_to_show >= buffer_size)
|
||||
return 0;
|
||||
|
||||
segment->offset = offset;
|
||||
segment->bytes = bytes_to_show;
|
||||
|
||||
read_offset = display->lastwritten_bytes - bytes_to_show;
|
||||
for (remaining = bytes_to_show; remaining > 0; remaining--) {
|
||||
char display_char = display->lastwritten_buffer[read_offset++];
|
||||
buffer[offset++] = pv_isprint(display_char) ? display_char : '.';
|
||||
}
|
||||
|
||||
return bytes_to_show;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Formatter function for showing the name of the transfer.
|
||||
*
|
||||
* Copyright 2024 Andrew Wood
|
||||
*
|
||||
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "pv.h"
|
||||
#include "pv-internal.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/*
|
||||
* Display the transfer's name.
|
||||
*/
|
||||
size_t pv_formatter_name(pvstate_t state, /*@unused@ */ __attribute__((unused)) pvdisplay_t display,
|
||||
pvdisplay_segment_t segment, char *buffer, size_t buffer_size, size_t offset)
|
||||
{
|
||||
char string_format[32]; /* flawfinder: ignore - always bounded */
|
||||
char content[512]; /* flawfinder: ignore - always bounded */
|
||||
size_t field_width;
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
field_width = segment->chosen_size;
|
||||
if (field_width < 1)
|
||||
field_width = 9;
|
||||
if (field_width > 500)
|
||||
field_width = 500;
|
||||
|
||||
memset(string_format, 0, sizeof(string_format));
|
||||
(void) pv_snprintf(string_format, sizeof(string_format), "%%%d.500s:", field_width);
|
||||
|
||||
content[0] = '\0';
|
||||
if (state->control.name) {
|
||||
(void) pv_snprintf(content, sizeof(content), string_format, state->control.name);
|
||||
}
|
||||
|
||||
return pv_formatter_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Formatter function for the most recently written line.
|
||||
*
|
||||
* Copyright 2024 Andrew Wood
|
||||
*
|
||||
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "pv.h"
|
||||
#include "pv-internal.h"
|
||||
|
||||
|
||||
/*
|
||||
* Display the previously written line.
|
||||
*/
|
||||
size_t pv_formatter_previous_line( /*@unused@ */
|
||||
__attribute__((unused)) pvstate_t state, pvdisplay_t display,
|
||||
pvdisplay_segment_t segment, char *buffer, size_t buffer_size,
|
||||
size_t offset)
|
||||
{
|
||||
size_t bytes_to_show, read_offset, remaining;
|
||||
|
||||
display->showing_previous_line = true;
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
bytes_to_show = segment->chosen_size;
|
||||
if (0 == bytes_to_show)
|
||||
bytes_to_show = segment->width;
|
||||
if (0 == bytes_to_show)
|
||||
return 0;
|
||||
|
||||
if (bytes_to_show > PV_SIZEOF_PREVLINE_BUFFER)
|
||||
bytes_to_show = PV_SIZEOF_PREVLINE_BUFFER;
|
||||
|
||||
if (offset + bytes_to_show >= buffer_size)
|
||||
return 0;
|
||||
|
||||
segment->offset = offset;
|
||||
segment->bytes = bytes_to_show;
|
||||
|
||||
read_offset = 0;
|
||||
for (remaining = bytes_to_show; remaining > 0; remaining--) {
|
||||
char display_char = display->previous_line[read_offset++];
|
||||
buffer[offset++] = pv_isprint(display_char) ? display_char : ' ';
|
||||
}
|
||||
|
||||
return bytes_to_show;
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Formatter functions for progress bars.
|
||||
*
|
||||
* Copyright 2024 Andrew Wood
|
||||
*
|
||||
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "pv.h"
|
||||
#include "pv-internal.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/*
|
||||
* Write a progress bar to a buffer, in known-size or rate-gauge mode - a
|
||||
* bar, and a percentage (size) or max rate (gauge). The total width of the
|
||||
* content is bounded to the given width. Returns the number of bytes
|
||||
* written to the buffer.
|
||||
*
|
||||
* If "bar_sides" is false, only the bar itself is rendered, not the opening
|
||||
* and closing characters.
|
||||
*
|
||||
* If "include_bar" is false, the bar is omitted entirely.
|
||||
*
|
||||
* If "include_amount" is false, the percentage or rate after the bar is
|
||||
* omitted.
|
||||
*
|
||||
* This is only called by pv_formatter_progress().
|
||||
*/
|
||||
static size_t pv_formatter_progress_knownsize(pvstate_t state, pvdisplay_t display, char *buffer, size_t buffer_size,
|
||||
size_t width, bool bar_sides, bool include_bar, bool include_amount)
|
||||
{
|
||||
char after_bar[32]; /* flawfinder: ignore - only populated by pv_snprintf(). */
|
||||
size_t after_bar_bytes, after_bar_width;
|
||||
size_t bar_area_width, filled_bar_width, buffer_offset, pad_count;
|
||||
int bar_percentage;
|
||||
|
||||
buffer[0] = '\0';
|
||||
|
||||
memset(after_bar, 0, sizeof(after_bar));
|
||||
|
||||
if (state->control.size > 0) {
|
||||
/* Percentage of data transferred. */
|
||||
bar_percentage = state->calc.percentage;
|
||||
(void) pv_snprintf(after_bar, sizeof(after_bar), " %3ld%%", bar_percentage);
|
||||
} else {
|
||||
/* Current rate vs max rate. */
|
||||
bar_percentage = 0;
|
||||
if (state->calc.rate_max > 0) {
|
||||
bar_percentage = (int) (100.0 * state->calc.transfer_rate / state->calc.rate_max);
|
||||
}
|
||||
|
||||
/*@-mustfreefresh@ */
|
||||
if (state->control.bits && !state->control.linemode) {
|
||||
/* bits per second */
|
||||
pv_describe_amount(after_bar, sizeof(after_bar), "/%s",
|
||||
8.0 * state->calc.rate_max, "", _("b/s"), display->count_type);
|
||||
} else {
|
||||
/* bytes or lines per second */
|
||||
pv_describe_amount(after_bar, sizeof(after_bar),
|
||||
"/%s", state->calc.rate_max, _("/s"), _("B/s"), display->count_type);
|
||||
}
|
||||
/*@+mustfreefresh@ *//* splint: see above about gettext(). */
|
||||
}
|
||||
|
||||
if (!include_amount)
|
||||
after_bar[0] = '\0';
|
||||
|
||||
after_bar_bytes = strlen(after_bar); /* flawfinder: ignore */
|
||||
/* flawfinder: always \0-terminated by pv_snprintf() and the earlier memset(). */
|
||||
after_bar_width = pv_strwidth(after_bar, after_bar_bytes);
|
||||
|
||||
if (!include_bar) {
|
||||
if (buffer_size < after_bar_bytes)
|
||||
return 0;
|
||||
if (after_bar_bytes > 1) {
|
||||
/* NB we skip the leading space. */
|
||||
memmove(buffer, after_bar + 1, after_bar_bytes - 1);
|
||||
buffer[after_bar_bytes - 1] = '\0';
|
||||
return after_bar_bytes - 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bar_sides) {
|
||||
if (width < (after_bar_width + 2))
|
||||
return 0;
|
||||
bar_area_width = width - after_bar_width - 2;
|
||||
} else {
|
||||
if (width < after_bar_width)
|
||||
return 0;
|
||||
bar_area_width = width - after_bar_width;
|
||||
}
|
||||
|
||||
if (bar_area_width > buffer_size - 16)
|
||||
bar_area_width = buffer_size - 16;
|
||||
|
||||
filled_bar_width = (size_t) (bar_area_width * bar_percentage) / 100;
|
||||
/* Leave room for the tip of the bar. */
|
||||
if (filled_bar_width > 0)
|
||||
filled_bar_width--;
|
||||
|
||||
debug("width=%d bar_area_width=%d filled_bar_width=%d", width, bar_area_width, filled_bar_width);
|
||||
|
||||
buffer_offset = 0;
|
||||
|
||||
if (bar_sides) {
|
||||
/* The opening of the bar area. */
|
||||
buffer[buffer_offset++] = '[';
|
||||
}
|
||||
|
||||
/* The bar portion. */
|
||||
for (pad_count = 0; pad_count < filled_bar_width && buffer_offset < buffer_size - 1; pad_count++) {
|
||||
if (pad_count < bar_area_width)
|
||||
buffer[buffer_offset++] = '=';
|
||||
}
|
||||
|
||||
/* The tip of the bar, if not at 100%. */
|
||||
if (pad_count < bar_area_width) {
|
||||
if (buffer_offset < buffer_size - 1)
|
||||
buffer[buffer_offset++] = '>';
|
||||
pad_count++;
|
||||
}
|
||||
|
||||
/* The spaces after the bar. */
|
||||
for (; pad_count < bar_area_width; pad_count++) {
|
||||
if (buffer_offset < buffer_size - 1)
|
||||
buffer[buffer_offset++] = ' ';
|
||||
}
|
||||
|
||||
if (bar_sides) {
|
||||
/* The closure of the bar area. */
|
||||
if (buffer_offset < buffer_size - 1)
|
||||
buffer[buffer_offset++] = ']';
|
||||
}
|
||||
|
||||
/* The percentage. */
|
||||
if (after_bar_bytes > 0 && after_bar_bytes < (buffer_size - 1 - buffer_offset)) {
|
||||
memmove(buffer + buffer_offset, after_bar, after_bar_bytes);
|
||||
buffer_offset += after_bar_bytes;
|
||||
}
|
||||
|
||||
buffer[buffer_offset] = '\0';
|
||||
|
||||
return buffer_offset;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write a progress bar to a buffer, in unknown-size mode - just a moving
|
||||
* indicator. The total width of the content is bounded to the given width.
|
||||
* Returns the number of bytes written to the buffer.
|
||||
*
|
||||
* If "bar_sides" is false, only the bar itself is rendered, not the opening
|
||||
* and closing characters.
|
||||
*
|
||||
* This is only called by pv_formatter_progress().
|
||||
*/
|
||||
static size_t pv_formatter_progress_unknownsize(pvstate_t state, /*@unused@ */
|
||||
__attribute__((unused)) pvdisplay_t display, char *buffer,
|
||||
size_t buffer_size, size_t width, bool bar_sides)
|
||||
{
|
||||
size_t bar_area_width, buffer_offset, pad_count;
|
||||
size_t indicator_position;
|
||||
|
||||
buffer[0] = '\0';
|
||||
|
||||
if (bar_sides) {
|
||||
if (width < 6)
|
||||
return 0;
|
||||
bar_area_width = width - 5;
|
||||
} else {
|
||||
if (width < 5)
|
||||
return 0;
|
||||
bar_area_width = width - 3;
|
||||
}
|
||||
|
||||
if (bar_area_width > buffer_size - 16)
|
||||
bar_area_width = buffer_size - 16;
|
||||
|
||||
/*
|
||||
* Note that pv_calculate_transfer_rate() sets the percentage when
|
||||
* the size is unknown to a value that goes 0 - 200 and resets, so
|
||||
* here we make values above 100 send the indicator back down again,
|
||||
* so it moves back and forth.
|
||||
*/
|
||||
indicator_position = (size_t) (state->calc.percentage);
|
||||
if (indicator_position > 200)
|
||||
indicator_position = indicator_position % 200;
|
||||
if (indicator_position > 100 && indicator_position <= 200)
|
||||
indicator_position = 200 - indicator_position;
|
||||
|
||||
buffer_offset = 0;
|
||||
|
||||
if (bar_sides) {
|
||||
/* The opening of the bar area. */
|
||||
buffer[buffer_offset++] = '[';
|
||||
}
|
||||
|
||||
/* The spaces before the indicator. */
|
||||
for (pad_count = 0; pad_count < (bar_area_width * indicator_position) / 100; pad_count++) {
|
||||
if (pad_count < bar_area_width && buffer_offset < buffer_size - 1)
|
||||
buffer[buffer_offset++] = ' ';
|
||||
}
|
||||
|
||||
/* The indicator. */
|
||||
if (buffer_offset < buffer_size - 4) {
|
||||
buffer[buffer_offset++] = '<';
|
||||
buffer[buffer_offset++] = '=';
|
||||
buffer[buffer_offset++] = '>';
|
||||
}
|
||||
|
||||
/* The spaces after the indicator. */
|
||||
for (; pad_count < bar_area_width; pad_count++) {
|
||||
if (pad_count < bar_area_width && buffer_offset < buffer_size - 1)
|
||||
buffer[buffer_offset++] = ' ';
|
||||
}
|
||||
|
||||
if (bar_sides) {
|
||||
/* The closure of the bar area. */
|
||||
if (buffer_offset < buffer_size - 1)
|
||||
buffer[buffer_offset++] = ']';
|
||||
}
|
||||
|
||||
buffer[buffer_offset] = '\0';
|
||||
|
||||
return buffer_offset;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Progress bar.
|
||||
*/
|
||||
size_t pv_formatter_progress(pvstate_t state, pvdisplay_t display, pvdisplay_segment_t segment, char *buffer,
|
||||
size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[1024]; /* flawfinder: ignore - always bounded */
|
||||
size_t bytes;
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
if (state->control.size > 0 || state->control.rate_gauge) {
|
||||
/* Known size or rate gauge - bar with percentage. */
|
||||
bytes =
|
||||
pv_formatter_progress_knownsize(state, display, content, sizeof(content), segment->width, true, true,
|
||||
true);
|
||||
} else {
|
||||
/* Unknown size - back-and-forth moving indicator. */
|
||||
bytes = pv_formatter_progress_unknownsize(state, display, content, sizeof(content), segment->width, true);
|
||||
}
|
||||
|
||||
content[bytes] = '\0';
|
||||
|
||||
return pv_formatter_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Progress bar, without sides and without a number afterwards.
|
||||
*/
|
||||
size_t pv_formatter_progress_bar_only(pvstate_t state, pvdisplay_t display, pvdisplay_segment_t segment,
|
||||
char *buffer, size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[1024]; /* flawfinder: ignore - always bounded */
|
||||
size_t bytes;
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
if (state->control.size > 0 || state->control.rate_gauge) {
|
||||
/* Known size or rate gauge - bar with percentage. */
|
||||
bytes =
|
||||
pv_formatter_progress_knownsize(state, display, content, sizeof(content), segment->width, false, true,
|
||||
false);
|
||||
} else {
|
||||
/* Unknown size - back-and-forth moving indicator. */
|
||||
bytes =
|
||||
pv_formatter_progress_unknownsize(state, display, content, sizeof(content), segment->width, false);
|
||||
}
|
||||
|
||||
content[bytes] = '\0';
|
||||
|
||||
return pv_formatter_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The number after the progress bar.
|
||||
*/
|
||||
size_t pv_formatter_progress_amount_only(pvstate_t state, pvdisplay_t display, pvdisplay_segment_t segment,
|
||||
char *buffer, size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[256]; /* flawfinder: ignore - always bounded */
|
||||
size_t bytes;
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
if (state->control.size > 0 || state->control.rate_gauge) {
|
||||
/* Known size or rate gauge - percentage or rate. */
|
||||
bytes =
|
||||
pv_formatter_progress_knownsize(state, display, content, sizeof(content), segment->width, false,
|
||||
false, true);
|
||||
} else {
|
||||
/* Unknown size - no number. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
content[bytes] = '\0';
|
||||
|
||||
return pv_formatter_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Formatter function for the current rate of transfer.
|
||||
*
|
||||
* Copyright 2024 Andrew Wood
|
||||
*
|
||||
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "pv.h"
|
||||
#include "pv-internal.h"
|
||||
|
||||
|
||||
/*
|
||||
* Transfer rate.
|
||||
*/
|
||||
size_t pv_formatter_rate(pvstate_t state, pvdisplay_t display, pvdisplay_segment_t segment, char *buffer,
|
||||
size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[128]; /* flawfinder: ignore - always bounded */
|
||||
|
||||
display->showing_rate = true;
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
/*@-mustfreefresh@ */
|
||||
if (state->control.bits && !state->control.linemode) {
|
||||
/* bits per second */
|
||||
pv_describe_amount(content, sizeof(content), "[%s]",
|
||||
8 * state->calc.transfer_rate, "", _("b/s"), display->count_type);
|
||||
} else {
|
||||
/* bytes or lines per second */
|
||||
pv_describe_amount(content, sizeof(content),
|
||||
"[%s]", state->calc.transfer_rate, _("/s"), _("B/s"), display->count_type);
|
||||
}
|
||||
/*@+mustfreefresh@ *//* splint: see above. */
|
||||
|
||||
return pv_formatter_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Formatter function for the elapsed transfer time.
|
||||
*
|
||||
* Copyright 2024 Andrew Wood
|
||||
*
|
||||
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "pv.h"
|
||||
#include "pv-internal.h"
|
||||
|
||||
|
||||
/*
|
||||
* Elapsed time.
|
||||
*/
|
||||
size_t pv_formatter_timer(pvstate_t state, pvdisplay_t display, pvdisplay_segment_t segment, char *buffer,
|
||||
size_t buffer_size, size_t offset)
|
||||
{
|
||||
char content[128]; /* flawfinder: ignore - always bounded */
|
||||
|
||||
display->showing_timer = true;
|
||||
|
||||
content[0] = '\0';
|
||||
|
||||
if (0 == buffer_size)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Bounds check, so we don't overrun the prefix buffer. This does
|
||||
* mean that the timer will stop at a 100,000 hours, but since
|
||||
* that's 11 years, it shouldn't be a problem.
|
||||
*/
|
||||
if (state->transfer.elapsed_seconds > (long double) 360000000.0L)
|
||||
state->transfer.elapsed_seconds = (long double) 360000000.0L;
|
||||
|
||||
/*
|
||||
* If the elapsed time is more than a day, include a day count as
|
||||
* well as hours, minutes, and seconds.
|
||||
*/
|
||||
if (state->transfer.elapsed_seconds > (long double) 86400.0L) {
|
||||
(void) pv_snprintf(content,
|
||||
sizeof(content),
|
||||
"%ld:%02ld:%02ld:%02ld",
|
||||
((long) (state->transfer.elapsed_seconds)) / 86400,
|
||||
(((long) (state->transfer.elapsed_seconds)) / 3600) %
|
||||
24, (((long) (state->transfer.elapsed_seconds)) / 60) % 60,
|
||||
((long) (state->transfer.elapsed_seconds)) % 60);
|
||||
} else {
|
||||
(void) pv_snprintf(content,
|
||||
sizeof(content),
|
||||
"%ld:%02ld:%02ld",
|
||||
((long) (state->transfer.elapsed_seconds)) / 3600,
|
||||
(((long) (state->transfer.elapsed_seconds)) / 60) % 60,
|
||||
((long) (state->transfer.elapsed_seconds)) % 60);
|
||||
}
|
||||
|
||||
return pv_formatter_segmentcontent(content, segment, buffer, buffer_size, offset);
|
||||
}
|
||||
+10
-10
@@ -17,7 +17,7 @@
|
||||
* including <ctype.h> causes weird versioned glibc dependencies on certain
|
||||
* Red Hat systems, complicating package management.
|
||||
*/
|
||||
static bool pv__isdigit(char c)
|
||||
bool pv_isdigit(char c)
|
||||
{
|
||||
return ((c >= '0') && (c <= '9')) ? true : false;
|
||||
}
|
||||
@@ -42,14 +42,14 @@ off_t pv_getnum_size(const char *str, bool decimal_units)
|
||||
return (off_t) 0;
|
||||
|
||||
/* Skip any non-numeric leading characters. */
|
||||
while (str[0] != '\0' && (!pv__isdigit(str[0])))
|
||||
while (str[0] != '\0' && (!pv_isdigit(str[0])))
|
||||
str++;
|
||||
|
||||
/*
|
||||
* Parse the integral part of the number - the digits before the
|
||||
* decimal mark or units.
|
||||
*/
|
||||
for (; pv__isdigit(str[0]); str++) {
|
||||
for (; pv_isdigit(str[0]); str++) {
|
||||
integral_part = integral_part * 10;
|
||||
integral_part += (off_t) (str[0] - '0');
|
||||
}
|
||||
@@ -64,7 +64,7 @@ off_t pv_getnum_size(const char *str, bool decimal_units)
|
||||
*/
|
||||
if (('.' == str[0]) || (',' == str[0])) {
|
||||
str++;
|
||||
for (; pv__isdigit(str[0]); str++) {
|
||||
for (; pv_isdigit(str[0]); str++) {
|
||||
/* Stop counting below 0.0001. */
|
||||
if (fractional_divisor < 10000) {
|
||||
fractional_part = fractional_part * 10;
|
||||
@@ -176,11 +176,11 @@ double pv_getnum_interval(const char *str)
|
||||
return 0.0;
|
||||
|
||||
/* Skip any non-digit characters at the start. */
|
||||
while (str[0] != '\0' && (!pv__isdigit(str[0])))
|
||||
while (str[0] != '\0' && (!pv_isdigit(str[0])))
|
||||
str++;
|
||||
|
||||
/* Parse the digits before the decimal mark. */
|
||||
for (; pv__isdigit(str[0]); str++) {
|
||||
for (; pv_isdigit(str[0]); str++) {
|
||||
result = result * 10;
|
||||
result += (double) (str[0] - '0');
|
||||
}
|
||||
@@ -193,7 +193,7 @@ double pv_getnum_interval(const char *str)
|
||||
str++;
|
||||
|
||||
/* Parse the digits after the decimal mark, up to 0.0000001. */
|
||||
for (; pv__isdigit(str[0]) && step < 1000000; str++) {
|
||||
for (; pv_isdigit(str[0]) && step < 1000000; str++) {
|
||||
step = step * 10;
|
||||
result += ((double) (str[0] - '0')) / step;
|
||||
}
|
||||
@@ -226,11 +226,11 @@ bool pv_getnum_check(const char *str, pv_numtype type)
|
||||
str++;
|
||||
|
||||
/* If the next character isn't a digit, this isn't a number. */
|
||||
if (!pv__isdigit(str[0]))
|
||||
if (!pv_isdigit(str[0]))
|
||||
return false;
|
||||
|
||||
/* Skip over the digits. */
|
||||
for (; pv__isdigit(str[0]); str++);
|
||||
for (; pv_isdigit(str[0]); str++);
|
||||
|
||||
/*
|
||||
* If there's a decimal mark (see note in pv_getnum_size() above),
|
||||
@@ -242,7 +242,7 @@ bool pv_getnum_check(const char *str, pv_numtype type)
|
||||
return false;
|
||||
/* Skip the decimal mark, then all digits. */
|
||||
str++;
|
||||
for (; pv__isdigit(str[0]); str++);
|
||||
for (; pv_isdigit(str[0]); str++);
|
||||
}
|
||||
|
||||
/* If the string ends here, this is a valid number. */
|
||||
|
||||
@@ -304,3 +304,14 @@ size_t pv_strwidth(const char *string, size_t bytes)
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return true if the character is printable. This function is used instead
|
||||
* of the macro from <ctype.h> to avoid causing versioned glibc dependencies
|
||||
* on some systems.
|
||||
*/
|
||||
bool pv_isprint(char c)
|
||||
{
|
||||
return ((c >= (char) 32) && (c <= (char) 126)) ? true : false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user