about summary refs log tree commit diff
path: root/shared/nm-utils/nm-enum-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'shared/nm-utils/nm-enum-utils.c')
-rw-r--r--shared/nm-utils/nm-enum-utils.c181
1 files changed, 128 insertions, 53 deletions
diff --git a/shared/nm-utils/nm-enum-utils.c b/shared/nm-utils/nm-enum-utils.c
index 70a8b415..023f8385 100644
--- a/shared/nm-utils/nm-enum-utils.c
+++ b/shared/nm-utils/nm-enum-utils.c
@@ -27,6 +27,62 @@
 
 #define IS_FLAGS_SEPARATOR(ch)  (NM_IN_SET ((ch), ' ', '\t', ',', '\n', '\r'))
 
+static void
+_ASSERT_enum_values_info (GType type,
+                          const NMUtilsEnumValueInfo *value_infos)
+{
+#if NM_MORE_ASSERTS > 5
+	nm_auto_unref_gtypeclass GTypeClass *klass = NULL;
+	gs_unref_hashtable GHashTable *ht = NULL;
+
+	klass = g_type_class_ref (type);
+
+	g_assert (G_IS_ENUM_CLASS (klass) || G_IS_FLAGS_CLASS (klass));
+
+	if (!value_infos)
+		return;
+
+	ht = g_hash_table_new (g_str_hash, g_str_equal);
+
+	for (; value_infos->nick; value_infos++) {
+
+		g_assert (value_infos->nick[0]);
+
+		/* duplicate nicks make no sense!! */
+		g_assert (!g_hash_table_contains (ht, value_infos->nick));
+		g_hash_table_add (ht, (gpointer) value_infos->nick);
+
+		if (G_IS_ENUM_CLASS (klass)) {
+			GEnumValue *enum_value;
+
+			enum_value = g_enum_get_value_by_nick (G_ENUM_CLASS (klass), value_infos->nick);
+			if (enum_value) {
+				/* we do allow specifying the same name via @value_infos and @type.
+				 * That might make sense, if @type comes from a library where older versions
+				 * of the library don't yet support the value. In this case, the caller can
+				 * provide the nick via @value_infos, to support the older library version.
+				 * And then, when actually running against a newer library version where
+				 * @type knows the nick, we have this situation.
+				 *
+				 * However, what never is allowed, is to use a name (nick) to re-number
+				 * the value. That is, if both @value_infos and @type contain a particular
+				 * nick, their numeric values must agree as well.
+				 */
+				g_assert (enum_value->value == value_infos->value);
+			}
+		} else {
+			GFlagsValue *flags_value;
+
+			flags_value = g_flags_get_value_by_nick (G_FLAGS_CLASS (klass), value_infos->nick);
+			if (flags_value) {
+				/* see ENUM case above. */
+				g_assert (flags_value->value == (guint) value_infos->value);
+			}
+		}
+	}
+#endif
+}
+
 static gboolean
 _is_hex_string (const char *str)
 {
@@ -64,36 +120,65 @@ _enum_is_valid_flags_nick (const char *str)
 char *
 _nm_utils_enum_to_str_full (GType type,
                             int value,
-                            const char *flags_separator)
+                            const char *flags_separator,
+                            const NMUtilsEnumValueInfo *value_infos)
 {
-	GTypeClass *class;
-	char *ret;
+	nm_auto_unref_gtypeclass GTypeClass *klass = NULL;
+
+	_ASSERT_enum_values_info (type, value_infos);
 
 	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);
+	klass = g_type_class_ref (type);
 
-	if (G_IS_ENUM_CLASS (class)) {
+	if (G_IS_ENUM_CLASS (klass)) {
 		GEnumValue *enum_value;
 
-		enum_value = g_enum_get_value (G_ENUM_CLASS (class), 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 (klass), value);
 		if (   !enum_value
 		    || !_enum_is_valid_enum_nick (enum_value->value_nick))
-			ret = g_strdup_printf ("%d", value);
+			return g_strdup_printf ("%d", value);
 		else
-			ret = strdup (enum_value->value_nick);
-	} else if (G_IS_FLAGS_CLASS (class)) {
+			return g_strdup (enum_value->value_nick);
+	} else if (G_IS_FLAGS_CLASS (klass)) {
 		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);
+			flags_value = g_flags_get_first_value (G_FLAGS_CLASS (klass), uvalue);
 			if (str->len)
 				g_string_append (str, flags_separator);
 			if (   !flags_value
@@ -105,12 +190,12 @@ _nm_utils_enum_to_str_full (GType type,
 			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;
+flags_done:
+		return g_string_free (str, FALSE);
+	}
+
+	g_return_val_if_reached (NULL);
 }
 
 static const NMUtilsEnumValueInfo *
@@ -132,7 +217,7 @@ _nm_utils_enum_from_str_full (GType type,
                               char **err_token,
                               const NMUtilsEnumValueInfo *value_infos)
 {
-	GTypeClass *class;
+	GTypeClass *klass;
 	gboolean ret = FALSE;
 	int value = 0;
 	gs_free char *str_clone = NULL;
@@ -142,13 +227,15 @@ _nm_utils_enum_from_str_full (GType type,
 
 	g_return_val_if_fail (str, FALSE);
 
+	_ASSERT_enum_values_info (type, value_infos);
+
 	str_clone = strdup (str);
 	s = nm_str_skip_leading_spaces (str_clone);
 	g_strchomp (s);
 
-	class = g_type_class_ref (type);
+	klass = g_type_class_ref (type);
 
-	if (G_IS_ENUM_CLASS (class)) {
+	if (G_IS_ENUM_CLASS (klass)) {
 		GEnumValue *enum_value;
 
 		if (s[0]) {
@@ -164,21 +251,15 @@ _nm_utils_enum_from_str_full (GType type,
 					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 ((nick = _find_value_info (value_infos, s))) {
+				value = nick->value;
+				ret = TRUE;
+			} else if ((enum_value = g_enum_get_value_by_nick (G_ENUM_CLASS (klass), s))) {
+				value = enum_value->value;
+				ret = TRUE;
 			}
 		}
-	} else if (G_IS_FLAGS_CLASS (class)) {
+	} else if (G_IS_FLAGS_CLASS (klass)) {
 		GFlagsValue *flags_value;
 		unsigned uvalue = 0;
 
@@ -209,19 +290,13 @@ _nm_utils_enum_from_str_full (GType type,
 						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;
-						}
-					}
+				} else if ((nick = _find_value_info (value_infos, s)))
+					uvalue |= (unsigned) nick->value;
+				else if ((flags_value = g_flags_get_value_by_nick (G_FLAGS_CLASS (klass), s)))
+					uvalue |= flags_value->value;
+				else {
+					ret = FALSE;
+					break;
 				}
 			}
 
@@ -234,23 +309,23 @@ _nm_utils_enum_from_str_full (GType type,
 
 	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);
+	g_type_class_unref (klass);
 	return ret;
 }
 
 const char **
 _nm_utils_enum_get_values (GType type, gint from, gint to)
 {
-	GTypeClass *class;
+	GTypeClass *klass;
 	GPtrArray *array;
 	gint i;
 	char sbuf[64];
 
-	class = g_type_class_ref (type);
+	klass = g_type_class_ref (type);
 	array = g_ptr_array_new ();
 
-	if (G_IS_ENUM_CLASS (class)) {
-		GEnumClass *enum_class = G_ENUM_CLASS (class);
+	if (G_IS_ENUM_CLASS (klass)) {
+		GEnumClass *enum_class = G_ENUM_CLASS (klass);
 		GEnumValue *enum_value;
 
 		for (i = 0; i < enum_class->n_values; i++) {
@@ -262,8 +337,8 @@ _nm_utils_enum_get_values (GType type, gint from, gint to)
 					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);
+	} else if (G_IS_FLAGS_CLASS (klass)) {
+		GFlagsClass *flags_class = G_FLAGS_CLASS (klass);
 		GFlagsValue *flags_value;
 
 		for (i = 0; i < flags_class->n_values; i++) {
@@ -276,12 +351,12 @@ _nm_utils_enum_get_values (GType type, gint from, gint to)
 			}
 		}
 	} else {
-		g_type_class_unref (class);
+		g_type_class_unref (klass);
 		g_ptr_array_free (array, TRUE);
 		g_return_val_if_reached (NULL);
 	}
 
-	g_type_class_unref (class);
+	g_type_class_unref (klass);
 	g_ptr_array_add (array, NULL);
 
 	return (const char **) g_ptr_array_free (array, FALSE);