205 lines
7.3 KiB
C
205 lines
7.3 KiB
C
|
/***
|
||
|
*cgetws.c - buffered keyboard input
|
||
|
*
|
||
|
* Copyright (c) 1989-2001, Microsoft Corporation. All rights reserved.
|
||
|
*
|
||
|
*Purpose:
|
||
|
* defines _cgetws() - read a string directly from console
|
||
|
*
|
||
|
*Revision History:
|
||
|
* 04-19-00 GB Module created based on cgets.
|
||
|
* 05-17-00 GB Use ERROR_CALL_NOT_IMPLEMENTED for existance of W API
|
||
|
*
|
||
|
*******************************************************************************/
|
||
|
#include <cruntime.h>
|
||
|
#include <oscalls.h>
|
||
|
#include <mtdll.h>
|
||
|
#include <conio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <internal.h>
|
||
|
|
||
|
#define BUF_MAX_LEN 64
|
||
|
|
||
|
extern intptr_t _coninpfh;
|
||
|
static int bUseW = 2;
|
||
|
|
||
|
/***
|
||
|
*wchar_t *_cgetws(string) - read string from console
|
||
|
*
|
||
|
*Purpose:
|
||
|
* Reads a string from the console via ReadConsoleW on a cooked console
|
||
|
* handle. string[0] must contain the maximum length of the
|
||
|
* string. Returns pointer to str[2].
|
||
|
*
|
||
|
* NOTE: _cgetsw() does NOT check the pushback character buffer (i.e.,
|
||
|
* _chbuf). Thus, _cgetws() will not return any character that is
|
||
|
* pushed back by the _ungetwch() call.
|
||
|
*
|
||
|
*Entry:
|
||
|
* char *string - place to store read string, str[0] = max length.
|
||
|
*
|
||
|
*Exit:
|
||
|
* returns pointer to str[2], where the string starts.
|
||
|
* returns NULL if error occurs
|
||
|
*
|
||
|
*Exceptions:
|
||
|
*
|
||
|
*******************************************************************************/
|
||
|
|
||
|
wchar_t * __cdecl _cgetws (
|
||
|
wchar_t *string
|
||
|
)
|
||
|
{
|
||
|
ULONG oldstate;
|
||
|
ULONG num_read;
|
||
|
wchar_t *result;
|
||
|
|
||
|
_mlock(_CONIO_LOCK); /* lock the console */
|
||
|
|
||
|
string[1] = 0; /* no chars read yet */
|
||
|
result = &string[2];
|
||
|
|
||
|
/*
|
||
|
* _coninpfh, the handle to the console input, is created the first
|
||
|
* time that either _getch() or _cgets() or _kbhit() is called.
|
||
|
*/
|
||
|
|
||
|
if ( _coninpfh == -2 )
|
||
|
__initconin();
|
||
|
|
||
|
if ( _coninpfh == -1 ) {
|
||
|
_munlock(_CONIO_LOCK); /* unlock the console */
|
||
|
return(NULL); /* return failure */
|
||
|
}
|
||
|
|
||
|
GetConsoleMode( (HANDLE)_coninpfh, &oldstate );
|
||
|
SetConsoleMode( (HANDLE)_coninpfh, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT );
|
||
|
// First try usual way just as _cgets
|
||
|
if ( bUseW)
|
||
|
{
|
||
|
if ( !ReadConsoleW( (HANDLE)_coninpfh,
|
||
|
(LPVOID)result,
|
||
|
(unsigned)string[0],
|
||
|
&num_read,
|
||
|
NULL )
|
||
|
)
|
||
|
{
|
||
|
result = NULL;
|
||
|
if ( bUseW == 2 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
|
||
|
bUseW = FALSE;
|
||
|
}
|
||
|
else
|
||
|
bUseW = TRUE;
|
||
|
|
||
|
if ( result != NULL ) {
|
||
|
|
||
|
/* set length of string and null terminate it */
|
||
|
|
||
|
if (string[num_read] == L'\r') {
|
||
|
string[1] = (wchar_t)(num_read - 2);
|
||
|
string[num_read] = L'\0';
|
||
|
} else if ( (num_read == (ULONG)string[0]) &&
|
||
|
(string[num_read + 1] == L'\r') ) {
|
||
|
/* special case 1 - \r\n straddles the boundary */
|
||
|
string[1] = (wchar_t)(num_read -1);
|
||
|
string[1 + num_read] = L'\0';
|
||
|
} else if ( (num_read == 1) && (string[2] == L'\n') ) {
|
||
|
/* special case 2 - read a single '\n'*/
|
||
|
string[1] = string[2] = L'\0';
|
||
|
} else {
|
||
|
string[1] = (wchar_t)num_read;
|
||
|
string[2 + num_read] = L'\0';
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// If ReadConsoleW is not present, use ReadConsoleA and then convert
|
||
|
// to Wide Char.
|
||
|
if ( !bUseW)
|
||
|
{
|
||
|
static char AStr[BUF_MAX_LEN +1];
|
||
|
static int in_buff = 0, was_buff_full = 0;
|
||
|
unsigned int Copy, Sz, consoleCP;
|
||
|
unsigned int last_read = 0, i;
|
||
|
consoleCP = GetConsoleCP();
|
||
|
do {
|
||
|
if (!in_buff)
|
||
|
{
|
||
|
if ( !ReadConsoleA( (HANDLE)_coninpfh,
|
||
|
(LPVOID)AStr,
|
||
|
BUF_MAX_LEN,
|
||
|
&num_read,
|
||
|
NULL)
|
||
|
)
|
||
|
result = NULL;
|
||
|
if (result != NULL) {
|
||
|
if (AStr[num_read -2] == '\r')
|
||
|
AStr[num_read -2] = '\0';
|
||
|
else if (num_read == sizeof(AStr) &&
|
||
|
AStr[num_read -1] == '\r')
|
||
|
AStr[num_read -1] = '\0';
|
||
|
else if (num_read == 1 && string[0] == '\n')
|
||
|
AStr[0] = '\0';
|
||
|
else
|
||
|
AStr[num_read] = '\0';
|
||
|
}
|
||
|
}
|
||
|
for ( i = 0; AStr[i] != '\0' &&
|
||
|
i < (BUF_MAX_LEN) &&
|
||
|
last_read < (unsigned)string[0]; i += Sz)
|
||
|
{
|
||
|
// Check if this character is lead byte. If yes, the size
|
||
|
// of this character is 2. Else 1.
|
||
|
if ( IsDBCSLeadByteEx( GetConsoleCP(), AStr[i]))
|
||
|
Sz = 2;
|
||
|
else
|
||
|
Sz = 1;
|
||
|
if ( (Copy = MultiByteToWideChar( consoleCP,
|
||
|
MB_PRECOMPOSED | MB_ERR_INVALID_CHARS,
|
||
|
&AStr[i],
|
||
|
Sz,
|
||
|
&string[2+last_read],
|
||
|
string[0] - last_read)))
|
||
|
{
|
||
|
last_read += Copy;
|
||
|
}
|
||
|
}
|
||
|
// Check if this conversion was from buffer. If yes, was
|
||
|
// buffer fully filled when it was first read using
|
||
|
// ReadConsoleA. If the buffer not fully filled, we don't need
|
||
|
// to read more from buffer. This is necessary to make it
|
||
|
// behave same as if we are reading using ReadConsoleW.
|
||
|
if ( in_buff && i == strlen(AStr))
|
||
|
{
|
||
|
in_buff = 0;
|
||
|
if ( was_buff_full)
|
||
|
{
|
||
|
was_buff_full = 0;
|
||
|
continue;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else if ( i < (BUF_MAX_LEN))
|
||
|
break;
|
||
|
} while (last_read < (unsigned)string[0]);
|
||
|
// We save the buffer to be used again.
|
||
|
if ( i < strlen(AStr))
|
||
|
{
|
||
|
in_buff = 1;
|
||
|
if ( strlen(AStr) == (BUF_MAX_LEN))
|
||
|
was_buff_full = 1;
|
||
|
memmove(AStr, &AStr[i], BUF_MAX_LEN +1 - i);
|
||
|
}
|
||
|
string[2+last_read] = '\0';
|
||
|
string[1] = (wchar_t)last_read;
|
||
|
}
|
||
|
|
||
|
SetConsoleMode( (HANDLE)_coninpfh, oldstate );
|
||
|
|
||
|
_munlock(_CONIO_LOCK); /* unlock the console */
|
||
|
|
||
|
return result;
|
||
|
}
|