d18ee04dc5b5bc6038bbf5b07358e40d291f6305
[reactos.git] / reactos / base / shell / cmd / console.c
1 /*
2 * CONSOLE.C - console input/output functions.
3 *
4 *
5 * History:
6 *
7 * 20-Jan-1999 (Eric Kohl)
8 * started
9 *
10 * 03-Apr-2005 (Magnus Olsen <magnus@greatlord.com>)
11 * Remove all hardcoded strings in En.rc
12 *
13 * 01-Jul-2005 (Brandon Turner <turnerb7@msu.edu>)
14 * Added ConPrintfPaging and ConOutPrintfPaging
15 *
16 * 02-Feb-2007 (Paolo Devoti <devotip at gmail.com>)
17 * Fixed ConPrintfPaging
18 */
19
20 #include "precomp.h"
21
22 #define OUTPUT_BUFFER_SIZE 4096
23
24
25 UINT InputCodePage;
26 UINT OutputCodePage;
27
28
29 BOOL IsConsoleHandle(HANDLE hHandle)
30 {
31 DWORD dwMode;
32
33 /* Check whether the handle may be that of a console... */
34 if ((GetFileType(hHandle) & ~FILE_TYPE_REMOTE) != FILE_TYPE_CHAR)
35 return FALSE;
36
37 /*
38 * It may be. Perform another test... The idea comes from the
39 * MSDN description of the WriteConsole API:
40 *
41 * "WriteConsole fails if it is used with a standard handle
42 * that is redirected to a file. If an application processes
43 * multilingual output that can be redirected, determine whether
44 * the output handle is a console handle (one method is to call
45 * the GetConsoleMode function and check whether it succeeds).
46 * If the handle is a console handle, call WriteConsole. If the
47 * handle is not a console handle, the output is redirected and
48 * you should call WriteFile to perform the I/O."
49 */
50 return GetConsoleMode(hHandle, &dwMode);
51 }
52
53
54
55 /********************* Console STREAM IN utility functions ********************/
56
57 VOID ConInDisable(VOID)
58 {
59 HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
60 DWORD dwMode;
61
62 GetConsoleMode(hInput, &dwMode);
63 dwMode &= ~ENABLE_PROCESSED_INPUT;
64 SetConsoleMode(hInput, dwMode);
65 }
66
67 VOID ConInEnable(VOID)
68 {
69 HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
70 DWORD dwMode;
71
72 GetConsoleMode(hInput, &dwMode);
73 dwMode |= ENABLE_PROCESSED_INPUT;
74 SetConsoleMode(hInput, dwMode);
75 }
76
77 VOID ConInFlush(VOID)
78 {
79 FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
80 }
81
82 VOID ConInKey(PINPUT_RECORD lpBuffer)
83 {
84 HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
85 DWORD dwRead;
86
87 if (hInput == INVALID_HANDLE_VALUE)
88 WARN ("Invalid input handle!!!\n");
89
90 do
91 {
92 ReadConsoleInput(hInput, lpBuffer, 1, &dwRead);
93 if ((lpBuffer->EventType == KEY_EVENT) &&
94 (lpBuffer->Event.KeyEvent.bKeyDown == TRUE))
95 break;
96 }
97 while (TRUE);
98 }
99
100 VOID ConInString(LPTSTR lpInput, DWORD dwLength)
101 {
102 DWORD dwOldMode;
103 DWORD dwRead = 0;
104 HANDLE hFile;
105
106 LPTSTR p;
107 PCHAR pBuf;
108
109 #ifdef _UNICODE
110 pBuf = (PCHAR)cmd_alloc(dwLength - 1);
111 #else
112 pBuf = lpInput;
113 #endif
114 ZeroMemory(lpInput, dwLength * sizeof(TCHAR));
115 hFile = GetStdHandle(STD_INPUT_HANDLE);
116 GetConsoleMode(hFile, &dwOldMode);
117
118 SetConsoleMode(hFile, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
119
120 ReadFile(hFile, (PVOID)pBuf, dwLength - 1, &dwRead, NULL);
121
122 #ifdef _UNICODE
123 MultiByteToWideChar(InputCodePage, 0, pBuf, dwRead, lpInput, dwLength - 1);
124 cmd_free(pBuf);
125 #endif
126 for (p = lpInput; *p; p++)
127 {
128 if (*p == _T('\x0d'))
129 {
130 *p = _T('\0');
131 break;
132 }
133 }
134
135 SetConsoleMode(hFile, dwOldMode);
136 }
137
138
139
140 /******************** Console STREAM OUT utility functions ********************/
141
142 static VOID ConWrite(TCHAR *str, DWORD len, DWORD nStdHandle)
143 {
144 DWORD dwNumBytes = 0;
145 HANDLE hOutput = GetStdHandle(nStdHandle);
146 PVOID p;
147
148 /* If we don't write anything, just return */
149 if (!str || len == 0)
150 return;
151
152 /* Check whether we are writing to a console and if so, write to it */
153 if (IsConsoleHandle(hOutput))
154 {
155 WriteConsole(hOutput, str, len, &dwNumBytes, NULL);
156 return;
157 }
158
159 /* We're writing to a file or pipe instead of the console. Convert the
160 * string from TCHARs to the desired output format, if the two differ */
161 if (bUnicodeOutput)
162 {
163 #ifndef _UNICODE
164 WCHAR *buffer = cmd_alloc(len * sizeof(WCHAR));
165 if (!buffer)
166 {
167 error_out_of_memory();
168 return;
169 }
170 len = (DWORD)MultiByteToWideChar(OutputCodePage, 0, str, (INT)len, buffer, (INT)len);
171 str = (PVOID)buffer;
172 #endif
173 /*
174 * Find any newline character in the buffer,
175 * write the part BEFORE the newline, then write
176 * a carriage-return + newline, and then write
177 * the remaining part of the buffer.
178 *
179 * This fixes output in files and serial console.
180 */
181 while (len > 0)
182 {
183 /* Loop until we find a \r or \n character */
184 // FIXME: What about the pair \r\n ?
185 p = str;
186 while (len > 0 && *(PWCHAR)p != L'\r' && *(PWCHAR)p != L'\n')
187 {
188 /* Advance one character */
189 p = (PVOID)((PWCHAR)p + 1);
190 len--;
191 }
192
193 /* Write everything up to \r or \n */
194 dwNumBytes = ((PWCHAR)p - (PWCHAR)str) * sizeof(WCHAR);
195 WriteFile(hOutput, str, dwNumBytes, &dwNumBytes, NULL);
196
197 /* If we hit \r or \n ... */
198 if (len > 0 && (*(PWCHAR)p == L'\r' || *(PWCHAR)p == L'\n'))
199 {
200 /* ... send a carriage-return + newline sequence and skip \r or \n */
201 WriteFile(hOutput, L"\r\n", 2 * sizeof(WCHAR), &dwNumBytes, NULL);
202 str = (PVOID)((PWCHAR)p + 1);
203 len--;
204 }
205 }
206
207 #ifndef _UNICODE
208 cmd_free(buffer);
209 #endif
210 }
211 else
212 {
213 #ifdef _UNICODE
214 CHAR *buffer = cmd_alloc(len * MB_LEN_MAX * sizeof(CHAR));
215 if (!buffer)
216 {
217 error_out_of_memory();
218 return;
219 }
220 len = WideCharToMultiByte(OutputCodePage, 0, str, len, buffer, len * MB_LEN_MAX, NULL, NULL);
221 str = (PVOID)buffer;
222 #endif
223 /*
224 * Find any newline character in the buffer,
225 * write the part BEFORE the newline, then write
226 * a carriage-return + newline, and then write
227 * the remaining part of the buffer.
228 *
229 * This fixes output in files and serial console.
230 */
231 while (len > 0)
232 {
233 /* Loop until we find a \r or \n character */
234 // FIXME: What about the pair \r\n ?
235 p = str;
236 while (len > 0 && *(PCHAR)p != '\r' && *(PCHAR)p != '\n')
237 {
238 /* Advance one character */
239 p = (PVOID)((PCHAR)p + 1);
240 len--;
241 }
242
243 /* Write everything up to \r or \n */
244 dwNumBytes = ((PCHAR)p - (PCHAR)str) * sizeof(CHAR);
245 WriteFile(hOutput, str, dwNumBytes, &dwNumBytes, NULL);
246
247 /* If we hit \r or \n ... */
248 if (len > 0 && (*(PCHAR)p == '\r' || *(PCHAR)p == '\n'))
249 {
250 /* ... send a carriage-return + newline sequence and skip \r or \n */
251 WriteFile(hOutput, "\r\n", 2, &dwNumBytes, NULL);
252 str = (PVOID)((PCHAR)p + 1);
253 len--;
254 }
255 }
256
257 #ifdef _UNICODE
258 cmd_free(buffer);
259 #endif
260 }
261 }
262
263 VOID ConOutChar(TCHAR c)
264 {
265 ConWrite(&c, 1, STD_OUTPUT_HANDLE);
266 }
267
268 VOID ConPuts(LPTSTR szText, DWORD nStdHandle)
269 {
270 ConWrite(szText, (DWORD)_tcslen(szText), nStdHandle);
271 }
272
273 VOID ConOutResPuts(UINT resID)
274 {
275 TCHAR szMsg[RC_STRING_MAX_SIZE];
276 LoadString(CMD_ModuleHandle, resID, szMsg, ARRAYSIZE(szMsg));
277 ConPuts(szMsg, STD_OUTPUT_HANDLE);
278 }
279
280 VOID ConOutPuts(LPTSTR szText)
281 {
282 ConPuts(szText, STD_OUTPUT_HANDLE);
283 }
284
285 VOID ConPrintfV(DWORD nStdHandle, LPTSTR szFormat, va_list arg_ptr)
286 {
287 TCHAR szOut[OUTPUT_BUFFER_SIZE];
288 DWORD len;
289
290 len = (DWORD)_vstprintf(szOut, szFormat, arg_ptr);
291 ConWrite(szOut, len, nStdHandle);
292 }
293
294 VOID ConErrFormatMessage(DWORD MessageId, ...)
295 {
296 TCHAR szMsg[RC_STRING_MAX_SIZE];
297 DWORD ret;
298 LPTSTR text;
299 va_list arg_ptr;
300
301 va_start(arg_ptr, MessageId);
302 ret = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
303 NULL,
304 MessageId,
305 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
306 (LPTSTR) &text,
307 0,
308 &arg_ptr);
309
310 va_end(arg_ptr);
311 if (ret > 0)
312 {
313 ConErrPuts(text);
314 LocalFree(text);
315 }
316 else
317 {
318 LoadString(CMD_ModuleHandle, STRING_CONSOLE_ERROR, szMsg, ARRAYSIZE(szMsg));
319 ConErrPrintf(szMsg);
320 }
321 }
322
323 VOID ConOutFormatMessage(DWORD MessageId, ...)
324 {
325 TCHAR szMsg[RC_STRING_MAX_SIZE];
326 DWORD ret;
327 LPTSTR text;
328 va_list arg_ptr;
329
330 va_start(arg_ptr, MessageId);
331 ret = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
332 NULL,
333 MessageId,
334 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
335 (LPTSTR) &text,
336 0,
337 &arg_ptr);
338
339 va_end(arg_ptr);
340 if (ret > 0)
341 {
342 ConErrPuts(text);
343 LocalFree(text);
344 }
345 else
346 {
347 LoadString(CMD_ModuleHandle, STRING_CONSOLE_ERROR, szMsg, ARRAYSIZE(szMsg));
348 ConErrPrintf(szMsg);
349 }
350 }
351
352 VOID ConOutResPrintf(UINT resID, ...)
353 {
354 TCHAR szMsg[RC_STRING_MAX_SIZE];
355 va_list arg_ptr;
356
357 va_start(arg_ptr, resID);
358 LoadString(CMD_ModuleHandle, resID, szMsg, ARRAYSIZE(szMsg));
359 ConPrintfV(STD_OUTPUT_HANDLE, szMsg, arg_ptr);
360 va_end(arg_ptr);
361 }
362
363 VOID ConOutPrintf(LPTSTR szFormat, ...)
364 {
365 va_list arg_ptr;
366
367 va_start(arg_ptr, szFormat);
368 ConPrintfV(STD_OUTPUT_HANDLE, szFormat, arg_ptr);
369 va_end(arg_ptr);
370 }
371
372 VOID ConErrChar(TCHAR c)
373 {
374 ConWrite(&c, 1, STD_ERROR_HANDLE);
375 }
376
377 VOID ConErrResPuts(UINT resID)
378 {
379 TCHAR szMsg[RC_STRING_MAX_SIZE];
380 LoadString(CMD_ModuleHandle, resID, szMsg, ARRAYSIZE(szMsg));
381 ConPuts(szMsg, STD_ERROR_HANDLE);
382 }
383
384 VOID ConErrPuts(LPTSTR szText)
385 {
386 ConPuts(szText, STD_ERROR_HANDLE);
387 }
388
389 VOID ConErrResPrintf(UINT resID, ...)
390 {
391 TCHAR szMsg[RC_STRING_MAX_SIZE];
392 va_list arg_ptr;
393
394 va_start(arg_ptr, resID);
395 LoadString(CMD_ModuleHandle, resID, szMsg, ARRAYSIZE(szMsg));
396 ConPrintfV(STD_ERROR_HANDLE, szMsg, arg_ptr);
397 va_end(arg_ptr);
398 }
399
400 VOID ConErrPrintf(LPTSTR szFormat, ...)
401 {
402 va_list arg_ptr;
403
404 va_start(arg_ptr, szFormat);
405 ConPrintfV(STD_ERROR_HANDLE, szFormat, arg_ptr);
406 va_end(arg_ptr);
407 }
408
409
410
411 /************************** Console PAGER functions ***************************/
412
413 INT ConPrintfVPaging(DWORD nStdHandle, BOOL NewPage, LPTSTR szFormat, va_list arg_ptr)
414 {
415 INT len;
416 CONSOLE_SCREEN_BUFFER_INFO csbi;
417 TCHAR szOut[OUTPUT_BUFFER_SIZE];
418 DWORD dwWritten;
419 HANDLE hOutput = GetStdHandle(nStdHandle);
420
421 /* Used to count number of lines since last pause */
422 static int LineCount = 0;
423
424 /* Used to see how big the screen is */
425 int ScreenLines = 0;
426
427 /* Chars since start of line */
428 int CharSL;
429
430 int from = 0, i = 0;
431
432 if (NewPage == TRUE)
433 LineCount = 0;
434
435 /* Reset LineCount and return if no string has been given */
436 if (szFormat == NULL)
437 return 0;
438
439 /* Get the size of the visual screen that can be printed to */
440 if (!IsConsoleHandle(hOutput) || !GetConsoleScreenBufferInfo(hOutput, &csbi))
441 {
442 /* We assume it's a file handle */
443 ConPrintfV(nStdHandle, szFormat, arg_ptr);
444 return 0;
445 }
446
447 /*
448 * Get the number of lines currently displayed on screen, minus 1
449 * to account for the "press any key..." prompt from PagePrompt().
450 */
451 ScreenLines = (csbi.srWindow.Bottom - csbi.srWindow.Top);
452 CharSL = csbi.dwCursorPosition.X;
453
454 /* Make sure the user doesn't have the screen too small */
455 if (ScreenLines < 4)
456 {
457 ConPrintfV(nStdHandle, szFormat, arg_ptr);
458 return 0;
459 }
460
461 len = _vstprintf(szOut, szFormat, arg_ptr);
462
463 while (i < len)
464 {
465 /* Search until the end of a line is reached */
466 if (szOut[i++] != _T('\n') && ++CharSL < csbi.dwSize.X)
467 continue;
468
469 LineCount++;
470 CharSL=0;
471
472 if (LineCount >= ScreenLines)
473 {
474 WriteConsole(hOutput, &szOut[from], i-from, &dwWritten, NULL);
475 from = i;
476
477 /* Prompt the user */
478 if (PagePrompt() != PROMPT_YES)
479 {
480 return 1;
481 }
482
483 // TODO: Recalculate 'ScreenLines' in case the user redimensions
484 // the window during the prompt.
485
486 /* Reset the number of lines being printed */
487 LineCount = 0;
488 }
489 }
490
491 WriteConsole(hOutput, &szOut[from], i-from, &dwWritten, NULL);
492
493 return 0;
494 }
495
496 INT ConOutPrintfPaging(BOOL NewPage, LPTSTR szFormat, ...)
497 {
498 INT iReturn;
499 va_list arg_ptr;
500
501 va_start(arg_ptr, szFormat);
502 iReturn = ConPrintfVPaging(STD_OUTPUT_HANDLE, NewPage, szFormat, arg_ptr);
503 va_end(arg_ptr);
504 return iReturn;
505 }
506
507 VOID ConOutResPaging(BOOL NewPage, UINT resID)
508 {
509 TCHAR szMsg[RC_STRING_MAX_SIZE];
510 LoadString(CMD_ModuleHandle, resID, szMsg, ARRAYSIZE(szMsg));
511 ConOutPrintfPaging(NewPage, szMsg);
512 }
513
514
515
516 /************************** Console SCREEN functions **************************/
517
518 VOID SetCursorXY(SHORT x, SHORT y)
519 {
520 COORD coPos;
521
522 coPos.X = x;
523 coPos.Y = y;
524 SetConsoleCursorPosition(GetStdHandle (STD_OUTPUT_HANDLE), coPos);
525 }
526
527 VOID GetCursorXY(PSHORT x, PSHORT y)
528 {
529 CONSOLE_SCREEN_BUFFER_INFO csbi;
530
531 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
532
533 *x = csbi.dwCursorPosition.X;
534 *y = csbi.dwCursorPosition.Y;
535 }
536
537 SHORT GetCursorX(VOID)
538 {
539 CONSOLE_SCREEN_BUFFER_INFO csbi;
540
541 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
542 return csbi.dwCursorPosition.X;
543 }
544
545 SHORT GetCursorY(VOID)
546 {
547 CONSOLE_SCREEN_BUFFER_INFO csbi;
548
549 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
550 return csbi.dwCursorPosition.Y;
551 }
552
553 VOID SetCursorType(BOOL bInsert, BOOL bVisible)
554 {
555 CONSOLE_CURSOR_INFO cci;
556
557 cci.dwSize = bInsert ? 10 : 99;
558 cci.bVisible = bVisible;
559
560 SetConsoleCursorInfo(GetStdHandle (STD_OUTPUT_HANDLE), &cci);
561 }
562
563 VOID GetScreenSize(PSHORT maxx, PSHORT maxy)
564 {
565 CONSOLE_SCREEN_BUFFER_INFO csbi;
566
567 if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
568 {
569 csbi.dwSize.X = 80;
570 csbi.dwSize.Y = 25;
571 }
572
573 if (maxx) *maxx = csbi.dwSize.X;
574 if (maxy) *maxy = csbi.dwSize.Y;
575 }
576
577
578
579
580 BOOL ConSetTitle(IN LPCTSTR lpConsoleTitle)
581 {
582 /* Now really set the console title */
583 return SetConsoleTitle(lpConsoleTitle);
584 }
585
586 #ifdef INCLUDE_CMD_BEEP
587 VOID ConRingBell(HANDLE hOutput)
588 {
589 #if 0
590 /* Emit an error beep sound */
591 if (IsConsoleHandle(hOutput))
592 Beep(800, 200);
593 else if (IsTTYHandle(hOutput))
594 ConOutPuts(_T("\a")); // BEL character 0x07
595 else
596 #endif
597 MessageBeep(-1);
598 }
599 #endif
600
601 #ifdef INCLUDE_CMD_CLS
602 VOID ConClearScreen(HANDLE hOutput)
603 {
604 CONSOLE_SCREEN_BUFFER_INFO csbi;
605 COORD coPos;
606 DWORD dwWritten;
607
608 if (GetConsoleScreenBufferInfo(hOutput, &csbi))
609 {
610 coPos.X = 0;
611 coPos.Y = 0;
612 FillConsoleOutputAttribute(hOutput, csbi.wAttributes,
613 csbi.dwSize.X * csbi.dwSize.Y,
614 coPos, &dwWritten);
615 FillConsoleOutputCharacter(hOutput, _T(' '),
616 csbi.dwSize.X * csbi.dwSize.Y,
617 coPos, &dwWritten);
618 SetConsoleCursorPosition(hOutput, coPos);
619 }
620 else
621 {
622 ConOutChar(_T('\f'));
623 }
624 }
625 #endif
626
627 #ifdef INCLUDE_CMD_COLOR
628 BOOL ConSetScreenColor(WORD wColor, BOOL bFill)
629 {
630 HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
631 DWORD dwWritten;
632 CONSOLE_SCREEN_BUFFER_INFO csbi;
633 COORD coPos;
634
635 /* Foreground and Background colors can't be the same */
636 if ((wColor & 0x0F) == (wColor & 0xF0) >> 4)
637 return FALSE;
638
639 /* Fill the whole background if needed */
640 if (bFill)
641 {
642 GetConsoleScreenBufferInfo(hConsole, &csbi);
643
644 coPos.X = 0;
645 coPos.Y = 0;
646 FillConsoleOutputAttribute(hConsole,
647 wColor & 0x00FF,
648 csbi.dwSize.X * csbi.dwSize.Y,
649 coPos,
650 &dwWritten);
651 }
652
653 /* Set the text attribute */
654 SetConsoleTextAttribute(hConsole, wColor & 0x00FF);
655 return TRUE;
656 }
657 #endif
658
659 /* EOF */