feat: allow optional arguments in tr_getopt() (#7510)

* feat: allow optional arguments in `tr_getopt()`

* test: new tests for optional arg

* code review: `using Arg = tr_option::Arg`

* refactor: static assert option array size

* test: add new tests for missing arguments in the middle

* test: static auto constexpr
This commit is contained in:
Yat Ho
2025-10-16 01:08:11 +08:00
committed by GitHub
parent a7a5bc38ad
commit 9c14fa58d8
10 changed files with 536 additions and 402 deletions

View File

@@ -42,19 +42,24 @@ char constexpr Usage[] = "Usage: transmission-create [options] <file|directory>"
uint32_t constexpr KiB = 1024;
auto constexpr Options = std::array<tr_option, 10>{
{ { '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>" },
{ 'w', "webseed", "Add a webseed URL", "w", true, "<url>" },
{ 'x', "anonymize", R"(Omit "Creation date" and "Created by" info)", nullptr, false, nullptr },
{ 'V', "version", "Show version number and exit", "V", false, nullptr },
{ 0, nullptr, nullptr, nullptr, false, nullptr } }
};
using Arg = tr_option::Arg;
auto constexpr Options = std::array<tr_option, 10>{ {
{ 'p', "private", "Allow this torrent to only be used with the specified tracker(s)", "p", Arg::None, nullptr },
{ 'r', "source", "Set the source for private trackers", "r", Arg::Required, "<source>" },
{ 'o', "outfile", "Save the generated .torrent to this filename", "o", Arg::Required, "<file>" },
{ 's', "piecesize", "Set the piece size in KiB, overriding the preferred default", "s", Arg::Required, "<KiB>" },
{ 'c', "comment", "Add a comment", "c", Arg::Required, "<comment>" },
{ 't', "tracker", "Add a tracker's announce URL", "t", Arg::Required, "<url>" },
{ 'w', "webseed", "Add a webseed URL", "w", Arg::Required, "<url>" },
{ 'x', "anonymize", R"(Omit "Creation date" and "Created by" info)", nullptr, Arg::None, nullptr },
{ 'V', "version", "Show version number and exit", "V", Arg::None, nullptr },
{ 0, nullptr, nullptr, nullptr, Arg::None, nullptr },
} };
static_assert(Options[std::size(Options) - 2].val != 0);
} // namespace
namespace
{
struct app_options
{
tr_announce_list trackers;

View File

@@ -35,15 +35,20 @@ struct app_options
bool show_version = false;
};
auto constexpr Options = std::array<tr_option, 6>{
{ { '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>" },
{ 's', "source", "Set the source", "s", true, "<source>" },
{ 'V', "version", "Show version number and exit", "V", false, nullptr },
{ 0, nullptr, nullptr, nullptr, false, nullptr } }
};
using Arg = tr_option::Arg;
auto constexpr Options = std::array<tr_option, 6>{ {
{ 'a', "add", "Add a tracker's announce URL", "a", Arg::Required, "<url>" },
{ 'd', "delete", "Delete a tracker's announce URL", "d", Arg::Required, "<url>" },
{ 'r', "replace", "Search and replace a substring in the announce URLs", "r", Arg::Required, "<old> <new>" },
{ 's', "source", "Set the source", "s", Arg::Required, "<source>" },
{ 'V', "version", "Show version number and exit", "V", Arg::None, nullptr },
{ 0, nullptr, nullptr, nullptr, Arg::None, nullptr },
} };
static_assert(Options[std::size(Options) - 2].val != 0);
} // namespace
namespace
{
int parseCommandLine(app_options& opts, int argc, char const* const* argv)
{
int c;

View File

@@ -211,166 +211,170 @@ enum
// --- Command-Line Arguments
auto constexpr Options = std::array<tr_option, 105>{
{ { '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 },
{ 730, "bandwidth-group", "Set the current torrents' bandwidth group", "bwg", true, "<group>" },
{ 731, "no-bandwidth-group", "Reset the current torrents' bandwidth group", "nwg", false, nullptr },
{ 732, "list-groups", "Show bandwidth groups with their parameters", "lg", 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 },
{ 'F', "filter", "Filter the current torrent(s)", "F", true, "criterion" },
{ '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 },
{ 944, "print-ids", "Print the current torrent(s)' ids", "ids", 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 },
{ 'j', "json", "Return RPC response as a JSON string", "j", 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>" },
{ 968, "unix-socket", "Use a Unix domain socket", nullptr, true, "<path>" },
{ 961, "find", "Tell Transmission where to find a torrent's data", nullptr, true, "<path>" },
{ 964, "rename", "Rename torrents root folder or a file", nullptr, true, "<name>" },
{ 965, "path", "Provide path for rename functions", 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", "A script to run when a torrent finishes downloading", nullptr, true, "<file>" },
{ 801, "no-torrent-done-script", "Don't run the done-downloading script", nullptr, false, nullptr },
{ 802, "torrent-done-seeding-script", "A script to run when a torrent finishes seeding", nullptr, true, "<file>" },
{ 803, "no-torrent-done-seeding-script", "Don't run the done-seeding script", 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 },
{ 955,
"idle-seeding-limit",
"Let the current torrent(s) seed until a specific amount idle time",
"isl",
true,
"<minutes>" },
{ 956,
"default-idle-seeding-limit",
"Let the current torrent(s) use the default idle seeding settings",
"isld",
false,
nullptr },
{ 957, "no-idle-seeding-limit", "Let the current torrent(s) seed regardless of idle time", "ISL", false, nullptr },
{ 958,
"global-idle-seeding-limit",
"All torrents, unless overridden by a per-torrent setting, should seed until a specific amount of idle time",
"gisl",
true,
"<minutes>" },
{ 959,
"no-global-idle-seeding-limit",
"All torrents, unless overridden by a per-torrent setting, should seed regardless of idle time",
"GISL",
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 },
{ 994, "sequential-download", "Download the torrent sequentially", "seq", false, nullptr },
{ 995, "no-sequential-download", "Download the torrent normally", "SEQ", 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 µTP for peer connections", nullptr, false, nullptr },
{ 831, "no-utp", "Disable µTP 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 } }
};
using Arg = tr_option::Arg;
auto constexpr Options = std::array<tr_option, 105>{ {
{ 'a', "add", "Add torrent files by filename or URL", "a", Arg::None, nullptr },
{ 970, "alt-speed", "Use the alternate Limits", "as", Arg::None, nullptr },
{ 971, "no-alt-speed", "Don't use the alternate Limits", "AS", Arg::None, nullptr },
{ 972, "alt-speed-downlimit", "max alternate download speed (in " SPEED_K_STR ")", "asd", Arg::Required, "<speed>" },
{ 973, "alt-speed-uplimit", "max alternate upload speed (in " SPEED_K_STR ")", "asu", Arg::Required, "<speed>" },
{ 974, "alt-speed-scheduler", "Use the scheduled on/off times", "asc", Arg::None, nullptr },
{ 975, "no-alt-speed-scheduler", "Don't use the scheduled on/off times", "ASC", Arg::None, nullptr },
{ 976, "alt-speed-time-begin", "Time to start using the alt speed limits (in hhmm)", nullptr, Arg::Required, "<time>" },
{ 977, "alt-speed-time-end", "Time to stop using the alt speed limits (in hhmm)", nullptr, Arg::Required, "<time>" },
{ 978, "alt-speed-days", "Numbers for any/all days of the week - eg. \"1-7\"", nullptr, Arg::Required, "<days>" },
{ 963, "blocklist-update", "Blocklist update", nullptr, Arg::None, nullptr },
{ 'c', "incomplete-dir", "Where to store new torrents until they're complete", "c", Arg::Required, "<dir>" },
{ 'C', "no-incomplete-dir", "Don't store incomplete torrents in a different location", "C", Arg::None, nullptr },
{ 'b', "debug", "Print debugging information", "b", Arg::None, nullptr },
{ 730, "bandwidth-group", "Set the current torrents' bandwidth group", "bwg", Arg::Required, "<group>" },
{ 731, "no-bandwidth-group", "Reset the current torrents' bandwidth group", "nwg", Arg::None, nullptr },
{ 732, "list-groups", "Show bandwidth groups with their parameters", "lg", Arg::None, nullptr },
{ 'd',
"downlimit",
"Set the max download speed in " SPEED_K_STR " for the current torrent(s) or globally",
"d",
Arg::Required,
"<speed>" },
{ 'D', "no-downlimit", "Disable max download speed for the current torrent(s) or globally", "D", Arg::None, nullptr },
{ 'e', "cache", "Set the maximum size of the session's memory cache (in " MEM_M_STR ")", "e", Arg::Required, "<size>" },
{ 910, "encryption-required", "Encrypt all peer connections", "er", Arg::None, nullptr },
{ 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", Arg::None, nullptr },
{ 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", Arg::None, nullptr },
{ 850, "exit", "Tell the transmission session to shut down", nullptr, Arg::None, nullptr },
{ 940, "files", "List the current torrent(s)' files", "f", Arg::None, nullptr },
{ 'F', "filter", "Filter the current torrent(s)", "F", Arg::Required, "criterion" },
{ 'g', "get", "Mark files for download", "g", Arg::Required, "<files>" },
{ 'G', "no-get", "Mark files for not downloading", "G", Arg::Required, "<files>" },
{ 'i', "info", "Show the current torrent(s)' details", "i", Arg::None, nullptr },
{ 944, "print-ids", "Print the current torrent(s)' ids", "ids", Arg::None, nullptr },
{ 940, "info-files", "List the current torrent(s)' files", "if", Arg::None, nullptr },
{ 941, "info-peers", "List the current torrent(s)' peers", "ip", Arg::None, nullptr },
{ 942, "info-pieces", "List the current torrent(s)' pieces", "ic", Arg::None, nullptr },
{ 943, "info-trackers", "List the current torrent(s)' trackers", "it", Arg::None, nullptr },
{ 'j', "json", "Return RPC response as a JSON string", "j", Arg::None, nullptr },
{ 920, "session-info", "Show the session's details", "si", Arg::None, nullptr },
{ 921, "session-stats", "Show the session's statistics", "st", Arg::None, nullptr },
{ 'l', "list", "List all torrents", "l", Arg::None, nullptr },
{ 'L', "labels", "Set the current torrents' labels", "L", Arg::Required, "<label[,label...]>" },
{ 960, "move", "Move current torrent's data to a new folder", nullptr, Arg::Required, "<path>" },
{ 968, "unix-socket", "Use a Unix domain socket", nullptr, Arg::Required, "<path>" },
{ 961, "find", "Tell Transmission where to find a torrent's data", nullptr, Arg::Required, "<path>" },
{ 964, "rename", "Rename torrents root folder or a file", nullptr, Arg::Required, "<name>" },
{ 965, "path", "Provide path for rename functions", nullptr, Arg::Required, "<path>" },
{ 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", Arg::None, nullptr },
{ 'M', "no-portmap", "Disable portmapping", "M", Arg::None, nullptr },
{ 'n', "auth", "Set username and password", "n", Arg::Required, "<user:pw>" },
{ 810, "authenv", "Set authentication info from the TR_AUTH environment variable (user:pw)", "ne", Arg::None, nullptr },
{ 'N', "netrc", "Set authentication info from a .netrc file", "N", Arg::Required, "<file>" },
{ 820, "ssl", "Use SSL when talking to daemon", nullptr, Arg::None, nullptr },
{ 'o', "dht", "Enable distributed hash tables (DHT)", "o", Arg::None, nullptr },
{ 'O', "no-dht", "Disable distributed hash tables (DHT)", "O", Arg::None, nullptr },
{ 'p', "port", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "p", Arg::Required, "<port>" },
{ 962, "port-test", "Port testing", "pt", Arg::None, nullptr },
{ 'P', "random-port", "Random port for incoming peers", "P", Arg::None, nullptr },
{ 900, "priority-high", "Try to download these file(s) first", "ph", Arg::Required, "<files>" },
{ 901, "priority-normal", "Try to download these file(s) normally", "pn", Arg::Required, "<files>" },
{ 902, "priority-low", "Try to download these file(s) last", "pl", Arg::Required, "<files>" },
{ 700, "bandwidth-high", "Give this torrent first chance at available bandwidth", "Bh", Arg::None, nullptr },
{ 701, "bandwidth-normal", "Give this torrent bandwidth left over by high priority torrents", "Bn", Arg::None, nullptr },
{ 702,
"bandwidth-low",
"Give this torrent bandwidth left over by high and normal priority torrents",
"Bl",
Arg::None,
nullptr },
{ 600, "reannounce", "Reannounce the current torrent(s)", nullptr, Arg::None, nullptr },
{ 'r', "remove", "Remove the current torrent(s)", "r", Arg::None, nullptr },
{ 930, "peers", "Set the maximum number of peers for the current torrent(s) or globally", "pr", Arg::Required, "<max>" },
{ 840, "remove-and-delete", "Remove the current torrent(s) and delete local data", "rad", Arg::None, nullptr },
{ 800, "torrent-done-script", "A script to run when a torrent finishes downloading", nullptr, Arg::Required, "<file>" },
{ 801, "no-torrent-done-script", "Don't run the done-downloading script", nullptr, Arg::None, nullptr },
{ 802, "torrent-done-seeding-script", "A script to run when a torrent finishes seeding", nullptr, Arg::Required, "<file>" },
{ 803, "no-torrent-done-seeding-script", "Don't run the done-seeding script", nullptr, Arg::None, nullptr },
{ 950, "seedratio", "Let the current torrent(s) seed until a specific ratio", "sr", Arg::Required, "ratio" },
{ 951, "seedratio-default", "Let the current torrent(s) use the global seedratio settings", "srd", Arg::None, nullptr },
{ 952, "no-seedratio", "Let the current torrent(s) seed regardless of ratio", "SR", Arg::None, nullptr },
{ 953,
"global-seedratio",
"All torrents, unless overridden by a per-torrent setting, should seed until a specific ratio",
"gsr",
Arg::Required,
"ratio" },
{ 954,
"no-global-seedratio",
"All torrents, unless overridden by a per-torrent setting, should seed regardless of ratio",
"GSR",
Arg::None,
nullptr },
{ 955,
"idle-seeding-limit",
"Let the current torrent(s) seed until a specific amount idle time",
"isl",
Arg::Required,
"<minutes>" },
{ 956,
"default-idle-seeding-limit",
"Let the current torrent(s) use the default idle seeding settings",
"isld",
Arg::None,
nullptr },
{ 957, "no-idle-seeding-limit", "Let the current torrent(s) seed regardless of idle time", "ISL", Arg::None, nullptr },
{ 958,
"global-idle-seeding-limit",
"All torrents, unless overridden by a per-torrent setting, should seed until a specific amount of idle time",
"gisl",
Arg::Required,
"<minutes>" },
{ 959,
"no-global-idle-seeding-limit",
"All torrents, unless overridden by a per-torrent setting, should seed regardless of idle time",
"GISL",
Arg::None,
nullptr },
{ 710, "tracker-add", "Add a tracker to a torrent", "td", Arg::Required, "<tracker>" },
{ 712, "tracker-remove", "Remove a tracker from a torrent", "tr", Arg::Required, "<trackerId>" },
{ 's', "start", "Start the current torrent(s)", "s", Arg::None, nullptr },
{ 'S', "stop", "Stop the current torrent(s)", "S", Arg::None, nullptr },
{ 't', "torrent", "Set the current torrent(s)", "t", Arg::Required, "<torrent>" },
{ 990, "start-paused", "Start added torrents paused", nullptr, Arg::None, nullptr },
{ 991, "no-start-paused", "Start added torrents unpaused", nullptr, Arg::None, nullptr },
{ 992, "trash-torrent", "Delete torrents after adding", nullptr, Arg::None, nullptr },
{ 993, "no-trash-torrent", "Do not delete torrents after adding", nullptr, Arg::None, nullptr },
{ 994, "sequential-download", "Download the torrent sequentially", "seq", Arg::None, nullptr },
{ 995, "no-sequential-download", "Download the torrent normally", "SEQ", Arg::None, nullptr },
{ 984, "honor-session", "Make the current torrent(s) honor the session limits", "hl", Arg::None, nullptr },
{ 985, "no-honor-session", "Make the current torrent(s) not honor the session limits", "HL", Arg::None, nullptr },
{ 'u',
"uplimit",
"Set the max upload speed in " SPEED_K_STR " for the current torrent(s) or globally",
"u",
Arg::Required,
"<speed>" },
{ 'U', "no-uplimit", "Disable max upload speed for the current torrent(s) or globally", "U", Arg::None, nullptr },
{ 830, "utp", "Enable µTP for peer connections", nullptr, Arg::None, nullptr },
{ 831, "no-utp", "Disable µTP for peer connections", nullptr, Arg::None, nullptr },
{ 'v', "verify", "Verify the current torrent(s)", "v", Arg::None, nullptr },
{ 'V', "version", "Show version number and exit", "V", Arg::None, nullptr },
{ 'w',
"download-dir",
"When used in conjunction with --add, set the new torrent's download folder. "
"Otherwise, set the default download folder",
"w",
Arg::Required,
"<path>" },
{ 'x', "pex", "Enable peer exchange (PEX)", "x", Arg::None, nullptr },
{ 'X', "no-pex", "Disable peer exchange (PEX)", "X", Arg::None, nullptr },
{ 'y', "lpd", "Enable local peer discovery (LPD)", "y", Arg::None, nullptr },
{ 'Y', "no-lpd", "Disable local peer discovery (LPD)", "Y", Arg::None, nullptr },
{ 941, "peer-info", "List the current torrent(s)' peers", "pi", Arg::None, nullptr },
{ 0, nullptr, nullptr, nullptr, Arg::None, nullptr },
} };
static_assert(Options[std::size(Options) - 2].val != 0);
} // namespace
namespace
{
void show_usage()
{
tr_getopt_usage(MyName, Usage, std::data(Options));

View File

@@ -41,29 +41,33 @@ using namespace libtransmission::Values;
namespace
{
auto constexpr TimeoutSecs = std::chrono::seconds{ 30 };
char constexpr MyName[] = "transmission-show";
char constexpr Usage[] = "Usage: transmission-show [options] <torrent-file>";
auto options = std::array<tr_option, 14>{
{ { 'd', "header", "Show only header section", "d", false, nullptr },
{ 'i', "info", "Show only info section", "i", false, nullptr },
{ 't', "trackers", "Show only trackers section", "t", false, nullptr },
{ 'f', "files", "Show only file list", "f", false, nullptr },
{ 'D', "no-header", "Do not show header section", "D", false, nullptr },
{ 'I', "no-info", "Do not show info section", "I", false, nullptr },
{ 'T', "no-trackers", "Do not show trackers section", "T", false, nullptr },
{ 'F', "no-files", "Do not show files section", "F", false, nullptr },
{ 'b', "bytes", "Show file sizes in bytes", "b", false, nullptr },
{ '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 } }
};
using Arg = tr_option::Arg;
auto constexpr Options = std::array<tr_option, 14>{ {
{ 'd', "header", "Show only header section", "d", Arg::None, nullptr },
{ 'i', "info", "Show only info section", "i", Arg::None, nullptr },
{ 't', "trackers", "Show only trackers section", "t", Arg::None, nullptr },
{ 'f', "files", "Show only file list", "f", Arg::None, nullptr },
{ 'D', "no-header", "Do not show header section", "D", Arg::None, nullptr },
{ 'I', "no-info", "Do not show info section", "I", Arg::None, nullptr },
{ 'T', "no-trackers", "Do not show trackers section", "T", Arg::None, nullptr },
{ 'F', "no-files", "Do not show files section", "F", Arg::None, nullptr },
{ 'b', "bytes", "Show file sizes in bytes", "b", Arg::None, nullptr },
{ 'm', "magnet", "Give a magnet link for the specified torrent", "m", Arg::None, nullptr },
{ 's', "scrape", "Ask the torrent's trackers how many peers are in the torrent's swarm", "s", Arg::None, nullptr },
{ 'u', "unsorted", "Do not sort files by name", "u", Arg::None, nullptr },
{ 'V', "version", "Show version number and exit", "V", Arg::None, nullptr },
{ 0, nullptr, nullptr, nullptr, Arg::None, nullptr },
} };
static_assert(Options[std::size(Options) - 2].val != 0);
} // namespace
namespace
{
struct app_opts
{
std::string_view filename;
@@ -83,7 +87,7 @@ int parseCommandLine(app_opts& opts, int argc, char const* const* argv)
int c;
char const* optarg;
while ((c = tr_getopt(Usage, argc, argv, std::data(options), &optarg)) != TR_OPT_DONE)
while ((c = tr_getopt(Usage, argc, argv, std::data(Options), &optarg)) != TR_OPT_DONE)
{
switch (c)
{
@@ -426,7 +430,7 @@ int tr_main(int argc, char* argv[])
if (std::empty(opts.filename))
{
fmt::print(stderr, "ERROR: No torrent file specified.\n");
tr_getopt_usage(MyName, Usage, std::data(options));
tr_getopt_usage(MyName, Usage, std::data(Options));
fmt::print(stderr, "\n");
return EXIT_FAILURE;
}