Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomáš Rylek <trylek@microsoft.com>2020-12-08 05:19:44 +0300
committerGitHub <noreply@github.com>2020-12-08 05:19:44 +0300
commit69e114c1abf91241a0eeecf1ecceab4711b8aa62 (patch)
treeb81a0b35748f5e598412bcc504335cdbd322cd43 /src/coreclr/palrt
parent0ec07945a9759a72a689edbb01e69b232e26e05a (diff)
December infra rollout - remove duplicated 'src' from coreclr subrepo (src/coreclr/src becomes src/coreclr) (#44973)
* Move src/coreclr/src/Directory.Build.targets to src/coreclr Merge src/coreclr/src/CMakeLists.txt into src/coreclr/CMakeLists.txt * Mechanical move of src/coreclr/src to src/coreclr * Scripts adjustments to reflect the changed paths
Diffstat (limited to 'src/coreclr/palrt')
-rw-r--r--src/coreclr/palrt/CMakeLists.txt20
-rw-r--r--src/coreclr/palrt/bstr.cpp263
-rw-r--r--src/coreclr/palrt/coguid.cpp192
-rw-r--r--src/coreclr/palrt/comem.cpp21
-rw-r--r--src/coreclr/palrt/common.h17
-rw-r--r--src/coreclr/palrt/guid.cpp47
-rw-r--r--src/coreclr/palrt/memorystream.cpp316
-rw-r--r--src/coreclr/palrt/path.cpp646
-rw-r--r--src/coreclr/palrt/shlwapip.h37
-rw-r--r--src/coreclr/palrt/variant.cpp32
10 files changed, 1591 insertions, 0 deletions
diff --git a/src/coreclr/palrt/CMakeLists.txt b/src/coreclr/palrt/CMakeLists.txt
new file mode 100644
index 00000000000..a4c6fdd5ae8
--- /dev/null
+++ b/src/coreclr/palrt/CMakeLists.txt
@@ -0,0 +1,20 @@
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(PALRT_SOURCES
+ bstr.cpp
+ coguid.cpp
+ comem.cpp
+ guid.cpp
+ memorystream.cpp
+ path.cpp
+ variant.cpp
+)
+
+add_library_clr(palrt
+ STATIC
+ ${PALRT_SOURCES}
+)
+
+# Install the static PAL library for VS
+_install (TARGETS palrt DESTINATION lib)
diff --git a/src/coreclr/palrt/bstr.cpp b/src/coreclr/palrt/bstr.cpp
new file mode 100644
index 00000000000..2f5ccd9cd48
--- /dev/null
+++ b/src/coreclr/palrt/bstr.cpp
@@ -0,0 +1,263 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+//
+// ===========================================================================
+// File: bstr.cpp
+//
+// ===========================================================================
+
+
+/*++
+
+Abstract:
+
+ PALRT BSTR support
+
+Revision History:
+
+--*/
+
+#include "common.h"
+#include "intsafe.h"
+
+#define CCH_BSTRMAX 0x7FFFFFFF // 4 + (0x7ffffffb + 1 ) * 2 ==> 0xFFFFFFFC
+#define CB_BSTRMAX 0xFFFFFFFa // 4 + (0xfffffff6 + 2) ==> 0xFFFFFFFC
+
+#define WIN32_ALLOC_ALIGN (16 - 1)
+
+inline HRESULT CbSysStringSize(ULONG cchSize, BOOL isByteLen, ULONG *result)
+{
+ if (result == NULL)
+ return E_INVALIDARG;
+
+ // +2 for the null terminator
+ // + DWORD_PTR to store the byte length of the string
+ int constant = sizeof(WCHAR) + sizeof(DWORD_PTR) + WIN32_ALLOC_ALIGN;
+
+ if (isByteLen)
+ {
+ if (SUCCEEDED(ULongAdd(constant, cchSize, result)))
+ {
+ *result = *result & ~WIN32_ALLOC_ALIGN;
+ return NOERROR;
+ }
+ }
+ else
+ {
+ ULONG temp = 0; // should not use in-place addition in ULongAdd
+ if (SUCCEEDED(ULongMult(cchSize, sizeof(WCHAR), &temp)) &
+ SUCCEEDED(ULongAdd(temp, constant, result)))
+ {
+ *result = *result & ~WIN32_ALLOC_ALIGN;
+ return NOERROR;
+ }
+ }
+ return INTSAFE_E_ARITHMETIC_OVERFLOW;
+}
+
+/***
+*BSTR SysAllocStringLen(char*, unsigned int)
+*Purpose:
+* Allocation a bstr of the given length and initialize with
+* the pasted in string
+*
+*Entry:
+* [optional]
+*
+*Exit:
+* return value = BSTR, NULL if the allocation failed.
+*
+***********************************************************************/
+STDAPI_(BSTR) SysAllocStringLen(const OLECHAR *psz, UINT len)
+{
+
+ BSTR bstr;
+ DWORD cbTotal = 0;
+
+ if (FAILED(CbSysStringSize(len, FALSE, &cbTotal)))
+ return NULL;
+
+ bstr = (OLECHAR *)PAL_malloc(cbTotal);
+
+ if(bstr != NULL){
+
+#if defined(HOST_64BIT)
+ // NOTE: There are some apps which peek back 4 bytes to look at the size of the BSTR. So, in case of 64-bit code,
+ // we need to ensure that the BSTR length can be found by looking one DWORD before the BSTR pointer.
+ *(DWORD_PTR *)bstr = (DWORD_PTR) 0;
+ bstr = (BSTR) ((char *) bstr + sizeof (DWORD));
+#endif
+ *(DWORD FAR*)bstr = (DWORD)len * sizeof(OLECHAR);
+
+ bstr = (BSTR) ((char*) bstr + sizeof(DWORD));
+
+ if(psz != NULL){
+ memcpy(bstr, psz, len * sizeof(OLECHAR));
+ }
+
+ bstr[len] = '\0'; // always 0 terminate
+ }
+
+ return bstr;
+}
+
+/***
+*BSTR SysAllocString(char*)
+*Purpose:
+* Allocation a bstr using the passed in string
+*
+*Entry:
+* String to create a bstr for
+*
+*Exit:
+* return value = BSTR, NULL if allocation failed
+*
+***********************************************************************/
+STDAPI_(BSTR) SysAllocString(const OLECHAR* psz)
+{
+ if(psz == NULL)
+ return NULL;
+
+ return SysAllocStringLen(psz, (DWORD)wcslen(psz));
+}
+
+STDAPI_(BSTR)
+SysAllocStringByteLen(const char FAR* psz, unsigned int len)
+{
+ BSTR bstr;
+ DWORD cbTotal = 0;
+
+ if (FAILED(CbSysStringSize(len, TRUE, &cbTotal)))
+ return FALSE;
+
+ bstr = (OLECHAR *)PAL_malloc(cbTotal);
+
+ if (bstr != NULL) {
+#if defined(HOST_64BIT)
+ *(DWORD FAR*)((char *)bstr + sizeof (DWORD)) = (DWORD)len;
+#else
+ *(DWORD FAR*)bstr = (DWORD)len;
+#endif
+
+ bstr = (WCHAR*) ((char*) bstr + sizeof(DWORD_PTR));
+
+ if (psz != NULL) {
+ memcpy(bstr, psz, len);
+ }
+
+ // NULL-terminate with both a narrow and wide zero.
+ *((char *)bstr + len) = '\0';
+ *(WCHAR *)((char *)bstr + ((len + 1) & ~1)) = 0;
+ }
+
+ return bstr;
+}
+
+/***
+*void SysFreeString(BSTR)
+*Purpose:
+* Free the given BSTR.
+*
+*Entry:
+* bstr = the BSTR to free
+*
+*Exit:
+* None
+*
+***********************************************************************/
+STDAPI_(void) SysFreeString(BSTR bstr)
+{
+ if(bstr == NULL)
+ return;
+ free((BYTE *)bstr-sizeof(DWORD_PTR));
+}
+
+/***
+*unsigned int SysStringLen(BSTR)
+*Purpose:
+* return the length in characters of the given BSTR.
+*
+*Entry:
+* bstr = the BSTR to return the length of
+*
+*Exit:
+* return value = unsigned int, length in characters.
+*
+***********************************************************************/
+STDAPI_(unsigned int)
+SysStringLen(BSTR bstr)
+{
+ if(bstr == NULL)
+ return 0;
+ return (unsigned int)((((DWORD FAR*)bstr)[-1]) / sizeof(OLECHAR));
+}
+
+/***
+*unsigned int SysStringByteLen(BSTR)
+*Purpose:
+* return the size in bytes of the given BSTR.
+*
+*Entry:
+* bstr = the BSTR to return the size of
+*
+*Exit:
+* return value = unsigned int, size in bytes.
+*
+***********************************************************************/
+STDAPI_(unsigned int)
+SysStringByteLen(BSTR bstr)
+{
+ if(bstr == NULL)
+ return 0;
+ return (unsigned int)(((DWORD FAR*)bstr)[-1]);
+}
+
+extern "C" HRESULT
+ErrStringCopy(BSTR bstrSource, BSTR FAR *pbstrOut)
+{
+ if (bstrSource == NULL) {
+ *pbstrOut = NULL;
+ return NOERROR;
+ }
+ if ((*pbstrOut = SysAllocStringLen(bstrSource,
+ SysStringLen(bstrSource))) == NULL)
+ return E_OUTOFMEMORY;
+
+ return NOERROR;
+}
+
+/***
+*PRIVATE HRESULT ErrSysAllocString(char*, BSTR*)
+*Purpose:
+* This is an implementation of SysAllocString that check for the
+* NULL return value and return the corresponding error - E_OUTOFMEMORY.
+*
+* This is simply a convenience, and this routine is only used
+* internally by the oledisp component.
+*
+*Entry:
+* psz = the source string
+*
+*Exit:
+* return value = HRESULT
+* S_OK
+* E_OUTOFMEMORY
+*
+* *pbstrOut = the newly allocated BSTR
+*
+***********************************************************************/
+extern "C" HRESULT
+ErrSysAllocString(const OLECHAR FAR* psz, BSTR FAR* pbstrOut)
+{
+ if(psz == NULL){
+ *pbstrOut = NULL;
+ return NOERROR;
+ }
+
+ if((*pbstrOut = SysAllocString(psz)) == NULL)
+ return E_OUTOFMEMORY;
+
+ return NOERROR;
+}
diff --git a/src/coreclr/palrt/coguid.cpp b/src/coreclr/palrt/coguid.cpp
new file mode 100644
index 00000000000..8a91d8fa864
--- /dev/null
+++ b/src/coreclr/palrt/coguid.cpp
@@ -0,0 +1,192 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+//
+// ===========================================================================
+// File: coguid.cpp
+//
+// misc guid functions for PALRT
+// ===========================================================================
+
+#include "common.h"
+
+STDAPI_(int) StringFromGUID2(REFGUID rguid, LPOLESTR lptsz, int cchMax)
+{
+ if (cchMax < 39)
+ return 0;
+
+ return swprintf_s(lptsz, cchMax, W("{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}"),
+ rguid.Data1, rguid.Data2, rguid.Data3,
+ rguid.Data4[0], rguid.Data4[1],
+ rguid.Data4[2], rguid.Data4[3],
+ rguid.Data4[4], rguid.Data4[5],
+ rguid.Data4[6], rguid.Data4[7]) + 1;
+}
+
+static BOOL wUUIDFromString(LPCWSTR lpsz, GUID * pguid);
+static BOOL wGUIDFromString(LPCWSTR lpsz, GUID * pguid);
+
+static BOOL HexStringToDword(LPCWSTR FAR& lpsz, DWORD FAR& Value,
+ int cDigits, WCHAR chDelim);
+
+//+-------------------------------------------------------------------------
+//
+// Function: IIDFromString
+//
+// Synopsis: converts string {...} form int guid
+//
+// Arguments: [lpsz] - ptr to buffer for results
+// [lpclsid] - the guid to convert
+//
+// Returns: NOERROR
+// CO_E_CLASSSTRING
+//
+//--------------------------------------------------------------------------
+STDAPI IIDFromString(LPWSTR lpsz, CLSID * lpclsid)
+{
+ if (lpsz == NULL)
+ {
+ *lpclsid = CLSID_NULL;
+ return NOERROR;
+ }
+
+ if (*lpsz == 0)
+ {
+ return(CO_E_CLASSSTRING);
+ }
+
+ return wGUIDFromString(lpsz,lpclsid)
+ ? NOERROR : CO_E_CLASSSTRING;
+}
+
+//+-------------------------------------------------------------------------
+//
+// Function: wGUIDFromString (internal)
+//
+// Synopsis: Parse GUID such as {00000000-0000-0000-0000-000000000000}
+//
+// Arguments: [lpsz] - the guid string to convert
+// [pguid] - guid to return
+//
+// Returns: TRUE if successful
+//
+//--------------------------------------------------------------------------
+static BOOL wGUIDFromString(LPCWSTR lpsz, GUID * pguid)
+{
+ if (*lpsz++ != '{' )
+ return FALSE;
+
+ if (wUUIDFromString(lpsz, pguid) != TRUE)
+ return FALSE;
+
+ lpsz +=36;
+
+ if (*lpsz++ != '}' )
+ return FALSE;
+
+ if (*lpsz != '\0')
+ return FALSE;
+
+ return TRUE;
+}
+
+//+-------------------------------------------------------------------------
+//
+// Function: wUUIDFromString (internal)
+//
+// Synopsis: Parse UUID such as 00000000-0000-0000-0000-000000000000
+//
+// Arguments: [lpsz] - Supplies the UUID string to convert
+// [pguid] - Returns the GUID.
+//
+// Returns: TRUE if successful
+//
+//--------------------------------------------------------------------------
+static BOOL wUUIDFromString(LPCWSTR lpsz, GUID * pguid)
+{
+ DWORD dw;
+
+ if (!HexStringToDword(lpsz, pguid->Data1, sizeof(DWORD)*2, '-'))
+ return FALSE;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(WORD)*2, '-'))
+ return FALSE;
+ pguid->Data2 = (WORD)dw;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(WORD)*2, '-'))
+ return FALSE;
+ pguid->Data3 = (WORD)dw;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(BYTE)*2, 0))
+ return FALSE;
+ pguid->Data4[0] = (BYTE)dw;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(BYTE)*2, '-'))
+ return FALSE;
+ pguid->Data4[1] = (BYTE)dw;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(BYTE)*2, 0))
+ return FALSE;
+ pguid->Data4[2] = (BYTE)dw;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(BYTE)*2, 0))
+ return FALSE;
+ pguid->Data4[3] = (BYTE)dw;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(BYTE)*2, 0))
+ return FALSE;
+ pguid->Data4[4] = (BYTE)dw;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(BYTE)*2, 0))
+ return FALSE;
+ pguid->Data4[5] = (BYTE)dw;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(BYTE)*2, 0))
+ return FALSE;
+ pguid->Data4[6] = (BYTE)dw;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(BYTE)*2, 0))
+ return FALSE;
+ pguid->Data4[7] = (BYTE)dw;
+
+ return TRUE;
+}
+
+//+-------------------------------------------------------------------------
+//
+// Function: HexStringToDword (private)
+//
+// Synopsis: scan lpsz for a number of hex digits (at most 8); update lpsz
+// return value in Value; check for chDelim;
+//
+// Arguments: [lpsz] - the hex string to convert
+// [Value] - the returned value
+// [cDigits] - count of digits
+//
+// Returns: TRUE for success
+//
+//--------------------------------------------------------------------------
+static BOOL HexStringToDword(LPCWSTR FAR& lpsz, DWORD FAR& Value,
+ int cDigits, WCHAR chDelim)
+{
+ int Count;
+
+ Value = 0;
+ for (Count = 0; Count < cDigits; Count++, lpsz++)
+ {
+ if (*lpsz >= '0' && *lpsz <= '9')
+ Value = (Value << 4) + *lpsz - '0';
+ else if (*lpsz >= 'A' && *lpsz <= 'F')
+ Value = (Value << 4) + *lpsz - 'A' + 10;
+ else if (*lpsz >= 'a' && *lpsz <= 'f')
+ Value = (Value << 4) + *lpsz - 'a' + 10;
+ else
+ return(FALSE);
+ }
+
+ if (chDelim != 0)
+ return *lpsz++ == chDelim;
+ else
+ return TRUE;
+}
diff --git a/src/coreclr/palrt/comem.cpp b/src/coreclr/palrt/comem.cpp
new file mode 100644
index 00000000000..e56e720cc80
--- /dev/null
+++ b/src/coreclr/palrt/comem.cpp
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+//
+// ===========================================================================
+// File: comem.cpp
+//
+// ===========================================================================
+
+#include "common.h"
+
+STDAPI_(LPVOID) CoTaskMemAlloc(SIZE_T cb)
+{
+ return malloc(cb);
+}
+
+STDAPI_(void) CoTaskMemFree(LPVOID pv)
+{
+ free(pv);
+}
diff --git a/src/coreclr/palrt/common.h b/src/coreclr/palrt/common.h
new file mode 100644
index 00000000000..684c134abed
--- /dev/null
+++ b/src/coreclr/palrt/common.h
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// common.h
+//
+
+//
+// Common include file for the palrt code.
+//*****************************************************************************
+
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+#include <switches.h>
+#include <winwrap.h>
+#include "shlwapip.h"
+#endif // _COMMON_H_
diff --git a/src/coreclr/palrt/guid.cpp b/src/coreclr/palrt/guid.cpp
new file mode 100644
index 00000000000..68cc157d91c
--- /dev/null
+++ b/src/coreclr/palrt/guid.cpp
@@ -0,0 +1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+//
+// ===========================================================================
+// File: guid.cpp
+//
+// PALRT guids
+// ===========================================================================
+
+#define INITGUID
+#include <guiddef.h>
+
+// These are GUIDs and IIDs that would normally be provided by the system via uuid.lib,
+// and that the PALRT exposes through headers.
+
+DEFINE_GUID(GUID_NULL, 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+DEFINE_GUID(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
+DEFINE_GUID(IID_IClassFactory, 0x00000001, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
+
+
+// objidl.idl
+DEFINE_GUID(IID_ISequentialStream, 0x0c733a30, 0x2a1c, 0x11ce, 0xad, 0xe5, 0x00, 0xaa, 0x00, 0x44, 0x77, 0x3d);
+DEFINE_GUID(IID_IStream, 0x0000000c, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
+
+// Create a random guid based on the https://www.ietf.org/rfc/rfc4122.txt
+STDAPI
+CoCreateGuid(OUT GUID * pguid)
+{
+ PAL_Random(pguid, sizeof(GUID));
+
+ static const USHORT VersionMask = 0xF000;
+ static const USHORT RandomGuidVersion = 0x4000;
+
+ static const BYTE ClockSeqHiAndReservedMask = 0xC0;
+ static const BYTE ClockSeqHiAndReservedValue = 0x80;
+
+ // Modify bits indicating the type of the GUID
+
+ // time_hi_and_version
+ pguid->Data3 = (pguid->Data3 & ~VersionMask) | RandomGuidVersion;
+ // clock_seq_hi_and_reserved
+ pguid->Data4[0] = (pguid->Data4[0] & ~ClockSeqHiAndReservedMask) | ClockSeqHiAndReservedValue;
+
+ return S_OK;
+}
diff --git a/src/coreclr/palrt/memorystream.cpp b/src/coreclr/palrt/memorystream.cpp
new file mode 100644
index 00000000000..0ed06547f3b
--- /dev/null
+++ b/src/coreclr/palrt/memorystream.cpp
@@ -0,0 +1,316 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+//
+// ===========================================================================
+// File: memorystream.cpp
+//
+// ===========================================================================
+/*++
+
+Abstract:
+
+ in memory stream
+
+
+
+
+Revision History:
+
+--*/
+
+#include "common.h"
+
+#include "objidl.h"
+
+class MemoryStream : public IStream
+{
+ LONG m_cRef; // QI refcount
+ ULONG m_nPos; // the current position in the stream
+ ULONG m_nSize; // the current size of the stream
+ ULONG m_nData; // the size of the allocated data storage, can be < m_nSize
+ BYTE* m_pData; // the data storage
+
+private:
+ HRESULT Ensure(ULONG nNewData)
+ {
+ if (nNewData > m_nData)
+ {
+ // apply some heurestic for growing
+ ULONG n = m_nData;
+
+ // grow 2x for smaller sizes, 1.25x for bigger sizes
+ n = min(2 * n, n + n / 4 + 0x100000);
+
+ // don't allocate tiny chunks
+ n = max(n, 0x100);
+
+ // compare with the hard limit
+ nNewData = max(n, nNewData);
+ }
+ else
+ if (nNewData > m_nData / 4)
+ {
+ // shrinking but it is not worth it
+ return S_OK;
+ }
+
+ BYTE * pNewData = (BYTE*)realloc(m_pData, nNewData);
+ if (pNewData == NULL && nNewData != 0)
+ return E_OUTOFMEMORY;
+
+ m_nData = nNewData;
+ m_pData = pNewData;
+ return S_OK;
+ }
+
+public:
+ MemoryStream()
+ {
+ m_cRef = 1;
+ m_nPos = 0;
+ m_nSize = 0;
+ m_nData = 0;
+ m_pData = NULL;
+ }
+
+#ifdef __GNUC__
+ virtual
+#endif
+ ~MemoryStream()
+ {
+ free(m_pData);
+ }
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(
+ REFIID riid,
+ void **ppvObject)
+ {
+ if (riid == IID_IStream ||
+ riid == IID_ISequentialStream ||
+ riid == IID_IUnknown)
+ {
+ InterlockedIncrement(&m_cRef);
+ *ppvObject = this;
+ return S_OK;
+ }
+ else
+ {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return InterlockedIncrement(&m_cRef);
+ }
+
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ delete this;
+ return cRef;
+ }
+
+ HRESULT STDMETHODCALLTYPE Read(
+ void *pv,
+ ULONG cb,
+ ULONG *pcbRead)
+ {
+ ULONG nData;
+ ULONG nNewPos = m_nPos + cb;
+
+ // check for overflow
+ if (nNewPos < cb)
+ return STG_E_INVALIDFUNCTION;
+
+ // compare with the actual size
+ nNewPos = min(nNewPos, m_nSize);
+
+ // compare with the data available
+ nData = min(nNewPos, m_nData);
+
+ // copy the data over
+ if (nData > m_nPos)
+ memcpy(pv, m_pData + m_nPos, nData - m_nPos);
+
+ // fill the rest with zeros
+ if (nNewPos > nData)
+ memset((BYTE*)pv + (nData - m_nPos), 0, nNewPos - nData);
+
+ cb = nNewPos - m_nPos;
+ m_nPos = nNewPos;
+
+ if (pcbRead)
+ *pcbRead = cb;
+
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE Write(
+ const void *pv,
+ ULONG cb,
+ ULONG *pcbWritten)
+ {
+ ULONG nNewPos = m_nPos + cb;
+
+ // check for overflow
+ if (nNewPos < cb)
+ return STG_E_INVALIDFUNCTION;
+
+ // ensure the space
+ if (nNewPos > m_nData)
+ {
+ HRESULT hr = Ensure(nNewPos);
+ if (FAILED(hr)) return hr;
+ }
+
+ // copy the data over
+ memcpy(m_pData + m_nPos, pv, cb);
+
+ m_nPos = nNewPos;
+ if (m_nPos > m_nSize)
+ m_nSize = m_nPos;
+
+ if (pcbWritten)
+ *pcbWritten = cb;
+
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE Seek(
+ LARGE_INTEGER dlibMove,
+ DWORD dwOrigin,
+ ULARGE_INTEGER *plibNewPosition)
+ {
+ ULONG lStartPos;
+ LONGLONG lNewPos;
+
+ switch (dwOrigin)
+ {
+ case STREAM_SEEK_SET:
+ lStartPos = 0;
+ break;
+ case STREAM_SEEK_CUR:
+ lStartPos = m_nPos;
+ break;
+ case STREAM_SEEK_END:
+ lStartPos = m_nSize;
+ break;
+ default:
+ return STG_E_INVALIDFUNCTION;
+ }
+
+ lNewPos = lStartPos + dlibMove.QuadPart;
+
+ // it is an error to seek before the beginning of the stream
+ if (lNewPos < 0)
+ return STG_E_INVALIDFUNCTION;
+
+ // It is not, however, an error to seek past the end of the stream
+ if (lNewPos > m_nSize)
+ {
+ ULARGE_INTEGER NewSize;
+ NewSize.QuadPart = lNewPos;
+
+ HRESULT hr = SetSize(NewSize);
+ if (FAILED(hr)) return hr;
+ }
+
+ m_nPos = (ULONG)lNewPos;
+
+ if (plibNewPosition != NULL)
+ plibNewPosition->QuadPart = m_nPos;
+
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE SetSize(
+ ULARGE_INTEGER libNewSize)
+ {
+ if (libNewSize.u.HighPart != 0)
+ return STG_E_INVALIDFUNCTION;
+
+ m_nSize = libNewSize.u.LowPart;
+
+ // free the space if we are shrinking
+ if (m_nSize < m_nData)
+ Ensure(m_nSize);
+
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE CopyTo(
+ IStream *pstm,
+ ULARGE_INTEGER cb,
+ ULARGE_INTEGER *pcbRead,
+ ULARGE_INTEGER *pcbWritten)
+ {
+ _ASSERTE(false);
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE Commit(
+ DWORD grfCommitFlags)
+ {
+ _ASSERTE(false);
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE Revert()
+ {
+ _ASSERTE(false);
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE LockRegion(
+ ULARGE_INTEGER libOffset,
+ ULARGE_INTEGER cb,
+ DWORD dwLockType)
+ {
+ _ASSERTE(false);
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE UnlockRegion(
+ ULARGE_INTEGER libOffset,
+ ULARGE_INTEGER cb,
+ DWORD dwLockType)
+ {
+ _ASSERTE(false);
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE Stat(
+ STATSTG *pstatstg,
+ DWORD grfStatFlag)
+ {
+ memset(pstatstg, 0, sizeof(STATSTG));
+ pstatstg->cbSize.QuadPart = m_nSize;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE Clone(
+ IStream **ppstm)
+ {
+ _ASSERTE(false);
+ return E_NOTIMPL;
+ }
+};
+
+STDAPI CreateStreamOnHGlobal(PVOID hGlobal, BOOL fDeleteOnRelease, IStream** ppstm)
+{
+ MemoryStream* pStream;
+
+ if (hGlobal != NULL) return E_NOTIMPL;
+ _ASSERTE(fDeleteOnRelease == TRUE);
+
+ pStream = new MemoryStream;
+ if (pStream == NULL) return E_OUTOFMEMORY;
+
+ *ppstm = pStream;
+ return S_OK;
+}
diff --git a/src/coreclr/palrt/path.cpp b/src/coreclr/palrt/path.cpp
new file mode 100644
index 00000000000..7e0d2279f4a
--- /dev/null
+++ b/src/coreclr/palrt/path.cpp
@@ -0,0 +1,646 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+//
+// ===========================================================================
+// File: path.cpp
+//
+// Path APIs ported from shlwapi (especially for Fusion)
+// ===========================================================================
+
+#include "common.h"
+#include "strsafe.h"
+
+
+#define CH_SLASH W('/')
+#define CH_WHACK W('\\')
+
+//
+// Inline function to check for a double-backslash at the
+// beginning of a string
+//
+
+static __inline BOOL DBL_BSLASH(LPCWSTR psz)
+{
+ return (psz[0] == W('\\') && psz[1] == W('\\'));
+}
+
+//
+// Inline function to check for a path separator character.
+//
+
+static __inline BOOL IsPathSeparator(WCHAR ch)
+{
+ return (ch == CH_SLASH || ch == CH_WHACK);
+}
+
+__inline BOOL ChrCmpW_inline(WCHAR w1, WCHAR wMatch)
+{
+ return(!(w1 == wMatch));
+}
+
+STDAPI_(LPWSTR) StrRChrW(LPCWSTR lpStart, LPCWSTR lpEnd, WCHAR wMatch)
+{
+ LPCWSTR lpFound = NULL;
+
+ RIPMSG(lpStart && IS_VALID_STRING_PTRW(lpStart, -1), "StrRChrW: caller passed bad lpStart");
+ RIPMSG(!lpEnd || lpEnd <= lpStart + wcslen(lpStart), "StrRChrW: caller passed bad lpEnd");
+ // don't need to check for NULL lpStart
+
+ if (!lpEnd)
+ lpEnd = lpStart + wcslen(lpStart);
+
+ for ( ; lpStart < lpEnd; lpStart++)
+ {
+ if (!ChrCmpW_inline(*lpStart, wMatch))
+ lpFound = lpStart;
+ }
+ return ((LPWSTR)lpFound);
+}
+
+
+// check if a path is a root
+//
+// returns:
+// TRUE
+// "\" "X:\" "\\" "\\foo" "\\foo\bar"
+//
+// FALSE for others including "\\foo\bar\" (!)
+//
+STDAPI_(BOOL) PathIsRootW(LPCWSTR pPath)
+{
+ RIPMSG(pPath && IS_VALID_STRING_PTR(pPath, -1), "PathIsRoot: caller passed bad pPath");
+
+ if (!pPath || !*pPath)
+ {
+ return FALSE;
+ }
+
+ if (!lstrcmpiW(pPath + 1, W(":\\")))
+ {
+ return TRUE; // "X:\" case
+ }
+
+ if (IsPathSeparator(*pPath) && (*(pPath + 1) == 0))
+ {
+ return TRUE; // "/" or "\" case
+ }
+
+ if (DBL_BSLASH(pPath)) // smells like UNC name
+ {
+ LPCWSTR p;
+ int cBackslashes = 0;
+
+ for (p = pPath + 2; *p; p++)
+ {
+ if (*p == W('\\'))
+ {
+ //
+ // return FALSE for "\\server\share\dir"
+ // so just check if there is more than one slash
+ //
+ // "\\server\" without a share name causes
+ // problems for WNet APIs. we should return
+ // FALSE for this as well
+ //
+ if ((++cBackslashes > 1) || !*(p+1))
+ return FALSE;
+ }
+ }
+ // end of string with only 1 more backslash
+ // must be a bare UNC, which looks like a root dir
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+// rips the last part of the path off including the backslash
+// C:\foo -> C:\
+// C:\foo\bar -> C:\foo
+// C:\foo\ -> C:\foo
+// \\x\y\x -> \\x\y
+// \\x\y -> \\x
+// \\x -> \\ (Just the double slash!)
+// \foo -> \ (Just the slash!)
+//
+// in/out:
+// pFile fully qualified path name
+// returns:
+// TRUE we stripped something
+// FALSE didn't strip anything (root directory case)
+//
+*/
+STDAPI_(BOOL) PathRemoveFileSpecW(LPWSTR pFile)
+{
+ RIPMSG(pFile && IS_VALID_STRING_PTR(pFile, -1), "PathRemoveFileSpec: caller passed bad pFile");
+
+ if (pFile)
+ {
+ LPWSTR pT;
+ LPWSTR pT2 = pFile;
+
+ for (pT = pT2; *pT2; pT2++)
+ {
+ if (IsPathSeparator(*pT2))
+ {
+ pT = pT2; // last "\" found, (we will strip here)
+ }
+ else if (*pT2 == W(':')) // skip ":\" so we don't
+ {
+ if (IsPathSeparator(pT2[1])) // strip the "\" from "C:\"
+ {
+ pT2++;
+ }
+ pT = pT2 + 1;
+ }
+ }
+
+ if (*pT == 0)
+ {
+ // didn't strip anything
+ return FALSE;
+ }
+ else if (((pT == pFile) && IsPathSeparator(*pT)) || // is it the "\foo" case?
+ ((pT == pFile+1) && (*pT == CH_WHACK && *pFile == CH_WHACK))) // or the "\\bar" case?
+ {
+ // Is it just a '\'?
+ if (*(pT+1) != W('\0'))
+ {
+ // Nope.
+ *(pT+1) = W('\0');
+ return TRUE; // stripped something
+ }
+ else
+ {
+ // Yep.
+ return FALSE;
+ }
+ }
+ else
+ {
+ *pT = 0;
+ return TRUE; // stripped something
+ }
+ }
+ return FALSE;
+}
+
+//
+// Return a pointer to the end of the next path component in the string.
+// ie return a pointer to the next backslash or terminating NULL.
+//
+LPCWSTR GetPCEnd(LPCWSTR lpszStart)
+{
+ LPCWSTR lpszEnd;
+ LPCWSTR lpszSlash;
+
+ lpszEnd = StrChr(lpszStart, CH_WHACK);
+ lpszSlash = StrChr(lpszStart, CH_SLASH);
+ if ((lpszSlash && lpszSlash < lpszEnd) ||
+ !lpszEnd)
+ {
+ lpszEnd = lpszSlash;
+ }
+ if (!lpszEnd)
+ {
+ lpszEnd = lpszStart + wcslen(lpszStart);
+ }
+
+ return lpszEnd;
+}
+
+//
+// Given a pointer to the end of a path component, return a pointer to
+// its beginning.
+// ie return a pointer to the previous backslash (or start of the string).
+//
+LPCWSTR PCStart(LPCWSTR lpszStart, LPCWSTR lpszEnd)
+{
+ LPCWSTR lpszBegin = StrRChrW(lpszStart, lpszEnd, CH_WHACK);
+ LPCWSTR lpszSlash = StrRChrW(lpszStart, lpszEnd, CH_SLASH);
+ if (lpszSlash > lpszBegin)
+ {
+ lpszBegin = lpszSlash;
+ }
+ if (!lpszBegin)
+ {
+ lpszBegin = lpszStart;
+ }
+ return lpszBegin;
+}
+
+//
+// Fix up a few special cases so that things roughly make sense.
+//
+void NearRootFixups(LPWSTR lpszPath, BOOL fUNC)
+{
+ // Check for empty path.
+ if (lpszPath[0] == W('\0'))
+ {
+ // Fix up.
+#ifndef TARGET_UNIX
+ lpszPath[0] = CH_WHACK;
+#else
+ lpszPath[0] = CH_SLASH;
+#endif
+ lpszPath[1] = W('\0');
+ }
+ // Check for missing slash.
+ if (lpszPath[1] == W(':') && lpszPath[2] == W('\0'))
+ {
+ // Fix up.
+ lpszPath[2] = W('\\');
+ lpszPath[3] = W('\0');
+ }
+ // Check for UNC root.
+ if (fUNC && lpszPath[0] == W('\\') && lpszPath[1] == W('\0'))
+ {
+ // Fix up.
+ //lpszPath[0] = W('\\'); // already checked in if guard
+ lpszPath[1] = W('\\');
+ lpszPath[2] = W('\0');
+ }
+}
+
+/*----------------------------------------------------------
+Purpose: Canonicalize a path.
+
+Returns:
+Cond: --
+*/
+STDAPI_(BOOL) PathCanonicalizeW(LPWSTR lpszDst, LPCWSTR lpszSrc)
+{
+ LPCWSTR lpchSrc;
+ LPCWSTR lpchPCEnd; // Pointer to end of path component.
+ LPWSTR lpchDst;
+ BOOL fUNC;
+ int cchPC;
+
+ RIPMSG(lpszDst && IS_VALID_WRITE_BUFFER(lpszDst, WCHAR, MAX_PATH), "PathCanonicalize: caller passed bad lpszDst");
+ RIPMSG(lpszSrc && IS_VALID_STRING_PTR(lpszSrc, -1), "PathCanonicalize: caller passed bad lpszSrc");
+ RIPMSG(lpszDst != lpszSrc, "PathCanonicalize: caller passed the same buffer for lpszDst and lpszSrc");
+
+ if (!lpszDst || !lpszSrc)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ *lpszDst = W('\0');
+
+ fUNC = PathIsUNCW(lpszSrc); // Check for UNCness.
+
+ // Init.
+ lpchSrc = lpszSrc;
+ lpchDst = lpszDst;
+
+ while (*lpchSrc)
+ {
+ lpchPCEnd = GetPCEnd(lpchSrc);
+ cchPC = (int) (lpchPCEnd - lpchSrc)+1;
+
+ if (cchPC == 1 && IsPathSeparator(*lpchSrc)) // Check for slashes.
+ {
+ // Just copy them.
+#ifndef TARGET_UNIX
+ *lpchDst = CH_WHACK;
+#else
+ *lpchDst = CH_SLASH;
+#endif
+ lpchDst++;
+ lpchSrc++;
+ }
+ else if (cchPC == 2 && *lpchSrc == W('.')) // Check for dots.
+ {
+ // Skip it...
+ // Are we at the end?
+ if (*(lpchSrc+1) == W('\0'))
+ {
+ lpchSrc++;
+
+ // remove the last slash we copied (if we've copied one), but don't make a mal-formed root
+ if ((lpchDst > lpszDst) && !PathIsRootW(lpszDst))
+ lpchDst--;
+ }
+ else
+ {
+ lpchSrc += 2;
+ }
+ }
+ else if (cchPC == 3 && *lpchSrc == W('.') && *(lpchSrc + 1) == W('.')) // Check for dot dot.
+ {
+ // make sure we aren't already at the root
+ if (!PathIsRootW(lpszDst))
+ {
+ // Go up... Remove the previous path component.
+ lpchDst = (LPWSTR)PCStart(lpszDst, lpchDst - 1);
+ }
+ else
+ {
+ // When we can't back up, skip the trailing backslash
+ // so we don't copy one again. (C:\..\FOO would otherwise
+ // turn into C:\\FOO).
+ if (IsPathSeparator(*(lpchSrc + 2)))
+ {
+ lpchSrc++;
+ }
+ }
+
+ // skip ".."
+ lpchSrc += 2;
+ }
+ else // Everything else
+ {
+ // Just copy it.
+ int cchRemainingBuffer = MAX_PATH - (lpszDst - lpchDst);
+ StringCchCopyNW(lpchDst, cchRemainingBuffer, lpchSrc, cchPC);
+ lpchDst += cchPC - 1;
+ lpchSrc += cchPC - 1;
+ }
+
+ // Keep everything nice and tidy.
+ *lpchDst = W('\0');
+ }
+
+ // Check for weirdo root directory stuff.
+ NearRootFixups(lpszDst, fUNC);
+
+ return TRUE;
+}
+
+// Modifies:
+// pszRoot
+//
+// Returns:
+// TRUE if a drive root was found
+// FALSE otherwise
+//
+STDAPI_(BOOL) PathStripToRootW(LPWSTR pszRoot)
+{
+ RIPMSG(pszRoot && IS_VALID_STRING_PTR(pszRoot, -1), "PathStripToRoot: caller passed bad pszRoot");
+
+ if (pszRoot)
+ {
+ while (!PathIsRootW(pszRoot))
+ {
+ if (!PathRemoveFileSpecW(pszRoot))
+ {
+ // If we didn't strip anything off,
+ // must be current drive
+ return FALSE;
+ }
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+
+/*----------------------------------------------------------
+Purpose: Concatenate lpszDir and lpszFile into a properly formed
+ path and canonicalize any relative path pieces.
+
+ lpszDest and lpszFile can be the same buffer
+ lpszDest and lpszDir can be the same buffer
+
+Returns: pointer to lpszDest
+*/
+STDAPI_(LPWSTR) PathCombineW(LPWSTR lpszDest, LPCWSTR lpszDir, LPCWSTR lpszFile)
+{
+#ifdef DEBUG
+ RIPMSG(lpszDest && IS_VALID_WRITE_BUFFER(lpszDest, TCHAR, MAX_LONGPATH), "PathCombine: caller passed bad lpszDest");
+ RIPMSG(!lpszDir || IS_VALID_STRING_PTR(lpszDir, -1), "PathCombine: caller passed bad lpszDir");
+ RIPMSG(!lpszFile || IS_VALID_STRING_PTR(lpszFile, -1), "PathCombine: caller passed bad lpszFile");
+ RIPMSG(lpszDir || lpszFile, "PathCombine: caller neglected to pass lpszDir or lpszFile");
+#endif // DEBUG
+
+
+ if (lpszDest)
+ {
+ TCHAR szTemp[MAX_LONGPATH];
+ LPWSTR pszT;
+
+ *szTemp = W('\0');
+
+ if (lpszDir && *lpszDir)
+ {
+ if (!lpszFile || *lpszFile==W('\0'))
+ {
+ // lpszFile is empty
+ StringCchCopyNW(szTemp, ARRAYSIZE(szTemp), lpszDir, ARRAYSIZE(szTemp));
+ }
+ else if (PathIsRelativeW(lpszFile))
+ {
+ StringCchCopyNW(szTemp, ARRAYSIZE(szTemp), lpszDir, ARRAYSIZE(szTemp));
+ pszT = PathAddBackslashW(szTemp);
+ if (pszT)
+ {
+ size_t iRemaining = ARRAYSIZE(szTemp) - (pszT - szTemp);
+
+ if (wcslen(lpszFile) < iRemaining)
+ {
+ StringCchCopyNW(pszT, iRemaining, lpszFile, iRemaining);
+ }
+ else
+ {
+ *szTemp = W('\0');
+ }
+ }
+ else
+ {
+ *szTemp = W('\0');
+ }
+ }
+ else if (IsPathSeparator(*lpszFile) && !PathIsUNCW(lpszFile))
+ {
+ StringCchCopyNW(szTemp, ARRAYSIZE(szTemp), lpszDir, ARRAYSIZE(szTemp));
+ // FEATURE: Note that we do not check that an actual root is returned;
+ // it is assumed that we are given valid parameters
+ PathStripToRootW(szTemp);
+
+ pszT = PathAddBackslashW(szTemp);
+ if (pszT)
+ {
+ // Skip the backslash when copying
+ // Note: We don't support strings longer than 4GB, but that's
+ // okay because we already fail at MAX_PATH
+ int iRemaining = (int)(ARRAYSIZE(szTemp) - (pszT - szTemp));
+ StringCchCopyNW(pszT, iRemaining, lpszFile+1, iRemaining);
+ }
+ else
+ {
+ *szTemp = W('\0');
+ }
+ }
+ else
+ {
+ // already fully qualified file part
+ StringCchCopyNW(szTemp, ARRAYSIZE(szTemp), lpszFile, ARRAYSIZE(szTemp));
+ }
+ }
+ else if (lpszFile && *lpszFile)
+ {
+ // no dir just use file.
+ StringCchCopyNW(szTemp, ARRAYSIZE(szTemp), lpszFile, ARRAYSIZE(szTemp));
+ }
+
+ //
+ // if szTemp has something in it we succeeded. Also if szTemp is empty and
+ // the input strings are empty we succeed and PathCanonicalize() will
+ // return "\"
+ //
+ if (*szTemp || ((lpszDir || lpszFile) && !((lpszDir && *lpszDir) || (lpszFile && *lpszFile))))
+ {
+ PathCanonicalizeW(lpszDest, szTemp); // this deals with .. and . stuff
+ // returns "\" on empty szTemp
+ }
+ else
+ {
+ *lpszDest = W('\0'); // set output buffer to empty string.
+ lpszDest = NULL; // return failure.
+ }
+ }
+
+ return lpszDest;
+}
+
+// add a backslash to a qualified path
+//
+// in:
+// lpszPath path (A:, C:\foo, etc)
+//
+// out:
+// lpszPath A:\, C:\foo\ ;
+//
+// returns:
+// pointer to the NULL that terminates the path
+//
+STDAPI_(LPWSTR) PathAddBackslashW(LPWSTR lpszPath)
+{
+ LPWSTR lpszRet = NULL;
+
+ RIPMSG(lpszPath && IS_VALID_STRING_PTR(lpszPath, -1), "PathAddBackslash: caller passed bad lpszPath");
+
+ if (lpszPath)
+ {
+ size_t ichPath = wcslen(lpszPath);
+ LPWSTR lpszEnd = lpszPath + ichPath;
+
+ if (ichPath)
+ {
+
+ // Get the end of the source directory
+ switch(*(lpszEnd-1))
+ {
+ case CH_SLASH:
+ case CH_WHACK:
+ break;
+
+ default:
+ // try to keep us from tromping over MAX_PATH in size.
+ // if we find these cases, return NULL. Note: We need to
+ // check those places that call us to handle their GP fault
+ // if they try to use the NULL!
+ if (ichPath >= (MAX_PATH - 2)) // -2 because ichPath doesn't include NULL, and we're adding a CH_WHACK.
+ {
+ return(NULL);
+ }
+
+ *lpszEnd++ = CH_WHACK;
+ *lpszEnd = W('\0');
+ }
+ }
+
+ lpszRet = lpszEnd;
+ }
+
+ return lpszRet;
+}
+
+
+
+
+//---------------------------------------------------------------------------
+// Returns TRUE if the given string is a UNC path.
+//
+// TRUE
+// "\\foo\bar"
+// "\\foo" <- careful
+// "\\"
+// FALSE
+// "\foo"
+// "foo"
+// "c:\foo"
+//
+//
+STDAPI_(BOOL) PathIsUNCW(LPCWSTR pszPath)
+{
+ RIPMSG(pszPath && IS_VALID_STRING_PTR(pszPath, -1), "PathIsUNC: caller passed bad pszPath");
+
+ if (pszPath)
+ {
+ return DBL_BSLASH(pszPath);
+ }
+ return FALSE;
+}
+
+
+
+
+
+//---------------------------------------------------------------------------
+// Return TRUE if the path isn't absoulte.
+//
+// TRUE
+// "foo.exe"
+// ".\foo.exe"
+// "..\boo\foo.exe"
+//
+// FALSE
+// "\foo"
+// "c:bar" <- be careful
+// "c:\bar"
+// "\\foo\bar"
+//
+STDAPI_(BOOL) PathIsRelativeW(LPCWSTR lpszPath)
+{
+ RIPMSG(lpszPath && IS_VALID_STRING_PTR(lpszPath, -1), "PathIsRelative: caller passed bad lpszPath");
+
+ if (!lpszPath || *lpszPath == 0)
+ {
+ // The NULL path is assumed relative
+ return TRUE;
+ }
+
+ if (IsPathSeparator(lpszPath[0]))
+ {
+ // Does it begin with a slash ?
+ return FALSE;
+ }
+ else if (lpszPath[1] == W(':'))
+ {
+ // Does it begin with a drive and a colon ?
+ return FALSE;
+ }
+ else
+ {
+ // Probably relative.
+ return TRUE;
+ }
+}
+
+// find the next slash or null terminator
+LPWSTR StrSlash(LPCWSTR psz)
+{
+ for (; *psz && !IsPathSeparator(*psz); psz++);
+
+ // Cast to a non-const string to mimic the behavior
+ // of wcschr/StrChr and strchr.
+ return (LPWSTR) psz;
+}
+
+
+
diff --git a/src/coreclr/palrt/shlwapip.h b/src/coreclr/palrt/shlwapip.h
new file mode 100644
index 00000000000..a34c923c346
--- /dev/null
+++ b/src/coreclr/palrt/shlwapip.h
@@ -0,0 +1,37 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+//
+// ===========================================================================
+// File: shlwapi.h
+//
+// Header for ported shlwapi stuff
+// ===========================================================================
+
+#ifndef SHLWAPIP_H_INCLUDED
+#define SHLWAPIP_H_INCLUDED
+
+#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
+#define SIZECHARS(sz) (sizeof(sz)/sizeof(sz[0]))
+
+#define SIZEOF(x) sizeof(x)
+#define PRIVATE
+#define PUBLIC
+#ifndef ASSERT
+#define ASSERT _ASSERTE
+#endif
+#define AssertMsg(f,m) _ASSERTE(f)
+#define RIP(f) _ASSERTE(f)
+#define RIPMSG(f,m) _ASSERTE(f)
+
+#define IS_VALID_READ_BUFFER(p, t, n) (p != NULL)
+#define IS_VALID_WRITE_BUFFER(p, t, n) (p != NULL)
+
+#define IS_VALID_READ_PTR(p, t) IS_VALID_READ_BUFFER(p, t, 1)
+#define IS_VALID_WRITE_PTR(p, t) IS_VALID_WRITE_BUFFER(p, t, 1)
+
+#define IS_VALID_STRING_PTR(p, c) (p != NULL)
+#define IS_VALID_STRING_PTRW(p, c) (p != NULL)
+
+#endif // ! SHLWAPIP_H_INCLUDED
diff --git a/src/coreclr/palrt/variant.cpp b/src/coreclr/palrt/variant.cpp
new file mode 100644
index 00000000000..f96d1defdd1
--- /dev/null
+++ b/src/coreclr/palrt/variant.cpp
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+//
+// ===========================================================================
+// File: variant.cpp
+//
+// PALRT variant conversion functions
+// ===========================================================================
+
+#include "common.h"
+
+/***
+*PUBLIC void VariantInit(VARIANT*)
+*Purpose:
+* Initialize the given VARIANT to VT_EMPTY.
+*
+*Entry:
+* None
+*
+*Exit:
+* return value = void
+*
+* pvarg = pointer to initialized VARIANT
+*
+***********************************************************************/
+STDAPI_(void)
+VariantInit(VARIANT FAR* pvarg)
+{
+ V_VT(pvarg) = VT_EMPTY;
+}