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