summary refs log tree commit diff
path: root/clients/cli/connections.c
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/connections.c
parentee9c73a923909e23a649407be77e25235d769e25 (diff)
New upstream version 1.11.4 upstream/1.11.4
Diffstat (limited to 'clients/cli/connections.c')
-rw-r--r--clients/cli/connections.c1486
1 files changed, 689 insertions, 797 deletions
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;
 }