1 /* 2 * regexpl - Console Registry Explorer 3 * 4 * Copyright (C) 2000-2005 Nedko Arnaudov <nedko@users.sourceforge.net> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; see the file COPYING. If not, write to 18 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 19 * Boston, MA 02111-1307, USA. 20 */ 21 22 // Console.cpp: implementation of the CConsole class. 23 // 24 ////////////////////////////////////////////////////////////////////// 25 26 #include "ph.h" 27 #include "Console.h" 28 29 #define TAB_WIDTH 8 30 #define MORE_STRING _T("-- Press space to view more. Press q or Ctrl+break to cancel.--") 31 #define MORE_EMPTY_STRING _T(" ") 32 33 /* 34 TCHAR * _tcsnchr(const TCHAR *string, TCHAR ch, int count) 35 { 36 while (count--) 37 { 38 if (*string == 0) return NULL; 39 if (*string == ch) return const_cast <char *>(string); 40 string++; 41 } 42 return NULL; 43 }*/ 44 45 46 ////////////////////////////////////////////////////////////////////// 47 // Construction/Destruction 48 ////////////////////////////////////////////////////////////////////// 49 50 CConsole::CConsole() 51 { 52 m_hStdIn = INVALID_HANDLE_VALUE; 53 m_hStdOut = INVALID_HANDLE_VALUE; 54 m_blnInsetMode = TRUE; // Insert 55 // m_blnInsetMode = FALSE; // Overwrite 56 m_dwInsertModeCursorHeight = 15; 57 m_dwOverwriteModeCursorHeight = 100; 58 // m_Lines = 0; 59 m_pchBuffer = NULL; 60 m_pchBuffer1 = NULL; 61 m_pchBuffer2 = NULL; 62 m_pfReplaceCompletionCallback = NULL; 63 m_blnMoreMode = TRUE; 64 m_dwOldInputMode = 0; 65 m_dwOldOutputMode = 0; 66 m_blnOldInputModeSaved = FALSE; 67 m_blnOldOutputModeSaved = FALSE; 68 } 69 70 CConsole::~CConsole() 71 { 72 if (m_pchBuffer) 73 delete[] m_pchBuffer; 74 if (m_pchBuffer1) 75 delete[] m_pchBuffer1; 76 if (m_pchBuffer2) 77 delete[] m_pchBuffer2; 78 79 if (m_blnOldInputModeSaved) 80 SetConsoleMode(m_hStdIn,m_dwOldInputMode); 81 if (m_blnOldOutputModeSaved) 82 SetConsoleMode(m_hStdOut,m_dwOldOutputMode); 83 84 if (m_hStdIn != INVALID_HANDLE_VALUE) 85 VERIFY(CloseHandle(m_hStdIn)); 86 if (m_hStdOut != INVALID_HANDLE_VALUE) 87 VERIFY(CloseHandle(m_hStdOut)); 88 } 89 90 BOOL CConsole::Write(const TCHAR *p, DWORD dwChars) 91 { 92 if (m_hStdOut == INVALID_HANDLE_VALUE) 93 return FALSE; 94 if (m_hStdIn == INVALID_HANDLE_VALUE) 95 return FALSE; 96 if (p == NULL) 97 { 98 ASSERT(FALSE); 99 return FALSE; 100 } 101 DWORD dwCharsToWrite = (dwChars)?dwChars:_tcslen(p); 102 DWORD dwCharsWrittenAdd = 0; 103 BOOL ret = TRUE; 104 while (dwCharsToWrite && (!m_blnDisableWrite)) 105 { 106 switch(p[dwCharsWrittenAdd]) 107 { 108 case _T('\n'): 109 m_CursorPosition.Y++; 110 m_CursorPosition.X = 0; 111 break; 112 case _T('\r'): 113 dwCharsWrittenAdd++; 114 dwCharsToWrite--; 115 continue; 116 case _T('\t'): 117 do 118 { 119 if (!Write(_T(" "))) return FALSE; 120 } 121 while ((m_CursorPosition.X % TAB_WIDTH) && (!m_blnDisableWrite)); 122 dwCharsWrittenAdd++; 123 dwCharsToWrite--; 124 continue; 125 default: 126 { 127 if (!WriteChar(p[dwCharsWrittenAdd])) return FALSE; 128 m_CursorPosition.X++; 129 } 130 } 131 if (m_CursorPosition.X == m_BufferSize.X) 132 { 133 m_CursorPosition.Y++; 134 m_CursorPosition.X = 0; 135 } 136 if (m_CursorPosition.Y == m_BufferSize.Y) 137 { 138 ASSERT(m_CursorPosition.X == 0); 139 SMALL_RECT Src; 140 Src.Left = 0; 141 Src.Right = (SHORT)(m_BufferSize.X-1); 142 Src.Top = 1; 143 Src.Bottom = (SHORT)(m_BufferSize.Y-1); 144 CHAR_INFO ci; 145 #ifdef UNICODE 146 ci.Char.UnicodeChar = L' '; 147 #else 148 ci.Char.AsciiChar = ' '; 149 #endif 150 ci.Attributes = 0; 151 COORD Dest; 152 Dest.X = 0; 153 Dest.Y = 0; 154 if (!ScrollConsoleScreenBuffer(m_hStdOut,&Src,NULL,Dest,&ci)) return FALSE; 155 m_CursorPosition.Y--; 156 m_LinesScrolled++; 157 } 158 if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)) return FALSE; 159 VERIFY(WriteChar(_T(' '))); 160 if ((m_blnMoreMode)&&(m_CursorPosition.X == 0)) 161 { 162 m_Lines++; 163 if (m_Lines >= m_BufferSize.Y-1) 164 { 165 ASSERT(m_Lines == m_BufferSize.Y-1); 166 m_Lines = 0; 167 VERIFY(WriteString(MORE_STRING,m_CursorPosition)); 168 VERIFY(FlushInputBuffer()); 169 170 CONSOLE_CURSOR_INFO cci; 171 cci.bVisible = FALSE; 172 cci.dwSize = 100; 173 VERIFY(SetConsoleCursorInfo(m_hStdOut,&cci)); 174 175 INPUT_RECORD InputRecord; 176 DWORD dwRecordsReaded; 177 while ((ret = ReadConsoleInput(m_hStdIn,&InputRecord,1,&dwRecordsReaded)) != FALSE) 178 { 179 ASSERT(dwRecordsReaded == 1); 180 if (dwRecordsReaded != 1) 181 break; 182 if (InputRecord.EventType != KEY_EVENT) 183 continue; 184 if (!InputRecord.Event.KeyEvent.bKeyDown) 185 continue; 186 187 if ((InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_CANCEL)|| 188 (InputRecord.Event.KeyEvent.wVirtualKeyCode == _T('Q'))) 189 { 190 VERIFY(GenerateConsoleCtrlEvent(CTRL_C_EVENT,0)); 191 continue; 192 } 193 #ifdef UNICODE 194 TCHAR ch = InputRecord.Event.KeyEvent.uChar.UnicodeChar; 195 #else 196 TCHAR ch = InputRecord.Event.KeyEvent.uChar.AsciiChar; 197 #endif 198 if (ch) 199 break; 200 } 201 202 // delete "more" msg 203 VERIFY(WriteString(MORE_EMPTY_STRING,m_CursorPosition)); 204 m_CursorPosition.X = 0; 205 206 cci.bVisible = TRUE; 207 cci.dwSize = m_blnInsetMode?m_dwInsertModeCursorHeight:m_dwOverwriteModeCursorHeight; 208 VERIFY(SetConsoleCursorInfo(m_hStdOut,&cci)); 209 } 210 } 211 dwCharsWrittenAdd++; 212 dwCharsToWrite--; 213 } 214 return ret; 215 } 216 217 unsigned int CConsole::GetTabWidth() 218 { 219 return TAB_WIDTH; 220 } 221 222 BOOL CConsole::SetTitle(const TCHAR *p) 223 { 224 return SetConsoleTitle(p); 225 } 226 227 BOOL CConsole::SetTextAttribute(WORD wAttributes) 228 { 229 m_wAttributes = wAttributes; 230 return TRUE; 231 } 232 /* 233 BOOL CConsole::SetInputMode(DWORD dwMode) 234 { 235 return SetConsoleMode(m_hStdIn,dwMode); 236 } 237 238 BOOL CConsole::SetOutputMode(DWORD dwMode) 239 { 240 return SetConsoleMode(m_hStdOut,dwMode); 241 }*/ 242 243 BOOL CConsole::FlushInputBuffer() 244 { 245 if (m_hStdIn == INVALID_HANDLE_VALUE) return FALSE; 246 return FlushConsoleInputBuffer(m_hStdIn); 247 } 248 249 BOOL CConsole::ReadLine() 250 { 251 if (m_hStdIn == INVALID_HANDLE_VALUE) return FALSE; 252 if (m_hStdOut == INVALID_HANDLE_VALUE) return FALSE; 253 if (m_dwBufferSize == 0) 254 { 255 ASSERT(FALSE); 256 return FALSE; 257 } 258 if (m_pchBuffer == NULL) 259 { 260 ASSERT(FALSE); 261 return FALSE; 262 } 263 if (m_pchBuffer1 == NULL) 264 { 265 ASSERT(FALSE); 266 return FALSE; 267 } 268 if (!FlushConsoleInputBuffer(m_hStdIn)) return FALSE; 269 270 COORD FristCharCursorPosition = m_CursorPosition; 271 #define X_CURSOR_POSITION_FROM_OFFSET(ofs) USHORT(((FristCharCursorPosition.X + ofs)%m_BufferSize.X)) 272 #define Y_CURSOR_POSITION_FROM_OFFSET(ofs) USHORT((FristCharCursorPosition.Y + (FristCharCursorPosition.X + ofs)/m_BufferSize.X)) 273 //#define OFFSET_FROM_CURSOR_POSITION(pos) ((pos.Y-FristCharCursorPosition.Y)*m_BufferSize.X+pos.X-FristCharCursorPosition.X) 274 275 DWORD dwRecordsReaded; 276 DWORD dwCurrentCharOffset = 0; 277 DWORD dwLastCharOffset = 0; 278 BOOL ret; 279 280 BOOL blnCompletionMode = FALSE; 281 // unsigned __int64 nCompletionIndex = 0; 282 unsigned long long nCompletionIndex = 0; 283 DWORD dwCompletionOffset = 0; 284 DWORD dwCompletionStringSize = 0; 285 COORD CompletionPosition = FristCharCursorPosition; 286 287 m_LinesScrolled = 0; 288 BOOL blnOldMoreMode = m_blnMoreMode; 289 m_blnMoreMode = FALSE; 290 291 DWORD dwHistoryIndex = 0; 292 293 INPUT_RECORD InputRecord; 294 while ((ret = ReadConsoleInput(m_hStdIn,&InputRecord,1,&dwRecordsReaded)) != FALSE) 295 { 296 ASSERT(dwRecordsReaded == 1); 297 if (dwRecordsReaded != 1) return FALSE; 298 if (InputRecord.EventType != KEY_EVENT) continue; 299 if (!InputRecord.Event.KeyEvent.bKeyDown) continue; 300 #ifdef UNICODE 301 TCHAR ch = InputRecord.Event.KeyEvent.uChar.UnicodeChar; 302 #else 303 TCHAR ch = InputRecord.Event.KeyEvent.uChar.AsciiChar; 304 #endif 305 KeyRepeat: 306 if (m_LinesScrolled) 307 { 308 if (m_LinesScrolled > FristCharCursorPosition.Y) return FALSE; 309 FristCharCursorPosition.Y = SHORT(FristCharCursorPosition.Y - m_LinesScrolled); 310 if (m_LinesScrolled > CompletionPosition.Y) return FALSE; 311 CompletionPosition.Y = SHORT(CompletionPosition.Y - m_LinesScrolled); 312 m_LinesScrolled = 0; 313 } 314 // char Buf[1024]; 315 // sprintf(Buf,"wVirtualKeyCode = %u\nchar = %u\n\n",InputRecord.Event.KeyEvent.wVirtualKeyCode,ch); 316 // OutputDebugString(Buf); 317 318 #ifndef NO_PASTE 319 if ((ch == 0x16)&&(InputRecord.Event.KeyEvent.wVirtualKeyCode == 'V')) 320 { 321 goto Paste; 322 } 323 else 324 #endif 325 if (ch == 0) 326 { 327 #ifndef NO_PASTE 328 if (InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_INSERT) 329 { 330 if (!(InputRecord.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)) 331 { 332 VERIFY(SetInsertMode(!m_blnInsetMode)); 333 } 334 else 335 { 336 if (blnCompletionMode) blnCompletionMode = FALSE; 337 338 Paste: 339 if (!IsClipboardFormatAvailable( 340 #ifdef UNICODE 341 CF_UNICODETEXT 342 #else 343 CF_TEXT 344 #endif 345 )) 346 continue; 347 if (!OpenClipboard(NULL)) 348 continue; 349 350 const TCHAR *pch = NULL; 351 352 HANDLE hglb = GetClipboardData( 353 #ifdef UNICODE 354 CF_UNICODETEXT 355 #else 356 CF_TEXT 357 #endif 358 ); 359 if (hglb != NULL) 360 { 361 LPTSTR lptstr = (LPTSTR)GlobalLock(hglb); 362 if (lptstr != NULL) 363 { 364 _tcsncpy(m_pchBuffer1,lptstr,m_dwBufferSize); 365 m_pchBuffer1[m_dwBufferSize-1] = 0; 366 pch = m_pchBuffer1; 367 GlobalUnlock(hglb); 368 } 369 } 370 CloseClipboard(); 371 372 if (pch == NULL) continue; 373 374 while (*pch) 375 { 376 if (_istprint(*pch)) 377 { 378 if (dwLastCharOffset >= m_dwBufferSize-1) 379 { 380 ASSERT(dwLastCharOffset == m_dwBufferSize-1); 381 // Beep(1000,100); 382 break; 383 } 384 TCHAR ch1; 385 //if (m_blnInsetMode) 386 ch1 = m_pchBuffer[dwCurrentCharOffset]; 387 m_pchBuffer[dwCurrentCharOffset] = *pch; 388 if ((m_blnInsetMode)||(dwCurrentCharOffset == dwLastCharOffset)) dwLastCharOffset++; 389 dwCurrentCharOffset++; 390 if (!Write(pch,1)) return FALSE; 391 if (m_blnInsetMode) 392 { 393 COORD Cursor = m_CursorPosition; 394 DWORD ofs = dwCurrentCharOffset; 395 396 while(ofs <= dwLastCharOffset) 397 { 398 ch = m_pchBuffer[ofs]; 399 m_pchBuffer[ofs] = ch1; 400 ch1 = ch; 401 ofs++; 402 } 403 404 if (dwCurrentCharOffset < dwLastCharOffset) 405 { 406 if (!Write(m_pchBuffer+dwCurrentCharOffset,dwLastCharOffset-dwCurrentCharOffset)) return FALSE; 407 408 if (m_LinesScrolled) 409 { 410 if (m_LinesScrolled > FristCharCursorPosition.Y) return FALSE; 411 Cursor.Y = SHORT(Cursor.Y - m_LinesScrolled); 412 } 413 // Update cursor position 414 m_CursorPosition = Cursor; 415 if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)) return FALSE; 416 } 417 } 418 } 419 pch++; 420 } 421 } 422 } 423 else 424 #endif 425 if (InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_LEFT) 426 { 427 if (blnCompletionMode) blnCompletionMode = FALSE; 428 if (dwCurrentCharOffset) 429 { 430 if (InputRecord.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED)) 431 { 432 TCHAR *pchWordBegin = m_pchBuffer+dwCurrentCharOffset-1; 433 434 while (pchWordBegin > m_pchBuffer) 435 { 436 if (!_istspace(*pchWordBegin)) break; 437 pchWordBegin--; 438 } 439 440 while (pchWordBegin > m_pchBuffer) 441 { 442 if (_istspace(*(pchWordBegin-1))) break; 443 pchWordBegin--; 444 } 445 446 ASSERT(pchWordBegin >= m_pchBuffer); 447 dwCurrentCharOffset = pchWordBegin - m_pchBuffer; 448 449 ASSERT(dwCurrentCharOffset < dwLastCharOffset); 450 451 m_CursorPosition.X = X_CURSOR_POSITION_FROM_OFFSET(dwCurrentCharOffset); 452 m_CursorPosition.Y = Y_CURSOR_POSITION_FROM_OFFSET(dwCurrentCharOffset); 453 VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); 454 } 455 else 456 { 457 dwCurrentCharOffset--; 458 if (m_CursorPosition.X) 459 { 460 m_CursorPosition.X--; 461 } 462 else 463 { 464 m_CursorPosition.X = SHORT(m_BufferSize.X-1); 465 ASSERT(m_CursorPosition.Y); 466 m_CursorPosition.Y--; 467 } 468 VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); 469 } 470 } 471 } 472 else if (InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_RIGHT) 473 { 474 if (blnCompletionMode) blnCompletionMode = FALSE; 475 if (dwCurrentCharOffset < dwLastCharOffset) 476 { 477 if (InputRecord.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED)) 478 { 479 TCHAR *pchWordBegin = m_pchBuffer+dwCurrentCharOffset; 480 481 while ((DWORD)(pchWordBegin - m_pchBuffer) < dwLastCharOffset) 482 { 483 if (_istspace(*pchWordBegin)) break; 484 pchWordBegin++; 485 } 486 487 while ((DWORD)(pchWordBegin - m_pchBuffer) < dwLastCharOffset) 488 { 489 if (!_istspace(*pchWordBegin)) break; 490 pchWordBegin++; 491 } 492 493 dwCurrentCharOffset = pchWordBegin - m_pchBuffer; 494 ASSERT(dwCurrentCharOffset <= dwLastCharOffset); 495 m_CursorPosition.X = X_CURSOR_POSITION_FROM_OFFSET(dwCurrentCharOffset); 496 m_CursorPosition.Y = Y_CURSOR_POSITION_FROM_OFFSET(dwCurrentCharOffset); 497 VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); 498 } 499 else 500 { 501 dwCurrentCharOffset++; 502 m_CursorPosition.X++; 503 if (m_CursorPosition.X == m_BufferSize.X) 504 { 505 m_CursorPosition.Y++; 506 m_CursorPosition.X = 0; 507 } 508 VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); 509 } 510 } 511 } 512 else if (InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_HOME) 513 { 514 if (blnCompletionMode) blnCompletionMode = FALSE; 515 dwCurrentCharOffset = 0; 516 m_CursorPosition = FristCharCursorPosition; 517 VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); 518 } 519 else if (InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_END) 520 { 521 if (blnCompletionMode) blnCompletionMode = FALSE; 522 dwCurrentCharOffset = dwLastCharOffset; 523 m_CursorPosition.X = X_CURSOR_POSITION_FROM_OFFSET(dwLastCharOffset); 524 m_CursorPosition.Y = Y_CURSOR_POSITION_FROM_OFFSET(dwLastCharOffset); 525 VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); 526 } 527 else if (InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_UP) 528 { 529 if (blnCompletionMode) blnCompletionMode = FALSE; 530 dwHistoryIndex++; 531 const TCHAR *pchHistoryLine = m_History.GetHistoryLine(dwHistoryIndex-1); 532 if (pchHistoryLine) 533 { 534 if (dwLastCharOffset) 535 { 536 _tcsnset(m_pchBuffer,_T(' '),dwLastCharOffset); 537 m_CursorPosition = FristCharCursorPosition; 538 VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); 539 VERIFY(Write(m_pchBuffer,dwLastCharOffset)); 540 dwCurrentCharOffset = dwLastCharOffset = 0; 541 m_CursorPosition = FristCharCursorPosition; 542 VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); 543 } 544 dwCurrentCharOffset = dwLastCharOffset = _tcslen(pchHistoryLine); 545 if (dwLastCharOffset >= m_dwBufferSize) 546 { 547 ASSERT(FALSE); 548 return FALSE; 549 } 550 _tcscpy(m_pchBuffer,pchHistoryLine); 551 if (!Write(m_pchBuffer)) return FALSE; 552 } 553 else 554 { 555 dwHistoryIndex--; 556 } 557 } 558 else if (InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_DOWN) 559 { 560 if (blnCompletionMode) blnCompletionMode = FALSE; 561 if (dwHistoryIndex) 562 { 563 dwHistoryIndex--; 564 const TCHAR *pchHistoryLine = m_History.GetHistoryLine(dwHistoryIndex-1); 565 if (dwLastCharOffset) 566 { 567 _tcsnset(m_pchBuffer,_T(' '),dwLastCharOffset); 568 m_CursorPosition = FristCharCursorPosition; 569 VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); 570 VERIFY(Write(m_pchBuffer,dwLastCharOffset)); 571 dwCurrentCharOffset = dwLastCharOffset = 0; 572 m_CursorPosition = FristCharCursorPosition; 573 VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); 574 } 575 if (pchHistoryLine) 576 { 577 dwCurrentCharOffset = dwLastCharOffset = _tcslen(pchHistoryLine); 578 if (dwLastCharOffset >= m_dwBufferSize) 579 { 580 ASSERT(FALSE); 581 return FALSE; 582 } 583 _tcscpy(m_pchBuffer,pchHistoryLine); 584 if (!Write(m_pchBuffer)) return FALSE; 585 } 586 } 587 } 588 else if ((InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_DELETE)&& 589 (dwLastCharOffset)) 590 { 591 // Move the characters if any... 592 ASSERT(dwLastCharOffset); 593 DWORD dwCharOffset = dwCurrentCharOffset; 594 if (dwCharOffset < dwLastCharOffset) 595 { 596 while(dwCharOffset < dwLastCharOffset) 597 { 598 m_pchBuffer[dwCharOffset] = m_pchBuffer[dwCharOffset+1]; 599 dwCharOffset++; 600 } 601 602 m_pchBuffer[dwLastCharOffset-1] = _T(' '); 603 604 // Save cursor position 605 COORD Cursor = m_CursorPosition; 606 607 if (!Write(m_pchBuffer+dwCurrentCharOffset,dwLastCharOffset-dwCurrentCharOffset)) return FALSE; 608 609 dwLastCharOffset--; 610 611 // Update cursor position 612 m_CursorPosition = Cursor; 613 if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)) return FALSE; 614 } 615 616 } 617 // else if ((InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_PAUSE)&& 618 // (InputRecord.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))) 619 // { 620 // if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,0)) return FALSE; 621 // } 622 } 623 else if ((ch == 27) && dwLastCharOffset && 624 (InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)) 625 { 626 if (blnCompletionMode) blnCompletionMode = FALSE; 627 _tcsnset(m_pchBuffer,_T(' '),dwLastCharOffset); 628 m_CursorPosition = FristCharCursorPosition; 629 VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); 630 VERIFY(Write(m_pchBuffer,dwLastCharOffset)); 631 dwCurrentCharOffset = dwLastCharOffset = 0; 632 m_CursorPosition = FristCharCursorPosition; 633 VERIFY(SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)); 634 } 635 else if (ch == _T('\r')) 636 { // carriage return 637 if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition = FristCharCursorPosition)) return FALSE; 638 ASSERT(dwLastCharOffset <= m_dwBufferSize); 639 m_pchBuffer[dwLastCharOffset] = 0; // terminate string in buffer 640 ret = Write(m_pchBuffer); 641 m_History.AddHistoryLine(m_pchBuffer); 642 static TCHAR strLF[] = _T("\n"); 643 ret = Write(strLF); 644 break; 645 } 646 else if (ch == _T('\b')) 647 { // backspace 648 if (blnCompletionMode) blnCompletionMode = FALSE; 649 if ((dwCurrentCharOffset) && ((m_CursorPosition.X != 0) || (m_CursorPosition.Y != 0))) 650 { 651 // Calculate new cursor position 652 COORD NewCursorPosition; 653 if (m_CursorPosition.X) 654 { 655 NewCursorPosition.X = SHORT(m_CursorPosition.X-1); 656 NewCursorPosition.Y = m_CursorPosition.Y; 657 } 658 else 659 { 660 ASSERT(m_BufferSize.X); 661 NewCursorPosition.X = SHORT(m_BufferSize.X-1); 662 ASSERT(m_CursorPosition.Y); 663 NewCursorPosition.Y = SHORT(m_CursorPosition.Y-1); 664 } 665 666 // Move the characters if any... 667 ASSERT(dwLastCharOffset); 668 DWORD dwCharOffset = dwCurrentCharOffset-1; 669 while(dwCharOffset < dwLastCharOffset-1) 670 { 671 m_pchBuffer[dwCharOffset] = m_pchBuffer[dwCharOffset+1]; 672 dwCharOffset++; 673 } 674 675 m_pchBuffer[dwLastCharOffset-1] = _T(' '); 676 677 dwCurrentCharOffset--; 678 m_CursorPosition = NewCursorPosition; 679 if (!Write(m_pchBuffer+dwCurrentCharOffset,dwLastCharOffset-dwCurrentCharOffset)) return FALSE; 680 681 // Update cursor position 682 m_CursorPosition = NewCursorPosition; 683 if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)) return FALSE; 684 685 dwLastCharOffset--; 686 } 687 } 688 else if (ch == _T('\t')) 689 { // Tab 690 691 if (!blnCompletionMode) // If tab was pressed after non-tab. We enter in completion mode. 692 { 693 // Initialize completion index 694 if (InputRecord.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) // If shift was pressed 695 nCompletionIndex = (unsigned long long) -1; // Last completion 696 else 697 nCompletionIndex = 0; // First completion 698 699 // Find completion offset. It points at char after first non-quoted whitespace. 700 dwCompletionOffset = dwCurrentCharOffset; 701 BOOL blnQuotedParameter = FALSE; 702 while(dwCompletionOffset) 703 { 704 dwCompletionOffset--; 705 if (m_pchBuffer[dwCompletionOffset] == _T('\"')) 706 { 707 blnQuotedParameter = !blnQuotedParameter; 708 } 709 else if (!blnQuotedParameter && _istspace(m_pchBuffer[dwCompletionOffset])) 710 { // Found ! We are not inside quored parameter and we are on whitespace. 711 dwCompletionOffset++; // dwCompletionOffset must point at char AFTER first non-quoted whitespace. 712 break; 713 } 714 } 715 716 ASSERT(dwCompletionOffset <= dwCurrentCharOffset); 717 718 // Save not changing part (context) of completion in m_pchBuffer1 719 720 // FIXME: dwCompletionOffset is always 0 here 721 // _tcsncpy(m_pchBuffer1,m_pchBuffer,dwCompletionOffset); 722 m_pchBuffer1[dwCompletionOffset] = 0; 723 724 // Size of changing part 725 dwCompletionStringSize = dwCurrentCharOffset-dwCompletionOffset; 726 727 // Save intial changing part of completion in m_pchBuffer2 728 if (dwCompletionStringSize) 729 _tcsncpy(m_pchBuffer2,m_pchBuffer+dwCompletionOffset,dwCompletionStringSize); 730 m_pchBuffer2[dwCompletionStringSize] = 0; 731 732 // Calculate cursor position of point between changing and not changing ports 733 CompletionPosition.X = X_CURSOR_POSITION_FROM_OFFSET(dwCompletionOffset); 734 CompletionPosition.Y = Y_CURSOR_POSITION_FROM_OFFSET(dwCompletionOffset); 735 } // if first time tab 736 737 const TCHAR *pchCompletion = NULL; 738 739 // Direction 740 BOOL blnForward = !(InputRecord.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED); 741 742 if (m_pfReplaceCompletionCallback) // If we are using replace completion callback 743 pchCompletion = m_pfReplaceCompletionCallback(nCompletionIndex, 744 blnCompletionMode?&blnForward:NULL, // If this is first time we call the completion callback, do not change completion index 745 m_pchBuffer1,m_pchBuffer2); 746 747 if (pchCompletion) // If completion found 748 { 749 // Set cursor position to compeltion position 750 m_CursorPosition = CompletionPosition; 751 if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)) 752 return FALSE; 753 754 // Calculate buffer free space 755 ASSERT(m_dwBufferSize > dwCompletionOffset); 756 DWORD dwFree = m_dwBufferSize - dwCompletionOffset - 1; 757 758 // Save old completion string size 759 DWORD dwOldCompletionStringSize = dwCompletionStringSize; 760 761 // Write completion string to buffer 762 dwCompletionStringSize = _tcslen(pchCompletion); 763 764 // If there is not enough space in buffer, so we truncate the completion 765 if (dwCompletionStringSize > dwFree) 766 dwCompletionStringSize = dwFree; 767 768 if (dwCompletionStringSize) 769 { 770 // Copy competion into main buffer 771 _tcsncpy(m_pchBuffer+dwCompletionOffset,pchCompletion,dwCompletionStringSize); 772 773 // Write completion string to console 774 if (!Write(m_pchBuffer+dwCompletionOffset,dwCompletionStringSize)) 775 return FALSE; 776 777 // Set new offsets 778 dwCurrentCharOffset = dwLastCharOffset = dwCompletionOffset + dwCompletionStringSize; 779 780 ASSERT(dwLastCharOffset < m_dwBufferSize); 781 } 782 783 // Erase rest from previous completion string, if the new completion is shorter than old 784 if (dwOldCompletionStringSize > dwCompletionStringSize) 785 { 786 _tcsnset(m_pchBuffer+dwCompletionOffset+dwCompletionStringSize,_T(' '), 787 dwOldCompletionStringSize - dwCompletionStringSize); 788 789 // Save cursor position 790 COORD pos = m_CursorPosition; 791 792 if (!Write(m_pchBuffer+dwCompletionOffset+dwCompletionStringSize, 793 dwOldCompletionStringSize - dwCompletionStringSize)) 794 return FALSE; 795 796 // Set cursor position 797 m_CursorPosition = pos; 798 if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)) 799 return FALSE; 800 } 801 } // If completion found 802 803 // Ok, we are in completion mode 804 blnCompletionMode = TRUE; 805 } 806 else if (_istprint(ch)) 807 { 808 if (blnCompletionMode) blnCompletionMode = FALSE; 809 if (dwLastCharOffset >= m_dwBufferSize-1) 810 { 811 ASSERT(dwLastCharOffset == m_dwBufferSize-1); 812 // Beep(1000,100); 813 continue; 814 } 815 TCHAR ch1; 816 //if (m_blnInsetMode) 817 ch1 = m_pchBuffer[dwCurrentCharOffset]; 818 m_pchBuffer[dwCurrentCharOffset] = ch; 819 if ((m_blnInsetMode)||(dwCurrentCharOffset == dwLastCharOffset)) dwLastCharOffset++; 820 dwCurrentCharOffset++; 821 if (!Write(&ch,1)) return FALSE; 822 if (m_blnInsetMode) 823 { 824 COORD Cursor = m_CursorPosition; 825 DWORD ofs = dwCurrentCharOffset; 826 827 while(ofs <= dwLastCharOffset) 828 { 829 ch = m_pchBuffer[ofs]; 830 m_pchBuffer[ofs] = ch1; 831 ch1 = ch; 832 ofs++; 833 } 834 835 if (dwCurrentCharOffset < dwLastCharOffset) 836 { 837 if (!Write(m_pchBuffer+dwCurrentCharOffset,dwLastCharOffset-dwCurrentCharOffset)) return FALSE; 838 839 if (m_LinesScrolled) 840 { 841 if (m_LinesScrolled > FristCharCursorPosition.Y) return FALSE; 842 Cursor.Y = SHORT(Cursor.Y - m_LinesScrolled); 843 } 844 // Update cursor position 845 m_CursorPosition = Cursor; 846 if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)) return FALSE; 847 } 848 } 849 } 850 ASSERT(InputRecord.Event.KeyEvent.wRepeatCount); 851 if (!InputRecord.Event.KeyEvent.wRepeatCount) return FALSE; 852 if (--InputRecord.Event.KeyEvent.wRepeatCount) goto KeyRepeat; 853 } 854 m_blnMoreMode = blnOldMoreMode; 855 return TRUE; 856 } 857 858 BOOL CConsole::GetTextAttribute(WORD& rwAttributes) 859 { 860 rwAttributes = m_wAttributes; 861 return TRUE; 862 } 863 864 // Parameters: 865 // dwBufferSize - size in chars of the input line buffer 866 // 867 // Rerturns: 868 // NULL - Failed. 869 // pointer to the input buffer 870 TCHAR * CConsole::Init(DWORD dwBufferSize, DWORD dwMaxHistoryLines) 871 { 872 if (m_hStdIn != INVALID_HANDLE_VALUE) VERIFY(CloseHandle(m_hStdIn)); 873 if (m_hStdOut != INVALID_HANDLE_VALUE) VERIFY(CloseHandle(m_hStdOut)); 874 875 m_hStdIn = GetStdHandle(STD_INPUT_HANDLE); 876 877 if (m_hStdIn == INVALID_HANDLE_VALUE) 878 { 879 // _ftprintf(stderr,_T("GetStdHandle(STD_INPUT_HANDLE) failed. GetLastError return 0x%X\n"),(unsigned)GetLastError()); 880 goto Abort; 881 } 882 883 m_hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); 884 885 if (m_hStdOut == INVALID_HANDLE_VALUE) 886 { 887 // _ftprintf(stderr,_T("GetStdHandle(STD_OUTPUT_HANDLE) failed. GetLastError return 0x%X\n"),(unsigned)GetLastError()); 888 goto Abort; 889 } 890 891 CONSOLE_SCREEN_BUFFER_INFO info; 892 if (!GetConsoleScreenBufferInfo(m_hStdOut,&info)) 893 { 894 // _ftprintf(stderr,_T("GetConsoleScreenBufferInfo(m_hStdOut,&info) failed. GetLastError return 0x%X\n"),(unsigned)GetLastError()); 895 if (GetLastError() == 6) // redirected output 896 _ftprintf(stderr,_T("Redirection is not supported.\n")); 897 898 goto Abort; 899 } 900 m_wAttributes = info.wAttributes; 901 902 if (!m_blnOldInputModeSaved) 903 { 904 if (!GetConsoleMode(m_hStdIn,&m_dwOldInputMode)) 905 { 906 if (GetLastError() == 6) // redirected input 907 _ftprintf(stderr,_T("Redirection is not supported.\n")); 908 // _ftprintf(stderr,_T("GetConsoleMode(0x%X,&m_dwOldINputMode) failed. GetLastError() returns 0x%X\n"),(int)m_hStdIn,GetLastError()); 909 goto Abort; 910 } 911 912 m_blnOldInputModeSaved = TRUE; 913 } 914 915 // _ftprintf(stderr,_T("Calling GetConsoleMode(0x%X,&m_dwOldOutputMode) ...\n"),(int)m_hStdOut); 916 if (!m_blnOldOutputModeSaved) 917 { 918 if (!GetConsoleMode(m_hStdOut,&m_dwOldOutputMode)) 919 goto Abort; 920 // _ftprintf(stderr,_T("Calling GetConsoleMode(0x%X,&m_dwOldOutputMode) done.\n"),(int)m_hStdOut); 921 m_blnOldOutputModeSaved = TRUE; 922 } 923 924 // _ftprintf(stderr,_T("Calling SetConsoleMode(0x%X,0) ...\n"),(int)m_hStdIn); 925 if (!SetConsoleMode(m_hStdIn,0)) 926 goto Abort; 927 if (!SetConsoleMode(m_hStdOut,0)) 928 goto Abort; 929 930 m_CursorPosition = info.dwCursorPosition; 931 m_BufferSize = info.dwSize; 932 933 CONSOLE_CURSOR_INFO cci; 934 cci.bVisible = TRUE; 935 cci.dwSize = m_blnInsetMode?m_dwInsertModeCursorHeight:m_dwOverwriteModeCursorHeight; 936 937 if (!SetConsoleCursorInfo(m_hStdOut,&cci)) goto Abort; 938 939 m_dwBufferSize = dwBufferSize; 940 941 if (m_pchBuffer) delete m_pchBuffer; 942 m_pchBuffer = NULL; 943 944 if (m_pchBuffer1) delete m_pchBuffer1; 945 m_pchBuffer1 = NULL; 946 947 if (m_pchBuffer2) delete m_pchBuffer2; 948 m_pchBuffer2 = NULL; 949 950 m_pchBuffer = new (std::nothrow) TCHAR [dwBufferSize]; 951 if (!m_pchBuffer) goto Abort; 952 m_pchBuffer[dwBufferSize-1] = 0; 953 954 m_pchBuffer1 = new (std::nothrow) TCHAR [dwBufferSize]; 955 if (!m_pchBuffer1) goto Abort; 956 m_pchBuffer1[dwBufferSize-1] = 0; 957 958 m_pchBuffer2 = new (std::nothrow) TCHAR [dwBufferSize]; 959 if (!m_pchBuffer2) goto Abort; 960 m_pchBuffer2[dwBufferSize-1] = 0; 961 962 if (dwMaxHistoryLines) 963 { 964 if (!m_History.Init(dwBufferSize,dwMaxHistoryLines)) goto Abort; 965 } 966 967 return m_pchBuffer; 968 969 Abort: 970 if (m_hStdIn != INVALID_HANDLE_VALUE) VERIFY(CloseHandle(m_hStdIn)); 971 m_hStdIn = INVALID_HANDLE_VALUE; 972 973 if (m_hStdOut != INVALID_HANDLE_VALUE) VERIFY(CloseHandle(m_hStdOut)); 974 m_hStdOut = INVALID_HANDLE_VALUE; 975 976 if (m_pchBuffer) delete[] m_pchBuffer; 977 m_pchBuffer = NULL; 978 979 if (m_pchBuffer1) delete[] m_pchBuffer1; 980 m_pchBuffer1 = NULL; 981 982 if (m_pchBuffer2) delete[] m_pchBuffer2; 983 m_pchBuffer2 = NULL; 984 985 m_dwBufferSize = 0; 986 987 return NULL; 988 } 989 990 BOOL CConsole::WriteChar(TCHAR ch) 991 { 992 CHAR_INFO ci; 993 ci.Attributes = m_wAttributes; 994 #ifdef UNICODE 995 ci.Char.UnicodeChar = ch; 996 #else 997 ci.Char.AsciiChar = ch; 998 #endif 999 static COORD BufferSize = {1,1}; 1000 static COORD BufferCoord = {0,0}; 1001 SMALL_RECT Dest; 1002 Dest.Bottom = Dest.Top = m_CursorPosition.Y; 1003 Dest.Left = Dest.Right = m_CursorPosition.X; 1004 return WriteConsoleOutput(m_hStdOut,&ci,BufferSize,BufferCoord,&Dest); 1005 } 1006 1007 void CConsole::BeginScrollingOperation() 1008 { 1009 m_Lines = 0; 1010 } 1011 1012 BOOL CConsole::WriteString(const TCHAR *pchString, COORD Position) 1013 { 1014 CHAR_INFO ciBuffer[256]; 1015 int nSize = _tcslen(pchString); 1016 if ((nSize > 256)||(nSize <= 0)) 1017 { 1018 ASSERT(FALSE); 1019 return FALSE; 1020 } 1021 1022 COORD BufferSize; 1023 BufferSize.X = (SHORT)nSize; 1024 BufferSize.Y = 1; 1025 static COORD BufferCoord = {0,0}; 1026 SMALL_RECT Dest; 1027 Dest.Bottom = Dest.Top = Position.Y; 1028 Dest.Right = SHORT((Dest.Left = Position.X) + nSize - 1); 1029 1030 while(nSize--) 1031 { 1032 ciBuffer[nSize].Attributes = m_wAttributes; 1033 #ifdef UNICODE 1034 ciBuffer[nSize].Char.UnicodeChar = pchString[nSize]; 1035 #else 1036 ciBuffer[nSize].Char.AsciiChar = pchString[nSize]; 1037 #endif 1038 } 1039 1040 return WriteConsoleOutput(m_hStdOut,ciBuffer,BufferSize,BufferCoord,&Dest); 1041 } 1042 1043 BOOL CConsole::SetInsertMode(BOOL blnInsetMode) 1044 { 1045 if (m_hStdOut == INVALID_HANDLE_VALUE) return FALSE; 1046 1047 CONSOLE_CURSOR_INFO cci; 1048 cci.bVisible = TRUE; 1049 cci.dwSize = blnInsetMode?m_dwInsertModeCursorHeight:m_dwOverwriteModeCursorHeight; 1050 1051 BOOL ret = SetConsoleCursorInfo(m_hStdOut,&cci); 1052 if (ret) m_blnInsetMode = blnInsetMode; 1053 return ret; 1054 } 1055 1056 1057 1058 void CConsole::SetReplaceCompletionCallback(ReplaceCompletionCallback pfCallback) 1059 { 1060 m_pfReplaceCompletionCallback = pfCallback; 1061 } 1062 1063 1064 void CConsole::DisableWrite() 1065 { 1066 m_blnDisableWrite = TRUE; 1067 INPUT_RECORD InputRecord; 1068 DWORD dwRecordsWriten; 1069 InputRecord.EventType = KEY_EVENT; 1070 InputRecord.Event.KeyEvent.bKeyDown = TRUE; 1071 #ifdef UNICODE 1072 InputRecord.Event.KeyEvent.uChar.UnicodeChar = L' '; 1073 #else 1074 InputRecord.Event.KeyEvent.uChar.AsciiChar = ' '; 1075 #endif 1076 BOOL ret = WriteConsoleInput(m_hStdIn,&InputRecord,1,&dwRecordsWriten); 1077 ASSERT(ret); 1078 } 1079 1080 void CConsole::EnableWrite() 1081 { 1082 m_blnDisableWrite = FALSE; 1083 } 1084