]> git.proxmox.com Git - ovs.git/blob - lib/table.c
table: Avoid segmentation fault when printing an empty cell in JSON format.
[ovs.git] / lib / table.c
1 /*
2 * Copyright (c) 2009, 2010, 2011 Nicira Networks.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <config.h>
18
19 #include "table.h"
20
21 #include <assert.h>
22
23 #include "dynamic-string.h"
24 #include "json.h"
25 #include "ovsdb-data.h"
26 #include "ovsdb-error.h"
27 #include "util.h"
28
29 struct column {
30 char *heading;
31 };
32
33 static char *
34 cell_to_text(struct cell *cell, const struct table_style *style)
35 {
36 if (!cell->text) {
37 if (cell->json) {
38 if (style->cell_format == CF_JSON || !cell->type) {
39 cell->text = json_to_string(cell->json, JSSF_SORT);
40 } else {
41 struct ovsdb_datum datum;
42 struct ovsdb_error *error;
43 struct ds s;
44
45 error = ovsdb_datum_from_json(&datum, cell->type, cell->json,
46 NULL);
47 if (!error) {
48 ds_init(&s);
49 if (style->cell_format == CF_STRING) {
50 ovsdb_datum_to_string(&datum, cell->type, &s);
51 } else {
52 ovsdb_datum_to_bare(&datum, cell->type, &s);
53 }
54 ovsdb_datum_destroy(&datum, cell->type);
55 cell->text = ds_steal_cstr(&s);
56 } else {
57 cell->text = json_to_string(cell->json, JSSF_SORT);
58 ovsdb_error_destroy(error);
59 }
60 }
61 } else {
62 cell->text = xstrdup("");
63 }
64 }
65
66 return cell->text;
67 }
68
69 static void
70 cell_destroy(struct cell *cell)
71 {
72 free(cell->text);
73 json_destroy(cell->json);
74 }
75
76 /* Initializes 'table' as an empty table.
77 *
78 * The caller should then:
79 *
80 * 1. Call table_add_column() once for each column.
81 * 2. For each row:
82 * 2a. Call table_add_row().
83 * 2b. For each column in the cell, call table_add_cell() and fill in
84 * the returned cell.
85 * 3. Call table_print() to print the final table.
86 * 4. Free the table with table_destroy().
87 */
88 void
89 table_init(struct table *table)
90 {
91 memset(table, 0, sizeof *table);
92 }
93
94 /* Destroys 'table' and frees all associated storage. (However, the client
95 * owns the 'type' members pointed to by cells, so these are not destroyed.) */
96 void
97 table_destroy(struct table *table)
98 {
99 if (table) {
100 size_t i;
101
102 for (i = 0; i < table->n_columns; i++) {
103 free(table->columns[i].heading);
104 }
105 free(table->columns);
106
107 for (i = 0; i < table->n_columns * table->n_rows; i++) {
108 cell_destroy(&table->cells[i]);
109 }
110 free(table->cells);
111
112 free(table->caption);
113 }
114 }
115
116 /* Sets 'caption' as the caption for 'table'.
117 *
118 * 'table' takes ownership of 'caption'. */
119 void
120 table_set_caption(struct table *table, char *caption)
121 {
122 free(table->caption);
123 table->caption = caption;
124 }
125
126 /* Adds a new column to 'table' just to the right of any existing column, with
127 * 'heading' as a title for the column. 'heading' must be a valid printf()
128 * format specifier.
129 *
130 * Columns must be added before any data is put into 'table'. */
131 void
132 table_add_column(struct table *table, const char *heading, ...)
133 {
134 struct column *column;
135 va_list args;
136
137 assert(!table->n_rows);
138 if (table->n_columns >= table->allocated_columns) {
139 table->columns = x2nrealloc(table->columns, &table->allocated_columns,
140 sizeof *table->columns);
141 }
142 column = &table->columns[table->n_columns++];
143
144 va_start(args, heading);
145 column->heading = xvasprintf(heading, args);
146 va_end(args);
147 }
148
149 static struct cell *
150 table_cell__(const struct table *table, size_t row, size_t column)
151 {
152 return &table->cells[column + row * table->n_columns];
153 }
154
155 /* Adds a new row to 'table'. The table's columns must already have been added
156 * with table_add_column().
157 *
158 * The row is initially empty; use table_add_cell() to start filling it in. */
159 void
160 table_add_row(struct table *table)
161 {
162 size_t x, y;
163
164 if (table->n_rows >= table->allocated_rows) {
165 table->cells = x2nrealloc(table->cells, &table->allocated_rows,
166 table->n_columns * sizeof *table->cells);
167 }
168
169 y = table->n_rows++;
170 table->current_column = 0;
171 for (x = 0; x < table->n_columns; x++) {
172 struct cell *cell = table_cell__(table, y, x);
173 memset(cell, 0, sizeof *cell);
174 }
175 }
176
177 /* Adds a new cell in the current row of 'table', which must have been added
178 * with table_add_row(). Cells are filled in the same order that the columns
179 * were added with table_add_column().
180 *
181 * The caller is responsible for filling in the returned cell, in one of two
182 * fashions:
183 *
184 * - If the cell should contain an ovsdb_datum, formatted according to the
185 * table style, then fill in the 'json' member with the JSON representation
186 * of the datum and 'type' with its type.
187 *
188 * - If the cell should contain a fixed text string, then the caller should
189 * assign that string to the 'text' member. This is undesirable if the
190 * cell actually contains OVSDB data because 'text' cannot be formatted
191 * according to the table style; it is always output verbatim.
192 */
193 struct cell *
194 table_add_cell(struct table *table)
195 {
196 size_t x, y;
197
198 assert(table->n_rows > 0);
199 assert(table->current_column < table->n_columns);
200
201 x = table->current_column++;
202 y = table->n_rows - 1;
203
204 return table_cell__(table, y, x);
205 }
206
207 static void
208 table_print_table_line__(struct ds *line)
209 {
210 puts(ds_cstr(line));
211 ds_clear(line);
212 }
213
214 static void
215 table_print_table__(const struct table *table, const struct table_style *style)
216 {
217 static int n = 0;
218 struct ds line = DS_EMPTY_INITIALIZER;
219 int *widths;
220 size_t x, y;
221
222 if (n++ > 0) {
223 putchar('\n');
224 }
225
226 if (table->caption) {
227 puts(table->caption);
228 }
229
230 widths = xmalloc(table->n_columns * sizeof *widths);
231 for (x = 0; x < table->n_columns; x++) {
232 const struct column *column = &table->columns[x];
233
234 widths[x] = strlen(column->heading);
235 for (y = 0; y < table->n_rows; y++) {
236 const char *text = cell_to_text(table_cell__(table, y, x), style);
237 size_t length = strlen(text);
238
239 if (length > widths[x]) {
240 widths[x] = length;
241 }
242 }
243 }
244
245 if (style->headings) {
246 for (x = 0; x < table->n_columns; x++) {
247 const struct column *column = &table->columns[x];
248 if (x) {
249 ds_put_char(&line, ' ');
250 }
251 ds_put_format(&line, "%-*s", widths[x], column->heading);
252 }
253 table_print_table_line__(&line);
254
255 for (x = 0; x < table->n_columns; x++) {
256 if (x) {
257 ds_put_char(&line, ' ');
258 }
259 ds_put_char_multiple(&line, '-', widths[x]);
260 }
261 table_print_table_line__(&line);
262 }
263
264 for (y = 0; y < table->n_rows; y++) {
265 for (x = 0; x < table->n_columns; x++) {
266 const char *text = cell_to_text(table_cell__(table, y, x), style);
267 if (x) {
268 ds_put_char(&line, ' ');
269 }
270 ds_put_format(&line, "%-*s", widths[x], text);
271 }
272 table_print_table_line__(&line);
273 }
274
275 ds_destroy(&line);
276 free(widths);
277 }
278
279 static void
280 table_print_list__(const struct table *table, const struct table_style *style)
281 {
282 static int n = 0;
283 size_t x, y;
284
285 if (n++ > 0) {
286 putchar('\n');
287 }
288
289 if (table->caption) {
290 puts(table->caption);
291 }
292
293 for (y = 0; y < table->n_rows; y++) {
294 if (y > 0) {
295 putchar('\n');
296 }
297 for (x = 0; x < table->n_columns; x++) {
298 const char *text = cell_to_text(table_cell__(table, y, x), style);
299 if (style->headings) {
300 printf("%-20s: ", table->columns[x].heading);
301 }
302 puts(text);
303 }
304 }
305 }
306
307 static void
308 table_escape_html_text__(const char *s, size_t n)
309 {
310 size_t i;
311
312 for (i = 0; i < n; i++) {
313 char c = s[i];
314
315 switch (c) {
316 case '&':
317 fputs("&amp;", stdout);
318 break;
319 case '<':
320 fputs("&lt;", stdout);
321 break;
322 case '>':
323 fputs("&gt;", stdout);
324 break;
325 case '"':
326 fputs("&quot;", stdout);
327 break;
328 default:
329 putchar(c);
330 break;
331 }
332 }
333 }
334
335 static void
336 table_print_html_cell__(const char *element, const char *content)
337 {
338 const char *p;
339
340 printf(" <%s>", element);
341 for (p = content; *p; ) {
342 struct uuid uuid;
343
344 if (uuid_from_string_prefix(&uuid, p)) {
345 printf("<a href=\"#%.*s\">%.*s</a>", UUID_LEN, p, 8, p);
346 p += UUID_LEN;
347 } else {
348 table_escape_html_text__(p, 1);
349 p++;
350 }
351 }
352 printf("</%s>\n", element);
353 }
354
355 static void
356 table_print_html__(const struct table *table, const struct table_style *style)
357 {
358 size_t x, y;
359
360 fputs("<table border=1>\n", stdout);
361
362 if (table->caption) {
363 table_print_html_cell__("caption", table->caption);
364 }
365
366 if (style->headings) {
367 fputs(" <tr>\n", stdout);
368 for (x = 0; x < table->n_columns; x++) {
369 const struct column *column = &table->columns[x];
370 table_print_html_cell__("th", column->heading);
371 }
372 fputs(" </tr>\n", stdout);
373 }
374
375 for (y = 0; y < table->n_rows; y++) {
376 fputs(" <tr>\n", stdout);
377 for (x = 0; x < table->n_columns; x++) {
378 const char *content;
379
380 content = cell_to_text(table_cell__(table, y, x), style);
381 if (!strcmp(table->columns[x].heading, "_uuid")) {
382 fputs(" <td><a name=\"", stdout);
383 table_escape_html_text__(content, strlen(content));
384 fputs("\">", stdout);
385 table_escape_html_text__(content, 8);
386 fputs("</a></td>\n", stdout);
387 } else {
388 table_print_html_cell__("td", content);
389 }
390 }
391 fputs(" </tr>\n", stdout);
392 }
393
394 fputs("</table>\n", stdout);
395 }
396
397 static void
398 table_print_csv_cell__(const char *content)
399 {
400 const char *p;
401
402 if (!strpbrk(content, "\n\",")) {
403 fputs(content, stdout);
404 } else {
405 putchar('"');
406 for (p = content; *p != '\0'; p++) {
407 switch (*p) {
408 case '"':
409 fputs("\"\"", stdout);
410 break;
411 default:
412 putchar(*p);
413 break;
414 }
415 }
416 putchar('"');
417 }
418 }
419
420 static void
421 table_print_csv__(const struct table *table, const struct table_style *style)
422 {
423 static int n = 0;
424 size_t x, y;
425
426 if (n++ > 0) {
427 putchar('\n');
428 }
429
430 if (table->caption) {
431 puts(table->caption);
432 }
433
434 if (style->headings) {
435 for (x = 0; x < table->n_columns; x++) {
436 const struct column *column = &table->columns[x];
437 if (x) {
438 putchar(',');
439 }
440 table_print_csv_cell__(column->heading);
441 }
442 putchar('\n');
443 }
444
445 for (y = 0; y < table->n_rows; y++) {
446 for (x = 0; x < table->n_columns; x++) {
447 if (x) {
448 putchar(',');
449 }
450 table_print_csv_cell__(cell_to_text(table_cell__(table, y, x),
451 style));
452 }
453 putchar('\n');
454 }
455 }
456
457 static void
458 table_print_json__(const struct table *table, const struct table_style *style)
459 {
460 struct json *json, *headings, *data;
461 size_t x, y;
462 char *s;
463
464 json = json_object_create();
465 if (table->caption) {
466 json_object_put_string(json, "caption", table->caption);
467 }
468
469 headings = json_array_create_empty();
470 for (x = 0; x < table->n_columns; x++) {
471 const struct column *column = &table->columns[x];
472 json_array_add(headings, json_string_create(column->heading));
473 }
474 json_object_put(json, "headings", headings);
475
476 data = json_array_create_empty();
477 for (y = 0; y < table->n_rows; y++) {
478 struct json *row = json_array_create_empty();
479 for (x = 0; x < table->n_columns; x++) {
480 const struct cell *cell = table_cell__(table, y, x);
481 if (cell->text) {
482 json_array_add(row, json_string_create(cell->text));
483 } else if (cell->json) {
484 json_array_add(row, json_clone(cell->json));
485 } else {
486 json_array_add(row, json_null_create());
487 }
488 }
489 json_array_add(data, row);
490 }
491 json_object_put(json, "data", data);
492
493 s = json_to_string(json, style->json_flags);
494 json_destroy(json);
495 puts(s);
496 free(s);
497 }
498 \f
499 /* Parses 'format' as the argument to a --format command line option, updating
500 * 'style->format'. */
501 void
502 table_parse_format(struct table_style *style, const char *format)
503 {
504 if (!strcmp(format, "table")) {
505 style->format = TF_TABLE;
506 } else if (!strcmp(format, "list")) {
507 style->format = TF_LIST;
508 } else if (!strcmp(format, "html")) {
509 style->format = TF_HTML;
510 } else if (!strcmp(format, "csv")) {
511 style->format = TF_CSV;
512 } else if (!strcmp(format, "json")) {
513 style->format = TF_JSON;
514 } else {
515 ovs_fatal(0, "unknown output format \"%s\"", format);
516 }
517 }
518
519 /* Parses 'format' as the argument to a --data command line option, updating
520 * 'style->cell_format'. */
521 void
522 table_parse_cell_format(struct table_style *style, const char *format)
523 {
524 if (!strcmp(format, "string")) {
525 style->cell_format = CF_STRING;
526 } else if (!strcmp(format, "bare")) {
527 style->cell_format = CF_BARE;
528 } else if (!strcmp(format, "json")) {
529 style->cell_format = CF_JSON;
530 } else {
531 ovs_fatal(0, "unknown data format \"%s\"", format);
532 }
533 }
534
535 /* Outputs 'table' on stdout in the specified 'style'. */
536 void
537 table_print(const struct table *table, const struct table_style *style)
538 {
539 switch (style->format) {
540 case TF_TABLE:
541 table_print_table__(table, style);
542 break;
543
544 case TF_LIST:
545 table_print_list__(table, style);
546 break;
547
548 case TF_HTML:
549 table_print_html__(table, style);
550 break;
551
552 case TF_CSV:
553 table_print_csv__(table, style);
554 break;
555
556 case TF_JSON:
557 table_print_json__(table, style);
558 break;
559 }
560 }