2 * This file Copyright (C) Mnemosyne LLC
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.
8 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10 * $Id: edit.c 11791 2011-01-30 16:40:11Z jordan $
15 #include <event2/buffer.h>
17 #include <libtransmission/transmission.h>
18 #include <libtransmission/bencode.h>
19 #include <libtransmission/tr-getopt.h>
20 #include <libtransmission/utils.h>
21 #include <libtransmission/version.h>
23 #define MY_NAME "transmission-edit"
25 static int fileCount
= 0;
26 static tr_bool showVersion
= FALSE
;
27 static const char ** files
= NULL
;
28 static const char * add
= NULL
;
29 static const char * deleteme
= NULL
;
30 static const char * replace
[2] = { NULL
, NULL
};
32 static tr_option options
[] =
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
}
44 return "Usage: " MY_NAME
" [options] torrent-file(s)";
48 parseCommandLine( int argc
, const char ** argv
)
53 while(( c
= tr_getopt( getUsage( ), argc
, argv
, options
, &optarg
)))
57 case 'a': add
= optarg
;
59 case 'd': deleteme
= optarg
;
61 case 'r': replace
[0] = optarg
;
62 c
= tr_getopt( getUsage( ), argc
, argv
, options
, &optarg
);
63 if( c
!= TR_OPT_UNK
) return 1;
66 case 'V': showVersion
= TRUE
;
68 case TR_OPT_UNK
: files
[fileCount
++] = optarg
; break;
77 removeURL( tr_benc
* metainfo
, const char * url
)
80 tr_benc
* announce_list
;
81 tr_bool changed
= FALSE
;
83 if( tr_bencDictFindStr( metainfo
, "announce", &str
) && !strcmp( str
, url
) )
85 printf( "\tRemoved \"%s\" from \"announce\"\n", str
);
86 tr_bencDictRemove( metainfo
, "announce" );
90 if( tr_bencDictFindList( metainfo
, "announce-list", &announce_list
) )
94 while(( tier
= tr_bencListChild( announce_list
, tierIndex
)))
98 while(( node
= tr_bencListChild( tier
, nodeIndex
)))
100 if( tr_bencGetStr( node
, &str
) && !strcmp( str
, url
) )
102 printf( "\tRemoved \"%s\" from \"announce-list\" tier #%d\n", str
, (tierIndex
+1) );
103 tr_bencListRemove( tier
, nodeIndex
);
109 if( tr_bencListSize( tier
) == 0 )
111 printf( "\tNo URLs left in tier #%d... removing tier\n", (tierIndex
+1) );
112 tr_bencListRemove( announce_list
, tierIndex
);
117 if( tr_bencListSize( announce_list
) == 0 )
119 printf( "\tNo tiers left... removing announce-list\n" );
120 tr_bencDictRemove( metainfo
, "announce-list" );
124 /* if we removed the "announce" field and there's still another track left,
125 * use it as the "announce" field */
126 if( changed
&& !tr_bencDictFindStr( metainfo
, "announce", &str
) )
131 if(( tier
= tr_bencListChild( announce_list
, 0 ))) {
132 if(( node
= tr_bencListChild( tier
, 0 ))) {
133 if( tr_bencGetStr( node
, &str
) ) {
134 tr_bencDictAddStr( metainfo
, "announce", str
);
135 printf( "\tAdded \"%s\" to announce\n", str
);
145 replaceSubstr( const char * str
, const char * in
, const char * out
)
148 struct evbuffer
* buf
= evbuffer_new( );
149 const size_t inlen
= strlen( in
);
150 const size_t outlen
= strlen( out
);
152 while(( walk
= strstr( str
, in
)))
154 evbuffer_add( buf
, str
, walk
-str
);
155 evbuffer_add( buf
, out
, outlen
);
159 evbuffer_add( buf
, str
, strlen( str
) );
161 return evbuffer_free_to_str( buf
);
165 replaceURL( tr_benc
* metainfo
, const char * in
, const char * out
)
168 tr_benc
* announce_list
;
169 tr_bool changed
= FALSE
;
171 if( tr_bencDictFindStr( metainfo
, "announce", &str
) && strstr( str
, in
) )
173 char * newstr
= replaceSubstr( str
, in
, out
);
174 printf( "\tReplaced in \"announce\": \"%s\" --> \"%s\"\n", str
, newstr
);
175 tr_bencDictAddStr( metainfo
, "announce", newstr
);
180 if( tr_bencDictFindList( metainfo
, "announce-list", &announce_list
) )
184 while(( tier
= tr_bencListChild( announce_list
, tierCount
++ )))
188 while(( node
= tr_bencListChild( tier
, nodeCount
++ )))
190 if( tr_bencGetStr( node
, &str
) && strstr( str
, in
) )
192 char * newstr
= replaceSubstr( str
, in
, out
);
193 printf( "\tReplaced in \"announce-list\" tier %d: \"%s\" --> \"%s\"\n", tierCount
, str
, newstr
);
195 tr_bencInitStr( node
, newstr
, -1 );
207 addURL( tr_benc
* metainfo
, const char * url
)
210 tr_benc
* announce_list
;
211 tr_bool changed
= FALSE
;
212 tr_bool match
= FALSE
;
214 /* maybe add it to "announce" */
215 if( !tr_bencDictFindStr( metainfo
, "announce", &str
) )
217 printf( "\tAdded \"%s\" in \"announce\"\n", url
);
218 tr_bencDictAddStr( metainfo
, "announce", url
);
222 /* see if it's already in announce-list */
223 if( tr_bencDictFindList( metainfo
, "announce-list", &announce_list
) ) {
226 while(( tier
= tr_bencListChild( announce_list
, tierCount
++ ))) {
229 while(( node
= tr_bencListChild( tier
, nodeCount
++ )))
230 if( tr_bencGetStr( node
, &str
) && !strcmp( str
, url
) )
235 /* if it's not in announce-list, add it now */
240 if( !tr_bencDictFindList( metainfo
, "announce-list", &announce_list
) )
241 announce_list
= tr_bencDictAddList( metainfo
, "announce-list", 1 );
243 tier
= tr_bencListAddList( announce_list
, 1 );
244 tr_bencListAddStr( tier
, url
);
245 printf( "\tAdded \"%s\" to \"announce-list\" tier %zu\n", url
, tr_bencListSize( announce_list
) );
253 main( int argc
, char * argv
[] )
256 int changedCount
= 0;
258 files
= tr_new0( const char*, argc
);
260 tr_setMessageLevel( TR_MSG_ERR
);
262 if( parseCommandLine( argc
, (const char**)argv
) )
267 fprintf( stderr
, MY_NAME
" "LONG_VERSION_STRING
"\n" );
273 fprintf( stderr
, "ERROR: No torrent files specified.\n" );
274 tr_getopt_usage( MY_NAME
, getUsage( ), options
);
275 fprintf( stderr
, "\n" );
279 if( !add
&& !deleteme
&& !replace
[0] )
281 fprintf( stderr
, "ERROR: Must specify -a, -d or -r\n" );
282 tr_getopt_usage( MY_NAME
, getUsage( ), options
);
283 fprintf( stderr
, "\n" );
287 for( i
=0; i
<fileCount
; ++i
)
290 tr_bool changed
= FALSE
;
291 const char * filename
= files
[i
];
293 printf( "%s\n", filename
);
295 if( tr_bencLoadFile( &top
, TR_FMT_BENC
, filename
) )
297 printf( "\tError reading file\n" );
301 if( deleteme
!= NULL
)
302 changed
|= removeURL( &top
, deleteme
);
305 changed
= addURL( &top
, add
);
307 if( replace
[0] && replace
[1] )
308 changed
|= replaceURL( &top
, replace
[0], replace
[1] );
313 tr_bencToFile( &top
, TR_FMT_BENC
, filename
);
319 printf( "Changed %d files\n", changedCount
);