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