ntdll._iswctype should be ntdll.iswctype.
[wine] / dlls / msvcrt / console.c
1 /*
2  * msvcrt.dll console functions
3  *
4  * Copyright 2000 Jon Griffiths
5  *
6  * Note: init and free don't need MT locking since they are called at DLL
7  * (de)attachment time, which is syncronised for us
8  */
9 #include "msvcrt.h"
10 #include "wincon.h"
11
12 #include "msvcrt/conio.h"
13 #include "msvcrt/malloc.h"
14 #include "msvcrt/stdio.h"
15
16 DEFAULT_DEBUG_CHANNEL(msvcrt);
17
18
19
20 /* MT */
21 extern CRITICAL_SECTION MSVCRT_console_cs;
22 #define LOCK_CONSOLE   EnterCriticalSection(&MSVCRT_console_cs)
23 #define UNLOCK_CONSOLE LeaveCriticalSection(&MSVCRT_console_cs)
24
25 static HANDLE MSVCRT_console_in = INVALID_HANDLE_VALUE;
26 static HANDLE MSVCRT_console_out= INVALID_HANDLE_VALUE;
27 static int __MSVCRT_console_buffer = MSVCRT_EOF;
28
29 /* INTERNAL: Initialise console handles */
30 void msvcrt_init_console(void)
31 {
32   TRACE(":Opening console handles\n");
33
34   MSVCRT_console_in = GetStdHandle(STD_INPUT_HANDLE);
35
36   /* FIXME: Should be initialised with:
37    * CreateFileA("CONIN$", GENERIC_READ, FILE_SHARE_READ,
38    * NULL, OPEN_EXISTING, 0, (HANDLE)NULL);
39    */
40
41   MSVCRT_console_out= CreateFileA("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE,
42                                     NULL, OPEN_EXISTING, 0, (HANDLE)NULL);
43
44   if ((MSVCRT_console_in == INVALID_HANDLE_VALUE) ||
45       (MSVCRT_console_out== INVALID_HANDLE_VALUE))
46     WARN(":Console handle Initialisation FAILED!\n");
47 }
48
49 /* INTERNAL: Free console handles */
50 void msvcrt_free_console(void)
51 {
52   TRACE(":Closing console handles\n");
53   CloseHandle(MSVCRT_console_in);
54   CloseHandle(MSVCRT_console_out);
55 }
56
57 /*********************************************************************
58  *              _cputs (MSVCRT.@)
59  */
60 int _cputs(const char* str)
61 {
62   DWORD count;
63   int retval = MSVCRT_EOF;
64
65   LOCK_CONSOLE;
66   if (WriteConsoleA(MSVCRT_console_out, str, strlen(str), &count, NULL)
67       && count == 1)
68     retval = 0;
69   UNLOCK_CONSOLE;
70   return retval;
71 }
72
73 /*********************************************************************
74  *              _getch (MSVCRT.@)
75  */
76 int _getch(void)
77 {
78   int retval = MSVCRT_EOF;
79
80   LOCK_CONSOLE;
81   if (__MSVCRT_console_buffer != MSVCRT_EOF)
82   {
83     retval = __MSVCRT_console_buffer;
84     __MSVCRT_console_buffer = MSVCRT_EOF;
85   }
86   else
87   {
88     INPUT_RECORD ir;
89     DWORD count;
90     DWORD mode = 0;
91
92     GetConsoleMode(MSVCRT_console_in, &mode);
93     if(mode)
94       SetConsoleMode(MSVCRT_console_in, 0);
95
96     do {
97       if (ReadConsoleInputA(MSVCRT_console_in, &ir, 1, &count))
98       {
99         /* Only interested in ASCII chars */
100         if (ir.EventType == KEY_EVENT &&
101             ir.Event.KeyEvent.bKeyDown &&
102             ir.Event.KeyEvent.uChar.AsciiChar)
103         {
104           retval = ir.Event.KeyEvent.uChar.AsciiChar;
105           break;
106         }
107       }
108       else
109         break;
110     } while(1);
111     if (mode)
112       SetConsoleMode(MSVCRT_console_in, mode);
113   }
114   UNLOCK_CONSOLE;
115   return retval;
116 }
117
118 /*********************************************************************
119  *              _putch (MSVCRT.@)
120  */
121 int _putch(int c)
122 {
123   int retval = MSVCRT_EOF;
124   DWORD count;
125   LOCK_CONSOLE;
126   if (WriteConsoleA(MSVCRT_console_out, &c, 1, &count, NULL) && count == 1)
127     retval = c;
128   UNLOCK_CONSOLE;
129   return retval;
130 }
131
132 /*********************************************************************
133  *              _getche (MSVCRT.@)
134  */
135 int _getche(void)
136 {
137   int retval;
138   LOCK_CONSOLE;
139   retval = _getch();
140   if (retval != MSVCRT_EOF)
141     retval = _putch(retval);
142   UNLOCK_CONSOLE;
143   return retval;
144 }
145
146 /*********************************************************************
147  *              _cgets (MSVCRT.@)
148  */
149 char* _cgets(char* str)
150 {
151   char *buf = str + 2;
152   int c;
153   str[1] = 0; /* Length */
154   /* FIXME: No editing of string supported */
155   LOCK_CONSOLE;
156   do
157   {
158     if (str[1] >= str[0] || (str[1]++, c = _getche()) == MSVCRT_EOF || c == '\n')
159       break;
160     *buf++ = c & 0xff;
161   } while (1);
162   UNLOCK_CONSOLE;
163   *buf = '\0';
164   return str + 2;
165 }
166
167 /*********************************************************************
168  *              _ungetch (MSVCRT.@)
169  */
170 int _ungetch(int c)
171 {
172   int retval = MSVCRT_EOF;
173   LOCK_CONSOLE;
174   if (c != MSVCRT_EOF && __MSVCRT_console_buffer == MSVCRT_EOF)
175     retval = __MSVCRT_console_buffer = c;
176   UNLOCK_CONSOLE;
177   return retval;
178 }
179
180 /*********************************************************************
181  *              _cscanf (MSVCRT.@)
182  */
183 int _cscanf(const char* format, ...)
184 {
185     /* NOTE: If you extend this function, extend MSVCRT_fscanf in file.c too */
186     int rd = 0;
187     int nch;
188     va_list ap;
189     if (!*format) return 0;
190     WARN("\"%s\": semi-stub\n", format);
191     va_start(ap, format);
192   LOCK_CONSOLE;
193     nch = _getch();
194     while (*format) {
195         if (*format == ' ') {
196             /* skip whitespace */
197             while ((nch!=MSVCRT_EOF) && isspace(nch))
198                 nch = _getch();
199         }
200         else if (*format == '%') {
201             int st = 0;
202             format++;
203             switch(*format) {
204             case 'd': { /* read an integer */
205                     int*val = va_arg(ap, int*);
206                     int cur = 0;
207                     /* skip initial whitespace */
208                     while ((nch!=MSVCRT_EOF) && isspace(nch))
209                         nch = _getch();
210                     /* get sign and first digit */
211                     if (nch == '-') {
212                         nch = _getch();
213                         if (isdigit(nch))
214                             cur = -(nch - '0');
215                         else break;
216                     } else {
217                         if (isdigit(nch))
218                             cur = nch - '0';
219                         else break;
220                     }
221                     nch = _getch();
222                     /* read until no more digits */
223                     while ((nch!=MSVCRT_EOF) && isdigit(nch)) {
224                         cur = cur*10 + (nch - '0');
225                         nch = _getch();
226                     }
227                     st = 1;
228                     *val = cur;
229                 }
230                 break;
231             case 'f': { /* read a float */
232                     float*val = va_arg(ap, float*);
233                     float cur = 0;
234                     /* skip initial whitespace */
235                     while ((nch!=MSVCRT_EOF) && isspace(nch))
236                         nch = _getch();
237                     /* get sign and first digit */
238                     if (nch == '-') {
239                         nch = _getch();
240                         if (isdigit(nch))
241                             cur = -(nch - '0');
242                         else break;
243                     } else {
244                         if (isdigit(nch))
245                             cur = nch - '0';
246                         else break;
247                     }
248                     /* read until no more digits */
249                     while ((nch!=MSVCRT_EOF) && isdigit(nch)) {
250                         cur = cur*10 + (nch - '0');
251                         nch = _getch();
252                     }
253                     if (nch == '.') {
254                         /* handle decimals */
255                         float dec = 1;
256                         nch = _getch();
257                         while ((nch!=MSVCRT_EOF) && isdigit(nch)) {
258                             dec /= 10;
259                             cur += dec * (nch - '0');
260                             nch = _getch();
261                         }
262                     }
263                     st = 1;
264                     *val = cur;
265                 }
266                 break;
267             case 's': { /* read a word */
268                     char*str = va_arg(ap, char*);
269                     char*sptr = str;
270                     /* skip initial whitespace */
271                     while ((nch!=MSVCRT_EOF) && isspace(nch))
272                         nch = _getch();
273                     /* read until whitespace */
274                     while ((nch!=MSVCRT_EOF) && !isspace(nch)) {
275                         *sptr++ = nch; st++;
276                         nch = _getch();
277                     }
278                     /* terminate */
279                     *sptr = 0;
280                     TRACE("read word: %s\n", str);
281                 }
282                 break;
283             default: FIXME("unhandled: %%%c\n", *format);
284             }
285             if (st) rd++;
286             else break;
287         }
288         else {
289             /* check for character match */
290             if (nch == *format)
291                nch = _getch();
292             else break;
293         }
294         format++;
295     }
296     if (nch != MSVCRT_EOF)
297       _ungetch(nch);
298     UNLOCK_CONSOLE;
299     va_end(ap);
300     TRACE("returning %d\n", rd);
301     return rd;
302 }
303
304 /*********************************************************************
305  *              _kbhit (MSVCRT.@)
306  */
307 int _kbhit(void)
308 {
309   int retval = 0;
310
311   LOCK_CONSOLE;
312   if (__MSVCRT_console_buffer != MSVCRT_EOF)
313     retval = 1;
314   else
315   {
316     /* FIXME: There has to be a faster way than this in Win32.. */
317     INPUT_RECORD *ir = NULL;
318     DWORD count = 0, i;
319
320     GetNumberOfConsoleInputEvents(MSVCRT_console_in, &count);
321
322     if (count && (ir = MSVCRT_malloc(count * sizeof(INPUT_RECORD))) &&
323         PeekConsoleInputA(MSVCRT_console_in, ir, count, &count))
324       for(i = 0; i < count - 1; i++)
325       {
326         if (ir[i].EventType == KEY_EVENT &&
327             ir[i].Event.KeyEvent.bKeyDown &&
328             ir[i].Event.KeyEvent.uChar.AsciiChar)
329         {
330           retval = 1;
331           break;
332         }
333       }
334     if (ir)
335       MSVCRT_free(ir);
336   }
337   UNLOCK_CONSOLE;
338   return retval;
339 }
340
341
342 /*********************************************************************
343  *              _cprintf (MSVCRT.@)
344  */
345 int _cprintf(const char* format, ...)
346 {
347   char buf[2048], *mem = buf;
348   int written, resize = sizeof(buf), retval;
349   va_list valist;
350
351   va_start( valist, format );
352   /* There are two conventions for snprintf failing:
353    * Return -1 if we truncated, or
354    * Return the number of bytes that would have been written
355    * The code below handles both cases
356    */
357   while ((written = _snprintf( mem, resize, format, valist )) == -1 ||
358           written > resize)
359   {
360     resize = (written == -1 ? resize * 2 : written + 1);
361     if (mem != buf)
362       MSVCRT_free (mem);
363     if (!(mem = (char *)MSVCRT_malloc(resize)))
364       return MSVCRT_EOF;
365     va_start( valist, format );
366   }
367   va_end(valist);
368   LOCK_CONSOLE;
369   retval = _cputs( mem );
370   UNLOCK_CONSOLE;
371   if (mem != buf)
372     MSVCRT_free (mem);
373   return retval;
374 }