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