From e49747ab51953a3c2f81767b320f1f415eba9150 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 16 Aug 2022 19:28:57 -0500 Subject: [PATCH] feat: add tr_strvToBuf() (#3657) * feat: add tr_getDefaultConfigDirToBuf() * feat: add tr_getDefaultDownloadDirToBuf() * feat: add tr_strvToBuf() * feat: add tr_torrentFindFileToBuf() * feat: add tr_torrentGetMagnetLinkToBuf() * refactor: remove unused makeString() from tests * feat: add tr_torrentFilenameToBuf() * feat: add tr_torrentGetTrackerListToBuf() * chore: remove obsolete comment references to tr_free * chore: remove unused tr_strdup() * chore: remove unused forward declarations --- cli/cli.cc | 20 ++--- daemon/daemon.cc | 29 +++---- gtk/Application.cc | 5 +- gtk/DetailsDialog.cc | 17 +--- gtk/Prefs.cc | 4 +- gtk/main.cc | 4 +- libtransmission/platform.cc | 40 +++++---- libtransmission/session.cc | 7 +- libtransmission/torrent-magnet.cc | 9 +- libtransmission/torrent-metainfo.cc | 102 ----------------------- libtransmission/torrent.cc | 30 +++++-- libtransmission/transmission.h | 68 ++++++++++----- libtransmission/utils.cc | 17 +++- libtransmission/utils.h | 19 +++-- macosx/Controller.mm | 7 +- macosx/Torrent.mm | 36 ++------ qt/Application.cc | 4 +- qt/Prefs.cc | 8 +- tests/libtransmission/move-test.cc | 8 +- tests/libtransmission/platform-test.cc | 21 +++-- tests/libtransmission/rename-test.cc | 22 ++--- tests/libtransmission/subprocess-test.cc | 8 +- tests/libtransmission/test-fixtures.h | 7 -- utils/remote.cc | 6 +- 24 files changed, 193 insertions(+), 305 deletions(-) diff --git a/cli/cli.cc b/cli/cli.cc index 8196413b7..bee918f0b 100644 --- a/cli/cli.cc +++ b/cli/cli.cc @@ -173,10 +173,9 @@ static std::string getStatusStr(tr_stat const* st) return ""; } -static char const* getConfigDir(int argc, char const** argv) +static std::string getConfigDir(int argc, char const** argv) { int c; - char const* configDir = nullptr; char const* my_optarg; int const ind = tr_optind; @@ -184,19 +183,14 @@ static char const* getConfigDir(int argc, char const** argv) { if (c == 'g') { - configDir = my_optarg; + return my_optarg; break; } } tr_optind = ind; - if (configDir == nullptr) - { - configDir = tr_getDefaultConfigDir(MyConfigName); - } - - return configDir; + return tr_getDefaultConfigDir(MyConfigName); } int tr_main(int argc, char* argv[]) @@ -218,8 +212,8 @@ int tr_main(int argc, char* argv[]) /* load the defaults from config file + libtransmission defaults */ tr_variantInitDict(&settings, 0); - char const* const configDir = getConfigDir(argc, (char const**)argv); - tr_sessionLoadSettings(&settings, configDir, MyConfigName); + auto const config_dir = getConfigDir(argc, (char const**)argv); + tr_sessionLoadSettings(&settings, config_dir.c_str(), MyConfigName); /* the command line overrides defaults */ if (parseCommandLine(&settings, argc, (char const**)argv) != 0) @@ -256,7 +250,7 @@ int tr_main(int argc, char* argv[]) } } - auto* const h = tr_sessionInit(configDir, false, &settings); + auto* const h = tr_sessionInit(config_dir.c_str(), false, &settings); auto* const ctor = tr_ctorNew(h); tr_ctorSetPaused(ctor, TR_FORCE, false); @@ -353,7 +347,7 @@ int tr_main(int argc, char* argv[]) } } - tr_sessionSaveSettings(h, configDir, &settings); + tr_sessionSaveSettings(h, config_dir.c_str(), &settings); printf("\n"); tr_variantFree(&settings); diff --git a/daemon/daemon.cc b/daemon/daemon.cc index bbe3abeed..b0256eed9 100644 --- a/daemon/daemon.cc +++ b/daemon/daemon.cc @@ -202,10 +202,9 @@ static bool reopen_log_file(char const* filename) return true; } -static char const* getConfigDir(int argc, char const* const* argv) +static std::string getConfigDir(int argc, char const* const* argv) { int c; - char const* configDir = nullptr; char const* optstr; int const ind = tr_optind; @@ -213,19 +212,13 @@ static char const* getConfigDir(int argc, char const* const* argv) { if (c == 'g') { - configDir = optstr; - break; + return optstr; } } tr_optind = ind; - if (configDir == nullptr) - { - configDir = tr_getDefaultConfigDir(MyName); - } - - return configDir; + return tr_getDefaultConfigDir(MyName); } static auto onFileAdded(tr_session* session, std::string_view dirname, std::string_view basename) @@ -671,7 +664,7 @@ static bool parse_args( struct daemon_data { tr_variant settings; - char const* configDir; + std::string config_dir; bool paused; }; @@ -719,7 +712,7 @@ static int daemon_start(void* varg, [[maybe_unused]] bool foreground) auto* arg = static_cast(varg); tr_variant* const settings = &arg->settings; - char const* const configDir = arg->configDir; + char const* const config_dir = arg->config_dir.c_str(); sd_notifyf(0, "MAINPID=%d\n", (int)getpid()); @@ -744,10 +737,10 @@ static int daemon_start(void* varg, [[maybe_unused]] bool foreground) tr_formatter_mem_init(MemK, MemKStr, MemMStr, MemGStr, MemTStr); tr_formatter_size_init(DiskK, DiskKStr, DiskMStr, DiskGStr, DiskTStr); tr_formatter_speed_init(SpeedK, SpeedKStr, SpeedMStr, SpeedGStr, SpeedTStr); - session = tr_sessionInit(configDir, true, settings); + session = tr_sessionInit(config_dir, true, settings); tr_sessionSetRPCCallback(session, on_rpc_callback, nullptr); - tr_logAddInfo(fmt::format(_("Loading settings from '{path}'"), fmt::arg("path", configDir))); - tr_sessionSaveSettings(session, configDir, settings); + tr_logAddInfo(fmt::format(_("Loading settings from '{path}'"), fmt::arg("path", config_dir))); + tr_sessionSaveSettings(session, config_dir, settings); auto sv = std::string_view{}; (void)tr_variantDictFindStrView(settings, key_pidfile, &sv); @@ -893,7 +886,7 @@ CLEANUP: event_base_free(ev_base); - tr_sessionSaveSettings(mySession, configDir, settings); + tr_sessionSaveSettings(mySession, config_dir, settings); tr_sessionClose(mySession); pumpLogMessages(logfile); printf(" done.\n"); @@ -922,12 +915,12 @@ CLEANUP: static bool init_daemon_data(int argc, char* argv[], struct daemon_data* data, bool* foreground, int* ret) { - data->configDir = getConfigDir(argc, (char const* const*)argv); + data->config_dir = getConfigDir(argc, (char const* const*)argv); /* load settings from defaults + config file */ tr_variantInitDict(&data->settings, 0); tr_variantDictAddBool(&data->settings, TR_KEY_rpc_enabled, true); - bool const loaded = tr_sessionLoadSettings(&data->settings, data->configDir, MyName); + bool const loaded = tr_sessionLoadSettings(&data->settings, data->config_dir.c_str(), MyName); bool dumpSettings; diff --git a/gtk/Application.cc b/gtk/Application.cc index d4d76b080..8b7cd2019 100644 --- a/gtk/Application.cc +++ b/gtk/Application.cc @@ -1382,7 +1382,7 @@ tr_torrent* Application::Impl::get_first_selected_torrent() const void Application::Impl::copy_magnet_link_to_clipboard(tr_torrent* tor) const { - char* magnet = tr_torrentGetMagnetLink(tor); + auto const magnet = tr_torrentGetMagnetLink(tor); auto const display = wind_->get_display(); GdkAtom selection; Glib::RefPtr clipboard; @@ -1396,9 +1396,6 @@ void Application::Impl::copy_magnet_link_to_clipboard(tr_torrent* tor) const selection = GDK_SELECTION_PRIMARY; clipboard = Gtk::Clipboard::get_for_display(display, selection); clipboard->set_text(magnet); - - /* cleanup */ - tr_free(magnet); } void gtr_actions_handler(Glib::ustring const& action_name, gpointer user_data) diff --git a/gtk/DetailsDialog.cc b/gtk/DetailsDialog.cc index 4fe6f7275..9cc71f6f7 100644 --- a/gtk/DetailsDialog.cc +++ b/gtk/DetailsDialog.cc @@ -22,7 +22,7 @@ #include #include -#include /* tr_free */ +#include #include #include "Actions.h" @@ -2299,19 +2299,6 @@ void DetailsDialog::Impl::on_edit_trackers_response(int response, std::shared_pt } } -namespace -{ - -std::string get_editable_tracker_list(tr_torrent const* tor) -{ - char* cstr = tr_torrentGetTrackerList(tor); - auto str = std::string{ cstr != nullptr ? cstr : "" }; - tr_free(cstr); - return str; -} - -} // namespace - void DetailsDialog::Impl::on_edit_trackers() { tr_torrent const* tor = tracker_list_get_current_torrent(); @@ -2343,7 +2330,7 @@ void DetailsDialog::Impl::on_edit_trackers() t->add_wide_control(row, *l); auto* w = Gtk::make_managed(); - w->get_buffer()->set_text(get_editable_tracker_list(tor)); + w->get_buffer()->set_text(tr_torrentGetTrackerList(tor)); auto* fr = Gtk::make_managed(); fr->set_shadow_type(Gtk::SHADOW_IN); auto* sw = Gtk::make_managed(); diff --git a/gtk/Prefs.cc b/gtk/Prefs.cc index dffd48c14..d00aa2c53 100644 --- a/gtk/Prefs.cc +++ b/gtk/Prefs.cc @@ -49,9 +49,7 @@ static void tr_prefs_init_defaults(tr_variant* d) if (dir.empty()) { - auto* const tmp = tr_getDefaultDownloadDir(); - dir = tmp; - tr_free(tmp); + dir = tr_getDefaultDownloadDir(); } tr_variantDictReserve(d, 31); diff --git a/gtk/main.cc b/gtk/main.cc index 12799456a..3e3566ca9 100644 --- a/gtk/main.cc +++ b/gtk/main.cc @@ -96,9 +96,7 @@ int main(int argc, char** argv) /* set up the config dir */ if (std::empty(config_dir)) { - auto* const default_config_dir = tr_getDefaultConfigDir(AppConfigDirName); - config_dir = default_config_dir; - tr_free(default_config_dir); + config_dir = tr_getDefaultConfigDir(AppConfigDirName); } gtr_pref_init(config_dir); diff --git a/libtransmission/platform.cc b/libtransmission/platform.cc index 7e64a3af4..a03a5cf31 100644 --- a/libtransmission/platform.cc +++ b/libtransmission/platform.cc @@ -110,40 +110,45 @@ static std::string xdgConfigHome() return fmt::format("{:s}/.config"sv, getHomeDir()); } -char* tr_getDefaultConfigDir(char const* appname) +std::string tr_getDefaultConfigDir(std::string_view appname) { - if (auto dir = tr_env_get_string("TRANSMISSION_HOME"sv); !std::empty(dir)) + if (std::empty(appname)) { - return tr_strvDup(dir); + appname = "Transmission"sv; } - if (tr_str_is_empty(appname)) + if (auto dir = tr_env_get_string("TRANSMISSION_HOME"sv); !std::empty(dir)) { - appname = "Transmission"; + return dir; } #ifdef __APPLE__ - return tr_strvDup(fmt::format("{:s}/Library/Application Support/{:s}"sv, getHomeDir(), appname)); + return fmt::format("{:s}/Library/Application Support/{:s}"sv, getHomeDir(), appname); #elif defined(_WIN32) auto const appdata = win32_get_known_folder(FOLDERID_LocalAppData); - return tr_strvDup(fmt::format("{:s}/{:s}"sv, appdata, appname)); + return fmt::format("{:s}/{:s}"sv, appdata, appname); #elif defined(__HAIKU__) char buf[PATH_MAX]; find_directory(B_USER_SETTINGS_DIRECTORY, -1, true, buf, sizeof(buf)); - return tr_strvDup(fmt::format("{:s}/{:s}"sv, buf, appname); + return fmt::format("{:s}/{:s}"sv, buf, appname); #else - return tr_strvDup(fmt::format("{:s}/{:s}"sv, xdgConfigHome(), appname)); + return fmt::format("{:s}/{:s}"sv, xdgConfigHome(), appname); #endif } +size_t tr_getDefaultConfigDirToBuf(char const* appname, char* buf, size_t buflen) +{ + return tr_strvToBuf(tr_getDefaultConfigDir(appname != nullptr ? appname : ""), buf, buflen); +} + static std::string getXdgEntryFromUserDirs(std::string_view key) { auto content = std::vector{}; @@ -178,25 +183,30 @@ static std::string getXdgEntryFromUserDirs(std::string_view key) return val; } -char* tr_getDefaultDownloadDir() +std::string tr_getDefaultDownloadDir() { - if (auto const dir = getXdgEntryFromUserDirs("XDG_DOWNLOAD_DIR"sv); !std::empty(dir)) + if (auto dir = getXdgEntryFromUserDirs("XDG_DOWNLOAD_DIR"sv); !std::empty(dir)) { - return tr_strvDup(dir); + return dir; } #ifdef _WIN32 if (auto dir = win32_get_known_folder(FOLDERID_Downloads); !std::empty(dir)) { - return tr_strvDup(dir); + return dir; } #endif #ifdef __HAIKU__ - return tr_strvDup(fmt::format("{:s}/Desktop"sv, getHomeDir())); + return fmt::format("{:s}/Desktop"sv, getHomeDir()); #endif - return tr_strvDup(fmt::format("{:s}/Downloads"sv, getHomeDir())); + return fmt::format("{:s}/Downloads"sv, getHomeDir()); +} + +size_t tr_getDefaultDownloadDirToBuf(char* buf, size_t buflen) +{ + return tr_strvToBuf(tr_getDefaultDownloadDir(), buf, buflen); } /*** diff --git a/libtransmission/session.cc b/libtransmission/session.cc index 5e7a1431b..f65032ca8 100644 --- a/libtransmission/session.cc +++ b/libtransmission/session.cc @@ -296,7 +296,7 @@ tr_session::PublicAddressResult tr_session::publicAddress(tr_address_type type) void tr_sessionGetDefaultSettings(tr_variant* setme_dictionary) { - auto* const download_dir = tr_getDefaultDownloadDir(); + auto const download_dir = tr_getDefaultDownloadDir(); auto* const d = setme_dictionary; TR_ASSERT(tr_variantIsDict(d)); @@ -376,8 +376,6 @@ void tr_sessionGetDefaultSettings(tr_variant* setme_dictionary) tr_variantDictAddBool(d, TR_KEY_anti_brute_force_enabled, true); tr_variantDictAddStrView(d, TR_KEY_announce_ip, ""); tr_variantDictAddBool(d, TR_KEY_announce_ip_enabled, false); - - tr_free(download_dir); } void tr_sessionGetSettings(tr_session const* s, tr_variant* setme_dictionary) @@ -469,9 +467,8 @@ static void getSettingsFilename(tr_pathbuf& setme, char const* config_dir, char return; } - auto* const default_config_dir = tr_getDefaultConfigDir(appname); + auto const default_config_dir = tr_getDefaultConfigDir(appname); setme.assign(std::string_view{ default_config_dir }, "/settings.json"sv); - tr_free(default_config_dir); } bool tr_sessionLoadSettings(tr_variant* dict, char const* config_dir, char const* appName) diff --git a/libtransmission/torrent-magnet.cc b/libtransmission/torrent-magnet.cc index 2bb491dab..b674ce3fd 100644 --- a/libtransmission/torrent-magnet.cc +++ b/libtransmission/torrent-magnet.cc @@ -414,7 +414,12 @@ double tr_torrentGetMetadataPercent(tr_torrent const* tor) return m == nullptr || m->piece_count == 0 ? 0.0 : (m->piece_count - std::size(m->pieces_needed)) / (double)m->piece_count; } -char* tr_torrentGetMagnetLink(tr_torrent const* tor) +std::string tr_torrentGetMagnetLink(tr_torrent const* tor) { - return tr_strvDup(tor->metainfo_.magnet()); + return std::string{ tor->metainfo_.magnet().sv() }; +} + +size_t tr_torrentGetMagnetLinkToBuf(tr_torrent const* tor, char* buf, size_t buflen) +{ + return tr_strvToBuf(tr_torrentGetMagnetLink(tor), buf, buflen); } diff --git a/libtransmission/torrent-metainfo.cc b/libtransmission/torrent-metainfo.cc index f500c9b5d..0473cd147 100644 --- a/libtransmission/torrent-metainfo.cc +++ b/libtransmission/torrent-metainfo.cc @@ -30,108 +30,6 @@ using namespace std::literals; -//// C BINDINGS - -#if 0 -/// Lifecycle - -tr_torrent_metainfo* tr_torrentMetainfoNewFromData(char const* data, size_t data_len, struct tr_error** error) -{ - auto* tm = new tr_torrent_metainfo{}; - if (!tm->parseBenc(std::string_view{ data, data_len }, error)) - { - delete tm; - return nullptr; - } - - return tm; -} - -tr_torrent_metainfo* tr_torrentMetainfoNewFromFile(char const* filename, struct tr_error** error) -{ - auto* tm = new tr_torrent_metainfo{}; - if (!tm->parseBencFromFile(filename ? filename : "", nullptr, error)) - { - delete tm; - return nullptr; - } - - return tm; -} - -void tr_torrentMetainfoFree(tr_torrent_metainfo* tm) -{ - delete tm; -} - -//// Accessors - -char* tr_torrentMetainfoMagnet(struct tr_torrent_metainfo const* tm) -{ - return tr_strvDup(tm->magnet()); -} - -/// Info - -tr_torrent_metainfo_info* tr_torrentMetainfoGet(tr_torrent_metainfo const* tm, tr_torrent_metainfo_info* setme) -{ - setme->comment = tm->comment.c_str(); - setme->creator = tm->creator.c_str(); - setme->info_hash = tm->info_hash; - setme->info_hash_string = std::data(tm->info_hash_chars); - setme->is_private = tm->is_private; - setme->n_pieces = tm->n_pieces; - setme->name = tm->name.c_str(); - setme->source = tm->source.c_str(); - setme->time_created = tm->time_created; - setme->total_size = tm->total_size; - return setme; -} - -/// Files - -size_t tr_torrentMetainfoFileCount(tr_torrent_metainfo const* tm) -{ - return std::size(tm->files); -} - -tr_torrent_metainfo_file_info* tr_torrentMetainfoFile( - tr_torrent_metainfo const* tm, - size_t n, - tr_torrent_metainfo_file_info* setme) -{ - auto& file = tm->files[n]; - setme->path = file.path.c_str(); - setme->size = file.size; - return setme; -} - -/// Trackers - -size_t tr_torrentMetainfoTrackerCount(tr_torrent_metainfo const* tm) -{ - return std::size(tm->trackers); -} - -tr_torrent_metainfo_tracker_info* tr_torrentMetainfoTracker( - tr_torrent_metainfo const* tm, - size_t n, - tr_torrent_metainfo_tracker_info* setme) -{ - auto it = std::begin(tm->trackers); - std::advance(it, n); - auto const& tracker = it->second; - setme->announce_url = tr_quark_get_string(tracker.announce_url); - setme->scrape_url = tr_quark_get_string(tracker.scrape_url); - setme->tier = tracker.tier; - return setme; -} -#endif - -/*** -**** -***/ - /** * @brief Ensure that the URLs for multfile torrents end in a slash. * diff --git a/libtransmission/torrent.cc b/libtransmission/torrent.cc index f5dd7f602..96d93d5d5 100644 --- a/libtransmission/torrent.cc +++ b/libtransmission/torrent.cc @@ -1243,9 +1243,14 @@ tr_torrent_view tr_torrentView(tr_torrent const* tor) return ret; } -char* tr_torrentFilename(tr_torrent const* tor) +std::string tr_torrentFilename(tr_torrent const* tor) { - return tr_strvDup(tor->torrentFile()); + return std::string{ tor->torrentFile() }; +} + +size_t tr_torrentFilenameToBuf(tr_torrent const* tor, char* buf, size_t buflen) +{ + return tr_strvToBuf(tr_torrentFilename(tor), buf, buflen); } /*** @@ -2058,9 +2063,14 @@ bool tr_torrentSetTrackerList(tr_torrent* tor, char const* text) return text != nullptr && tor->setTrackerList(text); } -char* tr_torrentGetTrackerList(tr_torrent const* tor) +std::string tr_torrentGetTrackerList(tr_torrent const* tor) { - return tr_strvDup(tor->trackerList()); + return tor->trackerList(); +} + +size_t tr_torrentGetTrackerListToBuf(tr_torrent const* tor, char* buf, size_t buflen) +{ + return tr_strvToBuf(tr_torrentGetTrackerList(tor), buf, buflen); } /** @@ -2310,11 +2320,15 @@ void tr_torrentGotBlock(tr_torrent* tor, tr_block_index_t block) **** ***/ -// TODO: clients that call this should call tr_torrent::findFile() instead -char* tr_torrentFindFile(tr_torrent const* tor, tr_file_index_t fileNum) +std::string tr_torrentFindFile(tr_torrent const* tor, tr_file_index_t file_num) { - auto const found = tor->findFile(fileNum); - return found ? tr_strdup(found->filename()) : nullptr; + auto const found = tor->findFile(file_num); + return std::string{ found ? found->filename().sv() : ""sv }; +} + +size_t tr_torrentFindFileToBuf(tr_torrent const* tor, tr_file_index_t file_num, char* buf, size_t buflen) +{ + return tr_strvToBuf(tr_torrentFindFile(tor, file_num), buf, buflen); } // decide whether we should be looking for files in downloadDir or incompleteDir diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index e5663c7bc..c4702b744 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -15,9 +15,7 @@ #pragma once /*** -**** **** Basic Types -**** ***/ #include /* bool */ @@ -25,6 +23,11 @@ #include /* uintN_t */ #include /* time_t */ +#ifdef __cplusplus +#include +#include +#endif + #include "tr-macros.h" using tr_file_index_t = uint32_t; @@ -49,8 +52,6 @@ struct tr_byte_span_t uint64_t end; }; -class tr_announce_list; - struct tr_ctor; struct tr_error; struct tr_session; @@ -97,9 +98,7 @@ enum tr_encryption_mode */ /** - * @brief returns Transmission's default configuration file directory. - * - * Use tr_free() to free the string when done. + * @brief get Transmission's default configuration file directory. * * The default configuration directory is determined this way: * -# If the TRANSMISSION_HOME environment variable is set, its value is used. @@ -108,19 +107,27 @@ enum tr_encryption_mode * -# If XDG_CONFIG_HOME is set, "${XDG_CONFIG_HOME}/${appname}" is used. * -# ${HOME}/.config/${appname}" is used as a last resort. */ -char* tr_getDefaultConfigDir(char const* appname); +#ifdef __cplusplus +[[nodiscard]] std::string tr_getDefaultConfigDir(std::string_view appname); +#endif + +/** @brief buffer variant of tr_getDefaultConfigDir(). See tr_strvToBuf(). */ +size_t tr_getDefaultConfigDirToBuf(char const* appname, char* buf, size_t buflen); /** * @brief returns Transmisson's default download directory. * - * Use tr_free() to free the string when done. - * * The default download directory is determined this way: * -# If the HOME environment variable is set, "${HOME}/Downloads" is used. * -# On Windows, "${CSIDL_MYDOCUMENTS}/Downloads" is used. * -# Otherwise, getpwuid(getuid())->pw_dir + "/Downloads" is used. */ -char* tr_getDefaultDownloadDir(); +#ifdef __cplusplus +[[nodiscard]] std::string tr_getDefaultDownloadDir(); +#endif + +/** @brief buffer variant of tr_getDefaultDownloadDir(). See tr_strvToBuf(). */ +size_t tr_getDefaultDownloadDirToBuf(char* buf, size_t buflen); #define TR_DEFAULT_BIND_ADDRESS_IPV4 "0.0.0.0" #define TR_DEFAULT_BIND_ADDRESS_IPV6 "::" @@ -1010,13 +1017,16 @@ uint64_t tr_torrentTotalSize(tr_torrent const*); /** * @brief find the location of a torrent's file by looking with and without * the ".part" suffix, looking in downloadDir and incompleteDir, etc. - * @return a newly-allocated string (that must be tr_free()d by the caller - * when done) that gives the location of this file on disk, - * or nullptr if no file exists yet. + * @return the path of this file, or an empty string if no file exists yet. * @param tor the torrent whose file we're looking for * @param fileNum the fileIndex, in [0...tr_torrentFileCount()) */ -char* tr_torrentFindFile(tr_torrent const* tor, tr_file_index_t fileNum); +#ifdef __cplusplus +[[nodiscard]] std::string tr_torrentFindFile(tr_torrent const* tor, tr_file_index_t file_num); +#endif + +/** @brief buffer variant of tr_torrentFindFile(). See tr_strvToBuf(). */ +size_t tr_torrentFindFileToBuf(tr_torrent const* tor, tr_file_index_t file_num, char* buf, size_t buflen); /*** **** Torrent speed limits @@ -1130,17 +1140,21 @@ char const* tr_torrentGetDownloadDir(tr_torrent const* torrent); char const* tr_torrentGetCurrentDir(tr_torrent const* tor); /** - * Returns a newly-allocated string with a magnet link of the torrent. - * Use tr_free() to free the string when done. + * Returns a the magnet link to the torrent. */ -char* tr_torrentGetMagnetLink(tr_torrent const* tor); +#ifdef __cplusplus +[[nodiscard]] std::string tr_torrentGetMagnetLink(tr_torrent const* tor); +#endif + +/** @brief buffer variant of tr_torrentGetMagnetLink(). See tr_strvToBuf(). */ +size_t tr_torrentGetMagnetLinkToBuf(tr_torrent const* tor, char* buf, size_t buflen); /** *** **/ /** - * Returns a newly-allocated string listing its tracker's announce URLs. + * Returns a string listing its tracker's announce URLs. * One URL per line, with a blank line between tiers. * * NOTE: this only includes the trackers included in the torrent and, @@ -1149,7 +1163,12 @@ char* tr_torrentGetMagnetLink(tr_torrent const* tor); * are applied to all public torrents. If you want a full display of all * trackers, use tr_torrentTracker() and tr_torrentTrackerCount() */ -char* tr_torrentGetTrackerList(tr_torrent const* tor); +#ifdef __cplusplus +[[nodiscard]] std::string tr_torrentGetTrackerList(tr_torrent const* tor); +#endif + +/** @brief buffer variant of tr_torrentGetTrackerList(). See tr_strvToBuf(). */ +size_t tr_torrentGetTrackerListToBuf(tr_torrent const* tor, char* buf, size_t buflen); /** * Sets a torrent's tracker list from a list of announce URLs with one @@ -1436,9 +1455,13 @@ struct tr_torrent_view tr_torrentView(tr_torrent const* tor); /* * Get the filename of Transmission's internal copy of the torrent file. - * This is a duplicate that must be freed with tr_free() when done. */ -char* tr_torrentFilename(tr_torrent const* tor); +#ifdef __cplusplus +[[nodiscard]] std::string tr_torrentFilename(tr_torrent const* tor); +#endif + +/** @brief buffer variant of tr_torrentFilename(). See tr_strvToBuf(). */ +size_t tr_torrentFilenameToBuf(tr_torrent const* tor, char* buf, size_t buflen); /*********************************************************************** * tr_torrentAvailability @@ -1475,7 +1498,6 @@ enum tr_torrent_activity TR_STATUS_SEED_WAIT = 5, /* Queued to seed */ TR_STATUS_SEED = 6 /* Seeding */ }; - enum { TR_PEER_FROM_INCOMING = 0, /* connections made to the listening port */ diff --git a/libtransmission/utils.cc b/libtransmission/utils.cc index b82aa7084..0c593f747 100644 --- a/libtransmission/utils.cc +++ b/libtransmission/utils.cc @@ -217,9 +217,21 @@ char* tr_strvDup(std::string_view in) return ret; } -char* tr_strdup(void const* in) +size_t tr_strvToBuf(std::string_view src, char* buf, size_t buflen) { - return in == nullptr ? nullptr : tr_strvDup(static_cast(in)); + size_t const len = std::size(src); + + if (buflen >= len) + { + auto const out = std::copy(std::begin(src), std::end(src), buf); + + if (buflen > len) + { + *out = '\0'; + } + } + + return len; } extern "C" @@ -625,7 +637,6 @@ static bool parseNumberSection(std::string_view str, number_range& range) * Given a string like "1-4" or "1-4,6,9,14-51", this allocates and returns an * array of setmeCount ints of all the values in the array. * For example, "5-8" will return [ 5, 6, 7, 8 ] and setmeCount will be 4. - * It's the caller's responsibility to call tr_free () on the returned array. * If a fragment of the string can't be parsed, nullptr is returned. */ std::vector tr_parseNumberRange(std::string_view str) diff --git a/libtransmission/utils.h b/libtransmission/utils.h index 6d8f6f53f..ecea16fb0 100644 --- a/libtransmission/utils.h +++ b/libtransmission/utils.h @@ -141,13 +141,6 @@ void tr_free(void* p); #define tr_renew(struct_type, mem, n_structs) \ (static_cast(tr_realloc((mem), sizeof(struct_type) * (size_t)(n_structs)))) -/** - * @brief make a newly-allocated copy of a string - * @param in is a void* so that callers can pass in both signed & unsigned without a cast - * @return a newly-allocated copy of `in' that can be freed with tr_free() - */ -[[nodiscard]] char* tr_strdup(void const* in); - constexpr bool tr_str_is_empty(char const* value) { return value == nullptr || *value == '\0'; @@ -236,6 +229,15 @@ constexpr bool tr_strvSep(std::string_view* sv, std::string_view* token, char de std::string& tr_strvUtf8Clean(std::string_view cleanme, std::string& setme); +/** + * @brief copies `src` into `buf`. + * + * - Always returns std::size(src). + * - `src` will be copied into `buf` iff `buflen >= std::size(src)` + * - `buf` will also be zero terminated iff `buflen >= std::size(src) + 1`. + */ +size_t tr_strvToBuf(std::string_view src, char* buf, size_t buflen); + /*** **** ***/ @@ -247,8 +249,7 @@ std::string& tr_strvUtf8Clean(std::string_view cleanme, std::string& setme); /** * @brief Given a string like "1-4" or "1-4,6,9,14-51", this returns a * newly-allocated array of all the integers in the set. - * @return a newly-allocated array of integers that must be freed with tr_free(), - * or nullptr if a fragment of the string can't be parsed. + * @return a vector of integers, which is empty if the string can't be parsed. * * For example, "5-8" will return [ 5, 6, 7, 8 ] and setmeCount will be 4. */ diff --git a/macosx/Controller.mm b/macosx/Controller.mm index ef8e2b376..56ab8e6bb 100644 --- a/macosx/Controller.mm +++ b/macosx/Controller.mm @@ -563,11 +563,10 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool tr_formatter_mem_init(1000, kbString.UTF8String, mbString.UTF8String, gbString.UTF8String, tbString.UTF8String); - char* const default_config_dir = tr_getDefaultConfigDir("Transmission"); - _fLib = tr_sessionInit(default_config_dir, YES, &settings); + auto const default_config_dir = tr_getDefaultConfigDir("Transmission"); + _fLib = tr_sessionInit(default_config_dir.c_str(), YES, &settings); tr_variantFree(&settings); - _fConfigDirectory = @(default_config_dir); - tr_free(default_config_dir); + _fConfigDirectory = @(default_config_dir.c_str()); tr_sessionSetIdleLimitHitCallback(_fLib, onIdleLimitHit, (__bridge void*)(self)); tr_sessionSetQueueStartCallback(_fLib, onStartQueue, (__bridge void*)(self)); diff --git a/macosx/Torrent.mm b/macosx/Torrent.mm index ae276dd29..aaa070b6f 100644 --- a/macosx/Torrent.mm +++ b/macosx/Torrent.mm @@ -11,7 +11,7 @@ #include #include -#include // tr_free() +#include #import "Torrent.h" #import "GroupsController.h" @@ -361,7 +361,7 @@ bool trashDataFile(char const* filename, tr_error** error) - (NSString*)magnetLink { - return @(tr_torrentGetMagnetLink(self.fHandle)); + return @(tr_torrentGetMagnetLink(self.fHandle).c_str()); } - (CGFloat)ratio @@ -682,10 +682,9 @@ bool trashDataFile(char const* filename, tr_error** error) new_tracker = [@"http://" stringByAppendingString:new_tracker]; } - char* old_list = tr_torrentGetTrackerList(self.fHandle); + auto const old_list = tr_torrentGetTrackerList(self.fHandle); auto const new_list = fmt::format(FMT_STRING("{:s}\n\n{:s}"), old_list, new_tracker.UTF8String); BOOL const success = tr_torrentSetTrackerList(self.fHandle, new_list.c_str()); - tr_free(old_list); return success; } @@ -759,10 +758,7 @@ bool trashDataFile(char const* filename, tr_error** error) - (NSString*)torrentLocation { - auto* const filename = tr_torrentFilename(self.fHandle); - NSString* ret = @(filename ? filename : ""); - tr_free(filename); - return ret; + return @(tr_torrentFilename(self.fHandle).c_str()); } - (NSString*)dataLocation @@ -785,16 +781,8 @@ bool trashDataFile(char const* filename, tr_error** error) } else { - char* location = tr_torrentFindFile(self.fHandle, 0); - if (location == NULL) - { - return nil; - } - - NSString* dataLocation = @(location); - free(location); - - return dataLocation; + auto const location = tr_torrentFindFile(self.fHandle, 0); + return std::empty(location) ? nil : @(location.c_str()); } } @@ -814,16 +802,8 @@ bool trashDataFile(char const* filename, tr_error** error) } else { - char* location = tr_torrentFindFile(self.fHandle, node.indexes.firstIndex); - if (location == NULL) - { - return nil; - } - - NSString* dataLocation = @(location); - free(location); - - return dataLocation; + auto const location = tr_torrentFindFile(self.fHandle, node.indexes.firstIndex); + return std::empty(location) ? nil : @(location.c_str()); } } diff --git a/qt/Application.cc b/qt/Application.cc index b7f7e8447..ef1b39e60 100644 --- a/qt/Application.cc +++ b/qt/Application.cc @@ -220,9 +220,7 @@ Application::Application(int& argc, char** argv) // set the fallback config dir if (config_dir.isNull()) { - auto* const default_config_dir = tr_getDefaultConfigDir("transmission"); - config_dir = QString::fromUtf8(default_config_dir); - tr_free(default_config_dir); + config_dir = QString::fromStdString(tr_getDefaultConfigDir("transmission")); } // ensure our config directory exists diff --git a/qt/Prefs.cc b/qt/Prefs.cc index a75d43ac9..0c578fff3 100644 --- a/qt/Prefs.cc +++ b/qt/Prefs.cc @@ -432,7 +432,7 @@ void Prefs::initDefaults(tr_variant* d) const auto constexpr StatsMode = std::string_view{ "total-ratio" }; auto constexpr WindowLayout = std::string_view{ "menu,toolbar,filter,list,statusbar" }; - auto* const download_dir = tr_getDefaultDownloadDir(); + auto const download_dir = tr_getDefaultDownloadDir(); tr_variantDictReserve(d, 38); dictAdd(d, TR_KEY_blocklist_updates_enabled, true); @@ -462,7 +462,7 @@ void Prefs::initDefaults(tr_variant* d) const dictAdd(d, TR_KEY_main_window_x, 50); dictAdd(d, TR_KEY_main_window_y, 50); dictAdd(d, TR_KEY_remote_session_port, TR_DEFAULT_RPC_PORT); - dictAdd(d, TR_KEY_download_dir, std::string_view{ download_dir }); + dictAdd(d, TR_KEY_download_dir, download_dir); dictAdd(d, TR_KEY_filter_mode, FilterMode); dictAdd(d, TR_KEY_main_window_layout_order, WindowLayout); dictAdd(d, TR_KEY_open_dialog_dir, QDir::home().absolutePath()); @@ -471,10 +471,8 @@ void Prefs::initDefaults(tr_variant* d) const dictAdd(d, TR_KEY_remote_session_username, SessionUsername); dictAdd(d, TR_KEY_sort_mode, SortMode); dictAdd(d, TR_KEY_statusbar_stats, StatsMode); - dictAdd(d, TR_KEY_watch_dir, std::string_view{ download_dir }); + dictAdd(d, TR_KEY_watch_dir, download_dir); dictAdd(d, TR_KEY_read_clipboard, false); - - tr_free(download_dir); } /*** diff --git a/tests/libtransmission/move-test.cc b/tests/libtransmission/move-test.cc index 9feaf955e..4a6493987 100644 --- a/tests/libtransmission/move-test.cc +++ b/tests/libtransmission/move-test.cc @@ -60,9 +60,9 @@ TEST_P(IncompleteDirTest, incompleteDir) auto path = tr_pathbuf{}; path.assign(incomplete_dir, '/', tr_torrentFile(tor, 0).name, tr_torrent_files::PartialFileSuffix); - EXPECT_EQ(path, makeString(tr_torrentFindFile(tor, 0))); + EXPECT_EQ(path, tr_torrentFindFile(tor, 0)); path.assign(incomplete_dir, '/', tr_torrentFile(tor, 1).name); - EXPECT_EQ(path, makeString(tr_torrentFindFile(tor, 1))); + EXPECT_EQ(path, tr_torrentFindFile(tor, 1)); EXPECT_EQ(tor->pieceSize(), tr_torrentStat(tor)->leftUntilDone); // auto constexpr completeness_unset = tr_completeness { -1 }; @@ -129,7 +129,7 @@ TEST_P(IncompleteDirTest, incompleteDir) for (tr_file_index_t i = 0; i < n; ++i) { auto const expected = tr_pathbuf{ download_dir, '/', tr_torrentFile(tor, i).name }; - EXPECT_EQ(expected, makeString(tr_torrentFindFile(tor, i))); + EXPECT_EQ(expected, tr_torrentFindFile(tor, i)); } // cleanup @@ -183,7 +183,7 @@ TEST_F(MoveTest, setLocation) for (tr_file_index_t i = 0; i < n; ++i) { auto const expected = tr_pathbuf{ target_dir, '/', tr_torrentFile(tor, i).name }; - EXPECT_EQ(expected, makeString(tr_torrentFindFile(tor, i))); + EXPECT_EQ(expected, tr_torrentFindFile(tor, i)); } // cleanup diff --git a/tests/libtransmission/platform-test.cc b/tests/libtransmission/platform-test.cc index 8b77cc75b..c69b7b8a7 100644 --- a/tests/libtransmission/platform-test.cc +++ b/tests/libtransmission/platform-test.cc @@ -15,7 +15,6 @@ using namespace std::literals; using PlatformTest = ::libtransmission::test::SessionTest; -using ::libtransmission::test::makeString; #ifdef _WIN32 #include @@ -28,8 +27,8 @@ TEST_F(PlatformTest, defaultDownloadDirXdg) setenv("HOME", sandboxDir().c_str(), 1); setenv("XDG_CONFIG_HOME", LIBTRANSMISSION_TEST_ASSETS_DIR, 1); - auto actual = makeString(tr_getDefaultDownloadDir()); - auto expected = fmt::format("{:s}/UserDirsDownloads"sv, sandboxDir()); + auto const expected = fmt::format("{:s}/UserDirsDownloads"sv, sandboxDir()); + auto const actual = tr_getDefaultDownloadDir(); EXPECT_EQ(expected, actual); unsetenv("XDG_CONFIG_HOME"); @@ -41,8 +40,8 @@ TEST_F(PlatformTest, defaultDownloadDir) { setenv("HOME", sandboxDir().c_str(), 1); - auto expected = fmt::format("{:s}/Downloads"sv, sandboxDir()); - auto actual = makeString(tr_getDefaultDownloadDir()); + auto const expected = fmt::format("{:s}/Downloads"sv, sandboxDir()); + auto const actual = tr_getDefaultDownloadDir(); EXPECT_EQ(expected, actual); unsetenv("HOME"); @@ -53,8 +52,8 @@ TEST_F(PlatformTest, defaultConfigDirEnv) { setenv("TRANSMISSION_HOME", sandboxDir().c_str(), 1); - auto actual = makeString(tr_getDefaultConfigDir("appname")); - auto expected = sandboxDir(); + auto const expected = sandboxDir(); + auto const actual = tr_getDefaultConfigDir("appname"); EXPECT_EQ(expected, actual); unsetenv("TRANSMISSION_HOME"); @@ -66,8 +65,8 @@ TEST_F(PlatformTest, defaultConfigDirXdgConfig) { setenv("XDG_CONFIG_HOME", sandboxDir().c_str(), 1); - auto expected = fmt::format("{:s}/appname", sandboxDir()); - auto actual = makeString(tr_getDefaultConfigDir("appname")); + auto const expected = fmt::format("{:s}/appname", sandboxDir()); + auto const actual = tr_getDefaultConfigDir("appname"); EXPECT_EQ(expected, actual); unsetenv("XDG_CONFIG_HOME"); @@ -78,8 +77,8 @@ TEST_F(PlatformTest, defaultConfigDirXdgConfigHome) auto const home = tr_pathbuf{ sandboxDir(), "/home/user" }; setenv("HOME", home, 1); - auto expected = fmt::format("{:s}/.config/appname", home.sv()); - auto actual = makeString(tr_getDefaultConfigDir("appname")); + auto const expected = fmt::format("{:s}/.config/appname", home.sv()); + auto const actual = tr_getDefaultConfigDir("appname"); EXPECT_EQ(expected, actual); unsetenv("HOME"); diff --git a/tests/libtransmission/rename-test.cc b/tests/libtransmission/rename-test.cc index 0530b4a3d..d21520af7 100644 --- a/tests/libtransmission/rename-test.cc +++ b/tests/libtransmission/rename-test.cc @@ -189,9 +189,8 @@ TEST_F(RenameTest, singleFilenameTorrent) EXPECT_FALSE(tr_sys_path_exists(tmpstr)); // confirm the old filename can't be found EXPECT_STREQ("foobar", tr_torrentName(tor)); // confirm the torrent's name is now 'foobar' EXPECT_STREQ("foobar", tr_torrentFile(tor, 0).name); // confirm the file's name is now 'foobar' - char* const torrent_filename = tr_torrentFilename(tor); - EXPECT_STREQ(nullptr, strstr(torrent_filename, "foobar")); // confirm torrent file hasn't changed - tr_free(torrent_filename); + auto const torrent_filename = tr_torrentFilename(tor); + EXPECT_EQ(std::string::npos, torrent_filename.find("foobar")); // confirm torrent file hasn't changed tmpstr.assign(tor->currentDir(), "/foobar"); EXPECT_TRUE(tr_sys_path_exists(tmpstr)); // confirm the file's name is now 'foobar' on the disk EXPECT_TRUE(testFileExistsAndConsistsOfThisString(tor, 0, "hello, world!\n")); // confirm the contents are right @@ -228,7 +227,6 @@ TEST_F(RenameTest, singleFilenameTorrent) TEST_F(RenameTest, multifileTorrent) { - char* str; auto constexpr TotalSize = size_t{ 67 }; auto constexpr ExpectedFiles = std::array{ "Felidae/Felinae/Acinonyx/Cheetah/Chester"sv, @@ -332,15 +330,13 @@ TEST_F(RenameTest, multifileTorrent) ***/ // remove the directory Felidae/Felinae/Felis/catus - str = tr_torrentFindFile(tor, 1); - EXPECT_NE(nullptr, str); + auto str = tr_torrentFindFile(tor, 1); + EXPECT_NE(""sv, str); tr_sys_path_remove(str); - tr_free(str); str = tr_torrentFindFile(tor, 2); - EXPECT_NE(nullptr, str); + EXPECT_NE(""sv, str); tr_sys_path_remove(str); tr_sys_path_remove(std::string{ tr_sys_path_dirname(str) }); - tr_free(str); sync(); blockingTorrentVerify(tor); testFileExistsAndConsistsOfThisString(tor, 0, ExpectedContents[0]); @@ -348,8 +344,7 @@ TEST_F(RenameTest, multifileTorrent) for (tr_file_index_t i = 1; i <= 2; ++i) { str = tr_torrentFindFile(tor, i); - EXPECT_STREQ(nullptr, str); - tr_free(str); + EXPECT_EQ(""sv, str); } testFileExistsAndConsistsOfThisString(tor, 3, ExpectedContents[3]); @@ -485,9 +480,8 @@ TEST_F(RenameTest, partialFile) for (tr_file_index_t i = 0; i < 3; ++i) { auto const expected = tr_pathbuf{ tor->currentDir(), '/', strings[i] }; - char* path = tr_torrentFindFile(tor, i); - EXPECT_EQ(expected, path); - tr_free(path); + auto const actual = tr_torrentFindFile(tor, i); + EXPECT_EQ(expected, actual); } torrentRemoveAndWait(tor, 0); diff --git a/tests/libtransmission/subprocess-test.cc b/tests/libtransmission/subprocess-test.cc index 56a895598..0d3e68a06 100644 --- a/tests/libtransmission/subprocess-test.cc +++ b/tests/libtransmission/subprocess-test.cc @@ -256,9 +256,11 @@ TEST_P(SubprocessTest, SpawnAsyncCwdExplicit) auto buffer = std::array{}; EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size())); - EXPECT_EQ( - makeString(tr_sys_path_native_separators(tr_strdup(test_dir.c_str()))), - tr_sys_path_native_separators(&buffer.front())); + auto expected = std::string{ test_dir }; + tr_sys_path_native_separators(std::data(expected)); + auto actual = std::string{ std::data(buffer) }; + tr_sys_path_native_separators(std::data(actual)); + EXPECT_EQ(expected, actual); EXPECT_FALSE(tr_sys_file_read_line(fd, buffer.data(), buffer.size())); diff --git a/tests/libtransmission/test-fixtures.h b/tests/libtransmission/test-fixtures.h index 638ac093d..f609ea828 100644 --- a/tests/libtransmission/test-fixtures.h +++ b/tests/libtransmission/test-fixtures.h @@ -71,13 +71,6 @@ static void depthFirstWalk(char const* path, file_func_t func) func(path); } -inline std::string makeString(char*&& s) -{ - auto const ret = std::string(s != nullptr ? s : ""); - tr_free(s); - return ret; -} - inline bool waitFor(std::function const& test, int msec) { auto const deadline = std::chrono::milliseconds{ msec }; diff --git a/utils/remote.cc b/utils/remote.cc index 2216656b8..e62277de7 100644 --- a/utils/remote.cc +++ b/utils/remote.cc @@ -2447,7 +2447,7 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv) break; case 'n': /* auth */ - auth = tr_strdup(optarg); + auth = tr_strvDup(optarg); break; case 810: /* authenv */ @@ -2464,7 +2464,7 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv) break; case 'N': /* netrc */ - netrc = tr_strdup(optarg); + netrc = tr_strvDup(optarg); break; case 820: /* UseSSL */ @@ -2543,7 +2543,7 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv) switch (c) { case 'F': - filter = tr_strdup(optarg); /* Unnecessary dup? we will use it before optarg will be changed */ + filter = tr_strvDup(optarg); /* Unnecessary dup? we will use it before optarg will be changed */ tr_variantDictAddInt(top, TR_KEY_tag, TAG_FILTER); for (size_t i = 0; i < TR_N_ELEMENTS(details_keys); ++i)