merge: branch 'fg/nmcli_parsing_rh1391170'

https://bugzilla.redhat.com/show_bug.cgi?id=1391170
This commit is contained in:
Francesco Giudici 2017-03-28 10:55:48 +02:00
commit 5526769950
9 changed files with 663 additions and 550 deletions

View file

@ -1801,10 +1801,7 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
tmpl = nmc_fields_con_show;
tmpl_len = sizeof (nmc_fields_con_show);
nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &err);
if (err) {
goto finish;
}
if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &err))
if (err)
goto finish;
/* Add headers */

View file

@ -92,8 +92,8 @@ NmcOutputField nmc_fields_dev_show_connections[] = {
{"AVAILABLE-CONNECTIONS", N_("AVAILABLE-CONNECTIONS")}, /* 2 */
{NULL, NULL}
};
#define NMC_FIELDS_DEV_SHOW_CONNECTIONS_ALL "AVAILABLE-CONNECTION-PATHS,AVAILABLE-CONNECTIONS"
#define NMC_FIELDS_DEV_SHOW_CONNECTIONS_COMMON "AVAILABLE-CONNECTION-PATHS,AVAILABLE-CONNECTIONS"
#define NMC_FIELDS_DEV_SHOW_CONNECTIONS_ALL "NAME,AVAILABLE-CONNECTION-PATHS,AVAILABLE-CONNECTIONS"
#define NMC_FIELDS_DEV_SHOW_CONNECTIONS_COMMON "NAME,AVAILABLE-CONNECTION-PATHS,AVAILABLE-CONNECTIONS"
/* Available fields for 'device show' - CAPABILITIES part */
NmcOutputField nmc_fields_dev_show_cap[] = {
@ -1084,7 +1084,7 @@ show_device_info (NMDevice *device, NmCli *nmc)
if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
fields_str = fields_common;
else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
else if (strcasecmp (nmc->required_fields, "all") == 0)
fields_str = fields_all;
else
fields_str = nmc->required_fields;
@ -1097,8 +1097,10 @@ show_device_info (NMDevice *device, NmCli *nmc)
return FALSE;
}
/* Main header */
/* Main header (pretty only) */
nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_device_get_iface (device));
/* Lazy way to retrieve sorted array from 0 to the number of dev fields */
nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_DEV_SHOW_GENERAL_ALL,
nmc_fields_dev_show_general, FALSE, NULL, NULL);
@ -1492,12 +1494,6 @@ do_devices_status (NmCli *nmc, int argc, char **argv)
if (nmc->complete)
return nmc->return_value;
if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) {
g_string_printf (nmc->return_text, _("Error: %s."), error->message);
g_error_free (error);
return NMC_RESULT_ERROR_USER_INPUT;
}
while (argc > 0) {
g_printerr (_("Unknown parameter: %s\n"), *argv);
argc--;
@ -3617,14 +3613,6 @@ static NMCCommand device_wifi_cmds[] = {
static NMCResultCode
do_device_wifi (NmCli *nmc, int argc, char **argv)
{
GError *error = NULL;
if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) {
g_string_printf (nmc->return_text, _("Error: %s."), error->message);
g_error_free (error);
return NMC_RESULT_ERROR_USER_INPUT;
}
nmc_do_cmd (nmc, device_wifi_cmds, *argv, argc, argv);
return nmc->return_value;
@ -3789,14 +3777,6 @@ static NMCCommand device_lldp_cmds[] = {
static NMCResultCode
do_device_lldp (NmCli *nmc, int argc, char **argv)
{
GError *error = NULL;
if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) {
g_string_printf (nmc->return_text, _("Error: %s."), error->message);
g_error_free (error);
return NMC_RESULT_ERROR_USER_INPUT;
}
if (!nmc->mode_specified)
nmc->multiline_output = TRUE; /* multiline mode is default for 'device lldp' */

View file

@ -385,13 +385,6 @@ show_nm_status (NmCli *nmc, const char *pretty_header_name, const char *print_fl
static NMCResultCode
do_general_status (NmCli *nmc, int argc, char **argv)
{
gs_free_error GError *error = NULL;
if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) {
g_string_printf (nmc->return_text, _("Error: %s."), error->message);
return NMC_RESULT_ERROR_USER_INPUT;
}
if (nmc->complete)
return nmc->return_value;
@ -566,13 +559,6 @@ show_nm_permissions (NmCli *nmc)
static NMCResultCode
do_general_permissions (NmCli *nmc, int argc, char **argv)
{
gs_free_error GError *error = NULL;
if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) {
g_string_printf (nmc->return_text, _("Error: %s."), error->message);
return NMC_RESULT_ERROR_USER_INPUT;
}
if (nmc->complete)
return nmc->return_value;
@ -655,12 +641,6 @@ do_general_logging (NmCli *nmc, int argc, char **argv)
gs_free_error GError *error = NULL;
if (argc == 0) {
if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) {
g_string_printf (nmc->return_text, _("Error: %s."), error->message);
g_error_free (error);
return NMC_RESULT_ERROR_USER_INPUT;
}
if (nmc->complete)
return nmc->return_value;
@ -918,17 +898,12 @@ static NMCResultCode
do_radio_all (NmCli *nmc, int argc, char **argv)
{
gboolean enable_flag;
gs_free_error GError *error = NULL;
if (argc == 0) {
if (nmc->complete)
return nmc->return_value;
/* no argument, show all radio switches */
if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) {
g_string_printf (nmc->return_text, _("Error: %s."), error->message);
return NMC_RESULT_ERROR_USER_INPUT;
}
show_nm_status (nmc, _("Radio switches"), NMC_FIELDS_NM_STATUS_RADIO);
} else {
if (nmc->complete) {

View file

@ -171,17 +171,18 @@ usage (void)
g_printerr (_("Usage: nmcli [OPTIONS] OBJECT { COMMAND | help }\n"
"\n"
"OPTIONS\n"
" -t[erse] terse output\n"
" -p[retty] pretty output\n"
" -m[ode] tabular|multiline output mode\n"
" -c[olors] auto|yes|no whether to use colors in output\n"
" -f[ields] <field1,field2,...>|all|common specify fields to output\n"
" -e[scape] yes|no escape columns separators in values\n"
" -a[sk] ask for missing parameters\n"
" -s[how-secrets] allow displaying passwords\n"
" -w[ait] <seconds> set timeout waiting for finishing operations\n"
" -v[ersion] show program version\n"
" -h[elp] print this help\n"
" -t[erse] terse output\n"
" -p[retty] pretty output\n"
" -m[ode] tabular|multiline output mode\n"
" -c[olors] auto|yes|no whether to use colors in output\n"
" -f[ields] <field1,field2,...>|all|common specify fields to output\n"
" -g[et-values] <field1,field2,...>|all|common shortcut for -m tabular -t -f\n"
" -e[scape] yes|no escape columns separators in values\n"
" -a[sk] ask for missing parameters\n"
" -s[how-secrets] allow displaying passwords\n"
" -w[ait] <seconds> set timeout waiting for finishing operations\n"
" -v[ersion] show program version\n"
" -h[elp] print this help\n"
"\n"
"OBJECT\n"
" g[eneral] NetworkManager's general status and operations\n"
@ -231,7 +232,7 @@ process_command_line (NmCli *nmc, int argc, char **argv)
if (argc == 1 && nmc->complete) {
nmc_complete_strings (opt, "--terse", "--pretty", "--mode", "--colors", "--escape",
"--fields", "--nocheck", "--ask", "--show-secrets",
"--wait", "--version", "--help", NULL);
"--get-values", "--wait", "--version", "--help", NULL);
}
if (opt[1] == '-') {
@ -332,6 +333,21 @@ process_command_line (NmCli *nmc, int argc, char **argv)
if (argc == 1 && nmc->complete)
complete_fields (argv[0]);
nmc->required_fields = g_strdup (argv[0]);
} else if (matches (opt, "-get-values")) {
if (next_arg (&argc, &argv) != 0) {
g_string_printf (nmc->return_text, _("Error: fields for '%s' options are missing."), opt);
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
return FALSE;
}
if (argc == 1 && nmc->complete)
complete_fields (argv[0]);
nmc->required_fields = g_strdup (argv[0]);
nmc->print_output = NMC_PRINT_TERSE;
/* We want fixed tabular mode here, but just set the mode specified and rely on the initialization
* in nmc_init: in this way we allow use of "-m multiline" to swap the output mode also if placed
* before the "-g <field>" option (-g may be still more practical and easy to remember than -t -f).
*/
nmc->mode_specified = TRUE;
} else if (matches (opt, "-nocheck")) {
/* ignore for backward compatibility */
} else if (matches (opt, "-ask")) {

File diff suppressed because it is too large Load diff

View file

@ -941,24 +941,6 @@ nmc_get_allowed_fields (const NmcOutputField fields_array[], int group_idx)
return g_string_free (allowed_fields, FALSE);
}
gboolean
nmc_terse_option_check (NMCPrintOutput print_output, const char *fields, GError **error)
{
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (print_output == NMC_PRINT_TERSE) {
if (!fields) {
g_set_error_literal (error, NMCLI_ERROR, 0, _("Option '--terse' requires specifying '--fields'"));
return FALSE;
} else if ( !strcasecmp (fields, "all")
|| !strcasecmp (fields, "common")) {
g_set_error (error, NMCLI_ERROR, 0, _("Option '--terse' requires specific '--fields' option values , not '%s'"), fields);
return FALSE;
}
}
return TRUE;
}
NmcOutputField *
nmc_dup_fields_array (NmcOutputField fields[], size_t size, guint32 flags)
{
@ -992,54 +974,55 @@ nmc_empty_output_fields (NmCli *nmc)
}
}
static char *
static const char *
colorize_string (NmCli *nmc,
NmcTermColor color,
NmcTermFormat color_fmt,
const char *str,
gboolean *dealloc)
char **out_to_free)
{
char *out;
const char *out = str;
if ( use_colors (nmc)
&& (color != NMC_TERM_COLOR_NORMAL || color_fmt != NMC_TERM_FORMAT_NORMAL)) {
out = nmc_colorize (nmc, color, color_fmt, "%s", str);
*dealloc = TRUE;
} else {
out = (char *) str;
*dealloc = FALSE;
*out_to_free = nmc_colorize (nmc, color, color_fmt, "%s", str);
out = *out_to_free;
}
return out;
}
static char *
static const char *
get_value_to_print (NmCli *nmc,
NmcOutputField *field,
gboolean field_name,
const char *not_set_str,
gboolean *dealloc)
char **out_to_free)
{
gboolean is_array = field->value_is_array;
char *value, *out;
gboolean free_value, free_out;
char *value;
const char *out;
gboolean free_value;
if (field_name)
value = _(field->name_l10n);
else
value = field->value ?
(is_array ? g_strjoinv (" | ", (char **) field->value) :
(char *) field->value) :
(char *) not_set_str;
value = field->value
? (is_array
? g_strjoinv (" | ", (char **) field->value)
: (*((char *) field->value)
? (char *) field->value
: (char *) not_set_str))
: (char *) not_set_str;
free_value = field->value && is_array && !field_name;
/* colorize the value */
out = colorize_string (nmc, field->color, field->color_fmt, value, &free_out);
if (free_out) {
out = colorize_string (nmc, field->color, field->color_fmt, value, out_to_free);
if (*out_to_free) {
if (free_value)
g_free (value);
*dealloc = TRUE;
} else
*dealloc = free_value;
} else if (free_value)
*out_to_free = value;
return out;
}
@ -1072,104 +1055,111 @@ print_required_fields (NmCli *nmc, const NmcOutputField field_values[])
gboolean section_prefix = field_values[0].flags & NMC_OF_FLAG_SECTION_PREFIX;
gboolean main_header = main_header_add || main_header_only;
/* No headers are printed in terse mode:
* - neither main header nor field (column) names
*/
if ((main_header_only || field_names) && terse)
return;
enum { ML_HEADER_WIDTH = 79 };
enum { ML_VALUE_INDENT = 40 };
if (multiline) {
/* --- Multiline mode --- */
enum { ML_HEADER_WIDTH = 79 };
enum { ML_VALUE_INDENT = 40 };
if (main_header && pretty) {
/* Print the main header */
int header_width = nmc_string_screen_width (fields.header_name, NULL) + 4;
/* --- Main header --- */
if (main_header && pretty) {
int header_width = nmc_string_screen_width (fields.header_name, NULL) + 4;
if (multiline) {
table_width = header_width < ML_HEADER_WIDTH ? ML_HEADER_WIDTH : header_width;
line = g_strnfill (ML_HEADER_WIDTH, '=');
width1 = strlen (fields.header_name);
width2 = nmc_string_screen_width (fields.header_name, NULL);
g_print ("%s\n", line);
g_print ("%*s\n", (table_width + width2)/2 + width1 - width2, fields.header_name);
g_print ("%s\n", line);
g_free (line);
} else { /* tabular */
table_width = table_width < header_width ? header_width : table_width;
line = g_strnfill (table_width, '=');
}
/* Print values */
if (!main_header_only && !field_names) {
for (i = 0; i < fields.indices->len; i++) {
char *tmp;
gboolean free_print_val;
int idx = g_array_index (fields.indices, int, i);
gboolean is_array = field_values[idx].value_is_array;
width1 = strlen (fields.header_name);
width2 = nmc_string_screen_width (fields.header_name, NULL);
g_print ("%s\n", line);
g_print ("%*s\n", (table_width + width2)/2 + width1 - width2, fields.header_name);
g_print ("%s\n", line);
g_free (line);
}
/* section prefix can't be an array */
g_assert (!is_array || !section_prefix || idx != 0);
if (main_header_only)
return;
if (section_prefix && idx == 0) /* The first field is section prefix */
continue;
/* No field headers are printed in terse mode nor for multiline output */
if ((terse || multiline) && field_names)
return;
if (is_array) {
/* value is a null-terminated string array */
const char **p, *val;
char *print_val;
int j;
if (terse)
not_set_str = ""; /* Don't replace empty strings in terse mode */
for (p = (const char **) field_values[idx].value, j = 1; p && *p; p++, j++) {
val = *p ? *p : not_set_str;
print_val = colorize_string (nmc, field_values[idx].color, field_values[idx].color_fmt,
val, &free_print_val);
tmp = g_strdup_printf ("%s%s%s[%d]:",
section_prefix ? (const char*) field_values[0].value : "",
section_prefix ? "." : "",
_(field_values[idx].name_l10n),
j);
width1 = strlen (tmp);
width2 = nmc_string_screen_width (tmp, NULL);
g_print ("%-*s%s\n", terse ? 0 : ML_VALUE_INDENT+width1-width2, tmp, print_val);
g_free (tmp);
if (free_print_val)
g_free (print_val);
}
} else {
/* value is a string */
const char *hdr_name = (const char*) field_values[0].value;
const char *val = (const char*) field_values[idx].value;
char *print_val;
val = val ? val : not_set_str;
if (multiline) {
for (i = 0; i < fields.indices->len; i++) {
char *tmp;
int idx = g_array_index (fields.indices, int, i);
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) {
/* value is a null-terminated string array */
const char **p, *val, *print_val;
gs_free char *val_to_free = NULL;
int j;
for (p = (const char **) field_values[idx].value, j = 1; p && *p; p++, j++) {
val = *p ? *p : not_set_str;
print_val = colorize_string (nmc, field_values[idx].color, field_values[idx].color_fmt,
val, &free_print_val);
tmp = g_strdup_printf ("%s%s%s:",
section_prefix ? hdr_name : "",
val, &val_to_free);
tmp = g_strdup_printf ("%s%s%s[%d]:",
section_prefix ? (const char*) field_values[0].value : "",
section_prefix ? "." : "",
_(field_values[idx].name_l10n));
_(field_values[idx].name_l10n),
j);
width1 = strlen (tmp);
width2 = nmc_string_screen_width (tmp, NULL);
g_print ("%-*s%s\n", terse ? 0 : ML_VALUE_INDENT+width1-width2, tmp, print_val);
g_free (tmp);
if (free_print_val)
g_free (print_val);
}
}
if (pretty) {
line = g_strnfill (ML_HEADER_WIDTH, '-');
g_print ("%s\n", line);
g_free (line);
} else {
/* value is a string */
const char *hdr_name = (const char*) field_values[0].value;
const char *val = (const char*) field_values[idx].value;
const char *print_val;
gs_free char *val_to_free = NULL;
val = val && *val ? val : not_set_str;
print_val = colorize_string (nmc, field_values[idx].color, field_values[idx].color_fmt,
val, &val_to_free);
tmp = g_strdup_printf ("%s%s%s:",
section_prefix ? hdr_name : "",
section_prefix ? "." : "",
_(field_values[idx].name_l10n));
width1 = strlen (tmp);
width2 = nmc_string_screen_width (tmp, NULL);
g_print ("%-*s%s\n", terse ? 0 : ML_VALUE_INDENT+width1-width2, tmp, print_val);
g_free (tmp);
}
}
if (pretty) {
line = g_strnfill (ML_HEADER_WIDTH, '-');
g_print ("%s\n", line);
g_free (line);
}
return;
}
/* --- Tabular mode: each line = one object --- */
str = g_string_new (NULL);
for (i = 0; i < fields.indices->len; i++) {
int idx = g_array_index (fields.indices, int, i);
gboolean dealloc;
char *value = get_value_to_print (nmc, (NmcOutputField *) field_values+idx, field_names,
not_set_str, &dealloc);
gs_free char *val_to_free = NULL;
const char *value = get_value_to_print (nmc, (NmcOutputField *) field_values+idx, field_names,
not_set_str, &val_to_free);
if (terse) {
if (escape) {
@ -1187,31 +1177,14 @@ print_required_fields (NmCli *nmc, const NmcOutputField field_values[])
} else {
width1 = strlen (value);
width2 = nmc_string_screen_width (value, NULL); /* Width of the string (in screen colums) */
g_string_append_printf (str, "%-*s", field_values[idx].width + width1 - width2, strlen (value) > 0 ? value : "--");
g_string_append_printf (str, "%-*s", field_values[idx].width + width1 - width2, strlen (value) > 0 ? value : not_set_str);
g_string_append_c (str, ' '); /* Column separator */
table_width += field_values[idx].width + width1 - width2 + 1;
}
if (dealloc)
g_free (value);
}
/* Print the main table header */
if (main_header && pretty) {
int header_width = nmc_string_screen_width (fields.header_name, NULL) + 4;
table_width = table_width < header_width ? header_width : table_width;
line = g_strnfill (table_width, '=');
width1 = strlen (fields.header_name);
width2 = nmc_string_screen_width (fields.header_name, NULL);
g_print ("%s\n", line);
g_print ("%*s\n", (table_width + width2)/2 + width1 - width2, fields.header_name);
g_print ("%s\n", line);
g_free (line);
}
/* Print actual values */
if (!main_header_only && str->len > 0) {
if (str->len > 0) {
g_string_truncate (str, str->len-1); /* Chop off last column separator */
if (fields.indent > 0) {
indent_str = g_strnfill (fields.indent, ' ');
@ -1219,11 +1192,9 @@ print_required_fields (NmCli *nmc, const NmcOutputField field_values[])
g_free (indent_str);
}
g_print ("%s\n", str->str);
}
/* Print horizontal separator */
if (!main_header_only && field_names && pretty) {
if (str->len > 0) {
/* Print horizontal separator */
if (field_names && pretty) {
line = g_strnfill (table_width, '-');
g_print ("%s\n", line);
g_free (line);
@ -1263,15 +1234,15 @@ print_data (NmCli *nmc)
for (i = 0; i < num_fields; i++) {
size_t max_width = 0;
for (j = 0; j < nmc->output_data->len; j++) {
gboolean field_names, dealloc;
char *value;
gboolean field_names;
gs_free char * val_to_free = NULL;
const char *value;
row = g_ptr_array_index (nmc->output_data, j);
field_names = row[0].flags & NMC_OF_FLAG_FIELD_NAMES;
value = get_value_to_print (NULL, row+i, field_names, "--", &dealloc);
value = get_value_to_print (NULL, row+i, field_names, "--", &val_to_free);
len = nmc_string_screen_width (value, NULL);
max_width = len > max_width ? len : max_width;
if (dealloc)
g_free (value);
}
for (j = 0; j < nmc->output_data->len; j++) {
row = g_ptr_array_index (nmc->output_data, j);

View file

@ -97,7 +97,6 @@ GArray *parse_output_fields (const char *fields_str,
GPtrArray **group_fields,
GError **error);
char *nmc_get_allowed_fields (const NmcOutputField fields_array[], int group_idx);
gboolean nmc_terse_option_check (NMCPrintOutput print_output, const char *fields, GError **error);
NmcOutputField *nmc_dup_fields_array (NmcOutputField fields[], size_t size, guint32 flags);
void nmc_empty_output_fields (NmCli *nmc);
void print_required_fields (NmCli *nmc, const NmcOutputField field_values[]);

View file

@ -254,6 +254,25 @@ B,DISPATCH</screen>
</para>
</example>
<example><title>Convenient field values retrieval for scripting</title>
<screen><prompt>$ </prompt><userinput>nmcli -g ip4.address connection show my-con-eth0</userinput>
192.168.1.12/24</screen>
<screen><prompt>$ </prompt><userinput>nmcli -g ip4.address,ip4.dns connection show my-con-eth0</userinput>
192.168.1.12/24
192.168.1.1</screen>
<screen><prompt>$ </prompt><userinput>nmcli -g ip4 connection show my-con-eth0</userinput>
IP4:192.168.1.12/24:192.168.1.1::192.168.1.1::</screen>
<para>
This example shows retrieval of ip4 connection field values via the --get-values
option. Multiple comma separated fields can be provided: they will be printed one
per line. If a whole section is provided instead of a single field, the name of the
section will be printed followed by all the related field values on the same line.
See also --terse, --mode, --fields and --escape options in <link linkend='nmcli'><citerefentry><refentrytitle>nmcli</refentrytitle><manvolnum>1</manvolnum></citerefentry></link>
manual page for more customized output.
</para>
</example>
<example><title>Escaping colon characters in tabular mode</title>
<screen><prompt>$ </prompt><userinput>nmcli -t -f general -e yes -m tab dev show eth0</userinput>
GENERAL:eth0:ethernet:Intel Corporation:82567LM Gigabit Network Connection:

View file

@ -205,10 +205,30 @@
command. <literal>common</literal> is used to print common field values of
the command.</para>
<para>If omitted, default is <literal>common</literal>. The option is
mandatory when <option>--terse</option> is used. In this case, generic values
<literal>all</literal> and <literal>common</literal> cannot be used. This
is to maintain compatibility when new fields are added in the future.</para>
<para>If omitted, default is <literal>common</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><group choice='plain'>
<arg choice='plain'><option>-g</option></arg>
<arg choice='plain'><option>--get-values</option></arg>
<group choice='req'>
<arg choice='plain' rep='repeat'><replaceable>field1</replaceable>,<replaceable>field2</replaceable></arg>
<arg choice='plain'>all</arg>
<arg choice='plain'>common</arg>
</group>
</group></term>
<listitem>
<para>This option is used to print values from specific fields. It is basically
a shortcut for <literal>--mode tabular --terse --fields</literal> and is a convenient
way to retrieve values for particular fields. The values are printed one per line
without headers.</para>
<para>If a section is specified instead of a field, the section name will be printed
followed by colon separated values of the fields belonging to that section, all on
the same line.</para>
</listitem>
</varlistentry>