/**
 * WinPR: Windows Portable Runtime
 * Path Functions
 *
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <winpr/config.h>

#include <winpr/crt.h>
#include <winpr/tchar.h>

#include <winpr/path.h>
#include <winpr/file.h>

#define PATH_SLASH_CHR '/'
#define PATH_SLASH_STR "/"

#define PATH_BACKSLASH_CHR '\\'
#define PATH_BACKSLASH_STR "\\"

#ifdef _WIN32
#define PATH_SLASH_STR_W L"/"
#define PATH_BACKSLASH_STR_W L"\\"
#else
#define PATH_SLASH_STR_W \
	{                    \
		'/', '\0'        \
	}
#define PATH_BACKSLASH_STR_W \
	{                        \
		'\\', '\0'           \
	}
#endif

#ifdef _WIN32
#define PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
#define PATH_SEPARATOR_STR PATH_BACKSLASH_STR
#define PATH_SEPARATOR_STR_W PATH_BACKSLASH_STR_W
#else
#define PATH_SEPARATOR_CHR PATH_SLASH_CHR
#define PATH_SEPARATOR_STR PATH_SLASH_STR
#define PATH_SEPARATOR_STR_W PATH_SLASH_STR_W
#endif

#define SHARED_LIBRARY_EXT_DLL "dll"
#define SHARED_LIBRARY_EXT_SO "so"
#define SHARED_LIBRARY_EXT_DYLIB "dylib"

#ifdef _WIN32
#define SHARED_LIBRARY_EXT SHARED_LIBRARY_EXT_DLL
#elif defined(__APPLE__)
#define SHARED_LIBRARY_EXT SHARED_LIBRARY_EXT_DYLIB
#else
#define SHARED_LIBRARY_EXT SHARED_LIBRARY_EXT_SO
#endif

#include "../log.h"
#define TAG WINPR_TAG("path")

/*
 * PathCchAddBackslash
 */

/* Windows-style Paths */

#define DEFINE_UNICODE FALSE
#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
#define PATH_CCH_ADD_SEPARATOR PathCchAddBackslashA
#include "include/PathCchAddSeparator.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef PATH_CCH_ADD_SEPARATOR

#define DEFINE_UNICODE TRUE
#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
#define PATH_CCH_ADD_SEPARATOR PathCchAddBackslashW
#include "include/PathCchAddSeparator.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef PATH_CCH_ADD_SEPARATOR

/* Unix-style Paths */

#define DEFINE_UNICODE FALSE
#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR
#define PATH_CCH_ADD_SEPARATOR PathCchAddSlashA
#include "include/PathCchAddSeparator.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef PATH_CCH_ADD_SEPARATOR

#define DEFINE_UNICODE TRUE
#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR
#define PATH_CCH_ADD_SEPARATOR PathCchAddSlashW
#include "include/PathCchAddSeparator.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef PATH_CCH_ADD_SEPARATOR

/* Native-style Paths */

#define DEFINE_UNICODE FALSE
#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR
#define PATH_CCH_ADD_SEPARATOR PathCchAddSeparatorA
#include "include/PathCchAddSeparator.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef PATH_CCH_ADD_SEPARATOR

#define DEFINE_UNICODE TRUE
#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR
#define PATH_CCH_ADD_SEPARATOR PathCchAddSeparatorW
#include "include/PathCchAddSeparator.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef PATH_CCH_ADD_SEPARATOR

/*
 * PathCchRemoveBackslash
 */

HRESULT PathCchRemoveBackslashA(PSTR pszPath, size_t cchPath)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

HRESULT PathCchRemoveBackslashW(PWSTR pszPath, size_t cchPath)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

/*
 * PathCchAddBackslashEx
 */

/* Windows-style Paths */

#define DEFINE_UNICODE FALSE
#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddBackslashExA
#include "include/PathCchAddSeparatorEx.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef PATH_CCH_ADD_SEPARATOR_EX

#define DEFINE_UNICODE TRUE
#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddBackslashExW
#include "include/PathCchAddSeparatorEx.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef PATH_CCH_ADD_SEPARATOR_EX

/* Unix-style Paths */

#define DEFINE_UNICODE FALSE
#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR
#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddSlashExA
#include "include/PathCchAddSeparatorEx.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef PATH_CCH_ADD_SEPARATOR_EX

#define DEFINE_UNICODE TRUE
#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR
#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddSlashExW
#include "include/PathCchAddSeparatorEx.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef PATH_CCH_ADD_SEPARATOR_EX

/* Native-style Paths */

#define DEFINE_UNICODE FALSE
#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR
#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddSeparatorExA
#include "include/PathCchAddSeparatorEx.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef PATH_CCH_ADD_SEPARATOR_EX

#define DEFINE_UNICODE TRUE
#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR
#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddSeparatorExW
#include "include/PathCchAddSeparatorEx.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef PATH_CCH_ADD_SEPARATOR_EX

HRESULT PathCchRemoveBackslashExA(PSTR pszPath, size_t cchPath, PSTR* ppszEnd,
                                  size_t* pcchRemaining)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

HRESULT PathCchRemoveBackslashExW(PWSTR pszPath, size_t cchPath, PWSTR* ppszEnd,
                                  size_t* pcchRemaining)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

/*
 * PathCchAddExtension
 */

/* Windows-style Paths */

#define DEFINE_UNICODE FALSE
#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
#define PATH_CCH_ADD_EXTENSION PathCchAddExtensionA
#include "include/PathCchAddExtension.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef PATH_CCH_ADD_EXTENSION

#define DEFINE_UNICODE TRUE
#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
#define PATH_CCH_ADD_EXTENSION PathCchAddExtensionW
#include "include/PathCchAddExtension.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef PATH_CCH_ADD_EXTENSION

/* Unix-style Paths */

#define DEFINE_UNICODE FALSE
#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR
#define PATH_CCH_ADD_EXTENSION UnixPathCchAddExtensionA
#include "include/PathCchAddExtension.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef PATH_CCH_ADD_EXTENSION

#define DEFINE_UNICODE TRUE
#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR
#define PATH_CCH_ADD_EXTENSION UnixPathCchAddExtensionW
#include "include/PathCchAddExtension.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef PATH_CCH_ADD_EXTENSION

/* Native-style Paths */

#define DEFINE_UNICODE FALSE
#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR
#define PATH_CCH_ADD_EXTENSION NativePathCchAddExtensionA
#include "include/PathCchAddExtension.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef PATH_CCH_ADD_EXTENSION

#define DEFINE_UNICODE TRUE
#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR
#define PATH_CCH_ADD_EXTENSION NativePathCchAddExtensionW
#include "include/PathCchAddExtension.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef PATH_CCH_ADD_EXTENSION

/*
 * PathCchAppend
 */

/* Windows-style Paths */

#define DEFINE_UNICODE FALSE
#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
#define CUR_PATH_SEPARATOR_STR PATH_BACKSLASH_STR
#define PATH_CCH_APPEND PathCchAppendA
#include "include/PathCchAppend.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef CUR_PATH_SEPARATOR_STR
#undef PATH_CCH_APPEND

#define DEFINE_UNICODE TRUE
#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
#define CUR_PATH_SEPARATOR_STR PATH_BACKSLASH_STR_W
#define PATH_CCH_APPEND PathCchAppendW
#include "include/PathCchAppend.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef CUR_PATH_SEPARATOR_STR
#undef PATH_CCH_APPEND

/* Unix-style Paths */

#define DEFINE_UNICODE FALSE
#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR
#define CUR_PATH_SEPARATOR_STR PATH_SLASH_STR
#define PATH_CCH_APPEND UnixPathCchAppendA
#include "include/PathCchAppend.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef CUR_PATH_SEPARATOR_STR
#undef PATH_CCH_APPEND

#define DEFINE_UNICODE TRUE
#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR
#define CUR_PATH_SEPARATOR_STR PATH_SLASH_STR_W
#define PATH_CCH_APPEND UnixPathCchAppendW
#include "include/PathCchAppend.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef CUR_PATH_SEPARATOR_STR
#undef PATH_CCH_APPEND

/* Native-style Paths */

#define DEFINE_UNICODE FALSE
#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR
#define CUR_PATH_SEPARATOR_STR PATH_SEPARATOR_STR
#define PATH_CCH_APPEND NativePathCchAppendA
#include "include/PathCchAppend.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef CUR_PATH_SEPARATOR_STR
#undef PATH_CCH_APPEND

#define DEFINE_UNICODE TRUE
#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR
#define CUR_PATH_SEPARATOR_STR PATH_SEPARATOR_STR_W
#define PATH_CCH_APPEND NativePathCchAppendW
#include "include/PathCchAppend.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef CUR_PATH_SEPARATOR_STR
#undef PATH_CCH_APPEND

/*
 * PathCchAppendEx
 */

HRESULT PathCchAppendExA(PSTR pszPath, size_t cchPath, PCSTR pszMore, unsigned long dwFlags)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

HRESULT PathCchAppendExW(PWSTR pszPath, size_t cchPath, PCWSTR pszMore, unsigned long dwFlags)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

/*
 * PathCchCanonicalize
 */

HRESULT PathCchCanonicalizeA(PSTR pszPathOut, size_t cchPathOut, PCSTR pszPathIn)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

HRESULT PathCchCanonicalizeW(PWSTR pszPathOut, size_t cchPathOut, PCWSTR pszPathIn)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

/*
 * PathCchCanonicalizeEx
 */

HRESULT PathCchCanonicalizeExA(PSTR pszPathOut, size_t cchPathOut, PCSTR pszPathIn,
                               unsigned long dwFlags)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

HRESULT PathCchCanonicalizeExW(PWSTR pszPathOut, size_t cchPathOut, PCWSTR pszPathIn,
                               unsigned long dwFlags)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

/*
 * PathAllocCanonicalize
 */

HRESULT PathAllocCanonicalizeA(PCSTR pszPathIn, unsigned long dwFlags, PSTR* ppszPathOut)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

HRESULT PathAllocCanonicalizeW(PCWSTR pszPathIn, unsigned long dwFlags, PWSTR* ppszPathOut)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

/*
 * PathCchCombine
 */

HRESULT PathCchCombineA(PSTR pszPathOut, size_t cchPathOut, PCSTR pszPathIn, PCSTR pszMore)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

HRESULT PathCchCombineW(PWSTR pszPathOut, size_t cchPathOut, PCWSTR pszPathIn, PCWSTR pszMore)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

/*
 * PathCchCombineEx
 */

HRESULT PathCchCombineExA(PSTR pszPathOut, size_t cchPathOut, PCSTR pszPathIn, PCSTR pszMore,
                          unsigned long dwFlags)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

HRESULT PathCchCombineExW(PWSTR pszPathOut, size_t cchPathOut, PCWSTR pszPathIn, PCWSTR pszMore,
                          unsigned long dwFlags)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

/*
 * PathAllocCombine
 */

/* Windows-style Paths */

#define DEFINE_UNICODE FALSE
#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
#define CUR_PATH_SEPARATOR_STR PATH_BACKSLASH_STR
#define PATH_ALLOC_COMBINE PathAllocCombineA
#include "include/PathAllocCombine.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef CUR_PATH_SEPARATOR_STR
#undef PATH_ALLOC_COMBINE

#define DEFINE_UNICODE TRUE
#define CUR_PATH_SEPARATOR_CHR PATH_BACKSLASH_CHR
#define CUR_PATH_SEPARATOR_STR PATH_BACKSLASH_STR_W
#define PATH_ALLOC_COMBINE PathAllocCombineW
#include "include/PathAllocCombine.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef CUR_PATH_SEPARATOR_STR
#undef PATH_ALLOC_COMBINE

/* Unix-style Paths */

#define DEFINE_UNICODE FALSE
#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR
#define CUR_PATH_SEPARATOR_STR PATH_SLASH_STR
#define PATH_ALLOC_COMBINE UnixPathAllocCombineA
#include "include/PathAllocCombine.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef CUR_PATH_SEPARATOR_STR
#undef PATH_ALLOC_COMBINE

#define DEFINE_UNICODE TRUE
#define CUR_PATH_SEPARATOR_CHR PATH_SLASH_CHR
#define CUR_PATH_SEPARATOR_STR PATH_SLASH_STR_W
#define PATH_ALLOC_COMBINE UnixPathAllocCombineW
#include "include/PathAllocCombine.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef CUR_PATH_SEPARATOR_STR
#undef PATH_ALLOC_COMBINE

/* Native-style Paths */

#define DEFINE_UNICODE FALSE
#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR
#define CUR_PATH_SEPARATOR_STR PATH_SEPARATOR_STR
#define PATH_ALLOC_COMBINE NativePathAllocCombineA
#include "include/PathAllocCombine.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef CUR_PATH_SEPARATOR_STR
#undef PATH_ALLOC_COMBINE

#define DEFINE_UNICODE TRUE
#define CUR_PATH_SEPARATOR_CHR PATH_SEPARATOR_CHR
#define CUR_PATH_SEPARATOR_STR PATH_SEPARATOR_STR_W
#define PATH_ALLOC_COMBINE NativePathAllocCombineW
#include "include/PathAllocCombine.c"
#undef DEFINE_UNICODE
#undef CUR_PATH_SEPARATOR_CHR
#undef CUR_PATH_SEPARATOR_STR
#undef PATH_ALLOC_COMBINE

/**
 * PathCchFindExtension
 */

HRESULT PathCchFindExtensionA(PCSTR pszPath, size_t cchPath, PCSTR* ppszExt)
{
	const char* p = (const char*)pszPath;

	if (!pszPath || !cchPath || !ppszExt)
		return E_INVALIDARG;

	/* find end of string */

	while (*p && --cchPath)
	{
		p++;
	}

	if (*p)
	{
		/* pszPath is not null terminated within the cchPath range */
		return E_INVALIDARG;
	}

	/* If no extension is found, ppszExt must point to the string's terminating null */
	*ppszExt = p;

	/* search backwards for '.' */

	while (p > pszPath)
	{
		if (*p == '.')
		{
			*ppszExt = (PCSTR)p;
			break;
		}

		if ((*p == '\\') || (*p == '/') || (*p == ':'))
			break;

		p--;
	}

	return S_OK;
}

HRESULT PathCchFindExtensionW(PCWSTR pszPath, size_t cchPath, PCWSTR* ppszExt)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

/**
 * PathCchRenameExtension
 */

HRESULT PathCchRenameExtensionA(PSTR pszPath, size_t cchPath, PCSTR pszExt)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

HRESULT PathCchRenameExtensionW(PWSTR pszPath, size_t cchPath, PCWSTR pszExt)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

/**
 * PathCchRemoveExtension
 */

HRESULT PathCchRemoveExtensionA(PSTR pszPath, size_t cchPath)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

HRESULT PathCchRemoveExtensionW(PWSTR pszPath, size_t cchPath)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

/**
 * PathCchIsRoot
 */

BOOL PathCchIsRootA(PCSTR pszPath)
{
	WLog_ERR(TAG, "not implemented");
	return FALSE;
}

BOOL PathCchIsRootW(PCWSTR pszPath)
{
	WLog_ERR(TAG, "not implemented");
	return FALSE;
}

/**
 * PathIsUNCEx
 */

BOOL PathIsUNCExA(PCSTR pszPath, PCSTR* ppszServer)
{
	if (!pszPath)
		return FALSE;

	if ((pszPath[0] == '\\') && (pszPath[1] == '\\'))
	{
		*ppszServer = &pszPath[2];
		return TRUE;
	}

	return FALSE;
}

BOOL PathIsUNCExW(PCWSTR pszPath, PCWSTR* ppszServer)
{
	if (!pszPath)
		return FALSE;

	if ((pszPath[0] == '\\') && (pszPath[1] == '\\'))
	{
		*ppszServer = &pszPath[2];
		return TRUE;
	}

	return FALSE;
}

/**
 * PathCchSkipRoot
 */

HRESULT PathCchSkipRootA(PCSTR pszPath, PCSTR* ppszRootEnd)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

HRESULT PathCchSkipRootW(PCWSTR pszPath, PCWSTR* ppszRootEnd)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

/**
 * PathCchStripToRoot
 */

HRESULT PathCchStripToRootA(PSTR pszPath, size_t cchPath)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

HRESULT PathCchStripToRootW(PWSTR pszPath, size_t cchPath)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

/**
 * PathCchStripPrefix
 */

HRESULT PathCchStripPrefixA(PSTR pszPath, size_t cchPath)
{
	BOOL hasPrefix = 0;

	if (!pszPath)
		return E_INVALIDARG;

	if (cchPath < 4 || cchPath > PATHCCH_MAX_CCH)
		return E_INVALIDARG;

	hasPrefix = ((pszPath[0] == '\\') && (pszPath[1] == '\\') && (pszPath[2] == '?') &&
	             (pszPath[3] == '\\'))
	                ? TRUE
	                : FALSE;

	if (hasPrefix)
	{
		if (cchPath < 6)
			return S_FALSE;

		if (IsCharAlpha(pszPath[4]) && (pszPath[5] == ':')) /* like C: */
		{
			memmove_s(pszPath, cchPath, &pszPath[4], cchPath - 4);
			/* since the passed pszPath must not necessarily be null terminated
			 * and we always have enough space after the strip we can always
			 * ensure the null termination of the stripped result
			 */
			pszPath[cchPath - 4] = 0;
			return S_OK;
		}
	}

	return S_FALSE;
}

HRESULT PathCchStripPrefixW(PWSTR pszPath, size_t cchPath)
{
	BOOL hasPrefix = 0;

	if (!pszPath)
		return E_INVALIDARG;

	if (cchPath < 4 || cchPath > PATHCCH_MAX_CCH)
		return E_INVALIDARG;

	hasPrefix = ((pszPath[0] == '\\') && (pszPath[1] == '\\') && (pszPath[2] == '?') &&
	             (pszPath[3] == '\\'))
	                ? TRUE
	                : FALSE;

	if (hasPrefix)
	{
		int rc = 0;
		if (cchPath < 6)
			return S_FALSE;

		rc = (_wcslen(&pszPath[4]) + 1);
		if ((rc < 0) || ((INT64)cchPath < rc))
			return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);

		if (IsCharAlphaW(pszPath[4]) && (pszPath[5] == L':')) /* like C: */
		{
			wmemmove_s(pszPath, cchPath, &pszPath[4], cchPath - 4);
			/* since the passed pszPath must not necessarily be null terminated
			 * and we always have enough space after the strip we can always
			 * ensure the null termination of the stripped result
			 */
			pszPath[cchPath - 4] = 0;
			return S_OK;
		}
	}

	return S_FALSE;
}

/**
 * PathCchRemoveFileSpec
 */

HRESULT PathCchRemoveFileSpecA(PSTR pszPath, size_t cchPath)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

HRESULT PathCchRemoveFileSpecW(PWSTR pszPath, size_t cchPath)
{
	WLog_ERR(TAG, "not implemented");
	return E_NOTIMPL;
}

/*
 * Path Portability Functions
 */

/**
 * PathCchConvertStyle
 */

HRESULT PathCchConvertStyleA(PSTR pszPath, size_t cchPath, unsigned long dwFlags)
{
	if (dwFlags == PATH_STYLE_WINDOWS)
	{
		for (size_t index = 0; index < cchPath; index++)
		{
			if (pszPath[index] == PATH_SLASH_CHR)
				pszPath[index] = PATH_BACKSLASH_CHR;
		}
	}
	else if (dwFlags == PATH_STYLE_UNIX)
	{
		for (size_t index = 0; index < cchPath; index++)
		{
			if (pszPath[index] == PATH_BACKSLASH_CHR)
				pszPath[index] = PATH_SLASH_CHR;
		}
	}
	else if (dwFlags == PATH_STYLE_NATIVE)
	{
#if (PATH_SEPARATOR_CHR == PATH_BACKSLASH_CHR)
		/* Unix-style to Windows-style */

		for (size_t index = 0; index < cchPath; index++)
		{
			if (pszPath[index] == PATH_SLASH_CHR)
				pszPath[index] = PATH_BACKSLASH_CHR;
		}
#elif (PATH_SEPARATOR_CHR == PATH_SLASH_CHR)
		/* Windows-style to Unix-style */

		for (size_t index = 0; index < cchPath; index++)
		{
			if (pszPath[index] == PATH_BACKSLASH_CHR)
				pszPath[index] = PATH_SLASH_CHR;
		}
#else
		/* Unexpected error */
		return E_FAIL;
#endif
	}
	else
	{
		/* Gangnam style? */
		return E_FAIL;
	}

	return S_OK;
}

HRESULT PathCchConvertStyleW(PWSTR pszPath, size_t cchPath, unsigned long dwFlags)
{
	if (dwFlags == PATH_STYLE_WINDOWS)
	{
		for (size_t index = 0; index < cchPath; index++)
		{
			if (pszPath[index] == PATH_SLASH_CHR)
				pszPath[index] = PATH_BACKSLASH_CHR;
		}
	}
	else if (dwFlags == PATH_STYLE_UNIX)
	{
		for (size_t index = 0; index < cchPath; index++)
		{
			if (pszPath[index] == PATH_BACKSLASH_CHR)
				pszPath[index] = PATH_SLASH_CHR;
		}
	}
	else if (dwFlags == PATH_STYLE_NATIVE)
	{
#if (PATH_SEPARATOR_CHR == PATH_BACKSLASH_CHR)
		{
			/* Unix-style to Windows-style */

			for (size_t index = 0; index < cchPath; index++)
			{
				if (pszPath[index] == PATH_SLASH_CHR)
					pszPath[index] = PATH_BACKSLASH_CHR;
			}
		}
#elif (PATH_SEPARATOR_CHR == PATH_SLASH_CHR)
		{
			/* Windows-style to Unix-style */

			for (size_t index = 0; index < cchPath; index++)
			{
				if (pszPath[index] == PATH_BACKSLASH_CHR)
					pszPath[index] = PATH_SLASH_CHR;
			}
		}
#else
		{
			/* Unexpected error */
			return E_FAIL;
		}
#endif
	}
	else
	{
		/* Gangnam style? */
		return E_FAIL;
	}

	return S_OK;
}

/**
 * PathGetSeparator
 */

char PathGetSeparatorA(unsigned long dwFlags)
{
	char separator = PATH_SEPARATOR_CHR;

	if (!dwFlags)
		dwFlags = PATH_STYLE_NATIVE;

	if (dwFlags == PATH_STYLE_WINDOWS)
		separator = PATH_SEPARATOR_CHR;
	else if (dwFlags == PATH_STYLE_UNIX)
		separator = PATH_SEPARATOR_CHR;
	else if (dwFlags == PATH_STYLE_NATIVE)
		separator = PATH_SEPARATOR_CHR;

	return separator;
}

WCHAR PathGetSeparatorW(unsigned long dwFlags)
{
	union
	{
		WCHAR w;
		char c[2];
	} cnv;

	cnv.c[0] = PATH_SEPARATOR_CHR;
	cnv.c[1] = '\0';

	if (!dwFlags)
		dwFlags = PATH_STYLE_NATIVE;

	if (dwFlags == PATH_STYLE_WINDOWS)
		cnv.c[0] = PATH_SEPARATOR_CHR;
	else if (dwFlags == PATH_STYLE_UNIX)
		cnv.c[0] = PATH_SEPARATOR_CHR;
	else if (dwFlags == PATH_STYLE_NATIVE)
		cnv.c[0] = PATH_SEPARATOR_CHR;

	return cnv.w;
}

/**
 * PathGetSharedLibraryExtension
 */
static const CHAR SharedLibraryExtensionDllA[] = "dll";
static const CHAR SharedLibraryExtensionSoA[] = "so";
static const CHAR SharedLibraryExtensionDylibA[] = "dylib";

static const CHAR SharedLibraryExtensionDotDllA[] = ".dll";
static const CHAR SharedLibraryExtensionDotSoA[] = ".so";
static const CHAR SharedLibraryExtensionDotDylibA[] = ".dylib";
PCSTR PathGetSharedLibraryExtensionA(unsigned long dwFlags)
{
	if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT)
	{
		if (dwFlags & PATH_SHARED_LIB_EXT_WITH_DOT)
		{
			if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DLL)
				return SharedLibraryExtensionDotDllA;

			if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_SO)
				return SharedLibraryExtensionDotSoA;

			if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DYLIB)
				return SharedLibraryExtensionDotDylibA;
		}
		else
		{
			if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DLL)
				return SharedLibraryExtensionDllA;

			if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_SO)
				return SharedLibraryExtensionSoA;

			if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DYLIB)
				return SharedLibraryExtensionDylibA;
		}
	}

	if (dwFlags & PATH_SHARED_LIB_EXT_WITH_DOT)
	{
#ifdef _WIN32
		return SharedLibraryExtensionDotDllA;
#elif defined(__APPLE__)
		if (dwFlags & PATH_SHARED_LIB_EXT_APPLE_SO)
			return SharedLibraryExtensionDotSoA;
		else
			return SharedLibraryExtensionDotDylibA;
#else
		return SharedLibraryExtensionDotSoA;
#endif
	}
	else
	{
#ifdef _WIN32
		return SharedLibraryExtensionDllA;
#elif defined(__APPLE__)
		if (dwFlags & PATH_SHARED_LIB_EXT_APPLE_SO)
			return SharedLibraryExtensionSoA;
		else
			return SharedLibraryExtensionDylibA;
#else
		return SharedLibraryExtensionSoA;
#endif
	}

	return NULL;
}

PCWSTR PathGetSharedLibraryExtensionW(unsigned long dwFlags)
{
	WCHAR buffer[6][16] = { 0 };
	const WCHAR* SharedLibraryExtensionDotDllW = InitializeConstWCharFromUtf8(
	    SharedLibraryExtensionDotDllA, buffer[0], ARRAYSIZE(buffer[0]));
	const WCHAR* SharedLibraryExtensionDotSoW =
	    InitializeConstWCharFromUtf8(SharedLibraryExtensionDotSoA, buffer[1], ARRAYSIZE(buffer[1]));
	const WCHAR* SharedLibraryExtensionDotDylibW = InitializeConstWCharFromUtf8(
	    SharedLibraryExtensionDotDylibA, buffer[2], ARRAYSIZE(buffer[2]));
	const WCHAR* SharedLibraryExtensionDllW =
	    InitializeConstWCharFromUtf8(SharedLibraryExtensionDllA, buffer[3], ARRAYSIZE(buffer[3]));
	const WCHAR* SharedLibraryExtensionSoW =
	    InitializeConstWCharFromUtf8(SharedLibraryExtensionSoA, buffer[4], ARRAYSIZE(buffer[4]));
	const WCHAR* SharedLibraryExtensionDylibW =
	    InitializeConstWCharFromUtf8(SharedLibraryExtensionDylibA, buffer[5], ARRAYSIZE(buffer[5]));

	if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT)
	{
		if (dwFlags & PATH_SHARED_LIB_EXT_WITH_DOT)
		{
			if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DLL)
				return SharedLibraryExtensionDotDllW;

			if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_SO)
				return SharedLibraryExtensionDotSoW;

			if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DYLIB)
				return SharedLibraryExtensionDotDylibW;
		}
		else
		{
			if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DLL)
				return SharedLibraryExtensionDllW;

			if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_SO)
				return SharedLibraryExtensionSoW;

			if (dwFlags & PATH_SHARED_LIB_EXT_EXPLICIT_DYLIB)
				return SharedLibraryExtensionDylibW;
		}
	}

	if (dwFlags & PATH_SHARED_LIB_EXT_WITH_DOT)
	{
#ifdef _WIN32
		return SharedLibraryExtensionDotDllW;
#elif defined(__APPLE__)
		if (dwFlags & PATH_SHARED_LIB_EXT_APPLE_SO)
			return SharedLibraryExtensionDotSoW;
		else
			return SharedLibraryExtensionDotDylibW;
#else
		return SharedLibraryExtensionDotSoW;
#endif
	}
	else
	{
#ifdef _WIN32
		return SharedLibraryExtensionDllW;
#elif defined(__APPLE__)
		if (dwFlags & PATH_SHARED_LIB_EXT_APPLE_SO)
			return SharedLibraryExtensionSoW;
		else
			return SharedLibraryExtensionDylibW;
#else
		return SharedLibraryExtensionSoW;
#endif
	}

	return NULL;
}

const char* GetKnownPathIdString(int id)
{
	switch (id)
	{
		case KNOWN_PATH_HOME:
			return "KNOWN_PATH_HOME";
		case KNOWN_PATH_TEMP:
			return "KNOWN_PATH_TEMP";
		case KNOWN_PATH_XDG_DATA_HOME:
			return "KNOWN_PATH_XDG_DATA_HOME";
		case KNOWN_PATH_XDG_CONFIG_HOME:
			return "KNOWN_PATH_XDG_CONFIG_HOME";
		case KNOWN_PATH_XDG_CACHE_HOME:
			return "KNOWN_PATH_XDG_CACHE_HOME";
		case KNOWN_PATH_XDG_RUNTIME_DIR:
			return "KNOWN_PATH_XDG_RUNTIME_DIR";
		default:
			return "KNOWN_PATH_UNKNOWN_ID";
	}
}

static WCHAR* concat(const WCHAR* path, size_t pathlen, const WCHAR* name, size_t namelen)
{
	WCHAR* str = calloc(pathlen + namelen + 1, sizeof(WCHAR));
	if (!str)
		return NULL;

	_wcsncat(str, path, pathlen);
	_wcsncat(str, name, namelen);
	return str;
}

BOOL winpr_RemoveDirectory_RecursiveA(LPCSTR lpPathName)
{
	WCHAR* name = ConvertUtf8ToWCharAlloc(lpPathName, NULL);
	if (!name)
		return FALSE;
	const BOOL rc = winpr_RemoveDirectory_RecursiveW(name);
	free(name);
	return rc;
}

BOOL winpr_RemoveDirectory_RecursiveW(LPCWSTR lpPathName)
{
	BOOL ret = FALSE;

	if (!lpPathName)
		return FALSE;

	const size_t pathnamelen = _wcslen(lpPathName);
	const size_t path_slash_len = pathnamelen + 3;
	WCHAR* path_slash = calloc(pathnamelen + 4, sizeof(WCHAR));
	if (!path_slash)
		return FALSE;
	_wcsncat(path_slash, lpPathName, pathnamelen);

	WCHAR starbuffer[8] = { 0 };
	const WCHAR* star = InitializeConstWCharFromUtf8("*", starbuffer, ARRAYSIZE(starbuffer));
	const HRESULT hr = NativePathCchAppendW(path_slash, path_slash_len, star);
	if (FAILED(hr))
		goto fail;

	WIN32_FIND_DATAW findFileData = { 0 };
	HANDLE dir = FindFirstFileW(path_slash, &findFileData);

	if (dir == INVALID_HANDLE_VALUE)
		goto fail;

	ret = TRUE;
	path_slash[path_slash_len - 1] = '\0'; /* remove trailing '*' */
	do
	{
		const size_t len = _wcsnlen(findFileData.cFileName, ARRAYSIZE(findFileData.cFileName));

		if ((len == 1 && findFileData.cFileName[0] == '.') ||
		    (len == 2 && findFileData.cFileName[0] == '.' && findFileData.cFileName[1] == '.'))
		{
			continue;
		}

		WCHAR* fullpath = concat(path_slash, path_slash_len, findFileData.cFileName, len);
		if (!fullpath)
			goto fail;

		if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			ret = winpr_RemoveDirectory_RecursiveW(fullpath);
		else
			ret = DeleteFileW(fullpath);

		free(fullpath);

		if (!ret)
			break;
	} while (ret && FindNextFileW(dir, &findFileData) != 0);

	FindClose(dir);

	if (ret)
	{
		if (!RemoveDirectoryW(lpPathName))
			ret = FALSE;
	}

fail:
	free(path_slash);
	return ret;
}
