#undef USE_FCOPYFILE
#define USE_SENDFILE …
#ifdef _WIN32
#ifdef CLAR_WIN32_LONGPATHS
#define CLAR_MAX_PATH …
#else
#define CLAR_MAX_PATH …
#endif
#define RM_RETRY_COUNT …
#define RM_RETRY_DELAY …
#ifdef __MINGW32__
#define wcscpy_s …
#define wcscat_s …
#endif
static int
fs__dotordotdot(WCHAR *_tocheck)
{
return _tocheck[0] == '.' &&
(_tocheck[1] == '\0' ||
(_tocheck[1] == '.' && _tocheck[2] == '\0'));
}
static int
fs_rmdir_rmdir(WCHAR *_wpath)
{
unsigned retries = 1;
while (!RemoveDirectoryW(_wpath)) {
if (retries++ > RM_RETRY_COUNT ||
ERROR_DIR_NOT_EMPTY != GetLastError())
return -1;
Sleep(RM_RETRY_DELAY * retries * retries);
}
return 0;
}
static void translate_path(WCHAR *path, size_t path_size)
{
size_t path_len, i;
if (wcsncmp(path, L"\\\\?\\", 4) == 0)
return;
path_len = wcslen(path);
cl_assert(path_size > path_len + 4);
for (i = path_len; i > 0; i--) {
WCHAR c = path[i - 1];
if (c == L'/')
path[i + 3] = L'\\';
else
path[i + 3] = path[i - 1];
}
path[0] = L'\\';
path[1] = L'\\';
path[2] = L'?';
path[3] = L'\\';
path[path_len + 4] = L'\0';
}
static void
fs_rmdir_helper(WCHAR *_wsource)
{
WCHAR buffer[CLAR_MAX_PATH];
HANDLE find_handle;
WIN32_FIND_DATAW find_data;
size_t buffer_prefix_len;
wcscpy_s(buffer, CLAR_MAX_PATH, _wsource);
translate_path(buffer, CLAR_MAX_PATH);
wcscat_s(buffer, CLAR_MAX_PATH, L"\\");
buffer_prefix_len = wcslen(buffer);
wcscat_s(buffer, CLAR_MAX_PATH, L"*");
find_handle = FindFirstFileW(buffer, &find_data);
cl_assert(INVALID_HANDLE_VALUE != find_handle);
do {
if (fs__dotordotdot(find_data.cFileName))
continue;
wcscpy_s(buffer + buffer_prefix_len, CLAR_MAX_PATH - buffer_prefix_len, find_data.cFileName);
if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes)
fs_rmdir_helper(buffer);
else {
if (FILE_ATTRIBUTE_READONLY & find_data.dwFileAttributes)
cl_assert(SetFileAttributesW(buffer, find_data.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY));
cl_assert(DeleteFileW(buffer));
}
}
while (FindNextFileW(find_handle, &find_data));
cl_assert(ERROR_NO_MORE_FILES == GetLastError());
FindClose(find_handle);
cl_assert(0 == fs_rmdir_rmdir(_wsource));
}
static int
fs_rm_wait(WCHAR *_wpath)
{
unsigned retries = 1;
DWORD last_error;
do {
if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(_wpath))
last_error = GetLastError();
else
last_error = ERROR_SUCCESS;
if (ERROR_FILE_NOT_FOUND == last_error ||
ERROR_PATH_NOT_FOUND == last_error)
return 0;
Sleep(RM_RETRY_DELAY * retries * retries);
}
while (retries++ <= RM_RETRY_COUNT);
return -1;
}
static void
fs_rm(const char *_source)
{
WCHAR wsource[CLAR_MAX_PATH];
DWORD attrs;
cl_assert(MultiByteToWideChar(CP_UTF8,
MB_ERR_INVALID_CHARS,
_source,
-1,
wsource,
CLAR_MAX_PATH));
translate_path(wsource, CLAR_MAX_PATH);
attrs = GetFileAttributesW(wsource);
if (INVALID_FILE_ATTRIBUTES == attrs)
return;
if (FILE_ATTRIBUTE_DIRECTORY & attrs)
fs_rmdir_helper(wsource);
else {
if (FILE_ATTRIBUTE_READONLY & attrs)
cl_assert(SetFileAttributesW(wsource, attrs & ~FILE_ATTRIBUTE_READONLY));
cl_assert(DeleteFileW(wsource));
}
cl_assert(0 == fs_rm_wait(wsource));
}
static void
fs_copydir_helper(WCHAR *_wsource, WCHAR *_wdest)
{
WCHAR buf_source[CLAR_MAX_PATH], buf_dest[CLAR_MAX_PATH];
HANDLE find_handle;
WIN32_FIND_DATAW find_data;
size_t buf_source_prefix_len, buf_dest_prefix_len;
wcscpy_s(buf_source, CLAR_MAX_PATH, _wsource);
wcscat_s(buf_source, CLAR_MAX_PATH, L"\\");
translate_path(buf_source, CLAR_MAX_PATH);
buf_source_prefix_len = wcslen(buf_source);
wcscpy_s(buf_dest, CLAR_MAX_PATH, _wdest);
wcscat_s(buf_dest, CLAR_MAX_PATH, L"\\");
translate_path(buf_dest, CLAR_MAX_PATH);
buf_dest_prefix_len = wcslen(buf_dest);
wcscat_s(buf_source, CLAR_MAX_PATH, L"*");
find_handle = FindFirstFileW(buf_source, &find_data);
cl_assert(INVALID_HANDLE_VALUE != find_handle);
cl_assert(CreateDirectoryW(_wdest, NULL));
do {
if (fs__dotordotdot(find_data.cFileName))
continue;
wcscpy_s(buf_source + buf_source_prefix_len, CLAR_MAX_PATH - buf_source_prefix_len, find_data.cFileName);
wcscpy_s(buf_dest + buf_dest_prefix_len, CLAR_MAX_PATH - buf_dest_prefix_len, find_data.cFileName);
if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes)
fs_copydir_helper(buf_source, buf_dest);
else
cl_assert(CopyFileW(buf_source, buf_dest, TRUE));
}
while (FindNextFileW(find_handle, &find_data));
cl_assert(ERROR_NO_MORE_FILES == GetLastError());
FindClose(find_handle);
}
static void
fs_copy(const char *_source, const char *_dest)
{
WCHAR wsource[CLAR_MAX_PATH], wdest[CLAR_MAX_PATH];
DWORD source_attrs, dest_attrs;
HANDLE find_handle;
WIN32_FIND_DATAW find_data;
cl_assert(MultiByteToWideChar(CP_UTF8,
MB_ERR_INVALID_CHARS,
_source,
-1,
wsource,
CLAR_MAX_PATH));
cl_assert(MultiByteToWideChar(CP_UTF8,
MB_ERR_INVALID_CHARS,
_dest,
-1,
wdest,
CLAR_MAX_PATH));
translate_path(wsource, CLAR_MAX_PATH);
translate_path(wdest, CLAR_MAX_PATH);
source_attrs = GetFileAttributesW(wsource);
cl_assert(INVALID_FILE_ATTRIBUTES != source_attrs);
dest_attrs = GetFileAttributesW(wdest);
if (INVALID_FILE_ATTRIBUTES != dest_attrs) {
find_handle = FindFirstFileW(wsource, &find_data);
cl_assert(INVALID_HANDLE_VALUE != find_handle);
wcscat_s(wdest, CLAR_MAX_PATH, L"\\");
wcscat_s(wdest, CLAR_MAX_PATH, find_data.cFileName);
FindClose(find_handle);
cl_assert(INVALID_FILE_ATTRIBUTES == GetFileAttributesW(wdest));
}
if (FILE_ATTRIBUTE_DIRECTORY & source_attrs)
fs_copydir_helper(wsource, wdest);
else
cl_assert(CopyFileW(wsource, wdest, TRUE));
}
void
cl_fs_cleanup(void)
{
#ifdef CLAR_FIXTURE_PATH
fs_rm(fixture_path(_clar_path, "*"));
#else
((void)fs_copy);
#endif
}
#else
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#if defined(__linux__)
# include <sys/sendfile.h>
#endif
#if defined(__APPLE__)
# include <copyfile.h>
#endif
static void basename_r(const char **out, int *out_len, const char *in)
{ … }
static char *joinpath(const char *dir, const char *base, int base_len)
{ … }
static void
fs_copydir_helper(const char *source, const char *dest, int dest_mode)
{ … }
static void
fs_copyfile_helper(const char *source, size_t source_len, const char *dest, int dest_mode)
{ … }
static void
fs_copy(const char *source, const char *_dest)
{ … }
static void
fs_rmdir_helper(const char *path)
{ … }
static void
fs_rm(const char *path)
{ … }
void
cl_fs_cleanup(void)
{ … }
#endif