diff --git a/docs/NEWS.md b/docs/NEWS.md index 14cafb1..1f877fc 100644 --- a/docs/NEWS.md +++ b/docs/NEWS.md @@ -1,6 +1,7 @@ ### UNRELEASED - * *fix:* allow **--format** to include "%nA" more than once, with different "n" valeus (#122) + * *feature:* new **--format** sequence "**%nL**", showing the most recent line written (#121) + * *fix:* allow **--format** to include "%nA" more than once, with different "n" values (#122) * *fix:* allow **--format** to include "%p" more than once * *fix:* add _configure_ script fallback for **--remote** check when cross-compiling (#120) * *fix:* allow **extra-display** to be changed by **--remote** (#123) diff --git a/docs/pv.1 b/docs/pv.1 index 43afaec..8add255 100644 --- a/docs/pv.1 +++ b/docs/pv.1 @@ -1,4 +1,4 @@ -.TH PV 1 2024-12-02 pv-1.9.7 "User Commands" +.TH PV 1 2024-12-07 pv-1.9.7 "User Commands" .\" .SH NAME pv \- monitor the progress of data through a pipe @@ -515,6 +515,12 @@ the last 16 bytes). Shows only dots if the transfer is being done with \fBsplice\fR(2), since splicing to or from pipes does not use the buffer. .TP +.B %nL +Show the first \fIn\fR bytes of the most recently written line (for example, +\*(lq\fB%40L\fR\*(rq shows the first 40 bytes). +If no \fIn\fR is given, then this expands to fill the available space. +Shows only spaces if the transfer is being done with \fBsplice\fR(2). +.TP .B %N Show the name prefix given by \*(lq\fB\-\-name\fR\*(rq. Padded to 9 characters with spaces, and suffixed with \*(lq:\*(rq. @@ -629,6 +635,15 @@ pv \-d 1234:3 pv \-d 1234 .EE .in +.PP +Sending logs to a processing script, showing the most recent line as part of +the progress display: +.PP +.in +4 +.EX +pv \-F '%a %p : %L' big.log | processing-script +.EE +.in .\" .SH EXIT STATUS An exit status of 1 indicates a problem with the \*(lq\fB\-\-remote\fR\*(rq @@ -721,6 +736,10 @@ If the input size cannot be calculated, and the output is a block device, then \fBpv\fR will read the output device's size, use that as if it had been passed to \*(lq\fB\-\-size\fR\*(rq, and activate \*(lq\fB\-\-stop\-at\-size\fR\*(rq. +.PP +The \*(lq\fB%nA\fR\*(rq and \*(lq\fB%nL\fR\*(rq format sequences may not be +effective with small input files, and \*(lq\fB%nL\fR\*(rq may be a few lines +out due to buffering within the pipeline itself. .\" .SH REPORTING BUGS Please report any bugs to \fBpv@ivarch.com\fR. diff --git a/docs/pv.1.md b/docs/pv.1.md index 0bfbb0e..962ca07 100644 --- a/docs/pv.1.md +++ b/docs/pv.1.md @@ -466,6 +466,13 @@ contain the following sequences: **splice**(2), since splicing to or from pipes does not use the buffer. +**%nL** + +: Show the first *n* bytes of the most recently written line (for + example, "**%40L**" shows the first 40 bytes). If no *n* is given, + then this expands to fill the available space. Shows only spaces if + the transfer is being done with **splice**(2). + **%N** : Show the name prefix given by "**\--name**". Padded to 9 characters @@ -557,6 +564,11 @@ been given. pv -d 1234 +Sending logs to a processing script, showing the most recent line as +part of the progress display: + + pv -F '%a %p : %L' big.log | processing-script + # EXIT STATUS An exit status of 1 indicates a problem with the "**\--remote**" or @@ -644,6 +656,10 @@ If the input size cannot be calculated, and the output is a block device, then **pv** will read the output device\'s size, use that as if it had been passed to "**\--size**", and activate "**\--stop-at-size**". +The "**%nA**" and "**%nL**" format sequences may not be effective with +small input files, and "**%nL**" may be a few lines out due to buffering +within the pipeline itself. + # REPORTING BUGS Please report any bugs to **pv@ivarch.com**. diff --git a/po/cs.po b/po/cs.po index cdbd0b5..1d89375 100644 --- a/po/cs.po +++ b/po/cs.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: pv@ivarch.com\n" -"POT-Creation-Date: 2024-12-07 20:21+0000\n" +"POT-Creation-Date: 2024-12-07 23:06+0000\n" "PO-Revision-Date: 2024-10-13 20:43+0000\n" "Last-Translator: mmatous \n" "Language-Team: Czech \n" @@ -403,37 +403,37 @@ msgstr "yzafpnum kMGTPEZY" msgid "yzafpnum KMGTPEZY" msgstr "yzafpnum KMGTPEZY" -#: src/pv/display.c:686 src/pv/transfer.c:1011 +#: src/pv/display.c:693 src/pv/transfer.c:1064 msgid "buffer allocation failed" msgstr "selhala alokace vyrovnávací paměti" -#: src/pv/display.c:815 +#: src/pv/display.c:822 msgid "b" msgstr "b" -#: src/pv/display.c:818 src/pv/transfer.c:602 +#: src/pv/display.c:825 src/pv/transfer.c:602 msgid "B" msgstr "B" -#: src/pv/display.c:862 src/pv/display.c:877 src/pv/display.c:1115 +#: src/pv/display.c:869 src/pv/display.c:884 src/pv/display.c:1139 #: src/pv/loop.c:549 msgid "b/s" msgstr "b/s" -#: src/pv/display.c:866 src/pv/display.c:881 src/pv/display.c:1119 +#: src/pv/display.c:873 src/pv/display.c:888 src/pv/display.c:1143 msgid "/s" msgstr "/s" -#: src/pv/display.c:866 src/pv/display.c:881 src/pv/display.c:1119 +#: src/pv/display.c:873 src/pv/display.c:888 src/pv/display.c:1143 #: src/pv/loop.c:549 msgid "B/s" msgstr "B/s" -#: src/pv/display.c:909 src/pv/display.c:913 +#: src/pv/display.c:916 src/pv/display.c:920 msgid "ETA" msgstr "ETA" -#: src/pv/display.c:979 +#: src/pv/display.c:986 msgid "FIN" msgstr "FIN" @@ -521,15 +521,15 @@ msgstr "selhalo nastavení intervalového časovače" msgid "failed to clear interval timer" msgstr "selhalo vynulování intervalového časovače" -#: src/pv/transfer.c:740 +#: src/pv/transfer.c:752 msgid "line position buffer allocation failed" msgstr "selhala alokace vyrovnávací paměti pro pozici v řádcích" -#: src/pv/transfer.c:872 +#: src/pv/transfer.c:925 msgid "write failed" msgstr "zápis selhal" -#: src/pv/transfer.c:1109 +#: src/pv/transfer.c:1162 msgid "select call failed" msgstr "selhalo volání select" diff --git a/po/de.po b/po/de.po index a89cd6f..eb0ceec 100644 --- a/po/de.po +++ b/po/de.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: pv@ivarch.com\n" -"POT-Creation-Date: 2024-12-07 20:21+0000\n" +"POT-Creation-Date: 2024-12-07 23:06+0000\n" "PO-Revision-Date: 2024-10-13 20:43+0000\n" "Last-Translator: fnetX \n" "Language-Team: German \n" @@ -426,37 +426,37 @@ msgstr "yzafpnµm kMGTPEZY" msgid "yzafpnum KMGTPEZY" msgstr "yzafpnµm KMGTPEZY" -#: src/pv/display.c:686 src/pv/transfer.c:1011 +#: src/pv/display.c:693 src/pv/transfer.c:1064 msgid "buffer allocation failed" msgstr "Speicherzuweisung für den Puffer fehlgeschlagen" -#: src/pv/display.c:815 +#: src/pv/display.c:822 msgid "b" msgstr "b" -#: src/pv/display.c:818 src/pv/transfer.c:602 +#: src/pv/display.c:825 src/pv/transfer.c:602 msgid "B" msgstr "B" -#: src/pv/display.c:862 src/pv/display.c:877 src/pv/display.c:1115 +#: src/pv/display.c:869 src/pv/display.c:884 src/pv/display.c:1139 #: src/pv/loop.c:549 msgid "b/s" msgstr "b/s" -#: src/pv/display.c:866 src/pv/display.c:881 src/pv/display.c:1119 +#: src/pv/display.c:873 src/pv/display.c:888 src/pv/display.c:1143 msgid "/s" msgstr "/s" -#: src/pv/display.c:866 src/pv/display.c:881 src/pv/display.c:1119 +#: src/pv/display.c:873 src/pv/display.c:888 src/pv/display.c:1143 #: src/pv/loop.c:549 msgid "B/s" msgstr "B/s" -#: src/pv/display.c:909 src/pv/display.c:913 +#: src/pv/display.c:916 src/pv/display.c:920 msgid "ETA" msgstr "ETA" -#: src/pv/display.c:979 +#: src/pv/display.c:986 msgid "FIN" msgstr "Endzeit" @@ -545,15 +545,15 @@ msgstr "konnte Intervall-Timer nicht setzen" msgid "failed to clear interval timer" msgstr "konnte Intervall-Timer nicht löschen" -#: src/pv/transfer.c:740 +#: src/pv/transfer.c:752 msgid "line position buffer allocation failed" msgstr "Zuweisung des Zeilenpuffers fehlgeschlagen" -#: src/pv/transfer.c:872 +#: src/pv/transfer.c:925 msgid "write failed" msgstr "write-Aufruf fehlgeschlagen" -#: src/pv/transfer.c:1109 +#: src/pv/transfer.c:1162 msgid "select call failed" msgstr "select-Aufruf fehlgeschlagen" diff --git a/po/es.po b/po/es.po index 7b646dc..9e1d418 100644 --- a/po/es.po +++ b/po/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: pv@ivarch.com\n" -"POT-Creation-Date: 2024-12-07 20:21+0000\n" +"POT-Creation-Date: 2024-12-07 23:06+0000\n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -395,37 +395,37 @@ msgstr "" msgid "yzafpnum KMGTPEZY" msgstr "" -#: src/pv/display.c:686 src/pv/transfer.c:1011 +#: src/pv/display.c:693 src/pv/transfer.c:1064 msgid "buffer allocation failed" msgstr "" -#: src/pv/display.c:815 +#: src/pv/display.c:822 msgid "b" msgstr "" -#: src/pv/display.c:818 src/pv/transfer.c:602 +#: src/pv/display.c:825 src/pv/transfer.c:602 msgid "B" msgstr "" -#: src/pv/display.c:862 src/pv/display.c:877 src/pv/display.c:1115 +#: src/pv/display.c:869 src/pv/display.c:884 src/pv/display.c:1139 #: src/pv/loop.c:549 msgid "b/s" msgstr "" -#: src/pv/display.c:866 src/pv/display.c:881 src/pv/display.c:1119 +#: src/pv/display.c:873 src/pv/display.c:888 src/pv/display.c:1143 msgid "/s" msgstr "" -#: src/pv/display.c:866 src/pv/display.c:881 src/pv/display.c:1119 +#: src/pv/display.c:873 src/pv/display.c:888 src/pv/display.c:1143 #: src/pv/loop.c:549 msgid "B/s" msgstr "" -#: src/pv/display.c:909 src/pv/display.c:913 +#: src/pv/display.c:916 src/pv/display.c:920 msgid "ETA" msgstr "" -#: src/pv/display.c:979 +#: src/pv/display.c:986 msgid "FIN" msgstr "" @@ -513,15 +513,15 @@ msgstr "" msgid "failed to clear interval timer" msgstr "" -#: src/pv/transfer.c:740 +#: src/pv/transfer.c:752 msgid "line position buffer allocation failed" msgstr "" -#: src/pv/transfer.c:872 +#: src/pv/transfer.c:925 msgid "write failed" msgstr "" -#: src/pv/transfer.c:1109 +#: src/pv/transfer.c:1162 msgid "select call failed" msgstr "" diff --git a/po/fr.po b/po/fr.po index e5012ae..bbb0dc5 100644 --- a/po/fr.po +++ b/po/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: pv@ivarch.com\n" -"POT-Creation-Date: 2024-12-07 20:21+0000\n" +"POT-Creation-Date: 2024-12-07 23:06+0000\n" "PO-Revision-Date: 2024-10-12 11:13+0000\n" "Last-Translator: a-j-wood \n" "Language-Team: French \n" @@ -430,37 +430,37 @@ msgstr "" msgid "yzafpnum KMGTPEZY" msgstr "" -#: src/pv/display.c:686 src/pv/transfer.c:1011 +#: src/pv/display.c:693 src/pv/transfer.c:1064 msgid "buffer allocation failed" msgstr "échec de l'allocation de mémoire tampon" -#: src/pv/display.c:815 +#: src/pv/display.c:822 msgid "b" msgstr "" -#: src/pv/display.c:818 src/pv/transfer.c:602 +#: src/pv/display.c:825 src/pv/transfer.c:602 msgid "B" msgstr "O" -#: src/pv/display.c:862 src/pv/display.c:877 src/pv/display.c:1115 +#: src/pv/display.c:869 src/pv/display.c:884 src/pv/display.c:1139 #: src/pv/loop.c:549 msgid "b/s" msgstr "" -#: src/pv/display.c:866 src/pv/display.c:881 src/pv/display.c:1119 +#: src/pv/display.c:873 src/pv/display.c:888 src/pv/display.c:1143 msgid "/s" msgstr "/s" -#: src/pv/display.c:866 src/pv/display.c:881 src/pv/display.c:1119 +#: src/pv/display.c:873 src/pv/display.c:888 src/pv/display.c:1143 #: src/pv/loop.c:549 msgid "B/s" msgstr "O/s" -#: src/pv/display.c:909 src/pv/display.c:913 +#: src/pv/display.c:916 src/pv/display.c:920 msgid "ETA" msgstr "ETA" -#: src/pv/display.c:979 +#: src/pv/display.c:986 msgid "FIN" msgstr "" @@ -548,15 +548,15 @@ msgstr "" msgid "failed to clear interval timer" msgstr "" -#: src/pv/transfer.c:740 +#: src/pv/transfer.c:752 msgid "line position buffer allocation failed" msgstr "" -#: src/pv/transfer.c:872 +#: src/pv/transfer.c:925 msgid "write failed" msgstr "l'écriture a échoué" -#: src/pv/transfer.c:1109 +#: src/pv/transfer.c:1162 msgid "select call failed" msgstr "l'appel de sélection a échoué" diff --git a/po/pl.po b/po/pl.po index ea521ba..9973f6c 100644 --- a/po/pl.po +++ b/po/pl.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: pv@ivarch.com\n" -"POT-Creation-Date: 2024-12-07 20:21+0000\n" +"POT-Creation-Date: 2024-12-07 23:06+0000\n" "PO-Revision-Date: 2024-10-20 14:07+0000\n" "Last-Translator: coralpink \n" "Language-Team: Polish \n" @@ -421,37 +421,37 @@ msgstr "yzafpnum kMGTPEZY" msgid "yzafpnum KMGTPEZY" msgstr "yzafpnum KMGTPEZY" -#: src/pv/display.c:686 src/pv/transfer.c:1011 +#: src/pv/display.c:693 src/pv/transfer.c:1064 msgid "buffer allocation failed" msgstr "nie udało się zaalokować bufora" -#: src/pv/display.c:815 +#: src/pv/display.c:822 msgid "b" msgstr "b" -#: src/pv/display.c:818 src/pv/transfer.c:602 +#: src/pv/display.c:825 src/pv/transfer.c:602 msgid "B" msgstr "B" -#: src/pv/display.c:862 src/pv/display.c:877 src/pv/display.c:1115 +#: src/pv/display.c:869 src/pv/display.c:884 src/pv/display.c:1139 #: src/pv/loop.c:549 msgid "b/s" msgstr "b/s" -#: src/pv/display.c:866 src/pv/display.c:881 src/pv/display.c:1119 +#: src/pv/display.c:873 src/pv/display.c:888 src/pv/display.c:1143 msgid "/s" msgstr "/s" -#: src/pv/display.c:866 src/pv/display.c:881 src/pv/display.c:1119 +#: src/pv/display.c:873 src/pv/display.c:888 src/pv/display.c:1143 #: src/pv/loop.c:549 msgid "B/s" msgstr "B/s" -#: src/pv/display.c:909 src/pv/display.c:913 +#: src/pv/display.c:916 src/pv/display.c:920 msgid "ETA" msgstr "ETA" -#: src/pv/display.c:979 +#: src/pv/display.c:986 msgid "FIN" msgstr "FIN" @@ -541,15 +541,15 @@ msgstr "nie udało się ustawić interwału czasowego" msgid "failed to clear interval timer" msgstr "nie udało się wyczyścić interwału czasowego" -#: src/pv/transfer.c:740 +#: src/pv/transfer.c:752 msgid "line position buffer allocation failed" msgstr "alokacja buforu pozycji linii nie powiodła się" -#: src/pv/transfer.c:872 +#: src/pv/transfer.c:925 msgid "write failed" msgstr "błąd zapisu" -#: src/pv/transfer.c:1109 +#: src/pv/transfer.c:1162 msgid "select call failed" msgstr "nie udało się wywołać funkcji select" diff --git a/po/pt.po b/po/pt.po index 86c76c3..543e021 100644 --- a/po/pt.po +++ b/po/pt.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: pv@ivarch.com\n" -"POT-Creation-Date: 2024-12-07 20:21+0000\n" +"POT-Creation-Date: 2024-12-07 23:06+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -409,37 +409,37 @@ msgstr "" msgid "yzafpnum KMGTPEZY" msgstr "" -#: src/pv/display.c:686 src/pv/transfer.c:1011 +#: src/pv/display.c:693 src/pv/transfer.c:1064 msgid "buffer allocation failed" msgstr "erro alocando o buffer" -#: src/pv/display.c:815 +#: src/pv/display.c:822 msgid "b" msgstr "" -#: src/pv/display.c:818 src/pv/transfer.c:602 +#: src/pv/display.c:825 src/pv/transfer.c:602 msgid "B" msgstr "" -#: src/pv/display.c:862 src/pv/display.c:877 src/pv/display.c:1115 +#: src/pv/display.c:869 src/pv/display.c:884 src/pv/display.c:1139 #: src/pv/loop.c:549 msgid "b/s" msgstr "" -#: src/pv/display.c:866 src/pv/display.c:881 src/pv/display.c:1119 +#: src/pv/display.c:873 src/pv/display.c:888 src/pv/display.c:1143 msgid "/s" msgstr "" -#: src/pv/display.c:866 src/pv/display.c:881 src/pv/display.c:1119 +#: src/pv/display.c:873 src/pv/display.c:888 src/pv/display.c:1143 #: src/pv/loop.c:549 msgid "B/s" msgstr "B/s" -#: src/pv/display.c:909 src/pv/display.c:913 +#: src/pv/display.c:916 src/pv/display.c:920 msgid "ETA" msgstr "ETA" -#: src/pv/display.c:979 +#: src/pv/display.c:986 msgid "FIN" msgstr "" @@ -527,15 +527,15 @@ msgstr "" msgid "failed to clear interval timer" msgstr "" -#: src/pv/transfer.c:740 +#: src/pv/transfer.c:752 msgid "line position buffer allocation failed" msgstr "" -#: src/pv/transfer.c:872 +#: src/pv/transfer.c:925 msgid "write failed" msgstr "erro de gravação" -#: src/pv/transfer.c:1109 +#: src/pv/transfer.c:1162 msgid "select call failed" msgstr "erro na chamada da função select" diff --git a/po/ru.po b/po/ru.po index ede6c02..c69eb17 100644 --- a/po/ru.po +++ b/po/ru.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: pv@ivarch.com\n" -"POT-Creation-Date: 2024-12-07 20:21+0000\n" +"POT-Creation-Date: 2024-12-07 23:06+0000\n" "PO-Revision-Date: 2024-10-22 18:07+0000\n" "Last-Translator: 0ko <0ko@users.noreply.translate.codeberg.org>\n" "Language-Team: Russian \n" @@ -404,37 +404,37 @@ msgstr "" msgid "yzafpnum KMGTPEZY" msgstr "" -#: src/pv/display.c:686 src/pv/transfer.c:1011 +#: src/pv/display.c:693 src/pv/transfer.c:1064 msgid "buffer allocation failed" msgstr "не удалось выделить память для буфера" -#: src/pv/display.c:815 +#: src/pv/display.c:822 msgid "b" msgstr "бит" -#: src/pv/display.c:818 src/pv/transfer.c:602 +#: src/pv/display.c:825 src/pv/transfer.c:602 msgid "B" msgstr "байт" -#: src/pv/display.c:862 src/pv/display.c:877 src/pv/display.c:1115 +#: src/pv/display.c:869 src/pv/display.c:884 src/pv/display.c:1139 #: src/pv/loop.c:549 msgid "b/s" msgstr "бит/с" -#: src/pv/display.c:866 src/pv/display.c:881 src/pv/display.c:1119 +#: src/pv/display.c:873 src/pv/display.c:888 src/pv/display.c:1143 msgid "/s" msgstr "/с" -#: src/pv/display.c:866 src/pv/display.c:881 src/pv/display.c:1119 +#: src/pv/display.c:873 src/pv/display.c:888 src/pv/display.c:1143 #: src/pv/loop.c:549 msgid "B/s" msgstr "байт/с" -#: src/pv/display.c:909 src/pv/display.c:913 +#: src/pv/display.c:916 src/pv/display.c:920 msgid "ETA" msgstr "ОЖИД" -#: src/pv/display.c:979 +#: src/pv/display.c:986 msgid "FIN" msgstr "ЗАВЕРШ" @@ -522,15 +522,15 @@ msgstr "не удалось задать измеритель временног msgid "failed to clear interval timer" msgstr "не удалось очистить измеритель временного промежутка" -#: src/pv/transfer.c:740 +#: src/pv/transfer.c:752 msgid "line position buffer allocation failed" msgstr "" -#: src/pv/transfer.c:872 +#: src/pv/transfer.c:925 msgid "write failed" msgstr "запись не удалась" -#: src/pv/transfer.c:1109 +#: src/pv/transfer.c:1162 msgid "select call failed" msgstr "запрос выборки не удался" diff --git a/po/tr.po b/po/tr.po index 4d00039..791b5ce 100644 --- a/po/tr.po +++ b/po/tr.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: pv 1.8.14\n" "Report-Msgid-Bugs-To: pv@ivarch.com\n" -"POT-Creation-Date: 2024-12-07 20:21+0000\n" +"POT-Creation-Date: 2024-12-07 23:06+0000\n" "PO-Revision-Date: 2024-10-12 23:51+0000\n" "Last-Translator: a-j-wood \n" "Language-Team: Turkish \n" @@ -405,37 +405,37 @@ msgstr "yzafpnum kMGTPEZY" msgid "yzafpnum KMGTPEZY" msgstr "yzafpnum KMGTPEZY" -#: src/pv/display.c:686 src/pv/transfer.c:1011 +#: src/pv/display.c:693 src/pv/transfer.c:1064 msgid "buffer allocation failed" msgstr "arabellek tahsisi başarısız oldu" -#: src/pv/display.c:815 +#: src/pv/display.c:822 msgid "b" msgstr "b" -#: src/pv/display.c:818 src/pv/transfer.c:602 +#: src/pv/display.c:825 src/pv/transfer.c:602 msgid "B" msgstr "B" -#: src/pv/display.c:862 src/pv/display.c:877 src/pv/display.c:1115 +#: src/pv/display.c:869 src/pv/display.c:884 src/pv/display.c:1139 #: src/pv/loop.c:549 msgid "b/s" msgstr "b/s" -#: src/pv/display.c:866 src/pv/display.c:881 src/pv/display.c:1119 +#: src/pv/display.c:873 src/pv/display.c:888 src/pv/display.c:1143 msgid "/s" msgstr "/s" -#: src/pv/display.c:866 src/pv/display.c:881 src/pv/display.c:1119 +#: src/pv/display.c:873 src/pv/display.c:888 src/pv/display.c:1143 #: src/pv/loop.c:549 msgid "B/s" msgstr "B/s" -#: src/pv/display.c:909 src/pv/display.c:913 +#: src/pv/display.c:916 src/pv/display.c:920 msgid "ETA" msgstr "ETA" -#: src/pv/display.c:979 +#: src/pv/display.c:986 msgid "FIN" msgstr "FIN" @@ -523,15 +523,15 @@ msgstr "aralık zamanlayıcısı ayarlanamadı" msgid "failed to clear interval timer" msgstr "aralık zamanlayıcısı temizlenemedi" -#: src/pv/transfer.c:740 +#: src/pv/transfer.c:752 msgid "line position buffer allocation failed" msgstr "satır konumu arabellek tahsisi başarısız oldu" -#: src/pv/transfer.c:872 +#: src/pv/transfer.c:925 msgid "write failed" msgstr "yazma başarısız" -#: src/pv/transfer.c:1109 +#: src/pv/transfer.c:1162 msgid "select call failed" msgstr "seçme çağrısı başarısız oldu" diff --git a/src/include/pv-internal.h b/src/include/pv-internal.h index 9dc2c4f..da5139a 100644 --- a/src/include/pv-internal.h +++ b/src/include/pv-internal.h @@ -35,6 +35,7 @@ typedef enum { PV_COMPONENT_NAME, /* name prefix */ PV_COMPONENT_BUFPERCENT, /* percentage of buffer used */ PV_COMPONENT_OUTPUTBUF, /* recent bytes in output buffer */ + PV_COMPONENT_PREVLINE, /* most recent complete line */ PV_COMPONENT__MAX } pv_display_component; @@ -56,6 +57,7 @@ typedef enum { #define PV_SIZEOF_DEFAULT_FORMAT 512 #define PV_SIZEOF_CWD 4096 #define PV_SIZEOF_LASTOUTPUT_BUFFER 256 +#define PV_SIZEOF_PREVLINE_BUFFER 1024 #define PV_FORMAT_ARRAY_MAX 100 #define PV_SIZEOF_CRS_LOCK_FILE 1024 @@ -202,13 +204,20 @@ struct pvstate_s { bool required; /* true if included in format */ } component[PV_COMPONENT__MAX]; + /* The last-output "n" bytes. */ char lastoutput_buffer[PV_SIZEOF_LASTOUTPUT_BUFFER]; + /* The most recently output complete line. */ + char previous_line[PV_SIZEOF_PREVLINE_BUFFER]; + /* The line being received now. */ + char next_line[PV_SIZEOF_PREVLINE_BUFFER]; + /*@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 */ off_t initial_offset; /* offset when first opened (when watching fds) */ size_t lastoutput_bytes; /* largest number of last-output bytes to show */ + size_t next_line_len; /* length of currently receiving line so far */ size_t format_segment_count; /* number of format string segments */ @@ -307,7 +316,7 @@ struct pvstate_s { off_t transferred; /* amount transferred (written - unconsumed) */ /* Keep track of line positions to backtrack written_but_not_consumed. */ - /*@null@*/ off_t *line_positions; /* line separator write positions (circular buffer) */ + /*@only@*/ /*@null@*/ off_t *line_positions; /* line separator write positions (circular buffer) */ size_t line_positions_capacity; /* total size of line position array */ size_t line_positions_length; /* number of positions stored in array */ size_t line_positions_head; /* index to use for next position */ diff --git a/src/pv/display.c b/src/pv/display.c index c020452..f5efee7 100644 --- a/src/pv/display.c +++ b/src/pv/display.c @@ -491,7 +491,8 @@ static void pv__format_init(pvstate_t state, /*@null@ */ const char *format_supp /* * Check for a numeric prefix between the % and the - * format character - currently only used with "%A". + * format character - currently only used with "%A" + * and "%L". */ #if HAVE_STRTOUL number_end_ptr = NULL; @@ -535,6 +536,12 @@ static void pv__format_init(pvstate_t state, /*@null@ */ const char *format_supp if (display->lastoutput_bytes < chosen_size) display->lastoutput_bytes = chosen_size; break; + case 'L': + seg_type = PV_COMPONENT_PREVLINE; + if (number_prefix > PV_SIZEOF_PREVLINE_BUFFER) + number_prefix = PV_SIZEOF_PREVLINE_BUFFER; + chosen_size = (size_t) number_prefix; + break; case 'r': seg_type = PV_COMPONENT_RATE; break; @@ -631,7 +638,7 @@ bool pv_format(pvstate_t state, /*@null@ */ const char *format_supplied, pvdispl bool final) { long eta; - int static_portion_size, dynamic_segment_count; + int static_portion_size, dynamic_segment_count, dynamic_segment_width; pv_display_component component_type; size_t segment; size_t new_display_string_len; @@ -1028,6 +1035,18 @@ bool pv_format(pvstate_t state, /*@null@ */ const char *format_supplied, pvdispl component_content[buf_idx] = '\0'; break; + case PV_COMPONENT_PREVLINE: + /* Most recent line. */ + for (buf_idx = 0; + buf_idx < PV_SIZEOF_PREVLINE_BUFFER - 1 && buf_idx < PV_SIZEOF_COMPONENT_STR - 1; + buf_idx++) { + int display_char; + display_char = (int) (display->previous_line[buf_idx]); + component_content[buf_idx] = isprint(display_char) ? (char) display_char : ' '; + } + component_content[buf_idx] = '\0'; + break; + default: break; } @@ -1054,6 +1073,10 @@ bool pv_format(pvstate_t state, /*@null@ */ const char *format_supplied, pvdispl } else if (display->format[segment].type == PV_COMPONENT_PROGRESS) { dynamic_segment_count++; debug("segment[%d] type:%d dynamic", segment, display->format[segment].type); + } else if (display->format[segment].type == PV_COMPONENT_PREVLINE + && 0 == display->format[segment].chosen_size) { + dynamic_segment_count++; + debug("segment[%d] type:%d dynamic", segment, display->format[segment].type); } else { size_t segment_width = display->format[segment].chosen_size; if (0 == segment_width) @@ -1066,6 +1089,14 @@ bool pv_format(pvstate_t state, /*@null@ */ const char *format_supplied, pvdispl debug("static_portion_size: %d", static_portion_size); debug("dynamic_segment_count: %d", dynamic_segment_count); + dynamic_segment_width = (int) (state->control.width) - static_portion_size; + /* + * Divide the total remaining screen space by the number of dynamic + * segments, so that multiple dynamic segments will share the space. + */ + if (dynamic_segment_count > 1) + dynamic_segment_width /= dynamic_segment_count; + /* * Assemble the progress bar now we know how big it should be. */ @@ -1073,7 +1104,7 @@ bool pv_format(pvstate_t state, /*@null@ */ const char *format_supplied, pvdispl char *component_content; size_t component_buf_size; char after_bar[32]; /* flawfinder: ignore - only populated by pv_snprintf(). */ - int component_width, bar_area_width, filled_bar_width, pad_count; + int bar_area_width, filled_bar_width, pad_count; component_content = display->component[PV_COMPONENT_PROGRESS].content; component_content[0] = '\0'; @@ -1081,13 +1112,6 @@ bool pv_format(pvstate_t state, /*@null@ */ const char *format_supplied, pvdispl memset(after_bar, 0, sizeof(after_bar)); - component_width = (int) (state->control.width) - static_portion_size; - /* - * Divide the total remaining screen space by the number of dynamic segments. - */ - if (dynamic_segment_count > 1) - component_width /= dynamic_segment_count; - if (state->control.size > 0 || state->control.rate_gauge) { /* * Known size, or rate gauge mode. Show a bar, and @@ -1125,7 +1149,7 @@ bool pv_format(pvstate_t state, /*@null@ */ const char *format_supplied, pvdispl after_bar_width = strlen(after_bar); /* flawfinder: ignore */ /* flawfinder: always \0-terminated by pv_snprintf() and the earlier memset(). */ - bar_area_width = component_width - (int) (after_bar_width) - 2; + bar_area_width = dynamic_segment_width - (int) (after_bar_width) - 2; if (bar_area_width < 0) bar_area_width = 0; @@ -1163,7 +1187,7 @@ bool pv_format(pvstate_t state, /*@null@ */ const char *format_supplied, pvdispl int indicator_position = state->calc.percentage; - bar_area_width = component_width - 5; + bar_area_width = dynamic_segment_width - 5; if (bar_area_width < 0) bar_area_width = 0; @@ -1237,16 +1261,26 @@ bool pv_format(pvstate_t state, /*@null@ */ const char *format_supplied, pvdispl segment_content = display->component[display->format[segment].type].content; segment_bytes = display->component[display->format[segment].type].bytes; + /* A chosen_size of 0 for PV_COMPONENT_PREVLINE means dynamic. */ + if (0 == chosen_size && PV_COMPONENT_PREVLINE == display->format[segment].type) + chosen_size = (size_t) dynamic_segment_width; + /* * If the segment's chosen size is smaller than the - * component, show only the last part of it. For + * component, show only the first or last part of + * it, depending on the component type. For * instance with "show N last output bytes" (%nA), * if one "n" was 16 and the other 8, then for the 8 * one, we show the last 8 bytes of the 16-byte - * buffer (issue #122). + * buffer (issue #122). With "show previous line", + * we show the first bytes. */ if (0 != chosen_size && chosen_size < segment_bytes) { - segment_content += (segment_bytes - chosen_size); + if (PV_COMPONENT_PREVLINE == display->format[segment].type) { + /* No need to move the segment content. */ + } else { + segment_content += (segment_bytes - chosen_size); + } segment_bytes = chosen_size; } } diff --git a/src/pv/transfer.c b/src/pv/transfer.c index b218be7..96d7165 100644 --- a/src/pv/transfer.c +++ b/src/pv/transfer.c @@ -718,28 +718,42 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long } if (nwritten > 0) { + bool tracking_lines = false; + + if ((state->control.linemode) && (lineswritten != NULL)) + tracking_lines = true; + else if (state->display.component[PV_COMPONENT_PREVLINE].required) + tracking_lines = true; + /* * Write returned >0 - data successfully written. */ - if ((state->control.linemode) && (lineswritten != NULL)) { + if (tracking_lines) { char separator; char *ptr; long lines = 0; /* - * Line mode - look through what we've just written - * to count how many lines there were. + * Tracking lines - either line mode, or we're + * showing the last line in the display, or both. + * So we need to look through what we've just + * written to either count how many lines there + * were, or get the content of the most recent + * complete line, or both. */ /* Allocate buffer to remember line positions. */ - if (NULL == state->transfer.line_positions) { + if (NULL == state->transfer.line_positions && NULL != lineswritten) { state->transfer.line_positions_capacity = MAX_LINE_POSITIONS; + /*@-mustfreeonly@ */ state->transfer.line_positions = calloc((size_t) (state->transfer.line_positions_capacity), sizeof(off_t)); if (NULL == state->transfer.line_positions) { pv_error(state, "%s: %s", _("line position buffer allocation failed"), strerror(errno)); } + /*@+mustfreeonly@ */ + /* splint doesn't see we only call calloc() when line_positions is NULL. */ } if (state->control.null_terminated_lines) { @@ -752,12 +766,50 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long for (ptr++; ptr - (char *) state->transfer.transfer_buffer - state->transfer.write_position < (size_t) nwritten; ptr++, state->transfer.last_output_position++) { - if (*ptr != separator) + if (*ptr != separator) { + /* + * If we're displaying the previous + * line ("%L"), add to our line + * buffer. + */ + if (state->display.component[PV_COMPONENT_PREVLINE].required + && state->display.next_line_len < PV_SIZEOF_PREVLINE_BUFFER - 1) { + state->display.next_line[state->display.next_line_len] = *ptr; + state->display.next_line_len++; + } continue; + } /* Separator found - increment line count. */ ++lines; + /* + * If we're displaying the previous line + * ("%L"), update the previous-line buffer + * with the line we just completed, and + * start a new one. + */ + if (state->display.component[PV_COMPONENT_PREVLINE].required) { + memset(state->display.previous_line, 0, PV_SIZEOF_PREVLINE_BUFFER); + if (state->display.next_line_len > PV_SIZEOF_PREVLINE_BUFFER - 1) + state->display.next_line_len = PV_SIZEOF_PREVLINE_BUFFER - 1; + if (state->display.next_line_len > 0) { + memcpy(state->display.previous_line, state->display.next_line, /* flawfinder: ignore */ + state->display.next_line_len); + debug("%s: [%s]", "updated previous_line", + state->display.previous_line); + } + state->display.next_line_len = 0; + /* + * flawfinder - next_line_len is + * guaranteed to be less than the + * size of the previous_line buffer + * since we check it just before + * memcpy(), and we ensure that the + * last byte in the buffer is \0. + */ + } + if (NULL == state->transfer.line_positions) continue; @@ -777,7 +829,8 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long } } - *lineswritten += lines; + if (NULL != lineswritten) + *lineswritten += lines; } state->transfer.write_position += nwritten;