Files
NetworkManager/shared/nm-utils/nm-enum-utils.c
Thomas Haller a8730c51c8 libnm: move enum utils to new shared file shared/nm-utils/nm-enum-utils.h
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.
2017-03-30 13:09:58 +02:00

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);
}