2 * Copyright (c) 2009, 2010, 2011 Nicira Networks.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
23 #include "dynamic-string.h"
25 #include "ovsdb-data.h"
33 cell_to_text(struct cell *cell, const struct table_style *style)
37 if (style->cell_format == CF_JSON || !cell->type) {
38 cell->text = json_to_string(cell->json, JSSF_SORT);
39 } else if (style->cell_format == CF_STRING) {
40 struct ovsdb_datum datum;
41 struct ovsdb_error *error;
44 error = ovsdb_datum_from_json(&datum, cell->type, cell->json,
48 ovsdb_datum_to_string(&datum, cell->type, &s);
49 ovsdb_datum_destroy(&datum, cell->type);
50 cell->text = ds_steal_cstr(&s);
52 cell->text = json_to_string(cell->json, JSSF_SORT);
58 cell->text = xstrdup("");
66 cell_destroy(struct cell *cell)
69 json_destroy(cell->json);
72 /* Initializes 'table' as an empty table.
74 * The caller should then:
76 * 1. Call table_add_column() once for each column.
78 * 2a. Call table_add_row().
79 * 2b. For each column in the cell, call table_add_cell() and fill in
81 * 3. Call table_print() to print the final table.
82 * 4. Free the table with table_destroy().
85 table_init(struct table *table)
87 memset(table, 0, sizeof *table);
90 /* Destroys 'table' and frees all associated storage. (However, the client
91 * owns the 'type' members pointed to by cells, so these are not destroyed.) */
93 table_destroy(struct table *table)
98 for (i = 0; i < table->n_columns; i++) {
99 free(table->columns[i].heading);
101 free(table->columns);
103 for (i = 0; i < table->n_columns * table->n_rows; i++) {
104 cell_destroy(&table->cells[i]);
108 free(table->caption);
112 /* Sets 'caption' as the caption for 'table'.
114 * 'table' takes ownership of 'caption'. */
116 table_set_caption(struct table *table, char *caption)
118 free(table->caption);
119 table->caption = caption;
122 /* Adds a new column to 'table' just to the right of any existing column, with
123 * 'heading' as a title for the column. 'heading' must be a valid printf()
126 * Columns must be added before any data is put into 'table'. */
128 table_add_column(struct table *table, const char *heading, ...)
130 struct column *column;
133 assert(!table->n_rows);
134 if (table->n_columns >= table->allocated_columns) {
135 table->columns = x2nrealloc(table->columns, &table->allocated_columns,
136 sizeof *table->columns);
138 column = &table->columns[table->n_columns++];
140 va_start(args, heading);
141 column->heading = xvasprintf(heading, args);
146 table_cell__(const struct table *table, size_t row, size_t column)
148 return &table->cells[column + row * table->n_columns];
151 /* Adds a new row to 'table'. The table's columns must already have been added
152 * with table_add_column().
154 * The row is initially empty; use table_add_cell() to start filling it in. */
156 table_add_row(struct table *table)
160 if (table->n_rows >= table->allocated_rows) {
161 table->cells = x2nrealloc(table->cells, &table->allocated_rows,
162 table->n_columns * sizeof *table->cells);
166 table->current_column = 0;
167 for (x = 0; x < table->n_columns; x++) {
168 struct cell *cell = table_cell__(table, y, x);
169 memset(cell, 0, sizeof *cell);
173 /* Adds a new cell in the current row of 'table', which must have been added
174 * with table_add_row(). Cells are filled in the same order that the columns
175 * were added with table_add_column().
177 * The caller is responsible for filling in the returned cell, in one of two
180 * - If the cell should contain an ovsdb_datum, formatted according to the
181 * table style, then fill in the 'json' member with the JSON representation
182 * of the datum and 'type' with its type.
184 * - If the cell should contain a fixed text string, then the caller should
185 * assign that string to the 'text' member. This is undesirable if the
186 * cell actually contains OVSDB data because 'text' cannot be formatted
187 * according to the table style; it is always output verbatim.
190 table_add_cell(struct table *table)
194 assert(table->n_rows > 0);
195 assert(table->current_column < table->n_columns);
197 x = table->current_column++;
198 y = table->n_rows - 1;
200 return table_cell__(table, y, x);
204 table_print_table_line__(struct ds *line)
211 table_print_table__(const struct table *table, const struct table_style *style)
214 struct ds line = DS_EMPTY_INITIALIZER;
222 if (table->caption) {
223 puts(table->caption);
226 widths = xmalloc(table->n_columns * sizeof *widths);
227 for (x = 0; x < table->n_columns; x++) {
228 const struct column *column = &table->columns[x];
230 widths[x] = strlen(column->heading);
231 for (y = 0; y < table->n_rows; y++) {
232 const char *text = cell_to_text(table_cell__(table, y, x), style);
233 size_t length = strlen(text);
235 if (length > widths[x]) {
241 if (style->headings) {
242 for (x = 0; x < table->n_columns; x++) {
243 const struct column *column = &table->columns[x];
245 ds_put_char(&line, ' ');
247 ds_put_format(&line, "%-*s", widths[x], column->heading);
249 table_print_table_line__(&line);
251 for (x = 0; x < table->n_columns; x++) {
253 ds_put_char(&line, ' ');
255 ds_put_char_multiple(&line, '-', widths[x]);
257 table_print_table_line__(&line);
260 for (y = 0; y < table->n_rows; y++) {
261 for (x = 0; x < table->n_columns; x++) {
262 const char *text = cell_to_text(table_cell__(table, y, x), style);
264 ds_put_char(&line, ' ');
266 ds_put_format(&line, "%-*s", widths[x], text);
268 table_print_table_line__(&line);
276 table_escape_html_text__(const char *s, size_t n)
280 for (i = 0; i < n; i++) {
285 fputs("&", stdout);
288 fputs("<", stdout);
291 fputs(">", stdout);
294 fputs(""", stdout);
304 table_print_html_cell__(const char *element, const char *content)
308 printf(" <%s>", element);
309 for (p = content; *p; ) {
312 if (uuid_from_string_prefix(&uuid, p)) {
313 printf("<a href=\"#%.*s\">%.*s</a>", UUID_LEN, p, 8, p);
316 table_escape_html_text__(p, 1);
320 printf("</%s>\n", element);
324 table_print_html__(const struct table *table, const struct table_style *style)
328 fputs("<table border=1>\n", stdout);
330 if (table->caption) {
331 table_print_html_cell__("caption", table->caption);
334 if (style->headings) {
335 fputs(" <tr>\n", stdout);
336 for (x = 0; x < table->n_columns; x++) {
337 const struct column *column = &table->columns[x];
338 table_print_html_cell__("th", column->heading);
340 fputs(" </tr>\n", stdout);
343 for (y = 0; y < table->n_rows; y++) {
344 fputs(" <tr>\n", stdout);
345 for (x = 0; x < table->n_columns; x++) {
348 content = cell_to_text(table_cell__(table, y, x), style);
349 if (!strcmp(table->columns[x].heading, "_uuid")) {
350 fputs(" <td><a name=\"", stdout);
351 table_escape_html_text__(content, strlen(content));
352 fputs("\">", stdout);
353 table_escape_html_text__(content, 8);
354 fputs("</a></td>\n", stdout);
356 table_print_html_cell__("td", content);
359 fputs(" </tr>\n", stdout);
362 fputs("</table>\n", stdout);
366 table_print_csv_cell__(const char *content)
370 if (!strpbrk(content, "\n\",")) {
371 fputs(content, stdout);
374 for (p = content; *p != '\0'; p++) {
377 fputs("\"\"", stdout);
389 table_print_csv__(const struct table *table, const struct table_style *style)
398 if (table->caption) {
399 puts(table->caption);
402 if (style->headings) {
403 for (x = 0; x < table->n_columns; x++) {
404 const struct column *column = &table->columns[x];
408 table_print_csv_cell__(column->heading);
413 for (y = 0; y < table->n_rows; y++) {
414 for (x = 0; x < table->n_columns; x++) {
418 table_print_csv_cell__(cell_to_text(table_cell__(table, y, x),
426 table_print_json__(const struct table *table, const struct table_style *style)
428 struct json *json, *headings, *data;
432 json = json_object_create();
433 if (table->caption) {
434 json_object_put_string(json, "caption", table->caption);
437 headings = json_array_create_empty();
438 for (x = 0; x < table->n_columns; x++) {
439 const struct column *column = &table->columns[x];
440 json_array_add(headings, json_string_create(column->heading));
442 json_object_put(json, "headings", headings);
444 data = json_array_create_empty();
445 for (y = 0; y < table->n_rows; y++) {
446 struct json *row = json_array_create_empty();
447 for (x = 0; x < table->n_columns; x++) {
448 const struct cell *cell = table_cell__(table, y, x);
450 json_array_add(row, json_string_create(cell->text));
452 json_array_add(row, json_clone(cell->json));
455 json_array_add(data, row);
457 json_object_put(json, "data", data);
459 s = json_to_string(json, style->json_flags);
465 /* Parses 'format' as the argument to a --format command line option, updating
466 * 'style->format'. */
468 table_parse_format(struct table_style *style, const char *format)
470 if (!strcmp(format, "table")) {
471 style->format = TF_TABLE;
472 } else if (!strcmp(format, "html")) {
473 style->format = TF_HTML;
474 } else if (!strcmp(format, "csv")) {
475 style->format = TF_CSV;
476 } else if (!strcmp(format, "json")) {
477 style->format = TF_JSON;
479 ovs_fatal(0, "unknown output format \"%s\"", format);
483 /* Parses 'format' as the argument to a --data command line option, updating
484 * 'style->cell_format'. */
486 table_parse_cell_format(struct table_style *style, const char *format)
488 if (!strcmp(format, "string")) {
489 style->cell_format = CF_STRING;
490 } else if (!strcmp(format, "json")) {
491 style->cell_format = CF_JSON;
493 ovs_fatal(0, "unknown data format \"%s\"", format);
497 /* Outputs 'table' on stdout in the specified 'style'. */
499 table_print(const struct table *table, const struct table_style *style)
501 switch (style->format) {
503 table_print_table__(table, style);
507 table_print_html__(table, style);
511 table_print_csv__(table, style);
515 table_print_json__(table, style);