Initial commit based on v1.6.6
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Replacement getopt function's header file. Include this AFTER config.h.
|
||||
*/
|
||||
|
||||
#ifndef _LIBRARY_GETOPT_H
|
||||
#define _LIBRARY_GETOPT_H 1
|
||||
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_GETOPT
|
||||
|
||||
int minigetopt(int, char **, char *);
|
||||
extern char *minioptarg;
|
||||
extern int minioptind, miniopterr, minioptopt;
|
||||
|
||||
#define getopt minigetopt /* Flawfinder: ignore */
|
||||
#define optarg minioptarg
|
||||
#define optind minioptind
|
||||
#define opterr miniopterr
|
||||
#define optopt minioptopt
|
||||
|
||||
#endif /* !HAVE_GETOPT */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBRARY_GETOPT_H */
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Replacement gettext library header file. Include this within config.h,
|
||||
* like this:
|
||||
*
|
||||
* #ifdef ENABLE_NLS
|
||||
* # include "library/gettext.h"
|
||||
* #else
|
||||
* # define _(String) (String)
|
||||
* # define N_(String) (String)
|
||||
* #endif
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LIBRARY_GETTEXT_H
|
||||
#define _LIBRARY_GETTEXT_H 1
|
||||
|
||||
#ifdef HAVE_GETTEXT
|
||||
# ifdef HAVE_LIBINTL_H
|
||||
# include <libintl.h>
|
||||
# endif
|
||||
# ifdef HAVE_LOCALE_H
|
||||
# include <locale.h>
|
||||
# endif
|
||||
# define _(String) gettext (String)
|
||||
# define N_(String) (String)
|
||||
#else
|
||||
# define _(String) minigettext (String)
|
||||
# define N_(String) (String)
|
||||
# define setlocale minisetlocale
|
||||
# define bindtextdomain minibindtextdomain
|
||||
# define textdomain minitextdomain
|
||||
# ifndef LC_ALL
|
||||
# define LC_ALL ""
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
char *minisetlocale(char *, char *);
|
||||
char *minibindtextdomain(char *, char *);
|
||||
char *minitextdomain(char *);
|
||||
char *minigettext(char *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBRARY_GETTEXT_H */
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Global program option structure and the parsing function prototype.
|
||||
*/
|
||||
|
||||
#ifndef _OPTIONS_H
|
||||
#define _OPTIONS_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct opts_s;
|
||||
typedef struct opts_s *opts_t;
|
||||
|
||||
struct opts_s { /* structure describing run-time options */
|
||||
char *program_name; /* name the program is running as */
|
||||
unsigned char do_nothing; /* exit-without-doing-anything flag */
|
||||
unsigned char progress; /* progress bar flag */
|
||||
unsigned char timer; /* timer flag */
|
||||
unsigned char eta; /* ETA flag */
|
||||
unsigned char fineta; /* absolute ETA flag */
|
||||
unsigned char rate; /* rate counter flag */
|
||||
unsigned char average_rate; /* average rate counter flag */
|
||||
unsigned char bytes; /* bytes transferred flag */
|
||||
unsigned char bufpercent; /* transfer buffer percentage flag */
|
||||
unsigned int lastwritten; /* show N bytes last written */
|
||||
unsigned char force; /* force-if-not-terminal flag */
|
||||
unsigned char cursor; /* whether to use cursor positioning */
|
||||
unsigned char numeric; /* numeric output only */
|
||||
unsigned char wait; /* wait for transfer before display */
|
||||
unsigned char linemode; /* count lines instead of bytes */
|
||||
unsigned char null; /* lines are null-terminated */
|
||||
unsigned char no_op; /* do nothing other than pipe data */
|
||||
unsigned long long rate_limit; /* rate limit, in bytes per second */
|
||||
unsigned long long buffer_size;/* buffer size, in bytes (0=default) */
|
||||
unsigned int remote; /* PID of pv to update settings of */
|
||||
unsigned long long size; /* total size of data */
|
||||
unsigned char no_splice; /* flag set if never to use splice */
|
||||
unsigned char skip_errors; /* skip read errors flag */
|
||||
unsigned char stop_at_size; /* set if we stop at "size" bytes */
|
||||
double interval; /* interval between updates */
|
||||
double delay_start; /* delay before first display */
|
||||
unsigned int watch_pid; /* process to watch fds of */
|
||||
int watch_fd; /* fd to watch */
|
||||
unsigned int width; /* screen width */
|
||||
unsigned int height; /* screen height */
|
||||
char *name; /* process name, if any */
|
||||
char *format; /* output format, if any */
|
||||
char *pidfile; /* PID file, if any */
|
||||
int argc; /* number of non-option arguments */
|
||||
char **argv; /* array of non-option arguments */
|
||||
};
|
||||
|
||||
extern opts_t opts_parse(int, char **);
|
||||
extern void opts_free(opts_t);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _OPTIONS_H */
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Functions internal to the PV library.
|
||||
*/
|
||||
|
||||
#ifndef _PV_INTERNAL_H
|
||||
#define _PV_INTERNAL_H 1
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef _PV_H
|
||||
#include "pv.h"
|
||||
#endif /* _PV_H */
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
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
|
||||
|
||||
#define RATE_GRANULARITY 100000 /* usec between -L rate chunks */
|
||||
#define REMOTE_INTERVAL 100000 /* usec between checks for -R */
|
||||
#define BUFFER_SIZE 409600 /* default transfer buffer size */
|
||||
#define BUFFER_SIZE_MAX 524288 /* max auto transfer buffer size */
|
||||
#define MAX_READ_AT_ONCE 524288 /* max to read() in one go */
|
||||
#define MAX_WRITE_AT_ONCE 524288 /* max to write() in one go */
|
||||
#define TRANSFER_READ_TIMEOUT 90000 /* usec to time reads out at */
|
||||
#define TRANSFER_WRITE_TIMEOUT 900000 /* usec to time writes out at */
|
||||
|
||||
#define MAXIMISE_BUFFER_FILL 1
|
||||
|
||||
|
||||
/*
|
||||
* Structure for holding PV internal state. Opaque outside the PV library.
|
||||
*/
|
||||
struct pvstate_s {
|
||||
/***************
|
||||
* Input files *
|
||||
***************/
|
||||
int input_file_count; /* number of input files */
|
||||
const char **input_files; /* input files (0=first) */
|
||||
|
||||
/*******************
|
||||
* Program control *
|
||||
*******************/
|
||||
unsigned char force; /* display even if not on terminal */
|
||||
unsigned char cursor; /* use cursor positioning */
|
||||
unsigned char numeric; /* numeric output only */
|
||||
unsigned char wait; /* wait for data before display */
|
||||
unsigned char linemode; /* count lines instead of bytes */
|
||||
unsigned char null; /* lines are null-terminated */
|
||||
unsigned char no_op; /* do nothing other than pipe data */
|
||||
unsigned char skip_errors; /* skip read errors flag */
|
||||
unsigned char stop_at_size; /* set if we stop at "size" bytes */
|
||||
unsigned char no_splice; /* never use splice() */
|
||||
unsigned long long rate_limit; /* rate limit, in bytes per second */
|
||||
unsigned long long target_buffer_size; /* buffer size (0=default) */
|
||||
unsigned long long size; /* total size of data */
|
||||
double interval; /* interval between updates */
|
||||
double delay_start; /* delay before first display */
|
||||
unsigned int watch_pid; /* process to watch fds of */
|
||||
int watch_fd; /* fd to watch */
|
||||
unsigned int width; /* screen width */
|
||||
unsigned int height; /* screen height */
|
||||
const char *name; /* display name */
|
||||
char default_format[512]; /* default format string */
|
||||
const char *format_string; /* output format string */
|
||||
|
||||
/******************
|
||||
* Program status *
|
||||
******************/
|
||||
const char *program_name; /* program name for error reporting */
|
||||
const char *current_file; /* current file being read */
|
||||
int exit_status; /* exit status to give (0=OK) */
|
||||
|
||||
/*******************
|
||||
* Signal handling *
|
||||
*******************/
|
||||
int pv_sig_old_stderr; /* see pv_sig_ttou() */
|
||||
struct timeval pv_sig_tstp_time; /* see pv_sig_tstp() / __cont() */
|
||||
struct timeval pv_sig_toffset; /* total time spent stopped */
|
||||
volatile sig_atomic_t pv_sig_newsize; /* whether we need to get term size again */
|
||||
volatile sig_atomic_t pv_sig_abort; /* whether we need to abort right now */
|
||||
volatile sig_atomic_t reparse_display; /* whether to re-check format string */
|
||||
struct sigaction pv_sig_old_sigpipe;
|
||||
struct sigaction pv_sig_old_sigttou;
|
||||
struct sigaction pv_sig_old_sigtstp;
|
||||
struct sigaction pv_sig_old_sigcont;
|
||||
struct sigaction pv_sig_old_sigwinch;
|
||||
struct sigaction pv_sig_old_sigint;
|
||||
struct sigaction pv_sig_old_sighup;
|
||||
struct sigaction pv_sig_old_sigterm;
|
||||
|
||||
/*****************
|
||||
* Display state *
|
||||
*****************/
|
||||
long percentage;
|
||||
long double prev_elapsed_sec;
|
||||
long double prev_rate;
|
||||
long double prev_trans;
|
||||
unsigned long long initial_offset;
|
||||
char *display_buffer;
|
||||
long display_buffer_size;
|
||||
int lastoutput_length; /* number of last-output bytes to show */
|
||||
unsigned char lastoutput_buffer[256];
|
||||
int prev_width; /* screen width last time we were called */
|
||||
int prev_length; /* length of last string we output */
|
||||
char str_name[512];
|
||||
char str_transferred[128];
|
||||
char str_bufpercent[128];
|
||||
char str_timer[128];
|
||||
char str_rate[128];
|
||||
char str_average_rate[128];
|
||||
char str_progress[1024];
|
||||
char str_lastoutput[512];
|
||||
char str_eta[128];
|
||||
char str_fineta[128];
|
||||
unsigned long components_used; /* bitmask of components used */
|
||||
struct {
|
||||
const char *string;
|
||||
int length;
|
||||
} format[100];
|
||||
unsigned char display_visible; /* set once anything written to terminal */
|
||||
|
||||
/********************
|
||||
* Cursor/IPC state *
|
||||
********************/
|
||||
#ifdef HAVE_IPC
|
||||
int crs_shmid; /* ID of our shared memory segment */
|
||||
int crs_pvcount; /* number of `pv' processes in total */
|
||||
int crs_pvmax; /* highest number of `pv's seen */
|
||||
int *crs_y_top; /* pointer to Y coord of topmost `pv' */
|
||||
int crs_y_lastread; /* last value of _y_top seen */
|
||||
int crs_y_offset; /* our Y offset from this top position */
|
||||
int crs_needreinit; /* set if we need to reinit cursor pos */
|
||||
int crs_noipc; /* set if we can't use IPC */
|
||||
#endif /* HAVE_IPC */
|
||||
int crs_lock_fd; /* fd of lockfile, -1 if none open */
|
||||
char crs_lock_file[1024];
|
||||
int crs_y_start; /* our initial Y coordinate */
|
||||
|
||||
/*******************
|
||||
* Transfer state *
|
||||
*******************/
|
||||
/*
|
||||
* The transfer buffer is used for moving data from the input files
|
||||
* to the output when splice() is not available.
|
||||
*
|
||||
* If buffer_size is smaller than pv__target_bufsize, then
|
||||
* pv_transfer will try to reallocate transfer_buffer to make
|
||||
* buffer_size equal to pv__target_bufsize.
|
||||
*
|
||||
* Data from the input files is read into the buffer; read_position
|
||||
* is the offset in the buffer that we've read data up to.
|
||||
*
|
||||
* Data is written to the output from the buffer, and write_position
|
||||
* is the offset in the buffer that we've written data up to. It
|
||||
* will always be less than or equal to read_position.
|
||||
*/
|
||||
unsigned char *transfer_buffer; /* data transfer buffer */
|
||||
unsigned long long buffer_size; /* size of buffer */
|
||||
unsigned long read_position; /* amount of data in buffer */
|
||||
unsigned long write_position; /* buffered data written */
|
||||
|
||||
/*
|
||||
* While reading from a file descriptor we keep track of how many
|
||||
* times in a row we've seen errors (read_errors_in_a_row), and
|
||||
* whether or not we have put a warning on stderr about read errors
|
||||
* on this fd (read_error_warning_shown).
|
||||
*
|
||||
* Whenever the active file descriptor changes from
|
||||
* last_read_skip_fd, we reset read_errors_in_a_row and
|
||||
* read_error_warning_shown to 0 for the new file descriptor and set
|
||||
* last_read_skip_fd to the new fd number.
|
||||
*
|
||||
* This way, we're treating each input file separately.
|
||||
*/
|
||||
int last_read_skip_fd;
|
||||
unsigned long read_errors_in_a_row;
|
||||
int read_error_warning_shown;
|
||||
#ifdef HAVE_SPLICE
|
||||
/*
|
||||
* These variables are used to keep track of whether splice() was
|
||||
* used; splice_failed_fd is the file descriptor that splice() last
|
||||
* failed on, so that we don't keep trying to use it on an fd that
|
||||
* doesn't support it, and splice_used is set to 1 if splice() was
|
||||
* used this time within pv_transfer().
|
||||
*/
|
||||
int splice_failed_fd;
|
||||
int splice_used;
|
||||
#endif
|
||||
long to_write; /* max to write this time around */
|
||||
long written; /* bytes sent to stdout this time */
|
||||
};
|
||||
|
||||
|
||||
struct pvwatchfd_s {
|
||||
unsigned int watch_pid; /* PID to watch */
|
||||
int watch_fd; /* fd to watch, -1 = not displayed */
|
||||
char file_fdinfo[4096]; /* path to /proc fdinfo file */
|
||||
char file_fd[4096]; /* path to /proc fd symlink */
|
||||
char file_fdpath[4096]; /* path to file that was opened */
|
||||
char display_name[512]; /* name to show on progress bar */
|
||||
struct stat64 sb_fd; /* stat of fd symlink */
|
||||
struct stat64 sb_fd_link; /* lstat of fd symlink */
|
||||
unsigned long long size; /* size of whole file, 0 if unknown */
|
||||
long long position; /* position last seen at */
|
||||
struct timeval start_time; /* time we started watching the fd */
|
||||
};
|
||||
typedef struct pvwatchfd_s *pvwatchfd_t;
|
||||
|
||||
void pv_error(pvstate_t, char *, ...);
|
||||
|
||||
int pv_main_loop(pvstate_t);
|
||||
void pv_display(pvstate_t, long double, long long, long long);
|
||||
long pv_transfer(pvstate_t, int, int *, int *, unsigned long long, long *);
|
||||
void pv_set_buffer_size(unsigned long long, int);
|
||||
int pv_next_file(pvstate_t, int, int);
|
||||
|
||||
void pv_crs_fini(pvstate_t);
|
||||
void pv_crs_init(pvstate_t);
|
||||
void pv_crs_update(pvstate_t, char *);
|
||||
#ifdef HAVE_IPC
|
||||
void pv_crs_needreinit(pvstate_t);
|
||||
#endif
|
||||
|
||||
void pv_sig_allowpause(void);
|
||||
void pv_sig_checkbg(void);
|
||||
void pv_sig_nopause(void);
|
||||
|
||||
void pv_remote_init(pvstate_t);
|
||||
void pv_remote_check(pvstate_t);
|
||||
void pv_remote_fini(pvstate_t);
|
||||
int pv_remote_set(pvstate_t);
|
||||
|
||||
int pv_watchfd_info(pvstate_t, pvwatchfd_t, int);
|
||||
int pv_watchfd_changed(pvwatchfd_t);
|
||||
long long pv_watchfd_position(pvwatchfd_t);
|
||||
int pv_watchpid_scanfds(pvstate_t, pvstate_t, unsigned int, int *, pvwatchfd_t *, pvstate_t *, int *);
|
||||
void pv_watchpid_setname(pvstate_t, pvwatchfd_t);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _PV_INTERNAL_H */
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Functions used across the program.
|
||||
*/
|
||||
|
||||
#ifndef _PV_H
|
||||
#define _PV_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Opaque structure for PV internal state.
|
||||
*/
|
||||
struct pvstate_s;
|
||||
typedef struct pvstate_s *pvstate_t;
|
||||
|
||||
/*
|
||||
* Valid number types for pv_getnum_check().
|
||||
*/
|
||||
typedef enum {
|
||||
PV_NUMTYPE_INTEGER,
|
||||
PV_NUMTYPE_DOUBLE
|
||||
} pv_numtype_t;
|
||||
|
||||
|
||||
/*
|
||||
* Simple string functions for processing numbers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Return the given string converted to a double.
|
||||
*/
|
||||
extern double pv_getnum_d(const char *);
|
||||
|
||||
/*
|
||||
* Return the given string converted to an integer.
|
||||
*/
|
||||
extern int pv_getnum_i(const char *);
|
||||
|
||||
/*
|
||||
* Return the given string converted to a long long.
|
||||
*/
|
||||
extern long long pv_getnum_ll(const char *);
|
||||
|
||||
/*
|
||||
* Return zero if the given string is a number of the given type. NB an
|
||||
* integer is both a valid integer and a valid double.
|
||||
*/
|
||||
extern int pv_getnum_check(const char *, pv_numtype_t);
|
||||
|
||||
/*
|
||||
* Main PV functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Create a new state structure, and return it, or 0 (NULL) on error.
|
||||
*/
|
||||
extern pvstate_t pv_state_alloc(const char *);
|
||||
|
||||
/*
|
||||
* Set the formatting string, given a set of old-style formatting options.
|
||||
*/
|
||||
extern void pv_state_set_format(pvstate_t state, unsigned char progress,
|
||||
unsigned char timer, unsigned char eta,
|
||||
unsigned char fineta, unsigned char rate,
|
||||
unsigned char average_rate, unsigned char bytes,
|
||||
unsigned char bufpercent,
|
||||
unsigned int lastwritten,
|
||||
const char *name);
|
||||
|
||||
/*
|
||||
* Set the various options.
|
||||
*/
|
||||
extern void pv_state_force_set(pvstate_t, unsigned char);
|
||||
extern void pv_state_cursor_set(pvstate_t, unsigned char);
|
||||
extern void pv_state_numeric_set(pvstate_t, unsigned char);
|
||||
extern void pv_state_wait_set(pvstate_t, unsigned char);
|
||||
extern void pv_state_delay_start_set(pvstate_t, double);
|
||||
extern void pv_state_linemode_set(pvstate_t, unsigned char);
|
||||
extern void pv_state_null_set(pvstate_t, unsigned char);
|
||||
extern void pv_state_no_op_set(pvstate_t, unsigned char);
|
||||
extern void pv_state_skip_errors_set(pvstate_t, unsigned char);
|
||||
extern void pv_state_stop_at_size_set(pvstate_t, unsigned char);
|
||||
extern void pv_state_rate_limit_set(pvstate_t, unsigned long long);
|
||||
extern void pv_state_target_buffer_size_set(pvstate_t, unsigned long long);
|
||||
extern void pv_state_no_splice_set(pvstate_t, unsigned char);
|
||||
extern void pv_state_size_set(pvstate_t, unsigned long long);
|
||||
extern void pv_state_interval_set(pvstate_t, double);
|
||||
extern void pv_state_width_set(pvstate_t, unsigned int);
|
||||
extern void pv_state_height_set(pvstate_t, unsigned int);
|
||||
extern void pv_state_name_set(pvstate_t, const char *);
|
||||
extern void pv_state_format_string_set(pvstate_t, const char *);
|
||||
extern void pv_state_watch_pid_set(pvstate_t, unsigned int);
|
||||
extern void pv_state_watch_fd_set(pvstate_t, int);
|
||||
|
||||
extern void pv_state_inputfiles(pvstate_t, int, const char **);
|
||||
|
||||
/*
|
||||
* Work out the terminal size.
|
||||
*/
|
||||
extern void pv_screensize(unsigned int *width, unsigned int *height);
|
||||
|
||||
/*
|
||||
* Calculate the total size of all input files.
|
||||
*/
|
||||
extern unsigned long long pv_calc_total_size(pvstate_t);
|
||||
|
||||
/*
|
||||
* Set up signal handlers ready for running the main loop.
|
||||
*/
|
||||
extern void pv_sig_init(pvstate_t);
|
||||
|
||||
/*
|
||||
* Enter the main transfer loop, transferring all input files to the output.
|
||||
*/
|
||||
extern int pv_main_loop(pvstate_t);
|
||||
|
||||
/*
|
||||
* Watch the selected file descriptor of the selected process.
|
||||
*/
|
||||
extern int pv_watchfd_loop(pvstate_t);
|
||||
|
||||
/*
|
||||
* Watch the selected process.
|
||||
*/
|
||||
extern int pv_watchpid_loop(pvstate_t);
|
||||
|
||||
/*
|
||||
* Shut down signal handlers after running the main loop.
|
||||
*/
|
||||
extern void pv_sig_fini(pvstate_t);
|
||||
|
||||
/*
|
||||
* Free a state structure, after which it can no longer be used.
|
||||
*/
|
||||
extern void pv_state_free(pvstate_t);
|
||||
|
||||
|
||||
#ifdef ENABLE_DEBUGGING
|
||||
# if __STDC_VERSION__ < 199901L
|
||||
# if __GNUC__ >= 2
|
||||
# define __func__ __FUNCTION__
|
||||
# else
|
||||
# define __func__ "<unknown>"
|
||||
# endif
|
||||
# endif
|
||||
# define debug(x,...) debugging_output(__func__, __FILE__, __LINE__, x, __VA_ARGS__)
|
||||
#else
|
||||
# define debug(x,...) do { } while (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Output debugging information, if debugging is enabled.
|
||||
*/
|
||||
void debugging_output(const char *, const char *, int, const char *, ...);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _PV_H */
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Small reimplementation of getopt().
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef HAVE_GETOPT
|
||||
|
||||
char *minioptarg = NULL;
|
||||
int minioptind = 0;
|
||||
int miniopterr = 1;
|
||||
int minioptopt = 0;
|
||||
|
||||
|
||||
/*
|
||||
* Minimalist getopt() clone, which handles short options only and doesn't
|
||||
* permute argv[].
|
||||
*/
|
||||
int minigetopt(int argc, char **argv, char *optstring)
|
||||
{
|
||||
static int nextchar = 0;
|
||||
int optchar;
|
||||
int i;
|
||||
|
||||
if ((0 == minioptind) && (argc > 0))
|
||||
minioptind++;
|
||||
|
||||
if ((nextchar > 0) && (0 == argv[minioptind][nextchar])) {
|
||||
minioptind++;
|
||||
nextchar = 0;
|
||||
}
|
||||
|
||||
if (minioptind >= argc)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* End of options if arg doesn't start with "-"
|
||||
*/
|
||||
if (argv[minioptind][0] != '-')
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* End of options if arg is just "-"
|
||||
*/
|
||||
if (0 == argv[minioptind][1])
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* End of options if arg is "--", but don't include the "--" in the
|
||||
* non-option arguments
|
||||
*/
|
||||
if (('-' == argv[minioptind][1]) && (0 == argv[minioptind][2])) {
|
||||
minioptind++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 == nextchar)
|
||||
nextchar = 1;
|
||||
|
||||
optchar = argv[minioptind][nextchar++];
|
||||
|
||||
for (i = 0; optstring[i] != 0 && optstring[i] != optchar; i++) {
|
||||
}
|
||||
|
||||
if (0 == optstring[i]) {
|
||||
minioptopt = optchar;
|
||||
if (miniopterr)
|
||||
fprintf(stderr, "%s: invalid option -- %c\n",
|
||||
argv[0], optchar);
|
||||
return '?';
|
||||
}
|
||||
|
||||
if (optstring[i + 1] != ':') {
|
||||
minioptarg = NULL;
|
||||
return optchar;
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point we've got an option that takes an argument.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Next character isn't 0, so the argument is within this array
|
||||
* element (i.e. "-dFOO").
|
||||
*/
|
||||
if (argv[minioptind][nextchar] != 0) {
|
||||
minioptarg = &(argv[minioptind][nextchar]);
|
||||
nextchar = 0;
|
||||
minioptind++;
|
||||
return optchar;
|
||||
}
|
||||
|
||||
/*
|
||||
* Argument is in the next array element (i.e. "-d FOO").
|
||||
*/
|
||||
nextchar = 0;
|
||||
minioptind++;
|
||||
if (minioptind >= argc) {
|
||||
fprintf(stderr, "%s: option `-%c' requires an argument\n",
|
||||
argv[0], optchar);
|
||||
return ':';
|
||||
}
|
||||
minioptarg = argv[minioptind++];
|
||||
|
||||
return optchar;
|
||||
}
|
||||
|
||||
#endif /* HAVE_GETOPT */
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Very minimal (and stupid) implementation of gettext, with a fixed lookup
|
||||
* table.
|
||||
*
|
||||
* This library ONLY handles gettext(), and that only for the basic form (it
|
||||
* translates strings to other strings with no other modification, so %2$d
|
||||
* style constructs are not dealt with). The setlocale(), bindtextdomain(),
|
||||
* and textdomain() functions are ignored.
|
||||
*
|
||||
* To use this library, create a function that, given a language string,
|
||||
* returns a struct msg_table_s[] of msgid and msgstr pairs, with the end
|
||||
* of the table being marked by a NULL msgid. The po2table.sh script will do
|
||||
* this.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef HAVE_GETTEXT
|
||||
|
||||
struct msgtable_s {
|
||||
char *msgid;
|
||||
char *msgstr;
|
||||
};
|
||||
|
||||
#if ENABLE_NLS
|
||||
struct msgtable_s *minigettext__gettable(char *);
|
||||
#else /* ENABLE_NLS */
|
||||
struct msgtable_s *minigettext__gettable(char *a)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif /* ENABLE_NLS */
|
||||
|
||||
char *minisetlocale(char *a, char *b)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
char *minibindtextdomain(char *a, char *b)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
char *minitextdomain(char *a)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
char *minigettext(char *msgid)
|
||||
{
|
||||
static struct msgtable_s *table = NULL;
|
||||
static int tried_lang = 0;
|
||||
char *lang;
|
||||
int i;
|
||||
|
||||
if (NULL == msgid)
|
||||
return msgid;
|
||||
|
||||
if (0 == tried_lang) {
|
||||
lang = getenv("LANGUAGE");
|
||||
if (lang)
|
||||
table = minigettext__gettable(lang);
|
||||
|
||||
if (NULL == table) {
|
||||
lang = getenv("LANG");
|
||||
if (lang)
|
||||
table = minigettext__gettable(lang);
|
||||
}
|
||||
|
||||
if (NULL == table) {
|
||||
lang = getenv("LC_ALL");
|
||||
if (lang)
|
||||
table = minigettext__gettable(lang);
|
||||
}
|
||||
|
||||
if (NULL == table) {
|
||||
lang = getenv("LC_MESSAGES");
|
||||
if (lang)
|
||||
table = minigettext__gettable(lang);
|
||||
}
|
||||
|
||||
tried_lang = 1;
|
||||
}
|
||||
|
||||
if (NULL == table)
|
||||
return msgid;
|
||||
|
||||
for (i = 0; table[i].msgid; i++) {
|
||||
if (0 == strcmp(table[i].msgid, msgid)) {
|
||||
if (0 == table[i].msgstr)
|
||||
return msgid;
|
||||
if (0 == table[i].msgstr[0])
|
||||
return msgid;
|
||||
return table[i].msgstr;
|
||||
}
|
||||
}
|
||||
|
||||
return msgid;
|
||||
}
|
||||
|
||||
#endif /* HAVE_GETTEXT */
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Output debugging information.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#include "pv.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#ifdef ENABLE_DEBUGGING
|
||||
/*
|
||||
* Output debugging information to the file given in the DEBUG environment
|
||||
* variable, if it is defined.
|
||||
*/
|
||||
void debugging_output(const char *function, const char *file, int line,
|
||||
const char *format, ...)
|
||||
{
|
||||
static int tried_open = 0;
|
||||
static FILE *debugfptr = NULL;
|
||||
char *debugfile;
|
||||
va_list ap;
|
||||
time_t t;
|
||||
struct tm *tm;
|
||||
char tbuf[128];
|
||||
|
||||
if (0 == tried_open) {
|
||||
debugfile = getenv("DEBUG");
|
||||
if (NULL != debugfile)
|
||||
debugfptr = fopen(debugfile, "a");
|
||||
tried_open = 1;
|
||||
}
|
||||
|
||||
if (NULL == debugfptr)
|
||||
return;
|
||||
|
||||
time(&t);
|
||||
tm = localtime(&t);
|
||||
tbuf[0] = 0;
|
||||
strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", tm);
|
||||
|
||||
fprintf(debugfptr, "[%s] (%d) %s (%s:%d): ", tbuf, getpid(),
|
||||
function, file, line);
|
||||
|
||||
va_start(ap, format);
|
||||
vfprintf(debugfptr, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
fprintf(debugfptr, "\n");
|
||||
fflush(debugfptr);
|
||||
}
|
||||
|
||||
#else /* ! ENABLE_DEBUGGING */
|
||||
|
||||
/*
|
||||
* Stub debugging output function.
|
||||
*/
|
||||
void debugging_output(const char *function, const char *file, int line,
|
||||
const char *format, ...)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* ENABLE_DEBUGGING */
|
||||
|
||||
/* EOF */
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Output command-line help to stdout.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define N_(String) (String)
|
||||
|
||||
struct optdesc_s {
|
||||
char *optshort;
|
||||
char *optlong;
|
||||
char *param;
|
||||
char *description;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Display command-line help.
|
||||
*/
|
||||
void display_help(void)
|
||||
{
|
||||
struct optdesc_s optlist[] = {
|
||||
{"-p", "--progress", 0,
|
||||
N_("show progress bar")},
|
||||
{"-t", "--timer", 0,
|
||||
N_("show elapsed time")},
|
||||
{"-e", "--eta", 0,
|
||||
N_("show estimated time of arrival (completion)")},
|
||||
{"-I", "--fineta", 0,
|
||||
N_
|
||||
("show absolute estimated time of arrival (completion)")},
|
||||
{"-r", "--rate", 0,
|
||||
N_("show data transfer rate counter")},
|
||||
{"-a", "--average-rate", 0,
|
||||
N_("show data transfer average rate counter")},
|
||||
{"-b", "--bytes", 0,
|
||||
N_("show number of bytes transferred")},
|
||||
{"-T", "--buffer-percent", 0,
|
||||
N_("show percentage of transfer buffer in use")},
|
||||
{"-A", "--last-written", _("NUM"),
|
||||
N_("show NUM bytes last written")},
|
||||
{"-F", "--format", N_("FORMAT"),
|
||||
N_("set output format to FORMAT")},
|
||||
{"-n", "--numeric", 0,
|
||||
N_("output percentages, not visual information")},
|
||||
{"-q", "--quiet", 0,
|
||||
N_("do not output any transfer information at all")},
|
||||
{"", 0, 0, 0},
|
||||
{"-W", "--wait", 0,
|
||||
N_("display nothing until first byte transferred")},
|
||||
{"-D", "--delay-start", N_("SEC"),
|
||||
N_("display nothing until SEC seconds have passed")},
|
||||
{"-s", "--size", N_("SIZE"),
|
||||
N_("set estimated data size to SIZE bytes")},
|
||||
{"-l", "--line-mode", 0,
|
||||
N_("count lines instead of bytes")},
|
||||
{"-0", "--null", 0,
|
||||
N_("lines are null-terminated")},
|
||||
{"-i", "--interval", N_("SEC"),
|
||||
N_("update every SEC seconds")},
|
||||
{"-w", "--width", N_("WIDTH"),
|
||||
N_("assume terminal is WIDTH characters wide")},
|
||||
{"-H", "--height", N_("HEIGHT"),
|
||||
N_("assume terminal is HEIGHT rows high")},
|
||||
{"-N", "--name", N_("NAME"),
|
||||
N_("prefix visual information with NAME")},
|
||||
{"-f", "--force", 0,
|
||||
N_("output even if standard error is not a terminal")},
|
||||
{"-c", "--cursor", 0,
|
||||
N_("use cursor positioning escape sequences")},
|
||||
{"", 0, 0, 0},
|
||||
{"-L", "--rate-limit", N_("RATE"),
|
||||
N_("limit transfer to RATE bytes per second")},
|
||||
{"-B", "--buffer-size", N_("BYTES"),
|
||||
N_("use a buffer size of BYTES")},
|
||||
{"-C", "--no-splice", 0,
|
||||
N_("never use splice(), always use read/write")},
|
||||
{"-E", "--skip-errors", 0,
|
||||
N_("skip read errors in input")},
|
||||
{"-S", "--stop-at-size", 0,
|
||||
N_("stop after --size bytes have been transferred")},
|
||||
#ifdef HAVE_IPC
|
||||
{"-R", "--remote", N_("PID"),
|
||||
N_("update settings of process PID")},
|
||||
#endif /* HAVE_IPC */
|
||||
{"", 0, 0, 0},
|
||||
{"-P", "--pidfile", N_("FILE"),
|
||||
N_("save process ID in FILE")},
|
||||
{"", 0, 0, 0},
|
||||
{"-d", "--watchfd", N_("PID[:FD]"),
|
||||
N_("watch file FD opened by process PID")},
|
||||
{"", 0, 0, 0},
|
||||
{"-h", "--help", 0,
|
||||
N_("show this help and exit")},
|
||||
{"-V", "--version", 0,
|
||||
N_("show version information and exit")},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
int i, col1max = 0, tw = 77;
|
||||
char *optbuf;
|
||||
|
||||
printf(_("Usage: %s [OPTION] [FILE]..."), PROGRAM_NAME);
|
||||
printf("\n%s\n\n",
|
||||
_
|
||||
("Concatenate FILE(s), or standard input, to standard output,\n"
|
||||
"with monitoring."));
|
||||
|
||||
for (i = 0; optlist[i].optshort; i++) {
|
||||
int width = 0;
|
||||
char *param;
|
||||
|
||||
width = 2 + strlen(optlist[i].optshort);
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
if (optlist[i].optlong)
|
||||
width += 2 + strlen(optlist[i].optlong);
|
||||
#endif
|
||||
param = optlist[i].param;
|
||||
if (param)
|
||||
param = _(param);
|
||||
if (param)
|
||||
width += 1 + strlen(param);
|
||||
|
||||
if (width > col1max)
|
||||
col1max = width;
|
||||
}
|
||||
|
||||
col1max++;
|
||||
|
||||
optbuf = malloc(col1max + 16);
|
||||
if (NULL == optbuf) {
|
||||
fprintf(stderr, "%s: %s\n", PROGRAM_NAME, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i = 0; optlist[i].optshort; i++) {
|
||||
char *param;
|
||||
char *description;
|
||||
char *start;
|
||||
char *end;
|
||||
|
||||
if (0 == optlist[i].optshort[0]) {
|
||||
printf("\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
param = optlist[i].param;
|
||||
if (param)
|
||||
param = _(param);
|
||||
description = optlist[i].description;
|
||||
if (description)
|
||||
description = _(description);
|
||||
|
||||
sprintf(optbuf, "%s%s%s%s%s", optlist[i].optshort,
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
optlist[i].optlong ? ", " : "",
|
||||
optlist[i].optlong ? optlist[i].optlong : "",
|
||||
#else
|
||||
"", "",
|
||||
#endif
|
||||
param ? " " : "", param ? param : "");
|
||||
|
||||
printf(" %-*s ", col1max - 2, optbuf);
|
||||
|
||||
if (NULL == description) {
|
||||
printf("\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
start = description;
|
||||
|
||||
while (strlen(start) > tw - col1max) {
|
||||
end = start + tw - col1max;
|
||||
while ((end > start) && (end[0] != ' '))
|
||||
end--;
|
||||
if (end == start) {
|
||||
end = start + tw - col1max;
|
||||
} else {
|
||||
end++;
|
||||
}
|
||||
printf("%.*s\n%*s ", (int) (end - start), start,
|
||||
col1max, "");
|
||||
if (end == start)
|
||||
end++;
|
||||
start = end;
|
||||
}
|
||||
|
||||
printf("%s\n", start);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DEBUGGING
|
||||
printf("\n");
|
||||
printf("%s",
|
||||
_
|
||||
("Debugging is enabled; export the DEBUG environment variable to define the\noutput filename.\n"));
|
||||
#endif
|
||||
|
||||
printf("\n");
|
||||
printf(_("Please report any bugs to %s."), BUG_REPORTS_TO);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* Main program entry point - read the command line options, then perform
|
||||
* the appropriate actions.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#include "options.h"
|
||||
#include "pv.h"
|
||||
|
||||
/* #undef MAKE_STDOUT_NONBLOCKING */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
int pv_remote_set(opts_t);
|
||||
void pv_remote_init(void);
|
||||
void pv_remote_fini(void);
|
||||
|
||||
|
||||
/*
|
||||
* Process command-line arguments and set option flags, then call functions
|
||||
* to initialise, and finally enter the main loop.
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct termios t, t_save;
|
||||
opts_t opts;
|
||||
pvstate_t state;
|
||||
int retcode = 0;
|
||||
|
||||
#ifdef ENABLE_NLS
|
||||
setlocale(LC_ALL, "");
|
||||
bindtextdomain(PACKAGE, LOCALEDIR);
|
||||
textdomain(PACKAGE);
|
||||
#endif
|
||||
|
||||
opts = opts_parse(argc, argv);
|
||||
if (NULL == opts) {
|
||||
debug("%s: %d", "exiting with status", 64);
|
||||
return 64;
|
||||
}
|
||||
|
||||
if (opts->do_nothing) {
|
||||
debug("%s", "nothing to do - exiting with status 0");
|
||||
opts_free(opts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* -R specified - send the message, then exit.
|
||||
*/
|
||||
if (opts->remote > 0) {
|
||||
retcode = pv_remote_set(opts);
|
||||
opts_free(opts);
|
||||
return retcode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate our internal state buffer.
|
||||
*/
|
||||
state = pv_state_alloc(opts->program_name);
|
||||
if (NULL == state) {
|
||||
fprintf(stderr, "%s: %s: %s\n", opts->program_name,
|
||||
_("state allocation failed"), strerror(errno));
|
||||
opts_free(opts);
|
||||
debug("%s: %d", "exiting with status", 64);
|
||||
return 64;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a PID file if -P was specified.
|
||||
*/
|
||||
if (opts->pidfile != NULL) {
|
||||
FILE *pidfptr;
|
||||
pidfptr = fopen(opts->pidfile, "w");
|
||||
if (NULL == pidfptr) {
|
||||
fprintf(stderr, "%s: %s: %s\n", opts->program_name,
|
||||
opts->pidfile, strerror(errno));
|
||||
pv_state_free(state);
|
||||
opts_free(opts);
|
||||
return 1;
|
||||
}
|
||||
fprintf(pidfptr, "%d\n", getpid());
|
||||
fclose(pidfptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* If no files were given, pretend "-" was given (stdin).
|
||||
*/
|
||||
if (0 == opts->argc) {
|
||||
debug("%s", "no files given - adding fake argument `-'");
|
||||
opts->argv[opts->argc++] = "-";
|
||||
}
|
||||
|
||||
/*
|
||||
* Put our list of files into the PV internal state.
|
||||
*/
|
||||
pv_state_inputfiles(state, opts->argc,
|
||||
(const char **) (opts->argv));
|
||||
|
||||
if (0 == opts->watch_pid) {
|
||||
/*
|
||||
* If no size was given, and we're not in line mode, try to
|
||||
* calculate the total size.
|
||||
*/
|
||||
if ((0 == opts->size) && (0 == opts->linemode)) {
|
||||
opts->size = pv_calc_total_size(state);
|
||||
debug("%s: %llu", "no size given - calculated",
|
||||
opts->size);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the size is unknown, we cannot have an ETA.
|
||||
*/
|
||||
if (opts->size < 1) {
|
||||
opts->eta = 0;
|
||||
debug("%s", "size unknown - ETA disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If stderr is not a terminal and we're neither forcing output nor
|
||||
* outputting numerically, we will have nothing to display at all.
|
||||
*/
|
||||
if ((0 == isatty(STDERR_FILENO))
|
||||
&& (0 == opts->force)
|
||||
&& (0 == opts->numeric)) {
|
||||
opts->no_op = 1;
|
||||
debug("%s", "nothing to display - setting no_op");
|
||||
}
|
||||
|
||||
/*
|
||||
* Auto-detect width or height if either are unspecified.
|
||||
*/
|
||||
if ((0 == opts->width) || (0 == opts->height)) {
|
||||
unsigned int width, height;
|
||||
width = 0;
|
||||
height = 0;
|
||||
pv_screensize(&width, &height);
|
||||
if (0 == opts->width) {
|
||||
opts->width = width;
|
||||
debug("%s: %u", "auto-detected terminal width",
|
||||
width);
|
||||
}
|
||||
if (0 == opts->height) {
|
||||
opts->height = height;
|
||||
debug("%s: %u", "auto-detected terminal height",
|
||||
height);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Width and height bounds checking (and defaults).
|
||||
*/
|
||||
if (opts->width < 1)
|
||||
opts->width = 80;
|
||||
if (opts->height < 1)
|
||||
opts->height = 25;
|
||||
if (opts->width > 999999)
|
||||
opts->width = 999999;
|
||||
if (opts->height > 999999)
|
||||
opts->height = 999999;
|
||||
|
||||
/*
|
||||
* Interval must be at least 0.1 second, and at most 10 minutes.
|
||||
*/
|
||||
if (opts->interval < 0.1)
|
||||
opts->interval = 0.1;
|
||||
if (opts->interval > 600)
|
||||
opts->interval = 600;
|
||||
|
||||
/*
|
||||
* Copy parameters from options into main state.
|
||||
*/
|
||||
pv_state_interval_set(state, opts->interval);
|
||||
pv_state_width_set(state, opts->width);
|
||||
pv_state_height_set(state, opts->height);
|
||||
pv_state_no_op_set(state, opts->no_op);
|
||||
pv_state_force_set(state, opts->force);
|
||||
pv_state_cursor_set(state, opts->cursor);
|
||||
pv_state_numeric_set(state, opts->numeric);
|
||||
pv_state_wait_set(state, opts->wait);
|
||||
pv_state_delay_start_set(state, opts->delay_start);
|
||||
pv_state_linemode_set(state, opts->linemode);
|
||||
pv_state_null_set(state, opts->null);
|
||||
pv_state_skip_errors_set(state, opts->skip_errors);
|
||||
pv_state_stop_at_size_set(state, opts->stop_at_size);
|
||||
pv_state_rate_limit_set(state, opts->rate_limit);
|
||||
pv_state_target_buffer_size_set(state, opts->buffer_size);
|
||||
pv_state_no_splice_set(state, opts->no_splice);
|
||||
pv_state_size_set(state, opts->size);
|
||||
pv_state_name_set(state, opts->name);
|
||||
pv_state_format_string_set(state, opts->format);
|
||||
pv_state_watch_pid_set(state, opts->watch_pid);
|
||||
pv_state_watch_fd_set(state, opts->watch_fd);
|
||||
|
||||
pv_state_set_format(state, opts->progress, opts->timer, opts->eta,
|
||||
opts->fineta, opts->rate, opts->average_rate,
|
||||
opts->bytes, opts->bufpercent,
|
||||
opts->lastwritten, opts->name);
|
||||
|
||||
#ifdef MAKE_STDOUT_NONBLOCKING
|
||||
/*
|
||||
* Try and make standard output use non-blocking I/O.
|
||||
*
|
||||
* Note that this can cause problems with (broken) applications
|
||||
* such as dd.
|
||||
*/
|
||||
fcntl(STDOUT_FILENO, F_SETFL,
|
||||
O_NONBLOCK | fcntl(STDOUT_FILENO, F_GETFL));
|
||||
#endif /* MAKE_STDOUT_NONBLOCKING */
|
||||
|
||||
/*
|
||||
* Set terminal option TOSTOP so we get signal SIGTTOU if we try to
|
||||
* write to the terminal while backgrounded.
|
||||
*
|
||||
* Also, save the current terminal attributes for later restoration.
|
||||
*/
|
||||
memset(&t, 0, sizeof(t));
|
||||
tcgetattr(STDERR_FILENO, &t);
|
||||
t_save = t;
|
||||
t.c_lflag |= TOSTOP;
|
||||
tcsetattr(STDERR_FILENO, TCSANOW, &t);
|
||||
|
||||
if (0 != opts->watch_pid) {
|
||||
if (0 <= opts->watch_fd) {
|
||||
pv_sig_init(state);
|
||||
retcode = pv_watchfd_loop(state);
|
||||
tcsetattr(STDERR_FILENO, TCSANOW, &t_save);
|
||||
if (opts->pidfile != NULL)
|
||||
remove(opts->pidfile);
|
||||
pv_sig_fini(state);
|
||||
} else {
|
||||
pv_sig_init(state);
|
||||
retcode = pv_watchpid_loop(state);
|
||||
tcsetattr(STDERR_FILENO, TCSANOW, &t_save);
|
||||
if (opts->pidfile != NULL)
|
||||
remove(opts->pidfile);
|
||||
pv_sig_fini(state);
|
||||
}
|
||||
} else {
|
||||
pv_sig_init(state);
|
||||
pv_remote_init();
|
||||
retcode = pv_main_loop(state);
|
||||
pv_remote_fini();
|
||||
tcsetattr(STDERR_FILENO, TCSANOW, &t_save);
|
||||
if (opts->pidfile != NULL)
|
||||
remove(opts->pidfile);
|
||||
pv_sig_fini(state);
|
||||
}
|
||||
|
||||
pv_state_free(state);
|
||||
|
||||
opts_free(opts);
|
||||
|
||||
debug("%s: %d", "exiting with status", retcode);
|
||||
|
||||
return retcode;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
* Parse command-line options.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#include "options.h"
|
||||
#include "library/getopt.h"
|
||||
#include "pv.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
void display_help(void);
|
||||
void display_version(void);
|
||||
|
||||
|
||||
/*
|
||||
* Free an opts_t object.
|
||||
*/
|
||||
void opts_free(opts_t opts)
|
||||
{
|
||||
if (!opts)
|
||||
return;
|
||||
if (opts->argv)
|
||||
free(opts->argv);
|
||||
free(opts);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parse the given command-line arguments into an opts_t object, handling
|
||||
* "help", "license" and "version" options internally.
|
||||
*
|
||||
* Returns an opts_t, or 0 on error.
|
||||
*
|
||||
* Note that the contents of *argv[] (i.e. the command line parameters)
|
||||
* aren't copied anywhere, just the pointers are copied, so make sure the
|
||||
* command line data isn't overwritten or argv[1] free()d or whatever.
|
||||
*/
|
||||
opts_t opts_parse(int argc, char **argv)
|
||||
{
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
struct option long_options[] = {
|
||||
{"help", 0, 0, 'h'},
|
||||
{"version", 0, 0, 'V'},
|
||||
{"progress", 0, 0, 'p'},
|
||||
{"timer", 0, 0, 't'},
|
||||
{"eta", 0, 0, 'e'},
|
||||
{"fineta", 0, 0, 'I'},
|
||||
{"rate", 0, 0, 'r'},
|
||||
{"average-rate", 0, 0, 'a'},
|
||||
{"bytes", 0, 0, 'b'},
|
||||
{"buffer-percent", 0, 0, 'T'},
|
||||
{"last-written", 1, 0, 'A'},
|
||||
{"force", 0, 0, 'f'},
|
||||
{"numeric", 0, 0, 'n'},
|
||||
{"quiet", 0, 0, 'q'},
|
||||
{"cursor", 0, 0, 'c'},
|
||||
{"wait", 0, 0, 'W'},
|
||||
{"delay-start", 1, 0, 'D'},
|
||||
{"size", 1, 0, 's'},
|
||||
{"line-mode", 0, 0, 'l'},
|
||||
{"null", 0, 0, '0'},
|
||||
{"interval", 1, 0, 'i'},
|
||||
{"width", 1, 0, 'w'},
|
||||
{"height", 1, 0, 'H'},
|
||||
{"name", 1, 0, 'N'},
|
||||
{"format", 1, 0, 'F'},
|
||||
{"rate-limit", 1, 0, 'L'},
|
||||
{"buffer-size", 1, 0, 'B'},
|
||||
{"no-splice", 0, 0, 'C'},
|
||||
{"skip-errors", 0, 0, 'E'},
|
||||
{"stop-at-size", 0, 0, 'S'},
|
||||
{"remote", 1, 0, 'R'},
|
||||
{"pidfile", 1, 0, 'P'},
|
||||
{"watchfd", 1, 0, 'd'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
int option_index = 0;
|
||||
#endif
|
||||
char *short_options =
|
||||
"hVpteIrabTA:fnqcWD:s:l0i:w:H:N:F:L:B:CESR:P:d:";
|
||||
int c, numopts;
|
||||
unsigned int check_pid;
|
||||
int check_fd;
|
||||
opts_t opts;
|
||||
char *ptr;
|
||||
|
||||
opts = calloc(1, sizeof(*opts));
|
||||
if (!opts) {
|
||||
fprintf(stderr,
|
||||
_("%s: option structure allocation failed (%s)"),
|
||||
argv[0], strerror(errno));
|
||||
fprintf(stderr, "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
opts->program_name = argv[0];
|
||||
ptr = strrchr(opts->program_name, '/');
|
||||
if (NULL != ptr)
|
||||
opts->program_name = &(ptr[1]);
|
||||
|
||||
opts->argc = 0;
|
||||
opts->argv = calloc(argc + 1, sizeof(char *));
|
||||
if (!opts->argv) {
|
||||
fprintf(stderr,
|
||||
_
|
||||
("%s: option structure argv allocation failed (%s)"),
|
||||
opts->program_name, strerror(errno));
|
||||
fprintf(stderr, "\n");
|
||||
opts_free(opts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
numopts = 0;
|
||||
|
||||
opts->interval = 1;
|
||||
opts->delay_start = 0;
|
||||
opts->watch_pid = 0;
|
||||
opts->watch_fd = -1;
|
||||
|
||||
do {
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
c = getopt_long(argc, argv,
|
||||
short_options, long_options,
|
||||
&option_index);
|
||||
#else
|
||||
c = getopt(argc, argv, short_options);
|
||||
#endif
|
||||
if (c < 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Check that any numeric arguments are of the right type.
|
||||
*/
|
||||
switch (c) {
|
||||
case 's':
|
||||
case 'A':
|
||||
case 'w':
|
||||
case 'H':
|
||||
case 'L':
|
||||
case 'B':
|
||||
case 'R':
|
||||
if (pv_getnum_check(optarg, PV_NUMTYPE_INTEGER) !=
|
||||
0) {
|
||||
fprintf(stderr, "%s: -%c: %s\n",
|
||||
opts->program_name, c,
|
||||
_("integer argument expected"));
|
||||
opts_free(opts);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
case 'D':
|
||||
if (pv_getnum_check(optarg, PV_NUMTYPE_DOUBLE) !=
|
||||
0) {
|
||||
fprintf(stderr, "%s: -%c: %s\n",
|
||||
opts->program_name, c,
|
||||
_("numeric argument expected"));
|
||||
opts_free(opts);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
if (sscanf(optarg, "%u:%d", &check_pid, &check_fd)
|
||||
< 1) {
|
||||
fprintf(stderr, "%s: -%c: %s\n",
|
||||
opts->program_name, c,
|
||||
_
|
||||
("process ID or pid:fd pair expected"));
|
||||
opts_free(opts);
|
||||
return 0;
|
||||
}
|
||||
if (check_pid < 1) {
|
||||
fprintf(stderr, "%s: -%c: %s\n",
|
||||
opts->program_name, c,
|
||||
_("invalid process ID"));
|
||||
opts_free(opts);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse each command line option.
|
||||
*/
|
||||
switch (c) {
|
||||
case 'h':
|
||||
display_help();
|
||||
opts->do_nothing = 1;
|
||||
return opts;
|
||||
break;
|
||||
case 'V':
|
||||
display_version();
|
||||
opts->do_nothing = 1;
|
||||
return opts;
|
||||
break;
|
||||
case 'p':
|
||||
opts->progress = 1;
|
||||
numopts++;
|
||||
break;
|
||||
case 't':
|
||||
opts->timer = 1;
|
||||
numopts++;
|
||||
break;
|
||||
case 'I':
|
||||
opts->fineta = 1;
|
||||
numopts++;
|
||||
break;
|
||||
case 'e':
|
||||
opts->eta = 1;
|
||||
numopts++;
|
||||
break;
|
||||
case 'r':
|
||||
opts->rate = 1;
|
||||
numopts++;
|
||||
break;
|
||||
case 'a':
|
||||
opts->average_rate = 1;
|
||||
numopts++;
|
||||
break;
|
||||
case 'b':
|
||||
opts->bytes = 1;
|
||||
numopts++;
|
||||
break;
|
||||
case 'T':
|
||||
opts->bufpercent = 1;
|
||||
numopts++;
|
||||
break;
|
||||
case 'A':
|
||||
opts->lastwritten = pv_getnum_i(optarg);
|
||||
numopts++;
|
||||
break;
|
||||
case 'f':
|
||||
opts->force = 1;
|
||||
break;
|
||||
case 'n':
|
||||
opts->numeric = 1;
|
||||
numopts++;
|
||||
break;
|
||||
case 'q':
|
||||
opts->no_op = 1;
|
||||
numopts++;
|
||||
break;
|
||||
case 'c':
|
||||
opts->cursor = 1;
|
||||
break;
|
||||
case 'W':
|
||||
opts->wait = 1;
|
||||
break;
|
||||
case 'D':
|
||||
opts->delay_start = pv_getnum_d(optarg);
|
||||
break;
|
||||
case 's':
|
||||
opts->size = pv_getnum_ll(optarg);
|
||||
break;
|
||||
case 'l':
|
||||
opts->linemode = 1;
|
||||
break;
|
||||
case '0':
|
||||
opts->null = 1;
|
||||
opts->linemode = 1;
|
||||
break;
|
||||
case 'i':
|
||||
opts->interval = pv_getnum_d(optarg);
|
||||
break;
|
||||
case 'w':
|
||||
opts->width = pv_getnum_i(optarg);
|
||||
break;
|
||||
case 'H':
|
||||
opts->height = pv_getnum_i(optarg);
|
||||
break;
|
||||
case 'N':
|
||||
opts->name = optarg;
|
||||
break;
|
||||
case 'L':
|
||||
opts->rate_limit = pv_getnum_ll(optarg);
|
||||
break;
|
||||
case 'B':
|
||||
opts->buffer_size = pv_getnum_ll(optarg);
|
||||
break;
|
||||
case 'C':
|
||||
opts->no_splice = 1;
|
||||
break;
|
||||
case 'E':
|
||||
opts->skip_errors++;
|
||||
break;
|
||||
case 'S':
|
||||
opts->stop_at_size = 1;
|
||||
break;
|
||||
case 'R':
|
||||
opts->remote = pv_getnum_i(optarg);
|
||||
break;
|
||||
case 'P':
|
||||
opts->pidfile = optarg;
|
||||
break;
|
||||
case 'F':
|
||||
opts->format = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
opts->watch_pid = 0;
|
||||
opts->watch_fd = -1;
|
||||
sscanf(optarg, "%u:%d", &(opts->watch_pid),
|
||||
&(opts->watch_fd));
|
||||
break;
|
||||
default:
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
fprintf(stderr,
|
||||
_("Try `%s --help' for more information."),
|
||||
opts->program_name);
|
||||
#else
|
||||
fprintf(stderr,
|
||||
_("Try `%s -h' for more information."),
|
||||
opts->program_name);
|
||||
#endif
|
||||
fprintf(stderr, "\n");
|
||||
opts_free(opts);
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
} while (c != -1);
|
||||
|
||||
if (0 != opts->watch_pid) {
|
||||
if (opts->linemode || opts->null || opts->stop_at_size
|
||||
|| (opts->skip_errors > 0) || (opts->buffer_size > 0)
|
||||
|| (opts->rate_limit > 0)) {
|
||||
fprintf(stderr,
|
||||
_
|
||||
("%s: cannot use line mode or transfer modifier options when watching file descriptors"),
|
||||
opts->program_name);
|
||||
fprintf(stderr, "\n");
|
||||
opts_free(opts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (opts->cursor) {
|
||||
fprintf(stderr,
|
||||
_
|
||||
("%s: cannot use cursor positioning when watching file descriptors"),
|
||||
opts->program_name);
|
||||
fprintf(stderr, "\n");
|
||||
opts_free(opts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (0 != opts->remote) {
|
||||
fprintf(stderr,
|
||||
_
|
||||
("%s: cannot use remote control when watching file descriptors"),
|
||||
opts->program_name);
|
||||
fprintf(stderr, "\n");
|
||||
opts_free(opts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
fprintf(stderr,
|
||||
_
|
||||
("%s: cannot transfer files when watching file descriptors"),
|
||||
opts->program_name);
|
||||
fprintf(stderr, "\n");
|
||||
opts_free(opts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (0 != access("/proc/self/fdinfo", X_OK)) {
|
||||
fprintf(stderr,
|
||||
_
|
||||
("%s: -d: not available on systems without /proc/self/fdinfo"),
|
||||
opts->program_name);
|
||||
fprintf(stderr, "\n");
|
||||
opts_free(opts);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Default options: -pterb
|
||||
*/
|
||||
if (0 == numopts) {
|
||||
opts->progress = 1;
|
||||
opts->timer = 1;
|
||||
opts->eta = 1;
|
||||
opts->rate = 1;
|
||||
opts->bytes = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Store remaining command-line arguments.
|
||||
*/
|
||||
while (optind < argc) {
|
||||
opts->argv[opts->argc++] = argv[optind++];
|
||||
}
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* Remote-control functions.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#include "options.h"
|
||||
#include "pv.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#ifdef HAVE_IPC
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/msg.h>
|
||||
#endif /* HAVE_IPC */
|
||||
|
||||
|
||||
#ifdef HAVE_IPC
|
||||
struct remote_msg {
|
||||
long mtype;
|
||||
unsigned char progress; /* progress bar flag */
|
||||
unsigned char timer; /* timer flag */
|
||||
unsigned char eta; /* ETA flag */
|
||||
unsigned char fineta; /* absolute ETA flag */
|
||||
unsigned char rate; /* rate counter flag */
|
||||
unsigned char average_rate; /* average rate counter flag */
|
||||
unsigned char bytes; /* bytes transferred flag */
|
||||
unsigned char bufpercent; /* transfer buffer percentage flag */
|
||||
unsigned int lastwritten; /* last-written bytes count */
|
||||
unsigned long long rate_limit; /* rate limit, in bytes per second */
|
||||
unsigned long long buffer_size; /* buffer size, in bytes (0=default) */
|
||||
unsigned long long size; /* total size of data */
|
||||
double interval; /* interval between updates */
|
||||
unsigned int width; /* screen width */
|
||||
unsigned int height; /* screen height */
|
||||
char name[256];
|
||||
char format[256];
|
||||
};
|
||||
|
||||
|
||||
static int remote__msgid = -1;
|
||||
|
||||
|
||||
/*
|
||||
* Return a key for use with msgget() which will be unique to the current
|
||||
* user.
|
||||
*
|
||||
* We can't just use ftok() because the queue needs to be user-specific
|
||||
* so that a user cannot send messages to another user's process, and we
|
||||
* can't easily find out the terminal a given process is connected to in a
|
||||
* cross-platform way.
|
||||
*/
|
||||
static key_t remote__genkey(void)
|
||||
{
|
||||
int uid;
|
||||
key_t key;
|
||||
|
||||
uid = geteuid();
|
||||
if (uid < 0)
|
||||
uid = 0;
|
||||
|
||||
key = ftok("/tmp", 'P') | uid;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a message queue ID that is unique to the current user and the
|
||||
* given process ID, or -1 on error.
|
||||
*/
|
||||
static int remote__msgget(void)
|
||||
{
|
||||
/* Catch SIGSYS in case msgget() raises it, so we get ENOSYS */
|
||||
signal(SIGSYS, SIG_IGN);
|
||||
return msgget(remote__genkey(), IPC_CREAT | 0600);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set the options of a remote process by setting up an IPC message queue,
|
||||
* sending a message containing the new options, and then waiting for the
|
||||
* message to be consumed by the remote process.
|
||||
*
|
||||
* Returns nonzero on error.
|
||||
*/
|
||||
int pv_remote_set(opts_t opts)
|
||||
{
|
||||
struct remote_msg msgbuf;
|
||||
struct msqid_ds qbuf;
|
||||
long timeout;
|
||||
int msgid;
|
||||
long initial_qnum;
|
||||
|
||||
/*
|
||||
* Check that the remote process exists.
|
||||
*/
|
||||
if (kill(opts->remote, 0) != 0) {
|
||||
fprintf(stderr, "%s: %d: %s\n", opts->program_name,
|
||||
opts->remote, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure parameters are within sensible bounds.
|
||||
*/
|
||||
if (opts->width < 0)
|
||||
opts->width = 80;
|
||||
if (opts->height < 0)
|
||||
opts->height = 25;
|
||||
if (opts->width > 999999)
|
||||
opts->width = 999999;
|
||||
if (opts->height > 999999)
|
||||
opts->height = 999999;
|
||||
if ((opts->interval != 0) && (opts->interval < 0.1))
|
||||
opts->interval = 0.1;
|
||||
if (opts->interval > 600)
|
||||
opts->interval = 600;
|
||||
|
||||
/*
|
||||
* Copy parameters into message buffer.
|
||||
*/
|
||||
memset(&msgbuf, 0, sizeof(msgbuf));
|
||||
msgbuf.mtype = opts->remote;
|
||||
msgbuf.progress = opts->progress;
|
||||
msgbuf.timer = opts->timer;
|
||||
msgbuf.eta = opts->eta;
|
||||
msgbuf.fineta = opts->fineta;
|
||||
msgbuf.rate = opts->rate;
|
||||
msgbuf.average_rate = opts->average_rate;
|
||||
msgbuf.bytes = opts->bytes;
|
||||
msgbuf.bufpercent = opts->bufpercent;
|
||||
msgbuf.lastwritten = opts->lastwritten;
|
||||
msgbuf.rate_limit = opts->rate_limit;
|
||||
msgbuf.buffer_size = opts->buffer_size;
|
||||
msgbuf.size = opts->size;
|
||||
msgbuf.interval = opts->interval;
|
||||
msgbuf.width = opts->width;
|
||||
msgbuf.height = opts->height;
|
||||
if (opts->name != NULL) {
|
||||
strncpy(msgbuf.name, opts->name, sizeof(msgbuf.name) - 1);
|
||||
}
|
||||
if (opts->format != NULL) {
|
||||
strncpy(msgbuf.format, opts->format,
|
||||
sizeof(msgbuf.format) - 1);
|
||||
}
|
||||
|
||||
msgid = remote__msgget();
|
||||
if (msgid < 0) {
|
||||
fprintf(stderr, "%s: %s\n", opts->program_name,
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (msgctl(msgid, IPC_STAT, &qbuf) < 0) {
|
||||
fprintf(stderr, "%s: %s\n", opts->program_name,
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
initial_qnum = qbuf.msg_qnum;
|
||||
|
||||
if (msgsnd(msgid, &msgbuf, sizeof(msgbuf) - sizeof(long), 0) != 0) {
|
||||
fprintf(stderr, "%s: %s\n", opts->program_name,
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
timeout = 1100000;
|
||||
|
||||
while (timeout > 10000) {
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 10000;
|
||||
select(0, NULL, NULL, NULL, &tv);
|
||||
timeout -= 10000;
|
||||
|
||||
/*
|
||||
* If we can't stat the queue, it must have been deleted.
|
||||
*/
|
||||
if (msgctl(msgid, IPC_STAT, &qbuf) < 0)
|
||||
break;
|
||||
|
||||
/*
|
||||
* If the message count is at or below the message count
|
||||
* before we sent our message, assume it was received.
|
||||
*/
|
||||
if (qbuf.msg_qnum <= initial_qnum) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Message not received - delete it.
|
||||
*/
|
||||
if (msgctl(msgid, IPC_STAT, &qbuf) >= 0) {
|
||||
msgrcv(msgid, &msgbuf, sizeof(msgbuf) - sizeof(long),
|
||||
opts->remote, IPC_NOWAIT);
|
||||
/*
|
||||
* If this leaves nothing on the queue, remove the
|
||||
* queue, in case we created one for no reason.
|
||||
*/
|
||||
if (msgctl(msgid, IPC_STAT, &qbuf) >= 0) {
|
||||
if (qbuf.msg_qnum < 1)
|
||||
msgctl(msgid, IPC_RMID, &qbuf);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s: %d: %s\n", opts->program_name, opts->remote,
|
||||
_("message not received"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check for an IPC remote handling message and, if there is one, replace
|
||||
* the current process's options with those being passed in.
|
||||
*
|
||||
* NB relies on pv_state_set_format() causing the output format to be
|
||||
* reparsed.
|
||||
*/
|
||||
void pv_remote_check(pvstate_t state)
|
||||
{
|
||||
struct remote_msg msgbuf;
|
||||
int got;
|
||||
|
||||
if (remote__msgid < 0)
|
||||
return;
|
||||
|
||||
memset(&msgbuf, 0, sizeof(msgbuf));
|
||||
|
||||
got =
|
||||
msgrcv(remote__msgid, &msgbuf, sizeof(msgbuf) - sizeof(long),
|
||||
getpid(), IPC_NOWAIT);
|
||||
if (got < 0) {
|
||||
/*
|
||||
* If our queue had been deleted, re-create it.
|
||||
*/
|
||||
if (errno != EAGAIN && errno != ENOMSG) {
|
||||
remote__msgid = remote__msgget();
|
||||
}
|
||||
}
|
||||
if (got < 1)
|
||||
return;
|
||||
|
||||
debug("%s", "received remote message");
|
||||
|
||||
pv_state_format_string_set(state, NULL);
|
||||
pv_state_name_set(state, NULL);
|
||||
|
||||
pv_state_set_format(state, msgbuf.progress, msgbuf.timer,
|
||||
msgbuf.eta, msgbuf.fineta, msgbuf.rate,
|
||||
msgbuf.average_rate,
|
||||
msgbuf.bytes, msgbuf.bufpercent,
|
||||
msgbuf.lastwritten,
|
||||
0 ==
|
||||
msgbuf.name[0] ? NULL : strdup(msgbuf.name));
|
||||
|
||||
if (msgbuf.rate_limit > 0)
|
||||
pv_state_rate_limit_set(state, msgbuf.rate_limit);
|
||||
if (msgbuf.buffer_size > 0) {
|
||||
pv_state_target_buffer_size_set(state, msgbuf.buffer_size);
|
||||
}
|
||||
if (msgbuf.size > 0)
|
||||
pv_state_size_set(state, msgbuf.size);
|
||||
if (msgbuf.interval > 0)
|
||||
pv_state_interval_set(state, msgbuf.interval);
|
||||
if (msgbuf.width > 0)
|
||||
pv_state_width_set(state, msgbuf.width);
|
||||
if (msgbuf.height > 0)
|
||||
pv_state_height_set(state, msgbuf.height);
|
||||
if (msgbuf.format[0] != 0)
|
||||
pv_state_format_string_set(state, strdup(msgbuf.format));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialise remote message reception handling.
|
||||
*/
|
||||
void pv_remote_init(void)
|
||||
{
|
||||
remote__msgid = remote__msgget();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Clean up after remote message reception handling.
|
||||
*/
|
||||
void pv_remote_fini(void)
|
||||
{
|
||||
if (remote__msgid >= 0) {
|
||||
struct msqid_ds qbuf;
|
||||
msgctl(remote__msgid, IPC_RMID, &qbuf);
|
||||
}
|
||||
}
|
||||
|
||||
#else /* !HAVE_IPC */
|
||||
|
||||
/*
|
||||
* Dummy stubs for remote control when we don't have IPC.
|
||||
*/
|
||||
void pv_remote_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
void pv_remote_check(pvstate_t state)
|
||||
{
|
||||
}
|
||||
|
||||
void pv_remote_fini(void)
|
||||
{
|
||||
}
|
||||
|
||||
int pv_remote_set(pvstate_t state)
|
||||
{
|
||||
fprintf(stderr, "%s\n", _("IPC not supported on this system"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /* HAVE_IPC */
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Output version information to stdout.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
/*
|
||||
* Display current package version.
|
||||
*/
|
||||
void display_version(void)
|
||||
{
|
||||
printf(_("%s %s - Copyright %s %s"),
|
||||
PROGRAM_NAME, VERSION, COPYRIGHT_YEAR, COPYRIGHT_HOLDER);
|
||||
printf("\n\n");
|
||||
printf(_("Web site: %s"), PROJECT_HOMEPAGE);
|
||||
printf("\n\n");
|
||||
printf("%s",
|
||||
_("This program is free software, and is being distributed "
|
||||
"under the\nterms of the Artistic License 2.0."));
|
||||
printf("\n\n");
|
||||
printf("%s",
|
||||
_
|
||||
("This program is distributed in the hope that it will be useful,\n"
|
||||
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
||||
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."));
|
||||
printf("\n\n");
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
+452
@@ -0,0 +1,452 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-06-30 22:21+0100\n"
|
||||
"Content-Type: text/plain; charset=iso-8859-15\n"
|
||||
"Date: 1999-06-01 15:18:29+0100\n"
|
||||
"From: Andrew Wood <ivarch@ubik>\n"
|
||||
"Xgettext-Options: --default-domain=pkgbuild --directory=./.pkgdir --add-"
|
||||
"comments --keyword=_ --keyword=N_\n"
|
||||
"Files: src/getopt/getopt.c src/getopt/getopt1.c src/version.c src/main/init."
|
||||
"c src/main/help.c src/main/xpmptr.c src/guts/load.c src/guts/stop.c src/guts/"
|
||||
"configure.c src/guts/make.c src/guts/install.c src/guts/prefs.c src/ui/"
|
||||
"generate.c src/ui/main.c src/ui/xpm.c src/ui/status.c src/ui/activate.c src/"
|
||||
"ui/handlers/main.c src/ui/handlers/package.c src/ui/handlers/prefs.c src/ui/"
|
||||
"handlers/dirsel.c src/ui/handlers/popup.c src/ui/handlers/help.c src/ui/"
|
||||
"textout.c src/ui/report.c src/ui/callback.c src/nls/intl/bindtextdom.c src/"
|
||||
"nls/intl/dcgettext.c src/nls/intl/dgettext.c src/nls/intl/finddomain.c src/"
|
||||
"nls/intl/gettext.c src/nls/intl/loadmsgcat.c src/nls/intl/localealias.c src/"
|
||||
"nls/intl/textdomain.c src/nls/intl-cat/cat-compat.c src/nls/intl-gett/intl-"
|
||||
"compat.c\n"
|
||||
|
||||
#: src/pv/watchpid.c:51 src/pv/watchpid.c:66 src/pv/watchpid.c:76
|
||||
#: src/pv/watchpid.c:107 src/pv/loop.c:519 src/pv/loop.c:580 src/pv/loop.c:632
|
||||
msgid "pid"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/watchpid.c:68 src/pv/watchpid.c:78 src/pv/watchpid.c:109
|
||||
msgid "fd"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/watchpid.c:112
|
||||
msgid "not a regular file or block device"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/cursor.c:53
|
||||
#, fuzzy
|
||||
msgid "failed to get terminal name"
|
||||
msgstr "Terminal konnte nicht geöffnet werden"
|
||||
|
||||
#: src/pv/cursor.c:96
|
||||
#, fuzzy
|
||||
msgid "failed to open lock file"
|
||||
msgstr "Datei konnte nicht geschlossen werden"
|
||||
|
||||
#: src/pv/cursor.c:129
|
||||
msgid "lock attempt failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/cursor.c:369
|
||||
msgid "failed to open terminal"
|
||||
msgstr "Terminal konnte nicht geöffnet werden"
|
||||
|
||||
#: src/pv/file.c:118
|
||||
#, fuzzy
|
||||
msgid "failed to seek to start of output"
|
||||
msgstr "Dateiinformationen für Ausgabe-Datei konnten nicht gelesen werden"
|
||||
|
||||
#: src/pv/file.c:214
|
||||
msgid "failed to close file"
|
||||
msgstr "Datei konnte nicht geschlossen werden"
|
||||
|
||||
#: src/pv/file.c:237
|
||||
msgid "failed to read file"
|
||||
msgstr "Datei konnte nicht gelesen werden"
|
||||
|
||||
#: src/pv/file.c:247
|
||||
msgid "failed to stat file"
|
||||
msgstr "Dateiinformationen konnten nicht gelesen werden"
|
||||
|
||||
#: src/pv/file.c:256
|
||||
msgid "failed to stat output file"
|
||||
msgstr "Dateiinformationen für Ausgabe-Datei konnten nicht gelesen werden"
|
||||
|
||||
#: src/pv/file.c:279
|
||||
msgid "input file is output file"
|
||||
msgstr "Eingabe-Datei ist Ausgabe-Datei"
|
||||
|
||||
#: src/pv/display.c:117
|
||||
msgid "yzafpnum kMGTPEZY"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:122
|
||||
msgid "yzafpnum KMGTPEZY"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:532 src/pv/transfer.c:679
|
||||
msgid "buffer allocation failed"
|
||||
msgstr "Puffer konnte nicht allokiert werden"
|
||||
|
||||
#: src/pv/display.c:592 src/pv/transfer.c:458
|
||||
msgid "B"
|
||||
msgstr "B"
|
||||
|
||||
#: src/pv/display.c:628 src/pv/display.c:636
|
||||
msgid "/s"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:628 src/pv/display.c:636
|
||||
msgid "B/s"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:671 src/pv/display.c:676 src/pv/display.c:738
|
||||
msgid "ETA"
|
||||
msgstr "ETA"
|
||||
|
||||
#: src/pv/transfer.c:341
|
||||
msgid "read failed"
|
||||
msgstr "read-Aufruf fehlgeschlagen"
|
||||
|
||||
#: src/pv/transfer.c:359
|
||||
msgid "warning: read errors detected"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:374
|
||||
msgid "file is not seekable"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:437
|
||||
#, fuzzy
|
||||
msgid "failed to seek past error"
|
||||
msgstr "Dateiinformationen konnten nicht gelesen werden"
|
||||
|
||||
#: src/pv/transfer.c:456
|
||||
msgid "skipped past read error"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:631
|
||||
msgid "write failed"
|
||||
msgstr "write-Aufruf fehlgeschlagen"
|
||||
|
||||
#: src/pv/transfer.c:770
|
||||
msgid "select call failed"
|
||||
msgstr "select-Aufruf fehlgeschlagen"
|
||||
|
||||
#: src/pv/state.c:33
|
||||
msgid "none"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/version.c:17
|
||||
#, fuzzy, c-format
|
||||
msgid "%s %s - Copyright %s %s"
|
||||
msgstr "%s %s - Copyright(C) %s %s"
|
||||
|
||||
#: src/main/version.c:20
|
||||
#, c-format
|
||||
msgid "Web site: %s"
|
||||
msgstr "Web-Site: %s"
|
||||
|
||||
#: src/main/version.c:23
|
||||
msgid ""
|
||||
"This program is free software, and is being distributed under the\n"
|
||||
"terms of the Artistic License 2.0."
|
||||
msgstr ""
|
||||
"Dieses Programm ist freie Software und wird unter den Bedingungen\n"
|
||||
"der Artistic License verbreitet."
|
||||
|
||||
#: src/main/version.c:28
|
||||
msgid ""
|
||||
"This program is distributed in the hope that it will be useful,\n"
|
||||
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
||||
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||
msgstr ""
|
||||
"Dieses Programm wird verbreitet unter der Annahme dass es nützlich ist,\n"
|
||||
"aber OHNE JEGLICHE GARANTIE; insbesondere ohne der impliziten Garantie\n"
|
||||
"einer MARKTGÄNGIGKEIT oder EIGNUNG ZU EINEM BESTIMMTEN ZWECK."
|
||||
|
||||
#: src/main/main.c:73
|
||||
#, fuzzy
|
||||
msgid "state allocation failed"
|
||||
msgstr "Puffer konnte nicht allokiert werden"
|
||||
|
||||
#: src/main/options.c:98
|
||||
#, c-format
|
||||
msgid "%s: option structure allocation failed (%s)"
|
||||
msgstr "%s: `option' konnte nicht allokiert werden (%s)"
|
||||
|
||||
#: src/main/options.c:114
|
||||
#, c-format
|
||||
msgid "%s: option structure argv allocation failed (%s)"
|
||||
msgstr "%s: `option' (argv) konnte nicht allokiert werden (%s)"
|
||||
|
||||
#: src/main/options.c:154
|
||||
msgid "integer argument expected"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:165
|
||||
msgid "numeric argument expected"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:176
|
||||
msgid "process ID or pid:fd pair expected"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:183
|
||||
msgid "invalid process ID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:317
|
||||
#, c-format
|
||||
msgid "Try `%s --help' for more information."
|
||||
msgstr "`%s --help' zeigt weitere Informationen an."
|
||||
|
||||
#: src/main/options.c:321
|
||||
#, fuzzy, c-format
|
||||
msgid "Try `%s -h' for more information."
|
||||
msgstr "`%s -h' zeigt weitere Informationen an."
|
||||
|
||||
#: src/main/options.c:338
|
||||
#, c-format
|
||||
msgid ""
|
||||
"%s: cannot use line mode or transfer modifier options when watching file "
|
||||
"descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:348
|
||||
#, c-format
|
||||
msgid "%s: cannot use cursor positioning when watching file descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:358
|
||||
#, c-format
|
||||
msgid "%s: cannot use remote control when watching file descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:368
|
||||
#, c-format
|
||||
msgid "%s: cannot transfer files when watching file descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:378
|
||||
#, c-format
|
||||
msgid "%s: -d: not available on systems without /proc/self/fdinfo"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/remote.c:218
|
||||
msgid "message not received"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/remote.c:324
|
||||
msgid "IPC not supported on this system"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:31
|
||||
msgid "show progress bar"
|
||||
msgstr "Fortschritts-Anzeige"
|
||||
|
||||
#: src/main/help.c:33
|
||||
msgid "show elapsed time"
|
||||
msgstr "zeige die verstrichene Zeit an"
|
||||
|
||||
#: src/main/help.c:35
|
||||
msgid "show estimated time of arrival (completion)"
|
||||
msgstr "zeige die erwartete Zeit bis zum Ende an"
|
||||
|
||||
#: src/main/help.c:38
|
||||
#, fuzzy
|
||||
msgid "show absolute estimated time of arrival (completion)"
|
||||
msgstr "zeige die erwartete Zeit bis zum Ende an"
|
||||
|
||||
#: src/main/help.c:40
|
||||
msgid "show data transfer rate counter"
|
||||
msgstr "zeige die Datentransferrate an"
|
||||
|
||||
#: src/main/help.c:42
|
||||
msgid "show data transfer average rate counter"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:44
|
||||
msgid "show number of bytes transferred"
|
||||
msgstr "zeige die Anzahl von Bytes, die transferiert worden sind"
|
||||
|
||||
#: src/main/help.c:46
|
||||
msgid "show percentage of transfer buffer in use"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:47
|
||||
msgid "NUM"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:48
|
||||
msgid "show NUM bytes last written"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:49
|
||||
msgid "FORMAT"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:50
|
||||
msgid "set output format to FORMAT"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:52
|
||||
msgid "output percentages, not visual information"
|
||||
msgstr "Ausgabe von Prozent-Angaben statt visueller Darstellung"
|
||||
|
||||
#: src/main/help.c:54
|
||||
msgid "do not output any transfer information at all"
|
||||
msgstr "sämtliche Transferinformationen unterdrücken"
|
||||
|
||||
#: src/main/help.c:57
|
||||
msgid "display nothing until first byte transferred"
|
||||
msgstr "keine Ausgabe bevor das erste Byte übertragen wurde"
|
||||
|
||||
#: src/main/help.c:58 src/main/help.c:66
|
||||
msgid "SEC"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:59
|
||||
#, fuzzy
|
||||
msgid "display nothing until SEC seconds have passed"
|
||||
msgstr "keine Ausgabe bevor das erste Byte übertragen wurde"
|
||||
|
||||
#: src/main/help.c:60
|
||||
msgid "SIZE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:61
|
||||
msgid "set estimated data size to SIZE bytes"
|
||||
msgstr "setze erwartete Daten-Länge auf SIZE Byte"
|
||||
|
||||
#: src/main/help.c:63
|
||||
msgid "count lines instead of bytes"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:65
|
||||
msgid "lines are null-terminated"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:67
|
||||
msgid "update every SEC seconds"
|
||||
msgstr "aktualisiere Ausgabe nach SEC Sekunden Intervall"
|
||||
|
||||
#: src/main/help.c:68
|
||||
msgid "WIDTH"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:69
|
||||
msgid "assume terminal is WIDTH characters wide"
|
||||
msgstr "setze Terminal-Breite auf WIDTH Zeichen"
|
||||
|
||||
#: src/main/help.c:70
|
||||
msgid "HEIGHT"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:71
|
||||
msgid "assume terminal is HEIGHT rows high"
|
||||
msgstr "setze Terminal-Breite auf WIDTH Zeichen"
|
||||
|
||||
#: src/main/help.c:72
|
||||
msgid "NAME"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:73
|
||||
msgid "prefix visual information with NAME"
|
||||
msgstr "setze den NAMEn für visuelle Darstellung"
|
||||
|
||||
#: src/main/help.c:75
|
||||
msgid "output even if standard error is not a terminal"
|
||||
msgstr ""
|
||||
"Ausgabe auch dann erzwingen, wenn der Fehlerausgabe-Kanal kein Terminal ist"
|
||||
|
||||
#: src/main/help.c:77
|
||||
msgid "use cursor positioning escape sequences"
|
||||
msgstr "benutze Escape-Sequenzen zur Cursor-Positionierung"
|
||||
|
||||
#: src/main/help.c:79
|
||||
msgid "RATE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:80
|
||||
msgid "limit transfer to RATE bytes per second"
|
||||
msgstr "beschränke die Transferrate auf RATE Byte pro Sekunde"
|
||||
|
||||
#: src/main/help.c:81
|
||||
msgid "BYTES"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:82
|
||||
msgid "use a buffer size of BYTES"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:84
|
||||
msgid "never use splice(), always use read/write"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:86
|
||||
msgid "skip read errors in input"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:88
|
||||
#, fuzzy
|
||||
msgid "stop after --size bytes have been transferred"
|
||||
msgstr "zeige die Anzahl von Bytes, die transferiert worden sind"
|
||||
|
||||
#: src/main/help.c:90
|
||||
msgid "PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:91
|
||||
msgid "update settings of process PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:94
|
||||
msgid "FILE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:95
|
||||
msgid "save process ID in FILE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:97
|
||||
msgid "PID[:FD]"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:98
|
||||
msgid "watch file FD opened by process PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:101
|
||||
msgid "show this help and exit"
|
||||
msgstr "zeige diese Hilfe und beende"
|
||||
|
||||
#: src/main/help.c:103
|
||||
msgid "show version information and exit"
|
||||
msgstr "zeige Versionsinformationen und beende"
|
||||
|
||||
#: src/main/help.c:109
|
||||
#, c-format
|
||||
msgid "Usage: %s [OPTION] [FILE]..."
|
||||
msgstr "Syntax: %s [OPTION] [DATEI]..."
|
||||
|
||||
#: src/main/help.c:112
|
||||
msgid ""
|
||||
"Concatenate FILE(s), or standard input, to standard output,\n"
|
||||
"with monitoring."
|
||||
msgstr ""
|
||||
"Verbindet DATEI(en) oder den Standard-Eingabe-Kanal mit dem\n"
|
||||
"Standard-Ausgabe-Kanal und misst den Datenstrom."
|
||||
|
||||
#: src/main/help.c:201
|
||||
msgid ""
|
||||
"Debugging is enabled; export the DEBUG environment variable to define the\n"
|
||||
"output filename.\n"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:205
|
||||
#, c-format
|
||||
msgid "Please report any bugs to %s."
|
||||
msgstr "Bitte senden Sie Fehlerberichte an %s."
|
||||
|
||||
#~ msgid "failed to lock terminal"
|
||||
#~ msgstr "Terminal konnte nicht geöffnet werden"
|
||||
+454
@@ -0,0 +1,454 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-06-30 22:21+0100\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Date: 1999-06-01 15:18:29+0100\n"
|
||||
"From: Andrew Wood <ivarch@ubik>\n"
|
||||
"Xgettext-Options: --default-domain=pkgbuild --directory=./.pkgdir --add-"
|
||||
"comments --keyword=_ --keyword=N_\n"
|
||||
"Files: src/getopt/getopt.c src/getopt/getopt1.c src/version.c src/main/init."
|
||||
"c src/main/help.c src/main/xpmptr.c src/guts/load.c src/guts/stop.c src/guts/"
|
||||
"configure.c src/guts/make.c src/guts/install.c src/guts/prefs.c src/ui/"
|
||||
"generate.c src/ui/main.c src/ui/xpm.c src/ui/status.c src/ui/activate.c src/"
|
||||
"ui/handlers/main.c src/ui/handlers/package.c src/ui/handlers/prefs.c src/ui/"
|
||||
"handlers/dirsel.c src/ui/handlers/popup.c src/ui/handlers/help.c src/ui/"
|
||||
"textout.c src/ui/report.c src/ui/callback.c src/nls/intl/bindtextdom.c src/"
|
||||
"nls/intl/dcgettext.c src/nls/intl/dgettext.c src/nls/intl/finddomain.c src/"
|
||||
"nls/intl/gettext.c src/nls/intl/loadmsgcat.c src/nls/intl/localealias.c src/"
|
||||
"nls/intl/textdomain.c src/nls/intl-cat/cat-compat.c src/nls/intl-gett/intl-"
|
||||
"compat.c\n"
|
||||
|
||||
#: src/pv/watchpid.c:51 src/pv/watchpid.c:66 src/pv/watchpid.c:76
|
||||
#: src/pv/watchpid.c:107 src/pv/loop.c:519 src/pv/loop.c:580 src/pv/loop.c:632
|
||||
msgid "pid"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/watchpid.c:68 src/pv/watchpid.c:78 src/pv/watchpid.c:109
|
||||
msgid "fd"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/watchpid.c:112
|
||||
msgid "not a regular file or block device"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/cursor.c:53
|
||||
#, fuzzy
|
||||
msgid "failed to get terminal name"
|
||||
msgstr "l'ouverture du terminal a échoué"
|
||||
|
||||
#: src/pv/cursor.c:96
|
||||
#, fuzzy
|
||||
msgid "failed to open lock file"
|
||||
msgstr "la fermeture du fichier a échoué"
|
||||
|
||||
#: src/pv/cursor.c:129
|
||||
msgid "lock attempt failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/cursor.c:369
|
||||
msgid "failed to open terminal"
|
||||
msgstr "l'ouverture du terminal a échoué"
|
||||
|
||||
#: src/pv/file.c:118
|
||||
#, fuzzy
|
||||
msgid "failed to seek to start of output"
|
||||
msgstr "échec à statuer sur le fichier de sortie"
|
||||
|
||||
#: src/pv/file.c:214
|
||||
msgid "failed to close file"
|
||||
msgstr "la fermeture du fichier a échoué"
|
||||
|
||||
#: src/pv/file.c:237
|
||||
msgid "failed to read file"
|
||||
msgstr "la lecture du fichier a échoué"
|
||||
|
||||
#: src/pv/file.c:247
|
||||
msgid "failed to stat file"
|
||||
msgstr "échec à statuer sur le fichier"
|
||||
|
||||
#: src/pv/file.c:256
|
||||
msgid "failed to stat output file"
|
||||
msgstr "échec à statuer sur le fichier de sortie"
|
||||
|
||||
#: src/pv/file.c:279
|
||||
msgid "input file is output file"
|
||||
msgstr "fichiers d'entré et de sortie sont les mêmes"
|
||||
|
||||
#: src/pv/display.c:117
|
||||
msgid "yzafpnum kMGTPEZY"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:122
|
||||
msgid "yzafpnum KMGTPEZY"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:532 src/pv/transfer.c:679
|
||||
msgid "buffer allocation failed"
|
||||
msgstr "l'allocation de mémoire tampon a échoué"
|
||||
|
||||
#: src/pv/display.c:592 src/pv/transfer.c:458
|
||||
msgid "B"
|
||||
msgstr "O"
|
||||
|
||||
#: src/pv/display.c:628 src/pv/display.c:636
|
||||
msgid "/s"
|
||||
msgstr "/s"
|
||||
|
||||
#: src/pv/display.c:628 src/pv/display.c:636
|
||||
#, fuzzy
|
||||
msgid "B/s"
|
||||
msgstr "O/s"
|
||||
|
||||
#: src/pv/display.c:671 src/pv/display.c:676 src/pv/display.c:738
|
||||
msgid "ETA"
|
||||
msgstr "ETA"
|
||||
|
||||
#: src/pv/transfer.c:341
|
||||
msgid "read failed"
|
||||
msgstr "la lecture a échoué"
|
||||
|
||||
#: src/pv/transfer.c:359
|
||||
msgid "warning: read errors detected"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:374
|
||||
msgid "file is not seekable"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:437
|
||||
#, fuzzy
|
||||
msgid "failed to seek past error"
|
||||
msgstr "échec à statuer sur le fichier"
|
||||
|
||||
#: src/pv/transfer.c:456
|
||||
msgid "skipped past read error"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:631
|
||||
msgid "write failed"
|
||||
msgstr "l'écriture a échoué"
|
||||
|
||||
#: src/pv/transfer.c:770
|
||||
msgid "select call failed"
|
||||
msgstr "appel de sélection a échoué"
|
||||
|
||||
#: src/pv/state.c:33
|
||||
msgid "none"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/version.c:17
|
||||
#, fuzzy, c-format
|
||||
msgid "%s %s - Copyright %s %s"
|
||||
msgstr "%s %s - Copyright(C) %s %s"
|
||||
|
||||
#: src/main/version.c:20
|
||||
#, c-format
|
||||
msgid "Web site: %s"
|
||||
msgstr "Site Web: %s"
|
||||
|
||||
#: src/main/version.c:23
|
||||
msgid ""
|
||||
"This program is free software, and is being distributed under the\n"
|
||||
"terms of the Artistic License 2.0."
|
||||
msgstr ""
|
||||
"Ce programme est un logiciel libre et est distribué sous les termes\n"
|
||||
"anglophone de la Licence Artistique 2.0 (Artistic License 2.0)."
|
||||
|
||||
#: src/main/version.c:28
|
||||
msgid ""
|
||||
"This program is distributed in the hope that it will be useful,\n"
|
||||
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
||||
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||
msgstr ""
|
||||
"Ce programme est distribué dans l'espoir qu'il sera utile, mais\n"
|
||||
"SANS GARANTIE AUCUNE; ni même de garantie sous-entendu que le\n"
|
||||
"programme soit COMMERCIALISABLE ou APPLICABLE À UNE TÂCHE\n"
|
||||
"PARTICULIÈRE."
|
||||
|
||||
#: src/main/main.c:73
|
||||
#, fuzzy
|
||||
msgid "state allocation failed"
|
||||
msgstr "l'allocation de mémoire tampon a échoué"
|
||||
|
||||
#: src/main/options.c:98
|
||||
#, c-format
|
||||
msgid "%s: option structure allocation failed (%s)"
|
||||
msgstr "%s: l'allocation pour la structure d'une option a echoué (%s)"
|
||||
|
||||
#: src/main/options.c:114
|
||||
#, c-format
|
||||
msgid "%s: option structure argv allocation failed (%s)"
|
||||
msgstr "%s: l'allocation pour la structure de l'option argv a echoué (%s)"
|
||||
|
||||
#: src/main/options.c:154
|
||||
msgid "integer argument expected"
|
||||
msgstr "Valeur entière attendue"
|
||||
|
||||
#: src/main/options.c:165
|
||||
msgid "numeric argument expected"
|
||||
msgstr "Valeur numérique attendue"
|
||||
|
||||
#: src/main/options.c:176
|
||||
msgid "process ID or pid:fd pair expected"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:183
|
||||
msgid "invalid process ID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:317
|
||||
#, c-format
|
||||
msgid "Try `%s --help' for more information."
|
||||
msgstr "Essayez `%s --help' pour plus d'information."
|
||||
|
||||
#: src/main/options.c:321
|
||||
#, fuzzy, c-format
|
||||
msgid "Try `%s -h' for more information."
|
||||
msgstr "Essayez `%s -h' pour plus d'information."
|
||||
|
||||
#: src/main/options.c:338
|
||||
#, c-format
|
||||
msgid ""
|
||||
"%s: cannot use line mode or transfer modifier options when watching file "
|
||||
"descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:348
|
||||
#, c-format
|
||||
msgid "%s: cannot use cursor positioning when watching file descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:358
|
||||
#, c-format
|
||||
msgid "%s: cannot use remote control when watching file descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:368
|
||||
#, c-format
|
||||
msgid "%s: cannot transfer files when watching file descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:378
|
||||
#, c-format
|
||||
msgid "%s: -d: not available on systems without /proc/self/fdinfo"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/remote.c:218
|
||||
msgid "message not received"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/remote.c:324
|
||||
msgid "IPC not supported on this system"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:31
|
||||
msgid "show progress bar"
|
||||
msgstr "affiche la barre de progression"
|
||||
|
||||
#: src/main/help.c:33
|
||||
msgid "show elapsed time"
|
||||
msgstr "affiche le temps écoulé"
|
||||
|
||||
#: src/main/help.c:35
|
||||
msgid "show estimated time of arrival (completion)"
|
||||
msgstr "affiche l'heure approximative de l'achèvement de la tâche"
|
||||
|
||||
#: src/main/help.c:38
|
||||
#, fuzzy
|
||||
msgid "show absolute estimated time of arrival (completion)"
|
||||
msgstr "affiche l'heure approximative de l'achèvement de la tâche"
|
||||
|
||||
#: src/main/help.c:40
|
||||
msgid "show data transfer rate counter"
|
||||
msgstr "affiche le taux de tranfert des données"
|
||||
|
||||
#: src/main/help.c:42
|
||||
msgid "show data transfer average rate counter"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:44
|
||||
msgid "show number of bytes transferred"
|
||||
msgstr "affiche le nombre d'octets transférés"
|
||||
|
||||
#: src/main/help.c:46
|
||||
msgid "show percentage of transfer buffer in use"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:47
|
||||
msgid "NUM"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:48
|
||||
msgid "show NUM bytes last written"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:49
|
||||
#, fuzzy
|
||||
msgid "FORMAT"
|
||||
msgstr "TAUX"
|
||||
|
||||
#: src/main/help.c:50
|
||||
msgid "set output format to FORMAT"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:52
|
||||
msgid "output percentages, not visual information"
|
||||
msgstr "imprime en pourcentage, pas les informations visuelles"
|
||||
|
||||
#: src/main/help.c:54
|
||||
msgid "do not output any transfer information at all"
|
||||
msgstr "n'afficher aucune information de transfert"
|
||||
|
||||
#: src/main/help.c:57
|
||||
msgid "display nothing until first byte transferred"
|
||||
msgstr "ne rien afficher avant qu'au moins un octet soit tranféré"
|
||||
|
||||
#: src/main/help.c:58 src/main/help.c:66
|
||||
msgid "SEC"
|
||||
msgstr "SEC"
|
||||
|
||||
#: src/main/help.c:59
|
||||
#, fuzzy
|
||||
msgid "display nothing until SEC seconds have passed"
|
||||
msgstr "ne rien afficher avant qu'au moins un octet soit tranféré"
|
||||
|
||||
#: src/main/help.c:60
|
||||
msgid "SIZE"
|
||||
msgstr "TAILLE"
|
||||
|
||||
#: src/main/help.c:61
|
||||
msgid "set estimated data size to SIZE bytes"
|
||||
msgstr "ajuste la taille estimée des données à TAILLE octets"
|
||||
|
||||
#: src/main/help.c:63
|
||||
msgid "count lines instead of bytes"
|
||||
msgstr "compte les lignes au lieu des octets"
|
||||
|
||||
#: src/main/help.c:65
|
||||
msgid "lines are null-terminated"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:67
|
||||
msgid "update every SEC seconds"
|
||||
msgstr "mise-à-jour toute les SEC secondes"
|
||||
|
||||
#: src/main/help.c:68
|
||||
msgid "WIDTH"
|
||||
msgstr "LARGEUR"
|
||||
|
||||
#: src/main/help.c:69
|
||||
msgid "assume terminal is WIDTH characters wide"
|
||||
msgstr "présumer la largeur du terminal à LARGEUR caractères"
|
||||
|
||||
#: src/main/help.c:70
|
||||
msgid "HEIGHT"
|
||||
msgstr "HAUTEUR"
|
||||
|
||||
#: src/main/help.c:71
|
||||
msgid "assume terminal is HEIGHT rows high"
|
||||
msgstr "présumer la hauteur du terminal à HAUTEUR lignes"
|
||||
|
||||
#: src/main/help.c:72
|
||||
msgid "NAME"
|
||||
msgstr "NOM"
|
||||
|
||||
#: src/main/help.c:73
|
||||
msgid "prefix visual information with NAME"
|
||||
msgstr "préfixer les informations visuelles avec NOM"
|
||||
|
||||
#: src/main/help.c:75
|
||||
msgid "output even if standard error is not a terminal"
|
||||
msgstr "imprime vers la sortie d'erreur même si ce n'est pas un terminal"
|
||||
|
||||
#: src/main/help.c:77
|
||||
msgid "use cursor positioning escape sequences"
|
||||
msgstr "utiliser les séquences d'échappements de positionnement de curseur"
|
||||
|
||||
#: src/main/help.c:79
|
||||
msgid "RATE"
|
||||
msgstr "TAUX"
|
||||
|
||||
#: src/main/help.c:80
|
||||
msgid "limit transfer to RATE bytes per second"
|
||||
msgstr "limite le taux de transfer à TAUX octets par seconde"
|
||||
|
||||
#: src/main/help.c:81
|
||||
msgid "BYTES"
|
||||
msgstr "OCTETS"
|
||||
|
||||
#: src/main/help.c:82
|
||||
msgid "use a buffer size of BYTES"
|
||||
msgstr "Utiliser une mémoire tampon de OCTETS octets"
|
||||
|
||||
#: src/main/help.c:84
|
||||
msgid "never use splice(), always use read/write"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:86
|
||||
msgid "skip read errors in input"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:88
|
||||
#, fuzzy
|
||||
msgid "stop after --size bytes have been transferred"
|
||||
msgstr "affiche le nombre d'octets transférés"
|
||||
|
||||
#: src/main/help.c:90
|
||||
msgid "PID"
|
||||
msgstr "PID"
|
||||
|
||||
#: src/main/help.c:91
|
||||
msgid "update settings of process PID"
|
||||
msgstr "mettre-à-jour la configuration du processus PID"
|
||||
|
||||
#: src/main/help.c:94
|
||||
msgid "FILE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:95
|
||||
msgid "save process ID in FILE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:97
|
||||
msgid "PID[:FD]"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:98
|
||||
msgid "watch file FD opened by process PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:101
|
||||
msgid "show this help and exit"
|
||||
msgstr "afficher cette aide puis quitter"
|
||||
|
||||
#: src/main/help.c:103
|
||||
msgid "show version information and exit"
|
||||
msgstr "afficher la version puis quitter"
|
||||
|
||||
#: src/main/help.c:109
|
||||
#, c-format
|
||||
msgid "Usage: %s [OPTION] [FILE]..."
|
||||
msgstr "Utilisation : %s [OPTIONS] [FICHIER]..."
|
||||
|
||||
#: src/main/help.c:112
|
||||
msgid ""
|
||||
"Concatenate FILE(s), or standard input, to standard output,\n"
|
||||
"with monitoring."
|
||||
msgstr ""
|
||||
"Concatène FICHIER(s), ou l'entrée standard, sur la sortie standard\n"
|
||||
"avec monitorage."
|
||||
|
||||
#: src/main/help.c:201
|
||||
msgid ""
|
||||
"Debugging is enabled; export the DEBUG environment variable to define the\n"
|
||||
"output filename.\n"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:205
|
||||
#, c-format
|
||||
msgid "Please report any bugs to %s."
|
||||
msgstr "SVP rapporter touts bogues à %s."
|
||||
|
||||
#~ msgid "failed to lock terminal"
|
||||
#~ msgstr "l'ouverture du terminal a échoué"
|
||||
+491
@@ -0,0 +1,491 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR Free Software Foundation, Inc.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-06-30 22:21+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-2\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: src/pv/watchpid.c:51 src/pv/watchpid.c:66 src/pv/watchpid.c:76
|
||||
#: src/pv/watchpid.c:107 src/pv/loop.c:519 src/pv/loop.c:580 src/pv/loop.c:632
|
||||
msgid "pid"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/watchpid.c:68 src/pv/watchpid.c:78 src/pv/watchpid.c:109
|
||||
msgid "fd"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/watchpid.c:112
|
||||
msgid "not a regular file or block device"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/cursor.c:53
|
||||
#, fuzzy
|
||||
msgid "failed to get terminal name"
|
||||
msgstr "nie uda³o siê otworzyæ terminala"
|
||||
|
||||
# "Przewidywany czas ukoñczenia" for ETA is too long
|
||||
#: src/pv/cursor.c:96
|
||||
#, fuzzy
|
||||
msgid "failed to open lock file"
|
||||
msgstr "nie uda³o siê zamkn±æ pliku"
|
||||
|
||||
#: src/pv/cursor.c:129
|
||||
msgid "lock attempt failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/cursor.c:369
|
||||
msgid "failed to open terminal"
|
||||
msgstr "nie uda³o siê otworzyæ terminala"
|
||||
|
||||
#: src/pv/file.c:118
|
||||
#, fuzzy
|
||||
msgid "failed to seek to start of output"
|
||||
msgstr "nie uda³o siê wykonaæ operacji stat na pliku wyj¶ciowym"
|
||||
|
||||
# "Przewidywany czas ukoñczenia" for ETA is too long
|
||||
#: src/pv/file.c:214
|
||||
msgid "failed to close file"
|
||||
msgstr "nie uda³o siê zamkn±æ pliku"
|
||||
|
||||
#: src/pv/file.c:237
|
||||
msgid "failed to read file"
|
||||
msgstr "nie uda³o siê odczytaæ pliku"
|
||||
|
||||
#: src/pv/file.c:247
|
||||
msgid "failed to stat file"
|
||||
msgstr "nie uda³o siê wykonaæ operacji stat na pliku"
|
||||
|
||||
#: src/pv/file.c:256
|
||||
msgid "failed to stat output file"
|
||||
msgstr "nie uda³o siê wykonaæ operacji stat na pliku wyj¶ciowym"
|
||||
|
||||
#: src/pv/file.c:279
|
||||
msgid "input file is output file"
|
||||
msgstr "plik wej¶ciowy jest zarazem plikiem wyj¶ciowym"
|
||||
|
||||
#: src/pv/display.c:117
|
||||
msgid "yzafpnum kMGTPEZY"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:122
|
||||
msgid "yzafpnum KMGTPEZY"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:532 src/pv/transfer.c:679
|
||||
msgid "buffer allocation failed"
|
||||
msgstr "nie uda³o siê zaalokowaæ bufora"
|
||||
|
||||
#: src/pv/display.c:592 src/pv/transfer.c:458
|
||||
msgid "B"
|
||||
msgstr "B"
|
||||
|
||||
#: src/pv/display.c:628 src/pv/display.c:636
|
||||
msgid "/s"
|
||||
msgstr "/s"
|
||||
|
||||
#: src/pv/display.c:628 src/pv/display.c:636
|
||||
msgid "B/s"
|
||||
msgstr "B/s"
|
||||
|
||||
#: src/pv/display.c:671 src/pv/display.c:676 src/pv/display.c:738
|
||||
msgid "ETA"
|
||||
msgstr "ETA"
|
||||
|
||||
#: src/pv/transfer.c:341
|
||||
msgid "read failed"
|
||||
msgstr "b³±d odczytu"
|
||||
|
||||
#: src/pv/transfer.c:359
|
||||
msgid "warning: read errors detected"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:374
|
||||
msgid "file is not seekable"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:437
|
||||
#, fuzzy
|
||||
msgid "failed to seek past error"
|
||||
msgstr "nie uda³o siê wykonaæ operacji stat na pliku"
|
||||
|
||||
#: src/pv/transfer.c:456
|
||||
msgid "skipped past read error"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:631
|
||||
msgid "write failed"
|
||||
msgstr "b³±d zapisu"
|
||||
|
||||
#: src/pv/transfer.c:770
|
||||
msgid "select call failed"
|
||||
msgstr "nie uda³o siê wywo³aæ funkcji select"
|
||||
|
||||
#: src/pv/state.c:33
|
||||
msgid "none"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/version.c:17
|
||||
#, fuzzy, c-format
|
||||
msgid "%s %s - Copyright %s %s"
|
||||
msgstr "%s %s - Prawa autorskie (C) %s %s"
|
||||
|
||||
#: src/main/version.c:20
|
||||
#, c-format
|
||||
msgid "Web site: %s"
|
||||
msgstr "Strona WWW: %s"
|
||||
|
||||
#: src/main/version.c:23
|
||||
msgid ""
|
||||
"This program is free software, and is being distributed under the\n"
|
||||
"terms of the Artistic License 2.0."
|
||||
msgstr ""
|
||||
|
||||
#: src/main/version.c:28
|
||||
msgid ""
|
||||
"This program is distributed in the hope that it will be useful,\n"
|
||||
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
||||
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||
msgstr ""
|
||||
|
||||
#: src/main/main.c:73
|
||||
#, fuzzy
|
||||
msgid "state allocation failed"
|
||||
msgstr "nie uda³o siê zaalokowaæ bufora"
|
||||
|
||||
#: src/main/options.c:98
|
||||
#, c-format
|
||||
msgid "%s: option structure allocation failed (%s)"
|
||||
msgstr "%s: nie uda³o siê zaalokowaæ struktur opcji (%s)"
|
||||
|
||||
#: src/main/options.c:114
|
||||
#, c-format
|
||||
msgid "%s: option structure argv allocation failed (%s)"
|
||||
msgstr "%s: nie uda³o siê zaalokowaæ struktur opcji argv (%s)"
|
||||
|
||||
#: src/main/options.c:154
|
||||
msgid "integer argument expected"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:165
|
||||
msgid "numeric argument expected"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:176
|
||||
msgid "process ID or pid:fd pair expected"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:183
|
||||
msgid "invalid process ID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:317
|
||||
#, c-format
|
||||
msgid "Try `%s --help' for more information."
|
||||
msgstr "Spróbuj `%s --help' by uzyskaæ wiêcej informacji"
|
||||
|
||||
#: src/main/options.c:321
|
||||
#, fuzzy, c-format
|
||||
msgid "Try `%s -h' for more information."
|
||||
msgstr "Spróbuj `%s -h' by uzyskaæ wiêcej informacji"
|
||||
|
||||
#: src/main/options.c:338
|
||||
#, c-format
|
||||
msgid ""
|
||||
"%s: cannot use line mode or transfer modifier options when watching file "
|
||||
"descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:348
|
||||
#, c-format
|
||||
msgid "%s: cannot use cursor positioning when watching file descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:358
|
||||
#, c-format
|
||||
msgid "%s: cannot use remote control when watching file descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:368
|
||||
#, c-format
|
||||
msgid "%s: cannot transfer files when watching file descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:378
|
||||
#, c-format
|
||||
msgid "%s: -d: not available on systems without /proc/self/fdinfo"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/remote.c:218
|
||||
msgid "message not received"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/remote.c:324
|
||||
msgid "IPC not supported on this system"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:31
|
||||
msgid "show progress bar"
|
||||
msgstr "poka¿ pasek postêpu"
|
||||
|
||||
#: src/main/help.c:33
|
||||
msgid "show elapsed time"
|
||||
msgstr "poka¿ up³ywaj±cy czas"
|
||||
|
||||
#: src/main/help.c:35
|
||||
msgid "show estimated time of arrival (completion)"
|
||||
msgstr "poka¿ szacowany czas ukoñczenia"
|
||||
|
||||
#: src/main/help.c:38
|
||||
#, fuzzy
|
||||
msgid "show absolute estimated time of arrival (completion)"
|
||||
msgstr "poka¿ szacowany czas ukoñczenia"
|
||||
|
||||
#: src/main/help.c:40
|
||||
msgid "show data transfer rate counter"
|
||||
msgstr "poka¿ licznik prêdko¶ci przesy³ania"
|
||||
|
||||
#: src/main/help.c:42
|
||||
msgid "show data transfer average rate counter"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:44
|
||||
msgid "show number of bytes transferred"
|
||||
msgstr "poka¿ ilo¶æ przes³anych bajtów"
|
||||
|
||||
#: src/main/help.c:46
|
||||
msgid "show percentage of transfer buffer in use"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:47
|
||||
msgid "NUM"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:48
|
||||
msgid "show NUM bytes last written"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:49
|
||||
msgid "FORMAT"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:50
|
||||
msgid "set output format to FORMAT"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:52
|
||||
msgid "output percentages, not visual information"
|
||||
msgstr "w¶wietl wyj¶cie procentowo, bez graficznej prezentacji"
|
||||
|
||||
#: src/main/help.c:54
|
||||
msgid "do not output any transfer information at all"
|
||||
msgstr "nie wy¶wietlaj ¿adnych informacji o przesy³aniu"
|
||||
|
||||
#: src/main/help.c:57
|
||||
msgid "display nothing until first byte transferred"
|
||||
msgstr "nie wy¶wietlaj nic a¿ do pierwszego przes³anego bajtu"
|
||||
|
||||
#: src/main/help.c:58 src/main/help.c:66
|
||||
msgid "SEC"
|
||||
msgstr "WARTO¦Æ"
|
||||
|
||||
#: src/main/help.c:59
|
||||
#, fuzzy
|
||||
msgid "display nothing until SEC seconds have passed"
|
||||
msgstr "nie wy¶wietlaj nic a¿ do pierwszego przes³anego bajtu"
|
||||
|
||||
#: src/main/help.c:60
|
||||
msgid "SIZE"
|
||||
msgstr "WARTO¦Æ"
|
||||
|
||||
#: src/main/help.c:61
|
||||
msgid "set estimated data size to SIZE bytes"
|
||||
msgstr "ustaw oczekiwany rozmiar danych na WARTO¦Æ bajtów"
|
||||
|
||||
#: src/main/help.c:63
|
||||
msgid "count lines instead of bytes"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:65
|
||||
msgid "lines are null-terminated"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:67
|
||||
msgid "update every SEC seconds"
|
||||
msgstr "aktualizuj co ka¿de WARTO¦Æ sekund"
|
||||
|
||||
#: src/main/help.c:68
|
||||
msgid "WIDTH"
|
||||
msgstr "WARTO¦Æ"
|
||||
|
||||
#: src/main/help.c:69
|
||||
msgid "assume terminal is WIDTH characters wide"
|
||||
msgstr "przyjmij, ¿e terminal ma szeroko¶æ WARTO¦Æ znaków"
|
||||
|
||||
#: src/main/help.c:70
|
||||
msgid "HEIGHT"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:71
|
||||
msgid "assume terminal is HEIGHT rows high"
|
||||
msgstr "przyjmij, ¿e terminal ma szeroko¶æ WARTO¦Æ znaków"
|
||||
|
||||
#: src/main/help.c:72
|
||||
msgid "NAME"
|
||||
msgstr "NAZWA"
|
||||
|
||||
#: src/main/help.c:73
|
||||
msgid "prefix visual information with NAME"
|
||||
msgstr "poprzed¼ informacje prefiksem NAZWA"
|
||||
|
||||
#: src/main/help.c:75
|
||||
msgid "output even if standard error is not a terminal"
|
||||
msgstr "poka¿ wyj¶cie nawet gdy b³êdy nie s± typu terminal"
|
||||
|
||||
#: src/main/help.c:77
|
||||
msgid "use cursor positioning escape sequences"
|
||||
msgstr "u¿ywaj sekwencji escape do pozycjonowania"
|
||||
|
||||
#: src/main/help.c:79
|
||||
msgid "RATE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:80
|
||||
msgid "limit transfer to RATE bytes per second"
|
||||
msgstr "ogranicz przesy³ane dane do RATE bajtów na sekundê"
|
||||
|
||||
#: src/main/help.c:81
|
||||
msgid "BYTES"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:82
|
||||
msgid "use a buffer size of BYTES"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:84
|
||||
msgid "never use splice(), always use read/write"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:86
|
||||
msgid "skip read errors in input"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:88
|
||||
#, fuzzy
|
||||
msgid "stop after --size bytes have been transferred"
|
||||
msgstr "poka¿ ilo¶æ przes³anych bajtów"
|
||||
|
||||
#: src/main/help.c:90
|
||||
msgid "PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:91
|
||||
msgid "update settings of process PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:94
|
||||
msgid "FILE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:95
|
||||
msgid "save process ID in FILE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:97
|
||||
msgid "PID[:FD]"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:98
|
||||
msgid "watch file FD opened by process PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:101
|
||||
msgid "show this help and exit"
|
||||
msgstr "wy¶wietl te informacje z pomoc± i wyjd¼"
|
||||
|
||||
#: src/main/help.c:103
|
||||
msgid "show version information and exit"
|
||||
msgstr "wy¶wietl informacje o wersji i wyjd¼"
|
||||
|
||||
#: src/main/help.c:109
|
||||
#, c-format
|
||||
msgid "Usage: %s [OPTION] [FILE]..."
|
||||
msgstr "Sposób u¿ycia: %s [OPCJA] [PLIK]..."
|
||||
|
||||
#: src/main/help.c:112
|
||||
msgid ""
|
||||
"Concatenate FILE(s), or standard input, to standard output,\n"
|
||||
"with monitoring."
|
||||
msgstr ""
|
||||
"£±czenie PLIK(ów) lub wej¶cia standardowego, do wyj¶cia standardowego\n"
|
||||
"z monitoringiem."
|
||||
|
||||
#: src/main/help.c:201
|
||||
msgid ""
|
||||
"Debugging is enabled; export the DEBUG environment variable to define the\n"
|
||||
"output filename.\n"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:205
|
||||
#, c-format
|
||||
msgid "Please report any bugs to %s."
|
||||
msgstr "Proszê przesy³aæ zg³oszenia b³êdów do %s."
|
||||
|
||||
#~ msgid "failed to lock terminal"
|
||||
#~ msgstr "nie uda³o siê otworzyæ terminala"
|
||||
|
||||
#~ msgid "G"
|
||||
#~ msgstr "GB"
|
||||
|
||||
#~ msgid "M"
|
||||
#~ msgstr "MB"
|
||||
|
||||
#~ msgid "k"
|
||||
#~ msgstr "kB"
|
||||
|
||||
#~ msgid "GB"
|
||||
#~ msgstr "GB"
|
||||
|
||||
#~ msgid "MB"
|
||||
#~ msgstr "MB"
|
||||
|
||||
#~ msgid "kB"
|
||||
#~ msgstr "kB"
|
||||
|
||||
#~ msgid "M/s"
|
||||
#~ msgstr "MB/s"
|
||||
|
||||
#~ msgid "k/s"
|
||||
#~ msgstr "kB/s"
|
||||
|
||||
#~ msgid "/s "
|
||||
#~ msgstr "B/s"
|
||||
|
||||
#~ msgid "GB/s"
|
||||
#~ msgstr "GB/s"
|
||||
|
||||
#~ msgid "MB/s"
|
||||
#~ msgstr "MB/s"
|
||||
|
||||
#~ msgid "kB/s"
|
||||
#~ msgstr "kB/s"
|
||||
|
||||
#~ msgid "For more information, please run `%s --license'."
|
||||
#~ msgstr "By uzyskaæ wiêcej informacji wprowad¼ `%s --license'."
|
||||
|
||||
#~ msgid "For more information, please run `%s -l'."
|
||||
#~ msgstr "By uzyskaæ wiêcej informacji wprowad¼ `%s -l'."
|
||||
|
||||
#~ msgid "show the license this program is distributed under"
|
||||
#~ msgstr "wy¶wietl informacje o licencji pod któr± program jest rozprowadzany"
|
||||
|
||||
#~ msgid "%s %s - Copyright (C) %s %s"
|
||||
#~ msgstr "%s %s - Prawa autorskie (C) %s %s"
|
||||
+446
@@ -0,0 +1,446 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR Free Software Foundation, Inc.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-06-30 22:21+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ISO-8859-15\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: src/pv/watchpid.c:51 src/pv/watchpid.c:66 src/pv/watchpid.c:76
|
||||
#: src/pv/watchpid.c:107 src/pv/loop.c:519 src/pv/loop.c:580 src/pv/loop.c:632
|
||||
msgid "pid"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/watchpid.c:68 src/pv/watchpid.c:78 src/pv/watchpid.c:109
|
||||
msgid "fd"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/watchpid.c:112
|
||||
msgid "not a regular file or block device"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/cursor.c:53
|
||||
#, fuzzy
|
||||
msgid "failed to get terminal name"
|
||||
msgstr "erro abrindo o terminal"
|
||||
|
||||
#: src/pv/cursor.c:96
|
||||
#, fuzzy
|
||||
msgid "failed to open lock file"
|
||||
msgstr "erro fechando o arquivo"
|
||||
|
||||
#: src/pv/cursor.c:129
|
||||
msgid "lock attempt failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/cursor.c:369
|
||||
msgid "failed to open terminal"
|
||||
msgstr "erro abrindo o terminal"
|
||||
|
||||
#: src/pv/file.c:118
|
||||
#, fuzzy
|
||||
msgid "failed to seek to start of output"
|
||||
msgstr "erro obtendo informações do arquivo de saída"
|
||||
|
||||
#: src/pv/file.c:214
|
||||
msgid "failed to close file"
|
||||
msgstr "erro fechando o arquivo"
|
||||
|
||||
#: src/pv/file.c:237
|
||||
msgid "failed to read file"
|
||||
msgstr "erro lendo o arquivo"
|
||||
|
||||
#: src/pv/file.c:247
|
||||
msgid "failed to stat file"
|
||||
msgstr "erro obtendo informações do arquivo"
|
||||
|
||||
#: src/pv/file.c:256
|
||||
msgid "failed to stat output file"
|
||||
msgstr "erro obtendo informações do arquivo de saída"
|
||||
|
||||
#: src/pv/file.c:279
|
||||
msgid "input file is output file"
|
||||
msgstr "os arquivos de entrada e saída são o mesmo"
|
||||
|
||||
#: src/pv/display.c:117
|
||||
msgid "yzafpnum kMGTPEZY"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:122
|
||||
msgid "yzafpnum KMGTPEZY"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:532 src/pv/transfer.c:679
|
||||
msgid "buffer allocation failed"
|
||||
msgstr "erro alocando o buffer"
|
||||
|
||||
#: src/pv/display.c:592 src/pv/transfer.c:458
|
||||
msgid "B"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:628 src/pv/display.c:636
|
||||
msgid "/s"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:628 src/pv/display.c:636
|
||||
msgid "B/s"
|
||||
msgstr "B/s"
|
||||
|
||||
#: src/pv/display.c:671 src/pv/display.c:676 src/pv/display.c:738
|
||||
msgid "ETA"
|
||||
msgstr "ETA"
|
||||
|
||||
#: src/pv/transfer.c:341
|
||||
msgid "read failed"
|
||||
msgstr "erro de leitura"
|
||||
|
||||
#: src/pv/transfer.c:359
|
||||
msgid "warning: read errors detected"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:374
|
||||
msgid "file is not seekable"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:437
|
||||
#, fuzzy
|
||||
msgid "failed to seek past error"
|
||||
msgstr "erro obtendo informações do arquivo"
|
||||
|
||||
#: src/pv/transfer.c:456
|
||||
msgid "skipped past read error"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:631
|
||||
msgid "write failed"
|
||||
msgstr "erro de gravação"
|
||||
|
||||
#: src/pv/transfer.c:770
|
||||
msgid "select call failed"
|
||||
msgstr "erro na chamada da função select"
|
||||
|
||||
#: src/pv/state.c:33
|
||||
msgid "none"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/version.c:17
|
||||
#, fuzzy, c-format
|
||||
msgid "%s %s - Copyright %s %s"
|
||||
msgstr "%s %s - Copyright(C) %s %s"
|
||||
|
||||
#: src/main/version.c:20
|
||||
#, c-format
|
||||
msgid "Web site: %s"
|
||||
msgstr "Site: %s"
|
||||
|
||||
#: src/main/version.c:23
|
||||
msgid ""
|
||||
"This program is free software, and is being distributed under the\n"
|
||||
"terms of the Artistic License 2.0."
|
||||
msgstr ""
|
||||
"Este programa é um software livre, distribuído sob os termos da\n"
|
||||
"Licença Artística."
|
||||
|
||||
#: src/main/version.c:28
|
||||
msgid ""
|
||||
"This program is distributed in the hope that it will be useful,\n"
|
||||
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
||||
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||
msgstr ""
|
||||
"Este programa é distribuído com o intuito de que seja útil,\n"
|
||||
"porém SEM QUAISQUER GARANTIAS; sem inclusive as garantias implícitas de\n"
|
||||
"COMERCIALIZAÇÃO e ADEQUAÇÃO A OBJETIVOS PARTICULARES."
|
||||
|
||||
#: src/main/main.c:73
|
||||
#, fuzzy
|
||||
msgid "state allocation failed"
|
||||
msgstr "erro alocando o buffer"
|
||||
|
||||
#: src/main/options.c:98
|
||||
#, c-format
|
||||
msgid "%s: option structure allocation failed (%s)"
|
||||
msgstr "%s: erro na alocação da estrutura de opções (%s)"
|
||||
|
||||
#: src/main/options.c:114
|
||||
#, c-format
|
||||
msgid "%s: option structure argv allocation failed (%s)"
|
||||
msgstr "%s: erro na alocação da estrutura de opções argv (%s)"
|
||||
|
||||
#: src/main/options.c:154
|
||||
msgid "integer argument expected"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:165
|
||||
msgid "numeric argument expected"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:176
|
||||
msgid "process ID or pid:fd pair expected"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:183
|
||||
msgid "invalid process ID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:317
|
||||
#, c-format
|
||||
msgid "Try `%s --help' for more information."
|
||||
msgstr "Tente `%s --help' para maiores informações."
|
||||
|
||||
#: src/main/options.c:321
|
||||
#, fuzzy, c-format
|
||||
msgid "Try `%s -h' for more information."
|
||||
msgstr "Tente `%s -h' para maiores informações."
|
||||
|
||||
#: src/main/options.c:338
|
||||
#, c-format
|
||||
msgid ""
|
||||
"%s: cannot use line mode or transfer modifier options when watching file "
|
||||
"descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:348
|
||||
#, c-format
|
||||
msgid "%s: cannot use cursor positioning when watching file descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:358
|
||||
#, c-format
|
||||
msgid "%s: cannot use remote control when watching file descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:368
|
||||
#, c-format
|
||||
msgid "%s: cannot transfer files when watching file descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:378
|
||||
#, c-format
|
||||
msgid "%s: -d: not available on systems without /proc/self/fdinfo"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/remote.c:218
|
||||
msgid "message not received"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/remote.c:324
|
||||
msgid "IPC not supported on this system"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:31
|
||||
msgid "show progress bar"
|
||||
msgstr "exibe barra de progressão"
|
||||
|
||||
#: src/main/help.c:33
|
||||
msgid "show elapsed time"
|
||||
msgstr "exibe tempo passado"
|
||||
|
||||
#: src/main/help.c:35
|
||||
msgid "show estimated time of arrival (completion)"
|
||||
msgstr "exibe o tempo estimado de término"
|
||||
|
||||
#: src/main/help.c:38
|
||||
#, fuzzy
|
||||
msgid "show absolute estimated time of arrival (completion)"
|
||||
msgstr "exibe o tempo estimado de término"
|
||||
|
||||
#: src/main/help.c:40
|
||||
msgid "show data transfer rate counter"
|
||||
msgstr "exibe a taxa de transferência"
|
||||
|
||||
#: src/main/help.c:42
|
||||
msgid "show data transfer average rate counter"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:44
|
||||
msgid "show number of bytes transferred"
|
||||
msgstr "exibe a quantidade de bytes transferidos"
|
||||
|
||||
#: src/main/help.c:46
|
||||
msgid "show percentage of transfer buffer in use"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:47
|
||||
msgid "NUM"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:48
|
||||
msgid "show NUM bytes last written"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:49
|
||||
msgid "FORMAT"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:50
|
||||
msgid "set output format to FORMAT"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:52
|
||||
msgid "output percentages, not visual information"
|
||||
msgstr "exibe apenas as porcentagens, sem informações visuais"
|
||||
|
||||
#: src/main/help.c:54
|
||||
msgid "do not output any transfer information at all"
|
||||
msgstr "executa programa sem exibir quaisquer informações"
|
||||
|
||||
#: src/main/help.c:57
|
||||
msgid "display nothing until first byte transferred"
|
||||
msgstr "não exibe nada até iniciar o processamento"
|
||||
|
||||
#: src/main/help.c:58 src/main/help.c:66
|
||||
msgid "SEC"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:59
|
||||
#, fuzzy
|
||||
msgid "display nothing until SEC seconds have passed"
|
||||
msgstr "não exibe nada até iniciar o processamento"
|
||||
|
||||
#: src/main/help.c:60
|
||||
msgid "SIZE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:61
|
||||
msgid "set estimated data size to SIZE bytes"
|
||||
msgstr "seta a quantidade estimada de dados em SIZE bytes"
|
||||
|
||||
#: src/main/help.c:63
|
||||
msgid "count lines instead of bytes"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:65
|
||||
msgid "lines are null-terminated"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:67
|
||||
msgid "update every SEC seconds"
|
||||
msgstr "atualiza informações a cada SEC segundos"
|
||||
|
||||
#: src/main/help.c:68
|
||||
msgid "WIDTH"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:69
|
||||
msgid "assume terminal is WIDTH characters wide"
|
||||
msgstr "assume que terminal possui WIDTH caracteres de largura"
|
||||
|
||||
#: src/main/help.c:70
|
||||
msgid "HEIGHT"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:71
|
||||
msgid "assume terminal is HEIGHT rows high"
|
||||
msgstr "assume que terminal possui WIDTH caracteres de largura"
|
||||
|
||||
#: src/main/help.c:72
|
||||
msgid "NAME"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:73
|
||||
msgid "prefix visual information with NAME"
|
||||
msgstr "exibe NAME antes das demais informações"
|
||||
|
||||
#: src/main/help.c:75
|
||||
msgid "output even if standard error is not a terminal"
|
||||
msgstr "gera dados mesmo que a saída de erro seja redirecionada"
|
||||
|
||||
#: src/main/help.c:77
|
||||
msgid "use cursor positioning escape sequences"
|
||||
msgstr "utiliza caracteres de escape para posicionar o cursor"
|
||||
|
||||
#: src/main/help.c:79
|
||||
msgid "RATE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:80
|
||||
msgid "limit transfer to RATE bytes per second"
|
||||
msgstr "limita a transferência a RATE bytes por segundo"
|
||||
|
||||
#: src/main/help.c:81
|
||||
msgid "BYTES"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:82
|
||||
msgid "use a buffer size of BYTES"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:84
|
||||
msgid "never use splice(), always use read/write"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:86
|
||||
msgid "skip read errors in input"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:88
|
||||
#, fuzzy
|
||||
msgid "stop after --size bytes have been transferred"
|
||||
msgstr "exibe a quantidade de bytes transferidos"
|
||||
|
||||
#: src/main/help.c:90
|
||||
msgid "PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:91
|
||||
msgid "update settings of process PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:94
|
||||
msgid "FILE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:95
|
||||
msgid "save process ID in FILE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:97
|
||||
msgid "PID[:FD]"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:98
|
||||
msgid "watch file FD opened by process PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:101
|
||||
msgid "show this help and exit"
|
||||
msgstr "exibe esta tela de ajuda e termina"
|
||||
|
||||
#: src/main/help.c:103
|
||||
msgid "show version information and exit"
|
||||
msgstr "exibe a versão e termina"
|
||||
|
||||
#: src/main/help.c:109
|
||||
#, c-format
|
||||
msgid "Usage: %s [OPTION] [FILE]..."
|
||||
msgstr "Uso: %s [OPÇÕES] [ARQUIVOS]..."
|
||||
|
||||
#: src/main/help.c:112
|
||||
msgid ""
|
||||
"Concatenate FILE(s), or standard input, to standard output,\n"
|
||||
"with monitoring."
|
||||
msgstr ""
|
||||
"Concatena ARQUIVO(s) ou a entrada padrão e grava na saída padrão,\n"
|
||||
"com monitoramento."
|
||||
|
||||
#: src/main/help.c:201
|
||||
msgid ""
|
||||
"Debugging is enabled; export the DEBUG environment variable to define the\n"
|
||||
"output filename.\n"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:205
|
||||
#, c-format
|
||||
msgid "Please report any bugs to %s."
|
||||
msgstr "Por favor, reporte quaisquer defeitos para %s."
|
||||
|
||||
#~ msgid "failed to lock terminal"
|
||||
#~ msgstr "erro abrindo o terminal"
|
||||
+430
@@ -0,0 +1,430 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-06-30 22:21+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: src/pv/watchpid.c:51 src/pv/watchpid.c:66 src/pv/watchpid.c:76
|
||||
#: src/pv/watchpid.c:107 src/pv/loop.c:519 src/pv/loop.c:580 src/pv/loop.c:632
|
||||
msgid "pid"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/watchpid.c:68 src/pv/watchpid.c:78 src/pv/watchpid.c:109
|
||||
msgid "fd"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/watchpid.c:112
|
||||
msgid "not a regular file or block device"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/cursor.c:53
|
||||
msgid "failed to get terminal name"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/cursor.c:96
|
||||
msgid "failed to open lock file"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/cursor.c:129
|
||||
msgid "lock attempt failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/cursor.c:369
|
||||
msgid "failed to open terminal"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/file.c:118
|
||||
msgid "failed to seek to start of output"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/file.c:214
|
||||
msgid "failed to close file"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/file.c:237
|
||||
msgid "failed to read file"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/file.c:247
|
||||
msgid "failed to stat file"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/file.c:256
|
||||
msgid "failed to stat output file"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/file.c:279
|
||||
msgid "input file is output file"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:117
|
||||
msgid "yzafpnum kMGTPEZY"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:122
|
||||
msgid "yzafpnum KMGTPEZY"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:532 src/pv/transfer.c:679
|
||||
msgid "buffer allocation failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:592 src/pv/transfer.c:458
|
||||
msgid "B"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:628 src/pv/display.c:636
|
||||
msgid "/s"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:628 src/pv/display.c:636
|
||||
msgid "B/s"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/display.c:671 src/pv/display.c:676 src/pv/display.c:738
|
||||
msgid "ETA"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:341
|
||||
msgid "read failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:359
|
||||
msgid "warning: read errors detected"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:374
|
||||
msgid "file is not seekable"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:437
|
||||
msgid "failed to seek past error"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:456
|
||||
msgid "skipped past read error"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:631
|
||||
msgid "write failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:770
|
||||
msgid "select call failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/state.c:33
|
||||
msgid "none"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/version.c:17
|
||||
#, c-format
|
||||
msgid "%s %s - Copyright %s %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/version.c:20
|
||||
#, c-format
|
||||
msgid "Web site: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/version.c:23
|
||||
msgid ""
|
||||
"This program is free software, and is being distributed under the\n"
|
||||
"terms of the Artistic License 2.0."
|
||||
msgstr ""
|
||||
|
||||
#: src/main/version.c:28
|
||||
msgid ""
|
||||
"This program is distributed in the hope that it will be useful,\n"
|
||||
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
||||
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||
msgstr ""
|
||||
|
||||
#: src/main/main.c:73
|
||||
msgid "state allocation failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:98
|
||||
#, c-format
|
||||
msgid "%s: option structure allocation failed (%s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:114
|
||||
#, c-format
|
||||
msgid "%s: option structure argv allocation failed (%s)"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:154
|
||||
msgid "integer argument expected"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:165
|
||||
msgid "numeric argument expected"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:176
|
||||
msgid "process ID or pid:fd pair expected"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:183
|
||||
msgid "invalid process ID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:317
|
||||
#, c-format
|
||||
msgid "Try `%s --help' for more information."
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:321
|
||||
#, c-format
|
||||
msgid "Try `%s -h' for more information."
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:338
|
||||
#, c-format
|
||||
msgid ""
|
||||
"%s: cannot use line mode or transfer modifier options when watching file "
|
||||
"descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:348
|
||||
#, c-format
|
||||
msgid "%s: cannot use cursor positioning when watching file descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:358
|
||||
#, c-format
|
||||
msgid "%s: cannot use remote control when watching file descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:368
|
||||
#, c-format
|
||||
msgid "%s: cannot transfer files when watching file descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:378
|
||||
#, c-format
|
||||
msgid "%s: -d: not available on systems without /proc/self/fdinfo"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/remote.c:218
|
||||
msgid "message not received"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/remote.c:324
|
||||
msgid "IPC not supported on this system"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:31
|
||||
msgid "show progress bar"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:33
|
||||
msgid "show elapsed time"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:35
|
||||
msgid "show estimated time of arrival (completion)"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:38
|
||||
msgid "show absolute estimated time of arrival (completion)"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:40
|
||||
msgid "show data transfer rate counter"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:42
|
||||
msgid "show data transfer average rate counter"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:44
|
||||
msgid "show number of bytes transferred"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:46
|
||||
msgid "show percentage of transfer buffer in use"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:47
|
||||
msgid "NUM"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:48
|
||||
msgid "show NUM bytes last written"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:49
|
||||
msgid "FORMAT"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:50
|
||||
msgid "set output format to FORMAT"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:52
|
||||
msgid "output percentages, not visual information"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:54
|
||||
msgid "do not output any transfer information at all"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:57
|
||||
msgid "display nothing until first byte transferred"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:58 src/main/help.c:66
|
||||
msgid "SEC"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:59
|
||||
msgid "display nothing until SEC seconds have passed"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:60
|
||||
msgid "SIZE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:61
|
||||
msgid "set estimated data size to SIZE bytes"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:63
|
||||
msgid "count lines instead of bytes"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:65
|
||||
msgid "lines are null-terminated"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:67
|
||||
msgid "update every SEC seconds"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:68
|
||||
msgid "WIDTH"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:69
|
||||
msgid "assume terminal is WIDTH characters wide"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:70
|
||||
msgid "HEIGHT"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:71
|
||||
msgid "assume terminal is HEIGHT rows high"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:72
|
||||
msgid "NAME"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:73
|
||||
msgid "prefix visual information with NAME"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:75
|
||||
msgid "output even if standard error is not a terminal"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:77
|
||||
msgid "use cursor positioning escape sequences"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:79
|
||||
msgid "RATE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:80
|
||||
msgid "limit transfer to RATE bytes per second"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:81
|
||||
msgid "BYTES"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:82
|
||||
msgid "use a buffer size of BYTES"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:84
|
||||
msgid "never use splice(), always use read/write"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:86
|
||||
msgid "skip read errors in input"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:88
|
||||
msgid "stop after --size bytes have been transferred"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:90
|
||||
msgid "PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:91
|
||||
msgid "update settings of process PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:94
|
||||
msgid "FILE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:95
|
||||
msgid "save process ID in FILE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:97
|
||||
msgid "PID[:FD]"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:98
|
||||
msgid "watch file FD opened by process PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:101
|
||||
msgid "show this help and exit"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:103
|
||||
msgid "show version information and exit"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:109
|
||||
#, c-format
|
||||
msgid "Usage: %s [OPTION] [FILE]..."
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:112
|
||||
msgid ""
|
||||
"Concatenate FILE(s), or standard input, to standard output,\n"
|
||||
"with monitoring."
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:201
|
||||
msgid ""
|
||||
"Debugging is enabled; export the DEBUG environment variable to define the\n"
|
||||
"output filename.\n"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:205
|
||||
#, c-format
|
||||
msgid "Please report any bugs to %s."
|
||||
msgstr ""
|
||||
+602
@@ -0,0 +1,602 @@
|
||||
/*
|
||||
* Cursor positioning functions.
|
||||
*
|
||||
* If IPC is available, then a shared memory segment is used to co-ordinate
|
||||
* cursor positioning across multiple instances of `pv'. The shared memory
|
||||
* segment contains an integer which is the original "y" co-ordinate of the
|
||||
* first `pv' process.
|
||||
*
|
||||
* However, some OSes (FreeBSD and MacOS X so far) don't allow locking of a
|
||||
* terminal, so we try to use a lockfile if terminal locking doesn't work,
|
||||
* and finally abort if even that is unavailable.
|
||||
*/
|
||||
|
||||
#include "pv-internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef HAVE_IPC
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
#ifdef HAVE_LIBGEN_H
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
#endif /* HAVE_IPC */
|
||||
|
||||
|
||||
/*
|
||||
* Create a per-euid, per-tty, lockfile in ${TMPDIR:-${TMP:-/tmp}} for the
|
||||
* tty on the given file descriptor.
|
||||
*/
|
||||
static void pv_crs_open_lockfile(pvstate_t state, int fd)
|
||||
{
|
||||
char *ttydev;
|
||||
char *tmpdir;
|
||||
int openflags;
|
||||
|
||||
state->crs_lock_fd = -1;
|
||||
|
||||
ttydev = ttyname(fd);
|
||||
if (!ttydev) {
|
||||
if (!state->force) {
|
||||
pv_error(state, "%s: %s",
|
||||
_("failed to get terminal name"),
|
||||
strerror(errno));
|
||||
}
|
||||
/*
|
||||
* If we don't know our terminal name, we can neither do IPC
|
||||
* nor make a lock file, so turn off cursor positioning.
|
||||
*/
|
||||
state->cursor = 0;
|
||||
debug("%s",
|
||||
"ttyname failed - cursor positioning disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
tmpdir = (char *) getenv("TMPDIR");
|
||||
if (!tmpdir)
|
||||
tmpdir = (char *) getenv("TMP");
|
||||
if (!tmpdir)
|
||||
tmpdir = "/tmp";
|
||||
|
||||
#ifdef HAVE_SNPRINTF
|
||||
snprintf(state->crs_lock_file, sizeof(state->crs_lock_file) - 1,
|
||||
"%s/pv-%s-%i.lock", tmpdir, basename(ttydev),
|
||||
(int) geteuid());
|
||||
#else
|
||||
sprintf(state->crs_lock_file,
|
||||
"%.*s/pv-%8s-%i.lock",
|
||||
sizeof(state->crs_lock_file) - 64, tmpdir,
|
||||
basename(ttydev), (int) geteuid());
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Pawel Piatek - not everyone has O_NOFOLLOW, e.g. AIX doesn't
|
||||
*/
|
||||
#ifdef O_NOFOLLOW
|
||||
openflags = O_RDWR | O_CREAT | O_NOFOLLOW;
|
||||
#else
|
||||
openflags = O_RDWR | O_CREAT;
|
||||
#endif
|
||||
|
||||
state->crs_lock_fd = open(state->crs_lock_file, openflags, 0600);
|
||||
if (state->crs_lock_fd < 0) {
|
||||
pv_error(state, "%s: %s: %s",
|
||||
state->crs_lock_file,
|
||||
_("failed to open lock file"), strerror(errno));
|
||||
state->cursor = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Lock the terminal on the given file descriptor, falling back to using a
|
||||
* lockfile if the terminal itself cannot be locked.
|
||||
*/
|
||||
static void pv_crs_lock(pvstate_t state, int fd)
|
||||
{
|
||||
struct flock lock;
|
||||
int lock_fd;
|
||||
|
||||
lock_fd = fd;
|
||||
if (state->crs_lock_fd >= 0)
|
||||
lock_fd = state->crs_lock_fd;
|
||||
|
||||
lock.l_type = F_WRLCK;
|
||||
lock.l_whence = SEEK_SET;
|
||||
lock.l_start = 0;
|
||||
lock.l_len = 1;
|
||||
while (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
|
||||
if (errno != EINTR) {
|
||||
if (state->crs_lock_fd == -2) {
|
||||
pv_crs_open_lockfile(state, fd);
|
||||
if (state->crs_lock_fd >= 0) {
|
||||
lock_fd = state->crs_lock_fd;
|
||||
}
|
||||
} else {
|
||||
pv_error(state, "%s: %s",
|
||||
_("lock attempt failed"),
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state->crs_lock_fd >= 0) {
|
||||
debug("%s: %s", state->crs_lock_file,
|
||||
"terminal lockfile acquired");
|
||||
} else {
|
||||
debug("%s", "terminal lock acquired");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Unlock the terminal on the given file descriptor. If pv_crs_lock used
|
||||
* lockfile locking, unlock the lockfile.
|
||||
*/
|
||||
static void pv_crs_unlock(pvstate_t state, int fd)
|
||||
{
|
||||
struct flock lock;
|
||||
int lock_fd;
|
||||
|
||||
lock_fd = fd;
|
||||
if (state->crs_lock_fd >= 0)
|
||||
lock_fd = state->crs_lock_fd;
|
||||
|
||||
lock.l_type = F_UNLCK;
|
||||
lock.l_whence = SEEK_SET;
|
||||
lock.l_start = 0;
|
||||
lock.l_len = 1;
|
||||
fcntl(lock_fd, F_SETLK, &lock);
|
||||
|
||||
if (state->crs_lock_fd >= 0) {
|
||||
debug("%s: %s", state->crs_lock_file,
|
||||
"terminal lockfile released");
|
||||
} else {
|
||||
debug("%s", "terminal lock released");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_IPC
|
||||
/*
|
||||
* Get the current number of processes attached to our shared memory
|
||||
* segment, i.e. find out how many `pv' processes in total are running in
|
||||
* cursor mode (including us), and store it in pv_crs_pvcount. If this is
|
||||
* larger than pv_crs_pvmax, update pv_crs_pvmax.
|
||||
*/
|
||||
static void pv_crs_ipccount(pvstate_t state)
|
||||
{
|
||||
struct shmid_ds buf;
|
||||
|
||||
buf.shm_nattch = 0;
|
||||
|
||||
shmctl(state->crs_shmid, IPC_STAT, &buf);
|
||||
state->crs_pvcount = buf.shm_nattch;
|
||||
|
||||
if (state->crs_pvcount > state->crs_pvmax)
|
||||
state->crs_pvmax = state->crs_pvcount;
|
||||
|
||||
debug("%s: %d", "pvcount", state->crs_pvcount);
|
||||
}
|
||||
#endif /* HAVE_IPC */
|
||||
|
||||
|
||||
/*
|
||||
* Get the current cursor Y co-ordinate by sending the ECMA-48 CPR code to
|
||||
* the terminal connected to the given file descriptor.
|
||||
*/
|
||||
static int pv_crs_get_ypos(int terminalfd)
|
||||
{
|
||||
struct termios tty;
|
||||
struct termios old_tty;
|
||||
char cpr[32];
|
||||
int ypos;
|
||||
int r;
|
||||
#ifdef CURSOR_ANSWERBACK_BYTE_BY_BYTE
|
||||
int got;
|
||||
#endif /* CURSOR_ANSWERBACK_BYTE_BY_BYTE */
|
||||
|
||||
tcgetattr(terminalfd, &tty);
|
||||
tcgetattr(terminalfd, &old_tty);
|
||||
tty.c_lflag &= ~(ICANON | ECHO);
|
||||
tcsetattr(terminalfd, TCSANOW | TCSAFLUSH, &tty);
|
||||
|
||||
write(terminalfd, "\033[6n", 4);
|
||||
|
||||
memset(cpr, 0, sizeof(cpr));
|
||||
|
||||
#ifdef CURSOR_ANSWERBACK_BYTE_BY_BYTE
|
||||
/* Read answerback byte by byte - fails on AIX */
|
||||
for (got = 0, r = 0; got < sizeof(cpr) - 2; got += r) {
|
||||
r = read(terminalfd, cpr + got, 1);
|
||||
if (r <= 0) {
|
||||
debug("got=%d, r=%d: %s", got, r, strerror(errno));
|
||||
break;
|
||||
}
|
||||
if (cpr[got] == 'R')
|
||||
break;
|
||||
}
|
||||
|
||||
debug
|
||||
("read answerback message from fd %d, length %d - buf = %02X %02X %02X %02X %02X %02X",
|
||||
terminalfd, got, cpr[0], cpr[1], cpr[2], cpr[3], cpr[4],
|
||||
cpr[5]);
|
||||
|
||||
#else /* !CURSOR_ANSWERBACK_BYTE_BY_BYTE */
|
||||
/* Read answerback in one big lump - may fail on Solaris */
|
||||
r = read(terminalfd, cpr, sizeof(cpr));
|
||||
if (r <= 0) {
|
||||
debug("r=%d: %s", r, strerror(errno));
|
||||
} else {
|
||||
debug
|
||||
("read answerback message from fd %d, length %d - buf = %02X %02X %02X %02X %02X %02X",
|
||||
terminalfd, r, cpr[0], cpr[1], cpr[2], cpr[3], cpr[4],
|
||||
cpr[5]);
|
||||
|
||||
}
|
||||
#endif /* CURSOR_ANSWERBACK_BYTE_BY_BYTE */
|
||||
|
||||
ypos = pv_getnum_i(cpr + 2);
|
||||
|
||||
tcsetattr(terminalfd, TCSANOW | TCSAFLUSH, &old_tty);
|
||||
|
||||
debug("%s: %d", "ypos", ypos);
|
||||
|
||||
return ypos;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_IPC
|
||||
/*
|
||||
* Initialise the IPC data, returning nonzero on error.
|
||||
*
|
||||
* To do this, we attach to the shared memory segment (creating it if it
|
||||
* does not exist). If we are the only process attached to it, then we
|
||||
* initialise it with the current cursor position.
|
||||
*
|
||||
* There is a race condition here: another process could attach before we've
|
||||
* had a chance to check, such that no process ends up getting an "attach
|
||||
* count" of one, and so no initialisation occurs. So, we lock the terminal
|
||||
* with pv_crs_lock() while we are attaching and checking.
|
||||
*/
|
||||
static int pv_crs_ipcinit(pvstate_t state, char *ttyfile, int terminalfd)
|
||||
{
|
||||
key_t key;
|
||||
|
||||
/*
|
||||
* Base the key for the shared memory segment on our current tty, so
|
||||
* we don't end up interfering in any way with instances of `pv'
|
||||
* running on another terminal.
|
||||
*/
|
||||
key = ftok(ttyfile, 'p');
|
||||
if (-1 == key) {
|
||||
debug("%s: %s\n", "ftok failed", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
pv_crs_lock(state, terminalfd);
|
||||
if (!state->cursor) {
|
||||
debug("%s", "early return - cursor has been disabled");
|
||||
return 1;
|
||||
}
|
||||
|
||||
state->crs_shmid = shmget(key, sizeof(int), 0600 | IPC_CREAT);
|
||||
if (state->crs_shmid < 0) {
|
||||
debug("%s: %s", "shmget failed", strerror(errno));
|
||||
pv_crs_unlock(state, terminalfd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
state->crs_y_top = shmat(state->crs_shmid, 0, 0);
|
||||
|
||||
pv_crs_ipccount(state);
|
||||
|
||||
/*
|
||||
* If nobody else is attached to the shared memory segment, we're
|
||||
* the first, so we need to initialise the shared memory with our
|
||||
* current Y cursor co-ordinate.
|
||||
*/
|
||||
if (state->crs_pvcount < 2) {
|
||||
state->crs_y_start = pv_crs_get_ypos(terminalfd);
|
||||
*(state->crs_y_top) = state->crs_y_start;
|
||||
state->crs_y_lastread = state->crs_y_start;
|
||||
debug("%s", "we are the first to attach");
|
||||
}
|
||||
|
||||
state->crs_y_offset = state->crs_pvcount - 1;
|
||||
if (state->crs_y_offset < 0)
|
||||
state->crs_y_offset = 0;
|
||||
|
||||
/*
|
||||
* If anyone else had attached to the shared memory segment, we need
|
||||
* to read the top Y co-ordinate from it.
|
||||
*/
|
||||
if (state->crs_pvcount > 1) {
|
||||
state->crs_y_start = *(state->crs_y_top);
|
||||
state->crs_y_lastread = state->crs_y_start;
|
||||
debug("%s: %d", "not the first to attach - got top y",
|
||||
state->crs_y_start);
|
||||
}
|
||||
|
||||
pv_crs_unlock(state, terminalfd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* HAVE_IPC */
|
||||
|
||||
|
||||
/*
|
||||
* Initialise the terminal for cursor positioning.
|
||||
*/
|
||||
void pv_crs_init(pvstate_t state)
|
||||
{
|
||||
char *ttyfile;
|
||||
int fd;
|
||||
|
||||
state->crs_lock_fd = -2;
|
||||
state->crs_lock_file[0] = 0;
|
||||
|
||||
if (!state->cursor)
|
||||
return;
|
||||
|
||||
debug("%s", "init");
|
||||
|
||||
ttyfile = ttyname(STDERR_FILENO);
|
||||
if (!ttyfile) {
|
||||
debug("%s: %s",
|
||||
"disabling cursor positioning because ttyname failed",
|
||||
strerror(errno));
|
||||
state->cursor = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
fd = open(ttyfile, O_RDWR);
|
||||
if (fd < 0) {
|
||||
pv_error(state, "%s: %s: %s",
|
||||
_("failed to open terminal"), ttyfile,
|
||||
strerror(errno));
|
||||
state->cursor = 0;
|
||||
return;
|
||||
}
|
||||
#ifdef HAVE_IPC
|
||||
if (pv_crs_ipcinit(state, ttyfile, fd) != 0) {
|
||||
debug("%s", "ipcinit failed, setting noipc flag");
|
||||
state->crs_noipc = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are not using IPC, then we need to get the current Y
|
||||
* co-ordinate. If we are using IPC, then the pv_crs_ipcinit()
|
||||
* function takes care of this in a more multi-process-friendly way.
|
||||
*/
|
||||
if (state->crs_noipc) {
|
||||
#else /* ! HAVE_IPC */
|
||||
if (1) {
|
||||
#endif /* HAVE_IPC */
|
||||
/*
|
||||
* Get current cursor position + 1.
|
||||
*/
|
||||
pv_crs_lock(state, fd);
|
||||
state->crs_y_start = pv_crs_get_ypos(fd);
|
||||
/*
|
||||
* Move down a line while the terminal is locked, so that
|
||||
* other processes in the pipeline will get a different
|
||||
* initial ypos.
|
||||
*/
|
||||
if (state->crs_y_start > 0)
|
||||
write(STDERR_FILENO, "\n", 1);
|
||||
pv_crs_unlock(state, fd);
|
||||
|
||||
if (state->crs_y_start < 1)
|
||||
state->cursor = 0;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_IPC
|
||||
/*
|
||||
* Set the "we need to reinitialise cursor positioning" flag.
|
||||
*/
|
||||
void pv_crs_needreinit(pvstate_t state)
|
||||
{
|
||||
state->crs_needreinit += 2;
|
||||
if (state->crs_needreinit > 3)
|
||||
state->crs_needreinit = 3;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAVE_IPC
|
||||
/*
|
||||
* Reinitialise the cursor positioning code (called if we are backgrounded
|
||||
* then foregrounded again).
|
||||
*/
|
||||
void pv_crs_reinit(pvstate_t state)
|
||||
{
|
||||
debug("%s", "reinit");
|
||||
|
||||
pv_crs_lock(state, STDERR_FILENO);
|
||||
|
||||
state->crs_needreinit--;
|
||||
if (state->crs_y_offset < 1)
|
||||
state->crs_needreinit = 0;
|
||||
|
||||
if (state->crs_needreinit > 0) {
|
||||
pv_crs_unlock(state, STDERR_FILENO);
|
||||
return;
|
||||
}
|
||||
|
||||
debug("%s", "reinit full");
|
||||
|
||||
state->crs_y_start = pv_crs_get_ypos(STDERR_FILENO);
|
||||
|
||||
if (state->crs_y_offset < 1)
|
||||
*(state->crs_y_top) = state->crs_y_start;
|
||||
state->crs_y_lastread = state->crs_y_start;
|
||||
|
||||
pv_crs_unlock(state, STDERR_FILENO);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Output a single-line update, moving the cursor to the correct position to
|
||||
* do so.
|
||||
*/
|
||||
void pv_crs_update(pvstate_t state, char *str)
|
||||
{
|
||||
char pos[32];
|
||||
int y;
|
||||
|
||||
#ifdef HAVE_IPC
|
||||
if (!state->crs_noipc) {
|
||||
if (state->crs_needreinit)
|
||||
pv_crs_reinit(state);
|
||||
|
||||
pv_crs_ipccount(state);
|
||||
if (state->crs_y_lastread != *(state->crs_y_top)) {
|
||||
state->crs_y_start = *(state->crs_y_top);
|
||||
state->crs_y_lastread = state->crs_y_start;
|
||||
}
|
||||
|
||||
if (state->crs_needreinit > 0)
|
||||
return;
|
||||
}
|
||||
#endif /* HAVE_IPC */
|
||||
|
||||
y = state->crs_y_start;
|
||||
|
||||
#ifdef HAVE_IPC
|
||||
/*
|
||||
* If the screen has scrolled, or is about to scroll, due to
|
||||
* multiple `pv' instances taking us near the bottom of the screen,
|
||||
* scroll the screen (only if we're the first `pv'), and then move
|
||||
* our initial Y co-ordinate up.
|
||||
*/
|
||||
if (((state->crs_y_start + state->crs_pvmax) > state->height)
|
||||
&& (!state->crs_noipc)
|
||||
) {
|
||||
int offs;
|
||||
|
||||
offs =
|
||||
((state->crs_y_start + state->crs_pvmax) -
|
||||
state->height);
|
||||
|
||||
state->crs_y_start -= offs;
|
||||
if (state->crs_y_start < 1)
|
||||
state->crs_y_start = 1;
|
||||
|
||||
debug("%s: %d", "scroll offset", offs);
|
||||
|
||||
/*
|
||||
* Scroll the screen if we're the first `pv'.
|
||||
*/
|
||||
if (0 == state->crs_y_offset) {
|
||||
pv_crs_lock(state, STDERR_FILENO);
|
||||
|
||||
sprintf(pos, "\033[%d;1H", state->height);
|
||||
write(STDERR_FILENO, pos, strlen(pos));
|
||||
for (; offs > 0; offs--) {
|
||||
write(STDERR_FILENO, "\n", 1);
|
||||
}
|
||||
|
||||
pv_crs_unlock(state, STDERR_FILENO);
|
||||
|
||||
debug("%s", "we are the first - scrolled screen");
|
||||
}
|
||||
}
|
||||
|
||||
if (!state->crs_noipc)
|
||||
y = state->crs_y_start + state->crs_y_offset;
|
||||
#endif /* HAVE_IPC */
|
||||
|
||||
/*
|
||||
* Keep the Y co-ordinate within sensible bounds, so we can never
|
||||
* overflow the "pos" buffer.
|
||||
*/
|
||||
if ((y < 1) || (y > 999999))
|
||||
y = 1;
|
||||
sprintf(pos, "\033[%d;1H", y);
|
||||
|
||||
pv_crs_lock(state, STDERR_FILENO);
|
||||
|
||||
write(STDERR_FILENO, pos, strlen(pos));
|
||||
write(STDERR_FILENO, str, strlen(str));
|
||||
|
||||
pv_crs_unlock(state, STDERR_FILENO);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Reposition the cursor to a final position.
|
||||
*/
|
||||
void pv_crs_fini(pvstate_t state)
|
||||
{
|
||||
char pos[32];
|
||||
int y;
|
||||
|
||||
debug("%s", "fini");
|
||||
|
||||
y = state->crs_y_start;
|
||||
|
||||
#ifdef HAVE_IPC
|
||||
if ((state->crs_pvmax > 0) && (!state->crs_noipc))
|
||||
y += state->crs_pvmax - 1;
|
||||
#endif /* HAVE_IPC */
|
||||
|
||||
if (y > state->height)
|
||||
y = state->height;
|
||||
|
||||
/*
|
||||
* Absolute bounds check.
|
||||
*/
|
||||
if ((y < 1) || (y > 999999))
|
||||
y = 1;
|
||||
|
||||
sprintf(pos, "\033[%d;1H\n", y);
|
||||
|
||||
pv_crs_lock(state, STDERR_FILENO);
|
||||
|
||||
write(STDERR_FILENO, pos, strlen(pos));
|
||||
|
||||
#ifdef HAVE_IPC
|
||||
pv_crs_ipccount(state);
|
||||
shmdt((void *) state->crs_y_top);
|
||||
|
||||
/*
|
||||
* If we are the last instance detaching from the shared memory,
|
||||
* delete it so it's not left lying around.
|
||||
*/
|
||||
if (state->crs_pvcount < 2)
|
||||
shmctl(state->crs_shmid, IPC_RMID, 0);
|
||||
|
||||
#endif /* HAVE_IPC */
|
||||
|
||||
pv_crs_unlock(state, STDERR_FILENO);
|
||||
|
||||
if (state->crs_lock_fd >= 0) {
|
||||
close(state->crs_lock_fd);
|
||||
/*
|
||||
* We can get away with removing this on exit because all
|
||||
* the other PVs will be finishing at the same sort of time.
|
||||
*/
|
||||
remove(state->crs_lock_file);
|
||||
}
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,968 @@
|
||||
/*
|
||||
* Display functions.
|
||||
*/
|
||||
|
||||
#include "pv-internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
|
||||
/*
|
||||
* Output an error message. If we've displayed anything to the terminal
|
||||
* already, then put a newline before our error so we don't write over what
|
||||
* we've written.
|
||||
*/
|
||||
void pv_error(pvstate_t state, char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
if (state->display_visible)
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "%s: ", state->program_name);
|
||||
va_start(ap, format);
|
||||
vfprintf(stderr, format, ap);
|
||||
va_end(ap);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fill in *width and *height with the current terminal size,
|
||||
* if possible.
|
||||
*/
|
||||
void pv_screensize(unsigned int *width, unsigned int *height)
|
||||
{
|
||||
#ifdef TIOCGWINSZ
|
||||
struct winsize wsz;
|
||||
|
||||
if (isatty(STDERR_FILENO)) {
|
||||
if (0 == ioctl(STDERR_FILENO, TIOCGWINSZ, &wsz)) {
|
||||
*width = wsz.ws_col;
|
||||
*height = wsz.ws_row;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Calculate the percentage transferred so far and return it.
|
||||
*/
|
||||
static long pv__calc_percentage(long long so_far, const long long total)
|
||||
{
|
||||
if (total < 1)
|
||||
return 0;
|
||||
|
||||
so_far *= 100;
|
||||
so_far /= total;
|
||||
|
||||
return (long) so_far;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Given how many bytes have been transferred, the total byte count to
|
||||
* transfer, and how long it's taken so far in seconds, return the estimated
|
||||
* number of seconds until completion.
|
||||
*/
|
||||
static long pv__calc_eta(const long long so_far, const long long total,
|
||||
const long elapsed)
|
||||
{
|
||||
long long amount_left;
|
||||
|
||||
if (so_far < 1)
|
||||
return 0;
|
||||
|
||||
amount_left = total - so_far;
|
||||
amount_left *= (long long) elapsed;
|
||||
amount_left /= so_far;
|
||||
|
||||
return (long) amount_left;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a long double value, it is divided or multiplied by the ratio until
|
||||
* a value in the range 1.0 to 999.999... is found. The string "prefix" to
|
||||
* is updated to the corresponding SI prefix.
|
||||
*
|
||||
* If "is_bytes" is 1, then the second byte of "prefix" is set to "i" to
|
||||
* denote MiB etc (IEEE1541). Thus "prefix" should be at least 3 bytes long
|
||||
* (to include the terminating null).
|
||||
*
|
||||
* Submitted by Henry Gebhardt <hsggebhardt@googlemail.com> and then
|
||||
* modified. Further changed after input from Thomas Rachel; changed still
|
||||
* further after Debian bug #706175.
|
||||
*/
|
||||
static void pv__si_prefix(long double *value, char *prefix,
|
||||
const long double ratio, int is_bytes)
|
||||
{
|
||||
static char *pfx_000 = NULL; /* kilo, mega, etc */
|
||||
static char *pfx_024 = NULL; /* kibi, mibi, etc */
|
||||
static char const *pfx_middle_000 = NULL;
|
||||
static char const *pfx_middle_024 = NULL;
|
||||
char *pfx;
|
||||
char const *pfx_middle;
|
||||
char const *i;
|
||||
long double cutoff;
|
||||
|
||||
if (NULL == pfx_000) {
|
||||
pfx_000 = _("yzafpnum kMGTPEZY");
|
||||
pfx_middle_000 = strchr(pfx_000, ' ');
|
||||
}
|
||||
|
||||
if (NULL == pfx_024) {
|
||||
pfx_024 = _("yzafpnum KMGTPEZY");
|
||||
pfx_middle_024 = strchr(pfx_024, ' ');
|
||||
}
|
||||
|
||||
pfx = pfx_000;
|
||||
pfx_middle = pfx_middle_000;
|
||||
if (is_bytes) {
|
||||
pfx = pfx_024;
|
||||
pfx_middle = pfx_middle_024;
|
||||
}
|
||||
|
||||
i = pfx_middle;
|
||||
|
||||
prefix[0] = ' '; /* Make the prefix start blank. */
|
||||
prefix[1] = 0;
|
||||
|
||||
/*
|
||||
* Force an empty prefix if the value is zero to avoid "0yB".
|
||||
*/
|
||||
if (0.0 == *value)
|
||||
return;
|
||||
|
||||
cutoff = ratio * 0.97;
|
||||
|
||||
while ((*value > cutoff) && (*(i += 1) != '\0')) {
|
||||
*value /= ratio;
|
||||
prefix[0] = *i;
|
||||
}
|
||||
|
||||
while ((*value < 1.0) && ((i -= 1) != (pfx - 1))) {
|
||||
*value *= ratio;
|
||||
prefix[0] = *i;
|
||||
}
|
||||
|
||||
if (is_bytes && prefix[0] != ' ') {
|
||||
prefix[1] = 'i';
|
||||
prefix[2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Put a string in "buffer" (max length "bufsize") containing "amount"
|
||||
* formatted such that it's 3 or 4 digits followed by an SI suffix and then
|
||||
* whichever of "suffix_basic" or "suffix_bytes" is appropriate (whether
|
||||
* "is_bytes" is 0 for non-byte amounts or 1 for byte amounts). If
|
||||
* "is_bytes" is 1 then the SI units are KiB, MiB etc and the divisor is
|
||||
* 1024 instead of 1000.
|
||||
*
|
||||
* 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, int bufsize, char *format,
|
||||
long double amount, char *suffix_basic,
|
||||
char *suffix_bytes, int is_bytes)
|
||||
{
|
||||
char sizestr_buffer[256];
|
||||
char si_prefix[8] = " ";
|
||||
long double divider;
|
||||
long double display_amount;
|
||||
char *suffix;
|
||||
|
||||
if (is_bytes) {
|
||||
suffix = suffix_bytes;
|
||||
divider = 1024.0;
|
||||
} else {
|
||||
suffix = suffix_basic;
|
||||
divider = 1000.0;
|
||||
}
|
||||
|
||||
display_amount = amount;
|
||||
|
||||
pv__si_prefix(&display_amount, si_prefix, divider, is_bytes);
|
||||
|
||||
/* Make sure we don't overrun our buffer. */
|
||||
if (display_amount > 100000)
|
||||
display_amount = 100000;
|
||||
|
||||
/* Fix for display of "1.01e+03" instead of "1010" */
|
||||
if (display_amount > 99.9) {
|
||||
sprintf(sizestr_buffer, "%4ld%.2s%.16s",
|
||||
(long) display_amount, si_prefix, suffix);
|
||||
} else {
|
||||
/*
|
||||
* AIX blows up with %4.3Lg%.2s%.16s for some reason, so we
|
||||
* write display_amount separately first.
|
||||
*/
|
||||
char str_disp[64];
|
||||
/* # to get 13.0GB instead of 13GB (#1477) */
|
||||
sprintf(str_disp, "%#4.3Lg", display_amount);
|
||||
sprintf(sizestr_buffer, "%s%.2s%.16s",
|
||||
str_disp, si_prefix, suffix);
|
||||
}
|
||||
|
||||
snprintf(buffer, bufsize, format, sizestr_buffer);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialise the output format structure, based on the current options.
|
||||
*/
|
||||
static void pv__format_init(pvstate_t state)
|
||||
{
|
||||
const char *formatstr;
|
||||
const char *searchptr;
|
||||
int strpos;
|
||||
int segment;
|
||||
|
||||
if (NULL == state)
|
||||
return;
|
||||
|
||||
state->str_name[0] = 0;
|
||||
state->str_transferred[0] = 0;
|
||||
state->str_timer[0] = 0;
|
||||
state->str_rate[0] = 0;
|
||||
state->str_average_rate[0] = 0;
|
||||
state->str_progress[0] = 0;
|
||||
state->str_eta[0] = 0;
|
||||
memset(state->format, 0, sizeof(state->format));
|
||||
|
||||
if (state->name) {
|
||||
sprintf(state->str_name, "%9.500s:", state->name);
|
||||
}
|
||||
|
||||
formatstr =
|
||||
state->format_string ? state->
|
||||
format_string : state->default_format;
|
||||
|
||||
state->components_used = 0;
|
||||
|
||||
/*
|
||||
* Split the format string into segments. Each segment consists
|
||||
* of a string pointer and a length.
|
||||
*
|
||||
* 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 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->str_progress).
|
||||
*
|
||||
* A length above zero indicates that the segment is a constant
|
||||
* string of the given length (not necessarily null terminated).
|
||||
*
|
||||
* In pv__format(), after the state->str_* buffers have all been
|
||||
* filled in, the output string is generated by sticking all of
|
||||
* these segments together.
|
||||
*/
|
||||
segment = 0;
|
||||
for (strpos = 0; formatstr[strpos] != 0 && segment < 99;
|
||||
strpos++, segment++) {
|
||||
if ('%' == formatstr[strpos]) {
|
||||
int num;
|
||||
strpos++;
|
||||
num = 0;
|
||||
while (isdigit(formatstr[strpos])) {
|
||||
num = num * 10;
|
||||
num += formatstr[strpos] - '0';
|
||||
strpos++;
|
||||
}
|
||||
switch (formatstr[strpos]) {
|
||||
case 'p':
|
||||
state->format[segment].string =
|
||||
state->str_progress;
|
||||
state->format[segment].length = -1;
|
||||
state->components_used |=
|
||||
PV_DISPLAY_PROGRESS;
|
||||
break;
|
||||
case 't':
|
||||
state->format[segment].string =
|
||||
state->str_timer;
|
||||
state->format[segment].length = 0;
|
||||
state->components_used |= PV_DISPLAY_TIMER;
|
||||
break;
|
||||
case 'e':
|
||||
state->format[segment].string =
|
||||
state->str_eta;
|
||||
state->format[segment].length = 0;
|
||||
state->components_used |= PV_DISPLAY_ETA;
|
||||
break;
|
||||
case 'I':
|
||||
state->format[segment].string =
|
||||
state->str_fineta;
|
||||
state->format[segment].length = 0;
|
||||
state->components_used |=
|
||||
PV_DISPLAY_FINETA;
|
||||
break;
|
||||
case 'A':
|
||||
state->format[segment].string =
|
||||
state->str_lastoutput;
|
||||
state->format[segment].length = 0;
|
||||
if (num > sizeof(state->lastoutput_buffer))
|
||||
num =
|
||||
sizeof
|
||||
(state->lastoutput_buffer);
|
||||
if (num < 1)
|
||||
num = 1;
|
||||
state->lastoutput_length = num;
|
||||
state->components_used |=
|
||||
PV_DISPLAY_OUTPUTBUF;
|
||||
break;
|
||||
case 'r':
|
||||
state->format[segment].string =
|
||||
state->str_rate;
|
||||
state->format[segment].length = 0;
|
||||
state->components_used |= PV_DISPLAY_RATE;
|
||||
break;
|
||||
case 'a':
|
||||
state->format[segment].string =
|
||||
state->str_average_rate;
|
||||
state->format[segment].length = 0;
|
||||
state->components_used |=
|
||||
PV_DISPLAY_AVERAGERATE;
|
||||
break;
|
||||
case 'b':
|
||||
state->format[segment].string =
|
||||
state->str_transferred;
|
||||
state->format[segment].length = 0;
|
||||
state->components_used |= PV_DISPLAY_BYTES;
|
||||
break;
|
||||
case 'T':
|
||||
state->format[segment].string =
|
||||
state->str_bufpercent;
|
||||
state->format[segment].length = 0;
|
||||
state->components_used |=
|
||||
PV_DISPLAY_BUFPERCENT;
|
||||
break;
|
||||
case 'N':
|
||||
state->format[segment].string =
|
||||
state->str_name;
|
||||
state->format[segment].length =
|
||||
strlen(state->str_name);
|
||||
state->components_used |= PV_DISPLAY_NAME;
|
||||
break;
|
||||
case '%':
|
||||
/* %% => % */
|
||||
state->format[segment].string =
|
||||
&(formatstr[strpos]);
|
||||
state->format[segment].length = 1;
|
||||
break;
|
||||
case 0:
|
||||
/* % at end => just % */
|
||||
state->format[segment].string =
|
||||
&(formatstr[--strpos]);
|
||||
state->format[segment].length = 1;
|
||||
break;
|
||||
default:
|
||||
/* %z (unknown) => %z */
|
||||
state->format[segment].string =
|
||||
&(formatstr[--strpos]);
|
||||
state->format[segment].length = 2;
|
||||
strpos++;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
int foundlength;
|
||||
searchptr = strchr(&(formatstr[strpos]), '%');
|
||||
if (NULL == searchptr) {
|
||||
foundlength = strlen(&(formatstr[strpos]));
|
||||
} else {
|
||||
foundlength =
|
||||
searchptr - &(formatstr[strpos]);
|
||||
}
|
||||
state->format[segment].string =
|
||||
&(formatstr[strpos]);
|
||||
state->format[segment].length = foundlength;
|
||||
strpos += foundlength - 1;
|
||||
}
|
||||
}
|
||||
|
||||
state->format[segment].string = 0;
|
||||
state->format[segment].length = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the original value x so that it has been clamped between
|
||||
* [min..max]
|
||||
*/
|
||||
static long bound_long(long x, long min, long max)
|
||||
{
|
||||
return x < min ? min : x > max ? max : x;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a pointer to a string (which must not be freed), containing status
|
||||
* information formatted according to the state held within the given
|
||||
* structure, where "elapsed_sec" is the seconds elapsed since the transfer
|
||||
* started, "bytes_since_last" is the number of bytes transferred since the
|
||||
* last update, and "total_bytes" is the total number of bytes transferred
|
||||
* so far.
|
||||
*
|
||||
* If "bytes_since_last" is negative, this is the final update so the rate
|
||||
* is given as an an average over the whole transfer; otherwise the current
|
||||
* rate is shown.
|
||||
*
|
||||
* In line mode, "bytes_since_last" and "total_bytes" are in lines, not bytes.
|
||||
*
|
||||
* If "total_bytes" is negative, then free all allocated memory and return
|
||||
* NULL.
|
||||
*/
|
||||
static 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;
|
||||
int output_length;
|
||||
int display_string_length;
|
||||
|
||||
/* Quick sanity check - state must exist */
|
||||
if (NULL == state)
|
||||
return NULL;
|
||||
|
||||
/* Negative total transfer - free memory and exit */
|
||||
if (total_bytes < 0) {
|
||||
if (state->display_buffer)
|
||||
free(state->display_buffer);
|
||||
state->display_buffer = NULL;
|
||||
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
|
||||
* adding to that until a reasonable amount of time has passed to
|
||||
* avoid rate spikes or division by zero.
|
||||
*/
|
||||
time_since_last = elapsed_sec - state->prev_elapsed_sec;
|
||||
if (time_since_last <= 0.01) {
|
||||
rate = state->prev_rate;
|
||||
state->prev_trans += bytes_since_last;
|
||||
} else {
|
||||
rate =
|
||||
((long double) bytes_since_last +
|
||||
state->prev_trans) / time_since_last;
|
||||
state->prev_elapsed_sec = elapsed_sec;
|
||||
state->prev_trans = 0;
|
||||
}
|
||||
state->prev_rate = rate;
|
||||
|
||||
/*
|
||||
* We only calculate the overall average rate if this is the last
|
||||
* update or if the average rate display is enabled. Otherwise it's
|
||||
* not worth the extra CPU cycles.
|
||||
*/
|
||||
if ((bytes_since_last < 0)
|
||||
|| ((state->components_used & PV_DISPLAY_AVERAGERATE) != 0)) {
|
||||
/* Sanity check to avoid division by zero */
|
||||
if (elapsed_sec < 0.000001)
|
||||
elapsed_sec = 0.000001;
|
||||
average_rate =
|
||||
(((long double) total_bytes) -
|
||||
((long double) state->initial_offset)) /
|
||||
(long double) elapsed_sec;
|
||||
if (bytes_since_last < 0)
|
||||
rate = average_rate;
|
||||
}
|
||||
|
||||
if (state->size <= 0) {
|
||||
/*
|
||||
* If we don't know the total size of the incoming data,
|
||||
* then for a percentage, we gradually increase the
|
||||
* percentage completion as data arrives, to a maximum of
|
||||
* 200, then reset it - we use this if we can't calculate
|
||||
* it, so that the numeric percentage output will go
|
||||
* 0%-100%, 100%-0%, 0%-100%, and so on.
|
||||
*/
|
||||
if (rate > 0)
|
||||
state->percentage += 2;
|
||||
if (state->percentage > 199)
|
||||
state->percentage = 0;
|
||||
} else if (state->numeric
|
||||
|| ((state->components_used & PV_DISPLAY_PROGRESS) !=
|
||||
0)) {
|
||||
/*
|
||||
* 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->percentage =
|
||||
pv__calc_percentage(total_bytes, state->size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reallocate output buffer if width changes.
|
||||
*/
|
||||
if (state->display_buffer != NULL
|
||||
&& state->display_buffer_size < (state->width * 2)) {
|
||||
free(state->display_buffer);
|
||||
state->display_buffer = NULL;
|
||||
state->display_buffer_size = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate output buffer if there isn't one.
|
||||
*/
|
||||
if (NULL == state->display_buffer) {
|
||||
state->display_buffer_size = (2 * state->width) + 80;
|
||||
if (state->name)
|
||||
state->display_buffer_size += strlen(state->name);
|
||||
state->display_buffer =
|
||||
malloc(state->display_buffer_size + 16);
|
||||
if (NULL == state->display_buffer) {
|
||||
pv_error(state, "%s: %s",
|
||||
_("buffer allocation failed"),
|
||||
strerror(errno));
|
||||
state->exit_status |= 64;
|
||||
return NULL;
|
||||
}
|
||||
state->display_buffer[0] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* In numeric output mode, our output is just a number.
|
||||
*
|
||||
* Patch from Sami Liedes:
|
||||
* With --timer we prefix the output with the elapsed time.
|
||||
* With --bytes we output the bytes transferred so far instead
|
||||
* of the percentage. (Or lines, if --lines was given with --bytes).
|
||||
*/
|
||||
if (state->numeric) {
|
||||
char numericprefix[128];
|
||||
|
||||
numericprefix[0] = 0;
|
||||
|
||||
if ((state->components_used & PV_DISPLAY_TIMER) != 0)
|
||||
sprintf(numericprefix, "%.4Lf ", elapsed_sec);
|
||||
|
||||
if ((state->components_used & PV_DISPLAY_BYTES) != 0) {
|
||||
sprintf(state->display_buffer, "%.99s%lld\n",
|
||||
numericprefix, total_bytes);
|
||||
} else if (state->percentage > 100) {
|
||||
/* As mentioned above, we go 0-100, then 100-0. */
|
||||
sprintf(state->display_buffer, "%.99s%ld\n",
|
||||
numericprefix, 200 - state->percentage);
|
||||
} else {
|
||||
sprintf(state->display_buffer, "%.99s%ld\n",
|
||||
numericprefix, state->percentage);
|
||||
}
|
||||
|
||||
return state->display_buffer;
|
||||
}
|
||||
|
||||
/*
|
||||
* First, work out what components we will be putting in the output
|
||||
* buffer, and for those that don't depend on the total width
|
||||
* available (i.e. all but the progress bar), prepare their strings
|
||||
* to be placed in the output buffer.
|
||||
*/
|
||||
|
||||
state->str_transferred[0] = 0;
|
||||
state->str_bufpercent[0] = 0;
|
||||
state->str_timer[0] = 0;
|
||||
state->str_rate[0] = 0;
|
||||
state->str_average_rate[0] = 0;
|
||||
state->str_progress[0] = 0;
|
||||
state->str_lastoutput[0] = 0;
|
||||
state->str_eta[0] = 0;
|
||||
state->str_fineta[0] = 0;
|
||||
|
||||
/* If we're showing bytes transferred, set up the display string. */
|
||||
if ((state->components_used & PV_DISPLAY_BYTES) != 0) {
|
||||
pv__sizestr(state->str_transferred,
|
||||
sizeof(state->str_transferred), "%s",
|
||||
(long double) total_bytes, "", _("B"),
|
||||
state->linemode ? 0 : 1);
|
||||
}
|
||||
|
||||
/* Transfer buffer percentage - set up the display string. */
|
||||
if ((state->components_used & PV_DISPLAY_BUFPERCENT) != 0) {
|
||||
if (state->buffer_size > 0)
|
||||
sprintf(state->str_bufpercent, "{%3ld%%}",
|
||||
pv__calc_percentage(state->read_position -
|
||||
state->write_position,
|
||||
state->buffer_size));
|
||||
#ifdef HAVE_SPLICE
|
||||
if (state->splice_used)
|
||||
strcpy(state->str_bufpercent, "{----}");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Timer - set up the display string. */
|
||||
if ((state->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;
|
||||
|
||||
sprintf(state->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->components_used & PV_DISPLAY_RATE) != 0) {
|
||||
pv__sizestr(state->str_rate, sizeof(state->str_rate),
|
||||
"[%s]", rate, _("/s"), _("B/s"),
|
||||
state->linemode ? 0 : 1);
|
||||
}
|
||||
|
||||
/* Average rate - set up the display string. */
|
||||
if ((state->components_used & PV_DISPLAY_AVERAGERATE) != 0) {
|
||||
pv__sizestr(state->str_average_rate,
|
||||
sizeof(state->str_average_rate), "[%s]",
|
||||
average_rate, _("/s"), _("B/s"),
|
||||
state->linemode ? 0 : 1);
|
||||
}
|
||||
|
||||
/* Last output bytes - set up the display string. */
|
||||
if ((state->components_used & PV_DISPLAY_OUTPUTBUF) != 0) {
|
||||
int idx;
|
||||
for (idx = 0; idx < state->lastoutput_length; idx++) {
|
||||
int c;
|
||||
c = state->lastoutput_buffer[idx];
|
||||
state->str_lastoutput[idx] = isprint(c) ? c : '.';
|
||||
}
|
||||
state->str_lastoutput[idx] = 0;
|
||||
}
|
||||
|
||||
/* ETA (only if size is known) - set up the display string. */
|
||||
if (((state->components_used & PV_DISPLAY_ETA) != 0)
|
||||
&& (state->size > 0)) {
|
||||
eta =
|
||||
pv__calc_eta(total_bytes - state->initial_offset,
|
||||
state->size - state->initial_offset,
|
||||
elapsed_sec);
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
sprintf(state->str_eta,
|
||||
"%.16s %ld:%02ld:%02ld:%02ld", _("ETA"),
|
||||
eta / 86400, (eta / 3600) % 24,
|
||||
(eta / 60) % 60, eta % 60);
|
||||
} else {
|
||||
sprintf(state->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) {
|
||||
int i;
|
||||
for (i = 0; i < sizeof(state->str_eta)
|
||||
&& state->str_eta[i] != 0; i++) {
|
||||
state->str_eta[i] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ETA as clock time (as above) - set up the display string. */
|
||||
if (((state->components_used & PV_DISPLAY_FINETA) != 0)
|
||||
&& (state->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->initial_offset,
|
||||
state->size - state->initial_offset,
|
||||
elapsed_sec);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
sprintf(state->str_fineta, "%.16s ", _("ETA"));
|
||||
strftime(state->str_fineta +
|
||||
strlen(state->str_fineta),
|
||||
sizeof(state->str_fineta) - 1 -
|
||||
strlen(state->str_fineta),
|
||||
time_format, &time);
|
||||
}
|
||||
|
||||
if (!show_eta) {
|
||||
int i;
|
||||
for (i = 0; i < sizeof(state->str_fineta)
|
||||
&& state->str_fineta[i] != 0; i++) {
|
||||
state->str_fineta[i] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now go through all the static portions of the format to work
|
||||
* out how much space will be left for any dynamic portions
|
||||
* (i.e. the progress bar).
|
||||
*/
|
||||
static_portion_size = 0;
|
||||
for (segment = 0; state->format[segment].string; segment++) {
|
||||
if (state->format[segment].length < 0) {
|
||||
continue;
|
||||
} else if (state->format[segment].length > 0) {
|
||||
static_portion_size +=
|
||||
state->format[segment].length;
|
||||
} else {
|
||||
static_portion_size +=
|
||||
strlen(state->format[segment].string);
|
||||
}
|
||||
}
|
||||
|
||||
debug("static_portion_size: %d", static_portion_size);
|
||||
|
||||
/*
|
||||
* Assemble the progress bar now we know how big it should be.
|
||||
*/
|
||||
if ((state->components_used & PV_DISPLAY_PROGRESS) != 0) {
|
||||
char pct[16];
|
||||
int available_width, i;
|
||||
|
||||
strcpy(state->str_progress, "[");
|
||||
|
||||
if (state->size > 0) {
|
||||
if (state->percentage < 0)
|
||||
state->percentage = 0;
|
||||
if (state->percentage > 100000)
|
||||
state->percentage = 100000;
|
||||
sprintf(pct, "%2ld%%", state->percentage);
|
||||
|
||||
available_width =
|
||||
state->width - static_portion_size -
|
||||
strlen(pct) - 3;
|
||||
|
||||
if (available_width < 0)
|
||||
available_width = 0;
|
||||
|
||||
if (available_width >
|
||||
sizeof(state->str_progress) - 16)
|
||||
available_width =
|
||||
sizeof(state->str_progress) - 16;
|
||||
|
||||
for (i = 0;
|
||||
i <
|
||||
(available_width * state->percentage) / 100 -
|
||||
1; i++) {
|
||||
if (i < available_width)
|
||||
strcat(state->str_progress, "=");
|
||||
}
|
||||
if (i < available_width) {
|
||||
strcat(state->str_progress, ">");
|
||||
i++;
|
||||
}
|
||||
for (; i < available_width; i++) {
|
||||
strcat(state->str_progress, " ");
|
||||
}
|
||||
strcat(state->str_progress, "] ");
|
||||
strcat(state->str_progress, pct);
|
||||
} else {
|
||||
int p = state->percentage;
|
||||
|
||||
available_width =
|
||||
state->width - static_portion_size - 5;
|
||||
|
||||
if (available_width < 0)
|
||||
available_width = 0;
|
||||
|
||||
if (available_width >
|
||||
sizeof(state->str_progress) - 16)
|
||||
available_width =
|
||||
sizeof(state->str_progress) - 16;
|
||||
|
||||
debug("available_width: %d", available_width);
|
||||
|
||||
if (p > 100)
|
||||
p = 200 - p;
|
||||
for (i = 0; i < (available_width * p) / 100; i++) {
|
||||
if (i < available_width)
|
||||
strcat(state->str_progress, " ");
|
||||
}
|
||||
strcat(state->str_progress, "<=>");
|
||||
for (; i < available_width; i++) {
|
||||
strcat(state->str_progress, " ");
|
||||
}
|
||||
strcat(state->str_progress, "]");
|
||||
}
|
||||
|
||||
/*
|
||||
* If the progress bar won't fit, drop it.
|
||||
*/
|
||||
if (strlen(state->str_progress) + static_portion_size >
|
||||
state->width)
|
||||
state->str_progress[0] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can now build the output string using the format structure.
|
||||
*/
|
||||
|
||||
state->display_buffer[0] = 0;
|
||||
display_string_length = 0;
|
||||
for (segment = 0; state->format[segment].string; segment++) {
|
||||
int segment_length;
|
||||
if (state->format[segment].length > 0) {
|
||||
segment_length = state->format[segment].length;
|
||||
} else {
|
||||
segment_length =
|
||||
strlen(state->format[segment].string);
|
||||
}
|
||||
/* Skip empty segments */
|
||||
if (segment_length == 0)
|
||||
continue;
|
||||
/*
|
||||
* Truncate segment if it would make the display string
|
||||
* overflow the buffer
|
||||
*/
|
||||
if (segment_length + display_string_length >
|
||||
state->display_buffer_size - 2)
|
||||
segment_length =
|
||||
state->display_buffer_size -
|
||||
display_string_length - 2;
|
||||
if (segment_length < 1)
|
||||
break;
|
||||
/* Skip segment if it would make the display too wide */
|
||||
if (segment_length + display_string_length > state->width)
|
||||
break;
|
||||
strncat(state->display_buffer,
|
||||
state->format[segment].string, segment_length);
|
||||
display_string_length += segment_length;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the size of our output shrinks, we need to keep appending
|
||||
* spaces at the end, so that we don't leave dangling bits behind.
|
||||
*/
|
||||
output_length = strlen(state->display_buffer);
|
||||
if ((output_length < state->prev_length)
|
||||
&& (state->width >= state->prev_width)) {
|
||||
char spaces[32];
|
||||
int spaces_to_add;
|
||||
spaces_to_add = state->prev_length - output_length;
|
||||
/* Upper boundary on number of spaces */
|
||||
if (spaces_to_add > 15) {
|
||||
spaces_to_add = 15;
|
||||
}
|
||||
output_length += spaces_to_add;
|
||||
spaces[spaces_to_add] = 0;
|
||||
while (--spaces_to_add >= 0) {
|
||||
spaces[spaces_to_add] = ' ';
|
||||
}
|
||||
strcat(state->display_buffer, spaces);
|
||||
}
|
||||
state->prev_width = state->width;
|
||||
state->prev_length = output_length;
|
||||
|
||||
return state->display_buffer;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Output status information on standard error, where "esec" is the seconds
|
||||
* elapsed since the transfer started, "sl" is the number of bytes transferred
|
||||
* since the last update, and "tot" is the total number of bytes transferred
|
||||
* so far.
|
||||
*
|
||||
* If "sl" is negative, this is the final update so the rate is given as an
|
||||
* an average over the whole transfer; otherwise the current rate is shown.
|
||||
*
|
||||
* In line mode, "sl" and "tot" are in lines, not bytes.
|
||||
*/
|
||||
void pv_display(pvstate_t state, long double esec, long long sl,
|
||||
long long tot)
|
||||
{
|
||||
char *display;
|
||||
|
||||
if (NULL == state)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If the display options need reparsing, do so to generate new
|
||||
* formatting parameters.
|
||||
*/
|
||||
if (state->reparse_display) {
|
||||
pv__format_init(state);
|
||||
state->reparse_display = 0;
|
||||
}
|
||||
|
||||
pv_sig_checkbg();
|
||||
|
||||
display = pv__format(state, esec, sl, tot);
|
||||
if (NULL == display)
|
||||
return;
|
||||
|
||||
if (state->numeric) {
|
||||
write(STDERR_FILENO, display, strlen(display));
|
||||
} else if (state->cursor) {
|
||||
pv_crs_update(state, display);
|
||||
state->display_visible = 1;
|
||||
} else {
|
||||
write(STDERR_FILENO, display, strlen(display));
|
||||
write(STDERR_FILENO, "\r", 1);
|
||||
state->display_visible = 1;
|
||||
}
|
||||
|
||||
debug("%s: [%s]", "display", display);
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
+293
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Functions for opening and closing files.
|
||||
*/
|
||||
|
||||
#include "pv-internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
/*
|
||||
* Try to work out the total size of all data by adding up the sizes of all
|
||||
* input files. If any of the input files are of indeterminate size (i.e.
|
||||
* they are a pipe), the total size is set to zero.
|
||||
*
|
||||
* Any files that cannot be stat()ed or that access() says we can't read
|
||||
* will cause a warning to be output and will be removed from the list.
|
||||
*
|
||||
* In line mode, any files that pass the above checks will then be read to
|
||||
* determine how many lines they contain, and the total size will be set to
|
||||
* the total line count. Only regular files will be read.
|
||||
*
|
||||
* Returns the total size, or 0 if it is unknown.
|
||||
*/
|
||||
unsigned long long pv_calc_total_size(pvstate_t state)
|
||||
{
|
||||
unsigned long long total;
|
||||
struct stat64 sb;
|
||||
int rc, i, j, fd;
|
||||
|
||||
total = 0;
|
||||
rc = 0;
|
||||
|
||||
/*
|
||||
* No files specified - check stdin.
|
||||
*/
|
||||
if (state->input_file_count < 1) {
|
||||
if (0 == fstat64(STDIN_FILENO, &sb))
|
||||
total = sb.st_size;
|
||||
return total;
|
||||
}
|
||||
|
||||
for (i = 0; i < state->input_file_count; i++) {
|
||||
if (0 == strcmp(state->input_files[i], "-")) {
|
||||
rc = fstat64(STDIN_FILENO, &sb);
|
||||
if (rc != 0) {
|
||||
total = 0;
|
||||
return total;
|
||||
}
|
||||
} else {
|
||||
rc = stat64(state->input_files[i], &sb);
|
||||
if (0 == rc)
|
||||
rc = access(state->input_files[i], R_OK);
|
||||
}
|
||||
|
||||
if (rc != 0) {
|
||||
pv_error(state, "%s: %s",
|
||||
state->input_files[i], strerror(errno));
|
||||
for (j = i; j < state->input_file_count - 1; j++) {
|
||||
state->input_files[j] =
|
||||
state->input_files[j + 1];
|
||||
}
|
||||
state->input_file_count--;
|
||||
i--;
|
||||
state->exit_status |= 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISBLK(sb.st_mode)) {
|
||||
/*
|
||||
* Get the size of block devices by opening
|
||||
* them and seeking to the end.
|
||||
*/
|
||||
if (0 == strcmp(state->input_files[i], "-")) {
|
||||
fd = open64("/dev/stdin", O_RDONLY);
|
||||
} else {
|
||||
fd = open64(state->input_files[i],
|
||||
O_RDONLY);
|
||||
}
|
||||
if (fd >= 0) {
|
||||
total += lseek64(fd, 0, SEEK_END);
|
||||
close(fd);
|
||||
} else {
|
||||
pv_error(state, "%s: %s",
|
||||
state->input_files[i],
|
||||
strerror(errno));
|
||||
state->exit_status |= 2;
|
||||
}
|
||||
} else if (S_ISREG(sb.st_mode)) {
|
||||
total += sb.st_size;
|
||||
} else {
|
||||
total = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Patch from Peter Samuelson: if we cannot work out the size of the
|
||||
* input, but we are writing to a block device, then use the size of
|
||||
* the output block device.
|
||||
*
|
||||
* Further modified to check that stdout is not in append-only mode
|
||||
* and that we can seek back to the start after getting the size.
|
||||
*/
|
||||
if (total <= 0) {
|
||||
rc = fstat64(STDOUT_FILENO, &sb);
|
||||
if ((0 == rc) && S_ISBLK(sb.st_mode)
|
||||
&& (0 == (fcntl(STDOUT_FILENO, F_GETFL) & O_APPEND))) {
|
||||
total = lseek64(STDOUT_FILENO, 0, SEEK_END);
|
||||
if (lseek64(STDOUT_FILENO, 0, SEEK_SET) != 0) {
|
||||
pv_error(state, "%s: %s: %s", "(stdout)",
|
||||
_
|
||||
("failed to seek to start of output"),
|
||||
strerror(errno));
|
||||
state->exit_status |= 2;
|
||||
}
|
||||
/*
|
||||
* If we worked out a size, then set the
|
||||
* stop-at-size flag to prevent a "no space left on
|
||||
* device" error when we reach the end of the output
|
||||
* device.
|
||||
*/
|
||||
if (total > 0) {
|
||||
state->stop_at_size = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!state->linemode)
|
||||
return total;
|
||||
|
||||
/*
|
||||
* In line mode, we count input lines to work out the total size.
|
||||
*/
|
||||
total = 0;
|
||||
|
||||
for (i = 0; i < state->input_file_count; i++) {
|
||||
fd = -1;
|
||||
|
||||
if (0 == strcmp(state->input_files[i], "-")) {
|
||||
rc = fstat64(STDIN_FILENO, &sb);
|
||||
if ((rc != 0) || (!S_ISREG(sb.st_mode))) {
|
||||
total = 0;
|
||||
return total;
|
||||
}
|
||||
fd = dup(STDIN_FILENO);
|
||||
} else {
|
||||
rc = stat64(state->input_files[i], &sb);
|
||||
if ((rc != 0) || (!S_ISREG(sb.st_mode))) {
|
||||
total = 0;
|
||||
return total;
|
||||
}
|
||||
fd = open64(state->input_files[i], O_RDONLY);
|
||||
}
|
||||
|
||||
if (fd < 0) {
|
||||
pv_error(state, "%s: %s", state->input_files[i],
|
||||
strerror(errno));
|
||||
total = 0;
|
||||
state->exit_status |= 2;
|
||||
return total;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
unsigned char scanbuf[1024];
|
||||
int numread, j;
|
||||
|
||||
numread = read(fd, scanbuf, sizeof(scanbuf));
|
||||
if (numread < 0) {
|
||||
pv_error(state, "%s: %s",
|
||||
state->input_files[i],
|
||||
strerror(errno));
|
||||
state->exit_status |= 2;
|
||||
break;
|
||||
} else if (0 == numread) {
|
||||
break;
|
||||
}
|
||||
for (j = 0; j < numread; j++) {
|
||||
if ('\n' == scanbuf[j])
|
||||
total++;
|
||||
}
|
||||
}
|
||||
|
||||
lseek64(fd, 0, SEEK_SET);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Close the given file descriptor and open the next one, whose number in
|
||||
* the list is "filenum", returning the new file descriptor (or negative on
|
||||
* error). It is an error if the next input file is the same as the file
|
||||
* stdout is pointing to.
|
||||
*
|
||||
* Updates state->current_file in the process.
|
||||
*/
|
||||
int pv_next_file(pvstate_t state, int filenum, int oldfd)
|
||||
{
|
||||
struct stat64 isb;
|
||||
struct stat64 osb;
|
||||
int fd, input_file_is_stdout;
|
||||
|
||||
if (oldfd > 0) {
|
||||
if (close(oldfd)) {
|
||||
pv_error(state, "%s: %s",
|
||||
_("failed to close file"),
|
||||
strerror(errno));
|
||||
state->exit_status |= 8;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (filenum >= state->input_file_count) {
|
||||
state->exit_status |= 8;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (filenum < 0) {
|
||||
state->exit_status |= 8;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 == strcmp(state->input_files[filenum], "-")) {
|
||||
fd = STDIN_FILENO;
|
||||
} else {
|
||||
fd = open64(state->input_files[filenum], O_RDONLY);
|
||||
if (fd < 0) {
|
||||
pv_error(state, "%s: %s: %s",
|
||||
_("failed to read file"),
|
||||
state->input_files[filenum],
|
||||
strerror(errno));
|
||||
state->exit_status |= 2;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (fstat64(fd, &isb)) {
|
||||
pv_error(state, "%s: %s: %s",
|
||||
_("failed to stat file"),
|
||||
state->input_files[filenum], strerror(errno));
|
||||
close(fd);
|
||||
state->exit_status |= 2;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fstat64(STDOUT_FILENO, &osb)) {
|
||||
pv_error(state, "%s: %s",
|
||||
_("failed to stat output file"), strerror(errno));
|
||||
close(fd);
|
||||
state->exit_status |= 2;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that this new input file is not the same as stdout's
|
||||
* destination. This restriction is ignored for anything other
|
||||
* than a regular file or block device.
|
||||
*/
|
||||
input_file_is_stdout = 1;
|
||||
if (isb.st_dev != osb.st_dev)
|
||||
input_file_is_stdout = 0;
|
||||
if (isb.st_ino != osb.st_ino)
|
||||
input_file_is_stdout = 0;
|
||||
if (isatty(fd))
|
||||
input_file_is_stdout = 0;
|
||||
if ((!S_ISREG(isb.st_mode)) && (!S_ISBLK(isb.st_mode)))
|
||||
input_file_is_stdout = 0;
|
||||
|
||||
if (input_file_is_stdout) {
|
||||
pv_error(state, "%s: %s",
|
||||
_("input file is output file"),
|
||||
state->input_files[filenum]);
|
||||
close(fd);
|
||||
state->exit_status |= 4;
|
||||
return -1;
|
||||
}
|
||||
|
||||
state->current_file = state->input_files[filenum];
|
||||
if (0 == strcmp(state->input_files[filenum], "-")) {
|
||||
state->current_file = "(stdin)";
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
+780
@@ -0,0 +1,780 @@
|
||||
/*
|
||||
* 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 ((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, next_remotecheck;
|
||||
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")) {
|
||||
snprintf(new_format_string, sizeof(new_format_string) - 1,
|
||||
"%%N %s", original_format_string);
|
||||
} else {
|
||||
snprintf(new_format_string, sizeof(new_format_string) - 1,
|
||||
"%s", original_format_string);
|
||||
}
|
||||
state_copy.format_string = NULL;
|
||||
snprintf(state_copy.default_format,
|
||||
sizeof(state_copy.default_format) - 1, "%s",
|
||||
new_format_string);
|
||||
|
||||
/*
|
||||
* Get things ready for the main loop.
|
||||
*/
|
||||
|
||||
gettimeofday(&cur_time, NULL);
|
||||
|
||||
next_remotecheck.tv_sec = cur_time.tv_sec;
|
||||
next_remotecheck.tv_usec = cur_time.tv_usec;
|
||||
|
||||
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 >= 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) {
|
||||
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) {
|
||||
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 */
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Functions for converting strings to numbers.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#include "pv.h"
|
||||
|
||||
|
||||
/*
|
||||
* This function is used instead of the macro from <ctype.h> because
|
||||
* including <ctype.h> causes weird versioned glibc dependencies on certain
|
||||
* Red Hat systems, complicating package management.
|
||||
*/
|
||||
static int pv__isdigit(char c)
|
||||
{
|
||||
return ((c >= '0') && (c <= '9'));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the numeric value of "str", as a long long.
|
||||
*/
|
||||
long long pv_getnum_ll(const char *str)
|
||||
{
|
||||
long long n = 0;
|
||||
long long decimal = 0;
|
||||
int decdivisor = 1;
|
||||
int shift = 0;
|
||||
|
||||
if (0 == str)
|
||||
return n;
|
||||
|
||||
while (str[0] != 0 && (!pv__isdigit(str[0])))
|
||||
str++;
|
||||
|
||||
for (; pv__isdigit(str[0]); str++) {
|
||||
n = n * 10;
|
||||
n += (str[0] - '0');
|
||||
}
|
||||
|
||||
/*
|
||||
* If a decimal value was given, skip the decimal part.
|
||||
*/
|
||||
if (('.' == str[0]) || (',' == str[0])) {
|
||||
str++;
|
||||
for (; pv__isdigit(str[0]); str++) {
|
||||
if (decdivisor < 10000) {
|
||||
decimal = decimal * 10;
|
||||
decimal += (str[0] - '0');
|
||||
decdivisor = decdivisor * 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse any units given (K=KiB=*1024, M=MiB=1024KiB, G=GiB=1024MiB,
|
||||
* T=TiB=1024GiB).
|
||||
*/
|
||||
if (str[0]) {
|
||||
while ((' ' == str[0]) || ('\t' == str[0]))
|
||||
str++;
|
||||
switch (str[0]) {
|
||||
case 'k':
|
||||
case 'K':
|
||||
shift = 10;
|
||||
break;
|
||||
case 'm':
|
||||
case 'M':
|
||||
shift = 20;
|
||||
break;
|
||||
case 'g':
|
||||
case 'G':
|
||||
shift = 30;
|
||||
break;
|
||||
case 't':
|
||||
case 'T':
|
||||
shift = 40;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Binary left-shift the supplied number by "shift" times, i.e.
|
||||
* apply the given units (KiB, MiB, etc) to it, but never shift left
|
||||
* more than 30 at a time to avoid overflows.
|
||||
*/
|
||||
while (shift > 0) {
|
||||
int shiftby;
|
||||
|
||||
shiftby = shift;
|
||||
if (shiftby > 30)
|
||||
shiftby = 30;
|
||||
|
||||
n = n << shiftby;
|
||||
decimal = decimal << shiftby;
|
||||
shift -= shiftby;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add any decimal component.
|
||||
*/
|
||||
decimal = decimal / decdivisor;
|
||||
n += decimal;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the numeric value of "str", as a double.
|
||||
*/
|
||||
double pv_getnum_d(const char *str)
|
||||
{
|
||||
double n = 0.0;
|
||||
double step = 1;
|
||||
|
||||
if (0 == str)
|
||||
return n;
|
||||
|
||||
while (str[0] != 0 && (!pv__isdigit(str[0])))
|
||||
str++;
|
||||
|
||||
for (; pv__isdigit(str[0]); str++) {
|
||||
n = n * 10;
|
||||
n += (str[0] - '0');
|
||||
}
|
||||
|
||||
if ((str[0] != '.') && (str[0] != ','))
|
||||
return n;
|
||||
|
||||
str++;
|
||||
|
||||
for (; pv__isdigit(str[0]) && step < 1000000; str++) {
|
||||
step = step * 10;
|
||||
n += (str[0] - '0') / step;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the numeric value of "str", as an int.
|
||||
*/
|
||||
int pv_getnum_i(const char *str)
|
||||
{
|
||||
return (int) pv_getnum_ll(str);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return nonzero if the given string is not a valid number of the given
|
||||
* type.
|
||||
*/
|
||||
int pv_getnum_check(const char *str, pv_numtype_t type)
|
||||
{
|
||||
if (0 == str)
|
||||
return 1;
|
||||
|
||||
while ((' ' == str[0]) || ('\t' == str[0]))
|
||||
str++;
|
||||
|
||||
if (!pv__isdigit(str[0]))
|
||||
return 1;
|
||||
|
||||
for (; pv__isdigit(str[0]); str++);
|
||||
|
||||
if (('.' == str[0]) || (',' == str[0])) {
|
||||
if (type == PV_NUMTYPE_INTEGER)
|
||||
return 1;
|
||||
str++;
|
||||
for (; pv__isdigit(str[0]); str++);
|
||||
}
|
||||
|
||||
if (0 == str[0])
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Suffixes are not allowed for doubles, only for integers.
|
||||
*/
|
||||
if (type == PV_NUMTYPE_DOUBLE)
|
||||
return 1;
|
||||
|
||||
while ((' ' == str[0]) || ('\t' == str[0]))
|
||||
str++;
|
||||
switch (str[0]) {
|
||||
case 'k':
|
||||
case 'K':
|
||||
case 'm':
|
||||
case 'M':
|
||||
case 'g':
|
||||
case 'G':
|
||||
case 't':
|
||||
case 'T':
|
||||
str++;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (str[0] != 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
+297
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Signal handling functions.
|
||||
*/
|
||||
|
||||
#include "pv-internal.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef HAVE_IPC
|
||||
void pv_crs_needreinit(pvstate_t);
|
||||
#endif
|
||||
|
||||
static pvstate_t pv_sig_state = NULL;
|
||||
|
||||
|
||||
/*
|
||||
* Handle SIGTTOU (tty output for background process) by redirecting stderr
|
||||
* to /dev/null, so that we can be stopped and backgrounded without messing
|
||||
* up the terminal. We store the old stderr file descriptor so that on a
|
||||
* subsequent SIGCONT we can try writing to the terminal again, in case we
|
||||
* get backgrounded and later get foregrounded again.
|
||||
*/
|
||||
static void pv_sig_ttou(int s)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open("/dev/null", O_RDWR);
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
if (-1 == pv_sig_state->pv_sig_old_stderr)
|
||||
pv_sig_state->pv_sig_old_stderr = dup(STDERR_FILENO);
|
||||
|
||||
dup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handle SIGTSTP (stop typed at tty) by storing the time the signal
|
||||
* happened for later use by pv_sig_cont(), and then stopping the process.
|
||||
*/
|
||||
static void pv_sig_tstp(int s)
|
||||
{
|
||||
gettimeofday(&(pv_sig_state->pv_sig_tstp_time), NULL);
|
||||
raise(SIGSTOP);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handle SIGCONT (continue if stopped) by adding the elapsed time since the
|
||||
* last SIGTSTP to the elapsed time offset, and by trying to write to the
|
||||
* terminal again (by replacing the /dev/null stderr with the old stderr).
|
||||
*/
|
||||
static void pv_sig_cont(int s)
|
||||
{
|
||||
struct timeval tv;
|
||||
struct termios t;
|
||||
|
||||
pv_sig_state->pv_sig_newsize = 1;
|
||||
|
||||
if (0 == pv_sig_state->pv_sig_tstp_time.tv_sec) {
|
||||
tcgetattr(STDERR_FILENO, &t);
|
||||
t.c_lflag |= TOSTOP;
|
||||
tcsetattr(STDERR_FILENO, TCSANOW, &t);
|
||||
#ifdef HAVE_IPC
|
||||
pv_crs_needreinit(pv_sig_state);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
pv_sig_state->pv_sig_toffset.tv_sec +=
|
||||
(tv.tv_sec - pv_sig_state->pv_sig_tstp_time.tv_sec);
|
||||
pv_sig_state->pv_sig_toffset.tv_usec +=
|
||||
(tv.tv_usec - pv_sig_state->pv_sig_tstp_time.tv_usec);
|
||||
if (pv_sig_state->pv_sig_toffset.tv_usec >= 1000000) {
|
||||
pv_sig_state->pv_sig_toffset.tv_sec++;
|
||||
pv_sig_state->pv_sig_toffset.tv_usec -= 1000000;
|
||||
}
|
||||
if (pv_sig_state->pv_sig_toffset.tv_usec < 0) {
|
||||
pv_sig_state->pv_sig_toffset.tv_sec--;
|
||||
pv_sig_state->pv_sig_toffset.tv_usec += 1000000;
|
||||
}
|
||||
|
||||
pv_sig_state->pv_sig_tstp_time.tv_sec = 0;
|
||||
pv_sig_state->pv_sig_tstp_time.tv_usec = 0;
|
||||
|
||||
if (pv_sig_state->pv_sig_old_stderr != -1) {
|
||||
dup2(pv_sig_state->pv_sig_old_stderr, STDERR_FILENO);
|
||||
close(pv_sig_state->pv_sig_old_stderr);
|
||||
pv_sig_state->pv_sig_old_stderr = -1;
|
||||
}
|
||||
|
||||
tcgetattr(STDERR_FILENO, &t);
|
||||
t.c_lflag |= TOSTOP;
|
||||
tcsetattr(STDERR_FILENO, TCSANOW, &t);
|
||||
|
||||
#ifdef HAVE_IPC
|
||||
pv_crs_needreinit(pv_sig_state);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handle SIGWINCH (window size changed) by setting a flag.
|
||||
*/
|
||||
static void pv_sig_winch(int s)
|
||||
{
|
||||
pv_sig_state->pv_sig_newsize = 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handle termination signals by setting the abort flag.
|
||||
*/
|
||||
static void pv_sig_term(int s)
|
||||
{
|
||||
pv_sig_state->pv_sig_abort = 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialise signal handling.
|
||||
*/
|
||||
void pv_sig_init(pvstate_t state)
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
pv_sig_state = state;
|
||||
|
||||
pv_sig_state->pv_sig_old_stderr = -1;
|
||||
pv_sig_state->pv_sig_tstp_time.tv_sec = 0;
|
||||
pv_sig_state->pv_sig_tstp_time.tv_usec = 0;
|
||||
pv_sig_state->pv_sig_toffset.tv_sec = 0;
|
||||
pv_sig_state->pv_sig_toffset.tv_usec = 0;
|
||||
|
||||
/*
|
||||
* Ignore SIGPIPE, so we don't die if stdout is a pipe and the other
|
||||
* end closes unexpectedly.
|
||||
*/
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigemptyset(&(sa.sa_mask));
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGPIPE, &sa, &(pv_sig_state->pv_sig_old_sigpipe));
|
||||
|
||||
/*
|
||||
* Handle SIGTTOU by continuing with output switched off, so that we
|
||||
* can be stopped and backgrounded without messing up the terminal.
|
||||
*/
|
||||
sa.sa_handler = pv_sig_ttou;
|
||||
sigemptyset(&(sa.sa_mask));
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGTTOU, &sa, &(pv_sig_state->pv_sig_old_sigttou));
|
||||
|
||||
/*
|
||||
* Handle SIGTSTP by storing the time the signal happened for later
|
||||
* use by pv_sig_cont(), and then stopping the process.
|
||||
*/
|
||||
sa.sa_handler = pv_sig_tstp;
|
||||
sigemptyset(&(sa.sa_mask));
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGTSTP, &sa, &(pv_sig_state->pv_sig_old_sigtstp));
|
||||
|
||||
/*
|
||||
* Handle SIGCONT by adding the elapsed time since the last SIGTSTP
|
||||
* to the elapsed time offset, and by trying to write to the
|
||||
* terminal again.
|
||||
*/
|
||||
sa.sa_handler = pv_sig_cont;
|
||||
sigemptyset(&(sa.sa_mask));
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGCONT, &sa, &(pv_sig_state->pv_sig_old_sigcont));
|
||||
|
||||
/*
|
||||
* Handle SIGWINCH by setting a flag to let the main loop know it
|
||||
* has to reread the terminal size.
|
||||
*/
|
||||
sa.sa_handler = pv_sig_winch;
|
||||
sigemptyset(&(sa.sa_mask));
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGWINCH, &sa, &(pv_sig_state->pv_sig_old_sigwinch));
|
||||
|
||||
/*
|
||||
* Handle SIGINT, SIGHUP, SIGTERM by setting a flag to let the
|
||||
* main loop know it should quit now.
|
||||
*/
|
||||
sa.sa_handler = pv_sig_term;
|
||||
sigemptyset(&(sa.sa_mask));
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGINT, &sa, &(pv_sig_state->pv_sig_old_sigint));
|
||||
|
||||
sa.sa_handler = pv_sig_term;
|
||||
sigemptyset(&(sa.sa_mask));
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGHUP, &sa, &(pv_sig_state->pv_sig_old_sighup));
|
||||
|
||||
sa.sa_handler = pv_sig_term;
|
||||
sigemptyset(&(sa.sa_mask));
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGTERM, &sa, &(pv_sig_state->pv_sig_old_sigterm));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Shut down signal handling.
|
||||
*/
|
||||
void pv_sig_fini(pvstate_t state)
|
||||
{
|
||||
sigaction(SIGPIPE, &(pv_sig_state->pv_sig_old_sigpipe), NULL);
|
||||
sigaction(SIGTTOU, &(pv_sig_state->pv_sig_old_sigttou), NULL);
|
||||
sigaction(SIGTSTP, &(pv_sig_state->pv_sig_old_sigtstp), NULL);
|
||||
sigaction(SIGCONT, &(pv_sig_state->pv_sig_old_sigcont), NULL);
|
||||
sigaction(SIGWINCH, &(pv_sig_state->pv_sig_old_sigwinch), NULL);
|
||||
sigaction(SIGINT, &(pv_sig_state->pv_sig_old_sigint), NULL);
|
||||
sigaction(SIGHUP, &(pv_sig_state->pv_sig_old_sighup), NULL);
|
||||
sigaction(SIGTERM, &(pv_sig_state->pv_sig_old_sigterm), NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Stop reacting to SIGTSTP and SIGCONT.
|
||||
*/
|
||||
void pv_sig_nopause(void)
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigemptyset(&(sa.sa_mask));
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGTSTP, &sa, NULL);
|
||||
|
||||
sa.sa_handler = SIG_DFL;
|
||||
sigemptyset(&(sa.sa_mask));
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGCONT, &sa, NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Start catching SIGTSTP and SIGCONT again.
|
||||
*/
|
||||
void pv_sig_allowpause(void)
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = pv_sig_tstp;
|
||||
sigemptyset(&(sa.sa_mask));
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGTSTP, &sa, NULL);
|
||||
|
||||
sa.sa_handler = pv_sig_cont;
|
||||
sigemptyset(&(sa.sa_mask));
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGCONT, &sa, NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* If we have redirected stderr to /dev/null, check every second or so to
|
||||
* see whether we can write to the terminal again - this is so that if we
|
||||
* get backgrounded, then foregrounded again, we start writing to the
|
||||
* terminal again.
|
||||
*/
|
||||
void pv_sig_checkbg(void)
|
||||
{
|
||||
static time_t next_check = 0;
|
||||
struct termios t;
|
||||
|
||||
if (time(NULL) < next_check)
|
||||
return;
|
||||
|
||||
next_check = time(NULL) + 1;
|
||||
|
||||
if (-1 == pv_sig_state->pv_sig_old_stderr)
|
||||
return;
|
||||
|
||||
dup2(pv_sig_state->pv_sig_old_stderr, STDERR_FILENO);
|
||||
close(pv_sig_state->pv_sig_old_stderr);
|
||||
pv_sig_state->pv_sig_old_stderr = -1;
|
||||
|
||||
tcgetattr(STDERR_FILENO, &t);
|
||||
t.c_lflag |= TOSTOP;
|
||||
tcsetattr(STDERR_FILENO, TCSANOW, &t);
|
||||
|
||||
#ifdef HAVE_IPC
|
||||
pv_crs_needreinit(pv_sig_state);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* State management functions.
|
||||
*/
|
||||
|
||||
#include "pv-internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/*
|
||||
* Create a new state structure, and return it, or 0 (NULL) on error.
|
||||
*/
|
||||
pvstate_t pv_state_alloc(const char *program_name)
|
||||
{
|
||||
pvstate_t state;
|
||||
|
||||
state = calloc(1, sizeof(*state));
|
||||
if (0 == state)
|
||||
return 0;
|
||||
|
||||
state->program_name = program_name;
|
||||
state->watch_pid = 0;
|
||||
state->watch_fd = -1;
|
||||
#ifdef HAVE_IPC
|
||||
state->crs_shmid = -1;
|
||||
state->crs_pvcount = 1;
|
||||
#endif /* HAVE_IPC */
|
||||
state->crs_lock_fd = -1;
|
||||
|
||||
state->reparse_display = 1;
|
||||
state->current_file = _("none");
|
||||
#ifdef HAVE_SPLICE
|
||||
state->splice_failed_fd = -1;
|
||||
#endif /* HAVE_SPLICE */
|
||||
state->display_visible = 0;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Free a state structure, after which it can no longer be used.
|
||||
*/
|
||||
void pv_state_free(pvstate_t state)
|
||||
{
|
||||
if (0 == state)
|
||||
return;
|
||||
|
||||
if (state->display_buffer)
|
||||
free(state->display_buffer);
|
||||
state->display_buffer = NULL;
|
||||
|
||||
if (state->transfer_buffer)
|
||||
free(state->transfer_buffer);
|
||||
state->transfer_buffer = NULL;
|
||||
|
||||
free(state);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set the formatting string, given a set of old-style formatting options.
|
||||
*/
|
||||
void pv_state_set_format(pvstate_t state, unsigned char progress,
|
||||
unsigned char timer, unsigned char eta,
|
||||
unsigned char fineta, unsigned char rate,
|
||||
unsigned char average_rate, unsigned char bytes,
|
||||
unsigned char bufpercent,
|
||||
unsigned int lastwritten, const char *name)
|
||||
{
|
||||
#define PV_ADDFORMAT(x,y) if (x) { \
|
||||
if (state->default_format[0] != 0) \
|
||||
strcat(state->default_format, " "); \
|
||||
strcat(state->default_format, y); \
|
||||
}
|
||||
|
||||
state->default_format[0] = 0;
|
||||
PV_ADDFORMAT(name, "%N");
|
||||
PV_ADDFORMAT(bytes, "%b");
|
||||
PV_ADDFORMAT(bufpercent, "%T");
|
||||
PV_ADDFORMAT(timer, "%t");
|
||||
PV_ADDFORMAT(rate, "%r");
|
||||
PV_ADDFORMAT(average_rate, "%a");
|
||||
PV_ADDFORMAT(progress, "%p");
|
||||
PV_ADDFORMAT(eta, "%e");
|
||||
PV_ADDFORMAT(fineta, "%I");
|
||||
if (lastwritten > 0) {
|
||||
char buf[16];
|
||||
sprintf(buf, "%%%uA", lastwritten);
|
||||
PV_ADDFORMAT(lastwritten, buf);
|
||||
}
|
||||
|
||||
state->name = name;
|
||||
state->reparse_display = 1;
|
||||
}
|
||||
|
||||
|
||||
void pv_state_force_set(pvstate_t state, unsigned char val)
|
||||
{
|
||||
state->force = val;
|
||||
}
|
||||
|
||||
void pv_state_cursor_set(pvstate_t state, unsigned char val)
|
||||
{
|
||||
state->cursor = val;
|
||||
};
|
||||
|
||||
void pv_state_numeric_set(pvstate_t state, unsigned char val)
|
||||
{
|
||||
state->numeric = val;
|
||||
};
|
||||
|
||||
void pv_state_wait_set(pvstate_t state, unsigned char val)
|
||||
{
|
||||
state->wait = val;
|
||||
};
|
||||
|
||||
void pv_state_delay_start_set(pvstate_t state, double val)
|
||||
{
|
||||
state->delay_start = val;
|
||||
};
|
||||
|
||||
void pv_state_linemode_set(pvstate_t state, unsigned char val)
|
||||
{
|
||||
state->linemode = val;
|
||||
};
|
||||
|
||||
void pv_state_null_set(pvstate_t state, unsigned char val)
|
||||
{
|
||||
state->null = val;
|
||||
};
|
||||
|
||||
void pv_state_no_op_set(pvstate_t state, unsigned char val)
|
||||
{
|
||||
state->no_op = val;
|
||||
};
|
||||
|
||||
void pv_state_skip_errors_set(pvstate_t state, unsigned char val)
|
||||
{
|
||||
state->skip_errors = val;
|
||||
};
|
||||
|
||||
void pv_state_stop_at_size_set(pvstate_t state, unsigned char val)
|
||||
{
|
||||
state->stop_at_size = val;
|
||||
};
|
||||
|
||||
void pv_state_rate_limit_set(pvstate_t state, unsigned long long val)
|
||||
{
|
||||
state->rate_limit = val;
|
||||
};
|
||||
|
||||
void pv_state_target_buffer_size_set(pvstate_t state,
|
||||
unsigned long long val)
|
||||
{
|
||||
state->target_buffer_size = val;
|
||||
};
|
||||
|
||||
void pv_state_no_splice_set(pvstate_t state, unsigned char val)
|
||||
{
|
||||
state->no_splice = val;
|
||||
};
|
||||
|
||||
void pv_state_size_set(pvstate_t state, unsigned long long val)
|
||||
{
|
||||
state->size = val;
|
||||
};
|
||||
|
||||
void pv_state_interval_set(pvstate_t state, double val)
|
||||
{
|
||||
state->interval = val;
|
||||
};
|
||||
|
||||
void pv_state_width_set(pvstate_t state, unsigned int val)
|
||||
{
|
||||
state->width = val;
|
||||
};
|
||||
|
||||
void pv_state_height_set(pvstate_t state, unsigned int val)
|
||||
{
|
||||
state->height = val;
|
||||
};
|
||||
|
||||
void pv_state_name_set(pvstate_t state, const char *val)
|
||||
{
|
||||
state->name = val;
|
||||
};
|
||||
|
||||
void pv_state_format_string_set(pvstate_t state, const char *val)
|
||||
{
|
||||
state->format_string = val;
|
||||
};
|
||||
|
||||
void pv_state_watch_pid_set(pvstate_t state, unsigned int val)
|
||||
{
|
||||
state->watch_pid = val;
|
||||
};
|
||||
|
||||
void pv_state_watch_fd_set(pvstate_t state, int val)
|
||||
{
|
||||
state->watch_fd = val;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Set the array of input files.
|
||||
*/
|
||||
void pv_state_inputfiles(pvstate_t state, int input_file_count,
|
||||
const char **input_files)
|
||||
{
|
||||
state->input_file_count = input_file_count;
|
||||
state->input_files = input_files;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,861 @@
|
||||
/*
|
||||
* Functions for transferring between file descriptors.
|
||||
*/
|
||||
|
||||
#include "pv-internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
|
||||
/*
|
||||
* Read up to "count" bytes from file descriptor "fd" into the buffer "buf",
|
||||
* and return the number of bytes read, like read().
|
||||
*
|
||||
* Unlike read(), if we have read less than "count" bytes, we check to see
|
||||
* if there's any more to read, and keep trying, to make sure we fill the
|
||||
* buffer as full as we can.
|
||||
*
|
||||
* We stop retrying if the time elapsed since this function was entered
|
||||
* reaches TRANSFER_READ_TIMEOUT microseconds.
|
||||
*/
|
||||
static ssize_t pv__transfer_read_repeated(int fd, void *buf, size_t count)
|
||||
{
|
||||
struct timeval start_time;
|
||||
ssize_t total_read;
|
||||
|
||||
gettimeofday(&start_time, NULL);
|
||||
|
||||
total_read = 0;
|
||||
|
||||
while (count > 0) {
|
||||
ssize_t nread;
|
||||
struct timeval now;
|
||||
long elapsed_usec;
|
||||
|
||||
nread =
|
||||
read(fd, buf,
|
||||
count >
|
||||
MAX_READ_AT_ONCE ? MAX_READ_AT_ONCE : count);
|
||||
if (nread < 0)
|
||||
return nread;
|
||||
|
||||
total_read += nread;
|
||||
buf += nread;
|
||||
count -= nread;
|
||||
|
||||
if (0 == nread)
|
||||
return total_read;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
elapsed_usec =
|
||||
1000000 * (now.tv_sec - start_time.tv_sec) +
|
||||
(now.tv_usec - start_time.tv_usec);
|
||||
if (elapsed_usec > TRANSFER_READ_TIMEOUT) {
|
||||
debug("%s %d: %s (%ld %s)", "fd", fd,
|
||||
"stopping read - timer expired",
|
||||
elapsed_usec, "usec elapsed");
|
||||
return total_read;
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
fd_set readfds;
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(fd, &readfds);
|
||||
|
||||
debug("%s %d: %s (%ld %s, %ld %s)", "fd", fd,
|
||||
"trying another read after partial buffer fill",
|
||||
nread, "read", count, "remaining");
|
||||
|
||||
if (select(fd + 1, &readfds, NULL, NULL, &tv) < 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write up to "count" bytes to file descriptor "fd" from the buffer "buf",
|
||||
* and return the number of bytes written, like write().
|
||||
*
|
||||
* Unlike write(), if we have written less than "count" bytes, we check to
|
||||
* see if we can write any more, and keep trying, to make sure we empty the
|
||||
* buffer as much as we can.
|
||||
*
|
||||
* We stop retrying if the time elapsed since this function was entered
|
||||
* reaches TRANSFER_WRITE_TIMEOUT microseconds.
|
||||
*/
|
||||
static ssize_t pv__transfer_write_repeated(int fd, void *buf, size_t count)
|
||||
{
|
||||
struct timeval start_time;
|
||||
ssize_t total_written;
|
||||
|
||||
gettimeofday(&start_time, NULL);
|
||||
|
||||
total_written = 0;
|
||||
|
||||
while (count > 0) {
|
||||
ssize_t nwritten;
|
||||
struct timeval now;
|
||||
long elapsed_usec;
|
||||
size_t asked_to_write;
|
||||
|
||||
asked_to_write = count >
|
||||
MAX_WRITE_AT_ONCE ? MAX_WRITE_AT_ONCE : count;
|
||||
|
||||
nwritten = write(fd, buf, asked_to_write);
|
||||
if (nwritten < 0) {
|
||||
if ((EINTR == errno) || (EAGAIN == errno)) {
|
||||
/*
|
||||
* Interrupted by a signal - probably our
|
||||
* alarm() - so just return what we've
|
||||
* written so far.
|
||||
*/
|
||||
return total_written;
|
||||
} else {
|
||||
/*
|
||||
* Legitimate error - return negative.
|
||||
*/
|
||||
return nwritten;
|
||||
}
|
||||
}
|
||||
|
||||
total_written += nwritten;
|
||||
buf += nwritten;
|
||||
count -= nwritten;
|
||||
|
||||
if (0 == nwritten)
|
||||
return total_written;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
elapsed_usec =
|
||||
1000000 * (now.tv_sec - start_time.tv_sec) +
|
||||
(now.tv_usec - start_time.tv_usec);
|
||||
if (elapsed_usec > TRANSFER_WRITE_TIMEOUT) {
|
||||
debug("%s %d: %s (%ld %s)", "fd", fd,
|
||||
"stopping write - timer expired",
|
||||
elapsed_usec, "usec elapsed");
|
||||
return total_written;
|
||||
}
|
||||
|
||||
/*
|
||||
* Running the select() here seems to make PV eat a lot of
|
||||
* CPU in some cases, so instead we just go round the loop
|
||||
* again and rely on our alarm() to interrupt us if we run
|
||||
* out of time - also on our gettimeofday() check.
|
||||
*/
|
||||
if (count > 0) {
|
||||
fd_set writefds;
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
FD_ZERO(&writefds);
|
||||
FD_SET(fd, &writefds);
|
||||
|
||||
debug("%s %d: %s (%ld %s, %ld %s)", "fd", fd,
|
||||
"trying another write after partial buffer flush",
|
||||
nwritten, "written", count, "remaining");
|
||||
|
||||
# if 0 /* disabled after 1.6.0 - see comment above */
|
||||
if (select(fd + 1, NULL, &writefds, NULL, &tv) < 1)
|
||||
break;
|
||||
# endif
|
||||
}
|
||||
}
|
||||
|
||||
return total_written;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read some data from the given file descriptor. Returns zero if there was
|
||||
* a transient error and we need to return 0 from pv_transfer, otherwise
|
||||
* returns 1.
|
||||
*
|
||||
* At most, the number of bytes read will be the number of bytes remaining
|
||||
* in the input buffer. If state->rate_limit is >0, and/or "allowed" is >0,
|
||||
* then the maximum number of bytes read will be the number remaining unused
|
||||
* in the input buffer or the value of "allowed", whichever is smaller.
|
||||
*
|
||||
* If splice() was successfully used, sets state->splice_used to 1; if it
|
||||
* failed, then state->splice_failed_fd is updated to the current fd so
|
||||
* splice() won't be tried again until the next input file.
|
||||
*
|
||||
* Updates state->read_position by the number of bytes read, unless splice()
|
||||
* was used, in which case it does not since there's nothing in the buffer
|
||||
* (and it also adds the bytes to state->written since they've been written
|
||||
* to the output).
|
||||
*
|
||||
* On read error, updates state->exit_status, and if allowed by
|
||||
* state->skip_errors, tries to skip past the problem.
|
||||
*
|
||||
* If the end of the input file is reached or the error is unrecoverable,
|
||||
* sets *eof_in to 1. If all data in the buffer has been written at this
|
||||
* point, then also sets *eof_out.
|
||||
*/
|
||||
static int pv__transfer_read(pvstate_t state, int fd,
|
||||
int *eof_in, int *eof_out,
|
||||
unsigned long long allowed,
|
||||
long *lineswritten)
|
||||
{
|
||||
unsigned long bytes_can_read;
|
||||
unsigned long amount_to_skip;
|
||||
long amount_skipped;
|
||||
long orig_offset;
|
||||
long skip_offset;
|
||||
ssize_t nread;
|
||||
#ifdef HAVE_SPLICE
|
||||
size_t bytes_to_splice;
|
||||
#endif /* HAVE_SPLICE */
|
||||
|
||||
bytes_can_read = state->buffer_size - state->read_position;
|
||||
|
||||
#ifdef HAVE_SPLICE
|
||||
state->splice_used = 0;
|
||||
if ((!state->linemode) && (!state->no_splice)
|
||||
&& (fd != state->splice_failed_fd)
|
||||
&& (0 == state->to_write)) {
|
||||
if (state->rate_limit || allowed != 0)
|
||||
bytes_to_splice = allowed;
|
||||
else
|
||||
bytes_to_splice = bytes_can_read;
|
||||
|
||||
nread = splice(fd, NULL, STDOUT_FILENO, NULL,
|
||||
bytes_to_splice, SPLICE_F_MORE);
|
||||
|
||||
state->splice_used = 1;
|
||||
if ((nread < 0) && (EINVAL == errno)) {
|
||||
debug("%s %d: %s", "fd", fd,
|
||||
"splice failed with EINVAL - disabling");
|
||||
state->splice_failed_fd = fd;
|
||||
state->splice_used = 0;
|
||||
/*
|
||||
* Fall through to read() below.
|
||||
*/
|
||||
} else if (nread > 0) {
|
||||
state->written = nread;
|
||||
} else if ((-1 == nread) && (EAGAIN == errno)) {
|
||||
/* nothing read yet - do nothing */
|
||||
} else {
|
||||
/* EOF might not really be EOF, it seems */
|
||||
state->splice_used = 0;
|
||||
}
|
||||
}
|
||||
if (0 == state->splice_used) {
|
||||
nread =
|
||||
pv__transfer_read_repeated(fd,
|
||||
state->transfer_buffer +
|
||||
state->read_position,
|
||||
bytes_can_read);
|
||||
}
|
||||
#else
|
||||
nread =
|
||||
pv__transfer_read_repeated(fd,
|
||||
state->transfer_buffer +
|
||||
state->read_position,
|
||||
bytes_can_read);
|
||||
#endif /* HAVE_SPLICE */
|
||||
|
||||
|
||||
if (0 == nread) {
|
||||
/*
|
||||
* If read returned 0, we've reached the end of this input
|
||||
* file. If we've also written all the data in the transfer
|
||||
* buffer, we set eof_out as well, so that the main loop can
|
||||
* move on to the next input file.
|
||||
*/
|
||||
*eof_in = 1;
|
||||
if (state->write_position >= state->read_position)
|
||||
*eof_out = 1;
|
||||
return 1;
|
||||
} else if (nread > 0) {
|
||||
/*
|
||||
* Read returned >0, so we successfully read data - clear
|
||||
* the error counter and update our record of how much data
|
||||
* we've got in the buffer.
|
||||
*/
|
||||
state->read_errors_in_a_row = 0;
|
||||
#ifdef HAVE_SPLICE
|
||||
/*
|
||||
* If we used splice(), there isn't any more data in the
|
||||
* buffer than there was before.
|
||||
*/
|
||||
if (0 == state->splice_used)
|
||||
state->read_position += nread;
|
||||
#else
|
||||
state->read_position += nread;
|
||||
#endif /* HAVE_SPLICE */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we reach this point, nread<0, so there was an error.
|
||||
*/
|
||||
|
||||
/*
|
||||
* If a read error occurred but it was EINTR or EAGAIN, just wait a
|
||||
* bit and then return zero, since this was a transient error.
|
||||
*/
|
||||
if ((EINTR == errno) || (EAGAIN == errno)) {
|
||||
struct timeval tv;
|
||||
debug("%s %d: %s: %s", "fd", fd,
|
||||
"transient error - waiting briefly",
|
||||
strerror(errno));
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 10000;
|
||||
select(0, NULL, NULL, NULL, &tv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The read error is not transient, so update the program's final
|
||||
* exit status, regardless of whether we're skipping errors, and
|
||||
* increment the error counter.
|
||||
*/
|
||||
state->exit_status |= 16;
|
||||
state->read_errors_in_a_row++;
|
||||
|
||||
/*
|
||||
* If we aren't skipping errors, show the error and pretend we
|
||||
* reached the end of this file.
|
||||
*/
|
||||
if (0 == state->skip_errors) {
|
||||
pv_error(state, "%s: %s: %s",
|
||||
state->current_file,
|
||||
_("read failed"), strerror(errno));
|
||||
*eof_in = 1;
|
||||
if (state->write_position >= state->read_position) {
|
||||
*eof_out = 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to skip past the error.
|
||||
*/
|
||||
|
||||
amount_skipped = -1;
|
||||
|
||||
if (!state->read_error_warning_shown) {
|
||||
pv_error(state, "%s: %s: %s",
|
||||
state->current_file,
|
||||
_
|
||||
("warning: read errors detected"),
|
||||
strerror(errno));
|
||||
state->read_error_warning_shown = 1;
|
||||
}
|
||||
|
||||
orig_offset = lseek64(fd, 0, SEEK_CUR);
|
||||
|
||||
/*
|
||||
* If the file is not seekable, we can't skip past the error, so we
|
||||
* will have to abandon the attempt and pretend we reached the end
|
||||
* of the file.
|
||||
*/
|
||||
if (0 > orig_offset) {
|
||||
pv_error(state, "%s: %s: %s",
|
||||
state->current_file,
|
||||
_("file is not seekable"), strerror(errno));
|
||||
*eof_in = 1;
|
||||
if (state->write_position >= state->read_position) {
|
||||
*eof_out = 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (state->read_errors_in_a_row < 10) {
|
||||
amount_to_skip = state->read_errors_in_a_row < 5 ? 1 : 2;
|
||||
} else if (state->read_errors_in_a_row < 20) {
|
||||
amount_to_skip = 1 << (state->read_errors_in_a_row - 10);
|
||||
} else {
|
||||
amount_to_skip = 512;
|
||||
}
|
||||
|
||||
/*
|
||||
* Round the skip amount down to the start of the next block of the
|
||||
* skip amount size. For instance if the skip amount is 512, but
|
||||
* our file offset is 257, we'll jump to 512 instead of 769.
|
||||
*/
|
||||
if (amount_to_skip > 1) {
|
||||
skip_offset = orig_offset + amount_to_skip;
|
||||
skip_offset -= (skip_offset % amount_to_skip);
|
||||
if (skip_offset > orig_offset) {
|
||||
amount_to_skip = skip_offset - orig_offset;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Trim the skip amount so we wouldn't read too much.
|
||||
*/
|
||||
if (amount_to_skip > bytes_can_read)
|
||||
amount_to_skip = bytes_can_read;
|
||||
|
||||
skip_offset = lseek64(fd, orig_offset + amount_to_skip, SEEK_SET);
|
||||
|
||||
/*
|
||||
* If the skip we just tried didn't work, try only skipping 1 byte
|
||||
* in case we were trying to go past the end of the input file.
|
||||
*/
|
||||
if (skip_offset < 0) {
|
||||
amount_to_skip = 1;
|
||||
skip_offset =
|
||||
lseek64(fd, orig_offset + amount_to_skip, SEEK_SET);
|
||||
}
|
||||
|
||||
if (skip_offset < 0) {
|
||||
/*
|
||||
* Failed to skip - lseek() returned an error, so mark the
|
||||
* file as having ended.
|
||||
*/
|
||||
*eof_in = 1;
|
||||
/*
|
||||
* EINVAL means the file has ended since we've tried to go
|
||||
* past the end of it, so we don't bother with a warning
|
||||
* since it just means we've reached the end anyway.
|
||||
*/
|
||||
if (EINVAL != errno) {
|
||||
pv_error(state,
|
||||
"%s: %s: %s",
|
||||
state->current_file,
|
||||
_
|
||||
("failed to seek past error"),
|
||||
strerror(errno));
|
||||
}
|
||||
} else {
|
||||
amount_skipped = skip_offset - orig_offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we succeeded in skipping some bytes, zero the equivalent part
|
||||
* of the transfer buffer, and update the buffer position.
|
||||
*/
|
||||
if (amount_skipped > 0) {
|
||||
memset(state->transfer_buffer +
|
||||
state->read_position, 0, amount_skipped);
|
||||
state->read_position += amount_skipped;
|
||||
if (state->skip_errors < 2) {
|
||||
pv_error(state, "%s: %s: %ld - %ld (%ld %s)",
|
||||
state->current_file,
|
||||
_
|
||||
("skipped past read error"),
|
||||
orig_offset,
|
||||
skip_offset, amount_skipped, _("B"));
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Failed to skip - mark file as ended.
|
||||
*/
|
||||
*eof_in = 1;
|
||||
if (state->write_position >= state->read_position) {
|
||||
*eof_out = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write state->to_write bytes of data from the transfer buffer to stdout.
|
||||
* Returns zero if there was a transient error and we need to return 0 from
|
||||
* pv_transfer, otherwise returns 1.
|
||||
*
|
||||
* Updates state->write_position by moving it on by the number of bytes
|
||||
* written; adds the number of bytes written to state->written; sets
|
||||
* *eof_out on stdout EOF or when the write position catches up with the
|
||||
* read position AND *eof_in is 1 (meaning we've reached the end of data).
|
||||
*
|
||||
* On error, sets *eof_out to 1, sets state->written to -1, and updates
|
||||
* state->exit_status.
|
||||
*/
|
||||
static int pv__transfer_write(pvstate_t state, int fd,
|
||||
int *eof_in, int *eof_out,
|
||||
long *lineswritten)
|
||||
{
|
||||
ssize_t nwritten;
|
||||
|
||||
signal(SIGALRM, SIG_IGN);
|
||||
alarm(1);
|
||||
|
||||
nwritten = pv__transfer_write_repeated(STDOUT_FILENO,
|
||||
state->transfer_buffer +
|
||||
state->write_position,
|
||||
state->to_write);
|
||||
|
||||
alarm(0);
|
||||
|
||||
if (0 == nwritten) {
|
||||
/*
|
||||
* Write returned 0 - EOF on stdout.
|
||||
*/
|
||||
*eof_out = 1;
|
||||
return 1;
|
||||
} else if (nwritten > 0) {
|
||||
/*
|
||||
* Write returned >0 - data successfully written.
|
||||
*/
|
||||
if ((state->linemode) && (lineswritten != NULL)) {
|
||||
/*
|
||||
* Guillaume Marcais: use strchr to count \n
|
||||
*/
|
||||
unsigned char save;
|
||||
char *ptr;
|
||||
long lines = 0;
|
||||
|
||||
save =
|
||||
state->transfer_buffer[state->write_position +
|
||||
nwritten];
|
||||
state->transfer_buffer[state->write_position +
|
||||
nwritten] = 0;
|
||||
ptr =
|
||||
(char *) (state->transfer_buffer +
|
||||
state->write_position - 1);
|
||||
|
||||
if (state->null) {
|
||||
for (ptr++;
|
||||
ptr -
|
||||
(char *) state->transfer_buffer -
|
||||
state->write_position < nwritten;
|
||||
ptr++) {
|
||||
if (*ptr == '\0')
|
||||
++lines;
|
||||
}
|
||||
} else {
|
||||
while ((ptr =
|
||||
strchr((char *) (ptr + 1), '\n')))
|
||||
++lines;
|
||||
}
|
||||
|
||||
*lineswritten += lines;
|
||||
state->transfer_buffer[state->write_position +
|
||||
nwritten] = save;
|
||||
}
|
||||
|
||||
state->write_position += nwritten;
|
||||
state->written += nwritten;
|
||||
|
||||
/*
|
||||
* If we're monitoring the output, update our copy of the
|
||||
* last few bytes we've written.
|
||||
*/
|
||||
if (((state->components_used & PV_DISPLAY_OUTPUTBUF) != 0)
|
||||
&& (nwritten > 0)) {
|
||||
long new_portion_length, old_portion_length;
|
||||
|
||||
new_portion_length = nwritten;
|
||||
if (new_portion_length > state->lastoutput_length)
|
||||
new_portion_length =
|
||||
state->lastoutput_length;
|
||||
|
||||
old_portion_length =
|
||||
state->lastoutput_length - new_portion_length;
|
||||
|
||||
/*
|
||||
* Make room for the new portion.
|
||||
*/
|
||||
if (old_portion_length > 0) {
|
||||
memmove(state->lastoutput_buffer,
|
||||
state->lastoutput_buffer +
|
||||
new_portion_length,
|
||||
old_portion_length);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy the new data in.
|
||||
*/
|
||||
memcpy(state->lastoutput_buffer +
|
||||
old_portion_length,
|
||||
state->transfer_buffer +
|
||||
state->write_position - new_portion_length,
|
||||
new_portion_length);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we've written all the data in the buffer, reset the
|
||||
* read pointer to the start, and if the input file is at
|
||||
* EOF, set eof_out as well to indicate that we've written
|
||||
* everything for this input file.
|
||||
*/
|
||||
if (state->write_position >= state->read_position) {
|
||||
state->write_position = 0;
|
||||
state->read_position = 0;
|
||||
if (*eof_in)
|
||||
*eof_out = 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we reach this point, nwritten<0, so there was an error.
|
||||
*/
|
||||
|
||||
/*
|
||||
* If a write error occurred but it was EINTR or EAGAIN, just wait a
|
||||
* bit and then return zero, since this was a transient error.
|
||||
*/
|
||||
if ((EINTR == errno) || (EAGAIN == errno)) {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 10000;
|
||||
select(0, NULL, NULL, NULL, &tv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* SIGPIPE means we've finished. Don't output an error because it's
|
||||
* not really our error to report.
|
||||
*/
|
||||
if (EPIPE == errno) {
|
||||
*eof_in = 1;
|
||||
*eof_out = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pv_error(state, "%s: %s", _("write failed"), strerror(errno));
|
||||
state->exit_status |= 16;
|
||||
*eof_out = 1;
|
||||
state->written = -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Transfer some data from "fd" to standard output, timing out after 9/100
|
||||
* of a second. If state->rate_limit is >0, and/or "allowed" is >0, only up
|
||||
* to "allowed" bytes can be written. The variables that "eof_in" and
|
||||
* "eof_out" point to are used to flag that we've finished reading and
|
||||
* writing respectively.
|
||||
*
|
||||
* Returns the number of bytes written, or negative on error (in which case
|
||||
* state->exit_status is updated). In line mode, the number of lines written
|
||||
* will be put into *lineswritten.
|
||||
*/
|
||||
long pv_transfer(pvstate_t state, int fd, int *eof_in, int *eof_out,
|
||||
unsigned long long allowed, long *lineswritten)
|
||||
{
|
||||
struct timeval tv;
|
||||
fd_set readfds;
|
||||
fd_set writefds;
|
||||
int max_fd;
|
||||
int n;
|
||||
|
||||
if (NULL == state)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Reinitialise the error skipping variables if the file descriptor
|
||||
* has changed since the last time we were called.
|
||||
*/
|
||||
if (fd != state->last_read_skip_fd) {
|
||||
state->last_read_skip_fd = fd;
|
||||
state->read_errors_in_a_row = 0;
|
||||
state->read_error_warning_shown = 0;
|
||||
}
|
||||
|
||||
if (NULL == state->transfer_buffer) {
|
||||
state->buffer_size = state->target_buffer_size;
|
||||
state->transfer_buffer =
|
||||
(unsigned char *) malloc(state->buffer_size + 32);
|
||||
if (NULL == state->transfer_buffer) {
|
||||
pv_error(state, "%s: %s",
|
||||
_("buffer allocation failed"),
|
||||
strerror(errno));
|
||||
state->exit_status |= 64;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reallocate the buffer if the buffer size has changed mid-transfer.
|
||||
*/
|
||||
if (state->buffer_size < state->target_buffer_size) {
|
||||
unsigned char *newptr;
|
||||
newptr =
|
||||
realloc(state->transfer_buffer,
|
||||
state->target_buffer_size + 32);
|
||||
if (NULL == newptr) {
|
||||
/*
|
||||
* Reset target if realloc failed so we don't keep
|
||||
* trying to realloc over and over.
|
||||
*/
|
||||
debug("realloc: %s", strerror(errno));
|
||||
state->target_buffer_size = state->buffer_size;
|
||||
} else {
|
||||
debug("%s: %ld", "buffer resized",
|
||||
state->buffer_size);
|
||||
state->transfer_buffer = newptr;
|
||||
state->buffer_size = state->target_buffer_size;
|
||||
}
|
||||
}
|
||||
|
||||
if ((state->linemode) && (lineswritten != NULL))
|
||||
*lineswritten = 0;
|
||||
|
||||
if ((*eof_in) && (*eof_out))
|
||||
return 0;
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 90000;
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_ZERO(&writefds);
|
||||
|
||||
max_fd = 0;
|
||||
|
||||
/*
|
||||
* If the input file is not at EOF and there's room in the buffer,
|
||||
* look for incoming data from it.
|
||||
*/
|
||||
if ((!(*eof_in)) && (state->read_position < state->buffer_size)) {
|
||||
FD_SET(fd, &readfds);
|
||||
if (fd > max_fd)
|
||||
max_fd = fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Work out how much we're allowed to write, based on the amount of
|
||||
* data left in the buffer. If rate limiting is active or "allowed"
|
||||
* is >0, then this puts an upper limit on how much we're allowed to
|
||||
* write.
|
||||
*/
|
||||
state->to_write = state->read_position - state->write_position;
|
||||
if ((state->rate_limit > 0) || (allowed > 0)) {
|
||||
if (state->to_write > allowed) {
|
||||
state->to_write = allowed;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we don't think we've finished writing and there's anything
|
||||
* we're allowed to write, look for the stdout becoming writable.
|
||||
*/
|
||||
if ((!(*eof_out)) && (state->to_write > 0)) {
|
||||
FD_SET(STDOUT_FILENO, &writefds);
|
||||
if (STDOUT_FILENO > max_fd)
|
||||
max_fd = STDOUT_FILENO;
|
||||
}
|
||||
|
||||
n = select(max_fd + 1, &readfds, &writefds, NULL, &tv);
|
||||
|
||||
if (n < 0) {
|
||||
/*
|
||||
* Ignore transient errors by returning 0 immediately.
|
||||
*/
|
||||
if (EINTR == errno)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Any other error is a problem and we must report back.
|
||||
*/
|
||||
pv_error(state, "%s: %s: %d: %s",
|
||||
state->current_file,
|
||||
_("select call failed"), n, strerror(errno));
|
||||
|
||||
state->exit_status |= 16;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
state->written = 0;
|
||||
|
||||
/*
|
||||
* If there is data to read, try to read some in. Return early if
|
||||
* there was a transient read error.
|
||||
*
|
||||
* NB this can update state->written because of splice().
|
||||
*/
|
||||
if (FD_ISSET(fd, &readfds)) {
|
||||
if (pv__transfer_read
|
||||
(state, fd, eof_in, eof_out, allowed,
|
||||
lineswritten) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* In line mode, only write up to and including the last newline,
|
||||
* so that we're writing output line-by-line.
|
||||
*/
|
||||
if ((state->to_write > 0) && (state->linemode) && !(state->null)) {
|
||||
/*
|
||||
* Guillaume Marcais: use strrchr to find last \n
|
||||
*/
|
||||
unsigned char save;
|
||||
char *start;
|
||||
char *end;
|
||||
|
||||
save =
|
||||
state->transfer_buffer[state->write_position +
|
||||
state->to_write];
|
||||
state->transfer_buffer[state->write_position +
|
||||
state->to_write] = 0;
|
||||
|
||||
start =
|
||||
(char *) (state->transfer_buffer +
|
||||
state->write_position);
|
||||
end = strrchr(start, '\n');
|
||||
state->transfer_buffer[state->write_position +
|
||||
state->to_write] = save;
|
||||
|
||||
if (end != NULL) {
|
||||
state->to_write = (end - start) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is data to write, and stdout is ready to receive it, and
|
||||
* we didn't use splice() this time, write some data. Return early
|
||||
* if there was a transient write error.
|
||||
*/
|
||||
if (FD_ISSET(STDOUT_FILENO, &writefds)
|
||||
#ifdef HAVE_SPLICE
|
||||
&& (0 == state->splice_used)
|
||||
#endif /* HAVE_SPLICE */
|
||||
&& (state->read_position > state->write_position)
|
||||
&& (state->to_write > 0)) {
|
||||
if (pv__transfer_write
|
||||
(state, fd, eof_in, eof_out, lineswritten) == 0)
|
||||
return 0;
|
||||
}
|
||||
#ifdef MAXIMISE_BUFFER_FILL
|
||||
/*
|
||||
* Rotate the written bytes out of the buffer so that it can be
|
||||
* filled up completely by the next read.
|
||||
*/
|
||||
if (state->write_position > 0) {
|
||||
if (state->write_position < state->read_position) {
|
||||
memmove(state->transfer_buffer,
|
||||
state->transfer_buffer +
|
||||
state->write_position,
|
||||
state->read_position -
|
||||
state->write_position);
|
||||
state->read_position -= state->write_position;
|
||||
state->write_position = 0;
|
||||
} else {
|
||||
state->write_position = 0;
|
||||
state->read_position = 0;
|
||||
}
|
||||
}
|
||||
#endif /* MAXIMISE_BUFFER_FILL */
|
||||
|
||||
return state->written;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
* Functions for watching file descriptors in other processes.
|
||||
*/
|
||||
|
||||
#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>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
|
||||
/*
|
||||
* Fill in the given information structure with the file paths and stat
|
||||
* details of the given file descriptor within the given process (given
|
||||
* within the info structure).
|
||||
*
|
||||
* Returns nonzero on error - error codes are:
|
||||
*
|
||||
* -1 - info or state were NULL
|
||||
* 1 - process does not exist
|
||||
* 2 - readlink on /proc/pid/fd/N failed
|
||||
* 3 - stat or lstat on /proc/pid/fd/N failed
|
||||
* 4 - file descriptor is not opened on a regular file
|
||||
*
|
||||
* If "automatic" is nonzero, then this fd was picked automatically, and so
|
||||
* if it's not readable or not a regular file, no error is displayed and the
|
||||
* function just returns an error code.
|
||||
*/
|
||||
int pv_watchfd_info(pvstate_t state, pvwatchfd_t info, int automatic)
|
||||
{
|
||||
if (NULL == state)
|
||||
return -1;
|
||||
if (NULL == info)
|
||||
return -1;
|
||||
|
||||
if (kill(info->watch_pid, 0) != 0) {
|
||||
if (!automatic)
|
||||
pv_error(state, "%s %u: %s",
|
||||
_("pid"),
|
||||
info->watch_pid, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
snprintf(info->file_fdinfo, sizeof(info->file_fdinfo) - 1,
|
||||
"/proc/%u/fdinfo/%d", info->watch_pid, info->watch_fd);
|
||||
snprintf(info->file_fd, sizeof(info->file_fd) - 1,
|
||||
"/proc/%u/fd/%d", info->watch_pid, info->watch_fd);
|
||||
|
||||
if (readlink
|
||||
(info->file_fd, info->file_fdpath,
|
||||
sizeof(info->file_fdpath) - 1) < 0) {
|
||||
if (!automatic)
|
||||
pv_error(state, "%s %u: %s %d: %s",
|
||||
_("pid"),
|
||||
info->watch_pid,
|
||||
_("fd"), info->watch_fd, strerror(errno));
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (!((0 == stat64(info->file_fd, &(info->sb_fd)))
|
||||
&& (0 == lstat64(info->file_fd, &(info->sb_fd_link))))) {
|
||||
if (!automatic)
|
||||
pv_error(state, "%s %u: %s %d: %s: %s",
|
||||
_("pid"),
|
||||
info->watch_pid,
|
||||
_("fd"),
|
||||
info->watch_fd, info->file_fdpath,
|
||||
strerror(errno));
|
||||
return 3;
|
||||
}
|
||||
|
||||
info->size = 0;
|
||||
|
||||
if (S_ISBLK(info->sb_fd.st_mode)) {
|
||||
int fd;
|
||||
|
||||
/*
|
||||
* Get the size of block devices by opening
|
||||
* them and seeking to the end.
|
||||
*/
|
||||
fd = open64(info->file_fdpath, O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
info->size = lseek64(fd, 0, SEEK_END);
|
||||
close(fd);
|
||||
} else {
|
||||
info->size = 0;
|
||||
}
|
||||
} else if (S_ISREG(info->sb_fd.st_mode)) {
|
||||
if ((info->sb_fd_link.st_mode & S_IWUSR) == 0) {
|
||||
info->size = info->sb_fd.st_size;
|
||||
}
|
||||
} else {
|
||||
if (!automatic)
|
||||
pv_error(state, "%s %u: %s %d: %s: %s",
|
||||
_("pid"),
|
||||
info->watch_pid,
|
||||
_("fd"),
|
||||
info->watch_fd,
|
||||
info->file_fdpath,
|
||||
_("not a regular file or block device"));
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return nonzero if the given file descriptor has changed in some way since
|
||||
* we started looking at it (i.e. changed destination or permissions).
|
||||
*/
|
||||
int pv_watchfd_changed(pvwatchfd_t info)
|
||||
{
|
||||
struct stat64 sb_fd, sb_fd_link;
|
||||
|
||||
if ((0 == stat64(info->file_fd, &sb_fd))
|
||||
&& (0 == lstat64(info->file_fd, &sb_fd_link))) {
|
||||
if ((sb_fd.st_dev != info->sb_fd.st_dev)
|
||||
|| (sb_fd.st_ino != info->sb_fd.st_ino)
|
||||
|| (sb_fd_link.st_mode != info->sb_fd_link.st_mode)
|
||||
) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the current file position of the given file descriptor, or -1 if
|
||||
* the fd has closed or has changed in some way.
|
||||
*/
|
||||
long long pv_watchfd_position(pvwatchfd_t info)
|
||||
{
|
||||
long long position;
|
||||
FILE *fptr;
|
||||
|
||||
if (pv_watchfd_changed(info))
|
||||
return -1;
|
||||
|
||||
fptr = fopen(info->file_fdinfo, "r");
|
||||
if (NULL == fptr)
|
||||
return -1;
|
||||
position = -1;
|
||||
fscanf(fptr, "pos: %llu", &position);
|
||||
fclose(fptr);
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Scan the given process and update the arrays with any new file
|
||||
* descriptors.
|
||||
*
|
||||
* Returns 0 on success, 1 if the process no longer exists or could not be
|
||||
* read, or 2 for a memory allocation error.
|
||||
*/
|
||||
int pv_watchpid_scanfds(pvstate_t state, pvstate_t pristine,
|
||||
unsigned int watch_pid, int *array_length_ptr,
|
||||
pvwatchfd_t * info_array_ptr,
|
||||
pvstate_t * state_array_ptr, int *fd_to_idx)
|
||||
{
|
||||
char fd_dir[512] = { 0, };
|
||||
DIR *dptr;
|
||||
struct dirent *d;
|
||||
int array_length = 0;
|
||||
struct pvwatchfd_s *info_array = NULL;
|
||||
struct pvstate_s *state_array = NULL;
|
||||
|
||||
snprintf(fd_dir, sizeof(fd_dir) - 1, "/proc/%u/fd", watch_pid);
|
||||
dptr = opendir(fd_dir);
|
||||
if (NULL == dptr)
|
||||
return 1;
|
||||
|
||||
array_length = *array_length_ptr;
|
||||
info_array = *info_array_ptr;
|
||||
state_array = *state_array_ptr;
|
||||
|
||||
while ((d = readdir(dptr)) != NULL) {
|
||||
int fd, check_idx, use_idx, rc;
|
||||
long long position_now;
|
||||
|
||||
fd = -1;
|
||||
if (sscanf(d->d_name, "%d", &fd) != 1)
|
||||
continue;
|
||||
if ((fd < 0) || (fd >= FD_SETSIZE))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Skip if this fd is already known to us.
|
||||
*/
|
||||
if (fd_to_idx[fd] != -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* See if there's an empty slot we can re-use. An empty slot
|
||||
* has a watch_pid of 0.
|
||||
*/
|
||||
use_idx = -1;
|
||||
for (check_idx = 0; check_idx < array_length; check_idx++) {
|
||||
if (info_array[check_idx].watch_pid == 0) {
|
||||
use_idx = check_idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If there's no empty slot, extend the arrays.
|
||||
*/
|
||||
if (use_idx < 0) {
|
||||
struct pvwatchfd_s *new_info_array;
|
||||
struct pvstate_s *new_state_array;
|
||||
|
||||
array_length++;
|
||||
use_idx = array_length - 1;
|
||||
|
||||
if (NULL == info_array) {
|
||||
new_info_array =
|
||||
malloc(array_length *
|
||||
sizeof(*info_array));
|
||||
} else {
|
||||
new_info_array =
|
||||
realloc(info_array,
|
||||
array_length *
|
||||
sizeof(*info_array));
|
||||
}
|
||||
if (NULL == new_info_array)
|
||||
return 2;
|
||||
info_array = new_info_array;
|
||||
*info_array_ptr = info_array;
|
||||
info_array[use_idx].watch_pid = 0;
|
||||
|
||||
if (NULL == state_array) {
|
||||
new_state_array =
|
||||
malloc(array_length *
|
||||
sizeof(*state_array));
|
||||
} else {
|
||||
new_state_array =
|
||||
realloc(state_array,
|
||||
array_length *
|
||||
sizeof(*state_array));
|
||||
}
|
||||
if (NULL == new_state_array)
|
||||
return 2;
|
||||
state_array = new_state_array;
|
||||
*state_array_ptr = state_array;
|
||||
|
||||
*array_length_ptr = array_length;
|
||||
|
||||
for (check_idx = 0; check_idx < array_length;
|
||||
check_idx++) {
|
||||
state_array[check_idx].name =
|
||||
info_array[check_idx].display_name;
|
||||
state_array[check_idx].reparse_display = 1;
|
||||
}
|
||||
}
|
||||
|
||||
debug("%s: %d => index %d", "found new fd", fd, use_idx);
|
||||
|
||||
/*
|
||||
* Initialise the details of this new entry.
|
||||
*/
|
||||
memcpy(&(state_array[use_idx]), pristine,
|
||||
sizeof(*pristine));
|
||||
memset(&(info_array[use_idx]), 0,
|
||||
sizeof(info_array[use_idx]));
|
||||
|
||||
info_array[use_idx].watch_pid = watch_pid;
|
||||
info_array[use_idx].watch_fd = fd;
|
||||
rc = pv_watchfd_info(state, &(info_array[use_idx]), 1);
|
||||
|
||||
/*
|
||||
* Lookup failed - mark this slot as being free for re-use.
|
||||
*/
|
||||
if ((rc != 0) && (rc != 4)) {
|
||||
info_array[use_idx].watch_pid = 0;
|
||||
debug("%s %d: %s: %d", "fd", fd,
|
||||
"lookup failed - marking slot for re-use",
|
||||
use_idx);
|
||||
continue;
|
||||
}
|
||||
|
||||
fd_to_idx[fd] = use_idx;
|
||||
|
||||
/*
|
||||
* Not displayable - set fd to -1 so the main loop doesn't
|
||||
* show it.
|
||||
*/
|
||||
if (rc != 0) {
|
||||
debug("%s %d: %s", "fd", fd,
|
||||
"marking as not displayable");
|
||||
info_array[use_idx].watch_fd = -1;
|
||||
}
|
||||
|
||||
state_array[use_idx].size = info_array[use_idx].size;
|
||||
if (state_array[use_idx].size < 1) {
|
||||
char *fmt;
|
||||
while (NULL !=
|
||||
(fmt =
|
||||
strstr(state_array[use_idx].default_format,
|
||||
"%e"))) {
|
||||
debug("%s", "zero size - removing ETA");
|
||||
/* strlen-1 here to include trailing NUL */
|
||||
memmove(fmt, fmt + 2, strlen(fmt) - 1);
|
||||
state_array[use_idx].reparse_display = 1;
|
||||
}
|
||||
}
|
||||
|
||||
state_array[use_idx].name =
|
||||
info_array[use_idx].display_name;
|
||||
|
||||
pv_watchpid_setname(state, &(info_array[use_idx]));
|
||||
|
||||
state_array[use_idx].reparse_display = 1;
|
||||
|
||||
gettimeofday(&(info_array[use_idx].start_time), NULL);
|
||||
|
||||
state_array[use_idx].initial_offset = 0;
|
||||
info_array[use_idx].position = 0;
|
||||
|
||||
position_now = pv_watchfd_position(&(info_array[use_idx]));
|
||||
if (position_now >= 0) {
|
||||
state_array[use_idx].initial_offset = position_now;
|
||||
info_array[use_idx].position = position_now;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dptr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set the display name for the given watched file descriptor, truncating at
|
||||
* the relevant places according to the current screen width.
|
||||
*/
|
||||
void pv_watchpid_setname(pvstate_t state, pvwatchfd_t info)
|
||||
{
|
||||
int path_length, max_display_length;
|
||||
|
||||
memset(info->display_name, 0, sizeof(info->display_name));
|
||||
|
||||
path_length = strlen(info->file_fdpath);
|
||||
|
||||
max_display_length = (state->width / 2) - 6;
|
||||
if (max_display_length >= path_length) {
|
||||
snprintf(info->display_name,
|
||||
sizeof(info->display_name) - 1, "%4d:%s",
|
||||
info->watch_fd, info->file_fdpath);
|
||||
} else {
|
||||
int prefix_length, suffix_length;
|
||||
|
||||
prefix_length = max_display_length / 4;
|
||||
suffix_length = max_display_length - prefix_length - 3;
|
||||
|
||||
snprintf(info->display_name,
|
||||
sizeof(info->display_name) - 1, "%4d:%.*s...%.*s",
|
||||
info->watch_fd, prefix_length, info->file_fdpath,
|
||||
suffix_length,
|
||||
info->file_fdpath + path_length - suffix_length);
|
||||
}
|
||||
|
||||
debug("%s: %d: [%s]", "set name for fd", info->watch_fd,
|
||||
info->display_name);
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
Reference in New Issue
Block a user