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 "rtbl.h"
38 :
39 : struct column_entry {
40 : char *data;
41 : };
42 :
43 : struct column_data {
44 : char *header;
45 : char *prefix;
46 : int width;
47 : unsigned flags;
48 : size_t num_rows;
49 : struct column_entry *rows;
50 : unsigned int column_id;
51 : char *suffix;
52 : };
53 :
54 : struct rtbl_data {
55 : char *column_prefix;
56 : size_t num_columns;
57 : struct column_data **columns;
58 : unsigned int flags;
59 : char *column_separator;
60 : };
61 :
62 : ROKEN_LIB_FUNCTION rtbl_t ROKEN_LIB_CALL
63 : rtbl_create (void)
64 0 : {
65 0 : return calloc (1, sizeof (struct rtbl_data));
66 : }
67 :
68 : ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
69 : rtbl_set_flags (rtbl_t table, unsigned int flags)
70 0 : {
71 0 : table->flags = flags;
72 0 : }
73 :
74 : ROKEN_LIB_FUNCTION unsigned int ROKEN_LIB_CALL
75 : rtbl_get_flags (rtbl_t table)
76 0 : {
77 0 : return table->flags;
78 : }
79 :
80 : static struct column_data *
81 : rtbl_get_column_by_id (rtbl_t table, unsigned int id)
82 0 : {
83 : size_t i;
84 0 : for(i = 0; i < table->num_columns; i++)
85 0 : if(table->columns[i]->column_id == id)
86 0 : return table->columns[i];
87 0 : return NULL;
88 : }
89 :
90 : static struct column_data *
91 : rtbl_get_column (rtbl_t table, const char *column)
92 0 : {
93 : size_t i;
94 0 : for(i = 0; i < table->num_columns; i++)
95 0 : if(strcmp(table->columns[i]->header, column) == 0)
96 0 : return table->columns[i];
97 0 : return NULL;
98 : }
99 :
100 : ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
101 : rtbl_destroy (rtbl_t table)
102 0 : {
103 : size_t i, j;
104 :
105 0 : for (i = 0; i < table->num_columns; i++) {
106 0 : struct column_data *c = table->columns[i];
107 :
108 0 : for (j = 0; j < c->num_rows; j++)
109 0 : free (c->rows[j].data);
110 0 : free (c->rows);
111 0 : free (c->header);
112 0 : free (c->prefix);
113 0 : free (c->suffix);
114 0 : free (c);
115 : }
116 0 : free (table->column_prefix);
117 0 : free (table->column_separator);
118 0 : free (table->columns);
119 0 : free (table);
120 0 : }
121 :
122 : ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
123 : rtbl_add_column_by_id (rtbl_t table, unsigned int id,
124 : const char *header, unsigned int flags)
125 0 : {
126 : struct column_data *col, **tmp;
127 :
128 0 : tmp = realloc (table->columns, (table->num_columns + 1) * sizeof (*tmp));
129 0 : if (tmp == NULL)
130 0 : return ENOMEM;
131 0 : table->columns = tmp;
132 0 : col = malloc (sizeof (*col));
133 0 : if (col == NULL)
134 0 : return ENOMEM;
135 0 : col->header = strdup (header);
136 0 : if (col->header == NULL) {
137 0 : free (col);
138 0 : return ENOMEM;
139 : }
140 0 : col->prefix = NULL;
141 0 : col->width = 0;
142 0 : col->flags = flags;
143 0 : col->num_rows = 0;
144 0 : col->rows = NULL;
145 0 : col->column_id = id;
146 0 : col->suffix = NULL;
147 0 : table->columns[table->num_columns++] = col;
148 0 : return 0;
149 : }
150 :
151 : ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
152 : rtbl_add_column (rtbl_t table, const char *header, unsigned int flags)
153 0 : {
154 0 : return rtbl_add_column_by_id(table, 0, header, flags);
155 : }
156 :
157 : ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
158 : rtbl_new_row(rtbl_t table)
159 0 : {
160 0 : size_t max_rows = 0;
161 : size_t c;
162 0 : for (c = 0; c < table->num_columns; c++)
163 0 : if(table->columns[c]->num_rows > max_rows)
164 0 : max_rows = table->columns[c]->num_rows;
165 0 : for (c = 0; c < table->num_columns; c++) {
166 : struct column_entry *tmp;
167 :
168 0 : if(table->columns[c]->num_rows == max_rows)
169 0 : continue;
170 0 : tmp = realloc(table->columns[c]->rows,
171 : max_rows * sizeof(table->columns[c]->rows));
172 0 : if(tmp == NULL)
173 0 : return ENOMEM;
174 0 : table->columns[c]->rows = tmp;
175 0 : while(table->columns[c]->num_rows < max_rows) {
176 0 : if((tmp[table->columns[c]->num_rows++].data = strdup("")) == NULL)
177 0 : return ENOMEM;
178 : }
179 : }
180 0 : return 0;
181 : }
182 :
183 : static void
184 : column_compute_width (rtbl_t table, struct column_data *column)
185 0 : {
186 : size_t i;
187 :
188 0 : if(table->flags & RTBL_HEADER_STYLE_NONE)
189 0 : column->width = 0;
190 : else
191 0 : column->width = strlen (column->header);
192 0 : for (i = 0; i < column->num_rows; i++)
193 0 : column->width = max (column->width, (int) strlen (column->rows[i].data));
194 0 : }
195 :
196 : /* DEPRECATED */
197 : ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
198 : rtbl_set_prefix (rtbl_t table, const char *prefix)
199 0 : {
200 0 : if (table->column_prefix)
201 0 : free (table->column_prefix);
202 0 : table->column_prefix = strdup (prefix);
203 0 : if (table->column_prefix == NULL)
204 0 : return ENOMEM;
205 0 : return 0;
206 : }
207 :
208 : ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
209 : rtbl_set_separator (rtbl_t table, const char *separator)
210 0 : {
211 0 : if (table->column_separator)
212 0 : free (table->column_separator);
213 0 : table->column_separator = strdup (separator);
214 0 : if (table->column_separator == NULL)
215 0 : return ENOMEM;
216 0 : return 0;
217 : }
218 :
219 : ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
220 : rtbl_set_column_prefix (rtbl_t table, const char *column,
221 : const char *prefix)
222 0 : {
223 0 : struct column_data *c = rtbl_get_column (table, column);
224 :
225 0 : if (c == NULL)
226 0 : return -1;
227 0 : if (c->prefix)
228 0 : free (c->prefix);
229 0 : c->prefix = strdup (prefix);
230 0 : if (c->prefix == NULL)
231 0 : return ENOMEM;
232 0 : return 0;
233 : }
234 :
235 : ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
236 : rtbl_set_column_affix_by_id(rtbl_t table, unsigned int id,
237 : const char *prefix, const char *suffix)
238 0 : {
239 0 : struct column_data *c = rtbl_get_column_by_id (table, id);
240 :
241 0 : if (c == NULL)
242 0 : return -1;
243 0 : if (c->prefix)
244 0 : free (c->prefix);
245 0 : if(prefix == NULL)
246 0 : c->prefix = NULL;
247 : else {
248 0 : c->prefix = strdup (prefix);
249 0 : if (c->prefix == NULL)
250 0 : return ENOMEM;
251 : }
252 :
253 0 : if (c->suffix)
254 0 : free (c->suffix);
255 0 : if(suffix == NULL)
256 0 : c->suffix = NULL;
257 : else {
258 0 : c->suffix = strdup (suffix);
259 0 : if (c->suffix == NULL)
260 0 : return ENOMEM;
261 : }
262 0 : return 0;
263 : }
264 :
265 :
266 : static const char *
267 : get_column_prefix (rtbl_t table, struct column_data *c)
268 0 : {
269 0 : if (c == NULL)
270 0 : return "";
271 0 : if (c->prefix)
272 0 : return c->prefix;
273 0 : if (table->column_prefix)
274 0 : return table->column_prefix;
275 0 : return "";
276 : }
277 :
278 : static const char *
279 : get_column_suffix (rtbl_t table, struct column_data *c)
280 0 : {
281 0 : if (c && c->suffix)
282 0 : return c->suffix;
283 0 : return "";
284 : }
285 :
286 : static int
287 : add_column_entry (struct column_data *c, const char *data)
288 0 : {
289 : struct column_entry row, *tmp;
290 :
291 0 : row.data = strdup (data);
292 0 : if (row.data == NULL)
293 0 : return ENOMEM;
294 0 : tmp = realloc (c->rows, (c->num_rows + 1) * sizeof (*tmp));
295 0 : if (tmp == NULL) {
296 0 : free (row.data);
297 0 : return ENOMEM;
298 : }
299 0 : c->rows = tmp;
300 0 : c->rows[c->num_rows++] = row;
301 0 : return 0;
302 : }
303 :
304 : ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
305 : rtbl_add_column_entry_by_id (rtbl_t table, unsigned int id, const char *data)
306 0 : {
307 0 : struct column_data *c = rtbl_get_column_by_id (table, id);
308 :
309 0 : if (c == NULL)
310 0 : return -1;
311 :
312 0 : return add_column_entry(c, data);
313 : }
314 :
315 : ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
316 : rtbl_add_column_entryv_by_id (rtbl_t table, unsigned int id,
317 : const char *fmt, ...)
318 0 : {
319 : va_list ap;
320 : char *str;
321 : int ret;
322 :
323 0 : va_start(ap, fmt);
324 0 : ret = vasprintf(&str, fmt, ap);
325 0 : va_end(ap);
326 0 : if (ret == -1)
327 0 : return -1;
328 0 : ret = rtbl_add_column_entry_by_id(table, id, str);
329 0 : free(str);
330 0 : return ret;
331 : }
332 :
333 : ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
334 : rtbl_add_column_entry (rtbl_t table, const char *column, const char *data)
335 0 : {
336 0 : struct column_data *c = rtbl_get_column (table, column);
337 :
338 0 : if (c == NULL)
339 0 : return -1;
340 :
341 0 : return add_column_entry(c, data);
342 : }
343 :
344 : ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
345 : rtbl_add_column_entryv (rtbl_t table, const char *column, const char *fmt, ...)
346 0 : {
347 : va_list ap;
348 : char *str;
349 : int ret;
350 :
351 0 : va_start(ap, fmt);
352 0 : ret = vasprintf(&str, fmt, ap);
353 0 : va_end(ap);
354 0 : if (ret == -1)
355 0 : return -1;
356 0 : ret = rtbl_add_column_entry(table, column, str);
357 0 : free(str);
358 0 : return ret;
359 : }
360 :
361 :
362 : ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
363 : rtbl_format (rtbl_t table, FILE * f)
364 0 : {
365 : size_t i, j;
366 :
367 0 : for (i = 0; i < table->num_columns; i++)
368 0 : column_compute_width (table, table->columns[i]);
369 0 : if((table->flags & RTBL_HEADER_STYLE_NONE) == 0) {
370 0 : for (i = 0; i < table->num_columns; i++) {
371 0 : struct column_data *c = table->columns[i];
372 :
373 0 : if(table->column_separator != NULL && i > 0)
374 0 : fprintf (f, "%s", table->column_separator);
375 0 : fprintf (f, "%s", get_column_prefix (table, c));
376 0 : if(i == table->num_columns - 1 && c->suffix == NULL)
377 : /* last column, so no need to pad with spaces */
378 0 : fprintf (f, "%-*s", 0, c->header);
379 : else
380 0 : fprintf (f, "%-*s", (int)c->width, c->header);
381 0 : fprintf (f, "%s", get_column_suffix (table, c));
382 : }
383 0 : fprintf (f, "\n");
384 : }
385 :
386 0 : for (j = 0;; j++) {
387 0 : int flag = 0;
388 :
389 : /* are there any more rows left? */
390 0 : for (i = 0; flag == 0 && i < table->num_columns; ++i) {
391 0 : struct column_data *c = table->columns[i];
392 :
393 0 : if (c->num_rows > j) {
394 0 : ++flag;
395 0 : break;
396 : }
397 : }
398 0 : if (flag == 0)
399 0 : break;
400 :
401 0 : for (i = 0; i < table->num_columns; i++) {
402 : int w;
403 0 : struct column_data *c = table->columns[i];
404 :
405 0 : if(table->column_separator != NULL && i > 0)
406 0 : fprintf (f, "%s", table->column_separator);
407 :
408 0 : w = c->width;
409 :
410 0 : if ((c->flags & RTBL_ALIGN_RIGHT) == 0) {
411 0 : if(i == table->num_columns - 1 && c->suffix == NULL)
412 : /* last column, so no need to pad with spaces */
413 0 : w = 0;
414 : else
415 0 : w = -w;
416 : }
417 0 : fprintf (f, "%s", get_column_prefix (table, c));
418 0 : if (c->num_rows <= j)
419 0 : fprintf (f, "%*s", w, "");
420 : else
421 0 : fprintf (f, "%*s", w, c->rows[j].data);
422 0 : fprintf (f, "%s", get_column_suffix (table, c));
423 : }
424 0 : fprintf (f, "\n");
425 0 : }
426 0 : return 0;
427 : }
428 :
429 : #ifdef TEST
430 : int
431 : main (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
|