1    	/* nmcli - command-line tool to control NetworkManager
2    	 *
3    	 * This program is free software; you can redistribute it and/or modify
4    	 * it under the terms of the GNU General Public License as published by
5    	 * the Free Software Foundation; either version 2 of the License, or
6    	 * (at your option) any later version.
7    	 *
8    	 * This program is distributed in the hope that it will be useful,
9    	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   	 * GNU General Public License for more details.
12   	 *
13   	 * You should have received a copy of the GNU General Public License along
14   	 * with this program; if not, write to the Free Software Foundation, Inc.,
15   	 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16   	 *
17   	 * (C) Copyright 2010 - 2013 Red Hat, Inc.
18   	 */
19   	
20   	/* Generated configuration file */
21   	#include "config.h"
22   	
23   	#include <stdio.h>
24   	#include <string.h>
25   	#include <stdlib.h>
26   	#include <errno.h>
27   	
28   	#include <sys/socket.h>
29   	#include <netinet/in.h>
30   	#include <arpa/inet.h>
31   	
32   	#include <glib.h>
33   	#include <glib/gi18n.h>
34   	#include <dbus/dbus-glib-bindings.h>
35   	
36   	#include "utils.h"
37   	
38   	int
39   	matches (const char *cmd, const char *pattern)
40   	{
41   		int len = strlen (cmd);
42   		if (len > strlen (pattern))
43   			return -1;
44   		return memcmp (pattern, cmd, len);
45   	}
46   	
47   	int
48   	next_arg (int *argc, char ***argv)
49   	{
50   		int arg_num = *argc;
51   	
52   		if (arg_num > 0) {
53   			(*argc)--;
54   			(*argv)++;
55   		}
56   		if (arg_num <= 1)
57   			return -1;
58   	
59   		return 0;
60   	}
61   	
62   	gboolean
63   	nmc_arg_is_help (const char *arg)
64   	{
65   		if (!arg)
66   			return FALSE;
67   		if (   matches (arg, "help") == 0
68   		    || (g_str_has_prefix (arg, "-")  && matches (arg+1, "help") == 0)
69   		    || (g_str_has_prefix (arg, "--") && matches (arg+2, "help") == 0)) {
70   			return TRUE;
71   		}
72   		return FALSE;
73   	}
74   	
75   	/*
76   	 * Helper function to parse command-line arguments.
77   	 * arg_arr: description of arguments to look for
78   	 * last:    whether these are last expected arguments
79   	 * argc:    command-line argument array
80   	 * argv:    command-line argument array size
81   	 * error:   error set on a failure (when FALSE is returned)
82   	 * Returns: TRUE on success, FALSE on an error and sets 'error'
83   	 */
84   	gboolean
85   	nmc_parse_args (nmc_arg_t *arg_arr, gboolean last, int *argc, char ***argv, GError **error)
86   	{
87   		nmc_arg_t *p;
88   		gboolean found;
89   		gboolean have_mandatory;
90   	
91   		g_return_val_if_fail (arg_arr != NULL, FALSE);
92   		g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
93   	
94   		while (*argc > 0) {
95   			found = FALSE;
96   	
(1) Event alias: Assigning: "p" = "arg_arr".
(4) Event deref_ptr: Directly dereferencing pointer "p".
Also see events: [alias][check_after_deref][deref_ptr]
97   			for (p = arg_arr; p->name; p++) {
98   				if (strcmp (**argv, p->name) == 0) {
99   	
100  					if (p->found) {
101  						/* Don't allow repeated arguments, because the argument of the same
102  						 * name could be used later on the line for another purpose. Assume
103  						 * that's the case and return.
104  						 */
105  						return TRUE;
106  					}
107  	
108  					if (p->has_value) {
109  						if (next_arg (argc, argv) != 0) {
110  							g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
111  							             _("Error: value for '%s' argument is required."), *(*argv-1));
112  							return FALSE;
113  						}
114  						*(p->value) = **argv;
115  					}
116  					p->found = TRUE;
117  					found = TRUE;
118  					break;
119  				}
120  			}
121  	
122  			if (!found) {
123  				have_mandatory = TRUE;
(2) Event alias: Assigning: "p" = "arg_arr".
(5) Event deref_ptr: Directly dereferencing pointer "p".
Also see events: [alias][check_after_deref][deref_ptr]
124  				for (p = arg_arr; p->name; p++) {
125  					if (p->mandatory && !p->found) {
126  						have_mandatory = FALSE;
127  						break;
128  					}
129  				}
130  	
131  				if (have_mandatory && !last)
132  					return TRUE;
133  	
(3) Event check_after_deref: Null-checking "p" suggests that it may be null, but it has already been dereferenced on all paths leading to the check.
Also see events: [alias][alias][deref_ptr][deref_ptr]
134  				if (p && p->name)
135  					g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
136  					             _("Error: Argument '%s' was expected, but '%s' provided."), p->name, **argv);
137  				else
138  					g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
139  					             _("Error: Unexpected argument '%s'"), **argv);
140  				return FALSE;
141  			}
142  	
143  			next_arg (argc, argv);
144  		}
145  	
146  		return TRUE;
147  	}
148  	
149  	/*
150  	 *  Convert SSID to a hex string representation.
151  	 *  Caller has to free the returned string using g_free()
152  	 */
153  	char *
154  	ssid_to_hex (const char *str, gsize len)
155  	{
156  		GString *printable;
157  		char *printable_str;
158  		int i;
159  	
160  		if (str == NULL || len == 0)
161  			return NULL;
162  	
163  		printable = g_string_new (NULL);
164  		for (i = 0; i < len; i++) {
165  			g_string_append_printf (printable, "%02X", (unsigned char) str[i]);
166  		}
167  		printable_str = g_string_free (printable, FALSE);
168  		return printable_str;
169  	}
170  	
171  	/*
172  	 * Converts IPv4 address from guint32 in network-byte order to text representation.
173  	 * Returns: text form of the IP or NULL (then error is set)
174  	 */
175  	char *
176  	nmc_ip4_address_as_string (guint32 ip, GError **error)
177  	{
178  		guint32 tmp_addr;
179  		char buf[INET_ADDRSTRLEN];
180  	
181  		g_return_val_if_fail (error == NULL || *error == NULL, NULL);
182  	
183  		memset (&buf, '\0', sizeof (buf));
184  		tmp_addr = ip;
185  	
186  		if (inet_ntop (AF_INET, &tmp_addr, buf, INET_ADDRSTRLEN)) {
187  			return g_strdup (buf);
188  		} else {
189  			g_set_error (error, NMCLI_ERROR, 0, _("Error converting IP4 address '0x%X' to text form"),
190  			             ntohl (tmp_addr));
191  			return NULL;
192  		}
193  	}
194  	
195  	/*
196  	 * Converts IPv6 address in in6_addr structure to text representation.
197  	 * Returns: text form of the IP or NULL (then error is set)
198  	 */
199  	char *
200  	nmc_ip6_address_as_string (const struct in6_addr *ip, GError **error)
201  	{
202  		char buf[INET6_ADDRSTRLEN];
203  	
204  		g_return_val_if_fail (error == NULL || *error == NULL, NULL);
205  	
206  		memset (&buf, '\0', sizeof (buf));
207  	
208  		if (inet_ntop (AF_INET6, ip, buf, INET6_ADDRSTRLEN)) {
209  			return g_strdup (buf);
210  		} else {
211  			if (error) {
212  				int j;
213  				GString *ip6_str = g_string_new (NULL);
214  				g_string_append_printf (ip6_str, "%02X", ip->s6_addr[0]);
215  				for (j = 1; j < 16; j++)
216  					g_string_append_printf (ip6_str, " %02X", ip->s6_addr[j]);
217  				g_set_error (error, NMCLI_ERROR, 0, _("Error converting IP6 address '%s' to text form"),
218  				             ip6_str->str);
219  				g_string_free (ip6_str, TRUE);
220  			}
221  			return NULL;
222  		}
223  	}
224  	
225  	/*
226  	 * Erase terminal line using ANSI escape sequences.
227  	 * It prints <ESC>[2K sequence to erase the line and then \r to return back
228  	 * to the beginning of the line.
229  	 *
230  	 * http://www.termsys.demon.co.uk/vtansi.htm
231  	 */
232  	void
233  	nmc_terminal_erase_line (void)
234  	{
235  		printf ("\33[2K\r");
236  		fflush (stdout);
237  	}
238  	
239  	/*
240  	 * Print animated progress for an operation.
241  	 * Repeated calls of the function will show rotating slash in terminal followed
242  	 * by the string passed in 'str' argument.
243  	 */
244  	void
245  	nmc_terminal_show_progress (const char *str)
246  	{
247  		static int idx = 0;
248  		const char slashes[4] = {'|', '/', '-', '\\'};
249  	
250  		nmc_terminal_erase_line ();
251  		printf ("%c %s", slashes[idx++], str ? str : "");
252  		fflush (stdout);
253  		if (idx == 4)
254  			idx = 0;
255  	}
256  	
257  	const char *
258  	nmc_term_color_sequence (NmcTermColor color)
259  	{
260  		switch (color) {
261  	        case NMC_TERM_COLOR_BLACK:
262  			return "\33[30m";
263  			break;
264  	        case NMC_TERM_COLOR_RED:
265  			return "\33[31m";
266  			break;
267  	        case NMC_TERM_COLOR_GREEN:
268  			return "\33[32m";
269  			break;
270  	        case NMC_TERM_COLOR_YELLOW:
271  			return "\33[33m";
272  			break;
273  	        case NMC_TERM_COLOR_BLUE:
274  			return "\33[34m";
275  			break;
276  	        case NMC_TERM_COLOR_MAGENTA:
277  			return "\33[35m";
278  			break;
279  	        case NMC_TERM_COLOR_CYAN:
280  			return "\33[36m";
281  			break;
282  	        case NMC_TERM_COLOR_WHITE:
283  			return "\33[37m";
284  			break;
285  		default:
286  			return "";
287  			break;
288  		}
289  	}
290  	
291  	char *
292  	nmc_colorize (NmcTermColor color, const char *fmt, ...)
293  	{
294  		va_list args;
295  		char *str;
296  		const char *ansi_color, *color_end;
297  	
298  		va_start (args, fmt);
299  		str = g_strdup_vprintf (fmt, args);
300  		va_end (args);
301  	
302  		ansi_color = nmc_term_color_sequence (color);
303  		if (*ansi_color)
304  			color_end = "\33[0m";
305  		else
306  			color_end = "";
307  	
308  		return g_strdup_printf ("%s%s%s", ansi_color, str, color_end);
309  	}
310  	
311  	/*
312  	 * Convert string to signed integer.
313  	 * If required, the resulting number is checked to be in the <min,max> range.
314  	 */
315  	gboolean
316  	nmc_string_to_int_base (const char *str,
317  	                        int base,
318  	                        gboolean range_check,
319  	                        long int min,
320  	                        long int max,
321  	                        long int *value)
322  	{
323  		char *end;
324  		long int tmp;
325  	
326  		errno = 0;
327  		tmp = strtol (str, &end, base);
328  		if (errno || *end != '\0' || (range_check && (tmp < min || tmp > max))) {
329  			return FALSE;
330  		}
331  		*value = tmp;
332  		return TRUE;
333  	}
334  	
335  	/*
336  	 * Convert string to unsigned integer.
337  	 * If required, the resulting number is checked to be in the <min,max> range.
338  	 */
339  	gboolean
340  	nmc_string_to_uint_base (const char *str,
341  	                         int base,
342  	                         gboolean range_check,
343  	                         unsigned long int min,
344  	                         unsigned long int max,
345  	                         unsigned long int *value)
346  	{
347  		char *end;
348  		unsigned long int tmp;
349  	
350  		errno = 0;
351  		tmp = strtoul (str, &end, base);
352  		if (errno || *end != '\0' || (range_check && (tmp < min || tmp > max))) {
353  			return FALSE;
354  		}
355  		*value = tmp;
356  		return TRUE;
357  	}
358  	
359  	gboolean
360  	nmc_string_to_int (const char *str,
361  	                   gboolean range_check,
362  	                   long int min,
363  	                   long int max,
364  	                   long int *value)
365  	{
366  		return nmc_string_to_int_base (str, 10, range_check, min, max, value);
367  	}
368  	
369  	gboolean
370  	nmc_string_to_uint (const char *str,
371  	                    gboolean range_check,
372  	                    unsigned long int min,
373  	                    unsigned long int max,
374  	                    unsigned long int *value)
375  	{
376  		return nmc_string_to_uint_base (str, 10, range_check, min, max, value);
377  	}
378  	
379  	gboolean
380  	nmc_string_to_bool (const char *str, gboolean *val_bool, GError **error)
381  	{
382  		const char *s_true[] = { "true", "yes", "on", NULL };
383  		const char *s_false[] = { "false", "no", "off", NULL };
384  	
385  		g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
386  	
387  		if (nmc_string_is_valid (str, s_true, NULL))
388  			*val_bool = TRUE;
389  		else if (nmc_string_is_valid (str, s_false, NULL))
390  			*val_bool = FALSE;
391  		else {
392  			g_set_error (error, 1, 0,
393  			             _("'%s' is not valid; use [%s] or [%s]"),
394  			             str, "true, yes, on", "false, no, off");
395  			return FALSE;
396  		}
397  		return TRUE;
398  	}
399  	
400  	/*
401  	 * Ask user for input and return the string.
402  	 * The caller is responsible for freeing the returned string.
403  	 */
404  	char *
405  	nmc_get_user_input (const char *ask_str)
406  	{
407  		char *line = NULL;
408  		size_t line_ln = 0;
409  		ssize_t num;
410  	
411  		fprintf (stdout, "%s", ask_str);
412  		num = getline (&line, &line_ln, stdin);
413  	
414  		/* Remove newline from the string */
415  		if (num < 1 || (num == 1 && line[0] == '\n')) {
416  			g_free (line);
417  			line = NULL;
418  		} else {
419  			if (line[num-1] == '\n')
420  				line[num-1] = '\0';
421  		}
422  	
423  		return line;
424  	}
425  	
426  	/*
427  	 * Split string in 'line' according to 'delim' to (argument) array.
428  	 */
429  	int
430  	nmc_string_to_arg_array (const char *line, const char *delim, char ***argv, int *argc)
431  	{
432  		int i = 0;
433  		char **arr;
434  	
435  		arr = g_strsplit_set (line ? line : "", delim ? delim : " \t", 0);
436  		while (arr && arr[i])
437  			i++;
438  	
439  		*argc = i;
440  		*argv = arr;
441  	
442  		return 0;
443  	}
444  	
445  	/*
446  	 * Check whether 'input' is contained in 'allowed' array. It performs case
447  	 * insensitive comparison and supports shortcut strings if they are unique.
448  	 * Returns: a pointer to found string in allowed array on success or NULL.
449  	 * On failure: error->code : 0 - string not found; 1 - string is ambiguous
450  	 */
451  	const char *
452  	nmc_string_is_valid (const char *input, const char **allowed, GError **error)
453  	{
454  		const char **p;
455  		size_t input_ln, p_len;
456  		gboolean prev_match = FALSE;
457  		const char *ret = NULL;
458  	
459  		g_return_val_if_fail (error == NULL || *error == NULL, NULL);
460  	
461  		if (!input || !*input)
462  			goto finish;
463  	
464  		input_ln = strlen (input);
465  		for (p = allowed; p && *p; p++) {
466  			p_len = strlen (*p);
467  			if (g_ascii_strncasecmp (input, *p, input_ln) == 0) {
468  				if (input_ln == p_len) {
469  					ret = *p;
470  					break;
471  				}
472  				if (!prev_match)
473  					ret = *p;
474  				else {
475  					g_set_error (error, 1, 1, _("'%s' is ambiguous (%s x %s)"),
476  					             input, ret, *p);
477  					return NULL;
478  				}
479  				prev_match = TRUE;
480  			}
481  		}
482  	
483  	finish:
484  		if (ret == NULL) {
485  			char *valid_vals = g_strjoinv (", ", (char **) allowed);
486  			if (!input || !*input) {
487  				g_set_error (error, 1, 0, _("missing name, try one of [%s]"),
488  				             valid_vals);
489  			} else {
490  				g_set_error (error, 1, 0, _("'%s' not among [%s]"),
491  				             input ? input : "", valid_vals);
492  			}
493  	
494  			g_free (valid_vals);
495  		}
496  		return ret;
497  	}
498  	
499  	/*
500  	 * Convert string array (char **) to GSList.
501  	 *
502  	 * Returns: pointer to newly created GSList. Caller should free it.
503  	 */
504  	GSList *
505  	nmc_util_strv_to_slist (char **strv)
506  	{
507  		GSList *list = NULL;
508  		guint i = 0;
509  	
510  		while (strv && strv[i])
511  			list = g_slist_prepend (list, g_strdup (strv[i++]));
512  	
513  		return g_slist_reverse (list);
514  	}
515  	
516  	/*
517  	 * Wrapper function for g_strsplit_set() that removes empty strings
518  	 * from the vector as they are not useful in most cases.
519  	 */
520  	char **
521  	nmc_strsplit_set (const char *str, const char *delimiter, int max_tokens)
522  	{
523  		char **result;
524  		uint i;
525  		uint j;
526  	
527  		result = g_strsplit_set (str, delimiter, max_tokens);
528  	
529  		/* remove empty strings */
530  		for (i = 0; result && result[i]; i++) {
531  			if (*(result[i]) == '\0') {
532  				g_free (result[i]);
533  				for (j = i; result[j]; j++)
534  					result[j] = result[j + 1];
535  				i--;
536  			}
537  		}
538  		return result;
539  	}
540  	
541  	/*
542  	 * Find out how many columns an UTF-8 string occupies on the screen
543  	 */
544  	int
545  	nmc_string_screen_width (const char *start, const char *end)
546  	{
547  		int width = 0;
548  	
549  		if (end == NULL)
550  			end = start + strlen (start);
551  	
552  		while (start < end) {
553  			width += g_unichar_iswide (g_utf8_get_char (start)) ? 2 : g_unichar_iszerowidth (g_utf8_get_char (start)) ? 0 : 1;
554  			start = g_utf8_next_char (start);
555  		}
556  		return width;
557  	}
558  	
559  	void
560  	set_val_str (NmcOutputField fields_array[], guint32 idx, char *value)
561  	{
562  		fields_array[idx].value = value;
563  		fields_array[idx].value_is_array = FALSE;
564  		fields_array[idx].free_value = TRUE;
565  	}
566  	
567  	void
568  	set_val_strc (NmcOutputField fields_array[], guint32 idx, const char *value)
569  	{
570  		fields_array[idx].value = (char *) value;
571  		fields_array[idx].value_is_array = FALSE;
572  		fields_array[idx].free_value = FALSE;
573  	}
574  	
575  	void
576  	set_val_arr (NmcOutputField fields_array[], guint32 idx, char **value)
577  	{
578  		fields_array[idx].value = value;
579  		fields_array[idx].value_is_array = TRUE;
580  		fields_array[idx].free_value = TRUE;
581  	}
582  	
583  	void
584  	set_val_arrc (NmcOutputField fields_array[], guint32 idx, const char **value)
585  	{
586  		fields_array[idx].value = (char **) value;
587  		fields_array[idx].value_is_array = TRUE;
588  		fields_array[idx].free_value = FALSE;
589  	}
590  	
591  	/*
592  	 * Free 'value' members in array of NmcOutputField
593  	 */
594  	void
595  	nmc_free_output_field_values (NmcOutputField fields_array[])
596  	{
597  		NmcOutputField *iter = fields_array;
598  	
599  		while (iter && iter->name) {
600  			if (iter->free_value) {
601  				if (iter->value_is_array)
602  					g_strfreev ((char **) iter->value);
603  				else
604  					g_free ((char *) iter->value);
605  				iter->value = NULL;
606  			}
607  			iter++;
608  		}
609  	}
610  	
611  	/*
612  	 * Parse comma separated fields in 'fields_str' according to 'fields_array'.
613  	 * IN:  'field_str':    comma-separated fields names
614  	 *      'fields_array': array of allowed fields
615  	 * RETURN: GArray with indices representing fields in 'fields_array'.
616  	 *         Caller is responsible to free it.
617  	 */
618  	GArray *
619  	parse_output_fields (const char *fields_str, const NmcOutputField fields_array[], GError **error)
620  	{
621  		char **fields, **iter;
622  		GArray *array;
623  		int i;
624  	
625  		g_return_val_if_fail (error == NULL || *error == NULL, NULL);
626  	
627  		array = g_array_new (FALSE, FALSE, sizeof (int));
628  	
629  		/* Split supplied fields string */
630  		fields = g_strsplit_set (fields_str, ",", -1);
631  		for (iter = fields; iter && *iter; iter++) {
632  			for (i = 0; fields_array[i].name; i++) {
633  				if (strcasecmp (*iter, fields_array[i].name) == 0) {
634  					g_array_append_val (array, i);
635  					break;
636  				}
637  			}
638  			if (fields_array[i].name == NULL) {
639  				if (!strcasecmp (*iter, "all") || !strcasecmp (*iter, "common"))
640  					g_set_error (error, NMCLI_ERROR, 0, _("field '%s' has to be alone"), *iter);
641  	
642  				else
643  					g_set_error (error, NMCLI_ERROR, 1, _("invalid field '%s'"), *iter);
644  				g_array_free (array, TRUE);
645  				array = NULL;
646  				goto done;
647  			}
648  		}
649  	done:
650  		if (fields)
651  			g_strfreev (fields);
652  		return array;
653  	}
654  	
655  	gboolean
656  	nmc_terse_option_check (NMCPrintOutput print_output, const char *fields, GError **error)
657  	{
658  		g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
659  	
660  		if (print_output == NMC_PRINT_TERSE) {
661  			if (!fields) {
662  				g_set_error_literal (error, NMCLI_ERROR, 0, _("Option '--terse' requires specifying '--fields'"));
663  				return FALSE;
664  			} else if (   !strcasecmp (fields, "all")
665  			           || !strcasecmp (fields, "common")) {
666  				g_set_error (error, NMCLI_ERROR, 0, _("Option '--terse' requires specific '--fields' option values , not '%s'"), fields);
667  				return FALSE;
668  			}
669  		}
670  		return TRUE;
671  	}
672  	
673  	NmcOutputField *
674  	nmc_dup_fields_array (NmcOutputField fields[], size_t size, guint32 flags)
675  	{
676  		NmcOutputField *row;
677  	
678  		row = g_malloc0 (size);
679  		memcpy (row, fields, size);
680  		row[0].flags = flags;
681  	
682  		return row;
683  	}
684  	
685  	void
686  	nmc_empty_output_fields (NmCli *nmc)
687  	{
688  		guint i;
689  	
690  		/* Free values in field structure */
691  		for (i = 0; i < nmc->output_data->len; i++) {
692  			NmcOutputField *fld_arr = g_ptr_array_index (nmc->output_data, i);
693  			nmc_free_output_field_values (fld_arr);
694  		}
695  	
696  		/* Empty output_data array */
697  		if (nmc->output_data->len > 0)
698  			g_ptr_array_remove_range (nmc->output_data, 0, nmc->output_data->len);
699  	
700  		if (nmc->print_fields.indices) {
701  			g_array_free (nmc->print_fields.indices, TRUE);
702  			nmc->print_fields.indices = NULL;
703  		}
704  	}
705  	
706  	static char *
707  	get_value_to_print (NmcOutputField *fields,
708  	                    gboolean field_name,
709  	                    const char *not_set_str,
710  	                    gboolean *dealloc)
711  	{
712  		gboolean is_array = fields->value_is_array;
713  		char *value;
714  	
715  		if (field_name)
716  			value = _(fields->name_l10n);
717  		else
718  			value = fields->value ?
719  			          (is_array ? g_strjoinv (" | ", (char **) fields->value) :
720  			                      (char *) fields->value) :
721  			          (char *) not_set_str;
722  		*dealloc = fields->value && is_array && !field_name;
723  		return value;
724  	}
725  	
726  	/*
727  	 * Print both headers or values of 'field_values' array.
728  	 * Entries to print and their order are specified via indices in
729  	 * 'nmc->print_fields.indices' array.
730  	 * Various flags influencing the output of fields are set up in the first item
731  	 * of 'field_values' array.
732  	 */
733  	void
734  	print_required_fields (NmCli *nmc, const NmcOutputField field_values[])
735  	{
736  		GString *str;
737  		int width1, width2;
738  		int table_width = 0;
739  		char *line = NULL;
740  		char *indent_str;
741  		const char *not_set_str = "--";
742  		int i;
743  		const NmcPrintFields fields = nmc->print_fields;
744  		gboolean multiline = nmc->multiline_output;
745  		gboolean terse = (nmc->print_output == NMC_PRINT_TERSE);
746  		gboolean pretty = (nmc->print_output == NMC_PRINT_PRETTY);
747  		gboolean escape = nmc->escape_values;
748  		gboolean main_header_add = field_values[0].flags & NMC_OF_FLAG_MAIN_HEADER_ADD;
749  		gboolean main_header_only = field_values[0].flags & NMC_OF_FLAG_MAIN_HEADER_ONLY;
750  		gboolean field_names = field_values[0].flags & NMC_OF_FLAG_FIELD_NAMES;
751  		gboolean section_prefix = field_values[0].flags & NMC_OF_FLAG_SECTION_PREFIX;
752  		gboolean main_header = main_header_add || main_header_only;
753  	
754  		/* No headers are printed in terse mode:
755  		 * - neither main header nor field (column) names
756  		 */
757  		if ((main_header_only || field_names) && terse)
758  			return;
759  	
760  		if (multiline) {
761  		/* --- Multiline mode --- */
762  			enum { ML_HEADER_WIDTH = 79 };
763  			enum { ML_VALUE_INDENT = 40 };
764  			if (main_header && pretty) {
765  				/* Print the main header */
766  				int header_width = nmc_string_screen_width (fields.header_name, NULL) + 4;
767  				table_width = header_width < ML_HEADER_WIDTH ? ML_HEADER_WIDTH : header_width;
768  	
769  				line = g_strnfill (ML_HEADER_WIDTH, '=');
770  				width1 = strlen (fields.header_name);
771  				width2 = nmc_string_screen_width (fields.header_name, NULL);
772  				printf ("%s\n", line);
773  				printf ("%*s\n", (table_width + width2)/2 + width1 - width2, fields.header_name);
774  				printf ("%s\n", line);
775  				g_free (line);
776  			}
777  	
778  			/* Print values */
779  			if (!main_header_only && !field_names) {
780  				for (i = 0; i < fields.indices->len; i++) {
781  					char *tmp;
782  					int idx = g_array_index (fields.indices, int, i);
783  					gboolean is_array = field_values[idx].value_is_array;
784  	
785  					/* section prefix can't be an array */
786  					g_assert (!is_array || !section_prefix || idx != 0);
787  	
788  					if (section_prefix && idx == 0)  /* The first field is section prefix */
789  						continue;
790  	
791  					if (is_array) {
792  						/* value is a null-terminated string array */
793  						const char **p;
794  						int j;
795  	
796  						for (p = (const char **) field_values[idx].value, j = 1; p && *p; p++, j++) {
797  							tmp = g_strdup_printf ("%s%s%s[%d]:",
798  							                       section_prefix ? (const char*) field_values[0].value : "",
799  							                       section_prefix ? "." : "",
800  							                       _(field_values[idx].name_l10n),
801  							                       j);
802  							width1 = strlen (tmp);
803  							width2 = nmc_string_screen_width (tmp, NULL);
804  							printf ("%-*s%s\n", terse ? 0 : ML_VALUE_INDENT+width1-width2, tmp,
805  							                    *p ? *p : not_set_str);
806  							g_free (tmp);
807  						}
808  					} else {
809  						/* value is a string */
810  						const char *hdr_name = (const char*) field_values[0].value;
811  						const char *val = (const char*) field_values[idx].value;
812  	
813  						tmp = g_strdup_printf ("%s%s%s:",
814  						                       section_prefix ? hdr_name : "",
815  						                       section_prefix ? "." : "",
816  						                       _(field_values[idx].name_l10n));
817  						width1 = strlen (tmp);
818  						width2 = nmc_string_screen_width (tmp, NULL);
819  						printf ("%-*s%s\n", terse ? 0 : ML_VALUE_INDENT+width1-width2, tmp,
820  						                    val ? val : not_set_str);
821  						g_free (tmp);
822  					}
823  				}
824  				if (pretty) {
825  					line = g_strnfill (ML_HEADER_WIDTH, '-');
826  					printf ("%s\n", line);
827  					g_free (line);
828  				}
829  			}
830  			return;
831  		}
832  	
833  		/* --- Tabular mode: each line = one object --- */
834  		str = g_string_new (NULL);
835  	
836  		for (i = 0; i < fields.indices->len; i++) {
837  			int idx = g_array_index (fields.indices, int, i);
838  			gboolean dealloc;
839  			char *value = get_value_to_print ((NmcOutputField *) field_values+idx, field_names, not_set_str, &dealloc);
840  	
841  			if (terse) {
842  				if (escape) {
843  					const char *p = value;
844  					while (*p) {
845  						if (*p == ':' || *p == '\\')
846  							g_string_append_c (str, '\\');  /* Escaping by '\' */
847  						g_string_append_c (str, *p);
848  						p++;
849  					}
850  				}
851  				else
852  					g_string_append_printf (str, "%s", value);
853  				g_string_append_c (str, ':');  /* Column separator */
854  			} else {
855  				width1 = strlen (value);
856  				width2 = nmc_string_screen_width (value, NULL);  /* Width of the string (in screen colums) */
857  				g_string_append_printf (str, "%-*s", field_values[idx].width + width1 - width2, strlen (value) > 0 ? value : "--");
858  				g_string_append_c (str, ' ');  /* Column separator */
859  				table_width += field_values[idx].width + width1 - width2 + 1;
860  			}
861  	
862  			if (dealloc)
863  				g_free (value);
864  		}
865  	
866  		/* Print the main table header */
867  		if (main_header && pretty) {
868  			int header_width = nmc_string_screen_width (fields.header_name, NULL) + 4;
869  			table_width = table_width < header_width ? header_width : table_width;
870  	
871  			line = g_strnfill (table_width, '=');
872  			width1 = strlen (fields.header_name);
873  			width2 = nmc_string_screen_width (fields.header_name, NULL);
874  			printf ("%s\n", line);
875  			printf ("%*s\n", (table_width + width2)/2 + width1 - width2, fields.header_name);
876  			printf ("%s\n", line);
877  			g_free (line);
878  		}
879  	
880  		/* Print actual values */
881  		if (!main_header_only && str->len > 0) {
882  			g_string_truncate (str, str->len-1);  /* Chop off last column separator */
883  			if (fields.indent > 0) {
884  				indent_str = g_strnfill (fields.indent, ' ');
885  				g_string_prepend (str, indent_str);
886  				g_free (indent_str);
887  			}
888  			printf ("%s\n", str->str);
889  		}
890  	
891  		/* Print horizontal separator */
892  		if (!main_header_only && field_names && pretty) {
893  			if (str->len > 0) {
894  				line = g_strnfill (table_width, '-');
895  				printf ("%s\n", line);
896  				g_free (line);
897  			}
898  		}
899  	
900  		g_string_free (str, TRUE);
901  	}
902  	
903  	/*
904  	 * Print nmc->output_data
905  	 *
906  	 * It first finds out maximal string length in columns and fill the value to
907  	 * 'width' member of NmcOutputField, so that columns in tabular output are
908  	 * properly aligned. Then each object (row in tabular) is printed using
909  	 * print_required_fields() function.
910  	 */
911  	void
912  	print_data (NmCli *nmc)
913  	{
914  		int i, j;
915  		size_t len;
916  		NmcOutputField *row;
917  		int num_fields = 0;
918  	
919  		if (!nmc->output_data || nmc->output_data->len < 1)
920  			return;
921  	
922  		/* How many fields? */
923  		row = g_ptr_array_index (nmc->output_data, 0);
924  		while (row->name) {
925  			num_fields++;
926  			row++;
927  		}
928  	
929  		/* Find out maximal string lengths */
930  		for (i = 0; i < num_fields; i++) {
931  			size_t max_width = 0;
932  			for (j = 0; j < nmc->output_data->len; j++) {
933  				gboolean field_names, dealloc;
934  				char *value;
935  				row = g_ptr_array_index (nmc->output_data, j);
936  				field_names = row[0].flags & NMC_OF_FLAG_FIELD_NAMES;
937  				value = get_value_to_print (row+i, field_names, "--", &dealloc);
938  				len = nmc_string_screen_width (value, NULL);
939  				max_width = len > max_width ? len : max_width;
940  				if (dealloc)
941  					g_free (value);
942  			}
943  			for (j = 0; j < nmc->output_data->len; j++) {
944  				row = g_ptr_array_index (nmc->output_data, j);
945  				row[i].width = max_width + 1;
946  			}
947  		}
948  	
949  		/* Now we can print the data. */
950  		for (i = 0; i < nmc->output_data->len; i++) {
951  			row = g_ptr_array_index (nmc->output_data, i);
952  			print_required_fields (nmc, row);
953  		}
954  	}
955  	
956  	/*
957  	* Compare versions of nmcli and NM daemon.
958  	* Return: TRUE  - the versions match (when only major and minor match, print a warning)
959  	*         FALSE - versions mismatch
960  	*/
961  	gboolean
962  	nmc_versions_match (NmCli *nmc)
963  	{
964  		const char *nm_ver = NULL;
965  		const char *dot;
966  		gboolean match = FALSE;
967  	
968  		g_return_val_if_fail (nmc != NULL, FALSE);
969  	
970  		/* --nocheck option - don't compare the versions */
971  		if (nmc->nocheck_ver)
972  			return TRUE;
973  	
974  		nmc->get_client (nmc);
975  		nm_ver = nm_client_get_version (nmc->client);
976  		if (nm_ver) {
977  			if (!strcmp (nm_ver, VERSION))
978  				match = TRUE;
979  			else {
980  				dot = strchr (nm_ver, '.');
981  				if (dot) {
982  					dot = strchr (dot + 1, '.');
983  					if (dot && !strncmp (nm_ver, VERSION, dot-nm_ver)) {
984  						fprintf(stderr,
985  						        _("Warning: nmcli (%s) and NetworkManager (%s) versions don't match. Use --nocheck to suppress the warning.\n"),
986  						        VERSION, nm_ver);
987  						match = TRUE;
988  					}
989  				}
990  			}
991  		}
992  	
993  		if (!match) {
994  			g_string_printf (nmc->return_text, _("Error: nmcli (%s) and NetworkManager (%s) versions don't match. Force execution using --nocheck, but the results are unpredictable."),
995  			                 VERSION, nm_ver ? nm_ver : _("unknown"));
996  			nmc->return_value = NMC_RESULT_ERROR_VERSIONS_MISMATCH;
997  		}
998  	
999  		return match;
1000 	}
1001 	
1002