Changeset 2738
- Timestamp:
- 03/02/06 00:41:31 (3 years ago)
- Files:
-
- hydranode/hncore/ed2k/clientlist.cpp (modified) (1 diff)
- hydranode/hncore/ed2k/ed2ksearch.cpp (modified) (2 diffs)
- hydranode/hncore/ed2k/ed2ksearch.h (modified) (1 diff)
- hydranode/hncore/ed2k/opcodes.h (modified) (1 diff)
- hydranode/hncore/ed2k/packets.cpp (modified) (3 diffs)
- hydranode/hncore/ed2k/packets.h (modified) (2 diffs)
- hydranode/hncore/ed2k/server.cpp (modified) (3 diffs)
- hydranode/hncore/ed2k/server.h (modified) (5 diffs)
- hydranode/hncore/ed2k/serverlist.cpp (modified) (7 diffs)
- hydranode/hncore/ed2k/serverlist.h (modified) (3 diffs)
- hydranode/hncore/hnsh/shellcommands.cpp (modified) (14 diffs)
- hydranode/hncore/hnsh/shellcommands.h (modified) (3 diffs)
- hydranode/hncore/search.cpp (modified) (5 diffs)
- hydranode/hncore/search.h (modified) (10 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
hydranode/hncore/ed2k/clientlist.cpp
r2706 r2738 813 813 ServerList::instance().handleGlobStatRes(packet, from); 814 814 break; 815 case OP_GLOBSEARCHRES: 816 ServerList::instance().handleGlobSearchRes( 817 packet, from 818 ); 819 break; 815 820 default: 816 821 logDebug( hydranode/hncore/ed2k/ed2ksearch.cpp
r2613 r2738 29 29 #include <hncore/fileslist.h> 30 30 #include <hncore/sharedfile.h> 31 #include <boost/lexical_cast.hpp> 31 32 32 33 namespace Donkey { … … 75 76 } 76 77 78 std::string ED2KSearchResult::identifier() const { 79 return m_hash.toString() + boost::lexical_cast<std::string>(getSize()); 80 } 81 77 82 } // end namespace Donkey hydranode/hncore/ed2k/ed2ksearch.h
r2613 r2738 52 52 */ 53 53 virtual void download(); 54 55 /** 56 * @returns unique identifier (hash + size combo) for this result 57 */ 58 virtual std::string identifier() const; 54 59 private: 55 60 Hash<ED2KHash> m_hash; hydranode/hncore/ed2k/opcodes.h
r2617 r2738 206 206 OP_GLOBGETSOURCES2 = 0x94, //!< cnt*[<hash>hash<u32>size] 207 207 OP_GLOBFOUNDSOURCES = 0x9b, //!< <hash>hash<u8>cnt*[<u32>id<u16>port] 208 208 OP_GLOBSEARCHREQ = 0x98, //!< search tree 209 OP_GLOBSEARCHRES = 0x99, //!< results 209 210 OP_GLOBSTATREQ = 0x96, //!< <u32>challenge 210 211 //! <u32>challenge<u32>users<u32>files<u32>maxusers<u32>softlimit hydranode/hncore/ed2k/packets.cpp
r2667 r2738 397 397 Utils::StopWatch stopwatch; 398 398 uint32_t cnt2 = count; // used for later debug msg 399 399 400 while (count-- && i) { 400 Hash<ED2KHash> h(Utils::getVal<std::string>(i, 16)); 401 (void)Utils::getVal<uint32_t>(i); // id - ignored 402 (void)Utils::getVal<uint16_t>(i); // port - ignored 403 uint32_t tagCount = Utils::getVal<uint32_t>(i); 404 std::string name; 405 uint32_t size = 0; 406 uint32_t sources = 0; 407 uint32_t completesrc = 0; 408 std::string codec; 409 uint32_t bitrate = 0; 410 uint32_t len = 0; 411 uint32_t lastSeen = 0; 412 uint8_t rating = 0; 413 while (tagCount-- && i) { 414 Tag t(i); 415 switch (t.getOpcode()) { 416 case CT_FILENAME: 417 name = t.getStr(); 418 break; 419 case CT_FILESIZE: 420 size = t.getInt(); 421 break; 422 case CT_SOURCES: 423 sources = t.getInt(); 424 break; 425 case CT_COMPLSRC: 426 completesrc = t.getInt(); 427 break; 428 case CT_MEDIA_CODEC: 429 codec = t.getStr(); 430 break; 431 case CT_MEDIA_BITRATE: 432 bitrate = t.getInt(); 433 break; 434 case CT_MEDIA_LENGTH: 435 len = t.getInt(); 436 break; 437 case CT_LASTSEENCOMPL: 438 lastSeen = t.getInt(); 439 break; 440 case CT_FILERATING: 441 rating = t.getInt(); 442 break; 443 default: 444 if (t.getName() == "bitrate") { 445 bitrate = t.getInt(); 446 } else if (t.getName() == "length") { 447 /** 448 * TODO: Convert str-tag LEN 449 * into integer-len 450 */ 451 } 452 warnUnHandled("SearchResult", t); 453 break; 454 } 455 } 456 boost::shared_ptr<ED2KSearchResult> f; 457 458 // Note - ignoring ID/Port values. 459 f.reset(new ED2KSearchResult(h, name, size)); 460 f->addSources(sources); 461 f->addComplete(completesrc); 462 f->addRating(rating); 463 if (codec.size() || bitrate || len) { 464 f->getStrd().reset(new StreamData(codec, bitrate, len)); 465 } 466 467 m_results.push_back(f); 401 readResult(i); 468 402 } 469 403 … … 474 408 ) % cnt2 % stopwatch 475 409 ); 410 } 411 412 // protected default constructor for usage by GlobSearchRes class 413 SearchResult::SearchResult() {} 414 415 void SearchResult::readResult(std::istream &i) { 416 Hash<ED2KHash> h(Utils::getVal<std::string>(i, 16)); 417 (void)Utils::getVal<uint32_t>(i); // id - ignored 418 (void)Utils::getVal<uint16_t>(i); // port - ignored 419 uint32_t tagCount = Utils::getVal<uint32_t>(i); 420 std::string name; 421 uint32_t size = 0; 422 uint32_t sources = 0; 423 uint32_t completesrc = 0; 424 std::string codec; 425 uint32_t bitrate = 0; 426 uint32_t len = 0; 427 uint32_t lastSeen = 0; 428 uint8_t rating = 0; 429 while (tagCount-- && i) { 430 Tag t(i); 431 switch (t.getOpcode()) { 432 case CT_FILENAME: 433 name = t.getStr(); 434 break; 435 case CT_FILESIZE: 436 size = t.getInt(); 437 break; 438 case CT_SOURCES: 439 sources = t.getInt(); 440 break; 441 case CT_COMPLSRC: 442 completesrc = t.getInt(); 443 break; 444 case CT_MEDIA_CODEC: 445 codec = t.getStr(); 446 break; 447 case CT_MEDIA_BITRATE: 448 bitrate = t.getInt(); 449 break; 450 case CT_MEDIA_LENGTH: 451 len = t.getInt(); 452 break; 453 case CT_LASTSEENCOMPL: 454 lastSeen = t.getInt(); 455 break; 456 case CT_FILERATING: 457 rating = t.getInt(); 458 break; 459 default: 460 if (t.getName() == "bitrate") { 461 bitrate = t.getInt(); 462 } else if (t.getName() == "length") { 463 /** 464 * TODO: Convert str-tag LEN 465 * into integer-len 466 */ 467 } 468 warnUnHandled("SearchResult", t); 469 break; 470 } 471 } 472 boost::shared_ptr<ED2KSearchResult> f; 473 474 // Note - ignoring ID/Port values. 475 f.reset(new ED2KSearchResult(h, name, size)); 476 f->addSources(sources); 477 f->addComplete(completesrc); 478 f->addRating(rating); 479 if (codec.size() || bitrate || len) { 480 f->getStrd().reset(new StreamData(codec, bitrate, len)); 481 } 482 483 m_results.push_back(f); 476 484 } 477 485 … … 579 587 } catch (std::exception &) {} // safe to ignore 580 588 } 589 590 // GlobSearchReq class 591 // ------------------- 592 GlobSearchReq::GlobSearchReq(SearchPtr data, uint8_t proto) 593 : Search(data, proto) {} 594 595 GlobSearchReq::operator std::string() { 596 std::string tmp(*static_cast<Search*>(this)); 597 tmp.erase(1, 4); // no size field in udp packets 598 tmp[1] = OP_GLOBSEARCHREQ; 599 return tmp; // makePacket is called by Search class already 600 } 601 602 // GlobSearchRes class 603 // ------------------- 604 GlobSearchRes::GlobSearchRes(std::istream &i) : SearchResult() { 605 while (i) { 606 readResult(i); 607 if (i.peek() == PR_ED2K) { 608 (void)Utils::getVal<uint8_t>(i); 609 } else { 610 i.seekg(-1, std::ios::cur); 611 break; 612 } 613 if (i.peek() == OP_GLOBSEARCHRES) { 614 (void)Utils::getVal<uint8_t>(i); 615 } else { 616 i.seekg(-2, std::ios::cur); 617 break; 618 } 619 } 620 if (!i.eof()) { 621 logDebug(boost::format( 622 "Extra bytes at the end of GlobSearchRes packet: %s" 623 ) % Utils::hexDump( 624 dynamic_cast<std::istringstream&>(i).str().substr( 625 i.tellg() 626 ) 627 )); 628 } 629 } 630 581 631 582 632 /*************************/ hydranode/hncore/ed2k/packets.h
r2613 r2738 323 323 return m_results.at(num); 324 324 } 325 protected: 326 SearchResult(); 327 328 //! Reads one result from stream 329 void readResult(std::istream &i); 325 330 private: 326 331 std::vector<boost::shared_ptr<ED2KSearchResult> > m_results; … … 462 467 uint32_t m_udpFlags; //!< UDP flags 463 468 uint32_t m_lowIdUsers; //!< Number of online LowId users 469 }; 470 471 /** 472 * Global search request, sent via UDP socket to servers; format is 473 * same as with Search packet, except for different opcode. 474 * 475 * Usage: Client -> Server (UDP) 476 */ 477 class GlobSearchReq : public Search { 478 public: 479 GlobSearchReq(SearchPtr data, uint8_t proto = PR_ED2K); 480 operator std::string(); 481 }; 482 483 /** 484 * Response to GlobSearchReq packet, sent by servers via UDP; format is 485 * same as SearchResult packet, except for different opcode. 486 */ 487 class GlobSearchRes : public SearchResult { 488 public: 489 GlobSearchRes(std::istream &i); 464 490 }; 465 491 hydranode/hncore/ed2k/server.cpp
r2613 r2738 38 38 m_addr(addr), m_failedCount(), m_preference(), m_ping(), m_lastPing(), 39 39 m_maxUsers(), m_softLimit(), m_hardLimit(), m_udpFlags(), m_users(), m_files(), 40 m_lowIdUsers(), m_lastUdpQuery(), m_pingInProgress(), m_challenge() { 40 m_lowIdUsers(), m_lastUdpQuery(), m_pingInProgress(), m_challenge(), 41 m_globSearch() { 41 42 // Sanity checking 42 43 CHECK_THROW_MSG(addr.getAddr(), "Server IP may not be null."); … … 44 45 45 46 setName(m_addr.getStr()); 47 48 // mark all servers udp-search capable initially, so when starting 49 // with empty server list, it's still possible to perform searches 50 m_udpFlags &= FL_GETFILES; 46 51 } 47 52 … … 53 58 m_failedCount(), m_preference(), m_ping(), m_lastPing(), m_maxUsers(), 54 59 m_softLimit(), m_hardLimit(), m_udpFlags(), m_users(), m_files(), 55 m_lowIdUsers(), m_lastUdpQuery(), m_pingInProgress(), m_challenge() { 60 m_lowIdUsers(), m_lastUdpQuery(), m_pingInProgress(), m_challenge(), 61 m_globSearch() { 56 62 m_addr.setAddr(Utils::getVal<uint32_t>(i)); 57 63 m_addr.setPort(Utils::getVal<uint16_t>(i)); hydranode/hncore/ed2k/server.h
r2613 r2738 28 28 #include <hnbase/ipv4addr.h> 29 29 #include <hnbase/object.h> 30 #include <hncore/fwd.h> 30 31 #include <hncore/ed2k/fwd.h> 31 32 … … 84 85 bool pingInProgress() const { return m_pingInProgress; } 85 86 uint32_t getChallenge() const { return m_challenge; } 87 SearchPtr currentSearch() const { return m_globSearch; } 86 88 87 89 //! ED2K Server UDP port is always TCP + 4 … … 89 91 bool supportsGetSources() const { return m_udpFlags & FL_GETSOURCES; } 90 92 bool supportsGetFiles() const { return m_udpFlags & FL_GETFILES; } 93 bool supportsUdpSearch() const { return m_udpFlags & FL_GETFILES; } 91 94 //@} 92 95 … … 116 119 117 120 void setLastUdpQuery(uint64_t time) { m_lastUdpQuery = time; } 118 void setPingInProgress(bool v) { m_pingInProgress = v; } 119 void setChallenge(uint32_t c) { m_challenge = c; } 121 void setPingInProgress(bool v) { m_pingInProgress = v; } 122 void setChallenge(uint32_t c) { m_challenge = c; } 123 void setCurrentSearch(SearchPtr p) { m_globSearch = p; } 120 124 //@} 121 125 private: … … 158 162 std::vector<uint16_t> m_auxPorts; //!< Auxiliary ports 159 163 160 uint64_t m_lastUdpQuery; //!< Time of last UDP query 161 bool m_pingInProgress; //!< UDP Ping is in progress 162 uint32_t m_challenge; //!< Last UDP query challenge 164 uint64_t m_lastUdpQuery; //!< Time of last UDP query 165 bool m_pingInProgress; //!< UDP Ping is in progress 166 uint32_t m_challenge; //!< Last UDP query challenge 167 SearchPtr m_globSearch; //!< Current search, if any 163 168 //@} 164 169 hydranode/hncore/ed2k/serverlist.cpp
r2707 r2738 72 72 }; 73 73 74 //! Server UDP Addr extractor functor 75 struct ServerUAddrExtractor { 76 typedef IPV4Address result_type; 77 result_type operator()(const Server *const s) const { 78 return IPV4Address(s->getAddr().getIp(), s->getUdpPort()); 79 } 80 }; 81 74 82 //! last udp query time extractor functor 75 83 struct QueryTimeExtractor { … … 87 95 boost::multi_index::ordered_non_unique<ServerAddrExtractor>, 88 96 boost::multi_index::ordered_non_unique<ServerNameExtractor>, 89 boost::multi_index::ordered_non_unique<QueryTimeExtractor> 97 boost::multi_index::ordered_non_unique<QueryTimeExtractor>, 98 boost::multi_index::ordered_non_unique<ServerUAddrExtractor> 90 99 > {}; 91 100 struct MIServerList : boost::multi_index_container< … … 99 108 QTIter(const T &t) : MIServerList::nth_index<3>::type::iterator(t) {} 100 109 }; 110 typedef MIServerList::nth_index<4>::type::iterator UAddrIter; 101 111 102 112 } // namespace Detail … … 455 465 logTrace(TRACE, "[ed2k] ServerList::performSearch"); 456 466 457 if (m_status != ST_CONNECTED || !m_serverSocket) { 458 logError( 459 "Unable to perform a search at this time: not " 460 "connected to a server." 461 ); 462 if (m_status != ST_CONNECTING) { 463 connect(); 464 } 465 } else { 467 // local server search only if we'r currently connected 468 if (m_serverSocket && m_status == ST_CONNECTED) { 466 469 *m_serverSocket << ED2KPacket::Search(search); 467 470 m_curSearch = search; 468 471 } 472 473 // global search 474 std::vector<Server*> tmp; 475 std::copy(m_list->begin(), m_list->end(), std::back_inserter(tmp)); 476 std::random_shuffle(tmp.begin(), tmp.end()); 477 boost::shared_ptr<std::list<Server*> > list(new std::list<Server*>); 478 std::copy(tmp.begin(), tmp.end(), std::back_inserter(*list)); 479 Utils::timedCallback( 480 boost::bind(&ServerList::doGlobSearch, this, search, list), 0 481 ); 482 std::string keywords; 483 for (uint32_t i = 0; i < search->getTermCount(); ++i) { 484 keywords += search->getTerm(i); 485 if (i + 1 < search->getTermCount()) { 486 keywords += " "; 487 } 488 } 489 logMsg( 490 boost::format( 491 "eDonkey2000: Search started with keywords '%s'." 492 ) % keywords 493 ); 469 494 } 470 495 … … 784 809 ) % e.what() 785 810 ); 786 } 787 MSVC_ONLY(;) 811 } MSVC_ONLY(;) 788 812 789 813 void ServerList::handleGlobStatRes(std::istringstream &i, IPV4Address from) try{ … … 849 873 ) % e.what() 850 874 ); 851 } 852 MSVC_ONLY(;) 875 } MSVC_ONLY(;) 876 877 void ServerList::handleGlobSearchRes(std::istringstream &i, IPV4Address from) 878 try { 879 logTrace(TRACE, 880 boost::format("Received GlobSearchRes from %s") % from 881 ); 882 883 ED2KPacket::GlobSearchRes p(i); 884 UAddrIter it(m_list->get<4>().find(from)); 885 886 if (it != m_list->get<4>().end() && (*it)->currentSearch()) { 887 // search stopped - ignore these results 888 if (!(*it)->currentSearch()->isRunning()) { 889 return; 890 } 891 for (size_t i = 0; i < p.getCount(); ++i) { 892 (*it)->currentSearch()->addResult(p.getResult(i)); 893 } 894 (*it)->currentSearch()->notifyResults(); 895 // Server::m_globSearch is reset from timed callback 896 } else { 897 logDebug( 898 boost::format( 899 "Received search results from unknown " 900 "server %s" 901 ) % from 902 ); 903 } 904 } catch (std::exception &e) { 905 logError( 906 boost::format("Handling GlobSearchRes packet from %s: %s") 907 % from % e.what() 908 ); 909 logError("Packet contents: " + Utils::hexDump(i.str())); 910 } MSVC_ONLY(;) 853 911 854 912 void ServerList::queryNextServer() { … … 1165 1223 } 1166 1224 1225 void ServerList::doGlobSearch( 1226 SearchPtr search, boost::shared_ptr<std::list<Server*> > srv 1227 ) { 1228 CHECK_RET(srv); 1229 if (!search->isRunning() || !m_list->size() || !srv->size()) { 1230 return; 1231 } 1232 uint32_t callback = 500; // default 500ms delay 1233 size_t loopCnt = srv->size(); 1234 1235 while (srv->size() && loopCnt--) { 1236 if (m_list->find(srv->front()) == m_list->end()) { 1237 srv->pop_front(); 1238 } else if (srv->front()->currentSearch()) { 1239 srv->push_back(srv->front()); 1240 srv->pop_front(); 1241 } else if (!srv->front()->supportsUdpSearch()) { 1242 srv->pop_front(); 1243 } 1244 } 1245 if (srv->size() && srv->front()->currentSearch()) { 1246 logTrace(TRACE, 1247 "No valid servers found for global search, " 1248 "retrying in 5 seconds." 1249 ); 1250 callback = 5000; 1251 } else if (srv->size()) { 1252 ED2KPacket::GlobSearchReq packet(search); 1253 IPV4Address to( 1254 srv->front()->getAddr().getIp(), 1255 srv->front()->getUdpPort() 1256 ); 1257 Client::getUdpSocket()->send(packet, to); 1258 srv->front()->setCurrentSearch(search); 1259 Utils::timedCallback( 1260 boost::bind( 1261 &Server::setCurrentSearch, 1262 srv->front(), SearchPtr() 1263 ), 10000 // 10sec timeout 1264 ); 1265 srv->pop_front(); 1266 logTrace(TRACE, 1267 boost::format("Sent global search request to %s") % to 1268 ); 1269 } 1270 if (srv->size()) { 1271 Utils::timedCallback( 1272 boost::bind( 1273 &ServerList::doGlobSearch, this, 1274 search, srv 1275 ), callback 1276 ); 1277 } else { 1278 std::string keywords; 1279 for (uint32_t i = 0; i < search->getTermCount(); ++i) { 1280 keywords += search->getTerm(i); 1281 if (i + 1 < search->getTermCount()) { 1282 keywords += " "; 1283 } 1284 } 1285 logMsg( 1286 boost::format( 1287 "eDonkey2000: Global search " 1288 "with keywords '%s' ended." 1289 ) % keywords 1290 ); 1291 } 1292 } 1293 1167 1294 } // end namespace Donkey hydranode/hncore/ed2k/serverlist.h
r2613 r2738 245 245 */ 246 246 void onUdpData(ED2KUDPSocket *sock, SocketEvent evt); 247 248 /** 249 * Sends global UDP search query to next server in list. 250 */ 251 void doGlobSearch( 252 SearchPtr search, 253 boost::shared_ptr<std::list<Detail::Server*> > srv 254 ); 247 255 public: 248 256 //! @name Packet handlers … … 256 264 void onPacket(const ED2KPacket::CallbackReq &p); 257 265 void onPacket(const ED2KPacket::FoundSources &p); 258 //@}259 266 260 267 /** … … 273 280 */ 274 281 void handleGlobStatRes(std::istringstream &i, IPV4Address from); 282 283 /** 284 * Handles GlobSearchRes packet from UDP servers 285 * 286 * @param i Input stream to read packet from 287 * @param from The server that sent this data 288 */ 289 void handleGlobSearchRes(std::istringstream &i, IPV4Address from); 290 //@} 275 291 276 292 //! @name Various operations hydranode/hncore/hnsh/shellcommands.cpp
r2669 r2738 85 85 m_commands["search"] = bind(&ShellCommands::cmdSearch, this, _1); 86 86 m_commands["vr"] = bind(&ShellCommands::cmdViewResults, this, _1); 87 m_commands["ss"] = bind(&ShellCommands::cmdStopSearch, this, _1); 87 88 m_commands["download"] = bind(&ShellCommands::cmdDownload, this, _1); 88 89 m_commands["vd"] = bind(&ShellCommands::cmdListDownloads, this, _1); … … 99 100 m_commands["hs"] = bind(&ShellCommands::cmdShowHasherStats, this, _1); 100 101 m_commands["history"] = bind(&ShellCommands::cmdShowHistory, this, _1); 101 m_commands["s s"]= bind(&ShellCommands::cmdSchedStats, this, _1);102 m_commands["scs"] = bind(&ShellCommands::cmdSchedStats, this, _1); 102 103 m_commands["ifs"] = bind(&ShellCommands::cmdFilterStats, this, _1); 103 104 #if !defined(NDEBUG) && !defined(NTRACE) … … 426 427 } 427 428 428 SearchPtr search(new Search()); 429 if (m_currentSearch) { 430 m_currentSearch->stop(); 431 } 432 m_currentSearch.reset(new Search); 429 433 430 434 // Parse arguments … … 451 455 boost::format("Adding search term '%s'.") % *i 452 456 ); 453 search->addTerm(*i);457 m_currentSearch->addTerm(*i); 454 458 } 455 459 } … … 460 464 if ((*i).first == "minsize") { 461 465 uint64_t mz = lexical_cast<uint64_t>((*i).second); 462 search->setMinSize(mz);466 m_currentSearch->setMinSize(mz); 463 467 } else if ((*i).first == "maxsize") { 464 468 uint64_t mz = lexical_cast<uint64_t>((*i).second); 465 search->setMaxSize(mz);469 m_currentSearch->setMaxSize(mz); 466 470 } else if ((*i).first == "type") { 467 search->setType(helperStrToType((*i).second));471 m_currentSearch->setType(helperStrToType((*i).second)); 468 472 } else { 469 473 boost::format fmt("Unknown argument: '%s'"); … … 483 487 (*i).first 484 488 ); 489 m_currentSearch.reset(); 485 490 return false; 486 491 } 487 492 488 493 // Parsing is done, check if everything we need is in place 489 if (! search->getTermCount()) {494 if (!m_currentSearch->getTermCount()) { 490 495 setError("please specify a term to search for"); 496 m_currentSearch.reset(); 491 497 return false; 492 498 } 493 499 494 500 m_searchResults.clear(); 495 search->addResultHandler(501 m_currentSearch->addResultHandler( 496 502 boost::bind(&ShellCommands::helperOnSearchEvent, this, _1) 497 503 ); 498 504 499 505 logMsg("Initialising new search."); 500 search->run();506 m_currentSearch->run(); 501 507 502 508 return true; … … 886 892 *m_socket << "cd Change directory." << Socket::Endl; 887 893 *m_socket << "uname Show Hydranode version." << Socket::Endl; 888 //*m_socket << "history List command history" << Socket::Endl;889 894 *m_socket << "search Search for files." << Socket::Endl; 895 *m_socket << "ss Stop current search." << Socket::Endl; 890 896 *m_socket << "vr [pred] View search results." << Socket::Endl; 891 897 *m_socket << "download Download a search result or link."; … … 899 905 *m_socket << "addsource Add a source to download." << Socket::Endl; 900 906 *m_socket << "hs View Hasher Statistics." << Socket::Endl; 901 *m_socket << "s sView Scheduler Statistics." << Socket::Endl;907 *m_socket << "scs View Scheduler Statistics." << Socket::Endl; 902 908 *m_socket << "ifs View IPFilter Statistics." << Socket::Endl; 903 909 *m_socket << "log [on/off] Enable/Disable log printing."<<Socket::Endl; … … 1410 1416 } 1411 1417 sortResults(pred); 1412 printSearchResults( );1418 printSearchResults(true); 1413 1419 return true; 1414 1420 } … … 1463 1469 fmt % Utils::bytesToString(avgUp) % Utils::bytesToString(avgDown); 1464 1470 *m_socket << fmt.str() << Socket::Endl; 1471 return true; 1472 } 1473 1474 bool ShellCommands::cmdStopSearch(Tokenizer) { 1475 if (m_currentSearch) { 1476 m_currentSearch->stop(); 1477 m_currentSearch.reset(); 1478 } else { 1479 *m_socket << "'ss': Stops current active search."; 1480 *m_socket << Socket::Endl; 1481 } 1465 1482 return true; 1466 1483 } … … 1624 1641 1625 1642 void ShellCommands::helperOnSearchEvent(SearchPtr search) try { 1626 CHECK_THROW(search->getResultCount() >= m_searchResults.size());1627 1628 1643 if (!search->getResultCount()) { 1629 *m_socket << "\rReceived 0 results from server." <<Socket::Endl;1630 m_parent->setPrompt();1631 1644 return; 1632 1645 } … … 1637 1650 } 1638 1651 1639 sortResults( 1640 Prefs::instance().read<std::string>("/SearchSortOrder", "rsrc") 1641 ); 1642 1643 printSearchResults(); 1652 sortResults("src"); 1653 printSearchResults(false); 1644 1654 1645 1655 m_parent->setPrompt(); … … 1650 1660 1651 1661 1652 void ShellCommands::printSearchResults( ) {1662 void ShellCommands::printSearchResults(bool printAll) { 1653 1663 CHECK_THROW(m_socket); 1654 1664 if (!m_searchResults.size()) { … … 1656 1666 } 1657 1667 1658 *m_socket << Socket::Endl; 1659 *m_socket << " # Size Src(Compl) Rating Filename"; 1660 *m_socket << Socket::Endl; 1661 1662 boost::format fmt("%3d) %10s %4d(%d) %3d %|30t|%s"); 1663 for (uint32_t i = 0; i < m_searchResults.size(); ++i) { 1668 uint32_t toPrint = m_parent->getHeight() - 2; 1669 if (m_searchResults.size() < toPrint) { 1670 toPrint = m_searchResults.size(); 1671 uint16_t emptyLines = m_parent->getHeight() - 2 - toPrint; 1672 // clear screen 1673 *m_socket << "\33[H"; 1674 for (uint32_t i = 1; i < m_parent->getHeight(); ++i) { 1675 *m_socket << std::string(m_parent->getWidth(), ' '); 1676 *m_socket << Socket::Endl; 1677 } 1678 *m_socket << "\33[H"; 1679 // navigate to correct position 1680 while (emptyLines--) { 1681 *m_socket << Socket::Endl; 1682 } 1683 } else if (m_searchResults.size() > toPrint) { 1684 toPrint -= 2; // 2-line Notice line in the end 1685 } 1686 1687 *m_socket << "\r # Size Src(Compl) Rating Filename" << Socket::Endl; 1688 1689 boost::format fmt("%3d) %10s %4d(%d) %|27t|%3d %|30t|%s"); 1690 for (uint32_t i = 0; i < toPrint; ++i) { 1664 1691 SearchResultPtr sr(m_searchResults[i]); 1665 1692 1666 1693 fmt % i % Utils::bytesToString(sr->getSize()); 1667 1694 fmt % sr->getSources() % sr->getComplete(); 1668 fmt % static_cast<int>(sr->getRating()) % sr->getName(); 1669 1695 fmt % static_cast<int>(sr->getRating()); 1696 1697 // can't truncate name if screen width is <31 1698 if (m_parent->getWidth() <= 31) { 1699 fmt % sr->getName(); 1700 } 1701 // truncate name to fit on screen single line 1702 if (sr->getName().size() > m_parent->getWidth() - 31u) { 1703 std::string tmp( 1704 sr->getName().substr( 1705 0, m_parent->getWidth() - 40 1706 ) 1707 ); 1708 tmp += "[...]" + sr->getName().substr( 1709 sr->getName().size() - 4 1710 ); 1711 fmt % tmp; 1712 } else { 1713 fmt % sr->getName(); 1714 } 1670 1715 *m_socket << fmt.str() << Socket::Endl; 1716 } 1717 if (toPrint < m_searchResults.size()) { 1718 *m_socket << 1719 " => Notice: Only the most popular results are printed. "; 1720 *m_socket << Socket::Endl; 1721 *m_socket << " => To view all results, type 'vr'."; 1722 *m_socket << " To stop searching, type 'ss'."; 1723 *m_socket << Socket::Endl; 1671 1724 } 1672 1725 } hydranode/hncore/hnsh/shellcommands.h
r2656 r2738 198 198 bool cmdMemStats(Tokenizer); 199 199 200 //! Stops current search 201 bool cmdStopSearch(Tokenizer); 202 200 203 //! Print one client 201 204 &
