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