summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2019-03-21 10:22:55 +0100
committerThomas Haller <thaller@redhat.com>2019-03-24 09:21:13 +0100
commitacf1cf61cf406729ef2704e6ace22b0367c90e77 (patch)
treec15ccb7fd2c64246fcdc0b5a04908223a7466857
parent0528c1e9787f58f1528d15bc46695b41eacd5526 (diff)
downloadNetworkManager-acf1cf61cf406729ef2704e6ace22b0367c90e77.tar.gz
shared: add _nm_utils_strv_cmp_n() and _nm_utils_strv_equal()
-rw-r--r--libnm-core/nm-core-internal.h1
-rw-r--r--libnm-core/nm-utils.c24
-rw-r--r--shared/nm-utils/nm-shared-utils.c51
-rw-r--r--shared/nm-utils/nm-shared-utils.h12
-rw-r--r--shared/nm-utils/tests/test-shared-general.c145
5 files changed, 208 insertions, 25 deletions
diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h
index f2332f766b..c60b42c6dc 100644
--- a/libnm-core/nm-core-internal.h
+++ b/libnm-core/nm-core-internal.h
@@ -284,7 +284,6 @@ char ** _nm_utils_slist_to_strv (GSList *slist, gboolean deep_copy);
GPtrArray * _nm_utils_strv_to_ptrarray (char **strv);
char ** _nm_utils_ptrarray_to_strv (GPtrArray *ptrarray);
-gboolean _nm_utils_strv_equal (char **strv1, char **strv2);
gboolean _nm_utils_check_file (const char *filename,
gint64 check_owner,
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c
index d276cfe62e..30ed43424c 100644
--- a/libnm-core/nm-utils.c
+++ b/libnm-core/nm-utils.c
@@ -1034,30 +1034,6 @@ _nm_utils_ptrarray_to_strv (GPtrArray *ptrarray)
return strv;
}
-/**
- * _nm_utils_strv_equal:
- * @strv1: a string array
- * @strv2: a string array
- *
- * Compare NULL-terminated string arrays for equality.
- *
- * Returns: %TRUE if the arrays are equal, %FALSE otherwise.
- **/
-gboolean
-_nm_utils_strv_equal (char **strv1, char **strv2)
-{
- if (strv1 == strv2)
- return TRUE;
-
- if (!strv1 || !strv2)
- return FALSE;
-
- for ( ; *strv1 && *strv2 && !strcmp (*strv1, *strv2); strv1++, strv2++)
- ;
-
- return !*strv1 && !*strv2;
-}
-
static gboolean
device_supports_ap_ciphers (guint32 dev_caps,
guint32 ap_flags,
diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c
index 6a43c67063..93e0b97e20 100644
--- a/shared/nm-utils/nm-shared-utils.c
+++ b/shared/nm-utils/nm-shared-utils.c
@@ -2318,6 +2318,57 @@ _nm_utils_strv_sort (const char **strv, gssize len)
NULL);
}
+/**
+ * _nm_utils_strv_cmp_n:
+ * @strv1: a string array
+ * @len1: the length of @strv1, or -1 for NULL terminated array.
+ * @strv2: a string array
+ * @len2: the length of @strv2, or -1 for NULL terminated array.
+ *
+ * Note that
+ * - len == -1 && strv == NULL
+ * is treated like a %NULL argument and compares differently from
+ * other arrays.
+ *
+ * Note that an empty array can be represented as
+ * - len == -1 && strv && !strv[0]
+ * - len == 0 && !strv
+ * - len == 0 && strv
+ * These 3 forms all compare equal.
+ * It also means, if length is 0, then it is permissible for strv to be %NULL.
+ *
+ * The strv arrays may contain %NULL strings (if len is positive).
+ *
+ * Returns: 0 if the arrays are equal (using strcmp).
+ **/
+int
+_nm_utils_strv_cmp_n (const char *const*strv1,
+ gssize len1,
+ const char *const*strv2,
+ gssize len2)
+{
+ gsize n, n2;
+
+ if (len1 < 0) {
+ if (!strv1)
+ return (len2 < 0 && !strv2) ? 0 : -1;
+ n = NM_PTRARRAY_LEN (strv1);
+ } else
+ n = len1;
+
+ if (len2 < 0) {
+ if (!strv2)
+ return 1;
+ n2 = NM_PTRARRAY_LEN (strv2);
+ } else
+ n2 = len2;
+
+ NM_CMP_DIRECT (n, n2);
+ for (; n > 0; n--, strv1++, strv2++)
+ NM_CMP_DIRECT_STRCMP0 (*strv1, *strv2);
+ return 0;
+}
+
/*****************************************************************************/
gpointer
diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h
index 65e3495983..51e57ee03f 100644
--- a/shared/nm-utils/nm-shared-utils.h
+++ b/shared/nm-utils/nm-shared-utils.h
@@ -941,6 +941,18 @@ gboolean nm_utils_hash_table_equal (const GHashTable *a,
void _nm_utils_strv_sort (const char **strv, gssize len);
#define nm_utils_strv_sort(strv, len) _nm_utils_strv_sort (NM_CAST_STRV_MC (strv), len)
+int _nm_utils_strv_cmp_n (const char *const*strv1,
+ gssize len1,
+ const char *const*strv2,
+ gssize len2);
+
+static inline gboolean
+_nm_utils_strv_equal (char **strv1, char **strv2)
+{
+ return _nm_utils_strv_cmp_n ((const char *const*) strv1, -1,
+ (const char *const*) strv2, -1) == 0;
+}
+
/*****************************************************************************/
#define NM_UTILS_NS_PER_SECOND ((gint64) 1000000000)
diff --git a/shared/nm-utils/tests/test-shared-general.c b/shared/nm-utils/tests/test-shared-general.c
index d53b21d972..5b220c10e0 100644
--- a/shared/nm-utils/tests/test-shared-general.c
+++ b/shared/nm-utils/tests/test-shared-general.c
@@ -248,6 +248,150 @@ test_unaligned (void)
/*****************************************************************************/
+static void
+_strv_cmp_fuzz_input (const char *const*in,
+ gssize l,
+ const char ***out_strv_free_shallow,
+ char ***out_strv_free_deep,
+ const char *const* *out_s1,
+ const char *const* *out_s2)
+{
+ const char **strv;
+ gsize i;
+
+ /* Fuzz the input argument. It will return two output arrays that are semantically
+ * equal the input. */
+
+ if (nmtst_get_rand_bool ()) {
+ char **ss;
+
+ if (l < 0)
+ ss = g_strdupv ((char **) in);
+ else if (l == 0) {
+ ss = nmtst_get_rand_bool ()
+ ? NULL
+ : g_new0 (char *, 1);
+ } else {
+ ss = nm_memdup (in, sizeof (const char *) * l);
+ for (i = 0; i < (gsize) l; i++)
+ ss[i] = g_strdup (ss[i]);
+ }
+ strv = (const char **) ss;
+ *out_strv_free_deep = ss;
+ } else {
+ if (l < 0) {
+ strv = in
+ ? nm_memdup (in, sizeof (const char *) * (NM_PTRARRAY_LEN (in) + 1))
+ : NULL;
+ } else if (l == 0) {
+ strv = nmtst_get_rand_bool ()
+ ? NULL
+ : g_new0 (const char *, 1);
+ } else
+ strv = nm_memdup (in, sizeof (const char *) * l);
+ *out_strv_free_shallow = strv;
+ }
+
+ *out_s1 = in;
+ *out_s2 = strv;
+
+ if (nmtst_get_rand_bool ()) {
+ /* randomly swap the original and the clone. That means, out_s1 is either
+ * the input argument (as-is) or the sementically equal clone. */
+ NMTST_SWAP (*out_s1, *out_s2);
+ }
+ if (nmtst_get_rand_bool ()) {
+ /* randomly make s1 and s2 the same. This is for testing that
+ * comparing two identical pointers yields the same result. */
+ *out_s2 = *out_s1;
+ }
+}
+
+static void
+_strv_cmp_free_deep (char **strv,
+ gssize len)
+{
+ gssize i;
+
+ if (strv) {
+ if (len < 0)
+ g_strfreev (strv);
+ else {
+ for (i = 0; i < len; i++)
+ g_free (strv[i]);
+ g_free (strv);
+ }
+ }
+}
+
+static void
+test_strv_cmp (void)
+{
+ const char *const strv0[1] = { };
+ const char *const strv1[2] = { "", };
+
+#define _STRV_CMP(a1, l1, a2, l2, equal) \
+ G_STMT_START { \
+ gssize _l1 = (l1); \
+ gssize _l2 = (l2); \
+ const char *const*_a1; \
+ const char *const*_a2; \
+ const char *const*_a1x; \
+ const char *const*_a2x; \
+ char **_a1_free_deep = NULL; \
+ char **_a2_free_deep = NULL; \
+ gs_free const char **_a1_free_shallow = NULL; \
+ gs_free const char **_a2_free_shallow = NULL; \
+ int _c1, _c2; \
+ \
+ _strv_cmp_fuzz_input ((a1), _l1, &_a1_free_shallow, &_a1_free_deep, &_a1, &_a1x); \
+ _strv_cmp_fuzz_input ((a2), _l2, &_a2_free_shallow, &_a2_free_deep, &_a2, &_a2x); \
+ \
+ _c1 = _nm_utils_strv_cmp_n (_a1, _l1, _a2, _l2); \
+ _c2 = _nm_utils_strv_cmp_n (_a2, _l2, _a1, _l1); \
+ if (equal) { \
+ g_assert_cmpint (_c1, ==, 0); \
+ g_assert_cmpint (_c2, ==, 0); \
+ } else { \
+ g_assert_cmpint (_c1, ==, -1); \
+ g_assert_cmpint (_c2, ==, 1); \
+ } \
+ \
+ /* Compare with self. _strv_cmp_fuzz_input() randomly swapped the arguments (_a1 and _a1x).
+ * Either way, the arrays must compare equal to their semantically equal alternative. */ \
+ g_assert_cmpint (_nm_utils_strv_cmp_n (_a1, _l1, _a1x, _l1), ==, 0); \
+ g_assert_cmpint (_nm_utils_strv_cmp_n (_a2, _l2, _a2x, _l2), ==, 0); \
+ \
+ _strv_cmp_free_deep (_a1_free_deep, _l1); \
+ _strv_cmp_free_deep (_a2_free_deep, _l2); \
+ } G_STMT_END
+
+ _STRV_CMP (NULL, -1, NULL, -1, TRUE);
+
+ _STRV_CMP (NULL, -1, NULL, 0, FALSE);
+ _STRV_CMP (NULL, -1, strv0, 0, FALSE);
+ _STRV_CMP (NULL, -1, strv0, -1, FALSE);
+
+ _STRV_CMP (NULL, 0, NULL, 0, TRUE);
+ _STRV_CMP (NULL, 0, strv0, 0, TRUE);
+ _STRV_CMP (NULL, 0, strv0, -1, TRUE);
+ _STRV_CMP (strv0, 0, strv0, 0, TRUE);
+ _STRV_CMP (strv0, 0, strv0, -1, TRUE);
+ _STRV_CMP (strv0, -1, strv0, -1, TRUE);
+
+ _STRV_CMP (NULL, 0, strv1, -1, FALSE);
+ _STRV_CMP (NULL, 0, strv1, 1, FALSE);
+ _STRV_CMP (strv0, 0, strv1, -1, FALSE);
+ _STRV_CMP (strv0, 0, strv1, 1, FALSE);
+ _STRV_CMP (strv0, -1, strv1, -1, FALSE);
+ _STRV_CMP (strv0, -1, strv1, 1, FALSE);
+
+ _STRV_CMP (strv1, -1, strv1, 1, TRUE);
+ _STRV_CMP (strv1, 1, strv1, 1, TRUE);
+}
+
+/*****************************************************************************/
+
NMTST_DEFINE ();
int main (int argc, char **argv)
@@ -261,6 +405,7 @@ int main (int argc, char **argv)
g_test_add_func ("/general/test_nm_strndup_a", test_nm_strndup_a);
g_test_add_func ("/general/test_nm_ip4_addr_is_localhost", test_nm_ip4_addr_is_localhost);
g_test_add_func ("/general/test_unaligned", test_unaligned);
+ g_test_add_func ("/general/test_strv_cmp", test_strv_cmp);
return g_test_run ();
}