[CRT] Update file descriptor handling to match Wine (4/7). CORE-14504
[reactos.git] / sdk / lib / crt / stdio / file.c
1 /*
2 * PROJECT: ReactOS CRT library
3 * LICENSE: LGPL - See COPYING in the top level directory
4 * FILE: lib/sdk/crt/stdio/file.c
5 * PURPOSE: File CRT functions
6 * PROGRAMMERS: Wine team
7 * Ported to ReactOS by Aleksey Bragin (aleksey@reactos.org)
8 */
9
10 /*********************************************
11 * This file contains ReactOS changes!!
12 * Don't blindly sync it with Wine code!
13 *
14 * If you break Unicode output on the console again, please update this counter:
15 * int hours_wasted_on_this = 42;
16 *********************************************/
17
18 /*
19 * msvcrt.dll file functions
20 *
21 * Copyright 1996,1998 Marcus Meissner
22 * Copyright 1996 Jukka Iivonen
23 * Copyright 1997,2000 Uwe Bonnes
24 * Copyright 2000 Jon Griffiths
25 * Copyright 2004 Eric Pouech
26 * Copyright 2004 Juan Lang
27 *
28 * This library is free software; you can redistribute it and/or
29 * modify it under the terms of the GNU Lesser General Public
30 * License as published by the Free Software Foundation; either
31 * version 2.1 of the License, or (at your option) any later version.
32 *
33 * This library is distributed in the hope that it will be useful,
34 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
36 * Lesser General Public License for more details.
37 *
38 * You should have received a copy of the GNU Lesser General Public
39 * License along with this library; if not, write to the Free Software
40 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
41 *
42 * TODO
43 * Use the file flag hints O_SEQUENTIAL, O_RANDOM, O_SHORT_LIVED
44 */
45
46 #include <precomp.h>
47 #include "wine/unicode.h"
48 #include "internal/wine/msvcrt.h"
49
50 #include <sys/utime.h>
51 #include <direct.h>
52
53 int *__p__fmode(void);
54 int *__p___mb_cur_max(void);
55
56 extern int _commode;
57
58 #ifndef _IOCOMMIT
59 #define _IOCOMMIT 0x4000
60 #endif
61
62 #ifdef feof
63 #undef feof
64 #endif
65 #ifdef _fileno
66 #undef _fileno
67 #endif
68 #ifdef ferror
69 #undef ferror
70 #endif
71 #ifdef clearerr
72 #undef clearerr
73 #endif
74
75 #undef getc
76 #undef getwc
77 #undef getchar
78 #undef getwchar
79 #undef putc
80 #undef putwc
81 #undef putchar
82 #undef putwchar
83
84 #undef vprintf
85 #undef vwprintf
86
87 /* _access() bit flags FIXME: incomplete */
88 /* defined in crt/io.h */
89
90 /* values for wxflag in file descriptor */
91 #define WX_OPEN 0x01
92 #define WX_ATEOF 0x02
93 #define WX_READNL 0x04 /* read started with \n */
94 #define WX_READEOF 0x04 /* like ATEOF, but for underlying file rather than buffer */
95 #define WX_PIPE 0x08
96 #define WX_READCR 0x08 /* underlying file is at \r */
97 #define WX_DONTINHERIT 0x10
98 #define WX_APPEND 0x20
99 #define WX_TTY 0x40
100 #define WX_TEXT 0x80
101
102 /* values for exflag - it's used differently in msvcr90.dll*/
103 #define EF_UTF8 0x01
104 #define EF_UTF16 0x02
105 #define EF_CRIT_INIT 0x04
106 #define EF_UNK_UNICODE 0x08
107
108 static char utf8_bom[3] = { 0xef, 0xbb, 0xbf };
109 static char utf16_bom[2] = { 0xff, 0xfe };
110
111 /* FIXME: this should be allocated dynamically */
112 #define MSVCRT_MAX_FILES 2048
113 #define MSVCRT_FD_BLOCK_SIZE 32
114
115 #define MSVCRT_INTERNAL_BUFSIZ 4096
116
117 /*********************************************************************
118 * __pioinfo (MSVCRT.@)
119 * array of pointers to ioinfo arrays [32]
120 */
121 ioinfo * __pioinfo[MSVCRT_MAX_FILES/MSVCRT_FD_BLOCK_SIZE] = { 0 };
122
123 /*********************************************************************
124 * __badioinfo (MSVCRT.@)
125 */
126 ioinfo __badioinfo = { INVALID_HANDLE_VALUE, WX_TEXT };
127
128 static int fdstart = 3; /* first unallocated fd */
129 static int fdend = 3; /* highest allocated fd */
130
131 typedef struct {
132 FILE file;
133 CRITICAL_SECTION crit;
134 } file_crit;
135
136 FILE _iob[_IOB_ENTRIES] = { { 0 } };
137 static file_crit* MSVCRT_fstream[MSVCRT_MAX_FILES/MSVCRT_FD_BLOCK_SIZE] = { NULL };
138 static int MSVCRT_max_streams = 512, MSVCRT_stream_idx;
139
140 /* INTERNAL: process umask */
141 static int MSVCRT_umask = 0;
142
143 /* INTERNAL: static data for tmpnam and _wtmpname functions */
144 static int tmpnam_unique;
145
146 /* This critical section protects the tables __pioinfo and fstreams,
147 * and their related indexes, fdstart, fdend,
148 * and MSVCRT_stream_idx, from race conditions.
149 * It doesn't protect against race conditions manipulating the underlying files
150 * or flags; doing so would probably be better accomplished with per-file
151 * protection, rather than locking the whole table for every change.
152 */
153 static CRITICAL_SECTION MSVCRT_file_cs;
154 static CRITICAL_SECTION_DEBUG MSVCRT_file_cs_debug =
155 {
156 0, 0, &MSVCRT_file_cs,
157 { &MSVCRT_file_cs_debug.ProcessLocksList, &MSVCRT_file_cs_debug.ProcessLocksList },
158 0, 0, { (DWORD_PTR)(__FILE__ ": MSVCRT_file_cs") }
159 };
160 static CRITICAL_SECTION MSVCRT_file_cs = { &MSVCRT_file_cs_debug, -1, 0, 0, 0, 0 };
161 #define LOCK_FILES() do { EnterCriticalSection(&MSVCRT_file_cs); } while (0)
162 #define UNLOCK_FILES() do { LeaveCriticalSection(&MSVCRT_file_cs); } while (0)
163
164 static inline ioinfo* get_ioinfo_nolock(int fd)
165 {
166 ioinfo *ret = NULL;
167 if(fd>=0 && fd<MSVCRT_MAX_FILES)
168 ret = __pioinfo[fd/MSVCRT_FD_BLOCK_SIZE];
169 if(!ret)
170 return &__badioinfo;
171
172 return ret + (fd%MSVCRT_FD_BLOCK_SIZE);
173 }
174
175 /*static*/ inline ioinfo* get_ioinfo(int fd)
176 {
177 ioinfo *ret = get_ioinfo_nolock(fd);
178 if(ret == &__badioinfo)
179 return ret;
180 if(!(ret->exflag & EF_CRIT_INIT)) {
181 LOCK_FILES();
182 if(!(ret->exflag & EF_CRIT_INIT)) {
183 InitializeCriticalSection(&ret->crit);
184 ret->exflag |= EF_CRIT_INIT;
185 }
186 UNLOCK_FILES();
187 }
188 EnterCriticalSection(&ret->crit);
189 return ret;
190 }
191
192 /*static*/ inline void release_ioinfo(ioinfo *info)
193 {
194 if(info!=&__badioinfo && info->exflag & EF_CRIT_INIT)
195 LeaveCriticalSection(&info->crit);
196 }
197
198 static inline FILE* msvcrt_get_file(int i)
199 {
200 file_crit *ret;
201
202 if(i >= MSVCRT_max_streams)
203 return NULL;
204
205 if(i < _IOB_ENTRIES)
206 return &_iob[i];
207
208 ret = MSVCRT_fstream[i/MSVCRT_FD_BLOCK_SIZE];
209 if(!ret) {
210 MSVCRT_fstream[i/MSVCRT_FD_BLOCK_SIZE] = calloc(MSVCRT_FD_BLOCK_SIZE, sizeof(file_crit));
211 if(!MSVCRT_fstream[i/MSVCRT_FD_BLOCK_SIZE]) {
212 ERR("out of memory\n");
213 *_errno() = ENOMEM;
214 return NULL;
215 }
216
217 ret = MSVCRT_fstream[i/MSVCRT_FD_BLOCK_SIZE] + (i%MSVCRT_FD_BLOCK_SIZE);
218 } else
219 ret += i%MSVCRT_FD_BLOCK_SIZE;
220
221 return &ret->file;
222 }
223
224 static inline BOOL is_valid_fd(int fd)
225 {
226 return fd >= 0 && fd < fdend && (get_ioinfo_nolock(fd)->wxflag & WX_OPEN);
227 }
228
229 /* INTERNAL: Get the HANDLE for a fd
230 * This doesn't lock the table, because a failure will result in
231 * INVALID_HANDLE_VALUE being returned, which should be handled correctly. If
232 * it returns a valid handle which is about to be closed, a subsequent call
233 * will fail, most likely in a sane way.
234 */
235 /*static*/ HANDLE fdtoh(int fd)
236 {
237 if (!is_valid_fd(fd))
238 {
239 WARN(":fd (%d) - no handle!\n",fd);
240 *__doserrno() = 0;
241 *_errno() = EBADF;
242 return INVALID_HANDLE_VALUE;
243 }
244 //if (get_ioinfo_nolock(fd)->handle == INVALID_HANDLE_VALUE)
245 //FIXME("returning INVALID_HANDLE_VALUE for %d\n", fd);
246 return get_ioinfo_nolock(fd)->handle;
247 }
248
249 /* INTERNAL: free a file entry fd */
250 static void msvcrt_free_fd(int fd)
251 {
252 ioinfo *fdinfo = get_ioinfo(fd);
253
254 if(fdinfo != &__badioinfo)
255 {
256 fdinfo->handle = INVALID_HANDLE_VALUE;
257 fdinfo->wxflag = 0;
258 }
259 TRACE(":fd (%d) freed\n",fd);
260
261 if (fd < 3)
262 {
263 switch (fd)
264 {
265 case 0:
266 SetStdHandle(STD_INPUT_HANDLE, 0);
267 break;
268 case 1:
269 SetStdHandle(STD_OUTPUT_HANDLE, 0);
270 break;
271 case 2:
272 SetStdHandle(STD_ERROR_HANDLE, 0);
273 break;
274 }
275 }
276 release_ioinfo(fdinfo);
277
278 LOCK_FILES();
279 if (fd == fdend - 1)
280 fdend--;
281 if (fd < fdstart)
282 fdstart = fd;
283 UNLOCK_FILES();
284 }
285
286 /* INTERNAL: Allocate an fd slot from a Win32 HANDLE, starting from fd */
287 /* caller must hold the files lock */
288 static int msvcrt_set_fd(HANDLE hand, int flag, int fd)
289 {
290 ioinfo *fdinfo;
291
292 if (fd >= MSVCRT_MAX_FILES)
293 {
294 WARN(":files exhausted!\n");
295 *_errno() = ENFILE;
296 return -1;
297 }
298
299 fdinfo = get_ioinfo_nolock(fd);
300 if(fdinfo == &__badioinfo) {
301 int i;
302
303 __pioinfo[fd/MSVCRT_FD_BLOCK_SIZE] = calloc(MSVCRT_FD_BLOCK_SIZE, sizeof(ioinfo));
304 if(!__pioinfo[fd/MSVCRT_FD_BLOCK_SIZE]) {
305 WARN(":out of memory!\n");
306 *_errno() = ENOMEM;
307 return -1;
308 }
309
310 for(i=0; i<MSVCRT_FD_BLOCK_SIZE; i++)
311 __pioinfo[fd/MSVCRT_FD_BLOCK_SIZE][i].handle = INVALID_HANDLE_VALUE;
312
313 fdinfo = get_ioinfo_nolock(fd);
314 }
315
316 fdinfo->handle = hand;
317 fdinfo->wxflag = WX_OPEN | (flag & (WX_DONTINHERIT | WX_APPEND | WX_TEXT | WX_PIPE | WX_TTY));
318 fdinfo->lookahead[0] = '\n';
319 fdinfo->lookahead[1] = '\n';
320 fdinfo->lookahead[2] = '\n';
321 if(!(fdinfo->exflag & EF_CRIT_INIT))
322 InitializeCriticalSection(&fdinfo->crit);
323 fdinfo->exflag = EF_CRIT_INIT;
324
325 /* locate next free slot */
326 if (fd == fdstart && fd == fdend)
327 fdstart = fdend + 1;
328 else
329 while (fdstart < fdend &&
330 get_ioinfo_nolock(fdstart)->handle != INVALID_HANDLE_VALUE)
331 fdstart++;
332 /* update last fd in use */
333 if (fd >= fdend)
334 fdend = fd + 1;
335 TRACE("fdstart is %d, fdend is %d\n", fdstart, fdend);
336
337 switch (fd)
338 {
339 case 0: SetStdHandle(STD_INPUT_HANDLE, hand); break;
340 case 1: SetStdHandle(STD_OUTPUT_HANDLE, hand); break;
341 case 2: SetStdHandle(STD_ERROR_HANDLE, hand); break;
342 }
343
344 return fd;
345 }
346
347 /* INTERNAL: Allocate an fd slot from a Win32 HANDLE */
348 /*static*/ int msvcrt_alloc_fd(HANDLE hand, int flag)
349 {
350 int ret;
351
352 LOCK_FILES();
353 TRACE(":handle (%p) allocating fd (%d)\n",hand,fdstart);
354 ret = msvcrt_set_fd(hand, flag, fdstart);
355 UNLOCK_FILES();
356 return ret;
357 }
358
359 /* INTERNAL: Allocate a FILE* for an fd slot */
360 /* caller must hold the files lock */
361 static FILE* msvcrt_alloc_fp(void)
362 {
363 int i;
364 FILE *file;
365
366 for (i = 3; i < MSVCRT_max_streams; i++)
367 {
368 file = msvcrt_get_file(i);
369 if (!file)
370 return NULL;
371
372 if (file->_flag == 0)
373 {
374 if (i == MSVCRT_stream_idx) MSVCRT_stream_idx++;
375 return file;
376 }
377 }
378
379 return NULL;
380 }
381
382 /* INTERNAL: initialize a FILE* from an open fd */
383 static int msvcrt_init_fp(FILE* file, int fd, unsigned stream_flags)
384 {
385 TRACE(":fd (%d) allocating FILE*\n",fd);
386 if (!is_valid_fd(fd))
387 {
388 WARN(":invalid fd %d\n",fd);
389 *__doserrno() = 0;
390 *_errno() = EBADF;
391 return -1;
392 }
393 file->_ptr = file->_base = NULL;
394 file->_cnt = 0;
395 file->_file = fd;
396 file->_flag = stream_flags;
397 file->_tmpfname = NULL;
398
399 if(file<_iob || file>=_iob+_IOB_ENTRIES)
400 InitializeCriticalSection(&((file_crit*)file)->crit);
401
402 TRACE(":got FILE* (%p)\n",file);
403 return 0;
404 }
405
406 /* INTERNAL: Create an inheritance data block (for spawned process)
407 * The inheritance block is made of:
408 * 00 int nb of file descriptor (NBFD)
409 * 04 char file flags (wxflag): repeated for each fd
410 * 4+NBFD HANDLE file handle: repeated for each fd
411 */
412 unsigned create_io_inherit_block(WORD *size, BYTE **block)
413 {
414 int fd;
415 char* wxflag_ptr;
416 HANDLE* handle_ptr;
417 ioinfo* fdinfo;
418
419 *size = sizeof(unsigned) + (sizeof(char) + sizeof(HANDLE)) * fdend;
420 *block = calloc(1, *size);
421 if (!*block)
422 {
423 *size = 0;
424 return FALSE;
425 }
426 wxflag_ptr = (char*)*block + sizeof(unsigned);
427 handle_ptr = (HANDLE*)(wxflag_ptr + fdend * sizeof(char));
428
429 *(unsigned*)*block = fdend;
430 for (fd = 0; fd < fdend; fd++)
431 {
432 /* to be inherited, we need it to be open, and that DONTINHERIT isn't set */
433 fdinfo = get_ioinfo(fd);
434 if ((fdinfo->wxflag & (WX_OPEN | WX_DONTINHERIT)) == WX_OPEN)
435 {
436 *wxflag_ptr = fdinfo->wxflag;
437 *handle_ptr = fdinfo->handle;
438 }
439 else
440 {
441 *wxflag_ptr = 0;
442 *handle_ptr = INVALID_HANDLE_VALUE;
443 }
444 release_ioinfo(fdinfo);
445 wxflag_ptr++; handle_ptr++;
446 }
447 return TRUE;
448 }
449
450 /* INTERNAL: Set up all file descriptors,
451 * as well as default streams (stdin, stderr and stdout)
452 */
453 void msvcrt_init_io(void)
454 {
455 STARTUPINFOA si;
456 unsigned int i;
457 ioinfo *fdinfo;
458
459 GetStartupInfoA(&si);
460 if (si.cbReserved2 >= sizeof(unsigned int) && si.lpReserved2 != NULL)
461 {
462 BYTE* wxflag_ptr;
463 HANDLE* handle_ptr;
464 unsigned int count;
465
466 count = *(unsigned*)si.lpReserved2;
467 wxflag_ptr = si.lpReserved2 + sizeof(unsigned);
468 handle_ptr = (HANDLE*)(wxflag_ptr + count);
469
470 count = min(count, (si.cbReserved2 - sizeof(unsigned)) / (sizeof(HANDLE) + 1));
471 count = min(count, MSVCRT_MAX_FILES);
472 for (i = 0; i < count; i++)
473 {
474 if ((*wxflag_ptr & WX_OPEN) && *handle_ptr != INVALID_HANDLE_VALUE)
475 msvcrt_set_fd(*handle_ptr, *wxflag_ptr, i);
476
477 wxflag_ptr++; handle_ptr++;
478 }
479 fdend = max( 3, count );
480 for (fdstart = 3; fdstart < fdend; fdstart++)
481 if (get_ioinfo_nolock(fdstart)->handle == INVALID_HANDLE_VALUE) break;
482 }
483
484 fdinfo = get_ioinfo_nolock(STDIN_FILENO);
485 if (!(fdinfo->wxflag & WX_OPEN) || fdinfo->handle == INVALID_HANDLE_VALUE) {
486 HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
487 DWORD type = GetFileType(h);
488
489 msvcrt_set_fd(h, WX_OPEN|WX_TEXT|((type&0xf)==FILE_TYPE_CHAR ? WX_TTY : 0)
490 |((type&0xf)==FILE_TYPE_PIPE ? WX_PIPE : 0), STDIN_FILENO);
491 }
492
493 fdinfo = get_ioinfo_nolock(STDOUT_FILENO);
494 if (!(fdinfo->wxflag & WX_OPEN) || fdinfo->handle == INVALID_HANDLE_VALUE) {
495 HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
496 DWORD type = GetFileType(h);
497
498 msvcrt_set_fd(h, WX_OPEN|WX_TEXT|((type&0xf)==FILE_TYPE_CHAR ? WX_TTY : 0)
499 |((type&0xf)==FILE_TYPE_PIPE ? WX_PIPE : 0), STDOUT_FILENO);
500 }
501
502 fdinfo = get_ioinfo_nolock(STDERR_FILENO);
503 if (!(fdinfo->wxflag & WX_OPEN) || fdinfo->handle == INVALID_HANDLE_VALUE) {
504 HANDLE h = GetStdHandle(STD_ERROR_HANDLE);
505 DWORD type = GetFileType(h);
506
507 msvcrt_set_fd(h, WX_OPEN|WX_TEXT|((type&0xf)==FILE_TYPE_CHAR ? WX_TTY : 0)
508 |((type&0xf)==FILE_TYPE_PIPE ? WX_PIPE : 0), STDERR_FILENO);
509 }
510
511 TRACE(":handles (%p)(%p)(%p)\n", get_ioinfo_nolock(STDIN_FILENO)->handle,
512 get_ioinfo_nolock(STDOUT_FILENO)->handle,
513 get_ioinfo_nolock(STDERR_FILENO)->handle);
514
515 memset(_iob,0,3*sizeof(FILE));
516 for (i = 0; i < 3; i++)
517 {
518 /* FILE structs for stdin/out/err are static and never deleted */
519 _iob[i]._file = i;
520 _iob[i]._tmpfname = NULL;
521 _iob[i]._flag = (i == 0) ? _IOREAD : _IOWRT;
522 }
523 MSVCRT_stream_idx = 3;
524 }
525
526 /* INTERNAL: Flush stdio file buffer */
527 static int msvcrt_flush_buffer(FILE* file)
528 {
529 if((file->_flag & (_IOREAD|_IOWRT)) == _IOWRT &&
530 file->_flag & (_IOMYBUF|_USERBUF)) {
531 int cnt=file->_ptr-file->_base;
532 if(cnt>0 && _write(file->_file, file->_base, cnt) != cnt) {
533 file->_flag |= _IOERR;
534 return EOF;
535 }
536
537 if(file->_flag & _IORW)
538 file->_flag &= ~_IOWRT;
539
540 #ifdef __REACTOS__ /* CORE-11949 */
541 file->_ptr=file->_base;
542 file->_cnt=0;
543 #endif
544 }
545
546 #ifndef __REACTOS__ /* CORE-11949 */
547 file->_ptr=file->_base;
548 file->_cnt=0;
549 #endif
550 return 0;
551 }
552
553 /*********************************************************************
554 * _isatty (MSVCRT.@)
555 */
556 int CDECL _isatty(int fd)
557 {
558 TRACE(":fd (%d)\n",fd);
559
560 return get_ioinfo_nolock(fd)->wxflag & WX_TTY;
561 }
562
563 /* INTERNAL: Allocate stdio file buffer */
564 /*static*/ BOOL msvcrt_alloc_buffer(FILE* file)
565 {
566 if((file->_file==STDOUT_FILENO || file->_file==STDERR_FILENO)
567 && _isatty(file->_file))
568 return FALSE;
569
570 file->_base = calloc(1, MSVCRT_INTERNAL_BUFSIZ);
571 if(file->_base) {
572 file->_bufsiz = MSVCRT_INTERNAL_BUFSIZ;
573 file->_flag |= _IOMYBUF;
574 } else {
575 file->_base = (char*)(&file->_charbuf);
576 file->_bufsiz = 2;
577 file->_flag |= _IONBF;
578 }
579 file->_ptr = file->_base;
580 file->_cnt = 0;
581 return TRUE;
582 }
583
584 /* INTERNAL: Allocate temporary buffer for stdout and stderr */
585 static BOOL add_std_buffer(FILE *file)
586 {
587 static char buffers[2][BUFSIZ];
588
589 if((file->_file!=STDOUT_FILENO && file->_file!=STDERR_FILENO)
590 || (file->_flag & (_IONBF | _IOMYBUF | _USERBUF))
591 || !_isatty(file->_file))
592 return FALSE;
593
594 file->_ptr = file->_base = buffers[file->_file == STDOUT_FILENO ? 0 : 1];
595 file->_bufsiz = file->_cnt = BUFSIZ;
596 file->_flag |= _USERBUF;
597 return TRUE;
598 }
599
600 /* INTERNAL: Removes temporary buffer from stdout or stderr */
601 /* Only call this function when add_std_buffer returned TRUE */
602 static void remove_std_buffer(FILE *file)
603 {
604 msvcrt_flush_buffer(file);
605 file->_ptr = file->_base = NULL;
606 file->_bufsiz = file->_cnt = 0;
607 file->_flag &= ~_USERBUF;
608 }
609
610 /* INTERNAL: Convert integer to base32 string (0-9a-v), 0 becomes "" */
611 static int msvcrt_int_to_base32(int num, char *str)
612 {
613 char *p;
614 int n = num;
615 int digits = 0;
616
617 while (n != 0)
618 {
619 n >>= 5;
620 digits++;
621 }
622 p = str + digits;
623 *p = 0;
624 while (--p >= str)
625 {
626 *p = (num & 31) + '0';
627 if (*p > '9')
628 *p += ('a' - '0' - 10);
629 num >>= 5;
630 }
631
632 return digits;
633 }
634
635 /* INTERNAL: wide character version of msvcrt_int_to_base32 */
636 static int msvcrt_int_to_base32_w(int num, wchar_t *str)
637 {
638 wchar_t *p;
639 int n = num;
640 int digits = 0;
641
642 while (n != 0)
643 {
644 n >>= 5;
645 digits++;
646 }
647 p = str + digits;
648 *p = 0;
649 while (--p >= str)
650 {
651 *p = (num & 31) + '0';
652 if (*p > '9')
653 *p += ('a' - '0' - 10);
654 num >>= 5;
655 }
656
657 return digits;
658 }
659
660 /* INTERNAL: Create a wide string from an ascii string */
661 wchar_t *msvcrt_wstrdupa(const char *str)
662 {
663 const unsigned int len = strlen(str) + 1 ;
664 wchar_t *wstr = malloc(len* sizeof (wchar_t));
665 if (!wstr)
666 return NULL;
667 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,str,len,wstr,len);
668 return wstr;
669 }
670
671 /*********************************************************************
672 * __iob_func(MSVCRT.@)
673 */
674 FILE * CDECL __iob_func(void)
675 {
676 return &_iob[0];
677 }
678
679 /*********************************************************************
680 * _access (MSVCRT.@)
681 */
682 int CDECL _access(const char *filename, int mode)
683 {
684 DWORD attr = GetFileAttributesA(filename);
685
686 TRACE("(%s,%d) %d\n",filename,mode,attr);
687
688 if (!filename || attr == INVALID_FILE_ATTRIBUTES)
689 {
690 _dosmaperr(GetLastError());
691 return -1;
692 }
693 if ((attr & FILE_ATTRIBUTE_READONLY) && (mode & W_OK))
694 {
695 _set_errno(ERROR_ACCESS_DENIED);
696 return -1;
697 }
698 return 0;
699 }
700
701 /*********************************************************************
702 * _access_s (MSVCRT.@)
703 */
704 int CDECL _access_s(const char *filename, int mode)
705 {
706 if (!MSVCRT_CHECK_PMT(filename != NULL) ||
707 !MSVCRT_CHECK_PMT((mode & ~(R_OK | W_OK)) == 0))
708 {
709 _set_errno(EINVAL);
710 return -1;
711 }
712
713 return _access(filename, mode);
714 }
715
716 /*********************************************************************
717 * _waccess (MSVCRT.@)
718 */
719 int CDECL _waccess(const wchar_t *filename, int mode)
720 {
721 DWORD attr = GetFileAttributesW(filename);
722
723 TRACE("(%s,%d) %d\n",debugstr_w(filename),mode,attr);
724
725 if (!filename || attr == INVALID_FILE_ATTRIBUTES)
726 {
727 _dosmaperr(GetLastError());
728 return -1;
729 }
730 if ((attr & FILE_ATTRIBUTE_READONLY) && (mode & W_OK))
731 {
732 _set_errno(ERROR_ACCESS_DENIED);
733 return -1;
734 }
735 return 0;
736 }
737
738 /*********************************************************************
739 * _waccess_s (MSVCRT.@)
740 */
741 int CDECL _waccess_s(const wchar_t *filename, int mode)
742 {
743 if (!MSVCRT_CHECK_PMT(filename != NULL) ||
744 !MSVCRT_CHECK_PMT((mode & ~(R_OK | W_OK)) == 0))
745 {
746 *_errno() = EINVAL;
747 return -1;
748 }
749
750 return _waccess(filename, mode);
751 }
752
753 /*********************************************************************
754 * _chmod (MSVCRT.@)
755 */
756 int CDECL _chmod(const char *path, int flags)
757 {
758 DWORD oldFlags = GetFileAttributesA(path);
759
760 if (oldFlags != INVALID_FILE_ATTRIBUTES)
761 {
762 DWORD newFlags = (flags & _S_IWRITE)? oldFlags & ~FILE_ATTRIBUTE_READONLY:
763 oldFlags | FILE_ATTRIBUTE_READONLY;
764
765 if (newFlags == oldFlags || SetFileAttributesA(path, newFlags))
766 return 0;
767 }
768 _dosmaperr(GetLastError());
769 return -1;
770 }
771
772 /*********************************************************************
773 * _wchmod (MSVCRT.@)
774 */
775 int CDECL _wchmod(const wchar_t *path, int flags)
776 {
777 DWORD oldFlags = GetFileAttributesW(path);
778
779 if (oldFlags != INVALID_FILE_ATTRIBUTES)
780 {
781 DWORD newFlags = (flags & _S_IWRITE)? oldFlags & ~FILE_ATTRIBUTE_READONLY:
782 oldFlags | FILE_ATTRIBUTE_READONLY;
783
784 if (newFlags == oldFlags || SetFileAttributesW(path, newFlags))
785 return 0;
786 }
787 _dosmaperr(GetLastError());
788 return -1;
789 }
790
791 /*********************************************************************
792 * _unlink (MSVCRT.@)
793 */
794 int CDECL _unlink(const char *path)
795 {
796 TRACE("%s\n",debugstr_a(path));
797 if(DeleteFileA(path))
798 return 0;
799 TRACE("failed (%d)\n",GetLastError());
800 _dosmaperr(GetLastError());
801 return -1;
802 }
803
804 /*********************************************************************
805 * _wunlink (MSVCRT.@)
806 */
807 int CDECL _wunlink(const wchar_t *path)
808 {
809 TRACE("(%s)\n",debugstr_w(path));
810 if(DeleteFileW(path))
811 return 0;
812 TRACE("failed (%d)\n",GetLastError());
813 _dosmaperr(GetLastError());
814 return -1;
815 }
816
817 /*********************************************************************
818 * _commit (MSVCRT.@)
819 */
820 int CDECL _commit(int fd)
821 {
822 ioinfo *info = get_ioinfo(fd);
823 int ret;
824
825 TRACE(":fd (%d) handle (%p)\n", fd, info->handle);
826 if (info->handle == INVALID_HANDLE_VALUE)
827 ret = -1;
828 else if (!FlushFileBuffers(info->handle))
829 {
830 if (GetLastError() == ERROR_INVALID_HANDLE)
831 {
832 /* FlushFileBuffers fails for console handles
833 * so we ignore this error.
834 */
835 ret = 0;
836 }
837 else
838 {
839 TRACE(":failed-last error (%d)\n",GetLastError());
840 _dosmaperr(GetLastError());
841 ret = -1;
842 }
843 }
844 else
845 {
846 TRACE(":ok\n");
847 ret = 0;
848 }
849
850 release_ioinfo(info);
851 return ret;
852 }
853
854 /* _flushall calls fflush which calls _flushall */
855 int CDECL fflush(FILE* file);
856
857 /* INTERNAL: Flush all stream buffer */
858 static int msvcrt_flush_all_buffers(int mask)
859 {
860 int i, num_flushed = 0;
861 FILE *file;
862
863 LOCK_FILES();
864 for (i = 0; i < MSVCRT_stream_idx; i++) {
865 file = msvcrt_get_file(i);
866
867 if (file->_flag)
868 {
869 if(file->_flag & mask) {
870 fflush(file);
871 num_flushed++;
872 }
873 }
874 }
875 UNLOCK_FILES();
876
877 TRACE(":flushed (%d) handles\n",num_flushed);
878 return num_flushed;
879 }
880
881 /*********************************************************************
882 * _flushall (MSVCRT.@)
883 */
884 int CDECL _flushall(void)
885 {
886 return msvcrt_flush_all_buffers(_IOWRT | _IOREAD);
887 }
888
889 /*********************************************************************
890 * fflush (MSVCRT.@)
891 */
892 int CDECL fflush(FILE* file)
893 {
894 if(!file) {
895 msvcrt_flush_all_buffers(_IOWRT);
896 } else if(file->_flag & _IOWRT) {
897 int res;
898
899 _lock_file(file);
900 res = msvcrt_flush_buffer(file);
901 /* FIXME
902 if(!res && (file->_flag & _IOCOMMIT))
903 res = _commit(file->_file) ? EOF : 0;
904 */
905 _unlock_file(file);
906
907 return res;
908 } else if(file->_flag & _IOREAD) {
909 _lock_file(file);
910 file->_cnt = 0;
911 file->_ptr = file->_base;
912 _unlock_file(file);
913
914 return 0;
915 }
916 return 0;
917 }
918
919 /*********************************************************************
920 * _close (MSVCRT.@)
921 */
922 int CDECL _close(int fd)
923 {
924 ioinfo *info = get_ioinfo(fd);
925 int ret;
926
927 LOCK_FILES();
928 TRACE(":fd (%d) handle (%p)\n", fd, info->handle);
929 if (!(info->wxflag & WX_OPEN)) {
930 ret = -1;
931 } else {
932 ret = CloseHandle(info->handle) ? 0 : -1;
933 msvcrt_free_fd(fd);
934 if (ret) {
935 WARN(":failed-last error (%d)\n",GetLastError());
936 _dosmaperr(GetLastError());
937 ret = -1;
938 }
939 }
940 UNLOCK_FILES();
941 release_ioinfo(info);
942 return ret;
943 }
944
945 /*********************************************************************
946 * _dup2 (MSVCRT.@)
947 * NOTES
948 * MSDN isn't clear on this point, but the remarks for _pipe
949 * indicate file descriptors duplicated with _dup and _dup2 are always
950 * inheritable.
951 */
952 int CDECL _dup2(int od, int nd)
953 {
954 ioinfo *info_od, *info_nd;
955 int ret;
956
957 TRACE("(od=%d, nd=%d)\n", od, nd);
958 LOCK_FILES();
959
960 if (od < nd)
961 {
962 info_od = get_ioinfo(od);
963 info_nd = get_ioinfo(nd);
964 }
965 else
966 {
967 info_nd = get_ioinfo(nd);
968 info_od = get_ioinfo(od);
969 }
970
971 if (nd < MSVCRT_MAX_FILES && nd >= 0 && (info_od->wxflag & WX_OPEN))
972 {
973 HANDLE handle;
974
975 if (DuplicateHandle(GetCurrentProcess(), info_od->handle,
976 GetCurrentProcess(), &handle, 0, TRUE, DUPLICATE_SAME_ACCESS))
977 {
978 int wxflag = info_od->wxflag & ~_O_NOINHERIT;
979
980 if (info_nd->wxflag & WX_OPEN)
981 _close(nd);
982 ret = msvcrt_set_fd(handle, wxflag, nd);
983 if (ret == -1)
984 {
985 CloseHandle(handle);
986 *_errno() = EMFILE;
987 }
988 else
989 {
990 /* _dup2 returns 0, not nd, on success */
991 ret = 0;
992 }
993 }
994 else
995 {
996 ret = -1;
997 _dosmaperr(GetLastError());
998 }
999 }
1000 else
1001 {
1002 *_errno() = EBADF;
1003 ret = -1;
1004 }
1005
1006 release_ioinfo(info_od);
1007 release_ioinfo(info_nd);
1008 UNLOCK_FILES();
1009 return ret;
1010 }
1011
1012 /*********************************************************************
1013 * _dup (MSVCRT.@)
1014 */
1015 int CDECL _dup(int od)
1016 {
1017 int fd, ret;
1018
1019 LOCK_FILES();
1020 fd = fdstart;
1021 if (_dup2(od, fd) == 0)
1022 ret = fd;
1023 else
1024 ret = -1;
1025 UNLOCK_FILES();
1026 return ret;
1027 }
1028
1029 /*********************************************************************
1030 * _eof (MSVCRT.@)
1031 */
1032 int CDECL _eof(int fd)
1033 {
1034 ioinfo *info = get_ioinfo(fd);
1035 DWORD curpos,endpos;
1036 LONG hcurpos,hendpos;
1037
1038 TRACE(":fd (%d) handle (%p)\n", fd, info->handle);
1039
1040 if (info->handle == INVALID_HANDLE_VALUE)
1041 {
1042 release_ioinfo(info);
1043 return -1;
1044 }
1045
1046 if (info->wxflag & WX_ATEOF)
1047 {
1048 release_ioinfo(info);
1049 return TRUE;
1050 }
1051
1052 /* Otherwise we do it the hard way */
1053 hcurpos = hendpos = 0;
1054 curpos = SetFilePointer(info->handle, 0, &hcurpos, FILE_CURRENT);
1055 endpos = SetFilePointer(info->handle, 0, &hendpos, FILE_END);
1056
1057 if (curpos == endpos && hcurpos == hendpos)
1058 {
1059 /* FIXME: shouldn't WX_ATEOF be set here? */
1060 release_ioinfo(info);
1061 return TRUE;
1062 }
1063
1064 SetFilePointer(info->handle, curpos, &hcurpos, FILE_BEGIN);
1065 release_ioinfo(info);
1066 return FALSE;
1067 }
1068
1069 /*********************************************************************
1070 * _fcloseall (MSVCRT.@)
1071 */
1072 int CDECL _fcloseall(void)
1073 {
1074 int num_closed = 0, i;
1075 FILE *file;
1076
1077 LOCK_FILES();
1078 for (i = 3; i < MSVCRT_stream_idx; i++) {
1079 file = msvcrt_get_file(i);
1080
1081 if (file->_flag && !fclose(file))
1082 num_closed++;
1083 }
1084 UNLOCK_FILES();
1085
1086 TRACE(":closed (%d) handles\n",num_closed);
1087 return num_closed;
1088 }
1089
1090 /* free everything on process exit */
1091 void msvcrt_free_io(void)
1092 {
1093 unsigned int i;
1094 int j;
1095
1096 _flushall();
1097 _fcloseall();
1098
1099 for(i=0; i<sizeof(__pioinfo)/sizeof(__pioinfo[0]); i++)
1100 {
1101 if(!__pioinfo[i])
1102 continue;
1103
1104 for(j=0; j<MSVCRT_FD_BLOCK_SIZE; j++)
1105 {
1106 if(__pioinfo[i][j].exflag & EF_CRIT_INIT)
1107 DeleteCriticalSection(&__pioinfo[i][j].crit);
1108 }
1109 free(__pioinfo[i]);
1110 }
1111
1112 for(j=0; j<MSVCRT_stream_idx; j++)
1113 {
1114 FILE *file = msvcrt_get_file(j);
1115 if(file<_iob || file>=_iob+_IOB_ENTRIES)
1116 {
1117 ((file_crit*)file)->crit.DebugInfo->Spare[0] = 0;
1118 DeleteCriticalSection(&((file_crit*)file)->crit);
1119 }
1120 }
1121
1122 for(i=0; i<sizeof(MSVCRT_fstream)/sizeof(MSVCRT_fstream[0]); i++)
1123 free(MSVCRT_fstream[i]);
1124 }
1125
1126 /*********************************************************************
1127 * _lseeki64 (MSVCRT.@)
1128 */
1129 __int64 CDECL _lseeki64(int fd, __int64 offset, int whence)
1130 {
1131 ioinfo *info = get_ioinfo(fd);
1132 LARGE_INTEGER ofs;
1133
1134 TRACE(":fd (%d) handle (%p)\n", fd, info->handle);
1135
1136 if (info->handle == INVALID_HANDLE_VALUE)
1137 {
1138 release_ioinfo(info);
1139 return -1;
1140 }
1141
1142 if (whence < 0 || whence > 2)
1143 {
1144 release_ioinfo(info);
1145 *_errno() = EINVAL;
1146 return -1;
1147 }
1148
1149 TRACE(":fd (%d) to %s pos %s\n",
1150 fd,wine_dbgstr_longlong(offset),
1151 (whence==SEEK_SET)?"SEEK_SET":
1152 (whence==SEEK_CUR)?"SEEK_CUR":
1153 (whence==SEEK_END)?"SEEK_END":"UNKNOWN");
1154
1155 /* The MoleBox protection scheme expects msvcrt to use SetFilePointer only,
1156 * so a LARGE_INTEGER offset cannot be passed directly via SetFilePointerEx. */
1157 ofs.QuadPart = offset;
1158 if ((ofs.u.LowPart = SetFilePointer(info->handle, ofs.u.LowPart, &ofs.u.HighPart, whence)) != INVALID_SET_FILE_POINTER ||
1159 GetLastError() == ERROR_SUCCESS)
1160 {
1161 info->wxflag &= ~(WX_ATEOF|WX_READEOF);
1162 /* FIXME: What if we seek _to_ EOF - is EOF set? */
1163
1164 release_ioinfo(info);
1165 return ofs.QuadPart;
1166 }
1167 release_ioinfo(info);
1168 TRACE(":error-last error (%d)\n",GetLastError());
1169 _dosmaperr(GetLastError());
1170 return -1;
1171 }
1172
1173 /*********************************************************************
1174 * _lseek (MSVCRT.@)
1175 */
1176 LONG CDECL _lseek(int fd, LONG offset, int whence)
1177 {
1178 return (LONG)_lseeki64(fd, offset, whence);
1179 }
1180
1181 /*********************************************************************
1182 * _lock_file (MSVCRT.@)
1183 */
1184 void CDECL _lock_file(FILE *file)
1185 {
1186 if(file>=_iob && file<_iob+_IOB_ENTRIES)
1187 _lock(_STREAM_LOCKS+(file-_iob));
1188 /* ReactOS: string streams dont need to be locked */
1189 else if(!(file->_flag & _IOSTRG))
1190 EnterCriticalSection(&((file_crit*)file)->crit);
1191 }
1192
1193 /*********************************************************************
1194 * _unlock_file (MSVCRT.@)
1195 */
1196 void CDECL _unlock_file(FILE *file)
1197 {
1198 if(file>=_iob && file<_iob+_IOB_ENTRIES)
1199 _unlock(_STREAM_LOCKS+(file-_iob));
1200 /* ReactOS: string streams dont need to be locked */
1201 else if(!(file->_flag & _IOSTRG))
1202 LeaveCriticalSection(&((file_crit*)file)->crit);
1203
1204 }
1205
1206 /*********************************************************************
1207 * _locking (MSVCRT.@)
1208 *
1209 * This is untested; the underlying LockFile doesn't work yet.
1210 */
1211 int CDECL _locking(int fd, int mode, LONG nbytes)
1212 {
1213 ioinfo *info = get_ioinfo(fd);
1214 BOOL ret;
1215 DWORD cur_locn;
1216
1217 TRACE(":fd (%d) handle (%p)\n", fd, info->handle);
1218 if (info->handle == INVALID_HANDLE_VALUE)
1219 {
1220 release_ioinfo(info);
1221 return -1;
1222 }
1223
1224 if (mode < 0 || mode > 4)
1225 {
1226 release_ioinfo(info);
1227 *_errno() = EINVAL;
1228 return -1;
1229 }
1230
1231 TRACE(":fd (%d) by 0x%08x mode %s\n",
1232 fd,nbytes,(mode==_LK_UNLCK)?"_LK_UNLCK":
1233 (mode==_LK_LOCK)?"_LK_LOCK":
1234 (mode==_LK_NBLCK)?"_LK_NBLCK":
1235 (mode==_LK_RLCK)?"_LK_RLCK":
1236 (mode==_LK_NBRLCK)?"_LK_NBRLCK":
1237 "UNKNOWN");
1238
1239 if ((cur_locn = SetFilePointer(info->handle, 0L, NULL, SEEK_CUR)) == INVALID_SET_FILE_POINTER)
1240 {
1241 release_ioinfo(info);
1242 FIXME ("Seek failed\n");
1243 *_errno() = EINVAL; /* FIXME */
1244 return -1;
1245 }
1246 if (mode == _LK_LOCK || mode == _LK_RLCK)
1247 {
1248 int nretry = 10;
1249 ret = 1; /* just to satisfy gcc */
1250 while (nretry--)
1251 {
1252 ret = LockFile(info->handle, cur_locn, 0L, nbytes, 0L);
1253 if (ret) break;
1254 Sleep(1);
1255 }
1256 }
1257 else if (mode == _LK_UNLCK)
1258 ret = UnlockFile(info->handle, cur_locn, 0L, nbytes, 0L);
1259 else
1260 ret = LockFile(info->handle, cur_locn, 0L, nbytes, 0L);
1261 /* FIXME - what about error settings? */
1262 release_ioinfo(info);
1263 return ret ? 0 : -1;
1264 }
1265
1266 /*********************************************************************
1267 * _fseeki64 (MSVCRT.@)
1268 */
1269 int CDECL _fseeki64(FILE* file, __int64 offset, int whence)
1270 {
1271 int ret;
1272
1273 _lock_file(file);
1274 /* Flush output if needed */
1275 if(file->_flag & _IOWRT)
1276 msvcrt_flush_buffer(file);
1277
1278 if(whence == SEEK_CUR && file->_flag & _IOREAD ) {
1279 whence = SEEK_SET;
1280 offset += _ftelli64(file);
1281 }
1282
1283 /* Discard buffered input */
1284 file->_cnt = 0;
1285 file->_ptr = file->_base;
1286 /* Reset direction of i/o */
1287 if(file->_flag & _IORW) {
1288 file->_flag &= ~(_IOREAD|_IOWRT);
1289 }
1290 /* Clear end of file flag */
1291 file->_flag &= ~_IOEOF;
1292 ret = (_lseeki64(file->_file,offset,whence) == -1)?-1:0;
1293
1294 _unlock_file(file);
1295 return ret;
1296 }
1297
1298 /*********************************************************************
1299 * fseek (MSVCRT.@)
1300 */
1301 int CDECL fseek(FILE* file, long offset, int whence)
1302 {
1303 return _fseeki64( file, offset, whence );
1304 }
1305
1306 /*********************************************************************
1307 * _chsize_s (MSVCRT.@)
1308 */
1309 int CDECL _chsize_s(int fd, __int64 size)
1310 {
1311 ioinfo *info;
1312 __int64 cur, pos;
1313 BOOL ret = FALSE;
1314
1315 TRACE("(fd=%d, size=%s)\n", fd, wine_dbgstr_longlong(size));
1316
1317 if (!MSVCRT_CHECK_PMT(size >= 0)) return EINVAL;
1318
1319 info = get_ioinfo(fd);
1320 if (info->handle != INVALID_HANDLE_VALUE)
1321 {
1322 /* save the current file pointer */
1323 cur = _lseeki64(fd, 0, SEEK_CUR);
1324 if (cur >= 0)
1325 {
1326 pos = _lseeki64(fd, size, SEEK_SET);
1327 if (pos >= 0)
1328 {
1329 ret = SetEndOfFile(info->handle);
1330 if (!ret) _dosmaperr(GetLastError());
1331 }
1332
1333 /* restore the file pointer */
1334 _lseeki64(fd, cur, SEEK_SET);
1335 }
1336 }
1337
1338 release_ioinfo(info);
1339 return ret ? 0 : *_errno();
1340 }
1341
1342 /*********************************************************************
1343 * _chsize (MSVCRT.@)
1344 */
1345 int CDECL _chsize(int fd, long size)
1346 {
1347 /* _chsize_s returns errno on failure but _chsize should return -1 */
1348 return _chsize_s( fd, size ) == 0 ? 0 : -1;
1349 }
1350
1351 /*********************************************************************
1352 * clearerr (MSVCRT.@)
1353 */
1354 void CDECL clearerr(FILE* file)
1355 {
1356 TRACE(":file (%p) fd (%d)\n",file,file->_file);
1357
1358 _lock_file(file);
1359 file->_flag &= ~(_IOERR | _IOEOF);
1360 _unlock_file(file);
1361 }
1362
1363 /*********************************************************************
1364 * rewind (MSVCRT.@)
1365 */
1366 void CDECL rewind(FILE* file)
1367 {
1368 TRACE(":file (%p) fd (%d)\n",file,file->_file);
1369
1370 _lock_file(file);
1371 fseek(file, 0L, SEEK_SET);
1372 clearerr(file);
1373 _unlock_file(file);
1374 }
1375
1376 static int msvcrt_get_flags(const wchar_t* mode, int *open_flags, int* stream_flags)
1377 {
1378 int plus = strchrW(mode, '+') != NULL;
1379
1380 TRACE("%s\n", debugstr_w(mode));
1381
1382 while(*mode == ' ') mode++;
1383
1384 switch(*mode++)
1385 {
1386 case 'R': case 'r':
1387 *open_flags = plus ? _O_RDWR : _O_RDONLY;
1388 *stream_flags = plus ? _IORW : _IOREAD;
1389 break;
1390 case 'W': case 'w':
1391 *open_flags = _O_CREAT | _O_TRUNC | (plus ? _O_RDWR : _O_WRONLY);
1392 *stream_flags = plus ? _IORW : _IOWRT;
1393 break;
1394 case 'A': case 'a':
1395 *open_flags = _O_CREAT | _O_APPEND | (plus ? _O_RDWR : _O_WRONLY);
1396 *stream_flags = plus ? _IORW : _IOWRT;
1397 break;
1398 default:
1399 MSVCRT_INVALID_PMT(0, EINVAL);
1400 return -1;
1401 }
1402
1403 *stream_flags |= _commode;
1404
1405 while (*mode && *mode!=',')
1406 switch (*mode++)
1407 {
1408 case 'B': case 'b':
1409 *open_flags |= _O_BINARY;
1410 *open_flags &= ~_O_TEXT;
1411 break;
1412 case 't':
1413 *open_flags |= _O_TEXT;
1414 *open_flags &= ~_O_BINARY;
1415 break;
1416 case 'D':
1417 *open_flags |= _O_TEMPORARY;
1418 break;
1419 case 'T':
1420 *open_flags |= _O_SHORT_LIVED;
1421 break;
1422 case 'c':
1423 *stream_flags |= _IOCOMMIT;
1424 break;
1425 case 'n':
1426 *stream_flags &= ~_IOCOMMIT;
1427 break;
1428 case 'N':
1429 *open_flags |= _O_NOINHERIT;
1430 break;
1431 case '+':
1432 case ' ':
1433 case 'a':
1434 case 'w':
1435 break;
1436 case 'S':
1437 case 'R':
1438 FIXME("ignoring cache optimization flag: %c\n", mode[-1]);
1439 break;
1440 default:
1441 ERR("incorrect mode flag: %c\n", mode[-1]);
1442 break;
1443 }
1444
1445 if(*mode == ',')
1446 {
1447 static const WCHAR ccs[] = {'c','c','s'};
1448 static const WCHAR utf8[] = {'u','t','f','-','8'};
1449 static const WCHAR utf16le[] = {'u','t','f','-','1','6','l','e'};
1450 static const WCHAR unicode[] = {'u','n','i','c','o','d','e'};
1451
1452 mode++;
1453 while(*mode == ' ') mode++;
1454 if(!MSVCRT_CHECK_PMT(!strncmpW(ccs, mode, sizeof(ccs)/sizeof(ccs[0]))))
1455 return -1;
1456 mode += sizeof(ccs)/sizeof(ccs[0]);
1457 while(*mode == ' ') mode++;
1458 if(!MSVCRT_CHECK_PMT(*mode == '='))
1459 return -1;
1460 mode++;
1461 while(*mode == ' ') mode++;
1462
1463 if(!strncmpiW(utf8, mode, sizeof(utf8)/sizeof(utf8[0])))
1464 {
1465 *open_flags |= _O_U8TEXT;
1466 mode += sizeof(utf8)/sizeof(utf8[0]);
1467 }
1468 else if(!strncmpiW(utf16le, mode, sizeof(utf16le)/sizeof(utf16le[0])))
1469 {
1470 *open_flags |= _O_U16TEXT;
1471 mode += sizeof(utf16le)/sizeof(utf16le[0]);
1472 }
1473 else if(!strncmpiW(unicode, mode, sizeof(unicode)/sizeof(unicode[0])))
1474 {
1475 *open_flags |= _O_WTEXT;
1476 mode += sizeof(unicode)/sizeof(unicode[0]);
1477 }
1478 else
1479 {
1480 MSVCRT_INVALID_PMT(0, EINVAL);
1481 return -1;
1482 }
1483
1484 while(*mode == ' ') mode++;
1485 }
1486
1487 if(!MSVCRT_CHECK_PMT(*mode == 0))
1488 return -1;
1489 return 0;
1490 }
1491
1492 /*********************************************************************
1493 * _fdopen (MSVCRT.@)
1494 */
1495 FILE* CDECL _fdopen(int fd, const char *mode)
1496 {
1497 FILE *ret;
1498 wchar_t *modeW = NULL;
1499
1500 if (mode && !(modeW = msvcrt_wstrdupa(mode))) return NULL;
1501
1502 ret = _wfdopen(fd, modeW);
1503
1504 free(modeW);
1505 return ret;
1506 }
1507
1508 /*********************************************************************
1509 * _wfdopen (MSVCRT.@)
1510 */
1511 FILE* CDECL _wfdopen(int fd, const wchar_t *mode)
1512 {
1513 int open_flags, stream_flags;
1514 FILE* file;
1515
1516 if (msvcrt_get_flags(mode, &open_flags, &stream_flags) == -1) return NULL;
1517
1518 LOCK_FILES();
1519 if (!(file = msvcrt_alloc_fp()))
1520 file = NULL;
1521 else if (msvcrt_init_fp(file, fd, stream_flags) == -1)
1522 {
1523 file->_flag = 0;
1524 file = NULL;
1525 }
1526 else TRACE(":fd (%d) mode (%s) FILE* (%p)\n", fd, debugstr_w(mode), file);
1527 UNLOCK_FILES();
1528
1529 return file;
1530 }
1531
1532 /*********************************************************************
1533 * _filelength (MSVCRT.@)
1534 */
1535 LONG CDECL _filelength(int fd)
1536 {
1537 LONG curPos = _lseek(fd, 0, SEEK_CUR);
1538 if (curPos != -1)
1539 {
1540 LONG endPos = _lseek(fd, 0, SEEK_END);
1541 if (endPos != -1)
1542 {
1543 if (endPos != curPos)
1544 _lseek(fd, curPos, SEEK_SET);
1545 return endPos;
1546 }
1547 }
1548 return -1;
1549 }
1550
1551 /*********************************************************************
1552 * _filelengthi64 (MSVCRT.@)
1553 */
1554 __int64 CDECL _filelengthi64(int fd)
1555 {
1556 __int64 curPos = _lseeki64(fd, 0, SEEK_CUR);
1557 if (curPos != -1)
1558 {
1559 __int64 endPos = _lseeki64(fd, 0, SEEK_END);
1560 if (endPos != -1)
1561 {
1562 if (endPos != curPos)
1563 _lseeki64(fd, curPos, SEEK_SET);
1564 return endPos;
1565 }
1566 }
1567 return -1;
1568 }
1569
1570 /*********************************************************************
1571 * _fileno (MSVCRT.@)
1572 */
1573 int CDECL _fileno(FILE* file)
1574 {
1575 TRACE(":FILE* (%p) fd (%d)\n",file,file->_file);
1576 return file->_file;
1577 }
1578
1579 /*********************************************************************
1580 * _get_osfhandle (MSVCRT.@)
1581 */
1582 intptr_t CDECL _get_osfhandle(int fd)
1583 {
1584 HANDLE hand = fdtoh(fd);
1585 TRACE(":fd (%d) handle (%p)\n",fd,hand);
1586
1587 return (intptr_t)hand;
1588 }
1589
1590 /*********************************************************************
1591 * _mktemp (MSVCRT.@)
1592 */
1593 char * CDECL _mktemp(char *pattern)
1594 {
1595 int numX = 0;
1596 char *retVal = pattern;
1597 int id;
1598 char letter = 'a';
1599
1600 if(!pattern)
1601 return NULL;
1602
1603 while(*pattern)
1604 numX = (*pattern++ == 'X')? numX + 1 : 0;
1605 if (numX < 6)
1606 return NULL;
1607 pattern--;
1608 id = GetCurrentProcessId();
1609 numX = 6;
1610 while(numX--)
1611 {
1612 int tempNum = id / 10;
1613 *pattern-- = id - (tempNum * 10) + '0';
1614 id = tempNum;
1615 }
1616 pattern++;
1617 do
1618 {
1619 *pattern = letter++;
1620 if (GetFileAttributesA(retVal) == INVALID_FILE_ATTRIBUTES)
1621 return retVal;
1622 } while(letter <= 'z');
1623 return NULL;
1624 }
1625
1626 /*********************************************************************
1627 * _wmktemp (MSVCRT.@)
1628 */
1629 wchar_t * CDECL _wmktemp(wchar_t *pattern)
1630 {
1631 int numX = 0;
1632 wchar_t *retVal = pattern;
1633 int id;
1634 wchar_t letter = 'a';
1635
1636 while(*pattern)
1637 numX = (*pattern++ == 'X')? numX + 1 : 0;
1638 if (numX < 5)
1639 return NULL;
1640 pattern--;
1641 id = GetCurrentProcessId();
1642 numX = 6;
1643 while(numX--)
1644 {
1645 int tempNum = id / 10;
1646 *pattern-- = id - (tempNum * 10) + '0';
1647 id = tempNum;
1648 }
1649 pattern++;
1650 do
1651 {
1652 if (GetFileAttributesW(retVal) == INVALID_FILE_ATTRIBUTES &&
1653 GetLastError() == ERROR_FILE_NOT_FOUND)
1654 return retVal;
1655 *pattern = letter++;
1656 } while(letter != '|');
1657 return NULL;
1658 }
1659
1660 /*static*/ unsigned split_oflags(unsigned oflags)
1661 {
1662 int wxflags = 0;
1663 unsigned unsupp; /* until we support everything */
1664
1665 if (oflags & _O_APPEND) wxflags |= WX_APPEND;
1666 if (oflags & _O_BINARY) {/* Nothing to do */}
1667 else if (oflags & _O_TEXT) wxflags |= WX_TEXT;
1668 else if (oflags & _O_WTEXT) wxflags |= WX_TEXT;
1669 else if (oflags & _O_U16TEXT) wxflags |= WX_TEXT;
1670 else if (oflags & _O_U8TEXT) wxflags |= WX_TEXT;
1671 else if (*__p__fmode() & _O_BINARY) {/* Nothing to do */}
1672 else wxflags |= WX_TEXT; /* default to TEXT*/
1673 if (oflags & _O_NOINHERIT) wxflags |= WX_DONTINHERIT;
1674
1675 if ((unsupp = oflags & ~(
1676 _O_BINARY|_O_TEXT|_O_APPEND|
1677 _O_TRUNC|_O_EXCL|_O_CREAT|
1678 _O_RDWR|_O_WRONLY|_O_TEMPORARY|
1679 _O_NOINHERIT|
1680 _O_SEQUENTIAL|_O_RANDOM|_O_SHORT_LIVED|
1681 _O_WTEXT|_O_U16TEXT|_O_U8TEXT
1682 )))
1683 ERR(":unsupported oflags 0x%04x\n",unsupp);
1684
1685 return wxflags;
1686 }
1687
1688 /*********************************************************************
1689 * _pipe (MSVCRT.@)
1690 */
1691 int CDECL _pipe(int *pfds, unsigned int psize, int textmode)
1692 {
1693 int ret = -1;
1694 SECURITY_ATTRIBUTES sa;
1695 HANDLE readHandle, writeHandle;
1696
1697 if (!pfds)
1698 {
1699 *_errno() = EINVAL;
1700 return -1;
1701 }
1702
1703 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
1704 sa.bInheritHandle = !(textmode & _O_NOINHERIT);
1705 sa.lpSecurityDescriptor = NULL;
1706 if (CreatePipe(&readHandle, &writeHandle, &sa, psize))
1707 {
1708 unsigned int wxflags = split_oflags(textmode);
1709 int fd;
1710
1711 LOCK_FILES();
1712 fd = msvcrt_alloc_fd(readHandle, wxflags);
1713 if (fd != -1)
1714 {
1715 pfds[0] = fd;
1716 fd = msvcrt_alloc_fd(writeHandle, wxflags);
1717 if (fd != -1)
1718 {
1719 pfds[1] = fd;
1720 ret = 0;
1721 }
1722 else
1723 {
1724 _close(pfds[0]);
1725 CloseHandle(writeHandle);
1726 *_errno() = EMFILE;
1727 }
1728 }
1729 else
1730 {
1731 CloseHandle(readHandle);
1732 CloseHandle(writeHandle);
1733 *_errno() = EMFILE;
1734 }
1735 UNLOCK_FILES();
1736 }
1737 else
1738 _dosmaperr(GetLastError());
1739
1740 return ret;
1741 }
1742
1743 static int check_bom(HANDLE h, int oflags, BOOL seek)
1744 {
1745 char bom[sizeof(utf8_bom)];
1746 DWORD r;
1747
1748 oflags &= ~(_O_WTEXT|_O_U16TEXT|_O_U8TEXT);
1749
1750 if (!ReadFile(h, bom, sizeof(utf8_bom), &r, NULL))
1751 return oflags;
1752
1753 if (r==sizeof(utf8_bom) && !memcmp(bom, utf8_bom, sizeof(utf8_bom))) {
1754 oflags |= _O_U8TEXT;
1755 }else if (r>=sizeof(utf16_bom) && !memcmp(bom, utf16_bom, sizeof(utf16_bom))) {
1756 if (seek && r>2)
1757 SetFilePointer(h, 2, NULL, FILE_BEGIN);
1758 oflags |= _O_U16TEXT;
1759 }else if (seek) {
1760 SetFilePointer(h, 0, NULL, FILE_BEGIN);
1761 }
1762
1763 return oflags;
1764 }
1765
1766 /*********************************************************************
1767 * _wsopen_s (MSVCRT.@)
1768 */
1769 int CDECL _wsopen_s( int *fd, const wchar_t* path, int oflags, int shflags, int pmode )
1770 {
1771 DWORD access = 0, creation = 0, attrib;
1772 SECURITY_ATTRIBUTES sa;
1773 DWORD sharing, type;
1774 int wxflag;
1775 HANDLE hand;
1776
1777 TRACE("fd*: %p :file (%s) oflags: 0x%04x shflags: 0x%04x pmode: 0x%04x\n",
1778 fd, debugstr_w(path), oflags, shflags, pmode);
1779
1780 if (!MSVCRT_CHECK_PMT( fd != NULL )) return EINVAL;
1781
1782 *fd = -1;
1783 wxflag = split_oflags(oflags);
1784 switch (oflags & (_O_RDONLY | _O_WRONLY | _O_RDWR))
1785 {
1786 case _O_RDONLY: access |= GENERIC_READ; break;
1787 case _O_WRONLY: access |= GENERIC_WRITE; break;
1788 case _O_RDWR: access |= GENERIC_WRITE | GENERIC_READ; break;
1789 }
1790
1791 if (oflags & _O_CREAT)
1792 {
1793 if(pmode & ~(_S_IREAD | _S_IWRITE))
1794 FIXME(": pmode 0x%04x ignored\n", pmode);
1795 else
1796 WARN(": pmode 0x%04x ignored\n", pmode);
1797
1798 if (oflags & _O_EXCL)
1799 creation = CREATE_NEW;
1800 else if (oflags & _O_TRUNC)
1801 creation = CREATE_ALWAYS;
1802 else
1803 creation = OPEN_ALWAYS;
1804 }
1805 else /* no _O_CREAT */
1806 {
1807 if (oflags & _O_TRUNC)
1808 creation = TRUNCATE_EXISTING;
1809 else
1810 creation = OPEN_EXISTING;
1811 }
1812
1813 switch( shflags )
1814 {
1815 case _SH_DENYRW:
1816 sharing = 0L;
1817 break;
1818 case _SH_DENYWR:
1819 sharing = FILE_SHARE_READ;
1820 break;
1821 case _SH_DENYRD:
1822 sharing = FILE_SHARE_WRITE;
1823 break;
1824 case _SH_DENYNO:
1825 sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
1826 break;
1827 default:
1828 ERR( "Unhandled shflags 0x%x\n", shflags );
1829 return EINVAL;
1830 }
1831 attrib = FILE_ATTRIBUTE_NORMAL;
1832
1833 if (oflags & _O_TEMPORARY)
1834 {
1835 attrib |= FILE_FLAG_DELETE_ON_CLOSE;
1836 access |= DELETE;
1837 sharing |= FILE_SHARE_DELETE;
1838 }
1839
1840 sa.nLength = sizeof( SECURITY_ATTRIBUTES );
1841 sa.lpSecurityDescriptor = NULL;
1842 sa.bInheritHandle = !(oflags & _O_NOINHERIT);
1843
1844 if ((oflags&(_O_WTEXT|_O_U16TEXT|_O_U8TEXT))
1845 && (creation==OPEN_ALWAYS || creation==OPEN_EXISTING)
1846 && !(access&GENERIC_READ))
1847 {
1848 hand = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
1849 &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
1850 if (hand != INVALID_HANDLE_VALUE)
1851 {
1852 oflags = check_bom(hand, oflags, FALSE);
1853 CloseHandle(hand);
1854 }
1855 else
1856 oflags &= ~(_O_WTEXT|_O_U16TEXT|_O_U8TEXT);
1857 }
1858
1859 hand = CreateFileW(path, access, sharing, &sa, creation, attrib, 0);
1860 if (hand == INVALID_HANDLE_VALUE) {
1861 WARN(":failed-last error (%d)\n",GetLastError());
1862 _dosmaperr(GetLastError());
1863 return *_errno();
1864 }
1865
1866 if (oflags & (_O_WTEXT|_O_U16TEXT|_O_U8TEXT))
1867 {
1868 if ((access & GENERIC_WRITE) && (creation==CREATE_NEW
1869 || creation==CREATE_ALWAYS || creation==TRUNCATE_EXISTING
1870 || (creation==OPEN_ALWAYS && GetLastError()==ERROR_ALREADY_EXISTS)))
1871 {
1872 if (oflags & _O_U8TEXT)
1873 {
1874 DWORD written = 0, tmp;
1875
1876 while(written!=sizeof(utf8_bom) && WriteFile(hand, (char*)utf8_bom+written,
1877 sizeof(utf8_bom)-written, &tmp, NULL))
1878 written += tmp;
1879 if (written != sizeof(utf8_bom)) {
1880 WARN("error writing BOM\n");
1881 CloseHandle(hand);
1882 _dosmaperr(GetLastError());
1883 return *_errno();
1884 }
1885 }
1886 else
1887 {
1888 DWORD written = 0, tmp;
1889
1890 while(written!=sizeof(utf16_bom) && WriteFile(hand, (char*)utf16_bom+written,
1891 sizeof(utf16_bom)-written, &tmp, NULL))
1892 written += tmp;
1893 if (written != sizeof(utf16_bom))
1894 {
1895 WARN("error writing BOM\n");
1896 CloseHandle(hand);
1897 _dosmaperr(GetLastError());
1898 return *_errno();
1899 }
1900 }
1901 }
1902 else if (access & GENERIC_READ)
1903 oflags = check_bom(hand, oflags, TRUE);
1904 }
1905
1906 type = GetFileType(hand);
1907 if (type == FILE_TYPE_CHAR)
1908 wxflag |= WX_TTY;
1909 else if (type == FILE_TYPE_PIPE)
1910 wxflag |= WX_PIPE;
1911
1912 *fd = msvcrt_alloc_fd(hand, wxflag);
1913 if (*fd == -1)
1914 return *_errno();
1915
1916 if (oflags & _O_WTEXT)
1917 get_ioinfo_nolock(*fd)->exflag |= EF_UTF16|EF_UNK_UNICODE;
1918 else if (oflags & _O_U16TEXT)
1919 get_ioinfo_nolock(*fd)->exflag |= EF_UTF16;
1920 else if (oflags & _O_U8TEXT)
1921 get_ioinfo_nolock(*fd)->exflag |= EF_UTF8;
1922
1923 TRACE(":fd (%d) handle (%p)\n", *fd, hand);
1924 return 0;
1925 }
1926
1927 /*********************************************************************
1928 * _wsopen (MSVCRT.@)
1929 */
1930 int CDECL _wsopen( const wchar_t *path, int oflags, int shflags, ... )
1931 {
1932 int pmode;
1933 int fd;
1934
1935 if (oflags & _O_CREAT)
1936 {
1937 __ms_va_list ap;
1938
1939 __ms_va_start(ap, shflags);
1940 pmode = va_arg(ap, int);
1941 __ms_va_end(ap);
1942 }
1943 else
1944 pmode = 0;
1945
1946 _wsopen_s(&fd, path, oflags, shflags, pmode);
1947 return fd;
1948 }
1949
1950 /*********************************************************************
1951 * _sopen_s (MSVCRT.@)
1952 */
1953 int CDECL _sopen_s( int *fd, const char *path, int oflags, int shflags, int pmode )
1954 {
1955 wchar_t *pathW;
1956 int ret;
1957
1958 if(!MSVCRT_CHECK_PMT(path && (pathW = msvcrt_wstrdupa(path))))
1959 return EINVAL;
1960
1961 ret = _wsopen_s(fd, pathW, oflags, shflags, pmode);
1962 free(pathW);
1963 return ret;
1964 }
1965
1966 /*********************************************************************
1967 * _sopen (MSVCRT.@)
1968 */
1969 int CDECL _sopen( const char *path, int oflags, int shflags, ... )
1970 {
1971 int pmode;
1972 int fd;
1973
1974 if (oflags & _O_CREAT)
1975 {
1976 va_list ap;
1977
1978 va_start(ap, shflags);
1979 pmode = va_arg(ap, int);
1980 va_end(ap);
1981 }
1982 else
1983 pmode = 0;
1984
1985 _sopen_s(&fd, path, oflags, shflags, pmode);
1986 return fd;
1987 }
1988
1989 /*********************************************************************
1990 * _open (MSVCRT.@)
1991 */
1992 int CDECL _open( const char *path, int flags, ... )
1993 {
1994 va_list ap;
1995
1996 if (flags & _O_CREAT)
1997 {
1998 int pmode;
1999 va_start(ap, flags);
2000 pmode = va_arg(ap, int);
2001 va_end(ap);
2002 return _sopen( path, flags, _SH_DENYNO, pmode );
2003 }
2004 else
2005 return _sopen( path, flags, _SH_DENYNO);
2006 }
2007
2008 /*********************************************************************
2009 * _wopen (MSVCRT.@)
2010 */
2011 int CDECL _wopen(const wchar_t *path,int flags,...)
2012 {
2013 va_list ap;
2014
2015 if (flags & _O_CREAT)
2016 {
2017 int pmode;
2018 va_start(ap, flags);
2019 pmode = va_arg(ap, int);
2020 va_end(ap);
2021 return _wsopen( path, flags, _SH_DENYNO, pmode );
2022 }
2023 else
2024 return _wsopen( path, flags, _SH_DENYNO);
2025 }
2026
2027 /*********************************************************************
2028 * _creat (MSVCRT.@)
2029 */
2030 int CDECL _creat(const char *path, int flags)
2031 {
2032 int usedFlags = (flags & _O_TEXT)| _O_CREAT| _O_WRONLY| _O_TRUNC;
2033 return _open(path, usedFlags);
2034 }
2035
2036 /*********************************************************************
2037 * _wcreat (MSVCRT.@)
2038 */
2039 int CDECL _wcreat(const wchar_t *path, int flags)
2040 {
2041 int usedFlags = (flags & _O_TEXT)| _O_CREAT| _O_WRONLY| _O_TRUNC;
2042 return _wopen(path, usedFlags);
2043 }
2044
2045 /*********************************************************************
2046 * _open_osfhandle (MSVCRT.@)
2047 */
2048 int CDECL _open_osfhandle(intptr_t handle, int oflags)
2049 {
2050 DWORD flags;
2051 int fd;
2052
2053 /* _O_RDONLY (0) always matches, so set the read flag
2054 * MFC's CStdioFile clears O_RDONLY (0)! if it wants to write to the
2055 * file, so set the write flag. It also only sets _O_TEXT if it wants
2056 * text - it never sets _O_BINARY.
2057 */
2058 /* don't let split_oflags() decide the mode if no mode is passed */
2059 if (!(oflags & (_O_BINARY | _O_TEXT)))
2060 oflags |= _O_BINARY;
2061
2062 flags = GetFileType((HANDLE)handle);
2063 if (flags==FILE_TYPE_UNKNOWN && GetLastError()!=NO_ERROR)
2064 {
2065 _dosmaperr(GetLastError());
2066 return -1;
2067 }
2068
2069 if (flags == FILE_TYPE_CHAR)
2070 flags = WX_TTY;
2071 else if (flags == FILE_TYPE_PIPE)
2072 flags = WX_PIPE;
2073 else
2074 flags = 0;
2075 flags |= split_oflags(oflags);
2076
2077 fd = msvcrt_alloc_fd((HANDLE)handle, flags);
2078 TRACE(":handle (%ld) fd (%d) flags 0x%08x\n", handle, fd, flags);
2079 return fd;
2080 }
2081
2082 /*********************************************************************
2083 * _rmtmp (MSVCRT.@)
2084 */
2085 int CDECL _rmtmp(void)
2086 {
2087 int num_removed = 0, i;
2088 FILE *file;
2089
2090 LOCK_FILES();
2091 for (i = 3; i < MSVCRT_stream_idx; i++) {
2092 file = msvcrt_get_file(i);
2093
2094 if (file->_tmpfname)
2095 {
2096 fclose(file);
2097 num_removed++;
2098 }
2099 }
2100 UNLOCK_FILES();
2101
2102 if (num_removed)
2103 TRACE(":removed (%d) temp files\n",num_removed);
2104 return num_removed;
2105 }
2106
2107 static inline int get_utf8_char_len(char ch)
2108 {
2109 if((ch&0xf8) == 0xf0)
2110 return 4;
2111 else if((ch&0xf0) == 0xe0)
2112 return 3;
2113 else if((ch&0xe0) == 0xc0)
2114 return 2;
2115 return 1;
2116 }
2117
2118 /*********************************************************************
2119 * (internal) read_utf8
2120 */
2121 static int read_utf8(ioinfo *fdinfo, wchar_t *buf, unsigned int count)
2122 {
2123 HANDLE hand = fdinfo->handle;
2124 char min_buf[4], *readbuf, lookahead;
2125 DWORD readbuf_size, pos=0, num_read=1, char_len, i, j;
2126
2127 /* make the buffer big enough to hold at least one character */
2128 /* read bytes have to fit to output and lookahead buffers */
2129 count /= 2;
2130 readbuf_size = count < 4 ? 4 : count;
2131 if(readbuf_size<=4 || !(readbuf = malloc(readbuf_size))) {
2132 readbuf_size = 4;
2133 readbuf = min_buf;
2134 }
2135
2136 if(fdinfo->lookahead[0] != '\n') {
2137 readbuf[pos++] = fdinfo->lookahead[0];
2138 fdinfo->lookahead[0] = '\n';
2139
2140 if(fdinfo->lookahead[1] != '\n') {
2141 readbuf[pos++] = fdinfo->lookahead[1];
2142 fdinfo->lookahead[1] = '\n';
2143
2144 if(fdinfo->lookahead[2] != '\n') {
2145 readbuf[pos++] = fdinfo->lookahead[2];
2146 fdinfo->lookahead[2] = '\n';
2147 }
2148 }
2149 }
2150
2151 /* NOTE: this case is broken in native dll, reading
2152 * sometimes fails when small buffer is passed
2153 */
2154 if(count < 4) {
2155 if(!pos && !ReadFile(hand, readbuf, 1, &num_read, NULL)) {
2156 if (GetLastError() == ERROR_BROKEN_PIPE) {
2157 fdinfo->wxflag |= WX_ATEOF;
2158 return 0;
2159 }else {
2160 _dosmaperr(GetLastError());
2161 return -1;
2162 }
2163 }else if(!num_read) {
2164 fdinfo->wxflag |= WX_ATEOF;
2165 return 0;
2166 }else {
2167 pos++;
2168 }
2169
2170 char_len = get_utf8_char_len(readbuf[0]);
2171 if(char_len>pos) {
2172 if(ReadFile(hand, readbuf+pos, char_len-pos, &num_read, NULL))
2173 pos += num_read;
2174 }
2175
2176 if(readbuf[0] == '\n')
2177 fdinfo->wxflag |= WX_READNL;
2178 else
2179 fdinfo->wxflag &= ~WX_READNL;
2180
2181 if(readbuf[0] == 0x1a) {
2182 fdinfo->wxflag |= WX_ATEOF;
2183 return 0;
2184 }
2185
2186 if(readbuf[0] == '\r') {
2187 if(!ReadFile(hand, &lookahead, 1, &num_read, NULL) || num_read!=1)
2188 buf[0] = '\r';
2189 else if(lookahead == '\n')
2190 buf[0] = '\n';
2191 else {
2192 buf[0] = '\r';
2193 if(fdinfo->wxflag & (WX_PIPE | WX_TTY))
2194 fdinfo->lookahead[0] = lookahead;
2195 else
2196 SetFilePointer(fdinfo->handle, -1, NULL, FILE_CURRENT);
2197 }
2198 return 2;
2199 }
2200
2201 if(!(num_read = MultiByteToWideChar(CP_UTF8, 0, readbuf, pos, buf, count))) {
2202 _dosmaperr(GetLastError());
2203 return -1;
2204 }
2205
2206 return num_read*2;
2207 }
2208
2209 if(!ReadFile(hand, readbuf+pos, readbuf_size-pos, &num_read, NULL)) {
2210 if(pos) {
2211 num_read = 0;
2212 }else if(GetLastError() == ERROR_BROKEN_PIPE) {
2213 fdinfo->wxflag |= WX_ATEOF;
2214 if (readbuf != min_buf) free(readbuf);
2215 return 0;
2216 }else {
2217 _dosmaperr(GetLastError());
2218 if (readbuf != min_buf) free(readbuf);
2219 return -1;
2220 }
2221 }else if(!pos && !num_read) {
2222 fdinfo->wxflag |= WX_ATEOF;
2223 if (readbuf != min_buf) free(readbuf);
2224 return 0;
2225 }
2226
2227 pos += num_read;
2228 if(readbuf[0] == '\n')
2229 fdinfo->wxflag |= WX_READNL;
2230 else
2231 fdinfo->wxflag &= ~WX_READNL;
2232
2233 /* Find first byte of last character (may be incomplete) */
2234 for(i=pos-1; i>0 && i>pos-4; i--)
2235 if((readbuf[i]&0xc0) != 0x80)
2236 break;
2237 char_len = get_utf8_char_len(readbuf[i]);
2238 if(char_len+i <= pos)
2239 i += char_len;
2240
2241 if(fdinfo->wxflag & (WX_PIPE | WX_TTY)) {
2242 if(i < pos)
2243 fdinfo->lookahead[0] = readbuf[i];
2244 if(i+1 < pos)
2245 fdinfo->lookahead[1] = readbuf[i+1];
2246 if(i+2 < pos)
2247 fdinfo->lookahead[2] = readbuf[i+2];
2248 }else if(i < pos) {
2249 SetFilePointer(fdinfo->handle, i-pos, NULL, FILE_CURRENT);
2250 }
2251 pos = i;
2252
2253 for(i=0, j=0; i<pos; i++) {
2254 if(readbuf[i] == 0x1a) {
2255 fdinfo->wxflag |= WX_ATEOF;
2256 break;
2257 }
2258
2259 /* strip '\r' if followed by '\n' */
2260 if(readbuf[i] == '\r' && i+1==pos) {
2261 if(fdinfo->lookahead[0] != '\n' || !ReadFile(hand, &lookahead, 1, &num_read, NULL) || !num_read) {
2262 readbuf[j++] = '\r';
2263 }else if(lookahead == '\n' && j==0) {
2264 readbuf[j++] = '\n';
2265 }else {
2266 if(lookahead != '\n')
2267 readbuf[j++] = '\r';
2268
2269 if(fdinfo->wxflag & (WX_PIPE | WX_TTY))
2270 fdinfo->lookahead[0] = lookahead;
2271 else
2272 SetFilePointer(fdinfo->handle, -1, NULL, FILE_CURRENT);
2273 }
2274 }else if(readbuf[i]!='\r' || readbuf[i+1]!='\n') {
2275 readbuf[j++] = readbuf[i];
2276 }
2277 }
2278 pos = j;
2279
2280 if(!(num_read = MultiByteToWideChar(CP_UTF8, 0, readbuf, pos, buf, count))) {
2281 _dosmaperr(GetLastError());
2282 if (readbuf != min_buf) free(readbuf);
2283 return -1;
2284 }
2285
2286 if (readbuf != min_buf) free(readbuf);
2287 return num_read*2;
2288 }
2289
2290 /*********************************************************************
2291 * (internal) read_i
2292 *
2293 * When reading \r as last character in text mode, read() positions
2294 * the file pointer on the \r character while getc() goes on to
2295 * the following \n
2296 */
2297 static int read_i(int fd, ioinfo *fdinfo, void *buf, unsigned int count)
2298 {
2299 DWORD num_read, utf16;
2300 char *bufstart = buf;
2301
2302 if (count == 0)
2303 return 0;
2304
2305 if (fdinfo->wxflag & WX_ATEOF) {
2306 TRACE("already at EOF, returning 0\n");
2307 return 0;
2308 }
2309 /* Don't trace small reads, it gets *very* annoying */
2310 if (count > 4)
2311 TRACE(":fd (%d) handle (%p) buf (%p) len (%d)\n", fd, fdinfo->handle, buf, count);
2312 if (fdinfo->handle == INVALID_HANDLE_VALUE)
2313 {
2314 *_errno() = EBADF;
2315 return -1;
2316 }
2317
2318 utf16 = (fdinfo->exflag & EF_UTF16) != 0;
2319 if (((fdinfo->exflag&EF_UTF8) || utf16) && count&1)
2320 {
2321 *_errno() = EINVAL;
2322 return -1;
2323 }
2324
2325 if((fdinfo->wxflag&WX_TEXT) && (fdinfo->exflag&EF_UTF8))
2326 return read_utf8(fdinfo, buf, count);
2327
2328 if (fdinfo->lookahead[0]!='\n' || ReadFile(fdinfo->handle, bufstart, count, &num_read, NULL))
2329 {
2330 if (fdinfo->lookahead[0] != '\n')
2331 {
2332 bufstart[0] = fdinfo->lookahead[0];
2333 fdinfo->lookahead[0] = '\n';
2334
2335 if (utf16)
2336 {
2337 bufstart[1] = fdinfo->lookahead[1];
2338 fdinfo->lookahead[1] = '\n';
2339 }
2340
2341 if(count>1+utf16 && ReadFile(fdinfo->handle, bufstart+1+utf16, count-1-utf16, &num_read, NULL))
2342 num_read += 1+utf16;
2343 else
2344 num_read = 1+utf16;
2345 }
2346
2347 if(utf16 && (num_read&1))
2348 {
2349 /* msvcr90 uses uninitialized value from the buffer in this case */
2350 /* msvcrt ignores additional data */
2351 ERR("got odd number of bytes in UTF16 mode\n");
2352 num_read--;
2353 }
2354
2355 if (count != 0 && num_read == 0)
2356 {
2357 fdinfo->wxflag |= WX_ATEOF;
2358 TRACE(":EOF %s\n",debugstr_an(buf,num_read));
2359 }
2360 else if (fdinfo->wxflag & WX_TEXT)
2361 {
2362 DWORD i, j;
2363
2364 if (bufstart[0]=='\n' && (!utf16 || bufstart[1]==0))
2365 fdinfo->wxflag |= WX_READNL;
2366 else
2367 fdinfo->wxflag &= ~WX_READNL;
2368
2369 for (i=0, j=0; i<num_read; i+=1+utf16)
2370 {
2371 /* in text mode, a ctrl-z signals EOF */
2372 if (bufstart[i]==0x1a && (!utf16 || bufstart[i+1]==0))
2373 {
2374 fdinfo->wxflag |= WX_ATEOF;
2375 TRACE(":^Z EOF %s\n",debugstr_an(buf,num_read));
2376 break;
2377 }
2378
2379 /* in text mode, strip \r if followed by \n */
2380 if (bufstart[i]=='\r' && (!utf16 || bufstart[i+1]==0) && i+1+utf16==num_read)
2381 {
2382 char lookahead[2];
2383 DWORD len;
2384
2385 lookahead[1] = '\n';
2386 if (ReadFile(fdinfo->handle, lookahead, 1+utf16, &len, NULL) && len)
2387 {
2388 if(lookahead[0]=='\n' && (!utf16 || lookahead[1]==0) && j==0)
2389 {
2390 bufstart[j++] = '\n';
2391 if(utf16) bufstart[j++] = 0;
2392 }
2393 else
2394 {
2395 if(lookahead[0]!='\n' || (utf16 && lookahead[1]!=0))
2396 {
2397 bufstart[j++] = '\r';
2398 if(utf16) bufstart[j++] = 0;
2399 }
2400
2401 if (fdinfo->wxflag & (WX_PIPE | WX_TTY))
2402 {
2403 if (lookahead[0]=='\n' && (!utf16 || !lookahead[1]))
2404 {
2405 bufstart[j++] = '\n';
2406 if (utf16) bufstart[j++] = 0;
2407 }
2408 else
2409 {
2410 fdinfo->lookahead[0] = lookahead[0];
2411 fdinfo->lookahead[1] = lookahead[1];
2412 }
2413 }
2414 else
2415 SetFilePointer(fdinfo->handle, -1-utf16, NULL, FILE_CURRENT);
2416 }
2417 }
2418 else
2419 {
2420 bufstart[j++] = '\r';
2421 if(utf16) bufstart[j++] = 0;
2422 }
2423 }
2424 else if((bufstart[i]!='\r' || (utf16 && bufstart[i+1]!=0))
2425 || (bufstart[i+1+utf16]!='\n' || (utf16 && bufstart[i+3]!=0)))
2426 {
2427 bufstart[j++] = bufstart[i];
2428 if(utf16) bufstart[j++] = bufstart[i+1];
2429 }
2430 }
2431 num_read = j;
2432 }
2433 }
2434 else
2435 {
2436 if (GetLastError() == ERROR_BROKEN_PIPE)
2437 {
2438 TRACE(":end-of-pipe\n");
2439 fdinfo->wxflag |= WX_ATEOF;
2440 return 0;
2441 }
2442 else
2443 {
2444 TRACE(":failed-last error (%d)\n",GetLastError());
2445 return -1;
2446 }
2447 }
2448
2449 if (count > 4)
2450 TRACE("(%u), %s\n",num_read,debugstr_an(buf, num_read));
2451 return num_read;
2452 }
2453
2454 /*********************************************************************
2455 * _read (MSVCRT.@)
2456 */
2457 int CDECL _read(int fd, void *buf, unsigned int count)
2458 {
2459 ioinfo *info = get_ioinfo(fd);
2460 int num_read = read_i(fd, info, buf, count);
2461 release_ioinfo(info);
2462 return num_read;
2463 }
2464
2465 /*********************************************************************
2466 * _setmode (MSVCRT.@)
2467 */
2468 int CDECL _setmode(int fd,int mode)
2469 {
2470 ioinfo *info = get_ioinfo(fd);
2471 int ret = info->wxflag & WX_TEXT ? _O_TEXT : _O_BINARY;
2472 if(ret==_O_TEXT && (info->exflag & (EF_UTF8|EF_UTF16)))
2473 ret = _O_WTEXT;
2474
2475 if(mode!=_O_TEXT && mode!=_O_BINARY && mode!=_O_WTEXT
2476 && mode!=_O_U16TEXT && mode!=_O_U8TEXT) {
2477 *_errno() = EINVAL;
2478 release_ioinfo(info);
2479 return -1;
2480 }
2481
2482 if(mode == _O_BINARY) {
2483 info->wxflag &= ~WX_TEXT;
2484 info->exflag &= ~(EF_UTF8|EF_UTF16);
2485 release_ioinfo(info);
2486 return ret;
2487 }
2488
2489 info->wxflag |= WX_TEXT;
2490 if(mode == _O_TEXT)
2491 info->exflag &= ~(EF_UTF8|EF_UTF16);
2492 else if(mode == _O_U8TEXT)
2493 info->exflag = (info->exflag & ~EF_UTF16) | EF_UTF8;
2494 else
2495 info->exflag = (info->exflag & ~EF_UTF8) | EF_UTF16;
2496
2497 release_ioinfo(info);
2498 return ret;
2499
2500 }
2501
2502 /*********************************************************************
2503 * _tell (MSVCRT.@)
2504 */
2505 long CDECL _tell(int fd)
2506 {
2507 return _lseek(fd, 0, SEEK_CUR);
2508 }
2509
2510 /*********************************************************************
2511 * _telli64 (MSVCRT.@)
2512 */
2513 __int64 CDECL _telli64(int fd)
2514 {
2515 return _lseeki64(fd, 0, SEEK_CUR);
2516 }
2517
2518 /*********************************************************************
2519 * _tempnam (MSVCRT.@)
2520 */
2521 char * CDECL _tempnam(const char *dir, const char *prefix)
2522 {
2523 char tmpbuf[MAX_PATH];
2524 const char *tmp_dir = getenv("TMP");
2525
2526 if (tmp_dir) dir = tmp_dir;
2527
2528 TRACE("dir (%s) prefix (%s)\n",dir,prefix);
2529 if (GetTempFileNameA(dir,prefix,0,tmpbuf))
2530 {
2531 TRACE("got name (%s)\n",tmpbuf);
2532 DeleteFileA(tmpbuf);
2533 return _strdup(tmpbuf);
2534 }
2535 TRACE("failed (%d)\n",GetLastError());
2536 return NULL;
2537 }
2538
2539 /*********************************************************************
2540 * _wtempnam (MSVCRT.@)
2541 */
2542 wchar_t * CDECL _wtempnam(const wchar_t *dir, const wchar_t *prefix)
2543 {
2544 wchar_t tmpbuf[MAX_PATH];
2545
2546 TRACE("dir (%s) prefix (%s)\n",debugstr_w(dir),debugstr_w(prefix));
2547 if (GetTempFileNameW(dir,prefix,0,tmpbuf))
2548 {
2549 TRACE("got name (%s)\n",debugstr_w(tmpbuf));
2550 DeleteFileW(tmpbuf);
2551 return _wcsdup(tmpbuf);
2552 }
2553 TRACE("failed (%d)\n",GetLastError());
2554 return NULL;
2555 }
2556
2557 /*********************************************************************
2558 * _umask (MSVCRT.@)
2559 */
2560 int CDECL _umask(int umask)
2561 {
2562 int old_umask = MSVCRT_umask;
2563 TRACE("(%d)\n",umask);
2564 MSVCRT_umask = umask;
2565 return old_umask;
2566 }
2567
2568 /*********************************************************************
2569 * _write (MSVCRT.@)
2570 */
2571 int CDECL _write(int fd, const void* buf, unsigned int count)
2572 {
2573 DWORD num_written;
2574 ioinfo *info = get_ioinfo(fd);
2575 HANDLE hand = info->handle;
2576
2577 /* Don't trace small writes, it gets *very* annoying */
2578 #if 0
2579 if (count > 32)
2580 TRACE(":fd (%d) handle (%d) buf (%p) len (%d)\n",fd,hand,buf,count);
2581 #endif
2582 if (hand == INVALID_HANDLE_VALUE)
2583 {
2584 *_errno() = EBADF;
2585 release_ioinfo(info);
2586 return -1;
2587 }
2588
2589 if (((info->exflag&EF_UTF8) || (info->exflag&EF_UTF16)) && count&1)
2590 {
2591 *_errno() = EINVAL;
2592 release_ioinfo(info);
2593 return -1;
2594 }
2595
2596 /* If appending, go to EOF */
2597 if (info->wxflag & WX_APPEND)
2598 _lseek(fd, 0, FILE_END);
2599
2600 if (!(info->wxflag & WX_TEXT))
2601 {
2602 if (WriteFile(hand, buf, count, &num_written, NULL)
2603 && (num_written == count))
2604 {
2605 release_ioinfo(info);
2606 return num_written;
2607 }
2608 TRACE("WriteFile (fd %d, hand %p) failed-last error (%d)\n", fd,
2609 hand, GetLastError());
2610 *_errno() = ENOSPC;
2611 }
2612 else
2613 {
2614 unsigned int i, j, nr_lf, size;
2615 char *p = NULL;
2616 const char *q;
2617 const char *s = buf, *buf_start = buf;
2618
2619 if (!(info->exflag & (EF_UTF8|EF_UTF16)))
2620 {
2621 /* find number of \n */
2622 for (nr_lf=0, i=0; i<count; i++)
2623 if (s[i] == '\n')
2624 nr_lf++;
2625 if (nr_lf)
2626 {
2627 size = count+nr_lf;
2628 if ((q = p = malloc(size)))
2629 {
2630 for (s = buf, i = 0, j = 0; i < count; i++)
2631 {
2632 if (s[i] == '\n')
2633 p[j++] = '\r';
2634 p[j++] = s[i];
2635 }
2636 }
2637 else
2638 {
2639 FIXME("Malloc failed\n");
2640 nr_lf = 0;
2641 size = count;
2642 q = buf;
2643 }
2644 }
2645 else
2646 {
2647 size = count;
2648 q = buf;
2649 }
2650 }
2651 else if (info->exflag & EF_UTF16)
2652 {
2653 for (nr_lf=0, i=0; i<count; i+=2)
2654 if (s[i]=='\n' && s[i+1]==0)
2655 nr_lf += 2;
2656 if (nr_lf)
2657 {
2658 size = count+nr_lf;
2659 if ((q = p = malloc(size)))
2660 {
2661 for (s=buf, i=0, j=0; i<count; i++)
2662 {
2663 if (s[i]=='\n' && s[i+1]==0)
2664 {
2665 p[j++] = '\r';
2666 p[j++] = 0;
2667 }
2668 p[j++] = s[i++];
2669 p[j++] = s[i];
2670 }
2671 }
2672 else
2673 {
2674 FIXME("Malloc failed\n");
2675 nr_lf = 0;
2676 size = count;
2677 q = buf;
2678 }
2679 }
2680 else
2681 {
2682 size = count;
2683 q = buf;
2684 }
2685 }
2686 else
2687 {
2688 DWORD conv_len;
2689
2690 for(nr_lf=0, i=0; i<count; i+=2)
2691 if (s[i]=='\n' && s[i+1]==0)
2692 nr_lf++;
2693
2694 conv_len = WideCharToMultiByte(CP_UTF8, 0, (WCHAR*)buf, count/2, NULL, 0, NULL, NULL);
2695 if(!conv_len) {
2696 _dosmaperr(GetLastError());
2697 free(p);
2698 release_ioinfo(info);
2699 return -1;
2700 }
2701
2702 size = conv_len+nr_lf;
2703 if((p = malloc(count+nr_lf*2+size)))
2704 {
2705 for (s=buf, i=0, j=0; i<count; i++)
2706 {
2707 if (s[i]=='\n' && s[i+1]==0)
2708 {
2709 p[j++] = '\r';
2710 p[j++] = 0;
2711 }
2712 p[j++] = s[i++];
2713 p[j++] = s[i];
2714 }
2715 q = p+count+nr_lf*2;
2716 WideCharToMultiByte(CP_UTF8, 0, (WCHAR*)p, count/2+nr_lf,
2717 p+count+nr_lf*2, conv_len+nr_lf, NULL, NULL);
2718 }
2719 else
2720 {
2721 FIXME("Malloc failed\n");
2722 nr_lf = 0;
2723 size = count;
2724 q = buf;
2725 }
2726 }
2727
2728 if (!WriteFile(hand, q, size, &num_written, NULL))
2729 num_written = -1;
2730 release_ioinfo(info);
2731 if(p)
2732 free(p);
2733 if (num_written != size)
2734 {
2735 TRACE("WriteFile (fd %d, hand %p) failed-last error (%d), num_written %d\n",
2736 fd, hand, GetLastError(), num_written);
2737 *_errno() = ENOSPC;
2738 return s - buf_start;
2739 }
2740 return count;
2741 }
2742
2743 release_ioinfo(info);
2744 return -1;
2745 }
2746
2747 /*********************************************************************
2748 * _putw (MSVCRT.@)
2749 */
2750 int CDECL _putw(int val, FILE* file)
2751 {
2752 int len;
2753
2754 _lock_file(file);
2755 len = _write(file->_file, &val, sizeof(val));
2756 if (len == sizeof(val)) {
2757 _unlock_file(file);
2758 return val;
2759 }
2760
2761 file->_flag |= _IOERR;
2762 _unlock_file(file);
2763 return EOF;
2764 }
2765
2766 /*********************************************************************
2767 * fclose (MSVCRT.@)
2768 */
2769 int CDECL fclose(FILE* file)
2770 {
2771 int r, flag;
2772
2773 _lock_file(file);
2774 flag = file->_flag;
2775 free(file->_tmpfname);
2776 file->_tmpfname = NULL;
2777 /* flush stdio buffers */
2778 if(file->_flag & _IOWRT)
2779 fflush(file);
2780 if(file->_flag & _IOMYBUF)
2781 free(file->_base);
2782
2783 r=_close(file->_file);
2784
2785 file->_flag = 0;
2786 _unlock_file(file);
2787 if(file<_iob || file>=_iob+_IOB_ENTRIES)
2788 DeleteCriticalSection(&((file_crit*)file)->crit);
2789
2790 if(file == msvcrt_get_file(MSVCRT_stream_idx-1)) {
2791 while(MSVCRT_stream_idx>3 && !file->_flag) {
2792 MSVCRT_stream_idx--;
2793 file = msvcrt_get_file(MSVCRT_stream_idx-1);
2794 }
2795 }
2796
2797 return ((r == -1) || (flag & _IOERR) ? EOF : 0);
2798 }
2799
2800 /*********************************************************************
2801 * feof (MSVCRT.@)
2802 */
2803 int CDECL feof(FILE* file)
2804 {
2805 return file->_flag & _IOEOF;
2806 }
2807
2808 /*********************************************************************
2809 * ferror (MSVCRT.@)
2810 */
2811 int CDECL ferror(FILE* file)
2812 {
2813 return file->_flag & _IOERR;
2814 }
2815
2816 /*********************************************************************
2817 * _filbuf (MSVCRT.@)
2818 */
2819 int CDECL _filbuf(FILE* file)
2820 {
2821 unsigned char c;
2822 _lock_file(file);
2823
2824 if(file->_flag & _IOSTRG) {
2825 _unlock_file(file);
2826 return EOF;
2827 }
2828
2829 /* Allocate buffer if needed */
2830 if(!(file->_flag & (_IONBF | _IOMYBUF | _USERBUF)))
2831 msvcrt_alloc_buffer(file);
2832
2833 if(!(file->_flag & _IOREAD)) {
2834 if(file->_flag & _IORW)
2835 file->_flag |= _IOREAD;
2836 else {
2837 _unlock_file(file);
2838 return EOF;
2839 }
2840 }
2841
2842 if(!(file->_flag & (_IOMYBUF | _USERBUF))) {
2843 int r;
2844 if ((r = _read(file->_file,&c,1)) != 1) {
2845 file->_flag |= (r == 0) ? _IOEOF : _IOERR;
2846 _unlock_file(file);
2847 return EOF;
2848 }
2849
2850 _unlock_file(file);
2851 return c;
2852 } else {
2853 file->_cnt = _read(file->_file, file->_base, file->_bufsiz);
2854 if(file->_cnt<=0) {
2855 file->_flag |= (file->_cnt == 0) ? _IOEOF : _IOERR;
2856 file->_cnt = 0;
2857 _unlock_file(file);
2858 return EOF;
2859 }
2860
2861 file->_cnt--;
2862 file->_ptr = file->_base+1;
2863 c = *(unsigned char *)file->_base;
2864 _unlock_file(file);
2865 return c;
2866 }
2867 }
2868
2869 /*********************************************************************
2870 * fgetc (MSVCRT.@)
2871 */
2872 int CDECL fgetc(FILE* file)
2873 {
2874 unsigned char *i;
2875 unsigned int j;
2876
2877 _lock_file(file);
2878 if (file->_cnt>0) {
2879 file->_cnt--;
2880 i = (unsigned char *)file->_ptr++;
2881 j = *i;
2882 } else
2883 j = _filbuf(file);
2884
2885 _unlock_file(file);
2886 return j;
2887 }
2888
2889 /*********************************************************************
2890 * _fgetchar (MSVCRT.@)
2891 */
2892 int CDECL _fgetchar(void)
2893 {
2894 return fgetc(stdin);
2895 }
2896
2897 /*********************************************************************
2898 * fgets (MSVCRT.@)
2899 */
2900 char * CDECL fgets(char *s, int size, FILE* file)
2901 {
2902 int cc = EOF;
2903 char * buf_start = s;
2904
2905 TRACE(":file(%p) fd (%d) str (%p) len (%d)\n",
2906 file,file->_file,s,size);
2907
2908 _lock_file(file);
2909
2910 while ((size >1) && (cc = fgetc(file)) != EOF && cc != '\n')
2911 {
2912 *s++ = (char)cc;
2913 size --;
2914 }
2915 if ((cc == EOF) && (s == buf_start)) /* If nothing read, return 0*/
2916 {
2917 TRACE(":nothing read\n");
2918 _unlock_file(file);
2919 return NULL;
2920 }
2921 if ((cc != EOF) && (size > 1))
2922 *s++ = cc;
2923 *s = '\0';
2924 TRACE(":got %s\n", debugstr_a(buf_start));
2925 _unlock_file(file);
2926 return buf_start;
2927 }
2928
2929 /*********************************************************************
2930 * fgetwc (MSVCRT.@)
2931 */
2932 wint_t CDECL fgetwc(FILE* file)
2933 {
2934 wint_t ret;
2935 int ch;
2936
2937 _lock_file(file);
2938
2939 if((get_ioinfo_nolock(file->_file)->exflag & (EF_UTF8 | EF_UTF16))
2940 || !(get_ioinfo_nolock(file->_file)->wxflag & WX_TEXT)) {
2941 char *p;
2942
2943 for(p=(char*)&ret; (wint_t*)p<&ret+1; p++) {
2944 ch = fgetc(file);
2945 if(ch == EOF) {
2946 ret = WEOF;
2947 break;
2948 }
2949 *p = (char)ch;
2950 }
2951 }else {
2952 char mbs[MB_LEN_MAX];
2953 int len = 0;
2954
2955 ch = fgetc(file);
2956 if(ch != EOF) {
2957 mbs[0] = (char)ch;
2958 if(isleadbyte((unsigned char)mbs[0])) {
2959 ch = fgetc(file);
2960 if(ch != EOF) {
2961 mbs[1] = (char)ch;
2962 len = 2;
2963 }
2964 }else {
2965 len = 1;
2966 }
2967 }
2968
2969 if(!len || mbtowc(&ret, mbs, len)==-1)
2970 ret = WEOF;
2971 }
2972
2973 _unlock_file(file);
2974 return ret;
2975 }
2976
2977 /*********************************************************************
2978 * _getw (MSVCRT.@)
2979 */
2980 int CDECL _getw(FILE* file)
2981 {
2982 char *ch;
2983 int i, k;
2984 unsigned int j;
2985 ch = (char *)&i;
2986
2987 _lock_file(file);
2988 for (j=0; j<sizeof(int); j++) {
2989 k = fgetc(file);
2990 if (k == EOF) {
2991 file->_flag |= _IOEOF;
2992 _unlock_file(file);
2993 return EOF;
2994 }
2995 ch[j] = k;
2996 }
2997
2998 _unlock_file(file);
2999 return i;
3000 }
3001
3002 /*********************************************************************
3003 * getwc (MSVCRT.@)
3004 */
3005 wint_t CDECL getwc(FILE* file)
3006 {
3007 return fgetwc(file);
3008 }
3009
3010 /*********************************************************************
3011 * _fgetwchar (MSVCRT.@)
3012 */
3013 wint_t CDECL _fgetwchar(void)
3014 {
3015 return fgetwc(stdin);
3016 }
3017
3018 /*********************************************************************
3019 * getwchar (MSVCRT.@)
3020 */
3021 wint_t CDECL getwchar(void)
3022 {
3023 return _fgetwchar();
3024 }
3025
3026 /*********************************************************************
3027 * fgetws (MSVCRT.@)
3028 */
3029 wchar_t * CDECL fgetws(wchar_t *s, int size, FILE* file)
3030 {
3031 int cc = WEOF;
3032 wchar_t * buf_start = s;
3033
3034 TRACE(":file(%p) fd (%d) str (%p) len (%d)\n",
3035 file,file->_file,s,size);
3036
3037 _lock_file(file);
3038
3039 while ((size >1) && (cc = fgetwc(file)) != WEOF && cc != '\n')
3040 {
3041 *s++ = (char)cc;
3042 size --;
3043 }
3044 if ((cc == WEOF) && (s == buf_start)) /* If nothing read, return 0*/
3045 {
3046 TRACE(":nothing read\n");
3047 _unlock_file(file);
3048 return NULL;
3049 }
3050 if ((cc != WEOF) && (size > 1))
3051 *s++ = cc;
3052 *s = 0;
3053 TRACE(":got %s\n", debugstr_w(buf_start));
3054 _unlock_file(file);
3055 return buf_start;
3056 }
3057
3058 /*********************************************************************
3059 * fwrite (MSVCRT.@)
3060 */
3061 size_t CDECL fwrite(const void *ptr, size_t size, size_t nmemb, FILE* file)
3062 {
3063 size_t wrcnt=size * nmemb;
3064 int written = 0;
3065 if (size == 0)
3066 return 0;
3067
3068 _lock_file(file);
3069
3070 while(wrcnt) {
3071 #ifndef __REACTOS__
3072 if(file->_cnt < 0) {
3073 WARN("negative file->_cnt value in %p\n", file);
3074 file->_flag |= MSVCRT__IOERR;
3075 break;
3076 } else
3077 #endif
3078 if(file->_cnt) {
3079 int pcnt=(file->_cnt>wrcnt)? wrcnt: file->_cnt;
3080 memcpy(file->_ptr, ptr, pcnt);
3081 file->_cnt -= pcnt;
3082 file->_ptr += pcnt;
3083 written += pcnt;
3084 wrcnt -= pcnt;
3085 ptr = (const char*)ptr + pcnt;
3086 } else if((file->_flag & _IONBF)
3087 || ((file->_flag & (_IOMYBUF | _USERBUF)) && wrcnt >= file->_bufsiz)
3088 || (!(file->_flag & (_IOMYBUF | _USERBUF)) && wrcnt >= MSVCRT_INTERNAL_BUFSIZ)) {
3089 size_t pcnt;
3090 int bufsiz;
3091
3092 if(file->_flag & _IONBF)
3093 bufsiz = 1;
3094 else if(!(file->_flag & (_IOMYBUF | _USERBUF)))
3095 bufsiz = MSVCRT_INTERNAL_BUFSIZ;
3096 else
3097 bufsiz = file->_bufsiz;
3098
3099 pcnt = (wrcnt / bufsiz) * bufsiz;
3100
3101 if(msvcrt_flush_buffer(file) == EOF)
3102 break;
3103
3104 if(_write(file->_file, ptr, pcnt) <= 0) {
3105 file->_flag |= _IOERR;
3106 break;
3107 }
3108 written += pcnt;
3109 wrcnt -= pcnt;
3110 ptr = (const char*)ptr + pcnt;
3111 } else {
3112 if(_flsbuf(*(const char*)ptr, file) == EOF)
3113 break;
3114 written++;
3115 wrcnt--;
3116 ptr = (const char*)ptr + 1;
3117 }
3118 }
3119
3120 _unlock_file(file);
3121 return written / size;
3122 }
3123
3124 /*********************************************************************
3125 * fputwc (MSVCRT.@)
3126 * FORKED for ReactOS, don't sync with Wine!
3127 * References:
3128 * - http://jira.reactos.org/browse/CORE-6495
3129 * - http://bugs.winehq.org/show_bug.cgi?id=8598
3130 */
3131 wint_t CDECL fputwc(wchar_t c, FILE* stream)
3132 {
3133 /* If this is a real file stream (and not some temporary one for
3134 sprintf-like functions), check whether it is opened in text mode.
3135 In this case, we have to perform an implicit conversion to ANSI. */
3136 if (!(stream->_flag & _IOSTRG) && get_ioinfo_nolock(stream->_file)->wxflag & WX_TEXT)
3137 {
3138 /* Convert to multibyte in text mode */
3139 char mbc[MB_LEN_MAX];
3140 int mb_return;
3141
3142 mb_return = wctomb(mbc, c);
3143
3144 if(mb_return == -1)
3145 return WEOF;
3146
3147 /* Output all characters */
3148 if (fwrite(mbc, mb_return, 1, stream) != 1)
3149 return WEOF;
3150 }
3151 else
3152 {
3153 if (fwrite(&c, sizeof(c), 1, stream) != 1)
3154 return WEOF;
3155 }
3156
3157 return c;
3158 }
3159
3160 /*********************************************************************
3161 * _fputwchar (MSVCRT.@)
3162 */
3163 wint_t CDECL _fputwchar(wint_t wc)
3164 {
3165 return fputwc(wc, stdout);
3166 }
3167
3168 /*********************************************************************
3169 * _wfsopen (MSVCRT.@)
3170 */
3171 FILE * CDECL _wfsopen(const wchar_t *path, const wchar_t *mode, int share)
3172 {
3173 FILE* file;
3174 int open_flags, stream_flags, fd;
3175
3176 TRACE("(%s,%s)\n", debugstr_w(path), debugstr_w(mode));
3177
3178 /* map mode string to open() flags. "man fopen" for possibilities. */
3179 if (msvcrt_get_flags(mode, &open_flags, &stream_flags) == -1)
3180 return NULL;
3181
3182 LOCK_FILES();
3183 fd = _wsopen(path, open_flags, share, _S_IREAD | _S_IWRITE);
3184 if (fd < 0)
3185 file = NULL;
3186 else if ((file = msvcrt_alloc_fp()) && msvcrt_init_fp(file, fd, stream_flags)
3187 != -1)
3188 TRACE(":fd (%d) mode (%s) FILE* (%p)\n", fd, debugstr_w(mode), file);
3189 else if (file)
3190 {
3191 file->_flag = 0;
3192 file = NULL;
3193 }
3194
3195 TRACE(":got (%p)\n",file);
3196 if (fd >= 0 && !file)
3197 _close(fd);
3198 UNLOCK_FILES();
3199 return file;
3200 }
3201
3202 /*********************************************************************
3203 * _fsopen (MSVCRT.@)
3204 */
3205 FILE * CDECL _fsopen(const char *path, const char *mode, int share)
3206 {
3207 FILE *ret;
3208 wchar_t *pathW = NULL, *modeW = NULL;
3209
3210 if (path && !(pathW = msvcrt_wstrdupa(path))) {
3211 _invalid_parameter(NULL, NULL, NULL, 0, 0);
3212 *_errno() = EINVAL;
3213 return NULL;
3214 }
3215 if (mode && !(modeW = msvcrt_wstrdupa(mode)))
3216 {
3217 free(pathW);
3218 _invalid_parameter(NULL, NULL, NULL, 0, 0);
3219 *_errno() = EINVAL;
3220 return NULL;
3221 }
3222
3223 ret = _wfsopen(pathW, modeW, share);
3224
3225 free(pathW);
3226 free(modeW);
3227 return ret;
3228 }
3229
3230 /*********************************************************************
3231 * fopen (MSVCRT.@)
3232 */
3233 FILE * CDECL fopen(const char *path, const char *mode)
3234 {
3235 return _fsopen( path, mode, _SH_DENYNO );
3236 }
3237
3238 /*********************************************************************
3239 * fopen_s (MSVCRT.@)
3240 */
3241 int CDECL fopen_s(FILE** pFile,
3242 const char *filename, const char *mode)
3243 {
3244 if (!MSVCRT_CHECK_PMT(pFile != NULL)) return EINVAL;
3245 if (!MSVCRT_CHECK_PMT