fixes bug where priorities where lost when force-rechecking.
[libtorrent.git] / examples / client_test.cpp
blobf501f3caabe8c736f3e136a96871ed08c4131449
1 /*
3 Copyright (c) 2003, Arvid Norberg
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
33 #include <iostream>
34 #include <fstream>
35 #include <iterator>
36 #include <exception>
38 #include "libtorrent/config.hpp"
40 #ifdef _MSC_VER
41 #pragma warning(push, 1)
42 #endif
44 #include <boost/filesystem/operations.hpp>
45 #include <boost/filesystem/convenience.hpp>
46 #include <boost/filesystem/fstream.hpp>
47 #include <boost/filesystem/exception.hpp>
48 #include <boost/bind.hpp>
49 #include <boost/program_options.hpp>
50 #include <boost/regex.hpp>
52 #ifdef _MSC_VER
53 #pragma warning(pop)
54 #endif
56 #include "libtorrent/extensions/metadata_transfer.hpp"
57 #include "libtorrent/extensions/ut_metadata.hpp"
58 #include "libtorrent/extensions/ut_pex.hpp"
59 #include "libtorrent/extensions/smart_ban.hpp"
61 #include "libtorrent/entry.hpp"
62 #include "libtorrent/bencode.hpp"
63 #include "libtorrent/session.hpp"
64 #include "libtorrent/identify_client.hpp"
65 #include "libtorrent/alert_types.hpp"
66 #include "libtorrent/ip_filter.hpp"
67 #include "libtorrent/magnet_uri.hpp"
68 #include "libtorrent/bitfield.hpp"
69 #include "libtorrent/file.hpp"
71 using boost::bind;
73 #ifdef _WIN32
75 #if defined(_MSC_VER)
76 # define for if (false) {} else for
77 #endif
79 #include <windows.h>
80 #include <conio.h>
82 bool sleep_and_input(char* c)
84 Sleep(500);
85 if (_kbhit())
87 *c = _getch();
88 return true;
90 return false;
93 void clear_home()
95 CONSOLE_SCREEN_BUFFER_INFO si;
96 HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
97 GetConsoleScreenBufferInfo(h, &si);
98 COORD c = {0, 0};
99 DWORD n;
100 FillConsoleOutputCharacter(h, ' ', si.dwSize.X * si.dwSize.Y, c, &n);
101 SetConsoleCursorPosition(h, c);
104 #else
106 #include <stdlib.h>
107 #include <stdio.h>
109 #include <termios.h>
110 #include <string.h>
111 #include <sys/ioctl.h>
113 #define ANSI_TERMINAL_COLORS
115 struct set_keypress
117 set_keypress()
119 termios new_settings;
120 tcgetattr(0,&stored_settings);
121 new_settings = stored_settings;
122 // Disable canonical mode, and set buffer size to 1 byte
123 new_settings.c_lflag &= (~ICANON);
124 new_settings.c_cc[VTIME] = 0;
125 new_settings.c_cc[VMIN] = 1;
126 tcsetattr(0,TCSANOW,&new_settings);
128 ~set_keypress() { tcsetattr(0,TCSANOW,&stored_settings); }
129 termios stored_settings;
132 bool sleep_and_input(char* c)
134 // sets the terminal to single-character mode
135 // and resets when destructed
136 set_keypress s;
138 fd_set set;
139 FD_ZERO(&set);
140 FD_SET(0, &set);
141 timeval tv = {0, 500000};
142 if (select(1, &set, 0, 0, &tv) > 0)
144 *c = getc(stdin);
145 return true;
147 return false;
150 void clear_home()
152 std::cout << "\033[2J\033[0;0H";
155 #endif
157 bool print_peers = false;
158 bool print_log = false;
159 bool print_downloads = false;
160 bool print_piece_bar = false;
161 bool print_file_progress = false;
162 bool sequential_download = false;
164 bool print_ip = true;
165 bool print_as = false;
166 bool print_timers = false;
167 bool print_block = false;
168 bool print_peer_rate = false;
169 bool print_fails = false;
170 bool print_send_bufs = true;
171 std::ofstream g_log_file;
173 int active_torrent = 0;
175 char const* esc(char const* code)
177 #ifdef ANSI_TERMINAL_COLORS
178 // this is a silly optimization
179 // to avoid copying of strings
180 enum { num_strings = 200 };
181 static char buf[num_strings][20];
182 static int round_robin = 0;
183 char* ret = buf[round_robin];
184 ++round_robin;
185 if (round_robin >= num_strings) round_robin = 0;
186 ret[0] = '\033';
187 ret[1] = '[';
188 int i = 2;
189 int j = 0;
190 while (code[j]) ret[i++] = code[j++];
191 ret[i++] = 'm';
192 ret[i++] = 0;
193 return ret;
194 #else
195 return "";
196 #endif
199 std::string to_string(int v, int width)
201 std::stringstream s;
202 s.flags(std::ios_base::right);
203 s.width(width);
204 s.fill(' ');
205 s << v;
206 return s.str();
209 std::string& to_string(float v, int width, int precision = 3)
211 // this is a silly optimization
212 // to avoid copying of strings
213 enum { num_strings = 20 };
214 static std::string buf[num_strings];
215 static int round_robin = 0;
216 std::string& ret = buf[round_robin];
217 ++round_robin;
218 if (round_robin >= num_strings) round_robin = 0;
219 ret.resize(20);
220 int size = std::sprintf(&ret[0], "%*.*f", width, precision, v);
221 ret.resize((std::min)(size, width));
222 return ret;
225 std::string const& add_suffix(float val)
227 const char* prefix[] = {"kB", "MB", "GB", "TB"};
228 const int num_prefix = sizeof(prefix) / sizeof(const char*);
229 for (int i = 0; i < num_prefix; ++i)
231 val /= 1000.f;
232 if (fabs(val) < 1000.f)
234 std::string& ret = to_string(val, 4);
235 ret += prefix[i];
236 return ret;
239 std::string& ret = to_string(val, 4);
240 ret += "PB";
241 return ret;
244 std::string const& piece_bar(libtorrent::bitfield const& p, int width)
246 #ifdef ANSI_TERMINAL_COLORS
247 static const char* lookup[] =
249 // black, blue, cyan, white
250 "40", "44", "46", "47"
253 const int table_size = sizeof(lookup) / sizeof(lookup[0]);
254 #else
255 static const char char_lookup[] =
256 { ' ', '.', ':', '-', '+', '*', '#'};
258 const int table_size = sizeof(char_lookup) / sizeof(char_lookup[0]);
259 #endif
261 double piece_per_char = p.size() / double(width);
262 static std::string bar;
263 bar.clear();
264 bar.reserve(width * 6);
265 bar += "[";
267 // the [piece, piece + pieces_per_char) range is the pieces that are represented by each character
268 double piece = 0;
269 for (int i = 0; i < width; ++i, piece += piece_per_char)
271 int num_pieces = 0;
272 int num_have = 0;
273 int end = (std::max)(int(piece + piece_per_char), int(piece) + 1);
274 for (int k = int(piece); k < end; ++k, ++num_pieces)
275 if (p[k]) ++num_have;
276 int color = int(std::ceil(num_have / float(num_pieces) * (table_size - 1)));
277 #ifdef ANSI_TERMINAL_COLORS
278 bar += esc(lookup[color]);
279 bar += " ";
280 #else
281 bar += char_lookup[color];
282 #endif
284 #ifdef ANSI_TERMINAL_COLORS
285 bar += esc("0");
286 #endif
287 bar += "]";
288 return bar;
291 std::string const& progress_bar(float progress, int width, char const* code = "33")
293 static std::string bar;
294 bar.clear();
295 bar.reserve(width + 10);
297 int progress_chars = static_cast<int>(progress * width + .5f);
298 bar = esc(code);
299 std::fill_n(std::back_inserter(bar), progress_chars, '#');
300 bar += esc("0");
301 std::fill_n(std::back_inserter(bar), width - progress_chars, '-');
302 return bar;
305 int peer_index(libtorrent::tcp::endpoint addr, std::vector<libtorrent::peer_info> const& peers)
307 using namespace libtorrent;
308 std::vector<peer_info>::const_iterator i = std::find_if(peers.begin()
309 , peers.end(), bind(&peer_info::ip, _1) == addr);
310 if (i == peers.end()) return -1;
312 return i - peers.begin();
315 void print_peer_info(std::ostream& out, std::vector<libtorrent::peer_info> const& peers)
317 using namespace libtorrent;
318 if (print_ip) out << "IP ";
319 #ifndef TORRENT_DISABLE_GEO_IP
320 if (print_as) out << "AS ";
321 #endif
322 out << "down (total | peak ) up (total | peak ) sent-req recv flags source ";
323 if (print_fails) out << "fail hshf ";
324 if (print_send_bufs) out << "rq sndb quota rcvb ";
325 if (print_timers) out << "inactive wait timeout ";
326 out << "disk rtt ";
327 if (print_block) out << "block-progress ";
328 #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
329 out << "country ";
330 #endif
331 if (print_peer_rate) out << "peer-rate ";
332 out << "client \n";
334 for (std::vector<peer_info>::const_iterator i = peers.begin();
335 i != peers.end(); ++i)
337 if (i->flags & (peer_info::handshake))
338 continue;
340 out.fill(' ');
341 if (print_ip)
343 std::stringstream ip;
344 ip << i->ip.address().to_string() << ":" << i->ip.port();
345 out.width(22);
346 out << ip.str() << " ";
349 #ifndef TORRENT_DISABLE_GEO_IP
350 if (print_as)
352 std::string as_name = i->inet_as_name;
353 if (as_name.size() > 42) as_name.resize(42);
354 out.width(42);
355 out << as_name << " ";
357 #endif
358 out.width(2);
359 out << esc("32") << (i->down_speed > 0 ? add_suffix(i->down_speed) + "/s " : " ")
360 << "(" << (i->total_download > 0 ? add_suffix(i->total_download) : " ") << "|"
361 << (i->download_rate_peak > 0 ? add_suffix(i->download_rate_peak) + "/s" : " ") << ") " << esc("0")
362 << esc("31") << (i->up_speed > 0 ? add_suffix(i->up_speed) + "/s ": " ")
363 << "(" << (i->total_upload > 0 ? add_suffix(i->total_upload) : " ") << "|"
364 << (i->upload_rate_peak > 0 ? add_suffix(i->upload_rate_peak) + "/s" : " ") << ") " << esc("0")
365 << to_string(i->download_queue_length, 3) << " ("
366 << to_string(i->target_dl_queue_length, 3) << ") "
367 << to_string(i->upload_queue_length, 3) << " "
368 << ((i->flags & peer_info::interesting)?'I':'.')
369 << ((i->flags & peer_info::choked)?'C':'.')
370 << ((i->flags & peer_info::remote_interested)?'i':'.')
371 << ((i->flags & peer_info::remote_choked)?'c':'.')
372 << ((i->flags & peer_info::supports_extensions)?'e':'.')
373 << ((i->flags & peer_info::local_connection)?'l':'r')
374 << ((i->flags & peer_info::seed)?'s':'.')
375 << ((i->flags & peer_info::on_parole)?'p':'.')
376 << ((i->flags & peer_info::optimistic_unchoke)?'O':'.')
377 << ((i->read_state == peer_info::bw_torrent)?'t':
378 (i->read_state == peer_info::bw_global)?'r':
379 (i->read_state == peer_info::bw_network)?'R':'.')
380 << ((i->write_state == peer_info::bw_torrent)?'t':
381 (i->write_state == peer_info::bw_global)?'w':
382 (i->write_state == peer_info::bw_network)?'W':'.')
383 << ((i->flags & peer_info::snubbed)?'S':'.')
384 << ((i->flags & peer_info::upload_only)?'U':'D')
385 #ifndef TORRENT_DISABLE_ENCRYPTION
386 << ((i->flags & peer_info::rc4_encrypted)?'E':
387 (i->flags & peer_info::plaintext_encrypted)?'e':'.')
388 #else
389 << ".."
390 #endif
391 << " "
392 << ((i->source & peer_info::tracker)?"T":"_")
393 << ((i->source & peer_info::pex)?"P":"_")
394 << ((i->source & peer_info::dht)?"D":"_")
395 << ((i->source & peer_info::lsd)?"L":"_")
396 << ((i->source & peer_info::resume_data)?"R":"_") << " ";
397 if (print_fails)
399 out << to_string(i->failcount, 3) << " "
400 << to_string(i->num_hashfails, 3) << " ";
402 if (print_send_bufs)
404 out << to_string(i->requests_in_buffer, 2) << " "
405 << to_string(i->used_send_buffer, 6) << " ("<< add_suffix(i->send_buffer_size) << ") "
406 << to_string(i->send_quota, 5) << " "
407 << to_string(i->used_receive_buffer, 6) << " ("<< add_suffix(i->receive_buffer_size) << ") ";
409 if (print_timers)
411 out << to_string(total_seconds(i->last_active), 8) << " "
412 << to_string(total_seconds(i->last_request), 4) << " "
413 << to_string(i->request_timeout, 7) << " ";
415 out << add_suffix(i->pending_disk_bytes) << " "
416 << to_string(i->rtt, 4) << " ";
418 if (print_block)
420 if (i->downloading_piece_index >= 0)
422 out << progress_bar(
423 i->downloading_progress / float(i->downloading_total), 14);
425 else
427 out << progress_bar(0.f, 14);
431 #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
432 if (i->country[0] == 0)
434 out << " ..";
436 else
438 out << " " << i->country[0] << i->country[1];
440 #endif
441 if (print_peer_rate) out << " " << (i->remote_dl_rate > 0 ? add_suffix(i->remote_dl_rate) + "/s ": " ");
442 out << " ";
444 if (i->flags & peer_info::handshake)
446 out << esc("31") << " waiting for handshake" << esc("0") << "\n";
448 else if (i->flags & peer_info::connecting)
450 out << esc("31") << " connecting to peer" << esc("0") << "\n";
452 else if (i->flags & peer_info::queued)
454 out << esc("33") << " queued" << esc("0") << "\n";
456 else
458 out << " " << i->client << "\n";
463 typedef std::multimap<std::string, libtorrent::torrent_handle> handles_t;
465 using boost::bind;
466 using boost::filesystem::path;
467 using boost::filesystem::exists;
468 using boost::filesystem::directory_iterator;
469 using boost::filesystem::extension;
472 // monitored_dir is true if this torrent is added because
473 // it was found in the directory that is monitored. If it
474 // is, it should be remembered so that it can be removed
475 // if it's no longer in that directory.
476 void add_torrent(libtorrent::session& ses
477 , handles_t& handles
478 , std::string const& torrent
479 , float preferred_ratio
480 , bool compact_mode
481 , path const& save_path
482 , bool monitored_dir
483 , int torrent_upload_limit
484 , int torrent_download_limit)
486 using namespace libtorrent;
488 boost::intrusive_ptr<torrent_info> t(new torrent_info(torrent.c_str()));
490 std::cout << t->name() << "\n";
492 add_torrent_params p;
493 lazy_entry resume_data;
495 std::string filename = (save_path / (t->name() + ".fastresume")).string();
497 std::vector<char> buf;
498 if (load_file(filename.c_str(), buf) == 0)
499 p.resume_data = &buf;
501 p.ti = t;
502 p.save_path = save_path;
503 p.storage_mode = compact_mode ? storage_mode_compact : storage_mode_sparse;
504 p.paused = true;
505 p.duplicate_is_error = false;
506 p.auto_managed = true;
507 torrent_handle h = ses.add_torrent(p);
509 handles.insert(std::make_pair(
510 monitored_dir?std::string(torrent):std::string(), h));
512 h.set_max_connections(50);
513 h.set_max_uploads(-1);
514 h.set_ratio(preferred_ratio);
515 h.set_upload_limit(torrent_upload_limit);
516 h.set_download_limit(torrent_download_limit);
517 #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
518 h.resolve_countries(true);
519 #endif
522 void scan_dir(path const& dir_path
523 , libtorrent::session& ses
524 , handles_t& handles
525 , float preferred_ratio
526 , bool compact_mode
527 , path const& save_path
528 , int torrent_upload_limit
529 , int torrent_download_limit)
531 std::set<std::string> valid;
533 using namespace libtorrent;
535 for (directory_iterator i(dir_path), end; i != end; ++i)
537 if (extension(*i) != ".torrent") continue;
538 std::string file = i->string();
540 handles_t::iterator k = handles.find(file);
541 if (k != handles.end())
543 valid.insert(file);
544 continue;
547 // the file has been added to the dir, start
548 // downloading it.
549 add_torrent(ses, handles, file, preferred_ratio, compact_mode
550 , save_path, true, torrent_upload_limit, torrent_download_limit);
551 valid.insert(file);
554 // remove the torrents that are no longer in the directory
556 for (handles_t::iterator i = handles.begin(); !handles.empty() && i != handles.end();)
558 if (i->first.empty() || valid.find(i->first) != valid.end())
560 ++i;
561 continue;
564 torrent_handle& h = i->second;
565 if (!h.is_valid())
567 handles.erase(i++);
568 continue;
571 h.auto_managed(false);
572 h.pause();
573 // the alert handler for save_resume_data_alert
574 // will save it to disk and remove the torrent
575 h.save_resume_data();
577 handles.erase(i++);
581 libtorrent::torrent_handle get_active_torrent(handles_t const& handles)
583 if (active_torrent >= handles.size()
584 || active_torrent < 0) return libtorrent::torrent_handle();
585 handles_t::const_iterator i = handles.begin();
586 std::advance(i, active_torrent);
587 return i->second;
590 void print_alert(libtorrent::alert const* a, std::ostream& os)
592 using namespace libtorrent;
594 #ifdef ANSI_TERMINAL_COLORS
595 if (a->category() & alert::error_notification)
597 os << esc("31");
599 else if (a->category() & (alert::peer_notification | alert::storage_notification))
601 os << esc("33");
603 #endif
604 os << "[" << time_now_string() << "] " << a->message();
605 #ifdef ANSI_TERMINAL_COLORS
606 os << esc("0");
607 #endif
609 if (g_log_file.good())
610 g_log_file << "[" << time_now_string() << "] " << a->message() << std::endl;
613 void handle_alert(libtorrent::session& ses, libtorrent::alert* a)
615 using namespace libtorrent;
617 if (torrent_finished_alert* p = dynamic_cast<torrent_finished_alert*>(a))
619 p->handle.set_max_connections(30);
621 // write resume data for the finished torrent
622 torrent_handle h = p->handle;
623 h.save_resume_data();
625 else if (save_resume_data_alert* p = dynamic_cast<save_resume_data_alert*>(a))
627 torrent_handle h = p->handle;
628 TORRENT_ASSERT(p->resume_data);
629 if (p->resume_data)
631 boost::filesystem::ofstream out(h.save_path() / (h.name() + ".fastresume")
632 , std::ios_base::binary);
633 out.unsetf(std::ios_base::skipws);
634 bencode(std::ostream_iterator<char>(out), *p->resume_data);
635 if (h.is_paused() && !h.is_auto_managed()) ses.remove_torrent(h);
640 static char const* state_str[] =
641 {"checking (q)", "checking", "dl metadata"
642 , "downloading", "finished", "seeding", "allocating"};
644 int main(int ac, char* av[])
646 #if BOOST_VERSION < 103400
647 using boost::filesystem::no_check;
648 path::default_name_check(no_check);
649 #endif
651 int listen_port;
652 float preferred_ratio;
653 int download_limit;
654 int upload_limit;
655 int torrent_upload_limit;
656 int torrent_download_limit;
657 int upload_slots_limit;
658 int half_open_limit;
659 std::string save_path_str;
660 std::string log_level;
661 std::string log_file_name;
662 std::string ip_filter_file;
663 std::string allocation_mode;
664 std::string in_monitor_dir;
665 std::string bind_to_interface;
666 std::string proxy;
667 std::string proxy_login;
668 std::string proxy_type;
669 int poll_interval;
670 int wait_retry;
671 int bind_port_start = 0;
672 int bind_port_end = 0;
674 namespace po = boost::program_options;
678 po::options_description desc("supported options");
679 desc.add_options()
680 ("help,h", "display this help message")
681 ("port,p", po::value<int>(&listen_port)->default_value(6881)
682 , "set listening port")
683 ("ratio,r", po::value<float>(&preferred_ratio)->default_value(0)
684 , "set the preferred upload/download ratio. 0 means infinite. Values "
685 "smaller than 1 are clamped to 1.")
686 ("max-download-rate,d", po::value<int>(&download_limit)->default_value(0)
687 , "the maximum download rate given in kB/s. 0 means infinite.")
688 ("max-upload-rate,u", po::value<int>(&upload_limit)->default_value(0)
689 , "the maximum upload rate given in kB/s. 0 means infinite.")
690 ("max-torrent-upload-rate", po::value<int>(&torrent_upload_limit)->default_value(20)
691 , "the maximum upload rate for an individual torrent, given in kB/s. 0 means infinite.")
692 ("max-torrent-download-rate", po::value<int>(&torrent_download_limit)->default_value(0)
693 , "the maximum download rate for an individual torrent, given in kB/s. 0 means infinite.")
694 ("max-upload-slots", po::value<int>(&upload_slots_limit)->default_value(5)
695 , "the maximum number of upload slots. 0 means infinite.")
696 ("save-path,s", po::value<std::string>(&save_path_str)->default_value("./")
697 , "the path where the downloaded file/folder should be placed.")
698 ("log-level,l", po::value<std::string>(&log_level)->default_value("info")
699 , "sets the level at which events are logged [debug | info | warning | fatal].")
700 ("log-file,f", po::value<std::string>(&log_file_name)->default_value("")
701 , "sets a file to log all events to")
702 ("ip-filter,f", po::value<std::string>(&ip_filter_file)->default_value("")
703 , "sets the path to the ip-filter file used to block access from certain "
704 "ips. ")
705 ("allocation-mode,a", po::value<std::string>(&allocation_mode)->default_value("full")
706 , "sets mode used for allocating the downloaded files on disk. "
707 "Possible options are [full | compact]")
708 ("input-file,i", po::value<std::vector<std::string> >()
709 , "adds an input .torrent file. At least one is required. arguments "
710 "without any flag are implicitly an input file. To start a torrentless "
711 "download, use <info-hash>@<tracker-url> instead of specifying a file.")
712 ("monitor-dir,m", po::value<std::string>(&in_monitor_dir)
713 , "monitors the given directory, looking for .torrent files and "
714 "automatically starts downloading them. It will stop downloading "
715 "torrent files that are removed from the directory")
716 ("poll-interval,t", po::value<int>(&poll_interval)->default_value(2)
717 , "if a directory is being monitored, this is the interval (given "
718 "in seconds) between two refreshes of the directory listing")
719 ("wait-retry,w", po::value<int>(&wait_retry)->default_value(30)
720 , "if the download of a url seed failes, this is the interval (given "
721 "in seconds) to wait until the next retry")
722 ("half-open-limit,o", po::value<int>(&half_open_limit)->default_value(-1)
723 , "Sets the maximum number of simultaneous half-open tcp connections")
724 ("bind,b", po::value<std::string>(&bind_to_interface)->default_value("")
725 , "Sets the local interface to bind outbound and the listen "
726 "socket to")
727 ("proxy-server,x", po::value<std::string>(&proxy)->default_value("")
728 , "Sets the http proxy to be used for tracker and web seeds "
729 "connections. The string is expected to be on the form: "
730 "<hostname>:<port>. If no port is specified, 8080 is assumed")
731 ("proxy-login,n", po::value<std::string>(&proxy_login)->default_value("")
732 , "Sets the username and password used to authenticate with the http "
733 "proxy. The string should be given in the form: <username>:<password>")
734 ("proxy-type", po::value<std::string>(&proxy_type)->default_value("socks5")
735 , "Sets the type of proxy to use [socks5 | http] ")
736 ("bind-port-start", po::value<int>(&bind_port_start)->default_value(0)
737 , "The lower port number that outgoing connections will be bound to")
738 ("bind-port-end", po::value<int>(&bind_port_end)->default_value(0)
739 , "The upper port number that outgoing connections will be bound to")
742 po::positional_options_description p;
743 p.add("input-file", -1);
745 po::variables_map vm;
746 po::store(po::command_line_parser(ac, av).
747 options(desc).positional(p).run(), vm);
748 po::notify(vm);
750 // make sure the arguments stays within the usable limits
751 path monitor_dir(in_monitor_dir);
752 if (listen_port < 0 || listen_port > 65525) listen_port = 6881;
753 if (preferred_ratio != 0 && preferred_ratio < 1.f) preferred_ratio = 1.f;
754 upload_limit *= 1000;
755 torrent_upload_limit *= 1000;
756 torrent_download_limit *= 1000;
757 download_limit *= 1000;
758 if (download_limit <= 0) download_limit = -1;
759 if (upload_limit <= 0) upload_limit = -1;
760 if (torrent_upload_limit <= 0) torrent_upload_limit = -1;
761 if (torrent_download_limit <= 0) torrent_download_limit = -1;
762 if (poll_interval < 2) poll_interval = 2;
763 if (wait_retry < 0) wait_retry = 0;
764 if (half_open_limit < 1) half_open_limit = -1;
765 if (upload_slots_limit <= 0) upload_slots_limit = -1;
766 if (!monitor_dir.empty() && !exists(monitor_dir))
768 std::cerr << "The monitor directory doesn't exist: " << monitor_dir.string() << std::endl;
769 return 1;
772 if (vm.count("help")
773 || vm.count("input-file") + vm.count("monitor-dir") == 0)
775 std::cout << desc << "\n";
776 return 1;
779 if (!log_file_name.empty())
780 g_log_file.open(log_file_name.c_str());
782 bool compact_allocation_mode = (allocation_mode == "compact");
784 using namespace libtorrent;
786 std::vector<std::string> input;
787 if (vm.count("input-file") > 0)
788 input = vm["input-file"].as< std::vector<std::string> >();
790 session_settings settings;
791 proxy_settings ps;
793 if (!proxy.empty())
795 std::size_t i = proxy.find(':');
796 ps.hostname = proxy.substr(0, i);
797 if (i == std::string::npos) ps.port = 8080;
798 else ps.port = atoi(proxy.substr(i + 1).c_str());
799 if (proxy_type == "socks5")
800 ps.type = proxy_settings::socks5;
801 else
802 ps.type = proxy_settings::http;
804 if (!proxy_login.empty())
806 std::size_t i = proxy_login.find(':');
807 if (i == std::string::npos)
809 std::cerr << "Proxy login did not match the required format: "
810 << proxy_login << std::endl;
811 return 1;
813 ps.username = proxy_login.substr(0, i);
814 ps.password = proxy_login.substr(i + 1);
815 if (proxy_type == "socks5")
816 ps.type = proxy_settings::socks5_pw;
817 else
818 ps.type = proxy_settings::http_pw;
822 settings.user_agent = "client_test/" LIBTORRENT_VERSION;
823 settings.urlseed_wait_retry = wait_retry;
825 settings.outgoing_ports.first = bind_port_start;
826 settings.outgoing_ports.second = bind_port_end;
828 std::deque<std::string> events;
830 ptime next_dir_scan = time_now();
832 // the string is the filename of the .torrent file, but only if
833 // it was added through the directory monitor. It is used to
834 // be able to remove torrents that were added via the directory
835 // monitor when they're not in the directory anymore.
836 handles_t handles;
837 session ses;
838 #ifndef TORRENT_DISABLE_GEO_IP
839 ses.load_asnum_db("GeoIPASNum.dat");
840 ses.load_country_db("GeoIP.dat");
841 #endif
842 // UPnP port mapping
843 ses.start_upnp();
844 // NAT-PMP port mapping
845 ses.start_natpmp();
846 // Local service discovery (finds peers on the local network)
847 ses.start_lsd();
848 ses.add_extension(&create_metadata_plugin);
849 ses.add_extension(&create_ut_pex_plugin);
850 ses.add_extension(&create_ut_metadata_plugin);
851 ses.add_extension(&create_smart_ban_plugin);
853 ses.set_max_uploads(upload_slots_limit);
854 ses.set_max_half_open_connections(half_open_limit);
855 ses.set_download_rate_limit(download_limit);
856 ses.set_upload_rate_limit(upload_limit);
857 ses.listen_on(std::make_pair(listen_port, listen_port + 10)
858 , bind_to_interface.c_str());
859 ses.set_settings(settings);
860 ses.set_tracker_proxy(ps);
861 ses.set_peer_proxy(ps);
862 ses.set_web_seed_proxy(ps);
864 #ifndef TORRENT_NO_DEPRECATE
865 if (log_level == "debug")
866 ses.set_severity_level(alert::debug);
867 else if (log_level == "warning")
868 ses.set_severity_level(alert::warning);
869 else if (log_level == "fatal")
870 ses.set_severity_level(alert::fatal);
871 else
872 ses.set_severity_level(alert::info);
873 #endif
875 boost::filesystem::ifstream ses_state_file(".ses_state"
876 , std::ios_base::binary);
877 ses_state_file.unsetf(std::ios_base::skipws);
878 ses.load_state(bdecode(
879 std::istream_iterator<char>(ses_state_file)
880 , std::istream_iterator<char>()));
882 #ifndef TORRENT_DISABLE_DHT
883 settings.use_dht_as_fallback = false;
885 boost::filesystem::ifstream dht_state_file(".dht_state"
886 , std::ios_base::binary);
887 dht_state_file.unsetf(std::ios_base::skipws);
888 entry dht_state;
889 dht_state = bdecode(
890 std::istream_iterator<char>(dht_state_file)
891 , std::istream_iterator<char>());
892 ses.start_dht(dht_state);
893 ses.add_dht_router(std::make_pair(std::string("router.bittorrent.com")
894 , 6881));
895 ses.add_dht_router(std::make_pair(std::string("router.utorrent.com")
896 , 6881));
897 ses.add_dht_router(std::make_pair(std::string("router.bitcomet.com")
898 , 6881));
899 #endif
901 // look for ipfilter.dat
902 // poor man's parser
903 // reads emule ipfilter files.
904 // with the following format:
906 // <first-ip> - <last-ip> , <access> , <comment>
908 // first-ip is an ip address that defines the first
909 // address of the range
910 // last-ip is the last ip address in the range
911 // access is a number specifying the access control
912 // for this ip-range. Right now values > 127 = allowed
913 // and numbers <= 127 = blocked
914 // the rest of the line is ignored
916 // In the original spec ranges may not overlap, but
917 // here ranges may overlap, and it is the last added
918 // rule that has precedence for addresses that may fall
919 // into more than one range.
920 if (!ip_filter_file.empty())
922 std::ifstream in(ip_filter_file.c_str());
923 ip_filter filter;
924 while (in.good())
926 char line[300];
927 in.getline(line, 300);
928 int len = in.gcount();
929 if (len <= 0) continue;
930 if (line[0] == '#') continue;
931 int a, b, c, d;
932 char dummy;
933 std::stringstream ln(line);
934 ln >> a >> dummy >> b >> dummy >> c >> dummy >> d >> dummy;
935 address_v4 start((a << 24) + (b << 16) + (c << 8) + d);
936 ln >> a >> dummy >> b >> dummy >> c >> dummy >> d >> dummy;
937 address_v4 last((a << 24) + (b << 16) + (c << 8) + d);
938 int flags;
939 ln >> flags;
940 if (flags <= 127) flags = ip_filter::blocked;
941 else flags = 0;
942 if (ln.fail()) break;
943 filter.add_rule(start, last, flags);
945 ses.set_ip_filter(filter);
947 boost::filesystem::path save_path(save_path_str);
949 // load the torrents given on the commandline
950 boost::regex ex("([0-9A-Fa-f]{40})@(.+)");
951 for (std::vector<std::string>::const_iterator i = input.begin();
952 i != input.end(); ++i)
954 #ifndef BOOST_NO_EXCEPTIONS
957 #endif
958 // first see if this is a torrentless download
959 if (i->substr(0, 7) == "magnet:")
961 add_torrent_params p;
962 p.save_path = save_path;
963 p.storage_mode = compact_allocation_mode ? storage_mode_compact
964 : storage_mode_sparse;
965 std::cout << "adding MANGET link: " << *i << std::endl;
966 torrent_handle h = add_magnet_uri(ses, *i, p);
968 handles.insert(std::make_pair(std::string(), h));
970 h.set_max_connections(50);
971 h.set_max_uploads(-1);
972 h.set_ratio(preferred_ratio);
973 h.set_upload_limit(torrent_upload_limit);
974 h.set_download_limit(torrent_download_limit);
975 continue;
977 boost::cmatch what;
978 if (boost::regex_match(i->c_str(), what, ex))
980 sha1_hash info_hash = boost::lexical_cast<sha1_hash>(what[1]);
982 add_torrent_params p;
983 p.name = std::string(what[2]).c_str();
984 p.info_hash = info_hash;
985 p.save_path = save_path;
986 p.storage_mode = compact_allocation_mode ? storage_mode_compact : storage_mode_sparse;
987 p.paused = true;
988 p.duplicate_is_error = false;
989 p.auto_managed = true;
990 torrent_handle h = ses.add_torrent(p);
992 handles.insert(std::make_pair(std::string(), h));
994 h.set_max_connections(50);
995 h.set_max_uploads(-1);
996 h.set_ratio(preferred_ratio);
997 h.set_upload_limit(torrent_upload_limit);
998 h.set_download_limit(torrent_download_limit);
999 continue;
1001 // if it's a torrent file, open it as usual
1002 add_torrent(ses, handles, i->c_str(), preferred_ratio
1003 , compact_allocation_mode, save_path, false
1004 , torrent_upload_limit, torrent_download_limit);
1005 #ifndef BOOST_NO_EXCEPTIONS
1007 catch (std::exception& e)
1009 std::cout << e.what() << "\n";
1011 #endif
1014 // main loop
1015 std::vector<peer_info> peers;
1016 std::vector<partial_piece_info> queue;
1018 for (;;)
1020 char c;
1021 while (sleep_and_input(&c))
1023 if (c == 27)
1025 // escape code, read another character
1026 #ifdef _WIN32
1027 c = _getch();
1028 #else
1029 c = getc(stdin);
1030 #endif
1031 if (c != '[') break;
1032 #ifdef _WIN32
1033 c = _getch();
1034 #else
1035 c = getc(stdin);
1036 #endif
1037 if (c == 65)
1039 // arrow up
1040 --active_torrent;
1041 if (active_torrent < 0) active_torrent = 0;
1043 else if (c == 66)
1045 // arrow down
1046 ++active_torrent;
1047 if (active_torrent >= handles.size()) active_torrent = handles.size() - 1;
1051 if (c == ' ')
1053 if (ses.is_paused()) ses.resume();
1054 else ses.pause();
1057 if (c == 'm')
1059 std::cout << "saving peers for torrents" << std::endl;
1061 std::vector<peer_list_entry> peers;
1062 for (handles_t::iterator i = handles.begin();
1063 i != handles.end(); ++i)
1065 i->second.get_full_peer_list(peers);
1066 std::ofstream f(("peers_" + i->second.name()).c_str());
1067 for (std::vector<peer_list_entry>::iterator k = peers.begin()
1068 , end(peers.end()); k != end; ++k)
1070 f << k->ip.address()
1071 #ifndef TORRENT_DISABLE_GEO_IP
1072 << "\t" << ses.as_for_ip(k->ip.address())
1073 #endif
1074 << std::endl;
1079 if (c == 'q')
1081 // keep track of the number of resume data
1082 // alerts to wait for
1083 int num_resume_data = 0;
1084 for (handles_t::iterator i = handles.begin();
1085 i != handles.end(); ++i)
1087 torrent_handle& h = i->second;
1088 if (!h.is_valid() || !h.has_metadata()) continue;
1090 // pause
1091 std::cout << "pausing " << h.name() << std::endl;
1092 h.pause();
1093 // save_resume_data will generate an alert when it's done
1094 h.save_resume_data();
1095 ++num_resume_data;
1097 std::cout << "waiting for resume data" << std::endl;
1099 while (num_resume_data > 0)
1101 alert const* a = ses.wait_for_alert(seconds(30));
1102 if (a == 0)
1104 std::cout << " aborting with " << num_resume_data << " outstanding "
1105 "torrents to save resume data for" << std::endl;
1106 break;
1109 std::auto_ptr<alert> holder = ses.pop_alert();
1111 ::print_alert(holder.get(), std::cout);
1112 std::cout << std::endl;
1114 save_resume_data_alert const* rd = dynamic_cast<save_resume_data_alert const*>(a);
1115 if (!rd) continue;
1116 --num_resume_data;
1118 if (!rd->resume_data) continue;
1120 torrent_handle h = rd->handle;
1121 boost::filesystem::ofstream out(h.save_path()
1122 / (h.get_torrent_info().name() + ".fastresume"), std::ios_base::binary);
1123 out.unsetf(std::ios_base::skipws);
1124 bencode(std::ostream_iterator<char>(out), *rd->resume_data);
1126 break;
1129 if (c == 'j')
1131 torrent_handle h = get_active_torrent(handles);
1132 if (h.is_valid()) h.force_recheck();
1135 if (c == 'r')
1137 torrent_handle h = get_active_torrent(handles);
1138 if (h.is_valid()) h.force_reannounce();
1141 if (c == 's')
1143 torrent_handle h = get_active_torrent(handles);
1144 if (h.is_valid()) h.set_sequential_download(!h.is_sequential_download());
1147 if (c == 'p')
1149 torrent_handle h = get_active_torrent(handles);
1150 if (h.is_valid())
1152 if (!h.is_auto_managed() && h.is_paused())
1154 h.auto_managed(true);
1156 else
1158 h.auto_managed(false);
1159 h.pause();
1164 if (c == 'c')
1166 torrent_handle h = get_active_torrent(handles);
1167 if (h.is_valid()) h.clear_error();
1170 // toggle displays
1171 if (c == 'i') print_peers = !print_peers;
1172 if (c == 'l') print_log = !print_log;
1173 if (c == 'd') print_downloads = !print_downloads;
1174 if (c == 'f') print_file_progress = !print_file_progress;
1175 if (c == 'a') print_piece_bar = !print_piece_bar;
1176 // toggle columns
1177 if (c == '1') print_ip = !print_ip;
1178 if (c == '2') print_as = !print_as;
1179 if (c == '3') print_timers = !print_timers;
1180 if (c == '4') print_block = !print_block;
1181 if (c == '5') print_peer_rate = !print_peer_rate;
1182 if (c == '6') print_fails = !print_fails;
1183 if (c == '7') print_send_bufs = !print_send_bufs;
1185 if (c == 'q') break;
1187 int terminal_width = 80;
1189 #ifndef _WIN32
1191 winsize size;
1192 ioctl(STDOUT_FILENO, TIOCGWINSZ, (char*)&size);
1193 terminal_width = size.ws_col;
1195 #endif
1197 // loop through the alert queue to see if anything has happened.
1198 std::auto_ptr<alert> a;
1199 a = ses.pop_alert();
1200 std::string now = time_now_string();
1201 while (a.get())
1203 std::stringstream event_string;
1205 ::print_alert(a.get(), event_string);
1206 ::handle_alert(ses, a.get());
1208 events.push_back(event_string.str());
1209 if (events.size() >= 20) events.pop_front();
1211 a = ses.pop_alert();
1214 session_status sess_stat = ses.status();
1216 std::stringstream out;
1217 out << "[q] quit [i] toggle peers [d] toggle downloading pieces [p] toggle paused "
1218 "[a] toggle piece bar [s] toggle download sequential [f] toggle files "
1219 "[j] force recheck [space] toggle session pause [c] clear error\n"
1220 "[1] toggle IP [2] toggle AS [3] toggle timers [4] toggle block progress "
1221 "[5] toggle peer rate [6] toggle failures [7] toggle send buffers\n";
1223 int torrent_index = 0;
1224 torrent_handle active_handle;
1225 for (handles_t::iterator i = handles.begin();
1226 i != handles.end(); ++torrent_index)
1228 torrent_handle& h = i->second;
1229 if (!h.is_valid())
1231 handles.erase(i++);
1232 continue;
1234 else
1236 ++i;
1239 #ifdef ANSI_TERMINAL_COLORS
1240 char const* term = "\x1b[0m";
1241 #else
1242 char const* term = "";
1243 #endif
1244 if (active_torrent == torrent_index)
1246 term = "\x1b[0m\x1b[7m";
1247 out << esc("7") << "*";
1249 else
1251 out << " ";
1254 int queue_pos = h.queue_position();
1255 if (queue_pos == -1) out << "- ";
1256 else out << std::setw(3) << queue_pos;
1258 if (h.is_paused()) out << esc("34");
1259 else out << esc("37");
1260 out << std::setw(40) << std::setiosflags(std::ios::left);
1262 std::string name = h.name();
1263 if (name.size() > 40) name.resize(40);
1264 out << name;
1266 out << term << " ";
1268 torrent_status s = h.status();
1270 bool paused = h.is_paused();
1271 bool auto_managed = h.is_auto_managed();
1272 out << std::setw(13) << std::setiosflags(std::ios::left);
1273 if (!s.error.empty())
1275 out << esc("31") << "error " << s.error;
1276 out << esc("0") << std::endl;
1277 continue;
1280 if (paused && !auto_managed) out << "paused";
1281 else if (paused && auto_managed) out << "queued";
1282 else out << state_str[s.state];
1284 int seeds = 0;
1285 int downloaders = 0;
1287 if (s.num_complete >= 0) seeds = s.num_complete;
1288 else seeds = s.list_seeds;
1290 if (s.num_incomplete >= 0) downloaders = s.num_incomplete;
1291 else downloaders = s.list_peers - s.list_seeds;
1293 out << "download: " << "(" << esc("32") << add_suffix(s.total_download) << term << ") "
1294 "upload: " << esc("31") << (s.upload_rate > 0 ? add_suffix(s.upload_rate) + "/s ": " ") << term
1295 << "(" << esc("31") << add_suffix(s.total_upload) << term << ") "
1296 << "swarm: " << to_string(downloaders, 4) << ":" << to_string(seeds, 4)
1297 << " bw queue: (" << s.up_bandwidth_queue << " | " << s.down_bandwidth_queue << ") "
1298 "all-time (Rx: " << esc("32") << add_suffix(s.all_time_download) << term
1299 << " Tx: " << esc("31") << add_suffix(s.all_time_upload) << term << ") "
1300 << std::hex << s.seed_rank << std::dec << " "
1301 << s.last_scrape << "\n" << esc("0");
1303 if (s.state != torrent_status::seeding)
1305 char const* progress_bar_color = "33"; // yellow
1306 if (s.state == torrent_status::checking_files
1307 || s.state == torrent_status::downloading_metadata)
1309 progress_bar_color = "35"; // magenta
1311 else if (s.current_tracker.empty())
1313 progress_bar_color = "31"; // red
1315 else if (sess_stat.has_incoming_connections)
1317 progress_bar_color = "32"; // green
1319 out << " progress: " << esc("32") << s.total_done << esc("0") << " Bytes ";
1320 out.precision(4);
1321 out.width(5);
1322 out.fill(' ');
1323 out << (s.progress*100) << "% ";
1324 out << progress_bar(s.progress, terminal_width - 37, progress_bar_color) << "\n";
1325 if (print_piece_bar && s.progress < 1.f)
1326 out << " " << piece_bar(s.pieces, terminal_width - 5) << "\n";
1327 out << " peers: " << esc("37") << s.num_peers << esc("0") << " (" << esc("37") << s.connect_candidates << esc("0") << ") "
1328 << "seeds: " << esc("37") << s.num_seeds << esc("0") << " "
1329 << "distributed copies: " << esc("37") << s.distributed_copies << esc("0")
1330 // << " magnet-link: " << make_magnet_uri(h) << "\n"
1331 << " download: " << esc("32") << (s.download_rate > 0 ? add_suffix(s.download_rate) + "/s ": " ") << esc("0");
1332 boost::posix_time::time_duration t = s.next_announce;
1333 out << " next announce: " << esc("37")
1334 << to_string(t.hours(), 2) << ":"
1335 << to_string(t.minutes(), 2) << ":"
1336 << to_string(t.seconds(), 2) << esc("0") << " ";
1337 out << "tracker: " << esc("36") << s.current_tracker << esc("0") << "\n";
1340 if (torrent_index != active_torrent) continue;
1341 active_handle = h;
1344 cache_status cs = ses.get_cache_status();
1345 if (cs.blocks_read < 1) cs.blocks_read = 1;
1346 if (cs.blocks_written < 1) cs.blocks_written = 1;
1348 out << "==== conns: " << sess_stat.num_peers
1349 << " down: " << esc("32") << add_suffix(sess_stat.download_rate) << "/s" << esc("0")
1350 << " (" << esc("32") << add_suffix(sess_stat.total_download) << esc("0") << ") "
1351 " up: " << esc("31") << add_suffix(sess_stat.upload_rate) << "/s " << esc("0")
1352 << " (" << esc("31") << add_suffix(sess_stat.total_upload) << esc("0") << ")"
1353 " waste: " << add_suffix(sess_stat.total_redundant_bytes)
1354 << " fail: " << add_suffix(sess_stat.total_failed_bytes)
1355 << " unchoked: " << sess_stat.num_unchoked << " / " << sess_stat.allowed_upload_slots
1356 << " bw queues: (" << sess_stat.up_bandwidth_queue
1357 << " | " << sess_stat.down_bandwidth_queue << ") "
1358 " write cache hits: " << ((cs.blocks_written - cs.writes) * 100 / cs.blocks_written) << "% "
1359 " read cache hits: " << (cs.blocks_read_hit * 100 / cs.blocks_read) << "% "
1360 " cache size: " << add_suffix(cs.cache_size * 16 * 1024)
1361 << " (" << add_suffix(cs.read_cache_size * 16 * 1024) << ")"
1362 " ====" << std::endl;
1364 if (active_handle.is_valid())
1366 torrent_handle h = active_handle;
1367 torrent_status s = h.status();
1369 if ((print_downloads && s.state != torrent_status::seeding)
1370 || print_peers)
1371 h.get_peer_info(peers);
1373 out << "====== " << h.name() << " ======" << std::endl;
1375 if (print_peers && !peers.empty())
1376 print_peer_info(out, peers);
1378 if (print_downloads)
1380 h.get_download_queue(queue);
1381 std::sort(queue.begin(), queue.end(), bind(&partial_piece_info::piece_index, _1)
1382 < bind(&partial_piece_info::piece_index, _2));
1384 std::vector<cached_piece_info> pieces;
1385 ses.get_cache_info(h.info_hash(), pieces);
1387 for (std::vector<partial_piece_info>::iterator i = queue.begin();
1388 i != queue.end(); ++i)
1390 cached_piece_info* cp = 0;
1391 std::vector<cached_piece_info>::iterator cpi = std::find_if(pieces.begin(), pieces.end()
1392 , bind(&cached_piece_info::piece, _1) == i->piece_index);
1393 if (cpi != pieces.end()) cp = &*cpi;
1395 out << to_string(i->piece_index, 4) << ": [";
1396 for (int j = 0; j < i->blocks_in_piece; ++j)
1398 int index = peer_index(i->blocks[j].peer, peers);
1399 char str[] = "+";
1400 if (index >= 0)
1401 str[0] = (index < 10)?'0' + index:'A' + index - 10;
1403 #ifdef ANSI_TERMINAL_COLORS
1404 if (cp && cp->blocks[j]) out << esc("36;7") << str << esc("0");
1405 else if (i->blocks[j].bytes_progress > 0
1406 && i->blocks[j].state == block_info::requested)
1408 if (i->blocks[j].num_peers > 1)
1409 out << esc("1;7");
1410 else
1411 out << esc("33;7");
1412 out << to_string(i->blocks[j].bytes_progress / float(i->blocks[j].block_size) * 10, 1) << esc("0");
1414 else if (i->blocks[j].state == block_info::finished) out << esc("32;7") << str << esc("0");
1415 else if (i->blocks[j].state == block_info::writing) out << esc("35;7") << str << esc("0");
1416 else if (i->blocks[j].state == block_info::requested) out << str;
1417 else out << " ";
1418 #else
1419 if (cp && cp->blocks[j]) out << "c";
1420 else if (i->blocks[j].state == block_info::finished) out << "#";
1421 else if (i->blocks[j].state == block_info::writing) out << "+";
1422 else if (i->blocks[j].state == block_info::requested) out << str;
1423 else out << " ";
1424 #endif
1426 char const* piece_state[4] = {"", "slow", "medium", "fast"};
1427 out << "] " << piece_state[i->piece_state];
1428 if (cp) out << (i->piece_state > 0?" | ":"") << "cache age: " << (total_milliseconds(time_now() - cp->last_use) / 1000.f);
1429 out << "\n";
1432 for (std::vector<cached_piece_info>::iterator i = pieces.begin()
1433 , end(pieces.end()); i != end; ++i)
1435 if (i->kind != cached_piece_info::read_cache) continue;
1436 out << to_string(i->piece, 4) << ": [";
1437 for (std::vector<bool>::iterator k = i->blocks.begin()
1438 , end(i->blocks.end()); k != end; ++k)
1440 #ifdef ANSI_TERMINAL_COLORS
1441 if (*k) out << esc("33;7") << " " << esc("0");
1442 else out << " ";
1443 #else
1444 if (*k) out << "#";
1445 else out << " ";
1446 #endif
1448 out << "] " << "cache age: "
1449 << (total_milliseconds(time_now() - i->last_use) / 1000.f)
1450 << "\n";
1452 out << "___________________________________\n";
1455 if (print_file_progress
1456 && s.state != torrent_status::seeding
1457 && h.has_metadata())
1459 std::vector<size_type> file_progress;
1460 h.file_progress(file_progress);
1461 torrent_info const& info = h.get_torrent_info();
1462 for (int i = 0; i < info.num_files(); ++i)
1464 float progress = info.file_at(i).size > 0
1465 ?float(file_progress[i]) / info.file_at(i).size:1;
1466 if (file_progress[i] == info.file_at(i).size)
1467 out << progress_bar(1.f, 100, "32");
1468 else
1469 out << progress_bar(progress, 100, "33");
1470 out << " " << to_string(progress * 100.f, 5) << "% "
1471 << add_suffix(file_progress[i]) << " "
1472 << info.file_at(i).path.leaf() << "\n";
1475 out << "___________________________________\n";
1480 if (print_log)
1482 for (std::deque<std::string>::iterator i = events.begin();
1483 i != events.end(); ++i)
1485 out << "\n" << *i;
1489 clear_home();
1490 puts(out.str().c_str());
1492 if (!monitor_dir.empty()
1493 && next_dir_scan < time_now())
1495 scan_dir(monitor_dir, ses, handles, preferred_ratio
1496 , compact_allocation_mode, save_path, torrent_upload_limit
1497 , torrent_download_limit);
1498 next_dir_scan = time_now() + seconds(poll_interval);
1502 std::cout << "saving session state" << std::endl;
1504 entry session_state = ses.state();
1505 boost::filesystem::ofstream out(".ses_state"
1506 , std::ios_base::binary);
1507 out.unsetf(std::ios_base::skipws);
1508 bencode(std::ostream_iterator<char>(out), session_state);
1511 #ifndef TORRENT_DISABLE_DHT
1512 std::cout << "saving DHT state" << std::endl;
1513 dht_state = ses.dht_state();
1514 boost::filesystem::ofstream out(".dht_state"
1515 , std::ios_base::binary);
1516 out.unsetf(std::ios_base::skipws);
1517 bencode(std::ostream_iterator<char>(out), dht_state);
1518 #endif
1519 std::cout << "closing session" << std::endl;
1521 #ifndef BOOST_NO_EXCEPTIONS
1522 catch (std::exception& e)
1524 std::cout << e.what() << "\n";
1526 #endif
1528 return 0;