summary refs log tree commit diff
path: root/clients/cli/connections.c
diff options
context:
space:
mode:
Diffstat (limited to 'clients/cli/connections.c')
-rw-r--r--clients/cli/connections.c3051
1 files changed, 1413 insertions, 1638 deletions
diff --git a/clients/cli/connections.c b/clients/cli/connections.c
index a93dc15f..c05a9e82 100644
--- a/clients/cli/connections.c
+++ b/clients/cli/connections.c
@@ -19,6 +19,8 @@
 
 #include "nm-default.h"
 
+#include "connections.h"
+
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -29,33 +31,35 @@
 #include <readline/readline.h>
 #include <readline/history.h>
 
+#include "nm-utils/nm-hash-utils.h"
+
+#include "nm-client-utils.h"
+#include "nm-vpn-helpers.h"
+#include "nm-meta-setting-access.h"
+#include "nm-secret-agent-simple.h"
+
 #include "utils.h"
 #include "common.h"
 #include "settings.h"
-#include "connections.h"
 #include "devices.h"
-#include "nm-secret-agent-simple.h"
 #include "polkit-agent.h"
-#include "nm-vpn-helpers.h"
+
+typedef enum {
+	PROPERTY_INF_FLAG_NONE                      = 0x0,
+	PROPERTY_INF_FLAG_DISABLED                  = 0x1, /* Don't ask due to runtime decision. */
+	PROPERTY_INF_FLAG_ENABLED                   = 0x2, /* Override NM_META_PROPERTY_INF_FLAG_DONT_ASK due to runtime decision. */
+	PROPERTY_INF_FLAG_ALL                       = 0x3,
+} PropertyInfFlags;
+
+typedef char *(*CompEntryFunc) (const char *, int);
 
 typedef struct _OptionInfo OptionInfo;
 struct _OptionInfo {
-	const char *setting_name;
+	const NMMetaSettingInfoEditor *setting_info;
 	const char *property;
 	const char *option;
-	enum {
-		OPTION_NONE = 0x00,
-		OPTION_REQD = 0x01,	/* Don't ask to ask. */
-		OPTION_DONT_ASK = 0x02,	/* Don't ask interactively by default */
-		OPTION_MULTI = 0x04,	/* Ask multiple times, do an append instead of set. */
-
-		OPTION_DISABLED = 0x10,	/* Don't ask due to runtime decision. */
-		OPTION_ENABLED = 0x20,	/* Override OPTION_DONT_ASK due to runtime decision. */
-	} flags;
-	const char *prompt;
-	const char *def_hint;
-	gboolean (*check_and_set)(NmCli *nmc, NMConnection *connection, OptionInfo *option, const char *value, GError **error);
-	rl_compentry_func_t *generator_func;
+	gboolean (*check_and_set)(NmCli *nmc, NMConnection *connection, const OptionInfo *option, const char *value, GError **error);
+	CompEntryFunc generator_func;
 };
 
 /* define some prompts for connection editor */
@@ -64,59 +68,6 @@ struct _OptionInfo {
 #define EDITOR_PROMPT_CON_TYPE _("Enter connection type: ")
 
 /* define some other prompts */
-#define PROMPT_CON_TYPE    N_("Connection type")
-#define PROMPT_IFNAME      N_("Interface name [*]")
-#define PROMPT_VPN_TYPE    N_("VPN type")
-#define PROMPT_MASTER      N_("Master")
-
-#define PROMPT_IB_MODE     N_("Transport mode")
-#define WORD_DATAGRAM  "datagram"
-#define WORD_CONNECTED "connected"
-#define PROMPT_IB_MODE_CHOICES "(" WORD_DATAGRAM "/" WORD_CONNECTED ") [" WORD_DATAGRAM "]"
-
-#define PROMPT_BT_TYPE N_("Bluetooth type")
-#define WORD_PANU      "panu"
-#define WORD_DUN_GSM   "dun-gsm"
-#define WORD_DUN_CDMA  "dun-cdma"
-#define PROMPT_BT_TYPE_CHOICES "(" WORD_PANU "/" WORD_DUN_GSM "/" WORD_DUN_CDMA ") [" WORD_PANU "]"
-
-#define PROMPT_BOND_MODE N_("Bonding mode")
-
-#define PROMPT_BOND_MON_MODE N_("Bonding monitoring mode")
-#define WORD_MIIMON "miimon"
-#define WORD_ARP    "arp"
-#define PROMPT_BOND_MON_MODE_CHOICES "(" WORD_MIIMON "/" WORD_ARP ") [" WORD_MIIMON "]"
-
-#define PROMPT_ADSL_PROTO N_("Protocol")
-#define PROMPT_ADSL_PROTO_CHOICES "(" NM_SETTING_ADSL_PROTOCOL_PPPOA "/" NM_SETTING_ADSL_PROTOCOL_PPPOE "/" NM_SETTING_ADSL_PROTOCOL_IPOATM ")"
-
-#define PROMPT_WIFI_MODE N_("Wi-Fi mode")
-#define WORD_INFRA  "infrastructure"
-#define WORD_AP     "ap"
-#define WORD_ADHOC  "adhoc"
-#define PROMPT_WIFI_MODE_CHOICES "(" WORD_INFRA "/" WORD_AP "/" WORD_ADHOC ") [" WORD_INFRA "]"
-
-#define PROMPT_ADSL_ENCAP N_("ADSL encapsulation")
-#define PROMPT_ADSL_ENCAP_CHOICES "(" NM_SETTING_ADSL_ENCAPSULATION_VCMUX "/" NM_SETTING_ADSL_ENCAPSULATION_LLC ") [none]"
-
-#define PROMPT_TUN_MODE N_("Tun mode")
-#define WORD_TUN  "tun"
-#define WORD_TAP  "tap"
-#define PROMPT_TUN_MODE_CHOICES "(" WORD_TUN "/" WORD_TAP ") [" WORD_TUN "]"
-
-#define PROMPT_IP_TUNNEL_MODE N_("IP Tunnel mode")
-
-#define PROMPT_MACVLAN_MODE N_("MACVLAN mode")
-
-#define PROMPT_MACSEC_MODE N_("MACsec mode")
-#define WORD_PSK "psk"
-#define WORD_EAP "eap"
-#define PROMPT_MACSEC_MODE_CHOICES "(" WORD_PSK "/" WORD_EAP ")"
-
-#define PROMPT_PROXY_METHOD N_("Proxy method")
-#define WORD_NONE "none"
-#define WORD_AUTO "auto"
-#define PROMPT_PROXY_METHOD_CHOICES "(" WORD_NONE "/" WORD_AUTO ") [" WORD_NONE "]"
 
 #define PROMPT_CONNECTION  _("Connection (name, UUID, or path)")
 #define PROMPT_VPN_CONNECTION  _("VPN connection (name, UUID, or path)")
@@ -125,68 +76,44 @@ struct _OptionInfo {
 
 #define BASE_PROMPT "nmcli> "
 
-/* Available fields for 'connection show' */
-NmcOutputField nmc_fields_con_show[] = {
-	{"NAME",                 N_("NAME")},                  /* 0 */
-	{"UUID",                 N_("UUID")},                  /* 1 */
-	{"TYPE",                 N_("TYPE")},                  /* 2 */
-	{"TIMESTAMP",            N_("TIMESTAMP")},             /* 3 */
-	{"TIMESTAMP-REAL",       N_("TIMESTAMP-REAL")},        /* 4 */
-	{"AUTOCONNECT",          N_("AUTOCONNECT")},           /* 5 */
-	{"AUTOCONNECT-PRIORITY", N_("AUTOCONNECT-PRIORITY")},  /* 6 */
-	{"READONLY",             N_("READONLY")},              /* 7 */
-	{"DBUS-PATH",            N_("DBUS-PATH")},             /* 8 */
-	{"ACTIVE",               N_("ACTIVE")},                /* 9 */
-	{"DEVICE",               N_("DEVICE")},                /* 10 */
-	{"STATE",                N_("STATE")},                 /* 11 */
-	{"ACTIVE-PATH",          N_("ACTIVE-PATH")},           /* 12 */
-	{"SLAVE",                N_("SLAVE")},                 /* 13 */
-	{NULL, NULL}
+/*****************************************************************************/
+
+const NmcMetaGenericInfo *const nmc_fields_con_show[] = {
+	NMC_META_GENERIC ("NAME"),                  /* 0 */
+	NMC_META_GENERIC ("UUID"),                  /* 1 */
+	NMC_META_GENERIC ("TYPE"),                  /* 2 */
+	NMC_META_GENERIC ("TIMESTAMP"),             /* 3 */
+	NMC_META_GENERIC ("TIMESTAMP-REAL"),        /* 4 */
+	NMC_META_GENERIC ("AUTOCONNECT"),           /* 5 */
+	NMC_META_GENERIC ("AUTOCONNECT-PRIORITY"),  /* 6 */
+	NMC_META_GENERIC ("READONLY"),              /* 7 */
+	NMC_META_GENERIC ("DBUS-PATH"),             /* 8 */
+	NMC_META_GENERIC ("ACTIVE"),                /* 9 */
+	NMC_META_GENERIC ("DEVICE"),                /* 10 */
+	NMC_META_GENERIC ("STATE"),                 /* 11 */
+	NMC_META_GENERIC ("ACTIVE-PATH"),           /* 12 */
+	NMC_META_GENERIC ("SLAVE"),                 /* 13 */
+	NULL,
 };
-#define NMC_FIELDS_CON_SHOW_ALL     "NAME,UUID,TYPE,TIMESTAMP,TIMESTAMP-REAL,AUTOCONNECT,AUTOCONNECT-PRIORITY,READONLY,DBUS-PATH,"\
-                                    "ACTIVE,DEVICE,STATE,ACTIVE-PATH,SLAVE"
 #define NMC_FIELDS_CON_SHOW_COMMON  "NAME,UUID,TYPE,DEVICE"
 
-/* Helper macro to define fields */
-#define SETTING_FIELD(setting, props) { setting, N_(setting), 0, props, NULL, FALSE, FALSE, 0 }
-
-/* Available settings for 'connection show <con>' - profile part */
-NmcOutputField nmc_fields_settings_names[] = {
-	SETTING_FIELD (NM_SETTING_CONNECTION_SETTING_NAME,        nmc_fields_setting_connection + 1),        /* 0 */
-	SETTING_FIELD (NM_SETTING_WIRED_SETTING_NAME,             nmc_fields_setting_wired + 1),             /* 1 */
-	SETTING_FIELD (NM_SETTING_802_1X_SETTING_NAME,            nmc_fields_setting_8021X + 1),             /* 2 */
-	SETTING_FIELD (NM_SETTING_WIRELESS_SETTING_NAME,          nmc_fields_setting_wireless + 1),          /* 3 */
-	SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, nmc_fields_setting_wireless_security + 1), /* 4 */
-	SETTING_FIELD (NM_SETTING_IP4_CONFIG_SETTING_NAME,        nmc_fields_setting_ip4_config + 1),        /* 5 */
-	SETTING_FIELD (NM_SETTING_IP6_CONFIG_SETTING_NAME,        nmc_fields_setting_ip6_config + 1),        /* 6 */
-	SETTING_FIELD (NM_SETTING_SERIAL_SETTING_NAME,            nmc_fields_setting_serial + 1),            /* 7 */
-	SETTING_FIELD (NM_SETTING_PPP_SETTING_NAME,               nmc_fields_setting_ppp + 1),               /* 8 */
-	SETTING_FIELD (NM_SETTING_PPPOE_SETTING_NAME,             nmc_fields_setting_pppoe + 1),             /* 9 */
-	SETTING_FIELD (NM_SETTING_GSM_SETTING_NAME,               nmc_fields_setting_gsm + 1),               /* 10 */
-	SETTING_FIELD (NM_SETTING_CDMA_SETTING_NAME,              nmc_fields_setting_cdma + 1),              /* 11 */
-	SETTING_FIELD (NM_SETTING_BLUETOOTH_SETTING_NAME,         nmc_fields_setting_bluetooth + 1),         /* 12 */
-	SETTING_FIELD (NM_SETTING_OLPC_MESH_SETTING_NAME,         nmc_fields_setting_olpc_mesh + 1),         /* 13 */
-	SETTING_FIELD (NM_SETTING_VPN_SETTING_NAME,               nmc_fields_setting_vpn + 1),               /* 14 */
-	SETTING_FIELD (NM_SETTING_WIMAX_SETTING_NAME,             nmc_fields_setting_wimax + 1),             /* 15 */
-	SETTING_FIELD (NM_SETTING_INFINIBAND_SETTING_NAME,        nmc_fields_setting_infiniband + 1),        /* 16 */
-	SETTING_FIELD (NM_SETTING_BOND_SETTING_NAME,              nmc_fields_setting_bond + 1),              /* 17 */
-	SETTING_FIELD (NM_SETTING_VLAN_SETTING_NAME,              nmc_fields_setting_vlan + 1),              /* 18 */
-	SETTING_FIELD (NM_SETTING_ADSL_SETTING_NAME,              nmc_fields_setting_adsl + 1),              /* 19 */
-	SETTING_FIELD (NM_SETTING_BRIDGE_SETTING_NAME,            nmc_fields_setting_bridge + 1),            /* 20 */
-	SETTING_FIELD (NM_SETTING_BRIDGE_PORT_SETTING_NAME,       nmc_fields_setting_bridge_port + 1),       /* 21 */
-	SETTING_FIELD (NM_SETTING_TEAM_SETTING_NAME,              nmc_fields_setting_team + 1),              /* 22 */
-	SETTING_FIELD (NM_SETTING_TEAM_PORT_SETTING_NAME,         nmc_fields_setting_team_port + 1),         /* 23 */
-	SETTING_FIELD (NM_SETTING_DCB_SETTING_NAME,               nmc_fields_setting_dcb + 1),               /* 24 */
-	SETTING_FIELD (NM_SETTING_TUN_SETTING_NAME,               nmc_fields_setting_tun + 1),               /* 25 */
-	SETTING_FIELD (NM_SETTING_IP_TUNNEL_SETTING_NAME,         nmc_fields_setting_ip_tunnel + 1),         /* 26 */
-	SETTING_FIELD (NM_SETTING_MACSEC_SETTING_NAME,            nmc_fields_setting_macsec + 1),            /* 27 */
-	SETTING_FIELD (NM_SETTING_MACVLAN_SETTING_NAME,           nmc_fields_setting_macvlan + 1),           /* 28 */
-	SETTING_FIELD (NM_SETTING_VXLAN_SETTING_NAME,             nmc_fields_setting_vxlan + 1),             /* 29 */
-	SETTING_FIELD (NM_SETTING_PROXY_SETTING_NAME,             nmc_fields_setting_proxy + 1),             /* 30 */
-	SETTING_FIELD (NM_SETTING_DUMMY_SETTING_NAME,             nmc_fields_setting_dummy + 1),             /* 31 */
-	{NULL, NULL, 0, NULL, NULL, FALSE, FALSE, 0}
+const NmcMetaGenericInfo *const nmc_fields_con_active_details_general[] = {
+	NMC_META_GENERIC ("GROUP"),        /* 0 */
+	NMC_META_GENERIC ("NAME"),         /* 1 */
+	NMC_META_GENERIC ("UUID"),         /* 2 */
+	NMC_META_GENERIC ("DEVICES"),      /* 3 */
+	NMC_META_GENERIC ("STATE"),        /* 4 */
+	NMC_META_GENERIC ("DEFAULT"),      /* 5 */
+	NMC_META_GENERIC ("DEFAULT6"),     /* 6 */
+	NMC_META_GENERIC ("SPEC-OBJECT"),  /* 7 */
+	NMC_META_GENERIC ("VPN"),          /* 8 */
+	NMC_META_GENERIC ("DBUS-PATH"),    /* 9 */
+	NMC_META_GENERIC ("CON-PATH"),     /* 10 */
+	NMC_META_GENERIC ("ZONE"),         /* 11 */
+	NMC_META_GENERIC ("MASTER-PATH"),  /* 12 */
+	NULL,
 };
-#define NMC_FIELDS_SETTINGS_NAMES_ALL_X  NM_SETTING_CONNECTION_SETTING_NAME","\
+#define NMC_FIELDS_SETTINGS_NAMES_ALL    NM_SETTING_CONNECTION_SETTING_NAME","\
                                          NM_SETTING_WIRED_SETTING_NAME","\
                                          NM_SETTING_802_1X_SETTING_NAME","\
                                          NM_SETTING_WIRELESS_SETTING_NAME","\
@@ -209,6 +136,10 @@ NmcOutputField nmc_fields_settings_names[] = {
                                          NM_SETTING_BRIDGE_PORT_SETTING_NAME","\
                                          NM_SETTING_TEAM_SETTING_NAME","\
                                          NM_SETTING_TEAM_PORT_SETTING_NAME"," \
+                                         NM_SETTING_OVS_BRIDGE_SETTING_NAME","\
+                                         NM_SETTING_OVS_INTERFACE_SETTING_NAME","\
+                                         NM_SETTING_OVS_PATCH_SETTING_NAME","\
+                                         NM_SETTING_OVS_PORT_SETTING_NAME","\
                                          NM_SETTING_DCB_SETTING_NAME"," \
                                          NM_SETTING_TUN_SETTING_NAME"," \
                                          NM_SETTING_IP_TUNNEL_SETTING_NAME"," \
@@ -216,61 +147,30 @@ NmcOutputField nmc_fields_settings_names[] = {
                                          NM_SETTING_MACVLAN_SETTING_NAME"," \
                                          NM_SETTING_VXLAN_SETTING_NAME"," \
                                          NM_SETTING_PROXY_SETTING_NAME
-#define NMC_FIELDS_SETTINGS_NAMES_ALL    NMC_FIELDS_SETTINGS_NAMES_ALL_X
-
-/* Active connection data */
-/* Available fields for GENERAL group */
-NmcOutputField nmc_fields_con_active_details_general[] = {
-	{"GROUP",         N_("GROUP")},        /* 0 */
-	{"NAME",          N_("NAME")},         /* 1 */
-	{"UUID",          N_("UUID")},         /* 2 */
-	{"DEVICES",       N_("DEVICES")},      /* 3 */
-	{"STATE",         N_("STATE")},        /* 4 */
-	{"DEFAULT",       N_("DEFAULT")},      /* 5 */
-	{"DEFAULT6",      N_("DEFAULT6")},     /* 6 */
-	{"SPEC-OBJECT",   N_("SPEC-OBJECT")},  /* 7 */
-	{"VPN",           N_("VPN")},          /* 8 */
-	{"DBUS-PATH",     N_("DBUS-PATH")},    /* 9 */
-	{"CON-PATH",      N_("CON-PATH")},     /* 10 */
-	{"ZONE",          N_("ZONE")},         /* 11 */
-	{"MASTER-PATH",   N_("MASTER-PATH")},  /* 12 */
-	{NULL, NULL}
-};
-#define NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL  "GROUP,NAME,UUID,DEVICES,STATE,DEFAULT,DEFAULT6,"\
-                                                   "VPN,ZONE,DBUS-PATH,CON-PATH,SPEC-OBJECT,MASTER-PATH"
-
-/* IP group is handled by common.c */
-
-/* Available fields for VPN group */
-NmcOutputField nmc_fields_con_active_details_vpn[] = {
-	{"GROUP",     N_("GROUP")},      /* 0 */
-	{"TYPE",      N_("TYPE")},       /* 1 */
-	{"USERNAME",  N_("USERNAME")},   /* 2 */
-	{"GATEWAY",   N_("GATEWAY")},    /* 3 */
-	{"BANNER",    N_("BANNER")},     /* 4 */
-	{"VPN-STATE", N_("VPN-STATE")},  /* 5 */
-	{"CFG",       N_("CFG")},        /* 6 */
-	{NULL, NULL}
+                                         // NM_SETTING_DUMMY_SETTING_NAME
+                                         // NM_SETTING_WIMAX_SETTING_NAME
+
+
+const NmcMetaGenericInfo *const nmc_fields_con_active_details_vpn[] = {
+	NMC_META_GENERIC ("GROUP"),      /* 0 */
+	NMC_META_GENERIC ("TYPE"),       /* 1 */
+	NMC_META_GENERIC ("USERNAME"),   /* 2 */
+	NMC_META_GENERIC ("GATEWAY"),    /* 3 */
+	NMC_META_GENERIC ("BANNER"),     /* 4 */
+	NMC_META_GENERIC ("VPN-STATE"),  /* 5 */
+	NMC_META_GENERIC ("CFG"),        /* 6 */
+	NULL,
 };
-#define NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL  "GROUP,TYPE,USERNAME,GATEWAY,BANNER,VPN-STATE,CFG"
-
-/* defined in common.c */
-extern NmcOutputField nmc_fields_ip4_config[];
-extern NmcOutputField nmc_fields_ip6_config[];
-extern NmcOutputField nmc_fields_dhcp4_config[];
-extern NmcOutputField nmc_fields_dhcp6_config[];
-
-/* Available fields for 'connection show <con>' - active part */
-NmcOutputField nmc_fields_con_active_details_groups[] = {
-	{"GENERAL",  N_("GENERAL"), 0, nmc_fields_con_active_details_general + 1},  /* 0 */
-	{"IP4",      N_("IP4"),     0, nmc_fields_ip4_config + 1                },  /* 1 */
-	{"DHCP4",    N_("DHCP4"),   0, nmc_fields_dhcp4_config + 1              },  /* 2 */
-	{"IP6",      N_("IP6"),     0, nmc_fields_ip6_config + 1                },  /* 3 */
-	{"DHCP6",    N_("DHCP6"),   0, nmc_fields_dhcp6_config + 1              },  /* 4 */
-	{"VPN",      N_("VPN"),     0, nmc_fields_con_active_details_vpn + 1    },  /* 5 */
-	{NULL, NULL, 0, NULL}
+
+const NmcMetaGenericInfo *const nmc_fields_con_active_details_groups[] = {
+	NMC_META_GENERIC_WITH_NESTED ("GENERAL", nmc_fields_con_active_details_general + 1), /* 0 */
+	NMC_META_GENERIC_WITH_NESTED ("IP4",     metagen_ip4_config),                        /* 1 */
+	NMC_META_GENERIC_WITH_NESTED ("DHCP4",   nmc_fields_dhcp4_config + 1),               /* 2 */
+	NMC_META_GENERIC_WITH_NESTED ("IP6",     nmc_fields_ip6_config + 1),                 /* 3 */
+	NMC_META_GENERIC_WITH_NESTED ("DHCP6",   nmc_fields_dhcp6_config + 1),               /* 4 */
+	NMC_META_GENERIC_WITH_NESTED ("VPN",     nmc_fields_con_active_details_vpn + 1),     /* 5 */
+	NULL,
 };
-#define NMC_FIELDS_CON_ACTIVE_DETAILS_ALL  "GENERAL,IP4,DHCP4,IP6,DHCP6,VPN"
 
 /* Pseudo group names for 'connection show <con>' */
 /* e.g.: nmcli -f profile con show my-eth0 */
@@ -278,9 +178,6 @@ NmcOutputField nmc_fields_con_active_details_groups[] = {
 #define CON_SHOW_DETAIL_GROUP_PROFILE "profile"
 #define CON_SHOW_DETAIL_GROUP_ACTIVE  "active"
 
-/* glib main loop variable - defined in nmcli.c */
-extern GMainLoop *loop;
-
 static guint progress_id = 0;  /* ID of event source for displaying progress */
 
 /* for readline TAB completion in editor */
@@ -293,8 +190,6 @@ typedef struct {
 } TabCompletionInfo;
 static TabCompletionInfo nmc_tab_completion = {NULL, NULL, NULL, NULL};
 
-static char *gen_connection_types (const char *text, int state);
-
 static void
 usage (void)
 {
@@ -412,7 +307,7 @@ usage_connection_add (void)
 	              "                  [parent <ifname>]\n"
 	              "                  [p-key <IPoIB P_Key>]\n\n"
 	              "    bluetooth:    [addr <bluetooth address>]\n"
-	              "                  [bt-type panu|dun-gsm|dun-cdma]\n\n"
+	              "                  [bt-type panu|nap|dun-gsm|dun-cdma]\n\n"
 	              "    vlan:         dev <parent device (connection UUID, ifname, or MAC)>\n"
 	              "                  id <VLAN ID>\n"
 	              "                  [flags <VLAN flags>]\n"
@@ -606,7 +501,6 @@ usage_connection_export (void)
 	              "The data are directed to standard output or to a file if a name is given.\n\n"));
 }
 
-/* quit main loop */
 static void
 quit (void)
 {
@@ -616,23 +510,16 @@ quit (void)
 		nmc_terminal_erase_line ();
 	}
 
-	g_main_loop_quit (loop);  /* quit main loop */
+	g_main_loop_quit (loop);
 }
 
-static const char *
+static char *
 construct_header_name (const char *base, const char *spec)
 {
-	static char header_name[128];
-
 	if (spec == NULL)
-		return base;
+		return g_strdup (base);
 
-	g_strlcpy (header_name, base, sizeof (header_name));
-	g_strlcat (header_name, " (", sizeof (header_name));
-	g_strlcat (header_name, spec, sizeof (header_name));
-	g_strlcat (header_name, ")", sizeof (header_name));
-
-	return header_name;
+	return g_strdup_printf ("%s (%s)", base, spec);
 }
 
 static const char *
@@ -736,12 +623,12 @@ update_secrets_in_connection (NMRemoteConnection *remote, NMConnection *local)
 	int i;
 	GError *error = NULL;
 
-	for (i = 0; nmc_fields_settings_names[i].name; i++) {
-		secrets = nm_remote_connection_get_secrets (remote, nmc_fields_settings_names[i].name, NULL, NULL);
+	for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) {
+		secrets = nm_remote_connection_get_secrets (remote, nm_meta_setting_infos[i].setting_name, NULL, NULL);
 		if (secrets) {
 			if (!nm_connection_update_secrets (local, NULL, secrets, &error) && error) {
 				g_printerr (_("Error updating secrets for %s: %s\n"),
-				            nmc_fields_settings_names[i].name,
+				            nm_meta_setting_infos[i].setting_name,
 				            error->message);
 				g_clear_error (&error);
 			}
@@ -751,7 +638,7 @@ update_secrets_in_connection (NMRemoteConnection *remote, NMConnection *local)
 }
 
 static gboolean
-nmc_connection_profile_details (NMConnection *connection, NmCli *nmc, gboolean secrets)
+nmc_connection_profile_details (NMConnection *connection, NmCli *nmc)
 {
 	GError *error = NULL;
 	GArray *print_settings_array;
@@ -770,7 +657,8 @@ nmc_connection_profile_details (NMConnection *connection, NmCli *nmc, gboolean s
 	else
 		fields_str = nmc->required_fields;
 
-	print_settings_array = parse_output_fields (fields_str, nmc_fields_settings_names, TRUE, &prop_array, &error);
+	print_settings_array = parse_output_fields (fields_str, (const NMMetaAbstractInfo *const*) nm_meta_setting_infos_editor_p (),
+	                                            TRUE, &prop_array, &error);
 	if (error) {
 		g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message);
 		g_error_free (error);
@@ -780,12 +668,24 @@ nmc_connection_profile_details (NMConnection *connection, NmCli *nmc, gboolean s
 	g_assert (print_settings_array);
 
 	/* Main header */
-	nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_connection_get_id (connection));
-	nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTINGS_NAMES_ALL,
-	                                                 nmc_fields_settings_names, FALSE, NULL, NULL);
+	{
+		gs_free char *header_name = NULL;
+		gs_free NmcOutputField *row = NULL;
+		gs_unref_array GArray *out_indices = NULL;
+
+		header_name = construct_header_name (base_hdr, nm_connection_get_id (connection));
+		out_indices = parse_output_fields (NMC_FIELDS_SETTINGS_NAMES_ALL,
+		                                   (const NMMetaAbstractInfo *const*) nm_meta_setting_infos_editor_p (),
+		                                   FALSE, NULL, NULL);
 
-	nmc_fields_settings_names[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY;
-	print_required_fields (nmc, nmc_fields_settings_names);
+		row = g_new0 (NmcOutputField, _NM_META_SETTING_TYPE_NUM + 1);
+		for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++)
+			row[i].info = (const NMMetaAbstractInfo *) &nm_meta_setting_infos_editor[i];
+
+		print_required_fields (&nmc->nmc_config, NMC_OF_FLAG_MAIN_HEADER_ONLY,
+		                       out_indices, header_name,
+		                       0, row);
+	}
 
 	/* Loop through the required settings and print them. */
 	for (i = 0; i < print_settings_array->len; i++) {
@@ -793,19 +693,15 @@ nmc_connection_profile_details (NMConnection *connection, NmCli *nmc, gboolean s
 		int section_idx = g_array_index (print_settings_array, int, i);
 		const char *prop_name = (const char *) g_ptr_array_index (prop_array, i);
 
-		if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output)
+		if (nmc->nmc_config.print_output != NMC_PRINT_TERSE && !nmc->nmc_config.multiline_output && was_output)
 			g_print ("\n"); /* Empty line */
 
 		was_output = FALSE;
 
-		/* Remove any previous data */
-		nmc_empty_output_fields (nmc);
-
-		setting = nm_connection_get_setting_by_name (connection, nmc_fields_settings_names[section_idx].name);
+		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);
 			was_output = TRUE;
-			continue;
 		}
 	}
 
@@ -894,20 +790,45 @@ found:
 }
 
 void
-nmc_active_connection_state_to_color (NMActiveConnectionState state, NmcTermColor *color)
+nmc_active_connection_state_to_color (NMActiveConnectionState state, NMMetaTermColor *color)
 {
-	*color = NMC_TERM_COLOR_NORMAL;
+	*color = NM_META_TERM_COLOR_NORMAL;
 
 	if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING)
-		*color = NMC_TERM_COLOR_YELLOW;
+		*color = NM_META_TERM_COLOR_YELLOW;
 	else if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
-		*color = NMC_TERM_COLOR_GREEN;
+		*color = NM_META_TERM_COLOR_GREEN;
 	else if (state > NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
-		*color = NMC_TERM_COLOR_RED;
+		*color = NM_META_TERM_COLOR_RED;
+}
+
+/* Essentially a version of nm_setting_connection_get_connection_type() that
+ * prefers an alias instead of the settings name when in pretty print mode.
+ * That is so that we print "wifi" instead of "802-11-wireless" in "nmcli c". */
+static const char *
+connection_type_pretty (const char *type, NMCPrintOutput print_output)
+{
+	const NMMetaSettingInfoEditor *editor;
+	int i;
+
+	if (print_output == NMC_PRINT_TERSE)
+		return type;
+
+	for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) {
+		editor = &nm_meta_setting_infos_editor[i];
+		if (strcmp (type, editor->general->setting_name) == 0) {
+			if (editor->alias)
+				return editor->alias;
+			break;
+		}
+	}
+
+	return type;
 }
 
 static void
-fill_output_connection (NMConnection *connection, NmCli *nmc, gboolean active_only)
+fill_output_connection (NMConnection *connection, NMClient *client, NMCPrintOutput print_output,
+                        GPtrArray *output_data, gboolean active_only)
 {
 	NMSettingConnection *s_con;
 	guint64 timestamp;
@@ -921,12 +842,12 @@ fill_output_connection (NMConnection *connection, NmCli *nmc, gboolean active_on
 	const char *ac_state = NULL;
 	NMActiveConnectionState ac_state_int = NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
 	char *ac_dev = NULL;
-	NmcTermColor color;
+	NMMetaTermColor color;
 
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
 
-	ac = get_ac_for_connection (nm_client_get_active_connections (nmc->client), connection);
+	ac = get_ac_for_connection (nm_client_get_active_connections (client), connection);
 	if (active_only && !ac)
 		return;
 
@@ -947,9 +868,7 @@ fill_output_connection (NMConnection *connection, NmCli *nmc, gboolean active_on
 	}
 	prio_str = g_strdup_printf ("%u", nm_setting_connection_get_autoconnect_priority (s_con));
 
-	arr = nmc_dup_fields_array (nmc_fields_con_show,
-	                            sizeof (nmc_fields_con_show),
-	                            0);
+	arr = nmc_dup_fields_array ((const NMMetaAbstractInfo *const*) nmc_fields_con_show, 0);
 
 	/* Show active connections in color */
 	nmc_active_connection_state_to_color (ac_state_int, &color);
@@ -957,7 +876,7 @@ fill_output_connection (NMConnection *connection, NmCli *nmc, gboolean active_on
 
 	set_val_strc (arr, 0, nm_setting_connection_get_id (s_con));
 	set_val_strc (arr, 1, nm_setting_connection_get_uuid (s_con));
-	set_val_strc (arr, 2, nm_setting_connection_get_connection_type (s_con));
+	set_val_strc (arr, 2, connection_type_pretty (nm_setting_connection_get_connection_type (s_con), print_output));
 	set_val_str  (arr, 3, timestamp_str);
 	set_val_str  (arr, 4, timestamp ? timestamp_real_str : g_strdup (_("never")));
 	set_val_strc (arr, 5, nm_setting_connection_get_autoconnect (s_con) ? _("yes") : _("no"));
@@ -970,11 +889,11 @@ fill_output_connection (NMConnection *connection, NmCli *nmc, gboolean active_on
 	set_val_strc (arr, 12, ac_path);
 	set_val_strc (arr, 13, nm_setting_connection_get_slave_type (s_con));
 
-	g_ptr_array_add (nmc->output_data, arr);
+	g_ptr_array_add (output_data, arr);
 }
 
 static void
-fill_output_connection_for_invisible (NMActiveConnection *ac, NmCli *nmc)
+fill_output_connection_for_invisible (NMActiveConnection *ac, NMCPrintOutput print_output, GPtrArray *output_data)
 {
 	NmcOutputField *arr;
 	const char *ac_path = NULL;
@@ -986,13 +905,11 @@ fill_output_connection_for_invisible (NMActiveConnection *ac, NmCli *nmc)
 	ac_state = active_connection_state_to_string (nm_active_connection_get_state (ac));
 	ac_dev = get_ac_device_string (ac);
 
-	arr = nmc_dup_fields_array (nmc_fields_con_show,
-	                            sizeof (nmc_fields_con_show),
-	                            0);
+	arr = nmc_dup_fields_array ((const NMMetaAbstractInfo *const*) nmc_fields_con_show, 0);
 
 	set_val_str  (arr, 0, name);
 	set_val_strc (arr, 1, nm_active_connection_get_uuid (ac));
-	set_val_strc (arr, 2, nm_active_connection_get_connection_type (ac));
+	set_val_strc (arr, 2, connection_type_pretty (nm_active_connection_get_connection_type (ac), print_output));
 	set_val_strc (arr, 3, NULL);
 	set_val_strc (arr, 4, NULL);
 	set_val_strc (arr, 5, NULL);
@@ -1005,14 +922,14 @@ fill_output_connection_for_invisible (NMActiveConnection *ac, NmCli *nmc)
 	set_val_strc (arr, 12, ac_path);
 	set_val_strc (arr, 13, NULL);
 
-	set_val_color_fmt_all (arr, NMC_TERM_FORMAT_DIM);
+	set_val_color_fmt_all (arr, NM_META_TERM_FORMAT_DIM);
 
-	g_ptr_array_add (nmc->output_data, arr);
+	g_ptr_array_add (output_data, arr);
 }
 
 static void
 fill_output_active_connection (NMActiveConnection *active,
-                               NmCli *nmc,
+                               GPtrArray *output_data,
                                gboolean with_group,
                                guint32 o_flags)
 {
@@ -1024,8 +941,8 @@ fill_output_active_connection (NMActiveConnection *active,
 	NMDevice *master;
 	const char *con_path = NULL, *con_zone = NULL;
 	int i;
-	NmcOutputField *tmpl, *arr;
-	size_t tmpl_len;
+	const NMMetaAbstractInfo *const*tmpl;
+	NmcOutputField *arr;
 	int idx_start = with_group ? 0 : 1;
 
 	con = nm_active_connection_get_connection (active);
@@ -1054,17 +971,14 @@ fill_output_active_connection (NMActiveConnection *active,
 	if (dev_str->len > 0)
 		g_string_truncate (dev_str, dev_str->len - 1);  /* Cut off last ',' */
 
-	tmpl = nmc_fields_con_active_details_general;
-	tmpl_len = sizeof (nmc_fields_con_active_details_general);
-	if (!with_group) {
+	tmpl = (const NMMetaAbstractInfo *const*) nmc_fields_con_active_details_general;
+	if (!with_group)
 		tmpl++;
-		tmpl_len -= sizeof (NmcOutputField);
-	}
 
 	/* Fill field values */
-	arr = nmc_dup_fields_array (tmpl, tmpl_len, o_flags);
+	arr = nmc_dup_fields_array (tmpl, o_flags);
 	if (with_group)
-		set_val_strc (arr, 0, nmc_fields_con_active_details_groups[0].name);
+		set_val_strc (arr, 0, nmc_fields_con_active_details_groups[0]->name);
 	set_val_strc (arr, 1-idx_start, nm_active_connection_get_id (active));
 	set_val_strc (arr, 2-idx_start, nm_active_connection_get_uuid (active));
 	set_val_str  (arr, 3-idx_start, dev_str->str);
@@ -1079,7 +993,7 @@ fill_output_active_connection (NMActiveConnection *active,
 	set_val_strc (arr, 12-idx_start, master ? nm_object_get_path (NM_OBJECT (master)) : NULL);
 	set_val_strc (arr, 13-idx_start, s_con ? nm_setting_connection_get_slave_type (s_con) : NULL);
 
-	g_ptr_array_add (nmc->output_data, arr);
+	g_ptr_array_add (output_data, arr);
 
 	g_string_free (dev_str, FALSE);
 }
@@ -1182,22 +1096,19 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
 	GArray *print_groups;
 	GPtrArray *group_fields = NULL;
 	int i;
-	char *fields_str;
-	char *fields_all =    NMC_FIELDS_CON_ACTIVE_DETAILS_ALL;
-	char *fields_common = NMC_FIELDS_CON_ACTIVE_DETAILS_ALL;
-	NmcOutputField *tmpl, *arr;
+	const char *fields_str = NULL;
+	const NMMetaAbstractInfo *const*tmpl;
+	NmcOutputField *arr;
 	size_t tmpl_len;
 	const char *base_hdr = _("Activate connection details");
 	gboolean was_output = FALSE;
 
-	if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
-		fields_str = fields_common;
-	else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
-		fields_str = fields_all;
-	else
+	if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) {
+	} else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) {
+	} else
 		fields_str = nmc->required_fields;
 
-	print_groups = parse_output_fields (fields_str, nmc_fields_con_active_details_groups, TRUE, &group_fields, &error);
+	print_groups = parse_output_fields (fields_str, (const NMMetaAbstractInfo *const*) nmc_fields_con_active_details_groups, TRUE, &group_fields, &error);
 	if (error) {
 		g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message);
 		g_error_free (error);
@@ -1207,83 +1118,95 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
 	g_assert (print_groups);
 
 	/* Main header */
-	nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_active_connection_get_uuid (acon));
-	nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_ACTIVE_DETAILS_ALL,
-	                                                 nmc_fields_con_active_details_groups, FALSE, NULL, NULL);
+	{
+		gs_free char *header_name = NULL;
+		gs_free NmcOutputField *row = NULL;
+		gs_unref_array GArray *out_indices = NULL;
+
+		header_name = construct_header_name (base_hdr, nm_active_connection_get_uuid (acon));
+		out_indices = parse_output_fields (NULL,
+		                                   (const NMMetaAbstractInfo *const*) nmc_fields_con_active_details_groups,
+		                                   FALSE, NULL, NULL);
 
-	nmc_fields_con_active_details_groups[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY;
-	print_required_fields (nmc, nmc_fields_con_active_details_groups);
+		row = g_new0 (NmcOutputField, G_N_ELEMENTS (nmc_fields_con_active_details_groups) + 1);
+		for (i = 0; nmc_fields_con_active_details_groups[i]; i++)
+			row[i].info = (const NMMetaAbstractInfo *) nmc_fields_con_active_details_groups[i];
+
+		print_required_fields (&nmc->nmc_config, NMC_OF_FLAG_MAIN_HEADER_ONLY,
+		                       out_indices, header_name,
+		                       0, row);
+	}
 
 	/* Loop through the groups and print them. */
 	for (i = 0; i < print_groups->len; i++) {
 		int group_idx = g_array_index (print_groups, int, i);
 		char *group_fld = (char *) g_ptr_array_index (group_fields, i);
 
-		if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output)
+		if (nmc->nmc_config.print_output != NMC_PRINT_TERSE && !nmc->nmc_config.multiline_output && was_output)
 			g_print ("\n"); /* Empty line */
 
 		was_output = FALSE;
 
-		/* Remove any previous data */
-		nmc_empty_output_fields (nmc);
-
 		/* GENERAL */
-		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[0].name) == 0) {
+		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx]->name, nmc_fields_con_active_details_groups[0]->name) == 0) {
+			NMC_OUTPUT_DATA_DEFINE_SCOPED (out);
+
 			/* Add field names */
-			tmpl = nmc_fields_con_active_details_general;
+			tmpl = (const NMMetaAbstractInfo *const*) nmc_fields_con_active_details_general;
 			tmpl_len = sizeof (nmc_fields_con_active_details_general);
-			nmc->print_fields.indices = parse_output_fields (group_fld ? group_fld : NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL,
-			                                                 tmpl, FALSE, NULL, NULL);
-			arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES);
-			g_ptr_array_add (nmc->output_data, arr);
+			out_indices = parse_output_fields (group_fld,
+			                                   tmpl, FALSE, NULL, NULL);
+			arr = nmc_dup_fields_array (tmpl, NMC_OF_FLAG_FIELD_NAMES);
+			g_ptr_array_add (out.output_data, arr);
 
 			/* Fill in values */
-			fill_output_active_connection (acon, nmc, TRUE, NMC_OF_FLAG_SECTION_PREFIX);
+			fill_output_active_connection (acon, out.output_data, TRUE, NMC_OF_FLAG_SECTION_PREFIX);
 
-			print_data (nmc);  /* Print all data */
+			print_data_prepare_width (out.output_data);
+			print_data (&nmc->nmc_config, out_indices, NULL, 0, &out);
 
 			was_output = TRUE;
 		}
 
 		/* IP4 */
-		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[1].name) == 0) {
+		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx]->name,  nmc_fields_con_active_details_groups[1]->name) == 0) {
 			gboolean b1 = FALSE;
 			NMIPConfig *cfg4 = nm_active_connection_get_ip4_config (acon);
 
-			b1 = print_ip4_config (cfg4, nmc, "IP4", group_fld);
+			b1 = print_ip4_config (cfg4, &nmc->nmc_config, group_fld);
 			was_output = was_output || b1;
 		}
 
 		/* DHCP4 */
-		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[2].name) == 0) {
+		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx]->name,  nmc_fields_con_active_details_groups[2]->name) == 0) {
 			gboolean b1 = FALSE;
 			NMDhcpConfig *dhcp4 = nm_active_connection_get_dhcp4_config (acon);
 
-			b1 = print_dhcp4_config (dhcp4, nmc, "DHCP4", group_fld);
+			b1 = print_dhcp4_config (dhcp4, &nmc->nmc_config, "DHCP4", group_fld);
 			was_output = was_output || b1;
 		}
 
 		/* IP6 */
-		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[3].name) == 0) {
+		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx]->name,  nmc_fields_con_active_details_groups[3]->name) == 0) {
 			gboolean b1 = FALSE;
 			NMIPConfig *cfg6 = nm_active_connection_get_ip6_config (acon);
 
-			b1 = print_ip6_config (cfg6, nmc, "IP6", group_fld);
+			b1 = print_ip6_config (cfg6, &nmc->nmc_config, "IP6", group_fld);
 			was_output = was_output || b1;
 		}
 
 		/* DHCP6 */
-		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[4].name) == 0) {
+		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx]->name,  nmc_fields_con_active_details_groups[4]->name) == 0) {
 			gboolean b1 = FALSE;
 			NMDhcpConfig *dhcp6 = nm_active_connection_get_dhcp6_config (acon);
 
-			b1 = print_dhcp6_config (dhcp6, nmc, "DHCP6", group_fld);
+			b1 = print_dhcp6_config (dhcp6, &nmc->nmc_config, "DHCP6", group_fld);
 			was_output = was_output || b1;
 		}
 
 		/* VPN */
 		if (NM_IS_VPN_CONNECTION (acon) &&
-		    strcasecmp (nmc_fields_con_active_details_groups[group_idx].name,  nmc_fields_con_active_details_groups[5].name) == 0) {
+		    strcasecmp (nmc_fields_con_active_details_groups[group_idx]->name,  nmc_fields_con_active_details_groups[5]->name) == 0) {
 			NMConnection *con;
 			NMSettingConnection *s_con;
 			NMSettingVpn *s_vpn;
@@ -1293,18 +1216,18 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
 			const char *username = NULL;
 			char **vpn_data_array = NULL;
 			guint32 items_num;
+			NMC_OUTPUT_DATA_DEFINE_SCOPED (out);
 
 			con = NM_CONNECTION (nm_active_connection_get_connection (acon));
 
 			s_con = nm_connection_get_setting_connection (con);
 			g_assert (s_con != NULL);
 
-			tmpl = nmc_fields_con_active_details_vpn;
-			tmpl_len = sizeof (nmc_fields_con_active_details_vpn);
-			nmc->print_fields.indices = parse_output_fields (group_fld ? group_fld : NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL,
-			                                                 tmpl, FALSE, NULL, NULL);
-			arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES);
-			g_ptr_array_add (nmc->output_data, arr);
+			tmpl = (const NMMetaAbstractInfo *const*) nmc_fields_con_active_details_vpn;
+			out_indices = parse_output_fields (group_fld,
+			                                   tmpl, FALSE, NULL, NULL);
+			arr = nmc_dup_fields_array (tmpl, NMC_OF_FLAG_FIELD_NAMES);
+			g_ptr_array_add (out.output_data, arr);
 
 			s_vpn = nm_connection_get_setting_vpn (con);
 			if (s_vpn) {
@@ -1329,17 +1252,18 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
 			vpn_state_str = g_strdup_printf ("%d - %s", vpn_state, vpn_connection_state_to_string (vpn_state));
 
 			/* Add values */
-			arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX);
-			set_val_strc (arr, 0, nmc_fields_con_active_details_groups[5].name);
+			arr = nmc_dup_fields_array (tmpl, NMC_OF_FLAG_SECTION_PREFIX);
+			set_val_strc (arr, 0, nmc_fields_con_active_details_groups[5]->name);
 			set_val_str  (arr, 1, type_str);
 			set_val_strc (arr, 2, username ? username : get_vpn_data_item (con, VPN_DATA_ITEM_USERNAME));
 			set_val_strc (arr, 3, get_vpn_data_item (con, VPN_DATA_ITEM_GATEWAY));
 			set_val_str  (arr, 4, banner_str);
 			set_val_str  (arr, 5, vpn_state_str);
 			set_val_arr  (arr, 6, vpn_data_array);
-			g_ptr_array_add (nmc->output_data, arr);
+			g_ptr_array_add (out.output_data, arr);
 
-			print_data (nmc);  /* Print all data */
+			print_data_prepare_width (out.output_data);
+			print_data (&nmc->nmc_config, out_indices, NULL, 0, &out);
 			was_output = TRUE;
 		}
 	}
@@ -1389,9 +1313,9 @@ split_required_fields_for_con_show (const char *input,
 
 		found = FALSE;
 
-		for (i = 0; nmc_fields_settings_names[i].name; i++) {
+		for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) {
 			if (   is_all || is_common
-			    || !strcasecmp (*iter, nmc_fields_settings_names[i].name)) {
+			    || !strcasecmp (*iter, nm_meta_setting_infos[i].setting_name)) {
 				if (dot)
 					*dot = '.';
 				g_string_append (str1, *iter);
@@ -1402,9 +1326,9 @@ split_required_fields_for_con_show (const char *input,
 		}
 		if (found)
 			continue;
-		for (i = 0; nmc_fields_con_active_details_groups[i].name; i++) {
+		for (i = 0; nmc_fields_con_active_details_groups[i]; i++) {
 			if (   is_all || is_common
-			    || !strcasecmp (*iter, nmc_fields_con_active_details_groups[i].name)) {
+			    || !strcasecmp (*iter, nmc_fields_con_active_details_groups[i]->name)) {
 				if (dot)
 					*dot = '.';
 				g_string_append (str2, *iter);
@@ -1421,8 +1345,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 (nmc_fields_settings_names, -1);
-				char *allowed2 = nmc_get_allowed_fields (nmc_fields_con_active_details_groups, -1);
+				char *allowed1 = nm_meta_abstract_infos_get_names_str ((const NMMetaAbstractInfo *const*) nm_meta_setting_infos_editor_p (), NULL);
+				char *allowed2 = nm_meta_abstract_infos_get_names_str ((const NMMetaAbstractInfo *const*) nmc_fields_con_active_details_groups, NULL);
 				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);
@@ -1775,49 +1699,49 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 
 	if (argc == 0) {
 		const GPtrArray *connections;
-		char *fields_str;
-		char *fields_all =    NMC_FIELDS_CON_SHOW_ALL;
+		const char *fields_str = NULL;
 		char *fields_common = NMC_FIELDS_CON_SHOW_COMMON;
-		NmcOutputField *tmpl, *arr;
-		size_t tmpl_len;
+		const NMMetaAbstractInfo *const*tmpl;
+		NmcOutputField *arr;
+		NMC_OUTPUT_DATA_DEFINE_SCOPED (out);
 
 		if (nmc->complete)
 			goto finish;
 
 		if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
 			fields_str = fields_common;
-		else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
-			fields_str = fields_all;
-		else
+		else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) {
+		} else
 			fields_str = nmc->required_fields;
 
-		tmpl = nmc_fields_con_show;
-		tmpl_len = sizeof (nmc_fields_con_show);
-		nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &err);
+		tmpl = (const NMMetaAbstractInfo *const*) nmc_fields_con_show;
+		out_indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &err);
 		if (err)
 			goto finish;
 
 		/* Add headers */
-		nmc->print_fields.header_name = active_only ? _("NetworkManager active profiles") :
-		                                              _("NetworkManager connection profiles");
-		arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES);
-		g_ptr_array_add (nmc->output_data, arr);
+		arr = nmc_dup_fields_array (tmpl, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES);
+		g_ptr_array_add (out.output_data, arr);
 
 		/* There might be active connections not present in connection list
 		 * (e.g. private connections of a different user). Show them as well. */
 		invisibles = get_invisible_active_connections (nmc);
 		for (i = 0; i < invisibles->len; i++)
-			fill_output_connection_for_invisible (invisibles->pdata[i], nmc);
+			fill_output_connection_for_invisible (invisibles->pdata[i], nmc->nmc_config.print_output, out.output_data);
 		g_ptr_array_free (invisibles, TRUE);
 
 		/* Sort the connections and fill the output data */
 		connections = nm_client_get_connections (nmc->client);
 		sorted_cons = sort_connections (connections, nmc, order);
 		for (i = 0; i < sorted_cons->len; i++)
-			fill_output_connection (sorted_cons->pdata[i], nmc, active_only);
+			fill_output_connection (sorted_cons->pdata[i], nmc->client, nmc->nmc_config.print_output, out.output_data, active_only);
 		g_ptr_array_free (sorted_cons, TRUE);
 
-		print_data (nmc);  /* Print all data */
+		print_data_prepare_width (out.output_data);
+		print_data (&nmc->nmc_config, out_indices,
+		            active_only ? _("NetworkManager active profiles")
+		                        : _("NetworkManager connection profiles"),
+		            0, &out);
 	} else {
 		gboolean new_line = FALSE;
 		gboolean without_fields = (nmc->required_fields == NULL);
@@ -1826,7 +1750,7 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 
 		/* multiline mode is default for 'connection show <ID>' */
 		if (!nmc->mode_specified)
-			nmc->multiline_output = TRUE;
+			nmc->nmc_config_mutable.multiline_output = TRUE;
 
 		/* Split required fields into the settings and active ones. */
 		if (!split_required_fields_for_con_show (nmc->required_fields, &profile_flds, &active_flds, &err))
@@ -1836,7 +1760,7 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 
 		/* Before printing the connections check if we have a "--show-secret"
 		 * option after the connection ids */
-		if (!nmc->show_secrets && !nmc->complete) {
+		if (!nmc->nmc_config.show_secrets && !nmc->complete) {
 			int argc_cp = argc;
 			char **argv_cp = argv;
 
@@ -1886,7 +1810,7 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 				if (acon)
 					con = NM_CONNECTION (nm_active_connection_get_connection (acon));
 			}
-			
+
 			if (!con && !acon) {
 				g_string_printf (nmc->return_text, _("Error: %s - no such connection profile."), *argv);
 				nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
@@ -1920,9 +1844,9 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 			if (without_fields || profile_flds) {
 				if (con) {
 					nmc->required_fields = profile_flds;
-					if (nmc->show_secrets)
+					if (nmc->nmc_config.show_secrets)
 						update_secrets_in_connection (NM_REMOTE_CONNECTION (con), con);
-					res = nmc_connection_profile_details (con, nmc, nmc->show_secrets);
+					res = nmc_connection_profile_details (con, nmc);
 					nmc->required_fields = NULL;
 					if (!res)
 						goto finish;
@@ -1940,7 +1864,7 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 				}
 			}
 			new_line = TRUE;
-			
+
 			/* Take next argument.
 			 * But for pos != NULL we have more connections of the same name,
 			 * so process the same argument again.
@@ -2132,111 +2056,45 @@ typedef struct {
 
 static void activate_connection_info_finish (ActivateConnectionInfo *info);
 
-static const char *
-active_connection_state_reason_to_string (NMActiveConnectionStateReason reason)
-{
-	switch (reason) {
-	case NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN:
-		return _("Unknown reason");
-	case NM_ACTIVE_CONNECTION_STATE_REASON_NONE:
-		return _("The connection was disconnected");
-	case NM_ACTIVE_CONNECTION_STATE_REASON_USER_DISCONNECTED:
-		return _("Disconnected by user");
-	case NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED:
-		return _("The base network connection was interrupted");
-	case NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_STOPPED:
-		return _("The VPN service stopped unexpectedly");
-	case NM_ACTIVE_CONNECTION_STATE_REASON_IP_CONFIG_INVALID:
-		return _("The VPN service returned invalid configuration");
-	case NM_ACTIVE_CONNECTION_STATE_REASON_CONNECT_TIMEOUT:
-		return _("The connection attempt timed out");
-	case NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT:
-		return _("The VPN service did not start in time");
-	case NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_FAILED:
-		return _("The VPN service failed to start");
-	case NM_ACTIVE_CONNECTION_STATE_REASON_NO_SECRETS:
-		return _("No valid secrets");
-	case NM_ACTIVE_CONNECTION_STATE_REASON_LOGIN_FAILED:
-		return _("Invalid secrets");
-	case NM_ACTIVE_CONNECTION_STATE_REASON_CONNECTION_REMOVED:
-		return _("The connection was removed");
-	case NM_ACTIVE_CONNECTION_STATE_REASON_DEPENDENCY_FAILED:
-		return _("Master connection failed");
-	case NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_REALIZE_FAILED:
-		return _("Could not create a software link");
-	case NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_REMOVED:
-		return _("The device disappeared");
-	}
-
-	g_return_val_if_reached (_("Invalid reason"));
-}
-
 static void
 check_activated (ActivateConnectionInfo *info)
 {
-	NmCli *nmc = info->nmc;
-	NMDevice *device = info->device;
-	NMActiveConnection *active = info->active;
 	NMActiveConnectionState ac_state;
-	NMActiveConnectionStateReason ac_reason;
-	NMDeviceState dev_state;
-	NMDeviceStateReason dev_reason;
-
-	ac_state = nm_active_connection_get_state (active);
-	ac_reason = nm_active_connection_get_state_reason (active);
-
-	if (device) {
-		dev_state = nm_device_get_state (device);
-		dev_reason = nm_device_get_state_reason (device);
-	}
+	NmCli *nmc = info->nmc;
+	const char *reason = NULL;
 
-	if (ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
-		if (nmc->print_output == NMC_PRINT_PRETTY)
+	ac_state = nmc_activation_get_effective_state (info->active, info->device, &reason);
+	switch (ac_state) {
+	case NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
+		if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY)
 			nmc_terminal_erase_line ();
-		g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
-		         nm_object_get_path (NM_OBJECT (active)));
-		activate_connection_info_finish (info);
-	} else if (ac_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) {
-		if (device && ac_reason == NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED) {
-			if (dev_state == NM_DEVICE_STATE_FAILED || dev_state == NM_DEVICE_STATE_DISCONNECTED) {
-				g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"),
-				                 nmc_device_reason_to_string (dev_reason));
-				nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
-				activate_connection_info_finish (info);
-			} else {
-				/* Just wait for the device to go failed. We'll get a better error message. */
-				return;
-			}
+		if (reason) {
+			g_print (_("Connection successfully activated (%s) (D-Bus active path: %s)\n"),
+			         reason,
+			         nm_object_get_path (NM_OBJECT (info->active)));
 		} else {
-			g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"),
-			                 active_connection_state_reason_to_string (ac_reason));
-			nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
-			activate_connection_info_finish (info);
+			g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
+			         nm_object_get_path (NM_OBJECT (info->active)));
 		}
-	} else if (ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING) {
-		/* activating master connection does not automatically activate any slaves, so their
-		 * active connection state will not progress beyond ACTIVATING state.
-		 * Monitor the device instead. */
-
+		activate_connection_info_finish (info);
+		break;
+	case NM_ACTIVE_CONNECTION_STATE_DEACTIVATED:
+		nm_assert (reason);
+		g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"),
+		                 reason);
+		nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
+		activate_connection_info_finish (info);
+		break;
+	case NM_ACTIVE_CONNECTION_STATE_ACTIVATING:
 		if (nmc->secret_agent) {
-			NMRemoteConnection *connection = nm_active_connection_get_connection (active);
+			NMRemoteConnection *connection = nm_active_connection_get_connection (info->active);
 
 			nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (nmc->secret_agent),
 			                               nm_connection_get_path (NM_CONNECTION (connection)));
 		}
-
-		if (   device
-		    && (   NM_IS_DEVICE_BOND (device)
-		        || NM_IS_DEVICE_TEAM (device)
-		        || NM_IS_DEVICE_BRIDGE (device))
-	            && dev_state >= NM_DEVICE_STATE_IP_CONFIG
-	            && dev_state <= NM_DEVICE_STATE_ACTIVATED) {
-			if (nmc->print_output == NMC_PRINT_PRETTY)
-				nmc_terminal_erase_line ();
-			g_print (_("Connection successfully activated (master waiting for slaves) (D-Bus active path: %s)\n"),
-			          nm_object_get_path (NM_OBJECT (active)));
-			activate_connection_info_finish (info);
-		}
+		break;
+	default:
+		break;
 	}
 }
 
@@ -2350,7 +2208,7 @@ activate_connection_cb (GObject *client, GAsyncResult *result, gpointer user_dat
 		activate_connection_info_finish (info);
 	} else {
 		state = nm_active_connection_get_state (active);
-		if (!device) {
+		if (!device && !nm_active_connection_get_vpn (active)) {
 			/* device could be NULL for virtual devices. Fill it here. */
 			ac_devs = nm_active_connection_get_devices (active);
 			device = ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL;
@@ -2361,7 +2219,7 @@ activate_connection_cb (GObject *client, GAsyncResult *result, gpointer user_dat
 		if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
 			/* User doesn't want to wait or already activated */
 			if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
-				if (nmc->print_output == NMC_PRINT_PRETTY)
+				if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY)
 					nmc_terminal_erase_line ();
 				g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
 				         nm_object_get_path (NM_OBJECT (active)));
@@ -2380,7 +2238,7 @@ activate_connection_cb (GObject *client, GAsyncResult *result, gpointer user_dat
 			check_activated (info);
 
 			/* Start progress indication showing VPN states */
-			if (nmc->print_output == NMC_PRINT_PRETTY) {
+			if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY) {
 				if (progress_id)
 					g_source_remove (progress_id);
 				progress_id = g_timeout_add (120, progress_active_connection_cb, active);
@@ -2416,7 +2274,7 @@ parse_passwords (const char *passwd_file, GError **error)
 	char *pwd_spec, *pwd, *prop;
 	const char *setting;
 
-	pwds_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+	pwds_hash = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free);
 
 	if (!passwd_file)
 		return pwds_hash;
@@ -2539,10 +2397,6 @@ nmc_activate_connection (NmCli *nmc,
 		                  NM_SECRET_AGENT_SIMPLE_REQUEST_SECRETS,
 		                  G_CALLBACK (nmc_secrets_requested),
 		                  nmc);
-		if (connection) {
-			nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (nmc->secret_agent),
-			                               nm_object_get_path (NM_OBJECT (connection)));
-		}
 	}
 
 	info = g_malloc0 (sizeof (ActivateConnectionInfo));
@@ -2672,7 +2526,7 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 	}
 
 	/* Start progress indication */
-	if (nmc->print_output == NMC_PRINT_PRETTY)
+	if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY)
 		progress_id = g_timeout_add (120, progress_cb, _("preparing"));
 
 	return nmc->return_value;
@@ -2707,7 +2561,7 @@ down_active_connection_state_cb (NMActiveConnection *active,
 	if (nm_active_connection_get_state (active) < NM_ACTIVE_CONNECTION_STATE_DEACTIVATED)
 		return;
 
-	if (info->nmc->print_output == NMC_PRINT_PRETTY)
+	if (info->nmc->nmc_config.print_output == NMC_PRINT_PRETTY)
 		nmc_terminal_erase_line ();
 	g_print (_("Connection '%s' successfully deactivated (D-Bus active path: %s)\n"),
 	         nm_active_connection_get_id (active), nm_object_get_path (NM_OBJECT (active)));
@@ -2896,254 +2750,29 @@ finish:
 	return nmc->return_value;
 }
 
-/*----------------------------------------------------------------------------*/
-
-typedef struct NameItem {
-	const char *name;
-	const char *alias;
-	const struct NameItem *settings;
-	gboolean mandatory;
-} NameItem;
-
-static const NameItem nmc_generic_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_ethernet_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
-	{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, TRUE  },
-	{ NM_SETTING_802_1X_SETTING_NAME,     NULL,       NULL, FALSE },
-	{ NM_SETTING_DCB_SETTING_NAME,        NULL,       NULL, FALSE },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_infiniband_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE  },
-	{ NM_SETTING_INFINIBAND_SETTING_NAME, NULL, NULL, TRUE  },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_wifi_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME,        NULL,       NULL, TRUE  },
-	{ NM_SETTING_WIRELESS_SETTING_NAME,          "wifi",     NULL, TRUE  },
-	{ NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, "wifi-sec", NULL, FALSE },
-	{ NM_SETTING_802_1X_SETTING_NAME,            NULL,       NULL, FALSE },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_wimax_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,   NULL, TRUE  },
-	{ NM_SETTING_WIMAX_SETTING_NAME,      NULL,   NULL, TRUE  },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_gsm_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
-	{ NM_SETTING_GSM_SETTING_NAME,        NULL,       NULL, TRUE  },
-	{ NM_SETTING_SERIAL_SETTING_NAME,     NULL,       NULL, FALSE },
-	{ NM_SETTING_PPP_SETTING_NAME,        NULL,       NULL, FALSE },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_cdma_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
-	{ NM_SETTING_CDMA_SETTING_NAME,       NULL,       NULL, TRUE  },
-	{ NM_SETTING_SERIAL_SETTING_NAME,     NULL,       NULL, FALSE },
-	{ NM_SETTING_PPP_SETTING_NAME,        NULL,       NULL, FALSE },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_bluetooth_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,   NULL, TRUE  },
-	{ NM_SETTING_BLUETOOTH_SETTING_NAME,  NULL,   NULL, TRUE  },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_adsl_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,   NULL, TRUE  },
-	{ NM_SETTING_ADSL_SETTING_NAME,       NULL,   NULL, TRUE  },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-/* PPPoE is a base connection type from historical reasons.
- * See libnm-core/nm-setting.c:_nm_setting_is_base_type()
- */
-static const NameItem nmc_pppoe_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
-	{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, TRUE  },
-	{ NM_SETTING_PPPOE_SETTING_NAME,      NULL,       NULL, TRUE  },
-	{ NM_SETTING_PPP_SETTING_NAME,        NULL,       NULL, FALSE },
-	{ NM_SETTING_802_1X_SETTING_NAME,     NULL,       NULL, FALSE },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_olpc_mesh_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,        NULL, TRUE  },
-	{ NM_SETTING_OLPC_MESH_SETTING_NAME,  "olpc-mesh", NULL, TRUE  },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_vpn_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,   NULL, TRUE  },
-	{ NM_SETTING_VPN_SETTING_NAME,        NULL,   NULL, TRUE  },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_vlan_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
-	{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, FALSE },
-	{ NM_SETTING_VLAN_SETTING_NAME,       NULL,       NULL, TRUE  },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_bond_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
-	{ NM_SETTING_BOND_SETTING_NAME,       NULL,       NULL, TRUE  },
-	{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, FALSE },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_team_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
-	{ NM_SETTING_TEAM_SETTING_NAME,       NULL,       NULL, TRUE  },
-	{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, FALSE },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_bridge_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
-	{ NM_SETTING_BRIDGE_SETTING_NAME,     NULL,       NULL, TRUE  },
-	{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, FALSE },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_bond_slave_settings [] = {
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_team_slave_settings [] = {
-	{ NM_SETTING_TEAM_PORT_SETTING_NAME,  NULL,       NULL, TRUE  },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_bridge_slave_settings [] = {
-	{ NM_SETTING_BRIDGE_PORT_SETTING_NAME, NULL,       NULL, TRUE  },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_no_slave_settings [] = {
-	{ NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL,   NULL, FALSE },
-	{ NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL,   NULL, FALSE },
-	{ NM_SETTING_PROXY_SETTING_NAME,      NULL,   NULL, FALSE },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_tun_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
-	{ NM_SETTING_TUN_SETTING_NAME,        NULL,       NULL, TRUE  },
-	{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, FALSE },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_ip_tunnel_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
-	{ NM_SETTING_IP_TUNNEL_SETTING_NAME,  NULL,       NULL, TRUE  },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_macsec_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
-	{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, FALSE },
-	{ NM_SETTING_802_1X_SETTING_NAME,     NULL,       NULL, FALSE },
-	{ NM_SETTING_MACSEC_SETTING_NAME,     NULL,       NULL, TRUE  },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_macvlan_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME, NULL,       NULL, TRUE  },
-	{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet", NULL, FALSE },
-	{ NM_SETTING_MACVLAN_SETTING_NAME,    NULL,       NULL, TRUE  },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_vxlan_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME,  NULL,       NULL, TRUE  },
-	{ NM_SETTING_VXLAN_SETTING_NAME,       NULL,       NULL, TRUE  },
-	{ NM_SETTING_WIRED_SETTING_NAME,       "ethernet", NULL, FALSE },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-static const NameItem nmc_dummy_settings [] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME,  NULL,       NULL, TRUE  },
-	{ NM_SETTING_DUMMY_SETTING_NAME,       NULL,       NULL, TRUE  },
-	{ NM_SETTING_WIRED_SETTING_NAME,       "ethernet", NULL, FALSE },
-	{ NULL, NULL, NULL, FALSE }
-};
-
-/* Available connection types */
-static const NameItem nmc_valid_connection_types[] = {
-	{ NM_SETTING_GENERIC_SETTING_NAME,    NULL,        nmc_generic_settings      }, /* Needs to be first. */
-	{ NM_SETTING_WIRED_SETTING_NAME,      "ethernet",  nmc_ethernet_settings     },
-	{ NM_SETTING_PPPOE_SETTING_NAME,      NULL,        nmc_pppoe_settings        },
-	{ NM_SETTING_WIRELESS_SETTING_NAME,   "wifi",      nmc_wifi_settings         },
-	{ NM_SETTING_WIMAX_SETTING_NAME,      NULL,        nmc_wimax_settings        },
-	{ NM_SETTING_GSM_SETTING_NAME,        NULL,        nmc_gsm_settings          },
-	{ NM_SETTING_CDMA_SETTING_NAME,       NULL,        nmc_cdma_settings         },
-	{ NM_SETTING_INFINIBAND_SETTING_NAME, NULL,        nmc_infiniband_settings   },
-	{ NM_SETTING_ADSL_SETTING_NAME,       NULL,        nmc_adsl_settings         },
-	{ NM_SETTING_BLUETOOTH_SETTING_NAME,  NULL,        nmc_bluetooth_settings    },
-	{ NM_SETTING_VPN_SETTING_NAME,        NULL,        nmc_vpn_settings          },
-	{ NM_SETTING_OLPC_MESH_SETTING_NAME,  "olpc-mesh", nmc_olpc_mesh_settings    },
-	{ NM_SETTING_VLAN_SETTING_NAME,       NULL,        nmc_vlan_settings         },
-	{ NM_SETTING_BOND_SETTING_NAME,       NULL,        nmc_bond_settings         },
-	{ NM_SETTING_TEAM_SETTING_NAME,       NULL,        nmc_team_settings         },
-	{ NM_SETTING_BRIDGE_SETTING_NAME,     NULL,        nmc_bridge_settings       },
-	{ "bond-slave",                       NULL,        nmc_bond_slave_settings   },
-	{ "team-slave",                       NULL,        nmc_team_slave_settings   },
-	{ "bridge-slave",                     NULL,        nmc_bridge_slave_settings },
-	{ "no-slave",                         NULL,        nmc_no_slave_settings     },
-	{ NM_SETTING_TUN_SETTING_NAME,        NULL,        nmc_tun_settings          },
-	{ NM_SETTING_IP_TUNNEL_SETTING_NAME,  NULL,        nmc_ip_tunnel_settings    },
-	{ NM_SETTING_MACSEC_SETTING_NAME,     NULL,        nmc_macsec_settings       },
-	{ NM_SETTING_MACVLAN_SETTING_NAME,    NULL,        nmc_macvlan_settings      },
-	{ NM_SETTING_VXLAN_SETTING_NAME,      NULL,        nmc_vxlan_settings        },
-	{ NM_SETTING_DUMMY_SETTING_NAME,      NULL,        nmc_dummy_settings        },
-	{ NULL, NULL, NULL }
-};
+/*****************************************************************************/
 
 /*
  * Return the most appropriate name for the connection of a type 'name' possibly with given 'slave_type'
  * if exists, else return the 'name'. The returned string must not be freed.
  */
 static const char *
-get_name_alias (const char *name, const char *slave_type, const NameItem array[])
+get_name_alias_toplevel (const char *name, const char *slave_type)
 {
-	const NameItem *iter = &array[0];
+	const NMMetaSettingInfoEditor *setting_info;
 
 	if (slave_type) {
-		while (iter && iter->name) {
-			if (   g_str_has_prefix (iter->name, slave_type)
-			    && g_str_has_suffix (iter->name, "-slave"))
-				break;
-			iter++;
-		}
-	} else if (name) {
-		while (iter && iter->name) {
-			if (!strcmp (name, iter->name))
-				break;
-			iter++;
-		}
-	} else
-		return NULL;
+		const char *slave_name;
 
-	if (iter) {
-		if (iter->alias)
-			return iter->alias;
-		else
-			return iter->name;
+		if (nm_meta_setting_info_valid_parts_for_slave_type (slave_type, &slave_name))
+			return slave_name ?: name;
+		return name;
 	}
 
+	setting_info = nm_meta_setting_info_editor_find_by_name (name, FALSE);
+	if (setting_info)
+		return setting_info->alias ?: setting_info->general->setting_name;
+
 	return name;
 }
 
@@ -3154,44 +2783,70 @@ get_name_alias (const char *name, const char *slave_type, const NameItem array[]
  * Returns: string; the caller is responsible for freeing it.
  */
 static char *
-get_valid_options_string (const NameItem *array, const NameItem *array_slv)
+get_valid_options_string (const NMMetaSettingValidPartItem *const*array, const NMMetaSettingValidPartItem *const*array_slv)
 {
-	const NameItem *iter = array;
+	const NMMetaSettingValidPartItem *const*iter = array;
 	GString *str;
 	int i;
 
 	str = g_string_sized_new (150);
 
 	for (i = 0; i < 2; i++, iter = array_slv) {
-		while (iter && iter->name) {
+		for (; iter && *iter; iter++) {
+			const NMMetaSettingInfoEditor *setting_info = (*iter)->setting_info;
+
 			if (str->len)
 				g_string_append (str, ", ");
-			if (iter->alias)
-				g_string_append_printf (str, "%s (%s)", iter->name, iter->alias);
+			if (setting_info->alias)
+				g_string_append_printf (str, "%s (%s)", setting_info->general->setting_name, setting_info->alias);
 			else
-				g_string_append (str, iter->name);
-			iter++;
+				g_string_append (str, setting_info->general->setting_name);
 		}
 	}
 	return g_string_free (str, FALSE);
 }
 
-static const NameItem *
+static char *
+get_valid_options_string_toplevel (void)
+{
+	GString *str;
+	int i;
+
+	str = g_string_sized_new (150);
+	for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) {
+		const NMMetaSettingInfoEditor *setting_info = &nm_meta_setting_infos_editor[i];
+
+		if (!setting_info->valid_parts)
+			continue;
+
+		if (str->len)
+			g_string_append (str, ", ");
+		if (setting_info->alias)
+			g_string_append_printf (str, "%s (%s)", setting_info->general->setting_name, setting_info->alias);
+		else
+			g_string_append (str, setting_info->general->setting_name);
+	}
+
+	if (str->len)
+		g_string_append (str, ", ");
+	g_string_append (str, "bond-slave, bridge-slave, team-slave");
+
+	return g_string_free (str, FALSE);
+}
+
+static const NMMetaSettingValidPartItem *const*
 get_valid_settings_array (const char *con_type)
 {
-	guint i, num;
+	const NMMetaSettingInfoEditor *setting_info;
 
 	/* No connection type yet? Return settings for a generic connection
 	 * (just the "connection" setting), which always makes sense. */
 	if (!con_type)
-		return nmc_valid_connection_types[0].settings;
-
-	num = G_N_ELEMENTS (nmc_valid_connection_types);
-	for (i = 0; i < num; i++) {
-		if (nm_streq0 (con_type, nmc_valid_connection_types[i].name))
-			return nmc_valid_connection_types[i].settings;
-	}
+		return nm_meta_setting_info_valid_parts_default;
 
+	setting_info = nm_meta_setting_info_editor_find_by_name (con_type, FALSE);
+	if (setting_info)
+		return setting_info->valid_parts ?: NM_PTRARRAY_EMPTY (const NMMetaSettingValidPartItem *);
 	return NULL;
 }
 
@@ -3210,14 +2865,13 @@ get_valid_settings_array (const char *con_type)
  * Returns: list of property.arg elements
  */
 static char *
-get_valid_properties_string (const NameItem *array,
-                             const NameItem *array_slv,
+get_valid_properties_string (const NMMetaSettingValidPartItem *const*array,
+                             const NMMetaSettingValidPartItem *const*array_slv,
                              char modifier,
                              const char *prefix,
                              const char *postfix)
 {
-	const NameItem *iter = array;
-	const NmcOutputField *field_iter;
+	const NMMetaSettingValidPartItem *const*iter = array;
 	const char *prop_name = NULL;
 	GString *str;
 	int i, j;
@@ -3228,69 +2882,60 @@ get_valid_properties_string (const NameItem *array,
 	str = g_string_sized_new (1024);
 
 	for (i = 0; i < 2; i++, iter = array_slv) {
-		while (!full_match && iter && iter->name) {
-			if (   !(g_str_has_prefix (iter->name, prefix))
-			    && (!(iter->alias) || !g_str_has_prefix (iter->alias, prefix))) {
-				iter++;
+		for(; !full_match && iter && *iter; iter++) {
+			const NMMetaSettingInfoEditor *setting_info = (*iter)->setting_info;
+
+			if (   !(g_str_has_prefix (setting_info->general->setting_name, prefix))
+			    && (   !setting_info->alias
+			        || !g_str_has_prefix (setting_info->alias, prefix))) {
 				continue;
 			}
+
 			/* If postix (so prefix is terminated by a dot), check
 			 * that prefix is not ambiguous */
 			if (postfix) {
 				/* If we have a perfect match, no need to look for others
 				 * prefix and no check on ambiguity should be performed.
 				 * Moreover, erase previous matches from output string */
-				if (   nm_streq (prefix, iter->name)
-				    || nm_streq0 (prefix, iter->alias)) {
+				if (   nm_streq (prefix, setting_info->general->setting_name)
+				    || nm_streq0 (prefix, setting_info->alias)) {
 					g_string_erase (str, 0, -1);
 					full_match = TRUE;
-				} else if (prop_name) {
+				} else if (prop_name)
 					return g_string_free (str, TRUE);
-				}
 				prop_name = prefix;
-			} else {
-				prop_name = iter->name;
-			}
+			} else
+				prop_name = setting_info->general->setting_name;
 
 			/* Search the array with the arguments of the current property */
-			j = 0;
-			while (!nm_streq0 (iter->name, nmc_fields_settings_names[j].name)) {
-				g_assert (nmc_fields_settings_names[j].name);
-				j++;
-			}
-			field_iter = nmc_fields_settings_names[j].group;
-
-			j = 0;
-			while (field_iter[j].name) {
+			for (j = 0; j < setting_info->properties_num; j++) {
 				gchar *new;
-				const char *arg_name = field_iter[j].name;
+				const char *arg_name;
+
+				arg_name = setting_info->properties[j]->property_name;
 
 				/* If required, expand the alias too */
-				if (!postfix && iter->alias) {
+				if (!postfix && setting_info->alias) {
 					if (modifier)
 						g_string_append_c (str, modifier);
 					new = g_strdup_printf ("%s.%s\n",
-							       iter->alias,
-							       arg_name);
+					                       setting_info->alias,
+					                       arg_name);
 					g_string_append (str, new);
 					g_free (new);
 				}
 
-				if (postfix && !g_str_has_prefix (arg_name, postfix)) {
-					j++;
+				if (postfix && !g_str_has_prefix (arg_name, postfix))
 					continue;
-				}
 
 				if (modifier)
 					g_string_append_c (str, modifier);
 				new = g_strdup_printf ("%s.%s\n",
-						       prop_name,
-						       arg_name);
+				                       prop_name,
+				                       arg_name);
 				g_string_append (str, new);
 				g_free (new);
-				j++;
 			}
-			iter++;
 		}
 	}
 	return g_string_free (str, FALSE);
@@ -3306,9 +2951,9 @@ get_valid_properties_string (const NameItem *array,
  * The returned string must not be freed.
  */
 static const char *
-check_valid_name (const char *val, const NameItem *array, const NameItem *array_slv, GError **error)
+check_valid_name (const char *val, const NMMetaSettingValidPartItem *const*array, const NMMetaSettingValidPartItem *const*array_slv, GError **error)
 {
-	const NameItem *iter;
+	const NMMetaSettingValidPartItem *const*iter;
 	gs_unref_ptrarray GPtrArray *tmp_arr = NULL;
 	const char *str;
 	GError *tmp_err = NULL;
@@ -3320,11 +2965,12 @@ check_valid_name (const char *val, const NameItem *array, const NameItem *array_
 	tmp_arr = g_ptr_array_sized_new (32);
 	iter = array;
 	for (i = 0; i < 2; i++, iter = array_slv) {
-		while (iter && iter->name) {
-			g_ptr_array_add (tmp_arr, (gpointer) iter->name);
-			if (iter->alias)
-				g_ptr_array_add (tmp_arr, (gpointer) iter->alias);
-			iter++;
+		for (; iter && *iter; iter++) {
+			const NMMetaSettingInfoEditor *setting_info = (*iter)->setting_info;
+
+			g_ptr_array_add (tmp_arr, (gpointer) setting_info->general->setting_name);
+			if (setting_info->alias)
+				g_ptr_array_add (tmp_arr, (gpointer) setting_info->alias);
 		}
 	}
 	g_ptr_array_add (tmp_arr, (gpointer) NULL);
@@ -3336,10 +2982,10 @@ check_valid_name (const char *val, const NameItem *array, const NameItem *array_
 			g_propagate_error (error, tmp_err);
 		else {
 			/* We want to handle aliases, so construct own error message */
-			char *err_str = get_valid_options_string (array, array_slv);
+			gs_free char *err_str = NULL;
 
+			err_str = get_valid_options_string (array, array_slv);
 			g_set_error (error, 1, 0, _("'%s' not among [%s]"), val, err_str);
-			g_free (err_str);
 			g_clear_error (&tmp_err);
 		}
 		return NULL;
@@ -3348,12 +2994,13 @@ check_valid_name (const char *val, const NameItem *array, const NameItem *array_
 	/* Return a pointer to the found string in passed 'array' */
 	iter = array;
 	for (i = 0; i < 2; i++, iter = array_slv) {
-		while (iter && iter->name) {
-			if (   nm_streq (iter->name, str)
-			    || nm_streq0 (iter->alias, str)) {
-				return iter->name;
+		for (; iter && *iter; iter++) {
+			const NMMetaSettingInfoEditor *setting_info = (*iter)->setting_info;
+
+			if (   nm_streq (setting_info->general->setting_name, str)
+			    || nm_streq0 (setting_info->alias, str)) {
+				return setting_info->general->setting_name;
 			}
-			iter++;
 		}
 	}
 
@@ -3362,44 +3009,98 @@ check_valid_name (const char *val, const NameItem *array, const NameItem *array_
 	return NULL;
 }
 
+static const char *
+check_valid_name_toplevel (const char *val, const char **slave_type, GError **error)
+{
+	gs_unref_ptrarray GPtrArray *tmp_arr = NULL;
+	const char *str;
+	GError *tmp_err = NULL;
+	int i;
+	const NMMetaSettingInfoEditor *setting_info;
+
+	NM_SET_OUT (slave_type, NULL);
+
+	/* Create a temporary array that can be used in nmc_string_is_valid() */
+	tmp_arr = g_ptr_array_sized_new (32);
+	for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) {
+		setting_info = &nm_meta_setting_infos_editor[i];
+		g_ptr_array_add (tmp_arr, (gpointer) setting_info->general->setting_name);
+		if (setting_info->alias)
+			g_ptr_array_add (tmp_arr, (gpointer) setting_info->alias);
+	}
+	g_ptr_array_add (tmp_arr, "bond-slave");
+	g_ptr_array_add (tmp_arr, "bridge-slave");
+	g_ptr_array_add (tmp_arr, "team-slave");
+	g_ptr_array_add (tmp_arr, (gpointer) NULL);
+
+	/* Check string validity */
+	str = nmc_string_is_valid (val, (const char **) tmp_arr->pdata, &tmp_err);
+	if (!str) {
+		if (tmp_err->code == 1)
+			g_propagate_error (error, tmp_err);
+		else {
+			/* We want to handle aliases, so construct own error message */
+			char *err_str = get_valid_options_string_toplevel ();
+
+			g_set_error (error, 1, 0, _("'%s' not among [%s]"), val, err_str);
+			g_free (err_str);
+			g_clear_error (&tmp_err);
+		}
+		return NULL;
+	}
+
+	if (nm_streq (str, "bond-slave")) {
+		NM_SET_OUT (slave_type, NM_SETTING_BOND_SETTING_NAME);
+		return NM_SETTING_WIRED_SETTING_NAME;
+	} else if (nm_streq (str, "bridge-slave")) {
+		NM_SET_OUT (slave_type, NM_SETTING_BRIDGE_SETTING_NAME);
+		return NM_SETTING_WIRED_SETTING_NAME;
+	} else if (nm_streq (str, "team-slave")) {
+		NM_SET_OUT (slave_type, NM_SETTING_TEAM_SETTING_NAME);
+		return NM_SETTING_WIRED_SETTING_NAME;
+	}
+
+	setting_info = nm_meta_setting_info_editor_find_by_name (str, TRUE);
+	if (setting_info)
+		return setting_info->general->setting_name;
+
+	/* We should not really come here */
+	g_set_error (error, 1, 0, _("Unknown error"));
+	return NULL;
+}
+
 static gboolean
 is_setting_mandatory (NMConnection *connection, NMSetting *setting)
 {
 	NMSettingConnection *s_con;
 	const char *c_type;
-	const NameItem *item;
+	const NMMetaSettingValidPartItem *const*item;
 	const char *name;
 	const char *s_type;
-	char *slv_type;
+	guint i;
 
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
 	c_type = nm_setting_connection_get_connection_type (s_con);
+	s_type = nm_setting_connection_get_slave_type (s_con);
 
 	name = nm_setting_get_name (setting);
 
-	item = get_valid_settings_array (c_type);
-	while (item && item->name) {
-		if (!strcmp (name, item->name))
-			return item->mandatory;
-		item++;
-	}
-
-	/* Let's give a try to parameters related to slave type */
-	s_type = nm_setting_connection_get_slave_type (s_con);
-	slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
-	item = get_valid_settings_array (slv_type);
-	g_free (slv_type);
-	while (item && item->name) {
-		if (!strcmp (name, item->name))
-			return item->mandatory;
-		item++;
+	for (i = 0; i < 2; i++) {
+		if (i == 0)
+			item = get_valid_settings_array (c_type);
+		else
+			item = nm_meta_setting_info_valid_parts_for_slave_type (s_type, NULL);
+		for (; item && *item; item++) {
+			if (!strcmp (name, (*item)->setting_info->general->setting_name))
+				return (*item)->mandatory;
+		}
 	}
 
 	return FALSE;
 }
 
-/*----------------------------------------------------------------------------*/
+/*****************************************************************************/
 
 static const char *
 _strip_master_prefix (const char *master, const char *(**func)(NMConnection *))
@@ -3509,8 +3210,6 @@ normalized_master_for_slave (const GPtrArray *connections,
 
 #define WORD_YES "yes"
 #define WORD_NO  "no"
-#define WORD_LOC_YES _("yes")
-#define WORD_LOC_NO  _("no")
 static const char *
 prompt_yes_no (gboolean default_yes, char *delim)
 {
@@ -3520,14 +3219,14 @@ prompt_yes_no (gboolean default_yes, char *delim)
 		delim = "";
 
 	snprintf (prompt, sizeof (prompt), "(%s/%s) [%s]%s ",
-	          WORD_LOC_YES, WORD_LOC_NO,
-	          default_yes ? WORD_LOC_YES : WORD_LOC_NO, delim);
+	          WORD_YES, WORD_NO,
+	          default_yes ? WORD_YES : WORD_NO, delim);
 
 	return prompt;
 }
 
 static NMSetting *
-is_setting_valid (NMConnection *connection, const NameItem *valid_settings_main, const NameItem *valid_settings_slave, char *setting)
+is_setting_valid (NMConnection *connection, const NMMetaSettingValidPartItem *const*valid_settings_main, const NMMetaSettingValidPartItem *const*valid_settings_slave, char *setting)
 {
 	const char *setting_name;
 
@@ -3600,9 +3299,94 @@ set_default_interface_name (NmCli *nmc, NMSettingConnection *s_con)
 	g_free (ifname);
 }
 
-/*----------------------------------------------------------------------------*/
+/*****************************************************************************/
 
-static OptionInfo option_info[];
+static PropertyInfFlags
+_dynamic_options_set (const NMMetaAbstractInfo *abstract_info,
+                      PropertyInfFlags mask, PropertyInfFlags set)
+{
+	static GHashTable *cache = NULL;
+	gpointer p;
+	PropertyInfFlags v, v2;
+
+	if (G_UNLIKELY (!cache))
+		cache = g_hash_table_new (NULL, NULL);
+
+	if (g_hash_table_lookup_extended (cache, (gpointer) abstract_info, NULL, &p))
+		v = GPOINTER_TO_UINT (p);
+	else
+		v = 0;
+
+	v2 = (v & ~mask) | (mask & set);
+	if (v != v2)
+		g_hash_table_insert (cache, (gpointer) abstract_info, GUINT_TO_POINTER (v2));
+
+	return v2;
+}
+
+static PropertyInfFlags
+_dynamic_options_get (const NMMetaAbstractInfo *abstract_info)
+{
+	return _dynamic_options_set (abstract_info, 0, 0);
+}
+
+/*****************************************************************************/
+
+static gboolean
+_meta_property_needs_bond_hack (const NMMetaPropertyInfo *property_info)
+{
+	/* hack: the bond property data is handled special and not generically.
+	 * Eventually, get rid of explicitly checking whether we handle a bond. */
+	if (!property_info)
+		g_return_val_if_reached (FALSE);
+	return    property_info->property_typ_data
+	       && property_info->property_typ_data->nested == &nm_meta_property_typ_data_bond;
+
+}
+
+static char **
+_meta_abstract_complete (const NMMetaAbstractInfo *abstract_info, const char *text)
+{
+	const char *const*values;
+	char **values_to_free = NULL;
+	const NMMetaOperationContext ctx = {
+		.connection = nmc_tab_completion.connection,
+	};
+
+	values = nm_meta_abstract_info_complete (abstract_info,
+	                                         nmc_meta_environment,
+	                                         nmc_meta_environment_arg,
+	                                         &ctx,
+	                                         text,
+	                                         &values_to_free);
+	if (values)
+		return values_to_free ?: g_strdupv ((char **) values);
+	return NULL;
+}
+
+static void
+_meta_abstract_get (const NMMetaAbstractInfo *abstract_info,
+                    const NMMetaSettingInfoEditor **out_setting_info,
+                    const char **out_setting_name,
+                    const char **out_property_name,
+                    const char **out_option,
+                    NMMetaPropertyInfFlags *out_inf_flags,
+                    const char **out_prompt,
+                    const char **out_def_hint)
+{
+	const NMMetaPropertyInfo *info = (const NMMetaPropertyInfo *) abstract_info;
+
+	NM_SET_OUT (out_option, info->property_alias);
+	NM_SET_OUT (out_setting_info, info->setting_info);
+	NM_SET_OUT (out_setting_name, info->setting_info->general->setting_name);
+	NM_SET_OUT (out_property_name, info->property_name);
+	NM_SET_OUT (out_option, info->property_alias);
+	NM_SET_OUT (out_inf_flags, info->inf_flags);
+	NM_SET_OUT (out_prompt, info->prompt);
+	NM_SET_OUT (out_def_hint, info->def_hint);
+}
+
+static const OptionInfo *_meta_abstract_get_option_info (const NMMetaAbstractInfo *abstract_info);
 
 /*
  * Mark options in option_info as relevant.
@@ -3611,17 +3395,34 @@ static OptionInfo option_info[];
 static void
 enable_options (const gchar *setting_name, const gchar *property, const gchar * const *opts)
 {
-	OptionInfo *candidate;
+	const NMMetaPropertyInfo *property_info;
+
+	property_info = nm_meta_property_info_find_by_name (setting_name, property);
+
+	if (!property_info)
+		g_return_if_reached ();
+
+	if (_meta_property_needs_bond_hack (property_info)) {
+		guint i;
+
+		for (i = 0; i < nm_meta_property_typ_data_bond.nested_len; i++) {
+			const NMMetaNestedPropertyInfo *bi = &nm_meta_property_typ_data_bond.nested[i];
 
-	for (candidate = option_info; candidate->setting_name; candidate++) {
-		if (   strcmp (candidate->setting_name, setting_name) == 0
-		    && strcmp (candidate->property, property) == 0
-		    && (candidate->flags & OPTION_DONT_ASK)
-		    && candidate->option
-		    && g_strv_contains (opts, candidate->option)) {
-			candidate->flags |= OPTION_ENABLED;
+			if (   bi->base.inf_flags & NM_META_PROPERTY_INF_FLAG_DONT_ASK
+			    && bi->base.property_alias
+			    && g_strv_contains (opts, bi->base.property_alias))
+				_dynamic_options_set ((const NMMetaAbstractInfo *) bi, PROPERTY_INF_FLAG_ENABLED, PROPERTY_INF_FLAG_ENABLED);
 		}
+		return;
 	}
+
+	if (!property_info->is_cli_option)
+		g_return_if_reached ();
+
+	if (   property_info->inf_flags & NM_META_PROPERTY_INF_FLAG_DONT_ASK
+	    && property_info->property_alias
+	    && g_strv_contains (opts, property_info->property_alias))
+		_dynamic_options_set ((const NMMetaAbstractInfo *) property_info, PROPERTY_INF_FLAG_ENABLED, PROPERTY_INF_FLAG_ENABLED);
 }
 
 /*
@@ -3632,12 +3433,49 @@ enable_options (const gchar *setting_name, const gchar *property, const gchar *
 static void
 disable_options (const gchar *setting_name, const gchar *property)
 {
-	OptionInfo *candidate;
+	const NMMetaPropertyInfo *property_infos_local[2];
+	const NMMetaPropertyInfo *const*property_infos;
+	guint p;
+
+	if (property) {
+		const NMMetaPropertyInfo *pi;
+
+		pi = nm_meta_property_info_find_by_name (setting_name, property);
+		if (!pi)
+			g_return_if_reached ();
+		if (   !_meta_property_needs_bond_hack (pi)
+		    && !pi->is_cli_option)
+			return;
+		property_infos_local[0] = pi;
+		property_infos_local[1] = NULL;
+		property_infos = property_infos_local;
+	} else {
+		const NMMetaSettingInfoEditor *setting_info;
+
+		setting_info = nm_meta_setting_info_editor_find_by_name (setting_name, FALSE);
+		if (!setting_info)
+			g_return_if_reached ();
+		property_infos = setting_info->properties;
+		if (!property_infos)
+			return;
+	}
 
-	for (candidate = option_info; candidate->setting_name; candidate++) {
-		if (   strcmp (candidate->setting_name, setting_name) == 0
-		    && (!property || strcmp (candidate->property, property) == 0))
-		candidate->flags |= OPTION_DISABLED;
+	for (p = 0; property_infos[p]; p++) {
+		const NMMetaPropertyInfo *property_info = property_infos[p];
+
+		if (_meta_property_needs_bond_hack (property_info)) {
+			guint i;
+
+			for (i = 0; i < nm_meta_property_typ_data_bond.nested_len; i++) {
+				const NMMetaNestedPropertyInfo *bi = &nm_meta_property_typ_data_bond.nested[i];
+
+				_dynamic_options_set ((const NMMetaAbstractInfo *) bi, PROPERTY_INF_FLAG_DISABLED, PROPERTY_INF_FLAG_DISABLED);
+			}
+			nm_assert (p == 0 && !property_infos[1]);
+		} else {
+			if (property_info->is_cli_option)
+				_dynamic_options_set ((const NMMetaAbstractInfo *) property_info, PROPERTY_INF_FLAG_DISABLED, PROPERTY_INF_FLAG_DISABLED);
+		}
 	}
 }
 
@@ -3649,11 +3487,31 @@ disable_options (const gchar *setting_name, const gchar *property)
 static void
 reset_options (void)
 {
-	OptionInfo *candidate;
+	NMMetaSettingType s;
+
+	for (s = 0; s < _NM_META_SETTING_TYPE_NUM; s++) {
+		const NMMetaPropertyInfo *const*property_infos;
+		guint p;
+
+		property_infos = nm_meta_setting_infos_editor[s].properties;
+		if (!property_infos)
+			continue;
+		for (p = 0; property_infos[p]; p++) {
+			const NMMetaPropertyInfo *property_info = property_infos[p];
+
+			if (_meta_property_needs_bond_hack (property_info)) {
+				guint i;
+
+				for (i = 0; i < nm_meta_property_typ_data_bond.nested_len; i++) {
+					const NMMetaNestedPropertyInfo *bi = &nm_meta_property_typ_data_bond.nested[i];
 
-	for (candidate = option_info; candidate->setting_name; candidate++) {
-		candidate->flags &= ~OPTION_DISABLED;
-		candidate->flags &= ~OPTION_ENABLED;
+					_dynamic_options_set ((const NMMetaAbstractInfo *) bi, PROPERTY_INF_FLAG_ALL, 0);
+				}
+			} else {
+				if (property_info->is_cli_option)
+					_dynamic_options_set ((const NMMetaAbstractInfo *) property_info, PROPERTY_INF_FLAG_ALL, 0);
+			}
+		}
 	}
 }
 
@@ -3666,17 +3524,12 @@ set_property (NMConnection *connection,
 	NMSetting *setting;
 	GError *local = NULL;
 
+	g_assert (setting_name && setting_name[0]);
+
 	setting = nm_connection_get_setting_by_name (connection, setting_name);
 	if (!setting) {
-		setting = nmc_setting_new_for_name (setting_name);
-		if (!setting) {
-			/* This should really not happen */
-			g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_UNKNOWN,
-			             _("Error: don't know how to create '%s' setting."),
-			             setting_name);
-			return FALSE;
-		}
-		nmc_setting_custom_init (setting);
+		setting = nm_meta_setting_info_editor_new_setting (nm_meta_setting_info_editor_find_by_name (setting_name, FALSE),
+		                                                   NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI);
 		nm_connection_add_setting (connection, setting);
 	}
 
@@ -3735,17 +3588,25 @@ set_property (NMConnection *connection,
 }
 
 static gboolean
-set_option (NmCli *nmc, NMConnection *connection, OptionInfo *option, const gchar *value, GError **error)
+set_option (NmCli *nmc, NMConnection *connection, const NMMetaAbstractInfo *abstract_info, const gchar *value, GError **error)
 {
-	option->flags |= OPTION_DISABLED;
-	if (option->check_and_set) {
+	const char *setting_name, *property_name, *option_name;
+	NMMetaPropertyInfFlags inf_flags;
+	const OptionInfo *option;
+
+	option = _meta_abstract_get_option_info (abstract_info);
+
+	_dynamic_options_set (abstract_info, PROPERTY_INF_FLAG_DISABLED, PROPERTY_INF_FLAG_DISABLED);
+
+	_meta_abstract_get (abstract_info, NULL, &setting_name, &property_name, &option_name, &inf_flags, NULL, NULL);
+	if (option && option->check_and_set) {
 		return option->check_and_set (nmc, connection, option, value, error);
 	} else if (value) {
-		return set_property (connection, option->setting_name, option->property,
-		                     value, option->flags & OPTION_MULTI ? '+' : '\0', error);
-	} else if (option->flags & OPTION_REQD) {
+		return set_property (connection, setting_name, property_name,
+		                     value, inf_flags & NM_META_PROPERTY_INF_FLAG_MULTI ? '+' : '\0', error);
+	} else if (inf_flags & NM_META_PROPERTY_INF_FLAG_REQD) {
 		g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
-	                     _("Error: '%s' is mandatory."), option->option);
+	                     _("Error: '%s' is mandatory."), option_name);
 		return FALSE;
 	}
 
@@ -3757,10 +3618,9 @@ set_option (NmCli *nmc, NMConnection *connection, OptionInfo *option, const gcha
  * and slave type.
  */
 static gboolean
-con_settings (NMConnection *connection, const NameItem **type_settings, const NameItem **slv_settings, GError **error)
+con_settings (NMConnection *connection, const NMMetaSettingValidPartItem *const**type_settings, const NMMetaSettingValidPartItem *const**slv_settings, GError **error)
 {
 	const char *con_type;
-	gs_free char *slv_type = NULL;
 	NMSettingConnection *s_con;
 
 	g_return_val_if_fail (type_settings, FALSE);
@@ -3770,23 +3630,14 @@ con_settings (NMConnection *connection, const NameItem **type_settings, const Na
 	g_assert (s_con);
 
 	con_type = nm_setting_connection_get_slave_type (s_con);
-	if (!con_type)
-		con_type = "no";
-	slv_type = g_strdup_printf ("%s-slave", con_type);
-	if (slv_type) {
-		*slv_settings = get_valid_settings_array (slv_type);
-		if (!*slv_settings) {
-			g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
-			             _("Error: invalid slave type; %s."), slv_type);
-			return FALSE;
-		}
-	} else {
-		*slv_settings = NULL;
+	*slv_settings = nm_meta_setting_info_valid_parts_for_slave_type (con_type, NULL);
+	if (!*slv_settings) {
+		g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
+		             _("Error: invalid slave type; %s."), con_type);
+		return FALSE;
 	}
 
 	con_type = nm_setting_connection_get_connection_type (s_con);
-	if (!con_type)
-		con_type = NM_SETTING_GENERIC_SETTING_NAME;
 	*type_settings = get_valid_settings_array (con_type);
 	if (!*type_settings) {
 		g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
@@ -3802,67 +3653,34 @@ con_settings (NMConnection *connection, const NameItem **type_settings, const Na
  * it's possible that a type is aready set).
  */
 static void
-ensure_settings (NMConnection *connection, const NameItem *item)
+ensure_settings (NMConnection *connection, const NMMetaSettingValidPartItem *const*item)
 {
-	const NameItem *setting_item;
 	NMSetting *setting;
 
-	for (setting_item = item; setting_item->name; setting_item++) {
-		if (!setting_item->mandatory)
+	for (; item && *item; item++) {
+		if (!(*item)->mandatory)
 			continue;
-		if (nm_connection_get_setting_by_name (connection, setting_item->name))
+		if (nm_connection_get_setting_by_name (connection, (*item)->setting_info->general->setting_name))
 			continue;
-		setting = nmc_setting_new_for_name (setting_item->name);
-		if (setting) {
-			nmc_setting_custom_init (setting);
-			nm_connection_add_setting (connection, setting);
-		}
+		setting = nm_meta_setting_info_editor_new_setting ((*item)->setting_info,
+		                                                   NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI);
+		nm_connection_add_setting (connection, setting);
 	}
 }
 
-/*----------------------------------------------------------------------------*/
-
-static char *
-gen_func_slave_type (const char *text, int state)
-{
-	const char *words[] = { "bond", "team", "bridge", NULL };
-	return nmc_rl_gen_func_basic (text, state, words);
-}
-
-static char *
-gen_func_vpn_types (const char *text, int state)
-{
-	gs_strfreev char **plugin_names = NULL;
-
-	plugin_names = nm_vpn_plugin_info_list_get_service_types (nm_vpn_get_plugin_infos (), FALSE, TRUE);
-	return nmc_rl_gen_func_basic (text, state, (const char **) plugin_names);
-}
+/*****************************************************************************/
 
 static char *
 gen_func_bool_values_l10n (const char *text, int state)
 {
-	const char *words[] = { WORD_LOC_YES, WORD_LOC_NO, NULL };
-	return nmc_rl_gen_func_basic (text, state, words);
-}
-
-static char *
-gen_func_wifi_mode (const char *text, int state)
-{
-	const char *words[] = { "infrastructure", "ap", "adhoc", NULL };
-	return nmc_rl_gen_func_basic (text, state, words);
-}
-
-static char *
-gen_func_ib_type (const char *text, int state)
-{
-	const char *words[] = { "datagram", "connected", NULL };
+	const char *words[] = { WORD_YES, WORD_NO, NULL };
 	return nmc_rl_gen_func_basic (text, state, words);
 }
 
 static char *
 gen_func_bt_type (const char *text, int state)
 {
-	const char *words[] = { "panu", "dun-gsm", "dun-cdma", NULL };
+	const char *words[] = { "panu", "nap", "dun-gsm", "dun-cdma", NULL };
 	return nmc_rl_gen_func_basic (text, state, words);
 }
 
@@ -3886,144 +3704,28 @@ gen_func_bond_lacp_rate (const char *text, int state)
 	return nmc_rl_gen_func_basic (text, state, words);
 }
 
-static char *
-gen_func_adsl_proto (const char *text, int state)
-{
-	const char *words[] = { "pppoe", "pppoa", "ipoatm", NULL };
-	return nmc_rl_gen_func_basic (text, state, words);
-}
-
-static char *
-gen_func_adsl_encap (const char *text, int state)
-{
-	const char *words[] = { "vcmux", "llc", NULL };
-	return nmc_rl_gen_func_basic (text, state, words);
-}
-
-static char *
-gen_func_tun_mode (const char *text, int state)
-{
-	const char *words[] = { "tun", "tap", NULL };
-	return nmc_rl_gen_func_basic (text, state, words);
-}
-
-static char *
-gen_func_ip_tunnel_mode (const char *text, int state)
-{
-	gs_free const char **words = NULL;
-
-	words = nm_utils_enum_get_values (nm_ip_tunnel_mode_get_type (),
-	                                  NM_IP_TUNNEL_MODE_UNKNOWN + 1,
-	                                  G_MAXINT);
-	return nmc_rl_gen_func_basic (text, state, words);
-}
-
-static char *
-gen_func_macsec_mode (const char *text, int state)
-{
-	gs_free const char **words = NULL;
-
-	words = nm_utils_enum_get_values (nm_setting_macsec_mode_get_type (),
-	                                  G_MININT,
-	                                  G_MAXINT);
-	return nmc_rl_gen_func_basic (text, state, words);
-}
-
-static char *
-gen_func_macvlan_mode (const char *text, int state)
-{
-	gs_free const char **words = NULL;
-
-	words = nm_utils_enum_get_values (nm_setting_macvlan_mode_get_type(),
-	                                  NM_SETTING_MACVLAN_MODE_UNKNOWN + 1,
-	                                  G_MAXINT);
-	return nmc_rl_gen_func_basic (text, state, words);
-}
-
-static char *
-gen_func_proxy_method (const char *text, int state)
-{
-	gs_free const char **words = NULL;
-
-	words = nm_utils_enum_get_values (nm_setting_proxy_method_get_type (),
-	                                  G_MININT,
-	                                  G_MAXINT);
-	return nmc_rl_gen_func_basic (text, state, words);
-}
-
-static char *
-gen_func_master_ifnames (const char *text, int state)
-{
-	int i;
-	GPtrArray *ifnames;
-	char *ret;
-	NMConnection *con;
-	NMSettingConnection *s_con;
-	const char *con_type, *ifname;
-	const GPtrArray *connections;
-
-	connections = nm_client_get_connections (nm_cli.client);
-
-	/* Disable appending space after completion */
-	rl_completion_append_character = '\0';
-
-	ifnames = g_ptr_array_sized_new (20);
-	for (i = 0; i < connections->len; i++) {
-		con = NM_CONNECTION (connections->pdata[i]);
-		s_con = nm_connection_get_setting_connection (con);
-		g_assert (s_con);
-		con_type = nm_setting_connection_get_connection_type (s_con);
-		if (g_strcmp0 (con_type, nmc_tab_completion.con_type) != 0)
-			continue;
-		ifname = nm_connection_get_interface_name (con);
-		g_ptr_array_add (ifnames, (gpointer) ifname);
-	}
-	g_ptr_array_add (ifnames, (gpointer) NULL);
-
-	ret = nmc_rl_gen_func_basic (text, state, (const char **) ifnames->pdata);
-
-	g_ptr_array_free (ifnames, TRUE);
-	return ret;
-}
-
-
-/*----------------------------------------------------------------------------*/
+/*****************************************************************************/
 
 static gboolean
-set_connection_type (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *value, GError **error)
+set_connection_type (NmCli *nmc, NMConnection *con, const OptionInfo *option, const char *value, GError **error)
 {
-	const NameItem *type_settings, *slv_settings;
+	const NMMetaSettingValidPartItem *const*type_settings;
+	const NMMetaSettingValidPartItem *const*slv_settings;
 	GError *local = NULL;
 	const gchar *master[] = { "master", NULL };
+	const char *slave_type = NULL;
 
-	value = check_valid_name (value, nmc_valid_connection_types, NULL, &local);
+	value = check_valid_name_toplevel (value, &slave_type, &local);
 	if (!value) {
 		g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
-		             _("Error: bad connection type: %s."), local->message);
+		             _("Error: bad connection type: %s"), local->message);
 		g_clear_error (&local);
 		return FALSE;
 	}
 
-	if (g_strcmp0 (value, "bond-slave") == 0) {
-		value = NM_SETTING_WIRED_SETTING_NAME;
-		if (!set_property (con, NM_SETTING_CONNECTION_SETTING_NAME,
-		                   NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME,
-		                   '\0', error)) {
-			return FALSE;
-		}
-		enable_options (NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_MASTER, master);
-	} else if (g_strcmp0 (value, "bridge-slave") == 0) {
-		value = NM_SETTING_WIRED_SETTING_NAME;
-		if (!set_property (con, NM_SETTING_CONNECTION_SETTING_NAME,
-		                   NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME,
-		                   '\0', error)) {
-			return FALSE;
-		}
-		enable_options (NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_MASTER, master);
-	} else if (g_strcmp0 (value, "team-slave") == 0) {
-		value = NM_SETTING_WIRED_SETTING_NAME;
+	if (slave_type) {
 		if (!set_property (con, NM_SETTING_CONNECTION_SETTING_NAME,
-		                   NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME,
+		                   NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
 		                   '\0', error)) {
 			return FALSE;
 		}
@@ -4039,7 +3741,7 @@ set_connection_type (NmCli *nmc, NMConnection *con, OptionInfo *option, const ch
 		                 NM_SETTING_CONNECTION_INTERFACE_NAME);
 	}
 
-	if (!set_property (con, option->setting_name, option->property, value, '\0', error))
+	if (!set_property (con, option->setting_info->general->setting_name, option->property, value, '\0', error))
 		return FALSE;
 
 	if (!con_settings (con, &type_settings, &slv_settings, error))
@@ -4052,7 +3754,7 @@ set_connection_type (NmCli *nmc, NMConnection *con, OptionInfo *option, const ch
 }
 
 static gboolean
-set_connection_iface (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *value, GError **error)
+set_connection_iface (NmCli *nmc, NMConnection *con, const OptionInfo *option, const char *value, GError **error)
 {
 	GError *tmp_error = NULL;
 
@@ -4068,11 +3770,11 @@ set_connection_iface (NmCli *nmc, NMConnection *con, OptionInfo *option, const c
 		}
 	}
 
-	return set_property (con, option->setting_name, option->property, value, '\0', error);
+	return set_property (con, option->setting_info->general->setting_name, option->property, value, '\0', error);
 }
 
 static gboolean
-set_connection_master (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *value, GError **error)
+set_connection_master (NmCli *nmc, NMConnection *con, const OptionInfo *option, const char *value, GError **error)
 {
 	const GPtrArray *connections;
 	NMSettingConnection *s_con;
@@ -4097,11 +3799,11 @@ set_connection_master (NmCli *nmc, NMConnection *con, OptionInfo *option, const
 		return FALSE;
 	}
 
-	return set_property (con, option->setting_name, option->property, value, '\0', error);
+	return set_property (con, option->setting_info->general->setting_name, option->property, value, '\0', error);
 }
 
 static gboolean
-set_bond_option (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *value, GError **error)
+set_bond_option (NmCli *nmc, NMConnection *con, const OptionInfo *option, const char *value, GError **error)
 {
 	NMSettingBond *s_bond;
 	gboolean success;
@@ -4150,7 +3852,7 @@ set_bond_option (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *
 }
 
 static gboolean
-set_bond_monitoring_mode (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *value, GError **error)
+set_bond_monitoring_mode (NmCli *nmc, NMConnection *con, const OptionInfo *option, const char *value, GError **error)
 {
 	NMSettingBond *s_bond;
 	gs_free gchar *monitor_mode = NULL;
@@ -4164,17 +3866,17 @@ set_bond_monitoring_mode (NmCli *nmc, NMConnection *con, OptionInfo *option, con
 		monitor_mode = g_strdup (value);
 		g_strstrip (monitor_mode);
 	} else {
-		monitor_mode = g_strdup (WORD_MIIMON);
+		monitor_mode = g_strdup (NM_META_TEXT_WORD_MIIMON);
 	}
 
-	if (matches (monitor_mode, WORD_MIIMON))
+	if (matches (monitor_mode, NM_META_TEXT_WORD_MIIMON))
 		enable_options (NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS, miimon_opts);
-	else if (matches (monitor_mode, WORD_ARP))
+	else if (matches (monitor_mode, NM_META_TEXT_WORD_ARP))
 		enable_options (NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS, arp_opts);
 	else {
 		g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
 		             _("Error: '%s' is not a valid monitoring mode; use '%s' or '%s'.\n"),
-		             monitor_mode, WORD_MIIMON, WORD_ARP);
+		             monitor_mode, NM_META_TEXT_WORD_MIIMON, NM_META_TEXT_WORD_ARP);
 		return FALSE;
 	}
 
@@ -4182,7 +3884,7 @@ set_bond_monitoring_mode (NmCli *nmc, NMConnection *con, OptionInfo *option, con
 }
 
 static gboolean
-set_bluetooth_type (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *value, GError **error)
+set_bluetooth_type (NmCli *nmc, NMConnection *con, const OptionInfo *option, const char *value, GError **error)
 {
 	NMSetting *setting;
 
@@ -4193,39 +3895,29 @@ set_bluetooth_type (NmCli *nmc, NMConnection *con, OptionInfo *option, const cha
 	if (   !strcmp (value, NM_SETTING_BLUETOOTH_TYPE_DUN)
 	    || !strcmp (value, NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm")) {
 		value = NM_SETTING_BLUETOOTH_TYPE_DUN;
-		setting = nm_setting_gsm_new ();
-		nmc_setting_custom_init (setting);
+		setting = nm_meta_setting_info_editor_new_setting (&nm_meta_setting_infos_editor[NM_META_SETTING_TYPE_GSM],
+		                                                   NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI);
 		nm_connection_add_setting (con, setting);
 	} else if (!strcmp (value, NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma")) {
 		value = NM_SETTING_BLUETOOTH_TYPE_DUN;
 		setting = nm_setting_cdma_new ();
 		nm_connection_add_setting (con, setting);
-	} else if (!strcmp (value, NM_SETTING_BLUETOOTH_TYPE_PANU)) {
+	} else if (!strcmp (value, NM_SETTING_BLUETOOTH_TYPE_PANU) || !strcmp (value, NM_SETTING_BLUETOOTH_TYPE_NAP)) {
 		/* no op */
 	} else {
 		g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
-		             _("Error: 'bt-type': '%s' not valid; use [%s, %s (%s), %s]."),
-		             value, NM_SETTING_BLUETOOTH_TYPE_PANU, NM_SETTING_BLUETOOTH_TYPE_DUN,
-		             NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm", NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma");
+		             _("Error: 'bt-type': '%s' not valid; use [%s, %s, %s (%s), %s]."),
+		             value, NM_SETTING_BLUETOOTH_TYPE_PANU, NM_SETTING_BLUETOOTH_TYPE_NAP,
+		             NM_SETTING_BLUETOOTH_TYPE_DUN, NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm",
+		             NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma");
 		return FALSE;
 	}
 
-	return set_property (con, option->setting_name, option->property, value, '\0', error);
-}
-
-static gboolean
-set_yes_no (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *value, GError **error)
-{
-	if (g_strcmp0 (value, _(WORD_LOC_YES)))
-		value = WORD_YES;
-	if (g_strcmp0 (value, _(WORD_LOC_NO)))
-		value = WORD_NO;
-
-	return set_property (con, option->setting_name, option->property, value, '\0', error);
+	return set_property (con, option->setting_info->general->setting_name, option->property, value, '\0', error);
 }
 
 static gboolean
-set_ip4_address (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *value, GError **error)
+set_ip4_address (NmCli *nmc, NMConnection *con, const OptionInfo *option, const char *value, GError **error)
 {
 	NMSettingIPConfig *s_ip4;
 
@@ -4240,12 +3932,12 @@ set_ip4_address (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *
 		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
 		              NULL);
 	}
-	return set_property (con, option->setting_name, option->property, value,
-	                     option->flags & OPTION_MULTI ? '+' : '\0', error);
+	return set_property (con, option->setting_info->general->setting_name, option->property, value,
+	                     '+', error);
 }
 
 static gboolean
-set_ip6_address (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *value, GError **error)
+set_ip6_address (NmCli *nmc, NMConnection *con, const OptionInfo *option, const char *value, GError **error)
 {
 	NMSettingIPConfig *s_ip6;
 
@@ -4260,187 +3952,97 @@ set_ip6_address (NmCli *nmc, NMConnection *con, OptionInfo *option, const char *
 		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL,
 		              NULL);
 	}
-	return set_property (con, option->setting_name, option->property, value,
-	                     option->flags & OPTION_MULTI ? '+' : '\0', error);
-}
-
-
-/*----------------------------------------------------------------------------*/
-
-static OptionInfo option_info[] = {
-	{ NM_SETTING_CONNECTION_SETTING_NAME,   NM_SETTING_CONNECTION_TYPE,             "type",         OPTION_REQD, PROMPT_CON_TYPE, NULL,
-                                                                                                        set_connection_type, gen_connection_types },
-	{ NM_SETTING_CONNECTION_SETTING_NAME,   NM_SETTING_CONNECTION_ID,               "con-name",     OPTION_DONT_ASK, NULL, NULL, NULL, NULL },
-	{ NM_SETTING_CONNECTION_SETTING_NAME,   NM_SETTING_CONNECTION_AUTOCONNECT,      "autoconnect",  OPTION_DONT_ASK, NULL, NULL, NULL,
-                                                                                                        gen_func_bool_values_l10n },
-	{ NM_SETTING_CONNECTION_SETTING_NAME,   NM_SETTING_CONNECTION_INTERFACE_NAME,   "ifname",       OPTION_REQD, PROMPT_IFNAME, NULL,
-                                                                                                        set_connection_iface, nmc_rl_gen_func_ifnames },
-	{ NM_SETTING_CONNECTION_SETTING_NAME,   NM_SETTING_CONNECTION_MASTER,           "master",       OPTION_DONT_ASK, PROMPT_MASTER, NULL,
-                                                                                                        set_connection_master, gen_func_master_ifnames },
-	{ NM_SETTING_CONNECTION_SETTING_NAME,   NM_SETTING_CONNECTION_SLAVE_TYPE,       "slave-type",   OPTION_DONT_ASK, NULL, NULL, NULL,
-                                                                                                        gen_func_slave_type },
-	{ NM_SETTING_PPPOE_SETTING_NAME,        NM_SETTING_PPPOE_USERNAME,              "username",     OPTION_REQD, N_("PPPoE username"), NULL, NULL, NULL },
-	{ NM_SETTING_PPPOE_SETTING_NAME,        NM_SETTING_PPPOE_PASSWORD,              "password",     OPTION_NONE, N_("Password [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_PPPOE_SETTING_NAME,        NM_SETTING_PPPOE_SERVICE,               "service",      OPTION_NONE, N_("Service [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_WIRED_SETTING_NAME,        NM_SETTING_WIRED_MTU,                   "mtu",          OPTION_NONE, N_("MTU [auto]"), NULL, NULL, NULL },
-	{ NM_SETTING_WIRED_SETTING_NAME,        NM_SETTING_WIRED_MAC_ADDRESS,           "mac",          OPTION_NONE, N_("MAC [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_WIRED_SETTING_NAME,        NM_SETTING_WIRED_CLONED_MAC_ADDRESS,    "cloned-mac",   OPTION_NONE, N_("Cloned MAC [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_INFINIBAND_SETTING_NAME,   NM_SETTING_INFINIBAND_MTU,              "mtu",          OPTION_NONE, N_("MTU [auto]"), NULL, NULL, NULL },
-	{ NM_SETTING_INFINIBAND_SETTING_NAME,   NM_SETTING_INFINIBAND_MAC_ADDRESS,      "mac",          OPTION_NONE, N_("MAC [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_INFINIBAND_SETTING_NAME,   NM_SETTING_INFINIBAND_TRANSPORT_MODE,   "transport-mode", OPTION_NONE, PROMPT_IB_MODE, PROMPT_IB_MODE_CHOICES,
-                                                                                                        NULL, gen_func_ib_type },
-	{ NM_SETTING_INFINIBAND_SETTING_NAME,   NM_SETTING_INFINIBAND_PARENT,           "parent",       OPTION_NONE, N_("Parent interface [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_INFINIBAND_SETTING_NAME,   NM_SETTING_INFINIBAND_P_KEY,            "p-key",        OPTION_NONE, N_("P_KEY [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_WIRELESS_SETTING_NAME,     NM_SETTING_WIRELESS_SSID,               "ssid",         OPTION_REQD, N_("SSID"), NULL, NULL, NULL },
-	{ NM_SETTING_WIRELESS_SETTING_NAME,     NM_SETTING_WIRELESS_MODE,               "mode",         OPTION_NONE, PROMPT_WIFI_MODE, PROMPT_WIFI_MODE_CHOICES,
-                                                                                                        NULL, gen_func_wifi_mode },
-	{ NM_SETTING_WIRELESS_SETTING_NAME,     NM_SETTING_WIRELESS_MTU,                "mtu",          OPTION_NONE, N_("MTU [auto]"), NULL, NULL, NULL },
-	{ NM_SETTING_WIRELESS_SETTING_NAME,     NM_SETTING_WIRELESS_MAC_ADDRESS,        "mac",          OPTION_NONE, N_("MAC [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_WIRELESS_SETTING_NAME,     NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, "cloned-mac",   OPTION_NONE, N_("Cloned MAC [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_WIMAX_SETTING_NAME,        NM_SETTING_WIMAX_NETWORK_NAME,          "nsp",          OPTION_REQD, N_("WiMAX NSP name"), NULL, NULL, NULL },
-	{ NM_SETTING_WIMAX_SETTING_NAME,        NM_SETTING_WIMAX_MAC_ADDRESS,           "mac",          OPTION_NONE, N_("MAC [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_GSM_SETTING_NAME,          NM_SETTING_GSM_APN,                     "apn",          OPTION_REQD, N_("APN"), NULL, NULL, NULL },
-	{ NM_SETTING_GSM_SETTING_NAME,          NM_SETTING_GSM_USERNAME,                "user",         OPTION_NONE, N_("Username [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_GSM_SETTING_NAME,          NM_SETTING_GSM_PASSWORD,                "password",     OPTION_NONE, N_("Password [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_CDMA_SETTING_NAME,         NM_SETTING_CDMA_USERNAME,               "user",         OPTION_NONE, N_("Username [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_CDMA_SETTING_NAME,         NM_SETTING_CDMA_PASSWORD,               "password",     OPTION_NONE, N_("Password [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_BLUETOOTH_SETTING_NAME,    NM_SETTING_BLUETOOTH_BDADDR,            "addr",         OPTION_REQD, N_("Bluetooth device address"), NULL, NULL, NULL },
-	{ NM_SETTING_BLUETOOTH_SETTING_NAME,    NM_SETTING_BLUETOOTH_TYPE,              "bt-type",      OPTION_NONE, PROMPT_BT_TYPE, PROMPT_BT_TYPE_CHOICES,
-                                                                                                        set_bluetooth_type, gen_func_bt_type },
-	{ NM_SETTING_VLAN_SETTING_NAME,         NM_SETTING_VLAN_PARENT,                 "dev",          OPTION_REQD, N_("VLAN parent device or connection UUID"), NULL,
-                                                                                                        NULL, nmc_rl_gen_func_ifnames },
-	{ NM_SETTING_VLAN_SETTING_NAME,         NM_SETTING_VLAN_ID,                     "id",           OPTION_REQD, N_("VLAN ID (<0-4094>)"), NULL, NULL, NULL },
-	{ NM_SETTING_VLAN_SETTING_NAME,         NM_SETTING_VLAN_FLAGS,                  "flags",        OPTION_NONE, N_("VLAN flags (<0-7>) [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_VLAN_SETTING_NAME,         NM_SETTING_VLAN_INGRESS_PRIORITY_MAP,   "ingress",      OPTION_NONE, N_("Ingress priority maps [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_VLAN_SETTING_NAME,         NM_SETTING_VLAN_EGRESS_PRIORITY_MAP,    "egress",       OPTION_NONE, N_("Egress priority maps [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_BOND_SETTING_NAME,         NM_SETTING_BOND_OPTIONS,                "mode",         OPTION_NONE, PROMPT_BOND_MODE, "[balance-rr]",
-                                                                                                        set_bond_option, gen_func_bond_mode },
-	{ NM_SETTING_BOND_SETTING_NAME,         NM_SETTING_BOND_OPTIONS,                "primary",      OPTION_DONT_ASK, N_("Bonding primary interface [none]"),
-                                                                                                        NULL, set_bond_option, nmc_rl_gen_func_ifnames },
-	{ NM_SETTING_BOND_SETTING_NAME,         NM_SETTING_BOND_OPTIONS,                NULL,           OPTION_NONE, N_("Bonding monitoring mode"), PROMPT_BOND_MON_MODE_CHOICES,
-                                                                                                        set_bond_monitoring_mode, gen_func_bond_mon_mode },
-	{ NM_SETTING_BOND_SETTING_NAME,         NM_SETTING_BOND_OPTIONS,                "miimon",       OPTION_DONT_ASK, N_("Bonding miimon [100]"), NULL, set_bond_option, NULL },
-	{ NM_SETTING_BOND_SETTING_NAME,         NM_SETTING_BOND_OPTIONS,                "downdelay",    OPTION_DONT_ASK, N_("Bonding downdelay [0]"), NULL, set_bond_option, NULL },
-	{ NM_SETTING_BOND_SETTING_NAME,         NM_SETTING_BOND_OPTIONS,                "updelay",      OPTION_DONT_ASK, N_("Bonding updelay [0]"), NULL, set_bond_option, NULL },
-	{ NM_SETTING_BOND_SETTING_NAME,         NM_SETTING_BOND_OPTIONS,                "arp-interval", OPTION_DONT_ASK, N_("Bonding arp-interval [0]"), NULL,
-                                                                                                        set_bond_option, NULL },
-	{ NM_SETTING_BOND_SETTING_NAME,         NM_SETTING_BOND_OPTIONS,                "arp-ip-target", OPTION_DONT_ASK, N_("Bonding arp-ip-target [none]"),
-                                                                                                        NULL, set_bond_option, NULL },
-	{ NM_SETTING_BOND_SETTING_NAME,         NM_SETTING_BOND_OPTIONS,                "lacp-rate",    OPTION_DONT_ASK, N_("LACP rate ('slow' or 'fast') [slow]"), NULL,
-                                                                                                        set_bond_option, gen_func_bond_lacp_rate },
-	{ NM_SETTING_TEAM_SETTING_NAME,         NM_SETTING_TEAM_CONFIG,                 "config",       OPTION_NONE, N_("Team JSON configuration [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_TEAM_PORT_SETTING_NAME,    NM_SETTING_TEAM_PORT_CONFIG,            "config",       OPTION_NONE, N_("Team JSON configuration [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_BRIDGE_SETTING_NAME,       NM_SETTING_BRIDGE_STP,                  "stp",          OPTION_NONE, N_("Enable STP [no]"), NULL,
-                                                                                                        set_yes_no, gen_func_bool_values_l10n },
-	{ NM_SETTING_BRIDGE_SETTING_NAME,       NM_SETTING_BRIDGE_PRIORITY,             "priority",     OPTION_NONE, N_("STP priority [32768]"), NULL, NULL, NULL },
-	{ NM_SETTING_BRIDGE_SETTING_NAME,       NM_SETTING_BRIDGE_FORWARD_DELAY,        "forward-delay", OPTION_NONE, N_("Forward delay [15]"), NULL, NULL, NULL },
-	{ NM_SETTING_BRIDGE_SETTING_NAME,       NM_SETTING_BRIDGE_HELLO_TIME,           "hello-time",   OPTION_NONE, N_("Hello time [2]"), NULL, NULL, NULL },
-	{ NM_SETTING_BRIDGE_SETTING_NAME,       NM_SETTING_BRIDGE_MAX_AGE,              "max-age",      OPTION_NONE, N_("Max age [20]"), NULL, NULL, NULL },
-	{ NM_SETTING_BRIDGE_SETTING_NAME,       NM_SETTING_BRIDGE_AGEING_TIME,          "ageing-time",  OPTION_NONE, N_("MAC address ageing time [300]"), NULL, NULL, NULL },
-	{ NM_SETTING_BRIDGE_SETTING_NAME,       NM_SETTING_BRIDGE_MULTICAST_SNOOPING,   "multicast-snooping", OPTION_NONE, N_("Enable IGMP snooping [no]"), NULL,
-                                                                                                        set_yes_no, gen_func_bool_values_l10n },
-	{ NM_SETTING_BRIDGE_SETTING_NAME,       NM_SETTING_BRIDGE_MAC_ADDRESS,          "mac",          OPTION_NONE, N_("MAC [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_BRIDGE_PORT_SETTING_NAME,  NM_SETTING_BRIDGE_PORT_PRIORITY,        "priority",     OPTION_NONE, N_("Bridge port priority [32]"), NULL, NULL, NULL },
-	{ NM_SETTING_BRIDGE_PORT_SETTING_NAME,  NM_SETTING_BRIDGE_PORT_PATH_COST,       "path-cost",    OPTION_NONE, N_("Bridge port STP path cost [100]"), NULL, NULL, NULL },
-	{ NM_SETTING_BRIDGE_PORT_SETTING_NAME,  NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE,    "hairpin",      OPTION_NONE, N_("Hairpin [no]"), NULL,
-                                                                                                        set_yes_no, gen_func_bool_values_l10n },
-	{ NM_SETTING_VPN_SETTING_NAME,          NM_SETTING_VPN_SERVICE_TYPE,            "vpn-type",     OPTION_REQD, PROMPT_VPN_TYPE, NULL, NULL, gen_func_vpn_types },
-	{ NM_SETTING_VPN_SETTING_NAME,          NM_SETTING_VPN_USER_NAME,               "user",         OPTION_NONE, N_("Username [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_OLPC_MESH_SETTING_NAME,    NM_SETTING_OLPC_MESH_SSID,              "ssid",         OPTION_REQD, N_("SSID"), NULL, NULL, NULL },
-	{ NM_SETTING_OLPC_MESH_SETTING_NAME,    NM_SETTING_OLPC_MESH_CHANNEL,           "channel",      OPTION_NONE, N_("OLPC Mesh channel [1]"), NULL, NULL, NULL },
-	{ NM_SETTING_OLPC_MESH_SETTING_NAME,    NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, "dhcp-anycast", OPTION_NONE, N_("DHCP anycast MAC address [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_ADSL_SETTING_NAME,         NM_SETTING_ADSL_USERNAME,               "username",     OPTION_REQD, N_("Username"), NULL, NULL, NULL },
-	{ NM_SETTING_ADSL_SETTING_NAME,         NM_SETTING_ADSL_PROTOCOL,               "protocol",     OPTION_REQD, PROMPT_ADSL_PROTO, PROMPT_ADSL_PROTO_CHOICES,
-                                                                                                        NULL, gen_func_adsl_proto },
-	{ NM_SETTING_ADSL_SETTING_NAME,         NM_SETTING_ADSL_PASSWORD,               "password",     OPTION_NONE, N_("Password [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_ADSL_SETTING_NAME,         NM_SETTING_ADSL_ENCAPSULATION,          "encapsulation", OPTION_NONE, PROMPT_ADSL_ENCAP, PROMPT_ADSL_ENCAP_CHOICES,
-                                                                                                        NULL, gen_func_adsl_encap },
-	{ NM_SETTING_MACSEC_SETTING_NAME,       NM_SETTING_MACSEC_PARENT,               "dev",          OPTION_REQD, N_("MACsec parent device or connection UUID"), NULL, NULL, NULL },
-	{ NM_SETTING_MACSEC_SETTING_NAME,       NM_SETTING_MACSEC_MODE,                 "mode",         OPTION_REQD, PROMPT_MACSEC_MODE, PROMPT_MACSEC_MODE_CHOICES, NULL, gen_func_macsec_mode },
-	{ NM_SETTING_MACSEC_SETTING_NAME,       NM_SETTING_MACSEC_ENCRYPT,              "encrypt",      OPTION_NONE, N_("Enable encryption [yes]"), NULL, set_yes_no, gen_func_bool_values_l10n },
-	{ NM_SETTING_MACSEC_SETTING_NAME,       NM_SETTING_MACSEC_MKA_CAK,              "cak",          OPTION_NONE, N_("MKA CAK"), NULL, NULL, NULL },
-	{ NM_SETTING_MACSEC_SETTING_NAME,       NM_SETTING_MACSEC_MKA_CKN,              "ckn",          OPTION_NONE, N_("MKA_CKN"), NULL, NULL, NULL },
-	{ NM_SETTING_MACSEC_SETTING_NAME,       NM_SETTING_MACSEC_PORT,                 "port",         OPTION_NONE, N_("SCI port [1]"), NULL, NULL, NULL },
-
-	{ NM_SETTING_MACVLAN_SETTING_NAME,      NM_SETTING_MACVLAN_PARENT,              "dev",          OPTION_REQD, N_("MACVLAN parent device or connection UUID"), NULL,
-                                                                                                        NULL, nmc_rl_gen_func_ifnames },
-	{ NM_SETTING_MACVLAN_SETTING_NAME,      NM_SETTING_MACVLAN_MODE,                "mode",         OPTION_REQD, PROMPT_MACVLAN_MODE, NULL,
-                                                                                                        NULL, gen_func_macvlan_mode },
-	{ NM_SETTING_MACVLAN_SETTING_NAME,      NM_SETTING_MACVLAN_TAP,                 "tap",          OPTION_NONE, N_("Tap [no]"), NULL,
-                                                                                                        set_yes_no, gen_func_bool_values_l10n },
-	{ NM_SETTING_VXLAN_SETTING_NAME,        NM_SETTING_VXLAN_ID,                    "id",           OPTION_REQD, N_("VXLAN ID"), NULL, NULL, NULL },
-	{ NM_SETTING_VXLAN_SETTING_NAME,        NM_SETTING_VXLAN_REMOTE,                "remote",       OPTION_REQD, N_("Remote"), NULL, NULL, NULL },
-	{ NM_SETTING_VXLAN_SETTING_NAME,        NM_SETTING_VXLAN_PARENT,                "dev",          OPTION_NONE, N_("Parent device [none]"), NULL,
-                                                                                                        NULL, nmc_rl_gen_func_ifnames },
-	{ NM_SETTING_VXLAN_SETTING_NAME,        NM_SETTING_VXLAN_LOCAL,                 "local",        OPTION_NONE, N_("Local address [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_VXLAN_SETTING_NAME,        NM_SETTING_VXLAN_SOURCE_PORT_MIN,       "source-port-min", OPTION_NONE, N_("Minimum source port [0]"), NULL, NULL, NULL },
-	{ NM_SETTING_VXLAN_SETTING_NAME,        NM_SETTING_VXLAN_SOURCE_PORT_MAX,       "source-port-max", OPTION_NONE, N_("Maximum source port [0]"), NULL, NULL, NULL },
-	{ NM_SETTING_VXLAN_SETTING_NAME,        NM_SETTING_VXLAN_DESTINATION_PORT,      "destination-port", OPTION_NONE, N_("Destination port [8472]"), NULL, NULL, NULL },
-	{ NM_SETTING_TUN_SETTING_NAME,          NM_SETTING_TUN_MODE,                    "mode",         OPTION_NONE, PROMPT_TUN_MODE, PROMPT_TUN_MODE_CHOICES,
-                                                                                                        NULL, gen_func_tun_mode },
-	{ NM_SETTING_TUN_SETTING_NAME,          NM_SETTING_TUN_OWNER,                   "owner",        OPTION_NONE, N_("User ID [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_TUN_SETTING_NAME,          NM_SETTING_TUN_GROUP,                   "group",        OPTION_NONE, N_("Group ID [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_TUN_SETTING_NAME,          NM_SETTING_TUN_PI,                      "pi",           OPTION_NONE, N_("Enable PI [no]"), NULL,
-                                                                                                        set_yes_no, gen_func_bool_values_l10n },
-	{ NM_SETTING_TUN_SETTING_NAME,          NM_SETTING_TUN_VNET_HDR,                "vnet-hdr",     OPTION_NONE, N_("Enable VNET header [no]"), NULL,
-                                                                                                        set_yes_no, gen_func_bool_values_l10n },
-	{ NM_SETTING_TUN_SETTING_NAME,          NM_SETTING_TUN_MULTI_QUEUE,             "multi-queue",  OPTION_NONE, N_("Enable multi queue [no]"), NULL,
-                                                                                                        set_yes_no, gen_func_bool_values_l10n },
-	{ NM_SETTING_IP_TUNNEL_SETTING_NAME,    NM_SETTING_IP_TUNNEL_MODE,              "mode",         OPTION_REQD, PROMPT_IP_TUNNEL_MODE, NULL, NULL, gen_func_ip_tunnel_mode },
-	{ NM_SETTING_IP_TUNNEL_SETTING_NAME,    NM_SETTING_IP_TUNNEL_LOCAL,             "local",        OPTION_NONE, N_("Local endpoint [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_IP_TUNNEL_SETTING_NAME,    NM_SETTING_IP_TUNNEL_REMOTE,            "remote",       OPTION_REQD, N_("Remote"), NULL, NULL, NULL },
-	{ NM_SETTING_IP_TUNNEL_SETTING_NAME,    NM_SETTING_IP_TUNNEL_PARENT,            "dev",          OPTION_NONE, N_("Parent device [none]"), NULL,
-                                                                                                        NULL, nmc_rl_gen_func_ifnames },
-	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,   NM_SETTING_IP_CONFIG_ADDRESSES,         "ip4",          OPTION_MULTI, N_("IPv4 address (IP[/plen]) [none]"), NULL,
-	                                                                                                set_ip4_address, NULL },
-	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,   NM_SETTING_IP_CONFIG_GATEWAY,           "gw4",          OPTION_NONE, N_("IPv4 gateway [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,   NM_SETTING_IP_CONFIG_ADDRESSES,         "ip6",          OPTION_MULTI, N_("IPv6 address (IP[/plen]) [none]"), NULL,
-	                                                                                                set_ip6_address, NULL },
-	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,   NM_SETTING_IP_CONFIG_GATEWAY,           "gw6",          OPTION_NONE, N_("IPv6 gateway [none]"), NULL, NULL, NULL },
-	{ NM_SETTING_PROXY_SETTING_NAME,        NM_SETTING_PROXY_METHOD,                "method",       OPTION_NONE, PROMPT_PROXY_METHOD, PROMPT_PROXY_METHOD_CHOICES, NULL, gen_func_proxy_method },
-	{ NM_SETTING_PROXY_SETTING_NAME,        NM_SETTING_PROXY_BROWSER_ONLY,          "browser-only", OPTION_NONE, N_("Browser only [no]"), NULL,
-                                                                                                        set_yes_no, gen_func_bool_values_l10n },
-	{ NM_SETTING_PROXY_SETTING_NAME,        NM_SETTING_PROXY_PAC_URL,               "pac-url",      OPTION_NONE, N_("PAC URL"), NULL, NULL, NULL },
-	{ NM_SETTING_PROXY_SETTING_NAME,        NM_SETTING_PROXY_PAC_SCRIPT,            "pac-script",   OPTION_NONE, N_("PAC script"), NULL, NULL, NULL },
-	{ NULL, NULL, NULL, OPTION_NONE, NULL, NULL, NULL, NULL },
-};
+	return set_property (con, option->setting_info->general->setting_name, option->property, value,
+	                     '+', error);
+}
+
+/*****************************************************************************/
+
+static const OptionInfo *
+_meta_abstract_get_option_info (const NMMetaAbstractInfo *abstract_info)
+{
+	static const OptionInfo option_info[] = {
+#define OPTION_INFO(name, property_name_, property_alias_, check_and_set_, generator_func_) \
+		{ \
+			.setting_info =        &nm_meta_setting_infos_editor[NM_META_SETTING_TYPE_##name], \
+			.property =            property_name_, \
+			.option =              property_alias_, \
+			.check_and_set =       check_and_set_, \
+			.generator_func =      generator_func_, \
+		}
+		OPTION_INFO (CONNECTION,   NM_SETTING_CONNECTION_TYPE,                  "type",               set_connection_type,       NULL),
+		OPTION_INFO (CONNECTION,   NM_SETTING_CONNECTION_INTERFACE_NAME,        "ifname",             set_connection_iface,      NULL),
+		OPTION_INFO (CONNECTION,   NM_SETTING_CONNECTION_MASTER,                "master",             set_connection_master,     NULL),
+		OPTION_INFO (BLUETOOTH,    NM_SETTING_BLUETOOTH_TYPE,                   "bt-type",            set_bluetooth_type,        gen_func_bt_type),
+		OPTION_INFO (BOND,         NM_SETTING_BOND_OPTIONS,                     "mode",               set_bond_option,           gen_func_bond_mode),
+		OPTION_INFO (BOND,         NM_SETTING_BOND_OPTIONS,                     "primary",            set_bond_option,           nmc_rl_gen_func_ifnames),
+		OPTION_INFO (BOND,         NM_SETTING_BOND_OPTIONS,                     NULL,                 set_bond_monitoring_mode,  gen_func_bond_mon_mode),
+		OPTION_INFO (BOND,         NM_SETTING_BOND_OPTIONS,                     "miimon",             set_bond_option,           NULL),
+		OPTION_INFO (BOND,         NM_SETTING_BOND_OPTIONS,                     "downdelay",          set_bond_option,           NULL),
+		OPTION_INFO (BOND,         NM_SETTING_BOND_OPTIONS,                     "updelay",            set_bond_option,           NULL),
+		OPTION_INFO (BOND,         NM_SETTING_BOND_OPTIONS,                     "arp-interval",       set_bond_option,           NULL),
+		OPTION_INFO (BOND,         NM_SETTING_BOND_OPTIONS,                     "arp-ip-target",      set_bond_option,           NULL),
+		OPTION_INFO (BOND,         NM_SETTING_BOND_OPTIONS,                     "lacp-rate",          set_bond_option,           gen_func_bond_lacp_rate),
+		OPTION_INFO (IP4_CONFIG,   NM_SETTING_IP_CONFIG_ADDRESSES,              "ip4",                set_ip4_address,           NULL),
+		OPTION_INFO (IP6_CONFIG,   NM_SETTING_IP_CONFIG_ADDRESSES,              "ip6",                set_ip6_address,           NULL),
+		{ 0 },
+	};
+	const char *property_name, *option;
+	const NMMetaSettingInfoEditor *setting_info;
+	const OptionInfo *candidate;
+
+	_meta_abstract_get (abstract_info, &setting_info, NULL, &property_name, &option, NULL, NULL, NULL);
+
+	for (candidate = option_info; candidate->setting_info; candidate++) {
+		if (   candidate->setting_info == setting_info
+		    && nm_streq0 (candidate->property, property_name)
+		    && nm_streq0 (candidate->option, option)) {
+			return candidate;
+		}
+	}
+	return NULL;
+}
 
 static gboolean
-option_relevant (NMConnection *connection, OptionInfo *option)
+option_relevant (NMConnection *connection, const NMMetaAbstractInfo *abstract_info)
 {
-	if (option->flags & OPTION_DONT_ASK && !(option->flags & OPTION_ENABLED))
+	const char *setting_name;
+	NMMetaPropertyInfFlags inf_flags;
+
+	_meta_abstract_get (abstract_info, NULL, &setting_name, NULL, NULL, &inf_flags, NULL, NULL);
+
+	if (   (inf_flags & NM_META_PROPERTY_INF_FLAG_DONT_ASK)
+	    && !(_dynamic_options_get (abstract_info) & PROPERTY_INF_FLAG_ENABLED))
 		return FALSE;
-	if (option->flags & OPTION_DISABLED)
+	if (_dynamic_options_get (abstract_info) & PROPERTY_INF_FLAG_DISABLED)
 		return FALSE;
-	if (!nm_connection_get_setting_by_name (connection, option->setting_name))
+	if (!nm_connection_get_setting_by_name (connection, setting_name))
 		return FALSE;
 	return TRUE;
 }
 
-/*----------------------------------------------------------------------------*/
+/*****************************************************************************/
 
 static void
 complete_property_name (NmCli *nmc, NMConnection *connection,
-			 char modifier,
-			 const gchar *prefix,
-			 const gchar *postfix)
+                        char modifier,
+                        const gchar *prefix,
+                        const gchar *postfix)
 {
 	NMSettingConnection *s_con;
-	const NameItem *valid_settings_main = NULL;
-	const NameItem *valid_settings_slave = NULL;
+	const NMMetaSettingValidPartItem *const*valid_settings_main;
+	const NMMetaSettingValidPartItem *const*valid_settings_slave;
 	const char *connection_type = NULL;
 	const char *slave_type = NULL;
-	gs_free char *slv_type = NULL;
 	gs_free char *word_list = NULL;
-	OptionInfo *candidate;
+	NMMetaSettingType s;
 
 	connection_type = nm_connection_get_connection_type (connection);
 	s_con = nm_connection_get_setting_connection (connection);
 	if (s_con)
 		slave_type = nm_setting_connection_get_slave_type (s_con);
-	slv_type = g_strdup_printf ("%s-slave", slave_type ? slave_type : "no");
 	valid_settings_main = get_valid_settings_array (connection_type);
-	valid_settings_slave = get_valid_settings_array (slv_type);
+	valid_settings_slave = nm_meta_setting_info_valid_parts_for_slave_type (slave_type, NULL);
 
 	word_list = get_valid_properties_string (valid_settings_main, valid_settings_slave, modifier, prefix, postfix);
 	if (word_list)
@@ -4449,14 +4051,39 @@ complete_property_name (NmCli *nmc, NMConnection *connection,
 	if (modifier != '\0')
 		return;
 
-	for (candidate = option_info; candidate->setting_name; candidate++) {
-		if (!nm_connection_get_setting_by_name (connection, candidate->setting_name))
-			continue;
-		if (!candidate->option)
+	for (s = 0; s < _NM_META_SETTING_TYPE_NUM; s++) {
+		const NMMetaPropertyInfo *const*property_infos;
+		guint p;
+
+		if (!nm_connection_get_setting_by_name (connection, nm_meta_setting_infos_editor[s].general->setting_name))
 			continue;
-		if (!g_str_has_prefix (candidate->option, prefix))
+
+		property_infos = nm_meta_setting_infos_editor[s].properties;
+		if (!property_infos)
 			continue;
-		g_print ("%s\n", candidate->option);
+		for (p = 0; property_infos[p]; p++) {
+			const NMMetaPropertyInfo *property_info = property_infos[p];
+
+			if (_meta_property_needs_bond_hack (property_info)) {
+				guint i;
+
+				for (i = 0; i < nm_meta_property_typ_data_bond.nested_len; i++) {
+					const NMMetaNestedPropertyInfo *bi = &nm_meta_property_typ_data_bond.nested[i];
+
+					if (   !bi->base.property_alias
+					    || !g_str_has_prefix (bi->base.property_alias, prefix))
+						continue;
+					g_print ("%s\n", bi->base.property_alias);
+				}
+			} else {
+				if (!property_info->is_cli_option)
+					continue;
+				if (   !property_info->property_alias
+				    || !g_str_has_prefix (property_info->property_alias, prefix))
+					continue;
+				g_print ("%s\n", property_info->property_alias);
+			}
+		}
 	}
 }
 
@@ -4474,62 +4101,54 @@ run_rl_generator (rl_compentry_func_t *generator_func, const char *prefix)
 	}
 }
 
-static void
-complete_option (OptionInfo *option, const gchar *prefix)
+static gboolean
+complete_option (const NMMetaAbstractInfo *abstract_info, const gchar *prefix, NMConnection *context_connection)
 {
-	if (option->generator_func)
-		run_rl_generator (option->generator_func, prefix);
+	const OptionInfo *candidate;
+	const char *const*values;
+	gs_strfreev char **values_to_free = NULL;
+	const NMMetaOperationContext ctx = {
+		.connection = context_connection,
+	};
+
+	values = nm_meta_abstract_info_complete (abstract_info,
+	                                         nmc_meta_environment,
+	                                         nmc_meta_environment_arg,
+	                                         &ctx,
+	                                         prefix,
+	                                         &values_to_free);
+	if (values) {
+		for (; values[0]; values++)
+			g_print ("%s\n", values[0]);
+		return TRUE;
+	}
+
+	candidate = _meta_abstract_get_option_info (abstract_info);
+	if (candidate && candidate->generator_func) {
+		run_rl_generator (candidate->generator_func, prefix);
+		return TRUE;
+	}
+
+	return FALSE;
 }
 
 static void
-complete_property (const gchar *setting_name, const gchar *property, const gchar *prefix)
-{
-	if (strcmp (setting_name, NM_SETTING_CONNECTION_SETTING_NAME) == 0) {
-		if (strcmp (property, NM_SETTING_CONNECTION_TYPE) == 0)
-			run_rl_generator (gen_connection_types, prefix);
-		else if (strcmp (property, NM_SETTING_CONNECTION_MASTER) == 0)
-			run_rl_generator (gen_func_master_ifnames, prefix);
-		else if (strcmp (property, NM_SETTING_CONNECTION_INTERFACE_NAME) == 0)
-			run_rl_generator (nmc_rl_gen_func_ifnames, prefix);
-	} else if (   strcmp (setting_name, NM_SETTING_VPN_SETTING_NAME) == 0
-	           && strcmp (property, NM_SETTING_VPN_SERVICE_TYPE) == 0)
-		run_rl_generator (gen_func_vpn_types, prefix);
-	else if (   strcmp (setting_name, NM_SETTING_WIRELESS_SETTING_NAME) == 0
-	         && strcmp (property, NM_SETTING_WIRELESS_MODE) == 0)
-		run_rl_generator (gen_func_wifi_mode, prefix);
-	else if (   strcmp (setting_name, NM_SETTING_INFINIBAND_SETTING_NAME) == 0
-	         && strcmp (property, NM_SETTING_INFINIBAND_TRANSPORT_MODE) == 0)
-		run_rl_generator (gen_func_ib_type, prefix);
-	else if (   strcmp (setting_name, NM_SETTING_BLUETOOTH_SETTING_NAME) == 0
+complete_property (const gchar *setting_name, const gchar *property, const gchar *prefix, NMConnection *connection)
+{
+	const NMMetaPropertyInfo *property_info;
+
+	property_info = nm_meta_property_info_find_by_name (setting_name, property);
+	if (property_info) {
+		if (complete_option ((const NMMetaAbstractInfo *) property_info, prefix, connection))
+			return;
+	}
+
+	if (   strcmp (setting_name, NM_SETTING_BLUETOOTH_SETTING_NAME) == 0
 	         && strcmp (property, NM_SETTING_BLUETOOTH_TYPE) == 0)
 		run_rl_generator (gen_func_bt_type, prefix);
-	else if (strcmp (setting_name, NM_SETTING_ADSL_SETTING_NAME) == 0) {
-		if (strcmp (property, NM_SETTING_ADSL_PROTOCOL) == 0)
-			run_rl_generator (gen_func_adsl_proto, prefix);
-		else if (strcmp (property, NM_SETTING_ADSL_ENCAPSULATION) == 0)
-			run_rl_generator (gen_func_adsl_encap, prefix);
-	} else if (   strcmp (setting_name, NM_SETTING_TUN_SETTING_NAME) == 0
-	           && strcmp (property, NM_SETTING_TUN_MODE) == 0)
-		run_rl_generator (gen_func_tun_mode, prefix);
-	else if (strcmp (setting_name, NM_SETTING_IP_TUNNEL_SETTING_NAME) == 0) {
-		if (strcmp (property, NM_SETTING_IP_TUNNEL_MODE) == 0)
-			run_rl_generator (gen_func_ip_tunnel_mode, prefix);
-		else if (strcmp (property, NM_SETTING_IP_TUNNEL_PARENT) == 0)
-			run_rl_generator (nmc_rl_gen_func_ifnames, prefix);
-	} else if (strcmp (setting_name, NM_SETTING_MACVLAN_SETTING_NAME) == 0) {
-		if (strcmp (property, NM_SETTING_MACVLAN_MODE) == 0)
-			run_rl_generator (gen_func_macvlan_mode, prefix);
-		else if (strcmp (property, NM_SETTING_MACVLAN_PARENT) == 0)
-			run_rl_generator (nmc_rl_gen_func_ifnames, prefix);
-		else if (strcmp (property, NM_SETTING_MACVLAN_TAP) == 0)
-			run_rl_generator (gen_func_bool_values_l10n, prefix);
-	} else if (   strcmp (setting_name, NM_SETTING_VXLAN_SETTING_NAME) == 0
-	           && strcmp (property, NM_SETTING_VXLAN_PARENT) == 0)
-		run_rl_generator (nmc_rl_gen_func_ifnames, prefix);
-
-}
-
-/*----------------------------------------------------------------------------*/
+}
+
+/*****************************************************************************/
 
 static gboolean
 get_value (const char **value, int *argc, char ***argv, const char *option, GError **error)
@@ -4567,10 +4186,10 @@ nmc_read_connection_properties (NmCli *nmc,
 	 */
 	/* Go through arguments and set properties */
 	do {
-		OptionInfo *candidate;
-		OptionInfo *chosen = NULL;
+		const NMMetaAbstractInfo *chosen = NULL;
 		gs_strfreev gchar **strv = NULL;
-		const NameItem *type_settings, *slv_settings;
+		const NMMetaSettingValidPartItem *const*type_settings;
+		const NMMetaSettingValidPartItem *const*slv_settings;
 		char modifier = '\0';
 
 		if (!con_settings (connection, &type_settings, &slv_settings, error))
@@ -4617,25 +4236,66 @@ nmc_read_connection_properties (NmCli *nmc,
 				return FALSE;
 
 			if (!*argc && nmc->complete)
-				complete_property (setting, strv[1], value ? value : "");
+				complete_property (setting, strv[1], value ? value : "", connection);
 
 			if (!set_property (connection, setting_name, strv[1], value, modifier, error))
 				return FALSE;
 		} else {
+			NMMetaSettingType s;
+			const char *chosen_setting_name = NULL;
+			const char *chosen_option = NULL;
+
 			/* Let's see if this is an property alias (such as "id", "mode", "type" or "con-name")*/
-			for (candidate = option_info; candidate->setting_name; candidate++) {
-				if (g_strcmp0 (candidate->option, option))
+			for (s = 0; s < _NM_META_SETTING_TYPE_NUM; s++) {
+				const NMMetaPropertyInfo *const*property_infos;
+				guint p;
+
+				if (!check_valid_name (nm_meta_setting_infos[s].setting_name,
+				                       type_settings, slv_settings, NULL))
 					continue;
-				if (!check_valid_name (candidate->setting_name, type_settings, slv_settings, NULL))
+
+				property_infos = nm_meta_setting_infos_editor[s].properties;
+				if (!property_infos)
 					continue;
-				if (chosen) {
-					g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
-						     _("Error: '%s' is ambiguous (%s.%s or %s.%s)."), option,
-						     chosen->setting_name, chosen->property,
-						     candidate->setting_name, candidate->property);
-					return FALSE;
+				for (p = 0; property_infos[p]; p++) {
+					const NMMetaPropertyInfo *property_info = property_infos[p];
+
+					if (_meta_property_needs_bond_hack (property_info)) {
+						guint i;
+
+						for (i = 0; i < nm_meta_property_typ_data_bond.nested_len; i++) {
+							const NMMetaNestedPropertyInfo *bi = &nm_meta_property_typ_data_bond.nested[i];
+
+							if (!nm_streq0 (bi->base.property_alias, option))
+								continue;
+							if (chosen) {
+								g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
+								             _("Error: '%s' is ambiguous (%s.%s or %s.%s)."), option,
+								             chosen_setting_name, chosen_option,
+								             nm_meta_setting_infos[s].setting_name, option);
+								return FALSE;
+							}
+							chosen_setting_name = nm_meta_setting_infos[s].setting_name;
+							chosen_option = option;
+							chosen = (const NMMetaAbstractInfo *) bi;
+						}
+					} else {
+						if (!property_info->is_cli_option)
+							continue;
+						if (!nm_streq0 (property_info->property_alias, option))
+							continue;
+						if (chosen) {
+							g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
+							             _("Error: '%s' is ambiguous (%s.%s or %s.%s)."), option,
+							             chosen_setting_name, chosen_option,
+							             nm_meta_setting_infos[s].setting_name, option);
+							return FALSE;
+						}
+						chosen_setting_name = nm_meta_setting_infos[s].setting_name;
+						chosen_option = option;
+						chosen = (const NMMetaAbstractInfo *) property_info;
+					}
 				}
-				chosen = candidate;
 			}
 
 			if (!chosen) {
@@ -4644,7 +4304,7 @@ nmc_read_connection_properties (NmCli *nmc,
 				if (*argc == 1 && nmc->complete)
 					complete_property_name (nmc, connection, modifier, option, NULL);
 				g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
-					     _("Error: invalid <setting>.<property> '%s'."), option);
+				             _("Error: invalid <setting>.<property> '%s'."), option);
 				return FALSE;
 			}
 
@@ -4657,7 +4317,7 @@ nmc_read_connection_properties (NmCli *nmc,
 				return FALSE;
 
 			if (!*argc && nmc->complete)
-				complete_option (chosen, value ? value : "");
+				complete_option (chosen, value ? value : "", connection);
 
 			if (!set_option (nmc, connection, chosen, value, error))
 				return FALSE;
@@ -4682,6 +4342,8 @@ add_connection_cb (GObject *client,
 	NmCli *nmc = info->nmc;
 	NMRemoteConnection *connection;
 	GError *error = NULL;
+	const GPtrArray *connections;
+	guint i, found;
 
 	connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
 	if (error) {
@@ -4691,6 +4353,28 @@ add_connection_cb (GObject *client,
 		g_error_free (error);
 		nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
 	} else {
+		connections = nm_client_get_connections (nmc->client);
+		if (connections) {
+			found = 0;
+			for (i = 0; i < connections->len; i++) {
+				NMConnection *candidate = NM_CONNECTION (connections->pdata[i]);
+
+				if ((NMConnection *) connection == candidate)
+					continue;
+				if (nm_streq0 (nm_connection_get_id (candidate), info->con_name))
+					found++;
+			}
+			if (found > 0) {
+				g_printerr (g_dngettext (GETTEXT_PACKAGE,
+				                         "Warning: There is another connection with the name '%1$s'. Reference the connection by its uuid '%2$s'\n",
+				                         "Warning: There are %3$u other connections with the name '%1$s'. Reference the connection by its uuid '%2$s'\n",
+				                         found),
+				            info->con_name,
+				            nm_connection_get_uuid (NM_CONNECTION (connection)),
+				            found);
+			}
+		}
+
 		g_print (_("Connection '%s' (%s) successfully added.\n"),
 		         nm_connection_get_id (NM_CONNECTION (connection)),
 		         nm_connection_get_uuid (NM_CONNECTION (connection)));
@@ -4741,6 +4425,7 @@ is_single_word (const char* line)
 static char **
 nmcli_con_add_tab_completion (const char *text, int start, int end)
 {
+	NMMetaSettingType s;
 	char **match_array = NULL;
 	rl_compentry_func_t *generator_func = NULL;
 	gs_free char *no = g_strdup_printf ("[%s]: ", gettext ("no"));
@@ -4755,38 +4440,48 @@ nmcli_con_add_tab_completion (const char *text, int start, int end)
 	if (!is_single_word (rl_line_buffer))
 		return NULL;
 
-	if (g_str_has_prefix (rl_prompt, PROMPT_CON_TYPE))
-		generator_func = gen_connection_types;
-	else if (g_str_has_prefix (rl_prompt, PROMPT_IFNAME))
-		generator_func = nmc_rl_gen_func_ifnames;
-	else if (g_str_has_prefix (rl_prompt, PROMPT_VPN_TYPE))
-		generator_func = gen_func_vpn_types;
-	else if (g_str_has_prefix (rl_prompt, PROMPT_MASTER))
-		generator_func = gen_func_master_ifnames;
-	else if (g_str_has_prefix (rl_prompt, PROMPT_WIFI_MODE))
-		generator_func = gen_func_wifi_mode;
-	else if (g_str_has_prefix (rl_prompt, PROMPT_IB_MODE))
-		generator_func = gen_func_ib_type;
-	else if (g_str_has_prefix (rl_prompt, PROMPT_BT_TYPE))
+	for (s = 0; s < _NM_META_SETTING_TYPE_NUM; s++) {
+		const NMMetaPropertyInfo *const*property_infos;
+		guint p;
+
+		property_infos = nm_meta_setting_infos_editor[s].properties;
+		if (!property_infos)
+			continue;
+		for (p = 0; property_infos[p]; p++) {
+			const NMMetaPropertyInfo *property_info = property_infos[p];
+
+			if (_meta_property_needs_bond_hack (property_info)) {
+				guint i;
+
+				for (i = 0; i < nm_meta_property_typ_data_bond.nested_len; i++) {
+					const NMMetaNestedPropertyInfo *bi = &nm_meta_property_typ_data_bond.nested[i];
+
+					if (   bi->base.prompt
+					    && g_str_has_prefix (rl_prompt, bi->base.prompt)) {
+						goto next;
+					}
+				}
+			} else {
+				if (   property_info->prompt
+				    && g_str_has_prefix (rl_prompt, property_info->prompt)) {
+					char **values;
+
+					values = _meta_abstract_complete ((const NMMetaAbstractInfo *) property_info, text);
+					if (values)
+						return values;
+					goto next;
+				}
+			}
+		}
+	}
+
+next:
+	if (g_str_has_prefix (rl_prompt, NM_META_TEXT_PROMPT_BT_TYPE))
 		generator_func = gen_func_bt_type;
-	else if (g_str_has_prefix (rl_prompt, PROMPT_BOND_MODE))
+	else if (g_str_has_prefix (rl_prompt, NM_META_TEXT_PROMPT_BOND_MODE))
 		generator_func = gen_func_bond_mode;
-	else if (g_str_has_prefix (rl_prompt, PROMPT_BOND_MON_MODE))
+	else if (g_str_has_prefix (rl_prompt, NM_META_TEXT_PROMPT_BOND_MON_MODE))
 		generator_func = gen_func_bond_mon_mode;
-	else if (g_str_has_prefix (rl_prompt, PROMPT_ADSL_PROTO))
-		generator_func = gen_func_adsl_proto;
-	else if (g_str_has_prefix (rl_prompt, PROMPT_ADSL_ENCAP))
-		generator_func = gen_func_adsl_encap;
-	else if (g_str_has_prefix (rl_prompt, PROMPT_TUN_MODE))
-		generator_func = gen_func_tun_mode;
-	else if (g_str_has_prefix (rl_prompt, PROMPT_IP_TUNNEL_MODE))
-		generator_func = gen_func_ip_tunnel_mode;
-	else if (g_str_has_prefix (rl_prompt, PROMPT_MACVLAN_MODE))
-		generator_func = gen_func_macvlan_mode;
-	else if (g_str_has_prefix (rl_prompt, PROMPT_MACSEC_MODE))
-		generator_func = gen_func_macsec_mode;
-	else if (g_str_has_prefix (rl_prompt, PROMPT_PROXY_METHOD))
-		generator_func = gen_func_proxy_method;
 	else if (   g_str_has_suffix (rl_prompt, yes)
 	         || g_str_has_suffix (rl_prompt, no))
 		generator_func = gen_func_bool_values_l10n;
@@ -4798,20 +4493,25 @@ nmcli_con_add_tab_completion (const char *text, int start, int end)
 }
 
 static void
-ask_option (NmCli *nmc, NMConnection *connection, OptionInfo *option)
+ask_option (NmCli *nmc, NMConnection *connection, const NMMetaAbstractInfo *abstract_info)
 {
 	gchar *value;
 	GError *error = NULL;
 	gs_free gchar *prompt = NULL;
-	gboolean multi = option->flags & OPTION_MULTI;
+	gboolean multi;
+	const char *opt_prompt, *opt_def_hint;
+	NMMetaPropertyInfFlags inf_flags;
 
+	_meta_abstract_get (abstract_info, NULL, NULL, NULL, NULL, &inf_flags, &opt_prompt, &opt_def_hint);
 	prompt = g_strjoin ("",
-	                    gettext (option->prompt),
-	                    option->def_hint ? " " : "",
-	                    option->def_hint ? option->def_hint : "",
+	                    gettext (opt_prompt),
+	                    opt_def_hint ? " " : "",
+	                    opt_def_hint ?: "",
 	                    ": ",
 	                    NULL);
 
+	multi = NM_FLAGS_HAS (inf_flags, NM_META_PROPERTY_INF_FLAG_MULTI);
+
 	if (multi)
 		g_print (_("You can specify this option more than once. Press <Enter> when you're done.\n"));
 
@@ -4820,7 +4520,7 @@ again:
 	if (multi && !value)
 		return;
 
-	if (!set_option (nmc, connection, option, value, &error)) {
+	if (!set_option (nmc, connection, abstract_info, value, &error)) {
 		g_printerr ("%s\n", error->message);
 		g_clear_error (&error);
 		goto again;
@@ -4830,17 +4530,78 @@ again:
 		goto again;
 }
 
+static NMMetaSettingType
+connection_get_base_meta_setting_type (NMConnection *connection)
+{
+	const char *connection_type;
+	NMSetting *base_setting;
+	const NMMetaSettingInfoEditor *editor;
+
+	connection_type = nm_connection_get_connection_type (connection);
+	nm_assert (connection_type);
+	base_setting = nm_connection_get_setting_by_name (connection, connection_type);
+	nm_assert (base_setting);
+	editor = nm_meta_setting_info_editor_find_by_setting (base_setting);
+	nm_assert (editor);
+
+	return editor - nm_meta_setting_infos_editor;
+}
+
+static void
+questionnaire_mandatory_ask_setting (NmCli *nmc, NMConnection *connection, NMMetaSettingType type)
+{
+	const NMMetaSettingInfoEditor *editor;
+	const NMMetaPropertyInfo *property_info;
+	guint p;
+
+	editor = &nm_meta_setting_infos_editor[type];
+	if (!editor->properties)
+		return;
+
+	for (p = 0; editor->properties[p]; p++) {
+		property_info = editor->properties[p];
+
+		if (_meta_property_needs_bond_hack (property_info)) {
+			guint i;
+
+			for (i = 0; i < nm_meta_property_typ_data_bond.nested_len; i++) {
+				const NMMetaNestedPropertyInfo *bi = &nm_meta_property_typ_data_bond.nested[i];
+
+				if (!option_relevant (connection, (const NMMetaAbstractInfo *) bi))
+					continue;
+				if (   (bi->base.inf_flags & NM_META_PROPERTY_INF_FLAG_REQD)
+				    || (_dynamic_options_get ((const NMMetaAbstractInfo *) bi) & PROPERTY_INF_FLAG_ENABLED))
+					ask_option (nmc, connection, (const NMMetaAbstractInfo *) bi);
+			}
+		} else {
+			if (!property_info->is_cli_option)
+				continue;
+
+			if (!option_relevant (connection, (const NMMetaAbstractInfo *) property_info))
+				continue;
+			if (   (property_info->inf_flags & NM_META_PROPERTY_INF_FLAG_REQD)
+			    || (_dynamic_options_get ((const NMMetaAbstractInfo *) property_info) & PROPERTY_INF_FLAG_ENABLED))
+				ask_option (nmc, connection, (const NMMetaAbstractInfo *) property_info);
+		}
+	}
+}
+
 static void
 questionnaire_mandatory (NmCli *nmc, NMConnection *connection)
 {
-	OptionInfo *candidate;
+	NMMetaSettingType s, base;
 
-	/* Mandatory settings. */
-	for (candidate = option_info; candidate->setting_name; candidate++) {
-		if (!option_relevant (connection, candidate))
-			continue;
-		if (candidate->flags & OPTION_REQD || candidate->flags & OPTION_ENABLED)
-			ask_option (nmc, connection, candidate);
+	/* First ask connection properties */
+	questionnaire_mandatory_ask_setting (nmc, connection, NM_META_SETTING_TYPE_CONNECTION);
+
+	/* Ask properties of the base setting */
+	base = connection_get_base_meta_setting_type (connection);
+	questionnaire_mandatory_ask_setting (nmc, connection, base);
+
+	/* Remaining settings */
+	for (s = 0; s < _NM_META_SETTING_TYPE_NUM; s++) {
+		if (!NM_IN_SET (s, NM_META_SETTING_TYPE_CONNECTION, base))
+			questionnaire_mandatory_ask_setting (nmc, connection, s);
 	}
 }
 
@@ -4858,104 +4619,93 @@ want_provide_opt_args (const char *type, int num)
 	                                 "Do you want to provide them? %s", num),
 	                       prompt_yes_no (TRUE, NULL));
 	answer = answer ? g_strstrip (answer) : NULL;
-	if (answer && !matches (answer, WORD_LOC_YES))
+	if (answer && !matches (answer, WORD_YES))
 		ret = FALSE;
 	g_free (answer);
 	return ret;
 }
 
-static const char *
-setting_name_to_name (const char *name)
-{
-	if (strcmp (name, NM_SETTING_WIRED_SETTING_NAME) == 0)
-		return _("Wired Ethernet");
-	if (strcmp (name, NM_SETTING_INFINIBAND_SETTING_NAME) == 0)
-		return _("InfiniBand connection");
-	if (strcmp (name, NM_SETTING_WIRELESS_SETTING_NAME) == 0)
-		return _("Wi-Fi connection");
-	if (strcmp (name, NM_SETTING_WIMAX_SETTING_NAME) == 0)
-		return _("WiMAX connection");
-	if (strcmp (name, NM_SETTING_PPPOE_SETTING_NAME) == 0)
-		return _("PPPoE");
-	if (strcmp (name, NM_SETTING_CDMA_SETTING_NAME) == 0)
-		return _("CDMA mobile broadband connection");
-	if (strcmp (name, NM_SETTING_GSM_SETTING_NAME) == 0)
-		return _("GSM mobile broadband connection");
-	if (strcmp (name, NM_SETTING_BLUETOOTH_SETTING_NAME) == 0)
-		return _("bluetooth connection");
-	if (strcmp (name, NM_SETTING_VLAN_SETTING_NAME) == 0)
-		return _("VLAN connection");
-	if (strcmp (name, NM_SETTING_BOND_SETTING_NAME) == 0)
-		return _("Bond device");
-	if (strcmp (name, NM_SETTING_TEAM_SETTING_NAME) == 0)
-		return _("Team device");
-	if (strcmp (name, NM_SETTING_TEAM_PORT_SETTING_NAME) == 0)
-		return _("Team port");
-	if (strcmp (name, NM_SETTING_BRIDGE_SETTING_NAME) == 0)
-		return _("Bridge device");
-	if (strcmp (name, NM_SETTING_BRIDGE_PORT_SETTING_NAME) == 0)
-		return _("Bridge port");
-	if (strcmp (name, NM_SETTING_VPN_SETTING_NAME) == 0)
-		return _("VPN connection");
-	if (strcmp (name, NM_SETTING_OLPC_MESH_SETTING_NAME) == 0)
-		return _("OLPC Mesh connection");
-	if (strcmp (name, NM_SETTING_ADSL_SETTING_NAME) == 0)
-		return _("ADSL connection");
-	if (strcmp (name, NM_SETTING_MACSEC_SETTING_NAME) == 0)
-		return _("MACsec connection");
-	if (strcmp (name, NM_SETTING_MACVLAN_SETTING_NAME) == 0)
-		return _("macvlan connection");
-	if (strcmp (name, NM_SETTING_VXLAN_SETTING_NAME) == 0)
-		return _("VXLAN connection");
-	if (strcmp (name, NM_SETTING_TUN_SETTING_NAME) == 0)
-		return _("Tun device");
-	if (strcmp (name, NM_SETTING_IP4_CONFIG_SETTING_NAME) == 0)
-		return _("IPv4 protocol");
-	if (strcmp (name, NM_SETTING_IP6_CONFIG_SETTING_NAME) == 0)
-		return _("IPv6 protocol");
-	if (strcmp (name, NM_SETTING_PROXY_SETTING_NAME) == 0)
-		return _("Proxy");
-
-	/* Should not happen; but let's still try to be somewhat sensible. */
-	return name;
-}
-
 static gboolean
 questionnaire_one_optional (NmCli *nmc, NMConnection *connection)
 {
-	OptionInfo *candidate;
+	NMMetaSettingType base;
+	gs_unref_ptrarray GPtrArray *infos = NULL;
+	guint i, j;
+	gboolean already_confirmed = FALSE;
+	NMMetaSettingType s_asking = NM_META_SETTING_TYPE_UNKNOWN;
+	NMMetaSettingType settings[_NM_META_SETTING_TYPE_NUM];
+
+	base = connection_get_base_meta_setting_type (connection);
+
+	i = 0;
+	settings[i++] = NM_META_SETTING_TYPE_CONNECTION;
+	settings[i++] = base;
+	for (j = 0; j < _NM_META_SETTING_TYPE_NUM; j++) {
+		if (!NM_IN_SET (j, NM_META_SETTING_TYPE_CONNECTION, base))
+			settings[i++] = j;
+	}
 
-	/* Optional settings. */
-	const gchar *setting_name = NULL;
-	int count = 0;
+	infos = g_ptr_array_new ();
 
 	/* Find first setting with relevant options and count them. */
-	for (candidate = option_info; candidate->setting_name; candidate++) {
-		if (!option_relevant (connection, candidate))
+again:
+	for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) {
+		const NMMetaPropertyInfo *const*property_infos;
+		guint p;
+
+		if (   s_asking != NM_META_SETTING_TYPE_UNKNOWN
+		    && settings[i] != s_asking)
 			continue;
-		if (!setting_name)
-			setting_name = candidate->setting_name;
-		else if (strcmp (setting_name, candidate->setting_name))
+
+		property_infos = nm_meta_setting_infos_editor[settings[i]].properties;
+		if (!property_infos)
+			continue;
+		for (p = 0; property_infos[p]; p++) {
+			const NMMetaPropertyInfo *property_info = property_infos[p];
+
+			if (_meta_property_needs_bond_hack (property_info)) {
+				for (j = 0; j < nm_meta_property_typ_data_bond.nested_len; j++) {
+					const NMMetaNestedPropertyInfo *bi = &nm_meta_property_typ_data_bond.nested[j];
+
+					if (!option_relevant (connection, (const NMMetaAbstractInfo *) bi))
+						continue;
+					g_ptr_array_add (infos, (gpointer) bi);
+				}
+			} else {
+				if (!property_info->is_cli_option)
+					continue;
+				if (!option_relevant (connection, (const NMMetaAbstractInfo *) property_info))
+					continue;
+				g_ptr_array_add (infos, (gpointer) property_info);
+			}
+		}
+		if (infos->len) {
+			s_asking = settings[i];
 			break;
-		count++;
+		}
 	}
-	if (!setting_name)
-		return FALSE;
 
-	/* Now ask for the settings. */
-	if (want_provide_opt_args (setting_name_to_name (setting_name), count)) {
-		for (candidate = option_info; candidate->setting_name; candidate++) {
-			if (!option_relevant (connection, candidate))
-				continue;
-			if (strcmp (setting_name, candidate->setting_name))
-				continue;
-			ask_option (nmc, connection, candidate);
+	if (infos->len) {
+		const NMMetaSettingInfoEditor *setting_info = NULL;
+
+		_meta_abstract_get (infos->pdata[0], &setting_info, NULL, NULL, NULL, NULL, NULL, NULL);
+
+		/* Now ask for the settings. */
+		if (   already_confirmed
+			|| want_provide_opt_args (_(setting_info->pretty_name), infos->len)) {
+			ask_option (nmc, connection, infos->pdata[0]);
+			already_confirmed = TRUE;
+			/* asking for an option may enable other options. Create the list again. */
+			g_ptr_array_set_size (infos, 0);
+			goto again;
 		}
 	}
 
-	/* Make sure we won't ask again. */
-	disable_options (setting_name, NULL);
+	if (s_asking == NM_META_SETTING_TYPE_UNKNOWN)
+		return FALSE;
 
+	/* Make sure we won't ask again. */
+	disable_options (nm_meta_setting_infos[s_asking].setting_name, NULL);
 	return TRUE;
 }
 
@@ -4967,8 +4717,8 @@ do_connection_add (NmCli *nmc, int argc, char **argv)
 	GError *error = NULL;
 	AddConnectionInfo *info = NULL;
 	gboolean save_bool = TRUE;
-	OptionInfo *candidate;
 	gboolean seen_dash_dash = FALSE;
+	NMMetaSettingType s;
 
 	next_arg (nmc, &argc, &argv, NULL);
 
@@ -5052,8 +4802,9 @@ read_properties:
 			const GPtrArray *connections;
 
 			connections = nm_client_get_connections (nmc->client);
-			try_name = ifname ? g_strdup_printf ("%s-%s", get_name_alias (type, slave_type, nmc_valid_connection_types), ifname)
-					  : g_strdup (get_name_alias (type, slave_type, nmc_valid_connection_types));
+			try_name = ifname
+			           ? g_strdup_printf ("%s-%s", get_name_alias_toplevel (type, slave_type), ifname)
+			           : g_strdup (get_name_alias_toplevel (type, slave_type));
 			default_name = nmc_unique_connection_name (connections, try_name);
 			g_free (try_name);
 			g_object_set (s_con, NM_SETTING_CONNECTION_ID, default_name, NULL);
@@ -5075,13 +4826,41 @@ read_properties:
 	 * from doing something that's not likely to make sense (such as missing ifname
 	 * on a bond/bridge/team, etc.). Added just to preserve traditional behavior, it
 	 * perhaps is a good idea to just remove this. */
-	for (candidate = option_info; candidate->setting_name; candidate++) {
-		if (!option_relevant (connection, candidate))
+	for (s = 0; s < _NM_META_SETTING_TYPE_NUM; s++) {
+		const NMMetaPropertyInfo *const*property_infos;
+		guint p;
+
+		property_infos = nm_meta_setting_infos_editor[s].properties;
+		if (!property_infos)
 			continue;
-		if (candidate->flags & OPTION_REQD) {
-			g_string_printf (nmc->return_text, _("Error: '%s' argument is required."), candidate->option);
-			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-			goto finish;
+		for (p = 0; property_infos[p]; p++) {
+			const NMMetaPropertyInfo *property_info = property_infos[p];
+
+			if (_meta_property_needs_bond_hack (property_info)) {
+				guint i;
+
+				for (i = 0; i < nm_meta_property_typ_data_bond.nested_len; i++) {
+					const NMMetaNestedPropertyInfo *bi = &nm_meta_property_typ_data_bond.nested[i];
+
+					if (!option_relevant (connection, (const NMMetaAbstractInfo *) bi))
+						continue;
+					if (bi->base.inf_flags & NM_META_PROPERTY_INF_FLAG_REQD) {
+						g_string_printf (nmc->return_text, _("Error: '%s' argument is required."), bi->base.property_alias);
+						nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+						goto finish;
+					}
+				}
+			} else {
+				if (!property_info->is_cli_option)
+					continue;
+				if (!option_relevant (connection, (const NMMetaAbstractInfo *) property_info))
+					continue;
+				if (property_info->inf_flags & NM_META_PROPERTY_INF_FLAG_REQD) {
+					g_string_printf (nmc->return_text, _("Error: '%s' argument is required."), property_info->property_alias);
+					nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+					goto finish;
+				}
+			}
 		}
 	}
 
@@ -5106,7 +4885,7 @@ finish:
 	return nmc->return_value;
 }
 
-/*----------------------------------------------------------------------------*/
+/*****************************************************************************/
 /* Functions for readline TAB completion in editor */
 
 static void
@@ -5226,28 +5005,33 @@ gen_cmd_save (const char *text, int state)
 	return nmc_rl_gen_func_basic (text, state, words);
 }
 
-static char *
-gen_connection_types (const char *text, int state)
+static rl_compentry_func_t *
+gen_connection_types (const char *text)
 {
-	static int list_idx, len;
-	const char *c_type, *a_type;
+	gs_free char **values = NULL;
+	const NMMetaSettingInfoEditor *editor;
+	GPtrArray *array;
+	int i;
 
-	if (!state) {
-		list_idx = 0;
-		len = strlen (text);
-	}
+	array = g_ptr_array_new ();
 
-	while (nmc_valid_connection_types[list_idx].name) {
-		a_type = nmc_valid_connection_types[list_idx].alias;
-		c_type = nmc_valid_connection_types[list_idx].name;
-		list_idx++;
-		if (a_type && !strncmp (text, a_type, len))
-			return g_strdup (a_type);
-		if (c_type && !strncmp (text, c_type, len))
-			return g_strdup (c_type);
+	for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) {
+		editor = &nm_meta_setting_infos_editor[i];
+		if (!editor->valid_parts)
+			continue;
+		g_ptr_array_add (array, (gpointer) nm_meta_setting_infos[i].setting_name);
+		if (editor->alias)
+			g_ptr_array_add (array, (gpointer) editor->alias);
 	}
 
-	return NULL;
+	g_ptr_array_add (array, "bond-slave");
+	g_ptr_array_add (array, "bridge-slave");
+	g_ptr_array_add (array, "team-slave");
+	g_ptr_array_add (array, NULL);
+
+	values = (char **) g_ptr_array_free (array, FALSE);
+
+	return nmc_rl_compentry_func_wrap ((const char *const*) values);
 }
 
 static char *
@@ -5255,10 +5039,9 @@ gen_setting_names (const char *text, int state)
 {
 	static int list_idx, len, is_slv;
 	const char *s_name, *a_name;
-	const NameItem *valid_settings_arr;
+	const NMMetaSettingValidPartItem *const*valid_settings_arr;
 	NMSettingConnection *s_con;
 	const char *s_type = NULL;
-	char *slv_type;
 
 	if (!state) {
 		list_idx = 0;
@@ -5268,11 +5051,13 @@ gen_setting_names (const char *text, int state)
 
 	if (!is_slv) {
 		valid_settings_arr = get_valid_settings_array (nmc_tab_completion.con_type);
-		if (!valid_settings_arr)
+		if (list_idx >= NM_PTRARRAY_LEN (valid_settings_arr))
 			return NULL;
-		while (valid_settings_arr[list_idx].name) {
-			a_name = valid_settings_arr[list_idx].alias;
-			s_name = valid_settings_arr[list_idx].name;
+		for (; valid_settings_arr[list_idx];) {
+			const NMMetaSettingInfoEditor *setting_info = valid_settings_arr[list_idx]->setting_info;
+
+			a_name = setting_info->alias;
+			s_name = setting_info->general->setting_name;
 			list_idx++;
 			if (len == 0 && a_name)
 				return g_strdup_printf ("%s (%s)", s_name, a_name);
@@ -5291,20 +5076,22 @@ gen_setting_names (const char *text, int state)
 	s_con = nm_connection_get_setting_connection (nmc_tab_completion.connection);
 	if (s_con)
 		s_type = nm_setting_connection_get_slave_type (s_con);
-	slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
-	valid_settings_arr = get_valid_settings_array (slv_type);
-	g_free (slv_type);
-
-	while (valid_settings_arr[list_idx].name) {
-		a_name = valid_settings_arr[list_idx].alias;
-		s_name = valid_settings_arr[list_idx].name;
-		list_idx++;
-		if (len == 0 && a_name)
-			return g_strdup_printf ("%s (%s)", s_name, a_name);
-		if (a_name && !strncmp (text, a_name, len))
-			return g_strdup (a_name);
-		if (s_name && !strncmp (text, s_name, len))
-			return g_strdup (s_name);
+	valid_settings_arr = nm_meta_setting_info_valid_parts_for_slave_type (s_type, NULL);
+
+	if (list_idx < NM_PTRARRAY_LEN (valid_settings_arr)) {
+		while (valid_settings_arr[list_idx]) {
+			const NMMetaSettingInfoEditor *setting_info = valid_settings_arr[list_idx]->setting_info;
+
+			a_name = setting_info->alias;
+			s_name = setting_info->general->setting_name;
+			list_idx++;
+			if (len == 0 && a_name)
+				return g_strdup_printf ("%s (%s)", s_name, a_name);
+			if (a_name && !strncmp (text, a_name, len))
+				return g_strdup (a_name);
+			if (s_name && !strncmp (text, s_name, len))
+				return g_strdup (s_name);
+		}
 	}
 
 	return NULL;
@@ -5319,8 +5106,8 @@ gen_property_names (const char *text, int state)
 	const char *line = rl_line_buffer;
 	const char *setting_name;
 	char **strv = NULL;
-	const NameItem *valid_settings_main;
-	const NameItem *valid_settings_slave;
+	const NMMetaSettingValidPartItem *const*valid_settings_main;
+	const NMMetaSettingValidPartItem *const*valid_settings_slave;
 	const char *p1;
 	const char *slv_type;
 
@@ -5330,7 +5117,7 @@ gen_property_names (const char *text, int state)
 		while (p1 > line && !g_ascii_isspace (*p1))
 			p1--;
 
-		strv = g_strsplit (p1+1, ".", 2);
+		strv = g_strsplit (p1 + 1, ".", 2);
 
 		valid_settings_main = get_valid_settings_array (nmc_tab_completion.con_type);
 
@@ -5338,19 +5125,24 @@ gen_property_names (const char *text, int state)
 		 * guessing the slave type from the setting name already
 		 * typed (or autocompleted) */
 		if (nm_streq0 (strv[0], NM_SETTING_TEAM_PORT_SETTING_NAME))
-			slv_type = "team-slave";
+			slv_type = NM_SETTING_TEAM_SETTING_NAME;
 		else if (nm_streq0 (strv[0], NM_SETTING_BRIDGE_PORT_SETTING_NAME))
-			slv_type = "bridge-slave";
+			slv_type = NM_SETTING_BRIDGE_SETTING_NAME;
 		else
-			slv_type = "no-slave";
-		valid_settings_slave = get_valid_settings_array (slv_type);
+			slv_type = NULL;
+		valid_settings_slave = nm_meta_setting_info_valid_parts_for_slave_type (slv_type, NULL);
 
 		setting_name = check_valid_name (strv[0],
 		                                 valid_settings_main,
 		                                 valid_settings_slave,
 		                                 NULL);
-		setting = nmc_setting_new_for_name (setting_name);
-	} else {
+		if (setting_name) {
+			setting = nm_meta_setting_info_editor_new_setting (nm_meta_setting_info_editor_find_by_name (setting_name, FALSE),
+			                                                   NM_META_ACCESSOR_SETTING_INIT_TYPE_DEFAULT);
+		}
+	}
+
+	if (!setting) {
 		/* Else take the current setting, if any */
 		setting = nmc_tab_completion.setting ? g_object_ref (nmc_tab_completion.setting) : NULL;
 	}
@@ -5628,15 +5420,14 @@ static void
 get_setting_and_property (const char *prompt, const char *line,
                           NMSetting **setting_out, char**property_out)
 {
-	const NameItem *valid_settings_main;
-	const NameItem *valid_settings_slave;
+	const NMMetaSettingValidPartItem *const*valid_settings_main;
+	const NMMetaSettingValidPartItem *const*valid_settings_slave;
 	const char *setting_name;
 	NMSetting *setting = NULL;
 	char *property = NULL;
 	char *sett = NULL, *prop = NULL;
 	NMSettingConnection *s_con;
 	const char *s_type = NULL;
-	char *slv_type;
 
 	extract_setting_and_property (prompt, line, &sett, &prop);
 	if (sett) {
@@ -5644,15 +5435,14 @@ get_setting_and_property (const char *prompt, const char *line,
 		s_con = nm_connection_get_setting_connection (nmc_tab_completion.connection);
 		if (s_con)
 			s_type = nm_setting_connection_get_slave_type (s_con);
-		slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
 
 		valid_settings_main = get_valid_settings_array (nmc_tab_completion.con_type);
-		valid_settings_slave = get_valid_settings_array (slv_type);
-		g_free (slv_type);
+		valid_settings_slave = nm_meta_setting_info_valid_parts_for_slave_type (s_type, NULL);
 
 		setting_name = check_valid_name (sett, valid_settings_main,
 		                                 valid_settings_slave,  NULL);
-		setting = nmc_setting_new_for_name (setting_name);
+		setting = nm_meta_setting_info_editor_new_setting (nm_meta_setting_info_editor_find_by_name (setting_name, FALSE),
+		                                                   NM_META_ACCESSOR_SETTING_INIT_TYPE_DEFAULT);
 	} else
 		setting = nmc_tab_completion.setting ? g_object_ref (nmc_tab_completion.setting) : NULL;
 
@@ -5723,27 +5513,24 @@ should_complete_vpn_uuids (const char *prompt, const char *line)
 	return _get_and_check_property (prompt, line, uuid_properties, NULL, NULL);
 }
 
-static const char **
-get_allowed_property_values (void)
+static const char *const*
+get_allowed_property_values (char ***out_to_free)
 {
-	NMSetting *setting;
-	char *property;
-	const char **avals = NULL;
+	gs_unref_object NMSetting *setting = NULL;
+	gs_free char *property = NULL;
+	const char *const*avals = NULL;
 
 	get_setting_and_property (rl_prompt, rl_line_buffer, &setting, &property);
 	if (setting && property)
-		avals = nmc_setting_get_property_allowed_values (setting, property);
-
-	if (setting)
-		g_object_unref (setting);
-	g_free (property);
-
+		avals = nmc_setting_get_property_allowed_values (setting, property, out_to_free);
 	return avals;
 }
 
 static gboolean
 should_complete_property_values (const char *prompt, const char *line, gboolean *multi)
 {
+	gs_strfreev char **to_free = NULL;
+
 	/* properties allowing multiple values */
 	const char *multi_props[] = {
 		/* '802-1x' properties */
@@ -5759,10 +5546,9 @@ should_complete_property_values (const char *prompt, const char *line, gboolean
 		NULL
 	};
 	_get_and_check_property (prompt, line, NULL, multi_props, multi);
-	return get_allowed_property_values () != NULL;
+	return !!get_allowed_property_values (&to_free);
 }
 
-//FIXME: this helper should go to libnm later
 static gboolean
 _setting_property_is_boolean (NMSetting *setting, const char *property_name)
 {
@@ -5798,13 +5584,13 @@ should_complete_boolean (const char *prompt, const char *line)
 static char *
 gen_property_values (const char *text, int state)
 {
-	char *ret = NULL;
-	const char **avals;
+	gs_strfreev char **to_free = NULL;
+	const char *const*avals;
 
-	avals = get_allowed_property_values ();
-	if (avals)
-		ret = nmc_rl_gen_func_basic (text, state, avals);
-	return ret;
+	avals = get_allowed_property_values (&to_free);
+	if (!avals)
+		return NULL;
+	return nmc_rl_gen_func_basic (text, state, avals);
 }
 
 /* from readline */
@@ -5847,7 +5633,7 @@ nmcli_editor_tab_completion (const char *text, int start, int end)
 
 	/* Choose the right generator function */
 	if (strcmp (prompt_tmp, EDITOR_PROMPT_CON_TYPE) == 0)
-		generator_func = gen_connection_types;
+		generator_func = gen_connection_types (text);
 	else if (strcmp (prompt_tmp, EDITOR_PROMPT_SETTING) == 0)
 		generator_func = gen_setting_names;
 	else if (strcmp (prompt_tmp, EDITOR_PROMPT_PROPERTY) == 0)
@@ -6033,19 +5819,16 @@ save_history_cmds (const char *uuid)
 	}
 }
 
-/*----------------------------------------------------------------------------*/
+/*****************************************************************************/
 
 static void
 editor_show_connection (NMConnection *connection, NmCli *nmc)
 {
-	nmc->print_output = NMC_PRINT_PRETTY;
-	nmc->multiline_output = TRUE;
-	nmc->escape_values = 0;
-
-	/* Remove any previous data */
-	nmc_empty_output_fields (nmc);
+	nmc->nmc_config_mutable.print_output = NMC_PRINT_PRETTY;
+	nmc->nmc_config_mutable.multiline_output = TRUE;
+	nmc->nmc_config_mutable.escape_values = 0;
 
-	nmc_connection_profile_details (connection, nmc, nmc->editor_show_secrets);
+	nmc_connection_profile_details (connection, nmc);
 }
 
 static void
@@ -6054,14 +5837,11 @@ editor_show_setting (NMSetting *setting, NmCli *nmc)
 	g_print (_("['%s' setting values]\n"),
 	         nm_setting_get_name (setting));
 
-	nmc->print_output = NMC_PRINT_NORMAL;
-	nmc->multiline_output = TRUE;
-	nmc->escape_values = 0;
+	nmc->nmc_config_mutable.print_output = NMC_PRINT_NORMAL;
+	nmc->nmc_config_mutable.multiline_output = TRUE;
+	nmc->nmc_config_mutable.escape_values = 0;
 
-	/* Remove any previous data */
-	nmc_empty_output_fields (nmc);
-
-	setting_details (setting, nmc, NULL, nmc->editor_show_secrets);
+	setting_details (&nmc->nmc_config, setting, NULL);
 }
 
 typedef enum {
@@ -6392,12 +6172,13 @@ editor_sub_usage (const char *command)
 	}
 }
 
-/*----------------------------------------------------------------------------*/
+/*****************************************************************************/
 
 typedef struct {
 	NMDevice *device;
 	NMActiveConnection *ac;
 	guint monitor_id;
+	NmCli *nmc;
 } MonitorACInfo;
 
 static gboolean nmc_editor_cb_called;
@@ -6475,6 +6256,14 @@ progress_activation_editor_cb (gpointer user_data)
 		goto finish; /* we are done */
 	}
 
+	if (info->nmc->secret_agent) {
+		NMRemoteConnection *connection;
+
+		connection = nm_active_connection_get_connection (ac);
+		nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (info->nmc->secret_agent),
+		                               nm_object_get_path (NM_OBJECT (connection)));
+	}
+
 	return TRUE;
 
 finish:
@@ -6510,6 +6299,7 @@ activate_connection_editor_cb (GObject *client,
 			monitor_ac_info->device = g_object_ref (device);
 			monitor_ac_info->ac = active;
 			monitor_ac_info->monitor_id = g_timeout_add (120, progress_activation_editor_cb, monitor_ac_info);
+			monitor_ac_info->nmc = info->nmc;
 		} else
 			g_object_unref (active);
 	}
@@ -6517,7 +6307,7 @@ activate_connection_editor_cb (GObject *client,
 	g_clear_error (&error);
 }
 
-/*----------------------------------------------------------------------------*/
+/*****************************************************************************/
 
 static void
 print_property_description (NMSetting *setting, const char *prop_name)
@@ -6525,8 +6315,10 @@ print_property_description (NMSetting *setting, const char *prop_name)
 	char *desc;
 
 	desc = nmc_setting_get_property_desc (setting, prop_name);
-	g_print ("\n=== [%s] ===\n%s\n", prop_name, desc);
-	g_free (desc);
+	if (desc) {
+		g_print ("\n=== [%s] ===\n%s\n", prop_name, desc);
+		g_free (desc);
+	}
 }
 
 static void
@@ -6613,7 +6405,7 @@ confirm_quit (void)
 	                         "Do you really want to quit? %s"),
 	                       prompt_yes_no (FALSE, NULL));
 	answer = answer ? g_strstrip (answer) : NULL;
-	if (answer && matches (answer, WORD_LOC_YES))
+	if (answer && matches (answer, WORD_YES))
 		want_quit = TRUE;
 
 	g_free (answer);
@@ -6647,7 +6439,7 @@ property_edit_submenu (NmCli *nmc,
 	/* Set global variable for use in TAB completion */
 	nmc_tab_completion.property = prop_name;
 
-	prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
+	prompt = nmc_colorize (nmc->nmc_config.use_colors, nmc->editor_prompt_color, NM_META_TERM_FORMAT_NORMAL,
 	                       "nmcli %s.%s> ",
 	                       nm_setting_get_name (curr_setting), prop_name);
 
@@ -6680,12 +6472,16 @@ property_edit_submenu (NmCli *nmc,
 			 * single values:  : both SET and ADD sets the new value
 			 */
 			if (!cmd_property_arg) {
-				const char **avals = nmc_setting_get_property_allowed_values (curr_setting, prop_name);
+				gs_strfreev char **to_free = NULL;
+				const char *const*avals;
+
+				avals = nmc_setting_get_property_allowed_values (curr_setting, prop_name, &to_free);
 				if (avals) {
-					char *avals_str = nmc_util_strv_for_display (avals, FALSE);
+					gs_free char *avals_str = NULL;
+
+					avals_str = nmc_util_strv_for_display (avals, FALSE);
 					g_print (_("Allowed values for '%s' property: %s\n"),
 					         prop_name, avals_str);
-					g_free (avals_str);
 				}
 				prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name);
 			} else
@@ -6851,7 +6647,7 @@ split_editor_main_cmd_args (const char *str, char **setting, char **property, ch
 }
 
 static NMSetting *
-create_setting_by_name (const char *name, const NameItem *valid_settings_main, const NameItem *valid_settings_slave)
+create_setting_by_name (const char *name, const NMMetaSettingValidPartItem *const*valid_settings_main, const NMMetaSettingValidPartItem *const*valid_settings_slave)
 {
 	const char *setting_name;
 	NMSetting *setting = NULL;
@@ -6860,18 +6656,16 @@ create_setting_by_name (const char *name, const NameItem *valid_settings_main, c
 	setting_name = check_valid_name (name, valid_settings_main, valid_settings_slave, NULL);
 
 	if (setting_name) {
-		setting = nmc_setting_new_for_name (setting_name);
-		if (!setting)
-			return NULL; /* This should really not happen */
-		nmc_setting_custom_init (setting);
+		setting = nm_meta_setting_info_editor_new_setting (nm_meta_setting_info_editor_find_by_name (setting_name, FALSE),
+		                                                   NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI);
 	}
 	return setting;
 }
 
 static const char *
 ask_check_setting (const char *arg,
-                   const NameItem *valid_settings_main,
-                   const NameItem *valid_settings_slave,
+                   const NMMetaSettingValidPartItem *const*valid_settings_main,
+                   const NMMetaSettingValidPartItem *const*valid_settings_slave,
                    const char *valid_settings_str)
 {
 	char *setting_name_user;
@@ -6961,7 +6755,7 @@ confirm_connection_saving (NMConnection *local, NMConnection *remote)
 		                         "That might result in an immediate activation of the connection.\n"
 		                         "Do you still want to save? %s"), prompt_yes_no (TRUE, NULL));
 		answer = answer ? g_strstrip (answer) : NULL;
-		if (!answer || matches (answer, WORD_LOC_YES))
+		if (!answer || matches (answer, WORD_YES))
 			confirmed = TRUE;
 		else
 			confirmed = FALSE;
@@ -6979,14 +6773,14 @@ typedef	struct {
 } NmcEditorMenuContext;
 
 static void
-menu_switch_to_level0 (NmCli *nmc,
+menu_switch_to_level0 (NmcColorOption color_option,
                        NmcEditorMenuContext *menu_ctx,
                        const char *prompt,
-                       NmcTermColor prompt_color)
+                       NMMetaTermColor prompt_color)
 {
 	menu_ctx->level = 0;
 	g_free (menu_ctx->main_prompt);
-	menu_ctx->main_prompt = nmc_colorize (nmc, prompt_color, NMC_TERM_FORMAT_NORMAL, "%s", prompt);
+	menu_ctx->main_prompt = nmc_colorize (color_option, prompt_color, NM_META_TERM_FORMAT_NORMAL, "%s", prompt);
 	menu_ctx->curr_setting = NULL;
 	g_strfreev (menu_ctx->valid_props);
 	menu_ctx->valid_props = NULL;
@@ -6995,15 +6789,15 @@ menu_switch_to_level0 (NmCli *nmc,
 }
 
 static void
-menu_switch_to_level1 (NmCli *nmc,
+menu_switch_to_level1 (NmcColorOption color_option,
                        NmcEditorMenuContext *menu_ctx,
                        NMSetting *setting,
                        const char *setting_name,
-                       NmcTermColor prompt_color)
+                       NMMetaTermColor prompt_color)
 {
 	menu_ctx->level = 1;
 	g_free (menu_ctx->main_prompt);
-	menu_ctx->main_prompt = nmc_colorize (nmc, prompt_color, NMC_TERM_FORMAT_NORMAL,
+	menu_ctx->main_prompt = nmc_colorize (color_option, prompt_color, NM_META_TERM_FORMAT_NORMAL,
 	                                      "nmcli %s> ", setting_name);
 	menu_ctx->curr_setting = setting;
 	g_strfreev (menu_ctx->valid_props);
@@ -7013,47 +6807,37 @@ menu_switch_to_level1 (NmCli *nmc,
 }
 
 static gboolean
-editor_menu_main (NmCli *nmc, NMConnection *connection)
+editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_type)
 {
+	gs_unref_object NMRemoteConnection *rem_con = NULL;
 	NMSettingConnection *s_con;
-	NMRemoteConnection *rem_con;
 	NMRemoteConnection *con_tmp;
 	GWeakRef weak = { { NULL } };
 	gboolean removed;
 	NmcEditorMainCmd cmd;
-	char *cmd_user;
 	gboolean cmd_loop = TRUE;
-	char *cmd_arg = NULL;
-	char *cmd_arg_s, *cmd_arg_p, *cmd_arg_v;
-	const NameItem *valid_settings_main = NULL;
-	const NameItem *valid_settings_slave = NULL;
-	char *valid_settings_str = NULL;
+	const NMMetaSettingValidPartItem *const*valid_settings_main;
+	const NMMetaSettingValidPartItem *const*valid_settings_slave;
+	gs_free char *valid_settings_str = NULL;
 	const char *s_type = NULL;
-	char *slv_type;
 	AddConnectionInfo *info = NULL;
 	gboolean dirty;
 	gboolean temp_changes;
 	GError *err1 = NULL;
-	NmcEditorMenuContext menu_ctx;
+	NmcEditorMenuContext menu_ctx = { 0 };
 
 	s_con = nm_connection_get_setting_connection (connection);
 	if (s_con)
 		s_type = nm_setting_connection_get_slave_type (s_con);
-	slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
 
-	valid_settings_main = get_valid_settings_array (nm_connection_get_connection_type (connection));
-	valid_settings_slave = get_valid_settings_array (slv_type);
-	g_free (slv_type);
+	valid_settings_main = get_valid_settings_array (connection_type);
+	valid_settings_slave = nm_meta_setting_info_valid_parts_for_slave_type (s_type, NULL);
 
 	valid_settings_str = get_valid_options_string (valid_settings_main, valid_settings_slave);
 	g_print (_("You may edit the following settings: %s\n"), valid_settings_str);
 
-	menu_ctx.level = 0;
-	menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
+	menu_ctx.main_prompt = nmc_colorize (nmc->nmc_config.use_colors, nmc->editor_prompt_color, NM_META_TERM_FORMAT_NORMAL,
 	                                     BASE_PROMPT);
-	menu_ctx.curr_setting = NULL;
-	menu_ctx.valid_props = NULL;
-	menu_ctx.valid_props_str = NULL;
 
 	/* Get remote connection */
 	con_tmp = nm_client_get_connection_by_uuid (nmc->client,
@@ -7062,6 +6846,12 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 	rem_con = g_weak_ref_get (&weak);
 
 	while (cmd_loop) {
+		gs_free char *cmd_user = NULL;
+		gs_free char *cmd_arg = NULL;
+		gs_free char *cmd_arg_s = NULL;
+		gs_free char *cmd_arg_p = NULL;
+		gs_free char *cmd_arg_v = NULL;
+
 		/* Connection is dirty? (not saved or differs from the saved) */
 		dirty = is_connection_dirty (connection, rem_con);
 		temp_changes = rem_con ? nm_remote_connection_get_unsaved (rem_con) : TRUE;
@@ -7081,18 +6871,16 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 			continue;
 		cmd = parse_editor_main_cmd (g_strstrip (cmd_user), &cmd_arg);
 
-		cmd_arg_s = NULL;
-		cmd_arg_p = NULL;
-		cmd_arg_v = NULL;
 		split_editor_main_cmd_args (cmd_arg, &cmd_arg_s, &cmd_arg_p, &cmd_arg_v);
 		switch (cmd) {
 		case NMC_EDITOR_MAIN_CMD_SET:
 			/* Set property value */
 			if (!cmd_arg) {
 				if (menu_ctx.level == 1) {
+					gs_strfreev char **avals_to_free = NULL;
+					gs_free char *prop_val_user = NULL;
 					const char *prop_name;
-					char *prop_val_user = NULL;
-					const char **avals;
+					const char *const*avals;
 					GError *tmp_err = NULL;
 
 					prop_name = ask_check_property (cmd_arg,
@@ -7101,12 +6889,13 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 					if (!prop_name)
 						break;
 
-					avals = nmc_setting_get_property_allowed_values (menu_ctx.curr_setting, prop_name);
+					avals = nmc_setting_get_property_allowed_values (menu_ctx.curr_setting, prop_name, &avals_to_free);
 					if (avals) {
-						char *avals_str = nmc_util_strv_for_display (avals, FALSE);
+						gs_free char *avals_str = NULL;
+
+						avals_str = nmc_util_strv_for_display (avals, FALSE);
 						g_print (_("Allowed values for '%s' property: %s\n"),
 						         prop_name, avals_str);
-						g_free (avals_str);
 					}
 					prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name);
 
@@ -7120,22 +6909,22 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 					g_print (_("use 'goto <setting>' first, or 'set <setting>.<property>'\n"));
 				}
 			} else {
+				gs_free char *prop_name = NULL;
+				gs_unref_object NMSetting *ss_created = NULL;
 				NMSetting *ss = NULL;
-				gboolean created_ss = FALSE;
-				char *prop_name;
 				GError *tmp_err = NULL;
 
 				if (cmd_arg_s) {
 					/* setting provided as "setting.property" */
 					ss = is_setting_valid (connection, valid_settings_main, valid_settings_slave, cmd_arg_s);
 					if (!ss) {
-						ss = create_setting_by_name (cmd_arg_s, valid_settings_main, valid_settings_slave);
+						ss_created = create_setting_by_name (cmd_arg_s, valid_settings_main, valid_settings_slave);
+						ss = ss_created;
 						if (!ss) {
 							g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
 							         cmd_arg_s, valid_settings_str);
 							break;
 						}
-						created_ss = TRUE;
 					}
 				} else {
 					if (menu_ctx.curr_setting)
@@ -7150,21 +6939,21 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 				if (!prop_name) {
 					g_print (_("Error: invalid property: %s\n"), tmp_err->message);
 					g_clear_error (&tmp_err);
-					if (created_ss)
-						g_object_unref (ss);
 					break;
 				}
 
-
-
 				/* Ask for value */
 				if (!cmd_arg_v) {
-					const char **avals = nmc_setting_get_property_allowed_values (ss, prop_name);
+					gs_strfreev char **avals_to_free = NULL;
+					const char *const*avals;
+
+					avals = nmc_setting_get_property_allowed_values (ss, prop_name, &avals_to_free);
 					if (avals) {
-						char *avals_str = nmc_util_strv_for_display (avals, FALSE);
+						gs_free char *avals_str = NULL;
+
+						avals_str = nmc_util_strv_for_display (avals, FALSE);
 						g_print (_("Allowed values for '%s' property: %s\n"),
 						         prop_name, avals_str);
-						g_free (avals_str);
 					}
 					cmd_arg_v = nmc_readline (_("Enter '%s' value: "), prop_name);
 				}
@@ -7176,9 +6965,8 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 					g_clear_error (&tmp_err);
 				}
 
-				if (created_ss)
-					nm_connection_add_setting (connection, ss);
-				g_free (prop_name);
+				if (ss_created)
+					nm_connection_add_setting (connection, g_steal_pointer (&ss_created));
 			}
 			break;
 
@@ -7191,20 +6979,24 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 				const char *user_arg = cmd_arg_s ? cmd_arg_s : cmd_arg_p;
 
 				setting_name = ask_check_setting (user_arg,
-								  valid_settings_main,
-								  valid_settings_slave,
-								  valid_settings_str);
+				                                  valid_settings_main,
+				                                  valid_settings_slave,
+				                                  valid_settings_str);
 				if (!setting_name)
 					break;
 
 				setting = nm_connection_get_setting_by_name (connection, setting_name);
 				if (!setting) {
-					setting = nmc_setting_new_for_name (setting_name);
-					if (!setting) {
+					const NMMetaSettingInfoEditor *setting_info;
+
+					setting_info = nm_meta_setting_info_editor_find_by_name (setting_name, FALSE);
+					if (!setting_info) {
 						g_print (_("Error: unknown setting '%s'\n"), setting_name);
 						break;
 					}
-					nmc_setting_custom_init (setting);
+
+					setting = nm_meta_setting_info_editor_new_setting (setting_info,
+					                                                   NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI);
 
 					if (NM_IS_SETTING_WIRELESS (setting))
 						nmc_setting_wireless_connect_handlers (NM_SETTING_WIRELESS (setting));
@@ -7221,7 +7013,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 				nmc_tab_completion.setting = setting;
 
 				/* Switch to level 1 */
-				menu_switch_to_level1 (nmc, &menu_ctx, setting, setting_name, nmc->editor_prompt_color);
+				menu_switch_to_level1 (nmc->nmc_config.use_colors, &menu_ctx, setting, setting_name, nmc->editor_prompt_color);
 
 				if (!cmd_arg_s) {
 					g_print (_("You may edit the following properties: %s\n"), menu_ctx.valid_props_str);
@@ -7279,19 +7071,20 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 				user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
 				if (user_s) {
 					ss = is_setting_valid (connection,
-							       valid_settings_main,
-							       valid_settings_slave,
-							       user_s);
+					                       valid_settings_main,
+					                       valid_settings_slave,
+					                       user_s);
 					if (!ss) {
 						if (check_valid_name (user_s,
 						                      valid_settings_main,
 						                      valid_settings_slave,
-						                      NULL))
+						                      NULL)) {
 							g_print (_("Setting '%s' is not present in the connection.\n"),
-								 user_s);
-						else
+							         user_s);
+						} else {
 							g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
 							         user_s, valid_settings_str);
+						}
 						break;
 					}
 				} else
@@ -7302,12 +7095,14 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 					connection_remove_setting (connection, ss);
 					if (ss == menu_ctx.curr_setting) {
 						/* If we removed the setting we are in, go up */
-						menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
+						menu_switch_to_level0 (nmc->nmc_config.use_colors, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
 						nmc_tab_completion.setting = NULL;  /* for TAB completion */
 					}
 				} else {
+					gs_free char *prop_name = NULL;
 					GError *tmp_err = NULL;
-					char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
+
+					prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
 					if (prop_name) {
 						/* Delete property value */
 						if (!nmc_setting_reset_property (ss, prop_name, &tmp_err)) {
@@ -7319,16 +7114,16 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 						/* If the string is not a property, try it as a setting */
 						NMSetting *s_tmp;
 						s_tmp = is_setting_valid (connection,
-									  valid_settings_main,
-									  valid_settings_slave,
-									  cmd_arg_p);
+						                          valid_settings_main,
+						                          valid_settings_slave,
+						                          cmd_arg_p);
 						if (s_tmp) {
 							/* Remove setting from the connection */
 							connection_remove_setting (connection, s_tmp);
 							/* coverity[copy_paste_error] - suppress Coverity COPY_PASTE_ERROR defect */
 							if (ss == menu_ctx.curr_setting) {
 								/* If we removed the setting we are in, go up */
-								menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
+								menu_switch_to_level0 (nmc->nmc_config.use_colors, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
 								nmc_tab_completion.setting = NULL;  /* for TAB completion */
 							}
 						} else
@@ -7336,7 +7131,6 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 							         tmp_err->message);
 						g_clear_error (&tmp_err);
 					}
-					g_free (prop_name);
 				}
 			}
 			break;
@@ -7360,8 +7154,8 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 					g_print (_("use 'goto <setting>' first, or 'describe <setting>.<property>'\n"));
 				}
 			} else {
+				gs_unref_object NMSetting *ss_free = NULL;
 				NMSetting *ss = NULL;
-				gboolean unref_ss = FALSE;
 				gboolean descr_all;
 				char *user_s;
 
@@ -7370,39 +7164,45 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 				user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
 				if (user_s) {
 					ss = is_setting_valid (connection,
-							       valid_settings_main,
-							       valid_settings_slave,
-							       user_s);
+					                       valid_settings_main,
+					                       valid_settings_slave,
+					                       user_s);
 					if (!ss) {
 						ss = create_setting_by_name (user_s,
-									     valid_settings_main,
-									     valid_settings_slave);
+						                             valid_settings_main,
+						                             valid_settings_slave);
 						if (!ss) {
 							g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
 							         user_s, valid_settings_str);
 							break;
 						}
-						unref_ss = TRUE;
+						ss_free = ss;
 					}
 				} else
 					ss = menu_ctx.curr_setting;
 
-				if (descr_all) {
+				if (!ss) {
+					g_print (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str);
+					g_print (_("use 'goto <setting>' first, or 'describe <setting>.<property>'\n"));
+				} else if (descr_all) {
 					/* Show description for all properties */
 					print_setting_description (ss);
 				} else {
 					GError *tmp_err = NULL;
-					char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
+					gs_free char *prop_name = NULL;
+
+					prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
 					if (prop_name) {
 						/* Show property description */
 						print_property_description (ss, prop_name);
 					} else {
 						/* If the string is not a property, try it as a setting */
 						NMSetting *s_tmp;
+
 						s_tmp = is_setting_valid (connection,
-									  valid_settings_main,
-									  valid_settings_slave,
-									  cmd_arg_p);
+						                          valid_settings_main,
+						                          valid_settings_slave,
+						                          cmd_arg_p);
 						if (s_tmp)
 							print_setting_description (s_tmp);
 						else
@@ -7411,10 +7211,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 							         tmp_err->message);
 						g_clear_error (&tmp_err);
 					}
-					g_free (prop_name);
 				}
-				if (unref_ss)
-					g_object_unref (ss);
 			}
 			break;
 
@@ -7433,6 +7230,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 					user_s = whole_setting ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
 					if (user_s) {
 						const char *s_name;
+
 						s_name = check_valid_name (user_s,
 						                           valid_settings_main,
 						                           valid_settings_slave,
@@ -7453,20 +7251,23 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 						/* Print the whole setting */
 						editor_show_setting (ss, nmc);
 					} else {
+						gs_free char *prop_name = NULL;
 						GError *err = NULL;
-						char *prop_name = is_property_valid (ss, cmd_arg_p, &err);
+
+						prop_name = is_property_valid (ss, cmd_arg_p, &err);
 						if (prop_name) {
 							/* Print one property */
-							char *prop_val = nmc_setting_get_property (ss, prop_name, NULL);
+							gs_free char *prop_val = NULL;
+
+							prop_val = nmc_setting_get_property (ss, prop_name, NULL);
 							g_print ("%s.%s: %s\n", nm_setting_get_name (ss),prop_name , prop_val);
-							g_free (prop_val);
 						} else {
 							/* If the string is not a property, try it as a setting */
 							NMSetting *s_tmp;
 							s_tmp = is_setting_valid (connection,
-										  valid_settings_main,
-										  valid_settings_slave,
-										  cmd_arg_p);
+							                          valid_settings_main,
+							                          valid_settings_slave,
+							                          cmd_arg_p);
 							if (s_tmp) {
 								/* Print the whole setting */
 								editor_show_setting (s_tmp, nmc);
@@ -7476,7 +7277,6 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 								         cmd_arg_s ? "" : _(", neither a valid setting name"));
 							g_clear_error (&err);
 						}
-						g_free (prop_name);
 					}
 				}
 			} else {
@@ -7587,7 +7387,8 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 					 * adding ipv{4,6} settings when not present.
 					 */
 					if (con_tmp) {
-						char *s_name = NULL;
+						gs_free char *s_name = NULL;
+
 						if (menu_ctx.curr_setting)
 							s_name = g_strdup (nm_setting_get_name (menu_ctx.curr_setting));
 
@@ -7598,7 +7399,6 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 						/* Also update setting for menu context and TAB-completion */
 						menu_ctx.curr_setting = s_name ? nm_connection_get_setting_by_name (connection, s_name) : NULL;
 						nmc_tab_completion.setting = menu_ctx.curr_setting;
-						g_free (s_name);
 					}
 				}
 
@@ -7640,7 +7440,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 
 			nmc->nowait_flag = FALSE;
 			nmc->should_wait++;
-			nmc->print_output = NMC_PRINT_PRETTY;
+			nmc->nmc_config_mutable.print_output = NMC_PRINT_PRETTY;
 			if (!nmc_activate_connection (nmc, NM_CONNECTION (rem_con), ifname, ap_nsp, ap_nsp, NULL,
 			                              activate_connection_editor_cb, &tmp_err)) {
 				g_print (_("Error: Cannot activate connection: %s.\n"), tmp_err->message);
@@ -7679,7 +7479,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 		case NMC_EDITOR_MAIN_CMD_BACK:
 			/* Go back (up) an the menu */
 			if (menu_ctx.level == 1) {
-				menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
+				menu_switch_to_level0 (nmc->nmc_config.use_colors, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
 				nmc_tab_completion.setting = NULL;  /* for TAB completion */
 			}
 			break;
@@ -7713,24 +7513,25 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 					g_print (_("Error: show-secrets: %s\n"), tmp_err->message);
 					g_clear_error (&tmp_err);
 				} else
-					nmc->editor_show_secrets = bb;
+					nmc->nmc_config_mutable.show_secrets = bb;
 			} else if (cmd_arg_p && matches (cmd_arg_p, "prompt-color")) {
 				GError *tmp_err = NULL;
-				NmcTermColor color;
+				NMMetaTermColor color;
 				color = nmc_term_color_parse_string (cmd_arg_v ? g_strstrip (cmd_arg_v) : " ", &tmp_err);
 				if (tmp_err) {
 					g_print (_("Error: bad color: %s\n"), tmp_err->message);
 					g_clear_error (&tmp_err);
 				} else {
 					nmc->editor_prompt_color = color;
-					g_free (menu_ctx.main_prompt);
-					if (menu_ctx.level == 0)
-						menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
+					nm_clear_g_free (&menu_ctx.main_prompt);
+					if (menu_ctx.level == 0) {
+						menu_ctx.main_prompt = nmc_colorize (nmc->nmc_config.use_colors, nmc->editor_prompt_color, NM_META_TERM_FORMAT_NORMAL,
 						                                     BASE_PROMPT);
-					else
-						menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
+					} else {
+						menu_ctx.main_prompt = nmc_colorize (nmc->nmc_config.use_colors, nmc->editor_prompt_color, NM_META_TERM_FORMAT_NORMAL,
 						                                     "nmcli %s> ",
 						                                     nm_setting_get_name (menu_ctx.curr_setting));
+					}
 				}
 			} else if (!cmd_arg_p) {
 				g_print (_("Current nmcli configuration:\n"));
@@ -7740,7 +7541,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 				         "prompt-color: %d\n",
 				         nmc->editor_status_line ? "yes" : "no",
 				         nmc->editor_save_confirmation ? "yes" : "no",
-				         nmc->editor_show_secrets ? "yes" : "no",
+				         nmc->nmc_config.show_secrets ? "yes" : "no",
 				         nmc->editor_prompt_color);
 			} else
 				g_print (_("Invalid configuration option '%s'; allowed [%s]\n"),
@@ -7761,19 +7562,11 @@ editor_menu_main (NmCli *nmc, NMConnection *connection)
 			g_print (_("Unknown command: '%s'\n"), cmd_user);
 			break;
 		}
-
-		g_free (cmd_user);
-		g_free (cmd_arg);
-		g_free (cmd_arg_s);
-		g_free (cmd_arg_p);
-		g_free (cmd_arg_v);
 	}
-	g_free (valid_settings_str);
+
 	g_free (menu_ctx.main_prompt);
 	g_strfreev (menu_ctx.valid_props);
 	g_free (menu_ctx.valid_props_str);
-	if (rem_con)
-		g_object_unref (rem_con);
 	g_weak_ref_clear (&weak);
 
 	/* Save history file */
@@ -7798,12 +7591,11 @@ get_ethernet_device_name (NmCli *nmc)
 }
 
 static void
-editor_init_new_connection (NmCli *nmc, NMConnection *connection)
+editor_init_new_connection (NmCli *nmc, NMConnection *connection, const char *slave_type)
 {
 	NMSetting *setting, *base_setting;
 	NMSettingConnection *s_con;
 	const char *con_type;
-	const char *slave_type = NULL;
 
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
@@ -7813,13 +7605,6 @@ editor_init_new_connection (NmCli *nmc, NMConnection *connection)
 
 	nmc_setting_connection_connect_handlers (s_con, connection);
 
-	if (g_strcmp0 (con_type, "bond-slave") == 0)
-		slave_type = NM_SETTING_BOND_SETTING_NAME;
-	if (g_strcmp0 (con_type, "team-slave") == 0)
-		slave_type = NM_SETTING_TEAM_SETTING_NAME;
-	if (g_strcmp0 (con_type, "bridge-slave") == 0)
-		slave_type = NM_SETTING_BRIDGE_SETTING_NAME;
-
 	if (slave_type) {
 		const char *dev_ifname = get_ethernet_device_name (nmc);
 
@@ -7833,11 +7618,14 @@ editor_init_new_connection (NmCli *nmc, NMConnection *connection)
 		              NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
 		              NULL);
 	} else {
+		const NMMetaSettingInfoEditor *setting_info;
+
 		/* Add a "base" setting to the connection by default */
-		base_setting = nmc_setting_new_for_name (con_type);
-		if (!base_setting)
+		setting_info = nm_meta_setting_info_editor_find_by_name (con_type, FALSE);
+		if (!setting_info)
 			return;
-		nmc_setting_custom_init (base_setting);
+		base_setting = nm_meta_setting_info_editor_new_setting (setting_info,
+		                                                        NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI);
 		nm_connection_add_setting (connection, base_setting);
 
 		set_default_interface_name (nmc, s_con);
@@ -7852,18 +7640,16 @@ editor_init_new_connection (NmCli *nmc, NMConnection *connection)
 		}
 
 
-		/* Always add IPv4 and IPv6 settings for non-slave connections */
-		setting = nm_setting_ip4_config_new ();
-		nmc_setting_custom_init (setting);
+		setting = nm_meta_setting_info_editor_new_setting (&nm_meta_setting_infos_editor[NM_META_SETTING_TYPE_IP4_CONFIG],
+		                                                   NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI);
 		nm_connection_add_setting (connection, setting);
 
-		setting = nm_setting_ip6_config_new ();
-		nmc_setting_custom_init (setting);
+		setting = nm_meta_setting_info_editor_new_setting (&nm_meta_setting_infos_editor[NM_META_SETTING_TYPE_IP6_CONFIG],
+		                                                   NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI);
 		nm_connection_add_setting (connection, setting);
 
-		/* Also Proxy Setting */
-		setting = nm_setting_proxy_new ();
-		nmc_setting_custom_init (setting);
+		setting = nm_meta_setting_info_editor_new_setting (&nm_meta_setting_infos_editor[NM_META_SETTING_TYPE_PROXY],
+		                                                   NM_META_ACCESSOR_SETTING_INIT_TYPE_CLI);
 		nm_connection_add_setting (connection, setting);
 	}
 }
@@ -7895,14 +7681,18 @@ editor_init_existing_connection (NMConnection *connection)
 }
 
 static void
-nmc_complete_connection_type (const char *prefix, const NameItem *types)
+nmc_complete_connection_type (const char *prefix)
 {
-	while (types->name) {
-		if (!*prefix || matches (prefix, types->name))
-			g_print ("%s\n", types->name);
-		if (types->alias && (!*prefix || matches (prefix, types->alias)))
-			g_print ("%s\n", types->alias);
-		types++;
+	guint i;
+
+	for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) {
+		const NMMetaSettingInfoEditor *setting_info = &nm_meta_setting_infos_editor[i];
+
+		if (!*prefix || matches (prefix, setting_info->general->setting_name))
+			g_print ("%s\n", setting_info->general->setting_name);
+		if (   setting_info->alias
+		    && (!*prefix || matches (prefix, setting_info->alias)))
+			g_print ("%s\n", setting_info->alias);
 	}
 }
 
@@ -8016,15 +7806,17 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 
 		editor_init_existing_connection (connection);
 	} else {
+		const char *slave_type = NULL;
+
 		/* New connection */
 		if (nmc->complete) {
 			if (type && argc == 0)
-				nmc_complete_connection_type (type, nmc_valid_connection_types);
+				nmc_complete_connection_type (type);
 			goto error;
 		}
 
-		connection_type = check_valid_name (type, nmc_valid_connection_types, NULL, &err1);
-		tmp_str = get_valid_options_string (nmc_valid_connection_types, NULL);
+		connection_type = check_valid_name_toplevel (type, &slave_type, &err1);
+		tmp_str = get_valid_options_string_toplevel ();
 
 		while (!connection_type) {
 			if (!type)
@@ -8035,7 +7827,7 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 
 			type_ask = nmc_readline (EDITOR_PROMPT_CON_TYPE);
 			type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
-			connection_type = check_valid_name (type_ask, nmc_valid_connection_types, NULL, &err1);
+			connection_type = check_valid_name_toplevel (type_ask, &slave_type, &err1);
 			g_free (type_ask);
 		}
 		g_free (tmp_str);
@@ -8048,9 +7840,10 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 		uuid = nm_utils_uuid_generate ();
 		if (con_name)
 			default_name = g_strdup (con_name);
-		else
+		else {
 			default_name = nmc_unique_connection_name (connections,
-			                                           get_name_alias (connection_type, NULL, nmc_valid_connection_types));
+			                                           get_name_alias_toplevel (connection_type, NULL));
+		}
 
 		g_object_set (s_con,
 		              NM_SETTING_CONNECTION_ID, default_name,
@@ -8062,11 +7855,11 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 		nm_connection_add_setting (connection, NM_SETTING (s_con));
 
 		/* Initialize the new connection so that it is valid from the start */
-		editor_init_new_connection (nmc, connection);
+		editor_init_new_connection (nmc, connection, slave_type);
 	}
 
 	/* nmcli runs the editor */
-	nmc->in_editor = TRUE;
+	nmc->nmc_config_mutable.in_editor = TRUE;
 
 	g_print ("\n");
 	g_print (_("===| nmcli interactive connection editor |==="));
@@ -8083,11 +7876,11 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 
 	/* Set global variables for use in TAB completion */
 	nmc_tab_completion.nmc = nmc;
-	nmc_tab_completion.con_type = g_strdup (nm_connection_get_connection_type (connection));
+	nmc_tab_completion.con_type = g_strdup (connection_type);
 	nmc_tab_completion.connection = connection;
 
 	/* Run menu loop */
-	editor_menu_main (nmc, connection);
+	editor_menu_main (nmc, connection, connection_type);
 
 	if (connection)
 		g_object_unref (connection);
@@ -8119,7 +7912,7 @@ modify_connection_cb (GObject *connection,
 		g_error_free (error);
 		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
 	} else {
-		if (nmc->print_output == NMC_PRINT_PRETTY)
+		if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY)
 			g_print (_("Connection '%s' (%s) successfully modified.\n"),
 			         nm_connection_get_id (NM_CONNECTION (connection)),
 			         nm_connection_get_uuid (NM_CONNECTION (connection)));
@@ -8590,26 +8383,8 @@ do_connection_load (NmCli *nmc, int argc, char **argv)
 	return nmc->return_value;
 }
 
-// FIXME: change the text when non-VPN connection types are supported
-#define PROMPT_IMPORT_TYPE PROMPT_VPN_TYPE
 #define PROMPT_IMPORT_FILE N_("File to import: ")
 
-static void
-nmc_complete_vpn_service (const char *prefix)
-{
-	char **services;
-	char **candidate;
-
-	services = nm_vpn_plugin_info_list_get_service_types (NULL, FALSE, TRUE);
-	for (candidate = services; *candidate; candidate++) {
-		if (!*prefix && g_str_has_prefix (*candidate, NM_DBUS_INTERFACE))
-			continue;
-		if (!*prefix || matches (prefix, *candidate))
-			g_print ("%s\n", *candidate);
-	}
-	g_strfreev (services);
-}
-
 static NMCResultCode
 do_connection_import (NmCli *nmc, int argc, char **argv)
 {
@@ -8634,7 +8409,7 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 		g_assert (!nmc->complete);
 
 		if (nmc->ask) {
-			type_ask = nmc_readline (gettext (PROMPT_IMPORT_TYPE));
+			type_ask = nmc_readline (gettext (NM_META_TEXT_PROMPT_VPN_TYPE));
 			filename_ask = nmc_readline (gettext (PROMPT_IMPORT_FILE));
 			type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
 			filename = filename_ask = filename_ask ? g_strstrip (filename_ask) : NULL;
@@ -8659,7 +8434,7 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 			}
 
 			if (argc == 1 && nmc->complete)
-				nmc_complete_vpn_service (*argv);
+				complete_option ((const NMMetaAbstractInfo *) nm_meta_property_info_vpn_service_type, *argv, NULL);
 
 			if (!type)
 				type = *argv;
@@ -8934,8 +8709,8 @@ nmcli_con_tab_completion (const char *text, int start, int end)
 		generator_func = gen_func_connection_names;
 	} else if (g_strcmp0 (rl_prompt, PROMPT_ACTIVE_CONNECTIONS) == 0) {
 		generator_func = gen_func_active_connection_names;
-	} else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_TYPE) == 0) {
-		generator_func = gen_func_vpn_types;
+	} else if (g_strcmp0 (rl_prompt, NM_META_TEXT_PROMPT_VPN_TYPE) == 0) {
+		return _meta_abstract_complete ((const NMMetaAbstractInfo *) nm_meta_property_info_vpn_service_type, text);
 	} else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_FILE) == 0) {
 		rl_attempted_completion_over = 0;
 		rl_complete_with_tilde_expansion = 1;