fix: sonarcloud (#2327)

This commit is contained in:
Charles Kerr 2021-12-17 14:48:02 -06:00 committed by GitHub
parent 37a8046ed5
commit 7ca0b4cc25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 429 additions and 443 deletions

View File

@ -20,6 +20,7 @@
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
#include <array>
#include <stdio.h> /* fprintf () */
#include <stdlib.h> /* atoi () */
#include <string.h> /* memcmp () */
@ -63,8 +64,13 @@
***/
#define LINEWIDTH 80
#define MY_CONFIG_NAME "transmission"
#define MY_READABLE_NAME "transmission-cli"
static char constexpr MyConfigName[] = "transmission";
static char constexpr MyReadableName[] = "transmission-cli";
static char constexpr Usage
[] = "A fast and easy BitTorrent client\n"
"\n"
"Usage: transmission-cli [options] <file|url|magnet>";
static bool showVersion = false;
static bool verify = false;
@ -73,38 +79,28 @@ static sig_atomic_t manualUpdate = false;
static char const* torrentPath = nullptr;
static struct tr_option const options[] = {
{ 'b', "blocklist", "Enable peer blocklists", "b", false, nullptr },
{ 'B', "no-blocklist", "Disable peer blocklists", "B", false, nullptr },
{ 'd', "downlimit", "Set max download speed in " SPEED_K_STR, "d", true, "<speed>" },
{ 'D', "no-downlimit", "Don't limit the download speed", "D", false, nullptr },
{ 910, "encryption-required", "Encrypt all peer connections", "er", false, nullptr },
{ 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", false, nullptr },
{ 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", false, nullptr },
{ 'f', "finish", "Run a script when the torrent finishes", "f", true, "<script>" },
{ 'g', "config-dir", "Where to find configuration files", "g", true, "<path>" },
{ 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", false, nullptr },
{ 'M', "no-portmap", "Disable portmapping", "M", false, nullptr },
{ 'p', "port", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "p", true, "<port>" },
{ 't', "tos", "Peer socket TOS (0 to 255, default=" TR_DEFAULT_PEER_SOCKET_TOS_STR ")", "t", true, "<tos>" },
{ 'u', "uplimit", "Set max upload speed in " SPEED_K_STR, "u", true, "<speed>" },
{ 'U', "no-uplimit", "Don't limit the upload speed", "U", false, nullptr },
{ 'v', "verify", "Verify the specified torrent", "v", false, nullptr },
{ 'V', "version", "Show version number and exit", "V", false, nullptr },
{ 'w', "download-dir", "Where to save downloaded data", "w", true, "<path>" },
{ 0, nullptr, nullptr, nullptr, false, nullptr }
static auto constexpr Options = std::array<tr_option, 19>{
{ { 'b', "blocklist", "Enable peer blocklists", "b", false, nullptr },
{ 'B', "no-blocklist", "Disable peer blocklists", "B", false, nullptr },
{ 'd', "downlimit", "Set max download speed in " SPEED_K_STR, "d", true, "<speed>" },
{ 'D', "no-downlimit", "Don't limit the download speed", "D", false, nullptr },
{ 910, "encryption-required", "Encrypt all peer connections", "er", false, nullptr },
{ 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", false, nullptr },
{ 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", false, nullptr },
{ 'f', "finish", "Run a script when the torrent finishes", "f", true, "<script>" },
{ 'g', "config-dir", "Where to find configuration files", "g", true, "<path>" },
{ 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", false, nullptr },
{ 'M', "no-portmap", "Disable portmapping", "M", false, nullptr },
{ 'p', "port", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "p", true, "<port>" },
{ 't', "tos", "Peer socket TOS (0 to 255, default=" TR_DEFAULT_PEER_SOCKET_TOS_STR ")", "t", true, "<tos>" },
{ 'u', "uplimit", "Set max upload speed in " SPEED_K_STR, "u", true, "<speed>" },
{ 'U', "no-uplimit", "Don't limit the upload speed", "U", false, nullptr },
{ 'v', "verify", "Verify the specified torrent", "v", false, nullptr },
{ 'V', "version", "Show version number and exit", "V", false, nullptr },
{ 'w', "download-dir", "Where to save downloaded data", "w", true, "<path>" },
{ 0, nullptr, nullptr, nullptr, false, nullptr } }
};
static char const* getUsage(void)
{
// clang-format off
return
"A fast and easy BitTorrent client\n"
"\n"
"Usage: " MY_READABLE_NAME " [options] <file|url|magnet>";
// clang-format on
}
static int parseCommandLine(tr_variant*, int argc, char const** argv);
static void sigHandler(int signal);
@ -217,7 +213,7 @@ static char const* getConfigDir(int argc, char const** argv)
char const* my_optarg;
int const ind = tr_optind;
while ((c = tr_getopt(getUsage(), argc, argv, options, &my_optarg)) != TR_OPT_DONE)
while ((c = tr_getopt(Usage, argc, argv, std::data(Options), &my_optarg)) != TR_OPT_DONE)
{
if (c == 'g')
{
@ -230,7 +226,7 @@ static char const* getConfigDir(int argc, char const** argv)
if (configDir == nullptr)
{
configDir = tr_getDefaultConfigDir(MY_CONFIG_NAME);
configDir = tr_getDefaultConfigDir(MyConfigName);
}
return configDir;
@ -248,19 +244,19 @@ int tr_main(int argc, char* argv[])
tr_formatter_size_init(DISK_K, DISK_K_STR, DISK_M_STR, DISK_G_STR, DISK_T_STR);
tr_formatter_speed_init(SPEED_K, SPEED_K_STR, SPEED_M_STR, SPEED_G_STR, SPEED_T_STR);
printf("%s %s\n", MY_READABLE_NAME, LONG_VERSION_STRING);
printf("%s %s\n", MyReadableName, LONG_VERSION_STRING);
/* user needs to pass in at least one argument */
if (argc < 2)
{
tr_getopt_usage(MY_READABLE_NAME, getUsage(), options);
tr_getopt_usage(MyReadableName, Usage, std::data(Options));
return EXIT_FAILURE;
}
/* load the defaults from config file + libtransmission defaults */
tr_variantInitDict(&settings, 0);
configDir = getConfigDir(argc, (char const**)argv);
tr_sessionLoadSettings(&settings, configDir, MY_CONFIG_NAME);
tr_sessionLoadSettings(&settings, configDir, MyConfigName);
/* the command line overrides defaults */
if (parseCommandLine(&settings, argc, (char const**)argv) != 0)
@ -422,7 +418,7 @@ static int parseCommandLine(tr_variant* d, int argc, char const** argv)
int c;
char const* my_optarg;
while ((c = tr_getopt(getUsage(), argc, argv, options, &my_optarg)) != TR_OPT_DONE)
while ((c = tr_getopt(Usage, argc, argv, std::data(Options), &my_optarg)) != TR_OPT_DONE)
{
switch (c)
{

View File

@ -6,6 +6,7 @@
*
*/
#include <array>
#include <errno.h>
#include <stdio.h> /* printf */
#include <stdlib.h> /* atoi */
@ -54,6 +55,16 @@ static void sd_notifyf(int /*status*/, char const* /*fmt*/, ...)
#include "daemon.h"
static char constexpr MyName[] = "transmission-daemon";
static char constexpr Usage[] = "Transmission " LONG_VERSION_STRING
" https://transmissionbt.com/\n"
"A fast and easy BitTorrent client\n"
"\n"
"transmission-daemon is a headless Transmission session that can be\n"
"controlled via transmission-qt, transmission-remote, or its web interface.\n"
"\n"
"Usage: transmission-daemon [options]";
#define MY_NAME "transmission-daemon"
#define MEM_K 1024
@ -88,85 +99,70 @@ static struct event_base* ev_base = nullptr;
**** Config File
***/
static char const* getUsage(void)
{
// clang-format off
return
"Transmission " LONG_VERSION_STRING " https://transmissionbt.com/\n"
"A fast and easy BitTorrent client\n"
"\n"
MY_NAME " is a headless Transmission session\n"
"that can be controlled via transmission-remote\n"
"or the web interface.\n"
"\n"
"Usage: " MY_NAME " [options]";
// clang-format on
}
static struct tr_option const options[] = {
{ 'a', "allowed", "Allowed IP addresses. (Default: " TR_DEFAULT_RPC_WHITELIST ")", "a", true, "<list>" },
{ 'b', "blocklist", "Enable peer blocklists", "b", false, nullptr },
{ 'B', "no-blocklist", "Disable peer blocklists", "B", false, nullptr },
{ 'c', "watch-dir", "Where to watch for new .torrent files", "c", true, "<directory>" },
{ 'C', "no-watch-dir", "Disable the watch-dir", "C", false, nullptr },
{ 941, "incomplete-dir", "Where to store new torrents until they're complete", nullptr, true, "<directory>" },
{ 942, "no-incomplete-dir", "Don't store incomplete torrents in a different location", nullptr, false, nullptr },
{ 'd', "dump-settings", "Dump the settings and exit", "d", false, nullptr },
{ 'e', "logfile", "Dump the log messages to this filename", "e", true, "<filename>" },
{ 'f', "foreground", "Run in the foreground instead of daemonizing", "f", false, nullptr },
{ 'g', "config-dir", "Where to look for configuration files", "g", true, "<path>" },
{ 'p', "port", "RPC port (Default: " TR_DEFAULT_RPC_PORT_STR ")", "p", true, "<port>" },
{ 't', "auth", "Require authentication", "t", false, nullptr },
{ 'T', "no-auth", "Don't require authentication", "T", false, nullptr },
{ 'u', "username", "Set username for authentication", "u", true, "<username>" },
{ 'v', "password", "Set password for authentication", "v", true, "<password>" },
{ 'V', "version", "Show version number and exit", "V", false, nullptr },
{ 810, "log-error", "Show error messages", nullptr, false, nullptr },
{ 811, "log-info", "Show error and info messages", nullptr, false, nullptr },
{ 812, "log-debug", "Show error, info, and debug messages", nullptr, false, nullptr },
{ 'w', "download-dir", "Where to save downloaded data", "w", true, "<path>" },
{ 800, "paused", "Pause all torrents on startup", nullptr, false, nullptr },
{ 'o', "dht", "Enable distributed hash tables (DHT)", "o", false, nullptr },
{ 'O', "no-dht", "Disable distributed hash tables (DHT)", "O", false, nullptr },
{ 'y', "lpd", "Enable local peer discovery (LPD)", "y", false, nullptr },
{ 'Y', "no-lpd", "Disable local peer discovery (LPD)", "Y", false, nullptr },
{ 830, "utp", "Enable uTP for peer connections", nullptr, false, nullptr },
{ 831, "no-utp", "Disable uTP for peer connections", nullptr, false, nullptr },
{ 'P', "peerport", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "P", true, "<port>" },
{ 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", false, nullptr },
{ 'M', "no-portmap", "Disable portmapping", "M", false, nullptr },
{ 'L',
"peerlimit-global",
"Maximum overall number of peers (Default: " TR_DEFAULT_PEER_LIMIT_GLOBAL_STR ")",
"L",
true,
"<limit>" },
{ 'l',
"peerlimit-torrent",
"Maximum number of peers per torrent (Default: " TR_DEFAULT_PEER_LIMIT_TORRENT_STR ")",
"l",
true,
"<limit>" },
{ 910, "encryption-required", "Encrypt all peer connections", "er", false, nullptr },
{ 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", false, nullptr },
{ 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", false, nullptr },
{ 'i', "bind-address-ipv4", "Where to listen for peer connections", "i", true, "<ipv4 addr>" },
{ 'I', "bind-address-ipv6", "Where to listen for peer connections", "I", true, "<ipv6 addr>" },
{ 'r', "rpc-bind-address", "Where to listen for RPC connections", "r", true, "<ip addr>" },
{ 953,
"global-seedratio",
"All torrents, unless overridden by a per-torrent setting, should seed until a specific ratio",
"gsr",
true,
"ratio" },
{ 954,
"no-global-seedratio",
"All torrents, unless overridden by a per-torrent setting, should seed regardless of ratio",
"GSR",
false,
nullptr },
{ 'x', "pid-file", "Enable PID file", "x", true, "<pid-file>" },
{ 0, nullptr, nullptr, nullptr, false, nullptr }
static auto constexpr Options = std::array<tr_option, 43>{
{ { 'a', "allowed", "Allowed IP addresses. (Default: " TR_DEFAULT_RPC_WHITELIST ")", "a", true, "<list>" },
{ 'b', "blocklist", "Enable peer blocklists", "b", false, nullptr },
{ 'B', "no-blocklist", "Disable peer blocklists", "B", false, nullptr },
{ 'c', "watch-dir", "Where to watch for new .torrent files", "c", true, "<directory>" },
{ 'C', "no-watch-dir", "Disable the watch-dir", "C", false, nullptr },
{ 941, "incomplete-dir", "Where to store new torrents until they're complete", nullptr, true, "<directory>" },
{ 942, "no-incomplete-dir", "Don't store incomplete torrents in a different location", nullptr, false, nullptr },
{ 'd', "dump-settings", "Dump the settings and exit", "d", false, nullptr },
{ 'e', "logfile", "Dump the log messages to this filename", "e", true, "<filename>" },
{ 'f', "foreground", "Run in the foreground instead of daemonizing", "f", false, nullptr },
{ 'g', "config-dir", "Where to look for configuration files", "g", true, "<path>" },
{ 'p', "port", "RPC port (Default: " TR_DEFAULT_RPC_PORT_STR ")", "p", true, "<port>" },
{ 't', "auth", "Require authentication", "t", false, nullptr },
{ 'T', "no-auth", "Don't require authentication", "T", false, nullptr },
{ 'u', "username", "Set username for authentication", "u", true, "<username>" },
{ 'v', "password", "Set password for authentication", "v", true, "<password>" },
{ 'V', "version", "Show version number and exit", "V", false, nullptr },
{ 810, "log-error", "Show error messages", nullptr, false, nullptr },
{ 811, "log-info", "Show error and info messages", nullptr, false, nullptr },
{ 812, "log-debug", "Show error, info, and debug messages", nullptr, false, nullptr },
{ 'w', "download-dir", "Where to save downloaded data", "w", true, "<path>" },
{ 800, "paused", "Pause all torrents on startup", nullptr, false, nullptr },
{ 'o', "dht", "Enable distributed hash tables (DHT)", "o", false, nullptr },
{ 'O', "no-dht", "Disable distributed hash tables (DHT)", "O", false, nullptr },
{ 'y', "lpd", "Enable local peer discovery (LPD)", "y", false, nullptr },
{ 'Y', "no-lpd", "Disable local peer discovery (LPD)", "Y", false, nullptr },
{ 830, "utp", "Enable uTP for peer connections", nullptr, false, nullptr },
{ 831, "no-utp", "Disable uTP for peer connections", nullptr, false, nullptr },
{ 'P', "peerport", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "P", true, "<port>" },
{ 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", false, nullptr },
{ 'M', "no-portmap", "Disable portmapping", "M", false, nullptr },
{ 'L',
"peerlimit-global",
"Maximum overall number of peers (Default: " TR_DEFAULT_PEER_LIMIT_GLOBAL_STR ")",
"L",
true,
"<limit>" },
{ 'l',
"peerlimit-torrent",
"Maximum number of peers per torrent (Default: " TR_DEFAULT_PEER_LIMIT_TORRENT_STR ")",
"l",
true,
"<limit>" },
{ 910, "encryption-required", "Encrypt all peer connections", "er", false, nullptr },
{ 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", false, nullptr },
{ 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", false, nullptr },
{ 'i', "bind-address-ipv4", "Where to listen for peer connections", "i", true, "<ipv4 addr>" },
{ 'I', "bind-address-ipv6", "Where to listen for peer connections", "I", true, "<ipv6 addr>" },
{ 'r', "rpc-bind-address", "Where to listen for RPC connections", "r", true, "<ip addr>" },
{ 953,
"global-seedratio",
"All torrents, unless overridden by a per-torrent setting, should seed until a specific ratio",
"gsr",
true,
"ratio" },
{ 954,
"no-global-seedratio",
"All torrents, unless overridden by a per-torrent setting, should seed regardless of ratio",
"GSR",
false,
nullptr },
{ 'x', "pid-file", "Enable PID file", "x", true, "<pid-file>" },
{ 0, nullptr, nullptr, nullptr, false, nullptr } }
};
static bool reopen_log_file(char const* filename)
@ -203,7 +199,7 @@ static char const* getConfigDir(int argc, char const* const* argv)
char const* optstr;
int const ind = tr_optind;
while ((c = tr_getopt(getUsage(), argc, argv, options, &optstr)) != TR_OPT_DONE)
while ((c = tr_getopt(Usage, argc, argv, std::data(Options), &optstr)) != TR_OPT_DONE)
{
if (c == 'g')
{
@ -216,7 +212,7 @@ static char const* getConfigDir(int argc, char const* const* argv)
if (configDir == nullptr)
{
configDir = tr_getDefaultConfigDir(MY_NAME);
configDir = tr_getDefaultConfigDir(MyName);
}
return configDir;
@ -415,7 +411,7 @@ static bool parse_args(
tr_optind = 1;
while ((c = tr_getopt(getUsage(), argc, argv, options, &optstr)) != TR_OPT_DONE)
while ((c = tr_getopt(Usage, argc, argv, std::data(Options), &optstr)) != TR_OPT_DONE)
{
switch (c)
{
@ -470,7 +466,7 @@ static bool parse_args(
break;
case 'V': /* version */
fprintf(stderr, "%s %s\n", MY_NAME, LONG_VERSION_STRING);
fprintf(stderr, "%s %s\n", MyName, LONG_VERSION_STRING);
*exit_code = 0;
return false;
@ -596,7 +592,7 @@ static bool parse_args(
break;
default:
tr_getopt_usage(MY_NAME, getUsage(), options);
tr_getopt_usage(MyName, Usage, std::data(Options));
*exit_code = 0;
return false;
}
@ -634,7 +630,7 @@ static void daemon_reconfigure(void* /*arg*/)
tr_logAddInfo("Reloading settings from \"%s\"", configDir);
tr_variantInitDict(&settings, 0);
tr_variantDictAddBool(&settings, TR_KEY_rpc_enabled, true);
tr_sessionLoadSettings(&settings, configDir, MY_NAME);
tr_sessionLoadSettings(&settings, configDir, MyName);
tr_sessionSet(mySession, &settings);
tr_variantFree(&settings);
tr_sessionReloadBlocklists(mySession);
@ -670,7 +666,7 @@ static int daemon_start(void* varg, [[maybe_unused]] bool foreground)
{
char buf[256];
tr_snprintf(buf, sizeof(buf), "Failed to init daemon event state: %s", tr_strerror(errno));
printMessage(logfile, TR_LOG_ERROR, MY_NAME, buf, __FILE__, __LINE__);
printMessage(logfile, TR_LOG_ERROR, MyName, buf, __FILE__, __LINE__);
return 1;
}
@ -711,7 +707,7 @@ static int daemon_start(void* varg, [[maybe_unused]] bool foreground)
if (tr_variantDictFindBool(settings, TR_KEY_rpc_authentication_required, &boolVal) && boolVal)
{
tr_logAddNamedInfo(MY_NAME, "requiring authentication");
tr_logAddNamedInfo(MyName, "requiring authentication");
}
mySession = session;
@ -761,7 +757,7 @@ static int daemon_start(void* varg, [[maybe_unused]] bool foreground)
if (!foreground)
{
openlog(MY_NAME, LOG_CONS | LOG_PID, LOG_DAEMON);
openlog(MyName, LOG_CONS | LOG_PID, LOG_DAEMON);
}
#endif
@ -841,7 +837,7 @@ static bool init_daemon_data(int argc, char* argv[], struct daemon_data* data, b
/* 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, MY_NAME);
bool const loaded = tr_sessionLoadSettings(&data->settings, data->configDir, MyName);
bool dumpSettings;
@ -860,7 +856,7 @@ static bool init_daemon_data(int argc, char* argv[], struct daemon_data* data, b
if (!loaded)
{
printMessage(logfile, TR_LOG_ERROR, MY_NAME, "Error loading config file -- exiting.", __FILE__, __LINE__);
printMessage(logfile, TR_LOG_ERROR, MyName, "Error loading config file -- exiting.", __FILE__, __LINE__);
*ret = 1;
goto EXIT_EARLY;
}
@ -906,7 +902,7 @@ int tr_main(int argc, char* argv[])
{
char buf[256];
tr_snprintf(buf, sizeof(buf), "Failed to daemonize: %s", error->message);
printMessage(logfile, TR_LOG_ERROR, MY_NAME, buf, __FILE__, __LINE__);
printMessage(logfile, TR_LOG_ERROR, MyName, buf, __FILE__, __LINE__);
tr_error_free(error);
}

View File

@ -6,6 +6,7 @@
*
*/
#include <array>
#include <stdio.h> /* fprintf() */
#include <stdlib.h> /* strtoul(), EXIT_FAILURE */
#include <inttypes.h> /* PRIu32 */
@ -22,91 +23,83 @@
using namespace std::literals;
#define MY_NAME "transmission-create"
static char constexpr MyName[] = "transmission-create";
static char constexpr Usage[] = "Usage: transmission-create [options] <file|directory>";
#define MAX_TRACKERS 128
static uint32_t const KiB = 1024;
static tr_tracker_info trackers[MAX_TRACKERS];
static int trackerCount = 0;
static bool isPrivate = false;
static bool showVersion = false;
static char const* comment = nullptr;
static char const* outfile = nullptr;
static char const* infile = nullptr;
static uint32_t piecesize_kib = 0;
static char const* source = NULL;
static uint32_t constexpr KiB = 1024;
static tr_option options[] = {
{ 'p', "private", "Allow this torrent to only be used with the specified tracker(s)", "p", false, nullptr },
{ 'r', "source", "Set the source for private trackers", "r", true, "<source>" },
{ 'o', "outfile", "Save the generated .torrent to this filename", "o", true, "<file>" },
{ 's', "piecesize", "Set how many KiB each piece should be, overriding the preferred default", "s", true, "<size in KiB>" },
{ 'c', "comment", "Add a comment", "c", true, "<comment>" },
{ 't', "tracker", "Add a tracker's announce URL", "t", true, "<url>" },
{ 'V', "version", "Show version number and exit", "V", false, nullptr },
{ 0, nullptr, nullptr, nullptr, false, nullptr }
static auto constexpr Options = std::array<tr_option, 8>{
{ { 'p', "private", "Allow this torrent to only be used with the specified tracker(s)", "p", false, nullptr },
{ 'r', "source", "Set the source for private trackers", "r", true, "<source>" },
{ 'o', "outfile", "Save the generated .torrent to this filename", "o", true, "<file>" },
{ 's', "piecesize", "Set the piece size in KiB, overriding the preferred default", "s", true, "<KiB>" },
{ 'c', "comment", "Add a comment", "c", true, "<comment>" },
{ 't', "tracker", "Add a tracker's announce URL", "t", true, "<url>" },
{ 'V', "version", "Show version number and exit", "V", false, nullptr },
{ 0, nullptr, nullptr, nullptr, false, nullptr } }
};
static char const* getUsage(void)
struct app_options
{
return "Usage: " MY_NAME " [options] <file|directory>";
}
std::vector<tr_tracker_info> trackers;
bool is_private = false;
bool show_version = false;
char const* comment = nullptr;
char const* outfile = nullptr;
char const* infile = nullptr;
uint32_t piecesize_kib = 0;
char const* source = nullptr;
};
static int parseCommandLine(int argc, char const* const* argv)
static int parseCommandLine(app_options& options, int argc, char const* const* argv)
{
int c;
char const* optarg;
while ((c = tr_getopt(getUsage(), argc, argv, options, &optarg)) != TR_OPT_DONE)
while ((c = tr_getopt(Usage, argc, argv, std::data(Options), &optarg)) != TR_OPT_DONE)
{
switch (c)
{
case 'V':
showVersion = true;
options.show_version = true;
break;
case 'p':
isPrivate = true;
options.is_private = true;
break;
case 'o':
outfile = optarg;
options.outfile = optarg;
break;
case 'c':
comment = optarg;
options.comment = optarg;
break;
case 't':
if (trackerCount + 1 < MAX_TRACKERS)
{
trackers[trackerCount].tier = trackerCount;
trackers[trackerCount].announce = (char*)optarg;
++trackerCount;
}
options.trackers.push_back(tr_tracker_info{ 0, const_cast<char*>(optarg), nullptr, 0 });
break;
case 's':
if (optarg != nullptr)
{
char* endptr = nullptr;
piecesize_kib = strtoul(optarg, &endptr, 10);
options.piecesize_kib = strtoul(optarg, &endptr, 10);
if (endptr != nullptr && *endptr == 'M')
{
piecesize_kib *= KiB;
options.piecesize_kib *= KiB;
}
}
break;
case 'r':
source = optarg;
options.source = optarg;
break;
case TR_OPT_UNK:
infile = optarg;
options.infile = optarg;
break;
default:
@ -144,29 +137,30 @@ int tr_main(int argc, char* argv[])
tr_formatter_size_init(DISK_K, DISK_K_STR, DISK_M_STR, DISK_G_STR, DISK_T_STR);
tr_formatter_speed_init(SPEED_K, SPEED_K_STR, SPEED_M_STR, SPEED_G_STR, SPEED_T_STR);
if (parseCommandLine(argc, (char const* const*)argv) != 0)
auto options = app_options{};
if (parseCommandLine(options, argc, (char const* const*)argv) != 0)
{
return EXIT_FAILURE;
}
if (showVersion)
if (options.show_version)
{
fprintf(stderr, MY_NAME " " LONG_VERSION_STRING "\n");
fprintf(stderr, "%s %s\n", MyName, LONG_VERSION_STRING);
return EXIT_SUCCESS;
}
if (infile == nullptr)
if (options.infile == nullptr)
{
fprintf(stderr, "ERROR: No input file or directory specified.\n");
tr_getopt_usage(MY_NAME, getUsage(), options);
tr_getopt_usage(MyName, Usage, std::data(Options));
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
if (outfile == nullptr)
if (options.outfile == nullptr)
{
tr_error* error = nullptr;
char* base = tr_sys_path_basename(infile, &error);
char* base = tr_sys_path_basename(options.infile, &error);
if (base == nullptr)
{
@ -176,14 +170,14 @@ int tr_main(int argc, char* argv[])
auto const end = tr_strvJoin(base, ".torrent"sv);
char* cwd = tr_getcwd();
outfile = out2 = tr_buildPath(cwd, end.c_str(), nullptr);
options.outfile = out2 = tr_buildPath(cwd, end.c_str(), nullptr);
tr_free(cwd);
tr_free(base);
}
if (trackerCount == 0)
if (std::empty(options.trackers))
{
if (isPrivate)
if (options.is_private)
{
fprintf(stderr, "ERROR: no trackers specified for a private torrent\n");
return EXIT_FAILURE;
@ -194,9 +188,9 @@ int tr_main(int argc, char* argv[])
}
}
printf("Creating torrent \"%s\"\n", outfile);
printf("Creating torrent \"%s\"\n", options.outfile);
b = tr_metaInfoBuilderCreate(infile);
b = tr_metaInfoBuilderCreate(options.infile);
if (b == nullptr)
{
@ -204,9 +198,9 @@ int tr_main(int argc, char* argv[])
return EXIT_FAILURE;
}
if (piecesize_kib != 0)
if (options.piecesize_kib != 0)
{
tr_metaInfoBuilderSetPieceSize(b, piecesize_kib * KiB);
tr_metaInfoBuilderSetPieceSize(b, options.piecesize_kib * KiB);
}
char buf[128];
@ -219,7 +213,14 @@ int tr_main(int argc, char* argv[])
b->pieceCount,
tr_formatter_size_B(buf, b->pieceSize, sizeof(buf)));
tr_makeMetaInfo(b, outfile, trackers, trackerCount, comment, isPrivate, source);
tr_makeMetaInfo(
b,
options.outfile,
std::data(options.trackers),
std::size(options.trackers),
options.comment,
options.is_private,
options.source);
uint32_t last = UINT32_MAX;
while (!b->isDone)

View File

@ -6,8 +6,10 @@
*
*/
#include <array>
#include <stdio.h> /* fprintf() */
#include <stdlib.h> /* EXIT_FAILURE */
#include <vector>
#include <event2/buffer.h>
@ -18,63 +20,61 @@
#include <libtransmission/variant.h>
#include <libtransmission/version.h>
#define MY_NAME "transmission-edit"
static char constexpr MyName[] = "transmission-edit";
static char constexpr Usage[] = "Usage: transmission-edit [options] torrent-file(s)";
static int fileCount = 0;
static bool showVersion = false;
static char const** files = nullptr;
static char const* add = nullptr;
static char const* deleteme = nullptr;
static char const* replace[2] = { nullptr, nullptr };
static tr_option options[] = {
{ 'a', "add", "Add a tracker's announce URL", "a", true, "<url>" },
{ 'd', "delete", "Delete a tracker's announce URL", "d", true, "<url>" },
{ 'r', "replace", "Search and replace a substring in the announce URLs", "r", true, "<old> <new>" },
{ 'V', "version", "Show version number and exit", "V", false, nullptr },
{ 0, nullptr, nullptr, nullptr, false, nullptr }
struct app_options
{
std::vector<char const*> files;
char const* add = nullptr;
char const* deleteme = nullptr;
std::array<char const*, 2> replace;
bool show_version = false;
};
static char const* getUsage(void)
{
return "Usage: " MY_NAME " [options] torrent-file(s)";
}
static auto constexpr Options = std::array<tr_option, 5>{
{ { 'a', "add", "Add a tracker's announce URL", "a", true, "<url>" },
{ 'd', "delete", "Delete a tracker's announce URL", "d", true, "<url>" },
{ 'r', "replace", "Search and replace a substring in the announce URLs", "r", true, "<old> <new>" },
{ 'V', "version", "Show version number and exit", "V", false, nullptr },
{ 0, nullptr, nullptr, nullptr, false, nullptr } }
};
static int parseCommandLine(int argc, char const* const* argv)
static int parseCommandLine(app_options& opts, int argc, char const* const* argv)
{
int c;
char const* optarg;
while ((c = tr_getopt(getUsage(), argc, argv, options, &optarg)) != TR_OPT_DONE)
while ((c = tr_getopt(Usage, argc, argv, std::data(Options), &optarg)) != TR_OPT_DONE)
{
switch (c)
{
case 'a':
add = optarg;
opts.add = optarg;
break;
case 'd':
deleteme = optarg;
opts.deleteme = optarg;
break;
case 'r':
replace[0] = optarg;
c = tr_getopt(getUsage(), argc, argv, options, &optarg);
opts.replace[0] = optarg;
c = tr_getopt(Usage, argc, argv, std::data(Options), &optarg);
if (c != TR_OPT_UNK)
{
return 1;
}
replace[1] = optarg;
opts.replace[1] = optarg;
break;
case 'V':
showVersion = true;
opts.show_version = true;
break;
case TR_OPT_UNK:
files[fileCount++] = optarg;
opts.files.push_back(optarg);
break;
default:
@ -166,11 +166,11 @@ static std::string replaceSubstr(std::string_view str, std::string_view oldval,
{
auto const pos = str.find(oldval);
ret += str.substr(0, pos);
ret += newval;
if (pos == str.npos)
{
break;
}
ret += newval;
str.remove_prefix(pos + std::size(oldval));
}
@ -301,42 +301,40 @@ int tr_main(int argc, char* argv[])
{
int changedCount = 0;
files = tr_new0(char const*, argc);
tr_logSetLevel(TR_LOG_ERROR);
if (parseCommandLine(argc, (char const* const*)argv) != 0)
auto options = app_options{};
if (parseCommandLine(options, argc, (char const* const*)argv) != 0)
{
return EXIT_FAILURE;
}
if (showVersion)
if (options.show_version)
{
fprintf(stderr, MY_NAME " " LONG_VERSION_STRING "\n");
fprintf(stderr, "%s %s\n", MyName, LONG_VERSION_STRING);
return EXIT_SUCCESS;
}
if (fileCount < 1)
if (std::empty(options.files))
{
fprintf(stderr, "ERROR: No torrent files specified.\n");
tr_getopt_usage(MY_NAME, getUsage(), options);
tr_getopt_usage(MyName, Usage, std::data(Options));
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
if (add == nullptr && deleteme == nullptr && replace[0] == 0)
if (options.add == nullptr && options.deleteme == nullptr && options.replace[0] == 0)
{
fprintf(stderr, "ERROR: Must specify -a, -d or -r\n");
tr_getopt_usage(MY_NAME, getUsage(), options);
tr_getopt_usage(MyName, Usage, std::data(Options));
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
for (int i = 0; i < fileCount; ++i)
for (auto const& filename : options.files)
{
tr_variant top;
bool changed = false;
char const* filename = files[i];
tr_error* error = nullptr;
printf("%s\n", filename);
@ -348,19 +346,19 @@ int tr_main(int argc, char* argv[])
continue;
}
if (deleteme != nullptr)
if (options.deleteme != nullptr)
{
changed |= removeURL(&top, deleteme);
changed |= removeURL(&top, options.deleteme);
}
if (add != nullptr)
if (options.add != nullptr)
{
changed = addURL(&top, add);
changed = addURL(&top, options.add);
}
if (replace[0] != nullptr && replace[1] != nullptr)
if (options.replace[0] != nullptr && options.replace[1] != nullptr)
{
changed |= replaceURL(&top, replace[0], replace[1]);
changed |= replaceURL(&top, options.replace[0], options.replace[1]);
}
if (changed)
@ -374,6 +372,5 @@ int tr_main(int argc, char* argv[])
printf("Changed %d files\n", changedCount);
tr_free((void*)files);
return EXIT_SUCCESS;
}

View File

@ -6,6 +6,7 @@
*
*/
#include <array>
#include <assert.h>
#include <ctype.h> /* isspace */
#include <errno.h>
@ -33,31 +34,45 @@
using namespace std::literals;
static auto constexpr DefaultHost = "localhost"sv;
static auto constexpr DefaultPort = int{ TR_DEFAULT_RPC_PORT };
#define MY_NAME "transmission-remote"
#define DEFAULT_URL TR_DEFAULT_RPC_URL_STR "rpc/"
#define ARGUMENTS TR_KEY_arguments
#define MEM_K 1024
#define MEM_K_STR "KiB"
#define MEM_M_STR "MiB"
#define MEM_G_STR "GiB"
#define MEM_T_STR "TiB"
#define DISK_K 1000
#define DISK_K_STR "kB"
#define DISK_M_STR "MB"
#define DISK_G_STR "GB"
#define DISK_T_STR "TB"
#define SPEED_K 1000
#define SPEED_K_STR "kB/s"
#define SPEED_M_STR "MB/s"
#define SPEED_G_STR "GB/s"
#define SPEED_T_STR "TB/s"
#define MEM_M_STR "MiB"
static auto constexpr DefaultPort = int{ TR_DEFAULT_RPC_PORT };
static char constexpr DefaultHost[] = "localhost";
static char constexpr DefaultUrl[] = TR_DEFAULT_RPC_URL_STR "rpc/";
static char constexpr MyName[] = "transmission-remote";
static char constexpr Usage[] = "transmission-remote " LONG_VERSION_STRING
"\n"
"A fast and easy BitTorrent client\n"
"https://transmissionbt.com/\n"
"\n"
"Usage: transmission-remote [host] [options]\n"
" transmission-remote [port] [options]\n"
" transmission-remote [host:port] [options]\n"
" transmission-remote [http(s?)://host:port/transmission/] [options]\n"
"\n"
"See the man page for detailed explanations and many examples.";
static auto constexpr Arguments = TR_KEY_arguments;
static auto constexpr MemK = size_t{ 1024 };
static char constexpr MemKStr[] = "KiB";
static char constexpr MemMStr[] = MEM_M_STR;
static char constexpr MemGStr[] = "GiB";
static char constexpr MemTStr[] = "TiB";
static auto constexpr DiskK = size_t{ 1000 };
static char constexpr DiskKStr[] = "kB";
static char constexpr DiskMStr[] = "MB";
static char constexpr DiskGStr[] = "GB";
static char constexpr DiskTStr[] = "TB";
static auto constexpr SpeedK = size_t{ 1000 };
static auto constexpr SpeedKStr = SPEED_K_STR;
static char constexpr SpeedMStr[] = "MB/s";
static char constexpr SpeedGStr[] = "GB/s";
static char constexpr SpeedTStr[] = "TB/s";
/***
****
@ -237,153 +252,136 @@ enum
TAG_TRACKERS
};
static char const* getUsage(void)
{
// clang-format off
return
MY_NAME " " LONG_VERSION_STRING "\n"
"A fast and easy BitTorrent client\n"
"https://transmissionbt.com/\n"
"\n"
"Usage: " MY_NAME " [host] [options]\n"
" " MY_NAME " [port] [options]\n"
" " MY_NAME " [host:port] [options]\n"
" " MY_NAME " [http(s?)://host:port/transmission/] [options]\n"
"\n"
"See the man page for detailed explanations and many examples.";
// clang-format on
}
/***
****
**** Command-Line Arguments
****
***/
static tr_option opts[] = {
{ 'a', "add", "Add torrent files by filename or URL", "a", false, nullptr },
{ 970, "alt-speed", "Use the alternate Limits", "as", false, nullptr },
{ 971, "no-alt-speed", "Don't use the alternate Limits", "AS", false, nullptr },
{ 972, "alt-speed-downlimit", "max alternate download speed (in " SPEED_K_STR ")", "asd", true, "<speed>" },
{ 973, "alt-speed-uplimit", "max alternate upload speed (in " SPEED_K_STR ")", "asu", true, "<speed>" },
{ 974, "alt-speed-scheduler", "Use the scheduled on/off times", "asc", false, nullptr },
{ 975, "no-alt-speed-scheduler", "Don't use the scheduled on/off times", "ASC", false, nullptr },
{ 976, "alt-speed-time-begin", "Time to start using the alt speed limits (in hhmm)", nullptr, true, "<time>" },
{ 977, "alt-speed-time-end", "Time to stop using the alt speed limits (in hhmm)", nullptr, true, "<time>" },
{ 978, "alt-speed-days", "Numbers for any/all days of the week - eg. \"1-7\"", nullptr, true, "<days>" },
{ 963, "blocklist-update", "Blocklist update", nullptr, false, nullptr },
{ 'c', "incomplete-dir", "Where to store new torrents until they're complete", "c", true, "<dir>" },
{ 'C', "no-incomplete-dir", "Don't store incomplete torrents in a different location", "C", false, nullptr },
{ 'b', "debug", "Print debugging information", "b", false, nullptr },
{ 'd',
"downlimit",
"Set the max download speed in " SPEED_K_STR " for the current torrent(s) or globally",
"d",
true,
"<speed>" },
{ 'D', "no-downlimit", "Disable max download speed for the current torrent(s) or globally", "D", false, nullptr },
{ 'e', "cache", "Set the maximum size of the session's memory cache (in " MEM_M_STR ")", "e", true, "<size>" },
{ 910, "encryption-required", "Encrypt all peer connections", "er", false, nullptr },
{ 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", false, nullptr },
{ 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", false, nullptr },
{ 850, "exit", "Tell the transmission session to shut down", nullptr, false, nullptr },
{ 940, "files", "List the current torrent(s)' files", "f", false, nullptr },
{ 'g', "get", "Mark files for download", "g", true, "<files>" },
{ 'G', "no-get", "Mark files for not downloading", "G", true, "<files>" },
{ 'i', "info", "Show the current torrent(s)' details", "i", false, nullptr },
{ 940, "info-files", "List the current torrent(s)' files", "if", false, nullptr },
{ 941, "info-peers", "List the current torrent(s)' peers", "ip", false, nullptr },
{ 942, "info-pieces", "List the current torrent(s)' pieces", "ic", false, nullptr },
{ 943, "info-trackers", "List the current torrent(s)' trackers", "it", false, nullptr },
{ 920, "session-info", "Show the session's details", "si", false, nullptr },
{ 921, "session-stats", "Show the session's statistics", "st", false, nullptr },
{ 'l', "list", "List all torrents", "l", false, nullptr },
{ 'L', "labels", "Set the current torrents' labels", "L", true, "<label[,label...]>" },
{ 960, "move", "Move current torrent's data to a new folder", nullptr, true, "<path>" },
{ 961, "find", "Tell Transmission where to find a torrent's data", nullptr, true, "<path>" },
{ 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", false, nullptr },
{ 'M', "no-portmap", "Disable portmapping", "M", false, nullptr },
{ 'n', "auth", "Set username and password", "n", true, "<user:pw>" },
{ 810, "authenv", "Set authentication info from the TR_AUTH environment variable (user:pw)", "ne", false, nullptr },
{ 'N', "netrc", "Set authentication info from a .netrc file", "N", true, "<file>" },
{ 820, "ssl", "Use SSL when talking to daemon", nullptr, false, nullptr },
{ 'o', "dht", "Enable distributed hash tables (DHT)", "o", false, nullptr },
{ 'O', "no-dht", "Disable distributed hash tables (DHT)", "O", false, nullptr },
{ 'p', "port", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "p", true, "<port>" },
{ 962, "port-test", "Port testing", "pt", false, nullptr },
{ 'P', "random-port", "Random port for incoming peers", "P", false, nullptr },
{ 900, "priority-high", "Try to download these file(s) first", "ph", true, "<files>" },
{ 901, "priority-normal", "Try to download these file(s) normally", "pn", true, "<files>" },
{ 902, "priority-low", "Try to download these file(s) last", "pl", true, "<files>" },
{ 700, "bandwidth-high", "Give this torrent first chance at available bandwidth", "Bh", false, nullptr },
{ 701, "bandwidth-normal", "Give this torrent bandwidth left over by high priority torrents", "Bn", false, nullptr },
{ 702,
"bandwidth-low",
"Give this torrent bandwidth left over by high and normal priority torrents",
"Bl",
false,
nullptr },
{ 600, "reannounce", "Reannounce the current torrent(s)", nullptr, false, nullptr },
{ 'r', "remove", "Remove the current torrent(s)", "r", false, nullptr },
{ 930, "peers", "Set the maximum number of peers for the current torrent(s) or globally", "pr", true, "<max>" },
{ 840, "remove-and-delete", "Remove the current torrent(s) and delete local data", "rad", false, nullptr },
{ 800, "torrent-done-script", "Specify a script to run when a torrent finishes", nullptr, true, "<file>" },
{ 801, "no-torrent-done-script", "Don't run a script when torrents finish", nullptr, false, nullptr },
{ 950, "seedratio", "Let the current torrent(s) seed until a specific ratio", "sr", true, "ratio" },
{ 951, "seedratio-default", "Let the current torrent(s) use the global seedratio settings", "srd", false, nullptr },
{ 952, "no-seedratio", "Let the current torrent(s) seed regardless of ratio", "SR", false, nullptr },
{ 953,
"global-seedratio",
"All torrents, unless overridden by a per-torrent setting, should seed until a specific ratio",
"gsr",
true,
"ratio" },
{ 954,
"no-global-seedratio",
"All torrents, unless overridden by a per-torrent setting, should seed regardless of ratio",
"GSR",
false,
nullptr },
{ 710, "tracker-add", "Add a tracker to a torrent", "td", true, "<tracker>" },
{ 712, "tracker-remove", "Remove a tracker from a torrent", "tr", true, "<trackerId>" },
{ 's', "start", "Start the current torrent(s)", "s", false, nullptr },
{ 'S', "stop", "Stop the current torrent(s)", "S", false, nullptr },
{ 't', "torrent", "Set the current torrent(s)", "t", true, "<torrent>" },
{ 990, "start-paused", "Start added torrents paused", nullptr, false, nullptr },
{ 991, "no-start-paused", "Start added torrents unpaused", nullptr, false, nullptr },
{ 992, "trash-torrent", "Delete torrents after adding", nullptr, false, nullptr },
{ 993, "no-trash-torrent", "Do not delete torrents after adding", nullptr, false, nullptr },
{ 984, "honor-session", "Make the current torrent(s) honor the session limits", "hl", false, nullptr },
{ 985, "no-honor-session", "Make the current torrent(s) not honor the session limits", "HL", false, nullptr },
{ 'u',
"uplimit",
"Set the max upload speed in " SPEED_K_STR " for the current torrent(s) or globally",
"u",
true,
"<speed>" },
{ 'U', "no-uplimit", "Disable max upload speed for the current torrent(s) or globally", "U", false, nullptr },
{ 830, "utp", "Enable uTP for peer connections", nullptr, false, nullptr },
{ 831, "no-utp", "Disable uTP for peer connections", nullptr, false, nullptr },
{ 'v', "verify", "Verify the current torrent(s)", "v", false, nullptr },
{ 'V', "version", "Show version number and exit", "V", false, nullptr },
{ 'w',
"download-dir",
"When used in conjunction with --add, set the new torrent's download folder. "
"Otherwise, set the default download folder",
"w",
true,
"<path>" },
{ 'x', "pex", "Enable peer exchange (PEX)", "x", false, nullptr },
{ 'X', "no-pex", "Disable peer exchange (PEX)", "X", false, nullptr },
{ 'y', "lpd", "Enable local peer discovery (LPD)", "y", false, nullptr },
{ 'Y', "no-lpd", "Disable local peer discovery (LPD)", "Y", false, nullptr },
{ 941, "peer-info", "List the current torrent(s)' peers", "pi", false, nullptr },
{ 0, nullptr, nullptr, nullptr, false, nullptr }
static auto constexpr Options = std::array<tr_option, 87>{
{ { 'a', "add", "Add torrent files by filename or URL", "a", false, nullptr },
{ 970, "alt-speed", "Use the alternate Limits", "as", false, nullptr },
{ 971, "no-alt-speed", "Don't use the alternate Limits", "AS", false, nullptr },
{ 972, "alt-speed-downlimit", "max alternate download speed (in " SPEED_K_STR ")", "asd", true, "<speed>" },
{ 973, "alt-speed-uplimit", "max alternate upload speed (in " SPEED_K_STR ")", "asu", true, "<speed>" },
{ 974, "alt-speed-scheduler", "Use the scheduled on/off times", "asc", false, nullptr },
{ 975, "no-alt-speed-scheduler", "Don't use the scheduled on/off times", "ASC", false, nullptr },
{ 976, "alt-speed-time-begin", "Time to start using the alt speed limits (in hhmm)", nullptr, true, "<time>" },
{ 977, "alt-speed-time-end", "Time to stop using the alt speed limits (in hhmm)", nullptr, true, "<time>" },
{ 978, "alt-speed-days", "Numbers for any/all days of the week - eg. \"1-7\"", nullptr, true, "<days>" },
{ 963, "blocklist-update", "Blocklist update", nullptr, false, nullptr },
{ 'c', "incomplete-dir", "Where to store new torrents until they're complete", "c", true, "<dir>" },
{ 'C', "no-incomplete-dir", "Don't store incomplete torrents in a different location", "C", false, nullptr },
{ 'b', "debug", "Print debugging information", "b", false, nullptr },
{ 'd',
"downlimit",
"Set the max download speed in " SPEED_K_STR " for the current torrent(s) or globally",
"d",
true,
"<speed>" },
{ 'D', "no-downlimit", "Disable max download speed for the current torrent(s) or globally", "D", false, nullptr },
{ 'e', "cache", "Set the maximum size of the session's memory cache (in " MEM_M_STR ")", "e", true, "<size>" },
{ 910, "encryption-required", "Encrypt all peer connections", "er", false, nullptr },
{ 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", false, nullptr },
{ 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", false, nullptr },
{ 850, "exit", "Tell the transmission session to shut down", nullptr, false, nullptr },
{ 940, "files", "List the current torrent(s)' files", "f", false, nullptr },
{ 'g', "get", "Mark files for download", "g", true, "<files>" },
{ 'G', "no-get", "Mark files for not downloading", "G", true, "<files>" },
{ 'i', "info", "Show the current torrent(s)' details", "i", false, nullptr },
{ 940, "info-files", "List the current torrent(s)' files", "if", false, nullptr },
{ 941, "info-peers", "List the current torrent(s)' peers", "ip", false, nullptr },
{ 942, "info-pieces", "List the current torrent(s)' pieces", "ic", false, nullptr },
{ 943, "info-trackers", "List the current torrent(s)' trackers", "it", false, nullptr },
{ 920, "session-info", "Show the session's details", "si", false, nullptr },
{ 921, "session-stats", "Show the session's statistics", "st", false, nullptr },
{ 'l', "list", "List all torrents", "l", false, nullptr },
{ 'L', "labels", "Set the current torrents' labels", "L", true, "<label[,label...]>" },
{ 960, "move", "Move current torrent's data to a new folder", nullptr, true, "<path>" },
{ 961, "find", "Tell Transmission where to find a torrent's data", nullptr, true, "<path>" },
{ 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", false, nullptr },
{ 'M', "no-portmap", "Disable portmapping", "M", false, nullptr },
{ 'n', "auth", "Set username and password", "n", true, "<user:pw>" },
{ 810, "authenv", "Set authentication info from the TR_AUTH environment variable (user:pw)", "ne", false, nullptr },
{ 'N', "netrc", "Set authentication info from a .netrc file", "N", true, "<file>" },
{ 820, "ssl", "Use SSL when talking to daemon", nullptr, false, nullptr },
{ 'o', "dht", "Enable distributed hash tables (DHT)", "o", false, nullptr },
{ 'O', "no-dht", "Disable distributed hash tables (DHT)", "O", false, nullptr },
{ 'p', "port", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "p", true, "<port>" },
{ 962, "port-test", "Port testing", "pt", false, nullptr },
{ 'P', "random-port", "Random port for incoming peers", "P", false, nullptr },
{ 900, "priority-high", "Try to download these file(s) first", "ph", true, "<files>" },
{ 901, "priority-normal", "Try to download these file(s) normally", "pn", true, "<files>" },
{ 902, "priority-low", "Try to download these file(s) last", "pl", true, "<files>" },
{ 700, "bandwidth-high", "Give this torrent first chance at available bandwidth", "Bh", false, nullptr },
{ 701, "bandwidth-normal", "Give this torrent bandwidth left over by high priority torrents", "Bn", false, nullptr },
{ 702,
"bandwidth-low",
"Give this torrent bandwidth left over by high and normal priority torrents",
"Bl",
false,
nullptr },
{ 600, "reannounce", "Reannounce the current torrent(s)", nullptr, false, nullptr },
{ 'r', "remove", "Remove the current torrent(s)", "r", false, nullptr },
{ 930, "peers", "Set the maximum number of peers for the current torrent(s) or globally", "pr", true, "<max>" },
{ 840, "remove-and-delete", "Remove the current torrent(s) and delete local data", "rad", false, nullptr },
{ 800, "torrent-done-script", "Specify a script to run when a torrent finishes", nullptr, true, "<file>" },
{ 801, "no-torrent-done-script", "Don't run a script when torrents finish", nullptr, false, nullptr },
{ 950, "seedratio", "Let the current torrent(s) seed until a specific ratio", "sr", true, "ratio" },
{ 951, "seedratio-default", "Let the current torrent(s) use the global seedratio settings", "srd", false, nullptr },
{ 952, "no-seedratio", "Let the current torrent(s) seed regardless of ratio", "SR", false, nullptr },
{ 953,
"global-seedratio",
"All torrents, unless overridden by a per-torrent setting, should seed until a specific ratio",
"gsr",
true,
"ratio" },
{ 954,
"no-global-seedratio",
"All torrents, unless overridden by a per-torrent setting, should seed regardless of ratio",
"GSR",
false,
nullptr },
{ 710, "tracker-add", "Add a tracker to a torrent", "td", true, "<tracker>" },
{ 712, "tracker-remove", "Remove a tracker from a torrent", "tr", true, "<trackerId>" },
{ 's', "start", "Start the current torrent(s)", "s", false, nullptr },
{ 'S', "stop", "Stop the current torrent(s)", "S", false, nullptr },
{ 't', "torrent", "Set the current torrent(s)", "t", true, "<torrent>" },
{ 990, "start-paused", "Start added torrents paused", nullptr, false, nullptr },
{ 991, "no-start-paused", "Start added torrents unpaused", nullptr, false, nullptr },
{ 992, "trash-torrent", "Delete torrents after adding", nullptr, false, nullptr },
{ 993, "no-trash-torrent", "Do not delete torrents after adding", nullptr, false, nullptr },
{ 984, "honor-session", "Make the current torrent(s) honor the session limits", "hl", false, nullptr },
{ 985, "no-honor-session", "Make the current torrent(s) not honor the session limits", "HL", false, nullptr },
{ 'u',
"uplimit",
"Set the max upload speed in " SPEED_K_STR " for the current torrent(s) or globally",
"u",
true,
"<speed>" },
{ 'U', "no-uplimit", "Disable max upload speed for the current torrent(s) or globally", "U", false, nullptr },
{ 830, "utp", "Enable uTP for peer connections", nullptr, false, nullptr },
{ 831, "no-utp", "Disable uTP for peer connections", nullptr, false, nullptr },
{ 'v', "verify", "Verify the current torrent(s)", "v", false, nullptr },
{ 'V', "version", "Show version number and exit", "V", false, nullptr },
{ 'w',
"download-dir",
"When used in conjunction with --add, set the new torrent's download folder. "
"Otherwise, set the default download folder",
"w",
true,
"<path>" },
{ 'x', "pex", "Enable peer exchange (PEX)", "x", false, nullptr },
{ 'X', "no-pex", "Disable peer exchange (PEX)", "X", false, nullptr },
{ 'y', "lpd", "Enable local peer discovery (LPD)", "y", false, nullptr },
{ 'Y', "no-lpd", "Disable local peer discovery (LPD)", "Y", false, nullptr },
{ 941, "peer-info", "List the current torrent(s)' peers", "pi", false, nullptr },
{ 0, nullptr, nullptr, nullptr, false, nullptr } }
};
static void showUsage(void)
{
tr_getopt_usage(MY_NAME, getUsage(), opts);
tr_getopt_usage(MyName, Usage, std::data(Options));
}
static int numarg(char const* arg)
@ -2041,7 +2039,7 @@ static int processResponse(char const* rpcurl, std::string_view response)
if (!tr_variantFromBuf(&top, TR_VARIANT_PARSE_JSON | TR_VARIANT_PARSE_INPLACE, response))
{
tr_logAddNamedError(MY_NAME, "Unable to parse response \"%" TR_PRIsv "\"", TR_PRIsv_ARG(response));
tr_logAddNamedError(MyName, "Unable to parse response \"%" TR_PRIsv "\"", TR_PRIsv_ARG(response));
status |= EXIT_FAILURE;
}
else
@ -2103,7 +2101,7 @@ static int processResponse(char const* rpcurl, std::string_view response)
int64_t i;
tr_variant* b = &top;
if (tr_variantDictFindDict(&top, ARGUMENTS, &b) &&
if (tr_variantDictFindDict(&top, Arguments, &b) &&
tr_variantDictFindDict(b, TR_KEY_torrent_added, &b) && tr_variantDictFindInt(b, TR_KEY_id, &i))
{
tr_snprintf(id, sizeof(id), "%" PRId64, i);
@ -2142,7 +2140,7 @@ static int processResponse(char const* rpcurl, std::string_view response)
static CURL* tr_curl_easy_init(struct evbuffer* writebuf)
{
CURL* curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_USERAGENT, MY_NAME "/" LONG_VERSION_STRING);
curl_easy_setopt(curl, CURLOPT_USERAGENT, tr_strvJoin(MyName, "/", LONG_VERSION_STRING).c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunc);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, writebuf);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, parseResponseHeader);
@ -2214,7 +2212,7 @@ static int flush(char const* rpcurl, tr_variant** benc)
if ((res = curl_easy_perform(curl)) != CURLE_OK)
{
tr_logAddNamedError(MY_NAME, " (%s) %s", rpcurl_http.c_str(), curl_easy_strerror(res));
tr_logAddNamedError(MyName, " (%s) %s", rpcurl_http.c_str(), curl_easy_strerror(res));
status |= EXIT_FAILURE;
}
else
@ -2273,14 +2271,14 @@ static tr_variant* ensure_sset(tr_variant** sset)
if (*sset != nullptr)
{
args = tr_variantDictFind(*sset, ARGUMENTS);
args = tr_variantDictFind(*sset, Arguments);
}
else
{
*sset = tr_new0(tr_variant, 1);
tr_variantInitDict(*sset, 3);
tr_variantDictAddStrView(*sset, TR_KEY_method, "session-set"sv);
args = tr_variantDictAddDict(*sset, ARGUMENTS, 0);
args = tr_variantDictAddDict(*sset, Arguments, 0);
}
return args;
@ -2292,14 +2290,14 @@ static tr_variant* ensure_tset(tr_variant** tset)
if (*tset != nullptr)
{
args = tr_variantDictFind(*tset, ARGUMENTS);
args = tr_variantDictFind(*tset, Arguments);
}
else
{
*tset = tr_new0(tr_variant, 1);
tr_variantInitDict(*tset, 3);
tr_variantDictAddStrView(*tset, TR_KEY_method, "torrent-set"sv);
args = tr_variantDictAddDict(*tset, ARGUMENTS, 1);
args = tr_variantDictAddDict(*tset, Arguments, 1);
}
return args;
@ -2316,7 +2314,7 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv)
*id = '\0';
while ((c = tr_getopt(getUsage(), argc, argv, opts, &optarg)) != TR_OPT_DONE)
while ((c = tr_getopt(Usage, argc, argv, std::data(Options), &optarg)) != TR_OPT_DONE)
{
int const stepMode = getOptMode(c);
@ -2337,7 +2335,7 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv)
if (tset != nullptr)
{
addIdArg(tr_variantDictFind(tset, ARGUMENTS), id, nullptr);
addIdArg(tr_variantDictFind(tset, Arguments), id, nullptr);
status |= flush(rpcurl, &tset);
}
@ -2345,7 +2343,7 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv)
tr_variantInitDict(tadd, 3);
tr_variantDictAddStrView(tadd, TR_KEY_method, "torrent-add"sv);
tr_variantDictAddInt(tadd, TR_KEY_tag, TAG_TORRENT_ADD);
tr_variantDictAddDict(tadd, ARGUMENTS, 0);
tr_variantDictAddDict(tadd, Arguments, 0);
break;
case 'b': /* debug */
@ -2383,7 +2381,7 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv)
if (tset != nullptr)
{
addIdArg(tr_variantDictFind(tset, ARGUMENTS), id, nullptr);
addIdArg(tr_variantDictFind(tset, Arguments), id, nullptr);
status |= flush(rpcurl, &tset);
}
@ -2391,7 +2389,7 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv)
break;
case 'V': /* show version number */
fprintf(stderr, "%s %s\n", MY_NAME, LONG_VERSION_STRING);
fprintf(stderr, "%s %s\n", MyName, LONG_VERSION_STRING);
exit(0);
case TR_OPT_ERR:
@ -2403,7 +2401,7 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv)
case TR_OPT_UNK:
if (tadd != nullptr)
{
tr_variant* args = tr_variantDictFind(tadd, ARGUMENTS);
tr_variant* args = tr_variantDictFind(tadd, Arguments);
char* tmp = getEncodedMetainfo(optarg);
if (tmp != nullptr)
@ -2433,12 +2431,12 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv)
tr_variant* fields;
tr_variantInitDict(top, 3);
tr_variantDictAddStrView(top, TR_KEY_method, "torrent-get"sv);
args = tr_variantDictAddDict(top, ARGUMENTS, 0);
args = tr_variantDictAddDict(top, Arguments, 0);
fields = tr_variantDictAddList(args, TR_KEY_fields, 0);
if (tset != nullptr)
{
addIdArg(tr_variantDictFind(tset, ARGUMENTS), id, nullptr);
addIdArg(tr_variantDictFind(tset, Arguments), id, nullptr);
status |= flush(rpcurl, &tset);
}
@ -2794,7 +2792,7 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv)
if (tadd != nullptr)
{
args = tr_variantDictFind(tadd, ARGUMENTS);
args = tr_variantDictFind(tadd, Arguments);
}
else
{
@ -2855,7 +2853,7 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv)
{
if (tadd != nullptr)
{
tr_variant* args = tr_variantDictFind(tadd, ARGUMENTS);
tr_variant* args = tr_variantDictFind(tadd, Arguments);
tr_variantDictAddStr(args, TR_KEY_download_dir, optarg);
}
else
@ -2864,7 +2862,7 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv)
tr_variant* top = tr_new0(tr_variant, 1);
tr_variantInitDict(top, 2);
tr_variantDictAddStrView(top, TR_KEY_method, "torrent-set-location"sv);
args = tr_variantDictAddDict(top, ARGUMENTS, 3);
args = tr_variantDictAddDict(top, Arguments, 3);
tr_variantDictAddStr(args, TR_KEY_location, optarg);
tr_variantDictAddBool(args, TR_KEY_move, false);
addIdArg(args, id, nullptr);
@ -2897,7 +2895,7 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv)
tr_variant* top = tr_new0(tr_variant, 1);
tr_variantInitDict(top, 2);
tr_variantDictAddStrView(top, TR_KEY_method, "torrent-start"sv);
addIdArg(tr_variantDictAddDict(top, ARGUMENTS, 1), id, nullptr);
addIdArg(tr_variantDictAddDict(top, Arguments, 1), id, nullptr);
status |= flush(rpcurl, &top);
}
@ -2915,7 +2913,7 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv)
tr_variant* top = tr_new0(tr_variant, 1);
tr_variantInitDict(top, 2);
tr_variantDictAddStrView(top, TR_KEY_method, "torrent-stop"sv);
addIdArg(tr_variantDictAddDict(top, ARGUMENTS, 1), id, nullptr);
addIdArg(tr_variantDictAddDict(top, Arguments, 1), id, nullptr);
status |= flush(rpcurl, &top);
}
@ -2973,14 +2971,14 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv)
if (tset != nullptr)
{
addIdArg(tr_variantDictFind(tset, ARGUMENTS), id, nullptr);
addIdArg(tr_variantDictFind(tset, Arguments), id, nullptr);
status |= flush(rpcurl, &tset);
}
top = tr_new0(tr_variant, 1);
tr_variantInitDict(top, 2);
tr_variantDictAddStrView(top, TR_KEY_method, "torrent-reannounce"sv);
addIdArg(tr_variantDictAddDict(top, ARGUMENTS, 1), id, nullptr);
addIdArg(tr_variantDictAddDict(top, Arguments, 1), id, nullptr);
status |= flush(rpcurl, &top);
break;
}
@ -2991,14 +2989,14 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv)
if (tset != nullptr)
{
addIdArg(tr_variantDictFind(tset, ARGUMENTS), id, nullptr);
addIdArg(tr_variantDictFind(tset, Arguments), id, nullptr);
status |= flush(rpcurl, &tset);
}
top = tr_new0(tr_variant, 1);
tr_variantInitDict(top, 2);
tr_variantDictAddStrView(top, TR_KEY_method, "torrent-verify"sv);
addIdArg(tr_variantDictAddDict(top, ARGUMENTS, 1), id, nullptr);
addIdArg(tr_variantDictAddDict(top, Arguments, 1), id, nullptr);
status |= flush(rpcurl, &top);
break;
}
@ -3010,7 +3008,7 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv)
tr_variant* top = tr_new0(tr_variant, 1);
tr_variantInitDict(top, 2);
tr_variantDictAddStrView(top, TR_KEY_method, "torrent-remove"sv);
args = tr_variantDictAddDict(top, ARGUMENTS, 2);
args = tr_variantDictAddDict(top, Arguments, 2);
tr_variantDictAddBool(args, TR_KEY_delete_local_data, c == 840);
addIdArg(args, id, nullptr);
status |= flush(rpcurl, &top);
@ -3023,7 +3021,7 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv)
tr_variant* top = tr_new0(tr_variant, 1);
tr_variantInitDict(top, 2);
tr_variantDictAddStrView(top, TR_KEY_method, "torrent-set-location"sv);
args = tr_variantDictAddDict(top, ARGUMENTS, 3);
args = tr_variantDictAddDict(top, Arguments, 3);
tr_variantDictAddStr(args, TR_KEY_location, optarg);
tr_variantDictAddBool(args, TR_KEY_move, true);
addIdArg(args, id, nullptr);
@ -3048,7 +3046,7 @@ static int processArgs(char const* rpcurl, int argc, char const* const* argv)
if (tset != nullptr)
{
addIdArg(tr_variantDictFind(tset, ARGUMENTS), id, nullptr);
addIdArg(tr_variantDictFind(tset, Arguments), id, nullptr);
status |= flush(rpcurl, &tset);
}
@ -3146,9 +3144,9 @@ int tr_main(int argc, char* argv[])
return EXIT_FAILURE;
}
tr_formatter_mem_init(MEM_K, MEM_K_STR, MEM_M_STR, MEM_G_STR, MEM_T_STR);
tr_formatter_size_init(DISK_K, DISK_K_STR, DISK_M_STR, DISK_G_STR, DISK_T_STR);
tr_formatter_speed_init(SPEED_K, SPEED_K_STR, SPEED_M_STR, SPEED_G_STR, SPEED_T_STR);
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);
getHostAndPortAndRpcUrl(&argc, argv, &host, &port, &rpcurl);
@ -3159,7 +3157,7 @@ int tr_main(int argc, char* argv[])
if (std::empty(rpcurl))
{
rpcurl = tr_strvJoin(host, ":", std::to_string(port), DEFAULT_URL);
rpcurl = tr_strvJoin(host, ":", std::to_string(port), DefaultUrl);
}
return processArgs(rpcurl.c_str(), argc, (char const* const*)argv);

View File

@ -6,6 +6,7 @@
*
*/
#include <array>
#include <stdio.h> /* fprintf() */
#include <string.h> /* strcmp(), strchr(), memcmp() */
#include <stdlib.h> /* qsort() */
@ -24,23 +25,20 @@
#include "units.h"
#define MY_NAME "transmission-show"
#define TIMEOUT_SECS 30
using namespace std::literals;
static tr_option options[] = {
{ 'm', "magnet", "Give a magnet link for the specified torrent", "m", false, nullptr },
{ 's', "scrape", "Ask the torrent's trackers how many peers are in the torrent's swarm", "s", false, nullptr },
{ 'u', "unsorted", "Do not sort files by name", "u", false, nullptr },
{ 'V', "version", "Show version number and exit", "V", false, nullptr },
{ 0, nullptr, nullptr, nullptr, false, nullptr }
};
static char constexpr MyName[] = "transmission-show";
static char constexpr Usage[] = "Usage: transmission-show [options] <.torrent file>";
static char const* getUsage(void)
{
return "Usage: " MY_NAME " [options] <.torrent file>";
}
static auto constexpr TimeoutSecs = long{ 30 };
static auto constexpr Options = std::array<tr_option, 5>{
{ { 'm', "magnet", "Give a magnet link for the specified torrent", "m", false, nullptr },
{ 's', "scrape", "Ask the torrent's trackers how many peers are in the torrent's swarm", "s", false, nullptr },
{ 'u', "unsorted", "Do not sort files by name", "u", false, nullptr },
{ 'V', "version", "Show version number and exit", "V", false, nullptr },
{ 0, nullptr, nullptr, nullptr, false, nullptr } }
};
static bool magnetFlag = false;
static bool scrapeFlag = false;
@ -53,7 +51,7 @@ static int parseCommandLine(int argc, char const* const* argv)
int c;
char const* optarg;
while ((c = tr_getopt(getUsage(), argc, argv, options, &optarg)) != TR_OPT_DONE)
while ((c = tr_getopt(Usage, argc, argv, std::data(Options), &optarg)) != TR_OPT_DONE)
{
switch (c)
{
@ -207,7 +205,7 @@ static size_t writeFunc(void* ptr, size_t size, size_t nmemb, void* vbuf)
static CURL* tr_curl_easy_init(struct evbuffer* writebuf)
{
CURL* curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_USERAGENT, MY_NAME "/" LONG_VERSION_STRING);
curl_easy_setopt(curl, CURLOPT_USERAGENT, tr_strvJoin(MyName, "/", LONG_VERSION_STRING).c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunc);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, writebuf);
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
@ -236,7 +234,7 @@ static void doScrape(tr_info const* inf)
auto* const buf = evbuffer_new();
auto* const curl = tr_curl_easy_init(buf);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_TIMEOUT, TIMEOUT_SECS);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, TimeoutSecs);
auto const res = curl_easy_perform(curl);
if (res != CURLE_OK)
@ -324,7 +322,7 @@ int tr_main(int argc, char* argv[])
if (showVersion)
{
fprintf(stderr, "%s %s\n", MY_NAME, LONG_VERSION_STRING);
fprintf(stderr, "%s %s\n", MyName, LONG_VERSION_STRING);
return EXIT_SUCCESS;
}
@ -332,7 +330,7 @@ int tr_main(int argc, char* argv[])
if (filename == nullptr)
{
fprintf(stderr, "ERROR: No .torrent file specified.\n");
tr_getopt_usage(MY_NAME, getUsage(), options);
tr_getopt_usage(MyName, Usage, std::data(Options));
fprintf(stderr, "\n");
return EXIT_FAILURE;
}