mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-25 06:00:08 +01:00
libnm-core: add attribute parsing/format helpers
Various libnm objects (addresses, routes) carry an hash table of attributes represented as GVariants indexed by name. Add common routines to convert to and from a string representation. To parse a string, a knowledge of the supported attributes (and their types) is needed: we represent it as an opaque type NMVariantAttributeSpec that callers must query to the library for the specific object type and pass to the parse function.
This commit is contained in:
parent
45dc2feded
commit
93b3a478bb
4 changed files with 277 additions and 0 deletions
|
|
@ -28,6 +28,11 @@
|
|||
#include "nm-setting-private.h"
|
||||
#include "nm-setting-ip-config.h"
|
||||
|
||||
struct _NMVariantAttributeSpec {
|
||||
char *name;
|
||||
const GVariantType *type;
|
||||
};
|
||||
|
||||
gboolean _nm_utils_string_slist_validate (GSList *list,
|
||||
const char **valid_values);
|
||||
|
||||
|
|
|
|||
|
|
@ -4697,6 +4697,261 @@ _nm_utils_team_config_equal (const char *conf1,
|
|||
}
|
||||
#endif
|
||||
|
||||
static char *
|
||||
attribute_escape (const char *src, char c1, char c2)
|
||||
{
|
||||
char *ret, *dest;
|
||||
|
||||
dest = ret = malloc (strlen (src) * 2 + 1);
|
||||
|
||||
while (*src) {
|
||||
if (*src == c1 || *src == c2 || *src == '\\')
|
||||
*dest++ = '\\';
|
||||
*dest++ = *src++;
|
||||
}
|
||||
*dest++ = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *
|
||||
attribute_unescape (const char *start, const char *end)
|
||||
{
|
||||
char *ret, *dest;
|
||||
|
||||
nm_assert (start <= end);
|
||||
dest = ret = g_malloc (end - start + 1);
|
||||
|
||||
for (; start < end && *start; start++) {
|
||||
if (*start == '\\') {
|
||||
start++;
|
||||
if (!*start)
|
||||
break;
|
||||
}
|
||||
*dest++ = *start;
|
||||
}
|
||||
*dest = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_utils_parse_variant_attributes:
|
||||
* @string: the input string
|
||||
* @attr_separator: the attribute separator character
|
||||
* @key_value_separator: character separating key and values
|
||||
* @ignore_unknown: whether unknown attributes should be ignored
|
||||
* @spec: the attribute format specifiers
|
||||
* @error: (out) (allow-none): location to store the error on failure
|
||||
*
|
||||
* Parse attributes from a string.
|
||||
*
|
||||
* Returns: (transfer full): a #GHashTable mapping attribute names to #GVariant values.
|
||||
*
|
||||
* Since: 1.8
|
||||
*/
|
||||
GHashTable *
|
||||
nm_utils_parse_variant_attributes (const char *string,
|
||||
char attr_separator,
|
||||
char key_value_separator,
|
||||
gboolean ignore_unknown,
|
||||
const NMVariantAttributeSpec *const *spec,
|
||||
GError **error)
|
||||
{
|
||||
gs_unref_hashtable GHashTable *ht = NULL;
|
||||
const char *ptr = string, *start = NULL, *sep;
|
||||
GVariant *variant;
|
||||
const NMVariantAttributeSpec * const *s;
|
||||
|
||||
g_return_val_if_fail (string, NULL);
|
||||
g_return_val_if_fail (attr_separator, NULL);
|
||||
g_return_val_if_fail (key_value_separator, NULL);
|
||||
g_return_val_if_fail (!error || !*error, NULL);
|
||||
|
||||
ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
|
||||
|
||||
while (TRUE) {
|
||||
gs_free char *name = NULL, *value = NULL;
|
||||
|
||||
if (!start)
|
||||
start = ptr;
|
||||
if (*ptr == '\\') {
|
||||
ptr++;
|
||||
if (!*ptr) {
|
||||
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED,
|
||||
_("unterminated escape sequence"));
|
||||
return NULL;
|
||||
}
|
||||
goto next;
|
||||
}
|
||||
if (*ptr == attr_separator || *ptr == '\0') {
|
||||
if (ptr == start) {
|
||||
/* multiple separators */
|
||||
start = NULL;
|
||||
goto next;
|
||||
}
|
||||
|
||||
/* Find the key-value separator */
|
||||
for (sep = start; sep != ptr; sep++) {
|
||||
if (*sep == '\\') {
|
||||
sep++;
|
||||
if (!*sep) {
|
||||
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED,
|
||||
_("unterminated escape sequence"));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (*sep == key_value_separator)
|
||||
break;
|
||||
}
|
||||
|
||||
if (*sep != key_value_separator) {
|
||||
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED,
|
||||
_("missing key-value separator '%c'"), key_value_separator);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
name = attribute_unescape (start, sep);
|
||||
value = attribute_unescape (sep + 1, ptr);
|
||||
|
||||
for (s = spec; *s; s++) {
|
||||
if (nm_streq (name, (*s)->name))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!*s) {
|
||||
if (ignore_unknown)
|
||||
goto next;
|
||||
else {
|
||||
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED,
|
||||
_("unknown attribute '%s'"), name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_variant_type_equal ((*s)->type, G_VARIANT_TYPE_UINT32)) {
|
||||
gint64 num = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1);
|
||||
|
||||
if (num == -1) {
|
||||
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED,
|
||||
_("invalid uint32 value '%s' for attribute '%s'"), value, name);
|
||||
return NULL;
|
||||
}
|
||||
variant = g_variant_new_uint32 (num);
|
||||
} else if (g_variant_type_equal ((*s)->type, G_VARIANT_TYPE_BYTE)) {
|
||||
gint64 num = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT8, -1);
|
||||
|
||||
if (num == -1) {
|
||||
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED,
|
||||
_("invalid uint8 value '%s' for attribute '%s'"), value, name);
|
||||
return NULL;
|
||||
}
|
||||
variant = g_variant_new_byte ((guchar) num);
|
||||
} else if (g_variant_type_equal ((*s)->type, G_VARIANT_TYPE_BOOLEAN)) {
|
||||
gboolean b;
|
||||
|
||||
if (nm_streq (value, "true"))
|
||||
b = TRUE;
|
||||
else if (nm_streq (value, "false"))
|
||||
b = FALSE;
|
||||
else {
|
||||
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED,
|
||||
_("invalid boolean value '%s' for attribute '%s'"), value, name);
|
||||
return NULL;
|
||||
}
|
||||
variant = g_variant_new_boolean (b);
|
||||
} else if (g_variant_type_equal ((*s)->type, G_VARIANT_TYPE_STRING)) {
|
||||
variant = g_variant_new_take_string (g_steal_pointer (&value));
|
||||
} else {
|
||||
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED,
|
||||
_("unsupported attribute '%s' of type '%s'"), name,
|
||||
(char *) (*s)->type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_hash_table_insert (ht, g_steal_pointer (&name), variant);
|
||||
start = NULL;
|
||||
}
|
||||
next:
|
||||
if (*ptr == '\0')
|
||||
break;
|
||||
ptr++;
|
||||
}
|
||||
|
||||
return g_steal_pointer (&ht);
|
||||
}
|
||||
|
||||
/*
|
||||
* nm_utils_format_variant_attributes:
|
||||
* @attributes: a #GHashTable mapping attribute names to #GVariant values
|
||||
* @attr_separator: the attribute separator character
|
||||
* @key_value_separator: character separating key and values
|
||||
*
|
||||
* Format attributes to a string.
|
||||
*
|
||||
* Returns: (transfer full): the string representing attributes, or %NULL
|
||||
* in case there are no attributes
|
||||
*
|
||||
* Since: 1.8
|
||||
*/
|
||||
char *
|
||||
nm_utils_format_variant_attributes (GHashTable *attributes,
|
||||
char attr_separator,
|
||||
char key_value_separator)
|
||||
{
|
||||
GString *str = NULL;
|
||||
GVariant *variant;
|
||||
char sep = 0;
|
||||
const char *name, *value;
|
||||
char *escaped;
|
||||
char buf[64];
|
||||
gs_free_list GList *keys = NULL;
|
||||
GList *iter;
|
||||
|
||||
g_return_val_if_fail (attr_separator, NULL);
|
||||
g_return_val_if_fail (key_value_separator, NULL);
|
||||
|
||||
if (!attributes || !g_hash_table_size (attributes))
|
||||
return NULL;
|
||||
|
||||
keys = g_list_sort (g_hash_table_get_keys (attributes), (GCompareFunc) g_strcmp0);
|
||||
str = g_string_new ("");
|
||||
|
||||
for (iter = keys; iter; iter = g_list_next (iter)) {
|
||||
name = iter->data;
|
||||
variant = g_hash_table_lookup (attributes, name);
|
||||
value = NULL;
|
||||
|
||||
if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32))
|
||||
value = nm_sprintf_buf (buf, "%u", g_variant_get_uint32 (variant));
|
||||
else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BYTE))
|
||||
value = nm_sprintf_buf (buf, "%hhu", g_variant_get_byte (variant));
|
||||
else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN))
|
||||
value = g_variant_get_boolean (variant) ? "true" : "false";
|
||||
else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING))
|
||||
value = g_variant_get_string (variant, NULL);
|
||||
else
|
||||
continue;
|
||||
|
||||
if (sep)
|
||||
g_string_append_c (str, sep);
|
||||
|
||||
escaped = attribute_escape (name, attr_separator, key_value_separator);
|
||||
g_string_append (str, escaped);
|
||||
g_free (escaped);
|
||||
|
||||
g_string_append_c (str, key_value_separator);
|
||||
|
||||
escaped = attribute_escape (value, attr_separator, key_value_separator);
|
||||
g_string_append (str, escaped);
|
||||
g_free (escaped);
|
||||
|
||||
sep = attr_separator;
|
||||
}
|
||||
|
||||
return g_string_free (str, FALSE);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@
|
|||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _NMVariantAttributeSpec NMVariantAttributeSpec;
|
||||
|
||||
/* SSID helpers */
|
||||
gboolean nm_utils_is_empty_ssid (const guint8 *ssid, gsize len);
|
||||
const char *nm_utils_escape_ssid (const guint8 *ssid, gsize len);
|
||||
|
|
@ -215,6 +217,19 @@ const char **nm_utils_enum_get_values (GType type, gint from, gint to);
|
|||
NM_AVAILABLE_IN_1_6
|
||||
guint nm_utils_version (void);
|
||||
|
||||
NM_AVAILABLE_IN_1_8
|
||||
GHashTable * nm_utils_parse_variant_attributes (const char *string,
|
||||
char attr_separator,
|
||||
char key_value_separator,
|
||||
gboolean ignore_unknown,
|
||||
const NMVariantAttributeSpec *const *spec,
|
||||
GError **error);
|
||||
|
||||
NM_AVAILABLE_IN_1_8
|
||||
char * nm_utils_format_variant_attributes (GHashTable *attributes,
|
||||
char attr_separator,
|
||||
char key_value_separator);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __NM_UTILS_H__ */
|
||||
|
|
|
|||
|
|
@ -1163,4 +1163,6 @@ global:
|
|||
nm_setting_dummy_get_type;
|
||||
nm_setting_dummy_new;
|
||||
nm_setting_gsm_get_mtu;
|
||||
nm_utils_format_variant_attributes;
|
||||
nm_utils_parse_variant_attributes;
|
||||
} libnm_1_6_0;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue