(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:
Jordan Lee 2014-09-21 17:52:36 +00:00
parent 728957c46a
commit 51a90d0da0
5 changed files with 730 additions and 18 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
/** @} */
/** @} */

View File

@ -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