rtbl.c revision 233294
167468Snon/*
279697Snon * Copyright (c) 2000, 2002, 2004 Kungliga Tekniska H��gskolan
367468Snon * (Royal Institute of Technology, Stockholm, Sweden).
467468Snon * All rights reserved.
5139749Simp *
667468Snon * Redistribution and use in source and binary forms, with or without
767468Snon * modification, are permitted provided that the following conditions
867468Snon * are met:
967468Snon *
1067468Snon * 1. Redistributions of source code must retain the above copyright
1167468Snon *    notice, this list of conditions and the following disclaimer.
1267468Snon *
1367468Snon * 2. Redistributions in binary form must reproduce the above copyright
1467468Snon *    notice, this list of conditions and the following disclaimer in the
1567468Snon *    documentation and/or other materials provided with the distribution.
1667468Snon *
1767468Snon * 3. Neither the name of the Institute nor the names of its contributors
1867468Snon *    may be used to endorse or promote products derived from this software
1967468Snon *    without specific prior written permission.
2067468Snon *
2167468Snon * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2267468Snon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2367468Snon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2467468Snon * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2567468Snon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2667468Snon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2767468Snon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2867468Snon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2967468Snon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3067468Snon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3167468Snon * SUCH DAMAGE.
3267468Snon */
3367468Snon
3467468Snon#include <config.h>
3567468Snon
3667468Snon#include "roken.h"
3767468Snon#include "rtbl.h"
3867468Snon
3967468Snonstruct column_entry {
4067468Snon    char *data;
4167468Snon};
4267468Snon
4367468Snonstruct column_data {
4467468Snon    char *header;
4567468Snon    char *prefix;
4667468Snon    int width;
4767468Snon    unsigned flags;
4867468Snon    size_t num_rows;
4967468Snon    struct column_entry *rows;
5067468Snon    unsigned int column_id;
5167468Snon    char *suffix;
5267468Snon};
5367468Snon
5467468Snonstruct rtbl_data {
5567468Snon    char *column_prefix;
5667468Snon    size_t num_columns;
5767468Snon    struct column_data **columns;
5867468Snon    unsigned int flags;
5967468Snon    char *column_separator;
6067468Snon};
6167468Snon
6267468SnonROKEN_LIB_FUNCTION rtbl_t ROKEN_LIB_CALL
6367468Snonrtbl_create (void)
6467468Snon{
6567468Snon    return calloc (1, sizeof (struct rtbl_data));
6667468Snon}
6767468Snon
6867468SnonROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
6967468Snonrtbl_set_flags (rtbl_t table, unsigned int flags)
7067468Snon{
7167468Snon    table->flags = flags;
7267468Snon}
7367468Snon
7467468SnonROKEN_LIB_FUNCTION unsigned int ROKEN_LIB_CALL
7567468Snonrtbl_get_flags (rtbl_t table)
7667468Snon{
7767468Snon    return table->flags;
7867468Snon}
7967468Snon
8067468Snonstatic struct column_data *
8167468Snonrtbl_get_column_by_id (rtbl_t table, unsigned int id)
8267468Snon{
8367468Snon    size_t i;
8467468Snon    for(i = 0; i < table->num_columns; i++)
8567468Snon	if(table->columns[i]->column_id == id)
8667468Snon	    return table->columns[i];
8767468Snon    return NULL;
8867468Snon}
8967468Snon
9067468Snonstatic struct column_data *
9167468Snonrtbl_get_column (rtbl_t table, const char *column)
9267468Snon{
9367468Snon    size_t i;
9467468Snon    for(i = 0; i < table->num_columns; i++)
9579697Snon	if(strcmp(table->columns[i]->header, column) == 0)
9667468Snon	    return table->columns[i];
9767468Snon    return NULL;
9867468Snon}
9967468Snon
10067468SnonROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
10167468Snonrtbl_destroy (rtbl_t table)
10267468Snon{
10367468Snon    size_t i, j;
10467468Snon
10567468Snon    for (i = 0; i < table->num_columns; i++) {
10667468Snon	struct column_data *c = table->columns[i];
10767468Snon
10867468Snon	for (j = 0; j < c->num_rows; j++)
10967468Snon	    free (c->rows[j].data);
11067468Snon	free (c->rows);
11167468Snon	free (c->header);
11267468Snon	free (c->prefix);
11367468Snon	free (c->suffix);
11467468Snon	free (c);
11567468Snon    }
11667468Snon    free (table->column_prefix);
11767468Snon    free (table->column_separator);
11867468Snon    free (table->columns);
11967468Snon    free (table);
12067468Snon}
12167468Snon
12267468SnonROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
12367468Snonrtbl_add_column_by_id (rtbl_t table, unsigned int id,
12467468Snon		       const char *header, unsigned int flags)
12567468Snon{
12667468Snon    struct column_data *col, **tmp;
12767468Snon
12867468Snon    tmp = realloc (table->columns, (table->num_columns + 1) * sizeof (*tmp));
12979697Snon    if (tmp == NULL)
13079697Snon	return ENOMEM;
13179697Snon    table->columns = tmp;
13267468Snon    col = malloc (sizeof (*col));
13367468Snon    if (col == NULL)
13467468Snon	return ENOMEM;
13567468Snon    col->header = strdup (header);
13667468Snon    if (col->header == NULL) {
13767468Snon	free (col);
13867468Snon	return ENOMEM;
13967468Snon    }
14067468Snon    col->prefix = NULL;
14167468Snon    col->width = 0;
14267468Snon    col->flags = flags;
14367468Snon    col->num_rows = 0;
14467468Snon    col->rows = NULL;
14567468Snon    col->column_id = id;
14679697Snon    col->suffix = NULL;
14779697Snon    table->columns[table->num_columns++] = col;
14879697Snon    return 0;
14967468Snon}
15067468Snon
15167468SnonROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
15267468Snonrtbl_add_column (rtbl_t table, const char *header, unsigned int flags)
15367468Snon{
15467468Snon    return rtbl_add_column_by_id(table, 0, header, flags);
15567468Snon}
15667468Snon
15767468SnonROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
15867468Snonrtbl_new_row(rtbl_t table)
15967468Snon{
16067468Snon    size_t max_rows = 0;
16167468Snon    size_t c;
16267468Snon    for (c = 0; c < table->num_columns; c++)
16367468Snon	if(table->columns[c]->num_rows > max_rows)
16467468Snon	    max_rows = table->columns[c]->num_rows;
16567468Snon    for (c = 0; c < table->num_columns; c++) {
16667468Snon	struct column_entry *tmp;
16767468Snon
16867468Snon	if(table->columns[c]->num_rows == max_rows)
16967468Snon	    continue;
17067468Snon	tmp = realloc(table->columns[c]->rows,
17167468Snon		      max_rows * sizeof(table->columns[c]->rows));
17267468Snon	if(tmp == NULL)
17367468Snon	    return ENOMEM;
17467468Snon	table->columns[c]->rows = tmp;
17567468Snon	while(table->columns[c]->num_rows < max_rows) {
17667468Snon	    if((tmp[table->columns[c]->num_rows++].data = strdup("")) == NULL)
17767468Snon		return ENOMEM;
17867468Snon	}
17967468Snon    }
18067468Snon    return 0;
18167468Snon}
18267468Snon
18367468Snonstatic void
18467468Snoncolumn_compute_width (rtbl_t table, struct column_data *column)
18567468Snon{
18667468Snon    size_t i;
18767468Snon
18867468Snon    if(table->flags & RTBL_HEADER_STYLE_NONE)
18967468Snon	column->width = 0;
19067468Snon    else
19167468Snon	column->width = strlen (column->header);
19267468Snon    for (i = 0; i < column->num_rows; i++)
19367468Snon	column->width = max (column->width, (int) strlen (column->rows[i].data));
19467468Snon}
19579697Snon
19679697Snon/* DEPRECATED */
19779697SnonROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
19879697Snonrtbl_set_prefix (rtbl_t table, const char *prefix)
19979697Snon{
20079697Snon    if (table->column_prefix)
20167468Snon	free (table->column_prefix);
20267468Snon    table->column_prefix = strdup (prefix);
20367468Snon    if (table->column_prefix == NULL)
20467468Snon	return ENOMEM;
20567468Snon    return 0;
20667468Snon}
20767468Snon
20867468SnonROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
20967468Snonrtbl_set_separator (rtbl_t table, const char *separator)
21079697Snon{
21179697Snon    if (table->column_separator)
21279697Snon	free (table->column_separator);
21379697Snon    table->column_separator = strdup (separator);
21479697Snon    if (table->column_separator == NULL)
21579697Snon	return ENOMEM;
21679697Snon    return 0;
21767468Snon}
21867468Snon
21967468SnonROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
22067468Snonrtbl_set_column_prefix (rtbl_t table, const char *column,
22167468Snon			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