[FONTSUB] Fix buildĀ²
[reactos.git] / modules / rosapps / applications / sysutils / regexpl / Console.cpp
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 _tcsncpy(m_pchBuffer1,m_pchBuffer,dwCompletionOffset);
720 m_pchBuffer1[dwCompletionOffset] = 0;
721
722 // Size of changing part
723 dwCompletionStringSize = dwCurrentCharOffset-dwCompletionOffset;
724
725 // Save intial changing part of completion in m_pchBuffer2
726 if (dwCompletionStringSize)
727 _tcsncpy(m_pchBuffer2,m_pchBuffer+dwCompletionOffset,dwCompletionStringSize);
728 m_pchBuffer2[dwCompletionStringSize] = 0;
729
730 // Calculate cursor position of point between changing and not changing ports
731 CompletionPosition.X = X_CURSOR_POSITION_FROM_OFFSET(dwCompletionOffset);
732 CompletionPosition.Y = Y_CURSOR_POSITION_FROM_OFFSET(dwCompletionOffset);
733 } // if first time tab
734
735 const TCHAR *pchCompletion = NULL;
736
737 // Direction
738 BOOL blnForward = !(InputRecord.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED);
739
740 if (m_pfReplaceCompletionCallback) // If we are using replace completion callback
741 pchCompletion = m_pfReplaceCompletionCallback(nCompletionIndex,
742 blnCompletionMode?&blnForward:NULL, // If this is first time we call the completion callback, do not change completion index
743 m_pchBuffer1,m_pchBuffer2);
744
745 if (pchCompletion) // If completion found
746 {
747 // Set cursor position to compeltion position
748 m_CursorPosition = CompletionPosition;
749 if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition))
750 return FALSE;
751
752 // Calculate buffer free space
753 ASSERT(m_dwBufferSize > dwCompletionOffset);
754 DWORD dwFree = m_dwBufferSize - dwCompletionOffset - 1;
755
756 // Save old completion string size
757 DWORD dwOldCompletionStringSize = dwCompletionStringSize;
758
759 // Write completion string to buffer
760 dwCompletionStringSize = _tcslen(pchCompletion);
761
762 // If there is not enough space in buffer, so we truncate the completion
763 if (dwCompletionStringSize > dwFree)
764 dwCompletionStringSize = dwFree;
765
766 if (dwCompletionStringSize)
767 {
768 // Copy competion into main buffer
769 _tcsncpy(m_pchBuffer+dwCompletionOffset,pchCompletion,dwCompletionStringSize);
770
771 // Write completion string to console
772 if (!Write(m_pchBuffer+dwCompletionOffset,dwCompletionStringSize))
773 return FALSE;
774
775 // Set new offsets
776 dwCurrentCharOffset = dwLastCharOffset = dwCompletionOffset + dwCompletionStringSize;
777
778 ASSERT(dwLastCharOffset < m_dwBufferSize);
779 }
780
781 // Erase rest from previous completion string, if the new completion is shorter than old
782 if (dwOldCompletionStringSize > dwCompletionStringSize)
783 {
784 _tcsnset(m_pchBuffer+dwCompletionOffset+dwCompletionStringSize,_T(' '),
785 dwOldCompletionStringSize - dwCompletionStringSize);
786
787 // Save cursor position
788 COORD pos = m_CursorPosition;
789
790 if (!Write(m_pchBuffer+dwCompletionOffset+dwCompletionStringSize,
791 dwOldCompletionStringSize - dwCompletionStringSize))
792 return FALSE;
793
794 // Set cursor position
795 m_CursorPosition = pos;
796 if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition))
797 return FALSE;
798 }
799 } // If completion found
800
801 // Ok, we are in completion mode
802 blnCompletionMode = TRUE;
803 }
804 else if (_istprint(ch))
805 {
806 if (blnCompletionMode) blnCompletionMode = FALSE;
807 if (dwLastCharOffset >= m_dwBufferSize-1)
808 {
809 ASSERT(dwLastCharOffset == m_dwBufferSize-1);
810 // Beep(1000,100);
811 continue;
812 }
813 TCHAR ch1;
814 //if (m_blnInsetMode)
815 ch1 = m_pchBuffer[dwCurrentCharOffset];
816 m_pchBuffer[dwCurrentCharOffset] = ch;
817 if ((m_blnInsetMode)||(dwCurrentCharOffset == dwLastCharOffset)) dwLastCharOffset++;
818 dwCurrentCharOffset++;
819 if (!Write(&ch,1)) return FALSE;
820 if (m_blnInsetMode)
821 {
822 COORD Cursor = m_CursorPosition;
823 DWORD ofs = dwCurrentCharOffset;
824
825 while(ofs <= dwLastCharOffset)
826 {
827 ch = m_pchBuffer[ofs];
828 m_pchBuffer[ofs] = ch1;
829 ch1 = ch;
830 ofs++;
831 }
832
833 if (dwCurrentCharOffset < dwLastCharOffset)
834 {
835 if (!Write(m_pchBuffer+dwCurrentCharOffset,dwLastCharOffset-dwCurrentCharOffset)) return FALSE;
836
837 if (m_LinesScrolled)
838 {
839 if (m_LinesScrolled > FristCharCursorPosition.Y) return FALSE;
840 Cursor.Y = SHORT(Cursor.Y - m_LinesScrolled);
841 }
842 // Update cursor position
843 m_CursorPosition = Cursor;
844 if (!SetConsoleCursorPosition(m_hStdOut,m_CursorPosition)) return FALSE;
845 }
846 }
847 }
848 ASSERT(InputRecord.Event.KeyEvent.wRepeatCount);
849 if (!InputRecord.Event.KeyEvent.wRepeatCount) return FALSE;
850 if (--InputRecord.Event.KeyEvent.wRepeatCount) goto KeyRepeat;
851 }
852 m_blnMoreMode = blnOldMoreMode;
853 return TRUE;
854 }
855
856 BOOL CConsole::GetTextAttribute(WORD& rwAttributes)
857 {
858 rwAttributes = m_wAttributes;
859 return TRUE;
860 }
861
862 // Parameters:
863 // dwBufferSize - size in chars of the input line buffer
864 //
865 // Rerturns:
866 // NULL - Failed.
867 // pointer to the input buffer
868 TCHAR * CConsole::Init(DWORD dwBufferSize, DWORD dwMaxHistoryLines)
869 {
870 if (m_hStdIn != INVALID_HANDLE_VALUE) VERIFY(CloseHandle(m_hStdIn));
871 if (m_hStdOut != INVALID_HANDLE_VALUE) VERIFY(CloseHandle(m_hStdOut));
872
873 m_hStdIn = GetStdHandle(STD_INPUT_HANDLE);
874
875 if (m_hStdIn == INVALID_HANDLE_VALUE)
876 {
877 // _ftprintf(stderr,_T("GetStdHandle(STD_INPUT_HANDLE) failed. GetLastError return 0x%X\n"),(unsigned)GetLastError());
878 goto Abort;
879 }
880
881 m_hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
882
883 if (m_hStdOut == INVALID_HANDLE_VALUE)
884 {
885 // _ftprintf(stderr,_T("GetStdHandle(STD_OUTPUT_HANDLE) failed. GetLastError return 0x%X\n"),(unsigned)GetLastError());
886 goto Abort;
887 }
888
889 CONSOLE_SCREEN_BUFFER_INFO info;
890 if (!GetConsoleScreenBufferInfo(m_hStdOut,&info))
891 {
892 // _ftprintf(stderr,_T("GetConsoleScreenBufferInfo(m_hStdOut,&info) failed. GetLastError return 0x%X\n"),(unsigned)GetLastError());
893 if (GetLastError() == 6) // redirected output
894 _ftprintf(stderr,_T("Redirection is not supported.\n"));
895
896 goto Abort;
897 }
898 m_wAttributes = info.wAttributes;
899
900 if (!m_blnOldInputModeSaved)
901 {
902 if (!GetConsoleMode(m_hStdIn,&m_dwOldInputMode))
903 {
904 if (GetLastError() == 6) // redirected input
905 _ftprintf(stderr,_T("Redirection is not supported.\n"));
906 // _ftprintf(stderr,_T("GetConsoleMode(0x%X,&m_dwOldINputMode) failed. GetLastError() returns 0x%X\n"),(int)m_hStdIn,GetLastError());
907 goto Abort;
908 }
909
910 m_blnOldInputModeSaved = TRUE;
911 }
912
913 // _ftprintf(stderr,_T("Calling GetConsoleMode(0x%X,&m_dwOldOutputMode) ...\n"),(int)m_hStdOut);
914 if (!m_blnOldOutputModeSaved)
915 {
916 if (!GetConsoleMode(m_hStdOut,&m_dwOldOutputMode))
917 goto Abort;
918 // _ftprintf(stderr,_T("Calling GetConsoleMode(0x%X,&m_dwOldOutputMode) done.\n"),(int)m_hStdOut);
919 m_blnOldOutputModeSaved = TRUE;
920 }
921
922 // _ftprintf(stderr,_T("Calling SetConsoleMode(0x%X,0) ...\n"),(int)m_hStdIn);
923 if (!SetConsoleMode(m_hStdIn,0))
924 goto Abort;
925 if (!SetConsoleMode(m_hStdOut,0))
926 goto Abort;
927
928 m_CursorPosition = info.dwCursorPosition;
929 m_BufferSize = info.dwSize;
930
931 CONSOLE_CURSOR_INFO cci;
932 cci.bVisible = TRUE;
933 cci.dwSize = m_blnInsetMode?m_dwInsertModeCursorHeight:m_dwOverwriteModeCursorHeight;
934
935 if (!SetConsoleCursorInfo(m_hStdOut,&cci)) goto Abort;
936
937 m_dwBufferSize = dwBufferSize;
938
939 if (m_pchBuffer) delete m_pchBuffer;
940 m_pchBuffer = NULL;
941
942 if (m_pchBuffer1) delete m_pchBuffer1;
943 m_pchBuffer1 = NULL;
944
945 if (m_pchBuffer2) delete m_pchBuffer2;
946 m_pchBuffer2 = NULL;
947
948 m_pchBuffer = new (std::nothrow) TCHAR [dwBufferSize];
949 if (!m_pchBuffer) goto Abort;
950 m_pchBuffer[dwBufferSize-1] = 0;
951
952 m_pchBuffer1 = new (std::nothrow) TCHAR [dwBufferSize];
953 if (!m_pchBuffer1) goto Abort;
954 m_pchBuffer1[dwBufferSize-1] = 0;
955
956 m_pchBuffer2 = new (std::nothrow) TCHAR [dwBufferSize];
957 if (!m_pchBuffer2) goto Abort;
958 m_pchBuffer2[dwBufferSize-1] = 0;
959
960 if (dwMaxHistoryLines)
961 {
962 if (!m_History.Init(dwBufferSize,dwMaxHistoryLines)) goto Abort;
963 }
964
965 return m_pchBuffer;
966
967 Abort:
968 if (m_hStdIn != INVALID_HANDLE_VALUE) VERIFY(CloseHandle(m_hStdIn));
969 m_hStdIn = INVALID_HANDLE_VALUE;
970
971 if (m_hStdOut != INVALID_HANDLE_VALUE) VERIFY(CloseHandle(m_hStdOut));
972 m_hStdOut = INVALID_HANDLE_VALUE;
973
974 if (m_pchBuffer) delete[] m_pchBuffer;
975 m_pchBuffer = NULL;
976
977 if (m_pchBuffer1) delete[] m_pchBuffer1;
978 m_pchBuffer1 = NULL;
979
980 if (m_pchBuffer2) delete[] m_pchBuffer2;
981 m_pchBuffer2 = NULL;
982
983 m_dwBufferSize = 0;
984
985 return NULL;
986 }
987
988 BOOL CConsole::WriteChar(TCHAR ch)
989 {
990 CHAR_INFO ci;
991 ci.Attributes = m_wAttributes;
992 #ifdef UNICODE
993 ci.Char.UnicodeChar = ch;
994 #else
995 ci.Char.AsciiChar = ch;
996 #endif
997 static COORD BufferSize = {1,1};
998 static COORD BufferCoord = {0,0};
999 SMALL_RECT Dest;
1000 Dest.Bottom = Dest.Top = m_CursorPosition.Y;
1001 Dest.Left = Dest.Right = m_CursorPosition.X;
1002 return WriteConsoleOutput(m_hStdOut,&ci,BufferSize,BufferCoord,&Dest);
1003 }
1004
1005 void CConsole::BeginScrollingOperation()
1006 {
1007 m_Lines = 0;
1008 }
1009
1010 BOOL CConsole::WriteString(const TCHAR *pchString, COORD Position)
1011 {
1012 CHAR_INFO ciBuffer[256];
1013 int nSize = _tcslen(pchString);
1014 if ((nSize > 256)||(nSize <= 0))
1015 {
1016 ASSERT(FALSE);
1017 return FALSE;
1018 }
1019
1020 COORD BufferSize;
1021 BufferSize.X = (SHORT)nSize;
1022 BufferSize.Y = 1;
1023 static COORD BufferCoord = {0,0};
1024 SMALL_RECT Dest;
1025 Dest.Bottom = Dest.Top = Position.Y;
1026 Dest.Right = SHORT((Dest.Left = Position.X) + nSize - 1);
1027
1028 while(nSize--)
1029 {
1030 ciBuffer[nSize].Attributes = m_wAttributes;
1031 #ifdef UNICODE
1032 ciBuffer[nSize].Char.UnicodeChar = pchString[nSize];
1033 #else
1034 ciBuffer[nSize].Char.AsciiChar = pchString[nSize];
1035 #endif
1036 }
1037
1038 return WriteConsoleOutput(m_hStdOut,ciBuffer,BufferSize,BufferCoord,&Dest);
1039 }
1040
1041 BOOL CConsole::SetInsertMode(BOOL blnInsetMode)
1042 {
1043 if (m_hStdOut == INVALID_HANDLE_VALUE) return FALSE;
1044
1045 CONSOLE_CURSOR_INFO cci;
1046 cci.bVisible = TRUE;
1047 cci.dwSize = blnInsetMode?m_dwInsertModeCursorHeight:m_dwOverwriteModeCursorHeight;
1048
1049 BOOL ret = SetConsoleCursorInfo(m_hStdOut,&cci);
1050 if (ret) m_blnInsetMode = blnInsetMode;
1051 return ret;
1052 }
1053
1054
1055
1056 void CConsole::SetReplaceCompletionCallback(ReplaceCompletionCallback pfCallback)
1057 {
1058 m_pfReplaceCompletionCallback = pfCallback;
1059 }
1060
1061
1062 void CConsole::DisableWrite()
1063 {
1064 m_blnDisableWrite = TRUE;
1065 INPUT_RECORD InputRecord;
1066 DWORD dwRecordsWriten;
1067 InputRecord.EventType = KEY_EVENT;
1068 InputRecord.Event.KeyEvent.bKeyDown = TRUE;
1069 #ifdef UNICODE
1070 InputRecord.Event.KeyEvent.uChar.UnicodeChar = L' ';
1071 #else
1072 InputRecord.Event.KeyEvent.uChar.AsciiChar = ' ';
1073 #endif
1074 BOOL ret = WriteConsoleInput(m_hStdIn,&InputRecord,1,&dwRecordsWriten);
1075 ASSERT(ret);
1076 }
1077
1078 void CConsole::EnableWrite()
1079 {
1080 m_blnDisableWrite = FALSE;
1081 }