about summary refs log tree commit diff
path: root/shared/nm-glib-aux/nm-shared-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'shared/nm-glib-aux/nm-shared-utils.c')
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.c371
1 files changed, 351 insertions, 20 deletions
diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c
index cf08a77f..c8a253a6 100644
--- a/shared/nm-glib-aux/nm-shared-utils.c
+++ b/shared/nm-glib-aux/nm-shared-utils.c
@@ -1,4 +1,3 @@
-/* -*- 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
@@ -734,10 +733,7 @@ _nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 ma
 	gint64 v;
 	const char *s = NULL;
 
-	if (str) {
-		while (g_ascii_isspace (str[0]))
-			str++;
-	}
+	str = nm_str_skip_leading_spaces (str);
 	if (!str || !str[0]) {
 		errno = EINVAL;
 		return fallback;
@@ -748,9 +744,9 @@ _nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 ma
 
 	if (errno != 0)
 		return fallback;
+
 	if (s[0] != '\0') {
-		while (g_ascii_isspace (s[0]))
-			s++;
+		s = nm_str_skip_leading_spaces (s);
 		if (s[0] != '\0') {
 			errno = EINVAL;
 			return fallback;
@@ -810,6 +806,15 @@ _nm_utils_ascii_str_to_uint64 (const char *str, guint base, guint64 min, guint64
 
 /*****************************************************************************/
 
+int
+nm_strcmp_with_data (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+	const char *s1 = a;
+	const char *s2 = b;
+
+	return strcmp (s1, s2);
+}
+
 /* like nm_strcmp_p(), suitable for g_ptr_array_sort_with_data().
  * g_ptr_array_sort() just casts nm_strcmp_p() to a function of different
  * signature. I guess, in glib there are knowledgeable people that ensure
@@ -827,6 +832,15 @@ nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data)
 }
 
 int
+nm_strcmp0_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+	const char *s1 = *((const char **) a);
+	const char *s2 = *((const char **) b);
+
+	return nm_strcmp0 (s1, s2);
+}
+
+int
 nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data)
 {
 	const guint32 a = *((const guint32 *) p_a);
@@ -1350,6 +1364,8 @@ _nm_utils_strv_cleanup (char **strv,
 		return strv;
 
 	if (strip_whitespace) {
+		/* we only modify the strings pointed to by @strv if @strip_whitespace is
+		 * requested. Otherwise, the strings themselves are untouched. */
 		for (i = 0; strv[i]; i++)
 			g_strstrip (strv[i]);
 	}
@@ -2130,6 +2146,8 @@ nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll)
 	return 0;
 }
 
+/*****************************************************************************/
+
 NMUtilsNamedValue *
 nm_utils_named_values_from_str_dict (GHashTable *hash, guint *out_len)
 {
@@ -2154,15 +2172,106 @@ nm_utils_named_values_from_str_dict (GHashTable *hash, guint *out_len)
 	values[i].name = NULL;
 	values[i].value_ptr = NULL;
 
-	if (len > 1) {
-		g_qsort_with_data (values, len, sizeof (values[0]),
-		                   nm_utils_named_entry_cmp_with_data, NULL);
-	}
+	nm_utils_named_value_list_sort (values, len, NULL, NULL);
 
 	NM_SET_OUT (out_len, len);
 	return values;
 }
 
+gssize
+nm_utils_named_value_list_find (const NMUtilsNamedValue *arr,
+                                gsize len,
+                                const char *name,
+                                gboolean sorted)
+{
+	gsize i;
+
+	nm_assert (name);
+
+#if NM_MORE_ASSERTS > 5
+	{
+		for (i = 0; i < len; i++) {
+			const NMUtilsNamedValue *v = &arr[i];
+
+			nm_assert (v->name);
+			if (   sorted
+			    && i > 0)
+				nm_assert (strcmp (arr[i - 1].name, v->name) < 0);
+		}
+	}
+
+	nm_assert (   !sorted
+	           || nm_utils_named_value_list_is_sorted (arr, len, FALSE, NULL, NULL));
+#endif
+
+	if (sorted) {
+		return nm_utils_array_find_binary_search (arr,
+		                                          sizeof (NMUtilsNamedValue),
+		                                          len,
+		                                          &name,
+		                                          nm_strcmp_p_with_data,
+		                                          NULL);
+	}
+	for (i = 0; i < len; i++) {
+		if (nm_streq (arr[i].name, name))
+			return i;
+	}
+	return ~((gssize) len);
+}
+
+gboolean
+nm_utils_named_value_list_is_sorted (const NMUtilsNamedValue *arr,
+                                     gsize len,
+                                     gboolean accept_duplicates,
+                                     GCompareDataFunc compare_func,
+                                     gpointer user_data)
+{
+	gsize i;
+	int c_limit;
+
+	if (len == 0)
+		return TRUE;
+
+	g_return_val_if_fail (arr, FALSE);
+
+	if (!compare_func)
+		compare_func = nm_strcmp_p_with_data;
+
+	c_limit = accept_duplicates ? 0 : -1;
+
+	for (i = 1; i < len; i++) {
+		int c;
+
+		c = compare_func (&arr[i - 1], &arr[i], user_data);
+		if (c > c_limit)
+			return FALSE;
+	}
+	return TRUE;
+}
+
+void
+nm_utils_named_value_list_sort (NMUtilsNamedValue *arr,
+                                gsize len,
+                                GCompareDataFunc compare_func,
+                                gpointer user_data)
+{
+	if (len == 0)
+		return;
+
+	g_return_if_fail (arr);
+
+	if (len == 1)
+		return;
+
+	g_qsort_with_data (arr,
+	                   len,
+	                   sizeof (NMUtilsNamedValue),
+	                   compare_func ?: nm_strcmp_p_with_data,
+	                   user_data);
+}
+
+/*****************************************************************************/
+
 gpointer *
 nm_utils_hash_keys_to_array (GHashTable *hash,
                              GCompareDataFunc compare_func,
@@ -2193,12 +2302,41 @@ nm_utils_hash_keys_to_array (GHashTable *hash,
 	return keys;
 }
 
+gboolean
+nm_utils_hashtable_same_keys (const GHashTable *a,
+                              const GHashTable *b)
+{
+	GHashTableIter h;
+	const char *k;
+
+	if (a == b)
+		return TRUE;
+	if (!a || !b)
+		return FALSE;
+	if (g_hash_table_size ((GHashTable *) a) != g_hash_table_size ((GHashTable *) b))
+		return FALSE;
+
+	g_hash_table_iter_init (&h, (GHashTable *) a);
+	while (g_hash_table_iter_next (&h, (gpointer) &k, NULL)) {
+		if (!g_hash_table_contains ((GHashTable *) b, k))
+			return FALSE;
+	}
+
+#if NM_MORE_ASSERTS > 5
+	g_hash_table_iter_init (&h, (GHashTable *) b);
+	while (g_hash_table_iter_next (&h, (gpointer) &k, NULL))
+		nm_assert (g_hash_table_contains ((GHashTable *) a, k));
+#endif
+
+	return TRUE;
+}
+
 char **
 nm_utils_strv_make_deep_copied (const char **strv)
 {
 	gsize i;
 
-	/* it takes a strv dictionary, and copies each
+	/* it takes a strv list, and copies each
 	 * strings. Note that this updates @strv *in-place*
 	 * and returns it. */
 
@@ -2210,6 +2348,79 @@ nm_utils_strv_make_deep_copied (const char **strv)
 	return (char **) strv;
 }
 
+char **
+nm_utils_strv_make_deep_copied_n (const char **strv, gsize len)
+{
+	gsize i;
+
+	/* it takes a strv array with len elements, and copies each
+	 * strings. Note that this updates @strv *in-place*
+	 * and returns it. */
+
+	if (!strv)
+		return NULL;
+	for (i = 0; i < len; i++)
+		strv[i] = g_strdup (strv[i]);
+
+	return (char **) strv;
+}
+
+/**
+ * @strv: the strv array to copy. It may be %NULL if @len
+ *   is negative or zero (in which case %NULL will be returned).
+ * @len: the length of strings in @str. If negative, strv is assumed
+ *   to be a NULL terminated array.
+ *
+ * Like g_strdupv(), with two differences:
+ *
+ * - accepts a @len parameter for non-null terminated strv array.
+ *
+ * - this never returns an empty strv array, but always %NULL if
+ *   there are no strings.
+ *
+ * Note that if @len is non-negative, then it still must not
+ * contain any %NULL pointers within the first @len elements.
+ * Otherwise you would leak elements if you try to free the
+ * array with g_strfreev(). Allowing that would be error prone.
+ *
+ * Returns: (transfer full): a clone of the strv array. Always
+ *   %NULL terminated.
+ */
+char **
+nm_utils_strv_dup (gpointer strv, gssize len)
+{
+	gsize i, l;
+	char **v;
+	const char *const *const src = strv;
+
+	if (len < 0)
+		l = NM_PTRARRAY_LEN (src);
+	else
+		l = len;
+	if (l == 0) {
+		/* this function never returns an empty strv array. If you
+		 * need that, handle it yourself. */
+		return NULL;
+	}
+
+	v = g_new (char *, l + 1);
+	for (i = 0; i < l; i++) {
+
+		if (G_UNLIKELY (!src[i])) {
+			/* NULL strings are not allowed. Clear the remainder of the array
+			 * and return it (with assertion failure). */
+			l++;
+			for (; i < l; i++)
+				v[i] = NULL;
+			g_return_val_if_reached (v);
+		}
+
+		v[i] = g_strdup (src[i]);
+	}
+	v[l] = NULL;
+	return v;
+}
+
 /*****************************************************************************/
 
 gssize
@@ -2499,8 +2710,8 @@ fail:
  * @len: the number of elements in strv. If negative,
  *   strv must be a NULL terminated array and the length
  *   will be calculated first. If @len is a positive
- *   number, all first @len elements in @strv must be
- *   non-NULL, valid strings.
+ *   number, @strv is allowed to contain %NULL strings
+ *   too.
  *
  * Ascending sort of the array @strv inplace, using plain strcmp() string
  * comparison.
@@ -2508,9 +2719,16 @@ fail:
 void
 _nm_utils_strv_sort (const char **strv, gssize len)
 {
+	GCompareDataFunc cmp;
 	gsize l;
 
-	l = len < 0 ? (gsize) NM_PTRARRAY_LEN (strv) : (gsize) len;
+	if (len < 0) {
+		l = NM_PTRARRAY_LEN (strv);
+		cmp = nm_strcmp_p_with_data;
+	} else {
+		l = len;
+		cmp = nm_strcmp0_p_with_data;
+	}
 
 	if (l <= 1)
 		return;
@@ -2520,7 +2738,7 @@ _nm_utils_strv_sort (const char **strv, gssize len)
 	g_qsort_with_data (strv,
 	                   l,
 	                   sizeof (const char *),
-	                   nm_strcmp_p_with_data,
+	                   cmp,
 	                   NULL);
 }
 
@@ -2577,6 +2795,53 @@ _nm_utils_strv_cmp_n (const char *const*strv1,
 
 /*****************************************************************************/
 
+/**
+ * nm_utils_g_slist_find_str:
+ * @list: the #GSList with NUL terminated strings to search
+ * @needle: the needle string to look for.
+ *
+ * Search the list for @needle and return the first found match
+ * (or %NULL if not found). Uses strcmp() for finding the first matching
+ * element.
+ *
+ * Returns: the #GSList element with @needle as string value or
+ *   %NULL if not found.
+ */
+GSList *
+nm_utils_g_slist_find_str (const GSList *list,
+                           const char *needle)
+{
+	nm_assert (needle);
+
+	for (; list; list = list->next) {
+		nm_assert (list->data);
+		if (nm_streq (list->data, needle))
+			return (GSList *) list;
+	}
+	return NULL;
+}
+
+/**
+ * nm_utils_g_slist_strlist_cmp:
+ * @a: the left #GSList of strings
+ * @b: the right #GSList of strings to compare.
+ *
+ * Compares two string lists. The data elements are compared with
+ * strcmp(), alloing %NULL elements.
+ *
+ * Returns: 0, 1, or -1, depending on how the lists compare.
+ */
+int
+nm_utils_g_slist_strlist_cmp (const GSList *a, const GSList *b)
+{
+	for (; a && b; a = a->next, b = b->next)
+		NM_CMP_DIRECT_STRCMP0 (a->data, b->data);
+	NM_CMP_SELF (a, b);
+	return 0;
+}
+
+/*****************************************************************************/
+
 gpointer
 _nm_utils_user_data_pack (int nargs, gconstpointer *args)
 {
@@ -2753,10 +3018,13 @@ nm_utils_memeqzero (gconstpointer data, gsize length)
  *   be returned and must be freed by the caller.
  *   If not %NULL, the buffer must already be preallocated and contain
  *   at least (@length*2+1) or (@length*3) bytes, depending on the delimiter.
+ *   If @length is zero, then of course at least one byte will be allocated
+ *   or @out (if given) must contain at least room for the trailing NUL byte.
  *
  * Returns: the binary value converted to a hex string. If @out is given,
  *   this always returns @out. If @out is %NULL, a newly allocated string
- *   is returned.
+ *   is returned. This never returns %NULL, for buffers of length zero
+ *   an empty string is returend.
  */
 char *
 nm_utils_bin2hexstr_full (gconstpointer addr,
@@ -2772,9 +3040,11 @@ nm_utils_bin2hexstr_full (gconstpointer addr,
 	if (out)
 		out0 = out;
 	else {
-		out0 = out = g_new (char, delimiter == '\0'
-		                          ? length * 2 + 1
-		                          : length * 3);
+		out0 = out = g_new (char, length == 0
+		                          ? 1u
+		                          : (  delimiter == '\0'
+		                             ? length * 2u + 1u
+		                             : length * 3u));
 	}
 
 	/* @out must contain at least @length*3 bytes if @delimiter is set,
@@ -2939,3 +3209,64 @@ fail:
 	NM_SET_OUT (out_len, 0);
 	return NULL;
 }
+
+/*****************************************************************************/
+
+GVariant *
+nm_utils_gvariant_vardict_filter (GVariant *src,
+                                  gboolean (*filter_fcn) (const char *key,
+                                                          GVariant *val,
+                                                          char **out_key,
+                                                          GVariant **out_val,
+                                                          gpointer user_data),
+                                  gpointer user_data)
+{
+	GVariantIter iter;
+	GVariantBuilder builder;
+	const char *key;
+	GVariant *val;
+
+	g_return_val_if_fail (src && g_variant_is_of_type (src, G_VARIANT_TYPE_VARDICT), NULL);
+	g_return_val_if_fail (filter_fcn, NULL);
+
+	g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+
+	g_variant_iter_init (&iter, src);
+	while (g_variant_iter_next (&iter, "{&sv}", &key, &val)) {
+		_nm_unused gs_unref_variant GVariant *val_free = val;
+		gs_free char *key2 = NULL;
+		gs_unref_variant GVariant *val2 = NULL;
+
+		if (filter_fcn (key,
+		                val,
+		                &key2,
+		                &val2,
+		                user_data)) {
+			g_variant_builder_add (&builder,
+			                       "{sv}",
+			                       key2 ?: key,
+			                       val2 ?: val);
+		}
+	}
+
+	return g_variant_builder_end (&builder);
+}
+
+static gboolean
+_gvariant_vardict_filter_drop_one (const char *key,
+                                   GVariant *val,
+                                   char **out_key,
+                                   GVariant **out_val,
+                                   gpointer user_data)
+{
+	return !nm_streq (key, user_data);
+}
+
+GVariant *
+nm_utils_gvariant_vardict_filter_drop_one (GVariant *src,
+                                           const char *key)
+{
+	return nm_utils_gvariant_vardict_filter (src,
+	                                         _gvariant_vardict_filter_drop_one,
+	                                         (gpointer) key);
+}