| /* |
| Copyright (c) 2020 Damjan Marion |
| |
| Permission is hereby granted, free of charge, to any person obtaining |
| a copy of this software and associated documentation files (the |
| "Software"), to deal in the Software without restriction, including |
| without limitation the rights to use, copy, modify, merge, publish, |
| distribute, sublicense, and/or sell copies of the Software, and to |
| permit persons to whom the Software is furnished to do so, subject to |
| the following conditions: |
| |
| The above copyright notice and this permission notice shall be |
| included in all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #include <vppinfra/format.h> |
| #include <vppinfra/format_table.h> |
| |
| static table_text_attr_t default_title = { |
| .flags = TTAF_FG_COLOR_SET | TTAF_BOLD, |
| .fg_color = TTAC_YELLOW, |
| .align = TTAA_CENTER, |
| }; |
| |
| static table_text_attr_t default_body = { |
| .align = TTAA_RIGHT, |
| }; |
| |
| static table_text_attr_t default_header_col = { |
| .flags = TTAF_FG_COLOR_SET, |
| .fg_color = TTAC_YELLOW, |
| .align = TTAA_CENTER, |
| }; |
| |
| static table_text_attr_t default_header_row = { |
| .flags = TTAF_FG_COLOR_SET | TTAF_BOLD, |
| .fg_color = TTAC_GREEN, |
| .align = TTAA_LEFT, |
| }; |
| |
| u8 * |
| format_text_cell (table_t *t, u8 *s, table_cell_t *c, table_text_attr_t *def, |
| int size) |
| { |
| table_text_attr_t _a = {}, *a = &_a; |
| |
| if (c == 0) |
| return format (s, t->no_ansi ? "" : "\x1b[0m"); |
| |
| clib_memcpy (a, def, sizeof (table_text_attr_t)); |
| |
| if (t->no_ansi == 0) |
| { |
| int *codes = 0; |
| if (c->attr.flags & TTAF_FG_COLOR_SET) |
| { |
| a->fg_color = c->attr.fg_color; |
| a->flags |= TTAF_FG_COLOR_SET; |
| a->flags |= c->attr.flags & TTAF_FG_COLOR_BRIGHT; |
| } |
| |
| if (c->attr.flags & TTAF_BG_COLOR_SET) |
| { |
| a->bg_color = c->attr.bg_color; |
| a->flags |= TTAF_BG_COLOR_SET; |
| a->flags |= c->attr.flags & TTAF_BG_COLOR_BRIGHT; |
| } |
| |
| if (a->flags & TTAF_RESET) |
| vec_add1 (codes, 0); |
| |
| if (a->flags & TTAF_BOLD) |
| vec_add1 (codes, 1); |
| |
| if (a->flags & TTAF_DIM) |
| vec_add1 (codes, 2); |
| |
| if (a->flags & TTAF_UNDERLINE) |
| vec_add1 (codes, 4); |
| |
| if (a->flags & TTAF_FG_COLOR_SET) |
| vec_add1 (codes, |
| (a->flags & TTAF_FG_COLOR_BRIGHT ? 90 : 30) + a->fg_color); |
| |
| if (a->flags & TTAF_BG_COLOR_SET) |
| vec_add1 (codes, |
| (a->flags & TTAF_BG_COLOR_BRIGHT ? 100 : 40) + a->bg_color); |
| |
| if (codes) |
| { |
| s = format (s, "\x1b["); |
| for (int i = 0; i < vec_len (codes); i++) |
| s = format (s, "%s%u", i ? ";" : "", codes[i]); |
| s = format (s, "m"); |
| vec_free (codes); |
| } |
| } |
| |
| u8 *fmt = 0; |
| table_text_attr_align_t align = c->attr.align; |
| if (align == TTAA_DEFAULT) |
| align = a->align; |
| if (align == TTAA_LEFT) |
| fmt = format (fmt, "%%-%uv%c", size, 0); |
| else if (align == TTAA_CENTER) |
| fmt = format (fmt, "%%=%uv%c", size, 0); |
| else |
| fmt = format (fmt, "%%%uv%c", size, 0); |
| s = format (s, (char *) fmt, c->text); |
| vec_free (fmt); |
| return format (s, t->no_ansi ? "" : "\x1b[0m"); |
| } |
| |
| u8 * |
| format_table (u8 *s, va_list *args) |
| { |
| table_t *t = va_arg (*args, table_t *); |
| table_cell_t title_cell = { .text = t->title }; |
| int table_width = 0; |
| for (int i = 0; i < vec_len (t->row_sizes); i++) |
| table_width += t->row_sizes[i]; |
| |
| if (t->title) |
| { |
| table_text_attr_t *title_default; |
| title_default = |
| t->default_title.as_u32 ? &t->default_title : &default_title; |
| s = format_text_cell (t, s, &title_cell, title_default, table_width); |
| s = format (s, "\n"); |
| } |
| |
| for (int c = 0; c < vec_len (t->cells); c++) |
| { |
| table_text_attr_t *col_default; |
| |
| if (c < t->n_header_cols) |
| col_default = t->default_header_col.as_u32 ? &t->default_header_col : |
| &default_header_col; |
| else |
| col_default = |
| t->default_body.as_u32 ? &t->default_body : &default_body; |
| |
| for (int r = 0; r < vec_len (t->cells[c]); r++) |
| { |
| table_text_attr_t *row_default = col_default; |
| if (r) |
| s = format (s, " "); |
| if (r < t->n_header_rows && c >= t->n_header_cols) |
| row_default = t->default_header_row.as_u32 ? |
| &t->default_header_row : |
| &default_header_row; |
| s = format_text_cell (t, s, &t->cells[c][r], row_default, |
| t->row_sizes[r]); |
| } |
| if (c + 1 < vec_len (t->cells)) |
| s = format (s, "\n"); |
| } |
| |
| return s; |
| } |
| |
| void |
| table_format_title (table_t *t, char *fmt, ...) |
| { |
| va_list va; |
| |
| va_start (va, fmt); |
| t->title = va_format (t->title, fmt, &va); |
| va_end (va); |
| } |
| |
| static table_cell_t * |
| table_get_cell (table_t *t, int c, int r) |
| { |
| c += t->n_header_cols; |
| r += t->n_header_rows; |
| |
| /* grow table if needed */ |
| vec_validate (t->cells, c); |
| for (int i = 0; i < vec_len (t->cells); i++) |
| vec_validate (t->cells[i], r); |
| return &t->cells[c][r]; |
| } |
| |
| void |
| table_format_cell (table_t *t, int c, int r, char *fmt, ...) |
| { |
| table_cell_t *cell = table_get_cell (t, c, r); |
| va_list va; |
| |
| c += t->n_header_cols; |
| r += t->n_header_rows; |
| |
| va_start (va, fmt); |
| cell->text = va_format (t->cells[c][r].text, fmt, &va); |
| va_end (va); |
| |
| vec_validate (t->row_sizes, r); |
| t->row_sizes[r] = clib_max (t->row_sizes[r], vec_len (t->cells[c][r].text)); |
| } |
| |
| void |
| table_set_cell_align (table_t *t, int c, int r, table_text_attr_align_t a) |
| { |
| table_cell_t *cell = table_get_cell (t, c, r); |
| cell->attr.align = a; |
| } |
| |
| void |
| table_set_cell_fg_color (table_t *t, int c, int r, table_text_attr_color_t v) |
| { |
| table_cell_t *cell = table_get_cell (t, c, r); |
| cell->attr.fg_color = v & 0x7; |
| cell->attr.flags |= TTAF_FG_COLOR_SET; |
| if (v & 8) |
| cell->attr.flags |= TTAF_FG_COLOR_BRIGHT; |
| else |
| cell->attr.flags &= ~TTAF_FG_COLOR_BRIGHT; |
| } |
| |
| void |
| table_set_cell_bg_color (table_t *t, int c, int r, table_text_attr_color_t v) |
| { |
| table_cell_t *cell = table_get_cell (t, c, r); |
| cell->attr.bg_color = v & 0x7; |
| cell->attr.flags |= TTAF_BG_COLOR_SET; |
| if (v & 8) |
| cell->attr.flags |= TTAF_BG_COLOR_BRIGHT; |
| else |
| cell->attr.flags &= ~TTAF_BG_COLOR_BRIGHT; |
| } |
| |
| void |
| table_free (table_t *t) |
| { |
| for (int c = 0; c < vec_len (t->cells); c++) |
| { |
| for (int r = 0; r < vec_len (t->cells[c]); r++) |
| vec_free (t->cells[c][r].text); |
| vec_free (t->cells[c]); |
| } |
| vec_free (t->cells); |
| vec_free (t->row_sizes); |
| vec_free (t->title); |
| clib_memset (t, 0, sizeof (table_t)); |
| } |
| |
| void |
| table_add_header_col (table_t *t, int n_strings, ...) |
| { |
| va_list arg; |
| int r, c = t->n_header_cols++; |
| int n_rows; |
| |
| vec_insert (t->cells, 1, c); |
| n_rows = clib_max (n_strings, 1); |
| n_rows = clib_max (vec_len (t->row_sizes), n_rows); |
| vec_validate (t->cells[c], n_rows - 1); |
| |
| va_start (arg, n_strings); |
| for (r = 0; r < n_rows; r++) |
| { |
| if (n_strings-- > 0) |
| table_format_cell (t, -1, r - t->n_header_rows, "%s", |
| va_arg (arg, char *)); |
| } |
| va_end (arg); |
| } |
| |
| void |
| table_add_header_row (table_t *t, int n_strings, ...) |
| { |
| va_list arg; |
| int c, r = t->n_header_rows++; |
| |
| vec_validate (t->cells, n_strings + t->n_header_cols - 1); |
| |
| va_start (arg, n_strings); |
| for (c = t->n_header_cols; c < vec_len (t->cells); c++) |
| { |
| vec_insert (t->cells[c + t->n_header_cols], 1, r); |
| if (n_strings-- > 0) |
| table_format_cell (t, c, -1, "%s", va_arg (arg, char *)); |
| } |
| va_end (arg); |
| } |