📄 console.cpp
字号:
#include "console.h" // def.h#include <stdlib.h> // atoi()#include <sys/types.h> // fstat(), FD_SET(), fork()#include <sys/stat.h> // fstat()#include <unistd.h> // isatty(), fork(), setsid()#include <string.h>#include <errno.h>#include <ctype.h> // isdigit()#include <signal.h>#include <fcntl.h> // open()#include <time.h> // clock()#if defined(HAVE_IOCTL_H)#include <ioctl.h> // ioctl()#elif defined(HAVE_SYS_IOCTL_H)#include <sys/ioctl.h>#endif#include "btconfig.h"#include "ctcs.h"#include "btcontent.h"#include "tracker.h"#include "peer.h"#include "peerlist.h"#include "bitfield.h"#include "bttime.h"#include "sigint.h"#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_SNPRINTF) || \ !defined(HAVE_STRCASECMP)#include "compat.h"#endif// console.cpp: Copyright 2007-2008 Dennis Holmes (dholmes@rahul.net)// input mode definitions#define K_CHARS 0#define K_LINES 1const char LIVE_CHAR[4] = {'-', '\\','|','/'};Console CONSOLE;static int g_console_ready = 0;//===========================================================================// ConStream class functionsConStream::ConStream(){ m_stream = (FILE *)0; m_name = (char *)0; m_restore = 0; m_newline = 1; m_suspend = 0; m_inputmode = K_LINES;}ConStream::~ConStream(){ if( m_restore ) RestoreMode(); if( !m_suspend ) _newline(); if( m_stream ) fclose(m_stream); if( m_name ) delete []m_name;}void ConStream::Close(){ if( m_stream ){ fclose(m_stream); m_stream = (FILE *)0; } m_suspend = 1;}void ConStream::Associate(FILE *stream, const char *name, int mode){ m_stream = stream; m_filemode = mode; if( m_name = new char[strlen(name)+1] ) strcpy(m_name, name); else Error(1, "Failed to allocate memory for output filename.");}int ConStream::SameDev(ConStream *master) const{ struct stat sbone, sbtwo; if( master == this || Fileno() == master->Fileno() ) return 1; else if( Fileno() < 0 || master->Fileno() < 0 ) return 0; 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;}int ConStream::IsTTY() const{ return (Fileno() >= 0) ? isatty(Fileno()) : 0;}void ConStream::PreserveMode(){ int r; if( !IsTTY() ) return;#if defined(USE_TERMIOS) r = tcgetattr(Fileno(), &m_original);#elif defined(USE_TERMIO) r = ioctl(Fileno(), TCGETA, &m_original);#elif defined(USE_SGTTY) r = gtty(Fileno(), &m_original);#endif if( r < 0 ){ Error(1, "Error preserving terminal mode on fd %d: %s", Fileno(), strerror(errno)); }else m_restore = 1;}void ConStream::RestoreMode(){ int r; if( !IsTTY() ) return;#if defined(USE_TERMIOS) r = tcsetattr(Fileno(), TCSANOW, &m_original);#elif defined(USE_TERMIO) r = ioctl(Fileno(), TCSETA, &m_original);#elif defined(USE_SGTTY) r = stty(Fileno(), &m_original);#endif if( r < 0 ){ Error(1, "Error restoring terminal mode on fd %d: %s", Fileno(), strerror(errno)); }}void ConStream::SetInputMode(int keymode){ if( m_suspend ) return; m_inputmode = keymode; if( !IsTTY() ) 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); termset.c_cc[VMIN] = 1; termset.c_cc[VTIME] = 0; tcsetattr(Fileno(), TCSANOW, &termset);#elif defined(USE_TERMIO) termset.c_lflag &= ~(ICANON | ECHO); termset.c_cc[VMIN] = 1; termset.c_cc[VTIME] = 0; 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); termset.c_cc[VMIN] = 1; termset.c_cc[VTIME] = 0; tcsetattr(Fileno(), TCSANOW, &termset);#elif defined(USE_TERMIO) termset.c_lflag |= (ICANON | ECHO); termset.c_cc[VMIN] = 1; termset.c_cc[VTIME] = 0; ioctl(Fileno(), TCSETA, &termset);#elif defined(USE_SGTTY) termset.sg_flags &= ~CBREAK; termset.sg_flags |= ECHO; stty(Fileno(), &termset);#endif break; default: break; }}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 || !*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, IsTTY() ? "\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);}/* ConStream functions need to call Error instead of CONSOLE.Warning, because CONSOLE may not be initialized yet (or may have been destroyed already).*/void ConStream::Error(int sev, const char *message, ...){ va_list ap; va_start(ap, message); /* Note the call to Warning sends only the literal message--a limitation to deal with later. */ if( g_console_ready ) CONSOLE.Warning(sev, message); else{ vfprintf(stderr, message, ap); fflush(stderr); } va_end(ap);}//===========================================================================// Console class functionsConsole::Console(){ m_skip_status = m_status_last = 0; m_live_idx = 0; m_oldfd = -1; 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_status_len = 80; m_stdout.Associate(stdout, "stdout", 1); m_stderr.Associate(stderr, "stderr", 1); m_stdin.Associate(stdin, "stdin", 0); m_off.Associate(NULL, "off", 1); m_off.Suspend(); 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; if( this == &CONSOLE ) g_console_ready = 1;}Console::~Console(){ if( this == &CONSOLE ) g_console_ready = 0;}int Console::IntervalCheck(fd_set *rfdp, fd_set *wfdp){ Status(0); if( m_oldfd >= 0 ){ FD_CLR(m_oldfd, rfdp); m_oldfd = -1; } if( !m_streams[O_INPUT]->IsSuspended() ){ FD_SET(m_streams[O_INPUT]->Fileno(), rfdp); return m_streams[O_INPUT]->Fileno(); }else{ if( m_streams[O_INPUT]->Fileno() >= 0 ) FD_CLR(m_streams[O_INPUT]->Fileno(), rfdp); return -1; }}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[MAXPATHLEN], *s; if( m_streams[O_INPUT]->Fileno() >= 0 && 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, sizeof(param)) ){ if( s = strchr(param, '\n') ) *s = '\0'; if( '0'==pending ){ if( OperatorMenu(param) ) pending = '\0'; }else{ m_streams[O_INPUT]->SetInputMode(K_CHARS); if( *param ) switch( pending ){ case 'n': // get1file if( arg_file_to_download ) delete []arg_file_to_download; arg_file_to_download = new char[strlen(param) + 1]; if( !arg_file_to_download ) Warning(1, "error, failed to allocate memory for option"); else strcpy(arg_file_to_download, param); BTCONTENT.SetFilter(); break; case 'S': // CTCS server if( !strchr(param, ':') ) Interact("Invalid input"); else{ if( arg_ctcs ) delete []arg_ctcs; if(0==strcmp(":", param)){ if(arg_ctcs) CTCS.Reset(1); arg_ctcs = (char*) 0; }else{ arg_ctcs = new char[strlen(param) + 1]; if( !arg_ctcs ) Warning(1, "error, failed to allocate memory for option"); else{ strcpy(arg_ctcs, param); CTCS.Initial(); CTCS.Reset(1); } } } break; case 'X': // completion command (user exit) if( arg_completion_exit ) delete []arg_completion_exit; arg_completion_exit = new char[strlen(param) + 1]; if( !arg_completion_exit ) Warning(1, "error, failed to allocate memory for option"); else strcpy(arg_completion_exit, param); break; case 'Q': // quit if( 'y'==*param || 'Y'==*param ){ Tracker.ClearRestart(); Tracker.SetStoped(); } break; default: Interact("Input mode error"); } } }else{ if( m_streams[O_INPUT]->Eof() ){ Interact("End of input reached."); if( ChangeChannel(O_INPUT, "off") < 0 ) m_streams[O_INPUT]->Suspend(); }else if(errno){ if( ENODEV==errno || ENOTTY==errno ) m_streams[O_INPUT]->Suspend(); else Interact("Input error: %s", strerror(errno)); }else Interact("Input error!"); } if( '0' != pending ){ m_streams[O_INPUT]->SetInputMode(K_CHARS); Status(1); } }else{ // command character received m_skip_status = 1; if( (c = m_streams[O_INPUT]->CharIn()) == EOF ){ if( m_streams[O_INPUT]->Eof() ){ Interact("End of input reached."); if( ChangeChannel(O_INPUT, "off") < 0 ) m_streams[O_INPUT]->Suspend(); }else if(errno){ if( ENODEV==errno || ENOTTY==errno ) m_streams[O_INPUT]->Suspend(); else Interact("Input error: %s", strerror(errno)); }else Interact("Input error!"); return; } 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 files", "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"); Interact(" %-9s%-30s %-9s%s", "X", "Completion command",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -