summaryrefslogtreecommitdiff
path: root/src/pv/loop.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pv/loop.c')
-rw-r--r--src/pv/loop.c287
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 */