diff options
author | Thomas Haller <thaller@redhat.com> | 2018-06-08 10:49:06 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2018-06-13 15:29:41 +0200 |
commit | 3d91144d9fc3e89ad373b61715496693b75e9215 (patch) | |
tree | 8cccb30906922076664ba69978e0a60ae4a0a204 | |
parent | e589eeb034227461fccedc48e2cb698213b1d9bf (diff) | |
download | NetworkManager-3d91144d9fc3e89ad373b61715496693b75e9215.tar.gz |
cli: extend printing to support different styles of headers
For
$ nmcli -f ALL device show wlan0
we print
AP[1].SSID: home
Note how the section header (AP[1]) is not only a static header,
instead it also contains an index (because we might have multiple
APs).
This output format was not supported by nmc_print() before.
Extend nmc_print() and the meta data, to handle displaying such
property types and groups.
There is no change in behavior, because no property is yet marked
as with_indexed_header.
-rw-r--r-- | clients/cli/utils.c | 125 | ||||
-rw-r--r-- | clients/cli/utils.h | 22 |
2 files changed, 123 insertions, 24 deletions
diff --git a/clients/cli/utils.c b/clients/cli/utils.c index 5b560f71d4..a838eac585 100644 --- a/clients/cli/utils.c +++ b/clients/cli/utils.c @@ -77,6 +77,10 @@ _meta_type_nmc_generic_info_get_nested (const NMMetaAbstractInfo *abstract_info, info = (const NmcMetaGenericInfo *) abstract_info; + /* don't accept setting an empty array. This hints to a bug. Did you set + * the NmcGenericInfoType enum right? */ + nm_assert (!info->nested || info->nested[0]); + NM_SET_OUT (out_len, NM_PTRARRAY_LEN (info->nested)); return (const NMMetaAbstractInfo *const*) info->nested; } @@ -934,17 +938,41 @@ nmc_empty_output_fields (NmcOutputData *output_data) /*****************************************************************************/ +static gboolean +_meta_abstract_info_with_indexed_header (const NMMetaAbstractInfo *info) +{ + nm_assert (info); + + return info->meta_type == &nmc_meta_type_generic_info + && ((const NmcMetaGenericInfo *) info)->with_indexed_header; +} + +static gboolean +_print_data_col_with_indexed_header (const PrintDataCol *col) +{ + nm_assert (col); + + /** a @col instance is considered to have an indexed-header, if the abstract-info + * that the col instance referens to is marked as such. + * ... or *any* of it's parents. */ + do { + if (_meta_abstract_info_with_indexed_header (col->selection_item->info)) + return TRUE; + col = col->parent_col; + } while (col); + return FALSE; +} + typedef struct { guint col_idx; + int width; const PrintDataCol *col; - const char *title; - bool title_to_free:1; + const char *title_parent; + const char *title_self; /* whether the column should be printed. If not %TRUE, * the column will be skipped. */ bool to_print:1; - - int width; } PrintDataHeaderCell; typedef enum { @@ -964,16 +992,45 @@ typedef struct { bool text_to_free:1; } PrintDataCell; +static const char * +_print_data_header_get_title (const PrintDataHeaderCell *header_cell, gssize row_idx, char **out_to_free) +{ + nm_assert (header_cell); + nm_assert (header_cell->col); + nm_assert (header_cell->title_self); + nm_assert (out_to_free && !*out_to_free); + + if (!header_cell->title_parent) + return header_cell->title_self; + + nm_assert (header_cell->col->parent_col); + + if ( row_idx >= 0 + && _print_data_col_with_indexed_header (header_cell->col)) { + return (*out_to_free = g_strdup_printf ("%s[%llu].%s", + header_cell->title_parent, + (long long unsigned) (row_idx + 1), + header_cell->title_self)); + } + return (*out_to_free = g_strconcat (header_cell->title_parent, ".", header_cell->title_self, NULL)); +} + +static guint +_print_data_header_get_title_width (const PrintDataHeaderCell *header_cell, gssize row_idx) +{ + gs_free char *s = NULL; + + return nmc_string_screen_width (_print_data_header_get_title (header_cell, row_idx, &s), + NULL); +} + static void _print_data_header_cell_clear (gpointer cell_p) { PrintDataHeaderCell *cell = cell_p; - if (cell->title_to_free) { - g_free ((char *) cell->title); - cell->title_to_free = FALSE; - } - cell->title = NULL; + cell->title_parent = NULL; + cell->title_self = NULL; } static void @@ -1019,7 +1076,6 @@ _print_fill (const NmcConfig *nmc_config, NMMetaAccessorGetType text_get_type; NMMetaAccessorGetFlags text_get_flags; - header_row = g_array_sized_new (FALSE, TRUE, sizeof (PrintDataHeaderCell), cols_len); g_array_set_clear_func (header_row, _print_data_header_cell_clear); @@ -1047,17 +1103,14 @@ _print_fill (const NmcConfig *nmc_config, * unless we have a cell (below) which opts-in to be printed. */ header_cell->to_print = FALSE; - header_cell->title = nm_meta_abstract_info_get_name (info, TRUE); + header_cell->title_parent = NULL; + header_cell->title_self = nm_meta_abstract_info_get_name (info, TRUE); if ( nmc_config->multiline_output && col->parent_col && NM_IN_SET (info->meta_type, &nm_meta_type_property_info, - &nmc_meta_type_generic_info)) { - header_cell->title = g_strdup_printf ("%s.%s", - nm_meta_abstract_info_get_name (col->parent_col->selection_item->info, FALSE), - header_cell->title); - header_cell->title_to_free = TRUE; - } + &nmc_meta_type_generic_info)) + header_cell->title_parent = nm_meta_abstract_info_get_name (col->parent_col->selection_item->info, FALSE); } targets_len = NM_PTRARRAY_LEN (targets); @@ -1145,13 +1198,31 @@ _print_fill (const NmcConfig *nmc_config, cell->text.plain = ""; nm_assert (cell->text_format == PRINT_DATA_CELL_FORMAT_TYPE_PLAIN); } + + if ( !nmc_config->multiline_output + && info->meta_type == &nmc_meta_type_generic_info + && ((const NmcMetaGenericInfo *) info)->nested + && _print_data_col_with_indexed_header (header_cell->col) + && cell->text_format == PRINT_DATA_CELL_FORMAT_TYPE_PLAIN) { + char *s; + + /* this cell is the name (e.g. NAME=AP). We need to add an index + * to the header name in multiline mode. */ + s = g_strdup_printf ("%s[%llu]", + cell->text.plain, + (long long unsigned) (i_row + 1)); + _print_data_cell_clear_text (cell); + nm_assert (cell->text_format == PRINT_DATA_CELL_FORMAT_TYPE_PLAIN); + cell->text.plain = s; + cell->text_to_free = TRUE; + } } } for (i_col = 0; i_col < header_row->len; i_col++) { PrintDataHeaderCell *header_cell = &g_array_index (header_row, PrintDataHeaderCell, i_col); - header_cell->width = nmc_string_screen_width (header_cell->title, NULL); + header_cell->width = _print_data_header_get_title_width (header_cell, ((gssize) targets_len) - 1); for (i_row = 0; i_row < targets_len; i_row++) { const PrintDataCell *cell = &g_array_index (cells, PrintDataCell, i_row * cols_len + i_col); @@ -1266,11 +1337,12 @@ _print_do (const NmcConfig *nmc_config, for (i_col = 0; i_col < col_len; i_col++) { const PrintDataHeaderCell *header_cell = &header_row[i_col]; const char *title; + gs_free char *title_free = NULL; if (_print_skip_column (nmc_config, header_cell)) continue; - title = header_cell->title; + title = _print_data_header_get_title (header_cell, -1, &title_free); width1 = strlen (title); width2 = nmc_string_screen_width (title, NULL); /* Width of the string (in screen colums) */ @@ -1294,6 +1366,7 @@ _print_do (const NmcConfig *nmc_config, for (i_row = 0; i_row < row_len; i_row++) { const PrintDataCell *current_line = &cells[i_row * col_len]; + gboolean any_with_indexed_header = FALSE; for (i_col = 0; i_col < col_len; i_col++) { const PrintDataCell *cell = ¤t_line[i_col]; @@ -1321,19 +1394,27 @@ _print_do (const NmcConfig *nmc_config, const char *text; text = colorize_string (nmc_config, cell->color, lines[i_lines], &text_to_free); + if (nmc_config->multiline_output) { gs_free char *prefix = NULL; + gs_free char *title_free = NULL; + const char *title; + + title = _print_data_header_get_title (cell->header_cell, i_row, &title_free); + + any_with_indexed_header = any_with_indexed_header + || _print_data_col_with_indexed_header (cell->header_cell->col); if (cell->text_format == PRINT_DATA_CELL_FORMAT_TYPE_STRV) - prefix = g_strdup_printf ("%s[%u]:", cell->header_cell->title, i_lines + 1); + prefix = g_strdup_printf ("%s[%u]:", title, i_lines + 1); else - prefix = g_strdup_printf ("%s:", cell->header_cell->title); + prefix = g_strdup_printf ("%s:", title); width1 = strlen (prefix); width2 = nmc_string_screen_width (prefix, NULL); g_print ("%-*s%s\n", (int) ( nmc_config->print_output == NMC_PRINT_TERSE ? 0 - : ML_VALUE_INDENT+width1-width2), + : ML_VALUE_INDENT + width1 - width2), prefix, text); } else { diff --git a/clients/cli/utils.h b/clients/cli/utils.h index b57e85b7fc..253ca79aa7 100644 --- a/clients/cli/utils.h +++ b/clients/cli/utils.h @@ -255,6 +255,21 @@ struct _NmcMetaGenericInfo { gconstpointer (*get_fcn) (NMC_META_GENERIC_INFO_GET_FCN_ARGS); + /* Some types behave rather different when being printed by nmc_print(). + * How exactly? Well... it's complicated. *sigh* + * + * For `nmcli -f ALL device show wlan0`, we print informations about the + * access point like + * + * AP[1].SSID: home + * + * Note how the header is not static, but also contains an index. + * + * Such groups will be marked as with_indexed_header, and cause nmc_print() + * to generate headers with an index. + */ + bool with_indexed_header:1; + /* @common_priority is used for implementing nm_meta_abstract_info_included_in_common(). * * Included-in-common returns two separate values: @@ -291,9 +306,12 @@ struct _NmcMetaGenericInfo { #define NMC_META_GENERIC_WITH_NESTED(n, nest, ...) \ NMC_META_GENERIC (n, .nested = (nest), __VA_ARGS__) -#define NMC_META_GENERIC_GROUP(_group_name, _nested, _name_header) \ +#define NMC_META_GENERIC_GROUP(_group_name, _nested, _name_header, ...) \ ((const NMMetaAbstractInfo *const*) ((const NmcMetaGenericInfo *const[]) { \ - NMC_META_GENERIC_WITH_NESTED (_group_name,_nested, .name_header = _name_header), \ + NMC_META_GENERIC_WITH_NESTED (_group_name, \ + _nested, \ + .name_header = _name_header, \ + ##__VA_ARGS__), \ NULL, \ })) |