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 Högskolan 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@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