New bar formats - default, block, granular, shaded - with the granular format providing a smoother bar using Unicode block characters (#15)

This commit is contained in:
Andrew Wood
2024-12-13 20:02:36 +00:00
parent c49abf1608
commit 60563f3e3c
13 changed files with 439 additions and 127 deletions
+1
View File
@@ -1,6 +1,7 @@
### UNRELEASED
* *feature:* allow decimal values such as "1.5G" with "**--size**", "**--rate-limit**", "**--buffer-size**", and "**error-skip-block**" ([#35](https://codeberg.org/a-j-wood/pv/issues/35))
* *feature:* new bar formats "**%{bar-default}**", "**%{bar-block}**", "**%{bar-granular}**", "**%{bar-shaded}**"
* *docs:* simplified the synopsis section of the manual
### 1.9.15 - 8 December 2024
+8 -8
View File
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: pv@ivarch.com\n"
"POT-Creation-Date: 2024-12-12 22:09+0000\n"
"POT-Creation-Date: 2024-12-13 19:54+0000\n"
"PO-Revision-Date: 2024-10-13 20:43+0000\n"
"Last-Translator: mmatous <mmatous@users.noreply.translate.codeberg.org>\n"
"Language-Team: Czech <https://translate.codeberg.org/projects/pv/pv/cs/>\n"
@@ -411,7 +411,7 @@ msgstr "yzafpnum kMGTPEZY"
msgid "yzafpnum KMGTPEZY"
msgstr "yzafpnum KMGTPEZY"
#: src/pv/display.c:914 src/pv/transfer.c:1056
#: src/pv/display.c:919 src/pv/transfer.c:1056
msgid "buffer allocation failed"
msgstr "selhala alokace vyrovnávací paměti"
@@ -443,17 +443,17 @@ msgstr "(žádný)"
msgid "(stdin)"
msgstr "(stdin)"
#: src/pv/format/averagerate.c:30 src/pv/format/progressbar.c:59
#: src/pv/format/averagerate.c:30 src/pv/format/progressbar.c:87
#: src/pv/format/rate.c:32 src/pv/loop.c:549
msgid "b/s"
msgstr "b/s"
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:63
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:91
#: src/pv/format/rate.c:36
msgid "/s"
msgstr "/s"
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:63
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:91
#: src/pv/format/rate.c:36 src/pv/loop.c:549
msgid "B/s"
msgstr "B/s"
@@ -462,15 +462,15 @@ msgstr "B/s"
msgid "b"
msgstr "b"
#: src/pv/format/bytes.c:34 src/pv/transfer.c:594
#: src/pv/format/bytes.c:35 src/pv/transfer.c:594
msgid "B"
msgstr "B"
#: src/pv/format/eta.c:52 src/pv/format/eta.c:56
#: src/pv/format/eta.c:53 src/pv/format/eta.c:57
msgid "ETA"
msgstr "ETA"
#: src/pv/format/fineta.c:80
#: src/pv/format/fineta.c:81
msgid "FIN"
msgstr "FIN"
+8 -8
View File
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: pv@ivarch.com\n"
"POT-Creation-Date: 2024-12-12 22:09+0000\n"
"POT-Creation-Date: 2024-12-13 19:54+0000\n"
"PO-Revision-Date: 2024-10-13 20:43+0000\n"
"Last-Translator: fnetX <otto@codeberg.org>\n"
"Language-Team: German <https://translate.codeberg.org/projects/pv/pv/de/>\n"
@@ -434,7 +434,7 @@ msgstr "yzafpnµm kMGTPEZY"
msgid "yzafpnum KMGTPEZY"
msgstr "yzafpnµm KMGTPEZY"
#: src/pv/display.c:914 src/pv/transfer.c:1056
#: src/pv/display.c:919 src/pv/transfer.c:1056
msgid "buffer allocation failed"
msgstr "Speicherzuweisung für den Puffer fehlgeschlagen"
@@ -466,17 +466,17 @@ msgstr "(keine)"
msgid "(stdin)"
msgstr "(stdin)"
#: src/pv/format/averagerate.c:30 src/pv/format/progressbar.c:59
#: src/pv/format/averagerate.c:30 src/pv/format/progressbar.c:87
#: src/pv/format/rate.c:32 src/pv/loop.c:549
msgid "b/s"
msgstr "b/s"
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:63
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:91
#: src/pv/format/rate.c:36
msgid "/s"
msgstr "/s"
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:63
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:91
#: src/pv/format/rate.c:36 src/pv/loop.c:549
msgid "B/s"
msgstr "B/s"
@@ -485,15 +485,15 @@ msgstr "B/s"
msgid "b"
msgstr "b"
#: src/pv/format/bytes.c:34 src/pv/transfer.c:594
#: src/pv/format/bytes.c:35 src/pv/transfer.c:594
msgid "B"
msgstr "B"
#: src/pv/format/eta.c:52 src/pv/format/eta.c:56
#: src/pv/format/eta.c:53 src/pv/format/eta.c:57
msgid "ETA"
msgstr "ETA"
#: src/pv/format/fineta.c:80
#: src/pv/format/fineta.c:81
msgid "FIN"
msgstr "Endzeit"
+8 -8
View File
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: pv@ivarch.com\n"
"POT-Creation-Date: 2024-12-12 22:09+0000\n"
"POT-Creation-Date: 2024-12-13 19:54+0000\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -403,7 +403,7 @@ msgstr ""
msgid "yzafpnum KMGTPEZY"
msgstr ""
#: src/pv/display.c:914 src/pv/transfer.c:1056
#: src/pv/display.c:919 src/pv/transfer.c:1056
msgid "buffer allocation failed"
msgstr ""
@@ -435,17 +435,17 @@ msgstr ""
msgid "(stdin)"
msgstr ""
#: src/pv/format/averagerate.c:30 src/pv/format/progressbar.c:59
#: src/pv/format/averagerate.c:30 src/pv/format/progressbar.c:87
#: src/pv/format/rate.c:32 src/pv/loop.c:549
msgid "b/s"
msgstr ""
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:63
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:91
#: src/pv/format/rate.c:36
msgid "/s"
msgstr ""
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:63
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:91
#: src/pv/format/rate.c:36 src/pv/loop.c:549
msgid "B/s"
msgstr ""
@@ -454,15 +454,15 @@ msgstr ""
msgid "b"
msgstr ""
#: src/pv/format/bytes.c:34 src/pv/transfer.c:594
#: src/pv/format/bytes.c:35 src/pv/transfer.c:594
msgid "B"
msgstr ""
#: src/pv/format/eta.c:52 src/pv/format/eta.c:56
#: src/pv/format/eta.c:53 src/pv/format/eta.c:57
msgid "ETA"
msgstr ""
#: src/pv/format/fineta.c:80
#: src/pv/format/fineta.c:81
msgid "FIN"
msgstr ""
+8 -8
View File
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: pv@ivarch.com\n"
"POT-Creation-Date: 2024-12-12 22:09+0000\n"
"POT-Creation-Date: 2024-12-13 19:54+0000\n"
"PO-Revision-Date: 2024-10-12 11:13+0000\n"
"Last-Translator: a-j-wood <a-j-wood@users.noreply.translate.codeberg.org>\n"
"Language-Team: French <https://translate.codeberg.org/projects/pv/pv/fr/>\n"
@@ -438,7 +438,7 @@ msgstr ""
msgid "yzafpnum KMGTPEZY"
msgstr ""
#: src/pv/display.c:914 src/pv/transfer.c:1056
#: src/pv/display.c:919 src/pv/transfer.c:1056
msgid "buffer allocation failed"
msgstr "échec de l'allocation de mémoire tampon"
@@ -470,17 +470,17 @@ msgstr "(aucun)"
msgid "(stdin)"
msgstr ""
#: src/pv/format/averagerate.c:30 src/pv/format/progressbar.c:59
#: src/pv/format/averagerate.c:30 src/pv/format/progressbar.c:87
#: src/pv/format/rate.c:32 src/pv/loop.c:549
msgid "b/s"
msgstr ""
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:63
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:91
#: src/pv/format/rate.c:36
msgid "/s"
msgstr "/s"
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:63
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:91
#: src/pv/format/rate.c:36 src/pv/loop.c:549
msgid "B/s"
msgstr "O/s"
@@ -489,15 +489,15 @@ msgstr "O/s"
msgid "b"
msgstr ""
#: src/pv/format/bytes.c:34 src/pv/transfer.c:594
#: src/pv/format/bytes.c:35 src/pv/transfer.c:594
msgid "B"
msgstr "O"
#: src/pv/format/eta.c:52 src/pv/format/eta.c:56
#: src/pv/format/eta.c:53 src/pv/format/eta.c:57
msgid "ETA"
msgstr "ETA"
#: src/pv/format/fineta.c:80
#: src/pv/format/fineta.c:81
msgid "FIN"
msgstr ""
+8 -8
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-12-12 22:09+0000\n"
"POT-Creation-Date: 2024-12-13 19:54+0000\n"
"PO-Revision-Date: 2024-10-20 14:07+0000\n"
"Last-Translator: coralpink <coralpink@users.noreply.translate.codeberg.org>\n"
"Language-Team: Polish <https://translate.codeberg.org/projects/pv/pv/pl/>\n"
@@ -429,7 +429,7 @@ msgstr "yzafpnum kMGTPEZY"
msgid "yzafpnum KMGTPEZY"
msgstr "yzafpnum KMGTPEZY"
#: src/pv/display.c:914 src/pv/transfer.c:1056
#: src/pv/display.c:919 src/pv/transfer.c:1056
msgid "buffer allocation failed"
msgstr "nie udało się zaalokować bufora"
@@ -462,17 +462,17 @@ msgstr "(brak)"
msgid "(stdin)"
msgstr "(stdin)"
#: src/pv/format/averagerate.c:30 src/pv/format/progressbar.c:59
#: src/pv/format/averagerate.c:30 src/pv/format/progressbar.c:87
#: src/pv/format/rate.c:32 src/pv/loop.c:549
msgid "b/s"
msgstr "b/s"
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:63
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:91
#: src/pv/format/rate.c:36
msgid "/s"
msgstr "/s"
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:63
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:91
#: src/pv/format/rate.c:36 src/pv/loop.c:549
msgid "B/s"
msgstr "B/s"
@@ -481,15 +481,15 @@ msgstr "B/s"
msgid "b"
msgstr "b"
#: src/pv/format/bytes.c:34 src/pv/transfer.c:594
#: src/pv/format/bytes.c:35 src/pv/transfer.c:594
msgid "B"
msgstr "B"
#: src/pv/format/eta.c:52 src/pv/format/eta.c:56
#: src/pv/format/eta.c:53 src/pv/format/eta.c:57
msgid "ETA"
msgstr "ETA"
#: src/pv/format/fineta.c:80
#: src/pv/format/fineta.c:81
msgid "FIN"
msgstr "FIN"
+8 -8
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-12-12 22:09+0000\n"
"POT-Creation-Date: 2024-12-13 19:54+0000\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"
@@ -417,7 +417,7 @@ msgstr ""
msgid "yzafpnum KMGTPEZY"
msgstr ""
#: src/pv/display.c:914 src/pv/transfer.c:1056
#: src/pv/display.c:919 src/pv/transfer.c:1056
msgid "buffer allocation failed"
msgstr "erro alocando o buffer"
@@ -449,17 +449,17 @@ msgstr ""
msgid "(stdin)"
msgstr ""
#: src/pv/format/averagerate.c:30 src/pv/format/progressbar.c:59
#: src/pv/format/averagerate.c:30 src/pv/format/progressbar.c:87
#: src/pv/format/rate.c:32 src/pv/loop.c:549
msgid "b/s"
msgstr ""
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:63
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:91
#: src/pv/format/rate.c:36
msgid "/s"
msgstr ""
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:63
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:91
#: src/pv/format/rate.c:36 src/pv/loop.c:549
msgid "B/s"
msgstr "B/s"
@@ -468,15 +468,15 @@ msgstr "B/s"
msgid "b"
msgstr ""
#: src/pv/format/bytes.c:34 src/pv/transfer.c:594
#: src/pv/format/bytes.c:35 src/pv/transfer.c:594
msgid "B"
msgstr ""
#: src/pv/format/eta.c:52 src/pv/format/eta.c:56
#: src/pv/format/eta.c:53 src/pv/format/eta.c:57
msgid "ETA"
msgstr "ETA"
#: src/pv/format/fineta.c:80
#: src/pv/format/fineta.c:81
msgid "FIN"
msgstr ""
+8 -8
View File
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: pv@ivarch.com\n"
"POT-Creation-Date: 2024-12-12 22:09+0000\n"
"POT-Creation-Date: 2024-12-13 19:54+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 <https://translate.codeberg.org/projects/pv/pv/ru/>\n"
@@ -412,7 +412,7 @@ msgstr ""
msgid "yzafpnum KMGTPEZY"
msgstr ""
#: src/pv/display.c:914 src/pv/transfer.c:1056
#: src/pv/display.c:919 src/pv/transfer.c:1056
msgid "buffer allocation failed"
msgstr "не удалось выделить память для буфера"
@@ -444,17 +444,17 @@ msgstr "(нет)"
msgid "(stdin)"
msgstr "(стд. ввод)"
#: src/pv/format/averagerate.c:30 src/pv/format/progressbar.c:59
#: src/pv/format/averagerate.c:30 src/pv/format/progressbar.c:87
#: src/pv/format/rate.c:32 src/pv/loop.c:549
msgid "b/s"
msgstr "бит/с"
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:63
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:91
#: src/pv/format/rate.c:36
msgid "/s"
msgstr "/с"
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:63
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:91
#: src/pv/format/rate.c:36 src/pv/loop.c:549
msgid "B/s"
msgstr "байт/с"
@@ -463,15 +463,15 @@ msgstr "байт/с"
msgid "b"
msgstr "бит"
#: src/pv/format/bytes.c:34 src/pv/transfer.c:594
#: src/pv/format/bytes.c:35 src/pv/transfer.c:594
msgid "B"
msgstr "байт"
#: src/pv/format/eta.c:52 src/pv/format/eta.c:56
#: src/pv/format/eta.c:53 src/pv/format/eta.c:57
msgid "ETA"
msgstr "ОЖИД"
#: src/pv/format/fineta.c:80
#: src/pv/format/fineta.c:81
msgid "FIN"
msgstr "ЗАВЕРШ"
+8 -8
View File
@@ -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-12 22:09+0000\n"
"POT-Creation-Date: 2024-12-13 19:54+0000\n"
"PO-Revision-Date: 2024-10-12 23:51+0000\n"
"Last-Translator: a-j-wood <a-j-wood@users.noreply.translate.codeberg.org>\n"
"Language-Team: Turkish <https://translate.codeberg.org/projects/pv/pv/tr/>\n"
@@ -413,7 +413,7 @@ msgstr "yzafpnum kMGTPEZY"
msgid "yzafpnum KMGTPEZY"
msgstr "yzafpnum KMGTPEZY"
#: src/pv/display.c:914 src/pv/transfer.c:1056
#: src/pv/display.c:919 src/pv/transfer.c:1056
msgid "buffer allocation failed"
msgstr "arabellek tahsisi başarısız oldu"
@@ -445,17 +445,17 @@ msgstr "(yok)"
msgid "(stdin)"
msgstr "(stdin)"
#: src/pv/format/averagerate.c:30 src/pv/format/progressbar.c:59
#: src/pv/format/averagerate.c:30 src/pv/format/progressbar.c:87
#: src/pv/format/rate.c:32 src/pv/loop.c:549
msgid "b/s"
msgstr "b/s"
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:63
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:91
#: src/pv/format/rate.c:36
msgid "/s"
msgstr "/s"
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:63
#: src/pv/format/averagerate.c:34 src/pv/format/progressbar.c:91
#: src/pv/format/rate.c:36 src/pv/loop.c:549
msgid "B/s"
msgstr "B/s"
@@ -464,15 +464,15 @@ msgstr "B/s"
msgid "b"
msgstr "b"
#: src/pv/format/bytes.c:34 src/pv/transfer.c:594
#: src/pv/format/bytes.c:35 src/pv/transfer.c:594
msgid "B"
msgstr "B"
#: src/pv/format/eta.c:52 src/pv/format/eta.c:56
#: src/pv/format/eta.c:53 src/pv/format/eta.c:57
msgid "ETA"
msgstr "ETA"
#: src/pv/format/fineta.c:80
#: src/pv/format/fineta.c:81
msgid "FIN"
msgstr "FIN"
+61
View File
@@ -9,6 +9,9 @@
#ifndef _PV_INTERNAL_H
#define _PV_INTERNAL_H 1
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <stdlib.h>
#include <signal.h>
#include <time.h>
@@ -45,6 +48,10 @@ extern "C" {
#define PV_SIZEOF_FILE_FDPATH 4096
#define PV_SIZEOF_DISPLAY_NAME 512
#define PV_BARSTYLE_MAX 4 /* number of different styles allowed in a format */
#define PV_BARSTYLE_SIZEOF_STRING 10 /* max length of a bar constituent component in bytes */
#define PV_BARSTYLE_MAX_FILLERS 10 /* max number of bar filler strings */
#define PV_DISPLAY_WINDOWTITLE 1
#define PV_DISPLAY_PROCESSTITLE 2
@@ -67,6 +74,50 @@ typedef enum {
} pvtransfercount_t;
/*
* Structure describing a short string used as part of a progress bar, whose
* width in display characters may not be the same as its length in bytes.
*/
struct pvbarstring_spec_s {
char string[PV_BARSTYLE_SIZEOF_STRING];
uint8_t width;
uint8_t bytes;
};
/*
* Structure describing a style of progress bar.
*
* The part that moves back and forth when the size isn't known, such as
* "<=>", is "indicator".
*
* The string used to populate the rest of the bar is one of "filler", which
* contains "filler_entries" array items. The first item "filler[0]" is
* used for an empty part such as " ". The last item
* "filler[filler_entries-1]" is used for a 100% full part, such as "=".
*
* If there are more than the minimum of 2 entries, then the intervening
* entries are used for partial fills.
*
* When there are only 2 entries and the bar isn't completely empty or full,
* the last filled portion of the bar is filled with "tip" instead of the
* last filler item to indicate the tip of the bar, such as ">".
*
* Note that only the indicator is expected to ever be wider than 1 display
* character. All other items are expected to have a width of 0 or 1.
*
* The "style_id" is an opaque identifier that should only be used to
* determine whether two styles are the same. It is always nonzero.
*/
struct pvbarstyle_s {
uint8_t style_id;
uint8_t filler_entries;
struct pvbarstring_spec_s indicator;
struct pvbarstring_spec_s tip;
struct pvbarstring_spec_s filler[PV_BARSTYLE_MAX_FILLERS];
};
typedef struct pvbarstyle_s *pvbarstyle_t;
/*
* Structure for holding PV internal state. Opaque outside the PV library.
*
@@ -183,12 +234,15 @@ struct pvstate_s {
struct pvdisplay_segment_s { /* format string broken into segments */
/* See pv__format_init() for more details. */
int type; /* component type, -1 for static string */
int parameter; /* component parameter, such as bar style index */
size_t chosen_size; /* "n" from %<n>A, or 0 */
size_t offset; /* start offset of this segment */
size_t bytes; /* length of segment in bytes */
size_t width; /* displayed width of segment */
} format[PV_FORMAT_ARRAY_MAX];
struct pvbarstyle_s barstyle[PV_BARSTYLE_MAX];
/* The last-written "n" bytes. */
char lastwritten_buffer[PV_SIZEOF_LASTWRITTEN_BUFFER];
@@ -412,6 +466,9 @@ long pv_seconds_remaining(const off_t, const off_t, const long double);
void pv_si_prefix(long double *, char *, const long double, pvtransfercount_t);
void pv_describe_amount(char *, size_t, char *, long double, char *, char *, pvtransfercount_t);
bool pv_barstyle(pvbarstyle_t, const char *);
int pv_display_barstyle_index(pvdisplay_t, const char *);
size_t pv_formatter_segmentcontent(char *, pvformatter_args_t);
/*
@@ -438,6 +495,10 @@ size_t pv_formatter_segmentcontent(char *, pvformatter_args_t);
size_t pv_formatter_progress(pvformatter_args_t);
size_t pv_formatter_progress_bar_only(pvformatter_args_t);
size_t pv_formatter_progress_amount_only(pvformatter_args_t);
size_t pv_formatter_bar_default(pvformatter_args_t);
size_t pv_formatter_bar_block(pvformatter_args_t);
size_t pv_formatter_bar_granular(pvformatter_args_t);
size_t pv_formatter_bar_shaded(pvformatter_args_t);
size_t pv_formatter_timer(pvformatter_args_t);
size_t pv_formatter_eta(pvformatter_args_t);
size_t pv_formatter_fineta(pvformatter_args_t);
+6 -1
View File
@@ -539,8 +539,13 @@ static bool pv__format_numeric(pvstate_t state, pvdisplay_t display)
/*@keep@ */ static struct pvdisplay_component_s format_component_array[] = {
{ "p", &pv_formatter_progress, true },
{ "{progress}", &pv_formatter_progress, true },
{ "{progress-bar-only}", &pv_formatter_progress_bar_only, true },
{ "{progress-amount-only}", &pv_formatter_progress_amount_only, false },
{ "{progress-bar-only}", &pv_formatter_bar_default, true },
{ "{bar}", &pv_formatter_bar_default, true },
{ "{bar-default}", &pv_formatter_bar_default, true },
{ "{bar-block}", &pv_formatter_bar_block, true },
{ "{bar-granular}", &pv_formatter_bar_granular, true },
{ "{bar-shaded}", &pv_formatter_bar_shaded, true },
{ "t", &pv_formatter_timer, false },
{ "{timer}", &pv_formatter_timer, false },
{ "e", &pv_formatter_eta, false },
+188
View File
@@ -0,0 +1,188 @@
/*
* Formatter functions for styled progress bars.
*
* Copyright 2024 Andrew Wood
*
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
*/
#include "config.h"
#include "pv.h"
#include "pv-internal.h"
#include <string.h>
#if defined(ENABLE_NLS) && defined(HAVE_WCHAR_H)
#include <wchar.h>
#if defined(HAVE_WCTYPE_H)
#include <wctype.h>
#endif
#endif
/*
* Populate "style" with the named bar style, falling back to the default if
* the name was not recognised. Returns true if the named style was found.
*
* Note that strings are copied into the structure, rather than just
* updating pointers, to maintain separation of concern between different
* parts of the code - and because on 64-bit architectures, the sizes of the
* data types and their associated alignment padding tend to outweigh the
* memory savings from using pointers and shared strings.
*/
bool pv_barstyle(pvbarstyle_t style, const char *name)
{
#define populate_string(item, str, w) { \
item.width = w; \
item.bytes = strlen(str); /* flawfinder: ignore */ \
if (item.bytes > 0 && item.bytes <= PV_BARSTYLE_SIZEOF_STRING) \
memcpy(item.string, str, item.bytes); /* flawfinder: ignore */ \
}
/*
* flawfinder - strlen() on null-terminated static strings is OK,
* and with the memcpy(), we check the buffer is big enough.
*/
memset(style, 0, sizeof(*style));
if (0 == strcmp(name, "block")) {
style->style_id = 2;
populate_string(style->indicator, "◀▶", 2);
populate_string(style->tip, "", 0);
populate_string(style->filler[0], " ", 1);
populate_string(style->filler[1], "", 1);
style->filler_entries = 2;
return true;
} else if (0 == strcmp(name, "granular")) {
style->style_id = 3;
populate_string(style->indicator, "◀▶", 2);
populate_string(style->tip, "", 0);
populate_string(style->filler[0], " ", 1);
populate_string(style->filler[1], "", 1);
populate_string(style->filler[2], "", 1);
populate_string(style->filler[3], "", 1);
populate_string(style->filler[4], "", 1);
populate_string(style->filler[5], "", 1);
populate_string(style->filler[6], "", 1);
populate_string(style->filler[7], "", 1);
populate_string(style->filler[8], "", 1);
style->filler_entries = 9;
return true;
} else if (0 == strcmp(name, "shaded")) {
style->style_id = 4;
populate_string(style->indicator, "▒▓▒", 3);
populate_string(style->tip, "", 0);
populate_string(style->filler[0], "", 1);
populate_string(style->filler[1], "", 1);
populate_string(style->filler[2], "", 1);
populate_string(style->filler[3], "", 1);
style->filler_entries = 4;
return true;
}
/* Default style. */
style->style_id = 1;
populate_string(style->indicator, "<=>", 3);
populate_string(style->tip, ">", 1);
populate_string(style->filler[0], " ", 1);
populate_string(style->filler[1], "=", 1);
style->filler_entries = 2;
if (0 == strcmp(name, "default"))
return true;
return false;
}
/*
* Return the index into display->barstyle for the style with the given
* name, adding that style to the array if it's not there already and
* there's room.
*
* If there is no room, returns zero, so the first style is re-used.
*/
int pv_display_barstyle_index(pvdisplay_t display, const char *name)
{
struct pvbarstyle_s style;
int barstyle_index;
#ifdef ENABLE_DEBUGGING
bool found;
#endif
memset(&style, 0, sizeof(style));
#ifdef ENABLE_DEBUGGING
found = pv_barstyle(&style, name);
if (!found)
debug("%s: %s", name, "bar style not found, using default");
#else
(void) pv_barstyle(&style, name);
#endif
for (barstyle_index = 0; barstyle_index < PV_BARSTYLE_MAX && display->barstyle[barstyle_index].style_id > 0;
barstyle_index++) {
if (display->barstyle[barstyle_index].style_id == style.style_id) {
debug("%s: %s: %d", name, "found in bar style array", barstyle_index);
return barstyle_index;
}
}
if (barstyle_index >= PV_BARSTYLE_MAX) {
debug("%s: %s", name, "no room to add another bar style - returning 0");
return 0;
}
memcpy(&(display->barstyle[barstyle_index]), &style, sizeof(style)); /* flawfinder: ignore */
/* flawfinder - the destination is an array element of the right size. */
debug("%s: %s: %d", name, "added to bar style array", barstyle_index);
return barstyle_index;
}
size_t pv_formatter_bar_default(pvformatter_args_t args)
{
if (0 == args->segment->parameter)
args->segment->parameter = 1 + pv_display_barstyle_index(args->display, "default");
return pv_formatter_progress_bar_only(args);
}
size_t pv_formatter_bar_block(pvformatter_args_t args)
{
if (0 == args->segment->parameter)
args->segment->parameter = 1 + pv_display_barstyle_index(args->display, "block");
return pv_formatter_progress_bar_only(args);
}
size_t pv_formatter_bar_granular(pvformatter_args_t args)
{
if (0 == args->segment->parameter)
args->segment->parameter = 1 + pv_display_barstyle_index(args->display, "granular");
return pv_formatter_progress_bar_only(args);
}
size_t pv_formatter_bar_shaded(pvformatter_args_t args)
{
if (0 == args->segment->parameter)
args->segment->parameter = 1 + pv_display_barstyle_index(args->display, "shaded");
return pv_formatter_progress_bar_only(args);
}
+119 -62
View File
@@ -12,6 +12,18 @@
#include <string.h>
/* Convenience macro for appending a string to the buffer. */
#define append_to_buffer(x) { \
if (buffer_offset < (buffer_size - x.bytes)) { \
memcpy(&(buffer[buffer_offset]), x.string, x.bytes); /* flawfinder: ignore */ \
buffer_offset += x.bytes; \
} \
}
/*
* flawfinder - we are checking that there is room in the destination
* buffer, given its size and our current offset.
*/
/*
* Write a progress bar to a buffer, in known-size or rate-gauge mode - a
@@ -29,38 +41,55 @@
*
* This is only called by pv_formatter_progress().
*/
static size_t pv_formatter_progress_knownsize(pvstate_t state, pvdisplay_t display, char *buffer, size_t buffer_size,
size_t width, bool bar_sides, bool include_bar, bool include_amount)
static size_t pv_formatter_progress_knownsize(pvformatter_args_t args, char *buffer, size_t buffer_size, bool bar_sides,
bool include_bar, bool include_amount)
{
char after_bar[32]; /* flawfinder: ignore - only populated by pv_snprintf(). */
size_t after_bar_bytes, after_bar_width;
size_t bar_area_width, filled_bar_width, buffer_offset, pad_count;
int bar_percentage;
double bar_percentage;
pvbarstyle_t style;
size_t full_cell_index;
bool has_tip = false;
buffer[0] = '\0';
if (args->segment->parameter > 0 && args->segment->parameter <= PV_BARSTYLE_MAX) {
style = &(args->display->barstyle[args->segment->parameter - 1]);
} else {
style = &(args->display->barstyle[0]);
}
full_cell_index = style->filler_entries;
if (full_cell_index > 0)
full_cell_index--;
if (1 == full_cell_index && style->tip.width > 0)
has_tip = true;
memset(after_bar, 0, sizeof(after_bar));
if (state->control.size > 0) {
if (args->state->control.size > 0) {
/* Percentage of data transferred. */
bar_percentage = state->calc.percentage;
(void) pv_snprintf(after_bar, sizeof(after_bar), " %3ld%%", bar_percentage);
bar_percentage = (double) (args->state->calc.percentage);
(void) pv_snprintf(after_bar, sizeof(after_bar), " %3ld%%", (int) bar_percentage);
} else {
/* Current rate vs max rate. */
bar_percentage = 0;
if (state->calc.rate_max > 0) {
bar_percentage = (int) (100.0 * state->calc.transfer_rate / state->calc.rate_max);
bar_percentage = 0.0;
if (args->state->calc.rate_max > 0) {
bar_percentage =
(double) (100.0 * args->state->calc.transfer_rate / args->state->calc.rate_max);
}
/*@-mustfreefresh@ */
if (state->control.bits && !state->control.linemode) {
if (args->state->control.bits && !args->state->control.linemode) {
/* bits per second */
pv_describe_amount(after_bar, sizeof(after_bar), "/%s",
8.0 * state->calc.rate_max, "", _("b/s"), display->count_type);
8.0 * args->state->calc.rate_max, "", _("b/s"), args->display->count_type);
} else {
/* bytes or lines per second */
pv_describe_amount(after_bar, sizeof(after_bar),
"/%s", state->calc.rate_max, _("/s"), _("B/s"), display->count_type);
"/%s", args->state->calc.rate_max, _("/s"), _("B/s"),
args->display->count_type);
}
/*@+mustfreefresh@ *//* splint: see above about gettext(). */
}
@@ -73,6 +102,10 @@ static size_t pv_formatter_progress_knownsize(pvstate_t state, pvdisplay_t displ
after_bar_width = pv_strwidth(after_bar, after_bar_bytes);
if (!include_bar) {
/*
* Only returning the "after bar" portion - the amount
* (progress or max rate).
*/
if (buffer_size < after_bar_bytes)
return 0;
if (after_bar_bytes > 1) {
@@ -85,24 +118,28 @@ static size_t pv_formatter_progress_knownsize(pvstate_t state, pvdisplay_t displ
}
if (bar_sides) {
if (width < (after_bar_width + 2))
if (args->segment->width < (after_bar_width + 2))
return 0;
bar_area_width = width - after_bar_width - 2;
bar_area_width = args->segment->width - after_bar_width - 2;
} else {
if (width < after_bar_width)
if (args->segment->width < after_bar_width)
return 0;
bar_area_width = width - after_bar_width;
bar_area_width = args->segment->width - after_bar_width;
}
if (bar_area_width > buffer_size - 16)
bar_area_width = buffer_size - 16;
/*
* Make sure there is enough room in the buffer, even if all of the
* bar characters are very wide.
*/
if (4 * bar_area_width > buffer_size - 16)
bar_area_width = (buffer_size - 16) / 4;
filled_bar_width = (size_t) (bar_area_width * bar_percentage) / 100;
filled_bar_width = (size_t) ((bar_area_width * bar_percentage) / 100);
/* Leave room for the tip of the bar. */
if (filled_bar_width > 0)
if (has_tip && filled_bar_width > 0)
filled_bar_width--;
debug("width=%d bar_area_width=%d filled_bar_width=%d", width, bar_area_width, filled_bar_width);
debug("width=%d bar_area_width=%d filled_bar_width=%d", args->segment->width, bar_area_width, filled_bar_width);
buffer_offset = 0;
@@ -112,22 +149,35 @@ static size_t pv_formatter_progress_knownsize(pvstate_t state, pvdisplay_t displ
}
/* The bar portion. */
for (pad_count = 0; pad_count < filled_bar_width && buffer_offset < buffer_size - 1; pad_count++) {
if (pad_count < bar_area_width)
buffer[buffer_offset++] = '=';
for (pad_count = 0; pad_count < filled_bar_width && buffer_offset < buffer_size - 4; pad_count++) {
if (pad_count < bar_area_width) {
append_to_buffer(style->filler[full_cell_index]);
}
}
/* The tip of the bar, if not at 100%. */
if (pad_count < bar_area_width) {
if (buffer_offset < buffer_size - 1)
buffer[buffer_offset++] = '>';
if (has_tip && pad_count < bar_area_width) {
append_to_buffer(style->tip);
pad_count++;
}
/* A partial cell, if there are intermediates and we're not at 100%. */
if (pad_count < bar_area_width && full_cell_index > 1 && !has_tip) {
double exact_width = (((double) bar_area_width) * bar_percentage) / 100.0;
double cell_portion = exact_width - (double) filled_bar_width;
double cell_index_double = ((double) full_cell_index) * cell_portion;
size_t cell_index = (size_t) cell_index_double;
if (cell_index > full_cell_index)
cell_index = full_cell_index;
append_to_buffer(style->filler[cell_index]);
pad_count++;
}
/* The spaces after the bar. */
for (; pad_count < bar_area_width; pad_count++) {
if (buffer_offset < buffer_size - 1)
buffer[buffer_offset++] = ' ';
append_to_buffer(style->filler[0]);
}
if (bar_sides) {
@@ -158,27 +208,37 @@ static size_t pv_formatter_progress_knownsize(pvstate_t state, pvdisplay_t displ
*
* This is only called by pv_formatter_progress().
*/
static size_t pv_formatter_progress_unknownsize(pvstate_t state, /*@unused@ */
__attribute__((unused)) pvdisplay_t display, char *buffer,
size_t buffer_size, size_t width, bool bar_sides)
static size_t pv_formatter_progress_unknownsize(pvformatter_args_t args, char *buffer,
size_t buffer_size, bool bar_sides)
{
size_t bar_area_width, buffer_offset, pad_count;
size_t indicator_position;
pvbarstyle_t style;
buffer[0] = '\0';
if (bar_sides) {
if (width < 6)
return 0;
bar_area_width = width - 5;
if (args->segment->parameter > 0 && args->segment->parameter <= PV_BARSTYLE_MAX) {
style = &(args->display->barstyle[args->segment->parameter - 1]);
} else {
if (width < 5)
return 0;
bar_area_width = width - 3;
style = &(args->display->barstyle[0]);
}
if (bar_area_width > buffer_size - 16)
bar_area_width = buffer_size - 16;
if (bar_sides) {
if (args->segment->width < (style->indicator.width + 3))
return 0;
bar_area_width = args->segment->width - (style->indicator.width + 2);
} else {
if (args->segment->width < (style->indicator.width + 2))
return 0;
bar_area_width = args->segment->width - style->indicator.width;
}
/*
* Make sure there is enough room in the buffer, even if all of the
* bar characters are very wide.
*/
if (4 * bar_area_width > buffer_size - 16)
bar_area_width = (buffer_size - 16) / 4;
/*
* Note that pv_calculate_transfer_rate() sets the percentage when
@@ -186,7 +246,7 @@ static size_t pv_formatter_progress_unknownsize(pvstate_t state, /*@unused@ */
* here we make values above 100 send the indicator back down again,
* so it moves back and forth.
*/
indicator_position = (size_t) (state->calc.percentage);
indicator_position = (size_t) (args->state->calc.percentage);
if (indicator_position > 200)
indicator_position = indicator_position % 200;
if (indicator_position > 100 && indicator_position <= 200)
@@ -201,21 +261,19 @@ static size_t pv_formatter_progress_unknownsize(pvstate_t state, /*@unused@ */
/* The spaces before the indicator. */
for (pad_count = 0; pad_count < (bar_area_width * indicator_position) / 100; pad_count++) {
if (pad_count < bar_area_width && buffer_offset < buffer_size - 1)
buffer[buffer_offset++] = ' ';
if (pad_count < bar_area_width) {
append_to_buffer(style->filler[0]);
}
}
/* The indicator. */
if (buffer_offset < buffer_size - 4) {
buffer[buffer_offset++] = '<';
buffer[buffer_offset++] = '=';
buffer[buffer_offset++] = '>';
if (buffer_offset < buffer_size - style->indicator.bytes) {
append_to_buffer(style->indicator);
}
/* The spaces after the indicator. */
for (; pad_count < bar_area_width; pad_count++) {
if (pad_count < bar_area_width && buffer_offset < buffer_size - 1)
buffer[buffer_offset++] = ' ';
append_to_buffer(style->filler[0]);
}
if (bar_sides) {
@@ -240,17 +298,18 @@ size_t pv_formatter_progress(pvformatter_args_t args)
content[0] = '\0';
if (0 == args->segment->parameter)
args->segment->parameter = 1 + pv_display_barstyle_index(args->display, "default");
if (0 == args->buffer_size)
return 0;
if (args->state->control.size > 0 || args->state->control.rate_gauge) {
/* Known size or rate gauge - bar with percentage. */
bytes =
pv_formatter_progress_knownsize(args->state, args->display, content, sizeof(content), args->segment->width, true, true,
true);
bytes = pv_formatter_progress_knownsize(args, content, sizeof(content), true, true, true);
} else {
/* Unknown size - back-and-forth moving indicator. */
bytes = pv_formatter_progress_unknownsize(args->state, args->display, content, sizeof(content), args->segment->width, true);
bytes = pv_formatter_progress_unknownsize(args, content, sizeof(content), true);
}
content[bytes] = '\0';
@@ -269,18 +328,18 @@ size_t pv_formatter_progress_bar_only(pvformatter_args_t args)
content[0] = '\0';
if (0 == args->segment->parameter)
args->segment->parameter = 1 + pv_display_barstyle_index(args->display, "default");
if (0 == args->buffer_size)
return 0;
if (args->state->control.size > 0 || args->state->control.rate_gauge) {
/* Known size or rate gauge - bar with percentage. */
bytes =
pv_formatter_progress_knownsize(args->state, args->display, content, sizeof(content), args->segment->width, false, true,
false);
bytes = pv_formatter_progress_knownsize(args, content, sizeof(content), false, true, false);
} else {
/* Unknown size - back-and-forth moving indicator. */
bytes =
pv_formatter_progress_unknownsize(args->state, args->display, content, sizeof(content), args->segment->width, false);
bytes = pv_formatter_progress_unknownsize(args, content, sizeof(content), false);
}
content[bytes] = '\0';
@@ -304,9 +363,7 @@ size_t pv_formatter_progress_amount_only(pvformatter_args_t args)
if (args->state->control.size > 0 || args->state->control.rate_gauge) {
/* Known size or rate gauge - percentage or rate. */
bytes =
pv_formatter_progress_knownsize(args->state, args->display, content, sizeof(content), args->segment->width, false,
false, true);
bytes = pv_formatter_progress_knownsize(args, content, sizeof(content), false, false, true);
} else {
/* Unknown size - no number. */
return 0;