1 /* $NetBSD: rtbl.c,v 1.2 2017/01/28 21:31:50 christos Exp $ */
2
3 /*
4 * Copyright (c) 2000, 2002, 2004 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <config.h>
37
38 #include <krb5/roken.h>
39 #include <ctype.h>
40 #include <krb5/rtbl.h>
41
42 struct column_entry {
43 char *data;
44 };
45
46 struct column_data {
47 char *header;
48 char *prefix;
49 int width;
50 unsigned flags;
51 size_t num_rows;
52 struct column_entry *rows;
53 unsigned int column_id;
54 char *suffix;
55 };
56
57 struct rtbl_data {
58 char *column_prefix;
59 size_t num_columns;
60 struct column_data **columns;
61 unsigned int flags;
62 char *column_separator;
63 };
64
65 ROKEN_LIB_FUNCTION rtbl_t ROKEN_LIB_CALL
rtbl_create(void)66 rtbl_create (void)
67 {
68 return calloc (1, sizeof (struct rtbl_data));
69 }
70
71 ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
rtbl_set_flags(rtbl_t table,unsigned int flags)72 rtbl_set_flags (rtbl_t table, unsigned int flags)
73 {
74 table->flags = flags;
75 }
76
77 ROKEN_LIB_FUNCTION unsigned int ROKEN_LIB_CALL
rtbl_get_flags(rtbl_t table)78 rtbl_get_flags (rtbl_t table)
79 {
80 return table->flags;
81 }
82
83 static struct column_data *
rtbl_get_column_by_id(rtbl_t table,unsigned int id)84 rtbl_get_column_by_id (rtbl_t table, unsigned int id)
85 {
86 size_t i;
87 for(i = 0; i < table->num_columns; i++)
88 if(table->columns[i]->column_id == id)
89 return table->columns[i];
90 return NULL;
91 }
92
93 static struct column_data *
rtbl_get_column(rtbl_t table,const char * column)94 rtbl_get_column (rtbl_t table, const char *column)
95 {
96 size_t i;
97 for(i = 0; i < table->num_columns; i++)
98 if(strcmp(table->columns[i]->header, column) == 0)
99 return table->columns[i];
100 return NULL;
101 }
102
103 ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
rtbl_destroy(rtbl_t table)104 rtbl_destroy (rtbl_t table)
105 {
106 size_t i, j;
107
108 for (i = 0; i < table->num_columns; i++) {
109 struct column_data *c = table->columns[i];
110
111 for (j = 0; j < c->num_rows; j++)
112 free (c->rows[j].data);
113 free (c->rows);
114 free (c->header);
115 free (c->prefix);
116 free (c->suffix);
117 free (c);
118 }
119 free (table->column_prefix);
120 free (table->column_separator);
121 free (table->columns);
122 free (table);
123 }
124
125 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_add_column_by_id(rtbl_t table,unsigned int id,const char * header,unsigned int flags)126 rtbl_add_column_by_id (rtbl_t table, unsigned int id,
127 const char *header, unsigned int flags)
128 {
129 struct column_data *col, **tmp;
130
131 tmp = realloc (table->columns, (table->num_columns + 1) * sizeof (*tmp));
132 if (tmp == NULL)
133 return ENOMEM;
134 table->columns = tmp;
135 col = malloc (sizeof (*col));
136 if (col == NULL)
137 return ENOMEM;
138 col->header = strdup (header);
139 if (col->header == NULL) {
140 free (col);
141 return ENOMEM;
142 }
143 col->prefix = NULL;
144 col->width = 0;
145 col->flags = flags;
146 col->num_rows = 0;
147 col->rows = NULL;
148 col->column_id = id;
149 col->suffix = NULL;
150 table->columns[table->num_columns++] = col;
151 return 0;
152 }
153
154 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_add_column(rtbl_t table,const char * header,unsigned int flags)155 rtbl_add_column (rtbl_t table, const char *header, unsigned int flags)
156 {
157 return rtbl_add_column_by_id(table, 0, header, flags);
158 }
159
160 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_new_row(rtbl_t table)161 rtbl_new_row(rtbl_t table)
162 {
163 size_t max_rows = 0;
164 size_t c;
165 for (c = 0; c < table->num_columns; c++)
166 if(table->columns[c]->num_rows > max_rows)
167 max_rows = table->columns[c]->num_rows;
168 for (c = 0; c < table->num_columns; c++) {
169 struct column_entry *tmp;
170
171 if(table->columns[c]->num_rows == max_rows)
172 continue;
173 tmp = realloc(table->columns[c]->rows,
174 max_rows * sizeof(table->columns[c]->rows[0]));
175 if(tmp == NULL)
176 return ENOMEM;
177 table->columns[c]->rows = tmp;
178 while(table->columns[c]->num_rows < max_rows) {
179 if((tmp[table->columns[c]->num_rows++].data = strdup("")) == NULL)
180 return ENOMEM;
181 }
182 }
183 return 0;
184 }
185
186 static void
column_compute_width(rtbl_t table,struct column_data * column)187 column_compute_width (rtbl_t table, struct column_data *column)
188 {
189 size_t i;
190
191 if(table->flags & RTBL_HEADER_STYLE_NONE)
192 column->width = 0;
193 else
194 column->width = (int)strlen (column->header);
195 for (i = 0; i < column->num_rows; i++)
196 column->width = max (column->width, (int) strlen (column->rows[i].data));
197 }
198
199 /* DEPRECATED */
200 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_set_prefix(rtbl_t table,const char * prefix)201 rtbl_set_prefix (rtbl_t table, const char *prefix)
202 {
203 if (table->column_prefix)
204 free (table->column_prefix);
205 table->column_prefix = strdup (prefix);
206 if (table->column_prefix == NULL)
207 return ENOMEM;
208 return 0;
209 }
210
211 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_set_separator(rtbl_t table,const char * separator)212 rtbl_set_separator (rtbl_t table, const char *separator)
213 {
214 if (table->column_separator)
215 free (table->column_separator);
216 table->column_separator = strdup (separator);
217 if (table->column_separator == NULL)
218 return ENOMEM;
219 return 0;
220 }
221
222 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_set_column_prefix(rtbl_t table,const char * column,const char * prefix)223 rtbl_set_column_prefix (rtbl_t table, const char *column,
224 const char *prefix)
225 {
226 struct column_data *c = rtbl_get_column (table, column);
227
228 if (c == NULL)
229 return -1;
230 if (c->prefix)
231 free (c->prefix);
232 c->prefix = strdup (prefix);
233 if (c->prefix == NULL)
234 return ENOMEM;
235 return 0;
236 }
237
238 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_set_column_affix_by_id(rtbl_t table,unsigned int id,const char * prefix,const char * suffix)239 rtbl_set_column_affix_by_id(rtbl_t table, unsigned int id,
240 const char *prefix, const char *suffix)
241 {
242 struct column_data *c = rtbl_get_column_by_id (table, id);
243
244 if (c == NULL)
245 return -1;
246 if (c->prefix)
247 free (c->prefix);
248 if(prefix == NULL)
249 c->prefix = NULL;
250 else {
251 c->prefix = strdup (prefix);
252 if (c->prefix == NULL)
253 return ENOMEM;
254 }
255
256 if (c->suffix)
257 free (c->suffix);
258 if(suffix == NULL)
259 c->suffix = NULL;
260 else {
261 c->suffix = strdup (suffix);
262 if (c->suffix == NULL)
263 return ENOMEM;
264 }
265 return 0;
266 }
267
268
269 static const char *
get_column_prefix(rtbl_t table,struct column_data * c)270 get_column_prefix (rtbl_t table, struct column_data *c)
271 {
272 if (c == NULL)
273 return "";
274 if (c->prefix)
275 return c->prefix;
276 if (table->column_prefix)
277 return table->column_prefix;
278 return "";
279 }
280
281 static const char *
get_column_suffix(rtbl_t table,struct column_data * c)282 get_column_suffix (rtbl_t table, struct column_data *c)
283 {
284 if (c && c->suffix)
285 return c->suffix;
286 return "";
287 }
288
289 static int
add_column_entry(struct column_data * c,const char * data)290 add_column_entry (struct column_data *c, const char *data)
291 {
292 struct column_entry row, *tmp;
293
294 row.data = strdup (data);
295 if (row.data == NULL)
296 return ENOMEM;
297 tmp = realloc (c->rows, (c->num_rows + 1) * sizeof (*tmp));
298 if (tmp == NULL) {
299 free (row.data);
300 return ENOMEM;
301 }
302 c->rows = tmp;
303 c->rows[c->num_rows++] = row;
304 return 0;
305 }
306
307 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_add_column_entry_by_id(rtbl_t table,unsigned int id,const char * data)308 rtbl_add_column_entry_by_id (rtbl_t table, unsigned int id, const char *data)
309 {
310 struct column_data *c = rtbl_get_column_by_id (table, id);
311
312 if (c == NULL)
313 return -1;
314
315 return add_column_entry(c, data);
316 }
317
318 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_add_column_entryv_by_id(rtbl_t table,unsigned int id,const char * fmt,...)319 rtbl_add_column_entryv_by_id (rtbl_t table, unsigned int id,
320 const char *fmt, ...)
321 {
322 va_list ap;
323 char *str;
324 int ret;
325
326 va_start(ap, fmt);
327 ret = vasprintf(&str, fmt, ap);
328 va_end(ap);
329 if (ret == -1)
330 return -1;
331 ret = rtbl_add_column_entry_by_id(table, id, str);
332 free(str);
333 return ret;
334 }
335
336 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_add_column_entry(rtbl_t table,const char * column,const char * data)337 rtbl_add_column_entry (rtbl_t table, const char *column, const char *data)
338 {
339 struct column_data *c = rtbl_get_column (table, column);
340
341 if (c == NULL)
342 return -1;
343
344 return add_column_entry(c, data);
345 }
346
347 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_add_column_entryv(rtbl_t table,const char * column,const char * fmt,...)348 rtbl_add_column_entryv (rtbl_t table, const char *column, const char *fmt, ...)
349 {
350 va_list ap;
351 char *str;
352 int ret;
353
354 va_start(ap, fmt);
355 ret = vasprintf(&str, fmt, ap);
356 va_end(ap);
357 if (ret == -1)
358 return -1;
359 ret = rtbl_add_column_entry(table, column, str);
360 free(str);
361 return ret;
362 }
363
364
365 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_format(rtbl_t table,FILE * f)366 rtbl_format (rtbl_t table, FILE * f)
367 {
368 char *str = rtbl_format_str(table);
369 if (str == NULL)
370 return ENOMEM;
371 fprintf(f, "%s", str);
372 free(str);
373 return 0;
374 }
375
376 static char *
rtbl_format_pretty(rtbl_t table)377 rtbl_format_pretty(rtbl_t table)
378 {
379 struct rk_strpool *p = NULL;
380 size_t i, j;
381
382 for (i = 0; i < table->num_columns; i++)
383 column_compute_width (table, table->columns[i]);
384 if((table->flags & RTBL_HEADER_STYLE_NONE) == 0) {
385 for (i = 0; i < table->num_columns; i++) {
386 struct column_data *c = table->columns[i];
387
388 if(table->column_separator != NULL && i > 0)
389 p = rk_strpoolprintf(p, "%s", table->column_separator);
390 p = rk_strpoolprintf(p, "%s", get_column_prefix (table, c));
391 if (c == NULL) {
392 /* do nothing if no column */
393 } else if(i == table->num_columns - 1 && c->suffix == NULL)
394 /* last column, so no need to pad with spaces */
395 p = rk_strpoolprintf(p, "%-*s", 0, c->header);
396 else
397 p = rk_strpoolprintf(p, "%-*s", (int)c->width, c->header);
398 p = rk_strpoolprintf(p, "%s", get_column_suffix (table, c));
399 }
400 p = rk_strpoolprintf(p, "\n");
401 }
402
403 for (j = 0;; j++) {
404 int flag = 0;
405
406 /* are there any more rows left? */
407 for (i = 0; flag == 0 && i < table->num_columns; ++i) {
408 struct column_data *c = table->columns[i];
409
410 if (c->num_rows > j) {
411 ++flag;
412 break;
413 }
414 }
415 if (flag == 0)
416 break;
417
418 for (i = 0; i < table->num_columns; i++) {
419 int w;
420 struct column_data *c = table->columns[i];
421
422 if(table->column_separator != NULL && i > 0)
423 p = rk_strpoolprintf(p, "%s", table->column_separator);
424
425 w = c->width;
426
427 if ((c->flags & RTBL_ALIGN_RIGHT) == 0) {
428 if(i == table->num_columns - 1 && c->suffix == NULL)
429 /* last column, so no need to pad with spaces */
430 w = 0;
431 else
432 w = -w;
433 }
434 p = rk_strpoolprintf(p, "%s", get_column_prefix (table, c));
435 if (c->num_rows <= j)
436 p = rk_strpoolprintf(p, "%*s", w, "");
437 else
438 p = rk_strpoolprintf(p, "%*s", w, c->rows[j].data);
439 p = rk_strpoolprintf(p, "%s", get_column_suffix (table, c));
440 }
441 p = rk_strpoolprintf(p, "\n");
442 }
443
444 return rk_strpoolcollect(p);
445 }
446
447 static char *
rtbl_format_json(rtbl_t table)448 rtbl_format_json(rtbl_t table)
449 {
450 struct rk_strpool *p = NULL;
451 size_t i, j;
452 int comma;
453
454 p = rk_strpoolprintf(p, "[");
455 for (j = 0;; j++) {
456 int flag = 0;
457
458 /* are there any more rows left? */
459 for (i = 0; flag == 0 && i < table->num_columns; ++i) {
460 struct column_data *c = table->columns[i];
461
462 if (c->num_rows > j) {
463 ++flag;
464 break;
465 }
466 }
467 if (flag == 0)
468 break;
469
470 p = rk_strpoolprintf(p, "%s{", j > 0 ? "," : "");
471
472 comma = 0;
473 for (i = 0; i < table->num_columns; i++) {
474 struct column_data *c = table->columns[i];
475
476 if (c->num_rows > j) {
477 char *header = c->header;
478 while (isspace((int)header[0])) /* trim off prefixed whitespace */
479 header++;
480 p = rk_strpoolprintf(p, "%s\"%s\" : \"%s\"",
481 comma ? "," : "", header,
482 c->rows[j].data);
483 comma = 1;
484 }
485 }
486 p = rk_strpoolprintf(p, "}");
487 }
488 p = rk_strpoolprintf(p, "]");
489
490 return rk_strpoolcollect(p);
491 }
492
493 ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL
rtbl_format_str(rtbl_t table)494 rtbl_format_str (rtbl_t table)
495 {
496 if (table->flags & RTBL_JSON)
497 return rtbl_format_json(table);
498
499 return rtbl_format_pretty(table);
500 }
501
502 #ifdef TEST
503 int
main(int argc,char ** argv)504 main (int argc, char **argv)
505 {
506 rtbl_t table;
507
508 table = rtbl_create ();
509 rtbl_add_column_by_id (table, 0, "Issued", 0);
510 rtbl_add_column_by_id (table, 1, "Expires", 0);
511 rtbl_add_column_by_id (table, 2, "Foo", RTBL_ALIGN_RIGHT);
512 rtbl_add_column_by_id (table, 3, "Principal", 0);
513
514 rtbl_add_column_entry_by_id (table, 0, "Jul 7 21:19:29");
515 rtbl_add_column_entry_by_id (table, 1, "Jul 8 07:19:29");
516 rtbl_add_column_entry_by_id (table, 2, "73");
517 rtbl_add_column_entry_by_id (table, 2, "0");
518 rtbl_add_column_entry_by_id (table, 2, "-2000");
519 rtbl_add_column_entry_by_id (table, 3, "krbtgt/NADA.KTH.SE@NADA.KTH.SE");
520
521 rtbl_add_column_entry_by_id (table, 0, "Jul 7 21:19:29");
522 rtbl_add_column_entry_by_id (table, 1, "Jul 8 07:19:29");
523 rtbl_add_column_entry_by_id (table, 3, "afs/pdc.kth.se@NADA.KTH.SE");
524
525 rtbl_add_column_entry_by_id (table, 0, "Jul 7 21:19:29");
526 rtbl_add_column_entry_by_id (table, 1, "Jul 8 07:19:29");
527 rtbl_add_column_entry_by_id (table, 3, "afs@NADA.KTH.SE");
528
529 rtbl_set_separator (table, " ");
530
531 rtbl_format (table, stdout);
532
533 rtbl_destroy (table);
534
535 printf("\n");
536
537 table = rtbl_create ();
538 rtbl_add_column_by_id (table, 0, "Column A", 0);
539 rtbl_set_column_affix_by_id (table, 0, "<", ">");
540 rtbl_add_column_by_id (table, 1, "Column B", 0);
541 rtbl_set_column_affix_by_id (table, 1, "[", "]");
542 rtbl_add_column_by_id (table, 2, "Column C", 0);
543 rtbl_set_column_affix_by_id (table, 2, "(", ")");
544
545 rtbl_add_column_entry_by_id (table, 0, "1");
546 rtbl_new_row(table);
547 rtbl_add_column_entry_by_id (table, 1, "2");
548 rtbl_new_row(table);
549 rtbl_add_column_entry_by_id (table, 2, "3");
550 rtbl_new_row(table);
551
552 rtbl_set_separator (table, " ");
553 rtbl_format (table, stdout);
554
555 rtbl_destroy (table);
556
557 return 0;
558 }
559
560 #endif
561