added dict_find_string to lazy_entry
[libtorrent.git] / examples / client_test.cpp
blob5be698dab9a4efaec777fb929121d31265710dfd
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 #ifndef TORRENT_DISABLE_ENCRYPTION
385 << ((i->flags & peer_info::rc4_encrypted)?'E':
386 (i->flags & peer_info::plaintext_encrypted)?'e':'.')
387 #else
388 << ".."
389 #endif
390 << " "
391 << ((i->source & peer_info::tracker)?"T":"_")
392 << ((i->source & peer_info::pex)?"P":"_")
393 << ((i->source & peer_info::dht)?"D":"_")
394 << ((i->source & peer_info::lsd)?"L":"_")
395 << ((i->source & peer_info::resume_data)?"R":"_") << " ";
396 if (print_fails)
398 out << to_string(i->failcount, 3) << " "
399 << to_string(i->num_hashfails, 3) << " ";
401 if (print_send_bufs)
403 out << to_string(i->requests_in_buffer, 2) << " "
404 << to_string(i->used_send_buffer, 6) << " ("<< add_suffix(i->send_buffer_size) << ") "
405 << to_string(i->send_quota, 5) << " "
406 << to_string(i->used_receive_buffer, 6) << " ("<< add_suffix(i->receive_buffer_size) << ") ";
408 if (print_timers)
410 out << to_string(total_seconds(i->last_active), 8) << " "
411 << to_string(total_seconds(i->last_request), 4) << " "
412 << to_string(i->request_timeout, 7) << " ";
414 out << add_suffix(i->pending_disk_bytes) << " "
415 << to_string(i->rtt, 4) << " ";
417 if (print_block)
419 if (i->downloading_piece_index >= 0)
421 out << progress_bar(
422 i->downloading_progress / float(i->downloading_total), 14);
424 else
426 out << progress_bar(0.f, 14);
430 #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
431 if (i->country[0] == 0)
433 out << " ..";
435 else
437 out << " " << i->country[0] << i->country[1];
439 #endif
440 if (print_peer_rate) out << " " << (i->remote_dl_rate > 0 ? add_suffix(i->remote_dl_rate) + "/s ": " ");
441 out << " ";
443 if (i->flags & peer_info::handshake)
445 out << esc("31") << " waiting for handshake" << esc("0") << "\n";
447 else if (i->flags & peer_info::connecting)
449 out << esc("31") << " connecting to peer" << esc("0") << "\n";
451 else if (i->flags & peer_info::queued)
453 out << esc("33") << " queued" << esc("0") << "\n";
455 else
457 out << " " << i->client << "\n";
462 typedef std::multimap<std::string, libtorrent::torrent_handle> handles_t;
464 using boost::bind;
465 using boost::filesystem::path;
466 using boost::filesystem::exists;
467 using boost::filesystem::directory_iterator;
468 using boost::filesystem::extension;
471 // monitored_dir is true if this torrent is added because
472 // it was found in the directory that is monitored. If it
473 // is, it should be remembered so that it can be removed
474 // if it's no longer in that directory.
475 void add_torrent(libtorrent::session& ses
476 , handles_t& handles
477 , std::string const& torrent
478 , float preferred_ratio
479 , bool compact_mode
480 , path const& save_path
481 , bool monitored_dir
482 , int torrent_upload_limit
483 , int torrent_download_limit)
485 using namespace libtorrent;
487 boost::intrusive_ptr<torrent_info> t(new torrent_info(torrent.c_str()));
489 std::cout << t->name() << "\n";
491 add_torrent_params p;
492 lazy_entry resume_data;
494 std::string filename = (save_path / (t->name() + ".fastresume")).string();
496 std::vector<char> buf;
497 if (load_file(filename.c_str(), buf) == 0)
498 p.resume_data = &buf;
500 p.ti = t;
501 p.save_path = save_path;
502 p.storage_mode = compact_mode ? storage_mode_compact : storage_mode_sparse;
503 p.paused = true;
504 p.duplicate_is_error = false;
505 p.auto_managed = true;
506 torrent_handle h = ses.add_torrent(p);
508 handles.insert(std::make_pair(
509 monitored_dir?std::string(torrent):std::string(), h));
511 h.set_max_connections(50);
512 h.set_max_uploads(-1);
513 h.set_ratio(preferred_ratio);
514 h.set_upload_limit(torrent_upload_limit);
515 h.set_download_limit(torrent_download_limit);
516 #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
517 h.resolve_countries(true);
518 #endif
521 void scan_dir(path const& dir_path
522 , libtorrent::session& ses
523 , handles_t& handles
524 , float preferred_ratio
525 , bool compact_mode
526 , path const& save_path
527 , int torrent_upload_limit
528 , int torrent_download_limit)
530 std::set<std::string> valid;
532 using namespace libtorrent;
534 for (directory_iterator i(dir_path), end; i != end; ++i)
536 if (extension(*i) != ".torrent") continue;
537 std::string file = i->string();
539 handles_t::iterator k = handles.find(file);
540 if (k != handles.end())
542 valid.insert(file);
543 continue;
546 // the file has been added to the dir, start
547 // downloading it.
548 add_torrent(ses, handles, file, preferred_ratio, compact_mode
549 , save_path, true, torrent_upload_limit, torrent_download_limit);
550 valid.insert(file);
553 // remove the torrents that are no longer in the directory
555 for (handles_t::iterator i = handles.begin(); !handles.empty() && i != handles.end();)
557 if (i->first.empty() || valid.find(i->first) != valid.end())
559 ++i;
560 continue;
563 torrent_handle& h = i->second;
564 if (!h.is_valid())
566 handles.erase(i++);
567 continue;
570 h.auto_managed(false);
571 h.pause();
572 // the alert handler for save_resume_data_alert
573 // will save it to disk and remove the torrent
574 h.save_resume_data();
576 handles.erase(i++);
580 libtorrent::torrent_handle get_active_torrent(handles_t const& handles)
582 if (active_torrent >= handles.size()
583 || active_torrent < 0) return libtorrent::torrent_handle();
584 handles_t::const_iterator i = handles.begin();
585 std::advance(i, active_torrent);
586 return i->second;
589 void print_alert(libtorrent::alert const* a, std::ostream& os)
591 using namespace libtorrent;
593 #ifdef ANSI_TERMINAL_COLORS
594 if (a->category() & alert::error_notification)
596 os << esc("31");
598 else if (a->category() & (alert::peer_notification | alert::storage_notification))
600 os << esc("33");
602 #endif
603 os << "[" << time_now_string() << "] " << a->message();
604 #ifdef ANSI_TERMINAL_COLORS
605 os << esc("0");
606 #endif
608 if (g_log_file.good())
609 g_log_file << "[" << time_now_string() << "] " << a->message() << std::endl;
612 void handle_alert(libtorrent::session& ses, libtorrent::alert* a)
614 using namespace libtorrent;
616 if (torrent_finished_alert* p = dynamic_cast<torrent_finished_alert*>(a))
618 p->handle.set_max_connections(30);
620 // write resume data for the finished torrent
621 torrent_handle h = p->handle;
622 h.save_resume_data();
624 else if (save_resume_data_alert* p = dynamic_cast<save_resume_data_alert*>(a))
626 torrent_handle h = p->handle;
627 TORRENT_ASSERT(p->resume_data);
628 if (p->resume_data)
630 boost::filesystem::ofstream out(h.save_path() / (h.name() + ".fastresume")
631 , std::ios_base::binary);
632 out.unsetf(std::ios_base::skipws);
633 bencode(std::ostream_iterator<char>(out), *p->resume_data);
634 if (h.is_paused() && !h.is_auto_managed()) ses.remove_torrent(h);
639 static char const* state_str[] =
640 {"checking (q)", "checking", "connecting", "dl metadata"
641 , "downloading", "finished", "seeding", "allocating"};
643 int main(int ac, char* av[])
645 #if BOOST_VERSION < 103400
646 using boost::filesystem::no_check;
647 path::default_name_check(no_check);
648 #endif
650 int listen_port;
651 float preferred_ratio;
652 int download_limit;
653 int upload_limit;
654 int torrent_upload_limit;
655 int torrent_download_limit;
656 int upload_slots_limit;
657 int half_open_limit;
658 std::string save_path_str;
659 std::string log_level;
660 std::string log_file_name;
661 std::string ip_filter_file;
662 std::string allocation_mode;
663 std::string in_monitor_dir;
664 std::string bind_to_interface;
665 std::string proxy;
666 std::string proxy_login;
667 std::string proxy_type;
668 int poll_interval;
669 int wait_retry;
670 int bind_port_start = 0;
671 int bind_port_end = 0;
673 namespace po = boost::program_options;
677 po::options_description desc("supported options");
678 desc.add_options()
679 ("help,h", "display this help message")
680 ("port,p", po::value<int>(&listen_port)->default_value(6881)
681 , "set listening port")
682 ("ratio,r", po::value<float>(&preferred_ratio)->default_value(0)
683 , "set the preferred upload/download ratio. 0 means infinite. Values "
684 "smaller than 1 are clamped to 1.")
685 ("max-download-rate,d", po::value<int>(&download_limit)->default_value(0)
686 , "the maximum download rate given in kB/s. 0 means infinite.")
687 ("max-upload-rate,u", po::value<int>(&upload_limit)->default_value(0)
688 , "the maximum upload rate given in kB/s. 0 means infinite.")
689 ("max-torrent-upload-rate", po::value<int>(&torrent_upload_limit)->default_value(20)
690 , "the maximum upload rate for an individual torrent, given in kB/s. 0 means infinite.")
691 ("max-torrent-download-rate", po::value<int>(&torrent_download_limit)->default_value(0)
692 , "the maximum download rate for an individual torrent, given in kB/s. 0 means infinite.")
693 ("max-upload-slots", po::value<int>(&upload_slots_limit)->default_value(5)
694 , "the maximum number of upload slots. 0 means infinite.")
695 ("save-path,s", po::value<std::string>(&save_path_str)->default_value("./")
696 , "the path where the downloaded file/folder should be placed.")
697 ("log-level,l", po::value<std::string>(&log_level)->default_value("info")
698 , "sets the level at which events are logged [debug | info | warning | fatal].")
699 ("log-file,f", po::value<std::string>(&log_file_name)->default_value("")
700 , "sets a file to log all events to")
701 ("ip-filter,f", po::value<std::string>(&ip_filter_file)->default_value("")
702 , "sets the path to the ip-filter file used to block access from certain "
703 "ips. ")
704 ("allocation-mode,a", po::value<std::string>(&allocation_mode)->default_value("full")
705 , "sets mode used for allocating the downloaded files on disk. "
706 "Possible options are [full | compact]")
707 ("input-file,i", po::value<std::vector<std::string> >()
708 , "adds an input .torrent file. At least one is required. arguments "
709 "without any flag are implicitly an input file. To start a torrentless "
710 "download, use <info-hash>@<tracker-url> instead of specifying a file.")
711 ("monitor-dir,m", po::value<std::string>(&in_monitor_dir)
712 , "monitors the given directory, looking for .torrent files and "
713 "automatically starts downloading them. It will stop downloading "
714 "torrent files that are removed from the directory")
715 ("poll-interval,t", po::value<int>(&poll_interval)->default_value(2)
716 , "if a directory is being monitored, this is the interval (given "
717 "in seconds) between two refreshes of the directory listing")
718 ("wait-retry,w", po::value<int>(&wait_retry)->default_value(30)
719 , "if the download of a url seed failes, this is the interval (given "
720 "in seconds) to wait until the next retry")
721 ("half-open-limit,o", po::value<int>(&half_open_limit)->default_value(-1)
722 , "Sets the maximum number of simultaneous half-open tcp connections")
723 ("bind,b", po::value<std::string>(&bind_to_interface)->default_value("")
724 , "Sets the local interface to bind outbound and the listen "
725 "socket to")
726 ("proxy-server,x", po::value<std::string>(&proxy)->default_value("")
727 , "Sets the http proxy to be used for tracker and web seeds "
728 "connections. The string is expected to be on the form: "
729 "<hostname>:<port>. If no port is specified, 8080 is assumed")
730 ("proxy-login,n", po::value<std::string>(&proxy_login)->default_value("")
731 , "Sets the username and password used to authenticate with the http "
732 "proxy. The string should be given in the form: <username>:<password>")
733 ("proxy-type", po::value<std::string>(&proxy_type)->default_value("socks5")
734 , "Sets the type of proxy to use [socks5 | http] ")
735 ("bind-port-start", po::value<int>(&bind_port_start)->default_value(0)
736 , "The lower port number that outgoing connections will be bound to")
737 ("bind-port-end", po::value<int>(&bind_port_end)->default_value(0)
738 , "The upper port number that outgoing connections will be bound to")
741 po::positional_options_description p;
742 p.add("input-file", -1);
744 po::variables_map vm;
745 po::store(po::command_line_parser(ac, av).
746 options(desc).positional(p).run(), vm);
747 po::notify(vm);
749 // make sure the arguments stays within the usable limits
750 path monitor_dir(in_monitor_dir);
751 if (listen_port < 0 || listen_port > 65525) listen_port = 6881;
752 if (preferred_ratio != 0 && preferred_ratio < 1.f) preferred_ratio = 1.f;
753 upload_limit *= 1000;
754 torrent_upload_limit *= 1000;
755 torrent_download_limit *= 1000;
756 download_limit *= 1000;
757 if (download_limit <= 0) download_limit = -1;
758 if (upload_limit <= 0) upload_limit = -1;
759 if (torrent_upload_limit <= 0) torrent_upload_limit = -1;
760 if (torrent_download_limit <= 0) torrent_download_limit = -1;
761 if (poll_interval < 2) poll_interval = 2;
762 if (wait_retry < 0) wait_retry = 0;
763 if (half_open_limit < 1) half_open_limit = -1;
764 if (upload_slots_limit <= 0) upload_slots_limit = -1;
765 if (!monitor_dir.empty() && !exists(monitor_dir))
767 std::cerr << "The monitor directory doesn't exist: " << monitor_dir.string() << std::endl;
768 return 1;
771 if (vm.count("help")
772 || vm.count("input-file") + vm.count("monitor-dir") == 0)
774 std::cout << desc << "\n";
775 return 1;
778 if (!log_file_name.empty())
779 g_log_file.open(log_file_name.c_str());
781 bool compact_allocation_mode = (allocation_mode == "compact");
783 using namespace libtorrent;
785 std::vector<std::string> input;
786 if (vm.count("input-file") > 0)
787 input = vm["input-file"].as< std::vector<std::string> >();
789 session_settings settings;
790 proxy_settings ps;
792 if (!proxy.empty())
794 std::size_t i = proxy.find(':');
795 ps.hostname = proxy.substr(0, i);
796 if (i == std::string::npos) ps.port = 8080;
797 else ps.port = atoi(proxy.substr(i + 1).c_str());
798 if (proxy_type == "socks5")
799 ps.type = proxy_settings::socks5;
800 else
801 ps.type = proxy_settings::http;
803 if (!proxy_login.empty())
805 std::size_t i = proxy_login.find(':');
806 if (i == std::string::npos)
808 std::cerr << "Proxy login did not match the required format: "
809 << proxy_login << std::endl;
810 return 1;
812 ps.username = proxy_login.substr(0, i);
813 ps.password = proxy_login.substr(i + 1);
814 if (proxy_type == "socks5")
815 ps.type = proxy_settings::socks5_pw;
816 else
817 ps.type = proxy_settings::http_pw;
821 settings.user_agent = "client_test/" LIBTORRENT_VERSION;
822 settings.urlseed_wait_retry = wait_retry;
824 settings.outgoing_ports.first = bind_port_start;
825 settings.outgoing_ports.second = bind_port_end;
827 std::deque<std::string> events;
829 ptime next_dir_scan = time_now();
831 // the string is the filename of the .torrent file, but only if
832 // it was added through the directory monitor. It is used to
833 // be able to remove torrents that were added via the directory
834 // monitor when they're not in the directory anymore.
835 handles_t handles;
836 session ses;
837 #ifndef TORRENT_DISABLE_GEO_IP
838 ses.load_asnum_db("GeoIPASNum.dat");
839 ses.load_country_db("GeoIP.dat");
840 #endif
841 // UPnP port mapping
842 ses.start_upnp();
843 // NAT-PMP port mapping
844 ses.start_natpmp();
845 // Local service discovery (finds peers on the local network)
846 ses.start_lsd();
847 ses.add_extension(&create_metadata_plugin);
848 ses.add_extension(&create_ut_pex_plugin);
849 ses.add_extension(&create_ut_metadata_plugin);
850 ses.add_extension(&create_smart_ban_plugin);
852 ses.set_max_uploads(upload_slots_limit);
853 ses.set_max_half_open_connections(half_open_limit);
854 ses.set_download_rate_limit(download_limit);
855 ses.set_upload_rate_limit(upload_limit);
856 ses.listen_on(std::make_pair(listen_port, listen_port + 10)
857 , bind_to_interface.c_str());
858 ses.set_settings(settings);
859 ses.set_tracker_proxy(ps);
860 ses.set_peer_proxy(ps);
861 ses.set_web_seed_proxy(ps);
863 if (log_level == "debug")
864 ses.set_severity_level(alert::debug);
865 else if (log_level == "warning")
866 ses.set_severity_level(alert::warning);
867 else if (log_level == "fatal")
868 ses.set_severity_level(alert::fatal);
869 else
870 ses.set_severity_level(alert::info);
872 boost::filesystem::ifstream ses_state_file(".ses_state"
873 , std::ios_base::binary);
874 ses_state_file.unsetf(std::ios_base::skipws);
875 ses.load_state(bdecode(
876 std::istream_iterator<char>(ses_state_file)
877 , std::istream_iterator<char>()));
879 #ifndef TORRENT_DISABLE_DHT
880 settings.use_dht_as_fallback = false;
882 boost::filesystem::ifstream dht_state_file(".dht_state"
883 , std::ios_base::binary);
884 dht_state_file.unsetf(std::ios_base::skipws);
885 entry dht_state;
886 dht_state = bdecode(
887 std::istream_iterator<char>(dht_state_file)
888 , std::istream_iterator<char>());
889 ses.start_dht(dht_state);
890 ses.add_dht_router(std::make_pair(std::string("router.bittorrent.com")
891 , 6881));
892 ses.add_dht_router(std::make_pair(std::string("router.utorrent.com")
893 , 6881));
894 ses.add_dht_router(std::make_pair(std::string("router.bitcomet.com")
895 , 6881));
896 #endif
898 // look for ipfilter.dat
899 // poor man's parser
900 // reads emule ipfilter files.
901 // with the following format:
903 // <first-ip> - <last-ip> , <access> , <comment>
905 // first-ip is an ip address that defines the first
906 // address of the range
907 // last-ip is the last ip address in the range
908 // access is a number specifying the access control
909 // for this ip-range. Right now values > 127 = allowed
910 // and numbers <= 127 = blocked
911 // the rest of the line is ignored
913 // In the original spec ranges may not overlap, but
914 // here ranges may overlap, and it is the last added
915 // rule that has precedence for addresses that may fall
916 // into more than one range.
917 if (!ip_filter_file.empty())
919 std::ifstream in(ip_filter_file.c_str());
920 ip_filter filter;
921 while (in.good())
923 char line[300];
924 in.getline(line, 300);
925 int len = in.gcount();
926 if (len <= 0) continue;
927 if (line[0] == '#') continue;
928 int a, b, c, d;
929 char dummy;
930 std::stringstream ln(line);
931 ln >> a >> dummy >> b >> dummy >> c >> dummy >> d >> dummy;
932 address_v4 start((a << 24) + (b << 16) + (c << 8) + d);
933 ln >> a >> dummy >> b >> dummy >> c >> dummy >> d >> dummy;
934 address_v4 last((a << 24) + (b << 16) + (c << 8) + d);
935 int flags;
936 ln >> flags;
937 if (flags <= 127) flags = ip_filter::blocked;
938 else flags = 0;
939 if (ln.fail()) break;
940 filter.add_rule(start, last, flags);
942 ses.set_ip_filter(filter);
944 boost::filesystem::path save_path(save_path_str);
946 // load the torrents given on the commandline
947 boost::regex ex("([0-9A-Fa-f]{40})@(.+)");
948 for (std::vector<std::string>::const_iterator i = input.begin();
949 i != input.end(); ++i)
951 #ifndef BOOST_NO_EXCEPTIONS
954 #endif
955 // first see if this is a torrentless download
956 if (i->substr(0, 7) == "magnet:")
958 std::cout << "adding MANGET link: " << *i << std::endl;
959 torrent_handle h = add_magnet_uri(ses, *i, save_path
960 , compact_allocation_mode ? storage_mode_compact
961 : storage_mode_sparse);
963 handles.insert(std::make_pair(std::string(), h));
965 h.set_max_connections(50);
966 h.set_max_uploads(-1);
967 h.set_ratio(preferred_ratio);
968 h.set_upload_limit(torrent_upload_limit);
969 h.set_download_limit(torrent_download_limit);
970 continue;
972 boost::cmatch what;
973 if (boost::regex_match(i->c_str(), what, ex))
975 sha1_hash info_hash = boost::lexical_cast<sha1_hash>(what[1]);
977 add_torrent_params p;
978 p.name = std::string(what[2]).c_str();
979 p.info_hash = info_hash;
980 p.save_path = save_path;
981 p.storage_mode = compact_allocation_mode ? storage_mode_compact : storage_mode_sparse;
982 p.paused = true;
983 p.duplicate_is_error = false;
984 p.auto_managed = true;
985 torrent_handle h = ses.add_torrent(p);
987 handles.insert(std::make_pair(std::string(), h));
989 h.set_max_connections(50);
990 h.set_max_uploads(-1);
991 h.set_ratio(preferred_ratio);
992 h.set_upload_limit(torrent_upload_limit);
993 h.set_download_limit(torrent_download_limit);
994 continue;
996 // if it's a torrent file, open it as usual
997 add_torrent(ses, handles, i->c_str(), preferred_ratio
998 , compact_allocation_mode, save_path, false
999 , torrent_upload_limit, torrent_download_limit);
1000 #ifndef BOOST_NO_EXCEPTIONS
1002 catch (std::exception& e)
1004 std::cout << e.what() << "\n";
1006 #endif
1009 // main loop
1010 std::vector<peer_info> peers;
1011 std::vector<partial_piece_info> queue;
1013 for (;;)
1015 char c;
1016 while (sleep_and_input(&c))
1018 if (c == 27)
1020 // escape code, read another character
1021 #ifdef _WIN32
1022 c = _getch();
1023 #else
1024 c = getc(stdin);
1025 #endif
1026 if (c != '[') break;
1027 #ifdef _WIN32
1028 c = _getch();
1029 #else
1030 c = getc(stdin);
1031 #endif
1032 if (c == 65)
1034 // arrow up
1035 --active_torrent;
1036 if (active_torrent < 0) active_torrent = 0;
1038 else if (c == 66)
1040 // arrow down
1041 ++active_torrent;
1042 if (active_torrent >= handles.size()) active_torrent = handles.size() - 1;
1046 if (c == ' ')
1048 if (ses.is_paused()) ses.resume();
1049 else ses.pause();
1052 if (c == 'm')
1054 std::cout << "saving peers for torrents" << std::endl;
1056 std::vector<peer_list_entry> peers;
1057 for (handles_t::iterator i = handles.begin();
1058 i != handles.end(); ++i)
1060 i->second.get_full_peer_list(peers);
1061 std::ofstream f(("peers_" + i->second.name()).c_str());
1062 for (std::vector<peer_list_entry>::iterator k = peers.begin()
1063 , end(peers.end()); k != end; ++k)
1065 f << k->ip.address()
1066 #ifndef TORRENT_DISABLE_GEO_IP
1067 << "\t" << ses.as_for_ip(k->ip.address())
1068 #endif
1069 << std::endl;
1074 if (c == 'q')
1076 // keep track of the number of resume data
1077 // alerts to wait for
1078 int num_resume_data = 0;
1079 for (handles_t::iterator i = handles.begin();
1080 i != handles.end(); ++i)
1082 torrent_handle& h = i->second;
1083 if (!h.is_valid() || !h.has_metadata()) continue;
1085 // pause
1086 std::cout << "pausing " << h.name() << std::endl;
1087 h.pause();
1088 // save_resume_data will generate an alert when it's done
1089 h.save_resume_data();
1090 ++num_resume_data;
1092 std::cout << "waiting for resume data" << std::endl;
1094 while (num_resume_data > 0)
1096 alert const* a = ses.wait_for_alert(seconds(30));
1097 if (a == 0)
1099 std::cout << " aborting with " << num_resume_data << " outstanding "
1100 "torrents to save resume data for" << std::endl;
1101 break;
1104 std::auto_ptr<alert> holder = ses.pop_alert();
1106 ::print_alert(holder.get(), std::cout);
1107 std::cout << std::endl;
1109 save_resume_data_alert const* rd = dynamic_cast<save_resume_data_alert const*>(a);
1110 if (!rd) continue;
1111 --num_resume_data;
1113 if (!rd->resume_data) continue;
1115 torrent_handle h = rd->handle;
1116 boost::filesystem::ofstream out(h.save_path()
1117 / (h.get_torrent_info().name() + ".fastresume"), std::ios_base::binary);
1118 out.unsetf(std::ios_base::skipws);
1119 bencode(std::ostream_iterator<char>(out), *rd->resume_data);
1121 break;
1124 if (c == 'j')
1126 torrent_handle h = get_active_torrent(handles);
1127 if (h.is_valid()) h.force_recheck();
1130 if (c == 'r')
1132 torrent_handle h = get_active_torrent(handles);
1133 if (h.is_valid()) h.force_reannounce();
1136 if (c == 's')
1138 torrent_handle h = get_active_torrent(handles);
1139 if (h.is_valid()) h.set_sequential_download(!h.is_sequential_download());
1142 if (c == 'p')
1144 torrent_handle h = get_active_torrent(handles);
1145 if (h.is_valid())
1147 if (!h.is_auto_managed() && h.is_paused())
1149 h.auto_managed(true);
1151 else
1153 h.auto_managed(false);
1154 h.pause();
1159 if (c == 'c')
1161 torrent_handle h = get_active_torrent(handles);
1162 if (h.is_valid()) h.clear_error();
1165 // toggle displays
1166 if (c == 'i') print_peers = !print_peers;
1167 if (c == 'l') print_log = !print_log;
1168 if (c == 'd') print_downloads = !print_downloads;
1169 if (c == 'f') print_file_progress = !print_file_progress;
1170 if (c == 'a') print_piece_bar = !print_piece_bar;
1171 // toggle columns
1172 if (c == '1') print_ip = !print_ip;
1173 if (c == '2') print_as = !print_as;
1174 if (c == '3') print_timers = !print_timers;
1175 if (c == '4') print_block = !print_block;
1176 if (c == '5') print_peer_rate = !print_peer_rate;
1177 if (c == '6') print_fails = !print_fails;
1178 if (c == '7') print_send_bufs = !print_send_bufs;
1180 if (c == 'q') break;
1182 int terminal_width = 80;
1184 #ifndef _WIN32
1186 winsize size;
1187 ioctl(STDOUT_FILENO, TIOCGWINSZ, (char*)&size);
1188 terminal_width = size.ws_col;
1190 #endif
1192 // loop through the alert queue to see if anything has happened.
1193 std::auto_ptr<alert> a;
1194 a = ses.pop_alert();
1195 std::string now = time_now_string();
1196 while (a.get())
1198 std::stringstream event_string;
1200 ::print_alert(a.get(), event_string);
1201 ::handle_alert(ses, a.get());
1203 events.push_back(event_string.str());
1204 if (events.size() >= 20) events.pop_front();
1206 a = ses.pop_alert();
1209 session_status sess_stat = ses.status();
1211 std::stringstream out;
1212 out << "[q] quit [i] toggle peers [d] toggle downloading pieces [p] toggle paused "
1213 "[a] toggle piece bar [s] toggle download sequential [f] toggle files "
1214 "[j] force recheck [space] toggle session pause [c] clear error\n"
1215 "[1] toggle IP [2] toggle AS [3] toggle timers [4] toggle block progress "
1216 "[5] toggle peer rate [6] toggle failures [7] toggle send buffers\n";
1218 int torrent_index = 0;
1219 torrent_handle active_handle;
1220 for (handles_t::iterator i = handles.begin();
1221 i != handles.end(); ++torrent_index)
1223 torrent_handle& h = i->second;
1224 if (!h.is_valid())
1226 handles.erase(i++);
1227 continue;
1229 else
1231 ++i;
1234 #ifdef ANSI_TERMINAL_COLORS
1235 char const* term = "\x1b[0m";
1236 #else
1237 char const* term = "";
1238 #endif
1239 if (active_torrent == torrent_index)
1241 term = "\x1b[0m\x1b[7m";
1242 out << esc("7") << "*";
1244 else
1246 out << " ";
1249 int queue_pos = h.queue_position();
1250 if (queue_pos == -1) out << "- ";
1251 else out << std::setw(3) << queue_pos;
1253 if (h.is_paused()) out << esc("34");
1254 else out << esc("37");
1255 out << std::setw(40) << std::setiosflags(std::ios::left);
1257 std::string name = h.name();
1258 if (name.size() > 40) name.resize(40);
1259 out << name;
1261 out << term << " ";
1263 torrent_status s = h.status();
1265 bool paused = h.is_paused();
1266 bool auto_managed = h.is_auto_managed();
1267 out << std::setw(13) << std::setiosflags(std::ios::left);
1268 if (!s.error.empty())
1270 out << esc("31") << "error " << s.error;
1271 out << esc("0") << std::endl;
1272 continue;
1275 if (paused && !auto_managed) out << "paused";
1276 else if (paused && auto_managed) out << "queued";
1277 else out << state_str[s.state];
1279 int seeds = 0;
1280 int downloaders = 0;
1282 if (s.num_complete >= 0) seeds = s.num_complete;
1283 else seeds = s.list_seeds;
1285 if (s.num_incomplete >= 0) downloaders = s.num_incomplete;
1286 else downloaders = s.list_peers - s.list_seeds;
1288 out << "download: " << "(" << esc("32") << add_suffix(s.total_download) << term << ") "
1289 "upload: " << esc("31") << (s.upload_rate > 0 ? add_suffix(s.upload_rate) + "/s ": " ") << term
1290 << "(" << esc("31") << add_suffix(s.total_upload) << term << ") "
1291 << "swarm: " << to_string(downloaders, 4) << ":" << to_string(seeds, 4)
1292 << " bw queue: (" << s.up_bandwidth_queue << " | " << s.down_bandwidth_queue << ") "
1293 "all-time (Rx: " << esc("32") << add_suffix(s.all_time_download) << term
1294 << " Tx: " << esc("31") << add_suffix(s.all_time_upload) << term << ") "
1295 << std::hex << s.seed_rank << std::dec << " "
1296 << s.last_scrape << "\n" << esc("0");
1298 if (s.state != torrent_status::seeding)
1300 char const* progress_bar_color = "33"; // yellow
1301 if (s.state == torrent_status::checking_files
1302 || s.state == torrent_status::downloading_metadata)
1304 progress_bar_color = "35"; // magenta
1306 else if (s.current_tracker.empty())
1308 progress_bar_color = "31"; // red
1310 else if (sess_stat.has_incoming_connections)
1312 progress_bar_color = "32"; // green
1314 out << " progress: " << esc("32") << s.total_done << esc("0") << " Bytes ";
1315 out.precision(4);
1316 out.width(5);
1317 out.fill(' ');
1318 out << (s.progress*100) << "% ";
1319 out << progress_bar(s.progress, terminal_width - 37, progress_bar_color) << "\n";
1320 if (print_piece_bar && s.progress < 1.f)
1321 out << " " << piece_bar(s.pieces, terminal_width - 5) << "\n";
1322 out << " peers: " << esc("37") << s.num_peers << esc("0") << " (" << esc("37") << s.connect_candidates << esc("0") << ") "
1323 << "seeds: " << esc("37") << s.num_seeds << esc("0") << " "
1324 << "distributed copies: " << esc("37") << s.distributed_copies << esc("0")
1325 // << " magnet-link: " << make_magnet_uri(h) << "\n"
1326 << " download: " << esc("32") << (s.download_rate > 0 ? add_suffix(s.download_rate) + "/s ": " ") << esc("0");
1327 boost::posix_time::time_duration t = s.next_announce;
1328 out << " next announce: " << esc("37")
1329 << to_string(t.hours(), 2) << ":"
1330 << to_string(t.minutes(), 2) << ":"
1331 << to_string(t.seconds(), 2) << esc("0") << " ";
1332 out << "tracker: " << esc("36") << s.current_tracker << esc("0") << "\n";
1335 if (torrent_index != active_torrent) continue;
1336 active_handle = h;
1339 cache_status cs = ses.get_cache_status();
1340 if (cs.blocks_read < 1) cs.blocks_read = 1;
1341 if (cs.blocks_written < 1) cs.blocks_written = 1;
1343 out << "==== conns: " << sess_stat.num_peers
1344 << " down: " << esc("32") << add_suffix(sess_stat.download_rate) << "/s" << esc("0")
1345 << " (" << esc("32") << add_suffix(sess_stat.total_download) << esc("0") << ") "
1346 " up: " << esc("31") << add_suffix(sess_stat.upload_rate) << "/s " << esc("0")
1347 << " (" << esc("31") << add_suffix(sess_stat.total_upload) << esc("0") << ")"
1348 " waste: " << add_suffix(sess_stat.total_redundant_bytes)
1349 << " fail: " << add_suffix(sess_stat.total_failed_bytes)
1350 << " unchoked: " << sess_stat.num_unchoked << " / " << sess_stat.allowed_upload_slots
1351 << " bw queues: (" << sess_stat.up_bandwidth_queue
1352 << " | " << sess_stat.down_bandwidth_queue << ") "
1353 " write cache hits: " << ((cs.blocks_written - cs.writes) * 100 / cs.blocks_written) << "% "
1354 " read cache hits: " << (cs.blocks_read_hit * 100 / cs.blocks_read) << "% "
1355 " cache size: " << add_suffix(cs.cache_size * 16 * 1024)
1356 << " (" << add_suffix(cs.read_cache_size * 16 * 1024) << ")"
1357 " ====" << std::endl;
1359 if (active_handle.is_valid())
1361 torrent_handle h = active_handle;
1362 torrent_status s = h.status();
1364 if ((print_downloads && s.state != torrent_status::seeding)
1365 || print_peers)
1366 h.get_peer_info(peers);
1368 out << "====== " << h.name() << " ======" << std::endl;
1370 if (print_peers && !peers.empty())
1371 print_peer_info(out, peers);
1373 if (print_downloads)
1375 h.get_download_queue(queue);
1376 std::sort(queue.begin(), queue.end(), bind(&partial_piece_info::piece_index, _1)
1377 < bind(&partial_piece_info::piece_index, _2));
1379 std::vector<cached_piece_info> pieces;
1380 ses.get_cache_info(h.info_hash(), pieces);
1382 for (std::vector<partial_piece_info>::iterator i = queue.begin();
1383 i != queue.end(); ++i)
1385 cached_piece_info* cp = 0;
1386 std::vector<cached_piece_info>::iterator cpi = std::find_if(pieces.begin(), pieces.end()
1387 , bind(&cached_piece_info::piece, _1) == i->piece_index);
1388 if (cpi != pieces.end()) cp = &*cpi;
1390 out << to_string(i->piece_index, 4) << ": [";
1391 for (int j = 0; j < i->blocks_in_piece; ++j)
1393 int index = peer_index(i->blocks[j].peer, peers);
1394 char str[] = "+";
1395 if (index >= 0)
1396 str[0] = (index < 10)?'0' + index:'A' + index - 10;
1398 #ifdef ANSI_TERMINAL_COLORS
1399 if (cp && cp->blocks[j]) out << esc("36;7") << str << esc("0");
1400 else if (i->blocks[j].bytes_progress > 0
1401 && i->blocks[j].state == block_info::requested)
1403 if (i->blocks[j].num_peers > 1)
1404 out << esc("1;7");
1405 else
1406 out << esc("33;7");
1407 out << to_string(i->blocks[j].bytes_progress / float(i->blocks[j].block_size) * 10, 1) << esc("0");
1409 else if (i->blocks[j].state == block_info::finished) out << esc("32;7") << str << esc("0");
1410 else if (i->blocks[j].state == block_info::writing) out << esc("35;7") << str << esc("0");
1411 else if (i->blocks[j].state == block_info::requested) out << str;
1412 else out << " ";
1413 #else
1414 if (cp && cp->blocks[j]) out << "c";
1415 else if (i->blocks[j].state == block_info::finished) out << "#";
1416 else if (i->blocks[j].state == block_info::writing) out << "+";
1417 else if (i->blocks[j].state == block_info::requested) out << str;
1418 else out << " ";
1419 #endif
1421 char const* piece_state[4] = {"", "slow", "medium", "fast"};
1422 out << "] " << piece_state[i->piece_state];
1423 if (cp) out << (i->piece_state > 0?" | ":"") << "cache age: " << (total_milliseconds(time_now() - cp->last_use) / 1000.f);
1424 out << "\n";
1427 for (std::vector<cached_piece_info>::iterator i = pieces.begin()
1428 , end(pieces.end()); i != end; ++i)
1430 if (i->kind != cached_piece_info::read_cache) continue;
1431 out << to_string(i->piece, 4) << ": [";
1432 for (std::vector<bool>::iterator k = i->blocks.begin()
1433 , end(i->blocks.end()); k != end; ++k)
1435 #ifdef ANSI_TERMINAL_COLORS
1436 if (*k) out << esc("33;7") << " " << esc("0");
1437 else out << " ";
1438 #else
1439 if (*k) out << "#";
1440 else out << " ";
1441 #endif
1443 out << "] " << "cache age: "
1444 << (total_milliseconds(time_now() - i->last_use) / 1000.f)
1445 << "\n";
1447 out << "___________________________________\n";
1450 if (print_file_progress
1451 && s.state != torrent_status::seeding
1452 && h.has_metadata())
1454 std::vector<size_type> file_progress;
1455 h.file_progress(file_progress);
1456 torrent_info const& info = h.get_torrent_info();
1457 for (int i = 0; i < info.num_files(); ++i)
1459 float progress = info.file_at(i).size > 0
1460 ?float(file_progress[i]) / info.file_at(i).size:1;
1461 if (file_progress[i] == info.file_at(i).size)
1462 out << progress_bar(1.f, 100, "32");
1463 else
1464 out << progress_bar(progress, 100, "33");
1465 out << " " << to_string(progress * 100.f, 5) << "% "
1466 << add_suffix(file_progress[i]) << " "
1467 << info.file_at(i).path.leaf() << "\n";
1470 out << "___________________________________\n";
1475 if (print_log)
1477 for (std::deque<std::string>::iterator i = events.begin();
1478 i != events.end(); ++i)
1480 out << "\n" << *i;
1484 clear_home();
1485 puts(out.str().c_str());
1487 if (!monitor_dir.empty()
1488 && next_dir_scan < time_now())
1490 scan_dir(monitor_dir, ses, handles, preferred_ratio
1491 , compact_allocation_mode, save_path, torrent_upload_limit
1492 , torrent_download_limit);
1493 next_dir_scan = time_now() + seconds(poll_interval);
1497 std::cout << "saving session state" << std::endl;
1499 entry session_state = ses.state();
1500 boost::filesystem::ofstream out(".ses_state"
1501 , std::ios_base::binary);
1502 out.unsetf(std::ios_base::skipws);
1503 bencode(std::ostream_iterator<char>(out), session_state);
1506 #ifndef TORRENT_DISABLE_DHT
1507 std::cout << "saving DHT state" << std::endl;
1508 dht_state = ses.dht_state();
1509 boost::filesystem::ofstream out(".dht_state"
1510 , std::ios_base::binary);
1511 out.unsetf(std::ios_base::skipws);
1512 bencode(std::ostream_iterator<char>(out), dht_state);
1513 #endif
1514 std::cout << "closing session" << std::endl;
1516 #ifndef BOOST_NO_EXCEPTIONS
1517 catch (std::exception& e)
1519 std::cout << e.what() << "\n";
1521 #endif
1523 return 0;