346d49749f19cedd00a9117d06a82d506f3d3612
[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 /* Cache codepage for text streams */
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(DWORD nStdHandle, TCHAR *str, DWORD len)
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 ConPuts(DWORD nStdHandle, LPTSTR szText)
264 {
265 ConWrite(nStdHandle, szText, (DWORD)_tcslen(szText));
266 }
267
268 VOID ConResPuts(DWORD nStdHandle, UINT resID)
269 {
270 TCHAR szMsg[RC_STRING_MAX_SIZE];
271 LoadString(CMD_ModuleHandle, resID, szMsg, ARRAYSIZE(szMsg));
272 ConPuts(nStdHandle, szMsg);
273 }
274
275 VOID ConOutChar(TCHAR c)
276 {
277 ConWrite(STD_OUTPUT_HANDLE, &c, 1);
278 }
279
280 VOID ConErrChar(TCHAR c)
281 {
282 ConWrite(STD_ERROR_HANDLE, &c, 1);
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(nStdHandle, szOut, len);
292 }
293
294 VOID ConPrintf(DWORD nStdHandle, LPTSTR szFormat, ...)
295 {
296 va_list arg_ptr;
297
298 va_start(arg_ptr, szFormat);
299 ConPrintfV(nStdHandle, szFormat, arg_ptr);
300 va_end(arg_ptr);
301 }
302
303 VOID ConResPrintf(DWORD nStdHandle, UINT resID, ...)
304 {
305 TCHAR szMsg[RC_STRING_MAX_SIZE];
306 va_list arg_ptr;
307
308 va_start(arg_ptr, resID);
309 LoadString(CMD_ModuleHandle, resID, szMsg, ARRAYSIZE(szMsg));
310 ConPrintfV(nStdHandle, szMsg, arg_ptr);
311 va_end(arg_ptr);
312 }
313
314 VOID ConFormatMessage(DWORD nStdHandle, DWORD MessageId, ...)
315 {
316 DWORD ret;
317 LPTSTR text;
318 va_list arg_ptr;
319
320 va_start(arg_ptr, MessageId);
321 ret = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
322 NULL,
323 MessageId,
324 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
325 (LPTSTR)&text,
326 0,
327 &arg_ptr);
328 va_end(arg_ptr);
329
330 if (ret > 0)
331 {
332 ConPuts(nStdHandle, text);
333 LocalFree(text);
334 }
335 else
336 {
337 ConResPrintf(nStdHandle, STRING_CONSOLE_ERROR, MessageId);
338 }
339 }
340
341
342
343 /************************** Console PAGER functions ***************************/
344
345 BOOL ConPrintfVPaging(DWORD nStdHandle, BOOL NewPage, LPTSTR szFormat, va_list arg_ptr)
346 {
347 INT len;
348 CONSOLE_SCREEN_BUFFER_INFO csbi;
349 TCHAR szOut[OUTPUT_BUFFER_SIZE];
350 DWORD dwWritten;
351 HANDLE hOutput = GetStdHandle(nStdHandle);
352
353 /* Used to count number of lines since last pause */
354 static int LineCount = 0;
355
356 /* Used to see how big the screen is */
357 int ScreenLines = 0;
358
359 /* Chars since start of line */
360 int CharSL;
361
362 int from = 0, i = 0;
363
364 if (NewPage == TRUE)
365 LineCount = 0;
366
367 /* Reset LineCount and return if no string has been given */
368 if (szFormat == NULL)
369 return TRUE;
370
371 /* Get the size of the visual screen that can be printed to */
372 if (!IsConsoleHandle(hOutput) || !GetConsoleScreenBufferInfo(hOutput, &csbi))
373 {
374 /* We assume it's a file handle */
375 ConPrintfV(nStdHandle, szFormat, arg_ptr);
376 return TRUE;
377 }
378
379 /*
380 * Get the number of lines currently displayed on screen, minus 1
381 * to account for the "press any key..." prompt from PagePrompt().
382 */
383 ScreenLines = (csbi.srWindow.Bottom - csbi.srWindow.Top);
384 CharSL = csbi.dwCursorPosition.X;
385
386 /* Make sure the user doesn't have the screen too small */
387 if (ScreenLines < 4)
388 {
389 ConPrintfV(nStdHandle, szFormat, arg_ptr);
390 return TRUE;
391 }
392
393 len = _vstprintf(szOut, szFormat, arg_ptr);
394
395 while (i < len)
396 {
397 /* Search until the end of a line is reached */
398 if (szOut[i++] != _T('\n') && ++CharSL < csbi.dwSize.X)
399 continue;
400
401 LineCount++;
402 CharSL=0;
403
404 if (LineCount >= ScreenLines)
405 {
406 WriteConsole(hOutput, &szOut[from], i-from, &dwWritten, NULL);
407 from = i;
408
409 /* Prompt the user */
410 if (PagePrompt() != PROMPT_YES)
411 return FALSE;
412
413 // TODO: Recalculate 'ScreenLines' in case the user redimensions
414 // the window during the prompt.
415
416 /* Reset the number of lines being printed */
417 LineCount = 0;
418 }
419 }
420
421 WriteConsole(hOutput, &szOut[from], i-from, &dwWritten, NULL);
422
423 return TRUE;
424 }
425
426 BOOL ConOutPrintfPaging(BOOL NewPage, LPTSTR szFormat, ...)
427 {
428 BOOL bRet;
429 va_list arg_ptr;
430
431 va_start(arg_ptr, szFormat);
432 bRet = ConPrintfVPaging(STD_OUTPUT_HANDLE, NewPage, szFormat, arg_ptr);
433 va_end(arg_ptr);
434 return bRet;
435 }
436
437 VOID ConOutResPaging(BOOL NewPage, UINT resID)
438 {
439 TCHAR szMsg[RC_STRING_MAX_SIZE];
440 LoadString(CMD_ModuleHandle, resID, szMsg, ARRAYSIZE(szMsg));
441 ConOutPrintfPaging(NewPage, szMsg);
442 }
443
444
445
446 /************************** Console SCREEN functions **************************/
447
448 VOID SetCursorXY(SHORT x, SHORT y)
449 {
450 COORD coPos;
451
452 coPos.X = x;
453 coPos.Y = y;
454 SetConsoleCursorPosition(GetStdHandle (STD_OUTPUT_HANDLE), coPos);
455 }
456
457 VOID GetCursorXY(PSHORT x, PSHORT y)
458 {
459 CONSOLE_SCREEN_BUFFER_INFO csbi;
460
461 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
462
463 *x = csbi.dwCursorPosition.X;
464 *y = csbi.dwCursorPosition.Y;
465 }
466
467 SHORT GetCursorX(VOID)
468 {
469 CONSOLE_SCREEN_BUFFER_INFO csbi;
470
471 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
472 return csbi.dwCursorPosition.X;
473 }
474
475 SHORT GetCursorY(VOID)
476 {
477 CONSOLE_SCREEN_BUFFER_INFO csbi;
478
479 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
480 return csbi.dwCursorPosition.Y;
481 }
482
483 VOID SetCursorType(BOOL bInsert, BOOL bVisible)
484 {
485 CONSOLE_CURSOR_INFO cci;
486
487 cci.dwSize = bInsert ? 10 : 99;
488 cci.bVisible = bVisible;
489
490 SetConsoleCursorInfo(GetStdHandle (STD_OUTPUT_HANDLE), &cci);
491 }
492
493 VOID GetScreenSize(PSHORT maxx, PSHORT maxy)
494 {
495 CONSOLE_SCREEN_BUFFER_INFO csbi;
496
497 if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
498 {
499 csbi.dwSize.X = 80;
500 csbi.dwSize.Y = 25;
501 }
502
503 if (maxx) *maxx = csbi.dwSize.X;
504 if (maxy) *maxy = csbi.dwSize.Y;
505 }
506
507
508
509
510 #ifdef INCLUDE_CMD_COLOR
511
512 BOOL ConGetDefaultAttributes(PWORD pwDefAttr)
513 {
514 BOOL Success;
515 HANDLE hConsole;
516 CONSOLE_SCREEN_BUFFER_INFO csbi;
517
518 /* Do not modify *pwDefAttr if we fail, in which case use default attributes */
519
520 hConsole = CreateFile(_T("CONOUT$"), GENERIC_READ|GENERIC_WRITE,
521 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
522 OPEN_EXISTING, 0, NULL);
523 if (hConsole == INVALID_HANDLE_VALUE)
524 return FALSE; // No default console
525
526 Success = GetConsoleScreenBufferInfo(hConsole, &csbi);
527 if (Success)
528 *pwDefAttr = csbi.wAttributes;
529
530 CloseHandle(hConsole);
531 return Success;
532 }
533
534 #endif
535
536
537 BOOL ConSetTitle(IN LPCTSTR lpConsoleTitle)
538 {
539 /* Now really set the console title */
540 return SetConsoleTitle(lpConsoleTitle);
541 }
542
543 #ifdef INCLUDE_CMD_BEEP
544 VOID ConRingBell(HANDLE hOutput)
545 {
546 #if 0
547 /* Emit an error beep sound */
548 if (IsConsoleHandle(hOutput))
549 Beep(800, 200);
550 else if (IsTTYHandle(hOutput))
551 ConOutPuts(_T("\a")); // BEL character 0x07
552 else
553 #endif
554 MessageBeep(-1);
555 }
556 #endif
557
558 #ifdef INCLUDE_CMD_CLS
559 VOID ConClearScreen(HANDLE hOutput)
560 {
561 CONSOLE_SCREEN_BUFFER_INFO csbi;
562 COORD coPos;
563 DWORD dwWritten;
564
565 if (GetConsoleScreenBufferInfo(hOutput, &csbi))
566 {
567 coPos.X = 0;
568 coPos.Y = 0;
569 FillConsoleOutputAttribute(hOutput, csbi.wAttributes,
570 csbi.dwSize.X * csbi.dwSize.Y,
571 coPos, &dwWritten);
572 FillConsoleOutputCharacter(hOutput, _T(' '),
573 csbi.dwSize.X * csbi.dwSize.Y,
574 coPos, &dwWritten);
575 SetConsoleCursorPosition(hOutput, coPos);
576 }
577 else
578 {
579 ConOutChar(_T('\f'));
580 }
581 }
582 #endif
583
584 #ifdef INCLUDE_CMD_COLOR
585 BOOL ConSetScreenColor(HANDLE hOutput, WORD wColor, BOOL bFill)
586 {
587 DWORD dwWritten;
588 CONSOLE_SCREEN_BUFFER_INFO csbi;
589 COORD coPos;
590
591 /* Foreground and Background colors can't be the same */
592 if ((wColor & 0x0F) == (wColor & 0xF0) >> 4)
593 return FALSE;
594
595 /* Fill the whole background if needed */
596 if (bFill)
597 {
598 GetConsoleScreenBufferInfo(hOutput, &csbi);
599
600 coPos.X = 0;
601 coPos.Y = 0;
602 FillConsoleOutputAttribute(hOutput,
603 wColor & 0x00FF,
604 csbi.dwSize.X * csbi.dwSize.Y,
605 coPos,
606 &dwWritten);
607 }
608
609 /* Set the text attribute */
610 SetConsoleTextAttribute(hOutput, wColor & 0x00FF);
611 return TRUE;
612 }
613 #endif
614
615 /* EOF */