about summary refs log tree commit diff
path: root/clients/cli
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2018-06-04 00:07:45 +0200
committerMichael Biebl <biebl@debian.org>2018-06-04 00:07:45 +0200
commit04bc9e1cd3544445d883ad29ea108c1645c8e7b7 (patch)
treed10c354b1b980ca8a7b9e48ec9019e8ed88bde2b /clients/cli
parentee9c73a923909e23a649407be77e25235d769e25 (diff)
New upstream version 1.11.4 upstream/1.11.4
Diffstat (limited to 'clients/cli')
-rw-r--r--clients/cli/agent.c3
-rw-r--r--clients/cli/common.c398
-rw-r--r--clients/cli/common.h14
-rw-r--r--clients/cli/connections.c1486
-rw-r--r--clients/cli/connections.h4
-rw-r--r--clients/cli/devices.c80
-rw-r--r--clients/cli/devices.h4
-rw-r--r--clients/cli/general.c115
-rw-r--r--clients/cli/meson.build49
-rw-r--r--clients/cli/nmcli.c430
-rw-r--r--clients/cli/nmcli.h34
-rw-r--r--clients/cli/polkit-agent.c73
-rw-r--r--clients/cli/settings.c13
-rw-r--r--clients/cli/utils.c228
-rw-r--r--clients/cli/utils.h85
15 files changed, 1612 insertions, 1404 deletions
diff --git a/clients/cli/agent.c b/clients/cli/agent.c
index 488b16f4..bbfe47fb 100644
--- a/clients/cli/agent.c
+++ b/clients/cli/agent.c
@@ -104,7 +104,7 @@ get_secrets_from_user (const char *request_id,
 			rl_startup_hook = set_deftext;
 			pre_input_deftext = g_strdup (secret->value);
 		}
-		pwd = nmc_readline ("%s (%s): ", secret->name, secret->prop_name);
+		pwd = nmc_readline ("%s (%s): ", secret->pretty_name, secret->entry_id);
 
 		/* No password provided, cancel the secrets. */
 		if (!pwd)
@@ -136,7 +136,6 @@ secrets_requested (NMSecretAgentSimple *agent,
 		nm_secret_agent_simple_response (agent, request_id, NULL);
 }
 
-
 static NMCResultCode
 do_agent_secret (NmCli *nmc, int argc, char **argv)
 {
diff --git a/clients/cli/common.c b/clients/cli/common.c
index 0f5aea57..e566de47 100644
--- a/clients/cli/common.c
+++ b/clients/cli/common.c
@@ -16,7 +16,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2012 - 2014 Red Hat, Inc.
+ * Copyright 2012 - 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -26,12 +26,10 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
-#include <termios.h>
 #include <sys/ioctl.h>
 #include <readline/readline.h>
 #include <readline/history.h>
 
-#include "nm-utils/nm-hash-utils.h"
 #include "nm-vpn-helpers.h"
 #include "nm-client-utils.h"
 
@@ -102,24 +100,19 @@ _ip_config_get_routes (NMIPConfig *cfg)
 }
 
 static gconstpointer
-_metagen_ip4_config_get_fcn (const NMMetaEnvironment *environment,
-                             gpointer environment_user_data,
-                             const NmcMetaGenericInfo *info,
-                             gpointer target,
-                             NMMetaAccessorGetType get_type,
-                             NMMetaAccessorGetFlags get_flags,
-                             NMMetaAccessorGetOutFlags *out_flags,
-                             gpointer *out_to_free)
+_metagen_ip4_config_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS)
 {
 	NMIPConfig *cfg4 = target;
 	GPtrArray *ptr_array;
 	char **arr;
 	const char *const*arrc;
 	guint i = 0;
+	const char *str;
 
 	nm_assert (info->info_type < _NMC_GENERIC_INFO_TYPE_IP4_CONFIG_NUM);
 
-	NMC_HANDLE_TERMFORMAT (NM_META_TERM_COLOR_NORMAL);
+	NMC_HANDLE_COLOR (NM_META_COLOR_NONE);
+	NM_SET_OUT (out_is_default, TRUE);
 
 	switch (info->info_type) {
 	case NMC_GENERIC_INFO_TYPE_IP4_CONFIG_ADDRESS:
@@ -140,7 +133,9 @@ _metagen_ip4_config_get_fcn (const NMMetaEnvironment *environment,
 			arr = NULL;
 		goto arr_out;
 	case NMC_GENERIC_INFO_TYPE_IP4_CONFIG_GATEWAY:
-		return nm_ip_config_get_gateway (cfg4);
+		str = nm_ip_config_get_gateway (cfg4);
+		NM_SET_OUT (out_is_default, !str);
+		return str;
 	case NMC_GENERIC_INFO_TYPE_IP4_CONFIG_ROUTE:
 		if (!NM_FLAGS_HAS (get_flags, NM_META_ACCESSOR_GET_FLAGS_ACCEPT_STRV))
 			return NULL;
@@ -168,34 +163,31 @@ _metagen_ip4_config_get_fcn (const NMMetaEnvironment *environment,
 	g_return_val_if_reached (NULL);
 
 arrc_out:
+	NM_SET_OUT (out_is_default, !arrc || !arrc[0]);
 	*out_flags |= NM_META_ACCESSOR_GET_OUT_FLAGS_STRV;
 	return arrc;
 
 arr_out:
+	NM_SET_OUT (out_is_default, !arr || !arr[0]);
 	*out_flags |= NM_META_ACCESSOR_GET_OUT_FLAGS_STRV;
 	*out_to_free = arr;
 	return arr;
 }
 
 static gconstpointer
-_metagen_ip6_config_get_fcn (const NMMetaEnvironment *environment,
-                             gpointer environment_user_data,
-                             const NmcMetaGenericInfo *info,
-                             gpointer target,
-                             NMMetaAccessorGetType get_type,
-                             NMMetaAccessorGetFlags get_flags,
-                             NMMetaAccessorGetOutFlags *out_flags,
-                             gpointer *out_to_free)
+_metagen_ip6_config_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS)
 {
 	NMIPConfig *cfg6 = target;
 	GPtrArray *ptr_array;
 	char **arr;
 	const char *const*arrc;
 	guint i = 0;
+	const char *str;
 
 	nm_assert (info->info_type < _NMC_GENERIC_INFO_TYPE_IP6_CONFIG_NUM);
 
-	NMC_HANDLE_TERMFORMAT (NM_META_TERM_COLOR_NORMAL);
+	NMC_HANDLE_COLOR (NM_META_COLOR_NONE);
+	NM_SET_OUT (out_is_default, TRUE);
 
 	switch (info->info_type) {
 	case NMC_GENERIC_INFO_TYPE_IP6_CONFIG_ADDRESS:
@@ -216,7 +208,9 @@ _metagen_ip6_config_get_fcn (const NMMetaEnvironment *environment,
 			arr = NULL;
 		goto arr_out;
 	case NMC_GENERIC_INFO_TYPE_IP6_CONFIG_GATEWAY:
-		return nm_ip_config_get_gateway (cfg6);
+		str = nm_ip_config_get_gateway (cfg6);
+		NM_SET_OUT (out_is_default, !str);
+		return str;
 	case NMC_GENERIC_INFO_TYPE_IP6_CONFIG_ROUTE:
 		if (!NM_FLAGS_HAS (get_flags, NM_META_ACCESSOR_GET_FLAGS_ACCEPT_STRV))
 			return NULL;
@@ -239,10 +233,12 @@ _metagen_ip6_config_get_fcn (const NMMetaEnvironment *environment,
 	g_return_val_if_reached (NULL);
 
 arrc_out:
+	NM_SET_OUT (out_is_default, !arrc || !arrc[0]);
 	*out_flags |= NM_META_ACCESSOR_GET_OUT_FLAGS_STRV;
 	return arrc;
 
 arr_out:
+	NM_SET_OUT (out_is_default, !arr || !arr[0]);
 	*out_flags |= NM_META_ACCESSOR_GET_OUT_FLAGS_STRV;
 	*out_to_free = arr;
 	return arr;
@@ -259,11 +255,6 @@ const NmcMetaGenericInfo *const metagen_ip4_config[_NMC_GENERIC_INFO_TYPE_IP4_CO
 	_METAGEN_IP4_CONFIG (NMC_GENERIC_INFO_TYPE_IP4_CONFIG_WINS,    "WINS"),
 };
 
-static const NmcMetaGenericInfo *const metagen_ip4_config_group[] = {
-	NMC_META_GENERIC_WITH_NESTED ("IP4", metagen_ip4_config, .name_header = N_("GROUP")),
-	NULL,
-};
-
 const NmcMetaGenericInfo *const metagen_ip6_config[_NMC_GENERIC_INFO_TYPE_IP6_CONFIG_NUM + 1] = {
 #define _METAGEN_IP6_CONFIG(type, name) \
 	[type] = NMC_META_GENERIC(name, .info_type = type, .get_fcn = _metagen_ip6_config_get_fcn)
@@ -274,14 +265,9 @@ const NmcMetaGenericInfo *const metagen_ip6_config[_NMC_GENERIC_INFO_TYPE_IP6_CO
 	_METAGEN_IP6_CONFIG (NMC_GENERIC_INFO_TYPE_IP6_CONFIG_DOMAIN,  "DOMAIN"),
 };
 
-static const NmcMetaGenericInfo *const metagen_ip6_config_group[] = {
-	NMC_META_GENERIC_WITH_NESTED ("IP6", metagen_ip6_config, .name_header = N_("GROUP")),
-	NULL,
-};
-
 /*****************************************************************************/
 
-const NmcMetaGenericInfo *const nmc_fields_dhcp4_config[] = {
+const NmcMetaGenericInfo *const nmc_fields_dhcp_config[] = {
 	NMC_META_GENERIC ("GROUP"),    /* 0 */
 	NMC_META_GENERIC ("OPTION"),   /* 1 */
 	NULL,
@@ -297,12 +283,6 @@ const NmcMetaGenericInfo *const nmc_fields_ip6_config[] = {
 	NULL,
 };
 
-const NmcMetaGenericInfo *const nmc_fields_dhcp6_config[] = {
-	NMC_META_GENERIC ("GROUP"),    /* 0 */
-	NMC_META_GENERIC ("OPTION"),   /* 1 */
-	NULL,
-};
-
 gboolean
 print_ip4_config (NMIPConfig *cfg4,
                   const NmcConfig *nmc_config,
@@ -320,7 +300,7 @@ print_ip4_config (NMIPConfig *cfg4,
 	if (!nmc_print (nmc_config,
 	                (gpointer[]) { cfg4, NULL },
 	                NULL,
-	                (const NMMetaAbstractInfo *const*) metagen_ip4_config_group,
+	                NMC_META_GENERIC_GROUP ("IP4", metagen_ip4_config, N_("GROUP")),
 	                field_str,
 	                &error)) {
 		return FALSE;
@@ -346,7 +326,7 @@ print_ip6_config (NMIPConfig *cfg6,
 	if (!nmc_print (nmc_config,
 	                (gpointer[]) { cfg6, NULL },
 	                NULL,
-	                (const NMMetaAbstractInfo *const*) metagen_ip6_config_group,
+	                NMC_META_GENERIC_GROUP ("IP6", metagen_ip6_config, N_("GROUP")),
 	                field_str,
 	                &error)) {
 		return FALSE;
@@ -355,82 +335,37 @@ print_ip6_config (NMIPConfig *cfg6,
 }
 
 gboolean
-print_dhcp4_config (NMDhcpConfig *dhcp4,
-                    const NmcConfig *nmc_config,
-                    const char *group_prefix,
-                    const char *one_field)
+print_dhcp_config (NMDhcpConfig *dhcp,
+                   const NmcConfig *nmc_config,
+                   const char *group_prefix,
+                   const char *one_field)
 {
 	GHashTable *table;
 	const NMMetaAbstractInfo *const*tmpl;
 	NmcOutputField *arr;
 
-	if (dhcp4 == NULL)
+	if (dhcp == NULL)
 		return FALSE;
 
-	table = nm_dhcp_config_get_options (dhcp4);
+	table = nm_dhcp_config_get_options (dhcp);
 	if (table) {
-		GHashTableIter table_iter;
-		gpointer key, value;
 		char **options_arr = NULL;
-		int i = 0;
 		NMC_OUTPUT_DATA_DEFINE_SCOPED (out);
+		gs_free const char **keys = NULL;
+		guint i, nkeys;
 
-		tmpl = (const NMMetaAbstractInfo *const*) nmc_fields_dhcp4_config;
+		tmpl = (const NMMetaAbstractInfo *const*) nmc_fields_dhcp_config;
 		out_indices = parse_output_fields (one_field,
 		                                   tmpl, FALSE, NULL, NULL);
 		arr = nmc_dup_fields_array (tmpl, NMC_OF_FLAG_FIELD_NAMES);
 		g_ptr_array_add (out.output_data, arr);
 
-		options_arr = g_new (char *, g_hash_table_size (table) + 1);
-		g_hash_table_iter_init (&table_iter, table);
-		while (g_hash_table_iter_next (&table_iter, &key, &value))
-			options_arr[i++] = g_strdup_printf ("%s = %s", (char *) key, (char *) value);
-		options_arr[i] = NULL;
+		keys = (const char **) g_hash_table_get_keys_as_array (table, &nkeys);
+		nm_utils_strv_sort (keys, nkeys);
 
-		arr = nmc_dup_fields_array (tmpl, NMC_OF_FLAG_SECTION_PREFIX);
-		set_val_strc (arr, 0, group_prefix);
-		set_val_arr  (arr, 1, options_arr);
-		g_ptr_array_add (out.output_data, arr);
-
-		print_data_prepare_width (out.output_data);
-		print_data (nmc_config, out_indices, NULL, 0, &out);
-
-		return TRUE;
-	}
-	return FALSE;
-}
-
-gboolean
-print_dhcp6_config (NMDhcpConfig *dhcp6,
-                    const NmcConfig *nmc_config,
-                    const char *group_prefix,
-                    const char *one_field)
-{
-	GHashTable *table;
-	const NMMetaAbstractInfo *const*tmpl;
-	NmcOutputField *arr;
-
-	if (dhcp6 == NULL)
-		return FALSE;
-
-	table = nm_dhcp_config_get_options (dhcp6);
-	if (table) {
-		GHashTableIter table_iter;
-		gpointer key, value;
-		char **options_arr = NULL;
-		int i = 0;
-		NMC_OUTPUT_DATA_DEFINE_SCOPED (out);
-
-		tmpl = (const NMMetaAbstractInfo *const*) nmc_fields_dhcp6_config;
-		out_indices = parse_output_fields (one_field,
-		                                   tmpl, FALSE, NULL, NULL);
-		arr = nmc_dup_fields_array (tmpl, NMC_OF_FLAG_FIELD_NAMES);
-		g_ptr_array_add (out.output_data, arr);
-
-		options_arr = g_new (char *, g_hash_table_size (table) + 1);
-		g_hash_table_iter_init (&table_iter, table);
-		while (g_hash_table_iter_next (&table_iter, &key, &value))
-			options_arr[i++] = g_strdup_printf ("%s = %s", (char *) key, (char *) value);
+		options_arr = g_new (char *, nkeys + 1);
+		for (i = 0; i < nkeys; i++)
+			options_arr[i] = g_strdup_printf ("%s = %s", keys[i], (const char *) g_hash_table_lookup (table, keys[i]));
 		options_arr[i] = NULL;
 
 		arr = nmc_dup_fields_array (tmpl, NMC_OF_FLAG_SECTION_PREFIX);
@@ -451,8 +386,11 @@ print_dhcp6_config (NMDhcpConfig *dhcp6,
  * @connections: array of NMConnections to search in
  * @filter_type: "id", "uuid", "path" or %NULL
  * @filter_val: connection to find (connection name, UUID or path)
- * @start: where to start in @list. The location is updated so that the function
- *   can be called multiple times (for connections with the same name).
+ * @out_result: if not NULL, attach all matching connection to this
+ *   list. If necessary, a new array will be allocated. If the array
+ *   already contains a connection, it will not be added a second time.
+ *   All object are referenced by the array. If the function allocates
+ *   a new array, it will set the free function to g_object_unref.
  * @complete: print possible completions
  *
  * Find a connection in @list according to @filter_val. @filter_type determines
@@ -467,64 +405,153 @@ NMConnection *
 nmc_find_connection (const GPtrArray *connections,
                      const char *filter_type,
                      const char *filter_val,
-                     int *start,
+                     GPtrArray **out_result,
                      gboolean complete)
 {
 	NMConnection *connection;
-	NMConnection *found = NULL;
-	int i;
-	const char *id;
-	const char *uuid;
-	const char *path, *path_num;
+	NMConnection *best_candidate = NULL;
+	GPtrArray *result = out_result ? *out_result : NULL;
+	guint i, j;
 
-	for (i = start ? *start : 0; i < connections->len; i++) {
-		connection = NM_CONNECTION (connections->pdata[i]);
+	nm_assert (connections);
+	nm_assert (filter_val);
 
-		id = nm_connection_get_id (connection);
-		uuid = nm_connection_get_uuid (connection);
-		path = nm_connection_get_path (connection);
-		path_num = path ? strrchr (path, '/') + 1 : NULL;
+	for (i = 0; i < connections->len; i++) {
+		const char *v, *v_num;
+
+		connection = NM_CONNECTION (connections->pdata[i]);
 
 		/* When filter_type is NULL, compare connection ID (filter_val)
 		 * against all types. Otherwise, only compare against the specific
 		 * type. If 'path' filter type is specified, comparison against
 		 * numeric index (in addition to the whole path) is allowed.
 		 */
-		if (!filter_type || strcmp (filter_type, "id")  == 0) {
+		if (NM_IN_STRSET (filter_type, NULL, "id")) {
+			v = nm_connection_get_id (connection);
 			if (complete)
-				nmc_complete_strings (filter_val, id, NULL);
-			if (strcmp (filter_val, id) == 0)
+				nmc_complete_strings (filter_val, v, NULL);
+			if (nm_streq0 (filter_val, v))
 				goto found;
 		}
 
-		if (!filter_type || strcmp (filter_type, "uuid") == 0) {
+		if (NM_IN_STRSET (filter_type, NULL, "uuid")) {
+			v = nm_connection_get_uuid (connection);
 			if (complete && (filter_type || *filter_val))
-				nmc_complete_strings (filter_val, uuid, NULL);
-			if (strcmp (filter_val, uuid) == 0)
+				nmc_complete_strings (filter_val, v, NULL);
+			if (nm_streq0 (filter_val, v))
 				goto found;
 		}
 
-		if (!filter_type || strcmp (filter_type, "path") == 0) {
+		if (NM_IN_STRSET (filter_type, NULL, "path")) {
+			v = nm_connection_get_path (connection);
+			v_num = nm_utils_dbus_path_get_last_component (v);
 			if (complete && (filter_type || *filter_val))
-				nmc_complete_strings (filter_val, path, filter_type ? path_num : NULL, NULL);
-		        if (g_strcmp0 (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0))
+				nmc_complete_strings (filter_val, v, filter_type ? v_num : NULL, NULL);
+			if (   nm_streq0 (filter_val, v)
+			    || (filter_type && nm_streq0 (filter_val, v_num)))
 				goto found;
 		}
 
 		continue;
 found:
-		if (!start)
+		if (!out_result)
 			return connection;
-		if (found) {
-			*start = i;
-			return found;
+		if (!best_candidate)
+			best_candidate = connection;
+		if (!result)
+			result = g_ptr_array_new_with_free_func (g_object_unref);
+		for (j = 0; j < result->len; j++) {
+			if (connection == result->pdata[j])
+				break;
+		}
+		if (j == result->len)
+			g_ptr_array_add (result, g_object_ref (connection));
+	}
+
+	NM_SET_OUT (out_result, result);
+	return best_candidate;
+}
+
+NMActiveConnection *
+nmc_find_active_connection (const GPtrArray *active_cons,
+                            const char *filter_type,
+                            const char *filter_val,
+                            GPtrArray **out_result,
+                            gboolean complete)
+{
+	guint i, j;
+	NMActiveConnection *best_candidate = NULL;
+	GPtrArray *result = out_result ? *out_result : NULL;
+
+	nm_assert (filter_val);
+
+	for (i = 0; i < active_cons->len; i++) {
+		NMRemoteConnection *con;
+		NMActiveConnection *candidate = g_ptr_array_index (active_cons, i);
+		const char *v, *v_num;
+
+		con = nm_active_connection_get_connection (candidate);
+
+		/* When filter_type is NULL, compare connection ID (filter_val)
+		 * against all types. Otherwise, only compare against the specific
+		 * type. If 'path' or 'apath' filter types are specified, comparison
+		 * against numeric index (in addition to the whole path) is allowed.
+		 */
+		if (NM_IN_STRSET (filter_type, NULL, "id")) {
+			v = nm_active_connection_get_id (candidate);
+			if (complete)
+				nmc_complete_strings (filter_val, v, NULL);
+			if (nm_streq0 (filter_val, v))
+				goto found;
+		}
+
+		if (NM_IN_STRSET (filter_type, NULL, "uuid")) {
+			v = nm_active_connection_get_uuid (candidate);
+			if (complete && (filter_type || *filter_val))
+				nmc_complete_strings (filter_val, v, NULL);
+			if (nm_streq0 (filter_val, v))
+				goto found;
+		}
+
+		if (NM_IN_STRSET (filter_type, NULL, "path")) {
+			v = con ? nm_connection_get_path (NM_CONNECTION (con)) : NULL;
+			v_num = nm_utils_dbus_path_get_last_component (v);
+			if (complete && (filter_type || *filter_val))
+				nmc_complete_strings (filter_val, v, filter_type ? v_num : NULL, NULL);
+			if (   nm_streq0 (filter_val, v)
+			    || (filter_type && nm_streq0 (filter_val, v_num)))
+				goto found;
+		}
+
+		if (NM_IN_STRSET (filter_type, NULL, "apath")) {
+			v = nm_object_get_path (NM_OBJECT (candidate));
+			v_num = nm_utils_dbus_path_get_last_component (v);
+			if (complete && (filter_type || *filter_val))
+				nmc_complete_strings (filter_val, v, filter_type ? v_num : NULL, NULL);
+			if (   nm_streq0 (filter_val, v)
+			    || (filter_type && nm_streq0 (filter_val, v_num)))
+				goto found;
+		}
+
+		continue;
+
+found:
+		if (!out_result)
+			return candidate;
+		if (!best_candidate)
+			best_candidate = candidate;
+		if (!result)
+			result = g_ptr_array_new_with_free_func (g_object_unref);
+		for (j = 0; j < result->len; j++) {
+			if (candidate == result->pdata[j])
+				break;
 		}
-		found = connection;
+		if (j == result->len)
+			g_ptr_array_add (result, g_object_ref (candidate));
 	}
 
-	if (start)
-		*start = 0;
-	return found;
+	NM_SET_OUT (out_result, result);
+	return best_candidate;
 }
 
 static gboolean
@@ -532,10 +559,10 @@ vpn_openconnect_get_secrets (NMConnection *connection, GPtrArray *secrets)
 {
 	GError *error = NULL;
 	NMSettingVpn *s_vpn;
-	const char *vpn_type, *gw, *port;
-	char *cookie = NULL;
-	char *gateway = NULL;
-	char *gwcert = NULL;
+	const char *gw, *port;
+	gs_free char *cookie = NULL;
+	gs_free char *gateway = NULL;
+	gs_free char *gwcert = NULL;
 	int status = 0;
 	int i;
 	gboolean ret;
@@ -547,8 +574,7 @@ vpn_openconnect_get_secrets (NMConnection *connection, GPtrArray *secrets)
 		return FALSE;
 
 	s_vpn = nm_connection_get_setting_vpn (connection);
-	vpn_type = nm_setting_vpn_get_service_type (s_vpn);
-	if (g_strcmp0 (vpn_type, NM_DBUS_INTERFACE ".openconnect"))
+	if (!nm_streq0 (nm_setting_vpn_get_service_type (s_vpn), NM_SECRET_AGENT_VPN_TYPE_OPENCONNECT))
 		return FALSE;
 
 	/* Get gateway and port */
@@ -571,34 +597,31 @@ vpn_openconnect_get_secrets (NMConnection *connection, GPtrArray *secrets)
 
 	/* Append port to the host value */
 	if (gateway && port) {
-		char *tmp = gateway;
-		gateway = g_strdup_printf ("%s%s", gateway, port);
-		g_free (tmp);
+		gs_free char *tmp = gateway;
+
+		gateway = g_strdup_printf ("%s%s", tmp, port);
 	}
 
 	/* Fill secrets to the array */
 	for (i = 0; i < secrets->len; i++) {
 		NMSecretAgentSimpleSecret *secret = secrets->pdata[i];
 
-		if (!g_strcmp0 (secret->vpn_type, vpn_type)) {
-			if (!g_strcmp0 (secret->vpn_property, "cookie")) {
-				g_free (secret->value);
-				secret->value = cookie;
-				cookie = NULL;
-			} else if (!g_strcmp0 (secret->vpn_property, "gateway")) {
-				g_free (secret->value);
-				secret->value = gateway;
-				gateway = NULL;
-			} else if (!g_strcmp0 (secret->vpn_property, "gwcert")) {
-				g_free (secret->value);
-				secret->value = gwcert;
-				gwcert = NULL;
-			}
+		if (secret->secret_type != NM_SECRET_AGENT_SECRET_TYPE_VPN_SECRET)
+			continue;
+		if (!nm_streq0 (secret->vpn_type, NM_SECRET_AGENT_VPN_TYPE_OPENCONNECT))
+			continue;
+
+		if (nm_streq0 (secret->entry_id, NM_SECRET_AGENT_ENTRY_ID_PREFX_VPN_SECRET "cookie")) {
+			g_free (secret->value);
+			secret->value = g_steal_pointer (&cookie);
+		} else if (nm_streq0 (secret->entry_id, NM_SECRET_AGENT_ENTRY_ID_PREFX_VPN_SECRET "gateway")) {
+			g_free (secret->value);
+			secret->value = g_steal_pointer (&gateway);
+		} else if (nm_streq0 (secret->entry_id, NM_SECRET_AGENT_ENTRY_ID_PREFX_VPN_SECRET "gwcert")) {
+			g_free (secret->value);
+			secret->value = g_steal_pointer (&gwcert);
 		}
 	}
-	g_free (cookie);
-	g_free (gateway);
-	g_free (gwcert);
 
 	return TRUE;
 }
@@ -625,7 +648,7 @@ get_secrets_from_user (const char *request_id,
 
 		/* First try to find the password in provided passwords file,
 		 * then ask user. */
-		if (pwds_hash && (pwd = g_hash_table_lookup (pwds_hash, secret->prop_name))) {
+		if (pwds_hash && (pwd = g_hash_table_lookup (pwds_hash, secret->entry_id))) {
 			pwd = g_strdup (pwd);
 		} else {
 			if (ask) {
@@ -641,8 +664,10 @@ get_secrets_from_user (const char *request_id,
 				}
 				if (msg)
 					g_print ("%s\n", msg);
-				pwd = nmc_readline_echo (secret->password ? echo_on : TRUE,
-				                         "%s (%s): ", secret->name, secret->prop_name);
+				pwd = nmc_readline_echo (secret->is_secret
+				                         ? echo_on
+				                         : TRUE,
+				                         "%s (%s): ", secret->pretty_name, secret->entry_id);
 				if (!pwd)
 					pwd = g_strdup ("");
 			} else {
@@ -650,7 +675,7 @@ get_secrets_from_user (const char *request_id,
 					g_print ("%s\n", msg);
 				g_printerr (_("Warning: password for '%s' not given in 'passwd-file' "
 				              "and nmcli cannot ask without '--ask' option.\n"),
-				            secret->prop_name);
+				            secret->entry_id);
 			}
 		}
 		/* No password provided, cancel the secrets. */
@@ -883,6 +908,31 @@ nmc_readline (const char *prompt_fmt, ...)
 	return str;
 }
 
+static void
+nmc_secret_redisplay (void)
+{
+	int save_point = rl_point;
+	int save_end = rl_end;
+	char *save_line_buffer = rl_line_buffer;
+	const char *subst = nmc_password_subst_char ();
+	int subst_len = strlen (subst);
+	int i;
+
+	rl_point = g_utf8_strlen (save_line_buffer, save_point) * subst_len;
+	rl_end = g_utf8_strlen (rl_line_buffer, -1) * subst_len;
+	rl_line_buffer = g_slice_alloc (rl_end + 1);
+
+	for (i = 0; i + subst_len <= rl_end; i += subst_len)
+		memcpy (&rl_line_buffer[i], subst, subst_len);
+	rl_line_buffer[i] = '\0';
+
+	rl_redisplay ();
+	g_slice_free1 (rl_end + 1, rl_line_buffer);
+	rl_line_buffer = save_line_buffer;
+	rl_end = save_end;
+	rl_point = save_point;
+}
+
 /**
  * nmc_readline_echo:
  *
@@ -894,29 +944,28 @@ nmc_readline_echo (gboolean echo_on, const char *prompt_fmt, ...)
 {
 	va_list args;
 	char *prompt, *str;
-	struct termios termios_orig, termios_new;
+	HISTORY_STATE *saved_history;
+	HISTORY_STATE passwd_history = { 0, };
 
 	va_start (args, prompt_fmt);
 	prompt = g_strdup_vprintf (prompt_fmt, args);
 	va_end (args);
 
-	/* Disable echoing characters */
+	/* Hide the actual password */
 	if (!echo_on) {
-		tcgetattr (STDIN_FILENO, &termios_orig);
-		termios_new = termios_orig;
-		termios_new.c_lflag &= ~(ECHO);
-		tcsetattr (STDIN_FILENO, TCSADRAIN, &termios_new);
+		saved_history = history_get_history_state ();
+		history_set_history_state (&passwd_history);
+		rl_redisplay_function = nmc_secret_redisplay;
 	}
 
 	str = nmc_readline_helper (prompt);
 
 	g_free (prompt);
 
-	/* Restore original terminal settings */
+	/* Restore the non-hiding behavior */
 	if (!echo_on) {
-		tcsetattr (STDIN_FILENO, TCSADRAIN, &termios_orig);
-		/* New line - setting ECHONL | ICANON did not help */
-		fprintf (stdout, "\n");
+		rl_redisplay_function = rl_redisplay;
+		history_set_history_state (saved_history);
 	}
 
 	return str;
@@ -1215,9 +1264,8 @@ nmc_do_cmd (NmCli *nmc, const NMCCommand cmds[], const char *cmd, int argc, char
 		/* A valid command was specified. */
 		if (c->usage && argc == 2 && nmc->complete)
 			nmc_complete_help (*(argv+1));
-		if (c->usage && nmc_arg_is_help (*(argv+1))) {
-			if (!nmc->complete)
-				c->usage ();
+		if (!nmc->complete && c->usage && nmc_arg_is_help (*(argv+1))) {
+			c->usage ();
 			g_simple_async_result_complete_in_idle (simple);
 			g_object_unref (simple);
 		} else {
diff --git a/clients/cli/common.h b/clients/cli/common.h
index 354b2a5f..8f4e47c2 100644
--- a/clients/cli/common.h
+++ b/clients/cli/common.h
@@ -27,15 +27,20 @@
 
 gboolean print_ip4_config (NMIPConfig *cfg4, const NmcConfig *nmc_config, const char *one_field);
 gboolean print_ip6_config (NMIPConfig *cfg6, const NmcConfig *nmc_config, const char *group_prefix, const char *one_field);
-gboolean print_dhcp4_config (NMDhcpConfig *dhcp4, const NmcConfig *nmc_config, const char *group_prefix, const char *one_field);
-gboolean print_dhcp6_config (NMDhcpConfig *dhcp6, const NmcConfig *nmc_config, const char *group_prefix, const char *one_field);
+gboolean print_dhcp_config (NMDhcpConfig *dhcp, const NmcConfig *nmc_config, const char *group_prefix, const char *one_field);
 
 NMConnection *nmc_find_connection (const GPtrArray *connections,
                                    const char *filter_type,
                                    const char *filter_val,
-                                   int *start,
+                                   GPtrArray **out_result,
                                    gboolean complete);
 
+NMActiveConnection *nmc_find_active_connection (const GPtrArray *active_cons,
+                                                const char *filter_type,
+                                                const char *filter_val,
+                                                GPtrArray **out_result,
+                                                gboolean complete);
+
 void nmc_secrets_requested (NMSecretAgentSimple *agent,
                             const char          *request_id,
                             const char          *title,
@@ -78,8 +83,7 @@ void nmc_complete_bool (const char *prefix);
 const char *nmc_error_get_simple_message (GError *error);
 
 extern const NmcMetaGenericInfo *const metagen_ip4_config[];
-extern const NmcMetaGenericInfo *const nmc_fields_dhcp4_config[];
 extern const NmcMetaGenericInfo *const nmc_fields_ip6_config[];
-extern const NmcMetaGenericInfo *const nmc_fields_dhcp6_config[];
+extern const NmcMetaGenericInfo *const nmc_fields_dhcp_config[];
 
 #endif /* NMC_COMMON_H */
diff --git a/clients/cli/connections.c b/clients/cli/connections.c
index e0f55c53..a4f129b6 100644
--- a/clients/cli/connections.c
+++ b/clients/cli/connections.c
@@ -14,7 +14,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2010 - 2017 Red Hat, Inc.
+ * Copyright 2010 - 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -31,8 +31,6 @@
 #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"
@@ -78,6 +76,82 @@ struct _OptionInfo {
 
 /*****************************************************************************/
 
+NM_UTILS_LOOKUP_STR_DEFINE_STATIC (active_connection_state_to_string, NMActiveConnectionState,
+	NM_UTILS_LOOKUP_DEFAULT (N_("unknown")),
+	NM_UTILS_LOOKUP_ITEM (NM_ACTIVE_CONNECTION_STATE_ACTIVATING,   N_("activating")),
+	NM_UTILS_LOOKUP_ITEM (NM_ACTIVE_CONNECTION_STATE_ACTIVATED,    N_("activated")),
+	NM_UTILS_LOOKUP_ITEM (NM_ACTIVE_CONNECTION_STATE_DEACTIVATING, N_("deactivating")),
+	NM_UTILS_LOOKUP_ITEM (NM_ACTIVE_CONNECTION_STATE_DEACTIVATED,  N_("deactivated")),
+	NM_UTILS_LOOKUP_ITEM_IGNORE (NM_ACTIVE_CONNECTION_STATE_UNKNOWN),
+)
+
+NM_UTILS_LOOKUP_STR_DEFINE_STATIC (vpn_connection_state_to_string, NMVpnConnectionState,
+	NM_UTILS_LOOKUP_DEFAULT (N_("unknown")),
+	NM_UTILS_LOOKUP_ITEM (NM_VPN_CONNECTION_STATE_PREPARE,       N_("VPN connecting (prepare)")),
+	NM_UTILS_LOOKUP_ITEM (NM_VPN_CONNECTION_STATE_NEED_AUTH,     N_("VPN connecting (need authentication)")),
+	NM_UTILS_LOOKUP_ITEM (NM_VPN_CONNECTION_STATE_CONNECT,       N_("VPN connecting")),
+	NM_UTILS_LOOKUP_ITEM (NM_VPN_CONNECTION_STATE_IP_CONFIG_GET, N_("VPN connecting (getting IP configuration)")),
+	NM_UTILS_LOOKUP_ITEM (NM_VPN_CONNECTION_STATE_ACTIVATED,     N_("VPN connected")),
+	NM_UTILS_LOOKUP_ITEM (NM_VPN_CONNECTION_STATE_FAILED,        N_("VPN connection failed")),
+	NM_UTILS_LOOKUP_ITEM (NM_VPN_CONNECTION_STATE_DISCONNECTED,  N_("VPN disconnected")),
+	NM_UTILS_LOOKUP_ITEM_IGNORE (NM_VPN_CONNECTION_STATE_UNKNOWN),
+)
+
+/* 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;
+}
+
+/* Caller has to free the returned string */
+static char *
+get_ac_device_string (NMActiveConnection *active)
+{
+	GString *dev_str;
+	const GPtrArray *devices;
+	int i;
+
+	if (!active)
+		return NULL;
+
+	/* Get devices of the active connection */
+	dev_str = g_string_new (NULL);
+	devices = nm_active_connection_get_devices (active);
+	for (i = 0; i < devices->len; i++) {
+		NMDevice *device = g_ptr_array_index (devices, i);
+		const char *dev_iface = nm_device_get_iface (device);
+
+		if (dev_iface) {
+			g_string_append (dev_str, dev_iface);
+			g_string_append_c (dev_str, ',');
+		}
+	}
+	if (dev_str->len > 0)
+		g_string_truncate (dev_str, dev_str->len - 1);  /* Cut off last ',' */
+
+	return g_string_free (dev_str, FALSE);
+}
+
+/*****************************************************************************/
+
 const NmcMetaGenericInfo *const nmc_fields_con_show[] = {
 	NMC_META_GENERIC ("NAME"),                  /* 0 */
 	NMC_META_GENERIC ("UUID"),                  /* 1 */
@@ -151,7 +225,6 @@ const NmcMetaGenericInfo *const nmc_fields_con_active_details_general[] = {
                                          // 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 */
@@ -166,9 +239,9 @@ const NmcMetaGenericInfo *const nmc_fields_con_active_details_vpn[] = {
 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 ("DHCP4",   nmc_fields_dhcp_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 ("DHCP6",   nmc_fields_dhcp_config + 1),                /* 4 */
 	NMC_META_GENERIC_WITH_NESTED ("VPN",     nmc_fields_con_active_details_vpn + 1),     /* 5 */
 	NULL,
 };
@@ -189,7 +262,10 @@ typedef struct {
 	NMSetting *setting;
 	const char *property;
 } TabCompletionInfo;
-static TabCompletionInfo nmc_tab_completion = {NULL, NULL, NULL, NULL};
+
+static TabCompletionInfo nmc_tab_completion;
+
+/*****************************************************************************/
 
 static void
 usage (void)
@@ -505,12 +581,8 @@ usage_connection_export (void)
 static void
 quit (void)
 {
-	if (progress_id) {
-		g_source_remove (progress_id);
-		progress_id = 0;
+	if (nm_clear_g_source (&progress_id))
 		nmc_terminal_erase_line ();
-	}
-
 	g_main_loop_quit (loop);
 }
 
@@ -523,97 +595,38 @@ construct_header_name (const char *base, const char *spec)
 	return g_strdup_printf ("%s (%s)", base, spec);
 }
 
-static const char *
-active_connection_state_to_string (NMActiveConnectionState state)
-{
-	switch (state) {
-	case NM_ACTIVE_CONNECTION_STATE_ACTIVATING:
-		return _("activating");
-	case NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
-		return _("activated");
-	case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING:
-		return _("deactivating");
-	case NM_ACTIVE_CONNECTION_STATE_DEACTIVATED:
-		return _("deactivated");
-	case NM_ACTIVE_CONNECTION_STATE_UNKNOWN:
-	default:
-		return _("unknown");
-	}
-}
-
-static const char *
-vpn_connection_state_to_string (NMVpnConnectionState state)
-{
-	switch (state) {
-	case NM_VPN_CONNECTION_STATE_PREPARE:
-		return _("VPN connecting (prepare)");
-	case NM_VPN_CONNECTION_STATE_NEED_AUTH:
-		return _("VPN connecting (need authentication)");
-	case NM_VPN_CONNECTION_STATE_CONNECT:
-		return _("VPN connecting");
-	case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
-		return _("VPN connecting (getting IP configuration)");
-	case NM_VPN_CONNECTION_STATE_ACTIVATED:
-		return _("VPN connected");
-	case NM_VPN_CONNECTION_STATE_FAILED:
-		return _("VPN connection failed");
-	case NM_VPN_CONNECTION_STATE_DISCONNECTED:
-		return _("VPN disconnected");
-	default:
-		return _("unknown");
-	}
-}
-
-/* Caller has to free the returned string */
-static char *
-get_ac_device_string (NMActiveConnection *active)
-{
-	GString *dev_str;
-	const GPtrArray *devices;
-	int i;
-
-	if (!active)
-		return NULL;
-
-	/* Get devices of the active connection */
-	dev_str = g_string_new (NULL);
-	devices = nm_active_connection_get_devices (active);
-	for (i = 0; i < devices->len; i++) {
-		NMDevice *device = g_ptr_array_index (devices, i);
-		const char *dev_iface = nm_device_get_iface (device);
-
-		if (dev_iface) {
-			g_string_append (dev_str, dev_iface);
-			g_string_append_c (dev_str, ',');
-		}
-	}
-	if (dev_str->len > 0)
-		g_string_truncate (dev_str, dev_str->len - 1);  /* Cut off last ',' */
-
-	return g_string_free (dev_str, FALSE);
-}
-
 static NMActiveConnection *
-get_ac_for_connection (const GPtrArray *active_cons, NMConnection *connection)
+get_ac_for_connection (const GPtrArray *active_cons, NMConnection *connection, GPtrArray **out_result)
 {
 	const char *con_path, *ac_con_path;
-	int i;
-	NMActiveConnection *ac = NULL;
+	guint i;
+	NMActiveConnection *best_candidate = NULL;
+	GPtrArray *result = out_result ? *out_result : NULL;
 
-	/* Is the connection active? */
 	con_path = nm_connection_get_path (connection);
 	for (i = 0; i < active_cons->len; i++) {
 		NMActiveConnection *candidate = g_ptr_array_index (active_cons, i);
 		NMRemoteConnection *con;
 
 		con = nm_active_connection_get_connection (candidate);
-		ac_con_path = con ? nm_connection_get_path (NM_CONNECTION (con)) : NULL;
-		if (!g_strcmp0 (ac_con_path, con_path)) {
-			ac = candidate;
-			break;
+		if (NM_CONNECTION (con) != connection) {
+			/* also compare the D-Bus paths. Why? I don't know. */
+			ac_con_path = con ? nm_connection_get_path (NM_CONNECTION (con)) : NULL;
+			if (!nm_streq0 (ac_con_path, con_path))
+				continue;
 		}
+
+		if (!out_result)
+			return candidate;
+		if (!best_candidate)
+			best_candidate = candidate;
+		if (!result)
+			result = g_ptr_array_new_with_free_func (g_object_unref);
+		g_ptr_array_add (result, g_object_ref (candidate));
 	}
-	return ac;
+
+	NM_SET_OUT (out_result, result);
+	return best_candidate;
 }
 
 typedef struct {
@@ -741,118 +754,17 @@ nmc_connection_profile_details (NMConnection *connection, NmCli *nmc)
 	return TRUE;
 }
 
-static NMActiveConnection *
-find_active_connection (const GPtrArray *active_cons,
-                        const GPtrArray *cons,
-                        const char *filter_type,
-                        const char *filter_val,
-                        int *idx,
-                        gboolean complete)
-{
-	int i;
-	int start = (idx && *idx > 0) ? *idx : 0;
-	const char *path, *a_path, *path_num, *a_path_num;
-	const char *id;
-	const char *uuid;
-	NMRemoteConnection *con;
-	NMActiveConnection *found = NULL;
-
-	for (i = start; i < active_cons->len; i++) {
-		NMActiveConnection *candidate = g_ptr_array_index (active_cons, i);
-
-		con = nm_active_connection_get_connection (candidate);
-
-		id = nm_active_connection_get_id (candidate);
-		uuid = nm_active_connection_get_uuid (candidate);
-		path = con ? nm_connection_get_path (NM_CONNECTION (con)) : NULL;
-		path_num = path ? strrchr (path, '/') + 1 : NULL;
-		a_path = nm_object_get_path (NM_OBJECT (candidate));
-		a_path_num = a_path ? strrchr (a_path, '/') + 1 : NULL;
-
-		/* When filter_type is NULL, compare connection ID (filter_val)
-		 * against all types. Otherwise, only compare against the specific
-		 * type. If 'path' or 'apath' filter types are specified, comparison
-		 * against numeric index (in addition to the whole path) is allowed.
-		 */
-		if (!filter_type || strcmp (filter_type, "id")  == 0) {
-			if (complete)
-				nmc_complete_strings (filter_val, id, NULL);
-			if (strcmp (filter_val, id) == 0)
-				goto found;
-		}
-
-		if (!filter_type || strcmp (filter_type, "uuid") == 0) {
-			if (complete && (filter_type || *filter_val))
-				nmc_complete_strings (filter_val, uuid, NULL);
-			if (strcmp (filter_val, uuid) == 0)
-				goto found;
-		}
-
-		if (!filter_type || strcmp (filter_type, "path") == 0) {
-			if (complete && (filter_type || *filter_val))
-				nmc_complete_strings (filter_val, path, filter_type ? path_num : NULL, NULL);
-		        if (g_strcmp0 (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0))
-				goto found;
-		}
-
-		if (!filter_type || strcmp (filter_type, "apath") == 0) {
-			if (complete && (filter_type || *filter_val))
-				nmc_complete_strings (filter_val, a_path, filter_type ? a_path_num : NULL, NULL);
-		        if (g_strcmp0 (filter_val, a_path) == 0 || (filter_type && g_strcmp0 (filter_val, a_path_num) == 0))
-				goto found;
-		}
-
-		continue;
-found:
-		if (!idx)
-			return candidate;
-		if (found) {
-			*idx = i;
-			return found;
-		}
-		found = candidate;
-	}
-
-	if (idx)
-		*idx = 0;
-	return found;
-}
-
-void
-nmc_active_connection_state_to_color (NMActiveConnectionState state, NMMetaTermColor *color)
+NMMetaColor
+nmc_active_connection_state_to_color (NMActiveConnectionState state)
 {
-	*color = NM_META_TERM_COLOR_NORMAL;
-
 	if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING)
-		*color = NM_META_TERM_COLOR_YELLOW;
+		return NM_META_COLOR_CONNECTION_ACTIVATING;
 	else if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
-		*color = NM_META_TERM_COLOR_GREEN;
+		return NM_META_COLOR_CONNECTION_ACTIVATED;
 	else if (state > NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
-		*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;
+		return NM_META_COLOR_CONNECTION_DISCONNECTING;
+	else
+		return NM_META_COLOR_CONNECTION_UNKNOWN;
 }
 
 static void
@@ -871,19 +783,19 @@ fill_output_connection (NMConnection *connection, NMClient *client, NMCPrintOutp
 	const char *ac_state = NULL;
 	NMActiveConnectionState ac_state_int = NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
 	char *ac_dev = NULL;
-	NMMetaTermColor color;
+	NMMetaColor color;
 
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
 
-	ac = get_ac_for_connection (nm_client_get_active_connections (client), connection);
+	ac = get_ac_for_connection (nm_client_get_active_connections (client), connection, NULL);
 	if (active_only && !ac)
 		return;
 
 	if (ac) {
 		ac_path = nm_object_get_path (NM_OBJECT (ac));
 		ac_state_int = nm_active_connection_get_state (ac);
-		ac_state = active_connection_state_to_string (ac_state_int);
+		ac_state = gettext (active_connection_state_to_string (ac_state_int));
 		ac_dev = get_ac_device_string (ac);
 	}
 
@@ -900,7 +812,7 @@ fill_output_connection (NMConnection *connection, NMClient *client, NMCPrintOutp
 	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);
+	color = nmc_active_connection_state_to_color (ac_state_int);
 	set_val_color_all (arr, color);
 
 	set_val_strc (arr, 0, nm_setting_connection_get_id (s_con));
@@ -951,7 +863,7 @@ fill_output_connection_for_invisible (NMActiveConnection *ac, NMCPrintOutput pri
 	set_val_strc (arr, 12, ac_path);
 	set_val_strc (arr, 13, NULL);
 
-	set_val_color_fmt_all (arr, NM_META_TERM_FORMAT_DIM);
+	set_val_color_all (arr, NM_META_COLOR_CONNECTION_INVISIBLE);
 
 	g_ptr_array_add (output_data, arr);
 }
@@ -978,7 +890,7 @@ fill_output_active_connection (NMActiveConnection *active,
 	if (con) {
 		con_path = nm_connection_get_path (NM_CONNECTION (con));
 		s_con = nm_connection_get_setting_connection (NM_CONNECTION (con));
-		g_assert (s_con != NULL);
+		g_assert (s_con);
 		con_zone = nm_setting_connection_get_zone (s_con);
 	}
 
@@ -1099,7 +1011,9 @@ static const gchar *
 get_vpn_data_item (NMConnection *connection, enum VpnDataItem vpn_data_item)
 {
 	const char *key;
-	char *type = get_vpn_connection_type (connection);
+	gs_free char *type = NULL;
+
+	type = get_vpn_connection_type (connection);
 
 	switch (vpn_data_item) {
 	case VPN_DATA_ITEM_GATEWAY:
@@ -1112,7 +1026,6 @@ get_vpn_data_item (NMConnection *connection, enum VpnDataItem vpn_data_item)
 		key = "";
 		break;
 	}
-	g_free (type);
 
 	return nm_setting_vpn_get_data_item (nm_connection_get_setting_vpn (connection), key);
 }
@@ -1128,7 +1041,6 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
 	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;
 
@@ -1182,7 +1094,6 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
 
 			/* Add field names */
 			tmpl = (const NMMetaAbstractInfo *const*) nmc_fields_con_active_details_general;
-			tmpl_len = sizeof (nmc_fields_con_active_details_general);
 			out_indices = parse_output_fields (group_fld,
 			                                   tmpl, FALSE, NULL, NULL);
 			arr = nmc_dup_fields_array (tmpl, NMC_OF_FLAG_FIELD_NAMES);
@@ -1211,7 +1122,7 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
 			gboolean b1 = FALSE;
 			NMDhcpConfig *dhcp4 = nm_active_connection_get_dhcp4_config (acon);
 
-			b1 = print_dhcp4_config (dhcp4, &nmc->nmc_config, "DHCP4", group_fld);
+			b1 = print_dhcp_config (dhcp4, &nmc->nmc_config, "DHCP4", group_fld);
 			was_output = was_output || b1;
 		}
 
@@ -1229,7 +1140,7 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
 			gboolean b1 = FALSE;
 			NMDhcpConfig *dhcp6 = nm_active_connection_get_dhcp6_config (acon);
 
-			b1 = print_dhcp6_config (dhcp6, &nmc->nmc_config, "DHCP6", group_fld);
+			b1 = print_dhcp_config (dhcp6, &nmc->nmc_config, "DHCP6", group_fld);
 			was_output = was_output || b1;
 		}
 
@@ -1250,7 +1161,7 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
 			con = NM_CONNECTION (nm_active_connection_get_connection (acon));
 
 			s_con = nm_connection_get_setting_connection (con);
-			g_assert (s_con != NULL);
+			g_assert (s_con);
 
 			tmpl = (const NMMetaAbstractInfo *const*) nmc_fields_con_active_details_vpn;
 			out_indices = parse_output_fields (group_fld,
@@ -1278,13 +1189,15 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
 			if (banner)
 				banner_str = g_strescape (banner, "");
 			vpn_state = nm_vpn_connection_get_vpn_state (NM_VPN_CONNECTION (acon));
-			vpn_state_str = g_strdup_printf ("%d - %s", vpn_state, vpn_connection_state_to_string (vpn_state));
+			vpn_state_str = g_strdup_printf ("%d - %s",
+			                                 vpn_state,
+			                                 gettext (vpn_connection_state_to_string (vpn_state)));
 
 			/* Add values */
 			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, 2, 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);
@@ -1439,34 +1352,34 @@ typedef struct {
 static int
 compare_connections (gconstpointer a, gconstpointer b, gpointer user_data)
 {
-	NMConnection *ca = *(NMConnection **)a;
-	NMConnection *cb = *(NMConnection **)b;
+	NMConnection *ca = *(NMConnection **) a;
+	NMConnection *cb = *(NMConnection **) b;
+	const NmcSortInfo *info = user_data;
 	NMActiveConnection *aca, *acb;
-	NmcSortInfo *info = (NmcSortInfo *) user_data;
-	GArray *default_order = NULL;
-	const GArray *order;
-	NmcSortOrder item;
-	int cmp = 0, i;
+	const NmcSortOrder *order_arr;
+	guint i, order_len;
 	const char *tmp1, *tmp2;
 	unsigned long tmp1_int, tmp2_int;
 
-	if (info->order )
-		order = info->order;
-	else {
-		NmcSortOrder def[] = { NMC_SORT_ACTIVE, NMC_SORT_NAME, NMC_SORT_PATH };
-		int num = G_N_ELEMENTS (def);
-		default_order = g_array_sized_new (FALSE, FALSE, sizeof (NmcSortOrder), num);
-		g_array_append_vals (default_order, def, num);
-		order = default_order;
+	if (info->order) {
+		order_arr = &g_array_index (info->order, NmcSortOrder, 0);
+		order_len = info->order->len;
+	} else {
+		static const NmcSortOrder def[] = { NMC_SORT_ACTIVE, NMC_SORT_NAME, NMC_SORT_PATH };
+
+		order_arr = def;
+		order_len = G_N_ELEMENTS (def);
 	}
 
-	for (i = 0; i < order->len; i++) {
-		item = g_array_index (order, NmcSortOrder, i);
+	for (i = 0; i < order_len; i++) {
+		NmcSortOrder item = order_arr[i];
+		int cmp = 0;
+
 		switch (item) {
 		case NMC_SORT_ACTIVE:
 		case NMC_SORT_ACTIVE_INV:
-			aca = get_ac_for_connection (nm_client_get_active_connections (info->nmc->client), ca);
-			acb = get_ac_for_connection (nm_client_get_active_connections (info->nmc->client), cb);
+			aca = get_ac_for_connection (nm_client_get_active_connections (info->nmc->client), ca, NULL);
+			acb = get_ac_for_connection (nm_client_get_active_connections (info->nmc->client), cb, NULL);
 			cmp = (aca && !acb) ? -1 : (!aca && acb) ? 1 : 0;
 			if (item == NMC_SORT_ACTIVE_INV)
 				cmp = -(cmp);
@@ -1502,12 +1415,10 @@ compare_connections (gconstpointer a, gconstpointer b, gpointer user_data)
 			break;
 		}
 		if (cmp != 0)
-			goto end;
+			return cmp;
 	}
-end:
-	if (default_order)
-		g_array_unref (default_order);
-	return cmp;
+
+	return 0;
 }
 
 static GPtrArray *
@@ -1559,7 +1470,7 @@ get_invisible_active_connections (NmCli *nmc)
 	GPtrArray *invisibles;
 	int a, c;
 
-	g_return_val_if_fail (nmc != NULL, NULL);
+	g_return_val_if_fail (nmc, NULL);
 
 	invisibles = g_ptr_array_new ();
 	acons = nm_client_get_active_connections (nmc->client);
@@ -1589,18 +1500,18 @@ get_invisible_active_connections (NmCli *nmc)
 static GArray *
 parse_preferred_connection_order (const char *order, GError **error)
 {
-	char **strv, **iter;
+	gs_free const char **strv = NULL;
+	const char *const*iter;
 	const char *str;
 	GArray *order_arr;
 	NmcSortOrder val;
 	gboolean inverse, unique;
 	int i;
 
-	strv = nmc_strsplit_set (order, ":", -1);
-	if (!strv || !*strv) {
+	strv = nm_utils_strsplit_set (order, ":");
+	if (!strv) {
 		g_set_error (error, NMCLI_ERROR, 0,
 		             _("incorrect string '%s' of '--order' option"), order);
-		g_strfreev (strv);
 		return NULL;
 	}
 
@@ -1642,17 +1553,25 @@ parse_preferred_connection_order (const char *order, GError **error)
 			g_array_append_val (order_arr, val);
 	}
 
-	g_strfreev (strv);
 	return order_arr;
 }
 
 static NMConnection *
-get_connection (NmCli *nmc, int *argc, char ***argv, int *pos, GError **error)
+get_connection (NmCli *nmc,
+                int *argc,
+                char ***argv,
+                const char **out_selector,
+                const char **out_value,
+                GPtrArray **out_result,
+                GError **error)
 {
 	const GPtrArray *connections;
 	NMConnection *connection = NULL;
 	const char *selector = NULL;
 
+	NM_SET_OUT (out_selector, NULL);
+	NM_SET_OUT (out_value, NULL);
+
 	if (*argc == 0) {
 		g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
 		                     _("No connection specified"));
@@ -1662,45 +1581,46 @@ get_connection (NmCli *nmc, int *argc, char ***argv, int *pos, GError **error)
 	if (*argc == 1 && nmc->complete)
 		nmc_complete_strings (**argv, "id", "uuid", "path", NULL);
 
-	if (   strcmp (**argv, "id") == 0
-	    || strcmp (**argv, "uuid") == 0
-	    || strcmp (**argv, "path") == 0) {
-		selector = **argv;
-		(*argc)--;
-		(*argv)++;
-		if (!*argc) {
-			g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
-			             _("%s argument is missing"), selector);
-			return NULL;
+	if (NM_IN_STRSET (**argv, "id", "uuid", "path")) {
+		if (*argc == 1) {
+			if (!nmc->complete) {
+				g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
+				             _("%s argument is missing"), selector);
+				return NULL;
+			}
+		} else {
+			selector = **argv;
+			(*argv)++;
+			(*argc)--;
 		}
 	}
 
+	NM_SET_OUT (out_selector, selector);
+	NM_SET_OUT (out_value, **argv);
+
 	connections = nm_client_get_connections (nmc->client);
-	connection = nmc_find_connection (connections, selector, **argv, pos,
+	connection = nmc_find_connection (connections, selector, **argv, out_result,
 	                                  *argc == 1 && nmc->complete);
 	if (!connection) {
 		g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND,
 		             _("unknown connection '%s'"), **argv);
 	}
 
-	/* If the caller wants multiple results (pos is set) and there are any,
-	 * don't switch to next argument.
-	 */
-	if (!pos || !*pos)
-		next_arg (nmc, argc, argv, NULL);
-
+	next_arg (nmc, argc, argv, NULL);
 	return connection;
 }
 
 static NMCResultCode
 do_connections_show (NmCli *nmc, int argc, char **argv)
 {
-	GError *err = NULL;
-	char *profile_flds = NULL, *active_flds = NULL;
+	gs_free_error GError *err = NULL;
+	gs_free char *profile_flds = NULL;
+	gs_free char *active_flds = NULL;
 	GPtrArray *invisibles, *sorted_cons;
 	gboolean active_only = FALSE;
-	GArray *order = NULL;
-	int i, option;
+	gs_unref_array GArray *order = NULL;
+	guint i, j;
+	int option;
 
 	/* check connection show options [--active] [--order <order spec>] */
 	while ((option = next_arg (nmc, &argc, &argv, "--active", "--order", NULL)) > 0) {
@@ -1775,7 +1695,6 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 		gboolean new_line = FALSE;
 		gboolean without_fields = (nmc->required_fields == NULL);
 		const GPtrArray *active_cons = nm_client_get_active_connections (nmc->client);
-		int pos = 0;
 
 		/* multiline mode is default for 'connection show <ID>' */
 		if (!nmc->mode_specified)
@@ -1784,8 +1703,8 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 		/* Split required fields into the settings and active ones. */
 		if (!split_required_fields_for_con_show (nmc->required_fields, &profile_flds, &active_flds, &err))
 			goto finish;
-		g_free (nmc->required_fields);
-		nmc->required_fields = NULL;
+
+		nm_clear_g_free (&nmc->required_fields);
 
 		/* Before printing the connections check if we have a "--show-secret"
 		 * option after the connection ids */
@@ -1794,10 +1713,7 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 			char **argv_cp = argv;
 
 			do {
-				if (   nm_streq (*argv_cp, "id")
-				    || nm_streq (*argv_cp, "uuid")
-				    || nm_streq (*argv_cp, "path")
-				    || nm_streq (*argv_cp, "apath")) {
+				if (NM_IN_STRSET (*argv_cp, "id", "uuid", "path", "apath")) {
 					argc_cp--;
 					argv_cp++;
 				}
@@ -1808,16 +1724,16 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 			const GPtrArray *connections;
 			gboolean res;
 			NMConnection *con;
-			NMActiveConnection *acon = NULL;
+			gs_unref_object NMActiveConnection *explicit_acon = NULL;
 			const char *selector = NULL;
+			gs_unref_ptrarray GPtrArray *found_cons = NULL;
+			gboolean explicit_acon_handled = FALSE;
+			guint i_found_cons;
 
 			if (argc == 1 && nmc->complete)
 				nmc_complete_strings (*argv, "id", "uuid", "path", "apath", NULL);
 
-			if (   strcmp (*argv, "id") == 0
-			    || strcmp (*argv, "uuid") == 0
-			    || strcmp (*argv, "path") == 0
-			    || strcmp (*argv, "apath") == 0) {
+			if (NM_IN_STRSET (*argv, "id", "uuid", "path", "apath")) {
 				selector = *argv;
 				argc--;
 				argv++;
@@ -1830,17 +1746,26 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 
 			/* Try to find connection by id, uuid or path first */
 			connections = nm_client_get_connections (nmc->client);
-			con = nmc_find_connection (connections, selector, *argv, &pos,
+			con = nmc_find_connection (connections, selector, *argv, &found_cons,
 			                           argc == 1 && nmc->complete);
-			if (!con && (!selector || strcmp (selector, "apath") == 0)) {
+			if (   !con
+			    && NM_IN_STRSET (selector, NULL, "apath")) {
 				/* Try apath too */
-				acon = find_active_connection (active_cons, connections, "apath", *argv, NULL,
-				                               argc == 1 && nmc->complete);
-				if (acon)
-					con = NM_CONNECTION (nm_active_connection_get_connection (acon));
+				explicit_acon = nmc_find_active_connection (active_cons, "apath", *argv, NULL,
+				                                            argc == 1 && nmc->complete);
+				if (explicit_acon) {
+					if (   !selector
+					    && !nm_streq0 (*argv, nm_object_get_path (NM_OBJECT (explicit_acon)))) {
+						/* we matched the apath based on the last component alone (note the full D-Bus path).
+						 * That is how nmc_find_active_connection() works, if you pass in a selector.
+						 * Reject it. */
+						explicit_acon = NULL;
+					}
+					nm_g_object_ref (explicit_acon);
+				}
 			}
 
-			if (!con && !acon) {
+			if (!con && !explicit_acon) {
 				g_string_printf (nmc->return_text, _("Error: %s - no such connection profile."), *argv);
 				nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
 				goto finish;
@@ -1852,54 +1777,81 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 			 * may see only the active connection.
 			 */
 
-			/* Filter only active connections */
-			if (!acon)
-				acon = get_ac_for_connection (active_cons, con);
-			if (active_only && !acon) {
-				next_arg (nmc, &argc, &argv, NULL);
-				continue;
-			}
-
 			if (nmc->complete) {
 				next_arg (nmc, &argc, &argv, NULL);
 				continue;
 			}
 
-			/* Show an empty line between connections */
-			if (new_line)
-				g_print ("\n");
-
-			/* Show profile configuration */
-			if (without_fields || profile_flds) {
-				if (con) {
-					nmc->required_fields = profile_flds;
-					if (nmc->nmc_config.show_secrets)
-						update_secrets_in_connection (NM_REMOTE_CONNECTION (con), con);
-					res = nmc_connection_profile_details (con, nmc);
-					nmc->required_fields = NULL;
-					if (!res)
-						goto finish;
+			explicit_acon_handled = FALSE;
+			i_found_cons = 0;
+			for (;;) {
+				gs_unref_ptrarray GPtrArray *found_acons = NULL;
+
+				if (explicit_acon) {
+					if (explicit_acon_handled)
+						break;
+					explicit_acon_handled = TRUE;
+					/* the user referenced an "apath". In this case, we can only have at most one connection
+					 * and one apath. */
+					con = NM_CONNECTION (nm_active_connection_get_connection (explicit_acon));
+				} else {
+					if (i_found_cons >= found_cons->len)
+						break;
+					con = found_cons->pdata[i_found_cons++];
+					get_ac_for_connection (active_cons, con, &found_acons);
 				}
-			}
 
-			/* If the profile is active, print also active details */
-			if (without_fields || active_flds) {
-				if (acon) {
-					nmc->required_fields = active_flds;
-					res = nmc_active_connection_details (acon, nmc);
-					nmc->required_fields = NULL;
-					if (!res)
-						goto finish;
+				if (active_only && !explicit_acon && !found_acons) {
+					/* this connection is not interesting, we only print active ones. */
+					continue;
+				}
+
+				nm_assert (explicit_acon || con);
+
+				if (new_line)
+					g_print ("\n");
+				new_line = TRUE;
+
+				if (without_fields || profile_flds) {
+					if (con) {
+						nmc->required_fields = profile_flds;
+						if (nmc->nmc_config.show_secrets)
+							update_secrets_in_connection (NM_REMOTE_CONNECTION (con), con);
+						res = nmc_connection_profile_details (con, nmc);
+						nmc->required_fields = NULL;
+						if (!res)
+							goto finish;
+					}
+				}
+
+				if (without_fields || active_flds) {
+					guint l = explicit_acon ? 1 : (found_acons ? found_acons->len : 0);
+
+					for (j = 0; j < l; j++) {
+						NMActiveConnection *acon;
+
+						if (j > 0) {
+							/* if there are multiple active connections, separate them with newline.
+							 * that is a bit odd, because we already separate connections with newlines,
+							 * and commonly don't separate the connection from the first active connection. */
+							g_print ("\n");
+						}
+
+						if (explicit_acon)
+							acon = explicit_acon;
+						else
+							acon = found_acons->pdata[j];
+
+						nmc->required_fields = active_flds;
+						res = nmc_active_connection_details (acon, nmc);
+						nmc->required_fields = NULL;
+						if (!res)
+							goto finish;
+					}
 				}
 			}
-			new_line = TRUE;
 
-			/* Take next argument.
-			 * But for pos != NULL we have more connections of the same name,
-			 * so process the same argument again.
-			 */
-			if (!pos)
-				next_arg (nmc, &argc, &argv, NULL);
+			next_arg (nmc, &argc, &argv, NULL);
 		}
 	}
 
@@ -1907,12 +1859,7 @@ finish:
 	if (err) {
 		g_string_printf (nmc->return_text, _("Error: %s."), err->message);
 		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-		g_error_free (err);
 	}
-	g_free (profile_flds);
-	g_free (active_flds);
-	if (order)
-		g_array_unref (order);
 	return nmc->return_value;
 }
 
@@ -1925,8 +1872,8 @@ get_default_active_connection (NmCli *nmc, NMDevice **device)
 	const GPtrArray *connections;
 	int i;
 
-	g_return_val_if_fail (nmc != NULL, NULL);
-	g_return_val_if_fail (device != NULL, NULL);
+	g_return_val_if_fail (nmc, NULL);
+	g_return_val_if_fail (device, NULL);
 	g_return_val_if_fail (*device == NULL, NULL);
 
 	connections = nm_client_get_active_connections (nmc->client);
@@ -1984,10 +1931,10 @@ find_device_for_connection (NmCli *nmc,
 	const char *con_type;
 	int i, j;
 
-	g_return_val_if_fail (nmc != NULL, FALSE);
+	g_return_val_if_fail (nmc, FALSE);
 	g_return_val_if_fail (iface || ap || nsp, FALSE);
-	g_return_val_if_fail (device != NULL && *device == NULL, FALSE);
-	g_return_val_if_fail (spec_object != NULL && *spec_object == NULL, FALSE);
+	g_return_val_if_fail (device && *device == NULL, FALSE);
+	g_return_val_if_fail (spec_object && *spec_object == NULL, FALSE);
 	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
 	s_con = nm_connection_get_setting_connection (connection);
@@ -2042,8 +1989,10 @@ find_device_for_connection (NmCli *nmc,
 			}
 
 			found_device = dev;
-			if (ap && !strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME) && NM_IS_DEVICE_WIFI (dev)) {
-				char *bssid_up = g_ascii_strup (ap, -1);
+			if (   ap
+			    && nm_streq (con_type, NM_SETTING_WIRELESS_SETTING_NAME)
+			    && NM_IS_DEVICE_WIFI (dev)) {
+				gs_free char *bssid_up = g_ascii_strup (ap, -1);
 				const GPtrArray *aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (dev));
 				found_device = NULL;  /* Mark as not found; set to the device again later, only if AP matches */
 
@@ -2051,29 +2000,28 @@ find_device_for_connection (NmCli *nmc,
 					NMAccessPoint *candidate_ap = g_ptr_array_index (aps, j);
 					const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap);
 
-					if (!strcmp (bssid_up, candidate_bssid)) {
+					if (nm_streq0 (bssid_up, candidate_bssid)) {
 						found_device = dev;
 						*spec_object = nm_object_get_path (NM_OBJECT (candidate_ap));
 						break;
 					}
 				}
-				g_free (bssid_up);
 			}
-
 		}
 
-		if (found_device) {
-			*device = found_device;
-			return TRUE;
-		} else {
-			if (iface)
+		if (!found_device) {
+			if (iface) {
 				g_set_error (error, NMCLI_ERROR, 0, _("device '%s' not compatible with connection '%s'"),
 				             iface, nm_setting_connection_get_id (s_con));
-			else
+			} else {
 				g_set_error (error, NMCLI_ERROR, 0, _("no device found for connection '%s'"),
 				             nm_setting_connection_get_id (s_con));
+			}
 			return FALSE;
 		}
+
+		*device = found_device;
+		return TRUE;
 	}
 }
 
@@ -2295,48 +2243,50 @@ activate_connection_cb (GObject *client, GAsyncResult *result, gpointer user_dat
 static GHashTable *
 parse_passwords (const char *passwd_file, GError **error)
 {
-	GHashTable *pwds_hash;
-	char *contents = NULL;
+	gs_unref_hashtable GHashTable *pwds_hash = NULL;
+	gs_free char *contents = NULL;
 	gsize len = 0;
 	GError *local_err = NULL;
-	char **lines, **iter;
+	gs_free const char **strv = NULL;
+	const char *const*iter;
 	char *pwd_spec, *pwd, *prop;
 	const char *setting;
 
 	pwds_hash = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free);
 
 	if (!passwd_file)
-		return pwds_hash;
+		return g_steal_pointer (&pwds_hash);
 
-        /* Read the passwords file */
+	/* Read the passwords file */
 	if (!g_file_get_contents (passwd_file, &contents, &len, &local_err)) {
 		g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
 		             _("failed to read passwd-file '%s': %s"),
 		             passwd_file, local_err->message);
 		g_error_free (local_err);
-		g_hash_table_destroy (pwds_hash);
 		return NULL;
 	}
 
-	lines = nmc_strsplit_set (contents, "\r\n", -1);
-	for (iter = lines; *iter; iter++) {
-		pwd = strchr (*iter, ':');
+	strv = nm_utils_strsplit_set (contents, "\r\n");
+	for (iter = strv; *iter; iter++) {
+		gs_free char *iter_s = g_strdup (*iter);
+
+		pwd = strchr (iter_s, ':');
 		if (!pwd) {
 			g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
 			             _("missing colon in 'password' entry '%s'"), *iter);
-			goto failure;
+			return NULL;
 		}
 		*(pwd++) = '\0';
 
-		prop = strchr (*iter, '.');
+		prop = strchr (iter_s, '.');
 		if (!prop) {
 			g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
 			             _("missing dot in 'password' entry '%s'"), *iter);
-			goto failure;
+			return NULL;
 		}
 		*(prop++) = '\0';
 
-		setting = *iter;
+		setting = iter_s;
 		while (g_ascii_isspace (*setting))
 			setting++;
 		/* Accept wifi-sec or wifi instead of cumbersome '802-11-wireless-security' */
@@ -2345,25 +2295,15 @@ parse_passwords (const char *passwd_file, GError **error)
 		if (nm_setting_lookup_type (setting) == G_TYPE_INVALID) {
 			g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
 			             _("invalid setting name in 'password' entry '%s'"), setting);
-			goto failure;
+			return NULL;
 		}
 
 		pwd_spec = g_strdup_printf ("%s.%s", setting, prop);
 		g_hash_table_insert (pwds_hash, pwd_spec, g_strdup (pwd));
 	}
-	g_strfreev (lines);
-	g_free (contents);
-	return pwds_hash;
-
-failure:
-	g_strfreev (lines);
-	g_free (contents);
-	g_hash_table_destroy (pwds_hash);
-	return NULL;
+	return g_steal_pointer (&pwds_hash);
 }
 
-
-
 static gboolean
 nmc_activate_connection (NmCli *nmc,
                          NMConnection *connection,
@@ -2382,7 +2322,7 @@ nmc_activate_connection (NmCli *nmc,
 	gboolean device_found;
 	GError *local = NULL;
 
-	g_return_val_if_fail (nmc != NULL, FALSE);
+	g_return_val_if_fail (nmc, FALSE);
 	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
 	if (connection && (ifname || ap || nsp)) {
@@ -2482,7 +2422,7 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 	}
 
 	if (argc > 0 && strcmp (*argv, "ifname") != 0) {
-		connection = get_connection (nmc, argc_ptr, argv_ptr, NULL, &error);
+		connection = get_connection (nmc, argc_ptr, argv_ptr, NULL, NULL, NULL, &error);
 		if (!connection) {
 			g_string_printf (nmc->return_text, _("Error: %s."), error->message);
 			return error->code;
@@ -2561,20 +2501,108 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 	return nmc->return_value;
 }
 
+/*****************************************************************************/
+
 typedef struct {
 	NmCli *nmc;
-	GSList *queue;
+	/* a list of object that is relevant for the callback. The object
+	 * type differs, and depends on the type of callback. */
+	GPtrArray *obj_list;
 	guint timeout_id;
 	GCancellable *cancellable;
 } ConnectionCbInfo;
 
-static void connection_cb_info_finish (ConnectionCbInfo *info,
-                                       gpointer connection);
+static void connection_removed_cb (NMClient *client, NMConnection *connection, ConnectionCbInfo *info);
+
+static void down_active_connection_state_cb (NMActiveConnection *active,
+                                             GParamSpec *pspec,
+                                             ConnectionCbInfo *info);
+
+static void
+connection_cb_info_obj_list_destroy (ConnectionCbInfo *info, gpointer obj)
+{
+	nm_assert (info);
+	nm_assert (info->obj_list);
+	nm_assert (G_IS_OBJECT (obj));
+
+	g_signal_handlers_disconnect_by_func (obj, down_active_connection_state_cb, info);
+	g_object_unref (obj);
+}
+
+static gssize
+connection_cb_info_obj_list_idx (ConnectionCbInfo *info, gpointer obj)
+{
+	guint i;
+
+	nm_assert (info);
+	nm_assert (info->obj_list);
+	nm_assert (G_IS_OBJECT (obj));
+
+	for (i = 0; i < info->obj_list->len; i++) {
+		if (info->obj_list->pdata[i] == obj)
+			return i;
+	}
+	return -1;
+}
+
+static gpointer
+connection_cb_info_obj_list_has (ConnectionCbInfo *info, gpointer obj)
+{
+	gssize idx;
+
+	idx = connection_cb_info_obj_list_idx (info, obj);
+	if (idx >= 0)
+		return info->obj_list->pdata[idx];
+	return NULL;
+}
+
+static gpointer
+connection_cb_info_obj_list_steal (ConnectionCbInfo *info, gpointer obj)
+{
+	gssize idx;
+
+	idx = connection_cb_info_obj_list_idx (info, obj);
+	if (idx >= 0) {
+		g_ptr_array_remove_index (info->obj_list, idx);
+		return obj;
+	}
+	return NULL;
+}
+
+static void
+connection_cb_info_finish (ConnectionCbInfo *info, gpointer obj)
+{
+	if (obj) {
+		obj = connection_cb_info_obj_list_steal (info, obj);
+		if (obj)
+			connection_cb_info_obj_list_destroy (info, obj);
+	} else {
+		while (info->obj_list->len > 0) {
+			obj = info->obj_list->pdata[info->obj_list->len - 1];
+			g_ptr_array_remove_index (info->obj_list, info->obj_list->len);
+			connection_cb_info_obj_list_destroy (info, obj);
+		}
+	}
+
+	if (info->obj_list->len > 0)
+		return;
+
+	nm_clear_g_source (&info->timeout_id);
+	nm_clear_g_cancellable (&info->cancellable);
+
+	g_signal_handlers_disconnect_by_func (info->nmc->client, connection_removed_cb, info);
+
+	g_slice_free (ConnectionCbInfo, info);
+
+	quit ();
+}
+
+/*****************************************************************************/
 
 static void
 connection_removed_cb (NMClient *client, NMConnection *connection, ConnectionCbInfo *info)
 {
-	if (!g_slist_find (info->queue, connection))
+	if (!connection_cb_info_obj_list_has (info, connection))
 		return;
 	g_print (_("Connection '%s' (%s) successfully deleted.\n"),
 	         nm_connection_get_id (connection),
@@ -2611,49 +2639,17 @@ connection_op_timeout_cb (gpointer user_data)
 	return G_SOURCE_REMOVE;
 }
 
-static void
-destroy_queue_element (gpointer data)
-{
-	g_signal_handlers_disconnect_matched (data, G_SIGNAL_MATCH_FUNC, 0, 0, 0,
-	                                      down_active_connection_state_cb, NULL);
-	g_object_unref (data);
-}
-
-static void
-connection_cb_info_finish (ConnectionCbInfo *info, gpointer connection)
-{
-	if (connection) {
-		info->queue = g_slist_remove (info->queue, connection);
-		g_object_unref (G_OBJECT (connection));
-	} else {
-		g_slist_free_full (info->queue, destroy_queue_element);
-		info->queue = NULL;
-	}
-
-	if (info->queue)
-		return;
-
-	if (info->timeout_id)
-		g_source_remove (info->timeout_id);
-
-	nm_clear_g_cancellable (&info->cancellable);
-
-	g_signal_handlers_disconnect_by_func (info->nmc->client, connection_removed_cb, info);
-	g_slice_free (ConnectionCbInfo, info);
-	quit ();
-}
-
 static NMCResultCode
 do_connection_down (NmCli *nmc, int argc, char **argv)
 {
 	NMActiveConnection *active;
 	ConnectionCbInfo *info = NULL;
 	const GPtrArray *active_cons;
-	GSList *queue = NULL, *iter, *next;
-	char **arg_arr = NULL;
+	gs_strfreev char **arg_arr = NULL;
 	char **arg_ptr;
 	int arg_num;
-	int idx = 0;
+	guint i;
+	gs_unref_ptrarray GPtrArray *found_active_cons = NULL;
 
 	if (nmc->timeout == -1)
 		nmc->timeout = 10;
@@ -2674,108 +2670,88 @@ do_connection_down (NmCli *nmc, int argc, char **argv)
 		}
 		if (arg_num == 0) {
 			g_string_printf (nmc->return_text, _("Error: No connection specified."));
-			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-			goto finish;
+			NMC_RETURN (nmc, NMC_RESULT_ERROR_USER_INPUT);
 		}
 	}
 
 	/* Get active connections */
 	active_cons = nm_client_get_active_connections (nmc->client);
 	while (arg_num > 0) {
-		const GPtrArray *connections;
 		const char *selector = NULL;
 
 		if (arg_num == 1 && nmc->complete)
 			nmc_complete_strings (*arg_ptr, "id", "uuid", "path", "apath", NULL);
 
-		if (   strcmp (*arg_ptr, "id") == 0
-		    || strcmp (*arg_ptr, "uuid") == 0
-		    || strcmp (*arg_ptr, "path") == 0
-		    || strcmp (*arg_ptr, "apath") == 0) {
-
+		if (NM_IN_STRSET (*arg_ptr, "id", "uuid", "path", "apath")) {
 			selector = *arg_ptr;
 			arg_num--;
 			arg_ptr++;
 			if (!arg_num) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
-				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-				goto finish;
+				NMC_RETURN (nmc, NMC_RESULT_ERROR_USER_INPUT);
 			}
 		}
 
-		connections = nm_client_get_connections (nmc->client);
-		active = find_active_connection (active_cons, connections, selector, *arg_ptr, &idx,
-		                                 arg_num == 1 && nmc->complete);
-		if (active) {
-			/* Check if the connection is unique. */
-			/* Calling down for the same connection repeatedly would result in
-			 * NM responding for the last D-Bus call only and we would stall. */
-			if (!g_slist_find (queue, active))
-				queue = g_slist_prepend (queue, g_object_ref (active));
-		} else {
+		active = nmc_find_active_connection (active_cons,
+		                                     selector,
+		                                     *arg_ptr,
+		                                     &found_active_cons,
+		                                     arg_num == 1 && nmc->complete);
+		if (!active) {
 			if (!nmc->complete)
 				g_printerr (_("Error: '%s' is not an active connection.\n"), *arg_ptr);
 			g_string_printf (nmc->return_text, _("Error: not all active connections found."));
 			nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
 		}
 
-		if (idx == 0)
-			next_arg (nmc->ask ? NULL : nmc, &arg_num, &arg_ptr, NULL);
+		next_arg (nmc->ask ? NULL : nmc, &arg_num, &arg_ptr, NULL);
 	}
 
-	if (!queue) {
+	if (!found_active_cons) {
 		g_string_printf (nmc->return_text, _("Error: no active connection provided."));
-		nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
-		goto finish;
-	} else if (nmc->complete) {
-		g_slist_free (queue);
-		goto finish;
+		NMC_RETURN (nmc, NMC_RESULT_ERROR_NOT_FOUND);
 	}
-	queue = g_slist_reverse (queue);
+	nm_assert (found_active_cons->len > 0);
+
+	if (nmc->complete)
+		return nmc->return_value;
 
 	if (nmc->timeout > 0) {
 		nmc->should_wait++;
 
 		info = g_slice_new0 (ConnectionCbInfo);
 		info->nmc = nmc;
-		info->queue = queue;
-		info->timeout_id = g_timeout_add_seconds (nmc->timeout, connection_op_timeout_cb, info);
-	}
-
-	iter = queue;
-	while (iter) {
-		GError *error = NULL;
-
-		next = g_slist_next (iter);
-		active = iter->data;
-
-		if (info) {
+		info->obj_list = g_ptr_array_sized_new (found_active_cons->len);
+		for (i = 0; i < found_active_cons->len; i++) {
+			active = found_active_cons->pdata[i];
+			g_ptr_array_add (info->obj_list, g_object_ref (active));
 			g_signal_connect (active,
 			                  "notify::" NM_ACTIVE_CONNECTION_STATE,
 			                  G_CALLBACK (down_active_connection_state_cb),
 			                  info);
 		}
+		info->timeout_id = g_timeout_add_seconds (nmc->timeout, connection_op_timeout_cb, info);
+	}
+
+	for (i = 0; i < found_active_cons->len; i++) {
+		GError *error = NULL;
+
+		active = found_active_cons->pdata[i];
 
-		/* Now deactivate the connection */
 		if (!nm_client_deactivate_connection (nmc->client, active, NULL, &error)) {
 			g_print (_("Connection '%s' deactivation failed: %s\n"),
 			         nm_active_connection_get_id (active), error->message);
-			g_error_free (error);
+			g_clear_error (&error);
 
 			if (info) {
 				g_signal_handlers_disconnect_by_func (active,
 				                                      down_active_connection_state_cb,
 				                                      info);
-				/* Remove the active connection from @queue */
 				connection_cb_info_finish (info, active);
 			}
 		}
-
-		iter = next;
 	}
 
-finish:
-	g_strfreev (arg_arr);
 	return nmc->return_value;
 }
 
@@ -3339,7 +3315,7 @@ _dynamic_options_set (const NMMetaAbstractInfo *abstract_info,
 	PropertyInfFlags v, v2;
 
 	if (G_UNLIKELY (!cache))
-		cache = g_hash_table_new (NULL, NULL);
+		cache = g_hash_table_new (nm_direct_hash, NULL);
 
 	if (g_hash_table_lookup_extended (cache, (gpointer) abstract_info, NULL, &p))
 		v = GPOINTER_TO_UINT (p);
@@ -3595,6 +3571,7 @@ set_property (NMConnection *connection,
 		 */
 		if (value) {
 			unsigned long idx;
+
 			if (nmc_string_to_uint (value, TRUE, 0, G_MAXUINT32, &idx))
 				nmc_setting_remove_property_option (setting, property_name, NULL, idx, &local);
 			else
@@ -4265,7 +4242,7 @@ nmc_read_connection_properties (NmCli *nmc,
 				return FALSE;
 
 			if (!*argc && nmc->complete)
-				complete_property (setting, strv[1], value ? value : "", connection);
+				complete_property (setting, strv[1], value ?: "", connection);
 
 			if (!set_property (connection, setting_name, strv[1], value, modifier, error))
 				return FALSE;
@@ -4346,7 +4323,7 @@ nmc_read_connection_properties (NmCli *nmc,
 				return FALSE;
 
 			if (!*argc && nmc->complete)
-				complete_option (chosen, value ? value : "", connection);
+				complete_option (chosen, value ?: "", connection);
 
 			if (!set_option (nmc, connection, chosen, value, error))
 				return FALSE;
@@ -4457,8 +4434,8 @@ 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"));
-	gs_free char *yes = g_strdup_printf ("[%s]: ", gettext ("yes"));
+	gs_free char *no = g_strdup_printf ("[%s]: ", _("no"));
+	gs_free char *yes = g_strdup_printf ("[%s]: ", _("yes"));
 
 	/* Disable readline's default filename completion */
 	rl_attempted_completion_over = 1;
@@ -4741,9 +4718,9 @@ again:
 static NMCResultCode
 do_connection_add (NmCli *nmc, int argc, char **argv)
 {
-	NMConnection *connection = NULL;
+	gs_unref_object NMConnection *connection = NULL;
 	NMSettingConnection *s_con;
-	GError *error = NULL;
+	gs_free_error GError *error = NULL;
 	AddConnectionInfo *info = NULL;
 	gboolean save_bool = TRUE;
 	gboolean seen_dash_dash = FALSE;
@@ -4755,27 +4732,24 @@ do_connection_add (NmCli *nmc, int argc, char **argv)
 
 	nmc->return_value = NMC_RESULT_SUCCESS;
 
-	/* Create a new connection object */
 	connection = nm_simple_connection_new ();
 
-	/* Build up the 'connection' setting */
 	s_con = (NMSettingConnection *) nm_setting_connection_new ();
 	nm_connection_add_setting (connection, NM_SETTING (s_con));
 
 read_properties:
+	g_clear_error (&error);
 	/* Get the arguments from the command line if any */
 	if (argc && !nmc_read_connection_properties (nmc, connection, &argc, &argv, &error)) {
 		if (g_strcmp0 (*argv, "--") == 0 && !seen_dash_dash) {
 			/* This is for compatibility with older nmcli that required
 			 * options and properties to be separated with "--" */
-			g_clear_error (&error);
 			seen_dash_dash = TRUE;
 			next_arg (nmc, &argc, &argv, NULL);
 			goto read_properties;
 		} else if (g_strcmp0 (*argv, "save") == 0) {
 			/* It would be better if "save" was a separate argument and not
 			 * mixed with properties, but there's not much we can do about it now. */
-			g_clear_error (&error);
 			argc--;
 			argv++;
 			if (!argc) {
@@ -4785,11 +4759,11 @@ read_properties:
 				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
 				goto finish;
 			}
+			g_clear_error (&error);
 			if (!nmc_string_to_bool (*argv, &save_bool, &error)) {
 				g_string_printf (nmc->return_text, _("Error: 'save': %s."),
 				                 error->message);
 				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-				g_clear_error (&error);
 				goto finish;
 			}
 			next_arg (nmc, &argc, &argv, NULL);
@@ -4798,7 +4772,6 @@ read_properties:
 
 		g_string_assign (nmc->return_text, error->message);
 		nmc->return_value = error->code;
-		g_clear_error (&error);
 		goto finish;
 	}
 
@@ -4908,9 +4881,6 @@ read_properties:
 
 finish:
 	reset_options ();
-	if (connection)
-		g_object_unref (connection);
-
 	return nmc->return_value;
 }
 
@@ -4962,15 +4932,7 @@ gen_nmcli_cmds_submenu (const char *text, int state)
 static char *
 gen_cmd_nmcli (const char *text, int state)
 {
-	const char *words[] = { "status-line", "save-confirmation", "show-secrets", "prompt-color", NULL };
-	return nmc_rl_gen_func_basic (text, state, words);
-}
-
-static char *
-gen_cmd_nmcli_prompt_color (const char *text, int state)
-{
-	const char *words[] = { "normal", "black", "red", "green", "yellow",
-	                        "blue", "magenta", "cyan", "white", NULL };
+	const char *words[] = { "status-line", "save-confirmation", "show-secrets", NULL };
 	return nmc_rl_gen_func_basic (text, state, words);
 }
 
@@ -5286,8 +5248,6 @@ get_gen_func_cmd_nmcli (const char *str)
 		return gen_func_bool_values;
 	if (matches (str, "show-secrets"))
 		return gen_func_bool_values;
-	if (matches (str, "prompt-color"))
-		return gen_cmd_nmcli_prompt_color;
 	return NULL;
 }
 
@@ -5889,50 +5849,80 @@ typedef enum {
 	NMC_EDITOR_MAIN_CMD_QUIT,
 } NmcEditorMainCmd;
 
+static void
+_split_cmd (const char *cmd, char **out_arg0, const char **out_argr)
+{
+	gs_free char *arg0 = NULL;
+	const char *argr = NULL;
+	gsize l;
+
+	NM_SET_OUT (out_arg0, NULL);
+	NM_SET_OUT (out_argr, NULL);
+
+	if (!cmd)
+		return;
+	while (nm_utils_is_separator (cmd[0]))
+		cmd++;
+	if (!cmd[0])
+		return;
+
+	l = strcspn (cmd, " \t");
+	arg0 = g_strndup (cmd, l);
+	cmd += l;
+	if (cmd[0]) {
+		while (nm_utils_is_separator (cmd[0]))
+			cmd++;
+		if (cmd[0])
+			argr = cmd;
+	}
+
+	NM_SET_OUT (out_arg0, g_steal_pointer (&arg0));
+	NM_SET_OUT (out_argr, argr);
+}
+
 static NmcEditorMainCmd
 parse_editor_main_cmd (const char *cmd, char **cmd_arg)
 {
 	NmcEditorMainCmd editor_cmd = NMC_EDITOR_MAIN_CMD_UNKNOWN;
-	char **vec;
+	gs_free char *cmd_arg0 = NULL;
+	const char *cmd_argr;
 
-	vec = nmc_strsplit_set (cmd, " \t", 2);
-	if (g_strv_length (vec) < 1) {
-		if (cmd_arg)
-			*cmd_arg = NULL;
-		return NMC_EDITOR_MAIN_CMD_UNKNOWN;
-	}
+	_split_cmd (cmd, &cmd_arg0, &cmd_argr);
+	if (!cmd_arg0)
+		goto fail;
 
-	if (matches (vec[0], "goto"))
+	if (matches (cmd_arg0, "goto"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_GOTO;
-	else if (matches (vec[0], "remove"))
+	else if (matches (cmd_arg0, "remove"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_REMOVE;
-	else if (matches (vec[0], "set"))
+	else if (matches (cmd_arg0, "set"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_SET;
-	else if (matches (vec[0], "describe"))
+	else if (matches (cmd_arg0, "describe"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_DESCRIBE;
-	else if (matches (vec[0], "print"))
+	else if (matches (cmd_arg0, "print"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_PRINT;
-	else if (matches (vec[0], "verify"))
+	else if (matches (cmd_arg0, "verify"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_VERIFY;
-	else if (matches (vec[0], "save"))
+	else if (matches (cmd_arg0, "save"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_SAVE;
-	else if (matches (vec[0], "activate"))
+	else if (matches (cmd_arg0, "activate"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_ACTIVATE;
-	else if (matches (vec[0], "back"))
+	else if (matches (cmd_arg0, "back"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_BACK;
-	else if (matches (vec[0], "help") || strcmp (vec[0], "?") == 0)
+	else if (matches (cmd_arg0, "help") || strcmp (cmd_arg0, "?") == 0)
 		editor_cmd = NMC_EDITOR_MAIN_CMD_HELP;
-	else if (matches (vec[0], "quit"))
+	else if (matches (cmd_arg0, "quit"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_QUIT;
-	else if (matches (vec[0], "nmcli"))
+	else if (matches (cmd_arg0, "nmcli"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_NMCLI;
+	else
+		goto fail;
 
-	/* set pointer to command argument */
-	if (cmd_arg)
-		*cmd_arg = vec[1] ? g_strstrip (g_strdup (vec[1])) : NULL;
-
-	g_strfreev (vec);
+	NM_SET_OUT (cmd_arg, g_strdup (cmd_argr));
 	return editor_cmd;
+fail:
+	NM_SET_OUT (cmd_arg, NULL);
+	return NMC_EDITOR_MAIN_CMD_UNKNOWN;
 }
 
 static void
@@ -6081,40 +6071,39 @@ static NmcEditorSubCmd
 parse_editor_sub_cmd (const char *cmd, char **cmd_arg)
 {
 	NmcEditorSubCmd editor_cmd = NMC_EDITOR_SUB_CMD_UNKNOWN;
-	char **vec;
+	gs_free char *cmd_arg0 = NULL;
+	const char *cmd_argr;
 
-	vec = nmc_strsplit_set (cmd, " \t", 2);
-	if (g_strv_length (vec) < 1) {
-		if (cmd_arg)
-			*cmd_arg = NULL;
-		return NMC_EDITOR_SUB_CMD_UNKNOWN;
-	}
+	_split_cmd (cmd, &cmd_arg0, &cmd_argr);
+	if (!cmd_arg0)
+		goto fail;
 
-	if (matches (vec[0], "set"))
+	if (matches (cmd_arg0, "set"))
 		editor_cmd = NMC_EDITOR_SUB_CMD_SET;
-	else if (matches (vec[0], "add"))
+	else if (matches (cmd_arg0, "add"))
 		editor_cmd = NMC_EDITOR_SUB_CMD_ADD;
-	else if (matches (vec[0], "change"))
+	else if (matches (cmd_arg0, "change"))
 		editor_cmd = NMC_EDITOR_SUB_CMD_CHANGE;
-	else if (matches (vec[0], "remove"))
+	else if (matches (cmd_arg0, "remove"))
 		editor_cmd = NMC_EDITOR_SUB_CMD_REMOVE;
-	else if (matches (vec[0], "describe"))
+	else if (matches (cmd_arg0, "describe"))
 		editor_cmd = NMC_EDITOR_SUB_CMD_DESCRIBE;
-	else if (matches (vec[0], "print"))
+	else if (matches (cmd_arg0, "print"))
 		editor_cmd = NMC_EDITOR_SUB_CMD_PRINT;
-	else if (matches (vec[0], "back"))
+	else if (matches (cmd_arg0, "back"))
 		editor_cmd = NMC_EDITOR_SUB_CMD_BACK;
-	else if (matches (vec[0], "help") || strcmp (vec[0], "?") == 0)
+	else if (matches (cmd_arg0, "help") || strcmp (cmd_arg0, "?") == 0)
 		editor_cmd = NMC_EDITOR_SUB_CMD_HELP;
-	else if (matches (vec[0], "quit"))
+	else if (matches (cmd_arg0, "quit"))
 		editor_cmd = NMC_EDITOR_SUB_CMD_QUIT;
+	else
+		goto fail;
 
-	/* set pointer to command argument */
-	if (cmd_arg)
-		*cmd_arg = g_strdup (vec[1]);
-
-	g_strfreev (vec);
+	NM_SET_OUT (cmd_arg, g_strdup (cmd_argr));
 	return editor_cmd;
+fail:
+	NM_SET_OUT (cmd_arg, NULL);
+	return NMC_EDITOR_SUB_CMD_UNKNOWN;
 }
 
 static void
@@ -6259,8 +6248,8 @@ static gboolean
 progress_activation_editor_cb (gpointer user_data)
 {
 	MonitorACInfo *info = (MonitorACInfo *) user_data;
-	NMDevice *device = info->device;
-	NMActiveConnection *ac = info->ac;
+	gs_unref_object NMDevice *device = info->device;
+	gs_unref_object NMActiveConnection *ac = info->ac;
 	NMActiveConnectionState ac_state;
 	NMDeviceState dev_state;
 
@@ -6277,12 +6266,12 @@ progress_activation_editor_cb (gpointer user_data)
 		nmc_terminal_erase_line ();
 		g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
 		         nm_object_get_path (NM_OBJECT (ac)));
-		goto finish; /* we are done */
+		goto finish;
 	} else if (   ac_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED
 	           || dev_state == NM_DEVICE_STATE_FAILED) {
 		nmc_terminal_erase_line ();
 		g_print (_("Error: Connection activation failed.\n"));
-		goto finish; /* we are done */
+		goto finish;
 	}
 
 	if (info->nmc->secret_agent) {
@@ -6297,10 +6286,6 @@ progress_activation_editor_cb (gpointer user_data)
 
 finish:
 	info->monitor_id = 0;
-	if (device)
-		g_object_unref (device);
-	if (ac)
-		g_object_unref (ac);
 	return FALSE;
 }
 
@@ -6405,7 +6390,7 @@ refresh_remote_connection (GWeakRef *weak, NMRemoteConnection **remote)
 {
 	gboolean previous;
 
-	g_return_val_if_fail (remote != NULL, FALSE);
+	g_return_val_if_fail (remote, FALSE);
 
 	previous = (*remote != NULL);
 	if (*remote)
@@ -6454,33 +6439,31 @@ property_edit_submenu (NmCli *nmc,
                        const char *prop_name)
 {
 	NmcEditorSubCmd cmdsub;
-	gboolean cmd_property_loop = TRUE;
-	gboolean should_quit = FALSE;
-	char *prop_val_user;
 	gboolean set_result;
 	GError *tmp_err = NULL;
-	char *prompt;
-	gboolean dirty;
-	GValue prop_g_value = G_VALUE_INIT;
+	gs_free char *prompt = NULL;
 	gboolean temp_changes;
-	gboolean removed;
 
 	/* Set global variable for use in TAB completion */
 	nmc_tab_completion.property = prop_name;
 
-	prompt = nmc_colorize (nmc->nmc_config.use_colors, nmc->editor_prompt_color, NM_META_TERM_FORMAT_NORMAL,
-	                       "nmcli %s.%s> ",
+	prompt = nmc_colorize (&nmc->nmc_config, NM_META_COLOR_PROMPT, "nmcli %s.%s> ",
 	                       nm_setting_get_name (curr_setting), prop_name);
 
-	while (cmd_property_loop) {
-		char *cmd_property_user;
-		char *cmd_property_arg;
+	for (;;) {
+		gs_free char *cmd_property_user = NULL;
+		gs_free char *cmd_property_arg = NULL;
+		gs_free char *prop_val_user = NULL;
+		nm_auto_unset_gvalue GValue prop_g_value = G_VALUE_INIT;
+		gboolean removed;
+		gboolean dirty;
 
 		/* Get the remote connection again, it may have disapeared */
 		removed = refresh_remote_connection (rem_con_weak, rem_con);
-		if (removed)
+		if (removed) {
 			g_print (_("The connection profile has been removed from another client. "
 			           "You may type 'save' in the main menu to restore it.\n"));
+		}
 
 		/* Connection is dirty? (not saved or differs from the saved) */
 		dirty = is_connection_dirty (connection, *rem_con);
@@ -6489,7 +6472,7 @@ property_edit_submenu (NmCli *nmc,
 			editor_show_status_line (connection, dirty, temp_changes);
 
 		cmd_property_user = nmc_readline ("%s", prompt);
-		if (!cmd_property_user || *cmd_property_user == '\0')
+		if (!cmd_property_user || !*cmd_property_user)
 			continue;
 		cmdsub = parse_editor_sub_cmd (g_strstrip (cmd_property_user), &cmd_property_arg);
 
@@ -6525,7 +6508,6 @@ property_edit_submenu (NmCli *nmc,
 			}
 
 			set_result = nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err);
-			g_free (prop_val_user);
 			if (!set_result) {
 				g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
 				g_clear_error (&tmp_err);
@@ -6536,8 +6518,6 @@ property_edit_submenu (NmCli *nmc,
 					g_signal_handlers_unblock_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
 				}
 			}
-			if (G_IS_VALUE (&prop_g_value))
-				g_value_unset (&prop_g_value);
 			break;
 
 		case NMC_EDITOR_SUB_CMD_CHANGE:
@@ -6555,9 +6535,6 @@ property_edit_submenu (NmCli *nmc,
 				nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value);
 				g_signal_handlers_unblock_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
 			}
-			g_free (prop_val_user);
-			if (G_IS_VALUE (&prop_g_value))
-				g_value_unset (&prop_g_value);
 			break;
 
 		case NMC_EDITOR_SUB_CMD_REMOVE:
@@ -6610,8 +6587,7 @@ property_edit_submenu (NmCli *nmc,
 		case NMC_EDITOR_SUB_CMD_BACK:
 			/* Set global variable for use in TAB completion */
 			nmc_tab_completion.property = NULL;
-			cmd_property_loop = FALSE;
-			break;
+			return TRUE;
 
 		case NMC_EDITOR_SUB_CMD_HELP:
 			editor_sub_usage (cmd_property_arg);
@@ -6619,14 +6595,10 @@ property_edit_submenu (NmCli *nmc,
 
 		case NMC_EDITOR_SUB_CMD_QUIT:
 			if (is_connection_dirty (connection, *rem_con)) {
-				if (confirm_quit ()) {
-					cmd_property_loop = FALSE;
-					should_quit = TRUE;  /* we will quit nmcli */
-				}
-			} else {
-				cmd_property_loop = FALSE;
-				should_quit = TRUE;  /* we will quit nmcli */
-			}
+				if (confirm_quit ())
+					return FALSE;
+			} else
+				return FALSE;
 			break;
 
 		case NMC_EDITOR_SUB_CMD_UNKNOWN:
@@ -6634,12 +6606,7 @@ property_edit_submenu (NmCli *nmc,
 			g_print (_("Unknown command: '%s'\n"), cmd_property_user);
 			break;
 		}
-		g_free (cmd_property_user);
-		g_free (cmd_property_arg);
 	}
-	g_free (prompt);
-
-	return !should_quit;
 }
 
 /*
@@ -6650,29 +6617,26 @@ property_edit_submenu (NmCli *nmc,
 static void
 split_editor_main_cmd_args (const char *str, char **setting, char **property, char **value)
 {
-	char **args, **items;
+	gs_free char *cmd_arg0 = NULL;
+	const char *cmd_argr;
+	const char *s;
 
-	if (!str)
-		return;
+	NM_SET_OUT (setting, NULL);
+	NM_SET_OUT (property, NULL);
+	NM_SET_OUT (value, NULL);
 
-	args = nmc_strsplit_set (str, " \t", 2);
-	if (args[0]) {
-		items = nmc_strsplit_set (args[0], ".", 2);
-		if (g_strv_length (items) == 2) {
-			if (setting)
-				*setting = g_strdup (items[0]);
-			if (property)
-				*property = g_strdup (items[1]);
-		} else {
-			if (property)
-				*property = g_strdup (items[0]);
-		}
-		g_strfreev (items);
+	_split_cmd (str, &cmd_arg0, &cmd_argr);
+	if (!cmd_arg0)
+		return;
 
-		if (value && args[1])
-			*value = g_strstrip (g_strdup (args[1]));
+	NM_SET_OUT (value, g_strdup (cmd_argr));
+	s = strchr (cmd_arg0, '.');
+	if (s && s > cmd_arg0) {
+		NM_SET_OUT (setting, g_strndup (cmd_arg0, s - cmd_arg0));
+		NM_SET_OUT (property, g_strdup (&s[1]));
+	} else {
+		NM_SET_OUT (property, g_steal_pointer (&cmd_arg0));
 	}
-	g_strfreev (args);
 }
 
 static NMSetting *
@@ -6793,7 +6757,7 @@ confirm_connection_saving (NMConnection *local, NMConnection *remote)
 	return confirmed;
 }
 
-typedef	struct {
+typedef struct {
 	guint level;
 	char *main_prompt;
 	NMSetting *curr_setting;
@@ -6802,14 +6766,13 @@ typedef	struct {
 } NmcEditorMenuContext;
 
 static void
-menu_switch_to_level0 (NmcColorOption color_option,
+menu_switch_to_level0 (const NmcConfig *nmc_config,
                        NmcEditorMenuContext *menu_ctx,
-                       const char *prompt,
-                       NMMetaTermColor prompt_color)
+                       const char *prompt)
 {
 	menu_ctx->level = 0;
 	g_free (menu_ctx->main_prompt);
-	menu_ctx->main_prompt = nmc_colorize (color_option, prompt_color, NM_META_TERM_FORMAT_NORMAL, "%s", prompt);
+	menu_ctx->main_prompt = nmc_colorize (nmc_config, NM_META_COLOR_PROMPT, "%s", prompt);
 	menu_ctx->curr_setting = NULL;
 	g_strfreev (menu_ctx->valid_props);
 	menu_ctx->valid_props = NULL;
@@ -6818,16 +6781,14 @@ menu_switch_to_level0 (NmcColorOption color_option,
 }
 
 static void
-menu_switch_to_level1 (NmcColorOption color_option,
+menu_switch_to_level1 (const NmcConfig *nmc_config,
                        NmcEditorMenuContext *menu_ctx,
                        NMSetting *setting,
-                       const char *setting_name,
-                       NMMetaTermColor prompt_color)
+                       const char *setting_name)
 {
 	menu_ctx->level = 1;
 	g_free (menu_ctx->main_prompt);
-	menu_ctx->main_prompt = nmc_colorize (color_option, prompt_color, NM_META_TERM_FORMAT_NORMAL,
-	                                      "nmcli %s> ", setting_name);
+	menu_ctx->main_prompt = nmc_colorize (nmc_config, NM_META_COLOR_PROMPT, "nmcli %s> ", setting_name);
 	menu_ctx->curr_setting = setting;
 	g_strfreev (menu_ctx->valid_props);
 	menu_ctx->valid_props = nmc_setting_get_valid_properties (menu_ctx->curr_setting);
@@ -6850,7 +6811,6 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 	gs_free char *valid_settings_str = NULL;
 	const char *s_type = NULL;
 	AddConnectionInfo *info = NULL;
-	gboolean dirty;
 	gboolean temp_changes;
 	GError *err1 = NULL;
 	NmcEditorMenuContext menu_ctx = { 0 };
@@ -6865,8 +6825,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 	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.main_prompt = nmc_colorize (nmc->nmc_config.use_colors, nmc->editor_prompt_color, NM_META_TERM_FORMAT_NORMAL,
-	                                     BASE_PROMPT);
+	menu_ctx.main_prompt = nmc_colorize (&nmc->nmc_config, NM_META_COLOR_PROMPT, BASE_PROMPT);
 
 	/* Get remote connection */
 	con_tmp = nm_client_get_connection_by_uuid (nmc->client,
@@ -6880,6 +6839,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 		gs_free char *cmd_arg_s = NULL;
 		gs_free char *cmd_arg_p = NULL;
 		gs_free char *cmd_arg_v = NULL;
+		gboolean dirty;
 
 		/* Connection is dirty? (not saved or differs from the saved) */
 		dirty = is_connection_dirty (connection, rem_con);
@@ -6887,17 +6847,18 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 		if (nmc->editor_status_line)
 			editor_show_status_line (connection, dirty, temp_changes);
 
-		/* Read user input */
 		cmd_user = nmc_readline ("%s", menu_ctx.main_prompt);
 
 		/* Get the remote connection again, it may have disapeared */
 		removed = refresh_remote_connection (&weak, &rem_con);
-		if (removed)
+		if (removed) {
 			g_print (_("The connection profile has been removed from another client. "
 			           "You may type 'save' to restore it.\n"));
+		}
 
-		if (!cmd_user || *cmd_user == '\0')
+		if (!cmd_user || !*cmd_user)
 			continue;
+
 		cmd = parse_editor_main_cmd (g_strstrip (cmd_user), &cmd_arg);
 
 		split_editor_main_cmd_args (cmd_arg, &cmd_arg_s, &cmd_arg_p, &cmd_arg_v);
@@ -7005,7 +6966,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 				/* in top level - no setting selected yet */
 				const char *setting_name;
 				NMSetting *setting;
-				const char *user_arg = cmd_arg_s ? cmd_arg_s : cmd_arg_p;
+				const char *user_arg = cmd_arg_s ?: cmd_arg_p;
 
 				setting_name = ask_check_setting (user_arg,
 				                                  valid_settings_main,
@@ -7042,7 +7003,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 				nmc_tab_completion.setting = setting;
 
 				/* Switch to level 1 */
-				menu_switch_to_level1 (nmc->nmc_config.use_colors, &menu_ctx, setting, setting_name, nmc->editor_prompt_color);
+				menu_switch_to_level1 (&nmc->nmc_config, &menu_ctx, setting, setting_name);
 
 				if (!cmd_arg_s) {
 					g_print (_("You may edit the following properties: %s\n"), menu_ctx.valid_props_str);
@@ -7097,7 +7058,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 
 				/* cmd_arg_s != NULL means argument is "setting.property" */
 				descr_all = !cmd_arg_s && !menu_ctx.curr_setting;
-				user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
+				user_s = descr_all ? cmd_arg_p : cmd_arg_s;
 				if (user_s) {
 					ss = is_setting_valid (connection,
 					                       valid_settings_main,
@@ -7124,7 +7085,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 					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->nmc_config.use_colors, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
+						menu_switch_to_level0 (&nmc->nmc_config, &menu_ctx, BASE_PROMPT);
 						nmc_tab_completion.setting = NULL;  /* for TAB completion */
 					}
 				} else {
@@ -7152,7 +7113,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 							/* 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->nmc_config.use_colors, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
+								menu_switch_to_level0 (&nmc->nmc_config, &menu_ctx, BASE_PROMPT);
 								nmc_tab_completion.setting = NULL;  /* for TAB completion */
 							}
 						} else
@@ -7190,7 +7151,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 
 				/* cmd_arg_s != NULL means argument is "setting.property" */
 				descr_all = !cmd_arg_s && !menu_ctx.curr_setting;
-				user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
+				user_s = descr_all ? cmd_arg_p : cmd_arg_s;
 				if (user_s) {
 					ss = is_setting_valid (connection,
 					                       valid_settings_main,
@@ -7247,7 +7208,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 		case NMC_EDITOR_MAIN_CMD_PRINT:
 			/* Print current connection settings/properties */
 			if (cmd_arg) {
-				if (strcmp (cmd_arg, "all") == 0)
+				if (nm_streq (cmd_arg, "all"))
 					editor_show_connection (connection, nmc);
 				else {
 					NMSetting *ss = NULL;
@@ -7256,7 +7217,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 
 					/* cmd_arg_s != NULL means argument is "setting.property" */
 					whole_setting = !cmd_arg_s && !menu_ctx.curr_setting;
-					user_s = whole_setting ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
+					user_s = whole_setting ? cmd_arg_p : cmd_arg_s;
 					if (user_s) {
 						const char *s_name;
 
@@ -7508,7 +7469,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 		case NMC_EDITOR_MAIN_CMD_BACK:
 			/* Go back (up) an the menu */
 			if (menu_ctx.level == 1) {
-				menu_switch_to_level0 (nmc->nmc_config.use_colors, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
+				menu_switch_to_level0 (&nmc->nmc_config, &menu_ctx, BASE_PROMPT);
 				nmc_tab_completion.setting = NULL;  /* for TAB completion */
 			}
 			break;
@@ -7544,37 +7505,18 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 				} else
 					nmc->nmc_config_mutable.show_secrets = bb;
 			} else if (cmd_arg_p && matches (cmd_arg_p, "prompt-color")) {
-				GError *tmp_err = NULL;
-				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;
-					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_config.use_colors, nmc->editor_prompt_color, NM_META_TERM_FORMAT_NORMAL,
-						                                     "nmcli %s> ",
-						                                     nm_setting_get_name (menu_ctx.curr_setting));
-					}
-				}
+				g_debug ("Ignoring erroneous --prompt-color argument. Use terminal-colors.d(5) to set the prompt color.\n");
 			} else if (!cmd_arg_p) {
 				g_print (_("Current nmcli configuration:\n"));
 				g_print ("status-line: %s\n"
 				         "save-confirmation: %s\n"
-				         "show-secrets: %s\n"
-				         "prompt-color: %d\n",
+				         "show-secrets: %s\n",
 				         nmc->editor_status_line ? "yes" : "no",
 				         nmc->editor_save_confirmation ? "yes" : "no",
-				         nmc->nmc_config.show_secrets ? "yes" : "no",
-				         nmc->editor_prompt_color);
+				         nmc->nmc_config.show_secrets ? "yes" : "no");
 			} else
 				g_print (_("Invalid configuration option '%s'; allowed [%s]\n"),
-				         cmd_arg_v ? cmd_arg_v : "", "status-line, save-confirmation, show-secrets, prompt-color");
+				         cmd_arg_v ?: "", "status-line, save-confirmation, show-secrets");
 
 			break;
 
@@ -7645,7 +7587,7 @@ editor_init_new_connection (NmCli *nmc, NMConnection *connection, const char *sl
 
 		g_object_set (s_con,
 		              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
-		              NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0",
+		              NM_SETTING_CONNECTION_MASTER, dev_ifname ?: "eth0",
 		              NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
 		              NULL);
 	} else {
@@ -7666,11 +7608,10 @@ editor_init_new_connection (NmCli *nmc, NMConnection *connection, const char *sl
 			const char *dev_ifname = get_ethernet_device_name (nmc);
 
 			g_object_set (NM_SETTING_VLAN (base_setting),
-			              NM_SETTING_VLAN_PARENT, dev_ifname ? dev_ifname : "eth0",
+			              NM_SETTING_VLAN_PARENT, dev_ifname ?: "eth0",
 			              NULL);
 		}
 
-
 		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);
@@ -7731,28 +7672,24 @@ static NMCResultCode
 do_connection_edit (NmCli *nmc, int argc, char **argv)
 {
 	const GPtrArray *connections;
-	NMConnection *connection = NULL;
+	gs_unref_object NMConnection *connection = NULL;
 	NMSettingConnection *s_con;
 	const char *connection_type;
-	char *uuid;
-	char *default_name = NULL;
 	const char *type = NULL;
-	char *type_ask = NULL;
 	const char *con_name = NULL;
 	const char *con = NULL;
 	const char *con_id = NULL;
 	const char *con_uuid = NULL;
 	const char *con_path = NULL;
 	const char *selector = NULL;
-	char *tmp_str;
-	GError *error = NULL;
+	gs_free_error GError *error = NULL;
 	GError *err1 = NULL;
-	nmc_arg_t exp_args[] = { {"type",     TRUE, &type,     FALSE},
-	                         {"con-name", TRUE, &con_name, FALSE},
-	                         {"id",       TRUE, &con_id,   FALSE},
-	                         {"uuid",     TRUE, &con_uuid, FALSE},
-	                         {"path",     TRUE, &con_path, FALSE},
-	                         {NULL} };
+	nmc_arg_t exp_args[] = { { "type",     TRUE, &type,     FALSE },
+	                         { "con-name", TRUE, &con_name, FALSE },
+	                         { "id",       TRUE, &con_id,   FALSE },
+	                         { "uuid",     TRUE, &con_uuid, FALSE },
+	                         { "path",     TRUE, &con_path, FALSE },
+	                         { NULL } };
 
 	next_arg (nmc, &argc, &argv, NULL);
 	if (argc == 1 && nmc->complete)
@@ -7765,9 +7702,7 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 	else {
 		if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, &error)) {
 			g_string_assign (nmc->return_text, error->message);
-			nmc->return_value = error->code;
-			g_clear_error (&error);
-			goto error;
+			NMC_RETURN (nmc, error->code);
 		}
 	}
 
@@ -7794,8 +7729,7 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 		} else {
 			g_string_printf (nmc->return_text,
 			                 _("Error: only one of 'id', uuid, or 'path' can be provided."));
-			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-			goto error;
+			NMC_RETURN (nmc, NMC_RESULT_ERROR_USER_INPUT);
 		}
 	}
 
@@ -7805,12 +7739,11 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 
 		found_con = nmc_find_connection (connections, selector, con, NULL, nmc->complete);
 		if (nmc->complete)
-			goto error;
+			return nmc->return_value;
 
 		if (!found_con) {
 			g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), con);
-			nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
-			goto error;
+			NMC_RETURN (nmc, NMC_RESULT_ERROR_NOT_FOUND);
 		}
 
 		/* Duplicate the connection and use that so that we need not
@@ -7822,7 +7755,6 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 		update_secrets_in_connection (NM_REMOTE_CONNECTION (found_con), connection);
 
 		s_con = nm_connection_get_setting_connection (connection);
-		g_assert (s_con);
 		connection_type = nm_setting_connection_get_connection_type (s_con);
 
 		if (type)
@@ -7838,18 +7770,23 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 		editor_init_existing_connection (connection);
 	} else {
 		const char *slave_type = NULL;
+		gs_free char *uuid = NULL;
+		gs_free char *default_name = NULL;
+		gs_free char *tmp_str = NULL;
 
 		/* New connection */
 		if (nmc->complete) {
 			if (type && argc == 0)
 				nmc_complete_connection_type (type);
-			goto error;
+			return nmc->return_value;
 		}
 
 		connection_type = check_valid_name_toplevel (type, &slave_type, &err1);
 		tmp_str = get_valid_options_string_toplevel ();
 
 		while (!connection_type) {
+			gs_free char *type_ask = NULL;
+
 			if (!type)
 				g_print (_("Valid connection types: %s\n"), tmp_str);
 			else
@@ -7859,14 +7796,11 @@ 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_toplevel (type_ask, &slave_type, &err1);
-			g_free (type_ask);
 		}
-		g_free (tmp_str);
+		nm_clear_g_free (&tmp_str);
 
-		/* Create a new connection object */
 		connection = nm_simple_connection_new ();
 
-		/* Build up the 'connection' setting */
 		s_con = (NMSettingConnection *) nm_setting_connection_new ();
 		uuid = nm_utils_uuid_generate ();
 		if (con_name)
@@ -7881,8 +7815,6 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 		              NM_SETTING_CONNECTION_UUID, uuid,
 		              NM_SETTING_CONNECTION_TYPE, connection_type,
 		              NULL);
-		g_free (uuid);
-		g_free (default_name);
 		nm_connection_add_setting (connection, NM_SETTING (s_con));
 
 		/* Initialize the new connection so that it is valid from the start */
@@ -7905,7 +7837,6 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 	g_print (_("Type 'describe [<setting>.<prop>]' for detailed property description."));
 	g_print ("\n\n");
 
-	/* Set global variables for use in TAB completion */
 	nmc_tab_completion.nmc = nmc;
 	nmc_tab_completion.con_type = g_strdup (connection_type);
 	nmc_tab_completion.connection = connection;
@@ -7913,15 +7844,9 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 	/* Run menu loop */
 	editor_menu_main (nmc, connection, connection_type);
 
-	if (connection)
-		g_object_unref (connection);
-	g_free (nmc_tab_completion.con_type);
-
-	return nmc->return_value;
-
-error:
-	g_assert (!connection);
-	g_free (type_ask);
+	nmc_tab_completion.nmc = NULL;
+	nm_clear_g_free (&nmc_tab_completion.con_type);
+	nmc_tab_completion.connection = NULL;
 
 	return nmc->return_value;
 }
@@ -7931,8 +7856,8 @@ modify_connection_cb (GObject *connection,
                       GAsyncResult *result,
                       gpointer user_data)
 {
-	NmCli *nmc = (NmCli *) user_data;
-	GError *error = NULL;
+	NmCli *nmc = user_data;
+	gs_free_error GError *error = NULL;
 
 	if (!nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (connection),
 	                                                 result, &error)) {
@@ -7940,13 +7865,13 @@ modify_connection_cb (GObject *connection,
 		                 _("Error: Failed to modify connection '%s': %s"),
 		                 nm_connection_get_id (NM_CONNECTION (connection)),
 		                 error->message);
-		g_error_free (error);
 		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
 	} else {
-		if (nmc->nmc_config.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)));
+		}
 	}
 	quit ();
 }
@@ -7958,20 +7883,18 @@ do_connection_modify (NmCli *nmc,
 {
 	NMConnection *connection = NULL;
 	NMRemoteConnection *rc = NULL;
-	GError *error = NULL;
+	gs_free_error GError *error = NULL;
 	gboolean temporary = FALSE;
 
-	/* Check --temporary */
 	if (next_arg (nmc, &argc, &argv, "--temporary", NULL) > 0) {
 		temporary = TRUE;
 		next_arg (nmc, &argc, &argv, NULL);
 	}
 
-	connection = get_connection (nmc, &argc, &argv, NULL, &error);
+	connection = get_connection (nmc, &argc, &argv, NULL, NULL, NULL, &error);
 	if (!connection) {
 		g_string_printf (nmc->return_text, _("Error: %s."), error->message);
-		nmc->return_value = error->code;
-		goto finish;
+		NMC_RETURN (nmc, error->code);
 	}
 
 	rc = nm_client_get_connection_by_uuid (nmc->client,
@@ -7979,24 +7902,20 @@ do_connection_modify (NmCli *nmc,
 	if (!rc) {
 		g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."),
 		                 nm_connection_get_uuid (connection));
-		nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
-		goto finish;
+		NMC_RETURN (nmc, NMC_RESULT_ERROR_NOT_FOUND);
 	}
 
 	if (!nmc_read_connection_properties (nmc, NM_CONNECTION (rc), &argc, &argv, &error)) {
 		g_string_assign (nmc->return_text, error->message);
-		nmc->return_value = error->code;
-		g_clear_error (&error);
-		goto finish;
+		NMC_RETURN (nmc, error->code);
 	}
 
 	if (nmc->complete)
-		goto finish;
+		return nmc->return_value;
 
 	update_connection (!temporary, rc, modify_connection_cb, nmc);
 	nmc->should_wait++;
 
-finish:
 	return nmc->return_value;
 }
 
@@ -8044,11 +7963,11 @@ static NMCResultCode
 do_connection_clone (NmCli *nmc, int argc, char **argv)
 {
 	NMConnection *connection = NULL;
-	NMConnection *new_connection = NULL;
+	gs_unref_object NMConnection *new_connection = NULL;
 	NMSettingConnection *s_con;
 	CloneConnectionInfo *info;
 	const char *new_name;
-	char *new_name_ask = NULL;
+	gs_free char *new_name_ask = NULL;
 	char *uuid;
 	gboolean temporary = FALSE;
 	char **arg_arr = NULL;
@@ -8078,15 +7997,14 @@ do_connection_clone (NmCli *nmc, int argc, char **argv)
 		argc_ptr = &arg_num;
 	}
 
-	connection = get_connection (nmc, argc_ptr, argv_ptr, NULL, &error);
+	connection = get_connection (nmc, argc_ptr, argv_ptr, NULL, NULL, NULL, &error);
 	if (!connection) {
 		g_string_printf (nmc->return_text, _("Error: %s."), error->message);
-		nmc->return_value = error->code;
-		goto finish;
+		NMC_RETURN (nmc, error->code);
 	}
 
 	if (nmc->complete)
-		goto finish;
+		return nmc->return_value;
 
 	if (argv[0])
 		new_name = *argv;
@@ -8094,14 +8012,12 @@ do_connection_clone (NmCli *nmc, int argc, char **argv)
 		new_name = new_name_ask = nmc_readline (_("New connection name: "));
 	else {
 		g_string_printf (nmc->return_text, _("Error: <new name> argument is missing."));
-		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-		goto finish;
+		NMC_RETURN (nmc, NMC_RESULT_ERROR_USER_INPUT);
 	}
 
 	if (next_arg (nmc->ask ? NULL : nmc, argc_ptr, argv_ptr, NULL) == 0) {
 		g_string_printf (nmc->return_text, _("Error: unknown extra argument: '%s'."), *argv);
-		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-		goto finish;
+		NMC_RETURN (nmc, NMC_RESULT_ERROR_USER_INPUT);
 	}
 
 	/* Copy the connection */
@@ -8133,11 +8049,6 @@ do_connection_clone (NmCli *nmc, int argc, char **argv)
 	                    info);
 
 	nmc->should_wait++;
-finish:
-	if (new_connection)
-		g_object_unref (new_connection);
-	g_free (new_name_ask);
-
 	return nmc->return_value;
 }
 
@@ -8167,12 +8078,12 @@ do_connection_delete (NmCli *nmc, int argc, char **argv)
 {
 	NMConnection *connection;
 	ConnectionCbInfo *info = NULL;
-	GSList *queue = NULL, *iter;
-	char **arg_arr = NULL, *old_arg;
+	gs_strfreev char **arg_arr = NULL;
 	char **arg_ptr;
+	guint i;
 	int arg_num;
-	GString *invalid_cons = NULL;
-	int pos = 0;
+	nm_auto_free_gstring GString *invalid_cons = NULL;
+	gs_unref_ptrarray GPtrArray *found_cons = NULL;
 	GError *error = NULL;
 
 	if (nmc->timeout == -1)
@@ -8202,15 +8113,10 @@ do_connection_delete (NmCli *nmc, int argc, char **argv)
 	}
 
 	while (arg_num > 0) {
-		old_arg = *arg_ptr;
-		connection = get_connection (nmc, &arg_num, &arg_ptr, &pos, &error);
-		if (connection) {
-			/* Check if the connection is unique. */
-			/* Calling delete for the same connection repeatedly would result in
-			 * NM responding for the last D-Bus call only and we would stall. */
-			if (!g_slist_find (queue, connection))
-				queue = g_slist_prepend (queue, g_object_ref (connection));
-		} else {
+		const char *cur_selector, *cur_value;
+
+		connection = get_connection (nmc, &arg_num, &arg_ptr, &cur_selector, &cur_value, &found_cons, &error);
+		if (!connection) {
 			if (!nmc->complete)
 				g_printerr (_("Error: %s.\n"), error->message);
 			g_string_printf (nmc->return_text, _("Error: not all connections found."));
@@ -8222,23 +8128,31 @@ do_connection_delete (NmCli *nmc, int argc, char **argv)
 
 			if (!invalid_cons)
 				invalid_cons = g_string_new (NULL);
-			g_string_append_printf (invalid_cons, "'%s', ", old_arg);
+			if (cur_selector)
+				g_string_append_printf (invalid_cons, "%s '%s', ", cur_selector, cur_value);
+			else
+				g_string_append_printf (invalid_cons, "'%s', ", cur_value);
 		}
 	}
 
-	if (!queue) {
-		g_string_printf (nmc->return_text, _("Error: No connection specified."));
-		nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
-		goto finish;
-	} else if (nmc->complete) {
-		g_slist_free (queue);
+	if (!found_cons) {
+		if (!invalid_cons) {
+			g_string_printf (nmc->return_text, _("Error: No connection specified."));
+			nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
+		}
 		goto finish;
 	}
-	queue = g_slist_reverse (queue);
+
+	if (nmc->complete)
+		goto finish;
 
 	info = g_slice_new0 (ConnectionCbInfo);
 	info->nmc = nmc;
-	info->queue = queue;
+	info->obj_list = g_ptr_array_sized_new (found_cons->len);
+	for (i = 0; i < found_cons->len; i++) {
+		connection = found_cons->pdata[i];
+		g_ptr_array_add (info->obj_list, g_object_ref (connection));
+	}
 	info->timeout_id = g_timeout_add_seconds (nmc->timeout, connection_op_timeout_cb, info);
 	info->cancellable = g_cancellable_new ();
 
@@ -8248,10 +8162,10 @@ do_connection_delete (NmCli *nmc, int argc, char **argv)
 	g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_REMOVED,
 	                  G_CALLBACK (connection_removed_cb), info);
 
-	/* Now delete the connections */
-	for (iter = queue; iter; iter = g_slist_next (iter))
-		nm_remote_connection_delete_async (NM_REMOTE_CONNECTION (iter->data),
+	for (i = 0; i < found_cons->len; i++) {
+		nm_remote_connection_delete_async (NM_REMOTE_CONNECTION (found_cons->pdata[i]),
 		                                   info->cancellable, delete_cb, info);
+	}
 
 finish:
 	if (invalid_cons) {
@@ -8259,9 +8173,7 @@ finish:
 		g_string_printf (nmc->return_text, _("Error: cannot delete unknown connection(s): %s."),
 		                 invalid_cons->str);
 		nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
-		g_string_free (invalid_cons, TRUE);
 	}
-	g_strfreev (arg_arr);
 	return nmc->return_value;
 }
 
@@ -8311,31 +8223,21 @@ static NMCResultCode
 do_connection_monitor (NmCli *nmc, int argc, char **argv)
 {
 	GError *error = NULL;
+	guint i;
+	gs_unref_ptrarray GPtrArray *found_cons = NULL;
+	const GPtrArray *connections = NULL;
 
 	next_arg (nmc, &argc, &argv, NULL);
 	if (argc == 0) {
 		/* No connections specified. Monitor all. */
-		const GPtrArray *connections;
-		int i;
 
 		/* nmc_do_cmd() should not call this with argc=0. */
 		g_assert (!nmc->complete);
 
 		connections = nm_client_get_connections (nmc->client);
-		for (i = 0; i < connections->len; i++)
-			connection_watch (nmc, g_ptr_array_index (connections, i));
-
-		/* We'll watch the connection additions too, never exit. */
-		nmc->should_wait++;
-		g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_ADDED, G_CALLBACK (connection_added), nmc);
 	} else {
-		/* Look up the specified connections and watch them. */
-		NMConnection *connection;
-		int pos = 0;
-
-		do {
-			connection = get_connection (nmc, &argc, &argv, &pos, &error);
-			if (!connection) {
+		while (argc > 0) {
+			if (!get_connection (nmc, &argc, &argv, NULL, NULL, &found_cons, &error)) {
 				if (!nmc->complete)
 					g_printerr (_("Error: %s.\n"), error->message);
 				g_string_printf (nmc->return_text, _("Error: not all connections found."));
@@ -8345,8 +8247,17 @@ do_connection_monitor (NmCli *nmc, int argc, char **argv)
 			if (nmc->complete)
 				continue;
 
-			connection_watch (nmc, connection);
-		} while (argc > 0);
+			connections = found_cons;
+		}
+	}
+
+	for (i = 0; i < connections->len; i++)
+		connection_watch (nmc, connections->pdata[i]);
+
+	if (argc == 0) {
+		/* We'll watch the connection additions too, never exit. */
+		nmc->should_wait++;
+		g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_ADDED, G_CALLBACK (connection_added), nmc);
 	}
 
 	if (nmc->complete)
@@ -8419,11 +8330,12 @@ do_connection_load (NmCli *nmc, int argc, char **argv)
 static NMCResultCode
 do_connection_import (NmCli *nmc, int argc, char **argv)
 {
-	GError *error = NULL;
+	gs_free_error GError *error = NULL;
 	const char *type = NULL, *filename = NULL;
-	char *type_ask = NULL, *filename_ask = NULL;
+	gs_free char *type_ask = NULL;
+	gs_free char *filename_ask = NULL;
 	AddConnectionInfo *info;
-	NMConnection *connection = NULL;
+	gs_unref_object NMConnection *connection = NULL;
 	NMVpnEditorPlugin *plugin;
 	gs_free char *service_type = NULL;
 	gboolean temporary = FALSE;
@@ -8434,7 +8346,6 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 		next_arg (nmc, &argc, &argv, NULL);
 	}
 
-
 	if (argc == 0) {
 		/* nmc_do_cmd() should not call this with argc=0. */
 		g_assert (!nmc->complete);
@@ -8446,8 +8357,7 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 			filename = filename_ask = filename_ask ? g_strstrip (filename_ask) : NULL;
 		} else {
 			g_string_printf (nmc->return_text, _("Error: No arguments provided."));
-			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-			goto finish;
+			NMC_RETURN (nmc, NMC_RESULT_ERROR_USER_INPUT);
 		}
 	}
 
@@ -8460,8 +8370,7 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
-				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-				goto finish;
+				NMC_RETURN (nmc, NMC_RESULT_ERROR_USER_INPUT);
 			}
 
 			if (argc == 1 && nmc->complete)
@@ -8477,8 +8386,7 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
-				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-				goto finish;
+				NMC_RETURN (nmc, NMC_RESULT_ERROR_USER_INPUT);
 			}
 			if (argc == 1 && nmc->complete)
 				nmc->return_value = NMC_RESULT_COMPLETE_FILE;
@@ -8488,32 +8396,28 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 				g_printerr (_("Warning: 'file' already specified, ignoring extra one.\n"));
 		} else {
 			g_string_printf (nmc->return_text, _("Unknown parameter: %s"), *argv);
-			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-			goto finish;
+			NMC_RETURN (nmc, NMC_RESULT_ERROR_USER_INPUT);
 		}
 
 		next_arg (nmc, &argc, &argv, NULL);
 	}
 
 	if (nmc->complete)
-		goto finish;
+		return nmc->return_value;
 
 	if (!type) {
 		g_string_printf (nmc->return_text, _("Error: 'type' argument is required."));
-		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-		goto finish;
+		NMC_RETURN (nmc, NMC_RESULT_ERROR_USER_INPUT);
 	}
 	if (!filename) {
 		g_string_printf (nmc->return_text, _("Error: 'file' argument is required."));
-		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-		goto finish;
+		NMC_RETURN (nmc, NMC_RESULT_ERROR_USER_INPUT);
 	}
 
 	service_type = nm_vpn_plugin_info_list_find_service_type (nm_vpn_get_plugin_infos (), type);
 	if (!service_type) {
 		g_string_printf (nmc->return_text, _("Error: failed to find VPN plugin for %s."), type);
-		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
-		goto finish;
+		NMC_RETURN (nmc, NMC_RESULT_ERROR_UNKNOWN);
 	}
 
 	/* Import VPN configuration */
@@ -8521,16 +8425,14 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 	if (!plugin) {
 		g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."),
 		                 error->message);
-		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
-		goto finish;
+		NMC_RETURN (nmc, NMC_RESULT_ERROR_UNKNOWN);
 	}
 
 	connection = nm_vpn_editor_plugin_import (plugin, filename, &error);
 	if (!connection) {
 		g_string_printf (nmc->return_text, _("Error: failed to import '%s': %s."),
 		                 filename, error->message);
-		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
-		goto finish;
+		NMC_RETURN (nmc, NMC_RESULT_ERROR_UNKNOWN);
 	}
 
 	info = g_malloc0 (sizeof (AddConnectionInfo));
@@ -8545,12 +8447,6 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 	                    info);
 
 	nmc->should_wait++;
-finish:
-	if (connection)
-		g_object_unref (connection);
-	g_clear_error (&error);
-	g_free (type_ask);
-	g_free (filename_ask);
 	return nmc->return_value;
 }
 
@@ -8559,12 +8455,11 @@ do_connection_export (NmCli *nmc, int argc, char **argv)
 {
 	NMConnection *connection = NULL;
 	const char *out_name = NULL;
-	char *name_ask = NULL;
-	char *out_name_ask = NULL;
+	gs_free char *out_name_ask = NULL;
 	const char *path = NULL;
 	const char *type = NULL;
 	NMVpnEditorPlugin *plugin;
-	GError *error = NULL;
+	gs_free_error GError *error = NULL;
 	char tmpfile[] = "/tmp/nmcli-export-temp-XXXXXX";
 	char **arg_arr = NULL;
 	int arg_num;
@@ -8588,7 +8483,7 @@ do_connection_export (NmCli *nmc, int argc, char **argv)
 		argc_ptr = &arg_num;
 	}
 
-	connection = get_connection (nmc, argc_ptr, argv_ptr, NULL, &error);
+	connection = get_connection (nmc, argc_ptr, argv_ptr, NULL, NULL, NULL, &error);
 	if (!connection) {
 		g_string_printf (nmc->return_text, _("Error: %s."), error->message);
 		nmc->return_value = error->code;
@@ -8629,14 +8524,14 @@ do_connection_export (NmCli *nmc, int argc, char **argv)
 	if (out_name)
 		path = out_name;
 	else {
-		int fd;
+		nm_auto_close int fd = -1;
+
 		fd = g_mkstemp (tmpfile);
 		if (fd == -1) {
 			g_string_printf (nmc->return_text, _("Error: failed to create temporary file %s."), tmpfile);
 			nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
 			goto finish;
 		}
-		nm_close (fd);
 		path = tmpfile;
 	}
 
@@ -8649,8 +8544,9 @@ do_connection_export (NmCli *nmc, int argc, char **argv)
 
 	/* No output file -> copy data to stdout */
 	if (!out_name) {
-		char *contents = NULL;
+		gs_free char *contents = NULL;
 		gsize len = 0;
+
 		if (!g_file_get_contents (path, &contents, &len, &error)) {
 			g_string_printf (nmc->return_text, _("Error: failed to read temporary file '%s': %s."),
 			                 path, error->message);
@@ -8658,15 +8554,11 @@ do_connection_export (NmCli *nmc, int argc, char **argv)
 			goto finish;
 		}
 		g_print ("%s", contents);
-		g_free (contents);
 	}
 
 finish:
 	if (!out_name && path)
 		unlink (path);
-	g_clear_error (&error);
-	g_free (name_ask);
-	g_free (out_name_ask);
 	return nmc->return_value;
 }
 
diff --git a/clients/cli/connections.h b/clients/cli/connections.h
index 01e78b02..591e9cda 100644
--- a/clients/cli/connections.h
+++ b/clients/cli/connections.h
@@ -14,7 +14,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * (C) Copyright 2010 - 2014 Red Hat, Inc.
+ * (C) Copyright 2010 - 2018 Red Hat, Inc.
  */
 
 #ifndef NMC_CONNECTIONS_H
@@ -33,7 +33,7 @@ nmc_read_connection_properties (NmCli *nmc,
                                 char ***argv,
                                 GError **error);
 
-void nmc_active_connection_state_to_color (NMActiveConnectionState state, NMMetaTermColor *color);
+NMMetaColor nmc_active_connection_state_to_color (NMActiveConnectionState state);
 
 extern const NmcMetaGenericInfo *const nmc_fields_con_show[];
 extern const NmcMetaGenericInfo *const nmc_fields_con_active_details_general[];
diff --git a/clients/cli/devices.c b/clients/cli/devices.c
index da969fbe..b99e606a 100644
--- a/clients/cli/devices.c
+++ b/clients/cli/devices.c
@@ -14,7 +14,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2010 - 2014 Red Hat, Inc.
+ * Copyright 2010 - 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -51,7 +51,6 @@ const NmcMetaGenericInfo *const nmc_fields_dev_status[] = {
 };
 #define NMC_FIELDS_DEV_STATUS_COMMON  "DEVICE,TYPE,STATE,CONNECTION"
 
-
 const NmcMetaGenericInfo *const nmc_fields_dev_show_general[] = {
 	NMC_META_GENERIC ("NAME"),                /* 0 */
 	NMC_META_GENERIC ("DEVICE"),              /* 1 */
@@ -210,9 +209,9 @@ const NmcMetaGenericInfo *const nmc_fields_dev_show_sections[] = {
 	NMC_META_GENERIC_WITH_NESTED ("WIMAX-PROPERTIES",  nmc_fields_dev_show_wimax_prop + 1),   /* 5 */
 	NMC_META_GENERIC_WITH_NESTED ("NSP",               nmc_fields_dev_wimax_list + 1),        /* 6 */
 	NMC_META_GENERIC_WITH_NESTED ("IP4",               metagen_ip4_config),                   /* 7 */
-	NMC_META_GENERIC_WITH_NESTED ("DHCP4",             nmc_fields_dhcp4_config + 1),          /* 8 */
+	NMC_META_GENERIC_WITH_NESTED ("DHCP4",             nmc_fields_dhcp_config + 1),           /* 8 */
 	NMC_META_GENERIC_WITH_NESTED ("IP6",               nmc_fields_ip6_config + 1),            /* 9 */
-	NMC_META_GENERIC_WITH_NESTED ("DHCP6",             nmc_fields_dhcp6_config + 1),          /* 10 */
+	NMC_META_GENERIC_WITH_NESTED ("DHCP6",             nmc_fields_dhcp_config + 1),           /* 10 */
 	NMC_META_GENERIC_WITH_NESTED ("BOND",              nmc_fields_dev_show_master_prop + 1),  /* 11 */
 	NMC_META_GENERIC_WITH_NESTED ("TEAM",              nmc_fields_dev_show_team_prop + 1),    /* 12 */
 	NMC_META_GENERIC_WITH_NESTED ("BRIDGE",            nmc_fields_dev_show_master_prop + 1),  /* 13 */
@@ -451,11 +450,8 @@ usage_device_lldp (void)
 static void
 quit (void)
 {
-	if (progress_id) {
-		g_source_remove (progress_id);
+	if (nm_clear_g_source (&progress_id))
 		nmc_terminal_erase_line ();
-	}
-
 	g_main_loop_quit (loop);
 }
 
@@ -711,22 +707,19 @@ sort_access_points (const GPtrArray *aps)
 	return sorted;
 }
 
-static void
-wifi_signal_to_color (guint8 strength, NMMetaTermColor *color, NMMetaTermFormat *color_fmt)
+static NMMetaColor
+wifi_signal_to_color (guint8 strength)
 {
-	*color = NM_META_TERM_COLOR_NORMAL;
-	*color_fmt = NM_META_TERM_FORMAT_NORMAL;
-
 	if (strength > 80)
-		*color = NM_META_TERM_COLOR_GREEN;
+		return NM_META_COLOR_WIFI_SIGNAL_EXCELLENT;
 	else if (strength > 55)
-		*color = NM_META_TERM_COLOR_YELLOW;
+		return NM_META_COLOR_WIFI_SIGNAL_GOOD;
 	else if (strength > 30)
-		*color = NM_META_TERM_COLOR_MAGENTA;
+		return NM_META_COLOR_WIFI_SIGNAL_FAIR;
 	else if (strength > 5)
-		*color = NM_META_TERM_COLOR_CYAN;
+		return NM_META_COLOR_WIFI_SIGNAL_POOR;
 	else
-		*color_fmt = NM_META_TERM_FORMAT_DIM;
+		return NM_META_COLOR_WIFI_SIGNAL_UNKNOWN;
 }
 
 static char *
@@ -799,8 +792,7 @@ fill_output_access_point (gpointer data, gpointer user_data)
 	GString *security_str;
 	char *ap_name;
 	const char *sig_bars;
-	NMMetaTermColor color;
-	NMMetaTermFormat color_fmt;
+	NMMetaColor color;
 
 	if (info->active_bssid) {
 		const char *current_bssid = nm_access_point_get_bssid (ap);
@@ -834,7 +826,7 @@ fill_output_access_point (gpointer data, gpointer user_data)
 	strength_str = g_strdup_printf ("%u", strength);
 	wpa_flags_str = ap_wpa_rsn_flags_to_string (wpa_flags);
 	rsn_flags_str = ap_wpa_rsn_flags_to_string (rsn_flags);
-	sig_bars = nm_utils_wifi_strength_bars (strength);
+	sig_bars = nmc_wifi_strength_bars (strength);
 
 	security_str = g_string_new (NULL);
 
@@ -886,11 +878,10 @@ fill_output_access_point (gpointer data, gpointer user_data)
 	set_val_strc (arr, 16, nm_object_get_path (NM_OBJECT (ap)));
 
 	/* Set colors */
-	wifi_signal_to_color (strength, &color, &color_fmt);
+	color = wifi_signal_to_color (strength);
 	set_val_color_all (arr, color);
-	set_val_color_fmt_all (arr, color_fmt);
 	if (active)
-		arr[15].color = NM_META_TERM_COLOR_GREEN;
+		arr[15].color = NM_META_COLOR_CONNECTION_ACTIVATED;
 
 	g_ptr_array_add (info->output_data, arr);
 
@@ -1164,7 +1155,7 @@ show_device_info (NMDevice *device, NmCli *nmc)
 			set_val_strc (arr, 6, nm_device_get_driver (device) ? nm_device_get_driver (device) : _("(unknown)"));
 			set_val_strc (arr, 7, nm_device_get_driver_version (device));
 			set_val_strc (arr, 8, nm_device_get_firmware_version (device));
-			set_val_strc (arr, 9, hwaddr ? hwaddr : _("(unknown)"));
+			set_val_strc (arr, 9, hwaddr ?: _("(unknown)"));
 			set_val_str  (arr, 10, mtu_str);
 			set_val_str  (arr, 11, state_str);
 			set_val_str  (arr, 12, reason_str);
@@ -1272,7 +1263,7 @@ show_device_info (NMDevice *device, NmCli *nmc)
 				}
 
 				tmpl = (const NMMetaAbstractInfo *const*) nmc_fields_dev_wifi_list;
-				out_indices = parse_output_fields (section_fld ? section_fld : NMC_FIELDS_DEV_WIFI_LIST_FOR_DEV_LIST,
+				out_indices = parse_output_fields (section_fld ?: NMC_FIELDS_DEV_WIFI_LIST_FOR_DEV_LIST,
 				                                   tmpl, FALSE, NULL, NULL);
 				arr = nmc_dup_fields_array (tmpl, NMC_OF_FLAG_FIELD_NAMES);
 				g_ptr_array_add (out.output_data, arr);
@@ -1332,7 +1323,7 @@ show_device_info (NMDevice *device, NmCli *nmc)
 
 		/* DHCP4 */
 		if (dhcp4 && !strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[8]->name))
-			was_output = print_dhcp4_config (dhcp4, &nmc->nmc_config, nmc_fields_dev_show_sections[8]->name, section_fld);
+			was_output = print_dhcp_config (dhcp4, &nmc->nmc_config, nmc_fields_dev_show_sections[8]->name, section_fld);
 
 		/* IP6 */
 		if (cfg6 && !strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[9]->name))
@@ -1340,7 +1331,7 @@ show_device_info (NMDevice *device, NmCli *nmc)
 
 		/* DHCP6 */
 		if (dhcp6 && !strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[10]->name))
-			was_output = print_dhcp6_config (dhcp6, &nmc->nmc_config, nmc_fields_dev_show_sections[10]->name, section_fld);
+			was_output = print_dhcp_config (dhcp6, &nmc->nmc_config, nmc_fields_dev_show_sections[10]->name, section_fld);
 
 		/* Bond specific information */
 		if (NM_IS_DEVICE_BOND (device)) {
@@ -1467,20 +1458,19 @@ show_device_info (NMDevice *device, NmCli *nmc)
 	return TRUE;
 }
 
-void
-nmc_device_state_to_color (NMDeviceState state, NMMetaTermColor *color, NMMetaTermFormat *color_fmt)
+NMMetaColor
+nmc_device_state_to_color (NMDeviceState state)
 {
-	*color = NM_META_TERM_COLOR_NORMAL;
-	*color_fmt = NM_META_TERM_FORMAT_NORMAL;
-
 	if (state <= NM_DEVICE_STATE_UNAVAILABLE)
-		*color_fmt= NM_META_TERM_FORMAT_DIM;
+		return NM_META_COLOR_DEVICE_UNAVAILABLE;
 	else if (state == NM_DEVICE_STATE_DISCONNECTED)
-		*color = NM_META_TERM_COLOR_RED;
+		return NM_META_COLOR_DEVICE_DISCONNECTED;
 	else if (state >= NM_DEVICE_STATE_PREPARE && state <= NM_DEVICE_STATE_SECONDARIES)
-		*color = NM_META_TERM_COLOR_YELLOW;
+		return NM_META_COLOR_DEVICE_ACTIVATING;
 	else if (state == NM_DEVICE_STATE_ACTIVATED)
-		*color = NM_META_TERM_COLOR_GREEN;
+		return NM_META_COLOR_DEVICE_ACTIVATED;
+
+	g_return_val_if_reached (NM_META_COLOR_DEVICE_UNKNOWN);
 }
 
 static void
@@ -1488,8 +1478,7 @@ fill_output_device_status (NMDevice *device, GPtrArray *output_data)
 {
 	NMActiveConnection *ac;
 	NMDeviceState state;
-	NMMetaTermColor color;
-	NMMetaTermFormat color_fmt;
+	NMMetaColor color;
 	NmcOutputField *arr = nmc_dup_fields_array ((const NMMetaAbstractInfo *const*) nmc_fields_dev_status,
 	                                            0);
 
@@ -1497,9 +1486,8 @@ fill_output_device_status (NMDevice *device, GPtrArray *output_data)
 	ac = nm_device_get_active_connection (device);
 
 	/* Show devices in color */
-	nmc_device_state_to_color (state, &color, &color_fmt);
+	color = nmc_device_state_to_color (state);
 	set_val_color_all (arr, color);
-	set_val_color_fmt_all (arr, color_fmt);
 
 	set_val_strc (arr, 0, nm_device_get_iface (device));
 	set_val_strc (arr, 1, nm_device_get_type_description (device));
@@ -2410,12 +2398,11 @@ static void
 device_state (NMDevice *device, GParamSpec *pspec, NmCli *nmc)
 {
 	NMDeviceState state = nm_device_get_state (device);
-	NMMetaTermColor color;
-	NMMetaTermFormat color_fmt;
+	NMMetaColor color;
 	char *str;
 
-	nmc_device_state_to_color (state, &color, &color_fmt);
-	str = nmc_colorize (nmc->nmc_config.use_colors, color, color_fmt, "%s: %s\n",
+	color = nmc_device_state_to_color (state);
+	str = nmc_colorize (&nmc->nmc_config, color, "%s: %s\n",
 	                    nm_device_get_iface (device),
 	                    nmc_device_state_to_string (state));
 
@@ -2910,7 +2897,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
 
 		if (nmc->ask) {
 			ssid_ask = nmc_readline (_("SSID or BSSID: "));
-			param_user = ssid_ask ? ssid_ask : "";
+			param_user = ssid_ask ?: "";
 			bssid1_arr = nm_utils_hwaddr_atoba (param_user, ETH_ALEN);
 		}
 		if (!ssid_ask) {
@@ -3661,7 +3648,6 @@ do_device_wifi_rescan (NmCli *nmc, int argc, char **argv)
 		goto finish;
 	}
 
-
 	if (ssids->len) {
 		g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
 		g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aay"));
diff --git a/clients/cli/devices.h b/clients/cli/devices.h
index 2c261bc7..c78680da 100644
--- a/clients/cli/devices.h
+++ b/clients/cli/devices.h
@@ -14,7 +14,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * (C) Copyright 2010 Red Hat, Inc.
+ * (C) Copyright 2010 - 2018 Red Hat, Inc.
  */
 
 #ifndef NMC_DEVICES_H
@@ -32,7 +32,7 @@ void monitor_devices (NmCli *nmc);
 
 NMDevice ** nmc_get_devices_sorted (NMClient *client);
 
-void nmc_device_state_to_color (NMDeviceState state, NMMetaTermColor *color, NMMetaTermFormat *color_fmt);
+NMMetaColor nmc_device_state_to_color (NMDeviceState state);
 
 extern const NmcMetaGenericInfo *const nmc_fields_dev_status[];
 extern const NmcMetaGenericInfo *const nmc_fields_dev_show_general[];
diff --git a/clients/cli/general.c b/clients/cli/general.c
index 7ac6b22c..841df8bd 100644
--- a/clients/cli/general.c
+++ b/clients/cli/general.c
@@ -14,7 +14,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2010 - 2017 Red Hat, Inc.
+ * Copyright 2010 - 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -55,23 +55,26 @@ nm_state_to_string (NMState state)
 	return _(nm_state_to_string_no_l10n (state));
 }
 
-static NMMetaTermColor
+static NMMetaColor
 state_to_color (NMState state)
 {
 	switch (state) {
 	case NM_STATE_CONNECTING:
-		return NM_META_TERM_COLOR_YELLOW;
+		return NM_META_COLOR_STATE_CONNECTING;
 	case NM_STATE_CONNECTED_LOCAL:
+		return NM_META_COLOR_STATE_CONNECTED_LOCAL;
 	case NM_STATE_CONNECTED_SITE:
+		return NM_META_COLOR_STATE_CONNECTED_SITE;
 	case NM_STATE_CONNECTED_GLOBAL:
-		return NM_META_TERM_COLOR_GREEN;
+		return NM_META_COLOR_STATE_CONNECTED_GLOBAL;
 	case NM_STATE_DISCONNECTING:
-		return NM_META_TERM_COLOR_YELLOW;
+		return NM_META_COLOR_STATE_DISCONNECTING;
 	case NM_STATE_ASLEEP:
+		return NM_META_COLOR_STATE_ASLEEP;
 	case NM_STATE_DISCONNECTED:
-		return NM_META_TERM_COLOR_RED;
+		return NM_META_COLOR_STATE_DISCONNECTED;
 	default:
-		return NM_META_TERM_COLOR_NORMAL;
+		return NM_META_COLOR_STATE_UNKNOWN;
 	}
 }
 
@@ -90,19 +93,20 @@ nm_connectivity_to_string (NMConnectivityState connectivity)
 	return _(nm_connectivity_to_string_no_l10n (connectivity));
 }
 
-static NMMetaTermColor
+static NMMetaColor
 connectivity_to_color (NMConnectivityState connectivity)
 {
 	switch (connectivity) {
 	case NM_CONNECTIVITY_NONE:
-		return NM_META_TERM_COLOR_RED;
+		return NM_META_COLOR_CONNECTIVITY_NONE;
 	case NM_CONNECTIVITY_PORTAL:
+		return NM_META_COLOR_CONNECTIVITY_PORTAL;
 	case NM_CONNECTIVITY_LIMITED:
-		return NM_META_TERM_COLOR_YELLOW;
+		return NM_META_COLOR_CONNECTIVITY_LIMITED;
 	case NM_CONNECTIVITY_FULL:
-		return NM_META_TERM_COLOR_GREEN;
+		return NM_META_COLOR_CONNECTIVITY_FULL;
 	default:
-		return NM_META_TERM_COLOR_NORMAL;
+		return NM_META_COLOR_CONNECTIVITY_UNKNOWN;
 	}
 }
 
@@ -155,11 +159,11 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (permission_result_to_string_no_l10n, NMClient
 	NM_UTILS_LOOKUP_ITEM_IGNORE (NM_CLIENT_PERMISSION_RESULT_UNKNOWN),
 );
 
-_NM_UTILS_LOOKUP_DEFINE (static, permission_result_to_color, NMClientPermissionResult, NMMetaTermColor,
-	NM_UTILS_LOOKUP_DEFAULT (NM_META_TERM_COLOR_NORMAL),
-	NM_UTILS_LOOKUP_ITEM (NM_CLIENT_PERMISSION_RESULT_YES,  NM_META_TERM_COLOR_GREEN),
-	NM_UTILS_LOOKUP_ITEM (NM_CLIENT_PERMISSION_RESULT_NO,   NM_META_TERM_COLOR_RED),
-	NM_UTILS_LOOKUP_ITEM (NM_CLIENT_PERMISSION_RESULT_AUTH, NM_META_TERM_COLOR_YELLOW),
+_NM_UTILS_LOOKUP_DEFINE (static, permission_result_to_color, NMClientPermissionResult, NMMetaColor,
+	NM_UTILS_LOOKUP_DEFAULT (NM_META_COLOR_PERMISSION_UNKNOWN),
+	NM_UTILS_LOOKUP_ITEM (NM_CLIENT_PERMISSION_RESULT_YES,  NM_META_COLOR_PERMISSION_YES),
+	NM_UTILS_LOOKUP_ITEM (NM_CLIENT_PERMISSION_RESULT_NO,   NM_META_COLOR_PERMISSION_NO),
+	NM_UTILS_LOOKUP_ITEM (NM_CLIENT_PERMISSION_RESULT_AUTH, NM_META_COLOR_PERMISSION_AUTH),
 	NM_UTILS_LOOKUP_ITEM_IGNORE (NM_CLIENT_PERMISSION_RESULT_UNKNOWN),
 );
 
@@ -168,14 +172,7 @@ _NM_UTILS_LOOKUP_DEFINE (static, permission_result_to_color, NMClientPermissionR
 static const NmcMetaGenericInfo *const metagen_general_status[];
 
 static gconstpointer
-_metagen_general_status_get_fcn (const NMMetaEnvironment *environment,
-                                 gpointer environment_user_data,
-                                 const NmcMetaGenericInfo *info,
-                                 gpointer target,
-                                 NMMetaAccessorGetType get_type,
-                                 NMMetaAccessorGetFlags get_flags,
-                                 NMMetaAccessorGetOutFlags *out_flags,
-                                 gpointer *out_to_free)
+_metagen_general_status_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS)
 {
 	NmCli *nmc = target;
 	const char *value;
@@ -185,26 +182,26 @@ _metagen_general_status_get_fcn (const NMMetaEnvironment *environment,
 
 	switch (info->info_type) {
 	case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_RUNNING:
-		NMC_HANDLE_TERMFORMAT (NM_META_TERM_COLOR_NORMAL);
+		NMC_HANDLE_COLOR (NM_META_COLOR_NONE);
 		value = N_("running");
 		goto translate_and_out;
 	case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_VERSION:
-		NMC_HANDLE_TERMFORMAT (NM_META_TERM_COLOR_NORMAL);
+		NMC_HANDLE_COLOR (NM_META_COLOR_NONE);
 		value = nm_client_get_version (nmc->client);
 		goto clone_and_out;
 	case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_STATE:
 		state = nm_client_get_state (nmc->client);
-		NMC_HANDLE_TERMFORMAT (state_to_color (state));
+		NMC_HANDLE_COLOR (state_to_color (state));
 		value = nm_state_to_string_no_l10n (state);
 		goto translate_and_out;
 	case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_STARTUP:
 		v_bool = nm_client_get_startup (nmc->client);
-		NMC_HANDLE_TERMFORMAT (v_bool ? NM_META_TERM_COLOR_YELLOW : NM_META_TERM_COLOR_GREEN);
+		NMC_HANDLE_COLOR (v_bool ? NM_META_COLOR_MANAGER_STARTING : NM_META_COLOR_MANAGER_RUNNING);
 		value = v_bool ? N_("starting") : N_("started");
 		goto translate_and_out;
 	case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_CONNECTIVITY:
 		connectivity = nm_client_get_connectivity (nmc->client);
-		NMC_HANDLE_TERMFORMAT (connectivity_to_color (connectivity));
+		NMC_HANDLE_COLOR (connectivity_to_color (connectivity));
 		value = nm_connectivity_to_string_no_l10n (connectivity);
 		goto translate_and_out;
 	case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_NETWORKING:
@@ -233,7 +230,7 @@ _metagen_general_status_get_fcn (const NMMetaEnvironment *environment,
 	g_return_val_if_reached (NULL);
 
 enabled_out:
-	NMC_HANDLE_TERMFORMAT (v_bool ? NM_META_TERM_COLOR_GREEN : NM_META_TERM_COLOR_RED);
+	NMC_HANDLE_COLOR (v_bool ? NM_META_COLOR_ENABLED : NM_META_COLOR_DISABLED);
 	value = v_bool ? N_("enabled") : N_("disabled");
 	goto translate_and_out;
 
@@ -275,14 +272,7 @@ static const NmcMetaGenericInfo *const metagen_general_status[_NMC_GENERIC_INFO_
 /*****************************************************************************/
 
 static gconstpointer
-_metagen_general_permissions_get_fcn (const NMMetaEnvironment *environment,
-                                      gpointer environment_user_data,
-                                      const NmcMetaGenericInfo *info,
-                                      gpointer target,
-                                      NMMetaAccessorGetType get_type,
-                                      NMMetaAccessorGetFlags get_flags,
-                                      NMMetaAccessorGetOutFlags *out_flags,
-                                      gpointer *out_to_free)
+_metagen_general_permissions_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS)
 {
 	NMClientPermission perm = GPOINTER_TO_UINT (target);
 	NmCli *nmc = environment_user_data;
@@ -291,11 +281,11 @@ _metagen_general_permissions_get_fcn (const NMMetaEnvironment *environment,
 
 	switch (info->info_type) {
 	case NMC_GENERIC_INFO_TYPE_GENERAL_PERMISSIONS_PERMISSION:
-		NMC_HANDLE_TERMFORMAT (NM_META_TERM_COLOR_NORMAL);
+		NMC_HANDLE_COLOR (NM_META_COLOR_NONE);
 		return permission_to_string (perm);
 	case NMC_GENERIC_INFO_TYPE_GENERAL_PERMISSIONS_VALUE:
 		perm_result = nm_client_get_permission_result (nmc->client, perm);
-		NMC_HANDLE_TERMFORMAT (permission_result_to_color (perm_result));
+		NMC_HANDLE_COLOR (permission_result_to_color (perm_result));
 		s = permission_result_to_string_no_l10n (perm_result);
 		if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY)
 			return _(s);
@@ -323,21 +313,14 @@ typedef struct {
 } GetGeneralLoggingData;
 
 static gconstpointer
-_metagen_general_logging_get_fcn (const NMMetaEnvironment *environment,
-                                  gpointer environment_user_data,
-                                  const NmcMetaGenericInfo *info,
-                                  gpointer target,
-                                  NMMetaAccessorGetType get_type,
-                                  NMMetaAccessorGetFlags get_flags,
-                                  NMMetaAccessorGetOutFlags *out_flags,
-                                  gpointer *out_to_free)
+_metagen_general_logging_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS)
 {
 	NmCli *nmc = environment_user_data;
 	GetGeneralLoggingData *d = target;
 
 	nm_assert (info->info_type < _NMC_GENERIC_INFO_TYPE_GENERAL_LOGGING_NUM);
 
-	NMC_HANDLE_TERMFORMAT (NM_META_TERM_COLOR_NORMAL);
+	NMC_HANDLE_COLOR (NM_META_COLOR_NONE);
 
 	if (!d->initialized) {
 		d->initialized = TRUE;
@@ -513,8 +496,8 @@ show_nm_status (NmCli *nmc, const char *pretty_header_name, const char *print_fl
 {
 	gs_free_error GError *error = NULL;
 	const char *fields_str;
-	const char *fields_all =    print_flds ? print_flds : NMC_FIELDS_NM_STATUS_ALL;
-	const char *fields_common = print_flds ? print_flds : NMC_FIELDS_NM_STATUS_COMMON;
+	const char *fields_all =    print_flds ?: NMC_FIELDS_NM_STATUS_ALL;
+	const char *fields_common = print_flds ?: NMC_FIELDS_NM_STATUS_COMMON;
 
 	if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
 		fields_str = fields_common;
@@ -1083,9 +1066,8 @@ networkmanager_running (NMClient *client, GParamSpec *param, NmCli *nmc)
 	char *str;
 
 	running = nm_client_get_nm_running (client);
-	str = nmc_colorize (nmc->nmc_config.use_colors,
-	                    running ? NM_META_TERM_COLOR_GREEN : NM_META_TERM_COLOR_RED,
-	                    NM_META_TERM_FORMAT_NORMAL,
+	str = nmc_colorize (&nmc->nmc_config,
+	                    running ? NM_META_COLOR_MANAGER_RUNNING : NM_META_COLOR_MANAGER_STOPPED,
 	                    running ? _("NetworkManager has started") : _("NetworkManager has stopped"));
 	g_print ("%s\n", str);
 	g_free (str);
@@ -1125,7 +1107,7 @@ client_connectivity (NMClient *client, GParamSpec *param, NmCli *nmc)
 	char *str;
 
 	g_object_get (client, NM_CLIENT_CONNECTIVITY, &connectivity, NULL);
-	str = nmc_colorize (nmc->nmc_config.use_colors, connectivity_to_color (connectivity), NM_META_TERM_FORMAT_NORMAL,
+	str = nmc_colorize (&nmc->nmc_config, connectivity_to_color (connectivity),
 	                    _("Connectivity is now '%s'\n"), nm_connectivity_to_string (connectivity));
 	g_print ("%s", str);
 	g_free (str);
@@ -1138,14 +1120,13 @@ client_state (NMClient *client, GParamSpec *param, NmCli *nmc)
 	char *str;
 
 	g_object_get (client, NM_CLIENT_STATE, &state, NULL);
-	str = nmc_colorize (nmc->nmc_config.use_colors, state_to_color (state), NM_META_TERM_FORMAT_NORMAL,
+	str = nmc_colorize (&nmc->nmc_config, state_to_color (state),
 	                    _("Networkmanager is now in the '%s' state\n"),
 	                    nm_state_to_string (state));
 	g_print ("%s", str);
 	g_free (str);
 }
 
-
 static void
 device_overview (NmCli *nmc, NMDevice *device)
 {
@@ -1182,12 +1163,12 @@ device_overview (NmCli *nmc, NMDevice *device)
 	if (!nm_device_get_autoconnect (device))
 		g_string_append_printf (outbuf, "%s, ", _("autoconnect"));
 	if (nm_device_get_firmware_missing (device)) {
-		tmp = nmc_colorize (nmc->nmc_config.use_colors, NM_META_TERM_COLOR_RED, NM_META_TERM_FORMAT_NORMAL, _("fw missing"));
+		tmp = nmc_colorize (&nmc->nmc_config, NM_META_COLOR_DEVICE_FIRMWARE_MISSING, _("fw missing"));
 		g_string_append_printf (outbuf, "%s, ", tmp);
 		g_free (tmp);
 	}
 	if (nm_device_get_nm_plugin_missing (device)) {
-		tmp = nmc_colorize (nmc->nmc_config.use_colors, NM_META_TERM_COLOR_RED, NM_META_TERM_FORMAT_NORMAL, _("plugin missing"));
+		tmp = nmc_colorize (&nmc->nmc_config, NM_META_COLOR_DEVICE_PLUGIN_MISSING, _("plugin missing"));
 		g_string_append_printf (outbuf, "%s, ", tmp);
 		g_free (tmp);
 	}
@@ -1288,7 +1269,7 @@ do_overview (NmCli *nmc, int argc, char **argv)
 	NMDevice **devices;
 	const GPtrArray *p;
 	NMActiveConnection *ac;
-	NMMetaTermColor color;
+	NMMetaColor color;
 	NMDnsEntry *dns;
 	char *tmp;
 	int i;
@@ -1312,8 +1293,8 @@ do_overview (NmCli *nmc, int argc, char **argv)
 			continue;
 
 		state = nm_active_connection_get_state (ac);
-		nmc_active_connection_state_to_color (state, &color);
-		tmp = nmc_colorize (nmc->nmc_config.use_colors, color, NM_META_TERM_FORMAT_NORMAL, _("%s VPN connection"),
+		color = nmc_active_connection_state_to_color (state);
+		tmp = nmc_colorize (&nmc->nmc_config, color, _("%s VPN connection"),
 		                    nm_active_connection_get_id (ac));
 		g_print ("%s\n", tmp);
 		g_free (tmp);
@@ -1324,14 +1305,13 @@ do_overview (NmCli *nmc, int argc, char **argv)
 
 	devices = nmc_get_devices_sorted (nmc->client);
 	for (i = 0; devices[i]; i++) {
-		NMMetaTermFormat color_fmt;
 		NMDeviceState state;
 
 		ac = nm_device_get_active_connection (devices[i]);
 
 		state = nm_device_get_state (devices[i]);
-		nmc_device_state_to_color (state, &color, &color_fmt);
-		tmp = nmc_colorize (nmc->nmc_config.use_colors, color, color_fmt, "%s: %s%s%s",
+		color = nmc_device_state_to_color (state);
+		tmp = nmc_colorize (&nmc->nmc_config, color, "%s: %s%s%s",
 		                    nm_device_get_iface (devices[i]),
 		                    nmc_device_state_to_string (state),
 		                    ac ? " to " : "",
@@ -1342,7 +1322,6 @@ do_overview (NmCli *nmc, int argc, char **argv)
 		if (nm_device_get_description (devices[i]) && strcmp (nm_device_get_description (devices[i]), ""))
 			g_print ("\t\"%s\"\n", nm_device_get_description (devices[i]));
 
-
 		device_overview (nmc, devices[i]);
 		if (ac)
 			ac_overview (nmc, ac);
@@ -1415,7 +1394,7 @@ do_monitor (NmCli *nmc, int argc, char **argv)
 	if (!nm_client_get_nm_running (nmc->client)) {
 		char *str;
 
-		str = nmc_colorize (nmc->nmc_config.use_colors, NM_META_TERM_COLOR_RED, NM_META_TERM_FORMAT_NORMAL,
+		str = nmc_colorize (&nmc->nmc_config, NM_META_COLOR_MANAGER_STOPPED,
 		                    _("Networkmanager is not running (waiting for it)\n"));
 		g_print ("%s", str);
 		g_free (str);
diff --git a/clients/cli/meson.build b/clients/cli/meson.build
new file mode 100644
index 00000000..9449ce63
--- /dev/null
+++ b/clients/cli/meson.build
@@ -0,0 +1,49 @@
+name = 'nmcli'
+
+# FIXME: nmcli-completion should be renamed to nmcli
+install_data(
+  'nmcli-completion',
+  install_dir: join_paths(nm_datadir, 'bash-completion', 'completions')
+)
+
+sources = files(
+  'agent.c',
+  'common.c',
+  'connections.c',
+  'devices.c',
+  'general.c',
+  'nmcli.c',
+  'polkit-agent.c',
+  'settings.c',
+  'utils.c'
+)
+
+deps = [
+  libnm_dep,
+  libnmc_base_dep,
+  libnmc_dep,
+  nm_core_dep,
+  readline_dep
+]
+
+cflags = clients_cflags + [
+  '-DSYSCONFDIR="@0@"'.format(nm_sysconfdir),
+  '-DG_LOG_DOMAIN="@0@"'.format(name),
+  '-DNMLOCALEDIR="@0@"'.format(nm_localedir)
+]
+
+if enable_polkit_agent
+  sources += nm_polkit_listener
+
+  deps += polkit_agent_dep
+endif
+
+executable(
+  name,
+  sources,
+  dependencies: deps,
+  c_args: cflags,
+  link_args: ldflags_linker_script_binary,
+  link_depends: linker_script_binary,
+  install: true
+)
diff --git a/clients/cli/nmcli.c b/clients/cli/nmcli.c
index f5a66cdd..e9752952 100644
--- a/clients/cli/nmcli.c
+++ b/clients/cli/nmcli.c
@@ -16,7 +16,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2010 - 2017 Red Hat, Inc.
+ * Copyright 2010 - 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -36,8 +36,6 @@
 
 #include "nm-client-utils.h"
 
-#include "nm-utils/nm-hash-utils.h"
-
 #include "polkit-agent.h"
 #include "utils.h"
 #include "common.h"
@@ -53,8 +51,67 @@
 # define NMCLI_VERSION VERSION
 #endif
 
-/* Global NmCli object */
-NmCli nm_cli;
+NmCli nm_cli = {
+	.client = NULL,
+
+	.return_value = NMC_RESULT_SUCCESS,
+
+	.timeout = -1,
+
+	.secret_agent = NULL,
+	.pwds_hash = NULL,
+	.pk_listener = NULL,
+
+	.should_wait = 0,
+	.nowait_flag = TRUE,
+	.nmc_config.print_output = NMC_PRINT_NORMAL,
+	.nmc_config.multiline_output = FALSE,
+	.mode_specified = FALSE,
+	.nmc_config.escape_values = TRUE,
+	.required_fields = NULL,
+	.ask = FALSE,
+	.complete = FALSE,
+	.nmc_config.show_secrets = FALSE,
+	.nmc_config.in_editor = FALSE,
+	.nmc_config.palette = {
+		[NM_META_COLOR_CONNECTION_ACTIVATED]	 = "32",
+		[NM_META_COLOR_CONNECTION_ACTIVATING]	 = "33",
+		[NM_META_COLOR_CONNECTION_DISCONNECTING] = "31",
+		[NM_META_COLOR_CONNECTION_INVISIBLE]	 = "2",
+		[NM_META_COLOR_CONNECTIVITY_FULL]	 = "32",
+		[NM_META_COLOR_CONNECTIVITY_LIMITED]	 = "33",
+		[NM_META_COLOR_CONNECTIVITY_NONE]	 = "31",
+		[NM_META_COLOR_CONNECTIVITY_PORTAL]	 = "33",
+		[NM_META_COLOR_DEVICE_ACTIVATED]	 = "32",
+		[NM_META_COLOR_DEVICE_ACTIVATING]	 = "33",
+		[NM_META_COLOR_DEVICE_DISCONNECTED]	 = "31",
+		[NM_META_COLOR_DEVICE_FIRMWARE_MISSING]	 = "31",
+		[NM_META_COLOR_DEVICE_PLUGIN_MISSING]	 = "31",
+		[NM_META_COLOR_DEVICE_UNAVAILABLE]	 = "2",
+		[NM_META_COLOR_MANAGER_RUNNING]		 = "32",
+		[NM_META_COLOR_MANAGER_STARTING]	 = "33",
+		[NM_META_COLOR_MANAGER_STOPPED]		 = "31",
+		[NM_META_COLOR_PERMISSION_AUTH]		 = "33",
+		[NM_META_COLOR_PERMISSION_NO]		 = "31",
+		[NM_META_COLOR_PERMISSION_YES]		 = "32",
+		[NM_META_COLOR_STATE_ASLEEP]		 = "31",
+		[NM_META_COLOR_STATE_CONNECTED_GLOBAL]	 = "32",
+		[NM_META_COLOR_STATE_CONNECTED_LOCAL]	 = "32",
+		[NM_META_COLOR_STATE_CONNECTED_SITE]	 = "32",
+		[NM_META_COLOR_STATE_CONNECTING]	 = "33",
+		[NM_META_COLOR_STATE_DISCONNECTED]	 = "31",
+		[NM_META_COLOR_STATE_DISCONNECTING]	 = "33",
+		[NM_META_COLOR_WIFI_SIGNAL_EXCELLENT]	 = "32",
+		[NM_META_COLOR_WIFI_SIGNAL_FAIR]	 = "35",
+		[NM_META_COLOR_WIFI_SIGNAL_GOOD]	 = "33",
+		[NM_META_COLOR_WIFI_SIGNAL_POOR]	 = "36",
+		[NM_META_COLOR_WIFI_SIGNAL_UNKNOWN]	 = "2",
+		[NM_META_COLOR_ENABLED]			 = "32",
+		[NM_META_COLOR_DISABLED]		 = "31",
+	},
+	.editor_status_line = FALSE,
+	.editor_save_confirmation = TRUE,
+};
 
 /*****************************************************************************/
 
@@ -129,9 +186,8 @@ complete_fields (const char *option, const char *prefix)
 	h = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
 
 	complete_field (h, metagen_ip4_config);
-	complete_field (h, nmc_fields_dhcp4_config);
+	complete_field (h, nmc_fields_dhcp_config);
 	complete_field (h, nmc_fields_ip6_config);
-	complete_field (h, nmc_fields_dhcp6_config);
 	complete_field (h, nmc_fields_con_show);
 	complete_field (h, nmc_fields_con_active_details_general);
 	complete_field (h, nmc_fields_con_active_details_vpn);
@@ -186,6 +242,7 @@ usage (void)
 	g_printerr (_("Usage: nmcli [OPTIONS] OBJECT { COMMAND | help }\n"
 	              "\n"
 	              "OPTIONS\n"
+	              "  -o[verview]                                    overview mode (hide default values)\n"
 	              "  -t[erse]                                       terse output\n"
 	              "  -p[retty]                                      pretty output\n"
 	              "  -m[ode] tabular|multiline                      output mode\n"
@@ -272,9 +329,299 @@ matches_arg (NmCli *nmc, int *argc, char ***argv, const char *pattern, char **ar
 	return TRUE;
 }
 
+/*************************************************************************************/
+
+typedef enum {
+        NMC_USE_COLOR_AUTO,
+        NMC_USE_COLOR_YES,
+        NMC_USE_COLOR_NO,
+} NmcColorOption;
+
+/* Checks whether a particular terminal-colors.d(5) file (.enabled, .disabled or .schem)
+ * exists. If contents is non-NULL, it returns the content. */
+static gboolean
+check_colors_file (NmCli *nmc, NmcColorOption *color_option,
+                   const char *base_dir, const char *name, const char *term, const char *type,
+                   char **contents)
+{
+	char *filename;
+	gboolean exists;
+
+	filename = g_strdup_printf ("%s/terminal-colors.d/%s%s%s%s%s",
+	                            base_dir,
+	                            name ? name : "",
+	                            term ? "@" : "", term ? term : "",
+	                            (name || term) ? "." : "",
+	                            type);
+	if (contents)
+		exists = g_file_get_contents (filename, contents, NULL, NULL);
+	else
+		exists = g_file_test (filename, G_FILE_TEST_EXISTS);
+	g_free (filename);
+
+	return exists;
+}
+
+static void
+check_colors_files_for_term (NmCli *nmc, NmcColorOption *color_option,
+                             const char *base_dir, const char *name, const char *term)
+{
+	if (   *color_option == NMC_USE_COLOR_AUTO
+	    && check_colors_file (nmc, color_option, base_dir, name, term, "enable", NULL)) {
+		*color_option = NMC_USE_COLOR_YES;
+	}
+
+	if (   *color_option == NMC_USE_COLOR_AUTO
+	    && check_colors_file (nmc, color_option, base_dir, name, term, "disable", NULL)) {
+		*color_option = NMC_USE_COLOR_NO;
+	}
+
+	if (*color_option == NMC_USE_COLOR_NO) {
+		/* No need to bother any further. */
+		return;
+	}
+
+	if (nmc->palette_buffer == NULL)
+		check_colors_file (nmc, color_option, base_dir, name, term, "schem", &nmc->palette_buffer);
+}
+
+static void
+check_colors_files_for_name (NmCli *nmc, NmcColorOption *color_option,
+                             const char *base_dir, const char *name)
+{
+	const gchar *term;
+
+	/* Take a shortcut if the directory is not there. */
+	if (!g_file_test (base_dir, G_FILE_TEST_EXISTS))
+		return;
+
+	term = g_getenv ("TERM");
+	if (term)
+		check_colors_files_for_term (nmc, color_option, base_dir, name, term);
+	check_colors_files_for_term (nmc, color_option, base_dir, name, NULL);
+}
+
+static void
+check_colors_files_for_base_dir (NmCli *nmc, NmcColorOption *color_option,
+                                 const char *base_dir)
+{
+	check_colors_files_for_name (nmc, color_option, base_dir, "nmcli");
+	check_colors_files_for_name (nmc, color_option, base_dir, NULL);
+}
+
+static const char *
+resolve_color_alias (const char *color)
+{
+	static const struct {
+		const char *name;
+		const char *alias;
+	} aliases[] = {
+		{ "reset",        "0" },
+		{ "bold",         "1" },
+		{ "white",        "1;37" },
+		{ "halfbright",   "2" },
+		{ "underscore",   "4" },
+		{ "blink",        "5" },
+		{ "reverse",      "7" },
+		{ "black",        "30" },
+		{ "red",          "31" },
+		{ "green",        "32" },
+		{ "brown",        "33" },
+		{ "yellow",       "33" }, /* well, yellow */
+		{ "blue",         "34" },
+		{ "magenta",      "35" },
+		{ "cyan",         "36" },
+		{ "gray",         "37" },
+		{ "darkgray",     "90" },
+		{ "lightred",     "91" },
+		{ "lightgreen",   "92" },
+		{ "lightblue",    "94" },
+		{ "lightmagenta", "95" },
+		{ "lightcyan",    "96" },
+		{ "lightgray",    "97" },
+	};
+	int i;
+
+	/* Shortcut literal sequences. */
+	if (g_ascii_isdigit (*color))
+		return color;
+
+	for (i = 0; i < G_N_ELEMENTS (aliases); i++) {
+		if (strcmp (color, aliases[i].name) == 0)
+			return aliases[i].alias;
+	}
+
+	return color;
+}
+
+static gboolean
+parse_color_scheme (NmCli *nmc, GError **error)
+{
+	char *p = nmc->palette_buffer;
+	const char *name;
+	const char *color;
+	const char *map[_NM_META_COLOR_NUM] = {
+		[NM_META_COLOR_NONE]                     = NULL,
+		[NM_META_COLOR_CONNECTION_ACTIVATED]     = "connection-activated",
+		[NM_META_COLOR_CONNECTION_ACTIVATING]    = "connection-activating",
+		[NM_META_COLOR_CONNECTION_DISCONNECTING] = "connection-disconnecting",
+		[NM_META_COLOR_CONNECTION_INVISIBLE]     = "connection-invisible",
+		[NM_META_COLOR_CONNECTION_UNKNOWN]       = "connection-unknown",
+		[NM_META_COLOR_CONNECTIVITY_FULL]        = "connectivity-full",
+		[NM_META_COLOR_CONNECTIVITY_LIMITED]     = "connectivity-limited",
+		[NM_META_COLOR_CONNECTIVITY_NONE]        = "connectivity-none",
+		[NM_META_COLOR_CONNECTIVITY_PORTAL]      = "connectivity-portal",
+		[NM_META_COLOR_CONNECTIVITY_UNKNOWN]     = "connectivity-unknown",
+		[NM_META_COLOR_DEVICE_ACTIVATED]         = "device-activated",
+		[NM_META_COLOR_DEVICE_ACTIVATING]        = "device-activating",
+		[NM_META_COLOR_DEVICE_DISCONNECTED]      = "device-disconnected",
+		[NM_META_COLOR_DEVICE_FIRMWARE_MISSING]  = "device-firmware-missing",
+		[NM_META_COLOR_DEVICE_PLUGIN_MISSING]    = "device-plugin-missing",
+		[NM_META_COLOR_DEVICE_UNAVAILABLE]       = "device-unavailable",
+		[NM_META_COLOR_DEVICE_UNKNOWN]           = "device-unknown",
+		[NM_META_COLOR_MANAGER_RUNNING]          = "manager-running",
+		[NM_META_COLOR_MANAGER_STARTING]         = "manager-starting",
+		[NM_META_COLOR_MANAGER_STOPPED]          = "manager-stopped",
+		[NM_META_COLOR_PERMISSION_AUTH]          = "permission-auth",
+		[NM_META_COLOR_PERMISSION_NO]            = "permission-no",
+		[NM_META_COLOR_PERMISSION_UNKNOWN]       = "permission-unknown",
+		[NM_META_COLOR_PERMISSION_YES]           = "permission-yes",
+		[NM_META_COLOR_PROMPT]                   = "prompt",
+		[NM_META_COLOR_STATE_ASLEEP]             = "state-asleep",
+		[NM_META_COLOR_STATE_CONNECTED_GLOBAL]   = "state-connected-global",
+		[NM_META_COLOR_STATE_CONNECTED_LOCAL]    = "state-connected-local",
+		[NM_META_COLOR_STATE_CONNECTED_SITE]     = "state-connected-site",
+		[NM_META_COLOR_STATE_CONNECTING]         = "state-connecting",
+		[NM_META_COLOR_STATE_DISCONNECTED]       = "state-disconnected",
+		[NM_META_COLOR_STATE_DISCONNECTING]      = "state-disconnecting",
+		[NM_META_COLOR_STATE_UNKNOWN]            = "state-unknown",
+		[NM_META_COLOR_WIFI_SIGNAL_EXCELLENT]    = "wifi-signal-excellent",
+		[NM_META_COLOR_WIFI_SIGNAL_FAIR]         = "wifi-signal-fair",
+		[NM_META_COLOR_WIFI_SIGNAL_GOOD]         = "wifi-signal-good",
+		[NM_META_COLOR_WIFI_SIGNAL_POOR]         = "wifi-signal-poor",
+		[NM_META_COLOR_WIFI_SIGNAL_UNKNOWN]      = "wifi-signal-unknown",
+		[NM_META_COLOR_DISABLED]                 = "disabled",
+		[NM_META_COLOR_ENABLED]                  = "enabled",
+	};
+	int i;
+
+	/* This reads through the raw color scheme file contents, identifying the
+	 * color names and sequences, putting in terminating NULs in place, so that
+	 * pointers into the buffer can readily be used as strings in the palette. */
+	while (1) {
+		/* Leading whitespace. */
+		while (nm_utils_is_separator (*p) || *p == '\n')
+			p++;
+
+		if (*p == '\0')
+			break;
+
+		/* Comments. */
+		if (*p == '#') {
+			while (*p != '\n' && *p != '\0')
+				p++;
+			continue;
+		}
+
+		/* Color name. */
+		name = p;
+		while (g_ascii_isgraph (*p))
+			p++;
+		if (*p == '\0') {
+			g_set_error (error, NMCLI_ERROR, 0,
+			             _("Unexpected end of file following '%s'\n"), name);
+			return FALSE;
+		}
+
+		/* Separating whitespace. */
+		if (!nm_utils_is_separator (*p)) {
+			*p = '\0';
+			g_set_error (error, NMCLI_ERROR, 0,
+			             _("Expected whitespace following '%s'\n"), name);
+			return FALSE;
+		}
+		while (nm_utils_is_separator (*p)) {
+			*p = '\0';
+			p++;
+		}
+
+		/* Color sequence. */
+		color = p;
+		if (!g_ascii_isgraph (*p)) {
+			g_set_error (error, NMCLI_ERROR, 0,
+			             _("Expected a value for '%s'\n"), name);
+			return FALSE;
+		}
+		while (g_ascii_isgraph (*p))
+			p++;
+
+		/* Trailing whitespace. */
+		while (nm_utils_is_separator (*p)) {
+			*p = '\0';
+			p++;
+		}
+		if (*p != '\0') {
+			if (*p != '\n') {
+				g_set_error (error, NMCLI_ERROR, 0,
+				             _("Expected a line break following '%s'\n"), color);
+				return FALSE;
+			}
+			*p = '\0';
+			p++;
+		}
+
+		/* All good, set the palette entry. */
+		for (i = NM_META_COLOR_NONE + 1; i < _NM_META_COLOR_NUM; i++) {
+			if (strcmp (map[i], name) == 0) {
+				nmc->nmc_config_mutable.palette[i] = resolve_color_alias (color);
+				break;
+			}
+		}
+		if (i == _NM_META_COLOR_NUM)
+			g_debug ("Ignoring an unrecognized color: '%s'\n", name);
+	}
+
+	return TRUE;
+}
+
+static void
+set_colors (NmCli *nmc, NmcColorOption color_option)
+{
+	GError *error = NULL;
+
+	if (color_option == NMC_USE_COLOR_AUTO) {
+		if (   g_strcmp0 (g_getenv ("TERM"), "dumb") == 0
+		    || !isatty (STDOUT_FILENO))
+			color_option = NMC_USE_COLOR_NO;
+	}
+
+	check_colors_files_for_base_dir (nmc, &color_option, g_get_user_config_dir ());
+	check_colors_files_for_base_dir (nmc, &color_option, SYSCONFDIR);
+
+	switch (color_option) {
+	case NMC_USE_COLOR_YES:
+	case NMC_USE_COLOR_AUTO:
+		nmc->nmc_config_mutable.use_colors = TRUE;
+		break;
+	case NMC_USE_COLOR_NO:
+		nmc->nmc_config_mutable.use_colors = FALSE;
+		break;
+	}
+
+	if (nmc->nmc_config_mutable.use_colors && nmc->palette_buffer) {
+		if (!parse_color_scheme (nmc, &error)) {
+			g_debug ("Error parsing color scheme: %s", error->message);
+			g_error_free (error);
+		}
+	}
+}
+
+/*************************************************************************************/
+
 static gboolean
 process_command_line (NmCli *nmc, int argc, char **argv)
 {
+	NmcColorOption colors = NMC_USE_COLOR_AUTO;
 	char *base;
 
 	base = strrchr (argv[0], '/');
@@ -297,9 +644,10 @@ process_command_line (NmCli *nmc, int argc, char **argv)
 			break;
 
 		if (argc == 1 && nmc->complete) {
-			nmc_complete_strings (argv[0], "--terse", "--pretty", "--mode", "--colors", "--escape",
-			                           "--fields", "--nocheck", "--get-values",
-			                            "--wait", "--version", "--help", NULL);
+			nmc_complete_strings (argv[0], "--terse", "--pretty", "--mode", "--overview",
+			                               "--colors", "--escape",
+			                               "--fields", "--nocheck", "--get-values",
+			                               "--wait", "--version", "--help", NULL);
 		}
 
 		if (argv[0][1] == '-' && argv[0][2] == '\0') {
@@ -308,7 +656,9 @@ process_command_line (NmCli *nmc, int argc, char **argv)
 			break;
 		}
 
-		if (matches_arg (nmc, &argc, &argv, "-terse", NULL)) {
+		if (matches_arg (nmc, &argc, &argv, "-overview", NULL)) {
+			nmc->nmc_config_mutable.overview = TRUE;
+		} else if (matches_arg (nmc, &argc, &argv, "-terse", NULL)) {
 			if (nmc->nmc_config.print_output == NMC_PRINT_TERSE) {
 				g_string_printf (nmc->return_text, _("Error: Option '--terse' is specified the second time."));
 				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
@@ -351,11 +701,11 @@ process_command_line (NmCli *nmc, int argc, char **argv)
 			if (argc == 1 && nmc->complete)
 				complete_option_with_value (argv[0], value, "yes", "no", "auto", NULL);
 			if (matches (value, "auto"))
-				nmc->nmc_config_mutable.use_colors = NMC_USE_COLOR_AUTO;
+				colors = NMC_USE_COLOR_AUTO;
 			else if (matches (value, "yes"))
-				nmc->nmc_config_mutable.use_colors = NMC_USE_COLOR_YES;
+				colors = NMC_USE_COLOR_YES;
 			else if (matches (value, "no"))
-				nmc->nmc_config_mutable.use_colors = NMC_USE_COLOR_NO;
+				colors = NMC_USE_COLOR_NO;
 			else {
 				g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), value, argv[0]);
 				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
@@ -382,8 +732,8 @@ process_command_line (NmCli *nmc, int argc, char **argv)
 				complete_fields (argv[0], value);
 			nmc->required_fields = g_strdup (value);
 			nmc->nmc_config_mutable.print_output = NMC_PRINT_TERSE;
-			/* We want fixed tabular mode here, but just set the mode specified and rely on the initialization
-			 * in nmc_init: in this way we allow use of "-m multiline" to swap the output mode also if placed
+			/* We want fixed tabular mode here, but just set the mode specified and rely on defaults:
+			 * in this way we allow use of "-m multiline" to swap the output mode also if placed
 			 * before the "-g <field>" option (-g may be still more practical and easy to remember than -t -f).
 			*/
 			nmc->mode_specified = TRUE;
@@ -417,6 +767,12 @@ process_command_line (NmCli *nmc, int argc, char **argv)
 		next_arg (nmc, &argc, &argv, NULL);
 	}
 
+	/* Ignore --overview when fields are set explicitly */
+	if (nmc->required_fields)
+		nmc->nmc_config_mutable.overview = FALSE;
+
+	set_colors (nmc, colors);
+
 	/* Now run the requested command */
 	nmc_do_cmd (nmc, nmcli_cmds, *argv, argc, argv);
 
@@ -553,38 +909,6 @@ nmc_value_transforms_register (void)
 	                                 nmc_convert_bytes_to_string);
 }
 
-/* Initialize NmCli structure - set default values */
-static void
-nmc_init (NmCli *nmc)
-{
-	nmc->client = NULL;
-
-	nmc->return_value = NMC_RESULT_SUCCESS;
-	nmc->return_text = g_string_new (_("Success"));
-
-	nmc->timeout = -1;
-
-	nmc->secret_agent = NULL;
-	nmc->pwds_hash = NULL;
-	nmc->pk_listener = NULL;
-
-	nmc->should_wait = 0;
-	nmc->nowait_flag = TRUE;
-	nmc->nmc_config_mutable.print_output = NMC_PRINT_NORMAL;
-	nmc->nmc_config_mutable.multiline_output = FALSE;
-	nmc->mode_specified = FALSE;
-	nmc->nmc_config_mutable.escape_values = TRUE;
-	nmc->required_fields = NULL;
-	nmc->ask = FALSE;
-	nmc->complete = FALSE;
-	nmc->nmc_config_mutable.show_secrets = FALSE;
-	nmc->nmc_config_mutable.use_colors = NMC_USE_COLOR_AUTO;
-	nmc->nmc_config_mutable.in_editor = FALSE;
-	nmc->editor_status_line = FALSE;
-	nmc->editor_save_confirmation = TRUE;
-	nmc->editor_prompt_color = NM_META_TERM_COLOR_NORMAL;
-}
-
 static void
 nmc_cleanup (NmCli *nmc)
 {
@@ -602,7 +926,7 @@ nmc_cleanup (NmCli *nmc)
 	if (nmc->pwds_hash)
 		g_hash_table_destroy (nmc->pwds_hash);
 
-	g_free (nmc->required_fields);
+	nm_clear_g_free (&nmc->required_fields);
 
 	if (nmc->pager_pid > 0) {
 		fclose (stdout);
@@ -613,6 +937,8 @@ nmc_cleanup (NmCli *nmc)
 		nmc->pager_pid = 0;
 	}
 
+	nm_clear_g_free (&nmc->palette_buffer);
+
 	nmc_polkit_agent_fini (nmc);
 }
 
@@ -624,19 +950,17 @@ main (int argc, char *argv[])
 
 #ifdef GETTEXT_PACKAGE
 	/* Set i18n stuff */
-	bindtextdomain (GETTEXT_PACKAGE, NMCLI_LOCALEDIR);
+	bindtextdomain (GETTEXT_PACKAGE, NMLOCALEDIR);
 	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 	textdomain (GETTEXT_PACKAGE);
 #endif
 
-	nm_g_type_init ();
-
 	/* Save terminal settings */
 	tcgetattr (STDIN_FILENO, &termios_orig);
 
 	nmc_value_transforms_register ();
 
-	nmc_init (&nm_cli);
+	nm_cli.return_text = g_string_new (_("Success"));
 	loop = g_main_loop_new (NULL, FALSE);
 
 	g_unix_signal_add (SIGTERM, signal_handler, GINT_TO_POINTER (SIGTERM));
diff --git a/clients/cli/nmcli.h b/clients/cli/nmcli.h
index 874bca21..61bf86de 100644
--- a/clients/cli/nmcli.h
+++ b/clients/cli/nmcli.h
@@ -14,23 +14,16 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2010 - 2017 Red Hat, Inc.
+ * Copyright 2010 - 2018 Red Hat, Inc.
  */
 
 #ifndef NMC_NMCLI_H
 #define NMC_NMCLI_H
 
-#include "NetworkManager.h"
 #include "nm-secret-agent-old.h"
-
 #include "nm-meta-setting-desc.h"
 
-#if WITH_POLKIT_AGENT
-#include "nm-polkit-listener.h"
-#else
-/* polkit agent is not available; define fake NMPolkitListener */
-typedef gpointer NMPolkitListener;
-#endif
+struct _NMPolkitListener;
 
 typedef char *(*NmcCompEntryFunc) (const char *, int);
 
@@ -100,23 +93,18 @@ struct _NmcOutputField {
 	gboolean value_is_array;        /* Whether value is char** instead of char* */
 	gboolean free_value;            /* Whether to free the value */
 	NmcOfFlags flags;               /* Flags - whether and how to print values/field names/headers */
-	NMMetaTermColor color;             /* Use this color to print value */
-	NMMetaTermFormat color_fmt;        /* Use this terminal format to print value */
+	NMMetaColor color;              /* Use this color to print value */
 };
 
-typedef enum {
-	NMC_USE_COLOR_AUTO,
-	NMC_USE_COLOR_YES,
-	NMC_USE_COLOR_NO,
-} NmcColorOption;
-
 typedef struct _NmcConfig {
 	NMCPrintOutput print_output;                      /* Output mode */
-	NmcColorOption use_colors;                        /* Whether to use colors for output: option '--color' */
+	gboolean use_colors;                              /* Whether to use colors for output: option '--color' */
 	bool multiline_output;                            /* Multiline output instead of default tabular */
 	bool escape_values;                               /* Whether to escape ':' and '\' in terse tabular mode */
 	bool in_editor;                                   /* Whether running the editor - nmcli con edit' */
 	bool show_secrets;                                /* Whether to display secrets (both input and output): option '--show-secrets' */
+	bool overview;                                    /* Overview mode (hide default values) */
+	const char *palette[_NM_META_COLOR_NUM];          /* Color palette */
 } NmcConfig;
 
 typedef struct _NmcOutputData {
@@ -135,7 +123,7 @@ typedef struct _NmCli {
 
 	NMSecretAgentOld *secret_agent;                   /* Secret agent */
 	GHashTable *pwds_hash;                            /* Hash table with passwords in passwd-file */
-	NMPolkitListener *pk_listener ;                   /* polkit agent listener */
+	struct _NMPolkitListener *pk_listener;            /* polkit agent listener */
 
 	int should_wait;                                  /* Semaphore indicating whether nmcli should not end or not yet */
 	gboolean nowait_flag;                             /* '--nowait' option; used for passing to callbacks */
@@ -149,9 +137,15 @@ typedef struct _NmCli {
 	gboolean complete;                                /* Autocomplete the command line */
 	gboolean editor_status_line;                      /* Whether to display status line in connection editor */
 	gboolean editor_save_confirmation;                /* Whether to ask for confirmation on saving connections with 'autoconnect=yes' */
-	NMMetaTermColor editor_prompt_color;              /* Color of prompt in connection editor */
+
+	char *palette_buffer;                             /* Buffer with sequences for terminal-colors.d(5)-based coloring. */
 } NmCli;
 
+#define NMC_RETURN(nmc, rvalue) \
+	G_STMT_START { \
+		return ((nmc)->return_value = (rvalue)); \
+	} G_STMT_END
+
 extern NmCli nm_cli;
 
 /* Error quark for GError domain */
diff --git a/clients/cli/polkit-agent.c b/clients/cli/polkit-agent.c
index ecd58ba5..338f0b15 100644
--- a/clients/cli/polkit-agent.c
+++ b/clients/cli/polkit-agent.c
@@ -19,8 +19,6 @@
 
 #include "nm-default.h"
 
-#if WITH_POLKIT_AGENT
-
 #include "polkit-agent.h"
 
 #include <stdio.h>
@@ -29,17 +27,18 @@
 #include <unistd.h>
 
 #include "nm-polkit-listener.h"
-
 #include "common.h"
 
+#if WITH_POLKIT_AGENT
 static char *
-polkit_request (const char *request,
+polkit_request (NMPolkitListener *listener,
+                const char *request,
                 const char *action_id,
                 const char *message,
                 const char *icon_name,
                 const char *user,
                 gboolean echo_on,
-		gpointer user_data)
+                gpointer user_data)
 {
 	char *response, *tmp, *p;
 
@@ -62,28 +61,42 @@ polkit_request (const char *request,
 }
 
 static void
-polkit_show_info (const char *text)
+polkit_show_info (NMPolkitListener *listener,
+                  const char *text,
+                  gpointer user_data)
 {
 	g_print (_("Authentication message: %s\n"), text);
 }
 
 static void
-polkit_show_error (const char *text)
+polkit_show_error (NMPolkitListener *listener,
+                   const char *text,
+                   gpointer user_data)
 {
 	g_print (_("Authentication error: %s\n"), text);
 }
 
 static void
-polkit_completed (gboolean gained_authorization)
+polkit_completed (NMPolkitListener *listener,
+                  gboolean gained_authorization,
+                  gpointer user_data)
 {
 	/* We don't print anything here. The outcome will be evident from
 	 * the operation result anyway. */
 }
+#endif
 
 gboolean
 nmc_polkit_agent_init (NmCli* nmc, gboolean for_session, GError **error)
 {
-	PolkitAgentListener *listener;
+#if WITH_POLKIT_AGENT
+	static const NMPolkitListenVtable vtable = {
+		.on_request = polkit_request,
+		.on_show_info = polkit_show_info,
+		.on_show_error = polkit_show_error,
+		.on_completed = polkit_completed,
+	};
+	NMPolkitListener *listener;
 
 	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
@@ -91,24 +104,28 @@ nmc_polkit_agent_init (NmCli* nmc, gboolean for_session, GError **error)
 	if (!listener)
 		return FALSE;
 
-	nm_polkit_listener_set_request_callback (NM_POLKIT_LISTENER (listener), polkit_request, nmc);
-	nm_polkit_listener_set_show_info_callback (NM_POLKIT_LISTENER (listener), polkit_show_info);
-	nm_polkit_listener_set_show_error_callback (NM_POLKIT_LISTENER (listener), polkit_show_error);
-	nm_polkit_listener_set_completed_callback (NM_POLKIT_LISTENER (listener), polkit_completed);
+	nm_polkit_listener_set_vtable (listener, &vtable, nmc);
 
-	nmc->pk_listener = NM_POLKIT_LISTENER (listener);
+	nmc->pk_listener = listener;
+#endif
 	return TRUE;
 }
 
 void
 nmc_polkit_agent_fini (NmCli* nmc)
 {
-	g_clear_object (&nmc->pk_listener);
+#if WITH_POLKIT_AGENT
+	if (nmc->pk_listener) {
+		nm_polkit_listener_set_vtable (nmc->pk_listener, NULL, NULL);
+		g_clear_object (&nmc->pk_listener);
+	}
+#endif
 }
 
 gboolean
 nmc_start_polkit_agent_start_try (NmCli *nmc)
 {
+#if WITH_POLKIT_AGENT
 	GError *error = NULL;
 
 	/* We don't register polkit agent at all when running non-interactively */
@@ -121,30 +138,6 @@ nmc_start_polkit_agent_start_try (NmCli *nmc)
 		g_error_free (error);
 		return FALSE;
 	}
+#endif
 	return TRUE;
 }
-
-#else
-/* polkit agent is not avalable; implement stub functions. */
-
-#include "nmcli.h"
-#include "polkit-agent.h"
-
-gboolean
-nmc_polkit_agent_init (NmCli* nmc, gboolean for_session, GError **error)
-{
-	return TRUE;
-}
-
-void
-nmc_polkit_agent_fini (NmCli* nmc)
-{
-}
-
-gboolean
-nmc_start_polkit_agent_start_try (NmCli *nmc)
-{
-	return TRUE;
-}
-
-#endif /* #if WITH_POLKIT_AGENT */
diff --git a/clients/cli/settings.c b/clients/cli/settings.c
index 69275aad..01142ba2 100644
--- a/clients/cli/settings.c
+++ b/clients/cli/settings.c
@@ -313,16 +313,18 @@ _set_fcn_precheck_connection_secondaries (const char *value,
 {
 	const GPtrArray *connections;
 	NMConnection *con;
+	gs_free const char **strv0 = NULL;
 	gs_strfreev char **strv = NULL;
 	char **iter;
-	gboolean modified;
+	gboolean modified = FALSE;
 
-	strv = nmc_strsplit_set (value, " \t,", 0);
-	if (!strv)
+	strv0 = nm_utils_strsplit_set (value, " \t,");
+	if (!strv0)
 		return TRUE;
 
 	connections = nm_client_get_connections (nm_cli.client);
 
+	strv = g_strdupv ((char **) strv0);
 	for (iter = strv; *iter; iter++) {
 		if (nm_utils_is_uuid (*iter)) {
 			con = nmc_find_connection (connections, "uuid", *iter, NULL, FALSE);
@@ -472,6 +474,7 @@ get_property_val (NMSetting *setting, const char *prop, NMMetaAccessorGetType ge
 			                                               get_type,
 			                                               show_secrets ? NM_META_ACCESSOR_GET_FLAGS_SHOW_SECRETS : 0,
 			                                               &out_flags,
+			                                               NULL,
 			                                               (gpointer *) &to_free);
 			nm_assert (!out_flags);
 			return to_free ?: g_strdup (value);
@@ -746,9 +749,9 @@ nmc_setting_get_property_desc (NMSetting *setting, const char *prop)
 
 	return g_strdup_printf ("%s\n%s\n%s%s%s%s",
 	                        setting_desc_title,
-	                        setting_desc ? setting_desc : "",
+	                        setting_desc ?: "",
 	                        nmcli_nl, nmcli_desc_title, nmcli_nl,
-	                        nmcli_desc ? nmcli_desc : "");
+	                        nmcli_desc ?: "");
 }
 
 /*
diff --git a/clients/cli/utils.c b/clients/cli/utils.c
index f20e24ce..32c44e39 100644
--- a/clients/cli/utils.c
+++ b/clients/cli/utils.c
@@ -15,7 +15,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
  * Copyright 2010 Lennart Poettering
- * Copyright 2010 - 2017 Red Hat, Inc.
+ * Copyright 2010 - 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -75,6 +75,7 @@ _meta_type_nmc_generic_info_get_fcn (const NMMetaAbstractInfo *abstract_info,
                                      NMMetaAccessorGetType get_type,
                                      NMMetaAccessorGetFlags get_flags,
                                      NMMetaAccessorGetOutFlags *out_flags,
+                                     gboolean *out_is_default,
                                      gpointer *out_to_free)
 {
 	const NmcMetaGenericInfo *info = (const NmcMetaGenericInfo *) abstract_info;
@@ -85,23 +86,25 @@ _meta_type_nmc_generic_info_get_fcn (const NMMetaAbstractInfo *abstract_info,
 	if (!NM_IN_SET (get_type,
 	                NM_META_ACCESSOR_GET_TYPE_PARSABLE,
 	                NM_META_ACCESSOR_GET_TYPE_PRETTY,
-	                NM_META_ACCESSOR_GET_TYPE_TERMFORMAT))
+	                NM_META_ACCESSOR_GET_TYPE_COLOR))
 		g_return_val_if_reached (NULL);
 
-	/* omitting the out_to_free value is only allowed for TERMFORMAT. */
-	nm_assert (out_to_free || NM_IN_SET (get_type, NM_META_ACCESSOR_GET_TYPE_TERMFORMAT));
+	/* omitting the out_to_free value is only allowed for COLOR. */
+	nm_assert (out_to_free || NM_IN_SET (get_type, NM_META_ACCESSOR_GET_TYPE_COLOR));
 
 	if (info->get_fcn) {
-		return info->get_fcn (environment, environment_user_data,
+		return info->get_fcn (environment,
+		                      environment_user_data,
 		                      info, target,
 		                      get_type,
 		                      get_flags,
 		                      out_flags,
+		                      out_is_default,
 		                      out_to_free);
 	}
 
 	if (info->nested) {
-		NMC_HANDLE_TERMFORMAT (NM_META_TERM_COLOR_NORMAL);
+		NMC_HANDLE_COLOR (NM_META_COLOR_NONE);
 		return info->name;
 	}
 
@@ -117,37 +120,16 @@ const NMMetaType nmc_meta_type_generic_info = {
 
 /*****************************************************************************/
 
-static gboolean
-use_colors (NmcColorOption color_option)
-{
-	if (color_option == NMC_USE_COLOR_AUTO) {
-		static NmcColorOption cached = NMC_USE_COLOR_AUTO;
-
-		if (G_UNLIKELY (cached == NMC_USE_COLOR_AUTO)) {
-			if (   g_strcmp0 (g_getenv ("TERM"), "dumb") == 0
-				|| !isatty (STDOUT_FILENO))
-				cached = NMC_USE_COLOR_NO;
-			else
-				cached = NMC_USE_COLOR_YES;
-		}
-		return cached == NMC_USE_COLOR_YES;
-	}
-
-	return color_option == NMC_USE_COLOR_YES;
-}
-
 static const char *
-colorize_string (NmcColorOption color_option,
-                 NMMetaTermColor color,
-                 NMMetaTermFormat color_fmt,
+colorize_string (const NmcConfig *nmc_config,
+                 NMMetaColor color,
                  const char *str,
                  char **out_to_free)
 {
 	const char *out = str;
 
-	if (   use_colors (color_option)
-	    && (color != NM_META_TERM_COLOR_NORMAL || color_fmt != NM_META_TERM_FORMAT_NORMAL)) {
-		*out_to_free = nmc_colorize (color_option, color, color_fmt, "%s", str);
+	if (nmc_config && nmc_config->use_colors) {
+		*out_to_free = nmc_colorize (nmc_config, color, "%s", str);
 		out = *out_to_free;
 	}
 
@@ -202,7 +184,6 @@ next_arg (NmCli *nmc, int *argc, char ***argv, ...)
 		if (*argc == 0)
 			return -1;
 
-
 		va_start (args, argv);
 
 		if (nmc && nmc->complete && *argc == 1) {
@@ -389,118 +370,30 @@ nmc_terminal_show_progress (const char *str)
 	const char slashes[4] = {'|', '/', '-', '\\'};
 
 	nmc_terminal_erase_line ();
-	g_print ("%c %s", slashes[idx++], str ? str : "");
+	g_print ("%c %s", slashes[idx++], str ?: "");
 	fflush (stdout);
 	if (idx == 4)
 		idx = 0;
 }
 
-const char *
-nmc_term_color_sequence (NMMetaTermColor color)
-{
-	switch (color) {
-        case NM_META_TERM_COLOR_BLACK:
-		return "\33[30m";
-		break;
-        case NM_META_TERM_COLOR_RED:
-		return "\33[31m";
-		break;
-        case NM_META_TERM_COLOR_GREEN:
-		return "\33[32m";
-		break;
-        case NM_META_TERM_COLOR_YELLOW:
-		return "\33[33m";
-		break;
-        case NM_META_TERM_COLOR_BLUE:
-		return "\33[34m";
-		break;
-        case NM_META_TERM_COLOR_MAGENTA:
-		return "\33[35m";
-		break;
-        case NM_META_TERM_COLOR_CYAN:
-		return "\33[36m";
-		break;
-        case NM_META_TERM_COLOR_WHITE:
-		return "\33[37m";
-		break;
-	default:
-		return "";
-		break;
-	}
-}
-
-/* Parses @str for color as string or number */
-NMMetaTermColor
-nmc_term_color_parse_string (const char *str, GError **error)
-{
-	unsigned long color_int;
-	static const char *colors[] = { "normal", "black", "red", "green", "yellow",
-	                                "blue", "magenta", "cyan", "white", NULL };
-
-	if (nmc_string_to_uint (str, TRUE, 0, 8, &color_int)) {
-		return (NMMetaTermColor) color_int;
-	} else {
-		const char *color, **p;
-		int i;
-
-		color = nmc_string_is_valid (str, colors, error);
-		for (p = colors, i = 0; *p != NULL; p++, i++) {
-			if (*p == color)
-				return (NMMetaTermColor) i;
-		}
-		return -1;
-	}
-}
-
-const char *
-nmc_term_format_sequence (NMMetaTermFormat format)
-{
-	switch (format) {
-        case NM_META_TERM_FORMAT_BOLD:
-		return "\33[1m";
-		break;
-        case NM_META_TERM_FORMAT_DIM:
-		return "\33[2m";
-		break;
-        case NM_META_TERM_FORMAT_UNDERLINE:
-		return "\33[4m";
-		break;
-        case NM_META_TERM_FORMAT_BLINK:
-		return "\33[5m";
-		break;
-        case NM_META_TERM_FORMAT_REVERSE:
-		return "\33[7m";
-		break;
-        case NM_META_TERM_FORMAT_HIDDEN:
-		return "\33[8m";
-		break;
-	default:
-		return "";
-		break;
-	}
-}
-
 char *
-nmc_colorize (NmcColorOption color_option, NMMetaTermColor color, NMMetaTermFormat format, const char *fmt, ...)
+nmc_colorize (const NmcConfig *nmc_config, NMMetaColor color, const char *fmt, ...)
 {
 	va_list args;
 	char *str, *colored;
-	const char *ansi_color, *color_end, *ansi_fmt, *format_end;
-	static const char *end_seq = "\33[0m";
+	const char *ansi_seq = NULL;
 
 	va_start (args, fmt);
 	str = g_strdup_vprintf (fmt, args);
 	va_end (args);
 
-	if (!use_colors (color_option))
-		return str;
+	if (nmc_config->use_colors)
+		ansi_seq =  nmc_config->palette[color];
 
-	ansi_color = nmc_term_color_sequence (color);
-	ansi_fmt = nmc_term_format_sequence (format);
-	color_end = *ansi_color ? end_seq : "";
-	format_end = *ansi_fmt ? end_seq : "";
+	if (ansi_seq == NULL)
+		return str;
 
-	colored = g_strdup_printf ("%s%s%s%s%s", ansi_fmt, ansi_color, str, color_end, format_end);
+	colored = g_strdup_printf ("\33[%sm%s\33[0m", ansi_seq, str);
 	g_free (str);
 	return colored;
 }
@@ -524,7 +417,7 @@ nmc_count_color_escape_chars (const char *start, const char *end)
 			inside = TRUE;
 		if (inside)
 			num++;
-		if (*start == 'm') 
+		if (*start == 'm')
 			inside = FALSE;
 		start++;
 	}
@@ -603,9 +496,14 @@ int
 nmc_string_to_arg_array (const char *line, const char *delim, gboolean unquote,
                          char ***argv, int *argc)
 {
+	gs_free const char **arr0 = NULL;
 	char **arr;
 
-	arr = nmc_strsplit_set (line ? line : "", delim ? delim : " \t", 0);
+	arr0 = nm_utils_strsplit_set (line ?: "", delim ?: " \t");
+	if (!arr0)
+		arr = g_new0 (char *, 1);
+	else
+		arr = g_strdupv ((char **) arr0);
 
 	if (unquote) {
 		int i = 0;
@@ -613,7 +511,7 @@ nmc_string_to_arg_array (const char *line, const char *delim, gboolean unquote,
 		size_t l;
 		const char *quotes = "\"'";
 
-		while (arr && arr[i]) {
+		while (arr[i]) {
 			s = arr[i];
 			l = strlen (s);
 			if (l >= 2) {
@@ -628,7 +526,6 @@ nmc_string_to_arg_array (const char *line, const char *delim, gboolean unquote,
 
 	*argv = arr;
 	*argc = g_strv_length (arr);
-
 	return 0;
 }
 
@@ -713,7 +610,7 @@ set_val_arrc (NmcOutputField fields_array[], guint32 idx, const char **value)
 }
 
 void
-set_val_color_all (NmcOutputField fields_array[], NMMetaTermColor color)
+set_val_color_all (NmcOutputField fields_array[], NMMetaColor color)
 {
 	int i;
 
@@ -722,16 +619,6 @@ set_val_color_all (NmcOutputField fields_array[], NMMetaTermColor color)
 	}
 }
 
-void
-set_val_color_fmt_all (NmcOutputField fields_array[], NMMetaTermFormat format)
-{
-	int i;
-
-	for (i = 0; fields_array[i].info; i++) {
-		fields_array[i].color_fmt = format;
-	}
-}
-
 /*
  * Free 'value' members in array of NmcOutputField
  */
@@ -998,6 +885,7 @@ typedef struct {
 	const PrintDataCol *col;
 	const char *title;
 	bool title_to_free:1;
+	bool skip:1;
 	int width;
 } PrintDataHeaderCell;
 
@@ -1009,8 +897,7 @@ typedef enum {
 typedef struct {
 	guint row_idx;
 	const PrintDataHeaderCell *header_cell;
-	NMMetaTermColor term_color;
-	NMMetaTermFormat term_format;
+	NMMetaColor color;
 	union {
 		const char *plain;
 		const char *const*strv;
@@ -1096,6 +983,7 @@ _print_fill (const NmcConfig *nmc_config,
 
 		header_cell->col_idx = col_idx;
 		header_cell->col = col;
+		header_cell->skip = FALSE;
 
 		header_cell->title = nm_meta_abstract_info_get_name (info, TRUE);
 		if (   nmc_config->multiline_output
@@ -1130,10 +1018,11 @@ _print_fill (const NmcConfig *nmc_config,
 		for (i_col = 0; i_col < header_row->len; i_col++) {
 			char *to_free = NULL;
 			PrintDataCell *cell = &cells_line[i_col];
-			const PrintDataHeaderCell *header_cell;
+			PrintDataHeaderCell *header_cell;
 			const NMMetaAbstractInfo *info;
 			NMMetaAccessorGetOutFlags text_out_flags, color_out_flags;
 			gconstpointer value;
+			gboolean is_default;
 
 			header_cell = &g_array_index (header_row, PrintDataHeaderCell, i_col);
 			info = header_cell->col->selection_item->info;
@@ -1148,7 +1037,11 @@ _print_fill (const NmcConfig *nmc_config,
 			                                   text_get_type,
 			                                   text_get_flags,
 			                                   &text_out_flags,
+			                                   &is_default,
 			                                   (gpointer *) &to_free);
+
+			header_cell->skip = nmc_config->overview && is_default;
+
 			if (NM_FLAGS_HAS (text_out_flags, NM_META_ACCESSOR_GET_OUT_FLAGS_STRV)) {
 				if (value) {
 					if (nmc_config->multiline_output) {
@@ -1167,16 +1060,15 @@ _print_fill (const NmcConfig *nmc_config,
 				cell->text_to_free = !!to_free;
 			}
 
-			nm_meta_termformat_unpack (nm_meta_abstract_info_get (info,
-			                                                      nmc_meta_environment,
-			                                                      nmc_meta_environment_arg,
-			                                                      target,
-			                                                      NM_META_ACCESSOR_GET_TYPE_TERMFORMAT,
-			                                                      NM_META_ACCESSOR_GET_FLAGS_NONE,
-			                                                      &color_out_flags,
-			                                                      NULL),
-			                           &cell->term_color,
-			                           &cell->term_format);
+			cell->color = GPOINTER_TO_INT (nm_meta_abstract_info_get (info,
+			                                                          nmc_meta_environment,
+			                                                          nmc_meta_environment_arg,
+			                                                          target,
+			                                                          NM_META_ACCESSOR_GET_TYPE_COLOR,
+			                                                          NM_META_ACCESSOR_GET_FLAGS_NONE,
+			                                                          &color_out_flags,
+			                                                          NULL,
+			                                                          NULL));
 
 			if (cell->text_format == PRINT_DATA_CELL_FORMAT_TYPE_PLAIN) {
 				if (pretty && (!cell->text.plain|| !cell->text.plain[0])) {
@@ -1231,6 +1123,9 @@ _print_skip_column (const NmcConfig *nmc_config,
 	selection_item = header_cell->col->selection_item;
 	info = selection_item->info;
 
+	if (header_cell->skip)
+		return TRUE;
+
 	if (nmc_config->multiline_output) {
 		if (info->meta_type == &nm_meta_type_setting_info_editor) {
 			/* we skip the "name" entry for the setting in multiline output. */
@@ -1358,9 +1253,7 @@ _print_do (const NmcConfig *nmc_config,
 				gs_free char *text_to_free = NULL;
 				const char *text;
 
-				text = colorize_string (nmc_config->use_colors,
-				                        cell->term_color, cell->term_format,
-				                        lines[i_lines], &text_to_free);
+				text = colorize_string (nmc_config, cell->color, lines[i_lines], &text_to_free);
 				if (multiline) {
 					gs_free char *prefix = NULL;
 
@@ -1408,8 +1301,7 @@ _print_do (const NmcConfig *nmc_config,
 		}
 
 		if (   pretty
-		    && (   i_row < row_len - 1
-		        || multiline)) {
+		    && multiline) {
 			gs_free char *line = NULL;
 
 			g_print ("%s\n", (line = g_strnfill (ML_HEADER_WIDTH, '-')));
@@ -1489,7 +1381,7 @@ nmc_terminal_spawn_pager (const NmcConfig *nmc_config)
 	if (   nm_cli.nmc_config.in_editor
 	    || nm_cli.pager_pid > 0
 	    || nmc_config->print_output == NMC_PRINT_TERSE
-	    || !use_colors (nmc_config->use_colors)
+	    || !nmc_config->use_colors
 	    || g_strcmp0 (pager, "") == 0
 	    || getauxval (AT_SECURE))
 		return;
@@ -1560,7 +1452,7 @@ nmc_terminal_spawn_pager (const NmcConfig *nmc_config)
 /*****************************************************************************/
 
 static const char *
-get_value_to_print (NmcColorOption color_option,
+get_value_to_print (const NmcConfig *nmc_config,
                     const NmcOutputField *field,
                     gboolean field_name,
                     const char *not_set_str,
@@ -1586,7 +1478,7 @@ get_value_to_print (NmcColorOption color_option,
 	}
 
 	/* colorize the value */
-	out = colorize_string (color_option, field->color, field->color_fmt, value, out_to_free);
+	out = colorize_string (nmc_config, field->color, value, out_to_free);
 
 	if (out && out == free_value) {
 		nm_assert (!*out_to_free);
@@ -1680,7 +1572,7 @@ print_required_fields (const NmcConfig *nmc_config,
 					gs_free char *tmp = NULL;
 
 					val = *p ?: not_set_str;
-					print_val = colorize_string (nmc_config->use_colors, field_values[idx].color, field_values[idx].color_fmt,
+					print_val = colorize_string (nmc_config, field_values[idx].color,
 					                             val, &val_to_free);
 					tmp = g_strdup_printf ("%s%s%s[%d]:",
 					                       section_prefix ? (const char*) field_values[0].value : "",
@@ -1701,7 +1593,7 @@ print_required_fields (const NmcConfig *nmc_config,
 				/* value is a string */
 
 				val = val && *val ? val : not_set_str;
-				print_val = colorize_string (nmc_config->use_colors, field_values[idx].color, field_values[idx].color_fmt,
+				print_val = colorize_string (nmc_config, field_values[idx].color,
 				                             val, &val_to_free);
 				tmp = g_strdup_printf ("%s%s%s:",
 				                       section_prefix ? hdr_name : "",
@@ -1732,7 +1624,7 @@ print_required_fields (const NmcConfig *nmc_config,
 
 		idx = g_array_index (indices, int, i);
 
-		value = get_value_to_print (nmc_config->use_colors, (NmcOutputField *) field_values+idx, field_names,
+		value = get_value_to_print (nmc_config, (NmcOutputField *) field_values+idx, field_names,
 		                            not_set_str, &val_to_free);
 
 		if (terse) {
@@ -1805,7 +1697,7 @@ print_data_prepare_width (GPtrArray *output_data)
 
 			row = g_ptr_array_index (output_data, j);
 			field_names = row[0].flags & NMC_OF_FLAG_FIELD_NAMES;
-			value = get_value_to_print (NMC_USE_COLOR_NO, row+i, field_names, "--", &val_to_free);
+			value = get_value_to_print (NULL, row+i, field_names, "--", &val_to_free);
 			len = nmc_string_screen_width (value, NULL);
 			max_width = len > max_width ? len : max_width;
 		}
diff --git a/clients/cli/utils.h b/clients/cli/utils.h
index ffc5b6f3..dc0ce083 100644
--- a/clients/cli/utils.h
+++ b/clients/cli/utils.h
@@ -14,7 +14,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2010 - 2017 Red Hat, Inc.
+ * Copyright 2010 - 2018 Red Hat, Inc.
  */
 
 #ifndef NMC_UTILS_H
@@ -41,11 +41,7 @@ char *ssid_to_hex (const char *str, gsize len);
 void nmc_terminal_erase_line (void);
 void nmc_terminal_show_progress (const char *str);
 void nmc_terminal_spawn_pager (const NmcConfig *nmc_config);
-gboolean nmc_term_use_colors (NmcColorOption color_option);
-const char *nmc_term_color_sequence (NMMetaTermColor color);
-const char *nmc_term_format_sequence (NMMetaTermFormat format);
-NMMetaTermColor nmc_term_color_parse_string (const char *str, GError **error);
-char *nmc_colorize (NmcColorOption color_option, NMMetaTermColor color, NMMetaTermFormat format, const char * fmt, ...)  _nm_printf (4, 5);
+char *nmc_colorize (const NmcConfig *nmc_config, NMMetaColor color, const char * fmt, ...)  _nm_printf (3, 4);
 void nmc_filter_out_colors_inplace (char *str);
 char *nmc_filter_out_colors (const char *str);
 char *nmc_get_user_input (const char *ask_str);
@@ -53,14 +49,12 @@ int nmc_string_to_arg_array (const char *line, const char *delim, gboolean unquo
                              char ***argv, int *argc);
 const char *nmc_string_is_valid (const char *input, const char **allowed, GError **error);
 char * nmc_util_strv_for_display (const char *const*strv, gboolean brackets);
-char **nmc_strsplit_set (const char *str, const char *delimiter, int max_tokens);
 int nmc_string_screen_width (const char *start, const char *end);
 void set_val_str  (NmcOutputField fields_array[], guint32 index, char *value);
 void set_val_strc (NmcOutputField fields_array[], guint32 index, const char *value);
 void set_val_arr  (NmcOutputField fields_array[], guint32 index, char **value);
 void set_val_arrc (NmcOutputField fields_array[], guint32 index, const char **value);
-void set_val_color_all (NmcOutputField fields_array[], NMMetaTermColor color);
-void set_val_color_fmt_all (NmcOutputField fields_array[], NMMetaTermFormat format);
+void set_val_color_all (NmcOutputField fields_array[], NMMetaColor color);
 void nmc_free_output_field_values (NmcOutputField fields_array[]);
 
 GArray *parse_output_fields (const char *fields_str,
@@ -129,10 +123,10 @@ typedef enum {
 
 } NmcGenericInfoType;
 
-#define NMC_HANDLE_TERMFORMAT(color) \
+#define NMC_HANDLE_COLOR(color) \
 	G_STMT_START { \
-		if (get_type == NM_META_ACCESSOR_GET_TYPE_TERMFORMAT) \
-			return nm_meta_termformat_pack ((color), NM_META_TERM_FORMAT_NORMAL); \
+		if (get_type == NM_META_ACCESSOR_GET_TYPE_COLOR) \
+			return GINT_TO_POINTER (color); \
 	} G_STMT_END
 
 struct _NmcMetaGenericInfo {
@@ -144,14 +138,19 @@ struct _NmcMetaGenericInfo {
 	const char *name;
 	const char *name_header;
 	const NmcMetaGenericInfo *const*nested;
-	gconstpointer (*get_fcn) (const NMMetaEnvironment *environment,
-	                          gpointer environment_user_data,
-	                          const NmcMetaGenericInfo *info,
-	                          gpointer target,
-	                          NMMetaAccessorGetType get_type,
-	                          NMMetaAccessorGetFlags get_flags,
-	                          NMMetaAccessorGetOutFlags *out_flags,
-	                          gpointer *out_to_free);
+
+#define NMC_META_GENERIC_INFO_GET_FCN_ARGS \
+	const NMMetaEnvironment *environment, \
+	gpointer environment_user_data, \
+	const NmcMetaGenericInfo *info, \
+	gpointer target, \
+	NMMetaAccessorGetType get_type, \
+	NMMetaAccessorGetFlags get_flags, \
+	NMMetaAccessorGetOutFlags *out_flags, \
+	gboolean *out_is_default, \
+	gpointer *out_to_free
+
+	gconstpointer (*get_fcn) (NMC_META_GENERIC_INFO_GET_FCN_ARGS);
 };
 
 #define NMC_META_GENERIC(n, ...) \
@@ -164,6 +163,52 @@ struct _NmcMetaGenericInfo {
 #define NMC_META_GENERIC_WITH_NESTED(n, nest, ...) \
 	NMC_META_GENERIC (n, .nested = (nest), __VA_ARGS__)
 
+#define NMC_META_GENERIC_GROUP(_group_name, _nested, _name_header) \
+	((const NMMetaAbstractInfo *const*) ((const NmcMetaGenericInfo *const[]) { \
+		NMC_META_GENERIC_WITH_NESTED (_group_name,_nested, .name_header = _name_header), \
+		NULL, \
+	}))
+
+static inline const char *
+nmc_meta_generic_get_str_i18n (const char *s, NMMetaAccessorGetType get_type)
+{
+	if (!NM_IN_SET (get_type, NM_META_ACCESSOR_GET_TYPE_PRETTY,
+	                          NM_META_ACCESSOR_GET_TYPE_PARSABLE))
+		g_return_val_if_reached (NULL);
+
+	if (!s)
+		return NULL;
+	if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY)
+		return gettext (s);
+	return s;
+}
+
+static inline const char *
+nmc_meta_generic_get_bool (gboolean val, NMMetaAccessorGetType get_type)
+{
+	return nmc_meta_generic_get_str_i18n (val ? N_("yes") : N_("no"), get_type);
+}
+
+static inline char *
+nmc_meta_generic_get_enum_with_detail (gint64 enum_val, const char *str_val, NMMetaAccessorGetType get_type)
+{
+	if (!NM_IN_SET (get_type, NM_META_ACCESSOR_GET_TYPE_PRETTY,
+	                          NM_META_ACCESSOR_GET_TYPE_PARSABLE))
+		g_return_val_if_reached (NULL);
+
+	if (!str_val) {
+		/* Pass %NULL for only printing the numeric value. */
+		return g_strdup_printf ("%lld", (long long) enum_val);
+	}
+
+	/* note that this function will always print "$NUM ($NICK)", also in PARSABLE
+	 * mode. That might not be desired, but it's done for certain properties to preserve
+	 * previous behavior. */
+	if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY)
+		return g_strdup_printf (_("%lld (%s)"), (long long) enum_val, gettext (str_val));
+	return g_strdup_printf ("%lld (%s)", (long long) enum_val, str_val);
+}
+
 /*****************************************************************************/
 
 gboolean nmc_print (const NmcConfig *nmc_config,