Home | History | Annotate | Line # | Download | only in roken
      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 Hgskolan
      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
     66 rtbl_create (void)
     67 {
     68     return calloc (1, sizeof (struct rtbl_data));
     69 }
     70 
     71 ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
     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
     78 rtbl_get_flags (rtbl_t table)
     79 {
     80     return table->flags;
     81 }
     82 
     83 static struct column_data *
     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 *
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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 *
    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 *
    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
    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
    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
    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
    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
    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
    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 *
    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 *
    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
    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
    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 (at) 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