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