diff --git a/src/basic/strv.c b/src/basic/strv.c index d081821a862..a4cbac5ea7a 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -704,6 +704,21 @@ char** strv_sort(char **l) { return l; } +char** strv_sort_uniq(char **l) { + if (strv_isempty(l)) + return l; + + char **tail = strv_sort(l), *prev = NULL; + STRV_FOREACH(i, l) + if (streq_ptr(*i, prev)) + free(*i); + else + *(tail++) = prev = *i; + + *tail = NULL; + return l; +} + int strv_compare(char * const *a, char * const *b) { int r; diff --git a/src/basic/strv.h b/src/basic/strv.h index 169737d1d8c..4ea9b1a0107 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -161,6 +161,7 @@ bool strv_overlap(char * const *a, char * const *b) _pure_; _STRV_FOREACH_PAIR(x, y, l, UNIQ_T(i, UNIQ)) char** strv_sort(char **l); +char** strv_sort_uniq(char **l); void strv_print_full(char * const *l, const char *prefix); static inline void strv_print(char * const *l) { strv_print_full(l, NULL); diff --git a/src/test/test-strv.c b/src/test/test-strv.c index 28b8b2270c0..2cd2029c403 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -527,6 +527,81 @@ TEST(strv_sort) { ASSERT_STREQ(input_table[4], "durian"); } +TEST(strv_sort_uniq) { + static const char* input_table[] = { + "durian", + "apple", + "citrus", + "CAPITAL LETTERS FIRST", + "banana", + "durian", + "apple", + "citrus", + "CAPITAL LETTERS FIRST", + "banana", + "durian", + "apple", + "citrus", + "CAPITAL LETTERS FIRST", + "banana", + NULL + }; + + _cleanup_strv_free_ char **a = NULL, **b = NULL, **c = NULL; + + ASSERT_NULL(strv_sort_uniq(a)); + + ASSERT_NOT_NULL(a = strv_new(NULL)); + assert_se(strv_sort_uniq(a) == a); + ASSERT_NULL(a[0]); + a = strv_free(a); + + ASSERT_NOT_NULL(a = strv_new("a", "a", "a", "a", "a")); + assert_se(strv_sort_uniq(a) == a); + ASSERT_STREQ(a[0], "a"); + ASSERT_NULL(a[1]); + a = strv_free(a); + + ASSERT_NOT_NULL(a = strv_new("a", "a", "a", "a", "b")); + assert_se(strv_sort_uniq(a) == a); + ASSERT_STREQ(a[0], "a"); + ASSERT_STREQ(a[1], "b"); + ASSERT_NULL(a[2]); + a = strv_free(a); + + ASSERT_NOT_NULL(a = strv_new("b", "a", "a", "a", "a")); + assert_se(strv_sort_uniq(a) == a); + ASSERT_STREQ(a[0], "a"); + ASSERT_STREQ(a[1], "b"); + ASSERT_NULL(a[2]); + a = strv_free(a); + + ASSERT_NOT_NULL(a = strv_new("a", "a", "b", "a", "b")); + assert_se(strv_sort_uniq(a) == a); + ASSERT_STREQ(a[0], "a"); + ASSERT_STREQ(a[1], "b"); + ASSERT_NULL(a[2]); + a = strv_free(a); + + ASSERT_NOT_NULL(a = strv_copy((char**) input_table)); + ASSERT_NOT_NULL(b = strv_copy((char**) input_table)); + ASSERT_NOT_NULL(c = strv_copy((char**) input_table)); + + assert_se(strv_sort_uniq(a) == a); + assert_se(strv_sort(strv_uniq(b)) == b); + assert_se(strv_uniq(strv_sort(c)) == c); + + assert_se(strv_equal(a, b)); + assert_se(strv_equal(a, c)); + + ASSERT_STREQ(a[0], "CAPITAL LETTERS FIRST"); + ASSERT_STREQ(a[1], "apple"); + ASSERT_STREQ(a[2], "banana"); + ASSERT_STREQ(a[3], "citrus"); + ASSERT_STREQ(a[4], "durian"); + ASSERT_NULL(a[5]); +} + TEST(strv_extend_strv_biconcat) { _cleanup_strv_free_ char **a = NULL, **b = NULL;