diff options
| author | Michael Biebl <biebl@debian.org> | 2017-11-07 00:14:39 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2017-11-07 00:14:39 +0100 |
| commit | 90e8691111889a7b5f3c812f5a41f15a8a058913 (patch) | |
| tree | f101a879eca27c34a9bfa5f3da52266b22539a36 /clients/cli/utils.c | |
| parent | bdb6eeb0670658255c2a4c3c501c0a27fa8cfe55 (diff) | |
New upstream version 1.9.90 upstream/1.9.90
Diffstat (limited to 'clients/cli/utils.c')
| -rw-r--r-- | clients/cli/utils.c | 1507 |
1 files changed, 1010 insertions, 497 deletions
diff --git a/clients/cli/utils.c b/clients/cli/utils.c index 24686ec7..570bfb98 100644 --- a/clients/cli/utils.c +++ b/clients/cli/utils.c @@ -14,11 +14,14 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright 2010 - 2015 Red Hat, Inc. + * Copyright 2010 Lennart Poettering + * Copyright 2010 - 2017 Red Hat, Inc. */ #include "nm-default.h" +#include "utils.h" + #include <stdio.h> #include <string.h> #include <stdlib.h> @@ -26,26 +29,140 @@ #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> +#include <sys/auxv.h> +#include <sys/prctl.h> + +#include "nm-client-utils.h" +#include "nm-meta-setting-access.h" -#include "utils.h" #include "common.h" +#include "settings.h" -gboolean -matches (const char *cmd, const char *pattern) +#define ML_HEADER_WIDTH 79 +#define ML_VALUE_INDENT 40 + +/*****************************************************************************/ + +static const char * +_meta_type_nmc_generic_info_get_name (const NMMetaAbstractInfo *abstract_info, gboolean for_header) { - size_t len = strlen (cmd); - if (!len || len > strlen (pattern)) - return FALSE; - return memcmp (pattern, cmd, len) == 0; + const NmcMetaGenericInfo *info = (const NmcMetaGenericInfo *) abstract_info; + + if (for_header) + return info->name_header ?: info->name; + return info->name; } +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; + + *out_to_free = NULL; + NM_SET_OUT (out_len, NM_PTRARRAY_LEN (info->nested)); + return (const NMMetaAbstractInfo *const*) info->nested; +} + +static gconstpointer +_meta_type_nmc_generic_info_get_fcn (const NMMetaAbstractInfo *abstract_info, + const NMMetaEnvironment *environment, + gpointer environment_user_data, + gpointer target, + NMMetaAccessorGetType get_type, + NMMetaAccessorGetFlags get_flags, + NMMetaAccessorGetOutFlags *out_flags, + gpointer *out_to_free) +{ + const NmcMetaGenericInfo *info = (const NmcMetaGenericInfo *) abstract_info; + + nm_assert (!out_to_free || !*out_to_free); + nm_assert (out_flags && !*out_flags); + + if (!NM_IN_SET (get_type, + NM_META_ACCESSOR_GET_TYPE_PARSABLE, + NM_META_ACCESSOR_GET_TYPE_PRETTY, + NM_META_ACCESSOR_GET_TYPE_TERMFORMAT)) + g_return_val_if_reached (NULL); + + /* omitting the out_to_free value is only allowed for TERMFORMAT. */ + nm_assert (out_to_free || NM_IN_SET (get_type, NM_META_ACCESSOR_GET_TYPE_TERMFORMAT)); + + if (info->get_fcn) { + return info->get_fcn (environment, environment_user_data, + info, target, + get_type, + get_flags, + out_flags, + out_to_free); + } + + if (info->nested) { + NMC_HANDLE_TERMFORMAT (NM_META_TERM_COLOR_NORMAL); + return info->name; + } + + g_return_val_if_reached (NULL); +} + +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, +}; + +/*****************************************************************************/ + +static gboolean +use_colors (NmcColorOption color_option) +{ + if (color_option == NMC_USE_COLOR_AUTO) { + static NmcColorOption cached = NMC_USE_COLOR_AUTO; + + if (G_UNLIKELY (cached == NMC_USE_COLOR_AUTO)) { + if ( g_strcmp0 (g_getenv ("TERM"), "dumb") == 0 + || !isatty (STDOUT_FILENO)) + cached = NMC_USE_COLOR_NO; + else + cached = NMC_USE_COLOR_YES; + } + return cached == NMC_USE_COLOR_YES; + } + + return color_option == NMC_USE_COLOR_YES; +} + +static const char * +colorize_string (NmcColorOption color_option, + NMMetaTermColor color, + NMMetaTermFormat color_fmt, + const char *str, + char **out_to_free) +{ + const char *out = str; + + if ( use_colors (color_option) + && (color != NM_META_TERM_COLOR_NORMAL || color_fmt != NM_META_TERM_FORMAT_NORMAL)) { + *out_to_free = nmc_colorize (color_option, color, color_fmt, "%s", str); + out = *out_to_free; + } + + return out; +} + +/*****************************************************************************/ + 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")) - nmc->show_secrets = TRUE; + nmc->nmc_config_mutable.show_secrets = TRUE; else return FALSE; @@ -279,31 +396,31 @@ nmc_terminal_show_progress (const char *str) } const char * -nmc_term_color_sequence (NmcTermColor color) +nmc_term_color_sequence (NMMetaTermColor color) { switch (color) { - case NMC_TERM_COLOR_BLACK: + case NM_META_TERM_COLOR_BLACK: return "\33[30m"; break; - case NMC_TERM_COLOR_RED: + case NM_META_TERM_COLOR_RED: return "\33[31m"; break; - case NMC_TERM_COLOR_GREEN: + case NM_META_TERM_COLOR_GREEN: return "\33[32m"; break; - case NMC_TERM_COLOR_YELLOW: + case NM_META_TERM_COLOR_YELLOW: return "\33[33m"; break; - case NMC_TERM_COLOR_BLUE: + case NM_META_TERM_COLOR_BLUE: return "\33[34m"; break; - case NMC_TERM_COLOR_MAGENTA: + case NM_META_TERM_COLOR_MAGENTA: return "\33[35m"; break; - case NMC_TERM_COLOR_CYAN: + case NM_META_TERM_COLOR_CYAN: return "\33[36m"; break; - case NMC_TERM_COLOR_WHITE: + case NM_META_TERM_COLOR_WHITE: return "\33[37m"; break; default: @@ -313,7 +430,7 @@ nmc_term_color_sequence (NmcTermColor color) } /* Parses @str for color as string or number */ -NmcTermColor +NMMetaTermColor nmc_term_color_parse_string (const char *str, GError **error) { unsigned long color_int; @@ -321,7 +438,7 @@ nmc_term_color_parse_string (const char *str, GError **error) "blue", "magenta", "cyan", "white", NULL }; if (nmc_string_to_uint (str, TRUE, 0, 8, &color_int)) { - return (NmcTermColor) color_int; + return (NMMetaTermColor) color_int; } else { const char *color, **p; int i; @@ -329,32 +446,32 @@ nmc_term_color_parse_string (const char *str, GError **error) color = nmc_string_is_valid (str, colors, error); for (p = colors, i = 0; *p != NULL; p++, i++) { if (*p == color) - return (NmcTermColor) i; + return (NMMetaTermColor) i; } return -1; } } const char * -nmc_term_format_sequence (NmcTermFormat format) +nmc_term_format_sequence (NMMetaTermFormat format) { switch (format) { - case NMC_TERM_FORMAT_BOLD: + case NM_META_TERM_FORMAT_BOLD: return "\33[1m"; break; - case NMC_TERM_FORMAT_DIM: + case NM_META_TERM_FORMAT_DIM: return "\33[2m"; break; - case NMC_TERM_FORMAT_UNDERLINE: + case NM_META_TERM_FORMAT_UNDERLINE: return "\33[4m"; break; - case NMC_TERM_FORMAT_BLINK: + case NM_META_TERM_FORMAT_BLINK: return "\33[5m"; break; - case NMC_TERM_FORMAT_REVERSE: + case NM_META_TERM_FORMAT_REVERSE: return "\33[7m"; break; - case NMC_TERM_FORMAT_HIDDEN: + case NM_META_TERM_FORMAT_HIDDEN: return "\33[8m"; break; default: @@ -363,25 +480,8 @@ nmc_term_format_sequence (NmcTermFormat format) } } -static gboolean -use_colors (NmCli *nmc) -{ - if (nmc == NULL) - return FALSE; - - if (nmc->use_colors == NMC_USE_COLOR_AUTO) { - if ( g_strcmp0 (g_getenv ("TERM"), "dumb") == 0 - || !isatty (fileno (stdout))) - nmc->use_colors = NMC_USE_COLOR_NO; - else - nmc->use_colors = NMC_USE_COLOR_YES; - } - - return nmc->use_colors == NMC_USE_COLOR_YES; -} - char * -nmc_colorize (NmCli *nmc, NmcTermColor color, NmcTermFormat format, const char *fmt, ...) +nmc_colorize (NmcColorOption color_option, NMMetaTermColor color, NMMetaTermFormat format, const char *fmt, ...) { va_list args; char *str, *colored; @@ -392,7 +492,7 @@ nmc_colorize (NmCli *nmc, NmcTermColor color, NmcTermFormat format, const char * str = g_strdup_vprintf (fmt, args); va_end (args); - if (!use_colors (nmc)) + if (!use_colors (color_option)) return str; ansi_color = nmc_term_color_sequence (color); @@ -471,137 +571,6 @@ nmc_filter_out_colors (const char *str) } /* - * Convert string to signed integer. - * If required, the resulting number is checked to be in the <min,max> range. - */ -gboolean -nmc_string_to_int_base (const char *str, - int base, - gboolean range_check, - long int min, - long int max, - long int *value) -{ - char *end; - long int tmp; - - errno = 0; - tmp = strtol (str, &end, base); - if (errno || *end != '\0' || (range_check && (tmp < min || tmp > max))) { - return FALSE; - } - *value = tmp; - return TRUE; -} - -/* - * Convert string to unsigned integer. - * If required, the resulting number is checked to be in the <min,max> range. - */ -gboolean -nmc_string_to_uint_base (const char *str, - int base, - gboolean range_check, - unsigned long int min, - unsigned long int max, - unsigned long int *value) -{ - char *end; - unsigned long int tmp; - - errno = 0; - tmp = strtoul (str, &end, base); - if (errno || *end != '\0' || (range_check && (tmp < min || tmp > max))) { - return FALSE; - } - *value = tmp; - return TRUE; -} - -gboolean -nmc_string_to_int (const char *str, - gboolean range_check, - long int min, - long int max, - long int *value) -{ - return nmc_string_to_int_base (str, 10, range_check, min, max, value); -} - -gboolean -nmc_string_to_uint (const char *str, - gboolean range_check, - unsigned long int min, - unsigned long int max, - unsigned long int *value) -{ - return nmc_string_to_uint_base (str, 10, range_check, min, max, value); -} - -gboolean -nmc_string_to_bool (const char *str, gboolean *val_bool, GError **error) -{ - const char *s_true[] = { "true", "yes", "on", NULL }; - const char *s_false[] = { "false", "no", "off", NULL }; - - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - if (g_strcmp0 (str, "o") == 0) { - g_set_error (error, 1, 0, - /* Translators: the first %s is the partial value entered by - * the user, the second %s a list of compatible values. - */ - _("'%s' is ambiguous (%s)"), str, "on x off"); - return FALSE; - } - - if (nmc_string_is_valid (str, s_true, NULL)) - *val_bool = TRUE; - else if (nmc_string_is_valid (str, s_false, NULL)) - *val_bool = FALSE; - else { - g_set_error (error, 1, 0, - _("'%s' is not valid; use [%s] or [%s]"), - str, "true, yes, on", "false, no, off"); - return FALSE; - } - return TRUE; -} - -gboolean -nmc_string_to_tristate (const char *str, NMCTriStateValue *val, GError **error) -{ - const char *s_true[] = { "true", "yes", "on", NULL }; - const char *s_false[] = { "false", "no", "off", NULL }; - const char *s_unknown[] = { "unknown", NULL }; - - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - if (g_strcmp0 (str, "o") == 0) { - g_set_error (error, 1, 0, - /* Translators: the first %s is the partial value entered by - * the user, the second %s a list of compatible values. - */ - _("'%s' is ambiguous (%s)"), str, "on x off"); - return FALSE; - } - - if (nmc_string_is_valid (str, s_true, NULL)) - *val = NMC_TRI_STATE_YES; - else if (nmc_string_is_valid (str, s_false, NULL)) - *val = NMC_TRI_STATE_NO; - else if (nmc_string_is_valid (str, s_unknown, NULL)) - *val = NMC_TRI_STATE_UNKNOWN; - else { - g_set_error (error, 1, 0, - _("'%s' is not valid; use [%s], [%s] or [%s]"), - str, "true, yes, on", "false, no, off", "unknown"); - return FALSE; - } - return TRUE; -} - -/* * Ask user for input and return the string. * The caller is responsible for freeing the returned string. */ @@ -664,64 +633,13 @@ nmc_string_to_arg_array (const char *line, const char *delim, gboolean unquote, } /* - * Check whether 'input' is contained in 'allowed' array. It performs case - * insensitive comparison and supports shortcut strings if they are unique. - * Returns: a pointer to found string in allowed array on success or NULL. - * On failure: error->code : 0 - string not found; 1 - string is ambiguous - */ -const char * -nmc_string_is_valid (const char *input, const char **allowed, GError **error) -{ - const char **p; - size_t input_ln, p_len; - gboolean prev_match = FALSE; - const char *ret = NULL; - - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - if (!input || !*input) - goto finish; - - input_ln = strlen (input); - for (p = allowed; p && *p; p++) { - p_len = strlen (*p); - if (g_ascii_strncasecmp (input, *p, input_ln) == 0) { - if (input_ln == p_len) { - ret = *p; - break; - } - if (!prev_match) - ret = *p; - else { - g_set_error (error, 1, 1, _("'%s' is ambiguous (%s x %s)"), - input, ret, *p); - return NULL; - } - prev_match = TRUE; - } - } - -finish: - if (ret == NULL) { - char *valid_vals = g_strjoinv (", ", (char **) allowed); - if (!input || !*input) - g_set_error (error, 1, 0, _("missing name, try one of [%s]"), valid_vals); - else - g_set_error (error, 1, 0, _("'%s' not among [%s]"), input, valid_vals); - - g_free (valid_vals); - } - return ret; -} - -/* * 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 * -nmc_util_strv_for_display (const char **strv, gboolean brackets) +nmc_util_strv_for_display (const char *const*strv, gboolean brackets) { GString *result; guint i = 0; @@ -742,31 +660,6 @@ nmc_util_strv_for_display (const char **strv, gboolean brackets) } /* - * Wrapper function for g_strsplit_set() that removes empty strings - * from the vector as they are not useful in most cases. - */ -char ** -nmc_strsplit_set (const char *str, const char *delimiter, int max_tokens) -{ - char **result; - uint i; - uint j; - - result = g_strsplit_set (str, delimiter, max_tokens); - - /* remove empty strings */ - for (i = 0; result && result[i]; i++) { - if (*(result[i]) == '\0') { - g_free (result[i]); - for (j = i; result[j]; j++) - result[j] = result[j + 1]; - i--; - } - } - return result; -} - -/* * Find out how many columns an UTF-8 string occupies on the screen. */ int @@ -820,21 +713,21 @@ set_val_arrc (NmcOutputField fields_array[], guint32 idx, const char **value) } void -set_val_color_all (NmcOutputField fields_array[], NmcTermColor color) +set_val_color_all (NmcOutputField fields_array[], NMMetaTermColor color) { int i; - for (i = 0; fields_array[i].name; i++) { + for (i = 0; fields_array[i].info; i++) { fields_array[i].color = color; } } void -set_val_color_fmt_all (NmcOutputField fields_array[], NmcTermFormat format) +set_val_color_fmt_all (NmcOutputField fields_array[], NMMetaTermFormat format) { int i; - for (i = 0; fields_array[i].name; i++) { + for (i = 0; fields_array[i].info; i++) { fields_array[i].color_fmt = format; } } @@ -847,7 +740,7 @@ nmc_free_output_field_values (NmcOutputField fields_array[]) { NmcOutputField *iter = fields_array; - while (iter && iter->name) { + while (iter && iter->info) { if (iter->free_value) { if (iter->value_is_array) g_strfreev ((char **) iter->value); @@ -859,6 +752,157 @@ nmc_free_output_field_values (NmcOutputField fields_array[]) } } +/*****************************************************************************/ + +#define PRINT_DATA_COL_PARENT_NIL (G_MAXUINT) + +typedef struct { + const NMMetaSelectionItem *selection_item; + guint parent_idx; + guint self_idx; + bool is_leaf; +} PrintDataCol; + +static gboolean +_output_selection_append (GArray *cols, + const char *fields_prefix, + guint parent_idx, + const NMMetaSelectionItem *selection_item, + GPtrArray *gfree_keeper, + GError **error) +{ + gs_free gpointer nested_to_free = NULL; + guint col_idx; + guint i; + const NMMetaAbstractInfo *const*nested; + NMMetaSelectionResultList *selection; + const NMMetaSelectionItem *si; + + col_idx = cols->len; + + { + PrintDataCol col = { + .selection_item = selection_item, + .parent_idx = parent_idx, + .self_idx = col_idx, + .is_leaf = TRUE, + }; + g_array_append_val (cols, col); + } + + nested = nm_meta_abstract_info_get_nested (selection_item->info, NULL, &nested_to_free); + + if (selection_item->sub_selection) { + if (!nested) { + gs_free char *allowed_fields = NULL; + + if (parent_idx != PRINT_DATA_COL_PARENT_NIL) { + si = g_array_index (cols, PrintDataCol, parent_idx).selection_item; + allowed_fields = nm_meta_abstract_info_get_nested_names_str (si->info, si->self_selection); + } + 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; + } + + selection = nm_meta_selection_create_parse_one (nested, selection_item->self_selection, + selection_item->sub_selection, FALSE, error); + if (!selection) + return FALSE; + nm_assert (selection->num == 1); + } else if (nested) { + selection = nm_meta_selection_create_all (nested); + nm_assert (selection && selection->num > 0); + } else + selection = NULL; + + if (selection) { + g_ptr_array_add (gfree_keeper, selection); + + for (i = 0; i < selection->num; i++) { + si = &selection->items[i]; + if (!_output_selection_append (cols, si->self_selection, col_idx, + si, gfree_keeper, error)) + return FALSE; + } + + if (!NM_IN_SET(selection_item->info->meta_type, + &nm_meta_type_setting_info_editor, + &nmc_meta_type_generic_info)) + g_array_index (cols, PrintDataCol, col_idx).is_leaf = FALSE; + } + + return TRUE; +} + +/*****************************************************************************/ + +/** + * _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, + GArray **out_cols, + GPtrArray **out_gfree_keeper, + GError **error) +{ + NMMetaSelectionResultList *selection; + gs_unref_ptrarray GPtrArray *gfree_keeper = NULL; + gs_unref_array GArray *cols = NULL; + guint i; + + selection = nm_meta_selection_create_parse_list (fields, NULL, fields_str, FALSE, error); + if (!selection) + return FALSE; + + if (!selection->num) { + g_set_error (error, NMCLI_ERROR, 1, _("failure to select field")); + g_free (selection); + 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++) { + const NMMetaSelectionItem *si = &selection->items[i]; + + if (!_output_selection_append (cols, NULL, PRINT_DATA_COL_PARENT_NIL, + si, gfree_keeper, error)) + return FALSE; + } + + *out_cols = g_steal_pointer (&cols); + *out_gfree_keeper = g_steal_pointer (&gfree_keeper); + return TRUE; +} + +/*****************************************************************************/ + /** * parse_output_fields: * @field_str: comma-separated field names to parse @@ -881,212 +925,673 @@ nmc_free_output_field_values (NmcOutputField fields_array[]) */ GArray * parse_output_fields (const char *fields_str, - const NmcOutputField fields_array[], + const NMMetaAbstractInfo *const*fields_array, gboolean parse_groups, - GPtrArray **group_fields, + GPtrArray **out_group_fields, GError **error) { - char **fields, **iter; + gs_free NMMetaSelectionResultList *selection = NULL; GArray *array; - int i, j; + GPtrArray *group_fields = NULL; + guint i; - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - g_return_val_if_fail (group_fields == NULL || *group_fields == NULL, NULL); - - array = g_array_new (FALSE, FALSE, sizeof (int)); - if (parse_groups && group_fields) - *group_fields = g_ptr_array_new_full (20, (GDestroyNotify) g_free); - - /* Split supplied fields string */ - fields = g_strsplit_set (fields_str, ",", -1); - for (iter = fields; iter && *iter; iter++) { - int idx = -1; - - g_strstrip (*iter); - if (parse_groups) { - /* e.g. "general.device,general.driver,ip4,ip6" */ - gboolean found = FALSE; - char *left = *iter; - char *right = strchr (*iter, '.'); - - if (right) - *right++ = '\0'; - - for (i = 0; fields_array[i].name; i++) { - if (strcasecmp (left, fields_array[i].name) == 0) { - NmcOutputField *valid_names = fields_array[i].group; - idx = i; - if (!right && !valid_names) { - found = TRUE; - break; - } - for (j = 0; valid_names && valid_names[j].name; j++) { - if (!right || strcasecmp (right, valid_names[j].name) == 0) { - found = TRUE; - break; - } + g_return_val_if_fail (!error || !*error, NULL); + g_return_val_if_fail (!out_group_fields || !*out_group_fields, NULL); + + selection = nm_meta_selection_create_parse_list (fields_array, NULL, fields_str, TRUE, error); + if (!selection) + return NULL; + + 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); + + for (i = 0; i < selection->num; i++) { + int idx = selection->items[i].idx; + + g_array_append_val (array, idx); + if (group_fields) + g_ptr_array_add (group_fields, g_strdup (selection->items[i].sub_selection)); + } + + if (group_fields) + *out_group_fields = group_fields; + return array; +} + +NmcOutputField * +nmc_dup_fields_array (const NMMetaAbstractInfo *const*fields, NmcOfFlags flags) +{ + NmcOutputField *row; + gsize l; + + for (l = 0; fields[l]; l++) { + } + + row = g_new0 (NmcOutputField, l + 1); + for (l = 0; fields[l]; l++) + row[l].info = fields[l]; + row[0].flags = flags; + return row; +} + +void +nmc_empty_output_fields (NmcOutputData *output_data) +{ + guint i; + + /* Free values in field structure */ + for (i = 0; i < output_data->output_data->len; i++) { + NmcOutputField *fld_arr = g_ptr_array_index (output_data->output_data, i); + nmc_free_output_field_values (fld_arr); + } + + /* Empty output_data array */ + if (output_data->output_data->len > 0) + g_ptr_array_remove_range (output_data->output_data, 0, output_data->output_data->len); +} + +/*****************************************************************************/ + +typedef struct { + guint col_idx; + const PrintDataCol *col; + const char *title; + bool title_to_free:1; + int width; +} PrintDataHeaderCell; + +typedef enum { + PRINT_DATA_CELL_FORMAT_TYPE_PLAIN = 0, + PRINT_DATA_CELL_FORMAT_TYPE_STRV, +} PrintDataCellFormatType; + +typedef struct { + guint row_idx; + const PrintDataHeaderCell *header_cell; + NMMetaTermColor term_color; + NMMetaTermFormat term_format; + union { + const char *plain; + const char *const*strv; + } text; + PrintDataCellFormatType text_format:3; + bool text_to_free:1; +} PrintDataCell; + +static void +_print_data_header_cell_clear (gpointer cell_p) +{ + PrintDataHeaderCell *cell = cell_p; + + if (cell->title_to_free) { + g_free ((char *) cell->title); + cell->title_to_free = FALSE; + } + cell->title = NULL; +} + +static void +_print_data_cell_clear_text (PrintDataCell *cell) +{ + if (cell->text_to_free) { + switch (cell->text_format) { + case PRINT_DATA_CELL_FORMAT_TYPE_PLAIN: + g_free ((char *) cell->text.plain); + break; + case PRINT_DATA_CELL_FORMAT_TYPE_STRV: + g_strfreev ((char **) cell->text.strv); + break; + }; + cell->text_to_free = FALSE; + } + memset (&cell->text, 0, sizeof (cell->text)); +} + +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, + 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; + gboolean pretty; + NMMetaAccessorGetType text_get_type; + NMMetaAccessorGetFlags text_get_flags; + + pretty = (nmc_config->print_output != NMC_PRINT_TERSE); + + 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; + const NMMetaAbstractInfo *info; + + col = &cols[i_col]; + if (!col->is_leaf) + continue; + + info = col->selection_item->info; + + 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; + + header_cell->title = nm_meta_abstract_info_get_name (info, TRUE); + if ( nmc_config->multiline_output + && col->parent_idx != PRINT_DATA_COL_PARENT_NIL + && NM_IN_SET (info->meta_type, + &nm_meta_type_property_info, + &nmc_meta_type_generic_info)) { + header_cell->title = g_strdup_printf ("%s.%s", + nm_meta_abstract_info_get_name (cols[col->parent_idx].selection_item->info, FALSE), + header_cell->title); + header_cell->title_to_free = TRUE; + } + } + + 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); + + text_get_type = pretty + ? NM_META_ACCESSOR_GET_TYPE_PRETTY + : NM_META_ACCESSOR_GET_TYPE_PARSABLE; + text_get_flags = NM_META_ACCESSOR_GET_FLAGS_ACCEPT_STRV; + if (nmc_config->show_secrets) + text_get_flags |= NM_META_ACCESSOR_GET_FLAGS_SHOW_SECRETS; + + 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]; + const PrintDataHeaderCell *header_cell; + const NMMetaAbstractInfo *info; + NMMetaAccessorGetOutFlags text_out_flags, color_out_flags; + gconstpointer value; + + 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; + + value = nm_meta_abstract_info_get (info, + nmc_meta_environment, + nmc_meta_environment_arg, + target, + text_get_type, + text_get_flags, + &text_out_flags, + (gpointer *) &to_free); + if (NM_FLAGS_HAS (text_out_flags, NM_META_ACCESSOR_GET_OUT_FLAGS_STRV)) { + if (value) { + 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 { + cell->text.plain = g_strjoinv (" | ", (char **) value); + cell->text_to_free = TRUE; + if (to_free) + g_strfreev ((char **) to_free); } - if (found) - break; } + } else { + cell->text.plain = value; + cell->text_to_free = !!to_free; } - if (found) { - /* Add index to array, and field name (or NULL) to group_fields array */ - g_array_append_val (array, idx); - if (group_fields && *group_fields) - g_ptr_array_add (*group_fields, g_strdup (right)); + + nm_meta_termformat_unpack (nm_meta_abstract_info_get (info, + nmc_meta_environment, + nmc_meta_environment_arg, + target, + NM_META_ACCESSOR_GET_TYPE_TERMFORMAT, + NM_META_ACCESSOR_GET_FLAGS_NONE, + &color_out_flags, + NULL), + &cell->term_color, + &cell->term_format); + + if (cell->text_format == PRINT_DATA_CELL_FORMAT_TYPE_PLAIN) { + if (pretty && (!cell->text.plain|| !cell->text.plain[0])) { + _print_data_cell_clear_text (cell); + cell->text.plain = "--"; + } else if (!cell->text.plain) + cell->text.plain = ""; } - if (right) - *(right-1) = '.'; /* Restore the original string */ - } else { - /* e.g. "general,ip4,ip6" */ - for (i = 0; fields_array[i].name; i++) { - if (strcasecmp (*iter, fields_array[i].name) == 0) { - g_array_append_val (array, i); - break; + } + } + + 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++) { + const PrintDataCell *cell = &g_array_index (cells, PrintDataCell, i_row * cols_len + i_col); + const char *const*i_strv; + + switch (cell->text_format) { + case PRINT_DATA_CELL_FORMAT_TYPE_PLAIN: + header_cell->width = NM_MAX (header_cell->width, + 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; } } - /* Field was not found - error case */ - if (fields_array[i].name == NULL) { - /* Set GError */ - if (!strcasecmp (*iter, "all") || !strcasecmp (*iter, "common")) - g_set_error (error, NMCLI_ERROR, 0, _("field '%s' has to be alone"), *iter); - else { - char *allowed_fields = nmc_get_allowed_fields (fields_array, idx); - g_set_error (error, NMCLI_ERROR, 1, _("invalid field '%s'; allowed fields: %s"), - *iter, allowed_fields); - g_free (allowed_fields); + header_cell->width += 1; + } + + *out_header_row = header_row; + *out_cells = cells; +} + +static gboolean +_print_skip_column (const NmcConfig *nmc_config, + const PrintDataHeaderCell *header_cell) +{ + const NMMetaSelectionItem *selection_item; + const NMMetaAbstractInfo *info; + + selection_item = header_cell->col->selection_item; + info = selection_item->info; + + if (nmc_config->multiline_output) { + if (info->meta_type == &nm_meta_type_setting_info_editor) { + /* we skip the "name" entry for the setting in multiline output. */ + return TRUE; + } + if ( info->meta_type == &nmc_meta_type_generic_info + && ((const NmcMetaGenericInfo *) info)->nested) { + /* skip the "name" entry for parent generic-infos */ + return TRUE; + } + } else { + if ( NM_IN_SET (info->meta_type, + &nm_meta_type_setting_info_editor, + &nmc_meta_type_generic_info) + && selection_item->sub_selection) { + /* 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; +} + +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; + gboolean pretty = (nmc_config->print_output == NMC_PRINT_PRETTY); + gboolean terse = (nmc_config->print_output == NMC_PRINT_TERSE); + gboolean multiline = nmc_config->multiline_output; + guint i_row, i_col; + nm_auto_free_gstring GString *str = NULL; + + g_assert (col_len && row_len); + + /* Main header */ + if (pretty && header_name_no_l10n) { + 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; + + if (multiline) { + 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); + } + + str = !multiline + ? g_string_sized_new (100) + : NULL; + + /* print the header for the tabular form */ + if (!multiline && !terse) { + for (i_col = 0; i_col < col_len; i_col++) { + const PrintDataHeaderCell *header_cell = &header_row[i_col]; + const char *title; + + if (_print_skip_column (nmc_config, header_cell)) + continue; + + title = header_cell->title; + + width1 = strlen (title); + width2 = nmc_string_screen_width (title, NULL); /* Width of the string (in screen colums) */ + 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 */ + if (pretty) { + 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]; + const char *const*lines = NULL; + guint i_lines, lines_len; + + if (_print_skip_column (nmc_config, cell->header_cell)) + continue; + + 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: + nm_assert (multiline); + lines = cell->text.strv; + lines_len = NM_PTRARRAY_LEN (lines); + break; } - /* Free arrays on error */ - g_array_free (array, TRUE); - array = NULL; - if (group_fields && *group_fields) { - g_ptr_array_free (*group_fields, TRUE); - *group_fields = NULL; + for (i_lines = 0; i_lines < lines_len; i_lines++) { + gs_free char *text_to_free = NULL; + const char *text; + + text = colorize_string (nmc_config->use_colors, + cell->term_color, cell->term_format, + lines[i_lines], &text_to_free); + if (multiline) { + gs_free char *prefix = NULL; + + if (cell->text_format == PRINT_DATA_CELL_FORMAT_TYPE_STRV) + prefix = g_strdup_printf ("%s[%u]:", cell->header_cell->title, i_lines + 1); + else + prefix = g_strdup_printf ("%s:", cell->header_cell->title); + width1 = strlen (prefix); + width2 = nmc_string_screen_width (prefix, NULL); + g_print ("%-*s%s\n", (int) (terse ? 0 : ML_VALUE_INDENT+width1-width2), prefix, text); + } else { + nm_assert (str); + if (terse) { + 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); + width2 = nmc_string_screen_width (text, NULL); /* Width of the string (in screen colums) */ + 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; + } + } } - goto done; + } + + if (!multiline) { + 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); + } + + if ( pretty + && ( i_row < row_len - 1 + || multiline)) { + gs_free char *line = NULL; + + g_print ("%s\n", (line = g_strnfill (ML_HEADER_WIDTH, '-'))); } } -done: - if (fields) - g_strfreev (fields); - return array; } -/** -* nmc_get_allowed_fields: -* @fields_array: array of fields -* @group_idx: index to the array (for second-level array in 'group' member), -* or -1 -* -* Returns: string of allowed fields names. -* Caller is responsible for freeing the array. -*/ -char * -nmc_get_allowed_fields (const NmcOutputField fields_array[], int group_idx) +gboolean +nmc_print (const NmcConfig *nmc_config, + gpointer const *targets, + const char *header_name_no_l10n, + const NMMetaAbstractInfo *const*fields, + const char *fields_str, + GError **error) { - GString *allowed_fields = g_string_sized_new (256); - int i; + gs_unref_ptrarray GPtrArray *gfree_keeper = NULL; + gs_unref_array GArray *cols = NULL; + gs_unref_array GArray *header_row = NULL; + gs_unref_array GArray *cells = NULL; + + if (!_output_selection_parse (fields, fields_str, + &cols, &gfree_keeper, + error)) + return FALSE; - if (group_idx != -1 && fields_array[group_idx].group) { - NmcOutputField *second_level = fields_array[group_idx].group; - for (i = 0; second_level[i].name; i++) - g_string_append_printf (allowed_fields, "%s.%s,", - fields_array[group_idx].name, second_level[i].name); - } else { - for (i = 0; fields_array[i].name; i++) - g_string_append_printf (allowed_fields, "%s,", fields_array[i].name); - } - g_string_truncate (allowed_fields, allowed_fields->len - 1); + _print_fill (nmc_config, + targets, + &g_array_index (cols, PrintDataCol, 0), + cols->len, + &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 g_string_free (allowed_fields, FALSE); + return TRUE; } -NmcOutputField * -nmc_dup_fields_array (NmcOutputField fields[], size_t size, guint32 flags) +/*****************************************************************************/ + +static void +pager_fallback (void) { - NmcOutputField *row; + char buf[64]; + int rb; - row = g_malloc0 (size); - memcpy (row, fields, size); - row[0].flags = flags; + do { + rb = read (STDIN_FILENO, buf, sizeof (buf)); + if (rb == -1) { + if (errno == EINTR) { + continue; + } else { + g_printerr (_("Error reading nmcli output: %s\n"), strerror (errno)); + _exit(EXIT_FAILURE); + } + } + if (write (STDOUT_FILENO, buf, rb) == -1) { + g_printerr (_("Error writing nmcli output: %s\n"), strerror (errno)); + _exit(EXIT_FAILURE); + } + } while (rb > 0); - return row; + _exit(EXIT_SUCCESS); } void -nmc_empty_output_fields (NmCli *nmc) +nmc_terminal_spawn_pager (const NmcConfig *nmc_config) { - guint i; + const char *pager = getenv ("PAGER"); + pid_t parent_pid; + int fd[2]; + + if ( nm_cli.nmc_config.in_editor + || nm_cli.pager_pid > 0 + || nmc_config->print_output == NMC_PRINT_TERSE + || !use_colors (nmc_config->use_colors) + || g_strcmp0 (pager, "") == 0 + || getauxval (AT_SECURE)) + return; - /* Free values in field structure */ - for (i = 0; i < nmc->output_data->len; i++) { - NmcOutputField *fld_arr = g_ptr_array_index (nmc->output_data, i); - nmc_free_output_field_values (fld_arr); + if (pipe (fd) == -1) { + g_printerr (_("Failed to create pager pipe: %s\n"), strerror (errno)); + return; } - /* Empty output_data array */ - if (nmc->output_data->len > 0) - g_ptr_array_remove_range (nmc->output_data, 0, nmc->output_data->len); + parent_pid = getpid (); - if (nmc->print_fields.indices) { - g_array_free (nmc->print_fields.indices, TRUE); - nmc->print_fields.indices = NULL; + nm_cli.pager_pid = fork (); + if (nm_cli.pager_pid == -1) { + g_printerr (_("Failed to fork pager: %s\n"), strerror (errno)); + close (fd[0]); + close (fd[1]); + return; } -} -static const char * -colorize_string (NmCli *nmc, - NmcTermColor color, - NmcTermFormat color_fmt, - const char *str, - char **out_to_free) -{ - const char *out = str; + /* In the child start the pager */ + if (nm_cli.pager_pid == 0) { + dup2 (fd[0], STDIN_FILENO); + close (fd[0]); + close (fd[1]); - if ( use_colors (nmc) - && (color != NMC_TERM_COLOR_NORMAL || color_fmt != NMC_TERM_FORMAT_NORMAL)) { - *out_to_free = nmc_colorize (nmc, color, color_fmt, "%s", str); - out = *out_to_free; + 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 out; + /* Return in the parent */ + if (dup2 (fd[1], STDOUT_FILENO) < 0) + g_printerr (_("Failed to duplicate pager pipe: %s\n"), strerror (errno)); + if (dup2 (fd[1], STDERR_FILENO) < 0) + g_printerr (_("Failed to duplicate pager pipe: %s\n"), strerror (errno)); + + close (fd[0]); + close (fd[1]); } +/*****************************************************************************/ + static const char * -get_value_to_print (NmCli *nmc, - NmcOutputField *field, +get_value_to_print (NmcColorOption color_option, + const NmcOutputField *field, gboolean field_name, const char *not_set_str, char **out_to_free) { gboolean is_array = field->value_is_array; - char *value; + const char *value; const char *out; - gboolean free_value; + gs_free char *free_value = NULL; + + nm_assert (out_to_free && !*out_to_free); if (field_name) - value = _(field->name_l10n); - else + value = nm_meta_abstract_info_get_name (field->info, FALSE); + else { 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; + ? (free_value = g_strjoinv (" | ", (char **) field->value)) + : (*((const char *) field->value)) + ? field->value + : not_set_str) + : not_set_str; + } /* colorize the value */ - out = colorize_string (nmc, field->color, field->color_fmt, value, out_to_free); - if (*out_to_free) { - if (free_value) - g_free (value); - } else if (free_value) - *out_to_free = value; + out = colorize_string (color_option, field->color, field->color_fmt, value, out_to_free); + + if (out && out == free_value) { + nm_assert (!*out_to_free); + *out_to_free = g_steal_pointer (&free_value); + } return out; } @@ -1094,70 +1599,68 @@ get_value_to_print (NmCli *nmc, /* * Print both headers or values of 'field_values' array. * Entries to print and their order are specified via indices in - * 'nmc->print_fields.indices' array. + * 'nmc->indices' array. * Various flags influencing the output of fields are set up in the first item * of 'field_values' array. */ void -print_required_fields (NmCli *nmc, const NmcOutputField field_values[]) +print_required_fields (const NmcConfig *nmc_config, + NmcOfFlags of_flags, + const GArray *indices, + const char *header_name, + int indent, + const NmcOutputField *field_values) { - GString *str; + nm_auto_free_gstring GString *str = NULL; int width1, width2; int table_width = 0; - char *line = NULL; - char *indent_str; - const char *not_set_str = "--"; + const char *not_set_str; int i; - const NmcPrintFields fields = nmc->print_fields; - gboolean multiline = nmc->multiline_output; - gboolean terse = (nmc->print_output == NMC_PRINT_TERSE); - gboolean pretty = (nmc->print_output == NMC_PRINT_PRETTY); - gboolean escape = nmc->escape_values; - gboolean main_header_add = field_values[0].flags & NMC_OF_FLAG_MAIN_HEADER_ADD; - gboolean main_header_only = field_values[0].flags & NMC_OF_FLAG_MAIN_HEADER_ONLY; - gboolean field_names = field_values[0].flags & NMC_OF_FLAG_FIELD_NAMES; - gboolean section_prefix = field_values[0].flags & NMC_OF_FLAG_SECTION_PREFIX; - gboolean main_header = main_header_add || main_header_only; - - enum { ML_HEADER_WIDTH = 79 }; - enum { ML_VALUE_INDENT = 40 }; + gboolean terse = (nmc_config->print_output == NMC_PRINT_TERSE); + gboolean pretty = (nmc_config->print_output == NMC_PRINT_PRETTY); + 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; + /* Optionally start paging the output. */ + nmc_terminal_spawn_pager (nmc_config); /* --- Main header --- */ - if (main_header && pretty) { - int header_width = nmc_string_screen_width (fields.header_name, NULL) + 4; + if ((main_header_add || main_header_only) && pretty) { + gs_free char *line = NULL; + int header_width; - if (multiline) { - table_width = header_width < ML_HEADER_WIDTH ? ML_HEADER_WIDTH : header_width; + header_width = nmc_string_screen_width (header_name, NULL) + 4; + + if (nmc_config->multiline_output) { + table_width = NM_MAX (header_width, ML_HEADER_WIDTH); line = g_strnfill (ML_HEADER_WIDTH, '='); } else { /* tabular */ - table_width = table_width < header_width ? header_width : table_width; + table_width = NM_MAX (table_width, header_width); line = g_strnfill (table_width, '='); } - width1 = strlen (fields.header_name); - width2 = nmc_string_screen_width (fields.header_name, NULL); + 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, fields.header_name); + g_print ("%*s\n", (table_width + width2)/2 + width1 - width2, header_name); g_print ("%s\n", line); - g_free (line); } if (main_header_only) return; /* No field headers are printed in terse mode nor for multiline output */ - if ((terse || multiline) && field_names) + if ((terse || nmc_config->multiline_output) && field_names) return; - if (terse) - not_set_str = ""; /* Don't replace empty strings in terse mode */ - + /* Don't replace empty strings in terse mode */ + not_set_str = terse ? "" : "--"; - if (multiline) { - for (i = 0; i < fields.indices->len; i++) { - char *tmp; - int idx = g_array_index (fields.indices, int, i); + if (nmc_config->multiline_output) { + for (i = 0; i < indices->len; i++) { + int idx = g_array_index (indices, int, i); gboolean is_array = field_values[idx].value_is_array; /* section prefix can't be an array */ @@ -1167,49 +1670,52 @@ print_required_fields (NmCli *nmc, const NmcOutputField field_values[]) continue; if (is_array) { - /* value is a null-terminated string array */ - const char **p, *val, *print_val; gs_free char *val_to_free = NULL; + const char **p, *val, *print_val; int j; + /* value is a null-terminated string array */ + 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, + gs_free char *tmp = NULL; + + val = *p ?: not_set_str; + print_val = colorize_string (nmc_config->use_colors, field_values[idx].color, field_values[idx].color_fmt, 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), + nm_meta_abstract_info_get_name (field_values[idx].info, FALSE), 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); + g_print ("%-*s%s\n", (int) (terse ? 0 : ML_VALUE_INDENT+width1-width2), tmp, print_val); } } else { - /* value is a string */ + gs_free char *val_to_free = NULL; + gs_free char *tmp = NULL; 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; + + /* value is a string */ val = val && *val ? val : not_set_str; - print_val = colorize_string (nmc, field_values[idx].color, field_values[idx].color_fmt, + print_val = colorize_string (nmc_config->use_colors, 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)); + nm_meta_abstract_info_get_name (field_values[idx].info, FALSE)); 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); + g_print ("%-*s%s\n", (int) (terse ? 0 : ML_VALUE_INDENT+width1-width2), tmp, print_val); } } if (pretty) { - line = g_strnfill (ML_HEADER_WIDTH, '-'); - g_print ("%s\n", line); - g_free (line); + gs_free char *line = NULL; + + g_print ("%s\n", (line = g_strnfill (ML_HEADER_WIDTH, '-'))); } return; @@ -1219,14 +1725,18 @@ print_required_fields (NmCli *nmc, const NmcOutputField field_values[]) str = g_string_new (NULL); - for (i = 0; i < fields.indices->len; i++) { - int idx = g_array_index (fields.indices, int, i); + for (i = 0; i < indices->len; i++) { 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); + int idx; + const char *value; + + idx = g_array_index (indices, int, i); + + value = get_value_to_print (nmc_config->use_colors, (NmcOutputField *) field_values+idx, field_names, + not_set_str, &val_to_free); if (terse) { - if (escape) { + if (nmc_config->escape_values) { const char *p = value; while (*p) { if (*p == ':' || *p == '\\') @@ -1250,46 +1760,37 @@ print_required_fields (NmCli *nmc, const NmcOutputField field_values[]) /* Print actual values */ 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, ' '); - g_string_prepend (str, indent_str); - g_free (indent_str); + if (indent > 0) { + gs_free char *indent_str = NULL; + + g_string_prepend (str, (indent_str = g_strnfill (indent, ' '))); } + g_print ("%s\n", str->str); /* Print horizontal separator */ if (field_names && pretty) { - line = g_strnfill (table_width, '-'); - g_print ("%s\n", line); - g_free (line); + gs_free char *line = NULL; + + g_print ("%s\n", (line = g_strnfill (table_width, '-'))); } } - - g_string_free (str, TRUE); } -/* - * Print nmc->output_data - * - * It first finds out maximal string length in columns and fill the value to - * 'width' member of NmcOutputField, so that columns in tabular output are - * properly aligned. Then each object (row in tabular) is printed using - * print_required_fields() function. - */ void -print_data (NmCli *nmc) +print_data_prepare_width (GPtrArray *output_data) { int i, j; size_t len; NmcOutputField *row; int num_fields = 0; - if (!nmc->output_data || nmc->output_data->len < 1) + if (!output_data || output_data->len < 1) return; /* How many fields? */ - row = g_ptr_array_index (nmc->output_data, 0); - while (row->name) { + row = g_ptr_array_index (output_data, 0); + while (row->info) { num_fields++; row++; } @@ -1297,27 +1798,39 @@ print_data (NmCli *nmc) /* Find out maximal string lengths */ for (i = 0; i < num_fields; i++) { size_t max_width = 0; - for (j = 0; j < nmc->output_data->len; j++) { + for (j = 0; j < output_data->len; j++) { gboolean field_names; gs_free char * val_to_free = NULL; const char *value; - row = g_ptr_array_index (nmc->output_data, j); + row = g_ptr_array_index (output_data, j); field_names = row[0].flags & NMC_OF_FLAG_FIELD_NAMES; - value = get_value_to_print (NULL, row+i, field_names, "--", &val_to_free); + value = get_value_to_print (NMC_USE_COLOR_NO, row+i, field_names, "--", &val_to_free); len = nmc_string_screen_width (value, NULL); max_width = len > max_width ? len : max_width; } - for (j = 0; j < nmc->output_data->len; j++) { - row = g_ptr_array_index (nmc->output_data, j); + for (j = 0; j < output_data->len; j++) { + row = g_ptr_array_index (output_data, j); row[i].width = max_width + 1; } } +} + +void +print_data (const NmcConfig *nmc_config, + const GArray *indices, + const char *header_name, + int indent, + const NmcOutputData *out) +{ + guint i; + + for (i = 0; i < out->output_data->len; i++) { + const NmcOutputField *field_values = g_ptr_array_index (out->output_data, i); - /* Now we can print the data. */ - for (i = 0; i < nmc->output_data->len; i++) { - row = g_ptr_array_index (nmc->output_data, i); - print_required_fields (nmc, row); + print_required_fields (nmc_config, field_values[0].flags, + indices, header_name, + indent, field_values); } } |