2019-09-10 11:19:01 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2019-09-25 13:13:40 +02:00
|
|
|
/*
|
2019-10-01 09:20:35 +02:00
|
|
|
* Copyright (C) 2010 Lennart Poettering
|
|
|
|
|
* Copyright (C) 2010 - 2018 Red Hat, Inc.
|
2010-02-25 09:52:30 -08:00
|
|
|
*/
|
|
|
|
|
|
2016-02-19 14:57:48 +01:00
|
|
|
#include "nm-default.h"
|
2011-02-16 17:36:50 +01:00
|
|
|
|
2017-04-04 13:52:13 +02:00
|
|
|
#include "utils.h"
|
|
|
|
|
|
2010-02-25 09:52:30 -08:00
|
|
|
#include <stdio.h>
|
2013-01-16 12:28:37 +01:00
|
|
|
#include <stdlib.h>
|
2012-01-05 16:30:22 +01:00
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
#include <arpa/inet.h>
|
2017-10-30 13:13:02 +01:00
|
|
|
#include <sys/auxv.h>
|
2017-05-15 17:23:51 +02:00
|
|
|
#include <sys/prctl.h>
|
2012-01-05 16:30:22 +01:00
|
|
|
|
2017-03-28 12:16:31 +02:00
|
|
|
#include "nm-client-utils.h"
|
2017-04-05 15:48:53 +02:00
|
|
|
#include "nm-meta-setting-access.h"
|
2017-03-28 12:16:31 +02:00
|
|
|
|
2017-03-24 14:35:56 +01:00
|
|
|
#include "common.h"
|
2018-10-09 13:08:24 +02:00
|
|
|
#include "nmcli.h"
|
2017-03-24 17:32:04 +01:00
|
|
|
#include "settings.h"
|
2010-02-25 09:52:30 -08:00
|
|
|
|
2017-04-06 10:46:16 +02:00
|
|
|
#define ML_HEADER_WIDTH 79
|
|
|
|
|
#define ML_VALUE_INDENT 40
|
|
|
|
|
|
2017-04-04 13:52:13 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static const char *
|
2017-04-06 15:14:23 +02:00
|
|
|
_meta_type_nmc_generic_info_get_name (const NMMetaAbstractInfo *abstract_info, gboolean for_header)
|
2017-04-04 13:52:13 +02:00
|
|
|
{
|
2017-04-06 15:14:23 +02:00
|
|
|
const NmcMetaGenericInfo *info = (const NmcMetaGenericInfo *) abstract_info;
|
|
|
|
|
|
|
|
|
|
if (for_header)
|
|
|
|
|
return info->name_header ?: info->name;
|
|
|
|
|
return info->name;
|
2017-04-04 13:52:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const NMMetaAbstractInfo *const*
|
|
|
|
|
_meta_type_nmc_generic_info_get_nested (const NMMetaAbstractInfo *abstract_info,
|
|
|
|
|
guint *out_len,
|
|
|
|
|
gpointer *out_to_free)
|
|
|
|
|
{
|
|
|
|
|
const NmcMetaGenericInfo *info;
|
|
|
|
|
|
|
|
|
|
info = (const NmcMetaGenericInfo *) abstract_info;
|
|
|
|
|
|
2017-04-12 15:27:41 +02:00
|
|
|
NM_SET_OUT (out_len, NM_PTRARRAY_LEN (info->nested));
|
2017-04-04 13:52:13 +02:00
|
|
|
return (const NMMetaAbstractInfo *const*) info->nested;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-04 15:07:06 +02:00
|
|
|
static gconstpointer
|
2017-04-07 12:58:13 +02:00
|
|
|
_meta_type_nmc_generic_info_get_fcn (const NMMetaAbstractInfo *abstract_info,
|
|
|
|
|
const NMMetaEnvironment *environment,
|
2017-04-04 13:52:13 +02:00
|
|
|
gpointer environment_user_data,
|
|
|
|
|
gpointer target,
|
2018-04-30 13:01:20 +02:00
|
|
|
gpointer target_data,
|
2017-04-04 13:52:13 +02:00
|
|
|
NMMetaAccessorGetType get_type,
|
|
|
|
|
NMMetaAccessorGetFlags get_flags,
|
2017-04-06 15:14:23 +02:00
|
|
|
NMMetaAccessorGetOutFlags *out_flags,
|
2018-02-27 15:34:49 +01:00
|
|
|
gboolean *out_is_default,
|
2017-04-04 15:07:06 +02:00
|
|
|
gpointer *out_to_free)
|
2017-04-04 13:52:13 +02:00
|
|
|
{
|
|
|
|
|
const NmcMetaGenericInfo *info = (const NmcMetaGenericInfo *) abstract_info;
|
|
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
nm_assert (!out_to_free || !*out_to_free);
|
2017-04-06 15:14:23 +02:00
|
|
|
nm_assert (out_flags && !*out_flags);
|
2017-04-04 13:52:13 +02:00
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
if (!NM_IN_SET (get_type,
|
|
|
|
|
NM_META_ACCESSOR_GET_TYPE_PARSABLE,
|
|
|
|
|
NM_META_ACCESSOR_GET_TYPE_PRETTY,
|
2018-03-29 12:31:33 +02:00
|
|
|
NM_META_ACCESSOR_GET_TYPE_COLOR))
|
2017-04-04 15:23:39 +02:00
|
|
|
g_return_val_if_reached (NULL);
|
|
|
|
|
|
2018-03-29 12:31:33 +02:00
|
|
|
/* omitting the out_to_free value is only allowed for COLOR. */
|
|
|
|
|
nm_assert (out_to_free || NM_IN_SET (get_type, NM_META_ACCESSOR_GET_TYPE_COLOR));
|
2017-04-04 15:23:39 +02:00
|
|
|
|
2017-04-06 15:14:23 +02:00
|
|
|
if (info->get_fcn) {
|
2018-04-27 11:10:13 +02:00
|
|
|
return info->get_fcn (environment,
|
|
|
|
|
environment_user_data,
|
2018-04-30 13:01:20 +02:00
|
|
|
info,
|
|
|
|
|
target,
|
|
|
|
|
target_data,
|
2017-04-06 15:14:23 +02:00
|
|
|
get_type,
|
|
|
|
|
get_flags,
|
|
|
|
|
out_flags,
|
2018-02-27 15:34:49 +01:00
|
|
|
out_is_default,
|
2017-04-06 15:14:23 +02:00
|
|
|
out_to_free);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (info->nested) {
|
2018-03-29 12:31:33 +02:00
|
|
|
NMC_HANDLE_COLOR (NM_META_COLOR_NONE);
|
2017-04-20 10:22:52 +02:00
|
|
|
return info->name;
|
2017-04-06 15:14:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_return_val_if_reached (NULL);
|
2017-04-04 13:52:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const NMMetaType nmc_meta_type_generic_info = {
|
|
|
|
|
.type_name = "nmc-generic-info",
|
|
|
|
|
.get_name = _meta_type_nmc_generic_info_get_name,
|
|
|
|
|
.get_nested = _meta_type_nmc_generic_info_get_nested,
|
|
|
|
|
.get_fcn = _meta_type_nmc_generic_info_get_fcn,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2017-04-06 14:43:44 +02:00
|
|
|
static const char *
|
2018-03-29 12:25:14 +02:00
|
|
|
colorize_string (const NmcConfig *nmc_config,
|
2018-03-29 12:31:33 +02:00
|
|
|
NMMetaColor color,
|
2017-04-06 14:43:44 +02:00
|
|
|
const char *str,
|
|
|
|
|
char **out_to_free)
|
|
|
|
|
{
|
|
|
|
|
const char *out = str;
|
|
|
|
|
|
2018-03-29 12:31:33 +02:00
|
|
|
if (nmc_config && nmc_config->use_colors) {
|
|
|
|
|
*out_to_free = nmc_colorize (nmc_config, color, "%s", str);
|
2017-04-06 14:43:44 +02:00
|
|
|
out = *out_to_free;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2017-03-24 14:35:56 +01:00
|
|
|
static gboolean
|
|
|
|
|
parse_global_arg (NmCli *nmc, const char *arg)
|
|
|
|
|
{
|
|
|
|
|
if (nmc_arg_is_option (arg, "ask"))
|
|
|
|
|
nmc->ask = TRUE;
|
|
|
|
|
else if (nmc_arg_is_option (arg, "show-secrets"))
|
2017-04-06 15:26:23 +02:00
|
|
|
nmc->nmc_config_mutable.show_secrets = TRUE;
|
2017-03-24 14:35:56 +01:00
|
|
|
else
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
2017-03-30 16:09:46 +02:00
|
|
|
/**
|
|
|
|
|
* next_arg:
|
|
|
|
|
* @nmc: NmCli data
|
|
|
|
|
* @*argc: pointer to left number of arguments to parse
|
|
|
|
|
* @***argv: pointer to const char *array of arguments still to parse
|
|
|
|
|
* @...: a %NULL terminated list of cmd options to match (e.g., "--active")
|
|
|
|
|
*
|
|
|
|
|
* Takes care of autocompleting options when needed and performs
|
|
|
|
|
* match against passed options while moving forward the pointer
|
|
|
|
|
* to the remaining arguments.
|
|
|
|
|
*
|
|
|
|
|
* Returns: the number of the matched option if a match is found against
|
|
|
|
|
* one of the custom options passed; 0 if no custom option matched and still
|
|
|
|
|
* some args need to be processed or autocompletion has been performed;
|
|
|
|
|
* -1 otherwise (no more args).
|
|
|
|
|
*/
|
2010-02-25 09:52:30 -08:00
|
|
|
int
|
2017-03-30 16:09:46 +02:00
|
|
|
next_arg (NmCli *nmc, int *argc, char ***argv, ...)
|
2010-02-25 09:52:30 -08:00
|
|
|
{
|
2017-03-30 16:09:46 +02:00
|
|
|
va_list args;
|
|
|
|
|
const char *cmd_option;
|
|
|
|
|
|
|
|
|
|
g_assert (*argc >= 0);
|
2012-11-16 22:43:29 +01:00
|
|
|
|
2017-03-24 14:35:56 +01:00
|
|
|
do {
|
2017-03-30 16:09:46 +02:00
|
|
|
int cmd_option_pos = 1;
|
|
|
|
|
|
|
|
|
|
if (*argc > 0) {
|
2017-03-24 14:35:56 +01:00
|
|
|
(*argc)--;
|
|
|
|
|
(*argv)++;
|
|
|
|
|
}
|
2017-03-30 16:09:46 +02:00
|
|
|
if (*argc == 0)
|
2017-03-24 14:35:56 +01:00
|
|
|
return -1;
|
2017-03-30 16:09:46 +02:00
|
|
|
|
|
|
|
|
va_start (args, argv);
|
|
|
|
|
|
|
|
|
|
if (nmc && nmc->complete && *argc == 1) {
|
|
|
|
|
while ((cmd_option = va_arg (args, const char *)))
|
2019-04-12 15:40:01 +02:00
|
|
|
nmc_complete_strings (**argv, cmd_option);
|
2017-03-30 16:09:46 +02:00
|
|
|
|
|
|
|
|
if (***argv == '-')
|
2019-04-12 15:40:01 +02:00
|
|
|
nmc_complete_strings (**argv, "--ask", "--show-secrets");
|
2017-03-30 16:09:46 +02:00
|
|
|
|
|
|
|
|
va_end (args);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check command dependent options first */
|
|
|
|
|
while ((cmd_option = va_arg (args, const char *))) {
|
cli/utils: make next_arg() recognize arguments that are not in "--option" form
This is going to make it possible to parse and complete argument lists in one go:
-nmc_complete_strings (*argv, "ifname", "bssid", NULL);
-next_arg (nmc, &argc, &argv, NULL);
-if (strcmp (*argv, "ifname") == 0)
-...
-else if (strcmp (*argv, "bssid") == 0)
-...
+option = next_arg (nmc, &argc, &argv, "ifname", "bssid", NULL)
+switch (option) {
+case 1:
+...
+case 2:
+...
Beautiful.
2018-02-25 14:26:20 +01:00
|
|
|
if (cmd_option[0] == '-' && cmd_option[1] == '-') {
|
|
|
|
|
/* Match as an option (leading "--" stripped) */
|
|
|
|
|
if (nmc_arg_is_option (**argv, cmd_option + 2)) {
|
|
|
|
|
va_end (args);
|
|
|
|
|
return cmd_option_pos;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* Match literally. */
|
|
|
|
|
if (strcmp (**argv, cmd_option) == 0) {
|
|
|
|
|
va_end (args);
|
|
|
|
|
return cmd_option_pos;
|
|
|
|
|
}
|
2017-03-30 16:09:46 +02:00
|
|
|
}
|
|
|
|
|
cmd_option_pos++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
va_end (args);
|
|
|
|
|
|
2017-03-24 14:35:56 +01:00
|
|
|
} while (nmc && parse_global_arg (nmc, **argv));
|
2012-11-16 22:43:29 +01:00
|
|
|
|
2010-02-25 09:52:30 -08:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-18 11:25:49 +02:00
|
|
|
gboolean
|
|
|
|
|
nmc_arg_is_help (const char *arg)
|
|
|
|
|
{
|
2013-04-18 12:42:54 +02:00
|
|
|
if (!arg)
|
|
|
|
|
return FALSE;
|
cli: make match() return boolean
Coccinelle semantic patch:
@@
@@
-int
+gboolean
matches (...);
@@
expression pattern, cmd, len;
@@
-int
+gboolean
matches (...)
{
...
- return memcmp (pattern, cmd, len);
+ return memcmp (pattern, cmd, len) == 0;
}
@@
expression prefix, str;
@@
(
-matches (prefix, str) != 0
+!matches (prefix, str)
|
-matches (prefix, str) == 0
+matches (prefix, str)
)
@@
expression prefix, str;
@@
-(matches (prefix, str))
+matches (prefix, str)
@@
expression prefix, str;
@@
-(!matches (prefix, str))
+!matches (prefix, str)
spatch --smpl-spacing --sp-file match.cocci --dir clients/cli/ \
--include-headers --macro-file shared/nm-utils/gsystem-local-alloc.h
2017-02-15 12:20:55 +01:00
|
|
|
if ( matches (arg, "help")
|
|
|
|
|
|| (g_str_has_prefix (arg, "-") && matches (arg + 1, "help"))
|
|
|
|
|
|| (g_str_has_prefix (arg, "--") && matches (arg + 2, "help"))) {
|
2013-04-18 11:25:49 +02:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-07 14:47:12 +01:00
|
|
|
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;
|
|
|
|
|
|
cli: make match() return boolean
Coccinelle semantic patch:
@@
@@
-int
+gboolean
matches (...);
@@
expression pattern, cmd, len;
@@
-int
+gboolean
matches (...)
{
...
- return memcmp (pattern, cmd, len);
+ return memcmp (pattern, cmd, len) == 0;
}
@@
expression prefix, str;
@@
(
-matches (prefix, str) != 0
+!matches (prefix, str)
|
-matches (prefix, str) == 0
+matches (prefix, str)
)
@@
expression prefix, str;
@@
-(matches (prefix, str))
+matches (prefix, str)
@@
expression prefix, str;
@@
-(!matches (prefix, str))
+!matches (prefix, str)
spatch --smpl-spacing --sp-file match.cocci --dir clients/cli/ \
--include-headers --macro-file shared/nm-utils/gsystem-local-alloc.h
2017-02-15 12:20:55 +01:00
|
|
|
return (*p ? matches (p, opt_name) : FALSE);
|
2013-11-07 14:47:12 +01:00
|
|
|
}
|
|
|
|
|
|
2013-01-16 12:26:45 +01:00
|
|
|
/*
|
|
|
|
|
* Helper function to parse command-line arguments.
|
|
|
|
|
* arg_arr: description of arguments to look for
|
|
|
|
|
* last: whether these are last expected arguments
|
2015-10-22 16:34:58 +02:00
|
|
|
* argc: command-line argument array size
|
|
|
|
|
* argv: command-line argument array
|
2013-01-16 12:26:45 +01:00
|
|
|
* 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) {
|
2017-03-29 11:11:05 +02:00
|
|
|
(*argc)--;
|
|
|
|
|
(*argv)++;
|
|
|
|
|
if (!*argc) {
|
2013-01-16 12:26:45 +01:00
|
|
|
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;
|
|
|
|
|
|
2013-10-31 14:13:33 +01:00
|
|
|
if (p->name)
|
2013-01-16 12:26:45 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-30 16:09:46 +02:00
|
|
|
next_arg (NULL, argc, argv, NULL);
|
2013-01-16 12:26:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-24 23:28:00 +01:00
|
|
|
/*
|
2013-05-16 16:26:15 +02:00
|
|
|
* Convert SSID to a hex string representation.
|
2010-03-24 23:28:00 +01:00
|
|
|
* Caller has to free the returned string using g_free()
|
|
|
|
|
*/
|
|
|
|
|
char *
|
2013-05-16 16:26:15 +02:00
|
|
|
ssid_to_hex (const char *str, gsize len)
|
2010-03-24 23:28:00 +01:00
|
|
|
{
|
2020-03-09 16:58:20 +01:00
|
|
|
if (len == 0)
|
2012-04-28 22:54:02 +02:00
|
|
|
return NULL;
|
2010-03-24 23:28:00 +01:00
|
|
|
|
2020-03-09 16:58:20 +01:00
|
|
|
return nm_utils_bin2hexstr_full (str,
|
|
|
|
|
len,
|
|
|
|
|
'\0',
|
|
|
|
|
TRUE,
|
|
|
|
|
NULL);
|
2010-03-24 23:28:00 +01:00
|
|
|
}
|
|
|
|
|
|
2012-04-28 22:32:21 +01:00
|
|
|
/*
|
|
|
|
|
* Erase terminal line using ANSI escape sequences.
|
|
|
|
|
* It prints <ESC>[2K sequence to erase the line and then \r to return back
|
|
|
|
|
* to the beginning of the line.
|
|
|
|
|
*
|
|
|
|
|
* http://www.termsys.demon.co.uk/vtansi.htm
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
nmc_terminal_erase_line (void)
|
|
|
|
|
{
|
2014-09-19 16:04:40 -04:00
|
|
|
/* We intentionally use printf(), not g_print() here, to ensure that
|
|
|
|
|
* GLib doesn't mistakenly try to convert the string.
|
|
|
|
|
*/
|
2012-04-28 22:32:21 +01:00
|
|
|
printf ("\33[2K\r");
|
|
|
|
|
fflush (stdout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Print animated progress for an operation.
|
|
|
|
|
* Repeated calls of the function will show rotating slash in terminal followed
|
|
|
|
|
* by the string passed in 'str' argument.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
nmc_terminal_show_progress (const char *str)
|
|
|
|
|
{
|
|
|
|
|
static int idx = 0;
|
|
|
|
|
const char slashes[4] = {'|', '/', '-', '\\'};
|
|
|
|
|
|
|
|
|
|
nmc_terminal_erase_line ();
|
2018-04-24 11:20:03 +02:00
|
|
|
g_print ("%c %s", slashes[idx++], str ?: "");
|
2012-04-28 22:32:21 +01:00
|
|
|
fflush (stdout);
|
|
|
|
|
if (idx == 4)
|
|
|
|
|
idx = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-05 23:42:56 +02:00
|
|
|
char *
|
2018-03-29 12:31:33 +02:00
|
|
|
nmc_colorize (const NmcConfig *nmc_config, NMMetaColor color, const char *fmt, ...)
|
2013-04-05 23:42:56 +02:00
|
|
|
{
|
|
|
|
|
va_list args;
|
2014-06-04 08:55:35 +02:00
|
|
|
char *str, *colored;
|
2018-03-29 12:31:33 +02:00
|
|
|
const char *ansi_seq = NULL;
|
2013-04-05 23:42:56 +02:00
|
|
|
|
|
|
|
|
va_start (args, fmt);
|
|
|
|
|
str = g_strdup_vprintf (fmt, args);
|
|
|
|
|
va_end (args);
|
|
|
|
|
|
2018-03-29 12:31:33 +02:00
|
|
|
if (nmc_config->use_colors)
|
|
|
|
|
ansi_seq = nmc_config->palette[color];
|
2015-03-26 17:51:13 +01:00
|
|
|
|
2018-03-29 12:31:33 +02:00
|
|
|
if (ansi_seq == NULL)
|
|
|
|
|
return str;
|
2013-04-05 23:42:56 +02:00
|
|
|
|
2018-03-29 12:31:33 +02:00
|
|
|
colored = g_strdup_printf ("\33[%sm%s\33[0m", ansi_seq, str);
|
2014-06-04 08:55:35 +02:00
|
|
|
g_free (str);
|
|
|
|
|
return colored;
|
2013-04-05 23:42:56 +02:00
|
|
|
}
|
|
|
|
|
|
2015-02-13 22:05:55 +01:00
|
|
|
/*
|
|
|
|
|
* Count characters belonging to terminal color escape sequences.
|
|
|
|
|
* @start points to beginning of the string, @end points to the end,
|
|
|
|
|
* or NULL if the string is nul-terminated.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
nmc_count_color_escape_chars (const char *start, const char *end)
|
|
|
|
|
{
|
|
|
|
|
int num = 0;
|
|
|
|
|
gboolean inside = FALSE;
|
|
|
|
|
|
|
|
|
|
if (end == NULL)
|
|
|
|
|
end = start + strlen (start);
|
|
|
|
|
|
|
|
|
|
while (start < end) {
|
|
|
|
|
if (*start == '\33' && *(start+1) == '[')
|
|
|
|
|
inside = TRUE;
|
|
|
|
|
if (inside)
|
|
|
|
|
num++;
|
2018-02-02 11:34:55 +01:00
|
|
|
if (*start == 'm')
|
2015-02-13 22:05:55 +01:00
|
|
|
inside = FALSE;
|
|
|
|
|
start++;
|
|
|
|
|
}
|
|
|
|
|
return num;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-13 13:25:16 +01:00
|
|
|
/* Filter out possible ANSI color escape sequences */
|
|
|
|
|
/* It directly modifies the passed string @str. */
|
|
|
|
|
void
|
|
|
|
|
nmc_filter_out_colors_inplace (char *str)
|
|
|
|
|
{
|
|
|
|
|
const char *p1;
|
|
|
|
|
char *p2;
|
|
|
|
|
gboolean copy_char = TRUE;
|
|
|
|
|
|
|
|
|
|
if (!str)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
p1 = p2 = str;
|
|
|
|
|
while (*p1) {
|
|
|
|
|
if (*p1 == '\33' && *(p1+1) == '[')
|
|
|
|
|
copy_char = FALSE;
|
|
|
|
|
if (copy_char)
|
|
|
|
|
*p2++ = *p1;
|
|
|
|
|
if (!copy_char && *p1 == 'm')
|
|
|
|
|
copy_char = TRUE;
|
|
|
|
|
p1++;
|
|
|
|
|
}
|
|
|
|
|
*p2 = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Filter out possible ANSI color escape sequences */
|
|
|
|
|
char *
|
|
|
|
|
nmc_filter_out_colors (const char *str)
|
|
|
|
|
{
|
|
|
|
|
char *filtered;
|
|
|
|
|
|
|
|
|
|
if (!str)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
filtered = g_strdup (str);
|
|
|
|
|
nmc_filter_out_colors_inplace (filtered);
|
|
|
|
|
return filtered;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-21 16:53:23 +01:00
|
|
|
/*
|
|
|
|
|
* 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;
|
2013-04-15 13:40:14 +02:00
|
|
|
ssize_t num;
|
2012-11-21 16:53:23 +01:00
|
|
|
|
2014-09-19 16:04:40 -04:00
|
|
|
g_print ("%s", ask_str);
|
2013-04-15 13:40:14 +02:00
|
|
|
num = getline (&line, &line_ln, stdin);
|
2012-11-21 16:53:23 +01:00
|
|
|
|
|
|
|
|
/* Remove newline from the string */
|
2013-04-15 13:40:14 +02:00
|
|
|
if (num < 1 || (num == 1 && line[0] == '\n')) {
|
2012-11-21 16:53:23 +01:00
|
|
|
g_free (line);
|
|
|
|
|
line = NULL;
|
|
|
|
|
} else {
|
2013-04-15 13:40:14 +02:00
|
|
|
if (line[num-1] == '\n')
|
|
|
|
|
line[num-1] = '\0';
|
2012-11-21 16:53:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return line;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-22 11:56:06 +01:00
|
|
|
/*
|
|
|
|
|
* Split string in 'line' according to 'delim' to (argument) array.
|
|
|
|
|
*/
|
|
|
|
|
int
|
2015-03-12 14:11:18 +01:00
|
|
|
nmc_string_to_arg_array (const char *line, const char *delim, gboolean unquote,
|
|
|
|
|
char ***argv, int *argc)
|
2012-11-22 11:56:06 +01:00
|
|
|
{
|
2017-12-08 10:44:36 +01:00
|
|
|
gs_free const char **arr0 = NULL;
|
2012-11-22 11:56:06 +01:00
|
|
|
char **arr;
|
|
|
|
|
|
2019-04-03 15:44:19 +02:00
|
|
|
arr0 = nm_utils_strsplit_set (line ?: "",
|
|
|
|
|
delim ?: " \t");
|
2017-12-08 10:44:36 +01:00
|
|
|
if (!arr0)
|
|
|
|
|
arr = g_new0 (char *, 1);
|
|
|
|
|
else
|
|
|
|
|
arr = g_strdupv ((char **) arr0);
|
2015-03-12 14:11:18 +01:00
|
|
|
|
|
|
|
|
if (unquote) {
|
|
|
|
|
int i = 0;
|
|
|
|
|
char *s;
|
|
|
|
|
size_t l;
|
|
|
|
|
const char *quotes = "\"'";
|
|
|
|
|
|
2017-12-08 10:44:36 +01:00
|
|
|
while (arr[i]) {
|
2015-03-12 14:11:18 +01:00
|
|
|
s = arr[i];
|
|
|
|
|
l = strlen (s);
|
|
|
|
|
if (l >= 2) {
|
|
|
|
|
if (strchr (quotes, s[0]) && s[l-1] == s[0]) {
|
|
|
|
|
memmove (s, s+1, l-2);
|
|
|
|
|
s[l-2] = '\0';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-11-22 11:56:06 +01:00
|
|
|
|
|
|
|
|
*argv = arr;
|
2015-03-12 14:11:18 +01:00
|
|
|
*argc = g_strv_length (arr);
|
2012-11-22 11:56:06 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-04 17:07:42 +02:00
|
|
|
/*
|
|
|
|
|
* Convert string array (char **) to description string in the form of:
|
|
|
|
|
* "[string1, string2, ]"
|
|
|
|
|
*
|
|
|
|
|
* Returns: a newly allocated string. Caller must free it with g_free().
|
|
|
|
|
*/
|
|
|
|
|
char *
|
2017-03-25 13:20:11 +01:00
|
|
|
nmc_util_strv_for_display (const char *const*strv, gboolean brackets)
|
2014-10-04 17:07:42 +02:00
|
|
|
{
|
|
|
|
|
GString *result;
|
|
|
|
|
guint i = 0;
|
|
|
|
|
|
|
|
|
|
result = g_string_sized_new (150);
|
2015-04-22 11:46:55 +02:00
|
|
|
if (brackets)
|
|
|
|
|
g_string_append_c (result, '[');
|
2014-10-04 17:07:42 +02:00
|
|
|
while (strv && strv[i]) {
|
|
|
|
|
if (result->len > 1)
|
|
|
|
|
g_string_append (result, ", ");
|
|
|
|
|
g_string_append (result, strv[i]);
|
|
|
|
|
i++;
|
|
|
|
|
}
|
2015-04-22 11:46:55 +02:00
|
|
|
if (brackets)
|
|
|
|
|
g_string_append_c (result, ']');
|
2014-10-04 17:07:42 +02:00
|
|
|
|
|
|
|
|
return g_string_free (result, FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-26 17:32:18 +02:00
|
|
|
/*
|
2015-02-13 22:05:55 +01:00
|
|
|
* Find out how many columns an UTF-8 string occupies on the screen.
|
2010-04-26 17:32:18 +02:00
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
nmc_string_screen_width (const char *start, const char *end)
|
|
|
|
|
{
|
|
|
|
|
int width = 0;
|
2015-02-13 22:05:55 +01:00
|
|
|
const char *p = start;
|
2010-04-26 17:32:18 +02:00
|
|
|
|
|
|
|
|
if (end == NULL)
|
|
|
|
|
end = start + strlen (start);
|
|
|
|
|
|
2015-02-13 22:05:55 +01:00
|
|
|
while (p < end) {
|
|
|
|
|
width += g_unichar_iswide (g_utf8_get_char (p)) ? 2 : g_unichar_iszerowidth (g_utf8_get_char (p)) ? 0 : 1;
|
|
|
|
|
p = g_utf8_next_char (p);
|
2010-04-26 17:32:18 +02:00
|
|
|
}
|
2015-02-13 22:05:55 +01:00
|
|
|
|
|
|
|
|
/* Subtract color escape sequences as they don't occupy space. */
|
|
|
|
|
return width - nmc_count_color_escape_chars (start, NULL);
|
2010-04-26 17:32:18 +02:00
|
|
|
}
|
|
|
|
|
|
2012-01-03 15:07:17 +01:00
|
|
|
void
|
2013-03-05 15:50:11 +01:00
|
|
|
set_val_str (NmcOutputField fields_array[], guint32 idx, char *value)
|
2012-01-03 15:07:17 +01:00
|
|
|
{
|
|
|
|
|
fields_array[idx].value = value;
|
2013-05-22 08:36:09 +02:00
|
|
|
fields_array[idx].value_is_array = FALSE;
|
|
|
|
|
fields_array[idx].free_value = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
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;
|
2012-01-03 15:07:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2013-03-05 15:50:11 +01:00
|
|
|
set_val_arr (NmcOutputField fields_array[], guint32 idx, char **value)
|
2012-01-03 15:07:17 +01:00
|
|
|
{
|
|
|
|
|
fields_array[idx].value = value;
|
2013-05-22 08:36:09 +02:00
|
|
|
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;
|
2012-01-03 15:07:17 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-13 08:54:32 +01:00
|
|
|
void
|
2018-03-29 12:31:33 +02:00
|
|
|
set_val_color_all (NmcOutputField fields_array[], NMMetaColor color)
|
2015-02-13 08:54:32 +01:00
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
cli: split tracking of meta data out of NmcOutputField
When generating output data, nmcli iterates over a list of
property-descriptors (nmc_fields_ip4_config), creates an intermediate
array (output_data) and finally prints it.
However, previously both the meta data (nmc_fields_ip4_config) and
the intermediate format use the same type NmcOutputField. This means,
certain fields are relevant to describe a property, and other fields
are output/formatting fields.
Split this up. Now, the meta data is tracked in form of an NMMetaAbstractInfo
lists. This separates the information about properties from intermediate steps
during creation of the output.
Note that currently functions like print_ip4_config() still have all the
knowledge about how to generate the output. That is wrong, instead, the
meta data (NMMetaAbstractInfo) should describe how to create the output
and then all those functions could be replaced. This means, later we want
to add more knowledge to the NMMetaAbstractInfo, so it is important to
keep them separate from NmcOutputField.
2017-03-31 19:18:16 +02:00
|
|
|
for (i = 0; fields_array[i].info; i++) {
|
2015-02-13 08:54:32 +01:00
|
|
|
fields_array[i].color = color;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-25 10:44:15 -04:00
|
|
|
/*
|
|
|
|
|
* Free 'value' members in array of NmcOutputField
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
nmc_free_output_field_values (NmcOutputField fields_array[])
|
|
|
|
|
{
|
2013-05-22 08:36:09 +02:00
|
|
|
NmcOutputField *iter = fields_array;
|
|
|
|
|
|
cli: split tracking of meta data out of NmcOutputField
When generating output data, nmcli iterates over a list of
property-descriptors (nmc_fields_ip4_config), creates an intermediate
array (output_data) and finally prints it.
However, previously both the meta data (nmc_fields_ip4_config) and
the intermediate format use the same type NmcOutputField. This means,
certain fields are relevant to describe a property, and other fields
are output/formatting fields.
Split this up. Now, the meta data is tracked in form of an NMMetaAbstractInfo
lists. This separates the information about properties from intermediate steps
during creation of the output.
Note that currently functions like print_ip4_config() still have all the
knowledge about how to generate the output. That is wrong, instead, the
meta data (NMMetaAbstractInfo) should describe how to create the output
and then all those functions could be replaced. This means, later we want
to add more knowledge to the NMMetaAbstractInfo, so it is important to
keep them separate from NmcOutputField.
2017-03-31 19:18:16 +02:00
|
|
|
while (iter && iter->info) {
|
2013-05-22 08:36:09 +02:00
|
|
|
if (iter->free_value) {
|
|
|
|
|
if (iter->value_is_array)
|
|
|
|
|
g_strfreev ((char **) iter->value);
|
|
|
|
|
else
|
|
|
|
|
g_free ((char *) iter->value);
|
|
|
|
|
iter->value = NULL;
|
|
|
|
|
}
|
|
|
|
|
iter++;
|
2013-03-25 10:44:15 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#define PRINT_DATA_COL_PARENT_NIL (G_MAXUINT)
|
2017-04-03 14:04:28 +02:00
|
|
|
|
2018-05-02 16:36:28 +02:00
|
|
|
typedef struct _PrintDataCol {
|
|
|
|
|
union {
|
|
|
|
|
const struct _PrintDataCol *parent_col;
|
|
|
|
|
|
|
|
|
|
/* while constructing the list of columns in _output_selection_append(), we keep track
|
|
|
|
|
* of the parent by index. The reason is, that at that point our columns are still
|
|
|
|
|
* tracked in a GArray which is growing (hence, the pointers are changing).
|
|
|
|
|
* Later, _output_selection_complete() converts the index into the actual pointer.
|
|
|
|
|
*/
|
|
|
|
|
guint _parent_idx;
|
|
|
|
|
};
|
2017-04-12 15:14:23 +02:00
|
|
|
const NMMetaSelectionItem *selection_item;
|
2017-04-04 15:23:39 +02:00
|
|
|
guint self_idx;
|
|
|
|
|
bool is_leaf;
|
|
|
|
|
} PrintDataCol;
|
2017-04-03 14:04:28 +02:00
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
static gboolean
|
|
|
|
|
_output_selection_append (GArray *cols,
|
|
|
|
|
guint parent_idx,
|
2017-04-12 15:14:23 +02:00
|
|
|
const NMMetaSelectionItem *selection_item,
|
2017-04-04 15:23:39 +02:00
|
|
|
GPtrArray *gfree_keeper,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
gs_free gpointer nested_to_free = NULL;
|
|
|
|
|
guint col_idx;
|
|
|
|
|
guint i;
|
|
|
|
|
const NMMetaAbstractInfo *const*nested;
|
2017-04-12 15:14:23 +02:00
|
|
|
NMMetaSelectionResultList *selection;
|
2017-04-04 15:23:39 +02:00
|
|
|
|
|
|
|
|
col_idx = cols->len;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
PrintDataCol col = {
|
|
|
|
|
.selection_item = selection_item,
|
2018-05-02 16:36:28 +02:00
|
|
|
._parent_idx = parent_idx,
|
2017-04-04 15:23:39 +02:00
|
|
|
.self_idx = col_idx,
|
|
|
|
|
.is_leaf = TRUE,
|
|
|
|
|
};
|
|
|
|
|
g_array_append_val (cols, col);
|
|
|
|
|
}
|
2017-04-03 14:04:28 +02:00
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
nested = nm_meta_abstract_info_get_nested (selection_item->info, NULL, &nested_to_free);
|
2017-04-03 14:04:28 +02:00
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
if (selection_item->sub_selection) {
|
|
|
|
|
if (!nested) {
|
|
|
|
|
gs_free char *allowed_fields = NULL;
|
2017-04-03 14:04:28 +02:00
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
if (parent_idx != PRINT_DATA_COL_PARENT_NIL) {
|
2020-03-13 12:17:07 +01:00
|
|
|
const NMMetaSelectionItem *si;
|
|
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
si = g_array_index (cols, PrintDataCol, parent_idx).selection_item;
|
2017-04-12 15:14:23 +02:00
|
|
|
allowed_fields = nm_meta_abstract_info_get_nested_names_str (si->info, si->self_selection);
|
2017-04-03 14:04:28 +02:00
|
|
|
}
|
2017-04-04 15:23:39 +02:00
|
|
|
if (!allowed_fields) {
|
|
|
|
|
g_set_error (error, NMCLI_ERROR, 1, _("invalid field '%s%s%s'; no such field"),
|
|
|
|
|
selection_item->self_selection ?: "", selection_item->self_selection ? "." : "",
|
|
|
|
|
selection_item->sub_selection);
|
|
|
|
|
} else {
|
|
|
|
|
g_set_error (error, NMCLI_ERROR, 1, _("invalid field '%s%s%s'; allowed fields: [%s]"),
|
|
|
|
|
selection_item->self_selection ?: "", selection_item->self_selection ? "." : "",
|
|
|
|
|
selection_item->sub_selection,
|
|
|
|
|
allowed_fields);
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2017-04-03 14:04:28 +02:00
|
|
|
|
2017-04-12 15:14:23 +02:00
|
|
|
selection = nm_meta_selection_create_parse_one (nested, selection_item->self_selection,
|
|
|
|
|
selection_item->sub_selection, FALSE, error);
|
2017-04-04 15:23:39 +02:00
|
|
|
if (!selection)
|
|
|
|
|
return FALSE;
|
|
|
|
|
nm_assert (selection->num == 1);
|
|
|
|
|
} else if (nested) {
|
2017-04-12 15:14:23 +02:00
|
|
|
selection = nm_meta_selection_create_all (nested);
|
2017-04-04 15:23:39 +02:00
|
|
|
nm_assert (selection && selection->num > 0);
|
|
|
|
|
} else
|
|
|
|
|
selection = NULL;
|
2017-04-03 14:04:28 +02:00
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
if (selection) {
|
|
|
|
|
g_ptr_array_add (gfree_keeper, selection);
|
2017-04-03 14:04:28 +02:00
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
for (i = 0; i < selection->num; i++) {
|
2018-05-02 16:36:28 +02:00
|
|
|
if (!_output_selection_append (cols,
|
|
|
|
|
col_idx,
|
2020-03-13 12:17:07 +01:00
|
|
|
&selection->items[i],
|
2018-05-02 16:36:28 +02:00
|
|
|
gfree_keeper,
|
|
|
|
|
error))
|
2017-04-04 15:23:39 +02:00
|
|
|
return FALSE;
|
2017-04-03 14:04:28 +02:00
|
|
|
}
|
2017-04-06 15:14:23 +02:00
|
|
|
|
2017-04-06 15:14:23 +02:00
|
|
|
if (!NM_IN_SET(selection_item->info->meta_type,
|
|
|
|
|
&nm_meta_type_setting_info_editor,
|
|
|
|
|
&nmc_meta_type_generic_info))
|
2017-04-06 15:14:23 +02:00
|
|
|
g_array_index (cols, PrintDataCol, col_idx).is_leaf = FALSE;
|
2017-04-03 14:04:28 +02:00
|
|
|
}
|
|
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
2017-04-03 14:04:28 +02:00
|
|
|
|
2018-05-02 16:36:28 +02:00
|
|
|
static void
|
|
|
|
|
_output_selection_complete (GArray *cols)
|
|
|
|
|
{
|
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
|
|
nm_assert (cols);
|
|
|
|
|
nm_assert (g_array_get_element_size (cols) == sizeof (PrintDataCol));
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < cols->len; i++) {
|
|
|
|
|
PrintDataCol *col = &g_array_index (cols, PrintDataCol, i);
|
|
|
|
|
|
|
|
|
|
if (col->_parent_idx == PRINT_DATA_COL_PARENT_NIL)
|
|
|
|
|
col->parent_col = NULL;
|
|
|
|
|
else {
|
|
|
|
|
nm_assert (col->_parent_idx < i);
|
|
|
|
|
col->parent_col = &g_array_index (cols, PrintDataCol, col->_parent_idx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
/*****************************************************************************/
|
2017-04-03 14:04:28 +02:00
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
/**
|
|
|
|
|
* _output_selection_parse:
|
|
|
|
|
* @fields: a %NULL terminated array of meta-data fields
|
|
|
|
|
* @fields_str: a comma separated selector for fields. Nested fields
|
|
|
|
|
* can be specified using '.' notation.
|
|
|
|
|
* @out_cols: (transfer full): the result, parsed as an GArray of PrintDataCol items.
|
|
|
|
|
* The order of the items is as specified by @fields_str. Meta data
|
|
|
|
|
* items that contain nested elements are unpacked (note the is_leaf
|
|
|
|
|
* and parent properties of PrintDataCol).
|
|
|
|
|
* @out_gfree_keeper: (transfer full): an output GPtrArray that owns
|
|
|
|
|
* strings to which @out_cols points to. The lifetime of @out_cols
|
|
|
|
|
* and @out_gfree_keeper should correspond.
|
|
|
|
|
* @error:
|
|
|
|
|
*
|
|
|
|
|
* Returns: %TRUE on success.
|
|
|
|
|
*/
|
|
|
|
|
static gboolean
|
|
|
|
|
_output_selection_parse (const NMMetaAbstractInfo *const*fields,
|
|
|
|
|
const char *fields_str,
|
2020-03-12 14:39:27 +01:00
|
|
|
PrintDataCol **out_cols_data,
|
|
|
|
|
guint *out_cols_len,
|
2017-04-04 15:23:39 +02:00
|
|
|
GPtrArray **out_gfree_keeper,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
2017-04-12 15:14:23 +02:00
|
|
|
NMMetaSelectionResultList *selection;
|
2017-04-04 15:23:39 +02:00
|
|
|
gs_unref_ptrarray GPtrArray *gfree_keeper = NULL;
|
|
|
|
|
gs_unref_array GArray *cols = NULL;
|
|
|
|
|
guint i;
|
|
|
|
|
|
2018-06-08 17:02:10 +02:00
|
|
|
selection = nm_meta_selection_create_parse_list (fields, fields_str, FALSE, error);
|
2017-04-04 15:23:39 +02:00
|
|
|
if (!selection)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (!selection->num) {
|
|
|
|
|
g_set_error (error, NMCLI_ERROR, 1, _("failure to select field"));
|
2017-10-30 11:17:48 +01:00
|
|
|
g_free (selection);
|
2017-04-04 15:23:39 +02:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gfree_keeper = g_ptr_array_new_with_free_func (g_free);
|
|
|
|
|
g_ptr_array_add (gfree_keeper, selection);
|
|
|
|
|
|
|
|
|
|
cols = g_array_new (FALSE, TRUE, sizeof (PrintDataCol));
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < selection->num; i++) {
|
2020-03-13 12:17:07 +01:00
|
|
|
if (!_output_selection_append (cols,
|
|
|
|
|
PRINT_DATA_COL_PARENT_NIL,
|
|
|
|
|
&selection->items[i],
|
|
|
|
|
gfree_keeper,
|
|
|
|
|
error))
|
2017-04-04 15:23:39 +02:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-02 16:36:28 +02:00
|
|
|
_output_selection_complete (cols);
|
|
|
|
|
|
2020-03-12 14:39:27 +01:00
|
|
|
*out_cols_len = cols->len;
|
|
|
|
|
*out_cols_data = (PrintDataCol *) g_array_free (g_steal_pointer (&cols), FALSE);
|
2017-04-04 15:23:39 +02:00
|
|
|
*out_gfree_keeper = g_steal_pointer (&gfree_keeper);
|
|
|
|
|
return TRUE;
|
2017-04-03 14:04:28 +02:00
|
|
|
}
|
|
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
/*****************************************************************************/
|
2017-04-03 14:04:28 +02:00
|
|
|
|
2013-12-10 12:00:53 +01:00
|
|
|
/**
|
|
|
|
|
* 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.
|
2010-03-18 15:39:15 +01:00
|
|
|
*/
|
|
|
|
|
GArray *
|
2013-12-10 12:00:53 +01:00
|
|
|
parse_output_fields (const char *fields_str,
|
cli: split tracking of meta data out of NmcOutputField
When generating output data, nmcli iterates over a list of
property-descriptors (nmc_fields_ip4_config), creates an intermediate
array (output_data) and finally prints it.
However, previously both the meta data (nmc_fields_ip4_config) and
the intermediate format use the same type NmcOutputField. This means,
certain fields are relevant to describe a property, and other fields
are output/formatting fields.
Split this up. Now, the meta data is tracked in form of an NMMetaAbstractInfo
lists. This separates the information about properties from intermediate steps
during creation of the output.
Note that currently functions like print_ip4_config() still have all the
knowledge about how to generate the output. That is wrong, instead, the
meta data (NMMetaAbstractInfo) should describe how to create the output
and then all those functions could be replaced. This means, later we want
to add more knowledge to the NMMetaAbstractInfo, so it is important to
keep them separate from NmcOutputField.
2017-03-31 19:18:16 +02:00
|
|
|
const NMMetaAbstractInfo *const*fields_array,
|
2013-12-10 12:00:53 +01:00
|
|
|
gboolean parse_groups,
|
cli: split tracking of meta data out of NmcOutputField
When generating output data, nmcli iterates over a list of
property-descriptors (nmc_fields_ip4_config), creates an intermediate
array (output_data) and finally prints it.
However, previously both the meta data (nmc_fields_ip4_config) and
the intermediate format use the same type NmcOutputField. This means,
certain fields are relevant to describe a property, and other fields
are output/formatting fields.
Split this up. Now, the meta data is tracked in form of an NMMetaAbstractInfo
lists. This separates the information about properties from intermediate steps
during creation of the output.
Note that currently functions like print_ip4_config() still have all the
knowledge about how to generate the output. That is wrong, instead, the
meta data (NMMetaAbstractInfo) should describe how to create the output
and then all those functions could be replaced. This means, later we want
to add more knowledge to the NMMetaAbstractInfo, so it is important to
keep them separate from NmcOutputField.
2017-03-31 19:18:16 +02:00
|
|
|
GPtrArray **out_group_fields,
|
2013-12-10 12:00:53 +01:00
|
|
|
GError **error)
|
2010-03-18 15:39:15 +01:00
|
|
|
{
|
2017-04-12 15:14:23 +02:00
|
|
|
gs_free NMMetaSelectionResultList *selection = NULL;
|
2017-04-03 14:04:28 +02:00
|
|
|
GArray *array;
|
|
|
|
|
GPtrArray *group_fields = NULL;
|
|
|
|
|
guint i;
|
2010-03-18 15:39:15 +01:00
|
|
|
|
cli: split tracking of meta data out of NmcOutputField
When generating output data, nmcli iterates over a list of
property-descriptors (nmc_fields_ip4_config), creates an intermediate
array (output_data) and finally prints it.
However, previously both the meta data (nmc_fields_ip4_config) and
the intermediate format use the same type NmcOutputField. This means,
certain fields are relevant to describe a property, and other fields
are output/formatting fields.
Split this up. Now, the meta data is tracked in form of an NMMetaAbstractInfo
lists. This separates the information about properties from intermediate steps
during creation of the output.
Note that currently functions like print_ip4_config() still have all the
knowledge about how to generate the output. That is wrong, instead, the
meta data (NMMetaAbstractInfo) should describe how to create the output
and then all those functions could be replaced. This means, later we want
to add more knowledge to the NMMetaAbstractInfo, so it is important to
keep them separate from NmcOutputField.
2017-03-31 19:18:16 +02:00
|
|
|
g_return_val_if_fail (!error || !*error, NULL);
|
|
|
|
|
g_return_val_if_fail (!out_group_fields || !*out_group_fields, NULL);
|
2010-03-18 15:39:15 +01:00
|
|
|
|
2018-06-08 17:02:10 +02:00
|
|
|
selection = nm_meta_selection_create_parse_list (fields_array, fields_str, TRUE, error);
|
2017-04-03 14:04:28 +02:00
|
|
|
if (!selection)
|
|
|
|
|
return NULL;
|
cli: split tracking of meta data out of NmcOutputField
When generating output data, nmcli iterates over a list of
property-descriptors (nmc_fields_ip4_config), creates an intermediate
array (output_data) and finally prints it.
However, previously both the meta data (nmc_fields_ip4_config) and
the intermediate format use the same type NmcOutputField. This means,
certain fields are relevant to describe a property, and other fields
are output/formatting fields.
Split this up. Now, the meta data is tracked in form of an NMMetaAbstractInfo
lists. This separates the information about properties from intermediate steps
during creation of the output.
Note that currently functions like print_ip4_config() still have all the
knowledge about how to generate the output. That is wrong, instead, the
meta data (NMMetaAbstractInfo) should describe how to create the output
and then all those functions could be replaced. This means, later we want
to add more knowledge to the NMMetaAbstractInfo, so it is important to
keep them separate from NmcOutputField.
2017-03-31 19:18:16 +02:00
|
|
|
|
2017-04-03 14:04:28 +02:00
|
|
|
array = g_array_sized_new (FALSE, FALSE, sizeof (int), selection->num);
|
|
|
|
|
if (parse_groups && out_group_fields)
|
|
|
|
|
group_fields = g_ptr_array_new_full (selection->num, g_free);
|
2013-12-10 12:00:53 +01:00
|
|
|
|
2017-04-03 14:04:28 +02:00
|
|
|
for (i = 0; i < selection->num; i++) {
|
|
|
|
|
int idx = selection->items[i].idx;
|
2013-12-10 12:00:53 +01:00
|
|
|
|
2017-04-03 14:04:28 +02:00
|
|
|
g_array_append_val (array, idx);
|
|
|
|
|
if (group_fields)
|
|
|
|
|
g_ptr_array_add (group_fields, g_strdup (selection->items[i].sub_selection));
|
2010-03-18 15:39:15 +01:00
|
|
|
}
|
cli: split tracking of meta data out of NmcOutputField
When generating output data, nmcli iterates over a list of
property-descriptors (nmc_fields_ip4_config), creates an intermediate
array (output_data) and finally prints it.
However, previously both the meta data (nmc_fields_ip4_config) and
the intermediate format use the same type NmcOutputField. This means,
certain fields are relevant to describe a property, and other fields
are output/formatting fields.
Split this up. Now, the meta data is tracked in form of an NMMetaAbstractInfo
lists. This separates the information about properties from intermediate steps
during creation of the output.
Note that currently functions like print_ip4_config() still have all the
knowledge about how to generate the output. That is wrong, instead, the
meta data (NMMetaAbstractInfo) should describe how to create the output
and then all those functions could be replaced. This means, later we want
to add more knowledge to the NMMetaAbstractInfo, so it is important to
keep them separate from NmcOutputField.
2017-03-31 19:18:16 +02:00
|
|
|
|
2017-04-03 14:04:28 +02:00
|
|
|
if (group_fields)
|
|
|
|
|
*out_group_fields = group_fields;
|
|
|
|
|
return array;
|
2010-03-18 15:39:15 +01:00
|
|
|
}
|
|
|
|
|
|
2013-05-22 08:36:09 +02:00
|
|
|
NmcOutputField *
|
cli: split tracking of meta data out of NmcOutputField
When generating output data, nmcli iterates over a list of
property-descriptors (nmc_fields_ip4_config), creates an intermediate
array (output_data) and finally prints it.
However, previously both the meta data (nmc_fields_ip4_config) and
the intermediate format use the same type NmcOutputField. This means,
certain fields are relevant to describe a property, and other fields
are output/formatting fields.
Split this up. Now, the meta data is tracked in form of an NMMetaAbstractInfo
lists. This separates the information about properties from intermediate steps
during creation of the output.
Note that currently functions like print_ip4_config() still have all the
knowledge about how to generate the output. That is wrong, instead, the
meta data (NMMetaAbstractInfo) should describe how to create the output
and then all those functions could be replaced. This means, later we want
to add more knowledge to the NMMetaAbstractInfo, so it is important to
keep them separate from NmcOutputField.
2017-03-31 19:18:16 +02:00
|
|
|
nmc_dup_fields_array (const NMMetaAbstractInfo *const*fields, NmcOfFlags flags)
|
2013-05-22 08:36:09 +02:00
|
|
|
{
|
|
|
|
|
NmcOutputField *row;
|
cli: split tracking of meta data out of NmcOutputField
When generating output data, nmcli iterates over a list of
property-descriptors (nmc_fields_ip4_config), creates an intermediate
array (output_data) and finally prints it.
However, previously both the meta data (nmc_fields_ip4_config) and
the intermediate format use the same type NmcOutputField. This means,
certain fields are relevant to describe a property, and other fields
are output/formatting fields.
Split this up. Now, the meta data is tracked in form of an NMMetaAbstractInfo
lists. This separates the information about properties from intermediate steps
during creation of the output.
Note that currently functions like print_ip4_config() still have all the
knowledge about how to generate the output. That is wrong, instead, the
meta data (NMMetaAbstractInfo) should describe how to create the output
and then all those functions could be replaced. This means, later we want
to add more knowledge to the NMMetaAbstractInfo, so it is important to
keep them separate from NmcOutputField.
2017-03-31 19:18:16 +02:00
|
|
|
gsize l;
|
2013-05-22 08:36:09 +02:00
|
|
|
|
cli: split tracking of meta data out of NmcOutputField
When generating output data, nmcli iterates over a list of
property-descriptors (nmc_fields_ip4_config), creates an intermediate
array (output_data) and finally prints it.
However, previously both the meta data (nmc_fields_ip4_config) and
the intermediate format use the same type NmcOutputField. This means,
certain fields are relevant to describe a property, and other fields
are output/formatting fields.
Split this up. Now, the meta data is tracked in form of an NMMetaAbstractInfo
lists. This separates the information about properties from intermediate steps
during creation of the output.
Note that currently functions like print_ip4_config() still have all the
knowledge about how to generate the output. That is wrong, instead, the
meta data (NMMetaAbstractInfo) should describe how to create the output
and then all those functions could be replaced. This means, later we want
to add more knowledge to the NMMetaAbstractInfo, so it is important to
keep them separate from NmcOutputField.
2017-03-31 19:18:16 +02:00
|
|
|
for (l = 0; fields[l]; l++) {
|
|
|
|
|
}
|
2013-05-22 08:36:09 +02:00
|
|
|
|
cli: split tracking of meta data out of NmcOutputField
When generating output data, nmcli iterates over a list of
property-descriptors (nmc_fields_ip4_config), creates an intermediate
array (output_data) and finally prints it.
However, previously both the meta data (nmc_fields_ip4_config) and
the intermediate format use the same type NmcOutputField. This means,
certain fields are relevant to describe a property, and other fields
are output/formatting fields.
Split this up. Now, the meta data is tracked in form of an NMMetaAbstractInfo
lists. This separates the information about properties from intermediate steps
during creation of the output.
Note that currently functions like print_ip4_config() still have all the
knowledge about how to generate the output. That is wrong, instead, the
meta data (NMMetaAbstractInfo) should describe how to create the output
and then all those functions could be replaced. This means, later we want
to add more knowledge to the NMMetaAbstractInfo, so it is important to
keep them separate from NmcOutputField.
2017-03-31 19:18:16 +02:00
|
|
|
row = g_new0 (NmcOutputField, l + 1);
|
|
|
|
|
for (l = 0; fields[l]; l++)
|
|
|
|
|
row[l].info = fields[l];
|
|
|
|
|
row[0].flags = flags;
|
2013-05-22 08:36:09 +02:00
|
|
|
return row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2017-03-30 13:34:35 +02:00
|
|
|
nmc_empty_output_fields (NmcOutputData *output_data)
|
2013-05-22 08:36:09 +02:00
|
|
|
{
|
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
|
|
/* Free values in field structure */
|
2017-03-30 13:34:35 +02:00
|
|
|
for (i = 0; i < output_data->output_data->len; i++) {
|
|
|
|
|
NmcOutputField *fld_arr = g_ptr_array_index (output_data->output_data, i);
|
2013-05-22 08:36:09 +02:00
|
|
|
nmc_free_output_field_values (fld_arr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Empty output_data array */
|
2017-03-30 13:34:35 +02:00
|
|
|
if (output_data->output_data->len > 0)
|
|
|
|
|
g_ptr_array_remove_range (output_data->output_data, 0, output_data->output_data->len);
|
2018-10-31 10:10:10 +01:00
|
|
|
|
|
|
|
|
g_ptr_array_unref (output_data->output_data);
|
2013-05-22 08:36:09 +02:00
|
|
|
}
|
|
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
guint col_idx;
|
|
|
|
|
const PrintDataCol *col;
|
|
|
|
|
const char *title;
|
2017-04-06 15:14:23 +02:00
|
|
|
bool title_to_free:1;
|
2018-06-08 13:35:58 +02:00
|
|
|
|
|
|
|
|
/* whether the column should be printed. If not %TRUE,
|
|
|
|
|
* the column will be skipped. */
|
|
|
|
|
bool to_print:1;
|
|
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
int width;
|
|
|
|
|
} PrintDataHeaderCell;
|
|
|
|
|
|
2017-04-06 15:14:23 +02:00
|
|
|
typedef enum {
|
|
|
|
|
PRINT_DATA_CELL_FORMAT_TYPE_PLAIN = 0,
|
|
|
|
|
PRINT_DATA_CELL_FORMAT_TYPE_STRV,
|
|
|
|
|
} PrintDataCellFormatType;
|
|
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
typedef struct {
|
|
|
|
|
guint row_idx;
|
|
|
|
|
const PrintDataHeaderCell *header_cell;
|
2018-03-29 12:31:33 +02:00
|
|
|
NMMetaColor color;
|
2017-04-06 15:14:23 +02:00
|
|
|
union {
|
|
|
|
|
const char *plain;
|
|
|
|
|
const char *const*strv;
|
|
|
|
|
} text;
|
|
|
|
|
PrintDataCellFormatType text_format:3;
|
2017-04-04 15:23:39 +02:00
|
|
|
bool text_to_free:1;
|
|
|
|
|
} PrintDataCell;
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_print_data_header_cell_clear (gpointer cell_p)
|
|
|
|
|
{
|
2017-04-06 15:14:23 +02:00
|
|
|
PrintDataHeaderCell *cell = cell_p;
|
|
|
|
|
|
|
|
|
|
if (cell->title_to_free) {
|
|
|
|
|
g_free ((char *) cell->title);
|
|
|
|
|
cell->title_to_free = FALSE;
|
|
|
|
|
}
|
|
|
|
|
cell->title = NULL;
|
2017-04-04 15:23:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_print_data_cell_clear_text (PrintDataCell *cell)
|
|
|
|
|
{
|
2018-06-08 14:19:39 +02:00
|
|
|
switch (cell->text_format) {
|
|
|
|
|
case PRINT_DATA_CELL_FORMAT_TYPE_PLAIN:
|
|
|
|
|
if (cell->text_to_free)
|
2017-04-06 15:14:23 +02:00
|
|
|
g_free ((char *) cell->text.plain);
|
2018-06-08 14:19:39 +02:00
|
|
|
cell->text.plain = NULL;
|
|
|
|
|
break;
|
|
|
|
|
case PRINT_DATA_CELL_FORMAT_TYPE_STRV:
|
|
|
|
|
if (cell->text_to_free)
|
2017-04-06 15:14:23 +02:00
|
|
|
g_strfreev ((char **) cell->text.strv);
|
2018-06-08 14:19:39 +02:00
|
|
|
cell->text.strv = NULL;
|
|
|
|
|
break;
|
|
|
|
|
};
|
|
|
|
|
cell->text_format = PRINT_DATA_CELL_FORMAT_TYPE_PLAIN;
|
|
|
|
|
cell->text_to_free = FALSE;
|
2017-04-04 15:23:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_print_data_cell_clear (gpointer cell_p)
|
|
|
|
|
{
|
|
|
|
|
PrintDataCell *cell = cell_p;
|
|
|
|
|
|
|
|
|
|
_print_data_cell_clear_text (cell);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_print_fill (const NmcConfig *nmc_config,
|
|
|
|
|
gpointer const *targets,
|
2018-04-30 13:01:20 +02:00
|
|
|
gpointer targets_data,
|
2017-04-04 15:23:39 +02:00
|
|
|
const PrintDataCol *cols,
|
|
|
|
|
guint cols_len,
|
|
|
|
|
GArray **out_header_row,
|
|
|
|
|
GArray **out_cells)
|
|
|
|
|
{
|
|
|
|
|
GArray *cells;
|
|
|
|
|
GArray *header_row;
|
|
|
|
|
guint i_row, i_col;
|
|
|
|
|
guint targets_len;
|
|
|
|
|
NMMetaAccessorGetType text_get_type;
|
2017-04-06 15:14:23 +02:00
|
|
|
NMMetaAccessorGetFlags text_get_flags;
|
2017-04-04 15:23:39 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
header_row = g_array_sized_new (FALSE, TRUE, sizeof (PrintDataHeaderCell), cols_len);
|
|
|
|
|
g_array_set_clear_func (header_row, _print_data_header_cell_clear);
|
|
|
|
|
|
|
|
|
|
for (i_col = 0; i_col < cols_len; i_col++) {
|
|
|
|
|
const PrintDataCol *col;
|
|
|
|
|
PrintDataHeaderCell *header_cell;
|
|
|
|
|
guint col_idx;
|
2017-04-06 15:14:23 +02:00
|
|
|
const NMMetaAbstractInfo *info;
|
2017-04-04 15:23:39 +02:00
|
|
|
|
|
|
|
|
col = &cols[i_col];
|
|
|
|
|
if (!col->is_leaf)
|
|
|
|
|
continue;
|
|
|
|
|
|
2017-04-06 15:14:23 +02:00
|
|
|
info = col->selection_item->info;
|
|
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
col_idx = header_row->len;
|
|
|
|
|
g_array_set_size (header_row, col_idx + 1);
|
|
|
|
|
|
|
|
|
|
header_cell = &g_array_index (header_row, PrintDataHeaderCell, col_idx);
|
|
|
|
|
|
|
|
|
|
header_cell->col_idx = col_idx;
|
|
|
|
|
header_cell->col = col;
|
2018-06-08 13:35:58 +02:00
|
|
|
|
|
|
|
|
/* by default, the entire column is skipped. That is the case,
|
|
|
|
|
* unless we have a cell (below) which opts-in to be printed. */
|
|
|
|
|
header_cell->to_print = FALSE;
|
2017-04-06 15:14:23 +02:00
|
|
|
|
2017-04-06 15:14:23 +02:00
|
|
|
header_cell->title = nm_meta_abstract_info_get_name (info, TRUE);
|
|
|
|
|
if ( nmc_config->multiline_output
|
2018-05-02 16:36:28 +02:00
|
|
|
&& col->parent_col
|
2017-04-06 15:14:23 +02:00
|
|
|
&& NM_IN_SET (info->meta_type,
|
|
|
|
|
&nm_meta_type_property_info,
|
|
|
|
|
&nmc_meta_type_generic_info)) {
|
|
|
|
|
header_cell->title = g_strdup_printf ("%s.%s",
|
2018-05-02 16:36:28 +02:00
|
|
|
nm_meta_abstract_info_get_name (col->parent_col->selection_item->info, FALSE),
|
2017-04-06 15:14:23 +02:00
|
|
|
header_cell->title);
|
|
|
|
|
header_cell->title_to_free = TRUE;
|
|
|
|
|
}
|
2017-04-04 15:23:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
targets_len = NM_PTRARRAY_LEN (targets);
|
|
|
|
|
|
|
|
|
|
cells = g_array_sized_new (FALSE, TRUE, sizeof (PrintDataCell), targets_len * header_row->len);
|
|
|
|
|
g_array_set_clear_func (cells, _print_data_cell_clear);
|
|
|
|
|
g_array_set_size (cells, targets_len * header_row->len);
|
|
|
|
|
|
cli: rework printing of `nmcli connection` for multiple active connections
The output of `nmcli connection show` contains also information about
whether the profile is currently active, for example the device and
the current (activation) state.
Even when a profile can be activated only once (without supporting
mutiple activations at the same time), there are moments when a
connection is activating and still deactivating on another device.
NetworkManager ensures in the case with single activations that
a profile cannot be in state "activated" multiple times. But that
doesn't mean, that one profile cannot have multiple active connection
which reference it. That was already handled wrongly before, because
`nmcli connection show` would only search the first matching
active-connection. That is, it would arbitrarily pick an active
connection in case there were multiple and only show activation
state about one.
Furthermore, we will soon also add the possibility, that a profile can be
active multiple times (at the same time). Especially then, we need to
extend the output format to show all the devices on which the profile is
currently active.
Rework printing the connection list to use nmc_print(), and fix various
issues.
- as discussed, a profile may have multiple active connections at each time.
There are only two possibilities: if a profile is active multiple
times, show a line for each activation, or otherwise, show the
information about multiple activations combined in one line, e.g. by
printing "DEVICE eth0,eth1". This patch, does the former.
We will now print a line for each active connection, to show
all the devices and activation states in multiple lines.
Yes, this may result in the same profile being printed multiple times.
That is a change in behavior, and inconvenient if you do something
like
for UUID in $(nmcli connection show | awk '{print$2}'); do ...
However, above is anyway wrong because it assumes that there are no
spaces in the connection name. The proper way to do this is like
for UUID in $(nmcli -g UUID connection show); do ...
In the latter case, whenever a user selects a subset of fields
(--fields, --get) which don't print information about active connections,
these multiple lines are combined. So, above still works as expected,
never returning duplicate UUIDs.
- if a user has no permissions to see a connection, we previously
would print "<invisible> $NAME". No longer do this but just print
the ID was it is reported by the active-connection. If the goal
of this was to prevent users from accidentally access the non-existing
connection by $NAME, then this was a bad solution, because a script
would instead try to access "<invisible> $NAME". This is now solved
better by hiding the active connection if the user selects "-g NAME".
- the --order option now sorts according to how the fields are shown.
For example, with --terse mode, it will evaluate type "802-11-wireless"
but with pretty mode it will consider "wifi". This may change the
ordering in which connections are shown. Also, for sorting the name,
we use g_utf8_collate() because it's unicode.
2018-04-25 10:00:40 +02:00
|
|
|
text_get_type = nmc_print_output_to_accessor_get_type (nmc_config->print_output);
|
2017-04-06 15:14:23 +02:00
|
|
|
text_get_flags = NM_META_ACCESSOR_GET_FLAGS_ACCEPT_STRV;
|
2017-04-06 15:14:23 +02:00
|
|
|
if (nmc_config->show_secrets)
|
|
|
|
|
text_get_flags |= NM_META_ACCESSOR_GET_FLAGS_SHOW_SECRETS;
|
2017-04-04 15:23:39 +02:00
|
|
|
|
|
|
|
|
for (i_row = 0; i_row < targets_len; i_row++) {
|
|
|
|
|
gpointer target = targets[i_row];
|
|
|
|
|
PrintDataCell *cells_line = &g_array_index (cells, PrintDataCell, i_row * header_row->len);
|
|
|
|
|
|
|
|
|
|
for (i_col = 0; i_col < header_row->len; i_col++) {
|
|
|
|
|
char *to_free = NULL;
|
|
|
|
|
PrintDataCell *cell = &cells_line[i_col];
|
2018-02-27 16:28:28 +01:00
|
|
|
PrintDataHeaderCell *header_cell;
|
2017-04-04 15:23:39 +02:00
|
|
|
const NMMetaAbstractInfo *info;
|
2017-04-06 15:14:23 +02:00
|
|
|
NMMetaAccessorGetOutFlags text_out_flags, color_out_flags;
|
|
|
|
|
gconstpointer value;
|
2018-02-27 16:28:28 +01:00
|
|
|
gboolean is_default;
|
2017-04-04 15:23:39 +02:00
|
|
|
|
|
|
|
|
header_cell = &g_array_index (header_row, PrintDataHeaderCell, i_col);
|
|
|
|
|
info = header_cell->col->selection_item->info;
|
|
|
|
|
|
|
|
|
|
cell->row_idx = i_row;
|
|
|
|
|
cell->header_cell = header_cell;
|
2017-04-06 15:14:23 +02:00
|
|
|
|
|
|
|
|
value = nm_meta_abstract_info_get (info,
|
2017-04-07 12:58:13 +02:00
|
|
|
nmc_meta_environment,
|
2020-04-02 13:39:39 +02:00
|
|
|
(gpointer) nmc_meta_environment_arg,
|
2017-04-06 15:14:23 +02:00
|
|
|
target,
|
2018-04-30 13:01:20 +02:00
|
|
|
targets_data,
|
2017-04-06 15:14:23 +02:00
|
|
|
text_get_type,
|
|
|
|
|
text_get_flags,
|
|
|
|
|
&text_out_flags,
|
2018-02-27 16:28:28 +01:00
|
|
|
&is_default,
|
2017-04-06 15:14:23 +02:00
|
|
|
(gpointer *) &to_free);
|
2018-02-27 16:28:28 +01:00
|
|
|
|
2018-06-08 08:08:58 +02:00
|
|
|
nm_assert (!to_free || value == to_free);
|
|
|
|
|
|
cli: add functionality to hide properties from output
Historically, nmcli printed all fields during operations like
`nmcli connection show "$PROFILE"`. As we supported more and
more options, this resulted in a verbose output, of most properties
just being the default values.
To counter that, we added the '-overview' option. When given,
it would hide options that are set at their default. This option
was not the default, to preserve established behavior.
However, for new options, we can afford to hide them. Add a mechanism,
that property getters can mark their value to be hidden. At the moment,
there is no way to show these properties. However, we could add a
'-verbose' option, with the opposite meaning of '-overview'. Anyway,
that does not seem necessary at the moment.
Hiding properties from output is only acceptable for new properties
(otherwise we badly change behavior), and if the properties are set
at their default values (otherwise, we hide important information).
2018-08-08 20:43:22 +02:00
|
|
|
if ( is_default
|
|
|
|
|
&& ( nmc_config->overview
|
|
|
|
|
|| NM_FLAGS_HAS (text_out_flags, NM_META_ACCESSOR_GET_OUT_FLAGS_HIDE))) {
|
|
|
|
|
/* don't mark the entry for display. This is to shorten the output in case
|
|
|
|
|
* the property is the default value. But we only do that, if the user
|
|
|
|
|
* opts in to this behavior (-overview), or of the property marks itself
|
2018-09-14 23:49:20 -04:00
|
|
|
* eligible to be hidden.
|
cli: add functionality to hide properties from output
Historically, nmcli printed all fields during operations like
`nmcli connection show "$PROFILE"`. As we supported more and
more options, this resulted in a verbose output, of most properties
just being the default values.
To counter that, we added the '-overview' option. When given,
it would hide options that are set at their default. This option
was not the default, to preserve established behavior.
However, for new options, we can afford to hide them. Add a mechanism,
that property getters can mark their value to be hidden. At the moment,
there is no way to show these properties. However, we could add a
'-verbose' option, with the opposite meaning of '-overview'. Anyway,
that does not seem necessary at the moment.
Hiding properties from output is only acceptable for new properties
(otherwise we badly change behavior), and if the properties are set
at their default values (otherwise, we hide important information).
2018-08-08 20:43:22 +02:00
|
|
|
*
|
|
|
|
|
* In general, only new API shall mark itself eligible to be hidden.
|
|
|
|
|
* Long established properties cannot, because it would be a change
|
|
|
|
|
* in behavior. */
|
|
|
|
|
} else
|
2018-06-08 13:35:58 +02:00
|
|
|
header_cell->to_print = TRUE;
|
2018-02-27 16:28:28 +01:00
|
|
|
|
2017-04-06 15:14:23 +02:00
|
|
|
if (NM_FLAGS_HAS (text_out_flags, NM_META_ACCESSOR_GET_OUT_FLAGS_STRV)) {
|
2018-06-08 08:08:58 +02:00
|
|
|
if (nmc_config->multiline_output) {
|
|
|
|
|
cell->text_format = PRINT_DATA_CELL_FORMAT_TYPE_STRV;
|
|
|
|
|
cell->text.strv = value;
|
|
|
|
|
cell->text_to_free = !!to_free;
|
|
|
|
|
} else {
|
|
|
|
|
if (value && ((const char *const*) value)[0]) {
|
2017-04-06 15:14:23 +02:00
|
|
|
cell->text.plain = g_strjoinv (" | ", (char **) value);
|
|
|
|
|
cell->text_to_free = TRUE;
|
|
|
|
|
}
|
2018-06-08 08:08:58 +02:00
|
|
|
if (to_free)
|
|
|
|
|
g_strfreev ((char **) to_free);
|
2017-04-06 15:14:23 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cell->text.plain = value;
|
|
|
|
|
cell->text_to_free = !!to_free;
|
|
|
|
|
}
|
2017-04-04 15:23:39 +02:00
|
|
|
|
2018-03-29 12:31:33 +02:00
|
|
|
cell->color = GPOINTER_TO_INT (nm_meta_abstract_info_get (info,
|
|
|
|
|
nmc_meta_environment,
|
2020-04-02 13:39:39 +02:00
|
|
|
(gpointer) nmc_meta_environment_arg,
|
2018-03-29 12:31:33 +02:00
|
|
|
target,
|
2018-04-30 13:01:20 +02:00
|
|
|
targets_data,
|
2018-03-29 12:31:33 +02:00
|
|
|
NM_META_ACCESSOR_GET_TYPE_COLOR,
|
|
|
|
|
NM_META_ACCESSOR_GET_FLAGS_NONE,
|
|
|
|
|
&color_out_flags,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL));
|
2017-04-04 15:23:39 +02:00
|
|
|
|
2017-04-06 15:14:23 +02:00
|
|
|
if (cell->text_format == PRINT_DATA_CELL_FORMAT_TYPE_PLAIN) {
|
2018-05-02 10:47:20 +02:00
|
|
|
if ( NM_IN_SET (nmc_config->print_output, NMC_PRINT_NORMAL, NMC_PRINT_PRETTY)
|
2018-05-02 10:47:20 +02:00
|
|
|
&& ( !cell->text.plain
|
|
|
|
|
|| !cell->text.plain[0])) {
|
2017-04-06 15:14:23 +02:00
|
|
|
_print_data_cell_clear_text (cell);
|
|
|
|
|
cell->text.plain = "--";
|
|
|
|
|
} else if (!cell->text.plain)
|
|
|
|
|
cell->text.plain = "";
|
2018-06-08 14:19:39 +02:00
|
|
|
nm_assert (cell->text_format == PRINT_DATA_CELL_FORMAT_TYPE_PLAIN);
|
2017-04-06 15:14:23 +02:00
|
|
|
}
|
2017-04-04 15:23:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i_col = 0; i_col < header_row->len; i_col++) {
|
|
|
|
|
PrintDataHeaderCell *header_cell = &g_array_index (header_row, PrintDataHeaderCell, i_col);
|
|
|
|
|
|
|
|
|
|
header_cell->width = nmc_string_screen_width (header_cell->title, NULL);
|
|
|
|
|
|
|
|
|
|
for (i_row = 0; i_row < targets_len; i_row++) {
|
2020-03-13 12:17:18 +01:00
|
|
|
const PrintDataCell *cells_line = &g_array_index (cells, PrintDataCell, i_row * header_row->len);
|
|
|
|
|
const PrintDataCell *cell = &cells_line[i_col];
|
2017-04-06 15:14:23 +02:00
|
|
|
const char *const*i_strv;
|
2017-04-04 15:23:39 +02:00
|
|
|
|
2017-04-06 15:14:23 +02:00
|
|
|
switch (cell->text_format) {
|
|
|
|
|
case PRINT_DATA_CELL_FORMAT_TYPE_PLAIN:
|
2017-04-04 15:23:39 +02:00
|
|
|
header_cell->width = NM_MAX (header_cell->width,
|
2017-04-06 15:14:23 +02:00
|
|
|
nmc_string_screen_width (cell->text.plain, NULL));
|
|
|
|
|
break;
|
|
|
|
|
case PRINT_DATA_CELL_FORMAT_TYPE_STRV:
|
|
|
|
|
i_strv = cell->text.strv;
|
|
|
|
|
if (i_strv) {
|
|
|
|
|
for (; *i_strv; i_strv++) {
|
|
|
|
|
header_cell->width = NM_MAX (header_cell->width,
|
|
|
|
|
nmc_string_screen_width (*i_strv, NULL));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
2017-04-04 15:23:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
header_cell->width += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*out_header_row = header_row;
|
|
|
|
|
*out_cells = cells;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-06 15:14:23 +02:00
|
|
|
static gboolean
|
|
|
|
|
_print_skip_column (const NmcConfig *nmc_config,
|
|
|
|
|
const PrintDataHeaderCell *header_cell)
|
|
|
|
|
{
|
2017-04-12 15:14:23 +02:00
|
|
|
const NMMetaSelectionItem *selection_item;
|
2017-04-06 15:14:23 +02:00
|
|
|
const NMMetaAbstractInfo *info;
|
|
|
|
|
|
|
|
|
|
selection_item = header_cell->col->selection_item;
|
|
|
|
|
info = selection_item->info;
|
|
|
|
|
|
2018-06-08 13:35:58 +02:00
|
|
|
if (!header_cell->to_print)
|
2018-02-27 16:28:28 +01:00
|
|
|
return TRUE;
|
|
|
|
|
|
2017-04-06 15:14:23 +02:00
|
|
|
if (nmc_config->multiline_output) {
|
2017-04-06 15:14:23 +02:00
|
|
|
if (info->meta_type == &nm_meta_type_setting_info_editor) {
|
2017-04-06 15:14:23 +02:00
|
|
|
/* we skip the "name" entry for the setting in multiline output. */
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
2017-04-06 15:14:23 +02:00
|
|
|
if ( info->meta_type == &nmc_meta_type_generic_info
|
|
|
|
|
&& ((const NmcMetaGenericInfo *) info)->nested) {
|
|
|
|
|
/* skip the "name" entry for parent generic-infos */
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
2017-04-06 15:14:23 +02:00
|
|
|
} else {
|
2017-04-06 15:14:23 +02:00
|
|
|
if ( NM_IN_SET (info->meta_type,
|
|
|
|
|
&nm_meta_type_setting_info_editor,
|
|
|
|
|
&nmc_meta_type_generic_info)
|
|
|
|
|
&& selection_item->sub_selection) {
|
2017-04-06 15:14:23 +02:00
|
|
|
/* in tabular form, we skip the "name" entry for sections that have sub-selections.
|
|
|
|
|
* That is, for "ipv4.may-fail", but not for "ipv4". */
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
static void
|
|
|
|
|
_print_do (const NmcConfig *nmc_config,
|
|
|
|
|
const char *header_name_no_l10n,
|
|
|
|
|
guint col_len,
|
|
|
|
|
guint row_len,
|
|
|
|
|
const PrintDataHeaderCell *header_row,
|
|
|
|
|
const PrintDataCell *cells)
|
|
|
|
|
{
|
|
|
|
|
int width1, width2;
|
|
|
|
|
int table_width = 0;
|
|
|
|
|
guint i_row, i_col;
|
|
|
|
|
nm_auto_free_gstring GString *str = NULL;
|
|
|
|
|
|
cli: rework printing of `nmcli connection` for multiple active connections
The output of `nmcli connection show` contains also information about
whether the profile is currently active, for example the device and
the current (activation) state.
Even when a profile can be activated only once (without supporting
mutiple activations at the same time), there are moments when a
connection is activating and still deactivating on another device.
NetworkManager ensures in the case with single activations that
a profile cannot be in state "activated" multiple times. But that
doesn't mean, that one profile cannot have multiple active connection
which reference it. That was already handled wrongly before, because
`nmcli connection show` would only search the first matching
active-connection. That is, it would arbitrarily pick an active
connection in case there were multiple and only show activation
state about one.
Furthermore, we will soon also add the possibility, that a profile can be
active multiple times (at the same time). Especially then, we need to
extend the output format to show all the devices on which the profile is
currently active.
Rework printing the connection list to use nmc_print(), and fix various
issues.
- as discussed, a profile may have multiple active connections at each time.
There are only two possibilities: if a profile is active multiple
times, show a line for each activation, or otherwise, show the
information about multiple activations combined in one line, e.g. by
printing "DEVICE eth0,eth1". This patch, does the former.
We will now print a line for each active connection, to show
all the devices and activation states in multiple lines.
Yes, this may result in the same profile being printed multiple times.
That is a change in behavior, and inconvenient if you do something
like
for UUID in $(nmcli connection show | awk '{print$2}'); do ...
However, above is anyway wrong because it assumes that there are no
spaces in the connection name. The proper way to do this is like
for UUID in $(nmcli -g UUID connection show); do ...
In the latter case, whenever a user selects a subset of fields
(--fields, --get) which don't print information about active connections,
these multiple lines are combined. So, above still works as expected,
never returning duplicate UUIDs.
- if a user has no permissions to see a connection, we previously
would print "<invisible> $NAME". No longer do this but just print
the ID was it is reported by the active-connection. If the goal
of this was to prevent users from accidentally access the non-existing
connection by $NAME, then this was a bad solution, because a script
would instead try to access "<invisible> $NAME". This is now solved
better by hiding the active connection if the user selects "-g NAME".
- the --order option now sorts according to how the fields are shown.
For example, with --terse mode, it will evaluate type "802-11-wireless"
but with pretty mode it will consider "wifi". This may change the
ordering in which connections are shown. Also, for sorting the name,
we use g_utf8_collate() because it's unicode.
2018-04-25 10:00:40 +02:00
|
|
|
g_assert (col_len);
|
2017-04-04 15:23:39 +02:00
|
|
|
|
|
|
|
|
/* Main header */
|
2018-05-02 10:47:20 +02:00
|
|
|
if ( nmc_config->print_output == NMC_PRINT_PRETTY
|
|
|
|
|
&& header_name_no_l10n) {
|
2017-04-04 15:23:39 +02:00
|
|
|
gs_free char *line = NULL;
|
|
|
|
|
int header_width;
|
|
|
|
|
const char *header_name = _(header_name_no_l10n);
|
|
|
|
|
|
|
|
|
|
header_width = nmc_string_screen_width (header_name, NULL) + 4;
|
|
|
|
|
|
2018-05-03 09:15:47 +02:00
|
|
|
if (nmc_config->multiline_output) {
|
2017-04-04 15:23:39 +02:00
|
|
|
table_width = NM_MAX (header_width, ML_HEADER_WIDTH);
|
|
|
|
|
line = g_strnfill (ML_HEADER_WIDTH, '=');
|
|
|
|
|
} else { /* tabular */
|
|
|
|
|
table_width = NM_MAX (table_width, header_width);
|
|
|
|
|
line = g_strnfill (table_width, '=');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
width1 = strlen (header_name);
|
|
|
|
|
width2 = nmc_string_screen_width (header_name, NULL);
|
|
|
|
|
g_print ("%s\n", line);
|
|
|
|
|
g_print ("%*s\n", (table_width + width2)/2 + width1 - width2, header_name);
|
|
|
|
|
g_print ("%s\n", line);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-03 09:15:47 +02:00
|
|
|
str = !nmc_config->multiline_output
|
2017-04-04 15:23:39 +02:00
|
|
|
? g_string_sized_new (100)
|
|
|
|
|
: NULL;
|
|
|
|
|
|
|
|
|
|
/* print the header for the tabular form */
|
2018-05-02 10:47:20 +02:00
|
|
|
if ( NM_IN_SET (nmc_config->print_output, NMC_PRINT_NORMAL, NMC_PRINT_PRETTY)
|
2018-05-03 09:15:47 +02:00
|
|
|
&& !nmc_config->multiline_output) {
|
2017-04-04 15:23:39 +02:00
|
|
|
for (i_col = 0; i_col < col_len; i_col++) {
|
|
|
|
|
const PrintDataHeaderCell *header_cell = &header_row[i_col];
|
|
|
|
|
const char *title;
|
|
|
|
|
|
2017-04-06 15:14:23 +02:00
|
|
|
if (_print_skip_column (nmc_config, header_cell))
|
|
|
|
|
continue;
|
|
|
|
|
|
2017-04-04 15:23:39 +02:00
|
|
|
title = header_cell->title;
|
|
|
|
|
|
|
|
|
|
width1 = strlen (title);
|
2018-09-14 23:49:20 -04:00
|
|
|
width2 = nmc_string_screen_width (title, NULL); /* Width of the string (in screen columns) */
|
2017-04-04 15:23:39 +02:00
|
|
|
g_string_append_printf (str, "%-*s", (int) (header_cell->width + width1 - width2), title);
|
|
|
|
|
g_string_append_c (str, ' '); /* Column separator */
|
|
|
|
|
table_width += header_cell->width + width1 - width2 + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (str->len)
|
|
|
|
|
g_string_truncate (str, str->len-1); /* Chop off last column separator */
|
|
|
|
|
g_print ("%s\n", str->str);
|
|
|
|
|
g_string_truncate (str, 0);
|
|
|
|
|
|
|
|
|
|
/* Print horizontal separator */
|
2018-05-02 10:47:20 +02:00
|
|
|
if (nmc_config->print_output == NMC_PRINT_PRETTY) {
|
2017-04-04 15:23:39 +02:00
|
|
|
gs_free char *line = NULL;
|
|
|
|
|
|
|
|
|
|
g_print ("%s\n", (line = g_strnfill (table_width, '-')));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i_row = 0; i_row < row_len; i_row++) {
|
|
|
|
|
const PrintDataCell *current_line = &cells[i_row * col_len];
|
|
|
|
|
|
|
|
|
|
for (i_col = 0; i_col < col_len; i_col++) {
|
|
|
|
|
const PrintDataCell *cell = ¤t_line[i_col];
|
2017-04-06 15:14:23 +02:00
|
|
|
const char *const*lines = NULL;
|
|
|
|
|
guint i_lines, lines_len;
|
2017-04-04 15:23:39 +02:00
|
|
|
|
2017-04-06 15:14:23 +02:00
|
|
|
if (_print_skip_column (nmc_config, cell->header_cell))
|
|
|
|
|
continue;
|
|
|
|
|
|
2017-04-06 15:14:23 +02:00
|
|
|
lines_len = 0;
|
|
|
|
|
switch (cell->text_format) {
|
|
|
|
|
case PRINT_DATA_CELL_FORMAT_TYPE_PLAIN:
|
|
|
|
|
lines = &cell->text.plain;
|
|
|
|
|
lines_len = 1;
|
|
|
|
|
break;
|
|
|
|
|
case PRINT_DATA_CELL_FORMAT_TYPE_STRV:
|
2018-05-03 09:15:47 +02:00
|
|
|
nm_assert (nmc_config->multiline_output);
|
2017-04-06 15:14:23 +02:00
|
|
|
lines = cell->text.strv;
|
|
|
|
|
lines_len = NM_PTRARRAY_LEN (lines);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i_lines = 0; i_lines < lines_len; i_lines++) {
|
|
|
|
|
gs_free char *text_to_free = NULL;
|
|
|
|
|
const char *text;
|
|
|
|
|
|
2018-03-29 12:31:33 +02:00
|
|
|
text = colorize_string (nmc_config, cell->color, lines[i_lines], &text_to_free);
|
2018-05-03 09:15:47 +02:00
|
|
|
if (nmc_config->multiline_output) {
|
2017-04-06 15:14:23 +02:00
|
|
|
gs_free char *prefix = NULL;
|
2017-04-04 15:23:39 +02:00
|
|
|
|
2017-04-06 15:14:23 +02:00
|
|
|
if (cell->text_format == PRINT_DATA_CELL_FORMAT_TYPE_STRV)
|
|
|
|
|
prefix = g_strdup_printf ("%s[%u]:", cell->header_cell->title, i_lines + 1);
|
2017-04-04 15:23:39 +02:00
|
|
|
else
|
2017-04-06 15:14:23 +02:00
|
|
|
prefix = g_strdup_printf ("%s:", cell->header_cell->title);
|
|
|
|
|
width1 = strlen (prefix);
|
|
|
|
|
width2 = nmc_string_screen_width (prefix, NULL);
|
2018-05-02 10:47:20 +02:00
|
|
|
g_print ("%-*s%s\n",
|
|
|
|
|
(int) ( nmc_config->print_output == NMC_PRINT_TERSE
|
|
|
|
|
? 0
|
|
|
|
|
: ML_VALUE_INDENT+width1-width2),
|
|
|
|
|
prefix,
|
|
|
|
|
text);
|
2017-04-04 15:23:39 +02:00
|
|
|
} else {
|
2017-04-06 15:14:23 +02:00
|
|
|
nm_assert (str);
|
2018-05-02 10:47:20 +02:00
|
|
|
if (nmc_config->print_output == NMC_PRINT_TERSE) {
|
2017-04-06 15:14:23 +02:00
|
|
|
if (nmc_config->escape_values) {
|
|
|
|
|
const char *p = text;
|
|
|
|
|
while (*p) {
|
|
|
|
|
if (*p == ':' || *p == '\\')
|
|
|
|
|
g_string_append_c (str, '\\'); /* Escaping by '\' */
|
|
|
|
|
g_string_append_c (str, *p);
|
|
|
|
|
p++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
g_string_append_printf (str, "%s", text);
|
|
|
|
|
g_string_append_c (str, ':'); /* Column separator */
|
|
|
|
|
} else {
|
|
|
|
|
const PrintDataHeaderCell *header_cell = &header_row[i_col];
|
|
|
|
|
|
|
|
|
|
width1 = strlen (text);
|
2018-09-14 23:49:20 -04:00
|
|
|
width2 = nmc_string_screen_width (text, NULL); /* Width of the string (in screen columns) */
|
2017-04-06 15:14:23 +02:00
|
|
|
g_string_append_printf (str, "%-*s", (int) (header_cell->width + width1 - width2), text);
|
|
|
|
|
g_string_append_c (str, ' '); /* Column separator */
|
|
|
|
|
table_width += header_cell->width + width1 - width2 + 1;
|
|
|
|
|
}
|
2017-04-04 15:23:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-03 09:15:47 +02:00
|
|
|
if (!nmc_config->multiline_output) {
|
2017-04-04 15:23:39 +02:00
|
|
|
if (str->len)
|
|
|
|
|
g_string_truncate (str, str->len-1); /* Chop off last column separator */
|
|
|
|
|
g_print ("%s\n", str->str);
|
|
|
|
|
|
|
|
|
|
g_string_truncate (str, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-02 10:47:20 +02:00
|
|
|
if ( nmc_config->print_output == NMC_PRINT_PRETTY
|
2018-05-03 09:15:47 +02:00
|
|
|
&& nmc_config->multiline_output) {
|
2017-04-04 15:23:39 +02:00
|
|
|
gs_free char *line = NULL;
|
|
|
|
|
|
|
|
|
|
g_print ("%s\n", (line = g_strnfill (ML_HEADER_WIDTH, '-')));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
|
nmc_print (const NmcConfig *nmc_config,
|
|
|
|
|
gpointer const *targets,
|
2018-04-30 13:01:20 +02:00
|
|
|
gpointer targets_data,
|
2017-04-04 15:23:39 +02:00
|
|
|
const char *header_name_no_l10n,
|
|
|
|
|
const NMMetaAbstractInfo *const*fields,
|
|
|
|
|
const char *fields_str,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
gs_unref_ptrarray GPtrArray *gfree_keeper = NULL;
|
2020-03-12 14:39:27 +01:00
|
|
|
gs_free PrintDataCol *cols_data = NULL;
|
|
|
|
|
guint cols_len;
|
2017-04-04 15:23:39 +02:00
|
|
|
gs_unref_array GArray *header_row = NULL;
|
|
|
|
|
gs_unref_array GArray *cells = NULL;
|
|
|
|
|
|
2020-03-12 14:39:27 +01:00
|
|
|
if (!_output_selection_parse (fields,
|
|
|
|
|
fields_str,
|
|
|
|
|
&cols_data,
|
|
|
|
|
&cols_len,
|
|
|
|
|
&gfree_keeper,
|
2017-04-04 15:23:39 +02:00
|
|
|
error))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
_print_fill (nmc_config,
|
|
|
|
|
targets,
|
2018-04-30 13:01:20 +02:00
|
|
|
targets_data,
|
2020-03-12 14:39:27 +01:00
|
|
|
cols_data,
|
|
|
|
|
cols_len,
|
2017-04-04 15:23:39 +02:00
|
|
|
&header_row,
|
|
|
|
|
&cells);
|
|
|
|
|
|
|
|
|
|
_print_do (nmc_config,
|
|
|
|
|
header_name_no_l10n,
|
|
|
|
|
header_row->len,
|
|
|
|
|
cells->len / header_row->len,
|
|
|
|
|
&g_array_index (header_row, PrintDataHeaderCell, 0),
|
|
|
|
|
&g_array_index (cells, PrintDataCell, 0));
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2017-05-15 17:23:51 +02:00
|
|
|
static void
|
|
|
|
|
pager_fallback (void)
|
|
|
|
|
{
|
|
|
|
|
char buf[64];
|
|
|
|
|
int rb;
|
2019-01-31 13:29:21 +01:00
|
|
|
int errsv;
|
2017-05-15 17:23:51 +02:00
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
rb = read (STDIN_FILENO, buf, sizeof (buf));
|
|
|
|
|
if (rb == -1) {
|
2019-01-31 13:29:21 +01:00
|
|
|
errsv = errno;
|
|
|
|
|
if (errsv == EINTR)
|
2017-05-15 17:23:51 +02:00
|
|
|
continue;
|
2019-01-31 17:22:18 +01:00
|
|
|
g_printerr (_("Error reading nmcli output: %s\n"), nm_strerror_native (errsv));
|
2019-01-31 13:29:21 +01:00
|
|
|
_exit(EXIT_FAILURE);
|
2017-05-15 17:23:51 +02:00
|
|
|
}
|
|
|
|
|
if (write (STDOUT_FILENO, buf, rb) == -1) {
|
2019-01-31 13:29:21 +01:00
|
|
|
errsv = errno;
|
2019-01-31 17:22:18 +01:00
|
|
|
g_printerr (_("Error writing nmcli output: %s\n"), nm_strerror_native (errsv));
|
2017-05-15 17:23:51 +02:00
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
} while (rb > 0);
|
|
|
|
|
|
|
|
|
|
_exit(EXIT_SUCCESS);
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-09 13:08:24 +02:00
|
|
|
pid_t
|
2017-05-15 17:23:51 +02:00
|
|
|
nmc_terminal_spawn_pager (const NmcConfig *nmc_config)
|
|
|
|
|
{
|
|
|
|
|
const char *pager = getenv ("PAGER");
|
2018-10-09 13:08:24 +02:00
|
|
|
pid_t pager_pid;
|
2017-05-15 17:23:51 +02:00
|
|
|
pid_t parent_pid;
|
|
|
|
|
int fd[2];
|
2019-01-31 13:29:21 +01:00
|
|
|
int errsv;
|
2017-05-15 17:23:51 +02:00
|
|
|
|
2018-10-09 13:08:24 +02:00
|
|
|
if ( nmc_config->in_editor
|
2017-05-15 17:23:51 +02:00
|
|
|
|| nmc_config->print_output == NMC_PRINT_TERSE
|
2018-03-29 12:25:14 +02:00
|
|
|
|| !nmc_config->use_colors
|
2017-10-30 13:13:02 +01:00
|
|
|
|| g_strcmp0 (pager, "") == 0
|
|
|
|
|
|| getauxval (AT_SECURE))
|
2018-10-09 13:08:24 +02:00
|
|
|
return 0;
|
2017-05-15 17:23:51 +02:00
|
|
|
|
|
|
|
|
if (pipe (fd) == -1) {
|
2019-01-31 13:29:21 +01:00
|
|
|
errsv = errno;
|
2019-01-31 17:22:18 +01:00
|
|
|
g_printerr (_("Failed to create pager pipe: %s\n"), nm_strerror_native (errsv));
|
2018-10-09 13:08:24 +02:00
|
|
|
return 0;
|
2017-05-15 17:23:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parent_pid = getpid ();
|
|
|
|
|
|
2018-10-09 13:08:24 +02:00
|
|
|
pager_pid = fork ();
|
|
|
|
|
if (pager_pid == -1) {
|
2019-01-31 13:29:21 +01:00
|
|
|
errsv = errno;
|
2019-01-31 17:22:18 +01:00
|
|
|
g_printerr (_("Failed to fork pager: %s\n"), nm_strerror_native (errsv));
|
2017-11-14 14:22:21 +01:00
|
|
|
nm_close (fd[0]);
|
|
|
|
|
nm_close (fd[1]);
|
2018-10-09 13:08:24 +02:00
|
|
|
return 0;
|
2017-05-15 17:23:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* In the child start the pager */
|
2018-10-09 13:08:24 +02:00
|
|
|
if (pager_pid == 0) {
|
2017-05-15 17:23:51 +02:00
|
|
|
dup2 (fd[0], STDIN_FILENO);
|
2017-11-14 14:22:21 +01:00
|
|
|
nm_close (fd[0]);
|
|
|
|
|
nm_close (fd[1]);
|
2017-05-15 17:23:51 +02:00
|
|
|
|
|
|
|
|
setenv ("LESS", "FRSXMK", 1);
|
|
|
|
|
setenv ("LESSCHARSET", "utf-8", 1);
|
|
|
|
|
|
|
|
|
|
/* Make sure the pager goes away when the parent dies */
|
|
|
|
|
if (prctl (PR_SET_PDEATHSIG, SIGTERM) < 0)
|
|
|
|
|
_exit (EXIT_FAILURE);
|
|
|
|
|
|
|
|
|
|
/* Check whether our parent died before we were able
|
|
|
|
|
* to set the death signal */
|
|
|
|
|
if (getppid () != parent_pid)
|
|
|
|
|
_exit (EXIT_SUCCESS);
|
|
|
|
|
|
|
|
|
|
if (pager) {
|
|
|
|
|
execlp (pager, pager, NULL);
|
|
|
|
|
execl ("/bin/sh", "sh", "-c", pager, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Debian's alternatives command for pagers is
|
|
|
|
|
* called 'pager'. Note that we do not call
|
|
|
|
|
* sensible-pagers here, since that is just a
|
|
|
|
|
* shell script that implements a logic that
|
|
|
|
|
* is similar to this one anyway, but is
|
|
|
|
|
* Debian-specific. */
|
|
|
|
|
execlp ("pager", "pager", NULL);
|
|
|
|
|
|
|
|
|
|
execlp ("less", "less", NULL);
|
|
|
|
|
execlp ("more", "more", NULL);
|
|
|
|
|
|
|
|
|
|
pager_fallback ();
|
|
|
|
|
/* not reached */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return in the parent */
|
2019-01-31 13:29:21 +01:00
|
|
|
if (dup2 (fd[1], STDOUT_FILENO) < 0) {
|
|
|
|
|
errsv = errno;
|
2019-01-31 17:22:18 +01:00
|
|
|
g_printerr (_("Failed to duplicate pager pipe: %s\n"), nm_strerror_native (errsv));
|
2019-01-31 13:29:21 +01:00
|
|
|
}
|
|
|
|
|
if (dup2 (fd[1], STDERR_FILENO) < 0) {
|
|
|
|
|
errsv = errno;
|
2019-01-31 17:22:18 +01:00
|
|
|
g_printerr (_("Failed to duplicate pager pipe: %s\n"), nm_strerror_native (errsv));
|
2019-01-31 13:29:21 +01:00
|
|
|
}
|
2017-05-15 17:23:51 +02:00
|
|
|
|
2017-11-14 14:22:21 +01:00
|
|
|
nm_close (fd[0]);
|
|
|
|
|
nm_close (fd[1]);
|
2018-10-09 13:08:24 +02:00
|
|
|
return pager_pid;
|
2017-05-15 17:23:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2017-03-27 15:42:49 +02:00
|
|
|
static const char *
|
2018-03-29 12:25:14 +02:00
|
|
|
get_value_to_print (const NmcConfig *nmc_config,
|
2017-03-30 13:20:40 +02:00
|
|
|
const NmcOutputField *field,
|
2013-05-22 08:36:09 +02:00
|
|
|
gboolean field_name,
|
|
|
|
|
const char *not_set_str,
|
2017-03-27 15:42:49 +02:00
|
|
|
char **out_to_free)
|
2013-05-22 08:36:09 +02:00
|
|
|
{
|
2015-02-13 08:54:32 +01:00
|
|
|
gboolean is_array = field->value_is_array;
|
2017-03-30 13:20:40 +02:00
|
|
|
const char *value;
|
2017-03-27 15:42:49 +02:00
|
|
|
const char *out;
|
2017-03-30 13:20:40 +02:00
|
|
|
gs_free char *free_value = NULL;
|
|
|
|
|
|
|
|
|
|
nm_assert (out_to_free && !*out_to_free);
|
2013-05-22 08:36:09 +02:00
|
|
|
|
|
|
|
|
if (field_name)
|
2017-04-20 10:22:52 +02:00
|
|
|
value = nm_meta_abstract_info_get_name (field->info, FALSE);
|
2017-03-30 13:20:40 +02:00
|
|
|
else {
|
2017-01-17 11:58:40 +01:00
|
|
|
value = field->value
|
|
|
|
|
? (is_array
|
2017-03-30 13:20:40 +02:00
|
|
|
? (free_value = g_strjoinv (" | ", (char **) field->value))
|
|
|
|
|
: (*((const char *) field->value))
|
|
|
|
|
? field->value
|
|
|
|
|
: not_set_str)
|
|
|
|
|
: not_set_str;
|
|
|
|
|
}
|
2015-02-13 08:54:32 +01:00
|
|
|
|
|
|
|
|
/* colorize the value */
|
2018-03-29 12:31:33 +02:00
|
|
|
out = colorize_string (nmc_config, field->color, value, out_to_free);
|
2017-03-30 13:20:40 +02:00
|
|
|
|
|
|
|
|
if (out && out == free_value) {
|
|
|
|
|
nm_assert (!*out_to_free);
|
|
|
|
|
*out_to_free = g_steal_pointer (&free_value);
|
|
|
|
|
}
|
2015-02-13 08:54:32 +01:00
|
|
|
|
|
|
|
|
return out;
|
2013-05-22 08:36:09 +02:00
|
|
|
}
|
|
|
|
|
|
2010-03-20 14:08:06 +01:00
|
|
|
/*
|
|
|
|
|
* Print both headers or values of 'field_values' array.
|
2013-05-22 08:36:09 +02:00
|
|
|
* Entries to print and their order are specified via indices in
|
2017-03-31 12:55:43 +02:00
|
|
|
* 'nmc->indices' array.
|
2013-05-22 08:36:09 +02:00
|
|
|
* Various flags influencing the output of fields are set up in the first item
|
|
|
|
|
* of 'field_values' array.
|
2010-03-20 14:08:06 +01:00
|
|
|
*/
|
2010-03-18 15:39:15 +01:00
|
|
|
void
|
2017-03-31 12:55:43 +02:00
|
|
|
print_required_fields (const NmcConfig *nmc_config,
|
|
|
|
|
NmcOfFlags of_flags,
|
|
|
|
|
const GArray *indices,
|
|
|
|
|
const char *header_name,
|
|
|
|
|
int indent,
|
|
|
|
|
const NmcOutputField *field_values)
|
2010-03-18 15:39:15 +01:00
|
|
|
{
|
2017-04-06 10:46:16 +02:00
|
|
|
nm_auto_free_gstring GString *str = NULL;
|
2010-03-18 15:39:15 +01:00
|
|
|
int width1, width2;
|
|
|
|
|
int table_width = 0;
|
2017-04-06 10:46:16 +02:00
|
|
|
const char *not_set_str;
|
2012-01-03 15:07:17 +01:00
|
|
|
int i;
|
2017-03-31 12:12:21 +02:00
|
|
|
gboolean main_header_add = of_flags & NMC_OF_FLAG_MAIN_HEADER_ADD;
|
|
|
|
|
gboolean main_header_only = of_flags & NMC_OF_FLAG_MAIN_HEADER_ONLY;
|
|
|
|
|
gboolean field_names = of_flags & NMC_OF_FLAG_FIELD_NAMES;
|
|
|
|
|
gboolean section_prefix = of_flags & NMC_OF_FLAG_SECTION_PREFIX;
|
2010-03-18 15:39:15 +01:00
|
|
|
|
2020-04-02 13:59:53 +02:00
|
|
|
nm_cli_spawn_pager (nmc_config, &nm_cli.pager_data);
|
2017-05-15 17:23:51 +02:00
|
|
|
|
2017-02-03 18:06:51 +01:00
|
|
|
/* --- Main header --- */
|
2018-05-02 10:47:20 +02:00
|
|
|
if ( nmc_config->print_output == NMC_PRINT_PRETTY
|
|
|
|
|
&& ( main_header_add
|
|
|
|
|
|| main_header_only)) {
|
2017-04-06 10:46:16 +02:00
|
|
|
gs_free char *line = NULL;
|
|
|
|
|
int header_width;
|
|
|
|
|
|
|
|
|
|
header_width = nmc_string_screen_width (header_name, NULL) + 4;
|
2017-02-03 18:06:51 +01:00
|
|
|
|
2017-04-06 10:46:16 +02:00
|
|
|
if (nmc_config->multiline_output) {
|
|
|
|
|
table_width = NM_MAX (header_width, ML_HEADER_WIDTH);
|
2010-03-20 14:08:06 +01:00
|
|
|
line = g_strnfill (ML_HEADER_WIDTH, '=');
|
2017-02-03 18:06:51 +01:00
|
|
|
} else { /* tabular */
|
2017-04-06 10:46:16 +02:00
|
|
|
table_width = NM_MAX (table_width, header_width);
|
2017-02-03 18:06:51 +01:00
|
|
|
line = g_strnfill (table_width, '=');
|
2010-03-18 15:39:15 +01:00
|
|
|
}
|
|
|
|
|
|
2017-03-31 12:55:43 +02:00
|
|
|
width1 = strlen (header_name);
|
|
|
|
|
width2 = nmc_string_screen_width (header_name, NULL);
|
2017-02-03 18:06:51 +01:00
|
|
|
g_print ("%s\n", line);
|
2017-03-31 12:55:43 +02:00
|
|
|
g_print ("%*s\n", (table_width + width2)/2 + width1 - width2, header_name);
|
2017-02-03 18:06:51 +01:00
|
|
|
g_print ("%s\n", line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (main_header_only)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* No field headers are printed in terse mode nor for multiline output */
|
2018-05-02 10:47:20 +02:00
|
|
|
if ( ( nmc_config->print_output == NMC_PRINT_TERSE
|
|
|
|
|
|| nmc_config->multiline_output)
|
|
|
|
|
&& field_names)
|
2017-02-03 18:06:51 +01:00
|
|
|
return;
|
2012-01-03 15:07:17 +01:00
|
|
|
|
2017-04-06 10:46:16 +02:00
|
|
|
/* Don't replace empty strings in terse mode */
|
2018-05-02 10:47:20 +02:00
|
|
|
not_set_str = nmc_config->print_output == NMC_PRINT_TERSE ? "" : "--";
|
2017-02-03 18:06:51 +01:00
|
|
|
|
2017-04-06 10:46:16 +02:00
|
|
|
if (nmc_config->multiline_output) {
|
2017-03-31 12:55:43 +02:00
|
|
|
for (i = 0; i < indices->len; i++) {
|
|
|
|
|
int idx = g_array_index (indices, int, i);
|
2017-02-03 18:06:51 +01:00
|
|
|
gboolean is_array = field_values[idx].value_is_array;
|
|
|
|
|
|
|
|
|
|
/* section prefix can't be an array */
|
|
|
|
|
g_assert (!is_array || !section_prefix || idx != 0);
|
|
|
|
|
|
|
|
|
|
if (section_prefix && idx == 0) /* The first field is section prefix */
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (is_array) {
|
2017-03-27 15:42:49 +02:00
|
|
|
gs_free char *val_to_free = NULL;
|
2017-04-06 10:46:16 +02:00
|
|
|
const char **p, *val, *print_val;
|
2017-02-03 18:06:51 +01:00
|
|
|
int j;
|
|
|
|
|
|
2017-04-06 10:46:16 +02:00
|
|
|
/* value is a null-terminated string array */
|
|
|
|
|
|
2017-02-03 18:06:51 +01:00
|
|
|
for (p = (const char **) field_values[idx].value, j = 1; p && *p; p++, j++) {
|
2017-04-06 10:46:16 +02:00
|
|
|
gs_free char *tmp = NULL;
|
|
|
|
|
|
|
|
|
|
val = *p ?: not_set_str;
|
2018-03-29 12:31:33 +02:00
|
|
|
print_val = colorize_string (nmc_config, field_values[idx].color,
|
2017-03-27 15:42:49 +02:00
|
|
|
val, &val_to_free);
|
2017-02-03 18:06:51 +01:00
|
|
|
tmp = g_strdup_printf ("%s%s%s[%d]:",
|
|
|
|
|
section_prefix ? (const char*) field_values[0].value : "",
|
2013-05-22 08:36:09 +02:00
|
|
|
section_prefix ? "." : "",
|
2017-04-20 10:22:52 +02:00
|
|
|
nm_meta_abstract_info_get_name (field_values[idx].info, FALSE),
|
2017-02-03 18:06:51 +01:00
|
|
|
j);
|
2013-05-28 09:21:02 +02:00
|
|
|
width1 = strlen (tmp);
|
|
|
|
|
width2 = nmc_string_screen_width (tmp, NULL);
|
2018-05-02 10:47:20 +02:00
|
|
|
g_print ("%-*s%s\n",
|
|
|
|
|
(int) (nmc_config->print_output == NMC_PRINT_TERSE
|
|
|
|
|
? 0
|
|
|
|
|
: ML_VALUE_INDENT + width1 - width2),
|
|
|
|
|
tmp,
|
|
|
|
|
print_val);
|
2012-01-03 15:07:17 +01:00
|
|
|
}
|
2017-02-03 18:06:51 +01:00
|
|
|
} else {
|
2017-04-06 10:46:16 +02:00
|
|
|
gs_free char *val_to_free = NULL;
|
|
|
|
|
gs_free char *tmp = NULL;
|
2017-02-03 18:06:51 +01:00
|
|
|
const char *hdr_name = (const char*) field_values[0].value;
|
|
|
|
|
const char *val = (const char*) field_values[idx].value;
|
2017-03-27 15:42:49 +02:00
|
|
|
const char *print_val;
|
2017-04-06 10:46:16 +02:00
|
|
|
|
|
|
|
|
/* value is a string */
|
2017-02-03 18:06:51 +01:00
|
|
|
|
|
|
|
|
val = val && *val ? val : not_set_str;
|
2018-03-29 12:31:33 +02:00
|
|
|
print_val = colorize_string (nmc_config, field_values[idx].color,
|
2017-03-27 15:42:49 +02:00
|
|
|
val, &val_to_free);
|
2017-02-03 18:06:51 +01:00
|
|
|
tmp = g_strdup_printf ("%s%s%s:",
|
|
|
|
|
section_prefix ? hdr_name : "",
|
|
|
|
|
section_prefix ? "." : "",
|
2017-04-20 10:22:52 +02:00
|
|
|
nm_meta_abstract_info_get_name (field_values[idx].info, FALSE));
|
2017-02-03 18:06:51 +01:00
|
|
|
width1 = strlen (tmp);
|
|
|
|
|
width2 = nmc_string_screen_width (tmp, NULL);
|
2018-05-02 10:47:20 +02:00
|
|
|
g_print ("%-*s%s\n",
|
|
|
|
|
(int) ( nmc_config->print_output == NMC_PRINT_TERSE
|
|
|
|
|
? 0
|
|
|
|
|
: ML_VALUE_INDENT + width1 - width2),
|
|
|
|
|
tmp,
|
|
|
|
|
print_val);
|
2010-03-18 15:39:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
2018-05-02 10:47:20 +02:00
|
|
|
if (nmc_config->print_output == NMC_PRINT_PRETTY) {
|
2017-04-06 10:46:16 +02:00
|
|
|
gs_free char *line = NULL;
|
|
|
|
|
|
|
|
|
|
g_print ("%s\n", (line = g_strnfill (ML_HEADER_WIDTH, '-')));
|
2017-02-03 18:06:51 +01:00
|
|
|
}
|
|
|
|
|
|
2010-03-18 15:39:15 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* --- Tabular mode: each line = one object --- */
|
2017-02-03 18:06:51 +01:00
|
|
|
|
2010-03-18 15:39:15 +01:00
|
|
|
str = g_string_new (NULL);
|
|
|
|
|
|
2017-03-31 12:55:43 +02:00
|
|
|
for (i = 0; i < indices->len; i++) {
|
2017-03-27 15:42:49 +02:00
|
|
|
gs_free char *val_to_free = NULL;
|
2017-04-06 10:46:16 +02:00
|
|
|
int idx;
|
|
|
|
|
const char *value;
|
|
|
|
|
|
|
|
|
|
idx = g_array_index (indices, int, i);
|
|
|
|
|
|
2018-03-29 12:25:14 +02:00
|
|
|
value = get_value_to_print (nmc_config, (NmcOutputField *) field_values+idx, field_names,
|
2017-04-06 10:46:16 +02:00
|
|
|
not_set_str, &val_to_free);
|
2012-01-03 15:07:17 +01:00
|
|
|
|
2018-05-02 10:47:20 +02:00
|
|
|
if (nmc_config->print_output == NMC_PRINT_TERSE) {
|
2017-04-06 10:46:16 +02:00
|
|
|
if (nmc_config->escape_values) {
|
2010-03-18 15:39:15 +01:00
|
|
|
const char *p = value;
|
|
|
|
|
while (*p) {
|
|
|
|
|
if (*p == ':' || *p == '\\')
|
|
|
|
|
g_string_append_c (str, '\\'); /* Escaping by '\' */
|
|
|
|
|
g_string_append_c (str, *p);
|
|
|
|
|
p++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-01-03 15:07:17 +01:00
|
|
|
else
|
2010-03-18 15:39:15 +01:00
|
|
|
g_string_append_printf (str, "%s", value);
|
|
|
|
|
g_string_append_c (str, ':'); /* Column separator */
|
|
|
|
|
} else {
|
|
|
|
|
width1 = strlen (value);
|
2018-09-14 23:49:20 -04:00
|
|
|
width2 = nmc_string_screen_width (value, NULL); /* Width of the string (in screen columns) */
|
2017-01-17 11:58:40 +01:00
|
|
|
g_string_append_printf (str, "%-*s", field_values[idx].width + width1 - width2, strlen (value) > 0 ? value : not_set_str);
|
2010-03-18 15:39:15 +01:00
|
|
|
g_string_append_c (str, ' '); /* Column separator */
|
|
|
|
|
table_width += field_values[idx].width + width1 - width2 + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-20 14:08:06 +01:00
|
|
|
/* Print actual values */
|
2017-02-03 18:06:51 +01:00
|
|
|
if (str->len > 0) {
|
2010-03-18 15:39:15 +01:00
|
|
|
g_string_truncate (str, str->len-1); /* Chop off last column separator */
|
2017-03-31 12:55:43 +02:00
|
|
|
if (indent > 0) {
|
2017-04-06 10:46:16 +02:00
|
|
|
gs_free char *indent_str = NULL;
|
|
|
|
|
|
|
|
|
|
g_string_prepend (str, (indent_str = g_strnfill (indent, ' ')));
|
2010-03-18 15:39:15 +01:00
|
|
|
}
|
2017-04-06 10:46:16 +02:00
|
|
|
|
2014-09-19 16:04:40 -04:00
|
|
|
g_print ("%s\n", str->str);
|
2010-03-18 15:39:15 +01:00
|
|
|
|
2017-02-03 18:06:51 +01:00
|
|
|
/* Print horizontal separator */
|
2018-05-02 10:47:20 +02:00
|
|
|
if ( nmc_config->print_output == NMC_PRINT_PRETTY
|
|
|
|
|
&& field_names) {
|
2017-04-06 10:46:16 +02:00
|
|
|
gs_free char *line = NULL;
|
|
|
|
|
|
|
|
|
|
g_print ("%s\n", (line = g_strnfill (table_width, '-')));
|
2010-03-18 15:39:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-22 08:36:09 +02:00
|
|
|
void
|
2017-03-30 13:46:09 +02:00
|
|
|
print_data_prepare_width (GPtrArray *output_data)
|
2013-05-22 08:36:09 +02:00
|
|
|
{
|
|
|
|
|
int i, j;
|
|
|
|
|
size_t len;
|
|
|
|
|
NmcOutputField *row;
|
|
|
|
|
int num_fields = 0;
|
|
|
|
|
|
2017-03-30 13:02:24 +02:00
|
|
|
if (!output_data || output_data->len < 1)
|
2013-05-22 08:36:09 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* How many fields? */
|
2017-03-30 13:02:24 +02:00
|
|
|
row = g_ptr_array_index (output_data, 0);
|
cli: split tracking of meta data out of NmcOutputField
When generating output data, nmcli iterates over a list of
property-descriptors (nmc_fields_ip4_config), creates an intermediate
array (output_data) and finally prints it.
However, previously both the meta data (nmc_fields_ip4_config) and
the intermediate format use the same type NmcOutputField. This means,
certain fields are relevant to describe a property, and other fields
are output/formatting fields.
Split this up. Now, the meta data is tracked in form of an NMMetaAbstractInfo
lists. This separates the information about properties from intermediate steps
during creation of the output.
Note that currently functions like print_ip4_config() still have all the
knowledge about how to generate the output. That is wrong, instead, the
meta data (NMMetaAbstractInfo) should describe how to create the output
and then all those functions could be replaced. This means, later we want
to add more knowledge to the NMMetaAbstractInfo, so it is important to
keep them separate from NmcOutputField.
2017-03-31 19:18:16 +02:00
|
|
|
while (row->info) {
|
2013-05-22 08:36:09 +02:00
|
|
|
num_fields++;
|
|
|
|
|
row++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Find out maximal string lengths */
|
|
|
|
|
for (i = 0; i < num_fields; i++) {
|
|
|
|
|
size_t max_width = 0;
|
2017-03-30 13:02:24 +02:00
|
|
|
for (j = 0; j < output_data->len; j++) {
|
2017-03-27 15:42:49 +02:00
|
|
|
gboolean field_names;
|
|
|
|
|
gs_free char * val_to_free = NULL;
|
|
|
|
|
const char *value;
|
|
|
|
|
|
2017-03-30 13:02:24 +02:00
|
|
|
row = g_ptr_array_index (output_data, j);
|
2013-05-22 08:36:09 +02:00
|
|
|
field_names = row[0].flags & NMC_OF_FLAG_FIELD_NAMES;
|
2018-03-29 12:25:14 +02:00
|
|
|
value = get_value_to_print (NULL, row+i, field_names, "--", &val_to_free);
|
2013-05-22 08:36:09 +02:00
|
|
|
len = nmc_string_screen_width (value, NULL);
|
|
|
|
|
max_width = len > max_width ? len : max_width;
|
|
|
|
|
}
|
2017-03-30 13:02:24 +02:00
|
|
|
for (j = 0; j < output_data->len; j++) {
|
|
|
|
|
row = g_ptr_array_index (output_data, j);
|
2013-05-22 08:36:09 +02:00
|
|
|
row[i].width = max_width + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-03-30 13:46:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2017-03-31 13:21:47 +02:00
|
|
|
print_data (const NmcConfig *nmc_config,
|
|
|
|
|
const GArray *indices,
|
|
|
|
|
const char *header_name,
|
|
|
|
|
int indent,
|
|
|
|
|
const NmcOutputData *out)
|
2017-03-30 13:46:09 +02:00
|
|
|
{
|
|
|
|
|
guint i;
|
2013-05-22 08:36:09 +02:00
|
|
|
|
2017-03-30 13:46:09 +02:00
|
|
|
for (i = 0; i < out->output_data->len; i++) {
|
2017-03-31 12:12:21 +02:00
|
|
|
const NmcOutputField *field_values = g_ptr_array_index (out->output_data, i);
|
|
|
|
|
|
|
|
|
|
print_required_fields (nmc_config, field_values[0].flags,
|
2017-03-31 13:21:47 +02:00
|
|
|
indices, header_name,
|
|
|
|
|
indent, field_values);
|
2013-05-22 08:36:09 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|