blob: 8b218f5f2e18ac69892caa91750407dd1a6ae11a [file] [log] [blame]
Damjan Marion8b60fb02020-11-27 20:15:17 +01001/*
2 Copyright (c) 2020 Damjan Marion
3
4 Permission is hereby granted, free of charge, to any person obtaining
5 a copy of this software and associated documentation files (the
6 "Software"), to deal in the Software without restriction, including
7 without limitation the rights to use, copy, modify, merge, publish,
8 distribute, sublicense, and/or sell copies of the Software, and to
9 permit persons to whom the Software is furnished to do so, subject to
10 the following conditions:
11
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22*/
23
24#include <vppinfra/format.h>
Nathan Skrzypczak0e658402021-09-17 11:51:46 +020025#include <vppinfra/format_table.h>
Damjan Marion8b60fb02020-11-27 20:15:17 +010026
27static table_text_attr_t default_title = {
28 .flags = TTAF_FG_COLOR_SET | TTAF_BOLD,
29 .fg_color = TTAC_YELLOW,
30 .align = TTAA_CENTER,
31};
32
33static table_text_attr_t default_body = {
34 .align = TTAA_RIGHT,
35};
36
37static table_text_attr_t default_header_col = {
38 .flags = TTAF_FG_COLOR_SET,
39 .fg_color = TTAC_YELLOW,
40 .align = TTAA_CENTER,
41};
42
43static table_text_attr_t default_header_row = {
44 .flags = TTAF_FG_COLOR_SET | TTAF_BOLD,
45 .fg_color = TTAC_GREEN,
46 .align = TTAA_LEFT,
47};
48
49u8 *
50format_text_cell (table_t *t, u8 *s, table_cell_t *c, table_text_attr_t *def,
51 int size)
52{
53 table_text_attr_t _a = {}, *a = &_a;
54
Florin Coras3b7003b2021-10-12 08:45:46 -070055 if (c == 0)
Damjan Marion8b60fb02020-11-27 20:15:17 +010056 return format (s, t->no_ansi ? "" : "\x1b[0m");
57
58 clib_memcpy (a, def, sizeof (table_text_attr_t));
59
60 if (t->no_ansi == 0)
61 {
62 int *codes = 0;
63 if (c->attr.flags & TTAF_FG_COLOR_SET)
64 {
65 a->fg_color = c->attr.fg_color;
66 a->flags |= TTAF_FG_COLOR_SET;
67 }
68
69 if (c->attr.flags & TTAF_BG_COLOR_SET)
70 {
71 a->bg_color = c->attr.bg_color;
72 a->flags |= TTAF_BG_COLOR_SET;
73 }
74
75 if (a->flags & TTAF_RESET)
76 vec_add1 (codes, 0);
77
78 if (a->flags & TTAF_BOLD)
79 vec_add1 (codes, 1);
80
81 if (a->flags & TTAF_DIM)
82 vec_add1 (codes, 2);
83
84 if (a->flags & TTAF_UNDERLINE)
85 vec_add1 (codes, 4);
86
87 if (a->flags & TTAF_FG_COLOR_SET)
88 vec_add1 (codes,
89 (a->flags & TTAF_FG_COLOR_BRIGHT ? 90 : 30) + a->fg_color);
90
91 if (a->flags & TTAF_BG_COLOR_SET)
92 vec_add1 (codes,
93 (a->flags & TTAF_BG_COLOR_BRIGHT ? 100 : 40) + a->bg_color);
94
95 if (codes)
96 {
97 s = format (s, "\x1b[");
98 for (int i = 0; i < vec_len (codes); i++)
99 s = format (s, "%s%u", i ? ";" : "", codes[i]);
100 s = format (s, "m");
101 vec_free (codes);
102 }
103 }
104
105 u8 *fmt = 0;
106 table_text_attr_align_t align = c->attr.align;
107 if (align == TTAA_DEFAULT)
108 align = a->align;
109 if (align == TTAA_LEFT)
110 fmt = format (fmt, "%%-%uv%c", size, 0);
111 else if (align == TTAA_CENTER)
112 fmt = format (fmt, "%%=%uv%c", size, 0);
113 else
114 fmt = format (fmt, "%%%uv%c", size, 0);
115 s = format (s, (char *) fmt, c->text);
116 vec_free (fmt);
117 return format (s, t->no_ansi ? "" : "\x1b[0m");
118}
119
120u8 *
121format_table (u8 *s, va_list *args)
122{
123 table_t *t = va_arg (*args, table_t *);
124 table_cell_t title_cell = { .text = t->title };
125 int table_width = 0;
126 for (int i = 0; i < vec_len (t->row_sizes); i++)
127 table_width += t->row_sizes[i];
128
Nathan Skrzypczak8430b8d2021-09-17 14:32:03 +0200129 if (t->title)
130 {
131 table_text_attr_t *title_default;
132 title_default =
133 t->default_title.as_u32 ? &t->default_title : &default_title;
134 s = format_text_cell (t, s, &title_cell, title_default, table_width);
135 s = format (s, "\n");
136 }
Damjan Marion8b60fb02020-11-27 20:15:17 +0100137
138 for (int c = 0; c < vec_len (t->cells); c++)
139 {
140 table_text_attr_t *col_default;
141
142 if (c < t->n_header_cols)
Nathan Skrzypczak8430b8d2021-09-17 14:32:03 +0200143 col_default = t->default_header_col.as_u32 ? &t->default_header_col :
144 &default_header_col;
Damjan Marion8b60fb02020-11-27 20:15:17 +0100145 else
Nathan Skrzypczak8430b8d2021-09-17 14:32:03 +0200146 col_default =
147 t->default_body.as_u32 ? &t->default_body : &default_body;
Damjan Marion8b60fb02020-11-27 20:15:17 +0100148
149 for (int r = 0; r < vec_len (t->cells[c]); r++)
150 {
151 table_text_attr_t *row_default = col_default;
152 if (r)
153 s = format (s, " ");
154 if (r < t->n_header_rows && c >= t->n_header_cols)
Nathan Skrzypczak8430b8d2021-09-17 14:32:03 +0200155 row_default = t->default_header_row.as_u32 ?
156 &t->default_header_row :
157 &default_header_row;
Damjan Marion8b60fb02020-11-27 20:15:17 +0100158 s = format_text_cell (t, s, &t->cells[c][r], row_default,
159 t->row_sizes[r]);
160 }
Nathan Skrzypczak8430b8d2021-09-17 14:32:03 +0200161 if (c + 1 < vec_len (t->cells))
162 s = format (s, "\n");
Damjan Marion8b60fb02020-11-27 20:15:17 +0100163 }
164
165 return s;
166}
167
168void
169table_format_title (table_t *t, char *fmt, ...)
170{
171 va_list va;
172
173 va_start (va, fmt);
174 t->title = va_format (t->title, fmt, &va);
175 va_end (va);
176}
177
178static table_cell_t *
179table_get_cell (table_t *t, int c, int r)
180{
181 c += t->n_header_cols;
182 r += t->n_header_rows;
183
184 /* grow table if needed */
185 vec_validate (t->cells, c);
186 for (int i = 0; i < vec_len (t->cells); i++)
187 vec_validate (t->cells[i], r);
188 return &t->cells[c][r];
189}
190
191void
192table_format_cell (table_t *t, int c, int r, char *fmt, ...)
193{
194 table_cell_t *cell = table_get_cell (t, c, r);
195 va_list va;
196
197 c += t->n_header_cols;
198 r += t->n_header_rows;
199
200 va_start (va, fmt);
201 cell->text = va_format (t->cells[c][r].text, fmt, &va);
202 va_end (va);
203
204 vec_validate (t->row_sizes, r);
205 t->row_sizes[r] = clib_max (t->row_sizes[r], vec_len (t->cells[c][r].text));
206}
207
208void
209table_set_cell_align (table_t *t, int c, int r, table_text_attr_align_t a)
210{
211 table_cell_t *cell = table_get_cell (t, c, r);
212 cell->attr.align = a;
213}
214
215void
216table_set_cell_fg_color (table_t *t, int c, int r, table_text_attr_color_t v)
217{
218 table_cell_t *cell = table_get_cell (t, c, r);
219 cell->attr.fg_color = v;
220 cell->attr.flags |= TTAF_FG_COLOR_SET;
221}
222
223void
224table_set_cell_bg_color (table_t *t, int c, int r, table_text_attr_color_t v)
225{
226 table_cell_t *cell = table_get_cell (t, c, r);
227 cell->attr.bg_color = v;
228 cell->attr.flags |= TTAF_BG_COLOR_SET;
229}
230
231void
232table_free (table_t *t)
233{
234 for (int c = 0; c < vec_len (t->cells); c++)
235 {
236 for (int r = 0; r < vec_len (t->cells[c]); r++)
237 vec_free (t->cells[c][r].text);
238 vec_free (t->cells[c]);
239 }
240 vec_free (t->cells);
241 vec_free (t->row_sizes);
242 vec_free (t->title);
243 clib_memset (t, 0, sizeof (table_t));
244}
245
246void
247table_add_header_col (table_t *t, int n_strings, ...)
248{
249 va_list arg;
250 int r, c = t->n_header_cols++;
251 int n_rows;
252
253 vec_insert (t->cells, 1, c);
254 n_rows = clib_max (n_strings, 1);
255 n_rows = clib_max (vec_len (t->row_sizes), n_rows);
256 vec_validate (t->cells[c], n_rows - 1);
257
258 va_start (arg, n_strings);
259 for (r = 0; r < n_rows; r++)
260 {
261 if (n_strings-- > 0)
262 table_format_cell (t, -1, r - t->n_header_rows, "%s",
263 va_arg (arg, char *));
264 }
265 va_end (arg);
266}
267
268void
269table_add_header_row (table_t *t, int n_strings, ...)
270{
271 va_list arg;
272 int c, r = t->n_header_rows++;
273
274 vec_validate (t->cells, n_strings + t->n_header_cols - 1);
275
276 va_start (arg, n_strings);
277 for (c = t->n_header_cols; c < vec_len (t->cells); c++)
278 {
279 vec_insert (t->cells[c + t->n_header_cols], 1, r);
280 if (n_strings-- > 0)
281 table_format_cell (t, c, -1, "%s", va_arg (arg, char *));
282 }
283 va_end (arg);
284}