Changeset 6795
- Timestamp:
- Sep 23, 2008, 7:11:04 PM (12 years ago)
- Location:
- trunk
- Files:
-
- 143 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/cli/cli.c
r6714 r6795 40 40 #define MY_NAME "transmission-cli" 41 41 42 static int showInfo = 0; 43 static int showScrape = 0; 44 static int isPrivate = 0; 45 static int verboseLevel = 0; 46 static int encryptionMode = TR_ENCRYPTION_PREFERRED; 47 static int peerPort = TR_DEFAULT_PORT; 48 static int peerSocketTOS = TR_DEFAULT_PEER_SOCKET_TOS; 49 static int blocklistEnabled = TR_DEFAULT_BLOCKLIST_ENABLED; 50 static int uploadLimit = 20; 51 static int downloadLimit = -1; 52 static int natTraversal = TR_DEFAULT_PORT_FORWARDING_ENABLED; 53 static int verify = 0; 54 static sig_atomic_t gotsig = 0; 55 static sig_atomic_t manualUpdate = 0; 56 57 static const char * torrentPath = NULL; 58 static const char * downloadDir = NULL; 59 static const char * finishCall = NULL; 60 static const char * announce = NULL; 61 static const char * configdir = NULL; 62 static const char * sourceFile = NULL; 63 static const char * comment = NULL; 64 65 static int parseCommandLine ( int argc, const char ** argv ); 66 static void sigHandler ( int signal ); 42 static int showInfo = 0; 43 static int showScrape = 0; 44 static int isPrivate = 0; 45 static int verboseLevel = 0; 46 static int encryptionMode = TR_ENCRYPTION_PREFERRED; 47 static int peerPort = TR_DEFAULT_PORT; 48 static int peerSocketTOS = TR_DEFAULT_PEER_SOCKET_TOS; 49 static int blocklistEnabled = TR_DEFAULT_BLOCKLIST_ENABLED; 50 static int uploadLimit = 20; 51 static int downloadLimit = -1; 52 static int natTraversal = TR_DEFAULT_PORT_FORWARDING_ENABLED; 53 static int verify = 0; 54 static sig_atomic_t gotsig = 0; 55 static sig_atomic_t manualUpdate = 0; 56 57 static const char * torrentPath = NULL; 58 static const char * downloadDir = NULL; 59 static const char * finishCall = NULL; 60 static const char * announce = NULL; 61 static const char * configdir = NULL; 62 static const char * sourceFile = NULL; 63 static const char * comment = NULL; 64 65 static int parseCommandLine( int argc, 66 const char ** argv ); 67 68 static void sigHandler( int signal ); 67 69 68 70 static char* 69 tr_strlratio( char * buf, double ratio, size_t buflen ) 71 tr_strlratio( char * buf, 72 double ratio, 73 size_t buflen ) 70 74 { 71 75 if( (int)ratio == TR_RATIO_NA ) … … 85 89 is_rfc2396_alnum( char ch ) 86 90 { 87 return 88 89 91 return ( '0' <= ch && ch <= '9' ) 92 || ( 'A' <= ch && ch <= 'Z' ) 93 || ( 'a' <= ch && ch <= 'z' ); 90 94 } 91 95 92 96 static void 93 escape( char * out, const uint8_t * in, int in_len ) /* rfc2396 */ 97 escape( char * out, 98 const uint8_t * in, 99 int in_len ) /* rfc2396 */ 94 100 { 95 101 const uint8_t *end = in + in_len; 102 96 103 while( in != end ) 97 if( is_rfc2396_alnum( *in) )104 if( is_rfc2396_alnum( *in ) ) 98 105 *out++ = (char) *in++; 99 106 else 100 107 out += tr_snprintf( out, 4, "%%%02X", (unsigned int)*in++ ); 108 101 109 *out = '\0'; 102 110 } 103 111 104 112 static void 105 torrentStateChanged( tr_torrent * torrent UNUSED,106 cp_status_t status UNUSED,113 torrentStateChanged( tr_torrent * torrent UNUSED, 114 cp_status_t status UNUSED, 107 115 void * user_data UNUSED ) 108 116 { … … 114 122 static void 115 123 scrapeDoneFunc( struct tr_handle * session UNUSED, 116 long response_code,117 const void *response,118 size_t response_byte_count,119 void *host )124 long response_code, 125 const void * response, 126 size_t response_byte_count, 127 void * host ) 120 128 { 121 129 tr_benc top, *files; 122 130 123 if( !tr_bencLoad( response, response_byte_count, &top, NULL ) 124 125 126 { 127 int64_t complete=-1, incomplete=-1, downloaded=-1;131 if( !tr_bencLoad( response, response_byte_count, &top, NULL ) 132 && tr_bencDictFindDict( &top, "files", &files ) 133 && files->val.l.count >= 2 ) 134 { 135 int64_t complete = -1, incomplete = -1, downloaded = -1; 128 136 tr_benc * hash = &files->val.l.vals[1]; 129 137 tr_bencDictFindInt( hash, "complete", &complete ); … … 131 139 tr_bencDictFindInt( hash, "downloaded", &downloaded ); 132 140 printf( "%4d seeders, %4d leechers, %5d downloads at %s\n", 133 (int)complete, (int)incomplete, (int)downloaded, (char*)host ); 141 (int)complete, (int)incomplete, (int)downloaded, 142 (char*)host ); 134 143 tr_bencFree( &top ); 135 144 } 136 145 else 137 fprintf( stderr, "Unable to parse response (http code %lu) at %s", response_code, (char*)host ); 146 fprintf( stderr, "Unable to parse response (http code %lu) at %s", 147 response_code, 148 (char*)host ); 138 149 139 150 --leftToScrape; … … 141 152 142 153 static void 143 dumpInfo( FILE * out, const tr_info * inf ) 144 { 145 int i; 146 int prevTier = -1; 154 dumpInfo( FILE * out, 155 const tr_info * inf ) 156 { 157 int i; 158 int prevTier = -1; 147 159 tr_file_index_t ff; 148 160 149 161 fprintf( out, "hash:\t" ); 150 for( i =0; i<SHA_DIGEST_LENGTH; ++i )162 for( i = 0; i < SHA_DIGEST_LENGTH; ++i ) 151 163 fprintf( out, "%02x", inf->hash[i] ); 152 164 fprintf( out, "\n" ); … … 154 166 fprintf( out, "name:\t%s\n", inf->name ); 155 167 156 for( i=0; i<inf->trackerCount; ++i ) { 157 if( prevTier != inf->trackers[i].tier ) { 168 for( i = 0; i < inf->trackerCount; ++i ) 169 { 170 if( prevTier != inf->trackers[i].tier ) 171 { 158 172 prevTier = inf->trackers[i].tier; 159 fprintf( out, "\ntracker tier #%d:\n", ( prevTier+1) );173 fprintf( out, "\ntracker tier #%d:\n", ( prevTier + 1 ) ); 160 174 } 161 175 fprintf( out, "\tannounce:\t%s\n", inf->trackers[i].announce ); 162 176 } 163 177 164 fprintf( out, "size:\t%" PRIu64" (%"PRIu64" * %d + %"PRIu64")\n",165 166 178 fprintf( out, "size:\t%" PRIu64 " (%" PRIu64 " * %d + %" PRIu64 ")\n", 179 inf->totalSize, inf->totalSize / inf->pieceSize, 180 inf->pieceSize, inf->totalSize % inf->pieceSize ); 167 181 168 182 if( inf->comment && *inf->comment ) … … 174 188 175 189 fprintf( out, "file(s):\n" ); 176 for( ff =0; ff<inf->fileCount; ++ff )177 fprintf( out, "\t%s (%" PRIu64")\n", inf->files[ff].name,178 190 for( ff = 0; ff < inf->fileCount; ++ff ) 191 fprintf( out, "\t%s (%" PRIu64 ")\n", inf->files[ff].name, 192 inf->files[ff].length ); 179 193 } 180 194 181 195 static void 182 getStatusStr( const tr_stat * st, char * buf, size_t buflen ) 196 getStatusStr( const tr_stat * st, 197 char * buf, 198 size_t buflen ) 183 199 { 184 200 if( st->status & TR_STATUS_CHECK_WAIT ) … … 188 204 else if( st->status & TR_STATUS_CHECK ) 189 205 { 190 tr_snprintf( buf, buflen, "Verifying local files (%.2f%%, %.2f%% valid)", 191 100 * st->recheckProgress, 100.0 * st->percentDone ); 206 tr_snprintf( buf, buflen, 207 "Verifying local files (%.2f%%, %.2f%% valid)", 208 100 * st->recheckProgress, 209 100.0 * st->percentDone ); 192 210 } 193 211 else if( st->status & TR_STATUS_DOWNLOAD ) … … 195 213 char ratioStr[80]; 196 214 tr_strlratio( ratioStr, st->ratio, sizeof( ratioStr ) ); 197 tr_snprintf( buf, buflen, 198 "Progress: %.1f%%, dl from %d of %d peers (%.0f KB/s), " 199 "ul to %d (%.0f KB/s) [%s]", 200 st->percentDone * 100.0, 201 st->peersSendingToUs, 202 st->peersConnected, 203 st->rateDownload, 204 st->peersGettingFromUs, 205 st->rateUpload, 206 ratioStr ); 215 tr_snprintf( 216 buf, buflen, 217 "Progress: %.1f%%, dl from %d of %d peers (%.0f KB/s), " 218 "ul to %d (%.0f KB/s) [%s]", 219 st->percentDone * 100.0, 220 st->peersSendingToUs, 221 st->peersConnected, 222 st->rateDownload, 223 st->peersGettingFromUs, 224 st->rateUpload, 225 ratioStr ); 207 226 } 208 227 else if( st->status & TR_STATUS_SEED ) … … 210 229 char ratioStr[80]; 211 230 tr_strlratio( ratioStr, st->ratio, sizeof( ratioStr ) ); 212 tr_snprintf( buf, buflen, 213 "Seeding, uploading to %d of %d peer(s), %.0f KB/s [%s]", 214 st->peersGettingFromUs, st->peersConnected, 215 st->rateUpload, ratioStr ); 231 tr_snprintf( 232 buf, buflen, 233 "Seeding, uploading to %d of %d peer(s), %.0f KB/s [%s]", 234 st->peersGettingFromUs, st->peersConnected, 235 st->rateUpload, ratioStr ); 216 236 } 217 237 else *buf = '\0'; … … 219 239 220 240 int 221 main( int argc, char ** argv ) 222 { 223 int error; 224 tr_handle * h; 225 tr_ctor * ctor; 241 main( int argc, 242 char ** argv ) 243 { 244 int error; 245 tr_handle * h; 246 tr_ctor * ctor; 226 247 tr_torrent * tor = NULL; 227 char cwd[MAX_PATH_LENGTH];248 char cwd[MAX_PATH_LENGTH]; 228 249 229 250 printf( "Transmission %s - http://www.transmissionbt.com/\n", … … 235 256 236 257 /* Check the options for validity */ 237 if( !torrentPath ) { 258 if( !torrentPath ) 259 { 238 260 fprintf( stderr, "No torrent specified!\n" ); 239 261 return EXIT_FAILURE; 240 262 } 241 if( peerPort < 1 || peerPort > 65535 ) { 242 fprintf( stderr, "Error: Port must between 1 and 65535; got %d\n", peerPort ); 263 if( peerPort < 1 || peerPort > 65535 ) 264 { 265 fprintf( stderr, "Error: Port must between 1 and 65535; got %d\n", 266 peerPort ); 243 267 return EXIT_FAILURE; 244 268 } 245 if( peerSocketTOS < 0 || peerSocketTOS > 255 ) { 246 fprintf( stderr, "Error: value must between 0 and 255; got %d\n", peerSocketTOS ); 269 if( peerSocketTOS < 0 || peerSocketTOS > 255 ) 270 { 271 fprintf( stderr, "Error: value must between 0 and 255; got %d\n", 272 peerSocketTOS ); 247 273 return EXIT_FAILURE; 248 274 } 249 275 250 /* don't bind the port if we're just running the CLI 276 /* don't bind the port if we're just running the CLI 251 277 * to get metainfo or to create a torrent */ 252 278 if( showInfo || showScrape || ( sourceFile != NULL ) ) … … 257 283 258 284 /* if no download directory specified, use cwd instead */ 259 if( !downloadDir ) { 285 if( !downloadDir ) 286 { 260 287 getcwd( cwd, sizeof( cwd ) ); 261 288 downloadDir = cwd; … … 265 292 /* Initialize libtransmission */ 266 293 h = tr_sessionInitFull( 267 268 "cli",/* tag */269 downloadDir,/* where to download torrents */270 271 natTraversal,/* nat enabled */272 273 274 275 276 277 278 279 280 verboseLevel + 1,/* messageLevel */281 0,/* is message queueing enabled? */282 283 284 285 286 287 288 289 290 291 292 293 294 294 configdir, 295 "cli", /* tag */ 296 downloadDir, /* where to download torrents */ 297 TR_DEFAULT_PEX_ENABLED, 298 natTraversal, /* nat enabled */ 299 peerPort, 300 encryptionMode, 301 TR_DEFAULT_LAZY_BITFIELD_ENABLED, 302 uploadLimit >= 0, 303 uploadLimit, 304 downloadLimit >= 0, 305 downloadLimit, 306 TR_DEFAULT_GLOBAL_PEER_LIMIT, 307 verboseLevel + 1, /* messageLevel */ 308 0, /* is message queueing enabled? */ 309 blocklistEnabled, 310 peerSocketTOS, 311 TR_DEFAULT_RPC_ENABLED, 312 TR_DEFAULT_RPC_PORT, 313 TR_DEFAULT_RPC_ACL, 314 FALSE, "fnord", "potzrebie", 315 TR_DEFAULT_PROXY_ENABLED, 316 TR_DEFAULT_PROXY, 317 TR_DEFAULT_PROXY_PORT, 318 TR_DEFAULT_PROXY_TYPE, 319 TR_DEFAULT_PROXY_AUTH_ENABLED, 320 TR_DEFAULT_PROXY_USERNAME, 321 TR_DEFAULT_PROXY_PASSWORD ); 295 322 296 323 if( sourceFile && *sourceFile ) /* creating a torrent */ 297 324 { 298 int err;325 int err; 299 326 tr_metainfo_builder * b = tr_metaInfoBuilderCreate( h, sourceFile ); 300 tr_tracker_info ti;327 tr_tracker_info ti; 301 328 ti.tier = 0; 302 329 ti.announce = (char*) announce; 303 330 tr_makeMetaInfo( b, torrentPath, &ti, 1, comment, isPrivate ); 304 while( !b->isDone ) { 331 while( !b->isDone ) 332 { 305 333 tr_wait( 1000 ); 306 334 printf( "." ); 307 335 } 336 308 337 err = b->result; 309 338 tr_metaInfoBuilderFree( b ); … … 322 351 if( !tr_torrentParse( h, ctor, &info ) ) 323 352 { 324 int i;353 int i; 325 354 const time_t start = time( NULL ); 326 for( i =0; i<info.trackerCount; ++i )355 for( i = 0; i < info.trackerCount; ++i ) 327 356 { 328 357 if( info.trackers[i].scrape ) 329 358 { 330 359 const char * scrape = info.trackers[i].scrape; 331 char escaped[SHA_DIGEST_LENGTH*3 + 1];332 char * url, *host;360 char escaped[SHA_DIGEST_LENGTH * 3 + 1]; 361 char * url, *host; 333 362 escape( escaped, info.hash, SHA_DIGEST_LENGTH ); 334 363 url = tr_strdup_printf( "%s%cinfo_hash=%s", 335 364 scrape, 336 strchr(scrape,'?')?'&':'?', 365 strchr( scrape, 366 '?' ) ? '&' : '?', 337 367 escaped ); 338 368 tr_httpParseURL( scrape, -1, &host, NULL, NULL ); … … 346 376 fprintf( stderr, "scraping %d trackers:\n", leftToScrape ); 347 377 348 while( leftToScrape >0 && ((time(NULL)-start)<20) )378 while( leftToScrape > 0 && ( ( time( NULL ) - start ) < 20 ) ) 349 379 tr_wait( 250 ); 350 380 } … … 382 412 tr_torrentStart( tor ); 383 413 384 if( verify ) { 414 if( verify ) 415 { 385 416 verify = 0; 386 417 tr_torrentVerify( tor ); 387 418 } 388 419 389 for( ; ; )390 { 391 char line[LINEWIDTH];420 for( ; ; ) 421 { 422 char line[LINEWIDTH]; 392 423 const tr_stat * st; 393 424 394 425 tr_wait( 200 ); 395 426 396 if( gotsig ) { 427 if( gotsig ) 428 { 397 429 gotsig = 0; 398 430 printf( "\nStopping torrent...\n" ); 399 431 tr_torrentStop( tor ); 400 432 } 401 402 if( manualUpdate ) { 433 434 if( manualUpdate ) 435 { 403 436 manualUpdate = 0; 404 if ( !tr_torrentCanManualUpdate( tor ) ) 405 fprintf( stderr, "\nReceived SIGHUP, but can't send a manual update now\n" ); 406 else { 407 fprintf( stderr, "\nReceived SIGHUP: manual update scheduled\n" ); 437 if( !tr_torrentCanManualUpdate( tor ) ) 438 fprintf( 439 stderr, 440 "\nReceived SIGHUP, but can't send a manual update now\n" ); 441 else 442 { 443 fprintf( stderr, 444 "\nReceived SIGHUP: manual update scheduled\n" ); 408 445 tr_torrentManualUpdate( tor ); 409 446 } … … 437 474 return "A fast and easy BitTorrent client\n" 438 475 "\n" 439 "Usage: "MY_NAME" [options] <torrent-filename>"; 440 } 441 442 static const struct tr_option options[] = { 443 { 'a', "announce", "Set the new torrent's announce URL", "a", 1, "<url>" }, 444 { 'b', "blocklist", "Enable peer blocklists", "b", 0, NULL }, 445 { 'B', "no-blocklist", "Disable peer blocklists", "B", 0, NULL }, 446 { 'c', "comment", "Set the new torrent's comment", "c", 1, "<comment>" }, 447 { 'd', "downlimit", "Set max download speed in KB/s", "d", 1, "<speed>" }, 448 { 'D', "no-downlimit", "Don't limit the download speed", "D", 0, NULL }, 449 { 910, "encryption-required", "Encrypt all peer connections", "er", 0, NULL }, 450 { 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", 0, NULL }, 451 { 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", 0, NULL }, 452 { 'f', "finish", "Run a script when the torrent finishes", 476 "Usage: " MY_NAME " [options] <torrent-filename>"; 477 } 478 479 static const struct tr_option options[] = 480 { 481 { 'a', "announce", "Set the new torrent's announce URL", 482 "a", 1, "<url>" }, 483 { 'b', "blocklist", "Enable peer blocklists", 484 "b", 0, NULL }, 485 { 'B', "no-blocklist", "Disable peer blocklists", 486 "B", 0, NULL }, 487 { 'c', "comment", "Set the new torrent's comment", 488 "c", 1, "<comment>" }, 489 { 'd', "downlimit", "Set max download speed in KB/s", 490 "d", 1, "<speed>" }, 491 { 'D', "no-downlimit", "Don't limit the download speed", 492 "D", 0, NULL }, 493 { 910, "encryption-required", "Encrypt all peer connections", 494 "er", 0, NULL }, 495 { 911, "encryption-preferred", "Prefer encrypted peer connections", 496 "ep", 0, NULL }, 497 { 912, "encryption-tolerated", "Prefer unencrypted peer connections", 498 "et", 0, NULL }, 499 { 'f', "finish", "Run a script when the torrent finishes", 453 500 "f", 1, "<script>" }, 454 { 'g', "config-dir", "Where to find configuration files",501 { 'g', "config-dir", "Where to find configuration files", 455 502 "g", 1, "<path>" }, 456 { 'i', "info", "Show torrent details and exit", "i", 0, NULL }, 457 { 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", 0, NULL }, 458 { 'M', "no-portmap", "Disable portmapping", "M", 0, NULL }, 459 { 'n', "new", "Create a new torrent", 503 { 'i', "info", "Show torrent details and exit", 504 "i", 0, NULL }, 505 { 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", 506 "m", 0, NULL }, 507 { 'M', "no-portmap", "Disable portmapping", 508 "M", 0, NULL }, 509 { 'n', "new", "Create a new torrent", 460 510 "n", 1, "<source>" }, 461 511 { 'p', "port", 462 "Port for incoming peers (Default: " TR_DEFAULT_PORT_STR")",512 "Port for incoming peers (Default: " TR_DEFAULT_PORT_STR ")", 463 513 "p", 1, "<port>" }, 464 { 'r', "private", "Set the new torrent's 'private' flag", "r", 0, NULL }, 465 { 's', "scrape", "Scrape the torrent and exit", "s", 0, NULL }, 514 { 'r', "private", "Set the new torrent's 'private' flag", 515 "r", 0, NULL }, 516 { 's', "scrape", "Scrape the torrent and exit", 517 "s", 0, NULL }, 466 518 { 't', "tos", 467 "Peer socket TOS (0 to 255, default="TR_DEFAULT_PEER_SOCKET_TOS_STR")", 519 "Peer socket TOS (0 to 255, default=" TR_DEFAULT_PEER_SOCKET_TOS_STR 520 ")", 468 521 "t", 1, "<tos>" }, 469 { 'u', "uplimit", "Set max upload speed in KB/s", "u", 1, "<speed>" }, 470 { 'U', "no-uplimit", "Don't limit the upload speed", "U", 0, NULL }, 471 { 'v', "verify", "Verify the specified torrent", "v", 0, NULL }, 472 { 'w', "download-dir", "Where to save downloaded data", "w", 1, "<path>" }, 473 { 0, NULL, NULL, NULL, 0, NULL } 522 { 'u', "uplimit", "Set max upload speed in KB/s", 523 "u", 1, "<speed>" }, 524 { 'U', "no-uplimit", "Don't limit the upload speed", 525 "U", 0, NULL }, 526 { 'v', "verify", "Verify the specified torrent", 527 "v", 0, NULL }, 528 { 'w', "download-dir", "Where to save downloaded data", 529 "w", 1, "<path>" }, 530 { 0, NULL, NULL, 531 NULL, 0, NULL } 474 532 }; 475 533 … … 477 535 showUsage( void ) 478 536 { 479 tr_getopt_usage( MY_NAME, getUsage( ), options );537 tr_getopt_usage( MY_NAME, getUsage( ), options ); 480 538 exit( 0 ); 481 539 } … … 484 542 numarg( const char * arg ) 485 543 { 486 char * end = NULL;544 char * end = NULL; 487 545 const long num = strtol( arg, &end, 10 ); 488 if( *end ) { 546 547 if( *end ) 548 { 489 549 fprintf( stderr, "Not a number: \"%s\"\n", arg ); 490 550 showUsage( ); … … 494 554 495 555 static int 496 parseCommandLine( int argc, const char ** argv ) 497 { 498 int c; 556 parseCommandLine( int argc, 557 const char ** argv ) 558 { 559 int c; 499 560 const char * optarg; 500 561 501 while( ( c = tr_getopt( getUsage(), argc, argv, options, &optarg )))562 while( ( c = tr_getopt( getUsage( ), argc, argv, options, &optarg ) ) ) 502 563 { 503 564 switch( c ) 504 565 { 505 case 'a': announce = optarg; break; 506 case 'b': blocklistEnabled = 1; break; 507 case 'B': blocklistEnabled = 0; break; 508 case 'c': comment = optarg; break; 509 case 'd': downloadLimit = numarg( optarg ); break; 510 case 'D': downloadLimit = -1; break; 511 case 'f': finishCall = optarg; break; 512 case 'g': configdir = optarg; break; 513 case 'i': showInfo = 1; break; 514 case 'm': natTraversal = 1; break; 515 case 'M': natTraversal = 0; break; 516 case 'n': sourceFile = optarg; break; 517 case 'p': peerPort = numarg( optarg ); break; 518 case 'r': isPrivate = 1; break; 519 case 's': showScrape = 1; break; 520 case 't': peerSocketTOS = numarg( optarg ); break; 521 case 'u': uploadLimit = numarg( optarg ); break; 522 case 'U': uploadLimit = -1; break; 523 case 'v': verify = 1; break; 524 case 'w': downloadDir = optarg; break; 525 case 910: encryptionMode = TR_ENCRYPTION_REQUIRED; break; 526 case 911: encryptionMode = TR_CLEAR_PREFERRED; break; 527 case 912: encryptionMode = TR_ENCRYPTION_PREFERRED; break; 528 case TR_OPT_UNK: torrentPath = optarg; break; 529 default: return 1; 566 case 'a': 567 announce = optarg; break; 568 569 case 'b': 570 blocklistEnabled = 1; break; 571 572 case 'B': 573 blocklistEnabled = 0; break; 574 575 case 'c': 576 comment = optarg; break; 577 578 case 'd': 579 downloadLimit = numarg( optarg ); break; 580 581 case 'D': 582 downloadLimit = -1; break; 583 584 case 'f': 585 finishCall = optarg; break; 586 587 case 'g': 588 configdir = optarg; break; 589 590 case 'i': 591 showInfo = 1; break; 592 593 case 'm': 594 natTraversal = 1; break; 595 596 case 'M': 597 natTraversal = 0; break; 598 599 case 'n': 600 sourceFile = optarg; break; 601 602 case 'p': 603 peerPort = numarg( optarg ); break; 604 605 case 'r': 606 isPrivate = 1; break; 607 608 case 's': 609 showScrape = 1; break; 610 611 case 't': 612 peerSocketTOS = numarg( optarg ); break; 613 614 case 'u': 615 uploadLimit = numarg( optarg ); break; 616 617 case 'U': 618 uploadLimit = -1; break; 619 620 case 'v': 621 verify = 1; break; 622 623 case 'w': 624 downloadDir = optarg; break; 625 626 case 910: 627 encryptionMode = TR_ENCRYPTION_REQUIRED; break; 628 629 case 911: 630 encryptionMode = TR_CLEAR_PREFERRED; break; 631 632 case 912: 633 encryptionMode = TR_ENCRYPTION_PREFERRED; break; 634 635 case TR_OPT_UNK: 636 torrentPath = optarg; break; 637 638 default: 639 return 1; 530 640 } 531 641 } … … 539 649 switch( signal ) 540 650 { 541 case SIGINT: gotsig = 1; break; 651 case SIGINT: 652 gotsig = 1; break; 653 542 654 #ifndef WIN32 543 case SIGHUP: manualUpdate = 1; break; 655 case SIGHUP: 656 manualUpdate = 1; break; 657 544 658 #endif 545 default: break; 546 } 547 } 659 default: 660 break; 661 } 662 } 663 -
trunk/daemon/daemon.c
r6703 r6795 4 4 * This file is licensed by the GPL version 2. Works owned by the 5 5 * Transmission project are granted a special exemption to clause 2(b) 6 * so that the bulk of its code can remain under the MIT license. 6 * so that the bulk of its code can remain under the MIT license. 7 7 * This exemption does not extend to derived works not owned by 8 8 * the Transmission project. … … 13 13 #include <assert.h> 14 14 #include <errno.h> 15 #include <stdio.h> /* printf */ 15 #include <stdio.h> /* printf */ 16 16 #include <stdlib.h> /* exit, atoi */ 17 17 #include <string.h> /* strcmp */ … … 30 30 #define MY_NAME "transmission-daemon" 31 31 32 static int closing = FALSE;32 static int closing = FALSE; 33 33 static tr_handle * mySession; 34 static char myConfigFilename[MAX_PATH_LENGTH];34 static char myConfigFilename[MAX_PATH_LENGTH]; 35 35 36 36 #define KEY_BLOCKLIST "blocklist-enabled" … … 59 59 60 60 static void 61 replaceInt( tr_benc * dict, const char * key, int64_t value ) 61 replaceInt( tr_benc * dict, 62 const char * key, 63 int64_t value ) 62 64 { 63 65 tr_bencDictRemove( dict, key ); 64 66 tr_bencDictAddInt( dict, key, value ); 65 67 } 66 static void 67 replaceStr( tr_benc * dict, const char * key, const char* value ) 68 69 static void 70 replaceStr( tr_benc * dict, 71 const char * key, 72 const char* value ) 68 73 { 69 74 tr_bencDictRemove( dict, key ); 70 75 tr_bencDictAddStr( dict, key, value ); 71 76 } 77 72 78 static void 73 79 saveState( tr_session * s ) 74 80 { 75 int i, n = 0;76 char * strs[4];81 int i, n = 0; 82 char * strs[4]; 77 83 78 84 tr_benc d; 85 79 86 if( tr_bencLoadJSONFile( myConfigFilename, &d ) ) 80 87 tr_bencInitDict( &d, 16 ); 81 88 82 89 replaceInt( &d, KEY_BLOCKLIST, tr_blocklistIsEnabled( s ) ); 83 90 replaceStr( &d, KEY_DOWNLOAD_DIR, tr_sessionGetDownloadDir( s ) ); 84 91 replaceInt( &d, KEY_PEER_LIMIT, tr_sessionGetPeerLimit( s ) ); 85 92 replaceInt( &d, KEY_PEER_PORT, tr_sessionGetPeerPort( s ) ); 86 replaceInt( &d, KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) ); 93 replaceInt( &d, KEY_PORT_FORWARDING, 94 tr_sessionIsPortForwardingEnabled( s ) ); 87 95 replaceInt( &d, KEY_PEX_ENABLED, tr_sessionIsPexEnabled( s ) ); 88 replaceStr( &d, KEY_USERNAME, strs[n++] = tr_sessionGetRPCUsername( s ) ); 89 replaceStr( &d, KEY_PASSWORD, strs[n++] = tr_sessionGetRPCPassword( s ) ); 96 replaceStr( &d, KEY_USERNAME, strs[n++] = 97 tr_sessionGetRPCUsername( 98 s ) ); 99 replaceStr( &d, KEY_PASSWORD, strs[n++] = 100 tr_sessionGetRPCPassword( 101 s ) ); 90 102 replaceStr( &d, KEY_ACL, strs[n++] = tr_sessionGetRPCACL( s ) ); 91 103 replaceInt( &d, KEY_RPC_PORT, tr_sessionGetRPCPort( s ) ); 92 104 replaceInt( &d, KEY_AUTH_REQUIRED, tr_sessionIsRPCPasswordEnabled( s ) ); 93 replaceInt( &d, KEY_DSPEED, tr_sessionGetSpeedLimit( s, TR_DOWN ) ); 94 replaceInt( &d, KEY_DSPEED_ENABLED, tr_sessionIsSpeedLimitEnabled( s, TR_DOWN ) ); 105 replaceInt( &d, KEY_DSPEED, 106 tr_sessionGetSpeedLimit( s, TR_DOWN ) ); 107 replaceInt( &d, KEY_DSPEED_ENABLED, 108 tr_sessionIsSpeedLimitEnabled( s, TR_DOWN ) ); 95 109 replaceInt( &d, KEY_USPEED, tr_sessionGetSpeedLimit( s, TR_UP ) ); 96 replaceInt( &d, KEY_USPEED_ENABLED, tr_sessionIsSpeedLimitEnabled( s, TR_UP ) ); 110 replaceInt( &d, KEY_USPEED_ENABLED, 111 tr_sessionIsSpeedLimitEnabled( s, TR_UP ) ); 97 112 replaceInt( &d, KEY_ENCRYPTION, tr_sessionGetEncryption( s ) ); 98 113 … … 101 116 tr_ninf( MY_NAME, "saved \"%s\"", myConfigFilename ); 102 117 103 for( i =0; i<n; ++i )118 for( i = 0; i < n; ++i ) 104 119 tr_free( strs[i] ); 105 120 } 106 121 107 122 static void 108 getConfigInt( tr_benc * dict, 109 const char * key, 110 int * setme, 111 int defaultVal ) 112 { 113 if( *setme < 0 ) { 123 getConfigInt( tr_benc * dict, 124 const char * key, 125 int * setme, 126 int defaultVal ) 127 { 128 if( *setme < 0 ) 129 { 114 130 int64_t i; 115 131 if( tr_bencDictFindInt( dict, key, &i ) ) … … 121 137 122 138 static void 123 getConfigStr( tr_benc * dict, 124 const char * key, 125 const char ** setme, 126 const char * defaultVal ) 127 { 128 if( !*setme ) { 139 getConfigStr( tr_benc * dict, 140 const char * key, 141 const char ** setme, 142 const char * defaultVal ) 143 { 144 if( !*setme ) 145 { 129 146 const char * s; 130 147 if( tr_bencDictFindStr( dict, key, &s ) ) … … 141 158 */ 142 159 static void 143 session_init( const char * configDir, const char * downloadDir, 144 int rpcPort, const char * acl, 145 int authRequired, const char * username, const char * password, 146 int blocklistEnabled ) 147 { 148 char mycwd[MAX_PATH_LENGTH]; 149 tr_benc state, *dict = NULL; 150 int peerPort=-1, peers=-1; 151 int pexEnabled = -1; 152 int fwdEnabled = -1; 153 int upLimit=-1, upLimited=-1, downLimit=-1, downLimited=-1; 154 int encryption = -1; 155 int useLazyBitfield = -1; 156 tr_ctor * ctor; 160 session_init( const char * configDir, 161 const char * downloadDir, 162 int rpcPort, 163 const char * acl, 164 int authRequired, 165 const char * username, 166 const char * password, 167 int blocklistEnabled ) 168 { 169 char mycwd[MAX_PATH_LENGTH]; 170 tr_benc state, *dict = NULL; 171 int peerPort = -1, peers = -1; 172 int pexEnabled = -1; 173 int fwdEnabled = -1; 174 int upLimit = -1, upLimited = -1, downLimit = -1, 175 downLimited = -1; 176 int encryption = -1; 177 int useLazyBitfield = -1; 178 tr_ctor * ctor; 157 179 tr_torrent ** torrents; 158 180 … … 169 191 getcwd( mycwd, sizeof( mycwd ) ); 170 192 getConfigStr( dict, KEY_DOWNLOAD_DIR, &downloadDir, mycwd ); 171 getConfigInt( dict, KEY_PEX_ENABLED, &pexEnabled, TR_DEFAULT_PEX_ENABLED ); 172 getConfigInt( dict, KEY_PORT_FORWARDING, &fwdEnabled, TR_DEFAULT_PORT_FORWARDING_ENABLED ); 173 getConfigInt( dict, KEY_PEER_PORT, &peerPort, TR_DEFAULT_PORT ); 193 getConfigInt( dict, KEY_PEX_ENABLED, &pexEnabled, 194 TR_DEFAULT_PEX_ENABLED ); 195 getConfigInt( dict, KEY_PORT_FORWARDING, &fwdEnabled, 196 TR_DEFAULT_PORT_FORWARDING_ENABLED ); 197 getConfigInt( dict, KEY_PEER_PORT, &peerPort, 198 TR_DEFAULT_PORT ); 174 199 getConfigInt( dict, KEY_DSPEED, &downLimit, 100 ); 175 200 getConfigInt( dict, KEY_DSPEED_ENABLED, &downLimited, FALSE ); 176 201 getConfigInt( dict, KEY_USPEED, &upLimit, 100 ); 177 202 getConfigInt( dict, KEY_USPEED_ENABLED, &upLimited, FALSE ); 178 getConfigInt( dict, KEY_LAZY_BITFIELD, &useLazyBitfield, TR_DEFAULT_LAZY_BITFIELD_ENABLED ); 179 getConfigInt( dict, KEY_PEER_LIMIT, &peers, TR_DEFAULT_GLOBAL_PEER_LIMIT ); 180 getConfigInt( dict, KEY_BLOCKLIST, &blocklistEnabled, TR_DEFAULT_BLOCKLIST_ENABLED ); 181 getConfigInt( dict, KEY_RPC_PORT, &rpcPort, TR_DEFAULT_RPC_PORT ); 182 getConfigStr( dict, KEY_ACL, &acl, TR_DEFAULT_RPC_ACL ); 203 getConfigInt( dict, KEY_LAZY_BITFIELD, &useLazyBitfield, 204 TR_DEFAULT_LAZY_BITFIELD_ENABLED ); 205 getConfigInt( dict, KEY_PEER_LIMIT, &peers, 206 TR_DEFAULT_GLOBAL_PEER_LIMIT ); 207 getConfigInt( dict, KEY_BLOCKLIST, &blocklistEnabled, 208 TR_DEFAULT_BLOCKLIST_ENABLED ); 209 getConfigInt( dict, KEY_RPC_PORT, &rpcPort, 210 TR_DEFAULT_RPC_PORT ); 211 getConfigStr( dict, KEY_ACL, &acl, 212 TR_DEFAULT_RPC_ACL ); 183 213 getConfigInt( dict, KEY_AUTH_REQUIRED, &authRequired, FALSE ); 184 214 getConfigStr( dict, KEY_USERNAME, &username, NULL ); 185 215 getConfigStr( dict, KEY_PASSWORD, &password, NULL ); 186 getConfigInt( dict, KEY_ENCRYPTION, &encryption, TR_ENCRYPTION_PREFERRED ); 216 getConfigInt( dict, KEY_ENCRYPTION, &encryption, 217 TR_ENCRYPTION_PREFERRED ); 187 218 188 219 /*** … … 201 232 blocklistEnabled, 202 233 TR_DEFAULT_PEER_SOCKET_TOS, 203 TRUE, rpcPort, acl, authRequired, username, password, 234 TRUE, rpcPort, acl, authRequired, 235 username, password, 204 236 TR_DEFAULT_PROXY_ENABLED, 205 237 TR_DEFAULT_PROXY, … … 227 259 getUsage( void ) 228 260 { 229 return "Transmission "LONG_VERSION_STRING" http://www.transmissionbt.com/\n" 261 return "Transmission " LONG_VERSION_STRING 262 " http://www.transmissionbt.com/\n" 230 263 "A fast and easy BitTorrent client\n" 231 264 "\n" 232 MY_NAME" is a headless Transmission session\n" 233 "that can be controlled via transmission-remote or Clutch.\n" 234 "\n" 235 "Usage: "MY_NAME" [options]"; 236 } 237 238 static const struct tr_option options[] = { 239 { 'a', "acl", "Access Control List. (Default: "TR_DEFAULT_RPC_ACL")", "a", 1, "<list>" }, 240 { 'b', "blocklist", "Enable peer blocklists", "b", 0, NULL }, 241 { 'B', "no-blocklist", "Disable peer blocklists", "B", 0, NULL }, 242 { 'f', "foreground", "Run in the foreground instead of daemonizing", "f", 0, NULL }, 243 { 'g', "config-dir", "Where to look for configuration files", "g", 1, "<path>" }, 244 { 'p', "port", "RPC port (Default: "TR_DEFAULT_RPC_PORT_STR")", "p", 1, "<port>" }, 245 { 't', "auth", "Require authentication", "t", 0, NULL }, 246 { 'T', "no-auth", "Don't require authentication", "T", 0, NULL }, 247 { 'u', "username", "Set username for authentication", "u", 1, "<username>" }, 248 { 'v', "password", "Set password for authentication", "v", 1, "<password>" }, 249 { 'w', "download-dir", "Where to save downloaded data", "w", 1, "<path>" }, 250 { 0, NULL, NULL, NULL, 0, NULL } 265 MY_NAME " is a headless Transmission session\n" 266 "that can be controlled via transmission-remote or Clutch.\n" 267 "\n" 268 "Usage: " MY_NAME " [options]"; 269 } 270 271 static const struct tr_option options[] = 272 { 273 { 'a', "acl", 274 "Access Control List. (Default: " TR_DEFAULT_RPC_ACL ")", "a", 275 1, "<list>" }, 276 { 'b', "blocklist", "Enable peer blocklists", 277 "b", 0, NULL }, 278 { 'B', "no-blocklist", "Disable peer blocklists", 279 "B", 0, NULL }, 280 { 'f', "foreground", "Run in the foreground instead of daemonizing", 281 "f", 0, NULL }, 282 { 'g', "config-dir", "Where to look for configuration files", 283 "g", 1, "<path>" }, 284 { 'p', "port", 285 "RPC port (Default: " TR_DEFAULT_RPC_PORT_STR ")", "p", 286 1, "<port>" }, 287 { 't', "auth", "Require authentication", 288 "t", 0, NULL }, 289 { 'T', "no-auth", "Don't require authentication", 290 "T", 0, NULL }, 291 { 'u', "username", "Set username for authentication", 292 "u", 1, "<username>" }, 293 { 'v', "password", "Set password for authentication", 294 "v", 1, "<password>" }, 295 { 'w', "download-dir", "Where to save downloaded data", 296 "w", 1, "<path>" }, 297 { 0, NULL, NULL, 298 NULL, 0, NULL } 251 299 }; 252 300 … … 254 302 showUsage( void ) 255 303 { 256 tr_getopt_usage( MY_NAME, getUsage( ), options );304 tr_getopt_usage( MY_NAME, getUsage( ), options ); 257 305 exit( 0 ); 258 306 } 259 307 260 308 static void 261 readargs( int argc, const char ** argv, 262 int * nofork, const char ** configDir, const char ** downloadDir, 263 int * rpcPort, const char ** acl, 264 int * authRequired, const char ** username, const char ** password, 265 int * blocklistEnabled ) 266 { 267 int c; 309 readargs( int argc, 310 const char ** argv, 311 int * nofork, 312 const char ** configDir, 313 const char ** downloadDir, 314 int * rpcPort, 315 const char ** acl, 316 int * authRequired, 317 const char ** username, 318 const char ** password, 319 int * blocklistEnabled ) 320 { 321 int c; 268 322 const char * optarg; 269 while(( c = tr_getopt( getUsage(), argc, argv, options, &optarg ))) 323 324 while( ( c = tr_getopt( getUsage( ), argc, argv, options, &optarg ) ) ) 270 325 { 271 326 switch( c ) 272 327 { 273 case 'a': *acl = optarg; break; 274 case 'b': *blocklistEnabled = 1; break; 275 case 'B': *blocklistEnabled = 0; break; 276 case 'f': *nofork = 1; break; 277 case 'g': *configDir = optarg; break; 278 case 'p': *rpcPort = atoi( optarg ); break; 279 case 't': *authRequired = TRUE; break; 280 case 'T': *authRequired = FALSE; break; 281 case 'u': *username = optarg; break; 282 case 'v': *password = optarg; break; 283 case 'w': *downloadDir = optarg; break; 284 default: showUsage( ); break; 328 case 'a': 329 *acl = optarg; break; 330 331 case 'b': 332 *blocklistEnabled = 1; break; 333 334 case 'B': 335 *blocklistEnabled = 0; break; 336 337 case 'f': 338 *nofork = 1; break; 339 340 case 'g': 341 *configDir = optarg; break; 342 343 case 'p': 344 *rpcPort = atoi( optarg ); break; 345 346 case 't': 347 *authRequired = TRUE; break; 348 349 case 'T': 350 *authRequired = FALSE; break; 351 352 case 'u': 353 *username = optarg; break; 354 355 case 'v': 356 *password = optarg; break; 357 358 case 'w': 359 *downloadDir = optarg; break; 360 361 default: 362 showUsage( ); break; 285 363 } 286 364 } … … 293 371 } 294 372 295 #if !defined( HAVE_DAEMON)373 #if !defined( HAVE_DAEMON ) 296 374 static int 297 daemon( int nochdir, int noclose ) 298 { 299 switch( fork( ) ) { 375 daemon( int nochdir, 376 int noclose ) 377 { 378 switch( fork( ) ) 379 { 300 380 case 0: 301 381 break; 302 case -1: 303 tr_nerr( MY_NAME, "Error daemonizing (fork)! %d - %s", errno, strerror(errno) ); 382 383 case - 1: 384 tr_nerr( MY_NAME, "Error daemonizing (fork)! %d - %s", errno, 385 strerror( 386 errno ) ); 304 387 return -1; 388 305 389 default: 306 _exit(0); 307 } 308 309 if( setsid() < 0 ) { 310 tr_nerr( MY_NAME, "Error daemonizing (setsid)! %d - %s", errno, strerror(errno) ); 390 _exit( 0 ); 391 } 392 393 if( setsid( ) < 0 ) 394 { 395 tr_nerr( MY_NAME, "Error daemonizing (setsid)! %d - %s", errno, 396 strerror( 397 errno ) ); 311 398 return -1; 312 399 } 313 400 314 switch( fork( ) ) { 401 switch( fork( ) ) 402 { 315 403 case 0: 316 404 break; 317 case -1: 318 tr_nerr( MY_NAME, "Error daemonizing (fork2)! %d - %s", errno, strerror(errno) ); 405 406 case - 1: 407 tr_nerr( MY_NAME, "Error daemonizing (fork2)! %d - %s", errno, 408 strerror( 409 errno ) ); 319 410 return -1; 411 320 412 default: 321 _exit(0); 322 } 323 324 if( !nochdir && 0 > chdir( "/" ) ) { 325 tr_nerr( MY_NAME, "Error daemonizing (chdir)! %d - %s", errno, strerror(errno) ); 413 _exit( 0 ); 414 } 415 416 if( !nochdir && 0 > chdir( "/" ) ) 417 { 418 tr_nerr( MY_NAME, "Error daemonizing (chdir)! %d - %s", errno, 419 strerror( 420 errno ) ); 326 421 return -1; 327 422 } 328 423 329 if( !noclose ) { 424 if( !noclose ) 425 { 330 426 int fd; 331 if((( fd = open("/dev/null", O_RDONLY))) != 0 ) { 427 if( ( ( fd = open( "/dev/null", O_RDONLY ) ) ) != 0 ) 428 { 332 429 dup2( fd, 0 ); 333 430 close( fd ); 334 431 } 335 if((( fd = open("/dev/null", O_WRONLY))) != 1 ) { 432 if( ( ( fd = open( "/dev/null", O_WRONLY ) ) ) != 1 ) 433 { 336 434 dup2( fd, 1 ); 337 435 close( fd ); 338 436 } 339 if((( fd = open("/dev/null", O_WRONLY))) != 2 ) { 437 if( ( ( fd = open( "/dev/null", O_WRONLY ) ) ) != 2 ) 438 { 340 439 dup2( fd, 2 ); 341 440 close( fd ); … … 345 444 return 0; 346 445 } 446 347 447 #endif 348 448 349 449 int 350 main( int argc, char ** argv ) 351 { 352 int nofork = 0; 353 int rpcPort = -1; 354 int authRequired = -1; 355 int blocklistEnabled = -1; 356 char * freeme = NULL; 450 main( int argc, 451 char ** argv ) 452 { 453 int nofork = 0; 454 int rpcPort = -1; 455 int authRequired = -1; 456 int blocklistEnabled = -1; 457 char * freeme = NULL; 357 458 const char * configDir = NULL; 358 459 const char * downloadDir = NULL; … … 371 472 &blocklistEnabled ); 372 473 if( configDir == NULL ) 373 configDir = freeme = tr_strdup_printf( "%s-daemon", tr_getDefaultConfigDir() ); 474 configDir = freeme = tr_strdup_printf( "%s-daemon", 475 tr_getDefaultConfigDir( ) ); 374 476 tr_buildPath( myConfigFilename, sizeof( myConfigFilename ), 375 477 configDir, CONFIG_FILE, NULL ); 376 478 377 if( !nofork ) { 378 if( 0 > daemon( 1, 0 ) ) { 479 if( !nofork ) 480 { 481 if( 0 > daemon( 1, 0 ) ) 482 { 379 483 fprintf( stderr, "failed to daemonize: %s\n", strerror( errno ) ); 380 484 exit( 1 ); … … 397 501 return 0; 398 502 } 503 -
trunk/daemon/remote.c
r6703 r6795 4 4 * This file is licensed by the GPL version 2. Works owned by the 5 5 * Transmission project are granted a special exemption to clause 2(b) 6 * so that the bulk of its code can remain under the MIT license. 6 * so that the bulk of its code can remain under the MIT license. 7 7 * This exemption does not extend to derived works not owned by 8 8 * the Transmission project. … … 39 39 { 40 40 return 41 "Transmission "LONG_VERSION_STRING" http://www.transmissionbt.com/\n" 42 "A fast and easy BitTorrent client\n" 43 "\n" 44 "Usage: "MY_NAME" [host] [options]\n" 45 " "MY_NAME" [port] [options]\n" 46 " "MY_NAME" [host:port] [options]\n" 47 "\n" 48 "See the man page for detailed explanations and many examples."; 41 "Transmission " LONG_VERSION_STRING 42 " http://www.transmissionbt.com/\n" 43 "A fast and easy BitTorrent client\n" 44 "\n" 45 "Usage: " MY_NAME 46 " [host] [options]\n" 47 " " 48 MY_NAME " [port] [options]\n" 49 " " 50 MY_NAME " [host:port] [options]\n" 51 "\n" 52 "See the man page for detailed explanations and many examples."; 49 53 } 50 54 51 55 static tr_option opts[] = 52 56 { 53 { 'a', "add", "Add torrent files", "a", 0, NULL }, 54 { 'b', "debug", "Print debugging information", "b", 0, NULL }, 55 { 'd', "downlimit", "Set the maximum download speed in KB/s", "d", 1, "<speed>" }, 56 { 'D', "no-downlimit", "Don't limit the download speed", "D", 0, NULL }, 57 { 910, "encryption-required", "Encrypt all peer connections", "er", 0, NULL }, 58 { 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", 0, NULL }, 59 { 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", 0, NULL }, 60 { 'f', "files", "List the current torrent's files", "f", 0, NULL }, 61 { 'g', "get", "Mark files for download", "g", 1, "<files>" }, 62 { 'G', "no-get", "Mark files for not downloading", "G", 1, "<files>" }, 63 { 'i', "info", "Show details of the current torrent(s)", "i", 0, NULL }, 64 { 'l', "list", "List all torrents", "l", 0, NULL }, 65 { 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", 0, NULL }, 66 { 'M', "no-portmap", "Disable portmapping", "M", 0, NULL }, 67 { 'n', "auth", "Set authentication info", "n", 1, "<username:password>" }, 57 { 'a', "add", "Add torrent files", 58 "a", 0, NULL }, 59 { 'b', "debug", "Print debugging information", 60 "b", 0, NULL }, 61 { 'd', "downlimit", "Set the maximum download speed in KB/s", 62 "d", 1, "<speed>" }, 63 { 'D', "no-downlimit", "Don't limit the download speed", 64 "D", 0, NULL }, 65 { 910, "encryption-required", "Encrypt all peer connections", 66 "er", 0, NULL }, 67 { 911, "encryption-preferred", "Prefer encrypted peer connections", 68 "ep", 0, NULL }, 69 { 912, "encryption-tolerated", "Prefer unencrypted peer connections", 70 "et", 0, NULL }, 71 { 'f', "files", "List the current torrent's files", 72 "f", 0, NULL }, 73 { 'g', "get", "Mark files for download", 74 "g", 1, "<files>" }, 75 { 'G', "no-get", "Mark files for not downloading", 76 "G", 1, "<files>" }, 77 { 'i', "info", "Show details of the current torrent(s)", 78 "i", 0, NULL }, 79 { 'l', "list", "List all torrents", 80 "l", 0, NULL }, 81 { 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", 82 "m", 0, NULL }, 83 { 'M', "no-portmap", "Disable portmapping", 84 "M", 0, NULL }, 85 { 'n', "auth", "Set authentication info", 86 "n", 1, "<username:password>" }, 68 87 { 'p', "port", 69 "Port for incoming peers (Default: " TR_DEFAULT_PORT_STR")",88 "Port for incoming peers (Default: " TR_DEFAULT_PORT_STR ")", 70 89 "p", 1, "<port>" }, 71 { 900, "priority-high", "Set the files' priorities as high", "ph", 1, "<files>" }, 72 { 901, "priority-normal", "Set the files' priorities as normal", "pn", 1, "<files>" }, 73 { 902, "priority-low", "Set the files' priorities as low", "pl", 1, "<files>" }, 74 { 'r', "remove", "Remove the current torrent(s)", "r", 0, NULL }, 75 { 's', "start", "Start the current torrent(s)", "s", 0, NULL }, 76 { 'S', "stop", "Stop the current torrent(s)", "S", 0, NULL }, 77 { 't', "torrent", "Set the current torrent(s)", "t", 1, "<torrent>" }, 78 { 'u', "uplimit", "Set the maximum upload speed in KB/s", "u", 1, "<speed>" }, 79 { 'U', "no-uplimit", "Don't limit the upload speed", "U", 0, NULL }, 80 { 'v', "verify", "Verify the current torrent(s)", "v", 0, NULL }, 81 { 'w', "download-dir", "Set the default download folder", "w", 1, "<path>" }, 82 { 'x', "pex", "Enable peer exchange (PEX)", "x", 0, NULL }, 83 { 'X', "no-pex", "Disable peer exchange (PEX)", "X", 0, NULL }, 84 { 'z', "peers", "List the current torrent's peers", "z", 0, NULL }, 85 { 0, NULL, NULL, NULL, 0, NULL } 90 { 900, "priority-high", "Set the files' priorities as high", 91 "ph", 1, "<files>" }, 92 { 901, "priority-normal", "Set the files' priorities as normal", 93 "pn", 1, "<files>" }, 94 { 902, "priority-low", "Set the files' priorities as low", 95 "pl", 1, "<files>" }, 96 { 'r', "remove", "Remove the current torrent(s)", 97 "r", 0, NULL }, 98 { 's', "start", "Start the current torrent(s)", 99 "s", 0, NULL }, 100 { 'S', "stop", "Stop the current torrent(s)", 101 "S", 0, NULL }, 102 { 't', "torrent", "Set the current torrent(s)", 103 "t", 1, "<torrent>" }, 104 { 'u', "uplimit", "Set the maximum upload speed in KB/s", 105 "u", 1, "<speed>" }, 106 { 'U', "no-uplimit", "Don't limit the upload speed", 107 "U", 0, NULL }, 108 { 'v', "verify", "Verify the current torrent(s)", 109 "v", 0, NULL }, 110 { 'w', "download-dir", "Set the default download folder", 111 "w", 1, "<path>" }, 112 { 'x', "pex", "Enable peer exchange (PEX)", 113 "x", 0, NULL }, 114 { 'X', "no-pex", "Disable peer exchange (PEX)", 115 "X", 0, NULL }, 116 { 'z', "peers", "List the current torrent's peers", 117 "z", 0, NULL }, 118 { 0, NULL, NULL, 119 NULL, 0, NULL } 86 120 }; 87 121 … … 89 123 showUsage( void ) 90 124 { 91 tr_getopt_usage( MY_NAME, getUsage( ), opts );125 tr_getopt_usage( MY_NAME, getUsage( ), opts ); 92 126 exit( 0 ); 93 127 } … … 96 130 numarg( const char * arg ) 97 131 { 98 char * end = NULL;132 char * end = NULL; 99 133 const long num = strtol( arg, &end, 10 ); 100 if( *end ) { 134 135 if( *end ) 136 { 101 137 fprintf( stderr, "Not a number: \"%s\"\n", arg ); 102 138 showUsage( ); … … 106 142 107 143 static char * reqs[256]; /* arbitrary max */ 108 static int reqCount = 0;109 static int debug = 0;144 static int reqCount = 0; 145 static int debug = 0; 110 146 static char * auth = NULL; 111 147 112 148 static char* 113 absolutify( char * buf, size_t len, const char * path ) 149 absolutify( char * buf, 150 size_t len, 151 const char * path ) 114 152 { 115 153 if( *path == '/' ) 116 154 tr_strlcpy( buf, path, len ); 117 else { 155 else 156 { 118 157 char cwd[MAX_PATH_LENGTH]; 119 158 getcwd( cwd, sizeof( cwd ) ); … … 126 165 getEncodedMetainfo( const char * filename ) 127 166 { 128 size_t len = 0;129 char * b64 = NULL;167 size_t len = 0; 168 char * b64 = NULL; 130 169 uint8_t * buf = tr_loadFile( filename, &len ); 170 131 171 if( buf ) 132 172 { … … 138 178 139 179 static void 140 addIdArg( tr_benc * args, const char * id ) 141 { 142 if( !*id ) { 143 fprintf( stderr, "No torrent specified! Please use the -t option first.\n" ); 180 addIdArg( tr_benc * args, 181 const char * id ) 182 { 183 if( !*id ) 184 { 185 fprintf( 186 stderr, 187 "No torrent specified! Please use the -t option first.\n" ); 144 188 id = "-1"; /* no torrent will have this ID, so should be a no-op */ 145 189 } 146 if( strcmp( id, "all" ) ) { 147 tr_rpc_parse_list_str( tr_bencDictAdd( args, "ids" ), id, strlen(id) ); 190 if( strcmp( id, "all" ) ) 191 { 192 tr_rpc_parse_list_str( tr_bencDictAdd( args, 193 "ids" ), id, strlen( id ) ); 148 194 } 149 195 } 150 196 151 197 static void 152 addFiles( tr_benc * args, const char * key, const char * arg ) 198 addFiles( tr_benc * args, 199 const char * key, 200 const char * arg ) 153 201 { 154 202 tr_benc * files = tr_bencDictAddList( args, key, 100 ); … … 162 210 { 163 211 const char * walk = arg; 164 while( *walk ) { 165 char * p; 212 while( *walk ) 213 { 214 char * p; 166 215 unsigned long l; 167 216 errno = 0; 168 217 l = strtol( walk, &p, 10 ); 169 218 if( errno ) 170 break; 219 break; 171 220 tr_bencListAddInt( files, l - 1 ); 172 221 if( *p != ',' ) … … 184 233 185 234 static const char * details_keys[] = { 186 "activityDate", "addedDate", "announceResponse", "announceURL", 187 "comment", "corruptEver", "creator", "dateCreated", "doneDate", 188 "downloadedEver", "errorString", "eta", "hashString", "haveUnchecked", 189 "haveValid", "id", "isPrivate", "lastAnnounceTime", "lastScrapeTime", 190 "leechers", "leftUntilDone", "name", "nextAnnounceTime", "nextScrapeTime", 235 "activityDate", "addedDate", "announceResponse", 236 "announceURL", 237 "comment", "corruptEver", "creator", 238 "dateCreated", "doneDate", 239 "downloadedEver", "errorString", "eta", 240 "hashString", "haveUnchecked", 241 "haveValid", "id", "isPrivate", 242 "lastAnnounceTime", "lastScrapeTime", 243 "leechers", "leftUntilDone", "name", 244 "nextAnnounceTime", "nextScrapeTime", 191 245 "peersConnected", "peersGettingFromUs", "peersSendingToUs", 192 "pieceCount", "pieceSize", "rateDownload", "rateUpload", "recheckProgress", 193 "scrapeResponse", "seeders", "sizeWhenDone", "sizeWhenDone", "startDate", 194 "status", "timesCompleted", "totalSize", "uploadedEver", 195 "webseeds", "webseedsSendingToUs" 246 "pieceCount", "pieceSize", "rateDownload", 247 "rateUpload", "recheckProgress", 248 "scrapeResponse", "seeders", "sizeWhenDone", 249 "sizeWhenDone", "startDate", 250 "status", "timesCompleted", "totalSize", 251 "uploadedEver", 252 "webseeds", "webseedsSendingToUs" 196 253 }; 197 254 198 255 static const char * list_keys[] = { 199 "downloadedEver", "eta", "id", "leftUntilDone", "name", "rateDownload", 200 "rateUpload", "sizeWhenDone", "status", "uploadedEver" 256 "downloadedEver", "eta", "id", 257 "leftUntilDone", 258 "name", 259 "rateDownload", 260 "rateUpload", "sizeWhenDone", "status", "uploadedEver" 201 261 }; 202 262 203 263 static void 204 readargs( int argc, const char ** argv ) 205 { 206 int c; 207 int addingTorrents = 0; 264 readargs( int argc, 265 const char ** argv ) 266 { 267 int c; 268 int addingTorrents = 0; 208 269 const char * optarg; 209 char id[4096];270 char id[4096]; 210 271 211 272 *id = '\0'; 212 273 213 while( ( c = tr_getopt( getUsage(), argc, argv, opts, &optarg )))214 { 215 int i, n;216 char buf[MAX_PATH_LENGTH];217 int addArg = TRUE;274 while( ( c = tr_getopt( getUsage( ), argc, argv, opts, &optarg ) ) ) 275 { 276 int i, n; 277 char buf[MAX_PATH_LENGTH]; 278 int addArg = TRUE; 218 279 tr_benc top, *args, *fields; 219 280 tr_bencInitDict( &top, 3 ); … … 223 284 { 224 285 case TR_OPT_UNK: 225 if( addingTorrents ) { 226 char * tmp = getEncodedMetainfo( optarg ); 227 if( tmp ) { 228 tr_bencDictAddStr( &top, "method", "torrent-add" ); 229 tr_bencDictAddStr( args, "metainfo", tmp ); 230 tr_free( tmp ); 231 } else { 232 fprintf( stderr, "Couldn't add file: %s\n", optarg ); 233 addArg = FALSE; 234 } 235 } else { 236 fprintf( stderr, "Unknown option: %s\n", optarg ); 237 addArg = FALSE; 238 } 239 break; 240 case 'a': addingTorrents = 1; 241 addArg = FALSE; 242 break; 243 case 'b': debug = 1; 244 addArg = FALSE; 245 break; 246 case 'd': tr_bencDictAddStr( &top, "method", "session-set" ); 247 tr_bencDictAddInt( args, "speed-limit-down", numarg( optarg ) ); 248 tr_bencDictAddInt( args, "speed-limit-down-enabled", 1 ); 249 break; 250 case 'D': tr_bencDictAddStr( &top, "method", "session-set" ); 251 tr_bencDictAddInt( args, "speed-limit-down-enabled", 0 ); 252 break; 253 case 'f': tr_bencDictAddStr( &top, "method", "torrent-get" ); 254 tr_bencDictAddInt( &top, "tag", TAG_FILES ); 255 addIdArg( args, id ); 256 n = TR_N_ELEMENTS( files_keys ); 257 fields = tr_bencDictAddList( args, "fields", n ); 258 for( i=0; i<n; ++i ) 259 tr_bencListAddStr( fields, files_keys[i] ); 260 break; 261 case 'g': tr_bencDictAddStr( &top, "method", "torrent-set" ); 262 addIdArg( args, id ); 263 addFiles( args, "files-wanted", optarg ); 264 break; 265 case 'G': tr_bencDictAddStr( &top, "method", "torrent-set" ); 266 addIdArg( args, id ); 267 addFiles( args, "files-unwanted", optarg ); 268 break; 269 case 'i': tr_bencDictAddStr( &top, "method", "torrent-get" ); 270 tr_bencDictAddInt( &top, "tag", TAG_DETAILS ); 271 addIdArg( args, id ); 272 n = TR_N_ELEMENTS( details_keys ); 273 fields = tr_bencDictAddList( args, "fields", n ); 274 for( i=0; i<n; ++i ) 275 tr_bencListAddStr( fields, details_keys[i] ); 276 break; 277 case 'l': tr_bencDictAddStr( &top, "method", "torrent-get" ); 278 tr_bencDictAddInt( &top, "tag", TAG_LIST ); 279 n = TR_N_ELEMENTS( list_keys ); 280 fields = tr_bencDictAddList( args, "fields", n ); 281 for( i=0; i<n; ++i ) 282 tr_bencListAddStr( fields, list_keys[i] ); 283 break; 284 case 'm': tr_bencDictAddStr( &top, "method", "session-set" ); 285 tr_bencDictAddInt( args, "port-forwarding-enabled", 1 ); 286 break; 287 case 'M': tr_bencDictAddStr( &top, "method", "session-set" ); 288 tr_bencDictAddInt( args, "port-forwarding-enabled", 0 ); 289 break; 290 case 'n': auth = tr_strdup( optarg ); 291 addArg = FALSE; 292 break; 293 case 'p': tr_bencDictAddStr( &top, "method", "session-set" ); 294 tr_bencDictAddInt( args, "port", numarg( optarg ) ); 295 break; 296 case 'r': tr_bencDictAddStr( &top, "method", "torrent-remove" ); 297 addIdArg( args, id ); 298 break; 299 case 's': tr_bencDictAddStr( &top, "method", "torrent-start" ); 300 addIdArg( args, id ); 301 break; 302 case 'S': tr_bencDictAddStr( &top, "method", "torrent-stop" ); 303 addIdArg( args, id ); 304 break; 305 case 't': tr_strlcpy( id, optarg, sizeof( id ) ); 306 addArg = FALSE; 307 break; 308 case 'u': tr_bencDictAddStr( &top, "method", "session-set" ); 309 tr_bencDictAddInt( args, "speed-limit-up", numarg( optarg ) ); 310 tr_bencDictAddInt( args, "speed-limit-up-enabled", 1 ); 311 break; 312 case 'U': tr_bencDictAddStr( &top, "method", "session-set" ); 313 tr_bencDictAddInt( args, "speed-limit-up-enabled", 0 ); 314 break; 315 case 'v': tr_bencDictAddStr( &top, "method", "torrent-verify" ); 316 addIdArg( args, id ); 317 break; 318 case 'w': tr_bencDictAddStr( &top, "method", "session-set" ); 319 tr_bencDictAddStr( args, "download-dir", 320 absolutify(buf,sizeof(buf),optarg) ); 321 break; 322 case 'x': tr_bencDictAddStr( &top, "method", "session-set" ); 323 tr_bencDictAddInt( args, "pex-allowed", 1 ); 324 break; 325 case 'X': tr_bencDictAddStr( &top, "method", "session-set" ); 326 tr_bencDictAddInt( args, "pex-allowed", 0 ); 327 break; 328 case 'z': tr_bencDictAddStr( &top, "method", "torrent-get" ); 329 tr_bencDictAddInt( &top, "tag", TAG_PEERS ); 330 fields = tr_bencDictAddList( args, "fields", 1 ); 331 tr_bencListAddStr( fields, "peers" ); 332 break; 333 case 900: tr_bencDictAddStr( &top, "method", "torrent-set" ); 334 addIdArg( args, id ); 335 addFiles( args, "priority-high", optarg ); 336 break; 337 case 901: tr_bencDictAddStr( &top, "method", "torrent-set" ); 338 addIdArg( args, id ); 339 addFiles( args, "priority-normal", optarg ); 340 break; 341 case 902: tr_bencDictAddStr( &top, "method", "torrent-set" ); 342 addIdArg( args, id ); 343 addFiles( args, "priority-low", optarg ); 344 break; 345 case 910: tr_bencDictAddStr( &top, "method", "session-set" ); 346 tr_bencDictAddStr( args, "encryption", "required" ); 347 break; 348 case 911: tr_bencDictAddStr( &top, "method", "session-set" ); 349 tr_bencDictAddStr( args, "encryption", "preferred" ); 350 break; 351 case 912: tr_bencDictAddStr( &top, "method", "session-set" ); 352 tr_bencDictAddStr( args, "encryption", "tolerated" ); 353 break; 286 if( addingTorrents ) 287 { 288 char * tmp = getEncodedMetainfo( optarg ); 289 if( tmp ) 290 { 291 tr_bencDictAddStr( &top, "method", "torrent-add" ); 292 tr_bencDictAddStr( args, "metainfo", tmp ); 293 tr_free( tmp ); 294 } 295 else 296 { 297 fprintf( stderr, "Couldn't add file: %s\n", optarg ); 298 addArg = FALSE; 299 } 300 } 301 else 302 { 303 fprintf( stderr, "Unknown option: %s\n", optarg ); 304 addArg = FALSE; 305 } 306 break; 307 308 case 'a': 309 addingTorrents = 1; 310 addArg = FALSE; 311 break; 312 313 case 'b': 314 debug = 1; 315 addArg = FALSE; 316 break; 317 318 case 'd': 319 tr_bencDictAddStr( &top, "method", "session-set" ); 320 tr_bencDictAddInt( args, "speed-limit-down", numarg( optarg ) ); 321 tr_bencDictAddInt( args, "speed-limit-down-enabled", 1 ); 322 break; 323 324 case 'D': 325 tr_bencDictAddStr( &top, "method", "session-set" ); 326 tr_bencDictAddInt( args, "speed-limit-down-enabled", 0 ); 327 break; 328 329 case 'f': 330 tr_bencDictAddStr( &top, "method", "torrent-get" ); 331 tr_bencDictAddInt( &top, "tag", TAG_FILES ); 332 addIdArg( args, id ); 333 n = TR_N_ELEMENTS( files_keys ); 334 fields = tr_bencDictAddList( args, "fields", n ); 335 for( i = 0; i < n; ++i ) 336 tr_bencListAddStr( fields, files_keys[i] ); 337 break; 338 339 case 'g': 340 tr_bencDictAddStr( &top, "method", "torrent-set" ); 341 addIdArg( args, id ); 342 addFiles( args, "files-wanted", optarg ); 343 break; 344 345 case 'G': 346 tr_bencDictAddStr( &top, "method", "torrent-set" ); 347 addIdArg( args, id ); 348 addFiles( args, "files-unwanted", optarg ); 349 break; 350 351 case 'i': 352 tr_bencDictAddStr( &top, "method", "torrent-get" ); 353 tr_bencDictAddInt( &top, "tag", TAG_DETAILS ); 354 addIdArg( args, id ); 355 n = TR_N_ELEMENTS( details_keys ); 356 fields = tr_bencDictAddList( args, "fields", n ); 357 for( i = 0; i < n; ++i ) 358 tr_bencListAddStr( fields, details_keys[i] ); 359 break; 360 361 case 'l': 362 tr_bencDictAddStr( &top, "method", "torrent-get" ); 363 tr_bencDictAddInt( &top, "tag", TAG_LIST ); 364 n = TR_N_ELEMENTS( list_keys ); 365 fields = tr_bencDictAddList( args, "fields", n ); 366 for( i = 0; i < n; ++i ) 367 tr_bencListAddStr( fields, list_keys[i] ); 368 break; 369 370 case 'm': 371 tr_bencDictAddStr( &top, "method", "session-set" ); 372 tr_bencDictAddInt( args, "port-forwarding-enabled", 1 ); 373 break; 374 375 case 'M': 376 tr_bencDictAddStr( &top, "method", "session-set" ); 377 tr_bencDictAddInt( args, "port-forwarding-enabled", 0 ); 378 break; 379 380 case 'n': 381 auth = tr_strdup( optarg ); 382 addArg = FALSE; 383 break; 384 385 case 'p': 386 tr_bencDictAddStr( &top, "method", "session-set" ); 387 tr_bencDictAddInt( args, "port", numarg( optarg ) ); 388 break; 389 390 case 'r': 391 tr_bencDictAddStr( &top, "method", "torrent-remove" ); 392 addIdArg( args, id ); 393 break; 394 395 case 's': 396 tr_bencDictAddStr( &top, "method", "torrent-start" ); 397 addIdArg( args, id ); 398 break; 399 400 case 'S': 401 tr_bencDictAddStr( &top, "method", "torrent-stop" ); 402 addIdArg( args, id ); 403 break; 404 405 case 't': 406 tr_strlcpy( id, optarg, sizeof( id ) ); 407 addArg = FALSE; 408 break; 409 410 case 'u': 411 tr_bencDictAddStr( &top, "method", "session-set" ); 412 tr_bencDictAddInt( args, "speed-limit-up", numarg( optarg ) ); 413 tr_bencDictAddInt( args, "speed-limit-up-enabled", 1 ); 414 break; 415 416 case 'U': 417 tr_bencDictAddStr( &top, "method", "session-set" ); 418 tr_bencDictAddInt( args, "speed-limit-up-enabled", 0 ); 419 break; 420 421 case 'v': 422 tr_bencDictAddStr( &top, "method", "torrent-verify" ); 423 addIdArg( args, id ); 424 break; 425 426 case 'w': 427 tr_bencDictAddStr( &top, "method", "session-set" ); 428 tr_bencDictAddStr( args, "download-dir", 429 absolutify( buf, sizeof( buf ), optarg ) ); 430 break; 431 432 case 'x': 433 tr_bencDictAddStr( &top, "method", "session-set" ); 434 tr_bencDictAddInt( args, "pex-allowed", 1 ); 435 break; 436 437 case 'X': 438 tr_bencDictAddStr( &top, "method", "session-set" ); 439 tr_bencDictAddInt( args, "pex-allowed", 0 ); 440 break; 441 442 case 'z': 443 tr_bencDictAddStr( &top, "method", "torrent-get" ); 444 tr_bencDictAddInt( &top, "tag", TAG_PEERS ); 445 fields = tr_bencDictAddList( args, "fields", 1 ); 446 tr_bencListAddStr( fields, "peers" ); 447 break; 448 449 case 900: 450 tr_bencDictAddStr( &top, "method", "torrent-set" ); 451 addIdArg( args, id ); 452 addFiles( args, "priority-high", optarg ); 453 break; 454 455 case 901: 456 tr_bencDictAddStr( &top, "method", "torrent-set" ); 457 addIdArg( args, id ); 458 addFiles( args, "priority-normal", optarg ); 459 break; 460 461 case 902: 462 tr_bencDictAddStr( &top, "method", "torrent-set" ); 463 addIdArg( args, id ); 464 addFiles( args, "priority-low", optarg ); 465 break; 466 467 case 910: 468 tr_bencDictAddStr( &top, "method", "session-set" ); 469 tr_bencDictAddStr( args, "encryption", "required" ); 470 break; 471 472 case 911: 473 tr_bencDictAddStr( &top, "method", "session-set" ); 474 tr_bencDictAddStr( args, "encryption", "preferred" ); 475 break; 476 477 case 912: 478 tr_bencDictAddStr( &top, "method", "session-set" ); 479 tr_bencDictAddStr( args, "encryption", "tolerated" ); 480 break; 481 354 482 case TR_OPT_ERR: 355 fprintf( stderr, "invalid option\n" ); 356 showUsage( ); 357 break; 358 default: fprintf( stderr, "got opt [%d]\n", (int)c ); 359 showUsage( ); 360 break; 483 fprintf( stderr, "invalid option\n" ); 484 showUsage( ); 485 break; 486 487 default: 488 fprintf( stderr, "got opt [%d]\n", (int)c ); 489 showUsage( ); 490 break; 361 491 } 362 492 … … 369 499 /* [host:port] or [host] or [port] */ 370 500 static void 371 getHostAndPort( int * argc, char ** argv, char ** host, int * port ) 501 getHostAndPort( int * argc, 502 char ** argv, 503 char ** host, 504 int * port ) 372 505 { 373 506 if( *argv[1] != '-' ) 374 507 { 375 int i;508 int i; 376 509 const char * s = argv[1]; 377 510 const char * delim = strchr( s, ':' ); 378 if( delim ) { /* user passed in both host and port */ 379 *host = tr_strndup( s, delim-s ); 380 *port = atoi( delim+1 ); 381 } else { 382 char * end; 511 if( delim ) /* user passed in both host and port */ 512 { 513 *host = tr_strndup( s, delim - s ); 514 *port = atoi( delim + 1 ); 515 } 516 else 517 { 518 char * end; 383 519 const int i = strtol( s, &end, 10 ); 384 520 if( !*end ) /* user passed in a port */ … … 389 525 390 526 *argc -= 1; 391 for( i =1; i<*argc; ++i )392 argv[i] = argv[i +1];527 for( i = 1; i < *argc; ++i ) 528 argv[i] = argv[i + 1]; 393 529 } 394 530 } 395 531 396 532 static size_t 397 writeFunc( void * ptr, size_t size, size_t nmemb, void * buf ) 533 writeFunc( void * ptr, 534 size_t size, 535 size_t nmemb, 536 void * buf ) 398 537 { 399 538 const size_t byteCount = size * nmemb; 539 400 540 evbuffer_add( buf, ptr, byteCount ); 401 541 return byteCount; … … 403 543 404 544 static void 405 etaToString( char * buf, size_t buflen, int64_t eta ) 406 { 407 if( eta < 0 ) tr_snprintf( buf, buflen, "Unknown" ); 408 else if( eta < 60 ) tr_snprintf( buf, buflen, "%"PRId64"sec", eta ); 409 else if( eta < (60*60) ) tr_snprintf( buf, buflen, "%"PRId64" min", eta/60 ); 410 else if( eta < (60*60*24) ) tr_snprintf( buf, buflen, "%"PRId64" hrs", eta/(60*60) ); 411 else tr_snprintf( buf, buflen, "%"PRId64" days", eta/(60*60*24) ); 545 etaToString( char * buf, 546 size_t buflen, 547 int64_t eta ) 548 { 549 if( eta < 0 ) tr_snprintf( buf, buflen, "Unknown" ); 550 else if( eta < 60 ) tr_snprintf( buf, buflen, "%" PRId64 "sec", eta ); 551 else if( eta < 552 ( 60 * 60 ) ) tr_snprintf( buf, buflen, "%" PRId64 " min", 553 eta / 60 ); 554 else if( eta < 555 ( 60 * 60 * 24 ) ) tr_snprintf( buf, buflen, "%" PRId64 " hrs", 556 eta / ( 60 * 60 ) ); 557 else tr_snprintf( buf, buflen, "%" PRId64 " days", eta / ( 60 * 60 * 24 ) ); 412 558 } 413 559 414 560 #define KILOBYTE_FACTOR 1024.0 415 #define MEGABYTE_FACTOR ( 1024.0 * 1024.0)416 #define GIGABYTE_FACTOR ( 1024.0 * 1024.0 * 1024.0)561 #define MEGABYTE_FACTOR ( 1024.0 * 1024.0 ) 562 #define GIGABYTE_FACTOR ( 1024.0 * 1024.0 * 1024.0 ) 417 563 418 564 static char* 419 strlratio( char * buf, double numerator, double denominator, size_t buflen ) 565 strlratio( char * buf, 566 double numerator, 567 double denominator, 568 size_t buflen ) 420 569 { 421 570 if( denominator ) … … 437 586 438 587 static char* 439 strlsize( char * buf, int64_t size, size_t buflen ) 588 strlsize( char * buf, 589 int64_t size, 590 size_t buflen ) 440 591 { 441 592 if( !size ) 442 593 tr_strlcpy( buf, "None", buflen ); 443 594 else if( size < (int64_t)KILOBYTE_FACTOR ) 444 tr_snprintf( buf, buflen, "%'"PRId64" bytes", (int64_t)size ); 445 else { 595 tr_snprintf( buf, buflen, "%'" PRId64 " bytes", (int64_t)size ); 596 else 597 { 446 598 double displayed_size; 447 if (size < (int64_t)MEGABYTE_FACTOR) { 599 if( size < (int64_t)MEGABYTE_FACTOR ) 600 { 448 601 displayed_size = (double) size / KILOBYTE_FACTOR; 449 602 tr_snprintf( buf, buflen, "%'.1f KB", displayed_size ); 450 } else if (size < (int64_t)GIGABYTE_FACTOR) { 603 } 604 else if( size < (int64_t)GIGABYTE_FACTOR ) 605 { 451 606 displayed_size = (double) size / MEGABYTE_FACTOR; 452 607 tr_snprintf( buf, buflen, "%'.1f MB", displayed_size ); 453 } else { 608 } 609 else 610 { 454 611 displayed_size = (double) size / GIGABYTE_FACTOR; 455 612 tr_snprintf( buf, buflen, "%'.1f GB", displayed_size ); … … 464 621 switch( i ) 465 622 { 466 case TR_STATUS_CHECK_WAIT: return "Will Verify"; 467 case TR_STATUS_CHECK: return "Verifying"; 468 case TR_STATUS_DOWNLOAD: return "Downloading"; 469 case TR_STATUS_SEED: return "Seeding"; 470 case TR_STATUS_STOPPED: return "Stopped"; 471 default: return "Error"; 623 case TR_STATUS_CHECK_WAIT: 624 return "Will Verify"; 625 626 case TR_STATUS_CHECK: 627 return "Verifying"; 628 629 case TR_STATUS_DOWNLOAD: 630 return "Downloading"; 631 632 case TR_STATUS_SEED: 633 return "Seeding"; 634 635 case TR_STATUS_STOPPED: 636 return "Stopped"; 637 638 default: 639 return "Error"; 472 640 } 473 641 } … … 476 644 isVerifying( int status ) 477 645 { 478 return ( ( status == TR_STATUS_CHECK_WAIT ) ||479 ( status == TR_STATUS_CHECK ));646 return ( status == TR_STATUS_CHECK_WAIT ) 647 || ( status == TR_STATUS_CHECK ); 480 648 } 481 649 … … 485 653 tr_benc *args, *torrents; 486 654 487 if( ( tr_bencDictFindDict( top, "arguments", &args ) ) &&488 655 if( ( tr_bencDictFindDict( top, "arguments", &args ) ) 656 && ( tr_bencDictFindList( args, "torrents", &torrents ) ) ) 489 657 { 490 658 int ti, tCount; 491 for( ti=0, tCount=tr_bencListSize( torrents ); ti<tCount; ++ti ) 659 for( ti = 0, tCount = tr_bencListSize( torrents ); ti < tCount; 660 ++ti ) 492 661 { 493 tr_benc * t = tr_bencListChild( torrents, ti );494 tr_benc * l;662 tr_benc * t = tr_bencListChild( torrents, ti ); 663 tr_benc * l; 495 664 const char * str; 496 char buf[512];497 char buf2[512];498 int64_t i, j, k;665 char buf[512]; 666 char buf2[512]; 667 int64_t i, j, k; 499 668 500 669 printf( "NAME\n" ); 501 670 if( tr_bencDictFindInt( t, "id", &i ) ) 502 printf( " Id: %" PRId64"\n", i );671 printf( " Id: %" PRId64 "\n", i ); 503 672 if( tr_bencDictFindStr( t, "name", &str ) ) 504 673 printf( " Name: %s\n", str ); … … 510 679 if( tr_bencDictFindInt( t, "status", &i ) ) 511 680 { 512 if( isVerifying( i ) && tr_bencDictFindStr( t, "recheckProgress", &str ) ) 513 tr_snprintf( buf, sizeof( buf ), " (%.0f%% Done)", 100.0*atof(str) ); 681 if( isVerifying( i ) 682 && tr_bencDictFindStr( t, "recheckProgress", &str ) ) 683 tr_snprintf( buf, sizeof( buf ), " (%.0f%% Done)", 684 100.0 * atof( 685 str ) ); 514 686 else 515 687 *buf = '\0'; … … 517 689 } 518 690 519 if( tr_bencDictFindInt( t, "sizeWhenDone", &i ) &&520 521 { 522 strlratio( buf, 100.0 *(i-j), i, sizeof( buf ) );691 if( tr_bencDictFindInt( t, "sizeWhenDone", &i ) 692 && tr_bencDictFindInt( t, "leftUntilDone", &j ) ) 693 { 694 strlratio( buf, 100.0 * ( i - j ), i, sizeof( buf ) ); 523 695 printf( " Percent Done: %s%%\n", buf ); 524 696 } 525 697 526 if( tr_bencDictFindInt( t, "eta", &i ) ) { 698 if( tr_bencDictFindInt( t, "eta", &i ) ) 699 { 527 700 etaToString( buf, sizeof( buf ), i ); 528 701 printf( " ETA: %s\n", buf ); 529 702 } 530 703 if( tr_bencDictFindInt( t, "rateDownload", &i ) ) 531 printf( " Download Speed: %.1f KB/s\n", i /1024.0 );704 printf( " Download Speed: %.1f KB/s\n", i / 1024.0 ); 532 705 if( tr_bencDictFindInt( t, "rateUpload", &i ) ) 533 printf( " Upload Speed: %.1f KB/s\n", i /1024.0 );534 if( tr_bencDictFindInt( t, "haveUnchecked", &i ) &&535 536 { 537 strlsize( buf, i +j, sizeof( buf ) );706 printf( " Upload Speed: %.1f KB/s\n", i / 1024.0 ); 707 if( tr_bencDictFindInt( t, "haveUnchecked", &i ) 708 && tr_bencDictFindInt( t, "haveValid", &j ) ) 709 { 710 strlsize( buf, i + j, sizeof( buf ) ); 538 711 strlsize( buf2, j, sizeof( buf2 ) ); 539 712 printf( " Have: %s (%s verified)\n", buf, buf2 ); 540 713 } 541 714 542 if( tr_bencDictFindInt( t, "sizeWhenDone", &i ) &&543 715 if( tr_bencDictFindInt( t, "sizeWhenDone", &i ) 716 && tr_bencDictFindInt( t, "totalSize", &j ) ) 544 717 { 545 718 strlsize( buf, j, sizeof( buf ) ); … … 547 720 printf( " Total size: %s (%s wanted)\n", buf, buf2 ); 548 721 } 549 if( tr_bencDictFindInt( t, "downloadedEver", &i ) && 550 tr_bencDictFindInt( t, "uploadedEver", &j ) ) { 722 if( tr_bencDictFindInt( t, "downloadedEver", &i ) 723 && tr_bencDictFindInt( t, "uploadedEver", &j ) ) 724 { 551 725 strlsize( buf, i, sizeof( buf ) ); 552 726 printf( " Downloaded: %s\n", buf ); … … 556 730 printf( " Ratio: %s\n", buf ); 557 731 } 558 if( tr_bencDictFindInt( t, "corruptEver", &i ) ) { 732 if( tr_bencDictFindInt( t, "corruptEver", &i ) ) 733 { 559 734 strlsize( buf, i, sizeof( buf ) ); 560 735 printf( " Corrupt DL: %s\n", buf ); … … 563 738 printf( " Error: %s\n", str ); 564 739 565 if( tr_bencDictFindInt( t, "peersConnected", &i ) && 566 tr_bencDictFindInt( t, "peersGettingFromUs", &j ) && 567 tr_bencDictFindInt( t, "peersSendingToUs", &k ) ) 568 { 569 printf( " Peers: " 570 "connected to %"PRId64", " 571 "uploading to %"PRId64", " 572 "downloading from %"PRId64"\n", 573 i, j, k ); 574 } 575 576 if( tr_bencDictFindList( t, "webseeds", &l ) && 577 tr_bencDictFindInt( t, "webseedsSendingToUs", &i ) ) 740 if( tr_bencDictFindInt( t, "peersConnected", &i ) 741 && tr_bencDictFindInt( t, "peersGettingFromUs", &j ) 742 && tr_bencDictFindInt( t, "peersSendingToUs", &k ) ) 743 { 744 printf( 745 " Peers: " 746 "connected to %" PRId64 ", " 747 "uploading to %" PRId64 748 ", " 749 "downloading from %" 750 PRId64 "\n", 751 i, j, k ); 752 } 753 754 if( tr_bencDictFindList( t, "webseeds", &l ) 755 && tr_bencDictFindInt( t, "webseedsSendingToUs", &i ) ) 578 756 { 579 757 const int64_t n = tr_bencListSize( l ); 580 758 if( n > 0 ) 581 printf( " Web Seeds: downloading from %"PRId64" of %"PRId64" web seeds\n", i, n ); 759 printf( 760 " Web Seeds: downloading from %" PRId64 " of %" 761 PRId64 762 " web seeds\n", i, n ); 582 763 } 583 764 printf( "\n" ); 584 765 585 766 printf( "HISTORY\n" ); 586 if( tr_bencDictFindInt( t, "addedDate", &i ) && i ) { 767 if( tr_bencDictFindInt( t, "addedDate", &i ) && i ) 768 { 587 769 const time_t tt = i; 588 770 printf( " Date added: %s", ctime( &tt ) ); 589 771 } 590 if( tr_bencDictFindInt( t, "doneDate", &i ) && i ) { 772 if( tr_bencDictFindInt( t, "doneDate", &i ) && i ) 773 { 591 774 const time_t tt = i; 592 775 printf( " Date finished: %s", ctime( &tt ) ); 593 776 } 594 if( tr_bencDictFindInt( t, "startDate", &i ) && i ) { 777 if( tr_bencDictFindInt( t, "startDate", &i ) && i ) 778 { 595 779 const time_t tt = i; 596 780 printf( " Date started: %s", ctime( &tt ) ); 597 781 } 598 if( tr_bencDictFindInt( t, "activityDate", &i ) && i ) { 782 if( tr_bencDictFindInt( t, "activityDate", &i ) && i ) 783 { 599 784 const time_t tt = i; 600 785 printf( " Latest activity: %s", ctime( &tt ) ); 601 786 } 602 787 printf( "\n" ); 603 788 604 789 printf( "TRACKER\n" ); 605 if( tr_bencDictFindInt( t, "lastAnnounceTime", &i ) && i ) { 790 if( tr_bencDictFindInt( t, "lastAnnounceTime", &i ) && i ) 791 { 606 792 const time_t tt = i; 607 793 printf( " Latest announce: %s", ctime( &tt ) ); … … 609 795 if( tr_bencDictFindStr( t, "announceURL", &str ) ) 610 796 printf( " Announce URL: %s\n", str ); 611 if( tr_bencDictFindStr( t, "announceResponse", &str ) && str && *str ) 797 if( tr_bencDictFindStr( t, "announceResponse", 798 &str ) && str && *str ) 612 799 printf( " Announce response: %s\n", str ); 613 if( tr_bencDictFindInt( t, "nextAnnounceTime", &i ) && i ) { 800 if( tr_bencDictFindInt( t, "nextAnnounceTime", &i ) && i ) 801 { 614 802 const time_t tt = i; 615 803 printf( " Next announce: %s", ctime( &tt ) ); 616 804 } 617 if( tr_bencDictFindInt( t, "lastScrapeTime", &i ) && i ) { 805 if( tr_bencDictFindInt( t, "lastScrapeTime", &i ) && i ) 806 { 618 807 const time_t tt = i; 619 808 printf( " Latest scrape: %s", ctime( &tt ) ); … … 621 810 if( tr_bencDictFindStr( t, "scrapeResponse", &str ) ) 622 811 printf( " Scrape response: %s\n", str ); 623 if( tr_bencDictFindInt( t, "nextScrapeTime", &i ) && i ) { 812 if( tr_bencDictFindInt( t, "nextScrapeTime", &i ) && i ) 813 { 624 814 const time_t tt = i; 625 815 printf( " Next scrape: %s", ctime( &tt ) ); 626 816 } 627 if( tr_bencDictFindInt( t, "seeders", &i ) && 628 tr_bencDictFindInt( t, "leechers", &j ) ) 629 printf( " Tracker knows of %"PRId64" seeders and %"PRId64" leechers\n", i, j ); 817 if( tr_bencDictFindInt( t, "seeders", &i ) 818 && tr_bencDictFindInt( t, "leechers", &j ) ) 819 printf( 820 " Tracker knows of %" PRId64 " seeders and %" PRId64 821 " leechers\n", i, j ); 630 822 if( tr_bencDictFindInt( t, "timesCompleted", &i ) ) 631 printf( " Tracker has seen %"PRId64" clients complete this torrent\n", i ); 823 printf( 824 " Tracker has seen %" PRId64 825 " clients complete this torrent\n", i ); 632 826 printf( "\n" ); 633 827 634 828 printf( "ORIGINS\n" ); 635 if( tr_bencDictFindInt( t, "dateCreated", &i ) && i ) { 829 if( tr_bencDictFindInt( t, "dateCreated", &i ) && i ) 830 { 636 831 const time_t tt = i; 637 832 printf( " Date created: %s", ctime( &tt ) ); … … 644 839 printf( " Creator: %s\n", str ); 645 840 if( tr_bencDictFindInt( t, "pieceCount", &i ) ) 646 printf( " Piece Count: %" PRId64"\n", i );841 printf( " Piece Count: %" PRId64 "\n", i ); 647 842 if( tr_bencDictFindInt( t, "pieceSize", &i ) ) 648 printf( " Piece Size: %" PRId64"\n", i );843 printf( " Piece Size: %" PRId64 "\n", i ); 649 844 } 650 845 } … … 656 851 tr_benc *args, *torrents; 657 852 658 if( ( tr_bencDictFindDict( top, "arguments", &args ) ) &&659 853 if( ( tr_bencDictFindDict( top, "arguments", &args ) ) 854 && ( tr_bencDictFindList( args, "torrents", &torrents ) ) ) 660 855 { 661 856 int i, in; 662 for( i =0, in=tr_bencListSize( torrents ); i<in; ++i )857 for( i = 0, in = tr_bencListSize( torrents ); i < in; ++i ) 663 858 { 664 tr_benc * d = tr_bencListChild( torrents, i );665 tr_benc * files, *priorities, *wanteds;859 tr_benc * d = tr_bencListChild( torrents, i ); 860 tr_benc * files, *priorities, *wanteds; 666 861 const char * name; 667 if( tr_bencDictFindStr( d, "name", &name ) &&668 tr_bencDictFindList( d, "files", &files ) &&669 tr_bencDictFindList( d, "priorities", &priorities ) &&670 671 { 672 int j =0, jn=tr_bencListSize(files);862 if( tr_bencDictFindStr( d, "name", &name ) 863 && tr_bencDictFindList( d, "files", &files ) 864 && tr_bencDictFindList( d, "priorities", &priorities ) 865 && tr_bencDictFindList( d, "wanted", &wanteds ) ) 866 { 867 int j = 0, jn = tr_bencListSize( files ); 673 868 printf( "%s (%d files):\n", name, jn ); 674 printf("%3s %4s %8s %3s %9s %s\n", "#", "Done", "Priority", "Get", "Size", "Name" ); 675 for( j=0, jn=tr_bencListSize( files ); j<jn; ++j ) 869 printf( "%3s %4s %8s %3s %9s %s\n", "#", "Done", 870 "Priority", "Get", "Size", 871 "Name" ); 872 for( j = 0, jn = tr_bencListSize( files ); j < jn; ++j ) 676 873 { 677 int64_t have;678 int64_t length;679 int64_t priority;680 int64_t wanted;874 int64_t have; 875 int64_t length; 876 int64_t priority; 877 int64_t wanted; 681 878 const char * filename; 682 tr_benc * file = tr_bencListChild( files, j ); 683 if( tr_bencDictFindInt( file, "length", &length ) && 684 tr_bencDictFindStr( file, "name", &filename ) && 685 tr_bencDictFindInt( file, "bytesCompleted", &have ) && 686 tr_bencGetInt( tr_bencListChild( priorities, j ), &priority ) && 687 tr_bencGetInt( tr_bencListChild( wanteds, j ), &wanted ) ) 879 tr_benc * file = tr_bencListChild( files, j ); 880 if( tr_bencDictFindInt( file, "length", &length ) 881 && tr_bencDictFindStr( file, "name", &filename ) 882 && tr_bencDictFindInt( file, "bytesCompleted", &have ) 883 && tr_bencGetInt( tr_bencListChild( priorities, 884 j ), &priority ) 885 && tr_bencGetInt( tr_bencListChild( wanteds, 886 j ), &wanted ) ) 688 887 { 689 char sizestr[64];690 double percent = (double)have / length;888 char sizestr[64]; 889 double percent = (double)have / length; 691 890 const char * pristr; 692 891 strlsize( sizestr, length, sizeof( sizestr ) ); 693 switch( priority ) { 694 case TR_PRI_LOW: pristr = "Low"; break; 695 case TR_PRI_HIGH: pristr = "High"; break; 696 default: pristr = "Normal"; break; 892 switch( priority ) 893 { 894 case TR_PRI_LOW: 895 pristr = "Low"; break; 896 897 case TR_PRI_HIGH: 898 pristr = "High"; break; 899 900 default: 901 pristr = "Normal"; break; 697 902 } 698 903 printf( "%3d: %3.0f%% %-8s %-3s %9s %s\n", 699 ( j+1),700 ( 100.0*percent),904 ( j + 1 ), 905 ( 100.0 * percent ), 701 906 pristr, 702 ( wanted?"Yes":"No"),907 ( wanted ? "Yes" : "No" ), 703 908 sizestr, 704 909 filename ); … … 715 920 tr_benc *args, *list; 716 921 717 if( ( tr_bencDictFindDict( top, "arguments", &args ) ) &&718 922 if( ( tr_bencDictFindDict( top, "arguments", &args ) ) 923 && ( tr_bencDictFindList( args, "peers", &list ) ) ) 719 924 { 720 925 int i, n; 721 926 printf( "%-20s %-12s %-5s %5s %s\n", 722 927 "Address", "Flags", "Down", "Up", "Client" ); 723 for( i =0, n=tr_bencListSize( list ); i<n; ++i )928 for( i = 0, n = tr_bencListSize( list ); i < n; ++i ) 724 929 { 725 930 const char * address, * client, * flagstr; 726 int64_t rateToClient, rateToPeer;727 tr_benc * d = tr_bencListChild( list, i );728 if( 729 730 731 732 931 int64_t rateToClient, rateToPeer; 932 tr_benc * d = tr_bencListChild( list, i ); 933 if( tr_bencDictFindStr( d, "address", &address ) 934 && tr_bencDictFindStr( d, "client", &client ) 935 && tr_bencDictFindStr( d, "flagstr", &flagstr ) 936 && tr_bencDictFindInt( d, "rateToClient", &rateToClient ) 937 && tr_bencDictFindInt( d, "rateToPeer", &rateToPeer ) ) 733 938 { 734 939 printf( "%-20s %-12s %5.1f %5.1f %s\n", … … 747 952 tr_benc *args, *list; 748 953 749 if( ( tr_bencDictFindDict( top, "arguments", &args ) ) &&750 954 if( ( tr_bencDictFindDict( top, "arguments", &args ) ) 955 && ( tr_bencDictFindList( args, "torrents", &list ) ) ) 751 956 { 752 957 int i, n; 753 958 printf( "%-3s %-4s %-8s %-5s %-5s %-5s %-11s %s\n", 754 "ID", "Done", "ETA", "Up", "Down", "Ratio", "Status", "Name" ); 755 for( i=0, n=tr_bencListSize( list ); i<n; ++i ) 959 "ID", "Done", "ETA", "Up", "Down", "Ratio", "Status", 960 "Name" ); 961 for( i = 0, n = tr_bencListSize( list ); i < n; ++i ) 756 962 { 757 int64_t id, eta, status, up, down;758 int64_t sizeWhenDone, leftUntilDone;759 int64_t upEver, downEver;963 int64_t id, eta, status, up, down; 964 int64_t sizeWhenDone, leftUntilDone; 965 int64_t upEver, downEver; 760 966 const char *name; 761 tr_benc * d = tr_bencListChild( list, i );762 if( 763 764 765 766 767 768 769 770 771 967 tr_benc * d = tr_bencListChild( list, i ); 968 if( tr_bencDictFindInt( d, "downloadedEver", &downEver ) 969 && tr_bencDictFindInt( d, "eta", &eta ) 970 && tr_bencDictFindInt( d, "id", &id ) 971 && tr_bencDictFindInt( d, "leftUntilDone", &leftUntilDone ) 972 && tr_bencDictFindStr( d, "name", &name ) 973 && tr_bencDictFindInt( d, "rateDownload", &down ) 974 && tr_bencDictFindInt( d, "rateUpload", &up ) 975 && tr_bencDictFindInt( d, "sizeWhenDone", &sizeWhenDone ) 976 && tr_bencDictFindInt( d, "status", &status ) 977 && tr_bencDictFindInt( d, "uploadedEver", &upEver ) ) 772 978 { 773 979 char etaStr[16]; … … 776 982 else 777 983 tr_snprintf( etaStr, sizeof( etaStr ), "Done" ); 778 printf( "%3d %3d%% %-8s %5.1f %5.1f %5.1f %-11s %s\n", 779 (int)id, 780 (int)(100.0*(sizeWhenDone-leftUntilDone)/sizeWhenDone), 781 etaStr, 782 up / 1024.0, 783 down / 1024.0, 784 (double)(downEver ? ((double)upEver/downEver) : 0.0), 785 torrentStatusToString( status ), 786 name ); 984 printf( 985 "%3d %3d%% %-8s %5.1f %5.1f %5.1f %-11s %s\n", 986 (int)id, 987 (int)( 100.0 * 988 ( sizeWhenDone - leftUntilDone ) / sizeWhenDone ), 989 etaStr, 990 up / 1024.0, 991 down / 1024.0, 992 (double)( downEver ? ( (double)upEver / 993 downEver ) : 0.0 ), 994 torrentStatusToString( status ), 995 name ); 787 996 } 788 997 } … … 791 1000 792 1001 static void 793 processResponse( const char * host, int port, 794 const void * response, size_t len ) 1002 processResponse( const char * host, 1003 int port, 1004 const void * response, 1005 size_t len ) 795 1006 { 796 1007 tr_benc top; … … 801 1012 802 1013 if( tr_jsonParse( response, len, &top, NULL ) ) 803 tr_nerr( MY_NAME, "Unable to parse response \"%*.*s\"", (int)len, (int)len, (char*)response ); 1014 tr_nerr( MY_NAME, "Unable to parse response \"%*.*s\"", (int)len, 1015 (int)len, (char*)response ); 804 1016 else 805 1017 { 806 int64_t tag = -1;1018 int64_t tag = -1; 807 1019 const char * str; 808 1020 tr_bencDictFindInt( &top, "tag", &tag ); 809 1021 810 switch( tag ) { 811 case TAG_FILES: printFileList( &top ); break; 812 case TAG_DETAILS: printDetails( &top ); break; 813 case TAG_LIST: printTorrentList( &top ); break; 814 case TAG_PEERS: printPeerList( &top ); break; 815 default: if( tr_bencDictFindStr( &top, "result", &str ) ) 816 printf( "%s:%d responded: \"%s\"\n", host, port, str ); 1022 switch( tag ) 1023 { 1024 case TAG_FILES: 1025 printFileList( &top ); break; 1026 1027 case TAG_DETAILS: 1028 printDetails( &top ); break; 1029 1030 case TAG_LIST: 1031 printTorrentList( &top ); break; 1032 1033 case TAG_PEERS: 1034 printPeerList( &top ); break; 1035 1036 default: 1037 if( tr_bencDictFindStr( &top, "result", &str ) ) 1038 printf( "%s:%d responded: \"%s\"\n", host, port, str ); 817 1039 } 818 1040 … … 822 1044 823 1045 static void 824 processRequests( const char * host, int port, 825 const char ** reqs, int reqCount ) 826 { 827 int i; 828 CURL * curl; 1046 processRequests( const char * host, 1047 int port, 1048 const char ** reqs, 1049 int reqCount ) 1050 { 1051 int i; 1052 CURL * curl; 829 1053 struct evbuffer * buf = evbuffer_new( ); 830 char * url = tr_strdup_printf( "http://%s:%d/transmission/rpc", host, port ); 1054 char * url = tr_strdup_printf( 1055 "http://%s:%d/transmission/rpc", host, port ); 831 1056 832 1057 curl = curl_easy_init( ); 833 1058 curl_easy_setopt( curl, CURLOPT_VERBOSE, debug ); 834 curl_easy_setopt( curl, CURLOPT_USERAGENT, MY_NAME"/"LONG_VERSION_STRING ); 1059 curl_easy_setopt( curl, CURLOPT_USERAGENT, 1060 MY_NAME "/" LONG_VERSION_STRING ); 835 1061 curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc ); 836 1062 curl_easy_setopt( curl, CURLOPT_WRITEDATA, buf ); 837 1063 curl_easy_setopt( curl, CURLOPT_POST, 1 ); 838 1064 curl_easy_setopt( curl, CURLOPT_URL, url ); 839 if( auth ) { 1065 if( auth ) 1066 { 840 1067 curl_easy_setopt( curl, CURLOPT_USERPWD, auth ); 841 1068 curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY ); 842 1069 } 843 1070 844 for( i =0; i<reqCount; ++i )1071 for( i = 0; i < reqCount; ++i ) 845 1072 { 846 1073 CURLcode res; … … 848 1075 if( debug ) 849 1076 tr_ninf( MY_NAME, "posting [%s]\n", reqs[i] ); 850 if(( res = curl_easy_perform( curl ))) 851 tr_nerr( MY_NAME, "(%s:%d) %s", host, port, curl_easy_strerror( res ) ); 1077 if( ( res = curl_easy_perform( curl ) ) ) 1078 tr_nerr( MY_NAME, "(%s:%d) %s", host, port, 1079 curl_easy_strerror( res ) ); 852 1080 else 853 processResponse( host, port, EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) ); 1081 processResponse( host, port, EVBUFFER_DATA( 1082 buf ), EVBUFFER_LENGTH( buf ) ); 854 1083 855 1084 evbuffer_drain( buf, EVBUFFER_LENGTH( buf ) ); … … 863 1092 864 1093 int 865 main( int argc, char ** argv ) 866 { 867 int i; 868 int port = DEFAULT_PORT; 1094 main( int argc, 1095 char ** argv ) 1096 { 1097 int i; 1098 int port = DEFAULT_PORT; 869 1099 char * host = NULL; 870 1100 … … 882 1112 showUsage( ); 883 1113 884 for( i =0; i<reqCount; ++i )1114 for( i = 0; i < reqCount; ++i ) 885 1115 tr_free( reqs[i] ); 886 1116 … … 888 1118 return 0; 889 1119 } 1120 -
trunk/gtk/actions.c
r6559 r6795 4 4 * This file is licensed by the GPL version 2. Works owned by the 5 5 * Transmission project are granted a special exemption to clause 2(b) 6 * so that the bulk of its code can remain under the MIT license. 6 * so that the bulk of its code can remain under the MIT license. 7 7 * This exemption does not extend to derived works not owned by 8 8 * the Transmission project. 9 * 9 * 10 10 * $Id$ 11 11 */ … … 24 24 #define UNUSED G_GNUC_UNUSED 25 25 26 static TrCore * myCore = NULL;26 static TrCore * myCore = NULL; 27 27 28 28 static GtkActionGroup * myGroup = NULL; 29 29 30 30 static void 31 action_cb ( GtkAction * a, gpointer user_data ) 32 { 33 doAction ( gtk_action_get_name(a), user_data ); 34 } 35 36 #if !GTK_CHECK_VERSION(2,8,0) 37 #define GTK_STOCK_INFO GTK_STOCK_PROPERTIES 31 action_cb( GtkAction * a, 32 gpointer user_data ) 33 { 34 doAction ( gtk_action_get_name( a ), user_data ); 35 } 36 37 #if !GTK_CHECK_VERSION( 2, 8, 0 ) 38 #define GTK_STOCK_INFO GTK_STOCK_PROPERTIES 38 39 #endif 39 40 40 #if !GTK_CHECK_VERSION( 2,10,0)41 #define GTK_STOCK_SELECT_ALL NULL41 #if !GTK_CHECK_VERSION( 2, 10, 0 ) 42 #define GTK_STOCK_SELECT_ALL NULL 42 43 #endif 43 44 44 45 static GtkRadioActionEntry sort_radio_entries[] = 45 46 { 46 { "sort-by-activity", NULL, N_("Sort by _Activity"), NULL, NULL, 0 }, 47 { "sort-by-name", NULL, N_("Sort by _Name"), NULL, NULL, 1 }, 48 { "sort-by-progress", NULL, N_("Sort by _Progress"), NULL, NULL, 2 }, 49 { "sort-by-ratio", NULL, N_("Sort by _Ratio"), NULL, NULL, 3 }, 50 { "sort-by-state", NULL, N_("Sort by _State"), NULL, NULL, 4 }, 51 { "sort-by-tracker", NULL, N_("Sort by _Tracker"), NULL, NULL, 5 }, 52 { "sort-by-age", NULL, N_("Sort by A_ge"), NULL, NULL, 6 } 47 { "sort-by-activity", NULL, N_( "Sort by _Activity" ), NULL, 48 NULL, 0 }, 49 { "sort-by-name", NULL, N_( "Sort by _Name" ), NULL, 50 NULL, 1 }, 51 { "sort-by-progress", NULL, N_( "Sort by _Progress" ), NULL, 52 NULL, 2 }, 53 { "sort-by-ratio", NULL, N_( "Sort by _Ratio" ), NULL, 54 NULL, 3 }, 55 { "sort-by-state", NULL, N_( "Sort by _State" ), NULL, 56 NULL, 4 }, 57 { "sort-by-tracker", NULL, N_( "Sort by _Tracker" ), NULL, 58 NULL, 5 }, 59 { "sort-by-age", NULL, N_( "Sort by A_ge" ), NULL, 60 NULL, 6 } 53 61 }; 54 62 55 63 static void 56 64 sort_changed_cb( GtkAction * action UNUSED, 57 GtkRadioAction *current,58 gpointer user_data UNUSED )65 GtkRadioAction * current, 66 gpointer user_data UNUSED ) 59 67 { 60 68 const char * key = PREF_KEY_SORT_MODE; 61 const int i = gtk_radio_action_get_current_value( current );69 const int i = gtk_radio_action_get_current_value( current ); 62 70 const char * val = sort_radio_entries[i].name; 71 63 72 tr_core_set_pref( myCore, key, val ); 64 73 } 65 74 66 static GtkToggleActionEntry show_toggle_entries[] = 67 { 68 { "toggle-main-window", NULL, 69 N_("_Main Window"), NULL, NULL, G_CALLBACK(action_cb), TRUE }, 70 { "toggle-message-log", NULL, 71 N_("Message _Log"), NULL, NULL, G_CALLBACK(action_cb), FALSE } 72 }; 73 74 static void 75 toggle_pref_cb ( GtkToggleAction * action, gpointer user_data UNUSED ) 76 { 77 const char * key = gtk_action_get_name( GTK_ACTION( action ) ); 75 static GtkToggleActionEntry show_toggle_entries[] = 76 { 77 { "toggle-main-window", NULL, 78 N_( "_Main Window" ), NULL, NULL, G_CALLBACK( action_cb ), TRUE }, 79 { "toggle-message-log", NULL, 80 N_( "Message _Log" ), NULL, NULL, G_CALLBACK( action_cb ), FALSE } 81 }; 82 83 static void 84 toggle_pref_cb( GtkToggleAction * action, 85 gpointer user_data UNUSED ) 86 { 87 const char * key = gtk_action_get_name( GTK_ACTION( action ) ); 78 88 const gboolean val = gtk_toggle_action_get_active( action ); 89 79 90 tr_core_set_pref_bool( myCore, key, val ); 80 91 } 81 92 82 static GtkToggleActionEntry pref_toggle_entries[] = 83 { 84 { "minimal-view", NULL, 85 N_("_Minimal View"), "<alt>M", NULL, G_CALLBACK(toggle_pref_cb), FALSE }, 86 { "sort-reversed", NULL, 87 N_("_Reverse Sort Order"), NULL, NULL, G_CALLBACK(toggle_pref_cb), FALSE }, 88 { "show-filterbar", NULL, 89 N_("_Filterbar"), NULL, NULL, G_CALLBACK(toggle_pref_cb), FALSE }, 90 { "show-statusbar", NULL, 91 N_("_Statusbar"), NULL, NULL, G_CALLBACK(toggle_pref_cb), FALSE }, 92 { "show-toolbar", NULL, 93 N_("_Toolbar"), NULL, NULL, G_CALLBACK(toggle_pref_cb), FALSE }, 94 { PREF_KEY_SHOW_TRAY_ICON, NULL, 95 N_("Tray _Icon" ), NULL, NULL, G_CALLBACK(toggle_pref_cb), FALSE } 96 }; 97 98 static GtkActionEntry entries[] = 99 { 100 { "torrent-menu", NULL, N_("_Torrent"), NULL, NULL, NULL }, 101 { "view-menu", NULL, N_("_View"), NULL, NULL, NULL }, 102 { "sort-menu", NULL, N_("_Sort Torrents By"), NULL, NULL, NULL }, 103 { "edit-menu", NULL, N_("_Edit"), NULL, NULL, NULL }, 104 { "help-menu", NULL, N_("_Help"), NULL, NULL, NULL }, 105 { "add-torrent-toolbar", GTK_STOCK_ADD, NULL, NULL, N_("Add a torrent"), G_CALLBACK(action_cb) }, 106 { "add-torrent-menu", GTK_STOCK_ADD, N_("_Add..."), "<control>D", N_("Add a torrent"), G_CALLBACK(action_cb) }, 107 { "start-torrent", GTK_STOCK_MEDIA_PLAY, 108 N_("_Start"), "<control>S", N_("Start torrent"), G_CALLBACK(action_cb) }, 109 { "show-stats", NULL, N_("_Statistics"), NULL, NULL, G_CALLBACK(action_cb) }, 110 { "verify-torrent", NULL, 111 N_("_Verify Local Data"), NULL, NULL, G_CALLBACK(action_cb) }, 112 { "pause-torrent", GTK_STOCK_MEDIA_PAUSE, 113 N_("_Pause"), "<control>P", N_("Pause torrent"), G_CALLBACK(action_cb) }, 114 { "remove-torrent", GTK_STOCK_REMOVE, NULL, "Delete", N_("Remove torrent"), G_CALLBACK(action_cb) }, 115 { "delete-torrent", GTK_STOCK_DELETE, N_("_Delete Files and Remove"), "<shift>Delete", NULL, G_CALLBACK(action_cb) }, 116 { "new-torrent", GTK_STOCK_NEW, N_("_New..."), NULL, 117 N_("Create a torrent"), 118 G_CALLBACK(action_cb) }, 119 { "quit", GTK_STOCK_QUIT, N_("_Quit"), NULL, NULL, G_CALLBACK(action_cb) }, 120 { "select-all", GTK_STOCK_SELECT_ALL, 121 N_( "Select _All" ), "<control>A", NULL, G_CALLBACK(action_cb) }, 122 { "deselect-all", NULL, 123 N_("Dese_lect All"), "<shift><control>A", NULL, G_CALLBACK(action_cb) }, 124 { "edit-preferences", GTK_STOCK_PREFERENCES, NULL, NULL, NULL, G_CALLBACK(action_cb) }, 125 { "show-torrent-details", GTK_STOCK_INFO, 126 N_("_Details"), "<alt>Return", N_("Torrent details"), G_CALLBACK(action_cb) }, 127 { "open-torrent-folder", GTK_STOCK_OPEN, 128 N_("_Open Folder"), NULL, NULL, G_CALLBACK(action_cb) }, 129 { "show-about-dialog", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(action_cb) }, 130 { "help", GTK_STOCK_HELP, N_("_Contents"), "F1", NULL, G_CALLBACK(action_cb) }, 131 { "update-tracker", GTK_STOCK_NETWORK, 132 N_("Ask Tracker for _More Peers"), NULL, NULL, G_CALLBACK(action_cb) }, 133 { "present-main-window", NULL, NULL, NULL, NULL, G_CALLBACK(action_cb) }, 93 static GtkToggleActionEntry pref_toggle_entries[] = 94 { 95 { "minimal-view", NULL, 96 N_( "_Minimal View" ), "<alt>M", NULL, G_CALLBACK( toggle_pref_cb ), 97 FALSE }, 98 { "sort-reversed", NULL, 99 N_( "_Reverse Sort Order" ), NULL, NULL, G_CALLBACK( toggle_pref_cb ), 100 FALSE }, 101 { "show-filterbar", NULL, 102 N_( "_Filterbar" ), NULL, NULL, G_CALLBACK( toggle_pref_cb ), FALSE }, 103 { "show-statusbar", NULL, 104 N_( "_Statusbar" ), NULL, NULL, G_CALLBACK( toggle_pref_cb ), FALSE }, 105 { "show-toolbar", NULL, 106 N_( "_Toolbar" ), NULL, NULL, G_CALLBACK( toggle_pref_cb ), FALSE }, 107 { PREF_KEY_SHOW_TRAY_ICON, NULL, 108 N_( "Tray _Icon" ), NULL, NULL, G_CALLBACK( toggle_pref_cb ), FALSE } 109 }; 110 111 static GtkActionEntry entries[] = 112 { 113 { "torrent-menu", NULL, N_( 114 "_Torrent" ), 115 NULL, NULL, NULL }, 116 { "view-menu", NULL, N_( "_View" ), 117 NULL, NULL, NULL }, 118 { "sort-menu", NULL, N_( 119 "_Sort Torrents By" ), NULL, NULL, 120 NULL }, 121 { "edit-menu", NULL, N_( "_Edit" ), 122 NULL, NULL, NULL }, 123 { "help-menu", NULL, N_( "_Help" ), 124 NULL, NULL, NULL }, 125 { "add-torrent-toolbar", GTK_STOCK_ADD, NULL, 126 NULL, N_( "Add a torrent" ), G_CALLBACK( action_cb ) }, 127 { "add-torrent-menu", GTK_STOCK_ADD, N_( "_Add..." ), 128 "<control>D", N_( "Add a torrent" ), G_CALLBACK( action_cb ) }, 129 { "start-torrent", GTK_STOCK_MEDIA_PLAY, 130 N_( "_Start" ), "<control>S", N_( "Start torrent" ), G_CALLBACK( 131 action_cb ) }, 132 { "show-stats", NULL, N_( "_Statistics" ), 133 NULL, NULL, G_CALLBACK( action_cb ) }, 134 { "verify-torrent", NULL, 135 N_( "_Verify Local Data" ), NULL, NULL, G_CALLBACK( action_cb ) }, 136 { "pause-torrent", GTK_STOCK_MEDIA_PAUSE, 137 N_( "_Pause" ), "<control>P", N_( "Pause torrent" ), G_CALLBACK( 138 action_cb ) }, 139 { "remove-torrent", GTK_STOCK_REMOVE, NULL, 140 "Delete", N_( "Remove torrent" ), G_CALLBACK( action_cb ) }, 141 { "delete-torrent", GTK_STOCK_DELETE, N_( 142 "_Delete Files and Remove" ), "<shift>Delete", NULL, 143 G_CALLBACK( action_cb ) }, 144 { "new-torrent", GTK_STOCK_NEW, N_( "_New..." ), 145 NULL, 146 N_( "Create a torrent" ), 147 G_CALLBACK( action_cb ) }, 148 { "quit", GTK_STOCK_QUIT, N_( "_Quit" ), 149 NULL, NULL, G_CALLBACK( action_cb ) }, 150 { "select-all", GTK_STOCK_SELECT_ALL, 151 N_( "Select _All" ), "<control>A", NULL, G_CALLBACK( action_cb ) }, 152 { "deselect-all", NULL, 153 N_( "Dese_lect All" ), "<shift><control>A", NULL, G_CALLBACK( 154 action_cb ) }, 155 { "edit-preferences", GTK_STOCK_PREFERENCES, NULL, 156 NULL, NULL, G_CALLBACK( action_cb ) }, 157 { "show-torrent-details", GTK_STOCK_INFO, 158 N_( "_Details" ), "<alt>Return", N_( "Torrent details" ), G_CALLBACK( 159 action_cb ) }, 160 { "open-torrent-folder", GTK_STOCK_OPEN, 161 N_( "_Open Folder" ), NULL, NULL, G_CALLBACK( action_cb ) }, 162 { "show-about-dialog", GTK_STOCK_ABOUT, NULL, 163 NULL, NULL, G_CALLBACK( action_cb ) }, 164 { "help", GTK_STOCK_HELP, N_( "_Contents" ), 165 "F1", NULL, G_CALLBACK( action_cb ) }, 166 { "update-tracker", GTK_STOCK_NETWORK, 167 N_( "Ask Tracker for _More Peers" ), NULL, NULL, G_CALLBACK( 168 action_cb ) }, 169 { "present-main-window", NULL, NULL, 170 NULL, NULL, G_CALLBACK( action_cb ) }, 134 171 }; 135 172 136 173 typedef struct 137 174 { 138 const guint8*raw;139 const char *name;175 const guint8* raw; 176 const char * name; 140 177 } 141 178 BuiltinIconInfo; 142 179 143 static const BuiltinIconInfo my_fallback_icons 144 { 145 { tr_icon_logo, "transmission" },180 static const BuiltinIconInfo my_fallback_icons[] = 181 { 182 { tr_icon_logo, "transmission" }, 146 183 { tr_icon_lock, "transmission-lock" } 147 184 }; 148 185 149 186 static void 150 register_my_icons 151 { 152 int i;153 const int n = G_N_ELEMENTS( my_fallback_icons );187 register_my_icons( void ) 188 { 189 int i; 190 const int n = G_N_ELEMENTS( my_fallback_icons ); 154 191 GtkIconFactory * factory = gtk_icon_factory_new( ); 155 GtkIconTheme * theme = gtk_icon_theme_get_default( ); 192 GtkIconTheme * theme = gtk_icon_theme_get_default( ); 193 156 194 gtk_icon_factory_add_default( factory ); 157 195 158 for( i =0; i<n; ++i )196 for( i = 0; i < n; ++i ) 159 197 { 160 198 const char * name = my_fallback_icons[i].name; … … 162 200 if( !gtk_icon_theme_has_icon( theme, name ) ) 163 201 { 164 int width;165 GdkPixbuf * p;202 int width; 203 GdkPixbuf * p; 166 204 GtkIconSet * icon_set; 167 205 168 p = gdk_pixbuf_new_from_inline( -1, my_fallback_icons[i].raw, FALSE, NULL ); 206 p = 207 gdk_pixbuf_new_from_inline( -1, my_fallback_icons[i].raw, 208 FALSE, 209 NULL ); 169 210 width = gdk_pixbuf_get_width( p ); 170 211 icon_set = gtk_icon_set_new_from_pixbuf( p ); … … 177 218 } 178 219 179 g_object_unref ( G_OBJECT (factory));220 g_object_unref ( G_OBJECT ( factory ) ); 180 221 } 181 222 … … 189 230 190 231 void 191 actions_init( GtkUIManager * ui_manager, gpointer callback_user_data ) 192 { 193 int i, n; 194 int active; 195 const char * match; 196 const int n_entries = G_N_ELEMENTS( entries ); 197 GtkActionGroup * action_group; 198 199 myUIManager = ui_manager; 200 201 register_my_icons( ); 202 203 action_group = myGroup = gtk_action_group_new( "Actions" ); 204 gtk_action_group_set_translation_domain( action_group, NULL ); 205 206 207 match = pref_string_get( PREF_KEY_SORT_MODE ); 208 for( i=0, n=G_N_ELEMENTS(sort_radio_entries), active=-1; active==-1 && i<n; ++i ) 209 if( !strcmp( sort_radio_entries[i].name, match ) ) 210 active = i; 211 212 gtk_action_group_add_radio_actions( action_group, 213 sort_radio_entries, 214 G_N_ELEMENTS(sort_radio_entries), 215 active, 216 G_CALLBACK(sort_changed_cb), 217 NULL ); 218 219 gtk_action_group_add_toggle_actions( action_group, 220 show_toggle_entries, 221 G_N_ELEMENTS(show_toggle_entries), 222 callback_user_data ); 223 224 for( i=0, n=G_N_ELEMENTS(pref_toggle_entries); i<n; ++i ) 225 pref_toggle_entries[i].is_active = 226 pref_flag_get( pref_toggle_entries[i].name ); 227 228 gtk_action_group_add_toggle_actions( action_group, 229 pref_toggle_entries, 230 G_N_ELEMENTS(pref_toggle_entries), 231 callback_user_data ); 232 233 gtk_action_group_add_actions( action_group, 234 entries, n_entries, 235 callback_user_data ); 236 237 gtk_ui_manager_insert_action_group( ui_manager, action_group, 0 ); 238 g_object_unref (G_OBJECT(action_group)); 232 actions_init( GtkUIManager * ui_manager, 233 gpointer callback_user_data ) 234 { 235 int i, n; 236 int active; 237 const char * match; 238 const int n_entries = G_N_ELEMENTS( entries ); 239 GtkActionGroup * action_group; 240 241 myUIManager = ui_manager; 242 243 register_my_icons( ); 244 245 action_group = myGroup = gtk_action_group_new( "Actions" ); 246 gtk_action_group_set_translation_domain( action_group, NULL ); 247 248 249 match = pref_string_get( PREF_KEY_SORT_MODE ); 250 for( i = 0, n = G_N_ELEMENTS( sort_radio_entries ), active = -1; 251 active == -1 && i < n; ++i ) 252 if( !strcmp( sort_radio_entries[i].name, match ) ) 253 active = i; 254 255 gtk_action_group_add_radio_actions( action_group, 256 sort_radio_entries, 257 G_N_ELEMENTS( sort_radio_entries ), 258 active, 259 G_CALLBACK( sort_changed_cb ), 260 NULL ); 261 262 gtk_action_group_add_toggle_actions( action_group, 263 show_toggle_entries, 264 G_N_ELEMENTS( show_toggle_entries ), 265 callback_user_data ); 266 267 for( i = 0, n = G_N_ELEMENTS( pref_toggle_entries ); i < n; ++i ) 268 pref_toggle_entries[i].is_active = 269 pref_flag_get( pref_toggle_entries[i].name ); 270 271 gtk_action_group_add_toggle_actions( action_group, 272 pref_toggle_entries, 273 G_N_ELEMENTS( pref_toggle_entries ), 274 callback_user_data ); 275 276 gtk_action_group_add_actions( action_group, 277 entries, n_entries, 278 callback_user_data ); 279 280 gtk_ui_manager_insert_action_group( ui_manager, action_group, 0 ); 281 g_object_unref ( G_OBJECT( action_group ) ); 239 282 } 240 283 … … 246 289 247 290 static void 248 ensure_action_map_loaded (GtkUIManager * uim)291 ensure_action_map_loaded( GtkUIManager * uim ) 249 292 { 250 293 GList * l; 251 294 252 if 295 if( key_to_action != NULL ) 253 296 return; 254 297 255 298 key_to_action = 256 g_hash_table_new_full( g_str_hash, g_str_equal, g_free, NULL); 257 258 for( l=gtk_ui_manager_get_action_groups(uim); l!=NULL; l=l->next ) 299 g_hash_table_new_full( g_str_hash, g_str_equal, g_free, NULL ); 300 301 for( l = gtk_ui_manager_get_action_groups( uim ); l != NULL; 302 l = l->next ) 259 303 { 260 304 GtkActionGroup * action_group = GTK_ACTION_GROUP( l->data ); 261 GList *ait, *actions = gtk_action_group_list_actions( action_group ); 262 for( ait=actions; ait!=NULL; ait=ait->next ) 305 GList * ait, *actions = gtk_action_group_list_actions( 306 action_group ); 307 for( ait = actions; ait != NULL; ait = ait->next ) 263 308 { 264 GtkAction * action = GTK_ACTION( ait->data );309 GtkAction * action = GTK_ACTION( ait->data ); 265 310 const char * name = gtk_action_get_name( action ); 266 g_hash_table_insert( key_to_action, g_strdup( name), action );311 g_hash_table_insert( key_to_action, g_strdup( name ), action ); 267 312 } 268 313 g_list_free( actions ); … … 278 323 279 324 void 280 action_activate 325 action_activate( const char * name ) 281 326 { 282 327 GtkAction * action = get_action( name ); 328 283 329 g_assert( action != NULL ); 284 330 gtk_action_activate( action ); … … 286 332 287 333 void 288 action_sensitize( const char * name, gboolean b ) 334 action_sensitize( const char * name, 335 gboolean b ) 289 336 { 290 337 GtkAction * action = get_action( name ); 338 291 339 g_assert( action != NULL ); 292 340 g_object_set( action, "sensitive", b, NULL ); … … 294 342 295 343 void 296 action_toggle( const char * name, gboolean b ) 344 action_toggle( const char * name, 345 gboolean b ) 297 346 { 298 347 GtkAction * action = get_action( name ); 299 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(action), b ); 348 349 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ), b ); 300 350 } 301 351 … … 305 355 return gtk_ui_manager_get_widget( myUIManager, path ); 306 356 } 357 -
trunk/gtk/actions.h
r6490 r6795 4 4 * This file is licensed by the GPL version 2. Works owned by the 5 5 * Transmission project are granted a special exemption to clause 2(b) 6 * so that the bulk of its code can remain under the MIT license. 6 * so that the bulk of its code can remain under the MIT license. 7 7 * This exemption does not extend to derived works not owned by 8 8 * the Transmission project. 9 * 9 * 10 10 * $Id$ 11 11 */ … … 17 17 #include "tr-core.h" 18 18 19 void actions_init ( GtkUIManager * ui_manager, gpointer callback_user_data ); 19 void actions_init( GtkUIManager * ui_manager, 20 gpointer callback_user_data ); 20 21 21 void actions_set_core( TrCore * core );22 void actions_set_core( TrCore * core ); 22 23 23 void action_activate( const char * name );24 void action_activate( const char * name ); 24 25 25 void action_sensitize ( const char * name, gboolean b ); 26 void action_sensitize( const char * name, 27 gboolean b ); 26 28 27 void action_toggle ( const char * name, gboolean b ); 29 void action_toggle( const char * name, 30 gboolean b ); 28 31 29 GtkWidget* action_get_widget 32 GtkWidget* action_get_widget( const char * path ); 30 33 31 void doAction( const char * action_name, gpointer user_data ); 34 void doAction( const char * action_name, 35 gpointer user_data ); 32 36 33 37 #endif -
trunk/gtk/add-dialog.c
r6512 r6795 4 4 * This file is licensed by the GPL version 2. Works owned by the 5 5 * Transmission project are granted a special exemption to clause 2(b) 6 * so that the bulk of its code can remain under the MIT license. 6 * so that the bulk of its code can remain under the MIT license. 7 7 * This exemption does not extend to derived works not owned by 8 8 * the Transmission project. 9 * 9 * 10 10 * $Id$ 11 11 */ … … 28 28 get_recent_destinations( void ) 29 29 { 30 int i;30 int i; 31 31 GSList * list = NULL; 32 for( i=0; i<N_RECENT; ++i ) 33 { 34 char key[64]; 32 33 for( i = 0; i < N_RECENT; ++i ) 34 { 35 char key[64]; 35 36 const char * val; 36 g_snprintf( key, sizeof( key), "recent-download-dir-%d", i+1 );37 if( ( val = pref_string_get( key )))37 g_snprintf( key, sizeof( key ), "recent-download-dir-%d", i + 1 ); 38 if( ( val = pref_string_get( key ) ) ) 38 39 list = g_slist_append( list, (void*)val ); 39 40 } … … 44 45 save_recent_destination( const char * dir ) 45 46 { 46 int i;47 int i; 47 48 GSList * list = get_recent_destinations( ); 48 49 GSList * l; … … 52 53 53 54 /* if it was already in the list, remove it */ 54 if( ( l = g_slist_find_custom( list, dir, (GCompareFunc)strcmp )))55 if( ( l = g_slist_find_custom( list, dir, (GCompareFunc)strcmp ) ) ) 55 56 list = g_slist_delete_link( list, l ); 56 57 … … 60 61 /* make local copies of the strings that aren't 61 62 * invalidated by pref_string_set() */ 62 for( l =list; l; l=l->next )63 63 for( l = list; l; l = l->next ) 64 l->data = g_strdup( l->data ); 64 65 65 66 /* save the first N_RECENT directories */ 66 for( l=list, i=0; l && (i<N_RECENT); ++i, l=l->next ) { 67 for( l = list, i = 0; l && ( i < N_RECENT ); ++i, l = l->next ) 68 { 67 69 char key[64]; 68 g_snprintf( key, sizeof( key), "recent-download-dir-%d", i+1 );70 g_snprintf( key, sizeof( key ), "recent-download-dir-%d", i + 1 ); 69 71 pref_string_set( key, l->data ); 70 72 } … … 82 84 struct AddData 83 85 { 84 TrCore * core;85 GtkWidget * list;86 GtkWidget * run_check;87 GtkWidget * trash_check;88 char * filename;89 char * downloadDir;90 TrTorrent * gtor;91 tr_ctor * ctor;86 TrCore * core; 87 GtkWidget * list; 88 GtkWidget * run_check; 89 GtkWidget * trash_check; 90 char * filename; 91 char * downloadDir; 92 TrTorrent * gtor; 93 tr_ctor * ctor; 92 94 }; 93 95 … … 106 108 107 109 static void 108 addResponseCB( GtkDialog * dialog, gint response, gpointer gdata ) 110 addResponseCB( GtkDialog * dialog, 111 gint response, 112 gpointer gdata ) 109 113 { 110 114 struct AddData * data = gdata; … … 114 118 if( response != GTK_RESPONSE_ACCEPT ) 115 119 removeOldTorrent( data ); 116 else { 117 if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( data->run_check ) ) ) 120 else 121 { 122 if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( data-> 123 run_check ) ) ) 118 124 tr_torrentStart( tr_torrent_handle( data->gtor ) ); 119 125 tr_core_add_torrent( data->core, data->gtor ); 120 if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( data->trash_check ) ) ) 126 if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( data-> 127 trash_check ) ) ) 121 128 tr_file_trash_or_unlink( data->filename ); 122 129 save_recent_destination( data->downloadDir ); … … 135 142 { 136 143 if( o->gtor ) 137 tr_torrentSetDownloadDir( tr_torrent_handle( o->gtor ), o->downloadDir ); 144 tr_torrentSetDownloadDir( tr_torrent_handle( 145 o->gtor ), o->downloadDir ); 138 146 139 147 file_list_set_torrent( o->list, o->gtor ); … … 148 156 */ 149 157 static void 150 sourceChanged( GtkFileChooserButton * b, gpointer gdata ) 158 sourceChanged( GtkFileChooserButton * b, 159 gpointer gdata ) 151 160 { 152 161 struct AddData * data = gdata; 153 char * filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( b ) ); 162 char * filename = gtk_file_chooser_get_filename( 163 GTK_FILE_CHOOSER( b ) ); 154 164 155 165 /* maybe instantiate a torrent */ 156 166 if( data->filename || !data->gtor ) 157 167 { 158 int err = 0;159 int new_file = 0;168 int err = 0; 169 int new_file = 0; 160 170 tr_torrent * torrent; 161 tr_handle * handle = tr_core_handle( data->core ); 162 163 if( filename && ( !data->filename || strcmp( filename, data->filename ) ) ) 171 tr_handle * handle = tr_core_handle( data->core ); 172 173 if( filename 174 && ( !data->filename || strcmp( filename, data->filename ) ) ) 164 175 { 165 176 g_free( data->filename ); … … 173 184 tr_ctorSetDeleteSource( data->ctor, FALSE ); 174 185 175 if(( torrent = tr_torrentNew( handle, data->ctor, &err ))) { 186 if( ( torrent = tr_torrentNew( handle, data->ctor, &err ) ) ) 187 { 176 188 removeOldTorrent( data ); 177 189 data->gtor = tr_torrent_new_preexisting( torrent ); 178 } else if( new_file ) {179 addTorrentErrorDialog( GTK_WIDGET(b), err, data->filename );180 190 } 191 else if( new_file ) 192 { 193 addTorrentErrorDialog( GTK_WIDGET( b ), err, data->filename ); 194 } 181 195 182 196 updateTorrent( data ); … … 187 201 188 202 static void 189 verifyRequested( GtkButton * button UNUSED, gpointer gdata ) 203 verifyRequested( GtkButton * button UNUSED, 204 gpointer gdata ) 190 205 { 191 206 struct AddData * data = gdata; 207 192 208 if( data->gtor ) 193 209 tr_torrentVerify( tr_torrent_handle( data->gtor ) ); … … 195 211 196 212 static void 197 downloadDirChanged( GtkFileChooserButton * b, gpointer gdata ) 198 { 199 char * fname = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( b ) ); 213 downloadDirChanged( GtkFileChooserButton * b, 214 gpointer gdata ) 215 { 216 char * fname = gtk_file_chooser_get_filename( 217 GTK_FILE_CHOOSER( b ) ); 200 218 struct AddData * data = gdata; 201 219 … … 233 251 234 252 GtkWidget* 235 addSingleTorrentDialog( GtkWindow 236 TrCore *core,237 tr_ctor *ctor )238 { 239 int row;240 int col;241 const char * str;242 GtkWidget * w;243 GtkWidget * d;244 GtkWidget * t;245 GtkWidget * l;253 addSingleTorrentDialog( GtkWindow * parent, 254 TrCore * core, 255 tr_ctor * ctor ) 256 { 257 int row; 258 int col; 259 const char * str; 260 GtkWidget * w; 261 GtkWidget * d; 262 GtkWidget * t; 263 GtkWidget * l; 246 264 struct AddData * data; 247 uint8_t flag;248 GSList * list;249 GSList * walk;265 uint8_t flag; 266 GSList * list; 267 GSList * walk; 250 268 251 269 /* make the dialog */ 252 d = gtk_dialog_new_with_buttons( _( "Torrent Options" ), parent, 253 GTK_DIALOG_DESTROY_WITH_PARENT|GTK_DIALOG_NO_SEPARATOR, 254 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 255 GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT, 256 NULL ); 270 d = gtk_dialog_new_with_buttons( _( 271 "Torrent Options" ), parent, 272 GTK_DIALOG_DESTROY_WITH_PARENT | 273 GTK_DIALOG_NO_SEPARATOR, 274 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 275 GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT, 276 NULL ); 257 277 gtk_dialog_set_default_response( GTK_DIALOG( d ), 258 278 GTK_RESPONSE_ACCEPT ); … … 272 292 data->downloadDir = g_strdup( str ); 273 293 data->list = file_list_new( NULL ); 274 data->trash_check = gtk_check_button_new_with_mnemonic( _( "_Move source file to Trash" ) ); 275 data->run_check = gtk_check_button_new_with_mnemonic( _( "_Start when added" ) ); 294 data->trash_check = 295 gtk_check_button_new_with_mnemonic( _( "_Move source file to Trash" ) ); 296 data->run_check = 297 gtk_check_button_new_with_mnemonic( _( "_Start when added" ) ); 276 298 277 299 g_signal_connect( G_OBJECT( d ), "response", … … 286 308 l = gtk_label_new_with_mnemonic( _( "_Torrent file:" ) ); 287 309 gtk_misc_set_alignment( GTK_MISC( l ), 0.0f, 0.5f ); 288 gtk_table_attach( GTK_TABLE( t ), l, col, col+1, row, row+1, GTK_FILL, 0, 0, 0 ); 310 gtk_table_attach( GTK_TABLE( 311 t ), l, col, col + 1, row, row + 1, GTK_FILL, 0, 312 0, 0 ); 289 313 ++col; 290 314 w = gtk_file_chooser_button_new( _( "Select Source File" ), 291 315 GTK_FILE_CHOOSER_ACTION_OPEN ); 292 gtk_table_attach( GTK_TABLE( t ), w, col, col+1, row, row+1, ~0, 0, 0, 0 ); 316 gtk_table_attach( GTK_TABLE( 317 t ), w, col, col + 1, row, row + 1, ~0, 0, 0, 0 ); 293 318 gtk_label_set_mnemonic_widget( GTK_LABEL( l ), w ); 294 319 addTorrentFilters( GTK_FILE_CHOOSER( w ) ); 295 320 if( data->filename ) 296 if( !gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( w ), data->filename ) ) 321 if( !gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( w ), 322 data->filename ) ) 297 323 g_warning( "couldn't select '%s'", data->filename ); 298 324 g_signal_connect( w, "selection-changed", … … 303 329 l = gtk_label_new_with_mnemonic( _( "_Destination folder:" ) ); 304 330 gtk_misc_set_alignment( GTK_MISC( l ), 0.0f, 0.5f ); 305 gtk_table_attach( GTK_TABLE( t ), l, col, col+1, row, row+1, GTK_FILL, 0, 0, 0 ); 331 gtk_table_attach( GTK_TABLE( 332 t ), l, col, col + 1, row, row + 1, GTK_FILL, 0, 333 0, 0 ); 306 334 ++col; 307 w = gtk_file_chooser_button_new( _( "Select Destination Folder" ), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ); 308 if( !gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( w ), data->downloadDir ) ) 335 w = gtk_file_chooser_button_new( _( 336 "Select Destination Folder" ), 337 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ); 338 if( !gtk_file_chooser_set_filename( GTK_FILE_CHOOSER( w ), 339 data->downloadDir ) ) 309 340 g_warning( "couldn't select '%s'", data->downloadDir ); 310 341 list = get_recent_destinations( ); 311 for( walk=list; walk; walk=walk->next ) 312 gtk_file_chooser_add_shortcut_folder( GTK_FILE_CHOOSER( w ), walk->data, NULL ); 342 for( walk = list; walk; walk = walk->next ) 343 gtk_file_chooser_add_shortcut_folder( GTK_FILE_CHOOSER( 344 w ), walk->data, NULL ); 313 345 g_slist_free( list ); 314 gtk_table_attach( GTK_TABLE( t ), w, col, col+1, row, row+1, ~0, 0, 0, 0 ); 346 gtk_table_attach( GTK_TABLE( 347 t ), w, col, col + 1, row, row + 1, ~0, 0, 0, 0 ); 315 348 gtk_label_set_mnemonic_widget( GTK_LABEL( l ), w ); 316 g_signal_connect( w, "selection-changed", G_CALLBACK( downloadDirChanged ), data ); 349 g_signal_connect( w, "selection-changed", 350 G_CALLBACK( downloadDirChanged ), data ); 317 351 318 352 ++row; … … 320 354 w = data->list; 321 355 gtk_widget_set_size_request ( w, 466u, 300u ); 322 gtk_table_attach_defaults( GTK_TABLE( t ), w, col, col+2, row, row+1 ); 356 gtk_table_attach_defaults( GTK_TABLE( 357 t ), w, col, col + 2, row, row + 1 ); 323 358 324 359 ++row; 325 360 col = 0; 326 361 w = gtk_button_new_with_mnemonic( _( "_Verify Local Data" ) ); 327 gtk_table_attach( GTK_TABLE( t ), w, col, col+1, row, row+1, GTK_FILL, 0, 0, 0 ); 362 gtk_table_attach( GTK_TABLE( 363 t ), w, col, col + 1, row, row + 1, GTK_FILL, 0, 364 0, 0 ); 328 365 g_signal_connect( w, "clicked", G_CALLBACK( verifyRequested ), data ); 329 366 … … 334 371 g_assert_not_reached( ); 335 372 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), !flag ); 336 gtk_table_attach( GTK_TABLE( t ), w, col, col+2, row, row+1, GTK_FILL, 0, 0, 0 ); 373 gtk_table_attach( GTK_TABLE( 374 t ), w, col, col + 2, row, row + 1, GTK_FILL, 0, 375 0, 0 ); 337 376 338 377 ++row; … … 342 381 g_assert_not_reached( ); 343 382 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( w ), flag ); 344 gtk_table_attach( GTK_TABLE( t ), w, col, col+2, row, row+1, GTK_FILL, 0, 0, 0 ); 383 gtk_table_attach( GTK_TABLE( 384 t ), w, col, col + 2, row, row + 1, GTK_FILL, 0, 385 0, 0 ); 345 386 346 387 gtk_box_pack_start_defaults( GTK_BOX( GTK_DIALOG( d )->vbox ), t ); … … 354 395 355 396 static void 356 onAddDialogResponse( GtkDialog * dialog, int response, gpointer core ) 397 onAddDialogResponse( GtkDialog * dialog, 398 int response, 399 gpointer core ) 357 400 { 358 401 char * folder; … … 365 408 if( response == GTK_RESPONSE_ACCEPT ) 366 409 { 367 GtkWidget * w = gtk_file_chooser_get_extra_widget( GTK_FILE_CHOOSER( dialog ) ); 368 const gboolean showOptions = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON ( w ) ); 410 GtkWidget * w = gtk_file_chooser_get_extra_widget( 411 GTK_FILE_CHOOSER( dialog ) ); 412 const gboolean showOptions = gtk_toggle_button_get_active( 413 GTK_TOGGLE_BUTTON ( w ) ); 369 414 const pref_flag_t start = PREF_FLAG_DEFAULT; 370 const pref_flag_t prompt = showOptions ? PREF_FLAG_TRUE : PREF_FLAG_FALSE; 371 GSList * l = gtk_file_chooser_get_filenames( GTK_FILE_CHOOSER( dialog ) ); 415 const pref_flag_t prompt = 416 showOptions ? PREF_FLAG_TRUE : PREF_FLAG_FALSE; 417 GSList * l = gtk_file_chooser_get_filenames( 418 GTK_FILE_CHOOSER( dialog ) ); 372 419 tr_core_add_list( core, l, start, prompt ); 373 420 } … … 378 425 GtkWidget* 379 426 addDialog( GtkWindow * parent, 380 TrCore *core )381 { 382 GtkWidget * w;383 GtkWidget * c;427 TrCore * core ) 428 { 429 GtkWidget * w; 430 GtkWidget * c; 384 431 const char * folder; 385 432 … … 395 442 gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER( w ), TRUE ); 396 443 addTorrentFilters( GTK_FILE_CHOOSER( w ) ); 397 g_signal_connect( w, "response", G_CALLBACK(onAddDialogResponse), core ); 398 399 if(( folder = pref_string_get( PREF_KEY_OPEN_DIALOG_FOLDER ))) 444 g_signal_connect( w, "response", G_CALLBACK( 445 onAddDialogResponse ), core ); 446 447 if( ( folder = pref_string_get( PREF_KEY_OPEN_DIALOG_FOLDER ) ) ) 400 448 gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( w ), folder ); 401 449 402 450 c = gtk_check_button_new_with_mnemonic( _( "Display _options dialog" ) ); 403 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( c ), pref_flag_get( PREF_KEY_OPTIONS_PROMPT ) ); 451 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( c ), 452 pref_flag_get( PREF_KEY_OPTIONS_PROMPT ) ); 404 453 gtk_file_chooser_set_extra_widget( GTK_FILE_CHOOSER( w ), c ); 405 454 gtk_widget_show( c ); … … 408 457 return w; 409 458 } 459 -
trunk/gtk/add-dialog.h
r5496 r6795 4 4 * This file is licensed by the GPL version 2. Works owned by the 5 5 * Transmission project are granted a special exemption to clause 2(b) 6 * so that the bulk of its code can remain under the MIT license. 6 * so that the bulk of its code can remain under the MIT license. 7 7 * This exemption does not extend to derived works not owned by 8 8 * the Transmission project. 9 * 9 * 10 10 * $Id$ 11 11 */ … … 18 18 19 19 /* This dialog assumes ownership of the ctor */ 20 GtkWidget* addSingleTorrentDialog( GtkWindow 21 TrCore *core,22 tr_ctor *ctor );20 GtkWidget* addSingleTorrentDialog( GtkWindow * parent, 21 TrCore * core, 22 tr_ctor * ctor ); 23 23 24 24 GtkWidget* addDialog( GtkWindow * parent, 25 TrCore *core );25 TrCore * core ); 26 26 27 27 #endif /* TR_GTK_ADD_DIALOG */ -
trunk/gtk/blocklist.c
r6687 r6795 1 2 1 #include <stdarg.h> 3 2 #include <stdlib.h> /* getenv() */ … … 26 25 struct idle_data 27 26 { 28 TrCore * core;29 gboolean isDone;30 char * str;27 TrCore * core; 28 gboolean isDone; 29 char * str; 31 30 }; 32 31 static gboolean … … 41 40 return FALSE; 42 41 } 42 43 43 static void 44 emitProgress( TrCore * core, gboolean isDone, const char * fmt, ... ) 44 emitProgress( TrCore * core, 45 gboolean isDone, 46 const char * fmt, 47 ... ) 45 48 { 46 49 struct idle_data * data = tr_new0( struct idle_data, 1 ); 47 va_list args;50 va_list args; 48 51 49 52 data->core = core; … … 62 65 63 66 static size_t 64 writeFunc( void * ptr, size_t size, size_t nmemb, void * fd ) 67 writeFunc( void * ptr, 68 size_t size, 69 size_t nmemb, 70 void * fd ) 65 71 { 66 72 const size_t byteCount = size * nmemb; 73 67 74 return write( *(int*)fd, ptr, byteCount ); 68 75 } … … 71 78 blocklistThreadFunc( gpointer gcore ) 72 79 { 73 TrCore * core = TR_CORE( gcore ); 74 const char * url = "http://download.m0k.org/transmission/files/level1.gz"; 75 gboolean ok = TRUE; 76 char * filename = NULL; 77 char * filename2 = NULL; 78 int fd; 79 int rules; 80 TrCore * core = TR_CORE( gcore ); 81 const char * url = 82 "http://download.m0k.org/transmission/files/level1.gz"; 83 gboolean ok = TRUE; 84 char * filename = NULL; 85 char * filename2 = NULL; 86 int fd; 87 int rules; 80 88 81 89 emitProgress( core, FALSE, _( "Retrieving blocklist..." ) ); … … 84 92 { 85 93 GError * err = NULL; 86 fd = g_file_open_tmp( "transmission-blockfile-XXXXXX", &filename, &err ); 87 if( err ) { 88 emitProgress( core, TRUE, _( "Unable to get blocklist: %s" ), err->message ); 94 fd = g_file_open_tmp( "transmission-blockfile-XXXXXX", &filename, 95 &err ); 96 if( err ) 97 { 98 emitProgress( core, TRUE, _( 99 "Unable to get blocklist: %s" ), err->message ); 89 100 g_clear_error( &err ); 90 101 ok = FALSE; … … 94 105 if( ok ) 95 106 { 96 CURL * curl = curl_easy_init( ); 107 CURL * curl = curl_easy_init( ); 97 108 curl_easy_setopt( curl, CURLOPT_URL, url ); 98 109 curl_easy_setopt( curl, CURLOPT_ENCODING, "deflate" ); 99 curl_easy_setopt( curl, CURLOPT_USERAGENT, "Transmission/" LONG_VERSION_STRING ); 100 curl_easy_setopt( curl, CURLOPT_VERBOSE, getenv( "TR_CURL_VERBOSE" ) != NULL ); 110 curl_easy_setopt( curl, CURLOPT_USERAGENT, 111 "Transmission/" LONG_VERSION_STRING ); 112 curl_easy_setopt( curl, CURLOPT_VERBOSE, getenv( 113 "TR_CURL_VERBOSE" ) != NULL ); 101 114 curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc ); 102 115 curl_easy_setopt( curl, CURLOPT_WRITEDATA, &fd ); … … 131 144 if( ok ) 132 145 { 133 emitProgress( core, TRUE, _( "Blocklist updated with %'d entries" ), rules ); 146 emitProgress( core, TRUE, _( 147 "Blocklist updated with %'d entries" ), rules ); 134 148 pref_int_set( BLOCKLIST_DATE, time( NULL ) ); 135 149 } … … 154 168 { 155 169 if( pref_flag_get( PREF_KEY_BLOCKLIST_UPDATES_ENABLED ) 156 && ( time( NULL ) - pref_int_get( BLOCKLIST_DATE ) > (60*60*24*7) ) ) 170 && ( time( NULL ) - pref_int_get( BLOCKLIST_DATE ) > 171 ( 60 * 60 * 24 * 7 ) ) ) 157 172 gtr_blocklist_update( core ); 158 173 } 174 -
trunk/gtk/conf.c
r6687 r6795 47 47 /* errstr may be NULL, this might be called before GTK is initialized */ 48 48 gboolean 49 cf_init(const char *dir, char **errstr) 49 cf_init( const char *dir, 50 char ** errstr ) 50 51 { 51 52 if( errstr != NULL ) … … 59 60 if( errstr != NULL ) 60 61 *errstr = g_strdup_printf( _( "Couldn't create \"%1$s\": %2$s" ), 61 gl_confdir, g_strerror(errno) );62 gl_confdir, g_strerror( errno ) ); 62 63 63 64 return FALSE; … … 72 73 /* errstr may be NULL, this might be called before GTK is initialized */ 73 74 static gboolean 74 lockfile(const char * filename, tr_lockfile_state_t *tr_state, char **errstr) 75 lockfile( const char * filename, 76 tr_lockfile_state_t *tr_state, 77 char ** errstr ) 75 78 { 76 79 const tr_lockfile_state_t state = tr_lockfile( filename ); 77 const gboolean success = state == TR_LOCKFILE_SUCCESS; 78 79 if( errstr ) switch( state ) { 80 case TR_LOCKFILE_EOPEN: 81 *errstr = g_strdup_printf( _( "Couldn't open \"%1$s\": %2$s" ), 82 filename, g_strerror( errno ) ); 83 break; 84 case TR_LOCKFILE_ELOCK: 85 *errstr = g_strdup_printf( _( "%s is already running." ), 86 g_get_application_name( ) ); 87 break; 88 case TR_LOCKFILE_SUCCESS: 89 *errstr = NULL; 90 break; 91 } 92 93 if( tr_state != NULL) 80 const gboolean success = state == TR_LOCKFILE_SUCCESS; 81 82 if( errstr ) switch( state ) 83 { 84 case TR_LOCKFILE_EOPEN: 85 *errstr = 86 g_strdup_printf( _( "Couldn't open \"%1$s\": %2$s" ), 87 filename, g_strerror( errno ) ); 88 break; 89 90 case TR_LOCKFILE_ELOCK: 91 *errstr = g_strdup_printf( _( "%s is already running." ), 92 g_get_application_name( ) ); 93 break; 94 95 case TR_LOCKFILE_SUCCESS: 96 *errstr = NULL; 97 break; 98 } 99 100 if( tr_state != NULL ) 94 101 *tr_state = state; 95 102 … … 107 114 cf_removelocks( void ) 108 115 { 109 if( gl_lockpath ) { 110 g_unlink( gl_lockpath ); 111 g_free( gl_lockpath ); 116 if( gl_lockpath ) 117 { 118 g_unlink( gl_lockpath ); 119 g_free( gl_lockpath ); 112 120 } 113 121 } … … 115 123 /* errstr may be NULL, this might be called before GTK is initialized */ 116 124 gboolean 117 cf_lock( tr_lockfile_state_t *tr_state, char ** errstr ) 118 { 119 char * path = getLockFilename( ); 125 cf_lock( tr_lockfile_state_t *tr_state, 126 char ** errstr ) 127 { 128 char * path = getLockFilename( ); 120 129 const gboolean didLock = lockfile( path, tr_state, errstr ); 130 121 131 if( didLock ) 122 132 gl_lockpath = g_strdup( path ); … … 142 152 getPrefs( void ) 143 153 { 144 static tr_benc dict;154 static tr_benc dict; 145 155 static gboolean loaded = FALSE; 146 156 … … 165 175 { 166 176 int64_t i = 0; 177 167 178 tr_bencDictFindInt( getPrefs( ), key, &i ); 168 179 return i; 169 180 } 170 void 171 pref_int_set( const char * key, int64_t value ) 181 182 void 183 pref_int_set( const char * key, 184 int64_t value ) 172 185 { 173 186 tr_benc * d = getPrefs( ); 187 174 188 tr_bencDictRemove( d, key ); 175 189 tr_bencDictAddInt( d, key, value ); 176 190 } 177 void 178 pref_int_set_default( const char * key, int64_t value ) 191 192 void 193 pref_int_set_default( const char * key, 194 int64_t value ) 179 195 { 180 196 if( !tr_bencDictFind( getPrefs( ), key ) ) … … 187 203 188 204 gboolean 189 pref_flag_get 205 pref_flag_get( const char * key ) 190 206 { 191 207 int64_t i; 208 192 209 tr_bencDictFindInt( getPrefs( ), key, &i ); 193 210 return i != 0; 194 211 } 212 195 213 gboolean 196 pref_flag_eval( pref_flag_t val, const char * key ) 197 { 198 switch( val ) { 199 case PREF_FLAG_TRUE: return TRUE; 200 case PREF_FLAG_FALSE: return FALSE; 201 default: return pref_flag_get( key ); 202 } 203 } 204 void 205 pref_flag_set( const char * key, gboolean value ) 206 { 207 pref_int_set( key, value!=0 ); 208 } 209 void 210 pref_flag_set_default( const char * key, gboolean value ) 211 { 212 pref_int_set_default( key, value!=0 ); 214 pref_flag_eval( pref_flag_t val, 215 const char * key ) 216 { 217 switch( val ) 218 { 219 case PREF_FLAG_TRUE: 220 return TRUE; 221 222 case PREF_FLAG_FALSE: 223 return FALSE; 224 225 default: 226 return pref_flag_get( key ); 227 } 228 } 229 230 void 231 pref_flag_set( const char * key, 232 gboolean value ) 233 { 234 pref_int_set( key, value != 0 ); 235 } 236 237 void 238 pref_flag_set_default( const char * key, 239 gboolean value ) 240 { 241 pref_int_set_default( key, value != 0 ); 213 242 } 214 243 … … 221 250 { 222 251 const char * str = NULL; 252 223 253 tr_bencDictFindStr( getPrefs( ), key, &str ); 224 254 return str; … … 226 256 227 257 void 228 pref_string_set( const char * key, const char * value ) 258 pref_string_set( const char * key, 259 const char * value ) 229 260 { 230 261 tr_benc * d = getPrefs( ); 262 231 263 tr_bencDictRemove( d, key ); 232 264 tr_bencDictAddStr( d, key, value ); … … 234 266 235 267 void 236 pref_string_set_default( const char * key, const char * value ) 268 pref_string_set_default( const char * key, 269 const char * value ) 237 270 { 238 271 if( !tr_bencDictFind( getPrefs( ), key ) ) … … 261 294 ***/ 262 295 263 #if !GLIB_CHECK_VERSION( 2,8,0)296 #if !GLIB_CHECK_VERSION( 2, 8, 0 ) 264 297 static void 265 tr_file_set_contents( const char * filename, const void * out, size_t len, GError* unused UNUSED ) 298 tr_file_set_contents( const char * filename, 299 const void * out, 300 size_t len, 301 GError* unused UNUSED ) 266 302 { 267 303 FILE * fp = fopen( filename, "wb+" ); 268 if( fp != NULL ) { 304 305 if( fp != NULL ) 306 { 269 307 fwrite( out, 1, len, fp ); 270 308 fclose( fp ); 271 309 } 272 310 } 273 #define g_file_set_contents tr_file_set_contents 311 312 #define g_file_set_contents tr_file_set_contents 274 313 #endif 275 314 … … 278 317 { 279 318 assert( gl_confdir != NULL ); 280 return g_build_filename( g_get_home_dir(), ".transmission", "gtk", "prefs", NULL ); 319 return g_build_filename( 320 g_get_home_dir( ), ".transmission", "gtk", "prefs", NULL ); 281 321 } 282 322 … … 285 325 { 286 326 assert( gl_confdir != NULL ); 287 return g_build_filename( g_get_home_dir(), ".transmission", "gtk", "prefs.ini", NULL ); 327 return g_build_filename( 328 g_get_home_dir( ), ".transmission", "gtk", "prefs.ini", NULL ); 288 329 } 289 330 … … 291 332 getCompat121PrefsFilename( void ) 292 333 { 293 return g_build_filename( g_get_user_config_dir(), "transmission", "gtk", "prefs.ini", NULL ); 334 return g_build_filename( 335 g_get_user_config_dir( ), "transmission", "gtk", "prefs.ini", 336 NULL ); 294 337 } 295 338 296 339 static void 297 translate_08_to_09( const char* oldfile, const char* newfile ) 298 { 299 static struct pref_entry { 300 const char* oldkey; 301 const char* newkey; 340 translate_08_to_09( const char* oldfile, 341 const char* newfile ) 342 { 343 static struct pref_entry 344 { 345 const char* oldkey; 346 const char* newkey; 302 347 } pref_table[] = { 303 { "add-behavior-ipc", "add-behavior-ipc"},304 { "add-behavior-standard", "add-behavior-standard"},305 { "download-directory", "default-download-directory"},306 { "download-limit", "download-limit"},307 { "use-download-limit", "download-limit-enabled"},308 { "listening-port", "listening-port"},309 { "use-nat-traversal", "nat-traversal-enabled"},310 { "use-peer-exchange", "pex-enabled"},311 { "ask-quit", "prompt-before-exit"},312 { "ask-download-directory", "prompt-for-download-directory"},313 { "use-tray-icon", "system-tray-icon-enabled"},314 { "upload-limit", "upload-limit"},315 { "use-upload-limit", "upload-limit-enabled"}348 { "add-behavior-ipc", "add-behavior-ipc" }, 349 { "add-behavior-standard", "add-behavior-standard" }, 350 { "download-directory", "default-download-directory" }, 351 { "download-limit", "download-limit" }, 352 { "use-download-limit", "download-limit-enabled" }, 353 { "listening-port", "listening-port" }, 354 { "use-nat-traversal", "nat-traversal-enabled" }, 355 { "use-peer-exchange", "pex-enabled" }, 356 { "ask-quit", "prompt-before-exit" }, 357 { "ask-download-directory", "prompt-for-download-directory" }, 358 { "use-tray-icon", "system-tray-icon-enabled" }, 359 { "upload-limit", "upload-limit" }, 360 { "use-upload-limit", "upload-limit-enabled" } 316 361 }; 317 362 318 363 GString * out = g_string_new( NULL ); 319 gchar * contents = NULL;320 gsize contents_len = 0;321 tr_benc top;322 323 memset( &top, 0, sizeof( tr_benc) );364 gchar * contents = NULL; 365 gsize contents_len = 0; 366 tr_benc top; 367 368 memset( &top, 0, sizeof( tr_benc ) ); 324 369 325 370 if( g_file_get_contents( oldfile, &contents, &contents_len, NULL ) 326 327 && top.type==TYPE_DICT )371 && !tr_bencLoad( contents, contents_len, &top, NULL ) 372 && top.type == TYPE_DICT ) 328 373 { 329 374 unsigned int i; 330 375 g_string_append( out, "\n[general]\n" ); 331 for ( i=0; i<G_N_ELEMENTS(pref_table); ++i ) { 332 const tr_benc * val = tr_bencDictFind( &top, pref_table[i].oldkey ); 333 if( val != NULL ) { 376 for( i = 0; i < G_N_ELEMENTS( pref_table ); ++i ) 377 { 378 const tr_benc * val = tr_bencDictFind( &top, 379 pref_table[i].oldkey ); 380 if( val != NULL ) 381 { 334 382 const char * valstr = val->val.s.s; 335 383 if( !strcmp( valstr, "yes" ) ) valstr = "true"; 336 384 if( !strcmp( valstr, "no" ) ) valstr = "false"; 337 g_string_append_printf( out, "%s=%s\n", pref_table[i].newkey, valstr ); 385 g_string_append_printf( out, "%s=%s\n", 386 pref_table[i].newkey, 387 valstr ); 338 388 } 339 389 } … … 346 396 347 397 static void 348 translate_keyfile_to_json( const char * old_file, const char * new_file ) 349 { 350 tr_benc dict; 398 translate_keyfile_to_json( const char * old_file, 399 const char * new_file ) 400 { 401 tr_benc dict; 351 402 GKeyFile * keyfile; 352 gchar ** keys; 353 gsize i; 354 gsize length; 355 356 static struct pref_entry { 357 const char* oldkey; 358 const char* newkey; 403 gchar ** keys; 404 gsize i; 405 gsize length; 406 407 static struct pref_entry 408 { 409 const char* oldkey; 410 const char* newkey; 359 411 } renamed[] = { 360 { "default-download-directory", "download-dir"},361 { "encrypted-connections-only", "encryption" },362 { "listening-port", "peer-port"},363 { "nat-traversal-enabled", "port-forwarding-enabled"},364 { "open-dialog-folder", "open-dialog-dir"},365 { "watch-folder", "watch-dir"},366 { "watch-folder-enabled", "watch-dir-enabled"}412 { "default-download-directory", "download-dir" }, 413 { "encrypted-connections-only", "encryption" }, 414 { "listening-port", "peer-port" }, 415 { "nat-traversal-enabled", "port-forwarding-enabled" }, 416 { "open-dialog-folder", "open-dialog-dir" }, 417 { "watch-folder", "watch-dir" }, 418 { "watch-folder-enabled", "watch-dir-enabled" } 367 419 }; 368 420 … … 373 425 374 426 tr_bencInitDict( &dict, length ); 375 for( i =0; i<length; ++i )376 { 377 guint j;427 for( i = 0; i < length; ++i ) 428 { 429 guint j; 378 430 const char * key = keys[i]; 379 gchar * val = g_key_file_get_value( keyfile, "general", key, NULL ); 380 381 for( j=0; j<G_N_ELEMENTS(renamed); ++j ) 431 gchar * val = g_key_file_get_value( keyfile, "general", key, 432 NULL ); 433 434 for( j = 0; j < G_N_ELEMENTS( renamed ); ++j ) 382 435 if( !strcmp( renamed[j].oldkey, key ) ) 383 436 key = renamed[j].newkey; 384 437 385 if( !strcmp( val,"true") || !strcmp(val,"false") )438 if( !strcmp( val, "true" ) || !strcmp( val, "false" ) ) 386 439 tr_bencDictAddInt( &dict, key, !strcmp( val, "true" ) ); 387 else { 440 else 441 { 388 442 char * end; 389 long l;443 long l; 390 444 errno = 0; 391 445 l = strtol( val, &end, 10 ); … … 426 480 { 427 481 char * tmpfile; 428 int fd = g_file_open_tmp( "transmission-prefs-XXXXXX", &tmpfile, NULL ); 482 int fd = 483 g_file_open_tmp( "transmission-prefs-XXXXXX", &tmpfile, 484 NULL ); 429 485 if( fd != -1 ) close( fd ); 430 486 translate_08_to_09( benc, tmpfile ); … … 440 496 g_free( filename ); 441 497 } 498 -
trunk/gtk/conf.h
r6687 r6795 30 30 #define TG_CONF_H 31 31 32 int64_t pref_int_get ( const char * key ); 33 void pref_int_set ( const char * key, int64_t value ); 34 void pref_int_set_default ( const char * key, int64_t value ); 32 int64_t pref_int_get( const char * key ); 35 33 36 gboolean pref_flag_get ( const char * key ); 37 void pref_flag_set ( const char * key, gboolean value ); 38 void pref_flag_set_default ( const char * key, gboolean value ); 34 void pref_int_set( const char * key, 35 int64_t value ); 39 36 40 const char* pref_string_get ( const char * key ); 41 void pref_string_set ( const char * key, const char * value ); 42 void pref_string_set_default ( const char * key, const char * value ); 37 void pref_int_set_default( const char * key, 38 int64_t value ); 43 39 44 void pref_save ( void ); 40 gboolean pref_flag_get( const char * key ); 41 42 void pref_flag_set( const char * key, 43 gboolean value ); 44 45 void pref_flag_set_default( const char * key, 46 gboolean value ); 47 48 const char* pref_string_get( const char * key ); 49 50 void pref_string_set( const char * key, 51 const char * value ); 52 53 void pref_string_set_default( const char * key, 54 const char * value ); 55 56 void pref_save( void ); 45 57 46 58 /** … … 57 69 typedef int pref_flag_t; 58 70 59 gboolean pref_flag_eval( pref_flag_t val, const char * key ); 71 gboolean pref_flag_eval( pref_flag_t val, 72 const char * key ); 60 73 61 74 … … 64 77 **/ 65 78 66 gboolean 67 cf_init(const char *confdir, char **errstr); 68 gboolean 69 cf_lock(tr_lockfile_state_t *tr_state, char **errstr); 70 void 71 cf_check_older_configs(void); 79 gboolean cf_init( const char *confdir, 80 char ** errstr ); 81 82 gboolean cf_lock( tr_lockfile_state_t *tr_state, 83 char ** errstr ); 84 85 void cf_check_older_configs( void ); 72 86 73 87 #endif /* TG_CONF_H */ -
trunk/gtk/details.c
r6781 r6795 4 4 * This file is licensed by the GPL version 2. Works owned by the 5 5 * Transmission project are granted a special exemption to clause 2(b) 6 * so that the bulk of its code can remain under the MIT license. 6 * so that the bulk of its code can remain under the MIT license. 7 7 * This exemption does not extend to derived works not owned by 8 8 * the Transmission project. 9 * 9 * 10 10 * $Id$ 11 11 */ … … 39 39 #ifdef SHOW_PIECES 40 40 static int 41 getGridSize (int pieceCount, int * n_rows, int * n_cols) 42 { 43 const int MAX_ACROSS = 16; 44 if (pieceCount >= (MAX_ACROSS * MAX_ACROSS)) { 45 *n_rows = *n_cols = MAX_ACROSS; 46 return MAX_ACROSS * MAX_ACROSS; 47 } 48 else { 49 int i; 50 for (i=0; (i*i) < pieceCount; ++i); 51 *n_rows = *n_cols = i; 52 return pieceCount; 53 } 54 } 55 56 #define TO16(a) ((guint16)((a<<8)|(a))) 57 #define RGB_2_GDK(R,G,B) { 0, TO16(R), TO16(G), TO16(B) } 41 getGridSize( int pieceCount, 42 int * n_rows, 43 int * n_cols ) 44 { 45 const int MAX_ACROSS = 16; 46 47 if( pieceCount >= ( MAX_ACROSS * MAX_ACROSS ) ) 48 { 49 *n_rows = *n_cols = MAX_ACROSS; 50 return MAX_ACROSS * MAX_ACROSS; 51 } 52 else 53 { 54 int i; 55 for( i = 0; ( i * i ) < pieceCount; ++i ) ; 56 *n_rows = *n_cols = i; 57 return pieceCount; 58 } 59 } 60 61 #define TO16( a ) ( (guint16)( ( a << 8 ) | ( a ) ) ) 62 #define RGB_2_GDK( R, G, B ) { 0, TO16( R ), TO16( G ), TO16( B ) } 58 63 59 64 enum { DRAW_AVAIL, DRAW_PROG }; 60 65 61 66 static void 62 release_gobject_array (gpointer data) 63 { 64 int i; 65 GObject **objects = (GObject**) data; 66 for (i=0; objects[i]!=NULL; ++i) 67 g_object_unref (G_OBJECT(objects[i])); 68 g_free (objects); 67 release_gobject_array( gpointer data ) 68 { 69 int i; 70 GObject **objects = (GObject**) data; 71 72 for( i = 0; objects[i] != NULL; ++i ) 73 g_object_unref ( G_OBJECT( objects[i] ) ); 74 g_free ( objects ); 69 75 } 70 76 71 77 static gboolean 72 refresh_pieces( GtkWidget * da, GdkEventExpose * event UNUSED, gpointer gtor ) 73 { 74 tr_torrent * tor = tr_torrent_handle( TR_TORRENT(gtor) ); 75 const tr_info * info = tr_torrent_info( TR_TORRENT(gtor) ); 76 int mode = GPOINTER_TO_INT (g_object_get_data (G_OBJECT(da), "draw-mode")); 77 78 GdkColormap * colormap = gtk_widget_get_colormap (da); 79 const int widget_w = da->allocation.width; 80 const int widget_h = da->allocation.height; 81 int n_rows, n_cols; 82 const int n_cells = getGridSize (info->pieceCount, &n_rows, &n_cols); 83 const GdkRectangle grid_bounds = { 0, 0, widget_w, widget_h }; 84 const double piece_w = grid_bounds.width / (double)n_cols; 85 const double piece_h = grid_bounds.height / (double)n_rows; 86 const int piece_w_int = (int) (piece_w + 1); /* pad for roundoff */ 87 const int piece_h_int = (int) (piece_h + 1); /* pad for roundoff */ 88 const gboolean rtl = gtk_widget_get_direction( da ) == GTK_TEXT_DIR_RTL; 89 90 guint8 * prev_color = NULL; 91 gboolean first_time = FALSE; 92 93 int i, x, y; 94 int8_t * pieces = NULL; 95 float * completeness = NULL; 96 97 /** 98 *** Get the Graphics Contexts... 99 **/ 100 101 enum { ALL, LOTS, SOME, FEW, NONE, 102 BLACK, GRAY, BLINK, 103 N_COLORS }; 104 GdkGC **gcs = (GdkGC**) g_object_get_data (G_OBJECT(da), "graphics-contexts"); 105 if (gcs == NULL) 106 { 107 const GdkColor colors [N_COLORS] = { 108 RGB_2_GDK ( 114, 159, 207 ), /* all */ 109 RGB_2_GDK ( 52, 101, 164 ), /* lots */ 110 RGB_2_GDK ( 32, 74, 135 ), /* some */ 111 RGB_2_GDK ( 85, 87, 83 ), /* few */ 112 RGB_2_GDK ( 238, 238, 236 ), /* none - tango aluminum highlight */ 113 RGB_2_GDK ( 46, 52, 54 ), /* black - tango slate shadow */ 114 RGB_2_GDK ( 186, 189, 182 ), /* gray - tango aluminum shadow */ 115 RGB_2_GDK ( 252, 233, 79 ), /* blink - tango butter highlight */ 116 }; 117 118 gcs = g_new (GdkGC*, N_COLORS+1); 119 120 for (i=0; i<N_COLORS; ++i) { 121 gcs[i] = gdk_gc_new (da->window); 122 gdk_gc_set_colormap (gcs[i], colormap); 123 gdk_gc_set_rgb_fg_color (gcs[i], &colors[i]); 124 gdk_gc_set_rgb_bg_color (gcs[i], &colors[i]); 125 }; 126 127 gcs[N_COLORS] = NULL; /* a sentinel in the release function */ 128 g_object_set_data_full (G_OBJECT(da), "graphics-contexts", 129 gcs, release_gobject_array); 130 } 131 132 /** 133 *** Get the cells' previous colors... 134 *** (this is used for blinking when the color changes) 135 **/ 136 137 prev_color = (guint8*) g_object_get_data (G_OBJECT(da), "prev-color"); 138 if (prev_color == NULL) 139 { 140 first_time = TRUE; 141 prev_color = g_new0 (guint8, n_cells); 142 g_object_set_data_full (G_OBJECT(da), "prev-color", prev_color, g_free); 143 } 144 145 /** 146 *** Get the piece data values... 147 **/ 148 149 switch (mode) { 150 case DRAW_AVAIL: 151 pieces = g_new (int8_t, n_cells); 152 tr_torrentAvailability ( tor, pieces, n_cells ); 153 break; 154 case DRAW_PROG: 155 completeness = g_new (float, n_cells); 156 tr_torrentAmountFinished ( tor, completeness, n_cells ); 157 break; 158 default: g_error("no mode defined!"); 159 } 160 161 /** 162 *** Draw... 163 **/ 164 165 i = 0; 166 for (y=0; y<n_rows; ++y) { 167 for (x=0; x<n_cols; ++x) { 168 int draw_x = grid_bounds.x + (int)(x * piece_w); 169 int draw_y = grid_bounds.y + (int)(y * piece_h); 170 int color = BLACK; 171 int border = BLACK; 172 173 if( rtl ) 174 draw_x = grid_bounds.x + grid_bounds.width - (int)((x+1) * piece_w); 175 else 176 draw_x = grid_bounds.x + (int)(x * piece_w); 177 draw_y = grid_bounds.y + (int)(y * piece_h); 178 179 if (i < n_cells) 180 { 181 border = GRAY; 182 183 if (mode == DRAW_AVAIL) { 184 const int8_t val = pieces[i]; 185 if (val < 0) color = ALL; 186 else if (val == 0) color = NONE; 187 else if (val <= 4) color = FEW; 188 else if (val <= 8) color = SOME; 189 else color = LOTS; 190 } else { /* completeness */ 191 const float val = completeness[i]; 192 if (val >= 1.00) color = ALL; 193 else if (val >= 0.66) color = LOTS; 194 else if (val >= 0.33) color = SOME; 195 else if (val >= 0.01) color = FEW; 196 else color = NONE; 78 refresh_pieces( GtkWidget * da, 79 GdkEventExpose * event UNUSED, 80 gpointer gtor ) 81 { 82 tr_torrent * tor = tr_torrent_handle( TR_TORRENT( gtor ) ); 83 const tr_info * info = tr_torrent_info( TR_TORRENT( gtor ) ); 84 int mode = 85 GPOINTER_TO_INT ( g_object_get_data ( G_OBJECT( da ), "draw-mode" ) ); 86 87 GdkColormap * colormap = gtk_widget_get_colormap ( da ); 88 const int widget_w = da->allocation.width; 89 const int widget_h = da->allocation.height; 90 int n_rows, n_cols; 91 const int n_cells = getGridSize ( info->pieceCount, &n_rows, 92 &n_cols ); 93 const GdkRectangle grid_bounds = { 0, 0, widget_w, widget_h }; 94 const double piece_w = grid_bounds.width / (double)n_cols; 95 const double piece_h = grid_bounds.height / (double)n_rows; 96 const int piece_w_int = (int) ( piece_w + 1 ); /* pad for roundoff 97 */ 98 const int piece_h_int = (int) ( piece_h + 1 ); /* pad for roundoff 99 */ 100 const gboolean rtl = gtk_widget_get_direction( da ) == 101 GTK_TEXT_DIR_RTL; 102 103 guint8 * prev_color = NULL; 104 gboolean first_time = FALSE; 105 106 int i, x, y; 107 int8_t * pieces = NULL; 108 float * completeness = NULL; 109 110 /** 111 *** Get the Graphics Contexts... 112 **/ 113 114 enum { ALL, LOTS, SOME, FEW, NONE, 115 BLACK, GRAY, BLINK, 116 N_COLORS }; 117 GdkGC ** gcs = (GdkGC**) g_object_get_data ( G_OBJECT( 118 da ), 119 "graphics-contexts" ); 120 if( gcs == NULL ) 121 { 122 const GdkColor colors[N_COLORS] = { 123 RGB_2_GDK ( 114, 159, 207 ), /* all */ 124 RGB_2_GDK ( 52, 101, 164 ), /* lots */ 125 RGB_2_GDK ( 32, 74, 135 ), /* some */ 126 RGB_2_GDK ( 85, 87, 83 ), /* few */ 127 RGB_2_GDK ( 238, 238, 236 ), /* none - tango aluminum highlight */ 128 RGB_2_GDK ( 46, 52, 54 ), /* black - tango slate shadow */ 129 RGB_2_GDK ( 186, 189, 182 ), /* gray - tango aluminum shadow */ 130 RGB_2_GDK ( 252, 233, 79 ), /* blink - tango butter highlight */ 131 }; 132 133 gcs = g_new ( GdkGC *, N_COLORS + 1 ); 134 135 for( i = 0; i < N_COLORS; ++i ) 136 { 137 gcs[i] = gdk_gc_new ( da->window ); 138 gdk_gc_set_colormap ( gcs[i], colormap ); 139 gdk_gc_set_rgb_fg_color ( gcs[i], &colors[i] ); 140 gdk_gc_set_rgb_bg_color ( gcs[i], &colors[i] ); 197 141 } 198 142 199 /* draw a "blink" for one interval when a piece changes */ 200 if (first_time) 201 prev_color[i] = color; 202 else if (color != prev_color[i]) { 203 prev_color[i] = color; 204 color = border = BLINK; 143 gcs[N_COLORS] = NULL; /* a sentinel in the release function */ 144 g_object_set_data_full ( G_OBJECT( da ), "graphics-contexts", 145 gcs, release_gobject_array ); 146 } 147 148 /** 149 *** Get the cells' previous colors... 150 *** (this is used for blinking when the color changes) 151 **/ 152 153 prev_color = (guint8*) g_object_get_data ( G_OBJECT( da ), "prev-color" ); 154 if( prev_color == NULL ) 155 { 156 first_time = TRUE; 157 prev_color = g_new0 ( guint8, n_cells ); 158 g_object_set_data_full ( G_OBJECT( 159 da ), "prev-color", prev_color, g_free ); 160 } 161 162 /** 163 *** Get the piece data values... 164 **/ 165 166 switch( mode ) 167 { 168 case DRAW_AVAIL: 169 pieces = g_new ( int8_t, n_cells ); 170 tr_torrentAvailability ( tor, pieces, n_cells ); 171 break; 172 173 case DRAW_PROG: 174 completeness = g_new ( float, n_cells ); 175 tr_torrentAmountFinished ( tor, completeness, n_cells ); 176 break; 177 178 default: 179 g_error( "no mode defined!" ); 180 } 181 182 /** 183 *** Draw... 184 **/ 185 186 i = 0; 187 for( y = 0; y < n_rows; ++y ) 188 { 189 for( x = 0; x < n_cols; ++x ) 190 { 191 int draw_x = grid_bounds.x + (int)( x * piece_w ); 192 int draw_y = grid_bounds.y + (int)( y * piece_h ); 193 int color = BLACK; 194 int border = BLACK; 195 196 if( rtl ) 197 draw_x = grid_bounds.x + grid_bounds.width - 198 (int)( ( x + 1 ) * piece_w ); 199 else 200 draw_x = grid_bounds.x + (int)( x * piece_w ); 201 draw_y = grid_bounds.y + (int)( y * piece_h ); 202 203 if( i < n_cells ) 204 { 205 border = GRAY; 206 207 if( mode == DRAW_AVAIL ) 208 { 209 const int8_t val = pieces[i]; 210 if( val < 0 ) color = ALL; 211 else if( val == 0 ) color = NONE; 212 else if( val <= 4 ) color = FEW; 213 else if( val <= 8 ) color = SOME; 214 else color = LOTS; 215 } 216 else /* completeness */ 217 { 218 const float val = completeness[i]; 219 if( val >= 1.00 ) color = ALL; 220 else if( val >= 0.66 ) color = LOTS; 221 else if( val >= 0.33 ) color = SOME; 222 else if( val >= 0.01 ) color = FEW; 223 else color = NONE; 224 } 225 226 /* draw a "blink" for one interval when a piece changes */ 227 if( first_time ) 228 prev_color[i] = color; 229 else if( color != prev_color[i] ) 230 { 231 prev_color[i] = color; 232 color = border = BLINK; 233 } 234 } 235 236 gdk_draw_rectangle ( da->window, gcs[color], TRUE, 237 draw_x, draw_y, 238 piece_w_int, piece_h_int ); 239 240 if( i < n_cells ) 241 gdk_draw_rectangle ( da->window, gcs[border], FALSE, 242 draw_x, draw_y, 243 piece_w_int, piece_h_int ); 244 245 ++i; 205 246 } 206 } 207 208 gdk_draw_rectangle (da->window, gcs[color], TRUE, 209 draw_x, draw_y, 210 piece_w_int, piece_h_int); 211 212 if (i < n_cells) 213 gdk_draw_rectangle (da->window, gcs[border], FALSE, 214 draw_x, draw_y, 215 piece_w_int, piece_h_int); 216 217 ++i; 218 } 219 } 220 221 gdk_draw_rectangle (da->window, gcs[GRAY], FALSE, 222 grid_bounds.x, grid_bounds.y, 223 grid_bounds.width-1, grid_bounds.height-1); 224 225 g_free (pieces); 226 g_free (completeness); 227 return FALSE; 228 } 247 } 248 249 gdk_draw_rectangle ( da->window, gcs[GRAY], FALSE, 250 grid_bounds.x, grid_bounds.y, 251 grid_bounds.width - 1, grid_bounds.height - 1 ); 252 253 g_free ( pieces ); 254 g_free ( completeness ); 255 return FALSE; 256 } 257 229 258 #endif 230 259 … … 235 264 enum 236 265 { 237 WEBSEED_COL_URL,238 WEBSEED_COL_DOWNLOAD_RATE,239 N_WEBSEED_COLS266 WEBSEED_COL_URL, 267 WEBSEED_COL_DOWNLOAD_RATE, 268 N_WEBSEED_COLS 240 269 }; 241 270 … … 250 279 webseed_model_new( const tr_torrent * tor ) 251 280 { 252 int i;281 int i; 253 282 const tr_info * inf = tr_torrentInfo( tor ); 254 float * speeds = tr_torrentWebSpeeds( tor ); 255 GtkListStore * store = gtk_list_store_new( N_WEBSEED_COLS, 256 G_TYPE_STRING, 257 G_TYPE_FLOAT ); 258 for( i=0; i<inf->webseedCount; ++i ) 283 float * speeds = tr_torrentWebSpeeds( tor ); 284 GtkListStore * store = gtk_list_store_new( N_WEBSEED_COLS, 285 G_TYPE_STRING, 286 G_TYPE_FLOAT ); 287 288 for( i = 0; i < inf->webseedCount; ++i ) 259 289 { 260 290 GtkTreeIter iter; 261 291 gtk_list_store_append( store, &iter ); 262 292 gtk_list_store_set( store, &iter, WEBSEED_COL_URL, inf->webseeds[i], 263 264 293 WEBSEED_COL_DOWNLOAD_RATE, speeds[i], 294 -1 ); 265 295 } 266 296 … … 271 301 enum 272 302 { 273 PEER_COL_ADDRESS,274 PEER_COL_DOWNLOAD_RATE,275 PEER_COL_UPLOAD_RATE,276 PEER_COL_CLIENT,277 PEER_COL_PROGRESS,278 PEER_COL_IS_ENCRYPTED,279 PEER_COL_STATUS,280 N_PEER_COLS303 PEER_COL_ADDRESS, 304 PEER_COL_DOWNLOAD_RATE, 305 PEER_COL_UPLOAD_RATE, 306 PEER_COL_CLIENT, 307 PEER_COL_PROGRESS, 308 PEER_COL_IS_ENCRYPTED, 309 PEER_COL_STATUS, 310 N_PEER_COLS 281 311 }; 282 312 283 313 static const char* peer_column_names[N_PEER_COLS] = 284 314 { 285 N_("Address"),286 /* 'download speed' column header. terse to keep the column narrow. */287 N_("Down"),288 /* 'upload speed' column header. terse to keep the column narrow. */289 N_("Up"),290 N_("Client"),291 /* 'percent done' column header. terse to keep the column narrow. */292 N_("%"),293 " ",294 N_("Status")315 N_( "Address" ), 316 /* 'download speed' column header. terse to keep the column narrow. */ 317 N_( "Down" ), 318 /* 'upload speed' column header. terse to keep the column narrow. */ 319 N_( "Up" ), 320 N_( "Client" ), 321 /* 'percent done' column header. terse to keep the column narrow. */ 322 N_( "%" ), 323 " ", 324 N_( "Status" ) 295 325 }; 296 326 297 static int compare_peers (const void * a, const void * b) 298 { 299 const tr_peer_stat * pa = a; 300 const tr_peer_stat * pb = b; 301 return strcmp (pa->addr, pb->addr); 302 } 303 static int compare_addr_to_peer (const void * a, const void * b) 304 { 305 const char * addr = (const char *) a; 306 const tr_peer_stat * peer = b; 307 return strcmp (addr, peer->addr); 308 } 309 310 static void 311 peer_row_set (GtkListStore * store, 312 GtkTreeIter * iter, 313 const tr_peer_stat * peer) 314 { 315 const char * client = peer->client; 316 317 if (!client || !strcmp(client,"Unknown Client")) 318 client = " "; 319 320 gtk_list_store_set( store, iter, 321 PEER_COL_ADDRESS, peer->addr, 322 PEER_COL_CLIENT, client, 323 PEER_COL_IS_ENCRYPTED, peer->isEncrypted, 324 PEER_COL_PROGRESS, (int)(100.0*peer->progress), 325 PEER_COL_DOWNLOAD_RATE, peer->rateToClient, 326 PEER_COL_UPLOAD_RATE, peer->rateToPeer, 327 PEER_COL_STATUS, peer->flagStr, 328 -1); 329 } 330 331 static void 332 append_peers_to_model (GtkListStore * store, 333 const tr_peer_stat * peers, 334 int n_peers) 335 { 336 int i; 337 for (i=0; i<n_peers; ++i) { 338 GtkTreeIter iter; 339 gtk_list_store_append( store, &iter ); 340 peer_row_set (store, &iter, &peers[i]); 341 } 327 static int 328 compare_peers( const void * a, 329 const void * b ) 330 { 331 const tr_peer_stat * pa = a; 332 const tr_peer_stat * pb = b; 333 334 return strcmp ( pa->addr, pb->addr ); 335 } 336 337 static int 338 compare_addr_to_peer( const void * a, 339 const void * b ) 340 { 341 const char * addr = (const char *) a; 342 const tr_peer_stat * peer = b; 343 344 return strcmp ( addr, peer->addr ); 345 } 346 347 static void 348 peer_row_set( GtkListStore * store, 349 GtkTreeIter * iter, 350 const tr_peer_stat * peer ) 351 { 352 const char * client = peer->client; 353 354 if( !client || !strcmp( client, "Unknown Client" ) ) 355 client = " "; 356 357 gtk_list_store_set( store, iter, 358 PEER_COL_ADDRESS, peer->addr, 359 PEER_COL_CLIENT, client, 360 PEER_COL_IS_ENCRYPTED, peer->isEncrypted, 361 PEER_COL_PROGRESS, (int)( 100.0 * peer->progress ), 362 PEER_COL_DOWNLOAD_RATE, peer->rateToClient, 363 PEER_COL_UPLOAD_RATE, peer->rateToPeer, 364 PEER_COL_STATUS, peer->flagStr, 365 -1 ); 366 } 367 368 static void 369 append_peers_to_model( GtkListStore * store, 370 const tr_peer_stat * peers, 371 int n_peers ) 372 { 373 int i; 374 375 for( i = 0; i < n_peers; ++i ) 376 { 377 GtkTreeIter iter; 378 gtk_list_store_append( store, &iter ); 379 peer_row_set ( store, &iter, &peers[i] ); 380 } 342 381 } 343 382 344 383 static GtkTreeModel* 345 peer_model_new (tr_torrent * tor) 346 { 347 GtkListStore * m = gtk_list_store_new (N_PEER_COLS, 348 G_TYPE_STRING, /* addr */ 349 G_TYPE_FLOAT, /* downloadFromRate */ 350 G_TYPE_FLOAT, /* uploadToRate */ 351 G_TYPE_STRING, /* client */ 352 G_TYPE_INT, /* progress [0..100] */ 353 G_TYPE_BOOLEAN, /* isEncrypted */ 354 G_TYPE_STRING ); /* flagString */ 355 356 int n_peers = 0; 357 tr_peer_stat * peers = tr_torrentPeers (tor, &n_peers); 358 qsort (peers, n_peers, sizeof(tr_peer_stat), compare_peers); 359 append_peers_to_model (m, peers, n_peers); 360 tr_torrentPeersFree( peers, 0 ); 361 return GTK_TREE_MODEL (m); 362 } 363 364 static void 365 render_encrypted (GtkTreeViewColumn * column UNUSED, 366 GtkCellRenderer * renderer, 367 GtkTreeModel * tree_model, 368 GtkTreeIter * iter, 369 gpointer data UNUSED) 370 { 371 gboolean is_encrypted = FALSE; 372 gtk_tree_model_get (tree_model, iter, PEER_COL_IS_ENCRYPTED, &is_encrypted, 373 -1); 374 g_object_set (renderer, "xalign", (gfloat)0.0, 375 "yalign", (gfloat)0.5, 376 "stock-id", (is_encrypted ? "transmission-lock" : NULL), 377 NULL); 378 } 379 380 static void 381 render_ul_rate (GtkTreeViewColumn * column UNUSED, 382 GtkCellRenderer * renderer, 383 GtkTreeModel * tree_model, 384 GtkTreeIter * iter, 385 gpointer data UNUSED) 386 { 387 float rate = 0.0; 388 gtk_tree_model_get (tree_model, iter, PEER_COL_UPLOAD_RATE, &rate, -1); 389 if( rate < 0.01 ) 390 g_object_set (renderer, "text", "", NULL); 391 else { 392 char speedStr[64]; 393 tr_strlspeed( speedStr, rate, sizeof(speedStr) ); 394 g_object_set( renderer, "text", speedStr, NULL ); 395 } 396 } 397 398 static void 399 render_dl_rate (GtkTreeViewColumn * column UNUSED, 400 GtkCellRenderer * renderer, 401 GtkTreeModel * tree_model, 402 GtkTreeIter * iter, 403 gpointer data UNUSED) 404 { 405 float rate = 0.0; 406 gtk_tree_model_get (tree_model, iter, PEER_COL_DOWNLOAD_RATE, &rate, -1); 407 if( rate < 0.01 ) 408 g_object_set (renderer, "text", "", NULL); 409 else { 410 char speedStr[64]; 411 tr_strlspeed( speedStr, rate, sizeof(speedStr) ); 412 g_object_set( renderer, "text", speedStr, NULL ); 413 } 414 } 415 416 static void 417 render_client (GtkTreeViewColumn * column UNUSED, 418 GtkCellRenderer * renderer, 419 GtkTreeModel * tree_model, 420 GtkTreeIter * iter, 421 gpointer data UNUSED) 422 { 423 char * client = NULL; 424 gtk_tree_model_get (tree_model, iter, PEER_COL_CLIENT, &client, 425 -1); 426 g_object_set (renderer, "text", (client ? client : ""), NULL); 427 g_free (client); 384 peer_model_new( tr_torrent * tor ) 385 { 386 GtkListStore * m = gtk_list_store_new ( N_PEER_COLS, 387 G_TYPE_STRING, /* addr */ 388 G_TYPE_FLOAT, /* downloadFromRate */ 389 G_TYPE_FLOAT, /* uploadToRate */ 390 G_TYPE_STRING, /* client */ 391 G_TYPE_INT, /* progress [0..100] 392 */ 393 G_TYPE_BOOLEAN, /* isEncrypted */ 394 G_TYPE_STRING ); /* flagString */ 395 396 int n_peers = 0; 397 tr_peer_stat * peers = tr_torrentPeers ( tor, &n_peers ); 398 399 qsort ( peers, n_peers, sizeof( tr_peer_stat ), compare_peers ); 400 append_peers_to_model ( m, peers, n_peers ); 401 tr_torrentPeersFree( peers, 0 ); 402 return GTK_TREE_MODEL ( m ); 403 } 404 405 static void 406 render_encrypted( GtkTreeViewColumn * column UNUSED, 407 GtkCellRenderer * renderer, 408 GtkTreeModel * tree_model, 409 GtkTreeIter * iter, 410 gpointer data UNUSED ) 411 { 412 gboolean is_encrypted = FALSE; 413 414 gtk_tree_model_get ( tree_model, iter, PEER_COL_IS_ENCRYPTED, 415 &is_encrypted, 416 -1 ); 417 g_object_set ( renderer, "xalign", (gfloat)0.0, 418 "yalign", (gfloat)0.5, 419 "stock-id", ( is_encrypted ? "transmission-lock" : NULL ), 420 NULL ); 421 } 422 423 static void 424 render_ul_rate( GtkTreeViewColumn * column UNUSED, 425 GtkCellRenderer * renderer, 426 GtkTreeModel * tree_model, 427 GtkTreeIter * iter, 428 gpointer data UNUSED ) 429 { 430 float rate = 0.0; 431 432 gtk_tree_model_get ( tree_model, iter, PEER_COL_UPLOAD_RATE, &rate, -1 ); 433 if( rate < 0.01 ) 434 g_object_set ( renderer, "text", "", NULL ); 435 else 436 { 437 char speedStr[64]; 438 tr_strlspeed( speedStr, rate, sizeof( speedStr ) ); 439 g_object_set( renderer, "text", speedStr, NULL ); 440 } 441 } 442 443 static void 444 render_dl_rate( GtkTreeViewColumn * column UNUSED, 445 GtkCellRenderer * renderer, 446 GtkTreeModel * tree_model, 447 GtkTreeIter * iter, 448 gpointer data UNUSED ) 449 { 450 float rate = 0.0; 451 452 gtk_tree_model_get ( tree_model, iter, PEER_COL_DOWNLOAD_RATE, &rate, 453 -1 ); 454 if( rate < 0.01 ) 455 g_object_set ( renderer, "text", "", NULL ); 456 else 457 { 458 char speedStr[64]; 459 tr_strlspeed( speedStr, rate, sizeof( speedStr ) ); 460 g_object_set( renderer, "text", speedStr, NULL ); 461 } 462 } 463 464 static void 465 render_client( GtkTreeViewColumn * column UNUSED, 466 GtkCellRenderer * renderer, 467 GtkTreeModel * tree_model, 468 GtkTreeIter * iter, 469 gpointer data UNUSED ) 470 { 471 char * client = NULL; 472 473 gtk_tree_model_get ( tree_model, iter, PEER_COL_CLIENT, &client, 474 -1 ); 475 g_object_set ( renderer, "text", ( client ? client : "" ), NULL ); 476 g_free ( client ); 428 477 } 429 478 430 479 typedef struct 431 480 { 432 TrTorrent *gtor;433 GtkTreeModel *model; /* same object as store, but recast */434 GtkListStore *store; /* same object as model, but recast */435 GtkListStore *webseeds;436 GtkWidget *completeness;437 GtkWidget *seeders_lb;438 GtkWidget *leechers_lb;439 GtkWidget *completed_lb;481 TrTorrent * gtor; 482 GtkTreeModel * model; /* same object as store, but recast */ 483 GtkListStore * store; /* same object as model, but recast */ 484 GtkListStore * webseeds; 485 GtkWidget * completeness; 486 GtkWidget * seeders_lb; 487 GtkWidget * leechers_lb; 488 GtkWidget * completed_lb; 440 489 } 441 490 PeerData; 442 491 443 492 static void 444 fmtpeercount (GtkWidget * l, int count) 445 { 446 if( 0 > count ) { 447 gtk_label_set_text( GTK_LABEL(l), "?" ); 448 } else { 449 char str[16]; 450 g_snprintf( str, sizeof str, "%'d", count ); 451 gtk_label_set_text( GTK_LABEL(l), str ); 452 } 453 } 454 455 static void 456 refresh_peers (GtkWidget * top) 457 { 458 int i; 459 int n_peers; 460 GtkTreeIter iter; 461 PeerData * p = (PeerData*) g_object_get_data (G_OBJECT(top), "peer-data"); 462 tr_torrent * tor = tr_torrent_handle ( p->gtor ); 463 GtkTreeModel * model = p->model; 464 GtkListStore * store = p->store; 465 tr_peer_stat * peers; 466 const tr_stat * stat = tr_torrent_stat( p->gtor ); 467 const tr_info * inf = tr_torrent_info( p->gtor ); 468 469 if( inf->webseedCount ) 470 { 471 float * speeds = tr_torrentWebSpeeds( tor ); 472 for( i=0; i<inf->webseedCount; ++i ) 473 { 474 GtkTreeIter iter; 475 gtk_tree_model_iter_nth_child( GTK_TREE_MODEL( p->webseeds ), &iter, NULL, i ); 476 gtk_list_store_set( p->webseeds, &iter, WEBSEED_COL_DOWNLOAD_RATE, speeds[i], 477 -1 ); 478 } 479 tr_free( speeds ); 480 } 481 482 /** 483 *** merge the peer diffs into the tree model. 484 *** 485 *** this is more complicated than creating a new model, 486 *** but is also (a) more efficient and (b) doesn't undo 487 *** the view's visible area and sorting on every refresh. 488 **/ 489 490 n_peers = 0; 491 peers = tr_torrentPeers (tor, &n_peers); 492 qsort (peers, n_peers, sizeof(tr_peer_stat), compare_peers); 493 494 if (gtk_tree_model_get_iter_first (model, &iter)) do 495 { 496 char * addr = NULL; 497 tr_peer_stat * peer = NULL; 498 gtk_tree_model_get (model, &iter, PEER_COL_ADDRESS, &addr, -1); 499 peer = bsearch (addr, peers, n_peers, sizeof(tr_peer_stat), 500 compare_addr_to_peer); 501 g_free (addr); 502 503 if (peer) /* update a pre-existing row */ 504 { 505 const int pos = peer - peers; 506 const int n_rhs = n_peers - (pos+1); 507 g_assert (n_rhs >= 0); 508 509 peer_row_set (store, &iter, peer); 510 511 /* remove it from the tr_peer_stat list */ 512 g_memmove (peer, peer+1, sizeof(tr_peer_stat)*n_rhs); 513 --n_peers; 514 } 515 else if (!gtk_list_store_remove (store, &iter)) 516 break; /* we removed the model's last item */ 517 } 518 while (gtk_tree_model_iter_next (model, &iter)); 519 520 append_peers_to_model (store, peers, n_peers); /* all these are new */ 493 fmtpeercount( GtkWidget * l, 494 int count ) 495 { 496 if( 0 > count ) 497 { 498 gtk_label_set_text( GTK_LABEL( l ), "?" ); 499 } 500 else 501 { 502 char str[16]; 503 g_snprintf( str, sizeof str, "%'d", count ); 504 gtk_label_set_text( GTK_LABEL( l ), str ); 505 } 506 } 507 508 static void 509 refresh_peers( GtkWidget * top ) 510 { 511 int i; 512 int n_peers; 513 GtkTreeIter iter; 514 PeerData * p = (PeerData*) g_object_get_data ( G_OBJECT( 515 top ), 516 "peer-data" ); 517 tr_torrent * tor = tr_torrent_handle ( p->gtor ); 518 GtkTreeModel * model = p->model; 519 GtkListStore * store = p->store; 520 tr_peer_stat * peers; 521 const tr_stat * stat = tr_torrent_stat( p->gtor ); 522 const tr_info * inf = tr_torrent_info( p->gtor ); 523 524 if( inf->webseedCount ) 525 { 526 float * speeds = tr_torrentWebSpeeds( tor ); 527 for( i = 0; i < inf->webseedCount; ++i ) 528 { 529 GtkTreeIter iter; 530 gtk_tree_model_iter_nth_child( GTK_TREE_MODEL( 531 p->webseeds ), &iter, NULL, 532 i ); 533 gtk_list_store_set( p->webseeds, &iter, 534 WEBSEED_COL_DOWNLOAD_RATE, speeds[i], 535 -1 ); 536 } 537 tr_free( speeds ); 538 } 539 540 /** 541 *** merge the peer diffs into the tree model. 542 *** 543 *** this is more complicated than creating a new model, 544 *** but is also (a) more efficient and (b) doesn't undo 545 *** the view's visible area and sorting on every refresh. 546 **/ 547 548 n_peers = 0; 549 peers = tr_torrentPeers ( tor, &n_peers ); 550 qsort ( peers, n_peers, sizeof( tr_peer_stat ), compare_peers ); 551 552 if( gtk_tree_model_get_iter_first ( model, &iter ) ) do 553 { 554 char * addr = NULL; 555 tr_peer_stat * peer = NULL; 556 gtk_tree_model_get ( model, &iter, PEER_COL_ADDRESS, &addr, -1 ); 557 peer = bsearch ( addr, peers, n_peers, sizeof( tr_peer_stat ), 558 compare_addr_to_peer ); 559 g_free ( addr ); 560 561 if( peer ) /* update a pre-existing row */ 562 { 563 const int pos = peer - peers; 564 const int n_rhs = n_peers - ( pos + 1 ); 565 g_assert ( n_rhs >= 0 ); 566 567 peer_row_set ( store, &iter, peer ); 568 569 /* remove it from the tr_peer_stat list */ 570 g_memmove ( peer, peer + 1, sizeof( tr_peer_stat ) * n_rhs ); 571 --n_peers; 572 } 573 else if( !gtk_list_store_remove ( store, &iter ) ) 574 break; /* we removed the model's last item */ 575 } 576 while( gtk_tree_model_iter_next ( model, &iter ) ); 577 578 append_peers_to_model ( store, peers, n_peers ); /* all these are new */ 521 579 522