791 lines
18 KiB
C
791 lines
18 KiB
C
/*
|
|
* Main program entry point - read the command line options, then perform
|
|
* the appropriate actions.
|
|
*/
|
|
|
|
#include "pv-internal.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#define _GNU_SOURCE 1
|
|
#include <limits.h>
|
|
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
|
|
|
|
/*
|
|
* Add the given number of microseconds (which may be negative) to the given
|
|
* timeval.
|
|
*/
|
|
static void pv_timeval_add_usec(struct timeval *val, long usec)
|
|
{
|
|
val->tv_usec += usec;
|
|
while (val->tv_usec < 0) {
|
|
val->tv_sec--;
|
|
val->tv_usec += 1000000;
|
|
}
|
|
while (val->tv_usec >= 1000000) {
|
|
val->tv_sec++;
|
|
val->tv_usec -= 1000000;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Pipe data from a list of files to standard output, giving information
|
|
* about the transfer on standard error according to the given options.
|
|
*
|
|
* Returns nonzero on error.
|
|
*/
|
|
int pv_main_loop(pvstate_t state)
|
|
{
|
|
long written, lineswritten;
|
|
long long total_written, since_last, cansend;
|
|
long double target;
|
|
int eof_in, eof_out, final_update;
|
|
struct timeval start_time, next_update, next_ratecheck, cur_time;
|
|
struct timeval init_time, next_remotecheck;
|
|
long double elapsed;
|
|
struct stat64 sb;
|
|
int fd, n;
|
|
|
|
/*
|
|
* "written" is ALWAYS bytes written by the last transfer.
|
|
*
|
|
* "lineswritten" is the lines written by the last transfer,
|
|
* but is only updated in line mode.
|
|
*
|
|
* "total_written" is the total bytes written since the start,
|
|
* or in line mode, the total lines written since the start.
|
|
*
|
|
* "since_last" is the bytes written since the last display,
|
|
* or in line mode, the lines written since the last display.
|
|
*
|
|
* The remaining variables are all unchanged by linemode.
|
|
*/
|
|
|
|
fd = -1;
|
|
|
|
pv_crs_init(state);
|
|
|
|
eof_in = 0;
|
|
eof_out = 0;
|
|
total_written = 0;
|
|
since_last = 0;
|
|
state->initial_offset = 0;
|
|
|
|
gettimeofday(&start_time, NULL);
|
|
gettimeofday(&cur_time, NULL);
|
|
|
|
next_update.tv_sec = start_time.tv_sec;
|
|
next_update.tv_usec = start_time.tv_usec;
|
|
if ((state->delay_start > 0)
|
|
&& (state->delay_start > state->interval)) {
|
|
pv_timeval_add_usec(&next_update,
|
|
(long) (1000000.0 *
|
|
state->delay_start));
|
|
} else {
|
|
pv_timeval_add_usec(&next_update,
|
|
(long) (1000000.0 * state->interval));
|
|
}
|
|
|
|
next_ratecheck.tv_sec = start_time.tv_sec;
|
|
next_ratecheck.tv_usec = start_time.tv_usec;
|
|
next_remotecheck.tv_sec = start_time.tv_sec;
|
|
next_remotecheck.tv_usec = start_time.tv_usec;
|
|
|
|
target = 0;
|
|
final_update = 0;
|
|
n = 0;
|
|
|
|
fd = pv_next_file(state, n, -1);
|
|
if (fd < 0) {
|
|
if (state->cursor)
|
|
pv_crs_fini(state);
|
|
return state->exit_status;
|
|
}
|
|
|
|
/*
|
|
* Set target buffer size if the initial file's block size can be
|
|
* read and we weren't given a target buffer size.
|
|
*/
|
|
if ((0 == fstat64(fd, &sb)) && (0 == state->target_buffer_size)) {
|
|
unsigned long long sz;
|
|
sz = sb.st_blksize * 32;
|
|
if (sz > BUFFER_SIZE_MAX)
|
|
sz = BUFFER_SIZE_MAX;
|
|
state->target_buffer_size = sz;
|
|
}
|
|
|
|
if (0 == state->target_buffer_size)
|
|
state->target_buffer_size = BUFFER_SIZE;
|
|
|
|
while ((!(eof_in && eof_out)) || (!final_update)) {
|
|
|
|
cansend = 0;
|
|
|
|
/*
|
|
* Check for remote messages from -R every short while
|
|
*/
|
|
if ((cur_time.tv_sec > next_remotecheck.tv_sec)
|
|
|| (cur_time.tv_sec == next_remotecheck.tv_sec
|
|
&& cur_time.tv_usec >= next_remotecheck.tv_usec)) {
|
|
pv_remote_check(state);
|
|
pv_timeval_add_usec(&next_remotecheck,
|
|
REMOTE_INTERVAL);
|
|
}
|
|
|
|
if (state->pv_sig_abort)
|
|
break;
|
|
|
|
if (state->rate_limit > 0) {
|
|
gettimeofday(&cur_time, NULL);
|
|
if ((cur_time.tv_sec > next_ratecheck.tv_sec)
|
|
|| (cur_time.tv_sec == next_ratecheck.tv_sec
|
|
&& cur_time.tv_usec >=
|
|
next_ratecheck.tv_usec)) {
|
|
target +=
|
|
((long double) (state->rate_limit)) /
|
|
(long double) (1000000 /
|
|
RATE_GRANULARITY);
|
|
pv_timeval_add_usec(&next_ratecheck,
|
|
RATE_GRANULARITY);
|
|
}
|
|
cansend = target;
|
|
}
|
|
|
|
/*
|
|
* If we have to stop at "size" bytes, make sure we don't
|
|
* try to write more than we're allowed to.
|
|
*/
|
|
if ((0 < state->size) && (state->stop_at_size)) {
|
|
if (((long) (state->size) <
|
|
(total_written + cansend))
|
|
|| ((0 == cansend)
|
|
&& (0 == state->rate_limit))) {
|
|
cansend = state->size - total_written;
|
|
if (0 >= cansend) {
|
|
eof_in = 1;
|
|
eof_out = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((0 < state->size) && (state->stop_at_size)
|
|
&& (0 >= cansend) && eof_in && eof_out) {
|
|
written = 0;
|
|
} else {
|
|
written =
|
|
pv_transfer(state, fd, &eof_in, &eof_out,
|
|
cansend, &lineswritten);
|
|
}
|
|
|
|
if (written < 0) {
|
|
if (state->cursor)
|
|
pv_crs_fini(state);
|
|
return state->exit_status;
|
|
}
|
|
|
|
if (state->linemode) {
|
|
since_last += lineswritten;
|
|
total_written += lineswritten;
|
|
if (state->rate_limit > 0)
|
|
target -= lineswritten;
|
|
} else {
|
|
since_last += written;
|
|
total_written += written;
|
|
if (state->rate_limit > 0)
|
|
target -= written;
|
|
}
|
|
|
|
if (eof_in && eof_out && n < (state->input_file_count - 1)) {
|
|
n++;
|
|
fd = pv_next_file(state, n, fd);
|
|
if (fd < 0) {
|
|
if (state->cursor)
|
|
pv_crs_fini(state);
|
|
return state->exit_status;
|
|
}
|
|
eof_in = 0;
|
|
eof_out = 0;
|
|
}
|
|
|
|
gettimeofday(&cur_time, NULL);
|
|
|
|
if (eof_in && eof_out) {
|
|
final_update = 1;
|
|
if ((state->display_visible)
|
|
|| (0 == state->delay_start))
|
|
next_update.tv_sec = cur_time.tv_sec - 1;
|
|
}
|
|
|
|
if (state->no_op)
|
|
continue;
|
|
|
|
/*
|
|
* If -W was given, we don't output anything until we have
|
|
* written a byte (or line, in line mode), at which point
|
|
* we then count time as if we started when the first byte
|
|
* was received.
|
|
*/
|
|
if (state->wait) {
|
|
if (state->linemode) {
|
|
if (lineswritten < 1)
|
|
continue;
|
|
} else {
|
|
if (written < 1)
|
|
continue;
|
|
}
|
|
|
|
state->wait = 0;
|
|
|
|
/*
|
|
* Reset the timer offset counter now that data
|
|
* transfer has begun, otherwise if we had been
|
|
* stopped and started (with ^Z / SIGTSTOP)
|
|
* previously (while waiting for data), the timers
|
|
* will be wrongly offset.
|
|
*
|
|
* While we reset the offset counter we must disable
|
|
* SIGTSTOP so things don't mess up.
|
|
*/
|
|
pv_sig_nopause();
|
|
gettimeofday(&start_time, NULL);
|
|
state->pv_sig_toffset.tv_sec = 0;
|
|
state->pv_sig_toffset.tv_usec = 0;
|
|
pv_sig_allowpause();
|
|
|
|
next_update.tv_sec = start_time.tv_sec;
|
|
next_update.tv_usec = start_time.tv_usec;
|
|
pv_timeval_add_usec(&next_update,
|
|
(long) (1000000.0 *
|
|
state->interval));
|
|
}
|
|
|
|
if ((cur_time.tv_sec < next_update.tv_sec)
|
|
|| (cur_time.tv_sec == next_update.tv_sec
|
|
&& cur_time.tv_usec < next_update.tv_usec)) {
|
|
continue;
|
|
}
|
|
|
|
pv_timeval_add_usec(&next_update,
|
|
(long) (1000000.0 * state->interval));
|
|
|
|
if (next_update.tv_sec < cur_time.tv_sec) {
|
|
next_update.tv_sec = cur_time.tv_sec;
|
|
next_update.tv_usec = cur_time.tv_usec;
|
|
} else if (next_update.tv_sec == cur_time.tv_sec
|
|
&& next_update.tv_usec < cur_time.tv_usec) {
|
|
next_update.tv_usec = cur_time.tv_usec;
|
|
}
|
|
|
|
init_time.tv_sec =
|
|
start_time.tv_sec + state->pv_sig_toffset.tv_sec;
|
|
init_time.tv_usec =
|
|
start_time.tv_usec + state->pv_sig_toffset.tv_usec;
|
|
if (init_time.tv_usec >= 1000000) {
|
|
init_time.tv_sec++;
|
|
init_time.tv_usec -= 1000000;
|
|
}
|
|
if (init_time.tv_usec < 0) {
|
|
init_time.tv_sec--;
|
|
init_time.tv_usec += 1000000;
|
|
}
|
|
|
|
elapsed = cur_time.tv_sec - init_time.tv_sec;
|
|
elapsed +=
|
|
(cur_time.tv_usec - init_time.tv_usec) / 1000000.0;
|
|
|
|
if (final_update)
|
|
since_last = -1;
|
|
|
|
if (state->pv_sig_newsize) {
|
|
state->pv_sig_newsize = 0;
|
|
pv_screensize(&(state->width), &(state->height));
|
|
}
|
|
|
|
pv_display(state, elapsed, since_last, total_written);
|
|
|
|
since_last = 0;
|
|
}
|
|
|
|
if (state->cursor) {
|
|
pv_crs_fini(state);
|
|
} else {
|
|
if ((!state->numeric) && (!state->no_op)
|
|
&& (state->display_visible))
|
|
write(STDERR_FILENO, "\n", 1);
|
|
}
|
|
|
|
if (state->pv_sig_abort)
|
|
state->exit_status |= 32;
|
|
|
|
if (fd >= 0)
|
|
close(fd);
|
|
|
|
return state->exit_status;
|
|
}
|
|
|
|
|
|
/*
|
|
* Watch the progress of file descriptor state->watch_fd in process
|
|
* state->watch_pid and show details about the transfer on standard error
|
|
* according to the given options.
|
|
*
|
|
* Returns nonzero on error.
|
|
*/
|
|
int pv_watchfd_loop(pvstate_t state)
|
|
{
|
|
struct pvwatchfd_s info;
|
|
long long position_now, total_written, since_last;
|
|
struct timeval next_update, cur_time;
|
|
struct timeval init_time, next_remotecheck;
|
|
long double elapsed;
|
|
int ended;
|
|
int first_check;
|
|
int rc;
|
|
|
|
info.watch_pid = state->watch_pid;
|
|
info.watch_fd = state->watch_fd;
|
|
rc = pv_watchfd_info(state, &info, 0);
|
|
if (0 != rc) {
|
|
state->exit_status |= 2;
|
|
return state->exit_status;
|
|
}
|
|
|
|
/*
|
|
* Use a size if one was passed, otherwise use the total size
|
|
* calculated.
|
|
*/
|
|
if (0 >= state->size)
|
|
state->size = info.size;
|
|
|
|
if (state->size < 1) {
|
|
char *fmt;
|
|
while (NULL != (fmt = strstr(state->default_format, "%e"))) {
|
|
debug("%s", "zero size - removing ETA");
|
|
/* strlen-1 here to include trailing NUL */
|
|
memmove(fmt, fmt + 2, strlen(fmt) - 1);
|
|
state->reparse_display = 1;
|
|
}
|
|
}
|
|
|
|
gettimeofday(&(info.start_time), NULL);
|
|
gettimeofday(&cur_time, NULL);
|
|
|
|
next_update.tv_sec = info.start_time.tv_sec;
|
|
next_update.tv_usec = info.start_time.tv_usec;
|
|
pv_timeval_add_usec(&next_update,
|
|
(long) (1000000.0 * state->interval));
|
|
|
|
next_remotecheck.tv_sec = info.start_time.tv_sec;
|
|
next_remotecheck.tv_usec = info.start_time.tv_usec;
|
|
|
|
ended = 0;
|
|
total_written = 0;
|
|
since_last = 0;
|
|
first_check = 1;
|
|
|
|
while (!ended) {
|
|
/*
|
|
* Check for remote messages from -R every short while
|
|
*/
|
|
if ((cur_time.tv_sec > next_remotecheck.tv_sec)
|
|
|| (cur_time.tv_sec == next_remotecheck.tv_sec
|
|
&& cur_time.tv_usec >= next_remotecheck.tv_usec)) {
|
|
pv_remote_check(state);
|
|
pv_timeval_add_usec(&next_remotecheck,
|
|
REMOTE_INTERVAL);
|
|
}
|
|
|
|
if (state->pv_sig_abort)
|
|
break;
|
|
|
|
position_now = pv_watchfd_position(&info);
|
|
|
|
if (position_now < 0) {
|
|
ended = 1;
|
|
} else {
|
|
since_last += position_now - total_written;
|
|
total_written = position_now;
|
|
if (first_check) {
|
|
state->initial_offset = position_now;
|
|
first_check = 0;
|
|
}
|
|
}
|
|
|
|
gettimeofday(&cur_time, NULL);
|
|
|
|
if (ended) {
|
|
ended = 1;
|
|
next_update.tv_sec = cur_time.tv_sec - 1;
|
|
}
|
|
|
|
if ((cur_time.tv_sec < next_update.tv_sec)
|
|
|| (cur_time.tv_sec == next_update.tv_sec
|
|
&& cur_time.tv_usec < next_update.tv_usec)) {
|
|
struct timeval tv;
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 50000;
|
|
select(0, NULL, NULL, NULL, &tv);
|
|
continue;
|
|
}
|
|
|
|
pv_timeval_add_usec(&next_update,
|
|
(long) (1000000.0 * state->interval));
|
|
|
|
if (next_update.tv_sec < cur_time.tv_sec) {
|
|
next_update.tv_sec = cur_time.tv_sec;
|
|
next_update.tv_usec = cur_time.tv_usec;
|
|
} else if (next_update.tv_sec == cur_time.tv_sec
|
|
&& next_update.tv_usec < cur_time.tv_usec) {
|
|
next_update.tv_usec = cur_time.tv_usec;
|
|
}
|
|
|
|
init_time.tv_sec =
|
|
info.start_time.tv_sec + state->pv_sig_toffset.tv_sec;
|
|
init_time.tv_usec =
|
|
info.start_time.tv_usec +
|
|
state->pv_sig_toffset.tv_usec;
|
|
if (init_time.tv_usec >= 1000000) {
|
|
init_time.tv_sec++;
|
|
init_time.tv_usec -= 1000000;
|
|
}
|
|
if (init_time.tv_usec < 0) {
|
|
init_time.tv_sec--;
|
|
init_time.tv_usec += 1000000;
|
|
}
|
|
|
|
elapsed = cur_time.tv_sec - init_time.tv_sec;
|
|
elapsed +=
|
|
(cur_time.tv_usec - init_time.tv_usec) / 1000000.0;
|
|
|
|
if (ended)
|
|
since_last = -1;
|
|
|
|
if (state->pv_sig_newsize) {
|
|
state->pv_sig_newsize = 0;
|
|
pv_screensize(&(state->width), &(state->height));
|
|
}
|
|
|
|
pv_display(state, elapsed, since_last, total_written);
|
|
|
|
since_last = 0;
|
|
}
|
|
|
|
if (!state->numeric)
|
|
write(STDERR_FILENO, "\n", 1);
|
|
|
|
if (state->pv_sig_abort)
|
|
state->exit_status |= 32;
|
|
|
|
return state->exit_status;
|
|
}
|
|
|
|
|
|
/*
|
|
* Watch the progress of all file descriptors in process state->watch_pid
|
|
* and show details about the transfers on standard error according to the
|
|
* given options.
|
|
*
|
|
* Returns nonzero on error.
|
|
*/
|
|
int pv_watchpid_loop(pvstate_t state)
|
|
{
|
|
struct pvstate_s state_copy;
|
|
const char *original_format_string;
|
|
char new_format_string[512] = { 0, };
|
|
struct pvwatchfd_s *info_array = NULL;
|
|
struct pvstate_s *state_array = NULL;
|
|
int array_length = 0;
|
|
int fd_to_idx[FD_SETSIZE] = { 0, };
|
|
struct timeval next_update, cur_time;
|
|
int idx;
|
|
int prev_displayed_lines, blank_lines;
|
|
int first_pass = 1;
|
|
|
|
/*
|
|
* Make sure the process exists first, so we can give an error if
|
|
* it's not there at the start.
|
|
*/
|
|
if (kill(state->watch_pid, 0) != 0) {
|
|
pv_error(state, "%s %u: %s",
|
|
_("pid"), state->watch_pid, strerror(errno));
|
|
state->exit_status |= 2;
|
|
return 2;
|
|
}
|
|
|
|
/*
|
|
* Make a copy of our state, ready to change in preparation for
|
|
* duplication.
|
|
*/
|
|
memcpy(&state_copy, state, sizeof(state_copy));
|
|
|
|
/*
|
|
* Make sure there's a format string, and then insert %N into it if
|
|
* it's not present.
|
|
*/
|
|
original_format_string =
|
|
state->format_string ? state->format_string : state->
|
|
default_format;
|
|
if (NULL == strstr(original_format_string, "%N")) {
|
|
#ifdef HAVE_SNPRINTF
|
|
snprintf(new_format_string, sizeof(new_format_string) - 1,
|
|
#else
|
|
sprintf(new_format_string,
|
|
#endif
|
|
"%%N %s", original_format_string);
|
|
} else {
|
|
#ifdef HAVE_SNPRINTF
|
|
snprintf(new_format_string, sizeof(new_format_string) - 1,
|
|
#else
|
|
sprintf(new_format_string,
|
|
#endif
|
|
"%s", original_format_string);
|
|
}
|
|
state_copy.format_string = NULL;
|
|
#ifdef HAVE_SNPRINTF
|
|
snprintf(state_copy.default_format,
|
|
sizeof(state_copy.default_format) - 1,
|
|
#else
|
|
sprintf(state_copy.default_format,
|
|
#endif
|
|
"%.510s", new_format_string);
|
|
|
|
/*
|
|
* Get things ready for the main loop.
|
|
*/
|
|
|
|
gettimeofday(&cur_time, NULL);
|
|
|
|
next_update.tv_sec = cur_time.tv_sec;
|
|
next_update.tv_usec = cur_time.tv_usec;
|
|
pv_timeval_add_usec(&next_update,
|
|
(long) (1000000.0 * state->interval));
|
|
|
|
for (idx = 0; idx < FD_SETSIZE; idx++) {
|
|
fd_to_idx[idx] = -1;
|
|
}
|
|
|
|
prev_displayed_lines = 0;
|
|
|
|
while (1) {
|
|
int rc, fd, displayed_lines;
|
|
|
|
if (state->pv_sig_abort)
|
|
break;
|
|
|
|
gettimeofday(&cur_time, NULL);
|
|
|
|
if (kill(state->watch_pid, 0) != 0) {
|
|
if (first_pass) {
|
|
pv_error(state, "%s %u: %s",
|
|
_("pid"), state->watch_pid,
|
|
strerror(errno));
|
|
state->exit_status |= 2;
|
|
if (NULL != info_array)
|
|
free(info_array);
|
|
if (NULL != state_array)
|
|
free(state_array);
|
|
return 2;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ((cur_time.tv_sec < next_update.tv_sec)
|
|
|| (cur_time.tv_sec == next_update.tv_sec
|
|
&& cur_time.tv_usec < next_update.tv_usec)) {
|
|
struct timeval tv;
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 50000;
|
|
select(0, NULL, NULL, NULL, &tv);
|
|
continue;
|
|
}
|
|
|
|
pv_timeval_add_usec(&next_update,
|
|
(long) (1000000.0 * state->interval));
|
|
|
|
if (next_update.tv_sec < cur_time.tv_sec) {
|
|
next_update.tv_sec = cur_time.tv_sec;
|
|
next_update.tv_usec = cur_time.tv_usec;
|
|
} else if (next_update.tv_sec == cur_time.tv_sec
|
|
&& next_update.tv_usec < cur_time.tv_usec) {
|
|
next_update.tv_usec = cur_time.tv_usec;
|
|
}
|
|
|
|
if (state->pv_sig_newsize) {
|
|
state->pv_sig_newsize = 0;
|
|
pv_screensize(&(state->width), &(state->height));
|
|
for (idx = 0; idx < array_length; idx++) {
|
|
state_array[idx].width = state->width;
|
|
state_array[idx].height = state->height;
|
|
pv_watchpid_setname(state,
|
|
&(info_array[idx]));
|
|
state_array[idx].reparse_display = 1;
|
|
}
|
|
}
|
|
|
|
rc = pv_watchpid_scanfds(state, &state_copy,
|
|
state->watch_pid, &array_length,
|
|
&info_array, &state_array,
|
|
fd_to_idx);
|
|
if (rc != 0) {
|
|
if (first_pass) {
|
|
pv_error(state, "%s %u: %s",
|
|
_("pid"), state->watch_pid,
|
|
strerror(errno));
|
|
state->exit_status |= 2;
|
|
if (NULL != info_array)
|
|
free(info_array);
|
|
if (NULL != state_array)
|
|
free(state_array);
|
|
return 2;
|
|
}
|
|
break;
|
|
}
|
|
|
|
first_pass = 0;
|
|
displayed_lines = 0;
|
|
|
|
for (fd = 0; fd < FD_SETSIZE; fd++) {
|
|
long long position_now, since_last;
|
|
struct timeval init_time;
|
|
long double elapsed;
|
|
|
|
if (displayed_lines >= (int) (state->height))
|
|
break;
|
|
|
|
idx = fd_to_idx[fd];
|
|
|
|
if (idx < 0)
|
|
continue;
|
|
|
|
if (info_array[idx].watch_fd < 0) {
|
|
/*
|
|
* Non-displayable fd - just remove if
|
|
* changed
|
|
*/
|
|
if (pv_watchfd_changed(&(info_array[idx]))) {
|
|
fd_to_idx[fd] = -1;
|
|
info_array[idx].watch_pid = 0;
|
|
debug("%s %d: %s", "fd", fd,
|
|
"removing");
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Displayable fd - display, or remove if changed
|
|
*/
|
|
|
|
position_now =
|
|
pv_watchfd_position(&(info_array[idx]));
|
|
|
|
if (position_now < 0) {
|
|
fd_to_idx[fd] = -1;
|
|
info_array[idx].watch_pid = 0;
|
|
debug("%s %d: %s", "fd", fd, "removing");
|
|
continue;
|
|
}
|
|
|
|
since_last =
|
|
position_now - info_array[idx].position;
|
|
info_array[idx].position = position_now;
|
|
|
|
init_time.tv_sec =
|
|
info_array[idx].start_time.tv_sec +
|
|
state->pv_sig_toffset.tv_sec;
|
|
init_time.tv_usec =
|
|
info_array[idx].start_time.tv_usec +
|
|
state->pv_sig_toffset.tv_usec;
|
|
if (init_time.tv_usec >= 1000000) {
|
|
init_time.tv_sec++;
|
|
init_time.tv_usec -= 1000000;
|
|
}
|
|
if (init_time.tv_usec < 0) {
|
|
init_time.tv_sec--;
|
|
init_time.tv_usec += 1000000;
|
|
}
|
|
|
|
elapsed = cur_time.tv_sec - init_time.tv_sec;
|
|
elapsed +=
|
|
(cur_time.tv_usec -
|
|
init_time.tv_usec) / 1000000.0;
|
|
|
|
if (displayed_lines > 0) {
|
|
debug("%s", "adding newline");
|
|
write(STDERR_FILENO, "\n", 1);
|
|
}
|
|
|
|
debug("%s %d [%d]: %Lf / %Ld / %Ld", "fd", fd, idx,
|
|
elapsed, since_last, position_now);
|
|
|
|
pv_display(&(state_array[idx]), elapsed,
|
|
since_last, position_now);
|
|
displayed_lines++;
|
|
}
|
|
|
|
/*
|
|
* Write blank lines if we're writing fewer lines than last
|
|
* time.
|
|
*/
|
|
blank_lines = prev_displayed_lines - displayed_lines;
|
|
prev_displayed_lines = displayed_lines;
|
|
|
|
if (blank_lines > 0)
|
|
debug("%s: %d", "adding blank lines", blank_lines);
|
|
|
|
while (blank_lines > 0) {
|
|
unsigned int x;
|
|
if (displayed_lines > 0)
|
|
write(STDERR_FILENO, "\n", 1);
|
|
for (x = 0; x < state->width; x++)
|
|
write(STDERR_FILENO, " ", 1);
|
|
write(STDERR_FILENO, "\r", 1);
|
|
blank_lines--;
|
|
displayed_lines++;
|
|
}
|
|
|
|
debug("%s: %d", "displayed lines", displayed_lines);
|
|
|
|
while (displayed_lines > 1) {
|
|
write(STDERR_FILENO, "\033[A", 3);
|
|
displayed_lines--;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Clean up our displayed lines on exit.
|
|
*/
|
|
blank_lines = prev_displayed_lines;
|
|
while (blank_lines > 0) {
|
|
unsigned int x;
|
|
for (x = 0; x < state->width; x++)
|
|
write(STDERR_FILENO, " ", 1);
|
|
write(STDERR_FILENO, "\r", 1);
|
|
blank_lines--;
|
|
if (blank_lines > 0)
|
|
write(STDERR_FILENO, "\n", 1);
|
|
}
|
|
while (prev_displayed_lines > 1) {
|
|
write(STDERR_FILENO, "\033[A", 3);
|
|
prev_displayed_lines--;
|
|
}
|
|
|
|
if (NULL != info_array)
|
|
free(info_array);
|
|
if (NULL != state_array)
|
|
free(state_array);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* EOF */
|