rtbl.c revision 302408
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
39struct column_entry {
40    char *data;
41};
42
43struct 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
54struct 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
62ROKEN_LIB_FUNCTION rtbl_t ROKEN_LIB_CALL
63rtbl_create (void)
64{
65    return calloc (1, sizeof (struct rtbl_data));
66}
67
68ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
69rtbl_set_flags (rtbl_t table, unsigned int flags)
70{
71    table->flags = flags;
72}
73
74ROKEN_LIB_FUNCTION unsigned int ROKEN_LIB_CALL
75rtbl_get_flags (rtbl_t table)
76{
77    return table->flags;
78}
79
80static struct column_data *
81rtbl_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
90static struct column_data *
91rtbl_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
100ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
101rtbl_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
122ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
123rtbl_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
151ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
152rtbl_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
157ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
158rtbl_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
183static void
184column_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 */
197ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
198rtbl_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
208ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
209rtbl_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
219ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
220rtbl_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
235ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
236rtbl_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
266static const char *
267get_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
278static const char *
279get_column_suffix (rtbl_t table, struct column_data *c)
280{
281    if (c && c->suffix)
282	return c->suffix;
283    return "";
284}
285
286static int
287add_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
304ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
305rtbl_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
315ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
316rtbl_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
333ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
334rtbl_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
344ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
345rtbl_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
362ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
363rtbl_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
430int
431main (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