source: trunk/utils/edit.c @ 13683

Last change on this file since 13683 was 13683, checked in by jordan, 9 years ago

(trunk, libT) first drop of the tr_quark patch.

  • Property svn:keywords set to Date Rev Author Id
File size: 9.7 KB
RevLine 
[10783]1/*
[11709]2 * This file Copyright (C) Mnemosyne LLC
[10783]3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2
6 * as published by the Free Software Foundation.
7 *
8 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
9 *
10 * $Id: edit.c 13683 2012-12-22 20:35:19Z jordan $
11 */
12
[13625]13#include <stdio.h> /* fprintf () */
14#include <string.h> /* strlen (), strstr (), strcmp () */
[12177]15#include <stdlib.h> /* EXIT_FAILURE */
[10783]16
[11548]17#include <event2/buffer.h>
[10783]18
19#include <libtransmission/transmission.h>
20#include <libtransmission/tr-getopt.h>
21#include <libtransmission/utils.h>
[13667]22#include <libtransmission/variant.h>
[11160]23#include <libtransmission/version.h>
[10783]24
25#define MY_NAME "transmission-edit"
26
[11160]27static int fileCount = 0;
[12204]28static bool showVersion = false;
[11160]29static const char ** files = NULL;
30static const char * add = NULL;
31static const char * deleteme = NULL;
32static const char * replace[2] = { NULL, NULL };
[10783]33
34static tr_option options[] =
35{
36  { 'a', "add", "Add a tracker's announce URL", "a", 1, "<url>" },
37  { 'd', "delete", "Delete a tracker's announce URL", "d", 1, "<url>" },
38  { 'r', "replace", "Search and replace a substring in the announce URLs", "r", 1, "<old> <new>" },
[11160]39  { 'V', "version", "Show version number and exit", "V", 0, NULL },
[10783]40  { 0, NULL, NULL, NULL, 0, NULL }
41};
42
43static const char *
[13625]44getUsage (void)
[10783]45{
[13625]46  return "Usage: " MY_NAME " [options] torrent-file (s)";
[10783]47}
48
49static int
[13625]50parseCommandLine (int argc, const char ** argv)
[10783]51{
[13625]52  int c;
53  const char * optarg;
[10783]54
[13625]55  while ((c = tr_getopt (getUsage (), argc, argv, options, &optarg)))
[10783]56    {
[13625]57      switch (c)
[10783]58        {
[13625]59          case 'a':
60            add = optarg;
61            break;
62
63          case 'd':
64            deleteme = optarg;
65            break;
66
67          case 'r':
68            replace[0] = optarg;
69            c = tr_getopt (getUsage (), argc, argv, options, &optarg);
70            if (c != TR_OPT_UNK)
71              return 1;
72            replace[1] = optarg;
73            break;
74
75          case 'V':
76            showVersion = true;
77            break;
78
79          case TR_OPT_UNK:
80            files[fileCount++] = optarg;
81            break;
82
83          default:
84            return 1;
[10783]85        }
86    }
87
[13625]88  return 0;
[10783]89}
90
[12204]91static bool
[13667]92removeURL (tr_variant * metainfo, const char * url)
[10783]93{
[13625]94  const char * str;
[13667]95  tr_variant * announce_list;
[13625]96  bool changed = false;
[10783]97
[13683]98  if (tr_variantDictFindStr (metainfo, TR_KEY_announce, &str, NULL) && !strcmp (str, url))
[10783]99    {
[13625]100      printf ("\tRemoved \"%s\" from \"announce\"\n", str);
[13683]101      tr_variantDictRemove (metainfo, TR_KEY_announce);
[13625]102      changed = true;
[10783]103    }
104
[13683]105  if (tr_variantDictFindList (metainfo, TR_KEY_announce_list, &announce_list))
[10783]106    {
[13667]107      tr_variant * tier;
[13625]108      int tierIndex = 0;
[13667]109      while ((tier = tr_variantListChild (announce_list, tierIndex)))
[10783]110        {
[13667]111          tr_variant * node;
[13625]112          int nodeIndex = 0;
[13667]113          while ((node = tr_variantListChild (tier, nodeIndex)))
[10783]114            {
[13667]115              if (tr_variantGetStr (node, &str, NULL) && !strcmp (str, url))
[10783]116                {
[13625]117                  printf ("\tRemoved \"%s\" from \"announce-list\" tier #%d\n", str, (tierIndex+1));
[13667]118                  tr_variantListRemove (tier, nodeIndex);
[13625]119                  changed = true;
[10783]120                }
[13625]121              else ++nodeIndex;
[10783]122            }
123
[13667]124          if (tr_variantListSize (tier) == 0)
[10783]125            {
[13625]126              printf ("\tNo URLs left in tier #%d... removing tier\n", (tierIndex+1));
[13667]127              tr_variantListRemove (announce_list, tierIndex);
[10783]128            }
[13625]129          else
130            {
131              ++tierIndex;
132            }
[10783]133        }
134
[13667]135      if (tr_variantListSize (announce_list) == 0)
[10783]136        {
[13625]137          printf ("\tNo tiers left... removing announce-list\n");
[13683]138          tr_variantDictRemove (metainfo, TR_KEY_announce_list);
[10783]139        }
140    }
141
[13625]142  /* if we removed the "announce" field and there's still another track left,
143   * use it as the "announce" field */
[13683]144  if (changed && !tr_variantDictFindStr (metainfo, TR_KEY_announce, &str, NULL))
[10783]145    {
[13667]146      tr_variant * tier;
147      tr_variant * node;
[10783]148
[13667]149      if ((tier = tr_variantListChild (announce_list, 0)))
[13625]150        {
[13667]151          if ((node = tr_variantListChild (tier, 0)))
[13625]152            {
[13667]153              if (tr_variantGetStr (node, &str, NULL))
[13625]154                {
[13683]155                  tr_variantDictAddStr (metainfo, TR_KEY_announce, str);
[13625]156                  printf ("\tAdded \"%s\" to announce\n", str);
[10783]157                }
158            }
159        }
160    }
161
[13625]162  return changed;
[10783]163}
164
165static char*
[13625]166replaceSubstr (const char * str, const char * in, const char * out)
[10783]167{
[13625]168  char * walk;
169  struct evbuffer * buf = evbuffer_new ();
170  const size_t inlen = strlen (in);
171  const size_t outlen = strlen (out);
[10783]172
[13625]173  while ((walk = strstr (str, in)))
[10783]174    {
[13625]175      evbuffer_add (buf, str, walk-str);
176      evbuffer_add (buf, out, outlen);
177      str = walk + inlen;
[10783]178    }
179
[13625]180  evbuffer_add (buf, str, strlen (str));
[11791]181
[13625]182  return evbuffer_free_to_str (buf);
[10783]183}
184
[12204]185static bool
[13667]186replaceURL (tr_variant * metainfo, const char * in, const char * out)
[10783]187{
[13625]188  const char * str;
[13667]189  tr_variant * announce_list;
[13625]190  bool changed = false;
[10783]191
[13683]192  if (tr_variantDictFindStr (metainfo, TR_KEY_announce, &str, NULL) && strstr (str, in))
[10783]193    {
[13625]194      char * newstr = replaceSubstr (str, in, out);
195      printf ("\tReplaced in \"announce\": \"%s\" --> \"%s\"\n", str, newstr);
[13683]196      tr_variantDictAddStr (metainfo, TR_KEY_announce, newstr);
[13625]197      tr_free (newstr);
198      changed = true;
[10783]199    }
200
[13683]201  if (tr_variantDictFindList (metainfo, TR_KEY_announce_list, &announce_list))
[10783]202    {
[13667]203      tr_variant * tier;
[13625]204      int tierCount = 0;
[13667]205      while ((tier = tr_variantListChild (announce_list, tierCount++)))
[10783]206        {
[13667]207          tr_variant * node;
[13625]208          int nodeCount = 0;
[13667]209          while ((node = tr_variantListChild (tier, nodeCount++)))
[10783]210            {
[13667]211              if (tr_variantGetStr (node, &str, NULL) && strstr (str, in))
[10783]212                {
[13625]213                  char * newstr = replaceSubstr (str, in, out);
214                  printf ("\tReplaced in \"announce-list\" tier %d: \"%s\" --> \"%s\"\n", tierCount, str, newstr);
[13667]215                  tr_variantFree (node);
216                  tr_variantInitStr (node, newstr, -1);
[13625]217                  tr_free (newstr);
218                  changed = true;
[10783]219                }
220            }
221        }
222    }
223
[13625]224  return changed;
[10783]225}
226
[12204]227static bool
[13667]228announce_list_has_url (tr_variant * announce_list, const char * url)
[12851]229{
[13667]230  tr_variant * tier;
[13625]231  int tierCount = 0;
[13667]232  while ((tier = tr_variantListChild (announce_list, tierCount++)))
[13625]233    {
[13667]234      tr_variant * node;
[13625]235      const char * str;
236      int nodeCount = 0;
[13667]237      while ((node = tr_variantListChild (tier, nodeCount++)))
238        if (tr_variantGetStr (node, &str, NULL) && !strcmp (str, url))
[13625]239          return true;
[12851]240    }
[13625]241
242  return false;
[12851]243}
244
245static bool
[13667]246addURL (tr_variant * metainfo, const char * url)
[10783]247{
[13625]248  const char * announce = NULL;
[13667]249  tr_variant * announce_list = NULL;
[13625]250  bool changed = false;
[13683]251  const bool had_announce = tr_variantDictFindStr (metainfo, TR_KEY_announce, &announce, NULL);
252  const bool had_announce_list = tr_variantDictFindList (metainfo, TR_KEY_announce_list, &announce_list);
[10783]253
[13625]254  if (!had_announce && !had_announce_list)
[10783]255    {
[13625]256      /* this new tracker is the only one, so add it to "announce"... */
257      printf ("\tAdded \"%s\" in \"announce\"\n", url);
[13683]258      tr_variantDictAddStr (metainfo, TR_KEY_announce, url);
[13625]259      changed = true;
[10783]260    }
[13625]261  else
[12851]262    {
[13625]263      if (!had_announce_list)
[12851]264        {
[13683]265          announce_list = tr_variantDictAddList (metainfo, TR_KEY_announce_list, 2);
[13191]266
[13625]267          if (had_announce)
[12851]268            {
[13625]269              /* we're moving from an 'announce' to an 'announce-list',
270               * so copy the old announce URL to the list */
[13667]271              tr_variant * tier = tr_variantListAddList (announce_list, 1);
272              tr_variantListAddStr (tier, announce);
[13625]273              changed = true;
[12851]274            }
275        }
[10783]276
[13625]277      /* If the user-specified URL isn't in the announce list yet, add it */
278      if (!announce_list_has_url (announce_list, url))
[12851]279        {
[13667]280          tr_variant * tier = tr_variantListAddList (announce_list, 1);
281          tr_variantListAddStr (tier, url);
282          printf ("\tAdded \"%s\" to \"announce-list\" tier %zu\n", url, tr_variantListSize (announce_list));
[13625]283          changed = true;
[10783]284        }
285    }
286
[13625]287  return changed;
[10783]288}
289
290int
[13625]291main (int argc, char * argv[])
[10783]292{
[13625]293  int i;
294  int changedCount = 0;
[10783]295
[13625]296  files = tr_new0 (const char*, argc);
[10783]297
[13625]298  tr_setMessageLevel (TR_MSG_ERR);
[10783]299
[13625]300  if (parseCommandLine (argc, (const char**)argv))
301    return EXIT_FAILURE;
[10783]302
[13625]303  if (showVersion)
[11160]304    {
[13625]305      fprintf (stderr, MY_NAME" "LONG_VERSION_STRING"\n");
306      return EXIT_SUCCESS;
[11160]307    }
308
[13625]309  if (fileCount < 1)
[10783]310    {
[13625]311      fprintf (stderr, "ERROR: No torrent files specified.\n");
312      tr_getopt_usage (MY_NAME, getUsage (), options);
313      fprintf (stderr, "\n");
314      return EXIT_FAILURE;
[10783]315    }
316
[13625]317  if (!add && !deleteme && !replace[0])
[10783]318    {
[13625]319      fprintf (stderr, "ERROR: Must specify -a, -d or -r\n");
320      tr_getopt_usage (MY_NAME, getUsage (), options);
321      fprintf (stderr, "\n");
322      return EXIT_FAILURE;
[10783]323    }
324
[13625]325  for (i=0; i<fileCount; ++i)
[10783]326    {
[13667]327      tr_variant top;
[13625]328      bool changed = false;
329      const char * filename = files[i];
[10783]330
[13625]331      printf ("%s\n", filename);
[10783]332
[13667]333      if (tr_variantFromFile (&top, TR_VARIANT_FMT_BENC, filename))
[10783]334        {
[13625]335          printf ("\tError reading file\n");
336          continue;
[10783]337        }
338
[13625]339      if (deleteme != NULL)
340        changed |= removeURL (&top, deleteme);
[10783]341
[13625]342      if (add != NULL)
343        changed = addURL (&top, add);
[10783]344
[13625]345      if (replace[0] && replace[1])
346        changed |= replaceURL (&top, replace[0], replace[1]);
[10783]347
[13625]348      if (changed)
[10783]349        {
[13625]350          ++changedCount;
[13667]351          tr_variantToFile (&top, TR_VARIANT_FMT_BENC, filename);
[10783]352        }
353
[13667]354      tr_variantFree (&top);
[10783]355    }
356
[13625]357  printf ("Changed %d files\n", changedCount);
[10783]358
[13625]359  tr_free (files);
360  return EXIT_SUCCESS;
[10783]361}
Note: See TracBrowser for help on using the repository browser.