Branch data 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 "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 : 0 : rtbl_create (void)
64 : : {
65 : 0 : return calloc (1, sizeof (struct rtbl_data));
66 : : }
67 : :
68 : : ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
69 : 0 : rtbl_set_flags (rtbl_t table, unsigned int flags)
70 : : {
71 : 0 : table->flags = flags;
72 : 0 : }
73 : :
74 : : ROKEN_LIB_FUNCTION unsigned int ROKEN_LIB_CALL
75 : 0 : rtbl_get_flags (rtbl_t table)
76 : : {
77 : 0 : return table->flags;
78 : : }
79 : :
80 : : static struct column_data *
81 : 0 : rtbl_get_column_by_id (rtbl_t table, unsigned int id)
82 : : {
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 : 0 : rtbl_get_column (rtbl_t table, const char *column)
92 : : {
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 : 0 : rtbl_destroy (rtbl_t table)
102 : : {
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 : 0 : rtbl_add_column_by_id (rtbl_t table, unsigned int id,
124 : : const char *header, unsigned int flags)
125 : : {
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 : 0 : rtbl_add_column (rtbl_t table, const char *header, unsigned int flags)
153 : : {
154 : 0 : return rtbl_add_column_by_id(table, 0, header, flags);
155 : : }
156 : :
157 : : ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
158 : 0 : rtbl_new_row(rtbl_t table)
159 : : {
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 : 0 : column_compute_width (rtbl_t table, struct column_data *column)
185 : : {
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 : 0 : rtbl_set_prefix (rtbl_t table, const char *prefix)
199 : : {
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 : 0 : rtbl_set_separator (rtbl_t table, const char *separator)
210 : : {
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 : 0 : rtbl_set_column_prefix (rtbl_t table, const char *column,
221 : : const char *prefix)
222 : : {
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 : 0 : rtbl_set_column_affix_by_id(rtbl_t table, unsigned int id,
237 : : const char *prefix, const char *suffix)
238 : : {
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 : 0 : get_column_prefix (rtbl_t table, struct column_data *c)
268 : : {
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 : 0 : get_column_suffix (rtbl_t table, struct column_data *c)
280 : : {
281 [ # # ][ # # ]: 0 : if (c && c->suffix)
282 : 0 : return c->suffix;
283 : 0 : return "";
284 : : }
285 : :
286 : : static int
287 : 0 : add_column_entry (struct column_data *c, const char *data)
288 : : {
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 : 0 : rtbl_add_column_entry_by_id (rtbl_t table, unsigned int id, const char *data)
306 : : {
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 : 0 : rtbl_add_column_entryv_by_id (rtbl_t table, unsigned int id,
317 : : const char *fmt, ...)
318 : : {
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 : 0 : rtbl_add_column_entry (rtbl_t table, const char *column, const char *data)
335 : : {
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 : 0 : rtbl_add_column_entryv (rtbl_t table, const char *column, const char *fmt, ...)
346 : : {
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 : 0 : rtbl_format (rtbl_t table, FILE * f)
364 : : {
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 : : 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
|