diff --git a/src/basic/env-util.c b/src/basic/env-util.c index 9e74ba0f0fc..2bd532caa12 100644 --- a/src/basic/env-util.c +++ b/src/basic/env-util.c @@ -941,11 +941,11 @@ int replace_env_argv( } if (ret_unset_variables) { - strv_uniq(strv_sort(unset_variables)); + strv_sort_uniq(unset_variables); *ret_unset_variables = TAKE_PTR(unset_variables); } if (ret_bad_variables) { - strv_uniq(strv_sort(bad_variables)); + strv_sort_uniq(bad_variables); *ret_bad_variables = TAKE_PTR(bad_variables); } diff --git a/src/basic/strv.c b/src/basic/strv.c index 4709dfaf246..1ca5118eff8 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -706,6 +706,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 c828bd612f5..965189d36e8 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/basic/time-util.c b/src/basic/time-util.c index b94f37c31c3..cd35e8a73c7 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -1519,8 +1519,7 @@ int get_timezones(char ***ret) { if (r < 0) return r; - strv_sort(zones); - strv_uniq(zones); + strv_sort_uniq(zones); *ret = TAKE_PTR(zones); return 0; diff --git a/src/boot/measure.c b/src/boot/measure.c index dca70d74a24..01e1fc7318a 100644 --- a/src/boot/measure.c +++ b/src/boot/measure.c @@ -329,8 +329,7 @@ static int parse_argv(int argc, char *argv[]) { return log_oom(); } - strv_sort(arg_banks); - strv_uniq(arg_banks); + strv_sort_uniq(arg_banks); if (arg_current) for (UnifiedSection us = 0; us < _UNIFIED_SECTION_MAX; us++) @@ -347,10 +346,8 @@ static int parse_argv(int argc, char *argv[]) { "enter-initrd:leave-initrd:sysinit", "enter-initrd:leave-initrd:sysinit:ready") < 0) return log_oom(); - } else { - strv_sort(arg_phase); - strv_uniq(arg_phase); - } + } else + strv_sort_uniq(arg_phase); _cleanup_free_ char *j = NULL; j = strv_join(arg_phase, ", "); diff --git a/src/home/homectl.c b/src/home/homectl.c index 535422537e9..f2a04d5a0ce 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -3836,8 +3836,7 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return log_oom(); - strv_sort(list); - strv_uniq(list); + strv_sort_uniq(list); mo = sd_json_variant_unref(mo); r = sd_json_variant_new_array_strv(&mo, list); diff --git a/src/locale/localectl.c b/src/locale/localectl.c index 32354027f17..9a2163bcfef 100644 --- a/src/locale/localectl.c +++ b/src/locale/localectl.c @@ -374,8 +374,7 @@ static int list_x11_keymaps(int argc, char **argv, void *userdata) { return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Couldn't find any entries."); - strv_sort(list); - strv_uniq(list); + strv_sort_uniq(list); pager_open(arg_pager_flags); diff --git a/src/shared/journal-util.c b/src/shared/journal-util.c index ab70f4da0d8..11d5d68478a 100644 --- a/src/shared/journal-util.c +++ b/src/shared/journal-util.c @@ -58,8 +58,7 @@ static int access_check_var_log_journal(sd_journal *j, bool want_other_users) { if (r < 0) return log_oom(); - strv_sort(g); - strv_uniq(g); + strv_sort_uniq(g); s = strv_join(g, "', '"); if (!s) diff --git a/src/shared/userdb.c b/src/shared/userdb.c index caf05302d9c..dca79cc735e 100644 --- a/src/shared/userdb.c +++ b/src/shared/userdb.c @@ -1436,8 +1436,7 @@ int membershipdb_by_group_strv(const char *name, UserDBFlags flags, char ***ret) return r; } - strv_sort(members); - strv_uniq(members); + strv_sort_uniq(members); *ret = TAKE_PTR(members); return 0; diff --git a/src/ssh-generator/ssh-generator.c b/src/ssh-generator/ssh-generator.c index c671b417033..842f0b3a940 100644 --- a/src/ssh-generator/ssh-generator.c +++ b/src/ssh-generator/ssh-generator.c @@ -453,8 +453,7 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) (void) parse_credentials(); - strv_sort(arg_listen_extra); - strv_uniq(arg_listen_extra); + strv_sort_uniq(arg_listen_extra); if (!arg_auto && strv_isempty(arg_listen_extra)) { log_debug("Disabling SSH generator logic, because as it has been turned off explicitly."); diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 52f4a47aa1f..9471c1843e1 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -362,8 +362,7 @@ static int putgrent_with_members( if (added) { struct group t; - strv_uniq(l); - strv_sort(l); + strv_sort_uniq(l); t = *gr; t.gr_mem = l; @@ -411,8 +410,7 @@ static int putsgent_with_members( if (added) { struct sgrp t; - strv_uniq(l); - strv_sort(l); + strv_sort_uniq(l); t = *sg; t.sg_mem = l; diff --git a/src/test/test-strv.c b/src/test/test-strv.c index 65afefed3be..f7b6ee2bb2d 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; diff --git a/src/varlinkctl/varlinkctl.c b/src/varlinkctl/varlinkctl.c index 8752ee575cc..fe5080dd439 100644 --- a/src/varlinkctl/varlinkctl.c +++ b/src/varlinkctl/varlinkctl.c @@ -336,7 +336,7 @@ static int verb_introspect(int argc, char *argv[], void *userdata) { if (strv_isempty(auto_interfaces)) return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "Service doesn't report any implemented interfaces."); - interfaces = strv_sort(strv_uniq(auto_interfaces)); + interfaces = strv_sort_uniq(auto_interfaces); } /* Automatically switch on JSON_SEQ if we output multiple JSON objects */ @@ -405,7 +405,7 @@ static int verb_introspect(int argc, char *argv[], void *userdata) { if (list_methods) { pager_open(arg_pager_flags); - strv_sort(strv_uniq(methods)); + strv_sort_uniq(methods); if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) strv_print(methods);