about summary refs log tree commit diff
path: root/cli/src/utils.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2014-07-06 02:16:10 +0200
committerMichael Biebl <biebl@debian.org>2014-07-06 02:16:10 +0200
commit33491bc4279481db8ae47213e34a6d695a0e8830 (patch)
tree097d2b0fdff3fae6885381ae5e57a182cd8cbbba /cli/src/utils.c
parent59c3714a494c3b3765657c0551ad82842d98a7d2 (diff)
Imported Upstream version 0.9.10.0 upstream/0.9.10.0
Diffstat (limited to 'cli/src/utils.c')
-rw-r--r--cli/src/utils.c799
1 files changed, 694 insertions, 105 deletions
diff --git a/cli/src/utils.c b/cli/src/utils.c
index ab0a5dba..9b513f5a 100644
--- a/cli/src/utils.c
+++ b/cli/src/utils.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.
  *
- * (C) Copyright 2010 - 2012 Red Hat, Inc.
+ * (C) Copyright 2010 - 2014 Red Hat, Inc.
  */
 
 /* Generated configuration file */
@@ -22,6 +22,8 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <stdlib.h>
+#include <errno.h>
 
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -36,8 +38,8 @@
 int
 matches (const char *cmd, const char *pattern)
 {
-	int len = strlen (cmd);
-	if (len > strlen (pattern))
+	size_t len = strlen (cmd);
+	if (!len || len > strlen (pattern))
 		return -1;
 	return memcmp (pattern, cmd, len);
 }
@@ -57,14 +59,116 @@ next_arg (int *argc, char ***argv)
 	return 0;
 }
 
+gboolean
+nmc_arg_is_help (const char *arg)
+{
+	if (!arg)
+		return FALSE;
+	if (   matches (arg, "help") == 0
+	    || (g_str_has_prefix (arg, "-")  && matches (arg+1, "help") == 0)
+	    || (g_str_has_prefix (arg, "--") && matches (arg+2, "help") == 0)) {
+		return TRUE;
+	}
+	return FALSE;
+}
+
+gboolean
+nmc_arg_is_option (const char *str, const char *opt_name)
+{
+	const char *p;
+
+	if (!str || !*str)
+		return FALSE;
+
+	if (str[0] != '-')
+		return FALSE;
+
+	p = (str[1] == '-') ? str + 2 : str + 1;
+
+	return (*p ? (matches (p, opt_name) == 0) : FALSE);
+}
+
+
 /*
- *  Convert SSID to a printable form.
- *  If it is an UTF-8 string, enclose it in quotes and return it.
- *  Otherwise convert it to a hex string representation.
+ * Helper function to parse command-line arguments.
+ * arg_arr: description of arguments to look for
+ * last:    whether these are last expected arguments
+ * argc:    command-line argument array
+ * argv:    command-line argument array size
+ * error:   error set on a failure (when FALSE is returned)
+ * Returns: TRUE on success, FALSE on an error and sets 'error'
+ */
+gboolean
+nmc_parse_args (nmc_arg_t *arg_arr, gboolean last, int *argc, char ***argv, GError **error)
+{
+	nmc_arg_t *p;
+	gboolean found;
+	gboolean have_mandatory;
+
+	g_return_val_if_fail (arg_arr != NULL, FALSE);
+	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+	while (*argc > 0) {
+		found = FALSE;
+
+		for (p = arg_arr; p->name; p++) {
+			if (strcmp (**argv, p->name) == 0) {
+
+				if (p->found) {
+					/* Don't allow repeated arguments, because the argument of the same
+					 * name could be used later on the line for another purpose. Assume
+					 * that's the case and return.
+					 */
+					return TRUE;
+				}
+
+				if (p->has_value) {
+					if (next_arg (argc, argv) != 0) {
+						g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
+						             _("Error: value for '%s' argument is required."), *(*argv-1));
+						return FALSE;
+					}
+					*(p->value) = **argv;
+				}
+				p->found = TRUE;
+				found = TRUE;
+				break;
+			}
+		}
+
+		if (!found) {
+			have_mandatory = TRUE;
+			for (p = arg_arr; p->name; p++) {
+				if (p->mandatory && !p->found) {
+					have_mandatory = FALSE;
+					break;
+				}
+			}
+
+			if (have_mandatory && !last)
+				return TRUE;
+
+			if (p->name)
+				g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
+				             _("Error: Argument '%s' was expected, but '%s' provided."), p->name, **argv);
+			else
+				g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
+				             _("Error: Unexpected argument '%s'"), **argv);
+			return FALSE;
+		}
+
+		next_arg (argc, argv);
+	}
+
+	return TRUE;
+}
+
+/*
+ *  Convert SSID to a hex string representation.
  *  Caller has to free the returned string using g_free()
  */
 char *
-ssid_to_printable (const char *str, gsize len)
+ssid_to_hex (const char *str, gsize len)
 {
 	GString *printable;
 	char *printable_str;
@@ -73,9 +177,6 @@ ssid_to_printable (const char *str, gsize len)
 	if (str == NULL || len == 0)
 		return NULL;
 
-	if (g_utf8_validate (str, len, NULL))
-		return g_strdup_printf ("'%.*s'", (int) len, str);
-
 	printable = g_string_new (NULL);
 	for (i = 0; i < len; i++) {
 		g_string_append_printf (printable, "%02X", (unsigned char) str[i]);
@@ -91,19 +192,19 @@ ssid_to_printable (const char *str, gsize len)
 char *
 nmc_ip4_address_as_string (guint32 ip, GError **error)
 {
-	struct in_addr tmp_addr;
+	guint32 tmp_addr;
 	char buf[INET_ADDRSTRLEN];
 
 	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
 	memset (&buf, '\0', sizeof (buf));
-	tmp_addr.s_addr = ip;
+	tmp_addr = ip;
 
 	if (inet_ntop (AF_INET, &tmp_addr, buf, INET_ADDRSTRLEN)) {
 		return g_strdup (buf);
 	} else {
 		g_set_error (error, NMCLI_ERROR, 0, _("Error converting IP4 address '0x%X' to text form"),
-		             ntohl (tmp_addr.s_addr));
+		             ntohl (tmp_addr));
 		return NULL;
 	}
 }
@@ -170,6 +271,298 @@ nmc_terminal_show_progress (const char *str)
 		idx = 0;
 }
 
+const char *
+nmc_term_color_sequence (NmcTermColor color)
+{
+	switch (color) {
+        case NMC_TERM_COLOR_BLACK:
+		return "\33[30m";
+		break;
+        case NMC_TERM_COLOR_RED:
+		return "\33[31m";
+		break;
+        case NMC_TERM_COLOR_GREEN:
+		return "\33[32m";
+		break;
+        case NMC_TERM_COLOR_YELLOW:
+		return "\33[33m";
+		break;
+        case NMC_TERM_COLOR_BLUE:
+		return "\33[34m";
+		break;
+        case NMC_TERM_COLOR_MAGENTA:
+		return "\33[35m";
+		break;
+        case NMC_TERM_COLOR_CYAN:
+		return "\33[36m";
+		break;
+        case NMC_TERM_COLOR_WHITE:
+		return "\33[37m";
+		break;
+	default:
+		return "";
+		break;
+	}
+}
+
+char *
+nmc_colorize (NmcTermColor color, const char *fmt, ...)
+{
+	va_list args;
+	char *str, *colored;
+	const char *ansi_color, *color_end;
+
+	va_start (args, fmt);
+	str = g_strdup_vprintf (fmt, args);
+	va_end (args);
+
+	ansi_color = nmc_term_color_sequence (color);
+	if (*ansi_color)
+		color_end = "\33[0m";
+	else
+		color_end = "";
+
+	colored = g_strdup_printf ("%s%s%s", ansi_color, str, color_end);
+	g_free (str);
+	return colored;
+}
+
+/*
+ * Convert string to signed integer.
+ * If required, the resulting number is checked to be in the <min,max> range.
+ */
+gboolean
+nmc_string_to_int_base (const char *str,
+                        int base,
+                        gboolean range_check,
+                        long int min,
+                        long int max,
+                        long int *value)
+{
+	char *end;
+	long int tmp;
+
+	errno = 0;
+	tmp = strtol (str, &end, base);
+	if (errno || *end != '\0' || (range_check && (tmp < min || tmp > max))) {
+		return FALSE;
+	}
+	*value = tmp;
+	return TRUE;
+}
+
+/*
+ * Convert string to unsigned integer.
+ * If required, the resulting number is checked to be in the <min,max> range.
+ */
+gboolean
+nmc_string_to_uint_base (const char *str,
+                         int base,
+                         gboolean range_check,
+                         unsigned long int min,
+                         unsigned long int max,
+                         unsigned long int *value)
+{
+	char *end;
+	unsigned long int tmp;
+
+	errno = 0;
+	tmp = strtoul (str, &end, base);
+	if (errno || *end != '\0' || (range_check && (tmp < min || tmp > max))) {
+		return FALSE;
+	}
+	*value = tmp;
+	return TRUE;
+}
+
+gboolean
+nmc_string_to_int (const char *str,
+                   gboolean range_check,
+                   long int min,
+                   long int max,
+                   long int *value)
+{
+	return nmc_string_to_int_base (str, 10, range_check, min, max, value);
+}
+
+gboolean
+nmc_string_to_uint (const char *str,
+                    gboolean range_check,
+                    unsigned long int min,
+                    unsigned long int max,
+                    unsigned long int *value)
+{
+	return nmc_string_to_uint_base (str, 10, range_check, min, max, value);
+}
+
+gboolean
+nmc_string_to_bool (const char *str, gboolean *val_bool, GError **error)
+{
+	const char *s_true[] = { "true", "yes", "on", NULL };
+	const char *s_false[] = { "false", "no", "off", NULL };
+
+	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+	if (g_strcmp0 (str, "o") == 0) {
+		g_set_error (error, 1, 0,
+		             _("'%s' is ambiguous (on x off)"), str);
+		return FALSE;
+	}
+
+	if (nmc_string_is_valid (str, s_true, NULL))
+		*val_bool = TRUE;
+	else if (nmc_string_is_valid (str, s_false, NULL))
+		*val_bool = FALSE;
+	else {
+		g_set_error (error, 1, 0,
+		             _("'%s' is not valid; use [%s] or [%s]"),
+		             str, "true, yes, on", "false, no, off");
+		return FALSE;
+	}
+	return TRUE;
+}
+
+/*
+ * Ask user for input and return the string.
+ * The caller is responsible for freeing the returned string.
+ */
+char *
+nmc_get_user_input (const char *ask_str)
+{
+	char *line = NULL;
+	size_t line_ln = 0;
+	ssize_t num;
+
+	fprintf (stdout, "%s", ask_str);
+	num = getline (&line, &line_ln, stdin);
+
+	/* Remove newline from the string */
+	if (num < 1 || (num == 1 && line[0] == '\n')) {
+		g_free (line);
+		line = NULL;
+	} else {
+		if (line[num-1] == '\n')
+			line[num-1] = '\0';
+	}
+
+	return line;
+}
+
+/*
+ * Split string in 'line' according to 'delim' to (argument) array.
+ */
+int
+nmc_string_to_arg_array (const char *line, const char *delim, char ***argv, int *argc)
+{
+	int i = 0;
+	char **arr;
+
+	arr = g_strsplit_set (line ? line : "", delim ? delim : " \t", 0);
+	while (arr && arr[i])
+		i++;
+
+	*argc = i;
+	*argv = arr;
+
+	return 0;
+}
+
+/*
+ * Check whether 'input' is contained in 'allowed' array. It performs case
+ * insensitive comparison and supports shortcut strings if they are unique.
+ * Returns: a pointer to found string in allowed array on success or NULL.
+ * On failure: error->code : 0 - string not found; 1 - string is ambiguous
+ */
+const char *
+nmc_string_is_valid (const char *input, const char **allowed, GError **error)
+{
+	const char **p;
+	size_t input_ln, p_len;
+	gboolean prev_match = FALSE;
+	const char *ret = NULL;
+
+	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+	if (!input || !*input)
+		goto finish;
+
+	input_ln = strlen (input);
+	for (p = allowed; p && *p; p++) {
+		p_len = strlen (*p);
+		if (g_ascii_strncasecmp (input, *p, input_ln) == 0) {
+			if (input_ln == p_len) {
+				ret = *p;
+				break;
+			}
+			if (!prev_match)
+				ret = *p;
+			else {
+				g_set_error (error, 1, 1, _("'%s' is ambiguous (%s x %s)"),
+				             input, ret, *p);
+				return NULL;
+			}
+			prev_match = TRUE;
+		}
+	}
+
+finish:
+	if (ret == NULL) {
+		char *valid_vals = g_strjoinv (", ", (char **) allowed);
+		if (!input || !*input) {
+			g_set_error (error, 1, 0, _("missing name, try one of [%s]"),
+			             valid_vals);
+		} else {
+			g_set_error (error, 1, 0, _("'%s' not among [%s]"),
+			             input ? input : "", valid_vals);
+		}
+
+		g_free (valid_vals);
+	}
+	return ret;
+}
+
+/*
+ * Convert string array (char **) to GSList.
+ *
+ * Returns: pointer to newly created GSList. Caller should free it.
+ */
+GSList *
+nmc_util_strv_to_slist (char **strv)
+{
+	GSList *list = NULL;
+	guint i = 0;
+
+	while (strv && strv[i])
+		list = g_slist_prepend (list, g_strdup (strv[i++]));
+
+	return g_slist_reverse (list);
+}
+
+/*
+ * Wrapper function for g_strsplit_set() that removes empty strings
+ * from the vector as they are not useful in most cases.
+ */
+char **
+nmc_strsplit_set (const char *str, const char *delimiter, int max_tokens)
+{
+	char **result;
+	uint i;
+	uint j;
+
+	result = g_strsplit_set (str, delimiter, max_tokens);
+
+	/* remove empty strings */
+	for (i = 0; result && result[i]; i++) {
+		if (*(result[i]) == '\0') {
+			g_free (result[i]);
+			for (j = i; result[j]; j++)
+				result[j] = result[j + 1];
+			i--;
+		}
+	}
+	return result;
+}
+
 /*
  * Find out how many columns an UTF-8 string occupies on the screen
  */
@@ -189,54 +582,165 @@ nmc_string_screen_width (const char *start, const char *end)
 }
 
 void
-set_val_str (NmcOutputField fields_array[], guint32 idx, const char *value)
+set_val_str (NmcOutputField fields_array[], guint32 idx, char *value)
 {
-	fields_array[idx].flags = 0;
 	fields_array[idx].value = value;
+	fields_array[idx].value_is_array = FALSE;
+	fields_array[idx].free_value = TRUE;
 }
 
 void
-set_val_arr (NmcOutputField fields_array[], guint32 idx, const char **value)
+set_val_strc (NmcOutputField fields_array[], guint32 idx, const char *value)
+{
+	fields_array[idx].value = (char *) value;
+	fields_array[idx].value_is_array = FALSE;
+	fields_array[idx].free_value = FALSE;
+}
+
+void
+set_val_arr (NmcOutputField fields_array[], guint32 idx, char **value)
 {
-	fields_array[idx].flags = NMC_OF_FLAG_ARRAY;
 	fields_array[idx].value = value;
+	fields_array[idx].value_is_array = TRUE;
+	fields_array[idx].free_value = TRUE;
+}
+
+void
+set_val_arrc (NmcOutputField fields_array[], guint32 idx, const char **value)
+{
+	fields_array[idx].value = (char **) value;
+	fields_array[idx].value_is_array = TRUE;
+	fields_array[idx].free_value = FALSE;
 }
 
 /*
- * Parse comma separated fields in 'fields_str' according to 'fields_array'.
- * IN:  'field_str':    comma-separated fields names
- *      'fields_array': array of allowed fields
- * RETURN: GArray with indices representing fields in 'fields_array'.
- *         Caller is responsible to free it.
+ * Free 'value' members in array of NmcOutputField
+ */
+void
+nmc_free_output_field_values (NmcOutputField fields_array[])
+{
+	NmcOutputField *iter = fields_array;
+
+	while (iter && iter->name) {
+		if (iter->free_value) {
+			if (iter->value_is_array)
+				g_strfreev ((char **) iter->value);
+			else
+				g_free ((char *) iter->value);
+			iter->value = NULL;
+		}
+		iter++;
+	}
+}
+
+/**
+ * parse_output_fields:
+ * @field_str: comma-separated field names to parse
+ * @fields_array: array of allowed fields
+ * @parse_groups: whether the fields can contain group prefix (e.g. general.driver)
+ * @group_fields: (out) (allow-none): array of field names for particular groups
+ * @error: (out) (allow-none): location to store error, or %NULL
+ *
+ * Parses comma separated fields in @fields_str according to @fields_array.
+ * When @parse_groups is %TRUE, fields can be in the form 'group.field'. Then
+ * @group_fields will be filled with the required field for particular group.
+ * @group_fields array corresponds to the returned array.
+ * Examples:
+ *   @field_str:     "type,name,uuid" | "ip4,general.device" | "ip4.address,ip6"
+ *   returned array:   2    0    1    |   7         0        |     7         9
+ *   @group_fields:   NULL NULL NULL  |  NULL    "device"    | "address"    NULL
+ *
+ * Returns: #GArray with indices representing fields in @fields_array.
+ *   Caller is responsible for freeing the array.
  */
 GArray *
-parse_output_fields (const char *fields_str, const NmcOutputField fields_array[], GError **error)
+parse_output_fields (const char *fields_str,
+                     const NmcOutputField fields_array[],
+                     gboolean parse_groups,
+                     GPtrArray **group_fields,
+                     GError **error)
 {
 	char **fields, **iter;
 	GArray *array;
-	int i;
+	int i, j;
 
 	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+	g_return_val_if_fail (group_fields == NULL || *group_fields == NULL, NULL);
 
 	array = g_array_new (FALSE, FALSE, sizeof (int));
+	if (parse_groups && group_fields)
+		*group_fields = g_ptr_array_new_full (20, (GDestroyNotify) g_free);
 
 	/* Split supplied fields string */
 	fields = g_strsplit_set (fields_str, ",", -1);
 	for (iter = fields; iter && *iter; iter++) {
-		for (i = 0; fields_array[i].name; i++) {
-			if (strcasecmp (*iter, fields_array[i].name) == 0) {
-				g_array_append_val (array, i);
-				break;
+		int idx = -1;
+
+		g_strstrip (*iter);
+		if (parse_groups) {
+			/* e.g. "general.device,general.driver,ip4,ip6" */
+			gboolean found = FALSE;
+			char *left = *iter;
+			char *right = strchr (*iter, '.');
+
+			if (right)
+				*right++ = '\0';
+
+			for (i = 0; fields_array[i].name; i++) {
+				if (strcasecmp (left, fields_array[i].name) == 0) {
+					NmcOutputField *valid_names = fields_array[i].group;
+					idx = i;
+					if (!right && !valid_names) {
+						found = TRUE;
+						break;
+					}
+					for (j = 0; valid_names && valid_names[j].name; j++) {
+						if (!right || strcasecmp (right, valid_names[j].name) == 0) {
+							found = TRUE;
+							break;
+						}
+					}
+				}
+				if (found)
+					break;
+			}
+			if (found) {
+				/* Add index to array, and field name (or NULL) to group_fields array */
+				g_array_append_val (array, idx);
+				if (group_fields && *group_fields)
+					g_ptr_array_add (*group_fields, g_strdup (right));
+			}
+			if (right)
+				*(right-1) = '.';  /* Restore the original string */
+		} else {
+			/* e.g. "general,ip4,ip6" */
+			for (i = 0; fields_array[i].name; i++) {
+				if (strcasecmp (*iter, fields_array[i].name) == 0) {
+					g_array_append_val (array, i);
+					break;
+				}
 			}
 		}
+
+		/* Field was not found - error case */
 		if (fields_array[i].name == NULL) {
+			/* Set GError */
 			if (!strcasecmp (*iter, "all") || !strcasecmp (*iter, "common"))
 				g_set_error (error, NMCLI_ERROR, 0, _("field '%s' has to be alone"), *iter);
+			else {
+				char *allowed_fields = nmc_get_allowed_fields (fields_array, idx);
+				g_set_error (error, NMCLI_ERROR, 1, _("invalid field '%s'; allowed fields: %s"),
+				             *iter, allowed_fields);
+				g_free (allowed_fields);
+			}
 
-			else
-				g_set_error (error, NMCLI_ERROR, 1, _("invalid field '%s'"), *iter);
+			/* Free arrays on error */
 			g_array_free (array, TRUE);
 			array = NULL;
+			if (group_fields && *group_fields) {
+				g_ptr_array_free (*group_fields, TRUE);
+				*group_fields = NULL;
+			}
 			goto done;
 		}
 	}
@@ -246,6 +750,35 @@ done:
 	return array;
 }
 
+/**
+* nmc_get_allowed_fields:
+* @fields_array: array of fields
+* @group_idx: index to the array (for second-level array in 'group' member),
+*   or -1
+*
+* Returns: string of allowed fields names.
+*   Caller is responsible for freeing the array.
+*/
+char *
+nmc_get_allowed_fields (const NmcOutputField fields_array[], int group_idx)
+{
+	GString *allowed_fields = g_string_sized_new (256);
+	int i;
+
+	if (group_idx != -1 && fields_array[group_idx].group) {
+		NmcOutputField *second_level = fields_array[group_idx].group;
+		for (i = 0; second_level[i].name; i++)
+			g_string_append_printf (allowed_fields, "%s.%s,",
+			                        fields_array[group_idx].name, second_level[i].name);
+	} else {
+		for (i = 0; fields_array[i].name; i++)
+			g_string_append_printf (allowed_fields, "%s,", fields_array[i].name);
+	}
+	g_string_truncate (allowed_fields, allowed_fields->len - 1);
+
+	return g_string_free (allowed_fields, FALSE);
+}
+
 gboolean
 nmc_terse_option_check (NMCPrintOutput print_output, const char *fields, GError **error)
 {
@@ -264,14 +797,68 @@ nmc_terse_option_check (NMCPrintOutput print_output, const char *fields, GError
 	return TRUE;
 }
 
+NmcOutputField *
+nmc_dup_fields_array (NmcOutputField fields[], size_t size, guint32 flags)
+{
+	NmcOutputField *row;
+
+	row = g_malloc0 (size);
+	memcpy (row, fields, size);
+	row[0].flags = flags;
+
+	return row;
+}
+
+void
+nmc_empty_output_fields (NmCli *nmc)
+{
+	guint i;
+
+	/* Free values in field structure */
+	for (i = 0; i < nmc->output_data->len; i++) {
+		NmcOutputField *fld_arr = g_ptr_array_index (nmc->output_data, i);
+		nmc_free_output_field_values (fld_arr);
+	}
+
+	/* Empty output_data array */
+	if (nmc->output_data->len > 0)
+		g_ptr_array_remove_range (nmc->output_data, 0, nmc->output_data->len);
+
+	if (nmc->print_fields.indices) {
+		g_array_free (nmc->print_fields.indices, TRUE);
+		nmc->print_fields.indices = NULL;
+	}
+}
+
+static char *
+get_value_to_print (NmcOutputField *fields,
+                    gboolean field_name,
+                    const char *not_set_str,
+                    gboolean *dealloc)
+{
+	gboolean is_array = fields->value_is_array;
+	char *value;
+
+	if (field_name)
+		value = _(fields->name_l10n);
+	else
+		value = fields->value ?
+		          (is_array ? g_strjoinv (" | ", (char **) fields->value) :
+		                      (char *) fields->value) :
+		          (char *) not_set_str;
+	*dealloc = fields->value && is_array && !field_name;
+	return value;
+}
+
 /*
  * Print both headers or values of 'field_values' array.
- * Entries to print and their order are specified via indices
- * in 'fields.indices' array.
- * 'fields.flags' specify various aspects influencing the output.
+ * Entries to print and their order are specified via indices in
+ * 'nmc->print_fields.indices' array.
+ * Various flags influencing the output of fields are set up in the first item
+ * of 'field_values' array.
  */
 void
-print_fields (const NmcPrintFields fields, const NmcOutputField field_values[])
+print_required_fields (NmCli *nmc, const NmcOutputField field_values[])
 {
 	GString *str;
 	int width1, width2;
@@ -280,14 +867,15 @@ print_fields (const NmcPrintFields fields, const NmcOutputField field_values[])
 	char *indent_str;
 	const char *not_set_str = "--";
 	int i;
-	gboolean multiline = fields.flags & NMC_PF_FLAG_MULTILINE;
-	gboolean terse = fields.flags & NMC_PF_FLAG_TERSE;
-	gboolean pretty = fields.flags & NMC_PF_FLAG_PRETTY;
-	gboolean main_header_add = fields.flags & NMC_PF_FLAG_MAIN_HEADER_ADD;
-	gboolean main_header_only = fields.flags & NMC_PF_FLAG_MAIN_HEADER_ONLY;
-	gboolean field_names = fields.flags & NMC_PF_FLAG_FIELD_NAMES;
-	gboolean escape = fields.flags & NMC_PF_FLAG_ESCAPE;
-	gboolean section_prefix = fields.flags & NMC_PF_FLAG_SECTION_PREFIX;
+	const NmcPrintFields fields = nmc->print_fields;
+	gboolean multiline = nmc->multiline_output;
+	gboolean terse = (nmc->print_output == NMC_PRINT_TERSE);
+	gboolean pretty = (nmc->print_output == NMC_PRINT_PRETTY);
+	gboolean escape = nmc->escape_values;
+	gboolean main_header_add = field_values[0].flags & NMC_OF_FLAG_MAIN_HEADER_ADD;
+	gboolean main_header_only = field_values[0].flags & NMC_OF_FLAG_MAIN_HEADER_ONLY;
+	gboolean field_names = field_values[0].flags & NMC_OF_FLAG_FIELD_NAMES;
+	gboolean section_prefix = field_values[0].flags & NMC_OF_FLAG_SECTION_PREFIX;
 	gboolean main_header = main_header_add || main_header_only;
 
 	/* No headers are printed in terse mode:
@@ -319,25 +907,29 @@ print_fields (const NmcPrintFields fields, const NmcOutputField field_values[])
 			for (i = 0; i < fields.indices->len; i++) {
 				char *tmp;
 				int idx = g_array_index (fields.indices, int, i);
-				guint32 value_is_array = field_values[idx].flags & NMC_OF_FLAG_ARRAY;
+				gboolean is_array = field_values[idx].value_is_array;
 
 				/* section prefix can't be an array */
-				g_assert (!value_is_array || !section_prefix || idx != 0);
+				g_assert (!is_array || !section_prefix || idx != 0);
 
 				if (section_prefix && idx == 0)  /* The first field is section prefix */
 					continue;
 
-				if (value_is_array) {
+				if (is_array) {
 					/* value is a null-terminated string array */
 					const char **p;
 					int j;
 
 					for (p = (const char **) field_values[idx].value, j = 1; p && *p; p++, j++) {
-						tmp = g_strdup_printf ("%s%s%s[%d]:", section_prefix ? (const char*) field_values[0].value : "",
-						                                      section_prefix ? "." : "",
-						                                      _(field_values[idx].name_l10n),
-						                                      j);
-						printf ("%-*s%s\n", terse ? 0 : ML_VALUE_INDENT, tmp, *p ? *p : not_set_str);
+						tmp = g_strdup_printf ("%s%s%s[%d]:",
+						                       section_prefix ? (const char*) field_values[0].value : "",
+						                       section_prefix ? "." : "",
+						                       _(field_values[idx].name_l10n),
+						                       j);
+						width1 = strlen (tmp);
+						width2 = nmc_string_screen_width (tmp, NULL);
+						printf ("%-*s%s\n", terse ? 0 : ML_VALUE_INDENT+width1-width2, tmp,
+						                    *p ? *p : not_set_str);
 						g_free (tmp);
 					}
 				} else {
@@ -345,10 +937,14 @@ print_fields (const NmcPrintFields fields, const NmcOutputField field_values[])
 					const char *hdr_name = (const char*) field_values[0].value;
 					const char *val = (const char*) field_values[idx].value;
 
-					tmp = g_strdup_printf ("%s%s%s:", section_prefix ? hdr_name : "",
-					                                  section_prefix ? "." : "",
-					                                  _(field_values[idx].name_l10n));
-					printf ("%-*s%s\n", terse ? 0 : ML_VALUE_INDENT, tmp, val ? val : not_set_str);
+					tmp = g_strdup_printf ("%s%s%s:",
+					                       section_prefix ? hdr_name : "",
+					                       section_prefix ? "." : "",
+					                       _(field_values[idx].name_l10n));
+					width1 = strlen (tmp);
+					width2 = nmc_string_screen_width (tmp, NULL);
+					printf ("%-*s%s\n", terse ? 0 : ML_VALUE_INDENT+width1-width2, tmp,
+					                    val ? val : not_set_str);
 					g_free (tmp);
 				}
 			}
@@ -366,14 +962,8 @@ print_fields (const NmcPrintFields fields, const NmcOutputField field_values[])
 
 	for (i = 0; i < fields.indices->len; i++) {
 		int idx = g_array_index (fields.indices, int, i);
-		guint32 value_is_array = field_values[idx].flags & NMC_OF_FLAG_ARRAY;
-		char *value;
-		if (field_names)
-			value = _(field_values[idx].name_l10n);
-		else
-			value = field_values[idx].value ?
-			        (value_is_array ? g_strjoinv (" | ", (char **) field_values[idx].value) : (char *) field_values[idx].value) :
-			        (char *) not_set_str;
+		gboolean dealloc;
+		char *value = get_value_to_print ((NmcOutputField *) field_values+idx, field_names, not_set_str, &dealloc);
 
 		if (terse) {
 			if (escape) {
@@ -396,7 +986,7 @@ print_fields (const NmcPrintFields fields, const NmcOutputField field_values[])
 			table_width += field_values[idx].width + width1 - width2 + 1;
 		}
 
-		if (value_is_array && field_values[idx].value && !field_values)
+		if (dealloc)
 			g_free (value);
 	}
 
@@ -438,57 +1028,56 @@ print_fields (const NmcPrintFields fields, const NmcOutputField field_values[])
 }
 
 /*
- * Find out whether NetworkManager is running (via D-Bus NameHasOwner), assuring
- * NetworkManager won't be autostart (by D-Bus) if not running.
- * We can't use NMClient (nm_client_get_manager_running()) because NMClient
- * constructor calls GetPermissions of NM_DBUS_SERVICE, which would autostart
- * NetworkManger if it is configured as D-Bus launchable service.
+ * Print nmc->output_data
+ *
+ * It first finds out maximal string length in columns and fill the value to
+ * 'width' member of NmcOutputField, so that columns in tabular output are
+ * properly aligned. Then each object (row in tabular) is printed using
+ * print_required_fields() function.
  */
-gboolean
-nmc_is_nm_running (NmCli *nmc, GError **error)
+void
+print_data (NmCli *nmc)
 {
-	DBusGConnection *connection = NULL;
-	DBusGProxy *proxy = NULL;
-	GError *err = NULL;
-	gboolean has_owner = FALSE;
+	int i, j;
+	size_t len;
+	NmcOutputField *row;
+	int num_fields = 0;
 
-	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+	if (!nmc->output_data || nmc->output_data->len < 1)
+		return;
 
-	connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
-	if (!connection) {
-		g_string_printf (nmc->return_text, _("Error: Couldn't connect to system bus: %s"), err->message);
-		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
-		g_propagate_error (error, err);
-		goto done;
+	/* How many fields? */
+	row = g_ptr_array_index (nmc->output_data, 0);
+	while (row->name) {
+		num_fields++;
+		row++;
 	}
 
-	proxy = dbus_g_proxy_new_for_name (connection,
-	                                   "org.freedesktop.DBus",
-	                                   "/org/freedesktop/DBus",
-	                                   "org.freedesktop.DBus");
-	if (!proxy) {
-		g_string_printf (nmc->return_text, _("Error: Couldn't create D-Bus object proxy for org.freedesktop.DBus"));
-		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
-		if (error)
-			g_set_error_literal (error, NMCLI_ERROR, 0, nmc->return_text->str);
-		goto done;
+	/* Find out maximal string lengths */
+	for (i = 0; i < num_fields; i++) {
+		size_t max_width = 0;
+		for (j = 0; j < nmc->output_data->len; j++) {
+			gboolean field_names, dealloc;
+			char *value;
+			row = g_ptr_array_index (nmc->output_data, j);
+			field_names = row[0].flags & NMC_OF_FLAG_FIELD_NAMES;
+			value = get_value_to_print (row+i, field_names, "--", &dealloc);
+			len = nmc_string_screen_width (value, NULL);
+			max_width = len > max_width ? len : max_width;
+			if (dealloc)
+				g_free (value);
+		}
+		for (j = 0; j < nmc->output_data->len; j++) {
+			row = g_ptr_array_index (nmc->output_data, j);
+			row[i].width = max_width + 1;
+		}
 	}
 
-	if (!org_freedesktop_DBus_name_has_owner (proxy, NM_DBUS_SERVICE, &has_owner, &err)) {
-		g_string_printf (nmc->return_text, _("Error: NameHasOwner request failed: %s"),
-		                 (err && err->message) ? err->message : _("(unknown)"));
-		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
-		g_propagate_error (error, err);
-		goto done;
+	/* Now we can print the data. */
+	for (i = 0; i < nmc->output_data->len; i++) {
+		row = g_ptr_array_index (nmc->output_data, i);
+		print_required_fields (nmc, row);
 	}
-
-done:
-	if (connection)
-		dbus_g_connection_unref (connection);
-	if (proxy)
-		g_object_unref (proxy);
-
-	return has_owner;
 }
 
 /*