blob: 5fe7521af86f93760a67de3ecb4370f4c39df87e [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;
128 for (int i = 0; i < vec_len (t->row_sizes); i++)
129 table_width += t->row_sizes[i];
130
Nathan Skrzypczak8430b8d2021-09-17 14:32:03 +0200131 if (t->title)
132 {
133 table_text_attr_t *title_default;
134 title_default =
135 t->default_title.as_u32 ? &t->default_title : &default_title;
136 s = format_text_cell (t, s, &title_cell, title_default, table_width);
137 s = format (s, "\n");
138 }
Damjan Marion8b60fb02020-11-27 20:15:17 +0100139
140 for (int c = 0; c < vec_len (t->cells); c++)
141 {
142 table_text_attr_t *col_default;
143
144 if (c < t->n_header_cols)
Nathan Skrzypczak8430b8d2021-09-17 14:32:03 +0200145 col_default = t->default_header_col.as_u32 ? &t->default_header_col :
146 &default_header_col;
Damjan Marion8b60fb02020-11-27 20:15:17 +0100147 else
Nathan Skrzypczak8430b8d2021-09-17 14:32:03 +0200148 col_default =
149 t->default_body.as_u32 ? &t->default_body : &default_body;
Damjan Marion8b60fb02020-11-27 20:15:17 +0100150
151 for (int r = 0; r < vec_len (t->cells[c]); r++)
152 {
153 table_text_attr_t *row_default = col_default;
154 if (r)
155 s = format (s, " ");
156 if (r < t->n_header_rows && c >= t->n_header_cols)
Nathan Skrzypczak8430b8d2021-09-17 14:32:03 +0200157 row_default = t->default_header_row.as_u32 ?
158 &t->default_header_row :
159 &default_header_row;
Damjan Marion8b60fb02020-11-27 20:15:17 +0100160 s = format_text_cell (t, s, &t->cells[c][r], row_default,
161 t->row_sizes[r]);
162 }
Nathan Skrzypczak8430b8d2021-09-17 14:32:03 +0200163 if (c + 1 < vec_len (t->cells))
164 s = format (s, "\n");
Damjan Marion8b60fb02020-11-27 20:15:17 +0100165 }
166
167 return s;
168}
169
170void
171table_format_title (table_t *t, char *fmt, ...)
172{
173 va_list va;
174
175 va_start (va, fmt);
176 t->title = va_format (t->title, fmt, &va);
177 va_end (va);
178}
179
180static table_cell_t *
181table_get_cell (table_t *t, int c, int r)
182{
183 c += t->n_header_cols;
184 r += t->n_header_rows;
185
186 /* grow table if needed */
187 vec_validate (t->cells, c);
188 for (int i = 0; i < vec_len (t->cells); i++)
189 vec_validate (t->cells[i], r);
190 return &t->cells[c][r];
191}
192
193void
194table_format_cell (table_t *t, int c, int r, char *fmt, ...)
195{
196 table_cell_t *cell = table_get_cell (t, c, r);
197 va_list va;
198
199 c += t->n_header_cols;
200 r += t->n_header_rows;
201
202 va_start (va, fmt);
203 cell->text = va_format (t->cells[c][r].text, fmt, &va);
204 va_end (va);
205
206 vec_validate (t->row_sizes, r);
207 t->row_sizes[r] = clib_max (t->row_sizes[r], vec_len (t->cells[c][r].text));
208}
209
210void
211table_set_cell_align (table_t *t, int c, int r, table_text_attr_align_t a)
212{
213 table_cell_t *cell = table_get_cell (t, c, r);
214 cell->attr.align = a;
215}
216
217void
218table_set_cell_fg_color (table_t *t, int c, int r, table_text_attr_color_t v)
219{
220 table_cell_t *cell = table_get_cell (t, c, r);
Damjan Marion853530b2022-04-08 13:42:41 +0200221 cell->attr.fg_color = v & 0x7;
Damjan Marion8b60fb02020-11-27 20:15:17 +0100222 cell->attr.flags |= TTAF_FG_COLOR_SET;
Damjan Marion853530b2022-04-08 13:42:41 +0200223 if (v & 8)
224 cell->attr.flags |= TTAF_FG_COLOR_BRIGHT;
225 else
226 cell->attr.flags &= ~TTAF_FG_COLOR_BRIGHT;
Damjan Marion8b60fb02020-11-27 20:15:17 +0100227}
228
229void
230table_set_cell_bg_color (table_t *t, int c, int r, table_text_attr_color_t v)
231{
232 table_cell_t *cell = table_get_cell (t, c, r);
Damjan Marion853530b2022-04-08 13:42:41 +0200233 cell->attr.bg_color = v & 0x7;
Damjan Marion8b60fb02020-11-27 20:15:17 +0100234 cell->attr.flags |= TTAF_BG_COLOR_SET;
Damjan Marion853530b2022-04-08 13:42:41 +0200235 if (v & 8)
236 cell->attr.flags |= TTAF_BG_COLOR_BRIGHT;
237 else
238 cell->attr.flags &= ~TTAF_BG_COLOR_BRIGHT;
Damjan Marion8b60fb02020-11-27 20:15:17 +0100239}
240
241void
242table_free (table_t *t)
243{
244 for (int c = 0; c < vec_len (t->cells); c++)
245 {
246 for (int r = 0; r < vec_len (t->cells[c]); r++)
247 vec_free (t->cells[c][r].text);
248 vec_free (t->cells[c]);
249 }
250 vec_free (t->cells);
251 vec_free (t->row_sizes);
252 vec_free (t->title);
253 clib_memset (t, 0, sizeof (table_t));
254}
255
256void
257table_add_header_col (table_t *t, int n_strings, ...)
258{
259 va_list arg;
260 int r, c = t->n_header_cols++;
261 int n_rows;
262
263 vec_insert (t->cells, 1, c);
264 n_rows = clib_max (n_strings, 1);
265 n_rows = clib_max (vec_len (t->row_sizes), n_rows);
266 vec_validate (t->cells[c], n_rows - 1);
267
268 va_start (arg, n_strings);
269 for (r = 0; r < n_rows; r++)
270 {
271 if (n_strings-- > 0)
272 table_format_cell (t, -1, r - t->n_header_rows, "%s",
273 va_arg (arg, char *));
274 }
275 va_end (arg);
276}
277
278void
279table_add_header_row (table_t *t, int n_strings, ...)
280{
281 va_list arg;
282 int c, r = t->n_header_rows++;
283
284 vec_validate (t->cells, n_strings + t->n_header_cols - 1);
285
286 va_start (arg, n_strings);
287 for (c = t->n_header_cols; c < vec_len (t->cells); c++)
288 {
289 vec_insert (t->cells[c + t->n_header_cols], 1, r);
290 if (n_strings-- > 0)
291 table_format_cell (t, c, -1, "%s", va_arg (arg, char *));
292 }
293 va_end (arg);
294}