/* * regexpl - Console Registry Explorer * * Copyright (C) 2000-2005 Nedko Arnaudov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ // Console.cpp: implementation of the CConsole class. // ////////////////////////////////////////////////////////////////////// #include "ph.h" #include "Console.h" #define TAB_WIDTH 8 #define MORE_STRING _T("-- Press space to view more. Press q or Ctrl+break to cancel.--") #define MORE_EMPTY_STRING _T(" ") /* TCHAR * _tcsnchr(const TCHAR *string, TCHAR ch, int count) { while (count--) { if (*string == 0) return NULL; if (*string == ch) return const_cast (string); string++; } return NULL; }*/ ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CConsole::CConsole() { m_hStdIn = INVALID_HANDLE_VALUE; m_hStdOut = INVALID_HANDLE_VALUE; m_blnInsetMode = TRUE; // Insert // m_blnInsetMode = FALSE; // Overwrite m_dwInsertModeCursorHeight = 15; m_dwOverwriteModeCursorHeight = 100; // m_Lines = 0; m_pchBuffer = NULL; m_pchBuffer1 = NULL; m_pchBuffer2 = NULL; m_pfReplaceCompletionCallback = NULL; m_blnMoreMode = TRUE; m_dwOldInputMode = 0; m_dwOldOutputMode = 0; m_blnOldInputModeSaved = FALSE; m_blnOldOutputModeSaved = FALSE; } CConsole::~CConsole() { if (m_pchBuffer) delete[] m_pchBuffer; if (m_pchBuffer1) delete[] m_pchBuffer1; if (m_pchBuffer2) delete[] m_pchBuffer2; if (m_blnOldInputModeSaved) SetConsoleMode(m_hStdIn,m_dwOldInputMode); if (m_blnOldOutputModeSaved) SetConsoleMode(m_hStdOut,m_dwOldOutputMode); if (m_hStdIn != INVALID_HANDLE_VALUE) VERIFY(CloseHandle(m_hStdIn)); if (m_hStdOut != INVALID_HANDLE_VALUE) VERIFY(CloseHandle(m_hStdOut)); } BOOL CConsole::Write(const TCHAR *p, DWORD dwChars) { if (m_hStdOut == INVALID_HANDLE_VALUE) return FALSE; if (m_hStdIn == INVALID_HANDLE_VALUE) return FALSE; if (p == NULL) { ASSERT(FALSE); return FALSE; } DWORD dwCharsToWrite = (dwChars)?dwChars:_tcslen(p); DWORD dwCharsWrittenAdd = 0; BOOL ret = TRUE; while (dwCharsToWrite && (!m_blnDisableWrite)) { switch(p[dwCharsWrittenAdd]) { case _T('\n'): m_CursorPosition.Y++; m_CursorPosition.X = 0; break; case _T('\r'): dwCharsWrittenAdd++; dwCharsToWrite--; continue; case _T('\t'): do { if (!Write(_T(" "))) return FALSE; } while ((m_CursorPosition.X % TAB_WIDTH) && (!m_blnDisableWrite)); dwCharsWrittenAdd++; dwCharsToWrite--; continue; default: { if (!WriteChar(p[dwCharsWrittenAdd])) return FALSE; m_CursorPosition.X++; } } if (m_CursorPosition.X == m_BufferSize.X) { m_CursorPosition.Y++; m_CursorPosition.X = 0; } if (m_CursorPosition.Y == m_BufferSize.Y) { ASSERT(m_CursorPosition.X == 0); SMALL_RECT Src; Src.Left = 0; Src.Right = (SHORT)(m_BufferSize.X-1); Src.Top = 1; Src.Bottom = (SHORT)(m_BufferSize.Y-1); CHAR_INFO ci; #ifdef UNICODE ci.Char.UnicodeChar = L' '; #else ci.Char.AsciiChar = ' '; #endif ci.Attributes = 0; COORD Dest; Dest.X = 0; Dest.Y = 0; if (!ScrollConsoleScreenBuffer(m_hStdOut,&Src,NULL,Dest,&ci)) return FALSE; m_CursorPosition.Y--; m_LinesScrolled++; } if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)) return FALSE; VERIFY(WriteChar(_T(' '))); if ((m_blnMoreMode)&&(m_CursorPosition.X == 0)) { m_Lines++; if (m_Lines >= m_BufferSize.Y-1) { ASSERT(m_Lines == m_BufferSize.Y-1); m_Lines = 0; VERIFY(WriteString(MORE_STRING,m_CursorPosition)); VERIFY(FlushInputBuffer()); CONSOLE_CURSOR_INFO cci; cci.bVisible = FALSE; cci.dwSize = 100; VERIFY(SetConsoleCursorInfo(m_hStdOut,&cci)); INPUT_RECORD InputRecord; DWORD dwRecordsReaded; while ((ret = ReadConsoleInput(m_hStdIn,&InputRecord,1,&dwRecordsReaded)) != FALSE) { ASSERT(dwRecordsReaded == 1); if (dwRecordsReaded != 1) break; if (InputRecord.EventType != KEY_EVENT) continue; if (!InputRecord.Event.KeyEvent.bKeyDown) continue; if ((InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_CANCEL)|| (InputRecord.Event.KeyEvent.wVirtualKeyCode == _T('Q'))) { VERIFY(GenerateConsoleCtrlEvent(CTRL_C_EVENT,0)); continue; } #ifdef UNICODE TCHAR ch = InputRecord.Event.KeyEvent.uChar.UnicodeChar; #else TCHAR ch = InputRecord.Event.KeyEvent.uChar.AsciiChar; #endif if (ch) break; } // delete "more" msg VERIFY(WriteString(MORE_EMPTY_STRING,m_CursorPosition)); m_CursorPosition.X = 0; cci.bVisible = TRUE; cci.dwSize = m_blnInsetMode?m_dwInsertModeCursorHeight:m_dwOverwriteModeCursorHeight; VERIFY(SetConsoleCursorInfo(m_hStdOut,&cci)); } } dwCharsWrittenAdd++; dwCharsToWrite--; } return ret; } unsigned int CConsole::GetTabWidth() { return TAB_WIDTH; } BOOL CConsole::SetTitle(const TCHAR *p) { return SetConsoleTitle(p); } BOOL CConsole::SetTextAttribute(WORD wAttributes) { m_wAttributes = wAttributes; return TRUE; } /* BOOL CConsole::SetInputMode(DWORD dwMode) { return SetConsoleMode(m_hStdIn,dwMode); } BOOL CConsole::SetOutputMode(DWORD dwMode) { return SetConsoleMode(m_hStdOut,dwMode); }*/ BOOL CConsole::FlushInputBuffer() { if (m_hStdIn == INVALID_HANDLE_VALUE) return FALSE; return FlushConsoleInputBuffer(m_hStdIn); } BOOL CConsole::ReadLine() { if (m_hStdIn == INVALID_HANDLE_VALUE) return FALSE; if (m_hStdOut == INVALID_HANDLE_VALUE) return FALSE; if (m_dwBufferSize == 0) { ASSERT(FALSE); return FALSE; } if (m_pchBuffer == NULL) { ASSERT(FALSE); return FALSE; } if (m_pchBuffer1 == NULL) { ASSERT(FALSE); return FALSE; } if (!FlushConsoleInputBuffer(m_hStdIn)) return FALSE; COORD FristCharCursorPosition = m_CursorPosition; #define X_CURSOR_POSITION_FROM_OFFSET(ofs) USHORT(((FristCharCursorPosition.X + ofs)%m_BufferSize.X)) #define Y_CURSOR_POSITION_FROM_OFFSET(ofs) USHORT((FristCharCursorPosition.Y + (FristCharCursorPosition.X + ofs)/m_BufferSize.X)) //#define OFFSET_FROM_CURSOR_POSITION(pos) ((pos.Y-FristCharCursorPosition.Y)*m_BufferSize.X+pos.X-FristCharCursorPosition.X) DWORD dwRecordsReaded; DWORD dwCurrentCharOffset = 0; DWORD dwLastCharOffset = 0; BOOL ret; BOOL blnCompletionMode = FALSE; // unsigned __int64 nCompletionIndex = 0; unsigned long long nCompletionIndex = 0; DWORD dwCompletionOffset = 0; DWORD dwCompletionStringSize = 0; COORD CompletionPosition = FristCharCursorPosition; m_LinesScrolled = 0; BOOL blnOldMoreMode = m_blnMoreMode; m_blnMoreMode = FALSE; DWORD dwHistoryIndex = 0; INPUT_RECORD InputRecord; while ((ret = ReadConsoleInput(m_hStdIn,&InputRecord,1,&dwRecordsReaded)) != FALSE) { ASSERT(dwRecordsReaded == 1); if (dwRecordsReaded != 1) return FALSE; if (InputRecord.EventType != KEY_EVENT) continue; if (!InputRecord.Event.KeyEvent.bKeyDown) continue; #ifdef UNICODE TCHAR ch = InputRecord.Event.KeyEvent.uChar.UnicodeChar; #else TCHAR ch = InputRecord.Event.KeyEvent.uChar.AsciiChar; #endif KeyRepeat: if (m_LinesScrolled) { if (m_LinesScrolled > FristCharCursorPosition.Y) return FALSE; FristCharCursorPosition.Y = SHORT(FristCharCursorPosition.Y - m_LinesScrolled); if (m_LinesScrolled > CompletionPosition.Y) return FALSE; CompletionPosition.Y = SHORT(CompletionPosition.Y - m_LinesScrolled); m_LinesScrolled = 0; } // char Buf[1024]; // sprintf(Buf,"wVirtualKeyCode = %u\nchar = %u\n\n",InputRecord.Event.KeyEvent.wVirtualKeyCode,ch); // OutputDebugString(Buf); #ifndef NO_PASTE if ((ch == 0x16)&&(InputRecord.Event.KeyEvent.wVirtualKeyCode == 'V')) { goto Paste; } else #endif if (ch == 0) { #ifndef NO_PASTE if (InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_INSERT) { if (!(InputRecord.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)) { VERIFY(SetInsertMode(!m_blnInsetMode)); } else { if (blnCompletionMode) blnCompletionMode = FALSE; Paste: if (!IsClipboardFormatAvailable( #ifdef UNICODE CF_UNICODETEXT #else CF_TEXT #endif )) continue; if (!OpenClipboard(NULL)) continue; const TCHAR *pch = NULL; HANDLE hglb = GetClipboardData( #ifdef UNICODE CF_UNICODETEXT #else CF_TEXT #endif ); if (hglb != NULL) { LPTSTR lptstr = (LPTSTR)GlobalLock(hglb); if (lptstr != NULL) { _tcsncpy(m_pchBuffer1,lptstr,m_dwBufferSize); m_pchBuffer1[m_dwBufferSize-1] = 0; pch = m_pchBuffer1; GlobalUnlock(hglb); } } CloseClipboard(); if (pch == NULL) continue; while (*pch) { if (_istprint(*pch)) { if (dwLastCharOffset >= m_dwBufferSize-1) { ASSERT(dwLastCharOffset == m_dwBufferSize-1); // Beep(1000,100); break; } TCHAR ch1; //if (m_blnInsetMode) ch1 = m_pchBuffer[dwCurrentCharOffset]; m_pchBuffer[dwCurrentCharOffset] = *pch; if ((m_blnInsetMode)||(dwCurrentCharOffset == dwLastCharOffset)) dwLastCharOffset++; dwCurrentCharOffset++; if (!Write(pch,1)) return FALSE; if (m_blnInsetMode) { COORD Cursor = m_CursorPosition; DWORD ofs = dwCurrentCharOffset; while(ofs <= dwLastCharOffset) { ch = m_pchBuffer[ofs]; m_pchBuffer[ofs] = ch1; ch1 = ch; ofs++; } if (dwCurrentCharOffset < dwLastCharOffset) { if (!Write(m_pchBuffer+dwCurrentCharOffset,dwLastCharOffset-dwCurrentCharOffset)) return FALSE; if (m_LinesScrolled) { if (m_LinesScrolled > FristCharCursorPosition.Y) return FALSE; Cursor.Y = SHORT(Cursor.Y - m_LinesScrolled); } // Update cursor position m_CursorPosition = Cursor; if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)) return FALSE; } } } pch++; } } } else #endif if (InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_LEFT) { if (blnCompletionMode) blnCompletionMode = FALSE; if (dwCurrentCharOffset) { if (InputRecord.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED)) { TCHAR *pchWordBegin = m_pchBuffer+dwCurrentCharOffset-1; while (pchWordBegin > m_pchBuffer) { if (!_istspace(*pchWordBegin)) break; pchWordBegin--; } while (pchWordBegin > m_pchBuffer) { if (_istspace(*(pchWordBegin-1))) break; pchWordBegin--; } ASSERT(pchWordBegin >= m_pchBuffer); dwCurrentCharOffset = pchWordBegin - m_pchBuffer; ASSERT(dwCurrentCharOffset < dwLastCharOffset); m_CursorPosition.X = X_CURSOR_POSITION_FROM_OFFSET(dwCurrentCharOffset); m_CursorPosition.Y = Y_CURSOR_POSITION_FROM_OFFSET(dwCurrentCharOffset); VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); } else { dwCurrentCharOffset--; if (m_CursorPosition.X) { m_CursorPosition.X--; } else { m_CursorPosition.X = SHORT(m_BufferSize.X-1); ASSERT(m_CursorPosition.Y); m_CursorPosition.Y--; } VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); } } } else if (InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_RIGHT) { if (blnCompletionMode) blnCompletionMode = FALSE; if (dwCurrentCharOffset < dwLastCharOffset) { if (InputRecord.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED)) { TCHAR *pchWordBegin = m_pchBuffer+dwCurrentCharOffset; while ((DWORD)(pchWordBegin - m_pchBuffer) < dwLastCharOffset) { if (_istspace(*pchWordBegin)) break; pchWordBegin++; } while ((DWORD)(pchWordBegin - m_pchBuffer) < dwLastCharOffset) { if (!_istspace(*pchWordBegin)) break; pchWordBegin++; } dwCurrentCharOffset = pchWordBegin - m_pchBuffer; ASSERT(dwCurrentCharOffset <= dwLastCharOffset); m_CursorPosition.X = X_CURSOR_POSITION_FROM_OFFSET(dwCurrentCharOffset); m_CursorPosition.Y = Y_CURSOR_POSITION_FROM_OFFSET(dwCurrentCharOffset); VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); } else { dwCurrentCharOffset++; m_CursorPosition.X++; if (m_CursorPosition.X == m_BufferSize.X) { m_CursorPosition.Y++; m_CursorPosition.X = 0; } VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); } } } else if (InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_HOME) { if (blnCompletionMode) blnCompletionMode = FALSE; dwCurrentCharOffset = 0; m_CursorPosition = FristCharCursorPosition; VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); } else if (InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_END) { if (blnCompletionMode) blnCompletionMode = FALSE; dwCurrentCharOffset = dwLastCharOffset; m_CursorPosition.X = X_CURSOR_POSITION_FROM_OFFSET(dwLastCharOffset); m_CursorPosition.Y = Y_CURSOR_POSITION_FROM_OFFSET(dwLastCharOffset); VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); } else if (InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_UP) { if (blnCompletionMode) blnCompletionMode = FALSE; dwHistoryIndex++; const TCHAR *pchHistoryLine = m_History.GetHistoryLine(dwHistoryIndex-1); if (pchHistoryLine) { if (dwLastCharOffset) { _tcsnset(m_pchBuffer,_T(' '),dwLastCharOffset); m_CursorPosition = FristCharCursorPosition; VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); VERIFY(Write(m_pchBuffer,dwLastCharOffset)); dwCurrentCharOffset = dwLastCharOffset = 0; m_CursorPosition = FristCharCursorPosition; VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); } dwCurrentCharOffset = dwLastCharOffset = _tcslen(pchHistoryLine); if (dwLastCharOffset >= m_dwBufferSize) { ASSERT(FALSE); return FALSE; } _tcscpy(m_pchBuffer,pchHistoryLine); if (!Write(m_pchBuffer)) return FALSE; } else { dwHistoryIndex--; } } else if (InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_DOWN) { if (blnCompletionMode) blnCompletionMode = FALSE; if (dwHistoryIndex) { dwHistoryIndex--; const TCHAR *pchHistoryLine = m_History.GetHistoryLine(dwHistoryIndex-1); if (dwLastCharOffset) { _tcsnset(m_pchBuffer,_T(' '),dwLastCharOffset); m_CursorPosition = FristCharCursorPosition; VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); VERIFY(Write(m_pchBuffer,dwLastCharOffset)); dwCurrentCharOffset = dwLastCharOffset = 0; m_CursorPosition = FristCharCursorPosition; VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); } if (pchHistoryLine) { dwCurrentCharOffset = dwLastCharOffset = _tcslen(pchHistoryLine); if (dwLastCharOffset >= m_dwBufferSize) { ASSERT(FALSE); return FALSE; } _tcscpy(m_pchBuffer,pchHistoryLine); if (!Write(m_pchBuffer)) return FALSE; } } } else if ((InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_DELETE)&& (dwLastCharOffset)) { // Move the characters if any... ASSERT(dwLastCharOffset); DWORD dwCharOffset = dwCurrentCharOffset; if (dwCharOffset < dwLastCharOffset) { while(dwCharOffset < dwLastCharOffset) { m_pchBuffer[dwCharOffset] = m_pchBuffer[dwCharOffset+1]; dwCharOffset++; } m_pchBuffer[dwLastCharOffset-1] = _T(' '); // Save cursor position COORD Cursor = m_CursorPosition; if (!Write(m_pchBuffer+dwCurrentCharOffset,dwLastCharOffset-dwCurrentCharOffset)) return FALSE; dwLastCharOffset--; // Update cursor position m_CursorPosition = Cursor; if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)) return FALSE; } } // else if ((InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_PAUSE)&& // (InputRecord.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))) // { // if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,0)) return FALSE; // } } else if ((ch == 27) && dwLastCharOffset && (InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)) { if (blnCompletionMode) blnCompletionMode = FALSE; _tcsnset(m_pchBuffer,_T(' '),dwLastCharOffset); m_CursorPosition = FristCharCursorPosition; VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); VERIFY(Write(m_pchBuffer,dwLastCharOffset)); dwCurrentCharOffset = dwLastCharOffset = 0; m_CursorPosition = FristCharCursorPosition; VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); } else if (ch == _T('\r')) { // carriage return if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition = FristCharCursorPosition)) return FALSE; ASSERT(dwLastCharOffset <= m_dwBufferSize); m_pchBuffer[dwLastCharOffset] = 0; // terminate string in buffer ret = Write(m_pchBuffer); m_History.AddHistoryLine(m_pchBuffer); static TCHAR strLF[] = _T("\n"); ret = Write(strLF); break; } else if (ch == _T('\b')) { // backspace if (blnCompletionMode) blnCompletionMode = FALSE; if ((dwCurrentCharOffset) && ((m_CursorPosition.X != 0) || (m_CursorPosition.Y != 0))) { // Calculate new cursor position COORD NewCursorPosition; if (m_CursorPosition.X) { NewCursorPosition.X = SHORT(m_CursorPosition.X-1); NewCursorPosition.Y = m_CursorPosition.Y; } else { ASSERT(m_BufferSize.X); NewCursorPosition.X = SHORT(m_BufferSize.X-1); ASSERT(m_CursorPosition.Y); NewCursorPosition.Y = SHORT(m_CursorPosition.Y-1); } // Move the characters if any... ASSERT(dwLastCharOffset); DWORD dwCharOffset = dwCurrentCharOffset-1; while(dwCharOffset < dwLastCharOffset-1) { m_pchBuffer[dwCharOffset] = m_pchBuffer[dwCharOffset+1]; dwCharOffset++; } m_pchBuffer[dwLastCharOffset-1] = _T(' '); dwCurrentCharOffset--; m_CursorPosition = NewCursorPosition; if (!Write(m_pchBuffer+dwCurrentCharOffset,dwLastCharOffset-dwCurrentCharOffset)) return FALSE; // Update cursor position m_CursorPosition = NewCursorPosition; if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)) return FALSE; dwLastCharOffset--; } } else if (ch == _T('\t')) { // Tab if (!blnCompletionMode) // If tab was pressed after non-tab. We enter in completion mode. { // Initialize completion index if (InputRecord.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) // If shift was pressed nCompletionIndex = (unsigned long long) -1; // Last completion else nCompletionIndex = 0; // First completion // Find completion offset. It points at char after first non-quoted whitespace. dwCompletionOffset = dwCurrentCharOffset; BOOL blnQuotedParameter = FALSE; while(dwCompletionOffset) { dwCompletionOffset--; if (m_pchBuffer[dwCompletionOffset] == _T('\"')) { blnQuotedParameter = !blnQuotedParameter; } else if (!blnQuotedParameter && _istspace(m_pchBuffer[dwCompletionOffset])) { // Found ! We are not inside quored parameter and we are on whitespace. dwCompletionOffset++; // dwCompletionOffset must point at char AFTER first non-quoted whitespace. break; } } ASSERT(dwCompletionOffset <= dwCurrentCharOffset); // Save not changing part (context) of completion in m_pchBuffer1 _tcsncpy(m_pchBuffer1,m_pchBuffer,dwCompletionOffset); m_pchBuffer1[dwCompletionOffset] = 0; // Size of changing part dwCompletionStringSize = dwCurrentCharOffset-dwCompletionOffset; // Save intial changing part of completion in m_pchBuffer2 if (dwCompletionStringSize) _tcsncpy(m_pchBuffer2,m_pchBuffer+dwCompletionOffset,dwCompletionStringSize); m_pchBuffer2[dwCompletionStringSize] = 0; // Calculate cursor position of point between changing and not changing ports CompletionPosition.X = X_CURSOR_POSITION_FROM_OFFSET(dwCompletionOffset); CompletionPosition.Y = Y_CURSOR_POSITION_FROM_OFFSET(dwCompletionOffset); } // if first time tab const TCHAR *pchCompletion = NULL; // Direction BOOL blnForward = !(InputRecord.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED); if (m_pfReplaceCompletionCallback) // If we are using replace completion callback pchCompletion = m_pfReplaceCompletionCallback(nCompletionIndex, blnCompletionMode?&blnForward:NULL, // If this is first time we call the completion callback, do not change completion index m_pchBuffer1,m_pchBuffer2); if (pchCompletion) // If completion found { // Set cursor position to compeltion position m_CursorPosition = CompletionPosition; if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)) return FALSE; // Calculate buffer free space ASSERT(m_dwBufferSize > dwCompletionOffset); DWORD dwFree = m_dwBufferSize - dwCompletionOffset - 1; // Save old completion string size DWORD dwOldCompletionStringSize = dwCompletionStringSize; // Write completion string to buffer dwCompletionStringSize = _tcslen(pchCompletion); // If there is not enough space in buffer, so we truncate the completion if (dwCompletionStringSize > dwFree) dwCompletionStringSize = dwFree; if (dwCompletionStringSize) { // Copy competion into main buffer _tcsncpy(m_pchBuffer+dwCompletionOffset,pchCompletion,dwCompletionStringSize); // Write completion string to console if (!Write(m_pchBuffer+dwCompletionOffset,dwCompletionStringSize)) return FALSE; // Set new offsets dwCurrentCharOffset = dwLastCharOffset = dwCompletionOffset + dwCompletionStringSize; ASSERT(dwLastCharOffset < m_dwBufferSize); } // Erase rest from previous completion string, if the new completion is shorter than old if (dwOldCompletionStringSize > dwCompletionStringSize) { _tcsnset(m_pchBuffer+dwCompletionOffset+dwCompletionStringSize,_T(' '), dwOldCompletionStringSize - dwCompletionStringSize); // Save cursor position COORD pos = m_CursorPosition; if (!Write(m_pchBuffer+dwCompletionOffset+dwCompletionStringSize, dwOldCompletionStringSize - dwCompletionStringSize)) return FALSE; // Set cursor position m_CursorPosition = pos; if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)) return FALSE; } } // If completion found // Ok, we are in completion mode blnCompletionMode = TRUE; } else if (_istprint(ch)) { if (blnCompletionMode) blnCompletionMode = FALSE; if (dwLastCharOffset >= m_dwBufferSize-1) { ASSERT(dwLastCharOffset == m_dwBufferSize-1); // Beep(1000,100); continue; } TCHAR ch1; //if (m_blnInsetMode) ch1 = m_pchBuffer[dwCurrentCharOffset]; m_pchBuffer[dwCurrentCharOffset] = ch; if ((m_blnInsetMode)||(dwCurrentCharOffset == dwLastCharOffset)) dwLastCharOffset++; dwCurrentCharOffset++; if (!Write(&ch,1)) return FALSE; if (m_blnInsetMode) { COORD Cursor = m_CursorPosition; DWORD ofs = dwCurrentCharOffset; while(ofs <= dwLastCharOffset) { ch = m_pchBuffer[ofs]; m_pchBuffer[ofs] = ch1; ch1 = ch; ofs++; } if (dwCurrentCharOffset < dwLastCharOffset) { if (!Write(m_pchBuffer+dwCurrentCharOffset,dwLastCharOffset-dwCurrentCharOffset)) return FALSE; if (m_LinesScrolled) { if (m_LinesScrolled > FristCharCursorPosition.Y) return FALSE; Cursor.Y = SHORT(Cursor.Y - m_LinesScrolled); } // Update cursor position m_CursorPosition = Cursor; if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)) return FALSE; } } } ASSERT(InputRecord.Event.KeyEvent.wRepeatCount); if (!InputRecord.Event.KeyEvent.wRepeatCount) return FALSE; if (--InputRecord.Event.KeyEvent.wRepeatCount) goto KeyRepeat; } m_blnMoreMode = blnOldMoreMode; return TRUE; } BOOL CConsole::GetTextAttribute(WORD& rwAttributes) { rwAttributes = m_wAttributes; return TRUE; } // Parameters: // dwBufferSize - size in chars of the input line buffer // // Rerturns: // NULL - Failed. // pointer to the input buffer TCHAR * CConsole::Init(DWORD dwBufferSize, DWORD dwMaxHistoryLines) { if (m_hStdIn != INVALID_HANDLE_VALUE) VERIFY(CloseHandle(m_hStdIn)); if (m_hStdOut != INVALID_HANDLE_VALUE) VERIFY(CloseHandle(m_hStdOut)); m_hStdIn = GetStdHandle(STD_INPUT_HANDLE); if (m_hStdIn == INVALID_HANDLE_VALUE) { // _ftprintf(stderr,_T("GetStdHandle(STD_INPUT_HANDLE) failed. GetLastError return 0x%X\n"),(unsigned)GetLastError()); goto Abort; } m_hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); if (m_hStdOut == INVALID_HANDLE_VALUE) { // _ftprintf(stderr,_T("GetStdHandle(STD_OUTPUT_HANDLE) failed. GetLastError return 0x%X\n"),(unsigned)GetLastError()); goto Abort; } CONSOLE_SCREEN_BUFFER_INFO info; if (!GetConsoleScreenBufferInfo(m_hStdOut,&info)) { // _ftprintf(stderr,_T("GetConsoleScreenBufferInfo(m_hStdOut,&info) failed. GetLastError return 0x%X\n"),(unsigned)GetLastError()); if (GetLastError() == 6) // redirected output _ftprintf(stderr,_T("Redirection is not supported.\n")); goto Abort; } m_wAttributes = info.wAttributes; if (!m_blnOldInputModeSaved) { if (!GetConsoleMode(m_hStdIn,&m_dwOldInputMode)) { if (GetLastError() == 6) // redirected input _ftprintf(stderr,_T("Redirection is not supported.\n")); // _ftprintf(stderr,_T("GetConsoleMode(0x%X,&m_dwOldINputMode) failed. GetLastError() returns 0x%X\n"),(int)m_hStdIn,GetLastError()); goto Abort; } m_blnOldInputModeSaved = TRUE; } // _ftprintf(stderr,_T("Calling GetConsoleMode(0x%X,&m_dwOldOutputMode) ...\n"),(int)m_hStdOut); if (!m_blnOldOutputModeSaved) { if (!GetConsoleMode(m_hStdOut,&m_dwOldOutputMode)) goto Abort; // _ftprintf(stderr,_T("Calling GetConsoleMode(0x%X,&m_dwOldOutputMode) done.\n"),(int)m_hStdOut); m_blnOldOutputModeSaved = TRUE; } // _ftprintf(stderr,_T("Calling SetConsoleMode(0x%X,0) ...\n"),(int)m_hStdIn); if (!SetConsoleMode(m_hStdIn,0)) goto Abort; if (!SetConsoleMode(m_hStdOut,0)) goto Abort; m_CursorPosition = info.dwCursorPosition; m_BufferSize = info.dwSize; CONSOLE_CURSOR_INFO cci; cci.bVisible = TRUE; cci.dwSize = m_blnInsetMode?m_dwInsertModeCursorHeight:m_dwOverwriteModeCursorHeight; if (!SetConsoleCursorInfo(m_hStdOut,&cci)) goto Abort; m_dwBufferSize = dwBufferSize; if (m_pchBuffer) delete m_pchBuffer; m_pchBuffer = NULL; if (m_pchBuffer1) delete m_pchBuffer1; m_pchBuffer1 = NULL; if (m_pchBuffer2) delete m_pchBuffer2; m_pchBuffer2 = NULL; m_pchBuffer = new (std::nothrow) TCHAR [dwBufferSize]; if (!m_pchBuffer) goto Abort; m_pchBuffer[dwBufferSize-1] = 0; m_pchBuffer1 = new (std::nothrow) TCHAR [dwBufferSize]; if (!m_pchBuffer1) goto Abort; m_pchBuffer1[dwBufferSize-1] = 0; m_pchBuffer2 = new (std::nothrow) TCHAR [dwBufferSize]; if (!m_pchBuffer2) goto Abort; m_pchBuffer2[dwBufferSize-1] = 0; if (dwMaxHistoryLines) { if (!m_History.Init(dwBufferSize,dwMaxHistoryLines)) goto Abort; } return m_pchBuffer; Abort: if (m_hStdIn != INVALID_HANDLE_VALUE) VERIFY(CloseHandle(m_hStdIn)); m_hStdIn = INVALID_HANDLE_VALUE; if (m_hStdOut != INVALID_HANDLE_VALUE) VERIFY(CloseHandle(m_hStdOut)); m_hStdOut = INVALID_HANDLE_VALUE; if (m_pchBuffer) delete[] m_pchBuffer; m_pchBuffer = NULL; if (m_pchBuffer1) delete[] m_pchBuffer1; m_pchBuffer1 = NULL; if (m_pchBuffer2) delete[] m_pchBuffer2; m_pchBuffer2 = NULL; m_dwBufferSize = 0; return NULL; } BOOL CConsole::WriteChar(TCHAR ch) { CHAR_INFO ci; ci.Attributes = m_wAttributes; #ifdef UNICODE ci.Char.UnicodeChar = ch; #else ci.Char.AsciiChar = ch; #endif static COORD BufferSize = {1,1}; static COORD BufferCoord = {0,0}; SMALL_RECT Dest; Dest.Bottom = Dest.Top = m_CursorPosition.Y; Dest.Left = Dest.Right = m_CursorPosition.X; return WriteConsoleOutput(m_hStdOut,&ci,BufferSize,BufferCoord,&Dest); } void CConsole::BeginScrollingOperation() { m_Lines = 0; } BOOL CConsole::WriteString(const TCHAR *pchString, COORD Position) { CHAR_INFO ciBuffer[256]; int nSize = _tcslen(pchString); if ((nSize > 256)||(nSize <= 0)) { ASSERT(FALSE); return FALSE; } COORD BufferSize; BufferSize.X = (SHORT)nSize; BufferSize.Y = 1; static COORD BufferCoord = {0,0}; SMALL_RECT Dest; Dest.Bottom = Dest.Top = Position.Y; Dest.Right = SHORT((Dest.Left = Position.X) + nSize - 1); while(nSize--) { ciBuffer[nSize].Attributes = m_wAttributes; #ifdef UNICODE ciBuffer[nSize].Char.UnicodeChar = pchString[nSize]; #else ciBuffer[nSize].Char.AsciiChar = pchString[nSize]; #endif } return WriteConsoleOutput(m_hStdOut,ciBuffer,BufferSize,BufferCoord,&Dest); } BOOL CConsole::SetInsertMode(BOOL blnInsetMode) { if (m_hStdOut == INVALID_HANDLE_VALUE) return FALSE; CONSOLE_CURSOR_INFO cci; cci.bVisible = TRUE; cci.dwSize = blnInsetMode?m_dwInsertModeCursorHeight:m_dwOverwriteModeCursorHeight; BOOL ret = SetConsoleCursorInfo(m_hStdOut,&cci); if (ret) m_blnInsetMode = blnInsetMode; return ret; } void CConsole::SetReplaceCompletionCallback(ReplaceCompletionCallback pfCallback) { m_pfReplaceCompletionCallback = pfCallback; } void CConsole::DisableWrite() { m_blnDisableWrite = TRUE; INPUT_RECORD InputRecord; DWORD dwRecordsWriten; InputRecord.EventType = KEY_EVENT; InputRecord.Event.KeyEvent.bKeyDown = TRUE; #ifdef UNICODE InputRecord.Event.KeyEvent.uChar.UnicodeChar = L' '; #else InputRecord.Event.KeyEvent.uChar.AsciiChar = ' '; #endif BOOL ret = WriteConsoleInput(m_hStdIn,&InputRecord,1,&dwRecordsWriten); ASSERT(ret); } void CConsole::EnableWrite() { m_blnDisableWrite = FALSE; }