add -o/--output option

This commit is contained in:
Martin Dørum
2024-06-08 14:31:19 +02:00
parent ace8f44f59
commit 30763684b7
13 changed files with 122 additions and 63 deletions
+6 -6
View File
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: pv@ivarch.com\n"
"POT-Creation-Date: 2024-04-21 14:15+0100\n"
"POT-Creation-Date: 2024-06-08 12:44+0200\n"
"Language: de\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Date: 1999-06-01 15:18:29+0100\n"
@@ -368,7 +368,7 @@ msgstr ""
msgid "yzafpnum KMGTPEZY"
msgstr ""
#: src/pv/display.c:750 src/pv/transfer.c:954
#: src/pv/display.c:750 src/pv/transfer.c:958
msgid "buffer allocation failed"
msgstr "Puffer konnte nicht allokiert werden"
@@ -424,7 +424,7 @@ msgstr ""
msgid "(stdin)"
msgstr ""
#: src/pv/loop.c:570 src/pv/loop.c:621 src/pv/loop.c:662 src/pv/watchpid.c:99
#: src/pv/loop.c:575 src/pv/loop.c:626 src/pv/loop.c:667 src/pv/watchpid.c:99
#: src/pv/watchpid.c:109 src/pv/watchpid.c:120 src/pv/watchpid.c:128
#: src/pv/watchpid.c:165 src/pv/watchpid.c:184 src/pv/watchpid.c:192
#: src/pv/watchpid.c:202 src/pv/watchpid.c:293 src/pv/watchpid.c:301
@@ -468,15 +468,15 @@ msgstr ""
msgid "failed to set interval timer"
msgstr ""
#: src/pv/transfer.c:694
#: src/pv/transfer.c:697
msgid "failed to clear interval timer"
msgstr ""
#: src/pv/transfer.c:818
#: src/pv/transfer.c:822
msgid "write failed"
msgstr "write-Aufruf fehlgeschlagen"
#: src/pv/transfer.c:1051
#: src/pv/transfer.c:1055
msgid "select call failed"
msgstr "select-Aufruf fehlgeschlagen"
+6 -6
View File
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: pv@ivarch.com\n"
"POT-Creation-Date: 2024-04-21 14:15+0100\n"
"POT-Creation-Date: 2024-06-08 12:44+0200\n"
"Language: fr\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Date: 1999-06-01 15:18:29+0100\n"
@@ -373,7 +373,7 @@ msgstr ""
msgid "yzafpnum KMGTPEZY"
msgstr ""
#: src/pv/display.c:750 src/pv/transfer.c:954
#: src/pv/display.c:750 src/pv/transfer.c:958
msgid "buffer allocation failed"
msgstr "échec de l'allocation de mémoire tampon"
@@ -429,7 +429,7 @@ msgstr "(aucun)"
msgid "(stdin)"
msgstr ""
#: src/pv/loop.c:570 src/pv/loop.c:621 src/pv/loop.c:662 src/pv/watchpid.c:99
#: src/pv/loop.c:575 src/pv/loop.c:626 src/pv/loop.c:667 src/pv/watchpid.c:99
#: src/pv/watchpid.c:109 src/pv/watchpid.c:120 src/pv/watchpid.c:128
#: src/pv/watchpid.c:165 src/pv/watchpid.c:184 src/pv/watchpid.c:192
#: src/pv/watchpid.c:202 src/pv/watchpid.c:293 src/pv/watchpid.c:301
@@ -473,15 +473,15 @@ msgstr "aucun tampon de transfert alloué"
msgid "failed to set interval timer"
msgstr ""
#: src/pv/transfer.c:694
#: src/pv/transfer.c:697
msgid "failed to clear interval timer"
msgstr ""
#: src/pv/transfer.c:818
#: src/pv/transfer.c:822
msgid "write failed"
msgstr "l'écriture a échoué"
#: src/pv/transfer.c:1051
#: src/pv/transfer.c:1055
msgid "select call failed"
msgstr "l'appel de sélection a échoué"
+6 -6
View File
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: pv@ivarch.com\n"
"POT-Creation-Date: 2024-04-21 14:15+0100\n"
"POT-Creation-Date: 2024-06-08 12:44+0200\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"
@@ -357,7 +357,7 @@ msgstr ""
msgid "yzafpnum KMGTPEZY"
msgstr ""
#: src/pv/display.c:750 src/pv/transfer.c:954
#: src/pv/display.c:750 src/pv/transfer.c:958
msgid "buffer allocation failed"
msgstr "nie udało się zaalokować bufora"
@@ -414,7 +414,7 @@ msgstr ""
msgid "(stdin)"
msgstr ""
#: src/pv/loop.c:570 src/pv/loop.c:621 src/pv/loop.c:662 src/pv/watchpid.c:99
#: src/pv/loop.c:575 src/pv/loop.c:626 src/pv/loop.c:667 src/pv/watchpid.c:99
#: src/pv/watchpid.c:109 src/pv/watchpid.c:120 src/pv/watchpid.c:128
#: src/pv/watchpid.c:165 src/pv/watchpid.c:184 src/pv/watchpid.c:192
#: src/pv/watchpid.c:202 src/pv/watchpid.c:293 src/pv/watchpid.c:301
@@ -459,15 +459,15 @@ msgid "failed to set interval timer"
msgstr ""
# "Przewidywany czas ukończenia" for ETA is too long
#: src/pv/transfer.c:694
#: src/pv/transfer.c:697
msgid "failed to clear interval timer"
msgstr ""
#: src/pv/transfer.c:818
#: src/pv/transfer.c:822
msgid "write failed"
msgstr "błąd zapisu"
#: src/pv/transfer.c:1051
#: src/pv/transfer.c:1055
msgid "select call failed"
msgstr "nie udało się wywołać funkcji select"
+6 -6
View File
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: pv@ivarch.com\n"
"POT-Creation-Date: 2024-04-21 14:15+0100\n"
"POT-Creation-Date: 2024-06-08 12:44+0200\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"
@@ -360,7 +360,7 @@ msgstr ""
msgid "yzafpnum KMGTPEZY"
msgstr ""
#: src/pv/display.c:750 src/pv/transfer.c:954
#: src/pv/display.c:750 src/pv/transfer.c:958
msgid "buffer allocation failed"
msgstr "erro alocando o buffer"
@@ -416,7 +416,7 @@ msgstr ""
msgid "(stdin)"
msgstr ""
#: src/pv/loop.c:570 src/pv/loop.c:621 src/pv/loop.c:662 src/pv/watchpid.c:99
#: src/pv/loop.c:575 src/pv/loop.c:626 src/pv/loop.c:667 src/pv/watchpid.c:99
#: src/pv/watchpid.c:109 src/pv/watchpid.c:120 src/pv/watchpid.c:128
#: src/pv/watchpid.c:165 src/pv/watchpid.c:184 src/pv/watchpid.c:192
#: src/pv/watchpid.c:202 src/pv/watchpid.c:293 src/pv/watchpid.c:301
@@ -460,15 +460,15 @@ msgstr ""
msgid "failed to set interval timer"
msgstr ""
#: src/pv/transfer.c:694
#: src/pv/transfer.c:697
msgid "failed to clear interval timer"
msgstr ""
#: src/pv/transfer.c:818
#: src/pv/transfer.c:822
msgid "write failed"
msgstr "erro de gravação"
#: src/pv/transfer.c:1051
#: src/pv/transfer.c:1055
msgid "select call failed"
msgstr "erro na chamada da função select"
+2 -1
View File
@@ -54,11 +54,12 @@ struct opts_s { /* structure describing run-time options */
bool stop_at_size; /* set if we stop at "size" bytes */
bool sync_after_write; /* set if we sync after every write */
bool direct_io; /* set if O_DIRECT is to be used */
bool discard_input; /* set to write nothing to stdout */
bool discard_input; /* set to write nothing to output */
double interval; /* interval between updates */
double delay_start; /* delay before first display */
pid_t watch_pid; /* process to watch fds of */
int watch_fd; /* fd to watch */
/*@keep@*/ /*@null@*/ char *output; /* fd to write output to */
unsigned int average_rate_window; /* time window in seconds for average rate calculations */
unsigned int width; /* screen width */
unsigned int height; /* screen height */
+2
View File
@@ -123,6 +123,8 @@ struct pvstate_s {
double delay_start; /* delay before first display */
pid_t watch_pid; /* process to watch fds of */
int watch_fd; /* fd to watch */
int output_fd; /* fd to write output to */
/*@null@*/ char *output_name; /* name of the output, for diagnostics */
unsigned int average_rate_window; /* time window in seconds for average rate calculations */
unsigned int width; /* screen width */
unsigned int height; /* screen height */
+1
View File
@@ -186,6 +186,7 @@ extern void pv_state_name_set(pvstate_t, /*@null@*/ const char *);
extern void pv_state_format_string_set(pvstate_t, /*@null@*/ const char *);
extern void pv_state_watch_pid_set(pvstate_t, pid_t);
extern void pv_state_watch_fd_set(pvstate_t, int);
extern void pv_state_output_set(pvstate_t, int, const char *);
extern void pv_state_average_rate_window_set(pvstate_t, unsigned int);
extern void pv_state_inputfiles(pvstate_t, unsigned int, const char **);
+14
View File
@@ -300,6 +300,20 @@ int main(int argc, char **argv)
opts->fineta, opts->rate, opts->average_rate,
opts->bytes, opts->bufpercent, opts->lastwritten, opts->name);
/*
* Set output file, treating no output or "-" as stdout
*/
if (NULL == opts->output || 0 == strcmp(opts->output, "-")) {
pv_state_output_set(state, STDOUT_FILENO, "(stdout)");
} else {
int fd = open(opts->output, O_WRONLY | O_CREAT);
if (fd < 0) {
fprintf(stderr, "%s: %s: %s\n", opts->program_name, opts->output, strerror(errno));
return 1;
}
pv_state_output_set(state, fd, opts->output);
}
#ifdef MAKE_STDOUT_NONBLOCKING
/*
* Try and make standard output use non-blocking I/O.
+12 -1
View File
@@ -44,6 +44,8 @@ void opts_free( /*@only@ */ opts_t opts)
free(opts->format);
if (NULL != opts->pidfile)
free(opts->pidfile);
if (NULL != opts->output)
free(opts->output);
if (NULL != opts->argv)
free(opts->argv);
/*@+keeptrans@ */
@@ -143,6 +145,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
{ "remote", 1, NULL, (int) 'R' },
{ "pidfile", 1, NULL, (int) 'P' },
{ "watchfd", 1, NULL, (int) 'd' },
{ "output", 1, NULL, (int) 'o' },
{ "average-rate-window", 1, NULL, (int) 'm' },
#ifdef ENABLE_DEBUGGING
{ "debug", 1, NULL, (int) '!' },
@@ -152,7 +155,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
/*@+nullassign@ */
int option_index = 0;
#endif /* HAVE_GETOPT_LONG */
char *short_options = "hVpteIrab8kTA:fnqcWD:s:l0i:w:H:N:F:L:B:CEZ:SYKXR:P:d:m:"
char *short_options = "hVpteIrab8kTA:fnqcWD:s:l0i:w:H:N:F:L:B:CEZ:SYKXR:P:d:m:o:"
#ifdef ENABLE_DEBUGGING
"!:"
#endif
@@ -476,6 +479,14 @@ opts_t opts_parse(unsigned int argc, char **argv)
opts->watch_pid = (pid_t) parse_pid;
opts->watch_fd = parse_fd;
break;
case 'o':
opts->output = pv_strdup(optarg);
if (NULL == opts->output) {
fprintf(stderr, "%s: -o: %s\n", opts->program_name, strerror(errno));
opts_free(opts);
return NULL;
}
break;
case 'm':
opts->average_rate_window = pv_getnum_count(optarg, opts->decimal_units);
break;
+16 -16
View File
@@ -127,24 +127,24 @@ static off_t pv_calc_total_bytes(pvstate_t state)
* 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
* Further modified to check that output is not in append-only mode
* and that we can seek back to the start after getting the size.
*/
if (total < 1) {
int rc;
rc = fstat(STDOUT_FILENO, &sb);
rc = fstat(state->control.output_fd, &sb);
if ((0 == rc) && S_ISBLK(sb.st_mode)
&& (0 == (fcntl(STDOUT_FILENO, F_GETFL) & O_APPEND))) {
&& (0 == (fcntl(state->control.output_fd, F_GETFL) & O_APPEND))) {
off_t end_position;
end_position = lseek(STDOUT_FILENO, 0, SEEK_END);
end_position = lseek(state->control.output_fd, 0, SEEK_END);
total = 0;
if (end_position > 0) {
total = end_position;
}
if (lseek(STDOUT_FILENO, 0, SEEK_SET) != 0) {
pv_error(state, "%s: %s: %s", "(stdout)",
if (lseek(state->control.output_fd, 0, SEEK_SET) != 0) {
pv_error(state, "%s: %s: %s", state->control.output_name,
_("failed to seek to start of output"), strerror(errno));
state->status.exit_status |= 2;
}
@@ -277,7 +277,7 @@ off_t pv_calc_total_size(pvstate_t state)
* 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.
* the output is pointing to.
*
* Updates state->status.current_input_file in the process.
*/
@@ -286,7 +286,7 @@ int pv_next_file(pvstate_t state, unsigned int filenum, int oldfd)
struct stat isb;
struct stat osb;
int fd;
bool input_file_is_stdout;
bool input_file_is_output;
if (oldfd >= 0) {
if (0 != close(oldfd)) {
@@ -327,7 +327,7 @@ int pv_next_file(pvstate_t state, unsigned int filenum, int oldfd)
return -1;
}
if (0 != fstat(STDOUT_FILENO, &osb)) {
if (0 != fstat(state->control.output_fd, &osb)) {
pv_error(state, "%s: %s", _("failed to stat output file"), strerror(errno));
(void) close(fd);
state->status.exit_status |= 2;
@@ -335,21 +335,21 @@ int pv_next_file(pvstate_t state, unsigned int filenum, int oldfd)
}
/*
* Check that this new input file is not the same as stdout's
* Check that this new input file is not the same as output's
* destination. This restriction is ignored for anything other
* than a regular file or block device.
*/
input_file_is_stdout = true;
input_file_is_output = true;
if (isb.st_dev != osb.st_dev)
input_file_is_stdout = false;
input_file_is_output = false;
if (isb.st_ino != osb.st_ino)
input_file_is_stdout = false;
input_file_is_output = false;
if (0 != isatty(fd))
input_file_is_stdout = false;
input_file_is_output = false;
if ((!S_ISREG(isb.st_mode)) && (!S_ISBLK(isb.st_mode)))
input_file_is_stdout = false;
input_file_is_output = false;
if (input_file_is_stdout) {
if (input_file_is_output) {
pv_error(state, "%s: %s", _("input file is output file"),
NULL == state->files.filename ? "-" : state->files.filename[filenum]);
(void) close(fd);
+2 -2
View File
@@ -289,8 +289,8 @@ void pv_sig_init(pvstate_t state)
*/
/*
* Ignore SIGPIPE, so we don't die if stdout is a pipe and the other
* end closes unexpectedly.
* Ignore SIGPIPE, so we don't die if the output is a pipe and the
* other end closes unexpectedly.
*/
sa.sa_handler = SIG_IGN;
(void) sigemptyset(&(sa.sa_mask));
+28
View File
@@ -67,6 +67,7 @@ pvstate_t pv_state_alloc(const char *program_name)
state->control.watch_pid = 0;
state->control.watch_fd = -1;
state->control.output_fd = -1;
#ifdef HAVE_IPC
state->cursor.shmid = -1;
state->cursor.pvcount = 1;
@@ -124,6 +125,23 @@ void pv_state_free(pvstate_t state)
state->control.format_string = NULL;
}
if (NULL != state->control.output_name) {
free(state->control.output_name);
state->control.output_name = NULL;
}
if (NULL != state->control.output_name) {
free(state->control.output_name);
state->control.output_name = NULL;
}
if (state->control.output_fd >= 0) {
if (STDOUT_FILENO != state->control.output_fd) {
close(state->control.output_fd);
}
state->control.output_fd = -1;
}
/*@-keeptrans@ */
if (NULL != state->transfer.transfer_buffer)
free(state->transfer.transfer_buffer);
@@ -347,6 +365,16 @@ void pv_state_watch_fd_set(pvstate_t state, int val)
state->control.watch_fd = val;
}
void pv_state_output_set(pvstate_t state, int fd, const char *name)
{
if (NULL != state->control.output_name)
free(state->control.output_name);
if (state->control.output_fd >= 0 && state->control.output_fd != STDOUT_FILENO)
close(state->control.output_fd);
state->control.output_fd = fd;
state->control.output_name = strdup(name);
}
void pv_state_average_rate_window_set(pvstate_t state, unsigned int val)
{
if (val < 1)
+21 -19
View File
@@ -345,7 +345,7 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou
/*@-nullpass@ */
/*@-type@ */
/* splint doesn't know about splice */
nread = splice(fd, NULL, STDOUT_FILENO, NULL, bytes_to_splice, SPLICE_F_MORE);
nread = splice(fd, NULL, state->control.output_fd, NULL, bytes_to_splice, SPLICE_F_MORE);
/*@+type@ */
/*@+nullpass@ */
@@ -370,7 +370,7 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou
* error, we cannot skip it, so set
* "do_not_skip_errors".
*/
if ((fdatasync(STDOUT_FILENO) < 0)
if ((fdatasync(state->control.output_fd) < 0)
&& (EIO == errno)) {
nread = -1;
do_not_skip_errors = true;
@@ -612,13 +612,13 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou
/*
* Write state->transfer.to_write bytes of data from the transfer buffer to stdout.
* Write state->transfer.to_write bytes of data from the transfer buffer to the output.
* Returns zero if there was a transient error and we need to return 0 from
* pv_transfer, otherwise returns 1.
*
* Updates state->transfer.write_position by moving it on by the number of bytes
* written; adds the number of bytes written to state->transfer.written; sets
* *eof_out to true, on stdout EOF, or when the write position catches up
* *eof_out to true, on output EOF, or when the write position catches up
* with the read position AND *eof_in is true (meaning we've reached the end
* of data).
*
@@ -681,7 +681,7 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long
debug("%s", "setting alarm");
#endif /* HAVE_SETITIMER */
debug("%s: %ld %s", "beginning write attempt", (long) (state->transfer.to_write), "bytes");
nwritten = pv__transfer_write_repeated(STDOUT_FILENO,
nwritten = pv__transfer_write_repeated(state->control.output_fd,
state->transfer.transfer_buffer +
state->transfer.write_position,
(size_t) (state->transfer.to_write),
@@ -706,7 +706,7 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long
if (0 == nwritten) {
/*
* Write returned 0 - EOF on stdout.
* Write returned 0 - EOF on output.
*/
*eof_out = true;
return 1;
@@ -842,7 +842,7 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long
*/
/*@null@*/
/*@only@*/
static char *pv__allocate_aligned_buffer(int fd, size_t target_size)
static char *pv__allocate_aligned_buffer(int outfd, int infd, size_t target_size)
{
char *newptr;
@@ -850,8 +850,8 @@ static char *pv__allocate_aligned_buffer(int fd, size_t target_size)
long input_alignment, output_alignment, min_alignment;
long required_alignment;
input_alignment = fd >= 0 ? fpathconf(fd, _PC_REC_XFER_ALIGN) : -1;
output_alignment = fpathconf(STDOUT_FILENO, _PC_REC_XFER_ALIGN);
input_alignment = infd >= 0 ? fpathconf(infd, _PC_REC_XFER_ALIGN) : -1;
output_alignment = fpathconf(outfd, _PC_REC_XFER_ALIGN);
#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
min_alignment = sysconf(_SC_PAGESIZE);
#else /* ! defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) */
@@ -928,9 +928,10 @@ ssize_t pv_transfer(pvstate_t state, int fd, bool *eof_in, bool *eof_out, off_t
}
}
if (!(*eof_out)) {
if (0 != fcntl(STDOUT_FILENO, F_SETFL,
(state->control.direct_io ? O_DIRECT : 0) | fcntl(STDOUT_FILENO, F_GETFL))) {
debug("%s: %s: %s", "(stdout)", "fcntl", strerror(errno));
if (0 != fcntl(state->control.output_fd, F_SETFL,
(state->control.direct_io ? O_DIRECT : 0) |
fcntl(state->control.output_fd, F_GETFL))) {
debug("%s: %s: %s", state->control.output_name, "fcntl", strerror(errno));
}
}
state->control.direct_io_changed = false;
@@ -953,7 +954,7 @@ ssize_t pv_transfer(pvstate_t state, int fd, bool *eof_in, bool *eof_out, off_t
*/
if (NULL == state->transfer.transfer_buffer) {
state->transfer.transfer_buffer =
pv__allocate_aligned_buffer(fd, state->control.target_buffer_size + 32);
pv__allocate_aligned_buffer(state->control.output_fd, fd, state->control.target_buffer_size + 32);
if (NULL == state->transfer.transfer_buffer) {
pv_error(state, "%s: %s", _("buffer allocation failed"), strerror(errno));
state->status.exit_status |= 64;
@@ -971,7 +972,8 @@ ssize_t pv_transfer(pvstate_t state, int fd, bool *eof_in, bool *eof_out, off_t
*/
if (state->transfer.buffer_size < state->control.target_buffer_size) {
char *newptr;
newptr = pv__allocate_aligned_buffer(fd, state->control.target_buffer_size + 32);
newptr =
pv__allocate_aligned_buffer(state->control.output_fd, fd, state->control.target_buffer_size + 32);
if (NULL == newptr) {
/*
* Reset target if realloc failed so we don't keep
@@ -1031,10 +1033,10 @@ ssize_t pv_transfer(pvstate_t state, int fd, bool *eof_in, bool *eof_out, off_t
/*
* If we don't think we've finished writing and there's anything
* we're allowed to write, look for the stdout becoming writable.
* we're allowed to write, look for the output becoming writable.
*/
if ((!(*eof_out)) && (state->transfer.to_write > 0)) {
check_write_fd = STDOUT_FILENO;
check_write_fd = state->control.output_fd;
}
ready_to_read = false;
@@ -1092,9 +1094,9 @@ ssize_t pv_transfer(pvstate_t state, int fd, bool *eof_in, bool *eof_out, off_t
}
/*
* 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 there is data to write, and the output 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 (ready_to_write
#ifdef HAVE_SPLICE