2010-06-16 14:27:24 +00:00
|
|
|
/*
|
2014-01-19 01:09:44 +00:00
|
|
|
* This file Copyright (C) 2012-2014 Mnemosyne LLC
|
2010-06-16 14:27:24 +00:00
|
|
|
*
|
2014-01-21 03:10:30 +00:00
|
|
|
* It may be used under the GNU GPL versions 2 or 3
|
2014-01-19 01:09:44 +00:00
|
|
|
* or any future license endorsed by Mnemosyne LLC.
|
2010-06-16 14:27:24 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2013-01-17 18:55:51 +00:00
|
|
|
#include <stdio.h> /* fprintf() */
|
|
|
|
#include <stdlib.h> /* strtoul(), EXIT_FAILURE */
|
2021-08-07 10:15:06 +00:00
|
|
|
#include <inttypes.h> /* PRIu32 */
|
2010-06-16 14:27:24 +00:00
|
|
|
|
|
|
|
#include <libtransmission/transmission.h>
|
2014-09-21 17:55:39 +00:00
|
|
|
#include <libtransmission/error.h>
|
2014-07-08 00:08:43 +00:00
|
|
|
#include <libtransmission/file.h>
|
2010-06-16 14:27:24 +00:00
|
|
|
#include <libtransmission/makemeta.h>
|
|
|
|
#include <libtransmission/tr-getopt.h>
|
|
|
|
#include <libtransmission/utils.h>
|
2010-08-08 22:53:24 +00:00
|
|
|
#include <libtransmission/version.h>
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2014-06-10 00:43:21 +00:00
|
|
|
#include "units.h"
|
|
|
|
|
2010-06-16 14:27:24 +00:00
|
|
|
#define MY_NAME "transmission-create"
|
|
|
|
|
|
|
|
#define MAX_TRACKERS 128
|
2017-04-20 16:02:19 +00:00
|
|
|
static uint32_t const KiB = 1024;
|
2010-06-16 14:27:24 +00:00
|
|
|
static tr_tracker_info trackers[MAX_TRACKERS];
|
|
|
|
static int trackerCount = 0;
|
2011-03-22 15:19:54 +00:00
|
|
|
static bool isPrivate = false;
|
|
|
|
static bool showVersion = false;
|
2021-10-06 16:32:17 +00:00
|
|
|
static char const* comment = nullptr;
|
|
|
|
static char const* outfile = nullptr;
|
|
|
|
static char const* infile = nullptr;
|
2013-01-17 18:55:51 +00:00
|
|
|
static uint32_t piecesize_kib = 0;
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
static tr_option options[] = {
|
2021-10-06 16:32:17 +00:00
|
|
|
{ 'p', "private", "Allow this torrent to only be used with the specified tracker(s)", "p", false, nullptr },
|
2019-07-14 12:40:41 +00:00
|
|
|
{ '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>" },
|
2021-10-06 16:32:17 +00:00
|
|
|
{ 'V', "version", "Show version number and exit", "V", false, nullptr },
|
|
|
|
{ 0, nullptr, nullptr, nullptr, false, nullptr }
|
2010-06-16 14:27:24 +00:00
|
|
|
};
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
static char const* getUsage(void)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
return "Usage: " MY_NAME " [options] <file|directory>";
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
static int parseCommandLine(int argc, char const* const* argv)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
int c;
|
2017-04-20 16:02:19 +00:00
|
|
|
char const* optarg;
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
while ((c = tr_getopt(getUsage(), argc, argv, options, &optarg)) != TR_OPT_DONE)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
switch (c)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
case 'V':
|
2012-12-05 17:29:46 +00:00
|
|
|
showVersion = true;
|
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case 'p':
|
2012-12-05 17:29:46 +00:00
|
|
|
isPrivate = true;
|
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case 'o':
|
2012-12-05 17:29:46 +00:00
|
|
|
outfile = optarg;
|
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case 'c':
|
2012-12-05 17:29:46 +00:00
|
|
|
comment = optarg;
|
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case 't':
|
2012-12-05 17:29:46 +00:00
|
|
|
if (trackerCount + 1 < MAX_TRACKERS)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2012-12-05 17:29:46 +00:00
|
|
|
trackers[trackerCount].tier = trackerCount;
|
2017-04-19 12:04:45 +00:00
|
|
|
trackers[trackerCount].announce = (char*)optarg;
|
|
|
|
++trackerCount;
|
|
|
|
}
|
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case 's':
|
2021-10-06 16:32:17 +00:00
|
|
|
if (optarg != nullptr)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-10-06 16:32:17 +00:00
|
|
|
char* endptr = nullptr;
|
2017-04-19 12:04:45 +00:00
|
|
|
piecesize_kib = strtoul(optarg, &endptr, 10);
|
|
|
|
|
2021-10-06 16:32:17 +00:00
|
|
|
if (endptr != nullptr && *endptr == 'M')
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
piecesize_kib *= KiB;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-17 18:55:51 +00:00
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case TR_OPT_UNK:
|
2012-12-05 17:29:46 +00:00
|
|
|
infile = optarg;
|
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
default:
|
2012-12-05 17:29:46 +00:00
|
|
|
return 1;
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return 0;
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static char* tr_getcwd(void)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
char* result;
|
2021-10-06 16:32:17 +00:00
|
|
|
tr_error* error = nullptr;
|
2012-12-05 17:29:46 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
result = tr_sys_dir_get_current(&error);
|
2012-12-05 17:29:46 +00:00
|
|
|
|
2021-10-06 16:32:17 +00:00
|
|
|
if (result == nullptr)
|
2011-02-09 06:10:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
fprintf(stderr, "getcwd error: \"%s\"", error->message);
|
|
|
|
tr_error_free(error);
|
|
|
|
result = tr_strdup("");
|
2011-02-09 06:10:01 +00:00
|
|
|
}
|
2012-12-05 17:29:46 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return result;
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
int tr_main(int argc, char* argv[])
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2021-10-06 16:32:17 +00:00
|
|
|
char* out2 = nullptr;
|
|
|
|
tr_metainfo_builder* b = nullptr;
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_logSetLevel(TR_LOG_ERROR);
|
|
|
|
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);
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (parseCommandLine(argc, (char const* const*)argv) != 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (showVersion)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
fprintf(stderr, MY_NAME " " LONG_VERSION_STRING "\n");
|
|
|
|
return EXIT_SUCCESS;
|
2010-08-08 22:53:24 +00:00
|
|
|
}
|
|
|
|
|
2021-10-06 16:32:17 +00:00
|
|
|
if (infile == nullptr)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
fprintf(stderr, "ERROR: No input file or directory specified.\n");
|
|
|
|
tr_getopt_usage(MY_NAME, getUsage(), options);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
return EXIT_FAILURE;
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
|
|
|
|
2021-10-06 16:32:17 +00:00
|
|
|
if (outfile == nullptr)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2021-10-06 16:32:17 +00:00
|
|
|
tr_error* error = nullptr;
|
2017-04-19 12:04:45 +00:00
|
|
|
char* base = tr_sys_path_basename(infile, &error);
|
|
|
|
|
2021-10-06 16:32:17 +00:00
|
|
|
if (base == nullptr)
|
2016-03-13 10:41:52 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
fprintf(stderr, "ERROR: Cannot deduce output path from input path: %s\n", error->message);
|
|
|
|
return EXIT_FAILURE;
|
2016-03-13 10:41:52 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
char* end = tr_strdup_printf("%s.torrent", base);
|
|
|
|
char* cwd = tr_getcwd();
|
2021-10-06 16:32:17 +00:00
|
|
|
outfile = out2 = tr_buildPath(cwd, end, nullptr);
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_free(cwd);
|
|
|
|
tr_free(end);
|
|
|
|
tr_free(base);
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (trackerCount == 0)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
if (isPrivate)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
fprintf(stderr, "ERROR: no trackers specified for a private torrent\n");
|
|
|
|
return EXIT_FAILURE;
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
printf("WARNING: no trackers specified\n");
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-07 10:15:06 +00:00
|
|
|
printf("Creating torrent \"%s\"\n", outfile);
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
b = tr_metaInfoBuilderCreate(infile);
|
2013-01-17 18:55:51 +00:00
|
|
|
|
2021-10-06 16:32:17 +00:00
|
|
|
if (b == nullptr)
|
2018-01-24 20:17:05 +00:00
|
|
|
{
|
|
|
|
fprintf(stderr, "ERROR: Cannot find specified input file or directory.\n");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (piecesize_kib != 0)
|
|
|
|
{
|
|
|
|
tr_metaInfoBuilderSetPieceSize(b, piecesize_kib * KiB);
|
|
|
|
}
|
|
|
|
|
2021-08-07 10:15:06 +00:00
|
|
|
char buf[128];
|
2021-08-15 09:41:48 +00:00
|
|
|
printf(
|
|
|
|
b->fileCount > 1 ? " %" PRIu32 " files, %s\n" : " %" PRIu32 " file, %s\n",
|
|
|
|
b->fileCount,
|
2021-08-07 10:15:06 +00:00
|
|
|
tr_formatter_size_B(buf, b->totalSize, sizeof(buf)));
|
2021-08-15 09:41:48 +00:00
|
|
|
printf(
|
|
|
|
b->pieceCount > 1 ? " %" PRIu32 " pieces, %s each\n" : " %" PRIu32 " piece, %s\n",
|
|
|
|
b->pieceCount,
|
2021-08-07 10:15:06 +00:00
|
|
|
tr_formatter_size_B(buf, b->pieceSize, sizeof(buf)));
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_makeMetaInfo(b, outfile, trackers, trackerCount, comment, isPrivate);
|
2013-01-17 18:55:51 +00:00
|
|
|
|
2021-08-07 10:15:06 +00:00
|
|
|
uint32_t last = UINT32_MAX;
|
2017-04-19 12:04:45 +00:00
|
|
|
while (!b->isDone)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_wait_msec(500);
|
2021-08-07 10:15:06 +00:00
|
|
|
|
|
|
|
uint32_t current = b->pieceIndex;
|
|
|
|
if (current != last)
|
|
|
|
{
|
|
|
|
printf("\rPiece %" PRIu32 "/%" PRIu32 " ...", current, b->pieceCount);
|
|
|
|
fflush(stdout);
|
|
|
|
|
|
|
|
last = current;
|
|
|
|
}
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
putc(' ', stdout);
|
|
|
|
|
|
|
|
switch (b->result)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
case TR_MAKEMETA_OK:
|
|
|
|
printf("done!");
|
2012-12-05 17:29:46 +00:00
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case TR_MAKEMETA_URL:
|
|
|
|
printf("bad announce URL: \"%s\"", b->errfile);
|
2012-12-05 17:29:46 +00:00
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case TR_MAKEMETA_IO_READ:
|
|
|
|
printf("error reading \"%s\": %s", b->errfile, tr_strerror(b->my_errno));
|
2012-12-05 17:29:46 +00:00
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case TR_MAKEMETA_IO_WRITE:
|
|
|
|
printf("error writing \"%s\": %s", b->errfile, tr_strerror(b->my_errno));
|
2012-12-05 17:29:46 +00:00
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case TR_MAKEMETA_CANCELLED:
|
|
|
|
printf("cancelled");
|
2012-12-05 17:29:46 +00:00
|
|
|
break;
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
putc('\n', stdout);
|
|
|
|
|
|
|
|
tr_metaInfoBuilderFree(b);
|
|
|
|
tr_free(out2);
|
|
|
|
return EXIT_SUCCESS;
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|