Files
NetworkManager/shared/nm-utils/nm-enum-utils.c
2018-04-13 17:02:55 +02:00

316 lines
8.1 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,
const NMUtilsEnumValueInfo *value_infos)
{
nm_auto_unref_gtypeclass GTypeClass *class = NULL;
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;
for ( ; value_infos && value_infos->nick; value_infos++) {
if (value_infos->value == value)
return g_strdup (value_infos->nick);
}
enum_value = g_enum_get_value (G_ENUM_CLASS (class), value);
if ( !enum_value
|| !_enum_is_valid_enum_nick (enum_value->value_nick))
return g_strdup_printf ("%d", value);
else
return g_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 ?: " ";
for ( ; value_infos && value_infos->nick; value_infos++) {
nm_assert (_enum_is_valid_flags_nick (value_infos->nick));
if (uvalue == 0) {
if (value_infos->value != 0)
continue;
} else {
if (!NM_FLAGS_ALL (uvalue, (unsigned) value_infos->value))
continue;
}
if (str->len)
g_string_append (str, flags_separator);
g_string_append (str, value_infos->nick);
uvalue &= ~((unsigned) value_infos->value);
if (uvalue == 0) {
/* we printed all flags. Done. */
goto flags_done;
}
}
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);
flags_done:
return g_string_free (str, FALSE);
}
g_return_val_if_reached (NULL);
}
static const NMUtilsEnumValueInfo *
_find_value_info (const NMUtilsEnumValueInfo *value_infos, const char *needle)
{
if (value_infos) {
for (; value_infos->nick; value_infos++) {
if (nm_streq (needle, value_infos->nick))
return value_infos;
}
}
return NULL;
}
gboolean
_nm_utils_enum_from_str_full (GType type,
const char *str,
int *out_value,
char **err_token,
const NMUtilsEnumValueInfo *value_infos)
{
GTypeClass *class;
gboolean ret = FALSE;
int value = 0;
gs_free char *str_clone = NULL;
char *s;
gint64 v64;
const NMUtilsEnumValueInfo *nick;
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 {
nick = _find_value_info (value_infos, s);
if (nick) {
value = nick->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)
uvalue |= flags_value->value;
else {
nick = _find_value_info (value_infos, s);
if (nick)
uvalue = (unsigned) nick->value;
else {
ret = FALSE;
break;
}
}
}
}
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;
}
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 >= (guint) from && flags_value->value <= (guint) 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);
}