vppinfra: toeplitz hash
[vpp.git] / src / vppinfra / format_table.c
1 /*
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>
25 #include <vppinfra/format_table.h>
26
27 static 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
33 static table_text_attr_t default_body = {
34   .align = TTAA_RIGHT,
35 };
36
37 static table_text_attr_t default_header_col = {
38   .flags = TTAF_FG_COLOR_SET,
39   .fg_color = TTAC_YELLOW,
40   .align = TTAA_CENTER,
41 };
42
43 static 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
49 u8 *
50 format_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
55   if (c == 0)
56     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
120 u8 *
121 format_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
129   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     }
137
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)
143         col_default = t->default_header_col.as_u32 ? &t->default_header_col :
144                                                      &default_header_col;
145       else
146         col_default =
147           t->default_body.as_u32 ? &t->default_body : &default_body;
148
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)
155             row_default = t->default_header_row.as_u32 ?
156                             &t->default_header_row :
157                             &default_header_row;
158           s = format_text_cell (t, s, &t->cells[c][r], row_default,
159                                 t->row_sizes[r]);
160         }
161       if (c + 1 < vec_len (t->cells))
162         s = format (s, "\n");
163     }
164
165   return s;
166 }
167
168 void
169 table_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
178 static table_cell_t *
179 table_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
191 void
192 table_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
208 void
209 table_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
215 void
216 table_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
223 void
224 table_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
231 void
232 table_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
246 void
247 table_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
268 void
269 table_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 }