diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 0f7d48e84..8b32748d4 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -770,7 +770,7 @@ nmc_connection_profile_details (NMConnection *connection, NmCli *nmc, gboolean s setting = nm_connection_get_setting_by_name (connection, nm_meta_setting_infos_editor[section_idx].general->setting_name); if (setting) { - setting_details (setting, nmc, prop_name, secrets); + setting_details (&nmc->nmc_config, setting, prop_name, secrets); was_output = TRUE; } } @@ -1390,8 +1390,8 @@ split_required_fields_for_con_show (const char *input, else if (!strcasecmp (*iter, CON_SHOW_DETAIL_GROUP_ACTIVE)) group_active = TRUE; else { - char *allowed1 = nmc_get_allowed_fields ((const NMMetaAbstractInfo *const*) nm_meta_setting_infos_editor_p (), -1); - char *allowed2 = nmc_get_allowed_fields ((const NMMetaAbstractInfo *const*) nmc_fields_con_active_details_groups, -1); + char *allowed1 = nmc_get_allowed_fields ((const NMMetaAbstractInfo *const*) nm_meta_setting_infos_editor_p ()); + char *allowed2 = nmc_get_allowed_fields ((const NMMetaAbstractInfo *const*) nmc_fields_con_active_details_groups); g_set_error (error, NMCLI_ERROR, 0, _("invalid field '%s'; allowed fields: %s and %s, or %s,%s"), *iter, allowed1, allowed2, CON_SHOW_DETAIL_GROUP_PROFILE, CON_SHOW_DETAIL_GROUP_ACTIVE); g_free (allowed1); @@ -5981,7 +5981,7 @@ editor_show_setting (NMSetting *setting, NmCli *nmc) nmc->nmc_config_mutable.multiline_output = TRUE; nmc->nmc_config_mutable.escape_values = 0; - setting_details (setting, nmc, NULL, nmc->editor_show_secrets); + setting_details (&nmc->nmc_config, setting, NULL, nmc->editor_show_secrets); } typedef enum { diff --git a/clients/cli/nmcli.c b/clients/cli/nmcli.c index 8ec4b42cd..b34add33a 100644 --- a/clients/cli/nmcli.c +++ b/clients/cli/nmcli.c @@ -61,9 +61,32 @@ _meta_type_nmc_generic_info_get_name (const NMMetaAbstractInfo *abstract_info) return ((const NmcMetaGenericInfo *) abstract_info)->name; } +static const NMMetaAbstractInfo *const* +_meta_type_nmc_generic_info_get_nested (const NMMetaAbstractInfo *abstract_info, + guint *out_len, + gpointer *out_to_free) +{ + const NmcMetaGenericInfo *info; + guint n; + + info = (const NmcMetaGenericInfo *) abstract_info; + + if (out_len) { + n = 0; + if (info->nested) { + for (; info->nested[n]; n++) { + } + } + *out_len = n; + } + *out_to_free = NULL; + return (const NMMetaAbstractInfo *const*) info->nested; +} + const NMMetaType nmc_meta_type_generic_info = { .type_name = "nmc-generic-info", .get_name = _meta_type_nmc_generic_info_get_name, + .get_nested = _meta_type_nmc_generic_info_get_nested, }; /*****************************************************************************/ diff --git a/clients/cli/settings.c b/clients/cli/settings.c index 110b8e012..158f82ecb 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -823,7 +823,7 @@ _dup_fields_array (const NMMetaSettingInfoEditor *setting_info, NmcOfFlags flags } gboolean -setting_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean show_secrets) +setting_details (const NmcConfig *nmc_config, NMSetting *setting, const char *one_prop, gboolean show_secrets) { const NMMetaSettingInfoEditor *setting_info; NmcOutputField *arr; @@ -834,11 +834,10 @@ setting_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); setting_info = nm_meta_setting_info_editor_find_by_setting (setting); - g_return_val_if_fail (setting_info, FALSE); + if (!setting_info) + return FALSE; - g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (setting, setting_info->general->get_setting_gtype ()), FALSE); - - if (nmc->nmc_config.print_output == NMC_PRINT_TERSE) + if (nmc_config->print_output == NMC_PRINT_TERSE) type = NM_META_ACCESSOR_GET_TYPE_PARSABLE; out_indices = parse_output_fields (one_prop, @@ -865,7 +864,7 @@ setting_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean g_ptr_array_add (out.output_data, arr); print_data_prepare_width (out.output_data); - print_data (&nmc->nmc_config, out_indices, NULL, 0, &out); + print_data (nmc_config, out_indices, NULL, 0, &out); return TRUE; } diff --git a/clients/cli/settings.h b/clients/cli/settings.h index 6235c08fd..e96e9366d 100644 --- a/clients/cli/settings.h +++ b/clients/cli/settings.h @@ -61,6 +61,6 @@ void nmc_property_set_default_value (NMSetting *setting, const char *prop); gboolean nmc_property_get_gvalue (NMSetting *setting, const char *prop, GValue *value); gboolean nmc_property_set_gvalue (NMSetting *setting, const char *prop, GValue *value); -gboolean setting_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets); +gboolean setting_details (const NmcConfig *nmc_config, NMSetting *setting, const char *one_prop, gboolean secrets); #endif /* NMC_SETTINGS_H */ diff --git a/clients/cli/utils.c b/clients/cli/utils.c index 3fc577763..046fb1c29 100644 --- a/clients/cli/utils.c +++ b/clients/cli/utils.c @@ -661,6 +661,159 @@ _abstract_info_get_name (const NMMetaAbstractInfo *abstract_info) return n; } +typedef struct { + guint idx; + gsize offset_plus_1; +} OutputSelectionItem; + +NmcOutputSelection * +nmc_output_selection_create (const NMMetaAbstractInfo *const* fields_array, + const char *fields_str, + GError **error) +{ + gs_unref_array GArray *array = NULL; + nm_auto_free_gstring GString *str = NULL; + guint i, j; + NmcOutputSelection *result; + + g_return_val_if_fail (!error || !*error, NULL); + + array = g_array_new (FALSE, FALSE, sizeof (OutputSelectionItem)); + + if (!fields_str) { + for (i = 0; fields_array[i]; i++) { + OutputSelectionItem s = { + .idx = i, + }; + + g_array_append_val (array, s); + } + } else { + gs_free char *fields_str_clone = NULL; + char *fields_str_cur; + char *fields_str_next; + + fields_str_clone = g_strdup (fields_str); + for (fields_str_cur = fields_str_clone; fields_str_cur; fields_str_cur = fields_str_next) { + const char *i_name; + const char *right = NULL; + gboolean found = FALSE; + const NMMetaAbstractInfo *fields_array_failure = NULL; + + fields_str_cur = nm_str_skip_leading_spaces (fields_str_cur); + fields_str_next = strchr (fields_str_cur, ','); + if (fields_str_next) + *fields_str_next++ = '\0'; + + g_strchomp (fields_str_cur); + if (!fields_str_cur[0]) + continue; + + i_name = fields_str_cur; + fields_str_cur = strchr (fields_str_cur, '.'); + if (fields_str_cur) { + right = fields_str_cur + 1; + *fields_str_cur = '\0'; + } + + for (i = 0; fields_array[i]; i++) { + const NMMetaAbstractInfo *fi = fields_array[i]; + + if (g_ascii_strcasecmp (i_name, _abstract_info_get_name (fi)) != 0) + continue; + + if (!right) + found = TRUE; + else { + found = FALSE; + if (fi->meta_type == &nm_meta_type_setting_info_editor) { + const NMMetaSettingInfoEditor *fi_s = &fi->as.setting_info; + + for (j = 1; j < fi_s->properties_num; j++) { + if (g_ascii_strcasecmp (right, fi_s->properties[j].property_name) == 0) { + found = TRUE; + break; + } + } + } else if (fi->meta_type == &nmc_meta_type_generic_info) { + const NmcMetaGenericInfo *fi_g = (const NmcMetaGenericInfo *) fi; + + for (j = 0; fi_g->nested && fi_g->nested[j]; j++) { + if (g_ascii_strcasecmp (right, _abstract_info_get_name ((const NMMetaAbstractInfo *) fi_g->nested[j])) == 0) { + found = TRUE; + break; + } + } + } + } + + if (found) { + OutputSelectionItem s = { + .idx = i, + }; + + if (right) { + if (!str) + str = g_string_sized_new (32); + + s.offset_plus_1 = str->len + 1; + g_string_append_len (str, right, strlen (right) + 1); + } + + g_array_append_val (array, s); + } + + fields_array_failure = fields_array[i]; + break; + } + + if (!found) { + if ( !right + && ( !g_ascii_strcasecmp (i_name, "all") + || !g_ascii_strcasecmp (i_name, "common"))) + g_set_error (error, NMCLI_ERROR, 0, _("field '%s' has to be alone"), i_name); + else { + gs_free char *allowed_fields = NULL; + + if (fields_array_failure) + allowed_fields = nmc_get_allowed_fields_nested (fields_array_failure); + else + allowed_fields = nmc_get_allowed_fields (fields_array); + + g_set_error (error, NMCLI_ERROR, 1, _("invalid field '%s%s%s'; allowed fields: %s"), + i_name, right ? "." : "", right ?: "", allowed_fields); + } + + return NULL; + } + } + } + + /* re-organize the collected output data in one buffer that can be freed using + * g_free(). This makes allocation more complicated, but saves us from special + * handling for free. */ + result = g_malloc0 (sizeof (NmcOutputSelection) + (array->len * sizeof (NmcOutputSelectionItem)) + (str ? str->len : 0)); + *((guint *) &result->num) = array->len; + if (array->len > 0) { + char *pdata = &((char *) result)[sizeof (NmcOutputSelection) + (array->len * sizeof (NmcOutputSelectionItem))]; + + if (str) + memcpy (pdata, str->str, str->len); + for (i = 0; i < array->len; i++) { + const OutputSelectionItem *a = &g_array_index (array, OutputSelectionItem, i); + NmcOutputSelectionItem *p = (NmcOutputSelectionItem *) &result->items[i]; + + p->info = fields_array[a->idx]; + p->idx = a->idx; + if (a->offset_plus_1 > 0) + p->sub_selection = &pdata[a->offset_plus_1 - 1]; + } + } + + return result; +} + + /** * parse_output_fields: * @field_str: comma-separated field names to parse @@ -688,144 +841,75 @@ parse_output_fields (const char *fields_str, GPtrArray **out_group_fields, GError **error) { - gs_strfreev char **fields = NULL; - const char *const*iter = NULL; - gs_unref_ptrarray GPtrArray *group_fields = NULL; - gs_unref_array GArray *array = NULL; - int i, j; + gs_free NmcOutputSelection *selection = NULL; + GArray *array; + GPtrArray *group_fields = NULL; + guint i; g_return_val_if_fail (!error || !*error, NULL); g_return_val_if_fail (!out_group_fields || !*out_group_fields, NULL); - array = g_array_new (FALSE, FALSE, sizeof (int)); + selection = nmc_output_selection_create (fields_array, fields_str, error); + if (!selection) + return NULL; + + array = g_array_sized_new (FALSE, FALSE, sizeof (int), selection->num); if (parse_groups && out_group_fields) - group_fields = g_ptr_array_new_full (20, g_free); + group_fields = g_ptr_array_new_full (selection->num, g_free); - if (!fields_str) { - for (i = 0; fields_array[i]; i++) { - g_array_append_val (array, i); - if (group_fields) - g_ptr_array_add (group_fields, NULL); - } - goto out; + for (i = 0; i < selection->num; i++) { + int idx = selection->items[i].idx; + + g_array_append_val (array, idx); + if (group_fields) + g_ptr_array_add (group_fields, g_strdup (selection->items[i].sub_selection)); } - fields = _nm_utils_strv_cleanup (g_strsplit_set (fields_str, ",", -1), - TRUE, - TRUE, - FALSE); - for (iter = (const char *const*) fields; *iter; iter++) { - gs_free char *tmp = NULL; - int idx = -1; - const char *i_name = *iter; - char *right = NULL; - - if (group_fields) { - tmp = g_strdup (i_name); - i_name = tmp; - right = strchr (tmp, '.'); - if (right) - *right++ = '\0'; - } - - for (i = 0; fields_array[i]; i++) { - const NMMetaAbstractInfo *fi = fields_array[i]; - gboolean found; - - if (g_ascii_strcasecmp (i_name, _abstract_info_get_name (fi)) != 0) - continue; - - if (!right) - found = TRUE; - else { - found = FALSE; - if (fi->meta_type == &nm_meta_type_setting_info_editor) { - const NMMetaSettingInfoEditor *fi_s = &fi->as.setting_info; - - for (j = 1; j < fi_s->properties_num; j++) { - if (g_ascii_strcasecmp (right, fi_s->properties[j].property_name) == 0) { - found = TRUE; - break; - } - } - } else if (fi->meta_type == &nmc_meta_type_generic_info) { - const NmcMetaGenericInfo *fi_g = (const NmcMetaGenericInfo *) fi; - - for (j = 0; fi_g->nested && fi_g->nested[j]; j++) { - if (g_ascii_strcasecmp (right, _abstract_info_get_name ((const NMMetaAbstractInfo *) fi_g->nested[j])) == 0) { - found = TRUE; - break; - } - } - } - } - - if (found) { - g_array_append_val (array, i); - if (group_fields) - g_ptr_array_add (group_fields, g_strdup (right)); - break; - } - - if ( !g_ascii_strcasecmp (i_name, "all") - || !g_ascii_strcasecmp (i_name, "common")) - g_set_error (error, NMCLI_ERROR, 0, _("field '%s' has to be alone"), i_name); - else { - gs_free char *allowed_fields = nmc_get_allowed_fields (fields_array, idx); - - g_set_error (error, NMCLI_ERROR, 1, _("invalid field '%s'; allowed fields: %s"), - i_name, allowed_fields); - } - NM_SET_OUT (out_group_fields, NULL); - return NULL; - } - } - -out: - NM_SET_OUT (out_group_fields, g_steal_pointer (&group_fields)); - return g_steal_pointer (&array); + if (group_fields) + *out_group_fields = group_fields; + return array; } -/** -* nmc_get_allowed_fields: -* @fields_array: array of fields -* @group_idx: index to the array (for second-level array in 'group' member), -* or -1 -* -* Returns: string of allowed fields names. -* Caller is responsible for freeing the array. -*/ char * -nmc_get_allowed_fields (const NMMetaAbstractInfo *const*fields_array, int group_idx) +nmc_get_allowed_fields_nested (const NMMetaAbstractInfo *abstract_info) { GString *allowed_fields = g_string_sized_new (256); int i; + const char *name = _abstract_info_get_name (abstract_info); + gs_free gpointer nested_to_free = NULL; + const NMMetaAbstractInfo *const*nested = NULL; - if (group_idx != -1 && fields_array[group_idx]->meta_type == &nmc_meta_type_generic_info) { - const NmcMetaGenericInfo *const*nested = ((const NmcMetaGenericInfo *) fields_array[group_idx])->nested; - const char *name = _abstract_info_get_name (fields_array[group_idx]); + if (abstract_info->meta_type->get_nested) + nested = abstract_info->meta_type->get_nested (abstract_info, NULL, &nested_to_free); + if (nested && nested[0]) { for (i = 0; nested && nested[i]; i++) { g_string_append_printf (allowed_fields, "%s.%s,", - name, _abstract_info_get_name ((const NMMetaAbstractInfo *) nested[i])); + name, _abstract_info_get_name (nested[i])); } - } else if (group_idx != -1 && fields_array[group_idx]->meta_type == &nm_meta_type_setting_info_editor) { - const NMMetaSettingInfoEditor *fi = &fields_array[group_idx]->as.setting_info; - const char *name = _abstract_info_get_name (fields_array[group_idx]); + } else + g_string_append_printf (allowed_fields, "%s,", name); - for (i = 1; i < fi->properties_num; i++) { - g_string_append_printf (allowed_fields, "%s.%s,", - name, fi->properties[i].property_name); - } - } else { - for (i = 0; fields_array[i]; i++) - g_string_append_printf (allowed_fields, "%s,", _abstract_info_get_name (fields_array[i])); - } g_string_truncate (allowed_fields, allowed_fields->len - 1); return g_string_free (allowed_fields, FALSE); } +char * +nmc_get_allowed_fields (const NMMetaAbstractInfo *const*fields_array) +{ + GString *allowed_fields = g_string_sized_new (256); + guint i; + + for (i = 0; fields_array[i]; i++) + g_string_append_printf (allowed_fields, "%s,", _abstract_info_get_name (fields_array[i])); + + if (allowed_fields->len) + g_string_truncate (allowed_fields, allowed_fields->len - 1); + + return g_string_free (allowed_fields, FALSE); +} + NmcOutputField * nmc_dup_fields_array (const NMMetaAbstractInfo *const*fields, NmcOfFlags flags) { @@ -943,14 +1027,13 @@ print_required_fields (const NmcConfig *nmc_config, gboolean main_header_only = of_flags & NMC_OF_FLAG_MAIN_HEADER_ONLY; gboolean field_names = of_flags & NMC_OF_FLAG_FIELD_NAMES; gboolean section_prefix = of_flags & NMC_OF_FLAG_SECTION_PREFIX; - gboolean main_header = main_header_add || main_header_only; enum { ML_HEADER_WIDTH = 79 }; enum { ML_VALUE_INDENT = 40 }; /* --- Main header --- */ - if (main_header && pretty) { + if ((main_header_add || main_header_only) && pretty) { int header_width = nmc_string_screen_width (header_name, NULL) + 4; if (multiline) { diff --git a/clients/cli/utils.h b/clients/cli/utils.h index 17f3af886..437deb776 100644 --- a/clients/cli/utils.h +++ b/clients/cli/utils.h @@ -60,12 +60,29 @@ void set_val_arrc (NmcOutputField fields_array[], guint32 index, const char **va void set_val_color_all (NmcOutputField fields_array[], NmcTermColor color); void set_val_color_fmt_all (NmcOutputField fields_array[], NmcTermFormat format); void nmc_free_output_field_values (NmcOutputField fields_array[]); + +typedef struct { + const NMMetaAbstractInfo *info; + const char *sub_selection; + guint idx; +} NmcOutputSelectionItem; + +typedef struct { + const guint num; + const NmcOutputSelectionItem items[]; +} NmcOutputSelection; + +NmcOutputSelection *nmc_output_selection_create (const NMMetaAbstractInfo *const* fields_array, + const char *fields_str, + GError **error); + GArray *parse_output_fields (const char *fields_str, const NMMetaAbstractInfo *const* fields_array, gboolean parse_groups, GPtrArray **group_fields, GError **error); -char *nmc_get_allowed_fields (const NMMetaAbstractInfo *const*fields_array, int group_idx); +char *nmc_get_allowed_fields_nested (const NMMetaAbstractInfo *abstract_info); +char *nmc_get_allowed_fields (const NMMetaAbstractInfo *const*fields_array); NmcOutputField *nmc_dup_fields_array (const NMMetaAbstractInfo *const*fields, NmcOfFlags flags); void nmc_empty_output_fields (NmcOutputData *output_data); void print_required_fields (const NmcConfig *nmc_config, diff --git a/clients/common/nm-meta-setting-access.c b/clients/common/nm-meta-setting-access.c index 2bce358ce..262a22208 100644 --- a/clients/common/nm-meta-setting-access.c +++ b/clients/common/nm-meta-setting-access.c @@ -82,6 +82,7 @@ nm_meta_setting_info_editor_find_by_setting (NMSetting *setting) setting_info = nm_meta_setting_info_editor_find_by_gtype (G_OBJECT_TYPE (setting)); nm_assert (setting_info == nm_meta_setting_info_editor_find_by_name (nm_setting_get_name (setting))); + nm_assert (!setting_info || G_TYPE_CHECK_INSTANCE_TYPE (setting, setting_info->general->get_setting_gtype ())); return setting_info; } diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c index 1454e5f49..afc498a16 100644 --- a/clients/common/nm-meta-setting-desc.c +++ b/clients/common/nm-meta-setting-desc.c @@ -30,6 +30,7 @@ #include "NetworkManager.h" #include "nm-vpn-helpers.h" #include "nm-client-utils.h" +#include "nm-meta-setting-access.h" /*****************************************************************************/ @@ -6637,9 +6638,24 @@ _meta_type_property_info_get_name (const NMMetaAbstractInfo *abstract_info) return ((const NMMetaPropertyInfo *) abstract_info)->property_name; } +static const NMMetaAbstractInfo *const* +_meta_type_setting_info_editor_get_nested (const NMMetaAbstractInfo *abstract_info, + guint *out_len, + gpointer *out_to_free) +{ + const NMMetaSettingInfoEditor *info; + + info = (const NMMetaSettingInfoEditor *) abstract_info; + + NM_SET_OUT (out_len, info->properties_num); + *out_to_free = NULL; + return (const NMMetaAbstractInfo *const*) nm_property_infos_for_setting_type (info->general->meta_type); +} + const NMMetaType nm_meta_type_setting_info_editor = { .type_name = "setting_info_editor", .get_name = _meta_type_setting_info_editor_get_name, + .get_nested = _meta_type_setting_info_editor_get_nested, }; const NMMetaType nm_meta_type_property_info = { diff --git a/clients/common/nm-meta-setting-desc.h b/clients/common/nm-meta-setting-desc.h index bcf7efd79..4363b8b0b 100644 --- a/clients/common/nm-meta-setting-desc.h +++ b/clients/common/nm-meta-setting-desc.h @@ -139,6 +139,9 @@ struct _NMMetaSettingInfoEditor { struct _NMMetaType { const char *type_name; const char *(*get_name) (const NMMetaAbstractInfo *abstract_info); + const NMMetaAbstractInfo *const*(*get_nested) (const NMMetaAbstractInfo *abstract_info, + guint *out_len, + gpointer *out_to_free); }; struct _NMMetaAbstractInfo {