📄 console.cpp
字号:
#include <stdlib.h> // atoi()#include <sys/types.h> // fstat(), FD_SET()#include <sys/stat.h> // fstat()#include <unistd.h> // isatty()#include <string.h>#include <errno.h>#include <ctype.h> // isdigit()#include <signal.h>#include "btconfig.h"#include "console.h"#include "ctcs.h"#include "btcontent.h"#include "tracker.h"#include "peer.h"#include "peerlist.h"#include "bttime.h"#include "sigint.h"// console.cpp: Copyright 2007 Dennis Holmes (dholmes@rahul.net)// input mode definitions#define K_CHARS 0#define K_LINES 1const char LIVE_CHAR[4] = {'-', '\\','|','/'};Console CONSOLE;//===========================================================================// ConStream class functionsConStream::ConStream(){ m_stream = (FILE *)0; m_name = (char *)0; m_newline = 1; m_suspend = 0; m_inputmode = K_LINES;}ConStream::~ConStream(){ if( !m_suspend ) _newline(); if( m_stream ) fclose(m_stream); if( m_name ) delete []m_name;}void ConStream::Associate(FILE *stream, const char *name){ m_stream = stream; if( m_name = new char[strlen(name)+1] ) strcpy(m_name, name); else CONSOLE.Warning(1, "Failed to allocate memory for output filename.");}int ConStream::SameDev(ConStream *master) const{ struct stat sbone, sbtwo; if( master == this ) return 1; if( !fstat(Fileno(), &sbone) && !fstat(master->Fileno(), &sbtwo) ) return (sbone.st_dev==sbtwo.st_dev && sbone.st_ino==sbtwo.st_ino) ? 1 : 0; else return 0;}void ConStream::PreserveMode(){ if( !isatty(Fileno()) ) return;#if defined(USE_TERMIOS) tcgetattr(Fileno(), &m_original);#elif defined(USE_TERMIO) ioctl(Fileno(), TCGETA, &m_original);#elif defined(USE_SGTTY) gtty(Fileno(), &m_original);#endif}void ConStream::RestoreMode(){ if( !isatty(Fileno()) ) return;#if defined(USE_TERMIOS) tcsetattr(Fileno(), TCSANOW, &m_original);#elif defined(USE_TERMIO) ioctl(Fileno(), TCSETA, &m_original);#elif defined(USE_SGTTY) stty(Fileno(), &m_original);#endif}void ConStream::SetInputMode(int keymode){ if( m_suspend || !isatty(Fileno()) ) return;#if defined(USE_TERMIOS) struct termios termset; tcgetattr(Fileno(), &termset);#elif defined(USE_TERMIO) struct termio termset; ioctl(Fileno(), TCGETA, &termset);#elif defined(USE_SGTTY) struct sgttyb termset; gtty(Fileno(), &termset);#endif switch(keymode) { case K_CHARS: // read a char at a time, no echo#if defined(USE_TERMIOS) termset.c_lflag &= ~(ICANON | ECHO); tcsetattr(Fileno(), TCSANOW, &termset);#elif defined(USE_TERMIO) termset.c_lflag &= ~(ICANON | ECHO); ioctl(Fileno(), TCSETA, &termset);#elif defined(USE_SGTTY) termset.sg_flags |= CBREAK; termset.sg_flags &= ~ECHO; stty(Fileno(), &termset);#endif break; case K_LINES: // read a line at a time (allow terminal editing)#if defined(USE_TERMIOS) termset.c_lflag |= (ICANON | ECHO); tcsetattr(Fileno(), TCSANOW, &termset);#elif defined(USE_TERMIO) termset.c_lflag |= (ICANON | ECHO); ioctl(Fileno(), TCSETA, &termset);#elif defined(USE_SGTTY) termset.sg_flags &= ~CBREAK; termset.sg_flags |= ECHO; stty(Fileno(), &termset);#endif break; default: break; } m_inputmode = keymode;}int ConStream::Output(const char *message, va_list ap){ if( m_suspend ) return 0; int old_newline = m_newline; _newline(); _convprintf(message, ap); _newline(); fflush(m_stream); return (old_newline==m_newline) ? 0 : 1;}int ConStream::Output_n(const char *message, va_list ap){ if( m_suspend ) return 0; int old_newline = m_newline; if( !*message ) _newline(); else _convprintf(message, ap); fflush(m_stream); return (old_newline==m_newline) ? 0 : 1;}int ConStream::Update(const char *message, va_list ap){ if( m_suspend ) return 0; int old_newline = m_newline; if( !m_newline) fprintf(m_stream, isatty(Fileno()) ? "\r" : "\n"); _convprintf(message, ap); fflush(m_stream); return (old_newline==m_newline) ? 0 : 1;}char *ConStream::Input(char *field, size_t length){ if( m_suspend ) return (char *)0; m_newline = 1; return fgets(field, length, m_stream);}int ConStream::CharIn(){ if( m_suspend ) return 0; return fgetc(m_stream);}inline void ConStream::_newline(){ if( !m_newline ){ fprintf(m_stream, "\n"); m_newline = 1; }}inline int ConStream::_convprintf(const char *format, va_list ap){ int r = ( '\n' == format[strlen(format)-1] ); m_newline = r; return vfprintf(m_stream, format, ap);}//===========================================================================// Console class functionsConsole::Console(){ m_skip_status = m_status_last = 0; m_live_idx = 0; m_status_format = 0; int i = 0; m_statusline[i++] = &Console::StatusLine0; m_statusline[i++] = &Console::StatusLine1; if( STATUSLINES > i ){ fprintf(stderr, "Unassigned status line in Console() constructor!\n"); exit(1); }else if ( STATUSLINES < i ){ fprintf(stderr, "Value of STATUSLINES is too small!\n"); exit(1); } m_stdout.Associate(stdout, "stdout"); m_stderr.Associate(stderr, "stderr"); m_stdin.Associate(stdin, "stdin"); m_streams[O_NORMAL] = &m_stdout; m_streams[O_WARNING] = &m_stderr; m_streams[O_DEBUG] = &m_stderr; m_streams[O_INTERACT] = &m_stdout; m_streams[O_INPUT] = &m_stdin; m_streams[O_INPUT]->PreserveMode(); m_streams[O_INPUT]->SetInputMode(K_CHARS); m_conmode = K_CHARS;}Console::~Console(){ m_streams[O_INPUT]->RestoreMode();}int Console::IntervalCheck(fd_set *rfdp, fd_set *wfdp){ Status(0); FD_SET(m_streams[O_INPUT]->Fileno(), rfdp); return m_streams[O_INPUT]->Fileno();}void Console::User(fd_set *rfdp, fd_set *wfdp, int *nready, fd_set *rfdnextp, fd_set *wfdnextp){ static char pending = '\0'; static int inc, count; char c, param[256], *s; if( FD_ISSET(m_streams[O_INPUT]->Fileno(), rfdp) ){ FD_CLR(m_streams[O_INPUT]->Fileno(), rfdnextp); (*nready)--; if( K_LINES==m_streams[O_INPUT]->GetInputMode() ){ // command parameter SyncNewlines(O_INPUT); if( m_streams[O_INPUT]->Input(param, 256) ){ if( s = strchr(param, '\n') ) *s = '\0'; if( '0'==pending ){ if( OperatorMenu(param) ) pending = '\0'; }else{ if( *param ) switch( pending ){ case 'n': // get1file if( isdigit(*param) ){ arg_file_to_download = atoi(param); BTCONTENT.SetFilter(); } break; case 'S': // CTCS server if( !strchr(param, ':') ) Interact("Invalid input"); else{ if( arg_ctcs ) delete []arg_ctcs; arg_ctcs = new char[strlen(param) + 1]; strcpy(arg_ctcs, param); CTCS.Initial(); CTCS.Reset(1); } break; case 'Q': // quit if( 'y'==*param || 'Y'==*param ) Tracker.SetStoped(); break; default: Interact("Input error!"); } } }else{ if(errno) Interact("Input error: %s", strerror(errno)); m_streams[O_INPUT]->SetInputMode(K_CHARS); Status(1); } if( '0' != pending ){ m_streams[O_INPUT]->SetInputMode(K_CHARS); Status(1); } }else{ // command character received m_skip_status = 1; c = m_streams[O_INPUT]->CharIn(); if( c!='+' && c!='-' ) pending = c; switch( c ){ case 'h': // help case '?': // help Interact("Available commands:"); Interact(" %-9s%-30s %-9s%s", "[Esc/0]", "Operator menu", "m[+/-]", "Adjust min peers count"); Interact(" %-9s%-30s %-9s%s", "d[+/-]", "Adjust download limit", "M[+/-]", "Adjust max peers count"); Interact(" %-9s%-30s %-9s%s", "u[+/-]", "Adjust upload limit", "C[+/-]", "Adjust max cache size"); Interact(" %-9s%-30s %-9s%s", "n", "Download specific file", "S", "Set/change CTCS server"); Interact(" %-9s%-30s %-9s%s", "e[+/-]", "Adjust seed exit time", "v", "Toggle verbose mode"); Interact(" %-9s%-30s %-9s%s", "E[+/-]", "Adjust seed exit ratio", "Q", "Quit"); break; case 'd': // download bw limit case 'u': // upload bw limit 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 m_streams[O_INPUT]->SetInputMode(K_LINES); ShowFiles(); Interact("Enter 0 for all files (normal behavior)."); Interact_n("Get file number (currently %d): ", (int)arg_file_to_download); }break; case 'S': // CTCS server m_streams[O_INPUT]->SetInputMode(K_LINES); Interact_n(""); if( arg_ctcs ) Interact_n("CTCS server:port (currently %s): ", arg_ctcs); else Interact_n("CTCS server:port: "); break; case 'v': // verbose if( arg_verbose && m_streams[O_INPUT] != 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(" Output 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()); char buffer[80]; Interact(" Status Line Formats:"); for( int i=0; i < STATUSLINES; i++ ){ (CONSOLE.*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); 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 ){ // change output channel channel = sel - 1; Interact("Possible values are:"); Interact(" %s", m_stdout.GetName()); Interact(" %s", m_stderr.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 + STATUSLINES ){ m_status_format = sel - (O_NCHANNELS+1); oper_mode = 0; return OperatorMenu(""); }else if( sel == 1 + O_NCHANNELS + STATUSLINES ){ // detailed status Interact(""); Interact("Torrent: %s", arg_metainfo_file); ShowFiles(); 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()));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -