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