source: trunk/utils/edit.c

Last change on this file was 14718, checked in by mikedld, 5 years ago

Explicitly compare result of str(n)cmp/memcmp to signify that it's not boolean

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