[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