Skip to content

Commit 6887116

Browse files
committed
unix,win: add uv_os_homedir()
PR-URL: libuv/libuv#350 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
1 parent a74c2c9 commit 6887116

File tree

9 files changed

+228
-7
lines changed

9 files changed

+228
-7
lines changed

deps/uv/Makefile.am

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ include_HEADERS += include/uv-win.h include/tree.h
4545
AM_CPPFLAGS += -I$(top_srcdir)/src/win \
4646
-DWIN32_LEAN_AND_MEAN \
4747
-D_WIN32_WINNT=0x0600
48-
LIBS += -lws2_32 -lpsapi -liphlpapi -lshell32
48+
LIBS += -lws2_32 -lpsapi -liphlpapi -lole32 -lshell32
4949
libuv_la_SOURCES += src/win/async.c \
5050
src/win/atomicops-inl.h \
5151
src/win/core.c \
@@ -165,6 +165,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
165165
test/test-getnameinfo.c \
166166
test/test-getsockname.c \
167167
test/test-handle-fileno.c \
168+
test/test-homedir.c \
168169
test/test-hrtime.c \
169170
test/test-idle.c \
170171
test/test-ip4-addr.c \

deps/uv/checksparse.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ test/test-get-loadavg.c
103103
test/test-get-memory.c
104104
test/test-getaddrinfo.c
105105
test/test-getsockname.c
106+
test/test-homedir.c
106107
test/test-hrtime.c
107108
test/test-idle.c
108109
test/test-ip6-addr.c

deps/uv/docs/src/misc.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,23 @@ API
227227
228228
Changes the current working directory.
229229
230+
.. c:function:: int uv_os_homedir(char* buffer, size_t* size)
231+
232+
Gets the current user's home directory. On Windows, `uv_os_homedir()` first
233+
checks the `USERPROFILE` environment variable using
234+
`GetEnvironmentVariableW()`. If `USERPROFILE` is not set,
235+
`SHGetKnownFolderPath()` is called. On all other operating systems,
236+
`uv_os_homedir()` first checks the `HOME` environment variable using
237+
:man:`getenv(3)`. If `HOME` is not set, :man:`getpwuid_r(3)` is called. The
238+
user's home directory is stored in `buffer`. When `uv_os_homedir()` is
239+
called, `size` indicates the maximum size of `buffer`. On success or
240+
`UV_ENOBUFS` failure, `size` is set to the string length of `buffer`.
241+
242+
.. warning::
243+
`uv_os_homedir()` is not thread safe.
244+
245+
.. versionadded:: 1.6.0
246+
230247
.. uint64_t uv_get_free_memory(void)
231248
.. c:function:: uint64_t uv_get_total_memory(void)
232249

deps/uv/include/uv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,8 @@ typedef struct {
10281028

10291029
UV_EXTERN int uv_getrusage(uv_rusage_t* rusage);
10301030

1031+
UV_EXTERN int uv_os_homedir(char* buffer, size_t* size);
1032+
10311033
UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count);
10321034
UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count);
10331035

deps/uv/src/unix/core.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <limits.h> /* INT_MAX, PATH_MAX */
3939
#include <sys/uio.h> /* writev */
4040
#include <sys/resource.h> /* getrusage */
41+
#include <pwd.h>
4142

4243
#ifdef __linux__
4344
# include <sys/ioctl.h>
@@ -983,3 +984,82 @@ int uv__dup2_cloexec(int oldfd, int newfd) {
983984
return r;
984985
}
985986
}
987+
988+
989+
int uv_os_homedir(char* buffer, size_t* size) {
990+
struct passwd pw;
991+
struct passwd* result;
992+
char* buf;
993+
uid_t uid;
994+
size_t bufsize;
995+
size_t len;
996+
int r;
997+
998+
if (buffer == NULL || size == NULL || *size == 0)
999+
return -EINVAL;
1000+
1001+
/* Check if the HOME environment variable is set first */
1002+
buf = getenv("HOME");
1003+
1004+
if (buf != NULL) {
1005+
len = strlen(buf);
1006+
1007+
if (len >= *size) {
1008+
*size = len;
1009+
return -ENOBUFS;
1010+
}
1011+
1012+
memcpy(buffer, buf, len + 1);
1013+
*size = len;
1014+
1015+
return 0;
1016+
}
1017+
1018+
/* HOME is not set, so call getpwuid() */
1019+
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
1020+
1021+
if (bufsize <= 0)
1022+
return -EIO;
1023+
1024+
uid = getuid();
1025+
buf = NULL;
1026+
1027+
for (;;) {
1028+
free(buf);
1029+
buf = malloc(bufsize);
1030+
1031+
if (buf == NULL)
1032+
return -ENOMEM;
1033+
1034+
r = getpwuid_r(uid, &pw, buf, bufsize, &result);
1035+
1036+
if (r != ERANGE)
1037+
break;
1038+
1039+
bufsize *= 2;
1040+
}
1041+
1042+
if (r != 0) {
1043+
free(buf);
1044+
return -r;
1045+
}
1046+
1047+
if (result == NULL) {
1048+
free(buf);
1049+
return -ENOENT;
1050+
}
1051+
1052+
len = strlen(pw.pw_dir);
1053+
1054+
if (len >= *size) {
1055+
*size = len;
1056+
free(buf);
1057+
return -ENOBUFS;
1058+
}
1059+
1060+
memcpy(buffer, pw.pw_dir, len + 1);
1061+
*size = len;
1062+
free(buf);
1063+
1064+
return 0;
1065+
}

deps/uv/src/win/util.c

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
#include <psapi.h>
3737
#include <tlhelp32.h>
3838
#include <windows.h>
39+
#include <shlobj.h>
40+
#include <objbase.h>
3941

4042

4143
/*
@@ -72,7 +74,7 @@ void uv__util_init() {
7274
InitializeCriticalSection(&process_title_lock);
7375

7476
/* Retrieve high-resolution timer frequency
75-
* and precompute its reciprocal.
77+
* and precompute its reciprocal.
7678
*/
7779
if (QueryPerformanceFrequency(&perf_frequency)) {
7880
hrtime_interval_ = 1.0 / perf_frequency.QuadPart;
@@ -801,8 +803,8 @@ static int is_windows_version_or_greater(DWORD os_major,
801803

802804
/* Perform the test. */
803805
return (int) VerifyVersionInfo(
804-
&osvi,
805-
VER_MAJORVERSION | VER_MINORVERSION |
806+
&osvi,
807+
VER_MAJORVERSION | VER_MINORVERSION |
806808
VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
807809
condition_mask);
808810
}
@@ -870,7 +872,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
870872
flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
871873
GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_PREFIX;
872874
}
873-
875+
874876

875877
/* Fetch the size of the adapters reported by windows, and then get the */
876878
/* list itself. */
@@ -1053,14 +1055,14 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
10531055
prefix->PrefixLength <= prefix_len)
10541056
continue;
10551057

1056-
if (address_prefix_match(sa->sa_family, sa,
1058+
if (address_prefix_match(sa->sa_family, sa,
10571059
prefix->Address.lpSockaddr, prefix->PrefixLength)) {
10581060
prefix_len = prefix->PrefixLength;
10591061
}
10601062
}
10611063

10621064
/* If there is no matching prefix information, return a single-host
1063-
* subnet mask (e.g. 255.255.255.255 for IPv4).
1065+
* subnet mask (e.g. 255.255.255.255 for IPv4).
10641066
*/
10651067
if (!prefix_len)
10661068
prefix_len = (sa->sa_family == AF_INET6) ? 128 : 32;
@@ -1153,3 +1155,67 @@ int uv_getrusage(uv_rusage_t *uv_rusage) {
11531155

11541156
return 0;
11551157
}
1158+
1159+
1160+
int uv_os_homedir(char* buffer, size_t* size) {
1161+
wchar_t* path;
1162+
size_t bufsize;
1163+
size_t len;
1164+
int r;
1165+
1166+
if (buffer == NULL || size == NULL || *size == 0)
1167+
return UV_EINVAL;
1168+
1169+
/* Check if the USERPROFILE environment variable is set first */
1170+
path = malloc(*size * sizeof(WCHAR));
1171+
1172+
if (path == NULL)
1173+
return UV_ENOMEM;
1174+
1175+
len = GetEnvironmentVariableW(L"USERPROFILE", path, *size);
1176+
1177+
if (len == 0) {
1178+
r = GetLastError();
1179+
free(path);
1180+
1181+
if (r != ERROR_ENVVAR_NOT_FOUND)
1182+
return uv_translate_sys_error(r);
1183+
} else {
1184+
if (len > *size) {
1185+
free(path);
1186+
*size = len - 1;
1187+
return UV_ENOBUFS;
1188+
}
1189+
1190+
bufsize = uv_utf16_to_utf8(path, -1, buffer, *size);
1191+
assert(len + 1 == bufsize);
1192+
free(path);
1193+
*size = len;
1194+
1195+
return 0;
1196+
}
1197+
1198+
/* USERPROFILE is not set, so call SHGetKnownFolderPath() */
1199+
if (SHGetKnownFolderPath(&FOLDERID_Profile, 0, NULL, &path) != S_OK)
1200+
return uv_translate_sys_error(GetLastError());
1201+
1202+
bufsize = uv_utf16_to_utf8(path, -1, buffer, *size);
1203+
1204+
if (bufsize == 0) {
1205+
r = GetLastError();
1206+
1207+
if (r == ERROR_INSUFFICIENT_BUFFER) {
1208+
*size = wcslen(path);
1209+
CoTaskMemFree(path);
1210+
return UV_ENOBUFS;
1211+
}
1212+
1213+
CoTaskMemFree(path);
1214+
return uv_translate_sys_error(r);
1215+
}
1216+
1217+
CoTaskMemFree(path);
1218+
*size = bufsize - 1;
1219+
1220+
return 0;
1221+
}

deps/uv/test/test-homedir.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#include "uv.h"
2+
#include "task.h"
3+
#include <string.h>
4+
5+
#define PATHMAX 1024
6+
#define SMALLPATH 1
7+
8+
TEST_IMPL(homedir) {
9+
char homedir[PATHMAX];
10+
size_t len;
11+
char last;
12+
int r;
13+
14+
/* Test the normal case */
15+
len = sizeof homedir;
16+
homedir[0] = '\0';
17+
ASSERT(strlen(homedir) == 0);
18+
r = uv_os_homedir(homedir, &len);
19+
ASSERT(r == 0);
20+
ASSERT(strlen(homedir) == len);
21+
ASSERT(len > 0);
22+
ASSERT(homedir[len] == '\0');
23+
24+
if (len > 1) {
25+
last = homedir[len - 1];
26+
#ifdef _WIN32
27+
ASSERT(last != '\\');
28+
#else
29+
ASSERT(last != '/');
30+
#endif
31+
}
32+
33+
/* Test the case where the buffer is too small */
34+
len = SMALLPATH;
35+
r = uv_os_homedir(homedir, &len);
36+
ASSERT(r == UV_ENOBUFS);
37+
ASSERT(len > SMALLPATH);
38+
39+
/* Test invalid inputs */
40+
r = uv_os_homedir(NULL, &len);
41+
ASSERT(r == UV_EINVAL);
42+
r = uv_os_homedir(homedir, NULL);
43+
ASSERT(r == UV_EINVAL);
44+
len = 0;
45+
r = uv_os_homedir(homedir, &len);
46+
ASSERT(r == UV_EINVAL);
47+
48+
return 0;
49+
}

deps/uv/test/test-list.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ TEST_DECLARE (process_title)
182182
TEST_DECLARE (cwd_and_chdir)
183183
TEST_DECLARE (get_memory)
184184
TEST_DECLARE (handle_fileno)
185+
TEST_DECLARE (homedir)
185186
TEST_DECLARE (hrtime)
186187
TEST_DECLARE (getaddrinfo_fail)
187188
TEST_DECLARE (getaddrinfo_fail_sync)
@@ -540,6 +541,8 @@ TASK_LIST_START
540541

541542
TEST_ENTRY (handle_fileno)
542543

544+
TEST_ENTRY (homedir)
545+
543546
TEST_ENTRY (hrtime)
544547

545548
TEST_ENTRY_CUSTOM (getaddrinfo_fail, 0, 0, 10000)

deps/uv/uv.gyp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
'libraries': [
109109
'-ladvapi32',
110110
'-liphlpapi',
111+
'-lole32',
111112
'-lpsapi',
112113
'-lshell32',
113114
'-lws2_32'
@@ -304,6 +305,7 @@
304305
'test/test-getnameinfo.c',
305306
'test/test-getsockname.c',
306307
'test/test-handle-fileno.c',
308+
'test/test-homedir.c',
307309
'test/test-hrtime.c',
308310
'test/test-idle.c',
309311
'test/test-ip6-addr.c',

0 commit comments

Comments
 (0)