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> 36 RCSID ("$Id: rtbl.c 17758 2006-06-30 13:41:40Z lha $"); 37 #endif 38 #include "roken.h" 39 #include "rtbl.h" 40 41 struct column_entry { 42 char *data; 43 }; 44 45 struct 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 56 struct 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 64 rtbl_t ROKEN_LIB_FUNCTION 65 rtbl_create (void) 66 { 67 return calloc (1, sizeof (struct rtbl_data)); 68 } 69 70 void ROKEN_LIB_FUNCTION 71 rtbl_set_flags (rtbl_t table, unsigned int flags) 72 { 73 table->flags = flags; 74 } 75 76 unsigned int ROKEN_LIB_FUNCTION 77 rtbl_get_flags (rtbl_t table) 78 { 79 return table->flags; 80 } 81 82 static struct column_data * 83 rtbl_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 92 static struct column_data * 93 rtbl_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 102 void ROKEN_LIB_FUNCTION 103 rtbl_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 124 int ROKEN_LIB_FUNCTION 125 rtbl_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 153 int ROKEN_LIB_FUNCTION 154 rtbl_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 159 int ROKEN_LIB_FUNCTION 160 rtbl_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 185 static void 186 column_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 */ 199 int ROKEN_LIB_FUNCTION 200 rtbl_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 210 int ROKEN_LIB_FUNCTION 211 rtbl_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 221 int ROKEN_LIB_FUNCTION 222 rtbl_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 237 int ROKEN_LIB_FUNCTION 238 rtbl_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 268 static const char * 269 get_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 280 static const char * 281 get_column_suffix (rtbl_t table, struct column_data *c) 282 { 283 if (c && c->suffix) 284 return c->suffix; 285 return ""; 286 } 287 288 static int 289 add_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 306 int ROKEN_LIB_FUNCTION 307 rtbl_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 317 int ROKEN_LIB_FUNCTION 318 rtbl_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 335 int ROKEN_LIB_FUNCTION 336 rtbl_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 346 int ROKEN_LIB_FUNCTION 347 rtbl_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 364 int ROKEN_LIB_FUNCTION 365 rtbl_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 432 int 433 main (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