[CRT]
[reactos.git] / reactos / lib / sdk / crt / stdio / file.c
index 94f3668..4bb99da 100644 (file)
 int *__p__fmode(void);
 int *__p___mb_cur_max(void);
 
+extern int _commode;
+
+#ifndef _IOCOMMIT
+#define _IOCOMMIT 0x4000
+#endif
+
 #ifdef feof
 #undef feof
 #endif
@@ -95,11 +101,17 @@ int *__p___mb_cur_max(void);
 /* values for exflag - it's used differently in msvcr90.dll*/
 #define EF_UTF8           0x01
 #define EF_UTF16          0x02
+#define EF_UNK_UNICODE    0x08
+
+static char utf8_bom[3] = { 0xef, 0xbb, 0xbf };
+static char utf16_bom[2] = { 0xff, 0xfe };
 
 /* FIXME: this should be allocated dynamically */
 #define MAX_FILES 2048
 #define FD_BLOCK_SIZE 64
 
+#define MSVCRT_INTERNAL_BUFSIZ 4096
+
 /* ioinfo structure size is different in msvcrXX.dll's */
 typedef struct {
     HANDLE              handle;
@@ -146,6 +158,13 @@ static int tmpnam_unique;
  * protection, rather than locking the whole table for every change.
  */
 static CRITICAL_SECTION file_cs;
+static CRITICAL_SECTION_DEBUG file_cs_debug =
+{
+    0, 0, &file_cs,
+    { &file_cs_debug.ProcessLocksList, &file_cs_debug.ProcessLocksList },
+      0, 0, { (DWORD_PTR)(__FILE__ ": file_cs") }
+};
+static CRITICAL_SECTION file_cs = { &file_cs_debug, -1, 0, 0, 0, 0 };
 #define LOCK_FILES()    do { EnterCriticalSection(&file_cs); } while (0)
 #define UNLOCK_FILES()  do { LeaveCriticalSection(&file_cs); } while (0)
 
@@ -356,9 +375,11 @@ static int init_fp(FILE* file, int fd, unsigned stream_flags)
     *_errno() = EBADF;
     return -1;
   }
-  memset(file, 0, sizeof(*file));
+  file->_ptr = file->_base = NULL;
+  file->_cnt = 0;
   file->_file = fd;
   file->_flag = stream_flags;
+  file->_tmpfname = NULL;
 
   if(file<_iob || file>=_iob+_IOB_ENTRIES)
       InitializeCriticalSection(&((file_crit*)file)->crit);
@@ -419,8 +440,6 @@ void msvcrt_init_io(void)
   unsigned int  i;
   ioinfo        *fdinfo;
 
-  InitializeCriticalSection(&file_cs);
-  file_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": file_cs");
   GetStartupInfoA(&si);
   if (si.cbReserved2 >= sizeof(unsigned int) && si.lpReserved2 != NULL)
   {
@@ -446,53 +465,36 @@ void msvcrt_init_io(void)
         if (get_ioinfo(fdstart)->handle == INVALID_HANDLE_VALUE) break;
   }
 
-  if(!__pioinfo[0])
-      set_fd(INVALID_HANDLE_VALUE, 0, 3);
+  fdinfo = get_ioinfo(STDIN_FILENO);
+  if (!(fdinfo->wxflag & WX_OPEN) || fdinfo->handle == INVALID_HANDLE_VALUE) {
+    HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
+    DWORD type = GetFileType(h);
 
-  fdinfo = get_ioinfo(0);
-  if (!(fdinfo->wxflag & WX_OPEN) || fdinfo->handle == INVALID_HANDLE_VALUE)
-  {
-      HANDLE std = GetStdHandle(STD_INPUT_HANDLE);
-#ifndef __REACTOS__
-      if (std != INVALID_HANDLE_VALUE && DuplicateHandle(GetCurrentProcess(), std,
-                                                         GetCurrentProcess(), &fdinfo->handle,
-                                                         0, TRUE, DUPLICATE_SAME_ACCESS))
-#else
-          fdinfo->handle = std;
-#endif
-          fdinfo->wxflag = WX_OPEN | WX_TEXT;
+    set_fd(h, WX_OPEN|WX_TEXT|((type&0xf)==FILE_TYPE_CHAR ? WX_NOSEEK : 0)
+            |((type&0xf)==FILE_TYPE_PIPE ? WX_PIPE : 0), STDIN_FILENO);
   }
 
-  fdinfo = get_ioinfo(1);
-  if (!(fdinfo->wxflag & WX_OPEN) || fdinfo->handle == INVALID_HANDLE_VALUE)
-  {
-      HANDLE std = GetStdHandle(STD_OUTPUT_HANDLE);
-#ifndef __REACTOS__
-      if (std != INVALID_HANDLE_VALUE && DuplicateHandle(GetCurrentProcess(), std,
-                                                         GetCurrentProcess(), &fdinfo->handle,
-                                                         0, TRUE, DUPLICATE_SAME_ACCESS))
-#else
-          fdinfo->handle = std;
-#endif
-          fdinfo->wxflag = WX_OPEN | WX_TEXT;
+  fdinfo = get_ioinfo(STDOUT_FILENO);
+  if (!(fdinfo->wxflag & WX_OPEN) || fdinfo->handle == INVALID_HANDLE_VALUE) {
+    HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
+    DWORD type = GetFileType(h);
+
+    set_fd(h, WX_OPEN|WX_TEXT|((type&0xf)==FILE_TYPE_CHAR ? WX_NOSEEK : 0)
+            |((type&0xf)==FILE_TYPE_PIPE ? WX_PIPE : 0), STDOUT_FILENO);
   }
 
-  fdinfo = get_ioinfo(2);
-  if (!(fdinfo->wxflag & WX_OPEN) || fdinfo->handle == INVALID_HANDLE_VALUE)
-  {
-      HANDLE std = GetStdHandle(STD_ERROR_HANDLE);
-#ifndef __REACTOS__
-      if (std != INVALID_HANDLE_VALUE && DuplicateHandle(GetCurrentProcess(), std,
-                                                         GetCurrentProcess(), &fdinfo->handle,
-                                                         0, TRUE, DUPLICATE_SAME_ACCESS))
-#else
-          fdinfo->handle = std;
-#endif
-          fdinfo->wxflag = WX_OPEN | WX_TEXT;
+  fdinfo = get_ioinfo(STDERR_FILENO);
+  if (!(fdinfo->wxflag & WX_OPEN) || fdinfo->handle == INVALID_HANDLE_VALUE) {
+    HANDLE h = GetStdHandle(STD_ERROR_HANDLE);
+    DWORD type = GetFileType(h);
+
+    set_fd(h, WX_OPEN|WX_TEXT|((type&0xf)==FILE_TYPE_CHAR ? WX_NOSEEK : 0)
+            |((type&0xf)==FILE_TYPE_PIPE ? WX_PIPE : 0), STDERR_FILENO);
   }
 
-  TRACE(":handles (%p)(%p)(%p)\n", get_ioinfo(0)->handle,
-       get_ioinfo(1)->handle, get_ioinfo(2)->handle);
+  TRACE(":handles (%p)(%p)(%p)\n", get_ioinfo(STDIN_FILENO)->handle,
+        get_ioinfo(STDOUT_FILENO)->handle,
+        get_ioinfo(STDERR_FILENO)->handle);
 
   memset(_iob,0,3*sizeof(FILE));
   for (i = 0; i < 3; i++)
@@ -508,32 +510,74 @@ void msvcrt_init_io(void)
 /* INTERNAL: Flush stdio file buffer */
 static int flush_buffer(FILE* file)
 {
-  if(file->_bufsiz) {
+  if(file->_flag & (_IOMYBUF | _USERBUF)) {
         int cnt=file->_ptr-file->_base;
         if(cnt>0 && _write(file->_file, file->_base, cnt) != cnt) {
             file->_flag |= _IOERR;
             return EOF;
         }
         file->_ptr=file->_base;
-        file->_cnt=file->_bufsiz;
+        file->_cnt=0;
   }
   return 0;
 }
 
+/*********************************************************************
+ *             _isatty (MSVCRT.@)
+ */
+int CDECL _isatty(int fd)
+{
+    HANDLE hand = fdtoh(fd);
+
+    TRACE(":fd (%d) handle (%p)\n",fd,hand);
+    if (hand == INVALID_HANDLE_VALUE)
+        return 0;
+
+    return GetFileType(hand) == FILE_TYPE_CHAR? 1 : 0;
+}
+
 /* INTERNAL: Allocate stdio file buffer */
-/*static*/ void alloc_buffer(FILE* file)
+/*static*/ BOOL alloc_buffer(FILE* file)
 {
-       file->_base = calloc(BUFSIZ,1);
-       if(file->_base) {
-               file->_bufsiz = BUFSIZ;
-               file->_flag |= _IOMYBUF;
-       } else {
-               file->_base = (char*)(&file->_charbuf);
-               /* put here 2 ??? */
-               file->_bufsiz = sizeof(file->_charbuf);
-       }
-       file->_ptr = file->_base;
-       file->_cnt = 0;
+    if((file->_file==STDOUT_FILENO || file->_file==STDERR_FILENO)
+            && _isatty(file->_file))
+        return FALSE;
+
+    file->_base = calloc(MSVCRT_INTERNAL_BUFSIZ,1);
+    if(file->_base) {
+        file->_bufsiz = MSVCRT_INTERNAL_BUFSIZ;
+        file->_flag |= _IOMYBUF;
+    } else {
+        file->_base = (char*)(&file->_charbuf);
+        file->_bufsiz = 2;
+        file->_flag |= _IONBF;
+    }
+    file->_ptr = file->_base;
+    file->_cnt = 0;
+    return TRUE;
+}
+
+/* INTERNAL: Allocate temporary buffer for stdout and stderr */
+static BOOL add_std_buffer(FILE *file)
+{
+    static char buffers[2][BUFSIZ];
+
+    if((file->_file!=STDOUT_FILENO && file->_file!=STDERR_FILENO)
+            || !_isatty(file->_file) || file->_bufsiz)
+        return FALSE;
+
+    file->_ptr = file->_base = buffers[file->_file == STDOUT_FILENO ? 0 : 1];
+    file->_bufsiz = file->_cnt = BUFSIZ;
+    return TRUE;
+}
+
+/* INTERNAL: Removes temporary buffer from stdout or stderr */
+/* Only call this function when add_std_buffer returned TRUE */
+static void remove_std_buffer(FILE *file)
+{
+    flush_buffer(file);
+    file->_ptr = file->_base = NULL;
+    file->_bufsiz = file->_cnt = 0;
 }
 
 /* INTERNAL: Convert integer to base32 string (0-9a-v), 0 becomes "" */
@@ -753,13 +797,13 @@ static int flush_all_buffers(int mask)
   FILE *file;
 
   LOCK_FILES();
-  for (i = 3; i < stream_idx; i++) {
+  for (i = 0; i < stream_idx; i++) {
     file = get_file(i);
 
     if (file->_flag)
     {
       if(file->_flag & mask) {
-        fflush(file);
+       fflush(file);
         num_flushed++;
       }
     }
@@ -988,24 +1032,28 @@ int CDECL _fcloseall(void)
 /* free everything on process exit */
 void msvcrt_free_io(void)
 {
-    int i;
+    unsigned int i;
+    int j;
 
+    _flushall();
     _fcloseall();
-    /* The Win32 _fcloseall() function explicitly doesn't close stdin,
-     * stdout, and stderr (unlike GNU), so we need to fclose() them here
-     * or they won't get flushed.
-     */
-    fclose(&_iob[0]);
-    fclose(&_iob[1]);
-    fclose(&_iob[2]);
 
     for(i=0; i<sizeof(__pioinfo)/sizeof(__pioinfo[0]); i++)
         free(__pioinfo[i]);
 
+    for(j=0; j<stream_idx; j++)
+    {
+        FILE *file = get_file(j);
+        if(file<_iob || file>=_iob+_IOB_ENTRIES)
+        {
+            ((file_crit*)file)->crit.DebugInfo->Spare[0] = 0;
+            DeleteCriticalSection(&((file_crit*)file)->crit);
+        }
+    }
+
     for(i=0; i<sizeof(fstream)/sizeof(fstream[0]); i++)
         free(fstream[i]);
 
-    file_cs.DebugInfo->Spare[0] = 0;
     DeleteCriticalSection(&file_cs);
 }
 
@@ -1149,19 +1197,10 @@ int CDECL _fseeki64(FILE* file, __int64 offset, int whence)
        flush_buffer(file);
 
   if(whence == SEEK_CUR && file->_flag & _IOREAD ) {
-       offset -= file->_cnt;
-       if (get_ioinfo(file->_file)->wxflag & WX_TEXT) {
-               /* Black magic correction for CR removal */
-               int i;
-               for (i=0; i<file->_cnt; i++) {
-                       if (file->_ptr[i] == '\n')
-                               offset--;
-               }
-               /* Black magic when reading CR at buffer boundary*/
-               if(get_ioinfo(file->_file)->wxflag & WX_READCR)
-                   offset--;
-       }
+      whence = SEEK_SET;
+      offset += _ftelli64(file);
   }
+
   /* Discard buffered input */
   file->_cnt = 0;
   file->_ptr = file->_base;
@@ -1186,15 +1225,17 @@ int CDECL fseek(FILE* file, long offset, int whence)
 }
 
 /*********************************************************************
- *             _chsize (MSVCRT.@)
+ *             _chsize_s (MSVCRT.@)
  */
-int CDECL _chsize(int fd, long size)
+int CDECL _chsize_s(int fd, __int64 size)
 {
-    LONG cur, pos;
+    __int64 cur, pos;
     HANDLE handle;
     BOOL ret = FALSE;
 
-    TRACE("(fd=%d, size=%d)\n", fd, size);
+    TRACE("(fd=%d, size=%s)\n", fd, wine_dbgstr_longlong(size));
+
+    if (!MSVCRT_CHECK_PMT(size >= 0)) return EINVAL;
 
     LOCK_FILES();
 
@@ -1202,10 +1243,10 @@ int CDECL _chsize(int fd, long size)
     if (handle != INVALID_HANDLE_VALUE)
     {
         /* save the current file pointer */
-        cur = _lseek(fd, 0, SEEK_CUR);
+        cur = _lseeki64(fd, 0, SEEK_CUR);
         if (cur >= 0)
         {
-            pos = _lseek(fd, size, SEEK_SET);
+            pos = _lseeki64(fd, size, SEEK_SET);
             if (pos >= 0)
             {
                 ret = SetEndOfFile(handle);
@@ -1213,12 +1254,21 @@ int CDECL _chsize(int fd, long size)
             }
 
             /* restore the file pointer */
-            _lseek(fd, cur, SEEK_SET);
+            _lseeki64(fd, cur, SEEK_SET);
         }
     }
 
     UNLOCK_FILES();
-    return ret ? 0 : -1;
+    return ret ? 0 : *_errno();
+}
+
+/*********************************************************************
+ *             _chsize (MSVCRT.@)
+ */
+int CDECL _chsize(int fd, long size)
+{
+    /* _chsize_s returns errno on failure but _chsize should return -1 */
+    return _chsize_s( fd, size ) == 0 ? 0 : -1;
 }
 
 /*********************************************************************
@@ -1250,6 +1300,10 @@ static int get_flags(const wchar_t* mode, int *open_flags, int* stream_flags)
 {
   int plus = strchrW(mode, '+') != NULL;
 
+  TRACE("%s\n", debugstr_w(mode));
+
+  while(*mode == ' ') mode++;
+
   switch(*mode++)
   {
   case 'R': case 'r':
@@ -1265,12 +1319,13 @@ static int get_flags(const wchar_t* mode, int *open_flags, int* stream_flags)
     *stream_flags = plus ? _IORW : _IOWRT;
     break;
   default:
-    _invalid_parameter(NULL, NULL, NULL, 0, 0);
-    *_errno() = EINVAL;
+    MSVCRT_INVALID_PMT(0, EINVAL);
     return -1;
   }
 
-  while (*mode)
+  *stream_flags |= _commode;
+
+  while (*mode && *mode!=',')
     switch (*mode++)
     {
     case 'B': case 'b':
@@ -1281,12 +1336,79 @@ static int get_flags(const wchar_t* mode, int *open_flags, int* stream_flags)
       *open_flags |=  _O_TEXT;
       *open_flags &= ~_O_BINARY;
       break;
+    case 'D':
+      *open_flags |= _O_TEMPORARY;
+      break;
+    case 'T':
+      *open_flags |= _O_SHORT_LIVED;
+      break;
+    case 'c':
+      *stream_flags |= _IOCOMMIT;
+      break;
+    case 'n':
+      *stream_flags &= ~_IOCOMMIT;
+      break;
+    case 'N':
+      *open_flags |= _O_NOINHERIT;
+      break;
     case '+':
     case ' ':
+    case 'a':
+    case 'w':
+      break;
+    case 'S':
+    case 'R':
+      FIXME("ignoring cache optimization flag: %c\n", mode[-1]);
       break;
     default:
-      FIXME(":unknown flag %c not supported\n",mode[-1]);
+      ERR("incorrect mode flag: %c\n", mode[-1]);
+      break;
+    }
+
+  if(*mode == ',')
+  {
+    static const WCHAR ccs[] = {'c','c','s'};
+    static const WCHAR utf8[] = {'u','t','f','-','8'};
+    static const WCHAR utf16le[] = {'u','t','f','-','1','6','l','e'};
+    static const WCHAR unicode[] = {'u','n','i','c','o','d','e'};
+
+    mode++;
+    while(*mode == ' ') mode++;
+    if(!MSVCRT_CHECK_PMT(!strncmpW(ccs, mode, sizeof(ccs)/sizeof(ccs[0]))))
+      return -1;
+    mode += sizeof(ccs)/sizeof(ccs[0]);
+    while(*mode == ' ') mode++;
+    if(!MSVCRT_CHECK_PMT(*mode == '='))
+        return -1;
+    mode++;
+    while(*mode == ' ') mode++;
+
+    if(!strncmpiW(utf8, mode, sizeof(utf8)/sizeof(utf8[0])))
+    {
+      *open_flags |= _O_U8TEXT;
+      mode += sizeof(utf8)/sizeof(utf8[0]);
+    }
+    else if(!strncmpiW(utf16le, mode, sizeof(utf16le)/sizeof(utf16le[0])))
+    {
+      *open_flags |= _O_U16TEXT;
+      mode += sizeof(utf16le)/sizeof(utf16le[0]);
+    }
+    else if(!strncmpiW(unicode, mode, sizeof(unicode)/sizeof(unicode[0])))
+    {
+      *open_flags |= _O_WTEXT;
+      mode += sizeof(unicode)/sizeof(unicode[0]);
+    }
+    else
+    {
+      MSVCRT_INVALID_PMT(0, EINVAL);
+      return -1;
     }
+
+    while(*mode == ' ') mode++;
+  }
+
+  if(!MSVCRT_CHECK_PMT(*mode == 0))
+    return -1;
   return 0;
 }
 
@@ -1388,20 +1510,6 @@ intptr_t CDECL _get_osfhandle(int fd)
   return (intptr_t)hand;
 }
 
-/*********************************************************************
- *             _isatty (MSVCRT.@)
- */
-int CDECL _isatty(int fd)
-{
-  HANDLE hand = fdtoh(fd);
-
-  TRACE(":fd (%d) handle (%p)\n",fd,hand);
-  if (hand == INVALID_HANDLE_VALUE)
-    return 0;
-
-  return GetFileType(hand) == FILE_TYPE_CHAR? 1 : 0;
-}
-
 /*********************************************************************
  *             _mktemp (MSVCRT.@)
  */
@@ -1480,6 +1588,9 @@ wchar_t * CDECL _wmktemp(wchar_t *pattern)
     if (oflags & _O_APPEND)              wxflags |= WX_APPEND;
     if (oflags & _O_BINARY)              {/* Nothing to do */}
     else if (oflags & _O_TEXT)           wxflags |= WX_TEXT;
+    else if (oflags & _O_WTEXT)          wxflags |= WX_TEXT;
+    else if (oflags & _O_U16TEXT)        wxflags |= WX_TEXT;
+    else if (oflags & _O_U8TEXT)         wxflags |= WX_TEXT;
     else if (*__p__fmode() & _O_BINARY)  {/* Nothing to do */}
     else                                        wxflags |= WX_TEXT; /* default to TEXT*/
     if (oflags & _O_NOINHERIT)           wxflags |= WX_DONTINHERIT;
@@ -1489,7 +1600,8 @@ wchar_t * CDECL _wmktemp(wchar_t *pattern)
                     _O_TRUNC|_O_EXCL|_O_CREAT|
                     _O_RDWR|_O_WRONLY|_O_TEMPORARY|
                     _O_NOINHERIT|
-                    _O_SEQUENTIAL|_O_RANDOM|_O_SHORT_LIVED
+                    _O_SEQUENTIAL|_O_RANDOM|_O_SHORT_LIVED|
+                    _O_WTEXT|_O_U16TEXT|_O_U8TEXT
                     )))
         ERR(":unsupported oflags 0x%04x\n",unsupp);
 
@@ -1551,25 +1663,44 @@ int CDECL _pipe(int *pfds, unsigned int psize, int textmode)
   return ret;
 }
 
+static int check_bom(HANDLE h, int oflags, BOOL seek)
+{
+    char bom[sizeof(utf8_bom)];
+    DWORD r;
+
+    oflags &= ~(_O_WTEXT|_O_U16TEXT|_O_U8TEXT);
+
+    if (!ReadFile(h, bom, sizeof(utf8_bom), &r, NULL))
+        return oflags;
+
+    if (r==sizeof(utf8_bom) && !memcmp(bom, utf8_bom, sizeof(utf8_bom))) {
+        oflags |= _O_U8TEXT;
+    }else if (r>=sizeof(utf16_bom) && !memcmp(bom, utf16_bom, sizeof(utf16_bom))) {
+        if (seek && r>2)
+            SetFilePointer(h, 2, NULL, FILE_BEGIN);
+        oflags |= _O_U16TEXT;
+    }else if (seek) {
+        SetFilePointer(h, 0, NULL, FILE_BEGIN);
+    }
+
+    return oflags;
+}
+
 /*********************************************************************
- *              _sopen_s (MSVCRT.@)
+ *              _wsopen_s (MSVCRT.@)
  */
-int CDECL _sopen_s( int *fd, const char *path, int oflags, int shflags, int pmode )
+int CDECL _wsopen_s( int *fd, const wchar_t* path, int oflags, int shflags, int pmode )
 {
   DWORD access = 0, creation = 0, attrib;
+  SECURITY_ATTRIBUTES sa;
   DWORD sharing;
   int wxflag;
   HANDLE hand;
-  SECURITY_ATTRIBUTES sa;
 
-  TRACE("fd*: %p file: (%s) oflags: 0x%04x shflags: 0x%04x pmode: 0x%04x\n",
-        fd, path, oflags, shflags, pmode);
+  TRACE("fd*: %p :file (%s) oflags: 0x%04x shflags: 0x%04x pmode: 0x%04x\n",
+        fd, debugstr_w(path), oflags, shflags, pmode);
 
-  if (!fd)
-  {
-    MSVCRT_INVALID_PMT("null out fd pointer", EINVAL);
-    return EINVAL;
-  }
+  if (!MSVCRT_CHECK_PMT( fd != NULL )) return EINVAL;
 
   *fd = -1;
   wxflag = split_oflags(oflags);
@@ -1631,144 +1762,128 @@ int CDECL _sopen_s( int *fd, const char *path, int oflags, int shflags, int pmod
 
   sa.nLength              = sizeof( SECURITY_ATTRIBUTES );
   sa.lpSecurityDescriptor = NULL;
-  sa.bInheritHandle       = (oflags & _O_NOINHERIT) ? FALSE : TRUE;
+  sa.bInheritHandle       = !(oflags & _O_NOINHERIT);
+
+  if ((oflags&(_O_WTEXT|_O_U16TEXT|_O_U8TEXT))
+          && (creation==OPEN_ALWAYS || creation==OPEN_EXISTING)
+          && !(access&GENERIC_READ))
+  {
+      hand = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
+              &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+      if (hand != INVALID_HANDLE_VALUE)
+      {
+          oflags = check_bom(hand, oflags, FALSE);
+          CloseHandle(hand);
+      }
+      else
+          oflags &= ~(_O_WTEXT|_O_U16TEXT|_O_U8TEXT);
+  }
 
-  hand = CreateFileA(path, access, sharing, &sa, creation, attrib, 0);
+  hand = CreateFileW(path, access, sharing, &sa, creation, attrib, 0);
   if (hand == INVALID_HANDLE_VALUE)  {
-    WARN(":failed-last error (%d)\n", GetLastError());
+    WARN(":failed-last error (%d)\n",GetLastError());
     _dosmaperr(GetLastError());
     return *_errno();
   }
 
+  if (oflags & (_O_WTEXT|_O_U16TEXT|_O_U8TEXT))
+  {
+      if ((access & GENERIC_WRITE) && (creation==CREATE_NEW
+                  || creation==CREATE_ALWAYS || creation==TRUNCATE_EXISTING
+                  || (creation==OPEN_ALWAYS && GetLastError()==ERROR_ALREADY_EXISTS)))
+      {
+          if (oflags & _O_U8TEXT)
+          {
+              DWORD written = 0, tmp;
+
+              while(written!=sizeof(utf8_bom) && WriteFile(hand, (char*)utf8_bom+written,
+                          sizeof(utf8_bom)-written, &tmp, NULL))
+                  written += tmp;
+              if (written != sizeof(utf8_bom)) {
+                  WARN("error writing BOM\n");
+                  CloseHandle(hand);
+                  _dosmaperr(GetLastError());
+                  return *_errno();
+              }
+          }
+          else
+          {
+              DWORD written = 0, tmp;
+
+              while(written!=sizeof(utf16_bom) && WriteFile(hand, (char*)utf16_bom+written,
+                          sizeof(utf16_bom)-written, &tmp, NULL))
+                  written += tmp;
+              if (written != sizeof(utf16_bom))
+              {
+                  WARN("error writing BOM\n");
+                  CloseHandle(hand);
+                  _dosmaperr(GetLastError());
+                  return *_errno();
+              }
+          }
+      }
+      else if (access & GENERIC_READ)
+          oflags = check_bom(hand, oflags, TRUE);
+  }
+
   *fd = alloc_fd(hand, wxflag);
+  if (*fd == -1)
+      return *_errno();
+
+  if (oflags & _O_WTEXT)
+      get_ioinfo(*fd)->exflag |= EF_UTF16|EF_UNK_UNICODE;
+  else if (oflags & _O_U16TEXT)
+      get_ioinfo(*fd)->exflag |= EF_UTF16;
+  else if (oflags & _O_U8TEXT)
+      get_ioinfo(*fd)->exflag |= EF_UTF8;
 
   TRACE(":fd (%d) handle (%p)\n", *fd, hand);
   return 0;
 }
 
 /*********************************************************************
- *              _sopen (MSVCRT.@)
+ *              _wsopen (MSVCRT.@)
  */
-int CDECL _sopen( const char *path, int oflags, int shflags, ... )
+int CDECL _wsopen( const wchar_t *path, int oflags, int shflags, ... )
 {
   int pmode;
   int fd;
 
   if (oflags & _O_CREAT)
   {
-    va_list ap;
+    __ms_va_list ap;
 
-    va_start(ap, shflags);
+    __ms_va_start(ap, shflags);
     pmode = va_arg(ap, int);
-    va_end(ap);
+    __ms_va_end(ap);
   }
   else
     pmode = 0;
 
-  _sopen_s(&fd, path, oflags, shflags, pmode);
+  _wsopen_s(&fd, path, oflags, shflags, pmode);
   return fd;
 }
 
 /*********************************************************************
- *              _wsopen_s (MSVCRT.@)
+ *              _sopen_s (MSVCRT.@)
  */
-int CDECL _wsopen_s( int *fd, const wchar_t* path, int oflags, int shflags, int pmode )
+int CDECL _sopen_s( int *fd, const char *path, int oflags, int shflags, int pmode )
 {
-  DWORD access = 0, creation = 0, attrib;
-  SECURITY_ATTRIBUTES sa;
-  DWORD sharing;
-  int wxflag;
-  HANDLE hand;
-
-  TRACE("fd*: %p :file (%s) oflags: 0x%04x shflags: 0x%04x pmode: 0x%04x\n",
-        fd, debugstr_w(path), oflags, shflags, pmode);
-
-  if (!fd)
-  {
-    MSVCRT_INVALID_PMT("null out fd pointer", EINVAL);
-    return EINVAL;
-  }
-
-  *fd = -1;
-  wxflag = split_oflags(oflags);
-  switch (oflags & (_O_RDONLY | _O_WRONLY | _O_RDWR))
-  {
-  case _O_RDONLY: access |= GENERIC_READ; break;
-  case _O_WRONLY: access |= GENERIC_WRITE; break;
-  case _O_RDWR:   access |= GENERIC_WRITE | GENERIC_READ; break;
-  }
-
-  if (oflags & _O_CREAT)
-  {
-    if(pmode & ~(_S_IREAD | _S_IWRITE))
-      FIXME(": pmode 0x%04x ignored\n", pmode);
-    else
-      WARN(": pmode 0x%04x ignored\n", pmode);
-
-    if (oflags & _O_EXCL)
-      creation = CREATE_NEW;
-    else if (oflags & _O_TRUNC)
-      creation = CREATE_ALWAYS;
-    else
-      creation = OPEN_ALWAYS;
-  }
-  else  /* no _O_CREAT */
-  {
-    if (oflags & _O_TRUNC)
-      creation = TRUNCATE_EXISTING;
-    else
-      creation = OPEN_EXISTING;
-  }
-
-  switch( shflags )
-  {
-    case _SH_DENYRW:
-      sharing = 0L;
-      break;
-    case _SH_DENYWR:
-      sharing = FILE_SHARE_READ;
-      break;
-    case _SH_DENYRD:
-      sharing = FILE_SHARE_WRITE;
-      break;
-    case _SH_DENYNO:
-      sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
-      break;
-    default:
-      ERR( "Unhandled shflags 0x%x\n", shflags );
-      return EINVAL;
-  }
-  attrib = FILE_ATTRIBUTE_NORMAL;
-
-  if (oflags & _O_TEMPORARY)
-  {
-      attrib |= FILE_FLAG_DELETE_ON_CLOSE;
-      access |= DELETE;
-      sharing |= FILE_SHARE_DELETE;
-  }
-
-  sa.nLength              = sizeof( SECURITY_ATTRIBUTES );
-  sa.lpSecurityDescriptor = NULL;
-  sa.bInheritHandle       = (oflags & _O_NOINHERIT) ? FALSE : TRUE;
-
-  hand = CreateFileW(path, access, sharing, &sa, creation, attrib, 0);
-
-  if (hand == INVALID_HANDLE_VALUE)  {
-    WARN(":failed-last error (%d)\n",GetLastError());
-    _dosmaperr(GetLastError());
-    return *_errno();
-  }
+    wchar_t *pathW;
+    int ret;
 
-  *fd = alloc_fd(hand, wxflag);
+    if(!MSVCRT_CHECK_PMT(path && (pathW = msvcrt_wstrdupa(path))))
+        return EINVAL;
 
-  TRACE(":fd (%d) handle (%p)\n", *fd, hand);
-  return 0;
+    ret = _wsopen_s(fd, pathW, oflags, shflags, pmode);
+    free(pathW);
+    return ret;
 }
 
 /*********************************************************************
- *              _wsopen (MSVCRT.@)
+ *              _sopen (MSVCRT.@)
  */
-int CDECL _wsopen( const wchar_t *path, int oflags, int shflags, ... )
+int CDECL _sopen( const char *path, int oflags, int shflags, ... )
 {
   int pmode;
   int fd;
@@ -1784,7 +1899,7 @@ int CDECL _wsopen( const wchar_t *path, int oflags, int shflags, ... )
   else
     pmode = 0;
 
-  _wsopen_s(&fd, path, oflags, shflags, pmode);
+  _sopen_s(&fd, path, oflags, shflags, pmode);
   return fd;
 }
 
@@ -1906,6 +2021,190 @@ int CDECL _rmtmp(void)
   return num_removed;
 }
 
+static inline int get_utf8_char_len(char ch)
+{
+    if((ch&0xf8) == 0xf0)
+        return 4;
+    else if((ch&0xf0) == 0xe0)
+        return 3;
+    else if((ch&0xe0) == 0xc0)
+        return 2;
+    return 1;
+}
+
+/*********************************************************************
+ * (internal) read_utf8
+ */
+static int read_utf8(int fd, wchar_t *buf, unsigned int count)
+{
+    ioinfo *fdinfo = get_ioinfo(fd);
+    HANDLE hand = fdinfo->handle;
+    char min_buf[4], *readbuf, lookahead;
+    DWORD readbuf_size, pos=0, num_read=1, char_len, i, j;
+
+    /* make the buffer big enough to hold at least one character */
+    /* read bytes have to fit to output and lookahead buffers */
+    count /= 2;
+    readbuf_size = count < 4 ? 4 : count;
+    if(readbuf_size<=4 || !(readbuf = malloc(readbuf_size))) {
+        readbuf_size = 4;
+        readbuf = min_buf;
+    }
+
+    if(fdinfo->lookahead[0] != '\n') {
+        readbuf[pos++] = fdinfo->lookahead[0];
+        fdinfo->lookahead[0] = '\n';
+
+        if(fdinfo->lookahead[1] != '\n') {
+            readbuf[pos++] = fdinfo->lookahead[1];
+            fdinfo->lookahead[1] = '\n';
+
+            if(fdinfo->lookahead[2] != '\n') {
+                readbuf[pos++] = fdinfo->lookahead[2];
+                fdinfo->lookahead[2] = '\n';
+            }
+        }
+    }
+
+    /* NOTE: this case is broken in native dll, reading
+     *        sometimes fails when small buffer is passed
+     */
+    if(count < 4) {
+        if(!pos && !ReadFile(hand, readbuf, 1, &num_read, NULL)) {
+            if (GetLastError() == ERROR_BROKEN_PIPE) {
+                fdinfo->wxflag |= WX_ATEOF;
+                return 0;
+            }else {
+                _dosmaperr(GetLastError());
+                return -1;
+            }
+        }else if(!num_read) {
+            fdinfo->wxflag |= WX_ATEOF;
+            return 0;
+        }else {
+            pos++;
+        }
+
+        char_len = get_utf8_char_len(readbuf[0]);
+        if(char_len>pos) {
+            if(ReadFile(hand, readbuf+pos, char_len-pos, &num_read, NULL))
+                pos += num_read;
+        }
+
+        if(readbuf[0] == '\n')
+            fdinfo->wxflag |= WX_READNL;
+        else
+            fdinfo->wxflag &= ~WX_READNL;
+
+        if(readbuf[0] == 0x1a) {
+            fdinfo->wxflag |= WX_ATEOF;
+            return 0;
+        }
+
+        if(readbuf[0] == '\r') {
+            if(!ReadFile(hand, &lookahead, 1, &num_read, NULL) || num_read!=1)
+                buf[0] = '\r';
+            else if(lookahead == '\n')
+                buf[0] = '\n';
+            else {
+                buf[0] = '\r';
+                if(fdinfo->wxflag & (WX_PIPE | WX_NOSEEK))
+                    fdinfo->lookahead[0] = lookahead;
+                else
+                    SetFilePointer(fdinfo->handle, -1, NULL, FILE_CURRENT);
+            }
+            return 2;
+        }
+
+        if(!(num_read = MultiByteToWideChar(CP_UTF8, 0, readbuf, pos, buf, count))) {
+            _dosmaperr(GetLastError());
+            return -1;
+        }
+
+        return num_read*2;
+    }
+
+    if(!ReadFile(hand, readbuf+pos, readbuf_size-pos, &num_read, NULL)) {
+        if(pos) {
+            num_read = 0;
+        }else if(GetLastError() == ERROR_BROKEN_PIPE) {
+            fdinfo->wxflag |= WX_ATEOF;
+            if (readbuf != min_buf) free(readbuf);
+            return 0;
+        }else {
+            _dosmaperr(GetLastError());
+            if (readbuf != min_buf) free(readbuf);
+            return -1;
+        }
+    }else if(!pos && !num_read) {
+        fdinfo->wxflag |= WX_ATEOF;
+        if (readbuf != min_buf) free(readbuf);
+        return 0;
+    }
+
+    pos += num_read;
+    if(readbuf[0] == '\n')
+        fdinfo->wxflag |= WX_READNL;
+    else
+        fdinfo->wxflag &= ~WX_READNL;
+
+    /* Find first byte of last character (may be incomplete) */
+    for(i=pos-1; i>0 && i>pos-4; i--)
+        if((readbuf[i]&0xc0) != 0x80)
+            break;
+    char_len = get_utf8_char_len(readbuf[i]);
+    if(char_len+i <= pos)
+        i += char_len;
+
+    if(fdinfo->wxflag & (WX_PIPE | WX_NOSEEK)) {
+        if(i < pos)
+            fdinfo->lookahead[0] = readbuf[i];
+        if(i+1 < pos)
+            fdinfo->lookahead[1] = readbuf[i+1];
+        if(i+2 < pos)
+            fdinfo->lookahead[2] = readbuf[i+2];
+    }else if(i < pos) {
+        SetFilePointer(fdinfo->handle, i-pos, NULL, FILE_CURRENT);
+    }
+    pos = i;
+
+    for(i=0, j=0; i<pos; i++) {
+        if(readbuf[i] == 0x1a) {
+            fdinfo->wxflag |= WX_ATEOF;
+            break;
+        }
+
+        /* strip '\r' if followed by '\n' */
+        if(readbuf[i] == '\r' && i+1==pos) {
+            if(fdinfo->lookahead[0] != '\n' || !ReadFile(hand, &lookahead, 1, &num_read, NULL) || !num_read) {
+                readbuf[j++] = '\r';
+            }else if(lookahead == '\n' && j==0) {
+                readbuf[j++] = '\n';
+            }else {
+                if(lookahead != '\n')
+                    readbuf[j++] = '\r';
+
+                if(fdinfo->wxflag & (WX_PIPE | WX_NOSEEK))
+                    fdinfo->lookahead[0] = lookahead;
+                else
+                    SetFilePointer(fdinfo->handle, -1, NULL, FILE_CURRENT);
+            }
+        }else if(readbuf[i]!='\r' || readbuf[i+1]!='\n') {
+            readbuf[j++] = readbuf[i];
+        }
+    }
+    pos = j;
+
+    if(!(num_read = MultiByteToWideChar(CP_UTF8, 0, readbuf, pos, buf, count))) {
+        _dosmaperr(GetLastError());
+        if (readbuf != min_buf) free(readbuf);
+        return -1;
+    }
+
+    if (readbuf != min_buf) free(readbuf);
+    return num_read*2;
+}
+
 /*********************************************************************
  * (internal) read_i
  *
@@ -1915,69 +2214,139 @@ int CDECL _rmtmp(void)
  */
 static int read_i(int fd, void *buf, unsigned int count)
 {
-  DWORD num_read;
-  char *bufstart = buf;
-  HANDLE hand = fdtoh(fd);
-  ioinfo *fdinfo = get_ioinfo(fd);
+    DWORD num_read, utf16;
+    char *bufstart = buf;
+    HANDLE hand = fdtoh(fd);
+    ioinfo *fdinfo = get_ioinfo(fd);
 
-  if (count == 0)
-    return 0;
+    if (count == 0)
+        return 0;
 
-  if (fdinfo->wxflag & WX_READEOF) {
-     fdinfo->wxflag |= WX_ATEOF;
-     TRACE("already at EOF, returning 0\n");
-     return 0;
-  }
-  /* Don't trace small reads, it gets *very* annoying */
-  if (count > 4)
-    TRACE(":fd (%d) handle (%p) buf (%p) len (%d)\n",fd,hand,buf,count);
-  if (hand == INVALID_HANDLE_VALUE)
-    return -1;
+    if (fdinfo->wxflag & WX_ATEOF) {
+        TRACE("already at EOF, returning 0\n");
+        return 0;
+    }
+    /* Don't trace small reads, it gets *very* annoying */
+    if (count > 4)
+        TRACE(":fd (%d) handle (%p) buf (%p) len (%d)\n",fd,hand,buf,count);
+    if (hand == INVALID_HANDLE_VALUE)
+    {
+        *_errno() = EBADF;
+        return -1;
+    }
 
-  /* Reading single bytes in O_TEXT mode makes things slow
-   * So read big chunks
-   */
-    if (ReadFile(hand, bufstart, count, &num_read, NULL))
+    utf16 = (fdinfo->exflag & EF_UTF16) != 0;
+    if (((fdinfo->exflag&EF_UTF8) || utf16) && count&1)
+    {
+        *_errno() = EINVAL;
+        return -1;
+    }
+
+    if((fdinfo->wxflag&WX_TEXT) && (fdinfo->exflag&EF_UTF8))
+        return read_utf8(fd, buf, count);
+
+    if (fdinfo->lookahead[0]!='\n' || ReadFile(hand, bufstart, count, &num_read, NULL))
     {
+        if (fdinfo->lookahead[0] != '\n')
+        {
+            bufstart[0] = fdinfo->lookahead[0];
+            fdinfo->lookahead[0] = '\n';
+
+            if (utf16)
+            {
+                bufstart[1] =  fdinfo->lookahead[1];
+                fdinfo->lookahead[1] = '\n';
+            }
+
+            if(count>1+utf16 && ReadFile(hand, bufstart+1+utf16, count-1-utf16, &num_read, NULL))
+                num_read += 1+utf16;
+            else
+                num_read = 1+utf16;
+        }
+
+        if(utf16 && (num_read&1))
+        {
+            /* msvcr90 uses uninitialized value from the buffer in this case */
+            /* msvcrt ignores additional data */
+            ERR("got odd number of bytes in UTF16 mode\n");
+            num_read--;
+        }
+
         if (count != 0 && num_read == 0)
         {
-            fdinfo->wxflag |= (WX_ATEOF|WX_READEOF);
+            fdinfo->wxflag |= WX_ATEOF;
             TRACE(":EOF %s\n",debugstr_an(buf,num_read));
         }
         else if (fdinfo->wxflag & WX_TEXT)
         {
             DWORD i, j;
-            if (bufstart[num_read-1] == '\r')
+
+            if (bufstart[0]=='\n' && (!utf16 || bufstart[1]==0))
+                fdinfo->wxflag |= WX_READNL;
+            else
+                fdinfo->wxflag &= ~WX_READNL;
+
+            for (i=0, j=0; i<num_read; i+=1+utf16)
             {
-                if(count == 1)
+                /* in text mode, a ctrl-z signals EOF */
+                if (bufstart[i]==0x1a && (!utf16 || bufstart[i+1]==0))
                 {
-                    fdinfo->wxflag  &=  ~WX_READCR;
-                    ReadFile(hand, bufstart, 1, &num_read, NULL);
+                    fdinfo->wxflag |= WX_ATEOF;
+                    TRACE(":^Z EOF %s\n",debugstr_an(buf,num_read));
+                    break;
                 }
-                else
+
+                /* in text mode, strip \r if followed by \n */
+                if (bufstart[i]=='\r' && (!utf16 || bufstart[i+1]==0) && i+1+utf16==num_read)
                 {
-                    fdinfo->wxflag  |= WX_READCR;
-                    num_read--;
+                    char lookahead[2];
+                    DWORD len;
+
+                    lookahead[1] = '\n';
+                    if (ReadFile(hand, lookahead, 1+utf16, &len, NULL) && len)
+                    {
+                        if(lookahead[0]=='\n' && (!utf16 || lookahead[1]==0) && j==0)
+                        {
+                            bufstart[j++] = '\n';
+                            if(utf16) bufstart[j++] = 0;
+                        }
+                        else
+                        {
+                            if(lookahead[0]!='\n' || (utf16 && lookahead[1]!=0))
+                            {
+                                bufstart[j++] = '\r';
+                                if(utf16) bufstart[j++] = 0;
+                            }
+
+                            if (fdinfo->wxflag & (WX_PIPE | WX_NOSEEK))
+                            {
+                                if (lookahead[0]=='\n' && (!utf16 || !lookahead[1]))
+                                {
+                                    bufstart[j++] = '\n';
+                                    if (utf16) bufstart[j++] = 0;
+                                }
+                                else
+                                {
+                                    fdinfo->lookahead[0] = lookahead[0];
+                                    fdinfo->lookahead[1] = lookahead[1];
+                                }
+                            }
+                            else
+                                SetFilePointer(fdinfo->handle, -1-utf16, NULL, FILE_CURRENT);
+                        }
+                    }
+                    else
+                    {
+                        bufstart[j++] = '\r';
+                        if(utf16) bufstart[j++] = 0;
+                    }
                 }
-            }
-           else
-             fdinfo->wxflag  &=  ~WX_READCR;
-            for (i=0, j=0; i<num_read; i++)
-            {
-                /* in text mode, a ctrl-z signals EOF */
-                if (bufstart[i] == 0x1a)
+                else if((bufstart[i]!='\r' || (utf16 && bufstart[i+1]!=0))
+                        || (bufstart[i+1+utf16]!='\n' || (utf16 && bufstart[i+3]!=0)))
                 {
-                    fdinfo->wxflag |= (WX_ATEOF|WX_READEOF);
-                    TRACE(":^Z EOF %s\n",debugstr_an(buf,num_read));
-                    break;
+                    bufstart[j++] = bufstart[i];
+                    if(utf16) bufstart[j++] = bufstart[i+1];
                 }
-                /* in text mode, strip \r if followed by \n.
-                 * BUG: should save state across calls somehow, so CR LF that
-                 * straddles buffer boundary gets recognized properly?
-                 */
-               if ((bufstart[i] != '\r')
-                ||  ((i+1) < num_read && bufstart[i+1] != '\n'))
-                   bufstart[j++] = bufstart[i];
             }
             num_read = j;
         }
@@ -1987,7 +2356,7 @@ static int read_i(int fd, void *buf, unsigned int count)
         if (GetLastError() == ERROR_BROKEN_PIPE)
         {
             TRACE(":end-of-pipe\n");
-            fdinfo->wxflag |= (WX_ATEOF|WX_READEOF);
+            fdinfo->wxflag |= WX_ATEOF;
             return 0;
         }
         else
@@ -1997,9 +2366,9 @@ static int read_i(int fd, void *buf, unsigned int count)
         }
     }
 
-  if (count > 4)
-      TRACE("(%u), %s\n",num_read,debugstr_an(buf, num_read));
-  return num_read;
+    if (count > 4)
+        TRACE("(%u), %s\n",num_read,debugstr_an(buf, num_read));
+    return num_read;
 }
 
 /*********************************************************************
@@ -2116,89 +2485,170 @@ int CDECL _umask(int umask)
  */
 int CDECL _write(int fd, const void* buf, unsigned int count)
 {
-  DWORD num_written;
-  HANDLE hand = fdtoh(fd);
+    DWORD num_written;
+    ioinfo *info = get_ioinfo(fd);
+    HANDLE hand = info->handle;
 
-  /* Don't trace small writes, it gets *very* annoying */
+    /* Don't trace small writes, it gets *very* annoying */
 #if 0
-  if (count > 32)
-    TRACE(":fd (%d) handle (%d) buf (%p) len (%d)\n",fd,hand,buf,count);
+    if (count > 32)
+        TRACE(":fd (%d) handle (%d) buf (%p) len (%d)\n",fd,hand,buf,count);
 #endif
-  if (hand == INVALID_HANDLE_VALUE)
+    if (hand == INVALID_HANDLE_VALUE)
     {
-      *_errno() = EBADF;
-      return -1;
+        *_errno() = EBADF;
+        return -1;
     }
 
-  /* If appending, go to EOF */
-  if (get_ioinfo(fd)->wxflag & WX_APPEND)
-    _lseek(fd, 0, FILE_END);
+    if (((info->exflag&EF_UTF8) || (info->exflag&EF_UTF16)) && count&1)
+    {
+        *_errno() = EINVAL;
+        return -1;
+    }
 
-  if (!(get_ioinfo(fd)->wxflag & WX_TEXT))
+    /* If appending, go to EOF */
+    if (info->wxflag & WX_APPEND)
+        _lseek(fd, 0, FILE_END);
+
+    if (!(info->wxflag & WX_TEXT))
     {
-      if (WriteFile(hand, buf, count, &num_written, NULL)
-         &&  (num_written == count))
-       return num_written;
-      TRACE("WriteFile (fd %d, hand %p) failed-last error (%d)\n", fd,
-       hand, GetLastError());
-      *_errno() = ENOSPC;
+        if (WriteFile(hand, buf, count, &num_written, NULL)
+                &&  (num_written == count))
+            return num_written;
+        TRACE("WriteFile (fd %d, hand %p) failed-last error (%d)\n", fd,
+                hand, GetLastError());
+        *_errno() = ENOSPC;
     }
-  else
-  {
-      unsigned int i, j, nr_lf;
-      char *p = NULL;
-      const char *q;
-      const char *s = buf, *buf_start = buf;
-      /* find number of \n ( without preceding \r ) */
-      for ( nr_lf=0,i = 0; i <count; i++)
-      {
-          if (s[i]== '\n')
-          {
-              nr_lf++;
-              /*if ((i >1) && (s[i-1] == '\r'))        nr_lf--; */
-          }
-      }
-      if (nr_lf)
-      {
-          if ((q = p = malloc(count + nr_lf)))
-          {
-              for (s = buf, i = 0, j = 0; i < count; i++)
-              {
-                  if (s[i]== '\n')
-                  {
-                      p[j++] = '\r';
-                      /*if ((i >1) && (s[i-1] == '\r'))j--;*/
-                  }
-                  p[j++] = s[i];
-              }
-          }
-          else
-          {
-              FIXME("Malloc failed\n");
-              nr_lf =0;
-              q = buf;
-          }
-      }
-      else
-          q = buf;
+    else
+    {
+        unsigned int i, j, nr_lf, size;
+        char *p = NULL;
+        const char *q;
+        const char *s = buf, *buf_start = buf;
 
-      if ((WriteFile(hand, q, count+nr_lf, &num_written, NULL) == 0 ) || (num_written != count+nr_lf))
-      {
-          TRACE("WriteFile (fd %d, hand %p) failed-last error (%d), num_written %d\n",
-           fd, hand, GetLastError(), num_written);
-          *_errno() = ENOSPC;
-          if(nr_lf)
-              free(p);
-          return s - buf_start;
-      }
-      else
-      {
-          if(nr_lf)
-              free(p);
-          return count;
-      }
-  }
-  return -1;
+        if (!(info->exflag & (EF_UTF8|EF_UTF16)))
+        {
+            /* find number of \n */
+            for (nr_lf=0, i=0; i<count; i++)
+                if (s[i] == '\n')
+                    nr_lf++;
+            if (nr_lf)
+            {
+                size = count+nr_lf;
+                if ((q = p = malloc(size)))
+                {
+                    for (s = buf, i = 0, j = 0; i < count; i++)
+                    {
+                        if (s[i] == '\n')
+                            p[j++] = '\r';
+                        p[j++] = s[i];
+                    }
+                }
+                else
+                {
+                    FIXME("Malloc failed\n");
+                    nr_lf = 0;
+                    size = count;
+                    q = buf;
+                }
+            }
+            else
+            {
+                size = count;
+                q = buf;
+            }
+        }
+        else if (info->exflag & EF_UTF16)
+        {
+            for (nr_lf=0, i=0; i<count; i+=2)
+                if (s[i]=='\n' && s[i+1]==0)
+                    nr_lf += 2;
+            if (nr_lf)
+            {
+                size = count+nr_lf;
+                if ((q = p = malloc(size)))
+                {
+                    for (s=buf, i=0, j=0; i<count; i++)
+                    {
+                        if (s[i]=='\n' && s[i+1]==0)
+                        {
+                            p[j++] = '\r';
+                            p[j++] = 0;
+                        }
+                        p[j++] = s[i++];
+                        p[j++] = s[i];
+                    }
+                }
+                else
+                {
+                    FIXME("Malloc failed\n");
+                    nr_lf = 0;
+                    size = count;
+                    q = buf;
+                }
+            }
+            else
+            {
+                size = count;
+                q = buf;
+            }
+        }
+        else
+        {
+            DWORD conv_len;
+
+            for(nr_lf=0, i=0; i<count; i+=2)
+                if (s[i]=='\n' && s[i+1]==0)
+                    nr_lf++;
+
+            conv_len = WideCharToMultiByte(CP_UTF8, 0, (WCHAR*)buf, count/2, NULL, 0, NULL, NULL);
+            if(!conv_len) {
+                _dosmaperr(GetLastError());
+                free(p);
+                return -1;
+            }
+
+            size = conv_len+nr_lf;
+            if((p = malloc(count+nr_lf*2+size)))
+            {
+                for (s=buf, i=0, j=0; i<count; i++)
+                {
+                    if (s[i]=='\n' && s[i+1]==0)
+                    {
+                        p[j++] = '\r';
+                        p[j++] = 0;
+                    }
+                    p[j++] = s[i++];
+                    p[j++] = s[i];
+                }
+                q = p+count+nr_lf*2;
+                WideCharToMultiByte(CP_UTF8, 0, (WCHAR*)p, count/2+nr_lf,
+                        p+count+nr_lf*2, conv_len+nr_lf, NULL, NULL);
+            }
+            else
+            {
+                FIXME("Malloc failed\n");
+                nr_lf = 0;
+                size = count;
+                q = buf;
+            }
+        }
+
+        if (!WriteFile(hand, q, size, &num_written, NULL))
+            num_written = -1;
+        if(p)
+            free(p);
+        if (num_written != size)
+        {
+            TRACE("WriteFile (fd %d, hand %p) failed-last error (%d), num_written %d\n",
+                    fd, hand, GetLastError(), num_written);
+            *_errno() = ENOSPC;
+            return s - buf_start;
+        }
+        return count;
+    }
+
+    return -1;
 }
 
 /*********************************************************************
@@ -2284,7 +2734,7 @@ int CDECL _filbuf(FILE* file)
     }
 
     /* Allocate buffer if needed */
-    if(file->_bufsiz == 0 && !(file->_flag & _IONBF))
+    if(!(file->_flag & (_IONBF | _IOMYBUF | _USERBUF)))
         alloc_buffer(file);
 
     if(!(file->_flag & _IOREAD)) {
@@ -2296,7 +2746,7 @@ int CDECL _filbuf(FILE* file)
         }
     }
 
-    if(file->_flag & _IONBF) {
+    if(!(file->_flag & (_IOMYBUF | _USERBUF))) {
         int r;
         if ((r = read_i(file->_file,&c,1)) != 1) {
             file->_flag |= (r == 0) ? _IOEOF : _IOERR;
@@ -2385,60 +2835,50 @@ char * CDECL fgets(char *s, int size, FILE* file)
 
 /*********************************************************************
  *             fgetwc (MSVCRT.@)
- *
- * In _O_TEXT mode, multibyte characters are read from the file, dropping
- * the CR from CR/LF combinations
  */
 wint_t CDECL fgetwc(FILE* file)
 {
-  int c;
+    wint_t ret;
+    int ch;
 
-  _lock_file(file);
-  if (!(get_ioinfo(file->_file)->wxflag & WX_TEXT))
-    {
-      wchar_t wc;
-      unsigned int i;
-      int j;
-      char *chp, *wcp;
-      wcp = (char *)&wc;
-      for(i=0; i<sizeof(wc); i++)
-      {
-        if (file->_cnt>0)
-        {
-          file->_cnt--;
-          chp = file->_ptr++;
-          wcp[i] = *chp;
-        }
-        else
-        {
-          j = _filbuf(file);
-          if(file->_cnt<=0)
-          {
-            file->_flag |= (file->_cnt == 0) ? _IOEOF : _IOERR;
-            file->_cnt = 0;
+    _lock_file(file);
 
-            _unlock_file(file);
-            return WEOF;
-          }
-          wcp[i] = j;
-        }
-      }
+    if((get_ioinfo(file->_file)->exflag & (EF_UTF8 | EF_UTF16))
+            || !(get_ioinfo(file->_file)->wxflag & WX_TEXT)) {
+        char *p;
 
-      _unlock_file(file);
-      return wc;
-    }
+        for(p=(char*)&ret; (wint_t*)p<&ret+1; p++) {
+            ch = fgetc(file);
+            if(ch == EOF) {
+                ret = WEOF;
+                break;
+            }
+            *p = (char)ch;
+        }
+    }else {
+        char mbs[MB_LEN_MAX];
+        int len = 0;
+
+        ch = fgetc(file);
+        if(ch != EOF) {
+            mbs[0] = (char)ch;
+            if(isleadbyte((unsigned char)mbs[0])) {
+                ch = fgetc(file);
+                if(ch != EOF) {
+                    mbs[1] = (char)ch;
+                    len = 2;
+                }
+            }else {
+                len = 1;
+            }
+        }
 
-  c = fgetc(file);
-  if ((__mb_cur_max > 1) && isleadbyte(c))
-    {
-      FIXME("Treat Multibyte characters\n");
+        if(!len || mbtowc(&ret, mbs, len)==-1)
+            ret = WEOF;
     }
 
-  _unlock_file(file);
-  if (c == EOF)
-    return WEOF;
-  else
-    return (wint_t)c;
+    _unlock_file(file);
+    return ret;
 }
 
 /*********************************************************************
@@ -2690,11 +3130,9 @@ FILE * CDECL fopen(const char *path, const char *mode)
 int CDECL fopen_s(FILE** pFile,
         const char *filename, const char *mode)
 {
-    if (!MSVCRT_CHECK_PMT(pFile != NULL) || !MSVCRT_CHECK_PMT(filename != NULL) ||
-        !MSVCRT_CHECK_PMT(mode != NULL)) {
-        *_errno() = EINVAL;
-        return EINVAL;
-    }
+    if (!MSVCRT_CHECK_PMT(pFile != NULL)) return EINVAL;
+    if (!MSVCRT_CHECK_PMT(filename != NULL)) return EINVAL;
+    if (!MSVCRT_CHECK_PMT(mode != NULL)) return EINVAL;
 
     *pFile = fopen(filename, mode);
 
@@ -2776,7 +3214,7 @@ size_t CDECL fread(void *ptr, size_t size, size_t nmemb, FILE* file)
 {
   size_t rcnt=size * nmemb;
   size_t read=0;
-  int pread=0;
+  size_t pread=0;
 
   if(!rcnt)
        return 0;
@@ -2785,7 +3223,7 @@ size_t CDECL fread(void *ptr, size_t size, size_t nmemb, FILE* file)
 
   /* first buffered data */
   if(file->_cnt>0) {
-       int pcnt= (rcnt>(unsigned int)file->_cnt)? file->_cnt:rcnt;
+       int pcnt= (rcnt>file->_cnt)? file->_cnt:rcnt;
        memcpy(ptr, file->_ptr, pcnt);
        file->_cnt -= pcnt;
        file->_ptr += pcnt;
@@ -2800,19 +3238,17 @@ size_t CDECL fread(void *ptr, size_t size, size_t nmemb, FILE* file)
         return 0;
     }
   }
+
+  if(rcnt>0 && !(file->_flag & (_IONBF | _IOMYBUF | _USERBUF)))
+      alloc_buffer(file);
+
   while(rcnt>0)
   {
     int i;
-    /* Fill the buffer on small reads.
-     * TODO: Use a better buffering strategy.
-     */
-    if (!file->_cnt && size*nmemb <= BUFSIZ/2 && !(file->_flag & _IONBF)) {
-      if (file->_bufsiz == 0) {
-        alloc_buffer(file);
-      }
+    if (!file->_cnt && rcnt<BUFSIZ && (file->_flag & (_IOMYBUF | _USERBUF))) {
       file->_cnt = _read(file->_file, file->_base, file->_bufsiz);
       file->_ptr = file->_base;
-      i = ((unsigned int)file->_cnt<rcnt) ? file->_cnt : rcnt;
+      i = (file->_cnt<rcnt) ? file->_cnt : rcnt;
       /* If the buffer fill reaches eof but fread wouldn't, clear eof. */
       if (i > 0 && i < file->_cnt) {
         get_ioinfo(file->_file)->wxflag &= ~WX_ATEOF;
@@ -2823,8 +3259,12 @@ size_t CDECL fread(void *ptr, size_t size, size_t nmemb, FILE* file)
         file->_cnt -= i;
         file->_ptr += i;
       }
+    } else if (rcnt > INT_MAX) {
+      i = _read(file->_file, ptr, INT_MAX);
+    } else if (rcnt < BUFSIZ) {
+      i = _read(file->_file, ptr, rcnt);
     } else {
-      i = _read(file->_file,ptr, rcnt);
+      i = _read(file->_file, ptr, rcnt - BUFSIZ/2);
     }
     pread += i;
     rcnt -= i;
@@ -2946,7 +3386,7 @@ __int64 CDECL _ftelli64(FILE* file)
         _unlock_file(file);
         return -1;
     }
-    if(file->_bufsiz)  {
+    if(file->_flag & (_IOMYBUF | _USERBUF))  {
         if(file->_flag & _IOWRT) {
             pos += file->_ptr - file->_base;
 
@@ -3006,34 +3446,9 @@ LONG CDECL ftell(FILE* file)
  */
 int CDECL fgetpos(FILE* file, fpos_t *pos)
 {
-    int off=0;
-
-    _lock_file(file);
-    *pos = _lseeki64(file->_file,0,SEEK_CUR);
-    if(*pos == -1) {
-        _unlock_file(file);
+    *pos = _ftelli64(file);
+    if(*pos == -1)
         return -1;
-    }
-    if(file->_bufsiz)  {
-        if( file->_flag & _IOWRT ) {
-            off = file->_ptr - file->_base;
-        } else {
-            off = -file->_cnt;
-            if (get_ioinfo(file->_file)->wxflag & WX_TEXT) {
-                /* Black magic correction for CR removal */
-                int i;
-                for (i=0; i<file->_cnt; i++) {
-                    if (file->_ptr[i] == '\n')
-                        off--;
-                }
-                /* Black magic when reading CR at buffer boundary*/
-                if(get_ioinfo(file->_file)->wxflag & WX_READCR)
-                    off--;
-            }
-        }
-    }
-    *pos += off;
-    _unlock_file(file);
     return 0;
 }
 
@@ -3042,23 +3457,13 @@ int CDECL fgetpos(FILE* file, fpos_t *pos)
  */
 int CDECL fputs(const char *s, FILE* file)
 {
-    size_t i, len = strlen(s);
+    size_t len = strlen(s);
     int ret;
 
     _lock_file(file);
-    if (!(get_ioinfo(file->_file)->wxflag & WX_TEXT)) {
-      ret = fwrite(s,sizeof(*s),len,file) == len ? 0 : EOF;
-      _unlock_file(file);
-      return ret;
-    }
-    for (i=0; i<len; i++)
-      if (fputc(s[i], file) == EOF)  {
-        _unlock_file(file);
-        return EOF;
-      }
-
+    ret = fwrite(s, sizeof(*s), len, file) == len ? 0 : EOF;
     _unlock_file(file);
-    return 0;
+    return ret;
 }
 
 /*********************************************************************
@@ -3067,6 +3472,7 @@ int CDECL fputs(const char *s, FILE* file)
 int CDECL fputws(const wchar_t *s, FILE* file)
 {
     size_t i, len = strlenW(s);
+    BOOL tmp_buf;
     int ret;
 
     _lock_file(file);
@@ -3075,14 +3481,17 @@ int CDECL fputws(const wchar_t *s, FILE* file)
         _unlock_file(file);
         return ret;
     }
+
+    tmp_buf = add_std_buffer(file);
     for (i=0; i<len; i++) {
-        if (((s[i] == '\n') && (fputc('\r', file) == EOF))
-                || fputwc(s[i], file) == WEOF) {
+        if(fputwc(s[i], file) == WEOF) {
+            if(tmp_buf) remove_std_buffer(file);
             _unlock_file(file);
             return WEOF;
         }
     }
 
+    if(tmp_buf) remove_std_buffer(file);
     _unlock_file(file);
     return 0;
 }
@@ -3316,6 +3725,7 @@ char * CDECL tmpnam(char *s)
   {
     size = int_to_base32(tmpnam_unique++, tmpstr);
     memcpy(p, tmpstr, size);
+    p[size] = '\0';
     if (GetFileAttributesA(s) == INVALID_FILE_ATTRIBUTES &&
         GetLastError() == ERROR_FILE_NOT_FOUND)
       break;
@@ -3347,6 +3757,7 @@ wchar_t * CDECL _wtmpnam(wchar_t *s)
     {
         size = int_to_base32_w(tmpnam_unique++, tmpstr);
         memcpy(p, tmpstr, size*sizeof(wchar_t));
+        p[size] = '\0';
         if (GetFileAttributesW(s) == INVALID_FILE_ATTRIBUTES &&
                 GetLastError() == ERROR_FILE_NOT_FOUND)
             break;
@@ -3391,13 +3802,19 @@ int CDECL ungetc(int c, FILE * file)
         return EOF;
 
     _lock_file(file);
-    if(file->_bufsiz == 0) {
-        alloc_buffer(file);
+    if(file->_bufsiz == 0 && alloc_buffer(file))
         file->_ptr++;
-    }
     if(file->_ptr>file->_base) {
         file->_ptr--;
-        *file->_ptr=c;
+        if(file->_flag & _IOSTRG) {
+            if(*file->_ptr != c) {
+                file->_ptr++;
+                _unlock_file(file);
+                return EOF;
+            }
+        }else {
+            *file->_ptr = c;
+        }
         file->_cnt++;
         clearerr(file);
         _unlock_file(file);
@@ -3414,15 +3831,39 @@ int CDECL ungetc(int c, FILE * file)
 wint_t CDECL ungetwc(wint_t wc, FILE * file)
 {
     wchar_t mwc = wc;
-    char * pp = (char *)&mwc;
-    int i;
+
+    if (wc == WEOF)
+        return WEOF;
 
     _lock_file(file);
-    for(i=sizeof(wchar_t)-1;i>=0;i--) {
-        if(pp[i] != ungetc(pp[i],file)) {
+
+    if((get_ioinfo(file->_file)->exflag & (EF_UTF8 | EF_UTF16))
+            || !(get_ioinfo(file->_file)->wxflag & WX_TEXT)) {
+        unsigned char * pp = (unsigned char *)&mwc;
+        int i;
+
+        for(i=sizeof(wchar_t)-1;i>=0;i--) {
+            if(pp[i] != ungetc(pp[i],file)) {
+                _unlock_file(file);
+                return WEOF;
+            }
+        }
+    }else {
+        char mbs[MB_LEN_MAX];
+        int len;
+
+        len = wctomb(mbs, mwc);
+        if(len == -1) {
             _unlock_file(file);
             return WEOF;
         }
+
+        for(len--; len>=0; len--) {
+            if(mbs[len] != ungetc(mbs[len], file)) {
+                _unlock_file(file);
+                return WEOF;
+            }
+        }
     }
 
     _unlock_file(file);