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