]>
Commit | Line | Data |
---|---|---|
3a3eb9da | 1 | /* |
9b6937eb | 2 | * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. |
3a3eb9da BP |
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 | ||
3e8a2ad1 | 21 | #include "openvswitch/dynamic-string.h" |
ee89ea7b | 22 | #include "openvswitch/json.h" |
3a3eb9da | 23 | #include "ovsdb-data.h" |
84ba64b9 | 24 | #include "ovsdb-error.h" |
8f46c9bb | 25 | #include "timeval.h" |
3a3eb9da BP |
26 | #include "util.h" |
27 | ||
28 | struct column { | |
29 | char *heading; | |
30 | }; | |
31 | ||
32 | static char * | |
33 | cell_to_text(struct cell *cell, const struct table_style *style) | |
34 | { | |
35 | if (!cell->text) { | |
36 | if (cell->json) { | |
37 | if (style->cell_format == CF_JSON || !cell->type) { | |
38 | cell->text = json_to_string(cell->json, JSSF_SORT); | |
c6a41252 | 39 | } else { |
3a3eb9da BP |
40 | struct ovsdb_datum datum; |
41 | struct ovsdb_error *error; | |
42 | struct ds s; | |
43 | ||
44 | error = ovsdb_datum_from_json(&datum, cell->type, cell->json, | |
45 | NULL); | |
46 | if (!error) { | |
47 | ds_init(&s); | |
c6a41252 BP |
48 | if (style->cell_format == CF_STRING) { |
49 | ovsdb_datum_to_string(&datum, cell->type, &s); | |
50 | } else { | |
51 | ovsdb_datum_to_bare(&datum, cell->type, &s); | |
52 | } | |
3a3eb9da BP |
53 | ovsdb_datum_destroy(&datum, cell->type); |
54 | cell->text = ds_steal_cstr(&s); | |
55 | } else { | |
56 | cell->text = json_to_string(cell->json, JSSF_SORT); | |
84ba64b9 | 57 | ovsdb_error_destroy(error); |
3a3eb9da | 58 | } |
3a3eb9da BP |
59 | } |
60 | } else { | |
61 | cell->text = xstrdup(""); | |
62 | } | |
63 | } | |
64 | ||
65 | return cell->text; | |
66 | } | |
67 | ||
68 | static void | |
69 | cell_destroy(struct cell *cell) | |
70 | { | |
71 | free(cell->text); | |
72 | json_destroy(cell->json); | |
73 | } | |
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 | table_init(struct table *table) | |
89 | { | |
90 | memset(table, 0, sizeof *table); | |
91 | } | |
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 | table_destroy(struct table *table) | |
97 | { | |
98 | if (table) { | |
99 | size_t i; | |
100 | ||
101 | for (i = 0; i < table->n_columns; i++) { | |
102 | free(table->columns[i].heading); | |
103 | } | |
104 | free(table->columns); | |
105 | ||
106 | for (i = 0; i < table->n_columns * table->n_rows; i++) { | |
107 | cell_destroy(&table->cells[i]); | |
108 | } | |
109 | free(table->cells); | |
110 | ||
111 | free(table->caption); | |
112 | } | |
113 | } | |
114 | ||
115 | /* Sets 'caption' as the caption for 'table'. | |
116 | * | |
117 | * 'table' takes ownership of 'caption'. */ | |
118 | void | |
119 | table_set_caption(struct table *table, char *caption) | |
120 | { | |
121 | free(table->caption); | |
122 | table->caption = caption; | |
123 | } | |
124 | ||
8f46c9bb BP |
125 | /* Turns printing a timestamp along with 'table' on or off, according to |
126 | * 'timestamp'. */ | |
127 | void | |
128 | table_set_timestamp(struct table *table, bool timestamp) | |
129 | { | |
130 | table->timestamp = timestamp; | |
131 | } | |
132 | ||
3a3eb9da BP |
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 | table_add_column(struct table *table, const char *heading, ...) | |
140 | { | |
141 | struct column *column; | |
142 | va_list args; | |
143 | ||
cb22974d | 144 | ovs_assert(!table->n_rows); |
3a3eb9da BP |
145 | if (table->n_columns >= table->allocated_columns) { |
146 | table->columns = x2nrealloc(table->columns, &table->allocated_columns, | |
147 | sizeof *table->columns); | |
148 | } | |
149 | column = &table->columns[table->n_columns++]; | |
150 | ||
151 | va_start(args, heading); | |
152 | column->heading = xvasprintf(heading, args); | |
153 | va_end(args); | |
154 | } | |
155 | ||
156 | static struct cell * | |
157 | table_cell__(const struct table *table, size_t row, size_t column) | |
158 | { | |
159 | 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 | table_add_row(struct table *table) | |
168 | { | |
169 | size_t x, y; | |
170 | ||
171 | if (table->n_rows >= table->allocated_rows) { | |
172 | table->cells = x2nrealloc(table->cells, &table->allocated_rows, | |
173 | table->n_columns * sizeof *table->cells); | |
174 | } | |
175 | ||
176 | y = table->n_rows++; | |
177 | table->current_column = 0; | |
178 | for (x = 0; x < table->n_columns; x++) { | |
179 | struct cell *cell = table_cell__(table, y, x); | |
180 | memset(cell, 0, sizeof *cell); | |
181 | } | |
182 | } | |
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 | table_add_cell(struct table *table) | |
202 | { | |
203 | size_t x, y; | |
204 | ||
cb22974d BP |
205 | ovs_assert(table->n_rows > 0); |
206 | ovs_assert(table->current_column < table->n_columns); | |
3a3eb9da BP |
207 | |
208 | x = table->current_column++; | |
209 | y = table->n_rows - 1; | |
210 | ||
211 | return table_cell__(table, y, x); | |
212 | } | |
213 | ||
214 | static void | |
cb139fa8 | 215 | table_finish_line(struct ds *s) |
3a3eb9da | 216 | { |
cb139fa8 BP |
217 | while (ds_last(s) == ' ') { |
218 | s->length--; | |
e51d0a1d | 219 | } |
cb139fa8 | 220 | ds_put_char(s, '\n'); |
3a3eb9da BP |
221 | } |
222 | ||
3e78870d BP |
223 | static char * |
224 | table_format_timestamp__(void) | |
8f46c9bb | 225 | { |
2b31d8e7 | 226 | return xastrftime_msec("%Y-%m-%d %H:%M:%S.###", time_wall_msec(), true); |
8f46c9bb BP |
227 | } |
228 | ||
229 | static void | |
cb139fa8 | 230 | table_print_timestamp__(const struct table *table, struct ds *s) |
8f46c9bb BP |
231 | { |
232 | if (table->timestamp) { | |
cb139fa8 BP |
233 | char *timestamp = table_format_timestamp__(); |
234 | ds_put_format(s, "%s\n", timestamp); | |
235 | free(timestamp); | |
8f46c9bb BP |
236 | } |
237 | } | |
238 | ||
d9cf9b2d MM |
239 | static bool first_table = true; |
240 | ||
3a3eb9da | 241 | static void |
cb139fa8 BP |
242 | table_print_table__(const struct table *table, const struct table_style *style, |
243 | struct ds *s) | |
3a3eb9da | 244 | { |
3a3eb9da BP |
245 | int *widths; |
246 | size_t x, y; | |
247 | ||
d9cf9b2d MM |
248 | if (first_table) { |
249 | first_table = false; | |
250 | } else { | |
cb139fa8 | 251 | ds_put_char(s, '\n'); |
3a3eb9da BP |
252 | } |
253 | ||
cb139fa8 | 254 | table_print_timestamp__(table, s); |
8f46c9bb | 255 | |
3a3eb9da | 256 | if (table->caption) { |
cb139fa8 | 257 | ds_put_format(s, "%s\n", table->caption); |
3a3eb9da BP |
258 | } |
259 | ||
80f66ee0 | 260 | widths = xzalloc(table->n_columns * sizeof *widths); |
3a3eb9da BP |
261 | for (x = 0; x < table->n_columns; x++) { |
262 | const struct column *column = &table->columns[x]; | |
263 | ||
80f66ee0 | 264 | int w = 0; |
3a3eb9da BP |
265 | for (y = 0; y < table->n_rows; y++) { |
266 | const char *text = cell_to_text(table_cell__(table, y, x), style); | |
267 | size_t length = strlen(text); | |
268 | ||
80f66ee0 BP |
269 | if (length > w) { |
270 | w = length; | |
3a3eb9da BP |
271 | } |
272 | } | |
80f66ee0 BP |
273 | |
274 | int max = style->max_column_width; | |
275 | if (max > 0 && w > max) { | |
276 | w = max; | |
277 | } | |
278 | if (style->headings) { | |
279 | int min = strlen(column->heading); | |
280 | if (w < min) { | |
281 | w = min; | |
282 | } | |
283 | } | |
284 | widths[x] = w; | |
3a3eb9da BP |
285 | } |
286 | ||
287 | if (style->headings) { | |
288 | for (x = 0; x < table->n_columns; x++) { | |
289 | const struct column *column = &table->columns[x]; | |
290 | if (x) { | |
cb139fa8 | 291 | ds_put_char(s, ' '); |
3a3eb9da | 292 | } |
cb139fa8 | 293 | ds_put_format(s, "%-*s", widths[x], column->heading); |
3a3eb9da | 294 | } |
cb139fa8 | 295 | table_finish_line(s); |
3a3eb9da BP |
296 | |
297 | for (x = 0; x < table->n_columns; x++) { | |
298 | if (x) { | |
cb139fa8 | 299 | ds_put_char(s, ' '); |
3a3eb9da | 300 | } |
cb139fa8 | 301 | ds_put_char_multiple(s, '-', widths[x]); |
3a3eb9da | 302 | } |
cb139fa8 | 303 | table_finish_line(s); |
3a3eb9da BP |
304 | } |
305 | ||
306 | for (y = 0; y < table->n_rows; y++) { | |
307 | for (x = 0; x < table->n_columns; x++) { | |
308 | const char *text = cell_to_text(table_cell__(table, y, x), style); | |
309 | if (x) { | |
cb139fa8 | 310 | ds_put_char(s, ' '); |
3a3eb9da | 311 | } |
cb139fa8 | 312 | ds_put_format(s, "%-*.*s", widths[x], widths[x], text); |
3a3eb9da | 313 | } |
cb139fa8 | 314 | table_finish_line(s); |
3a3eb9da BP |
315 | } |
316 | ||
3a3eb9da BP |
317 | free(widths); |
318 | } | |
319 | ||
c6a41252 | 320 | static void |
cb139fa8 BP |
321 | table_print_list__(const struct table *table, const struct table_style *style, |
322 | struct ds *s) | |
c6a41252 | 323 | { |
c6a41252 BP |
324 | size_t x, y; |
325 | ||
d9cf9b2d MM |
326 | if (first_table) { |
327 | first_table = false; | |
328 | } else { | |
cb139fa8 | 329 | ds_put_char(s, '\n'); |
c6a41252 BP |
330 | } |
331 | ||
cb139fa8 | 332 | table_print_timestamp__(table, s); |
8f46c9bb | 333 | |
c6a41252 | 334 | if (table->caption) { |
cb139fa8 | 335 | ds_put_format(s, "%s\n", table->caption); |
c6a41252 BP |
336 | } |
337 | ||
338 | for (y = 0; y < table->n_rows; y++) { | |
339 | if (y > 0) { | |
cb139fa8 | 340 | ds_put_char(s, '\n'); |
c6a41252 BP |
341 | } |
342 | for (x = 0; x < table->n_columns; x++) { | |
343 | const char *text = cell_to_text(table_cell__(table, y, x), style); | |
344 | if (style->headings) { | |
cb139fa8 | 345 | ds_put_format(s, "%-20s: ", table->columns[x].heading); |
c6a41252 | 346 | } |
cb139fa8 | 347 | ds_put_format(s, "%s\n", text); |
c6a41252 BP |
348 | } |
349 | } | |
350 | } | |
351 | ||
3a3eb9da | 352 | static void |
cb139fa8 | 353 | table_escape_html_text__(const char *content, size_t n, struct ds *s) |
3a3eb9da | 354 | { |
cb139fa8 | 355 | if (!strpbrk(content, "&<>\"")) { |
c3cc694b | 356 | ds_put_buffer(s, content, n); |
cb139fa8 BP |
357 | } else { |
358 | size_t i; | |
359 | ||
360 | for (i = 0; i < n; i++) { | |
361 | char c = content[i]; | |
362 | ||
363 | switch (c) { | |
364 | case '&': | |
365 | ds_put_cstr(s, "&"); | |
366 | break; | |
367 | case '<': | |
368 | ds_put_cstr(s, "<"); | |
369 | break; | |
370 | case '>': | |
371 | ds_put_cstr(s, ">"); | |
372 | break; | |
373 | case '"': | |
374 | ds_put_cstr(s, """); | |
375 | break; | |
376 | default: | |
377 | ds_put_char(s, c); | |
378 | break; | |
379 | } | |
3a3eb9da BP |
380 | } |
381 | } | |
382 | } | |
383 | ||
384 | static void | |
cb139fa8 | 385 | table_print_html_cell__(const char *element, const char *content, struct ds *s) |
3a3eb9da BP |
386 | { |
387 | const char *p; | |
388 | ||
cb139fa8 | 389 | ds_put_format(s, " <%s>", element); |
3a3eb9da BP |
390 | for (p = content; *p; ) { |
391 | struct uuid uuid; | |
392 | ||
393 | if (uuid_from_string_prefix(&uuid, p)) { | |
cb139fa8 | 394 | ds_put_format(s, "<a href=\"#%.*s\">%.*s</a>", UUID_LEN, p, 8, p); |
3a3eb9da BP |
395 | p += UUID_LEN; |
396 | } else { | |
cb139fa8 | 397 | table_escape_html_text__(p, 1, s); |
3a3eb9da BP |
398 | p++; |
399 | } | |
400 | } | |
cb139fa8 | 401 | ds_put_format(s, "</%s>\n", element); |
3a3eb9da BP |
402 | } |
403 | ||
404 | static void | |
cb139fa8 BP |
405 | table_print_html__(const struct table *table, const struct table_style *style, |
406 | struct ds *s) | |
3a3eb9da BP |
407 | { |
408 | size_t x, y; | |
409 | ||
cb139fa8 | 410 | table_print_timestamp__(table, s); |
8f46c9bb | 411 | |
cb139fa8 | 412 | ds_put_cstr(s, "<table border=1>\n"); |
3a3eb9da BP |
413 | |
414 | if (table->caption) { | |
cb139fa8 | 415 | table_print_html_cell__("caption", table->caption, s); |
3a3eb9da BP |
416 | } |
417 | ||
418 | if (style->headings) { | |
cb139fa8 | 419 | ds_put_cstr(s, " <tr>\n"); |
3a3eb9da BP |
420 | for (x = 0; x < table->n_columns; x++) { |
421 | const struct column *column = &table->columns[x]; | |
cb139fa8 | 422 | table_print_html_cell__("th", column->heading, s); |
3a3eb9da | 423 | } |
cb139fa8 | 424 | ds_put_cstr(s, " </tr>\n"); |
3a3eb9da BP |
425 | } |
426 | ||
427 | for (y = 0; y < table->n_rows; y++) { | |
cb139fa8 | 428 | ds_put_cstr(s, " <tr>\n"); |
3a3eb9da BP |
429 | for (x = 0; x < table->n_columns; x++) { |
430 | const char *content; | |
431 | ||
432 | content = cell_to_text(table_cell__(table, y, x), style); | |
433 | if (!strcmp(table->columns[x].heading, "_uuid")) { | |
cb139fa8 BP |
434 | ds_put_cstr(s, " <td><a name=\""); |
435 | table_escape_html_text__(content, strlen(content), s); | |
436 | ds_put_cstr(s, "\">"); | |
437 | table_escape_html_text__(content, 8, s); | |
438 | ds_put_cstr(s, "</a></td>\n"); | |
3a3eb9da | 439 | } else { |
cb139fa8 | 440 | table_print_html_cell__("td", content, s); |
3a3eb9da BP |
441 | } |
442 | } | |
cb139fa8 | 443 | ds_put_cstr(s, " </tr>\n"); |
3a3eb9da BP |
444 | } |
445 | ||
cb139fa8 | 446 | ds_put_cstr(s, "</table>\n"); |
3a3eb9da BP |
447 | } |
448 | ||
449 | static void | |
cb139fa8 | 450 | table_print_csv_cell__(const char *content, struct ds *s) |
3a3eb9da BP |
451 | { |
452 | const char *p; | |
453 | ||
454 | if (!strpbrk(content, "\n\",")) { | |
cb139fa8 | 455 | ds_put_cstr(s, content); |
3a3eb9da | 456 | } else { |
cb139fa8 | 457 | ds_put_char(s, '"'); |
3a3eb9da BP |
458 | for (p = content; *p != '\0'; p++) { |
459 | switch (*p) { | |
460 | case '"': | |
cb139fa8 | 461 | ds_put_cstr(s, "\"\""); |
3a3eb9da BP |
462 | break; |
463 | default: | |
cb139fa8 | 464 | ds_put_char(s, *p); |
3a3eb9da BP |
465 | break; |
466 | } | |
467 | } | |
cb139fa8 | 468 | ds_put_char(s, '"'); |
3a3eb9da BP |
469 | } |
470 | } | |
471 | ||
472 | static void | |
cb139fa8 BP |
473 | table_print_csv__(const struct table *table, const struct table_style *style, |
474 | struct ds *s) | |
3a3eb9da | 475 | { |
3a3eb9da BP |
476 | size_t x, y; |
477 | ||
d9cf9b2d MM |
478 | if (first_table) { |
479 | first_table = false; | |
480 | } else { | |
cb139fa8 | 481 | ds_put_char(s, '\n'); |
3a3eb9da BP |
482 | } |
483 | ||
cb139fa8 | 484 | table_print_timestamp__(table, s); |
8f46c9bb | 485 | |
3a3eb9da | 486 | if (table->caption) { |
cb139fa8 | 487 | ds_put_format(s, "%s\n", table->caption); |
3a3eb9da BP |
488 | } |
489 | ||
490 | if (style->headings) { | |
491 | for (x = 0; x < table->n_columns; x++) { | |
492 | const struct column *column = &table->columns[x]; | |
493 | if (x) { | |
cb139fa8 | 494 | ds_put_char(s, ','); |
3a3eb9da | 495 | } |
cb139fa8 | 496 | table_print_csv_cell__(column->heading, s); |
3a3eb9da | 497 | } |
cb139fa8 | 498 | ds_put_char(s, '\n'); |
3a3eb9da BP |
499 | } |
500 | ||
501 | for (y = 0; y < table->n_rows; y++) { | |
502 | for (x = 0; x < table->n_columns; x++) { | |
503 | if (x) { | |
cb139fa8 | 504 | ds_put_char(s, ','); |
3a3eb9da BP |
505 | } |
506 | table_print_csv_cell__(cell_to_text(table_cell__(table, y, x), | |
cb139fa8 | 507 | style), s); |
3a3eb9da | 508 | } |
cb139fa8 | 509 | ds_put_char(s, '\n'); |
3a3eb9da BP |
510 | } |
511 | } | |
512 | ||
513 | static void | |
cb139fa8 BP |
514 | table_print_json__(const struct table *table, const struct table_style *style, |
515 | struct ds *s) | |
3a3eb9da BP |
516 | { |
517 | struct json *json, *headings, *data; | |
518 | size_t x, y; | |
3a3eb9da BP |
519 | |
520 | json = json_object_create(); | |
521 | if (table->caption) { | |
522 | json_object_put_string(json, "caption", table->caption); | |
523 | } | |
8f46c9bb | 524 | if (table->timestamp) { |
cb139fa8 BP |
525 | json_object_put_nocopy( |
526 | json, "time", | |
527 | json_string_create_nocopy(table_format_timestamp__())); | |
8f46c9bb | 528 | } |
3a3eb9da BP |
529 | |
530 | headings = json_array_create_empty(); | |
531 | for (x = 0; x < table->n_columns; x++) { | |
532 | const struct column *column = &table->columns[x]; | |
533 | json_array_add(headings, json_string_create(column->heading)); | |
534 | } | |
535 | json_object_put(json, "headings", headings); | |
536 | ||
537 | data = json_array_create_empty(); | |
538 | for (y = 0; y < table->n_rows; y++) { | |
539 | struct json *row = json_array_create_empty(); | |
540 | for (x = 0; x < table->n_columns; x++) { | |
541 | const struct cell *cell = table_cell__(table, y, x); | |
542 | if (cell->text) { | |
543 | json_array_add(row, json_string_create(cell->text)); | |
8d56fd26 | 544 | } else if (cell->json) { |
3a3eb9da | 545 | json_array_add(row, json_clone(cell->json)); |
8d56fd26 BP |
546 | } else { |
547 | json_array_add(row, json_null_create()); | |
3a3eb9da BP |
548 | } |
549 | } | |
550 | json_array_add(data, row); | |
551 | } | |
552 | json_object_put(json, "data", data); | |
553 | ||
cb139fa8 | 554 | json_to_ds(json, style->json_flags, s); |
3562353e | 555 | ds_put_char(s, '\n'); |
3a3eb9da | 556 | json_destroy(json); |
3a3eb9da BP |
557 | } |
558 | \f | |
559 | /* Parses 'format' as the argument to a --format command line option, updating | |
560 | * 'style->format'. */ | |
561 | void | |
562 | table_parse_format(struct table_style *style, const char *format) | |
563 | { | |
564 | if (!strcmp(format, "table")) { | |
565 | style->format = TF_TABLE; | |
c6a41252 BP |
566 | } else if (!strcmp(format, "list")) { |
567 | style->format = TF_LIST; | |
3a3eb9da BP |
568 | } else if (!strcmp(format, "html")) { |
569 | style->format = TF_HTML; | |
570 | } else if (!strcmp(format, "csv")) { | |
571 | style->format = TF_CSV; | |
572 | } else if (!strcmp(format, "json")) { | |
573 | style->format = TF_JSON; | |
574 | } else { | |
575 | ovs_fatal(0, "unknown output format \"%s\"", format); | |
576 | } | |
577 | } | |
578 | ||
579 | /* Parses 'format' as the argument to a --data command line option, updating | |
580 | * 'style->cell_format'. */ | |
581 | void | |
582 | table_parse_cell_format(struct table_style *style, const char *format) | |
583 | { | |
584 | if (!strcmp(format, "string")) { | |
585 | style->cell_format = CF_STRING; | |
c6a41252 BP |
586 | } else if (!strcmp(format, "bare")) { |
587 | style->cell_format = CF_BARE; | |
3a3eb9da BP |
588 | } else if (!strcmp(format, "json")) { |
589 | style->cell_format = CF_JSON; | |
590 | } else { | |
591 | ovs_fatal(0, "unknown data format \"%s\"", format); | |
592 | } | |
593 | } | |
594 | ||
3a3eb9da | 595 | void |
cb139fa8 BP |
596 | table_format(const struct table *table, const struct table_style *style, |
597 | struct ds *s) | |
3a3eb9da BP |
598 | { |
599 | switch (style->format) { | |
600 | case TF_TABLE: | |
cb139fa8 | 601 | table_print_table__(table, style, s); |
3a3eb9da BP |
602 | break; |
603 | ||
c6a41252 | 604 | case TF_LIST: |
cb139fa8 | 605 | table_print_list__(table, style, s); |
c6a41252 BP |
606 | break; |
607 | ||
3a3eb9da | 608 | case TF_HTML: |
cb139fa8 | 609 | table_print_html__(table, style, s); |
3a3eb9da BP |
610 | break; |
611 | ||
612 | case TF_CSV: | |
cb139fa8 | 613 | table_print_csv__(table, style, s); |
3a3eb9da BP |
614 | break; |
615 | ||
616 | case TF_JSON: | |
cb139fa8 | 617 | table_print_json__(table, style, s); |
3a3eb9da BP |
618 | break; |
619 | } | |
620 | } | |
bcb58ce0 | 621 | |
d9cf9b2d MM |
622 | void |
623 | table_format_reset(void) | |
624 | { | |
625 | first_table = true; | |
626 | } | |
627 | ||
cb139fa8 BP |
628 | /* Outputs 'table' on stdout in the specified 'style'. */ |
629 | void | |
630 | table_print(const struct table *table, const struct table_style *style) | |
631 | { | |
632 | struct ds s = DS_EMPTY_INITIALIZER; | |
633 | table_format(table, style, &s); | |
634 | fputs(ds_cstr(&s), stdout); | |
635 | ds_destroy(&s); | |
636 | } | |
637 | ||
bcb58ce0 LR |
638 | void |
639 | table_usage(void) | |
640 | { | |
641 | printf("\nOutput formatting options:\n" | |
642 | " -f, --format=FORMAT set output formatting to FORMAT\n" | |
643 | " (\"table\", \"html\", \"csv\", " | |
644 | "or \"json\")\n" | |
645 | " -d, --data=FORMAT set table cell output formatting to\n" | |
646 | " FORMAT (\"string\", \"bare\", " | |
647 | "or \"json\")\n" | |
648 | " --no-headings omit table heading row\n" | |
649 | " --pretty pretty-print JSON in output\n" | |
650 | " --bare equivalent to " | |
651 | "\"--format=list --data=bare --no-headings\"\n"); | |
652 | } |