2 * CONSOLE.C - console input/output functions.
7 * 20-Jan-1999 (Eric Kohl)
10 * 03-Apr-2005 (Magnus Olsen <magnus@greatlord.com>)
11 * Remove all hardcoded strings in En.rc
13 * 01-Jul-2005 (Brandon Turner <turnerb7@msu.edu>)
14 * Added ConPrintfPaging and ConOutPrintfPaging
16 * 02-Feb-2007 (Paolo Devoti <devotip at gmail.com>)
17 * Fixed ConPrintfPaging
22 #define OUTPUT_BUFFER_SIZE 4096
24 /* Cache codepage for text streams */
29 BOOL
IsConsoleHandle(HANDLE hHandle
)
33 /* Check whether the handle may be that of a console... */
34 if ((GetFileType(hHandle
) & ~FILE_TYPE_REMOTE
) != FILE_TYPE_CHAR
)
38 * It may be. Perform another test... The idea comes from the
39 * MSDN description of the WriteConsole API:
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."
50 return GetConsoleMode(hHandle
, &dwMode
);
55 /********************* Console STREAM IN utility functions ********************/
57 VOID
ConInDisable(VOID
)
59 HANDLE hInput
= GetStdHandle(STD_INPUT_HANDLE
);
62 GetConsoleMode(hInput
, &dwMode
);
63 dwMode
&= ~ENABLE_PROCESSED_INPUT
;
64 SetConsoleMode(hInput
, dwMode
);
67 VOID
ConInEnable(VOID
)
69 HANDLE hInput
= GetStdHandle(STD_INPUT_HANDLE
);
72 GetConsoleMode(hInput
, &dwMode
);
73 dwMode
|= ENABLE_PROCESSED_INPUT
;
74 SetConsoleMode(hInput
, dwMode
);
79 FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE
));
82 VOID
ConInKey(PINPUT_RECORD lpBuffer
)
84 HANDLE hInput
= GetStdHandle(STD_INPUT_HANDLE
);
87 if (hInput
== INVALID_HANDLE_VALUE
)
88 WARN ("Invalid input handle!!!\n");
92 ReadConsoleInput(hInput
, lpBuffer
, 1, &dwRead
);
93 if ((lpBuffer
->EventType
== KEY_EVENT
) &&
94 (lpBuffer
->Event
.KeyEvent
.bKeyDown
== TRUE
))
100 VOID
ConInString(LPTSTR lpInput
, DWORD dwLength
)
110 pBuf
= (PCHAR
)cmd_alloc(dwLength
- 1);
114 ZeroMemory(lpInput
, dwLength
* sizeof(TCHAR
));
115 hFile
= GetStdHandle(STD_INPUT_HANDLE
);
116 GetConsoleMode(hFile
, &dwOldMode
);
118 SetConsoleMode(hFile
, ENABLE_LINE_INPUT
| ENABLE_ECHO_INPUT
);
120 ReadFile(hFile
, (PVOID
)pBuf
, dwLength
- 1, &dwRead
, NULL
);
123 MultiByteToWideChar(InputCodePage
, 0, pBuf
, dwRead
, lpInput
, dwLength
- 1);
126 for (p
= lpInput
; *p
; p
++)
128 if (*p
== _T('\x0d'))
135 SetConsoleMode(hFile
, dwOldMode
);
140 /******************** Console STREAM OUT utility functions ********************/
142 static VOID
ConWrite(DWORD nStdHandle
, TCHAR
*str
, DWORD len
)
144 DWORD dwNumBytes
= 0;
145 HANDLE hOutput
= GetStdHandle(nStdHandle
);
148 /* If we don't write anything, just return */
149 if (!str
|| len
== 0)
152 /* Check whether we are writing to a console and if so, write to it */
153 if (IsConsoleHandle(hOutput
))
155 WriteConsole(hOutput
, str
, len
, &dwNumBytes
, NULL
);
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 */
164 WCHAR
*buffer
= cmd_alloc(len
* sizeof(WCHAR
));
167 error_out_of_memory();
170 len
= (DWORD
)MultiByteToWideChar(OutputCodePage
, 0, str
, (INT
)len
, buffer
, (INT
)len
);
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.
179 * This fixes output in files and serial console.
183 /* Loop until we find a \r or \n character */
184 // FIXME: What about the pair \r\n ?
186 while (len
> 0 && *(PWCHAR
)p
!= L
'\r' && *(PWCHAR
)p
!= L
'\n')
188 /* Advance one character */
189 p
= (PVOID
)((PWCHAR
)p
+ 1);
193 /* Write everything up to \r or \n */
194 dwNumBytes
= ((PWCHAR
)p
- (PWCHAR
)str
) * sizeof(WCHAR
);
195 WriteFile(hOutput
, str
, dwNumBytes
, &dwNumBytes
, NULL
);
197 /* If we hit \r or \n ... */
198 if (len
> 0 && (*(PWCHAR
)p
== L
'\r' || *(PWCHAR
)p
== L
'\n'))
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);
214 CHAR
*buffer
= cmd_alloc(len
* MB_LEN_MAX
* sizeof(CHAR
));
217 error_out_of_memory();
220 len
= WideCharToMultiByte(OutputCodePage
, 0, str
, len
, buffer
, len
* MB_LEN_MAX
, NULL
, NULL
);
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.
229 * This fixes output in files and serial console.
233 /* Loop until we find a \r or \n character */
234 // FIXME: What about the pair \r\n ?
236 while (len
> 0 && *(PCHAR
)p
!= '\r' && *(PCHAR
)p
!= '\n')
238 /* Advance one character */
239 p
= (PVOID
)((PCHAR
)p
+ 1);
243 /* Write everything up to \r or \n */
244 dwNumBytes
= ((PCHAR
)p
- (PCHAR
)str
) * sizeof(CHAR
);
245 WriteFile(hOutput
, str
, dwNumBytes
, &dwNumBytes
, NULL
);
247 /* If we hit \r or \n ... */
248 if (len
> 0 && (*(PCHAR
)p
== '\r' || *(PCHAR
)p
== '\n'))
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);
263 VOID
ConPuts(DWORD nStdHandle
, LPTSTR szText
)
265 ConWrite(nStdHandle
, szText
, (DWORD
)_tcslen(szText
));
268 VOID
ConResPuts(DWORD nStdHandle
, UINT resID
)
270 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
271 LoadString(CMD_ModuleHandle
, resID
, szMsg
, ARRAYSIZE(szMsg
));
272 ConPuts(nStdHandle
, szMsg
);
275 VOID
ConOutChar(TCHAR c
)
277 ConWrite(STD_OUTPUT_HANDLE
, &c
, 1);
280 VOID
ConErrChar(TCHAR c
)
282 ConWrite(STD_ERROR_HANDLE
, &c
, 1);
285 VOID
ConPrintfV(DWORD nStdHandle
, LPTSTR szFormat
, va_list arg_ptr
)
287 TCHAR szOut
[OUTPUT_BUFFER_SIZE
];
290 len
= (DWORD
)_vstprintf(szOut
, szFormat
, arg_ptr
);
291 ConWrite(nStdHandle
, szOut
, len
);
294 VOID
ConPrintf(DWORD nStdHandle
, LPTSTR szFormat
, ...)
298 va_start(arg_ptr
, szFormat
);
299 ConPrintfV(nStdHandle
, szFormat
, arg_ptr
);
303 VOID
ConResPrintf(DWORD nStdHandle
, UINT resID
, ...)
305 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
308 va_start(arg_ptr
, resID
);
309 LoadString(CMD_ModuleHandle
, resID
, szMsg
, ARRAYSIZE(szMsg
));
310 ConPrintfV(nStdHandle
, szMsg
, arg_ptr
);
314 VOID
ConFormatMessage(DWORD nStdHandle
, DWORD MessageId
, ...)
320 va_start(arg_ptr
, MessageId
);
321 ret
= FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
,
324 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
332 ConPuts(nStdHandle
, text
);
337 ConResPrintf(nStdHandle
, STRING_CONSOLE_ERROR
, MessageId
);
343 /************************** Console PAGER functions ***************************/
345 BOOL
ConPrintfVPaging(DWORD nStdHandle
, BOOL NewPage
, LPTSTR szFormat
, va_list arg_ptr
)
348 CONSOLE_SCREEN_BUFFER_INFO csbi
;
349 TCHAR szOut
[OUTPUT_BUFFER_SIZE
];
351 HANDLE hOutput
= GetStdHandle(nStdHandle
);
353 /* Used to count number of lines since last pause */
354 static int LineCount
= 0;
356 /* Used to see how big the screen is */
359 /* Chars since start of line */
367 /* Reset LineCount and return if no string has been given */
368 if (szFormat
== NULL
)
371 /* Get the size of the visual screen that can be printed to */
372 if (!IsConsoleHandle(hOutput
) || !GetConsoleScreenBufferInfo(hOutput
, &csbi
))
374 /* We assume it's a file handle */
375 ConPrintfV(nStdHandle
, szFormat
, arg_ptr
);
380 * Get the number of lines currently displayed on screen, minus 1
381 * to account for the "press any key..." prompt from PagePrompt().
383 ScreenLines
= (csbi
.srWindow
.Bottom
- csbi
.srWindow
.Top
);
384 CharSL
= csbi
.dwCursorPosition
.X
;
386 /* Make sure the user doesn't have the screen too small */
389 ConPrintfV(nStdHandle
, szFormat
, arg_ptr
);
393 len
= _vstprintf(szOut
, szFormat
, arg_ptr
);
397 /* Search until the end of a line is reached */
398 if (szOut
[i
++] != _T('\n') && ++CharSL
< csbi
.dwSize
.X
)
404 if (LineCount
>= ScreenLines
)
406 WriteConsole(hOutput
, &szOut
[from
], i
-from
, &dwWritten
, NULL
);
409 /* Prompt the user */
410 if (PagePrompt() != PROMPT_YES
)
413 // TODO: Recalculate 'ScreenLines' in case the user redimensions
414 // the window during the prompt.
416 /* Reset the number of lines being printed */
421 WriteConsole(hOutput
, &szOut
[from
], i
-from
, &dwWritten
, NULL
);
426 BOOL
ConOutPrintfPaging(BOOL NewPage
, LPTSTR szFormat
, ...)
431 va_start(arg_ptr
, szFormat
);
432 bRet
= ConPrintfVPaging(STD_OUTPUT_HANDLE
, NewPage
, szFormat
, arg_ptr
);
437 VOID
ConOutResPaging(BOOL NewPage
, UINT resID
)
439 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
440 LoadString(CMD_ModuleHandle
, resID
, szMsg
, ARRAYSIZE(szMsg
));
441 ConOutPrintfPaging(NewPage
, szMsg
);
446 /************************** Console SCREEN functions **************************/
448 VOID
SetCursorXY(SHORT x
, SHORT y
)
454 SetConsoleCursorPosition(GetStdHandle (STD_OUTPUT_HANDLE
), coPos
);
457 VOID
GetCursorXY(PSHORT x
, PSHORT y
)
459 CONSOLE_SCREEN_BUFFER_INFO csbi
;
461 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE
), &csbi
);
463 *x
= csbi
.dwCursorPosition
.X
;
464 *y
= csbi
.dwCursorPosition
.Y
;
467 SHORT
GetCursorX(VOID
)
469 CONSOLE_SCREEN_BUFFER_INFO csbi
;
471 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE
), &csbi
);
472 return csbi
.dwCursorPosition
.X
;
475 SHORT
GetCursorY(VOID
)
477 CONSOLE_SCREEN_BUFFER_INFO csbi
;
479 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE
), &csbi
);
480 return csbi
.dwCursorPosition
.Y
;
483 VOID
SetCursorType(BOOL bInsert
, BOOL bVisible
)
485 CONSOLE_CURSOR_INFO cci
;
487 cci
.dwSize
= bInsert
? 10 : 99;
488 cci
.bVisible
= bVisible
;
490 SetConsoleCursorInfo(GetStdHandle (STD_OUTPUT_HANDLE
), &cci
);
493 VOID
GetScreenSize(PSHORT maxx
, PSHORT maxy
)
495 CONSOLE_SCREEN_BUFFER_INFO csbi
;
497 if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE
), &csbi
))
503 if (maxx
) *maxx
= csbi
.dwSize
.X
;
504 if (maxy
) *maxy
= csbi
.dwSize
.Y
;
510 #ifdef INCLUDE_CMD_COLOR
512 BOOL
ConGetDefaultAttributes(PWORD pwDefAttr
)
516 CONSOLE_SCREEN_BUFFER_INFO csbi
;
518 /* Do not modify *pwDefAttr if we fail, in which case use default attributes */
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
526 Success
= GetConsoleScreenBufferInfo(hConsole
, &csbi
);
528 *pwDefAttr
= csbi
.wAttributes
;
530 CloseHandle(hConsole
);
537 BOOL
ConSetTitle(IN LPCTSTR lpConsoleTitle
)
539 /* Now really set the console title */
540 return SetConsoleTitle(lpConsoleTitle
);
543 #ifdef INCLUDE_CMD_BEEP
544 VOID
ConRingBell(HANDLE hOutput
)
547 /* Emit an error beep sound */
548 if (IsConsoleHandle(hOutput
))
550 else if (IsTTYHandle(hOutput
))
551 ConOutPuts(_T("\a")); // BEL character 0x07
558 #ifdef INCLUDE_CMD_CLS
559 VOID
ConClearScreen(HANDLE hOutput
)
561 CONSOLE_SCREEN_BUFFER_INFO csbi
;
565 if (GetConsoleScreenBufferInfo(hOutput
, &csbi
))
569 FillConsoleOutputAttribute(hOutput
, csbi
.wAttributes
,
570 csbi
.dwSize
.X
* csbi
.dwSize
.Y
,
572 FillConsoleOutputCharacter(hOutput
, _T(' '),
573 csbi
.dwSize
.X
* csbi
.dwSize
.Y
,
575 SetConsoleCursorPosition(hOutput
, coPos
);
579 ConOutChar(_T('\f'));
584 #ifdef INCLUDE_CMD_COLOR
585 BOOL
ConSetScreenColor(HANDLE hOutput
, WORD wColor
, BOOL bFill
)
588 CONSOLE_SCREEN_BUFFER_INFO csbi
;
591 /* Foreground and Background colors can't be the same */
592 if ((wColor
& 0x0F) == (wColor
& 0xF0) >> 4)
595 /* Fill the whole background if needed */
598 GetConsoleScreenBufferInfo(hOutput
, &csbi
);
602 FillConsoleOutputAttribute(hOutput
,
604 csbi
.dwSize
.X
* csbi
.dwSize
.Y
,
609 /* Set the text attribute */
610 SetConsoleTextAttribute(hOutput
, wColor
& 0x00FF);