xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/roken/rtbl.c (revision d3273b5b76f5afaafe308cead5511dbb8df8c5e9)
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