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