diff options
Diffstat (limited to 'winpr/libwinpr/path')
36 files changed, 3887 insertions, 0 deletions
diff --git a/winpr/libwinpr/path/CMakeLists.txt b/winpr/libwinpr/path/CMakeLists.txt new file mode 100644 index 0000000..1e4ed92 --- /dev/null +++ b/winpr/libwinpr/path/CMakeLists.txt @@ -0,0 +1,26 @@ +# WinPR: Windows Portable Runtime +# libwinpr-path cmake build script +# +# 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. + +winpr_module_add(path.c shell.c) + +if (IOS) + winpr_module_add(shell_ios.m) +endif (IOS) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/path/ModuleOptions.cmake b/winpr/libwinpr/path/ModuleOptions.cmake new file mode 100644 index 0000000..b330a21 --- /dev/null +++ b/winpr/libwinpr/path/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "1") +set(MINWIN_MINOR_VERSION "0") +set(MINWIN_SHORT_NAME "path") +set(MINWIN_LONG_NAME "Path Functions") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/path/include/PathAllocCombine.c b/winpr/libwinpr/path/include/PathAllocCombine.c new file mode 100644 index 0000000..abdbd29 --- /dev/null +++ b/winpr/libwinpr/path/include/PathAllocCombine.c @@ -0,0 +1,180 @@ + +/* +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR '\\' +#define CUR_PATH_SEPARATOR_STR "\\" +#define PATH_ALLOC_COMBINE PathAllocCombineA +*/ + +/** + * FIXME: These implementations of the PathAllocCombine functions have + * several issues: + * - pszPathIn or pszMore may be NULL (but not both) + * - no check if pszMore is fully qualified (if so, it must be directly + * copied to the output buffer without being combined with pszPathIn. + * - if pszMore begins with a _single_ backslash it must be combined with + * only the root of the path pointed to by pszPathIn and there's no code + * to extract the root of pszPathIn. + * - the function will crash with some short string lengths of the parameters + */ + +#if DEFINE_UNICODE + +HRESULT PATH_ALLOC_COMBINE(PCWSTR pszPathIn, PCWSTR pszMore, unsigned long dwFlags, + PWSTR* ppszPathOut) +{ + PWSTR pszPathOut; + BOOL backslashIn; + BOOL backslashMore; + size_t pszMoreLength; + size_t pszPathInLength; + size_t pszPathOutLength; + WLog_WARN(TAG, "has known bugs and needs fixing."); + + if (!ppszPathOut) + return E_INVALIDARG; + + if (!pszPathIn && !pszMore) + return E_INVALIDARG; + + if (!pszMore) + return E_FAIL; /* valid but not implemented, see top comment */ + + if (!pszPathIn) + return E_FAIL; /* valid but not implemented, see top comment */ + + pszPathInLength = _wcslen(pszPathIn); + pszMoreLength = _wcslen(pszMore); + + /* prevent segfaults - the complete implementation below is buggy */ + if (pszPathInLength < 3) + return E_FAIL; + + backslashIn = (pszPathIn[pszPathInLength - 1] == CUR_PATH_SEPARATOR_CHR) ? TRUE : FALSE; + backslashMore = (pszMore[0] == CUR_PATH_SEPARATOR_CHR) ? TRUE : FALSE; + + if (backslashMore) + { + if ((pszPathIn[1] == ':') && (pszPathIn[2] == CUR_PATH_SEPARATOR_CHR)) + { + const WCHAR colon[] = { ':', '\0' }; + size_t sizeOfBuffer; + pszPathOutLength = sizeof(WCHAR) + pszMoreLength; + sizeOfBuffer = (pszPathOutLength + 1) * sizeof(WCHAR); + pszPathOut = (PWSTR)calloc(sizeOfBuffer, sizeof(WCHAR)); + + if (!pszPathOut) + return E_OUTOFMEMORY; + + _wcsncat(pszPathOut, &pszPathIn[0], 1); + _wcsncat(pszPathOut, colon, ARRAYSIZE(colon)); + _wcsncat(pszPathOut, pszMore, pszMoreLength); + *ppszPathOut = pszPathOut; + return S_OK; + } + } + else + { + const WCHAR sep[] = CUR_PATH_SEPARATOR_STR; + size_t sizeOfBuffer; + pszPathOutLength = pszPathInLength + pszMoreLength; + sizeOfBuffer = (pszPathOutLength + 1) * 2; + pszPathOut = (PWSTR)calloc(sizeOfBuffer, 2); + + if (!pszPathOut) + return E_OUTOFMEMORY; + + _wcsncat(pszPathOut, pszPathIn, pszPathInLength); + if (!backslashIn) + _wcsncat(pszPathOut, sep, ARRAYSIZE(sep)); + _wcsncat(pszPathOut, pszMore, pszMoreLength); + + *ppszPathOut = pszPathOut; + return S_OK; + } + + return E_FAIL; +} + +#else + +HRESULT PATH_ALLOC_COMBINE(PCSTR pszPathIn, PCSTR pszMore, unsigned long dwFlags, PSTR* ppszPathOut) +{ + PSTR pszPathOut; + BOOL backslashIn; + BOOL backslashMore; + int pszMoreLength; + int pszPathInLength; + int pszPathOutLength; + WLog_WARN(TAG, "has known bugs and needs fixing."); + + if (!ppszPathOut) + return E_INVALIDARG; + + if (!pszPathIn && !pszMore) + return E_INVALIDARG; + + if (!pszMore) + return E_FAIL; /* valid but not implemented, see top comment */ + + if (!pszPathIn) + return E_FAIL; /* valid but not implemented, see top comment */ + + pszPathInLength = strlen(pszPathIn); + pszMoreLength = strlen(pszMore); + + /* prevent segfaults - the complete implementation below is buggy */ + if (pszPathInLength < 3) + return E_FAIL; + + backslashIn = (pszPathIn[pszPathInLength - 1] == CUR_PATH_SEPARATOR_CHR) ? TRUE : FALSE; + backslashMore = (pszMore[0] == CUR_PATH_SEPARATOR_CHR) ? TRUE : FALSE; + + if (backslashMore) + { + if ((pszPathIn[1] == ':') && (pszPathIn[2] == CUR_PATH_SEPARATOR_CHR)) + { + size_t sizeOfBuffer; + pszPathOutLength = 2 + pszMoreLength; + sizeOfBuffer = (pszPathOutLength + 1) * 2; + pszPathOut = (PSTR)calloc(sizeOfBuffer, 2); + + if (!pszPathOut) + return E_OUTOFMEMORY; + + sprintf_s(pszPathOut, sizeOfBuffer, "%c:%s", pszPathIn[0], pszMore); + *ppszPathOut = pszPathOut; + return S_OK; + } + } + else + { + size_t sizeOfBuffer; + pszPathOutLength = pszPathInLength + pszMoreLength; + sizeOfBuffer = (pszPathOutLength + 1) * 2; + pszPathOut = (PSTR)calloc(sizeOfBuffer, 2); + + if (!pszPathOut) + return E_OUTOFMEMORY; + + if (backslashIn) + sprintf_s(pszPathOut, sizeOfBuffer, "%s%s", pszPathIn, pszMore); + else + sprintf_s(pszPathOut, sizeOfBuffer, "%s" CUR_PATH_SEPARATOR_STR "%s", pszPathIn, + pszMore); + + *ppszPathOut = pszPathOut; + return S_OK; + } + + return E_FAIL; +} + +#endif + +/* +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef CUR_PATH_SEPARATOR_STR +#undef PATH_ALLOC_COMBINE +*/ diff --git a/winpr/libwinpr/path/include/PathCchAddExtension.c b/winpr/libwinpr/path/include/PathCchAddExtension.c new file mode 100644 index 0000000..498cfab --- /dev/null +++ b/winpr/libwinpr/path/include/PathCchAddExtension.c @@ -0,0 +1,101 @@ + +/* +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR '\\' +#define PATH_CCH_ADD_EXTENSION PathCchAddExtensionA +*/ + +#if DEFINE_UNICODE + +HRESULT PATH_CCH_ADD_EXTENSION(PWSTR pszPath, size_t cchPath, PCWSTR pszExt) +{ + LPWSTR pDot; + BOOL bExtDot; + LPWSTR pBackslash; + size_t pszExtLength; + size_t pszPathLength; + + if (!pszPath) + return E_INVALIDARG; + + if (!pszExt) + return E_INVALIDARG; + + pszExtLength = _wcslen(pszExt); + pszPathLength = _wcslen(pszPath); + bExtDot = (pszExt[0] == '.') ? TRUE : FALSE; + + pDot = _wcsrchr(pszPath, '.'); + pBackslash = _wcsrchr(pszPath, CUR_PATH_SEPARATOR_CHR); + + if (pDot && pBackslash) + { + if (pDot > pBackslash) + return S_FALSE; + } + + if (cchPath > pszPathLength + pszExtLength + ((bExtDot) ? 0 : 1)) + { + const WCHAR dot[] = { '.', '\0' }; + WCHAR* ptr = &pszPath[pszPathLength]; + *ptr = '\0'; + + if (!bExtDot) + _wcsncat(ptr, dot, _wcslen(dot)); + _wcsncat(ptr, pszExt, pszExtLength); + + return S_OK; + } + + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); +} + +#else + +HRESULT PATH_CCH_ADD_EXTENSION(PSTR pszPath, size_t cchPath, PCSTR pszExt) +{ + CHAR* pDot; + BOOL bExtDot; + CHAR* pBackslash; + size_t pszExtLength; + size_t pszPathLength; + + if (!pszPath) + return E_INVALIDARG; + + if (!pszExt) + return E_INVALIDARG; + + pszExtLength = strlen(pszExt); + pszPathLength = strlen(pszPath); + bExtDot = (pszExt[0] == '.') ? TRUE : FALSE; + + pDot = strrchr(pszPath, '.'); + pBackslash = strrchr(pszPath, CUR_PATH_SEPARATOR_CHR); + + if (pDot && pBackslash) + { + if (pDot > pBackslash) + return S_FALSE; + } + + if (cchPath > pszPathLength + pszExtLength + ((bExtDot) ? 0 : 1)) + { + if (bExtDot) + sprintf_s(&pszPath[pszPathLength], cchPath - pszPathLength, "%s", pszExt); + else + sprintf_s(&pszPath[pszPathLength], cchPath - pszPathLength, ".%s", pszExt); + + return S_OK; + } + + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); +} + +#endif + +/* +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_EXTENSION +*/ diff --git a/winpr/libwinpr/path/include/PathCchAddSeparator.c b/winpr/libwinpr/path/include/PathCchAddSeparator.c new file mode 100644 index 0000000..0ef391c --- /dev/null +++ b/winpr/libwinpr/path/include/PathCchAddSeparator.c @@ -0,0 +1,64 @@ + +/* +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR '\\' +#define PATH_CCH_ADD_SEPARATOR PathCchAddBackslashA +*/ + +#if DEFINE_UNICODE + +HRESULT PATH_CCH_ADD_SEPARATOR(PWSTR pszPath, size_t cchPath) +{ + size_t pszPathLength; + + if (!pszPath) + return E_INVALIDARG; + + pszPathLength = _wcslen(pszPath); + + if (pszPath[pszPathLength - 1] == CUR_PATH_SEPARATOR_CHR) + return S_FALSE; + + if (cchPath > (pszPathLength + 1)) + { + pszPath[pszPathLength] = CUR_PATH_SEPARATOR_CHR; + pszPath[pszPathLength + 1] = '\0'; + + return S_OK; + } + + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); +} + +#else + +HRESULT PATH_CCH_ADD_SEPARATOR(PSTR pszPath, size_t cchPath) +{ + size_t pszPathLength; + + if (!pszPath) + return E_INVALIDARG; + + pszPathLength = strlen(pszPath); + + if (pszPath[pszPathLength - 1] == CUR_PATH_SEPARATOR_CHR) + return S_FALSE; + + if (cchPath > (pszPathLength + 1)) + { + pszPath[pszPathLength] = CUR_PATH_SEPARATOR_CHR; + pszPath[pszPathLength + 1] = '\0'; + + return S_OK; + } + + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); +} + +#endif + +/* +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_SEPARATOR +*/ diff --git a/winpr/libwinpr/path/include/PathCchAddSeparatorEx.c b/winpr/libwinpr/path/include/PathCchAddSeparatorEx.c new file mode 100644 index 0000000..02832d8 --- /dev/null +++ b/winpr/libwinpr/path/include/PathCchAddSeparatorEx.c @@ -0,0 +1,66 @@ + +/* +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR '\\' +#define PATH_CCH_ADD_SEPARATOR_EX PathCchAddBackslashExA +*/ + +#if DEFINE_UNICODE + +HRESULT PATH_CCH_ADD_SEPARATOR_EX(PWSTR pszPath, size_t cchPath, PWSTR* ppszEnd, + size_t* pcchRemaining) +{ + size_t pszPathLength; + + if (!pszPath) + return E_INVALIDARG; + + pszPathLength = _wcslen(pszPath); + + if (pszPath[pszPathLength - 1] == CUR_PATH_SEPARATOR_CHR) + return S_FALSE; + + if (cchPath > (pszPathLength + 1)) + { + pszPath[pszPathLength] = CUR_PATH_SEPARATOR_CHR; + pszPath[pszPathLength + 1] = '\0'; + + return S_OK; + } + + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); +} + +#else + +HRESULT PATH_CCH_ADD_SEPARATOR_EX(PSTR pszPath, size_t cchPath, PSTR* ppszEnd, + size_t* pcchRemaining) +{ + size_t pszPathLength; + + if (!pszPath) + return E_INVALIDARG; + + pszPathLength = strlen(pszPath); + + if (pszPath[pszPathLength - 1] == CUR_PATH_SEPARATOR_CHR) + return S_FALSE; + + if (cchPath > (pszPathLength + 1)) + { + pszPath[pszPathLength] = CUR_PATH_SEPARATOR_CHR; + pszPath[pszPathLength + 1] = '\0'; + + return S_OK; + } + + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); +} + +#endif + +/* +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef PATH_CCH_ADD_SEPARATOR_EX +*/ diff --git a/winpr/libwinpr/path/include/PathCchAppend.c b/winpr/libwinpr/path/include/PathCchAppend.c new file mode 100644 index 0000000..a4f58cb --- /dev/null +++ b/winpr/libwinpr/path/include/PathCchAppend.c @@ -0,0 +1,131 @@ + +/* +#define DEFINE_UNICODE FALSE +#define CUR_PATH_SEPARATOR_CHR '\\' +#define CUR_PATH_SEPARATOR_STR "\\" +#define PATH_CCH_APPEND PathCchAppendA +*/ + +#if DEFINE_UNICODE + +HRESULT PATH_CCH_APPEND(PWSTR pszPath, size_t cchPath, PCWSTR pszMore) +{ + BOOL pathBackslash; + BOOL moreBackslash; + size_t pszMoreLength; + size_t pszPathLength; + + if (!pszPath) + return E_INVALIDARG; + + if (!pszMore) + return E_INVALIDARG; + + if (cchPath == 0 || cchPath > PATHCCH_MAX_CCH) + return E_INVALIDARG; + + pszMoreLength = _wcslen(pszMore); + pszPathLength = _wcslen(pszPath); + + pathBackslash = (pszPath[pszPathLength - 1] == CUR_PATH_SEPARATOR_CHR) ? TRUE : FALSE; + moreBackslash = (pszMore[0] == CUR_PATH_SEPARATOR_CHR) ? TRUE : FALSE; + + if (pathBackslash && moreBackslash) + { + if ((pszPathLength + pszMoreLength - 1) < cchPath) + { + WCHAR* ptr = &pszPath[pszPathLength]; + *ptr = '\0'; + _wcsncat(ptr, &pszMore[1], _wcslen(&pszMore[1])); + return S_OK; + } + } + else if ((pathBackslash && !moreBackslash) || (!pathBackslash && moreBackslash)) + { + if ((pszPathLength + pszMoreLength) < cchPath) + { + WCHAR* ptr = &pszPath[pszPathLength]; + *ptr = '\0'; + _wcsncat(ptr, pszMore, _wcslen(pszMore)); + return S_OK; + } + } + else if (!pathBackslash && !moreBackslash) + { + if ((pszPathLength + pszMoreLength + 1) < cchPath) + { + const WCHAR sep[] = CUR_PATH_SEPARATOR_STR; + WCHAR* ptr = &pszPath[pszPathLength]; + *ptr = '\0'; + _wcsncat(ptr, sep, _wcslen(sep)); + _wcsncat(ptr, pszMore, _wcslen(pszMore)); + return S_OK; + } + } + + return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE); +} + +#else + +HRESULT PATH_CCH_APPEND(PSTR pszPath, size_t cchPath, PCSTR pszMore) +{ + BOOL pathBackslash = FALSE; + BOOL moreBackslash = FALSE; + size_t pszMoreLength; + size_t pszPathLength; + + if (!pszPath) + return E_INVALIDARG; + + if (!pszMore) + return E_INVALIDARG; + + if (cchPath == 0 || cchPath > PATHCCH_MAX_CCH) + return E_INVALIDARG; + + pszPathLength = strlen(pszPath); + if (pszPathLength > 0) + pathBackslash = (pszPath[pszPathLength - 1] == CUR_PATH_SEPARATOR_CHR) ? TRUE : FALSE; + + pszMoreLength = strlen(pszMore); + if (pszMoreLength > 0) + moreBackslash = (pszMore[0] == CUR_PATH_SEPARATOR_CHR) ? TRUE : FALSE; + + if (pathBackslash && moreBackslash) + { + if ((pszPathLength + pszMoreLength - 1) < cchPath) + { + sprintf_s(&pszPath[pszPathLength], cchPath - pszPathLength, "%s", &pszMore[1]); + return S_OK; + } + } + else if ((pathBackslash && !moreBackslash) || (!pathBackslash && moreBackslash)) + { + if ((pszPathLength + pszMoreLength) < cchPath) + { + sprintf_s(&pszPath[pszPathLength], cchPath - pszPathLength, "%s", pszMore); + return S_OK; + } + } + else if (!pathBackslash && !moreBackslash) + { + if ((pszPathLength + pszMoreLength + 1) < cchPath) + { + sprintf_s(&pszPath[pszPathLength], cchPath - pszPathLength, CUR_PATH_SEPARATOR_STR "%s", + pszMore); + return S_OK; + } + } + + return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE); +} + +#endif + +/* +#undef DEFINE_UNICODE +#undef CUR_PATH_SEPARATOR_CHR +#undef CUR_PATH_SEPARATOR_STR +#undef PATH_CCH_APPEND +*/ diff --git a/winpr/libwinpr/path/path.c b/winpr/libwinpr/path/path.c new file mode 100644 index 0000000..82e6be1 --- /dev/null +++ b/winpr/libwinpr/path/path.c @@ -0,0 +1,1181 @@ +/** + * 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; +} diff --git a/winpr/libwinpr/path/shell.c b/winpr/libwinpr/path/shell.c new file mode 100644 index 0000000..4380a9b --- /dev/null +++ b/winpr/libwinpr/path/shell.c @@ -0,0 +1,821 @@ +/** + * WinPR: Windows Portable Runtime + * Path Functions + * + * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include <winpr/crt.h> +#include <winpr/platform.h> +#include <winpr/file.h> +#include <winpr/tchar.h> +#include <winpr/environment.h> + +#include <winpr/path.h> +#include <winpr/wlog.h> + +#include "../log.h" +#define TAG WINPR_TAG("path.shell") + +#if defined(__IOS__) +#include "shell_ios.h" +#endif + +#if defined(WIN32) +#include <shlobj.h> +#else +#include <errno.h> +#include <dirent.h> +#endif + +static char* GetPath_XDG_CONFIG_HOME(void); +static char* GetPath_XDG_RUNTIME_DIR(void); + +/** + * SHGetKnownFolderPath function: + * http://msdn.microsoft.com/en-us/library/windows/desktop/bb762188/ + */ + +/** + * XDG Base Directory Specification: + * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + */ + +char* GetEnvAlloc(LPCSTR lpName) +{ + DWORD nSize = 0; + DWORD nStatus = 0; + char* env = NULL; + + nSize = GetEnvironmentVariableX(lpName, NULL, 0); + + if (nSize > 0) + { + env = malloc(nSize); + + if (!env) + return NULL; + + nStatus = GetEnvironmentVariableX(lpName, env, nSize); + + if (nStatus != (nSize - 1)) + { + free(env); + return NULL; + } + } + + return env; +} + +static char* GetPath_HOME(void) +{ + char* path = NULL; +#ifdef _WIN32 + path = GetEnvAlloc("UserProfile"); +#elif defined(__IOS__) + path = ios_get_home(); +#else + path = GetEnvAlloc("HOME"); +#endif + return path; +} + +static char* GetPath_TEMP(void) +{ + char* path = NULL; +#ifdef _WIN32 + path = GetEnvAlloc("TEMP"); +#elif defined(__IOS__) + path = ios_get_temp(); +#else + path = GetEnvAlloc("TMPDIR"); + + if (!path) + path = _strdup("/tmp"); + +#endif + return path; +} + +static char* GetPath_XDG_DATA_HOME(void) +{ + char* path = NULL; +#if defined(WIN32) || defined(__IOS__) + path = GetPath_XDG_CONFIG_HOME(); +#else + size_t size = 0; + char* home = NULL; + /** + * There is a single base directory relative to which user-specific data files should be + * written. This directory is defined by the environment variable $XDG_DATA_HOME. + * + * $XDG_DATA_HOME defines the base directory relative to which user specific data files should + * be stored. If $XDG_DATA_HOME is either not set or empty, a default equal to + * $HOME/.local/share should be used. + */ + path = GetEnvAlloc("XDG_DATA_HOME"); + + if (path) + return path; + + home = GetPath_HOME(); + + if (!home) + return NULL; + + size = strlen(home) + strlen("/.local/share") + 1; + path = (char*)malloc(size); + + if (!path) + { + free(home); + return NULL; + } + + sprintf_s(path, size, "%s%s", home, "/.local/share"); + free(home); +#endif + return path; +} + +static char* GetPath_XDG_CONFIG_HOME(void) +{ + char* path = NULL; +#if defined(WIN32) && !defined(_UWP) + path = calloc(MAX_PATH, sizeof(char)); + + if (!path) + return NULL; + + if (FAILED(SHGetFolderPathA(0, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path))) + { + free(path); + return NULL; + } + +#elif defined(__IOS__) + path = ios_get_data(); +#else + size_t size = 0; + char* home = NULL; + /** + * There is a single base directory relative to which user-specific configuration files should + * be written. This directory is defined by the environment variable $XDG_CONFIG_HOME. + * + * $XDG_CONFIG_HOME defines the base directory relative to which user specific configuration + * files should be stored. If $XDG_CONFIG_HOME is either not set or empty, a default equal to + * $HOME/.config should be used. + */ + path = GetEnvAlloc("XDG_CONFIG_HOME"); + + if (path) + return path; + + home = GetPath_HOME(); + + if (!home) + home = GetPath_TEMP(); + + if (!home) + return NULL; + + size = strlen(home) + strlen("/.config") + 1; + path = (char*)malloc(size); + + if (!path) + { + free(home); + return NULL; + } + + sprintf_s(path, size, "%s%s", home, "/.config"); + free(home); +#endif + return path; +} + +static char* GetPath_XDG_CACHE_HOME(void) +{ + char* path = NULL; + char* home = NULL; +#if defined(WIN32) + home = GetPath_XDG_RUNTIME_DIR(); + + if (home) + { + path = GetCombinedPath(home, "cache"); + + if (!winpr_PathFileExists(path)) + if (!CreateDirectoryA(path, NULL)) + path = NULL; + } + + free(home); +#elif defined(__IOS__) + path = ios_get_cache(); +#else + size_t size = 0; + /** + * There is a single base directory relative to which user-specific non-essential (cached) data + * should be written. This directory is defined by the environment variable $XDG_CACHE_HOME. + * + * $XDG_CACHE_HOME defines the base directory relative to which user specific non-essential data + * files should be stored. If $XDG_CACHE_HOME is either not set or empty, a default equal to + * $HOME/.cache should be used. + */ + path = GetEnvAlloc("XDG_CACHE_HOME"); + + if (path) + return path; + + home = GetPath_HOME(); + + if (!home) + return NULL; + + size = strlen(home) + strlen("/.cache") + 1; + path = (char*)malloc(size); + + if (!path) + { + free(home); + return NULL; + } + + sprintf_s(path, size, "%s%s", home, "/.cache"); + free(home); +#endif + return path; +} + +char* GetPath_XDG_RUNTIME_DIR(void) +{ + char* path = NULL; +#if defined(WIN32) && !defined(_UWP) + path = calloc(MAX_PATH, sizeof(char)); + + if (!path) + return NULL; + + if (FAILED(SHGetFolderPathA(0, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path))) + { + free(path); + return NULL; + } + +#else + /** + * There is a single base directory relative to which user-specific runtime files and other file + * objects should be placed. This directory is defined by the environment variable + * $XDG_RUNTIME_DIR. + * + * $XDG_RUNTIME_DIR defines the base directory relative to which user-specific non-essential + * runtime files and other file objects (such as sockets, named pipes, ...) should be stored. + * The directory MUST be owned by the user, and he MUST be the only one having read and write + * access to it. Its Unix access mode MUST be 0700. + * + * The lifetime of the directory MUST be bound to the user being logged in. It MUST be created + * when the user first logs in and if the user fully logs out the directory MUST be removed. If + * the user logs in more than once he should get pointed to the same directory, and it is + * mandatory that the directory continues to exist from his first login to his last logout on + * the system, and not removed in between. Files in the directory MUST not survive reboot or a + * full logout/login cycle. + * + * The directory MUST be on a local file system and not shared with any other system. The + * directory MUST by fully-featured by the standards of the operating system. More specifically, + * on Unix-like operating systems AF_UNIX sockets, symbolic links, hard links, proper + * permissions, file locking, sparse files, memory mapping, file change notifications, a + * reliable hard link count must be supported, and no restrictions on the file name character + * set should be imposed. Files in this directory MAY be subjected to periodic clean-up. To + * ensure that your files are not removed, they should have their access time timestamp modified + * at least once every 6 hours of monotonic time or the 'sticky' bit should be set on the file. + * + * If $XDG_RUNTIME_DIR is not set applications should fall back to a replacement directory with + * similar capabilities and print a warning message. Applications should use this directory for + * communication and synchronization purposes and should not place larger files in it, since it + * might reside in runtime memory and cannot necessarily be swapped out to disk. + */ + path = GetEnvAlloc("XDG_RUNTIME_DIR"); +#endif + + if (path) + return path; + + path = GetPath_TEMP(); + return path; +} + +char* GetKnownPath(int id) +{ + char* path = NULL; + + switch (id) + { + case KNOWN_PATH_HOME: + path = GetPath_HOME(); + break; + + case KNOWN_PATH_TEMP: + path = GetPath_TEMP(); + break; + + case KNOWN_PATH_XDG_DATA_HOME: + path = GetPath_XDG_DATA_HOME(); + break; + + case KNOWN_PATH_XDG_CONFIG_HOME: + path = GetPath_XDG_CONFIG_HOME(); + break; + + case KNOWN_PATH_XDG_CACHE_HOME: + path = GetPath_XDG_CACHE_HOME(); + break; + + case KNOWN_PATH_XDG_RUNTIME_DIR: + path = GetPath_XDG_RUNTIME_DIR(); + break; + + default: + path = NULL; + break; + } + + if (!path) + WLog_WARN(TAG, "Path %s is %p", GetKnownPathIdString(id), path); + return path; +} + +char* GetKnownSubPath(int id, const char* path) +{ + char* subPath = NULL; + char* knownPath = NULL; + knownPath = GetKnownPath(id); + + if (!knownPath) + return NULL; + + subPath = GetCombinedPath(knownPath, path); + free(knownPath); + return subPath; +} + +char* GetEnvironmentPath(char* name) +{ + char* env = NULL; + DWORD nSize = 0; + DWORD nStatus = 0; + nSize = GetEnvironmentVariableX(name, NULL, 0); + + if (nSize) + { + env = (LPSTR)malloc(nSize); + + if (!env) + return NULL; + + nStatus = GetEnvironmentVariableX(name, env, nSize); + + if (nStatus != (nSize - 1)) + { + free(env); + return NULL; + } + } + + return env; +} + +char* GetEnvironmentSubPath(char* name, const char* path) +{ + char* env = NULL; + char* subpath = NULL; + env = GetEnvironmentPath(name); + + if (!env) + return NULL; + + subpath = GetCombinedPath(env, path); + free(env); + return subpath; +} + +char* GetCombinedPath(const char* basePath, const char* subPath) +{ + size_t length = 0; + HRESULT status = 0; + char* path = NULL; + char* subPathCpy = NULL; + size_t basePathLength = 0; + size_t subPathLength = 0; + + if (basePath) + basePathLength = strlen(basePath); + + if (subPath) + subPathLength = strlen(subPath); + + length = basePathLength + subPathLength + 1; + path = (char*)calloc(1, length + 1); + + if (!path) + goto fail; + + if (basePath) + CopyMemory(path, basePath, basePathLength); + + if (FAILED(PathCchConvertStyleA(path, basePathLength, PATH_STYLE_NATIVE))) + goto fail; + + if (!subPath) + return path; + + subPathCpy = _strdup(subPath); + + if (!subPathCpy) + goto fail; + + if (FAILED(PathCchConvertStyleA(subPathCpy, subPathLength, PATH_STYLE_NATIVE))) + goto fail; + + status = NativePathCchAppendA(path, length + 1, subPathCpy); + if (FAILED(status)) + goto fail; + + free(subPathCpy); + return path; + +fail: + free(path); + free(subPathCpy); + return NULL; +} + +BOOL PathMakePathA(LPCSTR path, LPSECURITY_ATTRIBUTES lpAttributes) +{ +#if defined(_UWP) + return FALSE; +#elif defined(_WIN32) + return (SHCreateDirectoryExA(NULL, path, lpAttributes) == ERROR_SUCCESS); +#else + const char delim = PathGetSeparatorA(PATH_STYLE_NATIVE); + char* dup = NULL; + BOOL result = TRUE; + /* we only operate on a non-null, absolute path */ +#if defined(__OS2__) + + if (!path) + return FALSE; + +#else + + if (!path || *path != delim) + return FALSE; + +#endif + + if (!(dup = _strdup(path))) + return FALSE; + +#ifdef __OS2__ + p = (strlen(dup) > 3) && (dup[1] == ':') && (dup[2] == delim)) ? &dup[3] : dup; + + while (p) +#else + for (char* p = dup; p;) +#endif + { + if ((p = strchr(p + 1, delim))) + *p = '\0'; + + if (mkdir(dup, 0777) != 0) + if (errno != EEXIST) + { + result = FALSE; + break; + } + + if (p) + *p = delim; + } + + free(dup); + return (result); +#endif +} + +BOOL PathMakePathW(LPCWSTR path, LPSECURITY_ATTRIBUTES lpAttributes) +{ +#if defined(_UWP) + return FALSE; +#elif defined(_WIN32) + return (SHCreateDirectoryExW(NULL, path, lpAttributes) == ERROR_SUCCESS); +#else + const WCHAR wdelim = PathGetSeparatorW(PATH_STYLE_NATIVE); + const char delim = PathGetSeparatorA(PATH_STYLE_NATIVE); + char* dup = NULL; + BOOL result = TRUE; + /* we only operate on a non-null, absolute path */ +#if defined(__OS2__) + + if (!path) + return FALSE; + +#else + + if (!path || *path != wdelim) + return FALSE; + +#endif + + dup = ConvertWCharToUtf8Alloc(path, NULL); + if (!dup) + return FALSE; + +#ifdef __OS2__ + p = (strlen(dup) > 3) && (dup[1] == ':') && (dup[2] == delim)) ? &dup[3] : dup; + + while (p) +#else + for (char* p = dup; p;) +#endif + { + if ((p = strchr(p + 1, delim))) + *p = '\0'; + + if (mkdir(dup, 0777) != 0) + if (errno != EEXIST) + { + result = FALSE; + break; + } + + if (p) + *p = delim; + } + + free(dup); + return (result); +#endif +} + +#if !defined(_WIN32) || defined(_UWP) + +BOOL PathIsRelativeA(LPCSTR pszPath) +{ + if (!pszPath) + return FALSE; + + return pszPath[0] != '/'; +} + +BOOL PathIsRelativeW(LPCWSTR pszPath) +{ + LPSTR lpFileNameA = NULL; + BOOL ret = FALSE; + + if (!pszPath) + goto fail; + + lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, NULL); + if (!lpFileNameA) + goto fail; + ret = PathIsRelativeA(lpFileNameA); +fail: + free(lpFileNameA); + return ret; +} + +BOOL PathFileExistsA(LPCSTR pszPath) +{ + struct stat stat_info; + + if (stat(pszPath, &stat_info) != 0) + return FALSE; + + return TRUE; +} + +BOOL PathFileExistsW(LPCWSTR pszPath) +{ + LPSTR lpFileNameA = NULL; + BOOL ret = FALSE; + + if (!pszPath) + goto fail; + lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, NULL); + if (!lpFileNameA) + goto fail; + + ret = winpr_PathFileExists(lpFileNameA); +fail: + free(lpFileNameA); + return ret; +} + +BOOL PathIsDirectoryEmptyA(LPCSTR pszPath) +{ + struct dirent* dp = NULL; + int empty = 1; + DIR* dir = opendir(pszPath); + + if (dir == NULL) /* Not a directory or doesn't exist */ + return 1; + + while ((dp = readdir(dir)) != NULL) + { + if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) + continue; /* Skip . and .. */ + + empty = 0; + break; + } + + closedir(dir); + return empty; +} + +BOOL PathIsDirectoryEmptyW(LPCWSTR pszPath) +{ + LPSTR lpFileNameA = NULL; + BOOL ret = FALSE; + if (!pszPath) + goto fail; + lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, NULL); + if (!lpFileNameA) + goto fail; + ret = PathIsDirectoryEmptyA(lpFileNameA); +fail: + free(lpFileNameA); + return ret; +} + +#else + +#ifdef _MSC_VER +#pragma comment(lib, "shlwapi.lib") +#endif + +#endif + +BOOL winpr_MoveFile(LPCSTR lpExistingFileName, LPCSTR lpNewFileName) +{ +#ifndef _WIN32 + return MoveFileA(lpExistingFileName, lpNewFileName); +#else + BOOL result = FALSE; + LPWSTR lpExistingFileNameW = NULL; + LPWSTR lpNewFileNameW = NULL; + + if (!lpExistingFileName || !lpNewFileName) + return FALSE; + + lpExistingFileNameW = ConvertUtf8ToWCharAlloc(lpExistingFileName, NULL); + if (!lpExistingFileNameW) + goto cleanup; + lpNewFileNameW = ConvertUtf8ToWCharAlloc(lpNewFileName, NULL); + if (!lpNewFileNameW) + goto cleanup; + + result = MoveFileW(lpExistingFileNameW, lpNewFileNameW); + +cleanup: + free(lpExistingFileNameW); + free(lpNewFileNameW); + return result; +#endif +} + +BOOL winpr_MoveFileEx(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags) +{ +#ifndef _WIN32 + return MoveFileExA(lpExistingFileName, lpNewFileName, dwFlags); +#else + BOOL result = FALSE; + LPWSTR lpExistingFileNameW = NULL; + LPWSTR lpNewFileNameW = NULL; + + if (!lpExistingFileName || !lpNewFileName) + return FALSE; + + lpExistingFileNameW = ConvertUtf8ToWCharAlloc(lpExistingFileName, NULL); + if (!lpExistingFileNameW) + goto cleanup; + lpNewFileNameW = ConvertUtf8ToWCharAlloc(lpNewFileName, NULL); + if (!lpNewFileNameW) + goto cleanup; + + result = MoveFileExW(lpExistingFileNameW, lpNewFileNameW, dwFlags); + +cleanup: + free(lpExistingFileNameW); + free(lpNewFileNameW); + return result; +#endif +} + +BOOL winpr_DeleteFile(const char* lpFileName) +{ +#ifndef _WIN32 + return DeleteFileA(lpFileName); +#else + LPWSTR lpFileNameW = NULL; + BOOL result = FALSE; + + if (lpFileName) + { + lpFileNameW = ConvertUtf8ToWCharAlloc(lpFileName, NULL); + if (!lpFileNameW) + goto cleanup; + } + + result = DeleteFileW(lpFileNameW); + +cleanup: + free(lpFileNameW); + return result; +#endif +} + +BOOL winpr_RemoveDirectory(LPCSTR lpPathName) +{ +#ifndef _WIN32 + return RemoveDirectoryA(lpPathName); +#else + LPWSTR lpPathNameW = NULL; + BOOL result = FALSE; + + if (lpPathName) + { + lpPathNameW = ConvertUtf8ToWCharAlloc(lpPathName, NULL); + if (!lpPathNameW) + goto cleanup; + } + + result = RemoveDirectoryW(lpPathNameW); + +cleanup: + free(lpPathNameW); + return result; +#endif +} + +BOOL winpr_PathFileExists(const char* pszPath) +{ + if (!pszPath) + return FALSE; +#ifndef _WIN32 + return PathFileExistsA(pszPath); +#else + WCHAR* pathW = ConvertUtf8ToWCharAlloc(pszPath, NULL); + BOOL result = FALSE; + + if (!pathW) + return FALSE; + + result = PathFileExistsW(pathW); + free(pathW); + + return result; +#endif +} + +BOOL winpr_PathMakePath(const char* path, LPSECURITY_ATTRIBUTES lpAttributes) +{ + if (!path) + return FALSE; +#ifndef _WIN32 + return PathMakePathA(path, lpAttributes); +#else + WCHAR* pathW = ConvertUtf8ToWCharAlloc(path, NULL); + BOOL result = FALSE; + + if (!pathW) + return FALSE; + + result = SHCreateDirectoryExW(NULL, pathW, lpAttributes) == ERROR_SUCCESS; + free(pathW); + + return result; +#endif +} diff --git a/winpr/libwinpr/path/shell_ios.h b/winpr/libwinpr/path/shell_ios.h new file mode 100644 index 0000000..3144d8d --- /dev/null +++ b/winpr/libwinpr/path/shell_ios.h @@ -0,0 +1,9 @@ +#ifndef SHELL_IOS_H_ +#define SHELL_IOS_H_ + +char* ios_get_home(void); +char* ios_get_temp(void); +char* ios_get_data(void); +char* ios_get_cache(void); + +#endif diff --git a/winpr/libwinpr/path/shell_ios.m b/winpr/libwinpr/path/shell_ios.m new file mode 100644 index 0000000..7e1185b --- /dev/null +++ b/winpr/libwinpr/path/shell_ios.m @@ -0,0 +1,54 @@ +/** + * WinPR: Windows Portable Runtime + * Path Functions + * + * Copyright 2016 Armin Novak <armin.novak@thincast.om> + * Copyright 2016 Thincast Technologies GmbH + * + * 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. + */ + +#import <Foundation/Foundation.h> + +#include <winpr/config.h> + +#include "shell_ios.h" + +NSString *ios_get_directory_for_search_path(NSSearchPathDirectory searchPath) +{ + return [NSSearchPathForDirectoriesInDomains(searchPath, NSUserDomainMask, YES) lastObject]; +} + +char *ios_get_home(void) +{ + NSString *path = ios_get_directory_for_search_path(NSDocumentDirectory); + return strdup([path UTF8String]); +} + +char *ios_get_temp(void) +{ + NSString *tmp_path = NSTemporaryDirectory(); + return strdup([tmp_path UTF8String]); +} + +char *ios_get_data(void) +{ + NSString *path = ios_get_directory_for_search_path(NSApplicationSupportDirectory); + return strdup([path UTF8String]); +} + +char *ios_get_cache(void) +{ + NSString *path = ios_get_directory_for_search_path(NSCachesDirectory); + return strdup([path UTF8String]); +} diff --git a/winpr/libwinpr/path/test/CMakeLists.txt b/winpr/libwinpr/path/test/CMakeLists.txt new file mode 100644 index 0000000..974907e --- /dev/null +++ b/winpr/libwinpr/path/test/CMakeLists.txt @@ -0,0 +1,48 @@ + +set(MODULE_NAME "TestPath") +set(MODULE_PREFIX "TEST_PATH") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestPathCchAddBackslash.c + TestPathCchRemoveBackslash.c + TestPathCchAddBackslashEx.c + TestPathCchRemoveBackslashEx.c + TestPathCchAddExtension.c + TestPathCchAppend.c + TestPathCchAppendEx.c + TestPathCchCanonicalize.c + TestPathCchCanonicalizeEx.c + TestPathAllocCanonicalize.c + TestPathCchCombine.c + TestPathCchCombineEx.c + TestPathAllocCombine.c + TestPathCchFindExtension.c + TestPathCchRenameExtension.c + TestPathCchRemoveExtension.c + TestPathCchIsRoot.c + TestPathIsUNCEx.c + TestPathCchSkipRoot.c + TestPathCchStripToRoot.c + TestPathCchStripPrefix.c + TestPathCchRemoveFileSpec.c + TestPathShell.c + TestPathMakePath.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") diff --git a/winpr/libwinpr/path/test/TestPathAllocCanonicalize.c b/winpr/libwinpr/path/test/TestPathAllocCanonicalize.c new file mode 100644 index 0000000..d04fff1 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathAllocCanonicalize.c @@ -0,0 +1,12 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +int TestPathAllocCanonicalize(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathAllocCombine.c b/winpr/libwinpr/path/test/TestPathAllocCombine.c new file mode 100644 index 0000000..4630df0 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathAllocCombine.c @@ -0,0 +1,98 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +static const TCHAR testBasePathBackslash[] = _T("C:\\Program Files\\"); +static const TCHAR testBasePathNoBackslash[] = _T("C:\\Program Files"); +static const TCHAR testMorePathBackslash[] = _T("\\Microsoft Visual Studio 11.0"); +static const TCHAR testMorePathNoBackslash[] = _T("Microsoft Visual Studio 11.0"); +static const TCHAR testPathOut[] = _T("C:\\Program Files\\Microsoft Visual Studio 11.0"); +static const TCHAR testPathOutMorePathBackslash[] = _T("C:\\Microsoft Visual Studio 11.0"); + +int TestPathAllocCombine(int argc, char* argv[]) +{ + HRESULT status = 0; + LPTSTR PathOut = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /* Base Path: Backslash, More Path: No Backslash */ + + status = PathAllocCombine(testBasePathBackslash, testMorePathNoBackslash, 0, &PathOut); + + if (status != S_OK) + { + _tprintf(_T("PathAllocCombine status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(PathOut, testPathOut) != 0) + { + _tprintf(_T("Path Mismatch 1: Actual: %s, Expected: %s\n"), PathOut, testPathOut); + return -1; + } + + free(PathOut); + + /* Base Path: Backslash, More Path: Backslash */ + + status = PathAllocCombine(testBasePathBackslash, testMorePathBackslash, 0, &PathOut); + + if (status != S_OK) + { + _tprintf(_T("PathAllocCombine status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(PathOut, testPathOutMorePathBackslash) != 0) + { + _tprintf(_T("Path Mismatch 2: Actual: %s, Expected: %s\n"), PathOut, + testPathOutMorePathBackslash); + return -1; + } + + free(PathOut); + + /* Base Path: No Backslash, More Path: Backslash */ + + status = PathAllocCombine(testBasePathNoBackslash, testMorePathBackslash, 0, &PathOut); + + if (status != S_OK) + { + _tprintf(_T("PathAllocCombine status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(PathOut, testPathOutMorePathBackslash) != 0) + { + _tprintf(_T("Path Mismatch 3: Actual: %s, Expected: %s\n"), PathOut, + testPathOutMorePathBackslash); + return -1; + } + + free(PathOut); + + /* Base Path: No Backslash, More Path: No Backslash */ + + status = PathAllocCombine(testBasePathNoBackslash, testMorePathNoBackslash, 0, &PathOut); + + if (status != S_OK) + { + _tprintf(_T("PathAllocCombine status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(PathOut, testPathOut) != 0) + { + _tprintf(_T("Path Mismatch 4: Actual: %s, Expected: %s\n"), PathOut, testPathOut); + return -1; + } + + free(PathOut); + + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchAddBackslash.c b/winpr/libwinpr/path/test/TestPathCchAddBackslash.c new file mode 100644 index 0000000..0a414e2 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchAddBackslash.c @@ -0,0 +1,100 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +static const TCHAR testPathBackslash[] = _T("C:\\Program Files\\"); +static const TCHAR testPathNoBackslash[] = _T("C:\\Program Files"); + +int TestPathCchAddBackslash(int argc, char* argv[]) +{ + HRESULT status = 0; + TCHAR Path[PATHCCH_MAX_CCH]; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /** + * PathCchAddBackslash returns S_OK if the function was successful, + * S_FALSE if the path string already ends in a backslash, + * or an error code otherwise. + */ + + _tcscpy(Path, testPathNoBackslash); + + /* Add a backslash to a path without a trailing backslash, expect S_OK */ + + status = PathCchAddBackslash(Path, PATHCCH_MAX_CCH); + + if (status != S_OK) + { + _tprintf(_T("PathCchAddBackslash status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathBackslash) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathBackslash); + return -1; + } + + /* Add a backslash to a path with a trailing backslash, expect S_FALSE */ + + _tcscpy(Path, testPathBackslash); + + status = PathCchAddBackslash(Path, PATHCCH_MAX_CCH); + + if (status != S_FALSE) + { + _tprintf(_T("PathCchAddBackslash status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathBackslash) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathBackslash); + return -1; + } + + /* Use NULL PSTR, expect FAILED(status) */ + + status = PathCchAddBackslash(NULL, PATHCCH_MAX_CCH); + + if (SUCCEEDED(status)) + { + _tprintf(_T("PathCchAddBackslash unexpectedly succeded with null buffer. Status: 0x%08") _T( + PRIX32) _T("\n"), + status); + return -1; + } + + /* Use insufficient size value, expect FAILED(status) */ + + _tcscpy(Path, _T("C:\\tmp")); + + status = PathCchAddBackslash(Path, 7); + + if (SUCCEEDED(status)) + { + _tprintf(_T("PathCchAddBackslash unexpectedly succeded with insufficient buffer size. ") + _T("Status: 0x%08") _T(PRIX32) _T("\n"), + status); + return -1; + } + + /* Use minimum required size value, expect S_OK */ + + _tcscpy(Path, _T("C:\\tmp")); + + status = PathCchAddBackslash(Path, 8); + + if (status != S_OK) + { + _tprintf(_T("PathCchAddBackslash failed with status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchAddBackslashEx.c b/winpr/libwinpr/path/test/TestPathCchAddBackslashEx.c new file mode 100644 index 0000000..4a84200 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchAddBackslashEx.c @@ -0,0 +1,103 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +static const TCHAR testPathBackslash[] = _T("C:\\Program Files\\"); +static const TCHAR testPathNoBackslash[] = _T("C:\\Program Files"); + +int TestPathCchAddBackslashEx(int argc, char* argv[]) +{ + HRESULT status = 0; + LPTSTR pszEnd = NULL; + size_t cchRemaining = 0; + TCHAR Path[PATHCCH_MAX_CCH]; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /** + * PathCchAddBackslashEx returns S_OK if the function was successful, + * S_FALSE if the path string already ends in a backslash, + * or an error code otherwise. + */ + + _tcscpy(Path, testPathNoBackslash); + + /* Add a backslash to a path without a trailing backslash, expect S_OK */ + + status = PathCchAddBackslashEx(Path, sizeof(Path) / sizeof(TCHAR), &pszEnd, &cchRemaining); + + if (status != S_OK) + { + _tprintf(_T("PathCchAddBackslash status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathBackslash) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathBackslash); + return -1; + } + + /* Add a backslash to a path with a trailing backslash, expect S_FALSE */ + + _tcscpy(Path, testPathBackslash); + + status = PathCchAddBackslashEx(Path, sizeof(Path) / sizeof(TCHAR), &pszEnd, &cchRemaining); + + if (status != S_FALSE) + { + _tprintf(_T("PathCchAddBackslash status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathBackslash) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathBackslash); + return -1; + } + + /* Use NULL PSTR, expect FAILED(status) */ + + status = PathCchAddBackslashEx(NULL, PATHCCH_MAX_CCH, NULL, NULL); + + if (SUCCEEDED(status)) + { + _tprintf( + _T("PathCchAddBackslashEx unexpectedly succeded with null buffer. Status: 0x%08") _T( + PRIX32) _T("\n"), + status); + return -1; + } + + /* Use insufficient size value, expect FAILED(status) */ + + _tcscpy(Path, _T("C:\\tmp")); + + status = PathCchAddBackslashEx(Path, 7, NULL, NULL); + + if (SUCCEEDED(status)) + { + _tprintf(_T("PathCchAddBackslashEx unexpectedly succeded with insufficient buffer size. ") + _T("Status: 0x%08") _T(PRIX32) _T("\n"), + status); + return -1; + } + + /* Use minimum required size value, expect S_OK */ + + _tcscpy(Path, _T("C:\\tmp")); + + status = PathCchAddBackslashEx(Path, 8, NULL, NULL); + + if (status != S_OK) + { + _tprintf(_T("PathCchAddBackslashEx failed with status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchAddExtension.c b/winpr/libwinpr/path/test/TestPathCchAddExtension.c new file mode 100644 index 0000000..71f5ddf --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchAddExtension.c @@ -0,0 +1,140 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +static const TCHAR testExtDot[] = _T(".exe"); +static const TCHAR testExtNoDot[] = _T("exe"); +static const TCHAR testPathNoExtension[] = _T("C:\\Windows\\System32\\cmd"); +static const TCHAR testPathExtension[] = _T("C:\\Windows\\System32\\cmd.exe"); + +int TestPathCchAddExtension(int argc, char* argv[]) +{ + HRESULT status = 0; + TCHAR Path[PATHCCH_MAX_CCH]; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /* Path: no extension, Extension: dot */ + + _tcscpy(Path, testPathNoExtension); + + status = PathCchAddExtension(Path, PATHCCH_MAX_CCH, testExtDot); + + if (status != S_OK) + { + _tprintf(_T("PathCchAddExtension status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathExtension) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathExtension); + return -1; + } + + /* Path: no extension, Extension: no dot */ + + _tcscpy(Path, testPathNoExtension); + + status = PathCchAddExtension(Path, PATHCCH_MAX_CCH, testExtNoDot); + + if (status != S_OK) + { + _tprintf(_T("PathCchAddExtension status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathExtension) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathExtension); + return -1; + } + + /* Path: extension, Extension: dot */ + + _tcscpy(Path, testPathExtension); + + status = PathCchAddExtension(Path, PATHCCH_MAX_CCH, testExtDot); + + if (status != S_FALSE) + { + _tprintf(_T("PathCchAddExtension status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathExtension) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathExtension); + return -1; + } + + /* Path: extension, Extension: no dot */ + + _tcscpy(Path, testPathExtension); + + status = PathCchAddExtension(Path, PATHCCH_MAX_CCH, testExtDot); + + if (status != S_FALSE) + { + _tprintf(_T("PathCchAddExtension status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathExtension) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathExtension); + return -1; + } + + /* Path: NULL */ + + status = PathCchAddExtension(NULL, PATHCCH_MAX_CCH, testExtDot); + if (status != E_INVALIDARG) + { + _tprintf(_T("PathCchAddExtension with null buffer returned status: 0x%08") _T( + PRIX32) _T(" (expected E_INVALIDARG)\n"), + status); + return -1; + } + + /* Extension: NULL */ + + status = PathCchAddExtension(Path, PATHCCH_MAX_CCH, NULL); + if (status != E_INVALIDARG) + { + _tprintf(_T("PathCchAddExtension with null extension returned status: 0x%08") _T( + PRIX32) _T(" (expected E_INVALIDARG)\n"), + status); + return -1; + } + + /* Insufficient Buffer size */ + + _tcscpy(Path, _T("C:\\456789")); + status = PathCchAddExtension(Path, 9 + 4, _T(".jpg")); + if (SUCCEEDED(status)) + { + _tprintf(_T("PathCchAddExtension with insufficient buffer unexpectedly succeeded with ") + _T("status: 0x%08") _T(PRIX32) _T("\n"), + status); + return -1; + } + + /* Minimum required buffer size */ + + _tcscpy(Path, _T("C:\\456789")); + status = PathCchAddExtension(Path, 9 + 4 + 1, _T(".jpg")); + if (FAILED(status)) + { + _tprintf(_T("PathCchAddExtension with sufficient buffer unexpectedly failed with status: ") + _T("0x%08") _T(PRIX32) _T("\n"), + status); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchAppend.c b/winpr/libwinpr/path/test/TestPathCchAppend.c new file mode 100644 index 0000000..93524ca --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchAppend.c @@ -0,0 +1,151 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +static const TCHAR testBasePathBackslash[] = _T("C:\\Program Files\\"); +static const TCHAR testBasePathNoBackslash[] = _T("C:\\Program Files"); +static const TCHAR testMorePathBackslash[] = _T("\\Microsoft Visual Studio 11.0"); +static const TCHAR testMorePathNoBackslash[] = _T("Microsoft Visual Studio 11.0"); +static const TCHAR testPathOut[] = _T("C:\\Program Files\\Microsoft Visual Studio 11.0"); + +int TestPathCchAppend(int argc, char* argv[]) +{ + HRESULT status = 0; + TCHAR Path[PATHCCH_MAX_CCH]; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /* Base Path: Backslash, More Path: No Backslash */ + + _tcscpy(Path, testBasePathBackslash); + + status = PathCchAppend(Path, PATHCCH_MAX_CCH, testMorePathNoBackslash); + + if (status != S_OK) + { + _tprintf(_T("PathCchAppend status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathOut) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathOut); + return -1; + } + + /* Base Path: Backslash, More Path: Backslash */ + + _tcscpy(Path, testBasePathBackslash); + + status = PathCchAppend(Path, PATHCCH_MAX_CCH, testMorePathBackslash); + + if (status != S_OK) + { + _tprintf(_T("PathCchAppend status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathOut) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathOut); + return -1; + } + + /* Base Path: No Backslash, More Path: Backslash */ + + _tcscpy(Path, testBasePathNoBackslash); + + status = PathCchAppend(Path, PATHCCH_MAX_CCH, testMorePathBackslash); + + if (status != S_OK) + { + _tprintf(_T("PathCchAppend status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathOut) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathOut); + return -1; + } + + /* Base Path: No Backslash, More Path: No Backslash */ + + _tcscpy(Path, testBasePathNoBackslash); + + status = PathCchAppend(Path, PATHCCH_MAX_CCH, testMorePathNoBackslash); + + if (status != S_OK) + { + _tprintf(_T("PathCchAppend status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathOut) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, testPathOut); + return -1; + } + + /* According to msdn a NULL Path is an invalid argument */ + status = PathCchAppend(NULL, PATHCCH_MAX_CCH, testMorePathNoBackslash); + if (status != E_INVALIDARG) + { + _tprintf(_T("PathCchAppend with NULL path unexpectedly returned status: 0x%08") _T( + PRIX32) _T("\n"), + status); + return -1; + } + + /* According to msdn a NULL pszMore is an invalid argument (although optional !?) */ + _tcscpy(Path, testBasePathNoBackslash); + status = PathCchAppend(Path, PATHCCH_MAX_CCH, NULL); + if (status != E_INVALIDARG) + { + _tprintf(_T("PathCchAppend with NULL pszMore unexpectedly returned status: 0x%08") _T( + PRIX32) _T("\n"), + status); + return -1; + } + + /* According to msdn cchPath must be > 0 and <= PATHCCH_MAX_CCH */ + _tcscpy(Path, testBasePathNoBackslash); + status = PathCchAppend(Path, 0, testMorePathNoBackslash); + if (status != E_INVALIDARG) + { + _tprintf(_T("PathCchAppend with cchPath value 0 unexpectedly returned status: 0x%08") _T( + PRIX32) _T("\n"), + status); + return -1; + } + _tcscpy(Path, testBasePathNoBackslash); + status = PathCchAppend(Path, PATHCCH_MAX_CCH + 1, testMorePathNoBackslash); + if (status != E_INVALIDARG) + { + _tprintf(_T("PathCchAppend with cchPath value > PATHCCH_MAX_CCH unexpectedly returned ") + _T("status: 0x%08") _T(PRIX32) _T("\n"), + status); + return -1; + } + + /* Resulting file must not exceed PATHCCH_MAX_CCH */ + + for (size_t i = 0; i < PATHCCH_MAX_CCH - 1; i++) + Path[i] = _T('X'); + + Path[PATHCCH_MAX_CCH - 1] = 0; + + status = PathCchAppend(Path, PATHCCH_MAX_CCH, _T("\\This cannot be appended to Path")); + if (SUCCEEDED(status)) + { + _tprintf(_T("PathCchAppend unexepectedly succeeded with status: 0x%08") _T(PRIX32) _T("\n"), + status); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchAppendEx.c b/winpr/libwinpr/path/test/TestPathCchAppendEx.c new file mode 100644 index 0000000..b6d83f5 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchAppendEx.c @@ -0,0 +1,12 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +int TestPathCchAppendEx(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchCanonicalize.c b/winpr/libwinpr/path/test/TestPathCchCanonicalize.c new file mode 100644 index 0000000..a7fa4ce --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchCanonicalize.c @@ -0,0 +1,12 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +int TestPathCchCanonicalize(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchCanonicalizeEx.c b/winpr/libwinpr/path/test/TestPathCchCanonicalizeEx.c new file mode 100644 index 0000000..2d670eb --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchCanonicalizeEx.c @@ -0,0 +1,12 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +int TestPathCchCanonicalizeEx(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchCombine.c b/winpr/libwinpr/path/test/TestPathCchCombine.c new file mode 100644 index 0000000..4b6f4e4 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchCombine.c @@ -0,0 +1,12 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +int TestPathCchCombine(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchCombineEx.c b/winpr/libwinpr/path/test/TestPathCchCombineEx.c new file mode 100644 index 0000000..89b794f --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchCombineEx.c @@ -0,0 +1,12 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +int TestPathCchCombineEx(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchFindExtension.c b/winpr/libwinpr/path/test/TestPathCchFindExtension.c new file mode 100644 index 0000000..f4b4151 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchFindExtension.c @@ -0,0 +1,116 @@ +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +static const char testPathExtension[] = "C:\\Windows\\System32\\cmd.exe"; + +int TestPathCchFindExtension(int argc, char* argv[]) +{ + PCSTR pszExt = NULL; + PCSTR pszTmp = NULL; + HRESULT hr = 0; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /* Test invalid args */ + + hr = PathCchFindExtensionA(NULL, sizeof(testPathExtension), &pszExt); + if (SUCCEEDED(hr)) + { + printf( + "PathCchFindExtensionA unexpectedly succeeded with pszPath = NULL. result: 0x%08" PRIX32 + "\n", + hr); + return -1; + } + + hr = PathCchFindExtensionA(testPathExtension, 0, &pszExt); + if (SUCCEEDED(hr)) + { + printf("PathCchFindExtensionA unexpectedly succeeded with cchPath = 0. result: 0x%08" PRIX32 + "\n", + hr); + return -1; + } + + hr = PathCchFindExtensionA(testPathExtension, sizeof(testPathExtension), NULL); + if (SUCCEEDED(hr)) + { + printf( + "PathCchFindExtensionA unexpectedly succeeded with ppszExt = NULL. result: 0x%08" PRIX32 + "\n", + hr); + return -1; + } + + /* Test missing null-termination of pszPath */ + + hr = PathCchFindExtensionA("c:\\45.789", 9, &pszExt); /* nb: correct would be 10 */ + if (SUCCEEDED(hr)) + { + printf("PathCchFindExtensionA unexpectedly succeeded with unterminated pszPath. result: " + "0x%08" PRIX32 "\n", + hr); + return -1; + } + + /* Test passing of an empty terminated string (must succeed) */ + + pszExt = NULL; + pszTmp = ""; + hr = PathCchFindExtensionA(pszTmp, 1, &pszExt); + if (hr != S_OK) + { + printf("PathCchFindExtensionA failed with an empty terminated string. result: 0x%08" PRIX32 + "\n", + hr); + return -1; + } + /* pszExt must point to the strings terminating 0 now */ + if (pszExt != pszTmp) + { + printf("PathCchFindExtensionA failed with an empty terminated string: pszExt pointer " + "mismatch\n"); + return -1; + } + + /* Test a path without file extension (must succeed) */ + + pszExt = NULL; + pszTmp = "c:\\4.678\\"; + hr = PathCchFindExtensionA(pszTmp, 10, &pszExt); + if (hr != S_OK) + { + printf("PathCchFindExtensionA failed with a directory path. result: 0x%08" PRIX32 "\n", hr); + return -1; + } + /* The extension must not have been found and pszExt must point to the + * strings terminating NULL now */ + if (pszExt != &pszTmp[9]) + { + printf("PathCchFindExtensionA failed with a directory path: pszExt pointer mismatch\n"); + return -1; + } + + /* Non-special tests */ + + pszExt = NULL; + if (PathCchFindExtensionA(testPathExtension, sizeof(testPathExtension), &pszExt) != S_OK) + { + printf("PathCchFindExtensionA failure: expected S_OK\n"); + return -1; + } + + if (!pszExt || strcmp(pszExt, ".exe")) + { + printf("PathCchFindExtensionA failure: unexpected extension\n"); + return -1; + } + + printf("Extension: %s\n", pszExt); + + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchIsRoot.c b/winpr/libwinpr/path/test/TestPathCchIsRoot.c new file mode 100644 index 0000000..1ffda37 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchIsRoot.c @@ -0,0 +1,12 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +int TestPathCchIsRoot(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchRemoveBackslash.c b/winpr/libwinpr/path/test/TestPathCchRemoveBackslash.c new file mode 100644 index 0000000..f23e72b --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchRemoveBackslash.c @@ -0,0 +1,12 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +int TestPathCchRemoveBackslash(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchRemoveBackslashEx.c b/winpr/libwinpr/path/test/TestPathCchRemoveBackslashEx.c new file mode 100644 index 0000000..80eb1aa --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchRemoveBackslashEx.c @@ -0,0 +1,12 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +int TestPathCchRemoveBackslashEx(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchRemoveExtension.c b/winpr/libwinpr/path/test/TestPathCchRemoveExtension.c new file mode 100644 index 0000000..bd315cb --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchRemoveExtension.c @@ -0,0 +1,12 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +int TestPathCchRemoveExtension(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchRemoveFileSpec.c b/winpr/libwinpr/path/test/TestPathCchRemoveFileSpec.c new file mode 100644 index 0000000..686e367 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchRemoveFileSpec.c @@ -0,0 +1,12 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +int TestPathCchRemoveFileSpec(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchRenameExtension.c b/winpr/libwinpr/path/test/TestPathCchRenameExtension.c new file mode 100644 index 0000000..cf021b1 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchRenameExtension.c @@ -0,0 +1,12 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +int TestPathCchRenameExtension(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchSkipRoot.c b/winpr/libwinpr/path/test/TestPathCchSkipRoot.c new file mode 100644 index 0000000..dca5ad9 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchSkipRoot.c @@ -0,0 +1,12 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +int TestPathCchSkipRoot(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchStripPrefix.c b/winpr/libwinpr/path/test/TestPathCchStripPrefix.c new file mode 100644 index 0000000..aaec4bc --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchStripPrefix.c @@ -0,0 +1,128 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +/** + * Naming Files, Paths, and Namespaces: + * http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247/ + */ + +static const TCHAR testPathPrefixFileNamespace[] = _T("\\\\?\\C:\\Program Files\\"); +static const TCHAR testPathNoPrefixFileNamespace[] = _T("C:\\Program Files\\"); +static const TCHAR testPathPrefixFileNamespaceMinimum[] = _T("\\\\?\\C:"); +static const TCHAR testPathNoPrefixFileNamespaceMinimum[] = _T("C:"); + +static const TCHAR testPathPrefixDeviceNamespace[] = _T("\\\\?\\GLOBALROOT"); + +int TestPathCchStripPrefix(int argc, char* argv[]) +{ + HRESULT status = 0; + TCHAR Path[PATHCCH_MAX_CCH]; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /** + * PathCchStripPrefix returns S_OK if the prefix was removed, S_FALSE if + * the path did not have a prefix to remove, or an HRESULT failure code. + */ + + /* Path with prefix (File Namespace) */ + + _tcscpy(Path, testPathPrefixFileNamespace); + + status = PathCchStripPrefix(Path, sizeof(testPathPrefixFileNamespace) / sizeof(TCHAR)); + + if (status != S_OK) + { + _tprintf(_T("PathCchStripPrefix status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathNoPrefixFileNamespace) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, + testPathNoPrefixFileNamespace); + return -1; + } + + /* Path with prefix (Device Namespace) */ + + _tcscpy(Path, testPathPrefixDeviceNamespace); + + status = PathCchStripPrefix(Path, sizeof(testPathPrefixDeviceNamespace) / sizeof(TCHAR)); + + if (status != S_FALSE) + { + _tprintf(_T("PathCchStripPrefix status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + if (_tcscmp(Path, testPathPrefixDeviceNamespace) != 0) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, + testPathPrefixDeviceNamespace); + return -1; + } + + /* NULL Path */ + status = PathCchStripPrefix(NULL, PATHCCH_MAX_CCH); + if (status != E_INVALIDARG) + { + _tprintf( + _T("PathCchStripPrefix with null path unexpectedly succeeded with status 0x%08") _T( + PRIX32) _T("\n"), + status); + return -1; + } + + /* Invalid cchPath values: 0, 1, 2, 3 and > PATHCCH_MAX_CCH */ + for (int i = 0; i < 5; i++) + { + _tcscpy(Path, testPathPrefixFileNamespace); + if (i == 4) + i = PATHCCH_MAX_CCH + 1; + status = PathCchStripPrefix(Path, i); + if (status != E_INVALIDARG) + { + _tprintf(_T("PathCchStripPrefix with invalid cchPath value %d unexpectedly succeeded ") + _T("with status 0x%08") _T(PRIX32) _T("\n"), + i, status); + return -1; + } + } + + /* Minimum Path that would get successfully stripped on windows */ + _tcscpy(Path, testPathPrefixFileNamespaceMinimum); + size_t i = sizeof(testPathPrefixFileNamespaceMinimum) / sizeof(TCHAR); + i = i - 1; /* include testing of a non-null terminated string */ + status = PathCchStripPrefix(Path, i); + if (status != S_OK) + { + _tprintf(_T("PathCchStripPrefix with minimum valid strippable path length unexpectedly ") + _T("returned status 0x%08") _T(PRIX32) _T("\n"), + status); + return -1; + } + if (_tcscmp(Path, testPathNoPrefixFileNamespaceMinimum)) + { + _tprintf(_T("Path Mismatch: Actual: %s, Expected: %s\n"), Path, + testPathNoPrefixFileNamespaceMinimum); + return -1; + } + + /* Invalid drive letter symbol */ + _tcscpy(Path, _T("\\\\?\\5:")); + status = PathCchStripPrefix(Path, 6); + if (status == S_OK) + { + _tprintf( + _T("PathCchStripPrefix with invalid drive letter symbol unexpectedly succeeded\n")); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathCchStripToRoot.c b/winpr/libwinpr/path/test/TestPathCchStripToRoot.c new file mode 100644 index 0000000..3b96e5c --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathCchStripToRoot.c @@ -0,0 +1,12 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +int TestPathCchStripToRoot(int argc, char* argv[]) +{ + printf("Warning: %s is not implemented!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathIsUNCEx.c b/winpr/libwinpr/path/test/TestPathIsUNCEx.c new file mode 100644 index 0000000..4fdf4f7 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathIsUNCEx.c @@ -0,0 +1,52 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +static const TCHAR testServer[] = _T("server\\share\\path\\file"); +static const TCHAR testPathUNC[] = _T("\\\\server\\share\\path\\file"); +static const TCHAR testPathNotUNC[] = _T("C:\\share\\path\\file"); + +int TestPathIsUNCEx(int argc, char* argv[]) +{ + BOOL status = 0; + LPCTSTR Server = NULL; + TCHAR Path[PATHCCH_MAX_CCH]; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + /* Path is UNC */ + + _tcscpy(Path, testPathUNC); + + status = PathIsUNCEx(Path, &Server); + + if (!status) + { + _tprintf(_T("PathIsUNCEx status: 0x%d\n"), status); + return -1; + } + + if (_tcscmp(Server, testServer) != 0) + { + _tprintf(_T("Server Name Mismatch: Actual: %s, Expected: %s\n"), Server, testServer); + return -1; + } + + /* Path is not UNC */ + + _tcscpy(Path, testPathNotUNC); + + status = PathIsUNCEx(Path, &Server); + + if (status) + { + _tprintf(_T("PathIsUNCEx status: 0x%08") _T(PRIX32) _T("\n"), status); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathMakePath.c b/winpr/libwinpr/path/test/TestPathMakePath.c new file mode 100644 index 0000000..488cab3 --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathMakePath.c @@ -0,0 +1,83 @@ +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <winpr/crt.h> +#include <winpr/file.h> +#include <winpr/path.h> + +int TestPathMakePath(int argc, char* argv[]) +{ + size_t baseLen = 0; + BOOL success = 0; + char tmp[64] = { 0 }; + char* path = NULL; + char* cur = NULL; + char delim = PathGetSeparatorA(0); + char* base = GetKnownPath(KNOWN_PATH_TEMP); + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!base) + { + fprintf(stderr, "Failed to get temporary directory!\n"); + return -1; + } + + baseLen = strlen(base); + srand(time(NULL)); + + for (int x = 0; x < 5; x++) + { + sprintf_s(tmp, ARRAYSIZE(tmp), "%08X", rand()); + path = GetCombinedPath(base, tmp); + free(base); + + if (!path) + { + fprintf(stderr, "GetCombinedPath failed!\n"); + return -1; + } + + base = path; + } + + printf("Creating path %s\n", path); + success = winpr_PathMakePath(path, NULL); + + if (!success) + { + fprintf(stderr, "MakePath failed!\n"); + free(path); + return -1; + } + + success = winpr_PathFileExists(path); + + if (!success) + { + fprintf(stderr, "MakePath lied about success!\n"); + free(path); + return -1; + } + + while (strlen(path) > baseLen) + { + if (!winpr_RemoveDirectory(path)) + { + fprintf(stderr, "winpr_RemoveDirectory %s failed!\n", path); + free(path); + return -1; + } + + cur = strrchr(path, delim); + + if (cur) + *cur = '\0'; + } + + free(path); + printf("%s success!\n", __func__); + return 0; +} diff --git a/winpr/libwinpr/path/test/TestPathShell.c b/winpr/libwinpr/path/test/TestPathShell.c new file mode 100644 index 0000000..1925fea --- /dev/null +++ b/winpr/libwinpr/path/test/TestPathShell.c @@ -0,0 +1,58 @@ + +#include <stdio.h> +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/tchar.h> +#include <winpr/winpr.h> + +int TestPathShell(int argc, char* argv[]) +{ + const int paths[] = { KNOWN_PATH_HOME, KNOWN_PATH_TEMP, + KNOWN_PATH_XDG_DATA_HOME, KNOWN_PATH_XDG_CONFIG_HOME, + KNOWN_PATH_XDG_CACHE_HOME, KNOWN_PATH_XDG_RUNTIME_DIR, + KNOWN_PATH_XDG_CONFIG_HOME }; + const char* names[] = { "KNOWN_PATH_HOME", "KNOWN_PATH_TEMP", + "KNOWN_PATH_XDG_DATA_HOME", "KNOWN_PATH_XDG_CONFIG_HOME", + "KNOWN_PATH_XDG_CACHE_HOME", "KNOWN_PATH_XDG_RUNTIME_DIR", + "KNOWN_PATH_XDG_CONFIG_HOME" }; + int rc = 0; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + for (size_t x = 0; x < sizeof(paths) / sizeof(paths[0]); x++) + { + const int id = paths[x]; + const char* name = names[x]; + { + char* path = GetKnownPath(id); + + if (!path) + { + fprintf(stderr, "GetKnownPath(%d) failed\n", id); + rc = -1; + } + else + { + printf("%s Path: %s\n", name, path); + } + free(path); + } + { + char* path = GetKnownSubPath(id, "freerdp"); + + if (!path) + { + fprintf(stderr, "GetKnownSubPath(%d) failed\n", id); + rc = -1; + } + else + { + printf("%s SubPath: %s\n", name, path); + } + free(path); + } + } + + return rc; +} |