2 * This file Copyright (C) 2012-2014 Mnemosyne LLC
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
7 * $Id: edit.c 14241 2014-01-21 03:10:30Z jordan $
10 #include <stdio.h> /* fprintf () */
11 #include <string.h> /* strlen (), strstr (), strcmp () */
12 #include <stdlib.h> /* EXIT_FAILURE */
14 #include <event2/buffer.h>
16 #include <libtransmission/transmission.h>
17 #include <libtransmission/tr-getopt.h>
18 #include <libtransmission/utils.h>
19 #include <libtransmission/variant.h>
20 #include <libtransmission/version.h>
22 #define MY_NAME "transmission-edit"
24 static int fileCount
= 0;
25 static bool showVersion
= false;
26 static const char ** files
= NULL
;
27 static const char * add
= NULL
;
28 static const char * deleteme
= NULL
;
29 static const char * replace
[2] = { NULL
, NULL
};
31 static tr_option options
[] =
33 { 'a', "add", "Add a tracker's announce URL", "a", 1, "<url>" },
34 { 'd', "delete", "Delete a tracker's announce URL", "d", 1, "<url>" },
35 { 'r', "replace", "Search and replace a substring in the announce URLs", "r", 1, "<old> <new>" },
36 { 'V', "version", "Show version number and exit", "V", 0, NULL
},
37 { 0, NULL
, NULL
, NULL
, 0, NULL
}
43 return "Usage: " MY_NAME
" [options] torrent-file(s)";
47 parseCommandLine (int argc
, const char ** argv
)
52 while ((c
= tr_getopt (getUsage (), argc
, argv
, options
, &optarg
)))
66 c
= tr_getopt (getUsage (), argc
, argv
, options
, &optarg
);
77 files
[fileCount
++] = optarg
;
89 removeURL (tr_variant
* metainfo
, const char * url
)
92 tr_variant
* announce_list
;
95 if (tr_variantDictFindStr (metainfo
, TR_KEY_announce
, &str
, NULL
) && !strcmp (str
, url
))
97 printf ("\tRemoved \"%s\" from \"announce\"\n", str
);
98 tr_variantDictRemove (metainfo
, TR_KEY_announce
);
102 if (tr_variantDictFindList (metainfo
, TR_KEY_announce_list
, &announce_list
))
106 while ((tier
= tr_variantListChild (announce_list
, tierIndex
)))
110 while ((node
= tr_variantListChild (tier
, nodeIndex
)))
112 if (tr_variantGetStr (node
, &str
, NULL
) && !strcmp (str
, url
))
114 printf ("\tRemoved \"%s\" from \"announce-list\" tier #%d\n", str
, (tierIndex
+1));
115 tr_variantListRemove (tier
, nodeIndex
);
121 if (tr_variantListSize (tier
) == 0)
123 printf ("\tNo URLs left in tier #%d... removing tier\n", (tierIndex
+1));
124 tr_variantListRemove (announce_list
, tierIndex
);
132 if (tr_variantListSize (announce_list
) == 0)
134 printf ("\tNo tiers left... removing announce-list\n");
135 tr_variantDictRemove (metainfo
, TR_KEY_announce_list
);
139 /* if we removed the "announce" field and there's still another track left,
140 * use it as the "announce" field */
141 if (changed
&& !tr_variantDictFindStr (metainfo
, TR_KEY_announce
, &str
, NULL
))
146 if ((tier
= tr_variantListChild (announce_list
, 0)))
148 if ((node
= tr_variantListChild (tier
, 0)))
150 if (tr_variantGetStr (node
, &str
, NULL
))
152 tr_variantDictAddStr (metainfo
, TR_KEY_announce
, str
);
153 printf ("\tAdded \"%s\" to announce\n", str
);
163 replaceSubstr (const char * str
, const char * in
, const char * out
)
166 struct evbuffer
* buf
= evbuffer_new ();
167 const size_t inlen
= strlen (in
);
168 const size_t outlen
= strlen (out
);
170 while ((walk
= strstr (str
, in
)))
172 evbuffer_add (buf
, str
, walk
-str
);
173 evbuffer_add (buf
, out
, outlen
);
177 evbuffer_add (buf
, str
, strlen (str
));
179 return evbuffer_free_to_str (buf
);
183 replaceURL (tr_variant
* metainfo
, const char * in
, const char * out
)
186 tr_variant
* announce_list
;
187 bool changed
= false;
189 if (tr_variantDictFindStr (metainfo
, TR_KEY_announce
, &str
, NULL
) && strstr (str
, in
))
191 char * newstr
= replaceSubstr (str
, in
, out
);
192 printf ("\tReplaced in \"announce\": \"%s\" --> \"%s\"\n", str
, newstr
);
193 tr_variantDictAddStr (metainfo
, TR_KEY_announce
, newstr
);
198 if (tr_variantDictFindList (metainfo
, TR_KEY_announce_list
, &announce_list
))
202 while ((tier
= tr_variantListChild (announce_list
, tierCount
++)))
206 while ((node
= tr_variantListChild (tier
, nodeCount
++)))
208 if (tr_variantGetStr (node
, &str
, NULL
) && strstr (str
, in
))
210 char * newstr
= replaceSubstr (str
, in
, out
);
211 printf ("\tReplaced in \"announce-list\" tier %d: \"%s\" --> \"%s\"\n", tierCount
, str
, newstr
);
212 tr_variantFree (node
);
213 tr_variantInitStr (node
, newstr
, -1);
225 announce_list_has_url (tr_variant
* announce_list
, const char * url
)
229 while ((tier
= tr_variantListChild (announce_list
, tierCount
++)))
234 while ((node
= tr_variantListChild (tier
, nodeCount
++)))
235 if (tr_variantGetStr (node
, &str
, NULL
) && !strcmp (str
, url
))
243 addURL (tr_variant
* metainfo
, const char * url
)
245 const char * announce
= NULL
;
246 tr_variant
* announce_list
= NULL
;
247 bool changed
= false;
248 const bool had_announce
= tr_variantDictFindStr (metainfo
, TR_KEY_announce
, &announce
, NULL
);
249 const bool had_announce_list
= tr_variantDictFindList (metainfo
, TR_KEY_announce_list
, &announce_list
);
251 if (!had_announce
&& !had_announce_list
)
253 /* this new tracker is the only one, so add it to "announce"... */
254 printf ("\tAdded \"%s\" in \"announce\"\n", url
);
255 tr_variantDictAddStr (metainfo
, TR_KEY_announce
, url
);
260 if (!had_announce_list
)
262 announce_list
= tr_variantDictAddList (metainfo
, TR_KEY_announce_list
, 2);
266 /* we're moving from an 'announce' to an 'announce-list',
267 * so copy the old announce URL to the list */
268 tr_variant
* tier
= tr_variantListAddList (announce_list
, 1);
269 tr_variantListAddStr (tier
, announce
);
274 /* If the user-specified URL isn't in the announce list yet, add it */
275 if (!announce_list_has_url (announce_list
, url
))
277 tr_variant
* tier
= tr_variantListAddList (announce_list
, 1);
278 tr_variantListAddStr (tier
, url
);
279 printf ("\tAdded \"%s\" to \"announce-list\" tier %"TR_PRIuSIZE
"\n", url
, tr_variantListSize (announce_list
));
288 main (int argc
, char * argv
[])
291 int changedCount
= 0;
293 files
= tr_new0 (const char*, argc
);
295 tr_logSetLevel (TR_LOG_ERROR
);
297 if (parseCommandLine (argc
, (const char**)argv
))
302 fprintf (stderr
, MY_NAME
" "LONG_VERSION_STRING
"\n");
308 fprintf (stderr
, "ERROR: No torrent files specified.\n");
309 tr_getopt_usage (MY_NAME
, getUsage (), options
);
310 fprintf (stderr
, "\n");
314 if (!add
&& !deleteme
&& !replace
[0])
316 fprintf (stderr
, "ERROR: Must specify -a, -d or -r\n");
317 tr_getopt_usage (MY_NAME
, getUsage (), options
);
318 fprintf (stderr
, "\n");
322 for (i
=0; i
<fileCount
; ++i
)
325 bool changed
= false;
326 const char * filename
= files
[i
];
328 printf ("%s\n", filename
);
330 if (tr_variantFromFile (&top
, TR_VARIANT_FMT_BENC
, filename
))
332 printf ("\tError reading file\n");
336 if (deleteme
!= NULL
)
337 changed
|= removeURL (&top
, deleteme
);
340 changed
= addURL (&top
, add
);
342 if (replace
[0] && replace
[1])
343 changed
|= replaceURL (&top
, replace
[0], replace
[1]);
348 tr_variantToFile (&top
, TR_VARIANT_FMT_BENC
, filename
);
351 tr_variantFree (&top
);
354 printf ("Changed %d files\n", changedCount
);