]> mj.ucw.cz Git - libucw.git/blob - ucw/table.c
tableprinter: allocation of handles now uses xmalloc_zero/xfree
[libucw.git] / ucw / table.c
1 /*
2  *      UCW Library -- Table printer
3  *
4  *      (c) 2014 Robert Kessl <robert.kessl@economia.cz>
5  */
6
7 #include <ucw/lib.h>
8 #include <ucw/string.h>
9 #include <ucw/stkstring.h>
10 #include <ucw/gary.h>
11 #include <ucw/table.h>
12 #include <ucw/strtonum.h>
13
14 #include <stdlib.h>
15 #include <stdio.h>
16
17 /* Forward declarations */
18
19 static void table_update_ll(struct table *tbl);
20
21 /*** Management of tables ***/
22
23 static struct table *table_template_copy(struct table_template *tbl_template)
24 {
25   struct table *copy = xmalloc_zero(sizeof(struct table));
26
27   copy->column_count = tbl_template->column_count;
28   copy->pool = mp_new(4096);
29   if(tbl_template->column_order) {
30     copy->column_order = mp_alloc_zero(copy->pool, sizeof(struct table_col_info) * tbl_template->cols_to_output);
31     memcpy(copy->column_order, tbl_template->column_order, sizeof(struct table_col_info) * tbl_template->cols_to_output);
32     for(uint i = 0; i < copy->cols_to_output; i++) {
33       copy->column_order[i].cell_content = NULL;
34       copy->column_order[i].col_def = NULL;
35       copy->column_order[i].output_type = tbl_template->column_order[i].output_type;
36     }
37
38     copy->cols_to_output = tbl_template->cols_to_output;
39   }
40
41   copy->columns = tbl_template->columns;
42
43   copy->col_delimiter = tbl_template->col_delimiter;
44   copy->print_header = 1;
45   copy->out = 0;
46   copy->last_printed_col = -1;
47   copy->row_printing_started = 0;
48   copy->col_out = -1;
49   copy->formatter = tbl_template->formatter;
50   copy->data = NULL;
51   return copy;
52 }
53
54 struct table *table_init(struct table_template *tbl_template)
55 {
56   int col_count = 0; // count the number of columns in the struct table
57
58   struct table *tbl = table_template_copy(tbl_template);
59
60   for(;;) {
61     if(tbl->columns[col_count].name == NULL &&
62        tbl->columns[col_count].fmt == NULL &&
63        tbl->columns[col_count].width == 0 &&
64        tbl->columns[col_count].type == COL_TYPE_LAST)
65       break;
66     ASSERT(tbl->columns[col_count].name != NULL);
67     ASSERT(tbl->columns[col_count].type == COL_TYPE_ANY || tbl_template->columns[col_count].fmt != NULL);
68     ASSERT(tbl->columns[col_count].width != 0);
69
70     col_count++;
71   }
72
73   tbl->column_count = col_count;
74
75   if(!tbl->formatter) {
76     tbl->formatter = &table_fmt_human_readable;
77   }
78
79   tbl->print_header = 1; // by default, print header
80   return tbl;
81 }
82
83 void table_cleanup(struct table *tbl)
84 {
85   mp_delete(tbl->pool);
86   memset(tbl, 0, sizeof(struct table));
87   xfree(tbl);
88 }
89
90 // TODO: test default column order
91 static void table_make_default_column_order(struct table *tbl)
92 {
93   int *col_order_int = mp_alloc_zero(tbl->pool, sizeof(int) * tbl->column_count); // FIXME: use stack instead of memory pool
94   for(int i = 0; i < tbl->column_count; i++) {
95     col_order_int[i] = i;
96   }
97   table_set_col_order(tbl, col_order_int, tbl->column_count);
98 }
99
100 void table_start(struct table *tbl, struct fastbuf *out)
101 {
102   tbl->last_printed_col = -1;
103   tbl->row_printing_started = 0;
104   tbl->out = out;
105
106   ASSERT_MSG(tbl->out, "Output fastbuf not specified.");
107
108   if(tbl->column_order == NULL) table_make_default_column_order(tbl);
109   else {
110     // update linked lists
111     table_update_ll(tbl);
112   }
113   if(tbl->formatter->table_start != NULL) tbl->formatter->table_start(tbl);
114
115   mp_save(tbl->pool, &tbl->pool_state);
116
117   ASSERT_MSG(tbl->col_delimiter, "In-between column delimiter not specified.");
118 }
119
120 void table_end(struct table *tbl)
121 {
122   tbl->last_printed_col = -1;
123   tbl->row_printing_started = 0;
124
125   mp_restore(tbl->pool, &tbl->pool_state);
126
127   if(tbl->formatter->table_end) tbl->formatter->table_end(tbl);
128 }
129
130 /*** Configuration ***/
131
132 void table_set_formatter(struct table *tbl, struct table_formatter *fmt)
133 {
134   tbl->formatter = fmt;
135 }
136
137 int table_get_col_idx(struct table *tbl, const char *col_name)
138 {
139   for(int i = 0; i < tbl->column_count; i++) {
140     if(strcmp(tbl->columns[i].name, col_name) == 0) return i;
141   }
142   return -1;
143 }
144
145 const char * table_get_col_list(struct table *tbl)
146 {
147   if(tbl->column_count == 0) return "";
148
149   char *tmp = mp_strdup(tbl->pool, tbl->columns[0].name);
150
151   for(int i = 1; i < tbl->column_count; i++) {
152     tmp = mp_printf_append(tbl->pool, tmp, ", %s", tbl->columns[i].name);
153   }
154
155   return tmp;
156 }
157
158 static void table_update_ll(struct table *tbl)
159 {
160   int cols_to_output = tbl->cols_to_output;
161
162   for(int i = 0; i < tbl->column_count; i++) {
163     tbl->columns[i].first_column = -1;
164     tbl->columns[i].last_column = -1;
165   }
166
167   for(int i = 0; i < cols_to_output; i++) {
168     int idx = tbl->column_order[i].idx;
169     tbl->column_order[i].col_def = tbl->columns + idx;
170   }
171
172   for(int i = 0; i < cols_to_output; i++) {
173     int last = tbl->column_order[i].col_def->last_column;
174     if(last != -1) {
175       tbl->column_order[i].col_def->last_column = i;
176       tbl->column_order[last].next_column = i;
177     } else {
178       tbl->column_order[i].col_def->last_column = i;
179       tbl->column_order[i].col_def->first_column = i;
180     }
181     tbl->column_order[i].next_column = -1;
182   }
183 }
184
185 void table_set_col_order(struct table *tbl, int *col_order, int cols_to_output)
186 {
187   for(int i = 0; i < cols_to_output; i++) {
188     ASSERT_MSG(col_order[i] >= 0 && col_order[i] < tbl->column_count, "Column %d does not exist (column number should be between 0 and %d)", col_order[i], tbl->column_count - 1);
189   }
190
191   tbl->cols_to_output = cols_to_output;
192   tbl->column_order = mp_alloc_zero(tbl->pool, sizeof(struct table_col_info) * cols_to_output);
193   for(int i = 0; i < cols_to_output; i++) {
194     int col_idx = col_order[i];
195     tbl->column_order[i].idx = col_idx;
196     tbl->column_order[i].col_def = tbl->columns + col_idx;
197     tbl->column_order[i].cell_content = NULL;
198     tbl->column_order[i].output_type = CELL_OUT_UNINITIALIZED;
199   }
200   table_update_ll(tbl);
201 }
202
203 bool table_col_is_printed(struct table *tbl, uint col_idx)
204 {
205   if(tbl->columns[col_idx].first_column == -1) return 0;
206
207   return 1;
208 }
209
210 static char * table_parse_col_arg(char *col_def)
211 {
212   // FIXME: should be switched to str_sepsplit
213   char * left_br = strchr(col_def, '[');
214   if(left_br == NULL) return NULL;
215   *left_br = 0;
216   left_br++;
217   char *right_br = strchr(left_br, ']');
218   *right_br = 0;
219   return left_br;
220 }
221
222 /**
223  * Setting options for basic table types (as defined in table.h)
224  **/
225 bool table_set_col_opt_default(struct table *tbl, int col_copy_idx, const char *col_arg, char **err)
226 {
227   struct table_column *tmp_col = tbl->column_order[col_copy_idx].col_def;
228   int col_type_idx = tbl->column_order[col_copy_idx].idx;
229
230   if(tmp_col->type == COL_TYPE_DOUBLE) {
231     uint precision = 0;
232     str_to_uint(&precision, col_arg, NULL, 0);
233     tbl->column_order[col_type_idx].output_type = precision;
234     return true;
235   }
236
237   *err = mp_printf(tbl->pool, "Tableprinter: invalid column format option: '%s' for column %d.", col_arg, col_copy_idx);
238   return false;
239 }
240
241 /**
242  * TODO: This function deliberately leaks memory. When it is called multiple times,
243  * previous column orders still remain allocated in the table's memory pool.
244  **/
245 const char * table_set_col_order_by_name(struct table *tbl, const char *col_order_str)
246 {
247   if(col_order_str[0] == '*') {
248     int *col_order_int = alloca(sizeof(int) * tbl->column_count);
249     for(int i = 0; i < tbl->column_count; i++) {
250       col_order_int[i] = i;
251     }
252     table_set_col_order(tbl, col_order_int, tbl->column_count);
253
254     return NULL;
255   }
256
257   if(!col_order_str[0]) {
258     tbl->column_order = mp_alloc(tbl->pool, 0);
259     tbl->cols_to_output = 0;
260     return NULL;
261   }
262
263   char *tmp_col_order = stk_strdup(col_order_str);
264
265   int col_count = 1;
266   for(int i = 0; col_order_str[i] != 0; i++) {
267     if(col_order_str[i] == ',') {
268       col_count++;
269     }
270   }
271
272   tbl->cols_to_output = col_count;
273   tbl->column_order = mp_alloc_zero(tbl->pool, sizeof(struct table_col_info) * col_count);
274
275   int curr_col_idx = 0;
276   char *name_start = tmp_col_order;
277   while(name_start) {
278     char *next = strchr(name_start, ',');
279     if(next) {
280       *next++ = 0;
281     }
282
283     char *arg = table_parse_col_arg(name_start); // this sets 0 on the '['
284     int col_idx = table_get_col_idx(tbl, name_start);
285
286     if(col_idx == -1) {
287       return mp_printf(tbl->pool, "Unknown table column '%s', possible column names are: %s.", name_start, table_get_col_list(tbl));
288     }
289     tbl->column_order[curr_col_idx].col_def = tbl->columns + col_idx;
290     tbl->column_order[curr_col_idx].idx = col_idx;
291     tbl->column_order[curr_col_idx].cell_content = NULL;
292     tbl->column_order[curr_col_idx].output_type = CELL_OUT_UNINITIALIZED;
293     if(tbl->columns[col_idx].type_def && tbl->columns[col_idx].type_def->set_col_instance_option) {
294       char *err = NULL;
295       tbl->columns[col_idx].type_def->set_col_instance_option(tbl, curr_col_idx, arg, &err);
296       if(err) return mp_printf(tbl->pool, "Error occured while setting column option: %s.", err);
297     }
298
299     name_start = next;
300     curr_col_idx++;
301   }
302
303   table_update_ll(tbl);
304
305   return NULL;
306 }
307
308 /*** Table cells ***/
309
310 static void table_set_all_cols_content(struct table *tbl, int col, char *col_content, int override)
311 {
312   int curr_col = tbl->columns[col].first_column;
313   while(curr_col != -1) {
314     if(override == 0 && tbl->column_order[curr_col].output_type != CELL_OUT_UNINITIALIZED ) {
315       die("Error while setting content of all cells of a single type column, cell format should not be overriden.");
316     }
317     tbl->column_order[curr_col].cell_content = col_content;
318     curr_col = tbl->column_order[curr_col].next_column;
319   }
320 }
321
322 void table_col_printf(struct table *tbl, int col, const char *fmt, ...)
323 {
324   ASSERT_MSG(col < tbl->column_count && col >= 0, "Table column %d does not exist.", col);
325   tbl->last_printed_col = col;
326   tbl->row_printing_started = 1;
327   va_list args;
328   va_start(args, fmt);
329   char *cell_content = mp_vprintf(tbl->pool, fmt, args);
330   table_set_all_cols_content(tbl, col, cell_content, 1);
331   va_end(args);
332 }
333
334 static const char *table_col_default_fmts[] = {
335   [COL_TYPE_STR] = "%s",
336   [COL_TYPE_INT] = "%d",
337   [COL_TYPE_S64] = "%lld",
338   [COL_TYPE_INTMAX] = "%jd",
339   [COL_TYPE_UINT] = "%u",
340   [COL_TYPE_U64] = "%llu",
341   [COL_TYPE_UINTMAX] = "%ju",
342   [COL_TYPE_BOOL] = "%d",
343   [COL_TYPE_DOUBLE] = "%.2lf",
344   [COL_TYPE_ANY] = NULL,
345   [COL_TYPE_LAST] = NULL
346 };
347
348 #define TABLE_COL(_name_, _type_, _typeconst_) void table_col_##_name_(struct table *tbl, int col, _type_ val)\
349   {\
350     const char *fmt = tbl->columns[col].fmt;\
351     if(tbl->columns[col].type == COL_TYPE_ANY) {\
352        fmt = table_col_default_fmts[_typeconst_];\
353     }\
354     table_col_##_name_##_fmt(tbl, col, fmt, val);\
355   }
356
357 #define TABLE_COL_STR(_name_, _type_, _typeconst_) void table_col_##_name_##_name(struct table *tbl, const char *col_name, _type_ val)\
358   {\
359     int col = table_get_col_idx(tbl, col_name);\
360     table_col_##_name_(tbl, col, val);\
361   }
362
363 #define TABLE_COL_FMT(_name_, _type_, _typeconst_, _override) void table_col_##_name_##_fmt(struct table *tbl, int col, const char *fmt, _type_ val) \
364   {\
365      ASSERT_MSG(col < tbl->column_count && col >= 0, "Table column %d does not exist.", col);\
366      ASSERT(tbl->columns[col].type == COL_TYPE_ANY || _typeconst_ == tbl->columns[col].type);\
367      ASSERT(fmt != NULL);\
368      tbl->last_printed_col = col;\
369      tbl->row_printing_started = 1;\
370      char *cell_content = mp_printf(tbl->pool, fmt, val);\
371      table_set_all_cols_content(tbl, col, cell_content, _override);\
372   }
373
374 #define TABLE_COL_BODIES(_name_, _type_, _typeconst_, _override) TABLE_COL(_name_, _type_, _typeconst_); \
375   TABLE_COL_STR(_name_, _type_, _typeconst_);\
376   TABLE_COL_FMT(_name_, _type_, _typeconst_, _override);
377
378 TABLE_COL_BODIES(int, int, COL_TYPE_INT, 0)
379 TABLE_COL_BODIES(uint, uint, COL_TYPE_UINT, 0)
380 TABLE_COL_BODIES(str, const char *, COL_TYPE_STR, 1)
381 TABLE_COL_BODIES(intmax, intmax_t, COL_TYPE_INTMAX, 0)
382 TABLE_COL_BODIES(uintmax, uintmax_t, COL_TYPE_UINTMAX, 0)
383 TABLE_COL_BODIES(s64, s64, COL_TYPE_S64, 0)
384 TABLE_COL_BODIES(u64, u64, COL_TYPE_U64, 0)
385
386 // column type double is a special case
387 TABLE_COL(double, double, COL_TYPE_DOUBLE);
388 TABLE_COL_STR(double, double, COL_TYPE_DOUBLE);
389 #undef TABLE_COL
390 #undef TABLE_COL_FMT
391 #undef TABLE_COL_STR
392 #undef TABLE_COL_BODIES
393
394 void table_col_double_fmt(struct table *tbl, int col, const char *fmt, double val)
395 {
396   ASSERT_MSG(col < tbl->column_count && col >= 0, "Table column %d does not exist.", col);
397   ASSERT(tbl->columns[col].type == COL_TYPE_ANY || COL_TYPE_DOUBLE == tbl->columns[col].type);
398   ASSERT(fmt != NULL);
399   tbl->last_printed_col = col;
400   tbl->row_printing_started = 1;
401   char *cell_content = mp_printf(tbl->pool, fmt, val);
402   int curr_col = tbl->columns[col].first_column;
403   while(curr_col != -1) {
404     if(tbl->column_order[curr_col].output_type < 0) tbl->column_order[curr_col].cell_content = cell_content;
405     else {
406       char *cell_content_tmp = mp_printf(tbl->pool, "%.*lf", tbl->column_order[curr_col].output_type, val);
407       tbl->column_order[curr_col].cell_content = cell_content_tmp;
408     }
409     curr_col = tbl->column_order[curr_col].next_column;
410   }
411 }
412
413 void table_col_bool(struct table *tbl, int col, uint val)
414 {
415   table_col_bool_fmt(tbl, col, tbl->columns[col].fmt, val);
416 }
417
418 void table_col_bool_name(struct table *tbl, const char *col_name, uint val)
419 {
420   int col = table_get_col_idx(tbl, col_name);
421   table_col_bool(tbl, col, val);
422 }
423
424 void table_col_bool_fmt(struct table *tbl, int col, const char *fmt, uint val)
425 {
426   ASSERT_MSG(col < tbl->column_count && col >= 0, "Table column %d does not exist.", col);
427   ASSERT(COL_TYPE_BOOL == tbl->columns[col].type);
428
429   tbl->last_printed_col = col;
430   tbl->row_printing_started = 1;
431
432   int curr_col = tbl->columns[col].first_column;
433   while(curr_col != -1) {
434     switch(tbl->column_order[curr_col].output_type) {
435     case CELL_OUT_HUMAN_READABLE:
436     case CELL_OUT_UNINITIALIZED:
437       tbl->column_order[curr_col].cell_content = mp_printf(tbl->pool, fmt, val ? "true" : "false");
438       break;
439     case CELL_OUT_MACHINE_READABLE:
440       // FIXME: this is just an example of printing in different formats
441       tbl->column_order[curr_col].cell_content = mp_printf(tbl->pool, fmt, val ? "1" : "0");
442       break;
443     default:
444       die("Unsupported output type.");
445     }
446     curr_col = tbl->column_order[curr_col].next_column;
447   }
448 }
449
450 void table_reset_row(struct table *tbl)
451 {
452   for(uint i = 0; i < tbl->cols_to_output; i++) {
453     tbl->column_order[i].cell_content = NULL;
454   }
455   mp_restore(tbl->pool, &tbl->pool_state);
456   tbl->last_printed_col = -1;
457   tbl->row_printing_started = 0;
458 }
459
460 void table_end_row(struct table *tbl)
461 {
462   ASSERT(tbl->formatter->row_output);
463   if(tbl->row_printing_started == 0) return;
464   tbl->formatter->row_output(tbl);
465   table_reset_row(tbl);
466 }
467
468 /* Construction of a cell using a fastbuf */
469
470 struct fastbuf *table_col_fbstart(struct table *tbl, int col)
471 {
472   fbpool_init(&tbl->fb_col_out);
473   fbpool_start(&tbl->fb_col_out, tbl->pool, 1);
474   tbl->col_out = col;
475   return &tbl->fb_col_out.fb;
476 }
477
478 void table_col_fbend(struct table *tbl)
479 {
480   char *cell_content = fbpool_end(&tbl->fb_col_out);
481   table_set_all_cols_content(tbl, tbl->col_out, cell_content, 1);
482   tbl->col_out = -1;
483 }
484
485 /*** Option parsing ***/
486
487 const char *table_set_option_value(struct table *tbl, const char *key, const char *value)
488 {
489   // Options with no value
490   if(value == NULL || (value != NULL && strlen(value) == 0)) {
491     if(strcmp(key, "noheader") == 0) {
492       tbl->print_header = 0;
493       return NULL;
494     }
495   }
496
497   // Options with a value
498   if(value) {
499     if(strcmp(key, "header") == 0) {
500       if(value[1] != 0)
501         return mp_printf(tbl->pool, "Tableprinter: invalid option: '%s' has invalid value: '%s'.", key, value);
502       uint tmp = value[0] - '0';
503       if(tmp > 1)
504         return mp_printf(tbl->pool, "Tableprinter: invalid option: '%s' has invalid value: '%s'.", key, value);
505       tbl->print_header = tmp;
506       return NULL;
507     } else if(strcmp(key, "cols") == 0) {
508       const char *err = table_set_col_order_by_name(tbl, value);
509       return err;
510     } else if(strcmp(key, "fmt") == 0) {
511       if(strcmp(value, "human") == 0) table_set_formatter(tbl, &table_fmt_human_readable);
512       else if(strcmp(value, "machine") == 0) table_set_formatter(tbl, &table_fmt_machine_readable);
513       else if(strcmp(value, "blockline") == 0) table_set_formatter(tbl, &table_fmt_blockline);
514       else {
515         return "Tableprinter: invalid argument to output-type option.";
516       }
517       return NULL;
518     } else if(strcmp(key, "col-delim") == 0) {
519       char * d = mp_printf(tbl->pool, "%s", value);
520       tbl->col_delimiter = d;
521       return NULL;
522     }
523   }
524
525   // Formatter options
526   if(tbl->formatter && tbl->formatter->process_option) {
527     const char *err = NULL;
528     if(tbl->formatter->process_option(tbl, key, value, &err)) {
529       return err;
530     }
531   }
532
533   // Unrecognized option
534   return mp_printf(tbl->pool, "Tableprinter: invalid option: '%s%s%s'.", key, (value ? ":" : ""), (value ? : ""));
535 }
536
537 const char *table_set_option(struct table *tbl, const char *opt)
538 {
539   char *key = stk_strdup(opt);
540   char *value = strchr(key, ':');
541   if(value) {
542     *value++ = 0;
543   }
544   return table_set_option_value(tbl, key, value);
545 }
546
547 const char *table_set_gary_options(struct table *tbl, char **gary_table_opts)
548 {
549   for (uint i = 0; i < GARY_SIZE(gary_table_opts); i++) {
550     const char *rv = table_set_option(tbl, gary_table_opts[i]);
551     if(rv != NULL) {
552       return rv;
553     }
554   }
555   return NULL;
556 }
557
558 /*** Default formatter for human-readable output ***/
559
560 static void table_row_human_readable(struct table *tbl)
561 {
562   for(uint i = 0; i < tbl->cols_to_output; i++) {
563     int col_idx = tbl->column_order[i].idx;
564     if(i) {
565       bputs(tbl->out, tbl->col_delimiter);
566     }
567     int col_width = tbl->columns[col_idx].width & CELL_WIDTH_MASK;
568     if(tbl->columns[col_idx].width & CELL_ALIGN_LEFT) col_width = -1 * col_width;
569     bprintf(tbl->out, "%*s", col_width, tbl->column_order[i].cell_content);
570   }
571   bputc(tbl->out, '\n');
572 }
573
574 static void table_write_header(struct table *tbl)
575 {
576   for(uint i = 0; i < tbl->cols_to_output; i++) {
577     int col_idx = tbl->column_order[i].col_def - tbl->columns;
578     if(i) {
579       bputs(tbl->out, tbl->col_delimiter);
580     }
581     int col_width = tbl->columns[col_idx].width & CELL_WIDTH_MASK;
582     if(tbl->columns[col_idx].width & CELL_ALIGN_LEFT) col_width = -1 * col_width;
583     bprintf(tbl->out, "%*s", col_width, tbl->columns[col_idx].name);
584   }
585   bputc(tbl->out, '\n');
586 }
587
588 static void table_start_human_readable(struct table *tbl)
589 {
590   if(tbl->col_delimiter == NULL) {
591     tbl->col_delimiter = " ";
592   }
593
594   if(tbl->print_header != 0) {
595     table_write_header(tbl);
596   }
597 }
598
599 struct table_formatter table_fmt_human_readable = {
600   .row_output = table_row_human_readable,
601   .table_start = table_start_human_readable,
602 };
603
604 /*** Default formatter for machine-readable output ***/
605
606 static void table_row_machine_readable(struct table *tbl)
607 {
608   for(uint i = 0; i < tbl->cols_to_output; i++) {
609     if(i) {
610       bputs(tbl->out, tbl->col_delimiter);
611     }
612     bputs(tbl->out, tbl->column_order[i].cell_content);
613   }
614   bputc(tbl->out, '\n');
615 }
616
617 static void table_start_machine_readable(struct table *tbl)
618 {
619   if(tbl->col_delimiter == NULL) {
620     tbl->col_delimiter = "\t";
621   }
622
623   if(tbl->print_header != 0) {
624     uint col_idx = tbl->column_order[0].col_def - tbl->columns;
625     bputs(tbl->out, tbl->columns[col_idx].name);
626     for(uint i = 1; i < tbl->cols_to_output; i++) {
627       col_idx = tbl->column_order[i].col_def - tbl->columns;
628       bputs(tbl->out, tbl->col_delimiter);
629       bputs(tbl->out, tbl->columns[col_idx].name);
630     }
631     bputc(tbl->out, '\n');
632   }
633 }
634
635 struct table_formatter table_fmt_machine_readable = {
636   .row_output = table_row_machine_readable,
637   .table_start = table_start_machine_readable,
638 };
639
640
641 /*** Blockline formatter ***/
642
643 static void table_row_blockline_output(struct table *tbl)
644 {
645   for(uint i = 0; i < tbl->cols_to_output; i++) {
646     int col_idx = tbl->column_order[i].idx;
647     bprintf(tbl->out, "%s: %s\n", tbl->columns[col_idx].name, tbl->column_order[i].cell_content);
648   }
649   bputc(tbl->out, '\n');
650 }
651
652 static void table_start_blockline(struct table *tbl)
653 {
654   if(tbl->col_delimiter == NULL) {
655     tbl->col_delimiter = "\n";
656   }
657 }
658
659 struct table_formatter table_fmt_blockline = {
660   .row_output = table_row_blockline_output,
661   .table_start = table_start_blockline
662 };
663
664
665
666 /*** Tests ***/
667
668 #ifdef TEST
669
670 #include <stdio.h>
671
672 enum test_table_cols {
673   test_col0_str, test_col1_int, test_col2_uint, test_col3_bool, test_col4_double
674 };
675
676 static struct table_col_info test_column_order[] = { TBL_COL(test_col3_bool), TBL_COL(test_col4_double), TBL_COL(test_col2_uint), TBL_COL(test_col1_int), TBL_COL(test_col0_str) };
677
678 static struct table_template test_tbl = {
679   TBL_COLUMNS {
680     [test_col0_str] = TBL_COL_STR("col0_str", 20),
681     [test_col1_int] = TBL_COL_INT("col1_int", 8),
682     [test_col2_uint] = TBL_COL_UINT("col2_uint", 9),
683     [test_col3_bool] = TBL_COL_BOOL("col3_bool", 9),
684     [test_col4_double] = TBL_COL_DOUBLE("col4_double", 11, 2),
685     TBL_COL_END
686   },
687   TBL_COL_ORDER(test_column_order),
688   TBL_OUTPUT_HUMAN_READABLE,
689   TBL_COL_DELIMITER("\t"),
690 };
691
692 /**
693  * tests: table_set_nt, table_set_uint, table_set_bool, table_set_double, table_set_printf
694  **/
695 static void do_print1(struct table *test_tbl)
696 {
697   table_col_str(test_tbl, test_col0_str, "sdsdf");
698   table_col_int(test_tbl, test_col1_int, -10);
699   table_col_int(test_tbl, test_col1_int, 10000);
700   table_col_uint(test_tbl, test_col2_uint, 10);
701   table_col_printf(test_tbl, test_col2_uint, "XXX-%u", 22222);
702   table_col_bool(test_tbl, test_col3_bool, 1);
703   table_col_double(test_tbl, test_col4_double, 1.5);
704   table_col_printf(test_tbl, test_col4_double, "AAA");
705   table_end_row(test_tbl);
706
707   table_col_str(test_tbl, test_col0_str, "test");
708   table_col_int(test_tbl, test_col1_int, -100);
709   table_col_uint(test_tbl, test_col2_uint, 100);
710   table_col_bool(test_tbl, test_col3_bool, 0);
711   table_col_printf(test_tbl, test_col4_double, "%.2lf", 1.5);
712   table_end_row(test_tbl);
713 }
714
715 static void test_simple1(struct fastbuf *out)
716 {
717   struct table *tbl = table_init(&test_tbl);
718
719   // print table with header
720   table_set_col_order_by_name(tbl, "col3_bool");
721   table_start(tbl, out);
722   do_print1(tbl);
723   table_end(tbl);
724
725   // print the same table as in the previous case without header
726   table_set_col_order_by_name(tbl, "col0_str,col2_uint,col1_int,col3_bool");
727   table_start(tbl, out);
728   do_print1(tbl);
729   table_end(tbl);
730
731   // this also tests whether there is need to call table_set_col_order_by_name after table_end was called
732   tbl->print_header = 0;
733   table_start(tbl, out);
734   do_print1(tbl);
735   table_end(tbl);
736   tbl->print_header = 1;
737
738   table_set_col_order_by_name(tbl, "col3_bool");
739   table_start(tbl, out);
740   do_print1(tbl);
741   table_end(tbl);
742
743   table_set_col_order_by_name(tbl, "col3_bool,col0_str");
744   table_start(tbl, out);
745   do_print1(tbl);
746   table_end(tbl);
747
748   table_set_col_order_by_name(tbl, "col0_str,col3_bool,col2_uint");
749   table_start(tbl, out);
750   do_print1(tbl);
751   table_end(tbl);
752
753   table_set_col_order_by_name(tbl, "col0_str,col3_bool,col2_uint,col0_str,col3_bool,col2_uint,col0_str,col3_bool,col2_uint");
754   table_start(tbl, out);
755   do_print1(tbl);
756   table_end(tbl);
757
758   table_set_col_order_by_name(tbl, "col0_str,col1_int,col2_uint,col3_bool,col4_double");
759   table_start(tbl, out);
760   do_print1(tbl);
761   table_end(tbl);
762
763   table_cleanup(tbl);
764 }
765
766 enum test_any_table_cols {
767   test_any_col0_int, test_any_col1_any
768 };
769
770 static struct table_col_info test_any_column_order[] = { TBL_COL(test_any_col0_int), TBL_COL(test_any_col1_any) };
771
772 static struct table_template test_any_tbl = {
773   TBL_COLUMNS {
774     [test_any_col0_int] = TBL_COL_INT("col0_int", 8),
775     [test_any_col1_any] = TBL_COL_ANY("col1_any", 9),
776     TBL_COL_END
777   },
778   TBL_COL_ORDER(test_any_column_order),
779   TBL_OUTPUT_HUMAN_READABLE,
780   TBL_COL_DELIMITER("\t"),
781 };
782
783 static void test_any_type(struct fastbuf *out)
784 {
785   struct table *tbl = table_init(&test_any_tbl);
786
787   table_start(tbl, out);
788
789   table_col_int(tbl, test_any_col0_int, -10);
790   table_col_int(tbl, test_any_col1_any, 10000);
791   table_end_row(tbl);
792
793   table_col_int(tbl, test_any_col0_int, -10);
794   table_col_double(tbl, test_any_col1_any, 1.4);
795   table_end_row(tbl);
796
797   table_col_printf(tbl, test_any_col0_int, "%d", 10);
798   table_col_double(tbl, test_any_col1_any, 1.4);
799   table_end_row(tbl);
800
801   table_end(tbl);
802   table_cleanup(tbl);
803 }
804
805 int main(int argc UNUSED, char **argv UNUSED)
806 {
807   struct fastbuf *out;
808   out = bfdopen_shared(1, 4096);
809
810   test_simple1(out);
811
812   test_any_type(out);
813
814   bclose(out);
815   return 0;
816 }
817
818 #endif