66 Commits
Author SHA1 Message Date
Andrew Wood b089303505 Version update. 2024-04-21 14:20:26 +01:00
Andrew Wood 8992e98c8d Line number adjustments from "make update-po". 2024-04-21 14:18:48 +01:00
Andrew Wood 205245f269 Issue #6 had only one outstanding action which was a duplicate of #34, so it is now removed. 2024-04-21 14:00:31 +01:00
Andrew Wood b375041790 Correct issues #34, #86, #87 - progress output stopping when writing output data blocks - by adding a SIGALRM handler, since ignoring the signal prevents it from interrupting writes; and use setitimer() where available, instead of alarm(), to provide sufficient granularity to handle the case where the --interval is not a whole number of seconds. 2024-04-21 13:54:01 +01:00
Andrew Wood 3a41665302 Fix incorrect "%lf" string format. 2024-04-21 12:02:35 +01:00
Andrew Wood f6686b8188 Minor formatting changes by "indent". 2024-04-21 12:01:52 +01:00
Andrew Wood 31b43cf3c7 Formatting correction. 2024-04-21 11:32:26 +01:00
Andrew Wood e483b19c14 Formatting correction. 2024-04-21 11:31:54 +01:00
Andrew Wood 50b400a1f8 Formatting correction. 2024-04-21 11:31:26 +01:00
Andrew Wood d90dc35dd3 Formatting correction. 2024-04-21 11:30:46 +01:00
Andrew Wood b45970b3b6 Added the ENVIRONMENT section. 2024-04-21 11:27:51 +01:00
Andrew Wood a5700cc914 Reinstate the use of $TMPDIR and $TMP in cursor positioning rather than hard-coding "/tmp" (#88). 2024-04-20 21:46:23 +01:00
Andrew Wood d3c59cbff3 Minor comment formatting correction. 2024-04-20 21:31:17 +01:00
Andrew Wood 8bc1cc28d0 Check for zero-length $HOME and document the reason flawfinder's warning is being ignored. 2024-04-20 21:01:04 +01:00
Andrew Wood 1ad9c321bc Adjust copyright years. 2024-03-19 22:19:42 +00:00
Andrew Wood c9dede08d4 Adjust the decimal units flag variable name from "si" to "decimal_units" so it does not overlap with other words like "size" and "signal"; add man page entry and adjust the "-L" man page entry; and allow this flag to also change how input prefixes are processed. 2024-03-19 22:17:43 +00:00
Andrew Wood 3b721112e6 Acknowledge pull request #85. 2024-03-19 21:42:08 +00:00
Andrew Wood 0093fb8276 Merge pull request 'Support for decimal SI units' (#85) from kevinruddy/pv:si-patch into main
Reviewed-on: https://codeberg.org/a-j-wood/pv/pulls/85
2024-03-19 21:30:01 +00:00
kevinruddy fe8137521a Support for decimal SI units 2024-03-19 16:57:57 -04:00
Andrew Wood 2bc7a1bb10 Added #83. 2024-03-13 21:56:34 +00:00
Andrew Wood 120ba0ba9a Minor typo correction. 2024-03-13 21:48:57 +00:00
Andrew Wood 88d93737aa Merge pull request 'Update to po/fr.po' (#83) from tbertels/pv:tbertels-patch-1 into main
Reviewed-on: https://codeberg.org/a-j-wood/pv/pulls/83
2024-03-13 21:44:46 +00:00
tbertels 317d5a3aba Update to po/fr.po
Missing strings and corrections
2024-03-12 07:31:42 +00:00
Andrew Wood ddc5cb0224 Added TODO #81. 2024-01-08 23:42:31 +00:00
Andrew Wood 4b4ec17ff9 Timestamp updates. 2023-11-19 16:39:51 +00:00
Andrew Wood cc9534fc7b Cast of values for pv_snprintf() is required on Cygwin. 2023-11-19 16:39:32 +00:00
Andrew Wood c8762821fb Version update. 2023-11-19 14:16:56 +00:00
Andrew Wood 272dacdd3a Update to reflect closure of #77. 2023-10-22 17:48:45 +01:00
Andrew Wood 78548460b7 Stamp and string position updates. 2023-10-22 17:44:45 +01:00
Andrew Wood 3eee7a1409 Addressed more issues raised by flawfinder. 2023-10-22 17:43:40 +01:00
Andrew Wood e446782c1f Addressed more issues raised by splint, and refactored out state_array so that state is part of info_array and each state is allocated and deallocated properly instead of referencing pointers multiple times. 2023-10-22 16:23:53 +01:00
Andrew Wood d8664ca4c1 Stamp and string position updates. 2023-10-22 00:09:06 +01:00
Andrew Wood d06374a0f1 Addressed more issues raised by splint and flawfinder. 2023-10-22 00:08:35 +01:00
Andrew Wood 9a9c117411 Addressed more issues raised by flawfinder. 2023-10-18 23:10:29 +01:00
Andrew Wood 2255dcc2bf Stamp and string position updates. 2023-10-18 23:10:14 +01:00
Andrew Wood be4402c1ff Addressed more issues raised by flawfinder. 2023-10-17 22:09:04 +01:00
Andrew Wood 4d46dbe545 Comment wording correction. 2023-10-17 21:02:21 +01:00
Andrew Wood 3fdf8d5a69 Improved comments. 2023-10-16 22:47:02 +01:00
Andrew Wood 7c0aea0b1e Stamp and string position updates. 2023-10-15 23:53:24 +01:00
Andrew Wood b4d6c5ed2a Addressed more issues raised by splint, and improved variable names and comments. 2023-10-15 23:42:04 +01:00
Andrew Wood d8e0674da0 Move locally scoped variables from "switch" statement into outer "for" loop, to avoid triggering a crash in the "splint" code analyser. 2023-10-15 22:29:37 +01:00
Andrew Wood b0df2e53a8 Replace str_* in state structure with an array of components of enumerated types, to avoid pointers everywhere in the format array and to allow for later expansion which will separate "string length" from "displayed width" to allow for wide character support. 2023-10-14 17:18:20 +01:00
Andrew Wood 94ffdaf60d Group state structure into sub-structures to allow future changes to watchpid code that will do a better job of duplicating the relevant parts of the internal state for each fd. 2023-10-14 01:23:54 +01:00
Andrew Wood 376aa5a8fa Stamp and string position updates. 2023-10-14 00:08:37 +01:00
Andrew Wood 7fcc7af07d Addressed more issues raised by flawfinder. 2023-10-14 00:04:56 +01:00
Andrew Wood 03d42555e7 Addressed more issues raised by splint. 2023-10-14 00:04:24 +01:00
Andrew Wood b8624035e0 Addressed more issues raised by splint. 2023-10-13 23:36:15 +01:00
Andrew Wood 41b223a757 Addressed more issues raised by splint. 2023-10-13 23:31:28 +01:00
Andrew Wood 03fb1dc673 Correct percentage formatting so it is always 3 characters wide. 2023-10-11 22:22:53 +01:00
Andrew Wood e864eca78d Only check for the IPC functions we are still using. 2023-10-01 15:16:31 +01:00
Andrew Wood ddafc2e897 Only check for the IPC functions we are still using. 2023-10-01 15:15:07 +01:00
Andrew Wood bf3f76e02e Position and string updates. 2023-10-01 15:10:48 +01:00
Andrew Wood 4398cd8677 It is now October. 2023-10-01 12:52:24 +01:00
Andrew Wood dbf55eff3c For message passing for "--remote", fall back to $HOME/.pv instead of /tmp if /run/user/<uid> is unavailable. 2023-10-01 12:41:27 +01:00
Andrew Wood 07f894f66c Minor formatting change from "indent". 2023-10-01 12:40:22 +01:00
Andrew Wood a9737ca05e Correct the test skip reason message, if "--remote" is not supported. 2023-10-01 01:50:52 +01:00
Andrew Wood 0e1750835e Rewrite of "--remote", to use a temporary file for information exchange instead of a SysV IPC message queue, to correct the fault where "make check -j32" would fail due to every PV exit removing the message queue. 2023-10-01 01:20:16 +01:00
Andrew Wood 965cc52815 Stamp and string position updates. 2023-09-25 22:24:21 +01:00
Andrew Wood 08ee154c09 Add more debugging to message receipt check, in sender. 2023-09-25 21:51:45 +01:00
Andrew Wood 37cacf3da4 Added another contributor. 2023-09-25 21:44:20 +01:00
Andrew Wood 55f7f94a6f Additional fix for compilation with "--disable-ipc". 2023-09-25 21:36:02 +01:00
Andrew Wood 1e91713bb6 Add debugging to message receipt check, in sender. 2023-09-25 21:32:14 +01:00
Andrew Wood 27f52cac6b Version update. 2023-09-25 21:20:24 +01:00
Andrew Wood b0a144fd54 Move variable declarations into the blocks in which they are used, to avoid compilation warnings on MacOS, as per patch from Benoit Pierre. 2023-09-25 21:14:30 +01:00
Andrew Wood bfa4a23751 Move struct stat in pv_main_loop inside the #if HAVE_STRUCT_STAT_ST_BLKSIZE block to avoid compilation warnings, as per patch from Benoit Pierre. 2023-09-25 21:07:24 +01:00
Andrew Wood 7c6568865c Move smb_bug in pv_crs_fini inside the #if HAVE_IPC block so the code can compile on non IPC systems, as per patch from Benoit Pierre. 2023-09-25 21:03:19 +01:00
37 changed files with 2838 additions and 1997 deletions
+1 -1
View File
@@ -33,7 +33,7 @@ development tools. You can then run "`autoreconf -is`" to generate the
# Copyright, bug reporting, and acknowledgements
Copyright (C) 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood.
Copyright (C) 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood.
License GPLv3+: GNU GPL version 3 or later <https://www.gnu.org/licenses/gpl-3.0.html>.
+5 -5
View File
@@ -1,10 +1,10 @@
dnl Process this file with autoconf to produce a configure script.
dnl
dnl Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
dnl Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
dnl
dnl License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
AC_INIT([pv], [1.8.0], [pv@ivarch.com], [pv], [https://www.ivarch.com/programs/pv.shtml])
AC_INIT([pv], [1.8.9], [pv@ivarch.com], [pv], [https://www.ivarch.com/programs/pv.shtml])
AC_CONFIG_SRCDIR([src/include/config.h.in])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_HEADERS([src/include/config.h])
@@ -25,10 +25,11 @@ fi
dnl Items we can use if present, but can do without if not present.
AC_CHECK_FUNCS([getopt_long])
AC_CHECK_FUNCS([vsnprintf strlcat strtoul])
AC_CHECK_FUNCS([vsnprintf strlcat strtoul memrchr])
AC_CHECK_FUNCS([fdatasync])
AC_CHECK_FUNCS([fpathconf sysconf posix_memalign posix_fadvise])
AC_CHECK_FUNCS([nanosleep])
AC_CHECK_FUNCS([setitimer])
AC_CHECK_HEADERS([getopt.h])
AC_CHECK_HEADERS([limits.h])
AC_CHECK_HEADERS([wctype.h])
@@ -103,10 +104,9 @@ yes
if test "$IPC_SUPPORT" = "yes"; then
AC_CHECK_HEADERS([sys/ipc.h], [], [IPC_SUPPORT=no])
AC_CHECK_HEADERS([sys/msg.h], [], [IPC_SUPPORT=no])
AC_CHECK_HEADERS([sys/param.h])
if test "$IPC_SUPPORT" = "yes"; then
AC_CHECK_FUNCS([msgget], [], [IPC_SUPPORT=no])
AC_CHECK_FUNCS([shmget], [], [IPC_SUPPORT=no])
fi
fi
+4
View File
@@ -87,5 +87,9 @@ is acknowledged and greatly appreciated:
* [Wilhelm von Thiele](https://github.com/TurtleWilly) - assisted with OS X cleanups ([#73](https://codeberg.org/a-j-wood/pv/issues/73), [#74](https://codeberg.org/a-j-wood/pv/issues/74))
* Matějů Miroslav, Ing. - suggested fix for ETA and elapsed time faults when suspending and resuming a machine ([#13](https://codeberg.org/a-j-wood/pv/issues/13))
* Anthony DeRobertis - suggested the "`--error-skip-block`" option ([#37](https://codeberg.org/a-j-wood/pv/issues/37))
* Benoit Pierre - provided patches for compilation without IPC support, without HAVE_STRUCT_STAT_ST_BLKSIZE, and on MacOS
* [gustav-b](https://codeberg.org/gustav-b) - suggested percentage formatting correction ([#80](https://codeberg.org/a-j-wood/pv/issues/80))
* [Thomas Bertels](https://codeberg.org/tbertels) - updated French translations ([#83](https://codeberg.org/a-j-wood/pv/pulls/83))
* [kevinruddy](https://codeberg.org/kevinruddy) - added decimal units option ([#85](https://codeberg.org/a-j-wood/pv/pulls/85))
---
+16 -1
View File
@@ -1,3 +1,18 @@
### 1.8.9 - 21 April 2024
* feature: new "`--si`" option to display and interpret size suffixes in multiples of 1000 rather than 1024 (pull request [#85](https://codeberg.org/a-j-wood/pv/pulls/85)) supplied by [kevinruddy](https://codeberg.org/kevinruddy)
* fix: continue producing progress output when the output is blocking writes ([#34](https://codeberg.org/a-j-wood/pv/issues/34), [#86](https://codeberg.org/a-j-wood/pv/issues/86), [#87](https://codeberg.org/a-j-wood/pv/issues/87))
* fix: honour the _TMPDIR_ / _TMP_ environment variables again, rather than hard-coding "`/tmp`", when using a terminal lock file (originally removed in 1.8.0) ([#88](https://codeberg.org/a-j-wood/pv/issues/88))
* i18n: corrections and missing strings added to French translations (pull request [#83](https://codeberg.org/a-j-wood/pv/pulls/83)) supplied by [Thomas Bertels](https://codeberg.org/tbertels)
### 1.8.5 - 19 November 2023
* fix: corrected percentage formatting so it doesn't jump from 2 to 3 characters wide at 100% ([#80](https://codeberg.org/a-j-wood/pv/issues/80))
* fix: replaced "`--remote`" mechanism, using a temporary file instead of SysV IPC, so it can work reliably even when there are multiple PV instances
* fix: corrected compilation failure when without IPC support
* security: addressed all issues highlighted by the software auditing tools "`splint`" and "`flawfinder`" (see "`make analyse`") ([#77](https://codeberg.org/a-j-wood/pv/issues/77))
* cleanup: compilation warnings fixed on non-IPC and MacOS systems
### 1.8.0 - 24 September 2023
#### Features
@@ -53,7 +68,7 @@
* cleanup: word wrapping of "`--help`" output is now multi-byte locale aware
* cleanup: adjusted "`indent`" rules to line length of 120 and reformatted code
### 1.7.18 - 28 July 2023
### 1.7.18 - 28 July 2023
* fix: language file installation had been broken by the configuration script rewrite
+1 -3
View File
@@ -2,7 +2,6 @@ Things still to do. (#n) indicates the issue tracker number.
* ([#3](https://codeberg.org/a-j-wood/pv/issues/3)) Option ("`-x`"?) to use xterm title line for status (Joachim Haga)
* ([#4](https://codeberg.org/a-j-wood/pv/issues/4)) Option for process title (Martin Sarsale) as "`pv - name:FooProcess -xyz - transferred: 1.3GB - 500KB/s - running: 10:15:30s`"
* ([#6](https://codeberg.org/a-j-wood/pv/issues/6)) Look at effect of *O_SYNC* or `fsync` on performance; update counters during buffer flush
* ([#9](https://codeberg.org/a-j-wood/pv/issues/9)) Option to switch rate to per minute if really slow
* ([#10](https://codeberg.org/a-j-wood/pv/issues/10)) Add watchfd tests
* ([#11](https://codeberg.org/a-j-wood/pv/issues/11)) Option "`--progress-from FILE`", read last number and use it as bytes read (Jacek Wielemborek)
@@ -11,7 +10,6 @@ Things still to do. (#n) indicates the issue tracker number.
* ([#17](https://codeberg.org/a-j-wood/pv/issues/17)) Allow "`-r`" with "`-l`" and "`-n`" to output lines/sec (Roland Kletzing)
* ([#22](https://codeberg.org/a-j-wood/pv/issues/22)) Options to skip input and seek on output (Jason A. Pfeil, Feb 2022)
* ([#25](https://codeberg.org/a-j-wood/pv/issues/25)) Normalise progress to 100% on overrun (Andrej Gantvorg)
* ([#34](https://codeberg.org/a-j-wood/pv/issues/34)) Continue timer even if input or output is blocking (Martin Probst - Jun 2017)
* ([#35](https://codeberg.org/a-j-wood/pv/issues/35)) Allow decimal values for "`-s`", "`-L`", "`-B`" (Thomas Watson - Aug 2020)
* ([#38](https://codeberg.org/a-j-wood/pv/issues/38)) Reset ETA on *SIGUSR1* (Jacek Wielemborek - Jan 2019)
* ([#40](https://codeberg.org/a-j-wood/pv/issues/40)) Permit "`-c`" with "`-d PID:FD`", reject "`-N`" with "`-d PID`" (Norman Rasmussen - Nov 2020)
@@ -26,6 +24,6 @@ Things still to do. (#n) indicates the issue tracker number.
* ([#56](https://codeberg.org/a-j-wood/pv/issues/56)) Support for backgrounding pv, and allowing it to be monitored separately ([jimbobmcgee](https://github.com/jimbobmcgee))
* ([#67](https://codeberg.org/a-j-wood/pv/issues/67)) Wrap another process to monitor its stdin & stdout ([Alex Mason](https://github.com/axman6))
* ([#76](https://codeberg.org/a-j-wood/pv/issues/76)) Provide a way to test translations without installing
* ([#77](https://codeberg.org/a-j-wood/pv/issues/77)) Eliminate all warnings from "splint" and "flawfinder"
* ([#81](https://codeberg.org/a-j-wood/pv/issues/81)) Keep last output on terminal when "pv -d" is done
Any assistance would be appreciated.
+51 -10
View File
@@ -1,4 +1,4 @@
.TH PV 1 "September 2023" Linux "User Manuals"
.TH PV 1 "April 2024" Linux "User Manuals"
.SH NAME
pv \- monitor the progress of data through a pipe
.SH SYNOPSIS
@@ -154,6 +154,11 @@ data transferred so far.
Display the total bits instead of the total bytes. The output suffix will
be "b" instead of "B".
.TP
.B \-k, \-\-si
Display and interpret suffixes as multiples of 1000 rather than the default
of 1024. Note that this only takes effect on options after this one, so for
consistency, specify this option first.
.TP
.B \-T, \-\-buffer\-percent
Turn on the transfer buffer percentage display. This will show the
percentage of the transfer buffer in use - but see the caveat under
@@ -343,7 +348,11 @@ invocations in a single, long, pipeline.
Limit the transfer to a maximum of
.I RATE
bytes per second. A suffix of "K", "M", "G", or "T" can be added to denote
kibibytes (*1024), mebibytes, and so on.
kibibytes (*1024), mebibytes, and so on. If
.B \-\-si
was also passed, suffixes will denote kilobytes (*1000), megabytes, etc.
Note the caveat about the positioning of
.B \-\-si .
.TP
.BI \-B\ BYTES \fR,\ \fB\-\-buffer-size\ BYTES
Use a transfer buffer size of
@@ -654,6 +663,40 @@ Memory allocation failed.
A zero exit status indicates no problems.
.SH ENVIRONMENT
The following environment variables may affect
.BR pv :
.TP
.B HOME
The current user's home directory. This may be used by the remote control
mechanism
(the
.B \-\-remote
option)
to exchange messages between
.B pv
instances: if the
.I /run/user/UID/
directory does not exist (where
.I UID
is the current user ID), then
.I $HOME/.pv/
will be used instead.
.TP
.BR TMPDIR ", " TMP
The directory to create per-tty lock files for the terminal when using the
.B \-\-cursor
option. If
.B TMPDIR
is set to a non-empty value, it is the directory under which lock files are
created. Otherwise, if
.B TMP
is set, then it is used; and if neither are set, then
.I /tmp
is used.
.SH AUTHOR
Written by Andrew Wood, with patches submitted by various other people.
Please see the package's ACKNOWLEDGEMENTS file for a complete list of
@@ -689,13 +732,11 @@ before using
.IP \(bu 3
The
.B -R
option is not available on Cygwin without
.B cygserver
running (SYSV IPC is needed). To fix this, start
.B cygserver
before running the instance of
.B pv
you want, at runtime, to change the parameters of.
option requires that either
.B /run/user/<uid>/
or
.B $HOME/
can be written to, for inter-process communication.
.P
If you find any other problems, please report them.
@@ -718,7 +759,7 @@ home page: <https://www.ivarch.com/programs/pv.shtml>
.BR O_DIRECT )
.SH COPYRIGHT
Copyright \(co 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood.
Copyright \(co 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood.
License GPLv3+: GNU GPL version 3 or later <https://www.gnu.org/licenses/gpl-3.0.html>.
+36 -9
View File
@@ -34,9 +34,9 @@ the expected size to **pv**:
A more complicated example using numeric output to feed into the
**dialog**(1) program for a full-screen progress display:
> **(tar cf - . \\**\
> ** \| pv -n -s \$(du -sb . \| awk \'{print \$1}\') \\**\
> ** \| gzip -9 \> out.tgz) 2\>&1 \\**\
> **(tar cf - . \\** \
> **\| pv -n -s \$(du -sb . \| awk \'{print \$1}\') \\** \
> **\| gzip -9 \> out.tgz) 2\>&1 \\** \
> **\| dialog \--gauge \'Progress\' 7 70**
Taking an image of a disk, skipping errors:
@@ -121,6 +121,12 @@ that are explicitly switched on will be shown.
: Display the total bits instead of the total bytes. The output suffix
will be \"b\" instead of \"B\".
**-k, \--si**
: Display and interpret suffixes as multiples of 1000 rather than the
default of 1024. Note that this only takes effect on options after
this one, so for consistency, specify this option first.
**-T, \--buffer-percent**
: Turn on the transfer buffer percentage display. This will show the
@@ -255,7 +261,9 @@ that are explicitly switched on will be shown.
: Limit the transfer to a maximum of *RATE* bytes per second. A suffix
of \"K\", \"M\", \"G\", or \"T\" can be added to denote kibibytes
(\*1024), mebibytes, and so on.
(\*1024), mebibytes, and so on. If **\--si** was also passed,
suffixes will denote kilobytes (\*1000), megabytes, etc. Note the
caveat about the positioning of **\--si .**
**-B BYTES, \--buffer-size BYTES**
@@ -496,6 +504,26 @@ Any other exit status is a bitmask of the following:
A zero exit status indicates no problems.
# ENVIRONMENT
The following environment variables may affect **pv**:
**HOME**
: The current user\'s home directory. This may be used by the remote
control mechanism (the **\--remote** option) to exchange messages
between **pv** instances: if the */run/user/UID/* directory does not
exist (where *UID* is the current user ID), then *\$HOME/.pv/* will
be used instead.
**TMPDIR**, **TMP**
: The directory to create per-tty lock files for the terminal when
using the **\--cursor** option. If **TMPDIR** is set to a non-empty
value, it is the directory under which lock files are created.
Otherwise, if **TMP** is set, then it is used; and if neither are
set, then */tmp* is used.
# AUTHOR
Written by Andrew Wood, with patches submitted by various other people.
@@ -517,10 +545,8 @@ The following problems are known to exist in **pv**:
is needed to handle the terminal scrolling). To fix this, start
**cygserver** before using **pv -c**.
- The **-R** option is not available on Cygwin without **cygserver**
running (SYSV IPC is needed). To fix this, start **cygserver**
before running the instance of **pv** you want, at runtime, to
change the parameters of.
- The **-R** option requires that either **/run/user/\<uid\>/** or
**\$HOME/** can be written to, for inter-process communication.
If you find any other problems, please report them.
@@ -537,7 +563,8 @@ Alternatively, use the issue tracker linked from the **pv** home page:
# COPYRIGHT
Copyright © 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood.
Copyright © 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew
Wood.
License GPLv3+: GNU GPL version 3 or later
\<https://www.gnu.org/licenses/gpl-3.0.html\>.
+114 -106
View File
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: pv@ivarch.com\n"
"POT-Creation-Date: 2023-09-24 21:33+0100\n"
"POT-Creation-Date: 2024-04-21 14:15+0100\n"
"Language: de\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Date: 1999-06-01 15:18:29+0100\n"
@@ -20,275 +20,279 @@ msgstr ""
"nls/intl/textdomain.c src/nls/intl-cat/cat-compat.c src/nls/intl-gett/intl-"
"compat.c\n"
#: src/main/help.c:283
#: src/main/help.c:284
msgid "show progress bar"
msgstr "Fortschritts-Anzeige"
#: src/main/help.c:286
#: src/main/help.c:287
msgid "show elapsed time"
msgstr "zeige die verstrichene Zeit an"
#: src/main/help.c:289
#: src/main/help.c:290
msgid "show estimated time of arrival (completion)"
msgstr "zeige die erwartete Zeit bis zum Ende an"
#: src/main/help.c:292
#: src/main/help.c:293
msgid "show absolute estimated time of arrival (completion)"
msgstr "zeige die absolute geschätzte Zeit bis zur Fertigstellung"
#: src/main/help.c:295
#: src/main/help.c:296
msgid "show data transfer rate counter"
msgstr "zeige die Datentransferrate an"
#: src/main/help.c:298
#: src/main/help.c:299
msgid "show data transfer average rate counter"
msgstr ""
#: src/main/help.c:300 src/main/help.c:328 src/main/help.c:340
#: src/main/help.c:301 src/main/help.c:332 src/main/help.c:344
msgid "SEC"
msgstr ""
#: src/main/help.c:301
#: src/main/help.c:302
msgid "compute average rate over past SEC seconds (default 30s)"
msgstr ""
#: src/main/help.c:304
#: src/main/help.c:305
msgid "show number of bytes transferred"
msgstr "zeige die Anzahl von Bytes, die transferiert worden sind"
#: src/main/help.c:307
#: src/main/help.c:308
msgid "show number of bits transferred"
msgstr ""
#: src/main/help.c:310
#: src/main/help.c:311
msgid "treat suffixes as multiples of 1000 rather than 1024"
msgstr ""
#: src/main/help.c:314
msgid "show percentage of transfer buffer in use"
msgstr "zeige, wie viel Prozent des Übertragungsbuffers in Benutzung sind"
#: src/main/help.c:312
#: src/main/help.c:316
msgid "NUM"
msgstr ""
#: src/main/help.c:313
#: src/main/help.c:317
msgid "show NUM bytes last written"
msgstr "NUM zeige die zuletzt geschriebenen NUM Bytes"
#: src/main/help.c:315
#: src/main/help.c:319
msgid "FORMAT"
msgstr ""
#: src/main/help.c:316
#: src/main/help.c:320
msgid "set output format to FORMAT"
msgstr "setze das Ausgabeformat auf FORMAT"
#: src/main/help.c:319
#: src/main/help.c:323
msgid "output percentages, not visual information"
msgstr "Ausgabe von Prozent-Angaben statt visueller Darstellung"
#: src/main/help.c:322
#: src/main/help.c:326
msgid "do not output any transfer information at all"
msgstr "sämtliche Transferinformationen unterdrücken"
#: src/main/help.c:326
#: src/main/help.c:330
msgid "display nothing until first byte transferred"
msgstr "keine Ausgabe bevor das erste Byte übertragen wurde"
#: src/main/help.c:329
#: src/main/help.c:333
msgid "display nothing until SEC seconds have passed"
msgstr "SEC nichts anzeigen, bis SEC Sekunden vergangen sind"
#: src/main/help.c:331
#: src/main/help.c:335
msgid "SIZE"
msgstr ""
#: src/main/help.c:332
#: src/main/help.c:336
msgid "set estimated data size to SIZE bytes"
msgstr "setze erwartete Datenlänge auf SIZE Byte"
#: src/main/help.c:335
#: src/main/help.c:339
msgid "count lines instead of bytes"
msgstr "zähle Zeilen anstelle von Bytes"
#: src/main/help.c:338
#: src/main/help.c:342
msgid "lines are null-terminated"
msgstr "Zeilen sind nullterminiert"
#: src/main/help.c:341
#: src/main/help.c:345
msgid "update every SEC seconds"
msgstr "aktualisiere Ausgabe nach SEC Sekunden Intervall"
#: src/main/help.c:343
#: src/main/help.c:347
msgid "WIDTH"
msgstr ""
#: src/main/help.c:344
#: src/main/help.c:348
msgid "assume terminal is WIDTH characters wide"
msgstr "setze Terminal-Breite auf WIDTH Zeichen"
#: src/main/help.c:346
#: src/main/help.c:350
msgid "HEIGHT"
msgstr ""
#: src/main/help.c:347
#: src/main/help.c:351
msgid "assume terminal is HEIGHT rows high"
msgstr "setze Terminal-Höhe auf HEIGHT Zeichen"
#: src/main/help.c:349
#: src/main/help.c:353
msgid "NAME"
msgstr ""
#: src/main/help.c:350
#: src/main/help.c:354
msgid "prefix visual information with NAME"
msgstr "setze den NAMEn für visuelle Darstellung"
#: src/main/help.c:353
#: src/main/help.c:357
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:356
#: src/main/help.c:360
msgid "use cursor positioning escape sequences"
msgstr "benutze Escape-Sequenzen zur Cursor-Positionierung"
#: src/main/help.c:359
#: src/main/help.c:363
msgid "RATE"
msgstr ""
#: src/main/help.c:360
#: src/main/help.c:364
msgid "limit transfer to RATE bytes per second"
msgstr "beschränke die Transferrate auf RATE Byte pro Sekunde"
#: src/main/help.c:362 src/main/help.c:371
#: src/main/help.c:366 src/main/help.c:375
msgid "BYTES"
msgstr ""
#: src/main/help.c:363
#: src/main/help.c:367
msgid "use a buffer size of BYTES"
msgstr "BYTES benutze einen Puffer in der Größe von BYTES"
#: src/main/help.c:366
#: src/main/help.c:370
msgid "never use splice(), always use read/write"
msgstr "anstelle von splice() immer read/write benutzen"
#: src/main/help.c:369
#: src/main/help.c:373
msgid "skip read errors in input"
msgstr "Lesefehler in der Eingabe nicht beachten"
#: src/main/help.c:372
#: src/main/help.c:376
msgid "skip errors in BYTES blocks at a time"
msgstr ""
#: src/main/help.c:375
#: src/main/help.c:379
msgid "stop after --size bytes have been transferred"
msgstr "anhalten, nachdem --size Bytes übertragen wurden"
#: src/main/help.c:378
#: src/main/help.c:382
msgid "flush cache to disk after every write"
msgstr ""
#: src/main/help.c:381
#: src/main/help.c:385
msgid "use direct I/O to bypass cache"
msgstr ""
#: src/main/help.c:384
#: src/main/help.c:388
msgid "discard input instead of writing to output"
msgstr ""
#: src/main/help.c:387
#: src/main/help.c:391
msgid "PID"
msgstr ""
#: src/main/help.c:388
#: src/main/help.c:392
msgid "update settings of process PID"
msgstr "Einstellungen der Prozess-PID updaten"
#: src/main/help.c:392 src/main/help.c:407
#: src/main/help.c:396 src/main/help.c:411
msgid "FILE"
msgstr ""
#: src/main/help.c:393
#: src/main/help.c:397
msgid "save process ID in FILE"
msgstr "Prozess-ID in FILE speichern"
#: src/main/help.c:396
#: src/main/help.c:400
msgid "PID[:FD]"
msgstr ""
#: src/main/help.c:397
#: src/main/help.c:401
msgid "watch file FD opened by process PID"
msgstr "die vom Prozess PID geöffnete Datei FD beobachten"
#: src/main/help.c:401
#: src/main/help.c:405
msgid "show this help and exit"
msgstr "zeige diese Hilfe und beende"
#: src/main/help.c:404
#: src/main/help.c:408
msgid "show version information and exit"
msgstr "zeige Versionsinformationen und beende"
#: src/main/help.c:408
#: src/main/help.c:412
msgid "write debug logs to FILE"
msgstr ""
#: src/main/help.c:433
#: src/main/help.c:437
#, c-format
msgid "Usage: %s [OPTION] [FILE]..."
msgstr "Syntax: %s [OPTION] [DATEI]..."
#: src/main/help.c:444
#: src/main/help.c:448
msgid ""
"Concatenate FILE(s), or standard input, to standard output, with monitoring."
msgstr ""
"Verbindet DATEI(en) oder den Standard-Eingabe-Kanal mit dem Standard-Ausgabe-"
"Kanal und misst den Datenstrom."
#: src/main/help.c:581
#: src/main/help.c:585
#, c-format
msgid "Please report any bugs to: %s"
msgstr "Bitte senden Sie Fehlerberichte an %s"
#: src/main/main.c:83
#: src/main/main.c:74
msgid "state allocation failed"
msgstr "Zustandszuweisung fehlgeschlagen"
#: src/main/options.c:173
#: src/main/options.c:174
msgid "option structure allocation failed"
msgstr "`option' konnte nicht allokiert werden"
#: src/main/options.c:192
#: src/main/options.c:193
msgid "option structure argv allocation failed"
msgstr "`option' (argv) konnte nicht allokiert werden"
#: src/main/options.c:254
#: src/main/options.c:255
msgid "integer argument expected"
msgstr "Ganzzahliges Argument erwartet"
#: src/main/options.c:265
#: src/main/options.c:266
msgid "numeric argument expected"
msgstr "numerisches Argument erwartet"
#: src/main/options.c:276
#: src/main/options.c:277
msgid "process ID or pid:fd pair expected"
msgstr "Prozess-ID oder PID:FD-Paar erwartet"
#: src/main/options.c:283
#: src/main/options.c:284
msgid "invalid process ID"
msgstr "ungültige Prozess-ID"
#: src/main/options.c:384 src/pv/file.c:323
#: src/main/options.c:388 src/pv/file.c:323
msgid "failed to stat file"
msgstr "Dateiinformationen konnten nicht gelesen werden"
#: src/main/options.c:487
#: src/main/options.c:491
#, c-format
msgid "Try `%s --help' for more information."
msgstr "`%s --help' zeigt weitere Informationen an."
#: src/main/options.c:489
#: src/main/options.c:493
#, c-format
msgid "Try `%s -h' for more information."
msgstr "`%s -h' zeigt weitere Informationen an."
#: src/main/options.c:520
#: src/main/options.c:524
msgid ""
"cannot use line mode or transfer modifier options when watching file "
"descriptors"
@@ -296,33 +300,33 @@ msgstr ""
"Beim Anzeigen von Dateideskriptoren können keine Optionen für den "
"Zeilenmodus oder den Übertragungsmodifikator verwendet werden"
#: src/main/options.c:529
#: src/main/options.c:533
msgid "cannot use cursor positioning when watching file descriptors"
msgstr ""
"Beim Anzeigen von Dateideskriptoren können keine Zeilenmodus- oder "
"Übertragungsmodifikatoroptionen verwendet werden."
#: src/main/options.c:538
#: src/main/options.c:542
msgid "cannot use remote control when watching file descriptors"
msgstr ""
"kann keine Fernbedienung verwenden, wenn Dateideskriptoren angezeigt werden"
#: src/main/options.c:547
#: src/main/options.c:551
msgid "cannot transfer files when watching file descriptors"
msgstr ""
"Dateien können beim Betrachten von Dateideskriptoren nicht übertragen werden"
#: src/main/options.c:562
#: src/main/options.c:566
msgid "not available on systems without /proc/self/fdinfo"
msgstr "Nicht verfügbar auf Systemen ohne /proc/self/fdinfo"
#: src/main/remote.c:242
#: src/main/remote.c:302
msgid "message not received"
msgstr "Nachricht nicht empfangen"
#: src/main/remote.c:351
msgid "IPC not supported on this system"
msgstr "IPC wird auf diesem System nicht unterstützt"
#: src/main/remote.c:429
msgid "SA_SIGINFO not supported on this system"
msgstr "SA_SIGINFO wird auf diesem System nicht unterstützt"
#: src/main/version.c:28
msgid "License: GPLv3+ <https://www.gnu.org/licenses/gpl-3.0.html>"
@@ -344,51 +348,51 @@ msgstr ""
msgid "failed to get terminal name"
msgstr "Terminalname konnte nicht gelesen werden"
#: src/pv/cursor.c:130
#: src/pv/cursor.c:132
msgid "failed to open lock file"
msgstr "Sperrdatei konnte nicht geöffnet werden"
#: src/pv/cursor.c:163
#: src/pv/cursor.c:165
msgid "lock attempt failed"
msgstr "Sperrversuch fehlgeschlagen"
#: src/pv/cursor.c:424
#: src/pv/cursor.c:426
msgid "failed to open terminal"
msgstr "Terminal konnte nicht geöffnet werden"
#: src/pv/display.c:187
#: src/pv/display.c:193
msgid "yzafpnum kMGTPEZY"
msgstr ""
#: src/pv/display.c:199
#: src/pv/display.c:211
msgid "yzafpnum KMGTPEZY"
msgstr ""
#: src/pv/display.c:701 src/pv/transfer.c:894
#: src/pv/display.c:750 src/pv/transfer.c:954
msgid "buffer allocation failed"
msgstr "Puffer konnte nicht allokiert werden"
#: src/pv/display.c:763
#: src/pv/display.c:856
msgid "b"
msgstr ""
#: src/pv/display.c:768 src/pv/transfer.c:584
#: src/pv/display.c:859 src/pv/transfer.c:596
msgid "B"
msgstr "B"
#: src/pv/display.c:820 src/pv/display.c:833
#: src/pv/display.c:900 src/pv/display.c:916
msgid "b/s"
msgstr ""
#: src/pv/display.c:824 src/pv/display.c:838
#: src/pv/display.c:905 src/pv/display.c:920
msgid "/s"
msgstr ""
#: src/pv/display.c:824 src/pv/display.c:838
#: src/pv/display.c:905 src/pv/display.c:920
msgid "B/s"
msgstr ""
#: src/pv/display.c:875 src/pv/display.c:879 src/pv/display.c:938
#: src/pv/display.c:947 src/pv/display.c:951 src/pv/display.c:1017
msgid "ETA"
msgstr "ETA"
@@ -420,11 +424,11 @@ msgstr ""
msgid "(stdin)"
msgstr ""
#: src/pv/loop.c:541 src/pv/loop.c:595 src/pv/loop.c:637 src/pv/watchpid.c:73
#: src/pv/watchpid.c:83 src/pv/watchpid.c:94 src/pv/watchpid.c:102
#: src/pv/watchpid.c:139 src/pv/watchpid.c:150 src/pv/watchpid.c:158
#: src/pv/watchpid.c:169 src/pv/watchpid.c:255 src/pv/watchpid.c:263
#: src/pv/watchpid.c:294 src/pv/watchpid.c:314
#: src/pv/loop.c:570 src/pv/loop.c:621 src/pv/loop.c:662 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
#: src/pv/watchpid.c:377 src/pv/watchpid.c:398
msgid "pid"
msgstr ""
@@ -432,51 +436,55 @@ msgstr ""
msgid "history structure allocation failed"
msgstr ""
#: src/pv/state.c:385 src/pv/state.c:395
#: src/pv/state.c:391 src/pv/state.c:402
msgid "file list allocation failed"
msgstr ""
#: src/pv/transfer.c:446
#: src/pv/transfer.c:458
msgid "read failed"
msgstr "read-Aufruf fehlgeschlagen"
#: src/pv/transfer.c:469
#: src/pv/transfer.c:481
msgid "warning: read errors detected"
msgstr "warnung: lesefehler erkannt"
#: src/pv/transfer.c:485
#: src/pv/transfer.c:497
msgid "file is not seekable"
msgstr "datei ist nicht durchsuchbar"
#: src/pv/transfer.c:563
#: src/pv/transfer.c:575
msgid "failed to seek past error"
msgstr "konnte den Fehler nicht überwinden"
#: src/pv/transfer.c:583
#: src/pv/transfer.c:595
msgid "skipped past read error"
msgstr "über den Lesefehler hinaus verschoben"
#: src/pv/transfer.c:623
#: src/pv/transfer.c:635
msgid "no transfer buffer allocated"
msgstr ""
#: src/pv/transfer.c:636
msgid "failed to set alarm signal handler"
#: src/pv/transfer.c:676
msgid "failed to set interval timer"
msgstr ""
#: src/pv/transfer.c:759
#: src/pv/transfer.c:694
msgid "failed to clear interval timer"
msgstr ""
#: src/pv/transfer.c:818
msgid "write failed"
msgstr "write-Aufruf fehlgeschlagen"
#: src/pv/transfer.c:985
#: src/pv/transfer.c:1051
msgid "select call failed"
msgstr "select-Aufruf fehlgeschlagen"
#: src/pv/watchpid.c:83 src/pv/watchpid.c:95 src/pv/watchpid.c:104
#: src/pv/watchpid.c:150 src/pv/watchpid.c:159 src/pv/watchpid.c:171
#: src/pv/watchpid.c:109 src/pv/watchpid.c:121 src/pv/watchpid.c:130
#: src/pv/watchpid.c:184 src/pv/watchpid.c:193 src/pv/watchpid.c:204
msgid "fd"
msgstr ""
#: src/pv/watchpid.c:104 src/pv/watchpid.c:171
#: src/pv/watchpid.c:130 src/pv/watchpid.c:204
msgid "not a regular file or block device"
msgstr "keine normale Datei oder Blockgerät"
+174 -159
View File
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: pv@ivarch.com\n"
"POT-Creation-Date: 2023-09-24 21:33+0100\n"
"POT-Creation-Date: 2024-04-21 14:15+0100\n"
"Language: fr\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Date: 1999-06-01 15:18:29+0100\n"
@@ -20,274 +20,280 @@ msgstr ""
"nls/intl/textdomain.c src/nls/intl-cat/cat-compat.c src/nls/intl-gett/intl-"
"compat.c\n"
#: src/main/help.c:283
#: src/main/help.c:284
msgid "show progress bar"
msgstr "affiche la barre de progression"
msgstr "afficher la barre de progression"
#: src/main/help.c:286
#: src/main/help.c:287
msgid "show elapsed time"
msgstr "affiche le temps écoulé"
msgstr "afficher le temps écoulé"
#: src/main/help.c:289
#: src/main/help.c:290
msgid "show estimated time of arrival (completion)"
msgstr "affiche l'heure approximative de l'achèvement de la tâche"
msgstr "afficher l'heure approximative de l'achèvement de la tâche"
#: src/main/help.c:292
#: src/main/help.c:293
msgid "show absolute estimated time of arrival (completion)"
msgstr "affiche l'heure absolute de l'achèvement de la tâche"
msgstr "afficher l'heure absolute de l'achèvement de la tâche"
#: src/main/help.c:295
#: src/main/help.c:296
msgid "show data transfer rate counter"
msgstr "affiche le taux de tranfert des données"
msgstr "afficher le taux de tranfert des données"
#: src/main/help.c:298
#: src/main/help.c:299
msgid "show data transfer average rate counter"
msgstr "afficher le compteur de taux moyen de transfert de données"
#: src/main/help.c:300 src/main/help.c:328 src/main/help.c:340
#: src/main/help.c:301 src/main/help.c:332 src/main/help.c:344
msgid "SEC"
msgstr "SEC"
#: src/main/help.c:301
#: src/main/help.c:302
msgid "compute average rate over past SEC seconds (default 30s)"
msgstr ""
"calculer le taux moyen d'après les SEC dernières secondes (30s par défaut)"
#: src/main/help.c:304
#: src/main/help.c:305
msgid "show number of bytes transferred"
msgstr "affiche le nombre d'octets transférés"
msgstr "afficher le nombre d'octets transférés"
#: src/main/help.c:307
#: src/main/help.c:308
msgid "show number of bits transferred"
msgstr "afficher le nombre de bits transférés"
#: src/main/help.c:311
msgid "treat suffixes as multiples of 1000 rather than 1024"
msgstr ""
#: src/main/help.c:310
#: src/main/help.c:314
msgid "show percentage of transfer buffer in use"
msgstr "afficher le pourcentage de tampon de transfert utilisé"
#: src/main/help.c:312
msgid "NUM"
msgstr ""
#: src/main/help.c:313
msgid "show NUM bytes last written"
msgstr "affiche NUM octets pour la dernière fois écrits"
#: src/main/help.c:315
msgid "FORMAT"
msgstr ""
#: src/main/help.c:316
msgid "NUM"
msgstr "NUM"
#: src/main/help.c:317
msgid "show NUM bytes last written"
msgstr "afficher les derniers NUM octets écrits"
#: src/main/help.c:319
msgid "FORMAT"
msgstr "FORMAT"
#: src/main/help.c:320
msgid "set output format to FORMAT"
msgstr "définir le format de sortie sur FORMAT"
#: src/main/help.c:319
#: src/main/help.c:323
msgid "output percentages, not visual information"
msgstr "imprime en pourcentage, pas les informations visuelles"
msgstr "imprimer en pourcentage plutôt que des informations visuelles"
#: src/main/help.c:322
#: src/main/help.c:326
msgid "do not output any transfer information at all"
msgstr "n'afficher aucune information de transfert"
#: src/main/help.c:326
#: src/main/help.c:330
msgid "display nothing until first byte transferred"
msgstr "ne rien afficher avant qu'au moins un octet soit tranféré"
#: src/main/help.c:329
#: src/main/help.c:333
msgid "display nothing until SEC seconds have passed"
msgstr "n'afficher rien jusqu'à ce que SEC secondes se soient écoulées"
msgstr "ne rien afficher jusqu'à ce que SEC secondes se soient écoulées"
#: src/main/help.c:331
#: src/main/help.c:335
msgid "SIZE"
msgstr "TAILLE"
#: src/main/help.c:332
#: src/main/help.c:336
msgid "set estimated data size to SIZE bytes"
msgstr "ajuste la taille estimée des données à TAILLE octets"
msgstr "ajuster la taille estimée des données à TAILLE octets"
#: src/main/help.c:335
#: src/main/help.c:339
msgid "count lines instead of bytes"
msgstr "compte les lignes au lieu des octets"
msgstr "compter les lignes au lieu des octets"
#: src/main/help.c:338
#: src/main/help.c:342
msgid "lines are null-terminated"
msgstr "les lignes sont terminées par un nul"
msgstr "les lignes sont terminées par un caractère nul"
#: src/main/help.c:341
#: src/main/help.c:345
msgid "update every SEC seconds"
msgstr "mise-à-jour toute les SEC secondes"
msgstr "mettre à jour toutes les SEC secondes"
#: src/main/help.c:343
#: src/main/help.c:347
msgid "WIDTH"
msgstr "LARGEUR"
#: src/main/help.c:344
#: src/main/help.c:348
msgid "assume terminal is WIDTH characters wide"
msgstr "présumer la largeur du terminal à LARGEUR caractères"
#: src/main/help.c:346
#: src/main/help.c:350
msgid "HEIGHT"
msgstr "HAUTEUR"
#: src/main/help.c:347
#: src/main/help.c:351
msgid "assume terminal is HEIGHT rows high"
msgstr "présumer la hauteur du terminal à HAUTEUR lignes"
#: src/main/help.c:349
#: src/main/help.c:353
msgid "NAME"
msgstr "NOM"
#: src/main/help.c:350
#: src/main/help.c:354
msgid "prefix visual information with NAME"
msgstr "préfixer les informations visuelles avec NOM"
#: src/main/help.c:353
#: src/main/help.c:357
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"
msgstr ""
"imprimer vers la sortie d'erreur standard même si ce n'est pas un terminal"
#: src/main/help.c:356
#: src/main/help.c:360
msgid "use cursor positioning escape sequences"
msgstr "utiliser les séquences d'échappements de positionnement de curseur"
#: src/main/help.c:359
#: src/main/help.c:363
msgid "RATE"
msgstr "TAUX"
#: src/main/help.c:360
#: src/main/help.c:364
msgid "limit transfer to RATE bytes per second"
msgstr "limite le taux de transfer à TAUX octets par seconde"
msgstr "limite le taux de transfert à TAUX octets par seconde"
#: src/main/help.c:362 src/main/help.c:371
#: src/main/help.c:366 src/main/help.c:375
msgid "BYTES"
msgstr "OCTETS"
#: src/main/help.c:363
#: src/main/help.c:367
msgid "use a buffer size of BYTES"
msgstr "Utiliser une mémoire tampon de OCTETS octets"
#: src/main/help.c:366
#: src/main/help.c:370
msgid "never use splice(), always use read/write"
msgstr "n'utilisez jamais splice(), utilisez toujours read/write"
msgstr "ne jamais utiliser splice(), toujours utiliser read/write"
#: src/main/help.c:369
#: src/main/help.c:373
msgid "skip read errors in input"
msgstr "ignorer les erreurs de lecture dans l'entrée"
#: src/main/help.c:372
#: src/main/help.c:376
msgid "skip errors in BYTES blocks at a time"
msgstr ""
msgstr "ignorer les erreurs dans OCTETS blocs à la fois"
#: src/main/help.c:375
#: src/main/help.c:379
msgid "stop after --size bytes have been transferred"
msgstr "arrêter après le transfert de --size octets"
#: src/main/help.c:378
#: src/main/help.c:382
msgid "flush cache to disk after every write"
msgstr ""
msgstr "vider le cache sur le disque après chaque écriture"
#: src/main/help.c:381
#: src/main/help.c:385
msgid "use direct I/O to bypass cache"
msgstr ""
msgstr "utiliser les E/S directes pour contourner le cache"
#: src/main/help.c:384
#: src/main/help.c:388
msgid "discard input instead of writing to output"
msgstr ""
msgstr "rejeter l'entrée au lieu de l'écrire vers la sortie"
#: src/main/help.c:387
#: src/main/help.c:391
msgid "PID"
msgstr "PID"
#: src/main/help.c:388
#: src/main/help.c:392
msgid "update settings of process PID"
msgstr "mettre-à-jour la configuration du processus PID"
msgstr "mettre à jour la configuration du processus PID"
#: src/main/help.c:392 src/main/help.c:407
#: src/main/help.c:396 src/main/help.c:411
msgid "FILE"
msgstr ""
#: src/main/help.c:393
msgid "save process ID in FILE"
msgstr "enregistrer l'ID de processus dans FILE"
#: src/main/help.c:396
msgid "PID[:FD]"
msgstr ""
msgstr "FICHIER"
#: src/main/help.c:397
msgid "watch file FD opened by process PID"
msgstr "regarder le fichier FD ouvert par le processus PID"
msgid "save process ID in FILE"
msgstr "enregistrer l'ID de processus dans FICHIER"
#: src/main/help.c:400
msgid "PID[:FD]"
msgstr "PID[:FD]"
#: src/main/help.c:401
msgid "watch file FD opened by process PID"
msgstr "surveiller le fichier FD ouvert par le processus PID"
#: src/main/help.c:405
msgid "show this help and exit"
msgstr "afficher cette aide puis quitter"
#: src/main/help.c:404
#: src/main/help.c:408
msgid "show version information and exit"
msgstr "afficher la version puis quitter"
#: src/main/help.c:408
#: src/main/help.c:412
msgid "write debug logs to FILE"
msgstr ""
msgstr "écrire les journaux de déboggage vers FICHIER"
#: src/main/help.c:433
#: src/main/help.c:437
#, c-format
msgid "Usage: %s [OPTION] [FILE]..."
msgstr "Utilisation : %s [OPTIONS] [FICHIER]..."
#: src/main/help.c:444
#: src/main/help.c:448
msgid ""
"Concatenate FILE(s), or standard input, to standard output, with monitoring."
msgstr ""
"Concatène FICHIER(s), ou l'entrée standard, sur la sortie standard avec "
"Concaténer FICHIER(s), ou l'entrée standard, sur la sortie standard avec "
"monitorage."
#: src/main/help.c:581
#: src/main/help.c:585
#, c-format
msgid "Please report any bugs to: %s"
msgstr "SVP rapporter touts bogues à %s"
msgstr "SVP rapporter tous bogues à %s"
#: src/main/main.c:83
#: src/main/main.c:74
msgid "state allocation failed"
msgstr "échec de l'allocation de mémoire de condition"
#: src/main/options.c:173
#: src/main/options.c:174
msgid "option structure allocation failed"
msgstr "l'allocation pour la structure d'une option a echoué"
msgstr "l'allocation pour la structure d'une option a échoué"
#: src/main/options.c:192
#: src/main/options.c:193
msgid "option structure argv allocation failed"
msgstr "l'allocation pour la structure de l'option argv a echoué"
msgstr "l'allocation pour la structure de l'option argv a échoué"
#: src/main/options.c:254
#: src/main/options.c:255
msgid "integer argument expected"
msgstr "Valeur entière attendue"
#: src/main/options.c:265
#: src/main/options.c:266
msgid "numeric argument expected"
msgstr "Valeur numérique attendue"
#: src/main/options.c:276
#: src/main/options.c:277
msgid "process ID or pid:fd pair expected"
msgstr "ID de processus ou paire pid:fd attendue"
#: src/main/options.c:283
#: src/main/options.c:284
msgid "invalid process ID"
msgstr "ID de processus non valide"
#: src/main/options.c:384 src/pv/file.c:323
#: src/main/options.c:388 src/pv/file.c:323
msgid "failed to stat file"
msgstr "échec à statuer sur le fichier"
#: src/main/options.c:487
#: src/main/options.c:491
#, c-format
msgid "Try `%s --help' for more information."
msgstr "Essayez `%s --help' pour plus d'information."
msgstr "Essayez `%s --help' pour plus d'informations."
#: src/main/options.c:489
#: src/main/options.c:493
#, c-format
msgid "Try `%s -h' for more information."
msgstr "Essayez `%s -h' pour plus d'information."
msgstr "Essayez `%s -h' pour plus d'informations."
#: src/main/options.c:520
#: src/main/options.c:524
msgid ""
"cannot use line mode or transfer modifier options when watching file "
"descriptors"
@@ -295,101 +301,103 @@ msgstr ""
"impossible d'utiliser le mode ligne ou les options de modificateur de "
"transfert lors de la visualisation des descripteurs de fichiers"
#: src/main/options.c:529
#: src/main/options.c:533
msgid "cannot use cursor positioning when watching file descriptors"
msgstr ""
"impossible d'utiliser le positionnement du curseur lors de la visualisation "
"des descripteurs de fichier"
#: src/main/options.c:538
#: src/main/options.c:542
msgid "cannot use remote control when watching file descriptors"
msgstr ""
"impossible d'utiliser la télécommande lors de la visualisation des "
"descripteurs de fichiers"
#: src/main/options.c:547
#: src/main/options.c:551
msgid "cannot transfer files when watching file descriptors"
msgstr ""
"impossible de transférer des fichiers lorsque vous regardez des descripteurs "
"de fichiers"
#: src/main/options.c:562
#: src/main/options.c:566
msgid "not available on systems without /proc/self/fdinfo"
msgstr "non disponible sur les systèmes sans /proc/self/fdinfo"
#: src/main/remote.c:242
#: src/main/remote.c:302
msgid "message not received"
msgstr "message non reçu"
#: src/main/remote.c:351
msgid "IPC not supported on this system"
msgstr "IPC non pris en charge sur ce système"
#: src/main/remote.c:429
msgid "SA_SIGINFO not supported on this system"
msgstr "SA_SIGINFO non pris en charge sur ce système"
#: src/main/version.c:28
msgid "License: GPLv3+ <https://www.gnu.org/licenses/gpl-3.0.html>"
msgstr ""
msgstr "License : GPLv3+ <https://www.gnu.org/licenses/gpl-3.0.html>"
#: src/main/version.c:29
msgid "This is free software: you are free to change and redistribute it."
msgstr ""
"Ceci est un logiciel libre : vous êtes libre de le modifier et de le "
"redistribuer."
#: src/main/version.c:30
msgid "There is NO WARRANTY, to the extent permitted by law."
msgstr ""
msgstr "Il n'y a AUCUNE GARANTIE, dans la mesure permise par la loi."
#: src/main/version.c:32
msgid "Project web site"
msgstr ""
msgstr "Site web du projet"
#: src/pv/cursor.c:87
msgid "failed to get terminal name"
msgstr "échec de lecture du nom du terminal"
#: src/pv/cursor.c:130
#: src/pv/cursor.c:132
msgid "failed to open lock file"
msgstr "échec de l'ouverture du fichier de verrouillage"
#: src/pv/cursor.c:163
#: src/pv/cursor.c:165
msgid "lock attempt failed"
msgstr "tentative de verrouillage échoué"
msgstr "échec de la tentative de verrouillage"
#: src/pv/cursor.c:424
#: src/pv/cursor.c:426
msgid "failed to open terminal"
msgstr "l'ouverture du terminal a échoué"
msgstr "échec de l'ouverture du terminal"
#: src/pv/display.c:187
#: src/pv/display.c:193
msgid "yzafpnum kMGTPEZY"
msgstr ""
#: src/pv/display.c:199
#: src/pv/display.c:211
msgid "yzafpnum KMGTPEZY"
msgstr ""
#: src/pv/display.c:701 src/pv/transfer.c:894
#: src/pv/display.c:750 src/pv/transfer.c:954
msgid "buffer allocation failed"
msgstr "l'allocation de mémoire tampon a échoué"
msgstr "échec de l'allocation de mémoire tampon"
#: src/pv/display.c:763
#: src/pv/display.c:856
msgid "b"
msgstr ""
#: src/pv/display.c:768 src/pv/transfer.c:584
#: src/pv/display.c:859 src/pv/transfer.c:596
msgid "B"
msgstr "O"
#: src/pv/display.c:820 src/pv/display.c:833
#: src/pv/display.c:900 src/pv/display.c:916
msgid "b/s"
msgstr ""
#: src/pv/display.c:824 src/pv/display.c:838
#: src/pv/display.c:905 src/pv/display.c:920
msgid "/s"
msgstr "/s"
#: src/pv/display.c:824 src/pv/display.c:838
#: src/pv/display.c:905 src/pv/display.c:920
msgid "B/s"
msgstr "O/s"
#: src/pv/display.c:875 src/pv/display.c:879 src/pv/display.c:938
#: src/pv/display.c:947 src/pv/display.c:951 src/pv/display.c:1017
msgid "ETA"
msgstr "ETA"
@@ -415,69 +423,76 @@ msgstr "fichiers d'entré et de sortie sont les mêmes"
#: src/pv/file.c:411
msgid "(none)"
msgstr ""
msgstr "(aucun)"
#: src/pv/file.c:413
msgid "(stdin)"
msgstr ""
#: src/pv/loop.c:541 src/pv/loop.c:595 src/pv/loop.c:637 src/pv/watchpid.c:73
#: src/pv/watchpid.c:83 src/pv/watchpid.c:94 src/pv/watchpid.c:102
#: src/pv/watchpid.c:139 src/pv/watchpid.c:150 src/pv/watchpid.c:158
#: src/pv/watchpid.c:169 src/pv/watchpid.c:255 src/pv/watchpid.c:263
#: src/pv/watchpid.c:294 src/pv/watchpid.c:314
#: src/pv/loop.c:570 src/pv/loop.c:621 src/pv/loop.c:662 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
#: src/pv/watchpid.c:377 src/pv/watchpid.c:398
msgid "pid"
msgstr ""
#: src/pv/state.c:36
msgid "history structure allocation failed"
msgstr ""
msgstr "l'allocation de la structure de l'historique a échoué"
#: src/pv/state.c:385 src/pv/state.c:395
#: src/pv/state.c:391 src/pv/state.c:402
msgid "file list allocation failed"
msgstr ""
msgstr "échec de l'allocation de la liste de fichiers"
#: src/pv/transfer.c:446
#: src/pv/transfer.c:458
msgid "read failed"
msgstr "la lecture a échoué"
#: src/pv/transfer.c:469
#: src/pv/transfer.c:481
msgid "warning: read errors detected"
msgstr "avertissement: erreurs de lecture détectées"
msgstr "avertissement : erreurs de lecture détectées"
#: src/pv/transfer.c:485
#: src/pv/transfer.c:497
msgid "file is not seekable"
msgstr "ne peut pas se déplacer dans le fichier"
#: src/pv/transfer.c:563
#: src/pv/transfer.c:575
msgid "failed to seek past error"
msgstr "n'a pas réussi à dépasser l'erreur"
#: src/pv/transfer.c:583
#: src/pv/transfer.c:595
msgid "skipped past read error"
msgstr "erreur de lecture dépassée"
#: src/pv/transfer.c:623
#: src/pv/transfer.c:635
msgid "no transfer buffer allocated"
msgstr "aucun tampon de transfert alloué"
#: src/pv/transfer.c:676
msgid "failed to set interval timer"
msgstr ""
#: src/pv/transfer.c:636
msgid "failed to set alarm signal handler"
#: src/pv/transfer.c:694
msgid "failed to clear interval timer"
msgstr ""
#: src/pv/transfer.c:759
#: src/pv/transfer.c:818
msgid "write failed"
msgstr "l'écriture a échoué"
#: src/pv/transfer.c:985
#: src/pv/transfer.c:1051
msgid "select call failed"
msgstr "appel de sélection a échoué"
msgstr "l'appel de sélection a échoué"
#: src/pv/watchpid.c:83 src/pv/watchpid.c:95 src/pv/watchpid.c:104
#: src/pv/watchpid.c:150 src/pv/watchpid.c:159 src/pv/watchpid.c:171
#: src/pv/watchpid.c:109 src/pv/watchpid.c:121 src/pv/watchpid.c:130
#: src/pv/watchpid.c:184 src/pv/watchpid.c:193 src/pv/watchpid.c:204
msgid "fd"
msgstr ""
#: src/pv/watchpid.c:104 src/pv/watchpid.c:171
#: src/pv/watchpid.c:130 src/pv/watchpid.c:204
msgid "not a regular file or block device"
msgstr "pas un fichier ordinaire ou un périphérique de bloc"
#~ msgid "failed to set alarm signal handler"
#~ msgstr "échec de la définition du gestionnaire de signal d'alarme"
+114 -105
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: 2023-09-24 21:33+0100\n"
"POT-Creation-Date: 2024-04-21 14:15+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"
@@ -15,301 +15,305 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/main/help.c:283
#: src/main/help.c:284
msgid "show progress bar"
msgstr "pokaż pasek postępu"
#: src/main/help.c:286
#: src/main/help.c:287
msgid "show elapsed time"
msgstr "pokaż upływający czas"
#: src/main/help.c:289
#: src/main/help.c:290
msgid "show estimated time of arrival (completion)"
msgstr "pokaż szacowany czas ukończenia"
#: src/main/help.c:292
#: src/main/help.c:293
msgid "show absolute estimated time of arrival (completion)"
msgstr ""
#: src/main/help.c:295
#: src/main/help.c:296
msgid "show data transfer rate counter"
msgstr "pokaż licznik prędkości przesyłania"
#: src/main/help.c:298
#: src/main/help.c:299
msgid "show data transfer average rate counter"
msgstr ""
#: src/main/help.c:300 src/main/help.c:328 src/main/help.c:340
#: src/main/help.c:301 src/main/help.c:332 src/main/help.c:344
msgid "SEC"
msgstr "WARTOŚĆ"
#: src/main/help.c:301
#: src/main/help.c:302
msgid "compute average rate over past SEC seconds (default 30s)"
msgstr ""
#: src/main/help.c:304
#: src/main/help.c:305
msgid "show number of bytes transferred"
msgstr "pokaż ilość przesłanych bajtów"
#: src/main/help.c:307
#: src/main/help.c:308
msgid "show number of bits transferred"
msgstr ""
#: src/main/help.c:310
#: src/main/help.c:311
msgid "treat suffixes as multiples of 1000 rather than 1024"
msgstr ""
#: src/main/help.c:314
msgid "show percentage of transfer buffer in use"
msgstr ""
#: src/main/help.c:312
#: src/main/help.c:316
msgid "NUM"
msgstr ""
#: src/main/help.c:313
#: src/main/help.c:317
msgid "show NUM bytes last written"
msgstr ""
#: src/main/help.c:315
#: src/main/help.c:319
msgid "FORMAT"
msgstr ""
#: src/main/help.c:316
#: src/main/help.c:320
msgid "set output format to FORMAT"
msgstr ""
#: src/main/help.c:319
#: src/main/help.c:323
msgid "output percentages, not visual information"
msgstr "wświetl wyjście procentowo, bez graficznej prezentacji"
#: src/main/help.c:322
#: src/main/help.c:326
msgid "do not output any transfer information at all"
msgstr "nie wyświetlaj żadnych informacji o przesyłaniu"
#: src/main/help.c:326
#: src/main/help.c:330
msgid "display nothing until first byte transferred"
msgstr "nie wyświetlaj nic aż do pierwszego przesłanego bajtu"
#: src/main/help.c:329
#: src/main/help.c:333
msgid "display nothing until SEC seconds have passed"
msgstr ""
#: src/main/help.c:331
#: src/main/help.c:335
msgid "SIZE"
msgstr "WARTOŚĆ"
#: src/main/help.c:332
#: src/main/help.c:336
msgid "set estimated data size to SIZE bytes"
msgstr "ustaw oczekiwany rozmiar danych na WARTOŚĆ bajtów"
#: src/main/help.c:335
#: src/main/help.c:339
msgid "count lines instead of bytes"
msgstr ""
#: src/main/help.c:338
#: src/main/help.c:342
msgid "lines are null-terminated"
msgstr ""
#: src/main/help.c:341
#: src/main/help.c:345
msgid "update every SEC seconds"
msgstr "aktualizuj co każde WARTOŚĆ sekund"
#: src/main/help.c:343
#: src/main/help.c:347
msgid "WIDTH"
msgstr "WARTOŚĆ"
#: src/main/help.c:344
#: src/main/help.c:348
msgid "assume terminal is WIDTH characters wide"
msgstr "przyjmij, że terminal ma szerokość WARTOŚĆ znaków"
#: src/main/help.c:346
#: src/main/help.c:350
msgid "HEIGHT"
msgstr ""
#: src/main/help.c:347
#: src/main/help.c:351
msgid "assume terminal is HEIGHT rows high"
msgstr "przyjmij, że terminal ma szerokość WARTOŚĆ znaków"
#: src/main/help.c:349
#: src/main/help.c:353
msgid "NAME"
msgstr "NAZWA"
#: src/main/help.c:350
#: src/main/help.c:354
msgid "prefix visual information with NAME"
msgstr "poprzedź informacje prefiksem NAZWA"
#: src/main/help.c:353
#: src/main/help.c:357
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:356
#: src/main/help.c:360
msgid "use cursor positioning escape sequences"
msgstr "używaj sekwencji escape do pozycjonowania"
#: src/main/help.c:359
#: src/main/help.c:363
msgid "RATE"
msgstr ""
#: src/main/help.c:360
#: src/main/help.c:364
msgid "limit transfer to RATE bytes per second"
msgstr "ogranicz przesyłane dane do RATE bajtów na sekundę"
#: src/main/help.c:362 src/main/help.c:371
#: src/main/help.c:366 src/main/help.c:375
msgid "BYTES"
msgstr ""
#: src/main/help.c:363
#: src/main/help.c:367
msgid "use a buffer size of BYTES"
msgstr ""
#: src/main/help.c:366
#: src/main/help.c:370
msgid "never use splice(), always use read/write"
msgstr ""
#: src/main/help.c:369
#: src/main/help.c:373
msgid "skip read errors in input"
msgstr ""
#: src/main/help.c:372
#: src/main/help.c:376
msgid "skip errors in BYTES blocks at a time"
msgstr ""
#: src/main/help.c:375
#: src/main/help.c:379
msgid "stop after --size bytes have been transferred"
msgstr ""
#: src/main/help.c:378
#: src/main/help.c:382
msgid "flush cache to disk after every write"
msgstr ""
#: src/main/help.c:381
#: src/main/help.c:385
msgid "use direct I/O to bypass cache"
msgstr ""
#: src/main/help.c:384
#: src/main/help.c:388
msgid "discard input instead of writing to output"
msgstr ""
#: src/main/help.c:387
#: src/main/help.c:391
msgid "PID"
msgstr ""
#: src/main/help.c:388
#: src/main/help.c:392
msgid "update settings of process PID"
msgstr ""
#: src/main/help.c:392 src/main/help.c:407
#: src/main/help.c:396 src/main/help.c:411
msgid "FILE"
msgstr ""
#: src/main/help.c:393
#: src/main/help.c:397
msgid "save process ID in FILE"
msgstr ""
#: src/main/help.c:396
#: src/main/help.c:400
msgid "PID[:FD]"
msgstr ""
#: src/main/help.c:397
#: src/main/help.c:401
msgid "watch file FD opened by process PID"
msgstr ""
#: src/main/help.c:401
#: src/main/help.c:405
msgid "show this help and exit"
msgstr "wyświetl te informacje z pomocą i wyjdź"
#: src/main/help.c:404
#: src/main/help.c:408
msgid "show version information and exit"
msgstr "wyświetl informacje o wersji i wyjdź"
#: src/main/help.c:408
#: src/main/help.c:412
msgid "write debug logs to FILE"
msgstr ""
#: src/main/help.c:433
#: src/main/help.c:437
#, c-format
msgid "Usage: %s [OPTION] [FILE]..."
msgstr "Sposób użycia: %s [OPCJA] [PLIK]..."
#: src/main/help.c:444
#: src/main/help.c:448
msgid ""
"Concatenate FILE(s), or standard input, to standard output, with monitoring."
msgstr ""
"Łączenie PLIK(ów) lub wejścia standardowego, do wyjścia standardowego z "
"monitoringiem."
#: src/main/help.c:581
#: src/main/help.c:585
#, c-format
msgid "Please report any bugs to: %s"
msgstr "Proszę przesyłać zgłoszenia błędów do %s"
#: src/main/main.c:83
#: src/main/main.c:74
msgid "state allocation failed"
msgstr ""
#: src/main/options.c:173
#: src/main/options.c:174
msgid "option structure allocation failed"
msgstr "nie udało się zaalokować struktur opcji"
#: src/main/options.c:192
#: src/main/options.c:193
msgid "option structure argv allocation failed"
msgstr "nie udało się zaalokować struktur opcji argv"
#: src/main/options.c:254
#: src/main/options.c:255
msgid "integer argument expected"
msgstr ""
#: src/main/options.c:265
#: src/main/options.c:266
msgid "numeric argument expected"
msgstr ""
#: src/main/options.c:276
#: src/main/options.c:277
msgid "process ID or pid:fd pair expected"
msgstr ""
#: src/main/options.c:283
#: src/main/options.c:284
msgid "invalid process ID"
msgstr ""
#: src/main/options.c:384 src/pv/file.c:323
#: src/main/options.c:388 src/pv/file.c:323
msgid "failed to stat file"
msgstr "nie udało się wykonać operacji stat na pliku"
#: src/main/options.c:487
#: src/main/options.c:491
#, c-format
msgid "Try `%s --help' for more information."
msgstr "Spróbuj `%s --help' by uzyskać więcej informacji"
#: src/main/options.c:489
#: src/main/options.c:493
#, c-format
msgid "Try `%s -h' for more information."
msgstr "Spróbuj `%s -h' by uzyskać więcej informacji"
#: src/main/options.c:520
#: src/main/options.c:524
msgid ""
"cannot use line mode or transfer modifier options when watching file "
"descriptors"
msgstr ""
#: src/main/options.c:529
#: src/main/options.c:533
msgid "cannot use cursor positioning when watching file descriptors"
msgstr ""
#: src/main/options.c:538
#: src/main/options.c:542
msgid "cannot use remote control when watching file descriptors"
msgstr ""
#: src/main/options.c:547
#: src/main/options.c:551
msgid "cannot transfer files when watching file descriptors"
msgstr ""
#: src/main/options.c:562
#: src/main/options.c:566
msgid "not available on systems without /proc/self/fdinfo"
msgstr ""
#: src/main/remote.c:242
#: src/main/remote.c:302
msgid "message not received"
msgstr ""
#: src/main/remote.c:351
msgid "IPC not supported on this system"
#: src/main/remote.c:429
msgid "SA_SIGINFO not supported on this system"
msgstr ""
#: src/main/version.c:28
@@ -333,51 +337,51 @@ msgid "failed to get terminal name"
msgstr ""
# "Przewidywany czas ukończenia" for ETA is too long
#: src/pv/cursor.c:130
#: src/pv/cursor.c:132
msgid "failed to open lock file"
msgstr ""
#: src/pv/cursor.c:163
#: src/pv/cursor.c:165
msgid "lock attempt failed"
msgstr ""
#: src/pv/cursor.c:424
#: src/pv/cursor.c:426
msgid "failed to open terminal"
msgstr "nie udało się otworzyć terminala"
#: src/pv/display.c:187
#: src/pv/display.c:193
msgid "yzafpnum kMGTPEZY"
msgstr ""
#: src/pv/display.c:199
#: src/pv/display.c:211
msgid "yzafpnum KMGTPEZY"
msgstr ""
#: src/pv/display.c:701 src/pv/transfer.c:894
#: src/pv/display.c:750 src/pv/transfer.c:954
msgid "buffer allocation failed"
msgstr "nie udało się zaalokować bufora"
#: src/pv/display.c:763
#: src/pv/display.c:856
msgid "b"
msgstr ""
#: src/pv/display.c:768 src/pv/transfer.c:584
#: src/pv/display.c:859 src/pv/transfer.c:596
msgid "B"
msgstr "B"
#: src/pv/display.c:820 src/pv/display.c:833
#: src/pv/display.c:900 src/pv/display.c:916
msgid "b/s"
msgstr ""
#: src/pv/display.c:824 src/pv/display.c:838
#: src/pv/display.c:905 src/pv/display.c:920
msgid "/s"
msgstr "/s"
#: src/pv/display.c:824 src/pv/display.c:838
#: src/pv/display.c:905 src/pv/display.c:920
msgid "B/s"
msgstr "B/s"
#: src/pv/display.c:875 src/pv/display.c:879 src/pv/display.c:938
#: src/pv/display.c:947 src/pv/display.c:951 src/pv/display.c:1017
msgid "ETA"
msgstr "ETA"
@@ -410,11 +414,11 @@ msgstr ""
msgid "(stdin)"
msgstr ""
#: src/pv/loop.c:541 src/pv/loop.c:595 src/pv/loop.c:637 src/pv/watchpid.c:73
#: src/pv/watchpid.c:83 src/pv/watchpid.c:94 src/pv/watchpid.c:102
#: src/pv/watchpid.c:139 src/pv/watchpid.c:150 src/pv/watchpid.c:158
#: src/pv/watchpid.c:169 src/pv/watchpid.c:255 src/pv/watchpid.c:263
#: src/pv/watchpid.c:294 src/pv/watchpid.c:314
#: src/pv/loop.c:570 src/pv/loop.c:621 src/pv/loop.c:662 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
#: src/pv/watchpid.c:377 src/pv/watchpid.c:398
msgid "pid"
msgstr ""
@@ -422,51 +426,56 @@ msgstr ""
msgid "history structure allocation failed"
msgstr ""
#: src/pv/state.c:385 src/pv/state.c:395
#: src/pv/state.c:391 src/pv/state.c:402
msgid "file list allocation failed"
msgstr ""
#: src/pv/transfer.c:446
#: src/pv/transfer.c:458
msgid "read failed"
msgstr "błąd odczytu"
#: src/pv/transfer.c:469
#: src/pv/transfer.c:481
msgid "warning: read errors detected"
msgstr ""
#: src/pv/transfer.c:485
#: src/pv/transfer.c:497
msgid "file is not seekable"
msgstr ""
#: src/pv/transfer.c:563
#: src/pv/transfer.c:575
msgid "failed to seek past error"
msgstr ""
#: src/pv/transfer.c:583
#: src/pv/transfer.c:595
msgid "skipped past read error"
msgstr ""
#: src/pv/transfer.c:623
#: src/pv/transfer.c:635
msgid "no transfer buffer allocated"
msgstr ""
#: src/pv/transfer.c:636
msgid "failed to set alarm signal handler"
#: src/pv/transfer.c:676
msgid "failed to set interval timer"
msgstr ""
#: src/pv/transfer.c:759
# "Przewidywany czas ukończenia" for ETA is too long
#: src/pv/transfer.c:694
msgid "failed to clear interval timer"
msgstr ""
#: src/pv/transfer.c:818
msgid "write failed"
msgstr "błąd zapisu"
#: src/pv/transfer.c:985
#: src/pv/transfer.c:1051
msgid "select call failed"
msgstr "nie udało się wywołać funkcji select"
#: src/pv/watchpid.c:83 src/pv/watchpid.c:95 src/pv/watchpid.c:104
#: src/pv/watchpid.c:150 src/pv/watchpid.c:159 src/pv/watchpid.c:171
#: src/pv/watchpid.c:109 src/pv/watchpid.c:121 src/pv/watchpid.c:130
#: src/pv/watchpid.c:184 src/pv/watchpid.c:193 src/pv/watchpid.c:204
msgid "fd"
msgstr ""
#: src/pv/watchpid.c:104 src/pv/watchpid.c:171
#: src/pv/watchpid.c:130 src/pv/watchpid.c:204
msgid "not a regular file or block device"
msgstr ""
+114 -106
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: 2023-09-24 21:33+0100\n"
"POT-Creation-Date: 2024-04-21 14:15+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"
@@ -15,274 +15,278 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/main/help.c:283
#: src/main/help.c:284
msgid "show progress bar"
msgstr "exibe barra de progressão"
#: src/main/help.c:286
#: src/main/help.c:287
msgid "show elapsed time"
msgstr "exibe tempo passado"
#: src/main/help.c:289
#: src/main/help.c:290
msgid "show estimated time of arrival (completion)"
msgstr "exibe o tempo estimado de término"
#: src/main/help.c:292
#: src/main/help.c:293
msgid "show absolute estimated time of arrival (completion)"
msgstr "exibe o tempo absoluto estimado de término"
#: src/main/help.c:295
#: src/main/help.c:296
msgid "show data transfer rate counter"
msgstr "exibe a taxa de transferência"
#: src/main/help.c:298
#: src/main/help.c:299
msgid "show data transfer average rate counter"
msgstr "exibe o contador da taxa média de transferência de dados"
#: src/main/help.c:300 src/main/help.c:328 src/main/help.c:340
#: src/main/help.c:301 src/main/help.c:332 src/main/help.c:344
msgid "SEC"
msgstr ""
#: src/main/help.c:301
#: src/main/help.c:302
msgid "compute average rate over past SEC seconds (default 30s)"
msgstr ""
#: src/main/help.c:304
#: src/main/help.c:305
msgid "show number of bytes transferred"
msgstr "exibe a quantidade de bytes transferidos"
#: src/main/help.c:307
#: src/main/help.c:308
msgid "show number of bits transferred"
msgstr ""
#: src/main/help.c:310
#: src/main/help.c:311
msgid "treat suffixes as multiples of 1000 rather than 1024"
msgstr ""
#: src/main/help.c:314
msgid "show percentage of transfer buffer in use"
msgstr "exibe a porcentagem de buffer de transferência em uso"
#: src/main/help.c:312
#: src/main/help.c:316
msgid "NUM"
msgstr ""
#: src/main/help.c:313
#: src/main/help.c:317
msgid "show NUM bytes last written"
msgstr "exibe NUM bytes gravados pela última vez"
#: src/main/help.c:315
#: src/main/help.c:319
msgid "FORMAT"
msgstr ""
#: src/main/help.c:316
#: src/main/help.c:320
msgid "set output format to FORMAT"
msgstr "definir o formato de saída para FORMAT"
#: src/main/help.c:319
#: src/main/help.c:323
msgid "output percentages, not visual information"
msgstr "exibe apenas as porcentagens, sem informações visuais"
#: src/main/help.c:322
#: src/main/help.c:326
msgid "do not output any transfer information at all"
msgstr "executa programa sem exibir quaisquer informações"
#: src/main/help.c:326
#: src/main/help.c:330
msgid "display nothing until first byte transferred"
msgstr "não exibe nada até iniciar o processamento"
#: src/main/help.c:329
#: src/main/help.c:333
msgid "display nothing until SEC seconds have passed"
msgstr "não exibir nada até que os segundos SEC tenham passado"
#: src/main/help.c:331
#: src/main/help.c:335
msgid "SIZE"
msgstr ""
#: src/main/help.c:332
#: src/main/help.c:336
msgid "set estimated data size to SIZE bytes"
msgstr "seta a quantidade estimada de dados em SIZE bytes"
#: src/main/help.c:335
#: src/main/help.c:339
msgid "count lines instead of bytes"
msgstr "contar linhas em vez de bytes"
#: src/main/help.c:338
#: src/main/help.c:342
msgid "lines are null-terminated"
msgstr "linhas são terminadas em nulo"
#: src/main/help.c:341
#: src/main/help.c:345
msgid "update every SEC seconds"
msgstr "atualiza informações a cada SEC segundos"
#: src/main/help.c:343
#: src/main/help.c:347
msgid "WIDTH"
msgstr ""
#: src/main/help.c:344
#: src/main/help.c:348
msgid "assume terminal is WIDTH characters wide"
msgstr "presuma que o terminal tem WIDTH caracteres de largura"
#: src/main/help.c:346
#: src/main/help.c:350
msgid "HEIGHT"
msgstr ""
#: src/main/help.c:347
#: src/main/help.c:351
msgid "assume terminal is HEIGHT rows high"
msgstr "presuma que o terminal tem HEIGHT caracteres de altura"
#: src/main/help.c:349
#: src/main/help.c:353
msgid "NAME"
msgstr ""
#: src/main/help.c:350
#: src/main/help.c:354
msgid "prefix visual information with NAME"
msgstr "exibe NAME antes das demais informações"
#: src/main/help.c:353
#: src/main/help.c:357
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:356
#: src/main/help.c:360
msgid "use cursor positioning escape sequences"
msgstr "utiliza caracteres de escape para posicionar o cursor"
#: src/main/help.c:359
#: src/main/help.c:363
msgid "RATE"
msgstr ""
#: src/main/help.c:360
#: src/main/help.c:364
msgid "limit transfer to RATE bytes per second"
msgstr "limita a transferência a RATE bytes por segundo"
#: src/main/help.c:362 src/main/help.c:371
#: src/main/help.c:366 src/main/help.c:375
msgid "BYTES"
msgstr ""
#: src/main/help.c:363
#: src/main/help.c:367
msgid "use a buffer size of BYTES"
msgstr "use um tamanho de buffer de BYTES"
#: src/main/help.c:366
#: src/main/help.c:370
msgid "never use splice(), always use read/write"
msgstr "nunca use splice(), sempre use read/write"
#: src/main/help.c:369
#: src/main/help.c:373
msgid "skip read errors in input"
msgstr "ignorar erros de leitura em entrada"
#: src/main/help.c:372
#: src/main/help.c:376
msgid "skip errors in BYTES blocks at a time"
msgstr ""
#: src/main/help.c:375
#: src/main/help.c:379
msgid "stop after --size bytes have been transferred"
msgstr "parar após --size bytes terem sido transferidos"
#: src/main/help.c:378
#: src/main/help.c:382
msgid "flush cache to disk after every write"
msgstr ""
#: src/main/help.c:381
#: src/main/help.c:385
msgid "use direct I/O to bypass cache"
msgstr ""
#: src/main/help.c:384
#: src/main/help.c:388
msgid "discard input instead of writing to output"
msgstr ""
#: src/main/help.c:387
#: src/main/help.c:391
msgid "PID"
msgstr ""
#: src/main/help.c:388
#: src/main/help.c:392
msgid "update settings of process PID"
msgstr "atualizar as configurações do processo PID"
#: src/main/help.c:392 src/main/help.c:407
#: src/main/help.c:396 src/main/help.c:411
msgid "FILE"
msgstr ""
#: src/main/help.c:393
#: src/main/help.c:397
msgid "save process ID in FILE"
msgstr "salvar ID do processo em FILE"
#: src/main/help.c:396
#: src/main/help.c:400
msgid "PID[:FD]"
msgstr ""
#: src/main/help.c:397
#: src/main/help.c:401
msgid "watch file FD opened by process PID"
msgstr "assistir arquivo FD aberto pelo processo PID"
#: src/main/help.c:401
#: src/main/help.c:405
msgid "show this help and exit"
msgstr "exibe esta tela de ajuda e termina"
#: src/main/help.c:404
#: src/main/help.c:408
msgid "show version information and exit"
msgstr "exibe a versão e termina"
#: src/main/help.c:408
#: src/main/help.c:412
msgid "write debug logs to FILE"
msgstr ""
#: src/main/help.c:433
#: src/main/help.c:437
#, c-format
msgid "Usage: %s [OPTION] [FILE]..."
msgstr "Uso: %s [OPÇÕES] [ARQUIVOS]..."
#: src/main/help.c:444
#: src/main/help.c:448
msgid ""
"Concatenate FILE(s), or standard input, to standard output, with monitoring."
msgstr ""
"Concatena ARQUIVO(s) ou a entrada padrão e grava na saída padrão, com "
"monitoramento."
#: src/main/help.c:581
#: src/main/help.c:585
#, c-format
msgid "Please report any bugs to: %s"
msgstr "Por favor, reporte quaisquer defeitos para %s"
#: src/main/main.c:83
#: src/main/main.c:74
msgid "state allocation failed"
msgstr "alocação de memória de status falhou"
#: src/main/options.c:173
#: src/main/options.c:174
msgid "option structure allocation failed"
msgstr "erro na alocação da estrutura de opções"
#: src/main/options.c:192
#: src/main/options.c:193
msgid "option structure argv allocation failed"
msgstr "erro na alocação da estrutura de opções argv"
#: src/main/options.c:254
#: src/main/options.c:255
msgid "integer argument expected"
msgstr "argumento inteiro esperado"
#: src/main/options.c:265
#: src/main/options.c:266
msgid "numeric argument expected"
msgstr "argumento numérico esperado"
#: src/main/options.c:276
#: src/main/options.c:277
msgid "process ID or pid:fd pair expected"
msgstr "ID do processo ou par pid:fd esperado"
#: src/main/options.c:283
#: src/main/options.c:284
msgid "invalid process ID"
msgstr "ID de processo inválido"
#: src/main/options.c:384 src/pv/file.c:323
#: src/main/options.c:388 src/pv/file.c:323
msgid "failed to stat file"
msgstr "erro obtendo informações do arquivo"
#: src/main/options.c:487
#: src/main/options.c:491
#, c-format
msgid "Try `%s --help' for more information."
msgstr "Tente `%s --help' para maiores informações."
#: src/main/options.c:489
#: src/main/options.c:493
#, c-format
msgid "Try `%s -h' for more information."
msgstr "Tente `%s -h' para maiores informações."
#: src/main/options.c:520
#: src/main/options.c:524
msgid ""
"cannot use line mode or transfer modifier options when watching file "
"descriptors"
@@ -290,31 +294,31 @@ msgstr ""
"não pode usar o modo de linha ou opções de modificador de transferência ao "
"assistir os descritores de arquivo"
#: src/main/options.c:529
#: src/main/options.c:533
msgid "cannot use cursor positioning when watching file descriptors"
msgstr ""
"não pode usar o posicionamento do cursor ao observar os descritores de "
"arquivo"
#: src/main/options.c:538
#: src/main/options.c:542
msgid "cannot use remote control when watching file descriptors"
msgstr "não pode usar o controle remoto ao assistir os descritores de arquivo"
#: src/main/options.c:547
#: src/main/options.c:551
msgid "cannot transfer files when watching file descriptors"
msgstr "não pode transferir arquivos ao assistir descritores de arquivo"
#: src/main/options.c:562
#: src/main/options.c:566
msgid "not available on systems without /proc/self/fdinfo"
msgstr "não disponível em sistemas sem /proc/self/fdinfo"
#: src/main/remote.c:242
#: src/main/remote.c:302
msgid "message not received"
msgstr "mensagem não recebida"
#: src/main/remote.c:351
msgid "IPC not supported on this system"
msgstr "IPC não suportado neste sistema"
#: src/main/remote.c:429
msgid "SA_SIGINFO not supported on this system"
msgstr "SA_SIGINFO não suportado neste sistema"
#: src/main/version.c:28
msgid "License: GPLv3+ <https://www.gnu.org/licenses/gpl-3.0.html>"
@@ -336,51 +340,51 @@ msgstr ""
msgid "failed to get terminal name"
msgstr "erro ao ler o nome do terminal"
#: src/pv/cursor.c:130
#: src/pv/cursor.c:132
msgid "failed to open lock file"
msgstr "erro ao abrir arquivo de bloqueio"
#: src/pv/cursor.c:163
#: src/pv/cursor.c:165
msgid "lock attempt failed"
msgstr "tentativa de bloqueio falhou"
#: src/pv/cursor.c:424
#: src/pv/cursor.c:426
msgid "failed to open terminal"
msgstr "erro abrindo o terminal"
#: src/pv/display.c:187
#: src/pv/display.c:193
msgid "yzafpnum kMGTPEZY"
msgstr ""
#: src/pv/display.c:199
#: src/pv/display.c:211
msgid "yzafpnum KMGTPEZY"
msgstr ""
#: src/pv/display.c:701 src/pv/transfer.c:894
#: src/pv/display.c:750 src/pv/transfer.c:954
msgid "buffer allocation failed"
msgstr "erro alocando o buffer"
#: src/pv/display.c:763
#: src/pv/display.c:856
msgid "b"
msgstr ""
#: src/pv/display.c:768 src/pv/transfer.c:584
#: src/pv/display.c:859 src/pv/transfer.c:596
msgid "B"
msgstr ""
#: src/pv/display.c:820 src/pv/display.c:833
#: src/pv/display.c:900 src/pv/display.c:916
msgid "b/s"
msgstr ""
#: src/pv/display.c:824 src/pv/display.c:838
#: src/pv/display.c:905 src/pv/display.c:920
msgid "/s"
msgstr ""
#: src/pv/display.c:824 src/pv/display.c:838
#: src/pv/display.c:905 src/pv/display.c:920
msgid "B/s"
msgstr "B/s"
#: src/pv/display.c:875 src/pv/display.c:879 src/pv/display.c:938
#: src/pv/display.c:947 src/pv/display.c:951 src/pv/display.c:1017
msgid "ETA"
msgstr "ETA"
@@ -412,11 +416,11 @@ msgstr ""
msgid "(stdin)"
msgstr ""
#: src/pv/loop.c:541 src/pv/loop.c:595 src/pv/loop.c:637 src/pv/watchpid.c:73
#: src/pv/watchpid.c:83 src/pv/watchpid.c:94 src/pv/watchpid.c:102
#: src/pv/watchpid.c:139 src/pv/watchpid.c:150 src/pv/watchpid.c:158
#: src/pv/watchpid.c:169 src/pv/watchpid.c:255 src/pv/watchpid.c:263
#: src/pv/watchpid.c:294 src/pv/watchpid.c:314
#: src/pv/loop.c:570 src/pv/loop.c:621 src/pv/loop.c:662 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
#: src/pv/watchpid.c:377 src/pv/watchpid.c:398
msgid "pid"
msgstr ""
@@ -424,51 +428,55 @@ msgstr ""
msgid "history structure allocation failed"
msgstr ""
#: src/pv/state.c:385 src/pv/state.c:395
#: src/pv/state.c:391 src/pv/state.c:402
msgid "file list allocation failed"
msgstr ""
#: src/pv/transfer.c:446
#: src/pv/transfer.c:458
msgid "read failed"
msgstr "erro de leitura"
#: src/pv/transfer.c:469
#: src/pv/transfer.c:481
msgid "warning: read errors detected"
msgstr "aviso: erros de leitura detectados"
#: src/pv/transfer.c:485
#: src/pv/transfer.c:497
msgid "file is not seekable"
msgstr "não pode mover dentro do arquivo"
#: src/pv/transfer.c:563
#: src/pv/transfer.c:575
msgid "failed to seek past error"
msgstr "não pode ir além do erro"
#: src/pv/transfer.c:583
#: src/pv/transfer.c:595
msgid "skipped past read error"
msgstr "ignorado erro de leitura"
#: src/pv/transfer.c:623
#: src/pv/transfer.c:635
msgid "no transfer buffer allocated"
msgstr ""
#: src/pv/transfer.c:636
msgid "failed to set alarm signal handler"
#: src/pv/transfer.c:676
msgid "failed to set interval timer"
msgstr ""
#: src/pv/transfer.c:759
#: src/pv/transfer.c:694
msgid "failed to clear interval timer"
msgstr ""
#: src/pv/transfer.c:818
msgid "write failed"
msgstr "erro de gravação"
#: src/pv/transfer.c:985
#: src/pv/transfer.c:1051
msgid "select call failed"
msgstr "erro na chamada da função select"
#: src/pv/watchpid.c:83 src/pv/watchpid.c:95 src/pv/watchpid.c:104
#: src/pv/watchpid.c:150 src/pv/watchpid.c:159 src/pv/watchpid.c:171
#: src/pv/watchpid.c:109 src/pv/watchpid.c:121 src/pv/watchpid.c:130
#: src/pv/watchpid.c:184 src/pv/watchpid.c:193 src/pv/watchpid.c:204
msgid "fd"
msgstr ""
#: src/pv/watchpid.c:104 src/pv/watchpid.c:171
#: src/pv/watchpid.c:130 src/pv/watchpid.c:204
msgid "not a regular file or block device"
msgstr "não é um arquivo regular ou dispositivo de bloqueio"
+1 -1
View File
@@ -3,7 +3,7 @@
* `configure' script. Include this after config.h and before anything
* else.
*
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
+9 -6
View File
@@ -79,15 +79,15 @@
/* Define to 1 if you have the `memmove' function. */
#undef HAVE_MEMMOVE
/* Define to 1 if you have the `memrchr' function. */
#undef HAVE_MEMRCHR
/* Define to 1 if you have the `memset' function. */
#undef HAVE_MEMSET
/* Define to 1 if you have the <minix/config.h> header file. */
#undef HAVE_MINIX_CONFIG_H
/* Define to 1 if you have the `msgget' function. */
#undef HAVE_MSGGET
/* Define to 1 if you have the `nanosleep' function. */
#undef HAVE_NANOSLEEP
@@ -100,9 +100,15 @@
/* Define to 1 if you have the `select' function. */
#undef HAVE_SELECT
/* Define to 1 if you have the `setitimer' function. */
#undef HAVE_SETITIMER
/* Define to 1 if you have the `setlocale' function. */
#undef HAVE_SETLOCALE
/* Define to 1 if you have the `shmget' function. */
#undef HAVE_SHMGET
/* Define to 1 if you have the `splice' function. */
#undef HAVE_SPLICE
@@ -157,9 +163,6 @@
/* Define to 1 if you have the <sys/ipc.h> header file. */
#undef HAVE_SYS_IPC_H
/* Define to 1 if you have the <sys/msg.h> header file. */
#undef HAVE_SYS_MSG_H
/* Define to 1 if you have the <sys/param.h> header file. */
#undef HAVE_SYS_PARAM_H
+2 -1
View File
@@ -1,7 +1,7 @@
/*
* Global program option structure and the parsing function prototype.
*
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
@@ -34,6 +34,7 @@ struct opts_s { /* structure describing run-time options */
bool average_rate; /* average rate counter flag */
bool bytes; /* bytes transferred flag */
bool bits; /* report transfer size in bits */
bool decimal_units; /* decimal prefix flag */
bool bufpercent; /* transfer buffer percentage flag */
size_t lastwritten; /* show N bytes last written */
bool force; /* force-if-not-terminal flag */
+195 -162
View File
@@ -1,7 +1,7 @@
/*
* Functions internal to the PV library. Include "config.h" first.
*
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
@@ -20,16 +20,25 @@
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
/*
* Types of display component that make up an output string.
*/
typedef enum {
PV_COMPONENT_STRING, /* fixed string */
PV_COMPONENT_PROGRESS, /* progress bar, with percentage if known */
PV_COMPONENT_BYTES, /* number of bytes transferred */
PV_COMPONENT_TIMER, /* elapsed time */
PV_COMPONENT_RATE, /* current transfer rate */
PV_COMPONENT_AVERAGERATE, /* average transfer rate */
PV_COMPONENT_ETA, /* estimated time remaining until completion */
PV_COMPONENT_FINETA, /* estimated time of completion */
PV_COMPONENT_NAME, /* name prefix */
PV_COMPONENT_BUFPERCENT, /* percentage of buffer used */
PV_COMPONENT_OUTPUTBUF, /* recent bytes in output buffer */
PV_COMPONENT__MAX
} pv_display_component;
#define PV_SIZEOF_COMPONENT_STR 1024 /* size of buffer for each component */
#define RATE_GRANULARITY 100000000 /* nsec between -L rate chunks */
#define RATE_BURST_WINDOW 5 /* rate burst window (multiples of rate) */
@@ -43,25 +52,9 @@ extern "C" {
#define MAXIMISE_BUFFER_FILL 1
typedef struct pvhistory {
off_t total_bytes;
long double elapsed_sec;
} pvhistory_t;
#define PV_SIZEOF_DEFAULT_FORMAT 512
#define PV_SIZEOF_CWD 4096
#define PV_SIZEOF_LASTOUTPUT_BUFFER 256
#define PV_SIZEOF_STR_NAME 512
#define PV_SIZEOF_STR_TRANSFERRED 128
#define PV_SIZEOF_STR_BUFPERCENT 128
#define PV_SIZEOF_STR_TIMER 128
#define PV_SIZEOF_STR_RATE 128
#define PV_SIZEOF_STR_AVERAGE_RATE 128
#define PV_SIZEOF_STR_PROGRESS 1024
#define PV_SIZEOF_STR_LASTOUTPUT 512
#define PV_SIZEOF_STR_ETA 128
#define PV_SIZEOF_STR_FINETA 128
#define PV_FORMAT_ARRAY_MAX 100
#define PV_SIZEOF_CRS_LOCK_FILE 1024
@@ -84,129 +77,163 @@ struct pvcursorstate_s {
* Structure for holding PV internal state. Opaque outside the PV library.
*/
struct pvstate_s {
/******************
* Program status *
******************/
struct {
/*@only@*/ char *program_name; /* program name for error reporting */
char cwd[PV_SIZEOF_CWD]; /* current working directory for relative path */
int current_input_file; /* index of current file being read */
int exit_status; /* exit status to give (0=OK) */
} status;
/***************
* Input files *
***************/
unsigned int input_file_count; /* number of input files */
/*@only@*/ /*@null@*/ char **input_files; /* input files */
struct {
unsigned int file_count; /* number of input files */
/*@only@*/ /*@null@*/ char **filename; /* input filenames */
} files;
/*******************
* Program control *
*******************/
bool force; /* display even if not on terminal */
bool cursor; /* use cursor positioning */
bool numeric; /* numeric output only */
bool wait; /* wait for data before display */
bool linemode; /* count lines instead of bytes */
bool bits; /* report bits instead of bytes */
bool null_terminated_lines; /* lines are null-terminated */
bool no_display; /* do nothing other than pipe data */
unsigned int skip_errors; /* skip read errors counter */
off_t error_skip_block; /* skip block size, 0 for adaptive */
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 direct_io_changed; /* set when direct_io is changed */
bool no_splice; /* never use splice() */
bool discard_input; /* write nothing to stdout */
off_t rate_limit; /* rate limit, in bytes per second */
size_t target_buffer_size; /* buffer size (0=default) */
off_t size; /* total size of data */
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 */
unsigned int width; /* screen width */
unsigned int height; /* screen height */
bool width_set_manually; /* width was set manually, not detected */
bool height_set_manually; /* height was set manually, not detected */
/*@only@*/ /*@null@*/ char *name; /* display name */
char default_format[PV_SIZEOF_DEFAULT_FORMAT]; /* default format string */
/*@only@*/ /*@null@*/ char *format_string; /* output format string */
/******************
* Program status *
******************/
/*@only@*/ char *program_name; /* program name for error reporting */
char cwd[PV_SIZEOF_CWD]; /* current working directory for relative path */
int current_input_file; /* index of current file being read */
int exit_status; /* exit status to give (0=OK) */
struct {
bool force; /* display even if not on terminal */
bool cursor; /* use cursor positioning */
bool numeric; /* numeric output only */
bool wait; /* wait for data before display */
bool linemode; /* count lines instead of bytes */
bool bits; /* report bits instead of bytes */
bool decimal_units; /* use decimal prefixes */
bool null_terminated_lines; /* lines are null-terminated */
bool no_display; /* do nothing other than pipe data */
unsigned int skip_errors; /* skip read errors counter */
off_t error_skip_block; /* skip block size, 0 for adaptive */
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 direct_io_changed; /* set when direct_io is changed */
bool no_splice; /* never use splice() */
bool discard_input; /* write nothing to stdout */
off_t rate_limit; /* rate limit, in bytes per second */
size_t target_buffer_size; /* buffer size (0=default) */
off_t size; /* total size of data */
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 */
unsigned int average_rate_window; /* time window in seconds for average rate calculations */
unsigned int width; /* screen width */
unsigned int height; /* screen height */
bool width_set_manually; /* width was set manually, not detected */
bool height_set_manually; /* height was set manually, not detected */
/*@only@*/ /*@null@*/ char *name; /* display name */
char default_format[PV_SIZEOF_DEFAULT_FORMAT]; /* default format string */
/*@only@*/ /*@null@*/ char *format_string; /* output format string */
} control;
/*******************
* Signal handling *
*******************/
int pv_sig_old_stderr; /* see pv_sig_ttou() */
bool pv_tty_tostop_added; /* whether we had to set TOSTOP on the terminal */
struct timespec pv_sig_tstp_time; /* see pv_sig_tstp() / __cont() */
struct timespec 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;
struct {
int old_stderr; /* see pv_sig_ttou() */
bool pv_tty_tostop_added; /* whether we had to set TOSTOP on the terminal */
struct timespec tstp_time; /* see pv_sig_tstp() / __cont() */
struct timespec toffset; /* total time spent stopped */
#ifdef SA_SIGINFO
volatile sig_atomic_t rxusr2; /* whether SIGUSR2 was received */
volatile pid_t sender; /* PID of sending process for SIGUSR2 */
#endif
/* old signal handlers to restore in pv_sig_fini(). */
struct sigaction old_sigpipe;
struct sigaction old_sigttou;
struct sigaction old_sigtstp;
struct sigaction old_sigcont;
struct sigaction old_sigwinch;
struct sigaction old_sigint;
struct sigaction old_sighup;
struct sigaction old_sigterm;
#ifdef SA_SIGINFO
struct sigaction old_sigusr2;
#endif
struct sigaction old_sigalrm;
} signal;
/*******************
* Transient flags *
*******************/
struct {
volatile sig_atomic_t reparse_display; /* whether to re-check format string */
volatile sig_atomic_t terminal_resized; /* whether we need to get term size again */
volatile sig_atomic_t trigger_exit; /* whether we need to abort right now */
} flag;
/*****************
* Display state *
*****************/
long percentage;
long double prev_elapsed_sec;
long double prev_rate;
long double prev_trans;
/* Keep track of progress over last intervals to compute current average rate. */
/*@null@*/ pvhistory_t *history; /* state at previous intervals (circular buffer) */
unsigned int history_len; /* total size */
int history_interval; /* seconds between each history entry */
int history_first;
int history_last;
long double current_avg_rate; /* current average rate over last history intervals */
off_t initial_offset;
/*@only@*/ char *display_buffer;
long display_buffer_size;
size_t lastoutput_length; /* number of last-output bytes to show */
char lastoutput_buffer[PV_SIZEOF_LASTOUTPUT_BUFFER];
int prev_width; /* screen width last time we were called */
int prev_length; /* length of last string we output */
char str_name[PV_SIZEOF_STR_NAME];
char str_transferred[PV_SIZEOF_STR_TRANSFERRED];
char str_bufpercent[PV_SIZEOF_STR_BUFPERCENT];
char str_timer[PV_SIZEOF_STR_TIMER];
char str_rate[PV_SIZEOF_STR_RATE];
char str_average_rate[PV_SIZEOF_STR_AVERAGE_RATE];
char str_progress[PV_SIZEOF_STR_PROGRESS];
char str_lastoutput[PV_SIZEOF_STR_LASTOUTPUT];
char str_eta[PV_SIZEOF_STR_ETA];
char str_fineta[PV_SIZEOF_STR_FINETA];
unsigned long components_used; /* bitmask of components used */
struct {
const char *string;
int length;
} format[PV_FORMAT_ARRAY_MAX];
bool display_visible; /* set once anything written to terminal */
/*@only@*/ /*@null@*/ char *display_buffer; /* buffer for display string */
size_t display_buffer_size; /* size allocated to display buffer */
size_t display_string_len; /* length of string in display buffer */
unsigned int prev_screen_width; /* screen width last time we were called */
bool display_visible; /* set once anything written to terminal */
int percentage; /* transfer percentage completion */
long double prev_elapsed_sec; /* elapsed sec at which rate last calculated */
long double prev_rate; /* last calculated instantaneous transfer rate */
long double prev_trans; /* bytes transferred since last rate calculation */
/* Keep track of progress over last intervals to compute current average rate. */
/*@null@*/ struct { /* state at previous intervals (circular buffer) */
long double elapsed_sec; /* time since start of transfer */
off_t total_bytes; /* amount transferred by that time */
} *history;
size_t history_len; /* total size of history array */
int history_interval; /* seconds between each history entry */
size_t history_first; /* index of oldest entry */
size_t history_last; /* index of newest entry */
long double current_avg_rate; /* current average rate over last history intervals */
off_t initial_offset; /* offset when first opened (when watching fds) */
size_t lastoutput_length; /* number of last-output bytes to show */
char lastoutput_buffer[PV_SIZEOF_LASTOUTPUT_BUFFER];
size_t format_segment_count; /* number of format string segments */
struct { /* format string broken into display components */
pv_display_component type; /* type of display component */
size_t str_start; /* for strings: start offset */
size_t str_length; /* for strings: length */
} format[PV_FORMAT_ARRAY_MAX];
struct { /* display components */
bool required; /* true if included in format */
char content[PV_SIZEOF_COMPONENT_STR]; /* string to display */
size_t length; /* number of bytes in string */
} component[PV_COMPONENT__MAX];
} display;
/********************
* Cursor/IPC state *
********************/
struct {
#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 */
/*@keep@*/ /*@null@*/ struct pvcursorstate_s *crs_shared; /* data shared between instances */
int crs_y_lastread; /* last value of _y_top seen */
int crs_y_offset; /* our Y offset from this top position */
int crs_needreinit; /* counter if we need to reinit cursor pos */
bool crs_noipc; /* set if we can't use IPC */
int shmid; /* ID of our shared memory segment */
int pvcount; /* number of `pv' processes in total */
int pvmax; /* highest number of `pv's seen */
/*@keep@*/ /*@null@*/ struct pvcursorstate_s *shared; /* data shared between instances */
int y_lastread; /* last value of _y_top seen */
int y_offset; /* our Y offset from this top position */
int needreinit; /* counter if we need to reinit cursor pos */
bool 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[PV_SIZEOF_CRS_LOCK_FILE];
int crs_y_start; /* our initial Y coordinate */
int lock_fd; /* fd of lockfile, -1 if none open */
char lock_file[PV_SIZEOF_CRS_LOCK_FILE];
int y_start; /* our initial Y coordinate */
} cursor;
/*******************
* Transfer state *
@@ -216,7 +243,7 @@ struct pvstate_s {
* 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
* 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
@@ -226,40 +253,45 @@ struct pvstate_s {
* is the offset in the buffer that we've written data up to. It
* will always be less than or equal to read_position.
*/
/*@keep@*/ /*@null@*/ char *transfer_buffer; /* data transfer buffer */
size_t buffer_size; /* size of buffer */
size_t read_position; /* amount of data in buffer */
size_t write_position; /* buffered data written */
struct {
/*@only@*/ /*@null@*/ char *transfer_buffer; /* data transfer buffer */
size_t buffer_size; /* size of buffer */
size_t read_position; /* amount of data in buffer */
size_t 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 to 0 and
* read_error_warning_shown to false 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;
off_t read_errors_in_a_row;
bool read_error_warning_shown;
/*
* 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 to 0 and
* read_error_warning_shown to false 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;
off_t read_errors_in_a_row;
bool 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 true if splice()
* was used this time within pv_transfer().
*/
int splice_failed_fd;
bool splice_used;
/*
* 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 true if splice() was used this
* time within pv_transfer().
*/
int splice_failed_fd;
bool splice_used;
#endif
ssize_t to_write; /* max to write this time around */
ssize_t written; /* bytes sent to stdout this time */
ssize_t to_write; /* max to write this time around */
ssize_t written; /* bytes sent to stdout this time */
} transfer;
};
@@ -278,6 +310,7 @@ struct pvwatchfd_s {
off_t size; /* size of whole file, 0 if unknown */
off_t position; /* position last seen at */
struct timespec start_time; /* time we started watching the fd */
/*@null@*/ pvstate_t state; /* state object for flags and display */
};
typedef struct pvwatchfd_s *pvwatchfd_t;
@@ -308,9 +341,9 @@ void pv_remote_fini(pvstate_t);
int pv_remote_set(pvstate_t);
int pv_watchfd_info(pvstate_t, pvwatchfd_t, bool);
int pv_watchfd_changed(pvwatchfd_t);
bool pv_watchfd_changed(pvwatchfd_t);
off_t pv_watchfd_position(pvwatchfd_t);
int pv_watchpid_scanfds(pvstate_t, pvstate_t, unsigned int, int *, pvwatchfd_t *, pvstate_t *, int *);
int pv_watchpid_scanfds(pvstate_t, pid_t, int *, pvwatchfd_t *, int *);
void pv_watchpid_setname(pvstate_t, pvwatchfd_t);
#ifdef __cplusplus
+21 -4
View File
@@ -1,7 +1,7 @@
/*
* Functions used across the program.
*
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
@@ -15,6 +15,7 @@
#include <stdlib.h>
#include <stdbool.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
@@ -48,15 +49,17 @@ typedef enum {
extern double pv_getnum_interval(const char *);
/*
* Return the given string converted to an off_t, for use as a size.
* Return the given string converted to an off_t, for use as a size,
* optionally interpreting suffixes in decimal units (multiples of 1000)
* instead of multiples of 1024.
*/
extern off_t pv_getnum_size(const char *);
extern off_t pv_getnum_size(const char *, bool);
/*
* Return the given string converted to an unsigned integer, for use as a
* count such as screen width.
*/
extern unsigned int pv_getnum_count(const char *);
extern unsigned int pv_getnum_count(const char *, bool);
/*
* Return true if the given string is a number of the given type. NB an
@@ -87,6 +90,12 @@ extern size_t pv_strlcat(char *, const char *, size_t);
*/
/*@null@ */ /*@only@ */ extern char *pv_strdup(const char *);
/*
* Return a pointer to the last matching character in the buffer, or NULL if
* not found.
*/
/*@null@ */ /*@temp@ */ extern void *pv_memrchr(const void *, int, size_t);
/*
* Functions relating to elapsed time.
*/
@@ -157,6 +166,7 @@ extern void pv_state_wait_set(pvstate_t, bool);
extern void pv_state_delay_start_set(pvstate_t, double);
extern void pv_state_linemode_set(pvstate_t, bool);
extern void pv_state_bits_set(pvstate_t, bool);
extern void pv_state_decimal_units_set(pvstate_t, bool);
extern void pv_state_null_terminated_lines_set(pvstate_t, bool);
extern void pv_state_no_display_set(pvstate_t, bool);
extern void pv_state_skip_errors_set(pvstate_t, unsigned int);
@@ -200,6 +210,13 @@ extern off_t pv_calc_total_size(pvstate_t);
*/
extern void pv_sig_init(pvstate_t);
#ifdef SA_SIGINFO
/*
* Return true if SIGUSR2 has been received, and indicate the sender.
*/
extern bool pv_sigusr2_received(pvstate_t, pid_t *);
#endif
/*
* Enter the main transfer loop, transferring all input files to the output.
*/
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* Output debugging information.
*
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
+7 -3
View File
@@ -1,7 +1,7 @@
/*
* Output command-line help to stdout.
*
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
@@ -9,6 +9,7 @@
#include "config.h"
#include "pv.h"
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
@@ -306,6 +307,9 @@ void display_help(void)
{ "-8", "--bits", NULL,
N_("show number of bits transferred"),
{ 0, 0, 0, 0} },
{ "-k", "--si", NULL,
N_("treat suffixes as multiples of 1000 rather than 1024"),
{ 0, 0, 0, 0} },
{ "-T", "--buffer-percent", NULL,
N_("show percentage of transfer buffer in use"),
{ 0, 0, 0, 0} },
@@ -383,11 +387,11 @@ void display_help(void)
{ "-X", "--discard", NULL,
N_("discard input instead of writing to output"),
{ 0, 0, 0, 0} },
#ifdef HAVE_IPC
#ifdef SA_SIGINFO
{ "-R", "--remote", N_("PID"),
N_("update settings of process PID"),
{ 0, 0, 0, 0} },
#endif /* HAVE_IPC */
#endif /* SA_SIGINFO */
{ "", NULL, NULL, NULL, { 0, 0, 0, 0} },
{ "-P", "--pidfile", N_("FILE"),
N_("save process ID in FILE"),
+20 -11
View File
@@ -2,7 +2,7 @@
* Main program entry point - read the command line options, then perform
* the appropriate actions.
*
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
@@ -24,7 +24,7 @@
#include <sys/stat.h>
int pv_remote_set(opts_t);
int pv_remote_set(opts_t, pvstate_t);
void pv_remote_init(void);
void pv_remote_fini(void);
@@ -60,15 +60,6 @@ int main(int argc, char **argv)
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.
*/
@@ -87,6 +78,23 @@ int main(int argc, char **argv)
/*@+mustfreefresh@ */
}
/*
* -R specified - send the message, then exit.
*/
if (opts->remote > 0) {
/* Initialise signal handling. */
pv_sig_init(state);
/* Send the message. */
retcode = pv_remote_set(opts, state);
/* Close down the signal handling. */
pv_sig_fini(state);
/* Free resources. */
pv_state_free(state);
opts_free(opts);
/* Early exit. */
return retcode;
}
/*
* Write a PID file if -P was specified.
*/
@@ -270,6 +278,7 @@ int main(int argc, char **argv)
pv_state_delay_start_set(state, opts->delay_start);
pv_state_linemode_set(state, opts->linemode);
pv_state_bits_set(state, opts->bits);
pv_state_decimal_units_set(state, opts->decimal_units);
pv_state_null_terminated_lines_set(state, opts->null_terminated_lines);
pv_state_skip_errors_set(state, opts->skip_errors);
pv_state_error_skip_block_set(state, opts->error_skip_block);
+15 -11
View File
@@ -1,7 +1,7 @@
/*
* Parse command-line options.
*
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
@@ -114,6 +114,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
{ "average-rate", 0, NULL, (int) 'a' },
{ "bytes", 0, NULL, (int) 'b' },
{ "bits", 0, NULL, (int) '8' },
{ "si", 0, NULL, (int) 'k' },
{ "buffer-percent", 0, NULL, (int) 'T' },
{ "last-written", 1, NULL, (int) 'A' },
{ "force", 0, NULL, (int) 'f' },
@@ -151,7 +152,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
/*@+nullassign@ */
int option_index = 0;
#endif /* HAVE_GETOPT_LONG */
char *short_options = "hVpteIrab8TA: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:"
#ifdef ENABLE_DEBUGGING
"!:"
#endif
@@ -335,13 +336,16 @@ opts_t opts_parse(unsigned int argc, char **argv)
opts->bits = true;
numopts++;
break;
case 'k':
opts->decimal_units = true;
break;
case 'T':
opts->bufpercent = true;
numopts++;
opts->no_splice = true;
break;
case 'A':
opts->lastwritten = (size_t) pv_getnum_count(optarg);
opts->lastwritten = (size_t) pv_getnum_count(optarg, opts->decimal_units);
numopts++;
opts->no_splice = true;
break;
@@ -387,7 +391,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
/*@+mustfreefresh@ */
}
} else {
opts->size = pv_getnum_size(optarg);
opts->size = pv_getnum_size(optarg, opts->decimal_units);
}
break;
case 'l':
@@ -401,11 +405,11 @@ opts_t opts_parse(unsigned int argc, char **argv)
opts->interval = pv_getnum_interval(optarg);
break;
case 'w':
opts->width = pv_getnum_count(optarg);
opts->width = pv_getnum_count(optarg, opts->decimal_units);
opts->width_set_manually = opts->width == 0 ? false : true;
break;
case 'H':
opts->height = pv_getnum_count(optarg);
opts->height = pv_getnum_count(optarg, opts->decimal_units);
opts->height_set_manually = opts->height == 0 ? false : true;
break;
case 'N':
@@ -417,10 +421,10 @@ opts_t opts_parse(unsigned int argc, char **argv)
}
break;
case 'L':
opts->rate_limit = pv_getnum_size(optarg);
opts->rate_limit = pv_getnum_size(optarg, opts->decimal_units);
break;
case 'B':
opts->buffer_size = (size_t) pv_getnum_size(optarg);
opts->buffer_size = (size_t) pv_getnum_size(optarg, opts->decimal_units);
opts->no_splice = true;
break;
case 'C':
@@ -430,7 +434,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
opts->skip_errors++;
break;
case 'Z':
opts->error_skip_block = pv_getnum_size(optarg);
opts->error_skip_block = pv_getnum_size(optarg, opts->decimal_units);
break;
case 'S':
opts->stop_at_size = true;
@@ -446,7 +450,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
opts->no_splice = true;
break;
case 'R':
opts->remote = pv_getnum_count(optarg);
opts->remote = pv_getnum_count(optarg, false);
break;
case 'P':
opts->pidfile = pv_strdup(optarg);
@@ -473,7 +477,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
opts->watch_fd = parse_fd;
break;
case 'm':
opts->average_rate_window = pv_getnum_count(optarg);
opts->average_rate_window = pv_getnum_count(optarg, opts->decimal_units);
break;
#ifdef ENABLE_DEBUGGING
case '!':
+193 -114
View File
@@ -1,7 +1,7 @@
/*
* Remote-control functions.
*
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
@@ -11,22 +11,20 @@
#include "pv.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#ifdef HAVE_IPC
#include <sys/ipc.h>
#include <sys/msg.h>
#endif /* HAVE_IPC */
#ifdef SA_SIGINFO
void pv_error(pvstate_t, char *, ...);
#ifdef HAVE_IPC
struct remote_msg {
long mtype;
bool progress; /* progress bar flag */
bool timer; /* timer flag */
bool eta; /* ETA flag */
@@ -53,67 +51,113 @@ struct remote_msg {
* bounded to one less than their size so they are always \0 terminated.
*/
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.
* Return a stream pointer, and populate the filename buffer, for a control
* file associated with a particular process ID; it will be opened for
* writing if "sender" is true. Returns NULL on error.
*/
static key_t remote__genkey(void)
static FILE *pv__control_file(char *filename, size_t bufsize, pid_t control_pid, bool sender)
{
uid_t uid;
key_t key;
int open_flags, open_mode, control_fd;
FILE *control_fptr;
/*@-type@ *//* splint doesn't like uid_t */
uid = geteuid();
/*@+type@ */
open_flags = O_RDONLY;
#ifdef O_NOFOLLOW
open_flags += O_NOFOLLOW;
#endif
if (sender)
open_flags = O_WRONLY | O_CREAT | O_EXCL;
key = ftok("/tmp", (int) 'P') | uid;
open_mode = 0644;
return key;
(void) pv_snprintf(filename, bufsize, "/run/user/%lu/pv.remote.%lu", (unsigned long) geteuid(),
(unsigned long) control_pid);
control_fd = open(filename, open_flags, open_mode); /* flawfinder: ignore */
/*
* If /run/user/<uid> wasn't usable, try $HOME/.pv instead.
*/
if (control_fd < 0) {
char *home_dir;
home_dir = getenv("HOME"); /* flawfinder: ignore */
if (NULL == home_dir)
return NULL;
if ('\0' == home_dir[0])
return NULL;
/*
* flawfinder rationale: null and zero-size values are
* rejected, and the destination buffer is bounded.
*/
(void) pv_snprintf(filename, bufsize, "%s/.pv/remote.%lu", home_dir, (unsigned long) control_pid);
control_fd = open(filename, open_flags, open_mode); /* flawfinder: ignore */
/*
* If the open failed, try creating the $HOME/.pv directory
* first.
*/
if (control_fd < 0) {
(void) pv_snprintf(filename, bufsize, "%s/.pv", home_dir);
(void) mkdir(filename, 0700);
/* In case of weird umask, explicitly chmod the dir. */
(void) chmod(filename, 0700); /* flawfinder: ignore */
(void) pv_snprintf(filename, bufsize, "%s/.pv/remote.%lu", home_dir,
(unsigned long) control_pid);
control_fd = open(filename, open_flags, open_mode); /* flawfinder: ignore */
}
}
/*
* flawfinder rationale: the files are in a directory whose parents
* cannot be manipulated, and we are not allowing the final
* component to be a symbolic link. We are checking that $HOME is
* not NULL, and it's bounded by pv_snprintf() so it can't overshoot
* the filename buffer. When we chmod $HOME/.pv, we assume that an
* attacker is unlikely to be able to manipulate $HOME's contents to
* make use of us setting $HOME/.pv to mode 700.
*/
if (control_fd < 0)
return NULL;
debug("%s: %s", "control filename", filename);
control_fptr = fdopen(control_fd, sender ? "wb" : "rb");
return control_fptr;
}
/*
* 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 */
/*@-unrecog@ *//* splint doesn't see SIGSYS */
(void) signal(SIGSYS, SIG_IGN);
/*@+unrecog@ */
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.
* Set the options of a remote process by writing them to a file, sending a
* signal to the receiving process, and waiting for the message to be
* consumed by the remote process.
*
* Returns nonzero on error.
*/
int pv_remote_set(opts_t opts)
int pv_remote_set(opts_t opts, pvstate_t state)
{
char control_filename[4096]; /* flawfinder: ignore */
FILE *control_fptr;
struct remote_msg msgbuf;
struct msqid_ds qbuf;
pid_t signal_sender;
long timeout;
int msgid;
unsigned long initial_qnum;
bool received;
/*
* flawfinder rationale: buffer is large enough, explicitly zeroed,
* and always bounded properly as we are only writing to it with
* pv_snprintf().
*/
/*
* Check that the remote process exists.
*/
if (kill((pid_t) (opts->remote), 0) != 0) {
fprintf(stderr, "%s: %u: %s\n", opts->program_name, opts->remote, strerror(errno));
pv_error(state, "%u: %s", opts->remote, strerror(errno));
return 1;
}
@@ -137,7 +181,6 @@ int pv_remote_set(opts_t opts)
* Copy parameters into message buffer.
*/
memset(&msgbuf, 0, sizeof(msgbuf));
msgbuf.mtype = (long) (opts->remote);
msgbuf.progress = opts->progress;
msgbuf.timer = opts->timer;
msgbuf.eta = opts->eta;
@@ -169,28 +212,56 @@ int pv_remote_set(opts_t opts)
* terminated by memset() earlier.
*/
msgid = remote__msgget();
if (msgid < 0) {
fprintf(stderr, "%s: %s\n", opts->program_name, strerror(errno));
/*
* Get the filename and file stream to use for remote control.
*/
memset(control_filename, 0, sizeof(control_filename));
control_fptr = pv__control_file(control_filename, sizeof(control_filename), getpid(), true);
if (NULL == control_fptr) {
pv_error(state, "%s", strerror(errno));
return 1;
}
memset(&qbuf, 0, sizeof(qbuf));
if (msgctl(msgid, IPC_STAT, &qbuf) < 0) {
fprintf(stderr, "%s: %s\n", opts->program_name, strerror(errno));
/*
* Write the message buffer to the remote control file, and close
* it.
*/
if (1 != fwrite(&msgbuf, sizeof(msgbuf), 1, control_fptr)) {
pv_error(state, "%s", strerror(errno));
(void) fclose(control_fptr);
(void) remove(control_filename);
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));
if (0 != fclose(control_fptr)) {
pv_error(state, "%s", strerror(errno));
(void) remove(control_filename);
return 1;
}
/*
* Send a SIGUSR2 signal to the remote process, to tell it a message
* is ready to read, after clearing our own "SIGUSR2 received" flag.
*/
signal_sender = 0;
(void) pv_sigusr2_received(state, &signal_sender);
if (kill((pid_t) (opts->remote), SIGUSR2) != 0) {
pv_error(state, "%u: %s", opts->remote, strerror(errno));
(void) remove(control_filename);
return 1;
}
debug("%s", "message sent");
/*
* Wait for a signal from the remote process to say it has received
* the message.
*/
timeout = 1100000;
received = false;
while (timeout > 10000) {
while (timeout > 10000 && !received) {
struct timeval tv;
memset(&tv, 0, sizeof(tv));
@@ -201,80 +272,91 @@ int pv_remote_set(opts_t opts)
/*@+nullpass@ */
timeout -= 10000;
/*
* If we can't stat the queue, it must have been deleted.
*/
memset(&qbuf, 0, sizeof(qbuf));
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;
if (pv_sigusr2_received(state, &signal_sender)) {
if (signal_sender == opts->remote) {
debug("%s", "message received");
received = true;
}
}
}
/*
* Message not received - delete it.
* Remove the remote control file.
*/
memset(&qbuf, 0, sizeof(qbuf));
if (msgctl(msgid, IPC_STAT, &qbuf) >= 0) {
(void) msgrcv(msgid, &msgbuf, sizeof(msgbuf) - sizeof(long), (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)
(void) msgctl(msgid, IPC_RMID, &qbuf);
}
if (0 != remove(control_filename)) {
pv_error(state, "%s", strerror(errno));
}
/*
* Return 0 if the message was received.
*/
if (received)
return 0;
/*@-mustfreefresh@ */
/*
* splint note: the gettext calls made by _() cause memory leak
* warnings, but in this case it's unavoidable, and mitigated by the
* fact we only translate each string once.
*/
fprintf(stderr, "%s: %u: %s\n", opts->program_name, opts->remote, _("message not received"));
pv_error(state, "%u: %s", opts->remote, _("message not received"));
return 1;
/*@+mustfreefresh @ */
}
/*
* Check for an IPC remote handling message and, if there is one, replace
* the current process's options with those being passed in.
* Check for a remote control 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)
{
pid_t signal_sender;
char control_filename[4096]; /* flawfinder: ignore */
FILE *control_fptr;
struct remote_msg msgbuf;
ssize_t got;
if (remote__msgid < 0)
/* flawfinder rationale: as above. */
/*
* Return early if a SIGUSR2 signal has not been received.
*/
signal_sender = 0;
if (!pv_sigusr2_received(state, &signal_sender))
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.
*/
/*@-unrecog@ *//* splint doesn't see ENOMSG */
if (errno != EAGAIN && errno != ENOMSG) {
remote__msgid = remote__msgget();
}
/*@+unrecog@ */
memset(control_filename, 0, sizeof(control_filename));
control_fptr = pv__control_file(control_filename, sizeof(control_filename), signal_sender, false);
if (NULL == control_fptr) {
pv_error(state, "%s", strerror(errno));
return;
}
if (got < 1)
/*
* Read the message buffer from the remote control file, and close
* it.
*/
if (1 != fread(&msgbuf, sizeof(msgbuf), 1, control_fptr)) {
pv_error(state, "%s", strerror(errno));
(void) fclose(control_fptr);
return;
}
if (0 != fclose(control_fptr)) {
pv_error(state, "%s", strerror(errno));
return;
}
/*
* Send a SIGUSR2 signal to the sending process, to tell it the
* message has been received.
*/
if (kill(signal_sender, SIGUSR2) != 0) {
debug("%u: %s", signal_sender, strerror(errno));
}
debug("%s", "received remote message");
@@ -313,7 +395,6 @@ void pv_remote_check(pvstate_t state)
*/
void pv_remote_init(void)
{
remote__msgid = remote__msgget();
}
@@ -322,23 +403,18 @@ void pv_remote_init(void)
*/
void pv_remote_fini(void)
{
if (remote__msgid >= 0) {
struct msqid_ds qbuf;
memset(&qbuf, 0, sizeof(qbuf));
(void) msgctl(remote__msgid, IPC_RMID, &qbuf);
}
}
#else /* !HAVE_IPC */
#else /* !SA_SIGINFO */
/*
* Dummy stubs for remote control when we don't have IPC.
* Dummy stubs for remote control when we don't have SA_SIGINFO.
*/
void pv_remote_init(void)
{
}
void pv_remote_check(pvstate_t state)
void pv_remote_check( /*@unused@ */ __attribute__((unused)) pvstate_t state)
{
}
@@ -346,12 +422,15 @@ void pv_remote_fini(void)
{
}
int pv_remote_set(pvstate_t state)
int pv_remote_set( /*@unused@ */
__attribute__((unused)) opts_t opts, /*@unused@ */ __attribute__((unused)) pvstate_t state)
{
fprintf(stderr, "%s\n", _("IPC not supported on this system"));
/*@-mustfreefresh@ *//* splint - see above */
fprintf(stderr, "%s\n", _("SA_SIGINFO not supported on this system"));
/*@+mustfreefresh@ */
return 1;
}
#endif /* HAVE_IPC */
#endif /* SA_SIGINFO */
/* EOF */
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* Output version information to stdout.
*
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
+115 -114
View File
@@ -10,7 +10,7 @@
* terminal, so we try to use a lockfile if terminal locking doesn't work,
* and finally abort if even that is unavailable.
*
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
@@ -79,33 +79,35 @@ static void pv_crs_open_lockfile(pvstate_t state, int fd)
char *tmpdir;
int openflags;
state->crs_lock_fd = -1;
state->cursor.lock_fd = -1;
ttydev = ttyname(fd);
if (!ttydev) {
if (!state->force) {
if (!state->control.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;
state->control.cursor = 0;
debug("%s", "ttyname failed - cursor positioning disabled");
return;
}
/*
* We used to look at the TMPDIR or TMP environment variables to
* override the temporary directory, but this leads to less
* predictable behaviour, and flawfinder points out that relying on
* environment variables in this way is not safe. So the lock
* directory is hard-coded to "/tmp" now (Sep 2023).
*/
tmpdir = "/tmp";
tmpdir = (char *) getenv("TMPDIR"); /* flawfinder: ignore */
if ((NULL == tmpdir) || ('\0' == tmpdir[0]))
tmpdir = (char *) getenv("TMP"); /* flawfinder: ignore */
if ((NULL == tmpdir) || ('\0' == tmpdir[0]))
tmpdir = "/tmp";
memset(state->crs_lock_file, 0, PV_SIZEOF_CRS_LOCK_FILE);
(void) pv_snprintf(state->crs_lock_file,
/*
* flawfinder rationale: null and zero-size values of $TMPDIR and
* $TMP are rejected, and the destination buffer is bounded.
*/
memset(state->cursor.lock_file, 0, PV_SIZEOF_CRS_LOCK_FILE);
(void) pv_snprintf(state->cursor.lock_file,
PV_SIZEOF_CRS_LOCK_FILE, "%s/pv-%s-%i.lock", tmpdir, basename(ttydev), (int) geteuid());
/*
@@ -117,7 +119,7 @@ static void pv_crs_open_lockfile(pvstate_t state, int fd)
openflags = O_RDWR | O_CREAT;
#endif
state->crs_lock_fd = open(state->crs_lock_file, openflags, 0600); /* flawfinder: ignore */
state->cursor.lock_fd = open(state->cursor.lock_file, openflags, 0600); /* flawfinder: ignore */
/*
* flawfinder rationale: we aren't truncating the lock file, we
@@ -126,9 +128,9 @@ static void pv_crs_open_lockfile(pvstate_t state, int fd)
* open() is as safe as we can make it.
*/
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;
if (state->cursor.lock_fd < 0) {
pv_error(state, "%s: %s: %s", state->cursor.lock_file, _("failed to open lock file"), strerror(errno));
state->control.cursor = 0;
return;
}
}
@@ -144,8 +146,8 @@ static void pv_crs_lock(pvstate_t state, int fd)
int lock_fd;
lock_fd = fd;
if (state->crs_lock_fd >= 0)
lock_fd = state->crs_lock_fd;
if (state->cursor.lock_fd >= 0)
lock_fd = state->cursor.lock_fd;
memset(&lock, 0, sizeof(lock));
lock.l_type = (short) F_WRLCK;
@@ -154,10 +156,10 @@ static void pv_crs_lock(pvstate_t state, int fd)
lock.l_len = 1;
while (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
if (errno != EINTR) {
if (state->crs_lock_fd == -2) {
if (state->cursor.lock_fd == -2) {
pv_crs_open_lockfile(state, fd);
if (state->crs_lock_fd >= 0) {
lock_fd = state->crs_lock_fd;
if (state->cursor.lock_fd >= 0) {
lock_fd = state->cursor.lock_fd;
}
} else {
pv_error(state, "%s: %s", _("lock attempt failed"), strerror(errno));
@@ -166,8 +168,8 @@ static void pv_crs_lock(pvstate_t state, int fd)
}
}
if (state->crs_lock_fd >= 0) {
debug("%s: %s", state->crs_lock_file, "terminal lockfile acquired");
if (state->cursor.lock_fd >= 0) {
debug("%s: %s", state->cursor.lock_file, "terminal lockfile acquired");
} else {
debug("%s", "terminal lock acquired");
}
@@ -184,8 +186,8 @@ static void pv_crs_unlock(pvstate_t state, int fd)
int lock_fd;
lock_fd = fd;
if (state->crs_lock_fd >= 0)
lock_fd = state->crs_lock_fd;
if (state->cursor.lock_fd >= 0)
lock_fd = state->cursor.lock_fd;
memset(&lock, 0, sizeof(lock));
lock.l_type = (short) F_UNLCK;
@@ -194,8 +196,8 @@ static void pv_crs_unlock(pvstate_t state, int fd)
lock.l_len = 1;
(void) fcntl(lock_fd, F_SETLK, &lock);
if (state->crs_lock_fd >= 0) {
debug("%s: %s", state->crs_lock_file, "terminal lockfile released");
if (state->cursor.lock_fd >= 0) {
debug("%s: %s", state->cursor.lock_file, "terminal lockfile released");
} else {
debug("%s", "terminal lock released");
}
@@ -216,13 +218,13 @@ static void pv_crs_ipccount(pvstate_t state)
memset(&buf, 0, sizeof(buf));
buf.shm_nattch = 0;
(void) shmctl(state->crs_shmid, IPC_STAT, &buf);
state->crs_pvcount = (int) (buf.shm_nattch);
(void) shmctl(state->cursor.shmid, IPC_STAT, &buf);
state->cursor.pvcount = (int) (buf.shm_nattch);
if (state->crs_pvcount > state->crs_pvmax)
state->crs_pvmax = state->crs_pvcount;
if (state->cursor.pvcount > state->cursor.pvmax)
state->cursor.pvmax = state->cursor.pvcount;
debug("%s: %d", "pvcount", state->crs_pvcount);
debug("%s: %d", "pvcount", state->cursor.pvcount);
}
#endif /* HAVE_IPC */
@@ -293,7 +295,7 @@ static int pv_crs_get_ypos(int terminalfd)
}
#endif /* !CURSOR_ANSWERBACK_BYTE_BY_BYTE */
ypos = (int) pv_getnum_count(cpr + 2);
ypos = (int) pv_getnum_count(cpr + 2, false);
if (0 != tcsetattr(terminalfd, TCSANOW | TCSAFLUSH, &old_tty)) {
debug("%s: %s", "tcsetattr (2) failed", strerror(errno));
@@ -334,13 +336,13 @@ static int pv_crs_ipcinit(pvstate_t state, char *ttyfile, int terminalfd)
}
pv_crs_lock(state, terminalfd);
if (!state->cursor) {
if (!state->control.cursor) {
debug("%s", "early return - cursor has been disabled");
return 1;
}
state->crs_shmid = shmget(key, sizeof(struct pvcursorstate_s), 0600 | IPC_CREAT);
if (state->crs_shmid < 0) {
state->cursor.shmid = shmget(key, sizeof(struct pvcursorstate_s), 0600 | IPC_CREAT);
if (state->cursor.shmid < 0) {
debug("%s: %s", "shmget failed", strerror(errno));
pv_crs_unlock(state, terminalfd);
return 1;
@@ -348,7 +350,7 @@ static int pv_crs_ipcinit(pvstate_t state, char *ttyfile, int terminalfd)
/*@-nullpass@ */
/* splint doesn't know shmaddr can be NULL */
state->crs_shared = shmat(state->crs_shmid, NULL, 0);
state->cursor.shared = shmat(state->cursor.shmid, NULL, 0);
/*@+nullpass@ */
pv_crs_ipccount(state);
@@ -359,26 +361,26 @@ static int pv_crs_ipcinit(pvstate_t state, char *ttyfile, int terminalfd)
* current Y cursor co-ordinate and with an initial false value for
* the TOSTOP-added flag.
*/
if (state->crs_pvcount < 2) {
state->crs_y_start = pv_crs_get_ypos(terminalfd);
state->crs_shared->y_topmost = state->crs_y_start;
state->crs_shared->tty_tostop_added = false;
state->crs_y_lastread = state->crs_y_start;
if (state->cursor.pvcount < 2) {
state->cursor.y_start = pv_crs_get_ypos(terminalfd);
state->cursor.shared->y_topmost = state->cursor.y_start;
state->cursor.shared->tty_tostop_added = false;
state->cursor.y_lastread = state->cursor.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;
state->cursor.y_offset = state->cursor.pvcount - 1;
if (state->cursor.y_offset < 0)
state->cursor.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_shared->y_topmost;
state->crs_y_lastread = state->crs_y_start;
debug("%s: %d", "not the first to attach - got top y", state->crs_y_start);
if (state->cursor.pvcount > 1) {
state->cursor.y_start = state->cursor.shared->y_topmost;
state->cursor.y_lastread = state->cursor.y_start;
debug("%s: %d", "not the first to attach - got top y", state->cursor.y_start);
}
pv_crs_unlock(state, terminalfd);
@@ -396,10 +398,10 @@ void pv_crs_init(pvstate_t state)
char *ttyfile;
int terminalfd;
state->crs_lock_fd = -2;
state->crs_lock_file[0] = '\0';
state->cursor.lock_fd = -2;
state->cursor.lock_file[0] = '\0';
if (!state->cursor)
if (!state->control.cursor)
return;
debug("%s", "init");
@@ -407,7 +409,7 @@ void pv_crs_init(pvstate_t state)
ttyfile = ttyname(STDERR_FILENO);
if (NULL == ttyfile) {
debug("%s: %s", "disabling cursor positioning because ttyname failed", strerror(errno));
state->cursor = false;
state->control.cursor = false;
return;
}
@@ -422,22 +424,22 @@ void pv_crs_init(pvstate_t state)
if (terminalfd < 0) {
pv_error(state, "%s: %s: %s", _("failed to open terminal"), ttyfile, strerror(errno));
state->cursor = false;
state->control.cursor = false;
return;
}
#ifdef HAVE_IPC
if (pv_crs_ipcinit(state, ttyfile, terminalfd) != 0) {
debug("%s", "ipcinit failed, setting noipc flag");
state->crs_noipc = true;
state->cursor.noipc = true;
}
/*
* If we have already set the terminal TOSTOP attribute, set the
* flag in shared memory to let the other instances know.
*/
if ((!state->crs_noipc) && state->pv_tty_tostop_added && (NULL != state->crs_shared)) {
if ((!state->cursor.noipc) && state->signal.pv_tty_tostop_added && (NULL != state->cursor.shared)) {
debug("%s", "propagating local pv_tty_tostop_added true value to shared flag");
state->crs_shared->tty_tostop_added = true;
state->cursor.shared->tty_tostop_added = true;
}
/*
@@ -445,7 +447,7 @@ void pv_crs_init(pvstate_t state)
* 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) {
if (state->cursor.noipc) {
#else /* ! HAVE_IPC */
if (1) {
#endif /* HAVE_IPC */
@@ -453,18 +455,18 @@ void pv_crs_init(pvstate_t state)
* Get current cursor position + 1.
*/
pv_crs_lock(state, terminalfd);
state->crs_y_start = pv_crs_get_ypos(terminalfd);
state->cursor.y_start = pv_crs_get_ypos(terminalfd);
/*
* 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)
if (state->cursor.y_start > 0)
pv_write_retry(STDERR_FILENO, "\n", 1);
pv_crs_unlock(state, terminalfd);
if (state->crs_y_start < 1)
state->cursor = 0;
if (state->cursor.y_start < 1)
state->control.cursor = 0;
}
(void) close(terminalfd);
@@ -477,9 +479,9 @@ void pv_crs_init(pvstate_t state)
*/
void pv_crs_needreinit(pvstate_t state)
{
state->crs_needreinit += 2;
if (state->crs_needreinit > 3)
state->crs_needreinit = 3;
state->cursor.needreinit += 2;
if (state->cursor.needreinit > 3)
state->cursor.needreinit = 3;
}
#endif
@@ -495,23 +497,23 @@ static void pv_crs_reinit(pvstate_t state)
pv_crs_lock(state, STDERR_FILENO);
state->crs_needreinit--;
if (state->crs_y_offset < 1)
state->crs_needreinit = 0;
state->cursor.needreinit--;
if (state->cursor.y_offset < 1)
state->cursor.needreinit = 0;
if (state->crs_needreinit > 0) {
if (state->cursor.needreinit > 0) {
pv_crs_unlock(state, STDERR_FILENO);
return;
}
debug("%s", "reinit full");
state->crs_y_start = pv_crs_get_ypos(STDERR_FILENO);
state->cursor.y_start = pv_crs_get_ypos(STDERR_FILENO);
if ((state->crs_y_offset < 1) && (NULL != state->crs_shared)) {
state->crs_shared->y_topmost = state->crs_y_start;
if ((state->cursor.y_offset < 1) && (NULL != state->cursor.shared)) {
state->cursor.shared->y_topmost = state->cursor.y_start;
}
state->crs_y_lastread = state->crs_y_start;
state->cursor.y_lastread = state->cursor.y_start;
pv_crs_unlock(state, STDERR_FILENO);
}
@@ -539,24 +541,24 @@ void pv_crs_update(pvstate_t state, const char *output_line)
/* flawfinder - output_line is explictly expected to be \0-terminated. */
#ifdef HAVE_IPC
if (!state->crs_noipc) {
if (state->crs_needreinit > 0)
if (!state->cursor.noipc) {
if (state->cursor.needreinit > 0)
pv_crs_reinit(state);
pv_crs_ipccount(state);
if (NULL != state->crs_shared) {
if (state->crs_y_lastread != state->crs_shared->y_topmost) {
state->crs_y_start = state->crs_shared->y_topmost;
state->crs_y_lastread = state->crs_y_start;
if (NULL != state->cursor.shared) {
if (state->cursor.y_lastread != state->cursor.shared->y_topmost) {
state->cursor.y_start = state->cursor.shared->y_topmost;
state->cursor.y_lastread = state->cursor.y_start;
}
}
if (state->crs_needreinit > 0)
if (state->cursor.needreinit > 0)
return;
}
#endif /* HAVE_IPC */
y = state->crs_y_start;
y = state->cursor.y_start;
#ifdef HAVE_IPC
/*
@@ -565,27 +567,27 @@ void pv_crs_update(pvstate_t state, const char *output_line)
* 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) > (int) (state->height))
&& (!state->crs_noipc)
if (((state->cursor.y_start + state->cursor.pvmax) > (int) (state->control.height))
&& (!state->cursor.noipc)
) {
int offs;
offs = ((state->crs_y_start + state->crs_pvmax) - state->height);
offs = ((state->cursor.y_start + state->cursor.pvmax) - state->control.height);
state->crs_y_start -= offs;
if (state->crs_y_start < 1)
state->crs_y_start = 1;
state->cursor.y_start -= offs;
if (state->cursor.y_start < 1)
state->cursor.y_start = 1;
debug("%s: %d", "scroll offset", offs);
/*
* Scroll the screen if we're the first `pv'.
*/
if (0 == state->crs_y_offset) {
if (0 == state->cursor.y_offset) {
pv_crs_lock(state, STDERR_FILENO);
memset(cup_cmd, 0, sizeof(cup_cmd));
(void) pv_snprintf(cup_cmd, sizeof(cup_cmd), "\033[%u;1H", state->height);
(void) pv_snprintf(cup_cmd, sizeof(cup_cmd), "\033[%u;1H", state->control.height);
cup_cmd_length = strlen(cup_cmd); /* flawfinder: ignore */
pv_write_retry(STDERR_FILENO, cup_cmd, cup_cmd_length);
for (; offs > 0; offs--) {
@@ -598,8 +600,8 @@ void pv_crs_update(pvstate_t state, const char *output_line)
}
}
if (!state->crs_noipc)
y = state->crs_y_start + state->crs_y_offset;
if (!state->cursor.noipc)
y = state->cursor.y_start + state->cursor.y_offset;
#endif /* HAVE_IPC */
/*
@@ -636,23 +638,20 @@ void pv_crs_fini(pvstate_t state)
{
char cup_cmd[32]; /* flawfinder: ignore */
unsigned int y;
struct shmid_ds shm_buf;
/* flawfinder - "cup_cmd" is zeroed, and only written by pv_snprintf() */
memset(&shm_buf, 0, sizeof(shm_buf));
debug("%s", "fini");
y = (unsigned int) (state->crs_y_start);
y = (unsigned int) (state->cursor.y_start);
#ifdef HAVE_IPC
if ((state->crs_pvmax > 0) && (!state->crs_noipc))
y += state->crs_pvmax - 1;
if ((state->cursor.pvmax > 0) && (!state->cursor.noipc))
y += state->cursor.pvmax - 1;
#endif /* HAVE_IPC */
if (y > state->height)
y = state->height;
if (y > state->control.height)
y = state->control.height;
/*
* Absolute bounds check.
@@ -668,44 +667,46 @@ void pv_crs_fini(pvstate_t state)
pv_write_retry(STDERR_FILENO, cup_cmd, strlen(cup_cmd)); /* flawfinder: ignore */
/* flawfinder - pv_snprintf() always \0-terminates (see above). */
#ifdef HAVE_IPC
/*
* If any other "pv -c" instances have set the terminal TOSTOP
* attribute, set our local flag so pv_sig_fini() will know about
* it.
*/
if ((!state->crs_noipc) && (NULL != state->crs_shared) && state->crs_shared->tty_tostop_added) {
if (!state->pv_tty_tostop_added) {
if ((!state->cursor.noipc) && (NULL != state->cursor.shared) && state->cursor.shared->tty_tostop_added) {
if (!state->signal.pv_tty_tostop_added) {
debug("%s", "propagating shared tty_tostop_added true value to local flag");
state->pv_tty_tostop_added = true;
state->signal.pv_tty_tostop_added = true;
}
}
#ifdef HAVE_IPC
pv_crs_ipccount(state);
if (NULL != state->crs_shared) {
(void) shmdt(state->crs_shared);
if (NULL != state->cursor.shared) {
(void) shmdt(state->cursor.shared);
}
state->crs_shared = NULL;
state->cursor.shared = NULL;
/*
* 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)
(void) shmctl(state->crs_shmid, IPC_RMID, &shm_buf);
if (state->cursor.pvcount < 2) {
struct shmid_ds shm_buf;
memset(&shm_buf, 0, sizeof(shm_buf));
(void) shmctl(state->cursor.shmid, IPC_RMID, &shm_buf);
}
#endif /* HAVE_IPC */
pv_crs_unlock(state, STDERR_FILENO);
if (state->crs_lock_fd >= 0) {
(void) close(state->crs_lock_fd);
if (state->cursor.lock_fd >= 0) {
(void) close(state->cursor.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.
*/
(void) remove(state->crs_lock_file);
(void) remove(state->cursor.lock_file);
}
}
+636 -445
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,7 +1,7 @@
/*
* Functions relating to elapsed time.
*
* Copyright 2023 Andrew Wood
* Copyright 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
+45 -45
View File
@@ -1,7 +1,7 @@
/*
* Functions for opening and closing files, and calculating their size.
*
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
@@ -45,25 +45,25 @@ static off_t pv_calc_total_bytes(pvstate_t state)
/*
* No files specified - check stdin.
*/
if ((state->input_file_count < 1) || (NULL == state->input_files)) {
if ((state->files.file_count < 1) || (NULL == state->files.filename)) {
if (0 == fstat(STDIN_FILENO, &sb))
total = sb.st_size;
return total;
}
for (file_idx = 0; file_idx < state->input_file_count; file_idx++) {
for (file_idx = 0; file_idx < state->files.file_count; file_idx++) {
int rc;
if (0 == strcmp(state->input_files[file_idx], "-")) {
if (0 == strcmp(state->files.filename[file_idx], "-")) {
rc = fstat(STDIN_FILENO, &sb);
if (rc != 0) {
total = 0;
return total;
}
} else {
rc = stat(state->input_files[file_idx], &sb);
rc = stat(state->files.filename[file_idx], &sb);
if (0 == rc) {
rc = access(state->input_files[file_idx], R_OK); /* flawfinder: ignore */
rc = access(state->files.filename[file_idx], R_OK); /* flawfinder: ignore */
/*
* flawfinder rationale: we're not really
* using access() to do permissions checks,
@@ -79,7 +79,7 @@ static off_t pv_calc_total_bytes(pvstate_t state)
}
if (rc != 0) {
debug("%s: %s", state->input_files[file_idx], strerror(errno));
debug("%s: %s", state->files.filename[file_idx], strerror(errno));
total = 0;
return total;
}
@@ -91,7 +91,7 @@ static off_t pv_calc_total_bytes(pvstate_t state)
* Get the size of block devices by opening
* them and seeking to the end.
*/
if (0 == strcmp(state->input_files[file_idx], "-")) {
if (0 == strcmp(state->files.filename[file_idx], "-")) {
fd = open("/dev/stdin", O_RDONLY); /* flawfinder: ignore */
/*
* flawfinder rationale: "/dev/stdin" may be
@@ -101,7 +101,7 @@ static off_t pv_calc_total_bytes(pvstate_t state)
* be under the control of someone else.
*/
} else {
fd = open(state->input_files[file_idx], O_RDONLY); /* flawfinder: ignore */
fd = open(state->files.filename[file_idx], O_RDONLY); /* flawfinder: ignore */
/* flawfinder - see last open() below. */
}
if (fd >= 0) {
@@ -146,7 +146,7 @@ static off_t pv_calc_total_bytes(pvstate_t state)
if (lseek(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;
state->status.exit_status |= 2;
}
/*
* If we worked out a size, then set the
@@ -155,7 +155,7 @@ static off_t pv_calc_total_bytes(pvstate_t state)
* device.
*/
if (total > 0) {
state->stop_at_size = true;
state->control.stop_at_size = true;
}
}
}
@@ -182,11 +182,11 @@ static off_t pv_calc_total_lines(pvstate_t state)
total = 0;
for (file_idx = 0; file_idx < state->input_file_count && NULL != state->input_files; file_idx++) {
for (file_idx = 0; file_idx < state->files.file_count && NULL != state->files.filename; file_idx++) {
int fd = -1;
int rc = 0;
if (0 == strcmp(state->input_files[file_idx], "-")) {
if (0 == strcmp(state->files.filename[file_idx], "-")) {
rc = fstat(STDIN_FILENO, &sb);
if ((rc != 0) || (!S_ISREG(sb.st_mode))) {
total = 0;
@@ -194,17 +194,17 @@ static off_t pv_calc_total_lines(pvstate_t state)
}
fd = dup(STDIN_FILENO);
} else {
rc = stat(state->input_files[file_idx], &sb);
rc = stat(state->files.filename[file_idx], &sb);
if ((rc != 0) || (!S_ISREG(sb.st_mode))) {
total = 0;
return total;
}
fd = open(state->input_files[file_idx], O_RDONLY); /* flawfinder: ignore */
fd = open(state->files.filename[file_idx], O_RDONLY); /* flawfinder: ignore */
/* flawfinder - see last open() below. */
}
if (fd < 0) {
debug("%s: %s", state->input_files[file_idx], strerror(errno));
debug("%s: %s", state->files.filename[file_idx], strerror(errno));
total = 0;
return total;
}
@@ -227,14 +227,14 @@ static off_t pv_calc_total_lines(pvstate_t state)
* OK.
*/
if (numread < 0) {
pv_error(state, "%s: %s", state->input_files[file_idx], strerror(errno));
state->exit_status |= 2;
pv_error(state, "%s: %s", state->files.filename[file_idx], strerror(errno));
state->status.exit_status |= 2;
break;
} else if (0 == numread) {
break;
}
for (buf_idx = 0; buf_idx < numread; buf_idx++) {
if (state->null_terminated_lines) {
if (state->control.null_terminated_lines) {
if ('\0' == scanbuf[buf_idx])
total++;
} else {
@@ -245,8 +245,8 @@ static off_t pv_calc_total_lines(pvstate_t state)
}
if (0 != lseek(fd, 0, SEEK_SET)) {
pv_error(state, "%s: %s", state->input_files[file_idx], strerror(errno));
state->exit_status |= 2;
pv_error(state, "%s: %s", state->files.filename[file_idx], strerror(errno));
state->status.exit_status |= 2;
}
(void) close(fd);
@@ -259,13 +259,13 @@ static off_t pv_calc_total_lines(pvstate_t state)
/*
* Work out the total size of all data by adding up the sizes of all input
* files, using either pv_calc_total_bytes() or pv_calc_total_lines()
* depending on whether state->linemode is true.
* depending on whether state->control.linemode is true.
*
* Returns the total size, or 0 if it is unknown.
*/
off_t pv_calc_total_size(pvstate_t state)
{
if (state->linemode) {
if (state->control.linemode) {
return pv_calc_total_lines(state);
} else {
return pv_calc_total_bytes(state);
@@ -279,7 +279,7 @@ off_t pv_calc_total_size(pvstate_t state)
* error). It is an error if the next input file is the same as the file
* stdout is pointing to.
*
* Updates state->current_input_file in the process.
* Updates state->status.current_input_file in the process.
*/
int pv_next_file(pvstate_t state, unsigned int filenum, int oldfd)
{
@@ -291,21 +291,21 @@ int pv_next_file(pvstate_t state, unsigned int filenum, int oldfd)
if (oldfd >= 0) {
if (0 != close(oldfd)) {
pv_error(state, "%s: %s", _("failed to close file"), strerror(errno));
state->exit_status |= 8;
state->status.exit_status |= 8;
return -1;
}
}
if (filenum >= state->input_file_count) {
debug("%s: %d >= %d", "filenum too large", filenum, state->input_file_count);
state->exit_status |= 8;
if (filenum >= state->files.file_count) {
debug("%s: %d >= %d", "filenum too large", filenum, state->files.file_count);
state->status.exit_status |= 8;
return -1;
}
if ((NULL == state->input_files) || (0 == strcmp(state->input_files[filenum], "-"))) {
if ((NULL == state->files.filename) || (0 == strcmp(state->files.filename[filenum], "-"))) {
fd = STDIN_FILENO;
} else {
fd = open(state->input_files[filenum], O_RDONLY); /* flawfinder: ignore */
fd = open(state->files.filename[filenum], O_RDONLY); /* flawfinder: ignore */
/*
* flawfinder rationale: the input file list is under the
* control of the operator by its nature, so we can't refuse
@@ -313,24 +313,24 @@ int pv_next_file(pvstate_t state, unsigned int filenum, int oldfd)
*/
if (fd < 0) {
pv_error(state, "%s: %s: %s",
_("failed to read file"), state->input_files[filenum], strerror(errno));
state->exit_status |= 2;
_("failed to read file"), state->files.filename[filenum], strerror(errno));
state->status.exit_status |= 2;
return -1;
}
}
if (0 != fstat(fd, &isb)) {
pv_error(state, "%s: %s: %s", _("failed to stat file"),
NULL == state->input_files ? "-" : state->input_files[filenum], strerror(errno));
NULL == state->files.filename ? "-" : state->files.filename[filenum], strerror(errno));
(void) close(fd);
state->exit_status |= 2;
state->status.exit_status |= 2;
return -1;
}
if (0 != fstat(STDOUT_FILENO, &osb)) {
pv_error(state, "%s: %s", _("failed to stat output file"), strerror(errno));
(void) close(fd);
state->exit_status |= 2;
state->status.exit_status |= 2;
return -1;
}
@@ -351,18 +351,18 @@ int pv_next_file(pvstate_t state, unsigned int filenum, int oldfd)
if (input_file_is_stdout) {
pv_error(state, "%s: %s", _("input file is output file"),
NULL == state->input_files ? "-" : state->input_files[filenum]);
NULL == state->files.filename ? "-" : state->files.filename[filenum]);
(void) close(fd);
state->exit_status |= 4;
state->status.exit_status |= 4;
return -1;
}
state->current_input_file = filenum;
state->status.current_input_file = filenum;
#ifdef O_DIRECT
/*
* Set or clear O_DIRECT on the file descriptor.
*/
if (0 != fcntl(fd, F_SETFL, (state->direct_io ? O_DIRECT : 0) | fcntl(fd, F_GETFL))) {
if (0 != fcntl(fd, F_SETFL, (state->control.direct_io ? O_DIRECT : 0) | fcntl(fd, F_GETFL))) {
/*@-compdef@ */
/*
* splint - passed or returned storage is undefined - but at
@@ -400,7 +400,7 @@ int pv_next_file(pvstate_t state, unsigned int filenum, int oldfd)
/*@-statictrans@ */
/*
* Here we are doing bad things with regards to whether the returned
* string is an allocated string from the state->input_files array,
* string is an allocated string from the state->files.filename array,
* a constant string, or a returned string from gettext(), but it
* has no impact. We explicitly document, above, that the returned
* string expires with the state, and hence switch off the
@@ -418,15 +418,15 @@ int pv_next_file(pvstate_t state, unsigned int filenum, int oldfd)
if (NULL == str_stdin)
str_stdin = "(stdin)";
if (state->current_input_file < 0)
if (state->status.current_input_file < 0)
return str_none;
if ((unsigned int) (state->current_input_file) >= state->input_file_count)
if ((unsigned int) (state->status.current_input_file) >= state->files.file_count)
return str_none;
if (NULL == state->input_files) {
if (NULL == state->files.filename) {
input_file_name = NULL;
} else {
input_file_name = state->input_files[state->current_input_file];
input_file_name = state->files.filename[state->status.current_input_file];
}
if (NULL == input_file_name)
return str_none;
@@ -437,7 +437,7 @@ int pv_next_file(pvstate_t state, unsigned int filenum, int oldfd)
return input_file_name;
/*@+compdef@ */
/*
* splint warns about state->input_files being undefined, but we
* splint warns about state->files.filename being undefined, but we
* know it's been populated fully by the time this function is
* called.
*/
+174 -138
View File
@@ -1,7 +1,7 @@
/*
* Functions providing the main transfer or file descriptor watching loop.
*
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
@@ -41,7 +41,6 @@ int pv_main_loop(pvstate_t state)
struct timespec start_time, next_update, next_ratecheck, cur_time;
struct timespec init_time, next_remotecheck, transfer_elapsed;
long double elapsed_seconds;
struct stat sb;
int fd;
unsigned int file_idx;
@@ -70,7 +69,7 @@ int pv_main_loop(pvstate_t state)
total_written = 0;
lineswritten = 0;
transferred_since_last = 0;
state->initial_offset = 0;
state->display.initial_offset = 0;
memset(&cur_time, 0, sizeof(cur_time));
memset(&start_time, 0, sizeof(start_time));
@@ -85,11 +84,11 @@ int pv_main_loop(pvstate_t state)
pv_elapsedtime_copy(&next_ratecheck, &cur_time);
pv_elapsedtime_copy(&next_remotecheck, &cur_time);
pv_elapsedtime_copy(&next_update, &cur_time);
if ((state->delay_start > 0)
&& (state->delay_start > state->interval)) {
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->delay_start));
if ((state->control.delay_start > 0)
&& (state->control.delay_start > state->control.interval)) {
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->control.delay_start));
} else {
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->interval));
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->control.interval));
}
target = 0;
@@ -100,7 +99,7 @@ int pv_main_loop(pvstate_t state)
* Open the first readable input file.
*/
fd = -1;
while (fd < 0 && file_idx < state->input_file_count) {
while (fd < 0 && file_idx < state->files.file_count) {
fd = pv_next_file(state, file_idx, -1);
if (fd < 0)
file_idx++;
@@ -110,9 +109,9 @@ int pv_main_loop(pvstate_t state)
* Exit early if there was no readable input file.
*/
if (fd < 0) {
if (state->cursor)
if (state->control.cursor)
pv_crs_fini(state);
return state->exit_status;
return state->status.exit_status;
}
#if HAVE_POSIX_FADVISE
/* Advise the OS that we will only be reading sequentially. */
@@ -123,10 +122,11 @@ int pv_main_loop(pvstate_t state)
/*
* Set or clear O_DIRECT on the output.
*/
if (0 != fcntl(STDOUT_FILENO, F_SETFL, (state->direct_io ? O_DIRECT : 0) | fcntl(STDOUT_FILENO, F_GETFL))) {
if (0 !=
fcntl(STDOUT_FILENO, F_SETFL, (state->control.direct_io ? O_DIRECT : 0) | fcntl(STDOUT_FILENO, F_GETFL))) {
debug("%s: %s", "fcntl", strerror(errno));
}
state->direct_io_changed = false;
state->control.direct_io_changed = false;
#endif /* O_DIRECT */
#if HAVE_STRUCT_STAT_ST_BLKSIZE
@@ -134,17 +134,22 @@ int pv_main_loop(pvstate_t state)
* 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 == fstat(fd, &sb)) && (0 == state->target_buffer_size)) {
size_t sz;
sz = (size_t) (sb.st_blksize * 32);
if (sz > BUFFER_SIZE_MAX)
sz = BUFFER_SIZE_MAX;
state->target_buffer_size = sz;
if (0 == state->control.target_buffer_size) {
struct stat sb;
memset(&sb, 0, sizeof(sb));
if (0 == fstat(fd, &sb)) {
size_t sz;
sz = (size_t) (sb.st_blksize * 32);
if (sz > BUFFER_SIZE_MAX) {
sz = BUFFER_SIZE_MAX;
}
state->control.target_buffer_size = sz;
}
}
#endif
if (0 == state->target_buffer_size)
state->target_buffer_size = BUFFER_SIZE;
if (0 == state->control.target_buffer_size)
state->control.target_buffer_size = BUFFER_SIZE;
while ((!(eof_in && eof_out)) || (!final_update)) {
@@ -158,17 +163,17 @@ int pv_main_loop(pvstate_t state)
pv_elapsedtime_add_nsec(&next_remotecheck, REMOTE_INTERVAL);
}
if (1 == state->pv_sig_abort)
if (1 == state->flag.trigger_exit)
break;
if (state->rate_limit > 0) {
if (state->control.rate_limit > 0) {
pv_elapsedtime_read(&cur_time);
if (pv_elapsedtime_compare(&cur_time, &next_ratecheck) > 0) {
target +=
((long double) (state->rate_limit)) / (long double) (1000000000.0 /
(long
double) (RATE_GRANULARITY));
long double burst_max = ((long double) (state->rate_limit * RATE_BURST_WINDOW));
((long double) (state->control.rate_limit)) / (long double) (1000000000.0 /
(long double)
(RATE_GRANULARITY));
long double burst_max = ((long double) (state->control.rate_limit * RATE_BURST_WINDOW));
if (target > burst_max) {
target = burst_max;
}
@@ -181,11 +186,11 @@ int pv_main_loop(pvstate_t state)
* 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))
if ((0 < state->control.size) && (state->control.stop_at_size)) {
if ((state->control.size < (total_written + cansend))
|| ((0 == cansend)
&& (0 == state->rate_limit))) {
cansend = state->size - total_written;
&& (0 == state->control.rate_limit))) {
cansend = state->control.size - total_written;
if (0 >= cansend) {
eof_in = true;
eof_out = true;
@@ -193,7 +198,7 @@ int pv_main_loop(pvstate_t state)
}
}
if ((0 < state->size) && (state->stop_at_size)
if ((0 < state->control.size) && (state->control.stop_at_size)
&& (0 >= cansend) && eof_in && eof_out) {
written = 0;
} else {
@@ -202,27 +207,27 @@ int pv_main_loop(pvstate_t state)
/* End on write error. */
if (written < 0) {
if (state->cursor)
if (state->control.cursor)
pv_crs_fini(state);
return state->exit_status;
return state->status.exit_status;
}
if (state->linemode) {
if (state->control.linemode) {
transferred_since_last += lineswritten;
total_written += lineswritten;
if (state->rate_limit > 0)
if (state->control.rate_limit > 0)
target -= lineswritten;
} else {
transferred_since_last += written;
total_written += written;
if (state->rate_limit > 0)
if (state->control.rate_limit > 0)
target -= written;
}
/*
* EOF, and files remain - advance to the next file.
*/
while (eof_in && eof_out && file_idx < (state->input_file_count - 1)) {
while (eof_in && eof_out && file_idx < (state->files.file_count - 1)) {
file_idx++;
fd = pv_next_file(state, file_idx, fd);
if (fd >= 0) {
@@ -237,14 +242,14 @@ int pv_main_loop(pvstate_t state)
/* If full EOF, final update, and force a display updaate. */
if (eof_in && eof_out) {
final_update = true;
if ((state->display_visible)
|| (state->delay_start < 0.001)) {
if ((state->display.display_visible)
|| (state->control.delay_start < 0.001)) {
pv_elapsedtime_copy(&next_update, &cur_time);
}
}
/* Just go round the loop again if there's no display. */
if (state->no_display)
if (state->control.no_display)
continue;
/*
@@ -253,9 +258,9 @@ int pv_main_loop(pvstate_t state)
* we then count time as if we started when the first byte
* was received.
*/
if (state->wait) {
if (state->control.wait) {
/* Restart the loop if nothing written yet. */
if (state->linemode) {
if (state->control.linemode) {
if (lineswritten < 1)
continue;
} else {
@@ -263,7 +268,7 @@ int pv_main_loop(pvstate_t state)
continue;
}
state->wait = 0;
state->control.wait = 0;
/*
* Reset the timer offset counter now that data
@@ -277,7 +282,7 @@ int pv_main_loop(pvstate_t state)
*/
pv_sig_nopause();
pv_elapsedtime_read(&start_time);
pv_elapsedtime_zero(&(state->pv_sig_toffset));
pv_elapsedtime_zero(&(state->signal.toffset));
pv_sig_allowpause();
/*
@@ -285,7 +290,7 @@ int pv_main_loop(pvstate_t state)
* not immediately.
*/
pv_elapsedtime_copy(&next_update, &start_time);
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->interval));
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->control.interval));
}
/* Restart the loop if it's not time to update the display. */
@@ -293,7 +298,7 @@ int pv_main_loop(pvstate_t state)
continue;
}
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->interval));
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->control.interval));
/* Set the "next update" time to now, if it's in the past. */
if (pv_elapsedtime_compare(&next_update, &cur_time) < 0)
@@ -304,7 +309,7 @@ int pv_main_loop(pvstate_t state)
* started, plus the total time we spent stopped.
*/
memset(&init_time, 0, sizeof(init_time));
pv_elapsedtime_add(&init_time, &start_time, &(state->pv_sig_toffset));
pv_elapsedtime_add(&init_time, &start_time, &(state->signal.toffset));
/*
* Now get the effective elapsed transfer time - current
@@ -319,19 +324,19 @@ int pv_main_loop(pvstate_t state)
transferred_since_last = -1;
/* Resize the display, if a resize signal was received. */
if (1 == state->pv_sig_newsize) {
if (1 == state->flag.terminal_resized) {
unsigned int new_width, new_height;
state->pv_sig_newsize = 0;
state->flag.terminal_resized = 0;
new_width = state->width;
new_height = state->height;
new_width = state->control.width;
new_height = state->control.height;
pv_screensize(&new_width, &new_height);
if (!state->width_set_manually)
state->width = new_width;
if (!state->height_set_manually)
state->height = new_height;
if (!state->control.width_set_manually)
state->control.width = new_width;
if (!state->control.height_set_manually)
state->control.height = new_height;
}
pv_display(state, elapsed_seconds, transferred_since_last, total_written);
@@ -339,27 +344,27 @@ int pv_main_loop(pvstate_t state)
transferred_since_last = 0;
}
if (state->cursor) {
if (state->control.cursor) {
pv_crs_fini(state);
} else {
if ((!state->numeric) && (!state->no_display)
&& (state->display_visible))
if ((!state->control.numeric) && (!state->control.no_display)
&& (state->display.display_visible))
pv_write_retry(STDERR_FILENO, "\n", 1);
}
if (1 == state->pv_sig_abort)
state->exit_status |= 32;
if (1 == state->flag.trigger_exit)
state->status.exit_status |= 32;
if (fd >= 0)
(void) close(fd);
return state->exit_status;
return state->status.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
* Watch the progress of file descriptor state->control.watch_fd in process
* state->control.watch_pid and show details about the transfer on standard error
* according to the given options.
*
* Returns nonzero on error.
@@ -375,28 +380,32 @@ int pv_watchfd_loop(pvstate_t state)
int rc;
memset(&info, 0, sizeof(info));
info.watch_pid = state->watch_pid;
info.watch_fd = state->watch_fd;
info.watch_pid = state->control.watch_pid;
info.watch_fd = state->control.watch_fd;
rc = pv_watchfd_info(state, &info, false);
if (0 != rc) {
state->exit_status |= 2;
return state->exit_status;
state->status.exit_status |= 2;
/*@-compdestroy@ */
return state->status.exit_status;
/*@+compdestroy@ */
/* splint: no leak of info.state as it is unused so far. */
}
/*
* Use a size if one was passed, otherwise use the total size
* calculated.
*/
if (0 >= state->size)
state->size = info.size;
if (0 >= state->control.size)
state->control.size = info.size;
if (state->size < 1) {
if (state->control.size < 1) {
char *fmt;
while (NULL != (fmt = strstr(state->default_format, "%e"))) {
while (NULL != (fmt = strstr(state->control.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;
/* strlen-1 here to include trailing \0 */
memmove(fmt, fmt + 2, strlen(fmt) - 1); /* flawfinder: ignore */
/* flawfinder: default_format is always \0 terminated */
state->flag.reparse_display = 1;
}
}
@@ -410,7 +419,7 @@ int pv_watchfd_loop(pvstate_t state)
pv_elapsedtime_copy(&(info.start_time), &cur_time);
pv_elapsedtime_copy(&next_remotecheck, &cur_time);
pv_elapsedtime_copy(&next_update, &cur_time);
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->interval));
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->control.interval));
ended = false;
total_written = 0;
@@ -426,7 +435,7 @@ int pv_watchfd_loop(pvstate_t state)
pv_elapsedtime_add_nsec(&next_remotecheck, REMOTE_INTERVAL);
}
if (1 == state->pv_sig_abort)
if (1 == state->flag.trigger_exit)
break;
position_now = pv_watchfd_position(&info);
@@ -437,7 +446,7 @@ int pv_watchfd_loop(pvstate_t state)
transferred_since_last += position_now - total_written;
total_written = position_now;
if (first_check) {
state->initial_offset = position_now;
state->display.initial_offset = position_now;
first_check = false;
}
}
@@ -458,7 +467,7 @@ int pv_watchfd_loop(pvstate_t state)
continue;
}
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->interval));
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->control.interval));
/* Set the "next update" time to now, if it's in the past. */
if (pv_elapsedtime_compare(&next_update, &cur_time) < 0)
@@ -468,7 +477,7 @@ int pv_watchfd_loop(pvstate_t state)
* Calculate the effective start time: the time we actually
* started, plus the total time we spent stopped.
*/
pv_elapsedtime_add(&init_time, &(info.start_time), &(state->pv_sig_toffset));
pv_elapsedtime_add(&init_time, &(info.start_time), &(state->signal.toffset));
/*
* Now get the effective elapsed transfer time - current
@@ -482,19 +491,19 @@ int pv_watchfd_loop(pvstate_t state)
transferred_since_last = -1;
/* Resize the display, if a resize signal was received. */
if (1 == state->pv_sig_newsize) {
if (1 == state->flag.terminal_resized) {
unsigned int new_width, new_height;
state->pv_sig_newsize = 0;
state->flag.terminal_resized = 0;
new_width = state->width;
new_height = state->height;
new_width = state->control.width;
new_height = state->control.height;
pv_screensize(&new_width, &new_height);
if (!state->width_set_manually)
state->width = new_width;
if (!state->height_set_manually)
state->height = new_height;
if (!state->control.width_set_manually)
state->control.width = new_width;
if (!state->control.height_set_manually)
state->control.height = new_height;
}
pv_display(state, elapsed_seconds, transferred_since_last, total_written);
@@ -502,30 +511,44 @@ int pv_watchfd_loop(pvstate_t state)
transferred_since_last = 0;
}
if (!state->numeric)
if (!state->control.numeric)
pv_write_retry(STDERR_FILENO, "\n", 1);
if (1 == state->pv_sig_abort)
state->exit_status |= 32;
if (1 == state->flag.trigger_exit)
state->status.exit_status |= 32;
return state->exit_status;
/*
* Free the state structure specific to this file descriptor.
* Uunused so far - this is so that in future we could watch
* multiple file descriptors similar to pv_watchpid_loop().
*/
if (NULL != info.state) {
pv_state_free(info.state);
info.state = NULL;
}
/*@-compdestroy@ */
return state->status.exit_status;
/*@+compdestroy@ */
/* splint: no leak of info.state as we just freed it above. */
}
/*
* Watch the progress of all file descriptors in process state->watch_pid
* Watch the progress of all file descriptors in process state->control.watch_pid
* and show details about the transfers on standard error according to the
* given options.
*
* Replaces format_string in "state" so that starts with "%N " if it doesn't
* already do so.
*
* 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];
char new_format_string[512]; /* flawfinder: ignore */
struct pvwatchfd_s *info_array = NULL;
struct pvstate_s *state_array = NULL;
int array_length = 0;
int fd_to_idx[FD_SETSIZE];
struct timespec next_update, cur_time;
@@ -533,37 +556,40 @@ int pv_watchpid_loop(pvstate_t state)
int prev_displayed_lines, blank_lines;
bool first_pass = true;
/*
* flawfinder rationale (new_format_string): zeroed with memset(),
* only written to with pv_snprintf() which checks boundaries, and
* explicitly terminated with \0.
*/
/*
* 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;
if (kill(state->control.watch_pid, 0) != 0) {
pv_error(state, "%s %u: %s", _("pid"), state->control.watch_pid, strerror(errno));
state->status.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;
original_format_string =
NULL != state->control.format_string ? state->control.format_string : state->control.default_format;
memset(new_format_string, 0, sizeof(new_format_string));
if (NULL == strstr(original_format_string, "%N")) {
if (NULL == original_format_string) {
(void) pv_snprintf(new_format_string, sizeof(new_format_string), "%%N");
} else if (NULL == strstr(original_format_string, "%N")) {
(void) pv_snprintf(new_format_string, sizeof(new_format_string), "%%N %s", original_format_string);
} else {
(void) pv_snprintf(new_format_string, sizeof(new_format_string), "%s", original_format_string);
}
new_format_string[sizeof(new_format_string) - 1] = '\0';
state_copy.format_string = NULL;
(void) pv_snprintf(state_copy.default_format, PV_SIZEOF_DEFAULT_FORMAT, "%.510s", new_format_string);
state_copy.default_format[PV_SIZEOF_DEFAULT_FORMAT - 1] = '\0';
if (NULL != state->control.format_string)
free(state->control.format_string);
state->control.format_string = pv_strdup(new_format_string);
/*
* Get things ready for the main loop.
@@ -574,7 +600,7 @@ int pv_watchpid_loop(pvstate_t state)
pv_elapsedtime_read(&cur_time);
pv_elapsedtime_copy(&next_update, &cur_time);
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->interval));
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->control.interval));
for (idx = 0; idx < FD_SETSIZE; idx++) {
fd_to_idx[idx] = -1;
@@ -585,19 +611,17 @@ int pv_watchpid_loop(pvstate_t state)
while (true) {
int rc, fd, displayed_lines;
if (1 == state->pv_sig_abort)
if (1 == state->flag.trigger_exit)
break;
pv_elapsedtime_read(&cur_time);
if (kill(state->watch_pid, 0) != 0) {
if (kill(state->control.watch_pid, 0) != 0) {
if (first_pass) {
pv_error(state, "%s %u: %s", _("pid"), state->watch_pid, strerror(errno));
state->exit_status |= 2;
pv_error(state, "%s %u: %s", _("pid"), state->control.watch_pid, strerror(errno));
state->status.exit_status |= 2;
if (NULL != info_array)
free(info_array);
if (NULL != state_array)
free(state_array);
return 2;
}
break;
@@ -612,34 +636,33 @@ int pv_watchpid_loop(pvstate_t state)
continue;
}
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->interval));
pv_elapsedtime_add_nsec(&next_update, (long long) (1000000000.0 * state->control.interval));
/* Set the "next update" time to now, if it's in the past. */
if (pv_elapsedtime_compare(&next_update, &cur_time) < 0)
pv_elapsedtime_copy(&next_update, &cur_time);
/* Resize the display, if a resize signal was received. */
if (1 == 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;
if (1 == state->flag.terminal_resized) {
state->flag.terminal_resized = 0;
pv_screensize(&(state->control.width), &(state->control.height));
for (idx = 0; NULL != info_array && idx < array_length; idx++) {
if (NULL == info_array[idx].state)
continue;
info_array[idx].state->control.width = state->control.width;
info_array[idx].state->control.height = state->control.height;
pv_watchpid_setname(state, &(info_array[idx]));
state_array[idx].reparse_display = 1;
info_array[idx].state->flag.reparse_display = 1;
}
}
rc = pv_watchpid_scanfds(state, &state_copy,
state->watch_pid, &array_length, &info_array, &state_array, fd_to_idx);
rc = pv_watchpid_scanfds(state, state->control.watch_pid, &array_length, &info_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;
pv_error(state, "%s %u: %s", _("pid"), state->control.watch_pid, strerror(errno));
state->status.exit_status |= 2;
if (NULL != info_array)
free(info_array);
if (NULL != state_array)
free(state_array);
return 2;
}
break;
@@ -648,12 +671,12 @@ int pv_watchpid_loop(pvstate_t state)
first_pass = false;
displayed_lines = 0;
for (fd = 0; fd < FD_SETSIZE; fd++) {
for (fd = 0; fd < FD_SETSIZE && NULL != info_array; fd++) {
off_t position_now, transferred_since_last;
struct timespec init_time, transfer_elapsed;
long double elapsed_seconds;
if (displayed_lines >= (int) (state->height))
if (displayed_lines >= (int) (state->control.height))
break;
idx = fd_to_idx[fd];
@@ -674,6 +697,11 @@ int pv_watchpid_loop(pvstate_t state)
continue;
}
if (NULL == info_array[idx].state) {
debug("%s %d: %s", "fd", fd, "null state - skipping");
continue;
}
/*
* Displayable fd - display, or remove if changed
*/
@@ -697,7 +725,7 @@ int pv_watchpid_loop(pvstate_t state)
* Calculate the effective start time: the time we actually
* started, plus the total time we spent stopped.
*/
pv_elapsedtime_add(&init_time, &(info_array[idx].start_time), &(state->pv_sig_toffset));
pv_elapsedtime_add(&init_time, &(info_array[idx].start_time), &(state->signal.toffset));
/*
* Now get the effective elapsed transfer time - current
@@ -715,7 +743,7 @@ int pv_watchpid_loop(pvstate_t state)
debug("%s %d [%d]: %Lf / %Ld / %Ld", "fd", fd, idx, elapsed_seconds, transferred_since_last,
position_now);
pv_display(&(state_array[idx]), elapsed_seconds, transferred_since_last, position_now);
pv_display(info_array[idx].state, elapsed_seconds, transferred_since_last, position_now);
displayed_lines++;
}
@@ -733,7 +761,7 @@ int pv_watchpid_loop(pvstate_t state)
unsigned int x;
if (displayed_lines > 0)
pv_write_retry(STDERR_FILENO, "\n", 1);
for (x = 0; x < state->width; x++)
for (x = 0; x < state->control.width; x++)
pv_write_retry(STDERR_FILENO, " ", 1);
pv_write_retry(STDERR_FILENO, "\r", 1);
blank_lines--;
@@ -754,7 +782,7 @@ int pv_watchpid_loop(pvstate_t state)
blank_lines = prev_displayed_lines;
while (blank_lines > 0) {
unsigned int x;
for (x = 0; x < state->width; x++)
for (x = 0; x < state->control.width; x++)
pv_write_retry(STDERR_FILENO, " ", 1);
pv_write_retry(STDERR_FILENO, "\r", 1);
blank_lines--;
@@ -766,10 +794,18 @@ int pv_watchpid_loop(pvstate_t state)
prev_displayed_lines--;
}
/*
* Free the per-fd state.
*/
for (idx = 0; NULL != info_array && idx < array_length; idx++) {
if (NULL == info_array[idx].state)
continue;
pv_state_free(info_array[idx].state);
info_array[idx].state = NULL;
}
if (NULL != info_array)
free(info_array);
if (NULL != state_array)
free(state_array);
return 0;
}
+39 -15
View File
@@ -1,7 +1,7 @@
/*
* Functions for converting strings to numbers.
*
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
@@ -27,14 +27,16 @@ static bool pv__isdigit(char c)
* Return the numeric value of "str", as an off_t, where "str" is expected
* to be a sequence of digits (without a thousands separator), possibly with
* a fractional part, optionally followed by a units suffix such as "K" for
* kibibytes.
* kibibytes. If "decimal_units" is true, suffixes are interpreted as
* multiples of 1000, rather than multiples of 1024.
*/
off_t pv_getnum_size(const char *str)
off_t pv_getnum_size(const char *str, bool decimal_units)
{
off_t integral_part = 0;
off_t fractional_part = 0;
unsigned int fractional_divisor = 1;
unsigned int shift = 0;
unsigned int binary_shift = 0;
off_t decimal_multiplier = 0;
if (NULL == str)
return (off_t) 0;
@@ -74,7 +76,7 @@ off_t pv_getnum_size(const char *str)
/*
* Parse any units given (K=KiB=*1024, M=MiB=1024KiB, G=GiB=1024MiB,
* T=TiB=1024GiB).
* T=TiB=1024GiB; replace 1024 with 1000 if decimal_units is true).
*/
if (str[0] != '\0') {
/* Skip any spaces or tabs after the digits. */
@@ -83,19 +85,23 @@ off_t pv_getnum_size(const char *str)
switch (str[0]) {
case 'k':
case 'K':
shift = 10;
binary_shift = 10;
decimal_multiplier = 1000;
break;
case 'm':
case 'M':
shift = 20;
binary_shift = 20;
decimal_multiplier = 1000000;
break;
case 'g':
case 'G':
shift = 30;
binary_shift = 30;
decimal_multiplier = 1000000000;
break;
case 't':
case 'T':
shift = 40;
binary_shift = 40;
decimal_multiplier = 1000000000000;
break;
default:
break;
@@ -103,14 +109,24 @@ off_t pv_getnum_size(const char *str)
}
/*
* Binary left-shift the supplied number by "shift" times, i.e.
* If decimal_units is false, zero decimal_multiplier; if true, zero
* binary_shift. This is so we only do one or the other.
*/
if (decimal_units) {
binary_shift = 0;
} else {
decimal_multiplier = 0;
}
/*
* Binary left-shift the supplied number by "binary_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) {
while (binary_shift > 0) {
unsigned int shiftby;
shiftby = shift;
shiftby = binary_shift;
if (shiftby > 30)
shiftby = 30;
@@ -124,7 +140,15 @@ off_t pv_getnum_size(const char *str)
fractional_part = (off_t) (fractional_part << shiftby);
/*@+shiftimplementation@ */
shift -= shiftby;
binary_shift -= shiftby;
}
/*
* Multiply the supplied number by the decimal multiplier.
*/
if (decimal_multiplier > 0) {
integral_part = integral_part * decimal_multiplier;
fractional_part = fractional_part * decimal_multiplier;
}
/*
@@ -183,9 +207,9 @@ double pv_getnum_interval(const char *str)
* rules as pv_getnum_size(), expecting "str" to express a value to be used
* as a count (such as number of screen columns, or size of a buffer).
*/
unsigned int pv_getnum_count(const char *str)
unsigned int pv_getnum_count(const char *str, bool decimal_units)
{
return (unsigned int) pv_getnum_size(str);
return (unsigned int) pv_getnum_size(str, decimal_units);
}
+127 -45
View File
@@ -1,7 +1,7 @@
/*
* Signal handling functions.
*
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
@@ -45,7 +45,7 @@ static void pv_sig_ensure_tty_tostop()
if (0 == (terminal_attributes.c_lflag & TOSTOP)) {
terminal_attributes.c_lflag |= TOSTOP;
if (0 == tcsetattr(STDERR_FILENO, TCSANOW, &terminal_attributes)) {
pv_sig_state->pv_tty_tostop_added = true;
pv_sig_state->signal.pv_tty_tostop_added = true;
debug("%s", "set terminal TOSTOP attribute");
} else {
debug("%s: %s", "failed to set terminal TOSTOP attribute", strerror(errno));
@@ -55,8 +55,9 @@ static void pv_sig_ensure_tty_tostop()
* In "-c" mode with IPC, make all "pv -c" instances aware
* that we set TOSTOP, so the last one can clear it on exit.
*/
if (pv_sig_state->cursor && (NULL != pv_sig_state->crs_shared) && (!pv_sig_state->crs_noipc)) {
pv_sig_state->crs_shared->tty_tostop_added = true;
if (pv_sig_state->control.cursor && (NULL != pv_sig_state->cursor.shared)
&& (!pv_sig_state->cursor.noipc)) {
pv_sig_state->cursor.shared->tty_tostop_added = true;
}
#endif
}
@@ -92,8 +93,8 @@ static void pv_sig_ttou( /*@unused@ */ __attribute__((unused))
* stderr while backgrounded.
*/
if (-1 == pv_sig_state->pv_sig_old_stderr)
pv_sig_state->pv_sig_old_stderr = dup(STDERR_FILENO);
if (-1 == pv_sig_state->signal.old_stderr)
pv_sig_state->signal.old_stderr = dup(STDERR_FILENO);
if (dup2(fd, STDERR_FILENO) < 0) {
debug("%s: %s", "failed to replace stderr", strerror(errno));
@@ -114,7 +115,7 @@ static void pv_sig_tstp( /*@unused@ */ __attribute__((unused))
{
if (NULL == pv_sig_state)
return;
pv_elapsedtime_read(&(pv_sig_state->pv_sig_tstp_time));
pv_elapsedtime_read(&(pv_sig_state->signal.tstp_time));
if (0 != raise(SIGSTOP)) {
debug("%s: %s", "raise", strerror(errno));
}
@@ -135,13 +136,13 @@ static void pv_sig_cont( /*@unused@ */ __attribute__((unused))
if (NULL == pv_sig_state)
return;
pv_sig_state->pv_sig_newsize = 1;
pv_sig_state->flag.terminal_resized = 1;
/*
* We can only make the time adjustments if this SIGCONT followed a
* SIGTSTP such that we have a stop time.
*/
if (0 != pv_sig_state->pv_sig_tstp_time.tv_sec) {
if (0 != pv_sig_state->signal.tstp_time.tv_sec) {
memset(&current_time, 0, sizeof(current_time));
memset(&time_spent_stopped, 0, sizeof(time_spent_stopped));
@@ -149,27 +150,27 @@ static void pv_sig_cont( /*@unused@ */ __attribute__((unused))
pv_elapsedtime_read(&current_time);
/* time spent stopped = current time - time SIGTSTP received */
pv_elapsedtime_subtract(&time_spent_stopped, &current_time, &(pv_sig_state->pv_sig_tstp_time));
pv_elapsedtime_subtract(&time_spent_stopped, &current_time, &(pv_sig_state->signal.tstp_time));
/* add time spent stopped the total stopped-time count */
pv_elapsedtime_add(&(pv_sig_state->pv_sig_toffset), &(pv_sig_state->pv_sig_toffset),
pv_elapsedtime_add(&(pv_sig_state->signal.toffset), &(pv_sig_state->signal.toffset),
&time_spent_stopped);
/* reset the SIGTSTP receipt time */
pv_elapsedtime_zero(&(pv_sig_state->pv_sig_tstp_time));
pv_elapsedtime_zero(&(pv_sig_state->signal.tstp_time));
}
/*
* Restore the old stderr, if we had replaced it.
*/
if (pv_sig_state->pv_sig_old_stderr != -1) {
if (dup2(pv_sig_state->pv_sig_old_stderr, STDERR_FILENO) < 0) {
if (pv_sig_state->signal.old_stderr != -1) {
if (dup2(pv_sig_state->signal.old_stderr, STDERR_FILENO) < 0) {
debug("%s: %s", "failed to restore old stderr", strerror(errno));
}
if (0 != close(pv_sig_state->pv_sig_old_stderr)) {
if (0 != close(pv_sig_state->signal.old_stderr)) {
debug("%s: %s", "failed to close duplicate old stderr", strerror(errno));
}
pv_sig_state->pv_sig_old_stderr = -1;
pv_sig_state->signal.old_stderr = -1;
}
pv_sig_ensure_tty_tostop();
@@ -189,7 +190,7 @@ static void pv_sig_winch( /*@unused@ */ __attribute__((unused))
{
if (NULL == pv_sig_state)
return;
pv_sig_state->pv_sig_newsize = 1;
pv_sig_state->flag.terminal_resized = 1;
}
#endif
@@ -202,7 +203,60 @@ static void pv_sig_term( /*@unused@ */ __attribute__((unused))
{
if (NULL == pv_sig_state)
return;
pv_sig_state->pv_sig_abort = 1;
pv_sig_state->flag.trigger_exit = 1;
}
#ifdef SA_SIGINFO
/*
* Handle a SIGUSR2 by setting a flag to say we received it, after recording
* the sending PID.
*/
static void pv_sig_usr2( /*@unused@ */ __attribute__((unused))
int sig, siginfo_t * info, /*@unused@ */ __attribute__((unused))
void *ucontext)
{
if (NULL == pv_sig_state)
return;
if (NULL == info)
return;
pv_sig_state->signal.rxusr2 = 1;
pv_sig_state->signal.sender = info->si_pid;
}
/*
* Return true if a SIGUSR2 signal has been received since the last time
* this function was called, populating *pid with the sending PID if so.
*/
bool pv_sigusr2_received(pvstate_t state, pid_t * pid)
{
if (NULL == state)
return false;
if (0 == state->signal.rxusr2)
return false;
if (NULL != pid)
*pid = state->signal.sender;
state->signal.rxusr2 = 0;
return true;
}
#endif
/*
* Handle alarm signals by doing nothing.
*
* Note that we have to use a signal handler like this, instead of using
* SIG_IGN, because if we ignore the signal entirely, it does nothing,
* including not interrupting blocking write() calls - which is what we're
* using alarm signals for in the first place.
*/
static void pv_sig_alrm( /*@unused@ */ __attribute__((unused))
int s)
{
debug("%s", "SIGALRM received");
/* Do nothing. */
}
@@ -224,9 +278,9 @@ void pv_sig_init(pvstate_t state)
pv_sig_state = state;
pv_sig_state->pv_sig_old_stderr = -1;
pv_elapsedtime_zero(&(pv_sig_state->pv_sig_tstp_time));
pv_elapsedtime_zero(&(pv_sig_state->pv_sig_toffset));
pv_sig_state->signal.old_stderr = -1;
pv_elapsedtime_zero(&(pv_sig_state->signal.tstp_time));
pv_elapsedtime_zero(&(pv_sig_state->signal.toffset));
/*
* Note that we cast all sigemptyset() and sigaction() return values
@@ -241,7 +295,7 @@ void pv_sig_init(pvstate_t state)
sa.sa_handler = SIG_IGN;
(void) sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;
(void) sigaction(SIGPIPE, &sa, &(pv_sig_state->pv_sig_old_sigpipe));
(void) sigaction(SIGPIPE, &sa, &(pv_sig_state->signal.old_sigpipe));
/*
* Handle SIGTTOU by continuing with output switched off, so that we
@@ -250,7 +304,7 @@ void pv_sig_init(pvstate_t state)
sa.sa_handler = pv_sig_ttou;
(void) sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;
(void) sigaction(SIGTTOU, &sa, &(pv_sig_state->pv_sig_old_sigttou));
(void) sigaction(SIGTTOU, &sa, &(pv_sig_state->signal.old_sigttou));
/*
* Handle SIGTSTP by storing the time the signal happened for later
@@ -259,7 +313,7 @@ void pv_sig_init(pvstate_t state)
sa.sa_handler = pv_sig_tstp;
(void) sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;
(void) sigaction(SIGTSTP, &sa, &(pv_sig_state->pv_sig_old_sigtstp));
(void) sigaction(SIGTSTP, &sa, &(pv_sig_state->signal.old_sigtstp));
/*
* Handle SIGCONT by adding the elapsed time since the last SIGTSTP
@@ -269,7 +323,7 @@ void pv_sig_init(pvstate_t state)
sa.sa_handler = pv_sig_cont;
(void) sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;
(void) sigaction(SIGCONT, &sa, &(pv_sig_state->pv_sig_old_sigcont));
(void) sigaction(SIGCONT, &sa, &(pv_sig_state->signal.old_sigcont));
/*
* Handle SIGWINCH by setting a flag to let the main loop know it
@@ -279,7 +333,7 @@ void pv_sig_init(pvstate_t state)
sa.sa_handler = pv_sig_winch;
(void) sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;
(void) sigaction(SIGWINCH, &sa, &(pv_sig_state->pv_sig_old_sigwinch));
(void) sigaction(SIGWINCH, &sa, &(pv_sig_state->signal.old_sigwinch));
#endif
/*
@@ -289,17 +343,32 @@ void pv_sig_init(pvstate_t state)
sa.sa_handler = pv_sig_term;
(void) sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;
(void) sigaction(SIGINT, &sa, &(pv_sig_state->pv_sig_old_sigint));
(void) sigaction(SIGINT, &sa, &(pv_sig_state->signal.old_sigint));
sa.sa_handler = pv_sig_term;
(void) sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;
(void) sigaction(SIGHUP, &sa, &(pv_sig_state->pv_sig_old_sighup));
(void) sigaction(SIGHUP, &sa, &(pv_sig_state->signal.old_sighup));
sa.sa_handler = pv_sig_term;
(void) sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;
(void) sigaction(SIGTERM, &sa, &(pv_sig_state->pv_sig_old_sigterm));
(void) sigaction(SIGTERM, &sa, &(pv_sig_state->signal.old_sigterm));
#ifdef SA_SIGINFO
/*
* Handle SIGUSR2 by setting a flag to say the signal has been
* received, and storing the sending process's PID.
*/
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = pv_sig_usr2;
(void) sigemptyset(&(sa.sa_mask));
/*@-unrecog@ *//* splint doesn't know about SA_SIGINFO */
sa.sa_flags = SA_SIGINFO;
/*@+unrecog@ */
(void) sigaction(SIGUSR2, &sa, &(pv_sig_state->signal.old_sigusr2));
memset(&sa, 0, sizeof(sa));
#endif
/*
* Ensure that the TOSTOP terminal attribute is set, so that a
@@ -307,6 +376,15 @@ void pv_sig_init(pvstate_t state)
* while backgrounded (see the SIGTTOU handler above).
*/
pv_sig_ensure_tty_tostop();
/*
* Handle SIGALRM by doing nothing, so we can use alarms or interval
* timers to interrupt blocking writes (returning EINTR).
*/
sa.sa_handler = pv_sig_alrm;
(void) sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;
(void) sigaction(SIGALRM, &sa, &(pv_sig_state->signal.old_sigalrm));
}
@@ -323,20 +401,24 @@ void pv_sig_fini( /*@unused@ */ __attribute__((unused)) pvstate_t state)
if (NULL == pv_sig_state)
return;
(void) sigaction(SIGPIPE, &(pv_sig_state->pv_sig_old_sigpipe), NULL);
(void) sigaction(SIGTTOU, &(pv_sig_state->pv_sig_old_sigttou), NULL);
(void) sigaction(SIGTSTP, &(pv_sig_state->pv_sig_old_sigtstp), NULL);
(void) sigaction(SIGCONT, &(pv_sig_state->pv_sig_old_sigcont), NULL);
(void) sigaction(SIGPIPE, &(pv_sig_state->signal.old_sigpipe), NULL);
(void) sigaction(SIGTTOU, &(pv_sig_state->signal.old_sigttou), NULL);
(void) sigaction(SIGTSTP, &(pv_sig_state->signal.old_sigtstp), NULL);
(void) sigaction(SIGCONT, &(pv_sig_state->signal.old_sigcont), NULL);
#ifdef SIGWINCH
(void) sigaction(SIGWINCH, &(pv_sig_state->pv_sig_old_sigwinch), NULL);
(void) sigaction(SIGWINCH, &(pv_sig_state->signal.old_sigwinch), NULL);
#endif
(void) sigaction(SIGINT, &(pv_sig_state->pv_sig_old_sigint), NULL);
(void) sigaction(SIGHUP, &(pv_sig_state->pv_sig_old_sighup), NULL);
(void) sigaction(SIGTERM, &(pv_sig_state->pv_sig_old_sigterm), NULL);
(void) sigaction(SIGINT, &(pv_sig_state->signal.old_sigint), NULL);
(void) sigaction(SIGHUP, &(pv_sig_state->signal.old_sighup), NULL);
(void) sigaction(SIGTERM, &(pv_sig_state->signal.old_sigterm), NULL);
#ifdef SA_SIGINFO
(void) sigaction(SIGUSR2, &(pv_sig_state->signal.old_sigusr2), NULL);
#endif
(void) sigaction(SIGALRM, &(pv_sig_state->signal.old_sigalrm), NULL);
need_to_clear_tostop = pv_sig_state->pv_tty_tostop_added;
need_to_clear_tostop = pv_sig_state->signal.pv_tty_tostop_added;
if (pv_sig_state->cursor) {
if (pv_sig_state->control.cursor) {
#ifdef HAVE_IPC
/*
* We won't clear TOSTOP if other "pv -c" instances
@@ -345,7 +427,7 @@ void pv_sig_fini( /*@unused@ */ __attribute__((unused)) pvstate_t state)
* TODO: we need a better way to determine if we're the last
* "pv" left.
*/
if (pv_sig_state->cursor && pv_sig_state->crs_pvcount > 1) {
if (pv_sig_state->control.cursor && pv_sig_state->cursor.pvcount > 1) {
need_to_clear_tostop = false;
}
#else /* !HAVE_IPC */
@@ -375,7 +457,7 @@ void pv_sig_fini( /*@unused@ */ __attribute__((unused)) pvstate_t state)
}
}
pv_sig_state->pv_tty_tostop_added = false;
pv_sig_state->signal.pv_tty_tostop_added = false;
}
}
@@ -440,18 +522,18 @@ void pv_sig_checkbg(void)
next_check = time(NULL) + 1;
if (-1 == pv_sig_state->pv_sig_old_stderr)
if (-1 == pv_sig_state->signal.old_stderr)
return;
if (dup2(pv_sig_state->pv_sig_old_stderr, STDERR_FILENO) < 0) {
if (dup2(pv_sig_state->signal.old_stderr, STDERR_FILENO) < 0) {
debug("%s: %s", "failed to restore old stderr", strerror(errno));
}
if (0 != close(pv_sig_state->pv_sig_old_stderr)) {
if (0 != close(pv_sig_state->signal.old_stderr)) {
debug("%s: %s", "failed to close duplicate old stderr", strerror(errno));
}
pv_sig_state->pv_sig_old_stderr = -1;
pv_sig_state->signal.old_stderr = -1;
pv_sig_ensure_tty_tostop();
#ifdef HAVE_IPC
+118 -111
View File
@@ -1,7 +1,7 @@
/*
* State management functions.
*
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
@@ -20,26 +20,26 @@
/* alloc / realloc history buffer */
static void pv_alloc_history(pvstate_t state)
{
if (NULL != state->history)
free(state->history);
state->history = NULL;
if (NULL != state->display.history)
free(state->display.history);
state->display.history = NULL;
state->history = calloc((size_t) (state->history_len), sizeof(state->history[0]));
if (NULL == state->history) {
state->display.history = calloc((size_t) (state->display.history_len), sizeof(state->display.history[0]));
if (NULL == state->display.history) {
/*@-mustfreefresh@ */
/*
* splint note: the gettext calls made by _() cause memory
* leak warnings, but in this case it's unavoidable, and
* mitigated by the fact we only translate each string once.
*/
fprintf(stderr, "%s: %s: %s\n", state->program_name,
fprintf(stderr, "%s: %s: %s\n", state->status.program_name,
_("history structure allocation failed"), strerror(errno));
/*@+mustfreefresh@ */
return;
}
state->history_first = state->history_last = 0;
state->history[0].elapsed_sec = 0.0; /* to be safe, memset() not recommended for doubles */
state->display.history_first = state->display.history_last = 0;
state->display.history[0].elapsed_sec = 0.0; /* to be safe, memset() not recommended for doubles */
}
/*
@@ -55,44 +55,44 @@ pvstate_t pv_state_alloc(const char *program_name)
memset(state, 0, sizeof(*state));
/* splint 3.1.2 thinks this is required for some reason. */
if (NULL != state->program_name) {
free(state->program_name);
if (NULL != state->status.program_name) {
free(state->status.program_name);
}
state->program_name = pv_strdup(program_name);
if (NULL == state->program_name) {
state->status.program_name = pv_strdup(program_name);
if (NULL == state->status.program_name) {
free(state);
return NULL;
}
state->watch_pid = 0;
state->watch_fd = -1;
state->control.watch_pid = 0;
state->control.watch_fd = -1;
#ifdef HAVE_IPC
state->crs_shmid = -1;
state->crs_pvcount = 1;
state->cursor.shmid = -1;
state->cursor.pvcount = 1;
#endif /* HAVE_IPC */
state->crs_lock_fd = -1;
state->cursor.lock_fd = -1;
state->reparse_display = 1;
state->current_input_file = -1;
state->flag.reparse_display = 1;
state->status.current_input_file = -1;
#ifdef HAVE_SPLICE
state->splice_failed_fd = -1;
state->transfer.splice_failed_fd = -1;
#endif /* HAVE_SPLICE */
state->display_visible = false;
state->display.display_visible = false;
/*
* Get the current working directory, if possible, as a base for
* showing relative filenames with --watchfd.
*/
if (NULL == getcwd(state->cwd, PV_SIZEOF_CWD - 1)) {
if (NULL == getcwd(state->status.cwd, PV_SIZEOF_CWD - 1)) {
/* failed - will always show full path */
state->cwd[0] = '\0';
state->status.cwd[0] = '\0';
}
if ('\0' == state->cwd[1]) {
if ('\0' == state->status.cwd[1]) {
/* CWD is root directory - always show full path */
state->cwd[0] = '\0';
state->status.cwd[0] = '\0';
}
state->cwd[PV_SIZEOF_CWD - 1] = '\0';
state->status.cwd[PV_SIZEOF_CWD - 1] = '\0';
return state;
}
@@ -106,45 +106,45 @@ void pv_state_free(pvstate_t state)
if (0 == state)
return;
if (NULL != state->program_name)
free(state->program_name);
state->program_name = NULL;
if (NULL != state->status.program_name)
free(state->status.program_name);
state->status.program_name = NULL;
if (NULL != state->display_buffer)
free(state->display_buffer);
state->display_buffer = NULL;
if (NULL != state->display.display_buffer)
free(state->display.display_buffer);
state->display.display_buffer = NULL;
if (NULL != state->name) {
free(state->name);
state->name = NULL;
if (NULL != state->control.name) {
free(state->control.name);
state->control.name = NULL;
}
if (NULL != state->format_string) {
free(state->format_string);
state->format_string = NULL;
if (NULL != state->control.format_string) {
free(state->control.format_string);
state->control.format_string = NULL;
}
/*@-keeptrans@ */
if (NULL != state->transfer_buffer)
free(state->transfer_buffer);
state->transfer_buffer = NULL;
if (NULL != state->transfer.transfer_buffer)
free(state->transfer.transfer_buffer);
state->transfer.transfer_buffer = NULL;
/*@+keeptrans@ */
/* splint - explicitly freeing this structure, so free() here is OK. */
if (NULL != state->history)
free(state->history);
state->history = NULL;
if (NULL != state->display.history)
free(state->display.history);
state->display.history = NULL;
if (NULL != state->input_files) {
if (NULL != state->files.filename) {
unsigned int file_idx;
for (file_idx = 0; file_idx < state->input_file_count; file_idx++) {
for (file_idx = 0; file_idx < state->files.file_count; file_idx++) {
/*@-unqualifiedtrans@ */
free(state->input_files[file_idx]);
free(state->files.filename[file_idx]);
/*@+unqualifiedtrans@ */
/* splint: see similar code below. */
}
free(state->input_files);
state->input_files = NULL;
free(state->files.filename);
state->files.filename = NULL;
}
free(state);
@@ -160,12 +160,12 @@ void pv_state_set_format(pvstate_t state, bool progress, bool timer, bool eta, b
const char *name)
{
#define PV_ADDFORMAT(x,y) if (x) { \
if (state->default_format[0] != '\0') \
(void) pv_strlcat(state->default_format, " ", sizeof(state->default_format)); \
(void) pv_strlcat(state->default_format, y, sizeof(state->default_format)); \
if (state->control.default_format[0] != '\0') \
(void) pv_strlcat(state->control.default_format, " ", sizeof(state->control.default_format)); \
(void) pv_strlcat(state->control.default_format, y, sizeof(state->control.default_format)); \
}
state->default_format[0] = '\0';
state->control.default_format[0] = '\0';
PV_ADDFORMAT(name, "%N");
PV_ADDFORMAT(bytes, "%b");
PV_ADDFORMAT(bufpercent, "%T");
@@ -187,171 +187,177 @@ void pv_state_set_format(pvstate_t state, bool progress, bool timer, bool eta, b
*/
}
if (NULL != state->name) {
free(state->name);
state->name = NULL;
if (NULL != state->control.name) {
free(state->control.name);
state->control.name = NULL;
}
if (NULL != name)
state->name = pv_strdup(name);
state->control.name = pv_strdup(name);
state->reparse_display = 1;
state->flag.reparse_display = 1;
}
void pv_state_force_set(pvstate_t state, bool val)
{
state->force = val;
state->control.force = val;
}
void pv_state_cursor_set(pvstate_t state, bool val)
{
state->cursor = val;
state->control.cursor = val;
}
void pv_state_numeric_set(pvstate_t state, bool val)
{
state->numeric = val;
state->control.numeric = val;
}
void pv_state_wait_set(pvstate_t state, bool val)
{
state->wait = val;
state->control.wait = val;
}
void pv_state_delay_start_set(pvstate_t state, double val)
{
state->delay_start = val;
state->control.delay_start = val;
}
void pv_state_linemode_set(pvstate_t state, bool val)
{
state->linemode = val;
state->control.linemode = val;
}
void pv_state_bits_set(pvstate_t state, bool bits)
{
state->bits = bits;
state->control.bits = bits;
}
void pv_state_decimal_units_set(pvstate_t state, bool decimal_units)
{
state->control.decimal_units = decimal_units;
}
void pv_state_null_terminated_lines_set(pvstate_t state, bool val)
{
state->null_terminated_lines = val;
state->control.null_terminated_lines = val;
}
void pv_state_no_display_set(pvstate_t state, bool val)
{
state->no_display = val;
state->control.no_display = val;
}
void pv_state_skip_errors_set(pvstate_t state, unsigned int val)
{
state->skip_errors = val;
state->control.skip_errors = val;
}
void pv_state_error_skip_block_set(pvstate_t state, off_t val)
{
state->error_skip_block = val;
state->control.error_skip_block = val;
}
void pv_state_stop_at_size_set(pvstate_t state, bool val)
{
state->stop_at_size = val;
state->control.stop_at_size = val;
}
void pv_state_sync_after_write_set(pvstate_t state, bool val)
{
state->sync_after_write = val;
state->control.sync_after_write = val;
}
void pv_state_direct_io_set(pvstate_t state, bool val)
{
state->direct_io = val;
state->direct_io_changed = true;
state->control.direct_io = val;
state->control.direct_io_changed = true;
}
void pv_state_discard_input_set(pvstate_t state, bool val)
{
state->discard_input = val;
state->control.discard_input = val;
}
void pv_state_rate_limit_set(pvstate_t state, off_t val)
{
state->rate_limit = val;
state->control.rate_limit = val;
}
void pv_state_target_buffer_size_set(pvstate_t state, size_t val)
{
state->target_buffer_size = val;
state->control.target_buffer_size = val;
}
void pv_state_no_splice_set(pvstate_t state, bool val)
{
state->no_splice = val;
state->control.no_splice = val;
}
void pv_state_size_set(pvstate_t state, off_t val)
{
state->size = val;
state->control.size = val;
}
void pv_state_interval_set(pvstate_t state, double val)
{
state->interval = val;
state->control.interval = val;
}
void pv_state_width_set(pvstate_t state, unsigned int val, bool was_set_manually)
{
state->width = val;
state->width_set_manually = was_set_manually;
state->control.width = val;
state->control.width_set_manually = was_set_manually;
}
void pv_state_height_set(pvstate_t state, unsigned int val, bool was_set_manually)
{
state->height = val;
state->height_set_manually = was_set_manually;
state->control.height = val;
state->control.height_set_manually = was_set_manually;
}
void pv_state_name_set(pvstate_t state, /*@null@ */ const char *val)
{
if (NULL != state->name) {
free(state->name);
state->name = NULL;
if (NULL != state->control.name) {
free(state->control.name);
state->control.name = NULL;
}
if (NULL != val)
state->name = pv_strdup(val);
state->control.name = pv_strdup(val);
}
void pv_state_format_string_set(pvstate_t state, /*@null@ */ const char *val)
{
if (NULL != state->format_string) {
free(state->format_string);
state->format_string = NULL;
if (NULL != state->control.format_string) {
free(state->control.format_string);
state->control.format_string = NULL;
}
if (NULL != val)
state->format_string = pv_strdup(val);
state->control.format_string = pv_strdup(val);
}
void pv_state_watch_pid_set(pvstate_t state, pid_t val)
{
state->watch_pid = val;
state->control.watch_pid = val;
}
void pv_state_watch_fd_set(pvstate_t state, int val)
{
state->watch_fd = val;
state->control.watch_fd = val;
}
void pv_state_average_rate_window_set(pvstate_t state, unsigned int val)
{
if (val < 1)
val = 1;
state->control.average_rate_window = val;
if (val >= 20) {
state->history_len = val / 5 + 1;
state->history_interval = 5;
state->display.history_len = (size_t) (val / 5 + 1);
state->display.history_interval = 5;
} else {
state->history_len = val + 1;
state->history_interval = 1;
state->display.history_len = (size_t) (val + 1);
state->display.history_interval = 1;
}
pv_alloc_history(state);
}
@@ -364,10 +370,10 @@ void pv_state_inputfiles(pvstate_t state, unsigned int input_file_count, const c
{
unsigned int file_idx;
if (NULL != state->input_files) {
for (file_idx = 0; file_idx < state->input_file_count; file_idx++) {
if (NULL != state->files.filename) {
for (file_idx = 0; file_idx < state->files.file_count; file_idx++) {
/*@-unqualifiedtrans@ */
free(state->input_files[file_idx]);
free(state->files.filename[file_idx]);
/*@+unqualifiedtrans@ */
/*
* TODO: find a way to tell splint the array
@@ -375,29 +381,30 @@ void pv_state_inputfiles(pvstate_t state, unsigned int input_file_count, const c
* array itself.
*/
}
free(state->input_files);
state->input_files = NULL;
state->input_file_count = 0;
free(state->files.filename);
state->files.filename = NULL;
state->files.file_count = 0;
}
state->input_files = calloc((size_t) (input_file_count + 1), sizeof(char *));
if (NULL == state->input_files) {
state->files.filename = calloc((size_t) (input_file_count + 1), sizeof(char *));
if (NULL == state->files.filename) {
/*@-mustfreefresh@ *//* see similar _() issue above */
fprintf(stderr, "%s: %s: %s\n", state->program_name, _("file list allocation failed"), strerror(errno));
fprintf(stderr, "%s: %s: %s\n", state->status.program_name, _("file list allocation failed"),
strerror(errno));
/*@+mustfreefresh@ */
return;
}
for (file_idx = 0; file_idx < input_file_count; file_idx++) {
/*@-nullstate@ */
state->input_files[file_idx] = pv_strdup(input_files[file_idx]);
if (NULL == state->input_files[file_idx]) {
state->files.filename[file_idx] = pv_strdup(input_files[file_idx]);
if (NULL == state->files.filename[file_idx]) {
/*@-mustfreefresh@ *//* see similar _() issue above */
fprintf(stderr, "%s: %s: %s\n", state->program_name,
fprintf(stderr, "%s: %s: %s\n", state->status.program_name,
_("file list allocation failed"), strerror(errno));
/*@+mustfreefresh@ */
return;
}
}
state->input_file_count = input_file_count;
state->files.file_count = input_file_count;
}
/*@+nullstate@*/
+30 -1
View File
@@ -1,7 +1,7 @@
/*
* Functions for portably managing strings.
*
* Copyright 2023 Andrew Wood
* Copyright 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
@@ -151,4 +151,33 @@ char *pv_strdup(const char *original)
return duplicate;
}
/*
* Return a pointer to the last matching character in the buffer, or NULL if
* not found.
*/
/*@null@ */
/*@temp@ */
void *pv_memrchr(const void *buffer, int match, size_t length)
{
#ifdef HAVE_MEMRCHR
/*@-unrecog @ *//* splint doesn't know of memrchr() */
return memrchr(buffer, match, length);
/*@+unrecog @ */
#else
unsigned char *ptr;
if (length < 1)
return NULL;
ptr = ((unsigned char *) buffer) + length - 1;
while (ptr >= (unsigned char *) buffer) {
if ((int) (ptr[0]) == match)
return (void *) ptr;
ptr--;
}
return NULL;
#endif
}
/* EOF */
+227 -168
View File
@@ -1,7 +1,7 @@
/*
* Functions for transferring data between file descriptors.
*
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
@@ -129,7 +129,14 @@ static ssize_t pv__transfer_read_repeated(int fd, void *buf, size_t count)
struct timespec cur_time, transfer_elapsed;
long double elapsed_seconds;
nread = read(fd, buf, (size_t) (count > MAX_READ_AT_ONCE ? MAX_READ_AT_ONCE : count));
nread = read(fd, buf, (size_t) (count > MAX_READ_AT_ONCE ? MAX_READ_AT_ONCE : count)); /* flawfinder: ignore */
/*
* flawfinder rationale: reads stop after "count" bytes, and
* we handle negative and zero results from read(), so it is
* bounded to the buffer size the caller told us to use.
*/
if (nread < 0)
return nread;
@@ -149,8 +156,8 @@ static ssize_t pv__transfer_read_repeated(int fd, void *buf, size_t count)
elapsed_seconds = pv_elapsedtime_seconds(&transfer_elapsed);
if (elapsed_seconds > TRANSFER_READ_TIMEOUT) {
debug("%s %d: %s (%lf %s)", "fd", fd,
"stopping read - timer expired", elapsed_seconds, "sec elapsed");
debug("%s %d: %s (%f %s)", "fd", fd,
"stopping read - timer expired", (double) elapsed_seconds, "sec elapsed");
return total_read;
}
@@ -224,8 +231,8 @@ static ssize_t pv__transfer_write_repeated(int fd, void *buf, size_t count, bool
if ((EINTR == errno) || (EAGAIN == errno)) {
/*
* Interrupted by a signal - probably our
* alarm() - so just return what we've
* written so far.
* alarm or interval timer - so just return
* what we've written so far.
*/
return total_written;
} else {
@@ -252,16 +259,17 @@ static ssize_t pv__transfer_write_repeated(int fd, void *buf, size_t count, bool
elapsed_seconds = pv_elapsedtime_seconds(&transfer_elapsed);
if (elapsed_seconds > TRANSFER_WRITE_TIMEOUT) {
debug("%s %d: %s (%lf %s)", "fd", fd,
"stopping write - timer expired", elapsed_seconds, "sec elapsed");
debug("%s %d: %s (%f %s)", "fd", fd,
"stopping write - timer expired", (double) elapsed_seconds, "sec 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 elapsed time check.
* again and rely on our alarm or interval timer to
* interrupt us if we run out of time - also on our elapsed
* time check.
*/
if (count > 0) {
debug("%s %d: %s (%ld %s, %ld %s)", "fd", fd,
@@ -286,21 +294,21 @@ static ssize_t pv__transfer_write_repeated(int fd, void *buf, size_t count, bool
* 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,
* in the input buffer. If state->control.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 true; if it
* failed, then state->splice_failed_fd is updated to the current fd so
* If splice() was successfully used, sets state->transfer.splice_used to true; if it
* failed, then state->transfer.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()
* Updates state->transfer.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
* (and it also adds the bytes to state->transfer.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.
* On read error, updates state->status.exit_status, and if allowed by
* state->control.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 true. If all data in the buffer has been written at this
@@ -317,18 +325,18 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou
#endif /* HAVE_SPLICE */
do_not_skip_errors = false;
if (0 == state->skip_errors)
if (0 == state->control.skip_errors)
do_not_skip_errors = true;
bytes_can_read = state->buffer_size - state->read_position;
bytes_can_read = state->transfer.buffer_size - state->transfer.read_position;
nread = 0;
#ifdef HAVE_SPLICE
state->splice_used = false;
if ((!state->linemode) && (!state->no_splice)
&& (fd != state->splice_failed_fd)
&& (0 == state->to_write)) {
if (state->rate_limit > 0 || allowed != 0) {
state->transfer.splice_used = false;
if ((!state->control.linemode) && (!state->control.no_splice)
&& (fd != state->transfer.splice_failed_fd)
&& (0 == state->transfer.to_write)) {
if (state->control.rate_limit > 0 || allowed != 0) {
bytes_to_splice = (size_t) allowed;
} else {
bytes_to_splice = bytes_can_read;
@@ -341,18 +349,18 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou
/*@+type@ */
/*@+nullpass@ */
state->splice_used = true;
state->transfer.splice_used = true;
if ((nread < 0) && (EINVAL == errno)) {
debug("%s %d: %s", "fd", fd, "splice failed with EINVAL - disabling");
state->splice_failed_fd = fd;
state->splice_used = false;
state->transfer.splice_failed_fd = fd;
state->transfer.splice_used = false;
/*
* Fall through to read() below.
*/
} else if (nread > 0) {
state->written = nread;
state->transfer.written = nread;
#ifdef HAVE_FDATASYNC
if (state->sync_after_write) {
if (state->control.sync_after_write) {
/*
* Ignore non IO errors, such as EBADFD (bad file
* descriptor), EINVAL (non syncable fd, such as a
@@ -373,14 +381,18 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou
/* nothing read yet - do nothing */
} else {
/* EOF might not really be EOF, it seems */
state->splice_used = false;
state->transfer.splice_used = false;
}
}
if (!state->splice_used) {
nread = pv__transfer_read_repeated(fd, state->transfer_buffer + state->read_position, bytes_can_read);
if (!state->transfer.splice_used) {
nread =
pv__transfer_read_repeated(fd, state->transfer.transfer_buffer + state->transfer.read_position,
bytes_can_read);
}
#else
nread = pv__transfer_read_repeated(fd, state->transfer_buffer + state->read_position, bytes_can_read);
nread =
pv__transfer_read_repeated(fd, state->transfer.transfer_buffer + state->transfer.read_position,
bytes_can_read);
#endif /* HAVE_SPLICE */
@@ -392,7 +404,7 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou
* move on to the next input file.
*/
*eof_in = true;
if (state->write_position >= state->read_position)
if (state->transfer.write_position >= state->transfer.read_position)
*eof_out = true;
return 1;
} else if (nread > 0) {
@@ -401,16 +413,16 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou
* the error counter and update our record of how much data
* we've got in the buffer.
*/
state->read_errors_in_a_row = 0;
state->transfer.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 (!state->splice_used)
state->read_position += nread;
if (!state->transfer.splice_used)
state->transfer.read_position += nread;
#else
state->read_position += nread;
state->transfer.read_position += nread;
#endif /* HAVE_SPLICE */
return 1;
}
@@ -434,8 +446,8 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou
* exit status, regardless of whether we're skipping errors, and
* increment the error counter.
*/
state->exit_status |= 16;
state->read_errors_in_a_row++;
state->status.exit_status |= 16;
state->transfer.read_errors_in_a_row++;
/*
* If we aren't skipping errors, show the error and pretend we
@@ -452,7 +464,7 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou
* TODO: investigate and fix the reason for this.
*/
*eof_in = true;
if (state->write_position >= state->read_position) {
if (state->transfer.write_position >= state->transfer.read_position) {
*eof_out = true;
}
return 1;
@@ -464,13 +476,13 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou
amount_skipped = -1;
if (!state->read_error_warning_shown) {
if (!state->transfer.read_error_warning_shown) {
/*@-compdef@ */
pv_error(state, "%s: %s: %s", pv_current_file_name(state), _("warning: read errors detected"),
strerror(errno));
/*@+compdef@ */
/* splint - see previous pv_current_file_name() call. */
state->read_error_warning_shown = true;
state->transfer.read_error_warning_shown = true;
}
orig_offset = (off_t) lseek(fd, 0, SEEK_CUR);
@@ -486,7 +498,7 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou
/*@+compdef@ */
/* splint - see previous pv_current_file_name() calls. */
*eof_in = true;
if (state->write_position >= state->read_position) {
if (state->transfer.write_position >= state->transfer.read_position) {
*eof_out = true;
}
return 1;
@@ -497,13 +509,13 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou
* otherwise start small and ramp up based on the number of errors
* in a row.
*/
if (state->error_skip_block > 0) {
amount_to_skip = state->error_skip_block;
if (state->control.error_skip_block > 0) {
amount_to_skip = state->control.error_skip_block;
} else {
if (state->read_errors_in_a_row < 10) {
amount_to_skip = (off_t) (state->read_errors_in_a_row < 5 ? 1 : 2);
} else if (state->read_errors_in_a_row < 20) {
unsigned int shift_by = (unsigned int) (state->read_errors_in_a_row - 10);
if (state->transfer.read_errors_in_a_row < 10) {
amount_to_skip = (off_t) (state->transfer.read_errors_in_a_row < 5 ? 1 : 2);
} else if (state->transfer.read_errors_in_a_row < 20) {
unsigned int shift_by = (unsigned int) (state->transfer.read_errors_in_a_row - 10);
amount_to_skip = (off_t) (1 << shift_by);
} else {
amount_to_skip = 512;
@@ -574,9 +586,9 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou
* of the transfer buffer, and update the buffer position.
*/
if (amount_skipped > 0) {
memset(state->transfer_buffer + state->read_position, 0, (size_t) amount_skipped);
state->read_position += amount_skipped;
if (state->skip_errors < 2) {
memset(state->transfer.transfer_buffer + state->transfer.read_position, 0, (size_t) amount_skipped);
state->transfer.read_position += amount_skipped;
if (state->control.skip_errors < 2) {
/*@-compdef@ */
pv_error(state, "%s: %s: %ld - %ld (%ld %s)",
pv_current_file_name(state),
@@ -590,7 +602,7 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou
* Failed to skip - mark file as ended.
*/
*eof_in = true;
if (state->write_position >= state->read_position) {
if (state->transfer.write_position >= state->transfer.read_position) {
*eof_out = true;
}
}
@@ -600,48 +612,92 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou
/*
* Write state->to_write bytes of data from the transfer buffer to stdout.
* Write state->transfer.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
* 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
* with the read position AND *eof_in is true (meaning we've reached the end
* of data).
*
* On error, sets *eof_out to true, sets state->written to -1, and updates
* state->exit_status.
* On error, sets *eof_out to true, sets state->transfer.written to -1, and updates
* state->status.exit_status.
*
* If state->discard_input is true, does not actually write anything.
* If state->control.discard_input is true, does not actually write anything.
*/
static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long *lineswritten)
{
ssize_t nwritten;
if (NULL == state->transfer_buffer) {
if (NULL == state->transfer.transfer_buffer) {
pv_error(state, "%s", _("no transfer buffer allocated"));
state->exit_status |= 64;
state->status.exit_status |= 64;
*eof_out = true;
state->written = -1;
state->transfer.written = -1;
return 1;
}
nwritten = 0;
if (state->discard_input) {
nwritten = state->to_write;
} else if (state->to_write > 0) {
if (signal(SIGALRM, SIG_IGN) == SIG_ERR) {
pv_error(state, "%s: %s", _("failed to set alarm signal handler"), strerror(errno));
} else {
(void) alarm(1);
if (state->control.discard_input) {
nwritten = state->transfer.to_write;
} else if (state->transfer.to_write > 0) {
/*
* Set an interval timer or an alarm to interrupt the write
* with a signal if the write takes too long, so we can
* continue producing progress information.
*/
#if HAVE_SETITIMER
struct itimerval new_timer;
/*@-unrecog@ */
/* splint doesn't know setitimer or ITIMER_REAL */
memset(&new_timer, 0, sizeof(new_timer));
new_timer.it_value.tv_sec = (time_t) (state->control.interval);
new_timer.it_value.tv_usec = (suseconds_t) (((long) (state->control.interval * 1000000.0)) % 1000000);
/*
* We have to set the interval so that the timer continues
* to repeat while writes are attempted, especially as it's
* possible that the initial timer run will expire
* immediately if the period is less than 1 second.
*/
new_timer.it_interval.tv_sec = new_timer.it_value.tv_sec;
new_timer.it_interval.tv_usec = new_timer.it_value.tv_usec;
debug("%s: [%lds,%ldus]", "setting interval timer", (long) (new_timer.it_value.tv_sec),
(long) (new_timer.it_value.tv_usec));
if (0 != setitimer(ITIMER_REAL, &new_timer, NULL)) {
pv_error(state, "%s: %s", _("failed to set interval timer"), strerror(errno));
}
#else /* ! HAVE_SETITIMER */
(void) alarm(1);
#endif /* HAVE_SETITIMER */
nwritten = pv__transfer_write_repeated(STDOUT_FILENO,
state->transfer_buffer +
state->write_position, (size_t) (state->to_write),
state->sync_after_write);
state->transfer.transfer_buffer +
state->transfer.write_position,
(size_t) (state->transfer.to_write),
state->control.sync_after_write);
#if HAVE_SETITIMER
memset(&new_timer, 0, sizeof(new_timer));
new_timer.it_interval.tv_sec = 0;
new_timer.it_interval.tv_usec = 0;
new_timer.it_value.tv_sec = 0;
new_timer.it_value.tv_usec = 0;
if (0 != setitimer(ITIMER_REAL, &new_timer, NULL)) {
pv_error(state, "%s: %s", _("failed to clear interval timer"), strerror(errno));
}
/*@+unrecog@ */
#else /* ! HAVE_SETITIMER */
(void) alarm(0);
#endif /* HAVE_SETITIMER */
}
if (0 == nwritten) {
@@ -654,66 +710,69 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long
/*
* Write returned >0 - data successfully written.
*/
if ((state->linemode) && (lineswritten != NULL)) {
/*
* Guillaume Marcais: use strchr to count \n
*/
char save;
if ((state->control.linemode) && (lineswritten != NULL)) {
char separator;
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_terminated_lines) {
for (ptr++;
ptr -
(char *) state->transfer_buffer -
state->write_position < (size_t) nwritten; ptr++) {
if (*ptr == '\0')
++lines;
}
if (state->control.null_terminated_lines) {
separator = '\0';
} else {
while ((ptr = strchr((char *) (ptr + 1), '\n')))
separator = '\n';
}
ptr = (char *) (state->transfer.transfer_buffer + state->transfer.write_position - 1);
for (ptr++;
ptr - (char *) state->transfer.transfer_buffer - state->transfer.write_position <
(size_t) nwritten; ptr++) {
if (*ptr == separator)
++lines;
}
*lineswritten += lines;
state->transfer_buffer[state->write_position + nwritten] = save;
}
state->write_position += nwritten;
state->written += nwritten;
state->transfer.write_position += nwritten;
state->transfer.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)) {
if (state->display.component[PV_COMPONENT_OUTPUTBUF].required && (nwritten > 0)) {
size_t new_portion_length, old_portion_length;
new_portion_length = (size_t) nwritten;
if (new_portion_length > state->lastoutput_length)
new_portion_length = state->lastoutput_length;
if (new_portion_length > state->display.lastoutput_length)
new_portion_length = state->display.lastoutput_length;
old_portion_length = state->lastoutput_length - new_portion_length;
old_portion_length = state->display.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);
memmove(state->display.lastoutput_buffer,
state->display.lastoutput_buffer + new_portion_length, old_portion_length);
}
/*
* Copy the new data in.
*/
memcpy(state->lastoutput_buffer +
memcpy(state->display.lastoutput_buffer + /* flawfinder: ignore */
old_portion_length,
state->transfer_buffer + state->write_position - new_portion_length, new_portion_length);
state->transfer.transfer_buffer + state->transfer.write_position - new_portion_length,
new_portion_length);
/*
* flawfinder rationale: calculations above ensure
* that old_portion_length + new_portion_length is
* always <= lastoutput_length, and
* lastoutput_length is guaranteed by
* pv__format_init() to be no more than
* PV_SIZEOF_LASTOUTPUT_BUFFER, which is the size of
* lastoutput_buffer, so the memcpy() will always
* fit into the buffer.
*/
}
/*
@@ -722,9 +781,9 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long
* 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 (state->transfer.write_position >= state->transfer.read_position) {
state->transfer.write_position = 0;
state->transfer.read_position = 0;
if (*eof_in)
*eof_out = true;
}
@@ -757,9 +816,9 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long
}
pv_error(state, "%s: %s", _("write failed"), strerror(errno));
state->exit_status |= 16;
state->status.exit_status |= 16;
*eof_out = true;
state->written = -1;
state->transfer.written = -1;
return 1;
}
@@ -832,13 +891,13 @@ static char *pv__allocate_aligned_buffer(int fd, size_t target_size)
/*
* 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
* of a second. If state->control.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
* state->status.exit_status is updated). In line mode, the number of lines written
* will be put into *lineswritten.
*/
ssize_t pv_transfer(pvstate_t state, int fd, bool *eof_in, bool *eof_out, off_t allowed, long *lineswritten)
@@ -855,9 +914,9 @@ ssize_t pv_transfer(pvstate_t state, int fd, bool *eof_in, bool *eof_out, off_t
* Set or clear O_DIRECT on the input and output file descriptors,
* if the setting has changed.
*/
if (state->direct_io_changed) {
if (state->control.direct_io_changed) {
if (!(*eof_in)) {
if (0 != fcntl(fd, F_SETFL, (state->direct_io ? O_DIRECT : 0) | fcntl(fd, F_GETFL))) {
if (0 != fcntl(fd, F_SETFL, (state->control.direct_io ? O_DIRECT : 0) | fcntl(fd, F_GETFL))) {
/*@-compdef@ */
debug("%s: %s: %s", pv_current_file_name(state), "fcntl", strerror(errno));
/*@+compdef@ */
@@ -866,11 +925,11 @@ 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->direct_io ? O_DIRECT : 0) | fcntl(STDOUT_FILENO, F_GETFL))) {
(state->control.direct_io ? O_DIRECT : 0) | fcntl(STDOUT_FILENO, F_GETFL))) {
debug("%s: %s: %s", "(stdout)", "fcntl", strerror(errno));
}
}
state->direct_io_changed = false;
state->control.direct_io_changed = false;
}
#endif /* O_DIRECT */
@@ -878,24 +937,25 @@ ssize_t pv_transfer(pvstate_t state, int fd, bool *eof_in, bool *eof_out, off_t
* 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 = false;
if (fd != state->transfer.last_read_skip_fd) {
state->transfer.last_read_skip_fd = fd;
state->transfer.read_errors_in_a_row = 0;
state->transfer.read_error_warning_shown = false;
}
/*
* Allocate a new buffer, aligned appropriately for the input file
* (important if using O_DIRECT).
*/
if (NULL == state->transfer_buffer) {
state->transfer_buffer = pv__allocate_aligned_buffer(fd, state->target_buffer_size + 32);
if (NULL == state->transfer_buffer) {
if (NULL == state->transfer.transfer_buffer) {
state->transfer.transfer_buffer =
pv__allocate_aligned_buffer(fd, state->control.target_buffer_size + 32);
if (NULL == state->transfer.transfer_buffer) {
pv_error(state, "%s: %s", _("buffer allocation failed"), strerror(errno));
state->exit_status |= 64;
state->status.exit_status |= 64;
return -1;
}
state->buffer_size = state->target_buffer_size;
state->transfer.buffer_size = state->control.target_buffer_size;
}
/*
@@ -905,31 +965,37 @@ ssize_t pv_transfer(pvstate_t state, int fd, bool *eof_in, bool *eof_out, off_t
* memory) because the buffer may need to be aligned for O_DIRECT,
* and we can't realloc() an aligned buffer.
*/
if (state->buffer_size < state->target_buffer_size) {
if (state->transfer.buffer_size < state->control.target_buffer_size) {
char *newptr;
newptr = pv__allocate_aligned_buffer(fd, state->target_buffer_size + 32);
newptr = pv__allocate_aligned_buffer(fd, state->control.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;
state->control.target_buffer_size = state->transfer.buffer_size;
} else {
debug("%s: %ld", "buffer resized", state->buffer_size);
debug("%s: %ld", "buffer resized", state->transfer.buffer_size);
/*
* Copy the old buffer contents into the new buffer,
* and free the old one.
*/
if (state->buffer_size > 0)
memcpy(newptr, state->transfer_buffer, state->buffer_size);
free(state->transfer_buffer);
state->transfer_buffer = newptr;
state->buffer_size = state->target_buffer_size;
if (state->transfer.buffer_size > 0) {
memcpy(newptr, state->transfer.transfer_buffer, state->transfer.buffer_size); /* flawfinder: ignore */
}
/*
* flawfinder rationale: number of bytes copied is
* definitely always smaller than the new buffer
* size.
*/
free(state->transfer.transfer_buffer);
state->transfer.transfer_buffer = newptr;
state->transfer.buffer_size = state->control.target_buffer_size;
}
}
if ((state->linemode) && (lineswritten != NULL))
if ((state->control.linemode) && (lineswritten != NULL))
*lineswritten = 0;
if ((*eof_in) && (*eof_out))
@@ -942,7 +1008,7 @@ ssize_t pv_transfer(pvstate_t state, int fd, bool *eof_in, bool *eof_out, off_t
* 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)) {
if ((!(*eof_in)) && (state->transfer.read_position < state->transfer.buffer_size)) {
check_read_fd = fd;
}
@@ -952,10 +1018,10 @@ ssize_t pv_transfer(pvstate_t state, int fd, bool *eof_in, bool *eof_out, off_t
* is >0, then this puts an upper limit on how much we're allowed to
* write.
*/
state->to_write = (ssize_t) (state->read_position - state->write_position);
if ((state->rate_limit > 0) || (allowed > 0)) {
if ((off_t) (state->to_write) > allowed) {
state->to_write = (ssize_t) allowed;
state->transfer.to_write = (ssize_t) (state->transfer.read_position - state->transfer.write_position);
if ((state->control.rate_limit > 0) || (allowed > 0)) {
if ((off_t) (state->transfer.to_write) > allowed) {
state->transfer.to_write = (ssize_t) allowed;
}
}
@@ -963,7 +1029,7 @@ 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.
*/
if ((!(*eof_out)) && (state->to_write > 0)) {
if ((!(*eof_out)) && (state->transfer.to_write > 0)) {
check_write_fd = STDOUT_FILENO;
}
@@ -987,18 +1053,18 @@ ssize_t pv_transfer(pvstate_t state, int fd, bool *eof_in, bool *eof_out, off_t
/*@+compdef@ */
/* splint - see previous pv_current_file_name() calls. */
state->exit_status |= 16;
state->status.exit_status |= 16;
return -1;
}
state->written = 0;
state->transfer.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().
* NB this can update state->transfer.written because of splice().
*/
if (ready_to_read) {
if (pv__transfer_read(state, fd, eof_in, eof_out, allowed) == 0)
@@ -1009,23 +1075,15 @@ ssize_t pv_transfer(pvstate_t state, int fd, bool *eof_in, bool *eof_out, off_t
* 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_terminated_lines)) {
/*
* Guillaume Marcais: use strrchr to find last \n
*/
char save;
if ((state->transfer.to_write > 0) && (state->control.linemode) && !(state->control.null_terminated_lines)) {
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.transfer_buffer + state->transfer.write_position);
end = pv_memrchr(start, (int) '\n', (size_t) (state->transfer.to_write));
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 (NULL != end) {
state->transfer.to_write = (ssize_t) ((end - start) + 1);
}
}
@@ -1036,10 +1094,10 @@ ssize_t pv_transfer(pvstate_t state, int fd, bool *eof_in, bool *eof_out, off_t
*/
if (ready_to_write
#ifdef HAVE_SPLICE
&& (!state->splice_used)
&& (!state->transfer.splice_used)
#endif /* HAVE_SPLICE */
&& (state->read_position > state->write_position)
&& (state->to_write > 0)
&& (state->transfer.read_position > state->transfer.write_position)
&& (state->transfer.to_write > 0)
&& (NULL != lineswritten)) {
if (pv__transfer_write(state, eof_in, eof_out, lineswritten) == 0)
return 0;
@@ -1049,21 +1107,22 @@ ssize_t pv_transfer(pvstate_t state, int fd, bool *eof_in, bool *eof_out, off_t
* 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;
if (state->transfer.write_position > 0) {
if (state->transfer.write_position < state->transfer.read_position) {
memmove(state->transfer.transfer_buffer,
state->transfer.transfer_buffer +
state->transfer.write_position,
state->transfer.read_position - state->transfer.write_position);
state->transfer.read_position -= state->transfer.write_position;
state->transfer.write_position = 0;
} else {
state->write_position = 0;
state->read_position = 0;
state->transfer.write_position = 0;
state->transfer.read_position = 0;
}
}
#endif /* MAXIMISE_BUFFER_FILL */
return state->written;
return state->transfer.written;
}
/* EOF */
+223 -86
View File
@@ -1,7 +1,7 @@
/*
* Functions for watching file descriptors in other processes.
*
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023 Andrew Wood
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
@@ -31,7 +31,16 @@
#include <sys/proc_info.h>
#endif
int filesize(pvwatchfd_t info)
/*@-type@*/
/* splint has trouble with off_t and mode_t. */
/*
* Set info->size to the size of the file info->file_fdpath points to,
* assuming that info->sb_fd has been populated by stat(), or to 0 if the
* file size could not be determined or the file was opened in write mode;
* returns false if the file was not a block device or regular file.
*/
static bool filesize(pvwatchfd_t info)
{
if (S_ISBLK(info->sb_fd.st_mode)) {
int fd;
@@ -40,10 +49,25 @@ int filesize(pvwatchfd_t info)
* Get the size of block devices by opening
* them and seeking to the end.
*/
fd = open(info->file_fdpath, O_RDONLY);
fd = open(info->file_fdpath, O_RDONLY); /* flawfinder: ignore */
/*
* flawfinder: redirection check below; risk is minimal as
* we are not actually reading any data here.
*/
if (fd >= 0) {
info->size = lseek(fd, 0, SEEK_END);
close(fd);
/*
* TOCTOU mitigation: check it's still a block
* device before trying to seek to the end.
* Otherwise treat it as unreadable and set the size
* to 0.
*/
struct stat check_fd_sb;
memset(&check_fd_sb, 0, sizeof(check_fd_sb));
info->size = 0;
if (0 == fstat(fd, &check_fd_sb) && S_ISBLK(check_fd_sb.st_mode)) {
info->size = lseek(fd, 0, SEEK_END);
}
(void) close(fd);
} else {
info->size = 0;
}
@@ -52,12 +76,14 @@ int filesize(pvwatchfd_t info)
info->size = info->sb_fd.st_size;
}
} else {
return 4;
return false;
}
return 0;
return true;
}
/*@+type@*/
#ifdef __APPLE__
int pv_watchfd_info(pvstate_t state, pvwatchfd_t info, bool automatic)
{
@@ -96,7 +122,7 @@ int pv_watchfd_info(pvstate_t state, pvwatchfd_t info, bool automatic)
return 3;
}
if (filesize(info) != 0) {
if (!filesize(info)) {
if (!automatic)
pv_error(state, "%s %u: %s %d: %s: %s",
_("pid"),
@@ -144,7 +170,15 @@ int pv_watchfd_info(pvstate_t state, pvwatchfd_t info, bool automatic)
(void) pv_snprintf(info->file_fd, PV_SIZEOF_FILE_FD, "/proc/%u/fd/%d", info->watch_pid, info->watch_fd);
memset(info->file_fdpath, 0, PV_SIZEOF_FILE_FDPATH);
if (readlink(info->file_fd, info->file_fdpath, PV_SIZEOF_FILE_FDPATH - 1) < 0) {
if (readlink(info->file_fd, info->file_fdpath, PV_SIZEOF_FILE_FDPATH - 1) < 0) { /* flawfinder: ignore */
/*
* flawfinder: memset() has put \0 at the end already, and
* we tell readlink() to use 1 byte less than the buffer
* length, so \0 termination is assured. There is no
* mitigation for the risk of the link changing while we
* read it, but we are only reading from the destination,
* and then only if it's a block device - see filesize().
*/
if (!automatic)
pv_error(state, "%s %u: %s %d: %s",
_("pid"), info->watch_pid, _("fd"), info->watch_fd, strerror(errno));
@@ -162,14 +196,13 @@ int pv_watchfd_info(pvstate_t state, pvwatchfd_t info, bool automatic)
info->size = 0;
int ret = filesize(info);
if (ret != 0) {
if (!filesize(info)) {
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 ret;
return 4;
}
return 0;
@@ -177,32 +210,35 @@ int pv_watchfd_info(pvstate_t state, pvwatchfd_t info, bool automatic)
#endif
#ifdef __APPLE__
int pv_watchfd_changed(pvwatchfd_t info)
bool pv_watchfd_changed(pvwatchfd_t info)
{
return 1;
return true;
}
#else
/*
* Return nonzero if the given file descriptor has changed in some way since
* we started looking at it (i.e. changed destination or permissions).
* Return true 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)
bool pv_watchfd_changed(pvwatchfd_t info)
{
struct stat sb_fd, sb_fd_link;
memset(&sb_fd, 0, sizeof(sb_fd));
memset(&sb_fd_link, 0, sizeof(sb_fd_link));
if ((0 == stat(info->file_fd, &sb_fd))
&& (0 == lstat(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;
return true;
}
} else {
return 1;
return true;
}
return 0;
return false;
}
#endif
@@ -214,7 +250,6 @@ int pv_watchfd_changed(pvwatchfd_t info)
off_t pv_watchfd_position(pvwatchfd_t info)
{
off_t position;
unsigned long long pos_long;
#ifdef __APPLE__
struct vnode_fdinfowithpath vnodeInfo = { };
int32_t proc_fd = (int32_t) info->watch_fd;
@@ -228,19 +263,22 @@ off_t pv_watchfd_position(pvwatchfd_t info)
position = (off_t) vnodeInfo.pfi.fi_offset;
#else
long long pos_long;
FILE *fptr;
if (pv_watchfd_changed(info))
return -1;
fptr = fopen(info->file_fdinfo, "r");
fptr = fopen(info->file_fdinfo, "r"); /* flawfinder: ignore */
/* flawfinder: trusted location (/proc). */
if (NULL == fptr)
return -1;
pos_long = -1;
position = -1;
if (1 == fscanf(fptr, "pos: %llu", &pos_long))
if (1 == fscanf(fptr, "pos: %lld", &pos_long)) {
position = (off_t) pos_long;
fclose(fptr);
}
(void) fclose(fptr);
#endif
return position;
@@ -270,6 +308,54 @@ static int pidfds(pvstate_t state, unsigned int pid, struct proc_fdinfo **fds, i
}
#endif
/*@-compdestroy @ */
/*@-usereleased @ */
/*@-compdef @ */
/*
* splint: extending or creating entries and then zeroing them makes splint
* believe that data previously there may refer to pointers that are now
* lost - but these are newly added array entries so there is no loss.
*/
/*
* Extend the info array by one, returning false on error.
*/
static bool extend_info_array(int *array_length_ptr, pvwatchfd_t * info_array_ptr)
{
int array_length = 0;
struct pvwatchfd_s *info_array = NULL;
struct pvwatchfd_s *new_info_array;
array_length = *array_length_ptr;
info_array = *info_array_ptr;
array_length++;
if (NULL == info_array) {
new_info_array = malloc(array_length * sizeof(*info_array));
if (NULL != new_info_array)
memset(new_info_array, 0, array_length * sizeof(*info_array));
} else {
new_info_array = realloc(info_array, array_length * sizeof(*info_array));
if (NULL != new_info_array)
memset(&(new_info_array[array_length - 1]), 0, sizeof(*info_array));
}
if (NULL == new_info_array) {
return false;
}
*info_array_ptr = new_info_array;
*array_length_ptr = array_length;
return true;
}
/*@+compdestroy @ */
/*@+usereleased @ */
/*@+compdef @ */
/*
* Scan the given process and update the arrays with any new file
* descriptors.
@@ -277,14 +363,11 @@ static int pidfds(pvstate_t state, unsigned int pid, struct proc_fdinfo **fds, i
* 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)
int pv_watchpid_scanfds(pvstate_t state,
pid_t watch_pid, int *array_length_ptr, pvwatchfd_t * info_array_ptr, int *fd_to_idx)
{
char fd_dir[512] = { 0, };
int array_length = 0;
struct pvwatchfd_s *info_array = NULL;
struct pvstate_s *state_array = NULL;
#ifdef __APPLE__
struct proc_fdinfo *fd_infos = NULL;
@@ -295,9 +378,11 @@ int pv_watchpid_scanfds(pvstate_t state, pvstate_t pristine,
return -1;
}
#else
char fd_dir[512]; /* flawfinder: ignore - zeroed, bounded with pv_snprintf(). */
DIR *dptr;
struct dirent *d;
memset(fd_dir, 0, sizeof(fd_dir));
(void) pv_snprintf(fd_dir, sizeof(fd_dir), "/proc/%u/fd", watch_pid);
dptr = opendir(fd_dir);
@@ -307,7 +392,6 @@ int pv_watchpid_scanfds(pvstate_t state, pvstate_t pristine,
array_length = *array_length_ptr;
info_array = *info_array_ptr;
state_array = *state_array_ptr;
#ifdef __APPLE__
if (fd_infos_count < 1) {
@@ -320,6 +404,7 @@ int pv_watchpid_scanfds(pvstate_t state, pvstate_t pristine,
#endif
int fd, check_idx, use_idx, rc;
off_t position_now;
const char *use_format_string;
fd = -1;
#ifdef __APPLE__
@@ -350,42 +435,14 @@ int pv_watchpid_scanfds(pvstate_t state, pvstate_t pristine,
}
/*
* If there's no empty slot, extend the arrays.
* If there's no empty slot, extend the array.
*/
if (use_idx < 0) {
struct pvwatchfd_s *new_info_array;
struct pvstate_s *new_state_array;
array_length++;
if (!extend_info_array(array_length_ptr, info_array_ptr))
return 2;
array_length = *array_length_ptr;
info_array = *info_array_ptr;
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);
@@ -393,24 +450,77 @@ int pv_watchpid_scanfds(pvstate_t state, pvstate_t pristine,
/*
* 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;
/* Allocate new display state. */
/*@-mustfreeonly@ *//* splint - this is not a leak, this is a new entry. */
info_array[use_idx].state = pv_state_alloc(state->status.program_name);
/*@+mustfreeonly@ */
if (NULL == info_array[use_idx].state)
return 2;
/* Copy the main status.cwd value to the new state. */
memcpy(&(info_array[use_idx].state->status.cwd), &(state->status.cwd), sizeof(state->status.cwd)); /* flawfinder: ignore */
/* flawfinder: bounded to destination object size. */
/*
* Copy over all the control values, blanking out the
* dynamically allocated strings, and setting the default
* format (fixed buffer) to the required format string
* instead of allocating a new dynamic format string for the
* copy (so there's less dynamic allocation going on).
*/
memcpy(&(info_array[use_idx].state->control), &(state->control), sizeof(state->control)); /* flawfinder: ignore */
/* flawfinder: bounded to destination object size. */
/*@-mustfreeonly@ *//* splint - this is not a leak, this is a new entry. */
info_array[use_idx].state->control.name = NULL;
info_array[use_idx].state->control.format_string = NULL;
/*@+mustfreeonly@ */
info_array[use_idx].state->control.default_format[0] = '\0';
use_format_string =
NULL != state->control.format_string ? state->control.format_string : state->control.default_format;
if (NULL != use_format_string)
(void) pv_snprintf(info_array[use_idx].state->control.default_format, PV_SIZEOF_DEFAULT_FORMAT,
"%.510s", use_format_string);
/*
* Copy over all the display values, blanking out the
* dynamically allocated parts as above; then set the
* average rate window so that a new history buffer is
* allocated for this state.
*/
memcpy(&(info_array[use_idx].state->display), &(state->display), sizeof(state->display)); /* flawfinder: ignore */
/* flawfinder: bounded to destination object size. */
/*@-mustfreeonly@ *//* splint - this is not a leak, this is a new entry. */
info_array[use_idx].state->display.display_buffer = NULL;
info_array[use_idx].state->display.display_buffer_size = 0;
info_array[use_idx].state->display.history = NULL;
info_array[use_idx].state->display.history_len = 0;
/*@+mustfreeonly@ */
pv_state_average_rate_window_set(info_array[use_idx].state, state->control.average_rate_window);
#ifdef __APPLE__
if (fd_infos[i].proc_fdtype != PROX_FDTYPE_VNODE) {
continue;
}
#endif
/* Retrieve the details of this file descriptor. */
rc = pv_watchfd_info(state, &(info_array[use_idx]), true);
/*
* 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);
info_array[use_idx].watch_pid = 0;
if (NULL != info_array[use_idx].state)
pv_state_free(info_array[use_idx].state);
/*@-mustfreeonly@ *//* not a leak - we've just free()d it. */
info_array[use_idx].state = NULL;
/*@+mustfreeonly@ */
continue;
}
@@ -425,31 +535,56 @@ int pv_watchpid_scanfds(pvstate_t state, pvstate_t pristine,
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;
}
/* NULL check on the state - skip if not usable. */
if (NULL == info_array[use_idx].state) {
debug("%s %d: %s", "fd", fd, "state is NULL - marking as not displayable");
info_array[use_idx].watch_fd = -1;
continue;
}
state_array[use_idx].name = info_array[use_idx].display_name;
/*
* Set the displayed size appropriately; if the size is 0,
* or not known, remove %e and %I from the state's default
* format string so that neither estimated time remaining
* nor estimated time of completion are displayed.
*/
info_array[use_idx].state->control.size = info_array[use_idx].size;
if (info_array[use_idx].state->control.size < 1) {
char *fmt;
while (NULL != (fmt = strstr(info_array[use_idx].state->control.default_format, "%e"))) {
debug("%s", "zero size - removing estimated time remaining");
/* strlen-1 here to include trailing \0 */
memmove(fmt, fmt + 2, strlen(fmt) - 1); /* flawfinder: ignore */
info_array[use_idx].state->flag.reparse_display = 1;
}
while (NULL != (fmt = strstr(info_array[use_idx].state->control.default_format, "%I"))) {
debug("%s", "zero size - removing estimated completion time");
/* strlen-1 here to include trailing \0 */
memmove(fmt, fmt + 2, strlen(fmt) - 1); /* flawfinder: ignore */
info_array[use_idx].state->flag.reparse_display = 1;
}
/* flawfinder: default_format is always \0-terminated. */
}
/* Set the info display_name appropriately. */
pv_watchpid_setname(state, &(info_array[use_idx]));
state_array[use_idx].reparse_display = 1;
/* Carry the display_name through to the display state, and reparse. */
pv_state_name_set(info_array[use_idx].state, info_array[use_idx].display_name);
info_array[use_idx].state->flag.reparse_display = 1;
pv_elapsedtime_read(&(info_array[use_idx].start_time));
state_array[use_idx].initial_offset = 0;
/*
* Set the starting position (and initial offset, for the
* display), if known, so that ETA and so on are calculated
* correctly.
*/
info_array[use_idx].state->display.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].state->display.initial_offset = position_now;
info_array[use_idx].position = position_now;
}
}
@@ -458,7 +593,7 @@ int pv_watchpid_scanfds(pvstate_t state, pvstate_t pristine,
#ifdef __APPLE__
free(fd_infos);
#else
closedir(dptr);
(void) closedir(dptr);
#endif
return 0;
@@ -474,22 +609,24 @@ int pv_watchpid_scanfds(pvstate_t state, pvstate_t pristine,
*/
void pv_watchpid_setname(pvstate_t state, pvwatchfd_t info)
{
int path_length, cwd_length, max_display_length;
size_t path_length, cwd_length;
int max_display_length;
char *file_fdpath = info->file_fdpath;
memset(info->display_name, 0, PV_SIZEOF_DISPLAY_NAME);
path_length = strlen(info->file_fdpath);
cwd_length = strlen(state->cwd);
path_length = strlen(info->file_fdpath); /* flawfinder: ignore */
cwd_length = strlen(state->status.cwd); /* flawfinder: ignore */
/* flawfinder: both strings are always \0 terminated. */
if (cwd_length > 0 && path_length > cwd_length) {
if (0 == strncmp(info->file_fdpath, state->cwd, cwd_length)) {
if (0 == strncmp(info->file_fdpath, state->status.cwd, cwd_length)) {
file_fdpath += cwd_length + 1;
path_length -= cwd_length + 1;
}
}
max_display_length = (state->width / 2) - 6;
if (max_display_length >= path_length) {
max_display_length = (int) (state->control.width / 2) - 6;
if (max_display_length >= (int) path_length) {
(void) pv_snprintf(info->display_name,
PV_SIZEOF_DISPLAY_NAME, "%4d:%.498s", info->watch_fd, file_fdpath);
} else {
@@ -6,9 +6,9 @@
# Dummy assignments for "shellcheck".
testSubject="${testSubject:-false}"; workFile1="${workFile1:-.tmp1}"; workFile2="${workFile2:-.tmp2}"; workFile3="${workFile3:-.tmp3}"; workFile4="${workFile4:-.tmp4}"
# Do nothing if IPC is not supported.
# Do nothing if it is not supported.
if ! "${testSubject}" -h 2>/dev/null | grep -Eq "^ -R,"; then
echo "IPC is not supported on this platform"
echo "Not supported on this platform"
exit 77
fi
@@ -5,9 +5,9 @@
# Dummy assignments for "shellcheck".
sourcePath="${sourcePath:-.}"; testSubject="${testSubject:-false}"; workFile1="${workFile1:-.tmp1}"; workFile2="${workFile2:-.tmp2}"; workFile3="${workFile3:-.tmp3}"; workFile4="${workFile4:-.tmp4}"
# Do nothing if IPC is not supported.
# Do nothing if it is not supported.
if ! "${testSubject}" -h 2>/dev/null | grep -Eq "^ -R,"; then
echo "IPC is not supported on this platform"
echo "Not supported on this platform"
exit 77
fi
@@ -6,9 +6,9 @@
# Dummy assignments for "shellcheck".
sourcePath="${sourcePath:-.}"; testSubject="${testSubject:-false}"; workFile1="${workFile1:-.tmp1}"; workFile2="${workFile2:-.tmp2}"; workFile3="${workFile3:-.tmp3}"; workFile4="${workFile4:-.tmp4}"
# Do nothing if IPC is not supported.
# Do nothing if it is not supported.
if ! "${testSubject}" -h 2>/dev/null | grep -Eq "^ -R,"; then
echo "IPC is not supported on this platform"
echo "Not supported on this platform"
exit 77
fi
+2 -2
View File
@@ -5,9 +5,9 @@
# Dummy assignments for "shellcheck".
testSubject="${testSubject:-false}"; workFile1="${workFile1:-.tmp1}"; workFile2="${workFile2:-.tmp2}"; workFile3="${workFile3:-.tmp3}"; workFile4="${workFile4:-.tmp4}"
# Do nothing if IPC is not supported.
# Do nothing if it is not supported.
if ! "${testSubject}" -h 2>/dev/null | grep -Eq "^ -R,"; then
echo "IPC is not supported on this platform"
echo "Not supported on this platform"
exit 77
fi