[MSVCRT_WINETEST]: Sync to Wine 1.5.13.
authorAmine Khaldi <amine.khaldi@reactos.org>
Sat, 29 Sep 2012 14:28:48 +0000 (14:28 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Sat, 29 Sep 2012 14:28:48 +0000 (14:28 +0000)
CORE-6415

svn path=/trunk/; revision=57432

rostests/winetests/msvcrt/cpp.c
rostests/winetests/msvcrt/file.c
rostests/winetests/msvcrt/locale.c
rostests/winetests/msvcrt/misc.c
rostests/winetests/msvcrt/printf.c
rostests/winetests/msvcrt/scanf.c
rostests/winetests/msvcrt/string.c
rostests/winetests/msvcrt/time.c

index bb6ba16..8baf9b6 100644 (file)
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- *
- * NOTES
- * This tests is only valid for ix86 platforms, on others it's a no-op.
- * Some tests cannot be checked with ok(), for example the dtors. We simply
- * call them to ensure we don't crash ;-)
- *
- * If we build this test with VC++ in debug mode, we will fail in _chkstk()
- * or at program exit malloc() checking if these methods haven't been
- * implemented correctly (they have).
- *
- * Tested with a range of native msvcrt's from v4 -> v7.
  */
 #include "wine/test.h"
 #include "winbase.h"
 #include "winnt.h"
 
-#ifndef __i386__
-/* Skip these tests for non x86 platforms */
-START_TEST(cpp)
-{
-}
-#else
+typedef void (*vtable_ptr)(void);
 
 typedef struct __exception
 {
-  void *vtable;
+  vtable_ptr *vtable;
   char *name;
   int   do_free;
 } exception;
 
 typedef struct __type_info
 {
-  void *vtable;
+  vtable_ptr *vtable;
   char *name;
   char  mangled[16];
 } type_info;
 
+#undef __thiscall
+#ifdef __i386__
+#define __thiscall __stdcall
+#else
+#define __thiscall __cdecl
+#endif
+
 /* Function pointers. We need to use these to call these funcs as __thiscall */
 static HMODULE hMsvcrt;
 
@@ -61,56 +52,56 @@ static void* (__cdecl *pmalloc)(unsigned int);
 static void  (__cdecl *pfree)(void*);
 
 /* exception */
-static void (WINAPI *pexception_ctor)(exception*,LPCSTR*);
-static void (WINAPI *pexception_copy_ctor)(exception*,exception*);
-static void (WINAPI *pexception_default_ctor)(exception*);
-static void (WINAPI *pexception_dtor)(exception*);
-static exception* (WINAPI *pexception_opequals)(exception*,exception*);
-static char* (WINAPI *pexception_what)(exception*);
-static void* (WINAPI *pexception_vtable)(exception*);
-static void (WINAPI *pexception_vector_dtor)(exception*,unsigned int);
-static void (WINAPI *pexception_scalar_dtor)(exception*,unsigned int);
+static void (__thiscall *pexception_ctor)(exception*,LPCSTR*);
+static void (__thiscall *pexception_copy_ctor)(exception*,exception*);
+static void (__thiscall *pexception_default_ctor)(exception*);
+static void (__thiscall *pexception_dtor)(exception*);
+static exception* (__thiscall *pexception_opequals)(exception*,exception*);
+static char* (__thiscall *pexception_what)(exception*);
+static vtable_ptr *pexception_vtable;
+static void (__thiscall *pexception_vector_dtor)(exception*,unsigned int);
+static void (__thiscall *pexception_scalar_dtor)(exception*,unsigned int);
 
 /* bad_typeid */
-static void (WINAPI *pbad_typeid_ctor)(exception*,LPCSTR);
-static void (WINAPI *pbad_typeid_ctor_closure)(exception*);
-static void (WINAPI *pbad_typeid_copy_ctor)(exception*,exception*);
-static void (WINAPI *pbad_typeid_dtor)(exception*);
-static exception* (WINAPI *pbad_typeid_opequals)(exception*,exception*);
-static char* (WINAPI *pbad_typeid_what)(exception*);
-static void* (WINAPI *pbad_typeid_vtable)(exception*);
-static void (WINAPI *pbad_typeid_vector_dtor)(exception*,unsigned int);
-static void (WINAPI *pbad_typeid_scalar_dtor)(exception*,unsigned int);
+static void (__thiscall *pbad_typeid_ctor)(exception*,LPCSTR);
+static void (__thiscall *pbad_typeid_ctor_closure)(exception*);
+static void (__thiscall *pbad_typeid_copy_ctor)(exception*,exception*);
+static void (__thiscall *pbad_typeid_dtor)(exception*);
+static exception* (__thiscall *pbad_typeid_opequals)(exception*,exception*);
+static char* (__thiscall *pbad_typeid_what)(exception*);
+static vtable_ptr *pbad_typeid_vtable;
+static void (__thiscall *pbad_typeid_vector_dtor)(exception*,unsigned int);
+static void (__thiscall *pbad_typeid_scalar_dtor)(exception*,unsigned int);
 
 /* bad_cast */
-static void (WINAPI *pbad_cast_ctor)(exception*,LPCSTR*);
-static void (WINAPI *pbad_cast_ctor2)(exception*,LPCSTR);
-static void (WINAPI *pbad_cast_ctor_closure)(exception*);
-static void (WINAPI *pbad_cast_copy_ctor)(exception*,exception*);
-static void (WINAPI *pbad_cast_dtor)(exception*);
-static exception* (WINAPI *pbad_cast_opequals)(exception*,exception*);
-static char* (WINAPI *pbad_cast_what)(exception*);
-static void* (WINAPI *pbad_cast_vtable)(exception*);
-static void (WINAPI *pbad_cast_vector_dtor)(exception*,unsigned int);
-static void (WINAPI *pbad_cast_scalar_dtor)(exception*,unsigned int);
+static void (__thiscall *pbad_cast_ctor)(exception*,LPCSTR*);
+static void (__thiscall *pbad_cast_ctor2)(exception*,LPCSTR);
+static void (__thiscall *pbad_cast_ctor_closure)(exception*);
+static void (__thiscall *pbad_cast_copy_ctor)(exception*,exception*);
+static void (__thiscall *pbad_cast_dtor)(exception*);
+static exception* (__thiscall *pbad_cast_opequals)(exception*,exception*);
+static char* (__thiscall *pbad_cast_what)(exception*);
+static vtable_ptr *pbad_cast_vtable;
+static void (__thiscall *pbad_cast_vector_dtor)(exception*,unsigned int);
+static void (__thiscall *pbad_cast_scalar_dtor)(exception*,unsigned int);
 
 /* __non_rtti_object */
-static void (WINAPI *p__non_rtti_object_ctor)(exception*,LPCSTR);
-static void (WINAPI *p__non_rtti_object_copy_ctor)(exception*,exception*);
-static void (WINAPI *p__non_rtti_object_dtor)(exception*);
-static exception* (WINAPI *p__non_rtti_object_opequals)(exception*,exception*);
-static char* (WINAPI *p__non_rtti_object_what)(exception*);
-static void* (WINAPI *p__non_rtti_object_vtable)(exception*);
-static void (WINAPI *p__non_rtti_object_vector_dtor)(exception*,unsigned int);
-static void (WINAPI *p__non_rtti_object_scalar_dtor)(exception*,unsigned int);
+static void (__thiscall *p__non_rtti_object_ctor)(exception*,LPCSTR);
+static void (__thiscall *p__non_rtti_object_copy_ctor)(exception*,exception*);
+static void (__thiscall *p__non_rtti_object_dtor)(exception*);
+static exception* (__thiscall *p__non_rtti_object_opequals)(exception*,exception*);
+static char* (__thiscall *p__non_rtti_object_what)(exception*);
+static vtable_ptr *p__non_rtti_object_vtable;
+static void (__thiscall *p__non_rtti_object_vector_dtor)(exception*,unsigned int);
+static void (__thiscall *p__non_rtti_object_scalar_dtor)(exception*,unsigned int);
 
 /* type_info */
-static void  (WINAPI *ptype_info_dtor)(type_info*);
-static char* (WINAPI *ptype_info_raw_name)(type_info*);
-static char* (WINAPI *ptype_info_name)(type_info*);
-static int   (WINAPI *ptype_info_before)(type_info*,type_info*);
-static int   (WINAPI *ptype_info_opequals_equals)(type_info*,type_info*);
-static int   (WINAPI *ptype_info_opnot_equals)(type_info*,type_info*);
+static void  (__thiscall *ptype_info_dtor)(type_info*);
+static char* (__thiscall *ptype_info_raw_name)(type_info*);
+static char* (__thiscall *ptype_info_name)(type_info*);
+static int   (__thiscall *ptype_info_before)(type_info*,type_info*);
+static int   (__thiscall *ptype_info_opequals_equals)(type_info*,type_info*);
+static int   (__thiscall *ptype_info_opnot_equals)(type_info*,type_info*);
 
 /* RTTI */
 static type_info* (__cdecl *p__RTtypeid)(void*);
@@ -125,137 +116,69 @@ static char* (__cdecl *p__unDName)(char*,const char*,int,void*,void*,unsigned sh
 static void* bAncientVersion;
 
 /* Emulate a __thiscall */
-#ifdef _MSC_VER
-static inline void* do_call_func1(void *func, void *_this)
+#ifdef __i386__
+
+#include "pshpack1.h"
+struct thiscall_thunk
 {
-  volatile void* retval = 0;
-  __asm
-  {
-    push ecx
-    mov ecx, _this
-    call func
-    mov retval, eax
-    pop ecx
-  }
-  return (void*)retval;
-}
+    BYTE pop_eax;    /* popl  %eax (ret addr) */
+    BYTE pop_edx;    /* popl  %edx (func) */
+    BYTE pop_ecx;    /* popl  %ecx (this) */
+    BYTE push_eax;   /* pushl %eax */
+    WORD jmp_edx;    /* jmp  *%edx */
+};
+#include "poppack.h"
+
+static void * (WINAPI *call_thiscall_func1)( void *func, void *this );
+static void * (WINAPI *call_thiscall_func2)( void *func, void *this, const void *a );
 
-static inline void* do_call_func2(void *func, void *_this, const void* arg)
+static void init_thiscall_thunk(void)
 {
-  volatile void* retval = 0;
-  __asm
-  {
-    push ecx
-    push arg
-    mov ecx, _this
-    call func
-    mov retval, eax
-    pop ecx
-  }
-  return (void*)retval;
+    struct thiscall_thunk *thunk = VirtualAlloc( NULL, sizeof(*thunk),
+            MEM_COMMIT, PAGE_EXECUTE_READWRITE );
+    thunk->pop_eax  = 0x58;   /* popl  %eax */
+    thunk->pop_edx  = 0x5a;   /* popl  %edx */
+    thunk->pop_ecx  = 0x59;   /* popl  %ecx */
+    thunk->push_eax = 0x50;   /* pushl %eax */
+    thunk->jmp_edx  = 0xe2ff; /* jmp  *%edx */
+    call_thiscall_func1 = (void *)thunk;
+    call_thiscall_func2 = (void *)thunk;
 }
+
+#define call_func1(func,_this) call_thiscall_func1(func,_this)
+#define call_func2(func,_this,a) call_thiscall_func2(func,_this,(const void*)a)
+
 #else
-static void* do_call_func1(void *func, void *_this)
-{
-  void *ret, *dummy;
-  __asm__ __volatile__ ("call *%2"
-                        : "=a" (ret), "=c" (dummy)
-                        : "g" (func), "1" (_this)
-                        : "edx", "memory" );
-  return ret;
-}
-static void* do_call_func2(void *func, void *_this, const void* arg)
-{
-  void *ret, *dummy;
-  __asm__ __volatile__ ("pushl %3\n\tcall *%2"
-                        : "=a" (ret), "=c" (dummy)
-                        : "r" (func), "r" (arg), "1" (_this)
-                        : "edx", "memory" );
-  return ret;
-}
-#endif
 
-#define call_func1(x,y)   do_call_func1((void*)x,(void*)y)
-#define call_func2(x,y,z) do_call_func2((void*)x,(void*)y,(const void*)z)
+#define init_thiscall_thunk() do { } while(0)
+#define call_func1(func,_this) func(_this)
+#define call_func2(func,_this,a) func(_this,a)
+
+#endif /* __i386__ */
 
 /* Some exports are only available in later versions */
 #define SETNOFAIL(x,y) x = (void*)GetProcAddress(hMsvcrt,y)
 #define SET(x,y) do { SETNOFAIL(x,y); ok(x != NULL, "Export '%s' not found\n", y); } while(0)
 
-static void InitFunctionPtrs(void)
+static BOOL InitFunctionPtrs(void)
 {
-  hMsvcrt = GetModuleHandleA("msvcrt.dll");
-  if (!hMsvcrt)
-    hMsvcrt = GetModuleHandleA("msvcrtd.dll");
-  ok(hMsvcrt != 0, "GetModuleHandleA failed\n");
-  if (hMsvcrt)
-  {
-    if (sizeof(void *) > sizeof(int))  /* 64-bit has different names */
-    {
-        SETNOFAIL(poperator_new, "??_U@YAPEAX_K@Z");
-        SETNOFAIL(poperator_delete, "??_V@YAXPEAX@Z");
-    }
-    else
+    hMsvcrt = GetModuleHandleA("msvcrt.dll");
+    if (!hMsvcrt)
+        hMsvcrt = GetModuleHandleA("msvcrtd.dll");
+    ok(hMsvcrt != 0, "GetModuleHandleA failed\n");
+    if (!hMsvcrt)
     {
-        SETNOFAIL(poperator_new, "??_U@YAPAXI@Z");
-        SETNOFAIL(poperator_delete, "??_V@YAXPAX@Z");
+        win_skip("Could not load msvcrt.dll\n");
+        return FALSE;
     }
+
     SET(pmalloc, "malloc");
     SET(pfree, "free");
 
-    if (!poperator_new)
-      poperator_new = pmalloc;
-    if (!poperator_delete)
-      poperator_delete = pfree;
-
-    SET(pexception_ctor, "??0exception@@QAE@ABQBD@Z");
-    SET(pexception_copy_ctor, "??0exception@@QAE@ABV0@@Z");
-    SET(pexception_default_ctor, "??0exception@@QAE@XZ");
-    SET(pexception_dtor, "??1exception@@UAE@XZ");
-    SET(pexception_opequals, "??4exception@@QAEAAV0@ABV0@@Z");
-    SET(pexception_what, "?what@exception@@UBEPBDXZ");
     SET(pexception_vtable, "??_7exception@@6B@");
-    SET(pexception_vector_dtor, "??_Eexception@@UAEPAXI@Z");
-    SET(pexception_scalar_dtor, "??_Gexception@@UAEPAXI@Z");
-
-    SET(pbad_typeid_ctor, "??0bad_typeid@@QAE@PBD@Z");
-    SETNOFAIL(pbad_typeid_ctor_closure, "??_Fbad_typeid@@QAEXXZ");
-    SET(pbad_typeid_copy_ctor, "??0bad_typeid@@QAE@ABV0@@Z");
-    SET(pbad_typeid_dtor, "??1bad_typeid@@UAE@XZ");
-    SET(pbad_typeid_opequals, "??4bad_typeid@@QAEAAV0@ABV0@@Z");
-    SET(pbad_typeid_what, "?what@exception@@UBEPBDXZ");
     SET(pbad_typeid_vtable, "??_7bad_typeid@@6B@");
-    SET(pbad_typeid_vector_dtor, "??_Ebad_typeid@@UAEPAXI@Z");
-    SET(pbad_typeid_scalar_dtor, "??_Gbad_typeid@@UAEPAXI@Z");
-
-    SETNOFAIL(pbad_cast_ctor, "??0bad_cast@@QAE@ABQBD@Z");
-    if (!pbad_cast_ctor)
-      SET(pbad_cast_ctor, "??0bad_cast@@AAE@PBQBD@Z");
-    SETNOFAIL(pbad_cast_ctor2, "??0bad_cast@@QAE@PBD@Z");
-    SETNOFAIL(pbad_cast_ctor_closure, "??_Fbad_cast@@QAEXXZ");
-    SET(pbad_cast_copy_ctor, "??0bad_cast@@QAE@ABV0@@Z");
-    SET(pbad_cast_dtor, "??1bad_cast@@UAE@XZ");
-    SET(pbad_cast_opequals, "??4bad_cast@@QAEAAV0@ABV0@@Z");
-    SET(pbad_cast_what, "?what@exception@@UBEPBDXZ");
     SET(pbad_cast_vtable, "??_7bad_cast@@6B@");
-    SET(pbad_cast_vector_dtor, "??_Ebad_cast@@UAEPAXI@Z");
-    SET(pbad_cast_scalar_dtor, "??_Gbad_cast@@UAEPAXI@Z");
-
-    SET(p__non_rtti_object_ctor, "??0__non_rtti_object@@QAE@PBD@Z");
-    SET(p__non_rtti_object_copy_ctor, "??0__non_rtti_object@@QAE@ABV0@@Z");
-    SET(p__non_rtti_object_dtor, "??1__non_rtti_object@@UAE@XZ");
-    SET(p__non_rtti_object_opequals, "??4__non_rtti_object@@QAEAAV0@ABV0@@Z");
-    SET(p__non_rtti_object_what, "?what@exception@@UBEPBDXZ");
     SET(p__non_rtti_object_vtable, "??_7__non_rtti_object@@6B@");
-    SET(p__non_rtti_object_vector_dtor, "??_E__non_rtti_object@@UAEPAXI@Z");
-    SET(p__non_rtti_object_scalar_dtor, "??_G__non_rtti_object@@UAEPAXI@Z");
-
-    SET(ptype_info_dtor, "??1type_info@@UAE@XZ");
-    SET(ptype_info_raw_name, "?raw_name@type_info@@QBEPBDXZ");
-    SET(ptype_info_name, "?name@type_info@@QBEPBDXZ");
-    SET(ptype_info_before, "?before@type_info@@QBEHABV1@@Z");
-    SET(ptype_info_opequals_equals, "??8type_info@@QBEHABV0@@Z");
-    SET(ptype_info_opnot_equals, "??9type_info@@QBEHABV0@@Z");
 
     SET(p__RTtypeid, "__RTtypeid");
     SET(p__RTCastToVoid, "__RTCastToVoid");
@@ -265,7 +188,112 @@ static void InitFunctionPtrs(void)
 
     /* Extremely early versions export logic_error, and crash in RTTI */
     SETNOFAIL(bAncientVersion, "??0logic_error@@QAE@ABQBD@Z");
-  }
+    if (sizeof(void *) > sizeof(int))  /* 64-bit initialization */
+    {
+        SETNOFAIL(poperator_new, "??_U@YAPEAX_K@Z");
+        SETNOFAIL(poperator_delete, "??_V@YAXPEAX@Z");
+
+        SET(pexception_ctor, "??0exception@@QEAA@AEBQEBD@Z");
+        SET(pexception_copy_ctor, "??0exception@@QEAA@AEBV0@@Z");
+        SET(pexception_default_ctor, "??0exception@@QEAA@XZ");
+        SET(pexception_dtor, "??1exception@@UEAA@XZ");
+        SET(pexception_opequals, "??4exception@@QEAAAEAV0@AEBV0@@Z");
+        SET(pexception_what, "?what@exception@@UEBAPEBDXZ");
+        pexception_vector_dtor = (void*)pexception_vtable[0];
+        pexception_scalar_dtor = (void*)pexception_vtable[0];
+
+        SET(pbad_typeid_ctor, "??0bad_typeid@@QEAA@PEBD@Z");
+        SETNOFAIL(pbad_typeid_ctor_closure, "??_Fbad_typeid@@QEAAXXZ");
+        SET(pbad_typeid_copy_ctor, "??0bad_typeid@@QEAA@AEBV0@@Z");
+        SET(pbad_typeid_dtor, "??1bad_typeid@@UEAA@XZ");
+        SET(pbad_typeid_opequals, "??4bad_typeid@@QEAAAEAV0@AEBV0@@Z");
+        SET(pbad_typeid_what, "?what@exception@@UEBAPEBDXZ");
+        pbad_typeid_vector_dtor = (void*)pbad_typeid_vtable[0];
+        pbad_typeid_scalar_dtor = (void*)pbad_typeid_vtable[0];
+
+        SET(pbad_cast_ctor, "??0bad_cast@@QEAA@AEBQEBD@Z");
+        SET(pbad_cast_ctor2, "??0bad_cast@@QEAA@PEBD@Z");
+        SET(pbad_cast_ctor_closure, "??_Fbad_cast@@QEAAXXZ");
+        SET(pbad_cast_copy_ctor, "??0bad_cast@@QEAA@AEBV0@@Z");
+        SET(pbad_cast_dtor, "??1bad_cast@@UEAA@XZ");
+        SET(pbad_cast_opequals, "??4bad_cast@@QEAAAEAV0@AEBV0@@Z");
+        SET(pbad_cast_what, "?what@exception@@UEBAPEBDXZ");
+        pbad_cast_vector_dtor = (void*)pbad_cast_vtable[0];
+        pbad_cast_scalar_dtor = (void*)pbad_cast_vtable[0];
+
+        SET(p__non_rtti_object_ctor, "??0__non_rtti_object@@QEAA@PEBD@Z");
+        SET(p__non_rtti_object_copy_ctor, "??0__non_rtti_object@@QEAA@AEBV0@@Z");
+        SET(p__non_rtti_object_dtor, "??1__non_rtti_object@@UEAA@XZ");
+        SET(p__non_rtti_object_opequals, "??4__non_rtti_object@@QEAAAEAV0@AEBV0@@Z");
+        SET(p__non_rtti_object_what, "?what@exception@@UEBAPEBDXZ");
+        p__non_rtti_object_vector_dtor = (void*)p__non_rtti_object_vtable[0];
+        p__non_rtti_object_scalar_dtor = (void*)p__non_rtti_object_vtable[0];
+
+        SET(ptype_info_dtor, "??1type_info@@UEAA@XZ");
+        SET(ptype_info_raw_name, "?raw_name@type_info@@QEBAPEBDXZ");
+        SET(ptype_info_name, "?name@type_info@@QEBAPEBDXZ");
+        SET(ptype_info_before, "?before@type_info@@QEBAHAEBV1@@Z");
+        SET(ptype_info_opequals_equals, "??8type_info@@QEBAHAEBV0@@Z");
+        SET(ptype_info_opnot_equals, "??9type_info@@QEBAHAEBV0@@Z");
+    }
+    else
+    {
+        SETNOFAIL(poperator_new, "??_U@YAPAXI@Z");
+        SETNOFAIL(poperator_delete, "??_V@YAXPAX@Z");
+
+        SET(pexception_ctor, "??0exception@@QAE@ABQBD@Z");
+        SET(pexception_copy_ctor, "??0exception@@QAE@ABV0@@Z");
+        SET(pexception_default_ctor, "??0exception@@QAE@XZ");
+        SET(pexception_dtor, "??1exception@@UAE@XZ");
+        SET(pexception_opequals, "??4exception@@QAEAAV0@ABV0@@Z");
+        SET(pexception_what, "?what@exception@@UBEPBDXZ");
+        SET(pexception_vector_dtor, "??_Eexception@@UAEPAXI@Z");
+        SET(pexception_scalar_dtor, "??_Gexception@@UAEPAXI@Z");
+
+        SET(pbad_typeid_ctor, "??0bad_typeid@@QAE@PBD@Z");
+        SETNOFAIL(pbad_typeid_ctor_closure, "??_Fbad_typeid@@QAEXXZ");
+        SET(pbad_typeid_copy_ctor, "??0bad_typeid@@QAE@ABV0@@Z");
+        SET(pbad_typeid_dtor, "??1bad_typeid@@UAE@XZ");
+        SET(pbad_typeid_opequals, "??4bad_typeid@@QAEAAV0@ABV0@@Z");
+        SET(pbad_typeid_what, "?what@exception@@UBEPBDXZ");
+        SET(pbad_typeid_vector_dtor, "??_Ebad_typeid@@UAEPAXI@Z");
+        SET(pbad_typeid_scalar_dtor, "??_Gbad_typeid@@UAEPAXI@Z");
+
+        SETNOFAIL(pbad_cast_ctor, "??0bad_cast@@QAE@ABQBD@Z");
+        if (!pbad_cast_ctor)
+            SET(pbad_cast_ctor, "??0bad_cast@@AAE@PBQBD@Z");
+        SETNOFAIL(pbad_cast_ctor2, "??0bad_cast@@QAE@PBD@Z");
+        SETNOFAIL(pbad_cast_ctor_closure, "??_Fbad_cast@@QAEXXZ");
+        SET(pbad_cast_copy_ctor, "??0bad_cast@@QAE@ABV0@@Z");
+        SET(pbad_cast_dtor, "??1bad_cast@@UAE@XZ");
+        SET(pbad_cast_opequals, "??4bad_cast@@QAEAAV0@ABV0@@Z");
+        SET(pbad_cast_what, "?what@exception@@UBEPBDXZ");
+        SET(pbad_cast_vector_dtor, "??_Ebad_cast@@UAEPAXI@Z");
+        SET(pbad_cast_scalar_dtor, "??_Gbad_cast@@UAEPAXI@Z");
+
+        SET(p__non_rtti_object_ctor, "??0__non_rtti_object@@QAE@PBD@Z");
+        SET(p__non_rtti_object_copy_ctor, "??0__non_rtti_object@@QAE@ABV0@@Z");
+        SET(p__non_rtti_object_dtor, "??1__non_rtti_object@@UAE@XZ");
+        SET(p__non_rtti_object_opequals, "??4__non_rtti_object@@QAEAAV0@ABV0@@Z");
+        SET(p__non_rtti_object_what, "?what@exception@@UBEPBDXZ");
+        SET(p__non_rtti_object_vector_dtor, "??_E__non_rtti_object@@UAEPAXI@Z");
+        SET(p__non_rtti_object_scalar_dtor, "??_G__non_rtti_object@@UAEPAXI@Z");
+
+        SET(ptype_info_dtor, "??1type_info@@UAE@XZ");
+        SET(ptype_info_raw_name, "?raw_name@type_info@@QBEPBDXZ");
+        SET(ptype_info_name, "?name@type_info@@QBEPBDXZ");
+        SET(ptype_info_before, "?before@type_info@@QBEHABV1@@Z");
+        SET(ptype_info_opequals_equals, "??8type_info@@QBEHABV0@@Z");
+        SET(ptype_info_opnot_equals, "??9type_info@@QBEHABV0@@Z");
+    }
+
+    if (!poperator_new)
+        poperator_new = pmalloc;
+    if (!poperator_delete)
+        poperator_delete = pfree;
+
+    init_thiscall_thunk();
+    return TRUE;
 }
 
 static void test_exception(void)
@@ -346,14 +374,14 @@ static void test_exception(void)
     call_func2(pexception_vector_dtor, pe, 1); /* Should delete pe as single element*/
   }
 
-  pe = poperator_new(sizeof(exception) * 4 + sizeof(int));
+  pe = poperator_new(sizeof(exception) * 4 + sizeof(size_t));
   ok(pe != NULL, "new() failed\n");
   if (pe)
   {
     /* vector dtor, multiple elements */
     char name[] = "a constant";
-    *((int*)pe) = 3;
-    pe = (exception*)((int*)pe + 1);
+    *((size_t*)pe) = 3;
+    pe = (exception*)((size_t*)pe + 1);
     call_func2(pexception_ctor, &pe[0], &e_name);
     call_func2(pexception_ctor, &pe[1], &e_name);
     call_func2(pexception_ctor, &pe[2], &e_name);
@@ -471,13 +499,13 @@ static void test_bad_typeid(void)
     call_func2(pbad_typeid_vector_dtor, pe, 1); /* Should delete pe as single element*/
   }
 
-  pe = poperator_new(sizeof(exception) * 4 + sizeof(int));
+  pe = poperator_new(sizeof(exception) * 4 + sizeof(size_t));
   ok(pe != NULL, "new() failed\n");
   if (pe)
   {
     /* vector dtor, multiple elements */
-    *((int*)pe) = 3;
-    pe = (exception*)((int*)pe + 1);
+    *((size_t*)pe) = 3;
+    pe = (exception*)((size_t*)pe + 1);
     call_func2(pbad_typeid_ctor, &pe[0], e_name);
     call_func2(pbad_typeid_ctor, &pe[1], e_name);
     call_func2(pbad_typeid_ctor, &pe[2], e_name);
@@ -599,13 +627,13 @@ static void test_bad_cast(void)
     call_func2(pbad_cast_vector_dtor, pe, 1); /* Should delete pe as single element*/
   }
 
-  pe = poperator_new(sizeof(exception) * 4 + sizeof(int));
+  pe = poperator_new(sizeof(exception) * 4 + sizeof(size_t));
   ok(pe != NULL, "new() failed\n");
   if (pe)
   {
     /* vector dtor, multiple elements */
-    *((int*)pe) = 3;
-    pe = (exception*)((int*)pe + 1);
+    *((size_t*)pe) = 3;
+    pe = (exception*)((size_t*)pe + 1);
     call_func2(pbad_cast_ctor, &pe[0], &e_name);
     call_func2(pbad_cast_ctor, &pe[1], &e_name);
     call_func2(pbad_cast_ctor, &pe[2], &e_name);
@@ -701,13 +729,13 @@ static void test___non_rtti_object(void)
     call_func2(p__non_rtti_object_vector_dtor, pe, 1); /* Should delete pe as single element*/
   }
 
-  pe = poperator_new(sizeof(exception) * 4 + sizeof(int));
+  pe = poperator_new(sizeof(exception) * 4 + sizeof(size_t));
   ok(pe != NULL, "new() failed\n");
   if (pe)
   {
     /* vector dtor, multiple elements */
-    *((int*)pe) = 3;
-    pe = (exception*)((int*)pe + 1);
+    *((size_t*)pe) = 3;
+    pe = (exception*)((size_t*)pe + 1);
     call_func2(p__non_rtti_object_ctor, &pe[0], e_name);
     call_func2(p__non_rtti_object_ctor, &pe[1], e_name);
     call_func2(p__non_rtti_object_ctor, &pe[2], e_name);
@@ -733,7 +761,6 @@ static void test___non_rtti_object(void)
   call_func2(p__non_rtti_object_vector_dtor, &e, 0); /* Should delete e.name, but not e */
 }
 
-
 static void test_type_info(void)
 {
   static type_info t1 = { NULL, NULL,{'.','?','A','V','t','e','s','t','1','@','@',0,0,0,0,0 } };
@@ -801,13 +828,99 @@ static void test_type_info(void)
   ok(res == 1, "expected 1, got %d\n", res);
 }
 
+static inline vtable_ptr *get_vtable( void *obj )
+{
+    return *(vtable_ptr **)obj;
+}
+
+static inline void/*rtti_object_locator*/ *get_obj_locator( void *cppobj )
+{
+    const vtable_ptr *vtable = get_vtable( cppobj );
+    return (void *)vtable[-1];
+}
+
+#ifndef __x86_64__
+#define DEFINE_RTTI_REF(type, name) type *name
+#define RTTI_REF(instance, name) &instance.name
+#define RTTI_REF_SIG0(instance, name, base) RTTI_REF(instance, name)
+#else
+#define DEFINE_RTTI_REF(type, name) unsigned name
+#define RTTI_REF(instance, name) FIELD_OFFSET(struct rtti_data, name)
+#define RTTI_REF_SIG0(instance, name, base) ((char*)&instance.name-base)
+#endif
 /* Test RTTI functions */
 static void test_rtti(void)
 {
+  struct _object_locator
+  {
+      unsigned int signature;
+      int base_class_offset;
+      unsigned int flags;
+      DEFINE_RTTI_REF(type_info, type_descriptor);
+      DEFINE_RTTI_REF(struct _rtti_object_hierarchy, type_hierarchy);
+      DEFINE_RTTI_REF(void, object_locator);
+  } *obj_locator;
+
+  struct rtti_data
+  {
+    type_info type_info[4];
+
+    struct _rtti_base_descriptor
+    {
+      DEFINE_RTTI_REF(type_info, type_descriptor);
+      int num_base_classes;
+      struct {
+        int this_offset;
+        int vbase_descr;
+        int vbase_offset;
+      } this_ptr_offsets;
+      unsigned int attributes;
+    } base_descriptor[4];
+
+    struct _rtti_base_array {
+      DEFINE_RTTI_REF(struct _rtti_base_descriptor, bases[4]);
+    } base_array;
+
+    struct _rtti_object_hierarchy {
+      unsigned int signature;
+      unsigned int attributes;
+      int array_len;
+      DEFINE_RTTI_REF(struct _rtti_base_array, base_classes);
+    } object_hierarchy;
+
+    struct _object_locator object_locator;
+  } simple_class_rtti = {
+    { {NULL, NULL, "simple_class"} },
+    { {RTTI_REF(simple_class_rtti, type_info[0]), 0, {0, 0, 0}, 0} },
+    { {RTTI_REF(simple_class_rtti, base_descriptor[0])} },
+    {0, 0, 1, RTTI_REF(simple_class_rtti, base_array)},
+    {1, 0, 0, RTTI_REF(simple_class_rtti, type_info[0]), RTTI_REF(simple_class_rtti, object_hierarchy), RTTI_REF(simple_class_rtti, object_locator)}
+  }, child_class_rtti = {
+    { {NULL, NULL, "simple_class"}, {NULL, NULL, "child_class"} },
+    { {RTTI_REF(child_class_rtti, type_info[1]), 0, {4, -1, 0}, 0}, {RTTI_REF(child_class_rtti, type_info[0]), 0, {8, -1, 0}, 0} },
+    { {RTTI_REF(child_class_rtti, base_descriptor[0]), RTTI_REF(child_class_rtti, base_descriptor[1])} },
+    {0, 0, 2, RTTI_REF(child_class_rtti, base_array)},
+    {1, 0, 0, RTTI_REF(child_class_rtti, type_info[1]), RTTI_REF(child_class_rtti, object_hierarchy), RTTI_REF(child_class_rtti, object_locator)}
+  };
+  static struct rtti_data simple_class_sig0_rtti, child_class_sig0_rtti;
+
+  void *simple_class_vtbl[2] = {&simple_class_rtti.object_locator};
+  void *simple_class = &simple_class_vtbl[1];
+  void *child_class_vtbl[2] = {&child_class_rtti.object_locator};
+  void *child_class = &child_class_vtbl[1];
+  void *simple_class_sig0_vtbl[2] = {&simple_class_sig0_rtti.object_locator};
+  void *simple_class_sig0 = &simple_class_sig0_vtbl[1];
+  void *child_class_sig0_vtbl[2] = {&child_class_sig0_rtti.object_locator};
+  void *child_class_sig0 = &child_class_sig0_vtbl[1];
+
   static const char* e_name = "name";
   type_info *ti,*bti;
   exception e,b;
   void *casted;
+  BOOL old_signature;
+#ifdef __x86_64__
+  char *base = (char*)GetModuleHandleW(NULL);
+#endif
 
   if (bAncientVersion ||
       !p__RTCastToVoid || !p__RTtypeid || !pexception_ctor || !pbad_typeid_ctor
@@ -817,6 +930,12 @@ static void test_rtti(void)
   call_func2(pexception_ctor, &e, &e_name);
   call_func2(pbad_typeid_ctor, &b, e_name);
 
+  obj_locator = get_obj_locator(&e);
+  if(obj_locator->signature!=1 && sizeof(void*)>sizeof(int))
+    old_signature = TRUE;
+  else
+    old_signature = FALSE;
+
   /* dynamic_cast to void* */
   casted = p__RTCastToVoid(&e);
   ok (casted == (void*)&e, "failed cast to void\n");
@@ -838,6 +957,75 @@ static void test_rtti(void)
 
   call_func1(pexception_dtor, &e);
   call_func1(pbad_typeid_dtor, &b);
+
+  memcpy(&simple_class_sig0_rtti, &simple_class_rtti, sizeof(struct rtti_data));
+  simple_class_sig0_rtti.object_locator.signature = 0;
+  simple_class_sig0_rtti.base_descriptor[0].type_descriptor = RTTI_REF_SIG0(simple_class_sig0_rtti, type_info[0], base);
+  simple_class_sig0_rtti.base_array.bases[0] = RTTI_REF_SIG0(simple_class_sig0_rtti, base_descriptor[0], base);
+  simple_class_sig0_rtti.object_hierarchy.base_classes = RTTI_REF_SIG0(simple_class_sig0_rtti, base_array, base);
+  simple_class_sig0_rtti.object_locator.type_descriptor = RTTI_REF_SIG0(simple_class_sig0_rtti, type_info[0], base);
+  simple_class_sig0_rtti.object_locator.type_hierarchy = RTTI_REF_SIG0(simple_class_sig0_rtti, object_hierarchy, base);
+
+  memcpy(&child_class_sig0_rtti, &child_class_rtti, sizeof(struct rtti_data));
+  child_class_sig0_rtti.object_locator.signature = 0;
+  child_class_sig0_rtti.base_descriptor[0].type_descriptor = RTTI_REF_SIG0(child_class_sig0_rtti, type_info[1], base);
+  child_class_sig0_rtti.base_descriptor[1].type_descriptor = RTTI_REF_SIG0(child_class_sig0_rtti, type_info[0], base);
+  child_class_sig0_rtti.base_array.bases[0] = RTTI_REF_SIG0(child_class_sig0_rtti, base_descriptor[0], base);
+  child_class_sig0_rtti.base_array.bases[1] = RTTI_REF_SIG0(child_class_sig0_rtti, base_descriptor[1], base);
+  child_class_sig0_rtti.object_hierarchy.base_classes = RTTI_REF_SIG0(child_class_sig0_rtti, base_array, base);
+  child_class_sig0_rtti.object_locator.type_descriptor = RTTI_REF_SIG0(child_class_sig0_rtti, type_info[1], base);
+  child_class_sig0_rtti.object_locator.type_hierarchy = RTTI_REF_SIG0(child_class_sig0_rtti, object_hierarchy, base);
+
+  ti = p__RTtypeid(&simple_class_sig0);
+  ok (ti && ti->mangled && !strcmp(ti->mangled, "simple_class"),
+          "incorrect rtti data\n");
+
+  casted = p__RTCastToVoid(&simple_class_sig0);
+  ok (casted == (void*)&simple_class_sig0, "failed cast to void\n");
+
+  ti = p__RTtypeid(&child_class_sig0);
+  ok (ti && ti->mangled && !strcmp(ti->mangled, "child_class"),
+          "incorrect rtti data\n");
+
+  casted = p__RTCastToVoid(&child_class_sig0);
+  ok (casted == (void*)&child_class_sig0, "failed cast to void\n");
+
+  casted = p__RTDynamicCast(&child_class_sig0, 0, NULL, simple_class_sig0_rtti.type_info, 0);
+  if(casted)
+  {
+      ok (casted == (char*)&child_class_sig0+8, "failed cast to simple_class (%p %p)\n", casted, &child_class_sig0);
+  }
+
+  casted = p__RTDynamicCast(&child_class_sig0, 0, &child_class_sig0_rtti.type_info[0], &child_class_sig0_rtti.type_info[1], 0);
+  ok(casted == (char*)&child_class_sig0+4, "failed cast to child class (%p %p)\n", casted, &child_class_sig0);
+
+  if(old_signature) {
+      skip("signature==1 is not supported\n");
+      return;
+  }
+
+  ti = p__RTtypeid(&simple_class);
+  ok (ti && ti->mangled && !strcmp(ti->mangled, "simple_class"),
+          "incorrect rtti data\n");
+
+  casted = p__RTCastToVoid(&simple_class);
+  ok (casted == (void*)&simple_class, "failed cast to void\n");
+
+  ti = p__RTtypeid(&child_class);
+  ok (ti && ti->mangled && !strcmp(ti->mangled, "child_class"),
+        "incorrect rtti data\n");
+
+  casted = p__RTCastToVoid(&child_class);
+  ok (casted == (void*)&child_class, "failed cast to void\n");
+
+  casted = p__RTDynamicCast(&child_class, 0, NULL, simple_class_rtti.type_info, 0);
+  if(casted)
+  {
+    ok (casted == (char*)&child_class+8, "failed cast to simple_class (%p %p)\n", casted, &child_class);
+  }
+
+  casted = p__RTDynamicCast(&child_class, 0, &child_class_rtti.type_info[0], &child_class_rtti.type_info[1], 0);
+  ok(casted == (char*)&child_class+4, "failed cast to child class (%p %p)\n", casted, &child_class);
 }
 
 struct _demangle {
@@ -1081,7 +1269,8 @@ static void test_demangle(void)
 
 START_TEST(cpp)
 {
-  InitFunctionPtrs();
+  if (!InitFunctionPtrs())
+    return;
 
   test_exception();
   test_bad_typeid();
@@ -1092,4 +1281,3 @@ START_TEST(cpp)
   test_demangle_datatype();
   test_demangle();
 }
-#endif /* __i386__ */
index e7b7353..e450b72 100644 (file)
@@ -434,6 +434,37 @@ static void test_asciimode2(void)
     unlink("ascii2.tst");
 }
 
+static void test_filemodeT(void)
+{
+    char DATA  [] = {26, 't', 'e', 's' ,'t'};
+    char DATA2 [100];
+    char temppath[MAX_PATH];
+    char tempfile[MAX_PATH];
+    FILE* f;
+    size_t bytesWritten;
+    size_t bytesRead;
+    WIN32_FIND_DATA findData;
+    HANDLE h;
+
+    GetTempPath (MAX_PATH, temppath);
+    GetTempFileName (temppath, "", 0, tempfile);
+
+    f = fopen(tempfile, "w+bDT");
+    bytesWritten = fwrite(DATA, 1, sizeof(DATA), f);
+    rewind(f);
+    bytesRead = fread(DATA2, 1, sizeof(DATA2), f);
+    fclose(f);
+
+    ok (bytesRead == bytesWritten && bytesRead == sizeof(DATA),
+        "fopen file mode 'T' wrongly interpreted as 't'\n" );
+
+    h = FindFirstFile(tempfile, &findData);
+
+    ok (h == INVALID_HANDLE_VALUE, "file wasn't deleted when closed.\n" );
+
+    if (h != INVALID_HANDLE_VALUE) FindClose(h);
+}
+
 static WCHAR* AtoW( const char* p )
 {
     WCHAR* buffer;
@@ -544,10 +575,21 @@ static void test_flsbuf( void )
                          bufmodes[bufmode], 0, ret);
     ret = _flsbuf(0xff,tempfh);
     ok(0xff == ret, "_flsbuf(0xff,tempfh) with bufmode %x expected %x got %x\n",
-                         bufmodes[bufmode], 0, ret);
+                         bufmodes[bufmode], 0xff, ret);
     ret = _flsbuf(0xffffffff,tempfh);
     ok(0xff == ret, "_flsbuf(0xffffffff,tempfh) with bufmode %x expected %x got %x\n",
-                         bufmodes[bufmode], 0, ret);
+                         bufmodes[bufmode], 0xff, ret);
+    if(tempfh->_base) {
+        fputc('x', tempfh);
+        tempfh->_cnt = -1;
+        tempfh->_base[1] = 'a';
+        ret = _flsbuf(0xab,tempfh);
+        ok(ret == 0xab, "_flsbuf(0xab,tempfh) with bufmode %x expected 0xab got %x\n",
+                bufmodes[bufmode], ret);
+        ok(tempfh->_base[1] == 'a', "tempfh->_base[1] should not be changed (%d)\n",
+                tempfh->_base[1]);
+    }
+
     fclose(tempfh);
   }
 
@@ -580,6 +622,58 @@ static void test_flsbuf( void )
   free(tempf);
 }
 
+static void test_fflush( void )
+{
+  static const char obuf[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+  char buf1[16], buf2[24];
+  char *tempf;
+  FILE *tempfh;
+  int ret;
+
+  tempf=_tempnam(".","wne");
+
+  /* Prepare the file. */
+  tempfh = fopen(tempf,"wb");
+  ok(tempfh != NULL, "Can't open test file.\n");
+  fwrite(obuf, 1, sizeof(obuf), tempfh);
+  fclose(tempfh);
+
+  /* Open the file for input. */
+  tempfh = fopen(tempf,"rb");
+  ok(tempfh != NULL, "Can't open test file.\n");
+  fread(buf1, 1, sizeof(buf1), tempfh);
+
+  /* Using fflush() on input stream is undefined in ANSI.
+   * But MSDN says that it clears input buffer. */
+  _lseek(_fileno(tempfh), 0, SEEK_SET);
+  ret = fflush(tempfh);
+  ok(ret == 0, "expected 0, got %d\n", ret);
+  memset(buf2, '?', sizeof(buf2));
+  fread(buf2, 1, sizeof(buf2), tempfh);
+  ok(memcmp(buf1, buf2, sizeof(buf1)) == 0, "Got unexpected data (%c)\n", buf2[0]);
+
+  /* fflush(NULL) doesn't clear input buffer. */
+  _lseek(_fileno(tempfh), 0, SEEK_SET);
+  ret = fflush(NULL);
+  ok(ret == 0, "expected 0, got %d\n", ret);
+  memset(buf2, '?', sizeof(buf2));
+  fread(buf2, 1, sizeof(buf2), tempfh);
+  ok(memcmp(buf1, buf2, sizeof(buf1)) != 0, "Got unexpected data (%c)\n", buf2[0]);
+
+  /* _flushall() clears input buffer. */
+  _lseek(_fileno(tempfh), 0, SEEK_SET);
+  ret = _flushall();
+  ok(ret >= 0, "unexpected ret %d\n", ret);
+  memset(buf2, '?', sizeof(buf2));
+  fread(buf2, 1, sizeof(buf2), tempfh);
+  ok(memcmp(buf1, buf2, sizeof(buf1)) == 0, "Got unexpected data (%c)\n", buf2[0]);
+
+  fclose(tempfh);
+
+  unlink(tempf);
+  free(tempf);
+}
+
 static void test_fgetwc( void )
 {
 #define LLEN 512
@@ -1565,12 +1659,14 @@ START_TEST(file)
     test_fileops();
     test_asciimode();
     test_asciimode2();
+    test_filemodeT();
     test_readmode(FALSE); /* binary mode */
     test_readmode(TRUE);  /* ascii mode */
     test_readboundary();
     test_fgetc();
     test_fputc();
     test_flsbuf();
+    test_fflush();
     test_fgetwc();
     test_ctrlz();
     test_file_put_get();
index 0e8d0dd..225cbb9 100644 (file)
@@ -25,6 +25,7 @@
 
 static BOOL (__cdecl *p__crtGetStringTypeW)(DWORD, DWORD, const wchar_t*, int, WORD*);
 static int (__cdecl *pmemcpy_s)(void *, size_t, void*, size_t);
+void* __cdecl _Gettnames(void);
 
 static void init(void)
 {
@@ -471,10 +472,11 @@ static void test_setlocale(void)
     if(ret)
         ok(!strcmp(ret, "Polish_Poland.1250"), "ret = %s\n", ret);
 
-    ret = setlocale(LC_ALL, "portugese");
+    ret = setlocale(LC_ALL, "portuguese");
     ok(ret != NULL || broken (ret == NULL), "ret == NULL\n");
     if(ret)
-        ok(!strcmp(ret, "Portuguese_Brazil.1252"), "ret = %s\n", ret);
+        ok(!strcmp(ret, "Portuguese_Brazil.1252")
+        || broken(!strcmp(ret, "Portuguese_Portugal.1252")) /* NT4 */, "ret = %s\n", ret);
 
     ret = setlocale(LC_ALL, "portuguese-brazil");
     ok(ret != NULL || broken (ret == NULL), "ret == NULL\n");
@@ -616,10 +618,131 @@ static void test_crtGetStringTypeW(void)
     ok(!ret, "ret == TRUE\n");
 }
 
+static void test__Gettnames(void)
+{
+    struct {
+        char *str[43];
+        LCID lcid;
+        int  unk[2];
+        wchar_t *wstr[43];
+        char data[1];
+    } *ret;
+    int size;
+
+    if(!setlocale(LC_ALL, "english"))
+        return;
+
+    ret = _Gettnames();
+    size = ret->data-(char*)ret;
+    /* Newer version of the structure stores both ascii and unicode strings.
+     * Unicode strings are only initialized on Windows 7
+     */
+    if(sizeof(void*) == 8)
+        ok(size==0x2c0 || broken(size==0x170), "strucure size: %x\n", size);
+    else
+        ok(size==0x164 || broken(size==0xb8), "strucure size: %x\n", size);
+
+    ok(!strcmp(ret->str[0], "Sun"), "ret->str[0] = %s\n", ret->str[0]);
+    ok(!strcmp(ret->str[1], "Mon"), "ret->str[1] = %s\n", ret->str[1]);
+    ok(!strcmp(ret->str[2], "Tue"), "ret->str[2] = %s\n", ret->str[2]);
+    ok(!strcmp(ret->str[3], "Wed"), "ret->str[3] = %s\n", ret->str[3]);
+    ok(!strcmp(ret->str[4], "Thu"), "ret->str[4] = %s\n", ret->str[4]);
+    ok(!strcmp(ret->str[5], "Fri"), "ret->str[5] = %s\n", ret->str[5]);
+    ok(!strcmp(ret->str[6], "Sat"), "ret->str[6] = %s\n", ret->str[6]);
+    ok(!strcmp(ret->str[7], "Sunday"), "ret->str[7] = %s\n", ret->str[7]);
+    ok(!strcmp(ret->str[8], "Monday"), "ret->str[8] = %s\n", ret->str[8]);
+    ok(!strcmp(ret->str[9], "Tuesday"), "ret->str[9] = %s\n", ret->str[9]);
+    ok(!strcmp(ret->str[10], "Wednesday"), "ret->str[10] = %s\n", ret->str[10]);
+    ok(!strcmp(ret->str[11], "Thursday"), "ret->str[11] = %s\n", ret->str[11]);
+    ok(!strcmp(ret->str[12], "Friday"), "ret->str[12] = %s\n", ret->str[12]);
+    ok(!strcmp(ret->str[13], "Saturday"), "ret->str[13] = %s\n", ret->str[13]);
+    ok(!strcmp(ret->str[14], "Jan"), "ret->str[14] = %s\n", ret->str[14]);
+    ok(!strcmp(ret->str[15], "Feb"), "ret->str[15] = %s\n", ret->str[15]);
+    ok(!strcmp(ret->str[16], "Mar"), "ret->str[16] = %s\n", ret->str[16]);
+    ok(!strcmp(ret->str[17], "Apr"), "ret->str[17] = %s\n", ret->str[17]);
+    ok(!strcmp(ret->str[18], "May"), "ret->str[18] = %s\n", ret->str[18]);
+    ok(!strcmp(ret->str[19], "Jun"), "ret->str[19] = %s\n", ret->str[19]);
+    ok(!strcmp(ret->str[20], "Jul"), "ret->str[20] = %s\n", ret->str[20]);
+    ok(!strcmp(ret->str[21], "Aug"), "ret->str[21] = %s\n", ret->str[21]);
+    ok(!strcmp(ret->str[22], "Sep"), "ret->str[22] = %s\n", ret->str[22]);
+    ok(!strcmp(ret->str[23], "Oct"), "ret->str[23] = %s\n", ret->str[23]);
+    ok(!strcmp(ret->str[24], "Nov"), "ret->str[24] = %s\n", ret->str[24]);
+    ok(!strcmp(ret->str[25], "Dec"), "ret->str[25] = %s\n", ret->str[25]);
+    ok(!strcmp(ret->str[26], "January"), "ret->str[26] = %s\n", ret->str[26]);
+    ok(!strcmp(ret->str[27], "February"), "ret->str[27] = %s\n", ret->str[27]);
+    ok(!strcmp(ret->str[28], "March"), "ret->str[28] = %s\n", ret->str[28]);
+    ok(!strcmp(ret->str[29], "April"), "ret->str[29] = %s\n", ret->str[29]);
+    ok(!strcmp(ret->str[30], "May"), "ret->str[30] = %s\n", ret->str[30]);
+    ok(!strcmp(ret->str[31], "June"), "ret->str[31] = %s\n", ret->str[31]);
+    ok(!strcmp(ret->str[32], "July"), "ret->str[32] = %s\n", ret->str[32]);
+    ok(!strcmp(ret->str[33], "August"), "ret->str[33] = %s\n", ret->str[33]);
+    ok(!strcmp(ret->str[34], "September"), "ret->str[34] = %s\n", ret->str[34]);
+    ok(!strcmp(ret->str[35], "October"), "ret->str[35] = %s\n", ret->str[35]);
+    ok(!strcmp(ret->str[36], "November"), "ret->str[36] = %s\n", ret->str[36]);
+    ok(!strcmp(ret->str[37], "December"), "ret->str[37] = %s\n", ret->str[37]);
+    ok(!strcmp(ret->str[38], "AM"), "ret->str[38] = %s\n", ret->str[38]);
+    ok(!strcmp(ret->str[39], "PM"), "ret->str[39] = %s\n", ret->str[39]);
+    ok(!strcmp(ret->str[40], "M/d/yyyy") || broken(!strcmp(ret->str[40], "M/d/yy"))/*NT*/,
+            "ret->str[40] = %s\n", ret->str[40]);
+    ok(!strcmp(ret->str[41], "dddd, MMMM dd, yyyy"), "ret->str[41] = %s\n", ret->str[41]);
+    free(ret);
+
+    if(!setlocale(LC_TIME, "german"))
+        return;
+
+    ret = _Gettnames();
+    ok(!strcmp(ret->str[0], "So"), "ret->str[0] = %s\n", ret->str[0]);
+    ok(!strcmp(ret->str[1], "Mo"), "ret->str[1] = %s\n", ret->str[1]);
+    ok(!strcmp(ret->str[2], "Di"), "ret->str[2] = %s\n", ret->str[2]);
+    ok(!strcmp(ret->str[3], "Mi"), "ret->str[3] = %s\n", ret->str[3]);
+    ok(!strcmp(ret->str[4], "Do"), "ret->str[4] = %s\n", ret->str[4]);
+    ok(!strcmp(ret->str[5], "Fr"), "ret->str[5] = %s\n", ret->str[5]);
+    ok(!strcmp(ret->str[6], "Sa"), "ret->str[6] = %s\n", ret->str[6]);
+    ok(!strcmp(ret->str[7], "Sonntag"), "ret->str[7] = %s\n", ret->str[7]);
+    ok(!strcmp(ret->str[8], "Montag"), "ret->str[8] = %s\n", ret->str[8]);
+    ok(!strcmp(ret->str[9], "Dienstag"), "ret->str[9] = %s\n", ret->str[9]);
+    ok(!strcmp(ret->str[10], "Mittwoch"), "ret->str[10] = %s\n", ret->str[10]);
+    ok(!strcmp(ret->str[11], "Donnerstag"), "ret->str[11] = %s\n", ret->str[11]);
+    ok(!strcmp(ret->str[12], "Freitag"), "ret->str[12] = %s\n", ret->str[12]);
+    ok(!strcmp(ret->str[13], "Samstag"), "ret->str[13] = %s\n", ret->str[13]);
+    ok(!strcmp(ret->str[14], "Jan"), "ret->str[14] = %s\n", ret->str[14]);
+    ok(!strcmp(ret->str[15], "Feb"), "ret->str[15] = %s\n", ret->str[15]);
+    ok(!strcmp(ret->str[16], "Mrz"), "ret->str[16] = %s\n", ret->str[16]);
+    ok(!strcmp(ret->str[17], "Apr"), "ret->str[17] = %s\n", ret->str[17]);
+    ok(!strcmp(ret->str[18], "Mai"), "ret->str[18] = %s\n", ret->str[18]);
+    ok(!strcmp(ret->str[19], "Jun"), "ret->str[19] = %s\n", ret->str[19]);
+    ok(!strcmp(ret->str[20], "Jul"), "ret->str[20] = %s\n", ret->str[20]);
+    ok(!strcmp(ret->str[21], "Aug"), "ret->str[21] = %s\n", ret->str[21]);
+    ok(!strcmp(ret->str[22], "Sep"), "ret->str[22] = %s\n", ret->str[22]);
+    ok(!strcmp(ret->str[23], "Okt"), "ret->str[23] = %s\n", ret->str[23]);
+    ok(!strcmp(ret->str[24], "Nov"), "ret->str[24] = %s\n", ret->str[24]);
+    ok(!strcmp(ret->str[25], "Dez"), "ret->str[25] = %s\n", ret->str[25]);
+    ok(!strcmp(ret->str[26], "Januar"), "ret->str[26] = %s\n", ret->str[26]);
+    ok(!strcmp(ret->str[27], "Februar"), "ret->str[27] = %s\n", ret->str[27]);
+    ok(!strcmp(ret->str[29], "April"), "ret->str[29] = %s\n", ret->str[29]);
+    ok(!strcmp(ret->str[30], "Mai"), "ret->str[30] = %s\n", ret->str[30]);
+    ok(!strcmp(ret->str[31], "Juni"), "ret->str[31] = %s\n", ret->str[31]);
+    ok(!strcmp(ret->str[32], "Juli"), "ret->str[32] = %s\n", ret->str[32]);
+    ok(!strcmp(ret->str[33], "August"), "ret->str[33] = %s\n", ret->str[33]);
+    ok(!strcmp(ret->str[34], "September"), "ret->str[34] = %s\n", ret->str[34]);
+    ok(!strcmp(ret->str[35], "Oktober"), "ret->str[35] = %s\n", ret->str[35]);
+    ok(!strcmp(ret->str[36], "November"), "ret->str[36] = %s\n", ret->str[36]);
+    ok(!strcmp(ret->str[37], "Dezember"), "ret->str[37] = %s\n", ret->str[37]);
+    ok(!strcmp(ret->str[38], ""), "ret->str[38] = %s\n", ret->str[38]);
+    ok(!strcmp(ret->str[39], ""), "ret->str[39] = %s\n", ret->str[39]);
+    ok(!strcmp(ret->str[40], "dd.MM.yyyy") || broken(!strcmp(ret->str[40], "dd.MM.yy"))/*NT*/,
+            "ret->str[40] = %s\n", ret->str[40]);
+    ok(!strcmp(ret->str[41], "dddd, d. MMMM yyyy"), "ret->str[41] = %s\n", ret->str[41]);
+    free(ret);
+
+    setlocale(LC_ALL, "C");
+}
+
 START_TEST(locale)
 {
     init();
 
     test_crtGetStringTypeW();
     test_setlocale();
+    test__Gettnames();
 }
index 7135248..a2bf0cb 100644 (file)
@@ -23,7 +23,6 @@
 #include "msvcrt.h"
 
 static int (__cdecl *prand_s)(unsigned int *);
-static int (__cdecl *pmemcpy_s)(void *, MSVCRT_size_t, void*, MSVCRT_size_t);
 static int (__cdecl *pI10_OUTPUT)(long double, int, int, void*);
 static int (__cdecl *pstrerror_s)(char *, MSVCRT_size_t, int);
 static int (__cdecl *p_get_doserrno)(int *);
@@ -36,7 +35,6 @@ static void init(void)
     HMODULE hmod = GetModuleHandleA("msvcrt.dll");
 
     prand_s = (void *)GetProcAddress(hmod, "rand_s");
-    pmemcpy_s = (void*)GetProcAddress(hmod, "memcpy_s");
     pI10_OUTPUT = (void*)GetProcAddress(hmod, "$I10_OUTPUT");
     pstrerror_s = (void *)GetProcAddress(hmod, "strerror_s");
     p_get_doserrno = (void *)GetProcAddress(hmod, "_get_doserrno");
@@ -65,57 +63,6 @@ static void test_rand_s(void)
     ok(ret == 0, "Expected rand_s to return 0, got %d\n", ret);
 }
 
-static void test_memcpy_s(void)
-{
-    static char data[] = "data\0to\0be\0copied";
-    static char dest[32];
-    int ret;
-
-    if(!pmemcpy_s)
-    {
-        win_skip("memcpy_s is not available\n");
-        return;
-    }
-
-    errno = 0xdeadbeef;
-    ret = pmemcpy_s(NULL, 0, NULL, 0);
-    ok(ret == 0, "ret = %x\n", ret);
-    ok(errno == 0xdeadbeef, "errno = %x\n", errno);
-
-    errno = 0xdeadbeef;
-    dest[0] = 'x';
-    ret = pmemcpy_s(dest, 10, NULL, 0);
-    ok(ret == 0, "ret = %x\n", ret);
-    ok(errno == 0xdeadbeef, "errno = %x\n", errno);
-    ok(dest[0] == 'x', "dest[0] != \'x\'\n");
-
-    errno = 0xdeadbeef;
-    ret = pmemcpy_s(NULL, 10, data, 10);
-    ok(ret == EINVAL, "ret = %x\n", ret);
-    ok(errno == EINVAL, "errno = %x\n", errno);
-
-    errno = 0xdeadbeef;
-    dest[7] = 'x';
-    ret = pmemcpy_s(dest, 10, data, 5);
-    ok(ret == 0, "ret = %x\n", ret);
-    ok(errno == 0xdeadbeef, "errno = %x\n", errno);
-    ok(memcmp(dest, data, 10), "All data copied\n");
-    ok(!memcmp(dest, data, 5), "First five bytes are different\n");
-
-    errno = 0xdeadbeef;
-    ret = pmemcpy_s(data, 10, data, 10);
-    ok(ret == 0, "ret = %x\n", ret);
-    ok(errno == 0xdeadbeef, "errno = %x\n", errno);
-    ok(!memcmp(dest, data, 5), "data was destroyed during overwriting\n");
-
-    errno = 0xdeadbeef;
-    dest[0] = 'x';
-    ret = pmemcpy_s(dest, 5, data, 10);
-    ok(ret == ERANGE, "ret = %x\n", ret);
-    ok(errno == ERANGE, "errno = %x\n", errno);
-    ok(dest[0] == '\0', "dest[0] != \'\\0\'\n");
-}
-
 typedef struct _I10_OUTPUT_data {
     short pos;
     char sign;
@@ -367,7 +314,6 @@ START_TEST(misc)
     init();
 
     test_rand_s();
-    test_memcpy_s();
     test_I10_OUTPUT();
     test_strerror_s();
     test__get_doserrno();
index 978b512..1507a01 100644 (file)
@@ -323,6 +323,22 @@ static void test_sprintf( void )
     ok(!strcmp(buffer,"1   "),"Character zero-padded and/or not left-adjusted \"%s\"\n",buffer);
     ok( r==4, "return count wrong\n");
 
+    format = "%#012x";
+    r = sprintf(buffer,format,1);
+    ok(!strcmp(buffer,"0x0000000001"),"Hexadecimal zero-padded \"%s\"\n",buffer);
+
+    format = "%#04.8x";
+    r = sprintf(buffer,format,1);
+    ok(!strcmp(buffer,"0x00000001"), "Hexadecimal zero-padded precision \"%s\"\n",buffer);
+
+    format = "%#-08.2x";
+    r = sprintf(buffer,format,1);
+    ok(!strcmp(buffer,"0x01    "), "Hexadecimal zero-padded not left-adjusted \"%s\"\n",buffer);
+
+    format = "%#08o";
+    r = sprintf(buffer,format,1);
+    ok(!strcmp(buffer,"00000001"), "Octal zero-padded \"%s\"\n",buffer);
+
     if (sizeof(void *) == 8)
     {
         format = "%p";
@@ -591,6 +607,16 @@ static void test_sprintf( void )
     r = sprintf(buffer, format);
     ok(!strcmp(buffer,"%0"), "failed: \"%s\"\n", buffer);
     ok( r==2, "return count wrong\n");
+
+    format = "%hx";
+    r = sprintf(buffer, format, 0x12345);
+    ok(!strcmp(buffer,"2345"), "failed \"%s\"\n", buffer);
+
+    format = "%hhx";
+    r = sprintf(buffer, format, 0x123);
+    ok(!strcmp(buffer,"123"), "failed: \"%s\"\n", buffer);
+    r = sprintf(buffer, format, 0x12345);
+    ok(!strcmp(buffer,"2345"), "failed \"%s\"\n", buffer);
 }
 
 static void test_swprintf( void )
index cbfb184..fb9d8e8 100644 (file)
@@ -27,6 +27,7 @@ static void test_sscanf( void )
     char buffer[100], buffer1[100];
     char format[20];
     int result, ret;
+    LONGLONG result64;
     char c;
     void *ptr;
     float res1= -82.6267f, res2= 27.76f, res11, res12;
@@ -125,6 +126,25 @@ static void test_sscanf( void )
     ok(ret == 0 , "problem with format arg \"%%*c%%n\"\n");
     ok(number_so_far == 1,"Read wrong arg for \"%%n\" %d instead of 2\n",number_so_far);
 
+    result = 0xdeadbeef;
+    strcpy(buffer,"12345678");
+    ret = sscanf(buffer, "%hd", &result);
+    ok(ret == 1, "Wrong number of arguments read: %d\n", ret);
+    ok(result == 0xdead614e, "Wrong number read (%x)\n", result);
+
+    result = 0xdeadbeef;
+    ret = sscanf(buffer, "%hhd", &result);
+    ok(ret == 1, "Wrong number of arguments read: %d\n", ret);
+    ok(result == 0xbc614e, "Wrong number read (%x)\n", result);
+
+    strcpy(buffer,"12345678901234");
+    ret = sscanf(buffer, "%lld", &result64);
+    ok(ret == 1, "Wrong number of arguments read: %d\n", ret);
+    ret = sprintf(buffer1, "%lld", result64);
+    ok(ret==14 || broken(ret==10), "sprintf retuned %d\n", ret);
+    if(ret == 14)
+        ok(!strcmp(buffer, buffer1), "got %s, expected %s\n", buffer1, buffer);
+
     /* Check %i according to bug 1878 */
     strcpy(buffer,"123");
     ret = sscanf(buffer, "%i", &result);
@@ -243,8 +263,25 @@ static void test_sscanf_s(void)
     ok(i==123, "i = %d\n", i);
 }
 
+static void test_swscanf( void )
+{
+    wchar_t buffer[100];
+    int result, ret;
+    static const WCHAR formatd[] = {'%','d',0};
+
+    /* check WEOF */
+    /* WEOF is an unsigned short -1 but swscanf returns int
+       so it should be sign-extended */
+    buffer[0] = 0;
+    ret = swscanf(buffer, formatd, &result);
+    /* msvcrt returns 0 but should return -1 (later versions do) */
+    ok( ret == (short)WEOF || broken(ret == 0),
+        "swscanf returns %x instead of %x\n", ret, WEOF );
+}
+
 START_TEST(scanf)
 {
     test_sscanf();
     test_sscanf_s();
+    test_swscanf();
 }
index b6c4738..1f2fae4 100644 (file)
@@ -58,6 +58,8 @@ static void __cdecl test_invalid_parameter_handler(const wchar_t *expression,
 #define expect_bin(buf, value, len) { ok(memcmp((buf), value, len) == 0, "Binary buffer mismatch - expected %s, got %s\n", buf_to_string((unsigned char *)value, len, 1), buf_to_string((buf), len, 0)); }
 
 static void* (__cdecl *pmemcpy)(void *, const void *, size_t n);
+static int (__cdecl *p_memcpy_s)(void *, size_t, const void *, size_t);
+static int (__cdecl *p_memmove_s)(void *, size_t, const void *, size_t);
 static int* (__cdecl *pmemcmp)(void *, const void *, size_t n);
 static int (__cdecl *pstrcpy_s)(char *dst, size_t len, const char *src);
 static int (__cdecl *pstrcat_s)(char *dst, size_t len, const char *src);
@@ -500,6 +502,148 @@ static void test_strcpy_s(void)
     ok(ret == EINVAL, "Copying a big string a NULL dest returned %d, expected EINVAL\n", ret);
 }
 
+#define NUMELMS(array) (sizeof(array)/sizeof((array)[0]))
+
+#define okchars(dst, b0, b1, b2, b3, b4, b5, b6, b7) \
+    ok(dst[0] == b0 && dst[1] == b1 && dst[2] == b2 && dst[3] == b3 && \
+       dst[4] == b4 && dst[5] == b5 && dst[6] == b6 && dst[7] == b7, \
+       "Bad result: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",\
+       dst[0], dst[1], dst[2], dst[3], dst[4], dst[5], dst[6], dst[7])
+
+static void test_memcpy_s(void)
+{
+    static char dest[8];
+    static const char tiny[] = {'T',0,'I','N','Y',0};
+    static const char big[] = {'a','t','o','o','l','o','n','g','s','t','r','i','n','g',0};
+    int ret;
+    if (!p_memcpy_s) {
+        skip("memcpy_s not found\n");
+        return;
+    }
+
+    if (p_set_invalid_parameter_handler)
+        ok(p_set_invalid_parameter_handler(test_invalid_parameter_handler) == NULL,
+            "Invalid parameter handler was already set\n");
+
+    /* Normal */
+    memset(dest, 'X', sizeof(dest));
+    ret = p_memcpy_s(dest, NUMELMS(dest), tiny, NUMELMS(tiny));
+    ok(ret == 0, "Copying a buffer into a big enough destination returned %d, expected 0\n", ret);
+    okchars(dest, tiny[0], tiny[1], tiny[2], tiny[3], tiny[4], tiny[5], 'X', 'X');
+
+    /* Vary source size */
+    errno = 0xdeadbeef;
+    memset(dest, 'X', sizeof(dest));
+    ret = p_memcpy_s(dest, NUMELMS(dest), big, NUMELMS(big));
+    ok(ret == ERANGE, "Copying a big buffer to a small destination returned %d, expected ERANGE\n", ret);
+    ok(errno == ERANGE, "errno is %d, expected ERANGE\n", errno);
+    okchars(dest, 0, 0, 0, 0, 0, 0, 0, 0);
+
+    /* Replace source with NULL */
+    errno = 0xdeadbeef;
+    memset(dest, 'X', sizeof(dest));
+    ret = p_memcpy_s(dest, NUMELMS(dest), NULL, NUMELMS(tiny));
+    ok(ret == EINVAL, "Copying a NULL source buffer returned %d, expected EINVAL\n", ret);
+    ok(errno == EINVAL, "errno is %d, expected EINVAL\n", errno);
+    okchars(dest, 0, 0, 0, 0, 0, 0, 0, 0);
+
+    /* Vary dest size */
+    errno = 0xdeadbeef;
+    memset(dest, 'X', sizeof(dest));
+    ret = p_memcpy_s(dest, 0, tiny, NUMELMS(tiny));
+    ok(ret == ERANGE, "Copying into a destination of size 0 returned %d, expected ERANGE\n", ret);
+    ok(errno == ERANGE, "errno is %d, expected ERANGE\n", errno);
+    okchars(dest, 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X');
+
+    /* Replace dest with NULL */
+    errno = 0xdeadbeef;
+    ret = p_memcpy_s(NULL, NUMELMS(dest), tiny, NUMELMS(tiny));
+    ok(ret == EINVAL, "Copying a tiny buffer to a big NULL destination returned %d, expected EINVAL\n", ret);
+    ok(errno == EINVAL, "errno is %d, expected EINVAL\n", errno);
+
+    /* Combinations */
+    errno = 0xdeadbeef;
+    memset(dest, 'X', sizeof(dest));
+    ret = p_memcpy_s(dest, 0, NULL, NUMELMS(tiny));
+    ok(ret == EINVAL, "Copying a NULL buffer into a destination of size 0 returned %d, expected EINVAL\n", ret);
+    ok(errno == EINVAL, "errno is %d, expected EINVAL\n", errno);
+    okchars(dest, 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X');
+
+    if (p_set_invalid_parameter_handler)
+        ok(p_set_invalid_parameter_handler(NULL) == test_invalid_parameter_handler,
+            "Cannot reset invalid parameter handler\n");
+}
+
+static void test_memmove_s(void)
+{
+    static char dest[8];
+    static const char tiny[] = {'T',0,'I','N','Y',0};
+    static const char big[] = {'a','t','o','o','l','o','n','g','s','t','r','i','n','g',0};
+    int ret;
+    if (!p_memmove_s) {
+        skip("memmove_s not found\n");
+        return;
+    }
+
+    if (p_set_invalid_parameter_handler)
+        ok(p_set_invalid_parameter_handler(test_invalid_parameter_handler) == NULL,
+            "Invalid parameter handler was already set\n");
+
+    /* Normal */
+    memset(dest, 'X', sizeof(dest));
+    ret = p_memmove_s(dest, NUMELMS(dest), tiny, NUMELMS(tiny));
+    ok(ret == 0, "Moving a buffer into a big enough destination returned %d, expected 0\n", ret);
+    okchars(dest, tiny[0], tiny[1], tiny[2], tiny[3], tiny[4], tiny[5], 'X', 'X');
+
+    /* Overlapping */
+    memcpy(dest, big, sizeof(dest));
+    ret = p_memmove_s(dest+1, NUMELMS(dest)-1, dest, NUMELMS(dest)-1);
+    ok(ret == 0, "Moving a buffer up one char returned %d, expected 0\n", ret);
+    okchars(dest, big[0], big[0], big[1], big[2], big[3], big[4], big[5], big[6]);
+
+    /* Vary source size */
+    errno = 0xdeadbeef;
+    memset(dest, 'X', sizeof(dest));
+    ret = p_memmove_s(dest, NUMELMS(dest), big, NUMELMS(big));
+    ok(ret == ERANGE, "Moving a big buffer to a small destination returned %d, expected ERANGE\n", ret);
+    ok(errno == ERANGE, "errno is %d, expected ERANGE\n", errno);
+    okchars(dest, 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X');
+
+    /* Replace source with NULL */
+    errno = 0xdeadbeef;
+    memset(dest, 'X', sizeof(dest));
+    ret = p_memmove_s(dest, NUMELMS(dest), NULL, NUMELMS(tiny));
+    ok(ret == EINVAL, "Moving a NULL source buffer returned %d, expected EINVAL\n", ret);
+    ok(errno == EINVAL, "errno is %d, expected EINVAL\n", errno);
+    okchars(dest, 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X');
+
+    /* Vary dest size */
+    errno = 0xdeadbeef;
+    memset(dest, 'X', sizeof(dest));
+    ret = p_memmove_s(dest, 0, tiny, NUMELMS(tiny));
+    ok(ret == ERANGE, "Moving into a destination of size 0 returned %d, expected ERANGE\n", ret);
+    ok(errno == ERANGE, "errno is %d, expected ERANGE\n", errno);
+    okchars(dest, 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X');
+
+    /* Replace dest with NULL */
+    errno = 0xdeadbeef;
+    ret = p_memmove_s(NULL, NUMELMS(dest), tiny, NUMELMS(tiny));
+    ok(ret == EINVAL, "Moving a tiny buffer to a big NULL destination returned %d, expected EINVAL\n", ret);
+    ok(errno == EINVAL, "errno is %d, expected EINVAL\n", errno);
+
+    /* Combinations */
+    errno = 0xdeadbeef;
+    memset(dest, 'X', sizeof(dest));
+    ret = p_memmove_s(dest, 0, NULL, NUMELMS(tiny));
+    ok(ret == EINVAL, "Moving a NULL buffer into a destination of size 0 returned %d, expected EINVAL\n", ret);
+    ok(errno == EINVAL, "errno is %d, expected EINVAL\n", errno);
+    okchars(dest, 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X');
+
+    if (p_set_invalid_parameter_handler)
+        ok(p_set_invalid_parameter_handler(NULL) == test_invalid_parameter_handler,
+            "Cannot reset invalid parameter handler\n");
+}
+
 static void test_strcat_s(void)
 {
     char dest[8];
@@ -685,6 +829,22 @@ static void test_wcscpy_s(void)
     ret = p_wcsncpy_s(szDestShort, 8, szLongText, sizeof(szLongText)/sizeof(WCHAR));
     ok(ret == ERANGE || ret == EINVAL, "expected ERANGE/EINVAL got %d\n", ret);
     ok(szDestShort[0] == 0, "szDestShort[0] not 0\n");
+
+    szDest[0] = 'A';
+    ret = p_wcsncpy_s(szDest, 5, szLongText, -1);
+    ok(ret == STRUNCATE, "expected STRUNCATE got %d\n", ret);
+    ok(szDest[4] == 0, "szDest[4] not 0\n");
+    ok(!memcmp(szDest, szLongText, 4*sizeof(WCHAR)), "szDest = %s\n", wine_dbgstr_w(szDest));
+
+    ret = p_wcsncpy_s(NULL, 0, (void*)0xdeadbeef, 0);
+    ok(ret == 0, "ret = %d\n", ret);
+
+    szDestShort[0] = '1';
+    szDestShort[1] = 0;
+    ret = p_wcsncpy_s(szDestShort+1, 4, szDestShort, -1);
+    ok(ret == STRUNCATE, "expected ERROR_SUCCESS got %d\n", ret);
+    ok(szDestShort[0]=='1' && szDestShort[1]=='1' && szDestShort[2]=='1' && szDestShort[3]=='1',
+            "szDestShort = %s\n", wine_dbgstr_w(szDestShort));
 }
 
 static void test__wcsupr_s(void)
@@ -866,21 +1026,79 @@ static void test_mbcjisjms(void)
     /* List of value-pairs to test. The test assumes the last pair to be {0, ..} */
     unsigned int jisjms[][2] = { {0x2020, 0}, {0x2021, 0}, {0x2120, 0}, {0x2121, 0x8140},
                                  {0x7f7f, 0}, {0x7f7e, 0}, {0x7e7f, 0}, {0x7e7e, 0xeffc},
+                                 {0x255f, 0x837e}, {0x2560, 0x8380}, {0x2561, 0x8381},
                                  {0x2121FFFF, 0}, {0x2223, 0x81a1}, {0x237e, 0x829e}, {0, 0}};
-    unsigned int ret, exp, i;
+    int cp[] = { 932, 936, 939, 950, 1361, _MB_CP_SBCS };
+    unsigned int i, j;
+    int prev_cp = _getmbcp();
 
-    i = 0;
-    do
+    for (i = 0; i < sizeof(cp)/sizeof(cp[0]); i++)
     {
-        ret = _mbcjistojms(jisjms[i][0]);
+        _setmbcp(cp[i]);
+        for (j = 0; jisjms[j][0] != 0; j++)
+        {
+            unsigned int ret, exp;
+            ret = _mbcjistojms(jisjms[j][0]);
+            exp = (cp[i] == 932) ? jisjms[j][1] : jisjms[j][0];
+            ok(ret == exp, "Expected 0x%x, got 0x%x (0x%x, codepage=%d)\n",
+               exp, ret, jisjms[j][0], cp[i]);
+        }
+    }
+    _setmbcp(prev_cp);
+}
 
-        if(_getmbcp() == 932)   /* Japanese codepage? */
-            exp = jisjms[i][1];
-        else
-            exp = jisjms[i][0]; /* If not, no conversion */
+static void test_mbcjmsjis(void)
+{
+    /* List of value-pairs to test. The test assumes the last pair to be {0, ..} */
+    unsigned int jmsjis[][2] = { {0x80fc, 0}, {0x813f, 0}, {0x8140, 0x2121},
+                                 {0x817e, 0x215f}, {0x817f, 0}, {0x8180, 0x2160},
+                                 {0x819e, 0x217e}, {0x819f, 0x2221}, {0x81fc, 0x227e},
+                                 {0x81fd, 0}, {0x9ffc, 0x5e7e}, {0x9ffd, 0},
+                                 {0xa040, 0}, {0xdffc, 0}, {0xe040, 0x5f21},
+                                 {0xeffc, 0x7e7e}, {0xf040, 0}, {0x21, 0}, {0, 0}};
+    int cp[] = { 932, 936, 939, 950, 1361, _MB_CP_SBCS };
+    unsigned int i, j;
+    int prev_cp = _getmbcp();
+
+    for (i = 0; i < sizeof(cp)/sizeof(cp[0]); i++)
+    {
+        _setmbcp(cp[i]);
+        for (j = 0; jmsjis[j][0] != 0; j++)
+        {
+            unsigned int ret, exp;
+            ret = _mbcjmstojis(jmsjis[j][0]);
+            exp = (cp[i] == 932) ? jmsjis[j][1] : jmsjis[j][0];
+            ok(ret == exp, "Expected 0x%x, got 0x%x (0x%x, codepage=%d)\n",
+               exp, ret, jmsjis[j][0], cp[i]);
+        }
+    }
+    _setmbcp(prev_cp);
+}
 
-        ok(ret == exp, "Expected 0x%x, got 0x%x\n", exp, ret);
-    } while(jisjms[i++][0] != 0);
+static void test_mbbtombc(void)
+{
+    static const unsigned int mbbmbc[][2] = {
+        {0x1f, 0x1f}, {0x20, 0x8140}, {0x39, 0x8258}, {0x40, 0x8197},
+        {0x41, 0x8260}, {0x5e, 0x814f}, {0x7e, 0x8150}, {0x7f, 0x7f},
+        {0x80, 0x80}, {0x81, 0x81}, {0xa0, 0xa0}, {0xa7, 0x8340},
+        {0xb0, 0x815b}, {0xd1, 0x8380}, {0xff, 0xff}, {0,0}};
+    int cp[] = { 932, 936, 939, 950, 1361, _MB_CP_SBCS };
+    int i, j;
+    int prev_cp = _getmbcp();
+
+    for (i = 0; i < sizeof(cp)/sizeof(cp[0]); i++)
+    {
+        _setmbcp(cp[i]);
+        for (j = 0; mbbmbc[j][0] != 0; j++)
+        {
+            unsigned int exp, ret;
+            ret = _mbbtombc(mbbmbc[j][0]);
+            exp = (cp[i] == 932) ? mbbmbc[j][1] : mbbmbc[j][0];
+            ok(ret == exp, "Expected 0x%x, got 0x%x (0x%x, codepage %d)\n",
+               exp, ret, mbbmbc[j][0], cp[i]);
+        }
+    }
+    _setmbcp(prev_cp);
 }
 
 static void test_mbctombb(void)
@@ -1586,7 +1804,7 @@ static void test__strlwr_s(void)
     ok(ret == EINVAL, "Expected _strlwr_s to return EINVAL, got %d\n", ret);
     ok(errno == EINVAL, "Expected errno to be EINVAL, got %d\n", errno);
     ok(!memcmp(buffer, "\0oRrIsTeR", sizeof("\0oRrIsTeR")),
-       "Expected the output buffer to be \"gorrIsTeR\"\n");
+       "Expected the output buffer to be \"\\0oRrIsTeR\"\n");
 
     strcpy(buffer, "GoRrIsTeR");
     errno = EBADF;
@@ -1594,7 +1812,7 @@ static void test__strlwr_s(void)
     ok(ret == EINVAL, "Expected _strlwr_s to return EINVAL, got %d\n", ret);
     ok(errno == EINVAL, "Expected errno to be EINVAL, got %d\n", errno);
     ok(!memcmp(buffer, "\0oRrIsTeR", sizeof("\0oRrIsTeR")),
-       "Expected the output buffer to be \"gorrIsTeR\"\n");
+       "Expected the output buffer to be \"\\0oRrIsTeR\"\n");
 
     strcpy(buffer, "GoRrIsTeR");
     ret = p_strlwr_s(buffer, sizeof("GoRrIsTeR"));
@@ -2005,6 +2223,9 @@ static void test_wctob(void)
     ret = p_wctob(0x81);
     ok(ret == (int)(char)0x81, "ret = %x\n", ret);
 
+    ret = p_wctob(0x9f);
+    ok(ret == (int)(char)0x9f, "ret = %x\n", ret);
+
     ret = p_wctob(0xe0);
     ok(ret == (int)(char)0xe0, "ret = %x\n", ret);
 }
@@ -2055,6 +2276,8 @@ START_TEST(string)
         hMsvcrt = GetModuleHandleA("msvcrtd.dll");
     ok(hMsvcrt != 0, "GetModuleHandleA failed\n");
     SET(pmemcpy,"memcpy");
+    p_memcpy_s = (void*)GetProcAddress( hMsvcrt, "memcpy_s" );
+    p_memmove_s = (void*)GetProcAddress( hMsvcrt, "memmove_s" );
     SET(pmemcmp,"memcmp");
     SET(p_mbctype,"_mbctype");
     SET(p__mb_cur_max,"__mb_cur_max");
@@ -2102,9 +2325,13 @@ START_TEST(string)
    /* test _strdup */
     test_strdup();
     test_strcpy_s();
+    test_memcpy_s();
+    test_memmove_s();
     test_strcat_s();
     test__mbsnbcpy_s();
     test_mbcjisjms();
+    test_mbcjmsjis();
+    test_mbbtombc();
     test_mbctombb();
     test_ismbclegal();
     test_strtok();
index 950042c..c6e2a9c 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <stdlib.h> /*setenv*/
 #include <stdio.h> /*printf*/
+#include <locale.h>
 #include <errno.h>
 
 #define _MAX__TIME64_T     (((__time64_t)0x00000007 << 32) | 0x93406FFF)
@@ -37,6 +38,7 @@
 
 static __time32_t (__cdecl *p_mkgmtime32)(struct tm*);
 static struct tm* (__cdecl *p_gmtime32)(__time32_t*);
+static struct tm* (__cdecl *p_gmtime)(time_t*);
 static errno_t    (__cdecl *p_gmtime32_s)(struct tm*, __time32_t*);
 static errno_t    (__cdecl *p_strtime_s)(char*,size_t);
 static errno_t    (__cdecl *p_strdate_s)(char*,size_t);
@@ -44,12 +46,16 @@ static errno_t    (__cdecl *p_localtime32_s)(struct tm*, __time32_t*);
 static errno_t    (__cdecl *p_localtime64_s)(struct tm*, __time64_t*);
 static int*       (__cdecl *p__daylight)(void);
 static int*       (__cdecl *p___p__daylight)(void);
+static size_t     (__cdecl *p_strftime)(char *, size_t, const char *, const struct tm *);
+static size_t     (__cdecl *p_wcsftime)(wchar_t *, size_t, const wchar_t *, const struct tm *);
+static char*      (__cdecl *p_asctime)(const struct tm *);
 
 static void init(void)
 {
-    HMODULE hmod = GetModuleHandleA("msvcrt.dll");
+    HMODULE hmod = LoadLibrary("msvcrt.dll");
 
     p_gmtime32 = (void*)GetProcAddress(hmod, "_gmtime32");
+    p_gmtime = (void*)GetProcAddress(hmod, "gmtime");
     p_gmtime32_s = (void*)GetProcAddress(hmod, "_gmtime32_s");
     p_mkgmtime32 = (void*)GetProcAddress(hmod, "_mkgmtime32");
     p_strtime_s = (void*)GetProcAddress(hmod, "_strtime_s");
@@ -58,6 +64,9 @@ static void init(void)
     p_localtime64_s = (void*)GetProcAddress(hmod, "_localtime64_s");
     p__daylight = (void*)GetProcAddress(hmod, "__daylight");
     p___p__daylight = (void*)GetProcAddress(hmod, "__p__daylight");
+    p_strftime = (void*)GetProcAddress(hmod, "strftime");
+    p_wcsftime = (void*)GetProcAddress(hmod, "wcsftime");
+    p_asctime = (void*)GetProcAddress(hmod, "asctime");
 }
 
 static int get_test_year(time_t *start)
@@ -568,10 +577,238 @@ static void test_daylight(void)
     ok(ret1 && ret1 == ret2, "got %p\n", ret1);
 }
 
+static void test_strftime(void)
+{
+    static const wchar_t cW[] = { '%','c',0 };
+    static const char expected[] = "01/01/70 00:00:00";
+    time_t gmt;
+    struct tm* gmt_tm;
+    char buf[256], bufA[256];
+    WCHAR bufW[256];
+    long retA, retW;
+
+    if (!p_strftime || !p_wcsftime || !p_gmtime)
+    {
+        win_skip("strftime, wcsftime or gmtime is not available\n");
+        return;
+    }
+
+    setlocale(LC_TIME, "C");
+
+    gmt = 0;
+    gmt_tm = p_gmtime(&gmt);
+    ok(gmt_tm != NULL, "gmtime failed\n");
+
+    errno = 0xdeadbeef;
+    retA = strftime(NULL, 0, "copy", gmt_tm);
+    ok(retA == 0, "expected 0, got %ld\n", retA);
+    ok(errno==EINVAL || broken(errno==0xdeadbeef), "errno = %d\n", errno);
+
+    retA = strftime(bufA, 256, "copy", NULL);
+    ok(retA == 4, "expected 4, got %ld\n", retA);
+    ok(!strcmp(bufA, "copy"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "copy it", gmt_tm);
+    ok(retA == 7, "expected 7, got %ld\n", retA);
+    ok(!strcmp(bufA, "copy it"), "got %s\n", bufA);
+
+    errno = 0xdeadbeef;
+    retA = strftime(bufA, 2, "copy", gmt_tm);
+    ok(retA == 0, "expected 0, got %ld\n", retA);
+    ok(!strcmp(bufA, "") || broken(!strcmp(bufA, "copy it")), "got %s\n", bufA);
+    ok(errno==ERANGE || errno==0xdeadbeef, "errno = %d\n", errno);
+
+    errno = 0xdeadbeef;
+    retA = strftime(bufA, 256, "a%e", gmt_tm);
+    ok(retA==0 || broken(retA==1), "expected 0, got %ld\n", retA);
+    ok(!strcmp(bufA, "") || broken(!strcmp(bufA, "a")), "got %s\n", bufA);
+    ok(errno==EINVAL || broken(errno==0xdeadbeef), "errno = %d\n", errno);
+
+    if(0) { /* crashes on Win2k */
+        errno = 0xdeadbeef;
+        retA = strftime(bufA, 256, "%c", NULL);
+        ok(retA == 0, "expected 0, got %ld\n", retA);
+        ok(!strcmp(bufA, ""), "got %s\n", bufA);
+        ok(errno == EINVAL, "errno = %d\n", errno);
+    }
+
+    retA = strftime(bufA, 256, "e%#%e", gmt_tm);
+    ok(retA == 3, "expected 3, got %ld\n", retA);
+    ok(!strcmp(bufA, "e%e"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "%c", gmt_tm);
+    ok(retA == 17, "expected 17, got %ld\n", retA);
+    ok(strcmp(bufA, expected) == 0, "expected %s, got %s\n", expected, bufA);
+
+    retW = wcsftime(bufW, 256, cW, gmt_tm);
+    ok(retW == 17, "expected 17, got %ld\n", retW);
+    ok(retA == retW, "expected %ld, got %ld\n", retA, retW);
+    buf[0] = 0;
+    retA = WideCharToMultiByte(CP_ACP, 0, bufW, retW, buf, 256, NULL, NULL);
+    buf[retA] = 0;
+    ok(strcmp(bufA, buf) == 0, "expected %s, got %s\n", bufA, buf);
+
+    retA = strftime(bufA, 256, "%x", gmt_tm);
+    ok(retA == 8, "expected 8, got %ld\n", retA);
+    ok(!strcmp(bufA, "01/01/70"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "%X", gmt_tm);
+    ok(retA == 8, "expected 8, got %ld\n", retA);
+    ok(!strcmp(bufA, "00:00:00"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "%a", gmt_tm);
+    ok(retA == 3, "expected 3, got %ld\n", retA);
+    ok(!strcmp(bufA, "Thu"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "%A", gmt_tm);
+    ok(retA == 8, "expected 8, got %ld\n", retA);
+    ok(!strcmp(bufA, "Thursday"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "%b", gmt_tm);
+    ok(retA == 3, "expected 3, got %ld\n", retA);
+    ok(!strcmp(bufA, "Jan"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "%B", gmt_tm);
+    ok(retA == 7, "expected 7, got %ld\n", retA);
+    ok(!strcmp(bufA, "January"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "%d", gmt_tm);
+    ok(retA == 2, "expected 2, got %ld\n", retA);
+    ok(!strcmp(bufA, "01"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "%#d", gmt_tm);
+    ok(retA == 1, "expected 1, got %ld\n", retA);
+    ok(!strcmp(bufA, "1"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "%H", gmt_tm);
+    ok(retA == 2, "expected 2, got %ld\n", retA);
+    ok(!strcmp(bufA, "00"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "%I", gmt_tm);
+    ok(retA == 2, "expected 2, got %ld\n", retA);
+    ok(!strcmp(bufA, "12"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "%j", gmt_tm);
+    ok(retA == 3, "expected 3, got %ld\n", retA);
+    ok(!strcmp(bufA, "001"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "%m", gmt_tm);
+    ok(retA == 2, "expected 2, got %ld\n", retA);
+    ok(!strcmp(bufA, "01"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "%#M", gmt_tm);
+    ok(retA == 1, "expected 1, got %ld\n", retA);
+    ok(!strcmp(bufA, "0"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "%p", gmt_tm);
+    ok(retA == 2, "expected 2, got %ld\n", retA);
+    ok(!strcmp(bufA, "AM"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "%U", gmt_tm);
+    ok(retA == 2, "expected 2, got %ld\n", retA);
+    ok(!strcmp(bufA, "00"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "%W", gmt_tm);
+    ok(retA == 2, "expected 2, got %ld\n", retA);
+    ok(!strcmp(bufA, "00"), "got %s\n", bufA);
+
+    gmt_tm->tm_wday = 0;
+    retA = strftime(bufA, 256, "%U", gmt_tm);
+    ok(retA == 2, "expected 2, got %ld\n", retA);
+    ok(!strcmp(bufA, "01"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "%W", gmt_tm);
+    ok(retA == 2, "expected 2, got %ld\n", retA);
+    ok(!strcmp(bufA, "00"), "got %s\n", bufA);
+
+    gmt_tm->tm_yday = 365;
+    retA = strftime(bufA, 256, "%U", gmt_tm);
+    ok(retA == 2, "expected 2, got %ld\n", retA);
+    ok(!strcmp(bufA, "53"), "got %s\n", bufA);
+
+    retA = strftime(bufA, 256, "%W", gmt_tm);
+    ok(retA == 2, "expected 2, got %ld\n", retA);
+    ok(!strcmp(bufA, "52"), "got %s\n", bufA);
+
+    gmt_tm->tm_mon = 1;
+    gmt_tm->tm_mday = 30;
+    retA = strftime(bufA, 256, "%c", gmt_tm);
+    todo_wine {
+        ok(retA == 17, "expected 17, got %ld\n", retA);
+        ok(!strcmp(bufA, "02/30/70 00:00:00"), "got %s\n", bufA);
+    }
+}
+
+static void test_asctime(void)
+{
+    struct tm* gmt_tm;
+    time_t gmt;
+    char *ret;
+
+    if(!p_asctime || !p_gmtime)
+    {
+        win_skip("asctime or gmtime is not available\n");
+        return;
+    }
+
+    gmt = 0;
+    gmt_tm = p_gmtime(&gmt);
+    ret = p_asctime(gmt_tm);
+    ok(!strcmp(ret, "Thu Jan 01 00:00:00 1970\n"), "asctime retunred %s\n", ret);
+
+    gmt = 312433121;
+    gmt_tm = p_gmtime(&gmt);
+    ret = p_asctime(gmt_tm);
+    ok(!strcmp(ret, "Mon Nov 26 02:58:41 1979\n"), "asctime retunred %s\n", ret);
+
+    /* Week day is only checked if it's in 0..6 range */
+    gmt_tm->tm_wday = 3;
+    ret = p_asctime(gmt_tm);
+    ok(!strcmp(ret, "Wed Nov 26 02:58:41 1979\n"), "asctime returned %s\n", ret);
+
+    errno = 0xdeadbeef;
+    gmt_tm->tm_wday = 7;
+    ret = p_asctime(gmt_tm);
+    ok(!ret || broken(!ret[0]), "asctime returned %s\n", ret);
+    ok(errno==EINVAL || broken(errno==0xdeadbeef), "errno = %d\n", errno);
+
+    /* Year day is ignored */
+    gmt_tm->tm_wday = 3;
+    gmt_tm->tm_yday = 1300;
+    ret = p_asctime(gmt_tm);
+    ok(!strcmp(ret, "Wed Nov 26 02:58:41 1979\n"), "asctime returned %s\n", ret);
+
+    /* Dates that can't be displayed using 26 characters are broken */
+    gmt_tm->tm_mday = 28;
+    gmt_tm->tm_year = 8100;
+    ret = p_asctime(gmt_tm);
+    ok(!strcmp(ret, "Wed Nov 28 02:58:41 :000\n"), "asctime returned %s\n", ret);
+
+    gmt_tm->tm_year = 264100;
+    ret = p_asctime(gmt_tm);
+    ok(!strcmp(ret, "Wed Nov 28 02:58:41 :000\n"), "asctime returned %s\n", ret);
+
+    /* asctime works from year 1900 */
+    errno = 0xdeadbeef;
+    gmt_tm->tm_year = -1;
+    ret = p_asctime(gmt_tm);
+    ok(!ret || broken(!strcmp(ret, "Wed Nov 28 02:58:41 190/\n")), "asctime returned %s\n", ret);
+    ok(errno==EINVAL || broken(errno == 0xdeadbeef), "errno = %d\n", errno);
+
+    errno = 0xdeadbeef;
+    gmt_tm->tm_mon = 1;
+    gmt_tm->tm_mday = 30;
+    gmt_tm->tm_year = 79;
+    ret = p_asctime(gmt_tm);
+    ok(!ret || broken(!strcmp(ret, "Wed Feb 30 02:58:41 1979\n")), "asctime returned %s\n", ret);
+    ok(errno==EINVAL || broken(errno==0xdeadbeef), "errno = %d\n", errno);
+}
+
 START_TEST(time)
 {
     init();
 
+    test_strftime();
     test_ctime();
     test_gmtime();
     test_mktime();
@@ -583,4 +820,5 @@ START_TEST(time)
     test_localtime32_s();
     test_localtime64_s();
     test_daylight();
+    test_asctime();
 }