(trunk, libt) #4160 - the slow slog to catch trunk up to mike.dld's 4160 diff continues. This step applies 4160-04a-dir.patch, which adds tr_sys_dir_*() portability wrappers.
This commit is contained in:
parent
728957c46a
commit
51a90d0da0
|
@ -7,7 +7,12 @@
|
|||
* $Id$
|
||||
*/
|
||||
|
||||
#if (defined (HAVE_POSIX_FADVISE) || defined (HAVE_POSIX_FALLOCATE)) && (!defined (_XOPEN_SOURCE) || _XOPEN_SOURCE < 600)
|
||||
#if defined (HAVE_MKDTEMP) && (!defined (_XOPEN_SOURCE) || _XOPEN_SOURCE < 700)
|
||||
#ifdef _XOPEN_SOURCE
|
||||
#undef _XOPEN_SOURCE
|
||||
#endif
|
||||
#define _XOPEN_SOURCE 700
|
||||
#elif (defined (HAVE_POSIX_FADVISE) || defined (HAVE_POSIX_FALLOCATE)) && (!defined (_XOPEN_SOURCE) || _XOPEN_SOURCE < 600)
|
||||
#ifdef _XOPEN_SOURCE
|
||||
#undef _XOPEN_SOURCE
|
||||
#endif
|
||||
|
@ -23,6 +28,7 @@
|
|||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h> /* O_LARGEFILE, posix_fadvise (), [posix_]fallocate () */
|
||||
#include <libgen.h> /* basename (), dirname () */
|
||||
|
@ -41,6 +47,7 @@
|
|||
|
||||
#include "transmission.h"
|
||||
#include "file.h"
|
||||
#include "log.h"
|
||||
#include "platform.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
@ -147,6 +154,79 @@ set_file_for_single_pass (tr_sys_file_t handle)
|
|||
errno = err;
|
||||
}
|
||||
|
||||
#ifndef HAVE_MKDIRP
|
||||
|
||||
static bool
|
||||
create_path (const char * path_in,
|
||||
int permissions,
|
||||
tr_error ** error)
|
||||
{
|
||||
char * p;
|
||||
char * pp;
|
||||
bool done;
|
||||
int tmperr;
|
||||
int rv;
|
||||
struct stat sb;
|
||||
char * path;
|
||||
|
||||
/* make a temporary copy of path */
|
||||
path = tr_strdup (path_in);
|
||||
|
||||
/* walk past the root */
|
||||
p = path;
|
||||
while (*p == TR_PATH_DELIMITER)
|
||||
++p;
|
||||
|
||||
pp = p;
|
||||
done = false;
|
||||
while ((p = strchr (pp, TR_PATH_DELIMITER)) || (p = strchr (pp, '\0')))
|
||||
{
|
||||
if (!*p)
|
||||
done = true;
|
||||
else
|
||||
*p = '\0';
|
||||
|
||||
tmperr = errno;
|
||||
rv = stat (path, &sb);
|
||||
errno = tmperr;
|
||||
if (rv)
|
||||
{
|
||||
tr_error * my_error = NULL;
|
||||
|
||||
/* Folder doesn't exist yet */
|
||||
if (!tr_sys_dir_create (path, 0, permissions, &my_error))
|
||||
{
|
||||
tr_logAddError (_ ("Couldn't create \"%1$s\": %2$s"), path, my_error->message);
|
||||
tr_free (path);
|
||||
tr_error_propagate (error, &my_error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ((sb.st_mode & S_IFMT) != S_IFDIR)
|
||||
{
|
||||
/* Node exists but isn't a folder */
|
||||
char * const buf = tr_strdup_printf (_ ("File \"%s\" is in the way"), path);
|
||||
tr_logAddError (_ ("Couldn't create \"%1$s\": %2$s"), path_in, buf);
|
||||
tr_free (buf);
|
||||
tr_free (path);
|
||||
set_system_error (error, ENOTDIR);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (done)
|
||||
break;
|
||||
|
||||
*p = TR_PATH_DELIMITER;
|
||||
p++;
|
||||
pp = p;
|
||||
}
|
||||
|
||||
tr_free (path);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool
|
||||
tr_sys_path_exists (const char * path,
|
||||
tr_error ** error)
|
||||
|
@ -824,3 +904,166 @@ tr_sys_file_unmap (const void * address,
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
tr_sys_dir_get_current (tr_error ** error)
|
||||
{
|
||||
char * ret;
|
||||
|
||||
ret = getcwd (NULL, 0);
|
||||
|
||||
if (ret == NULL && (errno == EINVAL || errno == ERANGE))
|
||||
{
|
||||
size_t size = PATH_MAX;
|
||||
char * tmp = NULL;
|
||||
|
||||
do
|
||||
{
|
||||
tmp = tr_renew (char, tmp, size);
|
||||
if (tmp == NULL)
|
||||
break;
|
||||
ret = getcwd (tmp, size);
|
||||
size += 2048;
|
||||
}
|
||||
while (ret == NULL && errno == ERANGE);
|
||||
|
||||
if (ret == NULL)
|
||||
{
|
||||
const int err = errno;
|
||||
tr_free (tmp);
|
||||
errno = err;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == NULL)
|
||||
set_system_error (error, errno);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_dir_create (const char * path,
|
||||
int flags,
|
||||
int permissions,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
tr_error * my_error = NULL;
|
||||
|
||||
assert (path != NULL);
|
||||
|
||||
if ((flags & TR_SYS_DIR_CREATE_PARENTS) != 0)
|
||||
#ifdef HAVE_MKDIRP
|
||||
ret = mkdirp (path, permissions) != -1;
|
||||
#else
|
||||
ret = create_path (path, permissions, &my_error);
|
||||
#endif
|
||||
else
|
||||
ret = mkdir (path, permissions) != -1;
|
||||
|
||||
if (!ret && errno == EEXIST)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
if (stat (path, &sb) != -1 && S_ISDIR (sb.st_mode))
|
||||
{
|
||||
tr_error_clear (&my_error);
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
errno = EEXIST;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
if (my_error != NULL)
|
||||
tr_error_propagate (error, &my_error);
|
||||
else
|
||||
set_system_error (error, errno);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_dir_create_temp (char * path_template,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
assert (path_template != NULL);
|
||||
|
||||
#ifdef HAVE_MKDTEMP
|
||||
|
||||
ret = mkdtemp (path_template) != NULL;
|
||||
|
||||
#else
|
||||
|
||||
ret = mktemp (path_template) != NULL && mkdir (path_template, 0700) != -1;
|
||||
|
||||
#endif
|
||||
|
||||
if (!ret)
|
||||
set_system_error (error, errno);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
tr_sys_dir_t
|
||||
tr_sys_dir_open (const char * path,
|
||||
tr_error ** error)
|
||||
{
|
||||
tr_sys_dir_t ret;
|
||||
|
||||
#ifndef __clang__
|
||||
/* Clang gives "static_assert expression is not an integral constant expression" error */
|
||||
TR_STATIC_ASSERT (TR_BAD_SYS_DIR == NULL, "values should match");
|
||||
#endif
|
||||
|
||||
assert (path != NULL);
|
||||
|
||||
ret = opendir (path);
|
||||
|
||||
if (ret == TR_BAD_SYS_DIR)
|
||||
set_system_error (error, errno);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *
|
||||
tr_sys_dir_read_name (tr_sys_dir_t handle,
|
||||
tr_error ** error)
|
||||
{
|
||||
const char * ret = NULL;
|
||||
struct dirent * entry;
|
||||
|
||||
assert (handle != TR_BAD_SYS_DIR);
|
||||
|
||||
errno = 0;
|
||||
entry = readdir (handle);
|
||||
|
||||
if (entry != NULL)
|
||||
ret = entry->d_name;
|
||||
else if (errno != 0)
|
||||
set_system_error (error, errno);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_dir_close (tr_sys_dir_t handle,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
assert (handle != TR_BAD_SYS_DIR);
|
||||
|
||||
ret = closedir (handle) != -1;
|
||||
|
||||
if (!ret)
|
||||
set_system_error (error, errno);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ static char *
|
|||
create_test_dir (const char * name)
|
||||
{
|
||||
char * const test_dir = tr_buildPath (tr_sessionGetConfigDir (session), name, NULL);
|
||||
tr_mkdirp (test_dir, 0777);
|
||||
tr_sys_dir_create (test_dir, 0, 0777, NULL);
|
||||
return test_dir;
|
||||
}
|
||||
|
||||
|
@ -194,7 +194,7 @@ test_get_info (void)
|
|||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
/* Good directory info */
|
||||
tr_mkdirp (path1, 0777);
|
||||
tr_sys_dir_create (path1, 0, 0777, NULL);
|
||||
clear_path_info (&info);
|
||||
check (tr_sys_path_get_info (path1, 0, &info, &err));
|
||||
check (err == NULL);
|
||||
|
@ -233,7 +233,7 @@ test_get_info (void)
|
|||
tr_sys_path_remove (path2, NULL);
|
||||
|
||||
/* Good directory info */
|
||||
tr_mkdirp (path2, 0777);
|
||||
tr_sys_dir_create (path2, 0, 0777, NULL);
|
||||
clear_path_info (&info);
|
||||
check (tr_sys_path_get_info (path1, 0, &info, &err));
|
||||
check (err == NULL);
|
||||
|
@ -278,7 +278,7 @@ test_path_exists (void)
|
|||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
/* Create directory and see that it exists */
|
||||
tr_mkdirp (path1, 0777);
|
||||
tr_sys_dir_create (path1, 0, 0777, NULL);
|
||||
check (tr_sys_path_exists (path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
|
@ -298,7 +298,7 @@ test_path_exists (void)
|
|||
tr_sys_path_remove (path2, NULL);
|
||||
|
||||
/* Create directory and see that it exists (via symlink) */
|
||||
tr_mkdirp (path2, 0777);
|
||||
tr_sys_dir_create (path2, 0, 0777, NULL);
|
||||
check (tr_sys_path_exists (path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
|
@ -353,7 +353,7 @@ test_path_is_same (void)
|
|||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
/* Two same directories are the same */
|
||||
tr_mkdirp (path1, 0777);
|
||||
tr_sys_dir_create (path1, 0, 0777, NULL);
|
||||
check (tr_sys_path_is_same (path1, path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
|
@ -366,7 +366,7 @@ test_path_is_same (void)
|
|||
tr_sys_path_remove (path2, NULL);
|
||||
|
||||
/* Two separate directories are not the same */
|
||||
tr_mkdirp (path2, 0777);
|
||||
tr_sys_dir_create (path2, 0, 0777, NULL);
|
||||
check (!tr_sys_path_is_same (path1, path2, &err));
|
||||
check (err == NULL);
|
||||
|
||||
|
@ -404,7 +404,7 @@ test_path_is_same (void)
|
|||
tr_sys_path_remove (path2, NULL);
|
||||
|
||||
/* Directory and symlink pointing to another directory are not the same */
|
||||
tr_mkdirp (path2, 0777);
|
||||
tr_sys_dir_create (path2, 0, 0777, NULL);
|
||||
check (!tr_sys_path_is_same (path1, path2, &err));
|
||||
check (err == NULL);
|
||||
check (!tr_sys_path_is_same (path2, path1, &err));
|
||||
|
@ -552,7 +552,7 @@ test_path_resolve (void)
|
|||
tr_free (tmp);
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
tr_mkdirp (path1, 0755);
|
||||
tr_sys_dir_create (path1, 0, 0755, NULL);
|
||||
|
||||
tmp = tr_sys_path_resolve (path2, &err);
|
||||
check (tmp != NULL);
|
||||
|
@ -692,7 +692,7 @@ test_path_rename (void)
|
|||
check (tr_sys_path_rename (path2, path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_mkdirp (path2, 0777);
|
||||
tr_sys_dir_create (path2, 0, 0777, NULL);
|
||||
|
||||
/* Renaming file does not overwrite existing directory, and vice versa */
|
||||
check (!tr_sys_path_rename (path1, path2, &err));
|
||||
|
@ -785,14 +785,14 @@ test_path_remove (void)
|
|||
check (!tr_sys_path_exists (path1, NULL));
|
||||
|
||||
/* Removing empty directory works */
|
||||
tr_mkdirp (path1, 0777);
|
||||
tr_sys_dir_create (path1, 0, 0777, NULL);
|
||||
check (tr_sys_path_exists (path1, NULL));
|
||||
check (tr_sys_path_remove (path1, &err));
|
||||
check (err == NULL);
|
||||
check (!tr_sys_path_exists (path1, NULL));
|
||||
|
||||
/* Removing non-empty directory fails */
|
||||
tr_mkdirp (path2, 0777);
|
||||
tr_sys_dir_create (path2, 0, 0777, NULL);
|
||||
libtest_create_file_with_string_contents (path3, "test");
|
||||
check (tr_sys_path_exists (path2, NULL));
|
||||
check (tr_sys_path_exists (path3, NULL));
|
||||
|
@ -837,7 +837,7 @@ test_file_open (void)
|
|||
tr_error_clear (&err);
|
||||
|
||||
/* Can't open directory */
|
||||
tr_mkdirp (path1, 0777);
|
||||
tr_sys_dir_create (path1, 0, 0777, NULL);
|
||||
#ifdef _WIN32
|
||||
/* This works on *NIX */
|
||||
check (tr_sys_file_open (path1, TR_SYS_FILE_READ, 0600, &err) == TR_BAD_SYS_FILE);
|
||||
|
@ -1152,6 +1152,146 @@ test_file_map (void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_dir_create (void)
|
||||
{
|
||||
char * const test_dir = create_test_dir (__FUNCTION__);
|
||||
tr_error * err = NULL;
|
||||
char * path1, * path2;
|
||||
|
||||
path1 = tr_buildPath (test_dir, "a", NULL);
|
||||
path2 = tr_buildPath (path1, "b", NULL);
|
||||
|
||||
/* Can create directory which has parent */
|
||||
check (tr_sys_dir_create (path1, 0, 0700, &err));
|
||||
check (err == NULL);
|
||||
check (tr_sys_path_exists (path1, NULL));
|
||||
check (validate_permissions (path1, 0700));
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
libtest_create_file_with_string_contents (path1, "test");
|
||||
|
||||
/* Can't create directory where file already exists */
|
||||
check (!tr_sys_dir_create (path1, 0, 0700, &err));
|
||||
check (err != NULL);
|
||||
tr_error_clear (&err);
|
||||
check (!tr_sys_dir_create (path1, TR_SYS_DIR_CREATE_PARENTS, 0700, &err));
|
||||
check (err != NULL);
|
||||
tr_error_clear (&err);
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
/* Can't create directory which has no parent */
|
||||
check (!tr_sys_dir_create (path2, 0, 0700, &err));
|
||||
check (err != NULL);
|
||||
check (!tr_sys_path_exists (path2, NULL));
|
||||
tr_error_clear (&err);
|
||||
|
||||
/* Can create directory with parent directories */
|
||||
check (tr_sys_dir_create (path2, TR_SYS_DIR_CREATE_PARENTS, 0751, &err));
|
||||
check (err == NULL);
|
||||
check (tr_sys_path_exists (path1, NULL));
|
||||
check (tr_sys_path_exists (path2, NULL));
|
||||
check (validate_permissions (path1, 0751));
|
||||
check (validate_permissions (path2, 0751));
|
||||
|
||||
/* Can create existing directory (no-op) */
|
||||
check (tr_sys_dir_create (path1, 0, 0700, &err));
|
||||
check (err == NULL);
|
||||
check (tr_sys_dir_create (path1, TR_SYS_DIR_CREATE_PARENTS, 0700, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_path_remove (path2, NULL);
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
tr_free (path2);
|
||||
tr_free (path1);
|
||||
|
||||
tr_free (test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_dir_read_impl (const char * path,
|
||||
bool * have1,
|
||||
bool * have2)
|
||||
{
|
||||
tr_error * err = NULL;
|
||||
tr_sys_dir_t dd;
|
||||
const char * name;
|
||||
|
||||
*have1 = *have2 = false;
|
||||
|
||||
dd = tr_sys_dir_open (path, &err);
|
||||
check (dd != TR_BAD_SYS_DIR);
|
||||
check (err == NULL);
|
||||
|
||||
while ((name = tr_sys_dir_read_name (dd, &err)) != NULL)
|
||||
{
|
||||
check (err == NULL);
|
||||
|
||||
if (strcmp (name, ".") == 0 || strcmp (name, "..") == 0)
|
||||
continue;
|
||||
|
||||
if (strcmp (name, "a") == 0)
|
||||
*have1 = true;
|
||||
else if (strcmp (name, "b") == 0)
|
||||
*have2 = true;
|
||||
else
|
||||
check (false);
|
||||
}
|
||||
|
||||
check (err == NULL);
|
||||
|
||||
check (tr_sys_dir_close (dd, &err));
|
||||
check (err == NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_dir_read (void)
|
||||
{
|
||||
char * const test_dir = create_test_dir (__FUNCTION__);
|
||||
char * path1, * path2;
|
||||
bool have1, have2;
|
||||
|
||||
path1 = tr_buildPath (test_dir, "a", NULL);
|
||||
path2 = tr_buildPath (test_dir, "b", NULL);
|
||||
|
||||
if (test_dir_read_impl (test_dir, &have1, &have2) != 0)
|
||||
return 1;
|
||||
check (!have1);
|
||||
check (!have2);
|
||||
|
||||
libtest_create_file_with_string_contents (path1, "test");
|
||||
|
||||
if (test_dir_read_impl (test_dir, &have1, &have2) != 0)
|
||||
return 1;
|
||||
check (have1);
|
||||
check (!have2);
|
||||
|
||||
libtest_create_file_with_string_contents (path2, "test");
|
||||
|
||||
if (test_dir_read_impl (test_dir, &have1, &have2) != 0)
|
||||
return 1;
|
||||
check (have1);
|
||||
check (have2);
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
if (test_dir_read_impl (test_dir, &have1, &have2) != 0)
|
||||
return 1;
|
||||
check (!have1);
|
||||
check (have2);
|
||||
|
||||
tr_free (path2);
|
||||
tr_free (path1);
|
||||
|
||||
tr_free (test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
|
@ -1168,7 +1308,9 @@ main (void)
|
|||
test_file_read_write_seek,
|
||||
test_file_truncate,
|
||||
test_file_preallocate,
|
||||
test_file_map
|
||||
test_file_map,
|
||||
test_dir_create,
|
||||
test_dir_read
|
||||
};
|
||||
int ret;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <assert.h>
|
||||
#include <stdlib.h> /* _splitpath_s (), _makepath_s () */
|
||||
|
||||
#include <shlobj.h> /* SHCreateDirectoryEx () */
|
||||
#include <winioctl.h> /* FSCTL_SET_SPARSE */
|
||||
|
||||
#include "transmission.h"
|
||||
|
@ -25,6 +26,14 @@
|
|||
"i64" suffix for C code, but no warning is issued */
|
||||
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
|
||||
|
||||
struct tr_sys_dir_win32
|
||||
{
|
||||
wchar_t * pattern;
|
||||
HANDLE find_handle;
|
||||
WIN32_FIND_DATAW find_data;
|
||||
char * utf8_name;
|
||||
};
|
||||
|
||||
static void
|
||||
set_system_error (tr_error ** error,
|
||||
DWORD code)
|
||||
|
@ -123,6 +132,54 @@ open_file (const char * path,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
create_dir (const char * path,
|
||||
int flags,
|
||||
int permissions,
|
||||
bool okay_if_exists,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
wchar_t * wide_path;
|
||||
DWORD error_code = ERROR_SUCCESS;
|
||||
|
||||
assert (path != NULL);
|
||||
|
||||
wide_path = tr_win32_utf8_to_native (path, -1);
|
||||
|
||||
if ((flags & TR_SYS_DIR_CREATE_PARENTS) != 0)
|
||||
{
|
||||
/* For some reason SHCreateDirectoryEx has issues with forward slashes */
|
||||
wchar_t * p = wide_path;
|
||||
while ((p = wcschr (p, L'/')) != NULL)
|
||||
*p++ = L'\\';
|
||||
|
||||
error_code = SHCreateDirectoryExW (NULL, wide_path, NULL);
|
||||
ret = error_code == ERROR_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = CreateDirectoryW (wide_path, NULL);
|
||||
if (!ret)
|
||||
error_code = GetLastError ();
|
||||
}
|
||||
|
||||
if (!ret && error_code == ERROR_ALREADY_EXISTS && okay_if_exists)
|
||||
{
|
||||
const DWORD attributes = GetFileAttributesW (wide_path);
|
||||
if (attributes != INVALID_FILE_ATTRIBUTES &&
|
||||
(attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
set_system_error (error, error_code);
|
||||
|
||||
tr_free (wide_path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
create_temp_path (char * path_template,
|
||||
void (* callback) (const char * path, void * param, tr_error ** error),
|
||||
|
@ -965,3 +1022,162 @@ tr_sys_file_unmap (const void * address,
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
tr_sys_dir_get_current (tr_error ** error)
|
||||
{
|
||||
char * ret = NULL;
|
||||
wchar_t * wide_ret = NULL;
|
||||
DWORD size;
|
||||
|
||||
size = GetCurrentDirectoryW (0, NULL);
|
||||
|
||||
if (size != 0)
|
||||
{
|
||||
wide_ret = tr_new (wchar_t, size);
|
||||
if (GetCurrentDirectoryW (size, wide_ret) != 0)
|
||||
ret = tr_win32_native_to_utf8 (wide_ret, size);
|
||||
}
|
||||
|
||||
if (ret == NULL)
|
||||
set_system_error (error, GetLastError ());
|
||||
|
||||
tr_free (wide_ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_dir_create (const char * path,
|
||||
int flags,
|
||||
int permissions,
|
||||
tr_error ** error)
|
||||
{
|
||||
return create_dir (path, flags, permissions, true, error);
|
||||
}
|
||||
|
||||
static void
|
||||
dir_create_temp_callback (const char * path,
|
||||
void * param,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool * result = (bool *) param;
|
||||
|
||||
assert (result != NULL);
|
||||
|
||||
*result = create_dir (path, 0, 0, false, error);
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_dir_create_temp (char * path_template,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
assert (path_template != NULL);
|
||||
|
||||
create_temp_path (path_template, dir_create_temp_callback, &ret, error);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
tr_sys_dir_t
|
||||
tr_sys_dir_open (const char * path,
|
||||
tr_error ** error)
|
||||
{
|
||||
tr_sys_dir_t ret;
|
||||
|
||||
#ifndef __clang__
|
||||
/* Clang gives "static_assert expression is not an integral constant expression" error */
|
||||
TR_STATIC_ASSERT (TR_BAD_SYS_DIR == NULL, "values should match");
|
||||
#endif
|
||||
|
||||
assert (path != NULL);
|
||||
|
||||
ret = tr_new (struct tr_sys_dir_win32, 1);
|
||||
ret->pattern = tr_win32_utf8_to_native_ex (path, -1, 2);
|
||||
|
||||
if (ret->pattern != NULL)
|
||||
{
|
||||
const size_t pattern_size = wcslen (ret->pattern);
|
||||
ret->pattern[pattern_size + 0] = L'\\';
|
||||
ret->pattern[pattern_size + 1] = L'*';
|
||||
ret->pattern[pattern_size + 2] = L'\0';
|
||||
|
||||
ret->find_handle = INVALID_HANDLE_VALUE;
|
||||
ret->utf8_name = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_system_error (error, GetLastError ());
|
||||
|
||||
tr_free (ret->pattern);
|
||||
tr_free (ret);
|
||||
|
||||
ret = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *
|
||||
tr_sys_dir_read_name (tr_sys_dir_t handle,
|
||||
tr_error ** error)
|
||||
{
|
||||
char * ret;
|
||||
DWORD error_code = ERROR_SUCCESS;
|
||||
|
||||
assert (handle != TR_BAD_SYS_DIR);
|
||||
|
||||
if (handle->find_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
handle->find_handle = FindFirstFileW (handle->pattern, &handle->find_data);
|
||||
if (handle->find_handle == INVALID_HANDLE_VALUE)
|
||||
error_code = GetLastError ();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!FindNextFileW (handle->find_handle, &handle->find_data))
|
||||
error_code = GetLastError ();
|
||||
}
|
||||
|
||||
if (error_code != ERROR_SUCCESS)
|
||||
{
|
||||
set_system_error_if_file_found (error, error_code);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = tr_win32_native_to_utf8 (handle->find_data.cFileName, -1);
|
||||
|
||||
if (ret != NULL)
|
||||
{
|
||||
tr_free (handle->utf8_name);
|
||||
handle->utf8_name = ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_system_error (error, GetLastError ());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_dir_close (tr_sys_dir_t handle,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
assert (handle != TR_BAD_SYS_DIR);
|
||||
|
||||
ret = FindClose (handle->find_handle);
|
||||
|
||||
if (!ret)
|
||||
set_system_error (error, GetLastError ());
|
||||
|
||||
tr_free (handle->utf8_name);
|
||||
tr_free (handle->pattern);
|
||||
tr_free (handle);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -33,11 +33,17 @@ extern "C" {
|
|||
typedef int tr_sys_file_t;
|
||||
/** @brief Platform-specific invalid file descriptor constant. */
|
||||
#define TR_BAD_SYS_FILE (-1)
|
||||
/** @brief Platform-specific directory descriptor type. */
|
||||
typedef void * tr_sys_dir_t;
|
||||
#else
|
||||
typedef HANDLE tr_sys_file_t;
|
||||
#define TR_BAD_SYS_FILE INVALID_HANDLE_VALUE
|
||||
typedef struct tr_sys_dir_win32 * tr_sys_dir_t;
|
||||
#endif
|
||||
|
||||
/** @brief Platform-specific invalid directory descriptor constant. */
|
||||
#define TR_BAD_SYS_DIR ((tr_sys_dir_t)NULL)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TR_STD_SYS_FILE_IN,
|
||||
|
@ -78,6 +84,12 @@ typedef enum
|
|||
}
|
||||
tr_sys_file_preallocate_flags_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TR_SYS_DIR_CREATE_PARENTS = 1 << 0
|
||||
}
|
||||
tr_sys_dir_create_flags_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TR_SYS_PATH_IS_FILE,
|
||||
|
@ -99,9 +111,9 @@ tr_sys_path_info;
|
|||
*
|
||||
* Following functions accept paths in UTF-8 encoding and convert them to native
|
||||
* encoding internally if needed.
|
||||
* Descriptors returned (@ref tr_sys_file_t) may have different type depending
|
||||
* on platform and should generally not be passed to native functions, but to
|
||||
* wrapper functions instead.
|
||||
* Descriptors returned (@ref tr_sys_file_t and @ref tr_sys_dir_t) may have
|
||||
* different type depending on platform and should generally not be passed to
|
||||
* native functions, but to wrapper functions instead.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
@ -497,6 +509,94 @@ bool tr_sys_file_unmap (const void * address,
|
|||
uint64_t size,
|
||||
tr_error ** error);
|
||||
|
||||
/* Directory-related wrappers */
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `getcwd ()`.
|
||||
*
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you are
|
||||
* not interested in error details.
|
||||
*
|
||||
* @return Pointer to newly allocated buffer containing path to current
|
||||
* directory (use @ref tr_free to free it when no longer needed) on
|
||||
* success, `NULL` otherwise (with `error` set accordingly).
|
||||
*/
|
||||
char * tr_sys_dir_get_current (tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Like `mkdir ()`, but makes parent directories if needed.
|
||||
*
|
||||
* @param[in] path Path to directory.
|
||||
* @param[in] flags Combination of @ref tr_sys_dir_create_flags_t values.
|
||||
* @param[in] permissions Permissions to create directory with. Not used on
|
||||
Windows.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you
|
||||
* are not interested in error details.
|
||||
*
|
||||
* @return `True` on success, `false` otherwise (with `error` set accordingly).
|
||||
*/
|
||||
bool tr_sys_dir_create (const char * path,
|
||||
int flags,
|
||||
int permissions,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `mkdtemp ()`.
|
||||
*
|
||||
* @param[in,out] path_template Template path to directory. Should end with at
|
||||
* least six 'X' characters. Upon success, trailing
|
||||
* 'X' characters are replaced with actual random
|
||||
* characters used to form a unique path to
|
||||
* temporary directory.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL`
|
||||
* if you are not interested in error details.
|
||||
*
|
||||
* @return `True` on success, `false` otherwise (with `error` set accordingly).
|
||||
*/
|
||||
bool tr_sys_dir_create_temp (char * path_template,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `opendir ()`.
|
||||
*
|
||||
* @param[in] path Path to directory.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you are
|
||||
* not interested in error details.
|
||||
*
|
||||
* @return Opened directory descriptor on success, `TR_BAD_SYS_DIR` otherwise
|
||||
* (with `error` set accordingly).
|
||||
*/
|
||||
tr_sys_dir_t tr_sys_dir_open (const char * path,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `readdir ()`.
|
||||
*
|
||||
* @param[in] handle Valid directory descriptor.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you are
|
||||
* not interested in error details.
|
||||
*
|
||||
* @return Pointer to next directory entry name (stored internally, DO NOT pass
|
||||
* it to @ref tr_free) on success, `NULL` otherwise (with `error` set
|
||||
* accordingly). Note that `NULL` will also be returned in case of end
|
||||
* of directory; if you need to distinguish the two, check if `error`
|
||||
* is `NULL` afterwards.
|
||||
*/
|
||||
const char * tr_sys_dir_read_name (tr_sys_dir_t handle,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `closedir ()`.
|
||||
*
|
||||
* @param[in] handle Valid directory descriptor.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you are
|
||||
* not interested in error details.
|
||||
*
|
||||
* @return `True` on success, `false` otherwise (with `error` set accordingly).
|
||||
*/
|
||||
bool tr_sys_dir_close (tr_sys_dir_t handle,
|
||||
tr_error ** error);
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
#ifndef TR_QUARK_H
|
||||
#define TR_QUARK_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Quarks — a 2-way association between a string and a unique integer identifier */
|
||||
typedef size_t tr_quark;
|
||||
|
||||
|
@ -420,5 +424,12 @@ const char * tr_quark_get_string (tr_quark quark, size_t * len);
|
|||
*/
|
||||
tr_quark tr_quark_new (const void * str, size_t len);
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue