
libnm contains the public function nm_utils_enum_from_str() et al. The function is not flexible enough for nmcli's usecase. So, I would need another public function like nm_utils_enum_from_str_full() that has an extended API. That was already required previously for ifcfg-rh writer, but in that case I could just add it as internal API as libnm-core is linked statically with NetworkManager. I don't want to commit to a public API for an utility function. So move the code instead to the shared directory, so that nmcli may link statically against it and use the internal API.
312 lines
8.3 KiB
C
312 lines
8.3 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
|
/* NetworkManager -- Network link manager
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA.
|
|
*
|
|
* (C) Copyright 2017 Red Hat, Inc.
|
|
*/
|
|
|
|
#include "nm-default.h"
|
|
|
|
#include "nm-enum-utils.h"
|
|
|
|
/*****************************************************************************/
|
|
|
|
#define IS_FLAGS_SEPARATOR(ch) (NM_IN_SET ((ch), ' ', '\t', ',', '\n', '\r'))
|
|
|
|
static gboolean
|
|
_is_hex_string (const char *str)
|
|
{
|
|
return str[0] == '0'
|
|
&& str[1] == 'x'
|
|
&& str[2]
|
|
&& NM_STRCHAR_ALL (&str[2], ch, g_ascii_isxdigit (ch));
|
|
}
|
|
|
|
static gboolean
|
|
_is_dec_string (const char *str)
|
|
{
|
|
return str[0]
|
|
&& NM_STRCHAR_ALL (&str[0], ch, g_ascii_isdigit (ch));
|
|
}
|
|
|
|
static gboolean
|
|
_enum_is_valid_enum_nick (const char *str)
|
|
{
|
|
return str[0]
|
|
&& !NM_STRCHAR_ANY (str, ch, g_ascii_isspace (ch))
|
|
&& !_is_dec_string (str)
|
|
&& !_is_hex_string (str);
|
|
}
|
|
|
|
static gboolean
|
|
_enum_is_valid_flags_nick (const char *str)
|
|
{
|
|
return str[0]
|
|
&& !NM_STRCHAR_ANY (str, ch, IS_FLAGS_SEPARATOR (ch))
|
|
&& !_is_dec_string (str)
|
|
&& !_is_hex_string (str);
|
|
}
|
|
|
|
char *
|
|
_nm_utils_enum_to_str_full (GType type,
|
|
int value,
|
|
const char *flags_separator)
|
|
{
|
|
GTypeClass *class;
|
|
char *ret;
|
|
|
|
if ( flags_separator
|
|
&& ( !flags_separator[0]
|
|
|| NM_STRCHAR_ANY (flags_separator, ch, !IS_FLAGS_SEPARATOR (ch))))
|
|
g_return_val_if_reached (NULL);
|
|
|
|
class = g_type_class_ref (type);
|
|
|
|
if (G_IS_ENUM_CLASS (class)) {
|
|
GEnumValue *enum_value;
|
|
|
|
enum_value = g_enum_get_value (G_ENUM_CLASS (class), value);
|
|
if ( !enum_value
|
|
|| !_enum_is_valid_enum_nick (enum_value->value_nick))
|
|
ret = g_strdup_printf ("%d", value);
|
|
else
|
|
ret = strdup (enum_value->value_nick);
|
|
} else if (G_IS_FLAGS_CLASS (class)) {
|
|
GFlagsValue *flags_value;
|
|
GString *str = g_string_new ("");
|
|
unsigned uvalue = (unsigned) value;
|
|
|
|
flags_separator = flags_separator ?: " ";
|
|
|
|
do {
|
|
flags_value = g_flags_get_first_value (G_FLAGS_CLASS (class), uvalue);
|
|
if (str->len)
|
|
g_string_append (str, flags_separator);
|
|
if ( !flags_value
|
|
|| !_enum_is_valid_flags_nick (flags_value->value_nick)) {
|
|
if (uvalue)
|
|
g_string_append_printf (str, "0x%x", uvalue);
|
|
break;
|
|
}
|
|
g_string_append (str, flags_value->value_nick);
|
|
uvalue &= ~flags_value->value;
|
|
} while (uvalue);
|
|
ret = g_string_free (str, FALSE);
|
|
} else
|
|
g_return_val_if_reached (NULL);
|
|
|
|
g_type_class_unref (class);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* nm_utils_enum_to_str:
|
|
* @type: the %GType of the enum
|
|
* @value: the value to be translated
|
|
*
|
|
* Converts an enum value to its string representation. If the enum is a
|
|
* %G_TYPE_FLAGS the function returns a comma-separated list of matching values.
|
|
* If the value has no corresponding string representation, it is converted
|
|
* to a number. For enums it is converted to a decimal number, for flags
|
|
* to an (unsigned) hex number.
|
|
*
|
|
* Returns: a newly allocated string or %NULL
|
|
*
|
|
* Since: 1.2
|
|
*/
|
|
char *
|
|
nm_utils_enum_to_str (GType type, int value)
|
|
{
|
|
return _nm_utils_enum_to_str_full (type, value, ", ");
|
|
}
|
|
|
|
/**
|
|
* nm_utils_enum_from_str:
|
|
* @type: the %GType of the enum
|
|
* @str: the input string
|
|
* @out_value: (out) (allow-none): the output value
|
|
* @err_token: (out) (allow-none) (transfer full): location to store the first unrecognized token
|
|
*
|
|
* Converts a string to the matching enum value.
|
|
*
|
|
* If the enum is a %G_TYPE_FLAGS the function returns the logical OR of values
|
|
* matching the comma-separated tokens in the string; if an unknown token is found
|
|
* the function returns %FALSE and stores a pointer to a newly allocated string
|
|
* containing the unrecognized token in @err_token.
|
|
*
|
|
* Returns: %TRUE if the conversion was successful, %FALSE otherwise
|
|
*
|
|
* Since: 1.2
|
|
*/
|
|
gboolean
|
|
nm_utils_enum_from_str (GType type, const char *str,
|
|
int *out_value, char **err_token)
|
|
{
|
|
GTypeClass *class;
|
|
gboolean ret = FALSE;
|
|
int value = 0;
|
|
gs_free char *str_clone = NULL;
|
|
char *s;
|
|
gint64 v64;
|
|
|
|
g_return_val_if_fail (str, FALSE);
|
|
|
|
str_clone = strdup (str);
|
|
s = nm_str_skip_leading_spaces (str_clone);
|
|
g_strchomp (s);
|
|
|
|
class = g_type_class_ref (type);
|
|
|
|
if (G_IS_ENUM_CLASS (class)) {
|
|
GEnumValue *enum_value;
|
|
|
|
if (s[0]) {
|
|
if (_is_hex_string (s)) {
|
|
v64 = _nm_utils_ascii_str_to_int64 (s, 16, 0, G_MAXUINT, -1);
|
|
if (v64 != -1) {
|
|
value = (int) v64;
|
|
ret = TRUE;
|
|
}
|
|
} else if (_is_dec_string (s)) {
|
|
v64 = _nm_utils_ascii_str_to_int64 (s, 10, 0, G_MAXUINT, -1);
|
|
if (v64 != -1) {
|
|
value = (int) v64;
|
|
ret = TRUE;
|
|
}
|
|
} else {
|
|
enum_value = g_enum_get_value_by_nick (G_ENUM_CLASS (class), s);
|
|
if (enum_value) {
|
|
value = enum_value->value;
|
|
ret = TRUE;
|
|
}
|
|
}
|
|
}
|
|
} else if (G_IS_FLAGS_CLASS (class)) {
|
|
GFlagsValue *flags_value;
|
|
unsigned uvalue = 0;
|
|
|
|
ret = TRUE;
|
|
while (s[0]) {
|
|
char *s_end;
|
|
|
|
for (s_end = s; s_end[0]; s_end++) {
|
|
if (IS_FLAGS_SEPARATOR (s_end[0])) {
|
|
s_end[0] = '\0';
|
|
s_end++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (s[0]) {
|
|
if (_is_hex_string (s)) {
|
|
v64 = _nm_utils_ascii_str_to_int64 (&s[2], 16, 0, G_MAXUINT, -1);
|
|
if (v64 == -1) {
|
|
ret = FALSE;
|
|
break;
|
|
}
|
|
uvalue |= (unsigned) v64;
|
|
} else if (_is_dec_string (s)) {
|
|
v64 = _nm_utils_ascii_str_to_int64 (s, 10, 0, G_MAXUINT, -1);
|
|
if (v64 == -1) {
|
|
ret = FALSE;
|
|
break;
|
|
}
|
|
uvalue |= (unsigned) v64;
|
|
} else {
|
|
flags_value = g_flags_get_value_by_nick (G_FLAGS_CLASS (class), s);
|
|
if (!flags_value) {
|
|
ret = FALSE;
|
|
break;
|
|
}
|
|
uvalue |= flags_value->value;
|
|
}
|
|
}
|
|
|
|
s = s_end;
|
|
}
|
|
|
|
value = (int) uvalue;
|
|
} else
|
|
g_return_val_if_reached (FALSE);
|
|
|
|
NM_SET_OUT (err_token, !ret && s[0] ? g_strdup (s) : NULL);
|
|
NM_SET_OUT (out_value, ret ? value : 0);
|
|
g_type_class_unref (class);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* nm_utils_enum_get_values:
|
|
* @type: the %GType of the enum
|
|
* @from: the first element to be returned
|
|
* @to: the last element to be returned
|
|
*
|
|
* Returns the list of possible values for a given enum.
|
|
*
|
|
* Returns: (transfer container): a NULL-terminated dynamically-allocated array of static strings
|
|
* or %NULL on error
|
|
*
|
|
* Since: 1.2
|
|
*/
|
|
const char **nm_utils_enum_get_values (GType type, gint from, gint to)
|
|
{
|
|
GTypeClass *class;
|
|
GPtrArray *array;
|
|
gint i;
|
|
char sbuf[64];
|
|
|
|
class = g_type_class_ref (type);
|
|
array = g_ptr_array_new ();
|
|
|
|
if (G_IS_ENUM_CLASS (class)) {
|
|
GEnumClass *enum_class = G_ENUM_CLASS (class);
|
|
GEnumValue *enum_value;
|
|
|
|
for (i = 0; i < enum_class->n_values; i++) {
|
|
enum_value = &enum_class->values[i];
|
|
if (enum_value->value >= from && enum_value->value <= to) {
|
|
if (_enum_is_valid_enum_nick (enum_value->value_nick))
|
|
g_ptr_array_add (array, (gpointer) enum_value->value_nick);
|
|
else
|
|
g_ptr_array_add (array, (gpointer) g_intern_string (nm_sprintf_buf (sbuf, "%d", enum_value->value)));
|
|
}
|
|
}
|
|
} else if (G_IS_FLAGS_CLASS (class)) {
|
|
GFlagsClass *flags_class = G_FLAGS_CLASS (class);
|
|
GFlagsValue *flags_value;
|
|
|
|
for (i = 0; i < flags_class->n_values; i++) {
|
|
flags_value = &flags_class->values[i];
|
|
if (flags_value->value >= from && flags_value->value <= to) {
|
|
if (_enum_is_valid_flags_nick (flags_value->value_nick))
|
|
g_ptr_array_add (array, (gpointer) flags_value->value_nick);
|
|
else
|
|
g_ptr_array_add (array, (gpointer) g_intern_string (nm_sprintf_buf (sbuf, "0x%x", (unsigned) flags_value->value)));
|
|
}
|
|
}
|
|
} else {
|
|
g_type_class_unref (class);
|
|
g_ptr_array_free (array, TRUE);
|
|
g_return_val_if_reached (NULL);
|
|
}
|
|
|
|
g_type_class_unref (class);
|
|
g_ptr_array_add (array, NULL);
|
|
|
|
return (const char **) g_ptr_array_free (array, FALSE);
|
|
}
|