From 82c730d18059ae5f1805bb66af847fceb7e75987 Mon Sep 17 00:00:00 2001 From: Frank Bergkemper Date: Sun, 15 Dec 2019 18:53:19 +0100 Subject: [PATCH] dird: Sort resource names in prompt lists --- core/src/dird/ua_select.cc | 93 +++++++++++++++++++++++++---- core/src/dird/ua_select.h | 1 + core/src/lib/util.cc | 17 ++++++ core/src/lib/util.h | 1 + core/src/tests/CMakeLists.txt | 8 +++ core/src/tests/sort_stringvector.cc | 42 +++++++++++++ 6 files changed, 152 insertions(+), 10 deletions(-) create mode 100644 core/src/tests/sort_stringvector.cc diff --git a/core/src/dird/ua_select.cc b/core/src/dird/ua_select.cc index 401708674c5..68d85b5e140 100644 --- a/core/src/dird/ua_select.cc +++ b/core/src/dird/ua_select.cc @@ -35,9 +35,13 @@ #include "dird/storage.h" #include "dird/ua_input.h" #include "dird/ua_select.h" -#include "dird/ua_select.h" #include "lib/edit.h" #include "lib/parse_conf.h" +#include "lib/util.h" + +#include +#include +#include namespace directordaemon { @@ -152,6 +156,7 @@ StorageResource* select_storage_resource(UaContext* ua, bool autochanger_only) { StorageResource* store; char name[MAX_NAME_LENGTH]; + std::vector storage_resource_names; if (autochanger_only) { StartPrompt(ua, _("The defined Autochanger Storage resources are:\n")); @@ -165,12 +170,18 @@ StorageResource* select_storage_resource(UaContext* ua, bool autochanger_only) if (autochanger_only && !store->autochanger) { continue; } else { - AddPrompt(ua, store->resource_name_); + storage_resource_names.emplace_back(store->resource_name_); } } } UnlockRes(my_config); + SortCaseInsensitive(storage_resource_names); + + for (auto& resource_name : storage_resource_names) { + AddPrompt(ua, std::move(resource_name)); + } + if (DoPrompt(ua, _("Storage"), _("Select Storage resource"), name, sizeof(name)) < 0) { return NULL; @@ -187,17 +198,24 @@ FilesetResource* select_fileset_resource(UaContext* ua) { FilesetResource* fs; char name[MAX_NAME_LENGTH]; + std::vector fileset_resource_names; StartPrompt(ua, _("The defined FileSet resources are:\n")); LockRes(my_config); foreach_res (fs, R_FILESET) { if (ua->AclAccessOk(FileSet_ACL, fs->resource_name_)) { - AddPrompt(ua, fs->resource_name_); + fileset_resource_names.emplace_back(fs->resource_name_); } } UnlockRes(my_config); + SortCaseInsensitive(fileset_resource_names); + + for (auto& resource_name : fileset_resource_names) { + AddPrompt(ua, std::move(resource_name)); + } + if (DoPrompt(ua, _("FileSet"), _("Select FileSet resource"), name, sizeof(name)) < 0) { return NULL; @@ -270,6 +288,7 @@ JobResource* select_enable_disable_job_resource(UaContext* ua, bool enable) { JobResource* job; char name[MAX_NAME_LENGTH]; + std::vector job_resource_names; StartPrompt(ua, _("The defined Job resources are:\n")); @@ -279,10 +298,16 @@ JobResource* select_enable_disable_job_resource(UaContext* ua, bool enable) if (job->enabled == enable) { /* Already enabled/disabled? */ continue; /* yes, skip */ } - AddPrompt(ua, job->resource_name_); + job_resource_names.emplace_back(job->resource_name_); } UnlockRes(my_config); + SortCaseInsensitive(job_resource_names); + + for (auto& resource_name : job_resource_names) { + AddPrompt(ua, std::move(resource_name)); + } + if (DoPrompt(ua, _("Job"), _("Select Job resource"), name, sizeof(name)) < 0) { return NULL; @@ -300,17 +325,24 @@ JobResource* select_job_resource(UaContext* ua) { JobResource* job; char name[MAX_NAME_LENGTH]; + std::vector job_resource_names; StartPrompt(ua, _("The defined Job resources are:\n")); LockRes(my_config); foreach_res (job, R_JOB) { if (ua->AclAccessOk(Job_ACL, job->resource_name_)) { - AddPrompt(ua, job->resource_name_); + job_resource_names.emplace_back(job->resource_name_); } } UnlockRes(my_config); + SortCaseInsensitive(job_resource_names); + + for (auto& resource_name : job_resource_names) { + AddPrompt(ua, std::move(resource_name)); + } + if (DoPrompt(ua, _("Job"), _("Select Job resource"), name, sizeof(name)) < 0) { return NULL; @@ -347,6 +379,7 @@ JobResource* select_restore_job_resource(UaContext* ua) { JobResource* job; char name[MAX_NAME_LENGTH]; + std::vector restore_job_names; StartPrompt(ua, _("The defined Restore Job resources are:\n")); @@ -354,11 +387,17 @@ JobResource* select_restore_job_resource(UaContext* ua) foreach_res (job, R_JOB) { if (job->JobType == JT_RESTORE && ua->AclAccessOk(Job_ACL, job->resource_name_)) { - AddPrompt(ua, job->resource_name_); + restore_job_names.emplace_back(job->resource_name_); } } UnlockRes(my_config); + SortCaseInsensitive(restore_job_names); + + for (auto& resource_name : restore_job_names) { + AddPrompt(ua, std::move(resource_name)); + } + if (DoPrompt(ua, _("Job"), _("Select Restore Job"), name, sizeof(name)) < 0) { return NULL; } @@ -375,17 +414,24 @@ ClientResource* select_client_resource(UaContext* ua) { ClientResource* client; char name[MAX_NAME_LENGTH]; + std::vector client_resource_names; StartPrompt(ua, _("The defined Client resources are:\n")); LockRes(my_config); foreach_res (client, R_CLIENT) { if (ua->AclAccessOk(Client_ACL, client->resource_name_)) { - AddPrompt(ua, client->resource_name_); + client_resource_names.emplace_back(client->resource_name_); } } UnlockRes(my_config); + SortCaseInsensitive(client_resource_names); + + for (auto& resource_name : client_resource_names) { + AddPrompt(ua, std::move(resource_name)); + } + if (DoPrompt(ua, _("Client"), _("Select Client (File daemon) resource"), name, sizeof(name)) < 0) { return NULL; @@ -404,6 +450,7 @@ ClientResource* select_enable_disable_client_resource(UaContext* ua, { ClientResource* client; char name[MAX_NAME_LENGTH]; + std::vector client_resource_names; StartPrompt(ua, _("The defined Client resources are:\n")); @@ -413,10 +460,16 @@ ClientResource* select_enable_disable_client_resource(UaContext* ua, if (client->enabled == enable) { /* Already enabled/disabled? */ continue; /* yes, skip */ } - AddPrompt(ua, client->resource_name_); + client_resource_names.emplace_back(client->resource_name_); } UnlockRes(my_config); + SortCaseInsensitive(client_resource_names); + + for (auto& resource_name : client_resource_names) { + AddPrompt(ua, std::move(resource_name)); + } + if (DoPrompt(ua, _("Client"), _("Select Client resource"), name, sizeof(name)) < 0) { return NULL; @@ -461,6 +514,7 @@ ScheduleResource* select_enable_disable_schedule_resource(UaContext* ua, { ScheduleResource* sched; char name[MAX_NAME_LENGTH]; + std::vector schedule_resource_names; StartPrompt(ua, _("The defined Schedule resources are:\n")); @@ -470,10 +524,16 @@ ScheduleResource* select_enable_disable_schedule_resource(UaContext* ua, if (sched->enabled == enable) { /* Already enabled/disabled? */ continue; /* yes, skip */ } - AddPrompt(ua, sched->resource_name_); + schedule_resource_names.emplace_back(sched->resource_name_); } UnlockRes(my_config); + SortCaseInsensitive(schedule_resource_names); + + for (auto& resource_name : schedule_resource_names) { + AddPrompt(ua, std::move(resource_name)); + } + if (DoPrompt(ua, _("Schedule"), _("Select Schedule resource"), name, sizeof(name)) < 0) { return NULL; @@ -883,16 +943,23 @@ PoolResource* select_pool_resource(UaContext* ua) { PoolResource* pool; char name[MAX_NAME_LENGTH]; + std::vector pool_resource_names; StartPrompt(ua, _("The defined Pool resources are:\n")); LockRes(my_config); foreach_res (pool, R_POOL) { if (ua->AclAccessOk(Pool_ACL, pool->resource_name_)) { - AddPrompt(ua, pool->resource_name_); + pool_resource_names.emplace_back(pool->resource_name_); } } UnlockRes(my_config); + SortCaseInsensitive(pool_resource_names); + + for (auto& resource_name : pool_resource_names) { + AddPrompt(ua, std::move(resource_name)); + } + if (DoPrompt(ua, _("Pool"), _("Select Pool resource"), name, sizeof(name)) < 0) { return NULL; @@ -1026,6 +1093,12 @@ void AddPrompt(UaContext* ua, const char* prompt) ua->prompt[ua->num_prompts++] = strdup(prompt); } +void AddPrompt(UaContext* ua, std::string&& prompt) +{ + std::string p{prompt}; + AddPrompt(ua, p.c_str()); +} + /** * Display prompts and get user's choice * diff --git a/core/src/dird/ua_select.h b/core/src/dird/ua_select.h index 1521d9a2c26..5a30f9391c7 100644 --- a/core/src/dird/ua_select.h +++ b/core/src/dird/ua_select.h @@ -48,6 +48,7 @@ bool SelectClientDbr(UaContext* ua, ClientDbRecord* cr); void StartPrompt(UaContext* ua, const char* msg); void AddPrompt(UaContext* ua, const char* prompt); +void AddPrompt(UaContext* ua, std::string&& prompt); int DoPrompt(UaContext* ua, const char* automsg, const char* msg, diff --git a/core/src/lib/util.cc b/core/src/lib/util.cc index 25bbfe5a586..c30b7473e24 100644 --- a/core/src/lib/util.cc +++ b/core/src/lib/util.cc @@ -1087,3 +1087,20 @@ const char* last_path_separator(const char* str) } return NULL; } + +void StringToLowerCase(std::string& s) +{ + for (auto& c : s) { c = std::tolower(c); } +} + +void SortCaseInsensitive(std::vector& v) +{ + if (v.empty()) { return; } + + std::sort(v.begin(), v.end(), [](const std::string& a, const std::string& b) { + std::string x{a}, y{b}; + StringToLowerCase(x); + StringToLowerCase(y); + return x < y; + }); +} diff --git a/core/src/lib/util.h b/core/src/lib/util.h index 1950750d362..5c4ec2e29c9 100644 --- a/core/src/lib/util.h +++ b/core/src/lib/util.h @@ -66,5 +66,6 @@ POOLMEM* edit_job_codes(JobControlRecord* jcr, job_code_callback_t job_code_callback = NULL); void SetWorkingDirectory(const char* wd); const char* last_path_separator(const char* str); +void SortCaseInsensitive(std::vector& v); #endif // BAREOS_LIB_UTIL_H_ diff --git a/core/src/tests/CMakeLists.txt b/core/src/tests/CMakeLists.txt index e052ee6e2a8..ccaa737c446 100644 --- a/core/src/tests/CMakeLists.txt +++ b/core/src/tests/CMakeLists.txt @@ -385,3 +385,11 @@ if(NOT HAVE_WIN32 AND NOT client-only) ${GTEST_MAIN_LIBRARIES} ) endif() # NOT HAVE_WIN32 AND NOT client-only + + +if(NOT client-only) + bareos_add_test( + sort_stringvector LINK_LIBRARIES bareos ${GTEST_LIBRARIES} + ${GTEST_MAIN_LIBRARIES} +) +endif() # NOT client-only diff --git a/core/src/tests/sort_stringvector.cc b/core/src/tests/sort_stringvector.cc new file mode 100644 index 00000000000..a00e84a0d9c --- /dev/null +++ b/core/src/tests/sort_stringvector.cc @@ -0,0 +1,42 @@ +/* + BAREOSĀ® - Backup Archiving REcovery Open Sourced + + Copyright (C) 2019-2019 Bareos GmbH & Co. KG + + This program is Free Software; you can redistribute it and/or + modify it under the terms of version three of the GNU Affero General Public + License as published by the Free Software Foundation, which is + listed in the file LICENSE. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ +#if defined(HAVE_MINGW) +#include "include/bareos.h" +#include "gtest/gtest.h" +#else +#include "gtest/gtest.h" +#include "include/bareos.h" +#endif + +#include "lib/util.h" + +TEST(sort_stringvector, sort_ascending) +{ + std::vector v{"Zfs", "EXT2", "ext3", "ext4", "xfs", "AFS"}; + SortCaseInsensitive(v); + + EXPECT_STREQ(v[0].c_str(), "AFS"); + EXPECT_STREQ(v[1].c_str(), "EXT2"); + EXPECT_STREQ(v[2].c_str(), "ext3"); + EXPECT_STREQ(v[3].c_str(), "ext4"); + EXPECT_STREQ(v[4].c_str(), "xfs"); + EXPECT_STREQ(v[5].c_str(), "Zfs"); +}