📄 console.cpp
字号:
"", ""); break; case 'd': // download bw limit case 'u': // upload bw limit if(arg_ctcs) Interact("Note, changes may be overridden by CTCS."); case 'e': // seed time case 'E': // seed ratio case 'm': // min peers case 'M': // max peers case 'C': // max cache size inc = 1; count = 0; Interact_n(""); break; case 'n': // get1file if( BTCONTENT.IsFull() ) Interact("Download is already complete."); else{ m_streams[O_INPUT]->SetInputMode(K_LINES); ShowFiles(); Interact("Enter 0 or * for all files (normal behavior)."); if( arg_file_to_download ) Interact_n("Get file number/list (currently %s): ", arg_file_to_download); else Interact_n("Get file number/list: "); } break; case 'S': // CTCS server m_streams[O_INPUT]->SetInputMode(K_LINES); Interact_n(""); if( arg_ctcs ){ Interact("Enter ':' to stop using CTCS."); Interact_n("CTCS server:port (currently %s): ", arg_ctcs); } else Interact_n("CTCS server:port: "); break; case 'X': // completion command (user exit) if( BTCONTENT.IsFull() ) Interact("Download is already complete."); else{ m_streams[O_INPUT]->SetInputMode(K_LINES); Interact("Enter a command to run upon download completion."); if( arg_completion_exit ) Interact("Currently: %s", arg_completion_exit); Interact_n(">"); } break; case 'v': // verbose if( arg_verbose && !m_streams[O_INPUT]->SameDev(m_streams[O_DEBUG]) ) Debug("Verbose output off"); arg_verbose = !arg_verbose; Interact("Verbose output %s", arg_verbose ? "on" : "off"); break; case 'Q': // quit if( !Tracker.IsQuitting() ){ m_streams[O_INPUT]->SetInputMode(K_LINES); Interact_n(""); Interact_n("Quit: Are you sure? "); } break; case '+': // increase value case '-': // decrease value if( ('+'==c && inc<0) || ('-'==c && inc>0) ) inc *= -1; switch( pending ){ int value; case 'd': cfg_max_bandwidth_down += ( (cfg_max_bandwidth_down * (abs(inc)/100.0) < 1) ? inc : (int)(cfg_max_bandwidth_down * (inc/100.0)) ); if( cfg_max_bandwidth_down < 0 ) cfg_max_bandwidth_down = 0; break; case 'u': cfg_max_bandwidth_up += ( (cfg_max_bandwidth_up * (abs(inc)/100.0) < 1) ? inc : (int)(cfg_max_bandwidth_up * (inc/100.0)) ); if( cfg_max_bandwidth_up < 0 ) cfg_max_bandwidth_up = 0; break; case 'e': cfg_seed_hours += inc; if( cfg_seed_hours < 0 ) cfg_seed_hours = 0; break; case 'E': cfg_seed_ratio += inc / 10.0; if( cfg_seed_ratio < 0 ) cfg_seed_ratio = 0; break; case 'm': value = (int)cfg_min_peers; value += inc; cfg_min_peers = (value < 1) ? 1 : (size_t)value; if( cfg_min_peers > cfg_max_peers ) cfg_min_peers = cfg_max_peers; break; case 'M': value = (int)cfg_max_peers; value += inc; cfg_max_peers = (value < (int)cfg_min_peers) ? cfg_min_peers : (size_t)value; if( cfg_max_peers > 1000 ) cfg_max_peers = 1000; break; case 'C': value = (int)cfg_cache_size; value += inc; cfg_cache_size = (value < 0) ? 0 : (size_t)value; BTCONTENT.CacheConfigure(); break; default: Status(1); break; } if( 10==++count ) inc *= 2; else if( 5==count ) inc *= 5; if( arg_ctcs ){ if( 'd'==pending || 'u'==pending ) CTCS.Send_bw(); else CTCS.Send_Config(); } break; case '0': // operator menu case 0x1b: // Escape key pending = '0'; OperatorMenu(""); break; default: Status(1); break; } switch( pending ){ case 'd': InteractU("DL Limit: %d B/s ", (int)cfg_max_bandwidth_down); break; case 'u': InteractU("UL Limit: %d B/s ", (int)cfg_max_bandwidth_up); break; case 'e': InteractU("Seed time: %.1f hours ", BTCONTENT.GetSeedTime() ? (cfg_seed_hours - (now - BTCONTENT.GetSeedTime())/(double)3600) : (double)cfg_seed_hours); break; case 'E': InteractU("Seed ratio: %.2f ", (double)cfg_seed_ratio); break; case 'm': InteractU("Minimum peers: %d ", (int)cfg_min_peers); break; case 'M': InteractU("Maximum peers: %d ", (int)cfg_max_peers); break; case 'C': InteractU("Maximum cache: %d MB ", (int)cfg_cache_size); break; default: break; } } }}// Return non-zero to exit operator menu mode.int Console::OperatorMenu(const char *param){ static int oper_mode = 0; static int channel, n_opt; if( 0==oper_mode ){ Interact("Operator Menu"); n_opt = 0; Interact(" Console Channels:"); Interact(" %2d) Normal/status: %s", ++n_opt, m_streams[O_NORMAL]->GetName()); Interact(" %2d) Interactive: %s", ++n_opt, m_streams[O_INTERACT]->GetName()); Interact(" %2d) Error/warning: %s", ++n_opt, m_streams[O_WARNING]->GetName()); Interact(" %2d) Debug/verbose: %s", ++n_opt, m_streams[O_DEBUG]->GetName()); Interact(" %2d) Input: %s", ++n_opt, m_streams[O_INPUT]->GetName()); char buffer[80]; Interact(" Status Line Formats:"); for( int i=0; i < STATUSLINES; i++ ){ (this->*m_statusline[i])(buffer, sizeof(buffer)); Interact(" %c%d) %s", (i==m_status_format) ? '*' : ' ', ++n_opt, buffer); } Interact(" Other options:"); Interact(" %2d) View detailed status", ++n_opt); if( WORLD.IsPaused() ) Interact(" %2d) Resume (continue upload/download)", ++n_opt); else Interact(" %2d) Pause (suspend upload/download)", ++n_opt); if( !arg_daemon ) Interact(" %2d) Become daemon (fork to background)", ++n_opt); Interact(" %2d) Update tracker stats & get peers", ++n_opt); Interact(" %2d) Restart (recover) the tracker session", ++n_opt); Interact_n("Enter selection: "); m_streams[O_INPUT]->SetInputMode(K_LINES); oper_mode = 1; return 0; } else if( 1==oper_mode ){ if( !*param ){ oper_mode = 0; Interact("Exiting menu"); return 1; } int sel = atoi(param); if( sel < 1 || sel > n_opt ){ Interact_n("Enter selection: "); return 0; } if( sel <= O_NCHANNELS+1 ){ // change i/o channel channel = sel - 1; Interact("Possible values are:"); Interact(" %s", m_stdout.GetName()); Interact(" %s", m_stderr.GetName()); Interact(" %s", m_off.GetName()); Interact(" a filename"); Interact_n("Enter a destination: "); m_streams[O_INPUT]->SetInputMode(K_LINES); oper_mode = 2; return 0; }else if( sel <= O_NCHANNELS+1 + STATUSLINES ){ m_status_format = sel - (O_NCHANNELS+1) - 1; oper_mode = 0; return OperatorMenu(""); }else if( sel == 1 + O_NCHANNELS+1 + STATUSLINES ){ // detailed status m_streams[O_INPUT]->SetInputMode(K_CHARS); Interact(""); Interact("Torrent: %s", arg_metainfo_file); ShowFiles(); if( arg_file_to_download && !BTCONTENT.Seeding() ) Interact("Downloading: %s", arg_file_to_download); Interact(""); Interact("Download rate: %dB/s Limit: %dB/s Total: %llu", (int)(Self.RateDL()), (int)cfg_max_bandwidth_down, (unsigned long long)(Self.TotalDL())); Interact(" Upload rate: %dB/s Limit: %dB/s Total: %llu", (int)(Self.RateUL()), (int)cfg_max_bandwidth_up, (unsigned long long)(Self.TotalUL())); time_t t = Tracker.GetReportTime(); if( t ){ char s[42];#ifdef HAVE_CTIME_R_3 ctime_r(&t, s, sizeof(s));#else ctime_r(&t, s);#endif if( s[strlen(s)-1] == '\n' ) s[strlen(s)-1] = '\0'; Interact("Reported to tracker: %llu up", (unsigned long long)(Tracker.GetReportUL())); Interact(" %llu down at %s", (unsigned long long)(Tracker.GetReportDL()), s); } Interact("Failed hashes: %d Dup blocks: %d Unwanted blocks: %d", (int)(BTCONTENT.GetHashFailures()), (int)(BTCONTENT.GetDupBlocks()), (int)(BTCONTENT.GetUnwantedBlocks())); Interact(""); Interact("Peers: %d Min: %d Max: %d", (int)(WORLD.GetPeersCount()), (int)cfg_min_peers, (int)cfg_max_peers); Interact("Listening on: %s", WORLD.GetListen()); Interact("Announce URL: %s", BTCONTENT.GetAnnounce()); Interact(""); Interact("Ratio: %.2f Seed time: %luh Seed ratio: %.2f", (double)(Self.TotalUL()) / ( Self.TotalDL() ? Self.TotalDL() : BTCONTENT.GetTotalFilesLength() ), (unsigned long)cfg_seed_hours, cfg_seed_ratio); Interact("Cache in use: %dKB Wants: %dKB Max: %dMB", (int)(BTCONTENT.CacheUsed()/1024), (int)(BTCONTENT.CacheSize()/1024), (int)cfg_cache_size); if(arg_ctcs) Interact("CTCS Server: %s", arg_ctcs); if(arg_verbose) cpu(); oper_mode = 0; return 1; }else if( sel == 2 + O_NCHANNELS+1 + STATUSLINES ){ // pause/resume if( WORLD.IsPaused() ) WORLD.Resume(); else WORLD.Pause(); oper_mode = 0; return 1; }else if( sel == 3 + O_NCHANNELS+1 + STATUSLINES ){ // daemon Daemonize(); oper_mode = 0; return 1; }else if( sel == 4 + O_NCHANNELS+1 + STATUSLINES ){ // update tracker if( Tracker.GetStatus() == T_FREE ) Tracker.Reset(15); else Interact("Already connecting, please be patient..."); oper_mode = 0; return 1; }else if( sel == 5 + O_NCHANNELS+1 + STATUSLINES ){ // update tracker Tracker.RestartTracker(); oper_mode = 0; return 1; } } else if( 2==oper_mode ){ if( !*param ){ oper_mode = 0; return OperatorMenu(""); } ChangeChannel(channel, param); oper_mode = 0; return OperatorMenu(""); } Interact("Exiting menu"); return 1;}int Console::ChangeChannel(int channel, const char *param, int notify){ ConStream *dest = (ConStream *)0; if( 0==strcasecmp(param, m_stdout.GetName()) ) dest = &m_stdout; else if( 0==strcasecmp(param, m_stderr.GetName()) ) dest = &m_stderr; else if( 0==strcasecmp(param, m_stdin.GetName()) ) dest = &m_stdin; else if( 0==strcasecmp(param, m_off.GetName()) ) dest = &m_off; else{ for( int i=0; i <= O_NCHANNELS; i++ ){ if( channel != i && 0==strcmp(param, m_streams[i]->GetName()) && m_streams[i]->GetMode() == ((channel==O_INPUT) ? 0 : 1) ){ dest = m_streams[i]; break; } } if( !dest ){ FILE *stream; if( dest = new ConStream ){ if( 0==strcmp(param, m_streams[channel]->GetName()) ){ delete m_streams[channel]; m_streams[channel] = &m_off; } if( stream = fopen(param, (channel==O_INPUT) ? "r" : "a") ) dest->Associate(stream, param, (channel==O_INPUT) ? 0 : 1); else{ Interact("Error opening file: %s", strerror(errno)); delete dest; dest = (ConStream *)0; } }else Interact("Failed to allocate memory."); } } if( dest ){ if( O_INPUT==channel ) m_oldfd = m_streams[channel]->Fileno(); if( m_streams[channel] != &m_stdout && m_streams[channel] != &m_stderr && m_streams[channel] != &m_stdin && m_streams[channel] != &m_off ){ int in_use = 0; for( int i=0; i <= O_NCHANNELS; i++ ){ if( channel != i && m_streams[channel] == m_streams[i] ) in_use = 1; } if( !in_use ) delete m_streams[channel]; else if( O_INPUT==channel ) m_streams[O_INPUT]->RestoreMode(); } if( notify && (!arg_daemon || !m_streams[channel]->IsTTY()) ){ switch(channel){ case O_NORMAL: Print("Output channel is now %s", dest->GetName()); break; case O_DEBUG: Debug("Debug channel is now %s", dest->GetName()); break; case O_INTERACT: Interact("Interactive output channel is now %s", dest->GetName()); break; case O_INPUT: Interact("Input channel is now %s", dest->GetName()); break; default: break; } } m_streams[channel] = dest; if( O_INPUT==channel ){ m_streams[O_INPUT]->PreserveMode(); m_streams[O_INPUT]->SetInputMode(K_CHARS); } return 0; }else return -1;}void Console::ShowFiles(){ BTFILE *file = 0; BitField tmpFilter; int n = 0; Interact("Files in this torrent:"); while( ++n <= BTCONTENT.GetNFiles() ){ BTCONTENT.SetTmpFilter(n, &tmpFilter); BitField tmpBitField = *BTCONTENT.pBF; tmpBitField.Except(tmpFilter); Interact("%d) %s [%llu] %d%%", n, BTCONTENT.GetFileName(n), (unsigned long long)(BTCONTENT.GetFileSize(n)), BTCONTENT.GetFilePieces(n) ? (int)(100 * tmpBitField.Count() / BTCONTENT.GetFilePieces(n)) : 0); }}void Console::Status(int immediate){ static char buffer[80]; if( immediate ) m_skip_status = 0; if( m_pre_dlrate.TimeUsed() || immediate ){ if( m_skip_status ) m_skip_status = 0; else if( !m_streams[O_NORMAL]->IsSuspended() || (arg_verbose && !m_streams[O_DEBUG]->IsSuspended()) ){ // optimized to generate the status line only if it will be output (this->*m_statusline[m_status_format])(buffer, sizeof(buffer)); if( !m_status_last ) Print_n(""); int tmplen = 0; if( m_streams[O_NORMAL]->IsTTY() ){#ifdef TIOCGWINSZ struct winsize tsize; if( ioctl(m_streams[O_NORMAL]->Fileno(), TIOCGWINSZ, &tsize) >= 0 ) tmplen = tsize.ws_col - 1;#else#ifdef TIOCGSIZE struct ttysize tsize; if( ioctl(m_streams[O_NORMAL]->Fileno(), TIOCGSIZE, &tsize) >= 0 ) tmplen = tsize.ts_cols - 1;#else#ifdef WIOCGETD struct uwdata tsize; if( ioctl(m_streams[O_NORMAL]->Fileno(), WIOCGETD, &tsize) >= 0 ) tmplen = tsize.uw_width / tsize.uw_hs - 1;#endif#endif#endif if( tmplen > 80 ) tmplen = 80; } int len = strlen(buffer); if( 0==tmplen ) tmplen = (len < m_status_len) ? m_status_len : len; m_status_len = len; Update("%*.*s", -tmplen, tmplen, buffer); m_status_last = 1; if(arg_verbose) Debug("Cache: %dK/%dM Hits: %d Miss: %d %d%% Pre: %d/%d", (int)(BTCONTENT.CacheUsed()/1024), (int)cfg_cache_size, (int)(BTCONTENT.CacheHits()), (int)(BTCONTENT.CacheMiss()), BTCONTENT.CacheHits() ? (int)(100 * BTCONTENT.CacheHits() / (BTCONTENT.CacheHits()+BTCONTENT.CacheMiss())) : 0, BTCONTENT.CachePre(), (int)(Self.TotalUL() / DEFAULT_SLICE_SIZE)); } m_pre_dlrate = Self.GetDLRate(); m_pre_ulrate = Self.GetULRate(); }}void Console::StatusLine0(char buffer[], size_t length){ char partial[30] = ""; if( BTCONTENT.GetFilter() && !BTCONTENT.GetFilter()->IsEmpty() ){ BitField tmpBitField = *BTCONTENT.pBF; tmpBitField.Except(BTCONTENT.GetFilter()); sprintf( partial, "P:%d/%d ", (int)(tmpBitField.Count()), (int)(BTCONTENT.GetNPieces() - BTCONTENT.GetFilter()->Count()) ); } char checked[14] = ""; if( BTCONTENT.CheckedPieces() < BTCONTENT.GetNPieces() ){ sprintf( checked, "Checking: %d%%", 100 * BTCONTENT.CheckedPieces() / BTCONTENT.GetNPieces() ); } snprintf(buffer, length, "%c %d/%d/%d [%d/%d/%d] %lluMB,%lluMB | %d,%dK/s | %d,%dK E:%d,%d %s%s", LIVE_CHAR[m_live_idx++], (int)(WORLD.GetSeedsCount()), (int)(WORLD.GetPeersCount()) - WORLD.GetSeedsCount(), (int)(Tracker.GetPeersCount()), (int)(BTCONTENT.pBF->Count()), (int)(BTCONTENT.GetNPieces()), (int)(WORLD.Pieces_I_Can_Get()), (unsigned long long)(Self.TotalDL() >> 20), (unsigned long long)(Self.TotalUL() >> 20), (int)(Self.RateDL() >> 10), (int)(Self.RateUL() >> 10), (int)(m_pre_dlrate.RateMeasure(Self.GetDLRate()) >> 10), (int)(m_pre_ulrate.RateMeasure(Self.GetULRate()) >> 10), (int)(Tracker.GetRefuseClick()), (int)(Tracker.GetOkClick()), partial, (Tracker.GetStatus()==T_CONNECTING) ? "Connecting" : ( (Tracker.GetStatus()==T_READY) ? "Connected" : (Tracker.IsRestarting() ? "Restarting" : (Tracker.IsQuitting() ? "Quitting" : (WORLD.IsPaused() ? "Paused" : checked))) ) );}void Console::StatusLine1(char buffer[], size_t length)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -