[CMD]: Continue refactoring to lay out the way to using the CONUTILS library in CMD...
[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(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 INT 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 0;
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 0;
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 0;
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 {
412 return 1;
413 }
414
415 // TODO: Recalculate 'ScreenLines' in case the user redimensions
416 // the window during the prompt.
417
418 /* Reset the number of lines being printed */
419 LineCount = 0;
420 }
421 }
422
423 WriteConsole(hOutput, &szOut[from], i-from, &dwWritten, NULL);
424
425 return 0;
426 }
427
428 INT ConOutPrintfPaging(BOOL NewPage, LPTSTR szFormat, ...)
429 {
430 INT iReturn;
431 va_list arg_ptr;
432
433 va_start(arg_ptr, szFormat);
434 iReturn = ConPrintfVPaging(STD_OUTPUT_HANDLE, NewPage, szFormat, arg_ptr);
435 va_end(arg_ptr);
436 return iReturn;
437 }
438
439 VOID ConOutResPaging(BOOL NewPage, UINT resID)
440 {
441 TCHAR szMsg[RC_STRING_MAX_SIZE];
442 LoadString(CMD_ModuleHandle, resID, szMsg, ARRAYSIZE(szMsg));
443 ConOutPrintfPaging(NewPage, szMsg);
444 }
445
446
447
448 /************************** Console SCREEN functions **************************/
449
450 VOID SetCursorXY(SHORT x, SHORT y)
451 {
452 COORD coPos;
453
454 coPos.X = x;
455 coPos.Y = y;
456 SetConsoleCursorPosition(GetStdHandle (STD_OUTPUT_HANDLE), coPos);
457 }
458
459 VOID GetCursorXY(PSHORT x, PSHORT y)
460 {
461 CONSOLE_SCREEN_BUFFER_INFO csbi;
462
463 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
464
465 *x = csbi.dwCursorPosition.X;
466 *y = csbi.dwCursorPosition.Y;
467 }
468
469 SHORT GetCursorX(VOID)
470 {
471 CONSOLE_SCREEN_BUFFER_INFO csbi;
472
473 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
474 return csbi.dwCursorPosition.X;
475 }
476
477 SHORT GetCursorY(VOID)
478 {
479 CONSOLE_SCREEN_BUFFER_INFO csbi;
480
481 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
482 return csbi.dwCursorPosition.Y;
483 }
484
485 VOID SetCursorType(BOOL bInsert, BOOL bVisible)
486 {
487 CONSOLE_CURSOR_INFO cci;
488
489 cci.dwSize = bInsert ? 10 : 99;
490 cci.bVisible = bVisible;
491
492 SetConsoleCursorInfo(GetStdHandle (STD_OUTPUT_HANDLE), &cci);
493 }
494
495 VOID GetScreenSize(PSHORT maxx, PSHORT maxy)
496 {
497 CONSOLE_SCREEN_BUFFER_INFO csbi;
498
499 if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
500 {
501 csbi.dwSize.X = 80;
502 csbi.dwSize.Y = 25;
503 }
504
505 if (maxx) *maxx = csbi.dwSize.X;
506 if (maxy) *maxy = csbi.dwSize.Y;
507 }
508
509
510
511
512 BOOL ConSetTitle(IN LPCTSTR lpConsoleTitle)
513 {
514 /* Now really set the console title */
515 return SetConsoleTitle(lpConsoleTitle);
516 }
517
518 #ifdef INCLUDE_CMD_BEEP
519 VOID ConRingBell(HANDLE hOutput)
520 {
521 #if 0
522 /* Emit an error beep sound */
523 if (IsConsoleHandle(hOutput))
524 Beep(800, 200);
525 else if (IsTTYHandle(hOutput))
526 ConOutPuts(_T("\a")); // BEL character 0x07
527 else
528 #endif
529 MessageBeep(-1);
530 }
531 #endif
532
533 #ifdef INCLUDE_CMD_CLS
534 VOID ConClearScreen(HANDLE hOutput)
535 {
536 CONSOLE_SCREEN_BUFFER_INFO csbi;
537 COORD coPos;
538 DWORD dwWritten;
539
540 if (GetConsoleScreenBufferInfo(hOutput, &csbi))
541 {
542 coPos.X = 0;
543 coPos.Y = 0;
544 FillConsoleOutputAttribute(hOutput, csbi.wAttributes,
545 csbi.dwSize.X * csbi.dwSize.Y,
546 coPos, &dwWritten);
547 FillConsoleOutputCharacter(hOutput, _T(' '),
548 csbi.dwSize.X * csbi.dwSize.Y,
549 coPos, &dwWritten);
550 SetConsoleCursorPosition(hOutput, coPos);
551 }
552 else
553 {
554 ConOutChar(_T('\f'));
555 }
556 }
557 #endif
558
559 #ifdef INCLUDE_CMD_COLOR
560 BOOL ConSetScreenColor(WORD wColor, BOOL bFill)
561 {
562 HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
563 DWORD dwWritten;
564 CONSOLE_SCREEN_BUFFER_INFO csbi;
565 COORD coPos;
566
567 /* Foreground and Background colors can't be the same */
568 if ((wColor & 0x0F) == (wColor & 0xF0) >> 4)
569 return FALSE;
570
571 /* Fill the whole background if needed */
572 if (bFill)
573 {
574 GetConsoleScreenBufferInfo(hConsole, &csbi);
575
576 coPos.X = 0;
577 coPos.Y = 0;
578 FillConsoleOutputAttribute(hConsole,
579 wColor & 0x00FF,
580 csbi.dwSize.X * csbi.dwSize.Y,
581 coPos,
582 &dwWritten);
583 }
584
585 /* Set the text attribute */
586 SetConsoleTextAttribute(hConsole, wColor & 0x00FF);
587 return TRUE;
588 }
589 #endif
590
591 /* EOF */