diff --git a/.drone.yml b/.drone.yml
index 3a10cfd60..a0d45438c 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -12,7 +12,7 @@ steps:
- cd build
- cmake .. -DCMAKE_BUILD_TYPE=Release # -DUSE_RUST=yes -DUSE_NETWORK=yes
- make
- - make test || ./notcurses-tester -p ../data
+ - ./notcurses-tester -p ../data
---
kind: pipeline
type: docker
@@ -27,7 +27,7 @@ steps:
- cd build
- cmake .. -DCMAKE_BUILD_TYPE=Release
- make
- - make test || ./notcurses-tester -p ../data
+ - ./notcurses-tester -p ../data
---
kind: pipeline
type: docker
diff --git a/doc/man/index.html b/doc/man/index.html
index 9e588dbbb..b2a6725b4 100644
--- a/doc/man/index.html
+++ b/doc/man/index.html
@@ -41,6 +41,7 @@
notcurses_input—collecting input
notcurses_lines—drawing lines and boxes on ncplanes
notcurses_menu—menus on the top or bottom rows
+ notcurses_menu—fixed-width formatting with metric suffixes
notcurses_multiselector—high-level widget for selecting items from a set
notcurses_reel—high-level widget for hierarchical data
notcurses_output—drawing text on ncplanes
diff --git a/doc/man/man3/notcurses_metric.3.md b/doc/man/man3/notcurses_metric.3.md
new file mode 100644
index 000000000..42ae4d729
--- /dev/null
+++ b/doc/man/man3/notcurses_metric.3.md
@@ -0,0 +1,94 @@
+% notcurses_metric(3)
+% nick black
+% v1.4.3
+
+# NAME
+
+notcurses_metric - fixed-width numeric output with metric suffixes
+
+# SYNOPSIS
+
+**#include **
+
+```c
+#define PREFIXCOLUMNS 7
+#define IPREFIXCOLUMNS 8
+#define BPREFIXCOLUMNS 9
+#define PREFIXSTRLEN (PREFIXCOLUMNS + 1)
+#define IPREFIXSTRLEN (IPREFIXCOLUMNS + 1)
+#define BPREFIXSTRLEN (BPREFIXCOLUMNS + 1)
+#define NCMETRICFWIDTH(x, cols) ((int)(strlen(x) - mbswidth(x) + (cols)))
+#define PREFIXFMT(x) NCMETRICFWIDTH((x), PREFIXCOLUMNS), (x)
+#define IPREFIXFMT(x) NCMETRIXFWIDTH((x), IPREFIXCOLUMNS), (x)
+#define BPREFIXFMT(x) NCMETRICFWIDTH((x), BPREFIXCOLUMNS), (x)
+```
+
+**const char* ncmetric(uintmax_t val, uintmax_t decimal, char* buf, int omitdec, unsigned mult, int uprefix);**
+
+```c
+// Mega, kilo, gigafoo. Use PREFIXSTRLEN + 1.
+static inline const char*
+qprefix(uintmax_t val, uintmax_t decimal, char* buf, int omitdec){
+ return ncmetric(val, decimal, buf, omitdec, 1000, '\0');
+}
+
+// Mibi, kebi, gibibytes. Use BPREFIXSTRLEN + 1.
+static inline const char*
+bprefix(uintmax_t val, uintmax_t decimal, char* buf, int omitdec){
+ return ncmetric(val, decimal, buf, omitdec, 1024, 'i');
+}
+```
+
+# DESCRIPTION
+
+**ncmetric** (and the helper wrappers **qprefix** and **bprefix**) accept
+very large (or very small) non-negative numbers, and prepare formatted output
+of a maximum width using metric suffixes. The suffix can represent arbitrary
+amounts of growth, but is designed for 1000 (**PREFIX**) or 1024
+(**IPREFIX**). 1024 is used for "digital units of information", i.e. kibibytes
+and kilobytes. **ncmetric** supports the large suffixes KMGTPEZY (Kilo, Mega,
+Giga, Tera, Peta, Exa, Zetta, and Yotta) and the small suffixes mµnpfazy
+(Milli, Micro, Nano, Pico, Femto, Atto, Zepto, and Yocto). This covers the
+range 10^-24 through 10^24. As **uintmax_t** is typically only 64 bits, this
+covers the entirety of its range.
+
+**val** is the value being output, having been scaled by **decimal**.
+**decimal** will typically be 1; to represent values less than 1, **decimal**
+should be larger than **val**. The output will be written to **buf**, which
+must be at least:
+
+* **PREFIXSTRLEN** + 1 bytes for a 1000-based value
+* **IPREFIXSTRLEN** + 1 bytes for a 1024-based value
+* **BPREFIXSTRLEN** + 1 bytes for a 1024-based value with an 'i' suffix
+
+If **omitdec** is not zero, the decimal point and mantissa will be
+omitted if all digits to be displayed would be zero. The decimal point takes
+the current locale into account (see **setlocale(3)** and **localeconv(3)**).
+**mult** is the relative multiple for each suffix. **uprefix**, if not zero,
+will be used as a suffix following any metric suffix.
+
+In general, the maximum-width output will take the form:
+
+ CCC.mmMu
+
+Where C are digits of the characteristic (up to ceil(log10(**mult**)) digits),
+the decimal point follows, m are digits of the mantissa (up to 2), M is the
+metric suffix, and u is the **uprefix**. The minimum-width output will take
+the form:
+
+ C
+
+This can occur if **omitdec** is non-zero and a value such as 5 is passed
+for **val**.
+
+# RETURN VALUES
+
+**NULL** if input parameters were invalid. Otherwise, a pointer to **buf**,
+filled in with the formatted output.
+
+# SEE ALSO
+
+**localeconv(3)**,
+**notcurses(3)**,
+**notcurses_plane(3)**,
+**setlocale(3)**
diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h
index 3501d1b20..b1dfc8d6a 100644
--- a/include/notcurses/notcurses.h
+++ b/include/notcurses/notcurses.h
@@ -2339,8 +2339,14 @@ API void* nctablet_userptr(struct nctablet* t);
// Access the ncplane associated with this nctablet, if one exists.
API struct ncplane* nctablet_ncplane(struct nctablet* t);
-// The number of columns is one fewer, as the STRLEN expressions must
-// leave an extra byte open in case 'µ' (U+00B5, 0xC2 0xB5) shows up.
+// The number of columns is one fewer, as the STRLEN expressions must leave
+// an extra byte open in case 'µ' (U+00B5, 0xC2 0xB5) shows up. PREFIXCOLUMNS
+// is the maximum number of columns used by a mult == 1000 (standard)
+// ncmetric() call. IPREFIXCOLUMNS is the maximum number of columns used by a
+// mult == 1024 (digital information) ncmetric(). BPREFIXSTRLEN is the maximum
+// number of columns used by a mult == 1024 call making use of the 'i' suffix.
+// This is the true number of columns; to set up a printf()-style maximum
+// field width, you should use [IB]PREFIXFMT (see below).
#define PREFIXCOLUMNS 7
#define IPREFIXCOLUMNS 8
#define BPREFIXCOLUMNS 9
@@ -2349,12 +2355,10 @@ API struct ncplane* nctablet_ncplane(struct nctablet* t);
#define BPREFIXSTRLEN (BPREFIXCOLUMNS + 1) // Does not include a '\0' (xxxx.xxUi), i == prefix
// Used as arguments to a variable field width (i.e. "%*s" -- these are the *).
// We need this convoluted grotesquery to properly handle 'µ'.
-#define PREFIXFWIDTH(x) ((int)(strlen(x) - mbswidth(x) + PREFIXCOLUMNS))
-#define IPREFIXFWIDTH(x) ((int)(strlen(x) - mbswidth(x) + IPREFIXCOLUMNS))
-#define BPREFIXFWIDTH(x) ((int)(strlen(x) - mbswidth(x) + BPREFIXCOLUMNS))
-#define PREFIXFMT(x) PREFIXFWIDTH(x), (x)
-#define IPREFIXFMT(x) IPREFIXFWIDTH(x), (x)
-#define BPREFIXFMT(x) BPREFIXFWIDTH(x), (x)
+#define NCMETRICFWIDTH(x, cols) ((int)(strlen(x) - mbswidth(x) + (cols)))
+#define PREFIXFMT(x) NCMETRICFWIDTH((x), PREFIXCOLUMNS), (x)
+#define IPREFIXFMT(x) NCMETRIXFWIDTH((x), IPREFIXCOLUMNS), (x)
+#define BPREFIXFMT(x) NCMETRICFWIDTH((x), BPREFIXCOLUMNS), (x)
// Takes an arbitrarily large number, and prints it into a fixed-size buffer by
// adding the necessary SI suffix. Usually, pass a |[IB]PREFIXSTRLEN+1|-sized