Fixed some issues found by winapi_check.
[wine] / dlls / crtdll / console.c
1 /*
2  * CRTDLL console functions
3  * 
4  * Copyright 2000 Jon Griffiths
5  *
6  * NOTES
7  * Only a one byte ungetch buffer is implemented, as per MS docs.
8  * Output is not redirectable using these functions, as per MS docs.
9  *
10  * FIXME:
11  * There are several problems with the console input mechanism as
12  * currently implemented in Wine. When these are ironed out the
13  * getch() function will work correctly (gets() is currently fine).
14  * The problem is that opening CONIN$ does not work, and
15  * reading from STD_INPUT_HANDLE is line buffered.
16  */
17 #include "crtdll.h"
18 #include "wincon.h"
19 #include <stdio.h>
20
21 DEFAULT_DEBUG_CHANNEL(crtdll);
22
23 static HANDLE __CRTDLL_console_in = INVALID_HANDLE_VALUE;
24 static HANDLE __CRTDLL_console_out = INVALID_HANDLE_VALUE;
25 static int __CRTDLL_console_buffer = CRTDLL_EOF;
26
27
28 /* INTERNAL: Initialise console handles */
29 VOID __CRTDLL_init_console(VOID)
30 {
31   TRACE(":Opening console handles\n");
32
33   __CRTDLL_console_in = GetStdHandle(STD_INPUT_HANDLE);
34
35 /* FIXME: Should be initialised with:
36    * CreateFileA("CONIN$", GENERIC_READ, FILE_SHARE_READ,
37    * NULL, OPEN_EXISTING, 0, (HANDLE)NULL);
38    */
39
40   __CRTDLL_console_out = CreateFileA("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE,
41                                     NULL, OPEN_EXISTING, 0, (HANDLE)NULL);
42
43   if ((__CRTDLL_console_in == INVALID_HANDLE_VALUE) ||
44       (__CRTDLL_console_out == INVALID_HANDLE_VALUE))
45     WARN(":Console handle Initialisation FAILED!\n");
46 }
47
48 /* INTERNAL: Free console handles */
49 void __CRTDLL_free_console(void)
50 {
51   TRACE(":Closing console handles\n");
52   CloseHandle(__CRTDLL_console_in);
53   CloseHandle(__CRTDLL_console_out);
54 }
55
56
57 /*********************************************************************
58  *                  _cgets       (CRTDLL.050)
59  *
60  * Get a string from CONIN$.
61  */
62 LPSTR __cdecl CRTDLL__cgets(LPSTR str)
63 {
64   char *buf = str + 2; 
65   int c;
66   str[1] = 0; /* Length */
67   /* FIXME: No editing of string supported */
68   do
69   {
70     if (str[1] >= str[0] || (str[1]++, c = CRTDLL__getche()) == CRTDLL_EOF || c == '\n')
71     {
72       *buf = '\0';
73       return str + 2;
74     }
75     *buf++ = c & 0xff;
76   } while(1);
77 }
78
79
80 /*********************************************************************
81  *                  _cprintf     (CRTDLL.064)
82  *
83  * Write a formatted string to CONOUT$.
84  */
85 INT __cdecl CRTDLL__cprintf( LPCSTR format, ... )
86 {
87   va_list valist;
88   char buffer[2048];
89
90   va_start( valist, format );
91   if (snprintf( buffer, sizeof(buffer), format, valist ) == -1)
92     ERR("Format too large for internal buffer!\n");
93   va_end(valist);
94   return CRTDLL__cputs( buffer );
95 }
96
97
98 /*********************************************************************
99  *                  _cputs       (CRTDLL.065)
100  *
101  * Write a string to CONOUT$.
102  */
103 INT __cdecl CRTDLL__cputs(LPCSTR str)
104 {
105   DWORD count;
106   if (WriteConsoleA(__CRTDLL_console_out, str, strlen(str), &count, NULL)
107       && count == 1)
108     return 0;
109   return CRTDLL_EOF;
110 }
111
112
113 /*********************************************************************
114  *                  _cscanf      (CRTDLL.067)
115  *
116  * Read formatted input from CONIN$.
117  */
118 INT __cdecl CRTDLL__cscanf( LPCSTR format, ... )
119 {
120     /* NOTE: If you extend this function, extend CRTDLL_fscanf in file.c too */
121     INT rd = 0;
122     int nch;
123     va_list ap;
124     if (!*format) return 0;
125     WARN("\"%s\": semi-stub\n", format);
126     nch = CRTDLL__getch();
127     va_start(ap, format);
128     while (*format) {
129         if (*format == ' ') {
130             /* skip whitespace */
131             while ((nch!=CRTDLL_EOF) && isspace(nch))
132                 nch = CRTDLL__getch();
133         }
134         else if (*format == '%') {
135             int st = 0;
136             format++;
137             switch(*format) {
138             case 'd': { /* read an integer */
139                     int*val = va_arg(ap, int*);
140                     int cur = 0;
141                     /* skip initial whitespace */
142                     while ((nch!=CRTDLL_EOF) && isspace(nch))
143                         nch = CRTDLL__getch();
144                     /* get sign and first digit */
145                     if (nch == '-') {
146                         nch = CRTDLL__getch();
147                         if (isdigit(nch))
148                             cur = -(nch - '0');
149                         else break;
150                     } else {
151                         if (isdigit(nch))
152                             cur = nch - '0';
153                         else break;
154                     }
155                     nch = CRTDLL__getch();
156                     /* read until no more digits */
157                     while ((nch!=CRTDLL_EOF) && isdigit(nch)) {
158                         cur = cur*10 + (nch - '0');
159                         nch = CRTDLL__getch();
160                     }
161                     st = 1;
162                     *val = cur;
163                 }
164                 break;
165             case 'f': { /* read a float */
166                     float*val = va_arg(ap, float*);
167                     float cur = 0;
168                     /* skip initial whitespace */
169                     while ((nch!=CRTDLL_EOF) && isspace(nch))
170                         nch = CRTDLL__getch();
171                     /* get sign and first digit */
172                     if (nch == '-') {
173                         nch = CRTDLL__getch();
174                         if (isdigit(nch))
175                             cur = -(nch - '0');
176                         else break;
177                     } else {
178                         if (isdigit(nch))
179                             cur = nch - '0';
180                         else break;
181                     }
182                     /* read until no more digits */
183                     while ((nch!=CRTDLL_EOF) && isdigit(nch)) {
184                         cur = cur*10 + (nch - '0');
185                         nch = CRTDLL__getch();
186                     }
187                     if (nch == '.') {
188                         /* handle decimals */
189                         float dec = 1;
190                         nch = CRTDLL__getch();
191                         while ((nch!=CRTDLL_EOF) && isdigit(nch)) {
192                             dec /= 10;
193                             cur += dec * (nch - '0');
194                             nch = CRTDLL__getch();
195                         }
196                     }
197                     st = 1;
198                     *val = cur;
199                 }
200                 break;
201             case 's': { /* read a word */
202                     char*str = va_arg(ap, char*);
203                     char*sptr = str;
204                     /* skip initial whitespace */
205                     while ((nch!=CRTDLL_EOF) && isspace(nch))
206                         nch = CRTDLL__getch();
207                     /* read until whitespace */
208                     while ((nch!=CRTDLL_EOF) && !isspace(nch)) {
209                         *sptr++ = nch; st++;
210                         nch = CRTDLL__getch();
211                     }
212                     /* terminate */
213                     *sptr = 0;
214                     TRACE("read word: %s\n", str);
215                 }
216                 break;
217             default: FIXME("unhandled: %%%c\n", *format);
218             }
219             if (st) rd++;
220             else break;
221         }
222         else {
223             /* check for character match */
224             if (nch == *format)
225                nch = CRTDLL__getch();
226             else break;
227         }
228         format++;
229     }
230     va_end(ap);
231     if (nch != CRTDLL_EOF)
232       CRTDLL__ungetch(nch);
233
234     TRACE("returning %d\n", rd);
235     return rd;
236 }
237
238
239 /*********************************************************************
240  *                  _getch      (CRTDLL.118)
241  *
242  * Get a character from CONIN$.
243  */
244 INT __cdecl CRTDLL__getch(VOID)
245 {
246   if (__CRTDLL_console_buffer != CRTDLL_EOF)
247   {
248     INT retVal = __CRTDLL_console_buffer;
249     __CRTDLL_console_buffer = CRTDLL_EOF;
250     return retVal;
251   }
252   else
253   {
254     INPUT_RECORD ir;
255     DWORD count;
256     DWORD mode = 0;
257
258     GetConsoleMode(__CRTDLL_console_in, &mode);
259     if(mode) SetConsoleMode(__CRTDLL_console_in, 0);
260
261     do {
262       if (ReadConsoleInputA(__CRTDLL_console_in, &ir, 1, &count))
263       {
264         /* Only interested in ASCII chars */
265         if (ir.EventType == KEY_EVENT &&
266             ir.Event.KeyEvent.bKeyDown &&
267             ir.Event.KeyEvent.uChar.AsciiChar)
268         {
269           if(mode) SetConsoleMode(__CRTDLL_console_in, mode);
270          return ir.Event.KeyEvent.uChar.AsciiChar;
271         }
272       }
273       else
274         break;
275     } while(1);
276     if (mode) SetConsoleMode(__CRTDLL_console_in, mode);
277   }
278   return CRTDLL_EOF;
279 }
280
281
282 /*********************************************************************
283  *                  _getche      (CRTDLL.119)
284  *
285  * Get a character from CONIN$ and echo it to CONOUT$.
286  */
287 INT __cdecl CRTDLL__getche(VOID)
288 {
289   INT res = CRTDLL__getch();
290   if (res != CRTDLL_EOF && CRTDLL__putch(res) != CRTDLL_EOF)
291     return res;
292   return CRTDLL_EOF;
293 }
294
295
296 /*********************************************************************
297  *                  _kbhit      (CRTDLL.169)
298  *
299  * Check if a character is waiting in CONIN$.
300  */
301 INT __cdecl CRTDLL__kbhit(VOID)
302 {
303   if (__CRTDLL_console_buffer != CRTDLL_EOF)
304     return 1;
305   else
306   {
307     /* FIXME: There has to be a faster way than this in Win32.. */
308     INPUT_RECORD *ir;
309     DWORD count = 0;
310     int retVal = 0, i;
311
312     GetNumberOfConsoleInputEvents(__CRTDLL_console_in, &count);
313     if (!count)
314       return 0;
315
316     if (!(ir = CRTDLL_malloc(count * sizeof(INPUT_RECORD))))
317       return 0;
318
319     if (!PeekConsoleInputA(__CRTDLL_console_in, ir, count, &count))
320       return 0;
321
322     for(i = 0; i < count - 1; i++)
323     {
324       if (ir[i].EventType == KEY_EVENT &&
325           ir[i].Event.KeyEvent.bKeyDown &&
326           ir[i].Event.KeyEvent.uChar.AsciiChar)
327       {
328         retVal = 1;
329         break;
330       }
331     }
332     CRTDLL_free(ir);
333     return retVal;
334   }
335 }
336
337
338 /*********************************************************************
339  *                  _putch      (CRTDLL.250)
340  *
341  * Write a character to CONOUT$.
342  */
343 INT __cdecl CRTDLL__putch(INT c)
344 {
345   DWORD count;
346   if (WriteConsoleA(__CRTDLL_console_out, &c, 1, &count, NULL) &&
347       count == 1)
348     return c;
349   return CRTDLL_EOF;
350 }
351
352
353 /*********************************************************************
354  *                  _ungetch      (CRTDLL.311)
355  *
356  * Un-get a character from CONIN$.
357  */
358 INT __cdecl CRTDLL__ungetch(INT c)
359 {
360   if (c == CRTDLL_EOF || __CRTDLL_console_buffer != CRTDLL_EOF)
361     return CRTDLL_EOF;
362
363   return __CRTDLL_console_buffer = c;
364 }
365