Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

204 lines
7.3 KiB

  1. /***
  2. *cgetws.c - buffered keyboard input
  3. *
  4. * Copyright (c) 1989-2001, Microsoft Corporation. All rights reserved.
  5. *
  6. *Purpose:
  7. * defines _cgetws() - read a string directly from console
  8. *
  9. *Revision History:
  10. * 04-19-00 GB Module created based on cgets.
  11. * 05-17-00 GB Use ERROR_CALL_NOT_IMPLEMENTED for existance of W API
  12. *
  13. *******************************************************************************/
  14. #include <cruntime.h>
  15. #include <oscalls.h>
  16. #include <mtdll.h>
  17. #include <conio.h>
  18. #include <stdlib.h>
  19. #include <internal.h>
  20. #define BUF_MAX_LEN 64
  21. extern intptr_t _coninpfh;
  22. static int bUseW = 2;
  23. /***
  24. *wchar_t *_cgetws(string) - read string from console
  25. *
  26. *Purpose:
  27. * Reads a string from the console via ReadConsoleW on a cooked console
  28. * handle. string[0] must contain the maximum length of the
  29. * string. Returns pointer to str[2].
  30. *
  31. * NOTE: _cgetsw() does NOT check the pushback character buffer (i.e.,
  32. * _chbuf). Thus, _cgetws() will not return any character that is
  33. * pushed back by the _ungetwch() call.
  34. *
  35. *Entry:
  36. * char *string - place to store read string, str[0] = max length.
  37. *
  38. *Exit:
  39. * returns pointer to str[2], where the string starts.
  40. * returns NULL if error occurs
  41. *
  42. *Exceptions:
  43. *
  44. *******************************************************************************/
  45. wchar_t * __cdecl _cgetws (
  46. wchar_t *string
  47. )
  48. {
  49. ULONG oldstate;
  50. ULONG num_read;
  51. wchar_t *result;
  52. _mlock(_CONIO_LOCK); /* lock the console */
  53. string[1] = 0; /* no chars read yet */
  54. result = &string[2];
  55. /*
  56. * _coninpfh, the handle to the console input, is created the first
  57. * time that either _getch() or _cgets() or _kbhit() is called.
  58. */
  59. if ( _coninpfh == -2 )
  60. __initconin();
  61. if ( _coninpfh == -1 ) {
  62. _munlock(_CONIO_LOCK); /* unlock the console */
  63. return(NULL); /* return failure */
  64. }
  65. GetConsoleMode( (HANDLE)_coninpfh, &oldstate );
  66. SetConsoleMode( (HANDLE)_coninpfh, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT );
  67. // First try usual way just as _cgets
  68. if ( bUseW)
  69. {
  70. if ( !ReadConsoleW( (HANDLE)_coninpfh,
  71. (LPVOID)result,
  72. (unsigned)string[0],
  73. &num_read,
  74. NULL )
  75. )
  76. {
  77. result = NULL;
  78. if ( bUseW == 2 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
  79. bUseW = FALSE;
  80. }
  81. else
  82. bUseW = TRUE;
  83. if ( result != NULL ) {
  84. /* set length of string and null terminate it */
  85. if (string[num_read] == L'\r') {
  86. string[1] = (wchar_t)(num_read - 2);
  87. string[num_read] = L'\0';
  88. } else if ( (num_read == (ULONG)string[0]) &&
  89. (string[num_read + 1] == L'\r') ) {
  90. /* special case 1 - \r\n straddles the boundary */
  91. string[1] = (wchar_t)(num_read -1);
  92. string[1 + num_read] = L'\0';
  93. } else if ( (num_read == 1) && (string[2] == L'\n') ) {
  94. /* special case 2 - read a single '\n'*/
  95. string[1] = string[2] = L'\0';
  96. } else {
  97. string[1] = (wchar_t)num_read;
  98. string[2 + num_read] = L'\0';
  99. }
  100. }
  101. }
  102. // If ReadConsoleW is not present, use ReadConsoleA and then convert
  103. // to Wide Char.
  104. if ( !bUseW)
  105. {
  106. static char AStr[BUF_MAX_LEN +1];
  107. static int in_buff = 0, was_buff_full = 0;
  108. unsigned int Copy, Sz, consoleCP;
  109. unsigned int last_read = 0, i;
  110. consoleCP = GetConsoleCP();
  111. do {
  112. if (!in_buff)
  113. {
  114. if ( !ReadConsoleA( (HANDLE)_coninpfh,
  115. (LPVOID)AStr,
  116. BUF_MAX_LEN,
  117. &num_read,
  118. NULL)
  119. )
  120. result = NULL;
  121. if (result != NULL) {
  122. if (AStr[num_read -2] == '\r')
  123. AStr[num_read -2] = '\0';
  124. else if (num_read == sizeof(AStr) &&
  125. AStr[num_read -1] == '\r')
  126. AStr[num_read -1] = '\0';
  127. else if (num_read == 1 && string[0] == '\n')
  128. AStr[0] = '\0';
  129. else
  130. AStr[num_read] = '\0';
  131. }
  132. }
  133. for ( i = 0; AStr[i] != '\0' &&
  134. i < (BUF_MAX_LEN) &&
  135. last_read < (unsigned)string[0]; i += Sz)
  136. {
  137. // Check if this character is lead byte. If yes, the size
  138. // of this character is 2. Else 1.
  139. if ( IsDBCSLeadByteEx( GetConsoleCP(), AStr[i]))
  140. Sz = 2;
  141. else
  142. Sz = 1;
  143. if ( (Copy = MultiByteToWideChar( consoleCP,
  144. MB_PRECOMPOSED | MB_ERR_INVALID_CHARS,
  145. &AStr[i],
  146. Sz,
  147. &string[2+last_read],
  148. string[0] - last_read)))
  149. {
  150. last_read += Copy;
  151. }
  152. }
  153. // Check if this conversion was from buffer. If yes, was
  154. // buffer fully filled when it was first read using
  155. // ReadConsoleA. If the buffer not fully filled, we don't need
  156. // to read more from buffer. This is necessary to make it
  157. // behave same as if we are reading using ReadConsoleW.
  158. if ( in_buff && i == strlen(AStr))
  159. {
  160. in_buff = 0;
  161. if ( was_buff_full)
  162. {
  163. was_buff_full = 0;
  164. continue;
  165. }
  166. else
  167. {
  168. break;
  169. }
  170. }
  171. else if ( i < (BUF_MAX_LEN))
  172. break;
  173. } while (last_read < (unsigned)string[0]);
  174. // We save the buffer to be used again.
  175. if ( i < strlen(AStr))
  176. {
  177. in_buff = 1;
  178. if ( strlen(AStr) == (BUF_MAX_LEN))
  179. was_buff_full = 1;
  180. memmove(AStr, &AStr[i], BUF_MAX_LEN +1 - i);
  181. }
  182. string[2+last_read] = '\0';
  183. string[1] = (wchar_t)last_read;
  184. }
  185. SetConsoleMode( (HANDLE)_coninpfh, oldstate );
  186. _munlock(_CONIO_LOCK); /* unlock the console */
  187. return result;
  188. }