[PATCHv2 3/4] nvme: add common APIs for printing tabular format output
Nilay Shroff
nilay at linux.ibm.com
Tue Aug 19 01:56:19 PDT 2025
On 8/18/25 12:57 PM, Hannes Reinecke wrote:
> On 8/12/25 14:56, Nilay Shroff wrote:
>> Some nvme-cli commands, such as nvme list and nvme list -v, support output
>> in tabular format. Currently, the table output is not well aligned because
>> column widths are fixed at print time, regardless of the length of the data
>> in each column. This often results in uneven and hard-to-read output.For
>> any new CLI command that requires tabular output, developers must manually
>> specify the column width and row value width, which is both error-prone and
>> inconsistent.
>>
>> This patch introduces a set of common table APIs that:
>> - Automatically calculate column widths based on the content
>> - Maintain proper alignment regardless of value length
>> - Simplify adding tabular output support to new and existing commands
>>
>> The new APIs are:
>> 1. table_init() — Allocate a table instance.
>> 2. table_add_columns() — Add column definitions (struct table_column),
>> including name and alignment (LEFT, RIGHT, CENTERED).
>> 3. table_get_row_id() — Reserve a row index for inserting values.
>> 4. table_add_row() — Add a row to the table.
>> 5. table_print() — Print the table with auto-calculated widths.
>> 6. table_free() — Free resources allocated for the table.
>>
>> For adding values, the following setter APIs are provided, each
>> supporting alignment types (LEFT, RIGHT, or CENTERED):
>> - table_set_value_str()
>> - table_set_value_int()
>> - table_set_value_unsigned()
>> - table_set_value_long()
>> - table_set_value_unsigned_long()
>>
>> Usage steps:
>> 1. Call table_init() to create a table handle.
>> 2. Define an array of struct table_column specifying column names and
>> alignment, then call table_add_columns().
>> 3. Obtain a row ID using table_get_row_id() and set values using the
>> appropriate setter table APIs : table_set_value_*() function.
>> 4. Add the completed row using table_add_row().
>> 5. Repeat steps 3–4 for each additional row.
>> 5. Call table_print() to display the table.
>> 6. Call table_free() to release table resources.
>>
>> With these APIs, developers no longer need to pre-calculate column or row
>> widths. The output is consistently aligned and easy to read.
>>
>> Suggested-by: Daniel Wagner <dwagner at suse.de>
>> Signed-off-by: Nilay Shroff <nilay at linux.ibm.com>
>> ---
>> util/meson.build | 3 +-
>> util/table.c | 278 +++++++++++++++++++++++++++++++++++++++++++++++
>> util/table.h | 117 ++++++++++++++++++++
>> 3 files changed, 397 insertions(+), 1 deletion(-)
>> create mode 100644 util/table.c
>> create mode 100644 util/table.h
>>
> Sheesh. Can o'worms.
>
> Displaying a table on an ASCII terminal is black magic (check
> manpage for ncurses ...), and I'm pretty certain that printing
> spaces for alignment is _not_ the way to go.
> If you really want to implement this then at the very least use
> tabs. Ideally check with the ncurses manpage how one can display
> a table with the correct escape characters.
>
Thanks for the feedback. You're absolutely right that terminal alignment
can get tricky, and ncurses does provide a more feature-complete way to
handle interactive table layouts.
However, for this patch, the intent was much narrower — to make existing
nvme-cli tabular output (e.g., nvme list, nvme list -v and nvme show-topology)
more consistent and readable without requiring each command to manually set
column/row widths. The current implementation just calculates the max width
per column and pads with spaces, which works for the non-interactive, plain-text
output we typically expect from CLI tools (including when the output is
redirected to a file).
Using tabs was considered, but since most values (especially long PCI/target
addresses, UUIDs, or model names) don’t line up cleanly on tab stops, the result
was often misaligned depending on the terminal settings. Spaces gave us
deterministic alignment across all environments.
That said, I completely agree ncurses (or similar) would be the right direction
if we wanted dynamic, interactive table display (resizable, scrollable, etc.).
For nvme-cli, which is mostly non-interactive, my goal was to keep things simple
while improving readability.
That said, if you think it’s worth exploring a tabs-based approach instead,
I can prototype that and compare the output side-by-side. Otherwise, perhaps
we can clarify in the commit message that this is meant for non-interactive,
plain ASCII output only. What do you think?
For your reference, I have printed below the output of nvme list -v
command (before/after patch):
### Without this patch:
$ nvme list -v
Subsystem Subsystem-NQN Controllers
---------------- ------------------------------------------------------------------------------------------------ ----------------
nvme-subsys0 nqn.2018-01.com.wdc:guid:E8238FA6BF53-0001-001B444A495BF972 nvme0
Device Cntlid SN MN FR TxPort Address Slot Subsystem Namespaces
---------------- ------ -------------------- ---------------------------------------- -------- ------ -------------- ------ ------------ ----------------
nvme0 8215 2136HZ464910 WDC PC SN730 SDBQNTY-512G-1001 11170101 pcie 0000:04:00.0 nvme-subsys0 nvme0n1
Device Generic NSID Usage Format Controllers
----------------- ----------------- ---------- -------------------------- ---------------- ----------------
/dev/nvme0n1 /dev/ng0n1 0x1 512.11 GB / 512.11 GB 512 B + 0 B nvme0
### With this patch applied and converting nvme list code to use the new table API:
$ nvme list -v
Subsystem Subsystem-NQN Controllers
------------ ----------------------------------------------------------- -----------
nvme-subsys0 nqn.2018-01.com.wdc:guid:E8238FA6BF53-0001-001B444A495BF972 nvme0
Device Cntlid SN MN FR TxPort Address Slot Subsystem Namespaces
------ ------ ------------ ------------------------------ -------- ------ ------------ ---- ------------ ----------
nvme0 8215 2136HZ464910 WDC PC SN730 SDBQNTY-512G-1001 11170101 pcie 0000:04:00.0 nvme-subsys0 nvme0n1
Device Generic NSID USAGE Format Controllers
------------ ---------- ---- ------------------------------------------------- -------------- -----------
/dev/nvme0n1 /dev/ng0n1 0x1 512.11 GB / 512.11 GB ( 476.94 GiB / 476.94 GiB) 512 B + 0 B nvme0
Thanks,
--Nilay
More information about the Linux-nvme
mailing list