Table printer ============= 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 <> (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 <> 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