blob: dd92e417acd318b1d528ba933daccaad16f63170 [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;
Damjan Marion853530b2022-04-08 13:42:41 +020067 a->flags |= c->attr.flags & TTAF_FG_COLOR_BRIGHT;
Damjan Marion8b60fb02020-11-27 20:15:17 +010068 }
69
70 if (c->attr.flags & TTAF_BG_COLOR_SET)
71 {
72 a->bg_color = c->attr.bg_color;
73 a->flags |= TTAF_BG_COLOR_SET;
Damjan Marion853530b2022-04-08 13:42:41 +020074 a->flags |= c->attr.flags & TTAF_BG_COLOR_BRIGHT;
Damjan Marion8b60fb02020-11-27 20:15:17 +010075 }
76
77 if (a->flags & TTAF_RESET)
78 vec_add1 (codes, 0);
79
80 if (a->flags & TTAF_BOLD)
81 vec_add1 (codes, 1);
82
83 if (a->flags & TTAF_DIM)
84 vec_add1 (codes, 2);
85
86 if (a->flags & TTAF_UNDERLINE)
87 vec_add1 (codes, 4);
88
89 if (a->flags & TTAF_FG_COLOR_SET)
90 vec_add1 (codes,
91 (a->flags & TTAF_FG_COLOR_BRIGHT ? 90 : 30) + a->fg_color);
92
93 if (a->flags & TTAF_BG_COLOR_SET)
94 vec_add1 (codes,
95 (a->flags & TTAF_BG_COLOR_BRIGHT ? 100 : 40) + a->bg_color);
96
97 if (codes)
98 {
99 s = format (s, "\x1b[");
100 for (int i = 0; i < vec_len (codes); i++)
101 s = format (s, "%s%u", i ? ";" : "", codes[i]);
102 s = format (s, "m");
103 vec_free (codes);
104 }
105 }
106
107 u8 *fmt = 0;
108 table_text_attr_align_t align = c->attr.align;
109 if (align == TTAA_DEFAULT)
110 align = a->align;
111 if (align == TTAA_LEFT)
112 fmt = format (fmt, "%%-%uv%c", size, 0);
113 else if (align == TTAA_CENTER)
114 fmt = format (fmt, "%%=%uv%c", size, 0);
115 else
116 fmt = format (fmt, "%%%uv%c", size, 0);
117 s = format (s, (char *) fmt, c->text);
118 vec_free (fmt);
119 return format (s, t->no_ansi ? "" : "\x1b[0m");
120}
121
122u8 *
123format_table (u8 *s, va_list *args)
124{
125 table_t *t = va_arg (*args, table_t *);
126 table_cell_t title_cell = { .text = t->title };
127 int table_width = 0;
Damjan Mariondd8f8de2023-11-13 21:46:48 +0000128 u32 indent = format_get_indent (s);
Damjan Marion8b60fb02020-11-27 20:15:17 +0100129 for (int i = 0; i < vec_len (t->row_sizes); i++)
130 table_width += t->row_sizes[i];
131
Nathan Skrzypczak8430b8d2021-09-17 14:32:03 +0200132 if (t->title)
133 {
134 table_text_attr_t *title_default;
135 title_default =
136 t->default_title.as_u32 ? &t->default_title : &default_title;
137 s = format_text_cell (t, s, &title_cell, title_default, table_width);
Damjan Mariondd8f8de2023-11-13 21:46:48 +0000138 s = format (s, "\n%U", format_white_space, indent);
Nathan Skrzypczak8430b8d2021-09-17 14:32:03 +0200139 }
Damjan Marion8b60fb02020-11-27 20:15:17 +0100140
141 for (int c = 0; c < vec_len (t->cells); c++)
142 {
143 table_text_attr_t *col_default;
144
145 if (c < t->n_header_cols)
Nathan Skrzypczak8430b8d2021-09-17 14:32:03 +0200146 col_default = t->default_header_col.as_u32 ? &t->default_header_col :
147 &default_header_col;
Damjan Marion8b60fb02020-11-27 20:15:17 +0100148 else
Nathan Skrzypczak8430b8d2021-09-17 14:32:03 +0200149 col_default =
150 t->default_body.as_u32 ? &t->default_body : &default_body;
Damjan Marion8b60fb02020-11-27 20:15:17 +0100151
152 for (int r = 0; r < vec_len (t->cells[c]); r++)
153 {
154 table_text_attr_t *row_default = col_default;
155 if (r)
156 s = format (s, " ");
157 if (r < t->n_header_rows && c >= t->n_header_cols)
Nathan Skrzypczak8430b8d2021-09-17 14:32:03 +0200158 row_default = t->default_header_row.as_u32 ?
159 &t->default_header_row :
160 &default_header_row;
Damjan Marion8b60fb02020-11-27 20:15:17 +0100161 s = format_text_cell (t, s, &t->cells[c][r], row_default,
162 t->row_sizes[r]);
163 }
Nathan Skrzypczak8430b8d2021-09-17 14:32:03 +0200164 if (c + 1 < vec_len (t->cells))
Damjan Mariondd8f8de2023-11-13 21:46:48 +0000165 s = format (s, "\n%U", format_white_space, indent);
Damjan Marion8b60fb02020-11-27 20:15:17 +0100166 }
167
168 return s;
169}
170
171void
172table_format_title (table_t *t, char *fmt, ...)
173{
174 va_list va;
175
176 va_start (va, fmt);
177 t->title = va_format (t->title, fmt, &va);
178 va_end (va);
179}
180
181static table_cell_t *
182table_get_cell (table_t *t, int c, int r)
183{
184 c += t->n_header_cols;
185 r += t->n_header_rows;
186
187 /* grow table if needed */
188 vec_validate (t->cells, c);
189 for (int i = 0; i < vec_len (t->cells); i++)
190 vec_validate (t->cells[i], r);
191 return &t->cells[c][r];
192}
193
194void
195table_format_cell (table_t *t, int c, int r, char *fmt, ...)
196{
197 table_cell_t *cell = table_get_cell (t, c, r);
198 va_list va;
199
200 c += t->n_header_cols;
201 r += t->n_header_rows;
202
203 va_start (va, fmt);
204 cell->text = va_format (t->cells[c][r].text, fmt, &va);
205 va_end (va);
206
207 vec_validate (t->row_sizes, r);
208 t->row_sizes[r] = clib_max (t->row_sizes[r], vec_len (t->cells[c][r].text));
209}
210
211void
212table_set_cell_align (table_t *t, int c, int r, table_text_attr_align_t a)
213{
214 table_cell_t *cell = table_get_cell (t, c, r);
215 cell->attr.align = a;
216}
217
218void
219table_set_cell_fg_color (table_t *t, int c, int r, table_text_attr_color_t v)
220{
221 table_cell_t *cell = table_get_cell (t, c, r);
Damjan Marion853530b2022-04-08 13:42:41 +0200222 cell->attr.fg_color = v & 0x7;
Damjan Marion8b60fb02020-11-27 20:15:17 +0100223 cell->attr.flags |= TTAF_FG_COLOR_SET;
Damjan Marion853530b2022-04-08 13:42:41 +0200224 if (v & 8)
225 cell->attr.flags |= TTAF_FG_COLOR_BRIGHT;
226 else
227 cell->attr.flags &= ~TTAF_FG_COLOR_BRIGHT;
Damjan Marion8b60fb02020-11-27 20:15:17 +0100228}
229
230void
231table_set_cell_bg_color (table_t *t, int c, int r, table_text_attr_color_t v)
232{
233 table_cell_t *cell = table_get_cell (t, c, r);
Damjan Marion853530b2022-04-08 13:42:41 +0200234 cell->attr.bg_color = v & 0x7;
Damjan Marion8b60fb02020-11-27 20:15:17 +0100235 cell->attr.flags |= TTAF_BG_COLOR_SET;
Damjan Marion853530b2022-04-08 13:42:41 +0200236 if (v & 8)
237 cell->attr.flags |= TTAF_BG_COLOR_BRIGHT;
238 else
239 cell->attr.flags &= ~TTAF_BG_COLOR_BRIGHT;
Damjan Marion8b60fb02020-11-27 20:15:17 +0100240}
241
242void
243table_free (table_t *t)
244{
245 for (int c = 0; c < vec_len (t->cells); c++)
246 {
247 for (int r = 0; r < vec_len (t->cells[c]); r++)
248 vec_free (t->cells[c][r].text);
249 vec_free (t->cells[c]);
250 }
251 vec_free (t->cells);
252 vec_free (t->row_sizes);
253 vec_free (t->title);
254 clib_memset (t, 0, sizeof (table_t));
255}
256
257void
258table_add_header_col (table_t *t, int n_strings, ...)
259{
260 va_list arg;
261 int r, c = t->n_header_cols++;
262 int n_rows;
263
264 vec_insert (t->cells, 1, c);
265 n_rows = clib_max (n_strings, 1);
266 n_rows = clib_max (vec_len (t->row_sizes), n_rows);
267 vec_validate (t->cells[c], n_rows - 1);
268
269 va_start (arg, n_strings);
270 for (r = 0; r < n_rows; r++)
271 {
272 if (n_strings-- > 0)
273 table_format_cell (t, -1, r - t->n_header_rows, "%s",
274 va_arg (arg, char *));
275 }
276 va_end (arg);
277}
278
279void
280table_add_header_row (table_t *t, int n_strings, ...)
281{
282 va_list arg;
283 int c, r = t->n_header_rows++;
284
285 vec_validate (t->cells, n_strings + t->n_header_cols - 1);
286
287 va_start (arg, n_strings);
288 for (c = t->n_header_cols; c < vec_len (t->cells); c++)
289 {
290 vec_insert (t->cells[c + t->n_header_cols], 1, r);
291 if (n_strings-- > 0)
292 table_format_cell (t, c, -1, "%s", va_arg (arg, char *));
293 }
294 va_end (arg);
295}