Replace str_* in state structure with an array of components of enumerated types, to avoid pointers everywhere in the format array and to allow for later expansion which will separate "string length" from "displayed width" to allow for wide character support.

This commit is contained in:
Andrew Wood
2023-10-14 17:18:20 +01:00
parent 94ffdaf60d
commit b0df2e53a8
3 changed files with 424 additions and 358 deletions
+44 -48
View File
@@ -20,16 +20,25 @@
extern "C" {
#endif
#define PV_DISPLAY_PROGRESS 1
#define PV_DISPLAY_TIMER 2
#define PV_DISPLAY_ETA 4
#define PV_DISPLAY_RATE 8
#define PV_DISPLAY_AVERAGERATE 16
#define PV_DISPLAY_BYTES 32
#define PV_DISPLAY_NAME 64
#define PV_DISPLAY_BUFPERCENT 128
#define PV_DISPLAY_OUTPUTBUF 256
#define PV_DISPLAY_FINETA 512
/*
* Types of display component that make up an output string.
*/
typedef enum {
PV_COMPONENT_STRING, /* fixed string */
PV_COMPONENT_PROGRESS, /* progress bar, with percentage if known */
PV_COMPONENT_BYTES, /* number of bytes transferred */
PV_COMPONENT_TIMER, /* elapsed time */
PV_COMPONENT_RATE, /* current transfer rate */
PV_COMPONENT_AVERAGERATE, /* average transfer rate */
PV_COMPONENT_ETA, /* estimated time remaining until completion */
PV_COMPONENT_FINETA, /* estimated time of completion */
PV_COMPONENT_NAME, /* name prefix */
PV_COMPONENT_BUFPERCENT, /* percentage of buffer used */
PV_COMPONENT_OUTPUTBUF, /* recent bytes in output buffer */
PV_COMPONENT__MAX
} pv_display_component;
#define PV_SIZEOF_COMPONENT_STR 1024 /* size of buffer for each component */
#define RATE_GRANULARITY 100000000 /* nsec between -L rate chunks */
#define RATE_BURST_WINDOW 5 /* rate burst window (multiples of rate) */
@@ -43,25 +52,9 @@ extern "C" {
#define MAXIMISE_BUFFER_FILL 1
typedef struct pvhistory {
off_t total_bytes;
long double elapsed_sec;
} pvhistory_t;
#define PV_SIZEOF_DEFAULT_FORMAT 512
#define PV_SIZEOF_CWD 4096
#define PV_SIZEOF_LASTOUTPUT_BUFFER 256
#define PV_SIZEOF_STR_NAME 512
#define PV_SIZEOF_STR_TRANSFERRED 128
#define PV_SIZEOF_STR_BUFPERCENT 128
#define PV_SIZEOF_STR_TIMER 128
#define PV_SIZEOF_STR_RATE 128
#define PV_SIZEOF_STR_AVERAGE_RATE 128
#define PV_SIZEOF_STR_PROGRESS 1024
#define PV_SIZEOF_STR_LASTOUTPUT 512
#define PV_SIZEOF_STR_ETA 128
#define PV_SIZEOF_STR_FINETA 128
#define PV_FORMAT_ARRAY_MAX 100
#define PV_SIZEOF_CRS_LOCK_FILE 1024
@@ -182,36 +175,39 @@ struct pvstate_s {
long double prev_trans;
/* Keep track of progress over last intervals to compute current average rate. */
/*@null@*/ pvhistory_t *history; /* state at previous intervals (circular buffer) */
unsigned int history_len; /* total size */
/*@null@*/ struct { /* state at previous intervals (circular buffer) */
long long total_bytes;
long double elapsed_sec;
} *history;
size_t history_len; /* total size of history array */
int history_interval; /* seconds between each history entry */
int history_first;
int history_last;
size_t history_first;
size_t history_last;
long double current_avg_rate; /* current average rate over last history intervals */
off_t initial_offset;
/*@only@*/ char *display_buffer;
off_t initial_offset; /* offset when first opened (when watching fds) */
/*@null@*/ /*@only@*/ char *display_buffer;
long display_buffer_size;
size_t lastoutput_length; /* number of last-output bytes to show */
char lastoutput_buffer[PV_SIZEOF_LASTOUTPUT_BUFFER];
int prev_width; /* screen width last time we were called */
int prev_length; /* length of last string we output */
char str_name[PV_SIZEOF_STR_NAME];
char str_transferred[PV_SIZEOF_STR_TRANSFERRED];
char str_bufpercent[PV_SIZEOF_STR_BUFPERCENT];
char str_timer[PV_SIZEOF_STR_TIMER];
char str_rate[PV_SIZEOF_STR_RATE];
char str_average_rate[PV_SIZEOF_STR_AVERAGE_RATE];
char str_progress[PV_SIZEOF_STR_PROGRESS];
char str_lastoutput[PV_SIZEOF_STR_LASTOUTPUT];
char str_eta[PV_SIZEOF_STR_ETA];
char str_fineta[PV_SIZEOF_STR_FINETA];
unsigned long components_used; /* bitmask of components used */
struct {
const char *string;
int length;
} format[PV_FORMAT_ARRAY_MAX];
bool display_visible; /* set once anything written to terminal */
size_t format_segment_count; /* number of format string segments */
struct { /* format string broken into display components */
pv_display_component type; /* type of display component */
size_t str_start; /* for strings: start offset */
size_t str_length; /* for strings: length */
} format[PV_FORMAT_ARRAY_MAX];
struct { /* display components */
bool required; /* true if included in format */
char content[PV_SIZEOF_COMPONENT_STR]; /* string to display */
size_t length; /* number of bytes in string */
} component[PV_COMPONENT__MAX];
} display;
/********************
+379 -308
View File
@@ -184,7 +184,13 @@ static void pv__si_prefix(long double *value, char *prefix, const long double ra
* Prefix list for multiples of 1000.
*/
if (NULL == pfx_000) {
/*@-onlytrans@ */
pfx_000 = _("yzafpnum kMGTPEZY");
/*
* splint: this is only looked up once in the program's run,
* so the memory leak is negligible.
*/
/*@+onlytrans@ */
if (NULL == pfx_000) {
debug("%s", "prefix list was NULL");
return;
@@ -196,7 +202,9 @@ static void pv__si_prefix(long double *value, char *prefix, const long double ra
* Prefix list for multiples of 1024.
*/
if (NULL == pfx_024) {
/*@-onlytrans@ */
pfx_024 = _("yzafpnum KMGTPEZY");
/*@+onlytrans@ *//* splint: see above. */
if (NULL == pfx_024) {
debug("%s", "prefix list was NULL");
return;
@@ -367,53 +375,52 @@ static void pv__sizestr(char *buffer, size_t bufsize, char *format,
static void pv__format_init(pvstate_t state)
{
const char *formatstr;
const char *searchptr;
int strpos;
int segment;
size_t strpos;
size_t segment;
if (NULL == state)
return;
state->display.str_name[0] = '\0';
state->display.str_transferred[0] = '\0';
state->display.str_timer[0] = '\0';
state->display.str_rate[0] = '\0';
state->display.str_average_rate[0] = '\0';
state->display.str_progress[0] = '\0';
state->display.str_eta[0] = '\0';
state->display.format_segment_count = 0;
memset(state->display.format, 0, PV_FORMAT_ARRAY_MAX * sizeof(state->display.format[0]));
memset(state->display.component, 0, PV_COMPONENT__MAX * sizeof(state->display.component[0]));
if (state->control.name) {
(void) pv_snprintf(state->display.str_name, PV_SIZEOF_STR_NAME, "%9.500s:", state->control.name);
(void) pv_snprintf(state->display.component[PV_COMPONENT_NAME].content, PV_SIZEOF_COMPONENT_STR,
"%9.500s:", state->control.name);
state->display.component[PV_COMPONENT_NAME].length =
strlen(state->display.component[PV_COMPONENT_NAME].content);
}
formatstr = state->control.format_string ? state->control.format_string : state->control.default_format;
state->display.components_used = 0;
if (NULL == formatstr)
return;
/*
* Split the format string into segments. Each segment consists
* of a string pointer and a length.
* of a type and some string information.
*
* A length of zero indicates that the segment is a fixed-size
* component updated by pv__format(), so it is a pointer to one
* of the state->str_* buffers that pv__format() updates.
* A type of PV_COMPONENT_STRING indicates that the segment is a
* constant string starting at a position in the format string and
* with a particular length.
*
* A length below zero indicates that the segment is a variable
* sized component which will be recalculated by pv__format()
* after the length of all fixed-size segments is known, and so
* the string is a pointer to another state->str_* buffer
* (currently it will only ever be state->display.str_progress).
* A type other than PV_COMPONENT_STRING indicates that the segment
* is a string updated by pv__format(), whose contents will be in
* component[type].
*
* A length above zero indicates that the segment is a constant
* string of the given length (not necessarily null terminated).
* In pv__format(), the content of a PV_COMPONENT_PROGRESS component
* is calculated after first populating all the other components
* referenced by the format segments.
*
* In pv__format(), after the state->str_* buffers have all been
* filled in, the output string is generated by sticking all of
* Then, pv_format() generates the output string by sticking all of
* these segments together.
*/
segment = 0;
for (strpos = 0; formatstr[strpos] != '\0' && segment < 99; strpos++, segment++) {
for (strpos = 0; formatstr[strpos] != '\0' && segment < PV_FORMAT_ARRAY_MAX; strpos++, segment++) {
pv_display_component seg_type;
size_t str_start, str_length;
if ('%' == formatstr[strpos]) {
unsigned long number_prefix;
#if HAVE_STRTOUL
@@ -441,95 +448,91 @@ static void pv__format_init(pvstate_t state)
}
#endif /* !HAVE_STRTOUL */
seg_type = PV_COMPONENT_STRING;
str_start = 0;
str_length = 0;
switch (formatstr[strpos]) {
case 'p':
state->display.format[segment].string = state->display.str_progress;
state->display.format[segment].length = -1;
state->display.components_used |= PV_DISPLAY_PROGRESS;
seg_type = PV_COMPONENT_PROGRESS;
break;
case 't':
state->display.format[segment].string = state->display.str_timer;
state->display.format[segment].length = 0;
state->display.components_used |= PV_DISPLAY_TIMER;
seg_type = PV_COMPONENT_TIMER;
break;
case 'e':
state->display.format[segment].string = state->display.str_eta;
state->display.format[segment].length = 0;
state->display.components_used |= PV_DISPLAY_ETA;
seg_type = PV_COMPONENT_ETA;
break;
case 'I':
state->display.format[segment].string = state->display.str_fineta;
state->display.format[segment].length = 0;
state->display.components_used |= PV_DISPLAY_FINETA;
seg_type = PV_COMPONENT_FINETA;
break;
case 'A':
state->display.format[segment].string = state->display.str_lastoutput;
state->display.format[segment].length = 0;
seg_type = PV_COMPONENT_OUTPUTBUF;
if (number_prefix > PV_SIZEOF_LASTOUTPUT_BUFFER)
number_prefix = PV_SIZEOF_LASTOUTPUT_BUFFER;
if (number_prefix < 1)
number_prefix = 1;
state->display.lastoutput_length = number_prefix;
state->display.components_used |= PV_DISPLAY_OUTPUTBUF;
state->display.lastoutput_length = (size_t) number_prefix;
break;
case 'r':
state->display.format[segment].string = state->display.str_rate;
state->display.format[segment].length = 0;
state->display.components_used |= PV_DISPLAY_RATE;
seg_type = PV_COMPONENT_RATE;
break;
case 'a':
state->display.format[segment].string = state->display.str_average_rate;
state->display.format[segment].length = 0;
state->display.components_used |= PV_DISPLAY_AVERAGERATE;
seg_type = PV_COMPONENT_AVERAGERATE;
break;
case 'b':
state->display.format[segment].string = state->display.str_transferred;
state->display.format[segment].length = 0;
state->display.components_used |= PV_DISPLAY_BYTES;
seg_type = PV_COMPONENT_BYTES;
break;
case 'T':
state->display.format[segment].string = state->display.str_bufpercent;
state->display.format[segment].length = 0;
state->display.components_used |= PV_DISPLAY_BUFPERCENT;
seg_type = PV_COMPONENT_BUFPERCENT;
break;
case 'N':
state->display.format[segment].string = state->display.str_name;
state->display.format[segment].length = (int) strlen(state->display.str_name);
state->display.components_used |= PV_DISPLAY_NAME;
seg_type = PV_COMPONENT_NAME;
break;
case '%':
/* %% => % */
state->display.format[segment].string = &(formatstr[strpos]);
state->display.format[segment].length = 1;
seg_type = PV_COMPONENT_STRING;
str_start = strpos;
str_length = 1;
break;
case 0:
case '\0':
/* % at end => just % */
state->display.format[segment].string = &(formatstr[--strpos]);
state->display.format[segment].length = 1;
seg_type = PV_COMPONENT_STRING;
str_start = strpos - 1;
str_length = 1;
break;
default:
/* %z (unknown) => %z */
state->display.format[segment].string = &(formatstr[--strpos]);
state->display.format[segment].length = 2;
strpos++;
seg_type = PV_COMPONENT_STRING;
str_start = strpos - 1;
str_length = 2;
break;
}
} else {
const char *searchptr;
int foundlength;
searchptr = strchr(&(formatstr[strpos]), '%');
if (NULL == searchptr) {
foundlength = (int) strlen(&(formatstr[strpos]));
} else {
foundlength = searchptr - &(formatstr[strpos]);
}
state->display.format[segment].string = &(formatstr[strpos]);
state->display.format[segment].length = foundlength;
seg_type = PV_COMPONENT_STRING;
str_start = strpos;
str_length = (size_t) foundlength;
strpos += foundlength - 1;
}
}
state->display.format[segment].string = 0;
state->display.format[segment].length = 0;
if (seg_type != PV_COMPONENT_STRING)
state->display.component[seg_type].required = true;
state->display.format[segment].type = seg_type;
state->display.format[segment].str_start = str_start;
state->display.format[segment].str_length = str_length;
state->display.format_segment_count++;
}
}
/*
@@ -544,8 +547,8 @@ static long bound_long(long x, long min, long max)
/* Update history and current average rate */
static void update_history_avg_rate(pvstate_t state, long long total_bytes, long double elapsed_sec, long double rate)
{
int first = state->display.history_first;
int last = state->display.history_last;
size_t first = state->display.history_first;
size_t last = state->display.history_last;
long double last_elapsed;
if (NULL == state->display.history)
@@ -566,10 +569,13 @@ static void update_history_avg_rate(pvstate_t state, long long total_bytes, long
* buffer.
*/
if (last_elapsed > 0.0) {
int len = state->display.history_len;
state->display.history_last = last = (last + 1) % len;
if (last == first)
state->display.history_first = first = (first + 1) % len;
size_t len = state->display.history_len;
last = (last + 1) % len;
state->display.history_last = last;
if (last == first) {
first = (first + 1) % len;
state->display.history_first = first;
}
}
state->display.history[last].elapsed_sec = elapsed_sec;
@@ -603,15 +609,18 @@ static void update_history_avg_rate(pvstate_t state, long long total_bytes, long
* If "total_bytes" is negative, then free all allocated memory and return
* NULL.
*/
/*@null@*/
static const char *pv__format(pvstate_t state,
long double elapsed_sec, long long bytes_since_last, long long total_bytes)
{
long double time_since_last, rate, average_rate;
long eta;
int static_portion_size;
int segment;
pv_display_component component_type;
size_t segment;
int output_length;
int display_string_length;
const char *formatstr;
/* Quick sanity check - state must exist */
if (NULL == state)
@@ -625,6 +634,11 @@ static const char *pv__format(pvstate_t state,
return NULL;
}
/* The format string is needed for the static segments. */
formatstr = state->control.format_string ? state->control.format_string : state->control.default_format;
if (NULL == formatstr)
return NULL;
/*
* In case the time since the last update is very small, we keep
* track of amount transferred since the last update, and just keep
@@ -674,13 +688,13 @@ static const char *pv__format(pvstate_t state,
state->display.percentage += 2;
if (state->display.percentage > 199)
state->display.percentage = 0;
} else if (state->control.numeric || ((state->display.components_used & PV_DISPLAY_PROGRESS) != 0)) {
} else if (state->control.numeric || state->display.component[PV_COMPONENT_PROGRESS].required) {
/*
* If we do know the total size, and we're going to show
* the percentage (numeric mode or a progress bar),
* calculate the percentage completion.
*/
state->display.percentage = pv__calc_percentage(total_bytes, state->control.size);
state->display.percentage = pv__calc_percentage(total_bytes, (long long)(state->control.size));
}
/*
@@ -721,10 +735,10 @@ static const char *pv__format(pvstate_t state,
numericprefix[0] = '\0';
if ((state->display.components_used & PV_DISPLAY_TIMER) != 0)
if (state->display.component[PV_COMPONENT_TIMER].required)
(void) pv_snprintf(numericprefix, sizeof(numericprefix), "%.4Lf ", elapsed_sec);
if ((state->display.components_used & PV_DISPLAY_BYTES) != 0) {
if (state->display.component[PV_COMPONENT_BYTES].required) {
if (state->control.bits) {
(void) pv_snprintf(state->display.display_buffer,
state->display.display_buffer_size,
@@ -750,209 +764,251 @@ static const char *pv__format(pvstate_t state,
* to be placed in the output buffer.
*/
state->display.str_transferred[0] = '\0';
state->display.str_bufpercent[0] = '\0';
state->display.str_timer[0] = '\0';
state->display.str_rate[0] = '\0';
state->display.str_average_rate[0] = '\0';
state->display.str_progress[0] = '\0';
state->display.str_lastoutput[0] = '\0';
state->display.str_eta[0] = '\0';
state->display.str_fineta[0] = '\0';
for (component_type = 0; component_type < PV_COMPONENT__MAX; component_type++) {
char *component_content;
if (!state->display.component[component_type].required)
continue;
/* If we're showing bytes transferred, set up the display string. */
if ((state->display.components_used & PV_DISPLAY_BYTES) != 0) {
if (state->control.bits && !state->control.linemode) {
pv__sizestr(state->display.str_transferred,
PV_SIZEOF_STR_TRANSFERRED, "%s", (long double) total_bytes * 8, "", _("b"),
PV_TRANSFERCOUNT_BYTES);
} else {
pv__sizestr(state->display.str_transferred,
PV_SIZEOF_STR_TRANSFERRED, "%s",
(long double) total_bytes, "", _("B"),
state->control.linemode ? PV_TRANSFERCOUNT_LINES : PV_TRANSFERCOUNT_BYTES);
/*
* Don't try to calculate ETA if the size is not known. We
* check here to avoid big indented blocks if we check
* later.
*/
if (state->control.size < 1
&& ((component_type == PV_COMPONENT_ETA) || (component_type == PV_COMPONENT_FINETA))) {
state->display.component[component_type].content[0] = '\0';
state->display.component[component_type].length = 0;
continue;
}
}
/* Transfer buffer percentage - set up the display string. */
if ((state->display.components_used & PV_DISPLAY_BUFPERCENT) != 0) {
if (state->transfer.buffer_size > 0)
(void) pv_snprintf(state->display.str_bufpercent,
PV_SIZEOF_STR_BUFPERCENT,
"{%3ld%%}",
pv__calc_percentage
(state->transfer.read_position - state->transfer.write_position,
state->transfer.buffer_size));
component_content = state->display.component[component_type].content;
component_content[0] = '\0';
switch (component_type) {
case PV_COMPONENT_STRING:
break;
case PV_COMPONENT_PROGRESS:
/* Progress bar - variable width, so do this later. */
break;
case PV_COMPONENT_BYTES:
/* Bytes / bits / lines transferred. */
if (state->control.bits && !state->control.linemode) {
pv__sizestr(component_content, PV_SIZEOF_COMPONENT_STR, "%s",
(long double) total_bytes * 8, "", _("b"), PV_TRANSFERCOUNT_BYTES);
} else {
pv__sizestr(component_content, PV_SIZEOF_COMPONENT_STR, "%s",
(long double) total_bytes, "", _("B"),
state->control.linemode ? PV_TRANSFERCOUNT_LINES : PV_TRANSFERCOUNT_BYTES);
}
break;
case PV_COMPONENT_TIMER:
/* Elapsed time. */
/*
* 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 (elapsed_sec > (long double) 360000000.0L)
elapsed_sec = (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 (elapsed_sec > (long double) 86400.0L) {
(void) pv_snprintf(component_content,
PV_SIZEOF_COMPONENT_STR,
"%ld:%02ld:%02ld:%02ld",
((long) elapsed_sec) / 86400,
(((long) elapsed_sec) / 3600) %
24, (((long) elapsed_sec) / 60) % 60, ((long) elapsed_sec) % 60);
} else {
(void) pv_snprintf(component_content,
PV_SIZEOF_COMPONENT_STR,
"%ld:%02ld:%02ld",
((long) elapsed_sec) / 3600,
(((long) elapsed_sec) / 60) % 60, ((long) elapsed_sec) % 60);
}
break;
case PV_COMPONENT_RATE:
/* Current transfer rate. */
if (state->control.bits && !state->control.linemode) {
/* bits per second */
pv__sizestr(component_content, PV_SIZEOF_COMPONENT_STR, "[%s]", 8 * rate, "", _("b/s"),
PV_TRANSFERCOUNT_BYTES);
} else {
/* bytes or lines per second */
pv__sizestr(component_content, PV_SIZEOF_COMPONENT_STR,
"[%s]", rate, _("/s"), _("B/s"),
state->control.linemode ? PV_TRANSFERCOUNT_LINES : PV_TRANSFERCOUNT_BYTES);
}
break;
case PV_COMPONENT_AVERAGERATE:
/* Average transfer rate. */
if (state->control.bits && !state->control.linemode) {
/* bits per second */
pv__sizestr(component_content, PV_SIZEOF_COMPONENT_STR,
"[%s]", 8 * average_rate, "", _("b/s"), PV_TRANSFERCOUNT_BYTES);
} else {
/* bytes or lines per second */
pv__sizestr(component_content,
PV_SIZEOF_COMPONENT_STR,
"[%s]", average_rate, _("/s"), _("B/s"),
state->control.linemode ? PV_TRANSFERCOUNT_LINES : PV_TRANSFERCOUNT_BYTES);
}
break;
case PV_COMPONENT_ETA:
/* Estimated time remaining until completion - if size is known. */
eta =
pv__calc_eta(total_bytes - state->display.initial_offset,
state->control.size - state->display.initial_offset,
state->display.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.
*/
if (eta > 86400L) {
(void) pv_snprintf(component_content,
PV_SIZEOF_COMPONENT_STR,
"%.16s %ld:%02ld:%02ld:%02ld",
_("ETA"), eta / 86400, (eta / 3600) % 24, (eta / 60) % 60, eta % 60);
} else {
(void) pv_snprintf(component_content,
PV_SIZEOF_COMPONENT_STR,
"%.16s %ld:%02ld:%02ld", _("ETA"), eta / 3600, (eta / 60) % 60,
eta % 60);
}
/*
* If this is the final update, show a blank space where the
* ETA used to be.
*/
if (bytes_since_last < 0) {
size_t erase_idx;
for (erase_idx = 0;
erase_idx < PV_SIZEOF_COMPONENT_STR && component_content[erase_idx] != '\0';
erase_idx++) {
component_content[erase_idx] = ' ';
}
}
break;
case PV_COMPONENT_FINETA:
/* Estimated time of completion - if size is known. */
/*
* The ETA may be hidden by a failed ETA string
* generation.
*/
bool show_eta = true;
time_t now = time(NULL);
time_t then;
struct tm *time_ptr;
char *time_format = NULL;
eta =
pv__calc_eta(total_bytes - state->display.initial_offset,
state->control.size - state->display.initial_offset,
state->display.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);
/*
* 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_eta = false;
} else {
/* Localtime keeps data stored in a static
* buffer that gets overwritten by time
* functions.
*/
struct tm time = *time_ptr;
(void) pv_snprintf(component_content, PV_SIZEOF_COMPONENT_STR, "%.16s ", _("ETA"));
(void) strftime(component_content + strlen(component_content),
PV_SIZEOF_COMPONENT_STR - 1 - strlen(component_content), time_format, &time);
}
if (!show_eta) {
size_t erase_idx;
for (erase_idx = 0;
erase_idx < PV_SIZEOF_COMPONENT_STR && component_content[erase_idx] != '\0';
erase_idx++) {
component_content[erase_idx] = ' ';
}
}
break;
case PV_COMPONENT_NAME:
/* Name prefix. */
if (state->control.name) {
(void) pv_snprintf(component_content, PV_SIZEOF_COMPONENT_STR, "%9.500s:",
state->control.name);
}
break;
case PV_COMPONENT_BUFPERCENT:
/* Transfer buffer percentage utilisation. */
if (state->transfer.buffer_size > 0)
(void) pv_snprintf(component_content,
PV_SIZEOF_COMPONENT_STR,
"{%3ld%%}",
pv__calc_percentage
(state->transfer.read_position - state->transfer.write_position,
state->transfer.buffer_size));
#ifdef HAVE_SPLICE
if (state->transfer.splice_used)
(void) pv_snprintf(state->display.str_bufpercent, PV_SIZEOF_STR_BUFPERCENT, "{%s}", "----");
if (state->transfer.splice_used)
(void) pv_snprintf(component_content, PV_SIZEOF_COMPONENT_STR, "{%s}", "----");
#endif
}
break;
/* Timer - set up the display string. */
if ((state->display.components_used & PV_DISPLAY_TIMER) != 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 (elapsed_sec > (long double) 360000000.0L)
elapsed_sec = (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 (elapsed_sec > (long double) 86400.0L) {
(void) pv_snprintf(state->display.str_timer,
PV_SIZEOF_STR_TIMER,
"%ld:%02ld:%02ld:%02ld",
((long) elapsed_sec) / 86400,
(((long) elapsed_sec) / 3600) %
24, (((long) elapsed_sec) / 60) % 60, ((long) elapsed_sec) % 60);
} else {
(void) pv_snprintf(state->display.str_timer,
PV_SIZEOF_STR_TIMER,
"%ld:%02ld:%02ld",
((long) elapsed_sec) / 3600,
(((long) elapsed_sec) / 60) % 60, ((long) elapsed_sec) % 60);
}
}
/* Rate - set up the display string. */
if ((state->display.components_used & PV_DISPLAY_RATE) != 0) {
if (state->control.bits && !state->control.linemode) {
pv__sizestr(state->display.str_rate, PV_SIZEOF_STR_RATE, "[%s]", 8 * rate, "", _("b/s"),
PV_TRANSFERCOUNT_BYTES);
} else {
pv__sizestr(state->display.str_rate,
PV_SIZEOF_STR_RATE, "[%s]", rate, _("/s"), _("B/s"),
state->control.linemode ? PV_TRANSFERCOUNT_LINES : PV_TRANSFERCOUNT_BYTES);
}
}
/* Average rate - set up the display string. */
if ((state->display.components_used & PV_DISPLAY_AVERAGERATE) != 0) {
if (state->control.bits && !state->control.linemode) {
pv__sizestr(state->display.str_average_rate,
PV_SIZEOF_STR_AVERAGE_RATE, "[%s]", 8 * average_rate, "", _("b/s"),
PV_TRANSFERCOUNT_BYTES);
} else {
pv__sizestr(state->display.str_average_rate,
PV_SIZEOF_STR_AVERAGE_RATE,
"[%s]", average_rate, _("/s"), _("B/s"),
state->control.linemode ? PV_TRANSFERCOUNT_LINES : PV_TRANSFERCOUNT_BYTES);
}
}
/* Last output bytes - set up the display string. */
if ((state->display.components_used & PV_DISPLAY_OUTPUTBUF) != 0) {
int idx;
for (idx = 0; idx < state->display.lastoutput_length; idx++) {
int c;
c = state->display.lastoutput_buffer[idx];
state->display.str_lastoutput[idx] = isprint(c) ? c : '.';
}
state->display.str_lastoutput[idx] = '\0';
}
/* ETA (only if size is known) - set up the display string. */
if (((state->display.components_used & PV_DISPLAY_ETA) != 0)
&& (state->control.size > 0)) {
eta =
pv__calc_eta(total_bytes - state->display.initial_offset,
state->control.size - state->display.initial_offset, state->display.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.
*/
if (eta > 86400L) {
(void) pv_snprintf(state->display.str_eta,
PV_SIZEOF_STR_ETA,
"%.16s %ld:%02ld:%02ld:%02ld",
_("ETA"), eta / 86400, (eta / 3600) % 24, (eta / 60) % 60, eta % 60);
} else {
(void) pv_snprintf(state->display.str_eta,
PV_SIZEOF_STR_ETA,
"%.16s %ld:%02ld:%02ld", _("ETA"), eta / 3600, (eta / 60) % 60, eta % 60);
}
/*
* If this is the final update, show a blank space where the
* ETA used to be.
*/
if (bytes_since_last < 0) {
unsigned int i;
for (i = 0; i < PV_SIZEOF_STR_ETA && state->display.str_eta[i] != '\0'; i++) {
state->display.str_eta[i] = ' ';
case PV_COMPONENT_OUTPUTBUF:
/* Recently transferred bytes. */
size_t buf_idx;
for (buf_idx = 0; buf_idx < state->display.lastoutput_length; buf_idx++) {
int display_char;
display_char = (int) (state->display.lastoutput_buffer[buf_idx]);
component_content[buf_idx] = isprint(display_char) ? (char) display_char : '.';
}
component_content[buf_idx] = '\0';
break;
default:
break;
}
/* Record the string length for this component. */
state->display.component[component_type].length = strlen(component_content);
}
/* ETA as clock time (as above) - set up the display string. */
if (((state->display.components_used & PV_DISPLAY_FINETA) != 0)
&& (state->control.size > 0)) {
/*
* The ETA may be hidden by a failed ETA string
* generation.
*/
int show_eta = 1;
time_t now = time(NULL);
time_t then;
struct tm *time_ptr;
char *time_format = NULL;
eta =
pv__calc_eta(total_bytes - state->display.initial_offset,
state->control.size - state->display.initial_offset, state->display.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);
/*
* 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_eta = 0;
} else {
/* Localtime keeps data stored in a
* static buffer that gets overwritten
* by time functions. */
struct tm time = *time_ptr;
(void) pv_snprintf(state->display.str_fineta, PV_SIZEOF_STR_FINETA, "%.16s ", _("ETA"));
strftime(state->display.str_fineta +
strlen(state->display.str_fineta),
PV_SIZEOF_STR_FINETA - 1 - strlen(state->display.str_fineta), time_format, &time);
}
if (!show_eta) {
unsigned int i;
for (i = 0; i < PV_SIZEOF_STR_FINETA && state->display.str_fineta[i] != '\0'; i++) {
state->display.str_fineta[i] = ' ';
}
}
}
/*
* Now go through all the static portions of the format to work
@@ -960,13 +1016,11 @@ static const char *pv__format(pvstate_t state,
* (i.e. the progress bar).
*/
static_portion_size = 0;
for (segment = 0; state->display.format[segment].string; segment++) {
if (state->display.format[segment].length < 0) {
continue;
} else if (state->display.format[segment].length > 0) {
static_portion_size += state->display.format[segment].length;
} else {
static_portion_size += strlen(state->display.format[segment].string);
for (segment = 0; segment < state->display.format_segment_count; segment++) {
if (state->display.format[segment].type == PV_COMPONENT_STRING) {
static_portion_size += state->display.format[segment].str_length;
} else if (state->display.format[segment].type != PV_COMPONENT_PROGRESS) {
static_portion_size += state->display.component[state->display.format[segment].type].length;
}
}
@@ -975,11 +1029,15 @@ static const char *pv__format(pvstate_t state,
/*
* Assemble the progress bar now we know how big it should be.
*/
if ((state->display.components_used & PV_DISPLAY_PROGRESS) != 0) {
if (state->display.component[PV_COMPONENT_PROGRESS].required) {
char *component_content;
char pct[16];
int available_width, i;
strcpy(state->display.str_progress, "[");
component_content = state->display.component[PV_COMPONENT_PROGRESS].content;
component_content[0] = '\0';
(void) pv_snprintf(component_content, PV_SIZEOF_COMPONENT_STR, "[");
if (state->control.size > 0) {
if (state->display.percentage < 0)
@@ -993,22 +1051,22 @@ static const char *pv__format(pvstate_t state,
if (available_width < 0)
available_width = 0;
if (available_width > (int) (PV_SIZEOF_STR_PROGRESS) - 16)
available_width = PV_SIZEOF_STR_PROGRESS - 16;
if (available_width > (int) (PV_SIZEOF_COMPONENT_STR) - 16)
available_width = PV_SIZEOF_COMPONENT_STR - 16;
for (i = 0; i < (available_width * state->display.percentage) / 100 - 1; i++) {
if (i < available_width)
(void) pv_strlcat(state->display.str_progress, "=", PV_SIZEOF_STR_PROGRESS);
(void) pv_strlcat(component_content, "=", PV_SIZEOF_COMPONENT_STR);
}
if (i < available_width) {
(void) pv_strlcat(state->display.str_progress, ">", PV_SIZEOF_STR_PROGRESS);
(void) pv_strlcat(component_content, ">", PV_SIZEOF_COMPONENT_STR);
i++;
}
for (; i < available_width; i++) {
(void) pv_strlcat(state->display.str_progress, " ", PV_SIZEOF_STR_PROGRESS);
(void) pv_strlcat(component_content, " ", PV_SIZEOF_COMPONENT_STR);
}
(void) pv_strlcat(state->display.str_progress, "] ", PV_SIZEOF_STR_PROGRESS);
(void) pv_strlcat(state->display.str_progress, pct, PV_SIZEOF_STR_PROGRESS);
(void) pv_strlcat(component_content, "] ", PV_SIZEOF_COMPONENT_STR);
(void) pv_strlcat(component_content, pct, PV_SIZEOF_COMPONENT_STR);
} else {
int p = state->display.percentage;
@@ -1017,8 +1075,8 @@ static const char *pv__format(pvstate_t state,
if (available_width < 0)
available_width = 0;
if (available_width > (int) (PV_SIZEOF_STR_PROGRESS) - 16)
available_width = PV_SIZEOF_STR_PROGRESS - 16;
if (available_width > (int) (PV_SIZEOF_COMPONENT_STR) - 16)
available_width = PV_SIZEOF_COMPONENT_STR - 16;
debug("available_width: %d", available_width);
@@ -1026,20 +1084,23 @@ static const char *pv__format(pvstate_t state,
p = 200 - p;
for (i = 0; i < (available_width * p) / 100; i++) {
if (i < available_width)
(void) pv_strlcat(state->display.str_progress, " ", PV_SIZEOF_STR_PROGRESS);
(void) pv_strlcat(component_content, " ", PV_SIZEOF_COMPONENT_STR);
}
(void) pv_strlcat(state->display.str_progress, "<=>", PV_SIZEOF_STR_PROGRESS);
(void) pv_strlcat(component_content, "<=>", PV_SIZEOF_COMPONENT_STR);
for (; i < available_width; i++) {
(void) pv_strlcat(state->display.str_progress, " ", PV_SIZEOF_STR_PROGRESS);
(void) pv_strlcat(component_content, " ", PV_SIZEOF_COMPONENT_STR);
}
(void) pv_strlcat(state->display.str_progress, "]", PV_SIZEOF_STR_PROGRESS);
(void) pv_strlcat(component_content, "]", PV_SIZEOF_COMPONENT_STR);
}
/*
* If the progress bar won't fit, drop it.
*/
if (strlen(state->display.str_progress) + static_portion_size > state->control.width)
state->display.str_progress[0] = '\0';
if (strlen(component_content) + static_portion_size > state->control.width)
component_content[0] = '\0';
/* Record the string length for this component. */
state->display.component[PV_COMPONENT_PROGRESS].length = strlen(component_content);
}
/*
@@ -1048,28 +1109,38 @@ static const char *pv__format(pvstate_t state,
state->display.display_buffer[0] = '\0';
display_string_length = 0;
for (segment = 0; state->display.format[segment].string; segment++) {
int segment_length;
if (state->display.format[segment].length > 0) {
segment_length = state->display.format[segment].length;
for (segment = 0; segment < state->display.format_segment_count; segment++) {
const char *segment_content;
size_t segment_length;
if (state->display.format[segment].type == PV_COMPONENT_STRING) {
segment_content = &(formatstr[state->display.format[segment].str_start]);
segment_length = state->display.format[segment].str_length;
} else {
segment_length = strlen(state->display.format[segment].string);
segment_content = state->display.component[state->display.format[segment].type].content;
segment_length = state->display.component[state->display.format[segment].type].length;
}
/* Skip empty segments */
/* Skip empty segments. */
if (segment_length == 0)
continue;
/*
* Truncate segment if it would make the display string
* overflow the buffer
* Truncate the segment if it would make the display string
* overflow the buffer.
*/
if (segment_length + display_string_length > state->display.display_buffer_size - 2)
segment_length = state->display.display_buffer_size - display_string_length - 2;
if (segment_length < 1)
break;
/* Skip segment if it would make the display too wide */
/* Skip the segment if it would make the display too wide. */
if (segment_length + display_string_length > (int) (state->control.width))
break;
strncat(state->display.display_buffer, state->display.format[segment].string, segment_length);
/* Append the segment to the output string. */
strncat(state->display.display_buffer, segment_content, segment_length);
display_string_length += segment_length;
}
+1 -2
View File
@@ -695,8 +695,7 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long
* If we're monitoring the output, update our copy of the
* last few bytes we've written.
*/
if (((state->display.components_used & PV_DISPLAY_OUTPUTBUF) != 0)
&& (nwritten > 0)) {
if (state->display.component[PV_COMPONENT_OUTPUTBUF].required && (nwritten > 0)) {
size_t new_portion_length, old_portion_length;
new_portion_length = (size_t) nwritten;