The table printer module provides formatting of 2-dimensional tables in various ways.

Each table print-out consists of a number of rows, which are processed one after another. All rows have the same number of columns, each intersection of a row with a column is called a cell. Each cell holds data, represented using the extended types (a.k.a. xtypes). The table printer checks that the cells are filled by values of the appropriate types; additionally, a string value is allowed in any cell (for example, this allows a numeric cell to be set to "--" or "unknown").

Once a table is defined, it can be printed using a variety of formatters (human-readable, tab-separated values, etc.) and its cells can be formatted using at least three different formats: pretty (or human-readable), raw, default. Formatting of cells is handled by the xtype module.

The order of columns can be modified, columns can be omitted, or even printed multiple times with different formatting options. We therefore distinguish between columns (according to the definition of the table) and column instances (in the specific print-out).

Example

Let us construct a simple table of music recordings:

First, we define an enum with column indices (the values are automatically numbered starting from 0):

enum table_columns {
   TBL_REC_ID,
   TBL_REC_ALBUM_NAME,
   TBL_REC_ARTIST,
   TBL_REC_YEAR
};

Then we create a structure with the definition of our table. The table columns are defined using the TBL_COL_type and TBL_COL_type_FMT macros. Each macro gets the name of the column and its default width in characters. The _FMT version adds an explicit format string for printf used for this column. Moreover, various flags can be OR-ed to the width of the column, for example CELL_ALIGN_LEFT prescribes that the cell should be aligned to the left.

To define the column order, we can create an array of struct table_col_info using the following macros: TBL_COL, TBL_COL_FMT, TBL_COL_TYPE. An example follows:

struct table_col_info column_order[] = { TBL_COL(TBL_REC_ID), TBL_COL(TBL_REC_ALBUM_NAME) };

The column order is supplied in the struct table_template using the TBL_COL_ORDER macro.

struct table_template recording_table_template = {
  TBL_COLUMNS {
     [TBL_REC_ID] = TBL_COL_UINT("id", 16),
     [TBL_REC_ALBUM_NAME] = TBL_COL_STR_FMT("album-name", 20 | CELL_ALIGN_LEFT, "%s"),
     [TBL_REC_ARTIST] = TBL_COL_STR("artist", 20),
     [TBL_REC_YEAR] = TBL_COL_UINT("year", 10),
     TBL_COL_END
  },
  TBL_COL_ORDER(column_order)
};

Each table definition has to be created from a template before use by table_init():

struct table *rec_table = table_init(&recording_table_template);

Once it is initialized, we can use it for printing multiple tables. At the start of each table, we should obtain a fastbuf where the output should be sent, store it in the table structure and call table_start():

struct fastbuf *out = bfdopen_shared(1, 4096);
table_start(&rec_table, out);

Then we can fill the rows one after another. Each row is ended by table_end_row():

table_col_uint(&rec_table, TBL_REC_ID, 0);
table_col_str(&rec_table, TBL_REC_ALBUM_NAME, "The Wall");
table_col_str(&rec_table, TBL_REC_ARTIST, "Pink Floyd");
table_col_uint(&rec_table, TBL_REC_YEAR, 1979);
table_end_row(&rec_table);
table_col_uint(&rec_table, TBL_REC_ID, 1);
table_col_str(&rec_table, TBL_REC_ALBUM_NAME, "Rio Grande Mud");
table_col_str(&rec_table, TBL_REC_ARTIST, "ZZ Top");
table_col_uint(&rec_table, TBL_REC_YEAR, 1972);
table_end_row(&rec_table);

Finally, we should close the table by calling table_end():

table_end(&rec_table);

At this moment, the table structure is ready to be used again. When you do not need it any longer, you can dispose of it by table_cleanup():

table_cleanup(&rec_table);

ucw/table.h

Table definitions


#define CELL_ALIGN_LEFT (1U << 31)

Justify cell contents to the left.


struct table_column {
  const char *name;             // [*] Name of the column displayed in table header
  uint width;                   // [*] Width of the column (in characters) OR'ed with column flags
  uint fmt; // [*] default format of the column
  const struct xtype *type_def; // [*] pointer to xtype of this column

  const char * (*set_col_opt)(struct table *tbl, uint col_inst_idx, const char *col_opt);
       // [*] process table option for a column instance. @col_inst_idx is the index of the column
       // instance to which the @col_opt is set. Return value is the error string.
};

Definition of a single table column. Usually, this is generated using the TABLE_COL_type macros. Fields marked with [*] are user-accessible.


struct table_col_instance {
  uint idx; // [*] idx is a index into struct table::columns
  const struct table_column *col_def; // this is pointer to the column definition, located in the array struct table::columns
  const char *cell_content; // content of the cell of the current row
  int next_column; // index of next column in linked list of columns of the same type
  uint fmt; // [*] format of this column
};

Definition of a column instance. The table_col_instance belongs to a struct table. col_def is pointing to a definition of the column in struct table::columns. The user can fill only the idx and fmt. The col_def, cell_content, next_column are private fields.

Please use only fields marked with [*].


struct table_template {
  const struct table_column *columns; // [*] Definition of columns
  struct table_col_instance *column_order; // [*] Order of the columns in the print-out of the table
  const char *col_delimiter; // [*] Delimiter that is placed between columns
  // Back-end used for table formatting and its private data
  const struct table_formatter *formatter;
};

Definition of a table. Contains column definitions, and some per-table settings. Please use only fields marked with [*].


struct table {
  const struct table_column *columns;   // [*] Definition of columns
  int column_count;                     // [*] Number of columns (calculated by table_init())
  int *ll_headers; // headers of linked lists that connects column instances
  struct mempool *pool;                 // Memory pool used for storing table data. Contains global state
                                        // and data of the current row.
  struct mempool_state pool_state;      // State of the pool after the table is initialized, i.e., before
                                        // per-row data have been allocated.

  struct table_col_instance *column_order; // [*] Order of the columns in the print-out of the table
  uint cols_to_output;                  // [*] Number of columns that are printed
  const char *col_delimiter;            // [*] Delimiter that is placed between columns
  bool print_header;                    // [*] false indicates that table header should not be printed

  struct fastbuf *out;                  // [*] Fastbuffer to which the table is printed
  bool row_printing_started;            // Indicates that a row has been started.
  struct fbpool fb_col_out;             // Per-cell fastbuf, see table_col_fbstart()
  int col_out;                          // Index of the column that is currently printed using fb_col_out

  // Back-end used for table formatting and its private data
  const struct table_formatter *formatter;
  void *formatter_data;
};

Handle of a table. Contains column definitions, per-table settings and internal data. To change the table definition, please use only fields marked with [*].

In most cases, table descriptions are constructed using the following macros. See the examples above for more details.

  • TBL_COLUMNS indicates the start of definition of columns

  • TBL_COL_type(name, width) defines a column of a given type

  • TBL_COL_type_FMT(name, width, fmt) defines a column with a custom format string

  • TBL_COL_END ends the column definitions

  • TBL_COL_ORDER specifies custom ordering of columns in the output

  • TBL_COL_DELIMITER overrides the default delimiter

  • TBL_FMT_HUMAN_READABLE requests human-readable formatting (this is the default)

  • TBL_FMT_MACHINE_READABLE requests machine-readable TSV output

  • TBL_FMT_BLOCKLINE requests block formatting (each cell printed a pair of a key and value on its own line)


#define TBL_COL(_idx) { .idx = _idx, .fmt = XTYPE_FMT_DEFAULT, .next_column = -1 }

These macros are used for definition of column order


#define TBL_FMT_HUMAN_READABLE .formatter = &table_fmt_human_readable

These macros are aliases to various kinds of table formats.


struct table *table_init(const struct table_template *tbl_template);

Creates a new table from a table template. The template should already contain the definitions of columns.


void table_cleanup(struct table *tbl);

Destroy a table instance, freeing all memory used by it.


void table_start(struct table *tbl, struct fastbuf *out);

Start printing of a table. This is a prerequisite to setting of column values. After table_start() is called, it is no longer possible to change parameters of the table by table_set_something nor by direct access to the table structure.


void table_end(struct table *tbl);

This function must be called after all the rows of the current table are printed, making the table structure ready for the next table. You can call table_set_something between table_end() and table_start().

Filling tables with data

For each column type, there are functions for filling of cells of the particular type:

  • table_col_type(table, col_def_idx, value) sets the cell in column col_def_idx to the value


#define TABLE_COL_BODY(_name, _type) void table_col_##_name(struct table *tbl, int col, _type val) {\
    table_col_generic_format(tbl, col, (void*)&val, &xt_##_name);\
  }

TABLE_COL_BODY macro enables easy definitions of bodies of table_col_<something> functions


void table_col_generic_format(struct table *tbl, int col, void *value, const struct xtype *expected_type);

The table_col_generic_format performs all the checks necessary while filling cell with value, calls the format function from expected_type and stores its result as a cell value. The function guarantees that each column instance is printed with its format.


void table_col_printf(struct table *tbl, int col, const char *fmt, ...) FORMAT_CHECK(printf, 3, 4);

Set a particular cell of the current row to a string formatted by sprintf(). This function can set a column of an arbitrary type.


struct fastbuf *table_col_fbstart(struct table *tbl, int col);

Alternatively, a string cell can be constructed as a stream. This function creates a fastbuf stream connected to the contents of the particular cell. Before you close the stream by table_col_fbend(), no other operations with cells are allowed.


void table_col_fbend(struct table *tbl);

Close the stream that is used for printing of the current column.


void table_end_row(struct table *tbl);

Called when all cells of the current row have their values filled in. It sends the completed row to the output stream.


void table_reset_row(struct table *tbl);

Resets data in the current row.

Configuration functions


int table_get_col_idx(struct table *tbl, const char *col_name);

Find the index of a column definition with name col_name. Returns -1 if there is no such column.


const char *table_set_col_opt(struct table *tbl, uint col_inst_idx, const char *col_opt);

Sets a string option on a column instance.

By default, the option is parsed as a formatting mode of the corresponding xtype using xtype_parse_fmt().

As special cases might require special handling (e.g., column decoration, post-processing, etc.), a column can define a set_col_opt hook, which takes over option parsing. (Beware, the hook must not be called directly and it must not call this function.)

See the list of options for more.


const char *table_get_col_list(struct table *tbl);

Returns a comma-and-space-separated list of column names, allocated from table’s internal memory pool.


void table_set_col_order(struct table *tbl, const struct table_col_instance *col_order);

Sets the order in which the columns are printed. The columns are specified by array of struct table_col_instance. This allows specification of format. The user should make an array of struct table_col_instance and fill the array using the TBL_COL and TBL_COL_FMT. The array has a special last element: TBL_COL_ORDER_END.

The table copies content of col_order into an internal representation stored in column_order. Options to column instances can be set using table_set_col_opt().


const char *table_set_col_order_by_name(struct table *tbl, const char *col_order);

Sets the order in which the columns are printed. The specification is a string with comma-separated column names. Returns NULL for success and an error message otherwise. The string is not referenced after this function returns.

See the list of options for full syntax.


bool table_col_is_printed(struct table *tbl, uint col_def_idx);

Returns true if col_def_idx will be printed, false otherwise.


void table_set_formatter(struct table *tbl, const struct table_formatter *fmt);

Sets table formatter. See below for the list of formatters.


const char *table_set_option_value(struct table *tbl, const char *key, const char *value);

Set a table option. All options have a key and a value. See the list of options.


const char *table_set_option(struct table *tbl, const char *opt);

Sets a table option given as key:value or key (with no value).


const char *table_set_gary_options(struct table *tbl, char **gary_table_opts);

Sets several table option in key:value form, stored in a growing array. This is frequently used for options given on the command line.

Formatters

Transformation of abstract cell data to the characters in the output stream is under control of a formatter (which serves as a back-end of the table printer). There are several built-in formatters, but you can define your own.

A formatter is described by a structure, which contains pointers to several call-back functions, which are called by the table printer at specific occasions.

The formatter can keep its internal state in the data field of struct table and allocate temporary data from the table’s memory pool. Memory allocated in the row_output call-back is freed before the next row begins. Memory allocated between the beginning of table_start and the end of table_end is freed after table_end. Memory allocated by process_option when no table is started is kept until table_cleanup().


struct table_formatter {
  void (*row_output)(struct table *tbl);        // [*] Function that outputs one row
  void (*table_start)(struct table *tbl);       // [*] table_start callback (optional)
  void (*table_end)(struct table *tbl);         // [*] table_end callback (optional)
  bool (*process_option)(struct table *tbl, const char *key, const char *value, const char **err);
        // [*] Process table option and possibly return an error message (optional)
};

Definition of a formatter back-end.


extern const struct table_formatter table_fmt_human_readable;

Standard formatter for human-readable output.


extern const struct table_formatter table_fmt_machine_readable;

Standard formatter for machine-readable output (tab-separated values).


extern const struct table_formatter table_fmt_blockline;

Standard formatter for block output. Each cell is output on its own line of the form column_name: value. Rows are separated by blank lines.