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