[crt]
[reactos.git] / reactos / lib / sdk / crt / stdio / file.c
index e3e4e2c..6425575 100644 (file)
@@ -7,6 +7,14 @@
  *                  Ported to ReactOS by Aleksey Bragin (aleksey@reactos.org)
  */
 
+/*********************************************
+ * This file contains ReactOS changes!!
+ * Don't blindly sync it with Wine code!
+ *
+ * If you break Unicode output on the console again, please update this counter:
+ *   int hours_wasted_on_this = 42;
+ *********************************************/
+
 /*
  * msvcrt.dll file functions
  *
@@ -81,15 +89,25 @@ int *__p___mb_cur_max(void);
 #define WX_APPEND         0x20
 #define WX_TEXT           0x80
 
+/* values for exflag - it's used differently in msvcr90.dll*/
+#define EF_UTF8           0x01
+#define EF_UTF16          0x02
+#define EF_CRIT_INIT      0x04
+#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
 
+/* ioinfo structure size is different in msvcrXX.dll's */
 typedef struct {
     HANDLE              handle;
     unsigned char       wxflag;
     char                unk1;
-    BOOL                crit_init;
+    int                 exflag;
     CRITICAL_SECTION    crit;
 } ioinfo;
 
@@ -112,8 +130,8 @@ typedef struct {
     CRITICAL_SECTION crit;
 } file_crit;
 
-FILE _iob[3] = { { 0 } };
-static file_crit* fstream[MAX_FILES/FD_BLOCK_SIZE];
+FILE _iob[_IOB_ENTRIES] = { { 0 } };
+static file_crit* fstream[MAX_FILES/FD_BLOCK_SIZE] = { NULL };
 static int max_streams = 512, stream_idx;
 
 /* INTERNAL: process umask */
@@ -309,7 +327,7 @@ static FILE* alloc_fp(void)
   unsigned int i;
   FILE *file;
 
-  for (i = 3; i < max_streams; i++)
+  for (i = 3; i < (unsigned int)max_streams; i++)
   {
     file = get_file(i);
     if (!file)
@@ -386,17 +404,17 @@ unsigned create_io_inherit_block(WORD *size, BYTE **block)
       *handle_ptr = INVALID_HANDLE_VALUE;
     }
     wxflag_ptr++; handle_ptr++;
-  } 
+  }
   return TRUE;
 }
 
-/* INTERNAL: Set up all file descriptors, 
- * as well as default streams (stdin, stderr and stdout) 
+/* INTERNAL: Set up all file descriptors,
+ * as well as default streams (stdin, stderr and stdout)
  */
 void msvcrt_init_io(void)
 {
   STARTUPINFOA  si;
-  int           i;
+  unsigned int  i;
   ioinfo        *fdinfo;
 
   InitializeCriticalSection(&file_cs);
@@ -726,10 +744,8 @@ int CDECL _wunlink(const wchar_t *path)
 /* _flushall calls fflush which calls _flushall */
 int CDECL fflush(FILE* file);
 
-/*********************************************************************
- *             _flushall (MSVCRT.@)
- */
-int CDECL _flushall(void)
+/* INTERNAL: Flush all stream buffer */
+static int flush_all_buffers(int mask)
 {
   int i, num_flushed = 0;
   FILE *file;
@@ -740,8 +756,8 @@ int CDECL _flushall(void)
 
     if (file->_flag)
     {
-      if(file->_flag & _IOWRT) {
-       fflush(file);
+      if(file->_flag & mask) {
+        fflush(file);
         num_flushed++;
       }
     }
@@ -752,22 +768,41 @@ int CDECL _flushall(void)
   return num_flushed;
 }
 
+/*********************************************************************
+ *             _flushall (MSVCRT.@)
+ */
+int CDECL _flushall(void)
+{
+    return flush_all_buffers(_IOWRT | _IOREAD);
+}
+
 /*********************************************************************
  *             fflush (MSVCRT.@)
  */
 int CDECL fflush(FILE* file)
 {
     if(!file) {
-        _flushall();
+        flush_all_buffers(_IOWRT);
     } else if(file->_flag & _IOWRT) {
         int res;
 
         _lock_file(file);
         res = flush_buffer(file);
+        /* FIXME
+        if(!res && (file->_flag & _IOCOMMIT))
+            res = _commit(file->_file) ? EOF : 0;
+        */
         _unlock_file(file);
 
         return res;
-    }
+    } else if(file->_flag & _IOREAD) {
+        _lock_file(file);
+        file->_cnt = 0;
+        file->_ptr = file->_base;
+        _unlock_file(file);
+
+        return 0;
+    }    
     return 0;
 }
 
@@ -963,10 +998,10 @@ void msvcrt_free_io(void)
     fclose(&_iob[2]);
 
     for(i=0; i<sizeof(__pioinfo)/sizeof(__pioinfo[0]); i++)
-            free(__pioinfo[i]);
+        free(__pioinfo[i]);
 
     for(i=0; i<sizeof(fstream)/sizeof(fstream[0]); i++)
-            free(fstream[i]);
+        free(fstream[i]);
 
     file_cs.DebugInfo->Spare[0] = 0;
     DeleteCriticalSection(&file_cs);
@@ -1017,7 +1052,7 @@ __int64 CDECL _lseeki64(int fd, __int64 offset, int whence)
  */
 LONG CDECL _lseek(int fd, LONG offset, int whence)
 {
-    return _lseeki64(fd, offset, whence);
+    return (LONG)_lseeki64(fd, offset, whence);
 }
 
 /*********************************************************************
@@ -1212,7 +1247,9 @@ void CDECL rewind(FILE* file)
 static int get_flags(const wchar_t* mode, int *open_flags, int* stream_flags)
 {
   int plus = strchrW(mode, '+') != NULL;
-
+  
+  while(*mode == ' ') mode++;
+  
   switch(*mode++)
   {
   case 'R': case 'r':
@@ -1233,23 +1270,97 @@ static int get_flags(const wchar_t* mode, int *open_flags, int* stream_flags)
     return -1;
   }
 
-  while (*mode)
+  /* FIXME */
+  /* *stream_flags |= MSVCRT__commode; */
+  
+  while (*mode && *mode!=',')
     switch (*mode++)
     {
     case 'B': case 'b':
       *open_flags |=  _O_BINARY;
       *open_flags &= ~_O_TEXT;
       break;
-    case 'T': case 't':
+    case 't':
       *open_flags |=  _O_TEXT;
       *open_flags &= ~_O_BINARY;
       break;
+    case 'D':
+      *open_flags |= _O_TEMPORARY;
+      break;
+    case 'T':
+      *open_flags |= _O_SHORT_LIVED;
+      break;
+      /* FIXME 
+   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
+    {
+      _invalid_parameter(NULL, NULL, NULL, 0, 0);
+      *_errno() = EINVAL;
+      return -1;
+    }
+
+    while(*mode == ' ') mode++;
+  }
+
+ if(!MSVCRT_CHECK_PMT(*mode == 0))
+    return -1;
+  
   return 0;
 }
 
@@ -1438,19 +1549,23 @@ wchar_t * CDECL _wmktemp(wchar_t *pattern)
     int         wxflags = 0;
     unsigned unsupp; /* until we support everything */
 
-    if (oflags & _O_APPEND)              wxflags |= WX_APPEND;
-    if (oflags & _O_BINARY)              {/* Nothing to do */}
-    else if (oflags & _O_TEXT)           wxflags |= WX_TEXT;
-    else if (*__p__fmode() & _O_BINARY)  {/* Nothing to do */}
+    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;
+    if (oflags & _O_NOINHERIT)                  wxflags |= WX_DONTINHERIT;
 
     if ((unsupp = oflags & ~(
                     _O_BINARY|_O_TEXT|_O_APPEND|
                     _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);
 
@@ -1563,7 +1678,7 @@ int CDECL _sopen_s( int *fd, const char *path, int oflags, int shflags, int pmod
     else
       creation = OPEN_EXISTING;
   }
-  
+
   switch( shflags )
   {
     case _SH_DENYRW:
@@ -1631,6 +1746,29 @@ int CDECL _sopen( const char *path, int oflags, int shflags, ... )
   return fd;
 }
 
+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;
+}
+
 /*********************************************************************
  *              _wsopen_s (MSVCRT.@)
  */
@@ -1714,6 +1852,21 @@ int CDECL _wsopen_s( int *fd, const wchar_t* path, int oflags, int shflags, int
   sa.lpSecurityDescriptor = NULL;
   sa.bInheritHandle       = (oflags & _O_NOINHERIT) ? FALSE : TRUE;
 
+   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 = CreateFileW(path, access, sharing, &sa, creation, attrib, 0);
 
   if (hand == INVALID_HANDLE_VALUE)  {
@@ -1722,7 +1875,56 @@ int CDECL _wsopen_s( int *fd, const wchar_t* path, int oflags, int shflags, int
     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);
+                  *_errno() = 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);
+                  *_errno() = 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;
@@ -2339,12 +2541,12 @@ wint_t CDECL fgetwc(FILE* file)
       wcp = (char *)&wc;
       for(i=0; i<sizeof(wc); i++)
       {
-        if (file->_cnt>0) 
+        if (file->_cnt>0)
         {
           file->_cnt--;
           chp = file->_ptr++;
           wcp[i] = *chp;
-        } 
+        }
         else
         {
           j = _filbuf(file);
@@ -2363,7 +2565,7 @@ wint_t CDECL fgetwc(FILE* file)
       _unlock_file(file);
       return wc;
     }
-    
+
   c = fgetc(file);
   if ((__mb_cur_max > 1) && isleadbyte(c))
     {
@@ -2470,7 +2672,7 @@ size_t CDECL fwrite(const void *ptr, size_t size, size_t nmemb, FILE* file)
 
     _lock_file(file);
     if(file->_cnt) {
-        int pcnt=(file->_cnt>wrcnt)? wrcnt: file->_cnt;
+        int pcnt=((unsigned)file->_cnt>wrcnt)? wrcnt: file->_cnt;
         memcpy(file->_ptr, ptr, pcnt);
         file->_cnt -= pcnt;
         file->_ptr += pcnt;
@@ -2505,13 +2707,38 @@ size_t CDECL fwrite(const void *ptr, size_t size, size_t nmemb, FILE* file)
 
 /*********************************************************************
  *             fputwc (MSVCRT.@)
+ * FORKED for ReactOS, don't sync with Wine!
+ * References:
+ *   - http://jira.reactos.org/browse/CORE-6495
+ *   - http://bugs.winehq.org/show_bug.cgi?id=8598
  */
-wint_t CDECL fputwc(wint_t wc, FILE* file)
+wint_t CDECL fputwc(wchar_t c, FILE* stream)
 {
-  wchar_t mwc=wc;
-  if (fwrite( &mwc, sizeof(mwc), 1, file) != 1)
-    return WEOF;
-  return wc;
+    /* If this is a real file stream (and not some temporary one for
+       sprintf-like functions), check whether it is opened in text mode.
+       In this case, we have to perform an implicit conversion to ANSI. */
+    if (!(stream->_flag & _IOSTRG) && get_ioinfo(stream->_file)->wxflag & WX_TEXT)
+    {
+        /* Convert to multibyte in text mode */
+        char mbc[MB_LEN_MAX];
+        int mb_return;
+
+        mb_return = wctomb(mbc, c);
+
+        if(mb_return == -1)
+            return WEOF;
+
+        /* Output all characters */
+        if (fwrite(mbc, mb_return, 1, stream) != 1)
+            return WEOF;
+    }
+    else
+    {
+        if (fwrite(&c, sizeof(c), 1, stream) != 1)
+            return WEOF;
+    }
+
+    return c;
 }
 
 /*********************************************************************
@@ -2693,7 +2920,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>file->_cnt)? file->_cnt:rcnt;
+       int pcnt= (rcnt>(unsigned int)file->_cnt)? file->_cnt:rcnt;
        memcpy(ptr, file->_ptr, pcnt);
        file->_cnt -= pcnt;
        file->_ptr += pcnt;
@@ -2720,7 +2947,7 @@ size_t CDECL fread(void *ptr, size_t size, size_t nmemb, FILE* file)
       }
       file->_cnt = _read(file->_file, file->_base, file->_bufsiz);
       file->_ptr = file->_base;
-      i = (file->_cnt<rcnt) ? file->_cnt : rcnt;
+      i = ((unsigned int)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;
@@ -2830,7 +3057,7 @@ int CDECL fsetpos(FILE* file, const fpos_t *pos)
   /* Discard buffered input */
   file->_cnt = 0;
   file->_ptr = file->_base;
-  
+
   /* Reset direction of i/o */
   if(file->_flag & _IORW) {
         file->_flag &= ~(_IOREAD|_IOWRT);
@@ -2884,7 +3111,7 @@ __int64 CDECL _ftelli64(FILE* file)
  */
 LONG CDECL ftell(FILE* file)
 {
-  return _ftelli64(file);
+  return (LONG)_ftelli64(file);
 }
 
 /*********************************************************************