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