Table printer ============= The table printer module provides formatting of 2-dimensional tables in various ways. Each table consists of a number of rows, which are processed one after another. All rows have the same number of columns. Internally, the columns have a fixed order, each column has its name and a data type (e.g., int, uint, u64, etc.). The table printer checks that each cell is filled by a value of the appropriate type, except that 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.). Also, a subset of columns can be selected and their order changed. 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 using the TBL_COL_ORDER macro. struct table 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 <> 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 ----------- !!ucw/table.h