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