New --store-and-forward option (#100), including refactoring several areas to allow PV to run in two passes, and the ability to look at the output pipe buffer utilisation to more accurately reflect the total data transferred to the receiving end, rather than just the total amount written to the output buffer.

This commit is contained in:
Andrew Wood
2024-10-05 16:21:59 +01:00
parent ea3787e393
commit d9a0041870
17 changed files with 700 additions and 331 deletions
+1
View File
@@ -1,5 +1,6 @@
### UNRELEASED
* feature: new "`--store-and-forward`" option to read input to a file first, then write it to the output ([#100](https://codeberg.org/a-j-wood/pv/issues/100))
* feature: new "`--stats`" option to show transfer stats at the end, like "`ping`" ([#49](https://codeberg.org/a-j-wood/pv/issues/49))
* feature: "`--rate`" can now be used with "`--numeric`" ([#17](https://codeberg.org/a-j-wood/pv/issues/17))
* feature: "`--gauge`" with "`--progress`" to show rate gauge when size is unknown ([#46](https://codeberg.org/a-j-wood/pv/issues/46))
+24
View File
@@ -491,6 +491,30 @@ except that
is never called. Implies
.BR \-C .
.TP
.BI \-U\ FILE \fR,\ \fB\-\-store\-and\-forward\ FILE
Instead of passing data through immediately, do it in two stages - first
read all input and write it to
.IR FILE ,
and then once the input is exhausted, read all of
.I FILE
and write it to the output.
.I FILE
remains in place afterwards, unless it is
"\fB-\fR", in which case
.B pv
creates a temporary file for this purpose, and automatically removes it
afterwards.
.TP
.B ""
This can be useful if you have a pipeline which generates data (your
input) quickly but you don't know the size, and you wish to pass it to some
slower process, once all of the input has been generated and you know its
size, so you can see its progress. Note that when doing this with
relatively small amounts of data,
.B \-\-no-splice
may be preferable so that pipe buffering doesn't affect the progress
display.
.TP
.BI \-d\ PID\fR[\fB:\fR\fIFD\fR],\ \fB\-\-watchfd\ PID\fR[\fB:\fR\fIFD\fR]
Instead of transferring data, watch file descriptor
.I FD
+17
View File
@@ -364,6 +364,23 @@ display types that are explicitly switched on will be shown.
This is equivalent to redirecting standard output to */dev/null*,
except that **write**(2) is never called. Implies **-C**.
**-U FILE, \--store-and-forward FILE**
: Instead of passing data through immediately, do it in two stages -
first read all input and write it to *FILE*, and then once the input
is exhausted, read all of *FILE* and write it to the output. *FILE*
remains in place afterwards, unless it is \"**-**\", in which case
**pv** creates a temporary file for this purpose, and automatically
removes it afterwards.
: This can be useful if you have a pipeline which generates data (your
input) quickly but you don\'t know the size, and you wish to pass it
to some slower process, once all of the input has been generated and
you know its size, so you can see its progress. Note that when doing
this with relatively small amounts of data, **\--no-splice** may be
preferable so that pipe buffering doesn\'t affect the progress
display.
**-d PID\[:FD\], \--watchfd PID\[:FD\]**
: Instead of transferring data, watch file descriptor *FD* of process
+57 -48
View File
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: pv@ivarch.com\n"
"POT-Creation-Date: 2024-10-04 22:08+0100\n"
"POT-Creation-Date: 2024-10-05 16:04+0100\n"
"PO-Revision-Date: 2024-09-29 11:07+0000\n"
"Last-Translator: a-j-wood <a-j-wood@users.noreply.translate.codeberg.org>\n"
"Language-Team: German <https://translate.codeberg.org/projects/pv/pv/de/>\n"
@@ -169,7 +169,8 @@ msgstr ""
msgid "use cursor positioning escape sequences"
msgstr "benutze Escape-Sequenzen zur Cursor-Positionierung"
#: src/main/help.c:369 src/main/help.c:405 src/main/help.c:420
#: src/main/help.c:369 src/main/help.c:399 src/main/help.c:408
#: src/main/help.c:423
msgid "FILE"
msgstr "DATEI"
@@ -222,117 +223,125 @@ msgid "discard input instead of writing to output"
msgstr "verwirf die Eingabe anstatt in die Ausgabe zu schreiben"
#: src/main/help.c:400
msgid "write all input to FILE before writing to output"
msgstr ""
#: src/main/help.c:403
msgid "PID"
msgstr "PID"
#: src/main/help.c:401
#: src/main/help.c:404
msgid "update settings of process PID"
msgstr "Einstellungen der Prozess-PID updaten"
#: src/main/help.c:406
#: src/main/help.c:409
msgid "save process ID in FILE"
msgstr "Prozess-ID in DATEI speichern"
#: src/main/help.c:409
#: src/main/help.c:412
msgid "PID[:FD]"
msgstr "PID[:FD]"
#: src/main/help.c:410
#: src/main/help.c:413
msgid "watch file FD opened by process PID"
msgstr "die vom Prozess PID geöffnete Datei FD beobachten"
#: src/main/help.c:414
#: src/main/help.c:417
msgid "show this help and exit"
msgstr "zeige diese Hilfe und beende"
#: src/main/help.c:417
#: src/main/help.c:420
msgid "show version information and exit"
msgstr "zeige Versionsinformationen und beende"
#: src/main/help.c:421
#: src/main/help.c:424
msgid "write debug logs to FILE"
msgstr "schreibe Debug-Protokolle in DATEI"
#: src/main/help.c:446
#: src/main/help.c:449
#, c-format
msgid "Usage: %s [OPTION] [FILE]..."
msgstr "Aufruf: %s [OPTION] [DATEI]..."
#: src/main/help.c:457
#: src/main/help.c:460
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:594
#: src/main/help.c:597
#, c-format
msgid "Please report any bugs to: %s"
msgstr "Bitte senden Sie Fehlerberichte an %s"
#: src/main/main.c:71
#: src/main/main.c:214
msgid "(input)"
msgstr ""
#: src/main/main.c:299
msgid "state allocation failed"
msgstr "Speicherzuweisung für `state' fehlgeschlagen"
#: src/main/options.c:138 src/pv/file.c:324
#: src/main/options.c:140 src/pv/file.c:324
msgid "failed to stat file"
msgstr "konnte Dateiinformationen nicht lesen"
#: src/main/options.c:152
#: src/main/options.c:154
msgid "is a directory"
msgstr "ist ein Verzeichnis"
#: src/main/options.c:181
#: src/main/options.c:183
msgid "failed to generate sysfs filename"
msgstr "konnte Sysfs-Dateinamen nicht erzeugen"
#: src/main/options.c:203
#: src/main/options.c:205
msgid "failed to read sysfs size file"
msgstr "konnte Größe der Sysfs-Datei nicht lesen"
#: src/main/options.c:223
#: src/main/options.c:225
msgid "failed to open block device"
msgstr "konnte Blockgerät nicht öffnen"
#: src/main/options.c:233
#: src/main/options.c:235
msgid "failed to determine size of block device"
msgstr "konnte Größe des Blockgeräts nicht ermitteln"
#: src/main/options.c:336
#: src/main/options.c:339
msgid "option structure allocation failed"
msgstr "Speicherzuweisung für `option' fehlgeschlagen"
#: src/main/options.c:355
#: src/main/options.c:358
msgid "option structure argv allocation failed"
msgstr "Speicherzuweisung für `argv' der Option fehlgeschlagen"
#: src/main/options.c:417
#: src/main/options.c:420
msgid "integer argument expected"
msgstr "Ganzzahliges Argument erwartet"
#: src/main/options.c:428
#: src/main/options.c:431
msgid "numeric argument expected"
msgstr "numerisches Argument erwartet"
#: src/main/options.c:439
#: src/main/options.c:442
msgid "process ID or pid:fd pair expected"
msgstr "Prozess-ID oder PID:FD-Paar erwartet"
#: src/main/options.c:446
#: src/main/options.c:449
msgid "invalid process ID"
msgstr "ungültige Prozess-ID"
#: src/main/options.c:655
#: src/main/options.c:666
#, c-format
msgid "Try `%s --help' for more information."
msgstr "`%s --help' zeigt weitere Informationen an."
#: src/main/options.c:657
#: src/main/options.c:668
#, c-format
msgid "Try `%s -h' for more information."
msgstr "`%s -h' zeigt weitere Informationen an."
#: src/main/options.c:688
#: src/main/options.c:699
msgid ""
"cannot use line mode or transfer modifier options when watching file "
"descriptors"
@@ -340,24 +349,24 @@ msgstr ""
"Beim Beobachten von Dateideskriptoren können keine Optionen für den "
"Zeilenmodus oder den Übertragungsmodifikator verwendet werden"
#: src/main/options.c:697
#: src/main/options.c:708
msgid "cannot use cursor positioning when watching file descriptors"
msgstr ""
"Beim Beobachten von Dateideskriptoren kann Cursor-Positionierung nicht "
"verwendet werden"
#: src/main/options.c:706
#: src/main/options.c:717
msgid "cannot use remote control when watching file descriptors"
msgstr ""
"Beim Beobachten von Dateideskriptoren kann die Fernbedienung nicht verwenden "
"werden"
#: src/main/options.c:715
#: src/main/options.c:726
msgid "cannot transfer files when watching file descriptors"
msgstr ""
"Beim Beobachten von Dateideskriptoren können Dateien nicht übertragen werden"
#: src/main/options.c:730
#: src/main/options.c:741
msgid "not available on systems without /proc/self/fdinfo"
msgstr "Nicht verfügbar auf Systemen ohne /proc/self/fdinfo"
@@ -401,45 +410,45 @@ msgstr "Sperrversuch fehlgeschlagen"
msgid "failed to open terminal"
msgstr "konnte Terminal nicht öffnen"
#: src/pv/display.c:216
#: src/pv/display.c:230
msgid "yzafpnum kMGTPEZY"
msgstr "yzafpnµm kMGTPEZY"
#: src/pv/display.c:234
#: src/pv/display.c:248
msgid "yzafpnum KMGTPEZY"
msgstr "yzafpnµm KMGTPEZY"
#: src/pv/display.c:661 src/pv/transfer.c:970
#: src/pv/display.c:675 src/pv/transfer.c:970
msgid "buffer allocation failed"
msgstr "Speicherzuweisung für den Puffer fehlgeschlagen"
#: src/pv/display.c:790
#: src/pv/display.c:804
msgid "b"
msgstr "b"
#: src/pv/display.c:793 src/pv/transfer.c:602
#: src/pv/display.c:807 src/pv/transfer.c:602
msgid "B"
msgstr "B"
#: src/pv/display.c:837 src/pv/display.c:852 src/pv/display.c:1071
#: src/pv/loop.c:418
#: src/pv/display.c:851 src/pv/display.c:866 src/pv/display.c:1085
#: src/pv/loop.c:494
msgid "b/s"
msgstr "b/s"
#: src/pv/display.c:841 src/pv/display.c:856 src/pv/display.c:1075
#: src/pv/display.c:855 src/pv/display.c:870 src/pv/display.c:1089
msgid "/s"
msgstr "/s"
#: src/pv/display.c:841 src/pv/display.c:856 src/pv/display.c:1075
#: src/pv/loop.c:418
#: src/pv/display.c:855 src/pv/display.c:870 src/pv/display.c:1089
#: src/pv/loop.c:494
msgid "B/s"
msgstr "B/s"
#: src/pv/display.c:884 src/pv/display.c:888
#: src/pv/display.c:898 src/pv/display.c:902
msgid "ETA"
msgstr "ETA"
#: src/pv/display.c:954
#: src/pv/display.c:968
msgid "FIN"
msgstr ""
@@ -471,15 +480,15 @@ msgstr "(keine)"
msgid "(stdin)"
msgstr "(stdin)"
#: src/pv/loop.c:417
#: src/pv/loop.c:493
msgid "rate min/avg/max/mdev"
msgstr ""
#: src/pv/loop.c:429
#: src/pv/loop.c:505
msgid "rate not measured"
msgstr ""
#: src/pv/loop.c:635 src/pv/loop.c:686 src/pv/loop.c:727 src/pv/watchpid.c:99
#: src/pv/loop.c:711 src/pv/loop.c:762 src/pv/loop.c:803 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
@@ -491,7 +500,7 @@ msgstr "pid"
msgid "history structure allocation failed"
msgstr "Speicherzuweisung für `history' fehlgeschlagen"
#: src/pv/state.c:470 src/pv/state.c:481
#: src/pv/state.c:501 src/pv/state.c:512
msgid "file list allocation failed"
msgstr "Speicherzuweisung für die Dateiliste fehlgeschlagen"
+57 -48
View File
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: pv@ivarch.com\n"
"POT-Creation-Date: 2024-10-04 22:08+0100\n"
"POT-Creation-Date: 2024-10-05 16:04+0100\n"
"Language: fr\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Date: 1999-06-01 15:18:29+0100\n"
@@ -162,7 +162,8 @@ msgstr ""
msgid "use cursor positioning escape sequences"
msgstr "utiliser les séquences d'échappements de positionnement de curseur"
#: src/main/help.c:369 src/main/help.c:405 src/main/help.c:420
#: src/main/help.c:369 src/main/help.c:399 src/main/help.c:408
#: src/main/help.c:423
msgid "FILE"
msgstr "FICHIER"
@@ -215,117 +216,125 @@ msgid "discard input instead of writing to output"
msgstr "rejeter l'entrée au lieu de l'écrire vers la sortie"
#: src/main/help.c:400
msgid "write all input to FILE before writing to output"
msgstr ""
#: src/main/help.c:403
msgid "PID"
msgstr "PID"
#: src/main/help.c:401
#: src/main/help.c:404
msgid "update settings of process PID"
msgstr "mettre à jour la configuration du processus PID"
#: src/main/help.c:406
#: src/main/help.c:409
msgid "save process ID in FILE"
msgstr "enregistrer l'ID de processus dans FICHIER"
#: src/main/help.c:409
#: src/main/help.c:412
msgid "PID[:FD]"
msgstr "PID[:FD]"
#: src/main/help.c:410
#: src/main/help.c:413
msgid "watch file FD opened by process PID"
msgstr "surveiller le fichier FD ouvert par le processus PID"
#: src/main/help.c:414
#: src/main/help.c:417
msgid "show this help and exit"
msgstr "afficher cette aide puis quitter"
#: src/main/help.c:417
#: src/main/help.c:420
msgid "show version information and exit"
msgstr "afficher la version puis quitter"
#: src/main/help.c:421
#: src/main/help.c:424
msgid "write debug logs to FILE"
msgstr "écrire les journaux de déboggage vers FICHIER"
#: src/main/help.c:446
#: src/main/help.c:449
#, c-format
msgid "Usage: %s [OPTION] [FILE]..."
msgstr "Utilisation : %s [OPTIONS] [FICHIER]..."
#: src/main/help.c:457
#: src/main/help.c:460
msgid ""
"Concatenate FILE(s), or standard input, to standard output, with monitoring."
msgstr ""
"Concaténer FICHIER(s), ou l'entrée standard, sur la sortie standard avec "
"monitorage."
#: src/main/help.c:594
#: src/main/help.c:597
#, c-format
msgid "Please report any bugs to: %s"
msgstr "SVP rapporter tous bogues à %s"
#: src/main/main.c:71
#: src/main/main.c:214
msgid "(input)"
msgstr ""
#: src/main/main.c:299
msgid "state allocation failed"
msgstr "échec de l'allocation de mémoire de condition"
#: src/main/options.c:138 src/pv/file.c:324
#: src/main/options.c:140 src/pv/file.c:324
msgid "failed to stat file"
msgstr "échec à statuer sur le fichier"
#: src/main/options.c:152
#: src/main/options.c:154
msgid "is a directory"
msgstr ""
#: src/main/options.c:181
#: src/main/options.c:183
msgid "failed to generate sysfs filename"
msgstr ""
#: src/main/options.c:203
#: src/main/options.c:205
msgid "failed to read sysfs size file"
msgstr ""
#: src/main/options.c:223
#: src/main/options.c:225
msgid "failed to open block device"
msgstr ""
#: src/main/options.c:233
#: src/main/options.c:235
msgid "failed to determine size of block device"
msgstr ""
#: src/main/options.c:336
#: src/main/options.c:339
msgid "option structure allocation failed"
msgstr "l'allocation pour la structure d'une option a échoué"
#: src/main/options.c:355
#: src/main/options.c:358
msgid "option structure argv allocation failed"
msgstr "l'allocation pour la structure de l'option argv a échoué"
#: src/main/options.c:417
#: src/main/options.c:420
msgid "integer argument expected"
msgstr "Valeur entière attendue"
#: src/main/options.c:428
#: src/main/options.c:431
msgid "numeric argument expected"
msgstr "Valeur numérique attendue"
#: src/main/options.c:439
#: src/main/options.c:442
msgid "process ID or pid:fd pair expected"
msgstr "ID de processus ou paire pid:fd attendue"
#: src/main/options.c:446
#: src/main/options.c:449
msgid "invalid process ID"
msgstr "ID de processus non valide"
#: src/main/options.c:655
#: src/main/options.c:666
#, c-format
msgid "Try `%s --help' for more information."
msgstr "Essayez `%s --help' pour plus d'informations."
#: src/main/options.c:657
#: src/main/options.c:668
#, c-format
msgid "Try `%s -h' for more information."
msgstr "Essayez `%s -h' pour plus d'informations."
#: src/main/options.c:688
#: src/main/options.c:699
msgid ""
"cannot use line mode or transfer modifier options when watching file "
"descriptors"
@@ -333,25 +342,25 @@ 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:697
#: src/main/options.c:708
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:706
#: src/main/options.c:717
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:715
#: src/main/options.c:726
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:730
#: src/main/options.c:741
msgid "not available on systems without /proc/self/fdinfo"
msgstr "non disponible sur les systèmes sans /proc/self/fdinfo"
@@ -397,45 +406,45 @@ msgstr "échec de la tentative de verrouillage"
msgid "failed to open terminal"
msgstr "échec de l'ouverture du terminal"
#: src/pv/display.c:216
#: src/pv/display.c:230
msgid "yzafpnum kMGTPEZY"
msgstr ""
#: src/pv/display.c:234
#: src/pv/display.c:248
msgid "yzafpnum KMGTPEZY"
msgstr ""
#: src/pv/display.c:661 src/pv/transfer.c:970
#: src/pv/display.c:675 src/pv/transfer.c:970
msgid "buffer allocation failed"
msgstr "échec de l'allocation de mémoire tampon"
#: src/pv/display.c:790
#: src/pv/display.c:804
msgid "b"
msgstr ""
#: src/pv/display.c:793 src/pv/transfer.c:602
#: src/pv/display.c:807 src/pv/transfer.c:602
msgid "B"
msgstr "O"
#: src/pv/display.c:837 src/pv/display.c:852 src/pv/display.c:1071
#: src/pv/loop.c:418
#: src/pv/display.c:851 src/pv/display.c:866 src/pv/display.c:1085
#: src/pv/loop.c:494
msgid "b/s"
msgstr ""
#: src/pv/display.c:841 src/pv/display.c:856 src/pv/display.c:1075
#: src/pv/display.c:855 src/pv/display.c:870 src/pv/display.c:1089
msgid "/s"
msgstr "/s"
#: src/pv/display.c:841 src/pv/display.c:856 src/pv/display.c:1075
#: src/pv/loop.c:418
#: src/pv/display.c:855 src/pv/display.c:870 src/pv/display.c:1089
#: src/pv/loop.c:494
msgid "B/s"
msgstr "O/s"
#: src/pv/display.c:884 src/pv/display.c:888
#: src/pv/display.c:898 src/pv/display.c:902
msgid "ETA"
msgstr "ETA"
#: src/pv/display.c:954
#: src/pv/display.c:968
msgid "FIN"
msgstr ""
@@ -467,15 +476,15 @@ msgstr "(aucun)"
msgid "(stdin)"
msgstr ""
#: src/pv/loop.c:417
#: src/pv/loop.c:493
msgid "rate min/avg/max/mdev"
msgstr ""
#: src/pv/loop.c:429
#: src/pv/loop.c:505
msgid "rate not measured"
msgstr ""
#: src/pv/loop.c:635 src/pv/loop.c:686 src/pv/loop.c:727 src/pv/watchpid.c:99
#: src/pv/loop.c:711 src/pv/loop.c:762 src/pv/loop.c:803 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
@@ -487,7 +496,7 @@ msgstr ""
msgid "history structure allocation failed"
msgstr "l'allocation de la structure de l'historique a échoué"
#: src/pv/state.c:470 src/pv/state.c:481
#: src/pv/state.c:501 src/pv/state.c:512
msgid "file list allocation failed"
msgstr "échec de l'allocation de la liste de fichiers"
+57 -48
View File
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: pv@ivarch.com\n"
"POT-Creation-Date: 2024-10-04 22:08+0100\n"
"POT-Creation-Date: 2024-10-05 16:04+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"
@@ -155,7 +155,8 @@ msgstr "pokaż wyjście nawet gdy błędy nie są typu terminal"
msgid "use cursor positioning escape sequences"
msgstr "używaj sekwencji escape do pozycjonowania"
#: src/main/help.c:369 src/main/help.c:405 src/main/help.c:420
#: src/main/help.c:369 src/main/help.c:399 src/main/help.c:408
#: src/main/help.c:423
msgid "FILE"
msgstr ""
@@ -208,135 +209,143 @@ msgid "discard input instead of writing to output"
msgstr ""
#: src/main/help.c:400
msgid "write all input to FILE before writing to output"
msgstr ""
#: src/main/help.c:403
msgid "PID"
msgstr ""
#: src/main/help.c:401
#: src/main/help.c:404
msgid "update settings of process PID"
msgstr ""
#: src/main/help.c:406
#: src/main/help.c:409
msgid "save process ID in FILE"
msgstr ""
#: src/main/help.c:409
#: src/main/help.c:412
msgid "PID[:FD]"
msgstr ""
#: src/main/help.c:410
#: src/main/help.c:413
msgid "watch file FD opened by process PID"
msgstr ""
#: src/main/help.c:414
#: src/main/help.c:417
msgid "show this help and exit"
msgstr "wyświetl te informacje z pomocą i wyjdź"
#: src/main/help.c:417
#: src/main/help.c:420
msgid "show version information and exit"
msgstr "wyświetl informacje o wersji i wyjdź"
#: src/main/help.c:421
#: src/main/help.c:424
msgid "write debug logs to FILE"
msgstr ""
#: src/main/help.c:446
#: src/main/help.c:449
#, c-format
msgid "Usage: %s [OPTION] [FILE]..."
msgstr "Sposób użycia: %s [OPCJA] [PLIK]..."
#: src/main/help.c:457
#: src/main/help.c:460
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:594
#: src/main/help.c:597
#, c-format
msgid "Please report any bugs to: %s"
msgstr "Proszę przesyłać zgłoszenia błędów do %s"
#: src/main/main.c:71
#: src/main/main.c:214
msgid "(input)"
msgstr ""
#: src/main/main.c:299
msgid "state allocation failed"
msgstr ""
#: src/main/options.c:138 src/pv/file.c:324
#: src/main/options.c:140 src/pv/file.c:324
msgid "failed to stat file"
msgstr "nie udało się wykonać operacji stat na pliku"
#: src/main/options.c:152
#: src/main/options.c:154
msgid "is a directory"
msgstr ""
#: src/main/options.c:181
#: src/main/options.c:183
msgid "failed to generate sysfs filename"
msgstr ""
#: src/main/options.c:203
#: src/main/options.c:205
msgid "failed to read sysfs size file"
msgstr ""
#: src/main/options.c:223
#: src/main/options.c:225
msgid "failed to open block device"
msgstr ""
#: src/main/options.c:233
#: src/main/options.c:235
msgid "failed to determine size of block device"
msgstr ""
#: src/main/options.c:336
#: src/main/options.c:339
msgid "option structure allocation failed"
msgstr "nie udało się zaalokować struktur opcji"
#: src/main/options.c:355
#: src/main/options.c:358
msgid "option structure argv allocation failed"
msgstr "nie udało się zaalokować struktur opcji argv"
#: src/main/options.c:417
#: src/main/options.c:420
msgid "integer argument expected"
msgstr ""
#: src/main/options.c:428
#: src/main/options.c:431
msgid "numeric argument expected"
msgstr ""
#: src/main/options.c:439
#: src/main/options.c:442
msgid "process ID or pid:fd pair expected"
msgstr ""
#: src/main/options.c:446
#: src/main/options.c:449
msgid "invalid process ID"
msgstr ""
#: src/main/options.c:655
#: src/main/options.c:666
#, c-format
msgid "Try `%s --help' for more information."
msgstr "Spróbuj `%s --help' by uzyskać więcej informacji"
#: src/main/options.c:657
#: src/main/options.c:668
#, c-format
msgid "Try `%s -h' for more information."
msgstr "Spróbuj `%s -h' by uzyskać więcej informacji"
#: src/main/options.c:688
#: src/main/options.c:699
msgid ""
"cannot use line mode or transfer modifier options when watching file "
"descriptors"
msgstr ""
#: src/main/options.c:697
#: src/main/options.c:708
msgid "cannot use cursor positioning when watching file descriptors"
msgstr ""
#: src/main/options.c:706
#: src/main/options.c:717
msgid "cannot use remote control when watching file descriptors"
msgstr ""
#: src/main/options.c:715
#: src/main/options.c:726
msgid "cannot transfer files when watching file descriptors"
msgstr ""
#: src/main/options.c:730
#: src/main/options.c:741
msgid "not available on systems without /proc/self/fdinfo"
msgstr ""
@@ -381,45 +390,45 @@ msgstr ""
msgid "failed to open terminal"
msgstr "nie udało się otworzyć terminala"
#: src/pv/display.c:216
#: src/pv/display.c:230
msgid "yzafpnum kMGTPEZY"
msgstr ""
#: src/pv/display.c:234
#: src/pv/display.c:248
msgid "yzafpnum KMGTPEZY"
msgstr ""
#: src/pv/display.c:661 src/pv/transfer.c:970
#: src/pv/display.c:675 src/pv/transfer.c:970
msgid "buffer allocation failed"
msgstr "nie udało się zaalokować bufora"
#: src/pv/display.c:790
#: src/pv/display.c:804
msgid "b"
msgstr ""
#: src/pv/display.c:793 src/pv/transfer.c:602
#: src/pv/display.c:807 src/pv/transfer.c:602
msgid "B"
msgstr "B"
#: src/pv/display.c:837 src/pv/display.c:852 src/pv/display.c:1071
#: src/pv/loop.c:418
#: src/pv/display.c:851 src/pv/display.c:866 src/pv/display.c:1085
#: src/pv/loop.c:494
msgid "b/s"
msgstr ""
#: src/pv/display.c:841 src/pv/display.c:856 src/pv/display.c:1075
#: src/pv/display.c:855 src/pv/display.c:870 src/pv/display.c:1089
msgid "/s"
msgstr "/s"
#: src/pv/display.c:841 src/pv/display.c:856 src/pv/display.c:1075
#: src/pv/loop.c:418
#: src/pv/display.c:855 src/pv/display.c:870 src/pv/display.c:1089
#: src/pv/loop.c:494
msgid "B/s"
msgstr "B/s"
#: src/pv/display.c:884 src/pv/display.c:888
#: src/pv/display.c:898 src/pv/display.c:902
msgid "ETA"
msgstr "ETA"
#: src/pv/display.c:954
#: src/pv/display.c:968
msgid "FIN"
msgstr ""
@@ -452,15 +461,15 @@ msgstr ""
msgid "(stdin)"
msgstr ""
#: src/pv/loop.c:417
#: src/pv/loop.c:493
msgid "rate min/avg/max/mdev"
msgstr ""
#: src/pv/loop.c:429
#: src/pv/loop.c:505
msgid "rate not measured"
msgstr ""
#: src/pv/loop.c:635 src/pv/loop.c:686 src/pv/loop.c:727 src/pv/watchpid.c:99
#: src/pv/loop.c:711 src/pv/loop.c:762 src/pv/loop.c:803 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
@@ -472,7 +481,7 @@ msgstr ""
msgid "history structure allocation failed"
msgstr ""
#: src/pv/state.c:470 src/pv/state.c:481
#: src/pv/state.c:501 src/pv/state.c:512
msgid "file list allocation failed"
msgstr ""
+57 -48
View File
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: pv@ivarch.com\n"
"POT-Creation-Date: 2024-10-04 22:08+0100\n"
"POT-Creation-Date: 2024-10-05 16:04+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"
@@ -155,7 +155,8 @@ msgstr "gera dados mesmo que a saída de erro seja redirecionada"
msgid "use cursor positioning escape sequences"
msgstr "utiliza caracteres de escape para posicionar o cursor"
#: src/main/help.c:369 src/main/help.c:405 src/main/help.c:420
#: src/main/help.c:369 src/main/help.c:399 src/main/help.c:408
#: src/main/help.c:423
msgid "FILE"
msgstr ""
@@ -208,117 +209,125 @@ msgid "discard input instead of writing to output"
msgstr ""
#: src/main/help.c:400
msgid "write all input to FILE before writing to output"
msgstr ""
#: src/main/help.c:403
msgid "PID"
msgstr ""
#: src/main/help.c:401
#: src/main/help.c:404
msgid "update settings of process PID"
msgstr "atualizar as configurações do processo PID"
#: src/main/help.c:406
#: src/main/help.c:409
msgid "save process ID in FILE"
msgstr "salvar ID do processo em FILE"
#: src/main/help.c:409
#: src/main/help.c:412
msgid "PID[:FD]"
msgstr ""
#: src/main/help.c:410
#: src/main/help.c:413
msgid "watch file FD opened by process PID"
msgstr "assistir arquivo FD aberto pelo processo PID"
#: src/main/help.c:414
#: src/main/help.c:417
msgid "show this help and exit"
msgstr "exibe esta tela de ajuda e termina"
#: src/main/help.c:417
#: src/main/help.c:420
msgid "show version information and exit"
msgstr "exibe a versão e termina"
#: src/main/help.c:421
#: src/main/help.c:424
msgid "write debug logs to FILE"
msgstr ""
#: src/main/help.c:446
#: src/main/help.c:449
#, c-format
msgid "Usage: %s [OPTION] [FILE]..."
msgstr "Uso: %s [OPÇÕES] [ARQUIVOS]..."
#: src/main/help.c:457
#: src/main/help.c:460
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:594
#: src/main/help.c:597
#, c-format
msgid "Please report any bugs to: %s"
msgstr "Por favor, reporte quaisquer defeitos para %s"
#: src/main/main.c:71
#: src/main/main.c:214
msgid "(input)"
msgstr ""
#: src/main/main.c:299
msgid "state allocation failed"
msgstr "alocação de memória de status falhou"
#: src/main/options.c:138 src/pv/file.c:324
#: src/main/options.c:140 src/pv/file.c:324
msgid "failed to stat file"
msgstr "erro obtendo informações do arquivo"
#: src/main/options.c:152
#: src/main/options.c:154
msgid "is a directory"
msgstr ""
#: src/main/options.c:181
#: src/main/options.c:183
msgid "failed to generate sysfs filename"
msgstr ""
#: src/main/options.c:203
#: src/main/options.c:205
msgid "failed to read sysfs size file"
msgstr ""
#: src/main/options.c:223
#: src/main/options.c:225
msgid "failed to open block device"
msgstr ""
#: src/main/options.c:233
#: src/main/options.c:235
msgid "failed to determine size of block device"
msgstr ""
#: src/main/options.c:336
#: src/main/options.c:339
msgid "option structure allocation failed"
msgstr "erro na alocação da estrutura de opções"
#: src/main/options.c:355
#: src/main/options.c:358
msgid "option structure argv allocation failed"
msgstr "erro na alocação da estrutura de opções argv"
#: src/main/options.c:417
#: src/main/options.c:420
msgid "integer argument expected"
msgstr "argumento inteiro esperado"
#: src/main/options.c:428
#: src/main/options.c:431
msgid "numeric argument expected"
msgstr "argumento numérico esperado"
#: src/main/options.c:439
#: src/main/options.c:442
msgid "process ID or pid:fd pair expected"
msgstr "ID do processo ou par pid:fd esperado"
#: src/main/options.c:446
#: src/main/options.c:449
msgid "invalid process ID"
msgstr "ID de processo inválido"
#: src/main/options.c:655
#: src/main/options.c:666
#, c-format
msgid "Try `%s --help' for more information."
msgstr "Tente `%s --help' para maiores informações."
#: src/main/options.c:657
#: src/main/options.c:668
#, c-format
msgid "Try `%s -h' for more information."
msgstr "Tente `%s -h' para maiores informações."
#: src/main/options.c:688
#: src/main/options.c:699
msgid ""
"cannot use line mode or transfer modifier options when watching file "
"descriptors"
@@ -326,21 +335,21 @@ 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:697
#: src/main/options.c:708
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:706
#: src/main/options.c:717
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:715
#: src/main/options.c:726
msgid "cannot transfer files when watching file descriptors"
msgstr "não pode transferir arquivos ao assistir descritores de arquivo"
#: src/main/options.c:730
#: src/main/options.c:741
msgid "not available on systems without /proc/self/fdinfo"
msgstr "não disponível em sistemas sem /proc/self/fdinfo"
@@ -384,45 +393,45 @@ msgstr "tentativa de bloqueio falhou"
msgid "failed to open terminal"
msgstr "erro abrindo o terminal"
#: src/pv/display.c:216
#: src/pv/display.c:230
msgid "yzafpnum kMGTPEZY"
msgstr ""
#: src/pv/display.c:234
#: src/pv/display.c:248
msgid "yzafpnum KMGTPEZY"
msgstr ""
#: src/pv/display.c:661 src/pv/transfer.c:970
#: src/pv/display.c:675 src/pv/transfer.c:970
msgid "buffer allocation failed"
msgstr "erro alocando o buffer"
#: src/pv/display.c:790
#: src/pv/display.c:804
msgid "b"
msgstr ""
#: src/pv/display.c:793 src/pv/transfer.c:602
#: src/pv/display.c:807 src/pv/transfer.c:602
msgid "B"
msgstr ""
#: src/pv/display.c:837 src/pv/display.c:852 src/pv/display.c:1071
#: src/pv/loop.c:418
#: src/pv/display.c:851 src/pv/display.c:866 src/pv/display.c:1085
#: src/pv/loop.c:494
msgid "b/s"
msgstr ""
#: src/pv/display.c:841 src/pv/display.c:856 src/pv/display.c:1075
#: src/pv/display.c:855 src/pv/display.c:870 src/pv/display.c:1089
msgid "/s"
msgstr ""
#: src/pv/display.c:841 src/pv/display.c:856 src/pv/display.c:1075
#: src/pv/loop.c:418
#: src/pv/display.c:855 src/pv/display.c:870 src/pv/display.c:1089
#: src/pv/loop.c:494
msgid "B/s"
msgstr "B/s"
#: src/pv/display.c:884 src/pv/display.c:888
#: src/pv/display.c:898 src/pv/display.c:902
msgid "ETA"
msgstr "ETA"
#: src/pv/display.c:954
#: src/pv/display.c:968
msgid "FIN"
msgstr ""
@@ -454,15 +463,15 @@ msgstr ""
msgid "(stdin)"
msgstr ""
#: src/pv/loop.c:417
#: src/pv/loop.c:493
msgid "rate min/avg/max/mdev"
msgstr ""
#: src/pv/loop.c:429
#: src/pv/loop.c:505
msgid "rate not measured"
msgstr ""
#: src/pv/loop.c:635 src/pv/loop.c:686 src/pv/loop.c:727 src/pv/watchpid.c:99
#: src/pv/loop.c:711 src/pv/loop.c:762 src/pv/loop.c:803 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
@@ -474,7 +483,7 @@ msgstr ""
msgid "history structure allocation failed"
msgstr ""
#: src/pv/state.c:470 src/pv/state.c:481
#: src/pv/state.c:501 src/pv/state.c:512
msgid "file list allocation failed"
msgstr ""
+1
View File
@@ -36,6 +36,7 @@ struct opts_s {
/*@keep@*/ /*@null@*/ char *name; /* display name, if any */
/*@keep@*/ /*@null@*/ char *format; /* output format, if any */
/*@keep@*/ /*@null@*/ char *pidfile; /* PID file, if any */
/*@keep@*/ /*@null@*/ char *store_and_forward_file; /* store and forward file, if any */
/*@keep@*/ /*@null@*/ const char **argv; /* array of non-option arguments */
size_t lastwritten; /* show N bytes last written */
off_t rate_limit; /* rate limit, in bytes per second */
+6 -3
View File
@@ -230,13 +230,13 @@ struct pvstate_s {
/* 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_written; /* amount transferred by that time */
off_t transferred; /* amount transferred by that time */
} *history;
size_t history_len; /* total size of history array */
size_t history_first; /* index of oldest entry */
size_t history_last; /* index of newest entry */
off_t prev_total_written; /* total amount transferred when called last time */
off_t prev_transferred; /* total amount transferred when called last time */
int percentage; /* transfer percentage completion */
} calc;
@@ -290,7 +290,10 @@ struct pvstate_s {
ssize_t to_write; /* max to write this time around */
ssize_t written; /* bytes sent to stdout this time */
off_t total_written; /* total bytes or lines transferred */
size_t written_but_not_consumed; /* bytes in the output pipe, unread */
off_t total_written; /* total bytes or lines written */
off_t transferred; /* amount transferred (written - unconsumed) */
/*
* While reading from a file descriptor we keep track of how
+6
View File
@@ -27,6 +27,7 @@ extern "C" {
* Exit status bitmask values.
*/
#define PV_ERROREXIT_REMOTE_OR_PID 1
#define PV_ERROREXIT_SAF 1 /* store and forward error */
#define PV_ERROREXIT_ACCESS 2
#define PV_ERROREXIT_OUROBOROS 4
#define PV_ERROREXIT_TRANSITION 8
@@ -163,6 +164,11 @@ void pv_nanosleep(long long);
*/
extern /*@null@*/ /*@only@*/ pvstate_t pv_state_alloc(const char *);
/*
* Clear the calculated parts of a state structure.
*/
extern void pv_state_reset(pvstate_t state);
/*
* Set the formatting string, given a set of old-style formatting options.
*/
+3
View File
@@ -396,6 +396,9 @@ void display_help(void)
{ "-X", "--discard", NULL,
N_("discard input instead of writing to output"),
{ 0, 0, 0, 0} },
{ "-U", "--store-and-forward", N_("FILE"),
N_("write all input to FILE before writing to output"),
{ 0, 0, 0, 0} },
#ifdef SA_SIGINFO
{ "-R", "--remote", N_("PID"),
N_("update settings of process PID"),
+246 -93
View File
@@ -25,6 +25,233 @@ int pv_remote_set(opts_t, pvstate_t);
void pv_remote_init(void);
void pv_remote_fini(void);
/*
* Write a PID file, returning nonzero on error.
*/
static int pv__write_pidfile(opts_t opts)
{
char *pidfile_tmp_name;
size_t pidfile_tmp_bufsize;
int pidfile_tmp_fd;
FILE *pidfile_tmp_fptr;
mode_t prev_umask;
if (NULL == opts->pidfile)
return 0;
pidfile_tmp_bufsize = 16 + strlen(opts->pidfile); /* flawfinder: ignore */
/*
* flawfinder rationale: pidfile was supplied as an argument
* so we have to assume it is \0 terminated.
*/
pidfile_tmp_name = malloc(pidfile_tmp_bufsize);
if (NULL == pidfile_tmp_name) {
fprintf(stderr, "%s: %s\n", opts->program_name, strerror(errno));
return PV_ERROREXIT_REMOTE_OR_PID;
}
memset(pidfile_tmp_name, 0, pidfile_tmp_bufsize);
(void) pv_snprintf(pidfile_tmp_name, pidfile_tmp_bufsize, "%s.XXXXXX", opts->pidfile);
/*@-type@ *//* splint doesn't like mode_t */
prev_umask = umask(0000); /* flawfinder: ignore */
(void) umask(prev_umask | 0133); /* flawfinder: ignore */
/*@-unrecog@ *//* splint doesn't know mkstemp() */
pidfile_tmp_fd = mkstemp(pidfile_tmp_name); /* flawfinder: ignore */
/*@+unrecog@ */
if (pidfile_tmp_fd < 0) {
fprintf(stderr, "%s: %s: %s\n", opts->program_name, pidfile_tmp_name, strerror(errno));
(void) umask(prev_umask); /* flawfinder: ignore */
free(pidfile_tmp_name);
return PV_ERROREXIT_REMOTE_OR_PID;
}
(void) umask(prev_umask); /* flawfinder: ignore */
/*
* flawfinder rationale (umask, mkstemp) - flawfinder
* recommends setting the most restrictive umask possible
* when calling mkstemp(), so this is what we have done.
*
* We get the original umask and OR it with 0133 to make
* sure new files will be at least chmod 644. Then we put
* the umask back to what it was, after creating the
* temporary file.
*/
/*@+type@ */
pidfile_tmp_fptr = fdopen(pidfile_tmp_fd, "w");
if (NULL == pidfile_tmp_fptr) {
fprintf(stderr, "%s: %s: %s\n", opts->program_name, pidfile_tmp_name, strerror(errno));
(void) close(pidfile_tmp_fd);
(void) remove(pidfile_tmp_name);
free(pidfile_tmp_name);
return PV_ERROREXIT_REMOTE_OR_PID;
}
fprintf(pidfile_tmp_fptr, "%d\n", getpid());
if (0 != fclose(pidfile_tmp_fptr)) {
fprintf(stderr, "%s: %s: %s\n", opts->program_name, opts->pidfile, strerror(errno));
}
if (rename(pidfile_tmp_name, opts->pidfile) < 0) {
fprintf(stderr, "%s: %s: %s\n", opts->program_name, opts->pidfile, strerror(errno));
(void) remove(pidfile_tmp_name);
}
free(pidfile_tmp_name);
return 0;
}
/*
* Set the output file, if applicable. Returns nonzero on error.
*/
static int pv__set_output(pvstate_t state, opts_t opts, /*@null@ */ const char *output_file)
{
int output_fd;
if ((NULL == state) || (NULL == opts))
return 0;
if (NULL == output_file || 0 == strcmp(output_file, "-")) {
pv_state_output_set(state, STDOUT_FILENO, "(stdout)");
return 0;
}
output_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0600); /* flawfinder: ignore */
/*
* flawfinder rationale: the output filename has been
* explicitly provided, and in many cases the operator will
* want to write to device files and other special
* destinations, so there is no sense-checking we can do to
* make this safer.
*/
if (output_fd < 0) {
fprintf(stderr, "%s: %s: %s\n", opts->program_name, output_file, strerror(errno));
return PV_ERROREXIT_ACCESS;
}
pv_state_output_set(state, output_fd, output_file);
return 0;
}
/*
* Run in store-and-forward mode: run the main loop once with the output
* forced to the store-and-forward file (taking care of creation and removal
* of a temporary file if "-" was specified); then run the main loop again
* with the input file list forced to be just the store-and-forward file.
* Returns nonzero on error.
*/
static int pv__store_and_forward(pvstate_t state, opts_t opts, bool can_have_eta)
{
char tmp_filename[4096]; /* flawfinder: ignore */
bool use_temporary_file;
char *real_store_and_forward_file;
int retcode;
/* flawfinder: zeroed with memset and bounded by pv_snprintf. */
if ((NULL == state) || (NULL == opts) || (NULL == opts->store_and_forward_file))
return 0;
memset(tmp_filename, 0, sizeof(tmp_filename));
use_temporary_file = false;
if (0 == strcmp(opts->store_and_forward_file, "-"))
use_temporary_file = true;
/*
* Create a temporary file if the specified file was "-".
*/
if (use_temporary_file) {
char *tmpdir;
int tmp_fd;
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";
/*
* flawfinder rationale: null and zero-size values of $TMPDIR and
* $TMP are rejected, and the destination buffer is bounded.
*/
(void) pv_snprintf(tmp_filename, sizeof(tmp_filename), "%s/pv.XXXXXX", tmpdir);
/*@-unrecog@ *//* splint doesn't know mkstemp() */
tmp_fd = mkstemp(tmp_filename); /* flawfinder: ignore */
/*@+unrecog@ */
if (tmp_fd < 0) {
fprintf(stderr, "%s: %s: %s\n", opts->program_name, tmp_filename, strerror(errno));
return PV_ERROREXIT_SAF;
}
(void) close(tmp_fd);
}
/*
* Real store-and-forward file: either the one we were given, or the
* temporary file we created if we were given "-".
*/
real_store_and_forward_file = use_temporary_file ? tmp_filename : opts->store_and_forward_file;
/*
* First, set the output file to the store-and-forward file.
*/
debug("%s: %s", "setting output to store-and-forward file", real_store_and_forward_file);
retcode = pv__set_output(state, opts, real_store_and_forward_file);
if (0 != retcode)
goto end_store_and_forward;
/* Reset the formatting to set the displayed name to "(input)". */
/*@-mustfreefresh@ */
pv_state_set_format(state, opts->progress, opts->timer, can_have_eta ? opts->eta : false,
can_have_eta ? opts->fineta : false, opts->rate, opts->average_rate,
opts->bytes, opts->bufpercent, opts->lastwritten, _("(input)"));
/*@+mustfreefresh@ *//* see below about gettext _() calls. */
/* Run the main loop as normal. */
debug("%s", "running store-and-forward receiver");
retcode = pv_main_loop(state);
if (0 != retcode)
goto end_store_and_forward;
/* Set the output file back to what it originally was. */
debug("%s: %s", "setting output to original value", NULL == opts->output ? "(null)" : opts->output);
retcode = pv__set_output(state, opts, opts->output);
if (0 != retcode)
goto end_store_and_forward;
/* Replace the list of input files with the store-and-forward file. */
debug("%s", "resetting input file list");
pv_state_inputfiles(state, 1, (const char **) &real_store_and_forward_file);
/* Recalculate the input size. */
pv_state_size_set(state, pv_calc_total_size(state));
/* Reset the format, since we might have been asked to show ETA. */
pv_state_set_format(state, opts->progress, opts->timer, opts->eta,
opts->fineta, opts->rate, opts->average_rate,
opts->bytes, opts->bufpercent, opts->lastwritten, opts->name);
/* Reset calculated values in the state. */
pv_state_reset(state);
/* Run the main loop again. */
debug("%s", "running store-and-forward transmitter");
retcode = pv_main_loop(state);
end_store_and_forward:
if (use_temporary_file)
(void) remove(tmp_filename);
return retcode;
}
/*
* Process command-line arguments and set option flags, then call functions
@@ -35,6 +262,7 @@ int main(int argc, char **argv)
/*@only@ */ opts_t opts = NULL;
/*@only@ */ pvstate_t state = NULL;
int retcode = 0;
bool can_have_eta = true;
#ifdef ENABLE_NLS
/* Initialise language translation. */
@@ -96,80 +324,13 @@ int main(int argc, char **argv)
* Write a PID file if -P was specified.
*/
if (opts->pidfile != NULL) {
char *pidfile_tmp_name;
size_t pidfile_tmp_bufsize;
int pidfile_tmp_fd;
FILE *pidfile_tmp_fptr;
mode_t prev_umask;
pidfile_tmp_bufsize = 16 + strlen(opts->pidfile); /* flawfinder: ignore */
/*
* flawfinder rationale: pidfile was supplied as an argument
* so we have to assume it is \0 terminated.
*/
pidfile_tmp_name = malloc(pidfile_tmp_bufsize);
if (NULL == pidfile_tmp_name) {
fprintf(stderr, "%s: %s\n", opts->program_name, strerror(errno));
int pidfile_rc;
pidfile_rc = pv__write_pidfile(opts);
if (0 != pidfile_rc) {
pv_state_free(state);
opts_free(opts);
return PV_ERROREXIT_REMOTE_OR_PID;
return pidfile_rc;
}
memset(pidfile_tmp_name, 0, pidfile_tmp_bufsize);
(void) pv_snprintf(pidfile_tmp_name, pidfile_tmp_bufsize, "%s.XXXXXX", opts->pidfile);
/*@-type@ *//* splint doesn't like mode_t */
prev_umask = umask(0000); /* flawfinder: ignore */
(void) umask(prev_umask | 0133); /* flawfinder: ignore */
/*@-unrecog@ *//* splint doesn't know mkstemp() */
pidfile_tmp_fd = mkstemp(pidfile_tmp_name); /* flawfinder: ignore */
/*@+unrecog@ */
if (pidfile_tmp_fd < 0) {
fprintf(stderr, "%s: %s: %s\n", opts->program_name, pidfile_tmp_name, strerror(errno));
(void) umask(prev_umask); /* flawfinder: ignore */
free(pidfile_tmp_name);
pv_state_free(state);
opts_free(opts);
return PV_ERROREXIT_REMOTE_OR_PID;
}
(void) umask(prev_umask); /* flawfinder: ignore */
/*
* flawfinder rationale (umask, mkstemp) - flawfinder
* recommends setting the most restrictive umask possible
* when calling mkstemp(), so this is what we have done.
*
* We get the original umask and OR it with 0133 to make
* sure new files will be at least chmod 644. Then we put
* the umask back to what it was, after creating the
* temporary file.
*/
/*@+type@ */
pidfile_tmp_fptr = fdopen(pidfile_tmp_fd, "w");
if (NULL == pidfile_tmp_fptr) {
fprintf(stderr, "%s: %s: %s\n", opts->program_name, pidfile_tmp_name, strerror(errno));
(void) close(pidfile_tmp_fd);
(void) remove(pidfile_tmp_name);
free(pidfile_tmp_name);
pv_state_free(state);
opts_free(opts);
return PV_ERROREXIT_REMOTE_OR_PID;
}
fprintf(pidfile_tmp_fptr, "%d\n", getpid());
if (0 != fclose(pidfile_tmp_fptr)) {
fprintf(stderr, "%s: %s: %s\n", opts->program_name, opts->pidfile, strerror(errno));
}
if (rename(pidfile_tmp_name, opts->pidfile) < 0) {
fprintf(stderr, "%s: %s: %s\n", opts->program_name, opts->pidfile, strerror(errno));
(void) remove(pidfile_tmp_name);
}
free(pidfile_tmp_name);
}
/*
@@ -246,24 +407,11 @@ int main(int argc, char **argv)
* calculation looks at the output file if the input size can't be
* calculated (issue #91).
*/
if (NULL == opts->output || 0 == strcmp(opts->output, "-")) {
pv_state_output_set(state, STDOUT_FILENO, "(stdout)");
} else {
int fd = open(opts->output, O_WRONLY | O_CREAT | O_TRUNC, 0600); /* flawfinder: ignore */
/*
* flawfinder rationale: the output filename has been
* explicitly provided, and in many cases the operator will
* want to write to device files and other special
* destinations, so there is no sense-checking we can do to
* make this safer.
*/
if (fd < 0) {
fprintf(stderr, "%s: %s: %s\n", opts->program_name, opts->output, strerror(errno));
pv_state_free(state);
opts_free(opts);
return PV_ERROREXIT_ACCESS;
}
pv_state_output_set(state, fd, opts->output);
retcode = pv__set_output(state, opts, opts->output);
if (0 != retcode) {
pv_state_free(state);
opts_free(opts);
return retcode;
}
/*
@@ -290,7 +438,7 @@ int main(int argc, char **argv)
* If the size is unknown, we cannot have an ETA.
*/
if (opts->size < 1) {
opts->eta = false;
can_have_eta = false;
debug("%s", "size unknown - ETA disabled");
}
}
@@ -330,19 +478,24 @@ int main(int argc, char **argv)
pv_state_watch_fd_set(state, opts->watch_fd);
pv_state_average_rate_window_set(state, opts->average_rate_window);
pv_state_set_format(state, opts->progress, opts->timer, opts->eta,
opts->fineta, opts->rate, opts->average_rate,
pv_state_set_format(state, opts->progress, opts->timer, can_have_eta ? opts->eta : false,
can_have_eta ? opts->fineta : false, opts->rate, opts->average_rate,
opts->bytes, opts->bufpercent, opts->lastwritten, opts->name);
/* Initialise the signal handling. */
pv_sig_init(state);
/* Run the appropriate main loop. */
if (0 == opts->watch_pid) {
if (0 == opts->watch_pid && NULL == opts->store_and_forward_file) {
/* Normal "transfer data" mode. */
pv_remote_init();
retcode = pv_main_loop(state);
pv_remote_fini();
} else if (0 == opts->watch_pid && NULL != opts->store_and_forward_file) {
/* Store-and-forward transfer mode. */
pv_remote_init();
retcode = pv__store_and_forward(state, opts, can_have_eta);
pv_remote_fini();
} else if (0 != opts->watch_pid && -1 == opts->watch_fd) {
/* "Watch all file descriptors of another process" mode. */
retcode = pv_watchpid_loop(state);
+12 -1
View File
@@ -55,6 +55,8 @@ void opts_free( /*@only@ */ opts_t opts)
free(opts->pidfile);
if (NULL != opts->output)
free(opts->output);
if (NULL != opts->store_and_forward_file)
free(opts->store_and_forward_file);
if (NULL != opts->argv)
free(opts->argv);
/*@+keeptrans@ */
@@ -301,6 +303,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
{ "sync", 0, NULL, (int) 'Y' },
{ "direct-io", 0, NULL, (int) 'K' },
{ "discard", 0, NULL, (int) 'X' },
{ "store-and-forward", 1, NULL, (int) 'U' },
{ "remote", 1, NULL, (int) 'R' },
{ "pidfile", 1, NULL, (int) 'P' },
{ "watchfd", 1, NULL, (int) 'd' },
@@ -314,7 +317,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
/*@+nullassign@ */
int option_index = 0;
#endif /* HAVE_GETOPT_LONG */
char *short_options = "hVpteIrab8kTA:fvnqcWD:s:gl0i:w:H:N:F:L:B:CEZ:SYKXR:P:d:m:o:"
char *short_options = "hVpteIrab8kTA:fvnqcWD:s:gl0i:w:H:N:F:L:B:CEZ:SYKXU:R:P:d:m:o:"
#ifdef ENABLE_DEBUGGING
"!:"
#endif
@@ -605,6 +608,14 @@ opts_t opts_parse(unsigned int argc, char **argv)
opts->discard_input = true;
opts->no_splice = true;
break;
case 'U':
opts->store_and_forward_file = pv_strdup(optarg);
if (NULL == opts->store_and_forward_file) {
fprintf(stderr, "%s: -U: %s\n", opts->program_name, strerror(errno));
opts_free(opts);
return NULL;
}
break;
case 'R':
opts->remote = pv_getnum_count(optarg, false);
break;
+7 -7
View File
@@ -51,12 +51,12 @@ static void pv__update_average_rate_history(pvstate_t state, long double rate)
}
state->calc.history[last].elapsed_sec = state->transfer.elapsed_seconds;
state->calc.history[last].total_written = state->transfer.total_written;
state->calc.history[last].transferred = state->transfer.transferred;
if (first == last) {
state->calc.current_avg_rate = rate;
} else {
off_t bytes = (state->calc.history[last].total_written - state->calc.history[first].total_written);
off_t bytes = (state->calc.history[last].transferred - state->calc.history[first].transferred);
long double sec = (state->calc.history[last].elapsed_sec - state->calc.history[first].elapsed_sec);
state->calc.current_avg_rate = (long double) bytes / sec;
}
@@ -85,9 +85,9 @@ void pv_calculate_transfer_rate(pvstate_t state, bool final)
return;
bytes_since_last = 0;
if (state->transfer.total_written >= 0) {
bytes_since_last = state->transfer.total_written - state->calc.prev_total_written;
state->calc.prev_total_written = state->transfer.total_written;
if (state->transfer.transferred >= 0) {
bytes_since_last = state->transfer.transferred - state->calc.prev_transferred;
state->calc.prev_transferred = state->transfer.transferred;
}
/*
@@ -138,7 +138,7 @@ void pv_calculate_transfer_rate(pvstate_t state, bool final)
if (state->transfer.elapsed_seconds < 0.000001)
state->transfer.elapsed_seconds = 0.000001;
average_rate =
(((long double) state->transfer.total_written) -
(((long double) (state->transfer.transferred)) -
((long double) state->display.initial_offset)) / (long double) (state->transfer.elapsed_seconds);
transfer_rate = average_rate;
}
@@ -160,7 +160,7 @@ void pv_calculate_transfer_rate(pvstate_t state, bool final)
if (state->calc.percentage > 199)
state->calc.percentage = 0;
} else {
state->calc.percentage = pv_percentage(state->transfer.total_written, state->control.size);
state->calc.percentage = pv_percentage(state->transfer.transferred, state->control.size);
}
/* Ensure the percentage is never negative or huge. */
+23 -9
View File
@@ -119,14 +119,28 @@ void pv_write_retry(int fd, const char *buf, size_t count)
/*
* Write the given buffer to the terminal with pv_write_retry(), unless
* Write the given buffer to the terminal, like pv_write_retry(), unless
* stderr is suspended.
*/
void pv_tty_write(pvstate_t state, const char *buf, size_t count)
{
if (1 == state->flag.suspend_stderr)
return;
pv_write_retry(STDERR_FILENO, buf, count);
while (0 == state->flag.suspend_stderr && count > 0) {
ssize_t nwritten;
nwritten = write(STDERR_FILENO, buf, count);
if (nwritten < 0) {
if ((EINTR == errno) || (EAGAIN == errno)) {
continue;
}
return;
}
if (nwritten < 1)
return;
count -= nwritten;
buf += nwritten;
}
}
@@ -706,7 +720,7 @@ bool pv_format(pvstate_t state, bool final)
if (state->display.component[PV_COMPONENT_BYTES].required) {
(void) pv_snprintf(msg_bytes, sizeof(msg_bytes),
"%s%lld", first_item ? "" : " ",
(long long) ((state->control.bits ? 8 : 1) * state->transfer.total_written));
(long long) ((state->control.bits ? 8 : 1) * state->transfer.transferred));
first_item = false;
show_percentage = false;
}
@@ -787,10 +801,10 @@ bool pv_format(pvstate_t state, bool final)
/*@-mustfreefresh @ */
if (state->control.bits && !state->control.linemode) {
pv__sizestr(component_content, component_buf_size, "%s",
(long double) (state->transfer.total_written * 8), "", _("b"), count_type);
(long double) (state->transfer.transferred * 8), "", _("b"), count_type);
} else {
pv__sizestr(component_content, component_buf_size, "%s",
(long double) (state->transfer.total_written), "", _("B"), count_type);
(long double) (state->transfer.transferred), "", _("B"), count_type);
}
/*@+mustfreefresh @ */
/* splint: we trust gettext() not to really leak memory. */
@@ -862,7 +876,7 @@ bool pv_format(pvstate_t state, bool final)
case PV_COMPONENT_ETA:
/* Estimated time remaining until completion - if size is known. */
eta =
pv__seconds_remaining((state->transfer.total_written - state->display.initial_offset),
pv__seconds_remaining((state->transfer.transferred - state->display.initial_offset),
state->control.size - state->display.initial_offset,
state->calc.current_avg_rate);
@@ -917,7 +931,7 @@ bool pv_format(pvstate_t state, bool final)
*/
eta =
pv__seconds_remaining(state->transfer.total_written - state->display.initial_offset,
pv__seconds_remaining(state->transfer.transferred - state->display.initial_offset,
state->control.size - state->display.initial_offset,
state->calc.current_avg_rate);
+73 -4
View File
@@ -68,6 +68,7 @@ int pv_main_loop(pvstate_t state)
struct timespec init_time, next_remotecheck, transfer_elapsed;
int input_fd, output_fd;
unsigned int file_idx;
bool output_is_pipe;
/*
* "written" is ALWAYS bytes written by the last transfer.
@@ -88,6 +89,35 @@ int pv_main_loop(pvstate_t state)
if (output_fd < 0)
output_fd = STDOUT_FILENO;
/* Determine whether the output is a pipe. */
output_is_pipe = false;
{
struct stat sb;
memset(&sb, 0, sizeof(sb));
if (0 == fstat(output_fd, &sb)) {
/*@-type@*/
if ((sb.st_mode & S_IFMT) == S_IFIFO) {
output_is_pipe = true;
debug("%s", "output is a pipe");
}
/*@+type@*/ /* splint says st_mode is __mode_t, not mode_t */
} else {
debug("%s(%d): %s", "fstat", output_fd, strerror(errno));
}
}
/*
* Note that we could reduce the size of the output pipe buffer like
* this, to avoid the case where we write a load of data and it just
* goes into the buffer so we think we're done, but the consumer
* takes ages to process it:
*
* fcntl(output_fd, F_SETPIPE_SZ, 4096);
*
* If we can peek at how much has been consumed by the other end, we
* don't need to.
*/
pv_crs_init(state);
eof_in = false;
@@ -95,6 +125,7 @@ int pv_main_loop(pvstate_t state)
state->transfer.total_written = 0;
lineswritten = 0;
state->display.initial_offset = 0;
state->transfer.written_but_not_consumed = 0;
memset(&cur_time, 0, sizeof(cur_time));
memset(&start_time, 0, sizeof(start_time));
@@ -147,8 +178,7 @@ int pv_main_loop(pvstate_t state)
/*
* Set or clear O_DIRECT on the output.
*/
if (0 !=
fcntl(output_fd, F_SETFL, (state->control.direct_io ? O_DIRECT : 0) | fcntl(output_fd, F_GETFL))) {
if (0 != fcntl(output_fd, F_SETFL, (state->control.direct_io ? O_DIRECT : 0) | fcntl(output_fd, F_GETFL))) {
debug("%s: %s", "fcntl", strerror(errno));
}
state->control.direct_io_changed = false;
@@ -176,6 +206,11 @@ int pv_main_loop(pvstate_t state)
if (0 == state->control.target_buffer_size)
state->control.target_buffer_size = BUFFER_SIZE;
/*
* Repeat until eof_in is true, eof_out is true, and final_update is
* true.
*/
while ((!(eof_in && eof_out)) || (!final_update)) {
cansend = 0;
@@ -249,6 +284,36 @@ int pv_main_loop(pvstate_t state)
target -= written;
}
#ifdef FIONREAD
/*
* If writing to a pipe, look at how much is sitting in the
* pipe buffer waiting for the receiver to read.
*/
if (output_is_pipe) {
int nbytes;
nbytes = 0;
if (0 == ioctl(output_fd, FIONREAD, &nbytes)) {
if (nbytes >= 0) {
if (((size_t) nbytes) != state->transfer.written_but_not_consumed)
debug("%s: %d", "written_but_not_consumed is now", nbytes);
state->transfer.written_but_not_consumed = (size_t) nbytes;
} else {
debug("%s: %d", "FIONREAD gave a negative byte count", nbytes);
state->transfer.written_but_not_consumed = 0;
}
} else {
debug("%s(%d,%s): %s", "ioctl", output_fd, "FIONREAD", strerror(errno));
state->transfer.written_but_not_consumed = 0;
}
}
#endif
state->transfer.transferred = state->transfer.total_written;
/* TODO: work out what to do in line mode. */
if (output_is_pipe && !state->control.linemode) {
state->transfer.transferred -= state->transfer.written_but_not_consumed;
}
/*
* EOF, and files remain - advance to the next file.
*/
@@ -268,8 +333,12 @@ int pv_main_loop(pvstate_t state)
/* Now check the current time. */
pv_elapsedtime_read(&cur_time);
/* If full EOF, final update, and force a display updaate. */
if (eof_in && eof_out) {
/*
* If we've read everything and written everything, and the
* output pipe buffer is empty, then set the final update
* flag, and force a display update.
*/
if (eof_in && eof_out && 0 == state->transfer.written_but_not_consumed) {
final_update = true;
if ((state->display.display_visible)
|| (state->control.delay_start < 0.001)) {
+53 -22
View File
@@ -46,6 +46,58 @@ static void pv_alloc_history(pvstate_t state)
state->calc.history[0].elapsed_sec = 0.0; /* to be safe, memset() not recommended for doubles */
}
/*
* Clear the calculated parts of a state structure.
*/
void pv_state_reset(pvstate_t state)
{
if (NULL == state)
return;
state->flag.reparse_display = 1;
state->status.current_input_file = -1;
state->display.initial_offset = 0;
state->display.display_visible = false;
/*
* Explicitly set important floating point values to 0, as memset()
* is not recommended for this.
*/
state->calc.transfer_rate = 0.0;
state->calc.average_rate = 0.0;
state->calc.prev_elapsed_sec = 0.0;
state->calc.prev_rate = 0.0;
state->calc.prev_trans = 0.0;
state->calc.current_avg_rate = 0.0;
state->calc.rate_min = 0.0;
state->calc.rate_max = 0.0;
state->calc.rate_sum = 0.0;
state->calc.ratesquared_sum = 0.0;
state->calc.measurements_taken = 0;
state->calc.prev_transferred = 0;
state->calc.percentage = 0;
state->calc.history_first = state->calc.history_last = 0;
if (NULL != state->calc.history) {
state->calc.history[0].elapsed_sec = 0.0;
}
state->transfer.elapsed_seconds = 0.0;
state->transfer.read_position = 0;
state->transfer.write_position = 0;
state->transfer.to_write = 0;
state->transfer.written = 0;
state->transfer.total_written = 0;
state->transfer.written_but_not_consumed = 0;
state->transfer.read_errors_in_a_row = 0;
state->transfer.last_read_skip_fd = 0;
#ifdef HAVE_SPLICE
state->transfer.splice_failed_fd = -1;
#endif /* HAVE_SPLICE */
}
/*
* Create a new state structure, and return it, or 0 (NULL) on error.
*/
@@ -78,28 +130,7 @@ pvstate_t pv_state_alloc(const char *program_name)
#endif /* HAVE_IPC */
state->cursor.lock_fd = -1;
state->flag.reparse_display = 1;
state->status.current_input_file = -1;
#ifdef HAVE_SPLICE
state->transfer.splice_failed_fd = -1;
#endif /* HAVE_SPLICE */
state->display.display_visible = false;
/*
* Explicitly set important floating point values to 0, as memset()
* is not recommended for this.
*/
state->calc.transfer_rate = 0.0;
state->calc.average_rate = 0.0;
state->calc.prev_elapsed_sec = 0.0;
state->calc.prev_rate = 0.0;
state->calc.prev_trans = 0.0;
state->calc.current_avg_rate = 0.0;
state->calc.rate_min = 0.0;
state->calc.rate_max = 0.0;
state->calc.rate_sum = 0.0;
state->calc.ratesquared_sum = 0.0;
state->transfer.elapsed_seconds = 0.0;
pv_state_reset(state);
/*
* Get the current working directory, if possible, as a base for