Index: cmake/config-ix.cmake =================================================================== --- cmake/config-ix.cmake +++ cmake/config-ix.cmake @@ -56,6 +56,7 @@ if( NOT PURE_WINDOWS ) check_include_file(pthread.h HAVE_PTHREAD_H) endif() +check_include_file(pwd.h HAVE_PWD_H) check_include_file(signal.h HAVE_SIGNAL_H) check_include_file(stdint.h HAVE_STDINT_H) check_include_file(sys/dir.h HAVE_SYS_DIR_H) Index: include/llvm/Config/config.h.cmake =================================================================== --- include/llvm/Config/config.h.cmake +++ include/llvm/Config/config.h.cmake @@ -232,6 +232,9 @@ /* Have pthread_rwlock_init */ #cmakedefine HAVE_PTHREAD_RWLOCK_INIT ${HAVE_PTHREAD_RWLOCK_INIT} +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_PWD_H ${HAVE_PWD_H} + /* Define to 1 if srand48/lrand48/drand48 exist in */ #cmakedefine HAVE_RAND48 ${HAVE_RAND48} Index: lib/Support/Unix/Path.inc =================================================================== --- lib/Support/Unix/Path.inc +++ lib/Support/Unix/Path.inc @@ -28,6 +28,9 @@ #ifdef HAVE_SYS_MMAN_H #include #endif +#ifdef HAVE_PWD_H +#include +#endif #if HAVE_DIRENT_H # include # define NAMLEN(dirent) strlen((dirent)->d_name) @@ -550,13 +553,42 @@ namespace path { -bool home_directory(SmallVectorImpl &result) { +static bool getHomeDirFromPasswd(SmallVectorImpl &Result) { + static const size_t InitialBufSize = 1024; + SmallString Buf; + Buf.reserve(InitialBufSize); + Buf.set_size(Buf.capacity()); + + ::passwd Passwd; + ::passwd *PasswdResult = nullptr; + int Err = 0; + while ((Err = ::getpwuid_r(::getuid(), &Passwd, Buf.data(), Buf.size(), + &PasswdResult)) == ERANGE) { + Buf.reserve(Buf.size() * 2); + Buf.set_size(Buf.capacity()); + } + + if (!Err) { + assert(PasswdResult == &Passwd && "getpwuid_r does strange things"); + auto Dir = Passwd.pw_dir; + Result.clear(); + Result.append(Dir, Dir + strlen(Dir)); + return true; + } + + return false; +} + +bool home_directory(SmallVectorImpl &Result) { if (char *RequestedDir = getenv("HOME")) { - result.clear(); - result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); + Result.clear(); + Result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); return true; } + if (getHomeDirFromPasswd(Result)) + return true; + return false; } Index: unittests/Support/Path.cpp =================================================================== --- unittests/Support/Path.cpp +++ unittests/Support/Path.cpp @@ -41,6 +41,34 @@ namespace { +class EnvVarModifier { + const char *Name; + const char *OrigValue; + const char *Value; + +public: + EnvVarModifier(const char *Name, const char *Value) + : Name(Name), Value(Value) { + OrigValue = std::getenv(Name); + if (std::strlen(Value)) + ::setenv(Name, Value, 1); + else + ::unsetenv(Name); + } + + ~EnvVarModifier() { ::setenv(Name, OrigValue, 1); } + + EnvVarModifier(const EnvVarModifier &) = delete; + EnvVarModifier &operator=(const EnvVarModifier &) = delete; + + const char *getValue() const { return Value; } + + void setValue(const char *Value) { + if (::setenv(Name, Value, 1) == 0) + this->Value = Value; + } +}; + TEST(is_separator, Works) { EXPECT_TRUE(path::is_separator('/')); EXPECT_FALSE(path::is_separator('\0')); @@ -322,6 +350,19 @@ } } +#ifdef LLVM_ON_UNIX +TEST(Support, HomeDirectoryOnUnix) { + SmallString<200> HomeDir; + + if (path::home_directory(HomeDir)) { + // If home_directory returns anything, check again without $HOME set. + // That will test the part of implementation that uses getpwuid(). + EnvVarModifier HomeVar{"HOME", ""}; + EXPECT_TRUE(path::home_directory(HomeDir)); + } +} +#endif + TEST(Support, UserCacheDirectory) { SmallString<13> CacheDir; SmallString<20> CacheDir2;