From: Jordan Lee Date: Sun, 21 Sep 2014 17:52:36 +0000 (+0000) Subject: (trunk, libt) #4160 - the slow slog to catch trunk up to mike.dld's 4160 diff continu... X-Git-Tag: 2.90~362 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=51a90d0da04a9752bca896c3791cb44aacf13927;p=transmission (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. --- diff --git a/libtransmission/file-posix.c b/libtransmission/file-posix.c index afcc0b212..a5fa7ec0d 100644 --- a/libtransmission/file-posix.c +++ b/libtransmission/file-posix.c @@ -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 +#include #include #include /* O_LARGEFILE, posix_fadvise (), [posix_]fallocate () */ #include /* 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; +} diff --git a/libtransmission/file-test.c b/libtransmission/file-test.c index 19a3d69b6..f21b9eaef 100644 --- a/libtransmission/file-test.c +++ b/libtransmission/file-test.c @@ -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; diff --git a/libtransmission/file-win32.c b/libtransmission/file-win32.c index f5340d31f..d9028ca94 100644 --- a/libtransmission/file-win32.c +++ b/libtransmission/file-win32.c @@ -10,6 +10,7 @@ #include #include /* _splitpath_s (), _makepath_s () */ +#include /* SHCreateDirectoryEx () */ #include /* 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; +} diff --git a/libtransmission/file.h b/libtransmission/file.h index d75131015..5ab191380 100644 --- a/libtransmission/file.h +++ b/libtransmission/file.h @@ -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); + /** @} */ /** @} */ diff --git a/libtransmission/quark.h b/libtransmission/quark.h index 2e3a9fbf2..545fc68a7 100644 --- a/libtransmission/quark.h +++ b/libtransmission/quark.h @@ -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