unix,win: add uv_os_homedir()
PR-URL: https://github.com/libuv/libuv/pull/350 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
This commit is contained in:
parent
d1b5008e76
commit
a62c2d5928
@ -45,7 +45,7 @@ include_HEADERS += include/uv-win.h include/tree.h
|
||||
AM_CPPFLAGS += -I$(top_srcdir)/src/win \
|
||||
-DWIN32_LEAN_AND_MEAN \
|
||||
-D_WIN32_WINNT=0x0600
|
||||
LIBS += -lws2_32 -lpsapi -liphlpapi -lshell32
|
||||
LIBS += -lws2_32 -lpsapi -liphlpapi -lole32 -lshell32
|
||||
libuv_la_SOURCES += src/win/async.c \
|
||||
src/win/atomicops-inl.h \
|
||||
src/win/core.c \
|
||||
@ -165,6 +165,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
||||
test/test-getnameinfo.c \
|
||||
test/test-getsockname.c \
|
||||
test/test-handle-fileno.c \
|
||||
test/test-homedir.c \
|
||||
test/test-hrtime.c \
|
||||
test/test-idle.c \
|
||||
test/test-ip4-addr.c \
|
||||
|
||||
@ -103,6 +103,7 @@ test/test-get-loadavg.c
|
||||
test/test-get-memory.c
|
||||
test/test-getaddrinfo.c
|
||||
test/test-getsockname.c
|
||||
test/test-homedir.c
|
||||
test/test-hrtime.c
|
||||
test/test-idle.c
|
||||
test/test-ip6-addr.c
|
||||
|
||||
@ -227,6 +227,23 @@ API
|
||||
|
||||
Changes the current working directory.
|
||||
|
||||
.. c:function:: int uv_os_homedir(char* buffer, size_t* size)
|
||||
|
||||
Gets the current user's home directory. On Windows, `uv_os_homedir()` first
|
||||
checks the `USERPROFILE` environment variable using
|
||||
`GetEnvironmentVariableW()`. If `USERPROFILE` is not set,
|
||||
`SHGetKnownFolderPath()` is called. On all other operating systems,
|
||||
`uv_os_homedir()` first checks the `HOME` environment variable using
|
||||
:man:`getenv(3)`. If `HOME` is not set, :man:`getpwuid_r(3)` is called. The
|
||||
user's home directory is stored in `buffer`. When `uv_os_homedir()` is
|
||||
called, `size` indicates the maximum size of `buffer`. On success or
|
||||
`UV_ENOBUFS` failure, `size` is set to the string length of `buffer`.
|
||||
|
||||
.. warning::
|
||||
`uv_os_homedir()` is not thread safe.
|
||||
|
||||
.. versionadded:: 1.6.0
|
||||
|
||||
.. uint64_t uv_get_free_memory(void)
|
||||
.. c:function:: uint64_t uv_get_total_memory(void)
|
||||
|
||||
|
||||
@ -1028,6 +1028,8 @@ typedef struct {
|
||||
|
||||
UV_EXTERN int uv_getrusage(uv_rusage_t* rusage);
|
||||
|
||||
UV_EXTERN int uv_os_homedir(char* buffer, size_t* size);
|
||||
|
||||
UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count);
|
||||
UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count);
|
||||
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
#include <limits.h> /* INT_MAX, PATH_MAX */
|
||||
#include <sys/uio.h> /* writev */
|
||||
#include <sys/resource.h> /* getrusage */
|
||||
#include <pwd.h>
|
||||
|
||||
#ifdef __linux__
|
||||
# include <sys/ioctl.h>
|
||||
@ -986,3 +987,82 @@ int uv__dup2_cloexec(int oldfd, int newfd) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv_os_homedir(char* buffer, size_t* size) {
|
||||
struct passwd pw;
|
||||
struct passwd* result;
|
||||
char* buf;
|
||||
uid_t uid;
|
||||
size_t bufsize;
|
||||
size_t len;
|
||||
int r;
|
||||
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check if the HOME environment variable is set first */
|
||||
buf = getenv("HOME");
|
||||
|
||||
if (buf != NULL) {
|
||||
len = strlen(buf);
|
||||
|
||||
if (len >= *size) {
|
||||
*size = len;
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
memcpy(buffer, buf, len + 1);
|
||||
*size = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* HOME is not set, so call getpwuid() */
|
||||
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
|
||||
if (bufsize <= 0)
|
||||
return -EIO;
|
||||
|
||||
uid = getuid();
|
||||
buf = NULL;
|
||||
|
||||
for (;;) {
|
||||
free(buf);
|
||||
buf = malloc(bufsize);
|
||||
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
r = getpwuid_r(uid, &pw, buf, bufsize, &result);
|
||||
|
||||
if (r != ERANGE)
|
||||
break;
|
||||
|
||||
bufsize *= 2;
|
||||
}
|
||||
|
||||
if (r != 0) {
|
||||
free(buf);
|
||||
return -r;
|
||||
}
|
||||
|
||||
if (result == NULL) {
|
||||
free(buf);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
len = strlen(pw.pw_dir);
|
||||
|
||||
if (len >= *size) {
|
||||
*size = len;
|
||||
free(buf);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
memcpy(buffer, pw.pw_dir, len + 1);
|
||||
*size = len;
|
||||
free(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -36,6 +36,8 @@
|
||||
#include <psapi.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#include <objbase.h>
|
||||
|
||||
|
||||
/*
|
||||
@ -72,7 +74,7 @@ void uv__util_init() {
|
||||
InitializeCriticalSection(&process_title_lock);
|
||||
|
||||
/* Retrieve high-resolution timer frequency
|
||||
* and precompute its reciprocal.
|
||||
* and precompute its reciprocal.
|
||||
*/
|
||||
if (QueryPerformanceFrequency(&perf_frequency)) {
|
||||
hrtime_interval_ = 1.0 / perf_frequency.QuadPart;
|
||||
@ -801,8 +803,8 @@ static int is_windows_version_or_greater(DWORD os_major,
|
||||
|
||||
/* Perform the test. */
|
||||
return (int) VerifyVersionInfo(
|
||||
&osvi,
|
||||
VER_MAJORVERSION | VER_MINORVERSION |
|
||||
&osvi,
|
||||
VER_MAJORVERSION | VER_MINORVERSION |
|
||||
VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
||||
condition_mask);
|
||||
}
|
||||
@ -870,7 +872,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
|
||||
flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
|
||||
GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_PREFIX;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Fetch the size of the adapters reported by windows, and then get the */
|
||||
/* list itself. */
|
||||
@ -1053,14 +1055,14 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
|
||||
prefix->PrefixLength <= prefix_len)
|
||||
continue;
|
||||
|
||||
if (address_prefix_match(sa->sa_family, sa,
|
||||
if (address_prefix_match(sa->sa_family, sa,
|
||||
prefix->Address.lpSockaddr, prefix->PrefixLength)) {
|
||||
prefix_len = prefix->PrefixLength;
|
||||
}
|
||||
}
|
||||
|
||||
/* If there is no matching prefix information, return a single-host
|
||||
* subnet mask (e.g. 255.255.255.255 for IPv4).
|
||||
* subnet mask (e.g. 255.255.255.255 for IPv4).
|
||||
*/
|
||||
if (!prefix_len)
|
||||
prefix_len = (sa->sa_family == AF_INET6) ? 128 : 32;
|
||||
@ -1153,3 +1155,67 @@ int uv_getrusage(uv_rusage_t *uv_rusage) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_os_homedir(char* buffer, size_t* size) {
|
||||
wchar_t* path;
|
||||
size_t bufsize;
|
||||
size_t len;
|
||||
int r;
|
||||
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
/* Check if the USERPROFILE environment variable is set first */
|
||||
path = malloc(*size * sizeof(WCHAR));
|
||||
|
||||
if (path == NULL)
|
||||
return UV_ENOMEM;
|
||||
|
||||
len = GetEnvironmentVariableW(L"USERPROFILE", path, *size);
|
||||
|
||||
if (len == 0) {
|
||||
r = GetLastError();
|
||||
free(path);
|
||||
|
||||
if (r != ERROR_ENVVAR_NOT_FOUND)
|
||||
return uv_translate_sys_error(r);
|
||||
} else {
|
||||
if (len > *size) {
|
||||
free(path);
|
||||
*size = len - 1;
|
||||
return UV_ENOBUFS;
|
||||
}
|
||||
|
||||
bufsize = uv_utf16_to_utf8(path, -1, buffer, *size);
|
||||
assert(len + 1 == bufsize);
|
||||
free(path);
|
||||
*size = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* USERPROFILE is not set, so call SHGetKnownFolderPath() */
|
||||
if (SHGetKnownFolderPath(&FOLDERID_Profile, 0, NULL, &path) != S_OK)
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
|
||||
bufsize = uv_utf16_to_utf8(path, -1, buffer, *size);
|
||||
|
||||
if (bufsize == 0) {
|
||||
r = GetLastError();
|
||||
|
||||
if (r == ERROR_INSUFFICIENT_BUFFER) {
|
||||
*size = wcslen(path);
|
||||
CoTaskMemFree(path);
|
||||
return UV_ENOBUFS;
|
||||
}
|
||||
|
||||
CoTaskMemFree(path);
|
||||
return uv_translate_sys_error(r);
|
||||
}
|
||||
|
||||
CoTaskMemFree(path);
|
||||
*size = bufsize - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
49
test/test-homedir.c
Normal file
49
test/test-homedir.c
Normal file
@ -0,0 +1,49 @@
|
||||
#include "uv.h"
|
||||
#include "task.h"
|
||||
#include <string.h>
|
||||
|
||||
#define PATHMAX 1024
|
||||
#define SMALLPATH 1
|
||||
|
||||
TEST_IMPL(homedir) {
|
||||
char homedir[PATHMAX];
|
||||
size_t len;
|
||||
char last;
|
||||
int r;
|
||||
|
||||
/* Test the normal case */
|
||||
len = sizeof homedir;
|
||||
homedir[0] = '\0';
|
||||
ASSERT(strlen(homedir) == 0);
|
||||
r = uv_os_homedir(homedir, &len);
|
||||
ASSERT(r == 0);
|
||||
ASSERT(strlen(homedir) == len);
|
||||
ASSERT(len > 0);
|
||||
ASSERT(homedir[len] == '\0');
|
||||
|
||||
if (len > 1) {
|
||||
last = homedir[len - 1];
|
||||
#ifdef _WIN32
|
||||
ASSERT(last != '\\');
|
||||
#else
|
||||
ASSERT(last != '/');
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Test the case where the buffer is too small */
|
||||
len = SMALLPATH;
|
||||
r = uv_os_homedir(homedir, &len);
|
||||
ASSERT(r == UV_ENOBUFS);
|
||||
ASSERT(len > SMALLPATH);
|
||||
|
||||
/* Test invalid inputs */
|
||||
r = uv_os_homedir(NULL, &len);
|
||||
ASSERT(r == UV_EINVAL);
|
||||
r = uv_os_homedir(homedir, NULL);
|
||||
ASSERT(r == UV_EINVAL);
|
||||
len = 0;
|
||||
r = uv_os_homedir(homedir, &len);
|
||||
ASSERT(r == UV_EINVAL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -183,6 +183,7 @@ TEST_DECLARE (process_title)
|
||||
TEST_DECLARE (cwd_and_chdir)
|
||||
TEST_DECLARE (get_memory)
|
||||
TEST_DECLARE (handle_fileno)
|
||||
TEST_DECLARE (homedir)
|
||||
TEST_DECLARE (hrtime)
|
||||
TEST_DECLARE (getaddrinfo_fail)
|
||||
TEST_DECLARE (getaddrinfo_fail_sync)
|
||||
@ -542,6 +543,8 @@ TASK_LIST_START
|
||||
|
||||
TEST_ENTRY (handle_fileno)
|
||||
|
||||
TEST_ENTRY (homedir)
|
||||
|
||||
TEST_ENTRY (hrtime)
|
||||
|
||||
TEST_ENTRY_CUSTOM (getaddrinfo_fail, 0, 0, 10000)
|
||||
|
||||
2
uv.gyp
2
uv.gyp
@ -108,6 +108,7 @@
|
||||
'libraries': [
|
||||
'-ladvapi32',
|
||||
'-liphlpapi',
|
||||
'-lole32',
|
||||
'-lpsapi',
|
||||
'-lshell32',
|
||||
'-lws2_32'
|
||||
@ -304,6 +305,7 @@
|
||||
'test/test-getnameinfo.c',
|
||||
'test/test-getsockname.c',
|
||||
'test/test-handle-fileno.c',
|
||||
'test/test-homedir.c',
|
||||
'test/test-hrtime.c',
|
||||
'test/test-idle.c',
|
||||
'test/test-ip6-addr.c',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user