Friday, May 30, 2014

libsmartcols - pretty output for everyone!

util-linux 2.25 is going to contain libsmartcols. The library code is based on code originally developed for lsblk(8), findmnt(8), lslocks(8) etc.  The goal is to share formatting code to print pretty tables and trees.

Features:
  • tree-like output (see lsblk(8) or findmnt(8))
  • table-like output (see lslocks(8))
  • formatting sensitive to terminal width
  • fully supports UTF8 (correctly truncate data in cells, tree lines, ...)
  • dynamic or fixed columns width
  • truncate data in cell on demand
  • raw (no formatting) output mode
  • NAME=value output mode
  • custom cells and lines delimiters (for example for CSV output)
  • built-in ASCII, UTF8 or custom symbols to draw trees
  • per column, line or cell colors
  • colors specified by words (e.g. "red") or ESC sequences
  • sort lines
  • enable/disable heading
  • output to FILE stream (default stdout), or to string
  • all columns, lines and cells accessible by iterators (no callbacks)
  • output data specified by strings (no crazy unions or to-string conversion functions in the library)
  • reference counting
  • integrated debug output (sensitive to LIBSMARTCOLS_DEBUG env)
  • usable to keep parent<->child relationship
  • library symbols versioning (no soname changes)

See bellow a trivial (so no errors handling, etc.) example.

int main(void)
{
        struct libscols_table *tb;
        struct libscols_line *ln, *dad, *gdad;
        enum { COL_NAME, COL_AGE };

        setlocale(LC_ALL, "");

        tb = scols_new_table();
        scols_table_new_column(tb, "NAME", 0.1, SCOLS_FL_TREE);
        scols_table_new_column(tb, "AGE",     2, SCOLS_FL_RIGHT);

        ln = gdad = scols_table_new_line(tb, NULL);
        scols_line_set_data(ln, COL_NAME, "Grandfather Bob");
        scols_line_set_data(ln, COL_AGE, "61");

        ln = dad = scols_table_new_line(tb, ln);
        scols_line_set_data(ln, COL_NAME, "Father Adam");
        scols_line_set_data(ln, COL_AGE, "38");

        ln = scols_table_new_line(tb, dad);
        scols_line_set_data(ln, COL_NAME, "Baby Val");
        scols_line_set_data(ln, COL_AGE, "9");

        ln = scols_table_new_line(tb, dad);
        scols_line_set_data(ln, COL_NAME, "Baby Dilbert");
        scols_line_set_data(ln, COL_AGE, "5");

        ln = scols_table_new_line(tb, gdad);
        scols_line_set_data(ln, COL_NAME, "Aunt Gaga");
        scols_line_set_data(ln, COL_AGE, "35");

        scols_print_table(tb);
        scols_unref_table(tb);
        return 0;
}

... and output:

NAME             AGE
Grandfather Bob   61
├─Father Adam     38
│ ├─Baby Val       9
│ └─Baby Dilbert   5
└─Aunt Gaga       35