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