rtbl.c revision 178826
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#ifdef HAVE_CONFIG_H
35#include <config.h>
36RCSID ("$Id: rtbl.c 17758 2006-06-30 13:41:40Z lha $");
37#endif
38#include "roken.h"
39#include "rtbl.h"
40
41struct column_entry {
42    char *data;
43};
44
45struct column_data {
46    char *header;
47    char *prefix;
48    int width;
49    unsigned flags;
50    size_t num_rows;
51    struct column_entry *rows;
52    unsigned int column_id;
53    char *suffix;
54};
55
56struct rtbl_data {
57    char *column_prefix;
58    size_t num_columns;
59    struct column_data **columns;
60    unsigned int flags;
61    char *column_separator;
62};
63
64rtbl_t ROKEN_LIB_FUNCTION
65rtbl_create (void)
66{
67    return calloc (1, sizeof (struct rtbl_data));
68}
69
70void ROKEN_LIB_FUNCTION
71rtbl_set_flags (rtbl_t table, unsigned int flags)
72{
73    table->flags = flags;
74}
75
76unsigned int ROKEN_LIB_FUNCTION
77rtbl_get_flags (rtbl_t table)
78{
79    return table->flags;
80}
81
82static struct column_data *
83rtbl_get_column_by_id (rtbl_t table, unsigned int id)
84{
85    int i;
86    for(i = 0; i < table->num_columns; i++)
87	if(table->columns[i]->column_id == id)
88	    return table->columns[i];
89    return NULL;
90}
91
92static struct column_data *
93rtbl_get_column (rtbl_t table, const char *column)
94{
95    int i;
96    for(i = 0; i < table->num_columns; i++)
97	if(strcmp(table->columns[i]->header, column) == 0)
98	    return table->columns[i];
99    return NULL;
100}
101
102void ROKEN_LIB_FUNCTION
103rtbl_destroy (rtbl_t table)
104{
105    int i, j;
106
107    for (i = 0; i < table->num_columns; i++) {
108	struct column_data *c = table->columns[i];
109
110	for (j = 0; j < c->num_rows; j++)
111	    free (c->rows[j].data);
112	free (c->rows);
113	free (c->header);
114	free (c->prefix);
115	free (c->suffix);
116	free (c);
117    }
118    free (table->column_prefix);
119    free (table->column_separator);
120    free (table->columns);
121    free (table);
122}
123
124int ROKEN_LIB_FUNCTION
125rtbl_add_column_by_id (rtbl_t table, unsigned int id,
126		       const char *header, unsigned int flags)
127{
128    struct column_data *col, **tmp;
129
130    tmp = realloc (table->columns, (table->num_columns + 1) * sizeof (*tmp));
131    if (tmp == NULL)
132	return ENOMEM;
133    table->columns = tmp;
134    col = malloc (sizeof (*col));
135    if (col == NULL)
136	return ENOMEM;
137    col->header = strdup (header);
138    if (col->header == NULL) {
139	free (col);
140	return ENOMEM;
141    }
142    col->prefix = NULL;
143    col->width = 0;
144    col->flags = flags;
145    col->num_rows = 0;
146    col->rows = NULL;
147    col->column_id = id;
148    col->suffix = NULL;
149    table->columns[table->num_columns++] = col;
150    return 0;
151}
152
153int ROKEN_LIB_FUNCTION
154rtbl_add_column (rtbl_t table, const char *header, unsigned int flags)
155{
156    return rtbl_add_column_by_id(table, 0, header, flags);
157}
158
159int ROKEN_LIB_FUNCTION
160rtbl_new_row(rtbl_t table)
161{
162    size_t max_rows = 0;
163    size_t c;
164    for (c = 0; c < table->num_columns; c++)
165	if(table->columns[c]->num_rows > max_rows)
166	    max_rows = table->columns[c]->num_rows;
167    for (c = 0; c < table->num_columns; c++) {
168	struct column_entry *tmp;
169
170	if(table->columns[c]->num_rows == max_rows)
171	    continue;
172	tmp = realloc(table->columns[c]->rows,
173		      max_rows * sizeof(table->columns[c]->rows));
174	if(tmp == NULL)
175	    return ENOMEM;
176	table->columns[c]->rows = tmp;
177	while(table->columns[c]->num_rows < max_rows) {
178	    if((tmp[table->columns[c]->num_rows++].data = strdup("")) == NULL)
179		return ENOMEM;
180	}
181    }
182    return 0;
183}
184
185static void
186column_compute_width (rtbl_t table, struct column_data *column)
187{
188    int i;
189
190    if(table->flags & RTBL_HEADER_STYLE_NONE)
191	column->width = 0;
192    else
193	column->width = strlen (column->header);
194    for (i = 0; i < column->num_rows; i++)
195	column->width = max (column->width, strlen (column->rows[i].data));
196}
197
198/* DEPRECATED */
199int ROKEN_LIB_FUNCTION
200rtbl_set_prefix (rtbl_t table, const char *prefix)
201{
202    if (table->column_prefix)
203	free (table->column_prefix);
204    table->column_prefix = strdup (prefix);
205    if (table->column_prefix == NULL)
206	return ENOMEM;
207    return 0;
208}
209
210int ROKEN_LIB_FUNCTION
211rtbl_set_separator (rtbl_t table, const char *separator)
212{
213    if (table->column_separator)
214	free (table->column_separator);
215    table->column_separator = strdup (separator);
216    if (table->column_separator == NULL)
217	return ENOMEM;
218    return 0;
219}
220
221int ROKEN_LIB_FUNCTION
222rtbl_set_column_prefix (rtbl_t table, const char *column,
223			const char *prefix)
224{
225    struct column_data *c = rtbl_get_column (table, column);
226
227    if (c == NULL)
228	return -1;
229    if (c->prefix)
230	free (c->prefix);
231    c->prefix = strdup (prefix);
232    if (c->prefix == NULL)
233	return ENOMEM;
234    return 0;
235}
236
237int ROKEN_LIB_FUNCTION
238rtbl_set_column_affix_by_id(rtbl_t table, unsigned int id,
239			    const char *prefix, const char *suffix)
240{
241    struct column_data *c = rtbl_get_column_by_id (table, id);
242
243    if (c == NULL)
244	return -1;
245    if (c->prefix)
246	free (c->prefix);
247    if(prefix == NULL)
248	c->prefix = NULL;
249    else {
250	c->prefix = strdup (prefix);
251	if (c->prefix == NULL)
252	    return ENOMEM;
253    }
254
255    if (c->suffix)
256	free (c->suffix);
257    if(suffix == NULL)
258	c->suffix = NULL;
259    else {
260	c->suffix = strdup (suffix);
261	if (c->suffix == NULL)
262	    return ENOMEM;
263    }
264    return 0;
265}
266
267
268static const char *
269get_column_prefix (rtbl_t table, struct column_data *c)
270{
271    if (c == NULL)
272	return "";
273    if (c->prefix)
274	return c->prefix;
275    if (table->column_prefix)
276	return table->column_prefix;
277    return "";
278}
279
280static const char *
281get_column_suffix (rtbl_t table, struct column_data *c)
282{
283    if (c && c->suffix)
284	return c->suffix;
285    return "";
286}
287
288static int
289add_column_entry (struct column_data *c, const char *data)
290{
291    struct column_entry row, *tmp;
292
293    row.data = strdup (data);
294    if (row.data == NULL)
295	return ENOMEM;
296    tmp = realloc (c->rows, (c->num_rows + 1) * sizeof (*tmp));
297    if (tmp == NULL) {
298	free (row.data);
299	return ENOMEM;
300    }
301    c->rows = tmp;
302    c->rows[c->num_rows++] = row;
303    return 0;
304}
305
306int ROKEN_LIB_FUNCTION
307rtbl_add_column_entry_by_id (rtbl_t table, unsigned int id, const char *data)
308{
309    struct column_data *c = rtbl_get_column_by_id (table, id);
310
311    if (c == NULL)
312	return -1;
313
314    return add_column_entry(c, data);
315}
316
317int ROKEN_LIB_FUNCTION
318rtbl_add_column_entryv_by_id (rtbl_t table, unsigned int id,
319			      const char *fmt, ...)
320{
321    va_list ap;
322    char *str;
323    int ret;
324
325    va_start(ap, fmt);
326    ret = vasprintf(&str, fmt, ap);
327    va_end(ap);
328    if (ret == -1)
329	return -1;
330    ret = rtbl_add_column_entry_by_id(table, id, str);
331    free(str);
332    return ret;
333}
334
335int ROKEN_LIB_FUNCTION
336rtbl_add_column_entry (rtbl_t table, const char *column, const char *data)
337{
338    struct column_data *c = rtbl_get_column (table, column);
339
340    if (c == NULL)
341	return -1;
342
343    return add_column_entry(c, data);
344}
345
346int ROKEN_LIB_FUNCTION
347rtbl_add_column_entryv (rtbl_t table, const char *column, const char *fmt, ...)
348{
349    va_list ap;
350    char *str;
351    int ret;
352
353    va_start(ap, fmt);
354    ret = vasprintf(&str, fmt, ap);
355    va_end(ap);
356    if (ret == -1)
357	return -1;
358    ret = rtbl_add_column_entry(table, column, str);
359    free(str);
360    return ret;
361}
362
363
364int ROKEN_LIB_FUNCTION
365rtbl_format (rtbl_t table, FILE * f)
366{
367    int i, j;
368
369    for (i = 0; i < table->num_columns; i++)
370	column_compute_width (table, table->columns[i]);
371    if((table->flags & RTBL_HEADER_STYLE_NONE) == 0) {
372	for (i = 0; i < table->num_columns; i++) {
373	    struct column_data *c = table->columns[i];
374
375	    if(table->column_separator != NULL && i > 0)
376		fprintf (f, "%s", table->column_separator);
377	    fprintf (f, "%s", get_column_prefix (table, c));
378	    if(i == table->num_columns - 1 && c->suffix == NULL)
379		/* last column, so no need to pad with spaces */
380		fprintf (f, "%-*s", 0, c->header);
381	    else
382		fprintf (f, "%-*s", (int)c->width, c->header);
383	    fprintf (f, "%s", get_column_suffix (table, c));
384	}
385	fprintf (f, "\n");
386    }
387
388    for (j = 0;; j++) {
389	int flag = 0;
390
391	/* are there any more rows left? */
392	for (i = 0; flag == 0 && i < table->num_columns; ++i) {
393	    struct column_data *c = table->columns[i];
394
395	    if (c->num_rows > j) {
396		++flag;
397		break;
398	    }
399	}
400	if (flag == 0)
401	    break;
402
403	for (i = 0; i < table->num_columns; i++) {
404	    int w;
405	    struct column_data *c = table->columns[i];
406
407	    if(table->column_separator != NULL && i > 0)
408		fprintf (f, "%s", table->column_separator);
409
410	    w = c->width;
411
412	    if ((c->flags & RTBL_ALIGN_RIGHT) == 0) {
413		if(i == table->num_columns - 1 && c->suffix == NULL)
414		    /* last column, so no need to pad with spaces */
415		    w = 0;
416		else
417		    w = -w;
418	    }
419	    fprintf (f, "%s", get_column_prefix (table, c));
420	    if (c->num_rows <= j)
421		fprintf (f, "%*s", w, "");
422	    else
423		fprintf (f, "%*s", w, c->rows[j].data);
424	    fprintf (f, "%s", get_column_suffix (table, c));
425	}
426	fprintf (f, "\n");
427    }
428    return 0;
429}
430
431#ifdef TEST
432int
433main (int argc, char **argv)
434{
435    rtbl_t table;
436
437    table = rtbl_create ();
438    rtbl_add_column_by_id (table, 0, "Issued", 0);
439    rtbl_add_column_by_id (table, 1, "Expires", 0);
440    rtbl_add_column_by_id (table, 2, "Foo", RTBL_ALIGN_RIGHT);
441    rtbl_add_column_by_id (table, 3, "Principal", 0);
442
443    rtbl_add_column_entry_by_id (table, 0, "Jul  7 21:19:29");
444    rtbl_add_column_entry_by_id (table, 1, "Jul  8 07:19:29");
445    rtbl_add_column_entry_by_id (table, 2, "73");
446    rtbl_add_column_entry_by_id (table, 2, "0");
447    rtbl_add_column_entry_by_id (table, 2, "-2000");
448    rtbl_add_column_entry_by_id (table, 3, "krbtgt/NADA.KTH.SE@NADA.KTH.SE");
449
450    rtbl_add_column_entry_by_id (table, 0, "Jul  7 21:19:29");
451    rtbl_add_column_entry_by_id (table, 1, "Jul  8 07:19:29");
452    rtbl_add_column_entry_by_id (table, 3, "afs/pdc.kth.se@NADA.KTH.SE");
453
454    rtbl_add_column_entry_by_id (table, 0, "Jul  7 21:19:29");
455    rtbl_add_column_entry_by_id (table, 1, "Jul  8 07:19:29");
456    rtbl_add_column_entry_by_id (table, 3, "afs@NADA.KTH.SE");
457
458    rtbl_set_separator (table, "  ");
459
460    rtbl_format (table, stdout);
461
462    rtbl_destroy (table);
463
464    printf("\n");
465
466    table = rtbl_create ();
467    rtbl_add_column_by_id (table, 0, "Column A", 0);
468    rtbl_set_column_affix_by_id (table, 0, "<", ">");
469    rtbl_add_column_by_id (table, 1, "Column B", 0);
470    rtbl_set_column_affix_by_id (table, 1, "[", "]");
471    rtbl_add_column_by_id (table, 2, "Column C", 0);
472    rtbl_set_column_affix_by_id (table, 2, "(", ")");
473
474    rtbl_add_column_entry_by_id (table, 0, "1");
475    rtbl_new_row(table);
476    rtbl_add_column_entry_by_id (table, 1, "2");
477    rtbl_new_row(table);
478    rtbl_add_column_entry_by_id (table, 2, "3");
479    rtbl_new_row(table);
480
481    rtbl_set_separator (table, "  ");
482    rtbl_format (table, stdout);
483
484    rtbl_destroy (table);
485
486    return 0;
487}
488
489#endif
490