From 88e8fa67df0c0db6f74f5ae0d839bcc9c6834209 Mon Sep 17 00:00:00 2001 From: Timo Kreuzer Date: Tue, 6 May 2025 18:53:35 +0300 Subject: [PATCH] [MSVCRT] Import msvcrt from wine-10.0 --- dll/win32/msvcrt/bnum.h | 137 + dll/win32/msvcrt/concurrency.c | 3839 +++++++++++++++++++ dll/win32/msvcrt/console.c | 649 ++++ dll/win32/msvcrt/cpp.c | 1090 ++++++ dll/win32/msvcrt/cppexcept.h | 373 ++ dll/win32/msvcrt/crt_gccmain.c | 31 + dll/win32/msvcrt/crt_main.c | 62 + dll/win32/msvcrt/crt_winmain.c | 54 + dll/win32/msvcrt/crt_wmain.c | 62 + dll/win32/msvcrt/crt_wwinmain.c | 56 + dll/win32/msvcrt/ctype.c | 617 +++ dll/win32/msvcrt/cxx.h | 509 +++ dll/win32/msvcrt/data.c | 735 ++++ dll/win32/msvcrt/dir.c | 1787 +++++++++ dll/win32/msvcrt/environ.c | 571 +++ dll/win32/msvcrt/errno.c | 554 +++ dll/win32/msvcrt/except.c | 1073 ++++++ dll/win32/msvcrt/except_arm.c | 97 + dll/win32/msvcrt/except_arm64.c | 128 + dll/win32/msvcrt/except_arm64ec.c | 169 + dll/win32/msvcrt/except_i386.c | 923 +++++ dll/win32/msvcrt/except_x86_64.c | 157 + dll/win32/msvcrt/exception_ptr.c | 271 ++ dll/win32/msvcrt/exit.c | 523 +++ dll/win32/msvcrt/file.c | 5896 +++++++++++++++++++++++++++++ dll/win32/msvcrt/handler4.c | 775 ++++ dll/win32/msvcrt/heap.c | 847 +++++ dll/win32/msvcrt/iob.c | 39 + dll/win32/msvcrt/locale.c | 2186 +++++++++++ dll/win32/msvcrt/lock.c | 163 + dll/win32/msvcrt/main.c | 175 + dll/win32/msvcrt/math.c | 2973 +++++++++++++++ dll/win32/msvcrt/mathf.c | 72 + dll/win32/msvcrt/mbcs.c | 3612 ++++++++++++++++++ dll/win32/msvcrt/misc.c | 584 +++ dll/win32/msvcrt/msvcrt.h | 458 +++ dll/win32/msvcrt/msvcrt.spec | 1533 ++++++++ dll/win32/msvcrt/mtdll.h | 61 + dll/win32/msvcrt/onexit.c | 120 + dll/win32/msvcrt/printf.h | 1318 +++++++ dll/win32/msvcrt/process.c | 1369 +++++++ dll/win32/msvcrt/rsrc.rc | 30 + dll/win32/msvcrt/scanf.c | 820 ++++ dll/win32/msvcrt/scanf.h | 743 ++++ dll/win32/msvcrt/sincos.c | 40 + dll/win32/msvcrt/string.c | 3556 +++++++++++++++++ dll/win32/msvcrt/thread.c | 262 ++ dll/win32/msvcrt/time.c | 1908 ++++++++++ dll/win32/msvcrt/undname.c | 1788 +++++++++ dll/win32/msvcrt/wcs.c | 3087 +++++++++++++++ 50 files changed, 48882 insertions(+) create mode 100644 dll/win32/msvcrt/bnum.h create mode 100644 dll/win32/msvcrt/concurrency.c create mode 100644 dll/win32/msvcrt/console.c create mode 100644 dll/win32/msvcrt/cpp.c create mode 100644 dll/win32/msvcrt/cppexcept.h create mode 100644 dll/win32/msvcrt/crt_gccmain.c create mode 100644 dll/win32/msvcrt/crt_main.c create mode 100644 dll/win32/msvcrt/crt_winmain.c create mode 100644 dll/win32/msvcrt/crt_wmain.c create mode 100644 dll/win32/msvcrt/crt_wwinmain.c create mode 100644 dll/win32/msvcrt/ctype.c create mode 100644 dll/win32/msvcrt/cxx.h create mode 100644 dll/win32/msvcrt/data.c create mode 100644 dll/win32/msvcrt/dir.c create mode 100644 dll/win32/msvcrt/environ.c create mode 100644 dll/win32/msvcrt/errno.c create mode 100644 dll/win32/msvcrt/except.c create mode 100644 dll/win32/msvcrt/except_arm.c create mode 100644 dll/win32/msvcrt/except_arm64.c create mode 100644 dll/win32/msvcrt/except_arm64ec.c create mode 100644 dll/win32/msvcrt/except_i386.c create mode 100644 dll/win32/msvcrt/except_x86_64.c create mode 100644 dll/win32/msvcrt/exception_ptr.c create mode 100644 dll/win32/msvcrt/exit.c create mode 100644 dll/win32/msvcrt/file.c create mode 100644 dll/win32/msvcrt/handler4.c create mode 100644 dll/win32/msvcrt/heap.c create mode 100644 dll/win32/msvcrt/iob.c create mode 100644 dll/win32/msvcrt/locale.c create mode 100644 dll/win32/msvcrt/lock.c create mode 100644 dll/win32/msvcrt/main.c create mode 100644 dll/win32/msvcrt/math.c create mode 100644 dll/win32/msvcrt/mathf.c create mode 100644 dll/win32/msvcrt/mbcs.c create mode 100644 dll/win32/msvcrt/misc.c create mode 100644 dll/win32/msvcrt/msvcrt.h create mode 100644 dll/win32/msvcrt/msvcrt.spec create mode 100644 dll/win32/msvcrt/mtdll.h create mode 100644 dll/win32/msvcrt/onexit.c create mode 100644 dll/win32/msvcrt/printf.h create mode 100644 dll/win32/msvcrt/process.c create mode 100644 dll/win32/msvcrt/rsrc.rc create mode 100644 dll/win32/msvcrt/scanf.c create mode 100644 dll/win32/msvcrt/scanf.h create mode 100644 dll/win32/msvcrt/sincos.c create mode 100644 dll/win32/msvcrt/string.c create mode 100644 dll/win32/msvcrt/thread.c create mode 100644 dll/win32/msvcrt/time.c create mode 100644 dll/win32/msvcrt/undname.c create mode 100644 dll/win32/msvcrt/wcs.c diff --git a/dll/win32/msvcrt/bnum.h b/dll/win32/msvcrt/bnum.h new file mode 100644 index 00000000000..3e42f61e3e1 --- /dev/null +++ b/dll/win32/msvcrt/bnum.h @@ -0,0 +1,137 @@ +/* + * Copyright 2020 Piotr Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#ifndef __WINE_BNUM_H +#define __WINE_BNUM_H + +#define EXP_BITS 11 +#define MANT_BITS 53 + +static const int p10s[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + +#define LIMB_DIGITS 9 /* each DWORD stores up to 9 digits */ +#define LIMB_MAX 1000000000 /* 10^9 */ + +#define BNUM_PREC64 128 /* data size needed to store 64-bit double */ +#define BNUM_PREC80 2048 /* data size needed to store 80-bit double */ + +/* bnum represents real number with fixed decimal point */ +struct bnum { + int b; /* least significant digit position */ + int e; /* most significant digit position + 1 */ + int size; /* data buffer size in DWORDS (power of 2) */ + DWORD data[1]; /* circular buffer, base 10 number */ +}; + +static inline int bnum_idx(struct bnum *b, int idx) +{ + return idx & (b->size - 1); +} + +/* Returns TRUE if new most significant limb was added */ +static inline BOOL bnum_lshift(struct bnum *b, int shift) +{ + DWORD rest = 0; + ULONGLONG tmp; + int i; + + /* The limbs number can change by up to 1 so shift <= 29 */ + assert(shift <= 29); + + for(i=b->b; ie; i++) { + tmp = ((ULONGLONG)b->data[bnum_idx(b, i)] << shift) + rest; + rest = tmp / LIMB_MAX; + b->data[bnum_idx(b, i)] = tmp % LIMB_MAX; + + if(i == b->b && !b->data[bnum_idx(b, i)]) + b->b++; + } + + if(rest) { + b->data[bnum_idx(b, b->e)] = rest; + b->e++; + + if(bnum_idx(b, b->b) == bnum_idx(b, b->e)) { + if(b->data[bnum_idx(b, b->b)]) b->data[bnum_idx(b, b->b+1)] |= 1; + b->b++; + } + return TRUE; + } + return FALSE; +} + +/* Returns TRUE if most significant limb was removed */ +static inline BOOL bnum_rshift(struct bnum *b, int shift) +{ + DWORD tmp, rest = 0; + BOOL ret = FALSE; + int i; + + /* Compute LIMB_MAX << shift without accuracy loss */ + assert(shift <= 9); + + for(i=b->e-1; i>=b->b; i--) { + tmp = b->data[bnum_idx(b, i)] & ((1<data[bnum_idx(b, i)] = (b->data[bnum_idx(b, i)] >> shift) + rest; + rest = (LIMB_MAX >> shift) * tmp; + if(i==b->e-1 && !b->data[bnum_idx(b, i)]) { + b->e--; + ret = TRUE; + } + } + + if(rest) { + if(bnum_idx(b, b->b-1) == bnum_idx(b, b->e)) { + if(rest) b->data[bnum_idx(b, b->b)] |= 1; + } else { + b->b--; + b->data[bnum_idx(b, b->b)] = rest; + } + } + return ret; +} + +static inline void bnum_mult(struct bnum *b, int mult) +{ + DWORD rest = 0; + ULONGLONG tmp; + int i; + + assert(mult <= LIMB_MAX); + + for(i=b->b; ie; i++) { + tmp = ((ULONGLONG)b->data[bnum_idx(b, i)] * mult) + rest; + rest = tmp / LIMB_MAX; + b->data[bnum_idx(b, i)] = tmp % LIMB_MAX; + + if(i == b->b && !b->data[bnum_idx(b, i)]) + b->b++; + } + + if(rest) { + b->data[bnum_idx(b, b->e)] = rest; + b->e++; + + if(bnum_idx(b, b->b) == bnum_idx(b, b->e)) { + if(b->data[bnum_idx(b, b->b)]) b->data[bnum_idx(b, b->b+1)] |= 1; + b->b++; + } + } +} + +#endif /* __WINE_BNUM_H */ diff --git a/dll/win32/msvcrt/concurrency.c b/dll/win32/msvcrt/concurrency.c new file mode 100644 index 00000000000..561b4d23289 --- /dev/null +++ b/dll/win32/msvcrt/concurrency.c @@ -0,0 +1,3839 @@ +/* + * Concurrency namespace implementation + * + * Copyright 2017 Piotr Caban + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include +#include + +#include "windef.h" +#include "winternl.h" +#include "wine/debug.h" +#include "wine/exception.h" +#include "wine/list.h" +#include "msvcrt.h" +#include "cppexcept.h" + +#if _MSVCR_VER >= 100 + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +typedef exception cexception; +CREATE_EXCEPTION_OBJECT(cexception) +DEFINE_CXX_TYPE_INFO(cexception) + +static LONG context_id = -1; +static LONG scheduler_id = -1; + +typedef enum { + SchedulerKind, + MaxConcurrency, + MinConcurrency, + TargetOversubscriptionFactor, + LocalContextCacheSize, + ContextStackSize, + ContextPriority, + SchedulingProtocol, + DynamicProgressFeedback, + WinRTInitialization, + last_policy_id +} PolicyElementKey; + +typedef struct { + struct _policy_container { + unsigned int policies[last_policy_id]; + } *policy_container; +} SchedulerPolicy; + +typedef struct { + const vtable_ptr *vtable; +} Context; +#define call_Context_GetId(this) CALL_VTBL_FUNC(this, 0, \ + unsigned int, (const Context*), (this)) +#define call_Context_GetVirtualProcessorId(this) CALL_VTBL_FUNC(this, 4, \ + unsigned int, (const Context*), (this)) +#define call_Context_GetScheduleGroupId(this) CALL_VTBL_FUNC(this, 8, \ + unsigned int, (const Context*), (this)) +#define call_Context_Unblock(this) CALL_VTBL_FUNC(this, 12, \ + void, (Context*), (this)) +#define call_Context_IsSynchronouslyBlocked(this) CALL_VTBL_FUNC(this, 16, \ + bool, (const Context*), (this)) +#define call_Context_dtor(this, flags) CALL_VTBL_FUNC(this, 20, \ + Context*, (Context*, unsigned int), (this, flags)) +#define call_Context_Block(this) CALL_VTBL_FUNC(this, 24, \ + void, (Context*), (this)) + +typedef struct { + Context *context; +} _Context; + +union allocator_cache_entry { + struct _free { + int depth; + union allocator_cache_entry *next; + } free; + struct _alloc { + int bucket; + char mem[1]; + } alloc; +}; + +struct scheduler_list { + struct Scheduler *scheduler; + struct scheduler_list *next; +}; + +struct beacon { + LONG cancelling; + struct list entry; + struct _StructuredTaskCollection *task_collection; +}; + +typedef struct { + Context context; + struct scheduler_list scheduler; + unsigned int id; + union allocator_cache_entry *allocator_cache[8]; + LONG blocked; + struct _StructuredTaskCollection *task_collection; + CRITICAL_SECTION beacons_cs; + struct list beacons; +} ExternalContextBase; +extern const vtable_ptr ExternalContextBase_vtable; +static void ExternalContextBase_ctor(ExternalContextBase*); + +typedef struct Scheduler { + const vtable_ptr *vtable; +} Scheduler; +#define call_Scheduler_Id(this) CALL_VTBL_FUNC(this, 4, unsigned int, (const Scheduler*), (this)) +#define call_Scheduler_GetNumberOfVirtualProcessors(this) CALL_VTBL_FUNC(this, 8, unsigned int, (const Scheduler*), (this)) +#define call_Scheduler_GetPolicy(this,policy) CALL_VTBL_FUNC(this, 12, \ + SchedulerPolicy*, (Scheduler*,SchedulerPolicy*), (this,policy)) +#define call_Scheduler_Reference(this) CALL_VTBL_FUNC(this, 16, unsigned int, (Scheduler*), (this)) +#define call_Scheduler_Release(this) CALL_VTBL_FUNC(this, 20, unsigned int, (Scheduler*), (this)) +#define call_Scheduler_RegisterShutdownEvent(this,event) CALL_VTBL_FUNC(this, 24, void, (Scheduler*,HANDLE), (this,event)) +#define call_Scheduler_Attach(this) CALL_VTBL_FUNC(this, 28, void, (Scheduler*), (this)) +#if _MSVCR_VER > 100 +#define call_Scheduler_CreateScheduleGroup_loc(this,placement) CALL_VTBL_FUNC(this, 32, \ + /*ScheduleGroup*/void*, (Scheduler*,/*location*/void*), (this,placement)) +#define call_Scheduler_CreateScheduleGroup(this) CALL_VTBL_FUNC(this, 36, /*ScheduleGroup*/void*, (Scheduler*), (this)) +#define call_Scheduler_ScheduleTask_loc(this,proc,data,placement) CALL_VTBL_FUNC(this, 40, \ + void, (Scheduler*,void (__cdecl*)(void*),void*,/*location*/void*), (this,proc,data,placement)) +#define call_Scheduler_ScheduleTask(this,proc,data) CALL_VTBL_FUNC(this, 44, \ + void, (Scheduler*,void (__cdecl*)(void*),void*), (this,proc,data)) +#define call_Scheduler_IsAvailableLocation(this,placement) CALL_VTBL_FUNC(this, 48, \ + bool, (Scheduler*,const /*location*/void*), (this,placement)) +#else +#define call_Scheduler_CreateScheduleGroup(this) CALL_VTBL_FUNC(this, 32, /*ScheduleGroup*/void*, (Scheduler*), (this)) +#define call_Scheduler_ScheduleTask(this,proc,data) CALL_VTBL_FUNC(this, 36, \ + void, (Scheduler*,void (__cdecl*)(void*),void*), (this,proc,data)) +#endif + +typedef struct { + Scheduler scheduler; + LONG ref; + unsigned int id; + unsigned int virt_proc_no; + SchedulerPolicy policy; + int shutdown_count; + int shutdown_size; + HANDLE *shutdown_events; + CRITICAL_SECTION cs; + struct list scheduled_chores; +} ThreadScheduler; +extern const vtable_ptr ThreadScheduler_vtable; + +typedef struct { + Scheduler *scheduler; +} _Scheduler; + +typedef struct { + char empty; +} _CurrentScheduler; + +typedef enum +{ + SPINWAIT_INIT, + SPINWAIT_SPIN, + SPINWAIT_YIELD, + SPINWAIT_DONE +} SpinWait_state; + +typedef void (__cdecl *yield_func)(void); + +typedef struct +{ + ULONG spin; + ULONG unknown; + SpinWait_state state; + yield_func yield_func; +} SpinWait; + +#define FINISHED_INITIAL 0x80000000 +typedef struct _StructuredTaskCollection +{ + void *unk1; + unsigned int unk2; + void *unk3; + Context *context; + volatile LONG count; + volatile LONG finished; + void *exception; + Context *event; +} _StructuredTaskCollection; + +bool __thiscall _StructuredTaskCollection__IsCanceling(_StructuredTaskCollection*); + +typedef enum +{ + TASK_COLLECTION_SUCCESS = 1, + TASK_COLLECTION_CANCELLED +} _TaskCollectionStatus; + +typedef enum +{ + STRUCTURED_TASK_COLLECTION_CANCELLED = 0x2, + STRUCTURED_TASK_COLLECTION_STATUS_MASK = 0x7 +} _StructuredTaskCollectionStatusBits; + +typedef struct _UnrealizedChore +{ + const vtable_ptr *vtable; + void (__cdecl *chore_proc)(struct _UnrealizedChore*); + _StructuredTaskCollection *task_collection; + void (__cdecl *chore_wrapper)(struct _UnrealizedChore*); + void *unk[6]; +} _UnrealizedChore; + +struct scheduled_chore { + struct list entry; + _UnrealizedChore *chore; +}; + +/* keep in sync with msvcp90/msvcp90.h */ +typedef struct cs_queue +{ + Context *ctx; + struct cs_queue *next; +#if _MSVCR_VER >= 110 + LONG free; + int unknown; +#endif +} cs_queue; + +typedef struct +{ + cs_queue unk_active; +#if _MSVCR_VER >= 110 + void *unknown[2]; +#else + void *unknown[1]; +#endif + cs_queue *head; + void *tail; +} critical_section; + +typedef struct +{ + critical_section *cs; + union { + cs_queue q; + struct { + void *unknown[4]; + int unknown2[2]; + } unknown; + } lock; +} critical_section_scoped_lock; + +typedef struct +{ + critical_section cs; +} _NonReentrantPPLLock; + +typedef struct +{ + _NonReentrantPPLLock *lock; + union { + cs_queue q; + struct { + void *unknown[4]; + int unknown2[2]; + } unknown; + } wait; +} _NonReentrantPPLLock__Scoped_lock; + +typedef struct +{ + critical_section cs; + LONG count; + LONG owner; +} _ReentrantPPLLock; + +typedef struct +{ + _ReentrantPPLLock *lock; + union { + cs_queue q; + struct { + void *unknown[4]; + int unknown2[2]; + } unknown; + } wait; +} _ReentrantPPLLock__Scoped_lock; + +typedef struct +{ + LONG state; + LONG count; +} _ReaderWriterLock; + +#define EVT_RUNNING (void*)1 +#define EVT_WAITING NULL + +struct thread_wait; +typedef struct thread_wait_entry +{ + struct thread_wait *wait; + struct thread_wait_entry *next; + struct thread_wait_entry *prev; +} thread_wait_entry; + +typedef struct thread_wait +{ + Context *ctx; + void *signaled; + LONG pending_waits; + thread_wait_entry entries[1]; +} thread_wait; + +typedef struct +{ + thread_wait_entry *waiters; + INT_PTR signaled; + critical_section cs; +} event; + +#if _MSVCR_VER >= 110 +#define CV_WAKE (void*)1 +typedef struct cv_queue { + Context *ctx; + struct cv_queue *next; + LONG expired; +} cv_queue; + +typedef struct { + struct beacon *beacon; +} _Cancellation_beacon; + +typedef struct { + /* cv_queue structure is not binary compatible */ + cv_queue *queue; + critical_section lock; +} _Condition_variable; +#endif + +typedef struct rwl_queue +{ + struct rwl_queue *next; + Context *ctx; +} rwl_queue; + +#define WRITER_WAITING 0x80000000 +/* FIXME: reader_writer_lock structure is not binary compatible + * it can't exceed 28/56 bytes */ +typedef struct +{ + LONG count; + LONG thread_id; + rwl_queue active; + rwl_queue *writer_head; + rwl_queue *writer_tail; + rwl_queue *reader_head; +} reader_writer_lock; + +typedef struct { + reader_writer_lock *lock; +} reader_writer_lock_scoped_lock; + +typedef struct { + CRITICAL_SECTION cs; +} _ReentrantBlockingLock; + +#define TICKSPERMSEC 10000 +typedef struct { + const vtable_ptr *vtable; + TP_TIMER *timer; + unsigned int elapse; + bool repeat; +} _Timer; +extern const vtable_ptr _Timer_vtable; +#define call__Timer_callback(this) CALL_VTBL_FUNC(this, 4, void, (_Timer*), (this)) + +typedef exception improper_lock; +extern const vtable_ptr improper_lock_vtable; + +typedef exception improper_scheduler_attach; +extern const vtable_ptr improper_scheduler_attach_vtable; + +typedef exception improper_scheduler_detach; +extern const vtable_ptr improper_scheduler_detach_vtable; + +typedef exception invalid_multiple_scheduling; +extern const vtable_ptr invalid_multiple_scheduling_vtable; + +typedef exception invalid_scheduler_policy_key; +extern const vtable_ptr invalid_scheduler_policy_key_vtable; + +typedef exception invalid_scheduler_policy_thread_specification; +extern const vtable_ptr invalid_scheduler_policy_thread_specification_vtable; + +typedef exception invalid_scheduler_policy_value; +extern const vtable_ptr invalid_scheduler_policy_value_vtable; + +typedef exception missing_wait; +extern const vtable_ptr missing_wait_vtable; + +typedef struct { + exception e; + HRESULT hr; +} scheduler_resource_allocation_error; +extern const vtable_ptr scheduler_resource_allocation_error_vtable; + +enum ConcRT_EventType +{ + CONCRT_EVENT_GENERIC, + CONCRT_EVENT_START, + CONCRT_EVENT_END, + CONCRT_EVENT_BLOCK, + CONCRT_EVENT_UNBLOCK, + CONCRT_EVENT_YIELD, + CONCRT_EVENT_ATTACH, + CONCRT_EVENT_DETACH +}; + +static DWORD context_tls_index = TLS_OUT_OF_INDEXES; + +static CRITICAL_SECTION default_scheduler_cs; +static CRITICAL_SECTION_DEBUG default_scheduler_cs_debug = +{ + 0, 0, &default_scheduler_cs, + { &default_scheduler_cs_debug.ProcessLocksList, &default_scheduler_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": default_scheduler_cs") } +}; +static CRITICAL_SECTION default_scheduler_cs = { &default_scheduler_cs_debug, -1, 0, 0, 0, 0 }; +static SchedulerPolicy default_scheduler_policy; +static ThreadScheduler *default_scheduler; + +static void create_default_scheduler(void); + +/* ??0improper_lock@Concurrency@@QAE@PBD@Z */ +/* ??0improper_lock@Concurrency@@QEAA@PEBD@Z */ +DEFINE_THISCALL_WRAPPER(improper_lock_ctor_str, 8) +improper_lock* __thiscall improper_lock_ctor_str(improper_lock *this, const char *str) +{ + TRACE("(%p %s)\n", this, str); + return __exception_ctor(this, str, &improper_lock_vtable); +} + +/* ??0improper_lock@Concurrency@@QAE@XZ */ +/* ??0improper_lock@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(improper_lock_ctor, 4) +improper_lock* __thiscall improper_lock_ctor(improper_lock *this) +{ + return improper_lock_ctor_str(this, NULL); +} + +DEFINE_THISCALL_WRAPPER(improper_lock_copy_ctor,8) +improper_lock * __thiscall improper_lock_copy_ctor(improper_lock *this, const improper_lock *rhs) +{ + TRACE("(%p %p)\n", this, rhs); + return __exception_copy_ctor(this, rhs, &improper_lock_vtable); +} + +/* ??0improper_scheduler_attach@Concurrency@@QAE@PBD@Z */ +/* ??0improper_scheduler_attach@Concurrency@@QEAA@PEBD@Z */ +DEFINE_THISCALL_WRAPPER(improper_scheduler_attach_ctor_str, 8) +improper_scheduler_attach* __thiscall improper_scheduler_attach_ctor_str( + improper_scheduler_attach *this, const char *str) +{ + TRACE("(%p %s)\n", this, str); + return __exception_ctor(this, str, &improper_scheduler_attach_vtable); +} + +/* ??0improper_scheduler_attach@Concurrency@@QAE@XZ */ +/* ??0improper_scheduler_attach@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(improper_scheduler_attach_ctor, 4) +improper_scheduler_attach* __thiscall improper_scheduler_attach_ctor( + improper_scheduler_attach *this) +{ + return improper_scheduler_attach_ctor_str(this, NULL); +} + +DEFINE_THISCALL_WRAPPER(improper_scheduler_attach_copy_ctor,8) +improper_scheduler_attach * __thiscall improper_scheduler_attach_copy_ctor( + improper_scheduler_attach * _this, const improper_scheduler_attach * rhs) +{ + TRACE("(%p %p)\n", _this, rhs); + return __exception_copy_ctor(_this, rhs, &improper_scheduler_attach_vtable); +} + +/* ??0improper_scheduler_detach@Concurrency@@QAE@PBD@Z */ +/* ??0improper_scheduler_detach@Concurrency@@QEAA@PEBD@Z */ +DEFINE_THISCALL_WRAPPER(improper_scheduler_detach_ctor_str, 8) +improper_scheduler_detach* __thiscall improper_scheduler_detach_ctor_str( + improper_scheduler_detach *this, const char *str) +{ + TRACE("(%p %s)\n", this, str); + return __exception_ctor(this, str, &improper_scheduler_detach_vtable); +} + +/* ??0improper_scheduler_detach@Concurrency@@QAE@XZ */ +/* ??0improper_scheduler_detach@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(improper_scheduler_detach_ctor, 4) +improper_scheduler_detach* __thiscall improper_scheduler_detach_ctor( + improper_scheduler_detach *this) +{ + return improper_scheduler_detach_ctor_str(this, NULL); +} + +DEFINE_THISCALL_WRAPPER(improper_scheduler_detach_copy_ctor,8) +improper_scheduler_detach * __thiscall improper_scheduler_detach_copy_ctor( + improper_scheduler_detach * _this, const improper_scheduler_detach * rhs) +{ + TRACE("(%p %p)\n", _this, rhs); + return __exception_copy_ctor(_this, rhs, &improper_scheduler_detach_vtable); +} + +/* ??0invalid_multiple_scheduling@Concurrency@@QAA@PBD@Z */ +/* ??0invalid_multiple_scheduling@Concurrency@@QAE@PBD@Z */ +/* ??0invalid_multiple_scheduling@Concurrency@@QEAA@PEBD@Z */ +DEFINE_THISCALL_WRAPPER(invalid_multiple_scheduling_ctor_str, 8) +invalid_multiple_scheduling* __thiscall invalid_multiple_scheduling_ctor_str( + invalid_multiple_scheduling *this, const char *str) +{ + TRACE("(%p %s)\n", this, str); + return __exception_ctor(this, str, &invalid_multiple_scheduling_vtable); +} + +/* ??0invalid_multiple_scheduling@Concurrency@@QAA@XZ */ +/* ??0invalid_multiple_scheduling@Concurrency@@QAE@XZ */ +/* ??0invalid_multiple_scheduling@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(invalid_multiple_scheduling_ctor, 4) +invalid_multiple_scheduling* __thiscall invalid_multiple_scheduling_ctor( + invalid_multiple_scheduling *this) +{ + return invalid_multiple_scheduling_ctor_str(this, NULL); +} + +DEFINE_THISCALL_WRAPPER(invalid_multiple_scheduling_copy_ctor,8) +invalid_multiple_scheduling * __thiscall invalid_multiple_scheduling_copy_ctor( + invalid_multiple_scheduling * _this, const invalid_multiple_scheduling * rhs) +{ + TRACE("(%p %p)\n", _this, rhs); + return __exception_copy_ctor(_this, rhs, &invalid_multiple_scheduling_vtable); +} + +/* ??0invalid_scheduler_policy_key@Concurrency@@QAE@PBD@Z */ +/* ??0invalid_scheduler_policy_key@Concurrency@@QEAA@PEBD@Z */ +DEFINE_THISCALL_WRAPPER(invalid_scheduler_policy_key_ctor_str, 8) +invalid_scheduler_policy_key* __thiscall invalid_scheduler_policy_key_ctor_str( + invalid_scheduler_policy_key *this, const char *str) +{ + TRACE("(%p %s)\n", this, str); + return __exception_ctor(this, str, &invalid_scheduler_policy_key_vtable); +} + +/* ??0invalid_scheduler_policy_key@Concurrency@@QAE@XZ */ +/* ??0invalid_scheduler_policy_key@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(invalid_scheduler_policy_key_ctor, 4) +invalid_scheduler_policy_key* __thiscall invalid_scheduler_policy_key_ctor( + invalid_scheduler_policy_key *this) +{ + return invalid_scheduler_policy_key_ctor_str(this, NULL); +} + +DEFINE_THISCALL_WRAPPER(invalid_scheduler_policy_key_copy_ctor,8) +invalid_scheduler_policy_key * __thiscall invalid_scheduler_policy_key_copy_ctor( + invalid_scheduler_policy_key * _this, const invalid_scheduler_policy_key * rhs) +{ + TRACE("(%p %p)\n", _this, rhs); + return __exception_copy_ctor(_this, rhs, &invalid_scheduler_policy_key_vtable); +} + +/* ??0invalid_scheduler_policy_thread_specification@Concurrency@@QAE@PBD@Z */ +/* ??0invalid_scheduler_policy_thread_specification@Concurrency@@QEAA@PEBD@Z */ +DEFINE_THISCALL_WRAPPER(invalid_scheduler_policy_thread_specification_ctor_str, 8) +invalid_scheduler_policy_thread_specification* __thiscall invalid_scheduler_policy_thread_specification_ctor_str( + invalid_scheduler_policy_thread_specification *this, const char *str) +{ + TRACE("(%p %s)\n", this, str); + return __exception_ctor(this, str, &invalid_scheduler_policy_thread_specification_vtable); +} + +/* ??0invalid_scheduler_policy_thread_specification@Concurrency@@QAE@XZ */ +/* ??0invalid_scheduler_policy_thread_specification@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(invalid_scheduler_policy_thread_specification_ctor, 4) +invalid_scheduler_policy_thread_specification* __thiscall invalid_scheduler_policy_thread_specification_ctor( + invalid_scheduler_policy_thread_specification *this) +{ + return invalid_scheduler_policy_thread_specification_ctor_str(this, NULL); +} + +DEFINE_THISCALL_WRAPPER(invalid_scheduler_policy_thread_specification_copy_ctor,8) +invalid_scheduler_policy_thread_specification * __thiscall invalid_scheduler_policy_thread_specification_copy_ctor( + invalid_scheduler_policy_thread_specification * _this, const invalid_scheduler_policy_thread_specification * rhs) +{ + TRACE("(%p %p)\n", _this, rhs); + return __exception_copy_ctor(_this, rhs, &invalid_scheduler_policy_thread_specification_vtable); +} + +/* ??0invalid_scheduler_policy_value@Concurrency@@QAE@PBD@Z */ +/* ??0invalid_scheduler_policy_value@Concurrency@@QEAA@PEBD@Z */ +DEFINE_THISCALL_WRAPPER(invalid_scheduler_policy_value_ctor_str, 8) +invalid_scheduler_policy_value* __thiscall invalid_scheduler_policy_value_ctor_str( + invalid_scheduler_policy_value *this, const char *str) +{ + TRACE("(%p %s)\n", this, str); + return __exception_ctor(this, str, &invalid_scheduler_policy_value_vtable); +} + +/* ??0invalid_scheduler_policy_value@Concurrency@@QAE@XZ */ +/* ??0invalid_scheduler_policy_value@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(invalid_scheduler_policy_value_ctor, 4) +invalid_scheduler_policy_value* __thiscall invalid_scheduler_policy_value_ctor( + invalid_scheduler_policy_value *this) +{ + return invalid_scheduler_policy_value_ctor_str(this, NULL); +} + +DEFINE_THISCALL_WRAPPER(invalid_scheduler_policy_value_copy_ctor,8) +invalid_scheduler_policy_value * __thiscall invalid_scheduler_policy_value_copy_ctor( + invalid_scheduler_policy_value * _this, const invalid_scheduler_policy_value * rhs) +{ + TRACE("(%p %p)\n", _this, rhs); + return __exception_copy_ctor(_this, rhs, &invalid_scheduler_policy_value_vtable); +} + +/* ??0missing_wait@Concurrency@@QAA@PBD@Z */ +/* ??0missing_wait@Concurrency@@QAE@PBD@Z */ +/* ??0missing_wait@Concurrency@@QEAA@PEBD@Z */ +DEFINE_THISCALL_WRAPPER(missing_wait_ctor_str, 8) +missing_wait* __thiscall missing_wait_ctor_str( + missing_wait *this, const char *str) +{ + TRACE("(%p %p)\n", this, str); + return __exception_ctor(this, str, &missing_wait_vtable); +} + +/* ??0missing_wait@Concurrency@@QAA@XZ */ +/* ??0missing_wait@Concurrency@@QAE@XZ */ +/* ??0missing_wait@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(missing_wait_ctor, 4) +missing_wait* __thiscall missing_wait_ctor(missing_wait *this) +{ + return missing_wait_ctor_str(this, NULL); +} + +DEFINE_THISCALL_WRAPPER(missing_wait_copy_ctor,8) +missing_wait * __thiscall missing_wait_copy_ctor( + missing_wait * _this, const missing_wait * rhs) +{ + TRACE("(%p %p)\n", _this, rhs); + return __exception_copy_ctor(_this, rhs, &missing_wait_vtable); +} + +/* ??0scheduler_resource_allocation_error@Concurrency@@QAE@PBDJ@Z */ +/* ??0scheduler_resource_allocation_error@Concurrency@@QEAA@PEBDJ@Z */ +DEFINE_THISCALL_WRAPPER(scheduler_resource_allocation_error_ctor_name, 12) +scheduler_resource_allocation_error* __thiscall scheduler_resource_allocation_error_ctor_name( + scheduler_resource_allocation_error *this, const char *name, HRESULT hr) +{ + TRACE("(%p %s %lx)\n", this, wine_dbgstr_a(name), hr); + __exception_ctor(&this->e, name, &scheduler_resource_allocation_error_vtable); + this->hr = hr; + return this; +} + +/* ??0scheduler_resource_allocation_error@Concurrency@@QAE@J@Z */ +/* ??0scheduler_resource_allocation_error@Concurrency@@QEAA@J@Z */ +DEFINE_THISCALL_WRAPPER(scheduler_resource_allocation_error_ctor, 8) +scheduler_resource_allocation_error* __thiscall scheduler_resource_allocation_error_ctor( + scheduler_resource_allocation_error *this, HRESULT hr) +{ + return scheduler_resource_allocation_error_ctor_name(this, NULL, hr); +} + +DEFINE_THISCALL_WRAPPER(scheduler_resource_allocation_error_copy_ctor,8) +scheduler_resource_allocation_error* __thiscall scheduler_resource_allocation_error_copy_ctor( + scheduler_resource_allocation_error *this, + const scheduler_resource_allocation_error *rhs) +{ + TRACE("(%p,%p)\n", this, rhs); + + if (!rhs->e.do_free) + memcpy(this, rhs, sizeof(*this)); + else + scheduler_resource_allocation_error_ctor_name(this, rhs->e.name, rhs->hr); + return this; +} + +/* ?get_error_code@scheduler_resource_allocation_error@Concurrency@@QBEJXZ */ +/* ?get_error_code@scheduler_resource_allocation_error@Concurrency@@QEBAJXZ */ +DEFINE_THISCALL_WRAPPER(scheduler_resource_allocation_error_get_error_code, 4) +HRESULT __thiscall scheduler_resource_allocation_error_get_error_code( + const scheduler_resource_allocation_error *this) +{ + TRACE("(%p)\n", this); + return this->hr; +} + +DEFINE_RTTI_DATA1(improper_lock, 0, &cexception_rtti_base_descriptor, + ".?AVimproper_lock@Concurrency@@") +DEFINE_RTTI_DATA1(improper_scheduler_attach, 0, &cexception_rtti_base_descriptor, + ".?AVimproper_scheduler_attach@Concurrency@@") +DEFINE_RTTI_DATA1(improper_scheduler_detach, 0, &cexception_rtti_base_descriptor, + ".?AVimproper_scheduler_detach@Concurrency@@") +DEFINE_RTTI_DATA1(invalid_multiple_scheduling, 0, &cexception_rtti_base_descriptor, + ".?AVinvalid_multiple_scheduling@Concurrency@@") +DEFINE_RTTI_DATA1(invalid_scheduler_policy_key, 0, &cexception_rtti_base_descriptor, + ".?AVinvalid_scheduler_policy_key@Concurrency@@") +DEFINE_RTTI_DATA1(invalid_scheduler_policy_thread_specification, 0, &cexception_rtti_base_descriptor, + ".?AVinvalid_scheduler_policy_thread_specification@Concurrency@@") +DEFINE_RTTI_DATA1(invalid_scheduler_policy_value, 0, &cexception_rtti_base_descriptor, + ".?AVinvalid_scheduler_policy_value@Concurrency@@") +DEFINE_RTTI_DATA1(missing_wait, 0, &cexception_rtti_base_descriptor, + ".?AVmissing_wait@Concurrency@@") +DEFINE_RTTI_DATA1(scheduler_resource_allocation_error, 0, &cexception_rtti_base_descriptor, + ".?AVscheduler_resource_allocation_error@Concurrency@@") + +DEFINE_CXX_DATA1(improper_lock, &cexception_cxx_type_info, cexception_dtor) +DEFINE_CXX_DATA1(improper_scheduler_attach, &cexception_cxx_type_info, cexception_dtor) +DEFINE_CXX_DATA1(improper_scheduler_detach, &cexception_cxx_type_info, cexception_dtor) +DEFINE_CXX_DATA1(invalid_multiple_scheduling, &cexception_cxx_type_info, cexception_dtor) +DEFINE_CXX_DATA1(invalid_scheduler_policy_key, &cexception_cxx_type_info, cexception_dtor) +DEFINE_CXX_DATA1(invalid_scheduler_policy_thread_specification, &cexception_cxx_type_info, cexception_dtor) +DEFINE_CXX_DATA1(invalid_scheduler_policy_value, &cexception_cxx_type_info, cexception_dtor) +#if _MSVCR_VER >= 120 +DEFINE_CXX_DATA1(missing_wait, &cexception_cxx_type_info, cexception_dtor) +#endif +DEFINE_CXX_DATA1(scheduler_resource_allocation_error, &cexception_cxx_type_info, cexception_dtor) + +__ASM_BLOCK_BEGIN(concurrency_exception_vtables) + __ASM_VTABLE(improper_lock, + VTABLE_ADD_FUNC(cexception_vector_dtor) + VTABLE_ADD_FUNC(cexception_what)); + __ASM_VTABLE(improper_scheduler_attach, + VTABLE_ADD_FUNC(cexception_vector_dtor) + VTABLE_ADD_FUNC(cexception_what)); + __ASM_VTABLE(improper_scheduler_detach, + VTABLE_ADD_FUNC(cexception_vector_dtor) + VTABLE_ADD_FUNC(cexception_what)); + __ASM_VTABLE(invalid_multiple_scheduling, + VTABLE_ADD_FUNC(cexception_vector_dtor) + VTABLE_ADD_FUNC(cexception_what)); + __ASM_VTABLE(invalid_scheduler_policy_key, + VTABLE_ADD_FUNC(cexception_vector_dtor) + VTABLE_ADD_FUNC(cexception_what)); + __ASM_VTABLE(invalid_scheduler_policy_thread_specification, + VTABLE_ADD_FUNC(cexception_vector_dtor) + VTABLE_ADD_FUNC(cexception_what)); + __ASM_VTABLE(invalid_scheduler_policy_value, + VTABLE_ADD_FUNC(cexception_vector_dtor) + VTABLE_ADD_FUNC(cexception_what)); + __ASM_VTABLE(missing_wait, + VTABLE_ADD_FUNC(cexception_vector_dtor) + VTABLE_ADD_FUNC(cexception_what)); + __ASM_VTABLE(scheduler_resource_allocation_error, + VTABLE_ADD_FUNC(cexception_vector_dtor) + VTABLE_ADD_FUNC(cexception_what)); +__ASM_BLOCK_END + +static Context* try_get_current_context(void) +{ + if (context_tls_index == TLS_OUT_OF_INDEXES) + return NULL; + return TlsGetValue(context_tls_index); +} + +static BOOL WINAPI init_context_tls_index(INIT_ONCE *once, void *param, void **context) +{ + context_tls_index = TlsAlloc(); + return context_tls_index != TLS_OUT_OF_INDEXES; +} + +static Context* get_current_context(void) +{ + static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; + Context *ret; + + if(!InitOnceExecuteOnce(&init_once, init_context_tls_index, NULL, NULL)) + { + scheduler_resource_allocation_error e; + scheduler_resource_allocation_error_ctor_name(&e, NULL, + HRESULT_FROM_WIN32(GetLastError())); + _CxxThrowException(&e, &scheduler_resource_allocation_error_exception_type); + } + + ret = TlsGetValue(context_tls_index); + if (!ret) { + ExternalContextBase *context = operator_new(sizeof(ExternalContextBase)); + ExternalContextBase_ctor(context); + TlsSetValue(context_tls_index, context); + ret = &context->context; + } + return ret; +} + +static Scheduler* get_scheduler_from_context(Context *ctx) +{ + ExternalContextBase *context = (ExternalContextBase*)ctx; + + if (context->context.vtable != &ExternalContextBase_vtable) + return NULL; + return context->scheduler.scheduler; +} + +static Scheduler* try_get_current_scheduler(void) +{ + Context *context = try_get_current_context(); + Scheduler *ret; + + if (!context) + return NULL; + + ret = get_scheduler_from_context(context); + if (!ret) + ERR("unknown context set\n"); + return ret; +} + +static Scheduler* get_current_scheduler(void) +{ + Context *context = get_current_context(); + Scheduler *ret; + + ret = get_scheduler_from_context(context); + if (!ret) + ERR("unknown context set\n"); + return ret; +} + +/* ?CurrentContext@Context@Concurrency@@SAPAV12@XZ */ +/* ?CurrentContext@Context@Concurrency@@SAPEAV12@XZ */ +Context* __cdecl Context_CurrentContext(void) +{ + TRACE("()\n"); + return get_current_context(); +} + +/* ?Id@Context@Concurrency@@SAIXZ */ +unsigned int __cdecl Context_Id(void) +{ + Context *ctx = try_get_current_context(); + TRACE("()\n"); + return ctx ? call_Context_GetId(ctx) : -1; +} + +/* ?Block@Context@Concurrency@@SAXXZ */ +void __cdecl Context_Block(void) +{ + Context *ctx = get_current_context(); + TRACE("()\n"); + call_Context_Block(ctx); +} + +/* ?Yield@Context@Concurrency@@SAXXZ */ +/* ?_Yield@_Context@details@Concurrency@@SAXXZ */ +void __cdecl Context_Yield(void) +{ + static unsigned int once; + + if (!once++) + FIXME("()\n"); +} + +/* ?_SpinYield@Context@Concurrency@@SAXXZ */ +void __cdecl Context__SpinYield(void) +{ + FIXME("()\n"); +} + +/* ?IsCurrentTaskCollectionCanceling@Context@Concurrency@@SA_NXZ */ +bool __cdecl Context_IsCurrentTaskCollectionCanceling(void) +{ + ExternalContextBase *ctx = (ExternalContextBase*)try_get_current_context(); + + TRACE("()\n"); + + if (ctx && ctx->context.vtable != &ExternalContextBase_vtable) { + ERR("unknown context set\n"); + return FALSE; + } + + if (ctx && ctx->task_collection) + return _StructuredTaskCollection__IsCanceling(ctx->task_collection); + return FALSE; +} + +/* ?Oversubscribe@Context@Concurrency@@SAX_N@Z */ +void __cdecl Context_Oversubscribe(bool begin) +{ + FIXME("(%x)\n", begin); +} + +/* ?ScheduleGroupId@Context@Concurrency@@SAIXZ */ +unsigned int __cdecl Context_ScheduleGroupId(void) +{ + Context *ctx = try_get_current_context(); + TRACE("()\n"); + return ctx ? call_Context_GetScheduleGroupId(ctx) : -1; +} + +/* ?VirtualProcessorId@Context@Concurrency@@SAIXZ */ +unsigned int __cdecl Context_VirtualProcessorId(void) +{ + Context *ctx = try_get_current_context(); + TRACE("()\n"); + return ctx ? call_Context_GetVirtualProcessorId(ctx) : -1; +} + +#if _MSVCR_VER > 100 +/* ?_CurrentContext@_Context@details@Concurrency@@SA?AV123@XZ */ +_Context *__cdecl _Context__CurrentContext(_Context *ret) +{ + TRACE("(%p)\n", ret); + ret->context = Context_CurrentContext(); + return ret; +} + +DEFINE_THISCALL_WRAPPER(_Context_IsSynchronouslyBlocked, 4) +BOOL __thiscall _Context_IsSynchronouslyBlocked(const _Context *this) +{ + TRACE("(%p)\n", this); + return call_Context_IsSynchronouslyBlocked(this->context); +} +#endif + +DEFINE_THISCALL_WRAPPER(ExternalContextBase_GetId, 4) +unsigned int __thiscall ExternalContextBase_GetId(const ExternalContextBase *this) +{ + TRACE("(%p)->()\n", this); + return this->id; +} + +DEFINE_THISCALL_WRAPPER(ExternalContextBase_GetVirtualProcessorId, 4) +unsigned int __thiscall ExternalContextBase_GetVirtualProcessorId(const ExternalContextBase *this) +{ + FIXME("(%p)->() stub\n", this); + return -1; +} + +DEFINE_THISCALL_WRAPPER(ExternalContextBase_GetScheduleGroupId, 4) +unsigned int __thiscall ExternalContextBase_GetScheduleGroupId(const ExternalContextBase *this) +{ + FIXME("(%p)->() stub\n", this); + return -1; +} + +DEFINE_THISCALL_WRAPPER(ExternalContextBase_Unblock, 4) +void __thiscall ExternalContextBase_Unblock(ExternalContextBase *this) +{ + TRACE("(%p)->()\n", this); + + /* TODO: throw context_unblock_unbalanced if this->blocked goes below -1 */ + if (!InterlockedDecrement(&this->blocked)) + RtlWakeAddressSingle(&this->blocked); +} + +DEFINE_THISCALL_WRAPPER(ExternalContextBase_IsSynchronouslyBlocked, 4) +bool __thiscall ExternalContextBase_IsSynchronouslyBlocked(const ExternalContextBase *this) +{ + TRACE("(%p)->()\n", this); + return this->blocked >= 1; +} + +DEFINE_THISCALL_WRAPPER(ExternalContextBase_Block, 4) +void __thiscall ExternalContextBase_Block(ExternalContextBase *this) +{ + LONG blocked; + + TRACE("(%p)->()\n", this); + + blocked = InterlockedIncrement(&this->blocked); + while (blocked >= 1) + { + RtlWaitOnAddress(&this->blocked, &blocked, sizeof(LONG), NULL); + blocked = this->blocked; + } +} + +DEFINE_THISCALL_WRAPPER(ExternalContextBase_Yield, 4) +void __thiscall ExternalContextBase_Yield(ExternalContextBase *this) +{ + FIXME("(%p)->() stub\n", this); +} + +DEFINE_THISCALL_WRAPPER(ExternalContextBase_SpinYield, 4) +void __thiscall ExternalContextBase_SpinYield(ExternalContextBase *this) +{ + FIXME("(%p)->() stub\n", this); +} + +DEFINE_THISCALL_WRAPPER(ExternalContextBase_Oversubscribe, 8) +void __thiscall ExternalContextBase_Oversubscribe( + ExternalContextBase *this, bool oversubscribe) +{ + FIXME("(%p)->(%x) stub\n", this, oversubscribe); +} + +DEFINE_THISCALL_WRAPPER(ExternalContextBase_Alloc, 8) +void* __thiscall ExternalContextBase_Alloc(ExternalContextBase *this, size_t size) +{ + FIXME("(%p)->(%Iu) stub\n", this, size); + return NULL; +} + +DEFINE_THISCALL_WRAPPER(ExternalContextBase_Free, 8) +void __thiscall ExternalContextBase_Free(ExternalContextBase *this, void *addr) +{ + FIXME("(%p)->(%p) stub\n", this, addr); +} + +DEFINE_THISCALL_WRAPPER(ExternalContextBase_EnterCriticalRegionHelper, 4) +int __thiscall ExternalContextBase_EnterCriticalRegionHelper(ExternalContextBase *this) +{ + FIXME("(%p)->() stub\n", this); + return 0; +} + +DEFINE_THISCALL_WRAPPER(ExternalContextBase_EnterHyperCriticalRegionHelper, 4) +int __thiscall ExternalContextBase_EnterHyperCriticalRegionHelper(ExternalContextBase *this) +{ + FIXME("(%p)->() stub\n", this); + return 0; +} + +DEFINE_THISCALL_WRAPPER(ExternalContextBase_ExitCriticalRegionHelper, 4) +int __thiscall ExternalContextBase_ExitCriticalRegionHelper(ExternalContextBase *this) +{ + FIXME("(%p)->() stub\n", this); + return 0; +} + +DEFINE_THISCALL_WRAPPER(ExternalContextBase_ExitHyperCriticalRegionHelper, 4) +int __thiscall ExternalContextBase_ExitHyperCriticalRegionHelper(ExternalContextBase *this) +{ + FIXME("(%p)->() stub\n", this); + return 0; +} + +DEFINE_THISCALL_WRAPPER(ExternalContextBase_GetCriticalRegionType, 4) +int __thiscall ExternalContextBase_GetCriticalRegionType(const ExternalContextBase *this) +{ + FIXME("(%p)->() stub\n", this); + return 0; +} + +DEFINE_THISCALL_WRAPPER(ExternalContextBase_GetContextKind, 4) +int __thiscall ExternalContextBase_GetContextKind(const ExternalContextBase *this) +{ + FIXME("(%p)->() stub\n", this); + return 0; +} + +static void remove_scheduled_chores(Scheduler *scheduler, const ExternalContextBase *context) +{ + ThreadScheduler *tscheduler = (ThreadScheduler*)scheduler; + struct scheduled_chore *sc, *next; + + if (tscheduler->scheduler.vtable != &ThreadScheduler_vtable) + return; + + EnterCriticalSection(&tscheduler->cs); + LIST_FOR_EACH_ENTRY_SAFE(sc, next, &tscheduler->scheduled_chores, + struct scheduled_chore, entry) { + if (sc->chore->task_collection->context == &context->context) { + list_remove(&sc->entry); + operator_delete(sc); + } + } + LeaveCriticalSection(&tscheduler->cs); +} + +static void ExternalContextBase_dtor(ExternalContextBase *this) +{ + struct scheduler_list *scheduler_cur, *scheduler_next; + union allocator_cache_entry *next, *cur; + int i; + + /* TODO: move the allocator cache to scheduler so it can be reused */ + for(i=0; iallocator_cache); i++) { + for(cur = this->allocator_cache[i]; cur; cur=next) { + next = cur->free.next; + operator_delete(cur); + } + } + + if (this->scheduler.scheduler) { + remove_scheduled_chores(this->scheduler.scheduler, this); + call_Scheduler_Release(this->scheduler.scheduler); + + for(scheduler_cur=this->scheduler.next; scheduler_cur; scheduler_cur=scheduler_next) { + scheduler_next = scheduler_cur->next; + remove_scheduled_chores(scheduler_cur->scheduler, this); + call_Scheduler_Release(scheduler_cur->scheduler); + operator_delete(scheduler_cur); + } + } + + DeleteCriticalSection(&this->beacons_cs); + if (!list_empty(&this->beacons)) + ERR("beacons list is not empty - expect crash\n"); +} + +DEFINE_THISCALL_WRAPPER(ExternalContextBase_vector_dtor, 8) +Context* __thiscall ExternalContextBase_vector_dtor(ExternalContextBase *this, unsigned int flags) +{ + TRACE("(%p %x)\n", this, flags); + if(flags & 2) { + /* we have an array, with the number of elements stored before the first object */ + INT_PTR i, *ptr = (INT_PTR *)this-1; + + for(i=*ptr-1; i>=0; i--) + ExternalContextBase_dtor(this+i); + operator_delete(ptr); + } else { + ExternalContextBase_dtor(this); + if(flags & 1) + operator_delete(this); + } + + return &this->context; +} + +static void ExternalContextBase_ctor(ExternalContextBase *this) +{ + TRACE("(%p)->()\n", this); + + memset(this, 0, sizeof(*this)); + this->context.vtable = &ExternalContextBase_vtable; + this->id = InterlockedIncrement(&context_id); + InitializeCriticalSection(&this->beacons_cs); + list_init(&this->beacons); + + create_default_scheduler(); + this->scheduler.scheduler = &default_scheduler->scheduler; + call_Scheduler_Reference(&default_scheduler->scheduler); +} + +/* ?Alloc@Concurrency@@YAPAXI@Z */ +/* ?Alloc@Concurrency@@YAPEAX_K@Z */ +void * CDECL Concurrency_Alloc(size_t size) +{ + ExternalContextBase *context = (ExternalContextBase*)get_current_context(); + union allocator_cache_entry *p; + + size += FIELD_OFFSET(union allocator_cache_entry, alloc.mem); + if (size < sizeof(*p)) + size = sizeof(*p); + + if (context->context.vtable != &ExternalContextBase_vtable) { + p = operator_new(size); + p->alloc.bucket = -1; + }else { + int i; + + C_ASSERT(sizeof(union allocator_cache_entry) <= 1 << 4); + for(i=0; iallocator_cache); i++) + if (1 << (i+4) >= size) break; + + if(i==ARRAY_SIZE(context->allocator_cache)) { + p = operator_new(size); + p->alloc.bucket = -1; + }else if (context->allocator_cache[i]) { + p = context->allocator_cache[i]; + context->allocator_cache[i] = p->free.next; + p->alloc.bucket = i; + }else { + p = operator_new(1 << (i+4)); + p->alloc.bucket = i; + } + } + + TRACE("(%Iu) returning %p\n", size, p->alloc.mem); + return p->alloc.mem; +} + +/* ?Free@Concurrency@@YAXPAX@Z */ +/* ?Free@Concurrency@@YAXPEAX@Z */ +void CDECL Concurrency_Free(void* mem) +{ + union allocator_cache_entry *p = (union allocator_cache_entry*)((char*)mem-FIELD_OFFSET(union allocator_cache_entry, alloc.mem)); + ExternalContextBase *context = (ExternalContextBase*)get_current_context(); + int bucket = p->alloc.bucket; + + TRACE("(%p)\n", mem); + + if (context->context.vtable != &ExternalContextBase_vtable) { + operator_delete(p); + }else { + if(bucket >= 0 && bucket < ARRAY_SIZE(context->allocator_cache) && + (!context->allocator_cache[bucket] || context->allocator_cache[bucket]->free.depth < 20)) { + p->free.next = context->allocator_cache[bucket]; + p->free.depth = p->free.next ? p->free.next->free.depth+1 : 0; + context->allocator_cache[bucket] = p; + }else { + operator_delete(p); + } + } +} + +/* ?SetPolicyValue@SchedulerPolicy@Concurrency@@QAEIW4PolicyElementKey@2@I@Z */ +/* ?SetPolicyValue@SchedulerPolicy@Concurrency@@QEAAIW4PolicyElementKey@2@I@Z */ +DEFINE_THISCALL_WRAPPER(SchedulerPolicy_SetPolicyValue, 12) +unsigned int __thiscall SchedulerPolicy_SetPolicyValue(SchedulerPolicy *this, + PolicyElementKey policy, unsigned int val) +{ + unsigned int ret; + + TRACE("(%p %d %d)\n", this, policy, val); + + if (policy == MinConcurrency) { + invalid_scheduler_policy_key e; + invalid_scheduler_policy_key_ctor_str(&e, "MinConcurrency"); + _CxxThrowException(&e, &invalid_scheduler_policy_key_exception_type); + } + if (policy == MaxConcurrency) { + invalid_scheduler_policy_key e; + invalid_scheduler_policy_key_ctor_str(&e, "MaxConcurrency"); + _CxxThrowException(&e, &invalid_scheduler_policy_key_exception_type); + } + if (policy >= last_policy_id) { + invalid_scheduler_policy_key e; + invalid_scheduler_policy_key_ctor_str(&e, "Invalid policy"); + _CxxThrowException(&e, &invalid_scheduler_policy_key_exception_type); + } + + switch(policy) { + case SchedulerKind: + if (val) { + invalid_scheduler_policy_value e; + invalid_scheduler_policy_value_ctor_str(&e, "SchedulerKind"); + _CxxThrowException(&e, &invalid_scheduler_policy_value_exception_type); + } + break; + case TargetOversubscriptionFactor: + if (!val) { + invalid_scheduler_policy_value e; + invalid_scheduler_policy_value_ctor_str(&e, "TargetOversubscriptionFactor"); + _CxxThrowException(&e, &invalid_scheduler_policy_value_exception_type); + } + break; + case ContextPriority: + if (((int)val < -7 /* THREAD_PRIORITY_REALTIME_LOWEST */ + || val > 6 /* THREAD_PRIORITY_REALTIME_HIGHEST */) + && val != THREAD_PRIORITY_IDLE && val != THREAD_PRIORITY_TIME_CRITICAL + && val != INHERIT_THREAD_PRIORITY) { + invalid_scheduler_policy_value e; + invalid_scheduler_policy_value_ctor_str(&e, "ContextPriority"); + _CxxThrowException(&e, &invalid_scheduler_policy_value_exception_type); + } + break; + case SchedulingProtocol: + case DynamicProgressFeedback: + case WinRTInitialization: + if (val != 0 && val != 1) { + invalid_scheduler_policy_value e; + invalid_scheduler_policy_value_ctor_str(&e, "SchedulingProtocol"); + _CxxThrowException(&e, &invalid_scheduler_policy_value_exception_type); + } + break; + default: + break; + } + + ret = this->policy_container->policies[policy]; + this->policy_container->policies[policy] = val; + return ret; +} + +/* ?SetConcurrencyLimits@SchedulerPolicy@Concurrency@@QAEXII@Z */ +/* ?SetConcurrencyLimits@SchedulerPolicy@Concurrency@@QEAAXII@Z */ +DEFINE_THISCALL_WRAPPER(SchedulerPolicy_SetConcurrencyLimits, 12) +void __thiscall SchedulerPolicy_SetConcurrencyLimits(SchedulerPolicy *this, + unsigned int min_concurrency, unsigned int max_concurrency) +{ + TRACE("(%p %d %d)\n", this, min_concurrency, max_concurrency); + + if (min_concurrency > max_concurrency) { + invalid_scheduler_policy_thread_specification e; + invalid_scheduler_policy_thread_specification_ctor_str(&e, NULL); + _CxxThrowException(&e, &invalid_scheduler_policy_thread_specification_exception_type); + } + if (!max_concurrency) { + invalid_scheduler_policy_value e; + invalid_scheduler_policy_value_ctor_str(&e, "MaxConcurrency"); + _CxxThrowException(&e, &invalid_scheduler_policy_value_exception_type); + } + + this->policy_container->policies[MinConcurrency] = min_concurrency; + this->policy_container->policies[MaxConcurrency] = max_concurrency; +} + +/* ?GetPolicyValue@SchedulerPolicy@Concurrency@@QBEIW4PolicyElementKey@2@@Z */ +/* ?GetPolicyValue@SchedulerPolicy@Concurrency@@QEBAIW4PolicyElementKey@2@@Z */ +DEFINE_THISCALL_WRAPPER(SchedulerPolicy_GetPolicyValue, 8) +unsigned int __thiscall SchedulerPolicy_GetPolicyValue( + const SchedulerPolicy *this, PolicyElementKey policy) +{ + TRACE("(%p %d)\n", this, policy); + + if (policy >= last_policy_id) { + invalid_scheduler_policy_key e; + invalid_scheduler_policy_key_ctor_str(&e, "Invalid policy"); + _CxxThrowException(&e, &invalid_scheduler_policy_key_exception_type); + } + return this->policy_container->policies[policy]; +} + +/* ??0SchedulerPolicy@Concurrency@@QAE@XZ */ +/* ??0SchedulerPolicy@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(SchedulerPolicy_ctor, 4) +SchedulerPolicy* __thiscall SchedulerPolicy_ctor(SchedulerPolicy *this) +{ + TRACE("(%p)\n", this); + + this->policy_container = operator_new(sizeof(*this->policy_container)); + /* TODO: default values can probably be affected by CurrentScheduler */ + this->policy_container->policies[SchedulerKind] = 0; + this->policy_container->policies[MaxConcurrency] = -1; + this->policy_container->policies[MinConcurrency] = 1; + this->policy_container->policies[TargetOversubscriptionFactor] = 1; + this->policy_container->policies[LocalContextCacheSize] = 8; + this->policy_container->policies[ContextStackSize] = 0; + this->policy_container->policies[ContextPriority] = THREAD_PRIORITY_NORMAL; + this->policy_container->policies[SchedulingProtocol] = 0; + this->policy_container->policies[DynamicProgressFeedback] = 1; + return this; +} + +/* ??0SchedulerPolicy@Concurrency@@QAA@IZZ */ +/* ??0SchedulerPolicy@Concurrency@@QEAA@_KZZ */ +/* TODO: don't leak policy_container on exception */ +SchedulerPolicy* WINAPIV SchedulerPolicy_ctor_policies( + SchedulerPolicy *this, size_t n, ...) +{ + unsigned int min_concurrency, max_concurrency; + va_list valist; + size_t i; + + TRACE("(%p %Iu)\n", this, n); + + SchedulerPolicy_ctor(this); + min_concurrency = this->policy_container->policies[MinConcurrency]; + max_concurrency = this->policy_container->policies[MaxConcurrency]; + + va_start(valist, n); + for(i=0; ipolicy_container->policies, rhs->policy_container->policies, + sizeof(this->policy_container->policies)); + return this; +} + +/* ??0SchedulerPolicy@Concurrency@@QAE@ABV01@@Z */ +/* ??0SchedulerPolicy@Concurrency@@QEAA@AEBV01@@Z */ +DEFINE_THISCALL_WRAPPER(SchedulerPolicy_copy_ctor, 8) +SchedulerPolicy* __thiscall SchedulerPolicy_copy_ctor( + SchedulerPolicy *this, const SchedulerPolicy *rhs) +{ + TRACE("(%p %p)\n", this, rhs); + SchedulerPolicy_ctor(this); + return SchedulerPolicy_op_assign(this, rhs); +} + +/* ??1SchedulerPolicy@Concurrency@@QAE@XZ */ +/* ??1SchedulerPolicy@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(SchedulerPolicy_dtor, 4) +void __thiscall SchedulerPolicy_dtor(SchedulerPolicy *this) +{ + TRACE("(%p)\n", this); + operator_delete(this->policy_container); +} + +static void ThreadScheduler_dtor(ThreadScheduler *this) +{ + int i; + struct scheduled_chore *sc, *next; + + if(this->ref != 0) WARN("ref = %ld\n", this->ref); + SchedulerPolicy_dtor(&this->policy); + + for(i=0; ishutdown_count; i++) + SetEvent(this->shutdown_events[i]); + operator_delete(this->shutdown_events); + + this->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&this->cs); + + if (!list_empty(&this->scheduled_chores)) + ERR("scheduled chore list is not empty\n"); + LIST_FOR_EACH_ENTRY_SAFE(sc, next, &this->scheduled_chores, + struct scheduled_chore, entry) + operator_delete(sc); +} + +DEFINE_THISCALL_WRAPPER(ThreadScheduler_Id, 4) +unsigned int __thiscall ThreadScheduler_Id(const ThreadScheduler *this) +{ + TRACE("(%p)\n", this); + return this->id; +} + +DEFINE_THISCALL_WRAPPER(ThreadScheduler_GetNumberOfVirtualProcessors, 4) +unsigned int __thiscall ThreadScheduler_GetNumberOfVirtualProcessors(const ThreadScheduler *this) +{ + TRACE("(%p)\n", this); + return this->virt_proc_no; +} + +DEFINE_THISCALL_WRAPPER(ThreadScheduler_GetPolicy, 8) +SchedulerPolicy* __thiscall ThreadScheduler_GetPolicy( + const ThreadScheduler *this, SchedulerPolicy *ret) +{ + TRACE("(%p %p)\n", this, ret); + return SchedulerPolicy_copy_ctor(ret, &this->policy); +} + +DEFINE_THISCALL_WRAPPER(ThreadScheduler_Reference, 4) +unsigned int __thiscall ThreadScheduler_Reference(ThreadScheduler *this) +{ + TRACE("(%p)\n", this); + return InterlockedIncrement(&this->ref); +} + +DEFINE_THISCALL_WRAPPER(ThreadScheduler_Release, 4) +unsigned int __thiscall ThreadScheduler_Release(ThreadScheduler *this) +{ + unsigned int ret = InterlockedDecrement(&this->ref); + + TRACE("(%p)\n", this); + + if(!ret) { + ThreadScheduler_dtor(this); + operator_delete(this); + } + return ret; +} + +DEFINE_THISCALL_WRAPPER(ThreadScheduler_RegisterShutdownEvent, 8) +void __thiscall ThreadScheduler_RegisterShutdownEvent(ThreadScheduler *this, HANDLE event) +{ + HANDLE *shutdown_events; + int size; + + TRACE("(%p %p)\n", this, event); + + EnterCriticalSection(&this->cs); + + size = this->shutdown_size ? this->shutdown_size * 2 : 1; + shutdown_events = operator_new(size * sizeof(*shutdown_events)); + memcpy(shutdown_events, this->shutdown_events, + this->shutdown_count * sizeof(*shutdown_events)); + operator_delete(this->shutdown_events); + this->shutdown_size = size; + this->shutdown_events = shutdown_events; + this->shutdown_events[this->shutdown_count++] = event; + + LeaveCriticalSection(&this->cs); +} + +DEFINE_THISCALL_WRAPPER(ThreadScheduler_Attach, 4) +void __thiscall ThreadScheduler_Attach(ThreadScheduler *this) +{ + ExternalContextBase *context = (ExternalContextBase*)get_current_context(); + + TRACE("(%p)\n", this); + + if(context->context.vtable != &ExternalContextBase_vtable) { + ERR("unknown context set\n"); + return; + } + + if(context->scheduler.scheduler == &this->scheduler) { + improper_scheduler_attach e; + improper_scheduler_attach_ctor_str(&e, NULL); + _CxxThrowException(&e, &improper_scheduler_attach_exception_type); + } + + if(context->scheduler.scheduler) { + struct scheduler_list *l = operator_new(sizeof(*l)); + *l = context->scheduler; + context->scheduler.next = l; + } + context->scheduler.scheduler = &this->scheduler; + ThreadScheduler_Reference(this); +} + +DEFINE_THISCALL_WRAPPER(ThreadScheduler_CreateScheduleGroup_loc, 8) +/*ScheduleGroup*/void* __thiscall ThreadScheduler_CreateScheduleGroup_loc( + ThreadScheduler *this, /*location*/void *placement) +{ + FIXME("(%p %p) stub\n", this, placement); + return NULL; +} + +DEFINE_THISCALL_WRAPPER(ThreadScheduler_CreateScheduleGroup, 4) +/*ScheduleGroup*/void* __thiscall ThreadScheduler_CreateScheduleGroup(ThreadScheduler *this) +{ + FIXME("(%p) stub\n", this); + return NULL; +} + +typedef struct +{ + void (__cdecl *proc)(void*); + void *data; + ThreadScheduler *scheduler; +} schedule_task_arg; + +void __cdecl CurrentScheduler_Detach(void); + +static void WINAPI schedule_task_proc(PTP_CALLBACK_INSTANCE instance, void *context, PTP_WORK work) +{ + schedule_task_arg arg; + BOOL detach = FALSE; + + arg = *(schedule_task_arg*)context; + operator_delete(context); + + if(&arg.scheduler->scheduler != get_current_scheduler()) { + ThreadScheduler_Attach(arg.scheduler); + detach = TRUE; + } + ThreadScheduler_Release(arg.scheduler); + + arg.proc(arg.data); + + if(detach) + CurrentScheduler_Detach(); +} + +DEFINE_THISCALL_WRAPPER(ThreadScheduler_ScheduleTask_loc, 16) +void __thiscall ThreadScheduler_ScheduleTask_loc(ThreadScheduler *this, + void (__cdecl *proc)(void*), void* data, /*location*/void *placement) +{ + static unsigned int once; + schedule_task_arg *arg; + TP_WORK *work; + + if(!once++) + FIXME("(%p %p %p %p) semi-stub\n", this, proc, data, placement); + else + TRACE("(%p %p %p %p) semi-stub\n", this, proc, data, placement); + + arg = operator_new(sizeof(*arg)); + arg->proc = proc; + arg->data = data; + arg->scheduler = this; + ThreadScheduler_Reference(this); + + work = CreateThreadpoolWork(schedule_task_proc, arg, NULL); + if(!work) { + scheduler_resource_allocation_error e; + + ThreadScheduler_Release(this); + operator_delete(arg); + scheduler_resource_allocation_error_ctor_name(&e, NULL, + HRESULT_FROM_WIN32(GetLastError())); + _CxxThrowException(&e, &scheduler_resource_allocation_error_exception_type); + } + SubmitThreadpoolWork(work); + CloseThreadpoolWork(work); +} + +DEFINE_THISCALL_WRAPPER(ThreadScheduler_ScheduleTask, 12) +void __thiscall ThreadScheduler_ScheduleTask(ThreadScheduler *this, + void (__cdecl *proc)(void*), void* data) +{ + TRACE("(%p %p %p)\n", this, proc, data); + ThreadScheduler_ScheduleTask_loc(this, proc, data, NULL); +} + +DEFINE_THISCALL_WRAPPER(ThreadScheduler_IsAvailableLocation, 8) +bool __thiscall ThreadScheduler_IsAvailableLocation( + const ThreadScheduler *this, const /*location*/void *placement) +{ + FIXME("(%p %p) stub\n", this, placement); + return FALSE; +} + +DEFINE_THISCALL_WRAPPER(ThreadScheduler_vector_dtor, 8) +Scheduler* __thiscall ThreadScheduler_vector_dtor(ThreadScheduler *this, unsigned int flags) +{ + TRACE("(%p %x)\n", this, flags); + if(flags & 2) { + /* we have an array, with the number of elements stored before the first object */ + INT_PTR i, *ptr = (INT_PTR *)this-1; + + for(i=*ptr-1; i>=0; i--) + ThreadScheduler_dtor(this+i); + operator_delete(ptr); + } else { + ThreadScheduler_dtor(this); + if(flags & 1) + operator_delete(this); + } + + return &this->scheduler; +} + +static ThreadScheduler* ThreadScheduler_ctor(ThreadScheduler *this, + const SchedulerPolicy *policy) +{ + SYSTEM_INFO si; + + TRACE("(%p)->()\n", this); + + this->scheduler.vtable = &ThreadScheduler_vtable; + this->ref = 1; + this->id = InterlockedIncrement(&scheduler_id); + SchedulerPolicy_copy_ctor(&this->policy, policy); + + GetSystemInfo(&si); + this->virt_proc_no = SchedulerPolicy_GetPolicyValue(&this->policy, MaxConcurrency); + if(this->virt_proc_no > si.dwNumberOfProcessors) + this->virt_proc_no = si.dwNumberOfProcessors; + + this->shutdown_count = this->shutdown_size = 0; + this->shutdown_events = NULL; + + InitializeCriticalSectionEx(&this->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO); + this->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ThreadScheduler"); + + list_init(&this->scheduled_chores); + return this; +} + +/* ?Create@Scheduler@Concurrency@@SAPAV12@ABVSchedulerPolicy@2@@Z */ +/* ?Create@Scheduler@Concurrency@@SAPEAV12@AEBVSchedulerPolicy@2@@Z */ +Scheduler* __cdecl Scheduler_Create(const SchedulerPolicy *policy) +{ + ThreadScheduler *ret; + + TRACE("(%p)\n", policy); + + ret = operator_new(sizeof(*ret)); + return &ThreadScheduler_ctor(ret, policy)->scheduler; +} + +/* ?ResetDefaultSchedulerPolicy@Scheduler@Concurrency@@SAXXZ */ +void __cdecl Scheduler_ResetDefaultSchedulerPolicy(void) +{ + TRACE("()\n"); + + EnterCriticalSection(&default_scheduler_cs); + if(default_scheduler_policy.policy_container) + SchedulerPolicy_dtor(&default_scheduler_policy); + SchedulerPolicy_ctor(&default_scheduler_policy); + LeaveCriticalSection(&default_scheduler_cs); +} + +/* ?SetDefaultSchedulerPolicy@Scheduler@Concurrency@@SAXABVSchedulerPolicy@2@@Z */ +/* ?SetDefaultSchedulerPolicy@Scheduler@Concurrency@@SAXAEBVSchedulerPolicy@2@@Z */ +void __cdecl Scheduler_SetDefaultSchedulerPolicy(const SchedulerPolicy *policy) +{ + TRACE("(%p)\n", policy); + + EnterCriticalSection(&default_scheduler_cs); + if(!default_scheduler_policy.policy_container) + SchedulerPolicy_copy_ctor(&default_scheduler_policy, policy); + else + SchedulerPolicy_op_assign(&default_scheduler_policy, policy); + LeaveCriticalSection(&default_scheduler_cs); +} + +/* ?Create@CurrentScheduler@Concurrency@@SAXABVSchedulerPolicy@2@@Z */ +/* ?Create@CurrentScheduler@Concurrency@@SAXAEBVSchedulerPolicy@2@@Z */ +void __cdecl CurrentScheduler_Create(const SchedulerPolicy *policy) +{ + Scheduler *scheduler; + + TRACE("(%p)\n", policy); + + scheduler = Scheduler_Create(policy); + call_Scheduler_Attach(scheduler); +} + +/* ?Detach@CurrentScheduler@Concurrency@@SAXXZ */ +void __cdecl CurrentScheduler_Detach(void) +{ + ExternalContextBase *context = (ExternalContextBase*)try_get_current_context(); + + TRACE("()\n"); + + if(!context) { + improper_scheduler_detach e; + improper_scheduler_detach_ctor_str(&e, NULL); + _CxxThrowException(&e, &improper_scheduler_detach_exception_type); + } + + if(context->context.vtable != &ExternalContextBase_vtable) { + ERR("unknown context set\n"); + return; + } + + if(!context->scheduler.next) { + improper_scheduler_detach e; + improper_scheduler_detach_ctor_str(&e, NULL); + _CxxThrowException(&e, &improper_scheduler_detach_exception_type); + } + + call_Scheduler_Release(context->scheduler.scheduler); + if(!context->scheduler.next) { + context->scheduler.scheduler = NULL; + }else { + struct scheduler_list *entry = context->scheduler.next; + context->scheduler.scheduler = entry->scheduler; + context->scheduler.next = entry->next; + operator_delete(entry); + } +} + +static void create_default_scheduler(void) +{ + if(default_scheduler) + return; + + EnterCriticalSection(&default_scheduler_cs); + if(!default_scheduler) { + ThreadScheduler *scheduler; + + if(!default_scheduler_policy.policy_container) + SchedulerPolicy_ctor(&default_scheduler_policy); + + scheduler = operator_new(sizeof(*scheduler)); + ThreadScheduler_ctor(scheduler, &default_scheduler_policy); + default_scheduler = scheduler; + } + LeaveCriticalSection(&default_scheduler_cs); +} + +/* ?Get@CurrentScheduler@Concurrency@@SAPAVScheduler@2@XZ */ +/* ?Get@CurrentScheduler@Concurrency@@SAPEAVScheduler@2@XZ */ +Scheduler* __cdecl CurrentScheduler_Get(void) +{ + TRACE("()\n"); + return get_current_scheduler(); +} + +#if _MSVCR_VER > 100 +/* ?CreateScheduleGroup@CurrentScheduler@Concurrency@@SAPAVScheduleGroup@2@AAVlocation@2@@Z */ +/* ?CreateScheduleGroup@CurrentScheduler@Concurrency@@SAPEAVScheduleGroup@2@AEAVlocation@2@@Z */ +/*ScheduleGroup*/void* __cdecl CurrentScheduler_CreateScheduleGroup_loc(/*location*/void *placement) +{ + TRACE("(%p)\n", placement); + return call_Scheduler_CreateScheduleGroup_loc(get_current_scheduler(), placement); +} +#endif + +/* ?CreateScheduleGroup@CurrentScheduler@Concurrency@@SAPAVScheduleGroup@2@XZ */ +/* ?CreateScheduleGroup@CurrentScheduler@Concurrency@@SAPEAVScheduleGroup@2@XZ */ +/*ScheduleGroup*/void* __cdecl CurrentScheduler_CreateScheduleGroup(void) +{ + TRACE("()\n"); + return call_Scheduler_CreateScheduleGroup(get_current_scheduler()); +} + +/* ?GetNumberOfVirtualProcessors@CurrentScheduler@Concurrency@@SAIXZ */ +unsigned int __cdecl CurrentScheduler_GetNumberOfVirtualProcessors(void) +{ + Scheduler *scheduler = try_get_current_scheduler(); + + TRACE("()\n"); + + if(!scheduler) + return -1; + return call_Scheduler_GetNumberOfVirtualProcessors(scheduler); +} + +/* ?GetPolicy@CurrentScheduler@Concurrency@@SA?AVSchedulerPolicy@2@XZ */ +SchedulerPolicy* __cdecl CurrentScheduler_GetPolicy(SchedulerPolicy *policy) +{ + TRACE("(%p)\n", policy); + return call_Scheduler_GetPolicy(get_current_scheduler(), policy); +} + +/* ?Id@CurrentScheduler@Concurrency@@SAIXZ */ +unsigned int __cdecl CurrentScheduler_Id(void) +{ + Scheduler *scheduler = try_get_current_scheduler(); + + TRACE("()\n"); + + if(!scheduler) + return -1; + return call_Scheduler_Id(scheduler); +} + +#if _MSVCR_VER > 100 +/* ?IsAvailableLocation@CurrentScheduler@Concurrency@@SA_NABVlocation@2@@Z */ +/* ?IsAvailableLocation@CurrentScheduler@Concurrency@@SA_NAEBVlocation@2@@Z */ +bool __cdecl CurrentScheduler_IsAvailableLocation(const /*location*/void *placement) +{ + Scheduler *scheduler = try_get_current_scheduler(); + + TRACE("(%p)\n", placement); + + if(!scheduler) + return FALSE; + return call_Scheduler_IsAvailableLocation(scheduler, placement); +} +#endif + +/* ?RegisterShutdownEvent@CurrentScheduler@Concurrency@@SAXPAX@Z */ +/* ?RegisterShutdownEvent@CurrentScheduler@Concurrency@@SAXPEAX@Z */ +void __cdecl CurrentScheduler_RegisterShutdownEvent(HANDLE event) +{ + TRACE("(%p)\n", event); + call_Scheduler_RegisterShutdownEvent(get_current_scheduler(), event); +} + +#if _MSVCR_VER > 100 +/* ?ScheduleTask@CurrentScheduler@Concurrency@@SAXP6AXPAX@Z0AAVlocation@2@@Z */ +/* ?ScheduleTask@CurrentScheduler@Concurrency@@SAXP6AXPEAX@Z0AEAVlocation@2@@Z */ +void __cdecl CurrentScheduler_ScheduleTask_loc(void (__cdecl *proc)(void*), + void *data, /*location*/void *placement) +{ + TRACE("(%p %p %p)\n", proc, data, placement); + call_Scheduler_ScheduleTask_loc(get_current_scheduler(), proc, data, placement); +} +#endif + +/* ?ScheduleTask@CurrentScheduler@Concurrency@@SAXP6AXPAX@Z0@Z */ +/* ?ScheduleTask@CurrentScheduler@Concurrency@@SAXP6AXPEAX@Z0@Z */ +void __cdecl CurrentScheduler_ScheduleTask(void (__cdecl *proc)(void*), void *data) +{ + TRACE("(%p %p)\n", proc, data); + call_Scheduler_ScheduleTask(get_current_scheduler(), proc, data); +} + +/* ??0_Scheduler@details@Concurrency@@QAE@PAVScheduler@2@@Z */ +/* ??0_Scheduler@details@Concurrency@@QEAA@PEAVScheduler@2@@Z */ +DEFINE_THISCALL_WRAPPER(_Scheduler_ctor_sched, 8) +_Scheduler* __thiscall _Scheduler_ctor_sched(_Scheduler *this, Scheduler *scheduler) +{ + TRACE("(%p %p)\n", this, scheduler); + + this->scheduler = scheduler; + return this; +} + +/* ??_F_Scheduler@details@Concurrency@@QAEXXZ */ +/* ??_F_Scheduler@details@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(_Scheduler_ctor, 4) +_Scheduler* __thiscall _Scheduler_ctor(_Scheduler *this) +{ + return _Scheduler_ctor_sched(this, NULL); +} + +/* ?_GetScheduler@_Scheduler@details@Concurrency@@QAEPAVScheduler@3@XZ */ +/* ?_GetScheduler@_Scheduler@details@Concurrency@@QEAAPEAVScheduler@3@XZ */ +DEFINE_THISCALL_WRAPPER(_Scheduler__GetScheduler, 4) +Scheduler* __thiscall _Scheduler__GetScheduler(_Scheduler *this) +{ + TRACE("(%p)\n", this); + return this->scheduler; +} + +/* ?_Reference@_Scheduler@details@Concurrency@@QAEIXZ */ +/* ?_Reference@_Scheduler@details@Concurrency@@QEAAIXZ */ +DEFINE_THISCALL_WRAPPER(_Scheduler__Reference, 4) +unsigned int __thiscall _Scheduler__Reference(_Scheduler *this) +{ + TRACE("(%p)\n", this); + return call_Scheduler_Reference(this->scheduler); +} + +/* ?_Release@_Scheduler@details@Concurrency@@QAEIXZ */ +/* ?_Release@_Scheduler@details@Concurrency@@QEAAIXZ */ +DEFINE_THISCALL_WRAPPER(_Scheduler__Release, 4) +unsigned int __thiscall _Scheduler__Release(_Scheduler *this) +{ + TRACE("(%p)\n", this); + return call_Scheduler_Release(this->scheduler); +} + +/* ?_Get@_CurrentScheduler@details@Concurrency@@SA?AV_Scheduler@23@XZ */ +_Scheduler* __cdecl _CurrentScheduler__Get(_Scheduler *ret) +{ + TRACE("()\n"); + return _Scheduler_ctor_sched(ret, get_current_scheduler()); +} + +/* ?_GetNumberOfVirtualProcessors@_CurrentScheduler@details@Concurrency@@SAIXZ */ +unsigned int __cdecl _CurrentScheduler__GetNumberOfVirtualProcessors(void) +{ + TRACE("()\n"); + get_current_scheduler(); + return CurrentScheduler_GetNumberOfVirtualProcessors(); +} + +/* ?_Id@_CurrentScheduler@details@Concurrency@@SAIXZ */ +unsigned int __cdecl _CurrentScheduler__Id(void) +{ + TRACE("()\n"); + get_current_scheduler(); + return CurrentScheduler_Id(); +} + +/* ?_ScheduleTask@_CurrentScheduler@details@Concurrency@@SAXP6AXPAX@Z0@Z */ +/* ?_ScheduleTask@_CurrentScheduler@details@Concurrency@@SAXP6AXPEAX@Z0@Z */ +void __cdecl _CurrentScheduler__ScheduleTask(void (__cdecl *proc)(void*), void *data) +{ + TRACE("(%p %p)\n", proc, data); + CurrentScheduler_ScheduleTask(proc, data); +} + +/* ?_Value@_SpinCount@details@Concurrency@@SAIXZ */ +unsigned int __cdecl SpinCount__Value(void) +{ + static unsigned int val = -1; + + TRACE("()\n"); + + if(val == -1) { + SYSTEM_INFO si; + + GetSystemInfo(&si); + val = si.dwNumberOfProcessors>1 ? 4000 : 0; + } + + return val; +} + +/* ??0?$_SpinWait@$00@details@Concurrency@@QAE@P6AXXZ@Z */ +/* ??0?$_SpinWait@$00@details@Concurrency@@QEAA@P6AXXZ@Z */ +DEFINE_THISCALL_WRAPPER(SpinWait_ctor_yield, 8) +SpinWait* __thiscall SpinWait_ctor_yield(SpinWait *this, yield_func yf) +{ + TRACE("(%p %p)\n", this, yf); + + this->state = SPINWAIT_INIT; + this->unknown = 1; + this->yield_func = yf; + return this; +} + +/* ??0?$_SpinWait@$0A@@details@Concurrency@@QAE@P6AXXZ@Z */ +/* ??0?$_SpinWait@$0A@@details@Concurrency@@QEAA@P6AXXZ@Z */ +DEFINE_THISCALL_WRAPPER(SpinWait_ctor, 8) +SpinWait* __thiscall SpinWait_ctor(SpinWait *this, yield_func yf) +{ + TRACE("(%p %p)\n", this, yf); + + this->state = SPINWAIT_INIT; + this->unknown = 0; + this->yield_func = yf; + return this; +} + +/* ??_F?$_SpinWait@$00@details@Concurrency@@QAEXXZ */ +/* ??_F?$_SpinWait@$00@details@Concurrency@@QEAAXXZ */ +/* ??_F?$_SpinWait@$0A@@details@Concurrency@@QAEXXZ */ +/* ??_F?$_SpinWait@$0A@@details@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(SpinWait_dtor, 4) +void __thiscall SpinWait_dtor(SpinWait *this) +{ + TRACE("(%p)\n", this); +} + +/* ?_DoYield@?$_SpinWait@$00@details@Concurrency@@IAEXXZ */ +/* ?_DoYield@?$_SpinWait@$00@details@Concurrency@@IEAAXXZ */ +/* ?_DoYield@?$_SpinWait@$0A@@details@Concurrency@@IAEXXZ */ +/* ?_DoYield@?$_SpinWait@$0A@@details@Concurrency@@IEAAXXZ */ +DEFINE_THISCALL_WRAPPER(SpinWait__DoYield, 4) +void __thiscall SpinWait__DoYield(SpinWait *this) +{ + TRACE("(%p)\n", this); + + if(this->unknown) + this->yield_func(); +} + +/* ?_NumberOfSpins@?$_SpinWait@$00@details@Concurrency@@IAEKXZ */ +/* ?_NumberOfSpins@?$_SpinWait@$00@details@Concurrency@@IEAAKXZ */ +/* ?_NumberOfSpins@?$_SpinWait@$0A@@details@Concurrency@@IAEKXZ */ +/* ?_NumberOfSpins@?$_SpinWait@$0A@@details@Concurrency@@IEAAKXZ */ +DEFINE_THISCALL_WRAPPER(SpinWait__NumberOfSpins, 4) +ULONG __thiscall SpinWait__NumberOfSpins(SpinWait *this) +{ + TRACE("(%p)\n", this); + return 1; +} + +/* ?_SetSpinCount@?$_SpinWait@$00@details@Concurrency@@QAEXI@Z */ +/* ?_SetSpinCount@?$_SpinWait@$00@details@Concurrency@@QEAAXI@Z */ +/* ?_SetSpinCount@?$_SpinWait@$0A@@details@Concurrency@@QAEXI@Z */ +/* ?_SetSpinCount@?$_SpinWait@$0A@@details@Concurrency@@QEAAXI@Z */ +DEFINE_THISCALL_WRAPPER(SpinWait__SetSpinCount, 8) +void __thiscall SpinWait__SetSpinCount(SpinWait *this, unsigned int spin) +{ + TRACE("(%p %d)\n", this, spin); + + this->spin = spin; + this->state = spin ? SPINWAIT_SPIN : SPINWAIT_YIELD; +} + +/* ?_Reset@?$_SpinWait@$00@details@Concurrency@@IAEXXZ */ +/* ?_Reset@?$_SpinWait@$00@details@Concurrency@@IEAAXXZ */ +/* ?_Reset@?$_SpinWait@$0A@@details@Concurrency@@IAEXXZ */ +/* ?_Reset@?$_SpinWait@$0A@@details@Concurrency@@IEAAXXZ */ +DEFINE_THISCALL_WRAPPER(SpinWait__Reset, 4) +void __thiscall SpinWait__Reset(SpinWait *this) +{ + SpinWait__SetSpinCount(this, SpinCount__Value()); +} + +/* ?_ShouldSpinAgain@?$_SpinWait@$00@details@Concurrency@@IAE_NXZ */ +/* ?_ShouldSpinAgain@?$_SpinWait@$00@details@Concurrency@@IEAA_NXZ */ +/* ?_ShouldSpinAgain@?$_SpinWait@$0A@@details@Concurrency@@IAE_NXZ */ +/* ?_ShouldSpinAgain@?$_SpinWait@$0A@@details@Concurrency@@IEAA_NXZ */ +DEFINE_THISCALL_WRAPPER(SpinWait__ShouldSpinAgain, 4) +bool __thiscall SpinWait__ShouldSpinAgain(SpinWait *this) +{ + TRACE("(%p)\n", this); + + this->spin--; + return this->spin > 0; +} + +/* ?_SpinOnce@?$_SpinWait@$00@details@Concurrency@@QAE_NXZ */ +/* ?_SpinOnce@?$_SpinWait@$00@details@Concurrency@@QEAA_NXZ */ +/* ?_SpinOnce@?$_SpinWait@$0A@@details@Concurrency@@QAE_NXZ */ +/* ?_SpinOnce@?$_SpinWait@$0A@@details@Concurrency@@QEAA_NXZ */ +DEFINE_THISCALL_WRAPPER(SpinWait__SpinOnce, 4) +bool __thiscall SpinWait__SpinOnce(SpinWait *this) +{ + switch(this->state) { + case SPINWAIT_INIT: + SpinWait__Reset(this); + /* fall through */ + case SPINWAIT_SPIN: + InterlockedDecrement((LONG*)&this->spin); + if(!this->spin) + this->state = this->unknown ? SPINWAIT_YIELD : SPINWAIT_DONE; + return TRUE; + case SPINWAIT_YIELD: + this->state = SPINWAIT_DONE; + this->yield_func(); + return TRUE; + default: + SpinWait__Reset(this); + return FALSE; + } +} + +#if _MSVCR_VER >= 110 + +/* ??0_StructuredTaskCollection@details@Concurrency@@QAE@PAV_CancellationTokenState@12@@Z */ +/* ??0_StructuredTaskCollection@details@Concurrency@@QEAA@PEAV_CancellationTokenState@12@@Z */ +DEFINE_THISCALL_WRAPPER(_StructuredTaskCollection_ctor, 8) +_StructuredTaskCollection* __thiscall _StructuredTaskCollection_ctor( + _StructuredTaskCollection *this, /*_CancellationTokenState*/void *token) +{ + TRACE("(%p)\n", this); + + if (token) + FIXME("_StructuredTaskCollection with cancellation token not implemented!\n"); + + memset(this, 0, sizeof(*this)); + this->finished = FINISHED_INITIAL; + return this; +} + +#endif /* _MSVCR_VER >= 110 */ + +#if _MSVCR_VER >= 120 + +/* ??1_StructuredTaskCollection@details@Concurrency@@QAA@XZ */ +/* ??1_StructuredTaskCollection@details@Concurrency@@QAE@XZ */ +/* ??1_StructuredTaskCollection@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_StructuredTaskCollection_dtor, 4) +void __thiscall _StructuredTaskCollection_dtor(_StructuredTaskCollection *this) +{ + TRACE("(%p)\n", this); + + if (this->count && !__uncaught_exception()) { + missing_wait e; + missing_wait_ctor_str(&e, "Missing call to _RunAndWait"); + _CxxThrowException(&e, &missing_wait_exception_type); + } +} + +#endif /* _MSVCR_VER >= 120 */ + +static ThreadScheduler *get_thread_scheduler_from_context(Context *context) +{ + Scheduler *scheduler = get_scheduler_from_context(context); + if (scheduler && scheduler->vtable == &ThreadScheduler_vtable) + return (ThreadScheduler*)scheduler; + return NULL; +} + +struct execute_chore_data { + _UnrealizedChore *chore; + _StructuredTaskCollection *task_collection; +}; + +/* ?_Cancel@_StructuredTaskCollection@details@Concurrency@@QAAXXZ */ +/* ?_Cancel@_StructuredTaskCollection@details@Concurrency@@QAEXXZ */ +/* ?_Cancel@_StructuredTaskCollection@details@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(_StructuredTaskCollection__Cancel, 4) +void __thiscall _StructuredTaskCollection__Cancel( + _StructuredTaskCollection *this) +{ + ThreadScheduler *scheduler; + void *prev_exception, *new_exception; + struct scheduled_chore *sc, *next; + LONG removed = 0, finished = 1; + struct beacon *beacon; + + TRACE("(%p)\n", this); + + if (!this->context) + this->context = get_current_context(); + scheduler = get_thread_scheduler_from_context(this->context); + if (!scheduler) + return; + + new_exception = this->exception; + do { + prev_exception = new_exception; + if ((ULONG_PTR)prev_exception & STRUCTURED_TASK_COLLECTION_CANCELLED) + return; + new_exception = (void*)((ULONG_PTR)prev_exception | + STRUCTURED_TASK_COLLECTION_CANCELLED); + } while ((new_exception = InterlockedCompareExchangePointer( + &this->exception, new_exception, prev_exception)) + != prev_exception); + + EnterCriticalSection(&((ExternalContextBase*)this->context)->beacons_cs); + LIST_FOR_EACH_ENTRY(beacon, &((ExternalContextBase*)this->context)->beacons, struct beacon, entry) { + if (beacon->task_collection == this) + InterlockedIncrement(&beacon->cancelling); + } + LeaveCriticalSection(&((ExternalContextBase*)this->context)->beacons_cs); + + EnterCriticalSection(&scheduler->cs); + LIST_FOR_EACH_ENTRY_SAFE(sc, next, &scheduler->scheduled_chores, + struct scheduled_chore, entry) { + if (sc->chore->task_collection != this) + continue; + sc->chore->task_collection = NULL; + list_remove(&sc->entry); + removed++; + operator_delete(sc); + } + LeaveCriticalSection(&scheduler->cs); + if (!removed) + return; + + if (InterlockedCompareExchange(&this->finished, removed, FINISHED_INITIAL) != FINISHED_INITIAL) + finished = InterlockedAdd(&this->finished, removed); + if (!finished) + call_Context_Unblock(this->event); +} + +static LONG CALLBACK execute_chore_except(EXCEPTION_POINTERS *pexc, void *_data) +{ + struct execute_chore_data *data = _data; + void *prev_exception, *new_exception; + exception_ptr *ptr; + + if (pexc->ExceptionRecord->ExceptionCode != CXX_EXCEPTION) + return EXCEPTION_CONTINUE_SEARCH; + + _StructuredTaskCollection__Cancel(data->task_collection); + + ptr = operator_new(sizeof(*ptr)); + __ExceptionPtrCreate(ptr); + exception_ptr_from_record(ptr, pexc->ExceptionRecord); + + new_exception = data->task_collection->exception; + do { + if ((ULONG_PTR)new_exception & ~STRUCTURED_TASK_COLLECTION_STATUS_MASK) { + __ExceptionPtrDestroy(ptr); + operator_delete(ptr); + break; + } + prev_exception = new_exception; + new_exception = (void*)((ULONG_PTR)new_exception | (ULONG_PTR)ptr); + } while ((new_exception = InterlockedCompareExchangePointer( + &data->task_collection->exception, new_exception, + prev_exception)) != prev_exception); + data->task_collection->event = 0; + return EXCEPTION_EXECUTE_HANDLER; +} + +static void CALLBACK execute_chore_finally(BOOL normal, void *data) +{ + ExternalContextBase *ctx = (ExternalContextBase*)try_get_current_context(); + _StructuredTaskCollection *old_collection = data; + + if (ctx && ctx->context.vtable == &ExternalContextBase_vtable) + ctx->task_collection = old_collection; +} + +static void execute_chore(_UnrealizedChore *chore, + _StructuredTaskCollection *task_collection) +{ + ExternalContextBase *ctx = (ExternalContextBase*)try_get_current_context(); + struct execute_chore_data data = { chore, task_collection }; + _StructuredTaskCollection *old_collection; + + TRACE("(%p %p)\n", chore, task_collection); + + if (ctx && ctx->context.vtable == &ExternalContextBase_vtable) + { + old_collection = ctx->task_collection; + ctx->task_collection = task_collection; + } + + __TRY + { + __TRY + { + if (!((ULONG_PTR)task_collection->exception & ~STRUCTURED_TASK_COLLECTION_STATUS_MASK) && + chore->chore_proc) + chore->chore_proc(chore); + } + __EXCEPT_CTX(execute_chore_except, &data) + { + } + __ENDTRY + } + __FINALLY_CTX(execute_chore_finally, old_collection) +} + +static void CALLBACK chore_wrapper_finally(BOOL normal, void *data) +{ + _UnrealizedChore *chore = data; + struct _StructuredTaskCollection *task_collection = chore->task_collection; + LONG finished = 1; + + TRACE("(%u %p)\n", normal, data); + + if (!task_collection) + return; + chore->task_collection = NULL; + + if (InterlockedCompareExchange(&task_collection->finished, 1, FINISHED_INITIAL) != FINISHED_INITIAL) + finished = InterlockedIncrement(&task_collection->finished); + if (!finished) + call_Context_Unblock(task_collection->event); +} + +static void __cdecl chore_wrapper(_UnrealizedChore *chore) +{ + __TRY + { + execute_chore(chore, chore->task_collection); + } + __FINALLY_CTX(chore_wrapper_finally, chore) +} + +static BOOL pick_and_execute_chore(ThreadScheduler *scheduler) +{ + struct list *entry; + struct scheduled_chore *sc; + _UnrealizedChore *chore; + + TRACE("(%p)\n", scheduler); + + if (scheduler->scheduler.vtable != &ThreadScheduler_vtable) + { + ERR("unknown scheduler set\n"); + return FALSE; + } + + EnterCriticalSection(&scheduler->cs); + entry = list_head(&scheduler->scheduled_chores); + if (entry) + list_remove(entry); + LeaveCriticalSection(&scheduler->cs); + if (!entry) + return FALSE; + + sc = LIST_ENTRY(entry, struct scheduled_chore, entry); + chore = sc->chore; + operator_delete(sc); + + chore->chore_wrapper(chore); + return TRUE; +} + +static void __cdecl _StructuredTaskCollection_scheduler_cb(void *data) +{ + pick_and_execute_chore((ThreadScheduler*)get_current_scheduler()); +} + +static bool schedule_chore(_StructuredTaskCollection *this, + _UnrealizedChore *chore, Scheduler **pscheduler) +{ + struct scheduled_chore *sc; + ThreadScheduler *scheduler; + + if (chore->task_collection) { + invalid_multiple_scheduling e; + invalid_multiple_scheduling_ctor_str(&e, "Chore scheduled multiple times"); + _CxxThrowException(&e, &invalid_multiple_scheduling_exception_type); + return FALSE; + } + + if (!this->context) + this->context = get_current_context(); + scheduler = get_thread_scheduler_from_context(this->context); + if (!scheduler) { + ERR("unknown context or scheduler set\n"); + return FALSE; + } + + sc = operator_new(sizeof(*sc)); + sc->chore = chore; + + chore->task_collection = this; + chore->chore_wrapper = chore_wrapper; + InterlockedIncrement(&this->count); + + EnterCriticalSection(&scheduler->cs); + list_add_head(&scheduler->scheduled_chores, &sc->entry); + LeaveCriticalSection(&scheduler->cs); + *pscheduler = &scheduler->scheduler; + return TRUE; +} + +#if _MSVCR_VER >= 110 + +/* ?_Schedule@_StructuredTaskCollection@details@Concurrency@@QAAXPAV_UnrealizedChore@23@PAVlocation@3@@Z */ +/* ?_Schedule@_StructuredTaskCollection@details@Concurrency@@QAEXPAV_UnrealizedChore@23@PAVlocation@3@@Z */ +/* ?_Schedule@_StructuredTaskCollection@details@Concurrency@@QEAAXPEAV_UnrealizedChore@23@PEAVlocation@3@@Z */ +DEFINE_THISCALL_WRAPPER(_StructuredTaskCollection__Schedule_loc, 12) +void __thiscall _StructuredTaskCollection__Schedule_loc( + _StructuredTaskCollection *this, _UnrealizedChore *chore, + /*location*/void *placement) +{ + Scheduler *scheduler; + + TRACE("(%p %p %p)\n", this, chore, placement); + + if (schedule_chore(this, chore, &scheduler)) + { + call_Scheduler_ScheduleTask_loc(scheduler, + _StructuredTaskCollection_scheduler_cb, NULL, placement); + } +} + +#endif /* _MSVCR_VER >= 110 */ + +/* ?_Schedule@_StructuredTaskCollection@details@Concurrency@@QAAXPAV_UnrealizedChore@23@@Z */ +/* ?_Schedule@_StructuredTaskCollection@details@Concurrency@@QAEXPAV_UnrealizedChore@23@@Z */ +/* ?_Schedule@_StructuredTaskCollection@details@Concurrency@@QEAAXPEAV_UnrealizedChore@23@@Z */ +DEFINE_THISCALL_WRAPPER(_StructuredTaskCollection__Schedule, 8) +void __thiscall _StructuredTaskCollection__Schedule( + _StructuredTaskCollection *this, _UnrealizedChore *chore) +{ + Scheduler *scheduler; + + TRACE("(%p %p)\n", this, chore); + + if (schedule_chore(this, chore, &scheduler)) + { + call_Scheduler_ScheduleTask(scheduler, + _StructuredTaskCollection_scheduler_cb, NULL); + } +} + +static void CALLBACK exception_ptr_rethrow_finally(BOOL normal, void *data) +{ + exception_ptr *ep = data; + + TRACE("(%u %p)\n", normal, data); + + __ExceptionPtrDestroy(ep); + operator_delete(ep); +} + +/* ?_RunAndWait@_StructuredTaskCollection@details@Concurrency@@QAA?AW4_TaskCollectionStatus@23@PAV_UnrealizedChore@23@@Z */ +/* ?_RunAndWait@_StructuredTaskCollection@details@Concurrency@@QAG?AW4_TaskCollectionStatus@23@PAV_UnrealizedChore@23@@Z */ +/* ?_RunAndWait@_StructuredTaskCollection@details@Concurrency@@QEAA?AW4_TaskCollectionStatus@23@PEAV_UnrealizedChore@23@@Z */ +_TaskCollectionStatus __stdcall _StructuredTaskCollection__RunAndWait( + _StructuredTaskCollection *this, _UnrealizedChore *chore) +{ + ULONG_PTR exception; + exception_ptr *ep; + LONG count; + + TRACE("(%p %p)\n", this, chore); + + if (chore) { + if (chore->task_collection) { + invalid_multiple_scheduling e; + invalid_multiple_scheduling_ctor_str(&e, "Chore scheduled multiple times"); + _CxxThrowException(&e, &invalid_multiple_scheduling_exception_type); + } + execute_chore(chore, this); + } + + if (this->context) { + ThreadScheduler *scheduler = get_thread_scheduler_from_context(this->context); + if (scheduler) { + while (pick_and_execute_chore(scheduler)) ; + } + } + + this->event = get_current_context(); + InterlockedCompareExchange(&this->finished, 0, FINISHED_INITIAL); + + while (this->count != 0) { + count = this->count; + InterlockedAdd(&this->count, -count); + count = InterlockedAdd(&this->finished, -count); + + if (count < 0) + call_Context_Block(this->event); + } + + exception = (ULONG_PTR)this->exception; + ep = (exception_ptr*)(exception & ~STRUCTURED_TASK_COLLECTION_STATUS_MASK); + if (ep) { + this->exception = 0; + __TRY + { + __ExceptionPtrRethrow(ep); + } + __FINALLY_CTX(exception_ptr_rethrow_finally, ep) + } + if (exception & STRUCTURED_TASK_COLLECTION_CANCELLED) + return TASK_COLLECTION_CANCELLED; + return TASK_COLLECTION_SUCCESS; +} + +/* ?_IsCanceling@_StructuredTaskCollection@details@Concurrency@@QAA_NXZ */ +/* ?_IsCanceling@_StructuredTaskCollection@details@Concurrency@@QAE_NXZ */ +/* ?_IsCanceling@_StructuredTaskCollection@details@Concurrency@@QEAA_NXZ */ +DEFINE_THISCALL_WRAPPER(_StructuredTaskCollection__IsCanceling, 4) +bool __thiscall _StructuredTaskCollection__IsCanceling( + _StructuredTaskCollection *this) +{ + TRACE("(%p)\n", this); + return !!((ULONG_PTR)this->exception & STRUCTURED_TASK_COLLECTION_CANCELLED); +} + +/* ?_CheckTaskCollection@_UnrealizedChore@details@Concurrency@@IAEXXZ */ +/* ?_CheckTaskCollection@_UnrealizedChore@details@Concurrency@@IEAAXXZ */ +DEFINE_THISCALL_WRAPPER(_UnrealizedChore__CheckTaskCollection, 4) +void __thiscall _UnrealizedChore__CheckTaskCollection(_UnrealizedChore *this) +{ + FIXME("() stub\n"); +} + +/* ??0critical_section@Concurrency@@QAE@XZ */ +/* ??0critical_section@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(critical_section_ctor, 4) +critical_section* __thiscall critical_section_ctor(critical_section *this) +{ + TRACE("(%p)\n", this); + + this->unk_active.ctx = NULL; + this->head = this->tail = NULL; + return this; +} + +/* ??1critical_section@Concurrency@@QAE@XZ */ +/* ??1critical_section@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(critical_section_dtor, 4) +void __thiscall critical_section_dtor(critical_section *this) +{ + TRACE("(%p)\n", this); +} + +static void __cdecl spin_wait_yield(void) +{ + Sleep(0); +} + +static inline void spin_wait_for_next_cs(cs_queue *q) +{ + SpinWait sw; + + if(q->next) return; + + SpinWait_ctor(&sw, &spin_wait_yield); + SpinWait__Reset(&sw); + while(!q->next) + SpinWait__SpinOnce(&sw); + SpinWait_dtor(&sw); +} + +static inline void cs_set_head(critical_section *cs, cs_queue *q) +{ + cs->unk_active.ctx = get_current_context(); + cs->unk_active.next = q->next; + cs->head = &cs->unk_active; +} + +static inline void cs_lock(critical_section *cs, cs_queue *q) +{ + cs_queue *last; + + if(cs->unk_active.ctx == get_current_context()) { + improper_lock e; + improper_lock_ctor_str(&e, "Already locked"); + _CxxThrowException(&e, &improper_lock_exception_type); + } + + memset(q, 0, sizeof(*q)); + q->ctx = get_current_context(); + last = InterlockedExchangePointer(&cs->tail, q); + if(last) { + last->next = q; + call_Context_Block(q->ctx); + } + + cs_set_head(cs, q); + if(InterlockedCompareExchangePointer(&cs->tail, &cs->unk_active, q) != q) { + spin_wait_for_next_cs(q); + cs->unk_active.next = q->next; + } +} + +/* ?lock@critical_section@Concurrency@@QAEXXZ */ +/* ?lock@critical_section@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(critical_section_lock, 4) +void __thiscall critical_section_lock(critical_section *this) +{ + cs_queue q; + + TRACE("(%p)\n", this); + cs_lock(this, &q); +} + +/* ?try_lock@critical_section@Concurrency@@QAE_NXZ */ +/* ?try_lock@critical_section@Concurrency@@QEAA_NXZ */ +DEFINE_THISCALL_WRAPPER(critical_section_try_lock, 4) +bool __thiscall critical_section_try_lock(critical_section *this) +{ + cs_queue q; + + TRACE("(%p)\n", this); + + if(this->unk_active.ctx == get_current_context()) + return FALSE; + + memset(&q, 0, sizeof(q)); + if(!InterlockedCompareExchangePointer(&this->tail, &q, NULL)) { + cs_set_head(this, &q); + if(InterlockedCompareExchangePointer(&this->tail, &this->unk_active, &q) != &q) { + spin_wait_for_next_cs(&q); + this->unk_active.next = q.next; + } + return TRUE; + } + return FALSE; +} + +/* ?unlock@critical_section@Concurrency@@QAEXXZ */ +/* ?unlock@critical_section@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(critical_section_unlock, 4) +void __thiscall critical_section_unlock(critical_section *this) +{ + TRACE("(%p)\n", this); + + this->unk_active.ctx = NULL; + this->head = NULL; + if(InterlockedCompareExchangePointer(&this->tail, NULL, &this->unk_active) + == &this->unk_active) return; + spin_wait_for_next_cs(&this->unk_active); + +#if _MSVCR_VER >= 110 + while(1) { + cs_queue *next; + + if(!InterlockedExchange(&this->unk_active.next->free, TRUE)) + break; + + next = this->unk_active.next; + if(InterlockedCompareExchangePointer(&this->tail, NULL, next) == next) { + HeapFree(GetProcessHeap(), 0, next); + return; + } + spin_wait_for_next_cs(next); + + this->unk_active.next = next->next; + HeapFree(GetProcessHeap(), 0, next); + } +#endif + + call_Context_Unblock(this->unk_active.next->ctx); +} + +/* ?native_handle@critical_section@Concurrency@@QAEAAV12@XZ */ +/* ?native_handle@critical_section@Concurrency@@QEAAAEAV12@XZ */ +DEFINE_THISCALL_WRAPPER(critical_section_native_handle, 4) +critical_section* __thiscall critical_section_native_handle(critical_section *this) +{ + TRACE("(%p)\n", this); + return this; +} + +static void set_timeout(FILETIME *ft, unsigned int timeout) +{ + LARGE_INTEGER to; + + GetSystemTimeAsFileTime(ft); + to.QuadPart = ((LONGLONG)ft->dwHighDateTime << 32) + + ft->dwLowDateTime + (LONGLONG)timeout * TICKSPERMSEC; + ft->dwHighDateTime = to.QuadPart >> 32; + ft->dwLowDateTime = to.QuadPart; +} + +struct timeout_unlock +{ + Context *ctx; + BOOL timed_out; +}; + +static void WINAPI timeout_unlock(TP_CALLBACK_INSTANCE *instance, void *ctx, TP_TIMER *timer) +{ + struct timeout_unlock *tu = ctx; + tu->timed_out = TRUE; + call_Context_Unblock(tu->ctx); +} + +/* returns TRUE if wait has timed out */ +static BOOL block_context_for(Context *ctx, unsigned int timeout) +{ + struct timeout_unlock tu = { ctx }; + TP_TIMER *tp_timer; + FILETIME ft; + + if(timeout == COOPERATIVE_TIMEOUT_INFINITE) { + call_Context_Block(ctx); + return FALSE; + } + + tp_timer = CreateThreadpoolTimer(timeout_unlock, &tu, NULL); + if(!tp_timer) { + FIXME("throw exception?\n"); + return TRUE; + } + set_timeout(&ft, timeout); + SetThreadpoolTimer(tp_timer, &ft, 0, 0); + + call_Context_Block(ctx); + + SetThreadpoolTimer(tp_timer, NULL, 0, 0); + WaitForThreadpoolTimerCallbacks(tp_timer, TRUE); + CloseThreadpoolTimer(tp_timer); + return tu.timed_out; +} + +#if _MSVCR_VER >= 110 +/* ?try_lock_for@critical_section@Concurrency@@QAE_NI@Z */ +/* ?try_lock_for@critical_section@Concurrency@@QEAA_NI@Z */ +DEFINE_THISCALL_WRAPPER(critical_section_try_lock_for, 8) +bool __thiscall critical_section_try_lock_for( + critical_section *this, unsigned int timeout) +{ + Context *ctx = get_current_context(); + cs_queue *q, *last; + + TRACE("(%p %d)\n", this, timeout); + + if(this->unk_active.ctx == ctx) { + improper_lock e; + improper_lock_ctor_str(&e, "Already locked"); + _CxxThrowException(&e, &improper_lock_exception_type); + } + + if(!(q = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*q)))) + return critical_section_try_lock(this); + q->ctx = ctx; + + last = InterlockedExchangePointer(&this->tail, q); + if(last) { + last->next = q; + + if(block_context_for(q->ctx, timeout)) + { + if(!InterlockedExchange(&q->free, TRUE)) + return FALSE; + /* Context was unblocked because of timeout and unlock operation */ + call_Context_Block(ctx); + } + } + + cs_set_head(this, q); + if(InterlockedCompareExchangePointer(&this->tail, &this->unk_active, q) != q) { + spin_wait_for_next_cs(q); + this->unk_active.next = q->next; + } + + HeapFree(GetProcessHeap(), 0, q); + return TRUE; +} +#endif + +/* ??0scoped_lock@critical_section@Concurrency@@QAE@AAV12@@Z */ +/* ??0scoped_lock@critical_section@Concurrency@@QEAA@AEAV12@@Z */ +DEFINE_THISCALL_WRAPPER(critical_section_scoped_lock_ctor, 8) +critical_section_scoped_lock* __thiscall critical_section_scoped_lock_ctor( + critical_section_scoped_lock *this, critical_section *cs) +{ + TRACE("(%p %p)\n", this, cs); + this->cs = cs; + cs_lock(this->cs, &this->lock.q); + return this; +} + +/* ??1scoped_lock@critical_section@Concurrency@@QAE@XZ */ +/* ??1scoped_lock@critical_section@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(critical_section_scoped_lock_dtor, 4) +void __thiscall critical_section_scoped_lock_dtor(critical_section_scoped_lock *this) +{ + TRACE("(%p)\n", this); + critical_section_unlock(this->cs); +} + +/* ??0_NonReentrantPPLLock@details@Concurrency@@QAE@XZ */ +/* ??0_NonReentrantPPLLock@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_NonReentrantPPLLock_ctor, 4) +_NonReentrantPPLLock* __thiscall _NonReentrantPPLLock_ctor(_NonReentrantPPLLock *this) +{ + TRACE("(%p)\n", this); + + critical_section_ctor(&this->cs); + return this; +} + +/* ?_Acquire@_NonReentrantPPLLock@details@Concurrency@@QAEXPAX@Z */ +/* ?_Acquire@_NonReentrantPPLLock@details@Concurrency@@QEAAXPEAX@Z */ +DEFINE_THISCALL_WRAPPER(_NonReentrantPPLLock__Acquire, 8) +void __thiscall _NonReentrantPPLLock__Acquire(_NonReentrantPPLLock *this, cs_queue *q) +{ + TRACE("(%p %p)\n", this, q); + cs_lock(&this->cs, q); +} + +/* ?_Release@_NonReentrantPPLLock@details@Concurrency@@QAEXXZ */ +/* ?_Release@_NonReentrantPPLLock@details@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(_NonReentrantPPLLock__Release, 4) +void __thiscall _NonReentrantPPLLock__Release(_NonReentrantPPLLock *this) +{ + TRACE("(%p)\n", this); + critical_section_unlock(&this->cs); +} + +/* ??0_Scoped_lock@_NonReentrantPPLLock@details@Concurrency@@QAE@AAV123@@Z */ +/* ??0_Scoped_lock@_NonReentrantPPLLock@details@Concurrency@@QEAA@AEAV123@@Z */ +DEFINE_THISCALL_WRAPPER(_NonReentrantPPLLock__Scoped_lock_ctor, 8) +_NonReentrantPPLLock__Scoped_lock* __thiscall _NonReentrantPPLLock__Scoped_lock_ctor( + _NonReentrantPPLLock__Scoped_lock *this, _NonReentrantPPLLock *lock) +{ + TRACE("(%p %p)\n", this, lock); + + this->lock = lock; + _NonReentrantPPLLock__Acquire(this->lock, &this->wait.q); + return this; +} + +/* ??1_Scoped_lock@_NonReentrantPPLLock@details@Concurrency@@QAE@XZ */ +/* ??1_Scoped_lock@_NonReentrantPPLLock@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_NonReentrantPPLLock__Scoped_lock_dtor, 4) +void __thiscall _NonReentrantPPLLock__Scoped_lock_dtor(_NonReentrantPPLLock__Scoped_lock *this) +{ + TRACE("(%p)\n", this); + + _NonReentrantPPLLock__Release(this->lock); +} + +/* ??0_ReentrantPPLLock@details@Concurrency@@QAE@XZ */ +/* ??0_ReentrantPPLLock@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_ReentrantPPLLock_ctor, 4) +_ReentrantPPLLock* __thiscall _ReentrantPPLLock_ctor(_ReentrantPPLLock *this) +{ + TRACE("(%p)\n", this); + + critical_section_ctor(&this->cs); + this->count = 0; + this->owner = -1; + return this; +} + +/* ?_Acquire@_ReentrantPPLLock@details@Concurrency@@QAEXPAX@Z */ +/* ?_Acquire@_ReentrantPPLLock@details@Concurrency@@QEAAXPEAX@Z */ +DEFINE_THISCALL_WRAPPER(_ReentrantPPLLock__Acquire, 8) +void __thiscall _ReentrantPPLLock__Acquire(_ReentrantPPLLock *this, cs_queue *q) +{ + TRACE("(%p %p)\n", this, q); + + if(this->owner == GetCurrentThreadId()) { + this->count++; + return; + } + + cs_lock(&this->cs, q); + this->count++; + this->owner = GetCurrentThreadId(); +} + +/* ?_Release@_ReentrantPPLLock@details@Concurrency@@QAEXXZ */ +/* ?_Release@_ReentrantPPLLock@details@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(_ReentrantPPLLock__Release, 4) +void __thiscall _ReentrantPPLLock__Release(_ReentrantPPLLock *this) +{ + TRACE("(%p)\n", this); + + this->count--; + if(this->count) + return; + + this->owner = -1; + critical_section_unlock(&this->cs); +} + +/* ??0_Scoped_lock@_ReentrantPPLLock@details@Concurrency@@QAE@AAV123@@Z */ +/* ??0_Scoped_lock@_ReentrantPPLLock@details@Concurrency@@QEAA@AEAV123@@Z */ +DEFINE_THISCALL_WRAPPER(_ReentrantPPLLock__Scoped_lock_ctor, 8) +_ReentrantPPLLock__Scoped_lock* __thiscall _ReentrantPPLLock__Scoped_lock_ctor( + _ReentrantPPLLock__Scoped_lock *this, _ReentrantPPLLock *lock) +{ + TRACE("(%p %p)\n", this, lock); + + this->lock = lock; + _ReentrantPPLLock__Acquire(this->lock, &this->wait.q); + return this; +} + +/* ??1_Scoped_lock@_ReentrantPPLLock@details@Concurrency@@QAE@XZ */ +/* ??1_Scoped_lock@_ReentrantPPLLock@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_ReentrantPPLLock__Scoped_lock_dtor, 4) +void __thiscall _ReentrantPPLLock__Scoped_lock_dtor(_ReentrantPPLLock__Scoped_lock *this) +{ + TRACE("(%p)\n", this); + + _ReentrantPPLLock__Release(this->lock); +} + +/* ?_GetConcurrency@details@Concurrency@@YAIXZ */ +unsigned int __cdecl _GetConcurrency(void) +{ + static unsigned int val = -1; + + TRACE("()\n"); + + if(val == -1) { + SYSTEM_INFO si; + + GetSystemInfo(&si); + val = si.dwNumberOfProcessors; + } + + return val; +} + +static void evt_add_queue(thread_wait_entry **head, thread_wait_entry *entry) +{ + entry->next = *head; + entry->prev = NULL; + if(*head) (*head)->prev = entry; + *head = entry; +} + +static void evt_remove_queue(thread_wait_entry **head, thread_wait_entry *entry) +{ + if(entry == *head) + *head = entry->next; + else if(entry->prev) + entry->prev->next = entry->next; + if(entry->next) entry->next->prev = entry->prev; +} + +static size_t evt_end_wait(thread_wait *wait, event **events, int count) +{ + size_t i, ret = COOPERATIVE_WAIT_TIMEOUT; + + for(i = 0; i < count; i++) { + critical_section_lock(&events[i]->cs); + if(events[i] == wait->signaled) ret = i; + evt_remove_queue(&events[i]->waiters, &wait->entries[i]); + critical_section_unlock(&events[i]->cs); + } + + return ret; +} + +static inline int evt_transition(void **state, void *from, void *to) +{ + return InterlockedCompareExchangePointer(state, to, from) == from; +} + +static size_t evt_wait(thread_wait *wait, event **events, int count, bool wait_all, unsigned int timeout) +{ + int i; + + wait->signaled = EVT_RUNNING; + wait->pending_waits = wait_all ? count : 1; + for(i = 0; i < count; i++) { + wait->entries[i].wait = wait; + + critical_section_lock(&events[i]->cs); + evt_add_queue(&events[i]->waiters, &wait->entries[i]); + if(events[i]->signaled) { + if(!InterlockedDecrement(&wait->pending_waits)) { + wait->signaled = events[i]; + critical_section_unlock(&events[i]->cs); + + return evt_end_wait(wait, events, i+1); + } + } + critical_section_unlock(&events[i]->cs); + } + + if(!timeout) + return evt_end_wait(wait, events, count); + + if(!evt_transition(&wait->signaled, EVT_RUNNING, EVT_WAITING)) + return evt_end_wait(wait, events, count); + + if(block_context_for(wait->ctx, timeout) && + !evt_transition(&wait->signaled, EVT_WAITING, EVT_RUNNING)) + call_Context_Block(wait->ctx); + + return evt_end_wait(wait, events, count); +} + +/* ??0event@Concurrency@@QAE@XZ */ +/* ??0event@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(event_ctor, 4) +event* __thiscall event_ctor(event *this) +{ + TRACE("(%p)\n", this); + + this->waiters = NULL; + this->signaled = FALSE; + critical_section_ctor(&this->cs); + + return this; +} + +/* ??1event@Concurrency@@QAE@XZ */ +/* ??1event@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(event_dtor, 4) +void __thiscall event_dtor(event *this) +{ + TRACE("(%p)\n", this); + critical_section_dtor(&this->cs); + if(this->waiters) + ERR("there's a wait on destroyed event\n"); +} + +/* ?reset@event@Concurrency@@QAEXXZ */ +/* ?reset@event@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(event_reset, 4) +void __thiscall event_reset(event *this) +{ + thread_wait_entry *entry; + + TRACE("(%p)\n", this); + + critical_section_lock(&this->cs); + if(this->signaled) { + this->signaled = FALSE; + for(entry=this->waiters; entry; entry = entry->next) + InterlockedIncrement(&entry->wait->pending_waits); + } + critical_section_unlock(&this->cs); +} + +/* ?set@event@Concurrency@@QAEXXZ */ +/* ?set@event@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(event_set, 4) +void __thiscall event_set(event *this) +{ + thread_wait_entry *wakeup = NULL; + thread_wait_entry *entry, *next; + + TRACE("(%p)\n", this); + + critical_section_lock(&this->cs); + if(!this->signaled) { + this->signaled = TRUE; + for(entry=this->waiters; entry; entry=next) { + next = entry->next; + if(!InterlockedDecrement(&entry->wait->pending_waits)) { + if(InterlockedExchangePointer(&entry->wait->signaled, this) == EVT_WAITING) { + evt_remove_queue(&this->waiters, entry); + evt_add_queue(&wakeup, entry); + } + } + } + } + critical_section_unlock(&this->cs); + + for(entry=wakeup; entry; entry=next) { + next = entry->next; + entry->next = entry->prev = NULL; + call_Context_Unblock(entry->wait->ctx); + } +} + +/* ?wait@event@Concurrency@@QAEII@Z */ +/* ?wait@event@Concurrency@@QEAA_KI@Z */ +DEFINE_THISCALL_WRAPPER(event_wait, 8) +size_t __thiscall event_wait(event *this, unsigned int timeout) +{ + thread_wait wait; + size_t signaled; + + TRACE("(%p %u)\n", this, timeout); + + critical_section_lock(&this->cs); + signaled = this->signaled; + critical_section_unlock(&this->cs); + + if(!timeout) return signaled ? 0 : COOPERATIVE_WAIT_TIMEOUT; + wait.ctx = get_current_context(); + return signaled ? 0 : evt_wait(&wait, &this, 1, FALSE, timeout); +} + +/* ?wait_for_multiple@event@Concurrency@@SAIPAPAV12@I_NI@Z */ +/* ?wait_for_multiple@event@Concurrency@@SA_KPEAPEAV12@_K_NI@Z */ +int __cdecl event_wait_for_multiple(event **events, size_t count, bool wait_all, unsigned int timeout) +{ + thread_wait *wait; + size_t ret; + + TRACE("(%p %Iu %d %u)\n", events, count, wait_all, timeout); + + if(count == 0) + return 0; + + wait = operator_new(FIELD_OFFSET(thread_wait, entries[count])); + wait->ctx = get_current_context(); + ret = evt_wait(wait, events, count, wait_all, timeout); + operator_delete(wait); + + return ret; +} + +#if _MSVCR_VER >= 110 + +/* ??0_Cancellation_beacon@details@Concurrency@@QAE@XZ */ +/* ??0_Cancellation_beacon@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_Cancellation_beacon_ctor, 4) +_Cancellation_beacon* __thiscall _Cancellation_beacon_ctor(_Cancellation_beacon *this) +{ + ExternalContextBase *ctx = (ExternalContextBase*)get_current_context(); + _StructuredTaskCollection *task_collection = NULL; + struct beacon *beacon; + + TRACE("(%p)\n", this); + + if (ctx->context.vtable == &ExternalContextBase_vtable) { + task_collection = ctx->task_collection; + if (task_collection) + ctx = (ExternalContextBase*)task_collection->context; + } + + if (ctx->context.vtable != &ExternalContextBase_vtable) { + ERR("unknown context\n"); + return NULL; + } + + beacon = malloc(sizeof(*beacon)); + beacon->cancelling = Context_IsCurrentTaskCollectionCanceling(); + beacon->task_collection = task_collection; + + if (task_collection) { + EnterCriticalSection(&ctx->beacons_cs); + list_add_head(&ctx->beacons, &beacon->entry); + LeaveCriticalSection(&ctx->beacons_cs); + } + + this->beacon = beacon; + return this; +} + +/* ??1_Cancellation_beacon@details@Concurrency@@QAE@XZ */ +/* ??1_Cancellation_beacon@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_Cancellation_beacon_dtor, 4) +void __thiscall _Cancellation_beacon_dtor(_Cancellation_beacon *this) +{ + TRACE("(%p)\n", this); + + if (this->beacon->task_collection) { + ExternalContextBase *ctx = (ExternalContextBase*)this->beacon->task_collection->context; + + EnterCriticalSection(&ctx->beacons_cs); + list_remove(&this->beacon->entry); + LeaveCriticalSection(&ctx->beacons_cs); + } + + free(this->beacon); +} + +/* ?_Confirm_cancel@_Cancellation_beacon@details@Concurrency@@QAA_NXZ */ +/* ?_Confirm_cancel@_Cancellation_beacon@details@Concurrency@@QAE_NXZ */ +/* ?_Confirm_cancel@_Cancellation_beacon@details@Concurrency@@QEAA_NXZ */ +DEFINE_THISCALL_WRAPPER(_Cancellation_beacon__Confirm_cancel, 4) +bool __thiscall _Cancellation_beacon__Confirm_cancel(_Cancellation_beacon *this) +{ + bool ret; + + TRACE("(%p)\n", this); + + ret = Context_IsCurrentTaskCollectionCanceling(); + if (!ret) + InterlockedDecrement(&this->beacon->cancelling); + return ret; +} + +/* ??0_Condition_variable@details@Concurrency@@QAE@XZ */ +/* ??0_Condition_variable@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_Condition_variable_ctor, 4) +_Condition_variable* __thiscall _Condition_variable_ctor(_Condition_variable *this) +{ + TRACE("(%p)\n", this); + + this->queue = NULL; + critical_section_ctor(&this->lock); + return this; +} + +/* ??1_Condition_variable@details@Concurrency@@QAE@XZ */ +/* ??1_Condition_variable@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_Condition_variable_dtor, 4) +void __thiscall _Condition_variable_dtor(_Condition_variable *this) +{ + TRACE("(%p)\n", this); + + while(this->queue) { + cv_queue *next = this->queue->next; + if(!this->queue->expired) + ERR("there's an active wait\n"); + operator_delete(this->queue); + this->queue = next; + } + critical_section_dtor(&this->lock); +} + +/* ?wait@_Condition_variable@details@Concurrency@@QAEXAAVcritical_section@3@@Z */ +/* ?wait@_Condition_variable@details@Concurrency@@QEAAXAEAVcritical_section@3@@Z */ +DEFINE_THISCALL_WRAPPER(_Condition_variable_wait, 8) +void __thiscall _Condition_variable_wait(_Condition_variable *this, critical_section *cs) +{ + cv_queue q; + + TRACE("(%p, %p)\n", this, cs); + + q.ctx = get_current_context(); + q.expired = FALSE; + critical_section_lock(&this->lock); + q.next = this->queue; + this->queue = &q; + critical_section_unlock(&this->lock); + + critical_section_unlock(cs); + call_Context_Block(q.ctx); + critical_section_lock(cs); +} + +/* ?wait_for@_Condition_variable@details@Concurrency@@QAE_NAAVcritical_section@3@I@Z */ +/* ?wait_for@_Condition_variable@details@Concurrency@@QEAA_NAEAVcritical_section@3@I@Z */ +DEFINE_THISCALL_WRAPPER(_Condition_variable_wait_for, 12) +bool __thiscall _Condition_variable_wait_for(_Condition_variable *this, + critical_section *cs, unsigned int timeout) +{ + cv_queue *q; + + TRACE("(%p %p %d)\n", this, cs, timeout); + + q = operator_new(sizeof(cv_queue)); + q->ctx = get_current_context(); + q->expired = FALSE; + critical_section_lock(&this->lock); + q->next = this->queue; + this->queue = q; + critical_section_unlock(&this->lock); + + critical_section_unlock(cs); + + if(block_context_for(q->ctx, timeout)) { + if(!InterlockedExchange(&q->expired, TRUE)) { + critical_section_lock(cs); + return FALSE; + } + call_Context_Block(q->ctx); + } + + operator_delete(q); + critical_section_lock(cs); + return TRUE; +} + +/* ?notify_one@_Condition_variable@details@Concurrency@@QAEXXZ */ +/* ?notify_one@_Condition_variable@details@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(_Condition_variable_notify_one, 4) +void __thiscall _Condition_variable_notify_one(_Condition_variable *this) +{ + cv_queue *node; + + TRACE("(%p)\n", this); + + if(!this->queue) + return; + + while(1) { + critical_section_lock(&this->lock); + node = this->queue; + if(!node) { + critical_section_unlock(&this->lock); + return; + } + this->queue = node->next; + critical_section_unlock(&this->lock); + + node->next = CV_WAKE; + if(!InterlockedExchange(&node->expired, TRUE)) { + call_Context_Unblock(node->ctx); + return; + } else { + operator_delete(node); + } + } +} + +/* ?notify_all@_Condition_variable@details@Concurrency@@QAEXXZ */ +/* ?notify_all@_Condition_variable@details@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(_Condition_variable_notify_all, 4) +void __thiscall _Condition_variable_notify_all(_Condition_variable *this) +{ + cv_queue *ptr; + + TRACE("(%p)\n", this); + + if(!this->queue) + return; + + critical_section_lock(&this->lock); + ptr = this->queue; + this->queue = NULL; + critical_section_unlock(&this->lock); + + while(ptr) { + cv_queue *next = ptr->next; + + ptr->next = CV_WAKE; + if(!InterlockedExchange(&ptr->expired, TRUE)) + call_Context_Unblock(ptr->ctx); + else + operator_delete(ptr); + ptr = next; + } +} +#endif + +/* ??0reader_writer_lock@Concurrency@@QAE@XZ */ +/* ??0reader_writer_lock@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_ctor, 4) +reader_writer_lock* __thiscall reader_writer_lock_ctor(reader_writer_lock *this) +{ + TRACE("(%p)\n", this); + + memset(this, 0, sizeof(*this)); + return this; +} + +/* ??1reader_writer_lock@Concurrency@@QAE@XZ */ +/* ??1reader_writer_lock@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_dtor, 4) +void __thiscall reader_writer_lock_dtor(reader_writer_lock *this) +{ + TRACE("(%p)\n", this); + + if (this->thread_id != 0 || this->count) + WARN("destroying locked reader_writer_lock\n"); +} + +static inline void spin_wait_for_next_rwl(rwl_queue *q) +{ + SpinWait sw; + + if(q->next) return; + + SpinWait_ctor(&sw, &spin_wait_yield); + SpinWait__Reset(&sw); + while(!q->next) + SpinWait__SpinOnce(&sw); + SpinWait_dtor(&sw); +} + +/* ?lock@reader_writer_lock@Concurrency@@QAEXXZ */ +/* ?lock@reader_writer_lock@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_lock, 4) +void __thiscall reader_writer_lock_lock(reader_writer_lock *this) +{ + rwl_queue q = { NULL, get_current_context() }, *last; + + TRACE("(%p)\n", this); + + if (this->thread_id == GetCurrentThreadId()) { + improper_lock e; + improper_lock_ctor_str(&e, "Already locked"); + _CxxThrowException(&e, &improper_lock_exception_type); + } + + last = InterlockedExchangePointer((void**)&this->writer_tail, &q); + if (last) { + last->next = &q; + call_Context_Block(q.ctx); + } else { + this->writer_head = &q; + if (InterlockedOr(&this->count, WRITER_WAITING)) + call_Context_Block(q.ctx); + } + + this->thread_id = GetCurrentThreadId(); + this->writer_head = &this->active; + this->active.next = NULL; + if (InterlockedCompareExchangePointer((void**)&this->writer_tail, &this->active, &q) != &q) { + spin_wait_for_next_rwl(&q); + this->active.next = q.next; + } +} + +/* ?lock_read@reader_writer_lock@Concurrency@@QAEXXZ */ +/* ?lock_read@reader_writer_lock@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_lock_read, 4) +void __thiscall reader_writer_lock_lock_read(reader_writer_lock *this) +{ + rwl_queue q = { NULL, get_current_context() }; + + TRACE("(%p)\n", this); + + if (this->thread_id == GetCurrentThreadId()) { + improper_lock e; + improper_lock_ctor_str(&e, "Already locked as writer"); + _CxxThrowException(&e, &improper_lock_exception_type); + } + + do { + q.next = this->reader_head; + } while(InterlockedCompareExchangePointer((void**)&this->reader_head, &q, q.next) != q.next); + + if (!q.next) { + rwl_queue *head; + LONG count; + + while (!((count = this->count) & WRITER_WAITING)) + if (InterlockedCompareExchange(&this->count, count+1, count) == count) break; + + if (count & WRITER_WAITING) + call_Context_Block(q.ctx); + + head = InterlockedExchangePointer((void**)&this->reader_head, NULL); + while(head && head != &q) { + rwl_queue *next = head->next; + InterlockedIncrement(&this->count); + call_Context_Unblock(head->ctx); + head = next; + } + } else { + call_Context_Block(q.ctx); + } +} + +/* ?try_lock@reader_writer_lock@Concurrency@@QAE_NXZ */ +/* ?try_lock@reader_writer_lock@Concurrency@@QEAA_NXZ */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_try_lock, 4) +bool __thiscall reader_writer_lock_try_lock(reader_writer_lock *this) +{ + rwl_queue q = { NULL }; + + TRACE("(%p)\n", this); + + if (this->thread_id == GetCurrentThreadId()) + return FALSE; + + if (InterlockedCompareExchangePointer((void**)&this->writer_tail, &q, NULL)) + return FALSE; + this->writer_head = &q; + if (!InterlockedCompareExchange(&this->count, WRITER_WAITING, 0)) { + this->thread_id = GetCurrentThreadId(); + this->writer_head = &this->active; + this->active.next = NULL; + if (InterlockedCompareExchangePointer((void**)&this->writer_tail, &this->active, &q) != &q) { + spin_wait_for_next_rwl(&q); + this->active.next = q.next; + } + return TRUE; + } + + if (InterlockedCompareExchangePointer((void**)&this->writer_tail, NULL, &q) == &q) + return FALSE; + spin_wait_for_next_rwl(&q); + this->writer_head = q.next; + if (!InterlockedOr(&this->count, WRITER_WAITING)) { + this->thread_id = GetCurrentThreadId(); + this->writer_head = &this->active; + this->active.next = q.next; + return TRUE; + } + return FALSE; +} + +/* ?try_lock_read@reader_writer_lock@Concurrency@@QAE_NXZ */ +/* ?try_lock_read@reader_writer_lock@Concurrency@@QEAA_NXZ */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_try_lock_read, 4) +bool __thiscall reader_writer_lock_try_lock_read(reader_writer_lock *this) +{ + LONG count; + + TRACE("(%p)\n", this); + + while (!((count = this->count) & WRITER_WAITING)) + if (InterlockedCompareExchange(&this->count, count+1, count) == count) return TRUE; + return FALSE; +} + +/* ?unlock@reader_writer_lock@Concurrency@@QAEXXZ */ +/* ?unlock@reader_writer_lock@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_unlock, 4) +void __thiscall reader_writer_lock_unlock(reader_writer_lock *this) +{ + LONG count; + rwl_queue *head, *next; + + TRACE("(%p)\n", this); + + if ((count = this->count) & ~WRITER_WAITING) { + count = InterlockedDecrement(&this->count); + if (count != WRITER_WAITING) + return; + call_Context_Unblock(this->writer_head->ctx); + return; + } + + this->thread_id = 0; + next = this->writer_head->next; + if (next) { + call_Context_Unblock(next->ctx); + return; + } + InterlockedAnd(&this->count, ~WRITER_WAITING); + head = InterlockedExchangePointer((void**)&this->reader_head, NULL); + while (head) { + next = head->next; + InterlockedIncrement(&this->count); + call_Context_Unblock(head->ctx); + head = next; + } + + if (InterlockedCompareExchangePointer((void**)&this->writer_tail, NULL, this->writer_head) == this->writer_head) + return; + InterlockedOr(&this->count, WRITER_WAITING); +} + +/* ??0scoped_lock@reader_writer_lock@Concurrency@@QAE@AAV12@@Z */ +/* ??0scoped_lock@reader_writer_lock@Concurrency@@QEAA@AEAV12@@Z */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_scoped_lock_ctor, 8) +reader_writer_lock_scoped_lock* __thiscall reader_writer_lock_scoped_lock_ctor( + reader_writer_lock_scoped_lock *this, reader_writer_lock *lock) +{ + TRACE("(%p %p)\n", this, lock); + + this->lock = lock; + reader_writer_lock_lock(lock); + return this; +} + +/* ??1scoped_lock@reader_writer_lock@Concurrency@@QAE@XZ */ +/* ??1scoped_lock@reader_writer_lock@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_scoped_lock_dtor, 4) +void __thiscall reader_writer_lock_scoped_lock_dtor(reader_writer_lock_scoped_lock *this) +{ + TRACE("(%p)\n", this); + reader_writer_lock_unlock(this->lock); +} + +/* ??0scoped_lock_read@reader_writer_lock@Concurrency@@QAE@AAV12@@Z */ +/* ??0scoped_lock_read@reader_writer_lock@Concurrency@@QEAA@AEAV12@@Z */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_scoped_lock_read_ctor, 8) +reader_writer_lock_scoped_lock* __thiscall reader_writer_lock_scoped_lock_read_ctor( + reader_writer_lock_scoped_lock *this, reader_writer_lock *lock) +{ + TRACE("(%p %p)\n", this, lock); + + this->lock = lock; + reader_writer_lock_lock_read(lock); + return this; +} + +/* ??1scoped_lock_read@reader_writer_lock@Concurrency@@QAE@XZ */ +/* ??1scoped_lock_read@reader_writer_lock@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_scoped_lock_read_dtor, 4) +void __thiscall reader_writer_lock_scoped_lock_read_dtor(reader_writer_lock_scoped_lock *this) +{ + TRACE("(%p)\n", this); + reader_writer_lock_unlock(this->lock); +} + +/* ??0_ReentrantBlockingLock@details@Concurrency@@QAE@XZ */ +/* ??0_ReentrantBlockingLock@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_ReentrantBlockingLock_ctor, 4) +_ReentrantBlockingLock* __thiscall _ReentrantBlockingLock_ctor(_ReentrantBlockingLock *this) +{ + TRACE("(%p)\n", this); + + InitializeCriticalSectionEx(&this->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO); + this->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": _ReentrantBlockingLock"); + return this; +} + +/* ??1_ReentrantBlockingLock@details@Concurrency@@QAE@XZ */ +/* ??1_ReentrantBlockingLock@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_ReentrantBlockingLock_dtor, 4) +void __thiscall _ReentrantBlockingLock_dtor(_ReentrantBlockingLock *this) +{ + TRACE("(%p)\n", this); + + this->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&this->cs); +} + +/* ?_Acquire@_ReentrantBlockingLock@details@Concurrency@@QAEXXZ */ +/* ?_Acquire@_ReentrantBlockingLock@details@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(_ReentrantBlockingLock__Acquire, 4) +void __thiscall _ReentrantBlockingLock__Acquire(_ReentrantBlockingLock *this) +{ + TRACE("(%p)\n", this); + EnterCriticalSection(&this->cs); +} + +/* ?_Release@_ReentrantBlockingLock@details@Concurrency@@QAEXXZ */ +/* ?_Release@_ReentrantBlockingLock@details@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(_ReentrantBlockingLock__Release, 4) +void __thiscall _ReentrantBlockingLock__Release(_ReentrantBlockingLock *this) +{ + TRACE("(%p)\n", this); + LeaveCriticalSection(&this->cs); +} + +/* ?_TryAcquire@_ReentrantBlockingLock@details@Concurrency@@QAE_NXZ */ +/* ?_TryAcquire@_ReentrantBlockingLock@details@Concurrency@@QEAA_NXZ */ +DEFINE_THISCALL_WRAPPER(_ReentrantBlockingLock__TryAcquire, 4) +bool __thiscall _ReentrantBlockingLock__TryAcquire(_ReentrantBlockingLock *this) +{ + TRACE("(%p)\n", this); + return TryEnterCriticalSection(&this->cs); +} + +/* ??0_ReaderWriterLock@details@Concurrency@@QAA@XZ */ +/* ??0_ReaderWriterLock@details@Concurrency@@QAE@XZ */ +/* ??0_ReaderWriterLock@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_ReaderWriterLock_ctor, 4) +_ReaderWriterLock* __thiscall _ReaderWriterLock_ctor(_ReaderWriterLock *this) +{ + TRACE("(%p)\n", this); + + this->state = 0; + this->count = 0; + return this; +} + +/* ?wait@Concurrency@@YAXI@Z */ +void __cdecl Concurrency_wait(unsigned int time) +{ + TRACE("(%d)\n", time); + block_context_for(get_current_context(), time); +} + +#if _MSVCR_VER>=110 +/* ?_Trace_agents@Concurrency@@YAXW4Agents_EventType@1@_JZZ */ +void WINAPIV _Trace_agents(/*enum Concurrency::Agents_EventType*/int type, __int64 id, ...) +{ + FIXME("(%d %#I64x)\n", type, id); +} +#endif + +/* ?_Trace_ppl_function@Concurrency@@YAXABU_GUID@@EW4ConcRT_EventType@1@@Z */ +/* ?_Trace_ppl_function@Concurrency@@YAXAEBU_GUID@@EW4ConcRT_EventType@1@@Z */ +void __cdecl _Trace_ppl_function(const GUID *guid, unsigned char level, enum ConcRT_EventType type) +{ + FIXME("(%s %u %i) stub\n", debugstr_guid(guid), level, type); +} + +/* ??0_Timer@details@Concurrency@@IAE@I_N@Z */ +/* ??0_Timer@details@Concurrency@@IEAA@I_N@Z */ +DEFINE_THISCALL_WRAPPER(_Timer_ctor, 12) +_Timer* __thiscall _Timer_ctor(_Timer *this, unsigned int elapse, bool repeat) +{ + TRACE("(%p %u %x)\n", this, elapse, repeat); + + this->vtable = &_Timer_vtable; + this->timer = NULL; + this->elapse = elapse; + this->repeat = repeat; + return this; +} + +static void WINAPI timer_callback(TP_CALLBACK_INSTANCE *instance, void *ctx, TP_TIMER *timer) +{ + _Timer *this = ctx; + TRACE("calling _Timer(%p) callback\n", this); + call__Timer_callback(this); +} + +/* ?_Start@_Timer@details@Concurrency@@IAEXXZ */ +/* ?_Start@_Timer@details@Concurrency@@IEAAXXZ */ +DEFINE_THISCALL_WRAPPER(_Timer__Start, 4) +void __thiscall _Timer__Start(_Timer *this) +{ + LONGLONG ll; + FILETIME ft; + + TRACE("(%p)\n", this); + + this->timer = CreateThreadpoolTimer(timer_callback, this, NULL); + if (!this->timer) + { + FIXME("throw exception?\n"); + return; + } + + ll = -(LONGLONG)this->elapse * TICKSPERMSEC; + ft.dwLowDateTime = ll & 0xffffffff; + ft.dwHighDateTime = ll >> 32; + SetThreadpoolTimer(this->timer, &ft, this->repeat ? this->elapse : 0, 0); +} + +/* ?_Stop@_Timer@details@Concurrency@@IAEXXZ */ +/* ?_Stop@_Timer@details@Concurrency@@IEAAXXZ */ +DEFINE_THISCALL_WRAPPER(_Timer__Stop, 4) +void __thiscall _Timer__Stop(_Timer *this) +{ + TRACE("(%p)\n", this); + + SetThreadpoolTimer(this->timer, NULL, 0, 0); + WaitForThreadpoolTimerCallbacks(this->timer, TRUE); + CloseThreadpoolTimer(this->timer); + this->timer = NULL; +} + +/* ??1_Timer@details@Concurrency@@MAE@XZ */ +/* ??1_Timer@details@Concurrency@@MEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_Timer_dtor, 4) +void __thiscall _Timer_dtor(_Timer *this) +{ + TRACE("(%p)\n", this); + + if (this->timer) + _Timer__Stop(this); +} + +DEFINE_THISCALL_WRAPPER(_Timer_vector_dtor, 8) +_Timer* __thiscall _Timer_vector_dtor(_Timer *this, unsigned int flags) +{ + TRACE("(%p %x)\n", this, flags); + if (flags & 2) { + /* we have an array, with the number of elements stored before the first object */ + INT_PTR i, *ptr = (INT_PTR *)this-1; + + for (i=*ptr-1; i>=0; i--) + _Timer_dtor(this+i); + operator_delete(ptr); + } else { + _Timer_dtor(this); + if (flags & 1) + operator_delete(this); + } + + return this; +} + +#ifdef __ASM_USE_THISCALL_WRAPPER + +#define DEFINE_VTBL_WRAPPER(off) \ + __ASM_GLOBAL_FUNC(vtbl_wrapper_ ## off, \ + "popl %eax\n\t" \ + "popl %ecx\n\t" \ + "pushl %eax\n\t" \ + "movl 0(%ecx), %eax\n\t" \ + "jmp *" #off "(%eax)\n\t") + +DEFINE_VTBL_WRAPPER(0); +DEFINE_VTBL_WRAPPER(4); +DEFINE_VTBL_WRAPPER(8); +DEFINE_VTBL_WRAPPER(12); +DEFINE_VTBL_WRAPPER(16); +DEFINE_VTBL_WRAPPER(20); +DEFINE_VTBL_WRAPPER(24); +DEFINE_VTBL_WRAPPER(28); +DEFINE_VTBL_WRAPPER(32); +DEFINE_VTBL_WRAPPER(36); +DEFINE_VTBL_WRAPPER(40); +DEFINE_VTBL_WRAPPER(44); +DEFINE_VTBL_WRAPPER(48); + +#endif + +DEFINE_RTTI_DATA0(Context, 0, ".?AVContext@Concurrency@@") +DEFINE_RTTI_DATA1(ContextBase, 0, &Context_rtti_base_descriptor, ".?AVContextBase@details@Concurrency@@") +DEFINE_RTTI_DATA2(ExternalContextBase, 0, &ContextBase_rtti_base_descriptor, + &Context_rtti_base_descriptor, ".?AVExternalContextBase@details@Concurrency@@") +DEFINE_RTTI_DATA0(Scheduler, 0, ".?AVScheduler@Concurrency@@") +DEFINE_RTTI_DATA1(SchedulerBase, 0, &Scheduler_rtti_base_descriptor, ".?AVSchedulerBase@details@Concurrency@@") +DEFINE_RTTI_DATA2(ThreadScheduler, 0, &SchedulerBase_rtti_base_descriptor, + &Scheduler_rtti_base_descriptor, ".?AVThreadScheduler@details@Concurrency@@") +DEFINE_RTTI_DATA0(_Timer, 0, ".?AV_Timer@details@Concurrency@@"); + +__ASM_BLOCK_BEGIN(concurrency_vtables) + __ASM_VTABLE(ExternalContextBase, + VTABLE_ADD_FUNC(ExternalContextBase_GetId) + VTABLE_ADD_FUNC(ExternalContextBase_GetVirtualProcessorId) + VTABLE_ADD_FUNC(ExternalContextBase_GetScheduleGroupId) + VTABLE_ADD_FUNC(ExternalContextBase_Unblock) + VTABLE_ADD_FUNC(ExternalContextBase_IsSynchronouslyBlocked) + VTABLE_ADD_FUNC(ExternalContextBase_vector_dtor) + VTABLE_ADD_FUNC(ExternalContextBase_Block) + VTABLE_ADD_FUNC(ExternalContextBase_Yield) + VTABLE_ADD_FUNC(ExternalContextBase_SpinYield) + VTABLE_ADD_FUNC(ExternalContextBase_Oversubscribe) + VTABLE_ADD_FUNC(ExternalContextBase_Alloc) + VTABLE_ADD_FUNC(ExternalContextBase_Free) + VTABLE_ADD_FUNC(ExternalContextBase_EnterCriticalRegionHelper) + VTABLE_ADD_FUNC(ExternalContextBase_EnterHyperCriticalRegionHelper) + VTABLE_ADD_FUNC(ExternalContextBase_ExitCriticalRegionHelper) + VTABLE_ADD_FUNC(ExternalContextBase_ExitHyperCriticalRegionHelper) + VTABLE_ADD_FUNC(ExternalContextBase_GetCriticalRegionType) + VTABLE_ADD_FUNC(ExternalContextBase_GetContextKind)); + __ASM_VTABLE(ThreadScheduler, + VTABLE_ADD_FUNC(ThreadScheduler_vector_dtor) + VTABLE_ADD_FUNC(ThreadScheduler_Id) + VTABLE_ADD_FUNC(ThreadScheduler_GetNumberOfVirtualProcessors) + VTABLE_ADD_FUNC(ThreadScheduler_GetPolicy) + VTABLE_ADD_FUNC(ThreadScheduler_Reference) + VTABLE_ADD_FUNC(ThreadScheduler_Release) + VTABLE_ADD_FUNC(ThreadScheduler_RegisterShutdownEvent) + VTABLE_ADD_FUNC(ThreadScheduler_Attach) +#if _MSVCR_VER > 100 + VTABLE_ADD_FUNC(ThreadScheduler_CreateScheduleGroup_loc) +#endif + VTABLE_ADD_FUNC(ThreadScheduler_CreateScheduleGroup) +#if _MSVCR_VER > 100 + VTABLE_ADD_FUNC(ThreadScheduler_ScheduleTask_loc) +#endif + VTABLE_ADD_FUNC(ThreadScheduler_ScheduleTask) +#if _MSVCR_VER > 100 + VTABLE_ADD_FUNC(ThreadScheduler_IsAvailableLocation) +#endif + ); + __ASM_VTABLE(_Timer, + VTABLE_ADD_FUNC(_Timer_vector_dtor)); +__ASM_BLOCK_END + +void msvcrt_init_concurrency(void *base) +{ +#ifdef RTTI_USE_RVA + init_cexception_rtti(base); + init_improper_lock_rtti(base); + init_improper_scheduler_attach_rtti(base); + init_improper_scheduler_detach_rtti(base); + init_invalid_multiple_scheduling_rtti(base); + init_invalid_scheduler_policy_key_rtti(base); + init_invalid_scheduler_policy_thread_specification_rtti(base); + init_invalid_scheduler_policy_value_rtti(base); + init_missing_wait_rtti(base); + init_scheduler_resource_allocation_error_rtti(base); + init_Context_rtti(base); + init_ContextBase_rtti(base); + init_ExternalContextBase_rtti(base); + init_Scheduler_rtti(base); + init_SchedulerBase_rtti(base); + init_ThreadScheduler_rtti(base); + init__Timer_rtti(base); + + init_cexception_cxx_type_info(base); + init_improper_lock_cxx(base); + init_improper_scheduler_attach_cxx(base); + init_improper_scheduler_detach_cxx(base); + init_invalid_multiple_scheduling_cxx(base); + init_invalid_scheduler_policy_key_cxx(base); + init_invalid_scheduler_policy_thread_specification_cxx(base); + init_invalid_scheduler_policy_value_cxx(base); +#if _MSVCR_VER >= 120 + init_missing_wait_cxx(base); +#endif + init_scheduler_resource_allocation_error_cxx(base); +#endif +} + +void msvcrt_free_concurrency(void) +{ + if (context_tls_index != TLS_OUT_OF_INDEXES) + TlsFree(context_tls_index); + if(default_scheduler_policy.policy_container) + SchedulerPolicy_dtor(&default_scheduler_policy); + if(default_scheduler) { + ThreadScheduler_dtor(default_scheduler); + operator_delete(default_scheduler); + } +} + +void msvcrt_free_scheduler_thread(void) +{ + Context *context = try_get_current_context(); + if (!context) return; + call_Context_dtor(context, 1); +} + +#endif /* _MSVCR_VER >= 100 */ diff --git a/dll/win32/msvcrt/console.c b/dll/win32/msvcrt/console.c new file mode 100644 index 00000000000..2c6b59d36c6 --- /dev/null +++ b/dll/win32/msvcrt/console.c @@ -0,0 +1,649 @@ +/* + * msvcrt.dll console functions + * + * Copyright 2000 Jon Griffiths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + * + * Note: init and free don't need MT locking since they are called at DLL + * (de)attachment time, which is synchronised for us + */ + +#include "msvcrt.h" +#include "winnls.h" +#include "wincon.h" +#include "mtdll.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +/* MT */ +#define LOCK_CONSOLE _lock(_CONIO_LOCK) +#define UNLOCK_CONSOLE _unlock(_CONIO_LOCK) + +static HANDLE MSVCRT_console_in; +static HANDLE MSVCRT_console_out; +static int __MSVCRT_console_buffer = EOF; +static wchar_t __MSVCRT_console_buffer_w = WEOF; + +/* INTERNAL: Initialise console handles, _CONIO_LOCK must be held */ +static HANDLE msvcrt_input_console(void) +{ + if (!MSVCRT_console_in) + { + MSVCRT_console_in = CreateFileA("CONIN$", GENERIC_WRITE|GENERIC_READ, + FILE_SHARE_WRITE|FILE_SHARE_READ, + NULL, OPEN_EXISTING, 0, NULL); + if (MSVCRT_console_in == INVALID_HANDLE_VALUE) + WARN("Input console handle initialization failed!\n"); + } + return MSVCRT_console_in; +} + +static HANDLE msvcrt_output_console(void) +{ + if (!MSVCRT_console_out) + { + MSVCRT_console_out = CreateFileA("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + if (MSVCRT_console_out == INVALID_HANDLE_VALUE) + WARN("Output console handle initialization failed!\n"); + } + return MSVCRT_console_out; +} + +/* INTERNAL: Free console handles */ +void msvcrt_free_console(void) +{ + TRACE(":Closing console handles\n"); + CloseHandle(MSVCRT_console_in); + CloseHandle(MSVCRT_console_out); +} + +/********************************************************************* + * _cputs (MSVCRT.@) + */ +int CDECL _cputs(const char* str) +{ + DWORD count; + int len, retval = -1; + + if (!MSVCRT_CHECK_PMT(str != NULL)) return -1; + len = strlen(str); + + LOCK_CONSOLE; + if (WriteConsoleA(msvcrt_output_console(), str, len, &count, NULL) + && count == len) + retval = 0; + UNLOCK_CONSOLE; + return retval; +} + +/********************************************************************* + * _cputws (MSVCRT.@) + */ +int CDECL _cputws(const wchar_t* str) +{ + DWORD count; + int len, retval = -1; + + if (!MSVCRT_CHECK_PMT(str != NULL)) return -1; + len = wcslen(str); + + LOCK_CONSOLE; + if (WriteConsoleW(msvcrt_output_console(), str, len, &count, NULL) + && count == len) + retval = 0; + UNLOCK_CONSOLE; + return retval; +} + +#define NORMAL_CHAR 0 +#define ALT_CHAR 1 +#define CTRL_CHAR 2 +#define SHIFT_CHAR 3 + +static const struct {unsigned short vk; unsigned char ch[4][2];} enh_map[] = { + {0x47, {{0xE0, 0x47}, {0x00, 0x97}, {0xE0, 0x77}, {0xE0, 0x47}}}, + {0x48, {{0xE0, 0x48}, {0x00, 0x98}, {0xE0, 0x8D}, {0xE0, 0x48}}}, + {0x49, {{0xE0, 0x49}, {0x00, 0x99}, {0xE0, 0x86}, {0xE0, 0x49}}}, + {0x4B, {{0xE0, 0x4B}, {0x00, 0x9B}, {0xE0, 0x73}, {0xE0, 0x4B}}}, + {0x4D, {{0xE0, 0x4D}, {0x00, 0x9D}, {0xE0, 0x74}, {0xE0, 0x4D}}}, + {0x4F, {{0xE0, 0x4F}, {0x00, 0x9F}, {0xE0, 0x75}, {0xE0, 0x4F}}}, + {0x50, {{0xE0, 0x50}, {0x00, 0xA0}, {0xE0, 0x91}, {0xE0, 0x50}}}, + {0x51, {{0xE0, 0x51}, {0x00, 0xA1}, {0xE0, 0x76}, {0xE0, 0x51}}}, + {0x52, {{0xE0, 0x52}, {0x00, 0xA2}, {0xE0, 0x92}, {0xE0, 0x52}}}, + {0x53, {{0xE0, 0x53}, {0x00, 0xA3}, {0xE0, 0x93}, {0xE0, 0x53}}}, +}; + +static BOOL handle_enhanced_keys(INPUT_RECORD *ir, unsigned char *ch1, unsigned char *ch2) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(enh_map); i++) + { + if (ir->Event.KeyEvent.wVirtualScanCode == enh_map[i].vk) + { + unsigned idx; + + if (ir->Event.KeyEvent.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) + idx = ALT_CHAR; + else if (ir->Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) ) + idx = CTRL_CHAR; + else if (ir->Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) + idx = SHIFT_CHAR; + else + idx = NORMAL_CHAR; + + *ch1 = enh_map[i].ch[idx][0]; + *ch2 = enh_map[i].ch[idx][1]; + return TRUE; + } + } + + WARN("Unmapped char keyState=%lx vk=%x\n", + ir->Event.KeyEvent.dwControlKeyState, ir->Event.KeyEvent.wVirtualScanCode); + return FALSE; +} + +/********************************************************************* + * _getch_nolock (MSVCR80.@) + */ +int CDECL _getch_nolock(void) +{ + int retval = EOF; + + if (__MSVCRT_console_buffer != EOF) + { + retval = __MSVCRT_console_buffer; + __MSVCRT_console_buffer = EOF; + } + else + { + INPUT_RECORD ir; + DWORD count; + DWORD mode = 0; + + GetConsoleMode(msvcrt_input_console(), &mode); + if(mode) + SetConsoleMode(msvcrt_input_console(), 0); + + do { + if (ReadConsoleInputA(msvcrt_input_console(), &ir, 1, &count)) + { + /* Only interested in ASCII chars */ + if (ir.EventType == KEY_EVENT && + ir.Event.KeyEvent.bKeyDown) + { + unsigned char ch1, ch2; + + if (ir.Event.KeyEvent.uChar.AsciiChar) + { + retval = ir.Event.KeyEvent.uChar.AsciiChar; + break; + } + + if (handle_enhanced_keys(&ir, &ch1, &ch2)) + { + retval = ch1; + __MSVCRT_console_buffer = ch2; + break; + } + } + } + else + break; + } while(1); + if (mode) + SetConsoleMode(msvcrt_input_console(), mode); + } + return retval; +} + +/********************************************************************* + * _getch (MSVCRT.@) + */ +int CDECL _getch(void) +{ + int ret; + + LOCK_CONSOLE; + ret = _getch_nolock(); + UNLOCK_CONSOLE; + return ret; +} + +/********************************************************************* + * _getwch_nolock (MSVCR80.@) + */ +wchar_t CDECL _getwch_nolock(void) +{ + wchar_t retval = WEOF; + + if (__MSVCRT_console_buffer_w != WEOF) + { + retval = __MSVCRT_console_buffer_w; + __MSVCRT_console_buffer_w = WEOF; + } + else + { + INPUT_RECORD ir; + DWORD count; + DWORD mode = 0; + + GetConsoleMode(msvcrt_input_console(), &mode); + if(mode) + SetConsoleMode(msvcrt_input_console(), 0); + + do { + if (ReadConsoleInputW(msvcrt_input_console(), &ir, 1, &count)) + { + /* Only interested in ASCII chars */ + if (ir.EventType == KEY_EVENT && + ir.Event.KeyEvent.bKeyDown) + { + unsigned char ch1, ch2; + + if (ir.Event.KeyEvent.uChar.UnicodeChar) + { + retval = ir.Event.KeyEvent.uChar.UnicodeChar; + break; + } + + if (handle_enhanced_keys(&ir, &ch1, &ch2)) + { + retval = ch1; + __MSVCRT_console_buffer_w = ch2; + break; + } + } + } + else + break; + } while(1); + if (mode) + SetConsoleMode(msvcrt_input_console(), mode); + } + return retval; +} + +/********************************************************************* + * _getwch (MSVCRT.@) + */ +wchar_t CDECL _getwch(void) +{ + wchar_t ret; + + LOCK_CONSOLE; + ret = _getwch_nolock(); + UNLOCK_CONSOLE; + return ret; +} + +/********************************************************************* + * _putch_nolock (MSVCR80.@) + */ +int CDECL _putch_nolock(int c) +{ + DWORD count; + if (WriteConsoleA(msvcrt_output_console(), &c, 1, &count, NULL) && count == 1) + return c; + return EOF; +} + +/********************************************************************* + * _putch (MSVCRT.@) + */ +int CDECL _putch(int c) +{ + LOCK_CONSOLE; + c = _putch_nolock(c); + UNLOCK_CONSOLE; + return c; +} + +/********************************************************************* + * _putwch_nolock (MSVCR80.@) + */ +wchar_t CDECL _putwch_nolock(wchar_t c) +{ + DWORD count; + if (WriteConsoleW(msvcrt_output_console(), &c, 1, &count, NULL) && count==1) + return c; + return WEOF; +} + +/********************************************************************* + * _putwch (MSVCRT.@) + */ +wchar_t CDECL _putwch(wchar_t c) +{ + LOCK_CONSOLE; + c = _putwch_nolock(c); + UNLOCK_CONSOLE; + return c; +} + +/********************************************************************* + * _getche_nolock (MSVCR80.@) + */ +int CDECL _getche_nolock(void) +{ + int retval; + retval = _getch_nolock(); + if (retval != EOF) + retval = _putch_nolock(retval); + return retval; +} + +/********************************************************************* + * _getche (MSVCRT.@) + */ +int CDECL _getche(void) +{ + int ret; + + LOCK_CONSOLE; + ret = _getche_nolock(); + UNLOCK_CONSOLE; + return ret; +} + +/********************************************************************* + * _getwche_nolock (MSVCR80.@) + */ +wchar_t CDECL _getwche_nolock(void) +{ + wchar_t wch; + wch = _getch_nolock(); + if (wch == WEOF) + return wch; + return _putwch_nolock(wch); +} + +/********************************************************************* + * _getwche (MSVCRT.@) + */ +wchar_t CDECL _getwche(void) +{ + wchar_t ret; + + LOCK_CONSOLE; + ret = _getwche_nolock(); + UNLOCK_CONSOLE; + return ret; +} + +/********************************************************************* + * _cgets (MSVCRT.@) + */ +char* CDECL _cgets(char* str) +{ + char *buf = str + 2; + DWORD got; + DWORD conmode = 0; + + TRACE("(%p)\n", str); + str[1] = 0; /* Length */ + LOCK_CONSOLE; + GetConsoleMode(msvcrt_input_console(), &conmode); + SetConsoleMode(msvcrt_input_console(), ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT); + + if(ReadConsoleA(msvcrt_input_console(), buf, str[0], &got, NULL)) { + if(buf[got-2] == '\r') { + buf[got-2] = 0; + str[1] = got-2; + } + else if(got == 1 && buf[got-1] == '\n') { + buf[0] = 0; + str[1] = 0; + } + else if(got == str[0] && buf[got-1] == '\r') { + buf[got-1] = 0; + str[1] = got-1; + } + else + str[1] = got; + } + else + buf = NULL; + SetConsoleMode(msvcrt_input_console(), conmode); + UNLOCK_CONSOLE; + return buf; +} + +/********************************************************************* + * _ungetch_nolock (MSVCRT.@) + */ +int CDECL _ungetch_nolock(int c) +{ + int retval = EOF; + if (c != EOF && __MSVCRT_console_buffer == EOF) + retval = __MSVCRT_console_buffer = c; + return retval; +} + +/********************************************************************* + * _ungetch (MSVCRT.@) + */ +int CDECL _ungetch(int c) +{ + LOCK_CONSOLE; + c = _ungetch_nolock(c); + UNLOCK_CONSOLE; + return c; +} + +/********************************************************************* + * _ungetwch_nolock (MSVCR80.@) + */ +wchar_t CDECL _ungetwch_nolock(wchar_t c) +{ + wchar_t retval = WEOF; + if (c != WEOF && __MSVCRT_console_buffer_w == WEOF) + retval = __MSVCRT_console_buffer_w = c; + return retval; +} + +/********************************************************************* + * _ungetwch (MSVCRT.@) + */ +wchar_t CDECL _ungetwch(wchar_t c) +{ + LOCK_CONSOLE; + c = _ungetwch_nolock(c); + UNLOCK_CONSOLE; + return c; +} + +/********************************************************************* + * _kbhit (MSVCRT.@) + */ +int CDECL _kbhit(void) +{ + int retval = 0; + + LOCK_CONSOLE; + if (__MSVCRT_console_buffer != EOF) + retval = 1; + else + { + /* FIXME: There has to be a faster way than this in Win32.. */ + INPUT_RECORD *ir = NULL; + DWORD count = 0, i; + + GetNumberOfConsoleInputEvents(msvcrt_input_console(), &count); + + if (count && (ir = malloc(count * sizeof(INPUT_RECORD))) && + PeekConsoleInputA(msvcrt_input_console(), ir, count, &count)) + for(i = 0; i < count; i++) + { + if (ir[i].EventType == KEY_EVENT && + ir[i].Event.KeyEvent.bKeyDown && + ir[i].Event.KeyEvent.uChar.AsciiChar) + { + retval = 1; + break; + } + } + free(ir); + } + UNLOCK_CONSOLE; + return retval; +} + +static int puts_clbk_console_a(void *ctx, int len, const char *str) +{ + LOCK_CONSOLE; + if(!WriteConsoleA(msvcrt_output_console(), str, len, NULL, NULL)) + len = -1; + UNLOCK_CONSOLE; + return len; +} + +static int puts_clbk_console_w(void *ctx, int len, const wchar_t *str) +{ + LOCK_CONSOLE; + if(!WriteConsoleW(msvcrt_output_console(), str, len, NULL, NULL)) + len = -1; + UNLOCK_CONSOLE; + return len; +} + +#if _MSVCR_VER<=120 +/********************************************************************* + * _vcprintf_l (MSVCRT.@) + */ +int CDECL _vcprintf_l(const char* format, _locale_t locale, va_list valist) +{ + return pf_printf_a(puts_clbk_console_a, NULL, format, locale, 0, arg_clbk_valist, NULL, &valist); +} +#endif + +/********************************************************************* + * _vcprintf (MSVCRT.@) + */ +int CDECL _vcprintf(const char* format, va_list valist) +{ + return pf_printf_a(puts_clbk_console_a, NULL, format, NULL, 0, arg_clbk_valist, NULL, &valist); +} + +#if _MSVCR_VER<=120 +/********************************************************************* + * _cprintf_l (MSVCRT.@) + */ +int WINAPIV _cprintf_l(const char* format, _locale_t locale, ...) +{ + int retval; + va_list valist; + + va_start(valist, locale); + retval = _vcprintf_l(format, locale, valist); + va_end(valist); + + return retval; +} +#endif + +/********************************************************************* + * _cprintf (MSVCRT.@) + */ +int WINAPIV _cprintf(const char* format, ...) +{ + int retval; + va_list valist; + + va_start( valist, format ); + retval = _vcprintf(format, valist); + va_end(valist); + + return retval; +} + +#if _MSVCR_VER<=120 +/********************************************************************* + * _vcwprintf_l (MSVCRT.@) + */ +int CDECL _vcwprintf_l(const wchar_t* format, _locale_t locale, va_list valist) +{ + return pf_printf_w(puts_clbk_console_w, NULL, format, locale, 0, arg_clbk_valist, NULL, &valist); +} + +/********************************************************************* + * _vcwprintf (MSVCRT.@) + */ +int CDECL _vcwprintf(const wchar_t* format, va_list valist) +{ + return pf_printf_w(puts_clbk_console_w, NULL, format, NULL, 0, arg_clbk_valist, NULL, &valist); +} + +/********************************************************************* + * _cwprintf_l (MSVCRT.@) + */ +int WINAPIV _cwprintf_l(const wchar_t* format, _locale_t locale, ...) +{ + int retval; + va_list valist; + + va_start(valist, locale); + retval = _vcwprintf_l(format, locale, valist); + va_end(valist); + + return retval; +} + +/********************************************************************* + * _cwprintf (MSVCRT.@) + */ +int WINAPIV _cwprintf(const wchar_t* format, ...) +{ + int retval; + va_list valist; + + va_start( valist, format ); + retval = _vcwprintf(format, valist); + va_end(valist); + + return retval; +} +#endif + +#if _MSVCR_VER>=140 + +/********************************************************************* + * __conio_common_vcprintf (UCRTBASE.@) + */ +int CDECL __conio_common_vcprintf(unsigned __int64 options, const char* format, + _locale_t locale, va_list valist) +{ + if (options & ~UCRTBASE_PRINTF_MASK) + FIXME("options %#I64x not handled\n", options); + return pf_printf_a(puts_clbk_console_a, NULL, format, locale, + options & UCRTBASE_PRINTF_MASK, arg_clbk_valist, NULL, &valist); +} + +/********************************************************************* + * __conio_common_vcwprintf (UCRTBASE.@) + */ +int CDECL __conio_common_vcwprintf(unsigned __int64 options, const wchar_t* format, + _locale_t locale, va_list valist) +{ + if (options & ~UCRTBASE_PRINTF_MASK) + FIXME("options %#I64x not handled\n", options); + return pf_printf_w(puts_clbk_console_w, NULL, format, locale, + options & UCRTBASE_PRINTF_MASK, arg_clbk_valist, NULL, &valist); +} + +#endif /* _MSVCR_VER>=140 */ diff --git a/dll/win32/msvcrt/cpp.c b/dll/win32/msvcrt/cpp.c new file mode 100644 index 00000000000..84959f1a05c --- /dev/null +++ b/dll/win32/msvcrt/cpp.c @@ -0,0 +1,1090 @@ +/* + * msvcrt.dll C++ objects + * + * Copyright 2000 Jon Griffiths + * Copyright 2003, 2004 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include +#include + +#include "windef.h" +#include "winternl.h" +#include "winbase.h" +#include "winver.h" +#include "imagehlp.h" +#include "wine/exception.h" +#include "wine/debug.h" +#include "msvcrt.h" +#include "mtdll.h" +#include "cppexcept.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +CREATE_TYPE_INFO_VTABLE +CREATE_EXCEPTION_OBJECT(exception) + +struct __type_info_node +{ + void *memPtr; + struct __type_info_node* next; +}; + +typedef exception bad_cast; +typedef exception bad_typeid; +typedef exception __non_rtti_object; + +extern const vtable_ptr bad_typeid_vtable; +extern const vtable_ptr bad_cast_vtable; +extern const vtable_ptr __non_rtti_object_vtable; +extern const vtable_ptr type_info_vtable; + +/* get the vtable pointer for a C++ object */ +static inline const vtable_ptr *get_vtable( void *obj ) +{ + return *(const vtable_ptr **)obj; +} + +static inline const rtti_object_locator *get_obj_locator( void *cppobj ) +{ + const vtable_ptr *vtable = get_vtable( cppobj ); + return (const rtti_object_locator *)vtable[-1]; +} + +static uintptr_t get_obj_locator_base( const rtti_object_locator *ptr ) +{ +#ifdef RTTI_USE_RVA + if (ptr->signature) return (uintptr_t)ptr - ptr->object_locator; +#endif + return rtti_rva_base( ptr ); +} + +static void dump_obj_locator( const rtti_object_locator *ptr ) +{ + int i; + uintptr_t base = get_obj_locator_base( ptr ); + const rtti_object_hierarchy *h = rtti_rva( ptr->type_hierarchy, base ); + const type_info *type_descriptor = rtti_rva( ptr->type_descriptor, base ); + + TRACE( "%p: sig=%08x base_offset=%08x flags=%08x type=%p %s hierarchy=%p\n", + ptr, ptr->signature, ptr->base_class_offset, ptr->flags, + type_descriptor, dbgstr_type_info(type_descriptor), h ); + TRACE( " hierarchy: sig=%08x attr=%08x len=%d base classes=%p\n", + h->signature, h->attributes, h->array_len, rtti_rva(h->base_classes, base) ); + for (i = 0; i < h->array_len; i++) + { + const rtti_base_array *base_array = rtti_rva( h->base_classes, base ); + const rtti_base_descriptor *bases = rtti_rva( base_array->bases[i], base ); + + TRACE( " base class %p: num %d off %d,%d,%d attr %08x type %p %s\n", + bases, bases->num_base_classes, bases->offsets.this_offset, + bases->offsets.vbase_descr, bases->offsets.vbase_offset, bases->attributes, + rtti_rva( bases->type_descriptor, base ), + dbgstr_type_info((const type_info*)(base + bases->type_descriptor)) ); + } +} + +/****************************************************************** + * ??0exception@@QAE@ABQBD@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(exception_ctor,8) +exception * __thiscall exception_ctor(exception * _this, const char ** name) +{ + TRACE("(%p,%s)\n", _this, *name); + return __exception_ctor(_this, *name, &exception_vtable); +} + +/****************************************************************** + * ??0exception@@QAE@ABQBDH@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(exception_ctor_noalloc,12) +exception * __thiscall exception_ctor_noalloc(exception * _this, char ** name, int noalloc) +{ + TRACE("(%p,%s)\n", _this, *name); + _this->vtable = &exception_vtable; + _this->name = *name; + _this->do_free = FALSE; + return _this; +} + +/****************************************************************** + * ??0exception@@QAE@XZ (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(exception_default_ctor,4) +exception * __thiscall exception_default_ctor(exception * _this) +{ + TRACE("(%p)\n", _this); + return __exception_ctor(_this, NULL, &exception_vtable); +} + +/****************************************************************** + * ??4exception@@QAEAAV0@ABV0@@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(exception_opequals,8) +exception * __thiscall exception_opequals(exception * _this, const exception * rhs) +{ + TRACE("(%p %p)\n", _this, rhs); + if (_this != rhs) + { + exception_dtor(_this); + exception_copy_ctor(_this, rhs); + } + TRACE("name = %s\n", _this->name); + return _this; +} + +/****************************************************************** + * ??_Gexception@@UAEPAXI@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(exception_scalar_dtor,8) +void * __thiscall exception_scalar_dtor(exception * _this, unsigned int flags) +{ + TRACE("(%p %x)\n", _this, flags); + exception_dtor(_this); + if (flags & 1) operator_delete(_this); + return _this; +} + +/****************************************************************** + * ??0bad_typeid@@QAE@ABV0@@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(bad_typeid_copy_ctor,8) +bad_typeid * __thiscall bad_typeid_copy_ctor(bad_typeid * _this, const bad_typeid * rhs) +{ + TRACE("(%p %p)\n", _this, rhs); + return __exception_copy_ctor(_this, rhs, &bad_typeid_vtable); +} + +/****************************************************************** + * ??0bad_typeid@@QAE@PBD@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(bad_typeid_ctor,8) +bad_typeid * __thiscall bad_typeid_ctor(bad_typeid * _this, const char * name) +{ + TRACE("(%p %s)\n", _this, name); + return __exception_ctor(_this, name, &bad_typeid_vtable); +} + +/****************************************************************** + * ??_Fbad_typeid@@QAEXXZ (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(bad_typeid_default_ctor,4) +bad_typeid * __thiscall bad_typeid_default_ctor(bad_typeid * _this) +{ + return bad_typeid_ctor( _this, "bad typeid" ); +} + +/****************************************************************** + * ??1bad_typeid@@UAE@XZ (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(bad_typeid_dtor,4) +void __thiscall bad_typeid_dtor(bad_typeid * _this) +{ + TRACE("(%p)\n", _this); + exception_dtor(_this); +} + +/****************************************************************** + * ??4bad_typeid@@QAEAAV0@ABV0@@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(bad_typeid_opequals,8) +bad_typeid * __thiscall bad_typeid_opequals(bad_typeid * _this, const bad_typeid * rhs) +{ + TRACE("(%p %p)\n", _this, rhs); + exception_opequals(_this, rhs); + return _this; +} + +/****************************************************************** + * ??_Ebad_typeid@@UAEPAXI@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(bad_typeid_vector_dtor,8) +void * __thiscall bad_typeid_vector_dtor(bad_typeid * _this, unsigned int flags) +{ + TRACE("(%p %x)\n", _this, flags); + if (flags & 2) + { + /* we have an array, with the number of elements stored before the first object */ + INT_PTR i, *ptr = (INT_PTR *)_this - 1; + + for (i = *ptr - 1; i >= 0; i--) bad_typeid_dtor(_this + i); + operator_delete(ptr); + } + else + { + bad_typeid_dtor(_this); + if (flags & 1) operator_delete(_this); + } + return _this; +} + +/****************************************************************** + * ??_Gbad_typeid@@UAEPAXI@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(bad_typeid_scalar_dtor,8) +void * __thiscall bad_typeid_scalar_dtor(bad_typeid * _this, unsigned int flags) +{ + TRACE("(%p %x)\n", _this, flags); + bad_typeid_dtor(_this); + if (flags & 1) operator_delete(_this); + return _this; +} + +/****************************************************************** + * ??0__non_rtti_object@@QAE@ABV0@@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(__non_rtti_object_copy_ctor,8) +__non_rtti_object * __thiscall __non_rtti_object_copy_ctor(__non_rtti_object * _this, + const __non_rtti_object * rhs) +{ + TRACE("(%p %p)\n", _this, rhs); + return __exception_copy_ctor(_this, rhs, &__non_rtti_object_vtable); +} + +/****************************************************************** + * ??0__non_rtti_object@@QAE@PBD@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(__non_rtti_object_ctor,8) +__non_rtti_object * __thiscall __non_rtti_object_ctor(__non_rtti_object * _this, + const char * name) +{ + TRACE("(%p %s)\n", _this, name); + return __exception_ctor(_this, name, &__non_rtti_object_vtable); +} + +/****************************************************************** + * ??1__non_rtti_object@@UAE@XZ (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(__non_rtti_object_dtor,4) +void __thiscall __non_rtti_object_dtor(__non_rtti_object * _this) +{ + TRACE("(%p)\n", _this); + bad_typeid_dtor(_this); +} + +/****************************************************************** + * ??4__non_rtti_object@@QAEAAV0@ABV0@@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(__non_rtti_object_opequals,8) +__non_rtti_object * __thiscall __non_rtti_object_opequals(__non_rtti_object * _this, + const __non_rtti_object *rhs) +{ + TRACE("(%p %p)\n", _this, rhs); + bad_typeid_opequals(_this, rhs); + return _this; +} + +/****************************************************************** + * ??_E__non_rtti_object@@UAEPAXI@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(__non_rtti_object_vector_dtor,8) +void * __thiscall __non_rtti_object_vector_dtor(__non_rtti_object * _this, unsigned int flags) +{ + TRACE("(%p %x)\n", _this, flags); + if (flags & 2) + { + /* we have an array, with the number of elements stored before the first object */ + INT_PTR i, *ptr = (INT_PTR *)_this - 1; + + for (i = *ptr - 1; i >= 0; i--) __non_rtti_object_dtor(_this + i); + operator_delete(ptr); + } + else + { + __non_rtti_object_dtor(_this); + if (flags & 1) operator_delete(_this); + } + return _this; +} + +/****************************************************************** + * ??_G__non_rtti_object@@UAEPAXI@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(__non_rtti_object_scalar_dtor,8) +void * __thiscall __non_rtti_object_scalar_dtor(__non_rtti_object * _this, unsigned int flags) +{ + TRACE("(%p %x)\n", _this, flags); + __non_rtti_object_dtor(_this); + if (flags & 1) operator_delete(_this); + return _this; +} + +/****************************************************************** + * ??0bad_cast@@AAE@PBQBD@Z (MSVCRT.@) + * ??0bad_cast@@QAE@ABQBD@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(bad_cast_ctor,8) +bad_cast * __thiscall bad_cast_ctor(bad_cast * _this, const char ** name) +{ + TRACE("(%p %s)\n", _this, *name); + return __exception_ctor(_this, *name, &bad_cast_vtable); +} + +/****************************************************************** + * ??0bad_cast@@QAE@ABV0@@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(bad_cast_copy_ctor,8) +bad_cast * __thiscall bad_cast_copy_ctor(bad_cast * _this, const bad_cast * rhs) +{ + TRACE("(%p %p)\n", _this, rhs); + return __exception_copy_ctor(_this, rhs, &bad_cast_vtable); +} + +/****************************************************************** + * ??0bad_cast@@QAE@PBD@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(bad_cast_ctor_charptr,8) +bad_cast * __thiscall bad_cast_ctor_charptr(bad_cast * _this, const char * name) +{ + TRACE("(%p %s)\n", _this, name); + return __exception_ctor(_this, name, &bad_cast_vtable); +} + +/****************************************************************** + * ??_Fbad_cast@@QAEXXZ (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(bad_cast_default_ctor,4) +bad_cast * __thiscall bad_cast_default_ctor(bad_cast * _this) +{ + return bad_cast_ctor_charptr( _this, "bad cast" ); +} + +/****************************************************************** + * ??1bad_cast@@UAE@XZ (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(bad_cast_dtor,4) +void __thiscall bad_cast_dtor(bad_cast * _this) +{ + TRACE("(%p)\n", _this); + exception_dtor(_this); +} + +/****************************************************************** + * ??4bad_cast@@QAEAAV0@ABV0@@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(bad_cast_opequals,8) +bad_cast * __thiscall bad_cast_opequals(bad_cast * _this, const bad_cast * rhs) +{ + TRACE("(%p %p)\n", _this, rhs); + exception_opequals(_this, rhs); + return _this; +} + +/****************************************************************** + * ??_Ebad_cast@@UAEPAXI@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(bad_cast_vector_dtor,8) +void * __thiscall bad_cast_vector_dtor(bad_cast * _this, unsigned int flags) +{ + TRACE("(%p %x)\n", _this, flags); + if (flags & 2) + { + /* we have an array, with the number of elements stored before the first object */ + INT_PTR i, *ptr = (INT_PTR *)_this - 1; + + for (i = *ptr - 1; i >= 0; i--) bad_cast_dtor(_this + i); + operator_delete(ptr); + } + else + { + bad_cast_dtor(_this); + if (flags & 1) operator_delete(_this); + } + return _this; +} + +/****************************************************************** + * ??_Gbad_cast@@UAEPAXI@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(bad_cast_scalar_dtor,8) +void * __thiscall bad_cast_scalar_dtor(bad_cast * _this, unsigned int flags) +{ + TRACE("(%p %x)\n", _this, flags); + bad_cast_dtor(_this); + if (flags & 1) operator_delete(_this); + return _this; +} + +/****************************************************************** + * ??8type_info@@QBEHABV0@@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(type_info_opequals_equals,8) +int __thiscall type_info_opequals_equals(type_info * _this, const type_info * rhs) +{ + int ret = !strcmp(_this->mangled + 1, rhs->mangled + 1); + TRACE("(%p %p) returning %d\n", _this, rhs, ret); + return ret; +} + +/****************************************************************** + * ??9type_info@@QBEHABV0@@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(type_info_opnot_equals,8) +int __thiscall type_info_opnot_equals(type_info * _this, const type_info * rhs) +{ + int ret = !!strcmp(_this->mangled + 1, rhs->mangled + 1); + TRACE("(%p %p) returning %d\n", _this, rhs, ret); + return ret; +} + +/****************************************************************** + * ?before@type_info@@QBEHABV1@@Z (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(type_info_before,8) +int __thiscall type_info_before(type_info * _this, const type_info * rhs) +{ + int ret = strcmp(_this->mangled + 1, rhs->mangled + 1) < 0; + TRACE("(%p %p) returning %d\n", _this, rhs, ret); + return ret; +} + +/****************************************************************** + * ??1type_info@@UAE@XZ (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(type_info_dtor,4) +void __thiscall type_info_dtor(type_info * _this) +{ + TRACE("(%p)\n", _this); + free(_this->name); +} + +/****************************************************************** + * ?name@type_info@@QBEPBDXZ (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(type_info_name,4) +const char * __thiscall type_info_name(type_info * _this) +{ + if (!_this->name) + { + /* Create and set the demangled name */ + /* Note: mangled name in type_info struct always starts with a '.', while + * it isn't valid for mangled name. + * Is this '.' really part of the mangled name, or has it some other meaning ? + */ + char* name = __unDName(0, _this->mangled + 1, 0, + malloc, free, UNDNAME_NO_ARGUMENTS | UNDNAME_32_BIT_DECODE); + if (name) + { + unsigned int len = strlen(name); + + /* It seems _unDName may leave blanks at the end of the demangled name */ + while (len && name[--len] == ' ') + name[len] = '\0'; + + if (InterlockedCompareExchangePointer((void**)&_this->name, name, NULL)) + { + /* Another thread set this member since we checked above - use it */ + free(name); + } + } + } + TRACE("(%p) returning %s\n", _this, _this->name); + return _this->name; +} + +/****************************************************************** + * ?raw_name@type_info@@QBEPBDXZ (MSVCRT.@) + */ +DEFINE_THISCALL_WRAPPER(type_info_raw_name,4) +const char * __thiscall type_info_raw_name(type_info * _this) +{ + TRACE("(%p) returning %s\n", _this, _this->mangled); + return _this->mangled; +} + +#if _MSVCR_VER >= 80 + +typedef exception bad_alloc; +extern const vtable_ptr bad_alloc_vtable; + +/* bad_alloc class implementation */ +DEFINE_THISCALL_WRAPPER(bad_alloc_copy_ctor,8) +bad_alloc * __thiscall bad_alloc_copy_ctor(bad_alloc * _this, const bad_alloc * rhs) +{ + TRACE("(%p %p)\n", _this, rhs); + return __exception_copy_ctor(_this, rhs, &bad_alloc_vtable); +} + +DEFINE_THISCALL_WRAPPER(bad_alloc_dtor,4) +void __thiscall bad_alloc_dtor(bad_alloc * _this) +{ + TRACE("(%p)\n", _this); + exception_dtor(_this); +} + +#endif /* _MSVCR_VER >= 80 */ + +__ASM_BLOCK_BEGIN(vtables) + +#if _MSVCR_VER >= 80 +__ASM_VTABLE(exception_old, + VTABLE_ADD_FUNC(exception_vector_dtor) + VTABLE_ADD_FUNC(exception_what)); +__ASM_VTABLE(bad_alloc, + VTABLE_ADD_FUNC(exception_vector_dtor) + VTABLE_ADD_FUNC(exception_what)); +#endif +__ASM_VTABLE(bad_typeid, + VTABLE_ADD_FUNC(bad_typeid_vector_dtor) + VTABLE_ADD_FUNC(exception_what)); +__ASM_VTABLE(bad_cast, + VTABLE_ADD_FUNC(bad_cast_vector_dtor) + VTABLE_ADD_FUNC(exception_what)); +__ASM_VTABLE(__non_rtti_object, + VTABLE_ADD_FUNC(__non_rtti_object_vector_dtor) + VTABLE_ADD_FUNC(exception_what)); + +__ASM_BLOCK_END + +#if _MSVCR_VER >= 80 +DEFINE_RTTI_DATA0( exception_old, 0, ".?AVexception@@" ) +DEFINE_RTTI_DATA1( bad_typeid, 0, &exception_rtti_base_descriptor, ".?AVbad_typeid@std@@" ) +DEFINE_RTTI_DATA1( bad_cast, 0, &exception_rtti_base_descriptor, ".?AVbad_cast@std@@" ) +DEFINE_RTTI_DATA2( __non_rtti_object, 0, &bad_typeid_rtti_base_descriptor, &exception_rtti_base_descriptor, ".?AV__non_rtti_object@std@@" ) +DEFINE_RTTI_DATA1( bad_alloc, 0, &exception_rtti_base_descriptor, ".?AVbad_alloc@std@@" ) +#else +DEFINE_RTTI_DATA1( bad_typeid, 0, &exception_rtti_base_descriptor, ".?AVbad_typeid@@" ) +DEFINE_RTTI_DATA1( bad_cast, 0, &exception_rtti_base_descriptor, ".?AVbad_cast@@" ) +DEFINE_RTTI_DATA2( __non_rtti_object, 0, &bad_typeid_rtti_base_descriptor, &exception_rtti_base_descriptor, ".?AV__non_rtti_object@@" ) +#endif + +DEFINE_CXX_DATA0( exception, exception_dtor ) +DEFINE_CXX_DATA1( bad_typeid, &exception_cxx_type_info, bad_typeid_dtor ) +DEFINE_CXX_DATA1( bad_cast, &exception_cxx_type_info, bad_cast_dtor ) +DEFINE_CXX_DATA2( __non_rtti_object, &bad_typeid_cxx_type_info, + &exception_cxx_type_info, __non_rtti_object_dtor ) +#if _MSVCR_VER >= 80 +DEFINE_CXX_DATA1( bad_alloc, &exception_cxx_type_info, bad_alloc_dtor ) +#endif + +void msvcrt_init_exception(void *base) +{ +#ifdef RTTI_USE_RVA + init_type_info_rtti(base); + init_exception_rtti(base); +#if _MSVCR_VER >= 80 + init_exception_old_rtti(base); + init_bad_alloc_rtti(base); +#endif + init_bad_typeid_rtti(base); + init_bad_cast_rtti(base); + init___non_rtti_object_rtti(base); + + init_exception_cxx(base); + init_bad_typeid_cxx(base); + init_bad_cast_cxx(base); + init___non_rtti_object_cxx(base); +#if _MSVCR_VER >= 80 + init_bad_alloc_cxx(base); +#endif +#endif +} + +#if _MSVCR_VER >= 80 +void throw_bad_alloc(void) +{ + bad_alloc e; + __exception_ctor(&e, "bad allocation", &bad_alloc_vtable); + _CxxThrowException(&e, &bad_alloc_exception_type); +} +#endif + +void throw_exception(const char* msg) +{ + exception e; + __exception_ctor(&e, msg, &exception_vtable); + _CxxThrowException(&e, &exception_exception_type); +} + +/****************************************************************** + * ?set_terminate@@YAP6AXXZP6AXXZ@Z (MSVCRT.@) + * + * Install a handler to be called when terminate() is called. + * + * PARAMS + * func [I] Handler function to install + * + * RETURNS + * The previously installed handler function, if any. + */ +terminate_function CDECL set_terminate(terminate_function func) +{ + thread_data_t *data = msvcrt_get_thread_data(); + terminate_function previous = data->terminate_handler; + TRACE("(%p) returning %p\n",func,previous); + data->terminate_handler = func; + return previous; +} + +/****************************************************************** + * _get_terminate (MSVCRT.@) + */ +terminate_function CDECL _get_terminate(void) +{ + thread_data_t *data = msvcrt_get_thread_data(); + TRACE("returning %p\n", data->terminate_handler); + return data->terminate_handler; +} + +/****************************************************************** + * ?set_unexpected@@YAP6AXXZP6AXXZ@Z (MSVCRT.@) + * + * Install a handler to be called when unexpected() is called. + * + * PARAMS + * func [I] Handler function to install + * + * RETURNS + * The previously installed handler function, if any. + */ +unexpected_function CDECL set_unexpected(unexpected_function func) +{ + thread_data_t *data = msvcrt_get_thread_data(); + unexpected_function previous = data->unexpected_handler; + TRACE("(%p) returning %p\n",func,previous); + data->unexpected_handler = func; + return previous; +} + +/****************************************************************** + * _get_unexpected (MSVCRT.@) + */ +unexpected_function CDECL _get_unexpected(void) +{ + thread_data_t *data = msvcrt_get_thread_data(); + TRACE("returning %p\n", data->unexpected_handler); + return data->unexpected_handler; +} + +/****************************************************************** + * ?_set_se_translator@@YAP6AXIPAU_EXCEPTION_POINTERS@@@ZP6AXI0@Z@Z (MSVCRT.@) + */ +_se_translator_function CDECL _set_se_translator(_se_translator_function func) +{ + thread_data_t *data = msvcrt_get_thread_data(); + _se_translator_function previous = data->se_translator; + TRACE("(%p) returning %p\n",func,previous); + data->se_translator = func; + return previous; +} + +/****************************************************************** + * ?terminate@@YAXXZ (MSVCRT.@) + * + * Default handler for an unhandled exception. + * + * PARAMS + * None. + * + * RETURNS + * This function does not return. Either control resumes from any + * handler installed by calling set_terminate(), or (by default) abort() + * is called. + */ +void CDECL terminate(void) +{ + thread_data_t *data = msvcrt_get_thread_data(); + if (data->terminate_handler) data->terminate_handler(); + abort(); +} + +/****************************************************************** + * ?unexpected@@YAXXZ (MSVCRT.@) + */ +void CDECL unexpected(void) +{ + thread_data_t *data = msvcrt_get_thread_data(); + if (data->unexpected_handler) data->unexpected_handler(); + terminate(); +} + + +/****************************************************************** + * __RTtypeid (MSVCRT.@) + * + * Retrieve the Run Time Type Information (RTTI) for a C++ object. + * + * PARAMS + * cppobj [I] C++ object to get type information for. + * + * RETURNS + * Success: A type_info object describing cppobj. + * Failure: If the object to be cast has no RTTI, a __non_rtti_object + * exception is thrown. If cppobj is NULL, a bad_typeid exception + * is thrown. In either case, this function does not return. + * + * NOTES + * This function is usually called by compiler generated code as a result + * of using one of the C++ dynamic cast statements. + */ +const type_info* CDECL __RTtypeid(void *cppobj) +{ + const type_info *ret; + + if (!cppobj) + { + bad_typeid e; + bad_typeid_ctor( &e, "Attempted a typeid of NULL pointer!" ); + _CxxThrowException( &e, &bad_typeid_exception_type ); + } + + __TRY + { + const rtti_object_locator *obj_locator = get_obj_locator( cppobj ); + uintptr_t base = get_obj_locator_base( obj_locator ); + + ret = rtti_rva( obj_locator->type_descriptor, base ); + } + __EXCEPT_PAGE_FAULT + { + __non_rtti_object e; + __non_rtti_object_ctor( &e, "Bad read pointer - no RTTI data!" ); + _CxxThrowException( &e, &__non_rtti_object_exception_type ); + } + __ENDTRY + return ret; +} + +/****************************************************************** + * __RTDynamicCast (MSVCRT.@) + * + * Dynamically cast a C++ object to one of its base classes. + * + * PARAMS + * cppobj [I] Any C++ object to cast + * unknown [I] Reserved, set to 0 + * src [I] type_info object describing cppobj + * dst [I] type_info object describing the base class to cast to + * do_throw [I] TRUE = throw an exception if the cast fails, FALSE = don't + * + * RETURNS + * Success: The address of cppobj, cast to the object described by dst. + * Failure: NULL, If the object to be cast has no RTTI, or dst is not a + * valid cast for cppobj. If do_throw is TRUE, a bad_cast exception + * is thrown and this function does not return. + * + * NOTES + * This function is usually called by compiler generated code as a result + * of using one of the C++ dynamic cast statements. + */ +void* CDECL __RTDynamicCast(void *cppobj, int unknown, + type_info *src, type_info *dst, + int do_throw) +{ + void *ret; + + if (!cppobj) return NULL; + + TRACE("obj: %p unknown: %d src: %p %s dst: %p %s do_throw: %d)\n", + cppobj, unknown, src, dbgstr_type_info(src), dst, dbgstr_type_info(dst), do_throw); + + /* To cast an object at runtime: + * 1.Find out the true type of the object from the typeinfo at vtable[-1] + * 2.Search for the destination type in the class hierarchy + * 3.If destination type is found, return base object address + dest offset + * Otherwise, fail the cast + * + * FIXME: the unknown parameter doesn't seem to be used for anything + */ + __TRY + { + int i; + const rtti_object_locator *obj_locator = get_obj_locator( cppobj ); + uintptr_t base = get_obj_locator_base( obj_locator ); + const rtti_object_hierarchy *obj_bases = rtti_rva( obj_locator->type_hierarchy, base ); + const rtti_base_array *base_array = rtti_rva( obj_bases->base_classes, base ); + + if (TRACE_ON(msvcrt)) dump_obj_locator(obj_locator); + + ret = NULL; + for (i = 0; i < obj_bases->array_len; i++) + { + const rtti_base_descriptor *base_desc = rtti_rva( base_array->bases[i], base ); + const type_info *typ = rtti_rva( base_desc->type_descriptor, base ); + + if (!strcmp(typ->mangled, dst->mangled)) + { + /* compute the correct this pointer for that base class */ + void *this_ptr = (char *)cppobj - obj_locator->base_class_offset; + ret = get_this_pointer( &base_desc->offsets, this_ptr ); + break; + } + } + /* VC++ sets do_throw to 1 when the result of a dynamic_cast is assigned + * to a reference, since references cannot be NULL. + */ + if (!ret && do_throw) + { + const char *msg = "Bad dynamic_cast!"; + bad_cast e; + bad_cast_ctor( &e, &msg ); + _CxxThrowException( &e, &bad_cast_exception_type ); + } + } + __EXCEPT_PAGE_FAULT + { + __non_rtti_object e; + __non_rtti_object_ctor( &e, "Access violation - no RTTI data!" ); + _CxxThrowException( &e, &__non_rtti_object_exception_type ); + } + __ENDTRY + return ret; +} + + +/****************************************************************** + * __RTCastToVoid (MSVCRT.@) + * + * Dynamically cast a C++ object to a void*. + * + * PARAMS + * cppobj [I] The C++ object to cast + * + * RETURNS + * Success: The base address of the object as a void*. + * Failure: NULL, if cppobj is NULL or has no RTTI. + * + * NOTES + * This function is usually called by compiler generated code as a result + * of using one of the C++ dynamic cast statements. + */ +void* CDECL __RTCastToVoid(void *cppobj) +{ + void *ret; + + if (!cppobj) return NULL; + + __TRY + { + const rtti_object_locator *obj_locator = get_obj_locator( cppobj ); + ret = (char *)cppobj - obj_locator->base_class_offset; + } + __EXCEPT_PAGE_FAULT + { + __non_rtti_object e; + __non_rtti_object_ctor( &e, "Access violation - no RTTI data!" ); + _CxxThrowException( &e, &__non_rtti_object_exception_type ); + } + __ENDTRY + return ret; +} + + +/********************************************************************* + * _CxxThrowException (MSVCRT.@) + */ +void WINAPI _CxxThrowException( void *object, const cxx_exception_type *type ) +{ + ULONG_PTR args[CXX_EXCEPTION_PARAMS]; + + args[0] = CXX_FRAME_MAGIC_VC6; + args[1] = (ULONG_PTR)object; + args[2] = (ULONG_PTR)type; + if (CXX_EXCEPTION_PARAMS == 4) args[3] = rtti_rva_base( type ); + for (;;) RaiseException( CXX_EXCEPTION, EXCEPTION_NONCONTINUABLE, CXX_EXCEPTION_PARAMS, args ); +} + +#if _MSVCR_VER >= 80 + +/********************************************************************* + * ?_is_exception_typeof@@YAHABVtype_info@@PAU_EXCEPTION_POINTERS@@@Z + * ?_is_exception_typeof@@YAHAEBVtype_info@@PEAU_EXCEPTION_POINTERS@@@Z + */ +int __cdecl _is_exception_typeof(const type_info *ti, EXCEPTION_POINTERS *ep) +{ + int ret = -1; + + TRACE("(%p %p)\n", ti, ep); + + __TRY + { + EXCEPTION_RECORD *rec = ep->ExceptionRecord; + + if (is_cxx_exception( rec )) + { + const cxx_exception_type *et = (cxx_exception_type*)rec->ExceptionInformation[2]; + uintptr_t base = (CXX_EXCEPTION_PARAMS == 4) ? rec->ExceptionInformation[3] : 0; + const cxx_type_info_table *tit = rtti_rva( et->type_info_table, base ); + int i; + + for (i=0; icount; i++) + { + const cxx_type_info *cti = rtti_rva( tit->info[i], base ); + const type_info *except_ti = rtti_rva( cti->type_info, base ); + if (ti==except_ti || !strcmp(ti->mangled, except_ti->mangled)) + { + ret = 1; + break; + } + } + + if (i == tit->count) + ret = 0; + } + } + __EXCEPT_PAGE_FAULT + __ENDTRY + + if(ret == -1) + terminate(); + return ret; +} + +/********************************************************************* + * __clean_type_info_names_internal (MSVCR80.@) + */ +void CDECL __clean_type_info_names_internal(void *p) +{ + FIXME("(%p) stub\n", p); +} + +/********************************************************************* + * ?_name_internal_method@type_info@@QBEPBDPAU__type_info_node@@@Z (MSVCR100.@) + */ +DEFINE_THISCALL_WRAPPER(type_info_name_internal_method,8) +const char * __thiscall type_info_name_internal_method(type_info * _this, struct __type_info_node *node) +{ + static int once; + if (node && !once++) FIXME("type_info_node parameter ignored\n"); + + return type_info_name(_this); +} + +void* __cdecl __AdjustPointer(void *obj, const this_ptr_offsets *off) +{ + return get_this_pointer(off, obj); +} + +#endif + +#if _MSVCR_VER >= 140 + +typedef struct +{ + char *name; + char mangled[1]; +} type_info140; + +typedef struct +{ + SLIST_ENTRY entry; + char name[1]; +} type_info_entry; + +static void* CDECL type_info_entry_malloc(size_t size) +{ + type_info_entry *ret = malloc(FIELD_OFFSET(type_info_entry, name) + size); + return ret->name; +} + +static void CDECL type_info_entry_free(void *ptr) +{ + ptr = (char*)ptr - FIELD_OFFSET(type_info_entry, name); + free(ptr); +} + +/****************************************************************** + * __std_type_info_compare (UCRTBASE.@) + */ +int CDECL __std_type_info_compare(const type_info140 *l, const type_info140 *r) +{ + int ret; + + if (l == r) ret = 0; + else ret = strcmp(l->mangled + 1, r->mangled + 1); + TRACE("(%p %p) returning %d\n", l, r, ret); + return ret; +} + +/****************************************************************** + * __std_type_info_name (UCRTBASE.@) + */ +const char* CDECL __std_type_info_name(type_info140 *ti, SLIST_HEADER *header) +{ + if (!ti->name) + { + char* name = __unDName(0, ti->mangled + 1, 0, + type_info_entry_malloc, type_info_entry_free, UNDNAME_NO_ARGUMENTS | UNDNAME_32_BIT_DECODE); + if (name) + { + unsigned int len = strlen(name); + + while (len && name[--len] == ' ') + name[len] = '\0'; + + if (InterlockedCompareExchangePointer((void**)&ti->name, name, NULL)) + { + type_info_entry_free(name); + } + else + { + type_info_entry *entry = (type_info_entry*)(name-FIELD_OFFSET(type_info_entry, name)); + InterlockedPushEntrySList(header, &entry->entry); + } + } + } + TRACE("(%p) returning %s\n", ti, ti->name); + return ti->name; +} + +/****************************************************************** + * __std_type_info_destroy_list (UCRTBASE.@) + */ +void CDECL __std_type_info_destroy_list(SLIST_HEADER *header) +{ + SLIST_ENTRY *cur, *next; + + TRACE("(%p)\n", header); + + for(cur = InterlockedFlushSList(header); cur; cur = next) + { + next = cur->Next; + free(cur); + } +} + +/****************************************************************** + * __std_type_info_hash (UCRTBASE.@) + */ +size_t CDECL __std_type_info_hash(const type_info140 *ti) +{ + size_t hash, fnv_prime; + const char *p; + +#ifdef _WIN64 + hash = 0xcbf29ce484222325; + fnv_prime = 0x100000001b3; +#else + hash = 0x811c9dc5; + fnv_prime = 0x1000193; +#endif + + TRACE("(%p)->%s\n", ti, ti->mangled); + + for(p = ti->mangled+1; *p; p++) { + hash ^= *p; + hash *= fnv_prime; + } + +#ifdef _WIN64 + hash ^= hash >> 32; +#endif + + return hash; +} + +#endif /* _MSVCR_VER >= 140 */ diff --git a/dll/win32/msvcrt/cppexcept.h b/dll/win32/msvcrt/cppexcept.h new file mode 100644 index 00000000000..e6afd1e871e --- /dev/null +++ b/dll/win32/msvcrt/cppexcept.h @@ -0,0 +1,373 @@ +/* + * msvcrt C++ exception handling + * + * Copyright 2002 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#ifndef __MSVCRT_CPPEXCEPT_H +#define __MSVCRT_CPPEXCEPT_H + +#include +#include "cxx.h" + +#define CXX_FRAME_MAGIC_VC6 0x19930520 +#define CXX_FRAME_MAGIC_VC7 0x19930521 +#define CXX_FRAME_MAGIC_VC8 0x19930522 +#define CXX_EXCEPTION 0xe06d7363 + +typedef struct +{ + UINT ip; + int state; +} ipmap_info; + +#ifndef RTTI_USE_RVA + +#define CXX_EXCEPTION_PARAMS 3 + +/* info about a single catch {} block */ +typedef struct +{ + UINT flags; /* flags (see below) */ + const type_info *type_info; /* C++ type caught by this block */ + int offset; /* stack offset to copy exception object to */ + void * (*handler)(void);/* catch block handler code */ +} catchblock_info; + +/* info about a single try {} block */ +typedef struct +{ + int start_level; /* start trylevel of that block */ + int end_level; /* end trylevel of that block */ + int catch_level; /* initial trylevel of the catch block */ + unsigned int catchblock_count; /* count of catch blocks in array */ + const catchblock_info *catchblock; /* array of catch blocks */ +} tryblock_info; + +/* info about the unwind handler for a given trylevel */ +typedef struct +{ + int prev; /* prev trylevel unwind handler, to run after this one */ + void * (*handler)(void);/* unwind handler */ +} unwind_info; + +/* descriptor of all try blocks of a given function */ +typedef struct +{ + UINT magic : 29; /* must be CXX_FRAME_MAGIC */ + UINT bbt_flags : 3; + UINT unwind_count; /* number of unwind handlers */ + const unwind_info *unwind_table; /* array of unwind handlers */ + UINT tryblock_count; /* number of try blocks */ + const tryblock_info *tryblock; /* array of try blocks */ + UINT ipmap_count; + const ipmap_info *ipmap; + const void *expect_list; /* expected exceptions list when magic >= VC7 */ + UINT flags; /* flags when magic >= VC8 */ +} cxx_function_descr; + +#else /* RTTI_USE_RVA */ + +#define CXX_EXCEPTION_PARAMS 4 + +typedef struct +{ + UINT flags; + UINT type_info; + int offset; + UINT handler; +#ifdef _WIN64 + UINT frame; +#endif +} catchblock_info; + +typedef struct +{ + int start_level; + int end_level; + int catch_level; + UINT catchblock_count; + UINT catchblock; +} tryblock_info; + +typedef struct +{ + int prev; + UINT handler; +} unwind_info; + +typedef struct +{ + UINT magic : 29; + UINT bbt_flags : 3; + UINT unwind_count; + UINT unwind_table; + UINT tryblock_count; + UINT tryblock; + UINT ipmap_count; + UINT ipmap; + int unwind_help; + UINT expect_list; + UINT flags; +} cxx_function_descr; + +#endif /* RTTI_USE_RVA */ + +#define FUNC_DESCR_SYNCHRONOUS 1 /* synchronous exceptions only (built with /EHs and /EHsc) */ +#define FUNC_DESCR_NOEXCEPT 4 /* noexcept function */ + +#define CLASS_IS_SIMPLE_TYPE 1 +#define CLASS_HAS_VIRTUAL_BASE_CLASS 4 + +#define TYPE_FLAG_CONST 1 +#define TYPE_FLAG_VOLATILE 2 +#define TYPE_FLAG_REFERENCE 8 + +void WINAPI DECLSPEC_NORETURN _CxxThrowException(void*,const cxx_exception_type*); + +static inline BOOL is_cxx_exception( EXCEPTION_RECORD *rec ) +{ + if (rec->ExceptionCode != CXX_EXCEPTION) return FALSE; + if (rec->NumberParameters != CXX_EXCEPTION_PARAMS) return FALSE; + return (rec->ExceptionInformation[0] >= CXX_FRAME_MAGIC_VC6 && + rec->ExceptionInformation[0] <= CXX_FRAME_MAGIC_VC8); +} + +typedef struct +{ + EXCEPTION_RECORD *rec; + LONG *ref; /* not binary compatible with native msvcr100 */ +} exception_ptr; + +void throw_exception(const char*); +void exception_ptr_from_record(exception_ptr*,EXCEPTION_RECORD*); + +void __cdecl __ExceptionPtrCreate(exception_ptr*); +void __cdecl __ExceptionPtrDestroy(exception_ptr*); +void __cdecl __ExceptionPtrRethrow(const exception_ptr*); + +BOOL __cdecl __uncaught_exception(void); + +static inline const char *dbgstr_type_info( const type_info *info ) +{ + if (!info) return "{}"; + return wine_dbg_sprintf( "{vtable=%p name=%s (%s)}", + info->vtable, info->mangled, info->name ? info->name : "" ); +} + +/* compute the this pointer for a base class of a given type */ +static inline void *get_this_pointer( const this_ptr_offsets *off, void *object ) +{ + if (!object) return NULL; + + if (off->vbase_descr >= 0) + { + int *offset_ptr; + + /* move this ptr to vbase descriptor */ + object = (char *)object + off->vbase_descr; + /* and fetch additional offset from vbase descriptor */ + offset_ptr = (int *)(*(char **)object + off->vbase_offset); + object = (char *)object + *offset_ptr; + } + + object = (char *)object + off->this_offset; + return object; +} + +#ifdef __ASM_USE_THISCALL_WRAPPER +extern void call_copy_ctor( void *func, void *this, void *src, int has_vbase ); +extern void call_dtor( void *func, void *this ); +#else +static inline void call_copy_ctor( void *func, void *this, void *src, int has_vbase ) +{ + if (has_vbase) + ((void (__thiscall*)(void*, void*, BOOL))func)(this, src, 1); + else + ((void (__thiscall*)(void*, void*))func)(this, src); +} +static inline void call_dtor( void *func, void *this ) +{ + ((void (__thiscall*)(void*))func)( this ); +} +#endif + +/* check if the exception type is caught by a given catch block, and return the type that matched */ +static inline const cxx_type_info *find_caught_type( cxx_exception_type *exc_type, uintptr_t base, + const type_info *catch_ti, UINT catch_flags ) +{ + const cxx_type_info_table *type_info_table = rtti_rva( exc_type->type_info_table, base ); + UINT i; + + for (i = 0; i < type_info_table->count; i++) + { + const cxx_type_info *type = rtti_rva( type_info_table->info[i], base ); + const type_info *ti = rtti_rva( type->type_info, base ); + + if (!catch_ti) return type; /* catch(...) matches any type */ + if (catch_ti != ti) + { + if (strcmp( catch_ti->mangled, ti->mangled )) continue; + } + /* type is the same, now check the flags */ + if ((exc_type->flags & TYPE_FLAG_CONST) && + !(catch_flags & TYPE_FLAG_CONST)) continue; + if ((exc_type->flags & TYPE_FLAG_VOLATILE) && + !(catch_flags & TYPE_FLAG_VOLATILE)) continue; + return type; /* it matched */ + } + return NULL; +} + +/* copy the exception object where the catch block wants it */ +static inline void copy_exception( void *object, void **dest, UINT catch_flags, + const cxx_type_info *type, uintptr_t base ) +{ + if (catch_flags & TYPE_FLAG_REFERENCE) + { + *dest = get_this_pointer( &type->offsets, object ); + } + else if (type->flags & CLASS_IS_SIMPLE_TYPE) + { + memmove( dest, object, type->size ); + /* if it is a pointer, adjust it */ + if (type->size == sizeof(void*)) *dest = get_this_pointer( &type->offsets, *dest ); + } + else /* copy the object */ + { + if (type->copy_ctor) + call_copy_ctor( rtti_rva( type->copy_ctor, base ), dest, + get_this_pointer( &type->offsets, object ), + (type->flags & CLASS_HAS_VIRTUAL_BASE_CLASS) ); + else + memmove( dest, get_this_pointer( &type->offsets, object ), type->size ); + } +} + +#define TRACE_EXCEPTION_TYPE(type,base) do { \ + const cxx_type_info_table *table = rtti_rva( type->type_info_table, base ); \ + unsigned int i; \ + TRACE( "flags %x destr %p handler %p type info %p\n", \ + type->flags, rtti_rva( type->destructor, base ), \ + type->custom_handler ? rtti_rva( type->custom_handler, base ) : NULL, table ); \ + for (i = 0; i < table->count; i++) \ + { \ + const cxx_type_info *type = rtti_rva( table->info[i], base ); \ + const type_info *info = rtti_rva( type->type_info, base ); \ + TRACE( " %d: flags %x type %p %s offsets %d,%d,%d size %d copy ctor %p\n", \ + i, type->flags, info, dbgstr_type_info( info ), \ + type->offsets.this_offset, type->offsets.vbase_descr, type->offsets.vbase_offset, \ + type->size, rtti_rva( type->copy_ctor, base )); \ + } \ +} while(0) + +extern void dump_function_descr( const cxx_function_descr *descr, uintptr_t base ); +extern void *find_catch_handler( void *object, uintptr_t frame, uintptr_t exc_base, + const tryblock_info *tryblock, + cxx_exception_type *exc_type, uintptr_t image_base ); +extern int handle_fpieee_flt( __msvcrt_ulong exception_code, EXCEPTION_POINTERS *ep, + int (__cdecl *handler)(_FPIEEE_RECORD*) ); +#ifndef __i386__ +extern void *call_catch_handler( EXCEPTION_RECORD *rec ); +extern void *call_unwind_handler( void *func, uintptr_t frame, DISPATCHER_CONTEXT *dispatch ); +extern ULONG_PTR get_exception_pc( DISPATCHER_CONTEXT *dispatch ); +#endif + +#if _MSVCR_VER >= 80 +#define EXCEPTION_MANGLED_NAME ".?AVexception@std@@" +#else +#define EXCEPTION_MANGLED_NAME ".?AVexception@@" +#endif + +#define CREATE_EXCEPTION_OBJECT(exception_name) \ +static exception* __exception_ctor(exception *this, const char *str, const vtable_ptr *vtbl) \ +{ \ + if (str) \ + { \ + unsigned int len = strlen(str) + 1; \ + this->name = malloc(len); \ + memcpy(this->name, str, len); \ + this->do_free = TRUE; \ + } \ + else \ + { \ + this->name = NULL; \ + this->do_free = FALSE; \ + } \ + this->vtable = vtbl; \ + return this; \ +} \ +\ +static exception* __exception_copy_ctor(exception *this, const exception *rhs, const vtable_ptr *vtbl) \ +{ \ + if (rhs->do_free) \ + { \ + __exception_ctor(this, rhs->name, vtbl); \ + } \ + else \ + { \ + *this = *rhs; \ + this->vtable = vtbl; \ + } \ + return this; \ +} \ +extern const vtable_ptr exception_name ## _vtable; \ +DEFINE_THISCALL_WRAPPER(exception_name ## _copy_ctor,8) \ +exception* __thiscall exception_name ## _copy_ctor(exception *this, const exception *rhs) \ +{ \ + return __exception_copy_ctor(this, rhs, & exception_name ## _vtable); \ +} \ +\ +DEFINE_THISCALL_WRAPPER(exception_name ## _dtor,4) \ +void __thiscall exception_name ## _dtor(exception *this) \ +{ \ + if (this->do_free) free(this->name); \ +} \ +\ +DEFINE_THISCALL_WRAPPER(exception_name ## _vector_dtor,8) \ +void* __thiscall exception_name ## _vector_dtor(exception *this, unsigned int flags) \ +{ \ + if (flags & 2) \ + { \ + INT_PTR i, *ptr = (INT_PTR *)this - 1; \ +\ + for (i = *ptr - 1; i >= 0; i--) exception_name ## _dtor(this + i); \ + operator_delete(ptr); \ + } \ + else \ + { \ + exception_name ## _dtor(this); \ + if (flags & 1) operator_delete(this); \ + } \ + return this; \ +} \ +\ +DEFINE_THISCALL_WRAPPER(exception_name ## _what,4) \ +const char* __thiscall exception_name ## _what(exception *this) \ +{ \ + return this->name ? this->name : "Unknown exception"; \ +} \ +\ +__ASM_BLOCK_BEGIN(exception_name ## _vtables) \ +__ASM_VTABLE(exception_name, \ + VTABLE_ADD_FUNC(exception_name ## _vector_dtor) \ + VTABLE_ADD_FUNC(exception_name ## _what)); \ +__ASM_BLOCK_END \ +\ +DEFINE_RTTI_DATA0(exception_name, 0, EXCEPTION_MANGLED_NAME) + +#endif /* __MSVCRT_CPPEXCEPT_H */ diff --git a/dll/win32/msvcrt/crt_gccmain.c b/dll/win32/msvcrt/crt_gccmain.c new file mode 100644 index 00000000000..88ca2874732 --- /dev/null +++ b/dll/win32/msvcrt/crt_gccmain.c @@ -0,0 +1,31 @@ +/* + * __main entry point + * + * Copyright 2019 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#if 0 +#pragma makedep implib +#endif + +#ifdef __WINE_PE_BUILD + +/* mingw compilers emit call to __main() when main() function is defined. + * it's used by crt to call global constructors and register global destructors. */ +void __cdecl __main(void) {} + +#endif diff --git a/dll/win32/msvcrt/crt_main.c b/dll/win32/msvcrt/crt_main.c new file mode 100644 index 00000000000..eb785e30bb0 --- /dev/null +++ b/dll/win32/msvcrt/crt_main.c @@ -0,0 +1,62 @@ +/* + * mainCRTStartup default entry point + * + * Copyright 2019 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#if 0 +#pragma makedep implib +#endif + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winternl.h" + +int __cdecl main(int argc, char **argv, char **env); + +static const IMAGE_NT_HEADERS *get_nt_header( void ) +{ + IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)NtCurrentTeb()->Peb->ImageBaseAddress; + return (const IMAGE_NT_HEADERS *)((char *)dos + dos->e_lfanew); +} + +int __cdecl mainCRTStartup(void) +{ + int argc, ret; + char **argv, **env; + +#ifdef _UCRT + _configure_narrow_argv(_crt_argv_unexpanded_arguments); + _initialize_narrow_environment(); + argc = *__p___argc(); + argv = *__p___argv(); + env = _get_initial_narrow_environment(); +#else + int new_mode = 0; + __getmainargs(&argc, &argv, &env, 0, &new_mode); +#endif + _set_app_type(get_nt_header()->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI ? _crt_gui_app : _crt_console_app); + + ret = main(argc, argv, env); + + exit(ret); + return ret; +} diff --git a/dll/win32/msvcrt/crt_winmain.c b/dll/win32/msvcrt/crt_winmain.c new file mode 100644 index 00000000000..fda3d12f688 --- /dev/null +++ b/dll/win32/msvcrt/crt_winmain.c @@ -0,0 +1,54 @@ +/* + * main default entry point for exe files + * + * Copyright 2005 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#if 0 +#pragma makedep implib +#endif + +#include +#include "windef.h" +#include "winbase.h" +#include "winuser.h" + +int __cdecl main( int argc, char *argv[] ) +{ + STARTUPINFOA info; + char *cmdline = GetCommandLineA(); + int bcount = 0; + BOOL in_quotes = FALSE; + + while (*cmdline) + { + if ((*cmdline == '\t' || *cmdline == ' ') && !in_quotes) break; + else if (*cmdline == '\\') bcount++; + else if (*cmdline == '\"') + { + if (!(bcount & 1)) in_quotes = !in_quotes; + bcount = 0; + } + else bcount = 0; + cmdline++; + } + while (*cmdline == '\t' || *cmdline == ' ') cmdline++; + + GetStartupInfoA( &info ); + if (!(info.dwFlags & STARTF_USESHOWWINDOW)) info.wShowWindow = SW_SHOWNORMAL; + return WinMain( GetModuleHandleA(0), 0, cmdline, info.wShowWindow ); +} diff --git a/dll/win32/msvcrt/crt_wmain.c b/dll/win32/msvcrt/crt_wmain.c new file mode 100644 index 00000000000..79755d1a6e9 --- /dev/null +++ b/dll/win32/msvcrt/crt_wmain.c @@ -0,0 +1,62 @@ +/* + * wmainCRTStartup default entry point + * + * Copyright 2019 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#if 0 +#pragma makedep implib +#endif + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winternl.h" + +int __cdecl wmain(int argc, WCHAR **argv, WCHAR **env); + +static const IMAGE_NT_HEADERS *get_nt_header( void ) +{ + IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)NtCurrentTeb()->Peb->ImageBaseAddress; + return (const IMAGE_NT_HEADERS *)((char *)dos + dos->e_lfanew); +} + +int __cdecl wmainCRTStartup(void) +{ + int argc, ret; + WCHAR **argv, **env; + +#ifdef _UCRT + _configure_wide_argv(_crt_argv_unexpanded_arguments); + _initialize_wide_environment(); + argc = *__p___argc(); + argv = *__p___wargv(); + env = _get_initial_wide_environment(); +#else + int new_mode = 0; + __wgetmainargs(&argc, &argv, &env, 0, &new_mode); +#endif + _set_app_type(get_nt_header()->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI ? _crt_gui_app : _crt_console_app); + + ret = wmain(argc, argv, env); + + exit(ret); + return ret; +} diff --git a/dll/win32/msvcrt/crt_wwinmain.c b/dll/win32/msvcrt/crt_wwinmain.c new file mode 100644 index 00000000000..b50f728025b --- /dev/null +++ b/dll/win32/msvcrt/crt_wwinmain.c @@ -0,0 +1,56 @@ +/* + * main default entry point for Unicode exe files + * + * Copyright 2005 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#if 0 +#pragma makedep implib +#endif + +#include +#include "windef.h" +#include "winbase.h" +#include "winuser.h" + +int WINAPI wWinMain(HINSTANCE,HINSTANCE,LPWSTR,int); + +int __cdecl wmain( int argc, WCHAR *argv[] ) +{ + STARTUPINFOW info; + WCHAR *cmdline = GetCommandLineW(); + int bcount = 0; + BOOL in_quotes = FALSE; + + while (*cmdline) + { + if ((*cmdline == '\t' || *cmdline == ' ') && !in_quotes) break; + else if (*cmdline == '\\') bcount++; + else if (*cmdline == '\"') + { + if (!(bcount & 1)) in_quotes = !in_quotes; + bcount = 0; + } + else bcount = 0; + cmdline++; + } + while (*cmdline == '\t' || *cmdline == ' ') cmdline++; + + GetStartupInfoW( &info ); + if (!(info.dwFlags & STARTF_USESHOWWINDOW)) info.wShowWindow = SW_SHOWNORMAL; + return wWinMain( GetModuleHandleW(0), 0, cmdline, info.wShowWindow ); +} diff --git a/dll/win32/msvcrt/ctype.c b/dll/win32/msvcrt/ctype.c new file mode 100644 index 00000000000..1297bc56046 --- /dev/null +++ b/dll/win32/msvcrt/ctype.c @@ -0,0 +1,617 @@ +/* + * msvcrt.dll ctype functions + * + * Copyright 2000 Jon Griffiths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include +#include "msvcrt.h" +#include "winnls.h" + +/* Some abbreviations to make the following table readable */ +#define _C_ _CONTROL +#define _S_ _SPACE +#define _P_ _PUNCT +#define _D_ _DIGIT +#define _H_ _HEX +#define _U_ _UPPER +#define _L_ _LOWER + +WORD MSVCRT__ctype [257] = { + 0, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _S_|_C_, _S_|_C_, + _S_|_C_, _S_|_C_, _S_|_C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, + _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _S_|_BLANK, + _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, + _P_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, + _D_|_H_, _D_|_H_, _D_|_H_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _U_|_H_, + _U_|_H_, _U_|_H_, _U_|_H_, _U_|_H_, _U_|_H_, _U_, _U_, _U_, _U_, _U_, + _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, + _U_, _P_, _P_, _P_, _P_, _P_, _P_, _L_|_H_, _L_|_H_, _L_|_H_, _L_|_H_, + _L_|_H_, _L_|_H_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, + _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _P_, _P_, _P_, _P_, + _C_, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +#if _MSVCR_VER <= 110 +# define B110 _BLANK +#else +# define B110 0 +#endif + +#if _MSVCR_VER == 120 +# define D120 0 +#else +# define D120 4 +#endif + +#if _MSVCR_VER >= 140 +# define S140 _SPACE +# define L140 _LOWER | 0x100 +# define C140 _CONTROL +#else +# define S140 0 +# define L140 0 +# define C140 0 +#endif +WORD MSVCRT__wctype[257] = +{ + 0, + /* 00 */ + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0028 | B110, 0x0028, 0x0028, 0x0028, 0x0028, 0x0020, 0x0020, + /* 10 */ + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + /* 20 */ + 0x0048, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, + 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, + /* 30 */ + 0x0084, 0x0084, 0x0084, 0x0084, 0x0084, 0x0084, 0x0084, 0x0084, + 0x0084, 0x0084, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, + /* 40 */ + 0x0010, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + /* 50 */ + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, + /* 60 */ + 0x0010, 0x0182, 0x0182, 0x0182, 0x0182, 0x0182, 0x0182, 0x0102, + 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, + /* 70 */ + 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, + 0x0102, 0x0102, 0x0102, 0x0010, 0x0010, 0x0010, 0x0010, 0x0020, + /* 80 */ + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020 | S140, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + /* 90 */ + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + /* a0 */ + 0x0008 | B110, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, + 0x0010, 0x0010, 0x0010 | L140, 0x0010, 0x0010, 0x0010 | C140, 0x0010, 0x0010, + /* b0 */ + 0x0010, 0x0010, 0x0010 | D120, 0x0010 | D120, 0x0010, 0x0010 | L140, 0x0010, 0x0010, + 0x0010, 0x0010 | D120, 0x0010 | L140, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, + /* c0 */ + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + /* d0 */ + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0010, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0102, + /* e0 */ + 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, + 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, + /* f0 */ + 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0010, + 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102 +}; + +WORD *MSVCRT__pwctype = MSVCRT__wctype + 1; + +/********************************************************************* + * __p__pctype (MSVCRT.@) + */ +unsigned short** CDECL __p__pctype(void) +{ + return &get_locinfo()->pctype; +} + +/********************************************************************* + * __pctype_func (MSVCRT.@) + */ +const unsigned short* CDECL __pctype_func(void) +{ + return get_locinfo()->pctype; +} + +/********************************************************************* + * __p__pwctype (MSVCRT.@) + */ +unsigned short** CDECL __p__pwctype(void) +{ + return &MSVCRT__pwctype; +} + +/********************************************************************* + * __pwctype_func (MSVCRT.@) + */ +const unsigned short* CDECL __pwctype_func(void) +{ + return MSVCRT__pwctype; +} + +/********************************************************************* + * _isctype_l (MSVCRT.@) + */ +int CDECL _isctype_l(int c, int type, _locale_t locale) +{ + pthreadlocinfo locinfo; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if (c >= -1 && c <= 255) + return locinfo->pctype[c] & type; + + if (locinfo->mb_cur_max != 1 && c > 0) + { + /* FIXME: Is there a faster way to do this? */ + WORD typeInfo; + char convert[3], *pconv = convert; + + if (locinfo->pctype[(UINT)c >> 8] & _LEADBYTE) + *pconv++ = (UINT)c >> 8; + *pconv++ = c & 0xff; + *pconv = 0; + + if (GetStringTypeExA(locinfo->lc_handle[LC_CTYPE], + CT_CTYPE1, convert, convert[1] ? 2 : 1, &typeInfo)) + return typeInfo & type; + } + return 0; +} + +/********************************************************************* + * _isctype (MSVCRT.@) + */ +int CDECL _isctype(int c, int type) +{ + return _isctype_l(c, type, NULL); +} + +/********************************************************************* + * _isalnum_l (MSVCRT.@) + */ +int CDECL _isalnum_l(int c, _locale_t locale) +{ + return _isctype_l( c, _ALPHA | _DIGIT, locale ); +} + +/********************************************************************* + * isalnum (MSVCRT.@) + */ +int CDECL isalnum(int c) +{ + return _isctype( c, _ALPHA | _DIGIT ); +} + +/********************************************************************* + * _isalpha_l (MSVCRT.@) + */ +int CDECL _isalpha_l(int c, _locale_t locale) +{ + return _isctype_l( c, _ALPHA, locale ); +} + +/********************************************************************* + * isalpha (MSVCRT.@) + */ +int CDECL isalpha(int c) +{ + return _isctype( c, _ALPHA ); +} + +/********************************************************************* + * _iscntrl_l (MSVCRT.@) + */ +int CDECL _iscntrl_l(int c, _locale_t locale) +{ + return _isctype_l( c, _CONTROL, locale ); +} + +/********************************************************************* + * iscntrl (MSVCRT.@) + */ +int CDECL iscntrl(int c) +{ + return _isctype( c, _CONTROL ); +} + +/********************************************************************* + * _isdigit_l (MSVCRT.@) + */ +int CDECL _isdigit_l(int c, _locale_t locale) +{ + return _isctype_l( c, _DIGIT, locale ); +} + +/********************************************************************* + * isdigit (MSVCRT.@) + */ +int CDECL isdigit(int c) +{ + return _isctype( c, _DIGIT ); +} + +/********************************************************************* + * _isgraph_l (MSVCRT.@) + */ +int CDECL _isgraph_l(int c, _locale_t locale) +{ + return _isctype_l( c, _ALPHA | _DIGIT | _PUNCT, locale ); +} + +/********************************************************************* + * isgraph (MSVCRT.@) + */ +int CDECL isgraph(int c) +{ + return _isctype( c, _ALPHA | _DIGIT | _PUNCT ); +} + +/********************************************************************* + * _isleadbyte_l (MSVCRT.@) + */ +int CDECL _isleadbyte_l(int c, _locale_t locale) +{ + return _isctype_l( c, _LEADBYTE, locale ); +} + +/********************************************************************* + * isleadbyte (MSVCRT.@) + */ +int CDECL isleadbyte(int c) +{ + return _isctype( c, _LEADBYTE ); +} + +/********************************************************************* + * _islower_l (MSVCRT.@) + */ +int CDECL _islower_l(int c, _locale_t locale) +{ + return _isctype_l( c, _LOWER, locale ); +} + +/********************************************************************* + * islower (MSVCRT.@) + */ +int CDECL islower(int c) +{ + return _isctype( c, _LOWER ); +} + +/********************************************************************* + * _isprint_l (MSVCRT.@) + */ +int CDECL _isprint_l(int c, _locale_t locale) +{ + return _isctype_l( c, _ALPHA | _DIGIT | _BLANK | _PUNCT, locale ); +} + +/********************************************************************* + * isprint (MSVCRT.@) + */ +int CDECL isprint(int c) +{ + return _isctype( c, _ALPHA | _DIGIT | _BLANK | _PUNCT ); +} + +/********************************************************************* + * ispunct (MSVCRT.@) + */ +int CDECL ispunct(int c) +{ + return _isctype( c, _PUNCT ); +} + +/********************************************************************* + * _ispunct_l (MSVCR80.@) + */ +int CDECL _ispunct_l(int c, _locale_t locale) +{ + return _isctype_l( c, _PUNCT, locale ); +} + +/********************************************************************* + * _isspace_l (MSVCRT.@) + */ +int CDECL _isspace_l(int c, _locale_t locale) +{ + return _isctype_l( c, _SPACE, locale ); +} + +/********************************************************************* + * isspace (MSVCRT.@) + */ +int CDECL isspace(int c) +{ + return _isctype( c, _SPACE ); +} + +/********************************************************************* + * _isupper_l (MSVCRT.@) + */ +int CDECL _isupper_l(int c, _locale_t locale) +{ + return _isctype_l( c, _UPPER, locale ); +} + +/********************************************************************* + * isupper (MSVCRT.@) + */ +int CDECL isupper(int c) +{ + return _isctype( c, _UPPER ); +} + +/********************************************************************* + * _isxdigit_l (MSVCRT.@) + */ +int CDECL _isxdigit_l(int c, _locale_t locale) +{ + return _isctype_l( c, _HEX, locale ); +} + +/********************************************************************* + * isxdigit (MSVCRT.@) + */ +int CDECL isxdigit(int c) +{ + return _isctype( c, _HEX ); +} + +/********************************************************************* + * _isblank_l (MSVCRT.@) + */ +int CDECL _isblank_l(int c, _locale_t locale) +{ +#if _MSVCR_VER < 140 + if (c == '\t') return _BLANK; +#endif + return _isctype_l( c, _BLANK, locale ); +} + +/********************************************************************* + * isblank (MSVCRT.@) + */ +int CDECL isblank(int c) +{ + return c == '\t' || _isctype( c, _BLANK ); +} + +/********************************************************************* + * __isascii (MSVCRT.@) + */ +int CDECL __isascii(int c) +{ + return ((unsigned)c < 0x80); +} + +/********************************************************************* + * __toascii (MSVCRT.@) + */ +int CDECL __toascii(int c) +{ + return (unsigned)c & 0x7f; +} + +/********************************************************************* + * iswascii (MSVCRT.@) + * + */ +int CDECL iswascii(wchar_t c) +{ + return ((unsigned)c < 0x80); +} + +/********************************************************************* + * __iscsym (MSVCRT.@) + */ +int CDECL __iscsym(int c) +{ + return (c < 127 && (isalnum(c) || c == '_')); +} + +/********************************************************************* + * __iscsymf (MSVCRT.@) + */ +int CDECL __iscsymf(int c) +{ + return (c < 127 && (isalpha(c) || c == '_')); +} + +/********************************************************************* + * __iswcsym (MSVCRT.@) + */ +int CDECL __iswcsym(wint_t c) +{ + return (iswalnum(c) || c == '_'); +} + +/********************************************************************* + * __iswcsymf (MSVCRT.@) + */ +int CDECL __iswcsymf(wint_t c) +{ + return (iswalpha(c) || c == '_'); +} + +/********************************************************************* + * _toupper_l (MSVCRT.@) + */ +int CDECL _toupper_l(int c, _locale_t locale) +{ + pthreadlocinfo locinfo; + unsigned char str[2], *p = str, ret[2]; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if((unsigned)c < 256) + { + if(locinfo->pctype[c] & _LEADBYTE) + return c; + return locinfo->pcumap[c]; + } + + if(locinfo->pctype[(c>>8)&255] & _LEADBYTE) + *p++ = (c>>8) & 255; + else { + *_errno() = EILSEQ; + str[1] = 0; + } + *p++ = c & 255; + + switch(__crtLCMapStringA(locinfo->lc_handle[LC_CTYPE], LCMAP_UPPERCASE, + (char*)str, p-str, (char*)ret, 2, locinfo->lc_codepage, 0)) + { + case 0: + return c; + case 1: + return ret[0]; + default: + return ret[0] + (ret[1]<<8); + } +} + +/********************************************************************* + * toupper (MSVCRT.@) + */ +int CDECL toupper(int c) +{ + if(initial_locale) + return c>='a' && c<='z' ? c-'a'+'A' : c; + return _toupper_l(c, NULL); +} + +/********************************************************************* + * _toupper (MSVCRT.@) + */ +int CDECL _toupper(int c) +{ + return c - 0x20; /* sic */ +} + +/********************************************************************* + * _tolower_l (MSVCRT.@) + */ +int CDECL _tolower_l(int c, _locale_t locale) +{ + pthreadlocinfo locinfo; + unsigned char str[2], *p = str, ret[2]; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if((unsigned)c < 256) + { + if(locinfo->pctype[c] & _LEADBYTE) + return c; + return locinfo->pclmap[c]; + } + + if(locinfo->pctype[(c>>8)&255] & _LEADBYTE) + *p++ = (c>>8) & 255; + else { + *_errno() = EILSEQ; + str[1] = 0; + } + *p++ = c & 255; + + switch(__crtLCMapStringA(locinfo->lc_handle[LC_CTYPE], LCMAP_LOWERCASE, + (char*)str, p-str, (char*)ret, 2, locinfo->lc_codepage, 0)) + { + case 0: + return c; + case 1: + return ret[0]; + default: + return ret[0] + (ret[1]<<8); + } +} + +/********************************************************************* + * tolower (MSVCRT.@) + */ +int CDECL tolower(int c) +{ + if(initial_locale) + return c>='A' && c<='Z' ? c-'A'+'a' : c; + return _tolower_l(c, NULL); +} + +/********************************************************************* + * _tolower (MSVCRT.@) + */ +int CDECL _tolower(int c) +{ + return c + 0x20; /* sic */ +} + +#if _MSVCR_VER>=120 +/********************************************************************* + * wctype (MSVCR120.@) + */ +unsigned short __cdecl wctype(const char *property) +{ + static const struct { + const char *name; + unsigned short mask; + } properties[] = { + { "alnum", _DIGIT|_ALPHA }, + { "alpha", _ALPHA }, + { "cntrl", _CONTROL }, + { "digit", _DIGIT }, + { "graph", _DIGIT|_PUNCT|_ALPHA }, + { "lower", _LOWER }, + { "print", _DIGIT|_PUNCT|_BLANK|_ALPHA }, + { "punct", _PUNCT }, + { "space", _SPACE }, + { "upper", _UPPER }, + { "xdigit", _HEX } + }; + unsigned int i; + + for(i=0; i= 0; i--) free(_this[i].name); \ + free(ptr); \ + } \ + else \ + { \ + free(_this->name); \ + if (flags & 1) free(_this); \ + } \ + return _this; \ +} \ +\ +DEFINE_RTTI_DATA0( type_info, 0, ".?AVtype_info@@" ) \ +\ +__ASM_BLOCK_BEGIN(type_info_vtables) \ + __ASM_VTABLE(type_info, \ + VTABLE_ADD_FUNC(type_info_vector_dtor)); \ +__ASM_BLOCK_END diff --git a/dll/win32/msvcrt/data.c b/dll/win32/msvcrt/data.c new file mode 100644 index 00000000000..6e764e52eae --- /dev/null +++ b/dll/win32/msvcrt/data.c @@ -0,0 +1,735 @@ +/* + * msvcrt.dll dll data items + * + * Copyright 2000 Jon Griffiths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include +#include +#include "msvcrt.h" +#include +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +static WCHAR **initial_wargv; +static int initial_argc; +int MSVCRT___argc = 0; +static int wargc_expand; +unsigned int MSVCRT__commode = 0; +int MSVCRT__fmode = 0; +unsigned int MSVCRT__osver = 0; +unsigned int MSVCRT__osplatform = 0; +unsigned int MSVCRT__winmajor = 0; +unsigned int MSVCRT__winminor = 0; +unsigned int MSVCRT__winver = 0; +#ifdef _CRTDLL +unsigned int CRTDLL__basemajor_dll = 0; +unsigned int CRTDLL__baseminor_dll = 0; +unsigned int CRTDLL__baseversion_dll = 0; +unsigned int CRTDLL__cpumode_dll = 1; +unsigned int CRTDLL__osmode_dll = 1; +#endif +unsigned int MSVCRT___setlc_active = 0; +unsigned int MSVCRT___unguarded_readlc_active = 0; +double MSVCRT__HUGE = 0; +char **MSVCRT___argv = NULL; +wchar_t **MSVCRT___wargv = NULL; +static wchar_t **wargv_expand; +char *MSVCRT__acmdln = NULL; +wchar_t *MSVCRT__wcmdln = NULL; +char **MSVCRT__environ = NULL; +wchar_t **MSVCRT__wenviron = NULL; +char **MSVCRT___initenv = NULL; +wchar_t **MSVCRT___winitenv = NULL; +int MSVCRT_app_type = 0; +char* MSVCRT__pgmptr = NULL; +WCHAR* MSVCRT__wpgmptr = NULL; + +static char **build_argv( WCHAR **wargv ) +{ + int argc; + char *p, **argv; + DWORD total = 0; + + for (argc = 0; wargv[argc]; argc++) + total += WideCharToMultiByte( CP_ACP, 0, wargv[argc], -1, NULL, 0, NULL, NULL ); + + argv = HeapAlloc( GetProcessHeap(), 0, total + (argc + 1) * sizeof(*argv) ); + p = (char *)(argv + argc + 1); + for (argc = 0; wargv[argc]; argc++) + { + DWORD reslen = WideCharToMultiByte( CP_ACP, 0, wargv[argc], -1, p, total, NULL, NULL ); + argv[argc] = p; + p += reslen; + total -= reslen; + } + argv[argc] = NULL; + return argv; +} + +static WCHAR **cmdline_to_argv( const WCHAR *src, int *ret_argc ) +{ + WCHAR **argv, *arg, *dst; + int argc, in_quotes = 0, bcount = 0, len = wcslen(src) + 1; + + argc = 2 + len / 2; + argv = HeapAlloc( GetProcessHeap(), 0, argc * sizeof(*argv) + len * sizeof(WCHAR) ); + arg = dst = (WCHAR *)(argv + argc); + argc = 0; + while (*src) + { + if ((*src == ' ' || *src == '\t') && !in_quotes) + { + /* skip the remaining spaces */ + while (*src == ' ' || *src == '\t') src++; + if (!*src) break; + /* close the argument and copy it */ + *dst++ = 0; + argv[argc++] = arg; + /* start with a new argument */ + arg = dst; + bcount = 0; + } + else if (*src == '\\') + { + *dst++ = *src++; + bcount++; + } + else if (*src == '"') + { + if ((bcount & 1) == 0) + { + /* Preceded by an even number of '\', this is half that + * number of '\', plus a '"' which we discard. + */ + dst -= bcount / 2; + src++; + if (in_quotes && *src == '"') *dst++ = *src++; + else in_quotes = !in_quotes; + } + else + { + /* Preceded by an odd number of '\', this is half that + * number of '\' followed by a '"' + */ + dst -= bcount / 2 + 1; + *dst++ = *src++; + } + bcount = 0; + } + else /* a regular character */ + { + *dst++ = *src++; + bcount = 0; + } + } + *dst = 0; + argv[argc++] = arg; + argv[argc] = NULL; + *ret_argc = argc; + return argv; +} + +typedef void (CDECL *_INITTERMFUN)(void); +typedef int (CDECL *_INITTERM_E_FN)(void); + +/*********************************************************************** + * __p___argc (MSVCRT.@) + */ +int* CDECL __p___argc(void) { return &MSVCRT___argc; } + +/*********************************************************************** + * __p__commode (MSVCRT.@) + */ +unsigned int* CDECL __p__commode(void) { return &MSVCRT__commode; } + + +/*********************************************************************** + * __p__pgmptr (MSVCRT.@) + */ +char** CDECL __p__pgmptr(void) { return &MSVCRT__pgmptr; } + +/*********************************************************************** + * __p__wpgmptr (MSVCRT.@) + */ +WCHAR** CDECL __p__wpgmptr(void) { return &MSVCRT__wpgmptr; } + +/*********************************************************************** + * _get_pgmptr (MSVCRT.@) + */ +int CDECL _get_pgmptr(char** p) +{ + if (!MSVCRT_CHECK_PMT(p)) return EINVAL; + + *p = MSVCRT__pgmptr; + return 0; +} + +/*********************************************************************** + * _get_wpgmptr (MSVCRT.@) + */ +int CDECL _get_wpgmptr(WCHAR** p) +{ + if (!MSVCRT_CHECK_PMT(p)) return EINVAL; + *p = MSVCRT__wpgmptr; + return 0; +} + +/*********************************************************************** + * __p__fmode (MSVCRT.@) + */ +int* CDECL __p__fmode(void) { return &MSVCRT__fmode; } + +/*********************************************************************** + * _set_fmode (MSVCRT.@) + */ +int CDECL _set_fmode(int mode) +{ + /* TODO: support _O_WTEXT */ + if(!MSVCRT_CHECK_PMT(mode==_O_TEXT || mode==_O_BINARY)) + return EINVAL; + + MSVCRT__fmode = mode; + return 0; +} + +/*********************************************************************** + * _get_fmode (MSVCRT.@) + */ +int CDECL _get_fmode(int *mode) +{ + if(!MSVCRT_CHECK_PMT(mode)) + return EINVAL; + + *mode = MSVCRT__fmode; + return 0; +} + +/*********************************************************************** + * __p__osver (MSVCRT.@) + */ +unsigned int* CDECL __p__osver(void) { return &MSVCRT__osver; } + +/*********************************************************************** + * __p__winmajor (MSVCRT.@) + */ +unsigned int* CDECL __p__winmajor(void) { return &MSVCRT__winmajor; } + +/*********************************************************************** + * __p__winminor (MSVCRT.@) + */ +unsigned int* CDECL __p__winminor(void) { return &MSVCRT__winminor; } + +/*********************************************************************** + * __p__winver (MSVCRT.@) + */ +unsigned int* CDECL __p__winver(void) { return &MSVCRT__winver; } + +/********************************************************************* + * __p__acmdln (MSVCRT.@) + */ +char** CDECL __p__acmdln(void) { return &MSVCRT__acmdln; } + +/********************************************************************* + * __p__wcmdln (MSVCRT.@) + */ +wchar_t** CDECL __p__wcmdln(void) { return &MSVCRT__wcmdln; } + +/********************************************************************* + * __p___argv (MSVCRT.@) + */ +char*** CDECL __p___argv(void) { return &MSVCRT___argv; } + +/********************************************************************* + * __p___wargv (MSVCRT.@) + */ +wchar_t*** CDECL __p___wargv(void) { return &MSVCRT___wargv; } + +/********************************************************************* + * __p__environ (MSVCRT.@) + */ +char*** CDECL __p__environ(void) +{ + return &MSVCRT__environ; +} + +/********************************************************************* + * __p__wenviron (MSVCRT.@) + */ +wchar_t*** CDECL __p__wenviron(void) +{ + return &MSVCRT__wenviron; +} + +/********************************************************************* + * __p___initenv (MSVCRT.@) + */ +char*** CDECL __p___initenv(void) { return &MSVCRT___initenv; } + +/********************************************************************* + * __p___winitenv (MSVCRT.@) + */ +wchar_t*** CDECL __p___winitenv(void) { return &MSVCRT___winitenv; } + +/********************************************************************* + * _get_osplatform (MSVCRT.@) + */ +int CDECL _get_osplatform(int *pValue) +{ + if (!MSVCRT_CHECK_PMT(pValue != NULL)) return EINVAL; + *pValue = MSVCRT__osplatform; + return 0; +} + +/* INTERNAL: Create a wide string from an ascii string */ +wchar_t *msvcrt_wstrdupa(const char *str) +{ + const unsigned int len = strlen(str) + 1 ; + wchar_t *wstr = malloc(len* sizeof (wchar_t)); + if (!wstr) + return NULL; + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,str,len,wstr,len); + return wstr; +} + +/********************************************************************* + * ___unguarded_readlc_active_add_func (MSVCRT.@) + */ +unsigned int * CDECL ___unguarded_readlc_active_add_func(void) +{ + return &MSVCRT___unguarded_readlc_active; +} + +/********************************************************************* + * ___setlc_active_func (MSVCRT.@) + */ +unsigned int CDECL ___setlc_active_func(void) +{ + return MSVCRT___setlc_active; +} + +/* INTERNAL: Since we can't rely on Winelib startup code calling w/getmainargs, + * we initialise data values during DLL loading. When called by a native + * program we simply return the data we've already initialised. This also means + * you can call multiple times without leaking + */ +void msvcrt_init_args(void) +{ + OSVERSIONINFOW osvi; + + MSVCRT__acmdln = _strdup( GetCommandLineA() ); + MSVCRT__wcmdln = _wcsdup( GetCommandLineW() ); + initial_wargv = cmdline_to_argv( GetCommandLineW(), &initial_argc ); + MSVCRT___argc = initial_argc; + MSVCRT___wargv = initial_wargv; + MSVCRT___argv = build_argv( initial_wargv ); + + TRACE("got %s, wide = %s argc=%d\n", debugstr_a(MSVCRT__acmdln), + debugstr_w(MSVCRT__wcmdln),MSVCRT___argc); + + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); + GetVersionExW( &osvi ); + MSVCRT__winver = (osvi.dwMajorVersion << 8) | osvi.dwMinorVersion; + MSVCRT__winmajor = osvi.dwMajorVersion; + MSVCRT__winminor = osvi.dwMinorVersion; + MSVCRT__osver = osvi.dwBuildNumber; + MSVCRT__osplatform = osvi.dwPlatformId; + TRACE( "winver %08x winmajor %08x winminor %08x osver %08x\n", + MSVCRT__winver, MSVCRT__winmajor, MSVCRT__winminor, MSVCRT__osver); +#ifdef _CRTDLL + CRTDLL__baseversion_dll = (GetVersion() >> 16); + CRTDLL__basemajor_dll = CRTDLL__baseversion_dll >> 8; + CRTDLL__baseminor_dll = CRTDLL__baseversion_dll & 0xff; +#endif + + MSVCRT__HUGE = HUGE_VAL; + MSVCRT___setlc_active = 0; + MSVCRT___unguarded_readlc_active = 0; + MSVCRT__fmode = _O_TEXT; + + env_init(FALSE, FALSE); + + MSVCRT__pgmptr = HeapAlloc(GetProcessHeap(), 0, MAX_PATH); + if (MSVCRT__pgmptr) + { + if (!GetModuleFileNameA(0, MSVCRT__pgmptr, MAX_PATH)) + MSVCRT__pgmptr[0] = '\0'; + else + MSVCRT__pgmptr[MAX_PATH - 1] = '\0'; + } + + MSVCRT__wpgmptr = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR)); + if (MSVCRT__wpgmptr) + { + if (!GetModuleFileNameW(0, MSVCRT__wpgmptr, MAX_PATH)) + MSVCRT__wpgmptr[0] = '\0'; + else + MSVCRT__wpgmptr[MAX_PATH - 1] = '\0'; + } +} + +/* INTERNAL: free memory used by args */ +void msvcrt_free_args(void) +{ + /* FIXME: more things to free */ + HeapFree(GetProcessHeap(), 0, MSVCRT___argv); + HeapFree(GetProcessHeap(), 0, MSVCRT__pgmptr); + HeapFree(GetProcessHeap(), 0, MSVCRT__wpgmptr); + HeapFree(GetProcessHeap(), 0, wargv_expand); +} + +static int build_expanded_wargv(int *argc, wchar_t **argv) +{ + int i, size=0, args_no=0, path_len; + BOOL is_expandable; + HANDLE h; + + args_no = 0; + for(i=0; i < initial_argc; i++) { + WIN32_FIND_DATAW data; + int len = 0; + + is_expandable = FALSE; + for(path_len = wcslen(initial_wargv[i])-1; path_len>=0; path_len--) { + if(initial_wargv[i][path_len]=='*' || initial_wargv[i][path_len]=='?') + is_expandable = TRUE; + else if(initial_wargv[i][path_len]=='\\' || initial_wargv[i][path_len]=='/') + break; + } + path_len++; + + if(is_expandable) + h = FindFirstFileW(initial_wargv[i], &data); + else + h = INVALID_HANDLE_VALUE; + + if(h != INVALID_HANDLE_VALUE) { + do { + if(data.cFileName[0]=='.' && (data.cFileName[1]=='\0' || + (data.cFileName[1]=='.' && data.cFileName[2]=='\0'))) + continue; + + len = wcslen(data.cFileName)+1; + if(argv) { + argv[args_no] = (wchar_t*)(argv+*argc+1)+size; + memcpy(argv[args_no], initial_wargv[i], path_len*sizeof(wchar_t)); + memcpy(argv[args_no]+path_len, data.cFileName, len*sizeof(wchar_t)); + } + args_no++; + size += len+path_len; + }while(FindNextFileW(h, &data)); + FindClose(h); + } + + if(!len) { + len = wcslen(initial_wargv[i])+1; + if(argv) { + argv[args_no] = (wchar_t*)(argv+*argc+1)+size; + memcpy(argv[args_no], initial_wargv[i], len*sizeof(wchar_t)); + } + args_no++; + size += len; + } + } + + if(argv) + argv[args_no] = NULL; + size *= sizeof(wchar_t); + size += (args_no+1)*sizeof(wchar_t*); + *argc = args_no; + return size; +} + +/********************************************************************* + * __wgetmainargs (MSVCRT.@) + */ +int CDECL __wgetmainargs(int *argc, wchar_t** *wargv, wchar_t** *wenvp, + int expand_wildcards, int *new_mode) +{ + TRACE("(%p,%p,%p,%d,%p).\n", argc, wargv, wenvp, expand_wildcards, new_mode); + + if (expand_wildcards) { + HeapFree(GetProcessHeap(), 0, wargv_expand); + wargv_expand = HeapAlloc(GetProcessHeap(), 0, + build_expanded_wargv(&wargc_expand, NULL)); + if (wargv_expand) { + build_expanded_wargv(&wargc_expand, wargv_expand); + + MSVCRT___argc = wargc_expand; + MSVCRT___wargv = wargv_expand; + }else { + expand_wildcards = 0; + } + } + if (!expand_wildcards) { + MSVCRT___argc = initial_argc; + MSVCRT___wargv = initial_wargv; + } + + env_init(TRUE, FALSE); + + *argc = MSVCRT___argc; + *wargv = MSVCRT___wargv; + *wenvp = MSVCRT__wenviron; + if (new_mode) + _set_new_mode( *new_mode ); + return 0; +} + +/********************************************************************* + * __getmainargs (MSVCRT.@) + */ +int CDECL __getmainargs(int *argc, char** *argv, char** *envp, + int expand_wildcards, int *new_mode) +{ + TRACE("(%p,%p,%p,%d,%p).\n", argc, argv, envp, expand_wildcards, new_mode); + + if (expand_wildcards) { + HeapFree(GetProcessHeap(), 0, wargv_expand); + wargv_expand = HeapAlloc(GetProcessHeap(), 0, + build_expanded_wargv(&wargc_expand, NULL)); + if (wargv_expand) { + build_expanded_wargv(&wargc_expand, wargv_expand); + + MSVCRT___argc = wargc_expand; + MSVCRT___argv = build_argv( wargv_expand ); + }else { + expand_wildcards = 0; + } + } + if (!expand_wildcards) { + MSVCRT___argc = initial_argc; + MSVCRT___argv = build_argv( initial_wargv ); + } + + *argc = MSVCRT___argc; + *argv = MSVCRT___argv; + *envp = MSVCRT__environ; + + if (new_mode) + _set_new_mode( *new_mode ); + return 0; +} + +#ifdef _CRTDLL +/********************************************************************* + * __GetMainArgs (CRTDLL.@) + */ +void CDECL __GetMainArgs( int *argc, char ***argv, char ***envp, int expand_wildcards ) +{ + int new_mode = 0; + __getmainargs( argc, argv, envp, expand_wildcards, &new_mode ); +} +#endif + +/********************************************************************* + * _initterm (MSVCRT.@) + */ +void CDECL _initterm(_INITTERMFUN *start,_INITTERMFUN *end) +{ + _INITTERMFUN* current = start; + + TRACE("(%p,%p)\n",start,end); + while (current=140 + +/********************************************************************* + * _configure_narrow_argv (UCRTBASE.@) + */ +int CDECL _configure_narrow_argv(int mode) +{ + TRACE("(%d)\n", mode); + return 0; +} + +/********************************************************************* + * _initialize_narrow_environment (UCRTBASE.@) + */ +int CDECL _initialize_narrow_environment(void) +{ + TRACE("\n"); + return env_init(FALSE, FALSE); +} + +/********************************************************************* + * _get_initial_narrow_environment (UCRTBASE.@) + */ +char** CDECL _get_initial_narrow_environment(void) +{ + TRACE("\n"); + _initialize_narrow_environment(); + return MSVCRT___initenv; +} + +/********************************************************************* + * _configure_wide_argv (UCRTBASE.@) + */ +int CDECL _configure_wide_argv(int mode) +{ + WARN("(%d) stub\n", mode); + return 0; +} + +/********************************************************************* + * _initialize_wide_environment (UCRTBASE.@) + */ +int CDECL _initialize_wide_environment(void) +{ + TRACE("\n"); + return env_init(TRUE, FALSE); +} + +/********************************************************************* + * _get_initial_wide_environment (UCRTBASE.@) + */ +wchar_t** CDECL _get_initial_wide_environment(void) +{ + TRACE("\n"); + _initialize_wide_environment(); + return MSVCRT___winitenv; +} + +/********************************************************************* + * _get_narrow_winmain_command_line (UCRTBASE.@) + */ +char* CDECL _get_narrow_winmain_command_line(void) +{ + static char *narrow_command_line; + char *s; + + if (narrow_command_line) + return narrow_command_line; + + s = GetCommandLineA(); + while (*s && *s != ' ' && *s != '\t') + { + if (*s++ == '"') + { + while (*s && *s++ != '"') + ; + } + } + + while (*s == ' ' || *s == '\t') + s++; + + return narrow_command_line = s; +} + +/********************************************************************* + * _get_wide_winmain_command_line (UCRTBASE.@) + */ +wchar_t* CDECL _get_wide_winmain_command_line(void) +{ + static wchar_t *wide_command_line; + wchar_t *s; + + if (wide_command_line) + return wide_command_line; + + s = GetCommandLineW(); + while (*s && *s != ' ' && *s != '\t') + { + if (*s++ == '"') + { + while (*s && *s++ != '"') + ; + } + } + + while (*s == ' ' || *s == '\t') + s++; + + return wide_command_line = s; +} + +#endif /* _MSVCR_VER>=140 */ + +/********************************************************************* + * _get_winmajor (MSVCRT.@) + */ +int CDECL _get_winmajor(int* value) +{ + if (!MSVCRT_CHECK_PMT(value != NULL)) return EINVAL; + *value = MSVCRT__winmajor; + return 0; +} + +/********************************************************************* + * _get_winminor (MSVCRT.@) + */ +int CDECL _get_winminor(int* value) +{ + if (!MSVCRT_CHECK_PMT(value != NULL)) return EINVAL; + *value = MSVCRT__winminor; + return 0; +} + +/********************************************************************* + * _get_osver (MSVCRT.@) + */ +int CDECL _get_osver(int* value) +{ + if (!MSVCRT_CHECK_PMT(value != NULL)) return EINVAL; + *value = MSVCRT__osver; + return 0; +} diff --git a/dll/win32/msvcrt/dir.c b/dll/win32/msvcrt/dir.c new file mode 100644 index 00000000000..a047ca47aab --- /dev/null +++ b/dll/win32/msvcrt/dir.c @@ -0,0 +1,1787 @@ +/* + * msvcrt.dll drive/directory functions + * + * Copyright 1996,1998 Marcus Meissner + * Copyright 1996 Jukka Iivonen + * Copyright 1997,2000 Uwe Bonnes + * Copyright 2000 Jon Griffiths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "msvcrt.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +#undef _wfindfirst +#undef _wfindfirsti64 +#undef _wfindnext +#undef _wfindnexti64 +#undef _wfindfirst32 +#undef _wfindnext32 +#undef _wfindfirst64i32 +#undef _wfindfirst64 +#undef _wfindnext64i32 +#undef _wfindnext64 + +#undef _findfirst +#undef _findfirsti64 +#undef _findnext +#undef _findnexti64 +#undef _findfirst32 +#undef _findnext32 +#undef _findfirst64i32 +#undef _findfirst64 +#undef _findnext64i32 +#undef _findnext64 + +/* INTERNAL: Translate WIN32_FIND_DATAA to finddata_t */ +static void msvcrt_fttofd( const WIN32_FIND_DATAA *fd, struct _finddata_t* ft) +{ + DWORD dw; + + if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL) + ft->attrib = 0; + else + ft->attrib = fd->dwFileAttributes; + + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftCreationTime, &dw ); + ft->time_create = dw; + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastAccessTime, &dw ); + ft->time_access = dw; + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastWriteTime, &dw ); + ft->time_write = dw; + ft->size = fd->nFileSizeLow; + strcpy(ft->name, fd->cFileName); +} + +/* INTERNAL: Translate WIN32_FIND_DATAW to wfinddata_t */ +static void msvcrt_wfttofd( const WIN32_FIND_DATAW *fd, struct _wfinddata_t* ft) +{ + DWORD dw; + + if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL) + ft->attrib = 0; + else + ft->attrib = fd->dwFileAttributes; + + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftCreationTime, &dw ); + ft->time_create = dw; + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastAccessTime, &dw ); + ft->time_access = dw; + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastWriteTime, &dw ); + ft->time_write = dw; + ft->size = fd->nFileSizeLow; + wcscpy(ft->name, fd->cFileName); +} + +/* INTERNAL: Translate WIN32_FIND_DATAW to wfinddata32_t */ +static void msvcrt_wfttofd32(const WIN32_FIND_DATAW *fd, struct _wfinddata32_t* ft) +{ + DWORD dw; + + if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL) + ft->attrib = 0; + else + ft->attrib = fd->dwFileAttributes; + + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftCreationTime, &dw ); + ft->time_create = dw; + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastAccessTime, &dw ); + ft->time_access = dw; + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastWriteTime, &dw ); + ft->time_write = dw; + ft->size = fd->nFileSizeLow; + wcscpy(ft->name, fd->cFileName); +} + +/* INTERNAL: Translate WIN32_FIND_DATAA to finddatai64_t */ +static void msvcrt_fttofdi64( const WIN32_FIND_DATAA *fd, struct _finddatai64_t* ft) +{ + DWORD dw; + + if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL) + ft->attrib = 0; + else + ft->attrib = fd->dwFileAttributes; + + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftCreationTime, &dw ); + ft->time_create = dw; + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastAccessTime, &dw ); + ft->time_access = dw; + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastWriteTime, &dw ); + ft->time_write = dw; + ft->size = ((__int64)fd->nFileSizeHigh) << 32 | fd->nFileSizeLow; + strcpy(ft->name, fd->cFileName); +} + +/* INTERNAL: Translate WIN32_FIND_DATAW to wfinddata64_t */ +static void msvcrt_wfttofd64( const WIN32_FIND_DATAW *fd, struct _wfinddata64_t* ft) +{ + DWORD dw; + + if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL) + ft->attrib = 0; + else + ft->attrib = fd->dwFileAttributes; + + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftCreationTime, &dw ); + ft->time_create = dw; + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastAccessTime, &dw ); + ft->time_access = dw; + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastWriteTime, &dw ); + ft->time_write = dw; + ft->size = ((__int64)fd->nFileSizeHigh) << 32 | fd->nFileSizeLow; + wcscpy(ft->name, fd->cFileName); +} + +/* INTERNAL: Translate WIN32_FIND_DATAW to wfinddatai64_t */ +static void msvcrt_wfttofdi64( const WIN32_FIND_DATAW *fd, struct _wfinddatai64_t* ft) +{ + DWORD dw; + + if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL) + ft->attrib = 0; + else + ft->attrib = fd->dwFileAttributes; + + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftCreationTime, &dw ); + ft->time_create = dw; + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastAccessTime, &dw ); + ft->time_access = dw; + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastWriteTime, &dw ); + ft->time_write = dw; + ft->size = ((__int64)fd->nFileSizeHigh) << 32 | fd->nFileSizeLow; + wcscpy(ft->name, fd->cFileName); +} + +/* INTERNAL: Translate WIN32_FIND_DATAW to wfinddata64i32_t */ +static void msvcrt_wfttofd64i32( const WIN32_FIND_DATAW *fd, struct _wfinddata64i32_t* ft) +{ + DWORD dw; + + if (fd->dwFileAttributes == FILE_ATTRIBUTE_NORMAL) + ft->attrib = 0; + else + ft->attrib = fd->dwFileAttributes; + + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftCreationTime, &dw ); + ft->time_create = dw; + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastAccessTime, &dw ); + ft->time_access = dw; + RtlTimeToSecondsSince1970( (const LARGE_INTEGER *)&fd->ftLastWriteTime, &dw ); + ft->time_write = dw; + ft->size = fd->nFileSizeLow; + wcscpy(ft->name, fd->cFileName); +} + +/********************************************************************* + * _chdir (MSVCRT.@) + * + * Change the current working directory. + * + * PARAMS + * newdir [I] Directory to change to + * + * RETURNS + * Success: 0. The current working directory is set to newdir. + * Failure: -1. errno indicates the error. + * + * NOTES + * See SetCurrentDirectoryA. + */ +int CDECL _chdir(const char * newdir) +{ + wchar_t *newdirW = NULL; + int ret; + + if (newdir && !(newdirW = wstrdupa_utf8(newdir))) return -1; + ret = _wchdir(newdirW); + free(newdirW); + return ret; +} + +/********************************************************************* + * _wchdir (MSVCRT.@) + * + * Unicode version of _chdir. + */ +int CDECL _wchdir(const wchar_t * newdir) +{ + if (!SetCurrentDirectoryW(newdir)) + { + msvcrt_set_errno(newdir?GetLastError():0); + return -1; + } + return 0; +} + +/********************************************************************* + * _chdrive (MSVCRT.@) + * + * Change the current drive. + * + * PARAMS + * newdrive [I] Drive number to change to (1 = 'A', 2 = 'B', ...) + * + * RETURNS + * Success: 0. The current drive is set to newdrive. + * Failure: -1. errno indicates the error. + * + * NOTES + * See SetCurrentDirectoryA. + */ +int CDECL _chdrive(int newdrive) +{ + WCHAR buffer[] = L"A:"; + + buffer[0] += newdrive - 1; + if (!SetCurrentDirectoryW( buffer )) + { + msvcrt_set_errno(GetLastError()); + if (newdrive <= 0) + *_errno() = EACCES; + return -1; + } + return 0; +} + +/********************************************************************* + * _findclose (MSVCRT.@) + * + * Close a handle returned by _findfirst(). + * + * PARAMS + * hand [I] Handle to close + * + * RETURNS + * Success: 0. All resources associated with hand are freed. + * Failure: -1. errno indicates the error. + * + * NOTES + * See FindClose. + */ +int CDECL _findclose(intptr_t hand) +{ + TRACE(":handle %Iu\n",hand); + if (!FindClose((HANDLE)hand)) + { + msvcrt_set_errno(GetLastError()); + return -1; + } + return 0; +} + +/********************************************************************* + * _findfirst (MSVCRT.@) + * + * Open a handle for iterating through a directory. + * + * PARAMS + * fspec [I] File specification of files to iterate. + * ft [O] Information for the first file found. + * + * RETURNS + * Success: A handle suitable for passing to _findnext() and _findclose(). + * ft is populated with the details of the found file. + * Failure: -1. errno indicates the error. + * + * NOTES + * See FindFirstFileA. + */ +intptr_t CDECL _findfirst(const char * fspec, struct _finddata_t* ft) +{ + WIN32_FIND_DATAA find_data; + HANDLE hfind; + + hfind = FindFirstFileA(fspec, &find_data); + if (hfind == INVALID_HANDLE_VALUE) + { + msvcrt_set_errno(GetLastError()); + return -1; + } + msvcrt_fttofd(&find_data,ft); + TRACE(":got handle %p\n",hfind); + return (intptr_t)hfind; +} + +/********************************************************************* + * _wfindfirst (MSVCRT.@) + * + * Unicode version of _findfirst. + */ +intptr_t CDECL _wfindfirst(const wchar_t * fspec, struct _wfinddata_t* ft) +{ + WIN32_FIND_DATAW find_data; + HANDLE hfind; + + hfind = FindFirstFileW(fspec, &find_data); + if (hfind == INVALID_HANDLE_VALUE) + { + msvcrt_set_errno(GetLastError()); + return -1; + } + msvcrt_wfttofd(&find_data,ft); + TRACE(":got handle %p\n",hfind); + return (intptr_t)hfind; +} + +/********************************************************************* + * _wfindfirst32 (MSVCRT.@) + * + * Unicode version of _findfirst32. + */ +intptr_t CDECL _wfindfirst32(const wchar_t * fspec, struct _wfinddata32_t* ft) +{ + WIN32_FIND_DATAW find_data; + HANDLE hfind; + + hfind = FindFirstFileW(fspec, &find_data); + if (hfind == INVALID_HANDLE_VALUE) + { + msvcrt_set_errno(GetLastError()); + return -1; + } + msvcrt_wfttofd32(&find_data, ft); + TRACE(":got handle %p\n", hfind); + return (intptr_t)hfind; +} + +static int finddata32_wtoa(const struct _wfinddata32_t *wfd, struct _finddata32_t *fd) +{ + fd->attrib = wfd->attrib; + fd->time_create = wfd->time_create; + fd->time_access = wfd->time_access; + fd->time_write = wfd->time_write; + fd->size = wfd->size; + return convert_wcs_to_acp_utf8(wfd->name, fd->name, ARRAY_SIZE(fd->name)); +} + +/********************************************************************* + * _findfirst32 (MSVCRT.@) + */ +intptr_t CDECL _findfirst32(const char *fspec, struct _finddata32_t *ft) +{ + struct _wfinddata32_t wft; + wchar_t *fspecW = NULL; + intptr_t ret; + + if (fspec && !(fspecW = wstrdupa_utf8(fspec))) return -1; + ret = _wfindfirst32(fspecW, &wft); + free(fspecW); + if (ret != -1 && !finddata32_wtoa(&wft, ft)) + { + _findclose(ret); + return -1; + } + return ret; +} + +/********************************************************************* + * _findfirsti64 (MSVCRT.@) + * + * 64-bit version of _findfirst. + */ +intptr_t CDECL _findfirsti64(const char * fspec, struct _finddatai64_t* ft) +{ + WIN32_FIND_DATAA find_data; + HANDLE hfind; + + hfind = FindFirstFileA(fspec, &find_data); + if (hfind == INVALID_HANDLE_VALUE) + { + msvcrt_set_errno(GetLastError()); + return -1; + } + msvcrt_fttofdi64(&find_data,ft); + TRACE(":got handle %p\n",hfind); + return (intptr_t)hfind; +} + +/********************************************************************* + * _wfindfirst64 (MSVCRT.@) + * + * Unicode version of _findfirst64. + */ +intptr_t CDECL _wfindfirst64(const wchar_t * fspec, struct _wfinddata64_t* ft) +{ + WIN32_FIND_DATAW find_data; + HANDLE hfind; + + hfind = FindFirstFileW(fspec, &find_data); + if (hfind == INVALID_HANDLE_VALUE) + { + msvcrt_set_errno(GetLastError()); + return -1; + } + msvcrt_wfttofd64(&find_data,ft); + TRACE(":got handle %p\n",hfind); + return (intptr_t)hfind; +} + +static int finddata64_wtoa(const struct _wfinddata64_t *wfd, struct _finddata64_t *fd) +{ + fd->attrib = wfd->attrib; + fd->time_create = wfd->time_create; + fd->time_access = wfd->time_access; + fd->time_write = wfd->time_write; + fd->size = wfd->size; + return convert_wcs_to_acp_utf8(wfd->name, fd->name, ARRAY_SIZE(fd->name)); +} + +/********************************************************************* + * _findfirst64 (MSVCRT.@) + * + * 64-bit version of _findfirst. + */ +intptr_t CDECL _findfirst64(const char *fspec, struct _finddata64_t *ft) +{ + struct _wfinddata64_t wft; + wchar_t *fspecW = NULL; + intptr_t ret; + + if (fspec && !(fspecW = wstrdupa_utf8(fspec))) return -1; + ret = _wfindfirst64(fspecW, &wft); + free(fspecW); + if (ret != -1 && !finddata64_wtoa(&wft, ft)) + { + _findclose(ret); + return -1; + } + return ret; +} + +/********************************************************************* + * _wfindfirst64i32 (MSVCRT.@) + * + * Unicode version of _findfirst64i32. + */ +intptr_t CDECL _wfindfirst64i32(const wchar_t * fspec, struct _wfinddata64i32_t* ft) +{ + WIN32_FIND_DATAW find_data; + HANDLE hfind; + + hfind = FindFirstFileW(fspec, &find_data); + if (hfind == INVALID_HANDLE_VALUE) + { + msvcrt_set_errno(GetLastError()); + return -1; + } + msvcrt_wfttofd64i32(&find_data,ft); + TRACE(":got handle %p\n",hfind); + return (intptr_t)hfind; +} + +static int finddata64i32_wtoa(const struct _wfinddata64i32_t *wfd, struct _finddata64i32_t *fd) +{ + fd->attrib = wfd->attrib; + fd->time_create = wfd->time_create; + fd->time_access = wfd->time_access; + fd->time_write = wfd->time_write; + fd->size = wfd->size; + return convert_wcs_to_acp_utf8(wfd->name, fd->name, ARRAY_SIZE(fd->name)); +} + +/********************************************************************* + * _findfirst64i32 (MSVCRT.@) + * + * 64-bit/32-bit version of _findfirst. + */ +intptr_t CDECL _findfirst64i32(const char *fspec, struct _finddata64i32_t *ft) +{ + struct _wfinddata64i32_t wft; + wchar_t *fspecW = NULL; + intptr_t ret; + + if (fspec && !(fspecW = wstrdupa_utf8(fspec))) return -1; + ret = _wfindfirst64i32(fspecW, &wft); + free(fspecW); + if (ret != -1 && !finddata64i32_wtoa(&wft, ft)) + { + _findclose(ret); + return -1; + } + return ret; +} + +/********************************************************************* + * _wfindfirsti64 (MSVCRT.@) + * + * Unicode version of _findfirsti64. + */ +intptr_t CDECL _wfindfirsti64(const wchar_t * fspec, struct _wfinddatai64_t* ft) +{ + WIN32_FIND_DATAW find_data; + HANDLE hfind; + + hfind = FindFirstFileW(fspec, &find_data); + if (hfind == INVALID_HANDLE_VALUE) + { + msvcrt_set_errno(GetLastError()); + return -1; + } + msvcrt_wfttofdi64(&find_data,ft); + TRACE(":got handle %p\n",hfind); + return (intptr_t)hfind; +} + +/********************************************************************* + * _findnext (MSVCRT.@) + * + * Find the next file from a file search handle. + * + * PARAMS + * hand [I] Handle to the search returned from _findfirst(). + * ft [O] Information for the file found. + * + * RETURNS + * Success: 0. ft is populated with the details of the found file. + * Failure: -1. errno indicates the error. + * + * NOTES + * See FindNextFileA. + */ +int CDECL _findnext(intptr_t hand, struct _finddata_t * ft) +{ + WIN32_FIND_DATAA find_data; + + if (!FindNextFileA((HANDLE)hand, &find_data)) + { + *_errno() = ENOENT; + return -1; + } + + msvcrt_fttofd(&find_data,ft); + return 0; +} + +/********************************************************************* + * _wfindnext32 (MSVCRT.@) + */ +int CDECL _wfindnext32(intptr_t hand, struct _wfinddata32_t * ft) +{ + WIN32_FIND_DATAW find_data; + + if (!FindNextFileW((HANDLE)hand, &find_data)) + { + *_errno() = ENOENT; + return -1; + } + + msvcrt_wfttofd32(&find_data, ft); + return 0; +} + +/********************************************************************* + * _findnext32 (MSVCRT.@) + */ +int CDECL _findnext32(intptr_t hand, struct _finddata32_t *ft) +{ + struct _wfinddata32_t wft; + int ret; + + ret = _wfindnext32(hand, &wft); + if (!ret && !finddata32_wtoa(&wft, ft)) ret = -1; + return ret; +} + +/********************************************************************* + * _wfindnext (MSVCRT.@) + * + * Unicode version of _findnext. + */ +int CDECL _wfindnext(intptr_t hand, struct _wfinddata_t * ft) +{ + WIN32_FIND_DATAW find_data; + + if (!FindNextFileW((HANDLE)hand, &find_data)) + { + *_errno() = ENOENT; + return -1; + } + + msvcrt_wfttofd(&find_data,ft); + return 0; +} + +/********************************************************************* + * _findnexti64 (MSVCRT.@) + * + * 64-bit version of _findnext. + */ +int CDECL _findnexti64(intptr_t hand, struct _finddatai64_t * ft) +{ + WIN32_FIND_DATAA find_data; + + if (!FindNextFileA((HANDLE)hand, &find_data)) + { + *_errno() = ENOENT; + return -1; + } + + msvcrt_fttofdi64(&find_data,ft); + return 0; +} + +/********************************************************************* + * _wfindnext64 (MSVCRT.@) + * + * Unicode version of _wfindnext64. + */ +int CDECL _wfindnext64(intptr_t hand, struct _wfinddata64_t * ft) +{ + WIN32_FIND_DATAW find_data; + + if (!FindNextFileW((HANDLE)hand, &find_data)) + { + *_errno() = ENOENT; + return -1; + } + + msvcrt_wfttofd64(&find_data,ft); + return 0; +} + +/********************************************************************* + * _findnext64 (MSVCRT.@) + * + * 64-bit version of _findnext. + */ +int CDECL _findnext64(intptr_t hand, struct _finddata64_t * ft) +{ + struct _wfinddata64_t wft; + int ret; + + ret = _wfindnext64(hand, &wft); + if (!ret && !finddata64_wtoa(&wft, ft)) ret = -1; + return ret; +} + +/********************************************************************* + * _wfindnexti64 (MSVCRT.@) + * + * Unicode version of _findnexti64. + */ +int CDECL _wfindnexti64(intptr_t hand, struct _wfinddatai64_t * ft) +{ + WIN32_FIND_DATAW find_data; + + if (!FindNextFileW((HANDLE)hand, &find_data)) + { + *_errno() = ENOENT; + return -1; + } + + msvcrt_wfttofdi64(&find_data,ft); + return 0; +} + +/********************************************************************* + * _wfindnext64i32 (MSVCRT.@) + * + * Unicode version of _findnext64i32. + */ +int CDECL _wfindnext64i32(intptr_t hand, struct _wfinddata64i32_t * ft) +{ + WIN32_FIND_DATAW find_data; + + if (!FindNextFileW((HANDLE)hand, &find_data)) + { + *_errno() = ENOENT; + return -1; + } + + msvcrt_wfttofd64i32(&find_data,ft); + return 0; +} + +/********************************************************************* + * _findnext64i32 (MSVCRT.@) + * + * 64-bit/32-bit version of _findnext. + */ +int CDECL _findnext64i32(intptr_t hand, struct _finddata64i32_t *ft) +{ + struct _wfinddata64i32_t wft; + int ret; + + ret = _wfindnext64i32(hand, &wft); + if (!ret && !finddata64i32_wtoa(&wft, ft)) ret = -1; + return ret; +} + +/********************************************************************* + * _getcwd (MSVCRT.@) + * + * Get the current working directory. + * + * PARAMS + * buf [O] Destination for current working directory. + * size [I] Size of buf in characters + * + * RETURNS + * Success: If buf is NULL, returns an allocated string containing the path. + * Otherwise populates buf with the path and returns it. + * Failure: NULL. errno indicates the error. + */ +char* CDECL _getcwd(char * buf, int size) +{ + wchar_t dirW[MAX_PATH]; + int len; + + if (!_wgetcwd(dirW, ARRAY_SIZE(dirW))) return NULL; + + if (!buf) return astrdupw_utf8(dirW); + len = convert_wcs_to_acp_utf8(dirW, NULL, 0); + if (!len) return NULL; + if (len > size) + { + *_errno() = ERANGE; + return NULL; + } + convert_wcs_to_acp_utf8(dirW, buf, size); + return buf; +} + +/********************************************************************* + * _wgetcwd (MSVCRT.@) + * + * Unicode version of _getcwd. + */ +wchar_t* CDECL _wgetcwd(wchar_t * buf, int size) +{ + wchar_t dir[MAX_PATH]; + int dir_len = GetCurrentDirectoryW(MAX_PATH,dir); + + if (dir_len < 1) + return NULL; /* FIXME: Real return value untested */ + + if (!buf) + { + if (size <= dir_len) size = dir_len + 1; + if (!(buf = malloc( size * sizeof(WCHAR) ))) return NULL; + } + else if (dir_len >= size) + { + *_errno() = ERANGE; + return NULL; /* buf too small */ + } + wcscpy(buf,dir); + return buf; +} + +/********************************************************************* + * _getdrive (MSVCRT.@) + * + * Get the current drive number. + * + * PARAMS + * None. + * + * RETURNS + * Success: The drive letter number from 1 to 26 ("A:" to "Z:"). + * Failure: 0. + */ +int CDECL _getdrive(void) +{ + WCHAR buffer[MAX_PATH]; + if (GetCurrentDirectoryW( MAX_PATH, buffer ) && + buffer[0] >= 'A' && buffer[0] <= 'z' && buffer[1] == ':') + return towupper(buffer[0]) - 'A' + 1; + return 0; +} + +/********************************************************************* + * _getdcwd (MSVCRT.@) + * + * Get the current working directory on a given disk. + * + * PARAMS + * drive [I] Drive letter to get the current working directory from. + * buf [O] Destination for the current working directory. + * size [I] Length of drive in characters. + * + * RETURNS + * Success: If drive is NULL, returns an allocated string containing the path. + * Otherwise populates drive with the path and returns it. + * Failure: NULL. errno indicates the error. + */ +char* CDECL _getdcwd(int drive, char * buf, int size) +{ + wchar_t dirW[MAX_PATH]; + int len; + + if (!_wgetdcwd(drive, dirW, ARRAY_SIZE(dirW))) return NULL; + + if (!buf) return astrdupw_utf8(dirW); + len = convert_wcs_to_acp_utf8(dirW, NULL, 0); + if (!len) return NULL; + if (len > size) + { + *_errno() = ERANGE; + return NULL; + } + convert_wcs_to_acp_utf8(dirW, buf, size); + return buf; +} + +/********************************************************************* + * _wgetdcwd (MSVCRT.@) + * + * Unicode version of _wgetdcwd. + */ +wchar_t* CDECL _wgetdcwd(int drive, wchar_t * buf, int size) +{ + static wchar_t* dummy; + + TRACE(":drive %d(%c), size %d\n",drive, drive + 'A' - 1, size); + + if (!drive || drive == _getdrive()) + return _wgetcwd(buf,size); /* current */ + else + { + wchar_t dir[MAX_PATH]; + wchar_t drivespec[4] = L"A:\\"; + int dir_len; + + drivespec[0] += drive - 1; + if (GetDriveTypeW(drivespec) < DRIVE_REMOVABLE) + { + *_errno() = EACCES; + return NULL; + } + + dir_len = GetFullPathNameW(drivespec,MAX_PATH,dir,&dummy); + if (dir_len >= size || dir_len < 1) + { + *_errno() = ERANGE; + return NULL; /* buf too small */ + } + + TRACE(":returning %s\n", debugstr_w(dir)); + if (!buf) + return _wcsdup(dir); /* allocate */ + wcscpy(buf,dir); + } + return buf; +} + +/********************************************************************* + * _getdiskfree (MSVCRT.@) + * + * Get information about the free space on a drive. + * + * PARAMS + * disk [I] Drive number to get information about (1 = 'A', 2 = 'B', ...) + * info [O] Destination for the resulting information. + * + * RETURNS + * Success: 0. info is updated with the free space information. + * Failure: An error code from GetLastError(). + * + * NOTES + * See GetLastError(). + */ +unsigned int CDECL _getdiskfree(unsigned int disk, struct _diskfree_t * d) +{ + WCHAR drivespec[] = L"@:\\"; + DWORD ret[4]; + unsigned int err; + + if (disk > 26) + return ERROR_INVALID_PARAMETER; /* MSVCRT doesn't set errno here */ + + drivespec[0] += disk; /* make a drive letter */ + + if (GetDiskFreeSpaceW(disk==0?NULL:drivespec,ret,ret+1,ret+2,ret+3)) + { + d->sectors_per_cluster = ret[0]; + d->bytes_per_sector = ret[1]; + d->avail_clusters = ret[2]; + d->total_clusters = ret[3]; + return 0; + } + err = GetLastError(); + msvcrt_set_errno(err); + return err; +} + +/********************************************************************* + * _mkdir (MSVCRT.@) + * + * Create a directory. + * + * PARAMS + * newdir [I] Name of directory to create. + * + * RETURNS + * Success: 0. The directory indicated by newdir is created. + * Failure: -1. errno indicates the error. + * + * NOTES + * See CreateDirectoryA. + */ +int CDECL _mkdir(const char * newdir) +{ + wchar_t *newdirW = NULL; + int ret; + + if (newdir && !(newdirW = wstrdupa_utf8(newdir))) return -1; + ret = _wmkdir(newdirW); + free(newdirW); + return ret; +} + +/********************************************************************* + * _wmkdir (MSVCRT.@) + * + * Unicode version of _mkdir. + */ +int CDECL _wmkdir(const wchar_t* newdir) +{ + if (CreateDirectoryW(newdir,NULL)) + return 0; + msvcrt_set_errno(GetLastError()); + return -1; +} + +/********************************************************************* + * _rmdir (MSVCRT.@) + * + * Delete a directory. + * + * PARAMS + * dir [I] Name of directory to delete. + * + * RETURNS + * Success: 0. The directory indicated by newdir is deleted. + * Failure: -1. errno indicates the error. + * + * NOTES + * See RemoveDirectoryA. + */ +int CDECL _rmdir(const char * dir) +{ + wchar_t *dirW = NULL; + int ret; + + if (dir && !(dirW = wstrdupa_utf8(dir))) return -1; + ret = _wrmdir(dirW); + free(dirW); + return ret; +} + +/********************************************************************* + * _wrmdir (MSVCRT.@) + * + * Unicode version of _rmdir. + */ +int CDECL _wrmdir(const wchar_t * dir) +{ + if (RemoveDirectoryW(dir)) + return 0; + msvcrt_set_errno(GetLastError()); + return -1; +} + +/****************************************************************** + * _splitpath_s (MSVCRT.@) + */ +errno_t CDECL _splitpath_s(const char* inpath, + char* drive, size_t sz_drive, + char* dir, size_t sz_dir, + char* fname, size_t sz_fname, + char* ext, size_t sz_ext) +{ + const char *p, *end; + + if (!inpath || (!drive && sz_drive) || + (drive && !sz_drive) || + (!dir && sz_dir) || + (dir && !sz_dir) || + (!fname && sz_fname) || + (fname && !sz_fname) || + (!ext && sz_ext) || + (ext && !sz_ext)) + { + *_errno() = EINVAL; + return EINVAL; + } + + if (inpath[0] && inpath[1] == ':') + { + if (drive) + { + if (sz_drive <= 2) goto do_error; + drive[0] = inpath[0]; + drive[1] = inpath[1]; + drive[2] = 0; + } + inpath += 2; + } + else if (drive) drive[0] = '\0'; + + /* look for end of directory part */ + end = NULL; + for (p = inpath; *p; p++) + { + if (_ismbblead((unsigned char)*p)) + { + p++; + continue; + } + if (*p == '/' || *p == '\\') end = p + 1; + } + + if (end) /* got a directory */ + { + if (dir) + { + if (sz_dir <= end - inpath) goto do_error; + memcpy( dir, inpath, (end - inpath) ); + dir[end - inpath] = 0; + } + inpath = end; + } + else if (dir) dir[0] = 0; + + /* look for extension: what's after the last dot */ + end = NULL; + for (p = inpath; *p; p++) if (*p == '.') end = p; + + if (!end) end = p; /* there's no extension */ + + if (fname) + { + if (sz_fname <= end - inpath) goto do_error; + memcpy( fname, inpath, (end - inpath) ); + fname[end - inpath] = 0; + } + if (ext) + { + if (sz_ext <= strlen(end)) goto do_error; + strcpy( ext, end ); + } + return 0; +do_error: + if (drive) drive[0] = '\0'; + if (dir) dir[0] = '\0'; + if (fname) fname[0]= '\0'; + if (ext) ext[0]= '\0'; + *_errno() = ERANGE; + return ERANGE; +} + +/********************************************************************* + * _splitpath (MSVCRT.@) + */ +void CDECL _splitpath(const char *inpath, char *drv, char *dir, + char *fname, char *ext) +{ + _splitpath_s(inpath, drv, drv ? _MAX_DRIVE : 0, dir, dir ? _MAX_DIR : 0, + fname, fname ? _MAX_FNAME : 0, ext, ext ? _MAX_EXT : 0); +} + +/****************************************************************** + * _wsplitpath_s (MSVCRT.@) + * + * Secure version of _wsplitpath + */ +int CDECL _wsplitpath_s(const wchar_t* inpath, + wchar_t* drive, size_t sz_drive, + wchar_t* dir, size_t sz_dir, + wchar_t* fname, size_t sz_fname, + wchar_t* ext, size_t sz_ext) +{ + const wchar_t *p, *end; + + if (!inpath || (!drive && sz_drive) || + (drive && !sz_drive) || + (!dir && sz_dir) || + (dir && !sz_dir) || + (!fname && sz_fname) || + (fname && !sz_fname) || + (!ext && sz_ext) || + (ext && !sz_ext)) + { + *_errno() = EINVAL; + return EINVAL; + } + + if (inpath[0] && inpath[1] == ':') + { + if (drive) + { + if (sz_drive <= 2) goto do_error; + drive[0] = inpath[0]; + drive[1] = inpath[1]; + drive[2] = 0; + } + inpath += 2; + } + else if (drive) drive[0] = '\0'; + + /* look for end of directory part */ + end = NULL; + for (p = inpath; *p; p++) if (*p == '/' || *p == '\\') end = p + 1; + + if (end) /* got a directory */ + { + if (dir) + { + if (sz_dir <= end - inpath) goto do_error; + memcpy( dir, inpath, (end - inpath) * sizeof(wchar_t) ); + dir[end - inpath] = 0; + } + inpath = end; + } + else if (dir) dir[0] = 0; + + /* look for extension: what's after the last dot */ + end = NULL; + for (p = inpath; *p; p++) if (*p == '.') end = p; + + if (!end) end = p; /* there's no extension */ + + if (fname) + { + if (sz_fname <= end - inpath) goto do_error; + memcpy( fname, inpath, (end - inpath) * sizeof(wchar_t) ); + fname[end - inpath] = 0; + } + if (ext) + { + if (sz_ext <= wcslen(end)) goto do_error; + wcscpy( ext, end ); + } + return 0; +do_error: + if (drive) drive[0] = '\0'; + if (dir) dir[0] = '\0'; + if (fname) fname[0]= '\0'; + if (ext) ext[0]= '\0'; + *_errno() = ERANGE; + return ERANGE; +} + +/********************************************************************* + * _wsplitpath (MSVCRT.@) + * + * Unicode version of _splitpath. + */ +void CDECL _wsplitpath(const wchar_t *inpath, wchar_t *drv, wchar_t *dir, + wchar_t *fname, wchar_t *ext) +{ + _wsplitpath_s(inpath, drv, drv ? _MAX_DRIVE : 0, dir, dir ? _MAX_DIR : 0, + fname, fname ? _MAX_FNAME : 0, ext, ext ? _MAX_EXT : 0); +} + +/********************************************************************* + * _wfullpath (MSVCRT.@) + * + * Unicode version of _fullpath. + */ +wchar_t * CDECL _wfullpath(wchar_t * absPath, const wchar_t* relPath, size_t size) +{ + DWORD rc; + WCHAR* buffer; + WCHAR* lastpart; + BOOL alloced = FALSE; + + if (!relPath || !*relPath) + return _wgetcwd(absPath, size); + + if (absPath == NULL) + { + buffer = malloc(MAX_PATH * sizeof(WCHAR)); + size = MAX_PATH; + alloced = TRUE; + } + else + buffer = absPath; + + if (size < 4) + { + *_errno() = ERANGE; + return NULL; + } + + TRACE(":resolving relative path %s\n",debugstr_w(relPath)); + + rc = GetFullPathNameW(relPath,size,buffer,&lastpart); + + if (rc > 0 && rc <= size ) + return buffer; + else + { + if (alloced) + free(buffer); + return NULL; + } +} + +/********************************************************************* + * _fullpath (MSVCRT.@) + * + * Create an absolute path from a relative path. + * + * PARAMS + * absPath [O] Destination for absolute path + * relPath [I] Relative path to convert to absolute + * size [I] Length of absPath in characters. + * + * RETURNS + * Success: If absPath is NULL, returns an allocated string containing the path. + * Otherwise populates absPath with the path and returns it. + * Failure: NULL. errno indicates the error. + */ +char * CDECL _fullpath(char *abs_path, const char *rel_path, size_t size) +{ + wchar_t abs_pathW[MAX_PATH], *rel_pathW = NULL, *retW; + size_t len; + + if (rel_path && !(rel_pathW = wstrdupa_utf8(rel_path))) return NULL; + retW = _wfullpath(abs_pathW, rel_pathW, ARRAY_SIZE(abs_pathW)); + free(rel_pathW); + if (!retW) return NULL; + + if (!abs_path) return astrdupw_utf8(abs_pathW); + len = convert_wcs_to_acp_utf8(abs_pathW, NULL, 0); + if (!len) return NULL; + if (len > size) + { + *_errno() = ERANGE; + return NULL; + } + convert_wcs_to_acp_utf8(abs_pathW, abs_path, size); + return abs_path; +} + +/********************************************************************* + * _makepath (MSVCRT.@) + * + * Create a pathname. + * + * PARAMS + * path [O] Destination for created pathname + * drive [I] Drive letter (e.g. "A:") + * directory [I] Directory + * filename [I] Name of the file, excluding extension + * extension [I] File extension (e.g. ".TXT") + * + * RETURNS + * Nothing. If path is not large enough to hold the resulting pathname, + * random process memory will be overwritten. + */ +VOID CDECL _makepath(char * path, const char * drive, + const char *directory, const char * filename, + const char * extension) +{ + char *p = path; + + TRACE("(%s %s %s %s)\n", debugstr_a(drive), debugstr_a(directory), + debugstr_a(filename), debugstr_a(extension) ); + + if ( !path ) + return; + + if (drive && drive[0]) + { + *p++ = drive[0]; + *p++ = ':'; + } + if (directory && directory[0]) + { + unsigned int len = strlen(directory); + memmove(p, directory, len); + p += len; + if (p[-1] != '/' && p[-1] != '\\') + *p++ = '\\'; + } + if (filename && filename[0]) + { + unsigned int len = strlen(filename); + memmove(p, filename, len); + p += len; + } + if (extension && extension[0]) + { + if (extension[0] != '.') + *p++ = '.'; + strcpy(p, extension); + } + else + *p = '\0'; + TRACE("returning %s\n",path); +} + +/********************************************************************* + * _wmakepath (MSVCRT.@) + * + * Unicode version of _wmakepath. + */ +VOID CDECL _wmakepath(wchar_t *path, const wchar_t *drive, const wchar_t *directory, + const wchar_t *filename, const wchar_t *extension) +{ + wchar_t *p = path; + + TRACE("%s %s %s %s\n", debugstr_w(drive), debugstr_w(directory), + debugstr_w(filename), debugstr_w(extension)); + + if ( !path ) + return; + + if (drive && drive[0]) + { + *p++ = drive[0]; + *p++ = ':'; + } + if (directory && directory[0]) + { + unsigned int len = wcslen(directory); + memmove(p, directory, len * sizeof(wchar_t)); + p += len; + if (p[-1] != '/' && p[-1] != '\\') + *p++ = '\\'; + } + if (filename && filename[0]) + { + unsigned int len = wcslen(filename); + memmove(p, filename, len * sizeof(wchar_t)); + p += len; + } + if (extension && extension[0]) + { + if (extension[0] != '.') + *p++ = '.'; + wcscpy(p, extension); + } + else + *p = '\0'; + + TRACE("returning %s\n", debugstr_w(path)); +} + +/********************************************************************* + * _makepath_s (MSVCRT.@) + * + * Safe version of _makepath. + */ +int CDECL _makepath_s(char *path, size_t size, const char *drive, + const char *directory, const char *filename, + const char *extension) +{ + char *p = path; + + if (!path || !size) + { + *_errno() = EINVAL; + return EINVAL; + } + + if (drive && drive[0]) + { + if (size <= 2) + goto range; + + *p++ = drive[0]; + *p++ = ':'; + size -= 2; + } + + if (directory && directory[0]) + { + unsigned int len = strlen(directory); + unsigned int needs_separator = directory[len - 1] != '/' && directory[len - 1] != '\\'; + unsigned int copylen = min(size - 1, len); + + if (size < 2) + goto range; + + memmove(p, directory, copylen); + + if (size <= len) + goto range; + + p += copylen; + size -= copylen; + + if (needs_separator) + { + if (size < 2) + goto range; + + *p++ = '\\'; + size -= 1; + } + } + + if (filename && filename[0]) + { + unsigned int len = strlen(filename); + unsigned int copylen = min(size - 1, len); + + if (size < 2) + goto range; + + memmove(p, filename, copylen); + + if (size <= len) + goto range; + + p += len; + size -= len; + } + + if (extension && extension[0]) + { + unsigned int len = strlen(extension); + unsigned int needs_period = extension[0] != '.'; + unsigned int copylen; + + if (size < 2) + goto range; + + if (needs_period) + { + *p++ = '.'; + size -= 1; + } + + copylen = min(size - 1, len); + memcpy(p, extension, copylen); + + if (size <= len) + goto range; + + p += copylen; + } + + *p = '\0'; + return 0; + +range: + path[0] = '\0'; + *_errno() = ERANGE; + return ERANGE; +} + +/********************************************************************* + * _wmakepath_s (MSVCRT.@) + * + * Safe version of _wmakepath. + */ +int CDECL _wmakepath_s(wchar_t *path, size_t size, const wchar_t *drive, + const wchar_t *directory, const wchar_t *filename, + const wchar_t *extension) +{ + wchar_t *p = path; + + if (!path || !size) + { + *_errno() = EINVAL; + return EINVAL; + } + + if (drive && drive[0]) + { + if (size <= 2) + goto range; + + *p++ = drive[0]; + *p++ = ':'; + size -= 2; + } + + if (directory && directory[0]) + { + unsigned int len = wcslen(directory); + unsigned int needs_separator = directory[len - 1] != '/' && directory[len - 1] != '\\'; + unsigned int copylen = min(size - 1, len); + + if (size < 2) + goto range; + + memmove(p, directory, copylen * sizeof(wchar_t)); + + if (size <= len) + goto range; + + p += copylen; + size -= copylen; + + if (needs_separator) + { + if (size < 2) + goto range; + + *p++ = '\\'; + size -= 1; + } + } + + if (filename && filename[0]) + { + unsigned int len = wcslen(filename); + unsigned int copylen = min(size - 1, len); + + if (size < 2) + goto range; + + memmove(p, filename, copylen * sizeof(wchar_t)); + + if (size <= len) + goto range; + + p += len; + size -= len; + } + + if (extension && extension[0]) + { + unsigned int len = wcslen(extension); + unsigned int needs_period = extension[0] != '.'; + unsigned int copylen; + + if (size < 2) + goto range; + + if (needs_period) + { + *p++ = '.'; + size -= 1; + } + + copylen = min(size - 1, len); + memcpy(p, extension, copylen * sizeof(wchar_t)); + + if (size <= len) + goto range; + + p += copylen; + } + + *p = '\0'; + return 0; + +range: + path[0] = '\0'; + *_errno() = ERANGE; + return ERANGE; +} + +/********************************************************************* + * _searchenv_s (MSVCRT.@) + */ +int CDECL _searchenv_s(const char* file, const char* env, char *buf, size_t count) +{ + char *envVal, *penv, *end; + char path[MAX_PATH]; + size_t path_len, fname_len; + int old_errno, access; + + if (!MSVCRT_CHECK_PMT(file != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(buf != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(count > 0)) return EINVAL; + + if (count > MAX_PATH) + FIXME("count > MAX_PATH not supported\n"); + + fname_len = strlen(file); + *buf = '\0'; + + /* Try CWD first */ + old_errno = *_errno(); + access = _access(file, 0); + *_errno() = old_errno; + if (!access) + { + if (!_fullpath(buf, file, count)) + return *_errno(); + return 0; + } + + /* Search given environment variable */ + envVal = getenv(env); + if (!envVal) + { + *_errno() = ENOENT; + return ENOENT; + } + + penv = envVal; + TRACE(":searching for %s in paths %s\n", file, envVal); + + for(; *penv; penv = (*end ? end + 1 : end)) + { + end = penv; + path_len = 0; + while(*end && *end != ';' && path_len < MAX_PATH) + { + if (*end == '"') + { + end++; + while(*end && *end != '"' && path_len < MAX_PATH) + { + path[path_len++] = *end; + end++; + } + if (*end == '"') end++; + continue; + } + + path[path_len++] = *end; + end++; + } + if (!path_len || path_len >= MAX_PATH) + continue; + + if (path[path_len - 1] != '/' && path[path_len - 1] != '\\') + path[path_len++] = '\\'; + if (path_len + fname_len >= MAX_PATH) + continue; + + memcpy(path + path_len, file, fname_len + 1); + TRACE("Checking for file %s\n", path); + old_errno = *_errno(); + access = _access(path, 0); + *_errno() = old_errno; + if (!access) + { + if (path_len + fname_len + 1 > count) + { + MSVCRT_INVALID_PMT("buf[count] is too small", ERANGE); + return ERANGE; + } + memcpy(buf, path, path_len + fname_len + 1); + return 0; + } + } + + *_errno() = ENOENT; + return ENOENT; +} + +/********************************************************************* + * _searchenv (MSVCRT.@) + */ +void CDECL _searchenv(const char* file, const char* env, char *buf) +{ + _searchenv_s(file, env, buf, MAX_PATH); +} + +/********************************************************************* + * _wsearchenv_s (MSVCRT.@) + */ +int CDECL _wsearchenv_s(const wchar_t* file, const wchar_t* env, + wchar_t *buf, size_t count) +{ + wchar_t *envVal, *penv, *end; + wchar_t path[MAX_PATH]; + size_t path_len, fname_len; + + if (!MSVCRT_CHECK_PMT(file != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(buf != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(count > 0)) return EINVAL; + + if (count > MAX_PATH) + FIXME("count > MAX_PATH not supported\n"); + + fname_len = wcslen(file); + *buf = '\0'; + + /* Try CWD first */ + if (GetFileAttributesW( file ) != INVALID_FILE_ATTRIBUTES) + { + if (!_wfullpath(buf, file, count)) + return *_errno(); + return 0; + } + + /* Search given environment variable */ + envVal = _wgetenv(env); + if (!envVal) + { + *_errno() = ENOENT; + return ENOENT; + } + + penv = envVal; + TRACE(":searching for %s in paths %s\n", debugstr_w(file), debugstr_w(envVal)); + + for(; *penv; penv = (*end ? end + 1 : end)) + { + end = penv; + path_len = 0; + while(*end && *end != ';' && path_len < MAX_PATH) + { + if (*end == '"') + { + end++; + while(*end && *end != '"' && path_len < MAX_PATH) + { + path[path_len++] = *end; + end++; + } + if (*end == '"') end++; + continue; + } + + path[path_len++] = *end; + end++; + } + if (!path_len || path_len >= MAX_PATH) + continue; + + if (path[path_len - 1] != '/' && path[path_len - 1] != '\\') + path[path_len++] = '\\'; + if (path_len + fname_len >= MAX_PATH) + continue; + + memcpy(path + path_len, file, (fname_len + 1) * sizeof(wchar_t)); + TRACE("Checking for file %s\n", debugstr_w(path)); + if (GetFileAttributesW( path ) != INVALID_FILE_ATTRIBUTES) + { + if (path_len + fname_len + 1 > count) + { + MSVCRT_INVALID_PMT("buf[count] is too small", ERANGE); + return ERANGE; + } + memcpy(buf, path, (path_len + fname_len + 1) * sizeof(wchar_t)); + return 0; + } + } + + *_errno() = ENOENT; + return ENOENT; +} + +/********************************************************************* + * _wsearchenv (MSVCRT.@) + */ +void CDECL _wsearchenv(const wchar_t* file, const wchar_t* env, wchar_t *buf) +{ + _wsearchenv_s(file, env, buf, MAX_PATH); +} diff --git a/dll/win32/msvcrt/environ.c b/dll/win32/msvcrt/environ.c new file mode 100644 index 00000000000..a0d2e08c7d0 --- /dev/null +++ b/dll/win32/msvcrt/environ.c @@ -0,0 +1,571 @@ +/* + * msvcrt.dll environment functions + * + * Copyright 1996,1998 Marcus Meissner + * Copyright 1996 Jukka Iivonen + * Copyright 1997,2000 Uwe Bonnes + * Copyright 2000 Jon Griffiths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ +#include "msvcrt.h" +#include "mtdll.h" +#include +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +int env_init(BOOL unicode, BOOL modif) +{ + if (!unicode && !MSVCRT___initenv) + { + char *environ_strings = GetEnvironmentStringsA(); + int count = 1, len = 1, i = 0; /* keep space for the trailing NULLS */ + char *ptr; + + for (ptr = environ_strings; *ptr; ptr += strlen(ptr) + 1) + { + /* Don't count environment variables starting with '=' which are command shell specific */ + if (*ptr != '=') count++; + len += strlen(ptr) + 1; + } + MSVCRT___initenv = malloc(count * sizeof(*MSVCRT___initenv) + len); + if (!MSVCRT___initenv) + { + FreeEnvironmentStringsA(environ_strings); + return -1; + } + + memcpy(&MSVCRT___initenv[count], environ_strings, len); + for (ptr = (char *)&MSVCRT___initenv[count]; *ptr; ptr += strlen(ptr) + 1) + { + /* Skip special environment strings set by the command shell */ + if (*ptr != '=') MSVCRT___initenv[i++] = ptr; + } + MSVCRT___initenv[i] = NULL; + FreeEnvironmentStringsA(environ_strings); + + MSVCRT__environ = MSVCRT___initenv; + } + + if (!unicode && modif && MSVCRT__environ == MSVCRT___initenv) + { + int i = 0; + + while(MSVCRT___initenv[i]) i++; + MSVCRT__environ = malloc((i + 1) * sizeof(char *)); + if (!MSVCRT__environ) return -1; + for (i = 0; MSVCRT___initenv[i]; i++) + MSVCRT__environ[i] = strdup(MSVCRT___initenv[i]); + MSVCRT__environ[i] = NULL; + } + + if (unicode && !MSVCRT___winitenv) + { + wchar_t *wenviron_strings = GetEnvironmentStringsW(); + int count = 1, len = 1, i = 0; /* keep space for the trailing NULLS */ + wchar_t *wptr; + + for (wptr = wenviron_strings; *wptr; wptr += wcslen(wptr) + 1) + { + /* Don't count environment variables starting with '=' which are command shell specific */ + if (*wptr != '=') count++; + len += wcslen(wptr) + 1; + } + MSVCRT___winitenv = malloc(count * sizeof(*MSVCRT___winitenv) + len * sizeof(wchar_t)); + if (!MSVCRT___winitenv) + { + FreeEnvironmentStringsW(wenviron_strings); + return -1; + } + + memcpy(&MSVCRT___winitenv[count], wenviron_strings, len * sizeof(wchar_t)); + for (wptr = (wchar_t *)&MSVCRT___winitenv[count]; *wptr; wptr += wcslen(wptr) + 1) + { + /* Skip special environment strings set by the command shell */ + if (*wptr != '=') MSVCRT___winitenv[i++] = wptr; + } + MSVCRT___winitenv[i] = NULL; + FreeEnvironmentStringsW(wenviron_strings); + + MSVCRT__wenviron = MSVCRT___winitenv; + } + + if (unicode && modif && MSVCRT__wenviron == MSVCRT___winitenv) + { + int i = 0; + + while(MSVCRT___winitenv[i]) i++; + MSVCRT__wenviron = malloc((i + 1) * sizeof(wchar_t *)); + if (!MSVCRT__wenviron) return -1; + for (i = 0; MSVCRT___winitenv[i]; i++) + MSVCRT__wenviron[i] = wcsdup(MSVCRT___winitenv[i]); + MSVCRT__wenviron[i] = NULL; + } + + return 0; +} + +static int env_get_index(const char *name) +{ + int i, len; + + len = strlen(name); + for (i = 0; MSVCRT__environ[i]; i++) + { + if (!strnicmp(name, MSVCRT__environ[i], len) && MSVCRT__environ[i][len] == '=') + return i; + } + return i; +} + +static int wenv_get_index(const wchar_t *name) +{ + int i, len; + + len = wcslen(name); + for (i = 0; MSVCRT__wenviron[i]; i++) + { + if (!wcsnicmp(name, MSVCRT__wenviron[i], len) && MSVCRT__wenviron[i][len] == '=') + return i; + } + return i; +} + +static int env_set(char **env, wchar_t **wenv) +{ + wchar_t *weq = wcschr(*wenv, '='); + char *eq = strchr(*env, '='); + int idx; + + *weq = 0; + if (!SetEnvironmentVariableW(*wenv, weq[1] ? weq + 1 : NULL) && + GetLastError() != ERROR_ENVVAR_NOT_FOUND) + return -1; + + if (env_init(FALSE, TRUE)) return -1; + *eq = 0; + idx = env_get_index(*env); + *eq = '='; + if (!eq[1]) + { + free(MSVCRT__environ[idx]); + for(; MSVCRT__environ[idx]; idx++) + MSVCRT__environ[idx] = MSVCRT__environ[idx + 1]; + } + else if (MSVCRT__environ[idx]) + { + free(MSVCRT__environ[idx]); + MSVCRT__environ[idx] = *env; + *env = NULL; + } + else + { + char **new_env = realloc(MSVCRT__environ, (idx + 2) * sizeof(*MSVCRT__environ)); + if (!new_env) return -1; + MSVCRT__environ = new_env; + MSVCRT__environ[idx] = *env; + MSVCRT__environ[idx + 1] = NULL; + *env = NULL; + } + + if (!MSVCRT__wenviron) return 0; + if (MSVCRT__wenviron == MSVCRT___winitenv) + if (env_init(TRUE, TRUE)) return -1; + idx = wenv_get_index(*wenv); + *weq = '='; + if (!weq[1]) + { + free(MSVCRT__wenviron[idx]); + for(; MSVCRT__wenviron[idx]; idx++) + MSVCRT__wenviron[idx] = MSVCRT__wenviron[idx + 1]; + } + else if (MSVCRT__wenviron[idx]) + { + free(MSVCRT__wenviron[idx]); + MSVCRT__wenviron[idx] = *wenv; + *wenv = NULL; + } + else + { + wchar_t **new_env = realloc(MSVCRT__wenviron, (idx + 2) * sizeof(*MSVCRT__wenviron)); + if (!new_env) return -1; + MSVCRT__wenviron = new_env; + MSVCRT__wenviron[idx] = *wenv; + MSVCRT__wenviron[idx + 1] = NULL; + *wenv = NULL; + } + return 0; +} + +static char * getenv_helper(const char *name) +{ + int idx; + + if (!name) return NULL; + + idx = env_get_index(name); + if (!MSVCRT__environ[idx]) return NULL; + return strchr(MSVCRT__environ[idx], '=') + 1; +} + +/********************************************************************* + * getenv (MSVCRT.@) + */ +char * CDECL getenv(const char *name) +{ + char *ret; + + if (!MSVCRT_CHECK_PMT(name != NULL)) return NULL; + + _lock(_ENV_LOCK); + ret = getenv_helper(name); + _unlock(_ENV_LOCK); + return ret; +} + +static wchar_t * wgetenv_helper(const wchar_t *name) +{ + int idx; + + if (!name) return NULL; + if (env_init(TRUE, FALSE)) return NULL; + + idx = wenv_get_index(name); + if (!MSVCRT__wenviron[idx]) return NULL; + return wcschr(MSVCRT__wenviron[idx], '=') + 1; +} + +/********************************************************************* + * _wgetenv (MSVCRT.@) + */ +wchar_t * CDECL _wgetenv(const wchar_t *name) +{ + wchar_t *ret; + + if (!MSVCRT_CHECK_PMT(name != NULL)) return NULL; + + _lock(_ENV_LOCK); + ret = wgetenv_helper(name); + _unlock(_ENV_LOCK); + return ret; +} + +static int putenv_helper(const char *name, const char *val, const char *eq) +{ + wchar_t *wenv; + char *env; + int r; + + if (eq) + { + env = strdup(name); + if (!env) return -1; + } + else + { + int name_len = strlen(name); + + r = strlen(val); + env = malloc(name_len + r + 2); + if (!env) return -1; + memcpy(env, name, name_len); + env[name_len] = '='; + strcpy(env + name_len + 1, val); + } + + wenv = msvcrt_wstrdupa(env); + if (!wenv) + { + free(env); + return -1; + } + + _lock(_ENV_LOCK); + r = env_set(&env, &wenv); + _unlock(_ENV_LOCK); + free(env); + free(wenv); + return r; +} + +static char *msvcrt_astrdupw(const wchar_t *wstr) +{ + const unsigned int len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL); + char *str = malloc(len * sizeof(char)); + + if (!str) + return NULL; + WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, len, NULL, NULL); + return str; +} + + +static int wputenv_helper(const wchar_t *name, const wchar_t *val, const wchar_t *eq) +{ + wchar_t *wenv; + char *env; + int r; + + _lock(_ENV_LOCK); + r = env_init(TRUE, TRUE); + _unlock(_ENV_LOCK); + if (r) return -1; + + if (eq) + { + wenv = wcsdup(name); + if (!wenv) return -1; + } + else + { + int name_len = wcslen(name); + + r = wcslen(val); + wenv = malloc((name_len + r + 2) * sizeof(wchar_t)); + if (!wenv) return -1; + memcpy(wenv, name, name_len * sizeof(wchar_t)); + wenv[name_len] = '='; + wcscpy(wenv + name_len + 1, val); + } + + env = msvcrt_astrdupw(wenv); + if (!env) + { + free(wenv); + return -1; + } + + _lock(_ENV_LOCK); + r = env_set(&env, &wenv); + _unlock(_ENV_LOCK); + free(env); + free(wenv); + return r; +} + +/********************************************************************* + * _putenv (MSVCRT.@) + */ +int CDECL _putenv(const char *str) +{ + const char *eq; + + TRACE("%s\n", debugstr_a(str)); + + if (!str || !(eq = strchr(str, '='))) + return -1; + + return putenv_helper(str, NULL, eq); +} + +/********************************************************************* + * _wputenv (MSVCRT.@) + */ +int CDECL _wputenv(const wchar_t *str) +{ + const wchar_t *eq; + + TRACE("%s\n", debugstr_w(str)); + + if (!str || !(eq = wcschr(str, '='))) + return -1; + + return wputenv_helper(str, NULL, eq); +} + +/********************************************************************* + * _putenv_s (MSVCRT.@) + */ +errno_t CDECL _putenv_s(const char *name, const char *value) +{ + errno_t ret = 0; + + TRACE("%s %s\n", debugstr_a(name), debugstr_a(value)); + + if (!MSVCRT_CHECK_PMT(name != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(value != NULL)) return EINVAL; + + if (putenv_helper(name, value, NULL) < 0) + { + msvcrt_set_errno(GetLastError()); + ret = *_errno(); + } + + return ret; +} + +/********************************************************************* + * _wputenv_s (MSVCRT.@) + */ +errno_t CDECL _wputenv_s(const wchar_t *name, const wchar_t *value) +{ + errno_t ret = 0; + + TRACE("%s %s\n", debugstr_w(name), debugstr_w(value)); + + if (!MSVCRT_CHECK_PMT(name != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(value != NULL)) return EINVAL; + + if (wputenv_helper(name, value, NULL) < 0) + { + msvcrt_set_errno(GetLastError()); + ret = *_errno(); + } + + return ret; +} + +#if _MSVCR_VER>=80 + +/****************************************************************** + * _dupenv_s (MSVCR80.@) + */ +int CDECL _dupenv_s(char **buffer, size_t *numberOfElements, const char *varname) +{ + char *e; + size_t sz; + + if (!MSVCRT_CHECK_PMT(buffer != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(varname != NULL)) return EINVAL; + + _lock(_ENV_LOCK); + if (!(e = getenv(varname))) + { + _unlock(_ENV_LOCK); + *buffer = NULL; + if (numberOfElements) *numberOfElements = 0; + return 0; + } + + sz = strlen(e) + 1; + *buffer = malloc(sz); + if (*buffer) strcpy(*buffer, e); + _unlock(_ENV_LOCK); + + if (!*buffer) + { + if (numberOfElements) *numberOfElements = 0; + return *_errno() = ENOMEM; + } + if (numberOfElements) *numberOfElements = sz; + return 0; +} + +/****************************************************************** + * _wdupenv_s (MSVCR80.@) + */ +int CDECL _wdupenv_s(wchar_t **buffer, size_t *numberOfElements, + const wchar_t *varname) +{ + wchar_t *e; + size_t sz; + + if (!MSVCRT_CHECK_PMT(buffer != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(varname != NULL)) return EINVAL; + + _lock(_ENV_LOCK); + if (!(e = _wgetenv(varname))) + { + _unlock(_ENV_LOCK); + *buffer = NULL; + if (numberOfElements) *numberOfElements = 0; + return 0; + } + + sz = wcslen(e) + 1; + *buffer = malloc(sz * sizeof(wchar_t)); + if (*buffer) wcscpy(*buffer, e); + _unlock(_ENV_LOCK); + + if (!*buffer) + { + if (numberOfElements) *numberOfElements = 0; + return *_errno() = ENOMEM; + } + if (numberOfElements) *numberOfElements = sz; + return 0; +} + +#endif /* _MSVCR_VER>=80 */ + +/****************************************************************** + * getenv_s (MSVCRT.@) + */ +int CDECL getenv_s(size_t *ret_len, char* buffer, size_t len, const char *varname) +{ + char *e; + + if (!MSVCRT_CHECK_PMT(ret_len != NULL)) return EINVAL; + *ret_len = 0; + if (!MSVCRT_CHECK_PMT((buffer && len > 0) || (!buffer && !len))) return EINVAL; + if (buffer) buffer[0] = 0; + + _lock(_ENV_LOCK); + e = getenv_helper(varname); + if (e) + { + *ret_len = strlen(e) + 1; + if (len >= *ret_len) strcpy(buffer, e); + } + _unlock(_ENV_LOCK); + + if (!e || !len) return 0; + if (len < *ret_len) return ERANGE; + return 0; +} + +/****************************************************************** + * _wgetenv_s (MSVCRT.@) + */ +int CDECL _wgetenv_s(size_t *ret_len, wchar_t *buffer, size_t len, + const wchar_t *varname) +{ + wchar_t *e; + + if (!MSVCRT_CHECK_PMT(ret_len != NULL)) return EINVAL; + *ret_len = 0; + if (!MSVCRT_CHECK_PMT((buffer && len > 0) || (!buffer && !len))) return EINVAL; + if (buffer) buffer[0] = 0; + + _lock(_ENV_LOCK); + e = wgetenv_helper(varname); + if (e) + { + *ret_len = wcslen(e) + 1; + if (len >= *ret_len) wcscpy(buffer, e); + } + _unlock(_ENV_LOCK); + + if (!e || !len) return 0; + if (len < *ret_len) return ERANGE; + return 0; +} + +/********************************************************************* + * _get_environ (MSVCRT.@) + */ +void CDECL _get_environ(char ***ptr) +{ + *ptr = MSVCRT__environ; +} + +/********************************************************************* + * _get_wenviron (MSVCRT.@) + */ +void CDECL _get_wenviron(wchar_t ***ptr) +{ + *ptr = MSVCRT__wenviron; +} diff --git a/dll/win32/msvcrt/errno.c b/dll/win32/msvcrt/errno.c new file mode 100644 index 00000000000..85752e52df7 --- /dev/null +++ b/dll/win32/msvcrt/errno.c @@ -0,0 +1,554 @@ +/* + * msvcrt.dll errno functions + * + * Copyright 2000 Jon Griffiths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include +#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winternl.h" +#include "msvcrt.h" +#include "winnls.h" +#include "excpt.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +/* error strings generated with glibc strerror */ +static char str_success[] = "Success"; +static char str_EPERM[] = "Operation not permitted"; +static char str_ENOENT[] = "No such file or directory"; +static char str_ESRCH[] = "No such process"; +static char str_EINTR[] = "Interrupted system call"; +static char str_EIO[] = "Input/output error"; +static char str_ENXIO[] = "No such device or address"; +static char str_E2BIG[] = "Argument list too long"; +static char str_ENOEXEC[] = "Exec format error"; +static char str_EBADF[] = "Bad file descriptor"; +static char str_ECHILD[] = "No child processes"; +static char str_EAGAIN[] = "Resource temporarily unavailable"; +static char str_ENOMEM[] = "Cannot allocate memory"; +static char str_EACCES[] = "Permission denied"; +static char str_EFAULT[] = "Bad address"; +static char str_EBUSY[] = "Device or resource busy"; +static char str_EEXIST[] = "File exists"; +static char str_EXDEV[] = "Invalid cross-device link"; +static char str_ENODEV[] = "No such device"; +static char str_ENOTDIR[] = "Not a directory"; +static char str_EISDIR[] = "Is a directory"; +static char str_EINVAL[] = "Invalid argument"; +static char str_ENFILE[] = "Too many open files in system"; +static char str_EMFILE[] = "Too many open files"; +static char str_ENOTTY[] = "Inappropriate ioctl for device"; +static char str_EFBIG[] = "File too large"; +static char str_ENOSPC[] = "No space left on device"; +static char str_ESPIPE[] = "Illegal seek"; +static char str_EROFS[] = "Read-only file system"; +static char str_EMLINK[] = "Too many links"; +static char str_EPIPE[] = "Broken pipe"; +static char str_EDOM[] = "Numerical argument out of domain"; +static char str_ERANGE[] = "Numerical result out of range"; +static char str_EDEADLK[] = "Resource deadlock avoided"; +static char str_ENAMETOOLONG[] = "File name too long"; +static char str_ENOLCK[] = "No locks available"; +static char str_ENOSYS[] = "Function not implemented"; +static char str_ENOTEMPTY[] = "Directory not empty"; +static char str_EILSEQ[] = "Invalid or incomplete multibyte or wide character"; +static char str_generic_error[] = "Unknown error"; + +char *MSVCRT__sys_errlist[] = +{ + str_success, + str_EPERM, + str_ENOENT, + str_ESRCH, + str_EINTR, + str_EIO, + str_ENXIO, + str_E2BIG, + str_ENOEXEC, + str_EBADF, + str_ECHILD, + str_EAGAIN, + str_ENOMEM, + str_EACCES, + str_EFAULT, + str_generic_error, + str_EBUSY, + str_EEXIST, + str_EXDEV, + str_ENODEV, + str_ENOTDIR, + str_EISDIR, + str_EINVAL, + str_ENFILE, + str_EMFILE, + str_ENOTTY, + str_generic_error, + str_EFBIG, + str_ENOSPC, + str_ESPIPE, + str_EROFS, + str_EMLINK, + str_EPIPE, + str_EDOM, + str_ERANGE, + str_generic_error, + str_EDEADLK, + str_generic_error, + str_ENAMETOOLONG, + str_ENOLCK, + str_ENOSYS, + str_ENOTEMPTY, + str_EILSEQ, + str_generic_error +}; + +unsigned int MSVCRT__sys_nerr = ARRAY_SIZE(MSVCRT__sys_errlist) - 1; + +static _invalid_parameter_handler invalid_parameter_handler = NULL; + +/* INTERNAL: Set the crt and dos errno's from the OS error given. */ +void msvcrt_set_errno(int err) +{ + int *errno_ptr = _errno(); + __msvcrt_ulong *doserrno = __doserrno(); + + *doserrno = err; + + switch(err) + { +#define ERR_CASE(oserr) case oserr: +#define ERR_MAPS(oserr, crterr) case oserr: *errno_ptr = crterr; break + ERR_CASE(ERROR_ACCESS_DENIED) + ERR_CASE(ERROR_NETWORK_ACCESS_DENIED) + ERR_CASE(ERROR_CANNOT_MAKE) + ERR_CASE(ERROR_SEEK_ON_DEVICE) + ERR_CASE(ERROR_LOCK_FAILED) + ERR_CASE(ERROR_FAIL_I24) + ERR_CASE(ERROR_CURRENT_DIRECTORY) + ERR_CASE(ERROR_DRIVE_LOCKED) + ERR_CASE(ERROR_NOT_LOCKED) + ERR_CASE(ERROR_INVALID_ACCESS) + ERR_CASE(ERROR_SHARING_VIOLATION) + ERR_MAPS(ERROR_LOCK_VIOLATION, EACCES); + ERR_CASE(ERROR_FILE_NOT_FOUND) + ERR_CASE(ERROR_NO_MORE_FILES) + ERR_CASE(ERROR_BAD_PATHNAME) + ERR_CASE(ERROR_BAD_NETPATH) + ERR_CASE(ERROR_INVALID_DRIVE) + ERR_CASE(ERROR_BAD_NET_NAME) + ERR_CASE(ERROR_FILENAME_EXCED_RANGE) + ERR_MAPS(ERROR_PATH_NOT_FOUND, ENOENT); + ERR_MAPS(ERROR_IO_DEVICE, EIO); + ERR_MAPS(ERROR_BAD_FORMAT, ENOEXEC); + ERR_MAPS(ERROR_INVALID_HANDLE, EBADF); + ERR_CASE(ERROR_OUTOFMEMORY) + ERR_CASE(ERROR_INVALID_BLOCK) + ERR_CASE(ERROR_NOT_ENOUGH_QUOTA) + ERR_MAPS(ERROR_ARENA_TRASHED, ENOMEM); + ERR_MAPS(ERROR_BUSY, EBUSY); + ERR_CASE(ERROR_ALREADY_EXISTS) + ERR_MAPS(ERROR_FILE_EXISTS, EEXIST); + ERR_MAPS(ERROR_BAD_DEVICE, ENODEV); + ERR_MAPS(ERROR_TOO_MANY_OPEN_FILES, EMFILE); + ERR_MAPS(ERROR_DISK_FULL, ENOSPC); + ERR_MAPS(ERROR_BROKEN_PIPE, EPIPE); + ERR_MAPS(ERROR_POSSIBLE_DEADLOCK, EDEADLK); + ERR_MAPS(ERROR_DIR_NOT_EMPTY, ENOTEMPTY); + ERR_MAPS(ERROR_BAD_ENVIRONMENT, E2BIG); + ERR_CASE(ERROR_WAIT_NO_CHILDREN) + ERR_MAPS(ERROR_CHILD_NOT_COMPLETE, ECHILD); + ERR_CASE(ERROR_NO_PROC_SLOTS) + ERR_CASE(ERROR_MAX_THRDS_REACHED) + ERR_MAPS(ERROR_NESTING_NOT_ALLOWED, EAGAIN); + default: + /* Remaining cases map to EINVAL */ + /* FIXME: may be missing some errors above */ + *errno_ptr = EINVAL; + } +} + +#if _MSVCR_VER >= 80 + +/********************************************************************* + * __sys_nerr (MSVCR80.@) + */ +int* CDECL __sys_nerr(void) +{ + return (int*)&MSVCRT__sys_nerr; +} + +/********************************************************************* + * __sys_errlist (MSVCR80.@) + */ +char** CDECL __sys_errlist(void) +{ + return MSVCRT__sys_errlist; +} + +#endif /* _MSVCR_VER >= 80 */ + +/********************************************************************* + * _errno (MSVCRT.@) + */ +int* CDECL _errno(void) +{ + return &msvcrt_get_thread_data()->thread_errno; +} + +/********************************************************************* + * __doserrno (MSVCRT.@) + */ +__msvcrt_ulong* CDECL __doserrno(void) +{ + return &msvcrt_get_thread_data()->thread_doserrno; +} + +/********************************************************************* + * _get_errno (MSVCRT.@) + */ +int CDECL _get_errno(int *pValue) +{ + if (!pValue) + return EINVAL; + + *pValue = *_errno(); + return 0; +} + +/********************************************************************* + * _get_doserrno (MSVCRT.@) + */ +int CDECL _get_doserrno(int *pValue) +{ + if (!pValue) + return EINVAL; + + *pValue = *__doserrno(); + return 0; +} + +/********************************************************************* + * _set_errno (MSVCRT.@) + */ +int CDECL _set_errno(int value) +{ + *_errno() = value; + return 0; +} + +/********************************************************************* + * _set_doserrno (MSVCRT.@) + */ +int CDECL _set_doserrno(int value) +{ + *__doserrno() = value; + return 0; +} + +/********************************************************************* + * strerror (MSVCRT.@) + */ +char* CDECL strerror(int err) +{ + thread_data_t *data = msvcrt_get_thread_data(); + + if (!data->strerror_buffer) + if (!(data->strerror_buffer = malloc(256))) return NULL; + + if (err < 0 || err > MSVCRT__sys_nerr) err = MSVCRT__sys_nerr; + strcpy( data->strerror_buffer, MSVCRT__sys_errlist[err] ); + return data->strerror_buffer; +} + +/********************************************************************** + * strerror_s (MSVCRT.@) + */ +int CDECL strerror_s(char *buffer, size_t numberOfElements, int errnum) +{ + char *ptr; + + if (!buffer || !numberOfElements) + { + *_errno() = EINVAL; + return EINVAL; + } + + if (errnum < 0 || errnum > MSVCRT__sys_nerr) + errnum = MSVCRT__sys_nerr; + + ptr = MSVCRT__sys_errlist[errnum]; + while (*ptr && numberOfElements > 1) + { + *buffer++ = *ptr++; + numberOfElements--; + } + + *buffer = '\0'; + return 0; +} + +/********************************************************************** + * _strerror (MSVCRT.@) + */ +char* CDECL _strerror(const char* str) +{ + thread_data_t *data = msvcrt_get_thread_data(); + int err; + + if (!data->strerror_buffer) + if (!(data->strerror_buffer = malloc(256))) return NULL; + + err = data->thread_errno; + if (err < 0 || err > MSVCRT__sys_nerr) err = MSVCRT__sys_nerr; + + if (str && *str) + sprintf( data->strerror_buffer, "%s: %s\n", str, MSVCRT__sys_errlist[err] ); + else + sprintf( data->strerror_buffer, "%s\n", MSVCRT__sys_errlist[err] ); + + return data->strerror_buffer; +} + +/********************************************************************* + * perror (MSVCRT.@) + */ +void CDECL perror(const char* str) +{ + int err = *_errno(); + if (err < 0 || err > MSVCRT__sys_nerr) err = MSVCRT__sys_nerr; + + if (str && *str) + { + _write( 2, str, strlen(str) ); + _write( 2, ": ", 2 ); + } + _write( 2, MSVCRT__sys_errlist[err], strlen(MSVCRT__sys_errlist[err]) ); + _write( 2, "\n", 1 ); +} + +/********************************************************************* + * _wperror (MSVCRT.@) + */ +void CDECL _wperror(const wchar_t* str) +{ + size_t size; + char *buffer = NULL; + + if (str && *str) + { + size = wcstombs(NULL, str, 0); + if (size == -1) return; + size++; + buffer = malloc(size); + if (!buffer) return; + if (wcstombs(buffer, str, size) == -1) + { + free(buffer); + return; + } + } + perror(buffer); + free(buffer); +} + +/********************************************************************* + * _wcserror_s (MSVCRT.@) + */ +int CDECL _wcserror_s(wchar_t* buffer, size_t nc, int err) +{ + if (!MSVCRT_CHECK_PMT(buffer != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(nc > 0)) return EINVAL; + + if (err < 0 || err > MSVCRT__sys_nerr) err = MSVCRT__sys_nerr; + MultiByteToWideChar(CP_ACP, 0, MSVCRT__sys_errlist[err], -1, buffer, nc); + return 0; +} + +/********************************************************************* + * _wcserror (MSVCRT.@) + */ +wchar_t* CDECL _wcserror(int err) +{ + thread_data_t *data = msvcrt_get_thread_data(); + + if (!data->wcserror_buffer) + if (!(data->wcserror_buffer = malloc(256 * sizeof(wchar_t)))) return NULL; + _wcserror_s(data->wcserror_buffer, 256, err); + return data->wcserror_buffer; +} + +/********************************************************************** + * __wcserror_s (MSVCRT.@) + */ +int CDECL __wcserror_s(wchar_t* buffer, size_t nc, const wchar_t* str) +{ + int err; + size_t len; + + err = *_errno(); + if (err < 0 || err > MSVCRT__sys_nerr) err = MSVCRT__sys_nerr; + + len = MultiByteToWideChar(CP_ACP, 0, MSVCRT__sys_errlist[err], -1, NULL, 0) + 1 /* \n */; + if (str && *str) len += wcslen(str) + 2 /* ': ' */; + if (len > nc) + { + MSVCRT_INVALID_PMT("buffer[nc] is too small", ERANGE); + return ERANGE; + } + if (str && *str) + { + lstrcpyW(buffer, str); + lstrcatW(buffer, L": "); + } + else buffer[0] = '\0'; + len = wcslen(buffer); + MultiByteToWideChar(CP_ACP, 0, MSVCRT__sys_errlist[err], -1, buffer + len, 256 - len); + lstrcatW(buffer, L"\n"); + + return 0; +} + +/********************************************************************** + * __wcserror (MSVCRT.@) + */ +wchar_t* CDECL __wcserror(const wchar_t* str) +{ + thread_data_t *data = msvcrt_get_thread_data(); + int err; + + if (!data->wcserror_buffer) + if (!(data->wcserror_buffer = malloc(256 * sizeof(wchar_t)))) return NULL; + + err = __wcserror_s(data->wcserror_buffer, 256, str); + if (err) FIXME("bad wcserror call (%d)\n", err); + + return data->wcserror_buffer; +} + +/****************************************************************************** + * _seterrormode (MSVCRT.@) + */ +void CDECL _seterrormode(int mode) +{ + SetErrorMode( mode ); +} + +/****************************************************************************** + * _invalid_parameter (MSVCRT.@) + */ +void __cdecl _invalid_parameter(const wchar_t *expr, const wchar_t *func, + const wchar_t *file, unsigned int line, uintptr_t arg) +{ +#if _MSVCR_VER >= 140 + thread_data_t *data = msvcrt_get_thread_data(); + + if (data->invalid_parameter_handler) + { + data->invalid_parameter_handler( expr, func, file, line, arg ); + return; + } +#endif + + if (invalid_parameter_handler) invalid_parameter_handler( expr, func, file, line, arg ); + else + { + ERR( "%s:%u %s: %s %Ix\n", debugstr_w(file), line, debugstr_w(func), debugstr_w(expr), arg ); +#if _MSVCR_VER >= 80 + RaiseException( STATUS_INVALID_CRUNTIME_PARAMETER, EXCEPTION_NONCONTINUABLE, 0, NULL ); +#endif + } +} + +#if _MSVCR_VER >= 80 + +/********************************************************************* + * _invalid_parameter_noinfo (MSVCR80.@) + */ +void CDECL _invalid_parameter_noinfo(void) +{ + _invalid_parameter( NULL, NULL, NULL, 0, 0 ); +} + +/********************************************************************* + * _invalid_parameter_noinfo_noreturn (MSVCR80.@) + */ +void CDECL _invalid_parameter_noinfo_noreturn(void) +{ + _invalid_parameter( NULL, NULL, NULL, 0, 0 ); + _exit( STATUS_INVALID_CRUNTIME_PARAMETER ); +} + +/********************************************************************* + * _get_invalid_parameter_handler (MSVCR80.@) + */ +_invalid_parameter_handler CDECL _get_invalid_parameter_handler(void) +{ + TRACE("\n"); + return invalid_parameter_handler; +} + +/********************************************************************* + * _set_invalid_parameter_handler (MSVCR80.@) + */ +_invalid_parameter_handler CDECL _set_invalid_parameter_handler( + _invalid_parameter_handler handler) +{ + _invalid_parameter_handler old = invalid_parameter_handler; + + TRACE("(%p)\n", handler); + + invalid_parameter_handler = handler; + return old; +} + +#endif /* _MSVCR_VER >= 80 */ + +#if _MSVCR_VER >= 140 + +/********************************************************************* + * _get_thread_local_invalid_parameter_handler (UCRTBASE.@) + */ +_invalid_parameter_handler CDECL _get_thread_local_invalid_parameter_handler(void) +{ + TRACE("\n"); + return msvcrt_get_thread_data()->invalid_parameter_handler; +} + +/********************************************************************* + * _set_thread_local_invalid_parameter_handler (UCRTBASE.@) + */ +_invalid_parameter_handler CDECL _set_thread_local_invalid_parameter_handler( + _invalid_parameter_handler handler) +{ + thread_data_t *data = msvcrt_get_thread_data(); + _invalid_parameter_handler old = data->invalid_parameter_handler; + + TRACE("(%p)\n", handler); + + data->invalid_parameter_handler = handler; + return old; +} + +#endif /* _MSVCR_VER >= 140 */ diff --git a/dll/win32/msvcrt/except.c b/dll/win32/msvcrt/except.c new file mode 100644 index 00000000000..50f131efdc7 --- /dev/null +++ b/dll/win32/msvcrt/except.c @@ -0,0 +1,1073 @@ +/* + * msvcrt.dll exception handling + * + * Copyright 2000 Jon Griffiths + * Copyright 2005 Juan Lang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + * + * FIXME: Incomplete support for nested exceptions/try block cleanup. + */ + +#include +#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "msvcrt.h" +#include "excpt.h" +#include "wincon.h" +#include "wine/exception.h" +#include "wine/debug.h" + +#include "cppexcept.h" + +WINE_DEFAULT_DEBUG_CHANNEL(seh); + +#if _MSVCR_VER>=70 && _MSVCR_VER<=71 +static MSVCRT_security_error_handler security_error_handler; +#endif + +static __sighandler_t sighandlers[NSIG] = { SIG_DFL }; + +void dump_function_descr( const cxx_function_descr *descr, uintptr_t base ) +{ + unwind_info *unwind_table = rtti_rva( descr->unwind_table, base ); + tryblock_info *tryblock = rtti_rva( descr->tryblock, base ); + ipmap_info *ipmap = rtti_rva( descr->ipmap, base ); + UINT i, j; + + TRACE( "magic %x\n", descr->magic ); + TRACE( "unwind table: %p %d\n", unwind_table, descr->unwind_count ); + for (i = 0; i < descr->unwind_count; i++) + { + TRACE(" %d: prev %d func %p\n", i, unwind_table[i].prev, + unwind_table[i].handler ? rtti_rva( unwind_table[i].handler, base ) : NULL ); + } + TRACE( "try table: %p %d\n", tryblock, descr->tryblock_count ); + for (i = 0; i < descr->tryblock_count; i++) + { + catchblock_info *catchblock = rtti_rva( tryblock[i].catchblock, base ); + + TRACE( " %d: start %d end %d catchlevel %d catch %p %d\n", i, + tryblock[i].start_level, tryblock[i].end_level, + tryblock[i].catch_level, catchblock, tryblock[i].catchblock_count); + for (j = 0; j < tryblock[i].catchblock_count; j++) + { + type_info *type_info = catchblock[j].type_info ? rtti_rva( catchblock[j].type_info, base ) : NULL; + TRACE( " %d: flags %x offset %d handler %p", + j, catchblock[j].flags, catchblock[j].offset, + catchblock[j].handler ? rtti_rva(catchblock[j].handler, base) : NULL ); +#ifdef _WIN64 + TRACE( " frame %x", catchblock[j].frame ); +#endif + TRACE( " type %p %s\n", type_info, dbgstr_type_info(type_info) ); + } + } + TRACE( "ipmap: %p %d\n", ipmap, descr->ipmap_count ); + for (i = 0; i < descr->ipmap_count; i++) + TRACE( " %d: ip %x state %d\n", i, ipmap[i].ip, ipmap[i].state ); +#ifdef RTTI_USE_RVA + TRACE( "unwind_help %+d\n", descr->unwind_help ); +#endif + if (descr->magic <= CXX_FRAME_MAGIC_VC6) return; + TRACE( "expect list: %p\n", rtti_rva( descr->expect_list, base ) ); + if (descr->magic <= CXX_FRAME_MAGIC_VC7) return; + TRACE( "flags: %08x\n", descr->flags ); +} + +void *find_catch_handler( void *object, uintptr_t frame, uintptr_t exc_base, + const tryblock_info *tryblock, + cxx_exception_type *exc_type, uintptr_t image_base ) +{ + unsigned int i; + const catchblock_info *catchblock = rtti_rva( tryblock->catchblock, image_base ); + const cxx_type_info *type; + const type_info *catch_ti; + + for (i = 0; i < tryblock->catchblock_count; i++) + { + if (exc_type) + { + catch_ti = catchblock[i].type_info ? rtti_rva( catchblock[i].type_info, image_base ) : NULL; + type = find_caught_type( exc_type, exc_base, catch_ti, catchblock[i].flags ); + if (!type) continue; + + TRACE( "matched type %p in catchblock %d\n", type, i ); + + if (catch_ti && catch_ti->mangled[0] && catchblock[i].offset) + { + /* copy the exception to its destination on the stack */ + void **dest = (void **)(frame + catchblock[i].offset); + copy_exception( object, dest, catchblock[i].flags, type, exc_base ); + } + } + else + { + /* no CXX_EXCEPTION only proceed with a catch(...) block*/ + if (catchblock[i].type_info) continue; + TRACE( "found catch(...) block\n" ); + } + return rtti_rva( catchblock[i].handler, image_base ); + } + return NULL; +} + +#ifndef __i386__ /* i386 implementation is in except_i386.c */ + +typedef struct +{ + cxx_frame_info frame_info; + BOOL rethrow; + EXCEPTION_RECORD *prev_rec; +} cxx_catch_ctx; + +typedef struct +{ + ULONG_PTR dest_frame; + ULONG_PTR orig_frame; + EXCEPTION_RECORD *seh_rec; + DISPATCHER_CONTEXT *dispatch; + const cxx_function_descr *descr; +} se_translator_ctx; + +static inline int ip_to_state( const cxx_function_descr *descr, uintptr_t ip, uintptr_t base ) +{ + const ipmap_info *ipmap = rtti_rva( descr->ipmap, base ); + unsigned int i; + int ret; + + for (i = 0; i < descr->ipmap_count; i++) if (base + ipmap[i].ip > ip) break; + ret = i ? ipmap[i - 1].state : -1; + TRACE( "%Ix -> %d\n", ip, ret ); + return ret; +} + +static void cxx_local_unwind(ULONG_PTR frame, DISPATCHER_CONTEXT *dispatch, + const cxx_function_descr *descr, int last_level) +{ + const unwind_info *unwind_table = rtti_rva(descr->unwind_table, dispatch->ImageBase); + int *unwind_help = (int *)(frame + descr->unwind_help); + int trylevel = unwind_help[0]; + + if (trylevel == -2) trylevel = ip_to_state( descr, get_exception_pc(dispatch), dispatch->ImageBase ); + + TRACE("current level: %d, last level: %d\n", trylevel, last_level); + while (trylevel > last_level) + { + if (trylevel<0 || trylevel>=descr->unwind_count) + { + ERR("invalid trylevel %d\n", trylevel); + terminate(); + } + if (unwind_table[trylevel].handler) + { + void *handler = rtti_rva( unwind_table[trylevel].handler, dispatch->ImageBase ); + call_unwind_handler( handler, frame, dispatch ); + } + trylevel = unwind_table[trylevel].prev; + } + unwind_help[0] = trylevel; +} + +static LONG CALLBACK cxx_rethrow_filter(PEXCEPTION_POINTERS eptrs, void *c) +{ + EXCEPTION_RECORD *rec = eptrs->ExceptionRecord; + cxx_catch_ctx *ctx = c; + + if (rec->ExceptionCode != CXX_EXCEPTION) + return EXCEPTION_CONTINUE_SEARCH; + if (!rec->ExceptionInformation[1] && !rec->ExceptionInformation[2]) + return EXCEPTION_EXECUTE_HANDLER; + if (rec->ExceptionInformation[1] == ctx->prev_rec->ExceptionInformation[1]) + ctx->rethrow = TRUE; + return EXCEPTION_CONTINUE_SEARCH; +} + +static void CALLBACK cxx_catch_cleanup(BOOL normal, void *c) +{ + cxx_catch_ctx *ctx = c; + __CxxUnregisterExceptionObject(&ctx->frame_info, ctx->rethrow); +} + +static void* WINAPI call_catch_block(EXCEPTION_RECORD *rec) +{ + ULONG_PTR frame = rec->ExceptionInformation[1]; + const cxx_function_descr *descr = (void*)rec->ExceptionInformation[2]; + EXCEPTION_RECORD *untrans_rec = (void*)rec->ExceptionInformation[4]; + EXCEPTION_RECORD *prev_rec = (void*)rec->ExceptionInformation[6]; + CONTEXT *context = (void*)rec->ExceptionInformation[7]; + int *unwind_help = (int *)(frame + descr->unwind_help); + EXCEPTION_POINTERS ep = { prev_rec, context }; + cxx_catch_ctx ctx; + void *ret_addr = NULL; + + ctx.rethrow = FALSE; + ctx.prev_rec = prev_rec; + __CxxRegisterExceptionObject(&ep, &ctx.frame_info); + msvcrt_get_thread_data()->processing_throw--; + __TRY + { + __TRY + { + ret_addr = call_catch_handler( rec ); + } + __EXCEPT_CTX(cxx_rethrow_filter, &ctx) + { + TRACE("detect rethrow: exception code: %lx\n", prev_rec->ExceptionCode); + ctx.rethrow = TRUE; + + if (untrans_rec) + { + __DestructExceptionObject(prev_rec); + RaiseException(untrans_rec->ExceptionCode, untrans_rec->ExceptionFlags, + untrans_rec->NumberParameters, untrans_rec->ExceptionInformation); + } + else + { + RaiseException(prev_rec->ExceptionCode, prev_rec->ExceptionFlags, + prev_rec->NumberParameters, prev_rec->ExceptionInformation); + } + } + __ENDTRY + } + __FINALLY_CTX(cxx_catch_cleanup, &ctx) + + unwind_help[0] = -2; + unwind_help[1] = -1; + return ret_addr; +} + +static inline BOOL cxx_is_consolidate(const EXCEPTION_RECORD *rec) +{ + return (rec->ExceptionCode == STATUS_UNWIND_CONSOLIDATE && + rec->NumberParameters > 10 && + rec->ExceptionInformation[0] == (ULONG_PTR)call_catch_block); +} + +static inline void find_catch_block(EXCEPTION_RECORD *rec, CONTEXT *context, + EXCEPTION_RECORD *untrans_rec, + ULONG_PTR frame, DISPATCHER_CONTEXT *dispatch, + const cxx_function_descr *descr, + cxx_exception_type *info, ULONG_PTR orig_frame) +{ + ULONG_PTR exc_base = (rec->NumberParameters == 4 ? rec->ExceptionInformation[3] : 0); + void *handler, *object = (void *)rec->ExceptionInformation[1]; + int trylevel = ip_to_state( descr, get_exception_pc(dispatch), dispatch->ImageBase ); + thread_data_t *data = msvcrt_get_thread_data(); + const tryblock_info *in_catch; + EXCEPTION_RECORD catch_record; + CONTEXT ctx; + UINT i; + INT *unwind_help; + + data->processing_throw++; + for (i=descr->tryblock_count; i>0; i--) + { + in_catch = rtti_rva(descr->tryblock, dispatch->ImageBase); + in_catch = &in_catch[i-1]; + + if (trylevel>in_catch->end_level && trylevel<=in_catch->catch_level) + break; + } + if (!i) + in_catch = NULL; + + unwind_help = (int *)(orig_frame + descr->unwind_help); + if (trylevel > unwind_help[1]) + unwind_help[0] = unwind_help[1] = trylevel; + else + trylevel = unwind_help[1]; + TRACE("current trylevel: %d\n", trylevel); + + for (i=0; itryblock_count; i++) + { + const tryblock_info *tryblock = rtti_rva(descr->tryblock, dispatch->ImageBase); + tryblock = &tryblock[i]; + + if (trylevel < tryblock->start_level) continue; + if (trylevel > tryblock->end_level) continue; + + if (in_catch) + { + if(tryblock->start_level <= in_catch->end_level) continue; + if(tryblock->end_level > in_catch->catch_level) continue; + } + + handler = find_catch_handler( object, orig_frame, exc_base, tryblock, info, dispatch->ImageBase ); + if (!handler) continue; + + /* unwind stack and call catch */ + memset(&catch_record, 0, sizeof(catch_record)); + catch_record.ExceptionCode = STATUS_UNWIND_CONSOLIDATE; + catch_record.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + catch_record.NumberParameters = 11; + catch_record.ExceptionInformation[0] = (ULONG_PTR)call_catch_block; + catch_record.ExceptionInformation[1] = orig_frame; + catch_record.ExceptionInformation[2] = (ULONG_PTR)descr; + catch_record.ExceptionInformation[3] = tryblock->start_level; + catch_record.ExceptionInformation[4] = (ULONG_PTR)untrans_rec; + catch_record.ExceptionInformation[5] = (ULONG_PTR)handler; + /* tyFlow expects ExceptionInformation[6] to contain exception record */ + catch_record.ExceptionInformation[6] = (ULONG_PTR)rec; + catch_record.ExceptionInformation[7] = (ULONG_PTR)context; + catch_record.ExceptionInformation[10] = -1; + RtlUnwindEx((void*)frame, (void*)dispatch->ControlPc, &catch_record, NULL, &ctx, NULL); + } + + TRACE("no matching catch block found\n"); + data->processing_throw--; +} + +static LONG CALLBACK se_translation_filter(EXCEPTION_POINTERS *ep, void *c) +{ + se_translator_ctx *ctx = (se_translator_ctx *)c; + EXCEPTION_RECORD *rec = ep->ExceptionRecord; + cxx_exception_type *exc_type; + + if (rec->ExceptionCode != CXX_EXCEPTION) + { + TRACE("non-c++ exception thrown in SEH handler: %lx\n", rec->ExceptionCode); + terminate(); + } + + exc_type = (cxx_exception_type *)rec->ExceptionInformation[2]; + find_catch_block(rec, ep->ContextRecord, ctx->seh_rec, ctx->dest_frame, ctx->dispatch, + ctx->descr, exc_type, ctx->orig_frame); + + __DestructExceptionObject(rec); + return ExceptionContinueSearch; +} + +static void check_noexcept( PEXCEPTION_RECORD rec, + const cxx_function_descr *descr, BOOL nested ) +{ + if (!nested && rec->ExceptionCode == CXX_EXCEPTION && + descr->magic >= CXX_FRAME_MAGIC_VC8 && + (descr->flags & FUNC_DESCR_NOEXCEPT)) + { + ERR("noexcept function propagating exception\n"); + terminate(); + } +} + +static DWORD cxx_frame_handler(EXCEPTION_RECORD *rec, ULONG_PTR frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatch, + const cxx_function_descr *descr) +{ + int trylevel = ip_to_state( descr, get_exception_pc(dispatch), dispatch->ImageBase ); + cxx_exception_type *exc_type; + ULONG_PTR orig_frame = frame; + ULONG_PTR throw_base; + DWORD throw_func_off; + void *throw_func; + UINT i, j; + int unwindlevel = -1; + + if (descr->magicmagic>CXX_FRAME_MAGIC_VC8) + { + FIXME("unhandled frame magic %x\n", descr->magic); + return ExceptionContinueSearch; + } + + if (descr->magic >= CXX_FRAME_MAGIC_VC8 && + (descr->flags & FUNC_DESCR_SYNCHRONOUS) && + (rec->ExceptionCode != CXX_EXCEPTION && + !cxx_is_consolidate(rec) && + rec->ExceptionCode != STATUS_LONGJUMP)) + return ExceptionContinueSearch; /* handle only c++ exceptions */ + + /* update orig_frame if it's a nested exception */ + throw_func_off = RtlLookupFunctionEntry(dispatch->ControlPc, &throw_base, NULL)->BeginAddress; + throw_func = rtti_rva(throw_func_off, throw_base); + TRACE("reconstructed handler pointer: %p\n", throw_func); + for (i=descr->tryblock_count; i>0; i--) + { + const tryblock_info *tryblock = rtti_rva(descr->tryblock, dispatch->ImageBase); + tryblock = &tryblock[i-1]; + + if (trylevel>tryblock->end_level && trylevel<=tryblock->catch_level) + { + for (j=0; jcatchblock_count; j++) + { + const catchblock_info *catchblock = rtti_rva(tryblock->catchblock, dispatch->ImageBase); + catchblock = &catchblock[j]; + + if (rtti_rva(catchblock->handler, dispatch->ImageBase) == throw_func) + { + unwindlevel = tryblock->end_level; +#ifdef _WIN64 + orig_frame = *(ULONG_PTR *)(frame + catchblock->frame); +#else + orig_frame = *(ULONG_PTR *)frame; +#endif + TRACE("nested exception detected, setting orig_frame to %Ix\n", orig_frame); + } + } + } + } + + if (rec->ExceptionFlags & (EXCEPTION_UNWINDING|EXCEPTION_EXIT_UNWIND)) + { + if (rec->ExceptionFlags & EXCEPTION_TARGET_UNWIND) + cxx_local_unwind(orig_frame, dispatch, descr, + cxx_is_consolidate(rec) ? rec->ExceptionInformation[3] : trylevel); + else + cxx_local_unwind(orig_frame, dispatch, descr, unwindlevel); + return ExceptionContinueSearch; + } + if (!descr->tryblock_count) + { + check_noexcept(rec, descr, orig_frame != frame); + return ExceptionContinueSearch; + } + + if (rec->ExceptionCode == CXX_EXCEPTION && + (!rec->ExceptionInformation[1] && !rec->ExceptionInformation[2])) + { + TRACE("rethrow detected.\n"); + *rec = *msvcrt_get_thread_data()->exc_record; + } + if (rec->ExceptionCode == CXX_EXCEPTION) + { + exc_type = (cxx_exception_type *)rec->ExceptionInformation[2]; + + if (TRACE_ON(seh)) + { + TRACE("handling C++ exception rec %p frame %Ix descr %p\n", rec, frame, descr); + TRACE_EXCEPTION_TYPE(exc_type, rec->ExceptionInformation[3]); + dump_function_descr(descr, dispatch->ImageBase); + } + } + else + { + thread_data_t *data = msvcrt_get_thread_data(); + + exc_type = NULL; + TRACE("handling C exception code %lx rec %p frame %Ix descr %p\n", + rec->ExceptionCode, rec, frame, descr); + + if (data->se_translator) { + EXCEPTION_POINTERS except_ptrs; + se_translator_ctx ctx; + + ctx.dest_frame = frame; + ctx.orig_frame = orig_frame; + ctx.seh_rec = rec; + ctx.dispatch = dispatch; + ctx.descr = descr; + __TRY + { + except_ptrs.ExceptionRecord = rec; + except_ptrs.ContextRecord = context; + data->se_translator(rec->ExceptionCode, &except_ptrs); + } + __EXCEPT_CTX(se_translation_filter, &ctx) + { + } + __ENDTRY + } + } + + find_catch_block(rec, context, NULL, frame, dispatch, descr, exc_type, orig_frame); + check_noexcept(rec, descr, orig_frame != frame); + return ExceptionContinueSearch; +} + +/********************************************************************* + * __CxxFrameHandler (MSVCRT.@) + */ +EXCEPTION_DISPOSITION CDECL __CxxFrameHandler( EXCEPTION_RECORD *rec, ULONG_PTR frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatch ) +{ + TRACE( "%p %Ix %p %p\n", rec, frame, context, dispatch ); + return cxx_frame_handler( rec, frame, context, dispatch, + rtti_rva(*(UINT *)dispatch->HandlerData, dispatch->ImageBase) ); +} + +#endif /* __i386__ */ + +static BOOL WINAPI msvcrt_console_handler(DWORD ctrlType) +{ + BOOL ret = FALSE; + + switch (ctrlType) + { + case CTRL_C_EVENT: + if (sighandlers[SIGINT]) + { + if (sighandlers[SIGINT] != SIG_IGN) + sighandlers[SIGINT](SIGINT); + ret = TRUE; + } + break; + } + return ret; +} + +/********************************************************************* + * __pxcptinfoptrs (MSVCRT.@) + */ +void** CDECL __pxcptinfoptrs(void) +{ + return (void**)&msvcrt_get_thread_data()->xcptinfo; +} + +typedef void (CDECL *float_handler)(int, int); + +/* The exception codes are actually NTSTATUS values */ +static const struct +{ + NTSTATUS status; + int signal; +} float_exception_map[] = { + { EXCEPTION_FLT_DENORMAL_OPERAND, _FPE_DENORMAL }, + { EXCEPTION_FLT_DIVIDE_BY_ZERO, _FPE_ZERODIVIDE }, + { EXCEPTION_FLT_INEXACT_RESULT, _FPE_INEXACT }, + { EXCEPTION_FLT_INVALID_OPERATION, _FPE_INVALID }, + { EXCEPTION_FLT_OVERFLOW, _FPE_OVERFLOW }, + { EXCEPTION_FLT_STACK_CHECK, _FPE_STACKOVERFLOW }, + { EXCEPTION_FLT_UNDERFLOW, _FPE_UNDERFLOW }, +}; + +static LONG msvcrt_exception_filter(struct _EXCEPTION_POINTERS *except) +{ + LONG ret = EXCEPTION_CONTINUE_SEARCH; + __sighandler_t handler; + + if (!except || !except->ExceptionRecord) + return EXCEPTION_CONTINUE_SEARCH; + + switch (except->ExceptionRecord->ExceptionCode) + { + case EXCEPTION_ACCESS_VIOLATION: + if ((handler = sighandlers[SIGSEGV]) != SIG_DFL) + { + if (handler != SIG_IGN) + { + EXCEPTION_POINTERS **ep = (EXCEPTION_POINTERS**)__pxcptinfoptrs(), *old_ep; + + old_ep = *ep; + *ep = except; + sighandlers[SIGSEGV] = SIG_DFL; + handler(SIGSEGV); + *ep = old_ep; + } + ret = EXCEPTION_CONTINUE_EXECUTION; + } + break; + /* According to msdn, + * the FPE signal handler takes as a second argument the type of + * floating point exception. + */ + case EXCEPTION_FLT_DENORMAL_OPERAND: + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + case EXCEPTION_FLT_INEXACT_RESULT: + case EXCEPTION_FLT_INVALID_OPERATION: + case EXCEPTION_FLT_OVERFLOW: + case EXCEPTION_FLT_STACK_CHECK: + case EXCEPTION_FLT_UNDERFLOW: + if ((handler = sighandlers[SIGFPE]) != SIG_DFL) + { + if (handler != SIG_IGN) + { + EXCEPTION_POINTERS **ep = (EXCEPTION_POINTERS**)__pxcptinfoptrs(), *old_ep; + unsigned int i; + int float_signal = _FPE_INVALID; + + sighandlers[SIGFPE] = SIG_DFL; + for (i = 0; i < ARRAY_SIZE(float_exception_map); i++) + { + if (float_exception_map[i].status == + except->ExceptionRecord->ExceptionCode) + { + float_signal = float_exception_map[i].signal; + break; + } + } + + old_ep = *ep; + *ep = except; + ((float_handler)handler)(SIGFPE, float_signal); + *ep = old_ep; + } + ret = EXCEPTION_CONTINUE_EXECUTION; + } + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + case EXCEPTION_PRIV_INSTRUCTION: + if ((handler = sighandlers[SIGILL]) != SIG_DFL) + { + if (handler != SIG_IGN) + { + EXCEPTION_POINTERS **ep = (EXCEPTION_POINTERS**)__pxcptinfoptrs(), *old_ep; + + old_ep = *ep; + *ep = except; + sighandlers[SIGILL] = SIG_DFL; + handler(SIGILL); + *ep = old_ep; + } + ret = EXCEPTION_CONTINUE_EXECUTION; + } + break; + } + return ret; +} + +void msvcrt_init_signals(void) +{ + SetConsoleCtrlHandler(msvcrt_console_handler, TRUE); +} + +void msvcrt_free_signals(void) +{ + SetConsoleCtrlHandler(msvcrt_console_handler, FALSE); +} + +/********************************************************************* + * signal (MSVCRT.@) + * Some signals may never be generated except through an explicit call to + * raise. + */ +__sighandler_t CDECL signal(int sig, __sighandler_t func) +{ + __sighandler_t ret = SIG_ERR; + + TRACE("(%d, %p)\n", sig, func); + + if (func == SIG_ERR) return SIG_ERR; + + switch (sig) + { + /* Cases handled internally. Note SIGTERM is never generated by Windows, + * so we effectively mask it. + */ + case SIGABRT: + case SIGFPE: + case SIGILL: + case SIGSEGV: + case SIGINT: + case SIGTERM: + case SIGBREAK: + ret = sighandlers[sig]; + sighandlers[sig] = func; + break; + default: + ret = SIG_ERR; + } + return ret; +} + +/********************************************************************* + * raise (MSVCRT.@) + */ +int CDECL raise(int sig) +{ + __sighandler_t handler; + + TRACE("(%d)\n", sig); + + switch (sig) + { + case SIGFPE: + case SIGILL: + case SIGSEGV: + handler = sighandlers[sig]; + if (handler == SIG_DFL) _exit(3); + if (handler != SIG_IGN) + { + EXCEPTION_POINTERS **ep = (EXCEPTION_POINTERS**)__pxcptinfoptrs(), *old_ep; + + sighandlers[sig] = SIG_DFL; + + old_ep = *ep; + *ep = NULL; + if (sig == SIGFPE) + ((float_handler)handler)(sig, _FPE_EXPLICITGEN); + else + handler(sig); + *ep = old_ep; + } + break; + case SIGABRT: + case SIGINT: + case SIGTERM: + case SIGBREAK: + handler = sighandlers[sig]; + if (handler == SIG_DFL) _exit(3); + if (handler != SIG_IGN) + { + sighandlers[sig] = SIG_DFL; + handler(sig); + } + break; + default: + return -1; + } + return 0; +} + +/********************************************************************* + * _XcptFilter (MSVCRT.@) + */ +int CDECL _XcptFilter(NTSTATUS ex, PEXCEPTION_POINTERS ptr) +{ + TRACE("(%08lx,%p)\n", ex, ptr); + /* I assume ptr->ExceptionRecord->ExceptionCode is the same as ex */ + return msvcrt_exception_filter(ptr); +} + +/********************************************************************* + * __CppXcptFilter (MSVCRT.@) + */ +int CDECL __CppXcptFilter(NTSTATUS ex, PEXCEPTION_POINTERS ptr) +{ + /* only filter c++ exceptions */ + if (ex != CXX_EXCEPTION) return EXCEPTION_CONTINUE_SEARCH; + return _XcptFilter(ex, ptr); +} + +/********************************************************************* + * __CxxDetectRethrow (MSVCRT.@) + */ +BOOL CDECL __CxxDetectRethrow(PEXCEPTION_POINTERS ptrs) +{ + PEXCEPTION_RECORD rec; + + if (!ptrs) + return FALSE; + + rec = ptrs->ExceptionRecord; + if (is_cxx_exception( rec ) && rec->ExceptionInformation[2]) + { + ptrs->ExceptionRecord = msvcrt_get_thread_data()->exc_record; + return TRUE; + } + return (msvcrt_get_thread_data()->exc_record == rec); +} + +/********************************************************************* + * __CxxExceptionFilter (MSVCRT.@) + */ +int CDECL __CxxExceptionFilter( EXCEPTION_POINTERS *ptrs, const type_info *ti, UINT flags, void **copy) +{ + const cxx_type_info *type; + EXCEPTION_RECORD *rec; + uintptr_t exc_base; + + TRACE( "%p %p %x %p\n", ptrs, ti, flags, copy ); + + if (!ptrs) return EXCEPTION_CONTINUE_SEARCH; + + /* handle catch(...) */ + if (!ti) return EXCEPTION_EXECUTE_HANDLER; + + rec = ptrs->ExceptionRecord; + if (!is_cxx_exception( rec )) return EXCEPTION_CONTINUE_SEARCH; + + if (rec->ExceptionInformation[1] == 0 && rec->ExceptionInformation[2] == 0) + { + rec = msvcrt_get_thread_data()->exc_record; + if (!rec) return EXCEPTION_CONTINUE_SEARCH; + } + + exc_base = rec->ExceptionInformation[3]; + type = find_caught_type( (cxx_exception_type *)rec->ExceptionInformation[2], exc_base, ti, flags ); + if (!type) return EXCEPTION_CONTINUE_SEARCH; + + if (copy) copy_exception( (void *)rec->ExceptionInformation[1], copy, flags, type, exc_base ); + + return EXCEPTION_EXECUTE_HANDLER; +} + +/********************************************************************* + * __CxxQueryExceptionSize (MSVCRT.@) + */ +unsigned int CDECL __CxxQueryExceptionSize(void) +{ + return sizeof(cxx_exception_type); +} + +/********************************************************************* + * _abnormal_termination (MSVCRT.@) + */ +int CDECL __intrinsic_abnormal_termination(void) +{ + FIXME("(void)stub\n"); + return 0; +} + +/****************************************************************** + * __uncaught_exception (MSVCRT.@) + */ +BOOL CDECL __uncaught_exception(void) +{ + return msvcrt_get_thread_data()->processing_throw != 0; +} + +/********************************************************************* + * _fpieee_flt (MSVCRT.@) + */ +int __cdecl _fpieee_flt( __msvcrt_ulong code, EXCEPTION_POINTERS *ep, + int (__cdecl *handler)(_FPIEEE_RECORD*) ) +{ + switch (code) + { + case STATUS_FLOAT_DIVIDE_BY_ZERO: + case STATUS_FLOAT_INEXACT_RESULT: + case STATUS_FLOAT_INVALID_OPERATION: + case STATUS_FLOAT_OVERFLOW: + case STATUS_FLOAT_UNDERFLOW: + return handle_fpieee_flt( code, ep, handler ); + default: + return EXCEPTION_CONTINUE_SEARCH; + } +} + +#if _MSVCR_VER>=70 && _MSVCR_VER<=71 + +/********************************************************************* + * _set_security_error_handler (MSVCR70.@) + */ +MSVCRT_security_error_handler CDECL _set_security_error_handler( + MSVCRT_security_error_handler handler ) +{ + MSVCRT_security_error_handler old = security_error_handler; + + TRACE("(%p)\n", handler); + + security_error_handler = handler; + return old; +} + +/********************************************************************* + * __security_error_handler (MSVCR70.@) + */ +void CDECL __security_error_handler(int code, void *data) +{ + if(security_error_handler) + security_error_handler(code, data); + else + FIXME("(%d, %p) stub\n", code, data); + + _exit(3); +} + +#endif /* _MSVCR_VER>=70 && _MSVCR_VER<=71 */ + +#if _MSVCR_VER>=110 +/********************************************************************* + * __crtSetUnhandledExceptionFilter (MSVCR110.@) + */ +LPTOP_LEVEL_EXCEPTION_FILTER CDECL __crtSetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER filter) +{ + return SetUnhandledExceptionFilter(filter); +} +#endif + +/********************************************************************* + * _CreateFrameInfo (MSVCR80.@) + */ +frame_info* CDECL _CreateFrameInfo(frame_info *fi, void *obj) +{ + thread_data_t *data = msvcrt_get_thread_data(); + + TRACE("(%p, %p)\n", fi, obj); + + fi->next = data->frame_info_head; + data->frame_info_head = fi; + fi->object = obj; + return fi; +} + +/********************************************************************* + * _FindAndUnlinkFrame (MSVCR80.@) + */ +void CDECL _FindAndUnlinkFrame(frame_info *fi) +{ + thread_data_t *data = msvcrt_get_thread_data(); + frame_info *cur = data->frame_info_head; + + TRACE("(%p)\n", fi); + + if (cur == fi) + { + data->frame_info_head = cur->next; + return; + } + + for (; cur->next; cur = cur->next) + { + if (cur->next == fi) + { + cur->next = fi->next; + return; + } + } + + ERR("frame not found, native crashes in this case\n"); +} + +/********************************************************************* + * _IsExceptionObjectToBeDestroyed (MSVCR80.@) + */ +BOOL __cdecl _IsExceptionObjectToBeDestroyed(const void *obj) +{ + frame_info *cur; + + TRACE( "%p\n", obj ); + + for (cur = msvcrt_get_thread_data()->frame_info_head; cur; cur = cur->next) + { + if (cur->object == obj) + return FALSE; + } + + return TRUE; +} + +/********************************************************************* + * __DestructExceptionObject (MSVCRT.@) + */ +void CDECL __DestructExceptionObject(EXCEPTION_RECORD *rec) +{ + cxx_exception_type *info = (cxx_exception_type*) rec->ExceptionInformation[2]; + void *object = (void*)rec->ExceptionInformation[1]; + + TRACE("(%p)\n", rec); + + if (!is_cxx_exception( rec )) return; + + if (!info || !info->destructor) + return; + + call_dtor( rtti_rva( info->destructor, rec->ExceptionInformation[3] ), object ); +} + +/********************************************************************* + * __CxxRegisterExceptionObject (MSVCRT.@) + */ +BOOL CDECL __CxxRegisterExceptionObject(EXCEPTION_POINTERS *ep, cxx_frame_info *frame_info) +{ + thread_data_t *data = msvcrt_get_thread_data(); + + TRACE("(%p, %p)\n", ep, frame_info); + + if (!ep || !ep->ExceptionRecord) + { + frame_info->rec = (void*)-1; + frame_info->context = (void*)-1; + return TRUE; + } + + frame_info->rec = data->exc_record; + frame_info->context = data->ctx_record; + data->exc_record = ep->ExceptionRecord; + data->ctx_record = ep->ContextRecord; + _CreateFrameInfo(&frame_info->frame_info, (void*)ep->ExceptionRecord->ExceptionInformation[1]); + return TRUE; +} + +/********************************************************************* + * __CxxUnregisterExceptionObject (MSVCRT.@) + */ +void CDECL __CxxUnregisterExceptionObject(cxx_frame_info *frame_info, BOOL in_use) +{ + thread_data_t *data = msvcrt_get_thread_data(); + + TRACE("(%p)\n", frame_info); + + if(frame_info->rec == (void*)-1) + return; + + _FindAndUnlinkFrame(&frame_info->frame_info); + if(data->exc_record->ExceptionCode == CXX_EXCEPTION && !in_use + && _IsExceptionObjectToBeDestroyed((void*)data->exc_record->ExceptionInformation[1])) + __DestructExceptionObject(data->exc_record); + data->exc_record = frame_info->rec; + data->ctx_record = frame_info->context; +} + +struct __std_exception_data { + char *what; + char dofree; +}; + +#if _MSVCR_VER>=140 + +/********************************************************************* + * __std_exception_copy (UCRTBASE.@) + */ +void CDECL __std_exception_copy(const struct __std_exception_data *src, + struct __std_exception_data *dst) +{ + TRACE("(%p %p)\n", src, dst); + + if(src->dofree && src->what) { + dst->what = _strdup(src->what); + dst->dofree = 1; + } else { + dst->what = src->what; + dst->dofree = 0; + } +} + +/********************************************************************* + * __std_exception_destroy (UCRTBASE.@) + */ +void CDECL __std_exception_destroy(struct __std_exception_data *data) +{ + TRACE("(%p)\n", data); + + if(data->dofree) + free(data->what); + data->what = NULL; + data->dofree = 0; +} + +/********************************************************************* + * __current_exception (UCRTBASE.@) + */ +void** CDECL __current_exception(void) +{ + TRACE("()\n"); + return (void**)&msvcrt_get_thread_data()->exc_record; +} + +/********************************************************************* + * __current_exception_context (UCRTBASE.@) + */ +void** CDECL __current_exception_context(void) +{ + TRACE("()\n"); + return (void**)&msvcrt_get_thread_data()->ctx_record; +} + +/********************************************************************* + * __processing_throw (UCRTBASE.@) + */ +int* CDECL __processing_throw(void) +{ + TRACE("()\n"); + return &msvcrt_get_thread_data()->processing_throw; +} + +#endif /* _MSVCR_VER>=140 */ diff --git a/dll/win32/msvcrt/except_arm.c b/dll/win32/msvcrt/except_arm.c new file mode 100644 index 00000000000..b2bf94d5338 --- /dev/null +++ b/dll/win32/msvcrt/except_arm.c @@ -0,0 +1,97 @@ +/* + * msvcrt C++ exception handling + * + * Copyright 2011 Alexandre Julliard + * Copyright 2013 André Hentschel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#ifdef __arm__ + +#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "msvcrt.h" +#include "excpt.h" +#include "wine/debug.h" + +#include "cppexcept.h" + +WINE_DEFAULT_DEBUG_CHANNEL(seh); + + +extern void *call_exc_handler( void *handler, ULONG_PTR frame, UINT flags, BYTE *nonvol_regs ); +__ASM_GLOBAL_FUNC( call_exc_handler, + "push {r1,r4-r11,lr}\n\t" + ".seh_save_regs_w {r1,r4-r11,lr}\n\t" + ".seh_endprologue\n\t" + "ldm r3, {r4-r11}\n\t" + "blx r0\n\t" + "pop {r3-r11,pc}" ) + + +/******************************************************************* + * call_catch_handler + */ +void *call_catch_handler( EXCEPTION_RECORD *rec ) +{ + ULONG_PTR frame = rec->ExceptionInformation[1]; + void *handler = (void *)rec->ExceptionInformation[5]; + BYTE *nonvol_regs = (BYTE *)rec->ExceptionInformation[10]; + + TRACE( "calling %p frame %Ix\n", handler, frame ); + return call_exc_handler( handler, frame, 0x100, nonvol_regs ); +} + + +/******************************************************************* + * call_unwind_handler + */ +void *call_unwind_handler( void *handler, ULONG_PTR frame, DISPATCHER_CONTEXT *dispatch ) +{ + TRACE( "calling %p frame %Ix\n", handler, frame ); + return call_exc_handler( handler, frame, 0x100, dispatch->NonVolatileRegisters ); +} + + +/******************************************************************* + * get_exception_pc + */ +ULONG_PTR get_exception_pc( DISPATCHER_CONTEXT *dispatch ) +{ + ULONG_PTR pc = dispatch->ControlPc; + if (dispatch->ControlPcIsUnwound) pc -= 2; + return pc; +} + + +/********************************************************************* + * handle_fpieee_flt + */ +int handle_fpieee_flt( __msvcrt_ulong exception_code, EXCEPTION_POINTERS *ep, + int (__cdecl *handler)(_FPIEEE_RECORD*) ) +{ + FIXME("(%lx %p %p)\n", exception_code, ep, handler); + return EXCEPTION_CONTINUE_SEARCH; +} + +#endif /* __arm__ */ diff --git a/dll/win32/msvcrt/except_arm64.c b/dll/win32/msvcrt/except_arm64.c new file mode 100644 index 00000000000..6d515f8b150 --- /dev/null +++ b/dll/win32/msvcrt/except_arm64.c @@ -0,0 +1,128 @@ +/* + * msvcrt C++ exception handling + * + * Copyright 2011 Alexandre Julliard + * Copyright 2013 André Hentschel + * Copyright 2017 Martin Storsjo + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#ifdef __aarch64__ + +#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "msvcrt.h" +#include "excpt.h" +#include "wine/debug.h" + +#include "cppexcept.h" + +WINE_DEFAULT_DEBUG_CHANNEL(seh); + + +extern void *call_exc_handler( void *handler, ULONG_PTR frame, UINT flags, BYTE *nonvol_regs ); +__ASM_GLOBAL_FUNC( call_exc_handler, + "stp x29, x30, [sp, #-96]!\n\t" + ".seh_save_fplr_x 96\n\t" + "stp x19, x20, [sp, #16]\n\t" + ".seh_save_regp x19, 16\n\t" + "stp x21, x22, [sp, #32]\n\t" + ".seh_save_regp x21, 32\n\t" + "stp x23, x24, [sp, #48]\n\t" + ".seh_save_regp x23, 48\n\t" + "stp x25, x26, [sp, #64]\n\t" + ".seh_save_regp x25, 64\n\t" + "stp x27, x28, [sp, #80]\n\t" + ".seh_save_regp x27, 80\n\t" + "str x1, [sp, #-16]!\n\t" + ".seh_stackalloc 16\n\t" + ".seh_endprologue\n\t" + "ldp x19, x20, [x3, #0]\n\t" /* nonvolatile regs */ + "ldp x21, x22, [x3, #16]\n\t" + "ldp x23, x24, [x3, #32]\n\t" + "ldp x25, x26, [x3, #48]\n\t" + "ldp x27, x28, [x3, #64]\n\t" + "ldr x29, [x3, #80]\n\t" + "blr x0\n\t" + "add sp, sp, 16\n\t" + "ldp x19, x20, [sp, #16]\n\t" + "ldp x21, x22, [sp, #32]\n\t" + "ldp x23, x24, [sp, #48]\n\t" + "ldp x25, x26, [sp, #64]\n\t" + "ldp x27, x28, [sp, #80]\n\t" + "ldp x29, x30, [sp], #96\n\t" + "ret" ) + + +/******************************************************************* + * call_catch_handler + */ +void *call_catch_handler( EXCEPTION_RECORD *rec ) +{ + ULONG_PTR frame = rec->ExceptionInformation[1]; + void *handler = (void *)rec->ExceptionInformation[5]; + BYTE *nonvol_regs = (BYTE *)rec->ExceptionInformation[10]; + + TRACE( "calling %p frame %Ix\n", handler, frame ); + return call_exc_handler( handler, frame, 0x100, nonvol_regs ); +} + + +/******************************************************************* + * call_unwind_handler + */ +void *call_unwind_handler( void *handler, ULONG_PTR frame, DISPATCHER_CONTEXT *dispatch ) +{ + TRACE( "calling %p frame %Ix\n", handler, frame ); + return call_exc_handler( handler, frame, 0x100, dispatch->NonVolatileRegisters ); +} + + +/******************************************************************* + * get_exception_pc + */ +ULONG_PTR get_exception_pc( DISPATCHER_CONTEXT *dispatch ) +{ + ULONG_PTR pc = dispatch->ControlPc; + if (dispatch->ControlPcIsUnwound) pc -= 4; + return pc; +} + + +/******************************************************************* + * _setjmp (MSVCRT.@) + */ +__ASM_GLOBAL_FUNC( _setjmp, "b _setjmpex" ); + + +/********************************************************************* + * handle_fpieee_flt + */ +int handle_fpieee_flt( __msvcrt_ulong exception_code, EXCEPTION_POINTERS *ep, + int (__cdecl *handler)(_FPIEEE_RECORD*) ) +{ + FIXME("(%lx %p %p)\n", exception_code, ep, handler); + return EXCEPTION_CONTINUE_SEARCH; +} + +#endif /* __aarch64__ */ diff --git a/dll/win32/msvcrt/except_arm64ec.c b/dll/win32/msvcrt/except_arm64ec.c new file mode 100644 index 00000000000..b2e723f79b3 --- /dev/null +++ b/dll/win32/msvcrt/except_arm64ec.c @@ -0,0 +1,169 @@ +/* + * msvcrt C++ exception handling + * + * Copyright 2011 Alexandre Julliard + * Copyright 2013 André Hentschel + * Copyright 2017 Martin Storsjo + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#ifdef __arm64ec__ + +#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "msvcrt.h" +#include "excpt.h" +#include "wine/debug.h" + +#include "cppexcept.h" + +WINE_DEFAULT_DEBUG_CHANNEL(seh); + + +static void * __attribute__((naked,used)) call_handler_arm64( void *func, uintptr_t frame, + UINT flags, BYTE *nonvol_regs ) +{ + asm( ".seh_proc \"#call_handler_arm64\"\n\t" + "stp x29, x30, [sp, #-80]!\n\t" + ".seh_save_fplr_x 80\n\t" + "stp x19, x20, [sp, #16]\n\t" + ".seh_save_regp x19, 16\n\t" + "stp x21, x22, [sp, #32]\n\t" + ".seh_save_regp x21, 32\n\t" + "stp x25, x26, [sp, #48]\n\t" + ".seh_save_regp x25, 48\n\t" + "str x27, [sp, #64]\n\t" + ".seh_save_reg x27, 64\n\t" + "str x1, [sp, #-16]!\n\t" + ".seh_stackalloc 16\n\t" + ".seh_endprologue\n\t" + "ldp x19, x20, [x3, #0]\n\t" /* nonvolatile regs */ + "ldp x21, x22, [x3, #16]\n\t" + "ldp x25, x26, [x3, #48]\n\t" + "ldr x27, [x3, #64]\n\t" + "ldr x29, [x3, #80]\n\t" + "blr x0\n\t" + "add sp, sp, 16\n\t" + "ldp x19, x20, [sp, #16]\n\t" + "ldp x21, x22, [sp, #32]\n\t" + "ldp x25, x26, [sp, #48]\n\t" + "ldr x27, [sp, #64]\n\t" + "ldp x29, x30, [sp], #80\n\t" + "ret\n\t" + ".seh_endproc" ); +} + +static void * __attribute__((naked,used)) call_handler_x64( void *func, uintptr_t frame, UINT flags ) +{ + asm( ".seh_proc \"#call_handler_x64\"\n\t" + "stp x29, x30, [sp, #-16]!\n\t" + ".seh_save_fplr_x 16\n\t" + ".seh_endprologue\n\t" + "mov x11, x0\n\t" + "adr x10, $iexit_thunk$cdecl$i8$i8i8i8\n\t" + "adrp x16, __os_arm64x_dispatch_icall\n\t" + "ldr x16, [x16, #:lo12:__os_arm64x_dispatch_icall]\n\t" + "blr x16\n\t" + "blr x11\n\t" + "ldp x29, x30, [sp], #16\n\t" + "ret\n\t" + ".seh_endproc" ); +} + + +/******************************************************************* + * call_catch_handler + */ +void *call_catch_handler( EXCEPTION_RECORD *rec ) +{ + ULONG_PTR frame = rec->ExceptionInformation[1]; + void *handler = (void *)rec->ExceptionInformation[5]; + + TRACE( "calling %p frame %Ix\n", handler, frame ); + if (!RtlIsEcCode( (ULONG_PTR)handler )) return call_handler_x64( handler, frame, 0x100 ); + return call_handler_arm64( handler, frame, 0x100, (BYTE *)rec->ExceptionInformation[10] ); +} + + +/******************************************************************* + * call_unwind_handler + */ +void *call_unwind_handler( void *handler, ULONG_PTR frame, DISPATCHER_CONTEXT *dispatch ) +{ + TRACE( "calling %p frame %Ix\n", handler, frame ); + if (!RtlIsEcCode( (ULONG_PTR)handler )) return call_handler_x64( handler, frame, 0x100 ); + return call_handler_arm64( handler, frame, 0x100, + ((DISPATCHER_CONTEXT_ARM64EC *)dispatch)->NonVolatileRegisters ); +} + + +/******************************************************************* + * get_exception_pc + */ +ULONG_PTR get_exception_pc( DISPATCHER_CONTEXT *dispatch ) +{ + ULONG_PTR pc = dispatch->ControlPc; + if (RtlIsEcCode( pc ) && ((DISPATCHER_CONTEXT_ARM64EC *)dispatch)->ControlPcIsUnwound) pc -= 4; + return pc; +} + + +/********************************************************************* + * handle_fpieee_flt + */ +int handle_fpieee_flt( __msvcrt_ulong exception_code, EXCEPTION_POINTERS *ep, + int (__cdecl *handler)(_FPIEEE_RECORD*) ) +{ + FIXME("(%lx %p %p)\n", exception_code, ep, handler); + return EXCEPTION_CONTINUE_SEARCH; +} + +#if _MSVCR_VER>=110 && _MSVCR_VER<=120 +/********************************************************************* + * __crtCapturePreviousContext (MSVCR110.@) + */ +void __cdecl __crtCapturePreviousContext( CONTEXT *ctx ) +{ + UNWIND_HISTORY_TABLE table; + RUNTIME_FUNCTION *func; + PEXCEPTION_ROUTINE handler; + ULONG_PTR pc, frame, base; + void *data; + ULONG i; + + RtlCaptureContext( ctx ); + for (i = 0; i < 2; i++) + { + pc = ctx->Rip; + if ((ctx->ContextFlags & CONTEXT_UNWOUND_TO_CALL) && RtlIsEcCode( pc )) pc -= 4; + if (!(func = RtlLookupFunctionEntry( pc, &base, &table ))) break; + if (RtlVirtualUnwind2( UNW_FLAG_NHANDLER, base, pc, func, ctx, NULL, + &data, &frame, NULL, NULL, NULL, &handler, 0 )) + break; + if (!ctx->Rip) break; + if (!frame) break; + } +} +#endif + +#endif /* __arm64ec__ */ diff --git a/dll/win32/msvcrt/except_i386.c b/dll/win32/msvcrt/except_i386.c new file mode 100644 index 00000000000..7d1faa95272 --- /dev/null +++ b/dll/win32/msvcrt/except_i386.c @@ -0,0 +1,923 @@ +/* + * msvcrt C++ exception handling + * + * Copyright 2000 Jon Griffiths + * Copyright 2002 Alexandre Julliard + * Copyright 2005 Juan Lang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + * A good reference is the article "How a C++ compiler implements + * exception handling" by Vishal Kochhar, available on + * www.thecodeproject.com. + */ + +#ifdef __i386__ + +#include +#include +#define longjmp ms_longjmp /* avoid prototype mismatch */ +#include +#undef longjmp + +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "msvcrt.h" +#include "wine/exception.h" +#include "excpt.h" +#include "wine/debug.h" + +#include "cppexcept.h" + +WINE_DEFAULT_DEBUG_CHANNEL(seh); + + +/* the exception frame used by CxxFrameHandler */ +typedef struct __cxx_exception_frame +{ + EXCEPTION_REGISTRATION_RECORD frame; /* the standard exception frame */ + int trylevel; + DWORD ebp; +} cxx_exception_frame; + +/* exception frame for nested exceptions in catch block */ +typedef struct +{ + EXCEPTION_REGISTRATION_RECORD frame; /* standard exception frame */ + cxx_exception_frame *cxx_frame; /* frame of parent exception */ + const cxx_function_descr *descr; /* descriptor of parent exception */ + int trylevel; /* current try level */ + cxx_frame_info frame_info; +} catch_func_nested_frame; + +typedef struct +{ + cxx_exception_frame *frame; + const cxx_function_descr *descr; + catch_func_nested_frame *nested_frame; +} se_translator_ctx; + +typedef struct _SCOPETABLE +{ + int previousTryLevel; + int (*lpfnFilter)(PEXCEPTION_POINTERS); + void * (*lpfnHandler)(void); +} SCOPETABLE, *PSCOPETABLE; + +typedef struct MSVCRT_EXCEPTION_FRAME +{ + EXCEPTION_REGISTRATION_RECORD *prev; + void (*handler)(PEXCEPTION_RECORD, EXCEPTION_REGISTRATION_RECORD*, + PCONTEXT, PEXCEPTION_RECORD); + PSCOPETABLE scopetable; + int trylevel; + int _ebp; + PEXCEPTION_POINTERS xpointers; +} MSVCRT_EXCEPTION_FRAME; + +typedef struct +{ + int gs_cookie_offset; + ULONG gs_cookie_xor; + int eh_cookie_offset; + ULONG eh_cookie_xor; + SCOPETABLE entries[1]; +} SCOPETABLE_V4; + +#define TRYLEVEL_END (-1) /* End of trylevel list */ + +typedef DWORD (CDECL *cxx_exc_custom_handler)( PEXCEPTION_RECORD, cxx_exception_frame*, + PCONTEXT, EXCEPTION_REGISTRATION_RECORD**, + const cxx_function_descr*, int nested_trylevel, + EXCEPTION_REGISTRATION_RECORD *nested_frame, DWORD unknown ); + +DWORD CDECL cxx_frame_handler( PEXCEPTION_RECORD rec, cxx_exception_frame* frame, + PCONTEXT context, EXCEPTION_REGISTRATION_RECORD** dispatch, + const cxx_function_descr *descr, + catch_func_nested_frame* nested_frame ); + +/* continue execution to the specified address after exception is caught */ +extern void DECLSPEC_NORETURN continue_after_catch( cxx_exception_frame* frame, void *addr ); + +__ASM_GLOBAL_FUNC( continue_after_catch, + "movl 4(%esp), %edx\n\t" + "movl 8(%esp), %eax\n\t" + "movl -4(%edx), %esp\n\t" + "leal 12(%edx), %ebp\n\t" + "jmp *%eax" ); + +extern void DECLSPEC_NORETURN call_finally_block( void *code_block, void *base_ptr ); + +__ASM_GLOBAL_FUNC( call_finally_block, + "movl 8(%esp), %ebp\n\t" + "jmp *4(%esp)" ); + +extern int call_filter( int (*func)(PEXCEPTION_POINTERS), void *arg, void *ebp ); + +__ASM_GLOBAL_FUNC( call_filter, + "pushl %ebp\n\t" + "pushl 12(%esp)\n\t" + "movl 20(%esp), %ebp\n\t" + "call *12(%esp)\n\t" + "popl %ebp\n\t" + "popl %ebp\n\t" + "ret" ); + +extern void *call_handler( void * (*func)(void), void *ebp ); + +__ASM_GLOBAL_FUNC( call_handler, + "pushl %ebp\n\t" + "pushl %ebx\n\t" + "pushl %esi\n\t" + "pushl %edi\n\t" + "movl 24(%esp), %ebp\n\t" + "call *20(%esp)\n\t" + "popl %edi\n\t" + "popl %esi\n\t" + "popl %ebx\n\t" + "popl %ebp\n\t" + "ret" ); + +/* unwind the local function up to a given trylevel */ +static void cxx_local_unwind( cxx_exception_frame* frame, const cxx_function_descr *descr, int last_level) +{ + void * (*handler)(void); + int trylevel = frame->trylevel; + + while (trylevel != last_level) + { + if (trylevel < 0 || trylevel >= descr->unwind_count) + { + ERR( "invalid trylevel %d\n", trylevel ); + terminate(); + } + handler = descr->unwind_table[trylevel].handler; + if (handler) + { + TRACE( "calling unwind handler %p trylevel %d last %d ebp %p\n", + handler, trylevel, last_level, &frame->ebp ); + call_handler( handler, &frame->ebp ); + } + trylevel = descr->unwind_table[trylevel].prev; + } + frame->trylevel = last_level; +} + +/* handler for exceptions happening while calling a catch function */ +static DWORD catch_function_nested_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) +{ + catch_func_nested_frame *nested_frame = (catch_func_nested_frame *)frame; + + if (rec->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND)) + { + __CxxUnregisterExceptionObject(&nested_frame->frame_info, FALSE); + return ExceptionContinueSearch; + } + + TRACE( "got nested exception in catch function\n" ); + + if(rec->ExceptionCode == CXX_EXCEPTION) + { + PEXCEPTION_RECORD prev_rec = msvcrt_get_thread_data()->exc_record; + + if((rec->ExceptionInformation[1] == 0 && rec->ExceptionInformation[2] == 0) || + (prev_rec->ExceptionCode == CXX_EXCEPTION && + rec->ExceptionInformation[1] == prev_rec->ExceptionInformation[1] && + rec->ExceptionInformation[2] == prev_rec->ExceptionInformation[2])) + { + /* exception was rethrown */ + *rec = *prev_rec; + rec->ExceptionFlags &= ~EXCEPTION_UNWINDING; + if(TRACE_ON(seh)) { + TRACE("detect rethrow: exception code: %lx\n", rec->ExceptionCode); + if(rec->ExceptionCode == CXX_EXCEPTION) + TRACE("re-propagate: obj: %Ix, type: %Ix\n", + rec->ExceptionInformation[1], rec->ExceptionInformation[2]); + } + } + else + { + TRACE("detect threw new exception in catch block\n"); + } + } + + return cxx_frame_handler( rec, nested_frame->cxx_frame, context, + NULL, nested_frame->descr, nested_frame ); +} + +/* find and call the appropriate catch block for an exception */ +/* returns the address to continue execution to after the catch block was called */ +static inline void call_catch_block( PEXCEPTION_RECORD rec, CONTEXT *context, + cxx_exception_frame *frame, + const cxx_function_descr *descr, + catch_func_nested_frame *catch_frame, + cxx_exception_type *info ) +{ + UINT i; + void *addr, *handler, *object = (void *)rec->ExceptionInformation[1]; + catch_func_nested_frame nested_frame; + int trylevel = frame->trylevel; + DWORD save_esp = ((DWORD*)frame)[-1]; + thread_data_t *data = msvcrt_get_thread_data(); + + data->processing_throw++; + for (i = 0; i < descr->tryblock_count; i++) + { + const tryblock_info *tryblock = &descr->tryblock[i]; + + /* only handle try blocks inside current catch block */ + if (catch_frame && catch_frame->trylevel > tryblock->start_level) continue; + + if (trylevel < tryblock->start_level) continue; + if (trylevel > tryblock->end_level) continue; + + handler = find_catch_handler( object, (uintptr_t)&frame->ebp, 0, tryblock, info, 0 ); + if (!handler) continue; + + /* Add frame info here so exception is not freed inside RtlUnwind call */ + _CreateFrameInfo(&nested_frame.frame_info.frame_info, object); + + /* unwind the stack */ + RtlUnwind( catch_frame ? &catch_frame->frame : &frame->frame, 0, rec, 0 ); + cxx_local_unwind( frame, descr, tryblock->start_level ); + frame->trylevel = tryblock->end_level + 1; + + nested_frame.frame_info.rec = data->exc_record; + nested_frame.frame_info.context = data->ctx_record; + data->exc_record = rec; + data->ctx_record = context; + data->processing_throw--; + + /* call the catch block */ + TRACE( "calling handler %p ebp %p\n", handler, &frame->ebp ); + + /* setup an exception block for nested exceptions */ + nested_frame.frame.Handler = catch_function_nested_handler; + nested_frame.cxx_frame = frame; + nested_frame.descr = descr; + nested_frame.trylevel = tryblock->end_level + 1; + + __wine_push_frame( &nested_frame.frame ); + addr = call_handler( handler, &frame->ebp ); + __wine_pop_frame( &nested_frame.frame ); + + ((DWORD*)frame)[-1] = save_esp; + __CxxUnregisterExceptionObject(&nested_frame.frame_info, FALSE); + TRACE( "done, continuing at %p\n", addr ); + + continue_after_catch( frame, addr ); + } + data->processing_throw--; +} + +static LONG CALLBACK se_translation_filter( EXCEPTION_POINTERS *ep, void *c ) +{ + se_translator_ctx *ctx = (se_translator_ctx *)c; + EXCEPTION_RECORD *rec = ep->ExceptionRecord; + cxx_exception_type *exc_type; + + if (rec->ExceptionCode != CXX_EXCEPTION) + { + TRACE( "non-c++ exception thrown in SEH handler: %lx\n", rec->ExceptionCode ); + terminate(); + } + + exc_type = (cxx_exception_type *)rec->ExceptionInformation[2]; + call_catch_block( rec, ep->ContextRecord, ctx->frame, ctx->descr, + ctx->nested_frame, exc_type ); + + __DestructExceptionObject( rec ); + return ExceptionContinueSearch; +} + +static void check_noexcept( PEXCEPTION_RECORD rec, + const cxx_function_descr *descr, BOOL nested ) +{ + if (!nested && rec->ExceptionCode == CXX_EXCEPTION && + descr->magic >= CXX_FRAME_MAGIC_VC8 && + (descr->flags & FUNC_DESCR_NOEXCEPT)) + { + ERR("noexcept function propagating exception\n"); + terminate(); + } +} + +/********************************************************************* + * cxx_frame_handler + * + * Implementation of __CxxFrameHandler. + */ +DWORD CDECL cxx_frame_handler( PEXCEPTION_RECORD rec, cxx_exception_frame* frame, + PCONTEXT context, EXCEPTION_REGISTRATION_RECORD** dispatch, + const cxx_function_descr *descr, + catch_func_nested_frame* nested_frame ) +{ + cxx_exception_type *exc_type; + + if (descr->magic < CXX_FRAME_MAGIC_VC6 || descr->magic > CXX_FRAME_MAGIC_VC8) + { + ERR( "invalid frame magic %x\n", descr->magic ); + return ExceptionContinueSearch; + } + if (descr->magic >= CXX_FRAME_MAGIC_VC8 && + (descr->flags & FUNC_DESCR_SYNCHRONOUS) && + (rec->ExceptionCode != CXX_EXCEPTION)) + return ExceptionContinueSearch; /* handle only c++ exceptions */ + + if (rec->ExceptionFlags & (EXCEPTION_UNWINDING|EXCEPTION_EXIT_UNWIND)) + { + if (descr->unwind_count && !nested_frame) cxx_local_unwind( frame, descr, -1 ); + return ExceptionContinueSearch; + } + if (!descr->tryblock_count) + { + check_noexcept(rec, descr, nested_frame != NULL); + return ExceptionContinueSearch; + } + + if(rec->ExceptionCode == CXX_EXCEPTION && + rec->ExceptionInformation[1] == 0 && rec->ExceptionInformation[2] == 0) + { + *rec = *msvcrt_get_thread_data()->exc_record; + rec->ExceptionFlags &= ~EXCEPTION_UNWINDING; + if(TRACE_ON(seh)) { + TRACE("detect rethrow: exception code: %lx\n", rec->ExceptionCode); + if(rec->ExceptionCode == CXX_EXCEPTION) + TRACE("re-propagate: obj: %Ix, type: %Ix\n", + rec->ExceptionInformation[1], rec->ExceptionInformation[2]); + } + } + + if(rec->ExceptionCode == CXX_EXCEPTION) + { + exc_type = (cxx_exception_type *)rec->ExceptionInformation[2]; + + if (rec->ExceptionInformation[0] > CXX_FRAME_MAGIC_VC8 && + exc_type->custom_handler) + { + cxx_exc_custom_handler handler = exc_type->custom_handler; + return handler( rec, frame, context, dispatch, descr, + nested_frame ? nested_frame->trylevel : 0, + nested_frame ? &nested_frame->frame : NULL, 0 ); + } + + if (TRACE_ON(seh)) + { + TRACE("handling C++ exception rec %p frame %p trylevel %d descr %p nested_frame %p\n", + rec, frame, frame->trylevel, descr, nested_frame ); + TRACE_EXCEPTION_TYPE( exc_type, 0 ); + dump_function_descr( descr, 0 ); + } + } + else + { + thread_data_t *data = msvcrt_get_thread_data(); + + exc_type = NULL; + TRACE("handling C exception code %lx rec %p frame %p trylevel %d descr %p nested_frame %p\n", + rec->ExceptionCode, rec, frame, frame->trylevel, descr, nested_frame ); + + if (data->se_translator) { + EXCEPTION_POINTERS except_ptrs; + se_translator_ctx ctx; + + ctx.frame = frame; + ctx.descr = descr; + ctx.nested_frame = nested_frame; + __TRY + { + except_ptrs.ExceptionRecord = rec; + except_ptrs.ContextRecord = context; + data->se_translator( rec->ExceptionCode, &except_ptrs ); + } + __EXCEPT_CTX(se_translation_filter, &ctx) + { + } + __ENDTRY + } + } + + call_catch_block( rec, context, frame, descr, + nested_frame, exc_type ); + check_noexcept(rec, descr, nested_frame != NULL); + return ExceptionContinueSearch; +} + + +/********************************************************************* + * __CxxFrameHandler (MSVCRT.@) + */ +extern DWORD CDECL __CxxFrameHandler( PEXCEPTION_RECORD rec, EXCEPTION_REGISTRATION_RECORD* frame, + PCONTEXT context, EXCEPTION_REGISTRATION_RECORD** dispatch ); +__ASM_GLOBAL_FUNC( __CxxFrameHandler, + "pushl $0\n\t" /* nested_frame */ + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + "pushl %eax\n\t" /* descr */ + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + "pushl 24(%esp)\n\t" /* dispatch */ + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + "pushl 24(%esp)\n\t" /* context */ + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + "pushl 24(%esp)\n\t" /* frame */ + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + "pushl 24(%esp)\n\t" /* rec */ + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + "call " __ASM_NAME("cxx_frame_handler") "\n\t" + "add $24,%esp\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset -24\n\t") + "ret" ) + + +/********************************************************************* + * __CxxLongjmpUnwind (MSVCRT.@) + * + * Callback meant to be used as UnwindFunc for setjmp/longjmp. + */ +void __stdcall __CxxLongjmpUnwind( const _JUMP_BUFFER *buf ) +{ + cxx_exception_frame *frame = (cxx_exception_frame *)buf->Registration; + const cxx_function_descr *descr = (const cxx_function_descr *)buf->UnwindData[0]; + + TRACE( "unwinding frame %p descr %p trylevel %ld\n", frame, descr, buf->TryLevel ); + cxx_local_unwind( frame, descr, buf->TryLevel ); +} + +/********************************************************************* + * _EH_prolog (MSVCRT.@) + */ + +/* Provided for VC++ binary compatibility only */ +__ASM_GLOBAL_FUNC(_EH_prolog, + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") /* skip ret addr */ + "pushl $-1\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + "pushl %eax\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + "pushl %fs:0\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + "movl %esp, %fs:0\n\t" + "movl 12(%esp), %eax\n\t" + "movl %ebp, 12(%esp)\n\t" + "leal 12(%esp), %ebp\n\t" + "pushl %eax\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + "ret") + +static const SCOPETABLE_V4 *get_scopetable_v4( MSVCRT_EXCEPTION_FRAME *frame, ULONG_PTR cookie ) +{ + return (const SCOPETABLE_V4 *)((ULONG_PTR)frame->scopetable ^ cookie); +} + +static DWORD MSVCRT_nested_handler(PEXCEPTION_RECORD rec, + EXCEPTION_REGISTRATION_RECORD* frame, + PCONTEXT context, + EXCEPTION_REGISTRATION_RECORD** dispatch) +{ + if (!(rec->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND))) + return ExceptionContinueSearch; + *dispatch = frame; + return ExceptionCollidedUnwind; +} + +static void msvcrt_local_unwind2(MSVCRT_EXCEPTION_FRAME* frame, int trylevel, void *ebp) +{ + EXCEPTION_REGISTRATION_RECORD reg; + + TRACE("(%p,%d,%d)\n",frame, frame->trylevel, trylevel); + + /* Register a handler in case of a nested exception */ + reg.Handler = MSVCRT_nested_handler; + reg.Prev = NtCurrentTeb()->Tib.ExceptionList; + __wine_push_frame(®); + + while (frame->trylevel != TRYLEVEL_END && frame->trylevel != trylevel) + { + int level = frame->trylevel; + frame->trylevel = frame->scopetable[level].previousTryLevel; + if (!frame->scopetable[level].lpfnFilter) + { + TRACE( "__try block cleanup level %d handler %p ebp %p\n", + level, frame->scopetable[level].lpfnHandler, ebp ); + call_handler( frame->scopetable[level].lpfnHandler, ebp ); + } + } + __wine_pop_frame(®); + TRACE("unwound OK\n"); +} + +static void msvcrt_local_unwind4( ULONG *cookie, MSVCRT_EXCEPTION_FRAME* frame, int trylevel, void *ebp ) +{ + EXCEPTION_REGISTRATION_RECORD reg; + const SCOPETABLE_V4 *scopetable = get_scopetable_v4( frame, *cookie ); + + TRACE("(%p,%d,%d)\n",frame, frame->trylevel, trylevel); + + /* Register a handler in case of a nested exception */ + reg.Handler = MSVCRT_nested_handler; + reg.Prev = NtCurrentTeb()->Tib.ExceptionList; + __wine_push_frame(®); + + while (frame->trylevel != -2 && frame->trylevel != trylevel) + { + int level = frame->trylevel; + frame->trylevel = scopetable->entries[level].previousTryLevel; + if (!scopetable->entries[level].lpfnFilter) + { + TRACE( "__try block cleanup level %d handler %p ebp %p\n", + level, scopetable->entries[level].lpfnHandler, ebp ); + call_handler( scopetable->entries[level].lpfnHandler, ebp ); + } + } + __wine_pop_frame(®); + TRACE("unwound OK\n"); +} + +/******************************************************************* + * _local_unwind2 (MSVCRT.@) + */ +void CDECL _local_unwind2(MSVCRT_EXCEPTION_FRAME* frame, int trylevel) +{ + msvcrt_local_unwind2( frame, trylevel, &frame->_ebp ); +} + +/******************************************************************* + * _local_unwind4 (MSVCRT.@) + */ +void CDECL _local_unwind4( ULONG *cookie, MSVCRT_EXCEPTION_FRAME* frame, int trylevel ) +{ + msvcrt_local_unwind4( cookie, frame, trylevel, &frame->_ebp ); +} + +/******************************************************************* + * _global_unwind2 (MSVCRT.@) + */ +void CDECL _global_unwind2(EXCEPTION_REGISTRATION_RECORD* frame) +{ + TRACE("(%p)\n",frame); + RtlUnwind( frame, 0, 0, 0 ); +} + +/********************************************************************* + * _except_handler2 (MSVCRT.@) + */ +int CDECL _except_handler2(PEXCEPTION_RECORD rec, + EXCEPTION_REGISTRATION_RECORD* frame, + PCONTEXT context, + EXCEPTION_REGISTRATION_RECORD** dispatcher) +{ + FIXME("exception %lx flags=%lx at %p handler=%p %p %p stub\n", + rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, + frame->Handler, context, dispatcher); + return ExceptionContinueSearch; +} + +/********************************************************************* + * _except_handler3 (MSVCRT.@) + */ +int CDECL _except_handler3(PEXCEPTION_RECORD rec, + MSVCRT_EXCEPTION_FRAME* frame, + PCONTEXT context, void* dispatcher) +{ + int retval, trylevel; + EXCEPTION_POINTERS exceptPtrs; + PSCOPETABLE pScopeTable; + + TRACE("exception %lx flags=%lx at %p handler=%p %p %p semi-stub\n", + rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, + frame->handler, context, dispatcher); + + __asm__ __volatile__ ("cld"); + + if (rec->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND)) + { + /* Unwinding the current frame */ + msvcrt_local_unwind2(frame, TRYLEVEL_END, &frame->_ebp); + TRACE("unwound current frame, returning ExceptionContinueSearch\n"); + return ExceptionContinueSearch; + } + else + { + /* Hunting for handler */ + exceptPtrs.ExceptionRecord = rec; + exceptPtrs.ContextRecord = context; + *((DWORD *)frame-1) = (DWORD)&exceptPtrs; + trylevel = frame->trylevel; + pScopeTable = frame->scopetable; + + while (trylevel != TRYLEVEL_END) + { + TRACE( "level %d prev %d filter %p\n", trylevel, pScopeTable[trylevel].previousTryLevel, + pScopeTable[trylevel].lpfnFilter ); + if (pScopeTable[trylevel].lpfnFilter) + { + retval = call_filter( pScopeTable[trylevel].lpfnFilter, &exceptPtrs, &frame->_ebp ); + + TRACE("filter returned %s\n", retval == EXCEPTION_CONTINUE_EXECUTION ? + "CONTINUE_EXECUTION" : retval == EXCEPTION_EXECUTE_HANDLER ? + "EXECUTE_HANDLER" : "CONTINUE_SEARCH"); + + if (retval == EXCEPTION_CONTINUE_EXECUTION) + return ExceptionContinueExecution; + + if (retval == EXCEPTION_EXECUTE_HANDLER) + { + /* Unwind all higher frames, this one will handle the exception */ + _global_unwind2((EXCEPTION_REGISTRATION_RECORD*)frame); + msvcrt_local_unwind2(frame, trylevel, &frame->_ebp); + + /* Set our trylevel to the enclosing block, and call the __finally + * code, which won't return + */ + frame->trylevel = pScopeTable[trylevel].previousTryLevel; + TRACE("__finally block %p\n",pScopeTable[trylevel].lpfnHandler); + call_finally_block(pScopeTable[trylevel].lpfnHandler, &frame->_ebp); + } + } + trylevel = pScopeTable[trylevel].previousTryLevel; + } + } + TRACE("reached TRYLEVEL_END, returning ExceptionContinueSearch\n"); + return ExceptionContinueSearch; +} + +/********************************************************************* + * _except_handler4_common (MSVCRT.@) + */ +int CDECL _except_handler4_common( ULONG *cookie, void (*check_cookie)(void), + EXCEPTION_RECORD *rec, MSVCRT_EXCEPTION_FRAME *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) +{ + int retval, trylevel; + EXCEPTION_POINTERS exceptPtrs; + const SCOPETABLE_V4 *scope_table = get_scopetable_v4( frame, *cookie ); + + TRACE( "exception %lx flags=%lx at %p handler=%p %p %p cookie=%lx scope table=%p cookies=%d/%lx,%d/%lx\n", + rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, + frame->handler, context, dispatcher, *cookie, scope_table, + scope_table->gs_cookie_offset, scope_table->gs_cookie_xor, + scope_table->eh_cookie_offset, scope_table->eh_cookie_xor ); + + /* FIXME: no cookie validation yet */ + + if (rec->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND)) + { + /* Unwinding the current frame */ + msvcrt_local_unwind4( cookie, frame, -2, &frame->_ebp ); + TRACE("unwound current frame, returning ExceptionContinueSearch\n"); + return ExceptionContinueSearch; + } + else + { + /* Hunting for handler */ + exceptPtrs.ExceptionRecord = rec; + exceptPtrs.ContextRecord = context; + *((DWORD *)frame-1) = (DWORD)&exceptPtrs; + trylevel = frame->trylevel; + + while (trylevel != -2) + { + TRACE( "level %d prev %d filter %p\n", trylevel, + scope_table->entries[trylevel].previousTryLevel, + scope_table->entries[trylevel].lpfnFilter ); + if (scope_table->entries[trylevel].lpfnFilter) + { + retval = call_filter( scope_table->entries[trylevel].lpfnFilter, &exceptPtrs, &frame->_ebp ); + + TRACE("filter returned %s\n", retval == EXCEPTION_CONTINUE_EXECUTION ? + "CONTINUE_EXECUTION" : retval == EXCEPTION_EXECUTE_HANDLER ? + "EXECUTE_HANDLER" : "CONTINUE_SEARCH"); + + if (retval == EXCEPTION_CONTINUE_EXECUTION) + return ExceptionContinueExecution; + + if (retval == EXCEPTION_EXECUTE_HANDLER) + { + __DestructExceptionObject(rec); + + /* Unwind all higher frames, this one will handle the exception */ + _global_unwind2((EXCEPTION_REGISTRATION_RECORD*)frame); + msvcrt_local_unwind4( cookie, frame, trylevel, &frame->_ebp ); + + /* Set our trylevel to the enclosing block, and call the __finally + * code, which won't return + */ + frame->trylevel = scope_table->entries[trylevel].previousTryLevel; + TRACE("__finally block %p\n",scope_table->entries[trylevel].lpfnHandler); + call_finally_block(scope_table->entries[trylevel].lpfnHandler, &frame->_ebp); + } + } + trylevel = scope_table->entries[trylevel].previousTryLevel; + } + } + TRACE("reached -2, returning ExceptionContinueSearch\n"); + return ExceptionContinueSearch; +} + + +/* + * setjmp/longjmp implementation + */ + +#define MSVCRT_JMP_MAGIC 0x56433230 /* ID value for new jump structure */ +typedef void (__stdcall *MSVCRT_unwind_function)(const _JUMP_BUFFER *); + +/* define an entrypoint for setjmp/setjmp3 that stores the registers in the jmp buf */ +/* and then jumps to the C backend function */ +#define DEFINE_SETJMP_ENTRYPOINT(name) \ + __ASM_GLOBAL_FUNC( name, \ + "movl 4(%esp),%ecx\n\t" /* jmp_buf */ \ + "movl %ebp,0(%ecx)\n\t" /* jmp_buf.Ebp */ \ + "movl %ebx,4(%ecx)\n\t" /* jmp_buf.Ebx */ \ + "movl %edi,8(%ecx)\n\t" /* jmp_buf.Edi */ \ + "movl %esi,12(%ecx)\n\t" /* jmp_buf.Esi */ \ + "movl %esp,16(%ecx)\n\t" /* jmp_buf.Esp */ \ + "movl 0(%esp),%eax\n\t" \ + "movl %eax,20(%ecx)\n\t" /* jmp_buf.Eip */ \ + "jmp " __ASM_NAME("__regs_") # name ) + +/******************************************************************* + * _setjmp (MSVCRT.@) + */ +#undef _setjmp +DEFINE_SETJMP_ENTRYPOINT( _setjmp ) +int CDECL __regs__setjmp(_JUMP_BUFFER *jmp) +{ + jmp->Registration = (unsigned long)NtCurrentTeb()->Tib.ExceptionList; + if (jmp->Registration == ~0UL) + jmp->TryLevel = TRYLEVEL_END; + else + jmp->TryLevel = ((MSVCRT_EXCEPTION_FRAME*)jmp->Registration)->trylevel; + + TRACE("buf=%p ebx=%08lx esi=%08lx edi=%08lx ebp=%08lx esp=%08lx eip=%08lx frame=%08lx\n", + jmp, jmp->Ebx, jmp->Esi, jmp->Edi, jmp->Ebp, jmp->Esp, jmp->Eip, jmp->Registration ); + return 0; +} + +/******************************************************************* + * _setjmp3 (MSVCRT.@) + */ +DEFINE_SETJMP_ENTRYPOINT( _setjmp3 ) +int WINAPIV __regs__setjmp3(_JUMP_BUFFER *jmp, int nb_args, ...) +{ + jmp->Cookie = MSVCRT_JMP_MAGIC; + jmp->UnwindFunc = 0; + jmp->Registration = (unsigned long)NtCurrentTeb()->Tib.ExceptionList; + if (jmp->Registration == ~0UL) + { + jmp->TryLevel = TRYLEVEL_END; + } + else + { + int i; + va_list args; + + va_start( args, nb_args ); + if (nb_args > 0) jmp->UnwindFunc = va_arg( args, unsigned long ); + if (nb_args > 1) jmp->TryLevel = va_arg( args, unsigned long ); + else jmp->TryLevel = ((MSVCRT_EXCEPTION_FRAME*)jmp->Registration)->trylevel; + for (i = 0; i < 6 && i < nb_args - 2; i++) + jmp->UnwindData[i] = va_arg( args, unsigned long ); + va_end( args ); + } + + TRACE("buf=%p ebx=%08lx esi=%08lx edi=%08lx ebp=%08lx esp=%08lx eip=%08lx frame=%08lx\n", + jmp, jmp->Ebx, jmp->Esi, jmp->Edi, jmp->Ebp, jmp->Esp, jmp->Eip, jmp->Registration ); + return 0; +} + +/********************************************************************* + * longjmp (MSVCRT.@) + */ +void __cdecl longjmp(_JUMP_BUFFER *jmp, int retval) +{ + unsigned long cur_frame = 0; + + TRACE("buf=%p ebx=%08lx esi=%08lx edi=%08lx ebp=%08lx esp=%08lx eip=%08lx frame=%08lx retval=%08x\n", + jmp, jmp->Ebx, jmp->Esi, jmp->Edi, jmp->Ebp, jmp->Esp, jmp->Eip, jmp->Registration, retval ); + + cur_frame=(unsigned long)NtCurrentTeb()->Tib.ExceptionList; + TRACE("cur_frame=%lx\n",cur_frame); + + if (cur_frame != jmp->Registration) + _global_unwind2((EXCEPTION_REGISTRATION_RECORD*)jmp->Registration); + + if (jmp->Registration) + { + if (IsBadReadPtr(&jmp->Cookie, sizeof(long)) || jmp->Cookie != MSVCRT_JMP_MAGIC) + { + msvcrt_local_unwind2((MSVCRT_EXCEPTION_FRAME*)jmp->Registration, + jmp->TryLevel, (void *)jmp->Ebp); + } + else if(jmp->UnwindFunc) + { + MSVCRT_unwind_function unwind_func; + + unwind_func=(MSVCRT_unwind_function)jmp->UnwindFunc; + unwind_func(jmp); + } + } + + if (!retval) + retval = 1; + + __wine_longjmp( (__wine_jmp_buf *)jmp, retval ); +} + +/********************************************************************* + * _seh_longjmp_unwind (MSVCRT.@) + */ +void __stdcall _seh_longjmp_unwind(_JUMP_BUFFER *jmp) +{ + msvcrt_local_unwind2( (MSVCRT_EXCEPTION_FRAME *)jmp->Registration, jmp->TryLevel, (void *)jmp->Ebp ); +} + +/********************************************************************* + * _seh_longjmp_unwind4 (MSVCRT.@) + */ +void __stdcall _seh_longjmp_unwind4(_JUMP_BUFFER *jmp) +{ + msvcrt_local_unwind4( (ULONG *)&jmp->Cookie, (MSVCRT_EXCEPTION_FRAME *)jmp->Registration, + jmp->TryLevel, (void *)jmp->Ebp ); +} + +/********************************************************************* + * handle_fpieee_flt + */ +int handle_fpieee_flt( __msvcrt_ulong exception_code, EXCEPTION_POINTERS *ep, + int (__cdecl *handler)(_FPIEEE_RECORD*) ) +{ + FLOATING_SAVE_AREA *ctx = &ep->ContextRecord->FloatSave; + _FPIEEE_RECORD rec; + int ret; + + memset(&rec, 0, sizeof(rec)); + rec.RoundingMode = ctx->ControlWord >> 10; + switch((ctx->ControlWord >> 8) & 0x3) { + case 0: rec.Precision = 2; break; + case 1: rec.Precision = 3; break; + case 2: rec.Precision = 1; break; + case 3: rec.Precision = 0; break; + } + rec.Status.InvalidOperation = ctx->StatusWord & 0x1; + rec.Status.ZeroDivide = ((ctx->StatusWord & 0x4) != 0); + rec.Status.Overflow = ((ctx->StatusWord & 0x8) != 0); + rec.Status.Underflow = ((ctx->StatusWord & 0x10) != 0); + rec.Status.Inexact = ((ctx->StatusWord & 0x20) != 0); + rec.Enable.InvalidOperation = ((ctx->ControlWord & 0x1) == 0); + rec.Enable.ZeroDivide = ((ctx->ControlWord & 0x4) == 0); + rec.Enable.Overflow = ((ctx->ControlWord & 0x8) == 0); + rec.Enable.Underflow = ((ctx->ControlWord & 0x10) == 0); + rec.Enable.Inexact = ((ctx->ControlWord & 0x20) == 0); + rec.Cause.InvalidOperation = rec.Enable.InvalidOperation & rec.Status.InvalidOperation; + rec.Cause.ZeroDivide = rec.Enable.ZeroDivide & rec.Status.ZeroDivide; + rec.Cause.Overflow = rec.Enable.Overflow & rec.Status.Overflow; + rec.Cause.Underflow = rec.Enable.Underflow & rec.Status.Underflow; + rec.Cause.Inexact = rec.Enable.Inexact & rec.Status.Inexact; + + TRACE("code %lx handler %p opcode %lx\n", exception_code, handler, + *(ULONG*)ep->ContextRecord->FloatSave.ErrorOffset); + + if(*(WORD*)ctx->ErrorOffset == 0x35dc) { /* fdiv m64fp */ + if(exception_code==STATUS_FLOAT_DIVIDE_BY_ZERO || exception_code==STATUS_FLOAT_INVALID_OPERATION) { + rec.Operand1.OperandValid = 1; + rec.Result.OperandValid = 0; + } else { + rec.Operand1.OperandValid = 0; + rec.Result.OperandValid = 1; + } + rec.Operand2.OperandValid = 1; + rec.Operation = _FpCodeDivide; + rec.Operand1.Format = _FpFormatFp80; + memcpy(&rec.Operand1.Value.Fp80Value, ctx->RegisterArea, sizeof(rec.Operand1.Value.Fp80Value)); + rec.Operand2.Format = _FpFormatFp64; + rec.Operand2.Value.Fp64Value = *(double*)ctx->DataOffset; + rec.Result.Format = _FpFormatFp80; + memcpy(&rec.Result.Value.Fp80Value, ctx->RegisterArea, sizeof(rec.Operand1.Value.Fp80Value)); + + ret = handler(&rec); + + if(ret == EXCEPTION_CONTINUE_EXECUTION) + memcpy(ctx->RegisterArea, &rec.Result.Value.Fp80Value, sizeof(rec.Operand1.Value.Fp80Value)); + return ret; + } + + FIXME("unsupported opcode: %lx\n", *(ULONG*)ep->ContextRecord->FloatSave.ErrorOffset); + return EXCEPTION_CONTINUE_SEARCH; +} + +#endif /* __i386__ */ diff --git a/dll/win32/msvcrt/except_x86_64.c b/dll/win32/msvcrt/except_x86_64.c new file mode 100644 index 00000000000..e0fda975be4 --- /dev/null +++ b/dll/win32/msvcrt/except_x86_64.c @@ -0,0 +1,157 @@ +/* + * msvcrt C++ exception handling + * + * Copyright 2011 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#if defined(__x86_64__) && !defined(__arm64ec__) + +#include +#include +#define longjmp ms_longjmp /* avoid prototype mismatch */ +#include +#undef longjmp + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "msvcrt.h" +#include "wine/exception.h" +#include "excpt.h" +#include "wine/debug.h" + +#include "cppexcept.h" + +WINE_DEFAULT_DEBUG_CHANNEL(seh); + +extern void *call_exc_handler( void *handler, ULONG_PTR frame, UINT flags ); +__ASM_GLOBAL_FUNC( call_exc_handler, + "subq $0x28,%rsp\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 0x28\n\t") + __ASM_SEH(".seh_stackalloc 0x28\n\t") + __ASM_SEH(".seh_endprologue\n\t") + "movq %rcx, 0x0(%rsp)\n\t" + "movl %r8d, 0x8(%rsp)\n\t" + "movq %rdx, 0x10(%rsp)\n\t" + "callq *%rcx\n\t" + "addq $0x28,%rsp\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset -0x28\n\t") + "ret" ) + + +/******************************************************************* + * call_catch_handler + */ +void *call_catch_handler( EXCEPTION_RECORD *rec ) +{ + ULONG_PTR frame = rec->ExceptionInformation[1]; + void *handler = (void *)rec->ExceptionInformation[5]; + + TRACE( "calling %p frame %Ix\n", handler, frame ); + return call_exc_handler( handler, frame, 0x100 ); +} + + +/******************************************************************* + * call_unwind_handler + */ +void *call_unwind_handler( void *handler, ULONG_PTR frame, DISPATCHER_CONTEXT *dispatch ) +{ + TRACE( "calling %p frame %Ix\n", handler, frame ); + return call_exc_handler( handler, frame, 0x100 ); +} + + +/******************************************************************* + * get_exception_pc + */ +ULONG_PTR get_exception_pc( DISPATCHER_CONTEXT *dispatch ) +{ + return dispatch->ControlPc; +} + + +/******************************************************************* + * longjmp (MSVCRT.@) + */ +#ifndef __WINE_PE_BUILD +void __cdecl longjmp( _JUMP_BUFFER *jmp, int retval ) +{ + EXCEPTION_RECORD rec; + + if (!retval) retval = 1; + if (jmp->Frame) + { + rec.ExceptionCode = STATUS_LONGJUMP; + rec.ExceptionFlags = 0; + rec.ExceptionRecord = NULL; + rec.ExceptionAddress = NULL; + rec.NumberParameters = 1; + rec.ExceptionInformation[0] = (DWORD_PTR)jmp; + RtlUnwind( (void *)jmp->Frame, (void *)jmp->Rip, &rec, IntToPtr(retval) ); + } + __wine_longjmp( (__wine_jmp_buf *)jmp, retval ); +} +#endif + +/******************************************************************* + * _local_unwind (MSVCRT.@) + */ +void __cdecl _local_unwind( void *frame, void *target ) +{ + RtlUnwind( frame, target, NULL, 0 ); +} + +/********************************************************************* + * handle_fpieee_flt + */ +int handle_fpieee_flt( __msvcrt_ulong exception_code, EXCEPTION_POINTERS *ep, + int (__cdecl *handler)(_FPIEEE_RECORD*) ) +{ + FIXME("(%lx %p %p) opcode: %#I64x\n", exception_code, ep, handler, + *(ULONG64*)ep->ContextRecord->Rip); + return EXCEPTION_CONTINUE_SEARCH; +} + +#if _MSVCR_VER>=110 && _MSVCR_VER<=120 +/********************************************************************* + * __crtCapturePreviousContext (MSVCR110.@) + */ +void __cdecl __crtCapturePreviousContext( CONTEXT *ctx ) +{ + UNWIND_HISTORY_TABLE table; + RUNTIME_FUNCTION *func; + PEXCEPTION_ROUTINE handler; + ULONG_PTR frame, base; + void *data; + ULONG i; + + RtlCaptureContext( ctx ); + for (i = 0; i < 2; i++) + { + if (!(func = RtlLookupFunctionEntry( ctx->Rip, &base, &table ))) break; + if (RtlVirtualUnwind2( UNW_FLAG_NHANDLER, base, ctx->Rip, func, ctx, NULL, + &data, &frame, NULL, NULL, NULL, &handler, 0 )) break; + if (!ctx->Rip) break; + if (!frame) break; + } +} +#endif + +#endif /* __x86_64__ */ diff --git a/dll/win32/msvcrt/exception_ptr.c b/dll/win32/msvcrt/exception_ptr.c new file mode 100644 index 00000000000..d4f86d713ca --- /dev/null +++ b/dll/win32/msvcrt/exception_ptr.c @@ -0,0 +1,271 @@ +/* + * std::exception_ptr helper functions + * + * Copyright 2022 Torge Matthies for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include +#include + +#include "windef.h" +#include "winternl.h" +#include "wine/exception.h" +#include "wine/debug.h" +#include "msvcrt.h" +#include "cppexcept.h" + +/* call a copy constructor */ +#ifdef __ASM_USE_THISCALL_WRAPPER +__ASM_GLOBAL_FUNC( call_copy_ctor, + "pushl %ebp\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + __ASM_CFI(".cfi_rel_offset %ebp,0\n\t") + "movl %esp, %ebp\n\t" + __ASM_CFI(".cfi_def_cfa_register %ebp\n\t") + "pushl $1\n\t" + "movl 12(%ebp), %ecx\n\t" + "pushl 16(%ebp)\n\t" + "call *8(%ebp)\n\t" + "leave\n" + __ASM_CFI(".cfi_def_cfa %esp,4\n\t") + __ASM_CFI(".cfi_same_value %ebp\n\t") + "ret" ) +__ASM_GLOBAL_FUNC( call_dtor, + "movl 8(%esp),%ecx\n\t" + "call *4(%esp)\n\t" + "ret" ) +#endif + +#if _MSVCR_VER >= 100 + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +/********************************************************************* + * ?__ExceptionPtrCreate@@YAXPAX@Z + * ?__ExceptionPtrCreate@@YAXPEAX@Z + */ +void __cdecl __ExceptionPtrCreate(exception_ptr *ep) +{ + TRACE("(%p)\n", ep); + + ep->rec = NULL; + ep->ref = NULL; +} + +/********************************************************************* + * ?__ExceptionPtrDestroy@@YAXPAX@Z + * ?__ExceptionPtrDestroy@@YAXPEAX@Z + */ +void __cdecl __ExceptionPtrDestroy(exception_ptr *ep) +{ + TRACE("(%p)\n", ep); + + if (!ep->rec) + return; + + if (!InterlockedDecrement(ep->ref)) + { + if (ep->rec->ExceptionCode == CXX_EXCEPTION) + { + const cxx_exception_type *type = (void*)ep->rec->ExceptionInformation[2]; + void *obj = (void*)ep->rec->ExceptionInformation[1]; + uintptr_t base = rtti_rva_base( type ); + + if (type && type->destructor) call_dtor( rtti_rva(type->destructor, base), obj ); + HeapFree(GetProcessHeap(), 0, obj); + } + + HeapFree(GetProcessHeap(), 0, ep->rec); + HeapFree(GetProcessHeap(), 0, ep->ref); + } +} + +#ifndef _CONCRT + +/********************************************************************* + * ?__ExceptionPtrCopy@@YAXPAXPBX@Z + * ?__ExceptionPtrCopy@@YAXPEAXPEBX@Z + */ +void __cdecl __ExceptionPtrCopy(exception_ptr *ep, const exception_ptr *copy) +{ + TRACE("(%p %p)\n", ep, copy); + + /* don't destroy object stored in ep */ + *ep = *copy; + if (ep->ref) + InterlockedIncrement(copy->ref); +} + +/********************************************************************* + * ?__ExceptionPtrAssign@@YAXPAXPBX@Z + * ?__ExceptionPtrAssign@@YAXPEAXPEBX@Z + */ +void __cdecl __ExceptionPtrAssign(exception_ptr *ep, const exception_ptr *assign) +{ + TRACE("(%p %p)\n", ep, assign); + + /* don't destroy object stored in ep */ + if (ep->ref) + InterlockedDecrement(ep->ref); + + *ep = *assign; + if (ep->ref) + InterlockedIncrement(ep->ref); +} + +#endif + +/********************************************************************* + * ?__ExceptionPtrRethrow@@YAXPBX@Z + * ?__ExceptionPtrRethrow@@YAXPEBX@Z + */ +void __cdecl __ExceptionPtrRethrow(const exception_ptr *ep) +{ + TRACE("(%p)\n", ep); + + if (!ep->rec) + { + throw_exception("bad exception"); + return; + } + + RaiseException(ep->rec->ExceptionCode, ep->rec->ExceptionFlags & ~EXCEPTION_UNWINDING, + ep->rec->NumberParameters, ep->rec->ExceptionInformation); +} + +void exception_ptr_from_record(exception_ptr *ep, EXCEPTION_RECORD *rec) +{ + TRACE("(%p)\n", ep); + + if (!rec) + { + ep->rec = NULL; + ep->ref = NULL; + return; + } + + ep->rec = HeapAlloc(GetProcessHeap(), 0, sizeof(EXCEPTION_RECORD)); + ep->ref = HeapAlloc(GetProcessHeap(), 0, sizeof(int)); + + *ep->rec = *rec; + *ep->ref = 1; + + if (ep->rec->ExceptionCode == CXX_EXCEPTION) + { + void *obj = (void*)ep->rec->ExceptionInformation[1]; + const cxx_exception_type *et = (void*)ep->rec->ExceptionInformation[2]; + uintptr_t base = rtti_rva_base( et ); + const cxx_type_info_table *table = rtti_rva( et->type_info_table, base ); + const cxx_type_info *ti = rtti_rva( table->info[0], base ); + void **data = HeapAlloc(GetProcessHeap(), 0, ti->size); + + if (ti->flags & CLASS_IS_SIMPLE_TYPE) + { + memcpy(data, obj, ti->size); + if (ti->size == sizeof(void *)) *data = get_this_pointer(&ti->offsets, *data); + } + else if (ti->copy_ctor) + { + call_copy_ctor(rtti_rva(ti->copy_ctor, base), data, get_this_pointer(&ti->offsets, obj), + ti->flags & CLASS_HAS_VIRTUAL_BASE_CLASS); + } + else + memcpy(data, get_this_pointer(&ti->offsets, obj), ti->size); + ep->rec->ExceptionInformation[1] = (ULONG_PTR)data; + } + return; +} + +#ifndef _CONCRT + +/********************************************************************* + * ?__ExceptionPtrCurrentException@@YAXPAX@Z + * ?__ExceptionPtrCurrentException@@YAXPEAX@Z + */ +void __cdecl __ExceptionPtrCurrentException(exception_ptr *ep) +{ + TRACE("(%p)\n", ep); + exception_ptr_from_record(ep, msvcrt_get_thread_data()->exc_record); +} + +#if _MSVCR_VER >= 110 +/********************************************************************* + * ?__ExceptionPtrToBool@@YA_NPBX@Z + * ?__ExceptionPtrToBool@@YA_NPEBX@Z + */ +bool __cdecl __ExceptionPtrToBool(exception_ptr *ep) +{ + return !!ep->rec; +} +#endif + +/********************************************************************* + * ?__ExceptionPtrCopyException@@YAXPAXPBX1@Z + * ?__ExceptionPtrCopyException@@YAXPEAXPEBX1@Z + */ +void __cdecl __ExceptionPtrCopyException(exception_ptr *ep, + exception *object, const cxx_exception_type *type) +{ + const cxx_type_info_table *table; + const cxx_type_info *ti; + void **data; + uintptr_t base = rtti_rva_base( type ); + + __ExceptionPtrDestroy(ep); + + ep->rec = HeapAlloc(GetProcessHeap(), 0, sizeof(EXCEPTION_RECORD)); + ep->ref = HeapAlloc(GetProcessHeap(), 0, sizeof(int)); + *ep->ref = 1; + + memset(ep->rec, 0, sizeof(EXCEPTION_RECORD)); + ep->rec->ExceptionCode = CXX_EXCEPTION; + ep->rec->ExceptionFlags = EXCEPTION_NONCONTINUABLE; + ep->rec->NumberParameters = CXX_EXCEPTION_PARAMS; + ep->rec->ExceptionInformation[0] = CXX_FRAME_MAGIC_VC6; + ep->rec->ExceptionInformation[2] = (ULONG_PTR)type; + if (CXX_EXCEPTION_PARAMS == 4) ep->rec->ExceptionInformation[3] = base; + + table = rtti_rva( type->type_info_table, base ); + ti = rtti_rva( table->info[0], base ); + data = HeapAlloc(GetProcessHeap(), 0, ti->size); + if (ti->flags & CLASS_IS_SIMPLE_TYPE) + { + memcpy(data, object, ti->size); + if (ti->size == sizeof(void *)) *data = get_this_pointer(&ti->offsets, *data); + } + else if (ti->copy_ctor) + { + call_copy_ctor( rtti_rva(ti->copy_ctor, base), data, get_this_pointer(&ti->offsets, object), + ti->flags & CLASS_HAS_VIRTUAL_BASE_CLASS); + } + else + memcpy(data, get_this_pointer(&ti->offsets, object), ti->size); + ep->rec->ExceptionInformation[1] = (ULONG_PTR)data; +} + +/********************************************************************* + * ?__ExceptionPtrCompare@@YA_NPBX0@Z + * ?__ExceptionPtrCompare@@YA_NPEBX0@Z + */ +bool __cdecl __ExceptionPtrCompare(const exception_ptr *ep1, const exception_ptr *ep2) +{ + return ep1->rec == ep2->rec; +} + +#endif + +#endif /* _MSVCR_VER >= 100 */ diff --git a/dll/win32/msvcrt/exit.c b/dll/win32/msvcrt/exit.c new file mode 100644 index 00000000000..c8db6c405da --- /dev/null +++ b/dll/win32/msvcrt/exit.c @@ -0,0 +1,523 @@ +/* + * msvcrt.dll exit functions + * + * Copyright 2000 Jon Griffiths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ +#include +#include +#include +#include +#include "msvcrt.h" +#include "mtdll.h" +#include "winuser.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +/* MT */ +#define LOCK_EXIT _lock(_EXIT_LOCK1) +#define UNLOCK_EXIT _unlock(_EXIT_LOCK1) + +static _purecall_handler purecall_handler = NULL; + +static _onexit_table_t MSVCRT_atexit_table; + +typedef void (__stdcall *_tls_callback_type)(void*,ULONG,void*); +static _tls_callback_type tls_atexit_callback; + +static CRITICAL_SECTION MSVCRT_onexit_cs; +static CRITICAL_SECTION_DEBUG MSVCRT_onexit_cs_debug = +{ + 0, 0, &MSVCRT_onexit_cs, + { &MSVCRT_onexit_cs_debug.ProcessLocksList, &MSVCRT_onexit_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": MSVCRT_onexit_cs") } +}; +static CRITICAL_SECTION MSVCRT_onexit_cs = { &MSVCRT_onexit_cs_debug, -1, 0, 0, 0, 0 }; + +extern int MSVCRT_app_type; +extern wchar_t *MSVCRT__wpgmptr; + +#if _MSVCR_VER > 0 || defined(_DEBUG) +static unsigned int MSVCRT_abort_behavior = _WRITE_ABORT_MSG | _CALL_REPORTFAULT; +#endif + +static int MSVCRT_error_mode = _OUT_TO_DEFAULT; + +void (*CDECL _aexit_rtn)(int) = _exit; + +static int initialize_onexit_table(_onexit_table_t *table) +{ + if (!table) + return -1; + + if (table->_first == table->_end) + table->_last = table->_end = table->_first = NULL; + return 0; +} + +static int register_onexit_function(_onexit_table_t *table, _onexit_t func) +{ + if (!table) + return -1; + + EnterCriticalSection(&MSVCRT_onexit_cs); + if (!table->_first) + { + table->_first = calloc(32, sizeof(void *)); + if (!table->_first) + { + WARN("failed to allocate initial table.\n"); + LeaveCriticalSection(&MSVCRT_onexit_cs); + return -1; + } + table->_last = table->_first; + table->_end = table->_first + 32; + } + + /* grow if full */ + if (table->_last == table->_end) + { + int len = table->_end - table->_first; + _PVFV *tmp = realloc(table->_first, 2 * len * sizeof(void *)); + if (!tmp) + { + WARN("failed to grow table.\n"); + LeaveCriticalSection(&MSVCRT_onexit_cs); + return -1; + } + table->_first = tmp; + table->_end = table->_first + 2 * len; + table->_last = table->_first + len; + } + + *table->_last = (_PVFV)func; + table->_last++; + LeaveCriticalSection(&MSVCRT_onexit_cs); + return 0; +} + +static int execute_onexit_table(_onexit_table_t *table) +{ + _onexit_table_t copy; + _PVFV *func; + + if (!table) + return -1; + + EnterCriticalSection(&MSVCRT_onexit_cs); + if (!table->_first || table->_first >= table->_last) + { + LeaveCriticalSection(&MSVCRT_onexit_cs); + return 0; + } + copy._first = table->_first; + copy._last = table->_last; + copy._end = table->_end; + memset(table, 0, sizeof(*table)); + initialize_onexit_table(table); + LeaveCriticalSection(&MSVCRT_onexit_cs); + + for (func = copy._last - 1; func >= copy._first; func--) + { + if (*func) + (*func)(); + } + + free(copy._first); + return 0; +} + +static void call_atexit(void) +{ + /* Note: should only be called with the exit lock held */ + if (tls_atexit_callback) tls_atexit_callback(NULL, DLL_PROCESS_DETACH, NULL); + execute_onexit_table(&MSVCRT_atexit_table); +} + +/********************************************************************* + * __dllonexit (MSVCRT.@) + */ +_onexit_t CDECL __dllonexit(_onexit_t func, _onexit_t **start, _onexit_t **end) +{ + _onexit_t *tmp; + int len; + + TRACE("(%p,%p,%p)\n", func, start, end); + + if (!start || !*start || !end || !*end) + { + FIXME("bad table\n"); + return NULL; + } + + len = (*end - *start); + + TRACE("table start %p-%p, %d entries\n", *start, *end, len); + + if (++len <= 0) + return NULL; + + tmp = realloc(*start, len * sizeof(*tmp)); + if (!tmp) + return NULL; + *start = tmp; + *end = tmp + len; + tmp[len - 1] = func; + TRACE("new table start %p-%p, %d entries\n", *start, *end, len); + return func; +} + +/********************************************************************* + * _exit (MSVCRT.@) + */ +void CDECL _exit(int exitcode) +{ + TRACE("(%d)\n", exitcode); + ExitProcess(exitcode); +} + +/* Print out an error message with an option to debug */ +static void DoMessageBoxW(const wchar_t *lead, const wchar_t *message) +{ + MSGBOXPARAMSW msgbox; + wchar_t text[2048]; + INT ret; + + _snwprintf(text, ARRAY_SIZE(text), L"%ls\n\nProgram: %ls\n%ls\n\n" + L"Press OK to exit the program, or Cancel to start the Wine debugger.\n", + lead, MSVCRT__wpgmptr, message); + + msgbox.cbSize = sizeof(msgbox); + msgbox.hwndOwner = GetActiveWindow(); + msgbox.hInstance = 0; + msgbox.lpszText = text; + msgbox.lpszCaption = L"Wine C++ Runtime Library"; + msgbox.dwStyle = MB_OKCANCEL|MB_ICONERROR; + msgbox.lpszIcon = NULL; + msgbox.dwContextHelpId = 0; + msgbox.lpfnMsgBoxCallback = NULL; + msgbox.dwLanguageId = LANG_NEUTRAL; + + ret = MessageBoxIndirectW(&msgbox); + if (ret == IDCANCEL) + DebugBreak(); +} + +static void DoMessageBox(const char *lead, const char *message) +{ + wchar_t leadW[1024], messageW[1024]; + + mbstowcs(leadW, lead, 1024); + mbstowcs(messageW, message, 1024); + + DoMessageBoxW(leadW, messageW); +} + +/********************************************************************* + * _amsg_exit (MSVCRT.@) + */ +void CDECL _amsg_exit(int errnum) +{ + TRACE("(%d)\n", errnum); + + if ((MSVCRT_error_mode == _OUT_TO_MSGBOX) || + ((MSVCRT_error_mode == _OUT_TO_DEFAULT) && (MSVCRT_app_type == 2))) + { + char text[32]; + sprintf(text, "Error: R60%d",errnum); + DoMessageBox("Runtime error!", text); + } + else + _cprintf("\nruntime error R60%d\n",errnum); + _aexit_rtn(255); +} + +/********************************************************************* + * abort (MSVCRT.@) + */ +void CDECL abort(void) +{ + TRACE("()\n"); + +#if (_MSVCR_VER > 0 && _MSVCR_VER < 100) || _MSVCR_VER == 120 || defined(_DEBUG) + if (MSVCRT_abort_behavior & _WRITE_ABORT_MSG) + { + if ((MSVCRT_error_mode == _OUT_TO_MSGBOX) || + ((MSVCRT_error_mode == _OUT_TO_DEFAULT) && (MSVCRT_app_type == 2))) + { + DoMessageBox("Runtime error!", "abnormal program termination"); + } + else + _cputs("\nabnormal program termination\n"); + } +#endif + raise(SIGABRT); + /* in case raise() returns */ + _exit(3); +} + +#if _MSVCR_VER>=80 +/********************************************************************* + * _set_abort_behavior (MSVCR80.@) + */ +unsigned int CDECL _set_abort_behavior(unsigned int flags, unsigned int mask) +{ + unsigned int old = MSVCRT_abort_behavior; + + TRACE("%x, %x\n", flags, mask); + if (mask & _CALL_REPORTFAULT) + FIXME("_WRITE_CALL_REPORTFAULT unhandled\n"); + + MSVCRT_abort_behavior = (MSVCRT_abort_behavior & ~mask) | (flags & mask); + return old; +} +#endif + +/********************************************************************* + * _wassert (MSVCRT.@) + */ +void DECLSPEC_NORETURN CDECL _wassert(const wchar_t* str, const wchar_t* file, unsigned int line) +{ + ERR("(%s,%s,%d)\n", debugstr_w(str), debugstr_w(file), line); + + if ((MSVCRT_error_mode == _OUT_TO_MSGBOX) || + ((MSVCRT_error_mode == _OUT_TO_DEFAULT) && (MSVCRT_app_type == 2))) + { + wchar_t text[2048]; + _snwprintf(text, sizeof(text), L"File: %ls\nLine: %d\n\nExpression: \"%ls\"", file, line, str); + DoMessageBoxW(L"Assertion failed!", text); + } + else + fwprintf(stderr, L"Assertion failed: %ls, file %ls, line %d\n\n", str, file, line); + + raise(SIGABRT); + _exit(3); +} + +/********************************************************************* + * _assert (MSVCRT.@) + */ +void DECLSPEC_NORETURN CDECL _assert(const char* str, const char* file, unsigned int line) +{ + wchar_t strW[1024], fileW[1024]; + + mbstowcs(strW, str, 1024); + mbstowcs(fileW, file, 1024); + + _wassert(strW, fileW, line); +} + +/********************************************************************* + * _c_exit (MSVCRT.@) + */ +void CDECL _c_exit(void) +{ + TRACE("(void)\n"); + /* All cleanup is done on DLL detach; Return to caller */ +} + +/********************************************************************* + * _cexit (MSVCRT.@) + */ +void CDECL _cexit(void) +{ + TRACE("(void)\n"); + LOCK_EXIT; + call_atexit(); + UNLOCK_EXIT; +} + +/********************************************************************* + * _onexit (MSVCRT.@) + */ +_onexit_t CDECL _onexit(_onexit_t func) +{ + TRACE("(%p)\n",func); + + if (!func) + return NULL; + + LOCK_EXIT; + register_onexit_function(&MSVCRT_atexit_table, func); + UNLOCK_EXIT; + + return func; +} + +/********************************************************************* + * exit (MSVCRT.@) + */ +void CDECL exit(int exitcode) +{ + HMODULE hmscoree; + void (WINAPI *pCorExitProcess)(int); + + TRACE("(%d)\n",exitcode); + _cexit(); + + hmscoree = GetModuleHandleW(L"mscoree"); + + if (hmscoree) + { + pCorExitProcess = (void*)GetProcAddress(hmscoree, "CorExitProcess"); + + if (pCorExitProcess) + pCorExitProcess(exitcode); + } + + ExitProcess(exitcode); +} + +/********************************************************************* + * atexit (MSVCRT.@) + */ +int CDECL MSVCRT_atexit(void (__cdecl *func)(void)) +{ + TRACE("(%p)\n", func); + return _onexit((_onexit_t)func) == (_onexit_t)func ? 0 : -1; +} + +#if _MSVCR_VER >= 140 +static _onexit_table_t MSVCRT_quick_exit_table; + +/********************************************************************* + * _crt_at_quick_exit (UCRTBASE.@) + */ +int CDECL _crt_at_quick_exit(void (__cdecl *func)(void)) +{ + TRACE("(%p)\n", func); + return register_onexit_function(&MSVCRT_quick_exit_table, (_onexit_t)func); +} + +/********************************************************************* + * quick_exit (UCRTBASE.@) + */ +void CDECL quick_exit(int exitcode) +{ + TRACE("(%d)\n", exitcode); + + execute_onexit_table(&MSVCRT_quick_exit_table); + _exit(exitcode); +} + +/********************************************************************* + * _crt_atexit (UCRTBASE.@) + */ +int CDECL _crt_atexit(void (__cdecl *func)(void)) +{ + TRACE("(%p)\n", func); + return _onexit((_onexit_t)func) == (_onexit_t)func ? 0 : -1; +} + +/********************************************************************* + * _initialize_onexit_table (UCRTBASE.@) + */ +int CDECL _initialize_onexit_table(_onexit_table_t *table) +{ + TRACE("(%p)\n", table); + + return initialize_onexit_table(table); +} + +/********************************************************************* + * _register_onexit_function (UCRTBASE.@) + */ +int CDECL _register_onexit_function(_onexit_table_t *table, _onexit_t func) +{ + TRACE("(%p %p)\n", table, func); + + return register_onexit_function(table, func); +} + +/********************************************************************* + * _execute_onexit_table (UCRTBASE.@) + */ +int CDECL _execute_onexit_table(_onexit_table_t *table) +{ + TRACE("(%p)\n", table); + + return execute_onexit_table(table); +} +#endif + +/********************************************************************* + * _register_thread_local_exe_atexit_callback (UCRTBASE.@) + */ +void CDECL _register_thread_local_exe_atexit_callback(_tls_callback_type callback) +{ + TRACE("(%p)\n", callback); + tls_atexit_callback = callback; +} + +#if _MSVCR_VER>=71 +/********************************************************************* + * _set_purecall_handler (MSVCR71.@) + */ +_purecall_handler CDECL _set_purecall_handler(_purecall_handler function) +{ + _purecall_handler ret = purecall_handler; + + TRACE("(%p)\n", function); + purecall_handler = function; + return ret; +} +#endif + +#if _MSVCR_VER>=80 +/********************************************************************* + * _get_purecall_handler (MSVCR80.@) + */ +_purecall_handler CDECL _get_purecall_handler(void) +{ + TRACE("\n"); + return purecall_handler; +} +#endif + +/********************************************************************* + * _purecall (MSVCRT.@) + */ +void CDECL _purecall(void) +{ + TRACE("(void)\n"); + + if(purecall_handler) + purecall_handler(); + _amsg_exit( 25 ); +} + +/****************************************************************************** + * _set_error_mode (MSVCRT.@) + * + * Set the error mode, which describes where the C run-time writes error messages. + * + * PARAMS + * mode - the new error mode + * + * RETURNS + * The old error mode. + * + */ +int CDECL _set_error_mode(int mode) +{ + + const int old = MSVCRT_error_mode; + if ( _REPORT_ERRMODE != mode ) { + MSVCRT_error_mode = mode; + } + return old; +} diff --git a/dll/win32/msvcrt/file.c b/dll/win32/msvcrt/file.c new file mode 100644 index 00000000000..dda12a70194 --- /dev/null +++ b/dll/win32/msvcrt/file.c @@ -0,0 +1,5896 @@ +/* + * msvcrt.dll file functions + * + * Copyright 1996,1998 Marcus Meissner + * Copyright 1996 Jukka Iivonen + * Copyright 1997,2000 Uwe Bonnes + * Copyright 2000 Jon Griffiths + * Copyright 2004 Eric Pouech + * Copyright 2004 Juan Lang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wincon.h" +#include "winternl.h" +#include "winnls.h" +#include "msvcrt.h" +#include "mtdll.h" +#include "wine/asm.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +#undef _fstat +#undef _fstati64 +#undef _stat +#undef _stati64 +#undef _wstat +#undef _wstati64 +#undef _fstat32 +#undef _fstat32i64 +#undef _stat32i64 +#undef _stat32 +#undef _wstat32 +#undef _wstat32i64 +#undef _fstat64i32 +#undef _fstat64 +#undef _stat64 +#undef _stat64i32 +#undef _wstat64i32 +#undef _wstat64 +int __cdecl _wstat64(const wchar_t*, struct _stat64*); + +/* for stat mode, permissions apply to all,owner and group */ +#define ALL_S_IREAD (_S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6)) +#define ALL_S_IWRITE (_S_IWRITE | (_S_IWRITE >> 3) | (_S_IWRITE >> 6)) +#define ALL_S_IEXEC (_S_IEXEC | (_S_IEXEC >> 3) | (_S_IEXEC >> 6)) + +/* _access() bit flags FIXME: incomplete */ +#define MSVCRT_W_OK 0x02 +#define MSVCRT_R_OK 0x04 + +/* values for wxflag in file descriptor */ +#define WX_OPEN 0x01 +#define WX_ATEOF 0x02 +#define WX_READNL 0x04 /* read started with \n */ +#define WX_PIPE 0x08 +#define WX_DONTINHERIT 0x10 +#define WX_APPEND 0x20 +#define WX_TTY 0x40 +#define WX_TEXT 0x80 + +static char utf8_bom[3] = { 0xef, 0xbb, 0xbf }; +static char utf16_bom[2] = { 0xff, 0xfe }; + +#define MSVCRT_INTERNAL_BUFSIZ 4096 + +enum textmode +{ + TEXTMODE_ANSI, + TEXTMODE_UTF8, + TEXTMODE_UTF16LE, +}; + +#if _MSVCR_VER >= 140 + +#define MSVCRT_MAX_FILES 8192 +#define MSVCRT_FD_BLOCK_SIZE 64 + +typedef struct { + CRITICAL_SECTION crit; + HANDLE handle; + __int64 startpos; + unsigned char wxflag; + char textmode; + char lookahead[3]; + unsigned int unicode : 1; + unsigned int utf8translations : 1; + unsigned int dbcsBufferUsed : 1; + char dbcsBuffer[MB_LEN_MAX]; +} ioinfo; + +/********************************************************************* + * __badioinfo (MSVCRT.@) + */ +ioinfo MSVCRT___badioinfo = { {0}, INVALID_HANDLE_VALUE, 0, WX_TEXT }; +#else + +#define MSVCRT_MAX_FILES 2048 +#define MSVCRT_FD_BLOCK_SIZE 32 + +typedef struct { + HANDLE handle; + unsigned char wxflag; + char lookahead[3]; + int exflag; + CRITICAL_SECTION crit; +#if _MSVCR_VER >= 80 + char textmode : 7; + char unicode : 1; + char pipech2[2]; + __int64 startpos; + BOOL utf8translations; + char dbcsBuffer[1]; + BOOL dbcsBufferUsed; +#endif +} ioinfo; + +/********************************************************************* + * __badioinfo (MSVCRT.@) + */ +ioinfo MSVCRT___badioinfo = { INVALID_HANDLE_VALUE, WX_TEXT }; +#endif + +/********************************************************************* + * __pioinfo (MSVCRT.@) + * array of pointers to ioinfo arrays [32] + */ +ioinfo * MSVCRT___pioinfo[MSVCRT_MAX_FILES/MSVCRT_FD_BLOCK_SIZE] = { 0 }; + +#if _MSVCR_VER >= 80 + +#if _MSVCR_VER >= 140 +static inline BOOL ioinfo_is_crit_init(ioinfo *info) +{ + return TRUE; +} + +static inline void ioinfo_set_crit_init(ioinfo *info) +{ +} +#else +static inline BOOL ioinfo_is_crit_init(ioinfo *info) +{ + return info->exflag & 1; +} + +static inline void ioinfo_set_crit_init(ioinfo *info) +{ + info->exflag |= 1; +} +#endif + +static inline enum textmode ioinfo_get_textmode(ioinfo *info) +{ + return info->textmode; +} + +static inline void ioinfo_set_textmode(ioinfo *info, enum textmode mode) +{ + info->textmode = mode; +} + +static inline void ioinfo_set_unicode(ioinfo *info, BOOL unicode) +{ + info->unicode = !!unicode; +} +#else + +#define EF_UTF8 0x01 +#define EF_UTF16 0x02 +#define EF_CRIT_INIT 0x04 +#define EF_UNK_UNICODE 0x08 + +static inline BOOL ioinfo_is_crit_init(ioinfo *info) +{ + return info->exflag & EF_CRIT_INIT; +} + +static inline void ioinfo_set_crit_init(ioinfo *info) +{ + info->exflag |= EF_CRIT_INIT; +} + +static inline enum textmode ioinfo_get_textmode(ioinfo *info) +{ + if (info->exflag & EF_UTF8) + return TEXTMODE_UTF8; + if (info->exflag & EF_UTF16) + return TEXTMODE_UTF16LE; + return TEXTMODE_ANSI; +} + +static inline void ioinfo_set_textmode(ioinfo *info, enum textmode mode) +{ + info->exflag &= EF_CRIT_INIT | EF_UNK_UNICODE; + switch (mode) + { + case TEXTMODE_ANSI: + break; + case TEXTMODE_UTF8: + info->exflag |= EF_UTF8; + break; + case TEXTMODE_UTF16LE: + info->exflag |= EF_UTF16; + break; + } +} + +static inline void ioinfo_set_unicode(ioinfo *info, BOOL unicode) +{ + if (unicode) + info->exflag |= EF_UNK_UNICODE; + else + info->exflag &= ~EF_UNK_UNICODE; +} +#endif + +typedef struct { + FILE file; + CRITICAL_SECTION crit; +} file_crit; + +#if _MSVCR_VER >= 140 +file_crit MSVCRT__iob[_IOB_ENTRIES] = { 0 }; + +static FILE* iob_get_file(int i) +{ + return &MSVCRT__iob[i].file; +} + +static CRITICAL_SECTION* file_get_cs(FILE *f) +{ + return &((file_crit*)f)->crit; +} +#else +FILE MSVCRT__iob[_IOB_ENTRIES] = { { 0 } }; + +static FILE* iob_get_file(int i) +{ + return &MSVCRT__iob[i]; +} + +static CRITICAL_SECTION* file_get_cs(FILE *f) +{ + if (f < iob_get_file(0) || f >= iob_get_file(_IOB_ENTRIES)) + return &((file_crit*)f)->crit; + return NULL; +} +#endif + +static file_crit* MSVCRT_fstream[MSVCRT_MAX_FILES/MSVCRT_FD_BLOCK_SIZE]; +static int MSVCRT_max_streams = 512, MSVCRT_stream_idx; + +/* INTERNAL: process umask */ +static int MSVCRT_umask = 0; + +/* INTERNAL: static data for tmpnam and _wtmpname functions */ +static LONG tmpnam_unique; +static LONG tmpnam_s_unique; + +#define TOUL(x) (ULONGLONG)(x) +static const ULONGLONG WCEXE = TOUL('e') << 32 | TOUL('x') << 16 | TOUL('e'); +static const ULONGLONG WCBAT = TOUL('b') << 32 | TOUL('a') << 16 | TOUL('t'); +static const ULONGLONG WCCMD = TOUL('c') << 32 | TOUL('m') << 16 | TOUL('d'); +static const ULONGLONG WCCOM = TOUL('c') << 32 | TOUL('o') << 16 | TOUL('m'); + +/* This critical section protects the MSVCRT_fstreams table + * and MSVCRT_stream_idx from race conditions. It also + * protects fd critical sections creation code. + */ +static CRITICAL_SECTION MSVCRT_file_cs; +static CRITICAL_SECTION_DEBUG MSVCRT_file_cs_debug = +{ + 0, 0, &MSVCRT_file_cs, + { &MSVCRT_file_cs_debug.ProcessLocksList, &MSVCRT_file_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": MSVCRT_file_cs") } +}; +static CRITICAL_SECTION MSVCRT_file_cs = { &MSVCRT_file_cs_debug, -1, 0, 0, 0, 0 }; +#define LOCK_FILES() do { EnterCriticalSection(&MSVCRT_file_cs); } while (0) +#define UNLOCK_FILES() do { LeaveCriticalSection(&MSVCRT_file_cs); } while (0) + +static void msvcrt_stat64_to_stat(const struct _stat64 *buf64, struct _stat *buf) +{ + buf->st_dev = buf64->st_dev; + buf->st_ino = buf64->st_ino; + buf->st_mode = buf64->st_mode; + buf->st_nlink = buf64->st_nlink; + buf->st_uid = buf64->st_uid; + buf->st_gid = buf64->st_gid; + buf->st_rdev = buf64->st_rdev; + buf->st_size = buf64->st_size; + buf->st_atime = buf64->st_atime; + buf->st_mtime = buf64->st_mtime; + buf->st_ctime = buf64->st_ctime; +} + +static void msvcrt_stat64_to_stati64(const struct _stat64 *buf64, struct _stati64 *buf) +{ + buf->st_dev = buf64->st_dev; + buf->st_ino = buf64->st_ino; + buf->st_mode = buf64->st_mode; + buf->st_nlink = buf64->st_nlink; + buf->st_uid = buf64->st_uid; + buf->st_gid = buf64->st_gid; + buf->st_rdev = buf64->st_rdev; + buf->st_size = buf64->st_size; + buf->st_atime = buf64->st_atime; + buf->st_mtime = buf64->st_mtime; + buf->st_ctime = buf64->st_ctime; +} + +static void msvcrt_stat64_to_stat32(const struct _stat64 *buf64, struct _stat32 *buf) +{ + buf->st_dev = buf64->st_dev; + buf->st_ino = buf64->st_ino; + buf->st_mode = buf64->st_mode; + buf->st_nlink = buf64->st_nlink; + buf->st_uid = buf64->st_uid; + buf->st_gid = buf64->st_gid; + buf->st_rdev = buf64->st_rdev; + buf->st_size = buf64->st_size; + buf->st_atime = buf64->st_atime; + buf->st_mtime = buf64->st_mtime; + buf->st_ctime = buf64->st_ctime; +} + +static void msvcrt_stat64_to_stat64i32(const struct _stat64 *buf64, struct _stat64i32 *buf) +{ + buf->st_dev = buf64->st_dev; + buf->st_ino = buf64->st_ino; + buf->st_mode = buf64->st_mode; + buf->st_nlink = buf64->st_nlink; + buf->st_uid = buf64->st_uid; + buf->st_gid = buf64->st_gid; + buf->st_rdev = buf64->st_rdev; + buf->st_size = buf64->st_size; + buf->st_atime = buf64->st_atime; + buf->st_mtime = buf64->st_mtime; + buf->st_ctime = buf64->st_ctime; +} + +static void msvcrt_stat64_to_stat32i64(const struct _stat64 *buf64, struct _stat32i64 *buf) +{ + buf->st_dev = buf64->st_dev; + buf->st_ino = buf64->st_ino; + buf->st_mode = buf64->st_mode; + buf->st_nlink = buf64->st_nlink; + buf->st_uid = buf64->st_uid; + buf->st_gid = buf64->st_gid; + buf->st_rdev = buf64->st_rdev; + buf->st_size = buf64->st_size; + buf->st_atime = buf64->st_atime; + buf->st_mtime = buf64->st_mtime; + buf->st_ctime = buf64->st_ctime; +} + +static void time_to_filetime( __time64_t time, FILETIME *ft ) +{ + /* 1601 to 1970 is 369 years plus 89 leap days */ + static const __int64 secs_1601_to_1970 = ((369 * 365 + 89) * (__int64)86400); + + __int64 ticks = (time + secs_1601_to_1970) * 10000000; + ft->dwHighDateTime = ticks >> 32; + ft->dwLowDateTime = ticks; +} + +static inline ioinfo* get_ioinfo_nolock(int fd) +{ + ioinfo *ret = NULL; + if(fd>=0 && fdcrit); + ioinfo_set_crit_init(info); + } + UNLOCK_FILES(); + } +} + +static inline ioinfo* get_ioinfo(int fd) +{ + ioinfo *ret = get_ioinfo_nolock(fd); + if(ret == &MSVCRT___badioinfo) + return ret; + init_ioinfo_cs(ret); + EnterCriticalSection(&ret->crit); + return ret; +} + +static inline BOOL alloc_pioinfo_block(int fd) +{ + ioinfo *block; + int i; + + if(fd<0 || fd>=MSVCRT_MAX_FILES) + { + *_errno() = ENFILE; + return FALSE; + } + + block = calloc(MSVCRT_FD_BLOCK_SIZE, sizeof(ioinfo)); + if(!block) + { + WARN(":out of memory!\n"); + *_errno() = ENOMEM; + return FALSE; + } + for(i=0; i= 140, + * ioinfo_is_crit_init() is always TRUE. */ + InitializeCriticalSection(&block[i].crit); + } + } + if(InterlockedCompareExchangePointer((void**)&MSVCRT___pioinfo[fd/MSVCRT_FD_BLOCK_SIZE], block, NULL)) + { + if (ioinfo_is_crit_init(&block[0])) + { + for(i = 0; i < MSVCRT_FD_BLOCK_SIZE; ++i) + DeleteCriticalSection(&block[i].crit); + } + free(block); + } + return TRUE; +} + +static inline ioinfo* get_ioinfo_alloc_fd(int fd) +{ + ioinfo *ret; + + ret = get_ioinfo(fd); + if(ret != &MSVCRT___badioinfo) + return ret; + + if(!alloc_pioinfo_block(fd)) + return &MSVCRT___badioinfo; + + return get_ioinfo(fd); +} + +static inline ioinfo* get_ioinfo_alloc(int *fd) +{ + int i; + + *fd = -1; + for(i=0; icrit)) + { + if(info->handle == INVALID_HANDLE_VALUE) + { + *fd = i; + return info; + } + LeaveCriticalSection(&info->crit); + } + } + + WARN(":files exhausted!\n"); + *_errno() = ENFILE; + return &MSVCRT___badioinfo; +} + +static inline void release_ioinfo(ioinfo *info) +{ + if(info!=&MSVCRT___badioinfo && ioinfo_is_crit_init(info)) + LeaveCriticalSection(&info->crit); +} + +static inline FILE* msvcrt_get_file(int i) +{ + file_crit *ret; + + if(i >= MSVCRT_max_streams) + return NULL; + + if(i < _IOB_ENTRIES) + return iob_get_file(i); + + ret = MSVCRT_fstream[i/MSVCRT_FD_BLOCK_SIZE]; + if(!ret) { + MSVCRT_fstream[i/MSVCRT_FD_BLOCK_SIZE] = calloc(MSVCRT_FD_BLOCK_SIZE, sizeof(file_crit)); + if(!MSVCRT_fstream[i/MSVCRT_FD_BLOCK_SIZE]) { + ERR("out of memory\n"); + *_errno() = ENOMEM; + return NULL; + } + + ret = MSVCRT_fstream[i/MSVCRT_FD_BLOCK_SIZE] + (i%MSVCRT_FD_BLOCK_SIZE); + } else + ret += i%MSVCRT_FD_BLOCK_SIZE; + + return &ret->file; +} + +/* INTERNAL: free a file entry fd */ +static void msvcrt_free_fd(int fd) +{ + ioinfo *fdinfo = get_ioinfo(fd); + + if(fdinfo != &MSVCRT___badioinfo) + { + fdinfo->handle = INVALID_HANDLE_VALUE; + fdinfo->wxflag = 0; + } + TRACE(":fd (%d) freed\n",fd); + + if (fd < 3) + { + switch (fd) + { + case 0: + SetStdHandle(STD_INPUT_HANDLE, 0); + break; + case 1: + SetStdHandle(STD_OUTPUT_HANDLE, 0); + break; + case 2: + SetStdHandle(STD_ERROR_HANDLE, 0); + break; + } + } + release_ioinfo(fdinfo); +} + +static void msvcrt_set_fd(ioinfo *fdinfo, HANDLE hand, int flag) +{ + fdinfo->handle = hand; + fdinfo->wxflag = WX_OPEN | (flag & (WX_DONTINHERIT | WX_APPEND | WX_TEXT | WX_PIPE | WX_TTY)); + fdinfo->lookahead[0] = '\n'; + fdinfo->lookahead[1] = '\n'; + fdinfo->lookahead[2] = '\n'; + ioinfo_set_unicode(fdinfo, FALSE); + ioinfo_set_textmode(fdinfo, TEXTMODE_ANSI); + + if (hand != MSVCRT_NO_CONSOLE) + { + switch (fdinfo-MSVCRT___pioinfo[0]) + { + case 0: SetStdHandle(STD_INPUT_HANDLE, hand); break; + case 1: SetStdHandle(STD_OUTPUT_HANDLE, hand); break; + case 2: SetStdHandle(STD_ERROR_HANDLE, hand); break; + } + } +} + +/* INTERNAL: Allocate an fd slot from a Win32 HANDLE */ +static int msvcrt_alloc_fd(HANDLE hand, int flag) +{ + int fd; + ioinfo *info = get_ioinfo_alloc(&fd); + + TRACE(":handle (%p) allocating fd (%d)\n", hand, fd); + + if(info == &MSVCRT___badioinfo) + return -1; + + msvcrt_set_fd(info, hand, flag); + release_ioinfo(info); + return fd; +} + +/* INTERNAL: Allocate a FILE* for an fd slot */ +/* caller must hold the files lock */ +static FILE* msvcrt_alloc_fp(void) +{ + int i = 0; + FILE *file; + +#if _MSVCR_VER >= 140 + i = 3; +#endif + for (; i < MSVCRT_max_streams; i++) + { + file = msvcrt_get_file(i); + if (!file) + return NULL; + + if (file->_flag == 0) + { + if (i == MSVCRT_stream_idx) + { + CRITICAL_SECTION *cs = file_get_cs(file); + if (cs) + { + InitializeCriticalSectionEx(cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO); + cs->DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": file_crit.crit"); + } + MSVCRT_stream_idx++; + } + return file; + } + } + + return NULL; +} + +/* INTERNAL: initialize a FILE* from an open fd */ +static int msvcrt_init_fp(FILE* file, int fd, unsigned stream_flags) +{ + TRACE(":fd (%d) allocating FILE*\n",fd); + if (!(get_ioinfo_nolock(fd)->wxflag & WX_OPEN)) + { + WARN(":invalid fd %d\n",fd); + *__doserrno() = 0; + *_errno() = EBADF; + return -1; + } + file->_ptr = file->_base = NULL; + file->_cnt = 0; + file->_file = fd; + file->_flag = stream_flags; + file->_tmpfname = NULL; + + TRACE(":got FILE* (%p)\n",file); + return 0; +} + +/* INTERNAL: Create an inheritance data block (for spawned process) + * The inheritance block is made of: + * 00 int nb of file descriptor (NBFD) + * 04 char file flags (wxflag): repeated for each fd + * 4+NBFD HANDLE file handle: repeated for each fd + */ +BOOL msvcrt_create_io_inherit_block(WORD *size, BYTE **block) +{ + int fd, last_fd; + char* wxflag_ptr; + HANDLE* handle_ptr; + ioinfo* fdinfo; + + for (last_fd=MSVCRT_MAX_FILES-1; last_fd>=0; last_fd--) + if (get_ioinfo_nolock(last_fd)->handle != INVALID_HANDLE_VALUE) + break; + last_fd++; + + *size = sizeof(unsigned) + (sizeof(char) + sizeof(HANDLE)) * last_fd; + *block = calloc(1, *size); + if (!*block) + { + *size = 0; + return FALSE; + } + wxflag_ptr = (char*)*block + sizeof(unsigned); + handle_ptr = (HANDLE*)(wxflag_ptr + last_fd); + + *(unsigned*)*block = last_fd; + for (fd = 0; fd < last_fd; fd++) + { + /* to be inherited, we need it to be open, and that DONTINHERIT isn't set */ + fdinfo = get_ioinfo_nolock(fd); + if ((fdinfo->wxflag & (WX_OPEN | WX_DONTINHERIT)) == WX_OPEN) + { + *wxflag_ptr = fdinfo->wxflag; + *handle_ptr = fdinfo->handle; + } + else + { + *wxflag_ptr = 0; + *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) + */ +void msvcrt_init_io(void) +{ + STARTUPINFOA si; + int i; + ioinfo *fdinfo; + + GetStartupInfoA(&si); + if (si.cbReserved2 >= sizeof(unsigned int) && si.lpReserved2 != NULL) + { + BYTE* wxflag_ptr; + HANDLE* handle_ptr; + unsigned int count; + + count = *(unsigned*)si.lpReserved2; + wxflag_ptr = si.lpReserved2 + sizeof(unsigned); + handle_ptr = (HANDLE*)(wxflag_ptr + count); + + count = min(count, (si.cbReserved2 - sizeof(unsigned)) / (sizeof(HANDLE) + 1)); + count = min(count, MSVCRT_MAX_FILES); + for (i = 0; i < count; i++) + { + if ((*wxflag_ptr & WX_OPEN) && GetFileType(*handle_ptr) != FILE_TYPE_UNKNOWN) + { + fdinfo = get_ioinfo_alloc_fd(i); + if(fdinfo != &MSVCRT___badioinfo) + msvcrt_set_fd(fdinfo, *handle_ptr, *wxflag_ptr); + release_ioinfo(fdinfo); + } + + wxflag_ptr++; handle_ptr++; + } + } + + fdinfo = get_ioinfo_alloc_fd(STDIN_FILENO); + if (!(fdinfo->wxflag & WX_OPEN) || fdinfo->handle == INVALID_HANDLE_VALUE) { + HANDLE h = GetStdHandle(STD_INPUT_HANDLE); + DWORD flags = WX_OPEN | WX_TEXT; + DWORD type = GetFileType(h); + + if (type == FILE_TYPE_UNKNOWN) { + h = MSVCRT_NO_CONSOLE; + flags |= WX_TTY; + } else if ((type & 0xf) == FILE_TYPE_CHAR) { + flags |= WX_TTY; + } else if ((type & 0xf) == FILE_TYPE_PIPE) { + flags |= WX_PIPE; + } + + msvcrt_set_fd(fdinfo, h, flags); + } + release_ioinfo(fdinfo); + + fdinfo = get_ioinfo_alloc_fd(STDOUT_FILENO); + if (!(fdinfo->wxflag & WX_OPEN) || fdinfo->handle == INVALID_HANDLE_VALUE) { + HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD flags = WX_OPEN | WX_TEXT; + DWORD type = GetFileType(h); + + if (type == FILE_TYPE_UNKNOWN) { + h = MSVCRT_NO_CONSOLE; + flags |= WX_TTY; + } else if ((type & 0xf) == FILE_TYPE_CHAR) { + flags |= WX_TTY; + } else if ((type & 0xf) == FILE_TYPE_PIPE) { + flags |= WX_PIPE; + } + + msvcrt_set_fd(fdinfo, h, flags); + } + release_ioinfo(fdinfo); + + fdinfo = get_ioinfo_alloc_fd(STDERR_FILENO); + if (!(fdinfo->wxflag & WX_OPEN) || fdinfo->handle == INVALID_HANDLE_VALUE) { + HANDLE h = GetStdHandle(STD_ERROR_HANDLE); + DWORD flags = WX_OPEN | WX_TEXT; + DWORD type = GetFileType(h); + + if (type == FILE_TYPE_UNKNOWN) { + h = MSVCRT_NO_CONSOLE; + flags |= WX_TTY; + } else if ((type & 0xf) == FILE_TYPE_CHAR) { + flags |= WX_TTY; + } else if ((type & 0xf) == FILE_TYPE_PIPE) { + flags |= WX_PIPE; + } + + msvcrt_set_fd(fdinfo, h, flags); + } + release_ioinfo(fdinfo); + + TRACE(":handles (%p)(%p)(%p)\n", get_ioinfo_nolock(STDIN_FILENO)->handle, + get_ioinfo_nolock(STDOUT_FILENO)->handle, + get_ioinfo_nolock(STDERR_FILENO)->handle); + + for (i = 0; i < 3; i++) + { + FILE *f = iob_get_file(i); + CRITICAL_SECTION *cs = file_get_cs(f); + + /* FILE structs for stdin/out/err are static and never deleted */ + f->_file = get_ioinfo_nolock(i)->handle == MSVCRT_NO_CONSOLE ? + MSVCRT_NO_CONSOLE_FD : i; + f->_tmpfname = NULL; + f->_flag = (i == 0) ? _IOREAD : _IOWRT; + + if (cs) + { + InitializeCriticalSectionEx(cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO); + cs->DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": file_crit.crit"); + } + } + MSVCRT_stream_idx = 3; +} + +/* INTERNAL: Flush stdio file buffer */ +static int msvcrt_flush_buffer(FILE* file) +{ + int ret = 0; + + if((file->_flag & (_IOREAD|_IOWRT)) == _IOWRT && + file->_flag & (_IOMYBUF|MSVCRT__USERBUF)) { + int cnt=file->_ptr-file->_base; + if(cnt>0 && _write(file->_file, file->_base, cnt) != cnt) { + file->_flag |= _IOERR; + ret = EOF; + } else if(file->_flag & _IORW) { + file->_flag &= ~_IOWRT; + } + } + + file->_ptr=file->_base; + file->_cnt=0; + return ret; +} + +/********************************************************************* + * _isatty (MSVCRT.@) + */ +int CDECL _isatty(int fd) +{ + TRACE(":fd (%d)\n",fd); + + return get_ioinfo_nolock(fd)->wxflag & WX_TTY; +} + +/* INTERNAL: Allocate stdio file buffer */ +static BOOL msvcrt_alloc_buffer(FILE* file) +{ +#if _MSVCR_VER >= 140 + if((file->_file==STDOUT_FILENO && _isatty(file->_file)) + || file->_file == STDERR_FILENO) + return FALSE; +#else + if((file->_file==STDOUT_FILENO || file->_file==STDERR_FILENO) + && _isatty(file->_file)) + return FALSE; +#endif + + file->_base = calloc(1, MSVCRT_INTERNAL_BUFSIZ); + if(file->_base) { + file->_bufsiz = MSVCRT_INTERNAL_BUFSIZ; + file->_flag |= _IOMYBUF; + } else { + file->_base = (char*)(&file->_charbuf); + file->_bufsiz = 2; + file->_flag |= MSVCRT__NOBUF; + } + 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) + || (file->_flag & (MSVCRT__NOBUF | _IOMYBUF | MSVCRT__USERBUF)) + || !_isatty(file->_file)) + return FALSE; + + file->_ptr = file->_base = buffers[file->_file == STDOUT_FILENO ? 0 : 1]; + file->_bufsiz = file->_cnt = BUFSIZ; + file->_flag |= MSVCRT__USERBUF; + 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) +{ + msvcrt_flush_buffer(file); + file->_ptr = file->_base = NULL; + file->_bufsiz = file->_cnt = 0; + file->_flag &= ~MSVCRT__USERBUF; +} + +/* INTERNAL: Convert integer to base32 string (0-9a-v), 0 becomes "" */ +static int msvcrt_int_to_base32(int num, char *str) +{ + char *p; + int n = num; + int digits = 0; + + while (n != 0) + { + n >>= 5; + digits++; + } + p = str + digits; + *p = 0; + while (--p >= str) + { + *p = (num & 31) + '0'; + if (*p > '9') + *p += ('a' - '0' - 10); + num >>= 5; + } + + return digits; +} + +/* INTERNAL: wide character version of msvcrt_int_to_base32 */ +static int msvcrt_int_to_base32_w(int num, wchar_t *str) +{ + wchar_t *p; + int n = num; + int digits = 0; + + while (n != 0) + { + n >>= 5; + digits++; + } + p = str + digits; + *p = 0; + while (--p >= str) + { + *p = (num & 31) + '0'; + if (*p > '9') + *p += ('a' - '0' - 10); + num >>= 5; + } + + return digits; +} + +/********************************************************************* + * __iob_func (MSVCRT.@) + */ +#undef __iob_func +FILE * CDECL __iob_func(void) +{ + return iob_get_file(0); +} + +#if _MSVCR_VER >= 140 +/********************************************************************* + * __acrt_iob_func(UCRTBASE.@) + */ +FILE * CDECL __acrt_iob_func(unsigned idx) +{ + return iob_get_file(idx); +} +#endif + +/********************************************************************* + * _access (MSVCRT.@) + */ +int CDECL _access(const char *filename, int mode) +{ + wchar_t *filenameW = NULL; + int ret; + + if (filename && !(filenameW = wstrdupa_utf8(filename))) return -1; + ret = _waccess(filenameW, mode); + free(filenameW); + return ret; +} + +/********************************************************************* + * _access_s (MSVCRT.@) + */ +int CDECL _access_s(const char *filename, int mode) +{ + if (!MSVCRT_CHECK_PMT(filename != NULL)) return *_errno(); + if (!MSVCRT_CHECK_PMT((mode & ~(MSVCRT_R_OK | MSVCRT_W_OK)) == 0)) return *_errno(); + + if (_access(filename, mode) == -1) + return *_errno(); + return 0; +} + +/********************************************************************* + * _waccess (MSVCRT.@) + */ +int CDECL _waccess(const wchar_t *filename, int mode) +{ + DWORD attr = GetFileAttributesW(filename); + + TRACE("(%s,%d) %ld\n", debugstr_w(filename), mode, attr); + + if (!filename || attr == INVALID_FILE_ATTRIBUTES) + { + msvcrt_set_errno(GetLastError()); + return -1; + } + if ((attr & FILE_ATTRIBUTE_READONLY) && (mode & MSVCRT_W_OK)) + { + msvcrt_set_errno(ERROR_ACCESS_DENIED); + return -1; + } + return 0; +} + +/********************************************************************* + * _waccess_s (MSVCRT.@) + */ +int CDECL _waccess_s(const wchar_t *filename, int mode) +{ + if (!MSVCRT_CHECK_PMT(filename != NULL)) return *_errno(); + if (!MSVCRT_CHECK_PMT((mode & ~(MSVCRT_R_OK | MSVCRT_W_OK)) == 0)) return *_errno(); + + if (_waccess(filename, mode) == -1) + return *_errno(); + return 0; +} + +/********************************************************************* + * _chmod (MSVCRT.@) + */ +int CDECL _chmod(const char *path, int flags) +{ + wchar_t *pathW = NULL; + int ret; + + if (path && !(pathW = wstrdupa_utf8(path))) return -1; + ret = _wchmod(pathW, flags); + free(pathW); + return ret; +} + +/********************************************************************* + * _wchmod (MSVCRT.@) + */ +int CDECL _wchmod(const wchar_t *path, int flags) +{ + DWORD oldFlags = GetFileAttributesW(path); + + if (oldFlags != INVALID_FILE_ATTRIBUTES) + { + DWORD newFlags = (flags & _S_IWRITE)? oldFlags & ~FILE_ATTRIBUTE_READONLY: + oldFlags | FILE_ATTRIBUTE_READONLY; + + if (newFlags == oldFlags || SetFileAttributesW(path, newFlags)) + return 0; + } + msvcrt_set_errno(GetLastError()); + return -1; +} + +/********************************************************************* + * _unlink (MSVCRT.@) + */ +int CDECL _unlink(const char *path) +{ + wchar_t *pathW = NULL; + int ret; + + if (path && !(pathW = wstrdupa_utf8(path))) return -1; + ret = _wunlink(pathW); + free(pathW); + return ret; +} + +/********************************************************************* + * _wunlink (MSVCRT.@) + */ +int CDECL _wunlink(const wchar_t *path) +{ + TRACE("(%s)\n", debugstr_w(path)); + if(DeleteFileW(path)) + return 0; + TRACE("failed (%ld)\n", GetLastError()); + msvcrt_set_errno(GetLastError()); + return -1; +} + +/********************************************************************* + * _commit (MSVCRT.@) + */ +int CDECL _commit(int fd) +{ + ioinfo *info = get_ioinfo(fd); + int ret; + + TRACE(":fd (%d) handle (%p)\n", fd, info->handle); + + if (info->handle == INVALID_HANDLE_VALUE) + ret = -1; + else if (!FlushFileBuffers(info->handle)) + { + if (GetLastError() == ERROR_INVALID_HANDLE) + { + /* FlushFileBuffers fails for console handles + * so we ignore this error. + */ + ret = 0; + } + else + { + TRACE(":failed-last error (%ld)\n", GetLastError()); + msvcrt_set_errno(GetLastError()); + ret = -1; + } + } + else + { + TRACE(":ok\n"); + ret = 0; + } + + release_ioinfo(info); + return ret; +} + +/* INTERNAL: Flush all stream buffer */ +static int msvcrt_flush_all_buffers(int mask) +{ + int i, num_flushed = 0; + FILE *file; + + LOCK_FILES(); + for (i = 0; i < MSVCRT_stream_idx; i++) { + file = msvcrt_get_file(i); + + if (file->_flag) + { + if(file->_flag & mask) { + fflush(file); + num_flushed++; + } + } + } + UNLOCK_FILES(); + + TRACE(":flushed (%d) handles\n",num_flushed); + return num_flushed; +} + +/********************************************************************* + * _flushall (MSVCRT.@) + */ +int CDECL _flushall(void) +{ + return msvcrt_flush_all_buffers(_IOWRT | _IOREAD); +} + +/********************************************************************* + * fflush (MSVCRT.@) + */ +int CDECL fflush(FILE* file) +{ + int ret; + + if(!file) { + msvcrt_flush_all_buffers(_IOWRT); + ret = 0; + } else { + _lock_file(file); + ret = _fflush_nolock(file); + _unlock_file(file); + } + + return ret; +} + +/********************************************************************* + * _fflush_nolock (MSVCRT.@) + */ +int CDECL _fflush_nolock(FILE* file) +{ + int res; + + if(!file) { + msvcrt_flush_all_buffers(_IOWRT); + return 0; + } + + res = msvcrt_flush_buffer(file); + if(!res && (file->_flag & MSVCRT__IOCOMMIT)) + res = _commit(file->_file) ? EOF : 0; + return res; +} + +/********************************************************************* + * _close (MSVCRT.@) + */ +int CDECL _close(int fd) +{ + ioinfo *info = get_ioinfo(fd); + int ret; + + TRACE(":fd (%d) handle (%p)\n", fd, info->handle); + + if (fd == MSVCRT_NO_CONSOLE_FD) { + *_errno() = EBADF; + ret = -1; + } else if (!MSVCRT_CHECK_PMT_ERR(info->wxflag & WX_OPEN, EBADF)) { + ret = -1; + } else if (fd == STDOUT_FILENO && + info->handle == get_ioinfo_nolock(STDERR_FILENO)->handle) { + msvcrt_free_fd(fd); + ret = 0; + } else if (fd == STDERR_FILENO && + info->handle == get_ioinfo_nolock(STDOUT_FILENO)->handle) { + msvcrt_free_fd(fd); + ret = 0; + } else { + ret = CloseHandle(info->handle) ? 0 : -1; + msvcrt_free_fd(fd); + if (ret) { + WARN(":failed-last error (%ld)\n", GetLastError()); + msvcrt_set_errno(GetLastError()); + } + } + release_ioinfo(info); + return ret; +} + +/********************************************************************* + * _dup2 (MSVCRT.@) + * NOTES + * MSDN isn't clear on this point, but the remarks for _pipe + * indicate file descriptors duplicated with _dup and _dup2 are always + * inheritable. + */ +int CDECL _dup2(int od, int nd) +{ + ioinfo *info_od, *info_nd; + int ret; + + TRACE("(od=%d, nd=%d)\n", od, nd); + + if (od < nd) + { + info_od = get_ioinfo(od); + info_nd = get_ioinfo_alloc_fd(nd); + } + else + { + info_nd = get_ioinfo_alloc_fd(nd); + info_od = get_ioinfo(od); + } + + if (info_nd == &MSVCRT___badioinfo) + { + *_errno() = EBADF; + ret = -1; + } + else if (info_od->wxflag & WX_OPEN) + { + HANDLE handle; + + if (DuplicateHandle(GetCurrentProcess(), info_od->handle, + GetCurrentProcess(), &handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) + { + int wxflag = info_od->wxflag & ~WX_DONTINHERIT; + + if (info_nd->wxflag & WX_OPEN) + _close(nd); + + msvcrt_set_fd(info_nd, handle, wxflag); + /* _dup2 returns 0, not nd, on success */ + ret = 0; + } + else + { + ret = -1; + msvcrt_set_errno(GetLastError()); + } + } + else + { + *_errno() = EBADF; + ret = -1; + } + + release_ioinfo(info_od); + release_ioinfo(info_nd); + return ret; +} + +/********************************************************************* + * _dup (MSVCRT.@) + */ +int CDECL _dup(int od) +{ + int fd, ret; + ioinfo *info = get_ioinfo_alloc(&fd); + + if (_dup2(od, fd) == 0) + ret = fd; + else + ret = -1; + release_ioinfo(info); + return ret; +} + +/********************************************************************* + * _eof (MSVCRT.@) + */ +int CDECL _eof(int fd) +{ + ioinfo *info = get_ioinfo(fd); + DWORD curpos,endpos; + LONG hcurpos,hendpos; + + TRACE(":fd (%d) handle (%p)\n", fd, info->handle); + + if (info->handle == INVALID_HANDLE_VALUE) + { + release_ioinfo(info); + return -1; + } + + if (info->wxflag & WX_ATEOF) + { + release_ioinfo(info); + return TRUE; + } + + /* Otherwise we do it the hard way */ + hcurpos = hendpos = 0; + curpos = SetFilePointer(info->handle, 0, &hcurpos, FILE_CURRENT); + endpos = SetFilePointer(info->handle, 0, &hendpos, FILE_END); + + if (curpos == endpos && hcurpos == hendpos) + { + /* FIXME: shouldn't WX_ATEOF be set here? */ + release_ioinfo(info); + return TRUE; + } + + SetFilePointer(info->handle, curpos, &hcurpos, FILE_BEGIN); + release_ioinfo(info); + return FALSE; +} + +/********************************************************************* + * _fcloseall (MSVCRT.@) + */ +int CDECL _fcloseall(void) +{ + int num_closed = 0, i; + FILE *file; + + LOCK_FILES(); + for (i = 3; i < MSVCRT_stream_idx; i++) { + file = msvcrt_get_file(i); + + if (file->_flag && !fclose(file)) + num_closed++; + } + UNLOCK_FILES(); + + TRACE(":closed (%d) handles\n",num_closed); + return num_closed; +} + +/* free everything on process exit */ +void msvcrt_free_io(void) +{ + unsigned int i; + int j; + + _flushall(); + _fcloseall(); + + for(i=0; iDebugInfo->Spare[0] = 0; + DeleteCriticalSection(cs); + } + } + + for(i=0; ihandle); + + if (info->handle == INVALID_HANDLE_VALUE) + { + *_errno() = EBADF; + release_ioinfo(info); + return -1; + } + + if (whence < 0 || whence > 2) + { + release_ioinfo(info); + *_errno() = EINVAL; + return -1; + } + + TRACE(":fd (%d) to %#I64x pos %s\n", + fd, offset, (whence == SEEK_SET) ? "SEEK_SET" : + (whence == SEEK_CUR) ? "SEEK_CUR" : + (whence == SEEK_END) ? "SEEK_END" : "UNKNOWN"); + + /* The MoleBox protection scheme expects msvcrt to use SetFilePointer only, + * so a LARGE_INTEGER offset cannot be passed directly via SetFilePointerEx. */ + ofs.QuadPart = offset; + if ((ofs.u.LowPart = SetFilePointer(info->handle, ofs.u.LowPart, &ofs.u.HighPart, whence)) != INVALID_SET_FILE_POINTER || + GetLastError() == ERROR_SUCCESS) + { + info->wxflag &= ~WX_ATEOF; + /* FIXME: What if we seek _to_ EOF - is EOF set? */ + + release_ioinfo(info); + return ofs.QuadPart; + } + release_ioinfo(info); + TRACE(":error-last error (%ld)\n", GetLastError()); + msvcrt_set_errno(GetLastError()); + return -1; +} + +/********************************************************************* + * _lseek (MSVCRT.@) + */ +__msvcrt_long CDECL _lseek(int fd, __msvcrt_long offset, int whence) +{ + return _lseeki64(fd, offset, whence); +} + +/********************************************************************* + * _lock_file (MSVCRT.@) + */ +void CDECL _lock_file(FILE *file) +{ + CRITICAL_SECTION *cs = file_get_cs(file); + if (!cs) + _lock(_STREAM_LOCKS + (file - iob_get_file(0))); + else + EnterCriticalSection(cs); +} + +/********************************************************************* + * _unlock_file (MSVCRT.@) + */ +void CDECL _unlock_file(FILE *file) +{ + CRITICAL_SECTION *cs = file_get_cs(file); + if (!cs) + _unlock(_STREAM_LOCKS + (file - iob_get_file(0))); + else + LeaveCriticalSection(cs); +} + +/********************************************************************* + * _locking (MSVCRT.@) + * + * This is untested; the underlying LockFile doesn't work yet. + */ +int CDECL _locking(int fd, int mode, __msvcrt_long nbytes) +{ + ioinfo *info = get_ioinfo(fd); + BOOL ret; + DWORD cur_locn; + + TRACE(":fd (%d) handle (%p)\n", fd, info->handle); + if (info->handle == INVALID_HANDLE_VALUE) + { + release_ioinfo(info); + return -1; + } + + if (mode < 0 || mode > 4) + { + release_ioinfo(info); + *_errno() = EINVAL; + return -1; + } + + TRACE(":fd (%d) by %#lx mode %s\n", + fd, nbytes, (mode == _LK_UNLCK) ? "_LK_UNLCK" : + (mode == _LK_LOCK) ? "_LK_LOCK" : + (mode == _LK_NBLCK) ? "_LK_NBLCK" : + (mode == _LK_RLCK) ? "_LK_RLCK" : + (mode == _LK_NBRLCK) ? "_LK_NBRLCK" : + "UNKNOWN"); + + if ((cur_locn = SetFilePointer(info->handle, 0L, NULL, FILE_CURRENT)) == INVALID_SET_FILE_POINTER) + { + release_ioinfo(info); + FIXME("Seek failed\n"); + *_errno() = EINVAL; /* FIXME */ + return -1; + } + if (mode == _LK_LOCK || mode == _LK_RLCK) + { + int nretry = 10; + ret = 1; /* just to satisfy gcc */ + while (nretry--) + { + ret = LockFile(info->handle, cur_locn, 0L, nbytes, 0L); + if (ret) break; + Sleep(1); + } + } + else if (mode == _LK_UNLCK) + ret = UnlockFile(info->handle, cur_locn, 0L, nbytes, 0L); + else + ret = LockFile(info->handle, cur_locn, 0L, nbytes, 0L); + /* FIXME - what about error settings? */ + release_ioinfo(info); + return ret ? 0 : -1; +} + +/********************************************************************* + * _fseeki64 (MSVCRT.@) + */ +int CDECL _fseeki64(FILE* file, __int64 offset, int whence) +{ + int ret; + + _lock_file(file); + ret = _fseeki64_nolock(file, offset, whence); + _unlock_file(file); + + return ret; +} + +/********************************************************************* + * _fseeki64_nolock (MSVCRT.@) + */ +int CDECL _fseeki64_nolock(FILE* file, __int64 offset, int whence) +{ + int ret; + + if(whence == SEEK_CUR && file->_flag & _IOREAD ) { + whence = SEEK_SET; + offset += _ftelli64_nolock(file); + } + + /* Flush output if needed */ + msvcrt_flush_buffer(file); + /* Reset direction of i/o */ + if(file->_flag & _IORW) { + file->_flag &= ~(_IOREAD|_IOWRT); + } + /* Clear end of file flag */ + file->_flag &= ~_IOEOF; + ret = (_lseeki64(file->_file,offset,whence) == -1)?-1:0; + + return ret; +} + +/********************************************************************* + * fseek (MSVCRT.@) + */ +int CDECL fseek(FILE* file, __msvcrt_long offset, int whence) +{ + return _fseeki64( file, offset, whence ); +} + +/********************************************************************* + * _fseek_nolock (MSVCRT.@) + */ +int CDECL _fseek_nolock(FILE* file, __msvcrt_long offset, int whence) +{ + return _fseeki64_nolock( file, offset, whence ); +} + +/********************************************************************* + * _chsize_s (MSVCRT.@) + */ +int CDECL _chsize_s(int fd, __int64 size) +{ + ioinfo *info; + __int64 cur, pos; + BOOL ret = FALSE; + + TRACE("(fd=%d, size=%#I64x)\n", fd, size); + + if (!MSVCRT_CHECK_PMT(size >= 0)) return EINVAL; + + + info = get_ioinfo(fd); + if (info->handle != INVALID_HANDLE_VALUE) + { + /* save the current file pointer */ + cur = _lseeki64(fd, 0, SEEK_CUR); + if (cur >= 0) + { + pos = _lseeki64(fd, size, SEEK_SET); + if (pos >= 0) + { + ret = SetEndOfFile(info->handle); + if (!ret) msvcrt_set_errno(GetLastError()); + } + + /* restore the file pointer */ + _lseeki64(fd, cur, SEEK_SET); + } + } + + release_ioinfo(info); + return ret ? 0 : *_errno(); +} + +/********************************************************************* + * _chsize (MSVCRT.@) + */ +int CDECL _chsize(int fd, __msvcrt_long size) +{ + /* _chsize_s returns errno on failure but _chsize should return -1 */ + return _chsize_s( fd, size ) == 0 ? 0 : -1; +} + +/********************************************************************* + * clearerr (MSVCRT.@) + */ +void CDECL clearerr(FILE* file) +{ + TRACE(":file (%p) fd (%d)\n",file,file->_file); + + _lock_file(file); + file->_flag &= ~(_IOERR | _IOEOF); + _unlock_file(file); +} + +/********************************************************************* + * clearerr_s (MSVCRT.@) + */ +int CDECL clearerr_s(FILE* file) +{ + TRACE(":file (%p)\n",file); + + if (!MSVCRT_CHECK_PMT(file != NULL)) return EINVAL; + + _lock_file(file); + file->_flag &= ~(_IOERR | _IOEOF); + _unlock_file(file); + return 0; +} + +#if defined(__i386__) +/* Stack preserving thunk for rewind + * needed for the UIO mod for Fallout: New Vegas + */ +__ASM_GLOBAL_FUNC(rewind_preserve_stack, + "pushl 4(%esp)\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + "call "__ASM_NAME("rewind") "\n\t" + "addl $4,%esp\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t") + "ret") +#endif + +/********************************************************************* + * rewind (MSVCRT.@) + */ +void CDECL rewind(FILE* file) +{ + TRACE(":file (%p) fd (%d)\n",file,file->_file); + + _lock_file(file); + _fseek_nolock(file, 0L, SEEK_SET); + clearerr(file); + _unlock_file(file); +} + +static int msvcrt_get_flags(const wchar_t* mode, int *open_flags, int* stream_flags) +{ + int plus = wcschr(mode, '+') != NULL; + + TRACE("%s\n", debugstr_w(mode)); + + while(*mode == ' ') mode++; + + switch(*mode++) + { + case 'R': case 'r': + *open_flags = plus ? _O_RDWR : _O_RDONLY; + *stream_flags = plus ? _IORW : _IOREAD; + break; + case 'W': case 'w': + *open_flags = _O_CREAT | _O_TRUNC | (plus ? _O_RDWR : _O_WRONLY); + *stream_flags = plus ? _IORW : _IOWRT; + break; + case 'A': case 'a': + *open_flags = _O_CREAT | _O_APPEND | (plus ? _O_RDWR : _O_WRONLY); + *stream_flags = plus ? _IORW : _IOWRT; + break; + default: + MSVCRT_INVALID_PMT(0, EINVAL); + return -1; + } + + *stream_flags |= MSVCRT__commode; + + while (*mode && *mode!=',') + switch (*mode++) + { + case 'B': case 'b': + *open_flags |= _O_BINARY; + *open_flags &= ~_O_TEXT; + break; + case 't': + *open_flags |= _O_TEXT; + *open_flags &= ~_O_BINARY; + break; +#if _MSVCR_VER>=140 + case 'x': + if(!MSVCRT_CHECK_PMT((*open_flags & (_O_CREAT | _O_APPEND)) == _O_CREAT)) + return -1; + *open_flags |= _O_EXCL; + break; +#endif + case 'D': + *open_flags |= _O_TEMPORARY; + break; + case 'T': + *open_flags |= _O_SHORT_LIVED; + break; + case 'c': + *stream_flags |= MSVCRT__IOCOMMIT; + break; + case 'n': + *stream_flags &= ~MSVCRT__IOCOMMIT; + break; + case 'N': + *open_flags |= _O_NOINHERIT; + break; + case '+': + case ' ': + case 'a': + case 'w': + break; + case 'S': + if (!(*open_flags & _O_RANDOM)) + *open_flags |= _O_SEQUENTIAL; + break; + case 'R': + if (!(*open_flags & _O_SEQUENTIAL)) + *open_flags |= _O_RANDOM; + break; + default: + ERR("incorrect mode flag: %c\n", mode[-1]); + break; + } + + if(*mode == ',') + { + mode++; + while(*mode == ' ') mode++; + if(!MSVCRT_CHECK_PMT(!wcsncmp(L"ccs", mode, 3))) + return -1; + mode += 3; + while(*mode == ' ') mode++; + if(!MSVCRT_CHECK_PMT(*mode == '=')) + return -1; + mode++; + while(*mode == ' ') mode++; + + if(!_wcsnicmp(L"utf-8", mode, 5)) + { + *open_flags |= _O_U8TEXT; + mode += 5; + } + else if(!_wcsnicmp(L"utf-16le", mode, 8)) + { + *open_flags |= _O_U16TEXT; + mode += 8; + } + else if(!_wcsnicmp(L"unicode", mode, 7)) + { + *open_flags |= _O_WTEXT; + mode += 7; + } + else + { + MSVCRT_INVALID_PMT(0, EINVAL); + return -1; + } + + while(*mode == ' ') mode++; + } + + if(!MSVCRT_CHECK_PMT(*mode == 0)) + return -1; + return 0; +} + +/********************************************************************* + * _fdopen (MSVCRT.@) + */ +FILE* CDECL _fdopen(int fd, const char *mode) +{ + FILE *ret; + wchar_t *modeW = NULL; + + if (mode && !(modeW = msvcrt_wstrdupa(mode))) return NULL; + + ret = _wfdopen(fd, modeW); + + free(modeW); + return ret; +} + +/********************************************************************* + * _wfdopen (MSVCRT.@) + */ +FILE* CDECL _wfdopen(int fd, const wchar_t *mode) +{ + int open_flags, stream_flags; + FILE* file; + + if (msvcrt_get_flags(mode, &open_flags, &stream_flags) == -1) return NULL; + + LOCK_FILES(); + if (!(file = msvcrt_alloc_fp())) + file = NULL; + else if (msvcrt_init_fp(file, fd, stream_flags) == -1) + { + file->_flag = 0; + file = NULL; + } + else TRACE(":fd (%d) mode (%s) FILE* (%p)\n", fd, debugstr_w(mode), file); + UNLOCK_FILES(); + + return file; +} + +/********************************************************************* + * _filelength (MSVCRT.@) + */ +__msvcrt_long CDECL _filelength(int fd) +{ + LONG curPos = _lseek(fd, 0, SEEK_CUR); + if (curPos != -1) + { + LONG endPos = _lseek(fd, 0, SEEK_END); + if (endPos != -1) + { + if (endPos != curPos) + _lseek(fd, curPos, SEEK_SET); + return endPos; + } + } + return -1; +} + +/********************************************************************* + * _filelengthi64 (MSVCRT.@) + */ +__int64 CDECL _filelengthi64(int fd) +{ + __int64 curPos = _lseeki64(fd, 0, SEEK_CUR); + if (curPos != -1) + { + __int64 endPos = _lseeki64(fd, 0, SEEK_END); + if (endPos != -1) + { + if (endPos != curPos) + _lseeki64(fd, curPos, SEEK_SET); + return endPos; + } + } + return -1; +} + +/********************************************************************* + * _fileno (MSVCRT.@) + */ +int CDECL _fileno(FILE* file) +{ + TRACE(":FILE* (%p) fd (%d)\n",file,file->_file); + return file->_file; +} + +/********************************************************************* + * _fstat64 (MSVCRT.@) + */ +int CDECL _fstat64(int fd, struct _stat64* buf) +{ + ioinfo *info = get_ioinfo(fd); + DWORD dw; + DWORD type; + + TRACE(":fd (%d) stat (%p)\n", fd, buf); + if (info->handle == INVALID_HANDLE_VALUE) + { + release_ioinfo(info); + return -1; + } + + if (!buf) + { + WARN(":failed-NULL buf\n"); + msvcrt_set_errno(ERROR_INVALID_PARAMETER); + release_ioinfo(info); + return -1; + } + + memset(buf, 0, sizeof(struct _stat64)); + type = GetFileType(info->handle); + if (type == FILE_TYPE_PIPE) + { + buf->st_dev = buf->st_rdev = fd; + buf->st_mode = _S_IFIFO; + buf->st_nlink = 1; + } + else if (type == FILE_TYPE_CHAR) + { + buf->st_dev = buf->st_rdev = fd; + buf->st_mode = _S_IFCHR; + buf->st_nlink = 1; + } + else /* FILE_TYPE_DISK etc. */ + { + FILE_BASIC_INFORMATION basic_info; + FILE_STANDARD_INFORMATION std_info; + IO_STATUS_BLOCK io; + NTSTATUS status; + + if ((status = NtQueryInformationFile( info->handle, &io, &basic_info, sizeof(basic_info), FileBasicInformation )) || + (status = NtQueryInformationFile( info->handle, &io, &std_info, sizeof(std_info), FileStandardInformation ))) + { + WARN(":failed-error %lx\n", status); + msvcrt_set_errno(ERROR_INVALID_PARAMETER); + release_ioinfo(info); + return -1; + } + buf->st_mode = _S_IFREG | 0444; + if (!(basic_info.FileAttributes & FILE_ATTRIBUTE_READONLY)) + buf->st_mode |= 0222; + buf->st_size = std_info.EndOfFile.QuadPart; + RtlTimeToSecondsSince1970((LARGE_INTEGER *)&basic_info.LastAccessTime, &dw); + buf->st_atime = dw; + RtlTimeToSecondsSince1970((LARGE_INTEGER *)&basic_info.LastWriteTime, &dw); + buf->st_mtime = buf->st_ctime = dw; + buf->st_nlink = std_info.NumberOfLinks; + TRACE(":dwFileAttributes = %#lx, mode set to %#x\n", + basic_info.FileAttributes, buf->st_mode); + } + release_ioinfo(info); + return 0; +} + +/********************************************************************* + * _fstati64 (MSVCRT.@) + */ +int CDECL _fstati64(int fd, struct _stati64* buf) +{ + int ret; + struct _stat64 buf64; + + ret = _fstat64(fd, &buf64); + if (!ret) + msvcrt_stat64_to_stati64(&buf64, buf); + return ret; +} + +/********************************************************************* + * _fstat (MSVCRT.@) + */ +int CDECL _fstat(int fd, struct _stat* buf) +{ int ret; + struct _stat64 buf64; + + ret = _fstat64(fd, &buf64); + if (!ret) + msvcrt_stat64_to_stat(&buf64, buf); + return ret; +} + +/********************************************************************* + * _fstat32 (MSVCR80.@) + */ +int CDECL _fstat32(int fd, struct _stat32* buf) +{ + int ret; + struct _stat64 buf64; + + ret = _fstat64(fd, &buf64); + if (!ret) + msvcrt_stat64_to_stat32(&buf64, buf); + return ret; +} + +/********************************************************************* + * _fstat32i64 (MSVCR80.@) + */ +int CDECL _fstat32i64(int fd, struct _stat32i64* buf) +{ + int ret; + struct _stat64 buf64; + + ret = _fstat64(fd, &buf64); + if (!ret) + msvcrt_stat64_to_stat32i64(&buf64, buf); + return ret; +} + +/********************************************************************* + * _fstat64i32 (MSVCR80.@) + */ +int CDECL _fstat64i32(int fd, struct _stat64i32* buf) +{ + int ret; + struct _stat64 buf64; + + ret = _fstat64(fd, &buf64); + if (!ret) + msvcrt_stat64_to_stat64i32(&buf64, buf); + return ret; +} + +/********************************************************************* + * _futime64 (MSVCRT.@) + */ +int CDECL _futime64(int fd, struct __utimbuf64 *t) +{ + ioinfo *info = get_ioinfo(fd); + FILETIME at, wt; + + if (!t) + { + time_to_filetime( _time64(NULL), &at ); + wt = at; + } + else + { + time_to_filetime( t->actime, &at ); + time_to_filetime( t->modtime, &wt ); + } + + if (!SetFileTime(info->handle, NULL, &at, &wt)) + { + release_ioinfo(info); + msvcrt_set_errno(GetLastError()); + return -1 ; + } + release_ioinfo(info); + return 0; +} + +/********************************************************************* + * _futime32 (MSVCRT.@) + */ +int CDECL _futime32(int fd, struct __utimbuf32 *t) +{ + if (t) + { + struct __utimbuf64 t64; + t64.actime = t->actime; + t64.modtime = t->modtime; + return _futime64( fd, &t64 ); + } + else + return _futime64( fd, NULL ); +} + +/********************************************************************* + * _get_osfhandle (MSVCRT.@) + */ +intptr_t CDECL _get_osfhandle(int fd) +{ + HANDLE hand = get_ioinfo_nolock(fd)->handle; + TRACE(":fd (%d) handle (%p)\n",fd,hand); + + if(hand == INVALID_HANDLE_VALUE) + *_errno() = EBADF; + return (intptr_t)hand; +} + +/********************************************************************* + * _mktemp_s (MSVCRT.@) + */ +int CDECL _mktemp_s(char *pattern, size_t size) +{ + DWORD len, wlen, xno, id; + wchar_t *pathW; + + if(!MSVCRT_CHECK_PMT(pattern!=NULL)) + return EINVAL; + + for(len=0; len=6)) { + if(size) + pattern[0] = 0; + return EINVAL; + } + + for(xno=1; xno<=6; xno++) + if(!MSVCRT_CHECK_PMT(pattern[len-xno] == 'X')) + return EINVAL; + + id = GetCurrentProcessId(); + for(xno=1; xno<6; xno++) { + pattern[len-xno] = id%10 + '0'; + id /= 10; + } + + if(!(pathW = wstrdupa_utf8(pattern))) return *_errno(); + wlen = wcslen(pathW); + for(pathW[wlen-6]='a'; pathW[wlen-6]<='z'; pathW[wlen-6]++) { + if(GetFileAttributesW(pathW) == INVALID_FILE_ATTRIBUTES) { + pattern[len-6] = pathW[wlen-6]; + free(pathW); + return 0; + } + } + free(pathW); + + pattern[0] = 0; + *_errno() = EEXIST; + return EEXIST; +} + +/********************************************************************* + * _mktemp (MSVCRT.@) + */ +char * CDECL _mktemp(char *pattern) +{ + wchar_t *pathW, *p; + int numX = 0; + char *retVal = pattern; + int id; + char letter = 'a'; + + if(!pattern) + return NULL; + + while(*pattern) + numX = (*pattern++ == 'X')? numX + 1 : 0; + if (numX < 6) + return NULL; + pattern--; + id = GetCurrentProcessId(); + numX = 6; + while(numX--) + { + int tempNum = id / 10; + *pattern-- = id - (tempNum * 10) + '0'; + id = tempNum; + } + pattern++; + if (!(pathW = wstrdupa_utf8(retVal))) + return NULL; + p = pathW + wcslen(pathW) - 6; + do + { + *p = letter++; + if (GetFileAttributesW(pathW) == INVALID_FILE_ATTRIBUTES) + { + *pattern = *p; + free(pathW); + return retVal; + } + } while(letter <= 'z'); + free(pathW); + return NULL; +} + +/********************************************************************* + * _wmktemp_s (MSVCRT.@) + */ +int CDECL _wmktemp_s(wchar_t *pattern, size_t size) +{ + DWORD len, xno, id; + + if(!MSVCRT_CHECK_PMT(pattern!=NULL)) + return EINVAL; + + for(len=0; len=6)) { + if(size) + pattern[0] = 0; + return EINVAL; + } + + for(xno=1; xno<=6; xno++) + if(!MSVCRT_CHECK_PMT(pattern[len-xno] == 'X')) + return EINVAL; + + id = GetCurrentProcessId(); + for(xno=1; xno<6; xno++) { + pattern[len-xno] = id%10 + '0'; + id /= 10; + } + + for(pattern[len-6]='a'; pattern[len-6]<='z'; pattern[len-6]++) { + if(GetFileAttributesW(pattern) == INVALID_FILE_ATTRIBUTES) + return 0; + } + + pattern[0] = 0; + *_errno() = EEXIST; + return EEXIST; +} + +/********************************************************************* + * _wmktemp (MSVCRT.@) + */ +wchar_t * CDECL _wmktemp(wchar_t *pattern) +{ + int numX = 0; + wchar_t *retVal = pattern; + int id; + wchar_t letter = 'a'; + + if(!pattern) + return NULL; + + while(*pattern) + numX = (*pattern++ == 'X')? numX + 1 : 0; + if (numX < 6) + return NULL; + pattern--; + id = GetCurrentProcessId(); + numX = 6; + while(numX--) + { + int tempNum = id / 10; + *pattern-- = id - (tempNum * 10) + '0'; + id = tempNum; + } + pattern++; + do + { + if (GetFileAttributesW(retVal) == INVALID_FILE_ATTRIBUTES) + return retVal; + *pattern = letter++; + } while(letter != '|'); + return NULL; +} + +static unsigned split_oflags(unsigned oflags) +{ + 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 (oflags & _O_WTEXT) wxflags |= WX_TEXT; + else if (oflags & _O_U16TEXT) wxflags |= WX_TEXT; + else if (oflags & _O_U8TEXT) wxflags |= WX_TEXT; + else + { + int fmode; + _get_fmode(&fmode); + if (!(fmode & _O_BINARY)) wxflags |= WX_TEXT; /* default to TEXT*/ + } + 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_WTEXT | _O_U16TEXT | _O_U8TEXT))) + ERR(":unsupported oflags %#x\n",unsupp); + + return wxflags; +} + +/********************************************************************* + * _pipe (MSVCRT.@) + */ +int CDECL _pipe(int *pfds, unsigned int psize, int textmode) +{ + int ret = -1; + SECURITY_ATTRIBUTES sa; + HANDLE readHandle, writeHandle; + + if (!pfds) + { + *_errno() = EINVAL; + return -1; + } + + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = !(textmode & _O_NOINHERIT); + sa.lpSecurityDescriptor = NULL; + if (CreatePipe(&readHandle, &writeHandle, &sa, psize)) + { + unsigned int wxflags = split_oflags(textmode); + int fd; + + fd = msvcrt_alloc_fd(readHandle, wxflags|WX_PIPE); + if (fd != -1) + { + pfds[0] = fd; + fd = msvcrt_alloc_fd(writeHandle, wxflags|WX_PIPE); + if (fd != -1) + { + pfds[1] = fd; + ret = 0; + } + else + { + _close(pfds[0]); + CloseHandle(writeHandle); + *_errno() = EMFILE; + } + } + else + { + CloseHandle(readHandle); + CloseHandle(writeHandle); + *_errno() = EMFILE; + } + } + else + msvcrt_set_errno(GetLastError()); + + return ret; +} + +static int check_bom(HANDLE h, int oflags, BOOL seek) +{ + char bom[sizeof(utf8_bom)]; + DWORD r; + + if (!ReadFile(h, bom, sizeof(utf8_bom), &r, NULL)) + return oflags; + + if (r==sizeof(utf8_bom) && !memcmp(bom, utf8_bom, sizeof(utf8_bom))) { + oflags = (oflags & ~(_O_WTEXT | _O_U16TEXT)) | _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 = (oflags & ~(_O_WTEXT | _O_U8TEXT)) | _O_U16TEXT; + }else if (seek) { + SetFilePointer(h, 0, NULL, FILE_BEGIN); + } + + return oflags; +} + +/********************************************************************* + * _wsopen_dispatch (UCRTBASE.@) + */ +int CDECL _wsopen_dispatch( const wchar_t* path, int oflags, int shflags, int pmode, + int *fd, int secure ) +{ + DWORD access = 0, creation = 0, attrib; + SECURITY_ATTRIBUTES sa; + DWORD sharing, type; + int wxflag; + HANDLE hand; + + TRACE("path: (%s) oflags: %#x shflags: %#x pmode: %#x fd*: %p secure: %d\n", + debugstr_w(path), oflags, shflags, pmode, fd, secure); + + if (!MSVCRT_CHECK_PMT( fd != NULL )) return EINVAL; + *fd = -1; + if (!MSVCRT_CHECK_PMT(path != NULL)) return EINVAL; + + 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 (secure && !MSVCRT_CHECK_PMT(!(pmode & ~(_S_IREAD | _S_IWRITE)))) + return EINVAL; + + 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 %#x\n", shflags ); + return EINVAL; + } + + if (!(pmode & ~MSVCRT_umask & _S_IWRITE)) + attrib = FILE_ATTRIBUTE_READONLY; + else + attrib = FILE_ATTRIBUTE_NORMAL; + + if (oflags & _O_TEMPORARY) + { + attrib |= FILE_FLAG_DELETE_ON_CLOSE; + access |= DELETE; + sharing |= FILE_SHARE_DELETE; + } + + if (oflags & _O_RANDOM) + attrib |= FILE_FLAG_RANDOM_ACCESS; + if (oflags & _O_SEQUENTIAL) + attrib |= FILE_FLAG_SEQUENTIAL_SCAN; + if (oflags & _O_SHORT_LIVED) + attrib |= FILE_ATTRIBUTE_TEMPORARY; + + sa.nLength = sizeof( SECURITY_ATTRIBUTES ); + sa.lpSecurityDescriptor = NULL; + 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); + } + } + + hand = CreateFileW(path, access, sharing, &sa, creation, attrib, 0); + if (hand == INVALID_HANDLE_VALUE) { + WARN(":failed-last error (%ld)\n", GetLastError()); + msvcrt_set_errno(GetLastError()); + return *_errno(); + } + + if (oflags & (_O_WTEXT | _O_U16TEXT | _O_U8TEXT)) + { + LARGE_INTEGER size = {{0}}; + + if ((access & GENERIC_WRITE) && (creation==OPEN_EXISTING || creation==OPEN_ALWAYS)) + GetFileSizeEx(hand, &size); + + if ((access & GENERIC_WRITE) && (creation==CREATE_NEW + || creation==CREATE_ALWAYS || creation==TRUNCATE_EXISTING + || ((creation==OPEN_EXISTING || creation==OPEN_ALWAYS) && !size.QuadPart))) + { + 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); + msvcrt_set_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); + msvcrt_set_errno(GetLastError()); + return *_errno(); + } + oflags |= _O_U16TEXT; + } + } + else if (access & GENERIC_READ) + oflags = check_bom(hand, oflags, TRUE); + } + + type = GetFileType(hand); + if (type == FILE_TYPE_CHAR) + wxflag |= WX_TTY; + else if (type == FILE_TYPE_PIPE) + wxflag |= WX_PIPE; + + *fd = msvcrt_alloc_fd(hand, wxflag); + if (*fd == -1) + return *_errno(); + + if (oflags & _O_WTEXT) + ioinfo_set_unicode(get_ioinfo_nolock(*fd), TRUE); + + if (oflags & _O_U16TEXT) + ioinfo_set_textmode(get_ioinfo_nolock(*fd), TEXTMODE_UTF16LE); + else if (oflags & _O_U8TEXT) + ioinfo_set_textmode(get_ioinfo_nolock(*fd), TEXTMODE_UTF8); + + TRACE(":fd (%d) handle (%p)\n", *fd, hand); + return 0; +} + + +/********************************************************************* + * _wsopen_s (MSVCRT.@) + */ +int CDECL _wsopen_s( int *fd, const wchar_t* path, int oflags, int shflags, int pmode ) +{ + return _wsopen_dispatch( path, oflags, shflags, pmode, fd, 1 ); +} + +/********************************************************************* + * _wsopen (MSVCRT.@) + */ +int WINAPIV _wsopen( const wchar_t *path, int oflags, int shflags, ... ) +{ + int pmode; + int fd; + + if (oflags & _O_CREAT) + { + va_list ap; + + va_start(ap, shflags); + pmode = va_arg(ap, int); + va_end(ap); + } + else + pmode = 0; + + return _wsopen_dispatch(path, oflags, shflags, pmode, &fd, 0) ? -1 : fd; +} + + +/********************************************************************* + * _sopen_dispatch (UCRTBASE.@) + */ +int CDECL _sopen_dispatch( const char *path, int oflags, int shflags, + int pmode, int *fd, int secure) +{ + wchar_t *pathW = NULL; + int ret; + + if (!MSVCRT_CHECK_PMT(fd != NULL)) + return EINVAL; + *fd = -1; + if (path && !(pathW = wstrdupa_utf8(path))) return *_errno(); + + ret = _wsopen_dispatch(pathW, oflags, shflags, pmode, fd, secure); + free(pathW); + return ret; +} + +/********************************************************************* + * _sopen_s (MSVCRT.@) + */ +int CDECL _sopen_s( int *fd, const char *path, int oflags, int shflags, int pmode ) +{ + return _sopen_dispatch(path, oflags, shflags, pmode, fd, 1); +} + +/********************************************************************* + * _sopen (MSVCRT.@) + */ +int WINAPIV _sopen( const char *path, int oflags, int shflags, ... ) +{ + int pmode; + int fd; + + if (oflags & _O_CREAT) + { + va_list ap; + + va_start(ap, shflags); + pmode = va_arg(ap, int); + va_end(ap); + } + else + pmode = 0; + + return _sopen_dispatch(path, oflags, shflags, pmode, &fd, 0) ? -1 : fd; +} + +/********************************************************************* + * _open (MSVCRT.@) + */ +int WINAPIV _open( const char *path, int flags, ... ) +{ + va_list ap; + + if (flags & _O_CREAT) + { + int pmode; + va_start(ap, flags); + pmode = va_arg(ap, int); + va_end(ap); + return _sopen( path, flags, _SH_DENYNO, pmode ); + } + else + return _sopen( path, flags, _SH_DENYNO); +} + +/********************************************************************* + * _wopen (MSVCRT.@) + */ +int WINAPIV _wopen(const wchar_t *path,int flags,...) +{ + va_list ap; + + if (flags & _O_CREAT) + { + int pmode; + va_start(ap, flags); + pmode = va_arg(ap, int); + va_end(ap); + return _wsopen( path, flags, _SH_DENYNO, pmode ); + } + else + return _wsopen( path, flags, _SH_DENYNO); +} + +/********************************************************************* + * _creat (MSVCRT.@) + */ +int CDECL _creat(const char *path, int pmode) +{ + int flags = _O_CREAT | _O_TRUNC | _O_RDWR; + return _open(path, flags, pmode); +} + +/********************************************************************* + * _wcreat (MSVCRT.@) + */ +int CDECL _wcreat(const wchar_t *path, int pmode) +{ + int flags = _O_CREAT | _O_TRUNC | _O_RDWR; + return _wopen(path, flags, pmode); +} + +/********************************************************************* + * _open_osfhandle (MSVCRT.@) + */ +int CDECL _open_osfhandle(intptr_t handle, int oflags) +{ + DWORD flags; + int fd; + + /* _O_RDONLY (0) always matches, so set the read flag + * MFC's CStdioFile clears O_RDONLY (0)! if it wants to write to the + * file, so set the write flag. It also only sets _O_TEXT if it wants + * text - it never sets _O_BINARY. + */ + /* don't let split_oflags() decide the mode if no mode is passed */ + if (!(oflags & (_O_BINARY | _O_TEXT))) + oflags |= _O_BINARY; + + flags = GetFileType((HANDLE)handle); + if (flags==FILE_TYPE_UNKNOWN && GetLastError()!=NO_ERROR) + { + msvcrt_set_errno(GetLastError()); + return -1; + } + + if (flags == FILE_TYPE_CHAR) + flags = WX_TTY; + else if (flags == FILE_TYPE_PIPE) + flags = WX_PIPE; + else + flags = 0; + flags |= split_oflags(oflags); + + fd = msvcrt_alloc_fd((HANDLE)handle, flags); + TRACE(":handle (%Iu) fd (%d) flags %#lx\n", handle, fd, flags); + return fd; +} + +/********************************************************************* + * _rmtmp (MSVCRT.@) + */ +int CDECL _rmtmp(void) +{ + int num_removed = 0, i; + FILE *file; + + LOCK_FILES(); + for (i = 3; i < MSVCRT_stream_idx; i++) { + file = msvcrt_get_file(i); + + if (file->_tmpfname) + { + fclose(file); + num_removed++; + } + } + UNLOCK_FILES(); + + if (num_removed) + TRACE(":removed (%d) temp files\n",num_removed); + 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(ioinfo *fdinfo, wchar_t *buf, unsigned int count) +{ + 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 { + msvcrt_set_errno(GetLastError()); + if (GetLastError() == ERROR_ACCESS_DENIED) + *_errno() = EBADF; + 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_TTY)) + 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))) { + msvcrt_set_errno(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 { + msvcrt_set_errno(GetLastError()); + if (GetLastError() == ERROR_ACCESS_DENIED) + *_errno() = EBADF; + 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_TTY)) { + 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; iwxflag |= 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_TTY)) + 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))) { + msvcrt_set_errno(GetLastError()); + if (readbuf != min_buf) free(readbuf); + return -1; + } + + if (readbuf != min_buf) free(readbuf); + return num_read*2; +} + +/********************************************************************* + * (internal) read_i + * + * When reading \r as last character in text mode, read() positions + * the file pointer on the \r character while getc() goes on to + * the following \n + */ +static int read_i(int fd, ioinfo *fdinfo, void *buf, unsigned int count) +{ + DWORD num_read, utf16; + char *bufstart = buf; + + if (count == 0) + return 0; + + 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, fdinfo->handle, buf, count); + if (fdinfo->handle == INVALID_HANDLE_VALUE) + { + *_errno() = EBADF; + return -1; + } + + utf16 = ioinfo_get_textmode(fdinfo) == TEXTMODE_UTF16LE; + if (ioinfo_get_textmode(fdinfo) != TEXTMODE_ANSI && count&1) + { + *_errno() = EINVAL; + return -1; + } + + if((fdinfo->wxflag&WX_TEXT) && ioinfo_get_textmode(fdinfo) == TEXTMODE_UTF8) + return read_utf8(fdinfo, buf, count); + + if (fdinfo->lookahead[0]!='\n' || ReadFile(fdinfo->handle, 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(fdinfo->handle, 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; + TRACE(":EOF %s\n",debugstr_an(buf,num_read)); + } + else if (fdinfo->wxflag & WX_TEXT) + { + DWORD i, j; + + if (bufstart[0]=='\n' && (!utf16 || bufstart[1]==0)) + fdinfo->wxflag |= WX_READNL; + else + fdinfo->wxflag &= ~WX_READNL; + + for (i=0, j=0; iwxflag |= WX_ATEOF; + TRACE(":^Z EOF %s\n",debugstr_an(buf,num_read)); + break; + } + + /* in text mode, strip \r if followed by \n */ + if (bufstart[i]=='\r' && (!utf16 || bufstart[i+1]==0) && i+1+utf16==num_read) + { + char lookahead[2]; + DWORD len; + + lookahead[1] = '\n'; + if (ReadFile(fdinfo->handle, 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_TTY)) + { + 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 if((bufstart[i]!='\r' || (utf16 && bufstart[i+1]!=0)) + || (bufstart[i+1+utf16]!='\n' || (utf16 && bufstart[i+3]!=0))) + { + bufstart[j++] = bufstart[i]; + if(utf16) bufstart[j++] = bufstart[i+1]; + } + } + num_read = j; + } + } + else + { + if (GetLastError() == ERROR_BROKEN_PIPE) + { + TRACE(":end-of-pipe\n"); + fdinfo->wxflag |= WX_ATEOF; + return 0; + } + else + { + TRACE(":failed-last error (%ld)\n", GetLastError()); + msvcrt_set_errno(GetLastError()); + if (GetLastError() == ERROR_ACCESS_DENIED) + *_errno() = EBADF; + return -1; + } + } + + if (count > 4) + TRACE("(%lu), %s\n", num_read, debugstr_an(buf, num_read)); + return num_read; +} + +/********************************************************************* + * _read (MSVCRT.@) + */ +int CDECL _read(int fd, void *buf, unsigned int count) +{ + ioinfo *info; + int num_read; + + if(fd == MSVCRT_NO_CONSOLE_FD) { + *_errno() = EBADF; + return -1; + } + + info = get_ioinfo(fd); + num_read = read_i(fd, info, buf, count); + release_ioinfo(info); + return num_read; +} + +/********************************************************************* + * _setmode (MSVCRT.@) + */ +int CDECL _setmode(int fd,int mode) +{ + ioinfo *info = get_ioinfo(fd); + int ret = info->wxflag & WX_TEXT ? _O_TEXT : _O_BINARY; + + if(ret==_O_TEXT && ioinfo_get_textmode(info) != TEXTMODE_ANSI) + ret = _O_WTEXT; + + if(mode!=_O_TEXT && mode!=_O_BINARY && mode!=_O_WTEXT + && mode!=_O_U16TEXT && mode!=_O_U8TEXT) { + *_errno() = EINVAL; + release_ioinfo(info); + return -1; + } + + if(info == &MSVCRT___badioinfo) { + *_errno() = EBADF; + return EOF; + } + + if(mode == _O_BINARY) { + info->wxflag &= ~WX_TEXT; + ioinfo_set_textmode(info, TEXTMODE_ANSI); + release_ioinfo(info); + return ret; + } + + info->wxflag |= WX_TEXT; + if(mode == _O_TEXT) + ioinfo_set_textmode(info, TEXTMODE_ANSI); + else if(mode == _O_U8TEXT) + ioinfo_set_textmode(info, TEXTMODE_UTF8); + else + ioinfo_set_textmode(info, TEXTMODE_UTF16LE); + + release_ioinfo(info); + return ret; +} + +/********************************************************************* + * _stat64 (MSVCRT.@) + */ +int CDECL _stat64(const char* path, struct _stat64 * buf) +{ + wchar_t *pathW = NULL; + int ret; + + if (path && !(pathW = wstrdupa_utf8(path))) return -1; + ret = _wstat64(pathW, buf); + free(pathW); + return ret; +} + +/********************************************************************* + * _stati64 (MSVCRT.@) + */ +int CDECL _stati64(const char* path, struct _stati64 * buf) +{ + int ret; + struct _stat64 buf64; + + ret = _stat64(path, &buf64); + if (!ret) + msvcrt_stat64_to_stati64(&buf64, buf); + return ret; +} + +/********************************************************************* + * _stat (MSVCRT.@) + */ +int CDECL _stat(const char* path, struct _stat * buf) +{ + int ret; + struct _stat64 buf64; + + ret = _stat64( path, &buf64); + if (!ret) + msvcrt_stat64_to_stat(&buf64, buf); + return ret; +} + +#if _MSVCR_VER >= 80 + +/********************************************************************* + * _stat32 (MSVCR80.@) + */ +int CDECL _stat32(const char *path, struct _stat32 *buf) +{ + int ret; + struct _stat64 buf64; + + ret = _stat64(path, &buf64); + if (!ret) + msvcrt_stat64_to_stat32(&buf64, buf); + return ret; +} + +/********************************************************************* + * _stat32i64 (MSVCR80.@) + */ +int CDECL _stat32i64(const char *path, struct _stat32i64 *buf) +{ + int ret; + struct _stat64 buf64; + + ret = _stat64(path, &buf64); + if (!ret) + msvcrt_stat64_to_stat32i64(&buf64, buf); + return ret; +} + +/********************************************************************* + * _stat64i32 (MSVCR80.@) + */ +int CDECL _stat64i32(const char* path, struct _stat64i32 *buf) +{ + int ret; + struct _stat64 buf64; + + ret = _stat64(path, &buf64); + if (!ret) + msvcrt_stat64_to_stat64i32(&buf64, buf); + return ret; +} + +#endif /* _MSVCR_VER >= 80 */ + +/********************************************************************* + * _wstat64 (MSVCRT.@) + */ +int CDECL _wstat64(const wchar_t* path, struct _stat64 * buf) +{ + DWORD dw; + WIN32_FILE_ATTRIBUTE_DATA hfi; + unsigned short mode = ALL_S_IREAD; + int plen; + + TRACE(":file (%s) buf(%p)\n", debugstr_w(path), buf); + + plen = wcslen(path); + while (plen && path[plen-1]==' ') + plen--; + + if (plen==2 && path[1]==':') + { + *_errno() = ENOENT; + return -1; + } + +#if _MSVCR_VER<140 + if (plen>=2 && path[plen-2]!=':' && (path[plen-1]=='\\' || path[plen-1]=='/')) + { + *_errno() = ENOENT; + return -1; + } +#endif + + if (!GetFileAttributesExW(path, GetFileExInfoStandard, &hfi)) + { + TRACE("failed (%ld)\n", GetLastError()); + *_errno() = ENOENT; + return -1; + } + + memset(buf,0,sizeof(struct _stat64)); + + /* FIXME: rdev isn't drive num, despite what the docs says-what is it? */ + if (iswalpha(*path) && path[1] == ':') + buf->st_dev = buf->st_rdev = towupper(*path) - 'A'; /* drive num */ + else + buf->st_dev = buf->st_rdev = _getdrive() - 1; + + /* Dir, or regular file? */ + if (hfi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + mode |= (_S_IFDIR | ALL_S_IEXEC); + else + { + mode |= _S_IFREG; + /* executable? */ + if (plen > 6 && path[plen-4] == '.') /* shortest exe: "\x.exe" */ + { + ULONGLONG ext = towlower(path[plen-1]) | (towlower(path[plen-2]) << 16) | + ((ULONGLONG)towlower(path[plen-3]) << 32); + if (ext == WCEXE || ext == WCBAT || ext == WCCMD || ext == WCCOM) + mode |= ALL_S_IEXEC; + } + } + + if (!(hfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) + mode |= ALL_S_IWRITE; + + buf->st_mode = mode; + buf->st_nlink = 1; + buf->st_size = ((__int64)hfi.nFileSizeHigh << 32) + hfi.nFileSizeLow; + RtlTimeToSecondsSince1970((LARGE_INTEGER *)&hfi.ftLastAccessTime, &dw); + buf->st_atime = dw; + RtlTimeToSecondsSince1970((LARGE_INTEGER *)&hfi.ftLastWriteTime, &dw); + buf->st_mtime = buf->st_ctime = dw; + TRACE("%d %d %#I64x %I64d %I64d %I64d\n", buf->st_mode, buf->st_nlink, + buf->st_size, buf->st_atime, buf->st_mtime, buf->st_ctime); + return 0; +} + +/********************************************************************* + * _wstati64 (MSVCRT.@) + */ +int CDECL _wstati64(const wchar_t* path, struct _stati64 * buf) +{ + int ret; + struct _stat64 buf64; + + ret = _wstat64(path, &buf64); + if (!ret) + msvcrt_stat64_to_stati64(&buf64, buf); + return ret; +} + +/********************************************************************* + * _wstat (MSVCRT.@) + */ +int CDECL _wstat(const wchar_t* path, struct _stat * buf) +{ + int ret; + struct _stat64 buf64; + + ret = _wstat64( path, &buf64 ); + if (!ret) msvcrt_stat64_to_stat(&buf64, buf); + return ret; +} + +#if _MSVCR_VER >= 80 + +/********************************************************************* + * _wstat32 (MSVCR80.@) + */ +int CDECL _wstat32(const wchar_t *path, struct _stat32 *buf) +{ + int ret; + struct _stat64 buf64; + + ret = _wstat64(path, &buf64); + if (!ret) + msvcrt_stat64_to_stat32(&buf64, buf); + return ret; +} + +/********************************************************************* + * _wstat32i64 (MSVCR80.@) + */ +int CDECL _wstat32i64(const wchar_t *path, struct _stat32i64 *buf) +{ + int ret; + struct _stat64 buf64; + + ret = _wstat64(path, &buf64); + if (!ret) + msvcrt_stat64_to_stat32i64(&buf64, buf); + return ret; +} + +/********************************************************************* + * _wstat64i32 (MSVCR80.@) + */ +int CDECL _wstat64i32(const wchar_t *path, struct _stat64i32 *buf) +{ + int ret; + struct _stat64 buf64; + + ret = _wstat64(path, &buf64); + if (!ret) + msvcrt_stat64_to_stat64i32(&buf64, buf); + return ret; +} + +#endif /* _MSVCR_VER >= 80 */ + +/********************************************************************* + * _tell (MSVCRT.@) + */ +__msvcrt_long CDECL _tell(int fd) +{ + return _lseek(fd, 0, SEEK_CUR); +} + +/********************************************************************* + * _telli64 (MSVCRT.@) + */ +__int64 CDECL _telli64(int fd) +{ + return _lseeki64(fd, 0, SEEK_CUR); +} + +/********************************************************************* + * _tempnam (MSVCRT.@) + */ +char * CDECL _tempnam(const char *dir, const char *prefix) +{ + wchar_t *dirW = NULL, *prefixW = NULL, *retW; + char *ret; + + if (dir && !(dirW = wstrdupa_utf8(dir))) return NULL; + if (prefix && !(prefixW = wstrdupa_utf8(prefix))) + { + free(dirW); + return NULL; + } + retW = _wtempnam(dirW, prefixW); + free(dirW); + free(prefixW); + /* TODO: don't do the conversion */ + ret = astrdupw_utf8(retW); + free(retW); + return ret; +} + +/********************************************************************* + * _wtempnam (MSVCRT.@) + */ +wchar_t * CDECL _wtempnam(const wchar_t *dir, const wchar_t *prefix) +{ + wchar_t tmpbuf[MAX_PATH]; + const wchar_t *tmp_dir = _wgetenv(L"TMP"); + + if (tmp_dir) dir = tmp_dir; + + TRACE("dir (%s) prefix (%s)\n", debugstr_w(dir), debugstr_w(prefix)); + /* TODO: use whole prefix */ + if (GetTempFileNameW(dir,prefix,0,tmpbuf)) + { + TRACE("got name (%s)\n", debugstr_w(tmpbuf)); + DeleteFileW(tmpbuf); + return _wcsdup(tmpbuf); + } + TRACE("failed (%ld)\n", GetLastError()); + return NULL; +} + +/********************************************************************* + * _umask (MSVCRT.@) + */ +int CDECL _umask(int umask) +{ + int old_umask = MSVCRT_umask; + TRACE("(%d)\n",umask); + MSVCRT_umask = umask; + return old_umask; +} + +/********************************************************************* + * _utime64 (MSVCRT.@) + */ +int CDECL _utime64(const char* path, struct __utimbuf64 *t) +{ + int fd = _open(path, _O_WRONLY | _O_BINARY); + + if (fd > 0) + { + int retVal = _futime64(fd, t); + _close(fd); + return retVal; + } + return -1; +} + +/********************************************************************* + * _utime32 (MSVCRT.@) + */ +int CDECL _utime32(const char* path, struct __utimbuf32 *t) +{ + if (t) + { + struct __utimbuf64 t64; + t64.actime = t->actime; + t64.modtime = t->modtime; + return _utime64( path, &t64 ); + } + else + return _utime64( path, NULL ); +} + +/********************************************************************* + * _wutime64 (MSVCRT.@) + */ +int CDECL _wutime64(const wchar_t* path, struct __utimbuf64 *t) +{ + int fd = _wopen(path, _O_WRONLY | _O_BINARY); + + if (fd > 0) + { + int retVal = _futime64(fd, t); + _close(fd); + return retVal; + } + return -1; +} + +/********************************************************************* + * _wutime32 (MSVCRT.@) + */ +int CDECL _wutime32(const wchar_t* path, struct __utimbuf32 *t) +{ + if (t) + { + struct __utimbuf64 t64; + t64.actime = t->actime; + t64.modtime = t->modtime; + return _wutime64( path, &t64 ); + } + else + return _wutime64( path, NULL ); +} + +/********************************************************************* + * _write (MSVCRT.@) + */ +int CDECL _write(int fd, const void* buf, unsigned int count) +{ + ioinfo *info = get_ioinfo(fd); + HANDLE hand = info->handle; + DWORD num_written, i; + BOOL console = FALSE; + + if (hand == INVALID_HANDLE_VALUE || fd == MSVCRT_NO_CONSOLE_FD) + { + *_errno() = EBADF; + release_ioinfo(info); + return -1; + } + + if (ioinfo_get_textmode(info) != TEXTMODE_ANSI && count&1) + { + *_errno() = EINVAL; + release_ioinfo(info); + return -1; + } + + /* 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) + { + TRACE("WriteFile (fd %d, hand %p) failed-last error (%ld)\n", fd, + hand, GetLastError()); + msvcrt_set_errno(GetLastError()); + if (GetLastError() == ERROR_ACCESS_DENIED) + *_errno() = EBADF; + num_written = -1; + } + + release_ioinfo(info); + return num_written; + } + + if (_isatty(fd)) console = VerifyConsoleIoHandle(hand); + for (i = 0; i < count;) + { + const char *s = buf; + char lfbuf[2048]; + DWORD j = 0; + + if (ioinfo_get_textmode(info) == TEXTMODE_ANSI && console) + { + char conv[sizeof(lfbuf)]; + size_t len = 0; + +#if _MSVCR_VER >= 80 + if (info->dbcsBufferUsed) + { + conv[j++] = info->dbcsBuffer[0]; + info->dbcsBufferUsed = FALSE; + conv[j++] = s[i++]; + len++; + } +#endif + + for (; i < count && j < sizeof(conv)-1 && + len < (sizeof(lfbuf) - 1) / sizeof(WCHAR); i++, j++, len++) + { + if (isleadbyte((unsigned char)s[i])) + { + conv[j++] = s[i++]; + + if (i == count) + { +#if _MSVCR_VER >= 80 + info->dbcsBuffer[0] = conv[j-1]; + info->dbcsBufferUsed = TRUE; + break; +#else + *_errno() = EINVAL; + release_ioinfo(info); + return -1; +#endif + } + } + else if (s[i] == '\n') + { + conv[j++] = '\r'; + len++; + } + conv[j] = s[i]; + } + + len = mbstowcs((WCHAR*)lfbuf, conv, len); + if (len == -1) + { + msvcrt_set_errno(GetLastError()); + release_ioinfo(info); + return -1; + } + j = len * 2; + } + else if (ioinfo_get_textmode(info) == TEXTMODE_ANSI) + { + for (j = 0; i < count && j < sizeof(lfbuf)-1; i++, j++) + { + if (s[i] == '\n') + lfbuf[j++] = '\r'; + lfbuf[j] = s[i]; + } + } + else if (ioinfo_get_textmode(info) == TEXTMODE_UTF16LE || console) + { + for (j = 0; i < count && j < sizeof(lfbuf)-3; i++, j++) + { + if (s[i] == '\n' && !s[i+1]) + { + lfbuf[j++] = '\r'; + lfbuf[j++] = 0; + } + lfbuf[j++] = s[i++]; + lfbuf[j] = s[i]; + } + } + else + { + char conv[sizeof(lfbuf)/4]; + + for (j = 0; i < count && j < sizeof(conv)-3; i++, j++) + { + if (s[i] == '\n' && !s[i+1]) + { + conv[j++] = '\r'; + conv[j++] = 0; + } + conv[j++] = s[i++]; + conv[j] = s[i]; + } + + j = WideCharToMultiByte(CP_UTF8, 0, (WCHAR*)conv, j/2, lfbuf, sizeof(lfbuf), NULL, NULL); + if (!j) + { + msvcrt_set_errno(GetLastError()); + release_ioinfo(info); + return -1; + } + } + + if (console) + { + j = j/2; + if (!WriteConsoleW(hand, lfbuf, j, &num_written, NULL)) + num_written = -1; + } + else if (!WriteFile(hand, lfbuf, j, &num_written, NULL)) + { + num_written = -1; + } + + if (num_written != j) + { + TRACE("WriteFile/WriteConsoleW (fd %d, hand %p) failed-last error (%ld)\n", fd, + hand, GetLastError()); + msvcrt_set_errno(GetLastError()); + if (GetLastError() == ERROR_ACCESS_DENIED) + *_errno() = EBADF; + release_ioinfo(info); + return -1; + } + } + + release_ioinfo(info); + return count; +} + +/********************************************************************* + * _putw (MSVCRT.@) + */ +int CDECL _putw(int val, FILE* file) +{ + int len; + + _lock_file(file); + len = _write(file->_file, &val, sizeof(val)); + if (len == sizeof(val)) { + _unlock_file(file); + return val; + } + + file->_flag |= _IOERR; + _unlock_file(file); + return EOF; +} + +/********************************************************************* + * fclose (MSVCRT.@) + */ +int CDECL fclose(FILE* file) +{ + int ret; + + if (!MSVCRT_CHECK_PMT(file != NULL)) return EOF; + + _lock_file(file); + ret = _fclose_nolock(file); + _unlock_file(file); + + return ret; +} + +/********************************************************************* + * _fclose_nolock (MSVCRT.@) + */ +int CDECL _fclose_nolock(FILE* file) +{ + int r, flag; + + if (!MSVCRT_CHECK_PMT(file != NULL)) return EOF; + + if(!(file->_flag & (_IOREAD | _IOWRT | _IORW))) + { + file->_flag = 0; + return EOF; + } + + flag = file->_flag; + free(file->_tmpfname); + file->_tmpfname = NULL; + /* flush stdio buffers */ + if(file->_flag & _IOWRT) + _fflush_nolock(file); + if(file->_flag & _IOMYBUF) + free(file->_base); + + r=_close(file->_file); + file->_flag = 0; + + return ((r == -1) || (flag & _IOERR) ? EOF : 0); +} + +/********************************************************************* + * feof (MSVCRT.@) + */ +int CDECL feof(FILE* file) +{ + return file->_flag & _IOEOF; +} + +/********************************************************************* + * ferror (MSVCRT.@) + */ +int CDECL ferror(FILE* file) +{ + return file->_flag & _IOERR; +} + +/********************************************************************* + * _filbuf (MSVCRT.@) + */ +int CDECL _filbuf(FILE* file) +{ + unsigned char c; + + if(file->_flag & _IOSTRG) + return EOF; + + /* Allocate buffer if needed */ + if(!(file->_flag & (MSVCRT__NOBUF | _IOMYBUF | MSVCRT__USERBUF))) + msvcrt_alloc_buffer(file); + + if(!(file->_flag & _IOREAD)) { + if(file->_flag & _IORW) + file->_flag |= _IOREAD; + else + return EOF; + } + + if(!(file->_flag & (_IOMYBUF | MSVCRT__USERBUF))) { + int r; + if ((r = _read(file->_file,&c,1)) != 1) { + file->_flag |= (r == 0) ? _IOEOF : _IOERR; + return EOF; + } + + return c; + } else { + file->_cnt = _read(file->_file, file->_base, file->_bufsiz); + if(file->_cnt<=0) { + file->_flag |= (file->_cnt == 0) ? _IOEOF : _IOERR; + file->_cnt = 0; + return EOF; + } + + file->_cnt--; + file->_ptr = file->_base+1; + c = *(unsigned char *)file->_base; + return c; + } +} + +/********************************************************************* + * fgetc (MSVCRT.@) + */ +int CDECL fgetc(FILE* file) +{ + int ret; + + _lock_file(file); + ret = _fgetc_nolock(file); + _unlock_file(file); + + return ret; +} + +/********************************************************************* + * _fgetc_nolock (MSVCRT.@) + */ +int CDECL _fgetc_nolock(FILE* file) +{ + unsigned char *i; + unsigned int j; + + if (file->_cnt>0) { + file->_cnt--; + i = (unsigned char *)file->_ptr++; + j = *i; + } else + j = _filbuf(file); + + return j; +} + +/********************************************************************* + * _fgetchar (MSVCRT.@) + */ +int CDECL _fgetchar(void) +{ + return fgetc(stdin); +} + +/********************************************************************* + * fgets (MSVCRT.@) + */ +char * CDECL fgets(char *s, int size, FILE* file) +{ + int cc = EOF; + char * buf_start = s; + + TRACE(":file(%p) fd (%d) str (%p) len (%d)\n", + file,file->_file,s,size); + + _lock_file(file); + + while ((size >1) && (cc = _fgetc_nolock(file)) != EOF && cc != '\n') + { + *s++ = (char)cc; + size --; + } + if ((cc == EOF) && (s == buf_start)) /* If nothing read, return 0*/ + { + TRACE(":nothing read\n"); + _unlock_file(file); + return NULL; + } + if ((cc != EOF) && (size > 1)) + *s++ = cc; + *s = '\0'; + TRACE(":got %s\n", debugstr_a(buf_start)); + _unlock_file(file); + return buf_start; +} + +/********************************************************************* + * fgetwc (MSVCRT.@) + */ +wint_t CDECL fgetwc(FILE* file) +{ + wint_t ret; + + _lock_file(file); + ret = _fgetwc_nolock(file); + _unlock_file(file); + + return ret; +} + +/********************************************************************* + * _fgetwc_nolock (MSVCRT.@) + */ +wint_t CDECL _fgetwc_nolock(FILE* file) +{ + wint_t ret; + int ch; + + if(ioinfo_get_textmode(get_ioinfo_nolock(file->_file)) != TEXTMODE_ANSI + || !(get_ioinfo_nolock(file->_file)->wxflag & WX_TEXT)) { + char *p; + + for(p=(char*)&ret; (wint_t*)p<&ret+1; p++) { + ch = _fgetc_nolock(file); + if(ch == EOF) { + ret = WEOF; + break; + } + *p = (char)ch; + } + }else { + char mbs[MB_LEN_MAX]; + int len = 0; + + ch = _fgetc_nolock(file); + if(ch != EOF) { + mbs[0] = (char)ch; + if(isleadbyte((unsigned char)mbs[0])) { + ch = _fgetc_nolock(file); + if(ch != EOF) { + mbs[1] = (char)ch; + len = 2; + } + }else { + len = 1; + } + } + + if(!len || mbtowc(&ret, mbs, len)==-1) + ret = WEOF; + } + + return ret; +} + +/********************************************************************* + * _getw (MSVCRT.@) + */ +int CDECL _getw(FILE* file) +{ + char *ch; + int i, k; + unsigned int j; + ch = (char *)&i; + + _lock_file(file); + for (j=0; j_flag |= _IOEOF; + _unlock_file(file); + return EOF; + } + ch[j] = k; + } + + _unlock_file(file); + return i; +} + +/********************************************************************* + * getwc (MSVCRT.@) + */ +wint_t CDECL getwc(FILE* file) +{ + return fgetwc(file); +} + +/********************************************************************* + * _fgetwchar (MSVCRT.@) + */ +wint_t CDECL _fgetwchar(void) +{ + return fgetwc(stdin); +} + +/********************************************************************* + * getwchar (MSVCRT.@) + */ +wint_t CDECL getwchar(void) +{ + return _fgetwchar(); +} + +/********************************************************************* + * fgetws (MSVCRT.@) + */ +wchar_t * CDECL fgetws(wchar_t *s, int size, FILE* file) +{ + wint_t cc = WEOF; + wchar_t * buf_start = s; + + TRACE(":file(%p) fd (%d) str (%p) len (%d)\n", + file,file->_file,s,size); + + _lock_file(file); + + while ((size >1) && (cc = _fgetwc_nolock(file)) != WEOF && cc != '\n') + { + *s++ = cc; + size --; + } + if ((cc == WEOF) && (s == buf_start)) /* If nothing read, return 0*/ + { + TRACE(":nothing read\n"); + _unlock_file(file); + return NULL; + } + if ((cc != WEOF) && (size > 1)) + *s++ = cc; + *s = 0; + TRACE(":got %s\n", debugstr_w(buf_start)); + _unlock_file(file); + return buf_start; +} + +/********************************************************************* + * _flsbuf (MSVCRT.@) + */ +int CDECL _flsbuf(int c, FILE* file) +{ + /* Flush output buffer */ + if(!(file->_flag & (MSVCRT__NOBUF | _IOMYBUF | MSVCRT__USERBUF))) { + msvcrt_alloc_buffer(file); + } + + if(!(file->_flag & _IOWRT)) { + if(!(file->_flag & _IORW)) { + file->_flag |= _IOERR; + *_errno() = EBADF; + return EOF; + } + file->_flag |= _IOWRT; + } + if(file->_flag & _IOREAD) { + if(!(file->_flag & _IOEOF)) { + file->_flag |= _IOERR; + return EOF; + } + file->_cnt = 0; + file->_ptr = file->_base; + file->_flag &= ~(_IOREAD | _IOEOF); + } + + if(file->_flag & (_IOMYBUF | MSVCRT__USERBUF)) { + int res = 0; + + if(file->_cnt <= 0) { + res = msvcrt_flush_buffer(file); + if(res) + return res; + file->_flag |= _IOWRT; + file->_cnt=file->_bufsiz; + } + *file->_ptr++ = c; + file->_cnt--; + return c&0xff; + } else { + unsigned char cc=c; + int len; + /* set _cnt to 0 for unbuffered FILEs */ + file->_cnt = 0; + len = _write(file->_file, &cc, 1); + if (len == 1) + return c & 0xff; + file->_flag |= _IOERR; + return EOF; + } +} + +/********************************************************************* + * fwrite (MSVCRT.@) + */ +size_t CDECL fwrite(const void *ptr, size_t size, size_t nmemb, FILE* file) +{ + size_t ret; + + _lock_file(file); + ret = _fwrite_nolock(ptr, size, nmemb, file); + _unlock_file(file); + + return ret; +} + +/********************************************************************* + * _fwrite_nolock (MSVCRT.@) + */ +size_t CDECL _fwrite_nolock(const void *ptr, size_t size, size_t nmemb, FILE* file) +{ + size_t wrcnt=size * nmemb; + int written = 0; + if (size == 0) + return 0; + + while(wrcnt) { + if(file->_cnt < 0) { + WARN("negative file->_cnt value in %p\n", file); + file->_flag |= _IOERR; + break; + } else if(file->_cnt) { + int pcnt=(file->_cnt>wrcnt)? wrcnt: file->_cnt; + memcpy(file->_ptr, ptr, pcnt); + file->_cnt -= pcnt; + file->_ptr += pcnt; + written += pcnt; + wrcnt -= pcnt; + ptr = (const char*)ptr + pcnt; + } else if((file->_flag & MSVCRT__NOBUF) + || ((file->_flag & (_IOMYBUF | MSVCRT__USERBUF)) && wrcnt >= file->_bufsiz) + || (!(file->_flag & (_IOMYBUF | MSVCRT__USERBUF)) && wrcnt >= MSVCRT_INTERNAL_BUFSIZ)) { + size_t pcnt; + int bufsiz; + + if(file->_flag & MSVCRT__NOBUF) + bufsiz = 1; + else if(!(file->_flag & (_IOMYBUF | MSVCRT__USERBUF))) + bufsiz = MSVCRT_INTERNAL_BUFSIZ; + else + bufsiz = file->_bufsiz; + + pcnt = (wrcnt / bufsiz) * bufsiz; + + if(msvcrt_flush_buffer(file) == EOF) + break; + + if(_write(file->_file, ptr, pcnt) <= 0) { + file->_flag |= _IOERR; + break; + } + written += pcnt; + wrcnt -= pcnt; + ptr = (const char*)ptr + pcnt; + } else { + if(_flsbuf(*(const char*)ptr, file) == EOF) + break; + written++; + wrcnt--; + ptr = (const char*)ptr + 1; + } + } + + return written / size; +} + +/********************************************************************* + * fputwc (MSVCRT.@) + */ +wint_t CDECL fputwc(wint_t wc, FILE* file) +{ + wint_t ret; + + _lock_file(file); + ret = _fputwc_nolock(wc, file); + _unlock_file(file); + + return ret; +} + +/********************************************************************* + * _fputwc_nolock (MSVCRT.@) + */ +wint_t CDECL _fputwc_nolock(wint_t wc, FILE* file) +{ + wchar_t mwc=wc; + ioinfo *fdinfo; + wint_t ret; + + fdinfo = get_ioinfo_nolock(file->_file); + + if((fdinfo->wxflag&WX_TEXT) && ioinfo_get_textmode(fdinfo) == TEXTMODE_ANSI) { + char buf[MB_LEN_MAX]; + int char_len; + + char_len = wctomb(buf, mwc); + if(char_len!=-1 && _fwrite_nolock(buf, char_len, 1, file)==1) + ret = wc; + else + ret = WEOF; + }else if(_fwrite_nolock(&mwc, sizeof(mwc), 1, file) == 1) { + ret = wc; + }else { + ret = WEOF; + } + + return ret; +} + +/********************************************************************* + * _fputwchar (MSVCRT.@) + */ +wint_t CDECL _fputwchar(wint_t wc) +{ + return fputwc(wc, stdout); +} + +/********************************************************************* + * _wfsopen (MSVCRT.@) + */ +FILE * CDECL _wfsopen(const wchar_t *path, const wchar_t *mode, int share) +{ + FILE* file; + int open_flags, stream_flags, fd; + + TRACE("(%s,%s)\n", debugstr_w(path), debugstr_w(mode)); + + /* map mode string to open() flags. "man fopen" for possibilities. */ + if (msvcrt_get_flags(mode, &open_flags, &stream_flags) == -1) + return NULL; + + LOCK_FILES(); + fd = _wsopen(path, open_flags, share, _S_IREAD | _S_IWRITE); + if (fd < 0) + file = NULL; + else if ((file = msvcrt_alloc_fp()) && msvcrt_init_fp(file, fd, stream_flags) + != -1) + TRACE(":fd (%d) mode (%s) FILE* (%p)\n", fd, debugstr_w(mode), file); + else if (file) + { + file->_flag = 0; + file = NULL; + } + + TRACE(":got (%p)\n",file); + if (fd >= 0 && !file) + _close(fd); + UNLOCK_FILES(); + return file; +} + +/********************************************************************* + * _fsopen (MSVCRT.@) + */ +FILE * CDECL _fsopen(const char *path, const char *mode, int share) +{ + wchar_t *pathW = NULL, *modeW = NULL; + FILE *ret; + + if (path && !(pathW = wstrdupa_utf8(path))) return NULL; + if (mode && !(modeW = wstrdupa_utf8(mode))) + { + free(pathW); + return NULL; + } + + ret = _wfsopen(pathW, modeW, share); + + free(pathW); + free(modeW); + return ret; +} + +/********************************************************************* + * fopen (MSVCRT.@) + */ +FILE * CDECL fopen(const char *path, const char *mode) +{ + return _fsopen( path, mode, _SH_DENYNO ); +} + +/********************************************************************* + * fopen_s (MSVCRT.@) + */ +int CDECL fopen_s(FILE** pFile, + const char *filename, const char *mode) +{ + 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); + + if(!*pFile) + return *_errno(); + return 0; +} + +/********************************************************************* + * _wfopen (MSVCRT.@) + */ +FILE * CDECL _wfopen(const wchar_t *path, const wchar_t *mode) +{ + return _wfsopen( path, mode, _SH_DENYNO ); +} + +/********************************************************************* + * _wfopen_s (MSVCRT.@) + */ +int CDECL _wfopen_s(FILE** pFile, const wchar_t *filename, + const wchar_t *mode) +{ + 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 = _wfopen(filename, mode); + + if(!*pFile) + return *_errno(); + return 0; +} + +/********************************************************************* + * fputc (MSVCRT.@) + */ +int CDECL fputc(int c, FILE* file) +{ + int ret; + + _lock_file(file); + ret = _fputc_nolock(c, file); + _unlock_file(file); + + return ret; +} + +/********************************************************************* + * _fputc_nolock (MSVCRT.@) + */ +int CDECL _fputc_nolock(int c, FILE* file) +{ + int res; + + if(file->_cnt>0) { + *file->_ptr++=c; + file->_cnt--; + if (c == '\n') + { + res = msvcrt_flush_buffer(file); + return res ? res : c; + } + else { + return c & 0xff; + } + } else { + res = _flsbuf(c, file); + return res; + } +} + +/********************************************************************* + * _fputchar (MSVCRT.@) + */ +int CDECL _fputchar(int c) +{ + return fputc(c, stdout); +} + +/********************************************************************* + * fread (MSVCRT.@) + */ +size_t CDECL fread(void *ptr, size_t size, size_t nmemb, FILE* file) +{ + size_t ret; + + _lock_file(file); + ret = _fread_nolock(ptr, size, nmemb, file); + _unlock_file(file); + + return ret; +} + +/********************************************************************* + * _fread_nolock (MSVCRT.@) + */ +size_t CDECL _fread_nolock(void *ptr, size_t size, size_t nmemb, FILE* file) +{ + size_t rcnt=size * nmemb; + size_t read=0; + size_t pread=0; + + if(!rcnt) + return 0; + + /* first buffered data */ + if(file->_cnt>0) { + int pcnt= (rcnt>file->_cnt)? file->_cnt:rcnt; + memcpy(ptr, file->_ptr, pcnt); + file->_cnt -= pcnt; + file->_ptr += pcnt; + read += pcnt ; + rcnt -= pcnt ; + ptr = (char*)ptr + pcnt; + } else if(!(file->_flag & _IOREAD )) { + if(file->_flag & _IORW) { + file->_flag |= _IOREAD; + } else { + return 0; + } + } + + if(rcnt>0 && !(file->_flag & (MSVCRT__NOBUF | _IOMYBUF | MSVCRT__USERBUF))) + msvcrt_alloc_buffer(file); + + while(rcnt>0) + { + int i; + if (!file->_cnt && rcnt_bufsiz && (file->_flag & (_IOMYBUF | MSVCRT__USERBUF))) { + i = _read(file->_file, file->_base, file->_bufsiz); + file->_ptr = file->_base; + if (i != -1) { + file->_cnt = i; + if (i > rcnt) i = rcnt; + } + /* If the buffer fill reaches eof but fread wouldn't, clear eof. */ + if (i > 0 && i < file->_cnt) { + get_ioinfo_nolock(file->_file)->wxflag &= ~WX_ATEOF; + file->_flag &= ~_IOEOF; + } + if (i > 0) { + memcpy(ptr, file->_ptr, i); + file->_cnt -= i; + file->_ptr += i; + } + } else if (rcnt > INT_MAX) { + i = _read(file->_file, ptr, INT_MAX); + } else if (rcnt < (file->_bufsiz ? file->_bufsiz : MSVCRT_INTERNAL_BUFSIZ)) { + i = _read(file->_file, ptr, rcnt); + } else { + i = _read(file->_file, ptr, rcnt - rcnt % (file->_bufsiz ? file->_bufsiz : MSVCRT_INTERNAL_BUFSIZ)); + } + pread += i; + rcnt -= i; + ptr = (char *)ptr+i; + /* expose feof condition in the flags + * MFC tests file->_flag for feof, and doesn't call feof()) + */ + if (get_ioinfo_nolock(file->_file)->wxflag & WX_ATEOF) + file->_flag |= _IOEOF; + else if (i == -1) + { + file->_flag |= _IOERR; + pread = 0; + rcnt = 0; + } + if (i < 1) break; + } + read+=pread; + return read / size; +} + +#if _MSVCR_VER >= 80 + +/********************************************************************* + * fread_s (MSVCR80.@) + */ +size_t CDECL fread_s(void *buf, size_t buf_size, size_t elem_size, + size_t count, FILE *stream) +{ + size_t ret; + + if(!MSVCRT_CHECK_PMT(stream != NULL)) { + if(buf && buf_size) + memset(buf, 0, buf_size); + return 0; + } + if(!elem_size || !count) return 0; + + _lock_file(stream); + ret = _fread_nolock_s(buf, buf_size, elem_size, count, stream); + _unlock_file(stream); + + return ret; +} + +/********************************************************************* + * _fread_nolock_s (MSVCR80.@) + */ +size_t CDECL _fread_nolock_s(void *buf, size_t buf_size, size_t elem_size, + size_t count, FILE *stream) +{ + size_t bytes_left, buf_pos; + + TRACE("(%p %Iu %Iu %Iu %p)\n", buf, buf_size, elem_size, count, stream); + + if(!MSVCRT_CHECK_PMT(stream != NULL)) { + if(buf && buf_size) + memset(buf, 0, buf_size); + return 0; + } + if(!elem_size || !count) return 0; + if(!MSVCRT_CHECK_PMT(buf != NULL)) return 0; + if(!MSVCRT_CHECK_PMT(SIZE_MAX/count >= elem_size)) return 0; + + bytes_left = elem_size*count; + buf_pos = 0; + while(bytes_left) { + if(stream->_cnt > 0) { + size_t size = bytes_left_cnt ? bytes_left : stream->_cnt; + + if(!MSVCRT_CHECK_PMT_ERR(size <= buf_size-buf_pos, ERANGE)) { + memset(buf, 0, buf_size); + return 0; + } + + _fread_nolock((char*)buf+buf_pos, 1, size, stream); + buf_pos += size; + bytes_left -= size; + }else { + int c = _filbuf(stream); + + if(c == EOF) + break; + + if(!MSVCRT_CHECK_PMT_ERR(buf_size != buf_pos, ERANGE)) { + memset(buf, 0, buf_size); + return 0; + } + + ((char*)buf)[buf_pos++] = c; + bytes_left--; + } + } + + return buf_pos/elem_size; +} + +#endif /* _MSVCR_VER >= 80 */ + +/********************************************************************* + * _wfreopen (MSVCRT.@) + * + */ +FILE* CDECL _wfreopen(const wchar_t *path, const wchar_t *mode, FILE* file) +{ + int open_flags, stream_flags, fd; + + TRACE(":path (%s) mode (%s) file (%p) fd (%d)\n", debugstr_w(path), debugstr_w(mode), file, file ? file->_file : -1); + + LOCK_FILES(); + if (file) + { + fclose(file); + if (msvcrt_get_flags(mode, &open_flags, &stream_flags) == -1) + file = NULL; + else if((fd = _wopen(path, open_flags, _S_IREAD | _S_IWRITE)) < 0) + file = NULL; + else if(msvcrt_init_fp(file, fd, stream_flags) == -1) + { + file->_flag = 0; + file = NULL; + } + } + UNLOCK_FILES(); + return file; +} + +/********************************************************************* + * _wfreopen_s (MSVCRT.@) + */ +int CDECL _wfreopen_s(FILE** pFile, + const wchar_t *path, const wchar_t *mode, FILE* file) +{ + if (!MSVCRT_CHECK_PMT(pFile != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(path != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(mode != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(file != NULL)) return EINVAL; + + *pFile = _wfreopen(path, mode, file); + + if(!*pFile) + return *_errno(); + return 0; +} + +/********************************************************************* + * freopen (MSVCRT.@) + * + */ +FILE* CDECL freopen(const char *path, const char *mode, FILE* file) +{ + FILE *ret; + wchar_t *pathW = NULL, *modeW = NULL; + + if (path && !(pathW = wstrdupa_utf8(path))) return NULL; + if (mode && !(modeW = msvcrt_wstrdupa(mode))) + { + free(pathW); + return NULL; + } + + ret = _wfreopen(pathW, modeW, file); + + free(pathW); + free(modeW); + return ret; +} + +/********************************************************************* + * freopen_s (MSVCRT.@) + */ +errno_t CDECL freopen_s(FILE** pFile, + const char *path, const char *mode, FILE* file) +{ + if (!MSVCRT_CHECK_PMT(pFile != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(path != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(mode != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(file != NULL)) return EINVAL; + + *pFile = freopen(path, mode, file); + + if(!*pFile) + return *_errno(); + return 0; +} + +/********************************************************************* + * fsetpos (MSVCRT.@) + */ +int CDECL fsetpos(FILE* file, fpos_t *pos) +{ + return _fseeki64(file,*pos,SEEK_SET); +} + +/********************************************************************* + * _ftelli64 (MSVCRT.@) + */ +__int64 CDECL _ftelli64(FILE* file) +{ + __int64 ret; + + _lock_file(file); + ret = _ftelli64_nolock(file); + _unlock_file(file); + + return ret; +} + +/********************************************************************* + * _ftelli64_nolock (MSVCRT.@) + */ +__int64 CDECL _ftelli64_nolock(FILE* file) +{ + __int64 pos; + + pos = _telli64(file->_file); + if(pos == -1) + return -1; + if(file->_flag & (_IOMYBUF | MSVCRT__USERBUF)) { + if(file->_flag & _IOWRT) { + pos += file->_ptr - file->_base; + + if(get_ioinfo_nolock(file->_file)->wxflag & WX_TEXT) { + char *p; + + for(p=file->_base; p_ptr; p++) + if(*p == '\n') + pos++; + } + } else if(!file->_cnt) { /* nothing to do */ + } else if(_lseeki64(file->_file, 0, SEEK_END)==pos) { + int i; + + pos -= file->_cnt; + if(get_ioinfo_nolock(file->_file)->wxflag & WX_TEXT) { + for(i=0; i_cnt; i++) + if(file->_ptr[i] == '\n') + pos--; + } + } else { + char *p; + + if(_lseeki64(file->_file, pos, SEEK_SET) != pos) + return -1; + + pos -= file->_bufsiz; + pos += file->_ptr - file->_base; + + if(get_ioinfo_nolock(file->_file)->wxflag & WX_TEXT) { + if(get_ioinfo_nolock(file->_file)->wxflag & WX_READNL) + pos--; + + for(p=file->_base; p_ptr; p++) + if(*p == '\n') + pos++; + } + } + } + + return pos; +} + +/********************************************************************* + * ftell (MSVCRT.@) + */ +__msvcrt_long CDECL ftell(FILE* file) +{ + return _ftelli64(file); +} + +#if _MSVCR_VER >= 80 +/********************************************************************* + * _ftell_nolock (MSVCR80.@) + */ +__msvcrt_long CDECL _ftell_nolock(FILE* file) +{ + return _ftelli64_nolock(file); +} +#endif + +/********************************************************************* + * fgetpos (MSVCRT.@) + */ +int CDECL fgetpos(FILE* file, fpos_t *pos) +{ + *pos = _ftelli64(file); + if(*pos == -1) + return -1; + return 0; +} + +/********************************************************************* + * fputs (MSVCRT.@) + */ +int CDECL fputs(const char *s, FILE* file) +{ + size_t len = strlen(s); + int ret; + + _lock_file(file); + ret = _fwrite_nolock(s, sizeof(*s), len, file) == len ? 0 : EOF; + _unlock_file(file); + return ret; +} + +/********************************************************************* + * fputws (MSVCRT.@) + */ +int CDECL fputws(const wchar_t *s, FILE* file) +{ + size_t i, len = wcslen(s); + BOOL tmp_buf; + int ret; + + _lock_file(file); + if (!(get_ioinfo_nolock(file->_file)->wxflag & WX_TEXT)) { + ret = _fwrite_nolock(s,sizeof(*s),len,file) == len ? 0 : EOF; + _unlock_file(file); + return ret; + } + + tmp_buf = add_std_buffer(file); + for (i=0; i= 0) + ret = _fputwc_nolock('\n', stdout); + _unlock_file(stdout); + return ret >= 0 ? 0 : WEOF; +} + +/********************************************************************* + * remove (MSVCRT.@) + */ +int CDECL remove(const char *path) +{ + return _unlink(path); +} + +/********************************************************************* + * _wremove (MSVCRT.@) + */ +int CDECL _wremove(const wchar_t *path) +{ + return _wunlink(path); +} + +/********************************************************************* + * rename (MSVCRT.@) + */ +int CDECL rename(const char *oldpath,const char *newpath) +{ + wchar_t *oldpathW = NULL, *newpathW = NULL; + int ret; + + if (oldpath && !(oldpathW = wstrdupa_utf8(oldpath))) return -1; + if (newpath && !(newpathW = wstrdupa_utf8(newpath))) + { + free(oldpathW); + return -1; + } + ret = _wrename(oldpathW, newpathW); + free(oldpathW); + free(newpathW); + return ret; +} + +/********************************************************************* + * _wrename (MSVCRT.@) + */ +int CDECL _wrename(const wchar_t *oldpath,const wchar_t *newpath) +{ + TRACE(":from %s to %s\n", debugstr_w(oldpath), debugstr_w(newpath)); + if (MoveFileExW(oldpath, newpath, MOVEFILE_COPY_ALLOWED)) + return 0; + TRACE(":failed (%ld)\n", GetLastError()); + msvcrt_set_errno(GetLastError()); + return -1; +} + +/********************************************************************* + * setvbuf (MSVCRT.@) + */ +int CDECL setvbuf(FILE* file, char *buf, int mode, size_t size) +{ + if(!MSVCRT_CHECK_PMT(file != NULL)) return -1; + if(!MSVCRT_CHECK_PMT(mode==_IONBF || mode==_IOFBF || mode==_IOLBF)) return -1; + if(!MSVCRT_CHECK_PMT(mode==_IONBF || (size>=2 && size<=INT_MAX))) return -1; + + _lock_file(file); + + _fflush_nolock(file); + if(file->_flag & _IOMYBUF) + free(file->_base); + file->_flag &= ~(MSVCRT__NOBUF | _IOMYBUF | MSVCRT__USERBUF); + file->_cnt = 0; + + if(mode == _IONBF) { + file->_flag |= MSVCRT__NOBUF; + file->_base = file->_ptr = (char*)&file->_charbuf; + file->_bufsiz = 2; + }else if(buf) { + file->_base = file->_ptr = buf; + file->_flag |= MSVCRT__USERBUF; + file->_bufsiz = size; + }else { + file->_base = file->_ptr = malloc(size); + if(!file->_base) { + file->_bufsiz = 0; + _unlock_file(file); + return -1; + } + + file->_flag |= _IOMYBUF; + file->_bufsiz = size; + } + _unlock_file(file); + return 0; +} + +/********************************************************************* + * setbuf (MSVCRT.@) + */ +void CDECL setbuf(FILE* file, char *buf) +{ + setvbuf(file, buf, buf ? _IOFBF : _IONBF, BUFSIZ); +} + +static int tmpnam_helper(char *s, size_t size, LONG *tmpnam_unique, int tmp_max) +{ + char tmpstr[8]; + char *p = s; + int digits; + + if (!MSVCRT_CHECK_PMT(s != NULL)) return EINVAL; + + if (size < 3) { + if (size) *s = 0; + *_errno() = ERANGE; + return ERANGE; + } + *p++ = '\\'; + *p++ = 's'; + size -= 2; + digits = msvcrt_int_to_base32(GetCurrentProcessId(), tmpstr); + if (digits+1 > size) { + *s = 0; + *_errno() = ERANGE; + return ERANGE; + } + memcpy(p, tmpstr, digits*sizeof(tmpstr[0])); + p += digits; + *p++ = '.'; + size -= digits+1; + + while(1) { + while ((digits = *tmpnam_unique)+1 < tmp_max) { + if (InterlockedCompareExchange(tmpnam_unique, digits+1, digits) == digits) + break; + } + + digits = msvcrt_int_to_base32(digits, tmpstr); + if (digits+1 > size) { + *s = 0; + *_errno() = ERANGE; + return ERANGE; + } + memcpy(p, tmpstr, digits*sizeof(tmpstr[0])); + p[digits] = 0; + + if (GetFileAttributesA(s) == INVALID_FILE_ATTRIBUTES && + GetLastError() == ERROR_FILE_NOT_FOUND) + break; + } + return 0; +} + +int CDECL tmpnam_s(char *s, size_t size) +{ + return tmpnam_helper(s, size, &tmpnam_s_unique, TMP_MAX_S); +} + +/********************************************************************* + * tmpnam (MSVCRT.@) + */ +char * CDECL tmpnam(char *s) +{ + if (!s) { + thread_data_t *data = msvcrt_get_thread_data(); + + if(!data->tmpnam_buffer) + data->tmpnam_buffer = malloc(MAX_PATH); + + s = data->tmpnam_buffer; + } + + return tmpnam_helper(s, -1, &tmpnam_unique, TMP_MAX) ? NULL : s; +} + +static int wtmpnam_helper(wchar_t *s, size_t size, LONG *tmpnam_unique, int tmp_max) +{ + wchar_t tmpstr[8]; + wchar_t *p = s; + int digits; + + if (!MSVCRT_CHECK_PMT(s != NULL)) return EINVAL; + + if (size < 3) { + if (size) *s = 0; + *_errno() = ERANGE; + return ERANGE; + } + *p++ = '\\'; + *p++ = 's'; + size -= 2; + digits = msvcrt_int_to_base32_w(GetCurrentProcessId(), tmpstr); + if (digits+1 > size) { + *s = 0; + *_errno() = ERANGE; + return ERANGE; + } + memcpy(p, tmpstr, digits*sizeof(tmpstr[0])); + p += digits; + *p++ = '.'; + size -= digits+1; + + while(1) { + while ((digits = *tmpnam_unique)+1 < tmp_max) { + if (InterlockedCompareExchange(tmpnam_unique, digits+1, digits) == digits) + break; + } + + digits = msvcrt_int_to_base32_w(digits, tmpstr); + if (digits+1 > size) { + *s = 0; + *_errno() = ERANGE; + return ERANGE; + } + memcpy(p, tmpstr, digits*sizeof(tmpstr[0])); + p[digits] = 0; + + if (GetFileAttributesW(s) == INVALID_FILE_ATTRIBUTES && + GetLastError() == ERROR_FILE_NOT_FOUND) + break; + } + return 0; +} + +/********************************************************************* + * _wtmpnam_s (MSVCRT.@) + */ +int CDECL _wtmpnam_s(wchar_t *s, size_t size) +{ + return wtmpnam_helper(s, size, &tmpnam_s_unique, TMP_MAX_S); +} + +/********************************************************************* + * _wtmpnam (MSVCRT.@) + */ +wchar_t * CDECL _wtmpnam(wchar_t *s) +{ + if (!s) { + thread_data_t *data = msvcrt_get_thread_data(); + + if(!data->wtmpnam_buffer) + data->wtmpnam_buffer = malloc(sizeof(wchar_t[MAX_PATH])); + + s = data->wtmpnam_buffer; + } + + return wtmpnam_helper(s, -1, &tmpnam_unique, TMP_MAX) ? NULL : s; +} + +/********************************************************************* + * tmpfile (MSVCRT.@) + */ +FILE* CDECL tmpfile(void) +{ + char *filename = _tempnam(",", "t"); + int fd; + FILE* file = NULL; + + LOCK_FILES(); + fd = _open(filename, _O_CREAT | _O_BINARY | _O_RDWR | _O_TEMPORARY, + _S_IREAD | _S_IWRITE); + if (fd != -1 && (file = msvcrt_alloc_fp())) + { + if (msvcrt_init_fp(file, fd, _IORW) == -1) + { + file->_flag = 0; + file = NULL; + } + else file->_tmpfname = _strdup(filename); + } + + if(fd != -1 && !file) + _close(fd); + free(filename); + UNLOCK_FILES(); + return file; +} + +/********************************************************************* + * tmpfile_s (MSVCRT.@) + */ +int CDECL tmpfile_s(FILE** file) +{ + if (!MSVCRT_CHECK_PMT(file != NULL)) return EINVAL; + + *file = tmpfile(); + return 0; +} + +static int puts_clbk_file_a(void *file, int len, const char *str) +{ + return fwrite(str, sizeof(char), len, file); +} + +static int puts_clbk_file_w(void *file, int len, const wchar_t *str) +{ + int i, ret; + + _lock_file(file); + + if(!(get_ioinfo_nolock(((FILE*)file)->_file)->wxflag & WX_TEXT)) { + ret = _fwrite_nolock(str, sizeof(wchar_t), len, file); + _unlock_file(file); + return ret; + } + + for(i=0; i= 140 + +/********************************************************************* + * __stdio_common_vfprintf (UCRTBASE.@) + */ +int CDECL _stdio_common_vfprintf(unsigned __int64 options, FILE *file, const char *format, + _locale_t locale, va_list valist) +{ + if (options & ~UCRTBASE_PRINTF_MASK) + FIXME("options %#I64x not handled\n", options); + + return vfprintf_helper(options & UCRTBASE_PRINTF_MASK, file, format, locale, valist); +} + +/********************************************************************* + * __stdio_common_vfprintf_p (UCRTBASE.@) + */ +int CDECL __stdio_common_vfprintf_p(unsigned __int64 options, FILE *file, const char *format, + _locale_t locale, va_list valist) +{ + if (options & ~UCRTBASE_PRINTF_MASK) + FIXME("options %#I64x not handled\n", options); + + return vfprintf_helper((options & UCRTBASE_PRINTF_MASK) | MSVCRT_PRINTF_POSITIONAL_PARAMS + | MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER, file, format, locale, valist); +} + + +/********************************************************************* + * __stdio_common_vfprintf_s (UCRTBASE.@) + */ +int CDECL __stdio_common_vfprintf_s(unsigned __int64 options, FILE *file, const char *format, + _locale_t locale, va_list valist) +{ + if (options & ~UCRTBASE_PRINTF_MASK) + FIXME("options %#I64x not handled\n", options); + + return vfprintf_helper((options & UCRTBASE_PRINTF_MASK) | MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER, + file, format, locale, valist); +} + +/********************************************************************* + * __stdio_common_vfwprintf (UCRTBASE.@) + */ +int CDECL __stdio_common_vfwprintf(unsigned __int64 options, FILE *file, const wchar_t *format, + _locale_t locale, va_list valist) +{ + if (options & ~UCRTBASE_PRINTF_MASK) + FIXME("options %#I64x not handled\n", options); + + return vfwprintf_helper(options & UCRTBASE_PRINTF_MASK, file, format, locale, valist); +} + +/********************************************************************* + * __stdio_common_vfwprintf_p (UCRTBASE.@) + */ +int CDECL __stdio_common_vfwprintf_p(unsigned __int64 options, FILE *file, const wchar_t *format, + _locale_t locale, va_list valist) +{ + if (options & ~UCRTBASE_PRINTF_MASK) + FIXME("options %#I64x not handled\n", options); + + return vfwprintf_helper((options & UCRTBASE_PRINTF_MASK) | MSVCRT_PRINTF_POSITIONAL_PARAMS + | MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER, file, format, locale, valist); +} + + +/********************************************************************* + * __stdio_common_vfwprintf_s (UCRTBASE.@) + */ +int CDECL __stdio_common_vfwprintf_s(unsigned __int64 options, FILE *file, const wchar_t *format, + _locale_t locale, va_list valist) +{ + if (options & ~UCRTBASE_PRINTF_MASK) + FIXME("options %#I64x not handled\n", options); + + return vfwprintf_helper((options & UCRTBASE_PRINTF_MASK) | MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER, + file, format, locale, valist); +} + +#endif /* _MSVCR_VER >= 140 */ + +/********************************************************************* + * _vfprintf_l (MSVCRT.@) + */ +int CDECL _vfprintf_l(FILE* file, const char *format, + _locale_t locale, va_list valist) +{ + return vfprintf_helper(0, file, format, locale, valist); +} + +/********************************************************************* + * _vfwprintf_l (MSVCRT.@) + */ +int CDECL _vfwprintf_l(FILE* file, const wchar_t *format, + _locale_t locale, va_list valist) +{ + return vfwprintf_helper(0, file, format, locale, valist); +} + +/********************************************************************* + * _vfprintf_p_l (MSVCRT.@) + */ +int CDECL _vfprintf_p_l(FILE* file, const char *format, + _locale_t locale, va_list valist) +{ + return vfprintf_helper(MSVCRT_PRINTF_POSITIONAL_PARAMS | MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER, + file, format, locale, valist); +} + +/********************************************************************* + * _vfprintf_p (MSVCRT.@) + */ +int CDECL _vfprintf_p(FILE* file, const char *format, va_list valist) +{ + return _vfprintf_p_l(file, format, NULL, valist); +} + +/********************************************************************* + * _vfwprintf_p_l (MSVCRT.@) + */ +int CDECL _vfwprintf_p_l(FILE* file, const wchar_t *format, + _locale_t locale, va_list valist) +{ + return vfwprintf_helper(MSVCRT_PRINTF_POSITIONAL_PARAMS | MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER, + file, format, locale, valist); +} + +/********************************************************************* + * _vfwprintf_p (MSVCRT.@) + */ +int CDECL _vfwprintf_p(FILE* file, const wchar_t *format, va_list valist) +{ + return _vfwprintf_p_l(file, format, NULL, valist); +} + +/********************************************************************* + * vprintf (MSVCRT.@) + */ +int CDECL vprintf(const char *format, va_list valist) +{ + return vfprintf(stdout,format,valist); +} + +/********************************************************************* + * vprintf_s (MSVCRT.@) + */ +int CDECL vprintf_s(const char *format, va_list valist) +{ + return vfprintf_s(stdout,format,valist); +} + +/********************************************************************* + * vwprintf (MSVCRT.@) + */ +int CDECL vwprintf(const wchar_t *format, va_list valist) +{ + return vfwprintf(stdout,format,valist); +} + +/********************************************************************* + * vwprintf_s (MSVCRT.@) + */ +int CDECL vwprintf_s(const wchar_t *format, va_list valist) +{ + return vfwprintf_s(stdout,format,valist); +} + +/********************************************************************* + * fprintf (MSVCRT.@) + */ +int WINAPIV fprintf(FILE* file, const char *format, ...) +{ + va_list valist; + int res; + va_start(valist, format); + res = vfprintf(file, format, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * fprintf_s (MSVCRT.@) + */ +int WINAPIV fprintf_s(FILE* file, const char *format, ...) +{ + va_list valist; + int res; + va_start(valist, format); + res = vfprintf_s(file, format, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _fprintf_l (MSVCRT.@) + */ +int WINAPIV _fprintf_l(FILE* file, const char *format, _locale_t locale, ...) +{ + va_list valist; + int res; + va_start(valist, locale); + res = _vfprintf_l(file, format, locale, valist); + va_end(valist); + return res; +} + + +/********************************************************************* + * _fprintf_p (MSVCRT.@) + */ +int WINAPIV _fprintf_p(FILE* file, const char *format, ...) +{ + va_list valist; + int res; + va_start(valist, format); + res = _vfprintf_p_l(file, format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _fprintf_p_l (MSVCRT.@) + */ +int WINAPIV _fprintf_p_l(FILE* file, const char *format, _locale_t locale, ...) +{ + va_list valist; + int res; + va_start(valist, locale); + res = _vfprintf_p_l(file, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _fprintf_s_l (MSVCRT.@) + */ +int WINAPIV _fprintf_s_l(FILE* file, const char *format, _locale_t locale, ...) +{ + va_list valist; + int res; + va_start(valist, locale); + res = _vfprintf_s_l(file, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * fwprintf (MSVCRT.@) + */ +int WINAPIV fwprintf(FILE* file, const wchar_t *format, ...) +{ + va_list valist; + int res; + va_start(valist, format); + res = vfwprintf(file, format, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * fwprintf_s (MSVCRT.@) + */ +int WINAPIV fwprintf_s(FILE* file, const wchar_t *format, ...) +{ + va_list valist; + int res; + va_start(valist, format); + res = vfwprintf_s(file, format, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _fwprintf_l (MSVCRT.@) + */ +int WINAPIV _fwprintf_l(FILE* file, const wchar_t *format, _locale_t locale, ...) +{ + va_list valist; + int res; + va_start(valist, locale); + res = _vfwprintf_l(file, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _fwprintf_p (MSVCRT.@) + */ +int WINAPIV _fwprintf_p(FILE* file, const wchar_t *format, ...) +{ + va_list valist; + int res; + va_start(valist, format); + res = _vfwprintf_p_l(file, format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _fwprintf_p_l (MSVCRT.@) + */ +int WINAPIV _fwprintf_p_l(FILE* file, const wchar_t *format, _locale_t locale, ...) +{ + va_list valist; + int res; + va_start(valist, locale); + res = _vfwprintf_p_l(file, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _fwprintf_s_l (MSVCRT.@) + */ +int WINAPIV _fwprintf_s_l(FILE* file, const wchar_t *format, _locale_t locale, ...) +{ + va_list valist; + int res; + va_start(valist, locale); + res = _vfwprintf_s_l(file, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * printf (MSVCRT.@) + */ +int WINAPIV printf(const char *format, ...) +{ + va_list valist; + int res; + va_start(valist, format); + res = vfprintf(stdout, format, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * printf_s (MSVCRT.@) + */ +int WINAPIV printf_s(const char *format, ...) +{ + va_list valist; + int res; + va_start(valist, format); + res = vprintf_s(format, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * ungetc (MSVCRT.@) + */ +int CDECL ungetc(int c, FILE * file) +{ + int ret; + + if(!MSVCRT_CHECK_PMT(file != NULL)) return EOF; + + _lock_file(file); + ret = _ungetc_nolock(c, file); + _unlock_file(file); + + return ret; +} + +/********************************************************************* + * _ungetc_nolock (MSVCRT.@) + */ +int CDECL _ungetc_nolock(int c, FILE * file) +{ + if(!MSVCRT_CHECK_PMT(file != NULL)) return EOF; + + if (c == EOF || !(file->_flag&_IOREAD || + (file->_flag&_IORW && !(file->_flag&_IOWRT)))) + return EOF; + + if((!(file->_flag & (MSVCRT__NOBUF | _IOMYBUF | MSVCRT__USERBUF)) + && msvcrt_alloc_buffer(file)) + || (!file->_cnt && file->_ptr==file->_base)) + file->_ptr++; + + if(file->_ptr>file->_base) { + file->_ptr--; + if(file->_flag & _IOSTRG) { + if(*file->_ptr != c) { + file->_ptr++; + return EOF; + } + }else { + *file->_ptr = c; + } + file->_cnt++; + file->_flag &= ~(_IOERR | _IOEOF); + file->_flag |= _IOREAD; + return c; + } + + return EOF; +} + +/********************************************************************* + * ungetwc (MSVCRT.@) + */ +wint_t CDECL ungetwc(wint_t wc, FILE * file) +{ + wint_t ret; + + if(!MSVCRT_CHECK_PMT(file != NULL)) return WEOF; + + _lock_file(file); + ret = _ungetwc_nolock(wc, file); + _unlock_file(file); + + return ret; +} + +/********************************************************************* + * _ungetwc_nolock (MSVCRT.@) + */ +wint_t CDECL _ungetwc_nolock(wint_t wc, FILE * file) +{ + wchar_t mwc = wc; + + if(!MSVCRT_CHECK_PMT(file != NULL)) return WEOF; + if (wc == WEOF) + return WEOF; + + if(ioinfo_get_textmode(get_ioinfo_nolock(file->_file)) != TEXTMODE_ANSI + || !(get_ioinfo_nolock(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_nolock(pp[i],file)) + return WEOF; + } + }else { + char mbs[MB_LEN_MAX]; + int len; + + len = wctomb(mbs, mwc); + if(len == -1) + return WEOF; + + for(len--; len>=0; len--) { + if(mbs[len] != _ungetc_nolock(mbs[len], file)) + return WEOF; + } + } + + return mwc; +} + +/********************************************************************* + * wprintf (MSVCRT.@) + */ +int WINAPIV wprintf(const wchar_t *format, ...) +{ + va_list valist; + int res; + va_start(valist, format); + res = vwprintf(format, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * wprintf_s (MSVCRT.@) + */ +int WINAPIV wprintf_s(const wchar_t *format, ...) +{ + va_list valist; + int res; + va_start(valist, format); + res = vwprintf_s(format, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _getmaxstdio (MSVCRT.@) + */ +int CDECL _getmaxstdio(void) +{ + return MSVCRT_max_streams; +} + +/********************************************************************* + * _setmaxstdio (MSVCRT.@) + */ +int CDECL _setmaxstdio(int newmax) +{ + TRACE("%d\n", newmax); + + if(newmax<_IOB_ENTRIES || newmax>MSVCRT_MAX_FILES || newmax= 140 +/********************************************************************* + * _get_stream_buffer_pointers (UCRTBASE.@) + */ +int CDECL _get_stream_buffer_pointers(FILE *file, char*** base, + char*** ptr, int** count) +{ + if (base) + *base = &file->_base; + if (ptr) + *ptr = &file->_ptr; + if (count) + *count = &file->_cnt; + return 0; +} +#endif diff --git a/dll/win32/msvcrt/handler4.c b/dll/win32/msvcrt/handler4.c new file mode 100644 index 00000000000..d6024e710c3 --- /dev/null +++ b/dll/win32/msvcrt/handler4.c @@ -0,0 +1,775 @@ +/* + * C++ exception handling (ver. 4) + * + * Copyright 2020 Piotr Caban + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include + +#if defined(__x86_64__) && _MSVCR_VER>=140 + +#include +#include + +#include "wine/exception.h" +#include "wine/debug.h" +#include "cppexcept.h" +#include "msvcrt.h" + +WINE_DEFAULT_DEBUG_CHANNEL(seh); + +static DWORD fls_index; + +typedef struct +{ + BYTE header; + UINT bbt_flags; + UINT unwind_count; + UINT unwind_map; + UINT tryblock_count; + UINT tryblock_map; + UINT ip_count; + UINT ip_map; + UINT frame; +} cxx_function_descr_v4; +#define FUNC_DESCR_IS_CATCH 0x01 +#define FUNC_DESCR_IS_SEPARATED 0x02 +#define FUNC_DESCR_BBT 0x04 +#define FUNC_DESCR_UNWIND_MAP 0x08 +#define FUNC_DESCR_TRYBLOCK_MAP 0x10 +#define FUNC_DESCR_EHS 0x20 +#define FUNC_DESCR_NO_EXCEPT 0x40 +#define FUNC_DESCR_RESERVED 0x80 + +typedef struct +{ + UINT type; + BYTE *prev; + UINT handler; + UINT object; +} unwind_info_v4; + +typedef struct +{ + BYTE header; + UINT flags; + UINT type_info; + int offset; + UINT handler; + UINT ret_addr[2]; +} catchblock_info_v4; +#define CATCHBLOCK_FLAGS 0x01 +#define CATCHBLOCK_TYPE_INFO 0x02 +#define CATCHBLOCK_OFFSET 0x04 +#define CATCHBLOCK_SEPARATED 0x08 +#define CATCHBLOCK_RET_ADDR_MASK 0x30 +#define CATCHBLOCK_RET_ADDR 0x10 +#define CATCHBLOCK_TWO_RET_ADDRS 0x20 + +#define UNWIND_TYPE_NO_HANDLER 0 +#define UNWIND_TYPE_DTOR_OBJ 1 +#define UNWIND_TYPE_DTOR_PTR 2 +#define UNWIND_TYPE_FRAME 3 + +#define CONSOLIDATE_UNWIND_PARAMETER_COUNT 10 + +typedef struct +{ + cxx_frame_info frame_info; + BOOL rethrow; + INT search_state; + INT unwind_state; + EXCEPTION_RECORD *prev_rec; +} cxx_catch_ctx; + +typedef struct +{ + ULONG64 dest_frame; + ULONG64 orig_frame; + EXCEPTION_RECORD *seh_rec; + DISPATCHER_CONTEXT *dispatch; + const cxx_function_descr_v4 *descr; + int trylevel; +} se_translator_ctx; + +static UINT decode_uint(BYTE **b) +{ + UINT ret; + BYTE *p = *b; + + if ((*p & 1) == 0) + { + ret = p[0] >> 1; + p += 1; + } + else if ((*p & 3) == 1) + { + ret = (p[0] >> 2) + (p[1] << 6); + p += 2; + } + else if ((*p & 7) == 3) + { + ret = (p[0] >> 3) + (p[1] << 5) + (p[2] << 13); + p += 3; + } + else if ((*p & 15) == 7) + { + ret = (p[0] >> 4) + (p[1] << 4) + (p[2] << 12) + (p[3] << 20); + p += 4; + } + else + { + FIXME("not implemented - expect crash\n"); + ret = 0; + p += 5; + } + + *b = p; + return ret; +} + +static UINT read_rva(BYTE **b) +{ + UINT ret = *(UINT*)(*b); + *b += sizeof(UINT); + return ret; +} + +static void read_unwind_info(BYTE **b, unwind_info_v4 *ui) +{ + BYTE *p = *b; + + memset(ui, 0, sizeof(*ui)); + ui->type = decode_uint(b); + ui->prev = p - (ui->type >> 2); + ui->type &= 0x3; + + switch (ui->type) + { + case UNWIND_TYPE_NO_HANDLER: + break; + case UNWIND_TYPE_DTOR_OBJ: + ui->handler = read_rva(b); + ui->object = decode_uint(b); /* frame offset to object */ + break; + case UNWIND_TYPE_DTOR_PTR: + ui->handler = read_rva(b); + ui->object = decode_uint(b); /* frame offset to pointer to object */ + break; + case UNWIND_TYPE_FRAME: + ui->handler = read_rva(b); + break; + } +} + +static void read_tryblock_info(BYTE **b, tryblock_info *ti, ULONG64 image_base) +{ + BYTE *count, *count_end; + + ti->start_level = decode_uint(b); + ti->end_level = decode_uint(b); + ti->catch_level = decode_uint(b); + ti->catchblock = read_rva(b); + + if (ti->catchblock) + { + count = count_end = rtti_rva(ti->catchblock, image_base); + ti->catchblock_count = decode_uint(&count_end); + ti->catchblock += count_end - count; + } + else + { + ti->catchblock_count = 0; + } +} + +static BOOL read_catchblock_info(BYTE **b, catchblock_info_v4 *ci, DWORD func_rva) +{ + BYTE ret_addr_type; + memset(ci, 0, sizeof(*ci)); + ci->header = **b; + (*b)++; + if (ci->header & ~(CATCHBLOCK_FLAGS | CATCHBLOCK_TYPE_INFO | CATCHBLOCK_OFFSET | + CATCHBLOCK_SEPARATED | CATCHBLOCK_RET_ADDR_MASK)) + { + FIXME("unknown header: %x\n", ci->header); + return FALSE; + } + ret_addr_type = ci->header & CATCHBLOCK_RET_ADDR_MASK; + if (ret_addr_type == (CATCHBLOCK_RET_ADDR | CATCHBLOCK_TWO_RET_ADDRS)) + { + FIXME("unsupported ret addr type.\n"); + return FALSE; + } + + if (ci->header & CATCHBLOCK_FLAGS) ci->flags = decode_uint(b); + if (ci->header & CATCHBLOCK_TYPE_INFO) ci->type_info = read_rva(b); + if (ci->header & CATCHBLOCK_OFFSET) ci->offset = decode_uint(b); + ci->handler = read_rva(b); + if (ci->header & CATCHBLOCK_SEPARATED) + { + if (ret_addr_type == CATCHBLOCK_RET_ADDR || ret_addr_type == CATCHBLOCK_TWO_RET_ADDRS) + ci->ret_addr[0] = read_rva(b); + if (ret_addr_type == CATCHBLOCK_TWO_RET_ADDRS) + ci->ret_addr[1] = read_rva(b); + } + else + { + if (ret_addr_type == CATCHBLOCK_RET_ADDR || ret_addr_type == CATCHBLOCK_TWO_RET_ADDRS) + ci->ret_addr[0] = decode_uint(b) + func_rva; + if (ret_addr_type == CATCHBLOCK_TWO_RET_ADDRS) + ci->ret_addr[1] = decode_uint(b) + func_rva; + } + + return TRUE; +} + +static void read_ipmap_info(BYTE **b, ipmap_info *ii) +{ + ii->ip = decode_uint(b); + ii->state = (INT)decode_uint(b) - 1; +} + +static BOOL validate_cxx_function_descr4(const cxx_function_descr_v4 *descr, DISPATCHER_CONTEXT *dispatch) +{ + ULONG64 image_base = dispatch->ImageBase; + BYTE *unwind_map = rtti_rva(descr->unwind_map, image_base); + BYTE *tryblock_map = rtti_rva(descr->tryblock_map, image_base); + BYTE *ip_map = rtti_rva(descr->ip_map, image_base); + UINT i, j; + char *ip; + + TRACE("header 0x%x\n", descr->header); + TRACE("basic block transformations flags: 0x%x\n", descr->bbt_flags); + + TRACE("unwind table: 0x%x(%p) %d\n", descr->unwind_map, unwind_map, descr->unwind_count); + for (i = 0; i < descr->unwind_count; i++) + { + BYTE *entry = unwind_map; + unwind_info_v4 ui; + + read_unwind_info(&unwind_map, &ui); + if (ui.prev < (BYTE*)rtti_rva(descr->unwind_map, image_base)) ui.prev = NULL; + TRACE(" %d (%p): type 0x%x prev %p func 0x%x object 0x%x\n", + i, entry, ui.type, ui.prev, ui.handler, ui.object); + } + + TRACE("try table: 0x%x(%p) %d\n", descr->tryblock_map, tryblock_map, descr->tryblock_count); + for (i = 0; i < descr->tryblock_count; i++) + { + tryblock_info ti; + BYTE *catchblock; + + read_tryblock_info(&tryblock_map, &ti, image_base); + catchblock = rtti_rva(ti.catchblock, image_base); + TRACE(" %d: start %d end %d catchlevel %d catch 0x%x(%p) %d\n", + i, ti.start_level, ti.end_level, ti.catch_level, + ti.catchblock, catchblock, ti.catchblock_count); + for (j = 0; j < ti.catchblock_count; j++) + { + catchblock_info_v4 ci; + if (!read_catchblock_info(&catchblock, &ci, + dispatch->FunctionEntry->BeginAddress)) return FALSE; + TRACE(" %d: header 0x%x offset %d handler 0x%x " + "ret addr[0] %#x ret_addr[1] %#x type %#x %s\n", j, ci.header, ci.offset, + ci.handler, ci.ret_addr[0], ci.ret_addr[1], ci.type_info, + dbgstr_type_info(ci.type_info ? rtti_rva(ci.type_info, image_base) : NULL)); + } + } + + TRACE("ipmap: 0x%x(%p) %d\n", descr->ip_map, ip_map, descr->ip_count); + ip = rtti_rva(dispatch->FunctionEntry->BeginAddress, image_base); + for (i = 0; i < descr->ip_count; i++) + { + ipmap_info ii; + + read_ipmap_info(&ip_map, &ii); + ip += ii.ip; + TRACE(" %d: ip offset 0x%x (%p) state %d\n", i, ii.ip, ip, ii.state); + } + + TRACE("establisher frame: %x\n", descr->frame); + return TRUE; +} + +static inline int ip_to_state4(const cxx_function_descr_v4 *descr, DISPATCHER_CONTEXT *dispatch, ULONG64 ip) +{ + BYTE *ip_map = rtti_rva( descr->ip_map, dispatch->ImageBase ); + ULONG64 state_ip; + ipmap_info ii; + int ret = -1; + UINT i; + + state_ip = dispatch->ImageBase + dispatch->FunctionEntry->BeginAddress; + for (i = 0; i < descr->ip_count; i++) + { + read_ipmap_info(&ip_map, &ii); + state_ip += ii.ip; + if (ip < state_ip) break; + ret = ii.state; + } + + TRACE("%I64x -> state %d\n", ip, ret); + return ret; +} + +static void cxx_local_unwind4(ULONG64 frame, DISPATCHER_CONTEXT *dispatch, + const cxx_function_descr_v4 *descr, int trylevel, int last_level) +{ + void (__cdecl *handler_dtor)(void *obj, ULONG64 frame); + BYTE *unwind_data, *last; + unwind_info_v4 ui; + void *obj; + int i; + + if (trylevel == -2) trylevel = ip_to_state4( descr, dispatch, get_exception_pc(dispatch) ); + + TRACE("current level: %d, last level: %d\n", trylevel, last_level); + + if (trylevel<-1 || trylevel>=(int)descr->unwind_count) + { + ERR("invalid trylevel %d\n", trylevel); + terminate(); + } + + if (trylevel <= last_level) return; + + unwind_data = rtti_rva(descr->unwind_map, dispatch->ImageBase); + last = unwind_data - 1; + for (i = 0; i < trylevel; i++) + { + BYTE *addr = unwind_data; + read_unwind_info(&unwind_data, &ui); + if (i == last_level) last = addr; + } + + while (unwind_data > last) + { + read_unwind_info(&unwind_data, &ui); + unwind_data = ui.prev; + + if (ui.handler) + { + handler_dtor = rtti_rva(ui.handler, dispatch->ImageBase); + obj = (void *)(frame + ui.object); + if(ui.type == UNWIND_TYPE_DTOR_PTR) + obj = *(void**)obj; + TRACE("handler: %p object: %p\n", handler_dtor, obj); + handler_dtor(obj, frame); + } + } +} + +static LONG CALLBACK cxx_rethrow_filter(PEXCEPTION_POINTERS eptrs, void *c) +{ + EXCEPTION_RECORD *rec = eptrs->ExceptionRecord; + cxx_catch_ctx *ctx = c; + + if (rec->ExceptionCode == CXX_EXCEPTION && !rec->ExceptionInformation[1] && !rec->ExceptionInformation[2]) + return EXCEPTION_EXECUTE_HANDLER; + + FlsSetValue(fls_index, (void*)(DWORD_PTR)ctx->search_state); + if (rec->ExceptionCode != CXX_EXCEPTION) + return EXCEPTION_CONTINUE_SEARCH; + if (rec->ExceptionInformation[1] == ctx->prev_rec->ExceptionInformation[1]) + ctx->rethrow = TRUE; + return EXCEPTION_CONTINUE_SEARCH; +} + +static void CALLBACK cxx_catch_cleanup(BOOL normal, void *c) +{ + cxx_catch_ctx *ctx = c; + __CxxUnregisterExceptionObject(&ctx->frame_info, ctx->rethrow); + + FlsSetValue(fls_index, (void*)(DWORD_PTR)ctx->unwind_state); +} + +static void* WINAPI call_catch_block4(EXCEPTION_RECORD *rec) +{ + EXCEPTION_RECORD *untrans_rec = (void*)rec->ExceptionInformation[4]; + EXCEPTION_RECORD *prev_rec = (void*)rec->ExceptionInformation[6]; + CONTEXT *context = (void*)rec->ExceptionInformation[7]; + EXCEPTION_POINTERS ep = { prev_rec, context }; + cxx_catch_ctx ctx; + void *ret_addr = NULL; + + ctx.rethrow = FALSE; + __CxxRegisterExceptionObject(&ep, &ctx.frame_info); + ctx.search_state = rec->ExceptionInformation[2]; + ctx.unwind_state = rec->ExceptionInformation[3]; + ctx.prev_rec = prev_rec; + (*__processing_throw())--; + __TRY + { + __TRY + { + ret_addr = call_catch_handler( rec ); + } + __EXCEPT_CTX(cxx_rethrow_filter, &ctx) + { + TRACE("detect rethrow: exception code: %lx\n", prev_rec->ExceptionCode); + ctx.rethrow = TRUE; + FlsSetValue(fls_index, (void*)(DWORD_PTR)ctx.search_state); + + if (untrans_rec) + { + __DestructExceptionObject(prev_rec); + RaiseException(untrans_rec->ExceptionCode, untrans_rec->ExceptionFlags, + untrans_rec->NumberParameters, untrans_rec->ExceptionInformation); + } + else + { + RaiseException(prev_rec->ExceptionCode, prev_rec->ExceptionFlags, + prev_rec->NumberParameters, prev_rec->ExceptionInformation); + } + } + __ENDTRY + } + __FINALLY_CTX(cxx_catch_cleanup, &ctx) + + FlsSetValue(fls_index, (void*)-2); + TRACE("handler returned %p, ret_addr[0] %#Ix, ret_addr[1] %#Ix.\n", + ret_addr, rec->ExceptionInformation[8], rec->ExceptionInformation[9]); + + if (rec->ExceptionInformation[9]) + { + if ((ULONG_PTR)ret_addr > 1) + { + ERR("unexpected handler result %p.\n", ret_addr); + abort(); + } + return (void*)rec->ExceptionInformation[8 + (ULONG_PTR)ret_addr]; + } + return rec->ExceptionInformation[8] ? (void *)rec->ExceptionInformation[8] : ret_addr; +} + +static inline BOOL cxx_is_consolidate(const EXCEPTION_RECORD *rec) +{ + return rec->ExceptionCode == STATUS_UNWIND_CONSOLIDATE + && rec->NumberParameters == CONSOLIDATE_UNWIND_PARAMETER_COUNT + && rec->ExceptionInformation[0] == (ULONG_PTR)call_catch_block4; +} + +static inline void find_catch_block4(EXCEPTION_RECORD *rec, CONTEXT *context, + EXCEPTION_RECORD *untrans_rec, ULONG64 frame, DISPATCHER_CONTEXT *dispatch, + const cxx_function_descr_v4 *descr, cxx_exception_type *info, + ULONG64 orig_frame, int trylevel) +{ + ULONG64 exc_base = (rec->NumberParameters == 4 ? rec->ExceptionInformation[3] : 0); + int *processing_throw = __processing_throw(); + EXCEPTION_RECORD catch_record; + BYTE *tryblock_map; + CONTEXT ctx; + UINT i, j; + + (*processing_throw)++; + + if (trylevel == -2) trylevel = ip_to_state4( descr, dispatch, get_exception_pc(dispatch) ); + TRACE("current trylevel: %d\n", trylevel); + + tryblock_map = rtti_rva(descr->tryblock_map, dispatch->ImageBase); + for (i=0; itryblock_count; i++) + { + tryblock_info tryblock; + BYTE *catchblock; + + read_tryblock_info(&tryblock_map, &tryblock, dispatch->ImageBase); + + if (trylevel < tryblock.start_level) continue; + if (trylevel > tryblock.end_level) continue; + + /* got a try block */ + catchblock = rtti_rva(tryblock.catchblock, dispatch->ImageBase); + for (j=0; jFunctionEntry->BeginAddress); + + if (info) + { + const type_info *catch_ti = NULL; + const cxx_type_info *type; + + if (ci.type_info) catch_ti = rtti_rva( ci.type_info, dispatch->ImageBase ); + if (!(type = find_caught_type( info, exc_base, catch_ti, ci.flags ))) continue; + + TRACE("matched type %p in tryblock %d catchblock %d\n", type, i, j); + + if (catch_ti && catch_ti->mangled[0] && ci.offset) + { + /* copy the exception to its destination on the stack */ + void **dest = (void **)(orig_frame + ci.offset); + copy_exception( (void *)rec->ExceptionInformation[1], dest, ci.flags, type, exc_base ); + } + } + else + { + /* no CXX_EXCEPTION only proceed with a catch(...) block*/ + if (ci.type_info) + continue; + TRACE("found catch(...) block\n"); + } + + /* unwind stack and call catch */ + memset(&catch_record, 0, sizeof(catch_record)); + catch_record.ExceptionCode = STATUS_UNWIND_CONSOLIDATE; + catch_record.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + catch_record.NumberParameters = CONSOLIDATE_UNWIND_PARAMETER_COUNT; + catch_record.ExceptionInformation[0] = (ULONG_PTR)call_catch_block4; + catch_record.ExceptionInformation[1] = orig_frame; + catch_record.ExceptionInformation[2] = tryblock.catch_level; + catch_record.ExceptionInformation[3] = tryblock.start_level; + catch_record.ExceptionInformation[4] = (ULONG_PTR)untrans_rec; + catch_record.ExceptionInformation[5] = (ULONG_PTR)rtti_rva(ci.handler, dispatch->ImageBase); + /* 3ds Max plugin expect ExceptionInformation[6] to contain exception record */ + catch_record.ExceptionInformation[6] = (ULONG_PTR)rec; + catch_record.ExceptionInformation[7] = (ULONG_PTR)context; + if (ci.ret_addr[0]) + { + catch_record.ExceptionInformation[8] = (ULONG_PTR)rtti_rva( + ci.ret_addr[0], dispatch->ImageBase); + } + if (ci.ret_addr[1]) + { + catch_record.ExceptionInformation[9] = (ULONG_PTR)rtti_rva( + ci.ret_addr[1], dispatch->ImageBase); + } + RtlUnwindEx((void*)frame, (void*)dispatch->ControlPc, &catch_record, NULL, &ctx, NULL); + } + } + + TRACE("no matching catch block found\n"); + (*processing_throw)--; +} + +static LONG CALLBACK se_translation_filter(EXCEPTION_POINTERS *ep, void *c) +{ + se_translator_ctx *ctx = (se_translator_ctx *)c; + EXCEPTION_RECORD *rec = ep->ExceptionRecord; + cxx_exception_type *exc_type; + + if (rec->ExceptionCode != CXX_EXCEPTION) + { + TRACE("non-c++ exception thrown in SEH handler: %lx\n", rec->ExceptionCode); + terminate(); + } + + exc_type = (cxx_exception_type *)rec->ExceptionInformation[2]; + find_catch_block4(rec, ep->ContextRecord, ctx->seh_rec, ctx->dest_frame, ctx->dispatch, + ctx->descr, exc_type, ctx->orig_frame, ctx->trylevel); + + __DestructExceptionObject(rec); + return ExceptionContinueSearch; +} + +/* Hacky way to obtain se_translator */ +static inline _se_translator_function get_se_translator(void) +{ + return __current_exception()[-2]; +} + +static void check_noexcept( PEXCEPTION_RECORD rec, const cxx_function_descr_v4 *descr ) +{ + if (!(descr->header & FUNC_DESCR_IS_CATCH) && + rec->ExceptionCode == CXX_EXCEPTION && + (descr->header & FUNC_DESCR_NO_EXCEPT)) + { + ERR("noexcept function propagating exception\n"); + terminate(); + } +} + +static DWORD cxx_frame_handler4(EXCEPTION_RECORD *rec, ULONG64 frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatch, + const cxx_function_descr_v4 *descr, int trylevel) +{ + cxx_exception_type *exc_type; + ULONG64 orig_frame = frame; + + if (descr->header & FUNC_DESCR_IS_CATCH) + { + TRACE("nested exception detected\n"); + orig_frame = *(ULONG64 *)(frame + descr->frame); + TRACE("setting orig_frame to %Ix\n", orig_frame); + } + + if (rec->ExceptionFlags & (EXCEPTION_UNWINDING|EXCEPTION_EXIT_UNWIND)) + { + int last_level = -1; + if ((rec->ExceptionFlags & EXCEPTION_TARGET_UNWIND) && cxx_is_consolidate(rec)) + last_level = rec->ExceptionInformation[3]; + else if ((rec->ExceptionFlags & EXCEPTION_TARGET_UNWIND) && rec->ExceptionCode == STATUS_LONGJUMP) + last_level = ip_to_state4( descr, dispatch, dispatch->TargetIp ); + + cxx_local_unwind4(orig_frame, dispatch, descr, trylevel, last_level); + return ExceptionContinueSearch; + } + if (!descr->tryblock_map) + { + check_noexcept(rec, descr); + return ExceptionContinueSearch; + } + + if (rec->ExceptionCode == CXX_EXCEPTION) + { + if (!rec->ExceptionInformation[1] && !rec->ExceptionInformation[2]) + { + TRACE("rethrow detected.\n"); + *rec = *(EXCEPTION_RECORD*)*__current_exception(); + } + + exc_type = (cxx_exception_type *)rec->ExceptionInformation[2]; + + if (TRACE_ON(seh)) + { + TRACE("handling C++ exception rec %p frame %Ix descr %p\n", rec, frame, descr); + TRACE_EXCEPTION_TYPE(exc_type, rec->ExceptionInformation[3]); + } + } + else + { + _se_translator_function se_translator = get_se_translator(); + + exc_type = NULL; + TRACE("handling C exception code %lx rec %p frame %Ix descr %p\n", + rec->ExceptionCode, rec, frame, descr); + + if (se_translator) { + EXCEPTION_POINTERS except_ptrs; + se_translator_ctx ctx; + + ctx.dest_frame = frame; + ctx.orig_frame = orig_frame; + ctx.seh_rec = rec; + ctx.dispatch = dispatch; + ctx.descr = descr; + ctx.trylevel = trylevel; + __TRY + { + except_ptrs.ExceptionRecord = rec; + except_ptrs.ContextRecord = context; + se_translator(rec->ExceptionCode, &except_ptrs); + } + __EXCEPT_CTX(se_translation_filter, &ctx) + { + } + __ENDTRY + } + } + + find_catch_block4(rec, context, NULL, frame, dispatch, descr, exc_type, orig_frame, trylevel); + check_noexcept(rec, descr); + return ExceptionContinueSearch; +} + +EXCEPTION_DISPOSITION __cdecl __CxxFrameHandler4(EXCEPTION_RECORD *rec, + ULONG64 frame, CONTEXT *context, DISPATCHER_CONTEXT *dispatch) +{ + cxx_function_descr_v4 descr; + BYTE *p, *count, *count_end; + int trylevel; + + TRACE("%p %Ix %p %p\n", rec, frame, context, dispatch); + + trylevel = (DWORD_PTR)FlsGetValue(fls_index); + FlsSetValue(fls_index, (void*)-2); + + memset(&descr, 0, sizeof(descr)); + p = rtti_rva(*(UINT*)dispatch->HandlerData, dispatch->ImageBase); + descr.header = *p++; + + if ((descr.header & FUNC_DESCR_EHS) && + rec->ExceptionCode != CXX_EXCEPTION && + !cxx_is_consolidate(rec) && + rec->ExceptionCode != STATUS_LONGJUMP) + return ExceptionContinueSearch; /* handle only c++ exceptions */ + + if (descr.header & ~(FUNC_DESCR_IS_CATCH | FUNC_DESCR_IS_SEPARATED | + FUNC_DESCR_UNWIND_MAP | FUNC_DESCR_TRYBLOCK_MAP | FUNC_DESCR_EHS | + FUNC_DESCR_NO_EXCEPT)) + { + FIXME("unsupported flags: %x\n", descr.header); + return ExceptionContinueSearch; + } + + if (descr.header & FUNC_DESCR_BBT) descr.bbt_flags = decode_uint(&p); + if (descr.header & FUNC_DESCR_UNWIND_MAP) + { + descr.unwind_map = read_rva(&p); + count_end = count = rtti_rva(descr.unwind_map, dispatch->ImageBase); + descr.unwind_count = decode_uint(&count_end); + descr.unwind_map += count_end - count; + } + if (descr.header & FUNC_DESCR_TRYBLOCK_MAP) + { + descr.tryblock_map = read_rva(&p); + count_end = count = rtti_rva(descr.tryblock_map, dispatch->ImageBase); + descr.tryblock_count = decode_uint(&count_end); + descr.tryblock_map += count_end - count; + } + descr.ip_map = read_rva(&p); + if (descr.header & FUNC_DESCR_IS_SEPARATED) + { + UINT i, num, func; + BYTE *map = rtti_rva(descr.ip_map, dispatch->ImageBase); + + num = decode_uint(&map); + for (i = 0; i < num; i++) + { + func = read_rva(&map); + descr.ip_map = read_rva(&map); + if (func == dispatch->FunctionEntry->BeginAddress) + break; + } + if (i == num) + { + FIXME("function ip_map not found\n"); + return ExceptionContinueSearch; + } + } + count_end = count = rtti_rva(descr.ip_map, dispatch->ImageBase); + descr.ip_count = decode_uint(&count_end); + descr.ip_map += count_end - count; + if (descr.header & FUNC_DESCR_IS_CATCH) descr.frame = decode_uint(&p); + + if (!validate_cxx_function_descr4(&descr, dispatch)) + return ExceptionContinueSearch; + + return cxx_frame_handler4(rec, frame, context, dispatch, &descr, trylevel); +} + +BOOL msvcrt_init_handler4(void) +{ + fls_index = FlsAlloc(NULL); + if (fls_index == FLS_OUT_OF_INDEXES) + return FALSE; + msvcrt_attach_handler4(); + return TRUE; +} + +void msvcrt_attach_handler4(void) +{ + FlsSetValue(fls_index, (void*)-2); +} + +void msvcrt_free_handler4(void) +{ + FlsFree(fls_index); +} + +#endif diff --git a/dll/win32/msvcrt/heap.c b/dll/win32/msvcrt/heap.c new file mode 100644 index 00000000000..70e4ecb815c --- /dev/null +++ b/dll/win32/msvcrt/heap.c @@ -0,0 +1,847 @@ +/* + * msvcrt.dll heap functions + * + * Copyright 2000 Jon Griffiths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + * + * Note: Win32 heap operations are MT safe. We only lock the new + * handler and non atomic heap operations + */ + +#include +#include "msvcrt.h" +#include "mtdll.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +/* MT */ +#define LOCK_HEAP _lock( _HEAP_LOCK ) +#define UNLOCK_HEAP _unlock( _HEAP_LOCK ) + +/* _aligned */ +#define SAVED_PTR(x) ((void *)((DWORD_PTR)((char *)x - sizeof(void *)) & \ + ~(sizeof(void *) - 1))) +#define ALIGN_PTR(ptr, alignment, offset) ((void *) \ + ((((DWORD_PTR)((char *)ptr + alignment + sizeof(void *) + offset)) & \ + ~(alignment - 1)) - offset)) + +#define SB_HEAP_ALIGN 16 + +static HANDLE heap, sb_heap; + +typedef int (CDECL *MSVCRT_new_handler_func)(size_t size); + +static MSVCRT_new_handler_func MSVCRT_new_handler; +static LONG MSVCRT_new_mode; + +/* FIXME - According to documentation it should be 8*1024, at runtime it returns 16 */ +static unsigned int MSVCRT_amblksiz = 16; +/* FIXME - According to documentation it should be 480 bytes, at runtime default is 0 */ +static size_t MSVCRT_sbh_threshold = 0; + +static void* msvcrt_heap_alloc(DWORD flags, size_t size) +{ + if(size < MSVCRT_sbh_threshold) + { + void *memblock, *temp, **saved; + + temp = HeapAlloc(sb_heap, flags, size+sizeof(void*)+SB_HEAP_ALIGN); + if(!temp) return NULL; + + memblock = ALIGN_PTR(temp, SB_HEAP_ALIGN, 0); + saved = SAVED_PTR(memblock); + *saved = temp; + return memblock; + } + + return HeapAlloc(heap, flags, size); +} + +static void* msvcrt_heap_realloc(DWORD flags, void *ptr, size_t size) +{ + if(sb_heap && ptr && !HeapValidate(heap, 0, ptr)) + { + /* TODO: move data to normal heap if it exceeds sbh_threshold limit */ + void *memblock, *temp, **saved; + size_t old_padding, new_padding, old_size; + + saved = SAVED_PTR(ptr); + old_padding = (char*)ptr - (char*)*saved; + old_size = HeapSize(sb_heap, 0, *saved); + if(old_size == -1) + return NULL; + old_size -= old_padding; + + temp = HeapReAlloc(sb_heap, flags, *saved, size+sizeof(void*)+SB_HEAP_ALIGN); + if(!temp) return NULL; + + memblock = ALIGN_PTR(temp, SB_HEAP_ALIGN, 0); + saved = SAVED_PTR(memblock); + new_padding = (char*)memblock - (char*)temp; + + if(new_padding != old_padding) + memmove(memblock, (char*)temp+old_padding, old_size>size ? size : old_size); + + *saved = temp; + return memblock; + } + + return HeapReAlloc(heap, flags, ptr, size); +} + +static BOOL msvcrt_heap_free(void *ptr) +{ + if(sb_heap && ptr && !HeapValidate(heap, 0, ptr)) + { + void **saved = SAVED_PTR(ptr); + return HeapFree(sb_heap, 0, *saved); + } + + return HeapFree(heap, 0, ptr); +} + +static size_t msvcrt_heap_size(void *ptr) +{ + if(sb_heap && ptr && !HeapValidate(heap, 0, ptr)) + { + void **saved = SAVED_PTR(ptr); + return HeapSize(sb_heap, 0, *saved); + } + + return HeapSize(heap, 0, ptr); +} + +/********************************************************************* + * _callnewh (MSVCRT.@) + */ +int CDECL _callnewh(size_t size) +{ + int ret = 0; + MSVCRT_new_handler_func handler = MSVCRT_new_handler; + if(handler) + ret = (*handler)(size) ? 1 : 0; + return ret; +} + +/********************************************************************* + * ??2@YAPAXI@Z (MSVCRT.@) + */ +void* CDECL DECLSPEC_HOTPATCH operator_new(size_t size) +{ + void *retval; + + do + { + retval = msvcrt_heap_alloc(0, size); + if(retval) + { + TRACE("(%Iu) returning %p\n", size, retval); + return retval; + } + } while(_callnewh(size)); + + TRACE("(%Iu) out of memory\n", size); +#if _MSVCR_VER >= 80 + throw_bad_alloc(); +#endif + return NULL; +} + + +/********************************************************************* + * ??2@YAPAXIHPBDH@Z (MSVCRT.@) + */ +void* CDECL operator_new_dbg(size_t size, int type, const char *file, int line) +{ + return operator_new( size ); +} + + +/********************************************************************* + * ??3@YAXPAX@Z (MSVCRT.@) + */ +void CDECL DECLSPEC_HOTPATCH operator_delete(void *mem) +{ + TRACE("(%p)\n", mem); + msvcrt_heap_free(mem); +} + + +/********************************************************************* + * ?_query_new_handler@@YAP6AHI@ZXZ (MSVCRT.@) + */ +MSVCRT_new_handler_func CDECL _query_new_handler(void) +{ + return MSVCRT_new_handler; +} + + +/********************************************************************* + * ?_query_new_mode@@YAHXZ (MSVCRT.@) + */ +int CDECL _query_new_mode(void) +{ + return MSVCRT_new_mode; +} + +/********************************************************************* + * ?_set_new_handler@@YAP6AHI@ZP6AHI@Z@Z (MSVCRT.@) + */ +MSVCRT_new_handler_func CDECL _set_new_handler(MSVCRT_new_handler_func func) +{ + MSVCRT_new_handler_func old_handler; + LOCK_HEAP; + old_handler = MSVCRT_new_handler; + MSVCRT_new_handler = func; + UNLOCK_HEAP; + return old_handler; +} + +/********************************************************************* + * ?set_new_handler@@YAP6AXXZP6AXXZ@Z (MSVCRT.@) + */ +MSVCRT_new_handler_func CDECL set_new_handler(void *func) +{ + TRACE("(%p)\n",func); + _set_new_handler(NULL); + return NULL; +} + +/********************************************************************* + * ?_set_new_mode@@YAHH@Z (MSVCRT.@) + */ +int CDECL _set_new_mode(int mode) +{ + if(!MSVCRT_CHECK_PMT(mode == 0 || mode == 1)) return -1; + return InterlockedExchange(&MSVCRT_new_mode, mode); +} + +/********************************************************************* + * _expand (MSVCRT.@) + */ +void* CDECL _expand(void* mem, size_t size) +{ + return msvcrt_heap_realloc(HEAP_REALLOC_IN_PLACE_ONLY, mem, size); +} + +/********************************************************************* + * _heapchk (MSVCRT.@) + */ +int CDECL _heapchk(void) +{ + if (!HeapValidate(heap, 0, NULL) || + (sb_heap && !HeapValidate(sb_heap, 0, NULL))) + { + msvcrt_set_errno(GetLastError()); + return _HEAPBADNODE; + } + return _HEAPOK; +} + +/********************************************************************* + * _heapmin (MSVCRT.@) + */ +int CDECL _heapmin(void) +{ + if (!HeapCompact( heap, 0 ) || + (sb_heap && !HeapCompact( sb_heap, 0 ))) + { + if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) + msvcrt_set_errno(GetLastError()); + return -1; + } + return 0; +} + +/********************************************************************* + * _heapwalk (MSVCRT.@) + */ +int CDECL _heapwalk(_HEAPINFO *next) +{ + PROCESS_HEAP_ENTRY phe; + + if (sb_heap) + FIXME("small blocks heap not supported\n"); + + LOCK_HEAP; + phe.lpData = next->_pentry; + phe.cbData = next->_size; + phe.wFlags = next->_useflag == _USEDENTRY ? PROCESS_HEAP_ENTRY_BUSY : 0; + + if (phe.lpData && phe.wFlags & PROCESS_HEAP_ENTRY_BUSY && + !HeapValidate( heap, 0, phe.lpData )) + { + UNLOCK_HEAP; + msvcrt_set_errno(GetLastError()); + return _HEAPBADNODE; + } + + do + { + if (!HeapWalk( heap, &phe )) + { + UNLOCK_HEAP; + if (GetLastError() == ERROR_NO_MORE_ITEMS) + return _HEAPEND; + msvcrt_set_errno(GetLastError()); + if (!phe.lpData) + return _HEAPBADBEGIN; + return _HEAPBADNODE; + } + } while (phe.wFlags & (PROCESS_HEAP_REGION|PROCESS_HEAP_UNCOMMITTED_RANGE)); + + UNLOCK_HEAP; + next->_pentry = phe.lpData; + next->_size = phe.cbData; + next->_useflag = phe.wFlags & PROCESS_HEAP_ENTRY_BUSY ? _USEDENTRY : _FREEENTRY; + return _HEAPOK; +} + +/********************************************************************* + * _heapset (MSVCRT.@) + */ +int CDECL _heapset(unsigned int value) +{ + int retval; + _HEAPINFO heap; + + memset( &heap, 0, sizeof(heap) ); + LOCK_HEAP; + while ((retval = _heapwalk(&heap)) == _HEAPOK) + { + if (heap._useflag == _FREEENTRY) + memset(heap._pentry, value, heap._size); + } + UNLOCK_HEAP; + return retval == _HEAPEND ? _HEAPOK : retval; +} + +/********************************************************************* + * _heapadd (MSVCRT.@) + */ +int CDECL _heapadd(void* mem, size_t size) +{ + TRACE("(%p,%Iu) unsupported in Win32\n", mem,size); + *_errno() = ENOSYS; + return -1; +} + +/********************************************************************* + * _get_heap_handle (MSVCRT.@) + */ +intptr_t CDECL _get_heap_handle(void) +{ + return (intptr_t)heap; +} + +/********************************************************************* + * _msize (MSVCRT.@) + */ +size_t CDECL _msize(void* mem) +{ + size_t size = msvcrt_heap_size(mem); + if (size == ~(size_t)0) + { + WARN(":Probably called with non wine-allocated memory, ret = -1\n"); + /* At least the Win32 crtdll/msvcrt also return -1 in this case */ + } + return size; +} + +#if _MSVCR_VER>=80 +/********************************************************************* + * _aligned_msize (MSVCR80.@) + */ +size_t CDECL _aligned_msize(void *p, size_t alignment, size_t offset) +{ + void **alloc_ptr; + + if(!MSVCRT_CHECK_PMT(p)) return -1; + + if(alignment < sizeof(void*)) + alignment = sizeof(void*); + + alloc_ptr = SAVED_PTR(p); + return _msize(*alloc_ptr)-alignment-sizeof(void*); +} +#endif + +/********************************************************************* + * calloc (MSVCRT.@) + */ +void* CDECL DECLSPEC_HOTPATCH calloc(size_t count, size_t size) +{ + size_t bytes = count*size; + + if (size && bytes / size != count) + { + *_errno() = ENOMEM; + return NULL; + } + + return msvcrt_heap_alloc(HEAP_ZERO_MEMORY, bytes); +} + +#if _MSVCR_VER>=140 +/********************************************************************* + * _calloc_base (UCRTBASE.@) + */ +void* CDECL _calloc_base(size_t count, size_t size) +{ + return calloc(count, size); +} +#endif + +/********************************************************************* + * free (MSVCRT.@) + */ +void CDECL DECLSPEC_HOTPATCH free(void* ptr) +{ + msvcrt_heap_free(ptr); +} + +#if _MSVCR_VER>=140 +/********************************************************************* + * _free_base (UCRTBASE.@) + */ +void CDECL _free_base(void* ptr) +{ + msvcrt_heap_free(ptr); +} +#endif + +/********************************************************************* + * malloc (MSVCRT.@) + */ +void* CDECL malloc(size_t size) +{ + void *ret; + + do + { + ret = msvcrt_heap_alloc(0, size); + if (ret || !MSVCRT_new_mode) + break; + } while(_callnewh(size)); + + if (!ret) + *_errno() = ENOMEM; + return ret; +} + +#if _MSVCR_VER>=140 +/********************************************************************* + * _malloc_base (UCRTBASE.@) + */ +void* CDECL _malloc_base(size_t size) +{ + return malloc(size); +} +#endif + +/********************************************************************* + * realloc (MSVCRT.@) + */ +void* CDECL DECLSPEC_HOTPATCH realloc(void* ptr, size_t size) +{ + if (!ptr) return malloc(size); + if (size) return msvcrt_heap_realloc(0, ptr, size); + free(ptr); + return NULL; +} + +#if _MSVCR_VER>=140 +/********************************************************************* + * _realloc_base (UCRTBASE.@) + */ +void* CDECL _realloc_base(void* ptr, size_t size) +{ + return realloc(ptr, size); +} +#endif + +#if _MSVCR_VER>=80 +/********************************************************************* + * _recalloc (MSVCR80.@) + */ +void* CDECL _recalloc(void *mem, size_t num, size_t size) +{ + size_t old_size; + void *ret; + + if(!mem) + return calloc(num, size); + + size = num*size; + old_size = _msize(mem); + + ret = realloc(mem, size); + if(!ret) { + *_errno() = ENOMEM; + return NULL; + } + + if(size>old_size) + memset((BYTE*)ret+old_size, 0, size-old_size); + return ret; +} +#endif + +/********************************************************************* + * __p__amblksiz (MSVCRT.@) + */ +unsigned int* CDECL __p__amblksiz(void) +{ + return &MSVCRT_amblksiz; +} + +/********************************************************************* + * _get_sbh_threshold (MSVCRT.@) + */ +size_t CDECL _get_sbh_threshold(void) +{ + return MSVCRT_sbh_threshold; +} + +/********************************************************************* + * _set_sbh_threshold (MSVCRT.@) + */ +int CDECL _set_sbh_threshold(size_t threshold) +{ +#ifdef _WIN64 + return 0; +#else + if(threshold > 1016) + return 0; + + if(!sb_heap) + { + sb_heap = HeapCreate(0, 0, 0); + if(!sb_heap) + return 0; + } + + MSVCRT_sbh_threshold = (threshold+0xf) & ~0xf; + return 1; +#endif +} + +/********************************************************************* + * _aligned_free (MSVCRT.@) + */ +void CDECL _aligned_free(void *memblock) +{ + TRACE("(%p)\n", memblock); + + if (memblock) + { + void **saved = SAVED_PTR(memblock); + free(*saved); + } +} + +/********************************************************************* + * _aligned_offset_malloc (MSVCRT.@) + */ +void * CDECL _aligned_offset_malloc(size_t size, size_t alignment, size_t offset) +{ + void *memblock, *temp, **saved; + TRACE("(%Iu, %Iu, %Iu)\n", size, alignment, offset); + + /* alignment must be a power of 2 */ + if ((alignment & (alignment - 1)) != 0) + { + *_errno() = EINVAL; + return NULL; + } + + /* offset must be less than size */ + if (offset && offset >= size) + { + *_errno() = EINVAL; + return NULL; + } + + /* don't align to less than void pointer size */ + if (alignment < sizeof(void *)) + alignment = sizeof(void *); + + /* allocate enough space for void pointer and alignment */ + temp = malloc(size + alignment + sizeof(void *)); + + if (!temp) + return NULL; + + /* adjust pointer for proper alignment and offset */ + memblock = ALIGN_PTR(temp, alignment, offset); + + /* Save the real allocation address below returned address */ + /* so it can be found later to free. */ + saved = SAVED_PTR(memblock); + *saved = temp; + + return memblock; +} + +/********************************************************************* + * _aligned_malloc (MSVCRT.@) + */ +void * CDECL _aligned_malloc(size_t size, size_t alignment) +{ + TRACE("(%Iu, %Iu)\n", size, alignment); + return _aligned_offset_malloc(size, alignment, 0); +} + +/********************************************************************* + * _aligned_offset_realloc (MSVCRT.@) + */ +void * CDECL _aligned_offset_realloc(void *memblock, size_t size, + size_t alignment, size_t offset) +{ + void * temp, **saved; + size_t old_padding, new_padding, old_size; + TRACE("(%p, %Iu, %Iu, %Iu)\n", memblock, size, alignment, offset); + + if (!memblock) + return _aligned_offset_malloc(size, alignment, offset); + + /* alignment must be a power of 2 */ + if ((alignment & (alignment - 1)) != 0) + { + *_errno() = EINVAL; + return NULL; + } + + /* offset must be less than size */ + if (offset >= size) + { + *_errno() = EINVAL; + return NULL; + } + + if (size == 0) + { + _aligned_free(memblock); + return NULL; + } + + /* don't align to less than void pointer size */ + if (alignment < sizeof(void *)) + alignment = sizeof(void *); + + /* make sure alignment and offset didn't change */ + saved = SAVED_PTR(memblock); + if (memblock != ALIGN_PTR(*saved, alignment, offset)) + { + *_errno() = EINVAL; + return NULL; + } + + old_padding = (char *)memblock - (char *)*saved; + + /* Get previous size of block */ + old_size = _msize(*saved); + if (old_size == -1) + { + /* It seems this function was called with an invalid pointer. Bail out. */ + return NULL; + } + + /* Adjust old_size to get amount of actual data in old block. */ + if (old_size < old_padding) + { + /* Shouldn't happen. Something's weird, so bail out. */ + return NULL; + } + old_size -= old_padding; + + temp = realloc(*saved, size + alignment + sizeof(void *)); + + if (!temp) + return NULL; + + /* adjust pointer for proper alignment and offset */ + memblock = ALIGN_PTR(temp, alignment, offset); + + /* Save the real allocation address below returned address */ + /* so it can be found later to free. */ + saved = SAVED_PTR(memblock); + + new_padding = (char *)memblock - (char *)temp; + +/* + Memory layout of old block is as follows: + +-------+---------------------+-+--------------------------+-----------+ + | ... | "old_padding" bytes | | ... "old_size" bytes ... | ... | + +-------+---------------------+-+--------------------------+-----------+ + ^ ^ ^ + | | | + *saved saved memblock + + Memory layout of new block is as follows: + +-------+-----------------------------+-+----------------------+-------+ + | ... | "new_padding" bytes | | ... "size" bytes ... | ... | + +-------+-----------------------------+-+----------------------+-------+ + ^ ^ ^ + | | | + temp saved memblock + + However, in the new block, actual data is still written as follows + (because it was copied by realloc): + +-------+---------------------+--------------------------------+-------+ + | ... | "old_padding" bytes | ... "old_size" bytes ... | ... | + +-------+---------------------+--------------------------------+-------+ + ^ ^ ^ + | | | + temp saved memblock + + Therefore, min(old_size,size) bytes of actual data have to be moved + from the offset they were at in the old block (temp + old_padding), + to the offset they have to be in the new block (temp + new_padding == memblock). +*/ + if (new_padding != old_padding) + memmove((char *)memblock, (char *)temp + old_padding, (old_size < size) ? old_size : size); + + *saved = temp; + + return memblock; +} + +/********************************************************************* + * _aligned_realloc (MSVCRT.@) + */ +void * CDECL _aligned_realloc(void *memblock, size_t size, size_t alignment) +{ + TRACE("(%p, %Iu, %Iu)\n", memblock, size, alignment); + return _aligned_offset_realloc(memblock, size, alignment, 0); +} + +/********************************************************************* + * memmove_s (MSVCRT.@) + */ +int CDECL memmove_s(void *dest, size_t numberOfElements, const void *src, size_t count) +{ + TRACE("(%p %Iu %p %Iu)\n", dest, numberOfElements, src, count); + + if(!count) + return 0; + + if (!MSVCRT_CHECK_PMT(dest != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(src != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT_ERR( count <= numberOfElements, ERANGE )) return ERANGE; + + memmove(dest, src, count); + return 0; +} + +#if _MSVCR_VER>=100 +/********************************************************************* + * wmemmove_s (MSVCR100.@) + */ +int CDECL wmemmove_s(wchar_t *dest, size_t numberOfElements, + const wchar_t *src, size_t count) +{ + TRACE("(%p %Iu %p %Iu)\n", dest, numberOfElements, src, count); + + if (!count) + return 0; + + /* Native does not seem to conform to 6.7.1.2.3 in + * http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1225.pdf + * in that it does not zero the output buffer on constraint violation. + */ + if (!MSVCRT_CHECK_PMT(dest != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(src != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT_ERR(count <= numberOfElements, ERANGE)) return ERANGE; + + memmove(dest, src, sizeof(wchar_t)*count); + return 0; +} +#endif + +/********************************************************************* + * memcpy_s (MSVCRT.@) + */ +int CDECL memcpy_s(void *dest, size_t numberOfElements, const void *src, size_t count) +{ + TRACE("(%p %Iu %p %Iu)\n", dest, numberOfElements, src, count); + + if(!count) + return 0; + + if (!MSVCRT_CHECK_PMT(dest != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(src != NULL)) + { + memset(dest, 0, numberOfElements); + return EINVAL; + } + if (!MSVCRT_CHECK_PMT_ERR( count <= numberOfElements, ERANGE )) + { + memset(dest, 0, numberOfElements); + return ERANGE; + } + + memmove(dest, src, count); + return 0; +} + +#if _MSVCR_VER>=100 +/********************************************************************* + * wmemcpy_s (MSVCR100.@) + */ +int CDECL wmemcpy_s(wchar_t *dest, size_t numberOfElements, + const wchar_t *src, size_t count) +{ + TRACE("(%p %Iu %p %Iu)\n", dest, numberOfElements, src, count); + + if (!count) + return 0; + + if (!MSVCRT_CHECK_PMT(dest != NULL)) return EINVAL; + + if (!MSVCRT_CHECK_PMT(src != NULL)) { + memset(dest, 0, numberOfElements*sizeof(wchar_t)); + return EINVAL; + } + if (!MSVCRT_CHECK_PMT_ERR(count <= numberOfElements, ERANGE)) { + memset(dest, 0, numberOfElements*sizeof(wchar_t)); + return ERANGE; + } + + memmove(dest, src, sizeof(wchar_t)*count); + return 0; +} +#endif + +BOOL msvcrt_init_heap(void) +{ +#if _MSVCR_VER <= 100 + heap = HeapCreate(0, 0, 0); +#else + heap = GetProcessHeap(); +#endif + return heap != NULL; +} + +void msvcrt_destroy_heap(void) +{ +#if _MSVCR_VER <= 100 + HeapDestroy(heap); +#endif + if(sb_heap) + HeapDestroy(sb_heap); +} diff --git a/dll/win32/msvcrt/iob.c b/dll/win32/msvcrt/iob.c new file mode 100644 index 00000000000..4d8456867a8 --- /dev/null +++ b/dll/win32/msvcrt/iob.c @@ -0,0 +1,39 @@ +/* + * acrt function needed for compatibility with mingw + * + * Copyright 2019 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +/* this function is part of the import lib for compatibility with ucrt runtime */ +#if 0 +#pragma makedep implib +#endif + +#include +#include + +#undef __iob_func +extern FILE * __cdecl __iob_func(void); + +/********************************************************************* + * __acrt_iob_func(UCRTBASE.@) + */ +FILE * __cdecl __acrt_iob_func(unsigned idx) +{ + return __iob_func() + idx; +} +__ASM_GLOBAL_IMPORT(__acrt_iob_func) diff --git a/dll/win32/msvcrt/locale.c b/dll/win32/msvcrt/locale.c new file mode 100644 index 00000000000..85714a724ab --- /dev/null +++ b/dll/win32/msvcrt/locale.c @@ -0,0 +1,2186 @@ +/* + * msvcrt.dll locale functions + * + * Copyright 2000 Jon Griffiths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winnls.h" + +#include "msvcrt.h" +#include "mtdll.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +#define MAX_ELEM_LEN 64 /* Max length of country/language/CP string */ +#define MAX_LOCALE_LENGTH 256 +_locale_t MSVCRT_locale = NULL; +unsigned short *MSVCRT__pctype = NULL; +unsigned int MSVCRT___lc_codepage = 0; +int MSVCRT___lc_collate_cp = 0; +LCID MSVCRT___lc_handle[LC_MAX - LC_MIN + 1] = { 0 }; +int MSVCRT___mb_cur_max = 1; +BOOL initial_locale = TRUE; + +#define MSVCRT_LEADBYTE 0x8000 +#define MSVCRT_C1_DEFINED 0x200 + +#if _MSVCR_VER >= 110 +#define LCID_CONVERSION_FLAGS LOCALE_ALLOW_NEUTRAL_NAMES +#else +#define LCID_CONVERSION_FLAGS 0 +#endif + +__lc_time_data cloc_time_data = +{ + {{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + "January", "February", "March", "April", "May", "June", "July", + "August", "September", "October", "November", "December", + "AM", "PM", "MM/dd/yy", "dddd, MMMM dd, yyyy", "HH:mm:ss"}}, +#if _MSVCR_VER < 110 + MAKELCID(LANG_ENGLISH, SORT_DEFAULT), +#endif + 1, -1, +#if _MSVCR_VER == 0 || _MSVCR_VER >= 100 + {{L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat", + L"Sunday", L"Monday", L"Tuesday", L"Wednesday", L"Thursday", L"Friday", L"Saturday", + L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec", + L"January", L"February", L"March", L"April", L"May", L"June", L"July", + L"August", L"September", L"October", L"November", L"December", + L"AM", L"PM", L"MM/dd/yy", L"dddd, MMMM dd, yyyy", L"HH:mm:ss"}}, +#endif +#if _MSVCR_VER >= 110 + L"en-US", +#endif +}; + +static const unsigned char cloc_clmap[256] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +static const unsigned char cloc_cumap[256] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +static char empty[] = ""; +static char cloc_dec_point[] = "."; +#if _MSVCR_VER >= 100 +static wchar_t emptyW[] = L""; +static wchar_t cloc_dec_pointW[] = L"."; +#endif +static struct lconv cloc_lconv = +{ + cloc_dec_point, empty, empty, empty, empty, empty, empty, empty, empty, empty, + CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, +#if _MSVCR_VER >= 100 + cloc_dec_pointW, emptyW, emptyW, emptyW, emptyW, emptyW, emptyW, emptyW +#endif +}; + +/* Friendly country strings & language names abbreviations. */ +static const char * const _country_synonyms[] = +{ + "american", "en", + "american english", "en-US", + "american-english", "en-US", + "english-american", "en-US", + "english-us", "en-US", + "english-usa", "en-US", + "us", "en-US", + "usa", "en-US", + "australian", "en-AU", + "english-aus", "en-AU", + "belgian", "nl-BE", + "french-belgian", "fr-BE", + "canadian", "en-CA", + "english-can", "en-CA", + "french-canadian", "fr-CA", +#if _MSVCR_VER >= 110 + "chinese", "zh", + "chinese-simplified", "zh", + "chinese-traditional", "zh-HK", + "chs", "zh", + "cht", "zh-HK", +#else + "chinese", "zh-CN", + "chinese-simplified", "zh-CN", + "chinese-traditional", "zh-TW", + "chs", "zh-CN", + "cht", "zh-TW", +#endif + "dutch-belgian", "nl-BE", + "english-nz", "en-NZ", + "uk", "en-GB", + "english-uk", "en-GB", + "french-swiss", "fr-CH", + "swiss", "de-CH", + "german-swiss", "de-CH", + "italian-swiss", "it-CH", + "german-austrian", "de-AT", + "portuguese", "pt-BR", + "portuguese-brazil", "pt-BR", + "spanish-mexican", "es-MX", + "norwegian-bokmal", "nb", + "norwegian-nynorsk", "nn-NO", + "spanish-modern", "es-ES" +}; + + +/* INTERNAL: Map a synonym to an ISO code */ +static BOOL remap_synonym(char *name) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(_country_synonyms); i += 2) + { + if (!_stricmp(_country_synonyms[i],name)) + { + TRACE(":Mapping synonym %s to %s\n",name,_country_synonyms[i+1]); + strcpy(name, _country_synonyms[i+1]); + return TRUE; + } + } + + return FALSE; +} + +/* Note: Flags are weighted in order of matching importance */ +#define FOUND_SNAME 0x4 +#define FOUND_LANGUAGE 0x2 +#define FOUND_COUNTRY 0x1 + +typedef struct { + WCHAR search_language[MAX_ELEM_LEN]; + WCHAR search_country[MAX_ELEM_LEN]; + WCHAR found_lang_sname[LOCALE_NAME_MAX_LENGTH]; + unsigned int match_flags; + BOOL allow_sname; +} locale_search_t; + +#define CONTINUE_LOOKING TRUE +#define STOP_LOOKING FALSE + +/* INTERNAL: Get and compare locale info with a given string */ +static int compare_info(WCHAR *name, DWORD flags, WCHAR *buff, const WCHAR *cmp, BOOL exact) +{ + int len; + + if(!cmp[0]) + return 0; + + buff[0] = 0; + GetLocaleInfoEx(name, flags|LOCALE_NOUSEROVERRIDE, buff, MAX_ELEM_LEN); + if (!buff[0]) + return 0; + + /* Partial matches are only allowed on language/country names */ + len = wcslen(cmp); + + if(exact || len<=3) + return !_wcsicmp(cmp, buff); + else + return !_wcsnicmp(cmp, buff, len); +} + +static BOOL CALLBACK +find_best_locale_proc( WCHAR *name, DWORD locale_flags, LPARAM lParam ) +{ + locale_search_t *res = (locale_search_t *)lParam; + WCHAR buff[MAX_ELEM_LEN]; + unsigned int flags = 0; + + if (res->allow_sname && compare_info(name,LOCALE_SNAME,buff,res->search_language, TRUE)) + { + TRACE(":Found locale: %s->%s\n", wine_dbgstr_w(res->search_language), wine_dbgstr_w(buff)); + res->match_flags = FOUND_SNAME; + wcscpy(res->found_lang_sname, name); + return STOP_LOOKING; + } + + /* Check Language */ + if (compare_info(name,LOCALE_SISO639LANGNAME,buff,res->search_language, TRUE) || + compare_info(name,LOCALE_SABBREVLANGNAME,buff,res->search_language, TRUE) || + compare_info(name,LOCALE_SENGLANGUAGE,buff,res->search_language, FALSE)) + { + TRACE(":Found language: %s->%s\n", wine_dbgstr_w(res->search_language), wine_dbgstr_w(buff)); + flags |= FOUND_LANGUAGE; + } + else if (res->match_flags & FOUND_LANGUAGE) + { + return CONTINUE_LOOKING; + } + + /* Check Country */ + if (compare_info(name,LOCALE_SISO3166CTRYNAME,buff,res->search_country, TRUE) || + compare_info(name,LOCALE_SABBREVCTRYNAME,buff,res->search_country, TRUE) || + compare_info(name,LOCALE_SENGCOUNTRY,buff,res->search_country, FALSE)) + { + TRACE("Found country:%s->%s\n", wine_dbgstr_w(res->search_country), wine_dbgstr_w(buff)); + flags |= FOUND_COUNTRY; + } + else if (!flags && (res->match_flags & FOUND_COUNTRY)) + { + return CONTINUE_LOOKING; + } + + if (flags > res->match_flags) + { + /* Found a better match than previously */ + res->match_flags = flags; + wcscpy(res->found_lang_sname, name); + } + if ((flags & (FOUND_LANGUAGE | FOUND_COUNTRY)) == + (FOUND_LANGUAGE | FOUND_COUNTRY)) + { + TRACE(":found exact locale match\n"); + return STOP_LOOKING; + } + return CONTINUE_LOOKING; +} + +/* Internal: Find the sname for a locale specification. + * sname must be at least LOCALE_NAME_MAX_LENGTH characters long + */ +BOOL locale_to_sname(const char *locale, unsigned short *codepage, BOOL *sname_match, WCHAR *sname) +{ + thread_data_t *data = msvcrt_get_thread_data(); + const char *cp, *region; + BOOL is_sname = FALSE; + DWORD locale_cp; + + if (!strcmp(locale, data->cached_locale)) { + if (codepage) + *codepage = data->cached_cp; + if (sname_match) + *sname_match = data->cached_sname_match; + wcscpy(sname, data->cached_sname); + return TRUE; + } + + cp = strchr(locale, '.'); + region = strchr(locale, '_'); + + if(!locale[0] || (cp == locale && !region)) { + GetUserDefaultLocaleName(sname, LOCALE_NAME_MAX_LENGTH); + } else { + char search_language_buf[MAX_ELEM_LEN] = { 0 }, search_country_buf[MAX_ELEM_LEN] = { 0 }; + locale_search_t search; + BOOL remapped = FALSE; + + memset(&search, 0, sizeof(locale_search_t)); + lstrcpynA(search_language_buf, locale, MAX_ELEM_LEN); + if(region) { + lstrcpynA(search_country_buf, region+1, MAX_ELEM_LEN); + if(region-locale < MAX_ELEM_LEN) + search_language_buf[region-locale] = '\0'; + } else + search_country_buf[0] = '\0'; + + if(cp) { + if(region && cp-region-1= 110 + if(!cp && !region) + { + search.allow_sname = TRUE; + } +#endif + + MultiByteToWideChar(CP_ACP, 0, search_language_buf, -1, search.search_language, MAX_ELEM_LEN); + if (search.allow_sname && IsValidLocaleName(search.search_language)) + { + search.match_flags = FOUND_SNAME; + wcscpy(sname, search.search_language); + } + else + { + MultiByteToWideChar(CP_ACP, 0, search_country_buf, -1, search.search_country, MAX_ELEM_LEN); + EnumSystemLocalesEx( find_best_locale_proc, 0, (LPARAM)&search, NULL); + + if (!search.match_flags) + return FALSE; + + /* If we were given something that didn't match, fail */ + if (search.search_language[0] && !(search.match_flags & (FOUND_SNAME | FOUND_LANGUAGE))) + return FALSE; + if (search.search_country[0] && !(search.match_flags & FOUND_COUNTRY)) + return FALSE; + + wcscpy(sname, search.found_lang_sname); + } + + is_sname = !remapped && (search.match_flags & FOUND_SNAME) != 0; + } + + /* Obtain code page */ + if (!cp || !cp[1] || !_strnicmp(cp, ".ACP", 4)) { + GetLocaleInfoEx(sname, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, + (WCHAR *)&locale_cp, sizeof(DWORD)/sizeof(WCHAR)); + if (!locale_cp) + locale_cp = GetACP(); + } else if (!_strnicmp(cp, ".OCP", 4)) { + GetLocaleInfoEx(sname, LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER, + (WCHAR *)&locale_cp, sizeof(DWORD)/sizeof(WCHAR)); +#if _MSVCR_VER >= 140 + } else if (!_strnicmp(cp, ".UTF-8", 6) + || !_strnicmp(cp, ".UTF8", 5)) { + locale_cp = CP_UTF8; +#endif + } else { + locale_cp = atoi(cp + 1); + } + if (!IsValidCodePage(locale_cp)) + return FALSE; + + if (!locale_cp) + return FALSE; + + if (codepage) + *codepage = locale_cp; + if (sname_match) + *sname_match = is_sname; + + if (strlen(locale) < sizeof(data->cached_locale)) { + strcpy(data->cached_locale, locale); + data->cached_cp = locale_cp; + data->cached_sname_match = is_sname; + wcscpy(data->cached_sname, sname); + } + + return TRUE; +} + +static void copy_threadlocinfo_category(pthreadlocinfo locinfo, + const threadlocinfo *old_locinfo, int category) +{ + locinfo->lc_handle[category] = old_locinfo->lc_handle[category]; + locinfo->lc_id[category] = old_locinfo->lc_id[category]; + if(!locinfo->lc_category[category].locale) { + locinfo->lc_category[category].locale = old_locinfo->lc_category[category].locale; + locinfo->lc_category[category].refcount = old_locinfo->lc_category[category].refcount; + InterlockedIncrement((LONG *)locinfo->lc_category[category].refcount); + } +#if _MSVCR_VER >= 110 + locinfo->lc_name[category] = old_locinfo->lc_name[category]; + locinfo->lc_category[category].wrefcount = old_locinfo->lc_category[category].wrefcount; + if(locinfo->lc_category[category].wrefcount) + InterlockedIncrement((LONG *)locinfo->lc_category[category].wrefcount); +#endif +} + +static BOOL init_category_name(const char *name, int len, + pthreadlocinfo locinfo, int category) +{ + locinfo->lc_category[category].locale = malloc(len+1); + locinfo->lc_category[category].refcount = malloc(sizeof(int)); + if(!locinfo->lc_category[category].locale + || !locinfo->lc_category[category].refcount) { + free(locinfo->lc_category[category].locale); + free(locinfo->lc_category[category].refcount); + locinfo->lc_category[category].locale = NULL; + locinfo->lc_category[category].refcount = NULL; + return FALSE; + } + + memcpy(locinfo->lc_category[category].locale, name, len); + locinfo->lc_category[category].locale[len] = 0; + *locinfo->lc_category[category].refcount = 1; + return TRUE; +} + +#if _MSVCR_VER >= 110 +static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat, WCHAR *sname) +{ + locinfo->lc_category[cat].wrefcount = malloc(sizeof(int)); + if(!locinfo->lc_category[cat].wrefcount) + return FALSE; + *locinfo->lc_category[cat].wrefcount = 1; + + if(!(locinfo->lc_name[cat] = wcsdup(sname))) + return FALSE; + + return TRUE; +} +#else +static inline BOOL set_lc_locale_name(pthreadlocinfo locinfo, int cat, WCHAR *sname) +{ + return TRUE; +} +#endif + +/* INTERNAL: Set lc_handle, lc_id and lc_category in threadlocinfo struct */ +static BOOL update_threadlocinfo_category(WCHAR *sname, unsigned short cp, + pthreadlocinfo locinfo, int category) +{ + WCHAR wbuf[256], *p; + + if(GetLocaleInfoEx(sname, LOCALE_ILANGUAGE|LOCALE_NOUSEROVERRIDE, wbuf, ARRAY_SIZE(wbuf))) { + p = wbuf; + + locinfo->lc_id[category].wLanguage = 0; + while(*p) { + locinfo->lc_id[category].wLanguage *= 16; + + if(*p <= '9') + locinfo->lc_id[category].wLanguage += *p-'0'; + else + locinfo->lc_id[category].wLanguage += *p-'a'+10; + + p++; + } + + locinfo->lc_id[category].wCountry = + locinfo->lc_id[category].wLanguage; + } + + locinfo->lc_id[category].wCodePage = cp; + + locinfo->lc_handle[category] = LocaleNameToLCID(sname, LCID_CONVERSION_FLAGS); + + set_lc_locale_name(locinfo, category, sname); + + if(!locinfo->lc_category[category].locale) { + char buf[256]; + int len = 0; + +#if _MSVCR_VER < 110 + if (LANGIDFROMLCID(locinfo->lc_handle[category]) == MAKELANGID(LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK)) + { + /* locale.nls contains "Norwegian Nynorsk" instead for LOCALE_SENGLANGUAGE */ + wcscpy( wbuf, L"Norwegian-Nynorsk" ); + len = wcslen( wbuf ) + 1; + } + else +#endif + len += GetLocaleInfoEx(sname, LOCALE_SENGLANGUAGE|LOCALE_NOUSEROVERRIDE, wbuf, ARRAY_SIZE(wbuf)); + wbuf[len-1] = '_'; + len += GetLocaleInfoEx(sname, LOCALE_SENGCOUNTRY + |LOCALE_NOUSEROVERRIDE, &wbuf[len], ARRAY_SIZE(wbuf) - len); + wbuf[len-1] = '.'; + swprintf(wbuf+len, ARRAY_SIZE(wbuf) - len,L"%d", cp); + len += wcslen(wbuf+len); + + WideCharToMultiByte(cp, 0, wbuf, -1, buf, ARRAY_SIZE(buf), NULL, NULL); + + return init_category_name(buf, len, locinfo, category); + } + + return TRUE; +} + +/********************************************************************* + * _lock_locales (UCRTBASE.@) + */ +void CDECL _lock_locales(void) +{ + _lock(_SETLOCALE_LOCK); +} + +/********************************************************************* + * _unlock_locales (UCRTBASE.@) + */ +void CDECL _unlock_locales(void) +{ + _unlock(_SETLOCALE_LOCK); +} + +static void grab_locinfo(pthreadlocinfo locinfo) +{ + int i; + + InterlockedIncrement((LONG *)&locinfo->refcount); + for(i=LC_MIN+1; i<=LC_MAX; i++) + { + InterlockedIncrement((LONG *)locinfo->lc_category[i].refcount); + if(locinfo->lc_category[i].wrefcount) + InterlockedIncrement((LONG *)locinfo->lc_category[i].wrefcount); + } + if(locinfo->lconv_intl_refcount) + InterlockedIncrement((LONG *)locinfo->lconv_intl_refcount); + if(locinfo->lconv_num_refcount) + InterlockedIncrement((LONG *)locinfo->lconv_num_refcount); + if(locinfo->lconv_mon_refcount) + InterlockedIncrement((LONG *)locinfo->lconv_mon_refcount); + if(locinfo->ctype1_refcount) + InterlockedIncrement((LONG *)locinfo->ctype1_refcount); + InterlockedIncrement(&locinfo->lc_time_curr->refcount); +} + +static void update_thread_locale(thread_data_t *data) +{ + if((data->locale_flags & LOCALE_FREE) && ((data->locale_flags & LOCALE_THREAD) || + (data->locinfo == MSVCRT_locale->locinfo && data->mbcinfo == MSVCRT_locale->mbcinfo))) + return; + + if(data->locale_flags & LOCALE_FREE) + { + free_locinfo(data->locinfo); + free_mbcinfo(data->mbcinfo); + } + + _lock_locales(); + data->locinfo = MSVCRT_locale->locinfo; + grab_locinfo(data->locinfo); + _unlock_locales(); + + _lock(_MB_CP_LOCK); + data->mbcinfo = MSVCRT_locale->mbcinfo; + InterlockedIncrement(&data->mbcinfo->refcount); + _unlock(_MB_CP_LOCK); + + data->locale_flags |= LOCALE_FREE; +} + +/* INTERNAL: returns threadlocinfo struct */ +pthreadlocinfo CDECL get_locinfo(void) { + thread_data_t *data = msvcrt_get_thread_data(); + update_thread_locale(data); + return data->locinfo; +} + +/* INTERNAL: returns pthreadmbcinfo struct */ +pthreadmbcinfo CDECL get_mbcinfo(void) { + thread_data_t *data = msvcrt_get_thread_data(); + update_thread_locale(data); + return data->mbcinfo; +} + +/* INTERNAL: constructs string returned by setlocale */ +static inline char* construct_lc_all(pthreadlocinfo locinfo) { + static char current_lc_all[MAX_LOCALE_LENGTH]; + + int i; + + for(i=LC_MIN+1; ilc_category[i].locale, + locinfo->lc_category[i+1].locale)) + break; + } + + if(i==LC_MAX) + return locinfo->lc_category[LC_COLLATE].locale; + + sprintf(current_lc_all, + "LC_COLLATE=%s;LC_CTYPE=%s;LC_MONETARY=%s;LC_NUMERIC=%s;LC_TIME=%s", + locinfo->lc_category[LC_COLLATE].locale, + locinfo->lc_category[LC_CTYPE].locale, + locinfo->lc_category[LC_MONETARY].locale, + locinfo->lc_category[LC_NUMERIC].locale, + locinfo->lc_category[LC_TIME].locale); + + return current_lc_all; +} + + +/********************************************************************* + * _Getdays (MSVCRT.@) + */ +char* CDECL _Getdays(void) +{ + __lc_time_data *cur = get_locinfo()->lc_time_curr; + int i, len, size = 0; + char *out; + + TRACE("\n"); + + for(i=0; i<7; i++) { + size += strlen(cur->str.names.short_wday[i]) + 1; + size += strlen(cur->str.names.wday[i]) + 1; + } + out = malloc(size+1); + if(!out) + return NULL; + + size = 0; + for(i=0; i<7; i++) { + out[size++] = ':'; + len = strlen(cur->str.names.short_wday[i]); + memcpy(&out[size], cur->str.names.short_wday[i], len); + size += len; + + out[size++] = ':'; + len = strlen(cur->str.names.wday[i]); + memcpy(&out[size], cur->str.names.wday[i], len); + size += len; + } + out[size] = '\0'; + + return out; +} + +#if _MSVCR_VER >= 110 +/********************************************************************* + * _W_Getdays (MSVCR110.@) + */ +wchar_t* CDECL _W_Getdays(void) +{ + __lc_time_data *cur = get_locinfo()->lc_time_curr; + wchar_t *out; + int i, len, size = 0; + + TRACE("\n"); + + for(i=0; i<7; i++) { + size += wcslen(cur->wstr.names.short_wday[i]) + 1; + size += wcslen(cur->wstr.names.wday[i]) + 1; + } + out = malloc((size+1)*sizeof(*out)); + if(!out) + return NULL; + + size = 0; + for(i=0; i<7; i++) { + out[size++] = ':'; + len = wcslen(cur->wstr.names.short_wday[i]); + memcpy(&out[size], cur->wstr.names.short_wday[i], len*sizeof(*out)); + size += len; + + out[size++] = ':'; + len = wcslen(cur->wstr.names.wday[i]); + memcpy(&out[size], cur->wstr.names.wday[i], len*sizeof(*out)); + size += len; + } + out[size] = '\0'; + + return out; +} +#endif + +/********************************************************************* + * _Getmonths (MSVCRT.@) + */ +char* CDECL _Getmonths(void) +{ + __lc_time_data *cur = get_locinfo()->lc_time_curr; + int i, len, size = 0; + char *out; + + TRACE("\n"); + + for(i=0; i<12; i++) { + size += strlen(cur->str.names.short_mon[i]) + 1; + size += strlen(cur->str.names.mon[i]) + 1; + } + out = malloc(size+1); + if(!out) + return NULL; + + size = 0; + for(i=0; i<12; i++) { + out[size++] = ':'; + len = strlen(cur->str.names.short_mon[i]); + memcpy(&out[size], cur->str.names.short_mon[i], len); + size += len; + + out[size++] = ':'; + len = strlen(cur->str.names.mon[i]); + memcpy(&out[size], cur->str.names.mon[i], len); + size += len; + } + out[size] = '\0'; + + return out; +} + +#if _MSVCR_VER >= 110 +/********************************************************************* + * _W_Getmonths (MSVCR110.@) + */ +wchar_t* CDECL _W_Getmonths(void) +{ + __lc_time_data *cur = get_locinfo()->lc_time_curr; + wchar_t *out; + int i, len, size = 0; + + TRACE("\n"); + + for(i=0; i<12; i++) { + size += wcslen(cur->wstr.names.short_mon[i]) + 1; + size += wcslen(cur->wstr.names.mon[i]) + 1; + } + out = malloc((size+1)*sizeof(*out)); + if(!out) + return NULL; + + size = 0; + for(i=0; i<12; i++) { + out[size++] = ':'; + len = wcslen(cur->wstr.names.short_mon[i]); + memcpy(&out[size], cur->wstr.names.short_mon[i], len*sizeof(*out)); + size += len; + + out[size++] = ':'; + len = wcslen(cur->wstr.names.mon[i]); + memcpy(&out[size], cur->wstr.names.mon[i], len*sizeof(*out)); + size += len; + } + out[size] = '\0'; + + return out; +} +#endif + +/********************************************************************* + * _Gettnames (MSVCRT.@) + */ +void* CDECL _Gettnames(void) +{ + __lc_time_data *ret, *cur = get_locinfo()->lc_time_curr; + unsigned int i, len, size = sizeof(__lc_time_data); + + TRACE("\n"); + + for(i=0; istr.str); i++) + size += strlen(cur->str.str[i])+1; +#if _MSVCR_VER >= 110 + for(i=0; iwstr.wstr); i++) + size += (wcslen(cur->wstr.wstr[i]) + 1) * sizeof(wchar_t); +#endif + + ret = malloc(size); + if(!ret) + return NULL; + memcpy(ret, cur, sizeof(*ret)); + + size = 0; + for(i=0; istr.str); i++) { + len = strlen(cur->str.str[i])+1; + memcpy(&ret->data[size], cur->str.str[i], len); + ret->str.str[i] = &ret->data[size]; + size += len; + } +#if _MSVCR_VER >= 110 + for(i=0; iwstr.wstr); i++) { + len = (wcslen(cur->wstr.wstr[i]) + 1) * sizeof(wchar_t); + memcpy(&ret->data[size], cur->wstr.wstr[i], len); + ret->wstr.wstr[i] = (wchar_t*)&ret->data[size]; + size += len; + } +#endif + + return ret; +} + +#if _MSVCR_VER >= 110 +/********************************************************************* + * _W_Gettnames (MSVCR110.@) + */ +void* CDECL _W_Gettnames(void) +{ + return _Gettnames(); +} +#endif + +/********************************************************************* + * __crtLCMapStringA (MSVCRT.@) + */ +int CDECL __crtLCMapStringA( + LCID lcid, DWORD mapflags, const char* src, int srclen, char* dst, + int dstlen, unsigned int codepage, int xflag +) { + WCHAR buf_in[32], *in = buf_in; + WCHAR buf_out[32], *out = buf_out; + int in_len, out_len, r; + + TRACE("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n", + lcid, mapflags, src, srclen, dst, dstlen, codepage, xflag); + + in_len = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, src, srclen, NULL, 0); + if (!in_len) return 0; + if (in_len > ARRAY_SIZE(buf_in)) + { + in = malloc(in_len * sizeof(WCHAR)); + if (!in) return 0; + } + + r = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, src, srclen, in, in_len); + if (!r) goto done; + + if (mapflags & LCMAP_SORTKEY) + { + r = LCMapStringW(lcid, mapflags, in, in_len, (WCHAR*)dst, dstlen); + goto done; + } + + r = LCMapStringW(lcid, mapflags, in, in_len, NULL, 0); + if (!r) goto done; + out_len = r; + if (r > ARRAY_SIZE(buf_out)) + { + out = malloc(r * sizeof(WCHAR)); + if (!out) + { + r = 0; + goto done; + } + } + + r = LCMapStringW(lcid, mapflags, in, in_len, out, out_len); + if (!r) goto done; + + r = WideCharToMultiByte(codepage, 0, out, out_len, dst, dstlen, NULL, NULL); + +done: + if (in != buf_in) free(in); + if (out != buf_out) free(out); + return r; +} + +/********************************************************************* + * __crtLCMapStringW (MSVCRT.@) + */ +int CDECL __crtLCMapStringW(LCID lcid, DWORD mapflags, const wchar_t *src, + int srclen, wchar_t *dst, int dstlen, unsigned int codepage, int xflag) +{ + FIXME("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n", + lcid, mapflags, debugstr_w(src), srclen, dst, dstlen, codepage, xflag); + + return LCMapStringW(lcid, mapflags, src, srclen, dst, dstlen); +} + +/********************************************************************* + * __crtCompareStringA (MSVCRT.@) + */ +int CDECL __crtCompareStringA( LCID lcid, DWORD flags, const char *src1, int len1, + const char *src2, int len2 ) +{ + FIXME("(lcid %lx, flags %lx, %s(%d), %s(%d), partial stub\n", + lcid, flags, debugstr_a(src1), len1, debugstr_a(src2), len2 ); + /* FIXME: probably not entirely right */ + return CompareStringA( lcid, flags, src1, len1, src2, len2 ); +} + +/********************************************************************* + * __crtCompareStringW (MSVCRT.@) + */ +int CDECL __crtCompareStringW( LCID lcid, DWORD flags, const wchar_t *src1, int len1, + const wchar_t *src2, int len2 ) +{ + FIXME("(lcid %lx, flags %lx, %s(%d), %s(%d), partial stub\n", + lcid, flags, debugstr_w(src1), len1, debugstr_w(src2), len2 ); + /* FIXME: probably not entirely right */ + return CompareStringW( lcid, flags, src1, len1, src2, len2 ); +} + +/********************************************************************* + * __crtGetLocaleInfoW (MSVCRT.@) + */ +int CDECL __crtGetLocaleInfoW( LCID lcid, LCTYPE type, wchar_t *buffer, int len ) +{ + FIXME("(lcid %lx, type %lx, %p(%d), partial stub\n", lcid, type, buffer, len ); + /* FIXME: probably not entirely right */ + return GetLocaleInfoW( lcid, type, buffer, len ); +} + +#if _MSVCR_VER >= 110 +/********************************************************************* + * __crtGetLocaleInfoEx (MSVC110.@) + */ +int CDECL __crtGetLocaleInfoEx( const WCHAR *locale, LCTYPE type, wchar_t *buffer, int len ) +{ + TRACE("(%s, %lx, %p, %d)\n", debugstr_w(locale), type, buffer, len); + return GetLocaleInfoEx(locale, type, buffer, len); +} +#endif + +/********************************************************************* + * __crtGetStringTypeW(MSVCRT.@) + * + * This function was accepting different number of arguments in older + * versions of msvcrt. + */ +BOOL CDECL __crtGetStringTypeW(DWORD unk, DWORD type, + wchar_t *buffer, int len, WORD *out) +{ + FIXME("(unk %lx, type %lx, wstr %p(%d), %p) partial stub\n", + unk, type, buffer, len, out); + + return GetStringTypeW(type, buffer, len, out); +} + +/********************************************************************* + * localeconv (MSVCRT.@) + */ +struct lconv* CDECL localeconv(void) +{ + return get_locinfo()->lconv; +} + +/********************************************************************* + * __lconv_init (MSVCRT.@) + */ +int CDECL __lconv_init(void) +{ + /* this is used to make chars unsigned */ + cloc_lconv.int_frac_digits = (char)UCHAR_MAX; + cloc_lconv.frac_digits = (char)UCHAR_MAX; + cloc_lconv.p_cs_precedes = (char)UCHAR_MAX; + cloc_lconv.p_sep_by_space = (char)UCHAR_MAX; + cloc_lconv.n_cs_precedes = (char)UCHAR_MAX; + cloc_lconv.n_sep_by_space = (char)UCHAR_MAX; + cloc_lconv.p_sign_posn = (char)UCHAR_MAX; + cloc_lconv.n_sign_posn = (char)UCHAR_MAX; + return 0; +} + +/********************************************************************* + * ___lc_handle_func (MSVCRT.@) + */ +LCID* CDECL ___lc_handle_func(void) +{ + return (LCID *)get_locinfo()->lc_handle; +} + +#if _MSVCR_VER >= 110 +/********************************************************************* + * ___lc_locale_name_func (MSVCR110.@) + */ +wchar_t** CDECL ___lc_locale_name_func(void) +{ + return get_locinfo()->lc_name; +} +#endif + +/********************************************************************* + * ___lc_codepage_func (MSVCRT.@) + */ +unsigned int CDECL ___lc_codepage_func(void) +{ + return get_locinfo()->lc_codepage; +} + +/********************************************************************* + * ___lc_collate_cp_func (MSVCRT.@) + */ +int CDECL ___lc_collate_cp_func(void) +{ + return get_locinfo()->lc_collate_cp; +} + +/* INTERNAL: frees pthreadlocinfo struct */ +void free_locinfo(pthreadlocinfo locinfo) +{ + int i; + + if(!locinfo) + return; + + for(i=LC_MIN+1; i<=LC_MAX; i++) { + if(!locinfo->lc_category[i].refcount + || !InterlockedDecrement((LONG *)locinfo->lc_category[i].refcount)) { + free(locinfo->lc_category[i].locale); + free(locinfo->lc_category[i].refcount); + } + if(!locinfo->lc_category[i].wrefcount + || !InterlockedDecrement((LONG *)locinfo->lc_category[i].wrefcount)) { +#if _MSVCR_VER >= 110 + free(locinfo->lc_name[i]); +#endif + free(locinfo->lc_category[i].wrefcount); + } + } + + if(locinfo->lconv_num_refcount + && !InterlockedDecrement((LONG *)locinfo->lconv_num_refcount)) { + free(locinfo->lconv->decimal_point); + free(locinfo->lconv->thousands_sep); + free(locinfo->lconv->grouping); +#if _MSVCR_VER >= 100 + free(locinfo->lconv->_W_decimal_point); + free(locinfo->lconv->_W_thousands_sep); +#endif + free(locinfo->lconv_num_refcount); + } + if(locinfo->lconv_mon_refcount + && !InterlockedDecrement((LONG *)locinfo->lconv_mon_refcount)) { + free(locinfo->lconv->int_curr_symbol); + free(locinfo->lconv->currency_symbol); + free(locinfo->lconv->mon_decimal_point); + free(locinfo->lconv->mon_thousands_sep); + free(locinfo->lconv->mon_grouping); + free(locinfo->lconv->positive_sign); + free(locinfo->lconv->negative_sign); +#if _MSVCR_VER >= 100 + free(locinfo->lconv->_W_int_curr_symbol); + free(locinfo->lconv->_W_currency_symbol); + free(locinfo->lconv->_W_mon_decimal_point); + free(locinfo->lconv->_W_mon_thousands_sep); + free(locinfo->lconv->_W_positive_sign); + free(locinfo->lconv->_W_negative_sign); +#endif + free(locinfo->lconv_mon_refcount); + } + if(locinfo->lconv_intl_refcount + && !InterlockedDecrement((LONG *)locinfo->lconv_intl_refcount)) { + free(locinfo->lconv_intl_refcount); + free(locinfo->lconv); + } + + if(locinfo->ctype1_refcount + && !InterlockedDecrement((LONG *)locinfo->ctype1_refcount)) { + free(locinfo->ctype1_refcount); + free(locinfo->ctype1); + free((void*)locinfo->pclmap); + free((void*)locinfo->pcumap); + } + + if(locinfo->lc_time_curr && !InterlockedDecrement(&locinfo->lc_time_curr->refcount) + && locinfo->lc_time_curr != &cloc_time_data) + free(locinfo->lc_time_curr); + + if(InterlockedDecrement((LONG *)&locinfo->refcount)) + return; + + free(locinfo); +} + +/* INTERNAL: frees pthreadmbcinfo struct */ +void free_mbcinfo(pthreadmbcinfo mbcinfo) +{ + if(!mbcinfo) + return; + + if(InterlockedDecrement(&mbcinfo->refcount)) + return; + + free(mbcinfo); +} + +_locale_t CDECL get_current_locale_noalloc(_locale_t locale) +{ + thread_data_t *data = msvcrt_get_thread_data(); + + update_thread_locale(data); + locale->locinfo = data->locinfo; + locale->mbcinfo = data->mbcinfo; + + grab_locinfo(locale->locinfo); + InterlockedIncrement(&locale->mbcinfo->refcount); + return locale; +} + +void CDECL free_locale_noalloc(_locale_t locale) +{ + free_locinfo(locale->locinfo); + free_mbcinfo(locale->mbcinfo); +} + +/********************************************************************* + * _get_current_locale (MSVCRT.@) + */ +_locale_t CDECL _get_current_locale(void) +{ + _locale_t loc = malloc(sizeof(_locale_tstruct)); + if(!loc) + return NULL; + + return get_current_locale_noalloc(loc); +} + +/********************************************************************* + * _free_locale (MSVCRT.@) + */ +void CDECL _free_locale(_locale_t locale) +{ + if (!locale) + return; + + free_locale_noalloc(locale); + free(locale); +} + +static inline BOOL category_needs_update(int cat, + const threadlocinfo *locinfo, WCHAR *sname, unsigned short cp) +{ +#if _MSVCR_VER < 110 + LCID lcid; +#endif + if(!locinfo) return TRUE; +#if _MSVCR_VER >= 110 + if(!locinfo->lc_name[cat] || !sname) return TRUE; + return wcscmp(sname, locinfo->lc_name[cat]) != 0 || cp!=locinfo->lc_id[cat].wCodePage; +#else + lcid = sname ? LocaleNameToLCID(sname, 0) : 0; + return lcid!=locinfo->lc_handle[cat] || cp!=locinfo->lc_id[cat].wCodePage; +#endif +} + +static __lc_time_data* create_time_data(WCHAR *sname) +{ + static const DWORD time_data[] = { + LOCALE_SABBREVDAYNAME7, LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2, + LOCALE_SABBREVDAYNAME3, LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5, + LOCALE_SABBREVDAYNAME6, + LOCALE_SDAYNAME7, LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3, + LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6, + LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3, + LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6, + LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9, + LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12, + LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3, LOCALE_SMONTHNAME4, + LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6, LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, + LOCALE_SMONTHNAME9, LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12, + LOCALE_S1159, LOCALE_S2359, + LOCALE_SSHORTDATE, LOCALE_SLONGDATE, + LOCALE_STIMEFORMAT + }; + + __lc_time_data *cur; + int i, ret, size; + LCID lcid = LocaleNameToLCID(sname, LCID_CONVERSION_FLAGS); + + size = 0; + for(i=0; i= 100 + ret = GetLocaleInfoEx(sname, time_data[i], NULL, 0); + if(!ret) + return NULL; + size += ret*sizeof(wchar_t); +#endif + } +#if _MSVCR_VER >= 110 + size += (wcslen(sname) + 1) * sizeof(wchar_t); +#endif + + cur = malloc(FIELD_OFFSET(__lc_time_data, data[size])); + if(!cur) + return NULL; + + ret = 0; + for(i=0; istr.str[i] = &cur->data[ret]; + ret += GetLocaleInfoA(lcid, time_data[i], &cur->data[ret], size-ret); + } +#if _MSVCR_VER == 0 || _MSVCR_VER >= 100 + for(i=0; iwstr.wstr[i] = (wchar_t*)&cur->data[ret]; + ret += GetLocaleInfoEx(sname, time_data[i], (wchar_t*)&cur->data[ret], + (size - ret) / sizeof(wchar_t)) * sizeof(wchar_t); + } +#endif +#if _MSVCR_VER >= 110 + cur->locname = (wchar_t*)&cur->data[ret]; + wcscpy((wchar_t*)&cur->data[ret], sname); +#else + cur->lcid = lcid; +#endif + cur->unk = 1; + cur->refcount = 1; + + return cur; +} + +static pthreadlocinfo create_locinfo(int category, + const char *locale, const threadlocinfo *old_locinfo) +{ + static const char collate[] = "COLLATE="; + static const char ctype[] = "CTYPE="; + static const char monetary[] = "MONETARY="; + static const char numeric[] = "NUMERIC="; + static const char time[] = "TIME="; + + pthreadlocinfo locinfo = NULL; + unsigned short cp[6] = { 0 }; + const char *locale_name[6] = { 0 }; + WCHAR *locale_sname[6] = { 0 }; + int val, locale_len[6] = { 0 }; + char buf[256]; + BOOL sname_match; + wchar_t wbuf[256], map_buf[256]; + int i; + + TRACE("(%d %s)\n", category, locale); + + if(categoryLC_MAX || !locale) + return NULL; + + if(locale[0]=='C' && !locale[1]) { + locale_sname[0] = NULL; + cp[0] = CP_ACP; + } else if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_') { + const char *p; + + while(1) { + locale += 3; /* LC_ */ + if(!memcmp(locale, collate, sizeof(collate)-1)) { + i = LC_COLLATE; + locale += sizeof(collate)-1; + } else if(!memcmp(locale, ctype, sizeof(ctype)-1)) { + i = LC_CTYPE; + locale += sizeof(ctype)-1; + } else if(!memcmp(locale, monetary, sizeof(monetary)-1)) { + i = LC_MONETARY; + locale += sizeof(monetary)-1; + } else if(!memcmp(locale, numeric, sizeof(numeric)-1)) { + i = LC_NUMERIC; + locale += sizeof(numeric)-1; + } else if(!memcmp(locale, time, sizeof(time)-1)) { + i = LC_TIME; + locale += sizeof(time)-1; + } else + goto fail; + + p = strchr(locale, ';'); + if(locale[0]=='C' && (locale[1]==';' || locale[1]=='\0')) { + locale_sname[i] = NULL; + cp[i] = CP_ACP; + } else { + BOOL locale_found = FALSE; + + if(p) { + memcpy(buf, locale, p-locale); + buf[p-locale] = '\0'; + locale_found = locale_to_sname(buf, &cp[i], &sname_match, wbuf); + } else { + locale_found = locale_to_sname(locale, &cp[i], &sname_match, wbuf); + } + + if(!locale_found || !(locale_sname[i] = wcsdup(wbuf))) + goto fail; + if(sname_match) { + locale_name[i] = locale; + locale_len[i] = p ? p-locale : strlen(locale); + } + } + + if(!p || *(p+1)!='L' || *(p+2)!='C' || *(p+3)!='_') + break; + + locale = p+1; + } + } else { + BOOL locale_found = locale_to_sname(locale, &cp[0], &sname_match, wbuf); + + if(!locale_found) + return NULL; + + locale_sname[0] = wcsdup(wbuf); + if(!locale_sname[0]) + return NULL; + + if(sname_match) { + locale_name[0] = locale; + locale_len[0] = strlen(locale); + } + + for(i=1; i<6; i++) { + locale_sname[i] = wcsdup(locale_sname[0]); + if(!locale_sname[i]) + goto fail; + + cp[i] = cp[0]; + locale_name[i] = locale_name[0]; + locale_len[i] = locale_len[0]; + } + } + + for(i=1; i<6; i++) { +#if _MSVCR_VER < 140 + if(i==LC_CTYPE && cp[i]==CP_UTF8) { +#if _MSVCR_VER >= 110 + if(old_locinfo) { + locale_sname[i] = wcsdup(old_locinfo->lc_name[i]); + if (old_locinfo->lc_name[i] && !locale_sname[i]) + goto fail; + } +#else + int sname_size; + if(old_locinfo && old_locinfo->lc_handle[i]) { + sname_size = LCIDToLocaleName(old_locinfo->lc_handle[i], NULL, 0, 0); + locale_sname[i] = malloc(sname_size * sizeof(WCHAR)); + if(!locale_sname[i]) + goto fail; + LCIDToLocaleName(old_locinfo->lc_handle[i], locale_sname[i], sname_size, 0); + } else { + locale_sname[i] = NULL; + } +#endif + + locale_name[i] = NULL; + locale_len[i] = 0; + cp[i] = old_locinfo ? old_locinfo->lc_id[i].wCodePage : 0; + } +#endif + if(category!=LC_ALL && category!=i) { + if(old_locinfo) { +#if _MSVCR_VER >= 110 + locale_sname[i] = wcsdup(old_locinfo->lc_name[i]); + if(old_locinfo->lc_name[i] && !locale_sname[i]) + goto fail; +#else + int sname_size; + if(old_locinfo->lc_handle[i]) { + sname_size = LCIDToLocaleName(old_locinfo->lc_handle[i], NULL, 0, 0); + locale_sname[i] = malloc(sname_size * sizeof(WCHAR)); + if(!locale_sname[i]) + goto fail; + LCIDToLocaleName(old_locinfo->lc_handle[i], locale_sname[i], sname_size, 0); + } else { + locale_sname[i] = NULL; + } +#endif + cp[i] = old_locinfo->lc_id[i].wCodePage; + } else { + locale_sname[i] = NULL; + cp[i] = 0; + } + } + } + + locinfo = malloc(sizeof(threadlocinfo)); + if(!locinfo) + goto fail; + + memset(locinfo, 0, sizeof(threadlocinfo)); + locinfo->refcount = 1; + + if(locale_name[LC_COLLATE] && + !init_category_name(locale_name[LC_COLLATE], + locale_len[LC_COLLATE], locinfo, LC_COLLATE)) { + goto fail; + } + + if(!category_needs_update(LC_COLLATE, old_locinfo, + locale_sname[LC_COLLATE], cp[LC_COLLATE])) { + copy_threadlocinfo_category(locinfo, old_locinfo, LC_COLLATE); + locinfo->lc_collate_cp = old_locinfo->lc_collate_cp; + } else if(locale_sname[LC_COLLATE]) { + if(!update_threadlocinfo_category(locale_sname[LC_COLLATE], + cp[LC_COLLATE], locinfo, LC_COLLATE)) { + goto fail; + } + + locinfo->lc_collate_cp = locinfo->lc_id[LC_COLLATE].wCodePage; + } else { + if(!init_category_name("C", 1, locinfo, LC_COLLATE)) { + goto fail; + } + } + + if(locale_name[LC_CTYPE] && + !init_category_name(locale_name[LC_CTYPE], + locale_len[LC_CTYPE], locinfo, LC_CTYPE)) { + goto fail; + } + + if(!category_needs_update(LC_CTYPE, old_locinfo, + locale_sname[LC_CTYPE], cp[LC_CTYPE])) { + copy_threadlocinfo_category(locinfo, old_locinfo, LC_CTYPE); + locinfo->lc_codepage = old_locinfo->lc_codepage; + locinfo->lc_clike = old_locinfo->lc_clike; + locinfo->mb_cur_max = old_locinfo->mb_cur_max; + locinfo->ctype1 = old_locinfo->ctype1; + locinfo->ctype1_refcount = old_locinfo->ctype1_refcount; + locinfo->pctype = old_locinfo->pctype; + locinfo->pclmap = old_locinfo->pclmap; + locinfo->pcumap = old_locinfo->pcumap; + if(locinfo->ctype1_refcount) + InterlockedIncrement((LONG *)locinfo->ctype1_refcount); + } else if(locale_sname[LC_CTYPE]) { + CPINFO cp_info; + int j; + + if(!update_threadlocinfo_category(locale_sname[LC_CTYPE], + cp[LC_CTYPE], locinfo, LC_CTYPE)) { + goto fail; + } + + locinfo->lc_codepage = locinfo->lc_id[LC_CTYPE].wCodePage; + locinfo->lc_clike = 1; + if(!GetCPInfo(locinfo->lc_codepage, &cp_info)) { + goto fail; + } + locinfo->mb_cur_max = cp_info.MaxCharSize; + + locinfo->ctype1_refcount = malloc(sizeof(int)); + if(!locinfo->ctype1_refcount) { + goto fail; + } + *locinfo->ctype1_refcount = 1; + + locinfo->ctype1 = malloc(257 * sizeof(*locinfo->ctype1)); + locinfo->pclmap = malloc(256 * sizeof(*locinfo->pclmap)); + locinfo->pcumap = malloc(256 * sizeof(*locinfo->pcumap)); + if(!locinfo->ctype1 || !locinfo->pclmap || !locinfo->pcumap) { + goto fail; + } + + locinfo->ctype1[0] = 0; + locinfo->pctype = locinfo->ctype1+1; + + buf[1] = buf[2] = '\0'; + for(i=1; i<257; i++) { + buf[0] = i-1; + + MultiByteToWideChar(locinfo->lc_codepage, 0, buf, 1, wbuf, 1); + /* builtin GetStringType doesn't set output to 0 on invalid input */ + locinfo->ctype1[i] = 0; + GetStringTypeW(CT_CTYPE1, wbuf, 1, &locinfo->ctype1[i]); + } + + for(i=0; cp_info.LeadByte[i+1]!=0; i+=2) + for(j=cp_info.LeadByte[i]; j<=cp_info.LeadByte[i+1]; j++) + locinfo->ctype1[j+1] |= _LEADBYTE; + + for(i=0; i<256; i++) { + if(locinfo->pctype[i] & _LEADBYTE) + buf[i] = ' '; + else + buf[i] = i; + } + + MultiByteToWideChar(locinfo->lc_codepage, 0, buf, 256, wbuf, 256); + LCMapStringW(LOCALE_INVARIANT, LCMAP_LOWERCASE, wbuf, 256, map_buf, 256); + WideCharToMultiByte(locinfo->lc_codepage, 0, map_buf, 256, (char *)locinfo->pclmap, 256, NULL, NULL); + LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, wbuf, 256, map_buf, 256); + WideCharToMultiByte(locinfo->lc_codepage, 0, map_buf, 256, (char *)locinfo->pcumap, 256, NULL, NULL); + } else { + locinfo->lc_clike = 1; + locinfo->mb_cur_max = 1; + locinfo->pctype = MSVCRT__ctype+1; + locinfo->pclmap = cloc_clmap; + locinfo->pcumap = cloc_cumap; + if(!init_category_name("C", 1, locinfo, LC_CTYPE)) { + goto fail; + } + } + + if(!category_needs_update(LC_MONETARY, old_locinfo, + locale_sname[LC_MONETARY], cp[LC_MONETARY]) && + !category_needs_update(LC_NUMERIC, old_locinfo, + locale_sname[LC_NUMERIC], cp[LC_NUMERIC])) { + locinfo->lconv = old_locinfo->lconv; + locinfo->lconv_intl_refcount = old_locinfo->lconv_intl_refcount; + if(locinfo->lconv_intl_refcount) + InterlockedIncrement((LONG *)locinfo->lconv_intl_refcount); + } else if(locale_sname[LC_MONETARY] || locale_sname[LC_NUMERIC]) { + locinfo->lconv = malloc(sizeof(struct lconv)); + locinfo->lconv_intl_refcount = malloc(sizeof(int)); + if(!locinfo->lconv || !locinfo->lconv_intl_refcount) { + free(locinfo->lconv); + free(locinfo->lconv_intl_refcount); + locinfo->lconv = NULL; + locinfo->lconv_intl_refcount = NULL; + goto fail; + } + memset(locinfo->lconv, 0, sizeof(struct lconv)); + *locinfo->lconv_intl_refcount = 1; + } else { + locinfo->lconv = &cloc_lconv; + } + + if(locale_name[LC_MONETARY] && + !init_category_name(locale_name[LC_MONETARY], + locale_len[LC_MONETARY], locinfo, LC_MONETARY)) { + goto fail; + } + + if(!category_needs_update(LC_MONETARY, old_locinfo, + locale_sname[LC_MONETARY], cp[LC_MONETARY])) { + copy_threadlocinfo_category(locinfo, old_locinfo, LC_MONETARY); + locinfo->lconv_mon_refcount = old_locinfo->lconv_mon_refcount; + if(locinfo->lconv_mon_refcount) + InterlockedIncrement((LONG *)locinfo->lconv_mon_refcount); + if(locinfo->lconv != &cloc_lconv && locinfo->lconv != old_locinfo->lconv) { + locinfo->lconv->int_curr_symbol = old_locinfo->lconv->int_curr_symbol; + locinfo->lconv->currency_symbol = old_locinfo->lconv->currency_symbol; + locinfo->lconv->mon_decimal_point = old_locinfo->lconv->mon_decimal_point; + locinfo->lconv->mon_thousands_sep = old_locinfo->lconv->mon_thousands_sep; + locinfo->lconv->mon_grouping = old_locinfo->lconv->mon_grouping; + locinfo->lconv->positive_sign = old_locinfo->lconv->positive_sign; + locinfo->lconv->negative_sign = old_locinfo->lconv->negative_sign; + locinfo->lconv->int_frac_digits = old_locinfo->lconv->int_frac_digits; + locinfo->lconv->frac_digits = old_locinfo->lconv->frac_digits; + locinfo->lconv->p_cs_precedes = old_locinfo->lconv->p_cs_precedes; + locinfo->lconv->p_sep_by_space = old_locinfo->lconv->p_sep_by_space; + locinfo->lconv->n_cs_precedes = old_locinfo->lconv->n_cs_precedes; + locinfo->lconv->n_sep_by_space = old_locinfo->lconv->n_sep_by_space; + locinfo->lconv->p_sign_posn = old_locinfo->lconv->p_sign_posn; + locinfo->lconv->n_sign_posn = old_locinfo->lconv->n_sign_posn; +#if _MSVCR_VER >= 100 + locinfo->lconv->_W_int_curr_symbol = old_locinfo->lconv->_W_int_curr_symbol; + locinfo->lconv->_W_currency_symbol = old_locinfo->lconv->_W_currency_symbol; + locinfo->lconv->_W_mon_decimal_point = old_locinfo->lconv->_W_mon_decimal_point; + locinfo->lconv->_W_mon_thousands_sep = old_locinfo->lconv->_W_mon_thousands_sep; + locinfo->lconv->_W_positive_sign = old_locinfo->lconv->_W_positive_sign; + locinfo->lconv->_W_negative_sign = old_locinfo->lconv->_W_negative_sign; +#endif + } + } else if(locale_sname[LC_MONETARY]) { + if(!update_threadlocinfo_category(locale_sname[LC_MONETARY], + cp[LC_MONETARY], locinfo, LC_MONETARY)) { + goto fail; + } + + locinfo->lconv_mon_refcount = malloc(sizeof(int)); + if(!locinfo->lconv_mon_refcount) { + goto fail; + } + + *locinfo->lconv_mon_refcount = 1; + + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SINTLSYMBOL + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL); + if(i && (locinfo->lconv->int_curr_symbol = malloc(i))) + WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->int_curr_symbol, i, NULL, NULL); + else { + goto fail; + } + + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SCURRENCY + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL); + if(i && (locinfo->lconv->currency_symbol = malloc(i))) + WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->currency_symbol, i, NULL, NULL); + else { + goto fail; + } + + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONDECIMALSEP + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL); + if(i && (locinfo->lconv->mon_decimal_point = malloc(i))) + WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->mon_decimal_point, i, NULL, NULL); + else { + goto fail; + } + + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONTHOUSANDSEP + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL); + if(i && (locinfo->lconv->mon_thousands_sep = malloc(i))) + WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->mon_thousands_sep, i, NULL, NULL); + else { + goto fail; + } + + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONGROUPING + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + WideCharToMultiByte(CP_ACP, 0, wbuf, -1, buf, 256, NULL, NULL); + if(i>1) + i = i/2 + (buf[i-2]=='0'?0:1); + if(i && (locinfo->lconv->mon_grouping = malloc(i))) { + for(i=0; buf[i+1]==';'; i+=2) + locinfo->lconv->mon_grouping[i/2] = buf[i]-'0'; + locinfo->lconv->mon_grouping[i/2] = buf[i]-'0'; + if(buf[i] != '0') + locinfo->lconv->mon_grouping[i/2+1] = 127; + } else { + goto fail; + } + + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SPOSITIVESIGN + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL); + if(i && (locinfo->lconv->positive_sign = malloc(i))) + WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->positive_sign, i, NULL, NULL); + else { + goto fail; + } + + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SNEGATIVESIGN + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + i = WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, NULL, 0, NULL, NULL); + if(i && (locinfo->lconv->negative_sign = malloc(i))) + WideCharToMultiByte(cp[LC_MONETARY], 0, wbuf, -1, locinfo->lconv->negative_sign, i, NULL, NULL); + else { + goto fail; + } + + if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IINTLCURRDIGITS + |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) + locinfo->lconv->int_frac_digits = val; + else { + goto fail; + } + + if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_ICURRDIGITS + |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) + locinfo->lconv->frac_digits = val; + else { + goto fail; + } + + if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IPOSSYMPRECEDES + |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) + locinfo->lconv->p_cs_precedes = val; + else { + goto fail; + } + + if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IPOSSEPBYSPACE + |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) + locinfo->lconv->p_sep_by_space = val; + else { + goto fail; + } + + if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_INEGSYMPRECEDES + |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) + locinfo->lconv->n_cs_precedes = val; + else { + goto fail; + } + + if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_INEGSEPBYSPACE + |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) + locinfo->lconv->n_sep_by_space = val; + else { + goto fail; + } + + if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_IPOSSIGNPOSN + |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) + locinfo->lconv->p_sign_posn = val; + else { + goto fail; + } + + if(GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_INEGSIGNPOSN + |LOCALE_NOUSEROVERRIDE|LOCALE_RETURN_NUMBER, (WCHAR *)&val, 2)) + locinfo->lconv->n_sign_posn = val; + else { + goto fail; + } + +#if _MSVCR_VER >= 100 + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SINTLSYMBOL + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + if(i && (locinfo->lconv->_W_int_curr_symbol = malloc(i * sizeof(wchar_t)))) + memcpy(locinfo->lconv->_W_int_curr_symbol, wbuf, i * sizeof(wchar_t)); + else { + goto fail; + } + + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SCURRENCY + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + if(i && (locinfo->lconv->_W_currency_symbol = malloc(i * sizeof(wchar_t)))) + memcpy(locinfo->lconv->_W_currency_symbol, wbuf, i * sizeof(wchar_t)); + else { + goto fail; + } + + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONDECIMALSEP + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + if(i && (locinfo->lconv->_W_mon_decimal_point = malloc(i * sizeof(wchar_t)))) + memcpy(locinfo->lconv->_W_mon_decimal_point, wbuf, i * sizeof(wchar_t)); + else { + goto fail; + } + + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SMONTHOUSANDSEP + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + if(i && (locinfo->lconv->_W_mon_thousands_sep = malloc(i * sizeof(wchar_t)))) + memcpy(locinfo->lconv->_W_mon_thousands_sep, wbuf, i * sizeof(wchar_t)); + else { + goto fail; + } + + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SPOSITIVESIGN + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + if(i && (locinfo->lconv->_W_positive_sign = malloc(i * sizeof(wchar_t)))) + memcpy(locinfo->lconv->_W_positive_sign, wbuf, i * sizeof(wchar_t)); + else { + goto fail; + } + + i = GetLocaleInfoEx(locale_sname[LC_MONETARY], LOCALE_SNEGATIVESIGN + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + if(i && (locinfo->lconv->_W_negative_sign = malloc(i * sizeof(wchar_t)))) + memcpy(locinfo->lconv->_W_negative_sign, wbuf, i * sizeof(wchar_t)); + else { + goto fail; + } +#endif + } else { + if (locinfo->lconv != &cloc_lconv) { + locinfo->lconv->int_curr_symbol = cloc_lconv.int_curr_symbol; + locinfo->lconv->currency_symbol = cloc_lconv.currency_symbol; + locinfo->lconv->mon_decimal_point = cloc_lconv.mon_decimal_point; + locinfo->lconv->mon_thousands_sep = cloc_lconv.mon_thousands_sep; + locinfo->lconv->mon_grouping = cloc_lconv.mon_grouping; + locinfo->lconv->positive_sign = cloc_lconv.positive_sign; + locinfo->lconv->negative_sign = cloc_lconv.negative_sign; + locinfo->lconv->int_frac_digits = cloc_lconv.int_frac_digits; + locinfo->lconv->frac_digits = cloc_lconv.frac_digits; + locinfo->lconv->p_cs_precedes = cloc_lconv.p_cs_precedes; + locinfo->lconv->p_sep_by_space = cloc_lconv.p_sep_by_space; + locinfo->lconv->n_cs_precedes = cloc_lconv.n_cs_precedes; + locinfo->lconv->n_sep_by_space = cloc_lconv.n_sep_by_space; + locinfo->lconv->p_sign_posn = cloc_lconv.p_sign_posn; + locinfo->lconv->n_sign_posn = cloc_lconv.n_sign_posn; + +#if _MSVCR_VER >= 100 + locinfo->lconv->_W_int_curr_symbol = cloc_lconv._W_int_curr_symbol; + locinfo->lconv->_W_currency_symbol = cloc_lconv._W_currency_symbol; + locinfo->lconv->_W_mon_decimal_point = cloc_lconv._W_mon_decimal_point; + locinfo->lconv->_W_mon_thousands_sep = cloc_lconv._W_mon_thousands_sep; + locinfo->lconv->_W_positive_sign = cloc_lconv._W_positive_sign; + locinfo->lconv->_W_negative_sign = cloc_lconv._W_negative_sign; +#endif + } + + if(!init_category_name("C", 1, locinfo, LC_MONETARY)) { + goto fail; + } + } + + if(locale_name[LC_NUMERIC] && + !init_category_name(locale_name[LC_NUMERIC], + locale_len[LC_NUMERIC], locinfo, LC_NUMERIC)) { + goto fail; + } + + if(!category_needs_update(LC_NUMERIC, old_locinfo, + locale_sname[LC_NUMERIC], cp[LC_NUMERIC])) { + copy_threadlocinfo_category(locinfo, old_locinfo, LC_NUMERIC); + locinfo->lconv_num_refcount = old_locinfo->lconv_num_refcount; + if(locinfo->lconv_num_refcount) + InterlockedIncrement((LONG *)locinfo->lconv_num_refcount); + if(locinfo->lconv != &cloc_lconv && locinfo->lconv != old_locinfo->lconv) { + locinfo->lconv->decimal_point = old_locinfo->lconv->decimal_point; + locinfo->lconv->thousands_sep = old_locinfo->lconv->thousands_sep; + locinfo->lconv->grouping = old_locinfo->lconv->grouping; +#if _MSVCR_VER >= 100 + locinfo->lconv->_W_decimal_point = old_locinfo->lconv->_W_decimal_point; + locinfo->lconv->_W_thousands_sep = old_locinfo->lconv->_W_thousands_sep; +#endif + } + } else if(locale_sname[LC_NUMERIC]) { + if(!update_threadlocinfo_category(locale_sname[LC_NUMERIC], + cp[LC_NUMERIC], locinfo, LC_NUMERIC)) { + goto fail; + } + + locinfo->lconv_num_refcount = malloc(sizeof(int)); + if(!locinfo->lconv_num_refcount) { + goto fail; + } + + *locinfo->lconv_num_refcount = 1; + + i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_SDECIMAL + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + i = WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, NULL, 0, NULL, NULL); + if(i && (locinfo->lconv->decimal_point = malloc(i))) + WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, locinfo->lconv->decimal_point, i, NULL, NULL); + else { + goto fail; + } + + i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_STHOUSAND + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + i = WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, NULL, 0, NULL, NULL); + if(i && (locinfo->lconv->thousands_sep = malloc(i))) + WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, locinfo->lconv->thousands_sep, i, NULL, NULL); + else { + goto fail; + } + + i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_SGROUPING + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + WideCharToMultiByte(cp[LC_NUMERIC], 0, wbuf, -1, buf, 256, NULL, NULL); + if(i>1) + i = i/2 + (buf[i-2]=='0'?0:1); + if(i && (locinfo->lconv->grouping = malloc(i))) { + for(i=0; buf[i+1]==';'; i+=2) + locinfo->lconv->grouping[i/2] = buf[i]-'0'; + locinfo->lconv->grouping[i/2] = buf[i]-'0'; + if(buf[i] != '0') + locinfo->lconv->grouping[i/2+1] = 127; + } else { + goto fail; + } + +#if _MSVCR_VER >= 100 + i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_SDECIMAL + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + if(i && (locinfo->lconv->_W_decimal_point = malloc(i * sizeof(wchar_t)))) + memcpy(locinfo->lconv->_W_decimal_point, wbuf, i * sizeof(wchar_t)); + else { + goto fail; + } + + i = GetLocaleInfoEx(locale_sname[LC_NUMERIC], LOCALE_STHOUSAND + |LOCALE_NOUSEROVERRIDE, wbuf, 256); + if(i && (locinfo->lconv->_W_thousands_sep = malloc(i * sizeof(wchar_t)))) + memcpy(locinfo->lconv->_W_thousands_sep, wbuf, i * sizeof(wchar_t)); + else { + goto fail; + } +#endif + } else { + if (locinfo->lconv != &cloc_lconv) { + locinfo->lconv->decimal_point = cloc_lconv.decimal_point; + locinfo->lconv->thousands_sep = cloc_lconv.thousands_sep; + locinfo->lconv->grouping = cloc_lconv.grouping; + +#if _MSVCR_VER >= 100 + locinfo->lconv->_W_decimal_point = cloc_lconv._W_decimal_point; + locinfo->lconv->_W_thousands_sep = cloc_lconv._W_thousands_sep; +#endif + } + + if (!init_category_name("C", 1, locinfo, LC_NUMERIC)) { + goto fail; + } + } + + if(locale_name[LC_TIME] && + !init_category_name(locale_name[LC_TIME], + locale_len[LC_TIME], locinfo, LC_TIME)) { + goto fail; + } + + if(!category_needs_update(LC_TIME, old_locinfo, + locale_sname[LC_TIME], cp[LC_TIME])) { + copy_threadlocinfo_category(locinfo, old_locinfo, LC_TIME); + locinfo->lc_time_curr = old_locinfo->lc_time_curr; + InterlockedIncrement(&locinfo->lc_time_curr->refcount); + } else if(locale_sname[LC_TIME]) { + if(!update_threadlocinfo_category(locale_sname[LC_TIME], + cp[LC_TIME], locinfo, LC_TIME)) { + goto fail; + } + + locinfo->lc_time_curr = create_time_data(locale_sname[LC_TIME]); + if(!locinfo->lc_time_curr) { + goto fail; + } + } else { + if(!init_category_name("C", 1, locinfo, LC_TIME)) { + goto fail; + } + locinfo->lc_time_curr = &cloc_time_data; + InterlockedIncrement(&locinfo->lc_time_curr->refcount); + } + + for (i = 0; i < LC_MAX; i++) + free(locale_sname[i]); + + return locinfo; + +fail: + free_locinfo(locinfo); + + for (i = 0; i < LC_MAX; i++) + free(locale_sname[i]); + + return NULL; +} + +/********************************************************************* + * _create_locale (MSVCRT.@) + */ +_locale_t CDECL _create_locale(int category, const char *locale) +{ + _locale_t loc; + + loc = malloc(sizeof(_locale_tstruct)); + if(!loc) + return NULL; + + loc->locinfo = create_locinfo(category, locale, NULL); + if(!loc->locinfo) { + free(loc); + return NULL; + } + + loc->mbcinfo = create_mbcinfo(loc->locinfo->lc_id[LC_CTYPE].wCodePage, + loc->locinfo->lc_handle[LC_CTYPE], NULL); + if(!loc->mbcinfo) { + free_locinfo(loc->locinfo); + free(loc); + return NULL; + } + return loc; +} + +#if _MSVCR_VER >= 110 +/********************************************************************* + * _wcreate_locale (MSVCR110.@) + */ +_locale_t CDECL _wcreate_locale(int category, const wchar_t *locale) +{ + _locale_t loc; + size_t len; + char *str; + + if(categoryLC_MAX || !locale) + return NULL; + + len = wcstombs(NULL, locale, 0); + if(len == -1) + return NULL; + if(!(str = malloc(++len))) + return NULL; + wcstombs(str, locale, len); + + loc = _create_locale(category, str); + + free(str); + return loc; +} +#endif + +/********************************************************************* + * setlocale (MSVCRT.@) + */ +char* CDECL setlocale(int category, const char* locale) +{ + thread_data_t *data = msvcrt_get_thread_data(); + pthreadlocinfo locinfo = get_locinfo(), newlocinfo; + int locale_flags; + + if(categoryLC_MAX) + return NULL; + + if(!locale) { + if(category == LC_ALL) + return construct_lc_all(locinfo); + + return locinfo->lc_category[category].locale; + } + + /* Make sure that locinfo is not updated by e.g. stricmp function */ + locale_flags = data->locale_flags; + data->locale_flags |= LOCALE_THREAD; + newlocinfo = create_locinfo(category, locale, locinfo); + data->locale_flags = locale_flags; + if(!newlocinfo) { + WARN("%d %s failed\n", category, locale); + return NULL; + } + + if(locale[0] != 'C' || locale[1] != '\0') + initial_locale = FALSE; + + if(data->locale_flags & LOCALE_THREAD) + { + if(data->locale_flags & LOCALE_FREE) + free_locinfo(data->locinfo); + data->locinfo = newlocinfo; + } + else + { + int i; + + _lock_locales(); + free_locinfo(MSVCRT_locale->locinfo); + MSVCRT_locale->locinfo = newlocinfo; + + MSVCRT___lc_codepage = newlocinfo->lc_codepage; + MSVCRT___lc_collate_cp = newlocinfo->lc_collate_cp; + MSVCRT___mb_cur_max = newlocinfo->mb_cur_max; + MSVCRT__pctype = newlocinfo->pctype; + for(i=LC_MIN; i<=LC_MAX; i++) + MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i]; + _unlock_locales(); + update_thread_locale(data); + } + + if(category == LC_ALL) + return construct_lc_all(data->locinfo); + + return data->locinfo->lc_category[category].locale; +} + +/********************************************************************* + * _wsetlocale (MSVCRT.@) + */ +wchar_t* CDECL _wsetlocale(int category, const wchar_t* wlocale) +{ + static wchar_t current_lc_all[MAX_LOCALE_LENGTH]; + + char *locale = NULL; + const char *ret; + size_t len; + + if(wlocale) { + len = wcstombs(NULL, wlocale, 0); + if(len == -1) + return NULL; + + locale = malloc(++len); + if(!locale) + return NULL; + + wcstombs(locale, wlocale, len); + } + + _lock_locales(); + ret = setlocale(category, locale); + free(locale); + + if(ret && mbstowcs(current_lc_all, ret, MAX_LOCALE_LENGTH)==-1) + ret = NULL; + + _unlock_locales(); + return ret ? current_lc_all : NULL; +} + +#if _MSVCR_VER >= 80 +/********************************************************************* + * _configthreadlocale (MSVCR80.@) + */ +int CDECL _configthreadlocale(int type) +{ + thread_data_t *data = msvcrt_get_thread_data(); + int ret; + + ret = (data->locale_flags & LOCALE_THREAD ? _ENABLE_PER_THREAD_LOCALE : + _DISABLE_PER_THREAD_LOCALE); + + if(type == _ENABLE_PER_THREAD_LOCALE) + data->locale_flags |= LOCALE_THREAD; + else if(type == _DISABLE_PER_THREAD_LOCALE) + data->locale_flags &= ~LOCALE_THREAD; + else if(type) + ret = -1; + + return ret; +} +#endif + +BOOL msvcrt_init_locale(void) +{ + int i; + + _lock_locales(); + MSVCRT_locale = _create_locale(0, "C"); + _unlock_locales(); + if(!MSVCRT_locale) + return FALSE; + + MSVCRT___lc_codepage = MSVCRT_locale->locinfo->lc_codepage; + MSVCRT___lc_collate_cp = MSVCRT_locale->locinfo->lc_collate_cp; + MSVCRT___mb_cur_max = MSVCRT_locale->locinfo->mb_cur_max; + MSVCRT__pctype = MSVCRT_locale->locinfo->pctype; + for(i=LC_MIN; i<=LC_MAX; i++) + MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i]; + _setmbcp(_MB_CP_ANSI); + return TRUE; +} + +#if _MSVCR_VER >= 120 +/********************************************************************* + * wctrans (MSVCR120.@) + */ +wctrans_t CDECL wctrans(const char *property) +{ + static const char str_tolower[] = "tolower"; + static const char str_toupper[] = "toupper"; + + if(!strcmp(property, str_tolower)) + return 2; + if(!strcmp(property, str_toupper)) + return 1; + return 0; +} +#endif diff --git a/dll/win32/msvcrt/lock.c b/dll/win32/msvcrt/lock.c new file mode 100644 index 00000000000..886bb625b10 --- /dev/null +++ b/dll/win32/msvcrt/lock.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2002, TransGaming Technologies Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include +#include + +#include "wine/debug.h" +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "msvcrt.h" +#include "mtdll.h" +#include "cxx.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +typedef struct +{ + BOOL bInit; + CRITICAL_SECTION crit; +} LOCKTABLEENTRY; + +static LOCKTABLEENTRY lock_table[ _TOTAL_LOCKS ]; + +static inline void msvcrt_mlock_set_entry_initialized( int locknum, BOOL initialized ) +{ + lock_table[ locknum ].bInit = initialized; +} + +static inline void msvcrt_initialize_mlock( int locknum ) +{ + InitializeCriticalSectionEx( &(lock_table[ locknum ].crit), 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO ); + lock_table[ locknum ].crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": LOCKTABLEENTRY.crit"); + msvcrt_mlock_set_entry_initialized( locknum, TRUE ); +} + +static inline void msvcrt_uninitialize_mlock( int locknum ) +{ + lock_table[ locknum ].crit.DebugInfo->Spare[0] = 0; + DeleteCriticalSection( &(lock_table[ locknum ].crit) ); + msvcrt_mlock_set_entry_initialized( locknum, FALSE ); +} + +/********************************************************************** + * msvcrt_init_mt_locks (internal) + * + * Initialize the table lock. All other locks will be initialized + * upon first use. + * + */ +void msvcrt_init_mt_locks(void) +{ + int i; + + TRACE( "initializing mtlocks\n" ); + + /* Initialize the table */ + for( i=0; i < _TOTAL_LOCKS; i++ ) + { + msvcrt_mlock_set_entry_initialized( i, FALSE ); + } + + /* Initialize our lock table lock */ + msvcrt_initialize_mlock( _LOCKTAB_LOCK ); +} + +/********************************************************************** + * _lock (MSVCRT.@) + */ +void CDECL _lock( int locknum ) +{ + TRACE( "(%d)\n", locknum ); + + /* If the lock doesn't exist yet, create it */ + if( lock_table[ locknum ].bInit == FALSE ) + { + /* Lock while we're changing the lock table */ + _lock( _LOCKTAB_LOCK ); + + /* Check again if we've got a bit of a race on lock creation */ + if( lock_table[ locknum ].bInit == FALSE ) + { + TRACE( ": creating lock #%d\n", locknum ); + msvcrt_initialize_mlock( locknum ); + } + + /* Unlock ourselves */ + _unlock( _LOCKTAB_LOCK ); + } + + EnterCriticalSection( &(lock_table[ locknum ].crit) ); +} + +/********************************************************************** + * _unlock (MSVCRT.@) + * + * NOTE: There is no error detection to make sure the lock exists and is acquired. + */ +void CDECL _unlock( int locknum ) +{ + TRACE( "(%d)\n", locknum ); + + LeaveCriticalSection( &(lock_table[ locknum ].crit) ); +} + +#if _MSVCR_VER == 110 +static LONG shared_ptr_lock; + +void __cdecl _Lock_shared_ptr_spin_lock(void) +{ + LONG l = 0; + + while(InterlockedCompareExchange(&shared_ptr_lock, 1, 0) != 0) { + if(l++ == 1000) { + Sleep(0); + l = 0; + } + } +} + +void __cdecl _Unlock_shared_ptr_spin_lock(void) +{ + shared_ptr_lock = 0; +} +#endif + +/********************************************************************** + * msvcrt_free_locks (internal) + * + * Uninitialize all mt locks. Assume that neither _lock or _unlock will + * be called once we're calling this routine (ie _LOCKTAB_LOCK can be deleted) + * + */ +void msvcrt_free_locks(void) +{ + int i; + + TRACE( ": uninitializing all mtlocks\n" ); + + /* Uninitialize the table */ + for( i=0; i < _TOTAL_LOCKS; i++ ) + { + if( lock_table[ i ].bInit ) + { + msvcrt_uninitialize_mlock( i ); + } + } +} diff --git a/dll/win32/msvcrt/main.c b/dll/win32/msvcrt/main.c new file mode 100644 index 00000000000..e4bcfe35681 --- /dev/null +++ b/dll/win32/msvcrt/main.c @@ -0,0 +1,175 @@ +/* + * msvcrt.dll initialisation functions + * + * Copyright 2000 Jon Griffiths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ +#include +#include "msvcrt.h" +#include "winternl.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +/* Index to TLS */ +DWORD msvcrt_tls_index; + +static const char* msvcrt_get_reason(DWORD reason) +{ + switch (reason) + { + case DLL_PROCESS_ATTACH: return "DLL_PROCESS_ATTACH"; + case DLL_PROCESS_DETACH: return "DLL_PROCESS_DETACH"; + case DLL_THREAD_ATTACH: return "DLL_THREAD_ATTACH"; + case DLL_THREAD_DETACH: return "DLL_THREAD_DETACH"; + } + return "UNKNOWN"; +} + +static inline BOOL msvcrt_init_tls(void) +{ + msvcrt_tls_index = TlsAlloc(); + + if (msvcrt_tls_index == TLS_OUT_OF_INDEXES) + { + ERR("TlsAlloc() failed!\n"); + return FALSE; + } + return TRUE; +} + +static inline BOOL msvcrt_free_tls(void) +{ + if (!TlsFree(msvcrt_tls_index)) + { + ERR("TlsFree() failed!\n"); + return FALSE; + } + return TRUE; +} + +static inline void msvcrt_free_tls_mem(void) +{ + thread_data_t *tls = TlsGetValue(msvcrt_tls_index); + + if (tls) + { + free(tls->efcvt_buffer); + free(tls->asctime_buffer); + free(tls->wasctime_buffer); + free(tls->strerror_buffer); + free(tls->wcserror_buffer); + free(tls->time_buffer); + free(tls->tmpnam_buffer); + free(tls->wtmpnam_buffer); + if(tls->locale_flags & LOCALE_FREE) { + free_locinfo(tls->locinfo); + free_mbcinfo(tls->mbcinfo); + } + } + HeapFree(GetProcessHeap(), 0, tls); +} + +/********************************************************************* + * Init + */ +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + TRACE("(%p, %s, %p) pid(%lx), tid(%lx), tls(%lu)\n", + hinstDLL, msvcrt_get_reason(fdwReason), lpvReserved, + GetCurrentProcessId(), GetCurrentThreadId(), + msvcrt_tls_index); + + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + msvcrt_init_exception(hinstDLL); + if(!msvcrt_init_heap()) + return FALSE; + if(!msvcrt_init_tls()) { + msvcrt_destroy_heap(); + return FALSE; + } + msvcrt_init_mt_locks(); + if(!msvcrt_init_locale()) { + msvcrt_free_locks(); + msvcrt_free_tls_mem(); + msvcrt_destroy_heap(); + return FALSE; + } +#if defined(__x86_64__) && _MSVCR_VER>=140 + if(!msvcrt_init_handler4()) { + msvcrt_free_locks(); + msvcrt_free_tls_mem(); + msvcrt_destroy_heap(); + _free_locale(MSVCRT_locale); + return FALSE; + } +#endif + msvcrt_init_math(hinstDLL); + msvcrt_init_io(); + msvcrt_init_args(); + msvcrt_init_signals(); +#if _MSVCR_VER >= 100 && _MSVCR_VER <= 120 + msvcrt_init_concurrency(hinstDLL); +#endif +#if _MSVCR_VER == 0 + /* don't allow unloading msvcrt, we can't setup file handles twice */ + LdrAddRefDll( LDR_ADDREF_DLL_PIN, hinstDLL ); +#elif _MSVCR_VER >= 80 + _set_printf_count_output(0); +#endif + msvcrt_init_clock(); + TRACE("finished process init\n"); + break; + case DLL_THREAD_ATTACH: +#if defined(__x86_64__) && _MSVCR_VER>=140 + msvcrt_attach_handler4(); +#endif + break; + case DLL_PROCESS_DETACH: + msvcrt_free_io(); + if (lpvReserved) break; + msvcrt_free_popen_data(); + msvcrt_free_locks(); + msvcrt_free_console(); + msvcrt_free_args(); + msvcrt_free_signals(); + msvcrt_free_tls_mem(); + if (!msvcrt_free_tls()) + return FALSE; +#if defined(__x86_64__) && _MSVCR_VER>=140 + msvcrt_free_handler4(); +#endif + _free_locale(MSVCRT_locale); +#if _MSVCR_VER >= 100 && _MSVCR_VER <= 120 + msvcrt_free_scheduler_thread(); + msvcrt_free_concurrency(); +#endif + msvcrt_destroy_heap(); + TRACE("finished process free\n"); + break; + case DLL_THREAD_DETACH: + msvcrt_free_tls_mem(); +#if _MSVCR_VER >= 100 && _MSVCR_VER <= 120 + msvcrt_free_scheduler_thread(); +#endif + TRACE("finished thread free\n"); + break; + } + return TRUE; +} diff --git a/dll/win32/msvcrt/math.c b/dll/win32/msvcrt/math.c new file mode 100644 index 00000000000..be345fb7a4a --- /dev/null +++ b/dll/win32/msvcrt/math.c @@ -0,0 +1,2973 @@ +/* + * msvcrt.dll math functions + * + * Copyright 2000 Jon Griffiths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + * + * + * For functions copied from musl libc (http://musl.libc.org/): + * ==================================================== + * Copyright 2005-2020 Rich Felker, et al. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * ==================================================== + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msvcrt.h" +#include "winternl.h" + +#include "wine/asm.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +#undef div +#undef ldiv + +#define _DOMAIN 1 /* domain error in argument */ +#define _SING 2 /* singularity */ +#define _OVERFLOW 3 /* range overflow */ +#define _UNDERFLOW 4 /* range underflow */ + +typedef int (CDECL *MSVCRT_matherr_func)(struct _exception *); + +static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL; + +BOOL sse2_supported; +static BOOL sse2_enabled; + +void msvcrt_init_math( void *module ) +{ + sse2_supported = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); +#if _MSVCR_VER <=71 + sse2_enabled = FALSE; +#else + sse2_enabled = sse2_supported; +#endif +} + +static inline double ret_nan( BOOL update_sw ) +{ + double x = 1.0; + if (!update_sw) return -NAN; + return (x - x) / (x - x); +} + +#define SET_X87_CW(MASK) \ + "subl $4, %esp\n\t" \ + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") \ + "fnstcw (%esp)\n\t" \ + "movw (%esp), %ax\n\t" \ + "movw %ax, 2(%esp)\n\t" \ + "testw $" #MASK ", %ax\n\t" \ + "jz 1f\n\t" \ + "andw $~" #MASK ", %ax\n\t" \ + "movw %ax, 2(%esp)\n\t" \ + "fldcw 2(%esp)\n\t" \ + "1:\n\t" + +#define RESET_X87_CW \ + "movw (%esp), %ax\n\t" \ + "cmpw %ax, 2(%esp)\n\t" \ + "je 1f\n\t" \ + "fstpl 8(%esp)\n\t" \ + "fldcw (%esp)\n\t" \ + "fldl 8(%esp)\n\t" \ + "fwait\n\t" \ + "1:\n\t" \ + "addl $4, %esp\n\t" \ + __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t") + +/********************************************************************* + * _matherr (CRTDLL.@) + */ +int CDECL _matherr(struct _exception *e) +{ + return 0; +} + + +double math_error(int type, const char *name, double arg1, double arg2, double retval) +{ + struct _exception exception = {type, (char *)name, arg1, arg2, retval}; + + TRACE("(%d, %s, %g, %g, %g)\n", type, debugstr_a(name), arg1, arg2, retval); + + if (MSVCRT_default_matherr_func && MSVCRT_default_matherr_func(&exception)) + return exception.retval; + + switch (type) + { + case 0: + /* don't set errno */ + break; + case _DOMAIN: + *_errno() = EDOM; + break; + case _SING: + case _OVERFLOW: + *_errno() = ERANGE; + break; + case _UNDERFLOW: + /* don't set errno */ + break; + default: + ERR("Unhandled math error!\n"); + } + + return exception.retval; +} + +/********************************************************************* + * __setusermatherr (MSVCRT.@) + */ +void CDECL __setusermatherr(MSVCRT_matherr_func func) +{ + MSVCRT_default_matherr_func = func; + TRACE("new matherr handler %p\n", func); +} + +/********************************************************************* + * _set_SSE2_enable (MSVCRT.@) + */ +int CDECL _set_SSE2_enable(int flag) +{ + sse2_enabled = flag && sse2_supported; + return sse2_enabled; +} + +#if defined(_WIN64) +# if _MSVCR_VER>=140 +/********************************************************************* + * _get_FMA3_enable (UCRTBASE.@) + */ +int CDECL _get_FMA3_enable(void) +{ + FIXME("() stub\n"); + return 0; +} +# endif + +# if _MSVCR_VER>=120 +/********************************************************************* + * _set_FMA3_enable (MSVCR120.@) + */ +int CDECL _set_FMA3_enable(int flag) +{ + FIXME("(%x) stub\n", flag); + return 0; +} +# endif +#endif + +#if !defined(__i386__) || _MSVCR_VER>=120 + +/********************************************************************* + * _chgsignf (MSVCRT.@) + */ +float CDECL _chgsignf( float num ) +{ + union { float f; UINT32 i; } u = { num }; + u.i ^= 0x80000000; + return u.f; +} + +#endif + +#ifndef __i386__ + +/********************************************************************* + * _fpclassf (MSVCRT.@) + */ +int CDECL _fpclassf( float num ) +{ + union { float f; UINT32 i; } u = { num }; + int e = u.i >> 23 & 0xff; + int s = u.i >> 31; + + switch (e) + { + case 0: + if (u.i << 1) return s ? _FPCLASS_ND : _FPCLASS_PD; + return s ? _FPCLASS_NZ : _FPCLASS_PZ; + case 0xff: + if (u.i << 9) return ((u.i >> 22) & 1) ? _FPCLASS_QNAN : _FPCLASS_SNAN; + return s ? _FPCLASS_NINF : _FPCLASS_PINF; + default: + return s ? _FPCLASS_NN : _FPCLASS_PN; + } +} + +/********************************************************************* + * _finitef (MSVCRT.@) + */ +int CDECL _finitef( float num ) +{ + union { float f; UINT32 i; } u = { num }; + return (u.i & 0x7fffffff) < 0x7f800000; +} + +/********************************************************************* + * _isnanf (MSVCRT.@) + */ +int CDECL _isnanf( float num ) +{ + union { float f; UINT32 i; } u = { num }; + return (u.i & 0x7fffffff) > 0x7f800000; +} + +/********************************************************************* + * atanf (MSVCRT.@) + */ +#if _MSVCR_VER == 0 /* other versions call atanf() directly */ +float CDECL MSVCRT_atanf( float x ) +{ + if (isnan(x)) return math_error(_DOMAIN, "atanf", x, 0, x); + return atanf( x ); +} +#endif + +#ifndef __i386__ +extern short CDECL _fdclass(float x); + +static BOOL sqrtf_validate( float *x ) +{ + short c = _fdclass(*x); + + if (c == FP_ZERO) return FALSE; + if (c == FP_NAN) return FALSE; + if (signbit(*x)) + { + *x = math_error(_DOMAIN, "sqrtf", *x, 0, ret_nan(TRUE)); + return FALSE; + } + if (c == FP_INFINITE) return FALSE; + return TRUE; +} + +#ifdef __arm64ec__ +static float __attribute__((naked)) CDECL asm_sqrtf(float x) +{ + asm( "fsqrt s0,s0; ret" ); +} +#elif defined __aarch64__ +float CDECL asm_sqrtf(float); +__ASM_GLOBAL_FUNC( asm_sqrtf, "fsqrt s0,s0; ret" ) +#elif defined __arm__ +float CDECL asm_sqrtf(float); +__ASM_GLOBAL_FUNC( asm_sqrtf, "vsqrt s0,s0; bx lr" ) +#elif defined __x86_64__ +float CDECL asm_sqrtf(float); +__ASM_GLOBAL_FUNC( asm_sqrtf, "sqrtss %xmm0, %xmm0; ret" ) +#endif +#endif + +/********************************************************************* + * sqrtf (MSVCRT.@) + */ +float CDECL MSVCRT_sqrtf( float x ) +{ +#ifndef __i386__ + if (!sqrtf_validate(&x)) + return x; + + return asm_sqrtf(x); +#else + return sqrtf( x ); +#endif +} + +/********************************************************************* + * tanhf (MSVCRT.@) + */ +#if _MSVCR_VER < 140 /* other versions call tanhf() directly */ +float CDECL MSVCRT_tanhf( float x ) +{ + if (isnan( x )) + { + *(UINT32*)&x |= 0x400000; + return math_error(_DOMAIN, "tanhf", x, 0, x); + } + return tanhf( x ); +} +#endif + +#endif + +/********************************************************************* + * asin (MSVCRT.@) + */ +#ifdef __i386__ +double CDECL x87_asin(double); +__ASM_GLOBAL_FUNC( x87_asin, + "fldl 4(%esp)\n\t" + SET_X87_CW(~0x37f) + "fld %st\n\t" + "fld1\n\t" + "fsubp\n\t" + "fld1\n\t" + "fadd %st(2)\n\t" + "fmulp\n\t" + "fsqrt\n\t" + "fpatan\n\t" + RESET_X87_CW + "ret" ) +#endif + +double CDECL MSVCRT_asin( double x ) +{ +#ifdef __i386__ + unsigned int x87_cw, sse2_cw; + unsigned int hx = *(ULONGLONG*)&x >> 32; + unsigned int ix = hx & 0x7fffffff; + + if (isnan(x)) return math_error(_DOMAIN, "asin", x, 0, x); + + /* |x| < 1 */ + if (ix < 0x3ff00000) + { + __control87_2(0, 0, &x87_cw, &sse2_cw); + if (!sse2_enabled || (x87_cw & _MCW_EM) != _MCW_EM + || (sse2_cw & (_MCW_EM | _MCW_RC)) != _MCW_EM) + return x87_asin(x); + } +#else + if (isnan(x)) return x; +#endif + + return asin( x ); +} + +/********************************************************************* + * atan (MSVCRT.@) + */ +#if _MSVCR_VER == 0 /* other versions call atan() directly */ +double CDECL MSVCRT_atan( double x ) +{ + if (isnan(x)) return math_error(_DOMAIN, "atan", x, 0, x); + return atan( x ); +} +#endif + +/********************************************************************* + * exp (MSVCRT.@) + */ +#if _MSVCR_VER == 0 /* other versions call exp() directly */ +double CDECL MSVCRT_exp( double x ) +{ + if (isnan( x )) return math_error(_DOMAIN, "exp", x, 0, 1.0 + x); + return exp( x ); +} +#endif + +extern short CDECL _dclass(double x); + +static BOOL sqrt_validate( double *x, BOOL update_sw ) +{ + short c = _dclass(*x); + + if (c == FP_ZERO) return FALSE; + if (c == FP_NAN) + { +#ifdef __i386__ + if (update_sw) + *x = math_error(_DOMAIN, "sqrt", *x, 0, *x); +#else + /* set signaling bit */ + *(ULONGLONG*)x |= 0x8000000000000ULL; +#endif + return FALSE; + } + if (signbit(*x)) + { + *x = math_error(_DOMAIN, "sqrt", *x, 0, ret_nan(update_sw)); + return FALSE; + } + if (c == FP_INFINITE) return FALSE; + return TRUE; +} + +#ifdef __arm64ec__ +static double __attribute__((naked)) CDECL asm_sqrt(double x) +{ + asm( "fsqrt d0,d0; ret" ); +} +#elif defined __aarch64__ +double CDECL asm_sqrt(double); +__ASM_GLOBAL_FUNC( asm_sqrt, "fsqrt d0,d0; ret" ) +#elif defined __arm__ +double CDECL asm_sqrt(double); +__ASM_GLOBAL_FUNC( asm_sqrt, "vsqrt d0,d0; bx lr" ) +#elif defined __x86_64__ +double CDECL asm_sqrt(double); +__ASM_GLOBAL_FUNC( asm_sqrt, "sqrtsd %xmm0, %xmm0; ret" ) +#elif defined __i386__ +double CDECL asm_sqrt(double); +__ASM_GLOBAL_FUNC( asm_sqrt, + "fldl 4(%esp)\n\t" + SET_X87_CW(0xc00) + "fsqrt\n\t" + RESET_X87_CW + "ret" ) +#endif + +/********************************************************************* + * sqrt (MSVCRT.@) + */ +double CDECL MSVCRT_sqrt( double x ) +{ + if (!sqrt_validate(&x, TRUE)) + return x; + + return asm_sqrt(x); +} + +/********************************************************************* + * tanh (MSVCRT.@) + */ +#if _MSVCR_VER < 140 /* other versions call tanh() directly */ +double CDECL MSVCRT_tanh( double x ) +{ + if (isnan( x )) + { + *(UINT64*)&x |= 0x0008000000000000ULL; + return math_error(_DOMAIN, "tanh", x, 0, x); + } + return tanh( x ); +} +#endif + +#if (defined(__GNUC__) || defined(__clang__)) && defined(__i386__) + +#define CREATE_FPU_FUNC1(name, call) \ + __ASM_GLOBAL_FUNC(name, \ + "pushl %ebp\n\t" \ + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") \ + __ASM_CFI(".cfi_rel_offset %ebp,0\n\t") \ + "movl %esp, %ebp\n\t" \ + __ASM_CFI(".cfi_def_cfa_register %ebp\n\t") \ + "subl $68, %esp\n\t" /* sizeof(double)*8 + sizeof(int) */ \ + "fstpl (%esp)\n\t" /* store function argument */ \ + "fwait\n\t" \ + "movl $1, %ecx\n\t" /* empty FPU stack */ \ + "1:\n\t" \ + "fxam\n\t" \ + "fstsw %ax\n\t" \ + "and $0x4500, %ax\n\t" \ + "cmp $0x4100, %ax\n\t" \ + "je 2f\n\t" \ + "fstpl (%esp,%ecx,8)\n\t" \ + "fwait\n\t" \ + "incl %ecx\n\t" \ + "jmp 1b\n\t" \ + "2:\n\t" \ + "movl %ecx, -4(%ebp)\n\t" \ + "call " __ASM_NAME( #call ) "\n\t" \ + "movl -4(%ebp), %ecx\n\t" \ + "fstpl (%esp)\n\t" /* save result */ \ + "3:\n\t" /* restore FPU stack */ \ + "decl %ecx\n\t" \ + "fldl (%esp,%ecx,8)\n\t" \ + "cmpl $0, %ecx\n\t" \ + "jne 3b\n\t" \ + "leave\n\t" \ + __ASM_CFI(".cfi_def_cfa %esp,4\n\t") \ + __ASM_CFI(".cfi_same_value %ebp\n\t") \ + "ret") + +#define CREATE_FPU_FUNC2(name, call) \ + __ASM_GLOBAL_FUNC(name, \ + "pushl %ebp\n\t" \ + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") \ + __ASM_CFI(".cfi_rel_offset %ebp,0\n\t") \ + "movl %esp, %ebp\n\t" \ + __ASM_CFI(".cfi_def_cfa_register %ebp\n\t") \ + "subl $68, %esp\n\t" /* sizeof(double)*8 + sizeof(int) */ \ + "fstpl 8(%esp)\n\t" /* store function argument */ \ + "fwait\n\t" \ + "fstpl (%esp)\n\t" \ + "fwait\n\t" \ + "movl $2, %ecx\n\t" /* empty FPU stack */ \ + "1:\n\t" \ + "fxam\n\t" \ + "fstsw %ax\n\t" \ + "and $0x4500, %ax\n\t" \ + "cmp $0x4100, %ax\n\t" \ + "je 2f\n\t" \ + "fstpl (%esp,%ecx,8)\n\t" \ + "fwait\n\t" \ + "incl %ecx\n\t" \ + "jmp 1b\n\t" \ + "2:\n\t" \ + "movl %ecx, -4(%ebp)\n\t" \ + "call " __ASM_NAME( #call ) "\n\t" \ + "movl -4(%ebp), %ecx\n\t" \ + "fstpl 8(%esp)\n\t" /* save result */ \ + "3:\n\t" /* restore FPU stack */ \ + "decl %ecx\n\t" \ + "fldl (%esp,%ecx,8)\n\t" \ + "cmpl $1, %ecx\n\t" \ + "jne 3b\n\t" \ + "leave\n\t" \ + __ASM_CFI(".cfi_def_cfa %esp,4\n\t") \ + __ASM_CFI(".cfi_same_value %ebp\n\t") \ + "ret") + +CREATE_FPU_FUNC1(_CIacos, acos) +CREATE_FPU_FUNC1(_CIasin, asin) +CREATE_FPU_FUNC1(_CIatan, atan) +CREATE_FPU_FUNC2(_CIatan2, atan2) +CREATE_FPU_FUNC1(_CIcos, cos) +CREATE_FPU_FUNC1(_CIcosh, cosh) +CREATE_FPU_FUNC1(_CIexp, exp) +CREATE_FPU_FUNC2(_CIfmod, fmod) +CREATE_FPU_FUNC1(_CIlog, log) +CREATE_FPU_FUNC1(_CIlog10, log10) +CREATE_FPU_FUNC2(_CIpow, pow) +CREATE_FPU_FUNC1(_CIsin, sin) +CREATE_FPU_FUNC1(_CIsinh, sinh) +CREATE_FPU_FUNC1(_CIsqrt, sqrt) +CREATE_FPU_FUNC1(_CItan, tan) +CREATE_FPU_FUNC1(_CItanh, tanh) + +__ASM_GLOBAL_FUNC(_ftol, + "pushl %ebp\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + __ASM_CFI(".cfi_rel_offset %ebp,0\n\t") + "movl %esp, %ebp\n\t" + __ASM_CFI(".cfi_def_cfa_register %ebp\n\t") + "subl $12, %esp\n\t" /* sizeof(LONGLONG) + 2*sizeof(WORD) */ + "fnstcw (%esp)\n\t" + "mov (%esp), %ax\n\t" + "or $0xc00, %ax\n\t" + "mov %ax, 2(%esp)\n\t" + "fldcw 2(%esp)\n\t" + "fistpq 4(%esp)\n\t" + "fldcw (%esp)\n\t" + "movl 4(%esp), %eax\n\t" + "movl 8(%esp), %edx\n\t" + "leave\n\t" + __ASM_CFI(".cfi_def_cfa %esp,4\n\t") + __ASM_CFI(".cfi_same_value %ebp\n\t") + "ret") + +#endif /* (defined(__GNUC__) || defined(__clang__)) && defined(__i386__) */ + +/********************************************************************* + * _fpclass (MSVCRT.@) + */ +int CDECL _fpclass(double num) +{ + union { double f; UINT64 i; } u = { num }; + int e = u.i >> 52 & 0x7ff; + int s = u.i >> 63; + + switch (e) + { + case 0: + if (u.i << 1) return s ? _FPCLASS_ND : _FPCLASS_PD; + return s ? _FPCLASS_NZ : _FPCLASS_PZ; + case 0x7ff: + if (u.i << 12) return ((u.i >> 51) & 1) ? _FPCLASS_QNAN : _FPCLASS_SNAN; + return s ? _FPCLASS_NINF : _FPCLASS_PINF; + default: + return s ? _FPCLASS_NN : _FPCLASS_PN; + } +} + +/********************************************************************* + * _rotl (MSVCRT.@) + */ +unsigned int CDECL MSVCRT__rotl(unsigned int num, int shift) +{ + shift &= 31; + return (num << shift) | (num >> (32-shift)); +} + +/********************************************************************* + * _lrotl (MSVCRT.@) + */ +__msvcrt_ulong CDECL MSVCRT__lrotl(__msvcrt_ulong num, int shift) +{ + shift &= 0x1f; + return (num << shift) | (num >> (32-shift)); +} + +/********************************************************************* + * _lrotr (MSVCRT.@) + */ +__msvcrt_ulong CDECL MSVCRT__lrotr(__msvcrt_ulong num, int shift) +{ + shift &= 0x1f; + return (num >> shift) | (num << (32-shift)); +} + +/********************************************************************* + * _rotr (MSVCRT.@) + */ +unsigned int CDECL MSVCRT__rotr(unsigned int num, int shift) +{ + shift &= 0x1f; + return (num >> shift) | (num << (32-shift)); +} + +/********************************************************************* + * _rotl64 (MSVCRT.@) + */ +unsigned __int64 CDECL MSVCRT__rotl64(unsigned __int64 num, int shift) +{ + shift &= 63; + return (num << shift) | (num >> (64-shift)); +} + +/********************************************************************* + * _rotr64 (MSVCRT.@) + */ +unsigned __int64 CDECL MSVCRT__rotr64(unsigned __int64 num, int shift) +{ + shift &= 63; + return (num >> shift) | (num << (64-shift)); +} + +/********************************************************************* + * abs (MSVCRT.@) + */ +int CDECL abs( int n ) +{ + return n >= 0 ? n : -n; +} + +/********************************************************************* + * labs (MSVCRT.@) + */ +__msvcrt_long CDECL labs( __msvcrt_long n ) +{ + return n >= 0 ? n : -n; +} + +#if _MSVCR_VER>=100 +/********************************************************************* + * llabs (MSVCR100.@) + */ +__int64 CDECL llabs( __int64 n ) +{ + return n >= 0 ? n : -n; +} +#endif + +#if _MSVCR_VER>=120 +/********************************************************************* + * imaxabs (MSVCR120.@) + */ +intmax_t CDECL imaxabs( intmax_t n ) +{ + return n >= 0 ? n : -n; +} +#endif + +/********************************************************************* + * _abs64 (MSVCRT.@) + */ +__int64 CDECL _abs64( __int64 n ) +{ + return n >= 0 ? n : -n; +} + +#if defined(__i386__) || defined(__x86_64__) + +static unsigned int get_mxcsr(void) +{ + unsigned int ret; +#ifdef __arm64ec__ + extern NTSTATUS (*__os_arm64x_get_x64_information)(ULONG,void*,void*); + __os_arm64x_get_x64_information( 0, &ret, NULL ); +#else + __asm__ __volatile__( "stmxcsr %0" : "=m" (ret) ); +#endif + return ret; +} + +static void set_mxcsr( unsigned int val ) +{ +#ifdef __arm64ec__ + extern NTSTATUS (*__os_arm64x_set_x64_information)(ULONG,ULONG_PTR,void*); + __os_arm64x_set_x64_information( 0, val, NULL ); +#else + __asm__ __volatile__( "ldmxcsr %0" : : "m" (val) ); +#endif +} + +static void _setfp_sse( unsigned int *cw, unsigned int cw_mask, + unsigned int *sw, unsigned int sw_mask ) +{ +#if defined(__GNUC__) || defined(__clang__) + unsigned int old_fpword, fpword = get_mxcsr(); + unsigned int flags; + + old_fpword = fpword; + + cw_mask &= _MCW_EM | _MCW_RC | _MCW_DN; + sw_mask &= _MCW_EM; + + if (sw) + { + flags = 0; + if (fpword & 0x1) flags |= _SW_INVALID; + if (fpword & 0x2) flags |= _SW_DENORMAL; + if (fpword & 0x4) flags |= _SW_ZERODIVIDE; + if (fpword & 0x8) flags |= _SW_OVERFLOW; + if (fpword & 0x10) flags |= _SW_UNDERFLOW; + if (fpword & 0x20) flags |= _SW_INEXACT; + + *sw = (flags & ~sw_mask) | (*sw & sw_mask); + TRACE("sse2 update sw %08x to %08x\n", flags, *sw); + fpword &= ~0x3f; + if (*sw & _SW_INVALID) fpword |= 0x1; + if (*sw & _SW_DENORMAL) fpword |= 0x2; + if (*sw & _SW_ZERODIVIDE) fpword |= 0x4; + if (*sw & _SW_OVERFLOW) fpword |= 0x8; + if (*sw & _SW_UNDERFLOW) fpword |= 0x10; + if (*sw & _SW_INEXACT) fpword |= 0x20; + *sw = flags; + } + + if (cw) + { + flags = 0; + if (fpword & 0x80) flags |= _EM_INVALID; + if (fpword & 0x100) flags |= _EM_DENORMAL; + if (fpword & 0x200) flags |= _EM_ZERODIVIDE; + if (fpword & 0x400) flags |= _EM_OVERFLOW; + if (fpword & 0x800) flags |= _EM_UNDERFLOW; + if (fpword & 0x1000) flags |= _EM_INEXACT; + switch (fpword & 0x6000) + { + case 0x6000: flags |= _RC_UP|_RC_DOWN; break; + case 0x4000: flags |= _RC_UP; break; + case 0x2000: flags |= _RC_DOWN; break; + } + switch (fpword & 0x8040) + { + case 0x0040: flags |= _DN_FLUSH_OPERANDS_SAVE_RESULTS; break; + case 0x8000: flags |= _DN_SAVE_OPERANDS_FLUSH_RESULTS; break; + case 0x8040: flags |= _DN_FLUSH; break; + } + + *cw = (flags & ~cw_mask) | (*cw & cw_mask); + TRACE("sse2 update cw %08x to %08x\n", flags, *cw); + fpword &= ~0xffc0; + if (*cw & _EM_INVALID) fpword |= 0x80; + if (*cw & _EM_DENORMAL) fpword |= 0x100; + if (*cw & _EM_ZERODIVIDE) fpword |= 0x200; + if (*cw & _EM_OVERFLOW) fpword |= 0x400; + if (*cw & _EM_UNDERFLOW) fpword |= 0x800; + if (*cw & _EM_INEXACT) fpword |= 0x1000; + switch (*cw & _MCW_RC) + { + case _RC_UP|_RC_DOWN: fpword |= 0x6000; break; + case _RC_UP: fpword |= 0x4000; break; + case _RC_DOWN: fpword |= 0x2000; break; + } + switch (*cw & _MCW_DN) + { + case _DN_FLUSH_OPERANDS_SAVE_RESULTS: fpword |= 0x0040; break; + case _DN_SAVE_OPERANDS_FLUSH_RESULTS: fpword |= 0x8000; break; + case _DN_FLUSH: fpword |= 0x8040; break; + } + + /* clear status word if anything changes */ + if (fpword != old_fpword && !sw) + { + TRACE("sse2 clear status word\n"); + fpword &= ~0x3f; + } + } + + if (fpword != old_fpword) set_mxcsr( fpword ); +#else + FIXME("not implemented\n"); + if (cw) *cw = 0; + if (sw) *sw = 0; +#endif +} +#endif + +static void _setfp( unsigned int *cw, unsigned int cw_mask, + unsigned int *sw, unsigned int sw_mask ) +{ +#if (defined(__GNUC__) || defined(__clang__)) && defined(__i386__) + unsigned long oldcw = 0, newcw = 0; + unsigned long oldsw = 0, newsw = 0; + unsigned int flags; + + cw_mask &= _MCW_EM | _MCW_IC | _MCW_RC | _MCW_PC; + sw_mask &= _MCW_EM; + + if (sw) + { + __asm__ __volatile__( "fstsw %0" : "=m" (newsw) ); + oldsw = newsw; + + flags = 0; + if (newsw & 0x1) flags |= _SW_INVALID; + if (newsw & 0x2) flags |= _SW_DENORMAL; + if (newsw & 0x4) flags |= _SW_ZERODIVIDE; + if (newsw & 0x8) flags |= _SW_OVERFLOW; + if (newsw & 0x10) flags |= _SW_UNDERFLOW; + if (newsw & 0x20) flags |= _SW_INEXACT; + + *sw = (flags & ~sw_mask) | (*sw & sw_mask); + TRACE("x86 update sw %08x to %08x\n", flags, *sw); + newsw &= ~0x3f; + if (*sw & _SW_INVALID) newsw |= 0x1; + if (*sw & _SW_DENORMAL) newsw |= 0x2; + if (*sw & _SW_ZERODIVIDE) newsw |= 0x4; + if (*sw & _SW_OVERFLOW) newsw |= 0x8; + if (*sw & _SW_UNDERFLOW) newsw |= 0x10; + if (*sw & _SW_INEXACT) newsw |= 0x20; + *sw = flags; + } + + if (cw) + { + __asm__ __volatile__( "fstcw %0" : "=m" (newcw) ); + oldcw = newcw; + + flags = 0; + if (newcw & 0x1) flags |= _EM_INVALID; + if (newcw & 0x2) flags |= _EM_DENORMAL; + if (newcw & 0x4) flags |= _EM_ZERODIVIDE; + if (newcw & 0x8) flags |= _EM_OVERFLOW; + if (newcw & 0x10) flags |= _EM_UNDERFLOW; + if (newcw & 0x20) flags |= _EM_INEXACT; + switch (newcw & 0xc00) + { + case 0xc00: flags |= _RC_UP|_RC_DOWN; break; + case 0x800: flags |= _RC_UP; break; + case 0x400: flags |= _RC_DOWN; break; + } + switch (newcw & 0x300) + { + case 0x0: flags |= _PC_24; break; + case 0x200: flags |= _PC_53; break; + case 0x300: flags |= _PC_64; break; + } + if (newcw & 0x1000) flags |= _IC_AFFINE; + + *cw = (flags & ~cw_mask) | (*cw & cw_mask); + TRACE("x86 update cw %08x to %08x\n", flags, *cw); + newcw &= ~0x1f3f; + if (*cw & _EM_INVALID) newcw |= 0x1; + if (*cw & _EM_DENORMAL) newcw |= 0x2; + if (*cw & _EM_ZERODIVIDE) newcw |= 0x4; + if (*cw & _EM_OVERFLOW) newcw |= 0x8; + if (*cw & _EM_UNDERFLOW) newcw |= 0x10; + if (*cw & _EM_INEXACT) newcw |= 0x20; + switch (*cw & _MCW_RC) + { + case _RC_UP|_RC_DOWN: newcw |= 0xc00; break; + case _RC_UP: newcw |= 0x800; break; + case _RC_DOWN: newcw |= 0x400; break; + } + switch (*cw & _MCW_PC) + { + case _PC_64: newcw |= 0x300; break; + case _PC_53: newcw |= 0x200; break; + case _PC_24: newcw |= 0x0; break; + } + if (*cw & _IC_AFFINE) newcw |= 0x1000; + } + + if (oldsw != newsw && (newsw & 0x3f)) + { + struct { + WORD control_word; + WORD unused1; + WORD status_word; + WORD unused2; + WORD tag_word; + WORD unused3; + DWORD instruction_pointer; + WORD code_segment; + WORD unused4; + DWORD operand_addr; + WORD data_segment; + WORD unused5; + } fenv; + + assert(cw); + + __asm__ __volatile__( "fnstenv %0" : "=m" (fenv) ); + fenv.control_word = newcw; + fenv.status_word = newsw; + __asm__ __volatile__( "fldenv %0" : : "m" (fenv) : "st", "st(1)", + "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)" ); + return; + } + + if (oldsw != newsw) + __asm__ __volatile__( "fnclex" ); + if (oldcw != newcw) + __asm__ __volatile__( "fldcw %0" : : "m" (newcw) ); +#elif defined(__x86_64__) + _setfp_sse(cw, cw_mask, sw, sw_mask); +#elif defined(__aarch64__) + ULONG_PTR old_fpsr = 0, fpsr = 0, old_fpcr = 0, fpcr = 0; + unsigned int flags; + + cw_mask &= _MCW_EM | _MCW_RC; + sw_mask &= _MCW_EM; + + if (sw) + { + __asm__ __volatile__( "mrs %0, fpsr" : "=r" (fpsr) ); + old_fpsr = fpsr; + + flags = 0; + if (fpsr & 0x1) flags |= _SW_INVALID; + if (fpsr & 0x2) flags |= _SW_ZERODIVIDE; + if (fpsr & 0x4) flags |= _SW_OVERFLOW; + if (fpsr & 0x8) flags |= _SW_UNDERFLOW; + if (fpsr & 0x10) flags |= _SW_INEXACT; + if (fpsr & 0x80) flags |= _SW_DENORMAL; + + *sw = (flags & ~sw_mask) | (*sw & sw_mask); + TRACE("aarch64 update sw %08x to %08x\n", flags, *sw); + fpsr &= ~0x9f; + if (*sw & _SW_INVALID) fpsr |= 0x1; + if (*sw & _SW_ZERODIVIDE) fpsr |= 0x2; + if (*sw & _SW_OVERFLOW) fpsr |= 0x4; + if (*sw & _SW_UNDERFLOW) fpsr |= 0x8; + if (*sw & _SW_INEXACT) fpsr |= 0x10; + if (*sw & _SW_DENORMAL) fpsr |= 0x80; + *sw = flags; + } + + if (cw) + { + __asm__ __volatile__( "mrs %0, fpcr" : "=r" (fpcr) ); + old_fpcr = fpcr; + + flags = 0; + if (!(fpcr & 0x100)) flags |= _EM_INVALID; + if (!(fpcr & 0x200)) flags |= _EM_ZERODIVIDE; + if (!(fpcr & 0x400)) flags |= _EM_OVERFLOW; + if (!(fpcr & 0x800)) flags |= _EM_UNDERFLOW; + if (!(fpcr & 0x1000)) flags |= _EM_INEXACT; + if (!(fpcr & 0x8000)) flags |= _EM_DENORMAL; + switch (fpcr & 0xc00000) + { + case 0x400000: flags |= _RC_UP; break; + case 0x800000: flags |= _RC_DOWN; break; + case 0xc00000: flags |= _RC_CHOP; break; + } + + *cw = (flags & ~cw_mask) | (*cw & cw_mask); + TRACE("aarch64 update cw %08x to %08x\n", flags, *cw); + fpcr &= ~0xc09f00ul; + if (!(*cw & _EM_INVALID)) fpcr |= 0x100; + if (!(*cw & _EM_ZERODIVIDE)) fpcr |= 0x200; + if (!(*cw & _EM_OVERFLOW)) fpcr |= 0x400; + if (!(*cw & _EM_UNDERFLOW)) fpcr |= 0x800; + if (!(*cw & _EM_INEXACT)) fpcr |= 0x1000; + if (!(*cw & _EM_DENORMAL)) fpcr |= 0x8000; + switch (*cw & _MCW_RC) + { + case _RC_CHOP: fpcr |= 0xc00000; break; + case _RC_UP: fpcr |= 0x400000; break; + case _RC_DOWN: fpcr |= 0x800000; break; + } + } + + /* mask exceptions if needed */ + if (old_fpcr != fpcr && ~(old_fpcr >> 8) & fpsr & 0x9f != fpsr & 0x9f) + { + ULONG_PTR mask = fpcr & ~0x9f00; + __asm__ __volatile__( "msr fpcr, %0" :: "r" (mask) ); + } + + if (old_fpsr != fpsr) + __asm__ __volatile__( "msr fpsr, %0" :: "r" (fpsr) ); + if (old_fpcr != fpcr) + __asm__ __volatile__( "msr fpcr, %0" :: "r" (fpcr) ); +#elif defined(__arm__) + DWORD old_fpscr, fpscr; + unsigned int flags; + + __asm__ __volatile__( "vmrs %0, fpscr" : "=r" (fpscr) ); + old_fpscr = fpscr; + + cw_mask &= _MCW_EM | _MCW_RC; + sw_mask &= _MCW_EM; + + if (sw) + { + flags = 0; + if (fpscr & 0x1) flags |= _SW_INVALID; + if (fpscr & 0x2) flags |= _SW_ZERODIVIDE; + if (fpscr & 0x4) flags |= _SW_OVERFLOW; + if (fpscr & 0x8) flags |= _SW_UNDERFLOW; + if (fpscr & 0x10) flags |= _SW_INEXACT; + if (fpscr & 0x80) flags |= _SW_DENORMAL; + + *sw = (flags & ~sw_mask) | (*sw & sw_mask); + TRACE("arm update sw %08x to %08x\n", flags, *sw); + fpscr &= ~0x9f; + if (*sw & _SW_INVALID) fpscr |= 0x1; + if (*sw & _SW_ZERODIVIDE) fpscr |= 0x2; + if (*sw & _SW_OVERFLOW) fpscr |= 0x4; + if (*sw & _SW_UNDERFLOW) fpscr |= 0x8; + if (*sw & _SW_INEXACT) fpscr |= 0x10; + if (*sw & _SW_DENORMAL) fpscr |= 0x80; + *sw = flags; + } + + if (cw) + { + flags = 0; + if (!(fpscr & 0x100)) flags |= _EM_INVALID; + if (!(fpscr & 0x200)) flags |= _EM_ZERODIVIDE; + if (!(fpscr & 0x400)) flags |= _EM_OVERFLOW; + if (!(fpscr & 0x800)) flags |= _EM_UNDERFLOW; + if (!(fpscr & 0x1000)) flags |= _EM_INEXACT; + if (!(fpscr & 0x8000)) flags |= _EM_DENORMAL; + switch (fpscr & 0xc00000) + { + case 0x400000: flags |= _RC_UP; break; + case 0x800000: flags |= _RC_DOWN; break; + case 0xc00000: flags |= _RC_CHOP; break; + } + + *cw = (flags & ~cw_mask) | (*cw & cw_mask); + TRACE("arm update cw %08x to %08x\n", flags, *cw); + fpscr &= ~0xc09f00ul; + if (!(*cw & _EM_INVALID)) fpscr |= 0x100; + if (!(*cw & _EM_ZERODIVIDE)) fpscr |= 0x200; + if (!(*cw & _EM_OVERFLOW)) fpscr |= 0x400; + if (!(*cw & _EM_UNDERFLOW)) fpscr |= 0x800; + if (!(*cw & _EM_INEXACT)) fpscr |= 0x1000; + if (!(*cw & _EM_DENORMAL)) fpscr |= 0x8000; + switch (*cw & _MCW_RC) + { + case _RC_CHOP: fpscr |= 0xc00000; break; + case _RC_UP: fpscr |= 0x400000; break; + case _RC_DOWN: fpscr |= 0x800000; break; + } + } + + if (old_fpscr != fpscr) + __asm__ __volatile__( "vmsr fpscr, %0" :: "r" (fpscr) ); +#else + FIXME("not implemented\n"); + if (cw) *cw = 0; + if (sw) *sw = 0; +#endif +} + +/********************************************************************** + * _statusfp2 (MSVCR80.@) + */ +#if defined(__i386__) +void CDECL _statusfp2( unsigned int *x86_sw, unsigned int *sse2_sw ) +{ + if (x86_sw) + _setfp(NULL, 0, x86_sw, 0); + if (!sse2_sw) return; + if (sse2_supported) + _setfp_sse(NULL, 0, sse2_sw, 0); + else *sse2_sw = 0; +} +#endif + +/********************************************************************** + * _statusfp (MSVCRT.@) + */ +unsigned int CDECL _statusfp(void) +{ + unsigned int flags = 0; +#if defined(__i386__) + unsigned int x86_sw, sse2_sw; + + _statusfp2( &x86_sw, &sse2_sw ); + /* FIXME: there's no definition for ambiguous status, just return all status bits for now */ + flags = x86_sw | sse2_sw; +#else + _setfp(NULL, 0, &flags, 0); +#endif + return flags; +} + +/********************************************************************* + * _clearfp (MSVCRT.@) + */ +unsigned int CDECL _clearfp(void) +{ + unsigned int flags = 0; +#ifdef __i386__ + _setfp(NULL, 0, &flags, _MCW_EM); + if (sse2_supported) + { + unsigned int sse_sw = 0; + + _setfp_sse(NULL, 0, &sse_sw, _MCW_EM); + flags |= sse_sw; + } +#else + _setfp(NULL, 0, &flags, _MCW_EM); +#endif + return flags; +} + +/********************************************************************* + * __fpecode (MSVCRT.@) + */ +int * CDECL __fpecode(void) +{ + return &msvcrt_get_thread_data()->fpecode; +} + +/********************************************************************* + * ldexp (MSVCRT.@) + */ +double CDECL ldexp(double num, int exp) +{ + double z = scalbn(num, exp); + + if (isfinite(num) && !isfinite(z)) + return math_error(_OVERFLOW, "ldexp", num, exp, z); + if (num && isfinite(num) && !z) + return math_error(_UNDERFLOW, "ldexp", num, exp, z); + return z; +} + +/********************************************************************* + * _cabs (MSVCRT.@) + */ +double CDECL _cabs(struct _complex num) +{ + return sqrt(num.x * num.x + num.y * num.y); +} + +/********************************************************************* + * _chgsign (MSVCRT.@) + */ +double CDECL _chgsign(double num) +{ + union { double f; UINT64 i; } u = { num }; + u.i ^= 1ull << 63; + return u.f; +} + +/********************************************************************* + * __control87_2 (MSVCR80.@) + * + * Not exported by native msvcrt, added in msvcr80. + */ +#ifdef __i386__ +int CDECL __control87_2( unsigned int newval, unsigned int mask, + unsigned int *x86_cw, unsigned int *sse2_cw ) +{ + if (x86_cw) + { + *x86_cw = newval; + _setfp(x86_cw, mask, NULL, 0); + } + + if (!sse2_cw) return 1; + + if (sse2_supported) + { + *sse2_cw = newval; + _setfp_sse(sse2_cw, mask, NULL, 0); + } + else *sse2_cw = 0; + + return 1; +} +#endif + +/********************************************************************* + * _control87 (MSVCRT.@) + */ +unsigned int CDECL _control87(unsigned int newval, unsigned int mask) +{ + unsigned int flags = 0; +#if defined(__i386__) && (_MSVCR_VER == 0 || _MSVCR_VER >= 80) + unsigned int sse2_cw; + + __control87_2( newval, mask, &flags, &sse2_cw ); + + if (sse2_supported) + { + if ((flags ^ sse2_cw) & (_MCW_EM | _MCW_RC)) flags |= _EM_AMBIGUOUS; + flags |= sse2_cw; + } +#else + flags = newval; + _setfp(&flags, mask, NULL, 0); +#endif + return flags; +} + +/********************************************************************* + * _controlfp (MSVCRT.@) + */ +unsigned int CDECL _controlfp(unsigned int newval, unsigned int mask) +{ + return _control87( newval, mask & ~_EM_DENORMAL ); +} + +/********************************************************************* + * _set_controlfp (MSVCRT.@) + */ +void CDECL _set_controlfp( unsigned int newval, unsigned int mask ) +{ + _controlfp( newval, mask ); +} + +/********************************************************************* + * _controlfp_s (MSVCRT.@) + */ +int CDECL _controlfp_s(unsigned int *cur, unsigned int newval, unsigned int mask) +{ + static const unsigned int all_flags = (_MCW_EM | _MCW_IC | _MCW_RC | + _MCW_PC | _MCW_DN); + unsigned int val; + + if (!MSVCRT_CHECK_PMT( !(newval & mask & ~all_flags) )) + { + if (cur) *cur = _controlfp( 0, 0 ); /* retrieve it anyway */ + return EINVAL; + } + val = _controlfp( newval, mask ); + if (cur) *cur = val; + return 0; +} + +#if _MSVCR_VER >= 140 && (defined(__i386__) || defined(__x86_64__)) +enum fenv_masks +{ + FENV_X_INVALID = 0x00100010, + FENV_X_DENORMAL = 0x00200020, + FENV_X_ZERODIVIDE = 0x00080008, + FENV_X_OVERFLOW = 0x00040004, + FENV_X_UNDERFLOW = 0x00020002, + FENV_X_INEXACT = 0x00010001, + FENV_X_AFFINE = 0x00004000, + FENV_X_UP = 0x00800200, + FENV_X_DOWN = 0x00400100, + FENV_X_24 = 0x00002000, + FENV_X_53 = 0x00001000, + FENV_Y_INVALID = 0x10000010, + FENV_Y_DENORMAL = 0x20000020, + FENV_Y_ZERODIVIDE = 0x08000008, + FENV_Y_OVERFLOW = 0x04000004, + FENV_Y_UNDERFLOW = 0x02000002, + FENV_Y_INEXACT = 0x01000001, + FENV_Y_UP = 0x80000200, + FENV_Y_DOWN = 0x40000100, + FENV_Y_FLUSH = 0x00000400, + FENV_Y_FLUSH_SAVE = 0x00000800 +}; + +/* encodes x87/sse control/status word in ulong */ +static __msvcrt_ulong fenv_encode(unsigned int x, unsigned int y) +{ + __msvcrt_ulong ret = 0; + +#ifdef __i386__ + if (x & _EM_INVALID) ret |= FENV_X_INVALID; + if (x & _EM_DENORMAL) ret |= FENV_X_DENORMAL; + if (x & _EM_ZERODIVIDE) ret |= FENV_X_ZERODIVIDE; + if (x & _EM_OVERFLOW) ret |= FENV_X_OVERFLOW; + if (x & _EM_UNDERFLOW) ret |= FENV_X_UNDERFLOW; + if (x & _EM_INEXACT) ret |= FENV_X_INEXACT; + if (x & _IC_AFFINE) ret |= FENV_X_AFFINE; + if (x & _RC_UP) ret |= FENV_X_UP; + if (x & _RC_DOWN) ret |= FENV_X_DOWN; + if (x & _PC_24) ret |= FENV_X_24; + if (x & _PC_53) ret |= FENV_X_53; +#endif + x &= ~(_MCW_EM | _MCW_IC | _MCW_RC | _MCW_PC); + + if (y & _EM_INVALID) ret |= FENV_Y_INVALID; + if (y & _EM_DENORMAL) ret |= FENV_Y_DENORMAL; + if (y & _EM_ZERODIVIDE) ret |= FENV_Y_ZERODIVIDE; + if (y & _EM_OVERFLOW) ret |= FENV_Y_OVERFLOW; + if (y & _EM_UNDERFLOW) ret |= FENV_Y_UNDERFLOW; + if (y & _EM_INEXACT) ret |= FENV_Y_INEXACT; + if (y & _RC_UP) ret |= FENV_Y_UP; + if (y & _RC_DOWN) ret |= FENV_Y_DOWN; + if (y & _DN_FLUSH) ret |= FENV_Y_FLUSH; + if (y & _DN_FLUSH_OPERANDS_SAVE_RESULTS) ret |= FENV_Y_FLUSH_SAVE; + y &= ~(_MCW_EM | _MCW_IC | _MCW_RC | _MCW_DN); + + if(x || y) FIXME("unsupported flags: %x, %x\n", x, y); + return ret; +} + +/* decodes x87/sse control/status word, returns FALSE on error */ +static BOOL fenv_decode(__msvcrt_ulong enc, unsigned int *x, unsigned int *y) +{ + *x = *y = 0; + if ((enc & FENV_X_INVALID) == FENV_X_INVALID) *x |= _EM_INVALID; + if ((enc & FENV_X_DENORMAL) == FENV_X_DENORMAL) *x |= _EM_DENORMAL; + if ((enc & FENV_X_ZERODIVIDE) == FENV_X_ZERODIVIDE) *x |= _EM_ZERODIVIDE; + if ((enc & FENV_X_OVERFLOW) == FENV_X_OVERFLOW) *x |= _EM_OVERFLOW; + if ((enc & FENV_X_UNDERFLOW) == FENV_X_UNDERFLOW) *x |= _EM_UNDERFLOW; + if ((enc & FENV_X_INEXACT) == FENV_X_INEXACT) *x |= _EM_INEXACT; + if ((enc & FENV_X_AFFINE) == FENV_X_AFFINE) *x |= _IC_AFFINE; + if ((enc & FENV_X_UP) == FENV_X_UP) *x |= _RC_UP; + if ((enc & FENV_X_DOWN) == FENV_X_DOWN) *x |= _RC_DOWN; + if ((enc & FENV_X_24) == FENV_X_24) *x |= _PC_24; + if ((enc & FENV_X_53) == FENV_X_53) *x |= _PC_53; + + if ((enc & FENV_Y_INVALID) == FENV_Y_INVALID) *y |= _EM_INVALID; + if ((enc & FENV_Y_DENORMAL) == FENV_Y_DENORMAL) *y |= _EM_DENORMAL; + if ((enc & FENV_Y_ZERODIVIDE) == FENV_Y_ZERODIVIDE) *y |= _EM_ZERODIVIDE; + if ((enc & FENV_Y_OVERFLOW) == FENV_Y_OVERFLOW) *y |= _EM_OVERFLOW; + if ((enc & FENV_Y_UNDERFLOW) == FENV_Y_UNDERFLOW) *y |= _EM_UNDERFLOW; + if ((enc & FENV_Y_INEXACT) == FENV_Y_INEXACT) *y |= _EM_INEXACT; + if ((enc & FENV_Y_UP) == FENV_Y_UP) *y |= _RC_UP; + if ((enc & FENV_Y_DOWN) == FENV_Y_DOWN) *y |= _RC_DOWN; + if ((enc & FENV_Y_FLUSH) == FENV_Y_FLUSH) *y |= _DN_FLUSH; + if ((enc & FENV_Y_FLUSH_SAVE) == FENV_Y_FLUSH_SAVE) *y |= _DN_FLUSH_OPERANDS_SAVE_RESULTS; + + if (fenv_encode(*x, *y) != enc) + { + WARN("can't decode: %lx\n", enc); + return FALSE; + } + return TRUE; +} +#elif _MSVCR_VER >= 120 +static __msvcrt_ulong fenv_encode(unsigned int x, unsigned int y) +{ + if (y & _EM_DENORMAL) + y = (y & ~_EM_DENORMAL) | 0x20; + + return x | y; +} + +static BOOL fenv_decode(__msvcrt_ulong enc, unsigned int *x, unsigned int *y) +{ + if (enc & 0x20) + enc = (enc & ~0x20) | _EM_DENORMAL; + + *x = *y = enc; + return TRUE; +} +#endif + +#if _MSVCR_VER>=120 +/********************************************************************* + * fegetenv (MSVCR120.@) + */ +int CDECL fegetenv(fenv_t *env) +{ +#if _MSVCR_VER>=140 && defined(__i386__) + unsigned int x87, sse; + __control87_2(0, 0, &x87, &sse); + env->_Fe_ctl = fenv_encode(x87, sse); + _statusfp2(&x87, &sse); + env->_Fe_stat = fenv_encode(x87, sse); +#elif _MSVCR_VER>=140 + env->_Fe_ctl = fenv_encode(0, _control87(0, 0)); + env->_Fe_stat = fenv_encode(0, _statusfp()); +#else + env->_Fe_ctl = _controlfp(0, 0) & (_EM_INEXACT | _EM_UNDERFLOW | + _EM_OVERFLOW | _EM_ZERODIVIDE | _EM_INVALID | _MCW_RC); + env->_Fe_stat = _statusfp(); +#endif + return 0; +} + +/********************************************************************* + * feupdateenv (MSVCR120.@) + */ +int CDECL feupdateenv(const fenv_t *env) +{ + fenv_t set; + fegetenv(&set); + set._Fe_ctl = env->_Fe_ctl; + set._Fe_stat |= env->_Fe_stat; + return fesetenv(&set); +} + +/********************************************************************* + * fetestexcept (MSVCR120.@) + */ +int CDECL fetestexcept(int flags) +{ + return _statusfp() & flags; +} + +/********************************************************************* + * fesetexceptflag (MSVCR120.@) + */ +int CDECL fesetexceptflag(const fexcept_t *status, int excepts) +{ + fenv_t env; + + excepts &= FE_ALL_EXCEPT; + if(!excepts) + return 0; + + fegetenv(&env); + env._Fe_stat &= ~fenv_encode(excepts, excepts); + env._Fe_stat |= *status & fenv_encode(excepts, excepts); + return fesetenv(&env); +} + +/********************************************************************* + * feraiseexcept (MSVCR120.@) + */ +int CDECL feraiseexcept(int flags) +{ + fenv_t env; + + flags &= FE_ALL_EXCEPT; + fegetenv(&env); + env._Fe_stat |= fenv_encode(flags, flags); + return fesetenv(&env); +} + +/********************************************************************* + * feclearexcept (MSVCR120.@) + */ +int CDECL feclearexcept(int flags) +{ + fenv_t env; + + fegetenv(&env); + flags &= FE_ALL_EXCEPT; + env._Fe_stat &= ~fenv_encode(flags, flags); + return fesetenv(&env); +} + +/********************************************************************* + * fegetexceptflag (MSVCR120.@) + */ +int CDECL fegetexceptflag(fexcept_t *status, int excepts) +{ +#if _MSVCR_VER>=140 && defined(__i386__) + unsigned int x87, sse; + _statusfp2(&x87, &sse); + *status = fenv_encode(x87 & excepts, sse & excepts); +#else + *status = fenv_encode(0, _statusfp() & excepts); +#endif + return 0; +} +#endif + +#if _MSVCR_VER>=140 +/********************************************************************* + * __fpe_flt_rounds (UCRTBASE.@) + */ +int CDECL __fpe_flt_rounds(void) +{ + unsigned int fpc = _controlfp(0, 0) & _RC_CHOP; + + TRACE("()\n"); + + switch(fpc) { + case _RC_CHOP: return 0; + case _RC_NEAR: return 1; + case _RC_UP: return 2; + default: return 3; + } +} +#endif + +#if _MSVCR_VER>=120 + +/********************************************************************* + * fegetround (MSVCR120.@) + */ +int CDECL fegetround(void) +{ + return _controlfp(0, 0) & _MCW_RC; +} + +/********************************************************************* + * fesetround (MSVCR120.@) + */ +int CDECL fesetround(int round_mode) +{ + if (round_mode & (~_MCW_RC)) + return 1; + _controlfp(round_mode, _MCW_RC); + return 0; +} + +#endif /* _MSVCR_VER>=120 */ + +/********************************************************************* + * _finite (MSVCRT.@) + */ +int CDECL _finite(double num) +{ + union { double f; UINT64 i; } u = { num }; + return (u.i & ~0ull >> 1) < 0x7ffull << 52; +} + +/********************************************************************* + * _fpreset (MSVCRT.@) + */ +void CDECL _fpreset(void) +{ +#if (defined(__GNUC__) || defined(__clang__)) && defined(__i386__) + const unsigned int x86_cw = 0x27f; + __asm__ __volatile__( "fninit; fldcw %0" : : "m" (x86_cw) ); + if (sse2_supported) + { + unsigned int cw = _MCW_EM, sw = 0; + _setfp_sse(&cw, ~0, &sw, ~0); + } +#else + unsigned int cw = _MCW_EM, sw = 0; + _setfp(&cw, ~0, &sw, ~0); +#endif +} + +#if _MSVCR_VER>=120 +/********************************************************************* + * feholdexcept (MSVCR120.@) + */ +int CDECL feholdexcept(fenv_t *env) +{ + TRACE( "(%p)\n", env ); + fegetenv(env); + return feclearexcept(FE_ALL_EXCEPT); +} + +/********************************************************************* + * fesetenv (MSVCR120.@) + */ +int CDECL fesetenv(const fenv_t *env) +{ + unsigned int x87_cw, cw, x87_stat, stat; + unsigned int mask; + + TRACE( "(%p)\n", env ); + + if (!env->_Fe_ctl && !env->_Fe_stat) { + _fpreset(); + return 0; + } + + if (!fenv_decode(env->_Fe_ctl, &x87_cw, &cw)) + return 1; + if (!fenv_decode(env->_Fe_stat, &x87_stat, &stat)) + return 1; + +#if _MSVCR_VER >= 140 + mask = ~0; +#else + mask = _EM_INEXACT | _EM_UNDERFLOW | _EM_OVERFLOW + | _EM_ZERODIVIDE | _EM_INVALID | _MCW_RC; +#endif + +#ifdef __i386__ + _setfp(&x87_cw, mask, &x87_stat, ~0); + if (sse2_supported) + _setfp_sse(&cw, mask, &stat, ~0); + return 0; +#else + _setfp(&cw, mask, &stat, ~0); + return 0; +#endif +} +#endif + +/********************************************************************* + * _isnan (MSVCRT.@) + */ +int CDECL _isnan(double num) +{ + union { double f; UINT64 i; } u = { num }; + return (u.i & ~0ull >> 1) > 0x7ffull << 52; +} + +#if _MSVCR_VER>=120 + +/********************************************************************* + * rint (MSVCR120.@) + */ +double CDECL MSVCRT_rint(double x) +{ + unsigned cw; + double y; + + cw = _controlfp(0, 0); + if ((cw & _MCW_PC) != _PC_53) + _controlfp(_PC_53, _MCW_PC); + y = rint(x); + if ((cw & _MCW_PC) != _PC_53) + _controlfp(cw, _MCW_PC); + return y; +} + +/********************************************************************* + * _nearbyint (MSVCR120.@) + * + * Based on musl: src/math/nearbyteint.c + */ +double CDECL nearbyint(double x) +{ + BOOL update_cw, update_sw; + unsigned int cw, sw; + + _setfp(&cw, 0, &sw, 0); + update_cw = !(cw & _EM_INEXACT); + update_sw = !(sw & _SW_INEXACT); + if (update_cw) + { + cw |= _EM_INEXACT; + _setfp(&cw, _EM_INEXACT, NULL, 0); + } + x = MSVCRT_rint(x); + if (update_cw || update_sw) + { + sw = 0; + cw &= ~_EM_INEXACT; + _setfp(update_cw ? &cw : NULL, _EM_INEXACT, + update_sw ? &sw : NULL, _SW_INEXACT); + } + return x; +} + +/********************************************************************* + * _nearbyintf (MSVCR120.@) + * + * Based on musl: src/math/nearbyteintf.c + */ +float CDECL nearbyintf(float x) +{ + BOOL update_cw, update_sw; + unsigned int cw, sw; + + _setfp(&cw, 0, &sw, 0); + update_cw = !(cw & _EM_INEXACT); + update_sw = !(sw & _SW_INEXACT); + if (update_cw) + { + cw |= _EM_INEXACT; + _setfp(&cw, _EM_INEXACT, NULL, 0); + } + x = rintf(x); + if (update_cw || update_sw) + { + sw = 0; + cw &= ~_EM_INEXACT; + _setfp(update_cw ? &cw : NULL, _EM_INEXACT, + update_sw ? &sw : NULL, _SW_INEXACT); + } + return x; +} + +#endif /* _MSVCR_VER>=120 */ + +/********************************************************************* + * _ecvt (MSVCRT.@) + */ +char * CDECL _ecvt( double number, int ndigits, int *decpt, int *sign ) +{ + int prec, len; + thread_data_t *data = msvcrt_get_thread_data(); + /* FIXME: check better for overflow (native supports over 300 chars) */ + ndigits = min( ndigits, 80 - 8); /* 8 : space for sign, dec point, "e", + * 4 for exponent and one for + * terminating '\0' */ + if (!data->efcvt_buffer) + data->efcvt_buffer = malloc( 80 ); /* ought to be enough */ + + /* handle cases with zero ndigits or less */ + prec = ndigits; + if( prec < 1) prec = 2; + len = _snprintf(data->efcvt_buffer, 80, "%.*le", prec - 1, number); + + if (data->efcvt_buffer[0] == '-') { + memmove( data->efcvt_buffer, data->efcvt_buffer + 1, len-- ); + *sign = 1; + } else *sign = 0; + + /* take the decimal "point away */ + if( prec != 1) + memmove( data->efcvt_buffer + 1, data->efcvt_buffer + 2, len - 1 ); + /* take the exponential "e" out */ + data->efcvt_buffer[ prec] = '\0'; + /* read the exponent */ + sscanf( data->efcvt_buffer + prec + 1, "%d", decpt); + (*decpt)++; + /* adjust for some border cases */ + if( data->efcvt_buffer[0] == '0')/* value is zero */ + *decpt = 0; + /* handle cases with zero ndigits or less */ + if( ndigits < 1){ + if( data->efcvt_buffer[ 0] >= '5') + (*decpt)++; + data->efcvt_buffer[ 0] = '\0'; + } + TRACE("out=\"%s\"\n",data->efcvt_buffer); + return data->efcvt_buffer; +} + +/********************************************************************* + * _ecvt_s (MSVCRT.@) + */ +int CDECL _ecvt_s( char *buffer, size_t length, double number, int ndigits, int *decpt, int *sign ) +{ + int prec, len; + char *result; + + if (!MSVCRT_CHECK_PMT(buffer != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(decpt != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(sign != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT_ERR( length > 2, ERANGE )) return ERANGE; + if (!MSVCRT_CHECK_PMT_ERR(ndigits < (int)length - 1, ERANGE )) return ERANGE; + + /* handle cases with zero ndigits or less */ + prec = ndigits; + if( prec < 1) prec = 2; + result = malloc(prec + 8); + + len = _snprintf(result, prec + 8, "%.*le", prec - 1, number); + if (result[0] == '-') { + memmove( result, result + 1, len-- ); + *sign = 1; + } else *sign = 0; + + /* take the decimal "point away */ + if( prec != 1) + memmove( result + 1, result + 2, len - 1 ); + /* take the exponential "e" out */ + result[ prec] = '\0'; + /* read the exponent */ + sscanf( result + prec + 1, "%d", decpt); + (*decpt)++; + /* adjust for some border cases */ + if( result[0] == '0')/* value is zero */ + *decpt = 0; + /* handle cases with zero ndigits or less */ + if( ndigits < 1){ + if( result[ 0] >= '5') + (*decpt)++; + result[ 0] = '\0'; + } + memcpy( buffer, result, max(ndigits + 1, 1) ); + free( result ); + return 0; +} + +/*********************************************************************** + * _fcvt (MSVCRT.@) + */ +char * CDECL _fcvt( double number, int ndigits, int *decpt, int *sign ) +{ + thread_data_t *data = msvcrt_get_thread_data(); + int stop, dec1, dec2; + char *ptr1, *ptr2, *first; + char buf[80]; /* ought to be enough */ + char decimal_separator = get_locinfo()->lconv->decimal_point[0]; + + if (!data->efcvt_buffer) + data->efcvt_buffer = malloc( 80 ); /* ought to be enough */ + + stop = _snprintf(buf, 80, "%.*f", ndigits < 0 ? 0 : ndigits, number); + ptr1 = buf; + ptr2 = data->efcvt_buffer; + first = NULL; + dec1 = 0; + dec2 = 0; + + if (*ptr1 == '-') { + *sign = 1; + ptr1++; + } else *sign = 0; + + /* For numbers below the requested resolution, work out where + the decimal point will be rather than finding it in the string */ + if (number < 1.0 && number > 0.0) { + dec2 = log10(number + 1e-10); + if (-dec2 <= ndigits) dec2 = 0; + } + + /* If requested digits is zero or less, we will need to truncate + * the returned string */ + if (ndigits < 1) { + stop += ndigits; + } + + while (*ptr1 == '0') ptr1++; /* Skip leading zeroes */ + while (*ptr1 != '\0' && *ptr1 != decimal_separator) { + if (!first) first = ptr2; + if ((ptr1 - buf) < stop) { + *ptr2++ = *ptr1++; + } else { + ptr1++; + } + dec1++; + } + + if (ndigits > 0) { + ptr1++; + if (!first) { + while (*ptr1 == '0') { /* Process leading zeroes */ + *ptr2++ = *ptr1++; + dec1--; + } + } + while (*ptr1 != '\0') { + if (!first) first = ptr2; + *ptr2++ = *ptr1++; + } + } + + *ptr2 = '\0'; + + /* We never found a non-zero digit, then our number is either + * smaller than the requested precision, or 0.0 */ + if (!first) { + if (number > 0.0) { + first = ptr2; + } else { + first = data->efcvt_buffer; + dec1 = 0; + } + } + + *decpt = dec2 ? dec2 : dec1; + return first; +} + +/*********************************************************************** + * _fcvt_s (MSVCRT.@) + */ +int CDECL _fcvt_s(char* outbuffer, size_t size, double number, int ndigits, int *decpt, int *sign) +{ + int stop, dec1, dec2; + char *ptr1, *ptr2, *first; + char buf[80]; /* ought to be enough */ + char decimal_separator = get_locinfo()->lconv->decimal_point[0]; + + if (!outbuffer || !decpt || !sign || size == 0) + { + *_errno() = EINVAL; + return EINVAL; + } + + stop = _snprintf(buf, 80, "%.*f", ndigits < 0 ? 0 : ndigits, number); + ptr1 = buf; + ptr2 = outbuffer; + first = NULL; + dec1 = 0; + dec2 = 0; + + if (*ptr1 == '-') { + *sign = 1; + ptr1++; + } else *sign = 0; + + /* For numbers below the requested resolution, work out where + the decimal point will be rather than finding it in the string */ + if (number < 1.0 && number > 0.0) { + dec2 = log10(number + 1e-10); + if (-dec2 <= ndigits) dec2 = 0; + } + + /* If requested digits is zero or less, we will need to truncate + * the returned string */ + if (ndigits < 1) { + stop += ndigits; + } + + while (*ptr1 == '0') ptr1++; /* Skip leading zeroes */ + while (*ptr1 != '\0' && *ptr1 != decimal_separator) { + if (!first) first = ptr2; + if ((ptr1 - buf) < stop) { + if (size > 1) { + *ptr2++ = *ptr1++; + size--; + } + } else { + ptr1++; + } + dec1++; + } + + if (ndigits > 0) { + ptr1++; + if (!first) { + while (*ptr1 == '0') { /* Process leading zeroes */ + if (number == 0.0 && size > 1) { + *ptr2++ = '0'; + size--; + } + ptr1++; + dec1--; + } + } + while (*ptr1 != '\0') { + if (!first) first = ptr2; + if (size > 1) { + *ptr2++ = *ptr1++; + size--; + } + } + } + + *ptr2 = '\0'; + + /* We never found a non-zero digit, then our number is either + * smaller than the requested precision, or 0.0 */ + if (!first && (number <= 0.0)) + dec1 = 0; + + *decpt = dec2 ? dec2 : dec1; + return 0; +} + +/*********************************************************************** + * _gcvt (MSVCRT.@) + */ +char * CDECL _gcvt( double number, int ndigit, char *buff ) +{ + if(!buff) { + *_errno() = EINVAL; + return NULL; + } + + if(ndigit < 0) { + *_errno() = ERANGE; + return NULL; + } + + sprintf(buff, "%.*g", ndigit, number); + return buff; +} + +/*********************************************************************** + * _gcvt_s (MSVCRT.@) + */ +int CDECL _gcvt_s(char *buff, size_t size, double number, int digits) +{ + int len; + + if(!buff) { + *_errno() = EINVAL; + return EINVAL; + } + + if( digits<0 || digits>=size) { + if(size) + buff[0] = '\0'; + + *_errno() = ERANGE; + return ERANGE; + } + + len = _scprintf("%.*g", digits, number); + if(len > size) { + buff[0] = '\0'; + *_errno() = ERANGE; + return ERANGE; + } + + sprintf(buff, "%.*g", digits, number); + return 0; +} + +#include /* div_t, ldiv_t */ + +/********************************************************************* + * div (MSVCRT.@) + * VERSION + * [i386] Windows binary compatible - returns the struct in eax/edx. + */ +#ifdef __i386__ +unsigned __int64 CDECL div(int num, int denom) +{ + union { + div_t div; + unsigned __int64 uint64; + } ret; + + ret.div.quot = num / denom; + ret.div.rem = num % denom; + return ret.uint64; +} +#else +/********************************************************************* + * div (MSVCRT.@) + * VERSION + * [!i386] Non-x86 can't run win32 apps so we don't need binary compatibility + */ +div_t CDECL div(int num, int denom) +{ + div_t ret; + + ret.quot = num / denom; + ret.rem = num % denom; + return ret; +} +#endif /* ifdef __i386__ */ + + +/********************************************************************* + * ldiv (MSVCRT.@) + * VERSION + * [i386] Windows binary compatible - returns the struct in eax/edx. + */ +#ifdef __i386__ +unsigned __int64 CDECL ldiv(__msvcrt_long num, __msvcrt_long denom) +{ + union { + ldiv_t ldiv; + unsigned __int64 uint64; + } ret; + + ret.ldiv.quot = num / denom; + ret.ldiv.rem = num % denom; + return ret.uint64; +} +#else +/********************************************************************* + * ldiv (MSVCRT.@) + * VERSION + * [!i386] Non-x86 can't run win32 apps so we don't need binary compatibility + */ +ldiv_t CDECL ldiv(__msvcrt_long num, __msvcrt_long denom) +{ + ldiv_t ret; + + ret.quot = num / denom; + ret.rem = num % denom; + return ret; +} +#endif /* ifdef __i386__ */ + +#if _MSVCR_VER>=100 +/********************************************************************* + * lldiv (MSVCR100.@) + */ +lldiv_t CDECL lldiv(__int64 num, __int64 denom) +{ + lldiv_t ret; + + ret.quot = num / denom; + ret.rem = num % denom; + + return ret; +} +#endif + +#ifdef __i386__ + +/********************************************************************* + * _adjust_fdiv (MSVCRT.@) + * Used by the MSVC compiler to work around the Pentium FDIV bug. + */ +int MSVCRT__adjust_fdiv = 0; + +/*********************************************************************** + * _adj_fdiv_m16i (MSVCRT.@) + * + * NOTE + * I _think_ this function is intended to work around the Pentium + * fdiv bug. + */ +void __stdcall _adj_fdiv_m16i( short arg ) +{ + TRACE("(): stub\n"); +} + +/*********************************************************************** + * _adj_fdiv_m32 (MSVCRT.@) + * + * NOTE + * I _think_ this function is intended to work around the Pentium + * fdiv bug. + */ +void __stdcall _adj_fdiv_m32( unsigned int arg ) +{ + TRACE("(): stub\n"); +} + +/*********************************************************************** + * _adj_fdiv_m32i (MSVCRT.@) + * + * NOTE + * I _think_ this function is intended to work around the Pentium + * fdiv bug. + */ +void __stdcall _adj_fdiv_m32i( int arg ) +{ + TRACE("(): stub\n"); +} + +/*********************************************************************** + * _adj_fdiv_m64 (MSVCRT.@) + * + * NOTE + * I _think_ this function is intended to work around the Pentium + * fdiv bug. + */ +void __stdcall _adj_fdiv_m64( unsigned __int64 arg ) +{ + TRACE("(): stub\n"); +} + +/*********************************************************************** + * _adj_fdiv_r (MSVCRT.@) + * FIXME + * This function is likely to have the wrong number of arguments. + * + * NOTE + * I _think_ this function is intended to work around the Pentium + * fdiv bug. + */ +void _adj_fdiv_r(void) +{ + TRACE("(): stub\n"); +} + +/*********************************************************************** + * _adj_fdivr_m16i (MSVCRT.@) + * + * NOTE + * I _think_ this function is intended to work around the Pentium + * fdiv bug. + */ +void __stdcall _adj_fdivr_m16i( short arg ) +{ + TRACE("(): stub\n"); +} + +/*********************************************************************** + * _adj_fdivr_m32 (MSVCRT.@) + * + * NOTE + * I _think_ this function is intended to work around the Pentium + * fdiv bug. + */ +void __stdcall _adj_fdivr_m32( unsigned int arg ) +{ + TRACE("(): stub\n"); +} + +/*********************************************************************** + * _adj_fdivr_m32i (MSVCRT.@) + * + * NOTE + * I _think_ this function is intended to work around the Pentium + * fdiv bug. + */ +void __stdcall _adj_fdivr_m32i( int arg ) +{ + TRACE("(): stub\n"); +} + +/*********************************************************************** + * _adj_fdivr_m64 (MSVCRT.@) + * + * NOTE + * I _think_ this function is intended to work around the Pentium + * fdiv bug. + */ +void __stdcall _adj_fdivr_m64( unsigned __int64 arg ) +{ + TRACE("(): stub\n"); +} + +/*********************************************************************** + * _adj_fpatan (MSVCRT.@) + * FIXME + * This function is likely to have the wrong number of arguments. + * + * NOTE + * I _think_ this function is intended to work around the Pentium + * fdiv bug. + */ +void _adj_fpatan(void) +{ + TRACE("(): stub\n"); +} + +/*********************************************************************** + * _adj_fprem (MSVCRT.@) + * FIXME + * This function is likely to have the wrong number of arguments. + * + * NOTE + * I _think_ this function is intended to work around the Pentium + * fdiv bug. + */ +void _adj_fprem(void) +{ + TRACE("(): stub\n"); +} + +/*********************************************************************** + * _adj_fprem1 (MSVCRT.@) + * FIXME + * This function is likely to have the wrong number of arguments. + * + * NOTE + * I _think_ this function is intended to work around the Pentium + * fdiv bug. + */ +void _adj_fprem1(void) +{ + TRACE("(): stub\n"); +} + +/*********************************************************************** + * _adj_fptan (MSVCRT.@) + * FIXME + * This function is likely to have the wrong number of arguments. + * + * NOTE + * I _think_ this function is intended to work around the Pentium + * fdiv bug. + */ +void _adj_fptan(void) +{ + TRACE("(): stub\n"); +} + +/*********************************************************************** + * _safe_fdiv (MSVCRT.@) + * FIXME + * This function is likely to have the wrong number of arguments. + * + * NOTE + * I _think_ this function is intended to work around the Pentium + * fdiv bug. + */ +void _safe_fdiv(void) +{ + TRACE("(): stub\n"); +} + +/*********************************************************************** + * _safe_fdivr (MSVCRT.@) + * FIXME + * This function is likely to have the wrong number of arguments. + * + * NOTE + * I _think_ this function is intended to work around the Pentium + * fdiv bug. + */ +void _safe_fdivr(void) +{ + TRACE("(): stub\n"); +} + +/*********************************************************************** + * _safe_fprem (MSVCRT.@) + * FIXME + * This function is likely to have the wrong number of arguments. + * + * NOTE + * I _think_ this function is intended to work around the Pentium + * fdiv bug. + */ +void _safe_fprem(void) +{ + TRACE("(): stub\n"); +} + +/*********************************************************************** + * _safe_fprem1 (MSVCRT.@) + * + * FIXME + * This function is likely to have the wrong number of arguments. + * + * NOTE + * I _think_ this function is intended to work around the Pentium + * fdiv bug. + */ +void _safe_fprem1(void) +{ + TRACE("(): stub\n"); +} + +/*********************************************************************** + * __libm_sse2_acos (MSVCRT.@) + */ +void __cdecl __libm_sse2_acos(void) +{ + double d; + __asm__ __volatile__( "movq %%xmm0,%0" : "=m" (d) ); + d = acos( d ); + __asm__ __volatile__( "movq %0,%%xmm0" : : "m" (d) ); +} + +/*********************************************************************** + * __libm_sse2_acosf (MSVCRT.@) + */ +void __cdecl __libm_sse2_acosf(void) +{ + float f; + __asm__ __volatile__( "movd %%xmm0,%0" : "=g" (f) ); + f = acosf( f ); + __asm__ __volatile__( "movd %0,%%xmm0" : : "g" (f) ); +} + +/*********************************************************************** + * __libm_sse2_asin (MSVCRT.@) + */ +void __cdecl __libm_sse2_asin(void) +{ + double d; + __asm__ __volatile__( "movq %%xmm0,%0" : "=m" (d) ); + d = asin( d ); + __asm__ __volatile__( "movq %0,%%xmm0" : : "m" (d) ); +} + +/*********************************************************************** + * __libm_sse2_asinf (MSVCRT.@) + */ +void __cdecl __libm_sse2_asinf(void) +{ + float f; + __asm__ __volatile__( "movd %%xmm0,%0" : "=g" (f) ); + f = asinf( f ); + __asm__ __volatile__( "movd %0,%%xmm0" : : "g" (f) ); +} + +/*********************************************************************** + * __libm_sse2_atan (MSVCRT.@) + */ +void __cdecl __libm_sse2_atan(void) +{ + double d; + __asm__ __volatile__( "movq %%xmm0,%0" : "=m" (d) ); + d = atan( d ); + __asm__ __volatile__( "movq %0,%%xmm0" : : "m" (d) ); +} + +/*********************************************************************** + * __libm_sse2_atan2 (MSVCRT.@) + */ +void __cdecl __libm_sse2_atan2(void) +{ + double d1, d2; + __asm__ __volatile__( "movq %%xmm0,%0; movq %%xmm1,%1 " : "=m" (d1), "=m" (d2) ); + d1 = atan2( d1, d2 ); + __asm__ __volatile__( "movq %0,%%xmm0" : : "m" (d1) ); +} + +/*********************************************************************** + * __libm_sse2_atanf (MSVCRT.@) + */ +void __cdecl __libm_sse2_atanf(void) +{ + float f; + __asm__ __volatile__( "movd %%xmm0,%0" : "=g" (f) ); + f = atanf( f ); + __asm__ __volatile__( "movd %0,%%xmm0" : : "g" (f) ); +} + +/*********************************************************************** + * __libm_sse2_cos (MSVCRT.@) + */ +void __cdecl __libm_sse2_cos(void) +{ + double d; + __asm__ __volatile__( "movq %%xmm0,%0" : "=m" (d) ); + d = cos( d ); + __asm__ __volatile__( "movq %0,%%xmm0" : : "m" (d) ); +} + +/*********************************************************************** + * __libm_sse2_cosf (MSVCRT.@) + */ +void __cdecl __libm_sse2_cosf(void) +{ + float f; + __asm__ __volatile__( "movd %%xmm0,%0" : "=g" (f) ); + f = cosf( f ); + __asm__ __volatile__( "movd %0,%%xmm0" : : "g" (f) ); +} + +/*********************************************************************** + * __libm_sse2_exp (MSVCRT.@) + */ +void __cdecl __libm_sse2_exp(void) +{ + double d; + __asm__ __volatile__( "movq %%xmm0,%0" : "=m" (d) ); + d = exp( d ); + __asm__ __volatile__( "movq %0,%%xmm0" : : "m" (d) ); +} + +/*********************************************************************** + * __libm_sse2_expf (MSVCRT.@) + */ +void __cdecl __libm_sse2_expf(void) +{ + float f; + __asm__ __volatile__( "movd %%xmm0,%0" : "=g" (f) ); + f = expf( f ); + __asm__ __volatile__( "movd %0,%%xmm0" : : "g" (f) ); +} + +/*********************************************************************** + * __libm_sse2_log (MSVCRT.@) + */ +void __cdecl __libm_sse2_log(void) +{ + double d; + __asm__ __volatile__( "movq %%xmm0,%0" : "=m" (d) ); + d = log( d ); + __asm__ __volatile__( "movq %0,%%xmm0" : : "m" (d) ); +} + +/*********************************************************************** + * __libm_sse2_log10 (MSVCRT.@) + */ +void __cdecl __libm_sse2_log10(void) +{ + double d; + __asm__ __volatile__( "movq %%xmm0,%0" : "=m" (d) ); + d = log10( d ); + __asm__ __volatile__( "movq %0,%%xmm0" : : "m" (d) ); +} + +/*********************************************************************** + * __libm_sse2_log10f (MSVCRT.@) + */ +void __cdecl __libm_sse2_log10f(void) +{ + float f; + __asm__ __volatile__( "movd %%xmm0,%0" : "=g" (f) ); + f = log10f( f ); + __asm__ __volatile__( "movd %0,%%xmm0" : : "g" (f) ); +} + +/*********************************************************************** + * __libm_sse2_logf (MSVCRT.@) + */ +void __cdecl __libm_sse2_logf(void) +{ + float f; + __asm__ __volatile__( "movd %%xmm0,%0" : "=g" (f) ); + f = logf( f ); + __asm__ __volatile__( "movd %0,%%xmm0" : : "g" (f) ); +} + +/*********************************************************************** + * __libm_sse2_pow (MSVCRT.@) + */ +void __cdecl __libm_sse2_pow(void) +{ + double d1, d2; + __asm__ __volatile__( "movq %%xmm0,%0; movq %%xmm1,%1 " : "=m" (d1), "=m" (d2) ); + d1 = pow( d1, d2 ); + __asm__ __volatile__( "movq %0,%%xmm0" : : "m" (d1) ); +} + +/*********************************************************************** + * __libm_sse2_powf (MSVCRT.@) + */ +void __cdecl __libm_sse2_powf(void) +{ + float f1, f2; + __asm__ __volatile__( "movd %%xmm0,%0; movd %%xmm1,%1" : "=g" (f1), "=g" (f2) ); + f1 = powf( f1, f2 ); + __asm__ __volatile__( "movd %0,%%xmm0" : : "g" (f1) ); +} + +/*********************************************************************** + * __libm_sse2_sin (MSVCRT.@) + */ +void __cdecl __libm_sse2_sin(void) +{ + double d; + __asm__ __volatile__( "movq %%xmm0,%0" : "=m" (d) ); + d = sin( d ); + __asm__ __volatile__( "movq %0,%%xmm0" : : "m" (d) ); +} + +/*********************************************************************** + * __libm_sse2_sinf (MSVCRT.@) + */ +void __cdecl __libm_sse2_sinf(void) +{ + float f; + __asm__ __volatile__( "movd %%xmm0,%0" : "=g" (f) ); + f = sinf( f ); + __asm__ __volatile__( "movd %0,%%xmm0" : : "g" (f) ); +} + +/*********************************************************************** + * __libm_sse2_tan (MSVCRT.@) + */ +void __cdecl __libm_sse2_tan(void) +{ + double d; + __asm__ __volatile__( "movq %%xmm0,%0" : "=m" (d) ); + d = tan( d ); + __asm__ __volatile__( "movq %0,%%xmm0" : : "m" (d) ); +} + +/*********************************************************************** + * __libm_sse2_tanf (MSVCRT.@) + */ +void __cdecl __libm_sse2_tanf(void) +{ + float f; + __asm__ __volatile__( "movd %%xmm0,%0" : "=g" (f) ); + f = tanf( f ); + __asm__ __volatile__( "movd %0,%%xmm0" : : "g" (f) ); +} + +/*********************************************************************** + * __libm_sse2_sqrt_precise (MSVCR110.@) + */ +void __cdecl __libm_sse2_sqrt_precise(void) +{ + unsigned int cw; + double d; + + __asm__ __volatile__( "movq %%xmm0,%0" : "=m" (d) ); + __control87_2(0, 0, NULL, &cw); + if (cw & _MCW_RC) + { + d = sqrt(d); + __asm__ __volatile__( "movq %0,%%xmm0" : : "m" (d) ); + return; + } + + if (!sqrt_validate(&d, FALSE)) + { + __asm__ __volatile__( "movq %0,%%xmm0" : : "m" (d) ); + return; + } + __asm__ __volatile__( "sqrtsd %xmm0, %xmm0" ); +} +#endif /* __i386__ */ + +#if _MSVCR_VER>=120 + +/********************************************************************* + * lrint (MSVCR120.@) + */ +__msvcrt_long CDECL lrint(double x) +{ + double d; + + d = MSVCRT_rint(x); + if ((d < 0 && d != (double)(__msvcrt_long)d) + || (d >= 0 && d != (double)(__msvcrt_ulong)d)) { + *_errno() = EDOM; + return 0; + } + return d; +} + +/********************************************************************* + * lrintf (MSVCR120.@) + */ +__msvcrt_long CDECL lrintf(float x) +{ + float f; + + f = rintf(x); + if ((f < 0 && f != (float)(__msvcrt_long)f) + || (f >= 0 && f != (float)(__msvcrt_ulong)f)) { + *_errno() = EDOM; + return 0; + } + return f; +} + +/********************************************************************* + * llrint (MSVCR120.@) + */ +__int64 CDECL llrint(double x) +{ + double d; + + d = MSVCRT_rint(x); + if ((d < 0 && d != (double)(__int64)d) + || (d >= 0 && d != (double)(unsigned __int64)d)) { + *_errno() = EDOM; + return 0; + } + return d; +} + +/********************************************************************* + * llrintf (MSVCR120.@) + */ +__int64 CDECL llrintf(float x) +{ + float f; + + f = rintf(x); + if ((f < 0 && f != (float)(__int64)f) + || (f >= 0 && f != (float)(unsigned __int64)f)) { + *_errno() = EDOM; + return 0; + } + return f; +} + +/********************************************************************* + * lround (MSVCR120.@) + * + * Copied from musl: src/math/lround.c + */ +__msvcrt_long CDECL lround(double x) +{ + double d = round(x); + if (d != (double)(__msvcrt_long)d) { + *_errno() = EDOM; + return 0; + } + return d; +} + +/********************************************************************* + * lroundf (MSVCR120.@) + * + * Copied from musl: src/math/lroundf.c + */ +__msvcrt_long CDECL lroundf(float x) +{ + float f = roundf(x); + if (f != (float)(__msvcrt_long)f) { + *_errno() = EDOM; + return 0; + } + return f; +} + +/********************************************************************* + * llround (MSVCR120.@) + * + * Copied from musl: src/math/llround.c + */ +__int64 CDECL llround(double x) +{ + double d = round(x); + if (d != (double)(__int64)d) { + *_errno() = EDOM; + return 0; + } + return d; +} + +/********************************************************************* + * llroundf (MSVCR120.@) + * + * Copied from musl: src/math/llroundf.c + */ +__int64 CDECL llroundf(float x) +{ + float f = roundf(x); + if (f != (float)(__int64)f) { + *_errno() = EDOM; + return 0; + } + return f; +} + +/********************************************************************* + * _dtest (MSVCR120.@) + */ +short CDECL _dtest(double *x) +{ + return _dclass(*x); +} + +/********************************************************************* + * _fdtest (MSVCR120.@) + */ +short CDECL _fdtest(float *x) +{ + return _fdclass(*x); +} + +/********************************************************************* + * _fdsign (MSVCR120.@) + */ +int CDECL _fdsign(float x) +{ + union { float f; UINT32 i; } u = { x }; + return (u.i >> 16) & 0x8000; +} + +/********************************************************************* + * _dsign (MSVCR120.@) + */ +int CDECL _dsign(double x) +{ + union { double f; UINT64 i; } u = { x }; + return (u.i >> 48) & 0x8000; +} + +/********************************************************************* + * _dpcomp (MSVCR120.@) + */ +int CDECL _dpcomp(double x, double y) +{ + if(isnan(x) || isnan(y)) + return 0; + + if(x == y) return 2; + return x < y ? 1 : 4; +} + +/********************************************************************* + * _fdpcomp (MSVCR120.@) + */ +int CDECL _fdpcomp(float x, float y) +{ + return _dpcomp(x, y); +} + +/********************************************************************* + * acosh (MSVCR120.@) + */ +double CDECL MSVCRT_acosh(double x) +{ + if (x < 1) + { + *_errno() = EDOM; + feraiseexcept(FE_INVALID); + return NAN; + } + return acosh( x ); +} + +/********************************************************************* + * acoshf (MSVCR120.@) + */ +float CDECL MSVCRT_acoshf(float x) +{ + if (x < 1) + { + *_errno() = EDOM; + feraiseexcept(FE_INVALID); + return NAN; + } + return acoshf( x ); +} + +/********************************************************************* + * atanh (MSVCR120.@) + */ +double CDECL MSVCRT_atanh(double x) +{ + if (fabs(x) > 1) + { + *_errno() = EDOM; + feraiseexcept(FE_INVALID); + return NAN; + } + return atanh( x ); +} + +/********************************************************************* + * atanhf (MSVCR120.@) + */ +float CDECL MSVCRT_atanhf(float x) +{ + if (fabs(x) > 1) + { + *_errno() = EDOM; + feraiseexcept(FE_INVALID); + return NAN; + } + return atanhf( x ); +} + +#endif /* _MSVCR_VER>=120 */ + +/********************************************************************* + * _scalb (MSVCRT.@) + * scalbn (MSVCR120.@) + * scalbln (MSVCR120.@) + */ +double CDECL _scalb(double num, __msvcrt_long power) +{ + return ldexp(num, power); +} + +/********************************************************************* + * _scalbf (MSVCRT.@) + * scalbnf (MSVCR120.@) + * scalblnf (MSVCR120.@) + */ +float CDECL _scalbf(float num, __msvcrt_long power) +{ + return ldexp(num, power); +} + +#if _MSVCR_VER == 120 /* other versions call remainder() directly */ + +/********************************************************************* + * remainder (MSVCR120.@) + */ +double CDECL MSVCRT_remainder(double x, double y) +{ +#ifdef __x86_64__ + if (isnan(x) || isnan(y)) *_errno() = EDOM; +#endif + return remainder(x, y); +} + +/********************************************************************* + * remainderf (MSVCR120.@) + */ +float CDECL MSVCRT_remainderf(float x, float y) +{ +#ifdef __x86_64__ + if (isnan(x) || isnan(y)) *_errno() = EDOM; +#endif + return remainderf(x, y); +} + +#endif /* _MSVCR_VER == 120 */ + +#if _MSVCR_VER>=120 + +/********************************************************************* + * _except1 (MSVCR120.@) + * TODO: + * - find meaning of ignored cw and operation bits + * - unk parameter + */ +double CDECL _except1(DWORD fpe, _FP_OPERATION_CODE op, double arg, double res, DWORD cw, void *unk) +{ + ULONG_PTR exception_arg; + DWORD exception = 0; + unsigned int fpword = 0; + WORD operation; + int raise = 0; + + TRACE("(%lx %x %lf %lf %lx %p)\n", fpe, op, arg, res, cw, unk); + +#ifdef _WIN64 + cw = ((cw >> 7) & 0x3f) | ((cw >> 3) & 0xc00); +#endif + operation = op << 5; + exception_arg = (ULONG_PTR)&operation; + + if (fpe & 0x1) { /* overflow */ + if ((fpe == 0x1 && (cw & 0x8)) || (fpe==0x11 && (cw & 0x28))) { + /* 32-bit version also sets SW_INEXACT here */ + raise |= FE_OVERFLOW; + if (fpe & 0x10) raise |= FE_INEXACT; + res = signbit(res) ? -INFINITY : INFINITY; + } else { + exception = EXCEPTION_FLT_OVERFLOW; + } + } else if (fpe & 0x2) { /* underflow */ + if ((fpe == 0x2 && (cw & 0x10)) || (fpe==0x12 && (cw & 0x30))) { + raise |= FE_UNDERFLOW; + if (fpe & 0x10) raise |= FE_INEXACT; + res = signbit(res) ? -0.0 : 0.0; + } else { + exception = EXCEPTION_FLT_UNDERFLOW; + } + } else if (fpe & 0x4) { /* zerodivide */ + if ((fpe == 0x4 && (cw & 0x4)) || (fpe==0x14 && (cw & 0x24))) { + raise |= FE_DIVBYZERO; + if (fpe & 0x10) raise |= FE_INEXACT; + } else { + exception = EXCEPTION_FLT_DIVIDE_BY_ZERO; + } + } else if (fpe & 0x8) { /* invalid */ + if (fpe == 0x8 && (cw & 0x1)) { + raise |= FE_INVALID; + } else { + exception = EXCEPTION_FLT_INVALID_OPERATION; + } + } else if (fpe & 0x10) { /* inexact */ + if (fpe == 0x10 && (cw & 0x20)) { + raise |= FE_INEXACT; + } else { + exception = EXCEPTION_FLT_INEXACT_RESULT; + } + } + + if (exception) + raise = 0; + feraiseexcept(raise); + if (exception) + RaiseException(exception, 0, 1, &exception_arg); + + if (cw & 0x1) fpword |= _EM_INVALID; + if (cw & 0x2) fpword |= _EM_DENORMAL; + if (cw & 0x4) fpword |= _EM_ZERODIVIDE; + if (cw & 0x8) fpword |= _EM_OVERFLOW; + if (cw & 0x10) fpword |= _EM_UNDERFLOW; + if (cw & 0x20) fpword |= _EM_INEXACT; + switch (cw & 0xc00) + { + case 0xc00: fpword |= _RC_UP|_RC_DOWN; break; + case 0x800: fpword |= _RC_UP; break; + case 0x400: fpword |= _RC_DOWN; break; + } + switch (cw & 0x300) + { + case 0x0: fpword |= _PC_24; break; + case 0x200: fpword |= _PC_53; break; + case 0x300: fpword |= _PC_64; break; + } + if (cw & 0x1000) fpword |= _IC_AFFINE; + _setfp(&fpword, _MCW_EM | _MCW_RC | _MCW_PC | _MCW_IC, NULL, 0); + + return res; +} + +_Dcomplex* CDECL _Cbuild(_Dcomplex *ret, double r, double i) +{ + ret->_Val[0] = r; + ret->_Val[1] = i; + return ret; +} + +double CDECL MSVCR120_creal(_Dcomplex z) +{ + return z._Val[0]; +} + +#endif /* _MSVCR_VER>=120 */ diff --git a/dll/win32/msvcrt/mathf.c b/dll/win32/msvcrt/mathf.c new file mode 100644 index 00000000000..3c87c7d0e00 --- /dev/null +++ b/dll/win32/msvcrt/mathf.c @@ -0,0 +1,72 @@ +/* + * msvcrt float functions + * + * Copyright 2019 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +/* this function is part of the import lib to provide floating */ +#if 0 +#pragma makedep implib +#endif + +#include +#include + +double __cdecl sin(double); +double __cdecl cos(double); +double __cdecl tan(double); +double __cdecl atan2(double, double); +double __cdecl exp(double); +double __cdecl log(double); +double __cdecl pow(double, double); +double __cdecl sqrt(double); +double __cdecl floor(double); +double __cdecl ceil(double); +float __cdecl powf(float, float); + +#if defined(__i386__) || (_MSVCR_VER > 0 && _MSVCR_VER < 80) +float sinf(float x) { return sin(x); } +float cosf(float x) { return cos(x); } +float tanf(float x) { return tan(x); } +float atan2f(float x, float y) { return atan2(x, y); } +float expf(float x) { return exp(x); } +float logf(float x) { return log(x); } +float sqrtf(float x) { return sqrt(x); } +float floorf(float x) { return floor(x); } +float ceilf(float x) { return ceil(x); } +__ASM_GLOBAL_IMPORT(sinf) +__ASM_GLOBAL_IMPORT(cosf) +__ASM_GLOBAL_IMPORT(tanf) +__ASM_GLOBAL_IMPORT(atan2f) +__ASM_GLOBAL_IMPORT(expf) +__ASM_GLOBAL_IMPORT(logf) +__ASM_GLOBAL_IMPORT(sqrtf) +__ASM_GLOBAL_IMPORT(floorf) +__ASM_GLOBAL_IMPORT(ceilf) + +#if _MSVCR_VER < 140 +float powf(float x, float y) { return pow(x, y); } +__ASM_GLOBAL_IMPORT(powf) +#endif +#endif + +#if _MSVCR_VER < 120 +double exp2(double x) { return pow(2.0, x); } +float exp2f(float x) { return powf(2.0f, x); } +__ASM_GLOBAL_IMPORT(exp2) +__ASM_GLOBAL_IMPORT(exp2f) +#endif diff --git a/dll/win32/msvcrt/mbcs.c b/dll/win32/msvcrt/mbcs.c new file mode 100644 index 00000000000..bd4245c2c53 --- /dev/null +++ b/dll/win32/msvcrt/mbcs.c @@ -0,0 +1,3612 @@ +/* + * msvcrt.dll mbcs functions + * + * Copyright 1999 Alexandre Julliard + * Copyright 2000 Jon Griffths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + * + * FIXME + * Not currently binary compatible with win32. MSVCRT_mbctype must be + * populated correctly and the ismb* functions should reference it. + */ + +#include +#include +#include +#include + +#include "msvcrt.h" +#include "mtdll.h" +#include "winnls.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +unsigned char MSVCRT_mbctype[257] = { 0 }; + +/* It seems that the data about valid trail bytes is not available from kernel32 + * so we have to store is here. The format is the same as for lead bytes in CPINFO */ +struct cp_extra_info_t +{ + int cp; + BYTE TrailBytes[MAX_LEADBYTES]; +}; + +static struct cp_extra_info_t g_cpextrainfo[] = +{ + {932, {0x40, 0x7e, 0x80, 0xfc, 0, 0}}, + {936, {0x40, 0xfe, 0, 0}}, + {949, {0x41, 0xfe, 0, 0}}, + {950, {0x40, 0x7e, 0xa1, 0xfe, 0, 0}}, + {1361, {0x31, 0x7e, 0x81, 0xfe, 0, 0}}, + {20932, {1, 255, 0, 0}}, /* seems to give different results on different systems */ + {0, {1, 255, 0, 0}} /* match all with FIXME */ +}; + +/* Maps cp932 single byte character to multi byte character */ +static const unsigned char mbbtombc_932[] = { + 0x40,0x49,0x68,0x94,0x90,0x93,0x95,0x66,0x69,0x6a,0x96,0x7b,0x43,0x7c,0x44,0x5e, + 0x4f,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x46,0x47,0x83,0x81,0x84,0x48, + 0x97,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e, + 0x6f,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x6d,0x8f,0x6e,0x4f,0x76, + 0x77,0x78,0x79,0x6d,0x8f,0x6e,0x4f,0x51,0x65,0x81,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,0x90,0x91,0x92,0x93,0x94,0x95,0x50, + 0x42,0x75,0x76,0x41,0x45,0x92,0x40,0x42,0x44,0x46,0x48,0x83,0x85,0x87,0x62, + 0x5b,0x41,0x43,0x45,0x47,0x49,0x4a,0x4c,0x4e,0x50,0x52,0x54,0x56,0x58,0x5a,0x5c, + 0x5e,0x60,0x63,0x65,0x67,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x71,0x74,0x77,0x7a,0x7d, + 0x7e,0x80,0x81,0x82,0x84,0x86,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8f,0x93,0x4a,0x4b }; + +/* Maps multibyte cp932 punctuation marks to single byte equivalents */ +static const unsigned char mbctombb_932_punct[] = { + 0x20,0xa4,0xa1,0x2c,0x2e,0xa5,0x3a,0x3b,0x3f,0x21,0xde,0xdf,0x00,0x00,0x00,0x5e, + 0x7e,0x5f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x00,0x00,0x2f,0x00, + 0x00,0x00,0x7c,0x00,0x00,0x60,0x27,0x00,0x22,0x28,0x29,0x00,0x00,0x5b,0x5d,0x7b, + 0x7d,0x00,0x00,0x00,0x00,0xa2,0xa3,0x00,0x00,0x00,0x00,0x2b,0x2d,0x00,0x00,0x00, + 0x00,0x3d,0x00,0x3c,0x3e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5c, + 0x24,0x00,0x00,0x25,0x23,0x26,0x2a,0x40}; + +/* Maps multibyte cp932 hiragana/katakana to single-byte equivalents */ +static const unsigned char mbctombb_932_kana[] = { + 0xa7,0xb1,0xa8,0xb2,0xa9,0xb3,0xaa,0xb4,0xab,0xb5,0xb6,0xb6,0xb7,0xb7,0xb8,0xb8, + 0xb9,0xb9,0xba,0xba,0xbb,0xbb,0xbc,0xbc,0xbd,0xbd,0xbe,0xbe,0xbf,0xbf,0xc0,0xc0, + 0xc1,0xc1,0xaf,0xc2,0xc2,0xc3,0xc3,0xc4,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xca, + 0xca,0xcb,0xcb,0xcb,0xcc,0xcc,0xcc,0xcd,0xcd,0xcd,0xce,0xce,0xce,0xcf,0xd0,0xd1, + 0xd2,0xd3,0xac,0xd4,0xad,0xd5,0xae,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdc,0xb2, + 0xb4,0xa6,0xdd,0xb3,0xb6,0xb9}; + +static wchar_t msvcrt_mbc_to_wc_l(unsigned int ch, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + wchar_t chW; + char mbch[2]; + int n_chars; + + if(locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if (ch <= 0xff) { + mbch[0] = ch; + n_chars = 1; + } else { + mbch[0] = (ch >> 8) & 0xff; + mbch[1] = ch & 0xff; + n_chars = 2; + } + if (!MultiByteToWideChar(mbcinfo->mbcodepage, 0, mbch, n_chars, &chW, 1)) + { + WARN("MultiByteToWideChar failed on %x\n", ch); + return 0; + } + return chW; +} + +static inline size_t u_strlen( const unsigned char *str ) +{ + return strlen( (const char*) str ); +} + +static inline unsigned char* u_strncat( unsigned char* dst, const unsigned char* src, size_t len ) +{ + return (unsigned char*)strncat( (char*)dst, (const char*)src, len); +} + +static inline int u_strcmp( const unsigned char *s1, const unsigned char *s2 ) +{ + return strcmp( (const char*)s1, (const char*)s2 ); +} + +static inline int u_strcasecmp( const unsigned char *s1, const unsigned char *s2 ) +{ + return _stricmp( (const char*)s1, (const char*)s2 ); +} + +static inline int u_strncmp( const unsigned char *s1, const unsigned char *s2, size_t len ) +{ + return strncmp( (const char*)s1, (const char*)s2, len ); +} + +static inline int u_strncasecmp( const unsigned char *s1, const unsigned char *s2, size_t len ) +{ + return _strnicmp( (const char*)s1, (const char*)s2, len ); +} + +static inline unsigned char *u_strchr( const unsigned char *s, unsigned char x ) +{ + return (unsigned char*) strchr( (const char*)s, x ); +} + +static inline unsigned char* u_strpbrk(const unsigned char *str, const unsigned char *accept) +{ + return (unsigned char*)strpbrk((const char*)str, (const char*)accept); +} + +static inline unsigned char *u_strrchr( const unsigned char *s, unsigned char x ) +{ + return (unsigned char*) strrchr( (const char*)s, x ); +} + +static inline unsigned char* u__strrev(unsigned char *str) +{ + return (unsigned char*)_strrev((char *)str); +} + +static inline unsigned char *u__strset( unsigned char *s, unsigned char c ) +{ + return (unsigned char*) _strset( (char*)s, c); +} + +static inline unsigned char *u__strnset( unsigned char *s, unsigned char c, size_t len ) +{ + return (unsigned char*) _strnset( (char*)s, c, len ); +} + +/********************************************************************* + * __p__mbctype (MSVCRT.@) + */ +unsigned char* CDECL __p__mbctype(void) +{ + return get_mbcinfo()->mbctype; +} + +/********************************************************************* + * __p___mb_cur_max(MSVCRT.@) + */ +int* CDECL __p___mb_cur_max(void) +{ + return &get_locinfo()->mb_cur_max; +} + +/********************************************************************* + * ___mb_cur_max_func(MSVCRT.@) + */ +int CDECL ___mb_cur_max_func(void) +{ + return get_locinfo()->mb_cur_max; +} + +#if _MSVCR_VER>=80 +/********************************************************************* + * ___mb_cur_max_l_func (MSVCR80.@) + */ +int CDECL ___mb_cur_max_l_func(_locale_t locale) +{ + pthreadlocinfo locinfo; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + return locinfo->mb_cur_max; +} +#endif + +threadmbcinfo* create_mbcinfo(int cp, LCID lcid, threadmbcinfo *old_mbcinfo) +{ + threadmbcinfo *mbcinfo; + int newcp; + CPINFO cpi; + BYTE *bytes; + WORD chartypes[256]; + char bufA[256]; + WCHAR bufW[256], lowW[256], upW[256]; + int charcount, maxchar; + int ret; + int i; + + if(old_mbcinfo && cp==old_mbcinfo->mbcodepage + && (lcid==-1 || lcid==old_mbcinfo->mblcid)) { + InterlockedIncrement(&old_mbcinfo->refcount); + return old_mbcinfo; + } + + mbcinfo = malloc(sizeof(threadmbcinfo)); + if(!mbcinfo) + return NULL; + mbcinfo->refcount = 1; + + switch (cp) + { + case _MB_CP_ANSI: + newcp = GetACP(); + break; + case _MB_CP_OEM: + newcp = GetOEMCP(); + break; + case _MB_CP_LOCALE: + newcp = get_locinfo()->lc_codepage; + if(newcp) + break; + /* fall through (C locale) */ + case _MB_CP_SBCS: + newcp = 20127; /* ASCII */ + break; + default: + newcp = cp; + break; + } + + if(lcid == -1) { + WCHAR wbuf[LOCALE_NAME_MAX_LENGTH]; + sprintf(bufA, ".%d", newcp); + mbcinfo->mblcid = locale_to_sname(bufA, NULL, NULL, wbuf) ? LocaleNameToLCID(wbuf, LOCALE_ALLOW_NEUTRAL_NAMES) : -1; + } else { + mbcinfo->mblcid = lcid; + } + + if(mbcinfo->mblcid == -1) + { + WARN("Can't assign LCID to codepage (%d)\n", mbcinfo->mblcid); + mbcinfo->mblcid = 0; + } + + if (!GetCPInfo(newcp, &cpi)) + { + WARN("Codepage %d not found\n", newcp); + free(mbcinfo); + return NULL; + } + + /* setup the _mbctype */ + memset(mbcinfo->mbctype, 0, sizeof(unsigned char[257])); + memset(mbcinfo->mbcasemap, 0, sizeof(unsigned char[256])); + + bytes = cpi.LeadByte; + while (bytes[0] || bytes[1]) + { + for (i = bytes[0]; i <= bytes[1]; i++) + mbcinfo->mbctype[i + 1] |= _M1; + bytes += 2; + } + + if (cpi.MaxCharSize == 2) + { + /* trail bytes not available through kernel32 but stored in a structure in msvcrt */ + struct cp_extra_info_t *cpextra = g_cpextrainfo; + + mbcinfo->ismbcodepage = 1; + while (TRUE) + { + if (cpextra->cp == 0 || cpextra->cp == newcp) + { + if (cpextra->cp == 0) + FIXME("trail bytes data not available for DBCS codepage %d - assuming all bytes\n", newcp); + + bytes = cpextra->TrailBytes; + while (bytes[0] || bytes[1]) + { + for (i = bytes[0]; i <= bytes[1]; i++) + mbcinfo->mbctype[i + 1] |= _M2; + bytes += 2; + } + break; + } + cpextra++; + } + } + else + mbcinfo->ismbcodepage = 0; + + maxchar = (newcp == CP_UTF8) ? 128 : 256; + + /* we can't use GetStringTypeA directly because we don't have a locale - only a code page + */ + charcount = 0; + for (i = 0; i < maxchar; i++) + if (!(mbcinfo->mbctype[i + 1] & _M1)) + bufA[charcount++] = i; + + ret = MultiByteToWideChar(newcp, 0, bufA, charcount, bufW, charcount); + if (ret != charcount) + { + ERR("MultiByteToWideChar of chars failed for cp %d, ret=%d (exp %d), error=%ld\n", + newcp, ret, charcount, GetLastError()); + } + + GetStringTypeW(CT_CTYPE1, bufW, charcount, chartypes); + LCMapStringW(mbcinfo->mblcid, LCMAP_LOWERCASE, bufW, charcount, lowW, charcount); + LCMapStringW(mbcinfo->mblcid, LCMAP_UPPERCASE, bufW, charcount, upW, charcount); + + charcount = 0; + for (i = 0; i < maxchar; i++) + if (!(mbcinfo->mbctype[i + 1] & _M1)) + { + if (chartypes[charcount] & C1_UPPER) + { + mbcinfo->mbctype[i + 1] |= _SBUP; + bufW[charcount] = lowW[charcount]; + } + else if (chartypes[charcount] & C1_LOWER) + { + mbcinfo->mbctype[i + 1] |= _SBLOW; + bufW[charcount] = upW[charcount]; + } + charcount++; + } + + ret = WideCharToMultiByte(newcp, 0, bufW, charcount, bufA, charcount, NULL, NULL); + if (ret != charcount) + { + ERR("WideCharToMultiByte failed for cp %d, ret=%d (exp %d), error=%ld\n", + newcp, ret, charcount, GetLastError()); + } + + charcount = 0; + for (i = 0; i < maxchar; i++) + { + if(!(mbcinfo->mbctype[i + 1] & _M1)) + { + if(mbcinfo->mbctype[i + 1] & (_SBUP | _SBLOW)) + mbcinfo->mbcasemap[i] = bufA[charcount]; + charcount++; + } + } + + if (newcp == 932) /* CP932 only - set _MP and _MS */ + { + /* On Windows it's possible to calculate the _MP and _MS from CT_CTYPE1 + * and CT_CTYPE3. But as of Wine 0.9.43 we return wrong values what makes + * it hard. As this is set only for codepage 932 we hardcode it what gives + * also faster execution. + */ + for (i = 161; i <= 165; i++) + mbcinfo->mbctype[i + 1] |= _MP; + for (i = 166; i <= 223; i++) + mbcinfo->mbctype[i + 1] |= _MS; + } + + mbcinfo->mbcodepage = newcp; + return mbcinfo; +} + +/********************************************************************* + * _setmbcp (MSVCRT.@) + */ +int CDECL _setmbcp(int cp) +{ + thread_data_t *data = msvcrt_get_thread_data(); + threadmbcinfo *mbcinfo; + + mbcinfo = create_mbcinfo(cp, -1, get_mbcinfo()); + if(!mbcinfo) + { + *_errno() = EINVAL; + return -1; + } + + if(data->locale_flags & LOCALE_THREAD) + { + if(data->locale_flags & LOCALE_FREE) + free_mbcinfo(data->mbcinfo); + data->mbcinfo = mbcinfo; + } + else + { + _lock(_MB_CP_LOCK); + free_mbcinfo(MSVCRT_locale->mbcinfo); + MSVCRT_locale->mbcinfo = mbcinfo; + memcpy(MSVCRT_mbctype, MSVCRT_locale->mbcinfo->mbctype, sizeof(MSVCRT_mbctype)); + _unlock(_MB_CP_LOCK); + } + return 0; +} + +/********************************************************************* + * _getmbcp (MSVCRT.@) + */ +int CDECL _getmbcp(void) +{ + return get_mbcinfo()->mbcodepage; +} + +/********************************************************************* + * _mbsnextc_l(MSVCRT.@) + */ +unsigned int CDECL _mbsnextc_l(const unsigned char* str, _locale_t locale) +{ + if(_ismbblead_l(*str, locale)) + return *str << 8 | str[1]; + return *str; +} + +/********************************************************************* + * _mbsnextc(MSVCRT.@) + */ +unsigned int CDECL _mbsnextc(const unsigned char* str) +{ + return _mbsnextc_l(str, NULL); +} + +/********************************************************************* + * _mbctolower_l(MSVCRT.@) + */ +unsigned int CDECL _mbctolower_l(unsigned int c, _locale_t locale) +{ + unsigned char str[2], ret[2]; + pthreadmbcinfo mbcinfo; + + if(!locale) + mbcinfo = get_mbcinfo(); + else + mbcinfo = locale->mbcinfo; + + if (c > 0xff) + { + if (!_ismbblead_l((c >> 8) & 0xff, locale)) + return c; + + str[0] = c >> 8; + str[1] = c; + switch(__crtLCMapStringA(mbcinfo->mblcid, LCMAP_LOWERCASE, + (char*)str, 2, (char*)ret, 2, mbcinfo->mbcodepage, 0)) + { + case 0: + return c; + case 1: + return ret[0]; + default: + return ret[1] + (ret[0] << 8); + } + } + + return mbcinfo->mbctype[c + 1] & _SBUP ? mbcinfo->mbcasemap[c] : c; +} + +/********************************************************************* + * _mbctolower(MSVCRT.@) + */ +unsigned int CDECL _mbctolower(unsigned int c) +{ + return _mbctolower_l(c, NULL); +} + +/********************************************************************* + * _mbctoupper_l(MSVCRT.@) + */ +unsigned int CDECL _mbctoupper_l(unsigned int c, _locale_t locale) +{ + unsigned char str[2], ret[2]; + pthreadmbcinfo mbcinfo; + + if(!locale) + mbcinfo = get_mbcinfo(); + else + mbcinfo = locale->mbcinfo; + + if (c > 0xff) + { + if (!_ismbblead_l((c >> 8) & 0xff, locale)) + return c; + + str[0] = c >> 8; + str[1] = c; + switch(__crtLCMapStringA(mbcinfo->mblcid, LCMAP_UPPERCASE, + (char*)str, 2, (char*)ret, 2, mbcinfo->mbcodepage, 0)) + { + case 0: + return c; + case 1: + return ret[0]; + default: + return ret[1] + (ret[0] << 8); + } + } + + return mbcinfo->mbctype[c + 1] & _SBLOW ? mbcinfo->mbcasemap[c] : c; +} + +/********************************************************************* + * _mbctoupper(MSVCRT.@) + */ +unsigned int CDECL _mbctoupper(unsigned int c) +{ + return _mbctoupper_l(c, NULL); +} + +/********************************************************************* + * _mbctombb_l (MSVCRT.@) + */ +unsigned int CDECL _mbctombb_l(unsigned int c, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + unsigned int value; + + if(locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if(mbcinfo->mbcodepage == 932) + { + if(c >= 0x829f && c <= 0x82f1) /* Hiragana */ + return mbctombb_932_kana[c - 0x829f]; + if(c >= 0x8340 && c <= 0x8396 && c != 0x837f) /* Katakana */ + return mbctombb_932_kana[c - 0x8340 - (c >= 0x837f ? 1 : 0)]; + if(c >= 0x8140 && c <= 0x8197) /* Punctuation */ + { + value = mbctombb_932_punct[c - 0x8140]; + return value ? value : c; + } + if((c >= 0x824f && c <= 0x8258) || /* Fullwidth digits */ + (c >= 0x8260 && c <= 0x8279)) /* Fullwidth capitals letters */ + return c - 0x821f; + if(c >= 0x8281 && c <= 0x829a) /* Fullwidth small letters */ + return c - 0x8220; + /* all other cases return c */ + } + return c; +} + +/********************************************************************* + * _mbctombb (MSVCRT.@) + */ +unsigned int CDECL _mbctombb(unsigned int c) +{ + return _mbctombb_l(c, NULL); +} + +/********************************************************************* + * _mbcjistojms_l(MSVCRT.@) + * + * Converts a jis character to sjis. + * Based on description from + * http://www.slayers.ne.jp/~oouchi/code/jistosjis.html + */ +unsigned int CDECL _mbcjistojms_l(unsigned int c, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if(locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + /* Conversion takes place only when codepage is 932. + In all other cases, c is returned unchanged */ + if(mbcinfo->mbcodepage == 932) + { + if(HIBYTE(c) >= 0x21 && HIBYTE(c) <= 0x7e && + LOBYTE(c) >= 0x21 && LOBYTE(c) <= 0x7e) + { + if(HIBYTE(c) % 2) + c += 0x1f; + else + c += 0x7d; + + if(LOBYTE(c) >= 0x7F) + c += 0x1; + + c = (((HIBYTE(c) - 0x21)/2 + 0x81) << 8) | LOBYTE(c); + + if(HIBYTE(c) > 0x9f) + c += 0x4000; + } + else + return 0; /* Codepage is 932, but c can't be converted */ + } + + return c; +} + +/********************************************************************* + * _mbcjistojms(MSVCRT.@) + */ +unsigned int CDECL _mbcjistojms(unsigned int c) +{ + return _mbcjistojms_l(c, NULL); +} + +/********************************************************************* + * _mbcjmstojis_l(MSVCRT.@) + * + * Converts a sjis character to jis. + */ +unsigned int CDECL _mbcjmstojis_l(unsigned int c, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if(locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + /* Conversion takes place only when codepage is 932. + In all other cases, c is returned unchanged */ + if(mbcinfo->mbcodepage == 932) + { + if(_ismbclegal_l(c, locale) && HIBYTE(c) < 0xf0) + { + if(HIBYTE(c) >= 0xe0) + c -= 0x4000; + + c = (((HIBYTE(c) - 0x81)*2 + 0x21) << 8) | LOBYTE(c); + + if(LOBYTE(c) > 0x7f) + c -= 0x1; + + if(LOBYTE(c) > 0x9d) + c += 0x83; + else + c -= 0x1f; + } + else + return 0; /* Codepage is 932, but c can't be converted */ + } + + return c; +} + +/********************************************************************* + * _mbcjmstojis(MSVCRT.@) + */ +unsigned int CDECL _mbcjmstojis(unsigned int c) +{ + return _mbcjmstojis_l(c, NULL); +} + +/********************************************************************* + * _mbclen_l(MSVCRT.@) + */ +size_t CDECL _mbclen_l(const unsigned char* str, _locale_t locale) +{ + return _ismbblead_l(*str, locale) && str[1] ? 2 : 1; +} + +/********************************************************************* + * _mbclen(MSVCRT.@) + */ +size_t CDECL _mbclen(const unsigned char* str) +{ + return _mbclen_l(str, NULL); +} + +/********************************************************************* + * _mbsinc_l(MSVCRT.@) + */ +unsigned char* CDECL _mbsinc_l(const unsigned char* str, _locale_t locale) +{ + return (unsigned char *)(str + _mbclen_l(str, locale)); +} + +/********************************************************************* + * _mbsinc(MSVCRT.@) + */ +unsigned char* CDECL _mbsinc(const unsigned char* str) +{ + return _mbsinc_l(str, NULL); +} + +/********************************************************************* + * _mbsninc(MSVCRT.@) + */ +unsigned char* CDECL _mbsninc(const unsigned char* str, size_t num) +{ + if(!str) + return NULL; + + while (num > 0 && *str) + { + if (_ismbblead(*str)) + { + if (!*(str+1)) + break; + str++; + } + str++; + num--; + } + + return (unsigned char*)str; +} + +/********************************************************************* + * _mbsnlen_l(MSVCRT.@) + */ +size_t CDECL _mbsnlen_l(const unsigned char *str, + size_t maxsize, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + size_t i = 0, len = 0; + + if(!locale) + mbcinfo = get_mbcinfo(); + else + mbcinfo = locale->mbcinfo; + + if(!mbcinfo->ismbcodepage) + return strnlen((const char*)str, maxsize); + + while(i= 1)) return EINVAL; + dest[0] = 0; + if(!MSVCRT_CHECK_PMT(src != NULL)) return EINVAL; + + if(_ismbblead_l(*src, locale)) { + if(!src[1]) { + if(copied) *copied = 1; + *_errno() = EILSEQ; + return EILSEQ; + } + + if(maxsize < 2) { + MSVCRT_INVALID_PMT("dst buffer is too small", ERANGE); + return ERANGE; + } + + *dest++ = *src++; + *dest = *src; + if(copied) *copied = 2; + }else { + *dest = *src; + if(copied) *copied = 1; + } + + return 0; +} + +/********************************************************************* + * _mbccpy(MSVCRT.@) + */ +void CDECL _mbccpy(unsigned char* dest, const unsigned char* src) +{ + _mbccpy_s_l(dest, 2, NULL, src, NULL); +} + +/********************************************************************* + * _mbccpy_l(MSVCRT.@) + */ +void CDECL _mbccpy_l(unsigned char* dest, const unsigned char* src, + _locale_t locale) +{ + _mbccpy_s_l(dest, 2, NULL, src, locale); +} + +/********************************************************************* + * _mbccpy_s(MSVCRT.@) + */ +int CDECL _mbccpy_s(unsigned char* dest, size_t maxsize, + int *copied, const unsigned char* src) +{ + return _mbccpy_s_l(dest, maxsize, copied, src, NULL); +} + + +/********************************************************************* + * _mbsncpy_l(MSVCRT.@) + * REMARKS + * The parameter n is the number or characters to copy, not the size of + * the buffer. Use _mbsnbcpy_l for a function analogical to strncpy + */ +unsigned char* CDECL _mbsncpy_l(unsigned char* dst, const unsigned char* src, size_t n, _locale_t locale) +{ + unsigned char* ret = dst; + pthreadmbcinfo mbcinfo; + + if (!n) + return dst; + if (!MSVCRT_CHECK_PMT(dst && src)) + return NULL; + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if (mbcinfo->ismbcodepage) + { + while (*src && n) + { + n--; + if (_ismbblead_l(*src, locale)) + { + if (!*(src + 1)) + { + *dst++ = 0; + *dst++ = 0; + break; + } + *dst++ = *src++; + } + *dst++ = *src++; + } + } + else + { + while (n) + { + n--; + if (!(*dst++ = *src++)) break; + } + } + while (n--) *dst++ = 0; + return ret; +} + +#if _MSVCR_VER>=80 +errno_t CDECL _mbsncpy_s_l(unsigned char* dst, size_t maxsize, const unsigned char* src, size_t n, _locale_t locale) +{ + BOOL truncate = (n == _TRUNCATE); + unsigned char *start = dst, *last; + pthreadmbcinfo mbcinfo; + unsigned int curlen; + + if (!dst && !maxsize && !n) + return 0; + + if (!MSVCRT_CHECK_PMT(dst != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(maxsize != 0)) return EINVAL; + if (!MSVCRT_CHECK_PMT(src != NULL)) + { + *start = 0; + return EINVAL; + } + + if (!n) + { + *start = 0; + return 0; + } + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + curlen = 0; + last = dst; + while (*src && n && maxsize) + { + if (curlen) + { + --maxsize; + *dst++ = *src++; + if (!--curlen) --n; + continue; + } + last = dst; + if (!(mbcinfo->ismbcodepage && _ismbblead_l(*src, locale))) + { + curlen = 1; + continue; + } + curlen = 2; + if (!truncate && maxsize <= curlen) maxsize = 0; + } + + if (!maxsize && truncate) + { + *last = 0; + return STRUNCATE; + } + if (!truncate && curlen && !src[curlen - 1]) + { + *_errno() = EILSEQ; + *start = 0; + return EILSEQ; + } + if (!maxsize) + { + *start = 0; + if (!MSVCRT_CHECK_PMT_ERR(FALSE, ERANGE)) return ERANGE; + } + *dst = 0; + return 0; +} + +errno_t CDECL _mbsncpy_s(unsigned char* dst, size_t maxsize, const unsigned char* src, size_t n) +{ + return _mbsncpy_s_l(dst, maxsize, src, n, NULL); +} +#endif + +/********************************************************************* + * _mbsncpy(MSVCRT.@) + * REMARKS + * The parameter n is the number or characters to copy, not the size of + * the buffer. Use _mbsnbcpy for a function analogical to strncpy + */ +unsigned char* CDECL _mbsncpy(unsigned char* dst, const unsigned char* src, size_t n) +{ + return _mbsncpy_l(dst, src, n, NULL); +} + +/********************************************************************* + * _mbsnbcpy_s_l(MSVCRT.@) + * REMARKS + * Unlike _mbsnbcpy this function does not pad the rest of the dest + * string with 0 + */ +int CDECL _mbsnbcpy_s_l(unsigned char* dst, size_t size, + const unsigned char* src, size_t n, _locale_t locale) +{ + size_t pos = 0; + + if(!dst || size == 0) + return EINVAL; + if(!src) + { + dst[0] = '\0'; + return EINVAL; + } + if(!n) + return 0; + + if((locale ? locale->mbcinfo : get_mbcinfo())->ismbcodepage) + { + BOOL is_lead = FALSE; + while (*src && n) + { + if(pos == size) + { + dst[0] = '\0'; + return ERANGE; + } + is_lead = (!is_lead && _ismbblead(*src)); + n--; + dst[pos++] = *src++; + } + + if (is_lead) /* if string ends with a lead, remove it */ + dst[pos - 1] = 0; + } + else + { + while (n) + { + n--; + if(pos == size) + { + dst[0] = '\0'; + return ERANGE; + } + + if(!(*src)) break; + dst[pos++] = *src++; + } + } + + if(pos < size) + dst[pos] = '\0'; + else + { + dst[0] = '\0'; + return ERANGE; + } + + return 0; +} + +/********************************************************************* + * _mbsnbcpy_s(MSVCRT.@) + */ +int CDECL _mbsnbcpy_s(unsigned char* dst, size_t size, const unsigned char* src, size_t n) +{ + return _mbsnbcpy_s_l(dst, size, src, n, NULL); +} + +/********************************************************************* + * _mbscpy_s_l(MSVCRT.@) + */ +int CDECL _mbscpy_s_l(unsigned char *dst, size_t size, + const unsigned char *src, _locale_t locale) +{ + return _mbsnbcpy_s_l(dst, size, src, -1, locale); +} + +/********************************************************************* + * _mbscpy_s(MSVCRT.@) + */ +int CDECL _mbscpy_s(unsigned char *dst, size_t size, const unsigned char *src) +{ + return _mbscpy_s_l(dst, size, src, NULL); +} + +/********************************************************************* + * _mbsnbcpy_l(MSVCRT.@) + * REMARKS + * Like strncpy this function doesn't enforce the string to be + * NUL-terminated + */ +unsigned char* CDECL _mbsnbcpy_l(unsigned char* dst, const unsigned char* src, size_t n, _locale_t locale) +{ + unsigned char* ret = dst; + pthreadmbcinfo mbcinfo; + + if (!n) + return dst; + if (!MSVCRT_CHECK_PMT(dst && src)) + return NULL; + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + if (mbcinfo->ismbcodepage) + { + BOOL is_lead = FALSE; + while (*src && n) + { + is_lead = (!is_lead && _ismbblead_l(*src, locale)); + n--; + *dst++ = *src++; + } + + if (is_lead) /* if string ends with a lead, remove it */ + *(dst - 1) = 0; + } + else + { + while (n) + { + n--; + if (!(*dst++ = *src++)) break; + } + } + while (n--) *dst++ = 0; + return ret; +} + +/********************************************************************* + * _mbsnbcpy(MSVCRT.@) + * REMARKS + * Like strncpy this function doesn't enforce the string to be + * NUL-terminated + */ +unsigned char* CDECL _mbsnbcpy(unsigned char* dst, const unsigned char* src, size_t n) +{ + return _mbsnbcpy_l(dst, src, n, NULL); +} + +/********************************************************************* + * _mbscmp_l(MSVCRT.@) + */ +int CDECL _mbscmp_l(const unsigned char* str, const unsigned char* cmp, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if (!MSVCRT_CHECK_PMT(str && cmp)) + return _NLSCMPERROR; + + mbcinfo = locale ? locale->mbcinfo : get_mbcinfo(); + + if(mbcinfo->ismbcodepage) + { + unsigned int strc, cmpc; + do { + if(!*str) + return *cmp ? -1 : 0; + if(!*cmp) + return 1; + strc = _mbsnextc_l(str, locale); + cmpc = _mbsnextc_l(cmp, locale); + if(strc != cmpc) + return strc < cmpc ? -1 : 1; + str +=(strc > 255) ? 2 : 1; + cmp +=(strc > 255) ? 2 : 1; /* equal, use same increment */ + } while(1); + } + return u_strcmp(str, cmp); /* ASCII CP */ +} + +/********************************************************************* + * _mbscmp(MSVCRT.@) + */ +int CDECL _mbscmp(const unsigned char* str, const unsigned char* cmp) +{ + return _mbscmp_l(str, cmp, NULL); +} + +/********************************************************************* + * _mbsnbicoll_l(MSVCRT.@) + */ +int CDECL _mbsnbicoll_l(const unsigned char *str1, const unsigned char *str2, size_t len, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if(!locale) + mbcinfo = get_mbcinfo(); + else + mbcinfo = locale->mbcinfo; + + if(!mbcinfo->ismbcodepage) + return _strnicoll_l((const char*)str1, (const char*)str2, len, locale); + return CompareStringA(mbcinfo->mblcid, NORM_IGNORECASE, + (const char*)str1, strnlen((const char*)str1, len), + (const char*)str2, strnlen((const char*)str2, len)) - CSTR_EQUAL; +} + +/********************************************************************* + * _mbsicoll_l(MSVCRT.@) + */ +int CDECL _mbsicoll_l(const unsigned char *str1, const unsigned char *str2, _locale_t locale) +{ + return _mbsnbicoll_l(str1, str2, INT_MAX, locale); +} + +/********************************************************************* + * _mbsnbicoll(MSVCRT.@) + */ +int CDECL _mbsnbicoll(const unsigned char *str1, const unsigned char *str2, size_t len) +{ + return _mbsnbicoll_l(str1, str2, len, NULL); +} + +/********************************************************************* + * _mbsicoll(MSVCRT.@) + */ +int CDECL _mbsicoll(const unsigned char* str, const unsigned char* cmp) +{ +#if _MSVCR_VER>=60 && _MSVCR_VER<=71 + return CompareStringA(get_mbcinfo()->mblcid, NORM_IGNORECASE, + (const char*)str, -1, (const char*)cmp, -1)-CSTR_EQUAL; +#else + return _mbsnbicoll_l(str, cmp, INT_MAX, NULL); +#endif +} + +/********************************************************************* + * _mbsnbcoll_l(MSVCRT.@) + */ +int CDECL _mbsnbcoll_l(const unsigned char *str1, const unsigned char *str2, size_t len, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if(!locale) + mbcinfo = get_mbcinfo(); + else + mbcinfo = locale->mbcinfo; + + if(!mbcinfo->ismbcodepage) + return _strncoll_l((const char*)str1, (const char*)str2, len, locale); + return CompareStringA(mbcinfo->mblcid, 0, + (const char*)str1, strnlen((const char*)str1, len), + (const char*)str2, strnlen((const char*)str2, len)) - CSTR_EQUAL; +} + +/********************************************************************* + * _mbscoll_l(MSVCRT.@) + */ +int CDECL _mbscoll_l(const unsigned char *str1, const unsigned char *str2, _locale_t locale) +{ + return _mbsnbcoll_l(str1, str2, INT_MAX, locale); +} + +/********************************************************************* + * _mbsnbcoll(MSVCRT.@) + */ +int CDECL _mbsnbcoll(const unsigned char *str1, const unsigned char *str2, size_t len) +{ + return _mbsnbcoll_l(str1, str2, len, NULL); +} + +/********************************************************************* + * _mbscoll(MSVCRT.@) + */ +int CDECL _mbscoll(const unsigned char* str, const unsigned char* cmp) +{ +#if _MSVCR_VER>=60 && _MSVCR_VER<=71 + return CompareStringA(get_mbcinfo()->mblcid, 0, + (const char*)str, -1, (const char*)cmp, -1)-CSTR_EQUAL; +#else + return _mbsnbcoll_l(str, cmp, INT_MAX, NULL); +#endif +} + +/********************************************************************* + * _mbsicmp_l(MSVCRT.@) + */ +int CDECL _mbsicmp_l(const unsigned char* str, const unsigned char* cmp, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if(!MSVCRT_CHECK_PMT(str && cmp)) + return _NLSCMPERROR; + + if(!locale) + mbcinfo = get_mbcinfo(); + else + mbcinfo = locale->mbcinfo; + if(mbcinfo->ismbcodepage) + { + unsigned int strc, cmpc; + do { + if(!*str) + return *cmp ? -1 : 0; + if(!*cmp) + return 1; + strc = _mbctolower_l(_mbsnextc_l(str, locale), locale); + cmpc = _mbctolower_l(_mbsnextc_l(cmp, locale), locale); + if(strc != cmpc) + return strc < cmpc ? -1 : 1; + str +=(strc > 255) ? 2 : 1; + cmp +=(strc > 255) ? 2 : 1; /* equal, use same increment */ + } while(1); + } + return u_strcasecmp(str, cmp); /* ASCII CP */ +} + +/********************************************************************* + * _mbsicmp(MSVCRT.@) + */ +int CDECL _mbsicmp(const unsigned char* str, const unsigned char* cmp) +{ + return _mbsicmp_l(str, cmp, NULL); +} + +/********************************************************************* + * _mbsncmp_l(MSVCRT.@) + */ +int CDECL _mbsncmp_l(const unsigned char* str, const unsigned char* cmp, + size_t len, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + unsigned int strc, cmpc; + + if (!len) + return 0; + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if (!mbcinfo->ismbcodepage) + return u_strncmp(str, cmp, len); /* ASCII CP */ + + if (!MSVCRT_CHECK_PMT(str && cmp)) + return _NLSCMPERROR; + + while (len--) + { + int inc; + + if (!*str) + return *cmp ? -1 : 0; + if (!*cmp) + return 1; + strc = _mbsnextc_l(str, locale); + cmpc = _mbsnextc_l(cmp, locale); + if (strc != cmpc) + return strc < cmpc ? -1 : 1; + inc = (strc > 255) ? 2 : 1; /* Equal, use same increment */ + str += inc; + cmp += inc; + } + return 0; /* Matched len chars */ +} + +/********************************************************************* + * _mbsncmp(MSVCRT.@) + */ +int CDECL _mbsncmp(const unsigned char* str, const unsigned char* cmp, size_t len) +{ + return _mbsncmp_l(str, cmp, len, NULL); +} + +/********************************************************************* + * _mbsnbcmp_l(MSVCRT.@) + */ +int CDECL _mbsnbcmp_l(const unsigned char* str, const unsigned char* cmp, size_t len, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if (!len) + return 0; + + if (!MSVCRT_CHECK_PMT(str && cmp)) + return _NLSCMPERROR; + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if (mbcinfo->ismbcodepage) + { + unsigned int strc, cmpc; + while (len) + { + int clen; + if (!*str) + return *cmp ? -1 : 0; + if (!*cmp) + return 1; + if (_ismbblead_l(*str, locale)) + { + strc = (len >= 2) ? _mbsnextc_l(str, locale) : 0; + clen = 2; + } + else + { + strc = *str; + clen = 1; + } + if (_ismbblead_l(*cmp, locale)) + cmpc = (len >= 2) ? _mbsnextc_l(cmp, locale) : 0; + else + cmpc = *cmp; + if(strc != cmpc) + return strc < cmpc ? -1 : 1; + len -= clen; + str += clen; + cmp += clen; + } + return 0; /* Matched len chars */ + } + return u_strncmp(str, cmp, len); +} + +/********************************************************************* + * _mbsnbcmp(MSVCRT.@) + */ +int CDECL _mbsnbcmp(const unsigned char* str, const unsigned char* cmp, size_t len) +{ + return _mbsnbcmp_l(str, cmp, len, NULL); +} + +/********************************************************************* + * _mbsnicmp(MSVCRT.@) + * + * Compare two multibyte strings case insensitively to 'len' characters. + */ +int CDECL _mbsnicmp_l(const unsigned char* str, const unsigned char* cmp, size_t len, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if (!len) + return 0; + if (!MSVCRT_CHECK_PMT(str && cmp)) + return _NLSCMPERROR; + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + /* FIXME: No tolower() for mb strings yet */ + if (mbcinfo->ismbcodepage) + { + unsigned int strc, cmpc; + while (len--) + { + if (!*str) + return *cmp ? -1 : 0; + if (!*cmp) + return 1; + strc = _mbctolower_l(_mbsnextc_l(str, locale), locale); + cmpc = _mbctolower_l(_mbsnextc_l(cmp, locale), locale); + if (strc != cmpc) + return strc < cmpc ? -1 : 1; + str += (strc > 255) ? 2 : 1; + cmp += (strc > 255) ? 2 : 1; /* Equal, use same increment */ + } + return 0; /* Matched len chars */ + } + return u_strncasecmp(str, cmp, len); /* ASCII CP */ +} + +/********************************************************************* + * _mbsnicmp(MSVCRT.@) + * + * Compare two multibyte strings case insensitively to 'len' characters. + */ +int CDECL _mbsnicmp(const unsigned char* str, const unsigned char* cmp, size_t len) +{ + return _mbsnicmp_l(str, cmp, len, NULL); +} + +/********************************************************************* + * _mbsnbicmp_l(MSVCRT.@) + */ +int CDECL _mbsnbicmp_l(const unsigned char* str, const unsigned char* cmp, size_t len, _locale_t locale) +{ + + pthreadmbcinfo mbcinfo; + + if (!len) + return 0; + if (!MSVCRT_CHECK_PMT(str && cmp)) + return _NLSCMPERROR; + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if (mbcinfo->ismbcodepage) + { + unsigned int strc, cmpc; + while (len) + { + int clen; + if (!*str) + return *cmp ? -1 : 0; + if (!*cmp) + return 1; + if (_ismbblead_l(*str, locale)) + { + strc = (len >= 2) ? _mbsnextc_l(str, locale) : 0; + clen = 2; + } + else + { + strc = *str; + clen = 1; + } + if (_ismbblead_l(*cmp, locale)) + cmpc = (len >= 2) ? _mbsnextc_l(cmp, locale) : 0; + else + cmpc = *cmp; + strc = _mbctolower_l(strc, locale); + cmpc = _mbctolower_l(cmpc, locale); + if (strc != cmpc) + return strc < cmpc ? -1 : 1; + len -= clen; + str += clen; + cmp += clen; + } + return 0; /* Matched len bytes */ + } + return u_strncasecmp(str, cmp, len); +} + +/********************************************************************* + * _mbsnbicmp(MSVCRT.@) + */ +int CDECL _mbsnbicmp(const unsigned char* str, const unsigned char* cmp, size_t len) +{ + return _mbsnbicmp_l(str, cmp, len, NULL); +} + +/********************************************************************* + * _mbscat (MSVCRT.@) + */ +unsigned char * CDECL _mbscat( unsigned char *dst, const unsigned char *src ) +{ + strcat( (char *)dst, (const char *)src ); + return dst; +} + +/********************************************************************* + * _mbscat_s_l (MSVCRT.@) + */ +int CDECL _mbscat_s_l( unsigned char *dst, size_t size, + const unsigned char *src, _locale_t locale ) +{ + size_t i, j; + int ret = 0; + + if(!MSVCRT_CHECK_PMT(dst != NULL)) return EINVAL; + if(!MSVCRT_CHECK_PMT(src != NULL)) return EINVAL; + + for(i=0; imbcinfo; + else + mbcinfo = get_mbcinfo(); + if(mbcinfo->ismbcodepage) + { + unsigned int c; + while (1) + { + c = _mbsnextc_l(s, locale); + if (c == x) + return (unsigned char*)s; + if (!c) + return NULL; + s += c > 255 ? 2 : 1; + } + } + return u_strchr(s, x); /* ASCII CP */ +} + +/********************************************************************* + * _mbschr(MSVCRT.@) + */ +unsigned char* CDECL _mbschr(const unsigned char* s, unsigned int x) +{ + return _mbschr_l(s, x, NULL); +} + +/********************************************************************* + * _mbsrchr_l(MSVCRT.@) + */ +unsigned char* CDECL _mbsrchr_l(const unsigned char *s, unsigned int x, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if (!MSVCRT_CHECK_PMT(s)) + return NULL; + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if (mbcinfo->ismbcodepage) + { + unsigned char *match = NULL; + unsigned int c; + + while (1) + { + c = _mbsnextc_l(s, locale); + if (c == x) + match = (unsigned char *)s; + if (!c) + return match; + s += (c > 255) ? 2 : 1; + } + } + return u_strrchr(s, x); +} + +/********************************************************************* + * _mbsrchr(MSVCRT.@) + */ +unsigned char* CDECL _mbsrchr(const unsigned char* s, unsigned int x) +{ + return _mbsrchr_l(s, x, NULL); +} + +/********************************************************************* + * _mbstok_s_l(MSVCRT.@) + */ +unsigned char* CDECL _mbstok_s_l(unsigned char *str, const unsigned char *delim, + unsigned char **ctx, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + unsigned int c; + + if(!MSVCRT_CHECK_PMT(delim != NULL)) return NULL; + if(!MSVCRT_CHECK_PMT(ctx != NULL)) return NULL; + if(!MSVCRT_CHECK_PMT(str || *ctx)) return NULL; + + if(locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if(!mbcinfo->ismbcodepage) + return (unsigned char*)strtok_s((char*)str, (const char*)delim, (char**)ctx); + + if(!str) + str = *ctx; + + while((c=_mbsnextc(str)) && _mbschr(delim, c)) + str += c>255 ? 2 : 1; + if(!*str) + { + *ctx = str; + return NULL; + } + + *ctx = str + (c>255 ? 2 : 1); + while((c=_mbsnextc(*ctx)) && !_mbschr(delim, c)) + *ctx += c>255 ? 2 : 1; + if (**ctx) { + *(*ctx)++ = 0; + if(c > 255) + *(*ctx)++ = 0; + } + + return str; +} + + +/********************************************************************* + * _mbstok_s(MSVCRT.@) + */ +unsigned char* CDECL _mbstok_s(unsigned char *str, + const unsigned char *delim, unsigned char **ctx) +{ + return _mbstok_s_l(str, delim, ctx, NULL); +} + +/********************************************************************* + * _mbstok_l(MSVCRT.@) + */ +unsigned char* CDECL _mbstok_l(unsigned char *str, + const unsigned char *delim, _locale_t locale) +{ + return _mbstok_s_l(str, delim, &msvcrt_get_thread_data()->mbstok_next, locale); +} + +/********************************************************************* + * _mbstok(MSVCRT.@) + */ +unsigned char* CDECL _mbstok(unsigned char *str, const unsigned char *delim) +{ + thread_data_t *data = msvcrt_get_thread_data(); + +#if _MSVCR_VER == 0 + if(!str && !data->mbstok_next) + return NULL; +#endif + + return _mbstok_s_l(str, delim, &data->mbstok_next, NULL); +} + +/********************************************************************* + * _mbbtombc_l(MSVCRT.@) + */ +unsigned int CDECL _mbbtombc_l(unsigned int c, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if(locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if(mbcinfo->mbcodepage == 932) + { + if(c >= 0x20 && c <= 0x7e) { + if((c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a) || (c >= 0x30 && c <= 0x39)) + return mbbtombc_932[c - 0x20] | 0x8200; + else + return mbbtombc_932[c - 0x20] | 0x8100; + } + else if(c >= 0xa1 && c <= 0xdf) { + if(c >= 0xa6 && c <= 0xdd && c != 0xb0) + return mbbtombc_932[c - 0xa1 + 0x5f] | 0x8300; + else + return mbbtombc_932[c - 0xa1 + 0x5f] | 0x8100; + } + } + return c; /* not Japanese or no MB char */ +} + +/********************************************************************* + * _mbbtombc(MSVCRT.@) + */ +unsigned int CDECL _mbbtombc(unsigned int c) +{ + return _mbbtombc_l(c, NULL); +} + +/********************************************************************* + * _ismbbkana_l(MSVCRT.@) + */ +int CDECL _ismbbkana_l(unsigned int c, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if(locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if(mbcinfo->mbcodepage == 932) + { + /* Japanese/Katakana, CP 932 */ + return (c >= 0xa1 && c <= 0xdf); + } + return 0; +} + +/********************************************************************* + * _ismbbkana(MSVCRT.@) + */ +int CDECL _ismbbkana(unsigned int c) +{ + return _ismbbkana_l( c, NULL ); +} + +/********************************************************************* + * _ismbcdigit_l(MSVCRT.@) + */ +int CDECL _ismbcdigit_l(unsigned int ch, _locale_t locale) +{ + return _iswdigit_l( msvcrt_mbc_to_wc_l(ch, locale), locale ); +} + +/********************************************************************* + * _ismbcdigit(MSVCRT.@) + */ +int CDECL _ismbcdigit(unsigned int ch) +{ + return _ismbcdigit_l( ch, NULL ); +} + +/********************************************************************* + * _ismbcgraph_l(MSVCRT.@) + */ +int CDECL _ismbcgraph_l(unsigned int ch, _locale_t locale) +{ + return _iswgraph_l( msvcrt_mbc_to_wc_l(ch, locale), locale ); +} + +/********************************************************************* + * _ismbcgraph(MSVCRT.@) + */ +int CDECL _ismbcgraph(unsigned int ch) +{ + return _ismbcgraph_l( ch, NULL ); +} + +/********************************************************************* + * _ismbcalpha_l (MSVCRT.@) + */ +int CDECL _ismbcalpha_l(unsigned int ch, _locale_t locale) +{ + return _iswalpha_l( msvcrt_mbc_to_wc_l(ch, locale), locale ); +} + +/********************************************************************* + * _ismbcalpha (MSVCRT.@) + */ +int CDECL _ismbcalpha(unsigned int ch) +{ + return _ismbcalpha_l( ch, NULL ); +} + +/********************************************************************* + * _ismbclower_l (MSVCRT.@) + */ +int CDECL _ismbclower_l(unsigned int ch, _locale_t locale) +{ + return _iswlower_l( msvcrt_mbc_to_wc_l(ch, locale), locale ); +} + +/********************************************************************* + * _ismbclower (MSVCRT.@) + */ +int CDECL _ismbclower(unsigned int ch) +{ + return _ismbclower_l( ch, NULL ); +} + +/********************************************************************* + * _ismbcupper_l (MSVCRT.@) + */ +int CDECL _ismbcupper_l(unsigned int ch, _locale_t locale) +{ + return _iswupper_l( msvcrt_mbc_to_wc_l(ch, locale), locale ); +} + +/********************************************************************* + * _ismbcupper (MSVCRT.@) + */ +int CDECL _ismbcupper(unsigned int ch) +{ + return _ismbcupper_l( ch, NULL ); +} + +/********************************************************************* + * _ismbcsymbol_l(MSVCRT.@) + */ +int CDECL _ismbcsymbol_l(unsigned int ch, _locale_t locale) +{ + wchar_t wch = msvcrt_mbc_to_wc_l( ch, locale ); + WORD ctype; + if (!GetStringTypeW(CT_CTYPE3, &wch, 1, &ctype)) + { + WARN("GetStringTypeW failed on %x\n", ch); + return 0; + } + return ((ctype & C3_SYMBOL) != 0); +} + +/********************************************************************* + * _ismbcsymbol(MSVCRT.@) + */ +int CDECL _ismbcsymbol(unsigned int ch) +{ + return _ismbcsymbol_l(ch, NULL); +} + +/********************************************************************* + * _ismbcalnum_l (MSVCRT.@) + */ +int CDECL _ismbcalnum_l(unsigned int ch, _locale_t locale) +{ + return _iswalnum_l( msvcrt_mbc_to_wc_l(ch, locale), locale ); +} + +/********************************************************************* + * _ismbcalnum (MSVCRT.@) + */ +int CDECL _ismbcalnum(unsigned int ch) +{ + return _ismbcalnum_l( ch, NULL ); +} + +/********************************************************************* + * _ismbcspace_l (MSVCRT.@) + */ +int CDECL _ismbcspace_l(unsigned int ch, _locale_t locale) +{ + return _iswspace_l( msvcrt_mbc_to_wc_l(ch, locale), locale ); +} + +/********************************************************************* + * _ismbcspace (MSVCRT.@) + */ +int CDECL _ismbcspace(unsigned int ch) +{ + return _ismbcspace_l( ch, NULL ); +} + +/********************************************************************* + * _ismbcprint_l (MSVCRT.@) + */ +int CDECL _ismbcprint_l(unsigned int ch, _locale_t locale) +{ + return _iswprint_l( msvcrt_mbc_to_wc_l(ch, locale), locale ); +} + +/********************************************************************* + * _ismbcprint (MSVCRT.@) + */ +int CDECL _ismbcprint(unsigned int ch) +{ + return _ismbcprint_l( ch, NULL ); +} + +/********************************************************************* + * _ismbcpunct_l (MSVCRT.@) + */ +int CDECL _ismbcpunct_l(unsigned int ch, _locale_t locale) +{ + return _iswpunct_l( msvcrt_mbc_to_wc_l(ch, locale), locale ); +} + +/********************************************************************* + * _ismbcpunct(MSVCRT.@) + */ +int CDECL _ismbcpunct(unsigned int ch) +{ + return _ismbcpunct_l( ch, NULL ); +} + +/********************************************************************* + * _ismbchira_l(MSVCRT.@) + */ +int CDECL _ismbchira_l(unsigned int c, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if(locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if(mbcinfo->mbcodepage == 932) + { + /* Japanese/Hiragana, CP 932 */ + return (c >= 0x829f && c <= 0x82f1); + } + return 0; +} + +/********************************************************************* + * _ismbchira(MSVCRT.@) + */ +int CDECL _ismbchira(unsigned int c) +{ + return _ismbchira_l(c, NULL); +} + +/********************************************************************* + * _ismbckata_l(MSVCRT.@) + */ +int CDECL _ismbckata_l(unsigned int c, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if(locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if(mbcinfo->mbcodepage == 932) + { + /* Japanese/Katakana, CP 932 */ + return (c >= 0x8340 && c <= 0x8396 && c != 0x837f); + } + return 0; +} + +/********************************************************************* + * _ismbckata(MSVCRT.@) + */ +int CDECL _ismbckata(unsigned int c) +{ + return _ismbckata_l(c, NULL); +} + +/********************************************************************* + * _ismbblead_l(MSVCRT.@) + */ +int CDECL _ismbblead_l(unsigned int c, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if(!locale) + mbcinfo = get_mbcinfo(); + else + mbcinfo = locale->mbcinfo; + + return (mbcinfo->mbctype[(c&0xff) + 1] & _M1) != 0; +} + +/********************************************************************* + * _ismbblead(MSVCRT.@) + */ +int CDECL _ismbblead(unsigned int c) +{ + return _ismbblead_l(c, NULL); +} + +/********************************************************************* + * _ismbbtrail_l(MSVCRT.@) + */ +int CDECL _ismbbtrail_l(unsigned int c, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if(!locale) + mbcinfo = get_mbcinfo(); + else + mbcinfo = locale->mbcinfo; + + return (mbcinfo->mbctype[(c&0xff) + 1] & _M2) != 0; +} + +/********************************************************************* + * _ismbbtrail(MSVCRT.@) + */ +int CDECL _ismbbtrail(unsigned int c) +{ + return _ismbbtrail_l(c, NULL); +} + +/********************************************************************* + * _ismbclegal_l(MSVCRT.@) + */ +int CDECL _ismbclegal_l(unsigned int c, _locale_t locale) +{ + return _ismbblead_l(HIBYTE(c), locale) && _ismbbtrail_l(LOBYTE(c), locale); +} + +/********************************************************************* + * _ismbclegal(MSVCRT.@) + */ +int CDECL _ismbclegal(unsigned int c) +{ + return _ismbclegal_l(c, NULL); +} + +/********************************************************************* + * _ismbslead_l(MSVCRT.@) + */ +int CDECL _ismbslead_l(const unsigned char* start, const unsigned char* str, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + int lead = 0; + + if (!MSVCRT_CHECK_PMT(start && str)) + return 0; + + if(locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if(!mbcinfo->ismbcodepage) + return 0; + + /* Lead bytes can also be trail bytes so we need to analyse the string + */ + while (start <= str) + { + if (!*start) + return 0; + lead = !lead && _ismbblead_l(*start, locale); + start++; + } + + return lead ? -1 : 0; +} + +/********************************************************************* + * _ismbslead(MSVCRT.@) + */ +int CDECL _ismbslead(const unsigned char* start, const unsigned char* str) +{ + return _ismbslead_l(start, str, NULL); +} + +/********************************************************************* + * _ismbstrail_l(MSVCRT.@) + */ +int CDECL _ismbstrail_l(const unsigned char* start, const unsigned char* str, _locale_t locale) +{ + if (!MSVCRT_CHECK_PMT(start && str)) + return 0; + + /* Note: this function doesn't check _ismbbtrail */ + if ((str > start) && _ismbslead_l(start, str-1, locale)) + return -1; + else + return 0; +} + +/********************************************************************* + * _ismbstrail(MSVCRT.@) + */ +int CDECL _ismbstrail(const unsigned char* start, const unsigned char* str) +{ + return _ismbstrail_l(start, str, NULL); +} + +/********************************************************************* + * _mbsdec_l(MSVCRT.@) + */ +unsigned char* CDECL _mbsdec_l(const unsigned char *start, + const unsigned char *cur, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if (!MSVCRT_CHECK_PMT(start && cur)) + return NULL; + if (start >= cur) + return NULL; + + if (!locale) + mbcinfo = get_mbcinfo(); + else + mbcinfo = locale->mbcinfo; + + if (mbcinfo->ismbcodepage) + return (unsigned char *)(_ismbstrail_l(start, cur - 1, locale) ? cur - 2 : cur - 1); + + return (unsigned char *)cur - 1; /* ASCII CP or SB char */ +} + +/********************************************************************* + * _mbsdec(MSVCRT.@) + */ +unsigned char* CDECL _mbsdec(const unsigned char *start, const unsigned char *cur) +{ + return _mbsdec_l(start, cur, NULL); +} + + +/********************************************************************* + * _mbbtype_l(MSVCRT.@) + */ +int CDECL _mbbtype_l(unsigned char c, int type, _locale_t locale) +{ + if (type == 1) + return _ismbbtrail_l(c, locale) ? _MBC_TRAIL : _MBC_ILLEGAL; + else + return _ismbblead_l(c, locale) ? _MBC_LEAD + : _isprint_l(c, locale) ? _MBC_SINGLE : _MBC_ILLEGAL; +} + +/********************************************************************* + * _mbbtype(MSVCRT.@) + */ +int CDECL _mbbtype(unsigned char c, int type) +{ + return _mbbtype_l(c, type, NULL); +} + +/********************************************************************* + * _mbsbtype_l (MSVCRT.@) + */ +int CDECL _mbsbtype_l(const unsigned char *str, size_t count, _locale_t locale) +{ + int lead = 0; + pthreadmbcinfo mbcinfo; + const unsigned char *end = str + count; + + if (!MSVCRT_CHECK_PMT(str)) + return _MBC_ILLEGAL; + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + /* Lead bytes can also be trail bytes so we need to analyse the string. + * Also we must return _MBC_ILLEGAL for chars past the end of the string + */ + while (str < end) /* Note: we skip the last byte - will check after the loop */ + { + if (!*str) + return _MBC_ILLEGAL; + lead = mbcinfo->ismbcodepage && !lead && _ismbblead_l(*str, locale); + str++; + } + + if (lead) + { + if (_ismbbtrail_l(*str, locale)) + return _MBC_TRAIL; + else + return _MBC_ILLEGAL; + } + else + { + if (_ismbblead_l(*str, locale)) + return _MBC_LEAD; + else + return _MBC_SINGLE; + } +} + +/********************************************************************* + * _mbsbtype (MSVCRT.@) + */ +int CDECL _mbsbtype(const unsigned char *str, size_t count) +{ + return _mbsbtype_l(str, count, NULL); +} + + +/********************************************************************* + * _mbsset_l(MSVCRT.@) + */ +unsigned char* CDECL _mbsset_l(unsigned char* str, unsigned int c, _locale_t locale) +{ + unsigned char* ret = str; + pthreadmbcinfo mbcinfo; + + if (!MSVCRT_CHECK_PMT(str)) + return NULL; + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if (!mbcinfo->ismbcodepage || c < 256) + return u__strset(str, c); /* ASCII CP or SB char */ + + c &= 0xffff; /* Strip high bits */ + + while (str[0] && str[1]) + { + *str++ = c >> 8; + *str++ = c & 0xff; + } + if (str[0]) + str[0] = '\0'; /* FIXME: OK to shorten? */ + + return ret; +} + +/********************************************************************* + * _mbsset(MSVCRT.@) + */ +unsigned char* CDECL _mbsset(unsigned char* str, unsigned int c) +{ + return _mbsset_l(str, c, NULL); +} + +/********************************************************************* + * _mbsnbset_l(MSVCRT.@) + */ +unsigned char* CDECL _mbsnbset_l(unsigned char *str, unsigned int c, size_t len, _locale_t locale) +{ + unsigned char *ret = str; + pthreadmbcinfo mbcinfo; + + if (!len) + return ret; + if (!MSVCRT_CHECK_PMT(str)) + return NULL; + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if (!mbcinfo->ismbcodepage || c < 256) + return u__strnset(str, c, len); /* ASCII CP or SB char */ + + c &= 0xffff; /* Strip high bits */ + + while (str[0] && str[1] && (len > 1)) + { + *str++ = c >> 8; + len--; + *str++ = c & 0xff; + len--; + } + if (len && str[0]) + { + /* as per msdn pad with a blank character */ + str[0] = ' '; + } + + return ret; +} + +/********************************************************************* + * _mbsnbset(MSVCRT.@) + */ +unsigned char* CDECL _mbsnbset(unsigned char *str, unsigned int c, size_t len) +{ + return _mbsnbset_l(str, c, len, NULL); +} + +/********************************************************************* + * _mbsnset(MSVCRT.@) + */ +unsigned char* CDECL _mbsnset_l(unsigned char* str, unsigned int c, size_t len, _locale_t locale) +{ + unsigned char *ret = str; + pthreadmbcinfo mbcinfo; + + if (!len) + return ret; + if (!MSVCRT_CHECK_PMT(str)) + return NULL; + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if (!mbcinfo->ismbcodepage || c < 256) + return u__strnset(str, c, len); /* ASCII CP or SB char */ + + c &= 0xffff; /* Strip high bits */ + + while (str[0] && str[1] && len--) + { + *str++ = c >> 8; + *str++ = c & 0xff; + } + if (len && str[0]) + str[0] = '\0'; /* FIXME: OK to shorten? */ + + return ret; +} + +/********************************************************************* + * _mbsnset(MSVCRT.@) + */ +unsigned char* CDECL _mbsnset(unsigned char* str, unsigned int c, size_t len) +{ + return _mbsnset_l(str, c, len, NULL); +} + +/********************************************************************* + * _mbsnccnt_l(MSVCRT.@) + * 'c' is for 'character'. + */ +size_t CDECL _mbsnccnt_l(const unsigned char* str, size_t len, _locale_t locale) +{ + size_t ret; + pthreadmbcinfo mbcinfo; + + if (!len) + return 0; + if (!MSVCRT_CHECK_PMT(str)) + return 0; + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if (mbcinfo->ismbcodepage) + { + ret = 0; + while (*str && len-- > 0) + { + if (_ismbblead_l(*str, locale)) + { + if (!len) + break; + len--; + str++; + } + str++; + ret++; + } + return ret; + } + ret = u_strlen(str); + return min(ret, len); /* ASCII CP */ +} + +/********************************************************************* + * _mbsnccnt(MSVCRT.@) + * 'c' is for 'character'. + */ +size_t CDECL _mbsnccnt(const unsigned char* str, size_t len) +{ + return _mbsnccnt_l(str, len, NULL); +} + +/********************************************************************* + * _mbsnbcnt_l(MSVCRT.@) + * 'b' is for byte count. + */ +size_t CDECL _mbsnbcnt_l(const unsigned char* str, size_t len, _locale_t locale) +{ + size_t ret; + pthreadmbcinfo mbcinfo; + + if (!len) + return 0; + if (!MSVCRT_CHECK_PMT(str)) + return 0; + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + if (mbcinfo->ismbcodepage) + { + const unsigned char* xstr = str; + while (*xstr && len-- > 0) + { + if (_ismbblead_l(*xstr++, locale)) + xstr++; + } + return xstr - str; + } + ret = u_strlen(str); + return min(ret, len); /* ASCII CP */ +} + +/********************************************************************* + * _mbsnbcnt(MSVCRT.@) + * 'b' is for byte count. + */ +size_t CDECL _mbsnbcnt(const unsigned char* str, size_t len) +{ + return _mbsnbcnt_l(str, len, NULL); +} + +/********************************************************************* + * _mbsnbcat_l(MSVCRT.@) + */ +unsigned char* CDECL _mbsnbcat_l(unsigned char *dst, const unsigned char *src, size_t len, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if (!MSVCRT_CHECK_PMT(dst && src)) + return NULL; + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if (mbcinfo->ismbcodepage) + { + unsigned char *res = dst; + + while (*dst) + { + if (_ismbblead_l(*dst++, locale)) + { + if (*dst) + { + dst++; + } + else + { + /* as per msdn overwrite the lead byte in front of '\0' */ + dst--; + break; + } + } + } + while (*src && len--) *dst++ = *src++; + *dst = '\0'; + return res; + } + return u_strncat(dst, src, len); /* ASCII CP */ +} + +/********************************************************************* + * _mbsnbcat(MSVCRT.@) + */ +unsigned char* CDECL _mbsnbcat(unsigned char *dst, const unsigned char *src, size_t len) +{ + return _mbsnbcat_l(dst, src, len, NULL); +} + +/********************************************************************* + * _mbsnbcat_s_l(MSVCRT.@) + */ +int CDECL _mbsnbcat_s_l(unsigned char *dst, size_t size, const unsigned char *src, size_t len, _locale_t locale) +{ + unsigned char *ptr = dst; + size_t i; + pthreadmbcinfo mbcinfo; + + if (!dst && !size && !len) + return 0; + + if (!MSVCRT_CHECK_PMT(dst && size && src)) + { + if (dst && size) + *dst = '\0'; + return EINVAL; + } + + /* Find the null terminator of the destination buffer. */ + while (size && *ptr) + size--, ptr++; + + if (!size) + { + *dst = '\0'; + *_errno() = EINVAL; + return EINVAL; + } + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + /* If necessary, check that the character preceding the null terminator is + * a lead byte and move the pointer back by one for later overwrite. */ + if (ptr != dst && mbcinfo->ismbcodepage && _ismbblead_l(*(ptr - 1), locale)) + size++, ptr--; + + for (i = 0; *src && i < len; i++) + { + *ptr++ = *src++; + size--; + + if (!size) + { + *dst = '\0'; + *_errno() = ERANGE; + return ERANGE; + } + } + + *ptr = '\0'; + return 0; +} + +/********************************************************************* + * _mbsnbcat_s(MSVCRT.@) + */ +int CDECL _mbsnbcat_s(unsigned char *dst, size_t size, const unsigned char *src, size_t len) +{ + return _mbsnbcat_s_l(dst, size, src, len, NULL); +} + +/********************************************************************* + * _mbsncat_l(MSVCRT.@) + */ +unsigned char* CDECL _mbsncat_l(unsigned char* dst, const unsigned char* src, size_t len, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if (!len) + return dst; + + if (!MSVCRT_CHECK_PMT(dst && src)) + return NULL; + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if (mbcinfo->ismbcodepage) + { + unsigned char *res = dst; + while (*dst) + { + if (_ismbblead_l(*dst++, locale)) + dst++; + } + while (*src && len--) + { + *dst++ = *src; + if (_ismbblead_l(*src++, locale)) + *dst++ = *src++; + } + *dst = '\0'; + return res; + } + return u_strncat(dst, src, len); /* ASCII CP */ +} + +/********************************************************************* + * _mbsncat(MSVCRT.@) + */ +unsigned char* CDECL _mbsncat(unsigned char* dst, const unsigned char* src, size_t len) +{ + return _mbsncat_l(dst, src, len, NULL); +} + +/********************************************************************* + * _mbslwr_l(MSVCRT.@) + */ +unsigned char* CDECL _mbslwr_l(unsigned char *s, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + unsigned char *ret = s; + + if (!s) + return NULL; + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if (mbcinfo->ismbcodepage) + { + unsigned int c; + + while (*s) + { + c = _mbctolower_l(_mbsnextc_l(s, locale), locale); + /* Note that I assume that the size of the character is unchanged */ + if (c > 255) + { + *s++ = (c >> 8); + c = c & 0xff; + } + *s++ = c; + } + } + else + { + for ( ; *s; s++) *s = _tolower_l(*s, locale); + } + return ret; +} + +/********************************************************************* + * _mbslwr(MSVCRT.@) + */ +unsigned char* CDECL _mbslwr(unsigned char *s) +{ + return _mbslwr_l(s, NULL); +} + +/********************************************************************* + * _mbslwr_s_l(MSVCRT.@) + */ +int CDECL _mbslwr_s_l(unsigned char* s, size_t len, _locale_t locale) +{ + unsigned char *p = s; + pthreadmbcinfo mbcinfo; + + if (!s && !len) + return 0; + if (!MSVCRT_CHECK_PMT(s && len)) + return EINVAL; + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if (mbcinfo->ismbcodepage) + { + unsigned int c; + for ( ; *s && len > 0; len--) + { + c = _mbctolower_l(_mbsnextc_l(s, locale), locale); + /* Note that I assume that the size of the character is unchanged */ + if (c > 255) + { + *s++=(c>>8); + c=c & 0xff; + } + *s++=c; + } + } + else + { + for ( ; *s && len > 0; s++, len--) + *s = _tolower_l(*s, locale); + } + + if (!MSVCRT_CHECK_PMT(len)) + { + *p = 0; + return EINVAL; + } + *s = 0; + return 0; +} + +/********************************************************************* + * _mbslwr_s(MSVCRT.@) + */ +int CDECL _mbslwr_s(unsigned char* str, size_t len) +{ + return _mbslwr_s_l(str, len, NULL); +} + +/********************************************************************* + * _mbsupr_l(MSVCRT.@) + */ +unsigned char* CDECL _mbsupr_l(unsigned char* s, _locale_t locale) +{ + unsigned char *ret = s; + pthreadmbcinfo mbcinfo; + + if (!MSVCRT_CHECK_PMT(s)) + return NULL; + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if (mbcinfo->ismbcodepage) + { + unsigned int c; + while (*s) + { + c = _mbctoupper_l(_mbsnextc_l(s, locale), locale); + /* Note that I assume that the size of the character is unchanged */ + if (c > 255) + { + *s++ = (c >> 8); + c = c & 0xff; + } + *s++ = c; + } + } + else + for ( ; *s; s++) *s = _toupper_l(*s, locale); + return ret; +} + +/********************************************************************* + * _mbsupr(MSVCRT.@) + */ +unsigned char* CDECL _mbsupr(unsigned char* s) +{ + return _mbsupr_l(s, NULL); +} + +/********************************************************************* + * _mbsupr_s_l(MSVCRT.@) + */ +int CDECL _mbsupr_s_l(unsigned char* s, size_t len, _locale_t locale) +{ + unsigned char *p = s; + + if (!s && !len) + return 0; + if (!MSVCRT_CHECK_PMT(s && len)) + return EINVAL; + + if (get_mbcinfo()->ismbcodepage) + { + unsigned int c; + for ( ; *s && len > 0; len--) + { + c = _mbctoupper_l(_mbsnextc_l(s, locale), locale); + /* Note that I assume that the size of the character is unchanged */ + if (c > 255) + { + *s++=(c>>8); + c=c & 0xff; + } + *s++=c; + } + } + else + { + for ( ; *s && len > 0; s++, len--) + *s = _toupper_l(*s, locale); + } + + if (!MSVCRT_CHECK_PMT(len)) + { + *p = 0; + return EINVAL; + } + *s = 0; + return 0; +} + +/********************************************************************* + * _mbsupr_s(MSVCRT.@) + */ +int CDECL _mbsupr_s(unsigned char* s, size_t len) +{ + return _mbsupr_s_l(s, len, NULL); +} + +/********************************************************************* + * _mbsspn_l (MSVCRT.@) + */ +size_t CDECL _mbsspn_l(const unsigned char* string, + const unsigned char* set, _locale_t locale) +{ + const unsigned char *p, *q; + + if (!MSVCRT_CHECK_PMT(string && set)) + return 0; + + for (p = string; *p; p++) + { + for (q = set; *q; q++) + { + if (_ismbblead_l(*q, locale)) + { + /* duplicate a bug in native implementation */ + if (!q[1]) break; + + if (p[0] == q[0] && p[1] == q[1]) + { + p++; + break; + } + q++; + } + else + { + if (p[0] == q[0]) break; + } + } + if (!*q) break; + } + return p - string; +} + +/********************************************************************* + * _mbsspn (MSVCRT.@) + */ +size_t CDECL _mbsspn(const unsigned char* string, const unsigned char* set) +{ + return _mbsspn_l(string, set, NULL); +} + +/********************************************************************* + * _mbsspnp_l (MSVCRT.@) + */ +unsigned char* CDECL _mbsspnp_l(const unsigned char* string, const unsigned char* set, _locale_t locale) +{ + if (!MSVCRT_CHECK_PMT(string && set)) + return 0; + + string += _mbsspn_l(string, set, locale); + return *string ? (unsigned char*)string : NULL; +} + +/********************************************************************* + * _mbsspnp (MSVCRT.@) + */ +unsigned char* CDECL _mbsspnp(const unsigned char* string, const unsigned char* set) +{ + return _mbsspnp_l(string, set, NULL); +} + +/********************************************************************* + * _mbscspn_l (MSVCRT.@) + */ +size_t CDECL _mbscspn_l(const unsigned char* str, + const unsigned char* cmp, _locale_t locale) +{ + const unsigned char *p, *q; + + for (p = str; *p; p++) + { + for (q = cmp; *q; q++) + { + if (_ismbblead_l(*q, locale)) + { + /* duplicate a bug in native implementation */ + if (!q[1]) return 0; + + if (p[0] == q[0] && p[1] == q[1]) + return p - str; + q++; + } + else if (p[0] == q[0]) + return p - str; + } + } + return p - str; +} + +/********************************************************************* + * _mbscspn (MSVCRT.@) + */ +size_t CDECL _mbscspn(const unsigned char* str, const unsigned char* cmp) +{ + return _mbscspn_l(str, cmp, NULL); +} + +/********************************************************************* + * _mbsrev_l (MSVCRT.@) + */ +unsigned char* CDECL _mbsrev_l(unsigned char* str, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + unsigned char *p, tmp; + + if (!MSVCRT_CHECK_PMT(str)) + return NULL; + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if (!mbcinfo->ismbcodepage) + return u__strrev(str); + + for (p = str; *p; p++) + { + if (_ismbblead_l(*p, locale)) + { + if (p[1]) + { + tmp = p[0]; + p[0] = p[1]; + p[1] = tmp; + p++; + } + else + { + /* drop trailing lead char */ + p[0] = 0; + } + } + } + return u__strrev(str); +} + +/********************************************************************* + * _mbsrev (MSVCRT.@) + */ +unsigned char* CDECL _mbsrev(unsigned char* str) +{ + return _mbsrev_l(str, NULL); +} + +/********************************************************************* + * _mbspbrk_l (MSVCRT.@) + */ +unsigned char* CDECL _mbspbrk_l(const unsigned char *str, + const unsigned char *accept, _locale_t locale) +{ + const unsigned char* p; + pthreadmbcinfo mbcinfo; + + if (locale) + mbcinfo = locale->mbcinfo; + else + mbcinfo = get_mbcinfo(); + + if (!mbcinfo->ismbcodepage) + return u_strpbrk(str, accept); + + if (!MSVCRT_CHECK_PMT(str && accept)) + return NULL; + + while (*str) + { + for (p = accept; *p; p += (_ismbblead_l(*p, locale) ? 2 : 1)) + { + if (*p == *str) + if (!_ismbblead_l(*p, locale) || p[1] == str[1]) + return (unsigned char*)str; + } + str += (_ismbblead_l(*str, locale) ? 2 : 1); + } + return NULL; +} + +/********************************************************************* + * _mbspbrk (MSVCRT.@) + */ +unsigned char* CDECL _mbspbrk(const unsigned char *str, const unsigned char *accept) +{ + return _mbspbrk_l(str, accept, NULL); +} + +/* + * Functions depending on locale codepage + */ + +/********************************************************************* + * _mblen_l(MSVCRT.@) + * REMARKS + * Unlike most of the multibyte string functions this function uses + * the locale codepage, not the codepage set by _setmbcp + */ +int CDECL _mblen_l(const char* str, size_t size, _locale_t locale) +{ + pthreadlocinfo locinfo; + + if (!str || !*str || !size) + return 0; + + if (locale) + locinfo = locale->locinfo; + else + locinfo = get_locinfo(); + + if (locinfo->mb_cur_max == 1) + return 1; /* ASCII CP */ + return !_isleadbyte_l((unsigned char)*str, locale) ? 1 : (size > 1 ? 2 : -1); +} + +/********************************************************************* + * mblen(MSVCRT.@) + */ +int CDECL mblen(const char* str, size_t size) +{ + return _mblen_l(str, size, NULL); +} + +/********************************************************************* + * mbrlen(MSVCRT.@) + */ +size_t CDECL mbrlen(const char *str, size_t len, mbstate_t *state) +{ + mbstate_t s = (state ? *state : 0); + size_t ret; + + if(!len || !str || !*str) + return 0; + + if(get_locinfo()->mb_cur_max == 1) { + return 1; + }else if(!s && isleadbyte((unsigned char)*str)) { + if(len == 1) { + s = (unsigned char)*str; + ret = -2; + }else { + ret = 2; + } + }else if(!s) { + ret = 1; + }else { + s = 0; + ret = 2; + } + + if(state) + *state = s; + return ret; +} + +/********************************************************************* + * _mbstrlen_l(MSVCRT.@) + */ +size_t CDECL _mbstrlen_l(const char* str, _locale_t locale) +{ + pthreadlocinfo locinfo; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(locinfo->mb_cur_max > 1) { + size_t len; + len = MultiByteToWideChar(locinfo->lc_codepage, MB_ERR_INVALID_CHARS, + str, -1, NULL, 0); + if (!len) { + *_errno() = EILSEQ; + return -1; + } + return len - 1; + } + + return strlen(str); +} + +/********************************************************************* + * _mbstrlen(MSVCRT.@) + */ +size_t CDECL _mbstrlen(const char* str) +{ + return _mbstrlen_l(str, NULL); +} + +/********************************************************************* + * _mbtowc_l(MSVCRT.@) + */ +int CDECL _mbtowc_l(wchar_t *dst, const char* str, size_t n, _locale_t locale) +{ + pthreadlocinfo locinfo; + wchar_t tmpdst; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(n <= 0 || !str) + return 0; + + if(!*str) { + if(dst) *dst = 0; + return 0; + } + + if(!locinfo->lc_codepage) { + if(dst) *dst = (unsigned char)*str; + return 1; + } + if(n>=2 && _isleadbyte_l((unsigned char)*str, locale)) { + if(!MultiByteToWideChar(locinfo->lc_codepage, 0, str, 2, &tmpdst, 1)) + return -1; + if(dst) *dst = tmpdst; + return 2; + } + if(!MultiByteToWideChar(locinfo->lc_codepage, 0, str, 1, &tmpdst, 1)) + return -1; + if(dst) *dst = tmpdst; + return 1; +} + +/********************************************************************* + * mbtowc(MSVCRT.@) + */ +int CDECL mbtowc(wchar_t *dst, const char* str, size_t n) +{ + return _mbtowc_l(dst, str, n, NULL); +} + +/********************************************************************* + * btowc(MSVCRT.@) + */ +wint_t CDECL btowc(int c) +{ + unsigned char letter = c; + wchar_t ret; + + if(c == EOF) + return WEOF; + if(!get_locinfo()->lc_codepage) + return c & 255; + if(!MultiByteToWideChar(get_locinfo()->lc_codepage, + MB_ERR_INVALID_CHARS, (LPCSTR)&letter, 1, &ret, 1)) + return WEOF; + + return ret; +} + +/********************************************************************* + * mbrtowc(MSVCRT.@) + */ +size_t CDECL mbrtowc(wchar_t *dst, const char *str, + size_t n, mbstate_t *state) +{ + pthreadlocinfo locinfo = get_locinfo(); + mbstate_t s = (state ? *state : 0); + char tmpstr[2]; + int len = 0; + + if(dst) + *dst = 0; + + if(!n || !str || !*str) + return 0; + + if(locinfo->mb_cur_max == 1) { + tmpstr[len++] = *str; + }else if(!s && isleadbyte((unsigned char)*str)) { + if(n == 1) { + s = (unsigned char)*str; + len = -2; + }else { + tmpstr[0] = str[0]; + tmpstr[1] = str[1]; + len = 2; + } + }else if(!s) { + tmpstr[len++] = *str; + }else { + tmpstr[0] = s; + tmpstr[1] = *str; + len = 2; + s = 0; + } + + if(len > 0) { + if(!MultiByteToWideChar(locinfo->lc_codepage, 0, tmpstr, len, dst, dst ? 1 : 0)) + len = -1; + } + + if(state) + *state = s; + return len; +} + +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; +} + +/********************************************************************* + * _mbstowcs_l(MSVCRT.@) + */ +size_t CDECL _mbstowcs_l(wchar_t *wcstr, const char *mbstr, + size_t count, _locale_t locale) +{ + pthreadlocinfo locinfo; + size_t i, size; + + if(!mbstr) { + *_errno() = EINVAL; + return -1; + } + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(!locinfo->lc_codepage) { + if(!wcstr) + return strlen(mbstr); + + for(i=0; ilc_codepage, + MB_ERR_INVALID_CHARS, mbstr, -1, NULL, 0); + if(!size) { + *_errno() = EILSEQ; + return -1; + } + return size - 1; + } + + for(i=0, size=0; ilc_codepage == CP_UTF8) { + int j, chlen = get_utf8_char_len(mbstr[size]); + + for(j = 1; j < chlen; j++) + { + if(!mbstr[size + j]) + { + if(count) wcstr[0] = '\0'; + *_errno() = EILSEQ; + return -1; + } + } + size += chlen; + } + else + size += (_isleadbyte_l((unsigned char)mbstr[size], locale) ? 2 : 1); + } + + if(size) { + size = MultiByteToWideChar(locinfo->lc_codepage, + MB_ERR_INVALID_CHARS, mbstr, size, wcstr, count); + if(!size) { + if(count) wcstr[0] = '\0'; + *_errno() = EILSEQ; + return -1; + } + } + + if(sizeret) { + int ch_len = mbrtowc(&tmpdst, p, 2, &s); + if(wcstr) + wcstr[ret] = tmpdst; + + if(ch_len < 0) { + return -1; + }else if(ch_len == 0) { + if(wcstr) *pmbstr = NULL; + return ret; + } + + p += ch_len; + ret++; + } + + if(wcstr) *pmbstr = p; + return ret; +} + +/********************************************************************* + * mbsrtowcs_s(MSVCRT.@) + */ +int CDECL mbsrtowcs_s(size_t *ret, wchar_t *wcstr, size_t len, + const char **mbstr, size_t count, mbstate_t *state) +{ + size_t tmp; + + if(!ret) ret = &tmp; + if(!MSVCRT_CHECK_PMT(!!wcstr == !!len)) { + *ret = -1; + return EINVAL; + } + + *ret = mbsrtowcs(wcstr, mbstr, count>len ? len : count, state); + if(*ret == -1) { + if(wcstr) *wcstr = 0; + return *_errno(); + } + (*ret)++; + if(*ret > len) { + /* no place for terminating '\0' */ + if(wcstr) *wcstr = 0; + return 0; + } + if(wcstr) wcstr[(*ret)-1] = 0; + return 0; +} + +/********************************************************************* + * _mbctohira_l (MSVCRT.@) + * + * Converts a sjis katakana character to hiragana. + */ +unsigned int CDECL _mbctohira_l(unsigned int c, _locale_t locale) +{ + if(_ismbckata_l(c, locale) && c <= 0x8393) + return (c - 0x8340 - (c >= 0x837f ? 1 : 0)) + 0x829f; + return c; +} + +/********************************************************************* + * _mbctohira (MSVCRT.@) + */ +unsigned int CDECL _mbctohira(unsigned int c) +{ + return _mbctohira_l(c, NULL); +} + +/********************************************************************* + * _mbctokata_l (MSVCRT.@) + * + * Converts a sjis hiragana character to katakana. + */ +unsigned int CDECL _mbctokata_l(unsigned int c, _locale_t locale) +{ + if(_ismbchira_l(c, locale)) + return (c - 0x829f) + 0x8340 + (c >= 0x82de ? 1 : 0); + return c; +} + + +/********************************************************************* + * _mbctokata (MSVCRT.@) + */ +unsigned int CDECL _mbctokata(unsigned int c) +{ + return _mbctokata_l(c, NULL); +} + +/********************************************************************* + * _ismbcl0_l (MSVCRT.@) + */ +int CDECL _ismbcl0_l(unsigned int c, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if(!locale) + mbcinfo = get_mbcinfo(); + else + mbcinfo = locale->mbcinfo; + + if(mbcinfo->mbcodepage == 932) + { + /* JIS non-Kanji */ + return _ismbclegal_l(c, locale) && c >= 0x8140 && c <= 0x889e; + } + + return 0; +} + +/********************************************************************* + * _ismbcl0 (MSVCRT.@) + */ +int CDECL _ismbcl0(unsigned int c) +{ + return _ismbcl0_l(c, NULL); +} + +/********************************************************************* + * _ismbcl1_l (MSVCRT.@) + */ +int CDECL _ismbcl1_l(unsigned int c, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if(!locale) + mbcinfo = get_mbcinfo(); + else + mbcinfo = locale->mbcinfo; + + if(mbcinfo->mbcodepage == 932) + { + /* JIS level-1 */ + return _ismbclegal_l(c, locale) && c >= 0x889f && c <= 0x9872; + } + + return 0; +} + +/********************************************************************* + * _ismbcl1 (MSVCRT.@) + */ +int CDECL _ismbcl1(unsigned int c) +{ + return _ismbcl1_l(c, NULL); +} + +/********************************************************************* + * _ismbcl2_l (MSVCRT.@) + */ +int CDECL _ismbcl2_l(unsigned int c, _locale_t locale) +{ + pthreadmbcinfo mbcinfo; + + if(!locale) + mbcinfo = get_mbcinfo(); + else + mbcinfo = locale->mbcinfo; + + if(mbcinfo->mbcodepage == 932) + { + /* JIS level-2 */ + return _ismbclegal_l(c, locale) && c >= 0x989f && c <= 0xeaa4; + } + + return 0; +} + +/********************************************************************* + * _ismbcl2 (MSVCRT.@) + */ +int CDECL _ismbcl2(unsigned int c) +{ + return _ismbcl2_l(c, NULL); +} diff --git a/dll/win32/msvcrt/misc.c b/dll/win32/msvcrt/misc.c new file mode 100644 index 00000000000..559bb7288b3 --- /dev/null +++ b/dll/win32/msvcrt/misc.c @@ -0,0 +1,584 @@ +/* + * msvcrt.dll misc functions + * + * Copyright 2000 Jon Griffiths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include +#include + +#include "msvcrt.h" +#include "wine/debug.h" +#include "ntsecapi.h" +#include "windows.h" +#include "wine/asm.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +static unsigned int output_format; + +/********************************************************************* + * _beep (MSVCRT.@) + */ +void CDECL _beep( unsigned int freq, unsigned int duration) +{ + TRACE(":Freq %d, Duration %d\n",freq,duration); + Beep(freq, duration); +} + +/********************************************************************* + * srand (MSVCRT.@) + */ +void CDECL srand( unsigned int seed ) +{ + thread_data_t *data = msvcrt_get_thread_data(); + data->random_seed = seed; +} + +/********************************************************************* + * rand (MSVCRT.@) + */ +int CDECL rand(void) +{ + thread_data_t *data = msvcrt_get_thread_data(); + + /* this is the algorithm used by MSVC, according to + * http://en.wikipedia.org/wiki/List_of_pseudorandom_number_generators */ + data->random_seed = data->random_seed * 214013 + 2531011; + return (data->random_seed >> 16) & RAND_MAX; +} + +/********************************************************************* + * rand_s (MSVCRT.@) + */ +int CDECL rand_s(unsigned int *pval) +{ + if (!pval || !RtlGenRandom(pval, sizeof(*pval))) + { + *_errno() = EINVAL; + return EINVAL; + } + return 0; +} + +/********************************************************************* + * _sleep (MSVCRT.@) + */ +void CDECL _sleep(__msvcrt_ulong timeout) +{ + TRACE("_sleep for %ld milliseconds\n",timeout); + Sleep((timeout)?timeout:1); +} + +/********************************************************************* + * _lfind (MSVCRT.@) + */ +void* CDECL _lfind(const void* match, const void* start, + unsigned int* array_size, unsigned int elem_size, + int (CDECL *cf)(const void*,const void*) ) +{ + unsigned int size = *array_size; + if (size) + do + { + if (cf(match, start) == 0) + return (void *)start; /* found */ + start = (const char *)start + elem_size; + } while (--size); + return NULL; +} + +/********************************************************************* + * _lfind_s (MSVCRT.@) + */ +void* CDECL _lfind_s(const void* match, const void* start, + unsigned int* array_size, unsigned int elem_size, + int (CDECL *cf)(void*,const void*,const void*), + void* context) +{ + unsigned int size; + if (!MSVCRT_CHECK_PMT(match != NULL)) return NULL; + if (!MSVCRT_CHECK_PMT(array_size != NULL)) return NULL; + if (!MSVCRT_CHECK_PMT(start != NULL || *array_size == 0)) return NULL; + if (!MSVCRT_CHECK_PMT(cf != NULL)) return NULL; + if (!MSVCRT_CHECK_PMT(elem_size != 0)) return NULL; + + size = *array_size; + if (size) + do + { + if (cf(context, match, start) == 0) + return (void *)start; /* found */ + start = (const char *)start + elem_size; + } while (--size); + return NULL; +} + +/********************************************************************* + * _lsearch (MSVCRT.@) + */ +void* CDECL _lsearch(const void* match, void* start, + unsigned int* array_size, unsigned int elem_size, + int (CDECL *cf)(const void*,const void*) ) +{ + unsigned int size = *array_size; + if (size) + do + { + if (cf(match, start) == 0) + return start; /* found */ + start = (char*)start + elem_size; + } while (--size); + + /* not found, add to end */ + memcpy(start, match, elem_size); + array_size[0]++; + return start; +} + +/********************************************************************* + * bsearch_s (msvcrt.@) + */ +void* CDECL bsearch_s(const void *key, const void *base, size_t nmemb, size_t size, + int (__cdecl *compare)(void *, const void *, const void *), void *ctx) +{ + ssize_t min = 0; + ssize_t max = nmemb - 1; + + if (!MSVCRT_CHECK_PMT(size != 0)) return NULL; + if (!MSVCRT_CHECK_PMT(compare != NULL)) return NULL; + + while (min <= max) + { + ssize_t cursor = min + (max - min) / 2; + int ret = compare(ctx, key,(const char *)base+(cursor*size)); + if (!ret) + return (char*)base+(cursor*size); + if (ret < 0) + max = cursor - 1; + else + min = cursor + 1; + } + return NULL; +} + +static int CDECL compare_wrapper(void *ctx, const void *e1, const void *e2) +{ + int (__cdecl *compare)(const void *, const void *) = ctx; + return compare(e1, e2); +} + +/********************************************************************* + * bsearch (msvcrt.@) + */ +void* CDECL bsearch(const void *key, const void *base, size_t nmemb, + size_t size, int (__cdecl *compar)(const void *, const void *)) +{ + return bsearch_s(key, base, nmemb, size, compare_wrapper, compar); +} +/********************************************************************* + * _chkesp (MSVCRT.@) + * + * Trap to a debugger if the value of the stack pointer has changed. + * + * PARAMS + * None. + * + * RETURNS + * Does not return. + * + * NOTES + * This function is available for iX86 only. + * + * When VC++ generates debug code, it stores the value of the stack pointer + * before calling any external function, and checks the value following + * the call. It then calls this function, which will trap if the values are + * not the same. Usually this means that the prototype used to call + * the function is incorrect. It can also mean that the .spec entry has + * the wrong calling convention or parameters. + */ +#ifdef __i386__ + +# if defined(__GNUC__) || defined(__clang__) + +__ASM_GLOBAL_FUNC(_chkesp, + "jnz 1f\n\t" + "ret\n" + "1:\tpushl %ebp\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + __ASM_CFI(".cfi_rel_offset %ebp,0\n\t") + "movl %esp,%ebp\n\t" + __ASM_CFI(".cfi_def_cfa_register %ebp\n\t") + "subl $12,%esp\n\t" + "pushl %eax\n\t" + "pushl %ecx\n\t" + "pushl %edx\n\t" + "call " __ASM_NAME("chkesp_fail") "\n\t" + "popl %edx\n\t" + "popl %ecx\n\t" + "popl %eax\n\t" + "leave\n\t" + __ASM_CFI(".cfi_def_cfa %esp,4\n\t") + __ASM_CFI(".cfi_same_value %ebp\n\t") + "ret") + +void CDECL chkesp_fail(void) +{ + ERR("Stack pointer incorrect after last function call - Bad prototype/spec entry?\n"); + DebugBreak(); +} + +# else /* __GNUC__ || __clang__ */ + +/**********************************************************************/ + +void CDECL _chkesp(void) +{ +} + +# endif /* __GNUC__ || __clang__ */ + +#endif /* __i386__ */ + +static inline void swap(char *l, char *r, size_t size) +{ + char tmp; + + while(size--) { + tmp = *l; + *l++ = *r; + *r++ = tmp; + } +} + +static void small_sort(void *base, size_t nmemb, size_t size, + int (CDECL *compar)(void *, const void *, const void *), void *context) +{ + size_t e, i; + char *max, *p; + + for(e=nmemb; e>1; e--) { + max = base; + for(i=1; i 0) + max = p; + } + + if(p != max) + swap(p, max, size); + } +} + +static void quick_sort(void *base, size_t nmemb, size_t size, + int (CDECL *compar)(void *, const void *, const void *), void *context) +{ + size_t stack_lo[8*sizeof(size_t)], stack_hi[8*sizeof(size_t)]; + size_t beg, end, lo, hi, med; + int stack_pos; + + stack_pos = 0; + stack_lo[stack_pos] = 0; + stack_hi[stack_pos] = nmemb-1; + +#define X(i) ((char*)base+size*(i)) + while(stack_pos >= 0) { + beg = stack_lo[stack_pos]; + end = stack_hi[stack_pos--]; + + if(end-beg < 8) { + small_sort(X(beg), end-beg+1, size, compar, context); + continue; + } + + lo = beg; + hi = end; + med = lo + (hi-lo+1)/2; + if(compar(context, X(lo), X(med)) > 0) + swap(X(lo), X(med), size); + if(compar(context, X(lo), X(hi)) > 0) + swap(X(lo), X(hi), size); + if(compar(context, X(med), X(hi)) > 0) + swap(X(med), X(hi), size); + + lo++; + hi--; + while(1) { + while(lo <= hi) { + if(lo!=med && compar(context, X(lo), X(med))>0) + break; + lo++; + } + + while(med != hi) { + if(compar(context, X(hi), X(med)) <= 0) + break; + hi--; + } + + if(hi < lo) + break; + + swap(X(lo), X(hi), size); + if(hi == med) + med = lo; + lo++; + hi--; + } + + while(hi > beg) { + if(hi!=med && compar(context, X(hi), X(med))!=0) + break; + hi--; + } + + if(hi-beg >= end-lo) { + stack_lo[++stack_pos] = beg; + stack_hi[stack_pos] = hi; + stack_lo[++stack_pos] = lo; + stack_hi[stack_pos] = end; + }else { + stack_lo[++stack_pos] = lo; + stack_hi[stack_pos] = end; + stack_lo[++stack_pos] = beg; + stack_hi[stack_pos] = hi; + } + } +#undef X +} + +/********************************************************************* + * qsort_s (MSVCRT.@) + * + * This function is trying to sort data doing identical comparisons + * as native does. There are still cases where it behaves differently. + */ +void CDECL qsort_s(void *base, size_t nmemb, size_t size, + int (CDECL *compar)(void *, const void *, const void *), void *context) +{ + const size_t total_size = nmemb*size; + + if (!MSVCRT_CHECK_PMT(base != NULL || (base == NULL && nmemb == 0))) return; + if (!MSVCRT_CHECK_PMT(size > 0)) return; + if (!MSVCRT_CHECK_PMT(compar != NULL)) return; + if (total_size / size != nmemb) return; + + if (nmemb < 2) return; + + quick_sort(base, nmemb, size, compar, context); +} + +/********************************************************************* + * qsort (MSVCRT.@) + */ +void CDECL qsort(void *base, size_t nmemb, size_t size, + int (CDECL *compar)(const void*, const void*)) +{ + qsort_s(base, nmemb, size, compare_wrapper, compar); +} + +/********************************************************************* + * _get_output_format (MSVCRT.@) + */ +unsigned int CDECL _get_output_format(void) +{ + return output_format; +} + +/********************************************************************* + * _set_output_format (MSVCRT.@) + */ +unsigned int CDECL _set_output_format(unsigned int new_output_format) +{ + unsigned int ret = output_format; + + if(!MSVCRT_CHECK_PMT(new_output_format==0 || new_output_format==_TWO_DIGIT_EXPONENT)) + return ret; + + output_format = new_output_format; + return ret; +} + +/********************************************************************* + * _resetstkoflw (MSVCRT.@) + */ +int CDECL _resetstkoflw(void) +{ + int stack_addr; + DWORD oldprot; + + /* causes stack fault that updates NtCurrentTeb()->Tib.StackLimit */ + return VirtualProtect(&stack_addr, 1, PAGE_GUARD|PAGE_READWRITE, &oldprot); +} + +#if _MSVCR_VER>=80 && _MSVCR_VER<=90 + +/********************************************************************* + * _decode_pointer (MSVCR80.@) + */ +void * CDECL _decode_pointer(void * ptr) +{ + return DecodePointer(ptr); +} + +/********************************************************************* + * _encode_pointer (MSVCR80.@) + */ +void * CDECL _encode_pointer(void * ptr) +{ + return EncodePointer(ptr); +} + +#endif /* _MSVCR_VER>=80 && _MSVCR_VER<=90 */ + +#if _MSVCR_VER>=80 && _MSVCR_VER<=100 +/********************************************************************* + * _encoded_null (MSVCR80.@) + */ +void * CDECL _encoded_null(void) +{ + TRACE("\n"); + + return EncodePointer(NULL); +} +#endif + +#if _MSVCR_VER>=70 +/********************************************************************* + * _CRT_RTC_INIT (MSVCR70.@) + */ +void* CDECL _CRT_RTC_INIT(void *unk1, void *unk2, int unk3, int unk4, int unk5) +{ + TRACE("%p %p %x %x %x\n", unk1, unk2, unk3, unk4, unk5); + return NULL; +} +#endif + +#if _MSVCR_VER>=80 + +/********************************************************************* + * _CRT_RTC_INITW (MSVCR80.@) + */ +void* CDECL _CRT_RTC_INITW(void *unk1, void *unk2, int unk3, int unk4, int unk5) +{ + TRACE("%p %p %x %x %x\n", unk1, unk2, unk3, unk4, unk5); + return NULL; +} + +/********************************************************************* + * _byteswap_ushort (MSVCR80.@) + */ +unsigned short CDECL _byteswap_ushort(unsigned short s) +{ + return (s<<8) + (s>>8); +} + +/********************************************************************* + * _byteswap_ulong (MSVCR80.@) + */ +__msvcrt_ulong CDECL _byteswap_ulong(__msvcrt_ulong l) +{ + return (l<<24) + ((l<<8)&0xFF0000) + ((l>>8)&0xFF00) + (l>>24); +} + +/********************************************************************* + * _byteswap_uint64 (MSVCR80.@) + */ +unsigned __int64 CDECL _byteswap_uint64(unsigned __int64 i) +{ + return (i<<56) + ((i&0xFF00)<<40) + ((i&0xFF0000)<<24) + ((i&0xFF000000)<<8) + + ((i>>8)&0xFF000000) + ((i>>24)&0xFF0000) + ((i>>40)&0xFF00) + (i>>56); +} + +#endif /* _MSVCR_VER>=80 */ + +#if _MSVCR_VER>=110 + +/********************************************************************* + * __crtGetShowWindowMode (MSVCR110.@) + */ +int CDECL __crtGetShowWindowMode(void) +{ + STARTUPINFOW si; + + GetStartupInfoW(&si); + TRACE("flags=%lx window=%d\n", si.dwFlags, si.wShowWindow); + return si.dwFlags & STARTF_USESHOWWINDOW ? si.wShowWindow : SW_SHOWDEFAULT; +} + +/********************************************************************* + * __crtInitializeCriticalSectionEx (MSVCR110.@) + */ +BOOL CDECL __crtInitializeCriticalSectionEx( + CRITICAL_SECTION *cs, DWORD spin_count, DWORD flags) +{ + TRACE("(%p %lx %lx)\n", cs, spin_count, flags); + return InitializeCriticalSectionEx(cs, spin_count, flags); +} + +#endif /* _MSVCR_VER>=110 */ + +#if _MSVCR_VER>=120 +/********************************************************************* + * _vacopy (MSVCR120.@) + */ +void CDECL _vacopy(va_list *dest, va_list src) +{ + va_copy(*dest, src); +} +#endif + +#if _MSVCR_VER>=80 +/********************************************************************* + * _crt_debugger_hook (MSVCR80.@) + */ +void CDECL _crt_debugger_hook(int reserved) +{ + WARN("(%x)\n", reserved); +} +#endif + +#if _MSVCR_VER>=110 +/********************************************************************* + * __crtUnhandledException (MSVCR110.@) + */ +LONG CDECL __crtUnhandledException(EXCEPTION_POINTERS *ep) +{ + TRACE("(%p)\n", ep); + SetUnhandledExceptionFilter(NULL); + return UnhandledExceptionFilter(ep); +} +#endif + +#if _MSVCR_VER>=120 +/********************************************************************* + * __crtSleep (MSVCR120.@) + */ +void CDECL __crtSleep(DWORD timeout) +{ + TRACE("(%lu)\n", timeout); + Sleep(timeout); +} + +/********************************************************************* + * _SetWinRTOutOfMemoryExceptionCallback (MSVCR120.@) + */ +void CDECL _SetWinRTOutOfMemoryExceptionCallback(void *callback) +{ + FIXME("(%p): stub\n", callback); +} +#endif diff --git a/dll/win32/msvcrt/msvcrt.h b/dll/win32/msvcrt/msvcrt.h new file mode 100644 index 00000000000..480d0e238fc --- /dev/null +++ b/dll/win32/msvcrt/msvcrt.h @@ -0,0 +1,458 @@ +/* + * Copyright 2001 Jon Griffiths + * Copyright 2004 Dimitrie O. Paun + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#ifndef __WINE_MSVCRT_H +#define __WINE_MSVCRT_H + +#if _MSVCR_VER >= 140 +#ifndef _FILE_DEFINED +#define _FILE_DEFINED +typedef struct _iobuf +{ + char* _ptr; + char* _base; + int _cnt; + int _flag; + int _file; + int _charbuf; + int _bufsiz; + char* _tmpfname; +} FILE; + +#define _IOREAD 0x0001 +#define _IOWRT 0x0002 +#define _IORW 0x0004 +#define _IOEOF 0x0008 +#define _IOERR 0x0010 +#define _IOMYBUF 0x0040 +#define _IOSTRG 0x1000 +#endif + +#define MSVCRT__NOBUF 0x0400 + +#else + +#define MSVCRT__NOBUF _IONBF + +#endif + +#include +#include +#include +#include +#define _NO_CRT_STDIO_INLINE +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#undef strncpy +#undef wcsncpy + +extern BOOL sse2_supported; + +#define DBL80_MAX_10_EXP 4932 +#define DBL80_MIN_10_EXP -4951 + +typedef void (__cdecl *terminate_function)(void); +typedef void (__cdecl *unexpected_function)(void); +typedef void (__cdecl *_se_translator_function)(unsigned int code, struct _EXCEPTION_POINTERS *info); +void __cdecl terminate(void); + +typedef void (__cdecl *MSVCRT_security_error_handler)(int, void *); + +typedef struct {ULONG x80[3];} MSVCRT__LDOUBLE; /* Intel 80 bit FP format has sizeof() 12 */ + +typedef struct __lc_time_data { + union { + const char *str[43]; + struct { + const char *short_wday[7]; + const char *wday[7]; + const char *short_mon[12]; + const char *mon[12]; + const char *am; + const char *pm; + const char *short_date; + const char *date; + const char *time; + } names; + } str; +#if _MSVCR_VER < 110 + LCID lcid; +#endif + int unk; + LONG refcount; +#if _MSVCR_VER == 0 || _MSVCR_VER >= 100 + union { + const wchar_t *wstr[43]; + struct { + const wchar_t *short_wday[7]; + const wchar_t *wday[7]; + const wchar_t *short_mon[12]; + const wchar_t *mon[12]; + const wchar_t *am; + const wchar_t *pm; + const wchar_t *short_date; + const wchar_t *date; + const wchar_t *time; + } names; + } wstr; +#endif +#if _MSVCR_VER >= 110 + const wchar_t *locname; +#endif + char data[1]; +} __lc_time_data; + +typedef struct threadmbcinfostruct { + LONG refcount; + int mbcodepage; + int ismbcodepage; + int mblcid; + unsigned short mbulinfo[6]; + unsigned char mbctype[257]; + unsigned char mbcasemap[256]; +} threadmbcinfo; + +typedef struct _frame_info +{ + void *object; + struct _frame_info *next; +} frame_info; + +typedef struct +{ + frame_info frame_info; + EXCEPTION_RECORD *rec; + CONTEXT *context; +} cxx_frame_info; + +frame_info* __cdecl _CreateFrameInfo(frame_info *fi, void *obj); +BOOL __cdecl __CxxRegisterExceptionObject(EXCEPTION_POINTERS*, cxx_frame_info*); +void __cdecl __CxxUnregisterExceptionObject(cxx_frame_info*, BOOL); +void CDECL __DestructExceptionObject(EXCEPTION_RECORD*); + +void** __cdecl __current_exception(void); +int* __cdecl __processing_throw(void); + +#if defined(__x86_64__) && _MSVCR_VER>=140 +BOOL msvcrt_init_handler4(void); +void msvcrt_attach_handler4(void); +void msvcrt_free_handler4(void); +#endif + +/* TLS data */ +extern DWORD msvcrt_tls_index; + +#define LOCALE_FREE 0x1 +#define LOCALE_THREAD 0x2 + +/* Keep in sync with msvcr90/tests/msvcr90.c */ +struct __thread_data { + DWORD tid; + HANDLE handle; + int thread_errno; + __msvcrt_ulong thread_doserrno; + int unk1; + unsigned int random_seed; /* seed for rand() */ + char *strtok_next; /* next ptr for strtok() */ + wchar_t *wcstok_next; /* next ptr for wcstok() */ + unsigned char *mbstok_next; /* next ptr for mbstok() */ + char *strerror_buffer; /* buffer for strerror */ + wchar_t *wcserror_buffer; /* buffer for wcserror */ + char *tmpnam_buffer; /* buffer for tmpname() */ + wchar_t *wtmpnam_buffer; /* buffer for wtmpname() */ + void *unk2[2]; + char *asctime_buffer; /* buffer for asctime */ + wchar_t *wasctime_buffer; /* buffer for wasctime */ + struct tm *time_buffer; /* buffer for localtime/gmtime */ + char *efcvt_buffer; /* buffer for ecvt/fcvt */ + int unk3[2]; + void *unk4[3]; + EXCEPTION_POINTERS *xcptinfo; + int fpecode; + pthreadmbcinfo mbcinfo; + pthreadlocinfo locinfo; + int locale_flags; + int unk5[1]; + terminate_function terminate_handler; + unexpected_function unexpected_handler; + _se_translator_function se_translator; /* preserve offset to exc_record and processing_throw */ + void *unk6; + EXCEPTION_RECORD *exc_record; + CONTEXT *ctx_record; + int processing_throw; + frame_info *frame_info_head; + void *unk8[6]; + BOOL cached_sname_match; + WCHAR cached_sname[LOCALE_NAME_MAX_LENGTH]; + int unk9[2]; + DWORD cached_cp; + char cached_locale[131]; + void *unk10[100]; +#if _MSVCR_VER >= 140 + _invalid_parameter_handler invalid_parameter_handler; + HMODULE module; +#endif +}; + +typedef struct __thread_data thread_data_t; + +extern thread_data_t *CDECL msvcrt_get_thread_data(void); + +BOOL locale_to_sname(const char*, unsigned short*, BOOL*, WCHAR*); +extern _locale_t MSVCRT_locale; +extern __lc_time_data cloc_time_data; +extern unsigned int MSVCRT___lc_codepage; +extern int MSVCRT___lc_collate_cp; +extern WORD MSVCRT__ctype [257]; +extern BOOL initial_locale; +extern WORD *MSVCRT__pwctype; + +void msvcrt_set_errno(int); +#if _MSVCR_VER >= 80 +void throw_bad_alloc(void); +#endif + +void __cdecl _purecall(void); +void __cdecl _amsg_exit(int errnum); + +extern char **MSVCRT__environ; +extern wchar_t **MSVCRT__wenviron; +extern char **MSVCRT___initenv; +extern wchar_t **MSVCRT___winitenv; + +int env_init(BOOL, BOOL); + +wchar_t *msvcrt_wstrdupa(const char *); + +extern unsigned int MSVCRT__commode; + +/* FIXME: This should be declared in new.h but it's not an extern "C" so + * it would not be much use anyway. Even for Winelib applications. + */ +void* __cdecl operator_new(size_t); +void __cdecl operator_delete(void*); +int __cdecl _set_new_mode(int mode); + +typedef void* (__cdecl *malloc_func_t)(size_t); +typedef void (__cdecl *free_func_t)(void*); + +/* Setup and teardown multi threaded locks */ +extern void msvcrt_init_mt_locks(void); +extern void msvcrt_free_locks(void); + +extern void msvcrt_init_exception(void*); +extern BOOL msvcrt_init_locale(void); +extern void msvcrt_init_math(void*); +extern void msvcrt_init_io(void); +extern void msvcrt_free_io(void); +extern void msvcrt_free_console(void); +extern void msvcrt_init_args(void); +extern void msvcrt_free_args(void); +extern void msvcrt_init_signals(void); +extern void msvcrt_free_signals(void); +extern void msvcrt_free_popen_data(void); +extern BOOL msvcrt_init_heap(void); +extern void msvcrt_destroy_heap(void); +extern void msvcrt_init_clock(void); + +#if _MSVCR_VER >= 100 +extern void msvcrt_init_concurrency(void*); +extern void msvcrt_free_concurrency(void); +extern void msvcrt_free_scheduler_thread(void); +#endif + +extern BOOL msvcrt_create_io_inherit_block(WORD*, BYTE**); + +/* run-time error codes */ +#define _RT_STACK 0 +#define _RT_NULLPTR 1 +#define _RT_FLOAT 2 +#define _RT_INTDIV 3 +#define _RT_EXECMEM 5 +#define _RT_EXECFORM 6 +#define _RT_EXECENV 7 +#define _RT_SPACEARG 8 +#define _RT_SPACEENV 9 +#define _RT_ABORT 10 +#define _RT_NPTR 12 +#define _RT_FPTR 13 +#define _RT_BREAK 14 +#define _RT_INT 15 +#define _RT_THREAD 16 +#define _RT_LOCK 17 +#define _RT_HEAP 18 +#define _RT_OPENCON 19 +#define _RT_QWIN 20 +#define _RT_NOMAIN 21 +#define _RT_NONCONT 22 +#define _RT_INVALDISP 23 +#define _RT_ONEXIT 24 +#define _RT_PUREVIRT 25 +#define _RT_STDIOINIT 26 +#define _RT_LOWIOINIT 27 +#define _RT_HEAPINIT 28 +#define _RT_DOMAIN 120 +#define _RT_SING 121 +#define _RT_TLOSS 122 +#define _RT_CRNL 252 +#define _RT_BANNER 255 + +#define MSVCRT_NO_CONSOLE_FD (-2) +#define MSVCRT_NO_CONSOLE ((HANDLE)MSVCRT_NO_CONSOLE_FD) + +#if _MSVCR_VER < 140 +extern FILE MSVCRT__iob[]; +#define __acrt_iob_func(idx) (MSVCRT__iob+(idx)) +#endif + +/* internal file._flag flags */ +#define MSVCRT__USERBUF 0x0100 +#define MSVCRT__IOCOMMIT 0x4000 + +#define _MAX__TIME64_T (((__time64_t)0x00000007 << 32) | 0x93406FFF) + +_locale_t CDECL get_current_locale_noalloc(_locale_t locale); +void CDECL free_locale_noalloc(_locale_t locale); +pthreadlocinfo CDECL get_locinfo(void); +pthreadmbcinfo CDECL get_mbcinfo(void); +threadmbcinfo* create_mbcinfo(int, LCID, threadmbcinfo*); +void free_locinfo(pthreadlocinfo); +void free_mbcinfo(pthreadmbcinfo); +int __cdecl __crtLCMapStringA(LCID, DWORD, const char*, int, char*, int, unsigned int, int); + +enum fpmod { + FP_ROUND_ZERO, /* only used when dropped part contains only zeros */ + FP_ROUND_DOWN, + FP_ROUND_EVEN, + FP_ROUND_UP, + FP_VAL_INFINITY, + FP_VAL_NAN +}; + +struct fpnum { + int sign; + int exp; + ULONGLONG m; + enum fpmod mod; +}; +struct fpnum fpnum_parse(wchar_t (*)(void*), void (*)(void*), + void*, pthreadlocinfo, BOOL); +int fpnum_double(struct fpnum*, double*); +/* Maybe one day we'll enable the invalid parameter handlers with the full set of information (msvcrXXd) + * #define MSVCRT_INVALID_PMT(x) MSVCRT_call_invalid_parameter_handler(x, __FUNCTION__, __FILE__, __LINE__, 0) + * #define MSVCRT_CHECK_PMT(x) ((x) ? TRUE : MSVCRT_INVALID_PMT(#x),FALSE) + * Until this is done, just keep the same semantics for CHECK_PMT(), but without generating / sending + * any information + * NB : MSVCRT_call_invalid_parameter_handler is a wrapper around _invalid_parameter in order + * to do the Ansi to Unicode transformation + */ +#define MSVCRT_INVALID_PMT(x,err) (*_errno() = (err), _invalid_parameter(NULL, NULL, NULL, 0, 0)) +#define MSVCRT_CHECK_PMT_ERR(x,err) ((x) || (MSVCRT_INVALID_PMT( 0, (err) ), FALSE)) +#define MSVCRT_CHECK_PMT(x) MSVCRT_CHECK_PMT_ERR((x), EINVAL) + +typedef int (*puts_clbk_a)(void*, int, const char*); +typedef int (*puts_clbk_w)(void*, int, const wchar_t*); +typedef union _printf_arg +{ + void *get_ptr; + int get_int; + LONGLONG get_longlong; + double get_double; +} printf_arg; +typedef printf_arg (*args_clbk)(void*, int, int, va_list*); +int pf_printf_a(puts_clbk_a, void*, const char*, _locale_t, + DWORD, args_clbk, void*, va_list*); +int pf_printf_w(puts_clbk_w, void*, const wchar_t*, _locale_t, + DWORD, args_clbk, void*, va_list*); +int create_positional_ctx_a(void*, const char*, va_list); +int create_positional_ctx_w(void*, const wchar_t*, va_list); +printf_arg arg_clbk_valist(void*, int, int, va_list*); +printf_arg arg_clbk_positional(void*, int, int, va_list*); + +extern char* __cdecl __unDName(char *,const char*,int,malloc_func_t,free_func_t,unsigned short int); + +#define UCRTBASE_PRINTF_MASK ( \ + _CRT_INTERNAL_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION | \ + _CRT_INTERNAL_PRINTF_STANDARD_SNPRINTF_BEHAVIOR | \ + _CRT_INTERNAL_PRINTF_LEGACY_WIDE_SPECIFIERS | \ + _CRT_INTERNAL_PRINTF_LEGACY_MSVCRT_COMPATIBILITY | \ + _CRT_INTERNAL_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS | \ + _CRT_INTERNAL_PRINTF_STANDARD_ROUNDING ) + +#define MSVCRT_PRINTF_POSITIONAL_PARAMS (0x0100) +#define MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER (0x0200) + +#define UCRTBASE_SCANF_MASK ( \ + _CRT_INTERNAL_SCANF_SECURECRT | \ + _CRT_INTERNAL_SCANF_LEGACY_WIDE_SPECIFIERS | \ + _CRT_INTERNAL_SCANF_LEGACY_MSVCRT_COMPATIBILITY ) + +#define COOPERATIVE_TIMEOUT_INFINITE ((unsigned int)-1) +#define COOPERATIVE_WAIT_TIMEOUT ~0 + +#define INHERIT_THREAD_PRIORITY 0xF000 + +static inline UINT get_aw_cp(void) +{ +#if _MSVCR_VER>=140 + if (___lc_codepage_func() == CP_UTF8) return CP_UTF8; +#endif + return CP_ACP; +} + +static inline int convert_acp_utf8_to_wcs(const char *str, wchar_t *wstr, int len) +{ + return MultiByteToWideChar(get_aw_cp(), MB_PRECOMPOSED, str, -1, wstr, len); +} + +static inline int convert_wcs_to_acp_utf8(const wchar_t *wstr, char *str, int len) +{ + return WideCharToMultiByte(get_aw_cp(), 0, wstr, -1, str, len, NULL, NULL); +} + +static inline wchar_t* wstrdupa_utf8(const char *str) +{ + int len = convert_acp_utf8_to_wcs(str, NULL, 0); + wchar_t *wstr; + + if (!len) return NULL; + wstr = malloc(len * sizeof(wchar_t)); + if (!wstr) return NULL; + convert_acp_utf8_to_wcs(str, wstr, len); + return wstr; +} + +static inline char* astrdupw_utf8(const wchar_t *wstr) +{ + int len = convert_wcs_to_acp_utf8(wstr, NULL, 0); + char *str; + + if (!len) return NULL; + str = malloc(len * sizeof(char)); + if (!str) return NULL; + convert_wcs_to_acp_utf8(wstr, str, len); + return str; +} + +#endif /* __WINE_MSVCRT_H */ diff --git a/dll/win32/msvcrt/msvcrt.spec b/dll/win32/msvcrt/msvcrt.spec new file mode 100644 index 00000000000..27cd61376e4 --- /dev/null +++ b/dll/win32/msvcrt/msvcrt.spec @@ -0,0 +1,1533 @@ +# msvcrt.dll - MS VC++ Run Time Library + +@ cdecl -norelay $I10_OUTPUT(double long long ptr) I10_OUTPUT +@ cdecl -arch=arm ??0__non_rtti_object@@QAA@ABV0@@Z(ptr ptr) __non_rtti_object_copy_ctor +@ thiscall -arch=i386 ??0__non_rtti_object@@QAE@ABV0@@Z(ptr ptr) __non_rtti_object_copy_ctor +@ cdecl -arch=win64 ??0__non_rtti_object@@QEAA@AEBV0@@Z(ptr ptr) __non_rtti_object_copy_ctor +@ cdecl -arch=arm ??0__non_rtti_object@@QAA@PBD@Z(ptr ptr) __non_rtti_object_ctor +@ thiscall -arch=i386 ??0__non_rtti_object@@QAE@PBD@Z(ptr ptr) __non_rtti_object_ctor +@ cdecl -arch=win64 ??0__non_rtti_object@@QEAA@PEBD@Z(ptr ptr) __non_rtti_object_ctor +@ cdecl -arch=arm ??0bad_cast@@AAA@PBQBD@Z(ptr ptr) bad_cast_ctor +@ thiscall -arch=i386 ??0bad_cast@@AAE@PBQBD@Z(ptr ptr) bad_cast_ctor +@ cdecl -arch=win64 ??0bad_cast@@AEAA@PEBQEBD@Z(ptr ptr) bad_cast_ctor +@ cdecl -arch=arm ??0bad_cast@@QAA@ABQBD@Z(ptr ptr) bad_cast_ctor +@ thiscall -arch=i386 ??0bad_cast@@QAE@ABQBD@Z(ptr ptr) bad_cast_ctor +@ cdecl -arch=win64 ??0bad_cast@@QEAA@AEBQEBD@Z(ptr ptr) bad_cast_ctor +@ cdecl -arch=arm ??0bad_cast@@QAA@ABV0@@Z(ptr ptr) bad_cast_copy_ctor +@ thiscall -arch=i386 ??0bad_cast@@QAE@ABV0@@Z(ptr ptr) bad_cast_copy_ctor +@ cdecl -arch=win64 ??0bad_cast@@QEAA@AEBV0@@Z(ptr ptr) bad_cast_copy_ctor +@ cdecl -arch=arm ??0bad_cast@@QAA@PBD@Z(ptr str) bad_cast_ctor_charptr +@ thiscall -arch=i386 ??0bad_cast@@QAE@PBD@Z(ptr str) bad_cast_ctor_charptr +@ cdecl -arch=win64 ??0bad_cast@@QEAA@PEBD@Z(ptr str) bad_cast_ctor_charptr +@ cdecl -arch=arm ??0bad_typeid@@QAA@ABV0@@Z(ptr ptr) bad_typeid_copy_ctor +@ thiscall -arch=i386 ??0bad_typeid@@QAE@ABV0@@Z(ptr ptr) bad_typeid_copy_ctor +@ cdecl -arch=win64 ??0bad_typeid@@QEAA@AEBV0@@Z(ptr ptr) bad_typeid_copy_ctor +@ cdecl -arch=arm ??0bad_typeid@@QAA@PBD@Z(ptr str) bad_typeid_ctor +@ thiscall -arch=i386 ??0bad_typeid@@QAE@PBD@Z(ptr str) bad_typeid_ctor +@ cdecl -arch=win64 ??0bad_typeid@@QEAA@PEBD@Z(ptr str) bad_typeid_ctor +@ cdecl -arch=arm ??0exception@@QAA@ABQBD@Z(ptr ptr) exception_ctor +@ thiscall -arch=i386 ??0exception@@QAE@ABQBD@Z(ptr ptr) exception_ctor +@ cdecl -arch=win64 ??0exception@@QEAA@AEBQEBD@Z(ptr ptr) exception_ctor +@ cdecl -arch=arm ??0exception@@QAA@ABQBDH@Z(ptr ptr long) exception_ctor_noalloc +@ thiscall -arch=i386 ??0exception@@QAE@ABQBDH@Z(ptr ptr long) exception_ctor_noalloc +@ cdecl -arch=win64 ??0exception@@QEAA@AEBQEBDH@Z(ptr ptr long) exception_ctor_noalloc +@ cdecl -arch=arm ??0exception@@QAA@ABV0@@Z(ptr ptr) exception_copy_ctor +@ thiscall -arch=i386 ??0exception@@QAE@ABV0@@Z(ptr ptr) exception_copy_ctor +@ cdecl -arch=win64 ??0exception@@QEAA@AEBV0@@Z(ptr ptr) exception_copy_ctor +@ cdecl -arch=arm ??0exception@@QAA@XZ(ptr) exception_default_ctor +@ thiscall -arch=i386 ??0exception@@QAE@XZ(ptr) exception_default_ctor +@ cdecl -arch=win64 ??0exception@@QEAA@XZ(ptr) exception_default_ctor +@ cdecl -arch=arm ??1__non_rtti_object@@UAA@XZ(ptr) __non_rtti_object_dtor +@ thiscall -arch=i386 ??1__non_rtti_object@@UAE@XZ(ptr) __non_rtti_object_dtor +@ cdecl -arch=win64 ??1__non_rtti_object@@UEAA@XZ(ptr) __non_rtti_object_dtor +@ cdecl -arch=arm ??1bad_cast@@UAA@XZ(ptr) bad_cast_dtor +@ thiscall -arch=i386 ??1bad_cast@@UAE@XZ(ptr) bad_cast_dtor +@ cdecl -arch=win64 ??1bad_cast@@UEAA@XZ(ptr) bad_cast_dtor +@ cdecl -arch=arm ??1bad_typeid@@UAA@XZ(ptr) bad_typeid_dtor +@ thiscall -arch=i386 ??1bad_typeid@@UAE@XZ(ptr) bad_typeid_dtor +@ cdecl -arch=win64 ??1bad_typeid@@UEAA@XZ(ptr) bad_typeid_dtor +@ cdecl -arch=arm ??1exception@@UAA@XZ(ptr) exception_dtor +@ thiscall -arch=i386 ??1exception@@UAE@XZ(ptr) exception_dtor +@ cdecl -arch=win64 ??1exception@@UEAA@XZ(ptr) exception_dtor +@ cdecl -arch=arm ??1type_info@@UAA@XZ(ptr) type_info_dtor +@ thiscall -arch=i386 ??1type_info@@UAE@XZ(ptr) type_info_dtor +@ cdecl -arch=win64 ??1type_info@@UEAA@XZ(ptr) type_info_dtor +@ cdecl -arch=win32 ??2@YAPAXI@Z(long) operator_new +@ cdecl -arch=win64 ??2@YAPEAX_K@Z(long) operator_new +@ cdecl -arch=win32 ??2@YAPAXIHPBDH@Z(long long str long) operator_new_dbg +@ cdecl -arch=win64 ??2@YAPEAX_KHPEBDH@Z(long long str long) operator_new_dbg +@ cdecl -arch=win32 ??3@YAXPAX@Z(ptr) operator_delete +@ cdecl -arch=win64 ??3@YAXPEAX@Z(ptr) operator_delete +@ cdecl -arch=arm ??4__non_rtti_object@@QAAAAV0@ABV0@@Z(ptr ptr) __non_rtti_object_opequals +@ thiscall -arch=i386 ??4__non_rtti_object@@QAEAAV0@ABV0@@Z(ptr ptr) __non_rtti_object_opequals +@ cdecl -arch=win64 ??4__non_rtti_object@@QEAAAEAV0@AEBV0@@Z(ptr ptr) __non_rtti_object_opequals +@ cdecl -arch=arm ??4bad_cast@@QAAAAV0@ABV0@@Z(ptr ptr) bad_cast_opequals +@ thiscall -arch=i386 ??4bad_cast@@QAEAAV0@ABV0@@Z(ptr ptr) bad_cast_opequals +@ cdecl -arch=win64 ??4bad_cast@@QEAAAEAV0@AEBV0@@Z(ptr ptr) bad_cast_opequals +@ cdecl -arch=arm ??4bad_typeid@@QAAAAV0@ABV0@@Z(ptr ptr) bad_typeid_opequals +@ thiscall -arch=i386 ??4bad_typeid@@QAEAAV0@ABV0@@Z(ptr ptr) bad_typeid_opequals +@ cdecl -arch=win64 ??4bad_typeid@@QEAAAEAV0@AEBV0@@Z(ptr ptr) bad_typeid_opequals +@ cdecl -arch=arm ??4exception@@QAAAAV0@ABV0@@Z(ptr ptr) exception_opequals +@ thiscall -arch=i386 ??4exception@@QAEAAV0@ABV0@@Z(ptr ptr) exception_opequals +@ cdecl -arch=win64 ??4exception@@QEAAAEAV0@AEBV0@@Z(ptr ptr) exception_opequals +@ cdecl -arch=arm ??8type_info@@QBAHABV0@@Z(ptr ptr) type_info_opequals_equals +@ thiscall -arch=i386 ??8type_info@@QBEHABV0@@Z(ptr ptr) type_info_opequals_equals +@ cdecl -arch=win64 ??8type_info@@QEBAHAEBV0@@Z(ptr ptr) type_info_opequals_equals +@ cdecl -arch=arm ??9type_info@@QBAHABV0@@Z(ptr ptr) type_info_opnot_equals +@ thiscall -arch=i386 ??9type_info@@QBEHABV0@@Z(ptr ptr) type_info_opnot_equals +@ cdecl -arch=win64 ??9type_info@@QEBAHAEBV0@@Z(ptr ptr) type_info_opnot_equals +@ extern ??_7__non_rtti_object@@6B@ __non_rtti_object_vtable +@ extern ??_7bad_cast@@6B@ bad_cast_vtable +@ extern ??_7bad_typeid@@6B@ bad_typeid_vtable +@ extern ??_7exception@@6B@ exception_vtable +@ thiscall -arch=i386 ??_E__non_rtti_object@@UAEPAXI@Z(ptr long) __non_rtti_object_vector_dtor +@ thiscall -arch=i386 ??_Ebad_cast@@UAEPAXI@Z(ptr long) bad_cast_vector_dtor +@ thiscall -arch=i386 ??_Ebad_typeid@@UAEPAXI@Z(ptr long) bad_typeid_vector_dtor +@ thiscall -arch=i386 ??_Eexception@@UAEPAXI@Z(ptr long) exception_vector_dtor +@ cdecl -arch=arm ??_Fbad_cast@@QAAXXZ(ptr) bad_cast_default_ctor +@ thiscall -arch=i386 ??_Fbad_cast@@QAEXXZ(ptr) bad_cast_default_ctor +@ cdecl -arch=win64 ??_Fbad_cast@@QEAAXXZ(ptr) bad_cast_default_ctor +@ cdecl -arch=arm ??_Fbad_typeid@@QAAXXZ(ptr) bad_typeid_default_ctor +@ thiscall -arch=i386 ??_Fbad_typeid@@QAEXXZ(ptr) bad_typeid_default_ctor +@ cdecl -arch=win64 ??_Fbad_typeid@@QEAAXXZ(ptr) bad_typeid_default_ctor +@ thiscall -arch=i386 ??_G__non_rtti_object@@UAEPAXI@Z(ptr long) __non_rtti_object_scalar_dtor +@ thiscall -arch=i386 ??_Gbad_cast@@UAEPAXI@Z(ptr long) bad_cast_scalar_dtor +@ thiscall -arch=i386 ??_Gbad_typeid@@UAEPAXI@Z(ptr long) bad_typeid_scalar_dtor +@ thiscall -arch=i386 ??_Gexception@@UAEPAXI@Z(ptr long) exception_scalar_dtor +@ cdecl -arch=win32 ??_U@YAPAXI@Z(long) operator_new +@ cdecl -arch=win64 ??_U@YAPEAX_K@Z(long) operator_new +@ cdecl -arch=win32 ??_U@YAPAXIHPBDH@Z(long long str long) operator_new_dbg +@ cdecl -arch=win64 ??_U@YAPEAX_KHPEBDH@Z(long long str long) operator_new_dbg +@ cdecl -arch=win32 ??_V@YAXPAX@Z(ptr) operator_delete +@ cdecl -arch=win64 ??_V@YAXPEAX@Z(ptr) operator_delete +@ cdecl -arch=win32 ?_query_new_handler@@YAP6AHI@ZXZ() _query_new_handler +@ cdecl -arch=win64 ?_query_new_handler@@YAP6AH_K@ZXZ() _query_new_handler +@ cdecl ?_query_new_mode@@YAHXZ() _query_new_mode +@ cdecl -arch=win32 ?_set_new_handler@@YAP6AHI@ZP6AHI@Z@Z(ptr) _set_new_handler +@ cdecl -arch=win64 ?_set_new_handler@@YAP6AH_K@ZP6AH0@Z@Z(ptr) _set_new_handler +@ cdecl ?_set_new_mode@@YAHH@Z(long) _set_new_mode +@ cdecl -arch=win32 ?_set_se_translator@@YAP6AXIPAU_EXCEPTION_POINTERS@@@ZP6AXI0@Z@Z(ptr) _set_se_translator +@ cdecl -arch=win64 ?_set_se_translator@@YAP6AXIPEAU_EXCEPTION_POINTERS@@@ZP6AXI0@Z@Z(ptr) _set_se_translator +@ cdecl -arch=arm ?before@type_info@@QBAHABV1@@Z(ptr ptr) type_info_before +@ thiscall -arch=i386 ?before@type_info@@QBEHABV1@@Z(ptr ptr) type_info_before +@ cdecl -arch=win64 ?before@type_info@@QEBAHAEBV1@@Z(ptr ptr) type_info_before +@ cdecl -arch=arm ?name@type_info@@QBAPBDXZ(ptr) type_info_name +@ thiscall -arch=i386 ?name@type_info@@QBEPBDXZ(ptr) type_info_name +@ cdecl -arch=win64 ?name@type_info@@QEBAPEBDXZ(ptr) type_info_name +@ cdecl -arch=arm ?raw_name@type_info@@QBAPBDXZ(ptr) type_info_raw_name +@ thiscall -arch=i386 ?raw_name@type_info@@QBEPBDXZ(ptr) type_info_raw_name +@ cdecl -arch=win64 ?raw_name@type_info@@QEBAPEBDXZ(ptr) type_info_raw_name +@ cdecl ?set_new_handler@@YAP6AXXZP6AXXZ@Z(ptr) set_new_handler +@ cdecl ?set_terminate@@YAP6AXXZP6AXXZ@Z(ptr) set_terminate +@ cdecl ?set_unexpected@@YAP6AXXZP6AXXZ@Z(ptr) set_unexpected +@ cdecl ?terminate@@YAXXZ() terminate +@ cdecl ?unexpected@@YAXXZ() unexpected +@ cdecl -arch=arm ?what@exception@@UBAPBDXZ(ptr) exception_what +@ thiscall -arch=i386 ?what@exception@@UBEPBDXZ(ptr) exception_what +@ cdecl -arch=win64 ?what@exception@@UEBAPEBDXZ(ptr) exception_what +@ cdecl -arch=i386 _CIacos() +@ cdecl -arch=i386 _CIasin() +@ cdecl -arch=i386 _CIatan() +@ cdecl -arch=i386 _CIatan2() +@ cdecl -arch=i386 _CIcos() +@ cdecl -arch=i386 _CIcosh() +@ cdecl -arch=i386 _CIexp() +@ cdecl -arch=i386 _CIfmod() +@ cdecl -arch=i386 _CIlog() +@ cdecl -arch=i386 _CIlog10() +@ cdecl -arch=i386 _CIpow() +@ cdecl -arch=i386 _CIsin() +@ cdecl -arch=i386 _CIsinh() +@ cdecl -arch=i386 _CIsqrt() +@ cdecl -arch=i386 _CItan() +@ cdecl -arch=i386 _CItanh() +# stub _CrtCheckMemory +# stub _CrtDbgBreak +# stub _CrtDbgReport +# stub _CrtDbgReportV +# stub _CrtDbgReportW +# stub _CrtDbgReportWV +# stub _CrtDoForAllClientObjects +# stub _CrtDumpMemoryLeaks +# stub _CrtIsMemoryBlock +# stub _CrtIsValidHeapPointer +# stub _CrtIsValidPointer +# stub _CrtMemCheckpoint +# stub _CrtMemDifference +# stub _CrtMemDumpAllObjectsSince +# stub _CrtMemDumpStatistics +# stub _CrtReportBlockType +# stub _CrtSetAllocHook +# stub _CrtSetBreakAlloc +# stub _CrtSetDbgBlockType +# stub _CrtSetDbgFlag +# stub _CrtSetDumpClient +# stub _CrtSetReportFile +# stub _CrtSetReportHook +# stub _CrtSetReportHook2 +# stub _CrtSetReportMode +@ stdcall _CxxThrowException(ptr ptr) +@ cdecl -arch=i386 -norelay _EH_prolog() +@ cdecl _Getdays() +@ cdecl _Getmonths() +@ cdecl _Gettnames() +@ extern _HUGE MSVCRT__HUGE +@ cdecl _Strftime(ptr long str ptr ptr) +@ cdecl _XcptFilter(long ptr) +@ stdcall -arch=!i386 __C_specific_handler(ptr long ptr ptr) ntdll.__C_specific_handler +@ cdecl __CppXcptFilter(long ptr) +# stub __CxxCallUnwindDelDtor +# stub __CxxCallUnwindDtor +# stub __CxxCallUnwindVecDtor +@ cdecl __CxxDetectRethrow(ptr) +@ cdecl __CxxExceptionFilter(ptr ptr long ptr) +@ cdecl -norelay __CxxFrameHandler(ptr ptr ptr ptr) +@ cdecl -norelay __CxxFrameHandler2(ptr ptr ptr ptr) __CxxFrameHandler +@ cdecl -norelay __CxxFrameHandler3(ptr ptr ptr ptr) __CxxFrameHandler +@ stdcall -arch=i386 __CxxLongjmpUnwind(ptr) +@ cdecl __CxxQueryExceptionSize() +@ cdecl __CxxRegisterExceptionObject(ptr ptr) +@ cdecl __CxxUnregisterExceptionObject(ptr long) +@ cdecl __DestructExceptionObject(ptr) +@ cdecl __RTCastToVoid(ptr) +@ cdecl __RTDynamicCast(ptr long ptr ptr long) +@ cdecl __RTtypeid(ptr) +@ cdecl __STRINGTOLD(ptr ptr str long) +@ cdecl ___lc_codepage_func() +@ cdecl ___lc_collate_cp_func() +@ cdecl ___lc_handle_func() +@ cdecl ___mb_cur_max_func() +@ cdecl ___setlc_active_func() +@ cdecl ___unguarded_readlc_active_add_func() +@ extern __argc MSVCRT___argc +@ extern __argv MSVCRT___argv +@ extern __badioinfo MSVCRT___badioinfo +@ cdecl __crtCompareStringA(long long str long str long) +@ cdecl __crtCompareStringW(long long wstr long wstr long) +@ cdecl __crtGetLocaleInfoW(long long ptr long) +@ cdecl __crtGetStringTypeW(long long wstr long ptr) +@ cdecl __crtLCMapStringA(long long str long ptr long long long) +@ cdecl __crtLCMapStringW(long long wstr long ptr long long long) +@ cdecl __daylight() __p__daylight +@ cdecl __dllonexit(ptr ptr ptr) +@ cdecl __doserrno() +@ cdecl __dstbias() __p__dstbias +@ cdecl __fpecode() +@ stub __get_app_type +@ cdecl __getmainargs(ptr ptr ptr long ptr) +@ extern __initenv MSVCRT___initenv +@ cdecl __iob_func() +@ cdecl __isascii(long) +@ cdecl __iscsym(long) +@ cdecl __iscsymf(long) +@ stdcall -arch=arm __jump_unwind(ptr ptr) ntdll.__jump_unwind +@ extern __lc_codepage MSVCRT___lc_codepage +@ stub __lc_collate +@ extern __lc_collate_cp MSVCRT___lc_collate_cp +@ extern __lc_handle MSVCRT___lc_handle +@ cdecl __lconv_init() +@ cdecl -arch=i386 -norelay __libm_sse2_acos() +@ cdecl -arch=i386 -norelay __libm_sse2_acosf() +@ cdecl -arch=i386 -norelay __libm_sse2_asin() +@ cdecl -arch=i386 -norelay __libm_sse2_asinf() +@ cdecl -arch=i386 -norelay __libm_sse2_atan() +@ cdecl -arch=i386 -norelay __libm_sse2_atan2() +@ cdecl -arch=i386 -norelay __libm_sse2_atanf() +@ cdecl -arch=i386 -norelay __libm_sse2_cos() +@ cdecl -arch=i386 -norelay __libm_sse2_cosf() +@ cdecl -arch=i386 -norelay __libm_sse2_exp() +@ cdecl -arch=i386 -norelay __libm_sse2_expf() +@ cdecl -arch=i386 -norelay __libm_sse2_log() +@ cdecl -arch=i386 -norelay __libm_sse2_log10() +@ cdecl -arch=i386 -norelay __libm_sse2_log10f() +@ cdecl -arch=i386 -norelay __libm_sse2_logf() +@ cdecl -arch=i386 -norelay __libm_sse2_pow() +@ cdecl -arch=i386 -norelay __libm_sse2_powf() +@ cdecl -arch=i386 -norelay __libm_sse2_sin() +@ cdecl -arch=i386 -norelay __libm_sse2_sinf() +@ cdecl -arch=i386 -norelay __libm_sse2_tan() +@ cdecl -arch=i386 -norelay __libm_sse2_tanf() +@ extern __mb_cur_max MSVCRT___mb_cur_max +@ cdecl -arch=i386 __p___argc() +@ cdecl -arch=i386 __p___argv() +@ cdecl -arch=i386 __p___initenv() +@ cdecl -arch=i386 __p___mb_cur_max() +@ cdecl -arch=i386 __p___wargv() +@ cdecl -arch=i386 __p___winitenv() +@ cdecl -arch=i386 __p__acmdln() +@ cdecl -arch=i386 __p__amblksiz() +@ cdecl -arch=i386 __p__commode() +@ cdecl -arch=i386 __p__daylight() +@ cdecl -arch=i386 __p__dstbias() +@ cdecl -arch=i386 __p__environ() +@ stub -arch=i386 __p__fileinfo() +@ cdecl -arch=i386 __p__fmode() +@ cdecl -arch=i386 __p__iob() __iob_func +@ stub -arch=i386 __p__mbcasemap() +@ cdecl -arch=i386 __p__mbctype() +@ cdecl -arch=i386 __p__osver() +@ cdecl -arch=i386 __p__pctype() +@ cdecl -arch=i386 __p__pgmptr() +@ cdecl -arch=i386 __p__pwctype() +@ cdecl -arch=i386 __p__timezone() +@ cdecl -arch=i386 __p__tzname() +@ cdecl -arch=i386 __p__wcmdln() +@ cdecl -arch=i386 __p__wenviron() +@ cdecl -arch=i386 __p__winmajor() +@ cdecl -arch=i386 __p__winminor() +@ cdecl -arch=i386 __p__winver() +@ cdecl -arch=i386 __p__wpgmptr() +@ cdecl __pctype_func() +@ extern __pioinfo MSVCRT___pioinfo +@ cdecl __pwctype_func() +@ cdecl __pxcptinfoptrs() +@ cdecl __set_app_type(long) +@ extern __setlc_active MSVCRT___setlc_active +@ cdecl __setusermatherr(ptr) +@ cdecl __strncnt(str long) +@ cdecl __threadhandle() kernel32.GetCurrentThread +@ cdecl __threadid() kernel32.GetCurrentThreadId +@ cdecl __toascii(long) +@ cdecl __uncaught_exception() +@ cdecl __unDName(ptr str long ptr ptr long) +@ cdecl __unDNameEx(ptr str long ptr ptr ptr long) +@ extern __unguarded_readlc_active MSVCRT___unguarded_readlc_active +@ extern __wargv MSVCRT___wargv +@ cdecl __wcserror(wstr) +@ cdecl __wcserror_s(ptr long wstr) +# stub __wcsncnt(wstr long) +@ cdecl __wgetmainargs(ptr ptr ptr long ptr) +@ extern __winitenv MSVCRT___winitenv +@ cdecl _abnormal_termination() __intrinsic_abnormal_termination +@ cdecl -ret64 _abs64(int64) +@ cdecl _access(str long) +@ cdecl _access_s(str long) +@ extern _acmdln MSVCRT__acmdln +@ stdcall -arch=i386 _adj_fdiv_m16i(long) +@ stdcall -arch=i386 _adj_fdiv_m32(long) +@ stdcall -arch=i386 _adj_fdiv_m32i(long) +@ stdcall -arch=i386 _adj_fdiv_m64(int64) +@ cdecl -arch=i386 _adj_fdiv_r() +@ stdcall -arch=i386 _adj_fdivr_m16i(long) +@ stdcall -arch=i386 _adj_fdivr_m32(long) +@ stdcall -arch=i386 _adj_fdivr_m32i(long) +@ stdcall -arch=i386 _adj_fdivr_m64(int64) +@ cdecl -arch=i386 _adj_fpatan() +@ cdecl -arch=i386 _adj_fprem() +@ cdecl -arch=i386 _adj_fprem1() +@ cdecl -arch=i386 _adj_fptan() +@ extern -arch=i386 _adjust_fdiv MSVCRT__adjust_fdiv +@ extern _aexit_rtn +@ cdecl _aligned_free(ptr) +# stub _aligned_free_dbg(ptr) +@ cdecl _aligned_malloc(long long) +# stub _aligned_malloc_dbg(long long str long) +@ cdecl _aligned_offset_malloc(long long long) +# stub _aligned_offset_malloc_dbg(long long long str long) +@ cdecl _aligned_offset_realloc(ptr long long long) +# stub _aligned_offset_realloc_dbg(ptr long long long str long) +@ cdecl _aligned_realloc(ptr long long) +# stub _aligned_realloc_dbg(ptr long long str long) +@ cdecl _amsg_exit(long) +@ cdecl _assert(str str long) +@ cdecl _atodbl(ptr str) +@ cdecl _atodbl_l(ptr str ptr) +@ cdecl _atof_l(str ptr) +@ cdecl _atoflt_l(ptr str ptr) +@ cdecl -ret64 _atoi64(str) +@ cdecl -ret64 _atoi64_l(str ptr) +@ cdecl _atoi_l(str ptr) +@ cdecl _atol_l(str ptr) +@ cdecl _atoldbl(ptr str) +@ cdecl _atoldbl_l(ptr str ptr) +@ cdecl _beep(long long) +@ cdecl _beginthread (ptr long ptr) +@ cdecl _beginthreadex (ptr long ptr ptr long ptr) +@ cdecl _c_exit() +@ cdecl _cabs(long) +@ cdecl _callnewh(long) +# stub _calloc_dbg(long long long str long) +@ cdecl _cexit() +@ cdecl _cgets(ptr) +# stub _cgets_s(ptr long ptr) +# stub _cgetws(ptr) +# stub _cgetws_s(ptr long ptr) +@ cdecl _chdir(str) +@ cdecl _chdrive(long) +@ cdecl _chgsign(double) +@ cdecl -arch=!i386 _chgsignf(float) +@ cdecl -arch=i386 -norelay _chkesp() +@ cdecl _chmod(str long) +@ cdecl _chsize(long long) +@ cdecl _chsize_s(long int64) +# stub _chvalidator(long long) +# stub _chvalidator_l(ptr long long) +@ cdecl _clearfp() +@ cdecl _close(long) +@ cdecl _commit(long) +@ extern _commode MSVCRT__commode +@ cdecl _control87(long long) +@ cdecl _controlfp(long long) +@ cdecl _controlfp_s(ptr long long) +@ cdecl _copysign(double double) copysign +@ cdecl -arch=!i386 _copysignf(float float) copysignf +@ varargs _cprintf(str) +@ varargs _cprintf_l(str ptr) +# stub _cprintf_p(str) +# stub _cprintf_p_l(str ptr) +# stub _cprintf_s(str) +# stub _cprintf_s_l(str ptr) +@ cdecl _cputs(str) +@ cdecl _cputws(wstr) +@ cdecl _creat(str long) +@ cdecl _create_locale(long str) +# stub _crtAssertBusy +# stub _crtBreakAlloc +# stub _crtDbgFlag +@ varargs _cscanf(str) +@ varargs _cscanf_l(str ptr) +@ varargs _cscanf_s(str) +@ varargs _cscanf_s_l(str ptr) +@ cdecl _ctime32(ptr) +@ cdecl _ctime32_s(str long ptr) +@ cdecl _ctime64(ptr) +@ cdecl _ctime64_s(str long ptr) +@ extern _ctype MSVCRT__ctype +@ cdecl _cwait(ptr long long) +@ varargs _cwprintf(wstr) +@ varargs _cwprintf_l(wstr ptr) +# stub _cwprintf_p(wstr) +# stub _cwprintf_p_l(wstr ptr) +# stub _cwprintf_s(wstr) +# stub _cwprintf_s_l(wstr ptr) +@ varargs _cwscanf(wstr) +@ varargs _cwscanf_l(wstr ptr) +@ varargs _cwscanf_s(wstr) +@ varargs _cwscanf_s_l(wstr ptr) +@ extern _daylight MSVCRT___daylight +@ cdecl _difftime32(long long) +@ cdecl _difftime64(int64 int64) +@ extern _dstbias MSVCRT__dstbias +@ cdecl _dup (long) _dup +@ cdecl _dup2 (long long) _dup2 +@ cdecl _ecvt(double long ptr ptr) +@ cdecl _ecvt_s(str long double long ptr ptr) +@ cdecl _endthread () +@ cdecl _endthreadex(long) +@ extern _environ MSVCRT__environ +@ cdecl _eof(long) +@ cdecl _errno() +@ cdecl -arch=i386 _except_handler2(ptr ptr ptr ptr) +@ cdecl -arch=i386 _except_handler3(ptr ptr ptr ptr) +@ cdecl -arch=i386 _except_handler4_common(ptr ptr ptr ptr ptr ptr) +@ varargs _execl(str str) +@ varargs _execle(str str) +@ varargs _execlp(str str) +@ varargs _execlpe(str str) +@ cdecl _execv(str ptr) +@ cdecl _execve(str ptr ptr) +@ cdecl _execvp(str ptr) +@ cdecl _execvpe(str ptr ptr) +@ cdecl _exit(long) +@ cdecl _expand(ptr long) +# stub _expand_dbg(ptr long long str long) +@ cdecl _fcloseall() +@ cdecl _fcvt(double long ptr ptr) +@ cdecl _fcvt_s(ptr long double long ptr ptr) +@ cdecl _fdopen(long str) +@ cdecl _fgetchar() +@ cdecl _fgetwchar() +@ cdecl _filbuf(ptr) +# extern _fileinfo +@ cdecl _filelength(long) +@ cdecl -ret64 _filelengthi64(long) +@ cdecl _fileno(ptr) +@ cdecl _findclose(long) +@ cdecl _findfirst(str ptr) +@ cdecl _findfirst32(str ptr) +@ cdecl _findfirst64(str ptr) +@ cdecl _findfirst64i32(str ptr) +@ cdecl _findfirsti64(str ptr) +@ cdecl _findnext(long ptr) +@ cdecl _findnext32(long ptr) +@ cdecl _findnext64(long ptr) +@ cdecl _findnext64i32(long ptr) +@ cdecl _findnexti64(long ptr) +@ cdecl _finite(double) +@ cdecl -arch=!i386 _finitef(float) +@ cdecl _flsbuf(long ptr) +@ cdecl _flushall() +@ extern _fmode MSVCRT__fmode +@ cdecl _fpclass(double) +@ cdecl -arch=!i386 _fpclassf(float) +@ cdecl _fpieee_flt(long ptr ptr) +@ cdecl _fpreset() +@ varargs _fprintf_l(ptr str ptr) +@ varargs _fprintf_p(ptr str) +@ varargs _fprintf_p_l(ptr str ptr) +@ varargs _fprintf_s_l(ptr str ptr) +@ cdecl _fputchar(long) +@ cdecl _fputwchar(long) +# stub _free_dbg(ptr long) +@ cdecl _free_locale(ptr) +# stub _freea(ptr) +# stub _freea_s +@ varargs _fscanf_l(ptr str ptr) +@ varargs _fscanf_s_l(ptr str ptr) +@ cdecl _fseeki64(ptr int64 long) +@ cdecl _fsopen(str str long) +@ cdecl _fstat(long ptr) +@ cdecl _fstat64(long ptr) +@ cdecl _fstati64(long ptr) +@ cdecl -ret64 _ftelli64(ptr) +@ cdecl -arch=win32 _ftime(ptr) _ftime32 +@ cdecl -arch=win64 _ftime(ptr) _ftime64 +@ cdecl _ftime32(ptr) +@ cdecl _ftime32_s(ptr) +@ cdecl _ftime64(ptr) +@ cdecl _ftime64_s(ptr) +@ cdecl -arch=i386 -ret64 _ftol() +@ cdecl -arch=i386 -ret64 _ftol2() _ftol +@ cdecl -arch=i386 -ret64 _ftol2_sse() _ftol #FIXME: SSE variant should be implemented +# stub _ftol2_sse_excpt +@ cdecl _fullpath(ptr str long) +# stub _fullpath_dbg(ptr str long long str long) +@ cdecl -arch=win32 _futime(long ptr) _futime32 +@ cdecl -arch=win64 _futime(long ptr) _futime64 +@ cdecl _futime32(long ptr) +@ cdecl _futime64(long ptr) +@ varargs _fwprintf_l(ptr wstr ptr) +@ varargs _fwprintf_p(ptr wstr) +@ varargs _fwprintf_p_l(ptr wstr ptr) +@ varargs _fwprintf_s_l(ptr wstr ptr) +@ varargs _fwscanf_l(ptr wstr ptr) +@ varargs _fwscanf_s_l(ptr wstr ptr) +@ cdecl _gcvt(double long str) +@ cdecl _gcvt_s(ptr long double long) +@ cdecl _get_current_locale() +@ cdecl _get_doserrno(ptr) +@ cdecl _get_environ(ptr) +@ cdecl _get_errno(ptr) +# stub _get_fileinfo(ptr) +@ cdecl _get_fmode(ptr) +@ cdecl _get_heap_handle() +@ cdecl _get_osfhandle(long) +@ cdecl _get_osplatform(ptr) +@ cdecl _get_osver(ptr) +@ cdecl _get_output_format() +@ cdecl _get_pgmptr(ptr) +@ cdecl _get_sbh_threshold() +@ cdecl _get_wenviron(ptr) +@ cdecl _get_winmajor(ptr) +@ cdecl _get_winminor(ptr) +# stub _get_winver(ptr) +@ cdecl _get_wpgmptr(ptr) +@ cdecl _get_terminate() +@ cdecl _get_tzname(ptr str long long) +@ cdecl _get_unexpected() +@ cdecl _getch() +@ cdecl _getche() +@ cdecl _getcwd(str long) +@ cdecl _getdcwd(long str long) +@ cdecl _getdiskfree(long ptr) +@ cdecl _getdllprocaddr(long str long) +@ cdecl _getdrive() +@ cdecl _getdrives() kernel32.GetLogicalDrives +@ cdecl _getmaxstdio() +@ cdecl _getmbcp() +@ cdecl _getpid() +@ stub _getsystime(ptr) +@ cdecl _getw(ptr) +@ cdecl _getwch() +@ cdecl _getwche() +@ cdecl _getws(ptr) +@ cdecl -arch=i386 _global_unwind2(ptr) +@ cdecl _gmtime32(ptr) +@ cdecl _gmtime32_s(ptr ptr) +@ cdecl _gmtime64(ptr) +@ cdecl _gmtime64_s(ptr ptr) +@ cdecl _heapadd (ptr long) +@ cdecl _heapchk() +@ cdecl _heapmin() +@ cdecl _heapset(long) +@ stub _heapused(ptr ptr) +@ cdecl _heapwalk(ptr) +@ cdecl _hypot(double double) +@ cdecl _hypotf(float float) +@ cdecl _i64toa(int64 ptr long) +@ cdecl _i64toa_s(int64 ptr long long) +@ cdecl _i64tow(int64 ptr long) +@ cdecl _i64tow_s(int64 ptr long long) +@ cdecl _initterm(ptr ptr) +@ cdecl _initterm_e(ptr ptr) +@ stub -arch=i386 _inp(long) +@ stub -arch=i386 _inpd(long) +@ stub -arch=i386 _inpw(long) +@ cdecl _invalid_parameter(wstr wstr wstr long long) +@ extern _iob MSVCRT__iob +@ cdecl _isalnum_l(long ptr) +@ cdecl _isalpha_l(long ptr) +@ cdecl _isatty(long) +@ cdecl _iscntrl_l(long ptr) +@ cdecl _isctype(long long) +@ cdecl _isctype_l(long long ptr) +@ cdecl _isdigit_l(long ptr) +@ cdecl _isgraph_l(long ptr) +@ cdecl _isleadbyte_l(long ptr) +@ cdecl _islower_l(long ptr) +@ stub _ismbbalnum(long) +# stub _ismbbalnum_l(long ptr) +@ stub _ismbbalpha(long) +# stub _ismbbalpha_l(long ptr) +@ stub _ismbbgraph(long) +# stub _ismbbgraph_l(long ptr) +@ stub _ismbbkalnum(long) +# stub _ismbbkalnum_l(long ptr) +@ cdecl _ismbbkana(long) +@ cdecl _ismbbkana_l(long ptr) +@ stub _ismbbkprint(long) +# stub _ismbbkprint_l(long ptr) +@ stub _ismbbkpunct(long) +# stub _ismbbkpunct_l(long ptr) +@ cdecl _ismbblead(long) +@ cdecl _ismbblead_l(long ptr) +@ stub _ismbbprint(long) +# stub _ismbbprint_l(long ptr) +@ stub _ismbbpunct(long) +# stub _ismbbpunct_l(long ptr) +@ cdecl _ismbbtrail(long) +@ cdecl _ismbbtrail_l(long ptr) +@ cdecl _ismbcalnum(long) +@ cdecl _ismbcalnum_l(long ptr) +@ cdecl _ismbcalpha(long) +@ cdecl _ismbcalpha_l(long ptr) +@ cdecl _ismbcdigit(long) +@ cdecl _ismbcdigit_l(long ptr) +@ cdecl _ismbcgraph(long) +@ cdecl _ismbcgraph_l(long ptr) +@ cdecl _ismbchira(long) +@ cdecl _ismbchira_l(long ptr) +@ cdecl _ismbckata(long) +@ cdecl _ismbckata_l(long ptr) +@ cdecl _ismbcl0(long) +@ cdecl _ismbcl0_l(long ptr) +@ cdecl _ismbcl1(long) +@ cdecl _ismbcl1_l(long ptr) +@ cdecl _ismbcl2(long) +@ cdecl _ismbcl2_l(long ptr) +@ cdecl _ismbclegal(long) +@ cdecl _ismbclegal_l(long ptr) +@ cdecl _ismbclower(long) +@ cdecl _ismbclower_l(long ptr) +@ cdecl _ismbcprint(long) +@ cdecl _ismbcprint_l(long ptr) +@ cdecl _ismbcpunct(long) +@ cdecl _ismbcpunct_l(long ptr) +@ cdecl _ismbcspace(long) +@ cdecl _ismbcspace_l(long ptr) +@ cdecl _ismbcsymbol(long) +@ cdecl _ismbcsymbol_l(long ptr) +@ cdecl _ismbcupper(long) +@ cdecl _ismbcupper_l(long ptr) +@ cdecl _ismbslead(ptr ptr) +@ cdecl _ismbslead_l(ptr ptr ptr) +@ cdecl _ismbstrail(ptr ptr) +@ cdecl _ismbstrail_l(ptr ptr ptr) +@ cdecl _isnan(double) +@ cdecl -arch=x86_64 _isnanf(float) +@ cdecl _isprint_l(long ptr) +@ cdecl _isspace_l(long ptr) +@ cdecl _isupper_l(long ptr) +@ cdecl _iswalnum_l(long ptr) +@ cdecl _iswalpha_l(long ptr) +@ cdecl _iswcntrl_l(long ptr) +@ cdecl _iswctype_l(long long ptr) +@ cdecl _iswdigit_l(long ptr) +@ cdecl _iswgraph_l(long ptr) +@ cdecl _iswlower_l(long ptr) +@ cdecl _iswprint_l(long ptr) +@ cdecl _iswpunct_l(long ptr) +@ cdecl _iswspace_l(long ptr) +@ cdecl _iswupper_l(long ptr) +@ cdecl _iswxdigit_l(long ptr) +@ cdecl _isxdigit_l(long ptr) +@ cdecl _itoa(long ptr long) +@ cdecl _itoa_s(long ptr long long) +@ cdecl _itow(long ptr long) +@ cdecl _itow_s(long ptr long long) +@ cdecl _j0(double) +@ cdecl _j1(double) +@ cdecl _jn(long double) +@ cdecl _kbhit() +@ cdecl _lfind(ptr ptr ptr long ptr) +@ cdecl _lfind_s(ptr ptr ptr long ptr ptr) +@ cdecl _loaddll(str) +@ cdecl -arch=win64 _local_unwind(ptr ptr) +@ cdecl -arch=i386 _local_unwind2(ptr long) +@ cdecl -arch=i386 _local_unwind4(ptr ptr long) +@ cdecl _localtime32(ptr) +@ cdecl _localtime32_s(ptr ptr) +@ cdecl _localtime64(ptr) +@ cdecl _localtime64_s(ptr ptr) +@ cdecl _lock(long) +@ cdecl _lock_file(ptr) +@ cdecl _locking(long long long) +@ cdecl _logb(double) logb +@ cdecl -arch=!i386 _logbf(float) logbf +@ cdecl -arch=i386 _longjmpex(ptr long) longjmp +@ cdecl _lrotl(long long) MSVCRT__lrotl +@ cdecl _lrotr(long long) MSVCRT__lrotr +@ cdecl _lsearch(ptr ptr ptr long ptr) +# stub _lsearch_s(ptr ptr ptr long ptr ptr) +@ cdecl _lseek(long long long) +@ cdecl -ret64 _lseeki64(long int64 long) +@ cdecl _ltoa(long ptr long) +@ cdecl _ltoa_s(long ptr long long) +@ cdecl _ltow(long ptr long) +@ cdecl _ltow_s(long ptr long long) +@ cdecl _makepath(ptr str str str str) +@ cdecl _makepath_s(ptr long str str str str) +# stub _malloc_dbg(long long str long) +@ cdecl _mbbtombc(long) +@ cdecl _mbbtombc_l(long ptr) +@ cdecl _mbbtype(long long) +# extern _mbcasemap +@ cdecl _mbccpy(ptr ptr) +@ cdecl _mbccpy_l(ptr ptr ptr) +@ cdecl _mbccpy_s(ptr long ptr ptr) +@ cdecl _mbccpy_s_l(ptr long ptr ptr ptr) +@ cdecl _mbcjistojms (long) +@ cdecl _mbcjistojms_l(long ptr) +@ cdecl _mbcjmstojis(long) +@ cdecl _mbcjmstojis_l(long ptr) +@ cdecl _mbclen(ptr) +@ cdecl _mbclen_l(ptr ptr) +@ cdecl _mbctohira(long) +@ cdecl _mbctohira_l(long ptr) +@ cdecl _mbctokata(long) +@ cdecl _mbctokata_l(long ptr) +@ cdecl _mbctolower(long) +@ cdecl _mbctolower_l(long ptr) +@ cdecl _mbctombb(long) +@ cdecl _mbctombb_l(long ptr) +@ cdecl _mbctoupper(long) +@ cdecl _mbctoupper_l(long ptr) +@ extern _mbctype MSVCRT_mbctype +@ cdecl _mblen_l(str long ptr) +@ cdecl _mbsbtype(str long) +@ cdecl _mbsbtype_l(str long ptr) +@ cdecl _mbscat(str str) +@ cdecl _mbscat_s(ptr long str) +@ cdecl _mbscat_s_l(ptr long str ptr) +@ cdecl _mbschr(str long) +@ cdecl _mbschr_l(str long ptr) +@ cdecl _mbscmp(str str) +@ cdecl _mbscmp_l(str str ptr) +@ cdecl _mbscoll(str str) +@ cdecl _mbscoll_l(str str ptr) +@ cdecl _mbscpy(ptr str) +@ cdecl _mbscpy_s(ptr long str) +@ cdecl _mbscpy_s_l(ptr long str ptr) +@ cdecl _mbscspn(str str) +@ cdecl _mbscspn_l(str str ptr) +@ cdecl _mbsdec(ptr ptr) +@ cdecl _mbsdec_l(ptr ptr ptr) +@ cdecl _mbsdup(str) _strdup +# stub _strdup_dbg(str long str long) +@ cdecl _mbsicmp(str str) +@ cdecl _mbsicmp_l(str str ptr) +@ cdecl _mbsicoll(str str) +@ cdecl _mbsicoll_l(str str ptr) +@ cdecl _mbsinc(str) +@ cdecl _mbsinc_l(str ptr) +@ cdecl _mbslen(str) +@ cdecl _mbslen_l(str ptr) +@ cdecl _mbslwr(str) +@ cdecl _mbslwr_l(str ptr) +@ cdecl _mbslwr_s(str long) +@ cdecl _mbslwr_s_l(str long ptr) +@ cdecl _mbsnbcat(str str long) +@ cdecl _mbsnbcat_l(str str long ptr) +@ cdecl _mbsnbcat_s(str long ptr long) +@ cdecl _mbsnbcat_s_l(str long ptr long ptr) +@ cdecl _mbsnbcmp(str str long) +@ cdecl _mbsnbcmp_l(str str long ptr) +@ cdecl _mbsnbcnt(ptr long) +@ cdecl _mbsnbcnt_l(ptr long ptr) +@ cdecl _mbsnbcoll(str str long) +@ cdecl _mbsnbcoll_l(str str long ptr) +@ cdecl _mbsnbcpy(ptr str long) +@ cdecl _mbsnbcpy_l(ptr str long ptr) +@ cdecl _mbsnbcpy_s(ptr long str long) +@ cdecl _mbsnbcpy_s_l(ptr long str long ptr) +@ cdecl _mbsnbicmp(str str long) +@ cdecl _mbsnbicmp_l(str str long ptr) +@ cdecl _mbsnbicoll(str str long) +@ cdecl _mbsnbicoll_l(str str long ptr) +@ cdecl _mbsnbset(ptr long long) +@ cdecl _mbsnbset_l(str long long ptr) +# stub _mbsnbset_s(ptr long long long) +# stub _mbsnbset_s_l(ptr long long long ptr) +@ cdecl _mbsncat(str str long) +@ cdecl _mbsncat_l(str str long ptr) +# stub _mbsncat_s(str long str long) +# stub _mbsncat_s_l(str long str long ptr) +@ cdecl _mbsnccnt(str long) +@ cdecl _mbsnccnt_l(str long ptr) +@ cdecl _mbsncmp(str str long) +@ cdecl _mbsncmp_l(str str long ptr) +@ stub _mbsncoll(str str long) +# stub _mbsncoll_l(str str long ptr) +@ cdecl _mbsncpy(ptr str long) +@ cdecl _mbsncpy_l(ptr str long ptr) +# stub _mbsncpy_s(ptr long str long) +# stub _mbsncpy_s_l(ptr long str long ptr) +@ cdecl _mbsnextc(str) +@ cdecl _mbsnextc_l(str ptr) +@ cdecl _mbsnicmp(str str long) +@ cdecl _mbsnicmp_l(str str long ptr) +@ stub _mbsnicoll(str str long) +# stub _mbsnicoll_l(str str long ptr) +@ cdecl _mbsninc(str long) +# stub _mbsninc_l(str long ptr) +@ cdecl _mbsnlen(str long) +@ cdecl _mbsnlen_l(str long ptr) +@ cdecl _mbsnset(ptr long long) +@ cdecl _mbsnset_l(ptr long long ptr) +# stub _mbsnset_s(ptr long long long) +# stub _mbsnset_s_l(ptr long long long ptr) +@ cdecl _mbspbrk(str str) +@ cdecl _mbspbrk_l(str str ptr) +@ cdecl _mbsrchr(str long) +@ cdecl _mbsrchr_l(str long ptr) +@ cdecl _mbsrev(str) +@ cdecl _mbsrev_l(str ptr) +@ cdecl _mbsset(ptr long) +@ cdecl _mbsset_l(ptr long ptr) +# stub _mbsset_s(ptr long long) +# stub _mbsset_s_l(ptr long long ptr) +@ cdecl _mbsspn(str str) +@ cdecl _mbsspn_l(str str ptr) +@ cdecl _mbsspnp(str str) +@ cdecl _mbsspnp_l(str str ptr) +@ cdecl _mbsstr(str str) +# stub _mbsstr_l(str str ptr) +@ cdecl _mbstok(str str) +@ cdecl _mbstok_l(str str ptr) +@ cdecl _mbstok_s(str str ptr) +@ cdecl _mbstok_s_l(str str ptr ptr) +@ cdecl _mbstowcs_l(ptr str long ptr) +@ cdecl _mbstowcs_s_l(ptr ptr long str long ptr) +@ cdecl _mbstrlen(str) +@ cdecl _mbstrlen_l(str ptr) +# stub _mbstrnlen(str long) +# stub _mbstrnlen_l(str long ptr) +@ cdecl _mbsupr(str) +@ cdecl _mbsupr_l(str ptr) +@ cdecl _mbsupr_s(str long) +@ cdecl _mbsupr_s_l(str long ptr) +@ cdecl _mbtowc_l(ptr str long ptr) +@ cdecl _memccpy(ptr ptr long long) +@ cdecl _memicmp(str str long) +@ cdecl _memicmp_l(str str long ptr) +@ cdecl _mkdir(str) +@ cdecl -arch=win32 _mkgmtime(ptr) _mkgmtime32 +@ cdecl -arch=win64 _mkgmtime(ptr) _mkgmtime64 +@ cdecl _mkgmtime32(ptr) +@ cdecl _mkgmtime64(ptr) +@ cdecl _mktemp(str) +@ cdecl _mktemp_s(str long) +@ cdecl _mktime32(ptr) +@ cdecl _mktime64(ptr) +@ cdecl _msize(ptr) +# stub -arch=win32 _msize_debug(ptr long) +# stub -arch=win64 _msize_dbg(ptr long) +@ cdecl _nextafter(double double) nextafter +@ cdecl -arch=x86_64 _nextafterf(float float) nextafterf +@ cdecl _onexit(ptr) +@ varargs _open(str long) +@ cdecl _open_osfhandle(long long) +@ extern _osplatform MSVCRT__osplatform +@ extern _osver MSVCRT__osver +@ stub -arch=i386 _outp(long long) +@ stub -arch=i386 _outpd(long long) +@ stub -arch=i386 _outpw(long long) +@ cdecl _pclose (ptr) _pclose +@ extern _pctype MSVCRT__pctype +@ extern _pgmptr MSVCRT__pgmptr +@ cdecl _pipe (ptr long long) _pipe +@ cdecl _popen (str str) _popen +# stub _printf_l(str ptr) +# stub _printf_p(str) +# stub _printf_p_l(str ptr) +# stub _printf_s_l(str ptr) +@ cdecl _purecall() +@ cdecl _putch(long) +@ cdecl _putenv(str) +@ cdecl _putenv_s(str str) +@ cdecl _putw(long ptr) +@ cdecl _putwch(long) +@ cdecl _putws(wstr) +@ extern _pwctype MSVCRT__pwctype +@ cdecl _read(long ptr long) +# stub _realloc_dbg(ptr long long str long) +@ cdecl _resetstkoflw() +@ cdecl _rmdir(str) +@ cdecl _rmtmp() +@ cdecl _rotl(long long) MSVCRT__rotl +@ cdecl -ret64 _rotl64(int64 long) MSVCRT__rotl64 +@ cdecl _rotr(long long) MSVCRT__rotr +@ cdecl -ret64 _rotr64(int64 long) MSVCRT__rotr64 +@ cdecl -arch=i386 _safe_fdiv() +@ cdecl -arch=i386 _safe_fdivr() +@ cdecl -arch=i386 _safe_fprem() +@ cdecl -arch=i386 _safe_fprem1() +@ cdecl _scalb(double long) +@ cdecl -arch=x86_64 _scalbf(float long) +@ varargs _scanf_l(str ptr) +@ varargs _scanf_s_l(str ptr) +@ varargs _scprintf(str) +@ varargs _scprintf_l(str ptr) +@ varargs _scprintf_p_l(str ptr) +@ varargs _scwprintf(wstr) +@ varargs _scwprintf_l(wstr ptr) +@ varargs _scwprintf_p_l(wstr ptr) +@ cdecl _searchenv(str str ptr) +@ cdecl _searchenv_s(str str ptr long) +@ stdcall -arch=i386 _seh_longjmp_unwind4(ptr) +@ stdcall -arch=i386 _seh_longjmp_unwind(ptr) +@ cdecl _set_SSE2_enable(long) +@ cdecl _set_controlfp(long long) +@ cdecl _set_doserrno(long) +@ cdecl _set_errno(long) +@ cdecl _set_error_mode(long) +# stub _set_fileinfo(long) +@ cdecl _set_fmode(long) +@ cdecl _set_output_format(long) +@ cdecl _set_sbh_threshold(long) +@ cdecl _seterrormode(long) +@ cdecl -norelay _setjmp(ptr) +@ cdecl -arch=i386 -norelay _setjmp3(ptr long) +@ cdecl -arch=!i386 -norelay _setjmpex(ptr ptr) +@ cdecl _setmaxstdio(long) +@ cdecl _setmbcp(long) +@ cdecl _setmode(long long) +@ stub _setsystime(ptr long) +@ cdecl _sleep(long) +@ varargs _snprintf(ptr long str) +@ varargs _snprintf_c(ptr long str) +@ varargs _snprintf_c_l(ptr long str ptr) +@ varargs _snprintf_l(ptr long str ptr) +@ varargs _snprintf_s(ptr long long str) +@ varargs _snprintf_s_l(ptr long long str ptr) +@ varargs _snscanf(str long str) +@ varargs _snscanf_l(str long str ptr) +@ varargs _snscanf_s(str long str) +@ varargs _snscanf_s_l(str long str ptr) +@ varargs _snwprintf(ptr long wstr) +@ varargs _snwprintf_l(ptr long wstr ptr) +@ varargs _snwprintf_s(ptr long long wstr) +@ varargs _snwprintf_s_l(ptr long long wstr ptr) +@ varargs _snwscanf(wstr long wstr) +@ varargs _snwscanf_l(wstr long wstr ptr) +@ varargs _snwscanf_s(wstr long wstr) +@ varargs _snwscanf_s_l(wstr long wstr ptr) +@ varargs _sopen(str long long) +@ cdecl _sopen_s(ptr str long long long) +@ varargs _spawnl(long str str) +@ varargs _spawnle(long str str) +@ varargs _spawnlp(long str str) +@ varargs _spawnlpe(long str str) +@ cdecl _spawnv(long str ptr) +@ cdecl _spawnve(long str ptr ptr) +@ cdecl _spawnvp(long str ptr) +@ cdecl _spawnvpe(long str ptr ptr) +@ cdecl _splitpath(str ptr ptr ptr ptr) +@ cdecl _splitpath_s(str ptr long ptr long ptr long ptr long) +@ varargs _sprintf_l(ptr str ptr) +@ varargs _sprintf_p_l(ptr long str ptr) +@ varargs _sprintf_s_l(ptr long str ptr) +@ varargs _sscanf_l(str str ptr) +@ varargs _sscanf_s_l(str str ptr) +@ cdecl _stat(str ptr) +@ cdecl _stat64(str ptr) +@ cdecl _stati64(str ptr) +@ cdecl _statusfp() +@ cdecl _strcmpi(str str) _stricmp +@ cdecl _strcoll_l(str str ptr) +@ cdecl _strdate(ptr) +@ cdecl _strdate_s(ptr long) +@ cdecl _strdup(str) +# stub _strdup_dbg(str long str long) +@ cdecl _strerror(long) +# stub _strerror_s(ptr long long) +@ cdecl _stricmp(str str) +@ cdecl _stricmp_l(str str ptr) +@ cdecl _stricoll(str str) +@ cdecl _stricoll_l(str str ptr) +@ cdecl _strlwr(str) +@ cdecl _strlwr_l(str ptr) +@ cdecl _strlwr_s(ptr long) +@ cdecl _strlwr_s_l(ptr long ptr) +@ cdecl _strncoll(str str long) +@ cdecl _strncoll_l(str str long ptr) +@ cdecl _strnicmp(str str long) +@ cdecl _strnicmp_l(str str long ptr) +@ cdecl _strnicoll(str str long) +@ cdecl _strnicoll_l(str str long ptr) +@ cdecl _strnset(str long long) +@ cdecl _strnset_s(str long long long) +@ cdecl _strrev(str) +@ cdecl _strset(str long) +# stub _strset_s(str long long) +@ cdecl _strtime(ptr) +@ cdecl _strtime_s(ptr long) +@ cdecl _strtod_l(str ptr ptr) +@ cdecl -ret64 _strtoi64(str ptr long) +@ cdecl -ret64 _strtoi64_l(str ptr long ptr) +@ cdecl _strtol_l(str ptr long ptr) +@ cdecl -ret64 _strtoui64(str ptr long) +@ cdecl -ret64 _strtoui64_l(str ptr long ptr) +@ cdecl _strtoul_l(str ptr long ptr) +@ cdecl _strupr(str) +@ cdecl _strupr_l(str ptr) +@ cdecl _strupr_s(str long) +@ cdecl _strupr_s_l(str long ptr) +@ cdecl _strxfrm_l(ptr str long ptr) +@ cdecl _swab(str str long) +@ varargs _swprintf(ptr wstr) +@ varargs _swprintf_c(ptr long str) +@ varargs _swprintf_c_l(ptr long str ptr) +@ varargs _swprintf_p_l(ptr long wstr ptr) +@ varargs _swprintf_s_l(ptr long wstr ptr) +@ varargs _swscanf_l(wstr wstr ptr) +@ varargs _swscanf_s_l(wstr wstr ptr) +@ extern _sys_errlist MSVCRT__sys_errlist +@ extern _sys_nerr MSVCRT__sys_nerr +@ cdecl _tell(long) +@ cdecl -ret64 _telli64(long) +@ cdecl _tempnam(str str) +# stub _tempnam_dbg(str str long str long) +@ cdecl _time32(ptr) +@ cdecl _time64(ptr) +@ extern _timezone MSVCRT___timezone +@ cdecl _tolower(long) +@ cdecl _tolower_l(long ptr) +@ cdecl _toupper(long) +@ cdecl _toupper_l(long ptr) +@ cdecl _towlower_l(long ptr) +@ cdecl _towupper_l(long ptr) +@ extern _tzname MSVCRT__tzname +@ cdecl _tzset() +@ cdecl _ui64toa(int64 ptr long) +@ cdecl _ui64toa_s(int64 ptr long long) +@ cdecl _ui64tow(int64 ptr long) +@ cdecl _ui64tow_s(int64 ptr long long) +@ cdecl _ultoa(long ptr long) +@ cdecl _ultoa_s(long ptr long long) +@ cdecl _ultow(long ptr long) +@ cdecl _ultow_s(long ptr long long) +@ cdecl _umask(long) +# stub _umask_s(long ptr) +@ cdecl _ungetch(long) +@ cdecl _ungetwch(long) +@ cdecl _unlink(str) +@ cdecl _unloaddll(long) +@ cdecl _unlock(long) +@ cdecl _unlock_file(ptr) +@ cdecl _utime32(str ptr) +@ cdecl _utime64(str ptr) +@ cdecl _vcprintf(str ptr) +@ cdecl _vcprintf_l(str ptr ptr) +# stub _vcprintf_p(str ptr) +# stub _vcprintf_p_l(str ptr ptr) +# stub _vcprintf_s(str ptr) +# stub _vcprintf_s_l(str ptr ptr) +@ cdecl _vcwprintf(wstr ptr) +@ cdecl _vcwprintf_l(wstr ptr ptr) +# stub _vcwprintf_p(wstr ptr) +# stub _vcwprintf_p_l(wstr ptr ptr) +# stub _vcwprintf_s(wstr ptr) +# stub _vcwprintf_s_l(wstr ptr ptr) +@ cdecl _vfprintf_l(ptr str ptr ptr) +@ cdecl _vfprintf_p(ptr str ptr) +@ cdecl _vfprintf_p_l(ptr str ptr ptr) +@ cdecl _vfprintf_s_l(ptr str ptr ptr) +@ cdecl _vfwprintf_l(ptr wstr ptr ptr) +@ cdecl _vfwprintf_p(ptr wstr ptr) +@ cdecl _vfwprintf_p_l(ptr wstr ptr ptr) +@ cdecl _vfwprintf_s_l(ptr wstr ptr ptr) +# stub _vprintf_l(str ptr ptr) +# stub _vprintf_p(str ptr) +# stub _vprintf_p_l(str ptr ptr) +# stub _vprintf_s_l(str ptr ptr) +@ cdecl -arch=win32 _utime(str ptr) _utime32 +@ cdecl -arch=win64 _utime(str ptr) _utime64 +@ cdecl _vscprintf(str ptr) +@ cdecl _vscprintf_l(str ptr ptr) +@ cdecl _vscprintf_p_l(str ptr ptr) +@ cdecl _vscwprintf(wstr ptr) +@ cdecl _vscwprintf_l(wstr ptr ptr) +@ cdecl _vscwprintf_p_l(wstr ptr ptr) +@ cdecl -norelay _vsnprintf(ptr long str ptr) +@ cdecl _vsnprintf_c(ptr long str ptr) +@ cdecl _vsnprintf_c_l(ptr long str ptr ptr) +@ cdecl _vsnprintf_l(ptr long str ptr ptr) +@ cdecl _vsnprintf_s(ptr long long str ptr) +@ cdecl _vsnprintf_s_l(ptr long long str ptr ptr) +@ cdecl _vsnwprintf(ptr long wstr ptr) +@ cdecl _vsnwprintf_l(ptr long wstr ptr ptr) +@ cdecl _vsnwprintf_s(ptr long long wstr ptr) +@ cdecl _vsnwprintf_s_l(ptr long long wstr ptr ptr) +@ cdecl _vsprintf_l(ptr str ptr ptr) +@ cdecl _vsprintf_p(ptr long str ptr) +@ cdecl _vsprintf_p_l(ptr long str ptr ptr) +@ cdecl _vsprintf_s_l(ptr long str ptr ptr) +@ cdecl _vswprintf(ptr wstr ptr) +@ cdecl _vswprintf_c(ptr long wstr ptr) +@ cdecl _vswprintf_c_l(ptr long wstr ptr ptr) +@ cdecl _vswprintf_l(ptr wstr ptr ptr) +@ cdecl _vswprintf_p_l(ptr long wstr ptr ptr) +@ cdecl _vswprintf_s_l(ptr long wstr ptr ptr) +# stub _vwprintf_l(wstr ptr ptr) +# stub _vwprintf_p(wstr ptr) +# stub _vwprintf_p_l(wstr ptr ptr) +# stub _vwprintf_s_l(wstr ptr ptr) +@ cdecl _waccess(wstr long) +@ cdecl _waccess_s(wstr long) +@ cdecl _wasctime(ptr) +@ cdecl _wasctime_s(ptr long ptr) +@ cdecl _wassert(wstr wstr long) +@ cdecl _wchdir(wstr) +@ cdecl _wchmod(wstr long) +@ extern _wcmdln MSVCRT__wcmdln +@ cdecl _wcreat(wstr long) +@ cdecl _wcscoll_l(wstr wstr ptr) +@ cdecl _wcsdup(wstr) +# stub _wcsdup_dbg(wstr long str long) +@ cdecl _wcserror(long) +@ cdecl _wcserror_s(ptr long long) +@ cdecl _wcsftime_l(ptr long wstr ptr ptr) +@ cdecl _wcsicmp(wstr wstr) +@ cdecl _wcsicmp_l(wstr wstr ptr) +@ cdecl _wcsicoll(wstr wstr) +@ cdecl _wcsicoll_l(wstr wstr ptr) +@ cdecl _wcslwr(wstr) +@ cdecl _wcslwr_l(wstr ptr) +@ cdecl _wcslwr_s(wstr long) +@ cdecl _wcslwr_s_l(wstr long ptr) +@ cdecl _wcsncoll(wstr wstr long) +@ cdecl _wcsncoll_l(wstr wstr long ptr) +@ cdecl _wcsnicmp(wstr wstr long) +@ cdecl _wcsnicmp_l(wstr wstr long ptr) +@ cdecl _wcsnicoll(wstr wstr long) +@ cdecl _wcsnicoll_l(wstr wstr long ptr) +@ cdecl _wcsnset(wstr long long) +@ cdecl _wcsnset_s(wstr long long long) +@ cdecl _wcsrev(wstr) +@ cdecl _wcsset(wstr long) +@ cdecl _wcsset_s(wstr long long) +@ cdecl _wcstod_l(wstr ptr ptr) +@ cdecl -ret64 _wcstoi64(wstr ptr long) +@ cdecl -ret64 _wcstoi64_l(wstr ptr long ptr) +@ cdecl _wcstol_l(wstr ptr long ptr) +@ cdecl _wcstombs_l(ptr ptr long ptr) +@ cdecl _wcstombs_s_l(ptr ptr long wstr long ptr) +@ cdecl -ret64 _wcstoui64(wstr ptr long) +@ cdecl -ret64 _wcstoui64_l(wstr ptr long ptr) +@ cdecl _wcstoul_l(wstr ptr long ptr) +@ cdecl _wcsupr(wstr) +@ cdecl _wcsupr_l(wstr ptr) +@ cdecl _wcsupr_s(wstr long) +@ cdecl _wcsupr_s_l(wstr long ptr) +@ cdecl _wcsxfrm_l(ptr wstr long ptr) +@ cdecl -arch=win32 _wctime(ptr) _wctime32 +@ cdecl -arch=win64 _wctime(ptr) _wctime64 +@ cdecl _wctime32(ptr) +@ cdecl _wctime32_s(ptr long ptr) +@ cdecl _wctime64(ptr) +@ cdecl _wctime64_s(ptr long ptr) +@ cdecl _wctomb_l(ptr long ptr) +@ cdecl _wctomb_s_l(ptr ptr long long ptr) +@ extern _wctype MSVCRT__wctype +@ extern _wenviron MSVCRT__wenviron +@ varargs _wexecl(wstr wstr) +@ varargs _wexecle(wstr wstr) +@ varargs _wexeclp(wstr wstr) +@ varargs _wexeclpe(wstr wstr) +@ cdecl _wexecv(wstr ptr) +@ cdecl _wexecve(wstr ptr ptr) +@ cdecl _wexecvp(wstr ptr) +@ cdecl _wexecvpe(wstr ptr ptr) +@ cdecl _wfdopen(long wstr) +@ cdecl _wfindfirst(wstr ptr) +@ cdecl _wfindfirst32(wstr ptr) +@ cdecl _wfindfirst64(wstr ptr) +@ cdecl _wfindfirsti64(wstr ptr) +@ cdecl _wfindfirst64i32(wstr ptr) +@ cdecl _wfindnext(long ptr) +@ cdecl _wfindnext64(long ptr) +@ cdecl _wfindnext64i32(long ptr) +@ cdecl _wfindnexti64(long ptr) +@ cdecl _wfopen(wstr wstr) +@ cdecl _wfopen_s(ptr wstr wstr) +@ cdecl _wfreopen(wstr wstr ptr) +@ cdecl _wfreopen_s(ptr wstr wstr ptr) +@ cdecl _wfsopen(wstr wstr long) +@ cdecl _wfullpath(ptr wstr long) +# stub _wfullpath_dbg(ptr wstr long long str long) +@ cdecl _wgetcwd(wstr long) +@ cdecl _wgetdcwd(long wstr long) +@ cdecl _wgetenv(wstr) +@ cdecl _wgetenv_s(ptr ptr long wstr) +@ extern _winmajor MSVCRT__winmajor +@ extern _winminor MSVCRT__winminor +# stub _winput_s +@ extern _winver MSVCRT__winver +@ cdecl _wmakepath(ptr wstr wstr wstr wstr) +@ cdecl _wmakepath_s(ptr long wstr wstr wstr wstr) +@ cdecl _wmkdir(wstr) +@ cdecl _wmktemp(wstr) +@ cdecl _wmktemp_s(wstr long) +@ varargs _wopen(wstr long) +# stub _woutput_s +@ cdecl _wperror(wstr) +@ extern _wpgmptr MSVCRT__wpgmptr +@ cdecl _wpopen (wstr wstr) _wpopen +# stub _wprintf_l(wstr ptr) +# stub _wprintf_p(wstr) +# stub _wprintf_p_l(wstr ptr) +# stub _wprintf_s_l(wstr ptr) +@ cdecl _wputenv(wstr) +@ cdecl _wputenv_s(wstr wstr) +@ cdecl _wremove(wstr) +@ cdecl _wrename(wstr wstr) +@ cdecl _write(long ptr long) +@ cdecl _wrmdir(wstr) +@ varargs _wscanf_l(wstr ptr) +@ varargs _wscanf_s_l(wstr ptr) +@ cdecl _wsearchenv(wstr wstr ptr) +@ cdecl _wsearchenv_s(wstr wstr ptr long) +@ cdecl _wsetlocale(long wstr) +@ varargs _wsopen(wstr long long) +@ cdecl _wsopen_s(ptr wstr long long long) +@ varargs _wspawnl(long wstr wstr) +@ varargs _wspawnle(long wstr wstr) +@ varargs _wspawnlp(long wstr wstr) +@ varargs _wspawnlpe(long wstr wstr) +@ cdecl _wspawnv(long wstr ptr) +@ cdecl _wspawnve(long wstr ptr ptr) +@ cdecl _wspawnvp(long wstr ptr) +@ cdecl _wspawnvpe(long wstr ptr ptr) +@ cdecl _wsplitpath(wstr ptr ptr ptr ptr) +@ cdecl _wsplitpath_s(wstr ptr long ptr long ptr long ptr long) +@ cdecl _wstat(wstr ptr) +@ cdecl _wstati64(wstr ptr) +@ cdecl _wstat64(wstr ptr) +@ cdecl _wstrdate(ptr) +@ cdecl _wstrdate_s(ptr long) +@ cdecl _wstrtime(ptr) +@ cdecl _wstrtime_s(ptr long) +@ cdecl _wsystem(wstr) +@ cdecl _wtempnam(wstr wstr) +# stub _wtempnam_dbg(wstr wstr long str long) +@ cdecl _wtmpnam(ptr) +@ cdecl _wtmpnam_s(ptr long) +@ cdecl _wtof(wstr) +@ cdecl _wtof_l(wstr ptr) +@ cdecl _wtoi(wstr) +@ cdecl -ret64 _wtoi64(wstr) +@ cdecl -ret64 _wtoi64_l(wstr ptr) +@ cdecl _wtoi_l(wstr ptr) +@ cdecl _wtol(wstr) +@ cdecl _wtol_l(wstr ptr) +@ cdecl _wunlink(wstr) +@ cdecl -arch=win32 _wutime(wstr ptr) _wutime32 +@ cdecl -arch=win64 _wutime(wstr ptr) _wutime64 +@ cdecl _wutime32(wstr ptr) +@ cdecl _wutime64(wstr ptr) +@ cdecl _y0(double) +@ cdecl _y1(double) +@ cdecl _yn(long double) +@ cdecl abort() +@ cdecl abs(long) +@ cdecl acos(double) +@ cdecl -arch=!i386 acosf(float) +@ cdecl asctime(ptr) +@ cdecl asctime_s(ptr long ptr) +@ cdecl asin(double) MSVCRT_asin +@ cdecl atan(double) MSVCRT_atan +@ cdecl atan2(double double) +@ cdecl -arch=!i386 asinf(float) +@ cdecl -arch=!i386 atanf(float) MSVCRT_atanf +@ cdecl -arch=!i386 atan2f(float float) +@ cdecl -private atexit(ptr) MSVCRT_atexit # not imported to avoid conflicts with Mingw +@ cdecl atof(str) +@ cdecl atoi(str) +@ cdecl atol(str) +@ cdecl bsearch(ptr ptr long long ptr) +@ cdecl bsearch_s(ptr ptr long long ptr ptr) +@ cdecl btowc(long) +@ cdecl calloc(long long) +@ cdecl ceil(double) +@ cdecl -arch=!i386 ceilf(float) +@ cdecl clearerr(ptr) +@ cdecl clearerr_s(ptr) +@ cdecl clock() +@ cdecl cos(double) +@ cdecl cosh(double) +@ cdecl -arch=!i386 cosf(float) +@ cdecl -arch=!i386 coshf(float) +@ cdecl -arch=win32 ctime(ptr) _ctime32 +@ cdecl -arch=win64 ctime(ptr) _ctime64 +@ cdecl -arch=win32 difftime(long long) _difftime32 +@ cdecl -arch=win64 difftime(long long) _difftime64 +@ cdecl -ret64 div(long long) +@ cdecl exit(long) +@ cdecl exp(double) MSVCRT_exp +@ cdecl -arch=!i386 expf(float) +@ cdecl fabs(double) +@ cdecl -arch=arm,arm64 fabsf(float) +@ cdecl fclose(ptr) +@ cdecl feof(ptr) +@ cdecl ferror(ptr) +@ cdecl fflush(ptr) +@ cdecl fgetc(ptr) +@ cdecl fgetpos(ptr ptr) +@ cdecl fgets(ptr long ptr) +@ cdecl fgetwc(ptr) +@ cdecl fgetws(ptr long ptr) +@ cdecl floor(double) +@ cdecl -arch=!i386 floorf(float) +@ cdecl fmod(double double) +@ cdecl -arch=!i386 fmodf(float float) +@ cdecl fopen(str str) +@ cdecl fopen_s(ptr str str) +@ varargs fprintf(ptr str) +@ varargs fprintf_s(ptr str) +@ cdecl fputc(long ptr) +@ cdecl fputs(str ptr) +@ cdecl fputwc(long ptr) +@ cdecl fputws(wstr ptr) +@ cdecl fread(ptr long long ptr) +@ cdecl free(ptr) +@ cdecl freopen(str str ptr) +@ cdecl freopen_s(ptr str str ptr) +@ cdecl frexp(double ptr) +@ cdecl -arch=x86_64 frexpf(float ptr) +@ varargs fscanf(ptr str) +@ varargs fscanf_s(ptr str) +@ cdecl fseek(ptr long long) +@ cdecl fsetpos(ptr ptr) +@ cdecl ftell(ptr) +@ varargs fwprintf(ptr wstr) +@ varargs fwprintf_s(ptr wstr) +@ cdecl fwrite(ptr long long ptr) +@ varargs fwscanf(ptr wstr) +@ varargs fwscanf_s(ptr wstr) +@ cdecl getc(ptr) +@ cdecl getchar() +@ cdecl getenv(str) +@ cdecl getenv_s(ptr ptr long str) +@ cdecl gets(str) +@ cdecl getwc(ptr) +@ cdecl getwchar() +@ cdecl -arch=win32 gmtime(ptr) _gmtime32 +@ cdecl -arch=win64 gmtime(ptr) _gmtime64 +@ cdecl is_wctype(long long) iswctype +@ cdecl isalnum(long) +@ cdecl isalpha(long) +@ cdecl iscntrl(long) +@ cdecl isdigit(long) +@ cdecl isgraph(long) +@ cdecl isleadbyte(long) +@ cdecl islower(long) +@ cdecl isprint(long) +@ cdecl ispunct(long) +@ cdecl isspace(long) +@ cdecl isupper(long) +@ cdecl iswalnum(long) +@ cdecl iswalpha(long) +@ cdecl iswascii(long) +@ cdecl iswcntrl(long) +@ cdecl iswctype(long long) +@ cdecl iswdigit(long) +@ cdecl iswgraph(long) +@ cdecl iswlower(long) +@ cdecl iswprint(long) +@ cdecl iswpunct(long) +@ cdecl iswspace(long) +@ cdecl iswupper(long) +@ cdecl iswxdigit(long) +@ cdecl isxdigit(long) +@ cdecl labs(long) +@ cdecl ldexp(double long) +@ cdecl -ret64 ldiv(long long) +@ cdecl localeconv() +@ cdecl -arch=win32 localtime(ptr) _localtime32 +@ cdecl -arch=win64 localtime(ptr) _localtime64 +@ cdecl log(double) +@ cdecl log10(double) +@ cdecl -arch=!i386 logf(float) +@ cdecl -arch=!i386 log10f(float) +@ cdecl longjmp(ptr long) +@ cdecl malloc(long) +@ cdecl mblen(ptr long) +@ cdecl mbrlen(ptr long ptr) +@ cdecl mbrtowc(ptr str long ptr) +# stub mbsdup_dbg(wstr long ptr long) +@ cdecl mbsrtowcs(ptr ptr long ptr) +@ cdecl mbsrtowcs_s(ptr ptr long ptr long ptr) +@ cdecl mbstowcs(ptr str long) +@ cdecl mbstowcs_s(ptr ptr long str long) _mbstowcs_s +@ cdecl mbtowc(ptr str long) +@ cdecl memchr(ptr long long) +@ cdecl memcmp(ptr ptr long) +@ cdecl memcpy(ptr ptr long) +@ cdecl memcpy_s(ptr long ptr long) +@ cdecl memmove(ptr ptr long) +@ cdecl memmove_s(ptr long ptr long) +@ cdecl memset(ptr long long) +@ cdecl -arch=win32 mktime(ptr) _mktime32 +@ cdecl -arch=win64 mktime(ptr) _mktime64 +@ cdecl modf(double ptr) +@ cdecl -arch=!i386 modff(float ptr) +@ cdecl perror(str) +@ cdecl pow(double double) +@ cdecl -arch=!i386 powf(float float) +@ varargs printf(str) +@ varargs printf_s(str) +@ cdecl putc(long ptr) +@ cdecl putchar(long) +@ cdecl puts(str) +@ cdecl putwc(long ptr) fputwc +@ cdecl putwchar(long) _fputwchar +@ cdecl qsort(ptr long long ptr) +@ cdecl qsort_s(ptr long long ptr ptr) +@ cdecl raise(long) +@ cdecl rand() +@ cdecl rand_s(ptr) +@ cdecl realloc(ptr long) +@ cdecl remove(str) +@ cdecl rename(str str) +@ cdecl rewind(ptr) +@ varargs scanf(str) +@ varargs scanf_s(str) +@ cdecl setbuf(ptr ptr) +@ cdecl -arch=arm,x86_64 -norelay -private setjmp(ptr ptr) _setjmp +@ cdecl setlocale(long str) +@ cdecl setvbuf(ptr str long long) +@ cdecl signal(long long) +@ cdecl sin(double) +@ cdecl sinh(double) +@ cdecl -arch=!i386 sinf(float) +@ cdecl -arch=!i386 sinhf(float) +@ varargs sprintf(ptr str) +@ varargs sprintf_s(ptr long str) +@ cdecl sqrt(double) MSVCRT_sqrt +@ cdecl -arch=!i386 sqrtf(float) MSVCRT_sqrtf +@ cdecl srand(long) +@ varargs sscanf(str str) +@ varargs sscanf_s(str str) +@ cdecl strcat(str str) +@ cdecl strcat_s(str long str) +@ cdecl strchr(str long) +@ cdecl strcmp(str str) +@ cdecl strcoll(str str) +@ cdecl strcpy(ptr str) +@ cdecl strcpy_s(ptr long str) +@ cdecl strcspn(str str) +@ cdecl strerror(long) +@ cdecl strerror_s(ptr long long) +@ cdecl strftime(ptr long str ptr) +@ cdecl strlen(str) +@ cdecl strncat(str str long) +@ cdecl strncat_s(str long str long) +@ cdecl strncmp(str str long) +@ cdecl strncpy(ptr str long) +@ cdecl strncpy_s(ptr long str long) +@ cdecl strnlen(str long) +@ cdecl strpbrk(str str) +@ cdecl strrchr(str long) +@ cdecl strspn(str str) +@ cdecl strstr(str str) +@ cdecl strtod(str ptr) +@ cdecl strtok(str str) +@ cdecl strtok_s(ptr str ptr) +@ cdecl strtol(str ptr long) +@ cdecl strtoul(str ptr long) +@ cdecl strxfrm(ptr str long) +@ varargs swprintf(ptr wstr) _swprintf +@ varargs swprintf_s(ptr long wstr) +@ varargs swscanf(wstr wstr) +@ varargs swscanf_s(wstr wstr) +@ cdecl system(str) +@ cdecl tan(double) +@ cdecl tanh(double) MSVCRT_tanh +@ cdecl -arch=!i386 tanf(float) +@ cdecl -arch=!i386 tanhf(float) MSVCRT_tanhf +@ cdecl -arch=win32 time(ptr) _time32 +@ cdecl -arch=win64 time(ptr) _time64 +@ cdecl tmpfile() +@ cdecl tmpfile_s(ptr) +@ cdecl tmpnam(ptr) +@ cdecl tmpnam_s(ptr long) +@ cdecl tolower(long) +@ cdecl toupper(long) +@ cdecl towlower(long) +@ cdecl towupper(long) +@ cdecl ungetc(long ptr) +@ cdecl ungetwc(long ptr) +# stub utime +@ cdecl vfprintf(ptr str ptr) +@ cdecl vfprintf_s(ptr str ptr) +@ cdecl vfwprintf(ptr wstr ptr) +@ cdecl vfwprintf_s(ptr wstr ptr) +@ cdecl vprintf(str ptr) +@ cdecl vprintf_s(str ptr) +@ cdecl vsnprintf(ptr long str ptr) _vsnprintf +@ cdecl vsprintf(ptr str ptr) +@ cdecl vsprintf_s(ptr long str ptr) +@ cdecl vswprintf(ptr wstr ptr) _vswprintf +@ cdecl vswprintf_s(ptr long wstr ptr) +@ cdecl vwprintf(wstr ptr) +@ cdecl vwprintf_s(wstr ptr) +@ cdecl wcrtomb(ptr long ptr) +@ cdecl wcrtomb_s(ptr ptr long long ptr) +@ cdecl wcscat(wstr wstr) +@ cdecl wcscat_s(wstr long wstr) +@ cdecl wcschr(wstr long) +@ cdecl wcscmp(wstr wstr) +@ cdecl wcscoll(wstr wstr) +@ cdecl wcscpy(ptr wstr) +@ cdecl wcscpy_s(ptr long wstr) +@ cdecl wcscspn(wstr wstr) +@ cdecl wcsftime(ptr long wstr ptr) +@ cdecl wcslen(wstr) +@ cdecl wcsncat(wstr wstr long) +@ cdecl wcsncat_s(wstr long wstr long) +@ cdecl wcsncmp(wstr wstr long) +@ cdecl wcsncpy(ptr wstr long) +@ cdecl wcsncpy_s(ptr long wstr long) +@ cdecl wcsnlen(wstr long) +@ cdecl wcspbrk(wstr wstr) +@ cdecl wcsrchr(wstr long) +@ cdecl wcsrtombs(ptr ptr long ptr) +@ cdecl wcsrtombs_s(ptr ptr long ptr long ptr) +@ cdecl wcsspn(wstr wstr) +@ cdecl wcsstr(wstr wstr) +@ cdecl wcstod(wstr ptr) +@ cdecl wcstok(wstr wstr) +@ cdecl wcstok_s(ptr wstr ptr) +@ cdecl wcstol(wstr ptr long) +@ cdecl wcstombs(ptr ptr long) +@ cdecl wcstombs_s(ptr ptr long wstr long) +@ cdecl wcstoul(wstr ptr long) +@ cdecl wcsxfrm(ptr wstr long) +@ cdecl wctob(long) +@ cdecl wctomb(ptr long) +@ cdecl wctomb_s(ptr ptr long long) +@ varargs wprintf(wstr) +@ varargs wprintf_s(wstr) +@ varargs wscanf(wstr) +@ varargs wscanf_s(wstr) diff --git a/dll/win32/msvcrt/mtdll.h b/dll/win32/msvcrt/mtdll.h new file mode 100644 index 00000000000..87eebd97dd4 --- /dev/null +++ b/dll/win32/msvcrt/mtdll.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2002, TransGaming Technologies Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#ifndef WINE_MTDLL_H +#define WINE_MTDLL_H + +void __cdecl _unlock( int locknum ); +void __cdecl _lock( int locknum ); + +#define _SIGNAL_LOCK 1 +#define _IOB_SCAN_LOCK 2 +#define _TMPNAM_LOCK 3 +#define _INPUT_LOCK 4 +#define _OUTPUT_LOCK 5 +#define _CSCANF_LOCK 6 +#define _CPRINTF_LOCK 7 +#define _CONIO_LOCK 8 +#define _HEAP_LOCK 9 +#define _BHEAP_LOCK 10 /* No longer used? */ +#define _TIME_LOCK 11 +#define _ENV_LOCK 12 +#define _EXIT_LOCK1 13 +#define _EXIT_LOCK2 14 +#define _THREADDATA_LOCK 15 /* No longer used? */ +#define _POPEN_LOCK 16 +#define _LOCKTAB_LOCK 17 +#define _OSFHND_LOCK 18 +#define _SETLOCALE_LOCK 19 +#define _LC_COLLATE_LOCK 20 /* No longer used? */ +#define _LC_CTYPE_LOCK 21 /* No longer used? */ +#define _LC_MONETARY_LOCK 22 /* No longer used? */ +#define _LC_NUMERIC_LOCK 23 /* No longer used? */ +#define _LC_TIME_LOCK 24 /* No longer used? */ +#define _MB_CP_LOCK 25 +#define _NLG_LOCK 26 +#define _TYPEINFO_LOCK 27 +#define _STREAM_LOCKS 28 + +/* Must match definition in msvcrt/stdio.h */ +#define _IOB_ENTRIES 20 + +#define _LAST_STREAM_LOCK (_STREAM_LOCKS+_IOB_ENTRIES-1) + +#define _TOTAL_LOCKS (_LAST_STREAM_LOCK+1) + +#endif /* WINE_MTDLL_H */ diff --git a/dll/win32/msvcrt/onexit.c b/dll/win32/msvcrt/onexit.c new file mode 100644 index 00000000000..d0c61700542 --- /dev/null +++ b/dll/win32/msvcrt/onexit.c @@ -0,0 +1,120 @@ +/* + * msvcrt onexit functions + * + * Copyright 2016 Nikolay Sivov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +/* these functions are part of the import lib for compatibility with the Mingw runtime */ +#if 0 +#pragma makedep implib +#endif + +#include +#include "msvcrt.h" +#include "mtdll.h" + + +/********************************************************************* + * _initialize_onexit_table (UCRTBASE.@) + */ +int __cdecl _initialize_onexit_table(_onexit_table_t *table) +{ + if (!table) + return -1; + + if (table->_first == table->_end) + table->_last = table->_end = table->_first = NULL; + return 0; +} + + +/********************************************************************* + * _register_onexit_function (UCRTBASE.@) + */ +int __cdecl _register_onexit_function(_onexit_table_t *table, _onexit_t func) +{ + if (!table) + return -1; + + _lock(_EXIT_LOCK1); + if (!table->_first) + { + table->_first = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 32 * sizeof(void *)); + if (!table->_first) + { + _unlock(_EXIT_LOCK1); + return -1; + } + table->_last = table->_first; + table->_end = table->_first + 32; + } + + /* grow if full */ + if (table->_last == table->_end) + { + int len = table->_end - table->_first; + _PVFV *tmp = HeapReAlloc(GetProcessHeap(), 0, table->_first, 2 * len * sizeof(void *)); + if (!tmp) + { + _unlock(_EXIT_LOCK1); + return -1; + } + table->_first = tmp; + table->_end = table->_first + 2 * len; + table->_last = table->_first + len; + } + + *table->_last = (_PVFV)func; + table->_last++; + _unlock(_EXIT_LOCK1); + return 0; +} + + +/********************************************************************* + * _execute_onexit_table (UCRTBASE.@) + */ +int __cdecl _execute_onexit_table(_onexit_table_t *table) +{ + _PVFV *func; + _onexit_table_t copy; + + if (!table) + return -1; + + _lock(_EXIT_LOCK1); + if (!table->_first || table->_first >= table->_last) + { + _unlock(_EXIT_LOCK1); + return 0; + } + copy._first = table->_first; + copy._last = table->_last; + copy._end = table->_end; + memset(table, 0, sizeof(*table)); + _initialize_onexit_table(table); + _unlock(_EXIT_LOCK1); + + for (func = copy._last - 1; func >= copy._first; func--) + { + if (*func) + (*func)(); + } + + HeapFree(GetProcessHeap(), 0, copy._first); + return 0; +} diff --git a/dll/win32/msvcrt/printf.h b/dll/win32/msvcrt/printf.h new file mode 100644 index 00000000000..be30d6ae719 --- /dev/null +++ b/dll/win32/msvcrt/printf.h @@ -0,0 +1,1318 @@ +/* + * Copyright 2011 Piotr Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include "bnum.h" + +#ifdef PRINTF_WIDE +#define APICHAR wchar_t +#define CONVCHAR char +#define FUNC_NAME(func) func ## _w +#else +#define APICHAR char +#define CONVCHAR wchar_t +#define FUNC_NAME(func) func ## _a +#endif + +struct FUNC_NAME(_str_ctx) { + size_t len; + APICHAR *buf; +}; + +static int FUNC_NAME(puts_clbk_str)(void *ctx, int len, const APICHAR *str) +{ + struct FUNC_NAME(_str_ctx) *out = ctx; + + if(!out->buf) + return len; + + if(out->len < len) { + memmove(out->buf, str, out->len*sizeof(APICHAR)); + out->buf += out->len; + out->len = 0; + return -1; + } + + memmove(out->buf, str, len*sizeof(APICHAR)); + out->buf += len; + out->len -= len; + return len; +} + +static inline const APICHAR* FUNC_NAME(pf_parse_int)(const APICHAR *fmt, int *val) +{ + *val = 0; + + while (*fmt >= '0' && *fmt <= '9') { + *val *= 10; + *val += *fmt++ - '0'; + } + + return fmt; +} + +/* pf_fill: takes care of signs, alignment, zero and field padding */ +static inline int FUNC_NAME(pf_fill)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, + int len, pf_flags *flags, BOOL left) +{ + int i, r = 0, written; + + if(flags->Sign && !strchr("diaAeEfFgG", flags->Format)) + flags->Sign = 0; + + if(left && flags->Sign) { + APICHAR ch = flags->Sign; + flags->FieldLength--; + if(flags->PadZero) + r = pf_puts(puts_ctx, 1, &ch); + } + written = r; + + if((!left && flags->LeftAlign) || (left && !flags->LeftAlign)) { + APICHAR ch; + + if(left && flags->PadZero) + ch = '0'; + else + ch = ' '; + + for(i=0; iFieldLength-len && r>=0; i++) { + r = pf_puts(puts_ctx, 1, &ch); + written += r; + } + } + + + if(r>=0 && left && flags->Sign && !flags->PadZero) { + APICHAR ch = flags->Sign; + r = pf_puts(puts_ctx, 1, &ch); + written += r; + } + + return r>=0 ? written : r; +} + +#ifndef PRINTF_HELPERS +#define PRINTF_HELPERS +static inline int wcstombs_len(char *mbstr, const wchar_t *wcstr, + int len, _locale_t locale) +{ + char buf[MB_LEN_MAX]; + int i, r, mblen = 0; + + for(i=0; iPrecision>=0) + len = wcsnlen(str, flags->Precision); + else + len = wcslen(str); + } + + if(flags->Precision>=0 && flags->PrecisionPrecision; + + r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, TRUE); + ret = r; + if(r >= 0) { + r = FUNC_NAME(pf_output_wstr)(pf_puts, puts_ctx, str, len, locale); + ret += r; + } + if(r >= 0) { + r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, FALSE); + ret += r; + } + + return r>=0 ? ret : r; +} + +static inline int FUNC_NAME(pf_output_format_str)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, + const char *str, int len, pf_flags *flags, _locale_t locale) +{ + int r, ret; + + if(len < 0) { + /* Do not search past the length specified by the precision. */ + if(flags->Precision>=0) + len = strnlen(str, flags->Precision); + else + len = strlen(str); + } + + if(flags->Precision>=0 && flags->PrecisionPrecision; + + r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, TRUE); + ret = r; + if(r >= 0) { + r = FUNC_NAME(pf_output_str)(pf_puts, puts_ctx, str, len, locale); + ret += r; + } + if(r >= 0) { + r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, FALSE); + ret += r; + } + + return r>=0 ? ret : r; +} + +static inline int FUNC_NAME(pf_handle_string)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, + const void *str, int len, pf_flags *flags, _locale_t locale, BOOL legacy_wide) +{ + BOOL api_is_wide = sizeof(APICHAR) == sizeof(wchar_t); + BOOL complement_is_narrow = legacy_wide ? api_is_wide : FALSE; +#ifdef PRINTF_WIDE + + if(!str) + return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, L"(null)", 6, flags, locale); +#else + if(!str) + return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, "(null)", 6, flags, locale); +#endif + + if((flags->NaturalString && api_is_wide) || flags->WideString || flags->IntegerLength == LEN_LONG) + return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, str, len, flags, locale); + if((flags->NaturalString && !api_is_wide) || flags->IntegerLength == LEN_SHORT) + return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, len, flags, locale); + + if((flags->Format=='S' || flags->Format=='C') == complement_is_narrow) + return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, len, flags, locale); + else + return FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, str, len, flags, locale); +} + +static inline int FUNC_NAME(pf_output_special_fp)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, + double v, pf_flags *flags, _locale_t locale, + BOOL legacy_msvcrt_compat, BOOL three_digit_exp) +{ + APICHAR pfx[16], sfx[8], *p; + int len = 0, r, frac_len, pfx_len, sfx_len; + + if(!legacy_msvcrt_compat) { + const char *str; + + if(isinf(v)) { + if(strchr("AEFG", flags->Format)) str = "INF"; + else str = "inf"; + }else { + if(strchr("AEFG", flags->Format)) str = (flags->Sign == '-' ? "NAN(IND)" : "NAN"); + else str = (flags->Sign == '-' ? "nan(ind)" : "nan"); + } + + flags->Precision = -1; + flags->PadZero = FALSE; + return FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, str, -1, flags, locale); + } + + /* workaround a bug in native implementation */ + if(flags->Format=='g' || flags->Format=='G') + flags->Precision--; + + p = pfx; + if(flags->PadZero && (flags->Format=='a' || flags->Format=='A')) { + if (flags->Sign) *p++ = flags->Sign; + *p++ = '0'; + *p++ = (flags->Format=='a' ? 'x' : 'X'); + r = pf_puts(puts_ctx, p-pfx, pfx); + if(r < 0) return r; + len += r; + + flags->FieldLength -= p-pfx; + } + + p = pfx; + if(!flags->PadZero && (flags->Format=='a' || flags->Format=='A')) { + *p++ = '0'; + *p++ = (flags->Format=='a' ? 'x' : 'X'); + } + + *p++ = '1'; + *p++ = *(locale ? locale->locinfo : get_locinfo())->lconv->decimal_point; + *p++ = '#'; + frac_len = 1; + + if(isinf(v)) { + *p++ = 'I'; + *p++ = 'N'; + *p++ = 'F'; + frac_len += 3; + }else if(flags->Sign == '-') { + *p++ = 'I'; + *p++ = 'N'; + *p++ = 'D'; + frac_len += 3; + }else { + *p++ = 'Q'; + *p++ = 'N'; + *p++ = 'A'; + *p++ = 'N'; + frac_len += 4; + } + *p = 0; + pfx_len = p - pfx; + + if(len) flags->Sign = 0; + + if(flags->Precision>=0 && flags->PrecisionPrecision - frac_len - 1]++; + + p = sfx; + if(strchr("aAeE", flags->Format)) { + if(flags->Format == 'a') *p++ = 'p'; + else if(flags->Format == 'A') *p++ = 'P'; + else if(flags->Format == 'e') *p++ = 'e'; + else *p++ = 'E'; + + *p++ = '+'; + *p++ = '0'; + + if(flags->Format == 'e' || flags->Format == 'E') { + *p++ = '0'; + if(three_digit_exp) *p++ = '0'; + } + } + *p = 0; + + if(!flags->Alternate && (flags->Format == 'g' || flags->Format == 'G')) sfx_len = frac_len; + else sfx_len = flags->Precision; + + if(sfx_len == -1) { + if(strchr("fFeE", flags->Format)) sfx_len = 6; + else if(flags->Format == 'a' || flags->Format == 'A') sfx_len = 13; + } + sfx_len += p - sfx - frac_len; + + if(sfx_len > 0) flags->FieldLength -= sfx_len; + if(flags->Precision >= 0) { + if(!flags->Precision) flags->Precision--; + flags->Precision += pfx_len - frac_len; + } +#ifdef PRINTF_WIDE + r = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, pfx, -1, flags, locale); +#else + r = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, pfx, -1, flags, locale); +#endif + if(r < 0) return r; + len += r; + + flags->FieldLength = sfx_len; + flags->PadZero = TRUE; + flags->Precision = -1; + flags->Sign = 0; +#ifdef PRINTF_WIDE + r = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, sfx, -1, flags, locale); +#else + r = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, sfx, -1, flags, locale); +#endif + if(r < 0) return r; + len += r; + + return len; +} + +static inline int FUNC_NAME(pf_output_hex_fp)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, + double v, pf_flags *flags, _locale_t locale, BOOL standard_rounding) +{ + const APICHAR digits[2][16] = { + { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }, + { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' } + }; + + APICHAR pfx[4+MANT_BITS/4+1], sfx[8], *p; + ULONGLONG mant; + int len = 0, sfx_len = 0, r, exp; + + mant = (*(ULONGLONG*)&v) << 1; + exp = (mant >> MANT_BITS); + exp -= (1 << (EXP_BITS - 1)) - 1; + mant = (mant << EXP_BITS) >> (EXP_BITS+1); + + p = pfx; + if(flags->PadZero) { + if(flags->Sign) *p++ = flags->Sign; + *p++ = '0'; + *p++ = (flags->Format=='a' ? 'x' : 'X'); + r = pf_puts(puts_ctx, p-pfx, pfx); + if(r < 0) return r; + len += r; + + flags->FieldLength -= p-pfx; + flags->Sign = 0; + p = pfx; + }else { + *p++ = '0'; + *p++ = (flags->Format=='a' ? 'x' : 'X'); + } + if(exp == -(1 << (EXP_BITS-1))+1) { + if(!mant) exp = 0; + else exp++; + *p++ = '0'; + }else { + *p++ = '1'; + } + *p++ = *(locale ? locale->locinfo : get_locinfo())->lconv->decimal_point; + for(r=MANT_BITS/4-1; r>=0; r--) { + p[r] = digits[flags->Format == 'A'][mant & 15]; + mant >>= 4; + } + if(!flags->Precision) { + if(p[0] >= '8') p[-2]++; + if(!flags->Alternate) p--; + }else if(flags->Precision>0 && flags->PrecisionPrecision] >= '8'); + else if(p[flags->Precision] > '8') round_up = TRUE; + else if(p[flags->Precision] == '8') { + for(r = flags->Precision+1; rPrecision-1] <= '9') round_up = (p[flags->Precision-1] - '0') & 1; + else if(p[flags->Precision-1] <= 'F') round_up = (p[flags->Precision-1] - 'A') & 1; + else round_up = (p[flags->Precision-1] - 'a') & 1; + } + } + + for(r=flags->Precision-1; r>=0 && round_up; r--) { + round_up = FALSE; + if(p[r]=='f' || p[r]=='F') { + p[r] = '0'; + round_up = TRUE; + }else if(p[r] == '9') { + p[r] = (flags->Format == 'a' ? 'a' : 'A'); + }else { + p[r]++; + } + } + if(round_up) p[-2]++; + p += flags->Precision; + }else { + p += MANT_BITS/4; + if(flags->Precision > MANT_BITS/4) sfx_len += flags->Precision - MANT_BITS/4; + } + *p = 0; + + p = sfx; + *p++ = (flags->Format == 'a' ? 'p' : 'P'); + if(exp < 0) { + *p++ = '-'; + exp = -exp; + }else { + *p++ = '+'; + } + for(r=3; r>=0; r--) { + p[r] = exp%10 + '0'; + exp /= 10; + if(!exp) break; + } + for(exp=0; exp<4-r; exp++) + p[exp] = p[exp+r]; + p += exp; + *p = 0; + sfx_len += p - sfx; + + flags->FieldLength -= sfx_len; + flags->Precision = -1; +#ifdef PRINTF_WIDE + r = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, pfx, -1, flags, locale); +#else + r = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, pfx, -1, flags, locale); +#endif + if(r < 0) return r; + len += r; + + flags->FieldLength = sfx_len; + flags->PadZero = TRUE; + flags->Sign = 0; +#ifdef PRINTF_WIDE + r = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, sfx, -1, flags, locale); +#else + r = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, sfx, -1, flags, locale); +#endif + if(r < 0) return r; + len += r; + + return len; +} + +/* pf_integer_conv: prints x to buf, including alternate formats and + additional precision digits, but not field characters or the sign */ +static inline void FUNC_NAME(pf_integer_conv)(APICHAR *buf, pf_flags *flags, LONGLONG x) +{ + unsigned int base; + const char *digits; + int i, j, k; + + if(flags->Format == 'o') + base = 8; + else if(flags->Format=='x' || flags->Format=='X') + base = 16; + else + base = 10; + + if(flags->Format == 'X') + digits = "0123456789ABCDEFX"; + else + digits = "0123456789abcdefx"; + + if(x<0 && (flags->Format=='d' || flags->Format=='i')) { + x = -x; + flags->Sign = '-'; + } + + i = 0; + if(x == 0) { + flags->Alternate = FALSE; + if(flags->Precision) + buf[i++] = '0'; + } else { + while(x != 0) { + j = (ULONGLONG)x%base; + x = (ULONGLONG)x/base; + buf[i++] = digits[j]; + } + } + k = flags->Precision-i; + while(k-- > 0) + buf[i++] = '0'; + if(flags->Alternate) { + if(base == 16) { + buf[i++] = digits[16]; + buf[i++] = '0'; + } else if(base==8 && buf[i-1]!='0') + buf[i++] = '0'; + } + + /* Adjust precision so pf_fill won't truncate the number later */ + flags->Precision = i; + + buf[i] = '\0'; + j = 0; + while(--i > j) { + APICHAR tmp = buf[j]; + buf[j] = buf[i]; + buf[i] = tmp; + j++; + } +} + +static inline int FUNC_NAME(pf_output_fp)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, + double v, pf_flags *flags, _locale_t locale, BOOL three_digit_exp, + BOOL standard_rounding) +{ + int e2, e10 = 0, round_pos, round_limb, radix_pos, first_limb_len, i, len, r, ret; + BYTE bnum_data[FIELD_OFFSET(struct bnum, data[BNUM_PREC64])]; + struct bnum *b = (struct bnum*)bnum_data; + APICHAR buf[LIMB_DIGITS + 1]; + BOOL trim_tail = FALSE, round_up = FALSE; + pf_flags f; + int limb_len, prec; + ULONGLONG m; + DWORD l; + + if(flags->Precision == -1) + flags->Precision = 6; + + v = frexp(v, &e2); + if(v) { + m = (ULONGLONG)1 << (MANT_BITS - 1); + m |= (*(ULONGLONG*)&v & (((ULONGLONG)1 << (MANT_BITS - 1)) - 1)); + b->b = 0; + b->e = 2; + b->size = BNUM_PREC64; + b->data[0] = m % LIMB_MAX; + b->data[1] = m / LIMB_MAX; + e2 -= MANT_BITS; + + while(e2 > 0) { + int shift = e2 > 29 ? 29 : e2; + if(bnum_lshift(b, shift)) e10 += LIMB_DIGITS; + e2 -= shift; + } + while(e2 < 0) { + int shift = -e2 > 9 ? 9 : -e2; + if(bnum_rshift(b, shift)) e10 -= LIMB_DIGITS; + e2 += shift; + } + } else { + b->b = 0; + b->e = 1; + b->size = BNUM_PREC64; + b->data[0] = 0; + e10 = -LIMB_DIGITS; + } + + if(!b->data[bnum_idx(b, b->e-1)]) + first_limb_len = 1; + else + first_limb_len = log10i(b->data[bnum_idx(b, b->e - 1)]) + 1; + radix_pos = first_limb_len + LIMB_DIGITS + e10; + + round_pos = flags->Precision; + if(flags->Format=='f' || flags->Format=='F') + round_pos += radix_pos; + else if(!flags->Precision || flags->Format=='e' || flags->Format=='E') + round_pos++; + if (round_pos <= first_limb_len) + round_limb = b->e + (first_limb_len - round_pos) / LIMB_DIGITS - 1; + else + round_limb = b->e - (round_pos - first_limb_len - 1) / LIMB_DIGITS - 2; + + if (b->b<=round_limb && round_limbe) { + if (round_pos <= first_limb_len) { + round_pos = first_limb_len - round_pos; + } else { + round_pos = LIMB_DIGITS - (round_pos - first_limb_len) % LIMB_DIGITS; + if (round_pos == LIMB_DIGITS) round_pos = 0; + } + + if (round_pos) { + l = b->data[bnum_idx(b, round_limb)] % p10s[round_pos]; + b->data[bnum_idx(b, round_limb)] -= l; + if(!standard_rounding) round_up = (2*l >= p10s[round_pos]); + else if(2*l > p10s[round_pos]) round_up = TRUE; + else if(2*l == p10s[round_pos]) { + for(r = round_limb-1; r >= b->b; r--) { + if(b->data[bnum_idx(b, r)]) { + round_up = TRUE; + break; + } + } + + if(!round_up) round_up = b->data[bnum_idx(b, round_limb)] / p10s[round_pos] & 1; + } + } else if(round_limb - 1 >= b->b) { + if(!standard_rounding) round_up = (2*b->data[bnum_idx(b, round_limb-1)] >= LIMB_MAX); + else if(2*b->data[bnum_idx(b, round_limb-1)] > LIMB_MAX) round_up = TRUE; + else if(2*b->data[bnum_idx(b, round_limb-1)] == LIMB_MAX) { + for(r = round_limb-2; r >= b->b; r--) { + if(b->data[bnum_idx(b, r)]) { + round_up = TRUE; + break; + } + } + + if(!round_up) round_up = b->data[bnum_idx(b, round_limb)] & 1; + } + } + b->b = round_limb; + + if(round_up) { + b->data[bnum_idx(b, b->b)] += p10s[round_pos]; + for(i = b->b; i < b->e; i++) { + if(b->data[bnum_idx(b, i)] < LIMB_MAX) break; + + b->data[bnum_idx(b, i)] -= LIMB_MAX; + if(i+1 < b->e) b->data[bnum_idx(b, i+1)]++; + else b->data[bnum_idx(b, i+1)] = 1; + } + if(i == b->e-1) { + if(!b->data[bnum_idx(b, b->e-1)]) + i = 1; + else + i = log10i(b->data[bnum_idx(b, b->e-1)]) + 1; + if(i != first_limb_len) { + first_limb_len = i; + radix_pos++; + + round_pos++; + if (round_pos == LIMB_DIGITS) + { + round_pos = 0; + round_limb++; + } + } + } else if(i == b->e) { + first_limb_len = 1; + radix_pos++; + b->e++; + + round_pos++; + if (round_pos == LIMB_DIGITS) + { + round_pos = 0; + round_limb++; + } + } + } + } + else if(b->e <= round_limb) { /* got 0 or 1 after rounding */ + if(b->e == round_limb) { + if(!standard_rounding) round_up = b->data[bnum_idx(b, b->e-1)] >= LIMB_MAX/2; + else if(b->data[bnum_idx(b, b->e-1)] > LIMB_MAX/2) round_up = TRUE; + else if(b->data[bnum_idx(b, b->e-1)] == LIMB_MAX/2) { + for(r = b->e-2; r >= b->b; r--) { + if(b->data[bnum_idx(b, r)]) { + round_up = TRUE; + break; + } + } + } + } + + b->data[bnum_idx(b, round_limb)] = round_up; + b->b = round_limb; + b->e = b->b + 1; + first_limb_len = 1; + radix_pos++; + } + + if(flags->Format=='g' || flags->Format=='G') { + trim_tail = TRUE; + + if(radix_pos>=-3 && radix_pos<=flags->Precision) { + flags->Format -= 1; + if(!flags->Precision) flags->Precision++; + flags->Precision -= radix_pos; + } else { + flags->Format -= 2; + if(flags->Precision > 0) flags->Precision--; + } + } + + if(trim_tail && !flags->Alternate) { + for(i=round_limb; flags->Precision>0 && ie; i++) { + if(i>=b->b) + l = b->data[bnum_idx(b, i)]; + else + l = 0; + + if(i == round_limb) { + if(flags->Format=='f' || flags->Format=='F') + r = radix_pos + flags->Precision; + else + r = flags->Precision + 1; + r = first_limb_len + LIMB_DIGITS * (b->e-1 - b->b) - r; + r %= LIMB_DIGITS; + if(r < 0) r += LIMB_DIGITS; + l /= p10s[r]; + limb_len = LIMB_DIGITS - r; + } else { + limb_len = LIMB_DIGITS; + } + + if(!l) { + flags->Precision -= limb_len; + } else { + while(l % 10 == 0) { + flags->Precision--; + l /= 10; + } + } + + if(flags->Precision <= 0) { + flags->Precision = 0; + break; + } + if(l) + break; + } + } + + len = flags->Precision; + if(flags->Precision || flags->Alternate) len++; + if(flags->Format=='f' || flags->Format=='F') { + len += (radix_pos > 0 ? radix_pos : 1); + } else if(flags->Format=='e' || flags->Format=='E') { + radix_pos--; + if(!trim_tail || radix_pos) { + len += 3; /* strlen("1e+") */ + if(three_digit_exp || radix_pos<-99 || radix_pos>99) len += 3; + else len += 2; + } else { + len++; + } + } + + r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, TRUE); + if(r < 0) return r; + ret = r; + + f.Format = 'd'; + f.PadZero = TRUE; + if(flags->Format=='f' || flags->Format=='F') { + if(radix_pos <= 0) { + buf[0] = '0'; + r = pf_puts(puts_ctx, 1, buf); + if(r < 0) return r; + ret += r; + } + + limb_len = LIMB_DIGITS; + for(i=b->e-1; radix_pos>0 && i>=b->b; i--) { + limb_len = (i == b->e-1 ? first_limb_len : LIMB_DIGITS); + l = b->data[bnum_idx(b, i)]; + if(limb_len > radix_pos) { + f.Precision = radix_pos; + l /= p10s[limb_len - radix_pos]; + limb_len = limb_len - radix_pos; + } else { + f.Precision = limb_len; + limb_len = LIMB_DIGITS; + } + radix_pos -= f.Precision; + FUNC_NAME(pf_integer_conv)(buf, &f, l); + + r = pf_puts(puts_ctx, f.Precision, buf); + if(r < 0) return r; + ret += r; + } + + buf[0] = '0'; + for(; radix_pos>0; radix_pos--) { + r = pf_puts(puts_ctx, 1, buf); + if(r < 0) return r; + ret += r; + } + + if(flags->Precision || flags->Alternate) { + buf[0] = *(locale ? locale->locinfo : get_locinfo())->lconv->decimal_point; + r = pf_puts(puts_ctx, 1, buf); + if(r < 0) return r; + ret += r; + } + + prec = flags->Precision; + buf[0] = '0'; + for(; prec>0 && radix_pos+LIMB_DIGITS-first_limb_len<0; radix_pos++, prec--) { + r = pf_puts(puts_ctx, 1, buf); + if(r < 0) return r; + ret += r; + } + + for(; prec>0 && i>=b->b; i--) { + l = b->data[bnum_idx(b, i)]; + if(limb_len != LIMB_DIGITS) + l %= p10s[limb_len]; + if(limb_len > prec) { + f.Precision = prec; + l /= p10s[limb_len - prec]; + } else { + f.Precision = limb_len; + limb_len = LIMB_DIGITS; + } + prec -= f.Precision; + FUNC_NAME(pf_integer_conv)(buf, &f, l); + + r = pf_puts(puts_ctx, f.Precision, buf); + if(r < 0) return r; + ret += r; + } + + buf[0] = '0'; + for(; prec>0; prec--) { + r = pf_puts(puts_ctx, 1, buf); + if(r < 0) return r; + ret += r; + } + } else { + l = b->data[bnum_idx(b, b->e - 1)]; + l /= p10s[first_limb_len - 1]; + + buf[0] = '0' + l; + r = pf_puts(puts_ctx, 1, buf); + if(r < 0) return r; + ret += r; + + if(flags->Precision || flags->Alternate) { + buf[0] = *(locale ? locale->locinfo : get_locinfo())->lconv->decimal_point; + r = pf_puts(puts_ctx, 1, buf); + if(r < 0) return r; + ret += r; + } + + prec = flags->Precision; + limb_len = LIMB_DIGITS; + for(i=b->e-1; prec>0 && i>=b->b; i--) { + l = b->data[bnum_idx(b, i)]; + if(i == b->e-1) { + limb_len = first_limb_len - 1; + l %= p10s[limb_len]; + } + + if(limb_len > prec) { + f.Precision = prec; + l /= p10s[limb_len - prec]; + } else { + f.Precision = limb_len; + limb_len = LIMB_DIGITS; + } + prec -= f.Precision; + FUNC_NAME(pf_integer_conv)(buf, &f, l); + + r = pf_puts(puts_ctx, f.Precision, buf); + if(r < 0) return r; + ret += r; + } + + buf[0] = '0'; + for(; prec>0; prec--) { + r = pf_puts(puts_ctx, 1, buf); + if(r < 0) return r; + ret += r; + } + + if(!trim_tail || radix_pos) { + buf[0] = flags->Format; + buf[1] = radix_pos < 0 ? '-' : '+'; + r = pf_puts(puts_ctx, 2, buf); + if(r < 0) return r; + ret += r; + + f.Precision = three_digit_exp ? 3 : 2; + FUNC_NAME(pf_integer_conv)(buf, &f, radix_pos); + r = pf_puts(puts_ctx, f.Precision, buf); + if(r < 0) return r; + ret += r; + } + } + + r = FUNC_NAME(pf_fill)(pf_puts, puts_ctx, len, flags, FALSE); + if(r < 0) return r; + ret += r; + return ret; +} + +int FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk) pf_puts, void *puts_ctx, const APICHAR *fmt, + _locale_t locale, DWORD options, + args_clbk pf_args, void *args_ctx, va_list *valist) +{ + const APICHAR *q, *p = fmt; + APICHAR buf[32]; + int written = 0, pos, i; + pf_flags flags; + BOOL positional_params = options & MSVCRT_PRINTF_POSITIONAL_PARAMS; + BOOL invoke_invalid_param_handler = options & MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER; +#if _MSVCR_VER >= 140 + BOOL legacy_wide = options & _CRT_INTERNAL_PRINTF_LEGACY_WIDE_SPECIFIERS; + BOOL legacy_msvcrt_compat = options & _CRT_INTERNAL_PRINTF_LEGACY_MSVCRT_COMPATIBILITY; + BOOL three_digit_exp = options & _CRT_INTERNAL_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS; + BOOL standard_rounding = options & _CRT_INTERNAL_PRINTF_STANDARD_ROUNDING; +#else + BOOL legacy_wide = TRUE, legacy_msvcrt_compat = TRUE; + BOOL three_digit_exp = _get_output_format() != _TWO_DIGIT_EXPONENT; + BOOL standard_rounding = FALSE; +#endif + + if (!MSVCRT_CHECK_PMT(fmt != NULL)) + return -1; + + while(*p) { + /* output characters before '%' */ + for(q=p; *q && *q!='%'; q++); + if(p != q) { + i = pf_puts(puts_ctx, q-p, p); + if(i < 0) + return i; + + written += i; + p = q; + continue; + } + + /* *p == '%' here */ + p++; + + /* output a single '%' character */ + if(*p == '%') { + i = pf_puts(puts_ctx, 1, p++); + if(i < 0) + return i; + + written += i; + continue; + } + + /* check parameter position */ + if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &pos)) && *q=='$') + p = q+1; + else + pos = -1; + + /* parse the flags */ + memset(&flags, 0, sizeof(flags)); + while(*p) { + if(*p=='+' || *p==' ') { + if(flags.Sign != '+') + flags.Sign = *p; + } else if(*p == '-') + flags.LeftAlign = TRUE; + else if(*p == '0') + flags.PadZero = TRUE; + else if(*p == '#') + flags.Alternate = TRUE; + else + break; + + p++; + } + + /* parse the width */ + if(*p == '*') { + p++; + if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &i)) && *q=='$') + p = q+1; + else + i = -1; + + flags.FieldLength = pf_args(args_ctx, i, VT_INT, valist).get_int; + if(flags.FieldLength < 0) { + flags.LeftAlign = TRUE; + flags.FieldLength = -flags.FieldLength; + } + } + +#if _MSVCR_VER >= 140 + if (*p >= '0' && *p <= '9') + flags.FieldLength = 0; +#endif + + while (*p >= '0' && *p <= '9') { + flags.FieldLength *= 10; + flags.FieldLength += *p++ - '0'; + } + + /* parse the precision */ + flags.Precision = -1; + if(*p == '.') { + flags.Precision = 0; + p++; + if(*p == '*') { + p++; + if(positional_params && (q = FUNC_NAME(pf_parse_int)(p, &i)) && *q=='$') + p = q+1; + else + i = -1; + + flags.Precision = pf_args(args_ctx, i, VT_INT, valist).get_int; + } else while (*p >= '0' && *p <= '9') { + flags.Precision *= 10; + flags.Precision += *p++ - '0'; + } + } + + /* parse argument size modifier */ + while(*p) { + if(*p=='l' && *(p+1)=='l') { + flags.IntegerDouble = TRUE; + p++; + } else if(*p=='l') { + flags.IntegerLength = LEN_LONG; + } else if(*p == 'h') { + flags.IntegerLength = LEN_SHORT; + } else if(*p == 'I') { + if(*(p+1)=='6' && *(p+2)=='4') { + flags.IntegerDouble = TRUE; + p += 2; + } else if(*(p+1)=='3' && *(p+2)=='2') + p += 2; + else if(p[1] && strchr("diouxX", p[1])) + flags.IntegerNative = TRUE; + else + break; + } else if(*p == 'w') + flags.WideString = TRUE; +#if _MSVCR_VER == 0 || _MSVCR_VER >= 140 + else if((*p == 'z' || *p == 't') && p[1] && strchr("diouxX", p[1])) + flags.IntegerNative = TRUE; + else if(*p == 'j') + flags.IntegerDouble = TRUE; +#endif +#if _MSVCR_VER >= 140 + else if(*p == 'T') + flags.NaturalString = TRUE; +#endif + else if(*p != 'L' && ((*p != 'F' && *p != 'N') || !legacy_msvcrt_compat)) + break; + p++; + } + + flags.Format = *p; + + if(flags.Format == 's' || flags.Format == 'S') { + i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx, + pf_args(args_ctx, pos, VT_PTR, valist).get_ptr, + -1, &flags, locale, legacy_wide); + } else if(flags.Format == 'c' || flags.Format == 'C') { + int ch = pf_args(args_ctx, pos, VT_INT, valist).get_int; + + i = FUNC_NAME(pf_handle_string)(pf_puts, puts_ctx, &ch, 1, &flags, locale, legacy_wide); + if(i < 0) i = 0; /* ignore conversion error */ + } else if(flags.Format == 'p') { + flags.Format = 'X'; + flags.PadZero = TRUE; + i = flags.Precision; + flags.Precision = 2*sizeof(void*); + FUNC_NAME(pf_integer_conv)(buf, &flags, + (ULONG_PTR)pf_args(args_ctx, pos, VT_PTR, valist).get_ptr); + flags.PadZero = FALSE; + flags.Precision = i; + +#ifdef PRINTF_WIDE + i = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, buf, -1, &flags, locale); +#else + i = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, buf, -1, &flags, locale); +#endif + } else if(flags.Format == 'n') { + int *used; + + if(!n_format_enabled) { + MSVCRT_INVALID_PMT("\'n\' format specifier disabled", EINVAL); + return -1; + } + + used = pf_args(args_ctx, pos, VT_PTR, valist).get_ptr; + *used = written; + i = 0; + } else if(flags.Format && strchr("diouxX", flags.Format)) { + APICHAR *tmp = buf; + int max_len; + + /* 0 padding is added after '0x' if Alternate flag is in use */ + if((flags.Format=='x' || flags.Format=='X') && flags.PadZero && flags.Alternate + && !flags.LeftAlign && flags.Precisionflags.Precision ? flags.FieldLength : flags.Precision) + 10; + if(max_len > ARRAY_SIZE(buf)) + tmp = HeapAlloc(GetProcessHeap(), 0, max_len); + if(!tmp) + return -1; + + if(flags.IntegerDouble || (flags.IntegerNative && sizeof(void*) == 8)) + FUNC_NAME(pf_integer_conv)(tmp, &flags, pf_args(args_ctx, pos, + VT_I8, valist).get_longlong); + else if(flags.Format=='d' || flags.Format=='i') + FUNC_NAME(pf_integer_conv)(tmp, &flags, + flags.IntegerLength != LEN_SHORT ? + pf_args(args_ctx, pos, VT_INT, valist).get_int : + (short)pf_args(args_ctx, pos, VT_INT, valist).get_int); + else + FUNC_NAME(pf_integer_conv)(tmp, &flags, + flags.IntegerLength != LEN_SHORT ? + (unsigned)pf_args(args_ctx, pos, VT_INT, valist).get_int : + (unsigned short)pf_args(args_ctx, pos, VT_INT, valist).get_int); + +#ifdef PRINTF_WIDE + i = FUNC_NAME(pf_output_format_wstr)(pf_puts, puts_ctx, tmp, -1, &flags, locale); +#else + i = FUNC_NAME(pf_output_format_str)(pf_puts, puts_ctx, tmp, -1, &flags, locale); +#endif + if(tmp != buf) + HeapFree(GetProcessHeap(), 0, tmp); + } else if(flags.Format && strchr("aAeEfFgG", flags.Format)) { + double val = pf_args(args_ctx, pos, VT_R8, valist).get_double; + + if(signbit(val)) { + flags.Sign = '-'; + val = -val; + } + + if(isinf(val) || isnan(val)) + i = FUNC_NAME(pf_output_special_fp)(pf_puts, puts_ctx, val, &flags, + locale, legacy_msvcrt_compat, three_digit_exp); + else if(flags.Format=='a' || flags.Format=='A') + i = FUNC_NAME(pf_output_hex_fp)(pf_puts, puts_ctx, val, &flags, + locale, standard_rounding); + else + i = FUNC_NAME(pf_output_fp)(pf_puts, puts_ctx, val, &flags, + locale, three_digit_exp, standard_rounding); + } else { + if(invoke_invalid_param_handler) { + _invalid_parameter(NULL, NULL, NULL, 0, 0); + *_errno() = EINVAL; + return -1; + } + + continue; + } + + if(i < 0) + return i; + written += i; + p++; + } + + return written; +} + +#ifndef PRINTF_WIDE +enum types_clbk_flags { + TYPE_CLBK_VA_LIST = 1, + TYPE_CLBK_POSITIONAL = 2, + TYPE_CLBK_ERROR_POS = 4, + TYPE_CLBK_ERROR_TYPE = 8 +}; + +/* This functions stores types of arguments. It uses args[0] internally */ +static printf_arg arg_clbk_type(void *ctx, int pos, int type, va_list *valist) +{ + static const printf_arg ret; + printf_arg *args = ctx; + + if(pos == -1) { + args[0].get_int |= TYPE_CLBK_VA_LIST; + return ret; + } else + args[0].get_int |= TYPE_CLBK_POSITIONAL; + + if(pos<1 || pos>_ARGMAX) + args[0].get_int |= TYPE_CLBK_ERROR_POS; + else if(args[pos].get_int && args[pos].get_int!=type) + args[0].get_int |= TYPE_CLBK_ERROR_TYPE; + else + args[pos].get_int = type; + + return ret; +} +#endif + +int FUNC_NAME(create_positional_ctx)(void *args_ctx, const APICHAR *format, va_list valist) +{ + struct FUNC_NAME(_str_ctx) puts_ctx = {INT_MAX, NULL}; + printf_arg *args = args_ctx; + int i, j; + + i = FUNC_NAME(pf_printf)(FUNC_NAME(puts_clbk_str), &puts_ctx, format, NULL, + MSVCRT_PRINTF_POSITIONAL_PARAMS, arg_clbk_type, args_ctx, NULL); + if(i < 0) + return i; + + if(args[0].get_int==0 || args[0].get_int==TYPE_CLBK_VA_LIST) + return 0; + if(args[0].get_int != TYPE_CLBK_POSITIONAL) + return -1; + + for(i=_ARGMAX; i>0; i--) + if(args[i].get_int) + break; + + for(j=1; j<=i; j++) { + switch(args[j].get_int) { + case VT_I8: + args[j].get_longlong = va_arg(valist, LONGLONG); + break; + case VT_INT: + args[j].get_int = va_arg(valist, int); + break; + case VT_R8: + args[j].get_double = va_arg(valist, double); + break; + case VT_PTR: + args[j].get_ptr = va_arg(valist, void*); + break; + default: + return -1; + } + } + + return j; +} + +#undef APICHAR +#undef CONVCHAR +#undef FUNC_NAME diff --git a/dll/win32/msvcrt/process.c b/dll/win32/msvcrt/process.c new file mode 100644 index 00000000000..f50fd7a213f --- /dev/null +++ b/dll/win32/msvcrt/process.c @@ -0,0 +1,1369 @@ +/* + * msvcrt.dll spawn/exec functions + * + * Copyright 1996,1998 Marcus Meissner + * Copyright 1996 Jukka Iivonen + * Copyright 1997,2000 Uwe Bonnes + * Copyright 2000 Jon Griffiths + * Copyright 2007 Hans Leidekker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + * + * FIXME: + * -File handles need some special handling. Sometimes children get + * open file handles, sometimes not. The docs are confusing + * -No check for maximum path/argument/environment size is done + */ + +#include +#include +#include +#include + +#include "msvcrt.h" +#include +#include "mtdll.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +static void msvcrt_search_executable(const wchar_t *name, wchar_t *fullname, int use_path) +{ + static const wchar_t suffix[][5] = + {L".com", L".exe", L".bat", L".cmd"}; + + wchar_t buffer[MAX_PATH]; + const wchar_t *env, *p, *end; + unsigned int i, name_len, path_len; + int extension = 1; + + *fullname = '\0'; + msvcrt_set_errno(ERROR_FILE_NOT_FOUND); + + end = name + MAX_PATH - 1; + for(p = name; p < end; p++) + if(!*p) break; + name_len = p - name; + + /* FIXME extra-long names are silently truncated */ + memcpy(buffer, name, name_len * sizeof(wchar_t)); + buffer[name_len] = '\0'; + + /* try current dir first */ + if (GetFileAttributesW(buffer) != INVALID_FILE_ATTRIBUTES) + { + wcscpy(fullname, buffer); + return; + } + + for (p--; p >= name; p--) + if (*p == '\\' || *p == '/' || *p == ':' || *p == '.') break; + + /* if there's no extension, try some well-known extensions */ + if ((p < name || *p != '.') && name_len <= MAX_PATH - 5) + { + for (i = 0; i < 4; i++) + { + memcpy(buffer + name_len, suffix[i], 5 * sizeof(wchar_t)); + if (GetFileAttributesW(buffer) != INVALID_FILE_ATTRIBUTES) + { + wcscpy(fullname, buffer); + return; + } + } + extension = 0; + } + + if (!use_path || !(env = _wgetenv(L"PATH"))) return; + + /* now try search path */ + do + { + p = env; + while (*p && *p != ';') p++; + if (p == env) return; + + path_len = p - env; + if (path_len + name_len <= MAX_PATH - 2) + { + memcpy(buffer, env, path_len * sizeof(wchar_t)); + if (buffer[path_len] != '/' && buffer[path_len] != '\\') + { + buffer[path_len++] = '\\'; + buffer[path_len] = '\0'; + } + else buffer[path_len] = '\0'; + + wcscat(buffer, name); + if (GetFileAttributesW(buffer) != INVALID_FILE_ATTRIBUTES) + { + wcscpy(fullname, buffer); + return; + } + } + /* again, if there's no extension, try some well-known extensions */ + if (!extension && path_len + name_len <= MAX_PATH - 5) + { + for (i = 0; i < 4; i++) + { + memcpy(buffer + path_len + name_len, suffix[i], 5 * sizeof(wchar_t)); + if (GetFileAttributesW(buffer) != INVALID_FILE_ATTRIBUTES) + { + wcscpy(fullname, buffer); + return; + } + } + } + env = *p ? p + 1 : p; + } while(1); +} + +static intptr_t msvcrt_spawn(int flags, const wchar_t* exe, wchar_t* cmdline, + wchar_t* env, int use_path) +{ + STARTUPINFOW si; + PROCESS_INFORMATION pi; + wchar_t fullname[MAX_PATH]; + DWORD create_flags = CREATE_UNICODE_ENVIRONMENT; + + TRACE("%x %s %s %s %d\n", flags, debugstr_w(exe), debugstr_w(cmdline), debugstr_w(env), use_path); + + if ((unsigned)flags > _P_DETACH) + { + *_errno() = EINVAL; + return -1; + } + + msvcrt_search_executable(exe, fullname, use_path); + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + msvcrt_create_io_inherit_block(&si.cbReserved2, &si.lpReserved2); + if (flags == _P_DETACH) create_flags |= DETACHED_PROCESS; + if (!CreateProcessW(fullname, cmdline, NULL, NULL, TRUE, + create_flags, env, NULL, &si, &pi)) + { + msvcrt_set_errno(GetLastError()); + free(si.lpReserved2); + return -1; + } + + free(si.lpReserved2); + switch(flags) + { + case _P_WAIT: + WaitForSingleObject(pi.hProcess, INFINITE); + GetExitCodeProcess(pi.hProcess,&pi.dwProcessId); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return pi.dwProcessId; + case _P_DETACH: + CloseHandle(pi.hProcess); + pi.hProcess = 0; + /* fall through */ + case _P_NOWAIT: + case _P_NOWAITO: + CloseHandle(pi.hThread); + return (intptr_t)pi.hProcess; + case _P_OVERLAY: + _exit(0); + } + return -1; /* can't reach here */ +} + +/* INTERNAL: Convert wide argv list to a single 'delim'-separated wide string, with an + * extra '\0' to terminate it. + */ +static wchar_t* msvcrt_argvtos(const wchar_t* const* arg, wchar_t delim) +{ + const wchar_t* const* a; + int size; + wchar_t* p; + wchar_t* ret; + + if (!arg) + { + /* Return NULL for an empty environment list */ + return NULL; + } + + /* get length */ + a = arg; + size = 0; + while (*a) + { + size += wcslen(*a) + 1; + a++; + } + + ret = malloc((size + 1) * sizeof(wchar_t)); + if (!ret) + return NULL; + + /* fill string */ + a = arg; + p = ret; + while (*a) + { + int len = wcslen(*a); + memcpy(p,*a,len * sizeof(wchar_t)); + p += len; + *p++ = delim; + a++; + } + if (delim && p > ret) p[-1] = 0; + else *p = 0; + return ret; +} + +/* INTERNAL: Convert ansi argv list to a single wide string, use ' ' or '\0' + * as separator depending on env argument. + */ +static wchar_t *msvcrt_argvtos_aw(const char * const *arg, BOOL env) +{ + UINT cp = env ? CP_ACP : get_aw_cp(); + const char * const *a; + unsigned int len; + wchar_t *p, *ret; + + if (!arg) + { + /* Return NULL for an empty environment list */ + return NULL; + } + + /* get length */ + a = arg; + len = 0; + while (*a) + { + len += MultiByteToWideChar(cp, 0, *a, -1, NULL, 0); + a++; + } + + ret = malloc((len + 1) * sizeof(wchar_t)); + if (!ret) + return NULL; + + /* fill string */ + a = arg; + p = ret; + while (*a) + { + p += MultiByteToWideChar(cp, 0, *a, strlen(*a), p, len - (p - ret)); + *p++ = env ? 0 : ' '; + a++; + } + if (!env && p > ret) p[-1] = 0; + else *p = 0; + return ret; +} + +/* INTERNAL: Convert wide va_list to a single 'delim'-separated wide string, with an + * extra '\0' to terminate it. + */ +static wchar_t *msvcrt_valisttos(const wchar_t *arg0, va_list alist, wchar_t delim) +{ + unsigned int size = 0, pos = 0; + const wchar_t *arg; + wchar_t *new, *ret = NULL; + + for (arg = arg0; arg; arg = va_arg( alist, wchar_t * )) + { + unsigned int len = wcslen( arg ) + 1; + if (pos + len >= size) + { + size = max( 256, size * 2 ); + size = max( size, pos + len + 1 ); + if (!(new = realloc( ret, size * sizeof(wchar_t) ))) + { + free( ret ); + return NULL; + } + ret = new; + } + wcscpy( ret + pos, arg ); + pos += len; + ret[pos - 1] = delim; + } + if (pos) + { + if (delim) ret[pos - 1] = 0; + else ret[pos] = 0; + } + return ret; +} + +/* INTERNAL: Convert ansi va_list to a single 'delim'-separated wide string, with an + * extra '\0' to terminate it. + */ +static wchar_t *msvcrt_valisttos_aw(const char *arg0, va_list alist, wchar_t delim) +{ + unsigned int size = 0, pos = 0; + const char *arg; + wchar_t *new, *ret = NULL; + + for (arg = arg0; arg; arg = va_arg( alist, char * )) + { + unsigned int len = convert_acp_utf8_to_wcs( arg, NULL, 0 ); + if (pos + len >= size) + { + size = max( 256, size * 2 ); + size = max( size, pos + len + 1 ); + if (!(new = realloc( ret, size * sizeof(wchar_t) ))) + { + free( ret ); + return NULL; + } + ret = new; + } + pos += convert_acp_utf8_to_wcs( arg, ret + pos, size - pos ); + ret[pos - 1] = delim; + } + if (pos) + { + if (delim) ret[pos - 1] = 0; + else ret[pos] = 0; + } + return ret; +} + +/* INTERNAL: retrieve COMSPEC environment variable */ +static wchar_t *msvcrt_get_comspec(void) +{ + wchar_t *ret; + unsigned int len; + + if (!(len = GetEnvironmentVariableW(L"COMSPEC", NULL, 0))) len = 4; + if ((ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(wchar_t)))) + { + if (!GetEnvironmentVariableW(L"COMSPEC", ret, len)) wcscpy(ret, L"cmd"); + } + return ret; +} + +/********************************************************************* + * _cwait (MSVCRT.@) + */ +intptr_t CDECL _cwait(int *status, intptr_t pid, int action) +{ + HANDLE hPid = (HANDLE)pid; + int doserrno; + + if (!WaitForSingleObject(hPid, INFINITE)) + { + if (status) + { + DWORD stat; + GetExitCodeProcess(hPid, &stat); + *status = (int)stat; + } + return pid; + } + doserrno = GetLastError(); + + if (doserrno == ERROR_INVALID_HANDLE) + { + *_errno() = ECHILD; + *__doserrno() = doserrno; + } + else + msvcrt_set_errno(doserrno); + + return status ? *status = -1 : -1; +} + +/********************************************************************* + * _wexecl (MSVCRT.@) + * + * Unicode version of _execl + */ +intptr_t WINAPIV _wexecl(const wchar_t* name, const wchar_t* arg0, ...) +{ + va_list ap; + wchar_t *args; + intptr_t ret; + + va_start(ap, arg0); + args = msvcrt_valisttos(arg0, ap, ' '); + va_end(ap); + + ret = msvcrt_spawn(_P_OVERLAY, name, args, NULL, 0); + + free(args); + return ret; +} + +/********************************************************************* + * _execl (MSVCRT.@) + * + * Like on Windows, this function does not handle arguments with spaces + * or double-quotes. + */ +intptr_t WINAPIV _execl(const char* name, const char* arg0, ...) +{ + va_list ap; + wchar_t *nameW, *args; + intptr_t ret; + + if (!(nameW = wstrdupa_utf8(name))) return -1; + + va_start(ap, arg0); + args = msvcrt_valisttos_aw(arg0, ap, ' '); + va_end(ap); + + ret = msvcrt_spawn(_P_OVERLAY, nameW, args, NULL, 0); + + free(nameW); + free(args); + return ret; +} + +/********************************************************************* + * _wexecle (MSVCRT.@) + * + * Unicode version of _execle + */ +intptr_t WINAPIV _wexecle(const wchar_t* name, const wchar_t* arg0, ...) +{ + va_list ap; + wchar_t *args, *envs = NULL; + const wchar_t * const *envp; + intptr_t ret; + + va_start(ap, arg0); + args = msvcrt_valisttos(arg0, ap, ' '); + va_end(ap); + + va_start(ap, arg0); + while (va_arg( ap, wchar_t * ) != NULL) /*nothing*/; + envp = va_arg( ap, const wchar_t * const * ); + if (envp) envs = msvcrt_argvtos(envp, 0); + va_end(ap); + + ret = msvcrt_spawn(_P_OVERLAY, name, args, envs, 0); + + free(args); + free(envs); + return ret; +} + +/********************************************************************* + * _execle (MSVCRT.@) + */ +intptr_t WINAPIV _execle(const char* name, const char* arg0, ...) +{ + va_list ap; + wchar_t *nameW, *args, *envs = NULL; + const char * const *envp; + intptr_t ret; + + if (!(nameW = wstrdupa_utf8(name))) return -1; + + va_start(ap, arg0); + args = msvcrt_valisttos_aw(arg0, ap, ' '); + va_end(ap); + + va_start(ap, arg0); + while (va_arg( ap, char * ) != NULL) /*nothing*/; + envp = va_arg( ap, const char * const * ); + if (envp) envs = msvcrt_argvtos_aw(envp, TRUE); + va_end(ap); + + ret = msvcrt_spawn(_P_OVERLAY, nameW, args, envs, 0); + + free(nameW); + free(args); + free(envs); + return ret; +} + +/********************************************************************* + * _wexeclp (MSVCRT.@) + * + * Unicode version of _execlp + */ +intptr_t WINAPIV _wexeclp(const wchar_t* name, const wchar_t* arg0, ...) +{ + va_list ap; + wchar_t *args; + intptr_t ret; + + va_start(ap, arg0); + args = msvcrt_valisttos(arg0, ap, ' '); + va_end(ap); + + ret = msvcrt_spawn(_P_OVERLAY, name, args, NULL, 1); + + free(args); + return ret; +} + +/********************************************************************* + * _execlp (MSVCRT.@) + * + * Like on Windows, this function does not handle arguments with spaces + * or double-quotes. + */ +intptr_t WINAPIV _execlp(const char* name, const char* arg0, ...) +{ + va_list ap; + wchar_t *nameW, *args; + intptr_t ret; + + if (!(nameW = wstrdupa_utf8(name))) return -1; + + va_start(ap, arg0); + args = msvcrt_valisttos_aw(arg0, ap, ' '); + va_end(ap); + + ret = msvcrt_spawn(_P_OVERLAY, nameW, args, NULL, 1); + + free(nameW); + free(args); + return ret; +} + +/********************************************************************* + * _wexeclpe (MSVCRT.@) + * + * Unicode version of _execlpe + */ +intptr_t WINAPIV _wexeclpe(const wchar_t* name, const wchar_t* arg0, ...) +{ + va_list ap; + wchar_t *args, *envs = NULL; + const wchar_t * const *envp; + intptr_t ret; + + va_start(ap, arg0); + args = msvcrt_valisttos(arg0, ap, ' '); + va_end(ap); + + va_start(ap, arg0); + while (va_arg( ap, wchar_t * ) != NULL) /*nothing*/; + envp = va_arg( ap, const wchar_t * const * ); + if (envp) envs = msvcrt_argvtos(envp, 0); + va_end(ap); + + ret = msvcrt_spawn(_P_OVERLAY, name, args, envs, 1); + + free(args); + free(envs); + return ret; +} + +/********************************************************************* + * _execlpe (MSVCRT.@) + */ +intptr_t WINAPIV _execlpe(const char* name, const char* arg0, ...) +{ + va_list ap; + wchar_t *nameW, *args, *envs = NULL; + const char * const *envp; + intptr_t ret; + + if (!(nameW = wstrdupa_utf8(name))) return -1; + + va_start(ap, arg0); + args = msvcrt_valisttos_aw(arg0, ap, ' '); + va_end(ap); + + va_start(ap, arg0); + while (va_arg( ap, char * ) != NULL) /*nothing*/; + envp = va_arg( ap, const char * const * ); + if (envp) envs = msvcrt_argvtos_aw(envp, TRUE); + va_end(ap); + + ret = msvcrt_spawn(_P_OVERLAY, nameW, args, envs, 1); + + free(nameW); + free(args); + free(envs); + return ret; +} + +/********************************************************************* + * _wexecv (MSVCRT.@) + * + * Unicode version of _execv + */ +intptr_t CDECL _wexecv(const wchar_t* name, const wchar_t* const* argv) +{ + return _wspawnve(_P_OVERLAY, name, argv, NULL); +} + +/********************************************************************* + * _execv (MSVCRT.@) + * + * Like on Windows, this function does not handle arguments with spaces + * or double-quotes. + */ +intptr_t CDECL _execv(const char* name, const char* const* argv) +{ + return _spawnve(_P_OVERLAY, name, argv, NULL); +} + +/********************************************************************* + * _wexecve (MSVCRT.@) + * + * Unicode version of _execve + */ +intptr_t CDECL _wexecve(const wchar_t* name, const wchar_t* const* argv, const wchar_t* const* envv) +{ + return _wspawnve(_P_OVERLAY, name, argv, envv); +} + +/********************************************************************* + * _execve (MSVCRT.@) + * + * Like on Windows, this function does not handle arguments with spaces + * or double-quotes. + */ +intptr_t CDECL _execve(const char* name, const char* const* argv, const char* const* envv) +{ + return _spawnve(_P_OVERLAY, name, argv, envv); +} + +/********************************************************************* + * _wexecvpe (MSVCRT.@) + * + * Unicode version of _execvpe + */ +intptr_t CDECL _wexecvpe(const wchar_t* name, const wchar_t* const* argv, const wchar_t* const* envv) +{ + return _wspawnvpe(_P_OVERLAY, name, argv, envv); +} + +/********************************************************************* + * _execvpe (MSVCRT.@) + * + * Like on Windows, this function does not handle arguments with spaces + * or double-quotes. + */ +intptr_t CDECL _execvpe(const char* name, const char* const* argv, const char* const* envv) +{ + return _spawnvpe(_P_OVERLAY, name, argv, envv); +} + +/********************************************************************* + * _wexecvp (MSVCRT.@) + * + * Unicode version of _execvp + */ +intptr_t CDECL _wexecvp(const wchar_t* name, const wchar_t* const* argv) +{ + return _wexecvpe(name, argv, NULL); +} + +/********************************************************************* + * _execvp (MSVCRT.@) + * + * Like on Windows, this function does not handle arguments with spaces + * or double-quotes. + */ +intptr_t CDECL _execvp(const char* name, const char* const* argv) +{ + return _execvpe(name, argv, NULL); +} + +/********************************************************************* + * _wspawnl (MSVCRT.@) + * + * Unicode version of _spawnl + */ +intptr_t WINAPIV _wspawnl(int flags, const wchar_t* name, const wchar_t* arg0, ...) +{ + va_list ap; + wchar_t *args; + intptr_t ret; + + va_start(ap, arg0); + args = msvcrt_valisttos(arg0, ap, ' '); + va_end(ap); + + ret = msvcrt_spawn(flags, name, args, NULL, 0); + + free(args); + return ret; +} + +/********************************************************************* + * _spawnl (MSVCRT.@) + * + * Like on Windows, this function does not handle arguments with spaces + * or double-quotes. + */ +intptr_t WINAPIV _spawnl(int flags, const char* name, const char* arg0, ...) +{ + va_list ap; + wchar_t *nameW, *args; + intptr_t ret; + + if (!(nameW = wstrdupa_utf8(name))) return -1; + + va_start(ap, arg0); + args = msvcrt_valisttos_aw(arg0, ap, ' '); + va_end(ap); + + ret = msvcrt_spawn(flags, nameW, args, NULL, 0); + + free(nameW); + free(args); + return ret; +} + +/********************************************************************* + * _wspawnle (MSVCRT.@) + * + * Unicode version of _spawnle + */ +intptr_t WINAPIV _wspawnle(int flags, const wchar_t* name, const wchar_t* arg0, ...) +{ + va_list ap; + wchar_t *args, *envs = NULL; + const wchar_t * const *envp; + intptr_t ret; + + va_start(ap, arg0); + args = msvcrt_valisttos(arg0, ap, ' '); + va_end(ap); + + va_start(ap, arg0); + while (va_arg( ap, wchar_t * ) != NULL) /*nothing*/; + envp = va_arg( ap, const wchar_t * const * ); + if (envp) envs = msvcrt_argvtos(envp, 0); + va_end(ap); + + ret = msvcrt_spawn(flags, name, args, envs, 0); + + free(args); + free(envs); + return ret; +} + +/********************************************************************* + * _spawnle (MSVCRT.@) + */ +intptr_t WINAPIV _spawnle(int flags, const char* name, const char* arg0, ...) +{ + va_list ap; + wchar_t *nameW, *args, *envs = NULL; + const char * const *envp; + intptr_t ret; + + if (!(nameW = wstrdupa_utf8(name))) return -1; + + va_start(ap, arg0); + args = msvcrt_valisttos_aw(arg0, ap, ' '); + va_end(ap); + + va_start(ap, arg0); + while (va_arg( ap, char * ) != NULL) /*nothing*/; + envp = va_arg( ap, const char * const * ); + if (envp) envs = msvcrt_argvtos_aw(envp, TRUE); + va_end(ap); + + ret = msvcrt_spawn(flags, nameW, args, envs, 0); + + free(nameW); + free(args); + free(envs); + return ret; +} + +/********************************************************************* + * _wspawnlp (MSVCRT.@) + * + * Unicode version of _spawnlp + */ +intptr_t WINAPIV _wspawnlp(int flags, const wchar_t* name, const wchar_t* arg0, ...) +{ + va_list ap; + wchar_t *args; + intptr_t ret; + + va_start(ap, arg0); + args = msvcrt_valisttos(arg0, ap, ' '); + va_end(ap); + + ret = msvcrt_spawn(flags, name, args, NULL, 1); + + free(args); + return ret; +} + +/********************************************************************* + * _spawnlp (MSVCRT.@) + * + * Like on Windows, this function does not handle arguments with spaces + * or double-quotes. + */ +intptr_t WINAPIV _spawnlp(int flags, const char* name, const char* arg0, ...) +{ + va_list ap; + wchar_t *nameW, *args; + intptr_t ret; + + if (!(nameW = wstrdupa_utf8(name))) return -1; + + va_start(ap, arg0); + args = msvcrt_valisttos_aw(arg0, ap, ' '); + va_end(ap); + + ret = msvcrt_spawn(flags, nameW, args, NULL, 1); + + free(nameW); + free(args); + return ret; +} + +/********************************************************************* + * _wspawnlpe (MSVCRT.@) + * + * Unicode version of _spawnlpe + */ +intptr_t WINAPIV _wspawnlpe(int flags, const wchar_t* name, const wchar_t* arg0, ...) +{ + va_list ap; + wchar_t *args, *envs = NULL; + const wchar_t * const *envp; + intptr_t ret; + + va_start(ap, arg0); + args = msvcrt_valisttos(arg0, ap, ' '); + va_end(ap); + + va_start(ap, arg0); + while (va_arg( ap, wchar_t * ) != NULL) /*nothing*/; + envp = va_arg( ap, const wchar_t * const * ); + if (envp) envs = msvcrt_argvtos(envp, 0); + va_end(ap); + + ret = msvcrt_spawn(flags, name, args, envs, 1); + + free(args); + free(envs); + return ret; +} + +/********************************************************************* + * _spawnlpe (MSVCRT.@) + */ +intptr_t WINAPIV _spawnlpe(int flags, const char* name, const char* arg0, ...) +{ + va_list ap; + wchar_t *nameW, *args, *envs = NULL; + const char * const *envp; + intptr_t ret; + + if (!(nameW = wstrdupa_utf8(name))) return -1; + + va_start(ap, arg0); + args = msvcrt_valisttos_aw(arg0, ap, ' '); + va_end(ap); + + va_start(ap, arg0); + while (va_arg( ap, char * ) != NULL) /*nothing*/; + envp = va_arg( ap, const char * const * ); + if (envp) envs = msvcrt_argvtos_aw(envp, TRUE); + va_end(ap); + + ret = msvcrt_spawn(flags, nameW, args, envs, 1); + + free(nameW); + free(args); + free(envs); + return ret; +} + +/********************************************************************* + * _spawnve (MSVCRT.@) + * + * Like on Windows, this function does not handle arguments with spaces + * or double-quotes. + */ +intptr_t CDECL _spawnve(int flags, const char* name, const char* const* argv, + const char* const* envv) +{ + wchar_t *nameW, *args, *envs; + intptr_t ret; + + if (!(nameW = wstrdupa_utf8(name))) return -1; + + args = msvcrt_argvtos_aw(argv, FALSE); + envs = msvcrt_argvtos_aw(envv, TRUE); + + ret = msvcrt_spawn(flags, nameW, args, envs, 0); + + free(nameW); + free(args); + free(envs); + return ret; +} + +/********************************************************************* + * _wspawnve (MSVCRT.@) + * + * Unicode version of _spawnve + */ +intptr_t CDECL _wspawnve(int flags, const wchar_t* name, const wchar_t* const* argv, + const wchar_t* const* envv) +{ + wchar_t *args, *envs; + intptr_t ret; + + args = msvcrt_argvtos(argv, ' '); + envs = msvcrt_argvtos(envv, 0); + + ret = msvcrt_spawn(flags, name, args, envs, 0); + + free(args); + free(envs); + return ret; +} + +/********************************************************************* + * _spawnv (MSVCRT.@) + * + * Like on Windows, this function does not handle arguments with spaces + * or double-quotes. + */ +intptr_t CDECL _spawnv(int flags, const char* name, const char* const* argv) +{ + return _spawnve(flags, name, argv, NULL); +} + +/********************************************************************* + * _wspawnv (MSVCRT.@) + * + * Unicode version of _spawnv + */ +intptr_t CDECL _wspawnv(int flags, const wchar_t* name, const wchar_t* const* argv) +{ + return _wspawnve(flags, name, argv, NULL); +} + +/********************************************************************* + * _spawnvpe (MSVCRT.@) + * + * Like on Windows, this function does not handle arguments with spaces + * or double-quotes. + */ +intptr_t CDECL _spawnvpe(int flags, const char* name, const char* const* argv, + const char* const* envv) +{ + wchar_t *nameW, *args, *envs; + intptr_t ret; + + if (!(nameW = wstrdupa_utf8(name))) return -1; + + args = msvcrt_argvtos_aw(argv, FALSE); + envs = msvcrt_argvtos_aw(envv, TRUE); + + ret = msvcrt_spawn(flags, nameW, args, envs, 1); + + free(nameW); + free(args); + free(envs); + return ret; +} + +/********************************************************************* + * _wspawnvpe (MSVCRT.@) + * + * Unicode version of _spawnvpe + */ +intptr_t CDECL _wspawnvpe(int flags, const wchar_t* name, const wchar_t* const* argv, + const wchar_t* const* envv) +{ + wchar_t *args, *envs; + intptr_t ret; + + args = msvcrt_argvtos(argv, ' '); + envs = msvcrt_argvtos(envv, 0); + + ret = msvcrt_spawn(flags, name, args, envs, 1); + + free(args); + free(envs); + return ret; +} + +/********************************************************************* + * _spawnvp (MSVCRT.@) + * + * Like on Windows, this function does not handle arguments with spaces + * or double-quotes. + */ +intptr_t CDECL _spawnvp(int flags, const char* name, const char* const* argv) +{ + return _spawnvpe(flags, name, argv, NULL); +} + +/********************************************************************* + * _wspawnvp (MSVCRT.@) + * + * Unicode version of _spawnvp + */ +intptr_t CDECL _wspawnvp(int flags, const wchar_t* name, const wchar_t* const* argv) +{ + return _wspawnvpe(flags, name, argv, NULL); +} + +static struct popen_handle { + FILE *f; + HANDLE proc; +} *popen_handles; +static DWORD popen_handles_size; + +void msvcrt_free_popen_data(void) +{ + free(popen_handles); +} + +/********************************************************************* + * _wpopen (MSVCRT.@) + * + * Unicode version of _popen + */ +FILE* CDECL _wpopen(const wchar_t* command, const wchar_t* mode) +{ + wchar_t *comspec, *fullcmd, fullname[MAX_PATH]; + int textmode, fds[2], fd_parent, fd_child; + struct popen_handle *container; + PROCESS_INFORMATION pi; + BOOL read_pipe = TRUE; + const wchar_t *p; + unsigned int len; + STARTUPINFOW si; + FILE *ret; + DWORD i; + BOOL r; + + TRACE("(command=%s, mode=%s)\n", debugstr_w(command), debugstr_w(mode)); + + if (!command || !mode) + return NULL; + + _get_fmode(&textmode); + textmode &= _O_BINARY | _O_TEXT; + textmode |= _O_NOINHERIT; + for (p = mode; *p; p++) + { + switch (*p) + { + case 'W': + case 'w': + read_pipe = FALSE; + break; + case 'B': + case 'b': + textmode |= _O_BINARY; + textmode &= ~_O_TEXT; + break; + case 'T': + case 't': + textmode |= _O_TEXT; + textmode &= ~_O_BINARY; + break; + } + } + if (_pipe(fds, 0, textmode) == -1) + return NULL; + + if (read_pipe) + { + fd_parent = fds[0]; + fd_child = _dup(fds[1]); + _close(fds[1]); + } + else + { + fd_parent = fds[1]; + fd_child = _dup(fds[0]); + _close(fds[0]); + } + if (fd_child == -1) + { + _close(fd_parent); + return NULL; + } + ret = _wfdopen(fd_parent, mode); + if (!ret) + { + _close(fd_child); + return NULL; + } + + _lock(_POPEN_LOCK); + for (i = 0; i < popen_handles_size; i++) + { + if (!popen_handles[i].f) + break; + } + if (i == popen_handles_size) + { + i = (popen_handles_size ? popen_handles_size * 2 : 8); + container = realloc(popen_handles, i * sizeof(*container)); + if (!container) goto error; + + popen_handles = container; + container = popen_handles + popen_handles_size; + memset(container, 0, (i - popen_handles_size) * sizeof(*container)); + popen_handles_size = i; + } + else container = popen_handles + i; + + if (!(comspec = msvcrt_get_comspec())) goto error; + len = wcslen(comspec) + wcslen(command) + 5; + + if (!(fullcmd = HeapAlloc(GetProcessHeap(), 0, len * sizeof(wchar_t)))) + { + HeapFree(GetProcessHeap(), 0, comspec); + goto error; + } + + wcscpy(fullcmd, comspec); + wcscat(fullcmd, L" /c "); + wcscat(fullcmd, command); + msvcrt_search_executable(comspec, fullname, 1); + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES; + if (read_pipe) + { + si.hStdInput = (HANDLE)_get_osfhandle(STDIN_FILENO); + si.hStdOutput = (HANDLE)_get_osfhandle(fd_child); + } + else + { + si.hStdInput = (HANDLE)_get_osfhandle(fd_child); + si.hStdOutput = (HANDLE)_get_osfhandle(STDOUT_FILENO); + } + si.hStdError = (HANDLE)_get_osfhandle(STDERR_FILENO); + r = CreateProcessW(fullname, fullcmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); + HeapFree(GetProcessHeap(), 0, comspec); + HeapFree(GetProcessHeap(), 0, fullcmd); + if (!r) + { + msvcrt_set_errno(GetLastError()); + goto error; + } + CloseHandle(pi.hThread); + _close(fd_child); + container->proc = pi.hProcess; + container->f = ret; + _unlock(_POPEN_LOCK); + return ret; + +error: + _unlock(_POPEN_LOCK); + _close(fd_child); + fclose(ret); + return NULL; +} + +/********************************************************************* + * _popen (MSVCRT.@) + */ +FILE* CDECL _popen(const char* command, const char* mode) +{ + FILE *ret; + wchar_t *cmdW, *modeW; + + TRACE("(command=%s, mode=%s)\n", debugstr_a(command), debugstr_a(mode)); + + if (!command || !mode) + return NULL; + + if (!(cmdW = wstrdupa_utf8(command))) return NULL; + if (!(modeW = msvcrt_wstrdupa(mode))) + { + free(cmdW); + return NULL; + } + + ret = _wpopen(cmdW, modeW); + + free(cmdW); + free(modeW); + return ret; +} + +/********************************************************************* + * _pclose (MSVCRT.@) + */ +int CDECL _pclose(FILE* file) +{ + HANDLE h; + DWORD i; + + if (!MSVCRT_CHECK_PMT(file != NULL)) return -1; + + _lock(_POPEN_LOCK); + for(i=0; i=110 +/********************************************************************* + * __crtTerminateProcess (MSVCR110.@) + */ +int CDECL __crtTerminateProcess(UINT exit_code) +{ + return TerminateProcess(GetCurrentProcess(), exit_code); +} +#endif diff --git a/dll/win32/msvcrt/rsrc.rc b/dll/win32/msvcrt/rsrc.rc new file mode 100644 index 00000000000..8fa3de90f19 --- /dev/null +++ b/dll/win32/msvcrt/rsrc.rc @@ -0,0 +1,30 @@ +/* + * Top level resource file for msvcrt.dll + * + * Copyright 2005 Marcus Meissner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include "winresrc.h" + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +#define WINE_FILEDESCRIPTION_STR "Wine CRT library" +#define WINE_FILENAME_STR "msvcrt.dll" +#define WINE_FILEVERSION 7,0,2600,2180 +#define WINE_FILEVERSION_STR "7.0.2600" + +#include "wine/wine_common_ver.rc" diff --git a/dll/win32/msvcrt/scanf.c b/dll/win32/msvcrt/scanf.c new file mode 100644 index 00000000000..c77649797ed --- /dev/null +++ b/dll/win32/msvcrt/scanf.c @@ -0,0 +1,820 @@ +/* + * general implementation of scanf used by scanf, sscanf, fscanf, + * _cscanf, wscanf, swscanf and fwscanf + * + * Copyright 1996,1998 Marcus Meissner + * Copyright 1996 Jukka Iivonen + * Copyright 1997,2000 Uwe Bonnes + * Copyright 2000 Jon Griffiths + * Copyright 2002 Daniel Gudbjartsson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "msvcrt.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +/* helper function for *scanf. Returns the value of character c in the + * given base, or -1 if the given character is not a digit of the base. + */ +static int char2digit(char c, int base) { + if ((c>='0') && (c<='9') && (c<='0'+base-1)) return (c-'0'); + if (base<=10) return -1; + if ((c>='A') && (c<='Z') && (c<='A'+base-11)) return (c-'A'+10); + if ((c>='a') && (c<='z') && (c<='a'+base-11)) return (c-'a'+10); + return -1; +} + +/* helper function for *wscanf. Returns the value of character c in the + * given base, or -1 if the given character is not a digit of the base. + */ +static int wchar2digit(wchar_t c, int base) { + if ((c>='0') && (c<='9') && (c<='0'+base-1)) return (c-'0'); + if (base<=10) return -1; + if ((c>='A') && (c<='Z') && (c<='A'+base-11)) return (c-'A'+10); + if ((c>='a') && (c<='z') && (c<='a'+base-11)) return (c-'a'+10); + return -1; +} + +/* vfscanf_l */ +#undef WIDE_SCANF +#undef CONSOLE +#undef STRING +#undef SECURE +#include "scanf.h" + +/* vfscanf_s_l */ +#define SECURE 1 +#include "scanf.h" + +/* vfwscanf_l */ +#define WIDE_SCANF 1 +#undef CONSOLE +#undef STRING +#undef SECURE +#include "scanf.h" + +/* vfwscanf_s_l */ +#define SECURE 1 +#include "scanf.h" + +/* vsscanf_l */ +#undef WIDE_SCANF +#undef CONSOLE +#define STRING 1 +#undef SECURE +#include "scanf.h" + +/* vsscanf_s_l */ +#define SECURE 1 +#include "scanf.h" + +/* vsnscanf_l */ +#undef SECURE +#define STRING_LEN 1 +#include "scanf.h" + +/* vsnscanf_s_l */ +#define SECURE +#include "scanf.h" +#undef STRING_LEN + +/* vswscanf_l */ +#define WIDE_SCANF 1 +#undef CONSOLE +#define STRING 1 +#undef SECURE +#include "scanf.h" + +/* vsnwscanf_l */ +#define STRING_LEN 1 +#include "scanf.h" + +/* vsnwscanf_s_l */ +#define SECURE 1 +#include "scanf.h" +#undef STRING_LEN + +/* vswscanf_s_l */ +#define SECURE 1 +#include "scanf.h" + +/* vcscanf_l */ +#undef WIDE_SCANF +#define CONSOLE 1 +#undef STRING +#undef SECURE +#include "scanf.h" + +/* vcscanf_s_l */ +#define SECURE 1 +#include "scanf.h" + +/* vcwscanf_l */ +#define WIDE_SCANF 1 +#define CONSOLE 1 +#undef STRING +#undef SECURE +#include "scanf.h" + +/* vcwscanf_s_l */ +#define SECURE 1 +#include "scanf.h" + + +/********************************************************************* + * fscanf (MSVCRT.@) + */ +int WINAPIV fscanf(FILE *file, const char *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vfscanf_l(file, format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _fscanf_l (MSVCRT.@) + */ +int WINAPIV _fscanf_l(FILE *file, const char *format, + _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vfscanf_l(file, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * fscanf_s (MSVCRT.@) + */ +int WINAPIV fscanf_s(FILE *file, const char *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vfscanf_s_l(file, format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _fscanf_s_l (MSVCRT.@) + */ +int WINAPIV _fscanf_s_l(FILE *file, const char *format, + _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vfscanf_s_l(file, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * scanf (MSVCRT.@) + */ +int WINAPIV scanf(const char *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vfscanf_l(stdin, format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _scanf_l (MSVCRT.@) + */ +int WINAPIV _scanf_l(const char *format, _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vfscanf_l(stdin, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * scanf_s (MSVCRT.@) + */ +int WINAPIV scanf_s(const char *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vfscanf_s_l(stdin, format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _scanf_s_l (MSVCRT.@) + */ +int WINAPIV _scanf_s_l(const char *format, _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vfscanf_s_l(stdin, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * fwscanf (MSVCRT.@) + */ +int WINAPIV fwscanf(FILE *file, const wchar_t *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vfwscanf_l(file, format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _fwscanf_l (MSVCRT.@) + */ +int WINAPIV _fwscanf_l(FILE *file, const wchar_t *format, + _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vfwscanf_l(file, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * fwscanf_s (MSVCRT.@) + */ +int WINAPIV fwscanf_s(FILE *file, const wchar_t *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vfwscanf_s_l(file, format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _fwscanf_s_l (MSVCRT.@) + */ +int WINAPIV _fwscanf_s_l(FILE *file, const wchar_t *format, + _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vfwscanf_s_l(file, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * wscanf (MSVCRT.@) + */ +int WINAPIV wscanf(const wchar_t *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vfwscanf_l(stdin, format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _wscanf_l (MSVCRT.@) + */ +int WINAPIV _wscanf_l(const wchar_t *format, + _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vfwscanf_l(stdin, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * wscanf_s (MSVCRT.@) + */ +int WINAPIV wscanf_s(const wchar_t *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vfwscanf_s_l(stdin, format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _wscanf_s_l (MSVCRT.@) + */ +int WINAPIV _wscanf_s_l(const wchar_t *format, + _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vfwscanf_s_l(stdin, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * sscanf (MSVCRT.@) + */ +int WINAPIV sscanf(const char *str, const char *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vsscanf_l(str, format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _sscanf_l (MSVCRT.@) + */ +int WINAPIV _sscanf_l(const char *str, const char *format, + _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vsscanf_l(str, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * sscanf_s (MSVCRT.@) + */ +int WINAPIV sscanf_s(const char *str, const char *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vsscanf_s_l(str, format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _sscanf_s_l (MSVCRT.@) + */ +int WINAPIV _sscanf_s_l(const char *str, const char *format, + _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vsscanf_s_l(str, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * swscanf (MSVCRT.@) + */ +int WINAPIV swscanf(const wchar_t *str, const wchar_t *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vswscanf_l(str, format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _swscanf_l (MSVCRT.@) + */ +int WINAPIV _swscanf_l(const wchar_t *str, const wchar_t *format, + _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vswscanf_l(str, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * swscanf_s (MSVCRT.@) + */ +int WINAPIV swscanf_s(const wchar_t *str, const wchar_t *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vswscanf_s_l(str, format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _swscanf_s_l (MSVCRT.@) + */ +int WINAPIV _swscanf_s_l(const wchar_t *str, const wchar_t *format, + _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vswscanf_s_l(str, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _cscanf (MSVCRT.@) + */ +int WINAPIV _cscanf(const char *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vcscanf_l(format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _cscanf_l (MSVCRT.@) + */ +int WINAPIV _cscanf_l(const char *format, _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vcscanf_l(format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _cscanf_s (MSVCRT.@) + */ +int WINAPIV _cscanf_s(const char *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vcscanf_s_l(format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _cscanf_s_l (MSVCRT.@) + */ +int WINAPIV _cscanf_s_l(const char *format, _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vcscanf_s_l(format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _cwscanf (MSVCRT.@) + */ +int WINAPIV _cwscanf(const wchar_t *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vcwscanf_l(format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _cwscanf_l (MSVCRT.@) + */ +int WINAPIV _cwscanf_l(const wchar_t *format, _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vcwscanf_l(format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _cwscanf_s (MSVCRT.@) + */ +int WINAPIV _cwscanf_s(const wchar_t *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vcwscanf_s_l(format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _cwscanf_s_l (MSVCRT.@) + */ +int WINAPIV _cwscanf_s_l(const wchar_t *format, _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vcwscanf_s_l(format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _snscanf (MSVCRT.@) + */ +int WINAPIV _snscanf(const char *input, size_t length, const char *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vsnscanf_l(input, length, format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _snscanf_l (MSVCRT.@) + */ +int WINAPIV _snscanf_l(const char *input, size_t length, + const char *format, _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vsnscanf_l(input, length, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _snscanf_s (MSVCRT.@) + */ +int WINAPIV _snscanf_s(const char *input, size_t length, const char *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vsnscanf_s_l(input, length, format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _snscanf_s_l (MSVCRT.@) + */ +int WINAPIV _snscanf_s_l(const char *input, size_t length, + const char *format, _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vsnscanf_s_l(input, length, format, locale, valist); + va_end(valist); + return res; +} + + +/********************************************************************* + * __stdio_common_vsscanf (UCRTBASE.@) + */ +int CDECL __stdio_common_vsscanf(unsigned __int64 options, + const char *input, size_t length, + const char *format, + _locale_t locale, + va_list valist) +{ + /* LEGACY_WIDE_SPECIFIERS only has got an effect on the wide + * scanf. LEGACY_MSVCRT_COMPATIBILITY affects parsing of nan/inf, + * but parsing of those isn't implemented at all yet. */ + if (options & ~UCRTBASE_SCANF_MASK) + FIXME("options %#I64x not handled\n", options); + if (options & _CRT_INTERNAL_SCANF_SECURECRT) + return vsnscanf_s_l(input, length, format, locale, valist); + else + return vsnscanf_l(input, length, format, locale, valist); +} + +/********************************************************************* + * __stdio_common_vswscanf (UCRTBASE.@) + */ +int CDECL __stdio_common_vswscanf(unsigned __int64 options, + const wchar_t *input, size_t length, + const wchar_t *format, + _locale_t locale, + va_list valist) +{ + /* LEGACY_WIDE_SPECIFIERS only has got an effect on the wide + * scanf. LEGACY_MSVCRT_COMPATIBILITY affects parsing of nan/inf, + * but parsing of those isn't implemented at all yet. */ + if (options & ~UCRTBASE_SCANF_MASK) + FIXME("options %#I64x not handled\n", options); + if (options & _CRT_INTERNAL_SCANF_SECURECRT) + return vsnwscanf_s_l(input, length, format, locale, valist); + else + return vsnwscanf_l(input, length, format, locale, valist); +} + +/********************************************************************* + * __stdio_common_vfscanf (UCRTBASE.@) + */ +int CDECL __stdio_common_vfscanf(unsigned __int64 options, + FILE *file, + const char *format, + _locale_t locale, + va_list valist) +{ + if (options & ~(_CRT_INTERNAL_SCANF_SECURECRT | _CRT_INTERNAL_SCANF_LEGACY_WIDE_SPECIFIERS)) + FIXME("options %#I64x not handled\n", options); + if (options & _CRT_INTERNAL_SCANF_SECURECRT) + return vfscanf_s_l(file, format, locale, valist); + else + return vfscanf_l(file, format, locale, valist); +} + +/********************************************************************* + * __stdio_common_vfwscanf (UCRTBASE.@) + */ +int CDECL __stdio_common_vfwscanf(unsigned __int64 options, + FILE *file, + const wchar_t *format, + _locale_t locale, + va_list valist) +{ + if (options & ~_CRT_INTERNAL_SCANF_SECURECRT) + FIXME("options %#I64x not handled\n", options); + if (options & _CRT_INTERNAL_SCANF_SECURECRT) + return vfwscanf_s_l(file, format, locale, valist); + else + return vfwscanf_l(file, format, locale, valist); +} + +/********************************************************************* + * _snwscanf (MSVCRT.@) + */ +int WINAPIV _snwscanf(wchar_t *input, size_t length, + const wchar_t *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vsnwscanf_l(input, length, format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _snwscanf_l (MSVCRT.@) + */ +int WINAPIV _snwscanf_l(wchar_t *input, size_t length, + const wchar_t *format, _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vsnwscanf_l(input, length, format, locale, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _snwscanf_s (MSVCRT.@) + */ +int WINAPIV _snwscanf_s(wchar_t *input, size_t length, + const wchar_t *format, ...) +{ + va_list valist; + int res; + + va_start(valist, format); + res = vsnwscanf_s_l(input, length, format, NULL, valist); + va_end(valist); + return res; +} + +/********************************************************************* + * _snscanf_s_l (MSVCRT.@) + */ +int WINAPIV _snwscanf_s_l(wchar_t *input, size_t length, + const wchar_t *format, _locale_t locale, ...) +{ + va_list valist; + int res; + + va_start(valist, locale); + res = vsnwscanf_s_l(input, length, format, locale, valist); + va_end(valist); + return res; +} + +#if _MSVCR_VER==120 + +/********************************************************************* + * vsscanf (MSVCRT120.@) + */ +int CDECL MSVCRT_vsscanf(const char *buffer, const char *format, va_list valist) +{ + if (!MSVCRT_CHECK_PMT(buffer != NULL && format != NULL)) return -1; + + return vsscanf_l(buffer, format, NULL, valist); +} + +/********************************************************************* + * vswscanf (MSVCRT120.@) + */ +int CDECL vswscanf(const wchar_t *buffer, const wchar_t *format, va_list valist) +{ + if (!MSVCRT_CHECK_PMT(buffer != NULL && format != NULL)) return -1; + + return vswscanf_l(buffer, format, NULL, valist); +} + +#endif /* _MSVCR_VER>=120 */ diff --git a/dll/win32/msvcrt/scanf.h b/dll/win32/msvcrt/scanf.h new file mode 100644 index 00000000000..1d7a92a9c1c --- /dev/null +++ b/dll/win32/msvcrt/scanf.h @@ -0,0 +1,743 @@ +/* + * general implementation of scanf used by scanf, sscanf, fscanf, + * _cscanf, wscanf, swscanf and fwscanf + * + * Copyright 1996,1998 Marcus Meissner + * Copyright 1996 Jukka Iivonen + * Copyright 1997,2000, 2003 Uwe Bonnes + * Copyright 2000 Jon Griffiths + * Copyright 2002 Daniel Gudbjartsson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#ifdef WIDE_SCANF +#define _CHAR_ wchar_t +#define _EOF_ WEOF +#define _EOF_RET (short)WEOF +#define _ISSPACE_(c) iswspace(c) +#define _WIDE2SUPPORTED_(c) c /* No conversion needed (wide to wide) */ +#define _CHAR2SUPPORTED_(c) c /* FIXME: convert char to wide char */ +#define _CHAR2DIGIT_(c, base) wchar2digit((c), (base)) +#define _BITMAPSIZE_ 256*256 +#else /* WIDE_SCANF */ +#define _CHAR_ char +#define _EOF_ EOF +#define _EOF_RET EOF +#define _ISSPACE_(c) isspace(c) +#define _WIDE2SUPPORTED_(c) c /* FIXME: convert wide char to char */ +#define _CHAR2SUPPORTED_(c) c /* No conversion needed (char to char) */ +#define _CHAR2DIGIT_(c, base) char2digit((c), (base)) +#define _BITMAPSIZE_ 256 +#endif /* WIDE_SCANF */ + +#ifdef CONSOLE +#define _GETC_FUNC_(file) _getch() +#define _STRTOD_NAME_(func) console_ ## func +#define _GETC_(file) (consumed++, _getch()) +#define _UNGETC_(nch, file) do { _ungetch(nch); consumed--; } while(0) +#define _LOCK_FILE_(file) _lock_file(stdin) +#define _UNLOCK_FILE_(file) _unlock_file(stdin) +#ifdef WIDE_SCANF +#ifdef SECURE +#define _FUNCTION_ static int vcwscanf_s_l(const wchar_t *format, _locale_t locale, va_list ap) +#else /* SECURE */ +#define _FUNCTION_ static int vcwscanf_l(const wchar_t *format, _locale_t locale, va_list ap) +#endif /* SECURE */ +#else /* WIDE_SCANF */ +#ifdef SECURE +#define _FUNCTION_ static int vcscanf_s_l(const char *format, _locale_t locale, va_list ap) +#else /* SECURE */ +#define _FUNCTION_ static int vcscanf_l(const char *format, _locale_t locale, va_list ap) +#endif /* SECURE */ +#endif /* WIDE_SCANF */ +#else +#ifdef STRING +#undef _EOF_ +#define _EOF_ 0 +#define _GETC_FUNC_(file) (*file++) +#ifdef WIDE_SCANF +#define _STRTOD_NAME_(func) wstr_ ## func +#else +#define _STRTOD_NAME_(func) str_ ## func +#endif +#ifdef STRING_LEN +#ifdef WIDE_SCANF +#define _GETC_(file) (consumed++, consumed>length ? '\0' : *file++) +#else /* WIDE_SCANF */ +#define _GETC_(file) (consumed++, consumed>length ? '\0' : (unsigned char)*file++) +#endif /* WIDE_SCANF */ +#define _UNGETC_(nch, file) do { file--; consumed--; } while(0) +#define _LOCK_FILE_(file) do {} while(0) +#define _UNLOCK_FILE_(file) do {} while(0) +#ifdef WIDE_SCANF +#ifdef SECURE +#define _FUNCTION_ static int vsnwscanf_s_l(const wchar_t *file, size_t length, const wchar_t *format, _locale_t locale, va_list ap) +#else /* SECURE */ +#define _FUNCTION_ static int vsnwscanf_l(const wchar_t *file, size_t length, const wchar_t *format, _locale_t locale, va_list ap) +#endif /* SECURE */ +#else /* WIDE_SCANF */ +#ifdef SECURE +#define _FUNCTION_ static int vsnscanf_s_l(const char *file, size_t length, const char *format, _locale_t locale, va_list ap) +#else /* SECURE */ +#define _FUNCTION_ static int vsnscanf_l(const char *file, size_t length, const char *format, _locale_t locale, va_list ap) +#endif /* SECURE */ +#endif /* WIDE_SCANF */ +#else /* STRING_LEN */ +#ifdef WIDE_SCANF +#define _GETC_(file) (consumed++, *file++) +#else /* WIDE_SCANF */ +#define _GETC_(file) (consumed++, (unsigned char)*file++) +#endif /* WIDE_SCANF */ +#define _UNGETC_(nch, file) do { file--; consumed--; } while(0) +#define _LOCK_FILE_(file) do {} while(0) +#define _UNLOCK_FILE_(file) do {} while(0) +#ifdef WIDE_SCANF +#ifdef SECURE +#define _FUNCTION_ static int vswscanf_s_l(const wchar_t *file, const wchar_t *format, _locale_t locale, va_list ap) +#else /* SECURE */ +#define _FUNCTION_ static int vswscanf_l(const wchar_t *file, const wchar_t *format, _locale_t locale, va_list ap) +#endif /* SECURE */ +#else /* WIDE_SCANF */ +#ifdef SECURE +#define _FUNCTION_ static int vsscanf_s_l(const char *file, const char *format, _locale_t locale, va_list ap) +#else /* SECURE */ +#define _FUNCTION_ static int vsscanf_l(const char *file, const char *format, _locale_t locale, va_list ap) +#endif /* SECURE */ +#endif /* WIDE_SCANF */ +#endif /* STRING_LEN */ +#else /* STRING */ +#ifdef WIDE_SCANF +#define _GETC_FUNC_(file) fgetwc(file) +#define _STRTOD_NAME_(func) filew_ ## func +#define _GETC_(file) (consumed++, fgetwc(file)) +#define _UNGETC_(nch, file) do { ungetwc(nch, file); consumed--; } while(0) +#define _LOCK_FILE_(file) _lock_file(file) +#define _UNLOCK_FILE_(file) _unlock_file(file) +#ifdef SECURE +#define _FUNCTION_ static int vfwscanf_s_l(FILE* file, const wchar_t *format, _locale_t locale, va_list ap) +#else /* SECURE */ +#define _FUNCTION_ static int vfwscanf_l(FILE* file, const wchar_t *format, _locale_t locale, va_list ap) +#endif /* SECURE */ +#else /* WIDE_SCANF */ +#define _GETC_FUNC_(file) fgetc(file) +#define _STRTOD_NAME_(func) file_ ## func +#define _GETC_(file) (consumed++, fgetc(file)) +#define _UNGETC_(nch, file) do { ungetc(nch, file); consumed--; } while(0) +#define _LOCK_FILE_(file) _lock_file(file) +#define _UNLOCK_FILE_(file) _unlock_file(file) +#ifdef SECURE +#define _FUNCTION_ static int vfscanf_s_l(FILE* file, const char *format, _locale_t locale, va_list ap) +#else /* SECURE */ +#define _FUNCTION_ static int vfscanf_l(FILE* file, const char *format, _locale_t locale, va_list ap) +#endif /* SECURE */ +#endif /* WIDE_SCANF */ +#endif /* STRING */ +#endif /* CONSOLE */ + +#if (!defined(SECURE) && !defined(STRING_LEN) && (!defined(CONSOLE) || !defined(WIDE_SCANF))) +struct _STRTOD_NAME_(strtod_scanf_ctx) { + pthreadlocinfo locinfo; +#ifdef STRING + const _CHAR_ *file; +#else + FILE *file; +#endif + int length; + int read; + int cur; + int unget; + BOOL err; +}; + +static wchar_t _STRTOD_NAME_(strtod_scanf_get)(void *ctx) +{ + struct _STRTOD_NAME_(strtod_scanf_ctx) *context = ctx; + + context->cur = _EOF_; + if (!context->length) return WEOF; + if (context->unget != _EOF_) { + context->cur = context->unget; + context->unget = _EOF_; + } else { + context->cur = _GETC_FUNC_(context->file); + if (context->cur == _EOF_) return WEOF; + } + + if (context->length > 0) context->length--; + context->read++; + return context->cur; +} + +static void _STRTOD_NAME_(strtod_scanf_unget)(void *ctx) +{ + struct _STRTOD_NAME_(strtod_scanf_ctx) *context = ctx; + + if (context->length >= 0) context->length++; + context->read--; + if (context->unget != _EOF_ || context->cur == _EOF_) { + context->err = TRUE; + return; + } + context->unget = context->cur; +} +#endif + +_FUNCTION_ { + pthreadlocinfo locinfo; + int rd = 0, consumed = 0; + int nch; + if (!*format) return 0; +#ifndef WIDE_SCANF +#ifdef CONSOLE + TRACE("(%s):\n", debugstr_a(format)); +#else /* CONSOLE */ +#ifdef STRING + TRACE("%s (%s)\n", debugstr_a(file), debugstr_a(format)); +#else /* STRING */ + TRACE("%p (%s)\n", file, debugstr_a(format)); +#endif /* STRING */ +#endif /* CONSOLE */ +#endif /* WIDE_SCANF */ + _LOCK_FILE_(file); + + nch = _GETC_(file); + if (nch == _EOF_) { + _UNLOCK_FILE_(file); + return _EOF_RET; + } + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + while (*format) { + /* a whitespace character in the format string causes scanf to read, + * but not store, all consecutive white-space characters in the input + * up to the next non-white-space character. One white space character + * in the input matches any number (including zero) and combination of + * white-space characters in the input. */ + if (_ISSPACE_(*format)) { + /* skip whitespace */ + while ((nch!=_EOF_) && _ISSPACE_(nch)) + nch = _GETC_(file); + } + /* a format specification causes scanf to read and convert characters + * in the input into values of a specified type. The value is assigned + * to an argument in the argument list. Format specifications have + * the form %[*][width][{h | l | I64 | L}]type */ + else if (*format == '%') { + int st = 0; int suppress = 0; int width = 0; + int base; + int h_prefix = 0; + int l_prefix = 0; + int L_prefix = 0; + int w_prefix = 0; + int prefix_finished = 0; + int I64_prefix = 0; + format++; + /* look for leading asterisk, which means 'suppress assignment of + * this field'. */ + if (*format=='*') { + format++; + suppress=1; + } + /* read prefix (if any) */ + while (!prefix_finished) { + /* look for width specification */ + while (*format >= '0' && *format <= '9') { + width *= 10; + width += *format++ - '0'; + } + + switch(*format) { + case 'h': h_prefix++; break; + case 'l': + if(*(format+1) == 'l') { + I64_prefix = 1; + format++; + } + l_prefix = 1; + break; + case 'w': w_prefix = 1; break; + case 'L': L_prefix = 1; break; + case 'I': + if (*(format + 1) == '6' && + *(format + 2) == '4') { + I64_prefix = 1; + format += 2; + break; + } + else if (*(format + 1) == '3' && + *(format + 2) == '2') { + format += 2; + break; + } + /* fall through */ +#if _MSVCR_VER == 0 || _MSVCR_VER >= 140 + case 'z': +#endif + if (sizeof(void *) == sizeof(LONGLONG)) I64_prefix = 1; + break; + default: + prefix_finished = 1; + } + if (!prefix_finished) format++; + } + if (width==0) width=-1; /* no width spec seen */ + /* read type */ + switch(*format) { + case 'p': + case 'P': /* pointer. */ + if (sizeof(void *) == sizeof(LONGLONG)) I64_prefix = 1; + /* fall through */ + case 'x': + case 'X': /* hexadecimal integer. */ + base = 16; + goto number; + case 'o': /* octal integer */ + base = 8; + goto number; + case 'u': /* unsigned decimal integer */ + base = 10; + goto number; + case 'd': /* signed decimal integer */ + base = 10; + goto number; + case 'i': /* generic integer */ + base = 0; + number: { + /* read an integer */ + ULONGLONG cur = 0; + int negative = 0; + int seendigit=0; + /* skip initial whitespace */ + while ((nch!=_EOF_) && _ISSPACE_(nch)) + nch = _GETC_(file); + /* get sign */ + if (nch == '-' || nch == '+') { + negative = (nch=='-'); + nch = _GETC_(file); + if (width>0) width--; + } + /* look for leading indication of base */ + if (width!=0 && nch == '0' && *format != 'p' && *format != 'P') { + nch = _GETC_(file); + if (width>0) width--; + seendigit=1; + if (width!=0 && (nch=='x' || nch=='X')) { + if (base==0) + base=16; + if (base==16) { + nch = _GETC_(file); + if (width>0) width--; + seendigit=0; + } + } else if (base==0) + base = 8; + } + /* format %i without indication of base */ + if (base==0) + base = 10; + /* throw away leading zeros */ + while (width!=0 && nch=='0') { + nch = _GETC_(file); + if (width>0) width--; + seendigit=1; + } + if (width!=0 && _CHAR2DIGIT_(nch, base)!=-1) { + cur = _CHAR2DIGIT_(nch, base); + nch = _GETC_(file); + if (width>0) width--; + seendigit=1; + } + /* read until no more digits */ + while (width!=0 && (nch!=_EOF_) && _CHAR2DIGIT_(nch, base)!=-1) { + cur = cur*base + _CHAR2DIGIT_(nch, base); + nch = _GETC_(file); + if (width>0) width--; + seendigit=1; + } + /* okay, done! */ + if (!seendigit) break; /* not a valid number */ + st = 1; + if (!suppress) { +#define _SET_NUMBER_(type) *va_arg(ap, type*) = negative ? -cur : cur + if (I64_prefix) _SET_NUMBER_(LONGLONG); + else if (l_prefix) _SET_NUMBER_(LONG); + else if (h_prefix == 1) _SET_NUMBER_(short int); +#if _MSVCR_VER >= 140 + else if (h_prefix == 2) _SET_NUMBER_(char); +#endif + else _SET_NUMBER_(int); + } + } + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': { /* read a float */ +#ifdef CONSOLE + struct _STRTOD_NAME_(strtod_scanf_ctx) ctx = {locinfo, 0, width}; +#else + struct _STRTOD_NAME_(strtod_scanf_ctx) ctx = {locinfo, file, width}; +#endif + int negative = 0; + struct fpnum fp; + double cur; + + /* skip initial whitespace */ + while ((nch!=_EOF_) && _ISSPACE_(nch)) + nch = _GETC_(file); + if (nch == _EOF_) + break; + ctx.unget = nch; +#ifdef STRING + ctx.file = file; +#endif +#ifdef STRING_LEN + if(ctx.length > length-consumed+1) ctx.length = length-consumed+1; +#endif + + fp = fpnum_parse(_STRTOD_NAME_(strtod_scanf_get), + _STRTOD_NAME_(strtod_scanf_unget), &ctx, locinfo, FALSE); + fpnum_double(&fp, &cur); + if(!rd && ctx.err) { + _UNLOCK_FILE_(file); + return _EOF_RET; + } + if(ctx.err || !ctx.read) + break; + consumed += ctx.read; +#ifdef STRING + file = ctx.file; +#endif + nch = ctx.cur; + + st = 1; + if (!suppress) { + if (L_prefix || l_prefix) _SET_NUMBER_(double); + else _SET_NUMBER_(float); + } + } + break; + /* According to msdn, + * 's' reads a character string in a call to fscanf + * and 'S' a wide character string and vice versa in a + * call to fwscanf. The 'h', 'w' and 'l' prefixes override + * this behaviour. 'h' forces reading char * but 'l' and 'w' + * force reading WCHAR. */ + case 's': + if (w_prefix || l_prefix) goto widecharstring; + else if (h_prefix) goto charstring; +#ifdef WIDE_SCANF + else goto widecharstring; +#else /* WIDE_SCANF */ + else goto charstring; +#endif /* WIDE_SCANF */ + case 'S': + if (w_prefix || l_prefix) goto widecharstring; + else if (h_prefix) goto charstring; +#ifdef WIDE_SCANF + else goto charstring; +#else /* WIDE_SCANF */ + else goto widecharstring; +#endif /* WIDE_SCANF */ + charstring: { /* read a word into a char */ + char *sptr = suppress ? NULL : va_arg(ap, char*); + char *sptr_beg = sptr; +#ifdef SECURE + unsigned size = suppress ? UINT_MAX : va_arg(ap, unsigned); +#else + unsigned size = UINT_MAX; +#endif + /* skip initial whitespace */ + while ((nch!=_EOF_) && _ISSPACE_(nch)) + nch = _GETC_(file); + /* read until whitespace */ + while (width!=0 && (nch!=_EOF_) && !_ISSPACE_(nch)) { + if (!suppress) { + *sptr++ = _CHAR2SUPPORTED_(nch); + if(size>1) size--; + else { + _UNLOCK_FILE_(file); + *sptr_beg = 0; + return rd; + } + } + st++; + nch = _GETC_(file); + if (width>0) width--; + } + /* if we have reached the EOF and output nothing then report EOF */ + if (nch==_EOF_ && rd==0 && st==0) { + _UNLOCK_FILE_(file); + return _EOF_RET; + } + /* terminate */ + if (st && !suppress) *sptr = 0; + } + break; + widecharstring: { /* read a word into a wchar_t* */ + wchar_t *sptr = suppress ? NULL : va_arg(ap, wchar_t*); + wchar_t *sptr_beg = sptr; +#ifdef SECURE + unsigned size = suppress ? UINT_MAX : va_arg(ap, unsigned); +#else + unsigned size = UINT_MAX; +#endif + /* skip initial whitespace */ + while ((nch!=_EOF_) && _ISSPACE_(nch)) + nch = _GETC_(file); + /* read until whitespace */ + while (width!=0 && (nch!=_EOF_) && !_ISSPACE_(nch)) { + if (!suppress) { + *sptr++ = _WIDE2SUPPORTED_(nch); + if(size>1) size--; + else { + _UNLOCK_FILE_(file); + *sptr_beg = 0; + return rd; + } + } + st++; + nch = _GETC_(file); + if (width>0) width--; + } +#if _MSVCR_VER >= 80 + /* if we have reached the EOF and output nothing then report EOF */ + if (nch==_EOF_ && rd==0 && st==0) { + _UNLOCK_FILE_(file); + return _EOF_RET; + } +#endif + /* terminate */ + if (st && !suppress) *sptr = 0; + } + break; + /* 'c' and 'C work analogously to 's' and 'S' as described + * above */ + case 'c': + if (w_prefix || l_prefix) goto widecharacter; + else if (h_prefix) goto character; +#ifdef WIDE_SCANF + else goto widecharacter; +#else /* WIDE_SCANF */ + else goto character; +#endif /* WIDE_SCANF */ + case 'C': + if (w_prefix || l_prefix) goto widecharacter; + else if (h_prefix) goto character; +#ifdef WIDE_SCANF + else goto character; +#else /* WIDE_SCANF */ + else goto widecharacter; +#endif /* WIDE_SCANF */ + character: { /* read single character into char */ + char *str = suppress ? NULL : va_arg(ap, char*); + char *pstr = str; +#ifdef SECURE + unsigned size = suppress ? UINT_MAX : va_arg(ap, unsigned); +#else + unsigned size = UINT_MAX; +#endif + if (width == -1) width = 1; + while (width && (nch != _EOF_)) + { + if (!suppress) { + if(size) size--; + else { + _UNLOCK_FILE_(file); + *pstr = 0; + return rd; + } + *str++ = _CHAR2SUPPORTED_(nch); + } + st++; + width--; + nch = _GETC_(file); + } + } + break; + widecharacter: { /* read single character into a wchar_t */ + wchar_t *str = suppress ? NULL : va_arg(ap, wchar_t*); + wchar_t *pstr = str; +#ifdef SECURE + unsigned size = suppress ? UINT_MAX : va_arg(ap, unsigned); +#else + unsigned size = UINT_MAX; +#endif + if (width == -1) width = 1; + while (width && (nch != _EOF_)) + { + if (!suppress) { + if(size) size--; + else { + _UNLOCK_FILE_(file); + *pstr = 0; + return rd; + } + *str++ = _WIDE2SUPPORTED_(nch); + } + st++; + width--; + nch = _GETC_(file); + } + } + break; + case 'n': { + if (!suppress) { + int*n = va_arg(ap, int*); + *n = consumed - 1; + } + /* This is an odd one: according to the standard, + * "Execution of a %n directive does not increment the + * assignment count returned at the completion of + * execution" even if it wasn't suppressed with the + * '*' flag. The Corrigendum to the standard seems + * to contradict this (comment out the assignment to + * suppress below if you want to implement these + * alternate semantics) but the windows program I'm + * looking at expects the behavior I've coded here + * (which happens to be what glibc does as well). + */ + suppress = 1; + st = 1; + } + break; + case '[': { + _CHAR_ *str = suppress ? NULL : va_arg(ap, _CHAR_*); + _CHAR_ *sptr = str; + RTL_BITMAP bitMask; + ULONG *Mask; + int invert = 0; /* Set if we are NOT to find the chars */ +#ifdef SECURE + unsigned size = suppress ? UINT_MAX : va_arg(ap, unsigned); +#else + unsigned size = UINT_MAX; +#endif + + /* Init our bitmap */ + Mask = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, _BITMAPSIZE_/8); + RtlInitializeBitMap(&bitMask, Mask, _BITMAPSIZE_); + + /* Read the format */ + format++; + if(*format == '^') { + invert = 1; + format++; + } + if(*format == ']') { + RtlSetBits(&bitMask, ']', 1); + format++; + } + while(*format && (*format != ']')) { + /* According to msdn: + * "Note that %[a-z] and %[z-a] are interpreted as equivalent to %[abcde...z]." */ + if(format[1] == '-' && format[2] && format[2] != ']') { + if (format[0] < format[2]) + RtlSetBits(&bitMask, format[0], format[2] - format[0] + 1); + else + RtlSetBits(&bitMask, format[2], format[0] - format[2] + 1); + format += 2; + } else + RtlSetBits(&bitMask, *format, 1); + format++; + } + /* read until char is not suitable */ + while ((width != 0) && (nch != _EOF_)) { + if(!invert) { + if(RtlAreBitsSet(&bitMask, nch, 1)) { + if (!suppress) *sptr++ = _CHAR2SUPPORTED_(nch); + } else + break; + } else { + if(RtlAreBitsClear(&bitMask, nch, 1)) { + if (!suppress) *sptr++ = _CHAR2SUPPORTED_(nch); + } else + break; + } + st++; + nch = _GETC_(file); + if (width>0) width--; + if(size>1) size--; + else { + _UNLOCK_FILE_(file); + *str = 0; + HeapFree(GetProcessHeap(), 0, Mask); + return rd; + } + } + /* terminate */ + if (!suppress) *sptr = 0; + HeapFree(GetProcessHeap(), 0, Mask); + } + break; + default: + /* From spec: "if a percent sign is followed by a character + * that has no meaning as a format-control character, that + * character and the following characters are treated as + * an ordinary sequence of characters, that is, a sequence + * of characters that must match the input. For example, + * to specify that a percent-sign character is to be input, + * use %%." */ + while ((nch!=_EOF_) && _ISSPACE_(nch)) + nch = _GETC_(file); + if ((_CHAR_)nch == *format) { + suppress = 1; /* whoops no field to be read */ + st = 1; /* but we got what we expected */ + nch = _GETC_(file); + } + break; + } + if (st && !suppress) rd++; + else if (!st) break; + } + /* a non-white-space character causes scanf to read, but not store, + * a matching non-white-space character. */ + else { + /* check for character match */ + if ((_CHAR_)nch == *format) { + nch = _GETC_(file); + } else break; + } + format++; + } + if (nch!=_EOF_) { + _UNGETC_(nch, file); + } + + TRACE("returning %d\n", rd); + _UNLOCK_FILE_(file); + return rd; +} + +#undef _CHAR_ +#undef _EOF_ +#undef _EOF_RET +#undef _ISSPACE_ +#undef _CHAR2SUPPORTED_ +#undef _WIDE2SUPPORTED_ +#undef _CHAR2DIGIT_ +#undef _GETC_FUNC_ +#undef _STRTOD_NAME_ +#undef _GETC_ +#undef _UNGETC_ +#undef _LOCK_FILE_ +#undef _UNLOCK_FILE_ +#undef _FUNCTION_ +#undef _BITMAPSIZE_ diff --git a/dll/win32/msvcrt/sincos.c b/dll/win32/msvcrt/sincos.c new file mode 100644 index 00000000000..1a34c50f034 --- /dev/null +++ b/dll/win32/msvcrt/sincos.c @@ -0,0 +1,40 @@ +/* + * sincos implementation + * + * Copyright 2021 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#if 0 +#pragma makedep implib +#endif + +#include + +/* GCC may optimize a pair of sin(), cos() calls to a single sincos() call, + * which is not exported by any msvcrt version. */ + +void sincos(double x, double *s, double *c) +{ + *s = sin(x); + *c = cos(x); +} + +void sincosf(float x, float *s, float *c) +{ + *s = sinf(x); + *c = cosf(x); +} diff --git a/dll/win32/msvcrt/string.c b/dll/win32/msvcrt/string.c new file mode 100644 index 00000000000..90538533d95 --- /dev/null +++ b/dll/win32/msvcrt/string.c @@ -0,0 +1,3556 @@ +/* + * MSVCRT string functions + * + * Copyright 1996,1998 Marcus Meissner + * Copyright 1996 Jukka Iivonen + * Copyright 1997,2000 Uwe Bonnes + * Copyright 2000 Jon Griffiths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include "msvcrt.h" +#include "bnum.h" +#include "winnls.h" +#include "wine/asm.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +/********************************************************************* + * _mbsdup (MSVCRT.@) + * _strdup (MSVCRT.@) + */ +char* CDECL _strdup(const char* str) +{ + if(str) + { + char * ret = malloc(strlen(str)+1); + if (ret) strcpy( ret, str ); + return ret; + } + else return 0; +} + +/********************************************************************* + * _strlwr_s_l (MSVCRT.@) + */ +int CDECL _strlwr_s_l(char *str, size_t len, _locale_t locale) +{ + pthreadlocinfo locinfo; + char *ptr = str; + + if (!str || !len) + { + *_errno() = EINVAL; + return EINVAL; + } + + while (len && *ptr) + { + len--; + ptr++; + } + + if (!len) + { + str[0] = '\0'; + *_errno() = EINVAL; + return EINVAL; + } + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(!locinfo->lc_handle[LC_CTYPE]) + { + while (*str) + { + if (*str >= 'A' && *str <= 'Z') + *str -= 'A' - 'a'; + str++; + } + } + else + { + while (*str) + { + *str = _tolower_l((unsigned char)*str, locale); + str++; + } + } + + return 0; +} + +/********************************************************************* + * _strlwr_s (MSVCRT.@) + */ +int CDECL _strlwr_s(char *str, size_t len) +{ + return _strlwr_s_l(str, len, NULL); +} + +/********************************************************************* + * _strlwr_l (MSVCRT.@) + */ +char* CDECL _strlwr_l(char *str, _locale_t locale) +{ + _strlwr_s_l(str, -1, locale); + return str; +} + +/********************************************************************* + * _strlwr (MSVCRT.@) + */ +char* CDECL _strlwr(char *str) +{ + _strlwr_s_l(str, -1, NULL); + return str; +} + +/********************************************************************* + * _strupr_s_l (MSVCRT.@) + */ +int CDECL _strupr_s_l(char *str, size_t len, _locale_t locale) +{ + pthreadlocinfo locinfo; + char *ptr = str; + + if (!str || !len) + { + *_errno() = EINVAL; + return EINVAL; + } + + while (len && *ptr) + { + len--; + ptr++; + } + + if (!len) + { + str[0] = '\0'; + *_errno() = EINVAL; + return EINVAL; + } + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(!locinfo->lc_handle[LC_CTYPE]) + { + while (*str) + { + if (*str >= 'a' && *str <= 'z') + *str -= 'a' - 'A'; + str++; + } + } + else + { + while (*str) + { + *str = _toupper_l((unsigned char)*str, locale); + str++; + } + } + + return 0; +} + +/********************************************************************* + * _strupr_s (MSVCRT.@) + */ +int CDECL _strupr_s(char *str, size_t len) +{ + return _strupr_s_l(str, len, NULL); +} + +/********************************************************************* + * _strupr_l (MSVCRT.@) + */ +char* CDECL _strupr_l(char *str, _locale_t locale) +{ + _strupr_s_l(str, -1, locale); + return str; +} + +/********************************************************************* + * _strupr (MSVCRT.@) + */ +char* CDECL _strupr(char *str) +{ + _strupr_s_l(str, -1, NULL); + return str; +} + +/********************************************************************* + * _strnset_s (MSVCRT.@) + */ +int CDECL _strnset_s(char *str, size_t size, int c, size_t count) +{ + size_t i; + + if(!str && !size && !count) return 0; + if(!MSVCRT_CHECK_PMT(str != NULL)) return EINVAL; + if(!MSVCRT_CHECK_PMT(size > 0)) return EINVAL; + + for(i=0; i 0 && str) + while (*str && len--) + *str++ = value; + return str; +} + +/********************************************************************* + * _strrev (MSVCRT.@) + */ +char* CDECL _strrev(char* str) +{ + char * p1; + char * p2; + + if (str && *str) + for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2) + { + *p1 ^= *p2; + *p2 ^= *p1; + *p1 ^= *p2; + } + + return str; +} + +/********************************************************************* + * _strset (MSVCRT.@) + */ +char* CDECL _strset(char* str, int value) +{ + char *ptr = str; + while (*ptr) + *ptr++ = value; + + return str; +} + +/********************************************************************* + * strtok (MSVCRT.@) + */ +char * CDECL strtok( char *str, const char *delim ) +{ + thread_data_t *data = msvcrt_get_thread_data(); + char *ret; + + if (!str) + if (!(str = data->strtok_next)) return NULL; + + while (*str && strchr( delim, *str )) str++; + if (!*str) + { + data->strtok_next = str; + return NULL; + } + ret = str++; + while (*str && !strchr( delim, *str )) str++; + if (*str) *str++ = 0; + data->strtok_next = str; + return ret; +} + +/********************************************************************* + * strtok_s (MSVCRT.@) + */ +char * CDECL strtok_s(char *str, const char *delim, char **ctx) +{ + if (!MSVCRT_CHECK_PMT(delim != NULL)) return NULL; + if (!MSVCRT_CHECK_PMT(ctx != NULL)) return NULL; + if (!MSVCRT_CHECK_PMT(str != NULL || *ctx != NULL)) return NULL; + + if(!str) + str = *ctx; + + while(*str && strchr(delim, *str)) + str++; + if(!*str) + { + *ctx = str; + return NULL; + } + + *ctx = str+1; + while(**ctx && !strchr(delim, **ctx)) + (*ctx)++; + if(**ctx) + *(*ctx)++ = 0; + + return str; +} + +/********************************************************************* + * _swab (MSVCRT.@) + */ +void CDECL _swab(char* src, char* dst, int len) +{ + if (len > 1) + { + len = (unsigned)len >> 1; + + while (len--) { + char s0 = src[0]; + char s1 = src[1]; + *dst++ = s1; + *dst++ = s0; + src = src + 2; + } + } +} + +static struct fpnum fpnum(int sign, int exp, ULONGLONG m, enum fpmod mod) +{ + struct fpnum ret; + + ret.sign = sign; + ret.exp = exp; + ret.m = m; + ret.mod = mod; + return ret; +} + +int fpnum_double(struct fpnum *fp, double *d) +{ + ULONGLONG bits = 0; + + if (fp->mod == FP_VAL_INFINITY) + { + *d = fp->sign * INFINITY; + return 0; + } + + if (fp->mod == FP_VAL_NAN) + { + bits = ~0; + if (fp->sign == 1) + bits &= ~((ULONGLONG)1 << (MANT_BITS + EXP_BITS - 1)); + *d = *(double*)&bits; + return 0; + } + + TRACE("%c %#I64x *2^%d (round %d)\n", fp->sign == -1 ? '-' : '+', + fp->m, fp->exp, fp->mod); + if (!fp->m) + { + *d = fp->sign * 0.0; + return 0; + } + + /* make sure that we don't overflow modifying exponent */ + if (fp->exp > 1<sign * INFINITY; + return ERANGE; + } + if (fp->exp < -(1<sign * 0.0; + return ERANGE; + } + fp->exp += MANT_BITS - 1; + + /* normalize mantissa */ + while(fp->m < (ULONGLONG)1 << (MANT_BITS-1)) + { + fp->m <<= 1; + fp->exp--; + } + while(fp->m >= (ULONGLONG)1 << MANT_BITS) + { + if (fp->m & 1 || fp->mod != FP_ROUND_ZERO) + { + if (!(fp->m & 1)) fp->mod = FP_ROUND_DOWN; + else if(fp->mod == FP_ROUND_ZERO) fp->mod = FP_ROUND_EVEN; + else fp->mod = FP_ROUND_UP; + } + fp->m >>= 1; + fp->exp++; + } + fp->exp += (1 << (EXP_BITS-1)) - 1; + + /* handle subnormals */ + if (fp->exp <= 0) + { + if (fp->m & 1 && fp->mod == FP_ROUND_ZERO) fp->mod = FP_ROUND_EVEN; + else if (fp->m & 1) fp->mod = FP_ROUND_UP; + else if (fp->mod != FP_ROUND_ZERO) fp->mod = FP_ROUND_DOWN; + fp->m >>= 1; + } + while(fp->m && fp->exp<0) + { + if (fp->m & 1 && fp->mod == FP_ROUND_ZERO) fp->mod = FP_ROUND_EVEN; + else if (fp->m & 1) fp->mod = FP_ROUND_UP; + else if (fp->mod != FP_ROUND_ZERO) fp->mod = FP_ROUND_DOWN; + fp->m >>= 1; + fp->exp++; + } + + /* round mantissa */ + if (fp->mod == FP_ROUND_UP || (fp->mod == FP_ROUND_EVEN && fp->m & 1)) + { + fp->m++; + + /* handle subnormal that falls into regular range due to rounding */ + if (fp->m == (ULONGLONG)1 << (MANT_BITS - 1)) + { + fp->exp++; + } + else if (fp->m >= (ULONGLONG)1 << MANT_BITS) + { + fp->exp++; + fp->m >>= 1; + } + } + + if (fp->exp >= (1<sign * INFINITY; + return ERANGE; + } + if (!fp->m || fp->exp < 0) + { + *d = fp->sign * 0.0; + return ERANGE; + } + + if (fp->sign == -1) + bits |= (ULONGLONG)1 << (MANT_BITS + EXP_BITS - 1); + bits |= (ULONGLONG)fp->exp << (MANT_BITS - 1); + bits |= fp->m & (((ULONGLONG)1 << (MANT_BITS - 1)) - 1); + + TRACE("returning %#I64x\n", bits); + *d = *(double*)&bits; + return 0; +} + +#define LDBL_EXP_BITS 15 +#define LDBL_MANT_BITS 64 +int fpnum_ldouble(struct fpnum *fp, MSVCRT__LDOUBLE *d) +{ + if (fp->mod == FP_VAL_INFINITY) + { + d->x80[0] = 0; + d->x80[1] = 0x80000000; + d->x80[2] = (1 << LDBL_EXP_BITS) - 1; + if (fp->sign == -1) + d->x80[2] |= 1 << LDBL_EXP_BITS; + return 0; + } + + if (fp->mod == FP_VAL_NAN) + { + d->x80[0] = ~0; + d->x80[1] = ~0; + d->x80[2] = (1 << LDBL_EXP_BITS) - 1; + if (fp->sign == -1) + d->x80[2] |= 1 << LDBL_EXP_BITS; + return 0; + } + + TRACE("%c %#I64x *2^%d (round %d)\n", fp->sign == -1 ? '-' : '+', + fp->m, fp->exp, fp->mod); + if (!fp->m) + { + d->x80[0] = 0; + d->x80[1] = 0; + d->x80[2] = 0; + if (fp->sign == -1) + d->x80[2] |= 1 << LDBL_EXP_BITS; + return 0; + } + + /* make sure that we don't overflow modifying exponent */ + if (fp->exp > 1<x80[0] = 0; + d->x80[1] = 0x80000000; + d->x80[2] = (1 << LDBL_EXP_BITS) - 1; + if (fp->sign == -1) + d->x80[2] |= 1 << LDBL_EXP_BITS; + return ERANGE; + } + if (fp->exp < -(1<x80[0] = 0; + d->x80[1] = 0; + d->x80[2] = 0; + if (fp->sign == -1) + d->x80[2] |= 1 << LDBL_EXP_BITS; + return ERANGE; + } + fp->exp += LDBL_MANT_BITS - 1; + + /* normalize mantissa */ + while(fp->m < (ULONGLONG)1 << (LDBL_MANT_BITS-1)) + { + fp->m <<= 1; + fp->exp--; + } + fp->exp += (1 << (LDBL_EXP_BITS-1)) - 1; + + /* handle subnormals */ + if (fp->exp <= 0) + { + if (fp->m & 1 && fp->mod == FP_ROUND_ZERO) fp->mod = FP_ROUND_EVEN; + else if (fp->m & 1) fp->mod = FP_ROUND_UP; + else if (fp->mod != FP_ROUND_ZERO) fp->mod = FP_ROUND_DOWN; + fp->m >>= 1; + } + while(fp->m && fp->exp<0) + { + if (fp->m & 1 && fp->mod == FP_ROUND_ZERO) fp->mod = FP_ROUND_EVEN; + else if (fp->m & 1) fp->mod = FP_ROUND_UP; + else if (fp->mod != FP_ROUND_ZERO) fp->mod = FP_ROUND_DOWN; + fp->m >>= 1; + fp->exp++; + } + + /* round mantissa */ + if (fp->mod == FP_ROUND_UP || (fp->mod == FP_ROUND_EVEN && fp->m & 1)) + { + if (fp->m == UI64_MAX) + { + fp->m = (ULONGLONG)1 << (LDBL_MANT_BITS - 1); + fp->exp++; + } + else + { + fp->m++; + + /* handle subnormal that falls into regular range due to rounding */ + if ((fp->m ^ (fp->m - 1)) & ((ULONGLONG)1 << (LDBL_MANT_BITS - 1))) fp->exp++; + } + } + + if (fp->exp >= (1<x80[0] = 0; + d->x80[1] = 0x80000000; + d->x80[2] = (1 << LDBL_EXP_BITS) - 1; + if (fp->sign == -1) + d->x80[2] |= 1 << LDBL_EXP_BITS; + return ERANGE; + } + if (!fp->m || fp->exp < 0) + { + d->x80[0] = 0; + d->x80[1] = 0; + d->x80[2] = 0; + if (fp->sign == -1) + d->x80[2] |= 1 << LDBL_EXP_BITS; + return ERANGE; + } + + d->x80[0] = fp->m; + d->x80[1] = fp->m >> 32; + d->x80[2] = fp->exp; + if (fp->sign == -1) + d->x80[2] |= 1 << LDBL_EXP_BITS; + return 0; +} + +#if _MSVCR_VER >= 140 + +static inline int hex2int(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; +} + +static struct fpnum fpnum_parse16(wchar_t get(void *ctx), void unget(void *ctx), + void *ctx, int sign, pthreadlocinfo locinfo) +{ + BOOL found_digit = FALSE, found_dp = FALSE; + enum fpmod round = FP_ROUND_ZERO; + wchar_t nch; + ULONGLONG m = 0; + int val, exp = 0; + + nch = get(ctx); + while(m < UI64_MAX/16) + { + val = hex2int(nch); + if (val == -1) break; + found_digit = TRUE; + nch = get(ctx); + + m = m*16 + val; + } + while(1) + { + val = hex2int(nch); + if (val == -1) break; + nch = get(ctx); + exp += 4; + + if (val || round != FP_ROUND_ZERO) + { + if (val < 8) round = FP_ROUND_DOWN; + else if (val == 8 && round == FP_ROUND_ZERO) round = FP_ROUND_EVEN; + else round = FP_ROUND_UP; + } + } + + if(nch == *locinfo->lconv->decimal_point) + { + found_dp = TRUE; + nch = get(ctx); + } + else if (!found_digit) + { + if(nch!=WEOF) unget(ctx); + unget(ctx); + return fpnum(0, 0, 0, 0); + } + + while(m <= UI64_MAX/16) + { + val = hex2int(nch); + if (val == -1) break; + found_digit = TRUE; + nch = get(ctx); + + m = m*16 + val; + exp -= 4; + } + while(1) + { + val = hex2int(nch); + if (val == -1) break; + nch = get(ctx); + + if (val || round != FP_ROUND_ZERO) + { + if (val < 8) round = FP_ROUND_DOWN; + else if (val == 8 && round == FP_ROUND_ZERO) round = FP_ROUND_EVEN; + else round = FP_ROUND_UP; + } + } + + if (!found_digit) + { + if (nch != WEOF) unget(ctx); + if (found_dp) unget(ctx); + unget(ctx); + return fpnum(0, 0, 0, 0); + } + + if(nch=='p' || nch=='P') { + BOOL found_sign = FALSE; + int e=0, s=1; + + nch = get(ctx); + if(nch == '-') { + found_sign = TRUE; + s = -1; + nch = get(ctx); + } else if(nch == '+') { + found_sign = TRUE; + nch = get(ctx); + } + if(nch>='0' && nch<='9') { + while(nch>='0' && nch<='9') { + if(e>INT_MAX/10 || e*10>INT_MAX-nch+'0') + e = INT_MAX; + else + e = e*10+nch-'0'; + nch = get(ctx); + } + if((nch!=WEOF) && (nch < '0' || nch > '9')) unget(ctx); + e *= s; + + if(e<0 && exp0 && exp>INT_MAX-e) exp = INT_MAX; + else exp += e; + } else { + if(nch != WEOF) unget(ctx); + if(found_sign) unget(ctx); + unget(ctx); + } + } + + return fpnum(sign, exp, m, round); +} +#endif + +/* Converts first 3 limbs to ULONGLONG */ +/* Return FALSE on overflow */ +static inline BOOL bnum_to_mant(struct bnum *b, ULONGLONG *m) +{ + if(UI64_MAX / LIMB_MAX / LIMB_MAX < b->data[bnum_idx(b, b->e-1)]) return FALSE; + *m = (ULONGLONG)b->data[bnum_idx(b, b->e-1)] * LIMB_MAX * LIMB_MAX; + if(b->b == b->e-1) return TRUE; + if(UI64_MAX - *m < (ULONGLONG)b->data[bnum_idx(b, b->e-2)] * LIMB_MAX) return FALSE; + *m += (ULONGLONG)b->data[bnum_idx(b, b->e-2)] * LIMB_MAX; + if(b->b == b->e-2) return TRUE; + if(UI64_MAX - *m < b->data[bnum_idx(b, b->e-3)]) return FALSE; + *m += b->data[bnum_idx(b, b->e-3)]; + return TRUE; +} + +static struct fpnum fpnum_parse_bnum(wchar_t (*get)(void *ctx), void (*unget)(void *ctx), + void *ctx, pthreadlocinfo locinfo, BOOL ldouble, struct bnum *b) +{ +#if _MSVCR_VER >= 140 + const wchar_t _infinity[] = L"infinity"; + const wchar_t _nan[] = L"nan"; + const wchar_t *str_match = NULL; + int matched=0; +#endif + BOOL found_digit = FALSE, found_dp = FALSE, found_sign = FALSE; + int e2 = 0, dp=0, sign=1, off, limb_digits = 0, i; + enum fpmod round = FP_ROUND_ZERO; + wchar_t nch; + ULONGLONG m; + + nch = get(ctx); + if(nch == '-') { + found_sign = TRUE; + sign = -1; + nch = get(ctx); + } else if(nch == '+') { + found_sign = TRUE; + nch = get(ctx); + } + +#if _MSVCR_VER >= 140 + if(nch == _infinity[0] || nch == _toupper(_infinity[0])) + str_match = _infinity; + if(nch == _nan[0] || nch == _toupper(_nan[0])) + str_match = _nan; + while(str_match && nch != WEOF && + (nch == str_match[matched] || nch == _toupper(str_match[matched]))) { + nch = get(ctx); + matched++; + } + if(str_match) { + int keep = 0; + if(matched >= 8) keep = 8; + else if(matched >= 3) keep = 3; + if(nch != WEOF) unget(ctx); + for (; matched > keep; matched--) { + unget(ctx); + } + if(keep) { + if (str_match == _infinity) + return fpnum(sign, 0, 0, FP_VAL_INFINITY); + if (str_match == _nan) + return fpnum(sign, 0, 0, FP_VAL_NAN); + } else if(found_sign) { + unget(ctx); + } + + return fpnum(0, 0, 0, 0); + } + + if(nch == '0') { + found_digit = TRUE; + nch = get(ctx); + if(nch == 'x' || nch == 'X') + return fpnum_parse16(get, unget, ctx, sign, locinfo); + } +#endif + + while(nch == '0') { + found_digit = TRUE; + nch = get(ctx); + } + + b->b = 0; + b->e = 1; + b->data[0] = 0; + while(nch>='0' && nch<='9') { + found_digit = TRUE; + if(limb_digits == LIMB_DIGITS) { + if(bnum_idx(b, b->b-1) == bnum_idx(b, b->e)) break; + else { + b->b--; + b->data[bnum_idx(b, b->b)] = 0; + limb_digits = 0; + } + } + + b->data[bnum_idx(b, b->b)] = b->data[bnum_idx(b, b->b)] * 10 + nch - '0'; + limb_digits++; + nch = get(ctx); + dp++; + } + while(nch>='0' && nch<='9') { + if(nch != '0') b->data[bnum_idx(b, b->b)] |= 1; + nch = get(ctx); + dp++; + } + + if(nch == *locinfo->lconv->decimal_point) { + found_dp = TRUE; + nch = get(ctx); + } + + /* skip leading '0' */ + if(nch=='0' && !limb_digits && !b->b) { + found_digit = TRUE; + while(nch == '0') { + nch = get(ctx); + dp--; + } + } + + while(nch>='0' && nch<='9') { + found_digit = TRUE; + if(limb_digits == LIMB_DIGITS) { + if(bnum_idx(b, b->b-1) == bnum_idx(b, b->e)) break; + else { + b->b--; + b->data[bnum_idx(b, b->b)] = 0; + limb_digits = 0; + } + } + + b->data[bnum_idx(b, b->b)] = b->data[bnum_idx(b, b->b)] * 10 + nch - '0'; + limb_digits++; + nch = get(ctx); + } + while(nch>='0' && nch<='9') { + if(nch != '0') b->data[bnum_idx(b, b->b)] |= 1; + nch = get(ctx); + } + + if(!found_digit) { + if(nch != WEOF) unget(ctx); + if(found_dp) unget(ctx); + if(found_sign) unget(ctx); + return fpnum(0, 0, 0, 0); + } + + if(nch=='e' || nch=='E' || nch=='d' || nch=='D') { + int e=0, s=1; + + nch = get(ctx); + if(nch == '-') { + found_sign = TRUE; + s = -1; + nch = get(ctx); + } else if(nch == '+') { + found_sign = TRUE; + nch = get(ctx); + } else { + found_sign = FALSE; + } + + if(nch>='0' && nch<='9') { + while(nch>='0' && nch<='9') { + if(e>INT_MAX/10 || e*10>INT_MAX-nch+'0') + e = INT_MAX; + else + e = e*10+nch-'0'; + nch = get(ctx); + } + if(nch != WEOF) unget(ctx); + e *= s; + + if(e<0 && dp0 && dp>INT_MAX-e) dp = INT_MAX; + else dp += e; + } else { + if(nch != WEOF) unget(ctx); + if(found_sign) unget(ctx); + unget(ctx); + } + } else if(nch != WEOF) { + unget(ctx); + } + + if(!b->data[bnum_idx(b, b->e-1)]) + return fpnum(sign, 0, 0, 0); + + /* Fill last limb with 0 if needed */ + if(b->b+1 != b->e) { + for(; limb_digits != LIMB_DIGITS; limb_digits++) + b->data[bnum_idx(b, b->b)] *= 10; + } + for(; bnum_idx(b, b->b) < bnum_idx(b, b->e); b->b++) { + if(b->data[bnum_idx(b, b->b)]) break; + } + + /* move decimal point to limb boundary */ + if(limb_digits==dp && b->b==b->e-1) + return fpnum(sign, 0, b->data[bnum_idx(b, b->e-1)], FP_ROUND_ZERO); + off = (dp - limb_digits) % LIMB_DIGITS; + if(off < 0) off += LIMB_DIGITS; + if(off) bnum_mult(b, p10s[off]); + + if(dp-1 > (ldouble ? DBL80_MAX_10_EXP : DBL_MAX_10_EXP)) + return fpnum(sign, INT_MAX, 1, FP_ROUND_ZERO); + /* Count part of exponent stored in denormalized mantissa. */ + /* Increase exponent range to handle subnormals. */ + if(dp-1 < (ldouble ? DBL80_MIN_10_EXP : DBL_MIN_10_EXP-DBL_DIG-18)) + return fpnum(sign, INT_MIN, 1, FP_ROUND_ZERO); + + while(dp > 3*LIMB_DIGITS) { + if(bnum_rshift(b, 9)) dp -= LIMB_DIGITS; + e2 += 9; + } + while(dp <= 2*LIMB_DIGITS) { + if(bnum_lshift(b, 29)) dp += LIMB_DIGITS; + e2 -= 29; + } + /* Make sure most significant mantissa bit will be set */ + while(b->data[bnum_idx(b, b->e-1)] <= 9) { + bnum_lshift(b, 1); + e2--; + } + while(!bnum_to_mant(b, &m)) { + bnum_rshift(b, 1); + e2++; + } + + if(b->e-4 >= b->b && b->data[bnum_idx(b, b->e-4)]) { + if(b->data[bnum_idx(b, b->e-4)] > LIMB_MAX/2) round = FP_ROUND_UP; + else if(b->data[bnum_idx(b, b->e-4)] == LIMB_MAX/2) round = FP_ROUND_EVEN; + else round = FP_ROUND_DOWN; + } + if(round == FP_ROUND_ZERO || round == FP_ROUND_EVEN) { + for(i=b->e-5; i>=b->b; i--) { + if(!b->data[bnum_idx(b, b->b)]) continue; + if(round == FP_ROUND_EVEN) round = FP_ROUND_UP; + else round = FP_ROUND_DOWN; + } + } + + return fpnum(sign, e2, m, round); +} + +struct fpnum fpnum_parse(wchar_t (*get)(void *ctx), void (*unget)(void *ctx), + void *ctx, pthreadlocinfo locinfo, BOOL ldouble) +{ + if(!ldouble) { + BYTE bnum_data[FIELD_OFFSET(struct bnum, data[BNUM_PREC64])]; + struct bnum *b = (struct bnum*)bnum_data; + + b->size = BNUM_PREC64; + return fpnum_parse_bnum(get, unget, ctx, locinfo, ldouble, b); + } else { + BYTE bnum_data[FIELD_OFFSET(struct bnum, data[BNUM_PREC80])]; + struct bnum *b = (struct bnum*)bnum_data; + + b->size = BNUM_PREC80; + return fpnum_parse_bnum(get, unget, ctx, locinfo, ldouble, b); + } +} + +static wchar_t strtod_str_get(void *ctx) +{ + const char **p = ctx; + if (!**p) return WEOF; + return *(*p)++; +} + +static void strtod_str_unget(void *ctx) +{ + const char **p = ctx; + (*p)--; +} + +static inline double strtod_helper(const char *str, char **end, _locale_t locale, int *perr) +{ + pthreadlocinfo locinfo; + const char *beg, *p; + struct fpnum fp; + double ret; + int err; + + if (perr) *perr = 0; +#if _MSVCR_VER == 0 + else *_errno() = 0; +#endif + + if (!MSVCRT_CHECK_PMT(str != NULL)) { + if (end) *end = NULL; + return 0; + } + + if (!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + p = str; + while(_isspace_l((unsigned char)*p, locale)) + p++; + beg = p; + + fp = fpnum_parse(strtod_str_get, strtod_str_unget, &p, locinfo, FALSE); + if (end) *end = (p == beg ? (char*)str : (char*)p); + + err = fpnum_double(&fp, &ret); + if (perr) *perr = err; + else if(err) *_errno() = err; + return ret; +} + +/********************************************************************* + * _strtod_l (MSVCRT.@) + */ +double CDECL _strtod_l(const char *str, char **end, _locale_t locale) +{ + return strtod_helper(str, end, locale, NULL); +} + +/********************************************************************* + * strtod (MSVCRT.@) + */ +double CDECL strtod( const char *str, char **end ) +{ + return _strtod_l( str, end, NULL ); +} + +#if _MSVCR_VER>=120 + +/********************************************************************* + * strtof_l (MSVCR120.@) + */ +float CDECL _strtof_l( const char *str, char **end, _locale_t locale ) +{ + double ret = _strtod_l(str, end, locale); + if (ret && isfinite(ret)) { + float f = ret; + if (!f || !isfinite(f)) + *_errno() = ERANGE; + } + return ret; +} + +/********************************************************************* + * strtof (MSVCR120.@) + */ +float CDECL strtof( const char *str, char **end ) +{ + return _strtof_l(str, end, NULL); +} + +#endif /* _MSVCR_VER>=120 */ + +/********************************************************************* + * atof (MSVCRT.@) + */ +double CDECL atof( const char *str ) +{ + return _strtod_l(str, NULL, NULL); +} + +/********************************************************************* + * _atof_l (MSVCRT.@) + */ +double CDECL _atof_l( const char *str, _locale_t locale) +{ + return _strtod_l(str, NULL, locale); +} + +/********************************************************************* + * _atoflt_l (MSVCRT.@) + */ +int CDECL _atoflt_l(_CRT_FLOAT *value, char *str, _locale_t locale) +{ + double d; + int err; + + d = strtod_helper(str, NULL, locale, &err); + value->f = d; + if(isinf(value->f)) + return _OVERFLOW; + if((d!=0 || err) && value->f>-FLT_MIN && value->fx = strtod_helper(str, NULL, locale, &err); + if(isinf(value->x)) + return _OVERFLOW; + if((value->x!=0 || err) && value->x>-DBL_MIN && value->xlocinfo; + + if(!locinfo->lc_handle[LC_COLLATE]) + return strcmp(str1, str2); + return CompareStringA(locinfo->lc_handle[LC_COLLATE], SORT_STRINGSORT, + str1, -1, str2, -1)-CSTR_EQUAL; +} + +/********************************************************************* + * strcoll (MSVCRT.@) + */ +int CDECL strcoll( const char* str1, const char* str2 ) +{ + return _strcoll_l(str1, str2, NULL); +} + +/********************************************************************* + * _stricoll_l (MSVCRT.@) + */ +int CDECL _stricoll_l( const char* str1, const char* str2, _locale_t locale ) +{ + pthreadlocinfo locinfo; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(!locinfo->lc_handle[LC_COLLATE]) + return _stricmp(str1, str2); + return CompareStringA(locinfo->lc_handle[LC_COLLATE], NORM_IGNORECASE, + str1, -1, str2, -1)-CSTR_EQUAL; +} + +/********************************************************************* + * _stricoll (MSVCRT.@) + */ +int CDECL _stricoll( const char* str1, const char* str2 ) +{ + return _stricoll_l(str1, str2, NULL); +} + +/********************************************************************* + * _strncoll_l (MSVCRT.@) + */ +int CDECL _strncoll_l( const char* str1, const char* str2, size_t count, _locale_t locale ) +{ + pthreadlocinfo locinfo; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(!locinfo->lc_handle[LC_COLLATE]) + return strncmp(str1, str2, count); + return CompareStringA(locinfo->lc_handle[LC_COLLATE], SORT_STRINGSORT, + str1, strnlen(str1, count), + str2, strnlen(str2, count))-CSTR_EQUAL; +} + +/********************************************************************* + * _strncoll (MSVCRT.@) + */ +int CDECL _strncoll( const char* str1, const char* str2, size_t count ) +{ + return _strncoll_l(str1, str2, count, NULL); +} + +/********************************************************************* + * _strnicoll_l (MSVCRT.@) + */ +int CDECL _strnicoll_l( const char* str1, const char* str2, size_t count, _locale_t locale ) +{ + pthreadlocinfo locinfo; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(!locinfo->lc_handle[LC_COLLATE]) + return _strnicmp(str1, str2, count); + return CompareStringA(locinfo->lc_handle[LC_COLLATE], NORM_IGNORECASE, + str1, strnlen(str1, count), + str2, strnlen(str2, count))-CSTR_EQUAL; +} + +/********************************************************************* + * _strnicoll (MSVCRT.@) + */ +int CDECL _strnicoll( const char* str1, const char* str2, size_t count ) +{ + return _strnicoll_l(str1, str2, count, NULL); +} + +/********************************************************************* + * strncpy (MSVCRT.@) + */ +char* __cdecl strncpy(char *dst, const char *src, size_t len) +{ + size_t i; + + for(i=0; i INT_MAX) { + FIXME("len > INT_MAX not supported\n"); + len = INT_MAX; + } + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(!locinfo->lc_handle[LC_COLLATE]) { + strncpy(dest, src, len); + return strlen(src); + } + + ret = LCMapStringA(locinfo->lc_handle[LC_COLLATE], + LCMAP_SORTKEY, src, -1, NULL, 0); + if(!ret) { + if(len) dest[0] = 0; + *_errno() = EILSEQ; + return INT_MAX; + } + if(!len) return ret-1; + + if(ret > len) { + dest[0] = 0; + *_errno() = ERANGE; + return ret-1; + } + + return LCMapStringA(locinfo->lc_handle[LC_COLLATE], + LCMAP_SORTKEY, src, -1, dest, len) - 1; +} + +/********************************************************************* + * strxfrm (MSVCRT.@) + */ +size_t CDECL strxfrm( char *dest, const char *src, size_t len ) +{ + return _strxfrm_l(dest, src, len, NULL); +} + +/******************************************************************** + * __STRINGTOLD_L (MSVCR80.@) + */ +int CDECL __STRINGTOLD_L( MSVCRT__LDOUBLE *value, char **endptr, + const char *str, int flags, _locale_t locale ) +{ + pthreadlocinfo locinfo; + const char *beg, *p; + int err, ret = 0; + struct fpnum fp; + + if (flags) FIXME("flags not supported: %x\n", flags); + + if (!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + p = str; + while (_isspace_l((unsigned char)*p, locale)) + p++; + beg = p; + + fp = fpnum_parse(strtod_str_get, strtod_str_unget, &p, locinfo, TRUE); + if (endptr) *endptr = (p == beg ? (char*)str : (char*)p); + if (p == beg) ret = 4; + + err = fpnum_ldouble(&fp, value); + if (err) ret = (value->x80[2] & 0x7fff ? 2 : 1); + return ret; +} + +/******************************************************************** + * __STRINGTOLD (MSVCRT.@) + */ +int CDECL __STRINGTOLD( MSVCRT__LDOUBLE *value, char **endptr, const char *str, int flags ) +{ + return __STRINGTOLD_L( value, endptr, str, flags, NULL ); +} + +/******************************************************************** + * _atoldbl_l (MSVCRT.@) + */ +int CDECL _atoldbl_l( MSVCRT__LDOUBLE *value, char *str, _locale_t locale ) +{ + char *endptr; + switch(__STRINGTOLD_L( value, &endptr, str, 0, locale )) + { + case 1: return _UNDERFLOW; + case 2: return _OVERFLOW; + default: return 0; + } +} + +/******************************************************************** + * _atoldbl (MSVCRT.@) + */ +int CDECL _atoldbl(_LDOUBLE *value, char *str) +{ + return _atoldbl_l( (MSVCRT__LDOUBLE*)value, str, NULL ); +} + +/********************************************************************* + * strlen (MSVCRT.@) + */ +size_t __cdecl strlen(const char *str) +{ + const char *s = str; + while (*s) s++; + return s - str; +} + +/****************************************************************** + * strnlen (MSVCRT.@) + */ +size_t CDECL strnlen(const char *s, size_t maxlen) +{ + size_t i; + + for(i=0; i= 2)) return 0; + if (!MSVCRT_CHECK_PMT(base <= 36)) return 0; + + while(_isspace_l((unsigned char)*nptr, locale)) nptr++; + + if(*nptr == '-') { + negative = TRUE; + nptr++; + } else if(*nptr == '+') + nptr++; + + if((base==0 || base==16) && *nptr=='0' && _tolower_l(*(nptr+1), locale)=='x') { + base = 16; + nptr += 2; + } + + if(base == 0) { + if(*nptr=='0') + base = 8; + else + base = 10; + } + + while(*nptr) { + char cur = _tolower_l(*nptr, locale); + int v; + + if(cur>='0' && cur<='9') { + if(cur >= '0'+base) + break; + v = cur-'0'; + } else { + if(cur<'a' || cur>='a'+base-10) + break; + v = cur-'a'+10; + } + got_digit = TRUE; + + if(negative) + v = -v; + + nptr++; + + if(!negative && (ret>I64_MAX/base || ret*base>I64_MAX-v)) { + ret = I64_MAX; + *_errno() = ERANGE; + } else if(negative && (ret INT_MAX) { + ret = INT_MAX; + *_errno() = ERANGE; + } else if(ret < INT_MIN) { + ret = INT_MIN; + *_errno() = ERANGE; + } + return ret; +} + +/********************************************************************* + * atoi (MSVCRT.@) + */ +#if _MSVCR_VER == 0 +int __cdecl atoi(const char *str) +{ + BOOL minus = FALSE; + int ret = 0; + + if(!str) + return 0; + + while(_isspace_l((unsigned char)*str, NULL)) str++; + + if(*str == '+') { + str++; + }else if(*str == '-') { + minus = TRUE; + str++; + } + + while(*str>='0' && *str<='9') { + ret = ret*10+*str-'0'; + str++; + } + + return minus ? -ret : ret; +} +#else +int CDECL atoi(const char *str) +{ + return _atoi_l(str, NULL); +} +#endif + +/****************************************************************** + * _atoi64_l (MSVCRT.@) + */ +__int64 CDECL _atoi64_l(const char *str, _locale_t locale) +{ + return _strtoi64_l(str, NULL, 10, locale); +} + +/****************************************************************** + * _atoi64 (MSVCRT.@) + */ +__int64 CDECL _atoi64(const char *str) +{ + return _strtoi64_l(str, NULL, 10, NULL); +} + +/****************************************************************** + * _atol_l (MSVCRT.@) + */ +__msvcrt_long CDECL _atol_l(const char *str, _locale_t locale) +{ + __int64 ret = _strtoi64_l(str, NULL, 10, locale); + + if(ret > LONG_MAX) { + ret = LONG_MAX; + *_errno() = ERANGE; + } else if(ret < LONG_MIN) { + ret = LONG_MIN; + *_errno() = ERANGE; + } + return ret; +} + +/****************************************************************** + * atol (MSVCRT.@) + */ +__msvcrt_long CDECL atol(const char *str) +{ +#if _MSVCR_VER == 0 + return atoi(str); +#else + return _atol_l(str, NULL); +#endif +} + +#if _MSVCR_VER>=120 + +/****************************************************************** + * _atoll_l (MSVCR120.@) + */ +__int64 CDECL _atoll_l(const char* str, _locale_t locale) +{ + return _strtoi64_l(str, NULL, 10, locale); +} + +/****************************************************************** + * atoll (MSVCR120.@) + */ +__int64 CDECL atoll(const char* str) +{ + return _atoll_l(str, NULL); +} + +#endif /* _MSVCR_VER>=120 */ + +/****************************************************************** + * _strtol_l (MSVCRT.@) + */ +__msvcrt_long CDECL _strtol_l(const char* nptr, + char** end, int base, _locale_t locale) +{ + __int64 ret = _strtoi64_l(nptr, end, base, locale); + + if(ret > LONG_MAX) { + ret = LONG_MAX; + *_errno() = ERANGE; + } else if(ret < LONG_MIN) { + ret = LONG_MIN; + *_errno() = ERANGE; + } + + return ret; +} + +/****************************************************************** + * strtol (MSVCRT.@) + */ +__msvcrt_long CDECL strtol(const char* nptr, char** end, int base) +{ + return _strtol_l(nptr, end, base, NULL); +} + +/****************************************************************** + * _strtoul_l (MSVCRT.@) + */ +__msvcrt_ulong CDECL _strtoul_l(const char* nptr, char** end, int base, _locale_t locale) +{ + __int64 ret = _strtoi64_l(nptr, end, base, locale); + + if(ret > ULONG_MAX) { + ret = ULONG_MAX; + *_errno() = ERANGE; + }else if(ret < -(__int64)ULONG_MAX) { + ret = 1; + *_errno() = ERANGE; + } + + return ret; +} + +/****************************************************************** + * strtoul (MSVCRT.@) + */ +__msvcrt_ulong CDECL strtoul(const char* nptr, char** end, int base) +{ + return _strtoul_l(nptr, end, base, NULL); +} + +/********************************************************************* + * _strtoui64_l (MSVCRT.@) + * + * FIXME: locale parameter is ignored + */ +unsigned __int64 CDECL _strtoui64_l(const char *nptr, char **endptr, int base, _locale_t locale) +{ + const char *p = nptr; + BOOL negative = FALSE; + BOOL got_digit = FALSE; + unsigned __int64 ret = 0; + + TRACE("(%s %p %d %p)\n", debugstr_a(nptr), endptr, base, locale); + + if (!MSVCRT_CHECK_PMT(nptr != NULL)) return 0; + if (!MSVCRT_CHECK_PMT(base == 0 || base >= 2)) return 0; + if (!MSVCRT_CHECK_PMT(base <= 36)) return 0; + + while(_isspace_l((unsigned char)*nptr, locale)) nptr++; + + if(*nptr == '-') { + negative = TRUE; + nptr++; + } else if(*nptr == '+') + nptr++; + + if((base==0 || base==16) && *nptr=='0' && _tolower_l(*(nptr+1), locale)=='x') { + base = 16; + nptr += 2; + } + + if(base == 0) { + if(*nptr=='0') + base = 8; + else + base = 10; + } + + while(*nptr) { + char cur = _tolower_l(*nptr, locale); + int v; + + if(cur>='0' && cur<='9') { + if(cur >= '0'+base) + break; + v = *nptr-'0'; + } else { + if(cur<'a' || cur>='a'+base-10) + break; + v = cur-'a'+10; + } + got_digit = TRUE; + + nptr++; + + if(ret>UI64_MAX/base || ret*base>UI64_MAX-v) { + ret = UI64_MAX; + *_errno() = ERANGE; + } else + ret = ret*base + v; + } + + if(endptr) + *endptr = (char*)(got_digit ? nptr : p); + + return negative ? -ret : ret; +} + +/********************************************************************* + * _strtoui64 (MSVCRT.@) + */ +unsigned __int64 CDECL _strtoui64(const char *nptr, char **endptr, int base) +{ + return _strtoui64_l(nptr, endptr, base, NULL); +} + +static int ltoa_helper(__msvcrt_long value, char *str, size_t size, int radix) +{ + __msvcrt_ulong val; + unsigned int digit; + BOOL is_negative; + char buffer[33], *pos; + size_t len; + + if (value < 0 && radix == 10) + { + is_negative = TRUE; + val = -value; + } + else + { + is_negative = FALSE; + val = value; + } + + pos = buffer + 32; + *pos = '\0'; + + do + { + digit = val % radix; + val /= radix; + + if (digit < 10) + *--pos = '0' + digit; + else + *--pos = 'a' + digit - 10; + } + while (val != 0); + + if (is_negative) + *--pos = '-'; + + len = buffer + 33 - pos; + if (len > size) + { + size_t i; + char *p = str; + + /* Copy the temporary buffer backwards up to the available number of + * characters. Don't copy the negative sign if present. */ + + if (is_negative) + { + p++; + size--; + } + + for (pos = buffer + 31, i = 0; i < size; i++) + *p++ = *pos--; + + str[0] = '\0'; + MSVCRT_INVALID_PMT("str[size] is too small", ERANGE); + return ERANGE; + } + + memcpy(str, pos, len); + return 0; +} + +static int ltow_helper(__msvcrt_long value, wchar_t *str, size_t size, int radix) +{ + __msvcrt_ulong val; + unsigned int digit; + BOOL is_negative; + wchar_t buffer[33], *pos; + size_t len; + + if (value < 0 && radix == 10) + { + is_negative = TRUE; + val = -value; + } + else + { + is_negative = FALSE; + val = value; + } + + pos = buffer + 32; + *pos = '\0'; + + do + { + digit = val % radix; + val /= radix; + + if (digit < 10) + *--pos = '0' + digit; + else + *--pos = 'a' + digit - 10; + } + while (val != 0); + + if (is_negative) + *--pos = '-'; + + len = buffer + 33 - pos; + if (len > size) + { + size_t i; + wchar_t *p = str; + + /* Copy the temporary buffer backwards up to the available number of + * characters. Don't copy the negative sign if present. */ + + if (is_negative) + { + p++; + size--; + } + + for (pos = buffer + 31, i = 0; i < size; i++) + *p++ = *pos--; + + str[0] = '\0'; + MSVCRT_INVALID_PMT("str[size] is too small", ERANGE); + return ERANGE; + } + + memcpy(str, pos, len * sizeof(wchar_t)); + return 0; +} + +/********************************************************************* + * _ltoa_s (MSVCRT.@) + */ +int CDECL _ltoa_s(__msvcrt_long value, char *str, size_t size, int radix) +{ + if (!MSVCRT_CHECK_PMT(str != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(size > 0)) return EINVAL; + if (!MSVCRT_CHECK_PMT(radix >= 2 && radix <= 36)) + { + str[0] = '\0'; + return EINVAL; + } + + return ltoa_helper(value, str, size, radix); +} + +/********************************************************************* + * _ltow_s (MSVCRT.@) + */ +int CDECL _ltow_s(__msvcrt_long value, wchar_t *str, size_t size, int radix) +{ + if (!MSVCRT_CHECK_PMT(str != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(size > 0)) return EINVAL; + if (!MSVCRT_CHECK_PMT(radix >= 2 && radix <= 36)) + { + str[0] = '\0'; + return EINVAL; + } + + return ltow_helper(value, str, size, radix); +} + +/********************************************************************* + * _itoa_s (MSVCRT.@) + */ +int CDECL _itoa_s(int value, char *str, size_t size, int radix) +{ + return _ltoa_s(value, str, size, radix); +} + +/********************************************************************* + * _itoa (MSVCRT.@) + */ +char* CDECL _itoa(int value, char *str, int radix) +{ + return ltoa_helper(value, str, SIZE_MAX, radix) ? NULL : str; +} + +/********************************************************************* + * _ltoa (MSVCRT.@) + */ +char* CDECL _ltoa(__msvcrt_long value, char *str, int radix) +{ + return ltoa_helper(value, str, SIZE_MAX, radix) ? NULL : str; +} + +/********************************************************************* + * _itow_s (MSVCRT.@) + */ +int CDECL _itow_s(int value, wchar_t *str, size_t size, int radix) +{ + return _ltow_s(value, str, size, radix); +} + +/********************************************************************* + * _itow (MSVCRT.@) + */ +wchar_t* CDECL _itow(int value, wchar_t *str, int radix) +{ + return ltow_helper(value, str, SIZE_MAX, radix) ? NULL : str; +} + +/********************************************************************* + * _ltow (MSVCRT.@) + */ +wchar_t* CDECL _ltow(__msvcrt_long value, wchar_t *str, int radix) +{ + return ltow_helper(value, str, SIZE_MAX, radix) ? NULL : str; +} + +/********************************************************************* + * _ultoa (MSVCRT.@) + */ +char* CDECL _ultoa(__msvcrt_ulong value, char *str, int radix) +{ + char buffer[33], *pos; + + pos = &buffer[32]; + *pos = '\0'; + + do { + int digit = value % radix; + value /= radix; + + if (digit < 10) + *--pos = '0' + digit; + else + *--pos = 'a' + digit - 10; + } while (value != 0); + + memcpy(str, pos, buffer + 33 - pos); + return str; +} + +/********************************************************************* + * _ui64toa (MSVCRT.@) + */ +char* CDECL _ui64toa(unsigned __int64 value, char *str, int radix) +{ + char buffer[65], *pos; + + pos = &buffer[64]; + *pos = '\0'; + + do { + int digit = value % radix; + value /= radix; + + if (digit < 10) + *--pos = '0' + digit; + else + *--pos = 'a' + digit - 10; + } while (value != 0); + + memcpy(str, pos, buffer + 65 - pos); + return str; +} + +/********************************************************************* + * _ultow (MSVCRT.@) + */ +wchar_t* CDECL _ultow(__msvcrt_ulong value, wchar_t *str, int radix) +{ + wchar_t buffer[33], *pos; + + pos = &buffer[32]; + *pos = '\0'; + + do { + int digit = value % radix; + value /= radix; + + if (digit < 10) + *--pos = '0' + digit; + else + *--pos = 'a' + digit - 10; + } while (value != 0); + + memcpy(str, pos, (buffer + 33 - pos) * sizeof(wchar_t)); + return str; +} + +/********************************************************************* + * _ui64tow (MSVCRT.@) + */ +wchar_t* CDECL _ui64tow(unsigned __int64 value, wchar_t *str, int radix) +{ + wchar_t buffer[65], *pos; + + pos = &buffer[64]; + *pos = '\0'; + + do { + int digit = value % radix; + value /= radix; + + if (digit < 10) + *--pos = '0' + digit; + else + *--pos = 'a' + digit - 10; + } while (value != 0); + + memcpy(str, pos, (buffer + 65 - pos) * sizeof(wchar_t)); + return str; +} + +/********************************************************************* + * _i64toa (MSVCRT.@) + */ +char* CDECL _i64toa(__int64 value, char *str, int radix) +{ + unsigned __int64 val; + BOOL is_negative; + char buffer[65], *pos; + + if (value < 0 && radix == 10) + { + is_negative = TRUE; + val = -value; + } + else + { + is_negative = FALSE; + val = value; + } + + pos = buffer + 64; + *pos = '\0'; + + do + { + int digit = val % radix; + val /= radix; + + if (digit < 10) + *--pos = '0' + digit; + else + *--pos = 'a' + digit - 10; + } + while (val != 0); + + if (is_negative) + *--pos = '-'; + + memcpy(str, pos, buffer + 65 - pos); + return str; +} + +/********************************************************************* + * _i64tow (MSVCRT.@) + */ +wchar_t* CDECL _i64tow(__int64 value, wchar_t *str, int radix) +{ + unsigned __int64 val; + BOOL is_negative; + wchar_t buffer[65], *pos; + + if (value < 0 && radix == 10) + { + is_negative = TRUE; + val = -value; + } + else + { + is_negative = FALSE; + val = value; + } + + pos = buffer + 64; + *pos = '\0'; + + do + { + int digit = val % radix; + val /= radix; + + if (digit < 10) + *--pos = '0' + digit; + else + *--pos = 'a' + digit - 10; + } + while (val != 0); + + if (is_negative) + *--pos = '-'; + + memcpy(str, pos, (buffer + 65 - pos) * sizeof(wchar_t)); + return str; +} + +/********************************************************************* + * _ui64toa_s (MSVCRT.@) + */ +int CDECL _ui64toa_s(unsigned __int64 value, char *str, + size_t size, int radix) +{ + char buffer[65], *pos; + int digit; + + if (!MSVCRT_CHECK_PMT(str != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(size > 0)) return EINVAL; + if (!MSVCRT_CHECK_PMT(radix >= 2 && radix <= 36)) + { + str[0] = '\0'; + return EINVAL; + } + + pos = buffer+64; + *pos = '\0'; + + do { + digit = value%radix; + value /= radix; + + if(digit < 10) + *--pos = '0'+digit; + else + *--pos = 'a'+digit-10; + }while(value != 0); + + if(buffer-pos+65 > size) { + MSVCRT_INVALID_PMT("str[size] is too small", EINVAL); + return EINVAL; + } + + memcpy(str, pos, buffer-pos+65); + return 0; +} + +/********************************************************************* + * _ui64tow_s (MSVCRT.@) + */ +int CDECL _ui64tow_s( unsigned __int64 value, wchar_t *str, + size_t size, int radix ) +{ + wchar_t buffer[65], *pos; + int digit; + + if (!MSVCRT_CHECK_PMT(str != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(size > 0)) return EINVAL; + if (!MSVCRT_CHECK_PMT(radix >= 2 && radix <= 36)) + { + str[0] = '\0'; + return EINVAL; + } + + pos = &buffer[64]; + *pos = '\0'; + + do { + digit = value % radix; + value = value / radix; + if (digit < 10) + *--pos = '0' + digit; + else + *--pos = 'a' + digit - 10; + } while (value != 0); + + if(buffer-pos+65 > size) { + MSVCRT_INVALID_PMT("str[size] is too small", EINVAL); + return EINVAL; + } + + memcpy(str, pos, (buffer-pos+65)*sizeof(wchar_t)); + return 0; +} + +/********************************************************************* + * _ultoa_s (MSVCRT.@) + */ +int CDECL _ultoa_s(__msvcrt_ulong value, char *str, size_t size, int radix) +{ + __msvcrt_ulong digit; + char buffer[33], *pos; + size_t len; + + if (!str || !size || radix < 2 || radix > 36) + { + if (str && size) + str[0] = '\0'; + + *_errno() = EINVAL; + return EINVAL; + } + + pos = buffer + 32; + *pos = '\0'; + + do + { + digit = value % radix; + value /= radix; + + if (digit < 10) + *--pos = '0' + digit; + else + *--pos = 'a' + digit - 10; + } + while (value != 0); + + len = buffer + 33 - pos; + if (len > size) + { + size_t i; + char *p = str; + + /* Copy the temporary buffer backwards up to the available number of + * characters. */ + + for (pos = buffer + 31, i = 0; i < size; i++) + *p++ = *pos--; + + str[0] = '\0'; + *_errno() = ERANGE; + return ERANGE; + } + + memcpy(str, pos, len); + return 0; +} + +/********************************************************************* + * _ultow_s (MSVCRT.@) + */ +int CDECL _ultow_s(__msvcrt_ulong value, wchar_t *str, size_t size, int radix) +{ + __msvcrt_ulong digit; + WCHAR buffer[33], *pos; + size_t len; + + if (!str || !size || radix < 2 || radix > 36) + { + if (str && size) + str[0] = '\0'; + + *_errno() = EINVAL; + return EINVAL; + } + + pos = buffer + 32; + *pos = '\0'; + + do + { + digit = value % radix; + value /= radix; + + if (digit < 10) + *--pos = '0' + digit; + else + *--pos = 'a' + digit - 10; + } + while (value != 0); + + len = buffer + 33 - pos; + if (len > size) + { + size_t i; + WCHAR *p = str; + + /* Copy the temporary buffer backwards up to the available number of + * characters. */ + + for (pos = buffer + 31, i = 0; i < size; i++) + *p++ = *pos--; + + str[0] = '\0'; + *_errno() = ERANGE; + return ERANGE; + } + + memcpy(str, pos, len * sizeof(wchar_t)); + return 0; +} + +/********************************************************************* + * _i64toa_s (MSVCRT.@) + */ +int CDECL _i64toa_s(__int64 value, char *str, size_t size, int radix) +{ + unsigned __int64 val; + unsigned int digit; + BOOL is_negative; + char buffer[65], *pos; + size_t len; + + if (!MSVCRT_CHECK_PMT(str != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(size > 0)) return EINVAL; + if (!MSVCRT_CHECK_PMT(radix >= 2 && radix <= 36)) + { + str[0] = '\0'; + return EINVAL; + } + + if (value < 0 && radix == 10) + { + is_negative = TRUE; + val = -value; + } + else + { + is_negative = FALSE; + val = value; + } + + pos = buffer + 64; + *pos = '\0'; + + do + { + digit = val % radix; + val /= radix; + + if (digit < 10) + *--pos = '0' + digit; + else + *--pos = 'a' + digit - 10; + } + while (val != 0); + + if (is_negative) + *--pos = '-'; + + len = buffer + 65 - pos; + if (len > size) + { + size_t i; + char *p = str; + + /* Copy the temporary buffer backwards up to the available number of + * characters. Don't copy the negative sign if present. */ + + if (is_negative) + { + p++; + size--; + } + + for (pos = buffer + 63, i = 0; i < size; i++) + *p++ = *pos--; + + str[0] = '\0'; + MSVCRT_INVALID_PMT("str[size] is too small", ERANGE); + return ERANGE; + } + + memcpy(str, pos, len); + return 0; +} + +/********************************************************************* + * _i64tow_s (MSVCRT.@) + */ +int CDECL _i64tow_s(__int64 value, wchar_t *str, size_t size, int radix) +{ + unsigned __int64 val; + unsigned int digit; + BOOL is_negative; + wchar_t buffer[65], *pos; + size_t len; + + if (!MSVCRT_CHECK_PMT(str != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(size > 0)) return EINVAL; + if (!MSVCRT_CHECK_PMT(radix >= 2 && radix <= 36)) + { + str[0] = '\0'; + return EINVAL; + } + + if (value < 0 && radix == 10) + { + is_negative = TRUE; + val = -value; + } + else + { + is_negative = FALSE; + val = value; + } + + pos = buffer + 64; + *pos = '\0'; + + do + { + digit = val % radix; + val /= radix; + + if (digit < 10) + *--pos = '0' + digit; + else + *--pos = 'a' + digit - 10; + } + while (val != 0); + + if (is_negative) + *--pos = '-'; + + len = buffer + 65 - pos; + if (len > size) + { + size_t i; + wchar_t *p = str; + + /* Copy the temporary buffer backwards up to the available number of + * characters. Don't copy the negative sign if present. */ + + if (is_negative) + { + p++; + size--; + } + + for (pos = buffer + 63, i = 0; i < size; i++) + *p++ = *pos--; + + str[0] = '\0'; + MSVCRT_INVALID_PMT("str[size] is too small", ERANGE); + return ERANGE; + } + + memcpy(str, pos, len * sizeof(wchar_t)); + return 0; +} + +#define I10_OUTPUT_MAX_PREC 21 +/* Internal structure used by $I10_OUTPUT */ +struct _I10_OUTPUT_DATA { + short pos; + char sign; + BYTE len; + char str[I10_OUTPUT_MAX_PREC+1]; /* add space for '\0' */ +}; + +/********************************************************************* + * $I10_OUTPUT (MSVCRT.@) + * ld80 - long double (Intel 80 bit FP in 12 bytes) to be printed to data + * prec - precision of part, we're interested in + * flag - 0 for first prec digits, 1 for fractional part + * data - data to be populated + * + * return value + * 0 if given double is NaN or INF + * 1 otherwise + * + * FIXME + * Native sets last byte of data->str to '0' or '9', I don't know what + * it means. Current implementation sets it always to '0'. + */ +int CDECL I10_OUTPUT(MSVCRT__LDOUBLE ld80, int prec, int flag, struct _I10_OUTPUT_DATA *data) +{ + struct fpnum num; + double d; + char format[8]; + char buf[I10_OUTPUT_MAX_PREC+9]; /* 9 = strlen("0.e+0000") + '\0' */ + char *p; + + if ((ld80.x80[2] & 0x7fff) == 0x7fff) + { + if (ld80.x80[0] == 0 && ld80.x80[1] == 0x80000000) + strcpy( data->str, "1#INF" ); + else + strcpy( data->str, (ld80.x80[1] & 0x40000000) ? "1#QNAN" : "1#SNAN" ); + data->pos = 1; + data->sign = (ld80.x80[2] & 0x8000) ? '-' : ' '; + data->len = strlen(data->str); + return 0; + } + + num.sign = (ld80.x80[2] & 0x8000) ? -1 : 1; + num.exp = (ld80.x80[2] & 0x7fff) - 0x3fff - 63; + num.m = ld80.x80[0] | ((ULONGLONG)ld80.x80[1] << 32); + num.mod = FP_ROUND_EVEN; + fpnum_double( &num, &d ); + TRACE("(%lf %d %x %p)\n", d, prec, flag, data); + + if(d<0) { + data->sign = '-'; + d = -d; + } else + data->sign = ' '; + + if(flag&1) { + int exp = 1 + floor(log10(d)); + + prec += exp; + if(exp < 0) + prec--; + } + prec--; + + if(prec+1 > I10_OUTPUT_MAX_PREC) + prec = I10_OUTPUT_MAX_PREC-1; + else if(prec < 0) { + d = 0.0; + prec = 0; + } + + sprintf(format, "%%.%dle", prec); + sprintf(buf, format, d); + + buf[1] = buf[0]; + data->pos = atoi(buf+prec+3); + if(buf[1] != '0') + data->pos++; + + for(p = buf+prec+1; p>buf+1 && *p=='0'; p--); + data->len = p-buf; + + memcpy(data->str, buf+1, data->len); + data->str[data->len] = '\0'; + + if(buf[1]!='0' && prec-data->len+1>0) + memcpy(data->str+data->len+1, buf+data->len+1, prec-data->len+1); + + return 1; +} +#undef I10_OUTPUT_MAX_PREC + +static inline int memcmp_bytes(const void *ptr1, const void *ptr2, size_t n) +{ + const unsigned char *p1, *p2; + + for (p1 = ptr1, p2 = ptr2; n; n--, p1++, p2++) + { + if (*p1 != *p2) + return *p1 > *p2 ? 1 : -1; + } + return 0; +} + +static inline int memcmp_blocks(const void *ptr1, const void *ptr2, size_t size) +{ + typedef uint64_t DECLSPEC_ALIGN(1) unaligned_ui64; + + const uint64_t *p1 = ptr1; + const unaligned_ui64 *p2 = ptr2; + size_t remainder = size & (sizeof(uint64_t) - 1); + size_t block_count = size / sizeof(uint64_t); + + while (block_count) + { + if (*p1 != *p2) + return memcmp_bytes(p1, p2, sizeof(uint64_t)); + + p1++; + p2++; + block_count--; + } + + return memcmp_bytes(p1, p2, remainder); +} + +/********************************************************************* + * memcmp (MSVCRT.@) + */ +int __cdecl memcmp(const void *ptr1, const void *ptr2, size_t n) +{ + const unsigned char *p1 = ptr1, *p2 = ptr2; + size_t align; + int result; + + if (n < sizeof(uint64_t)) + return memcmp_bytes(p1, p2, n); + + align = -(size_t)p1 & (sizeof(uint64_t) - 1); + + if ((result = memcmp_bytes(p1, p2, align))) + return result; + + p1 += align; + p2 += align; + n -= align; + + return memcmp_blocks(p1, p2, n); +} + +#if defined(__i386__) || (defined(__x86_64__) && !defined(__arm64ec__)) + +#ifdef __i386__ + +#define DEST_REG "%edi" +#define SRC_REG "%esi" +#define LEN_REG "%ecx" +#define TMP_REG "%edx" + +#define MEMMOVE_INIT \ + "pushl " SRC_REG "\n\t" \ + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") \ + "pushl " DEST_REG "\n\t" \ + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") \ + "movl 12(%esp), " DEST_REG "\n\t" \ + "movl 16(%esp), " SRC_REG "\n\t" \ + "movl 20(%esp), " LEN_REG "\n\t" + +#define MEMMOVE_CLEANUP \ + "movl 12(%esp), %eax\n\t" \ + "popl " DEST_REG "\n\t" \ + __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t") \ + "popl " SRC_REG "\n\t" \ + __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t") + +#else + +#define DEST_REG "%rdi" +#define SRC_REG "%rsi" +#define LEN_REG "%r8" +#define TMP_REG "%r9" + +#define MEMMOVE_INIT \ + "pushq " SRC_REG "\n\t" \ + __ASM_SEH(".seh_pushreg " SRC_REG "\n\t") \ + __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t") \ + "pushq " DEST_REG "\n\t" \ + __ASM_SEH(".seh_pushreg " DEST_REG "\n\t") \ + __ASM_SEH(".seh_endprologue\n\t") \ + __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t") \ + "movq %rcx, " DEST_REG "\n\t" \ + "movq %rdx, " SRC_REG "\n\t" + +#define MEMMOVE_CLEANUP \ + "movq %rcx, %rax\n\t" \ + "popq " DEST_REG "\n\t" \ + __ASM_CFI(".cfi_adjust_cfa_offset -8\n\t") \ + "popq " SRC_REG "\n\t" \ + __ASM_CFI(".cfi_adjust_cfa_offset -8\n\t") +#endif + +void * __cdecl sse2_memmove(void *dst, const void *src, size_t n); +__ASM_GLOBAL_FUNC( sse2_memmove, + MEMMOVE_INIT + "mov " DEST_REG ", " TMP_REG "\n\t" /* check copying direction */ + "sub " SRC_REG ", " TMP_REG "\n\t" + "cmp " LEN_REG ", " TMP_REG "\n\t" + "jb copy_bwd\n\t" + /* copy forwards */ + "cmp $4, " LEN_REG "\n\t" /* 4-bytes align */ + "jb copy_fwd3\n\t" + "mov " DEST_REG ", " TMP_REG "\n\t" + "shr $1, " TMP_REG "\n\t" + "jnc 1f\n\t" + "movsb\n\t" + "dec " LEN_REG "\n\t" + "inc " TMP_REG "\n\t" + "1:\n\t" + "shr $1, " TMP_REG "\n\t" + "jnc 1f\n\t" + "movsw\n\t" + "sub $2, " LEN_REG "\n\t" + "inc " TMP_REG "\n\t" + "1:\n\t" /* 16-bytes align */ + "cmp $16, " LEN_REG "\n\t" + "jb copy_fwd15\n\t" + "shr $1, " TMP_REG "\n\t" + "jnc 1f\n\t" + "movsl\n\t" + "sub $4, " LEN_REG "\n\t" + "inc " TMP_REG "\n\t" + "1:\n\t" + "shr $1, " TMP_REG "\n\t" + "jnc 1f\n\t" + "movsl\n\t" + "movsl\n\t" + "sub $8, " LEN_REG "\n\t" + "1:\n\t" + "cmp $64, " LEN_REG "\n\t" + "jb copy_fwd63\n\t" + "1:\n\t" /* copy 64-bytes blocks in loop, dest 16-bytes aligned */ + "movdqu 0x00(" SRC_REG "), %xmm0\n\t" + "movdqu 0x10(" SRC_REG "), %xmm1\n\t" + "movdqu 0x20(" SRC_REG "), %xmm2\n\t" + "movdqu 0x30(" SRC_REG "), %xmm3\n\t" + "movdqa %xmm0, 0x00(" DEST_REG ")\n\t" + "movdqa %xmm1, 0x10(" DEST_REG ")\n\t" + "movdqa %xmm2, 0x20(" DEST_REG ")\n\t" + "movdqa %xmm3, 0x30(" DEST_REG ")\n\t" + "add $64, " SRC_REG "\n\t" + "add $64, " DEST_REG "\n\t" + "sub $64, " LEN_REG "\n\t" + "cmp $64, " LEN_REG "\n\t" + "jae 1b\n\t" + "copy_fwd63:\n\t" /* copy last 63 bytes, dest 16-bytes aligned */ + "mov " LEN_REG ", " TMP_REG "\n\t" + "and $15, " LEN_REG "\n\t" + "shr $5, " TMP_REG "\n\t" + "jnc 1f\n\t" + "movdqu 0(" SRC_REG "), %xmm0\n\t" + "movdqa %xmm0, 0(" DEST_REG ")\n\t" + "add $16, " SRC_REG "\n\t" + "add $16, " DEST_REG "\n\t" + "1:\n\t" + "shr $1, " TMP_REG "\n\t" + "jnc copy_fwd15\n\t" + "movdqu 0x00(" SRC_REG "), %xmm0\n\t" + "movdqu 0x10(" SRC_REG "), %xmm1\n\t" + "movdqa %xmm0, 0x00(" DEST_REG ")\n\t" + "movdqa %xmm1, 0x10(" DEST_REG ")\n\t" + "add $32, " SRC_REG "\n\t" + "add $32, " DEST_REG "\n\t" + "copy_fwd15:\n\t" /* copy last 15 bytes, dest 4-bytes aligned */ + "mov " LEN_REG ", " TMP_REG "\n\t" + "and $3, " LEN_REG "\n\t" + "shr $3, " TMP_REG "\n\t" + "jnc 1f\n\t" + "movsl\n\t" + "1:\n\t" + "shr $1, " TMP_REG "\n\t" + "jnc copy_fwd3\n\t" + "movsl\n\t" + "movsl\n\t" + "copy_fwd3:\n\t" /* copy last 3 bytes */ + "shr $1, " LEN_REG "\n\t" + "jnc 1f\n\t" + "movsb\n\t" + "1:\n\t" + "shr $1, " LEN_REG "\n\t" + "jnc 1f\n\t" + "movsw\n\t" + "1:\n\t" + MEMMOVE_CLEANUP + "ret\n\t" + "copy_bwd:\n\t" + "lea (" DEST_REG ", " LEN_REG "), " DEST_REG "\n\t" + "lea (" SRC_REG ", " LEN_REG "), " SRC_REG "\n\t" + "cmp $4, " LEN_REG "\n\t" /* 4-bytes align */ + "jb copy_bwd3\n\t" + "mov " DEST_REG ", " TMP_REG "\n\t" + "shr $1, " TMP_REG "\n\t" + "jnc 1f\n\t" + "dec " SRC_REG "\n\t" + "dec " DEST_REG "\n\t" + "movb (" SRC_REG "), %al\n\t" + "movb %al, (" DEST_REG ")\n\t" + "dec " LEN_REG "\n\t" + "1:\n\t" + "shr $1, " TMP_REG "\n\t" + "jnc 1f\n\t" + "sub $2, " SRC_REG "\n\t" + "sub $2, " DEST_REG "\n\t" + "movw (" SRC_REG "), %ax\n\t" + "movw %ax, (" DEST_REG ")\n\t" + "sub $2, " LEN_REG "\n\t" + "1:\n\t" /* 16-bytes align */ + "cmp $16, " LEN_REG "\n\t" + "jb copy_bwd15\n\t" + "shr $1, " TMP_REG "\n\t" + "jnc 1f\n\t" + "sub $4, " SRC_REG "\n\t" + "sub $4, " DEST_REG "\n\t" + "movl (" SRC_REG "), %eax\n\t" + "movl %eax, (" DEST_REG ")\n\t" + "sub $4, " LEN_REG "\n\t" + "1:\n\t" + "shr $1, " TMP_REG "\n\t" + "jnc 1f\n\t" + "sub $8, " SRC_REG "\n\t" + "sub $8, " DEST_REG "\n\t" + "movl 4(" SRC_REG "), %eax\n\t" + "movl %eax, 4(" DEST_REG ")\n\t" + "movl (" SRC_REG "), %eax\n\t" + "movl %eax, (" DEST_REG ")\n\t" + "sub $8, " LEN_REG "\n\t" + "1:\n\t" + "cmp $64, " LEN_REG "\n\t" + "jb copy_bwd63\n\t" + "1:\n\t" /* copy 64-bytes blocks in loop, dest 16-bytes aligned */ + "sub $64, " SRC_REG "\n\t" + "sub $64, " DEST_REG "\n\t" + "movdqu 0x00(" SRC_REG "), %xmm0\n\t" + "movdqu 0x10(" SRC_REG "), %xmm1\n\t" + "movdqu 0x20(" SRC_REG "), %xmm2\n\t" + "movdqu 0x30(" SRC_REG "), %xmm3\n\t" + "movdqa %xmm0, 0x00(" DEST_REG ")\n\t" + "movdqa %xmm1, 0x10(" DEST_REG ")\n\t" + "movdqa %xmm2, 0x20(" DEST_REG ")\n\t" + "movdqa %xmm3, 0x30(" DEST_REG ")\n\t" + "sub $64, " LEN_REG "\n\t" + "cmp $64, " LEN_REG "\n\t" + "jae 1b\n\t" + "copy_bwd63:\n\t" /* copy last 63 bytes, dest 16-bytes aligned */ + "mov " LEN_REG ", " TMP_REG "\n\t" + "and $15, " LEN_REG "\n\t" + "shr $5, " TMP_REG "\n\t" + "jnc 1f\n\t" + "sub $16, " SRC_REG "\n\t" + "sub $16, " DEST_REG "\n\t" + "movdqu (" SRC_REG "), %xmm0\n\t" + "movdqa %xmm0, (" DEST_REG ")\n\t" + "1:\n\t" + "shr $1, " TMP_REG "\n\t" + "jnc copy_bwd15\n\t" + "sub $32, " SRC_REG "\n\t" + "sub $32, " DEST_REG "\n\t" + "movdqu 0x00(" SRC_REG "), %xmm0\n\t" + "movdqu 0x10(" SRC_REG "), %xmm1\n\t" + "movdqa %xmm0, 0x00(" DEST_REG ")\n\t" + "movdqa %xmm1, 0x10(" DEST_REG ")\n\t" + "copy_bwd15:\n\t" /* copy last 15 bytes, dest 4-bytes aligned */ + "mov " LEN_REG ", " TMP_REG "\n\t" + "and $3, " LEN_REG "\n\t" + "shr $3, " TMP_REG "\n\t" + "jnc 1f\n\t" + "sub $4, " SRC_REG "\n\t" + "sub $4, " DEST_REG "\n\t" + "movl (" SRC_REG "), %eax\n\t" + "movl %eax, (" DEST_REG ")\n\t" + "1:\n\t" + "shr $1, " TMP_REG "\n\t" + "jnc copy_bwd3\n\t" + "sub $8, " SRC_REG "\n\t" + "sub $8, " DEST_REG "\n\t" + "movl 4(" SRC_REG "), %eax\n\t" + "movl %eax, 4(" DEST_REG ")\n\t" + "movl (" SRC_REG "), %eax\n\t" + "movl %eax, (" DEST_REG ")\n\t" + "copy_bwd3:\n\t" /* copy last 3 bytes */ + "shr $1, " LEN_REG "\n\t" + "jnc 1f\n\t" + "dec " SRC_REG "\n\t" + "dec " DEST_REG "\n\t" + "movb (" SRC_REG "), %al\n\t" + "movb %al, (" DEST_REG ")\n\t" + "1:\n\t" + "shr $1, " LEN_REG "\n\t" + "jnc 1f\n\t" + "movw -2(" SRC_REG "), %ax\n\t" + "movw %ax, -2(" DEST_REG ")\n\t" + "1:\n\t" + MEMMOVE_CLEANUP + "ret" ) + +#endif + +/********************************************************************* + * memmove (MSVCRT.@) + */ +#ifdef WORDS_BIGENDIAN +# define MERGE(w1, sh1, w2, sh2) ((w1 << sh1) | (w2 >> sh2)) +#else +# define MERGE(w1, sh1, w2, sh2) ((w1 >> sh1) | (w2 << sh2)) +#endif +void * __cdecl memmove(void *dst, const void *src, size_t n) +{ +#if defined(__x86_64__) && !defined(__arm64ec__) + return sse2_memmove(dst, src, n); +#else + unsigned char *d = dst; + const unsigned char *s = src; + int sh1; + +#ifdef __i386__ + if (sse2_supported) + return sse2_memmove(dst, src, n); +#endif + + if (!n) return dst; + + if ((size_t)dst - (size_t)src >= n) + { + for (; (size_t)d % sizeof(size_t) && n; n--) *d++ = *s++; + + sh1 = 8 * ((size_t)s % sizeof(size_t)); + if (!sh1) + { + while (n >= sizeof(size_t)) + { + *(size_t*)d = *(size_t*)s; + s += sizeof(size_t); + d += sizeof(size_t); + n -= sizeof(size_t); + } + } + else if (n >= 2 * sizeof(size_t)) + { + int sh2 = 8 * sizeof(size_t) - sh1; + size_t x, y; + + s -= sh1 / 8; + x = *(size_t*)s; + do + { + s += sizeof(size_t); + y = *(size_t*)s; + *(size_t*)d = MERGE(x, sh1, y, sh2); + d += sizeof(size_t); + + s += sizeof(size_t); + x = *(size_t*)s; + *(size_t*)d = MERGE(y, sh1, x, sh2); + d += sizeof(size_t); + + n -= 2 * sizeof(size_t); + } while (n >= 2 * sizeof(size_t)); + s += sh1 / 8; + } + while (n--) *d++ = *s++; + return dst; + } + else + { + d += n; + s += n; + + for (; (size_t)d % sizeof(size_t) && n; n--) *--d = *--s; + + sh1 = 8 * ((size_t)s % sizeof(size_t)); + if (!sh1) + { + while (n >= sizeof(size_t)) + { + s -= sizeof(size_t); + d -= sizeof(size_t); + *(size_t*)d = *(size_t*)s; + n -= sizeof(size_t); + } + } + else if (n >= 2 * sizeof(size_t)) + { + int sh2 = 8 * sizeof(size_t) - sh1; + size_t x, y; + + s -= sh1 / 8; + x = *(size_t*)s; + do + { + s -= sizeof(size_t); + y = *(size_t*)s; + d -= sizeof(size_t); + *(size_t*)d = MERGE(y, sh1, x, sh2); + + s -= sizeof(size_t); + x = *(size_t*)s; + d -= sizeof(size_t); + *(size_t*)d = MERGE(x, sh1, y, sh2); + + n -= 2 * sizeof(size_t); + } while (n >= 2 * sizeof(size_t)); + s += sh1 / 8; + } + while (n--) *--d = *--s; + } + return dst; +#endif +} +#undef MERGE + +/********************************************************************* + * memcpy (MSVCRT.@) + */ +void * __cdecl memcpy(void *dst, const void *src, size_t n) +{ + return memmove(dst, src, n); +} + +/********************************************************************* + * _memccpy (MSVCRT.@) + */ +void * __cdecl _memccpy(void *dst, const void *src, int c, size_t n) +{ + unsigned char *d = dst; + const unsigned char *s = src; + while (n--) if ((*d++ = *s++) == (unsigned char)c) return d; + return NULL; +} + + +static inline void memset_aligned_32(unsigned char *d, uint64_t v, size_t n) +{ + unsigned char *end = d + n; + while (d < end) + { + *(uint64_t *)(d + 0) = v; + *(uint64_t *)(d + 8) = v; + *(uint64_t *)(d + 16) = v; + *(uint64_t *)(d + 24) = v; + d += 32; + } +} + +/********************************************************************* + * memset (MSVCRT.@) + */ +void *__cdecl memset(void *dst, int c, size_t n) +{ + typedef uint64_t DECLSPEC_ALIGN(1) unaligned_ui64; + typedef uint32_t DECLSPEC_ALIGN(1) unaligned_ui32; + typedef uint16_t DECLSPEC_ALIGN(1) unaligned_ui16; + + uint64_t v = 0x101010101010101ull * (unsigned char)c; + unsigned char *d = (unsigned char *)dst; + size_t a = 0x20 - ((uintptr_t)d & 0x1f); + + if (n >= 16) + { + *(unaligned_ui64 *)(d + 0) = v; + *(unaligned_ui64 *)(d + 8) = v; + *(unaligned_ui64 *)(d + n - 16) = v; + *(unaligned_ui64 *)(d + n - 8) = v; + if (n <= 32) return dst; + *(unaligned_ui64 *)(d + 16) = v; + *(unaligned_ui64 *)(d + 24) = v; + *(unaligned_ui64 *)(d + n - 32) = v; + *(unaligned_ui64 *)(d + n - 24) = v; + if (n <= 64) return dst; + + n = (n - a) & ~0x1f; + memset_aligned_32(d + a, v, n); + return dst; + } + if (n >= 8) + { + *(unaligned_ui64 *)d = v; + *(unaligned_ui64 *)(d + n - 8) = v; + return dst; + } + if (n >= 4) + { + *(unaligned_ui32 *)d = v; + *(unaligned_ui32 *)(d + n - 4) = v; + return dst; + } + if (n >= 2) + { + *(unaligned_ui16 *)d = v; + *(unaligned_ui16 *)(d + n - 2) = v; + return dst; + } + if (n >= 1) + { + *(uint8_t *)d = v; + return dst; + } + return dst; +} + +/********************************************************************* + * strchr (MSVCRT.@) + */ +char* __cdecl strchr(const char *str, int c) +{ + do + { + if (*str == (char)c) return (char*)str; + } while (*str++); + return NULL; +} + +/********************************************************************* + * strrchr (MSVCRT.@) + */ +char* __cdecl strrchr(const char *str, int c) +{ + char *ret = NULL; + do { if (*str == (char)c) ret = (char*)str; } while (*str++); + return ret; +} + +/********************************************************************* + * memchr (MSVCRT.@) + */ +void* __cdecl memchr(const void *ptr, int c, size_t n) +{ + const unsigned char *p = ptr; + + for (p = ptr; n; n--, p++) if (*p == (unsigned char)c) return (void *)(ULONG_PTR)p; + return NULL; +} + +/********************************************************************* + * strcmp (MSVCRT.@) + */ +int __cdecl strcmp(const char *str1, const char *str2) +{ + while (*str1 && *str1 == *str2) { str1++; str2++; } + if ((unsigned char)*str1 > (unsigned char)*str2) return 1; + if ((unsigned char)*str1 < (unsigned char)*str2) return -1; + return 0; +} + +/********************************************************************* + * strncmp (MSVCRT.@) + */ +int __cdecl strncmp(const char *str1, const char *str2, size_t len) +{ + if (!len) return 0; + while (--len && *str1 && *str1 == *str2) { str1++; str2++; } + +#if defined(_WIN64) || defined(_UCRT) || _MSVCR_VER == 70 || _MSVCR_VER == 71 || _MSVCR_VER >= 110 + if ((unsigned char)*str1 > (unsigned char)*str2) return 1; + if ((unsigned char)*str1 < (unsigned char)*str2) return -1; + return 0; +#else + return (unsigned char)*str1 - (unsigned char)*str2; +#endif +} + +/********************************************************************* + * _strnicmp_l (MSVCRT.@) + */ +int __cdecl _strnicmp_l(const char *s1, const char *s2, + size_t count, _locale_t locale) +{ + pthreadlocinfo locinfo; + int c1, c2; + + if(!count) + return 0; +#if _MSVCR_VER>=80 + if(!MSVCRT_CHECK_PMT(s1 && s2 && count <= INT_MAX)) +#else + /* Old versions of msvcrt.dll didn't have count <= INT_MAX check */ + if(!MSVCRT_CHECK_PMT(s1 && s2)) +#endif /* _MSVCR_VER>=140 */ + return _NLSCMPERROR; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(!locinfo->lc_handle[LC_CTYPE]) + { + do { + if ((c1 = *s1++) >= 'A' && c1 <= 'Z') + c1 -= 'A' - 'a'; + if ((c2 = *s2++) >= 'A' && c2 <= 'Z') + c2 -= 'A' - 'a'; + }while(--count && c1 && c1==c2); + + return (unsigned char)c1 - (unsigned char)c2; + } + + do { + c1 = _tolower_l((unsigned char)*s1++, locale); + c2 = _tolower_l((unsigned char)*s2++, locale); + }while(--count && c1 && c1==c2); + + return c1-c2; +} + +/********************************************************************* + * _stricmp_l (MSVCRT.@) + */ +int __cdecl _stricmp_l(const char *s1, const char *s2, _locale_t locale) +{ + return _strnicmp_l(s1, s2, INT_MAX, locale); +} + +/********************************************************************* + * _strnicmp (MSVCRT.@) + */ +int __cdecl _strnicmp(const char *s1, const char *s2, size_t count) +{ + return _strnicmp_l(s1, s2, count, NULL); +} + +/********************************************************************* + * _stricmp (MSVCRT.@) + */ +int __cdecl _stricmp(const char *s1, const char *s2) +{ + return _strnicmp_l(s1, s2, INT_MAX, NULL); +} + +/********************************************************************* + * strstr (MSVCRT.@) + */ +char* __cdecl strstr(const char *haystack, const char *needle) +{ + size_t i, j, len, needle_len, lps_len; + BYTE lps[256]; + + needle_len = strlen(needle); + if (!needle_len) return (char*)haystack; + lps_len = needle_len > ARRAY_SIZE(lps) ? ARRAY_SIZE(lps) : needle_len; + + lps[0] = 0; + len = 0; + i = 1; + while (i < lps_len) + { + if (needle[i] == needle[len]) lps[i++] = ++len; + else if (len) len = lps[len-1]; + else lps[i++] = 0; + } + + i = j = 0; + while (haystack[i]) + { + while (j < lps_len && haystack[i] && haystack[i] == needle[j]) + { + i++; + j++; + } + + if (j == needle_len) return (char*)haystack + i - j; + else if (j) + { + if (j == ARRAY_SIZE(lps) && !strncmp(haystack + i, needle + j, needle_len - j)) + return (char*)haystack + i - j; + j = lps[j-1]; + } + else if (haystack[i]) i++; + } + return NULL; +} + +/********************************************************************* + * _memicmp_l (MSVCRT.@) + */ +int __cdecl _memicmp_l(const void *v1, const void *v2, size_t len, _locale_t locale) +{ + const char *s1 = v1, *s2 = v2; + int ret = 0; + +#if _MSVCR_VER == 0 || _MSVCR_VER >= 80 + if (!s1 || !s2) + { + if (len) + MSVCRT_INVALID_PMT(NULL, EINVAL); + return len ? _NLSCMPERROR : 0; + } +#endif + + while (len--) + { + if ((ret = _tolower_l(*s1, locale) - _tolower_l(*s2, locale))) + break; + s1++; + s2++; + } + return ret; +} + +/********************************************************************* + * _memicmp (MSVCRT.@) + */ +int __cdecl _memicmp(const void *s1, const void *s2, size_t len) +{ + return _memicmp_l(s1, s2, len, NULL); +} + +/********************************************************************* + * strcspn (MSVCRT.@) + */ +size_t __cdecl strcspn(const char *str, const char *reject) +{ + BOOL rejects[256]; + const char *p; + + memset(rejects, 0, sizeof(rejects)); + + p = reject; + while(*p) + { + rejects[(unsigned char)*p] = TRUE; + p++; + } + + p = str; + while(*p && !rejects[(unsigned char)*p]) p++; + return p - str; +} + +/********************************************************************* + * strspn (MSVCRT.@) + */ +size_t __cdecl strspn(const char *str, const char *accept) +{ + const char *ptr; + for (ptr = str; *ptr; ptr++) if (!strchr( accept, *ptr )) break; + return ptr - str; +} + +/********************************************************************* + * strpbrk (MSVCRT.@) + */ +char* __cdecl strpbrk(const char *str, const char *accept) +{ + for (; *str; str++) if (strchr( accept, *str )) return (char*)str; + return NULL; +} + +/********************************************************************* + * __strncnt (MSVCRT.@) + */ +size_t __cdecl __strncnt(const char *str, size_t size) +{ + size_t ret = 0; + +#if _MSVCR_VER >= 140 + while (*str++ && size--) +#else + while (size-- && *str++) +#endif + { + ret++; + } + + return ret; +} + + +#ifdef _CRTDLL +/********************************************************************* + * _strdec (CRTDLL.@) + */ +char * CDECL _strdec(const char *str1, const char *str2) +{ + return (char *)(str2 - 1); +} + +/********************************************************************* + * _strinc (CRTDLL.@) + */ +char * CDECL _strinc(const char *str) +{ + return (char *)(str + 1); +} + +/********************************************************************* + * _strnextc (CRTDLL.@) + */ +unsigned int CDECL _strnextc(const char *str) +{ + return (unsigned char)str[0]; +} + +/********************************************************************* + * _strninc (CRTDLL.@) + */ +char * CDECL _strninc(const char *str, size_t len) +{ + return (char *)(str + len); +} + +/********************************************************************* + * _strspnp (CRTDLL.@) + */ +char * CDECL _strspnp( const char *str1, const char *str2) +{ + str1 += strspn( str1, str2 ); + return *str1 ? (char*)str1 : NULL; +} +#endif diff --git a/dll/win32/msvcrt/thread.c b/dll/win32/msvcrt/thread.c new file mode 100644 index 00000000000..2a8a99b847b --- /dev/null +++ b/dll/win32/msvcrt/thread.c @@ -0,0 +1,262 @@ +/* + * msvcrt.dll thread functions + * + * Copyright 2000 Jon Griffiths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ +#include +#include "msvcrt.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +/********************************************************************/ + +typedef struct { + HANDLE thread; + union { + _beginthread_start_routine_t start_address; + _beginthreadex_start_routine_t start_address_ex; + }; + void *arglist; +#if _MSVCR_VER >= 140 + HMODULE module; +#endif +} _beginthread_trampoline_t; + +/********************************************************************* + * msvcrt_get_thread_data + * + * Return the thread local storage structure. + */ +thread_data_t *CDECL msvcrt_get_thread_data(void) +{ + thread_data_t *ptr; + DWORD err = GetLastError(); /* need to preserve last error */ + + if (!(ptr = TlsGetValue( msvcrt_tls_index ))) + { + if (!(ptr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ptr) ))) + _amsg_exit( _RT_THREAD ); + if (!TlsSetValue( msvcrt_tls_index, ptr )) _amsg_exit( _RT_THREAD ); + ptr->tid = GetCurrentThreadId(); + ptr->handle = INVALID_HANDLE_VALUE; + ptr->random_seed = 1; + ptr->locinfo = MSVCRT_locale->locinfo; + ptr->mbcinfo = MSVCRT_locale->mbcinfo; + ptr->cached_locale[0] = 'C'; + ptr->cached_locale[1] = 0; +#if _MSVCR_VER >= 140 + ptr->module = NULL; +#endif + } + SetLastError( err ); + return ptr; +} + +/********************************************************************* + * _endthread (MSVCRT.@) + */ +void CDECL _endthread(void) +{ + thread_data_t *tls; + + TRACE("(void)\n"); + + tls = TlsGetValue(msvcrt_tls_index); + if (tls && tls->handle != INVALID_HANDLE_VALUE) + { + CloseHandle(tls->handle); + tls->handle = INVALID_HANDLE_VALUE; + } else + WARN("tls=%p tls->handle=%p\n", tls, tls ? tls->handle : INVALID_HANDLE_VALUE); + + _endthreadex(0); +} + +/********************************************************************* + * _endthreadex (MSVCRT.@) + */ +void CDECL _endthreadex( + unsigned int retval) /* [in] Thread exit code */ +{ + TRACE("(%d)\n", retval); + +#if _MSVCR_VER >= 140 + { + thread_data_t *tls = TlsGetValue(msvcrt_tls_index); + + if (tls && tls->module != NULL) + FreeLibraryAndExitThread(tls->module, retval); + else + WARN("tls=%p tls->module=%p\n", tls, tls ? tls->module : NULL); + } +#endif + + ExitThread(retval); +} + +/********************************************************************* + * _beginthread_trampoline + */ +static DWORD CALLBACK _beginthread_trampoline(LPVOID arg) +{ + _beginthread_trampoline_t local_trampoline; + thread_data_t *data = msvcrt_get_thread_data(); + + memcpy(&local_trampoline,arg,sizeof(local_trampoline)); + free(arg); + data->handle = local_trampoline.thread; +#if _MSVCR_VER >= 140 + data->module = local_trampoline.module; +#endif + + local_trampoline.start_address(local_trampoline.arglist); + _endthread(); +} + +/********************************************************************* + * _beginthread (MSVCRT.@) + */ +uintptr_t CDECL _beginthread( + _beginthread_start_routine_t start_address, /* [in] Start address of routine that begins execution of new thread */ + unsigned int stack_size, /* [in] Stack size for new thread or 0 */ + void *arglist) /* [in] Argument list to be passed to new thread or NULL */ +{ + _beginthread_trampoline_t* trampoline; + HANDLE thread; + + TRACE("(%p, %d, %p)\n", start_address, stack_size, arglist); + + if (!MSVCRT_CHECK_PMT(start_address)) return -1; + + trampoline = malloc(sizeof(*trampoline)); + if(!trampoline) { + *_errno() = EAGAIN; + return -1; + } + + thread = CreateThread(NULL, stack_size, _beginthread_trampoline, + trampoline, CREATE_SUSPENDED, NULL); + if(!thread) { + free(trampoline); + msvcrt_set_errno(GetLastError()); + return -1; + } + + trampoline->thread = thread; + trampoline->start_address = start_address; + trampoline->arglist = arglist; + +#if _MSVCR_VER >= 140 + if(!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + (void*)start_address, &trampoline->module)) + { + trampoline->module = NULL; + WARN("failed to get module for the start_address: %lu\n", GetLastError()); + } +#endif + + if(ResumeThread(thread) == -1) { +#if _MSVCR_VER >= 140 + FreeLibrary(trampoline->module); +#endif + free(trampoline); + *_errno() = EAGAIN; + return -1; + } + + return (uintptr_t)thread; +} + +/********************************************************************* + * _beginthreadex_trampoline + */ +static DWORD CALLBACK _beginthreadex_trampoline(LPVOID arg) +{ + unsigned int retval; + _beginthread_trampoline_t local_trampoline; + thread_data_t *data = msvcrt_get_thread_data(); + + memcpy(&local_trampoline, arg, sizeof(local_trampoline)); + free(arg); + data->handle = local_trampoline.thread; +#if _MSVCR_VER >= 140 + data->module = local_trampoline.module; +#endif + + retval = local_trampoline.start_address_ex(local_trampoline.arglist); + _endthreadex(retval); +} +/********************************************************************* + * _beginthreadex (MSVCRT.@) + */ +uintptr_t CDECL _beginthreadex( + void *security, /* [in] Security descriptor for new thread; must be NULL for Windows 9x applications */ + unsigned int stack_size, /* [in] Stack size for new thread or 0 */ + _beginthreadex_start_routine_t start_address, /* [in] Start address of routine that begins execution of new thread */ + void *arglist, /* [in] Argument list to be passed to new thread or NULL */ + unsigned int initflag, /* [in] Initial state of new thread (0 for running or CREATE_SUSPEND for suspended) */ + unsigned int *thrdaddr) /* [out] Points to a 32-bit variable that receives the thread identifier */ +{ + _beginthread_trampoline_t* trampoline; + HANDLE thread; + + TRACE("(%p, %d, %p, %p, %d, %p)\n", security, stack_size, start_address, arglist, initflag, thrdaddr); + + /* FIXME: may use different errno / return values */ + if (!MSVCRT_CHECK_PMT(start_address)) return 0; + + if (!(trampoline = malloc(sizeof(*trampoline)))) + return 0; + + trampoline->thread = INVALID_HANDLE_VALUE; + trampoline->start_address_ex = start_address; + trampoline->arglist = arglist; + +#if _MSVCR_VER >= 140 + if(!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + (void*)start_address, &trampoline->module)) + { + trampoline->module = NULL; + WARN("failed to get module for the start_address: %lu\n", GetLastError()); + } +#endif + + thread = CreateThread(security, stack_size, _beginthreadex_trampoline, + trampoline, initflag, (DWORD *)thrdaddr); + if(!thread) { +#if _MSVCR_VER >= 140 + FreeLibrary(trampoline->module); +#endif + free(trampoline); + msvcrt_set_errno(GetLastError()); + return 0; + } + + return (uintptr_t)thread; +} + +#if _MSVCR_VER>=80 +/********************************************************************* + * _getptd (MSVCR80.@) + */ +thread_data_t* CDECL _getptd(void) +{ + FIXME("returns undocumented/not fully filled data\n"); + return msvcrt_get_thread_data(); +} +#endif diff --git a/dll/win32/msvcrt/time.c b/dll/win32/msvcrt/time.c new file mode 100644 index 00000000000..8199fe01649 --- /dev/null +++ b/dll/win32/msvcrt/time.c @@ -0,0 +1,1908 @@ +/* + * msvcrt.dll date/time functions + * + * Copyright 1996,1998 Marcus Meissner + * Copyright 1996 Jukka Iivonen + * Copyright 1997,2000 Uwe Bonnes + * Copyright 2000 Jon Griffiths + * Copyright 2004 Hans Leidekker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include +#include +#include +#include + +#include "msvcrt.h" +#include "mtdll.h" +#include "winbase.h" +#include "winnls.h" +#include "winternl.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +#undef _ctime32 +#undef _difftime32 +#undef _gmtime32 +#undef _localtime32 +#undef _mktime32 +#undef _time32 +#undef _wctime32 + +static LONGLONG init_time; + +void msvcrt_init_clock(void) +{ + LARGE_INTEGER systime; + + NtQuerySystemTime(&systime); + init_time = systime.QuadPart; +} + +static const int MonthLengths[2][12] = +{ + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } +}; + +#if _MSVCR_VER>=140 +static const int MAX_SECONDS = 60; +#else +static const int MAX_SECONDS = 59; +#endif + +#if _MSVCR_VER == 0 +#define MIN_GMTIME64_TIME 0 +#define MAX_GMTIME64_TIME _MAX__TIME64_T +#elif _MSVCR_VER >= 140 +#define MIN_GMTIME64_TIME -43200 +#define MAX_GMTIME64_TIME (_MAX__TIME64_T + 1605600) +#else +#define MIN_GMTIME64_TIME -43200 +#define MAX_GMTIME64_TIME (_MAX__TIME64_T + 46800) +#endif + +static inline BOOL IsLeapYear(int Year) +{ + return Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0); +} + +static inline void write_invalid_msvcrt_tm( struct tm *tm ) +{ + tm->tm_sec = -1; + tm->tm_min = -1; + tm->tm_hour = -1; + tm->tm_mday = -1; + tm->tm_mon = -1; + tm->tm_year = -1; + tm->tm_wday = -1; + tm->tm_yday = -1; + tm->tm_isdst = -1; +} + +/********************************************************************* + * _daylight (MSVCRT.@) + */ +int MSVCRT___daylight = 1; + +/********************************************************************* + * _timezone (MSVCRT.@) + */ +__msvcrt_long MSVCRT___timezone = 28800; + +/********************************************************************* + * _dstbias (MSVCRT.@) + */ +__msvcrt_long MSVCRT__dstbias = -3600; + +/********************************************************************* + * _tzname (MSVCRT.@) + * NOTES + * Some apps (notably Mozilla) insist on writing to these, so the buffer + * must be large enough. + */ +static char tzname_std[64] = "PST"; +static char tzname_dst[64] = "PDT"; +char *MSVCRT__tzname[2] = { tzname_std, tzname_dst }; + +static TIME_ZONE_INFORMATION tzi = {0}; +/********************************************************************* + * _tzset (MSVCRT.@) + */ +void CDECL _tzset(void) +{ + char *tz = getenv("TZ"); + BOOL error; + + _lock(_TIME_LOCK); + if(tz && tz[0]) { + BOOL neg_zone = FALSE; + + memset(&tzi, 0, sizeof(tzi)); + + /* Parse timezone information: tzn[+|-]hh[:mm[:ss]][dzn] */ + lstrcpynA(MSVCRT__tzname[0], tz, 3); + tz += 3; + + if(*tz == '-') { + neg_zone = TRUE; + tz++; + }else if(*tz == '+') { + tz++; + } + MSVCRT___timezone = strtol(tz, &tz, 10)*3600; + if(*tz == ':') { + MSVCRT___timezone += strtol(tz+1, &tz, 10)*60; + if(*tz == ':') + MSVCRT___timezone += strtol(tz+1, &tz, 10); + } + if(neg_zone) + MSVCRT___timezone = -MSVCRT___timezone; + + MSVCRT___daylight = *tz; + lstrcpynA(MSVCRT__tzname[1], tz, 3); + }else if(GetTimeZoneInformation(&tzi) != TIME_ZONE_ID_INVALID) { + MSVCRT___timezone = tzi.Bias*60; + if(tzi.StandardDate.wMonth) + MSVCRT___timezone += tzi.StandardBias*60; + + if(tzi.DaylightDate.wMonth) { + MSVCRT___daylight = 1; + MSVCRT__dstbias = (tzi.DaylightBias-tzi.StandardBias)*60; + }else { + MSVCRT___daylight = 0; + MSVCRT__dstbias = 0; + } + + if(!WideCharToMultiByte(CP_ACP, 0, tzi.StandardName, -1, MSVCRT__tzname[0], + sizeof(tzname_std), NULL, &error) || error) + *MSVCRT__tzname[0] = 0; + if(!WideCharToMultiByte(CP_ACP, 0, tzi.DaylightName, -1, MSVCRT__tzname[1], + sizeof(tzname_dst), NULL, &error) || error) + *MSVCRT__tzname[0] = 0; + } + _unlock(_TIME_LOCK); +} + +static void _tzset_init(void) +{ + static BOOL init = FALSE; + + if(!init) { + _lock(_TIME_LOCK); + if(!init) { + _tzset(); + init = TRUE; + } + _unlock(_TIME_LOCK); + } +} + +static BOOL is_dst(const SYSTEMTIME *st) +{ + TIME_ZONE_INFORMATION tmp; + SYSTEMTIME out; + + if(!MSVCRT___daylight) + return FALSE; + + if(tzi.DaylightDate.wMonth) { + tmp = tzi; + }else if(st->wYear >= 2007) { + memset(&tmp, 0, sizeof(tmp)); + tmp.StandardDate.wMonth = 11; + tmp.StandardDate.wDay = 1; + tmp.StandardDate.wHour = 2; + tmp.DaylightDate.wMonth = 3; + tmp.DaylightDate.wDay = 2; + tmp.DaylightDate.wHour = 2; + }else { + memset(&tmp, 0, sizeof(tmp)); + tmp.StandardDate.wMonth = 10; + tmp.StandardDate.wDay = 5; + tmp.StandardDate.wHour = 2; + tmp.DaylightDate.wMonth = 4; + tmp.DaylightDate.wDay = 1; + tmp.DaylightDate.wHour = 2; + } + + tmp.Bias = 0; + tmp.StandardBias = 0; + tmp.DaylightBias = MSVCRT__dstbias/60; + if(!SystemTimeToTzSpecificLocalTime(&tmp, st, &out)) + return FALSE; + + return memcmp(st, &out, sizeof(SYSTEMTIME)); +} + +#define SECSPERDAY 86400 +/* 1601 to 1970 is 369 years plus 89 leap days */ +#define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)SECSPERDAY) +#define TICKSPERSEC 10000000 +#define TICKSPERMSEC 10000 +#define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKSPERSEC) + +static __time64_t mktime_helper(struct tm *mstm, BOOL local) +{ + SYSTEMTIME st; + FILETIME ft; + __time64_t ret = 0; + int i; + BOOL use_dst = FALSE; + + ret = mstm->tm_year + mstm->tm_mon/12; + mstm->tm_mon %= 12; + if(mstm->tm_mon < 0) { + mstm->tm_mon += 12; + ret--; + } + + if(ret<70 || ret>1100) { + *_errno() = EINVAL; + return -1; + } + + memset(&st, 0, sizeof(SYSTEMTIME)); + st.wDay = 1; + st.wMonth = mstm->tm_mon+1; + st.wYear = ret+1900; + + if(!SystemTimeToFileTime(&st, &ft)) { + *_errno() = EINVAL; + return -1; + } + + ret = ((__time64_t)ft.dwHighDateTime<<32)+ft.dwLowDateTime; + ret += (__time64_t)mstm->tm_sec*TICKSPERSEC; + ret += (__time64_t)mstm->tm_min*60*TICKSPERSEC; + ret += (__time64_t)mstm->tm_hour*60*60*TICKSPERSEC; + ret += (__time64_t)(mstm->tm_mday-1)*SECSPERDAY*TICKSPERSEC; + + ft.dwLowDateTime = ret & 0xffffffff; + ft.dwHighDateTime = ret >> 32; + FileTimeToSystemTime(&ft, &st); + + if(local) { + _tzset_init(); + use_dst = is_dst(&st); + if((mstm->tm_isdst<=-1 && use_dst) || (mstm->tm_isdst>=1)) { + SYSTEMTIME tmp; + + ret += (__time64_t)MSVCRT__dstbias*TICKSPERSEC; + + ft.dwLowDateTime = ret & 0xffffffff; + ft.dwHighDateTime = ret >> 32; + FileTimeToSystemTime(&ft, &tmp); + + if(!is_dst(&tmp)) { + st = tmp; + use_dst = FALSE; + }else { + use_dst = TRUE; + } + }else if(mstm->tm_isdst==0 && use_dst) { + ret -= (__time64_t)MSVCRT__dstbias*TICKSPERSEC; + ft.dwLowDateTime = ret & 0xffffffff; + ft.dwHighDateTime = ret >> 32; + FileTimeToSystemTime(&ft, &st); + ret += (__time64_t)MSVCRT__dstbias*TICKSPERSEC; + } + ret += (__time64_t)MSVCRT___timezone*TICKSPERSEC; + } + + mstm->tm_sec = st.wSecond; + mstm->tm_min = st.wMinute; + mstm->tm_hour = st.wHour; + mstm->tm_mday = st.wDay; + mstm->tm_mon = st.wMonth-1; + mstm->tm_year = st.wYear-1900; + mstm->tm_wday = st.wDayOfWeek; + for(i=mstm->tm_yday=0; itm_yday += MonthLengths[IsLeapYear(st.wYear)][i]; + mstm->tm_yday += st.wDay-1; + mstm->tm_isdst = use_dst ? 1 : 0; + + if(ret < TICKS_1601_TO_1970) { + *_errno() = EINVAL; + return -1; + } + ret = (ret-TICKS_1601_TO_1970)/TICKSPERSEC; + return ret; +} + +/********************************************************************** + * _mktime64 (MSVCRT.@) + */ +__time64_t CDECL _mktime64(struct tm *mstm) +{ + return mktime_helper(mstm, TRUE); +} + +/********************************************************************** + * _mktime32 (MSVCRT.@) + */ +__time32_t CDECL _mktime32(struct tm *mstm) +{ + __time64_t ret = _mktime64( mstm ); + return ret == (__time32_t)ret ? ret : -1; +} + +/********************************************************************** + * _mkgmtime64 (MSVCRT.@) + * + * time->tm_isdst value is ignored + */ +__time64_t CDECL _mkgmtime64(struct tm *time) +{ + return mktime_helper(time, FALSE); +} + +/********************************************************************** + * _mkgmtime32 (MSVCRT.@) + */ +__time32_t CDECL _mkgmtime32(struct tm *time) +{ + __time64_t ret = _mkgmtime64(time); + return ret == (__time32_t)ret ? ret : -1; +} + +/********************************************************************* + * _localtime64_s (MSVCRT.@) + */ +int CDECL _localtime64_s(struct tm *res, const __time64_t *secs) +{ + int i; + FILETIME ft; + SYSTEMTIME st; + ULONGLONG time; + + if (!res || !secs || *secs < 0 || *secs > _MAX__TIME64_T) + { + if (res) + write_invalid_msvcrt_tm(res); + + *_errno() = EINVAL; + return EINVAL; + } + + _tzset_init(); + time = (*secs - MSVCRT___timezone) * (ULONGLONG)TICKSPERSEC + TICKS_1601_TO_1970; + + ft.dwHighDateTime = (UINT)(time >> 32); + ft.dwLowDateTime = (UINT)time; + FileTimeToSystemTime(&ft, &st); + + res->tm_isdst = is_dst(&st) ? 1 : 0; + if(res->tm_isdst) { + time -= MSVCRT__dstbias * (ULONGLONG)TICKSPERSEC; + ft.dwHighDateTime = (UINT)(time >> 32); + ft.dwLowDateTime = (UINT)time; + FileTimeToSystemTime(&ft, &st); + } + + res->tm_sec = st.wSecond; + res->tm_min = st.wMinute; + res->tm_hour = st.wHour; + res->tm_mday = st.wDay; + res->tm_year = st.wYear - 1900; + res->tm_mon = st.wMonth - 1; + res->tm_wday = st.wDayOfWeek; + for (i = res->tm_yday = 0; i < st.wMonth - 1; i++) + res->tm_yday += MonthLengths[IsLeapYear(st.wYear)][i]; + res->tm_yday += st.wDay - 1; + + return 0; +} + +/********************************************************************* + * _localtime64 (MSVCRT.@) + */ +struct tm* CDECL _localtime64(const __time64_t* secs) +{ + thread_data_t *data = msvcrt_get_thread_data(); + + if(!data->time_buffer) + data->time_buffer = malloc(sizeof(struct tm)); + + if(_localtime64_s(data->time_buffer, secs)) + return NULL; + return data->time_buffer; +} + +/********************************************************************* + * _localtime32 (MSVCRT.@) + */ +struct tm* CDECL _localtime32(const __time32_t* secs) +{ + __time64_t secs64; + + if(!secs) + return NULL; + + secs64 = *secs; + return _localtime64( &secs64 ); +} + +/********************************************************************* + * _localtime32_s (MSVCRT.@) + */ +int CDECL _localtime32_s(struct tm *time, const __time32_t *secs) +{ + __time64_t secs64; + + if (!time || !secs || *secs < 0) + { + if (time) + write_invalid_msvcrt_tm(time); + + *_errno() = EINVAL; + return EINVAL; + } + + secs64 = *secs; + return _localtime64_s(time, &secs64); +} + +/********************************************************************* + * _gmtime64 (MSVCRT.@) + */ +int CDECL _gmtime64_s(struct tm *res, const __time64_t *secs) +{ + int i; + FILETIME ft; + SYSTEMTIME st; + ULONGLONG time; + + TRACE("res %p, secs %p (%I64d).\n", res, secs, secs ? *secs : 0); + + if (!res || !secs || *secs < MIN_GMTIME64_TIME || *secs > MAX_GMTIME64_TIME) { + if (res) { + write_invalid_msvcrt_tm(res); + } + + *_errno() = EINVAL; + return EINVAL; + } + + time = *secs * (ULONGLONG)TICKSPERSEC + TICKS_1601_TO_1970; + + ft.dwHighDateTime = (UINT)(time >> 32); + ft.dwLowDateTime = (UINT)time; + + FileTimeToSystemTime(&ft, &st); + + res->tm_sec = st.wSecond; + res->tm_min = st.wMinute; + res->tm_hour = st.wHour; + res->tm_mday = st.wDay; + res->tm_year = st.wYear - 1900; + res->tm_mon = st.wMonth - 1; + res->tm_wday = st.wDayOfWeek; + for (i = res->tm_yday = 0; i < st.wMonth - 1; i++) { + res->tm_yday += MonthLengths[IsLeapYear(st.wYear)][i]; + } + + res->tm_yday += st.wDay - 1; + res->tm_isdst = 0; + + return 0; +} + +/********************************************************************* + * _gmtime64 (MSVCRT.@) + */ +struct tm* CDECL _gmtime64(const __time64_t *secs) +{ + thread_data_t * const data = msvcrt_get_thread_data(); + + if(!data->time_buffer) + data->time_buffer = malloc(sizeof(struct tm)); + + if(_gmtime64_s(data->time_buffer, secs)) + return NULL; + return data->time_buffer; +} + +/********************************************************************* + * _gmtime32_s (MSVCRT.@) + */ +int CDECL _gmtime32_s(struct tm *res, const __time32_t *secs) +{ + __time64_t secs64; + + if(secs) { + secs64 = *secs; + return _gmtime64_s(res, &secs64); + } + return _gmtime64_s(res, NULL); +} + +/********************************************************************* + * _gmtime32 (MSVCRT.@) + */ +struct tm* CDECL _gmtime32(const __time32_t* secs) +{ + __time64_t secs64; + + if(!secs) + return NULL; + + secs64 = *secs; + return _gmtime64( &secs64 ); +} + +/********************************************************************** + * _strdate (MSVCRT.@) + */ +char* CDECL _strdate(char* date) +{ + SYSTEMTIME st; + + GetLocalTime(&st); + date[0] = '0' + st.wMonth / 10; + date[1] = '0' + st.wMonth % 10; + date[2] = '/'; + date[3] = '0' + st.wDay / 10; + date[4] = '0' + st.wDay % 10; + date[5] = '/'; + date[6] = '0' + st.wYear / 10 % 10; + date[7] = '0' + st.wYear % 10; + date[8] = 0; + return date; +} + +/********************************************************************** + * _strdate_s (MSVCRT.@) + */ +int CDECL _strdate_s(char* date, size_t size) +{ + if(date && size) + date[0] = '\0'; + + if(!date) { + *_errno() = EINVAL; + return EINVAL; + } + + if(size < 9) { + *_errno() = ERANGE; + return ERANGE; + } + + _strdate(date); + return 0; +} + +/********************************************************************** + * _wstrdate (MSVCRT.@) + */ +wchar_t* CDECL _wstrdate(wchar_t* date) +{ + GetDateFormatW(LOCALE_NEUTRAL, 0, NULL, L"MM'/'dd'/'yy", date, 9); + return date; +} + +/********************************************************************** + * _wstrdate_s (MSVCRT.@) + */ +int CDECL _wstrdate_s(wchar_t* date, size_t size) +{ + if(date && size) + date[0] = '\0'; + + if(!date) { + *_errno() = EINVAL; + return EINVAL; + } + + if(size < 9) { + *_errno() = ERANGE; + return ERANGE; + } + + _wstrdate(date); + return 0; +} + +/********************************************************************* + * _strtime (MSVCRT.@) + */ +char* CDECL _strtime(char* time) +{ + SYSTEMTIME st; + + GetLocalTime(&st); + time[0] = '0' + st.wHour / 10; + time[1] = '0' + st.wHour % 10; + time[2] = ':'; + time[3] = '0' + st.wMinute / 10; + time[4] = '0' + st.wMinute % 10; + time[5] = ':'; + time[6] = '0' + st.wSecond / 10; + time[7] = '0' + st.wSecond % 10; + time[8] = 0; + return time; +} + +/********************************************************************* + * _strtime_s (MSVCRT.@) + */ +int CDECL _strtime_s(char* time, size_t size) +{ + if(time && size) + time[0] = '\0'; + + if(!time) { + *_errno() = EINVAL; + return EINVAL; + } + + if(size < 9) { + *_errno() = ERANGE; + return ERANGE; + } + + _strtime(time); + return 0; +} + +/********************************************************************* + * _wstrtime (MSVCRT.@) + */ +wchar_t* CDECL _wstrtime(wchar_t* time) +{ + GetTimeFormatW(LOCALE_NEUTRAL, 0, NULL, L"HH':'mm':'ss", time, 9); + return time; +} + +/********************************************************************* + * _wstrtime_s (MSVCRT.@) + */ +int CDECL _wstrtime_s(wchar_t* time, size_t size) +{ + if(time && size) + time[0] = '\0'; + + if(!time) { + *_errno() = EINVAL; + return EINVAL; + } + + if(size < 9) { + *_errno() = ERANGE; + return ERANGE; + } + + _wstrtime(time); + return 0; +} + +/********************************************************************* + * clock (MSVCRT.@) + */ +clock_t CDECL clock(void) +{ + LARGE_INTEGER systime; + + NtQuerySystemTime(&systime); + return (systime.QuadPart - init_time) / (TICKSPERSEC / CLOCKS_PER_SEC); +} + +/********************************************************************* + * _difftime64 (MSVCRT.@) + */ +double CDECL _difftime64(__time64_t time1, __time64_t time2) +{ + return (double)(time1 - time2); +} + +/********************************************************************* + * _difftime32 (MSVCRT.@) + */ +double CDECL _difftime32(__time32_t time1, __time32_t time2) +{ + return (double)(time1 - time2); +} + +/********************************************************************* + * _ftime64 (MSVCRT.@) + */ +void CDECL _ftime64(struct __timeb64 *buf) +{ + TIME_ZONE_INFORMATION tzinfo; + FILETIME ft; + ULONGLONG time; + + _tzset_init(); + + GetSystemTimeAsFileTime(&ft); + + time = ((ULONGLONG)ft.dwHighDateTime << 32) | ft.dwLowDateTime; + + buf->time = time / TICKSPERSEC - SECS_1601_TO_1970; + buf->millitm = (time % TICKSPERSEC) / TICKSPERMSEC; + buf->timezone = MSVCRT___timezone / 60; + buf->dstflag = GetTimeZoneInformation( &tzinfo) == TIME_ZONE_ID_DAYLIGHT; +} + +/********************************************************************* + * _ftime64_s (MSVCRT.@) + */ +int CDECL _ftime64_s(struct __timeb64 *buf) +{ + if (!MSVCRT_CHECK_PMT( buf != NULL )) return EINVAL; + _ftime64(buf); + return 0; +} + +/********************************************************************* + * _ftime32 (MSVCRT.@) + */ +void CDECL _ftime32(struct __timeb32 *buf) +{ + struct __timeb64 buf64; + + _ftime64( &buf64 ); + buf->time = buf64.time; + buf->millitm = buf64.millitm; + buf->timezone = buf64.timezone; + buf->dstflag = buf64.dstflag; +} + +/********************************************************************* + * _ftime32_s (MSVCRT.@) + */ +int CDECL _ftime32_s(struct __timeb32 *buf) +{ + if (!MSVCRT_CHECK_PMT( buf != NULL )) return EINVAL; + _ftime32(buf); + return 0; +} + +/********************************************************************* + * _time64 (MSVCRT.@) + */ +__time64_t CDECL _time64(__time64_t *buf) +{ + __time64_t curtime; + struct __timeb64 tb; + + _ftime64(&tb); + + curtime = tb.time; + return buf ? *buf = curtime : curtime; +} + +/********************************************************************* + * _time32 (MSVCRT.@) + */ +__time32_t CDECL _time32(__time32_t *buf) +{ + __time32_t curtime; + struct __timeb64 tb; + + _ftime64(&tb); + + curtime = tb.time; + return buf ? *buf = curtime : curtime; +} + +/********************************************************************* + * __p__daylight (MSVCRT.@) + */ +int * CDECL __p__daylight(void) +{ + return &MSVCRT___daylight; +} + +/********************************************************************* + * __p__dstbias (MSVCRT.@) + */ +__msvcrt_long * CDECL __p__dstbias(void) +{ + return &MSVCRT__dstbias; +} + +#if _MSVCR_VER >= 80 +/********************************************************************* + * _get_dstbias (MSVCR80.@) + */ +int CDECL _get_dstbias(int *seconds) +{ + if (!MSVCRT_CHECK_PMT(seconds != NULL)) return EINVAL; + *seconds = MSVCRT__dstbias; + return 0; +} +#endif + +/********************************************************************* + * __p__timezone (MSVCRT.@) + */ +__msvcrt_long * CDECL __p__timezone(void) +{ + return &MSVCRT___timezone; +} + +/********************************************************************* + * _get_tzname (MSVCRT.@) + */ +int CDECL _get_tzname(size_t *ret, char *buf, size_t bufsize, int index) +{ + char *timezone; + + switch(index) + { + case 0: + timezone = tzname_std; + break; + case 1: + timezone = tzname_dst; + break; + default: + *_errno() = EINVAL; + return EINVAL; + } + + if(!ret || (!buf && bufsize > 0) || (buf && !bufsize)) + { + *_errno() = EINVAL; + return EINVAL; + } + + *ret = strlen(timezone)+1; + if(!buf && !bufsize) + return 0; + if(*ret > bufsize) + { + buf[0] = 0; + return ERANGE; + } + + strcpy(buf, timezone); + return 0; +} + +/********************************************************************* + * __p_tzname (MSVCRT.@) + */ +char ** CDECL __p__tzname(void) +{ + return MSVCRT__tzname; +} + +#if _MSVCR_VER <= 90 +#define STRFTIME_CHAR char +#define STRFTIME_TD(td, name) td->str.names.name +#else +#define STRFTIME_CHAR wchar_t +#define STRFTIME_TD(td, name) td->wstr.names.name +#endif + +#define strftime_str(a,b,c,d) strftime_nstr(a,b,c,d,SIZE_MAX) +static inline BOOL strftime_nstr(STRFTIME_CHAR *str, size_t *pos, + size_t max, const STRFTIME_CHAR *src, size_t len) +{ + while(*src && len) + { + if(*pos >= max) { + *str = 0; + *_errno() = ERANGE; + return FALSE; + } + + str[*pos] = *src; + src++; + *pos += 1; + len--; + } + return TRUE; +} + +static inline BOOL strftime_int(STRFTIME_CHAR *str, size_t *pos, size_t max, + int src, int prec, int l, int h) +{ + size_t len; + + if(!MSVCRT_CHECK_PMT(src>=l && src<=h)) { + *str = 0; + return FALSE; + } + +#if _MSVCR_VER <= 90 + len = _snprintf(str+*pos, max-*pos, "%0*d", prec, src); +#else + len = _snwprintf(str+*pos, max-*pos, L"%0*d", prec, src); +#endif + if(len == -1) { + *str = 0; + *_errno() = ERANGE; + return FALSE; + } + + *pos += len; + return TRUE; +} + +static inline BOOL strftime_format(STRFTIME_CHAR *str, size_t *pos, size_t max, + const struct tm *mstm, __lc_time_data *time_data, const STRFTIME_CHAR *format) +{ + size_t count; + BOOL ret = TRUE; + + while(*format && ret) + { + count = 1; + while(format[0] == format[count]) count++; + + switch(*format) { + case '\'': + if(count % 2 == 0) break; + + format += count; + count = 0; + while(format[count] && format[count] != '\'') count++; + + ret = strftime_nstr(str, pos, max, format, count); + if(!ret) return FALSE; + if(format[count] == '\'') count++; + break; + case 'd': + if(count > 2) + { + if(!MSVCRT_CHECK_PMT(mstm->tm_wday>=0 && mstm->tm_wday<=6)) + { + *str = 0; + return FALSE; + } + } + switch(count) { + case 1: + case 2: + ret = strftime_int(str, pos, max, mstm->tm_mday, count==1 ? 0 : 2, 1, 31); + break; + case 3: + ret = strftime_str(str, pos, max, STRFTIME_TD(time_data, short_wday)[mstm->tm_wday]); + break; + default: + ret = strftime_nstr(str, pos, max, format, count-4); + if(ret) + ret = strftime_str(str, pos, max, STRFTIME_TD(time_data, wday)[mstm->tm_wday]); + break; + } + break; + case 'M': + if(count > 2) + { + if(!MSVCRT_CHECK_PMT(mstm->tm_mon>=0 && mstm->tm_mon<=11)) + { + *str = 0; + return FALSE; + } + } + switch(count) { + case 1: + case 2: + ret = strftime_int(str, pos, max, mstm->tm_mon+1, count==1 ? 0 : 2, 1, 12); + break; + case 3: + ret = strftime_str(str, pos, max, STRFTIME_TD(time_data, short_mon)[mstm->tm_mon]); + break; + default: + ret = strftime_nstr(str, pos, max, format, count-4); + if(ret) + ret = strftime_str(str, pos, max, STRFTIME_TD(time_data, mon)[mstm->tm_mon]); + break; + } + break; + case 'y': + if(count > 1) + { +#if _MSVCR_VER>=140 + if(!MSVCRT_CHECK_PMT(mstm->tm_year >= -1900 && mstm->tm_year <= 8099)) +#else + if(!MSVCRT_CHECK_PMT(mstm->tm_year >= 0)) +#endif + { + *str = 0; + return FALSE; + } + } + + switch(count) { + case 1: + ret = strftime_nstr(str, pos, max, format, 1); + break; + case 2: + case 3: + ret = strftime_nstr(str, pos, max, format, count-2); + if(ret) + ret = strftime_int(str, pos, max, (mstm->tm_year+1900)%100, 2, 0, 99); + break; + default: + ret = strftime_nstr(str, pos, max, format, count-4); + if(ret) + ret = strftime_int(str, pos, max, mstm->tm_year+1900, 4, 0, 9999); + break; + } + break; + case 'h': + if(!MSVCRT_CHECK_PMT(mstm->tm_hour>=0 && mstm->tm_hour<=23)) + { + *str = 0; + return FALSE; + } + if(count > 2) + ret = strftime_nstr(str, pos, max, format, count-2); + if(ret) + ret = strftime_int(str, pos, max, (mstm->tm_hour + 11) % 12 + 1, + count == 1 ? 0 : 2, 1, 12); + break; + case 'H': + if(count > 2) + ret = strftime_nstr(str, pos, max, format, count-2); + if(ret) + ret = strftime_int(str, pos, max, mstm->tm_hour, count == 1 ? 0 : 2, 0, 23); + break; + case 'm': + if(count > 2) + ret = strftime_nstr(str, pos, max, format, count-2); + if(ret) + ret = strftime_int(str, pos, max, mstm->tm_min, count == 1 ? 0 : 2, 0, 59); + break; + case 's': + if(count > 2) + ret = strftime_nstr(str, pos, max, format, count-2); + if(ret) + ret = strftime_int(str, pos, max, mstm->tm_sec, count == 1 ? 0 : 2, 0, MAX_SECONDS); + break; + case 'a': + case 'A': + case 't': + if(!MSVCRT_CHECK_PMT(mstm->tm_hour>=0 && mstm->tm_hour<=23)) + { + *str = 0; + return FALSE; + } + ret = strftime_nstr(str, pos, max, + mstm->tm_hour < 12 ? STRFTIME_TD(time_data, am) : STRFTIME_TD(time_data, pm), + (*format == 't' && count == 1) ? 1 : SIZE_MAX); + break; + default: + ret = strftime_nstr(str, pos, max, format, count); + break; + } + format += count; + } + + return ret; +} + +#if _MSVCR_VER>=140 +static inline BOOL strftime_tzdiff(STRFTIME_CHAR *str, size_t *pos, size_t max, BOOL is_dst) +{ + __msvcrt_long tz = MSVCRT___timezone + (is_dst ? MSVCRT__dstbias : 0); + char sign; + + if(tz < 0) { + sign = '+'; + tz = -tz; + }else { + sign = '-'; + } + + if(*pos < max) + str[(*pos)++] = sign; + if(!strftime_int(str, pos, max, tz/60/60, 2, 0, 99)) + return FALSE; + return strftime_int(str, pos, max, tz/60%60, 2, 0, 59); +} +#endif + +static size_t strftime_impl(STRFTIME_CHAR *str, size_t max, + const STRFTIME_CHAR *format, const struct tm *mstm, + __lc_time_data *time_data, _locale_t loc) +{ + size_t ret, tmp; + BOOL alternate; + int year = mstm ? mstm->tm_year + 1900 : -1; + + if(!str || !format) { + if(str && max) + *str = 0; + *_errno() = EINVAL; + return 0; + } + + if(!time_data) + time_data = loc ? loc->locinfo->lc_time_curr : get_locinfo()->lc_time_curr; + + for(ret=0; *format && ret=140 + if(time_data == &cloc_time_data && !alternate) + { + tmp = strftime_impl(str+ret, max-ret, L"%a %b %e %T %Y", mstm, time_data, loc); + if(!tmp) + return 0; + ret += tmp; + break; + } +#endif + if(!strftime_format(str, &ret, max, mstm, time_data, + alternate ? STRFTIME_TD(time_data, date) : STRFTIME_TD(time_data, short_date))) + return 0; + if(ret < max) + str[ret++] = ' '; + if(!strftime_format(str, &ret, max, mstm, time_data, STRFTIME_TD(time_data, time))) + return 0; + break; + case 'x': + if(!strftime_format(str, &ret, max, mstm, time_data, + alternate ? STRFTIME_TD(time_data, date) : STRFTIME_TD(time_data, short_date))) + return 0; + break; + case 'X': + if(!strftime_format(str, &ret, max, mstm, time_data, STRFTIME_TD(time_data, time))) + return 0; + break; + case 'a': + if(!MSVCRT_CHECK_PMT(mstm->tm_wday>=0 && mstm->tm_wday<=6)) + goto einval_error; + if(!strftime_str(str, &ret, max, STRFTIME_TD(time_data, short_wday)[mstm->tm_wday])) + return 0; + break; + case 'A': + if(!MSVCRT_CHECK_PMT(mstm->tm_wday>=0 && mstm->tm_wday<=6)) + goto einval_error; + if(!strftime_str(str, &ret, max, STRFTIME_TD(time_data, wday)[mstm->tm_wday])) + return 0; + break; + case 'b': +#if _MSVCR_VER>=140 + case 'h': +#endif + if(!MSVCRT_CHECK_PMT(mstm->tm_mon>=0 && mstm->tm_mon<=11)) + goto einval_error; + if(!strftime_str(str, &ret, max, STRFTIME_TD(time_data, short_mon)[mstm->tm_mon])) + return 0; + break; + case 'B': + if(!MSVCRT_CHECK_PMT(mstm->tm_mon>=0 && mstm->tm_mon<=11)) + goto einval_error; + if(!strftime_str(str, &ret, max, STRFTIME_TD(time_data, mon)[mstm->tm_mon])) + return 0; + break; +#if _MSVCR_VER>=140 + case 'C': + if(!MSVCRT_CHECK_PMT(year>=0 && year<=9999)) + goto einval_error; + if(!strftime_int(str, &ret, max, year/100, alternate ? 0 : 2, 0, 99)) + return 0; + break; +#endif + case 'd': + if(!strftime_int(str, &ret, max, mstm->tm_mday, alternate ? 0 : 2, 1, 31)) + return 0; + break; +#if _MSVCR_VER>=140 + case 'D': + if(!MSVCRT_CHECK_PMT(year>=0 && year<=9999)) + goto einval_error; + if(!strftime_int(str, &ret, max, mstm->tm_mon+1, alternate ? 0 : 2, 1, 12)) + return 0; + if(ret < max) + str[ret++] = '/'; + if(!strftime_int(str, &ret, max, mstm->tm_mday, alternate ? 0 : 2, 1, 31)) + return 0; + if(ret < max) + str[ret++] = '/'; + if(!strftime_int(str, &ret, max, year%100, alternate ? 0 : 2, 0, 99)) + return 0; + break; + case 'e': + if(!strftime_int(str, &ret, max, mstm->tm_mday, alternate ? 0 : 2, 1, 31)) + return 0; + if(!alternate && str[ret-2] == '0') + str[ret-2] = ' '; + break; + case 'F': + if(!strftime_int(str, &ret, max, year, alternate ? 0 : 4, 0, 9999)) + return 0; + if(ret < max) + str[ret++] = '-'; + if(!strftime_int(str, &ret, max, mstm->tm_mon+1, alternate ? 0 : 2, 1, 12)) + return 0; + if(ret < max) + str[ret++] = '-'; + if(!strftime_int(str, &ret, max, mstm->tm_mday, alternate ? 0 : 2, 1, 31)) + return 0; + break; + case 'g': + case 'G': + if(!MSVCRT_CHECK_PMT(year>=0 && year<=9999)) + goto einval_error; + /* fall through */ + case 'V': + { + int iso_year = year; + int iso_days = mstm->tm_yday - (mstm->tm_wday ? mstm->tm_wday : 7) + 4; + if (iso_days < 0) + iso_days += 365 + IsLeapYear(--iso_year); + else if(iso_days >= 365 + IsLeapYear(iso_year)) + iso_days -= 365 + IsLeapYear(iso_year++); + + if(*format == 'G') { + if(!strftime_int(str, &ret, max, iso_year, 4, 0, 9999)) + return 0; + } else if(*format == 'g') { + if(!strftime_int(str, &ret, max, iso_year%100, 2, 0, 99)) + return 0; + } else { + if(!strftime_int(str, &ret, max, iso_days/7 + 1, alternate ? 0 : 2, 0, 53)) + return 0; + } + break; + } +#endif + case 'H': + if(!strftime_int(str, &ret, max, mstm->tm_hour, alternate ? 0 : 2, 0, 23)) + return 0; + break; + case 'I': + if(!MSVCRT_CHECK_PMT(mstm->tm_hour>=0 && mstm->tm_hour<=23)) + goto einval_error; + if(!strftime_int(str, &ret, max, (mstm->tm_hour + 11) % 12 + 1, + alternate ? 0 : 2, 1, 12)) + return 0; + break; + case 'j': + if(!strftime_int(str, &ret, max, mstm->tm_yday+1, alternate ? 0 : 3, 1, 366)) + return 0; + break; + case 'm': + if(!strftime_int(str, &ret, max, mstm->tm_mon+1, alternate ? 0 : 2, 1, 12)) + return 0; + break; + case 'M': + if(!strftime_int(str, &ret, max, mstm->tm_min, alternate ? 0 : 2, 0, 59)) + return 0; + break; +#if _MSVCR_VER>=140 + case 'n': + str[ret++] = '\n'; + break; +#endif + case 'p': + if(!MSVCRT_CHECK_PMT(mstm->tm_hour>=0 && mstm->tm_hour<=23)) + goto einval_error; + if(!strftime_str(str, &ret, max, mstm->tm_hour<12 ? + STRFTIME_TD(time_data, am) : STRFTIME_TD(time_data, pm))) + return 0; + break; +#if _MSVCR_VER>=140 + case 'r': + if(time_data == &cloc_time_data) + { + if(!MSVCRT_CHECK_PMT(mstm->tm_hour>=0 && mstm->tm_hour<=23)) + goto einval_error; + if(!strftime_int(str, &ret, max, (mstm->tm_hour + 11) % 12 + 1, + alternate ? 0 : 2, 1, 12)) + return 0; + if(ret < max) + str[ret++] = ':'; + if(!strftime_int(str, &ret, max, mstm->tm_min, alternate ? 0 : 2, 0, 59)) + return 0; + if(ret < max) + str[ret++] = ':'; + if(!strftime_int(str, &ret, max, mstm->tm_sec, alternate ? 0 : 2, 0, MAX_SECONDS)) + return 0; + if(ret < max) + str[ret++] = ' '; + if(!strftime_str(str, &ret, max, mstm->tm_hour<12 ? + STRFTIME_TD(time_data, am) : STRFTIME_TD(time_data, pm))) + return 0; + } + else + { + if(!strftime_format(str, &ret, max, mstm, time_data, STRFTIME_TD(time_data, time))) + return 0; + } + break; + case 'R': + if(!strftime_int(str, &ret, max, mstm->tm_hour, alternate ? 0 : 2, 0, 23)) + return 0; + if(ret < max) + str[ret++] = ':'; + if(!strftime_int(str, &ret, max, mstm->tm_min, alternate ? 0 : 2, 0, 59)) + return 0; + break; +#endif + case 'S': + if(!strftime_int(str, &ret, max, mstm->tm_sec, alternate ? 0 : 2, 0, MAX_SECONDS)) + return 0; + break; +#if _MSVCR_VER>=140 + case 't': + str[ret++] = '\t'; + break; + case 'T': + if(!strftime_int(str, &ret, max, mstm->tm_hour, alternate ? 0 : 2, 0, 23)) + return 0; + if(ret < max) + str[ret++] = ':'; + if(!strftime_int(str, &ret, max, mstm->tm_min, alternate ? 0 : 2, 0, 59)) + return 0; + if(ret < max) + str[ret++] = ':'; + if(!strftime_int(str, &ret, max, mstm->tm_sec, alternate ? 0 : 2, 0, MAX_SECONDS)) + return 0; + break; + case 'u': + if(!MSVCRT_CHECK_PMT(mstm->tm_wday>=0 && mstm->tm_wday<=6)) + goto einval_error; + tmp = mstm->tm_wday ? mstm->tm_wday : 7; + if(!strftime_int(str, &ret, max, tmp, 0, 1, 7)) + return 0; + break; +#endif + case 'w': + if(!strftime_int(str, &ret, max, mstm->tm_wday, 0, 0, 6)) + return 0; + break; + case 'y': +#if _MSVCR_VER>=140 + if(!MSVCRT_CHECK_PMT(year>=0 && year<=9999)) +#else + if(!MSVCRT_CHECK_PMT(year>=1900)) +#endif + goto einval_error; + if(!strftime_int(str, &ret, max, year%100, alternate ? 0 : 2, 0, 99)) + return 0; + break; + case 'Y': + if(!strftime_int(str, &ret, max, year, alternate ? 0 : 4, 0, 9999)) + return 0; + break; + case 'z': +#if _MSVCR_VER>=140 + _tzset(); + if(!strftime_tzdiff(str, &ret, max, mstm->tm_isdst)) + return 0; + break; +#endif + case 'Z': + _tzset(); +#if _MSVCR_VER <= 90 + if(_get_tzname(&tmp, str+ret, max-ret, mstm->tm_isdst ? 1 : 0)) + return 0; +#else + if(_mbstowcs_s_l(&tmp, str+ret, max-ret, + mstm->tm_isdst ? tzname_dst : tzname_std, + _TRUNCATE, loc) == STRUNCATE) + ret = max; +#endif + ret += tmp-1; + break; + case 'U': + case 'W': + if(!MSVCRT_CHECK_PMT(mstm->tm_wday>=0 && mstm->tm_wday<=6)) + goto einval_error; + if(!MSVCRT_CHECK_PMT(mstm->tm_yday>=0 && mstm->tm_yday<=365)) + goto einval_error; + if(*format == 'U') + tmp = mstm->tm_wday; + else if(!mstm->tm_wday) + tmp = 6; + else + tmp = mstm->tm_wday-1; + + tmp = mstm->tm_yday/7 + (tmp<=mstm->tm_yday%7); + if(!strftime_int(str, &ret, max, tmp, alternate ? 0 : 2, 0, 53)) + return 0; + break; + case '%': + str[ret++] = '%'; + break; + default: + WARN("unknown format %c\n", *format); + MSVCRT_INVALID_PMT("unknown format", EINVAL); + goto einval_error; + } + } + + if(ret == max) { + if(max) + *str = 0; + *_errno() = ERANGE; + return 0; + } + + str[ret] = 0; + return ret; + +einval_error: + *str = 0; + return 0; +} + +static size_t strftime_helper(char *str, size_t max, const char *format, + const struct tm *mstm, __lc_time_data *time_data, _locale_t loc) +{ +#if _MSVCR_VER <= 90 + TRACE("(%p %Iu %s %p %p %p)\n", str, max, format, mstm, time_data, loc); + return strftime_impl(str, max, format, mstm, time_data, loc); +#else + wchar_t *s, *fmt; + size_t len; + int cp; + + TRACE("(%p %Iu %s %p %p %p)\n", str, max, format, mstm, time_data, loc); + + if (!MSVCRT_CHECK_PMT(str != NULL)) return 0; + if (!MSVCRT_CHECK_PMT(max != 0)) return 0; + *str = 0; + if (!MSVCRT_CHECK_PMT(format != NULL)) return 0; + + cp = (loc ? loc->locinfo : get_locinfo())->lc_id[LC_TIME].wCodePage; + + len = MultiByteToWideChar( cp, 0, format, -1, NULL, 0 ); + if (!len) + { + *_errno() = EILSEQ; + return 0; + } + fmt = malloc( len*sizeof(wchar_t) ); + if (!fmt) return 0; + MultiByteToWideChar( cp, 0, format, -1, fmt, len ); + + if ((s = malloc( max*sizeof(wchar_t) ))) + { + len = strftime_impl( s, max, fmt, mstm, time_data, loc ); + if (len) + { + len = WideCharToMultiByte( cp, 0, s, -1, str, max, NULL, NULL ); + if (len) len--; + else *_errno() = EILSEQ; + } + free( s ); + } + else len = 0; + + free( fmt ); + return len; +#endif +} + +#if _MSVCR_VER >= 80 +/******************************************************************** + * _strftime_l (MSVCR80.@) + */ +size_t CDECL _strftime_l( char *str, size_t max, const char *format, + const struct tm *mstm, _locale_t loc ) +{ + return strftime_helper(str, max, format, mstm, NULL, loc); +} +#endif + +/********************************************************************* + * _Strftime (MSVCRT.@) + */ +size_t CDECL _Strftime(char *str, size_t max, const char *format, + const struct tm *mstm, void *time_data) +{ + return strftime_helper(str, max, format, mstm, time_data, NULL); +} + +/********************************************************************* + * strftime (MSVCRT.@) + */ +size_t CDECL strftime( char *str, size_t max, const char *format, + const struct tm *mstm ) +{ + return strftime_helper(str, max, format, mstm, NULL, NULL); +} + +static size_t wcsftime_helper( wchar_t *str, size_t max, + const wchar_t *format, const struct tm *mstm, + __lc_time_data *time_data, _locale_t loc ) +{ +#if _MSVCR_VER <= 90 + char *s, *fmt; + size_t len; + + TRACE("%p %Iu %s %p %p %p\n", str, max, debugstr_w(format), mstm, time_data, loc); + + len = _wcstombs_l( NULL, format, 0, loc ) + 1; + if (!(fmt = malloc( len ))) return 0; + _wcstombs_l(fmt, format, len, loc); + + if ((s = malloc( max*4 ))) + { + if (!strftime_impl( s, max*4, fmt, mstm, time_data, loc )) s[0] = 0; + len = _mbstowcs_l( str, s, max, loc ); + free( s ); + } + else len = 0; + + free( fmt ); + return len; +#else + TRACE("%p %Iu %s %p %p %p\n", str, max, debugstr_w(format), mstm, time_data, loc); + return strftime_impl(str, max, format, mstm, time_data, loc); +#endif +} + +/********************************************************************* + * _wcsftime_l (MSVCRT.@) + */ +size_t CDECL _wcsftime_l( wchar_t *str, size_t max, + const wchar_t *format, const struct tm *mstm, _locale_t loc ) +{ + return wcsftime_helper(str, max, format, mstm, NULL, loc); +} + +/********************************************************************* + * wcsftime (MSVCRT.@) + */ +size_t CDECL wcsftime( wchar_t *str, size_t max, + const wchar_t *format, const struct tm *mstm ) +{ + return wcsftime_helper(str, max, format, mstm, NULL, NULL); +} + +#if _MSVCR_VER >= 110 +/********************************************************************* + * _Wcsftime (MSVCR110.@) + */ +size_t CDECL _Wcsftime(wchar_t *str, size_t max, + const wchar_t *format, const struct tm *mstm, + __lc_time_data *time_data) +{ + return wcsftime_helper(str, max, format, mstm, time_data, NULL); +} +#endif + +static char* asctime_buf(char *buf, const struct tm *mstm) +{ + static const char wday[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + static const char month[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", + "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + if (!mstm || mstm->tm_sec<0 || mstm->tm_sec>59 + || mstm->tm_min<0 || mstm->tm_min>59 + || mstm->tm_hour<0 || mstm->tm_hour>23 + || mstm->tm_mon<0 || mstm->tm_mon>11 + || mstm->tm_wday<0 || mstm->tm_wday>6 + || mstm->tm_year<0 || mstm->tm_mday<0 + || mstm->tm_mday>MonthLengths[IsLeapYear(1900+mstm->tm_year)][mstm->tm_mon]) { + *_errno() = EINVAL; + return NULL; + } + +#if _MSVCR_VER>=140 + /* C89 (4.12.3.1) uses space-padding for day of month. */ + _snprintf(buf, 26, "%s %s %2d %02d:%02d:%02d %c%03d\n", wday[mstm->tm_wday], + month[mstm->tm_mon], mstm->tm_mday, mstm->tm_hour, mstm->tm_min, + mstm->tm_sec, '1'+(mstm->tm_year+900)/1000, (900+mstm->tm_year)%1000); +#else + _snprintf(buf, 26, "%s %s %02d %02d:%02d:%02d %c%03d\n", wday[mstm->tm_wday], + month[mstm->tm_mon], mstm->tm_mday, mstm->tm_hour, mstm->tm_min, + mstm->tm_sec, '1'+(mstm->tm_year+900)/1000, (900+mstm->tm_year)%1000); +#endif + return buf; +} + +/********************************************************************* + * asctime (MSVCRT.@) + */ +char * CDECL asctime(const struct tm *mstm) +{ + thread_data_t *data = msvcrt_get_thread_data(); + + /* asctime returns date in format that always has exactly 26 characters */ + if (!data->asctime_buffer) { + data->asctime_buffer = malloc(26); + if (!data->asctime_buffer) { + *_errno() = ENOMEM; + return NULL; + } + } + + return asctime_buf(data->asctime_buffer, mstm); +} + +/********************************************************************* + * asctime_s (MSVCRT.@) + */ +int CDECL asctime_s(char* time, size_t size, const struct tm *mstm) +{ + if (!MSVCRT_CHECK_PMT(time != NULL)) return EINVAL; + if (size) time[0] = 0; + if (!MSVCRT_CHECK_PMT(size >= 26)) return EINVAL; + if (!MSVCRT_CHECK_PMT(mstm != NULL)) return EINVAL; + if (!MSVCRT_CHECK_PMT(mstm->tm_sec >= 0 && mstm->tm_sec < 60)) return EINVAL; + if (!MSVCRT_CHECK_PMT(mstm->tm_min >= 0 && mstm->tm_min < 60)) return EINVAL; + if (!MSVCRT_CHECK_PMT(mstm->tm_hour >= 0 && mstm->tm_hour < 24)) return EINVAL; + if (!MSVCRT_CHECK_PMT(mstm->tm_mon >= 0 && mstm->tm_mon < 12)) return EINVAL; + if (!MSVCRT_CHECK_PMT(mstm->tm_wday >= 0 && mstm->tm_wday < 7)) return EINVAL; + if (!MSVCRT_CHECK_PMT(mstm->tm_year >= 0)) return EINVAL; + if (!MSVCRT_CHECK_PMT(mstm->tm_mday >= 0)) return EINVAL; + if (!MSVCRT_CHECK_PMT(mstm->tm_mday <= MonthLengths[IsLeapYear(1900+mstm->tm_year)][mstm->tm_mon])) return EINVAL; + + asctime_buf(time, mstm); + return 0; +} + +/********************************************************************* + * _wasctime (MSVCRT.@) + */ +wchar_t * CDECL _wasctime(const struct tm *mstm) +{ + thread_data_t *data = msvcrt_get_thread_data(); + char buffer[26]; + + if(!data->wasctime_buffer) { + data->wasctime_buffer = malloc(26*sizeof(wchar_t)); + if(!data->wasctime_buffer) { + *_errno() = ENOMEM; + return NULL; + } + } + + if(!asctime_buf(buffer, mstm)) + return NULL; + + MultiByteToWideChar(CP_ACP, 0, buffer, -1, data->wasctime_buffer, 26); + return data->wasctime_buffer; +} + +/********************************************************************* + * _wasctime_s (MSVCRT.@) + */ +int CDECL _wasctime_s(wchar_t* time, size_t size, const struct tm *mstm) +{ + char buffer[26]; + int ret; + + if (!MSVCRT_CHECK_PMT(time != NULL)) return EINVAL; + if (size) time[0] = 0; + if (!MSVCRT_CHECK_PMT(size >= 26)) return EINVAL; + if (!MSVCRT_CHECK_PMT(mstm != NULL)) return EINVAL; + + ret = asctime_s(buffer, sizeof(buffer), mstm); + if(ret) + return ret; + MultiByteToWideChar(CP_ACP, 0, buffer, -1, time, size); + return 0; +} + +/********************************************************************* + * _ctime64 (MSVCRT.@) + */ +char * CDECL _ctime64(const __time64_t *time) +{ + struct tm *t; + t = _localtime64( time ); + if (!t) return NULL; + return asctime( t ); +} + +/********************************************************************* + * _ctime64_s (MSVCRT.@) + */ +errno_t CDECL _ctime64_s(char *res, size_t len, const __time64_t *time) +{ + struct tm t; + int ret; + + if (!MSVCRT_CHECK_PMT( res != NULL )) return EINVAL; + if (!MSVCRT_CHECK_PMT( len >= 26 )) return EINVAL; + res[0] = '\0'; + if (!MSVCRT_CHECK_PMT( time != NULL )) return EINVAL; + if (!MSVCRT_CHECK_PMT( *time > 0 )) return EINVAL; + + ret = _localtime64_s( &t, time ); + if (ret) + return ret; + return asctime_s( res, len, &t ); +} + +/********************************************************************* + * _ctime32 (MSVCRT.@) + */ +char * CDECL _ctime32(const __time32_t *time) +{ + struct tm *t; + t = _localtime32( time ); + if (!t) return NULL; + return asctime( t ); +} + +/********************************************************************* + * _ctime32_s (MSVCRT.@) + */ +errno_t CDECL _ctime32_s(char *res, size_t len, const __time32_t *time) +{ + struct tm t; + int ret; + + if (!MSVCRT_CHECK_PMT( res != NULL )) return EINVAL; + if (!MSVCRT_CHECK_PMT( len >= 26 )) return EINVAL; + res[0] = '\0'; + if (!MSVCRT_CHECK_PMT( time != NULL )) return EINVAL; + if (!MSVCRT_CHECK_PMT( *time > 0 )) return EINVAL; + + ret = _localtime32_s( &t, time ); + if (ret) + return ret; + return asctime_s( res, len, &t ); +} + +/********************************************************************* + * _wctime64 (MSVCRT.@) + */ +wchar_t * CDECL _wctime64(const __time64_t *time) +{ + return _wasctime( _localtime64(time) ); +} + +/********************************************************************* + * _wctime32 (MSVCRT.@) + */ +wchar_t * CDECL _wctime32(const __time32_t *time) +{ + return _wasctime( _localtime32(time) ); +} + +/********************************************************************* + * _wctime64_s (MSVCRT.@) + */ +int CDECL _wctime64_s(wchar_t *buf, + size_t size, const __time64_t *time) +{ + struct tm tm; + int ret; + + if(!MSVCRT_CHECK_PMT(buf != NULL)) return EINVAL; + if(!MSVCRT_CHECK_PMT(size != 0)) return EINVAL; + buf[0] = 0; + if(!MSVCRT_CHECK_PMT(time != NULL)) return EINVAL; + if(!MSVCRT_CHECK_PMT(*time >= 0)) return EINVAL; + if(!MSVCRT_CHECK_PMT(*time <= _MAX__TIME64_T)) return EINVAL; + + ret = _localtime64_s(&tm, time); + if(ret != 0) + return ret; + + return _wasctime_s(buf, size, &tm); +} + +/********************************************************************* + * _wctime32_s (MSVCRT.@) + */ +int CDECL _wctime32_s(wchar_t *buf, size_t size, + const __time32_t *time) +{ + struct tm tm; + int ret; + + if(!MSVCRT_CHECK_PMT(buf != NULL)) return EINVAL; + if(!MSVCRT_CHECK_PMT(size != 0)) return EINVAL; + buf[0] = 0; + if(!MSVCRT_CHECK_PMT(time != NULL)) return EINVAL; + if(!MSVCRT_CHECK_PMT(*time >= 0)) return EINVAL; + + ret = _localtime32_s(&tm, time); + if(ret != 0) + return ret; + + return _wasctime_s(buf, size, &tm); +} + +#if _MSVCR_VER >= 80 + +/********************************************************************* + * _get_timezone (MSVCR80.@) + */ +int CDECL _get_timezone(LONG *timezone) +{ + if(!MSVCRT_CHECK_PMT(timezone != NULL)) return EINVAL; + + *timezone = MSVCRT___timezone; + return 0; +} + +/********************************************************************* + * _get_daylight (MSVCR80.@) + */ +int CDECL _get_daylight(int *hours) +{ + if(!MSVCRT_CHECK_PMT(hours != NULL)) return EINVAL; + + *hours = MSVCRT___daylight; + return 0; +} + +#endif /* _MSVCR_VER >= 80 */ + +#if _MSVCR_VER >= 140 + +#define TIME_UTC 1 + +/********************************************************************* + * _timespec64_get (UCRTBASE.@) + */ +int CDECL _timespec64_get(struct _timespec64 *ts, int base) +{ + ULONGLONG time; + FILETIME ft; + + if(!MSVCRT_CHECK_PMT(ts != NULL)) return 0; + if(base != TIME_UTC) return 0; + + GetSystemTimePreciseAsFileTime(&ft); + time = ((ULONGLONG)ft.dwHighDateTime << 32) | ft.dwLowDateTime; + + ts->tv_sec = time / TICKSPERSEC - SECS_1601_TO_1970; + ts->tv_nsec = time % TICKSPERSEC * 100; + return base; +} + +/********************************************************************* + * _timespec32_get (UCRTBASE.@) + */ +int CDECL _timespec32_get(struct _timespec32 *ts, int base) +{ + struct _timespec64 ts64; + + if(!MSVCRT_CHECK_PMT(ts != NULL)) return 0; + if(base != TIME_UTC) return 0; + + if(_timespec64_get(&ts64, base) != base) + return 0; + if(ts64.tv_sec != (__time32_t)ts64.tv_sec) + return 0; + + ts->tv_sec = ts64.tv_sec; + ts->tv_nsec = ts64.tv_nsec; + return base; +} +#endif /* _MSVCR_VER >= 140 */ diff --git a/dll/win32/msvcrt/undname.c b/dll/win32/msvcrt/undname.c new file mode 100644 index 00000000000..923ec67d363 --- /dev/null +++ b/dll/win32/msvcrt/undname.c @@ -0,0 +1,1788 @@ +/* + * Demangle VC++ symbols into C function prototypes + * + * Copyright 2000 Jon Griffiths + * 2004 Eric Pouech + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include +#include +#include +#include "msvcrt.h" +#include "winver.h" +#include "imagehlp.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +/* How data types qualifiers are stored: + * M (in the following definitions) is defined for + * 'A', 'B', 'C' and 'D' as follows + * {}: "" + * {}: "const " + * {}: "volatile " + * {}: "const volatile " + * + * in arguments: + * Px {}x* + * Qx {}x* const + * Ax {}x& + * in data fields: + * same as for arguments and also the following + * ?x {}x + * + */ + +#define UNDNAME_NO_COMPLEX_TYPE (0x8000) + +struct array +{ + unsigned start; /* first valid reference in array */ + unsigned num; /* total number of used elts */ + unsigned max; + unsigned alloc; + char** elts; +}; + +/* Structure holding a parsed symbol */ +struct parsed_symbol +{ + unsigned flags; /* the UNDNAME_ flags used for demangling */ + malloc_func_t mem_alloc_ptr; /* internal allocator */ + free_func_t mem_free_ptr; /* internal deallocator */ + + const char* current; /* pointer in input (mangled) string */ + char* result; /* demangled string */ + + struct array names; /* array of names for back reference */ + struct array args; /* array of arguments for back reference */ + struct array stack; /* stack of parsed strings */ + + void* alloc_list; /* linked list of allocated blocks */ + unsigned avail_in_first; /* number of available bytes in head block */ +}; + +enum datatype_e +{ + DT_NO_LEADING_WS = 0x01, + DT_NO_LRSEP_WS = 0x02, +}; + +/* Type for parsing mangled types */ +struct datatype_t +{ + const char* left; + const char* right; + enum datatype_e flags; +}; + +static BOOL symbol_demangle(struct parsed_symbol* sym); +static char* get_class_name(struct parsed_symbol* sym); + +/****************************************************************** + * und_alloc + * + * Internal allocator. Uses a simple linked list of large blocks + * where we use a poor-man allocator. It's fast, and since all + * allocation is pool, memory management is easy (esp. freeing). + */ +static void* und_alloc(struct parsed_symbol* sym, unsigned int len) +{ + void* ptr; + +#define BLOCK_SIZE 1024 +#define AVAIL_SIZE (1024 - sizeof(void*)) + + if (len > AVAIL_SIZE) + { + /* allocate a specific block */ + ptr = sym->mem_alloc_ptr(sizeof(void*) + len); + if (!ptr) return NULL; + *(void**)ptr = sym->alloc_list; + sym->alloc_list = ptr; + sym->avail_in_first = 0; + ptr = (char*)sym->alloc_list + sizeof(void*); + } + else + { + if (len > sym->avail_in_first) + { + /* add a new block */ + ptr = sym->mem_alloc_ptr(BLOCK_SIZE); + if (!ptr) return NULL; + *(void**)ptr = sym->alloc_list; + sym->alloc_list = ptr; + sym->avail_in_first = AVAIL_SIZE; + } + /* grab memory from head block */ + ptr = (char*)sym->alloc_list + BLOCK_SIZE - sym->avail_in_first; + sym->avail_in_first -= len; + } + return ptr; +#undef BLOCK_SIZE +#undef AVAIL_SIZE +} + +/****************************************************************** + * und_free + * Frees all the blocks in the list of large blocks allocated by + * und_alloc. + */ +static void und_free_all(struct parsed_symbol* sym) +{ + void* next; + + while (sym->alloc_list) + { + next = *(void**)sym->alloc_list; + if(sym->mem_free_ptr) sym->mem_free_ptr(sym->alloc_list); + sym->alloc_list = next; + } + sym->avail_in_first = 0; +} + +/****************************************************************** + * str_array_init + * Initialises an array of strings + */ +static void str_array_init(struct array* a) +{ + a->start = a->num = a->max = a->alloc = 0; + a->elts = NULL; +} + +/****************************************************************** + * str_array_push + * Adding a new string to an array + */ +static BOOL str_array_push(struct parsed_symbol* sym, const char* ptr, int len, + struct array* a) +{ + char** new; + + assert(ptr); + assert(a); + + if (!a->alloc) + { + new = und_alloc(sym, (a->alloc = 32) * sizeof(a->elts[0])); + if (!new) return FALSE; + a->elts = new; + } + else if (a->max >= a->alloc) + { + new = und_alloc(sym, (a->alloc * 2) * sizeof(a->elts[0])); + if (!new) return FALSE; + memcpy(new, a->elts, a->alloc * sizeof(a->elts[0])); + a->alloc *= 2; + a->elts = new; + } + if (len == -1) len = strlen(ptr); + a->elts[a->num] = und_alloc(sym, len + 1); + assert(a->elts[a->num]); + memcpy(a->elts[a->num], ptr, len); + a->elts[a->num][len] = '\0'; + if (++a->num >= a->max) a->max = a->num; + { + int i; + char c; + + for (i = a->max - 1; i >= 0; i--) + { + c = '>'; + if (i < a->start) c = '-'; + else if (i >= a->num) c = '}'; + TRACE("%p\t%d%c %s\n", a, i, c, debugstr_a(a->elts[i])); + } + } + + return TRUE; +} + +/****************************************************************** + * str_array_get_ref + * Extracts a reference from an existing array (doing proper type + * checking) + */ +static char* str_array_get_ref(struct array* cref, unsigned idx) +{ + assert(cref); + if (cref->start + idx >= cref->max) + { + WARN("Out of bounds: %p %d + %d >= %d\n", + cref, cref->start, idx, cref->max); + return NULL; + } + TRACE("Returning %p[%d] => %s\n", + cref, idx, debugstr_a(cref->elts[cref->start + idx])); + return cref->elts[cref->start + idx]; +} + +/****************************************************************** + * str_printf + * Helper for printf type of command (only %s and %c are implemented) + * while dynamically allocating the buffer + */ +static char* WINAPIV str_printf(struct parsed_symbol* sym, const char* format, ...) +{ + va_list args; + unsigned int len = 1, i, sz; + char* tmp; + char* p; + char* t; + + va_start(args, format); + for (i = 0; format[i]; i++) + { + if (format[i] == '%') + { + switch (format[++i]) + { + case 's': t = va_arg(args, char*); if (t) len += strlen(t); break; + case 'c': (void)va_arg(args, int); len++; break; + default: i--; /* fall through */ + case '%': len++; break; + } + } + else len++; + } + va_end(args); + if (!(tmp = und_alloc(sym, len))) return NULL; + va_start(args, format); + for (p = tmp, i = 0; format[i]; i++) + { + if (format[i] == '%') + { + switch (format[++i]) + { + case 's': + t = va_arg(args, char*); + if (t) + { + sz = strlen(t); + memcpy(p, t, sz); + p += sz; + } + break; + case 'c': + *p++ = (char)va_arg(args, int); + break; + default: i--; /* fall through */ + case '%': *p++ = '%'; break; + } + } + else *p++ = format[i]; + } + va_end(args); + *p = '\0'; + return tmp; +} + +enum datatype_flags +{ + IN_ARGS = 0x01, + WS_AFTER_QUAL_IF = 0x02, +}; + +/* forward declaration */ +static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct, + enum datatype_flags flags); + +static const char* get_number(struct parsed_symbol* sym) +{ + char* ptr; + BOOL sgn = FALSE; + + if (*sym->current == '?') + { + sgn = TRUE; + sym->current++; + } + if (*sym->current >= '0' && *sym->current <= '8') + { + ptr = und_alloc(sym, 3); + if (sgn) ptr[0] = '-'; + ptr[sgn ? 1 : 0] = *sym->current + 1; + ptr[sgn ? 2 : 1] = '\0'; + sym->current++; + } + else if (*sym->current == '9') + { + ptr = und_alloc(sym, 4); + if (sgn) ptr[0] = '-'; + ptr[sgn ? 1 : 0] = '1'; + ptr[sgn ? 2 : 1] = '0'; + ptr[sgn ? 3 : 2] = '\0'; + sym->current++; + } + else if (*sym->current >= 'A' && *sym->current <= 'P') + { + int ret = 0; + + while (*sym->current >= 'A' && *sym->current <= 'P') + { + ret *= 16; + ret += *sym->current++ - 'A'; + } + if (*sym->current != '@') return NULL; + + ptr = und_alloc(sym, 17); + sprintf(ptr, "%s%u", sgn ? "-" : "", ret); + sym->current++; + } + else return NULL; + return ptr; +} + +/****************************************************************** + * get_args + * Parses a list of function/method arguments, creates a string corresponding + * to the arguments' list. + */ +static char* get_args(struct parsed_symbol* sym, BOOL z_term, + char open_char, char close_char) + +{ + struct datatype_t ct; + struct array arg_collect; + char* args_str = NULL; + char* last; + unsigned int i; + const char *p; + + if (z_term && sym->current[0] == 'X' && sym->current[1] == 'Z') + { + sym->current += 2; + return str_printf(sym, "%cvoid%c", open_char, close_char); + } + + str_array_init(&arg_collect); + + /* Now come the function arguments */ + while (*sym->current) + { + p = sym->current; + + /* Decode each data type and append it to the argument list */ + if (*sym->current == '@') + { + sym->current++; + break; + } + if (z_term && sym->current[0] == 'Z') + { + sym->current++; + if (!str_array_push(sym, "...", -1, &arg_collect)) + return NULL; + break; + } + /* Handle empty list in variadic template */ + if (!z_term && sym->current[0] == '$' && sym->current[1] == '$' && sym->current[2] == 'V') + { + sym->current += 3; + continue; + } + if (!demangle_datatype(sym, &ct, IN_ARGS)) + return NULL; + if (!str_array_push(sym, str_printf(sym, "%s%s", ct.left, ct.right), -1, + &arg_collect)) + return NULL; + if (z_term && sym->current - p > 1 && sym->args.num < 20) + { + if (!str_array_push(sym, ct.left ? ct.left : "", -1, &sym->args) || + !str_array_push(sym, ct.right ? ct.right : "", -1, &sym->args)) + return NULL; + } + } + /* Functions are always terminated by 'Z'. If we made it this far and + * don't find it, we have incorrectly identified a data type. + */ + if (z_term && *sym->current++ != 'Z') return NULL; + + if (!arg_collect.num) return str_printf(sym, "%c%c", open_char, close_char); + for (i = 1; i < arg_collect.num; i++) + { + args_str = str_printf(sym, "%s,%s", args_str, arg_collect.elts[i]); + } + + last = args_str ? args_str : arg_collect.elts[0]; + if (close_char == '>' && last[strlen(last) - 1] == '>') + args_str = str_printf(sym, "%c%s%s %c", + open_char, arg_collect.elts[0], args_str, close_char); + else + args_str = str_printf(sym, "%c%s%s%c", + open_char, arg_collect.elts[0], args_str, close_char); + + return args_str; +} + +static void append_extended_qualifier(struct parsed_symbol *sym, const char **where, + const char *str, BOOL is_ms_keyword) +{ + if (!is_ms_keyword || !(sym->flags & UNDNAME_NO_MS_KEYWORDS)) + { + if (is_ms_keyword && (sym->flags & UNDNAME_NO_LEADING_UNDERSCORES)) + str += 2; + *where = *where ? str_printf(sym, "%s%s%s%s", *where, is_ms_keyword ? " " : "", str, is_ms_keyword ? "" : " ") : + str_printf(sym, "%s%s", str, is_ms_keyword ? "" : " "); + } +} + +static void get_extended_qualifier(struct parsed_symbol *sym, struct datatype_t *xdt) +{ + unsigned fl = 0; + xdt->left = xdt->right = NULL; + xdt->flags = 0; + for (;;) + { + switch (*sym->current) + { + case 'E': append_extended_qualifier(sym, &xdt->right, "__ptr64", TRUE); fl |= 2; break; + case 'F': append_extended_qualifier(sym, &xdt->left, "__unaligned", TRUE); fl |= 2; break; +#ifdef _UCRT + case 'G': append_extended_qualifier(sym, &xdt->right, "&", FALSE); fl |= 1; break; + case 'H': append_extended_qualifier(sym, &xdt->right, "&&", FALSE); fl |= 1; break; +#endif + case 'I': append_extended_qualifier(sym, &xdt->right, "__restrict", TRUE); fl |= 2; break; + default: if (fl == 1 || (fl == 3 && (sym->flags & UNDNAME_NO_MS_KEYWORDS))) xdt->flags = DT_NO_LRSEP_WS; return; + } + sym->current++; + } +} + +/****************************************************************** + * get_qualifier + * Parses the type qualifier. Always returns static strings. + */ +static BOOL get_qualifier(struct parsed_symbol *sym, struct datatype_t *xdt, const char** pclass) +{ + char ch; + const char* qualif; + + get_extended_qualifier(sym, xdt); + switch (ch = *sym->current++) + { + case 'A': qualif = NULL; break; + case 'B': qualif = "const"; break; + case 'C': qualif = "volatile"; break; + case 'D': qualif = "const volatile"; break; + case 'Q': qualif = NULL; break; + case 'R': qualif = "const"; break; + case 'S': qualif = "volatile"; break; + case 'T': qualif = "const volatile"; break; + default: return FALSE; + } + if (qualif) + { + xdt->flags &= ~DT_NO_LRSEP_WS; + xdt->left = xdt->left ? str_printf(sym, "%s %s", qualif, xdt->left) : qualif; + } + if (ch >= 'Q' && ch <= 'T') /* pointer to member, fetch class */ + { + const char* class = get_class_name(sym); + if (!class) return FALSE; + if (!pclass) + { + FIXME("Got pointer to class %s member without storage\n", class); + return FALSE; + } + *pclass = class; + } + else if (pclass) *pclass = NULL; + return TRUE; +} + +static BOOL get_function_qualifier(struct parsed_symbol *sym, const char** qualif) +{ + struct datatype_t xdt; + + if (!get_qualifier(sym, &xdt, NULL)) return FALSE; + *qualif = (xdt.left || xdt.right) ? + str_printf(sym, "%s%s%s", xdt.left, (xdt.flags & DT_NO_LRSEP_WS) ? "" : " ", xdt.right) : NULL; + return TRUE; +} + +static BOOL get_qualified_type(struct datatype_t *ct, struct parsed_symbol* sym, + char qualif, enum datatype_flags flags) +{ + struct datatype_t xdt1; + struct datatype_t xdt2; + const char* ref; + const char* str_qualif; + const char* class; + + get_extended_qualifier(sym, &xdt1); + + switch (qualif) + { + case 'A': ref = " &"; str_qualif = NULL; break; + case 'B': ref = " &"; str_qualif = " volatile"; break; + case 'P': ref = " *"; str_qualif = NULL; break; + case 'Q': ref = " *"; str_qualif = " const"; break; + case 'R': ref = " *"; str_qualif = " volatile"; break; + case 'S': ref = " *"; str_qualif = " const volatile"; break; + case '?': ref = NULL; str_qualif = NULL; break; + case '$': ref = " &&"; str_qualif = NULL; break; + default: return FALSE; + } + ct->right = NULL; + ct->flags = 0; + + /* parse managed handle information */ + if (sym->current[0] == '$' && sym->current[1] == 'A') + { + sym->current += 2; + + switch (qualif) + { + case 'A': + case 'B': + ref = " %"; + break; + case 'P': + case 'Q': + case 'R': + case 'S': + ref = " ^"; + break; + default: + return FALSE; + } + } + + if (get_qualifier(sym, &xdt2, &class)) + { + unsigned mark = sym->stack.num; + struct datatype_t sub_ct; + + if (ref || str_qualif || xdt1.left || xdt1.right) + { + if (class) + ct->left = str_printf(sym, "%s%s%s%s::%s%s%s", + xdt1.left ? " " : NULL, xdt1.left, + class ? " " : NULL, class, ref ? ref + 1 : NULL, + xdt1.right ? " " : NULL, xdt1.right, str_qualif); + else + ct->left = str_printf(sym, "%s%s%s%s%s%s", + xdt1.left ? " " : NULL, xdt1.left, ref, + xdt1.right ? " " : NULL, xdt1.right, str_qualif); + } + else + ct->left = NULL; + /* multidimensional arrays */ + if (*sym->current == 'Y') + { + const char* n1; + int num; + + sym->current++; + if (!(n1 = get_number(sym))) return FALSE; + num = atoi(n1); + + ct->left = str_printf(sym, " (%s%s", xdt2.left, ct->left && !xdt2.left ? ct->left + 1 : ct->left); + ct->right = ")"; + xdt2.left = NULL; + + while (num--) + ct->right = str_printf(sym, "%s[%s]", ct->right, get_number(sym)); + } + + /* Recurse to get the referred-to type */ + if (!demangle_datatype(sym, &sub_ct, 0)) + return FALSE; + if (sub_ct.flags & DT_NO_LEADING_WS) + ct->left++; + ct->left = str_printf(sym, "%s%s%s%s%s", sub_ct.left, xdt2.left ? " " : NULL, + xdt2.left, ct->left, + ((xdt2.left || str_qualif) && (flags & WS_AFTER_QUAL_IF)) ? " " : NULL); + if (sub_ct.right) ct->right = str_printf(sym, "%s%s", ct->right, sub_ct.right); + sym->stack.num = mark; + } + else if (ref || str_qualif || xdt1.left || xdt1.right) + ct->left = str_printf(sym, "%s%s%s%s%s%s", + xdt1.left ? " " : NULL, xdt1.left, ref, + xdt1.right ? " " : NULL, xdt1.right, str_qualif); + else + ct->left = NULL; + return TRUE; +} + +/****************************************************************** + * get_literal_string + * Gets the literal name from the current position in the mangled + * symbol to the first '@' character. It pushes the parsed name to + * the symbol names stack and returns a pointer to it or NULL in + * case of an error. + */ +static char* get_literal_string(struct parsed_symbol* sym) +{ + const char *ptr = sym->current; + + do { + if (!((*sym->current >= 'A' && *sym->current <= 'Z') || + (*sym->current >= 'a' && *sym->current <= 'z') || + (*sym->current >= '0' && *sym->current <= '9') || + *sym->current == '_' || *sym->current == '$' || + *sym->current == '<' || *sym->current == '>')) { + TRACE("Failed at '%c' in %s\n", *sym->current, debugstr_a(ptr)); + return NULL; + } + } while (*++sym->current != '@'); + sym->current++; + if (!str_array_push(sym, ptr, sym->current - 1 - ptr, &sym->names)) + return NULL; + + return str_array_get_ref(&sym->names, sym->names.num - sym->names.start - 1); +} + +/****************************************************************** + * get_template_name + * Parses a name with a template argument list and returns it as + * a string. + * In a template argument list the back reference to the names + * table is separately created. '0' points to the class component + * name with the template arguments. We use the same stack array + * to hold the names but save/restore the stack state before/after + * parsing the template argument list. + */ +static char* get_template_name(struct parsed_symbol* sym) +{ + char *name, *args; + unsigned num_mark = sym->names.num; + unsigned start_mark = sym->names.start; + unsigned stack_mark = sym->stack.num; + unsigned args_mark = sym->args.num; + + sym->names.start = sym->names.num; + if (!(name = get_literal_string(sym))) { + sym->names.start = start_mark; + return FALSE; + } + args = get_args(sym, FALSE, '<', '>'); + if (args != NULL) + name = str_printf(sym, "%s%s", name, args); + sym->names.num = num_mark; + sym->names.start = start_mark; + sym->stack.num = stack_mark; + sym->args.num = args_mark; + return name; +} + +/****************************************************************** + * get_class + * Parses class as a list of parent-classes, terminated by '@' and stores the + * result in 'a' array. Each parent-classes, as well as the inner element + * (either field/method name or class name), are represented in the mangled + * name by a literal name ([a-zA-Z0-9_]+ terminated by '@') or a back reference + * ([0-9]) or a name with template arguments ('?$' literal name followed by the + * template argument list). The class name components appear in the reverse + * order in the mangled name, e.g aaa@bbb@ccc@@ will be demangled to + * ccc::bbb::aaa + * For each of these class name components a string will be allocated in the + * array. + */ +static BOOL get_class(struct parsed_symbol* sym) +{ + const char* name = NULL; + + while (*sym->current != '@') + { + switch (*sym->current) + { + case '\0': return FALSE; + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + name = str_array_get_ref(&sym->names, *sym->current++ - '0'); + break; + case '?': + switch (*++sym->current) + { + case '$': + sym->current++; + if ((name = get_template_name(sym)) && + !str_array_push(sym, name, -1, &sym->names)) + return FALSE; + break; + case '?': + { + struct array stack = sym->stack; + unsigned int start = sym->names.start; + unsigned int num = sym->names.num; + + str_array_init( &sym->stack ); + if (symbol_demangle( sym )) name = str_printf( sym, "`%s'", sym->result ); + sym->names.start = start; + sym->names.num = num; + sym->stack = stack; + } + break; + default: + if (!(name = get_number( sym ))) return FALSE; + name = str_printf( sym, "`%s'", name ); + break; + } + break; + default: + name = get_literal_string(sym); + break; + } + if (!name || !str_array_push(sym, name, -1, &sym->stack)) + return FALSE; + } + sym->current++; + return TRUE; +} + +/****************************************************************** + * get_class_string + * From an array collected by get_class in sym->stack, constructs the + * corresponding (allocated) string + */ +static char* get_class_string(struct parsed_symbol* sym, int start) +{ + int i; + unsigned int len, sz; + char* ret; + struct array *a = &sym->stack; + + for (len = 0, i = start; i < a->num; i++) + { + assert(a->elts[i]); + len += 2 + strlen(a->elts[i]); + } + if (!(ret = und_alloc(sym, len - 1))) return NULL; + for (len = 0, i = a->num - 1; i >= start; i--) + { + sz = strlen(a->elts[i]); + memcpy(ret + len, a->elts[i], sz); + len += sz; + if (i > start) + { + ret[len++] = ':'; + ret[len++] = ':'; + } + } + ret[len] = '\0'; + return ret; +} + +/****************************************************************** + * get_class_name + * Wrapper around get_class and get_class_string. + */ +static char* get_class_name(struct parsed_symbol* sym) +{ + unsigned mark = sym->stack.num; + char* s = NULL; + + if (get_class(sym)) + s = get_class_string(sym, mark); + sym->stack.num = mark; + return s; +} + +/****************************************************************** + * get_calling_convention + * Returns a static string corresponding to the calling convention described + * by char 'ch'. Sets export to TRUE iff the calling convention is exported. + */ +static BOOL get_calling_convention(char ch, const char** call_conv, + const char** exported, unsigned flags) +{ + *call_conv = *exported = NULL; + + if (!(flags & (UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ALLOCATION_LANGUAGE))) + { + if (flags & UNDNAME_NO_LEADING_UNDERSCORES) + { + if (((ch - 'A') % 2) == 1) *exported = "dll_export "; + switch (ch) + { + case 'A': case 'B': *call_conv = "cdecl"; break; + case 'C': case 'D': *call_conv = "pascal"; break; + case 'E': case 'F': *call_conv = "thiscall"; break; + case 'G': case 'H': *call_conv = "stdcall"; break; + case 'I': case 'J': *call_conv = "fastcall"; break; + case 'K': case 'L': break; + case 'M': *call_conv = "clrcall"; break; + default: ERR("Unknown calling convention %c\n", ch); return FALSE; + } + } + else + { + if (((ch - 'A') % 2) == 1) *exported = "__dll_export "; + switch (ch) + { + case 'A': case 'B': *call_conv = "__cdecl"; break; + case 'C': case 'D': *call_conv = "__pascal"; break; + case 'E': case 'F': *call_conv = "__thiscall"; break; + case 'G': case 'H': *call_conv = "__stdcall"; break; + case 'I': case 'J': *call_conv = "__fastcall"; break; + case 'K': case 'L': break; + case 'M': *call_conv = "__clrcall"; break; + default: ERR("Unknown calling convention %c\n", ch); return FALSE; + } + } + } + return TRUE; +} + +/******************************************************************* + * get_simple_type + * Return a string containing an allocated string for a simple data type + */ +static const char* get_simple_type(char c) +{ + const char* type_string; + + switch (c) + { + case 'C': type_string = "signed char"; break; + case 'D': type_string = "char"; break; + case 'E': type_string = "unsigned char"; break; + case 'F': type_string = "short"; break; + case 'G': type_string = "unsigned short"; break; + case 'H': type_string = "int"; break; + case 'I': type_string = "unsigned int"; break; + case 'J': type_string = "long"; break; + case 'K': type_string = "unsigned long"; break; + case 'M': type_string = "float"; break; + case 'N': type_string = "double"; break; + case 'O': type_string = "long double"; break; + case 'X': type_string = "void"; break; +/* case 'Z': (...) variadic function arguments. Handled in get_args() */ + default: type_string = NULL; break; + } + return type_string; +} + +/******************************************************************* + * get_extended_type + * Return a string containing an allocated string for a simple data type + */ +static const char* get_extended_type(char c) +{ + const char* type_string; + + switch (c) + { + case 'D': type_string = "__int8"; break; + case 'E': type_string = "unsigned __int8"; break; + case 'F': type_string = "__int16"; break; + case 'G': type_string = "unsigned __int16"; break; + case 'H': type_string = "__int32"; break; + case 'I': type_string = "unsigned __int32"; break; + case 'J': type_string = "__int64"; break; + case 'K': type_string = "unsigned __int64"; break; + case 'L': type_string = "__int128"; break; + case 'M': type_string = "unsigned __int128"; break; + case 'N': type_string = "bool"; break; + case 'Q': type_string = "char8_t"; break; + case 'S': type_string = "char16_t"; break; + case 'U': type_string = "char32_t"; break; + case 'W': type_string = "wchar_t"; break; + default: type_string = NULL; break; + } + return type_string; +} + +struct function_signature +{ + const char* call_conv; + const char* exported; + struct datatype_t return_ct; + const char* arguments; +}; + +static BOOL get_function_signature(struct parsed_symbol* sym, struct function_signature* fs) +{ + unsigned mark = sym->stack.num; + + if (!get_calling_convention(*sym->current++, + &fs->call_conv, &fs->exported, + sym->flags & ~UNDNAME_NO_ALLOCATION_LANGUAGE) || + !demangle_datatype(sym, &fs->return_ct, 0)) + return FALSE; + + if (!(fs->arguments = get_args(sym, TRUE, '(', ')'))) + return FALSE; + sym->stack.num = mark; + + return TRUE; +} + +/******************************************************************* + * demangle_datatype + * + * Attempt to demangle a C++ data type, which may be datatype. + * a datatype type is made up of a number of simple types. e.g: + * char** = (pointer to (pointer to (char))) + */ +static BOOL demangle_datatype(struct parsed_symbol* sym, struct datatype_t* ct, + enum datatype_flags flags) +{ + char dt; + + assert(ct); + ct->left = ct->right = NULL; + ct->flags = 0; + + switch (dt = *sym->current++) + { + case '_': + /* MS type: __int8,__int16 etc */ + ct->left = get_extended_type(*sym->current++); + break; + case 'C': case 'D': case 'E': case 'F': case 'G': + case 'H': case 'I': case 'J': case 'K': case 'M': + case 'N': case 'O': case 'X': case 'Z': + /* Simple data types */ + ct->left = get_simple_type(dt); + break; + case 'T': /* union */ + case 'U': /* struct */ + case 'V': /* class */ + case 'Y': /* cointerface */ + /* Class/struct/union/cointerface */ + { + const char* struct_name = NULL; + const char* type_name = NULL; + + if (!(struct_name = get_class_name(sym))) + goto done; + if (!(sym->flags & UNDNAME_NO_COMPLEX_TYPE)) + { + switch (dt) + { + case 'T': type_name = "union "; break; + case 'U': type_name = "struct "; break; + case 'V': type_name = "class "; break; + case 'Y': type_name = "cointerface "; break; + } + } + ct->left = str_printf(sym, "%s%s", type_name, struct_name); + } + break; + case '?': + /* not all the time is seems */ + if (flags & IN_ARGS) + { + const char* ptr; + if (!(ptr = get_number(sym))) goto done; + ct->left = str_printf(sym, "`template-parameter-%s'", ptr); + } + else + { + if (!get_qualified_type(ct, sym, '?', flags)) goto done; + } + break; + case 'A': /* reference */ + case 'B': /* volatile reference */ + if (!get_qualified_type(ct, sym, dt, flags)) goto done; + break; + case 'P': /* Pointer */ + case 'Q': /* const pointer */ + case 'R': /* volatile pointer */ + case 'S': /* const volatile pointer */ + if (!(flags & IN_ARGS)) dt = 'P'; + if (isdigit(*sym->current)) + { + const char *ptr_qualif; + switch (dt) + { + default: + case 'P': ptr_qualif = NULL; break; + case 'Q': ptr_qualif = "const"; break; + case 'R': ptr_qualif = "volatile"; break; + case 'S': ptr_qualif = "const volatile"; break; + } + /* FIXME: + * P6 = Function pointer + * P8 = Member function pointer + * others who knows.. */ + if (*sym->current == '8') + { + struct function_signature fs; + const char* class; + const char* function_qualifier; + + sym->current++; + + if (!(class = get_class_name(sym))) + goto done; + if (!get_function_qualifier(sym, &function_qualifier)) + goto done; + if (!get_function_signature(sym, &fs)) + goto done; + + ct->left = str_printf(sym, "%s%s (%s %s::*%s", + fs.return_ct.left, fs.return_ct.right, fs.call_conv, class, ptr_qualif); + ct->right = str_printf(sym, ")%s%s", fs.arguments, function_qualifier); + } + else if (*sym->current == '6') + { + struct function_signature fs; + + sym->current++; + + if (!get_function_signature(sym, &fs)) + goto done; + + ct->left = str_printf(sym, "%s%s (%s*%s", + fs.return_ct.left, fs.return_ct.right, fs.call_conv, ptr_qualif); + ct->flags = DT_NO_LEADING_WS; + ct->right = str_printf(sym, ")%s", fs.arguments); + } + else goto done; + } + else if (!get_qualified_type(ct, sym, dt, flags)) goto done; + break; + case 'W': + if (*sym->current == '4') + { + char* enum_name; + sym->current++; + if (!(enum_name = get_class_name(sym))) + goto done; + if (sym->flags & UNDNAME_NO_COMPLEX_TYPE) + ct->left = enum_name; + else + ct->left = str_printf(sym, "enum %s", enum_name); + } + else goto done; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + /* Referring back to previously parsed type */ + /* left and right are pushed as two separate strings */ + ct->left = str_array_get_ref(&sym->args, (dt - '0') * 2); + ct->right = str_array_get_ref(&sym->args, (dt - '0') * 2 + 1); + if (!ct->left) goto done; + break; + case '$': + switch (*sym->current++) + { + case '0': + if (!(ct->left = get_number(sym))) goto done; + break; + case 'D': + { + const char* ptr; + if (!(ptr = get_number(sym))) goto done; + ct->left = str_printf(sym, "`template-parameter%s'", ptr); + } + break; + case 'F': + { + const char* p1; + const char* p2; + if (!(p1 = get_number(sym))) goto done; + if (!(p2 = get_number(sym))) goto done; + ct->left = str_printf(sym, "{%s,%s}", p1, p2); + } + break; + case 'G': + { + const char* p1; + const char* p2; + const char* p3; + if (!(p1 = get_number(sym))) goto done; + if (!(p2 = get_number(sym))) goto done; + if (!(p3 = get_number(sym))) goto done; + ct->left = str_printf(sym, "{%s,%s,%s}", p1, p2, p3); + } + break; + case 'Q': + { + const char* ptr; + if (!(ptr = get_number(sym))) goto done; + ct->left = str_printf(sym, "`non-type-template-parameter%s'", ptr); + } + break; + case '$': + if (*sym->current == 'A') + { + sym->current++; + if (*sym->current == '6') + { + struct function_signature fs; + + sym->current++; + + if (!get_function_signature(sym, &fs)) + goto done; + ct->left = str_printf(sym, "%s%s %s%s", + fs.return_ct.left, fs.return_ct.right, fs.call_conv, fs.arguments); + } + } + else if (*sym->current == 'B') + { + unsigned mark = sym->stack.num; + struct datatype_t sub_ct; + const char* arr = NULL; + sym->current++; + + /* multidimensional arrays */ + if (*sym->current == 'Y') + { + const char* n1; + int num; + + sym->current++; + if (!(n1 = get_number(sym))) goto done; + num = atoi(n1); + + while (num--) + arr = str_printf(sym, "%s[%s]", arr, get_number(sym)); + } + + if (!demangle_datatype(sym, &sub_ct, 0)) goto done; + + if (arr) + ct->left = str_printf(sym, "%s %s", sub_ct.left, arr); + else + ct->left = sub_ct.left; + ct->right = sub_ct.right; + sym->stack.num = mark; + } + else if (*sym->current == 'C') + { + struct datatype_t xdt; + + sym->current++; + if (!get_qualifier(sym, &xdt, NULL)) goto done; + if (!demangle_datatype(sym, ct, flags)) goto done; + ct->left = str_printf(sym, "%s %s", ct->left, xdt.left); + } + else if (*sym->current == 'Q') + { + sym->current++; + if (!get_qualified_type(ct, sym, '$', flags)) goto done; + } + else if (*sym->current == 'T') + { + sym->current++; + ct->left = str_printf(sym, "std::nullptr_t"); + } + break; + } + break; + default : + ERR("Unknown type %c\n", dt); + break; + } +done: + + return ct->left != NULL; +} + +/****************************************************************** + * handle_data + * Does the final parsing and handling for a variable or a field in + * a class. + */ +static BOOL handle_data(struct parsed_symbol* sym) +{ + const char* access = NULL; + const char* member_type = NULL; + struct datatype_t xdt = {NULL}; + struct datatype_t ct; + char* name = NULL; + BOOL ret = FALSE; + + /* 0 private static + * 1 protected static + * 2 public static + * 3 private non-static + * 4 protected non-static + * 5 public non-static + * 6 ?? static + * 7 ?? static + */ + + if (!(sym->flags & UNDNAME_NO_ACCESS_SPECIFIERS)) + { + /* we only print the access for static members */ + switch (*sym->current) + { + case '0': access = "private: "; break; + case '1': access = "protected: "; break; + case '2': access = "public: "; break; + } + } + + if (!(sym->flags & UNDNAME_NO_MEMBER_TYPE)) + { + if (*sym->current >= '0' && *sym->current <= '2') + member_type = "static "; + } + + name = get_class_string(sym, 0); + + switch (*sym->current++) + { + case '0': case '1': case '2': + case '3': case '4': case '5': + { + unsigned mark = sym->stack.num; + const char* class; + + if (!demangle_datatype(sym, &ct, 0)) goto done; + if (!get_qualifier(sym, &xdt, &class)) goto done; /* class doesn't seem to be displayed */ + if (xdt.left && xdt.right) xdt.left = str_printf(sym, "%s %s", xdt.left, xdt.right); + else if (!xdt.left) xdt.left = xdt.right; + sym->stack.num = mark; + } + break; + case '6' : /* compiler generated static */ + case '7' : /* compiler generated static */ + ct.left = ct.right = NULL; + if (!get_qualifier(sym, &xdt, NULL)) goto done; + if (*sym->current != '@') + { + char* cls = NULL; + + if (!(cls = get_class_name(sym))) + goto done; + ct.right = str_printf(sym, "{for `%s'}", cls); + } + break; + case '8': + case '9': + xdt.left = ct.left = ct.right = NULL; + break; + default: goto done; + } + if (sym->flags & UNDNAME_NAME_ONLY) ct.left = ct.right = xdt.left = NULL; + + sym->result = str_printf(sym, "%s%s%s%s%s%s%s%s", access, + member_type, ct.left, + xdt.left && ct.left ? " " : NULL, xdt.left, + xdt.left || ct.left ? " " : NULL, name, ct.right); + ret = TRUE; +done: + return ret; +} + +/****************************************************************** + * handle_method + * Does the final parsing and handling for a function or a method in + * a class. + */ +static BOOL handle_method(struct parsed_symbol* sym, BOOL cast_op) +{ + char accmem; + const char* access = NULL; + int access_id = -1; + const char* member_type = NULL; + struct datatype_t ct_ret; + const char* call_conv; + const char* function_qualifier = NULL; + const char* exported; + const char* args_str = NULL; + const char* name = NULL; + BOOL ret = FALSE, has_args = TRUE, has_ret = TRUE; + unsigned mark; + + /* FIXME: why 2 possible letters for each option? + * 'A' private: + * 'B' private: + * 'C' private: static + * 'D' private: static + * 'E' private: virtual + * 'F' private: virtual + * 'G' private: thunk + * 'H' private: thunk + * 'I' protected: + * 'J' protected: + * 'K' protected: static + * 'L' protected: static + * 'M' protected: virtual + * 'N' protected: virtual + * 'O' protected: thunk + * 'P' protected: thunk + * 'Q' public: + * 'R' public: + * 'S' public: static + * 'T' public: static + * 'U' public: virtual + * 'V' public: virtual + * 'W' public: thunk + * 'X' public: thunk + * 'Y' + * 'Z' + * "$0" private: thunk vtordisp + * "$1" private: thunk vtordisp + * "$2" protected: thunk vtordisp + * "$3" protected: thunk vtordisp + * "$4" public: thunk vtordisp + * "$5" public: thunk vtordisp + * "$B" vcall thunk + * "$R" thunk vtordispex + */ + accmem = *sym->current++; + if (accmem == '$') + { + if (*sym->current >= '0' && *sym->current <= '5') + access_id = (*sym->current - '0') / 2; + else if (*sym->current == 'R') + access_id = (sym->current[1] - '0') / 2; + else if (*sym->current != 'B') + goto done; + } + else if (accmem >= 'A' && accmem <= 'Z') + access_id = (accmem - 'A') / 8; + else + goto done; + + switch (access_id) + { + case 0: access = "private: "; break; + case 1: access = "protected: "; break; + case 2: access = "public: "; break; + } + if (accmem == '$' || (accmem - 'A') % 8 == 6 || (accmem - 'A') % 8 == 7) + access = str_printf(sym, "[thunk]:%s", access ? access : " "); + + if (accmem == '$' && *sym->current != 'B') + member_type = "virtual "; + else if (accmem <= 'X') + { + switch ((accmem - 'A') % 8) + { + case 2: case 3: member_type = "static "; break; + case 4: case 5: case 6: case 7: member_type = "virtual "; break; + } + } + + if (sym->flags & UNDNAME_NO_ACCESS_SPECIFIERS) + access = NULL; + if (sym->flags & UNDNAME_NO_MEMBER_TYPE) + member_type = NULL; + + name = get_class_string(sym, 0); + + if (accmem == '$' && *sym->current == 'B') /* vcall thunk */ + { + const char *n; + + sym->current++; + n = get_number(sym); + + if(!n || *sym->current++ != 'A') goto done; + name = str_printf(sym, "%s{%s,{flat}}' }'", name, n); + has_args = FALSE; + has_ret = FALSE; + } + else if (accmem == '$' && *sym->current == 'R') /* vtordispex thunk */ + { + const char *n1, *n2, *n3, *n4; + + sym->current += 2; + n1 = get_number(sym); + n2 = get_number(sym); + n3 = get_number(sym); + n4 = get_number(sym); + + if(!n1 || !n2 || !n3 || !n4) goto done; + name = str_printf(sym, "%s`vtordispex{%s,%s,%s,%s}' ", name, n1, n2, n3, n4); + } + else if (accmem == '$') /* vtordisp thunk */ + { + const char *n1, *n2; + + sym->current++; + n1 = get_number(sym); + n2 = get_number(sym); + + if (!n1 || !n2) goto done; + name = str_printf(sym, "%s`vtordisp{%s,%s}' ", name, n1, n2); + } + else if ((accmem - 'A') % 8 == 6 || (accmem - 'A') % 8 == 7) /* a thunk */ + name = str_printf(sym, "%s`adjustor{%s}' ", name, get_number(sym)); + + if (has_args && (accmem == '$' || + (accmem <= 'X' && (accmem - 'A') % 8 != 2 && (accmem - 'A') % 8 != 3))) + { + /* Implicit 'this' pointer */ + if (!get_function_qualifier(sym, &function_qualifier)) goto done; + } + + if (!get_calling_convention(*sym->current++, &call_conv, &exported, + sym->flags)) + goto done; + + /* Return type, or @ if 'void' */ + if (has_ret && *sym->current == '@') + { + ct_ret.left = "void"; + ct_ret.right = NULL; + sym->current++; + } + else if (has_ret) + { + if (!demangle_datatype(sym, &ct_ret, cast_op ? WS_AFTER_QUAL_IF : 0)) + goto done; + } + if (!has_ret || sym->flags & UNDNAME_NO_FUNCTION_RETURNS) + ct_ret.left = ct_ret.right = NULL; + if (cast_op) + { + name = str_printf(sym, "%s %s%s", name, ct_ret.left, ct_ret.right); + ct_ret.left = ct_ret.right = NULL; + } + + mark = sym->stack.num; + if (has_args && !(args_str = get_args(sym, TRUE, '(', ')'))) goto done; + if (sym->flags & UNDNAME_NAME_ONLY) args_str = function_qualifier = NULL; + if (sym->flags & UNDNAME_NO_THISTYPE) function_qualifier = NULL; + sym->stack.num = mark; + + /* Note: '()' after 'Z' means 'throws', but we don't care here + * Yet!!! FIXME + */ + sym->result = str_printf(sym, "%s%s%s%s%s%s%s%s%s%s%s", + access, member_type, ct_ret.left, + (ct_ret.left && !ct_ret.right) ? " " : NULL, + call_conv, call_conv ? " " : NULL, exported, + name, args_str, function_qualifier, ct_ret.right); + ret = TRUE; +done: + return ret; +} + +/******************************************************************* + * symbol_demangle + * Demangle a C++ linker symbol + */ +static BOOL symbol_demangle(struct parsed_symbol* sym) +{ + BOOL ret = FALSE; + enum { + PP_NONE, + PP_CONSTRUCTOR, + PP_DESTRUCTOR, + PP_CAST_OPERATOR, + } post_process = PP_NONE; + + /* FIXME seems wrong as name, as it demangles a simple data type */ + if (sym->flags & UNDNAME_NO_ARGUMENTS) + { + struct datatype_t ct; + + if (demangle_datatype(sym, &ct, 0)) + { + sym->result = str_printf(sym, "%s%s", ct.left, ct.right); + ret = TRUE; + } + goto done; + } + + /* MS mangled names always begin with '?' */ + if (*sym->current != '?') return FALSE; + sym->current++; + + /* Then function name or operator code */ + if (*sym->current == '?') + { + const char* function_name = NULL; + BOOL in_template = FALSE; + + if (sym->current[1] == '$' && sym->current[2] == '?') + { + in_template = TRUE; + sym->current += 2; + } + + /* C++ operator code (one character, or two if the first is '_') */ + switch (*++sym->current) + { + case '0': function_name = ""; post_process = PP_CONSTRUCTOR; break; + case '1': function_name = ""; post_process = PP_DESTRUCTOR; break; + case '2': function_name = "operator new"; break; + case '3': function_name = "operator delete"; break; + case '4': function_name = "operator="; break; + case '5': function_name = "operator>>"; break; + case '6': function_name = "operator<<"; break; + case '7': function_name = "operator!"; break; + case '8': function_name = "operator=="; break; + case '9': function_name = "operator!="; break; + case 'A': function_name = "operator[]"; break; + case 'B': function_name = "operator"; post_process = PP_CAST_OPERATOR; break; + case 'C': function_name = "operator->"; break; + case 'D': function_name = "operator*"; break; + case 'E': function_name = "operator++"; break; + case 'F': function_name = "operator--"; break; + case 'G': function_name = "operator-"; break; + case 'H': function_name = "operator+"; break; + case 'I': function_name = "operator&"; break; + case 'J': function_name = "operator->*"; break; + case 'K': function_name = "operator/"; break; + case 'L': function_name = "operator%"; break; + case 'M': function_name = "operator<"; break; + case 'N': function_name = "operator<="; break; + case 'O': function_name = "operator>"; break; + case 'P': function_name = "operator>="; break; + case 'Q': function_name = "operator,"; break; + case 'R': function_name = "operator()"; break; + case 'S': function_name = "operator~"; break; + case 'T': function_name = "operator^"; break; + case 'U': function_name = "operator|"; break; + case 'V': function_name = "operator&&"; break; + case 'W': function_name = "operator||"; break; + case 'X': function_name = "operator*="; break; + case 'Y': function_name = "operator+="; break; + case 'Z': function_name = "operator-="; break; + case '_': + switch (*++sym->current) + { + case '0': function_name = "operator/="; break; + case '1': function_name = "operator%="; break; + case '2': function_name = "operator>>="; break; + case '3': function_name = "operator<<="; break; + case '4': function_name = "operator&="; break; + case '5': function_name = "operator|="; break; + case '6': function_name = "operator^="; break; + case '7': function_name = "`vftable'"; break; + case '8': function_name = "`vbtable'"; break; + case '9': function_name = "`vcall'"; break; + case 'A': function_name = "`typeof'"; break; + case 'B': function_name = "`local static guard'"; break; + case 'C': sym->result = (char*)"`string'"; /* string literal: followed by string encoding (native never undecode it) */ + /* FIXME: should unmangle the whole string for error reporting */ + if (*sym->current && sym->current[strlen(sym->current) - 1] == '@') ret = TRUE; + goto done; + case 'D': function_name = "`vbase destructor'"; break; + case 'E': function_name = "`vector deleting destructor'"; break; + case 'F': function_name = "`default constructor closure'"; break; + case 'G': function_name = "`scalar deleting destructor'"; break; + case 'H': function_name = "`vector constructor iterator'"; break; + case 'I': function_name = "`vector destructor iterator'"; break; + case 'J': function_name = "`vector vbase constructor iterator'"; break; + case 'K': function_name = "`virtual displacement map'"; break; + case 'L': function_name = "`eh vector constructor iterator'"; break; + case 'M': function_name = "`eh vector destructor iterator'"; break; + case 'N': function_name = "`eh vector vbase constructor iterator'"; break; + case 'O': function_name = "`copy constructor closure'"; break; + case 'R': + sym->flags |= UNDNAME_NO_FUNCTION_RETURNS; + switch (*++sym->current) + { + case '0': + { + struct datatype_t ct; + + sym->current++; + if (!demangle_datatype(sym, &ct, 0)) + goto done; + function_name = str_printf(sym, "%s%s `RTTI Type Descriptor'", + ct.left, ct.right); + sym->current--; + } + break; + case '1': + { + const char* n1, *n2, *n3, *n4; + sym->current++; + n1 = get_number(sym); + n2 = get_number(sym); + n3 = get_number(sym); + n4 = get_number(sym); + sym->current--; + function_name = str_printf(sym, "`RTTI Base Class Descriptor at (%s,%s,%s,%s)'", + n1, n2, n3, n4); + } + break; + case '2': function_name = "`RTTI Base Class Array'"; break; + case '3': function_name = "`RTTI Class Hierarchy Descriptor'"; break; + case '4': function_name = "`RTTI Complete Object Locator'"; break; + default: + ERR("Unknown RTTI operator: _R%c\n", *sym->current); + break; + } + break; + case 'S': function_name = "`local vftable'"; break; + case 'T': function_name = "`local vftable constructor closure'"; break; + case 'U': function_name = "operator new[]"; break; + case 'V': function_name = "operator delete[]"; break; + case 'X': function_name = "`placement delete closure'"; break; + case 'Y': function_name = "`placement delete[] closure'"; break; + case '_': + switch (*++sym->current) + { + case 'K': + sym->current++; + function_name = str_printf(sym, "operator \"\" %s", get_literal_string(sym)); + --sym->current; + break; + default: + FIXME("Unknown operator: __%c\n", *sym->current); + return FALSE; + } + break; + default: + ERR("Unknown operator: _%c\n", *sym->current); + return FALSE; + } + break; + case '$': + sym->current++; + if (!(function_name = get_template_name(sym))) goto done; + --sym->current; + break; + default: + /* FIXME: Other operators */ + ERR("Unknown operator: %c\n", *sym->current); + return FALSE; + } + sym->current++; + if (in_template) + { + unsigned args_mark = sym->args.num; + const char *args; + + args = get_args(sym, FALSE, '<', '>'); + if (args) function_name = function_name ? str_printf(sym, "%s%s", function_name, args) : args; + sym->args.num = args_mark; + sym->names.num = 0; + } + if (!str_array_push(sym, function_name, -1, &sym->stack)) + return FALSE; + } + else if (*sym->current == '$') + { + /* Strange construct, it's a name with a template argument list + and that's all. */ + sym->current++; + ret = (sym->result = get_template_name(sym)) != NULL; + goto done; + } + + /* Either a class name, or '@' if the symbol is not a class member */ + switch (*sym->current) + { + case '@': sym->current++; break; + case '$': break; + default: + /* Class the function is associated with, terminated by '@@' */ + if (!get_class(sym)) goto done; + break; + } + + switch (post_process) + { + case PP_NONE: default: break; + case PP_CONSTRUCTOR: case PP_DESTRUCTOR: + /* it's time to set the member name for ctor & dtor */ + if (sym->stack.num <= 1) goto done; + sym->stack.elts[0] = str_printf(sym, "%s%s%s", post_process == PP_DESTRUCTOR ? "~" : NULL, + sym->stack.elts[1], sym->stack.elts[0]); + /* ctors and dtors don't have return type */ + sym->flags |= UNDNAME_NO_FUNCTION_RETURNS; + break; + case PP_CAST_OPERATOR: + sym->flags &= ~UNDNAME_NO_FUNCTION_RETURNS; + break; + } + + /* Function/Data type and access level */ + if (*sym->current >= '0' && *sym->current <= '9') + ret = handle_data(sym); + else if ((*sym->current >= 'A' && *sym->current <= 'Z') || *sym->current == '$') + ret = handle_method(sym, post_process == PP_CAST_OPERATOR); + else ret = FALSE; +done: + if (ret) assert(sym->result); + else WARN("Failed at %s\n", debugstr_a(sym->current)); + + return ret; +} + +/********************************************************************* + * __unDNameEx (MSVCRT.@) + * + * Demangle a C++ identifier. + * + * PARAMS + * buffer [O] If not NULL, the place to put the demangled string + * mangled [I] Mangled name of the function + * buflen [I] Length of buffer + * memget [I] Function to allocate memory with + * memfree [I] Function to free memory with + * unknown [?] Unknown, possibly a call back + * flags [I] Flags determining demangled format + * + * RETURNS + * Success: A string pointing to the unmangled name, allocated with memget. + * Failure: NULL. + */ +char* CDECL __unDNameEx(char* buffer, const char* mangled, int buflen, + malloc_func_t memget, free_func_t memfree, + void* unknown, unsigned short int flags) +{ + struct parsed_symbol sym; + const char* result; + + TRACE("(%p,%s,%d,%p,%p,%p,%x)\n", + buffer, debugstr_a(mangled), buflen, memget, memfree, unknown, flags); + + /* The flags details is not documented by MS. However, it looks exactly + * like the UNDNAME_ manifest constants from imagehlp.h and dbghelp.h + * So, we copied those (on top of the file) + */ + memset(&sym, 0, sizeof(struct parsed_symbol)); + if (flags & UNDNAME_NAME_ONLY) + flags |= UNDNAME_NO_FUNCTION_RETURNS | UNDNAME_NO_ACCESS_SPECIFIERS | + UNDNAME_NO_MEMBER_TYPE | UNDNAME_NO_ALLOCATION_LANGUAGE | + UNDNAME_NO_COMPLEX_TYPE; + + sym.flags = flags; + sym.mem_alloc_ptr = memget; + sym.mem_free_ptr = memfree; + sym.current = mangled; + str_array_init( &sym.names ); + str_array_init( &sym.stack ); + + result = symbol_demangle(&sym) ? sym.result : mangled; + if (buffer && buflen) + { + lstrcpynA( buffer, result, buflen); + } + else + { + buffer = memget(strlen(result) + 1); + if (buffer) strcpy(buffer, result); + } + + und_free_all(&sym); + + return buffer; +} + + +/********************************************************************* + * __unDName (MSVCRT.@) + */ +char* CDECL __unDName(char* buffer, const char* mangled, int buflen, + malloc_func_t memget, free_func_t memfree, + unsigned short int flags) +{ + return __unDNameEx(buffer, mangled, buflen, memget, memfree, NULL, flags); +} diff --git a/dll/win32/msvcrt/wcs.c b/dll/win32/msvcrt/wcs.c new file mode 100644 index 00000000000..1b7bc3e54e0 --- /dev/null +++ b/dll/win32/msvcrt/wcs.c @@ -0,0 +1,3087 @@ +/* + * msvcrt.dll wide-char functions + * + * Copyright 1999 Alexandre Julliard + * Copyright 2000 Jon Griffiths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#define _NO_CRT_STDIO_INLINE +#include +#include +#include +#include +#include +#include +#include "msvcrt.h" +#include "winnls.h" +#include "wtypes.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); + +typedef struct +{ + enum { LEN_DEFAULT, LEN_SHORT, LEN_LONG } IntegerLength; + BOOLEAN IntegerDouble, IntegerNative, LeftAlign, Alternate, PadZero; + BOOLEAN WideString, NaturalString; + int FieldLength, Precision; + char Sign, Format; +} pf_flags; + +static BOOL n_format_enabled = TRUE; + +#include "printf.h" +#define PRINTF_WIDE +#include "printf.h" +#undef PRINTF_WIDE + +#if _MSVCR_VER>=80 + +/********************************************************************* + * _get_printf_count_output (MSVCR80.@) + */ +int CDECL _get_printf_count_output( void ) +{ + return n_format_enabled ? 1 : 0; +} + +/********************************************************************* + * _set_printf_count_output (MSVCR80.@) + */ +int CDECL _set_printf_count_output( int enable ) +{ + BOOL old = n_format_enabled; + n_format_enabled = enable != 0; + return old ? 1 : 0; +} + +#endif /* _MSVCR_VER>=80 */ + +/********************************************************************* + * _wcsdup (MSVCRT.@) + */ +wchar_t* CDECL _wcsdup( const wchar_t* str ) +{ + wchar_t* ret = NULL; + if (str) + { + size_t size = (wcslen(str) + 1) * sizeof(wchar_t); + ret = malloc( size ); + if (ret) memcpy( ret, str, size ); + } + return ret; +} + +/********************************************************************* + * _towlower_l (MSVCRT.@) + */ +wint_t CDECL _towlower_l(wint_t c, _locale_t locale) +{ + pthreadlocinfo locinfo; + wchar_t ret; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(!locinfo->lc_handle[LC_CTYPE]) { + if(c >= 'A' && c <= 'Z') + return c + 'a' - 'A'; + return c; + } + + if(!LCMapStringW(locinfo->lc_handle[LC_CTYPE], LCMAP_LOWERCASE, &c, 1, &ret, 1)) + return c; + return ret; +} + +/********************************************************************* + * towlower (MSVCRT.@) + */ +wint_t CDECL towlower(wint_t c) +{ + return _towlower_l(c, NULL); +} + +INT CDECL _wcsicmp_l(const wchar_t *str1, const wchar_t *str2, _locale_t locale) +{ + _locale_tstruct tmp = {0}; + wchar_t c1, c2; + + if(!MSVCRT_CHECK_PMT(str1 != NULL) || !MSVCRT_CHECK_PMT(str2 != NULL)) + return _NLSCMPERROR; + + if(!locale) + locale = get_current_locale_noalloc(&tmp); + + do + { + c1 = _towlower_l(*str1++, locale); + c2 = _towlower_l(*str2++, locale); + } while(c1 && (c1 == c2)); + + free_locale_noalloc(&tmp); + return c1 - c2; +} + +/********************************************************************* + * towctrans (MSVCR120.@) + */ +wint_t CDECL towctrans(wint_t c, wctrans_t category) +{ + if(category == 1) + return _towupper_l(c, NULL); + return _towlower_l(c, NULL); +} + +/********************************************************************* + * _wcsicmp (MSVCRT.@) + */ +INT CDECL _wcsicmp( const wchar_t* str1, const wchar_t* str2 ) +{ + return _wcsicmp_l(str1, str2, NULL); +} + +/********************************************************************* + * _wcsnicmp_l (MSVCRT.@) + */ +INT CDECL _wcsnicmp_l(const wchar_t *str1, const wchar_t *str2, + size_t n, _locale_t locale) +{ + _locale_tstruct tmp = {0}; + wchar_t c1, c2; + + if (!n) + return 0; + + if(!MSVCRT_CHECK_PMT(str1 != NULL) || !MSVCRT_CHECK_PMT(str2 != NULL)) + return _NLSCMPERROR; + + if(!locale) + locale = get_current_locale_noalloc(&tmp); + + do + { + c1 = _towlower_l(*str1++, locale); + c2 = _towlower_l(*str2++, locale); + } while(--n && c1 && (c1 == c2)); + + free_locale_noalloc(&tmp); + return c1 - c2; +} + +/********************************************************************* + * _wcsnicmp (MSVCRT.@) + */ +INT CDECL _wcsnicmp(const wchar_t *str1, const wchar_t *str2, size_t n) +{ + return _wcsnicmp_l(str1, str2, n, NULL); +} + +/********************************************************************* + * _wcsicoll_l (MSVCRT.@) + */ +int CDECL _wcsicoll_l(const wchar_t* str1, const wchar_t* str2, _locale_t locale) +{ + pthreadlocinfo locinfo; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(!locinfo->lc_handle[LC_COLLATE]) + { + wchar_t c1, c2; + + do + { + c1 = *str1++; + if (c1 >= 'A' && c1 <= 'Z') + c1 += 'a' - 'A'; + + c2 = *str2++; + if (c2 >= 'A' && c2 <= 'Z') + c2 += 'a' - 'A'; + } while(c1 && (c1 == c2)); + return c1 - c2; + } + + return CompareStringW(locinfo->lc_handle[LC_COLLATE], NORM_IGNORECASE, + str1, -1, str2, -1)-CSTR_EQUAL; +} + +/********************************************************************* + * _wcsicoll (MSVCRT.@) + */ +INT CDECL _wcsicoll( const wchar_t* str1, const wchar_t* str2 ) +{ + return _wcsicoll_l(str1, str2, NULL); +} + +/********************************************************************* + * _wcsnicoll_l (MSVCRT.@) + */ +int CDECL _wcsnicoll_l(const wchar_t* str1, const wchar_t* str2, + size_t count, _locale_t locale) +{ + pthreadlocinfo locinfo; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(!locinfo->lc_handle[LC_COLLATE]) + { + wchar_t c1, c2; + + if (!count) + return 0; + + do + { + c1 = *str1++; + if (c1 >= 'A' && c1 <= 'Z') + c1 += 'a' - 'A'; + + c2 = *str2++; + if (c2 >= 'A' && c2 <= 'Z') + c2 += 'a' - 'A'; + } while(--count && c1 && (c1 == c2)); + return c1 - c2; + } + + return CompareStringW(locinfo->lc_handle[LC_COLLATE], NORM_IGNORECASE, + str1, wcsnlen(str1, count), + str2, wcsnlen(str2, count))-CSTR_EQUAL; +} + +/********************************************************************* + * _wcsnicoll (MSVCRT.@) + */ +INT CDECL _wcsnicoll( const wchar_t* str1, const wchar_t* str2, size_t count ) +{ + return _wcsnicoll_l(str1, str2, count, NULL); +} + +/********************************************************************* + * _wcsnset (MSVCRT.@) + */ +wchar_t* CDECL _wcsnset( wchar_t* str, wchar_t c, size_t n ) +{ + wchar_t* ret = str; + while ((n-- > 0) && *str) *str++ = c; + return ret; +} + +/********************************************************************* + * _wcsnset_s (MSVCRT.@) + */ +int CDECL _wcsnset_s( wchar_t *str, size_t size, wchar_t c, size_t count ) +{ + size_t i; + + if(!str && !size && !count) return 0; + if(!MSVCRT_CHECK_PMT(str != NULL)) return EINVAL; + if(!MSVCRT_CHECK_PMT(size > 0)) return EINVAL; + + for(i=0; i str) + { + wchar_t t = *end; + *end-- = *str; + *str++ = t; + } + return ret; +} + +/********************************************************************* + * _wcsset_s (MSVCRT.@) + */ +int CDECL _wcsset_s( wchar_t *str, size_t n, wchar_t c ) +{ + wchar_t *p = str; + + if(!MSVCRT_CHECK_PMT(str != NULL)) return EINVAL; + if(!MSVCRT_CHECK_PMT(n)) return EINVAL; + + while(*p && --n) *p++ = c; + if(!n) { + str[0] = 0; + _invalid_parameter(NULL, NULL, NULL, 0, 0); + *_errno() = EINVAL; + return EINVAL; + } + return 0; +} + +/********************************************************************* + * _wcsset (MSVCRT.@) + */ +wchar_t* CDECL _wcsset( wchar_t* str, wchar_t c ) +{ + wchar_t* ret = str; + while (*str) *str++ = c; + return ret; +} + +/****************************************************************** + * _wcsupr_s_l (MSVCRT.@) + */ +int CDECL _wcsupr_s_l( wchar_t* str, size_t n, _locale_t locale ) +{ + _locale_tstruct tmp = {0}; + wchar_t* ptr = str; + + if (!str || !n) + { + if (str) *str = '\0'; + *_errno() = EINVAL; + return EINVAL; + } + + if(!locale) + locale = get_current_locale_noalloc(&tmp); + + while (n--) + { + if (!*ptr) + { + free_locale_noalloc(&tmp); + return 0; + } + *ptr = _towupper_l(*ptr, locale); + ptr++; + } + + free_locale_noalloc(&tmp); + + /* MSDN claims that the function should return and set errno to + * ERANGE, which doesn't seem to be true based on the tests. */ + *str = '\0'; + *_errno() = EINVAL; + return EINVAL; +} + +/****************************************************************** + * _wcsupr_s (MSVCRT.@) + * + */ +INT CDECL _wcsupr_s( wchar_t* str, size_t n ) +{ + return _wcsupr_s_l( str, n, NULL ); +} + +/****************************************************************** + * _wcsupr_l (MSVCRT.@) + */ +wchar_t* CDECL _wcsupr_l( wchar_t *str, _locale_t locale ) +{ + _wcsupr_s_l( str, -1, locale); + return str; +} + +/****************************************************************** + * _wcsupr (MSVCRT.@) + */ +wchar_t* CDECL _wcsupr( wchar_t *str ) +{ + return _wcsupr_l(str, NULL); +} + +/****************************************************************** + * _wcslwr_s_l (MSVCRT.@) + */ +int CDECL _wcslwr_s_l( wchar_t* str, size_t n, _locale_t locale ) +{ + _locale_tstruct tmp = {0}; + wchar_t* ptr = str; + + if (!str || !n) + { + if (str) *str = '\0'; + *_errno() = EINVAL; + return EINVAL; + } + + if(!locale) + locale = get_current_locale_noalloc(&tmp); + + while (n--) + { + if (!*ptr) + { + free_locale_noalloc(&tmp); + return 0; + } + *ptr = _towlower_l(*ptr, locale); + ptr++; + } + + free_locale_noalloc(&tmp); + + /* MSDN claims that the function should return and set errno to + * ERANGE, which doesn't seem to be true based on the tests. */ + *str = '\0'; + *_errno() = EINVAL; + return EINVAL; +} + +/****************************************************************** + * _wcslwr_s (MSVCRT.@) + */ +int CDECL _wcslwr_s( wchar_t* str, size_t n ) +{ + return _wcslwr_s_l(str, n, NULL); +} + +/****************************************************************** + * _wcslwr_l (MSVCRT.@) + */ +wchar_t* CDECL _wcslwr_l( wchar_t* str, _locale_t locale ) +{ + _wcslwr_s_l(str, -1, locale); + return str; +} + +/****************************************************************** + * _wcslwr (MSVCRT.@) + */ +wchar_t* CDECL _wcslwr( wchar_t* str ) +{ + _wcslwr_s_l(str, -1, NULL); + return str; +} + +/********************************************************************* + * wcscspn (MSVCRT.@) + */ +size_t __cdecl wcscspn(const wchar_t *str, const wchar_t *reject) +{ + const wchar_t *ptr; + for (ptr = str; *ptr; ptr++) if (wcschr( reject, *ptr )) break; + return ptr - str; +} + +/********************************************************************* + * wcsspn (MSVCRT.@) + */ +size_t __cdecl wcsspn(const wchar_t *str, const wchar_t *accept) +{ + const wchar_t *ptr; + for (ptr = str; *ptr; ptr++) if (!wcschr( accept, *ptr )) break; + return ptr - str; +} + +/********************************************************************* + * wcsncmp (MSVCRT.@) + */ +int CDECL wcsncmp(const wchar_t *str1, const wchar_t *str2, size_t n) +{ + if (!n) + return 0; + while(--n && *str1 && (*str1 == *str2)) + { + str1++; + str2++; + } + return *str1 - *str2; +} + +/********************************************************************* + * _wcsncoll_l (MSVCRT.@) + */ +int CDECL _wcsncoll_l(const wchar_t* str1, const wchar_t* str2, + size_t count, _locale_t locale) +{ + pthreadlocinfo locinfo; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(!locinfo->lc_handle[LC_COLLATE]) + return wcsncmp(str1, str2, count); + return CompareStringW(locinfo->lc_handle[LC_COLLATE], SORT_STRINGSORT, + str1, wcsnlen(str1, count), + str2, wcsnlen(str2, count))-CSTR_EQUAL; +} + +/********************************************************************* + * _wcsncoll (MSVCRT.@) + */ +int CDECL _wcsncoll(const wchar_t* str1, const wchar_t* str2, size_t count) +{ + return _wcsncoll_l(str1, str2, count, NULL); +} + +static wchar_t strtod_wstr_get(void *ctx) +{ + const wchar_t **p = ctx; + if (!**p) return WEOF; + return *(*p)++; +} + +static void strtod_wstr_unget(void *ctx) +{ + const wchar_t **p = ctx; + (*p)--; +} + +/********************************************************************* + * _wcstod_l (MSVCRT.@) + */ +double CDECL _wcstod_l(const wchar_t* str, wchar_t** end, + _locale_t locale) +{ + pthreadlocinfo locinfo; + const wchar_t *beg, *p; + struct fpnum fp; + double ret; + int err; + + if (!MSVCRT_CHECK_PMT(str != NULL)) { + if (end) *end = NULL; + return 0; + } + + if (!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + p = str; + while(_iswspace_l(*p, locale)) + p++; + beg = p; + + fp = fpnum_parse(strtod_wstr_get, strtod_wstr_unget, &p, locinfo, FALSE); + if (end) *end = (p == beg ? (wchar_t*)str : (wchar_t*)p); + + err = fpnum_double(&fp, &ret); + if(err) *_errno() = err; + return ret; +} + +/********************************************************************* + * wcsrtombs_l (INTERNAL) + */ +static size_t wcsrtombs_l(char *mbstr, const wchar_t **wcstr, + size_t count, _locale_t locale) +{ + pthreadlocinfo locinfo; + size_t tmp = 0; + BOOL used_default = FALSE; + BOOL *pused_default; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(!locinfo->lc_codepage) { + size_t i; + + if(!mbstr) + return wcslen(*wcstr); + + for(i=0; i 255) { + *_errno() = EILSEQ; + return -1; + } + + mbstr[i] = (*wcstr)[i]; + if(!(*wcstr)[i]) break; + } + + if(i < count) *wcstr = NULL; + else *wcstr += i; + return i; + } + + pused_default = (locinfo->lc_codepage != CP_UTF8 ? &used_default : NULL); + + if(!mbstr) { + tmp = WideCharToMultiByte(locinfo->lc_codepage, WC_NO_BEST_FIT_CHARS, + *wcstr, -1, NULL, 0, NULL, pused_default); + if(!tmp || used_default) { + *_errno() = EILSEQ; + return -1; + } + return tmp-1; + } + + while(**wcstr) { + char buf[3]; + size_t i, size; + + size = WideCharToMultiByte(locinfo->lc_codepage, WC_NO_BEST_FIT_CHARS, + *wcstr, 1, buf, 3, NULL, pused_default); + if(!size || used_default) { + *_errno() = EILSEQ; + return -1; + } + if(tmp+size > count) + return tmp; + + for(i=0; i=120 + +/********************************************************************* + * _wcstof_l (MSVCR120.@) + */ +float CDECL _wcstof_l( const wchar_t *str, wchar_t **end, _locale_t locale ) +{ + double ret = _wcstod_l(str, end, locale); + if (ret && isfinite(ret)) { + float f = ret; + if (!f || !isfinite(f)) + *_errno() = ERANGE; + } + return ret; +} + +/********************************************************************* + * wcstof (MSVCR120.@) + */ +float CDECL wcstof( const wchar_t *str, wchar_t **end ) +{ + return _wcstof_l(str, end, NULL); +} + +#endif /* _MSVCR_VER>=120 */ + +/********************************************************************* + * arg_clbk_valist (INTERNAL) + */ +printf_arg arg_clbk_valist(void *ctx, int arg_pos, int type, va_list *valist) +{ + printf_arg ret; + + if(type == VT_I8) + ret.get_longlong = va_arg(*valist, LONGLONG); + else if(type == VT_INT) + ret.get_int = va_arg(*valist, int); + else if(type == VT_R8) + ret.get_double = va_arg(*valist, double); + else if(type == VT_PTR) + ret.get_ptr = va_arg(*valist, void*); + else { + ERR("Incorrect type\n"); + ret.get_int = 0; + } + + return ret; +} + +/********************************************************************* + * arg_clbk_positional (INTERNAL) + */ +printf_arg arg_clbk_positional(void *ctx, int pos, int type, va_list *valist) +{ + printf_arg *args = ctx; + return args[pos]; +} + +#if _MSVCR_VER < 140 + +/********************************************************************* + * _vsnprintf (MSVCRT.@) + */ +int CDECL _vsnprintf( char *str, size_t len, const char *format, va_list valist ) +{ + static const char nullbyte = '\0'; + struct _str_ctx_a ctx = {len, str}; + int ret; + + ret = pf_printf_a(puts_clbk_str_a, &ctx, format, NULL, 0, + arg_clbk_valist, NULL, &valist); + puts_clbk_str_a(&ctx, 1, &nullbyte); + return ret; +} + +#else + +static int puts_clbk_str_c99_a(void *ctx, int len, const char *str) +{ + struct _str_ctx_a *out = ctx; + + if(!out->buf) + return len; + + if(out->len < len) { + memmove(out->buf, str, out->len); + out->buf += out->len; + out->len = 0; + return len; + } + + memmove(out->buf, str, len); + out->buf += len; + out->len -= len; + return len; +} + +/********************************************************************* + * __stdio_common_vsprintf (UCRTBASE.@) + */ +int CDECL __stdio_common_vsprintf( unsigned __int64 options, char *str, size_t len, const char *format, + _locale_t locale, va_list valist ) +{ + static const char nullbyte = '\0'; + struct _str_ctx_a ctx = {len, str}; + int ret; + + if (options & ~UCRTBASE_PRINTF_MASK) + FIXME("options %#I64x not handled\n", options); + ret = pf_printf_a(puts_clbk_str_c99_a, + &ctx, format, (_locale_t)locale, options & UCRTBASE_PRINTF_MASK, arg_clbk_valist, NULL, &valist); + puts_clbk_str_a(&ctx, 1, &nullbyte); + + if(!str) + return ret; + if(options & _CRT_INTERNAL_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION) + return ret>len ? -1 : ret; + if(ret>=len) { + if(len) str[len-1] = 0; + if(options & _CRT_INTERNAL_PRINTF_STANDARD_SNPRINTF_BEHAVIOR) + return ret; + return len > 0 ? -2 : -1; + } + return ret; +} + +#endif /* _MSVCR_VER>=140 */ + +/********************************************************************* + * _vsnprintf_l (MSVCRT.@) + */ +int CDECL _vsnprintf_l( char *str, size_t len, const char *format, + _locale_t locale, va_list valist ) +{ + static const char nullbyte = '\0'; + struct _str_ctx_a ctx = {len, str}; + int ret; + + ret = pf_printf_a(puts_clbk_str_a, &ctx, format, locale, 0, + arg_clbk_valist, NULL, &valist); + puts_clbk_str_a(&ctx, 1, &nullbyte); + return ret; +} + +/********************************************************************* + * _vsprintf_l (MSVCRT.@) + */ +int CDECL _vsprintf_l( char *str, const char *format, + _locale_t locale, va_list valist ) +{ + return _vsnprintf_l(str, INT_MAX, format, locale, valist); +} + +/********************************************************************* + * _sprintf_l (MSVCRT.@) + */ +int WINAPIV _sprintf_l(char *str, const char *format, + _locale_t locale, ...) +{ + int retval; + va_list valist; + va_start(valist, locale); + retval = _vsnprintf_l(str, INT_MAX, format, locale, valist); + va_end(valist); + return retval; +} + +static int CDECL vsnprintf_s_l_opt( char *str, size_t sizeOfBuffer, + size_t count, const char *format, DWORD options, + _locale_t locale, va_list valist ) +{ + static const char nullbyte = '\0'; + struct _str_ctx_a ctx; + int len, ret; + + if(sizeOfBuffersizeOfBuffer) { + MSVCRT_INVALID_PMT("str[sizeOfBuffer] is too small", ERANGE); + memset(str, 0, sizeOfBuffer); + } else + str[len-1] = '\0'; + + return -1; + } + + return ret; +} + +static int vsnwprintf_s_l_opt( wchar_t *str, size_t sizeOfBuffer, + size_t count, const wchar_t *format, DWORD options, + _locale_t locale, va_list valist) +{ + struct _str_ctx_w ctx; + int len, ret; + + len = sizeOfBuffer; + if(count!=-1 && len>count+1) + len = count+1; + + ctx.len = len; + ctx.buf = str; + ret = pf_printf_w(puts_clbk_str_w, &ctx, format, locale, MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER | options, + arg_clbk_valist, NULL, &valist); + puts_clbk_str_w(&ctx, 1, L""); + + if(ret<0 || ret==len) { + if(count!=_TRUNCATE && count>sizeOfBuffer) { + MSVCRT_INVALID_PMT("str[sizeOfBuffer] is too small", ERANGE); + memset(str, 0, sizeOfBuffer*sizeof(wchar_t)); + } else + str[len-1] = '\0'; + + return -1; + } + + return ret; +} + +/********************************************************************* + * _vsnprintf_s_l (MSVCRT.@) + */ +int CDECL _vsnprintf_s_l( char *str, size_t sizeOfBuffer, + size_t count, const char *format, + _locale_t locale, va_list valist ) +{ + return vsnprintf_s_l_opt(str, sizeOfBuffer, count, format, 0, locale, valist); +} + +/********************************************************************* + * _vsprintf_s_l (MSVCRT.@) + */ +int CDECL _vsprintf_s_l( char *str, size_t count, const char *format, + _locale_t locale, va_list valist ) +{ + return _vsnprintf_s_l(str, INT_MAX, count, format, locale, valist); +} + +/********************************************************************* + * _sprintf_s_l (MSVCRT.@) + */ +int WINAPIV _sprintf_s_l( char *str, size_t count, const char *format, + _locale_t locale, ...) +{ + int retval; + va_list valist; + va_start(valist, locale); + retval = _vsnprintf_s_l(str, INT_MAX, count, format, locale, valist); + va_end(valist); + return retval; +} + +/********************************************************************* + * _vsnprintf_s (MSVCRT.@) + */ +int CDECL _vsnprintf_s( char *str, size_t sizeOfBuffer, + size_t count, const char *format, va_list valist ) +{ + return _vsnprintf_s_l(str,sizeOfBuffer, count, format, NULL, valist); +} + +/********************************************************************* + * _vsnprintf_c_l (MSVCRT.@) + */ +int CDECL _vsnprintf_c_l(char *str, size_t len, const char *format, + _locale_t locale, va_list valist) +{ + return vsnprintf_s_l_opt(str, len, len, format, 0, locale, valist); +} + +/********************************************************************* + * _vsnprintf_c (MSVCRT.@) + */ +int CDECL _vsnprintf_c(char *str, size_t len, + const char *format, va_list valist) +{ + return _vsnprintf_c_l(str, len, format, NULL, valist); +} + +#if _MSVCR_VER>=140 + +/********************************************************************* + * __stdio_common_vsnprintf_s (UCRTBASE.@) + */ +int CDECL __stdio_common_vsnprintf_s( unsigned __int64 options, + char *str, size_t sizeOfBuffer, size_t count, + const char *format, _locale_t locale, va_list valist ) +{ + if (options & ~UCRTBASE_PRINTF_MASK) + FIXME("options %#I64x not handled\n", options); + return vsnprintf_s_l_opt(str, sizeOfBuffer, count, format, options & UCRTBASE_PRINTF_MASK, locale, valist); +} + +/********************************************************************* + * __stdio_common_vsnwprintf_s (UCRTBASE.@) + */ +int CDECL __stdio_common_vsnwprintf_s( unsigned __int64 options, + wchar_t *str, size_t sizeOfBuffer, size_t count, + const wchar_t *format, _locale_t locale, va_list valist ) +{ + if (options & ~UCRTBASE_PRINTF_MASK) + FIXME("options %#I64x not handled\n", options); + return vsnwprintf_s_l_opt(str, sizeOfBuffer, count, format, options & UCRTBASE_PRINTF_MASK, locale, valist); +} + +/********************************************************************* + * __stdio_common_vswprintf_s (UCRTBASE.@) + */ +int CDECL __stdio_common_vswprintf_s( unsigned __int64 options, + wchar_t *str, size_t count, const wchar_t *format, + _locale_t locale, va_list valist ) +{ + return __stdio_common_vsnwprintf_s(options, str, INT_MAX, count, format, locale, valist); +} + +/********************************************************************* + * __stdio_common_vsprintf_s (UCRTBASE.@) + */ +int CDECL __stdio_common_vsprintf_s( unsigned __int64 options, + char *str, size_t count, const char *format, + _locale_t locale, va_list valist ) +{ + if (options & ~UCRTBASE_PRINTF_MASK) + FIXME("options %#I64x not handled\n", options); + return vsnprintf_s_l_opt(str, INT_MAX, count, format, options & UCRTBASE_PRINTF_MASK, locale, valist); +} + +#endif /* _MSVCR_VER>=140 */ + +/********************************************************************* + * vsprintf (MSVCRT.@) + */ +int CDECL vsprintf( char *str, const char *format, va_list valist) +{ + return vsnprintf(str, INT_MAX, format, valist); +} + +/********************************************************************* + * vsprintf_s (MSVCRT.@) + */ +int CDECL vsprintf_s( char *str, size_t num, const char *format, va_list valist) +{ + return vsnprintf(str, num, format, valist); +} + +/********************************************************************* + * _vscprintf (MSVCRT.@) + */ +int CDECL _vscprintf( const char *format, va_list valist ) +{ + return _vsnprintf_l( NULL, INT_MAX, format, NULL, valist ); +} + +/********************************************************************* + * _vscprintf_l (MSVCRT.@) + */ +int CDECL _vscprintf_l(const char *format, + _locale_t locale, va_list valist) +{ + return _vsnprintf_l(NULL, INT_MAX, format, locale, valist); +} + +/********************************************************************* + * _vscprintf_p_l (MSVCRT.@) + */ +int CDECL _vscprintf_p_l(const char *format, + _locale_t locale, va_list args) +{ + printf_arg args_ctx[_ARGMAX+1]; + struct _str_ctx_a puts_ctx = {INT_MAX, NULL}; + int ret; + + memset(args_ctx, 0, sizeof(args_ctx)); + + ret = create_positional_ctx_a(args_ctx, format, args); + if(ret < 0) { + _invalid_parameter(NULL, NULL, NULL, 0, 0); + *_errno() = EINVAL; + return ret; + } else if(ret == 0) { + ret = pf_printf_a(puts_clbk_str_a, &puts_ctx, format, locale, + MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER, + arg_clbk_valist, NULL, &args); + } else { + ret = pf_printf_a(puts_clbk_str_a, &puts_ctx, format, locale, + MSVCRT_PRINTF_POSITIONAL_PARAMS | MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER, + arg_clbk_positional, args_ctx, NULL); + } + + return ret; +} + +/********************************************************************* + * _vscprintf_p (MSVCR80.@) + */ +int CDECL _vscprintf_p(const char *format, va_list argptr) +{ + return _vscprintf_p_l(format, NULL, argptr); +} + +/********************************************************************* + * _snprintf (MSVCRT.@) + */ +int WINAPIV _snprintf(char *str, size_t len, const char *format, ...) +{ + int retval; + va_list valist; + va_start(valist, format); + retval = vsnprintf(str, len, format, valist); + va_end(valist); + return retval; +} + +/********************************************************************* + * _snprintf_l (MSVCRT.@) + */ +int WINAPIV _snprintf_l(char *str, size_t count, const char *format, + _locale_t locale, ...) +{ + int retval; + va_list valist; + va_start(valist, locale); + retval = _vsnprintf_l(str, count, format, locale, valist); + va_end(valist); + return retval; +} + +/********************************************************************* + * _snprintf_c_l (MSVCRT.@) + */ +int WINAPIV _snprintf_c_l(char *str, size_t count, const char *format, + _locale_t locale, ...) +{ + int retval; + va_list valist; + va_start(valist, locale); + retval = _vsnprintf_c_l(str, count, format, locale, valist); + va_end(valist); + return retval; +} + +/********************************************************************* + * _snprintf_c (MSVCRT.@) + */ +int WINAPIV _snprintf_c(char *str, size_t count, const char *format, ...) +{ + int retval; + va_list valist; + va_start(valist, format); + retval = _vsnprintf_c(str, count, format, valist); + va_end(valist); + return retval; +} + +/********************************************************************* + * _snprintf_s_l (MSVCRT.@) + */ +int WINAPIV _snprintf_s_l(char *str, size_t len, size_t count, + const char *format, _locale_t locale, ...) +{ + int retval; + va_list valist; + va_start(valist, locale); + retval = _vsnprintf_s_l(str, len, count, format, locale, valist); + va_end(valist); + return retval; +} + +/********************************************************************* + * _snprintf_s (MSVCRT.@) + */ +int WINAPIV _snprintf_s(char *str, size_t len, size_t count, + const char *format, ...) +{ + int retval; + va_list valist; + va_start(valist, format); + retval = _vsnprintf_s_l(str, len, count, format, NULL, valist); + va_end(valist); + return retval; +} + +/********************************************************************* + * _scprintf (MSVCRT.@) + */ +int WINAPIV _scprintf(const char *format, ...) +{ + int retval; + va_list valist; + va_start(valist, format); + retval = _vscprintf(format, valist); + va_end(valist); + return retval; +} + +/********************************************************************* + * _scprintf_l (MSVCRT.@) + */ +int WINAPIV _scprintf_l(const char *format, _locale_t locale, ...) +{ + int retval; + va_list valist; + va_start(valist, locale); + retval = _vscprintf_l(format, locale, valist); + va_end(valist); + return retval; +} + +/********************************************************************* + * _scprintf_p (MSVCRT.@) + */ +int WINAPIV _scprintf_p(const char *format, ...) +{ + int retval; + va_list valist; + va_start(valist, format); + retval = _vscprintf_p_l(format, NULL, valist); + va_end(valist); + return retval; +} + +/********************************************************************* + * _scprintf_p_l (MSVCRT.@) + */ +int WINAPIV _scprintf_p_l(const char *format, _locale_t locale, ...) +{ + int retval; + va_list valist; + va_start(valist, locale); + retval = _vscprintf_p_l(format, locale, valist); + va_end(valist); + return retval; +} + +/********************************************************************* + * _vsnwprintf (MSVCRT.@) + */ +int CDECL _vsnwprintf(wchar_t *str, size_t len, + const wchar_t *format, va_list valist) +{ + struct _str_ctx_w ctx = {len, str}; + int ret; + + ret = pf_printf_w(puts_clbk_str_w, &ctx, format, NULL, 0, + arg_clbk_valist, NULL, &valist); + puts_clbk_str_w(&ctx, 1, L""); + return ret; +} + +/********************************************************************* + * _vsnwprintf_l (MSVCRT.@) + */ +int CDECL _vsnwprintf_l(wchar_t *str, size_t len, const wchar_t *format, + _locale_t locale, va_list valist) +{ + struct _str_ctx_w ctx = {len, str}; + int ret; + + ret = pf_printf_w(puts_clbk_str_w, &ctx, format, locale, 0, + arg_clbk_valist, NULL, &valist); + puts_clbk_str_w(&ctx, 1, L""); + return ret; +} + +/********************************************************************* + * _vswprintf_c_l (MSVCRT.@) + */ +int CDECL _vswprintf_c_l(wchar_t *str, size_t len, const wchar_t *format, + _locale_t locale, va_list valist) +{ + return vsnwprintf_s_l_opt(str, len, len, format, 0, locale, valist); +} + +/********************************************************************* + * _vswprintf_c (MSVCRT.@) + */ +int CDECL _vswprintf_c(wchar_t *str, size_t len, + const wchar_t *format, va_list valist) +{ + return _vswprintf_c_l(str, len, format, NULL, valist); +} + +static int vswprintf_p_l_opt(wchar_t *buffer, size_t length, + const wchar_t *format, DWORD options, _locale_t locale, va_list args) +{ + printf_arg args_ctx[_ARGMAX+1]; + struct _str_ctx_w puts_ctx = {length, buffer}; + int ret; + + memset(args_ctx, 0, sizeof(args_ctx)); + + ret = create_positional_ctx_w(args_ctx, format, args); + if(ret < 0) { + _invalid_parameter(NULL, NULL, NULL, 0, 0); + *_errno() = EINVAL; + return ret; + } else if(ret == 0) + ret = pf_printf_w(puts_clbk_str_w, &puts_ctx, format, locale, MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER | options, + arg_clbk_valist, NULL, &args); + else + ret = pf_printf_w(puts_clbk_str_w, &puts_ctx, format, locale, + MSVCRT_PRINTF_POSITIONAL_PARAMS | MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER | options, + arg_clbk_positional, args_ctx, NULL); + + puts_clbk_str_w(&puts_ctx, 1, L""); + return ret; +} + +/********************************************************************* + * _vswprintf_p_l (MSVCRT.@) + */ +int CDECL _vswprintf_p_l(wchar_t *buffer, size_t length, + const wchar_t *format, _locale_t locale, va_list args) +{ + return vswprintf_p_l_opt(buffer, length, format, 0, locale, args); +} + +#if _MSVCR_VER>=80 +/********************************************************************* + * _vswprintf_p (MSVCR80.@) + */ +int CDECL _vswprintf_p(wchar_t *buffer, size_t length, + const wchar_t *format, va_list args) +{ + return vswprintf_p_l_opt(buffer, length, format, 0, NULL, args); +} +#endif + +#if _MSVCR_VER>=140 +/********************************************************************* + * __stdio_common_vswprintf_p (UCRTBASE.@) + */ +int CDECL __stdio_common_vswprintf_p( unsigned __int64 options, + wchar_t *str, size_t count, const wchar_t *format, + _locale_t locale, va_list valist ) +{ + if (options & ~UCRTBASE_PRINTF_MASK) + FIXME("options %#I64x not handled\n", options); + return vswprintf_p_l_opt(str, count, format, options & UCRTBASE_PRINTF_MASK, locale, valist); +} +#endif + +/********************************************************************* + * _vsnwprintf_s_l (MSVCRT.@) + */ +int CDECL _vsnwprintf_s_l( wchar_t *str, size_t sizeOfBuffer, + size_t count, const wchar_t *format, + _locale_t locale, va_list valist) +{ + return vsnwprintf_s_l_opt(str, sizeOfBuffer, count, format, 0, locale, valist); +} + +/********************************************************************* + * _vsnwprintf_s (MSVCRT.@) + */ +int CDECL _vsnwprintf_s(wchar_t *str, size_t sizeOfBuffer, + size_t count, const wchar_t *format, va_list valist) +{ + return _vsnwprintf_s_l(str, sizeOfBuffer, count, + format, NULL, valist); +} + +/********************************************************************* + * _snwprintf (MSVCRT.@) + */ +int WINAPIV _snwprintf( wchar_t *str, size_t len, const wchar_t *format, ...) +{ + int retval; + va_list valist; + va_start(valist, format); + retval = _vsnwprintf(str, len, format, valist); + va_end(valist); + return retval; +} + +/********************************************************************* + * _snwprintf_l (MSVCRT.@) + */ +int WINAPIV _snwprintf_l( wchar_t *str, size_t len, const wchar_t *format, + _locale_t locale, ...) +{ + int retval; + va_list valist; + va_start(valist, locale); + retval = _vsnwprintf_l(str, len, format, locale, valist); + va_end(valist); + return retval; +} + +/********************************************************************* + * _snwprintf_s (MSVCRT.@) + */ +int WINAPIV _snwprintf_s( wchar_t *str, size_t len, size_t count, + const wchar_t *format, ...) +{ + int retval; + va_list valist; + va_start(valist, format); + retval = _vsnwprintf_s_l(str, len, count, format, NULL, valist); + va_end(valist); + return retval; +} + +/********************************************************************* + * _snwprintf_s_l (MSVCRT.@) + */ +int WINAPIV _snwprintf_s_l( wchar_t *str, size_t len, size_t count, + const wchar_t *format, _locale_t locale, ... ) +{ + int retval; + va_list valist; + va_start(valist, locale); + retval = _vsnwprintf_s_l(str, len, count, format, locale, valist); + va_end(valist); + return retval; +} + +#if _MSVCR_VER>=140 + +static int puts_clbk_str_c99_w(void *ctx, int len, const wchar_t *str) +{ + struct _str_ctx_w *out = ctx; + + if(!out->buf) + return len; + + if(out->len < len) { + memcpy(out->buf, str, out->len*sizeof(wchar_t)); + out->buf += out->len; + out->len = 0; + return len; + } + + memcpy(out->buf, str, len*sizeof(wchar_t)); + out->buf += len; + out->len -= len; + return len; +} + +/********************************************************************* + * __stdio_common_vswprintf (UCRTBASE.@) + */ +int CDECL __stdio_common_vswprintf( unsigned __int64 options, + wchar_t *str, size_t len, const wchar_t *format, + _locale_t locale, va_list valist ) +{ + struct _str_ctx_w ctx = {len, str}; + int ret; + + if (options & ~UCRTBASE_PRINTF_MASK) + FIXME("options %#I64x not handled\n", options); + ret = pf_printf_w(puts_clbk_str_c99_w, + &ctx, format, locale, options & UCRTBASE_PRINTF_MASK, arg_clbk_valist, NULL, &valist); + puts_clbk_str_w(&ctx, 1, L""); + + if(!str) + return ret; + if(options & _CRT_INTERNAL_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION) + return ret>len ? -1 : ret; + if(ret>=len) { + if(len) str[len-1] = 0; + if(options & _CRT_INTERNAL_PRINTF_STANDARD_SNPRINTF_BEHAVIOR) + return ret; + return len > 0 ? -2 : -1; + } + return ret; +} + +#endif /* _MSVCR_VER>=140 */ + +/********************************************************************* + * sprintf (MSVCRT.@) + */ +int WINAPIV sprintf( char *str, const char *format, ... ) +{ + va_list ap; + int r; + + va_start( ap, format ); + r = vsnprintf( str, INT_MAX, format, ap ); + va_end( ap ); + return r; +} + +/********************************************************************* + * sprintf_s (MSVCRT.@) + */ +int WINAPIV sprintf_s( char *str, size_t num, const char *format, ... ) +{ + va_list ap; + int r; + + va_start( ap, format ); + r = vsnprintf( str, num, format, ap ); + va_end( ap ); + return r; +} + +/********************************************************************* + * _scwprintf_l (MSVCRT.@) + */ +int WINAPIV _scwprintf_l( const wchar_t *format, _locale_t locale, ... ) +{ + va_list ap; + int r; + + va_start( ap, locale ); + r = _vsnwprintf_l( NULL, INT_MAX, format, locale, ap ); + va_end( ap ); + return r; +} + +/********************************************************************* + * _scwprintf_p_l (MSVCRT.@) + */ +int WINAPIV _scwprintf_p_l( const wchar_t *format, _locale_t locale, ... ) +{ + va_list ap; + int r; + + va_start( ap, locale ); + r = vswprintf_p_l_opt( NULL, INT_MAX, format, 0, locale, ap ); + va_end( ap ); + return r; +} + +#if _MSVCR_VER>=80 +/********************************************************************* + * _scwprintf_p (MSVCRT.@) + */ +int WINAPIV _scwprintf_p( const wchar_t *format, ... ) +{ + va_list ap; + int r; + + va_start( ap, format ); + r = vswprintf_p_l_opt( NULL, INT_MAX, format, 0, NULL, ap ); + va_end( ap ); + return r; +} +#endif + +/********************************************************************* + * _scwprintf (MSVCRT.@) + */ +int WINAPIV _scwprintf( const wchar_t *format, ... ) +{ + va_list ap; + int r; + + va_start( ap, format ); + r = _vsnwprintf( NULL, INT_MAX, format, ap ); + va_end( ap ); + return r; +} + +/********************************************************************* + * swprintf (MSVCRT.@) + */ +int WINAPIV _swprintf( wchar_t *str, const wchar_t *format, ... ) +{ + va_list ap; + int r; + + va_start( ap, format ); + r = _vsnwprintf( str, INT_MAX, format, ap ); + va_end( ap ); + return r; +} + +/********************************************************************* + * swprintf_s (MSVCRT.@) + */ +int WINAPIV swprintf_s(wchar_t *str, size_t numberOfElements, + const wchar_t *format, ... ) +{ + va_list ap; + int r; + + va_start(ap, format); + r = _vsnwprintf_s(str, numberOfElements, INT_MAX, format, ap); + va_end(ap); + + return r; +} + +/********************************************************************* + * _swprintf_s_l (MSVCRT.@) + */ +int WINAPIV _swprintf_s_l(wchar_t *str, size_t numberOfElements, + const wchar_t *format, _locale_t locale, ... ) +{ + va_list ap; + int r; + + va_start(ap, locale); + r = _vsnwprintf_s_l(str, numberOfElements, INT_MAX, format, locale, ap); + va_end(ap); + + return r; +} + +/********************************************************************* + * _swprintf_c_l (MSVCRT.@) + */ +int WINAPIV _swprintf_c_l(wchar_t *str, size_t len, + const wchar_t *format, _locale_t locale, ... ) +{ + va_list ap; + int r; + + va_start(ap, locale); + r = _vswprintf_c_l(str, len, format, locale, ap); + va_end(ap); + + return r; +} + +/********************************************************************* + * _swprintf_c (MSVCRT.@) + */ +int WINAPIV _swprintf_c(wchar_t *str, size_t len, + const wchar_t *format, ... ) +{ + va_list ap; + int r; + + va_start(ap, format); + r = _vswprintf_c(str, len, format, ap); + va_end(ap); + + return r; +} + +/********************************************************************* + * _vswprintf (MSVCRT.@) + */ +int CDECL _vswprintf( wchar_t* str, const wchar_t* format, va_list args ) +{ + return _vsnwprintf( str, INT_MAX, format, args ); +} + +/********************************************************************* + * _vswprintf (MSVCRT.@) + */ +int CDECL _vswprintf_l( wchar_t* str, const wchar_t* format, + _locale_t locale, va_list args ) +{ + return _vsnwprintf_l( str, INT_MAX, format, locale, args ); +} + +/********************************************************************* + * _vscwprintf (MSVCRT.@) + */ +int CDECL _vscwprintf( const wchar_t *format, va_list args ) +{ + return _vsnwprintf( NULL, INT_MAX, format, args ); +} + +/********************************************************************* + * _vscwprintf_l (MSVCRT.@) + */ +int CDECL _vscwprintf_l( const wchar_t *format, _locale_t locale, va_list args ) +{ + return _vsnwprintf_l( NULL, INT_MAX, format, locale, args ); +} + +/********************************************************************* + * _vscwprintf_p_l (MSVCRT.@) + */ +int CDECL _vscwprintf_p_l( const wchar_t *format, _locale_t locale, va_list args ) +{ + return vswprintf_p_l_opt( NULL, INT_MAX, format, 0, locale, args ); +} + +#if _MSVCR_VER>=80 +/********************************************************************* + * _vscwprintf_p (MSVCR80.@) + */ +int CDECL _vscwprintf_p(const wchar_t *format, va_list args) +{ + return vswprintf_p_l_opt(NULL, INT_MAX, format, 0, NULL, args); +} +#endif + +/********************************************************************* + * vswprintf_s (MSVCRT.@) + */ +int CDECL vswprintf_s(wchar_t* str, size_t numberOfElements, + const wchar_t* format, va_list args) +{ + return _vsnwprintf_s(str, numberOfElements, INT_MAX, format, args ); +} + +/********************************************************************* + * _vswprintf_s_l (MSVCRT.@) + */ +int CDECL _vswprintf_s_l(wchar_t* str, size_t numberOfElements, + const wchar_t* format, _locale_t locale, va_list args) +{ + return _vsnwprintf_s_l(str, numberOfElements, INT_MAX, + format, locale, args ); +} + +static int vsprintf_p_l_opt(char *buffer, size_t length, const char *format, + DWORD options, _locale_t locale, va_list args) +{ + static const char nullbyte = '\0'; + printf_arg args_ctx[_ARGMAX+1]; + struct _str_ctx_a puts_ctx = {length, buffer}; + int ret; + + memset(args_ctx, 0, sizeof(args_ctx)); + + ret = create_positional_ctx_a(args_ctx, format, args); + if(ret < 0) { + _invalid_parameter(NULL, NULL, NULL, 0, 0); + *_errno() = EINVAL; + return ret; + } else if(ret == 0) + ret = pf_printf_a(puts_clbk_str_a, &puts_ctx, format, locale, + MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER | options, arg_clbk_valist, NULL, &args); + else + ret = pf_printf_a(puts_clbk_str_a, &puts_ctx, format, locale, + MSVCRT_PRINTF_POSITIONAL_PARAMS | MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER | options, + arg_clbk_positional, args_ctx, NULL); + + puts_clbk_str_a(&puts_ctx, 1, &nullbyte); + return ret; +} + +/********************************************************************* + * _vsprintf_p_l (MSVCRT.@) + */ +int CDECL _vsprintf_p_l(char *buffer, size_t length, const char *format, + _locale_t locale, va_list args) +{ + return vsprintf_p_l_opt(buffer, length, format, 0, locale, args); +} + +/********************************************************************* + * _vsprintf_p (MSVCRT.@) + */ +int CDECL _vsprintf_p(char *buffer, size_t length, + const char *format, va_list args) +{ + return _vsprintf_p_l(buffer, length, format, NULL, args); +} + +#if _MSVCR_VER>=140 +/********************************************************************* + * __stdio_common_vsprintf_p (UCRTBASE.@) + */ +int CDECL __stdio_common_vsprintf_p(unsigned __int64 options, char *buffer, size_t length, + const char *format, _locale_t locale, va_list args) +{ + if (options & ~UCRTBASE_PRINTF_MASK) + FIXME("options %#I64x not handled\n", options); + return vsprintf_p_l_opt(buffer, length, format, options & UCRTBASE_PRINTF_MASK, locale, args); +} +#endif + +/********************************************************************* + * _sprintf_p_l (MSVCRT.@) + */ +int WINAPIV _sprintf_p_l(char *buffer, size_t length, + const char *format, _locale_t locale, ...) +{ + va_list valist; + int r; + + va_start(valist, locale); + r = _vsprintf_p_l(buffer, length, format, locale, valist); + va_end(valist); + + return r; +} + +/********************************************************************* + * __swprintf_l (MSVCRT.@) + */ +int WINAPIV __swprintf_l( wchar_t *str, const wchar_t *format, + _locale_t locale, ...) +{ + int retval; + va_list valist; + va_start(valist, locale); + retval = _vswprintf_l(str, format, locale, valist); + va_end(valist); + return retval; +} + +#if _MSVCR_VER>=80 +/********************************************************************* + * _sprintf_p (MSVCR80.@) + */ +int WINAPIV _sprintf_p(char *buffer, size_t length, const char *format, ...) +{ + va_list valist; + int r; + + va_start(valist, format); + r = _vsprintf_p_l(buffer, length, format, NULL, valist); + va_end(valist); + + return r; +} +#endif + +/********************************************************************* + * _swprintf_p (MSVCRT.@) + */ +int WINAPIV _swprintf_p(wchar_t *buffer, size_t length, + const wchar_t *format, ...) +{ + va_list valist; + int r; + + va_start(valist, format); + r = vswprintf_p_l_opt(buffer, length, format, 0, NULL, valist); + va_end(valist); + + return r; +} + +/********************************************************************* + * _swprintf_p_l (MSVCRT.@) + */ +int WINAPIV _swprintf_p_l(wchar_t *buffer, size_t length, + const wchar_t *format, _locale_t locale, ...) +{ + va_list valist; + int r; + + va_start(valist, locale); + r = vswprintf_p_l_opt(buffer, length, format, 0, locale, valist); + va_end(valist); + + return r; +} + +/********************************************************************* + * wcscmp (MSVCRT.@) + */ +int CDECL wcscmp(const wchar_t *str1, const wchar_t *str2) +{ + while (*str1 && (*str1 == *str2)) + { + str1++; + str2++; + } + + if (*str1 < *str2) + return -1; + if (*str1 > *str2) + return 1; + return 0; +} + +/********************************************************************* + * _wcscoll_l (MSVCRT.@) + */ +int CDECL _wcscoll_l(const wchar_t* str1, const wchar_t* str2, _locale_t locale) +{ + pthreadlocinfo locinfo; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(!locinfo->lc_handle[LC_COLLATE]) + return wcscmp(str1, str2); + return CompareStringW(locinfo->lc_handle[LC_COLLATE], SORT_STRINGSORT, + str1, -1, str2, -1)-CSTR_EQUAL; +} + +/********************************************************************* + * wcscoll (MSVCRT.@) + */ +int CDECL wcscoll( const wchar_t* str1, const wchar_t* str2 ) +{ + return _wcscoll_l(str1, str2, NULL); +} + +/********************************************************************* + * wcspbrk (MSVCRT.@) + */ +wchar_t* CDECL wcspbrk( const wchar_t* str, const wchar_t* accept ) +{ + const wchar_t* p; + + while (*str) + { + for (p = accept; *p; p++) if (*p == *str) return (wchar_t*)str; + str++; + } + return NULL; +} + +/********************************************************************* + * wcstok_s (MSVCRT.@) + */ +wchar_t * CDECL wcstok_s( wchar_t *str, const wchar_t *delim, + wchar_t **next_token ) +{ + wchar_t *ret; + + if (!MSVCRT_CHECK_PMT(delim != NULL)) return NULL; + if (!MSVCRT_CHECK_PMT(next_token != NULL)) return NULL; + if (!MSVCRT_CHECK_PMT(str != NULL || *next_token != NULL)) return NULL; + + if (!str) str = *next_token; + + while (*str && wcschr( delim, *str )) str++; + if (!*str) ret = NULL; + else + { + ret = str++; + while (*str && !wcschr( delim, *str )) str++; + if (*str) *str++ = 0; + } + *next_token = str; + return ret; +} + +/********************************************************************* + * wcstok (MSVCRT.@) + */ +#if _MSVCR_VER>=140 +wchar_t * CDECL wcstok( wchar_t *str, const wchar_t *delim, wchar_t **ctx ) +{ + if (!ctx) + ctx = &msvcrt_get_thread_data()->wcstok_next; + return wcstok_s(str, delim, ctx); +} +#else +wchar_t * CDECL wcstok( wchar_t *str, const wchar_t *delim ) +{ + return wcstok_s(str, delim, &msvcrt_get_thread_data()->wcstok_next); +} +#endif + +/********************************************************************* + * _wctomb_s_l (MSVCRT.@) + */ +int CDECL _wctomb_s_l(int *len, char *mbchar, size_t size, + wchar_t wch, _locale_t locale) +{ + pthreadlocinfo locinfo; + BOOL error = FALSE; + BOOL *perror; + int mblen; + + if(!mbchar && size>0) { + if(len) + *len = 0; + return 0; + } + + if(len) + *len = -1; + + if(!MSVCRT_CHECK_PMT(size <= INT_MAX)) + return EINVAL; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(!locinfo->lc_codepage) { + if(wch > 0xff) { + if(mbchar && size>0) + memset(mbchar, 0, size); + *_errno() = EILSEQ; + return EILSEQ; + } + + if(!MSVCRT_CHECK_PMT_ERR(size >= 1, ERANGE)) + return ERANGE; + + *mbchar = wch; + if(len) + *len = 1; + return 0; + } + + perror = (locinfo->lc_codepage != CP_UTF8 ? &error : NULL); + mblen = WideCharToMultiByte(locinfo->lc_codepage, 0, &wch, 1, mbchar, size, NULL, perror); + if(!mblen || error) { + if(!mblen && GetLastError()==ERROR_INSUFFICIENT_BUFFER) { + if(mbchar && size>0) + memset(mbchar, 0, size); + + MSVCRT_INVALID_PMT("insufficient buffer size", ERANGE); + return ERANGE; + } + + *_errno() = EILSEQ; + return EILSEQ; + } + + if(len) + *len = mblen; + return 0; +} + +/********************************************************************* + * wctomb_s (MSVCRT.@) + */ +int CDECL wctomb_s(int *len, char *mbchar, size_t size, wchar_t wch) +{ + return _wctomb_s_l(len, mbchar, size, wch, NULL); +} + +/********************************************************************* + * _wctomb_l (MSVCRT.@) + */ +int CDECL _wctomb_l(char *dst, wchar_t ch, _locale_t locale) +{ + int len; + + _wctomb_s_l(&len, dst, dst ? MB_LEN_MAX : 0, ch, locale); + return len; +} + +/********************************************************************* + * wctomb (MSVCRT.@) + */ +INT CDECL wctomb( char *dst, wchar_t ch ) +{ + return _wctomb_l(dst, ch, NULL); +} + +/********************************************************************* + * wctob (MSVCRT.@) + */ +INT CDECL wctob( wint_t wchar ) +{ + char out; + BOOL error = FALSE; + BOOL *perror; + UINT codepage = get_locinfo()->lc_codepage; + + perror = (codepage != CP_UTF8 ? &error : NULL); + + if(!codepage) { + if (wchar < 0xff) + return (signed char)wchar; + else + return EOF; + } else if(WideCharToMultiByte( codepage, 0, &wchar, 1, &out, 1, NULL, perror ) && !error) + return (INT)out; + return EOF; +} + +/********************************************************************* + * wcrtomb_s (MSVCRT.@) + */ +INT CDECL wcrtomb_s(size_t *len, char *mbchar, + size_t size, wchar_t wch, mbstate_t *s) +{ + int ilen, ret; + + if (s) *s = 0; + ret = wctomb_s(&ilen, mbchar, size, wch); + if (len) *len = ilen; + return ret; +} + +/********************************************************************* + * wcrtomb (MSVCRT.@) + */ +size_t CDECL wcrtomb( char *dst, wchar_t ch, mbstate_t *s) +{ + if(s) + *s = 0; + return wctomb(dst, ch); +} + +/********************************************************************* + * _iswctype_l (MSVCRT.@) + */ +INT CDECL _iswctype_l( wchar_t wc, wctype_t type, _locale_t locale ) +{ + WORD ct; + + if (wc == WEOF) return 0; + if (wc < 256) return MSVCRT__pwctype[wc] & type; + + if (!GetStringTypeW(CT_CTYPE1, &wc, 1, &ct)) + { + ERR("GetStringTypeW failed for %x\n", wc); + return 0; + } + return ct & type; +} + +/********************************************************************* + * iswctype (MSVCRT.@) + */ +INT CDECL iswctype( wchar_t wc, wctype_t type ) +{ + return _iswctype_l( wc, type, NULL ); +} + +/********************************************************************* + * _iswalnum_l (MSVCRT.@) + */ +int CDECL _iswalnum_l( wchar_t wc, _locale_t locale ) +{ + return _iswctype_l( wc, _ALPHA | _DIGIT, locale ); +} + +/********************************************************************* + * iswalnum (MSVCRT.@) + */ +INT CDECL iswalnum( wchar_t wc ) +{ + return _iswalnum_l( wc, NULL ); +} + +/********************************************************************* + * iswalpha_l (MSVCRT.@) + */ +INT CDECL _iswalpha_l( wchar_t wc, _locale_t locale ) +{ + return _iswctype_l( wc, _ALPHA, locale ); +} + +/********************************************************************* + * iswalpha (MSVCRT.@) + */ +INT CDECL iswalpha( wchar_t wc ) +{ + return _iswalpha_l( wc, NULL ); +} + +/********************************************************************* + * _iswcntrl_l (MSVCRT.@) + */ +int CDECL _iswcntrl_l( wchar_t wc, _locale_t locale ) +{ + return _iswctype_l( wc, _CONTROL, locale ); +} + +/********************************************************************* + * iswcntrl (MSVCRT.@) + */ +INT CDECL iswcntrl( wchar_t wc ) +{ + return _iswcntrl_l( wc, NULL ); +} + +/********************************************************************* + * _iswdigit_l (MSVCRT.@) + */ +INT CDECL _iswdigit_l( wchar_t wc, _locale_t locale ) +{ + return _iswctype_l( wc, _DIGIT, locale ); +} + +/********************************************************************* + * iswdigit (MSVCRT.@) + */ +INT CDECL iswdigit( wchar_t wc ) +{ + return _iswdigit_l( wc, NULL ); +} + +/********************************************************************* + * _iswgraph_l (MSVCRT.@) + */ +int CDECL _iswgraph_l( wchar_t wc, _locale_t locale ) +{ + return _iswctype_l( wc, _ALPHA | _DIGIT | _PUNCT, locale ); +} + +/********************************************************************* + * iswgraph (MSVCRT.@) + */ +INT CDECL iswgraph( wchar_t wc ) +{ + return _iswgraph_l( wc, NULL ); +} + +/********************************************************************* + * _iswlower_l (MSVCRT.@) + */ +int CDECL _iswlower_l( wchar_t wc, _locale_t locale ) +{ + return _iswctype_l( wc, _LOWER, locale ); +} + +/********************************************************************* + * iswlower (MSVCRT.@) + */ +INT CDECL iswlower( wchar_t wc ) +{ + return _iswlower_l( wc, NULL ); +} + +/********************************************************************* + * _iswprint_l (MSVCRT.@) + */ +int CDECL _iswprint_l( wchar_t wc, _locale_t locale ) +{ + return _iswctype_l( wc, _ALPHA | _BLANK | _DIGIT | _PUNCT, locale ); +} + +/********************************************************************* + * iswprint (MSVCRT.@) + */ +INT CDECL iswprint( wchar_t wc ) +{ + return _iswprint_l( wc, NULL ); +} + +/********************************************************************* + * _iswpunct_l (MSVCRT.@) + */ +INT CDECL _iswpunct_l( wchar_t wc, _locale_t locale ) +{ + return _iswctype_l( wc, _PUNCT, locale ); +} + +/********************************************************************* + * iswpunct (MSVCRT.@) + */ +INT CDECL iswpunct( wchar_t wc ) +{ + return _iswpunct_l( wc, NULL ); +} + +/********************************************************************* + * _iswspace_l (MSVCRT.@) + */ +INT CDECL _iswspace_l( wchar_t wc, _locale_t locale ) +{ + return _iswctype_l( wc, _SPACE, locale ); +} + +/********************************************************************* + * iswspace (MSVCRT.@) + */ +INT CDECL iswspace( wchar_t wc ) +{ + return _iswspace_l( wc, NULL ); +} + +/********************************************************************* + * _iswupper_l (MSVCRT.@) + */ +int CDECL _iswupper_l( wchar_t wc, _locale_t locale ) +{ + return _iswctype_l( wc, _UPPER, locale ); +} + +/********************************************************************* + * iswupper (MSVCRT.@) + */ +INT CDECL iswupper( wchar_t wc ) +{ + return _iswupper_l( wc, NULL ); +} + +/********************************************************************* + * _iswxdigit_l (MSVCRT.@) + */ +int CDECL _iswxdigit_l( wchar_t wc, _locale_t locale ) +{ + return _iswctype_l( wc, _HEX, locale ); +} + +/********************************************************************* + * iswxdigit (MSVCRT.@) + */ +INT CDECL iswxdigit( wchar_t wc ) +{ + return _iswxdigit_l( wc, NULL ); +} + +/********************************************************************* + * _iswblank_l (MSVCRT.@) + */ +INT CDECL _iswblank_l( wchar_t wc, _locale_t locale ) +{ + return wc == '\t' || _iswctype_l( wc, _BLANK, locale ); +} + +/********************************************************************* + * iswblank (MSVCRT.@) + */ +INT CDECL iswblank( wchar_t wc ) +{ + return wc == '\t' || _iswctype_l( wc, _BLANK, NULL ); +} + +/********************************************************************* + * wcscpy_s (MSVCRT.@) + */ +INT CDECL wcscpy_s( wchar_t* wcDest, size_t numElement, const wchar_t *wcSrc) +{ + size_t size = 0; + + if(!MSVCRT_CHECK_PMT(wcDest)) return EINVAL; + if(!MSVCRT_CHECK_PMT(numElement)) return EINVAL; + + if(!MSVCRT_CHECK_PMT(wcSrc)) + { + wcDest[0] = 0; + return EINVAL; + } + + size = wcslen(wcSrc) + 1; + + if(!MSVCRT_CHECK_PMT_ERR(size <= numElement, ERANGE)) + { + wcDest[0] = 0; + return ERANGE; + } + + memmove( wcDest, wcSrc, size*sizeof(WCHAR) ); + + return 0; +} + +/*********************************************************************** + * wcscpy (MSVCRT.@) + */ +wchar_t* __cdecl wcscpy( wchar_t *dst, const wchar_t *src ) +{ + WCHAR *p = dst; + while ((*p++ = *src++)); + return dst; +} + +/****************************************************************** + * wcsncpy (MSVCRT.@) + */ +wchar_t* __cdecl wcsncpy( wchar_t* s1, const wchar_t *s2, size_t n ) +{ + size_t i; + + for(i=0; i 0)) return EINVAL; + if (count == 0) return 0; + if (!MSVCRT_CHECK_PMT(src != NULL)) + { + *dst = 0; + return EINVAL; + } + + for (i = 0; i < elem; i++) if (!dst[i]) break; + + if (i == elem) + { + MSVCRT_INVALID_PMT("dst[elem] is not NULL terminated\n", EINVAL); + *dst = 0; + return EINVAL; + } + + for (j = 0; (j + i) < elem; j++) + { + if(count == _TRUNCATE && j + i == elem - 1) + { + dst[j + i] = '\0'; + return STRUNCATE; + } + if(j == count || (dst[j + i] = src[j]) == '\0') + { + dst[j + i] = '\0'; + return 0; + } + } + + MSVCRT_INVALID_PMT("dst[elem] is too small", ERANGE); + dst[0] = '\0'; + return ERANGE; +} + +/********************************************************************* + * wcsncat (NTDLL.@) + */ +wchar_t * __cdecl wcsncat(wchar_t *s1, const wchar_t *s2, size_t n) +{ + wchar_t *ret = s1; + while (*s1) s1++; + while (n-- > 0) if (!(*s1++ = *s2++)) return ret; + *s1 = 0; + return ret; +} + +/********************************************************************* + * wctoint (INTERNAL) + */ +static int wctoint(WCHAR c, int base) +{ + int v = -1; + if ('0' <= c && c <= '9') + v = c - '0'; + else if ('A' <= c && c <= 'Z') + v = c - 'A' + 10; + else if ('a' <= c && c <= 'z') + v = c - 'a' + 10; + else { + /* NOTE: MAP_FOLDDIGITS supports too many things. */ + /* Unicode points that contain digits 0-9; keep this sorted! */ + static const WCHAR zeros[] = { + 0x660, 0x6f0, 0x966, 0x9e6, 0xa66, 0xae6, 0xb66, 0xc66, 0xce6, + 0xd66, 0xe50, 0xed0, 0xf20, 0x1040, 0x17e0, 0x1810, 0xff10 + }; + int i; + for (i = 0; i < ARRAY_SIZE(zeros) && c >= zeros[i]; ++i) { + if (zeros[i] <= c && c <= zeros[i] + 9) { + v = c - zeros[i]; + break; + } + } + } + return v < base ? v : -1; +} + +/********************************************************************* + * _wcstoi64_l (MSVCRT.@) + */ +__int64 CDECL _wcstoi64_l(const wchar_t *nptr, + wchar_t **endptr, int base, _locale_t locale) +{ + BOOL negative = FALSE, empty = TRUE; + __int64 ret = 0; + + TRACE("(%s %p %d %p)\n", debugstr_w(nptr), endptr, base, locale); + + if (!MSVCRT_CHECK_PMT(nptr != NULL)) return 0; + if (!MSVCRT_CHECK_PMT(base == 0 || base >= 2)) return 0; + if (!MSVCRT_CHECK_PMT(base <= 36)) return 0; + + if(endptr) + *endptr = (wchar_t*)nptr; + + while(_iswspace_l(*nptr, locale)) nptr++; + + if(*nptr == '-') { + negative = TRUE; + nptr++; + } else if(*nptr == '+') + nptr++; + + if((base==0 || base==16) && wctoint(*nptr, 1)==0 && (nptr[1]=='x' || nptr[1]=='X')) { + base = 16; + nptr += 2; + } + + if(base == 0) { + if(wctoint(*nptr, 1)==0) + base = 8; + else + base = 10; + } + + while(*nptr) { + int v = wctoint(*nptr, base); + if(v<0) + break; + + if(negative) + v = -v; + + nptr++; + empty = FALSE; + + if(!negative && (ret>I64_MAX/base || ret*base>I64_MAX-v)) { + ret = I64_MAX; + *_errno() = ERANGE; + } else if(negative && (ret LONG_MAX) { + ret = LONG_MAX; + *_errno() = ERANGE; + }else if(ret < LONG_MIN) { + ret = LONG_MIN; + *_errno() = ERANGE; + } + return ret; +} + +/********************************************************************* + * wcstol (MSVCRT.@) + */ +__msvcrt_long CDECL wcstol(const wchar_t *s, + wchar_t **end, int base) +{ + return _wcstol_l(s, end, base, NULL); +} + +/********************************************************************* + * _wtoi_l (MSVCRT.@) + */ +int __cdecl _wtoi_l(const wchar_t *str, _locale_t locale) +{ + __int64 ret = _wcstoi64_l(str, NULL, 10, locale); + + if(ret > INT_MAX) { + ret = INT_MAX; + *_errno() = ERANGE; + } else if(ret < INT_MIN) { + ret = INT_MIN; + *_errno() = ERANGE; + } + return ret; +} + +/********************************************************************* + * _wtoi (MSVCRT.@) + */ +int __cdecl _wtoi(const wchar_t *str) +{ + return _wtoi_l(str, NULL); +} + +/********************************************************************* + * _wtol_l (MSVCRT.@) + */ +__msvcrt_long __cdecl _wtol_l(const wchar_t *str, _locale_t locale) +{ + __int64 ret = _wcstoi64_l(str, NULL, 10, locale); + + if(ret > LONG_MAX) { + ret = LONG_MAX; + *_errno() = ERANGE; + } else if(ret < LONG_MIN) { + ret = LONG_MIN; + *_errno() = ERANGE; + } + return ret; +} + +/********************************************************************* + * _wtol (MSVCRT.@) + */ +__msvcrt_long __cdecl _wtol(const wchar_t *str) +{ + return _wtol_l(str, NULL); +} + +#if _MSVCR_VER>=120 + +/********************************************************************* + * _wtoll_l (MSVCR120.@) + */ +__int64 __cdecl _wtoll_l(const wchar_t *str, _locale_t locale) +{ + return _wcstoi64_l(str, NULL, 10, locale); +} + +/********************************************************************* + * _wtoll (MSVCR120.@) + */ +__int64 __cdecl _wtoll(const wchar_t *str) +{ + return _wtoll_l(str, NULL); +} + +#endif /* _MSVCR_VER>=120 */ + +/********************************************************************* + * _wcstoui64_l (MSVCRT.@) + */ +unsigned __int64 CDECL _wcstoui64_l(const wchar_t *nptr, + wchar_t **endptr, int base, _locale_t locale) +{ + BOOL negative = FALSE, empty = TRUE; + unsigned __int64 ret = 0; + + TRACE("(%s %p %d %p)\n", debugstr_w(nptr), endptr, base, locale); + + if (!MSVCRT_CHECK_PMT(nptr != NULL)) return 0; + if (!MSVCRT_CHECK_PMT(base == 0 || base >= 2)) return 0; + if (!MSVCRT_CHECK_PMT(base <= 36)) return 0; + + if(endptr) + *endptr = (wchar_t*)nptr; + + while(_iswspace_l(*nptr, locale)) nptr++; + + if(*nptr == '-') { + negative = TRUE; + nptr++; + } else if(*nptr == '+') + nptr++; + + if((base==0 || base==16) && wctoint(*nptr, 1)==0 && (nptr[1]=='x' || nptr[1]=='X')) { + base = 16; + nptr += 2; + } + + if(base == 0) { + if(wctoint(*nptr, 1)==0) + base = 8; + else + base = 10; + } + + while(*nptr) { + int v = wctoint(*nptr, base); + if(v<0) + break; + + nptr++; + empty = FALSE; + + if(ret>UI64_MAX/base || ret*base>UI64_MAX-v) { + ret = UI64_MAX; + *_errno() = ERANGE; + } else + ret = ret*base + v; + } + + if(endptr && !empty) + *endptr = (wchar_t*)nptr; + + return negative ? -ret : ret; +} + +/********************************************************************* + * _wcstoui64 (MSVCRT.@) + */ +unsigned __int64 CDECL _wcstoui64(const wchar_t *nptr, + wchar_t **endptr, int base) +{ + return _wcstoui64_l(nptr, endptr, base, NULL); +} + +/********************************************************************* + * _wcstoul_l (MSVCRT.@) + */ +__msvcrt_ulong __cdecl _wcstoul_l(const wchar_t *s, + wchar_t **end, int base, _locale_t locale) +{ + __int64 ret = _wcstoi64_l(s, end, base, locale); + + if(ret > ULONG_MAX) { + ret = ULONG_MAX; + *_errno() = ERANGE; + }else if(ret < -(__int64)ULONG_MAX) { + ret = 1; + *_errno() = ERANGE; + } + return ret; +} + +/********************************************************************* + * wcstoul (MSVCRT.@) + */ +__msvcrt_ulong __cdecl wcstoul(const wchar_t *s, wchar_t **end, int base) +{ + return _wcstoul_l(s, end, base, NULL); +} + +/****************************************************************** + * wcsnlen (MSVCRT.@) + */ +size_t CDECL wcsnlen(const wchar_t *s, size_t maxlen) +{ + size_t i; + + for (i = 0; i < maxlen; i++) + if (!s[i]) break; + return i; +} + +/********************************************************************* + * _towupper_l (MSVCRT.@) + */ +wint_t CDECL _towupper_l(wint_t c, _locale_t locale) +{ + pthreadlocinfo locinfo; + wchar_t ret; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(!locinfo->lc_handle[LC_CTYPE]) { + if(c >= 'a' && c <= 'z') + return c + 'A' - 'a'; + return c; + } + + if(!LCMapStringW(locinfo->lc_handle[LC_CTYPE], LCMAP_UPPERCASE, &c, 1, &ret, 1)) + return c; + return ret; +} + +/********************************************************************* + * towupper (MSVCRT.@) + */ +wint_t CDECL towupper(wint_t c) +{ + return _towupper_l(c, NULL); +} + +/********************************************************************* + * wcschr (MSVCRT.@) + */ +wchar_t* CDECL wcschr(const wchar_t *str, wchar_t ch) +{ + do { if (*str == ch) return (WCHAR *)(ULONG_PTR)str; } while (*str++); + return NULL; +} + +/********************************************************************* + * wcsrchr (MSVCRT.@) + */ +wchar_t* CDECL wcsrchr(const wchar_t *str, wchar_t ch) +{ + WCHAR *ret = NULL; + do { if (*str == ch) ret = (WCHAR *)(ULONG_PTR)str; } while (*str++); + return ret; +} + +/*********************************************************************** + * wcslen (MSVCRT.@) + */ +size_t CDECL wcslen(const wchar_t *str) +{ + const wchar_t *s = str; + while (*s) s++; + return s - str; +} + +/********************************************************************* + * wcsstr (MSVCRT.@) + */ +wchar_t* CDECL wcsstr(const wchar_t *str, const wchar_t *sub) +{ + while(*str) + { + const wchar_t *p1 = str, *p2 = sub; + while(*p1 && *p2 && *p1 == *p2) + { + p1++; + p2++; + } + if(!*p2) + return (wchar_t*)str; + str++; + } + return NULL; +} + +/********************************************************************* + * _wtoi64_l (MSVCRT.@) + */ +__int64 CDECL _wtoi64_l(const wchar_t *str, _locale_t locale) +{ + ULONGLONG RunningTotal = 0; + BOOL bMinus = FALSE; + + while (_iswspace_l(*str, locale)) { + str++; + } /* while */ + + if (*str == '+') { + str++; + } else if (*str == '-') { + bMinus = TRUE; + str++; + } /* if */ + + while (*str >= '0' && *str <= '9') { + RunningTotal = RunningTotal * 10 + *str - '0'; + str++; + } /* while */ + + return bMinus ? -RunningTotal : RunningTotal; +} + +/********************************************************************* + * _wtoi64 (MSVCRT.@) + */ +__int64 CDECL _wtoi64(const wchar_t *str) +{ + return _wtoi64_l(str, NULL); +} + +/********************************************************************* + * _wcsxfrm_l (MSVCRT.@) + */ +size_t CDECL _wcsxfrm_l(wchar_t *dest, const wchar_t *src, + size_t len, _locale_t locale) +{ + pthreadlocinfo locinfo; + int i, ret; + + if(!MSVCRT_CHECK_PMT(src)) return INT_MAX; + if(!MSVCRT_CHECK_PMT(dest || !len)) return INT_MAX; + + if(len > INT_MAX) { + FIXME("len > INT_MAX not supported\n"); + len = INT_MAX; + } + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = locale->locinfo; + + if(!locinfo->lc_handle[LC_COLLATE]) { + wcsncpy(dest, src, len); + return wcslen(src); + } + + ret = LCMapStringW(locinfo->lc_handle[LC_COLLATE], + LCMAP_SORTKEY, src, -1, NULL, 0); + if(!ret) { + if(len) dest[0] = 0; + *_errno() = EILSEQ; + return INT_MAX; + } + if(!len) return ret-1; + + if(ret > len) { + dest[0] = 0; + *_errno() = ERANGE; + return ret-1; + } + + ret = LCMapStringW(locinfo->lc_handle[LC_COLLATE], + LCMAP_SORTKEY, src, -1, dest, len) - 1; + for(i=ret; i>=0; i--) + dest[i] = ((unsigned char*)dest)[i]; + return ret; +} + +/********************************************************************* + * wcsxfrm (MSVCRT.@) + */ +size_t CDECL wcsxfrm(wchar_t *dest, const wchar_t *src, size_t len) +{ + return _wcsxfrm_l(dest, src, len, NULL); +}