mirror of
https://github.com/transmission/transmission
synced 2025-03-19 10:15:36 +00:00
(trunk, libT) mike.dld's 4160-02a-path.patch: portability wrapper around file paths.
This commit is contained in:
parent
db39d4096e
commit
60a5c793d9
8 changed files with 1811 additions and 0 deletions
|
@ -460,6 +460,8 @@
|
|||
BEFC1E580C07861A00B0BB3C /* clients.c in Sources */ = {isa = PBXBuildFile; fileRef = BEFC1E1F0C07861A00B0BB3C /* clients.c */; };
|
||||
C1077A4E183EB29600634C22 /* error.c in Sources */ = {isa = PBXBuildFile; fileRef = C1077A4A183EB29600634C22 /* error.c */; };
|
||||
C1077A4F183EB29600634C22 /* error.h in Headers */ = {isa = PBXBuildFile; fileRef = C1077A4B183EB29600634C22 /* error.h */; };
|
||||
C1077A50183EB29600634C22 /* file-posix.c in Sources */ = {isa = PBXBuildFile; fileRef = C1077A4C183EB29600634C22 /* file-posix.c */; };
|
||||
C1077A51183EB29600634C22 /* file.h in Headers */ = {isa = PBXBuildFile; fileRef = C1077A4D183EB29600634C22 /* file.h */; };
|
||||
D4AF3B2F0C41F7A500D46B6B /* list.c in Sources */ = {isa = PBXBuildFile; fileRef = D4AF3B2D0C41F7A500D46B6B /* list.c */; };
|
||||
D4AF3B300C41F7A600D46B6B /* list.h in Headers */ = {isa = PBXBuildFile; fileRef = D4AF3B2E0C41F7A500D46B6B /* list.h */; };
|
||||
E138A9780C04D88F00C5426C /* ProgressGradients.m in Sources */ = {isa = PBXBuildFile; fileRef = E138A9760C04D88F00C5426C /* ProgressGradients.m */; };
|
||||
|
@ -1196,6 +1198,8 @@
|
|||
BEFC1E1F0C07861A00B0BB3C /* clients.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = clients.c; path = libtransmission/clients.c; sourceTree = "<group>"; };
|
||||
C1077A4A183EB29600634C22 /* error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = error.c; path = libtransmission/error.c; sourceTree = "<group>"; };
|
||||
C1077A4B183EB29600634C22 /* error.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = error.h; path = libtransmission/error.h; sourceTree = "<group>"; };
|
||||
C1077A4C183EB29600634C22 /* file-posix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "file-posix.c"; path = "libtransmission/file-posix.c"; sourceTree = "<group>"; };
|
||||
C1077A4D183EB29600634C22 /* file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = file.h; path = libtransmission/file.h; sourceTree = "<group>"; };
|
||||
D4AF3B2D0C41F7A500D46B6B /* list.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = list.c; path = libtransmission/list.c; sourceTree = "<group>"; };
|
||||
D4AF3B2E0C41F7A500D46B6B /* list.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = list.h; path = libtransmission/list.h; sourceTree = "<group>"; };
|
||||
E138A9750C04D88F00C5426C /* ProgressGradients.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ProgressGradients.h; path = macosx/ProgressGradients.h; sourceTree = "<group>"; };
|
||||
|
@ -1652,6 +1656,8 @@
|
|||
children = (
|
||||
C1077A4A183EB29600634C22 /* error.c */,
|
||||
C1077A4B183EB29600634C22 /* error.h */,
|
||||
C1077A4C183EB29600634C22 /* file-posix.c */,
|
||||
C1077A4D183EB29600634C22 /* file.h */,
|
||||
4D80185710BBC0B0008A4AF2 /* magnet.c */,
|
||||
4D80185810BBC0B0008A4AF2 /* magnet.h */,
|
||||
4D8017E810BBC073008A4AF2 /* torrent-magnet.c */,
|
||||
|
@ -2047,6 +2053,7 @@
|
|||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C1077A51183EB29600634C22 /* file.h in Headers */,
|
||||
BEFC1E290C07861A00B0BB3C /* version.h in Headers */,
|
||||
BEFC1E2A0C07861A00B0BB3C /* utils.h in Headers */,
|
||||
BEFC1E2C0C07861A00B0BB3C /* upnp.h in Headers */,
|
||||
|
@ -2678,6 +2685,7 @@
|
|||
4D36BA720CA2F00800A63CA5 /* handshake.c in Sources */,
|
||||
4D36BA740CA2F00800A63CA5 /* peer-io.c in Sources */,
|
||||
4D36BA770CA2F00800A63CA5 /* peer-mgr.c in Sources */,
|
||||
C1077A50183EB29600634C22 /* file-posix.c in Sources */,
|
||||
4D36BA790CA2F00800A63CA5 /* peer-msgs.c in Sources */,
|
||||
A25D2CBD0CF4C73E0096A262 /* stats.c in Sources */,
|
||||
A201527E0D1C270F0081714F /* torrent-ctor.c in Sources */,
|
||||
|
|
|
@ -72,6 +72,12 @@ libtransmission_a_SOURCES = \
|
|||
webseed.c \
|
||||
wildmat.c
|
||||
|
||||
if WIN32
|
||||
libtransmission_a_SOURCES += file-win32.c
|
||||
else
|
||||
libtransmission_a_SOURCES += file-posix.c
|
||||
endif
|
||||
|
||||
noinst_HEADERS = \
|
||||
announcer.h \
|
||||
announcer-common.h \
|
||||
|
@ -85,6 +91,7 @@ noinst_HEADERS = \
|
|||
completion.h \
|
||||
error.h \
|
||||
fdlimit.h \
|
||||
file.h \
|
||||
handshake.h \
|
||||
history.h \
|
||||
inout.h \
|
||||
|
@ -136,6 +143,7 @@ TESTS = \
|
|||
clients-test \
|
||||
crypto-test \
|
||||
error-test \
|
||||
file-test \
|
||||
history-test \
|
||||
json-test \
|
||||
magnet-test \
|
||||
|
@ -191,6 +199,10 @@ error_test_SOURCES = error-test.c $(TEST_SOURCES)
|
|||
error_test_LDADD = ${apps_ldadd}
|
||||
error_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
file_test_SOURCES = file-test.c $(TEST_SOURCES)
|
||||
file_test_LDADD = ${apps_ldadd}
|
||||
file_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
history_test_SOURCES = history-test.c $(TEST_SOURCES)
|
||||
history_test_LDADD = ${apps_ldadd}
|
||||
history_test_LDFLAGS = ${apps_ldflags}
|
||||
|
|
240
libtransmission/file-posix.c
Normal file
240
libtransmission/file-posix.c
Normal file
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#if defined (HAVE_CANONICALIZE_FILE_NAME) && !defined (_GNU_SOURCE)
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h> /* basename (), dirname () */
|
||||
#include <limits.h> /* PATH_MAX */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "transmission.h"
|
||||
#include "file.h"
|
||||
#include "utils.h"
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
static void
|
||||
set_system_error (tr_error ** error,
|
||||
int code)
|
||||
{
|
||||
if (error == NULL)
|
||||
return;
|
||||
|
||||
tr_error_set_literal (error, code, tr_strerror (code));
|
||||
}
|
||||
|
||||
static void
|
||||
set_system_error_if_file_found (tr_error ** error,
|
||||
int code)
|
||||
{
|
||||
if (code != ENOENT)
|
||||
set_system_error (error, code);
|
||||
}
|
||||
|
||||
static void
|
||||
stat_to_sys_path_info (const struct stat * sb,
|
||||
tr_sys_path_info * info)
|
||||
{
|
||||
if (S_ISREG (sb->st_mode))
|
||||
info->type = TR_SYS_PATH_IS_FILE;
|
||||
else if (S_ISDIR (sb->st_mode))
|
||||
info->type = TR_SYS_PATH_IS_DIRECTORY;
|
||||
else
|
||||
info->type = TR_SYS_PATH_IS_OTHER;
|
||||
|
||||
info->size = (uint64_t) sb->st_size;
|
||||
info->last_modified_at = sb->st_mtime;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_path_exists (const char * path,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
assert (path != NULL);
|
||||
|
||||
ret = access (path, F_OK) != -1;
|
||||
|
||||
if (!ret)
|
||||
set_system_error_if_file_found (error, errno);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_path_get_info (const char * path,
|
||||
int flags,
|
||||
tr_sys_path_info * info,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
struct stat sb;
|
||||
|
||||
assert (path != NULL);
|
||||
assert (info != NULL);
|
||||
|
||||
if ((flags & TR_SYS_PATH_NO_FOLLOW) == 0)
|
||||
ret = stat (path, &sb) != -1;
|
||||
else
|
||||
ret = lstat (path, &sb) != -1;
|
||||
|
||||
if (ret)
|
||||
stat_to_sys_path_info (&sb, info);
|
||||
else
|
||||
set_system_error (error, errno);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_path_is_same (const char * path1,
|
||||
const char * path2,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
struct stat sb1, sb2;
|
||||
|
||||
assert (path1 != NULL);
|
||||
assert (path2 != NULL);
|
||||
|
||||
if (stat (path1, &sb1) != -1 && stat (path2, &sb2) != -1)
|
||||
ret = sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino;
|
||||
else
|
||||
set_system_error_if_file_found (error, errno);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
tr_sys_path_resolve (const char * path,
|
||||
tr_error ** error)
|
||||
{
|
||||
char * ret = NULL;
|
||||
char * tmp = NULL;
|
||||
|
||||
assert (path != NULL);
|
||||
|
||||
#if defined (HAVE_CANONICALIZE_FILE_NAME)
|
||||
|
||||
ret = canonicalize_file_name (path);
|
||||
|
||||
#endif
|
||||
|
||||
#if defined (_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L
|
||||
|
||||
/* Better safe than sorry: realpath () officially supports NULL as destination
|
||||
starting off POSIX.1-2008. */
|
||||
|
||||
if (ret == NULL)
|
||||
ret = realpath (path, NULL);
|
||||
|
||||
#endif
|
||||
|
||||
if (ret == NULL)
|
||||
{
|
||||
tmp = tr_new (char, PATH_MAX);
|
||||
ret = realpath (path, tmp);
|
||||
if (ret != NULL)
|
||||
ret = tr_strdup (ret);
|
||||
}
|
||||
|
||||
if (ret == NULL)
|
||||
set_system_error (error, errno);
|
||||
|
||||
tr_free (tmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
tr_sys_path_basename (const char * path,
|
||||
tr_error ** error)
|
||||
{
|
||||
char * ret = NULL;
|
||||
char * tmp;
|
||||
|
||||
assert (path != NULL);
|
||||
|
||||
tmp = tr_strdup (path);
|
||||
ret = basename (tmp);
|
||||
if (ret != NULL)
|
||||
ret = tr_strdup (ret);
|
||||
else
|
||||
set_system_error (error, errno);
|
||||
|
||||
tr_free (tmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
tr_sys_path_dirname (const char * path,
|
||||
tr_error ** error)
|
||||
{
|
||||
char * ret = NULL;
|
||||
char * tmp;
|
||||
|
||||
assert (path != NULL);
|
||||
|
||||
tmp = tr_strdup (path);
|
||||
ret = dirname (tmp);
|
||||
if (ret != NULL)
|
||||
ret = tr_strdup (ret);
|
||||
else
|
||||
set_system_error (error, errno);
|
||||
|
||||
tr_free (tmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_path_rename (const char * src_path,
|
||||
const char * dst_path,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
assert (src_path != NULL);
|
||||
assert (dst_path != NULL);
|
||||
|
||||
ret = rename (src_path, dst_path) != -1;
|
||||
|
||||
if (!ret)
|
||||
set_system_error (error, errno);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_path_remove (const char * path,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
assert (path != NULL);
|
||||
|
||||
ret = remove (path) != -1;
|
||||
|
||||
if (!ret)
|
||||
set_system_error (error, errno);
|
||||
|
||||
return ret;
|
||||
}
|
802
libtransmission/file-test.c
Normal file
802
libtransmission/file-test.c
Normal file
|
@ -0,0 +1,802 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#ifndef WIN32
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "transmission.h"
|
||||
#include "error.h"
|
||||
#include "file.h"
|
||||
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#define NATIVE_PATH_SEP "/"
|
||||
#else
|
||||
#define NATIVE_PATH_SEP "\\"
|
||||
#endif
|
||||
|
||||
static tr_session * session;
|
||||
|
||||
static char *
|
||||
create_test_dir (const char * name)
|
||||
{
|
||||
char * const test_dir = tr_buildPath (tr_sessionGetConfigDir (session), name, NULL);
|
||||
tr_mkdirp (test_dir, 0777);
|
||||
return test_dir;
|
||||
}
|
||||
|
||||
static bool
|
||||
create_symlink (const char * dst_path, const char * src_path, bool dst_is_dir)
|
||||
{
|
||||
#ifndef WIN32
|
||||
|
||||
(void) dst_is_dir;
|
||||
|
||||
return symlink (src_path, dst_path) != -1;
|
||||
|
||||
#else
|
||||
|
||||
wchar_t * wide_src_path;
|
||||
wchar_t * wide_dst_path;
|
||||
bool ret = false;
|
||||
|
||||
wide_src_path = tr_win32_utf8_to_native (src_path, -1);
|
||||
wide_dst_path = tr_win32_utf8_to_native (dst_path, -1);
|
||||
|
||||
ret = CreateSymbolicLinkW (wide_dst_path, wide_src_path,
|
||||
dst_is_dir ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0);
|
||||
|
||||
tr_free (wide_dst_path);
|
||||
tr_free (wide_src_path);
|
||||
|
||||
return ret;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool
|
||||
create_hardlink (const char * dst_path, const char * src_path)
|
||||
{
|
||||
#ifndef WIN32
|
||||
|
||||
return link (src_path, dst_path) != -1;
|
||||
|
||||
#else
|
||||
|
||||
wchar_t * wide_src_path = tr_win32_utf8_to_native (src_path, -1);
|
||||
wchar_t * wide_dst_path = tr_win32_utf8_to_native (dst_path, -1);
|
||||
|
||||
bool ret = CreateHardLinkW (wide_dst_path, wide_src_path, NULL);
|
||||
|
||||
tr_free (wide_dst_path);
|
||||
tr_free (wide_src_path);
|
||||
|
||||
return ret;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
clear_path_info (tr_sys_path_info * info)
|
||||
{
|
||||
info->type = (tr_sys_path_type_t)-1;
|
||||
info->size = (uint64_t)-1;
|
||||
info->last_modified_at = (time_t)-1;
|
||||
}
|
||||
|
||||
static bool
|
||||
path_contains_no_symlinks (const char * path)
|
||||
{
|
||||
const char * p = path;
|
||||
|
||||
while (*p != '\0')
|
||||
{
|
||||
tr_sys_path_info info;
|
||||
char * pathPart;
|
||||
const char * slashPos = strchr (p, '/');
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
const char * backslashPos = strchr (p, '\\');
|
||||
if (slashPos == NULL || (backslashPos != NULL && backslashPos < slashPos))
|
||||
slashPos = backslashPos;
|
||||
|
||||
#endif
|
||||
|
||||
if (slashPos == NULL)
|
||||
slashPos = p + strlen (p) - 1;
|
||||
|
||||
pathPart = tr_strndup (path, slashPos - path + 1);
|
||||
|
||||
if (!tr_sys_path_get_info (pathPart, TR_SYS_PATH_NO_FOLLOW, &info, NULL) ||
|
||||
(info.type != TR_SYS_PATH_IS_FILE && info.type != TR_SYS_PATH_IS_DIRECTORY))
|
||||
{
|
||||
tr_free (pathPart);
|
||||
return false;
|
||||
}
|
||||
|
||||
tr_free (pathPart);
|
||||
|
||||
p = slashPos + 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
test_get_info (void)
|
||||
{
|
||||
char * const test_dir = create_test_dir (__FUNCTION__);
|
||||
tr_sys_path_info info;
|
||||
tr_error * err = NULL;
|
||||
char * path1, * path2;
|
||||
|
||||
path1 = tr_buildPath (test_dir, "a", NULL);
|
||||
path2 = tr_buildPath (test_dir, "b", NULL);
|
||||
|
||||
/* Can't get info of non-existent file/directory */
|
||||
check (!tr_sys_path_get_info (path1, 0, &info, &err));
|
||||
check (err != NULL);
|
||||
tr_error_clear (&err);
|
||||
|
||||
libtest_create_file_with_string_contents (path1, "test");
|
||||
|
||||
/* Good file info */
|
||||
clear_path_info (&info);
|
||||
check (tr_sys_path_get_info (path1, 0, &info, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (TR_SYS_PATH_IS_FILE, info.type);
|
||||
check_int_eq (4, info.size);
|
||||
check (info.last_modified_at >= time(0) - 1 && info.last_modified_at <= time(0));
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
/* Good directory info */
|
||||
tr_mkdirp (path1, 0777);
|
||||
clear_path_info (&info);
|
||||
check (tr_sys_path_get_info (path1, 0, &info, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (TR_SYS_PATH_IS_DIRECTORY, info.type);
|
||||
check (info.size != (uint64_t)-1);
|
||||
check (info.last_modified_at >= time(0) - 1 && info.last_modified_at <= time(0));
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
if (create_symlink (path1, path2, false))
|
||||
{
|
||||
/* Can't get info of non-existent file/directory */
|
||||
check (!tr_sys_path_get_info (path1, 0, &info, &err));
|
||||
check (err != NULL);
|
||||
tr_error_clear (&err);
|
||||
|
||||
libtest_create_file_with_string_contents (path2, "test");
|
||||
|
||||
/* Good file info */
|
||||
clear_path_info (&info);
|
||||
check (tr_sys_path_get_info (path1, 0, &info, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (TR_SYS_PATH_IS_FILE, info.type);
|
||||
check_int_eq (4, info.size);
|
||||
check (info.last_modified_at >= time(0) - 1 && info.last_modified_at <= time(0));
|
||||
|
||||
tr_sys_path_remove (path2, NULL);
|
||||
|
||||
/* Good directory info */
|
||||
tr_mkdirp (path2, 0777);
|
||||
clear_path_info (&info);
|
||||
check (tr_sys_path_get_info (path1, 0, &info, &err));
|
||||
check (err == NULL);
|
||||
check_int_eq (TR_SYS_PATH_IS_DIRECTORY, info.type);
|
||||
check (info.size != (uint64_t)-1);
|
||||
check (info.last_modified_at >= time(0) - 1 && info.last_modified_at <= time(0));
|
||||
|
||||
tr_sys_path_remove (path2, NULL);
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "WARNING: [%s] unable to run symlink tests\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
tr_free (path2);
|
||||
tr_free (path1);
|
||||
|
||||
tr_free (test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_path_exists (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 (test_dir, "b", NULL);
|
||||
|
||||
/* Non-existent file does not exist */
|
||||
check (!tr_sys_path_exists (path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
/* Create file and see that it exists */
|
||||
libtest_create_file_with_string_contents (path1, "test");
|
||||
check (tr_sys_path_exists (path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
/* Create directory and see that it exists */
|
||||
tr_mkdirp (path1, 0777);
|
||||
check (tr_sys_path_exists (path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
if (create_symlink (path1, path2, false))
|
||||
{
|
||||
/* Non-existent file does not exist (via symlink) */
|
||||
check (!tr_sys_path_exists (path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
/* Create file and see that it exists (via symlink) */
|
||||
libtest_create_file_with_string_contents (path2, "test");
|
||||
check (tr_sys_path_exists (path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_path_remove (path2, NULL);
|
||||
|
||||
/* Create directory and see that it exists (via symlink) */
|
||||
tr_mkdirp (path2, 0777);
|
||||
check (tr_sys_path_exists (path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_path_remove (path2, NULL);
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "WARNING: [%s] unable to run symlink tests\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
tr_free (path2);
|
||||
tr_free (path1);
|
||||
|
||||
tr_free (test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_path_is_same (void)
|
||||
{
|
||||
char * const test_dir = create_test_dir (__FUNCTION__);
|
||||
tr_error * err = NULL;
|
||||
char * path1, * path2, * path3;
|
||||
|
||||
path1 = tr_buildPath (test_dir, "a", NULL);
|
||||
path2 = tr_buildPath (test_dir, "b", NULL);
|
||||
path3 = tr_buildPath (path2, "c", NULL);
|
||||
|
||||
/* Two non-existent files are not the same */
|
||||
check (!tr_sys_path_is_same (path1, path1, &err));
|
||||
check (err == NULL);
|
||||
check (!tr_sys_path_is_same (path1, path2, &err));
|
||||
check (err == NULL);
|
||||
|
||||
/* Two same files are the same */
|
||||
libtest_create_file_with_string_contents (path1, "test");
|
||||
check (tr_sys_path_is_same (path1, path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
/* Existent and non-existent files are not the same */
|
||||
check (!tr_sys_path_is_same (path1, path2, &err));
|
||||
check (err == NULL);
|
||||
check (!tr_sys_path_is_same (path2, path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
/* Two separate files (even with same content) are not the same */
|
||||
libtest_create_file_with_string_contents (path2, "test");
|
||||
check (!tr_sys_path_is_same (path1, path2, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
/* Two same directories are the same */
|
||||
tr_mkdirp (path1, 0777);
|
||||
check (tr_sys_path_is_same (path1, path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
/* File and directory are not the same */
|
||||
check (!tr_sys_path_is_same (path1, path2, &err));
|
||||
check (err == NULL);
|
||||
check (!tr_sys_path_is_same (path2, path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_path_remove (path2, NULL);
|
||||
|
||||
/* Two separate directories are not the same */
|
||||
tr_mkdirp (path2, 0777);
|
||||
check (!tr_sys_path_is_same (path1, path2, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
tr_sys_path_remove (path2, NULL);
|
||||
|
||||
if (create_symlink (path1, ".", true))
|
||||
{
|
||||
/* Directory and symlink pointing to it are the same */
|
||||
check (tr_sys_path_is_same (path1, test_dir, &err));
|
||||
check (err == NULL);
|
||||
check (tr_sys_path_is_same (test_dir, path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
/* Non-existent file and symlink are not the same */
|
||||
check (!tr_sys_path_is_same (path1, path2, &err));
|
||||
check (err == NULL);
|
||||
check (!tr_sys_path_is_same (path2, path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
/* Symlinks pointing to different directories are not the same */
|
||||
create_symlink (path2, "..", true);
|
||||
check (!tr_sys_path_is_same (path1, path2, &err));
|
||||
check (err == NULL);
|
||||
check (!tr_sys_path_is_same (path2, path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_path_remove (path2, NULL);
|
||||
|
||||
/* Symlinks pointing to same directory are the same */
|
||||
create_symlink (path2, ".", true);
|
||||
check (tr_sys_path_is_same (path1, path2, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_path_remove (path2, NULL);
|
||||
|
||||
/* Directory and symlink pointing to another directory are not the same */
|
||||
tr_mkdirp (path2, 0777);
|
||||
check (!tr_sys_path_is_same (path1, path2, &err));
|
||||
check (err == NULL);
|
||||
check (!tr_sys_path_is_same (path2, path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
/* Symlinks pointing to same directory are the same */
|
||||
create_symlink (path3, "..", true);
|
||||
check (tr_sys_path_is_same (path1, path3, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
/* File and symlink pointing to directory are not the same */
|
||||
libtest_create_file_with_string_contents (path1, "test");
|
||||
check (!tr_sys_path_is_same (path1, path3, &err));
|
||||
check (err == NULL);
|
||||
check (!tr_sys_path_is_same (path3, path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_path_remove (path3, NULL);
|
||||
|
||||
/* File and symlink pointing to same file are the same */
|
||||
create_symlink (path3, path1, false);
|
||||
check (tr_sys_path_is_same (path1, path3, &err));
|
||||
check (err == NULL);
|
||||
check (tr_sys_path_is_same (path3, path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
/* Symlinks pointing to non-existent files are not the same */
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
create_symlink (path1, "missing", false);
|
||||
tr_sys_path_remove (path3, NULL);
|
||||
create_symlink (path3, "missing", false);
|
||||
check (!tr_sys_path_is_same (path1, path3, &err));
|
||||
check (err == NULL);
|
||||
check (!tr_sys_path_is_same (path3, path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_path_remove (path3, NULL);
|
||||
|
||||
/* Symlinks pointing to same non-existent file are not the same */
|
||||
create_symlink (path3, ".." NATIVE_PATH_SEP "missing", false);
|
||||
check (!tr_sys_path_is_same (path1, path3, &err));
|
||||
check (err == NULL);
|
||||
check (!tr_sys_path_is_same (path3, path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
/* Non-existent file and symlink pointing to non-existent file are not the same */
|
||||
tr_sys_path_remove (path3, NULL);
|
||||
check (!tr_sys_path_is_same (path1, path3, &err));
|
||||
check (err == NULL);
|
||||
check (!tr_sys_path_is_same (path3, path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_path_remove (path2, NULL);
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "WARNING: [%s] unable to run symlink tests\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
tr_free (path3);
|
||||
path3 = tr_buildPath (test_dir, "c", NULL);
|
||||
|
||||
libtest_create_file_with_string_contents (path1, "test");
|
||||
|
||||
if (create_hardlink (path2, path1))
|
||||
{
|
||||
/* File and hardlink to it are the same */
|
||||
check (tr_sys_path_is_same (path1, path2, &err));
|
||||
check (err == NULL);
|
||||
|
||||
/* Two hardlinks to the same file are the same */
|
||||
create_hardlink (path3, path2);
|
||||
check (tr_sys_path_is_same (path2, path3, &err));
|
||||
check (err == NULL);
|
||||
check (tr_sys_path_is_same (path1, path3, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_path_remove (path2, NULL);
|
||||
|
||||
check (tr_sys_path_is_same (path1, path3, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_path_remove (path3, NULL);
|
||||
|
||||
/* File and hardlink to another file are not the same */
|
||||
libtest_create_file_with_string_contents (path3, "test");
|
||||
create_hardlink (path2, path3);
|
||||
check (!tr_sys_path_is_same (path1, path2, &err));
|
||||
check (err == NULL);
|
||||
check (!tr_sys_path_is_same (path2, path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_sys_path_remove (path3, NULL);
|
||||
tr_sys_path_remove (path2, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "WARNING: [%s] unable to run hardlink tests\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
if (create_symlink (path2, path1, false) && create_hardlink (path3, path1))
|
||||
{
|
||||
check (tr_sys_path_is_same (path2, path3, &err));
|
||||
check (err == NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "WARNING: [%s] unable to run combined symlink and hardlink tests\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
tr_sys_path_remove (path3, NULL);
|
||||
tr_sys_path_remove (path2, NULL);
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
tr_free (path3);
|
||||
tr_free (path2);
|
||||
tr_free (path1);
|
||||
|
||||
tr_free (test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_path_resolve (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 (test_dir, "b", NULL);
|
||||
|
||||
libtest_create_file_with_string_contents (path1, "test");
|
||||
if (create_symlink (path2, path1, false))
|
||||
{
|
||||
char * tmp;
|
||||
|
||||
tmp = tr_sys_path_resolve (path2, &err);
|
||||
check (tmp != NULL);
|
||||
check (err == NULL);
|
||||
check (path_contains_no_symlinks (tmp));
|
||||
tr_free (tmp);
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
tr_mkdirp (path1, 0755);
|
||||
|
||||
tmp = tr_sys_path_resolve (path2, &err);
|
||||
check (tmp != NULL);
|
||||
check (err == NULL);
|
||||
check (path_contains_no_symlinks (tmp));
|
||||
tr_free (tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "WARNING: [%s] unable to run symlink tests\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
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_path_basename_dirname (void)
|
||||
{
|
||||
tr_error * err = NULL;
|
||||
char * name;
|
||||
|
||||
name = tr_sys_path_basename ("/a/b/c", &err);
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq ("c", name);
|
||||
tr_free (name);
|
||||
|
||||
name = tr_sys_path_basename ("", &err);
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq (".", name);
|
||||
tr_free (name);
|
||||
|
||||
name = tr_sys_path_dirname ("/a/b/c", &err);
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq ("/a/b", name);
|
||||
tr_free (name);
|
||||
|
||||
name = tr_sys_path_dirname ("a/b/c", &err);
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq ("a/b", name);
|
||||
tr_free (name);
|
||||
|
||||
name = tr_sys_path_dirname ("a", &err);
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq (".", name);
|
||||
tr_free (name);
|
||||
|
||||
name = tr_sys_path_dirname ("", &err);
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq (".", name);
|
||||
tr_free (name);
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
name = tr_sys_path_basename ("c:\\a\\b\\c", &err);
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq ("c", name);
|
||||
tr_free (name);
|
||||
|
||||
name = tr_sys_path_dirname ("C:\\a/b\\c", &err);
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq ("C:\\a/b", name);
|
||||
tr_free (name);
|
||||
|
||||
name = tr_sys_path_dirname ("a/b\\c", &err);
|
||||
check (name != NULL);
|
||||
check (err == NULL);
|
||||
check_streq ("a/b", name);
|
||||
tr_free (name);
|
||||
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_path_rename (void)
|
||||
{
|
||||
char * const test_dir = create_test_dir (__FUNCTION__);
|
||||
tr_error * err = NULL;
|
||||
char * path1, * path2, * path3;
|
||||
|
||||
path1 = tr_buildPath (test_dir, "a", NULL);
|
||||
path2 = tr_buildPath (test_dir, "b", NULL);
|
||||
path3 = tr_buildPath (path2, "c", NULL);
|
||||
|
||||
libtest_create_file_with_string_contents (path1, "test");
|
||||
|
||||
/* Preconditions */
|
||||
check (tr_sys_path_exists (path1, NULL));
|
||||
check (!tr_sys_path_exists (path2, NULL));
|
||||
|
||||
/* Forward rename works */
|
||||
check (tr_sys_path_rename (path1, path2, &err));
|
||||
check (!tr_sys_path_exists (path1, NULL));
|
||||
check (tr_sys_path_exists (path2, NULL));
|
||||
check (err == NULL);
|
||||
|
||||
/* Backward rename works */
|
||||
check (tr_sys_path_rename (path2, path1, &err));
|
||||
check (tr_sys_path_exists (path1, NULL));
|
||||
check (!tr_sys_path_exists (path2, NULL));
|
||||
check (err == NULL);
|
||||
|
||||
/* Another backward rename [of non-existent file] does not work */
|
||||
check (!tr_sys_path_rename (path2, path1, &err));
|
||||
check (err != NULL);
|
||||
tr_error_clear (&err);
|
||||
|
||||
/* Rename to file which couldn't be created does not work */
|
||||
check (!tr_sys_path_rename (path1, path3, &err));
|
||||
check (err != NULL);
|
||||
tr_error_clear (&err);
|
||||
|
||||
/* Rename of non-existent file does not work */
|
||||
check (!tr_sys_path_rename (path3, path2, &err));
|
||||
check (err != NULL);
|
||||
tr_error_clear (&err);
|
||||
|
||||
libtest_create_file_with_string_contents (path2, "test");
|
||||
|
||||
/* Renaming file does overwrite existing file */
|
||||
check (tr_sys_path_rename (path2, path1, &err));
|
||||
check (err == NULL);
|
||||
|
||||
tr_mkdirp (path2, 0777);
|
||||
|
||||
/* Renaming file does not overwrite existing directory, and vice versa */
|
||||
check (!tr_sys_path_rename (path1, path2, &err));
|
||||
check (err != NULL);
|
||||
tr_error_clear (&err);
|
||||
check (!tr_sys_path_rename (path2, path1, &err));
|
||||
check (err != NULL);
|
||||
tr_error_clear (&err);
|
||||
|
||||
tr_sys_path_remove (path2, NULL);
|
||||
|
||||
tr_free (path3);
|
||||
path3 = tr_buildPath (test_dir, "c", NULL);
|
||||
|
||||
if (create_symlink (path2, path1, false))
|
||||
{
|
||||
/* Preconditions */
|
||||
check (tr_sys_path_exists (path2, NULL));
|
||||
check (!tr_sys_path_exists (path3, NULL));
|
||||
check (tr_sys_path_is_same (path1, path2, NULL));
|
||||
|
||||
/* Rename of symlink works, files stay the same */
|
||||
check (tr_sys_path_rename (path2, path3, &err));
|
||||
check (err == NULL);
|
||||
check (!tr_sys_path_exists (path2, NULL));
|
||||
check (tr_sys_path_exists (path3, NULL));
|
||||
check (tr_sys_path_is_same (path1, path3, NULL));
|
||||
|
||||
tr_sys_path_remove (path3, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "WARNING: [%s] unable to run symlink tests\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
if (create_hardlink (path2, path1))
|
||||
{
|
||||
/* Preconditions */
|
||||
check (tr_sys_path_exists (path2, NULL));
|
||||
check (!tr_sys_path_exists (path3, NULL));
|
||||
check (tr_sys_path_is_same (path1, path2, NULL));
|
||||
|
||||
/* Rename of hardlink works, files stay the same */
|
||||
check (tr_sys_path_rename (path2, path3, &err));
|
||||
check (err == NULL);
|
||||
check (!tr_sys_path_exists (path2, NULL));
|
||||
check (tr_sys_path_exists (path3, NULL));
|
||||
check (tr_sys_path_is_same (path1, path3, NULL));
|
||||
|
||||
tr_sys_path_remove (path3, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "WARNING: [%s] unable to run hardlink tests\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
tr_sys_path_remove (path1, NULL);
|
||||
|
||||
tr_free (path3);
|
||||
tr_free (path2);
|
||||
tr_free (path1);
|
||||
|
||||
tr_free (test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_path_remove (void)
|
||||
{
|
||||
char * const test_dir = create_test_dir (__FUNCTION__);
|
||||
tr_error * err = NULL;
|
||||
char * path1, * path2, * path3;
|
||||
|
||||
path1 = tr_buildPath (test_dir, "a", NULL);
|
||||
path2 = tr_buildPath (test_dir, "b", NULL);
|
||||
path3 = tr_buildPath (path2, "c", NULL);
|
||||
|
||||
/* Can't remove non-existent file/directory */
|
||||
check (!tr_sys_path_exists (path1, NULL));
|
||||
check (!tr_sys_path_remove (path1, &err));
|
||||
check (err != NULL);
|
||||
check (!tr_sys_path_exists (path1, NULL));
|
||||
tr_error_clear (&err);
|
||||
|
||||
/* Removing file works */
|
||||
libtest_create_file_with_string_contents (path1, "test");
|
||||
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 empty directory works */
|
||||
tr_mkdirp (path1, 0777);
|
||||
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);
|
||||
libtest_create_file_with_string_contents (path3, "test");
|
||||
check (tr_sys_path_exists (path2, NULL));
|
||||
check (tr_sys_path_exists (path3, NULL));
|
||||
check (!tr_sys_path_remove (path2, &err));
|
||||
check (err != NULL);
|
||||
check (tr_sys_path_exists (path2, NULL));
|
||||
check (tr_sys_path_exists (path3, NULL));
|
||||
tr_error_clear (&err);
|
||||
|
||||
tr_sys_path_remove (path3, NULL);
|
||||
tr_sys_path_remove (path2, NULL);
|
||||
|
||||
tr_free (path3);
|
||||
tr_free (path2);
|
||||
tr_free (path1);
|
||||
|
||||
tr_free (test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
const testFunc tests[] =
|
||||
{
|
||||
test_get_info,
|
||||
test_path_exists,
|
||||
test_path_is_same,
|
||||
test_path_resolve,
|
||||
test_path_basename_dirname,
|
||||
test_path_rename,
|
||||
test_path_remove
|
||||
};
|
||||
int ret;
|
||||
|
||||
/* init the session */
|
||||
session = libttest_session_init (NULL);
|
||||
|
||||
ret = runTests (tests, NUM_TESTS (tests));
|
||||
|
||||
if (ret == 0)
|
||||
libttest_session_close (session);
|
||||
|
||||
return ret;
|
||||
}
|
452
libtransmission/file-win32.c
Normal file
452
libtransmission/file-win32.c
Normal file
|
@ -0,0 +1,452 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h> /* _splitpath_s (), _makepath_s () */
|
||||
|
||||
#include "transmission.h"
|
||||
#include "file.h"
|
||||
#include "utils.h"
|
||||
|
||||
/* MSDN (http://msdn.microsoft.com/en-us/library/2k2xf226.aspx) only mentions
|
||||
"i64" suffix for C code, but no warning is issued */
|
||||
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
|
||||
|
||||
static void
|
||||
set_system_error (tr_error ** error,
|
||||
DWORD code)
|
||||
{
|
||||
char * message;
|
||||
|
||||
if (error == NULL)
|
||||
return;
|
||||
|
||||
message = tr_win32_format_message (code);
|
||||
|
||||
if (message != NULL)
|
||||
{
|
||||
tr_error_set_literal (error, code, message);
|
||||
tr_free (message);
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_error_set (error, code, "Unknown error: 0x%08x", code);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_system_error_if_file_found (tr_error ** error,
|
||||
DWORD code)
|
||||
{
|
||||
if (code != ERROR_FILE_NOT_FOUND &&
|
||||
code != ERROR_PATH_NOT_FOUND &&
|
||||
code != ERROR_NO_MORE_FILES)
|
||||
set_system_error (error, code);
|
||||
}
|
||||
|
||||
static time_t
|
||||
filetime_to_unix_time (const FILETIME * t)
|
||||
{
|
||||
uint64_t tmp = 0;
|
||||
|
||||
assert (t != NULL);
|
||||
|
||||
tmp |= t->dwHighDateTime;
|
||||
tmp <<= 32;
|
||||
tmp |= t->dwLowDateTime;
|
||||
tmp /= 10; /* to microseconds */
|
||||
tmp -= DELTA_EPOCH_IN_MICROSECS;
|
||||
|
||||
return tmp / 1000000UL;
|
||||
}
|
||||
|
||||
static void
|
||||
stat_to_sys_path_info (DWORD attributes,
|
||||
DWORD size_low,
|
||||
DWORD size_high,
|
||||
const FILETIME * mtime,
|
||||
tr_sys_path_info * info)
|
||||
{
|
||||
assert (mtime != NULL);
|
||||
assert (info != NULL);
|
||||
|
||||
if (attributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
info->type = TR_SYS_PATH_IS_DIRECTORY;
|
||||
else if (!(attributes & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_VIRTUAL)))
|
||||
info->type = TR_SYS_PATH_IS_FILE;
|
||||
else
|
||||
info->type = TR_SYS_PATH_IS_OTHER;
|
||||
|
||||
info->size = size_high;
|
||||
info->size <<= 32;
|
||||
info->size |= size_low;
|
||||
|
||||
info->last_modified_at = filetime_to_unix_time (mtime);
|
||||
}
|
||||
|
||||
static bool
|
||||
get_file_info (HANDLE handle,
|
||||
tr_sys_path_info * info,
|
||||
tr_error ** error);
|
||||
|
||||
bool
|
||||
tr_sys_path_exists (const char * path,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
wchar_t * wide_path;
|
||||
HANDLE handle = INVALID_HANDLE_VALUE;
|
||||
|
||||
assert (path != NULL);
|
||||
|
||||
wide_path = tr_win32_utf8_to_native (path, -1);
|
||||
|
||||
if (wide_path != NULL)
|
||||
{
|
||||
DWORD attributes = GetFileAttributesW (wide_path);
|
||||
if (attributes != INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
if (attributes & FILE_ATTRIBUTE_REPARSE_POINT)
|
||||
{
|
||||
handle = CreateFileW (wide_path, 0, 0, NULL, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
|
||||
ret = handle != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
set_system_error_if_file_found (error, GetLastError ());
|
||||
|
||||
if (handle != INVALID_HANDLE_VALUE)
|
||||
CloseHandle (handle);
|
||||
|
||||
tr_free (wide_path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_path_get_info (const char * path,
|
||||
int flags,
|
||||
tr_sys_path_info * info,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
wchar_t * wide_path;
|
||||
|
||||
assert (path != NULL);
|
||||
assert (info != NULL);
|
||||
|
||||
wide_path = tr_win32_utf8_to_native (path, -1);
|
||||
|
||||
if ((flags & TR_SYS_PATH_NO_FOLLOW) == 0)
|
||||
{
|
||||
HANDLE handle = INVALID_HANDLE_VALUE;
|
||||
|
||||
if (wide_path != NULL)
|
||||
handle = CreateFileW (wide_path, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
|
||||
if (handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
tr_error * my_error = NULL;
|
||||
ret = get_file_info (handle, info, &my_error);
|
||||
if (!ret)
|
||||
tr_error_propagate (error, &my_error);
|
||||
CloseHandle (handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_system_error (error, GetLastError ());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WIN32_FILE_ATTRIBUTE_DATA attributes;
|
||||
|
||||
if (wide_path != NULL)
|
||||
ret = GetFileAttributesExW (wide_path, GetFileExInfoStandard, &attributes);
|
||||
|
||||
if (ret)
|
||||
stat_to_sys_path_info (attributes.dwFileAttributes, attributes.nFileSizeLow,
|
||||
attributes.nFileSizeHigh, &attributes.ftLastWriteTime,
|
||||
info);
|
||||
else
|
||||
set_system_error (error, GetLastError ());
|
||||
}
|
||||
|
||||
tr_free (wide_path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_path_is_same (const char * path1,
|
||||
const char * path2,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
wchar_t * wide_path1 = NULL;
|
||||
wchar_t * wide_path2 = NULL;
|
||||
HANDLE handle1 = INVALID_HANDLE_VALUE;
|
||||
HANDLE handle2 = INVALID_HANDLE_VALUE;
|
||||
BY_HANDLE_FILE_INFORMATION fi1, fi2;
|
||||
|
||||
assert (path1 != NULL);
|
||||
assert (path2 != NULL);
|
||||
|
||||
wide_path1 = tr_win32_utf8_to_native (path1, -1);
|
||||
if (wide_path1 == NULL)
|
||||
goto fail;
|
||||
|
||||
wide_path2 = tr_win32_utf8_to_native (path2, -1);
|
||||
if (wide_path2 == NULL)
|
||||
goto fail;
|
||||
|
||||
handle1 = CreateFileW (wide_path1, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (handle1 == INVALID_HANDLE_VALUE)
|
||||
goto fail;
|
||||
|
||||
handle2 = CreateFileW (wide_path2, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (handle2 == INVALID_HANDLE_VALUE)
|
||||
goto fail;
|
||||
|
||||
/* TODO: Use GetFileInformationByHandleEx on >= Server 2012 */
|
||||
|
||||
if (!GetFileInformationByHandle (handle1, &fi1) || !GetFileInformationByHandle (handle2, &fi2))
|
||||
goto fail;
|
||||
|
||||
ret = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber &&
|
||||
fi1.nFileIndexHigh == fi2.nFileIndexHigh &&
|
||||
fi1.nFileIndexLow == fi2.nFileIndexLow;
|
||||
|
||||
goto cleanup;
|
||||
|
||||
fail:
|
||||
set_system_error_if_file_found (error, GetLastError ());
|
||||
|
||||
cleanup:
|
||||
CloseHandle (handle2);
|
||||
CloseHandle (handle1);
|
||||
|
||||
tr_free (wide_path2);
|
||||
tr_free (wide_path1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
tr_sys_path_resolve (const char * path,
|
||||
tr_error ** error)
|
||||
{
|
||||
char * ret = NULL;
|
||||
wchar_t * wide_path;
|
||||
wchar_t * wide_ret = NULL;
|
||||
HANDLE handle;
|
||||
DWORD wide_ret_size;
|
||||
|
||||
assert (path != NULL);
|
||||
|
||||
wide_path = tr_win32_utf8_to_native (path, -1);
|
||||
if (wide_path == NULL)
|
||||
goto fail;
|
||||
|
||||
handle = CreateFileW (wide_path, FILE_READ_EA,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
goto fail;
|
||||
|
||||
wide_ret_size = GetFinalPathNameByHandleW (handle, NULL, 0, 0);
|
||||
if (wide_ret_size == 0)
|
||||
goto fail;
|
||||
|
||||
wide_ret = tr_new (wchar_t, wide_ret_size);
|
||||
if (GetFinalPathNameByHandleW (handle, wide_ret, wide_ret_size, 0) != wide_ret_size - 1)
|
||||
goto fail;
|
||||
|
||||
/* Resolved path always begins with "\\?\", so skip those first four chars. */
|
||||
ret = tr_win32_native_to_utf8 (wide_ret + 4, -1);
|
||||
if (ret != NULL)
|
||||
goto cleanup;
|
||||
|
||||
fail:
|
||||
set_system_error (error, GetLastError ());
|
||||
|
||||
tr_free (ret);
|
||||
ret = NULL;
|
||||
|
||||
cleanup:
|
||||
tr_free (wide_ret);
|
||||
tr_free (wide_path);
|
||||
|
||||
if (handle != INVALID_HANDLE_VALUE)
|
||||
CloseHandle (handle);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
tr_sys_path_basename (const char * path,
|
||||
tr_error ** error)
|
||||
{
|
||||
char fname[_MAX_FNAME], ext[_MAX_EXT];
|
||||
|
||||
assert (path != NULL);
|
||||
|
||||
/* TODO: Error handling */
|
||||
|
||||
if (_splitpath_s (path, NULL, 0, NULL, 0, fname, sizeof (fname), ext, sizeof (ext)) == 0 &&
|
||||
(*fname != '\0' || *ext != '\0'))
|
||||
{
|
||||
const size_t tmp_len = strlen (fname) + strlen (ext) + 2;
|
||||
char * const tmp = tr_new (char, tmp_len);
|
||||
if (_makepath_s (tmp, tmp_len, NULL, NULL, fname, ext) == 0)
|
||||
return tmp;
|
||||
tr_free (tmp);
|
||||
}
|
||||
|
||||
return tr_strdup (".");
|
||||
}
|
||||
|
||||
char *
|
||||
tr_sys_path_dirname (const char * path,
|
||||
tr_error ** error)
|
||||
{
|
||||
char drive[_MAX_DRIVE], dir[_MAX_DIR];
|
||||
|
||||
assert (path != NULL);
|
||||
|
||||
/* TODO: Error handling */
|
||||
|
||||
if (_splitpath_s (path, drive, sizeof (drive), dir, sizeof (dir), NULL, 0, NULL, 0) == 0 &&
|
||||
(*drive != '\0' || *dir != '\0'))
|
||||
{
|
||||
const size_t tmp_len = strlen (drive) + strlen (dir) + 2;
|
||||
char * const tmp = tr_new (char, tmp_len);
|
||||
if (_makepath_s (tmp, tmp_len, drive, dir, NULL, NULL) == 0)
|
||||
{
|
||||
size_t len = strlen(tmp);
|
||||
while (len > 0 && (tmp[len - 1] == '/' || tmp[len - 1] == '\\'))
|
||||
tmp[--len] = '\0';
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
tr_free (tmp);
|
||||
}
|
||||
|
||||
return tr_strdup (".");
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_path_rename (const char * src_path,
|
||||
const char * dst_path,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
wchar_t * wide_src_path;
|
||||
wchar_t * wide_dst_path;
|
||||
|
||||
assert (src_path != NULL);
|
||||
assert (dst_path != NULL);
|
||||
|
||||
wide_src_path = tr_win32_utf8_to_native (src_path, -1);
|
||||
wide_dst_path = tr_win32_utf8_to_native (dst_path, -1);
|
||||
|
||||
if (wide_src_path != NULL && wide_dst_path != NULL)
|
||||
{
|
||||
DWORD flags = MOVEFILE_REPLACE_EXISTING;
|
||||
DWORD attributes;
|
||||
|
||||
attributes = GetFileAttributesW (wide_src_path);
|
||||
if (attributes != INVALID_FILE_ATTRIBUTES &&
|
||||
(attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
|
||||
{
|
||||
flags = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
attributes = GetFileAttributesW (wide_dst_path);
|
||||
if (attributes != INVALID_FILE_ATTRIBUTES &&
|
||||
(attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
ret = MoveFileExW (wide_src_path, wide_dst_path, flags);
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
set_system_error (error, GetLastError ());
|
||||
|
||||
tr_free (wide_dst_path);
|
||||
tr_free (wide_src_path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
tr_sys_path_remove (const char * path,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret = false;
|
||||
wchar_t * wide_path;
|
||||
|
||||
assert (path != NULL);
|
||||
|
||||
wide_path = tr_win32_utf8_to_native (path, -1);
|
||||
|
||||
if (wide_path != NULL)
|
||||
{
|
||||
const DWORD attributes = GetFileAttributesW (wide_path);
|
||||
|
||||
if (attributes != INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
|
||||
ret = RemoveDirectoryW (wide_path);
|
||||
else
|
||||
ret = DeleteFileW (wide_path);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
set_system_error (error, GetLastError ());
|
||||
|
||||
tr_free (wide_path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
get_file_info (HANDLE handle,
|
||||
tr_sys_path_info * info,
|
||||
tr_error ** error)
|
||||
{
|
||||
bool ret;
|
||||
BY_HANDLE_FILE_INFORMATION attributes;
|
||||
|
||||
assert (handle != INVALID_HANDLE_VALUE);
|
||||
assert (info != NULL);
|
||||
|
||||
ret = GetFileInformationByHandle (handle, &attributes);
|
||||
|
||||
if (ret)
|
||||
stat_to_sys_path_info (attributes.dwFileAttributes, attributes.nFileSizeLow,
|
||||
attributes.nFileSizeHigh, &attributes.ftLastWriteTime,
|
||||
info);
|
||||
else
|
||||
set_system_error (error, GetLastError ());
|
||||
|
||||
return ret;
|
||||
}
|
193
libtransmission/file.h
Normal file
193
libtransmission/file.h
Normal file
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef TR_FILE_H
|
||||
#define TR_FILE_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @addtogroup file_io File IO
|
||||
* @{
|
||||
*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TR_SYS_PATH_NO_FOLLOW = 1 << 0
|
||||
}
|
||||
tr_sys_path_get_info_flags_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TR_SYS_PATH_IS_FILE,
|
||||
TR_SYS_PATH_IS_DIRECTORY,
|
||||
TR_SYS_PATH_IS_OTHER
|
||||
}
|
||||
tr_sys_path_type_t;
|
||||
|
||||
typedef struct tr_sys_path_info
|
||||
{
|
||||
tr_sys_path_type_t type;
|
||||
uint64_t size;
|
||||
time_t last_modified_at;
|
||||
}
|
||||
tr_sys_path_info;
|
||||
|
||||
/**
|
||||
* @name Platform-specific wrapper functions
|
||||
*
|
||||
* Following functions accept paths in UTF-8 encoding and convert them to native
|
||||
* encoding internally if needed.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* Path-related wrappers */
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `stat ()`.
|
||||
*
|
||||
* @param[in] path Path to file or directory.
|
||||
* @param[in] flags Combination of @ref tr_sys_path_get_info_flags_t values.
|
||||
* @param[out] info Result buffer.
|
||||
* @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_path_get_info (const char * path,
|
||||
int flags,
|
||||
tr_sys_path_info * info,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `access ()`.
|
||||
*
|
||||
* @param[in] path Path to file or directory.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you are
|
||||
* not interested in error details.
|
||||
*
|
||||
* @return `True` if path exists, `false` otherwise. Note that `false` will also
|
||||
* be returned in case of error; if you need to distinguish the two,
|
||||
* check if `error` is `NULL` afterwards.
|
||||
*/
|
||||
bool tr_sys_path_exists (const char * path,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Test to see if the two filenames point to the same file.
|
||||
*
|
||||
* @param[in] path1 Path to first file or directory.
|
||||
* @param[in] path2 Path to second file or directory.
|
||||
* @param[out] error Pointer to error object. Optional, pass `NULL` if you are
|
||||
* not interested in error details.
|
||||
*
|
||||
* @return `True` if two paths point to the same file or directory, `false`
|
||||
* otherwise. Note that `false` will also be returned in case of error;
|
||||
* if you need to distinguish the two, check if `error` is `NULL`
|
||||
* afterwards.
|
||||
*/
|
||||
bool tr_sys_path_is_same (const char * path1,
|
||||
const char * path2,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `realpath ()`.
|
||||
*
|
||||
* @param[in] path Path to file or directory.
|
||||
* @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 full path (with symbolic
|
||||
* links, `.` and `..` resolved) on success (use @ref tr_free to free it
|
||||
* when no longer needed), `NULL` otherwise (with `error` set
|
||||
* accordingly).
|
||||
*/
|
||||
char * tr_sys_path_resolve (const char * path,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `basename ()`.
|
||||
*
|
||||
* @param[in] path Path to file or directory.
|
||||
* @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 base name (last path
|
||||
* component; parent path removed) on success (use @ref tr_free to free
|
||||
* it when no longer needed), `NULL` otherwise (with `error` set
|
||||
* accordingly).
|
||||
*/
|
||||
char * tr_sys_path_basename (const char * path,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `dirname ()`.
|
||||
*
|
||||
* @param[in] path Path to file or directory.
|
||||
* @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 directory (parent path;
|
||||
* last path component removed) on success (use @ref tr_free to free it
|
||||
* when no longer needed), `NULL` otherwise (with `error` set
|
||||
* accordingly).
|
||||
*/
|
||||
char * tr_sys_path_dirname (const char * path,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `rename ()`.
|
||||
*
|
||||
* @param[in] src_path Path to source file or directory.
|
||||
* @param[in] dst_path Path to destination file or 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).
|
||||
* Rename will generally only succeed if both source and destination are
|
||||
* on the same partition.
|
||||
*/
|
||||
bool tr_sys_path_rename (const char * src_path,
|
||||
const char * dst_path,
|
||||
tr_error ** error);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `remove ()`.
|
||||
*
|
||||
* @param[in] path Path to file or 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).
|
||||
* Directory removal will only succeed if it is empty (contains no other
|
||||
* files and directories).
|
||||
*/
|
||||
bool tr_sys_path_remove (const char * path,
|
||||
tr_error ** error);
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1316,6 +1316,98 @@ tr_utf8clean (const char * str, int max_len)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
char *
|
||||
tr_win32_native_to_utf8 (const wchar_t * text,
|
||||
int text_size)
|
||||
{
|
||||
char * ret = NULL;
|
||||
int size;
|
||||
|
||||
size = WideCharToMultiByte (CP_UTF8, 0, text, text_size, NULL, 0, NULL, NULL);
|
||||
if (size == 0)
|
||||
goto fail;
|
||||
|
||||
ret = tr_new (char, size + 1);
|
||||
size = WideCharToMultiByte (CP_UTF8, 0, text, text_size, ret, size, NULL, NULL);
|
||||
if (size == 0)
|
||||
goto fail;
|
||||
|
||||
ret[size] = '\0';
|
||||
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
tr_free (ret);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wchar_t *
|
||||
tr_win32_utf8_to_native (const char * text,
|
||||
int text_size)
|
||||
{
|
||||
return tr_win32_utf8_to_native_ex (text, text_size, 0);
|
||||
}
|
||||
|
||||
wchar_t *
|
||||
tr_win32_utf8_to_native_ex (const char * text,
|
||||
int text_size,
|
||||
int extra_chars)
|
||||
{
|
||||
wchar_t * ret = NULL;
|
||||
int size;
|
||||
|
||||
size = MultiByteToWideChar (CP_UTF8, 0, text, text_size, NULL, 0);
|
||||
if (size == 0)
|
||||
goto fail;
|
||||
|
||||
ret = tr_new (wchar_t, size + extra_chars + 1);
|
||||
size = MultiByteToWideChar (CP_UTF8, 0, text, text_size, ret, size);
|
||||
if (size == 0)
|
||||
goto fail;
|
||||
|
||||
ret[size] = L'\0';
|
||||
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
tr_free (ret);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
tr_win32_format_message (uint32_t code)
|
||||
{
|
||||
wchar_t * wide_text = NULL;
|
||||
DWORD wide_size;
|
||||
char * text = NULL;
|
||||
size_t text_size;
|
||||
|
||||
wide_size = FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, code, 0, (LPWSTR)&wide_text, 0, NULL);
|
||||
|
||||
if (wide_size != 0 && wide_text != NULL)
|
||||
text = tr_win32_native_to_utf8 (wide_text, wide_size);
|
||||
|
||||
LocalFree (wide_text);
|
||||
|
||||
/* Most (all?) messages contain "\r\n" in the end, chop it */
|
||||
text_size = strlen (text);
|
||||
while (text_size > 0 &&
|
||||
text[text_size - 1] >= '\0' &&
|
||||
text[text_size - 1] <= ' ')
|
||||
text[--text_size] = '\0';
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
|
|
@ -178,6 +178,18 @@ void tr_wait_msec (long int delay_milliseconds);
|
|||
*/
|
||||
char* tr_utf8clean (const char * str, int len) TR_GNUC_MALLOC;
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
char * tr_win32_native_to_utf8 (const wchar_t * text,
|
||||
int text_size);
|
||||
wchar_t * tr_win32_utf8_to_native (const char * text,
|
||||
int text_size);
|
||||
wchar_t * tr_win32_utf8_to_native_ex (const char * text,
|
||||
int text_size,
|
||||
int extra_chars);
|
||||
char * tr_win32_format_message (uint32_t code);
|
||||
|
||||
#endif
|
||||
|
||||
/***
|
||||
****
|
||||
|
|
Loading…
Add table
Reference in a new issue