diff options
author | Lorry <lorry@roadtrain.codethink.co.uk> | 2012-08-15 09:31:11 +0100 |
---|---|---|
committer | Lorry <lorry@roadtrain.codethink.co.uk> | 2012-08-15 09:31:11 +0100 |
commit | d0dc3f5c30ca0b8350b48ba032a65681bfa20bdb (patch) | |
tree | 17ada9ef2482441523576855dce14785e40d96a3 /src/pv/loop.c | |
download | pv-d0dc3f5c30ca0b8350b48ba032a65681bfa20bdb.tar.gz |
Tarball conversion
Diffstat (limited to 'src/pv/loop.c')
-rw-r--r-- | src/pv/loop.c | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/src/pv/loop.c b/src/pv/loop.c new file mode 100644 index 0000000..8812eff --- /dev/null +++ b/src/pv/loop.c @@ -0,0 +1,287 @@ +/* + * Main program entry point - read the command line options, then perform + * the appropriate actions. + * + * Copyright 2012 Andrew Wood, distributed under the Artistic License 2.0. + */ + +#include "options.h" +#include "pv.h" + +#define _GNU_SOURCE 1 +#include <limits.h> + +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define RATE_GRANULARITY 100000 /* usec between -L rate chunks */ + +extern struct timeval pv_sig_toffset; +extern sig_atomic_t pv_sig_newsize; +extern sig_atomic_t pv_sig_abort; + + +/* + * 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(opts_t opts) +{ + 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; + 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(opts); + + eof_in = 0; + eof_out = 0; + total_written = 0; + since_last = 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; + pv_timeval_add_usec(&next_update, + (long) (1000000.0 * opts->interval)); + + next_ratecheck.tv_sec = start_time.tv_sec; + next_ratecheck.tv_usec = start_time.tv_usec; + + target = 0; + cansend = 0; + final_update = 0; + n = 0; + + fd = pv_next_file(opts, n, -1); + if (fd < 0) { + return opts->exit_status; + } + + if (fstat64(fd, &sb) == 0) { + pv_set_buffer_size(sb.st_blksize * 32, 0); + } + + if (opts->buffer_size > 0) { + pv_set_buffer_size(opts->buffer_size, 1); + } + + while ((!(eof_in && eof_out)) || (!final_update)) { + + if (pv_sig_abort) + break; + + if (opts->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) (opts->rate_limit)) / + (long double) (1000000 / + RATE_GRANULARITY); + pv_timeval_add_usec(&next_ratecheck, + RATE_GRANULARITY); + } + cansend = target; + } + + written = + pv_transfer(opts, fd, &eof_in, &eof_out, cansend, + &lineswritten); + if (written < 0) + return opts->exit_status; + + if (opts->linemode) { + since_last += lineswritten; + total_written += lineswritten; + if (opts->rate_limit > 0) + target -= lineswritten; + } else { + since_last += written; + total_written += written; + if (opts->rate_limit > 0) + target -= written; + } + + if (eof_in && eof_out && n < (opts->argc - 1)) { + n++; + fd = pv_next_file(opts, n, fd); + if (fd < 0) + return opts->exit_status; + eof_in = 0; + eof_out = 0; + } + + gettimeofday(&cur_time, NULL); + + if (eof_in && eof_out) { + final_update = 1; + next_update.tv_sec = cur_time.tv_sec - 1; + } + + if (opts->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 (opts->wait) { + if (opts->linemode) { + if (lineswritten < 1) + continue; + } else { + if (written < 1) + continue; + } + + opts->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); + pv_sig_toffset.tv_sec = 0; + 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 * + opts->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 * opts->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 + pv_sig_toffset.tv_sec; + init_time.tv_usec = + start_time.tv_usec + 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 (pv_sig_newsize) { + pv_sig_newsize = 0; + pv_screensize(opts); + } + + pv_display(opts, elapsed, since_last, total_written); + + since_last = 0; + } + + if (opts->cursor) { + pv_crs_fini(opts); + } else { + if ((!opts->numeric) && (!opts->no_op)) + write(STDERR_FILENO, "\n", 1); + } + + /* + * Free up the buffers used by the display and data transfer + * routines. + */ + pv_display(0, 0, 0, 0); + pv_transfer(0, -1, 0, 0, 0, NULL); + + if (pv_sig_abort) + opts->exit_status |= 32; + + return opts->exit_status; +} + +/* EOF */ |